spiracha 1.1.2 → 1.3.0
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/AGENTS.md +57 -14
- package/README.md +122 -65
- package/apps/ui/AGENTS.md +18 -8
- package/apps/ui/README.md +30 -12
- package/apps/ui/dist/client/assets/{analytics-CqWZmyV6.js → analytics-B_hYz65v.js} +1 -1
- package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-qiyygB7e.js +1 -0
- package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-z1SQC2Kg.js +1 -0
- package/apps/ui/dist/client/assets/antigravity-keychain-panel-dYuRWtCf.js +1 -0
- package/apps/ui/dist/client/assets/antigravity._workspaceKey-CliqUr7o.js +1 -0
- package/apps/ui/dist/client/assets/antigravity._workspaceKey-CnoBzyX6.js +1 -0
- package/apps/ui/dist/client/assets/antigravity.index-CakfZz_E.js +1 -0
- package/apps/ui/dist/client/assets/antigravity.index-DY7M1KhG.js +1 -0
- package/apps/ui/dist/client/assets/badge-aHE9ETVe.js +1 -0
- package/apps/ui/dist/client/assets/checkbox-DN3XnJaA.js +1 -0
- package/apps/ui/dist/client/assets/cursor-threads._composerId-BMQyx8qG.js +1 -0
- package/apps/ui/dist/client/assets/cursor-threads._composerId-BTlaA-tV.js +1 -0
- package/apps/ui/dist/client/assets/cursor._workspaceKey-CrgrfevV.js +1 -0
- package/apps/ui/dist/client/assets/cursor._workspaceKey-bYS2syGL.js +1 -0
- package/apps/ui/dist/client/assets/cursor.index-CTqZMPYU.js +1 -0
- package/apps/ui/dist/client/assets/cursor.index-Clsz4E_e.js +2 -0
- package/apps/ui/dist/client/assets/{data-table-DnPYMPCD.js → data-table-Cj-v-uyB.js} +2 -2
- package/apps/ui/dist/client/assets/delete-confirm-dialog-DTpzBiNK.js +11 -0
- package/apps/ui/dist/client/assets/dist-BNAn99Pu.js +1 -0
- package/apps/ui/dist/client/assets/download-P3Rp23Ad.js +1 -0
- package/apps/ui/dist/client/assets/dropdown-menu-3qB5j9nt.js +1 -0
- package/apps/ui/dist/client/assets/es2015-Dwm_turD.js +41 -0
- package/apps/ui/dist/client/assets/export-dialog-CazdrASq.js +1 -0
- package/apps/ui/dist/client/assets/formatters-BdnWuM1z.js +1 -0
- package/apps/ui/dist/client/assets/index-BVFnfS78.js +22 -0
- package/apps/ui/dist/client/assets/json-panel-DLkS30sQ.js +1 -0
- package/apps/ui/dist/client/assets/metadata-section-jnIkB7dB.js +1 -0
- package/apps/ui/dist/client/assets/{metric-card-9jwBF7rG.js → metric-card-CBZuWLzQ.js} +1 -1
- package/apps/ui/dist/client/assets/page-header-CnD21cPn.js +1 -0
- package/apps/ui/dist/client/assets/projects._project-BLszwvYL.js +1 -0
- package/apps/ui/dist/client/assets/projects._project-DvLxYbvk.js +1 -0
- package/apps/ui/dist/client/assets/projects.index-COn8woBR.js +1 -0
- package/apps/ui/dist/client/assets/projects.index-DYs98skV.js +3 -0
- package/apps/ui/dist/client/assets/refresh-ccw-BDrYXjtD.js +1 -0
- package/apps/ui/dist/client/assets/reload-error-panel-DLAg0AW2.js +1 -0
- package/apps/ui/dist/client/assets/routes-BtF5-coe.js +1 -0
- package/apps/ui/dist/client/assets/scroll-text-CqaFm9by.js +1 -0
- package/apps/ui/dist/client/assets/select-DbnpwqL6.js +1 -0
- package/apps/ui/dist/client/assets/settings-CGX3VleN.js +1 -0
- package/apps/ui/dist/client/assets/styles-Ch0r3kMZ.css +1 -0
- package/apps/ui/dist/client/assets/text-document-panel-DPleOmmq.js +1 -0
- package/apps/ui/dist/client/assets/text-filter-7M6wRo-t.js +2 -0
- package/apps/ui/dist/client/assets/threads._threadId-D5w76IB-.js +7 -0
- package/apps/ui/dist/client/assets/{threads._threadId-DT75NiBa.js → threads._threadId-Dx85sI9P.js} +1 -1
- package/apps/ui/dist/client/assets/useMutation-MZ3Hr9h9.js +1 -0
- package/apps/ui/dist/client/assets/useQuery-Cb4V0AT0.js +1 -0
- package/apps/ui/dist/client/icon.svg +28 -0
- package/apps/ui/dist/client/manifest.json +6 -16
- package/apps/ui/dist/server/assets/_tanstack-start-manifest_v-CBbkUXw6.js +227 -0
- package/apps/ui/dist/server/assets/{analytics-BMxW_bZL.js → analytics-CBNOYZwJ.js} +2 -2
- package/apps/ui/dist/server/assets/antigravity-conversation-state-HgzS302O.js +16 -0
- package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-B9Rm4EXh.js +212 -0
- package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-BIdYNy68.js +20 -0
- package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-D426O-64.js +11 -0
- package/apps/ui/dist/server/assets/antigravity-db-D9gW1D8G.js +576 -0
- package/apps/ui/dist/server/assets/antigravity-keychain-DOiuHDwK.js +126 -0
- package/apps/ui/dist/server/assets/antigravity-keychain-panel-DcLyBBwd.js +55 -0
- package/apps/ui/dist/server/assets/antigravity-queries-CgQhlQ7J.js +37 -0
- package/apps/ui/dist/server/assets/antigravity-server-DFUx4Khk.js +114 -0
- package/apps/ui/dist/server/assets/antigravity._workspaceKey-3m_MzNFA.js +11 -0
- package/apps/ui/dist/server/assets/antigravity._workspaceKey-D42ixtzp.js +210 -0
- package/apps/ui/dist/server/assets/antigravity._workspaceKey-DnSlSC-C.js +28 -0
- package/apps/ui/dist/server/assets/antigravity.index-DZVT-cac.js +104 -0
- package/apps/ui/dist/server/assets/antigravity.index-DudTB3Tq.js +11 -0
- package/apps/ui/dist/server/assets/badge-EvdhKK_Z.js +26 -0
- package/apps/ui/dist/server/assets/{codex-queries-CAF6HYiG.js → codex-queries-eOJGfHQj.js} +6 -18
- package/apps/ui/dist/server/assets/{codex-server-C01sv0JJ.js → codex-server-nrETIF--.js} +166 -226
- package/apps/ui/dist/server/assets/createServerRpc-BtXIw2iP.js +12 -0
- package/apps/ui/dist/server/assets/createSsrRpc-COf5Zuye.js +16 -0
- package/apps/ui/dist/server/assets/cursor-db-B7agkAvM.js +643 -0
- package/apps/ui/dist/server/assets/cursor-exporter-types-CI3goo-c.js +34 -0
- package/apps/ui/dist/server/assets/cursor-queries-BMhuJeUO.js +65 -0
- package/apps/ui/dist/server/assets/cursor-recovery-9bJLs7vG.js +361 -0
- package/apps/ui/dist/server/assets/cursor-server-BgylIFgn.js +184 -0
- package/apps/ui/dist/server/assets/cursor-threads._composerId-BB0Y_Mao.js +11 -0
- package/apps/ui/dist/server/assets/cursor-threads._composerId-BsxFKzoJ.js +218 -0
- package/apps/ui/dist/server/assets/cursor-threads._composerId-DXffY_CK.js +18 -0
- package/apps/ui/dist/server/assets/cursor-transcript-2iL3KFSK.js +125 -0
- package/apps/ui/dist/server/assets/cursor._workspaceKey-BP2J1x_x.js +28 -0
- package/apps/ui/dist/server/assets/cursor._workspaceKey-BQd0e-Pd.js +399 -0
- package/apps/ui/dist/server/assets/cursor._workspaceKey-nmg3YIOQ.js +11 -0
- package/apps/ui/dist/server/assets/cursor.index-CQVxtCm8.js +189 -0
- package/apps/ui/dist/server/assets/cursor.index-CcsX7DG0.js +11 -0
- package/apps/ui/dist/server/assets/{delete-confirm-dialog-CWqcTXTF.js → delete-confirm-dialog-PCD7S0_M.js} +5 -4
- package/apps/ui/dist/server/assets/download-DMmiy1xf.js +92 -0
- package/apps/ui/dist/server/assets/{input-B4tEzctc.js → dropdown-menu-Dy_9t6TN.js} +1 -11
- package/apps/ui/dist/server/assets/{download-C5rkk_Bo.js → export-dialog-DaPlOGFT.js} +8 -99
- package/apps/ui/dist/server/assets/json-panel-RYsxWFae.js +16 -0
- package/apps/ui/dist/server/assets/{loading-panel-DbLdvjtR.js → loading-panel-BGFnWseS.js} +1 -1
- package/apps/ui/dist/server/assets/metadata-section-D6Lbc7D6.js +54 -0
- package/apps/ui/dist/server/assets/page-header-VNSaM3xd.js +29 -0
- package/apps/ui/dist/server/assets/projects._project-Bshqk7JA.js +12 -0
- package/apps/ui/dist/server/assets/{projects._project-CJ7l0ynC.js → projects._project-DUN3iWfg.js} +4 -4
- package/apps/ui/dist/server/assets/{projects._project-CcJLp_A8.js → projects._project-Dim9Y0kD.js} +54 -26
- package/apps/ui/dist/server/assets/projects.index-BLXOx5eL.js +12 -0
- package/apps/ui/dist/server/assets/{projects.index-srtogpuF.js → projects.index-DjSQK5dm.js} +23 -27
- package/apps/ui/dist/server/assets/{projects.index-CaplpeMy.js → reload-error-panel-BJMxY3U1.js} +5 -6
- package/apps/ui/dist/server/assets/{router-C_w-haH6.js → router-DrDgc-LD.js} +131 -44
- package/apps/ui/dist/server/assets/{routes-CPe-ppmC.js → routes-B-GlEe2C.js} +54 -39
- package/apps/ui/dist/server/assets/{routes-BhbxvJE7.js → routes-CNHAUMwo.js} +2 -2
- package/apps/ui/dist/server/assets/{settings-MvWDgc1u.js → settings-OayxIYQQ.js} +1 -1
- package/apps/ui/dist/server/assets/shared-CPRNYIql.js +134 -0
- package/apps/ui/dist/server/assets/text-document-panel-D8JbQWAn.js +23 -0
- package/apps/ui/dist/server/assets/text-filter-CGKxMCKt.js +36 -0
- package/apps/ui/dist/server/assets/{threads._threadId-Ba7vv6-K.js → threads._threadId-CJzm4KrZ.js} +3 -3
- package/apps/ui/dist/server/assets/{threads._threadId-euyNckhj.js → threads._threadId-DODTYddm.js} +69 -76
- package/apps/ui/dist/server/server.js +83 -36
- package/bin/codex-chats-claude.js +5 -0
- package/bin/codex-chats.js +5 -0
- package/bin/spiracha.js +5 -0
- package/package.json +26 -13
- package/src/export-cursor.ts +244 -0
- package/src/lib/antigravity-db.ts +936 -0
- package/src/lib/antigravity-exporter-types.ts +70 -0
- package/src/lib/antigravity-keychain.ts +203 -0
- package/src/lib/codex-browser-db.ts +7 -1
- package/src/lib/codex-browser-export.ts +2 -2
- package/src/lib/codex-browser-types.ts +22 -1
- package/src/lib/codex-exporter-cli.ts +9 -9
- package/src/lib/codex-exporter-transcript.ts +16 -190
- package/src/lib/codex-exporter-types.ts +1 -1
- package/src/lib/codex-exporter.ts +0 -1
- package/src/lib/codex-thread-recovery.ts +202 -0
- package/src/lib/cursor-db.ts +1096 -0
- package/src/lib/cursor-exporter-types.ts +190 -0
- package/src/lib/cursor-exporter.ts +266 -0
- package/src/lib/cursor-recovery.ts +543 -0
- package/src/lib/cursor-transcript.ts +183 -0
- package/src/lib/interactive-cli.ts +2 -2
- package/src/mcp-server.ts +2 -2
- package/src/spiracha.ts +16 -3
- package/src/ui-cli.ts +2 -2
- package/apps/ui/dist/client/assets/checkbox-DXM4lkJq.js +0 -1
- package/apps/ui/dist/client/assets/delete-confirm-dialog-CcZaRX33.js +0 -11
- package/apps/ui/dist/client/assets/download-DOwxk-cG.js +0 -1
- package/apps/ui/dist/client/assets/es2015-Bm0kEzx2.js +0 -41
- package/apps/ui/dist/client/assets/formatters-C12LmYaa.js +0 -1
- package/apps/ui/dist/client/assets/index-DdJ7ahIt.js +0 -22
- package/apps/ui/dist/client/assets/input-CEsI7EpI.js +0 -1
- package/apps/ui/dist/client/assets/page-header-Dr_h1CVv.js +0 -1
- package/apps/ui/dist/client/assets/projects._project-uyNGnpjH.js +0 -1
- package/apps/ui/dist/client/assets/projects._project-zoM8d2nH.js +0 -1
- package/apps/ui/dist/client/assets/projects.index-D1CWVN-O.js +0 -1
- package/apps/ui/dist/client/assets/projects.index-DukMuny6.js +0 -1
- package/apps/ui/dist/client/assets/routes-Gr2Wwh83.js +0 -1
- package/apps/ui/dist/client/assets/select-CFim44gT.js +0 -1
- package/apps/ui/dist/client/assets/settings-DqhyDxo2.js +0 -1
- package/apps/ui/dist/client/assets/styles-CMrP9Jb4.css +0 -1
- package/apps/ui/dist/client/assets/threads._threadId-Df5VXIuZ.js +0 -7
- package/apps/ui/dist/client/favicon.ico +0 -0
- package/apps/ui/dist/client/logo192.png +0 -0
- package/apps/ui/dist/client/logo512.png +0 -0
- package/apps/ui/dist/server/assets/_tanstack-start-manifest_v-C0V305Nt.js +0 -99
- package/apps/ui/dist/server/assets/page-header-CxdZM86z.js +0 -25
- package/apps/ui/dist/server/assets/projects._project-CLSohrBp.js +0 -26
|
@@ -40,16 +40,16 @@ export const convertSessionFile = async (target: ExportTarget, options: CodexCli
|
|
|
40
40
|
return null;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
if (options.optimized) {
|
|
44
|
-
return transcriptState.sections.join('\n\n').trimEnd() + '\n';
|
|
45
|
-
}
|
|
46
|
-
|
|
47
43
|
const title = getTitle(target, transcriptState.sessionMeta);
|
|
48
|
-
const metadata = buildMetadataEntries(target, transcriptState.sessionMeta, options);
|
|
49
44
|
const parts = [
|
|
50
45
|
renderDocumentTitle(title, options.outputFormat),
|
|
51
46
|
'',
|
|
52
|
-
|
|
47
|
+
options.includeMetadata
|
|
48
|
+
? renderMetadataBlock(
|
|
49
|
+
buildMetadataEntries(target, transcriptState.sessionMeta, options),
|
|
50
|
+
options.outputFormat,
|
|
51
|
+
)
|
|
52
|
+
: '',
|
|
53
53
|
...transcriptState.sections,
|
|
54
54
|
].filter(Boolean);
|
|
55
55
|
return parts.join('\n').trimEnd() + '\n';
|
|
@@ -69,7 +69,6 @@ export const writeSessionFileExport = async (
|
|
|
69
69
|
assistantModel: target.thread?.model ?? null,
|
|
70
70
|
sections: [],
|
|
71
71
|
sessionMeta: {},
|
|
72
|
-
startedTranscript: false,
|
|
73
72
|
};
|
|
74
73
|
let wroteSection = false;
|
|
75
74
|
|
|
@@ -82,7 +81,7 @@ export const writeSessionFileExport = async (
|
|
|
82
81
|
continue;
|
|
83
82
|
}
|
|
84
83
|
|
|
85
|
-
transcriptStream.write(transform(wroteSection ? `${getSectionSeparator(
|
|
84
|
+
transcriptStream.write(transform(wroteSection ? `${getSectionSeparator()}${block}` : block));
|
|
86
85
|
wroteSection = true;
|
|
87
86
|
}
|
|
88
87
|
await finalizeExportWriteStream(transcriptStream);
|
|
@@ -124,7 +123,6 @@ type CodexTranscriptState = {
|
|
|
124
123
|
assistantModel: string | null;
|
|
125
124
|
sessionMeta: SessionMeta;
|
|
126
125
|
sections: string[];
|
|
127
|
-
startedTranscript: boolean;
|
|
128
126
|
};
|
|
129
127
|
|
|
130
128
|
const collectCodexTranscript = async (
|
|
@@ -136,7 +134,6 @@ const collectCodexTranscript = async (
|
|
|
136
134
|
assistantModel,
|
|
137
135
|
sections: [],
|
|
138
136
|
sessionMeta: {},
|
|
139
|
-
startedTranscript: false,
|
|
140
137
|
};
|
|
141
138
|
|
|
142
139
|
for await (const parsed of readJsonlObjects(sessionFile)) {
|
|
@@ -146,9 +143,7 @@ const collectCodexTranscript = async (
|
|
|
146
143
|
return state;
|
|
147
144
|
};
|
|
148
145
|
|
|
149
|
-
const getSectionSeparator = (
|
|
150
|
-
return options.optimized ? '\n\n' : '\n';
|
|
151
|
-
};
|
|
146
|
+
const getSectionSeparator = () => '\n';
|
|
152
147
|
|
|
153
148
|
const processCodexTranscriptRecord = (
|
|
154
149
|
parsed: Record<string, JsonValue>,
|
|
@@ -181,58 +176,23 @@ const renderCodexTranscriptRecord = (
|
|
|
181
176
|
return '';
|
|
182
177
|
}
|
|
183
178
|
|
|
184
|
-
return options.
|
|
185
|
-
? renderCompactToolBlock(tool, options.outputFormat)
|
|
186
|
-
: renderToolBlock(tool, options.outputFormat);
|
|
179
|
+
return renderToolBlock(tool, options.outputFormat);
|
|
187
180
|
};
|
|
188
181
|
|
|
189
182
|
const processCodexMessageRecord = (message: MessageRecord, options: CodexCliOptions, state: CodexTranscriptState) => {
|
|
190
|
-
if (options.optimized) {
|
|
191
|
-
return processOptimizedCodexMessageRecord(message, options, state);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
183
|
return renderMessageBlock(message, options.outputFormat, state.assistantModel, options.includeCommentary);
|
|
195
184
|
};
|
|
196
185
|
|
|
197
|
-
const processOptimizedCodexMessageRecord = (
|
|
198
|
-
message: MessageRecord,
|
|
199
|
-
options: CodexCliOptions,
|
|
200
|
-
state: CodexTranscriptState,
|
|
201
|
-
) => {
|
|
202
|
-
if (message.role !== 'user' && message.role !== 'assistant') {
|
|
203
|
-
return '';
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if (message.role === 'assistant' && message.phase === 'commentary' && !options.includeCommentary) {
|
|
207
|
-
return '';
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const compact = compactMessageText(message, true);
|
|
211
|
-
if (!compact) {
|
|
212
|
-
return '';
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (!state.startedTranscript) {
|
|
216
|
-
if (shouldSkipOptimizedPrelude(message.role, compact)) {
|
|
217
|
-
return '';
|
|
218
|
-
}
|
|
219
|
-
state.startedTranscript = true;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
return renderCompactBlock(message, compact, options.outputFormat, state.assistantModel);
|
|
223
|
-
};
|
|
224
|
-
|
|
225
186
|
const buildStreamExportPrefix = (target: ExportTarget, sessionMeta: SessionMeta, options: CodexCliOptions) => {
|
|
226
|
-
if (options.
|
|
187
|
+
if (!options.includeMetadata) {
|
|
227
188
|
return '';
|
|
228
189
|
}
|
|
229
190
|
|
|
230
191
|
const title = getTitle(target, sessionMeta);
|
|
231
|
-
const metadata = buildMetadataEntries(target, sessionMeta, options);
|
|
232
192
|
const parts = [
|
|
233
193
|
renderDocumentTitle(title, options.outputFormat),
|
|
234
194
|
'',
|
|
235
|
-
renderMetadataBlock(
|
|
195
|
+
renderMetadataBlock(buildMetadataEntries(target, sessionMeta, options), options.outputFormat),
|
|
236
196
|
]
|
|
237
197
|
.filter(Boolean)
|
|
238
198
|
.join('\n');
|
|
@@ -240,13 +200,6 @@ const buildStreamExportPrefix = (target: ExportTarget, sessionMeta: SessionMeta,
|
|
|
240
200
|
return `${parts}\n`;
|
|
241
201
|
};
|
|
242
202
|
|
|
243
|
-
export const compactMessageText = (message: MessageRecord, optimized: boolean): string => {
|
|
244
|
-
const rawText = extractText(message.content);
|
|
245
|
-
const cleaned = stripPreviewBlock(rawText);
|
|
246
|
-
|
|
247
|
-
return optimized ? optimizePlainText(optimizeRenderedText(cleaned)) : cleaned.trim();
|
|
248
|
-
};
|
|
249
|
-
|
|
250
203
|
export const formatToolOutputSummary = (outputText: string, outputFormat: ExportFormat): string => {
|
|
251
204
|
if (!outputText) {
|
|
252
205
|
return '';
|
|
@@ -308,27 +261,6 @@ const getTitle = (target: ExportTarget, sessionMeta: SessionMeta): string => {
|
|
|
308
261
|
return sessionMeta.id ?? path.basename(target.sessionFile, '.jsonl');
|
|
309
262
|
};
|
|
310
263
|
|
|
311
|
-
const shouldSkipOptimizedPrelude = (role: string, text: string): boolean => {
|
|
312
|
-
if (role !== 'user') {
|
|
313
|
-
return true;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
return (
|
|
317
|
-
text.startsWith('AGENTS.md instructions for ') ||
|
|
318
|
-
text.startsWith('# AGENTS.md instructions for ') ||
|
|
319
|
-
text.startsWith('<permissions instructions>') ||
|
|
320
|
-
text.startsWith('<environment_context>') ||
|
|
321
|
-
text.startsWith('<app-context>') ||
|
|
322
|
-
text.startsWith('<collaboration_mode>') ||
|
|
323
|
-
text.startsWith('<skills_instructions>') ||
|
|
324
|
-
text.startsWith('You are Codex, a coding agent based on GPT-5.') ||
|
|
325
|
-
text.startsWith('Read this before making changes.') ||
|
|
326
|
-
text.includes('Filesystem sandboxing defines which files can be read or written.') ||
|
|
327
|
-
text.includes('approval_policy') ||
|
|
328
|
-
text.includes('base_instructions')
|
|
329
|
-
);
|
|
330
|
-
};
|
|
331
|
-
|
|
332
264
|
const buildMetadataEntries = (
|
|
333
265
|
target: ExportTarget,
|
|
334
266
|
sessionMeta: SessionMeta,
|
|
@@ -585,7 +517,7 @@ const renderMessageBlock = (
|
|
|
585
517
|
return '';
|
|
586
518
|
}
|
|
587
519
|
|
|
588
|
-
const text = cleanExtractedText(extractText(message.content)).trim();
|
|
520
|
+
const text = cleanExtractedText(stripPreviewBlock(extractText(message.content))).trim();
|
|
589
521
|
if (!text || shouldSkipMessage(message.role, text)) {
|
|
590
522
|
return '';
|
|
591
523
|
}
|
|
@@ -606,36 +538,6 @@ const renderToolBlock = (tool: ToolRecord, outputFormat: ExportFormat): string =
|
|
|
606
538
|
return summary ? renderSection('Tool Output', summary, outputFormat) : '';
|
|
607
539
|
};
|
|
608
540
|
|
|
609
|
-
const renderCompactBlock = (
|
|
610
|
-
message: MessageRecord,
|
|
611
|
-
text: string,
|
|
612
|
-
outputFormat: ExportFormat,
|
|
613
|
-
assistantModel: string | null,
|
|
614
|
-
): string => {
|
|
615
|
-
const prefix = message.role === 'user' ? 'U:' : `${formatModelLabel(message.model ?? assistantModel)}:`;
|
|
616
|
-
const lines = text.split('\n');
|
|
617
|
-
const [firstLine, ...rest] = lines;
|
|
618
|
-
|
|
619
|
-
if (rest.length === 0) {
|
|
620
|
-
return `${prefix} ${normalizeCompactLiteral(firstLine, outputFormat)}`;
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
return [
|
|
624
|
-
`${prefix} ${normalizeCompactLiteral(firstLine, outputFormat)}`,
|
|
625
|
-
...rest.map((line) => normalizeCompactLiteral(line, outputFormat)),
|
|
626
|
-
].join('\n');
|
|
627
|
-
};
|
|
628
|
-
|
|
629
|
-
const renderCompactToolBlock = (tool: ToolRecord, outputFormat: ExportFormat): string => {
|
|
630
|
-
if (tool.kind === 'call') {
|
|
631
|
-
const details = formatCompactToolCall(tool, outputFormat);
|
|
632
|
-
return details ? `T: ${details}` : '';
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
const summary = formatCompactToolOutput(tool.outputText ?? '');
|
|
636
|
-
return summary ? `R: ${summary}` : '';
|
|
637
|
-
};
|
|
638
|
-
|
|
639
541
|
const stripPreviewBlock = (text: string): string => {
|
|
640
542
|
const parts = text
|
|
641
543
|
.split(/\n{2,}/)
|
|
@@ -662,49 +564,15 @@ const stripPreviewBlock = (text: string): string => {
|
|
|
662
564
|
return parts.slice(1).join('\n\n');
|
|
663
565
|
};
|
|
664
566
|
|
|
665
|
-
const optimizeRenderedText = (text: string): string => {
|
|
666
|
-
return text
|
|
667
|
-
.replace(/^\*Phase:\s+`[^`]+`\*\s*\n*/gm, '')
|
|
668
|
-
.replace(/^\s*<image\b[^>]*>\s*$/gim, '')
|
|
669
|
-
.replace(/^\s*<\/image>\s*$/gim, '')
|
|
670
|
-
.replace(/^\s*\[Image attached\]\s*$/gim, '')
|
|
671
|
-
.replace(/^#{1,6}\s+/gm, '')
|
|
672
|
-
.replace(/^```[^\n]*\n?/gm, '')
|
|
673
|
-
.replace(/\n```$/gm, '')
|
|
674
|
-
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
|
|
675
|
-
.replace(/^##\s+User\s*$/gm, 'User:')
|
|
676
|
-
.replace(/^##\s+Assistant\s*$/gm, 'Assistant:')
|
|
677
|
-
.replace(/`([^`]+)`/g, '$1')
|
|
678
|
-
.replace(/\*\*([^*]+)\*\*/g, '$1')
|
|
679
|
-
.replace(/\*([^*\n]+)\*/g, '$1')
|
|
680
|
-
.replace(/\n{3,}/g, '\n\n')
|
|
681
|
-
.trim();
|
|
682
|
-
};
|
|
683
|
-
|
|
684
|
-
const optimizePlainText = (text: string): string => {
|
|
685
|
-
const normalized = text
|
|
686
|
-
.replace(/\r/g, '')
|
|
687
|
-
.replace(/^\s*<image\b[^>]*>\s*$/gim, '')
|
|
688
|
-
.replace(/^\s*<\/image>\s*$/gim, '')
|
|
689
|
-
.replace(/^\s*\[Image attached\]\s*$/gim, '')
|
|
690
|
-
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
|
|
691
|
-
.replace(/`([^`]+)`/g, '$1')
|
|
692
|
-
.replace(/\*\*([^*]+)\*\*/g, '$1')
|
|
693
|
-
.replace(/\*([^*\n]+)\*/g, '$1');
|
|
694
|
-
|
|
695
|
-
return normalized
|
|
696
|
-
.split('\n')
|
|
697
|
-
.map((line) => line.replace(/[ \t]+$/g, ''))
|
|
698
|
-
.join('\n')
|
|
699
|
-
.replace(/\n{3,}/g, '\n\n')
|
|
700
|
-
.trim();
|
|
701
|
-
};
|
|
702
|
-
|
|
703
567
|
const shouldSkipMessage = (role: string, text: string): boolean => {
|
|
704
568
|
if (text.startsWith('<environment_context>')) {
|
|
705
569
|
return true;
|
|
706
570
|
}
|
|
707
571
|
|
|
572
|
+
if (text.startsWith('AGENTS.md instructions for ')) {
|
|
573
|
+
return true;
|
|
574
|
+
}
|
|
575
|
+
|
|
708
576
|
if (text.startsWith('# AGENTS.md instructions for ')) {
|
|
709
577
|
return true;
|
|
710
578
|
}
|
|
@@ -725,44 +593,6 @@ const formatToolCallDetails = (tool: ToolRecord, outputFormat: ExportFormat): st
|
|
|
725
593
|
return details.cmd ? `Command: ${formatInlineLiteral(details.cmd, outputFormat)}` : '';
|
|
726
594
|
};
|
|
727
595
|
|
|
728
|
-
const formatCompactToolCall = (tool: ToolRecord, outputFormat: ExportFormat): string => {
|
|
729
|
-
if (tool.name === 'exec_command') {
|
|
730
|
-
const details = parseExecCommandArguments(tool.argumentsText);
|
|
731
|
-
if (!details.cmd) {
|
|
732
|
-
return 'exec_command';
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
const command = formatInlineLiteral(details.cmd, outputFormat);
|
|
736
|
-
return details.workdir ? `exec_command ${command} @ ${details.workdir}` : `exec_command ${command}`;
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
return tool.callId ? `${tool.name} (${tool.callId})` : tool.name;
|
|
740
|
-
};
|
|
741
|
-
|
|
742
|
-
const formatCompactToolOutput = (outputText: string): string => {
|
|
743
|
-
if (!outputText) {
|
|
744
|
-
return '';
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
const lines = outputText
|
|
748
|
-
.split('\n')
|
|
749
|
-
.map((line) => line.trim())
|
|
750
|
-
.filter(Boolean);
|
|
751
|
-
|
|
752
|
-
const exit = lines.find((line) => line.startsWith('Process exited with code '));
|
|
753
|
-
const wall = lines.find((line) => line.startsWith('Wall time: '));
|
|
754
|
-
|
|
755
|
-
if (exit && wall) {
|
|
756
|
-
return `${exit.replace('Process ', '')}; ${wall.toLowerCase()}`;
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
if (exit) {
|
|
760
|
-
return exit.replace('Process ', '');
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
return '';
|
|
764
|
-
};
|
|
765
|
-
|
|
766
596
|
const extractText = (content: JsonValue): string => {
|
|
767
597
|
if (typeof content === 'string') {
|
|
768
598
|
return content;
|
|
@@ -802,7 +632,3 @@ const extractContentPart = (value: JsonValue): string => {
|
|
|
802
632
|
|
|
803
633
|
return text ?? '';
|
|
804
634
|
};
|
|
805
|
-
|
|
806
|
-
const normalizeCompactLiteral = (value: string, outputFormat: ExportFormat): string => {
|
|
807
|
-
return outputFormat === 'md' ? value : value.replace(/`([^`]+)`/g, '$1');
|
|
808
|
-
};
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { Database } from 'bun:sqlite';
|
|
2
|
+
import { copyFile, utimes } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import type { RecoverProjectThreadsResult } from './codex-browser-types';
|
|
5
|
+
import { getPortablePathBasename } from './shared';
|
|
6
|
+
import { runWithSqliteRetry } from './sqlite-retry';
|
|
7
|
+
|
|
8
|
+
type RecoveryThreadRow = {
|
|
9
|
+
cwd: string;
|
|
10
|
+
id: string;
|
|
11
|
+
rollout_path: string;
|
|
12
|
+
thread_source: string | null;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type GlobalState = {
|
|
16
|
+
'active-workspace-roots'?: string[];
|
|
17
|
+
'electron-saved-workspace-roots'?: string[];
|
|
18
|
+
'project-order'?: string[];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const backupFile = async (filePath: string, label: string) => {
|
|
22
|
+
const stamp = new Date()
|
|
23
|
+
.toISOString()
|
|
24
|
+
.replaceAll(':', '')
|
|
25
|
+
.replace(/\.\d{3}Z$/, 'Z')
|
|
26
|
+
.replace('T', '-');
|
|
27
|
+
const backupPath = `${filePath}.bak-${label}-${stamp}`;
|
|
28
|
+
await copyFile(filePath, backupPath);
|
|
29
|
+
return backupPath;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const resolveCodexDirFromDbPath = (dbPath: string) => {
|
|
33
|
+
const dbDir = path.dirname(dbPath);
|
|
34
|
+
return path.basename(dbDir) === 'sqlite' ? path.dirname(dbDir) : dbDir;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const assertRequiredStatePath = async (filePath: string) => {
|
|
38
|
+
if (!(await Bun.file(filePath).exists())) {
|
|
39
|
+
throw new Error(`Required Codex state file not found: ${filePath}`);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const readGlobalState = async (globalStatePath: string) => {
|
|
44
|
+
return (await Bun.file(globalStatePath).json()) as GlobalState;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const writeGlobalState = async (globalStatePath: string, state: GlobalState) => {
|
|
48
|
+
await Bun.write(globalStatePath, JSON.stringify(state));
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const updateGlobalRoots = (state: GlobalState, projectCwds: string[]) => {
|
|
52
|
+
const savedRoots = state['electron-saved-workspace-roots'] ?? [];
|
|
53
|
+
const projectOrder = state['project-order'] ?? [];
|
|
54
|
+
const missingSaved = projectCwds.filter((cwd) => !savedRoots.includes(cwd));
|
|
55
|
+
const missingProjectOrder = projectCwds.filter((cwd) => !projectOrder.includes(cwd));
|
|
56
|
+
|
|
57
|
+
if (missingSaved.length === 0 && missingProjectOrder.length === 0) {
|
|
58
|
+
return {
|
|
59
|
+
projectRootsAdded: 0,
|
|
60
|
+
savedRootsAdded: 0,
|
|
61
|
+
state,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
state['electron-saved-workspace-roots'] = [...savedRoots, ...missingSaved];
|
|
66
|
+
state['project-order'] = [...projectOrder, ...missingProjectOrder];
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
projectRootsAdded: missingProjectOrder.length,
|
|
70
|
+
savedRootsAdded: missingSaved.length,
|
|
71
|
+
state,
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const getProjectTopLevelThreads = (db: Database, projectName: string): RecoveryThreadRow[] => {
|
|
76
|
+
const threads = db
|
|
77
|
+
.query('SELECT id, cwd, rollout_path, thread_source FROM threads WHERE archived = 0')
|
|
78
|
+
.all() as RecoveryThreadRow[];
|
|
79
|
+
return threads.filter((thread) => {
|
|
80
|
+
return getPortablePathBasename(thread.cwd) === projectName && thread.thread_source !== 'subagent';
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const refreshThreadRows = (db: Database, threadIds: string[]) => {
|
|
85
|
+
if (threadIds.length === 0) {
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
90
|
+
const nowMs = Date.now();
|
|
91
|
+
const placeholders = threadIds.map(() => '?').join(', ');
|
|
92
|
+
const statement = db.prepare(`
|
|
93
|
+
UPDATE threads
|
|
94
|
+
SET updated_at = ?1,
|
|
95
|
+
updated_at_ms = ?2,
|
|
96
|
+
has_user_event = 1
|
|
97
|
+
WHERE id IN (${placeholders})
|
|
98
|
+
`);
|
|
99
|
+
const result = statement.run(nowSeconds, nowMs, ...threadIds);
|
|
100
|
+
return Number(result.changes);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const refreshSessionIndex = async (sessionIndexPath: string, threadIds: string[]) => {
|
|
104
|
+
if (threadIds.length === 0) {
|
|
105
|
+
return 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const now = new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
|
|
109
|
+
const threadIdSet = new Set(threadIds);
|
|
110
|
+
const lines = (await Bun.file(sessionIndexPath).text()).split('\n');
|
|
111
|
+
let updated = 0;
|
|
112
|
+
const rewrittenLines: string[] = [];
|
|
113
|
+
|
|
114
|
+
for (const line of lines) {
|
|
115
|
+
if (!line.trim()) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const parsed = JSON.parse(line) as { id?: string; updated_at?: string };
|
|
120
|
+
if (parsed.id && threadIdSet.has(parsed.id)) {
|
|
121
|
+
parsed.updated_at = now;
|
|
122
|
+
updated += 1;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
rewrittenLines.push(JSON.stringify(parsed));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
await Bun.write(sessionIndexPath, `${rewrittenLines.join('\n')}\n`);
|
|
129
|
+
return updated;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const touchRolloutFiles = async (codexDir: string, rolloutPaths: string[]) => {
|
|
133
|
+
const now = new Date();
|
|
134
|
+
let touched = 0;
|
|
135
|
+
|
|
136
|
+
for (const rolloutPath of rolloutPaths) {
|
|
137
|
+
const absolutePath = path.isAbsolute(rolloutPath) ? rolloutPath : path.join(codexDir, rolloutPath);
|
|
138
|
+
if (!(await Bun.file(absolutePath).exists())) {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
await utimes(absolutePath, now, now);
|
|
143
|
+
touched += 1;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return touched;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
export const recoverCodexProjectThreads = async (
|
|
150
|
+
dbPath: string,
|
|
151
|
+
projectName: string,
|
|
152
|
+
): Promise<RecoverProjectThreadsResult> => {
|
|
153
|
+
const codexDir = resolveCodexDirFromDbPath(dbPath);
|
|
154
|
+
const globalStatePath = path.join(codexDir, '.codex-global-state.json');
|
|
155
|
+
const sessionIndexPath = path.join(codexDir, 'session_index.jsonl');
|
|
156
|
+
|
|
157
|
+
await assertRequiredStatePath(dbPath);
|
|
158
|
+
await assertRequiredStatePath(globalStatePath);
|
|
159
|
+
await assertRequiredStatePath(sessionIndexPath);
|
|
160
|
+
|
|
161
|
+
const backups = {
|
|
162
|
+
globalState: await backupFile(globalStatePath, 'recover-project-roots'),
|
|
163
|
+
sessionIndex: await backupFile(sessionIndexPath, 'recover-project-session-index'),
|
|
164
|
+
stateDb: await backupFile(dbPath, 'recover-project-threads'),
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const globalState = await readGlobalState(globalStatePath);
|
|
168
|
+
const db = runWithSqliteRetry({
|
|
169
|
+
action: () => {
|
|
170
|
+
const opened = new Database(dbPath);
|
|
171
|
+
opened.exec('PRAGMA busy_timeout = 5000');
|
|
172
|
+
return opened;
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
const topLevelThreads = getProjectTopLevelThreads(db, projectName);
|
|
178
|
+
const projectCwds = [...new Set(topLevelThreads.map((thread) => thread.cwd))];
|
|
179
|
+
const rootUpdateResult = updateGlobalRoots(globalState, projectCwds);
|
|
180
|
+
await writeGlobalState(globalStatePath, rootUpdateResult.state);
|
|
181
|
+
|
|
182
|
+
const threadIds = topLevelThreads.map((thread) => thread.id);
|
|
183
|
+
const rolloutPaths = topLevelThreads.map((thread) => thread.rollout_path);
|
|
184
|
+
const threadDbRowsUpdated = refreshThreadRows(db, threadIds);
|
|
185
|
+
const sessionIndexRowsUpdated = await refreshSessionIndex(sessionIndexPath, threadIds);
|
|
186
|
+
const rolloutFilesTouched = await touchRolloutFiles(codexDir, rolloutPaths);
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
backups,
|
|
190
|
+
projectName,
|
|
191
|
+
projectRootsAdded: rootUpdateResult.projectRootsAdded,
|
|
192
|
+
resolvedCwds: projectCwds,
|
|
193
|
+
rolloutFilesTouched,
|
|
194
|
+
savedRootsAdded: rootUpdateResult.savedRootsAdded,
|
|
195
|
+
sessionIndexRowsUpdated,
|
|
196
|
+
threadDbRowsUpdated,
|
|
197
|
+
topLevelThreadsFound: threadIds.length,
|
|
198
|
+
};
|
|
199
|
+
} finally {
|
|
200
|
+
db.close();
|
|
201
|
+
}
|
|
202
|
+
};
|