ework-daemon 0.4.17 → 0.4.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/opencode-reader.ts +79 -6
package/package.json
CHANGED
package/src/opencode-reader.ts
CHANGED
|
@@ -70,10 +70,76 @@ export class OpencodeReader {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
async exportSession(id: string): Promise<unknown> {
|
|
73
|
+
try {
|
|
74
|
+
const fromDB = this.exportSessionFromDB(id);
|
|
75
|
+
if (fromDB) return fromDB;
|
|
76
|
+
} catch {
|
|
77
|
+
// DB read failed (schema drift, locked, etc.) — fall back to CLI
|
|
78
|
+
}
|
|
73
79
|
const raw = await this.runJSON(["export", id]);
|
|
74
80
|
return raw;
|
|
75
81
|
}
|
|
76
82
|
|
|
83
|
+
private exportSessionFromDB(id: string): unknown | null {
|
|
84
|
+
let db: Database;
|
|
85
|
+
try {
|
|
86
|
+
db = new Database(this.dbPath, { readonly: true });
|
|
87
|
+
} catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const srow = db
|
|
92
|
+
.prepare("SELECT id, title, directory, version, time_created, time_updated FROM session WHERE id = ?")
|
|
93
|
+
.get(id) as { id: string; title: string; directory: string; version: string; time_created: number; time_updated: number } | null;
|
|
94
|
+
if (!srow) return null;
|
|
95
|
+
|
|
96
|
+
const mrows = db
|
|
97
|
+
.prepare("SELECT id, data FROM message WHERE session_id = ? ORDER BY time_created, id")
|
|
98
|
+
.all(id) as Array<{ id: string; data: string }>;
|
|
99
|
+
const prows = db
|
|
100
|
+
.prepare("SELECT message_id, data FROM part WHERE session_id = ? ORDER BY message_id, id")
|
|
101
|
+
.all(id) as Array<{ message_id: string; data: string }>;
|
|
102
|
+
|
|
103
|
+
const partsByMsg = new Map<string, unknown[]>();
|
|
104
|
+
for (const p of prows) {
|
|
105
|
+
let pd: unknown;
|
|
106
|
+
try { pd = JSON.parse(p.data); } catch { continue; }
|
|
107
|
+
if (!pd || typeof pd !== "object") continue;
|
|
108
|
+
const arr = partsByMsg.get(p.message_id);
|
|
109
|
+
if (arr) arr.push(pd); else partsByMsg.set(p.message_id, [pd]);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const messages: unknown[] = [];
|
|
113
|
+
for (const m of mrows) {
|
|
114
|
+
let md: Record<string, unknown>;
|
|
115
|
+
try { md = JSON.parse(m.data); } catch { continue; }
|
|
116
|
+
const info: Record<string, unknown> = { id: m.id, role: md.role };
|
|
117
|
+
if (md.agent) info.agent = md.agent;
|
|
118
|
+
const modelID = typeof md.modelID === "string" ? md.modelID : (typeof md.model === "string" ? md.model : undefined);
|
|
119
|
+
if (modelID) info.modelID = modelID;
|
|
120
|
+
const tRaw = md.time && typeof md.time === "object" ? (md.time as Record<string, unknown>) : null;
|
|
121
|
+
if (tRaw && typeof tRaw.created === "number") info.time = { created: tRaw.created };
|
|
122
|
+
if (md.tokens) info.tokens = md.tokens;
|
|
123
|
+
messages.push({ info, parts: partsByMsg.get(m.id) ?? [] });
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
info: {
|
|
128
|
+
id: srow.id,
|
|
129
|
+
title: srow.title || "(untitled)",
|
|
130
|
+
directory: srow.directory ?? "",
|
|
131
|
+
version: srow.version ?? "",
|
|
132
|
+
time: { created: srow.time_created, updated: srow.time_updated },
|
|
133
|
+
},
|
|
134
|
+
messages,
|
|
135
|
+
};
|
|
136
|
+
} catch {
|
|
137
|
+
return null;
|
|
138
|
+
} finally {
|
|
139
|
+
db.close();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
77
143
|
async exportSessionRaw(id: string): Promise<string> {
|
|
78
144
|
const { stdout, code } = await this.run(["export", id]);
|
|
79
145
|
if (code !== 0) {
|
|
@@ -168,10 +234,17 @@ async function readCapped(stream: ReadableStream<Uint8Array> | null, cap: number
|
|
|
168
234
|
}
|
|
169
235
|
|
|
170
236
|
function stripNonJsonPreamble(s: string): string | null {
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
237
|
+
const lines = s.split(/\r?\n/);
|
|
238
|
+
for (let i = 0; i < lines.length; i++) {
|
|
239
|
+
const trimmed = (lines[i] ?? "").trimStart();
|
|
240
|
+
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) continue;
|
|
241
|
+
const candidate = lines.slice(i).join("\n");
|
|
242
|
+
try {
|
|
243
|
+
JSON.parse(candidate);
|
|
244
|
+
return candidate;
|
|
245
|
+
} catch {
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return null;
|
|
177
250
|
}
|