UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

118 lines (117 loc) • 4.18 kB
import { c as isRecord } from "./record-coerce-TIsquoVR.mjs"; import { a as tableHasColumn, i as tableExists } from "./openclaw-state-db-schema-helpers-BlZefrPy.mjs"; import { s as transcriptEventJsonSql } from "./transcript-payload-Yc_w8Yx5.mjs"; import { t as getSessionKysely } from "./session-accessor.sqlite-scope-helpers-DOK3pvYX.mjs"; import "./session-accessor.sqlite-scope-CdpbxR_m.mjs"; //#region src/commands/doctor-session-sqlite-transcript-readers.ts var ReadOnlySqliteTranscriptReader = class { constructor(database) { this.database = database; this.eventJsonSql = tableHasColumn(database, "transcript_events", "event_zstd") ? transcriptEventJsonSql(database).compile(getSessionKysely(database)).sql : "event_json"; this.labelRowsSql = `SELECT ${this.eventJsonSql} AS event_json, seq FROM transcript_events WHERE session_id = ? ORDER BY seq ASC`; } sessionIds() { if (!tableExists(this.database, "transcript_events")) return []; return this.database.prepare("SELECT DISTINCT session_id FROM transcript_events ORDER BY session_id ASC").all().flatMap((row) => typeof row.session_id === "string" ? [row.session_id] : []); } repairSnapshot(sessionId, needsRepair, mayNeedRepair) { let iterator; try { this.labelDetection ??= this.database.prepare(this.labelRowsSql); iterator = this.labelDetection.iterate(sessionId); for (const row of iterator) { if (typeof row.event_json !== "string" || typeof row.seq !== "number") continue; if (!mayNeedRepair(row.event_json)) continue; let event; try { event = JSON.parse(row.event_json); } catch { continue; } if (!needsRepair(event)) continue; const rows = []; this.labelSnapshot ??= this.database.prepare(this.labelRowsSql); iterator = this.labelSnapshot.iterate(sessionId); for (const candidate of iterator) if (typeof candidate.event_json === "string" && typeof candidate.seq === "number") rows.push({ eventJson: candidate.event_json, seq: candidate.seq }); return { ok: true, rows }; } return { ok: true, rows: [] }; } catch (error) { try { iterator?.return?.(); } catch {} return { ok: false, error }; } } /** Reads exact row metadata for a guarded transcript replacement without opening a writer. */ headerlessSnapshot(sessionId, acceptRow) { let iterator; try { this.firstHeaderRow ??= this.database.prepare(`SELECT ${this.eventJsonSql} AS event_json FROM transcript_events WHERE session_id = ? ORDER BY seq ASC LIMIT 1`); const firstRow = this.firstHeaderRow.get(sessionId); if (typeof firstRow?.event_json === "string") { let first; try { first = JSON.parse(firstRow.event_json); } catch { return { ok: true, rows: [] }; } if (!isRecord(first) || first.type === "session") return { ok: true, rows: [] }; } this.sessionKey ??= this.database.prepare("SELECT session_key FROM session_windows WHERE session_id = ? LIMIT 1"); const sessionKeyRow = this.sessionKey.get(sessionId); const storageRows = []; this.headerSnapshot ??= this.database.prepare(`SELECT created_at, ${this.eventJsonSql} AS event_json, seq FROM transcript_events WHERE session_id = ? ORDER BY seq ASC`); iterator = this.headerSnapshot.iterate(sessionId); for (const row of iterator) { if (typeof row.created_at !== "number" || typeof row.event_json !== "string" || typeof row.seq !== "number") return { ok: false, error: /* @__PURE__ */ new Error(`Invalid transcript row metadata for session ${sessionId}`) }; const storageRow = { createdAt: row.created_at, eventJson: row.event_json, seq: row.seq }; if (!acceptRow(storageRow)) return { ok: true, rows: [] }; storageRows.push(storageRow); } return { ok: true, rows: storageRows, ...typeof sessionKeyRow?.session_key === "string" ? { sessionKey: sessionKeyRow.session_key } : {} }; } catch (error) { try { iterator?.return?.(); } catch {} return { ok: false, error }; } } }; //#endregion export { ReadOnlySqliteTranscriptReader as t };