Download packages/core/src/session/revert.ts from SaylorTwift/opencode: direct link, hf CLI and curl.
- Browser
- Download file 4.37 kB
-
https://huggingface.co/SaylorTwift/opencode/resolve/main/packages/core/src/session/revert.ts
- Command line
-
hf download hf://SaylorTwift/opencode/packages/core/src/session/revert.ts
-
curl -L -o revert.ts https://huggingface.co/SaylorTwift/opencode/resolve/main/packages/core/src/session/revert.ts
4.37 kB
| export * as SessionRevert from "./revert" | |
| import { and, asc, eq, gt } from "drizzle-orm" | |
| import { DateTime, Effect, Schema } from "effect" | |
| import { Database } from "../database/database" | |
| import { EventV2 } from "../event" | |
| import { RelativePath } from "../schema" | |
| import { Snapshot } from "../snapshot" | |
| import { SessionEvent } from "./event" | |
| import { SessionMessage } from "./message" | |
| import { SessionSchema } from "./schema" | |
| import { SessionMessageTable } from "./sql" | |
| export class MessageNotFoundError extends Schema.TaggedErrorClass<MessageNotFoundError>()( | |
| "Session.MessageNotFoundError", | |
| { | |
| sessionID: SessionSchema.ID, | |
| messageID: SessionMessage.ID, | |
| }, | |
| ) {} | |
| interface BoundaryInput { | |
| readonly sessionID: SessionSchema.ID | |
| readonly messageID: SessionMessage.ID | |
| } | |
| const plan = Effect.fn("SessionRevert.plan")(function* (input: BoundaryInput) { | |
| const db = (yield* Database.Service).db | |
| const boundary = yield* db | |
| .select({ seq: SessionMessageTable.seq }) | |
| .from(SessionMessageTable) | |
| .where(and(eq(SessionMessageTable.session_id, input.sessionID), eq(SessionMessageTable.id, input.messageID))) | |
| .get() | |
| .pipe(Effect.orDie) | |
| if (!boundary) return yield* new MessageNotFoundError(input) | |
| const rows = yield* db | |
| .select() | |
| .from(SessionMessageTable) | |
| .where( | |
| and( | |
| eq(SessionMessageTable.session_id, input.sessionID), | |
| eq(SessionMessageTable.type, "assistant"), | |
| gt(SessionMessageTable.seq, boundary.seq), | |
| ), | |
| ) | |
| .orderBy(asc(SessionMessageTable.seq)) | |
| .all() | |
| .pipe(Effect.orDie) | |
| const decode = Schema.decodeUnknownEffect(SessionMessage.Message) | |
| const files = new Map<RelativePath, Snapshot.ID>() | |
| for (const row of rows) { | |
| const message = yield* decode({ ...row.data, id: row.id, type: row.type }).pipe(Effect.orDie) | |
| if (message.type !== "assistant" || !message.snapshot?.start) continue | |
| for (const file of message.snapshot.files ?? []) | |
| if (!files.has(file)) files.set(file, Snapshot.ID.make(message.snapshot.start)) | |
| } | |
| return files | |
| }) | |
| export const stage = Effect.fn("SessionRevert.stage")(function* (input: { | |
| readonly session: SessionSchema.Info | |
| readonly messageID: SessionMessage.ID | |
| readonly files?: boolean | |
| }) { | |
| const snapshot = yield* Snapshot.Service | |
| const events = yield* EventV2.Service | |
| const original = input.session.revert?.snapshot | |
| ? Snapshot.ID.make(input.session.revert.snapshot) | |
| : yield* snapshot.capture() | |
| const next = yield* plan({ sessionID: input.session.id, messageID: input.messageID }) | |
| const restore = new Map<RelativePath, Snapshot.ID>() | |
| if (original) { | |
| for (const file of input.session.revert?.files ?? []) restore.set(file.path, original) | |
| } | |
| if (input.files !== false) for (const [file, tree] of next) restore.set(file, tree) | |
| if (restore.size) yield* snapshot.restore({ files: restore }) | |
| const paths = input.files === false ? [] : Array.from(next.keys()) | |
| const files = original | |
| ? yield* snapshot.diff({ from: original, to: (yield* snapshot.capture()) ?? original, paths }) | |
| : [] | |
| const revert = { | |
| messageID: input.messageID, | |
| snapshot: original, | |
| diff: files | |
| .map((file) => file.patch) | |
| .join("") | |
| .trim(), | |
| files, | |
| } satisfies SessionSchema.Info["revert"] | |
| yield* events.publish(SessionEvent.RevertEvent.Staged, { | |
| sessionID: input.session.id, | |
| timestamp: yield* DateTime.now, | |
| revert, | |
| }) | |
| return revert | |
| }) | |
| export const clear = Effect.fn("SessionRevert.clear")(function* (session: SessionSchema.Info) { | |
| if (!session.revert) return | |
| const snapshot = yield* Snapshot.Service | |
| const original = session.revert.snapshot ? Snapshot.ID.make(session.revert.snapshot) : undefined | |
| if (original) | |
| yield* snapshot.restore({ | |
| files: new Map((session.revert.files ?? []).map((file) => [file.path, original])), | |
| }) | |
| const events = yield* EventV2.Service | |
| yield* events.publish(SessionEvent.RevertEvent.Cleared, { | |
| sessionID: session.id, | |
| timestamp: yield* DateTime.now, | |
| }) | |
| }) | |
| export const commit = Effect.fn("SessionRevert.commit")(function* (session: SessionSchema.Info) { | |
| if (!session.revert) return | |
| const events = yield* EventV2.Service | |
| yield* events.publish(SessionEvent.RevertEvent.Committed, { | |
| sessionID: session.id, | |
| messageID: session.revert.messageID, | |
| timestamp: yield* DateTime.now, | |
| }) | |
| }) | |