spiracha 1.5.0 → 2.1.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 +114 -205
- package/README.md +94 -269
- package/apps/ui/AGENTS.md +17 -7
- package/apps/ui/README.md +52 -8
- package/apps/ui/components.json +21 -0
- package/apps/ui/package.json +65 -0
- package/apps/ui/src/components/antigravity-conversations-table.tsx +245 -0
- package/apps/ui/src/components/antigravity-keychain-panel.tsx +76 -0
- package/apps/ui/src/components/antigravity-workspaces-table.tsx +63 -0
- package/apps/ui/src/components/app-shell.tsx +125 -0
- package/apps/ui/src/components/breadcrumbs.tsx +71 -0
- package/apps/ui/src/components/claude-code-sessions-table.tsx +150 -0
- package/apps/ui/src/components/claude-code-workspaces-table.tsx +57 -0
- package/apps/ui/src/components/cursor-threads-table.tsx +152 -0
- package/apps/ui/src/components/cursor-workspaces-table.tsx +140 -0
- package/apps/ui/src/components/data-table.tsx +243 -0
- package/apps/ui/src/components/delete-confirm-dialog.tsx +88 -0
- package/apps/ui/src/components/export-dialog.tsx +174 -0
- package/apps/ui/src/components/grok-sessions-table.tsx +146 -0
- package/apps/ui/src/components/grok-workspaces-table.tsx +53 -0
- package/apps/ui/src/components/json-panel.tsx +17 -0
- package/apps/ui/src/components/kiro-sessions-table.tsx +148 -0
- package/apps/ui/src/components/kiro-workspaces-table.tsx +53 -0
- package/apps/ui/src/components/list-search-input.tsx +18 -0
- package/apps/ui/src/components/loading-panel.tsx +23 -0
- package/apps/ui/src/components/metadata-section.tsx +31 -0
- package/apps/ui/src/components/metric-card.tsx +19 -0
- package/apps/ui/src/components/opencode-sessions-table.tsx +164 -0
- package/apps/ui/src/components/opencode-workspaces-table.tsx +57 -0
- package/apps/ui/src/components/page-header.tsx +39 -0
- package/apps/ui/src/components/projects-loading-state.tsx +74 -0
- package/apps/ui/src/components/projects-table.tsx +108 -0
- package/apps/ui/src/components/qoder-sessions-table.tsx +133 -0
- package/apps/ui/src/components/qoder-workspaces-table.tsx +57 -0
- package/apps/ui/src/components/recent-threads-list.tsx +52 -0
- package/apps/ui/src/components/reload-error-panel.tsx +20 -0
- package/apps/ui/src/components/selection-actions-toolbar.tsx +80 -0
- package/apps/ui/src/components/text-document-panel.tsx +19 -0
- package/apps/ui/src/components/theme-toggle.tsx +48 -0
- package/apps/ui/src/components/threads-table.tsx +165 -0
- package/apps/ui/src/components/transcript-view.tsx +648 -0
- package/apps/ui/src/components/ui/alert-dialog.tsx +160 -0
- package/apps/ui/src/components/ui/badge.tsx +45 -0
- package/apps/ui/src/components/ui/button.tsx +62 -0
- package/apps/ui/src/components/ui/checkbox.tsx +29 -0
- package/apps/ui/src/components/ui/dialog.tsx +137 -0
- package/apps/ui/src/components/ui/dropdown-menu.tsx +217 -0
- package/apps/ui/src/components/ui/input.tsx +21 -0
- package/apps/ui/src/components/ui/scroll-area.tsx +46 -0
- package/apps/ui/src/components/ui/select.tsx +163 -0
- package/apps/ui/src/components/ui/separator.tsx +28 -0
- package/apps/ui/src/components/ui/sheet.tsx +107 -0
- package/apps/ui/src/components/ui/skeleton.tsx +7 -0
- package/apps/ui/src/components/ui/table.tsx +76 -0
- package/apps/ui/src/components/ui/tabs.tsx +71 -0
- package/apps/ui/src/components/ui/tooltip.tsx +44 -0
- package/apps/ui/src/integrations/tanstack-query/devtools.tsx +6 -0
- package/apps/ui/src/integrations/tanstack-query/root-provider.tsx +10 -0
- package/apps/ui/src/lib/antigravity-conversation-state.ts +41 -0
- package/apps/ui/src/lib/antigravity-queries.ts +33 -0
- package/apps/ui/src/lib/antigravity-server.ts +273 -0
- package/apps/ui/src/lib/antigravity-transcript-events.ts +343 -0
- package/apps/ui/src/lib/claude-code-queries.ts +34 -0
- package/apps/ui/src/lib/claude-code-server.ts +243 -0
- package/apps/ui/src/lib/claude-code-transcript-events.ts +161 -0
- package/apps/ui/src/lib/codex-queries.ts +82 -0
- package/apps/ui/src/lib/codex-server.ts +291 -0
- package/apps/ui/src/lib/cursor-queries.ts +37 -0
- package/apps/ui/src/lib/cursor-server.ts +315 -0
- package/apps/ui/src/lib/cursor-transcript-events.ts +229 -0
- package/apps/ui/src/lib/download.ts +166 -0
- package/apps/ui/src/lib/formatters.ts +147 -0
- package/apps/ui/src/lib/grok-queries.ts +22 -0
- package/apps/ui/src/lib/grok-server.ts +169 -0
- package/apps/ui/src/lib/grok-transcript-events.ts +149 -0
- package/apps/ui/src/lib/kiro-queries.ts +22 -0
- package/apps/ui/src/lib/kiro-server.ts +151 -0
- package/apps/ui/src/lib/kiro-transcript-events.ts +113 -0
- package/apps/ui/src/lib/opencode-queries.ts +37 -0
- package/apps/ui/src/lib/opencode-server.ts +151 -0
- package/apps/ui/src/lib/opencode-transcript-events.ts +221 -0
- package/apps/ui/src/lib/package-metadata.ts +16 -0
- package/apps/ui/src/lib/path-utils.ts +13 -0
- package/apps/ui/src/lib/qoder-queries.ts +22 -0
- package/apps/ui/src/lib/qoder-server.ts +135 -0
- package/apps/ui/src/lib/qoder-transcript-events.ts +139 -0
- package/apps/ui/src/lib/route-search.ts +182 -0
- package/apps/ui/src/lib/settings-store.tsx +59 -0
- package/apps/ui/src/lib/source-session-export-server.ts +139 -0
- package/apps/ui/src/lib/text-filter.ts +22 -0
- package/apps/ui/src/lib/thread-id.ts +3 -0
- package/apps/ui/src/lib/thread-transcript-load.ts +15 -0
- package/apps/ui/src/lib/thread-transcript-stats.ts +57 -0
- package/apps/ui/src/lib/utils.ts +7 -0
- package/apps/ui/src/lib/workspace-delete-navigation.ts +12 -0
- package/apps/ui/src/routeTree.gen.ts +837 -0
- package/apps/ui/src/router.tsx +20 -0
- package/apps/ui/src/routes/$threadId.tsx +22 -0
- package/apps/ui/src/routes/__root.tsx +127 -0
- package/apps/ui/src/routes/analytics.tsx +143 -0
- package/apps/ui/src/routes/antigravity-conversations.$conversationId.tsx +504 -0
- package/apps/ui/src/routes/antigravity.$workspaceKey.tsx +365 -0
- package/apps/ui/src/routes/antigravity.index.tsx +51 -0
- package/apps/ui/src/routes/api.v1.conversation-query.ts +12 -0
- package/apps/ui/src/routes/api.v1.conversations.$source.$id.export.ts +12 -0
- package/apps/ui/src/routes/api.v1.conversations.$source.$id.ts +16 -0
- package/apps/ui/src/routes/api.v1.conversations.delete.ts +12 -0
- package/apps/ui/src/routes/api.v1.conversations.export.ts +12 -0
- package/apps/ui/src/routes/api.v1.conversations.ts +12 -0
- package/apps/ui/src/routes/api.v1.resolve.ts +12 -0
- package/apps/ui/src/routes/api.v1.sources.ts +12 -0
- package/apps/ui/src/routes/claude-code-sessions.$sessionId.tsx +468 -0
- package/apps/ui/src/routes/claude-code.$workspaceKey.tsx +274 -0
- package/apps/ui/src/routes/claude-code.index.tsx +48 -0
- package/apps/ui/src/routes/codex.$project.tsx +390 -0
- package/apps/ui/src/routes/codex.index.tsx +116 -0
- package/apps/ui/src/routes/cursor-threads.$composerId.tsx +435 -0
- package/apps/ui/src/routes/cursor.$workspaceKey.tsx +426 -0
- package/apps/ui/src/routes/cursor.index.tsx +120 -0
- package/apps/ui/src/routes/grok-sessions.$sessionId.tsx +388 -0
- package/apps/ui/src/routes/grok.$workspaceKey.tsx +301 -0
- package/apps/ui/src/routes/grok.index.tsx +48 -0
- package/apps/ui/src/routes/index.tsx +124 -0
- package/apps/ui/src/routes/kiro-sessions.$sessionId.tsx +396 -0
- package/apps/ui/src/routes/kiro.$workspaceKey.tsx +303 -0
- package/apps/ui/src/routes/kiro.index.tsx +48 -0
- package/apps/ui/src/routes/opencode-sessions.$sessionId.tsx +420 -0
- package/apps/ui/src/routes/opencode.$workspaceKey.tsx +327 -0
- package/apps/ui/src/routes/opencode.index.tsx +61 -0
- package/apps/ui/src/routes/qoder-sessions.$sessionId.tsx +344 -0
- package/apps/ui/src/routes/qoder.$workspaceKey.tsx +186 -0
- package/apps/ui/src/routes/qoder.index.tsx +48 -0
- package/apps/ui/src/routes/settings.tsx +91 -0
- package/apps/ui/src/routes/threads.$threadId.tsx +1103 -0
- package/apps/ui/src/styles.css +122 -0
- package/apps/ui/tsconfig.json +29 -0
- package/apps/ui/vite.config.ts +84 -0
- package/bin/spiracha.ts +32 -0
- package/package.json +54 -80
- package/src/client.ts +423 -0
- package/src/lib/antigravity-db.ts +414 -44
- package/src/lib/antigravity-exporter-types.ts +4 -0
- package/src/lib/antigravity-projects.ts +201 -0
- package/src/lib/claude-code-db.ts +498 -32
- package/src/lib/claude-code-exporter-types.ts +5 -0
- package/src/lib/claude-code-transcript-phase.ts +60 -1
- package/src/lib/claude-code-transcript.ts +8 -5
- package/src/lib/codex-analytics.ts +1 -1
- package/src/lib/codex-browser-db.ts +410 -74
- package/src/lib/codex-browser-export.ts +29 -41
- package/src/lib/codex-browser-types.ts +1 -1
- package/src/lib/codex-thread-cache.ts +41 -13
- package/src/lib/codex-thread-parser.ts +86 -35
- package/src/lib/{codex-exporter-types.ts → codex-thread-types.ts} +2 -30
- package/src/lib/codex-transcript-filter.ts +31 -0
- package/src/lib/{codex-exporter-transcript.ts → codex-transcript-renderer.ts} +104 -48
- package/src/lib/concurrency.ts +41 -0
- package/src/lib/conversation-api.ts +1172 -0
- package/src/lib/conversation-data/adapter-helpers.ts +110 -0
- package/src/lib/conversation-data/antigravity-adapter.ts +167 -0
- package/src/lib/conversation-data/claude-code-adapter.ts +206 -0
- package/src/lib/conversation-data/codex-adapter.ts +269 -0
- package/src/lib/conversation-data/cursor-adapter.ts +205 -0
- package/src/lib/conversation-data/grok-adapter.ts +210 -0
- package/src/lib/conversation-data/index.ts +386 -0
- package/src/lib/conversation-data/kiro-adapter.ts +174 -0
- package/src/lib/conversation-data/message-selector.ts +37 -0
- package/src/lib/conversation-data/opencode-adapter.ts +202 -0
- package/src/lib/conversation-data/path-match.ts +79 -0
- package/src/lib/conversation-data/qoder-adapter.ts +255 -0
- package/src/lib/conversation-data/types.ts +159 -0
- package/src/lib/conversation-zip-export.ts +57 -0
- package/src/lib/cursor-db.ts +100 -25
- package/src/lib/cursor-exporter-types.ts +0 -19
- package/src/lib/grok-db.ts +1026 -0
- package/src/lib/grok-exporter-types.ts +110 -0
- package/src/lib/grok-transcript-phase.ts +52 -0
- package/src/lib/grok-transcript.ts +154 -0
- package/src/lib/kiro-db.ts +52 -1
- package/src/lib/model-label.ts +11 -3
- package/src/lib/opencode-db.ts +598 -55
- package/src/lib/opencode-transcript.ts +8 -5
- package/src/lib/qoder-acp-client.ts +268 -0
- package/src/lib/qoder-db.ts +1601 -0
- package/src/lib/qoder-exporter-types.ts +114 -0
- package/src/lib/qoder-transcript-phase.ts +41 -0
- package/src/lib/qoder-transcript.ts +112 -0
- package/src/lib/shared.ts +12 -0
- package/src/lib/transcript-load-limiter.ts +82 -0
- package/src/lib/ui-cache.ts +43 -17
- package/src/lib/ui-export-archive.ts +60 -0
- package/apps/ui/dist/client/assets/_threadId-CAIeH5mq.js +0 -1
- package/apps/ui/dist/client/assets/analytics-DOy1b6Sh.js +0 -1
- package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-DqySVO2K.js +0 -7
- package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-KAcXtNxI.js +0 -1
- package/apps/ui/dist/client/assets/antigravity-keychain-panel-B77qJz6f.js +0 -1
- package/apps/ui/dist/client/assets/antigravity._workspaceKey-B3NM6gRH.js +0 -1
- package/apps/ui/dist/client/assets/antigravity._workspaceKey-ByitGWki.js +0 -1
- package/apps/ui/dist/client/assets/antigravity.index-DP9JC2P9.js +0 -1
- package/apps/ui/dist/client/assets/antigravity.index-DgW25FEV.js +0 -1
- package/apps/ui/dist/client/assets/badge-C3i4OKs8.js +0 -1
- package/apps/ui/dist/client/assets/checkbox-CdHitXWQ.js +0 -1
- package/apps/ui/dist/client/assets/claude-code-sessions._sessionId-8pQ55ci2.js +0 -1
- package/apps/ui/dist/client/assets/claude-code-sessions._sessionId-DVz5UmtU.js +0 -1
- package/apps/ui/dist/client/assets/claude-code._workspaceKey-BBKskSum.js +0 -1
- package/apps/ui/dist/client/assets/claude-code._workspaceKey-OkikMUFs.js +0 -1
- package/apps/ui/dist/client/assets/claude-code.index-Cwu76dbb.js +0 -1
- package/apps/ui/dist/client/assets/claude-code.index-D6eMd08t.js +0 -1
- package/apps/ui/dist/client/assets/createServerFn-B-KkwmHU.js +0 -3
- package/apps/ui/dist/client/assets/cursor-threads._composerId-CmBFclK8.js +0 -1
- package/apps/ui/dist/client/assets/cursor-threads._composerId-hBWlP1D-.js +0 -1
- package/apps/ui/dist/client/assets/cursor._workspaceKey-BFPkIRi-.js +0 -1
- package/apps/ui/dist/client/assets/cursor._workspaceKey-CaAZjGmm.js +0 -1
- package/apps/ui/dist/client/assets/cursor.index-8y4o20kb.js +0 -2
- package/apps/ui/dist/client/assets/cursor.index-CrnMwDCi.js +0 -1
- package/apps/ui/dist/client/assets/data-table-Br78_dk3.js +0 -4
- package/apps/ui/dist/client/assets/delete-confirm-dialog-Bxs07u-j.js +0 -7
- package/apps/ui/dist/client/assets/dist-B9pyYCVU.js +0 -1
- package/apps/ui/dist/client/assets/dist-BrJhN2A0.js +0 -5
- package/apps/ui/dist/client/assets/dist-Cmd3AIfD.js +0 -1
- package/apps/ui/dist/client/assets/dist-DvMS2965.js +0 -1
- package/apps/ui/dist/client/assets/download-Bf0QJKD3.js +0 -1
- package/apps/ui/dist/client/assets/dropdown-menu-D5cvPaZt.js +0 -1
- package/apps/ui/dist/client/assets/es2015-ad6040dw.js +0 -41
- package/apps/ui/dist/client/assets/export-dialog-Dr8d2MqD.js +0 -1
- package/apps/ui/dist/client/assets/formatters-BtqyrX_c.js +0 -1
- package/apps/ui/dist/client/assets/index-C-STgIH4.js +0 -166
- package/apps/ui/dist/client/assets/jsx-runtime-DGeXAQPT.js +0 -1
- package/apps/ui/dist/client/assets/kiro-sessions._sessionId-CKgfJ7GR.js +0 -1
- package/apps/ui/dist/client/assets/kiro-sessions._sessionId-zvIJHtQF.js +0 -1
- package/apps/ui/dist/client/assets/kiro._workspaceKey-DhVuJuZB.js +0 -1
- package/apps/ui/dist/client/assets/kiro._workspaceKey-QArsjkTY.js +0 -1
- package/apps/ui/dist/client/assets/kiro.index-C-fr4m7i.js +0 -1
- package/apps/ui/dist/client/assets/kiro.index-OYgAdtZd.js +0 -1
- package/apps/ui/dist/client/assets/metric-card-Dplm0ZiM.js +0 -1
- package/apps/ui/dist/client/assets/opencode-sessions._sessionId-DLjiTst-.js +0 -1
- package/apps/ui/dist/client/assets/opencode-sessions._sessionId-biBwrRbt.js +0 -8
- package/apps/ui/dist/client/assets/opencode._workspaceKey-C7OIzMOJ.js +0 -1
- package/apps/ui/dist/client/assets/opencode._workspaceKey-sBakX0oI.js +0 -1
- package/apps/ui/dist/client/assets/opencode.index-Bl_kNs9N.js +0 -1
- package/apps/ui/dist/client/assets/opencode.index-CN53bVmb.js +0 -1
- package/apps/ui/dist/client/assets/page-header-Cwa3p6Tl.js +0 -1
- package/apps/ui/dist/client/assets/preload-helper-B8fFrdxC.js +0 -1
- package/apps/ui/dist/client/assets/projects._project-D8WloxQN.js +0 -1
- package/apps/ui/dist/client/assets/projects._project-DSDwH4NF.js +0 -1
- package/apps/ui/dist/client/assets/projects.index-5xRtuJdX.js +0 -3
- package/apps/ui/dist/client/assets/projects.index-DEfhWqa8.js +0 -1
- package/apps/ui/dist/client/assets/react-dom-DL96Jor4.js +0 -1
- package/apps/ui/dist/client/assets/refresh-ccw-BgKjOiA6.js +0 -1
- package/apps/ui/dist/client/assets/reload-error-panel-AHqu0akK.js +0 -1
- package/apps/ui/dist/client/assets/routes-DnzF_9CX.js +0 -1
- package/apps/ui/dist/client/assets/scroll-text-fM0sMOB3.js +0 -1
- package/apps/ui/dist/client/assets/select-CYF6pF37.js +0 -1
- package/apps/ui/dist/client/assets/settings-Dx70jgP_.js +0 -1
- package/apps/ui/dist/client/assets/sqlite-error-B6uZjUe8.js +0 -1
- package/apps/ui/dist/client/assets/styles-B7Zi9EFA.css +0 -1
- package/apps/ui/dist/client/assets/tabs-CmuhkT-c.js +0 -7
- package/apps/ui/dist/client/assets/text-filter-DVAZySUS.js +0 -2
- package/apps/ui/dist/client/assets/thread-transcript-stats-BUNz6u7F.js +0 -1
- package/apps/ui/dist/client/assets/threads._threadId-BB9tTsV4.js +0 -1
- package/apps/ui/dist/client/assets/threads._threadId-BMRSFow3.js +0 -1
- package/apps/ui/dist/client/assets/useMutation-BSFult2i.js +0 -1
- package/apps/ui/dist/client/assets/useQuery-CjINKZf1.js +0 -1
- package/apps/ui/dist/server/assets/_tanstack-start-manifest_v-CQ8Jjs3B.js +0 -387
- package/apps/ui/dist/server/assets/_threadId-B6SrBR9E.js +0 -6
- package/apps/ui/dist/server/assets/analytics-CYP_LT5Y.js +0 -16
- package/apps/ui/dist/server/assets/analytics-CtpOMVMO.js +0 -146
- package/apps/ui/dist/server/assets/antigravity-conversation-state-HgzS302O.js +0 -16
- package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-BKOyL2T6.js +0 -613
- package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-D426O-64.js +0 -11
- package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-eS87Dx7g.js +0 -20
- package/apps/ui/dist/server/assets/antigravity-db-Bh8_U9uw.js +0 -580
- package/apps/ui/dist/server/assets/antigravity-keychain-DOiuHDwK.js +0 -126
- package/apps/ui/dist/server/assets/antigravity-keychain-panel-CC7FLmAK.js +0 -55
- package/apps/ui/dist/server/assets/antigravity-queries-CGrJO9Vr.js +0 -37
- package/apps/ui/dist/server/assets/antigravity-server-CHRjVFqN.js +0 -114
- package/apps/ui/dist/server/assets/antigravity._workspaceKey-3m_MzNFA.js +0 -11
- package/apps/ui/dist/server/assets/antigravity._workspaceKey-B0QeYUOh.js +0 -28
- package/apps/ui/dist/server/assets/antigravity._workspaceKey-C3kP-qyN.js +0 -210
- package/apps/ui/dist/server/assets/antigravity.index-CPCYkqCi.js +0 -104
- package/apps/ui/dist/server/assets/antigravity.index-DudTB3Tq.js +0 -11
- package/apps/ui/dist/server/assets/badge-EvdhKK_Z.js +0 -26
- package/apps/ui/dist/server/assets/button-CmTDnzOn.js +0 -46
- package/apps/ui/dist/server/assets/checkbox-C0hovF41.js +0 -19
- package/apps/ui/dist/server/assets/claude-code-db-Ik3VStKZ.js +0 -361
- package/apps/ui/dist/server/assets/claude-code-queries-Cgzi4K-e.js +0 -37
- package/apps/ui/dist/server/assets/claude-code-server-PfraClXS.js +0 -72
- package/apps/ui/dist/server/assets/claude-code-sessions._sessionId-C55oe6zj.js +0 -18
- package/apps/ui/dist/server/assets/claude-code-sessions._sessionId-D1RYYi3j.js +0 -473
- package/apps/ui/dist/server/assets/claude-code-sessions._sessionId-DSvPLEUL.js +0 -11
- package/apps/ui/dist/server/assets/claude-code-transcript-C-bxh3cS.js +0 -139
- package/apps/ui/dist/server/assets/claude-code-transcript-phase-DmUMc8VU.js +0 -10
- package/apps/ui/dist/server/assets/claude-code._workspaceKey-B5Xg_-tE.js +0 -28
- package/apps/ui/dist/server/assets/claude-code._workspaceKey-BPlzyXPa.js +0 -11
- package/apps/ui/dist/server/assets/claude-code._workspaceKey-DqDve2wl.js +0 -191
- package/apps/ui/dist/server/assets/claude-code.index-B7yYR7dD.js +0 -92
- package/apps/ui/dist/server/assets/claude-code.index-BUHThG1W.js +0 -11
- package/apps/ui/dist/server/assets/codex-queries-CtgeZ7VL.js +0 -99
- package/apps/ui/dist/server/assets/codex-server-CGXU4H8-.js +0 -2294
- package/apps/ui/dist/server/assets/concurrency-VXtYvlGh.js +0 -18
- package/apps/ui/dist/server/assets/createServerRpc-CF_DEwnm.js +0 -12
- package/apps/ui/dist/server/assets/createSsrRpc-C754NPki.js +0 -16
- package/apps/ui/dist/server/assets/cursor-db-CZnYy7aT.js +0 -830
- package/apps/ui/dist/server/assets/cursor-exporter-types-CI3goo-c.js +0 -34
- package/apps/ui/dist/server/assets/cursor-queries-NCIM0Nat.js +0 -67
- package/apps/ui/dist/server/assets/cursor-recovery-CXpO8KcD.js +0 -361
- package/apps/ui/dist/server/assets/cursor-server-DNu52A3K.js +0 -213
- package/apps/ui/dist/server/assets/cursor-threads._composerId-BB0Y_Mao.js +0 -11
- package/apps/ui/dist/server/assets/cursor-threads._composerId-Ckn6ItHI.js +0 -582
- package/apps/ui/dist/server/assets/cursor-threads._composerId-p9-ARAzc.js +0 -18
- package/apps/ui/dist/server/assets/cursor-transcript-B7tGnqtn.js +0 -125
- package/apps/ui/dist/server/assets/cursor._workspaceKey-CZuohIUL.js +0 -28
- package/apps/ui/dist/server/assets/cursor._workspaceKey-DPp5JSjy.js +0 -401
- package/apps/ui/dist/server/assets/cursor._workspaceKey-nmg3YIOQ.js +0 -11
- package/apps/ui/dist/server/assets/cursor.index-CcsX7DG0.js +0 -11
- package/apps/ui/dist/server/assets/cursor.index-DkWT-mxw.js +0 -189
- package/apps/ui/dist/server/assets/data-table-Cdct823O.js +0 -189
- package/apps/ui/dist/server/assets/delete-confirm-dialog-BcBNCxWB.js +0 -144
- package/apps/ui/dist/server/assets/download-DMmiy1xf.js +0 -92
- package/apps/ui/dist/server/assets/dropdown-menu-Dy_9t6TN.js +0 -36
- package/apps/ui/dist/server/assets/empty-plugin-adapters-B3lHh1La.js +0 -5
- package/apps/ui/dist/server/assets/export-dialog-D88ze9Gy.js +0 -240
- package/apps/ui/dist/server/assets/formatters-FJaGZgJk.js +0 -91
- package/apps/ui/dist/server/assets/kiro-db--fXrED_-.js +0 -552
- package/apps/ui/dist/server/assets/kiro-queries-CzNlM2ok.js +0 -37
- package/apps/ui/dist/server/assets/kiro-server-9WRxg2Au.js +0 -72
- package/apps/ui/dist/server/assets/kiro-sessions._sessionId-CRY2c7YO.js +0 -11
- package/apps/ui/dist/server/assets/kiro-sessions._sessionId-F1UlwxmR.js +0 -449
- package/apps/ui/dist/server/assets/kiro-sessions._sessionId-bvTulT7C.js +0 -18
- package/apps/ui/dist/server/assets/kiro-transcript-DoekQBaH.js +0 -112
- package/apps/ui/dist/server/assets/kiro-transcript-phase-CJG40nQg.js +0 -27
- package/apps/ui/dist/server/assets/kiro._workspaceKey-BDe9sQwo.js +0 -11
- package/apps/ui/dist/server/assets/kiro._workspaceKey-CJsjgNwO.js +0 -28
- package/apps/ui/dist/server/assets/kiro._workspaceKey-D_-4Qb7y.js +0 -193
- package/apps/ui/dist/server/assets/kiro.index-C__-UROk.js +0 -11
- package/apps/ui/dist/server/assets/kiro.index-CmTbqeYj.js +0 -93
- package/apps/ui/dist/server/assets/loading-panel-BGFnWseS.js +0 -27
- package/apps/ui/dist/server/assets/metric-card-ByEeLu0r.js +0 -23
- package/apps/ui/dist/server/assets/model-label-B1NWGc65.js +0 -13
- package/apps/ui/dist/server/assets/opencode-db-BM7KjOzc.js +0 -397
- package/apps/ui/dist/server/assets/opencode-queries-BeNYXnQv.js +0 -50
- package/apps/ui/dist/server/assets/opencode-server-BpQ_kRze.js +0 -72
- package/apps/ui/dist/server/assets/opencode-sessions._sessionId-CGQ-t0vD.js +0 -518
- package/apps/ui/dist/server/assets/opencode-sessions._sessionId-D8Q7in_Z.js +0 -18
- package/apps/ui/dist/server/assets/opencode-sessions._sessionId-DKOvi0lV.js +0 -11
- package/apps/ui/dist/server/assets/opencode-think-tags-CAoD-EcZ.js +0 -87
- package/apps/ui/dist/server/assets/opencode-transcript-BNTcGNym.js +0 -141
- package/apps/ui/dist/server/assets/opencode-transcript-phase-OqLkiQD0.js +0 -32
- package/apps/ui/dist/server/assets/opencode._workspaceKey-BdqcWHw1.js +0 -11
- package/apps/ui/dist/server/assets/opencode._workspaceKey-CfH96HTT.js +0 -28
- package/apps/ui/dist/server/assets/opencode._workspaceKey-DdbHPDrO.js +0 -207
- package/apps/ui/dist/server/assets/opencode.index-BbVzkJXP.js +0 -11
- package/apps/ui/dist/server/assets/opencode.index-Vh_Gq_1L.js +0 -93
- package/apps/ui/dist/server/assets/page-header-VNSaM3xd.js +0 -29
- package/apps/ui/dist/server/assets/path-transforms-DL2IwtYd.js +0 -31
- package/apps/ui/dist/server/assets/projects._project-4io5LO0E.js +0 -20
- package/apps/ui/dist/server/assets/projects._project-Bshqk7JA.js +0 -12
- package/apps/ui/dist/server/assets/projects._project-D6ZoGhJ8.js +0 -395
- package/apps/ui/dist/server/assets/projects.index-BLXOx5eL.js +0 -12
- package/apps/ui/dist/server/assets/projects.index-BkLiF2FF.js +0 -182
- package/apps/ui/dist/server/assets/projects.index-DesYXwfi.js +0 -14
- package/apps/ui/dist/server/assets/reload-error-panel-BJMxY3U1.js +0 -25
- package/apps/ui/dist/server/assets/route-search-ts4W9MJ9.js +0 -38
- package/apps/ui/dist/server/assets/router-Sac2DGk6.js +0 -518
- package/apps/ui/dist/server/assets/routes-Bsact1uB.js +0 -184
- package/apps/ui/dist/server/assets/routes-BzkJgq4W.js +0 -34
- package/apps/ui/dist/server/assets/select-GW76p-ld.js +0 -76
- package/apps/ui/dist/server/assets/settings-OayxIYQQ.js +0 -100
- package/apps/ui/dist/server/assets/settings-store-DpEJEQ7M.js +0 -52
- package/apps/ui/dist/server/assets/shared-DyhChtHf.js +0 -137
- package/apps/ui/dist/server/assets/source-session-export-server-DNutCawb.js +0 -38
- package/apps/ui/dist/server/assets/sqlite-error-LZDrnxdd.js +0 -13
- package/apps/ui/dist/server/assets/start-B_HFwkkk.js +0 -4
- package/apps/ui/dist/server/assets/tabs-DkaChNVg.js +0 -501
- package/apps/ui/dist/server/assets/text-filter-CGKxMCKt.js +0 -36
- package/apps/ui/dist/server/assets/thread-transcript-stats-Dh34mt2u.js +0 -45
- package/apps/ui/dist/server/assets/threads._threadId-BSSK4nkI.js +0 -26
- package/apps/ui/dist/server/assets/threads._threadId-BeXEc4Lo.js +0 -648
- package/apps/ui/dist/server/assets/threads._threadId-Ci0S4mKU.js +0 -18
- package/apps/ui/dist/server/assets/ui-export-files-BHLX9bhN.js +0 -83
- package/apps/ui/dist/server/assets/utils-C_uf36nf.js +0 -8
- package/apps/ui/dist/server/server.js +0 -5895
- package/bin/codex-chats-claude.js +0 -5
- package/bin/codex-chats.js +0 -5
- package/bin/spiracha.js +0 -5
- package/src/export-chats.ts +0 -120
- package/src/export-claude.ts +0 -36
- package/src/export-cursor.ts +0 -244
- package/src/lib/claude-exporter.ts +0 -864
- package/src/lib/codex-exporter-cli.ts +0 -277
- package/src/lib/codex-exporter-db.ts +0 -319
- package/src/lib/codex-exporter.ts +0 -115
- package/src/lib/cursor-exporter.ts +0 -266
- package/src/lib/interactive-cli.ts +0 -433
- package/src/lib/native-open.ts +0 -54
- package/src/mcp-server.ts +0 -137
- package/src/spiracha.ts +0 -116
- package/src/ui-cli.ts +0 -310
- /package/apps/ui/{dist/client → public}/icon.svg +0 -0
- /package/apps/ui/{dist/client → public}/manifest.json +0 -0
- /package/apps/ui/{dist/client → public}/robots.txt +0 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import type { ParsedCodexTranscript } from '@spiracha/lib/codex-browser-types';
|
|
2
|
+
import { createServerFn } from '@tanstack/react-start';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
|
|
5
|
+
const projectSchema = z.object({
|
|
6
|
+
project: z.string().min(1),
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const deleteProjectSchema = z.object({
|
|
10
|
+
deleteSessionFiles: z.boolean().default(false),
|
|
11
|
+
project: z.string().min(1),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const threadSchema = z.object({
|
|
15
|
+
threadId: z.string().min(1),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const transcriptFiltersSchema = z.object({
|
|
19
|
+
showCommentary: z.boolean(),
|
|
20
|
+
showExtraEvents: z.boolean(),
|
|
21
|
+
showToolCalls: z.boolean(),
|
|
22
|
+
showUserMessages: z.boolean(),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const threadSnapshotSchema = z.object({
|
|
26
|
+
filters: transcriptFiltersSchema.optional(),
|
|
27
|
+
threadId: z.string().min(1),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const deleteThreadSchema = z.object({
|
|
31
|
+
deleteSessionFiles: z.boolean().default(false),
|
|
32
|
+
threadId: z.string().min(1),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const deleteThreadsSchema = z.object({
|
|
36
|
+
deleteSessionFiles: z.boolean().default(false),
|
|
37
|
+
threadIds: z.array(z.string().min(1)).min(1),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const analyticsSchema = z.object({
|
|
41
|
+
project: z.string().min(1).nullable(),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const exportSchema = z.object({
|
|
45
|
+
convertToProjectRoot: z.boolean(),
|
|
46
|
+
includeCommentary: z.boolean(),
|
|
47
|
+
includeMetadata: z.boolean(),
|
|
48
|
+
includeTools: z.boolean(),
|
|
49
|
+
outputFormat: z.enum(['md', 'txt']),
|
|
50
|
+
redactUsername: z.boolean(),
|
|
51
|
+
threadId: z.string().min(1),
|
|
52
|
+
zipArchive: z.boolean().default(false),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const exportThreadsSchema = z.object({
|
|
56
|
+
convertToProjectRoot: z.boolean(),
|
|
57
|
+
includeCommentary: z.boolean(),
|
|
58
|
+
includeMetadata: z.boolean(),
|
|
59
|
+
includeTools: z.boolean(),
|
|
60
|
+
outputFormat: z.enum(['md', 'txt']),
|
|
61
|
+
redactUsername: z.boolean(),
|
|
62
|
+
threadIds: z.array(z.string().min(1)).min(1),
|
|
63
|
+
zipArchive: z.boolean().default(true),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const getDbPath = async () => {
|
|
67
|
+
const configuredDbPath = process.env.SPIRACHA_CODEX_DB?.trim();
|
|
68
|
+
if (configuredDbPath) {
|
|
69
|
+
return configuredDbPath;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const { resolveCodexThreadDbPath } = await import('@spiracha/lib/codex-browser-db');
|
|
73
|
+
return resolveCodexThreadDbPath();
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const isMissingFileError = (error: unknown) => {
|
|
77
|
+
return error instanceof Error && /ENOENT|no such file/i.test(error.message);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const logCodexThreadLoad = (event: string, details: Record<string, unknown>) => {
|
|
81
|
+
console.info(`[spiracha:codex-thread] ${event}`, details);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export const getDashboardSummaryFn = createServerFn({ method: 'GET' }).handler(async () => {
|
|
85
|
+
const { getCodexDashboardSummary } = await import('@spiracha/lib/codex-browser-db');
|
|
86
|
+
return getCodexDashboardSummary(await getDbPath());
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
export const listProjectsFn = createServerFn({ method: 'GET' }).handler(async () => {
|
|
90
|
+
const { listCodexProjects } = await import('@spiracha/lib/codex-browser-db');
|
|
91
|
+
return listCodexProjects(await getDbPath());
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
export const listProjectThreadsFn = createServerFn({ method: 'GET' })
|
|
95
|
+
.validator(projectSchema)
|
|
96
|
+
.handler(async ({ data }) => {
|
|
97
|
+
const { listProjectThreads } = await import('@spiracha/lib/codex-browser-db');
|
|
98
|
+
return listProjectThreads(await getDbPath(), data.project, {
|
|
99
|
+
includeTranscriptStats: false,
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
export const getThreadSnapshotFn = createServerFn({ method: 'GET' })
|
|
104
|
+
.validator(threadSnapshotSchema)
|
|
105
|
+
.handler(async ({ data }) => {
|
|
106
|
+
const startedAt = Date.now();
|
|
107
|
+
const [{ getThreadBrowseData }, { getThreadRolloutLoadState }] = await Promise.all([
|
|
108
|
+
import('@spiracha/lib/codex-browser-db'),
|
|
109
|
+
import('@spiracha/lib/codex-thread-cache'),
|
|
110
|
+
]);
|
|
111
|
+
const dbPath = await getDbPath();
|
|
112
|
+
logCodexThreadLoad('snapshot_start', {
|
|
113
|
+
threadId: data.threadId,
|
|
114
|
+
});
|
|
115
|
+
const browseData = getThreadBrowseData(dbPath, data.threadId);
|
|
116
|
+
const transcript: ParsedCodexTranscript | null = null;
|
|
117
|
+
let rollout: Awaited<ReturnType<typeof getThreadRolloutLoadState>>;
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
rollout = await getThreadRolloutLoadState(browseData.thread.rollout_path);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
if (!isMissingFileError(error)) {
|
|
123
|
+
throw error;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
rollout = {
|
|
127
|
+
fileSizeBytes: null,
|
|
128
|
+
shouldDeferTranscriptLoad: false,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const transcriptState: 'available' | 'deferred' | 'missing' =
|
|
133
|
+
rollout.fileSizeBytes === null ? 'missing' : rollout.shouldDeferTranscriptLoad ? 'deferred' : 'available';
|
|
134
|
+
logCodexThreadLoad('snapshot_ready', {
|
|
135
|
+
durationMs: Date.now() - startedAt,
|
|
136
|
+
fileSizeBytes: rollout.fileSizeBytes,
|
|
137
|
+
shouldDeferTranscriptLoad: rollout.shouldDeferTranscriptLoad,
|
|
138
|
+
threadId: data.threadId,
|
|
139
|
+
transcriptState,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
...browseData,
|
|
144
|
+
availableTools: browseData.dynamicTools,
|
|
145
|
+
rollout,
|
|
146
|
+
transcript,
|
|
147
|
+
transcriptState,
|
|
148
|
+
};
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
export const loadThreadTranscriptPreview = async (
|
|
152
|
+
threadId: string,
|
|
153
|
+
filters?: z.infer<typeof transcriptFiltersSchema>,
|
|
154
|
+
) => {
|
|
155
|
+
const startedAt = Date.now();
|
|
156
|
+
const [{ getThreadBrowseData }, { getCachedThreadTranscriptPreview }] = await Promise.all([
|
|
157
|
+
import('@spiracha/lib/codex-browser-db'),
|
|
158
|
+
import('@spiracha/lib/codex-thread-cache'),
|
|
159
|
+
]);
|
|
160
|
+
const dbPath = await getDbPath();
|
|
161
|
+
const browseData = getThreadBrowseData(dbPath, threadId);
|
|
162
|
+
logCodexThreadLoad('preview_start', {
|
|
163
|
+
rolloutPath: browseData.thread.rollout_path,
|
|
164
|
+
threadId,
|
|
165
|
+
});
|
|
166
|
+
const transcript = await getCachedThreadTranscriptPreview(browseData.thread.rollout_path, {
|
|
167
|
+
filters,
|
|
168
|
+
});
|
|
169
|
+
logCodexThreadLoad('preview_ready', {
|
|
170
|
+
durationMs: Date.now() - startedAt,
|
|
171
|
+
eventCount: transcript.events.length,
|
|
172
|
+
isPartial: transcript.isPartial,
|
|
173
|
+
threadId,
|
|
174
|
+
});
|
|
175
|
+
return transcript;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
export const loadThreadTranscript = async (threadId: string) => {
|
|
179
|
+
const [{ getThreadBrowseData }, { getCachedParsedCodexTranscript }] = await Promise.all([
|
|
180
|
+
import('@spiracha/lib/codex-browser-db'),
|
|
181
|
+
import('@spiracha/lib/codex-thread-cache'),
|
|
182
|
+
]);
|
|
183
|
+
const dbPath = await getDbPath();
|
|
184
|
+
const browseData = getThreadBrowseData(dbPath, threadId);
|
|
185
|
+
const startedAt = Date.now();
|
|
186
|
+
logCodexThreadLoad('full_start', {
|
|
187
|
+
rolloutPath: browseData.thread.rollout_path,
|
|
188
|
+
threadId,
|
|
189
|
+
});
|
|
190
|
+
const transcript = await getCachedParsedCodexTranscript(browseData.thread.rollout_path);
|
|
191
|
+
logCodexThreadLoad('full_ready', {
|
|
192
|
+
durationMs: Date.now() - startedAt,
|
|
193
|
+
eventCount: transcript.events.length,
|
|
194
|
+
threadId,
|
|
195
|
+
});
|
|
196
|
+
return transcript;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
export const getThreadTranscriptPreviewFn = createServerFn({ method: 'GET' })
|
|
200
|
+
.validator(threadSnapshotSchema)
|
|
201
|
+
.handler(async ({ data }) => {
|
|
202
|
+
return loadThreadTranscriptPreview(data.threadId, data.filters);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
export const getThreadTranscriptFn = createServerFn({ method: 'GET' })
|
|
206
|
+
.validator(threadSchema)
|
|
207
|
+
.handler(async ({ data }) => {
|
|
208
|
+
return loadThreadTranscript(data.threadId);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
export const getAnalyticsFn = createServerFn({ method: 'GET' })
|
|
212
|
+
.validator(analyticsSchema)
|
|
213
|
+
.handler(async ({ data }) => {
|
|
214
|
+
const { getCodexAnalytics } = await import('@spiracha/lib/codex-analytics');
|
|
215
|
+
return getCodexAnalytics({
|
|
216
|
+
dbPath: await getDbPath(),
|
|
217
|
+
project: data.project,
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
export const exportThreadFn = createServerFn({ method: 'POST' })
|
|
222
|
+
.validator(exportSchema)
|
|
223
|
+
.handler(async ({ data }) => {
|
|
224
|
+
const { renderCodexThreadDownload } = await import('@spiracha/lib/codex-browser-export');
|
|
225
|
+
return renderCodexThreadDownload({
|
|
226
|
+
dbPath: await getDbPath(),
|
|
227
|
+
includeCommentary: data.includeCommentary,
|
|
228
|
+
includeMetadata: data.includeMetadata,
|
|
229
|
+
includeTools: data.includeTools,
|
|
230
|
+
outputFormat: data.outputFormat,
|
|
231
|
+
pathDisplaySettings: {
|
|
232
|
+
convertToProjectRoot: data.convertToProjectRoot,
|
|
233
|
+
redactUsername: data.redactUsername,
|
|
234
|
+
},
|
|
235
|
+
threadId: data.threadId,
|
|
236
|
+
zipArchive: data.zipArchive,
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
export const exportThreadsFn = createServerFn({ method: 'POST' })
|
|
241
|
+
.validator(exportThreadsSchema)
|
|
242
|
+
.handler(async ({ data }) => {
|
|
243
|
+
const { renderCodexThreadsDownload } = await import('@spiracha/lib/codex-browser-export');
|
|
244
|
+
return renderCodexThreadsDownload({
|
|
245
|
+
dbPath: await getDbPath(),
|
|
246
|
+
includeCommentary: data.includeCommentary,
|
|
247
|
+
includeMetadata: data.includeMetadata,
|
|
248
|
+
includeTools: data.includeTools,
|
|
249
|
+
outputFormat: data.outputFormat,
|
|
250
|
+
pathDisplaySettings: {
|
|
251
|
+
convertToProjectRoot: data.convertToProjectRoot,
|
|
252
|
+
redactUsername: data.redactUsername,
|
|
253
|
+
},
|
|
254
|
+
threadIds: data.threadIds,
|
|
255
|
+
zipArchive: data.zipArchive,
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
export const deleteThreadFn = createServerFn({ method: 'POST' })
|
|
260
|
+
.validator(deleteThreadSchema)
|
|
261
|
+
.handler(async ({ data }) => {
|
|
262
|
+
const { deleteCodexThread } = await import('@spiracha/lib/codex-browser-db');
|
|
263
|
+
return deleteCodexThread(await getDbPath(), data.threadId, {
|
|
264
|
+
deleteSessionFiles: data.deleteSessionFiles,
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
export const deleteThreadsFn = createServerFn({ method: 'POST' })
|
|
269
|
+
.validator(deleteThreadsSchema)
|
|
270
|
+
.handler(async ({ data }) => {
|
|
271
|
+
const { deleteCodexThreads } = await import('@spiracha/lib/codex-browser-db');
|
|
272
|
+
return deleteCodexThreads(await getDbPath(), data.threadIds, {
|
|
273
|
+
deleteSessionFiles: data.deleteSessionFiles,
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
export const deleteProjectFn = createServerFn({ method: 'POST' })
|
|
278
|
+
.validator(deleteProjectSchema)
|
|
279
|
+
.handler(async ({ data }) => {
|
|
280
|
+
const { deleteCodexProject } = await import('@spiracha/lib/codex-browser-db');
|
|
281
|
+
return deleteCodexProject(await getDbPath(), data.project, {
|
|
282
|
+
deleteSessionFiles: data.deleteSessionFiles,
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
export const recoverProjectThreadsFn = createServerFn({ method: 'POST' })
|
|
287
|
+
.validator(projectSchema)
|
|
288
|
+
.handler(async ({ data }) => {
|
|
289
|
+
const { recoverCodexProjectThreads } = await import('@spiracha/lib/codex-thread-recovery');
|
|
290
|
+
return recoverCodexProjectThreads(await getDbPath(), data.project);
|
|
291
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { isRetryableSqliteError } from '@spiracha/lib/sqlite-error';
|
|
2
|
+
import { queryOptions } from '@tanstack/react-query';
|
|
3
|
+
import { getCursorThreadDetailFn, listCursorThreadsFn, listCursorWorkspacesFn } from './cursor-server';
|
|
4
|
+
|
|
5
|
+
const retrySqliteQuery = (failureCount: number, error: unknown) => {
|
|
6
|
+
return failureCount < 3 && isRetryableSqliteError(error);
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const retryDelay = (attemptIndex: number) => {
|
|
10
|
+
return [150, 400][attemptIndex] ?? 800;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const cursorWorkspacesQueryOptions = () =>
|
|
14
|
+
queryOptions({
|
|
15
|
+
queryFn: () => listCursorWorkspacesFn(),
|
|
16
|
+
queryKey: ['cursor-workspaces'],
|
|
17
|
+
retry: retrySqliteQuery,
|
|
18
|
+
retryDelay,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export const cursorThreadsQueryOptions = (workspaceKey: string | null) =>
|
|
22
|
+
queryOptions({
|
|
23
|
+
enabled: workspaceKey !== null,
|
|
24
|
+
queryFn: () => listCursorThreadsFn({ data: { workspaceKey: workspaceKey ?? '' } }),
|
|
25
|
+
queryKey: ['cursor-threads', workspaceKey ?? 'none'],
|
|
26
|
+
retry: retrySqliteQuery,
|
|
27
|
+
retryDelay,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export const cursorThreadDetailQueryOptions = (composerId: string | null) =>
|
|
31
|
+
queryOptions({
|
|
32
|
+
enabled: composerId !== null,
|
|
33
|
+
queryFn: () => getCursorThreadDetailFn({ data: { composerId: composerId ?? '' } }),
|
|
34
|
+
queryKey: ['cursor-thread', composerId ?? 'none'],
|
|
35
|
+
retry: retrySqliteQuery,
|
|
36
|
+
retryDelay,
|
|
37
|
+
});
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { mkdtemp, rm } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import {
|
|
6
|
+
buildBatchExportBaseName,
|
|
7
|
+
buildConversationExportBaseName,
|
|
8
|
+
getExportMimeType,
|
|
9
|
+
resolveUniqueExportFileBaseName,
|
|
10
|
+
sanitizeExportFileName,
|
|
11
|
+
zipExportDirectory,
|
|
12
|
+
} from '@spiracha/lib/ui-export-archive';
|
|
13
|
+
import { buildUiExportDownloadUrl, ensureUiExportDir } from '@spiracha/lib/ui-export-files';
|
|
14
|
+
import { createServerFn } from '@tanstack/react-start';
|
|
15
|
+
import { z } from 'zod';
|
|
16
|
+
|
|
17
|
+
const workspaceSchema = z.object({
|
|
18
|
+
workspaceKey: z.string().min(1),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const threadSchema = z.object({
|
|
22
|
+
composerId: z.string().min(1),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const recoverSchema = z.object({
|
|
26
|
+
apply: z.boolean().default(false),
|
|
27
|
+
workspaceKey: z.string().min(1),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const exportSchema = z.object({
|
|
31
|
+
composerId: z.string().min(1),
|
|
32
|
+
includeCommentary: z.boolean().default(true),
|
|
33
|
+
includeMetadata: z.boolean().default(true),
|
|
34
|
+
includeTools: z.boolean().default(true),
|
|
35
|
+
outputFormat: z.enum(['md', 'txt']).default('md'),
|
|
36
|
+
zipArchive: z.boolean().default(false),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const exportThreadsSchema = z.object({
|
|
40
|
+
composerIds: z.array(z.string().min(1)).min(1),
|
|
41
|
+
includeCommentary: z.boolean().default(true),
|
|
42
|
+
includeMetadata: z.boolean().default(true),
|
|
43
|
+
includeTools: z.boolean().default(true),
|
|
44
|
+
outputFormat: z.enum(['md', 'txt']).default('md'),
|
|
45
|
+
zipArchive: z.boolean().default(true),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const deleteThreadsSchema = z.object({
|
|
49
|
+
composerIds: z.array(z.string().min(1)).min(1),
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const ensureCursorClosedForWrite = async () => {
|
|
53
|
+
const { isCursorRunning } = await import('@spiracha/lib/cursor-recovery');
|
|
54
|
+
if (await isCursorRunning()) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
'Quit Cursor before deleting. It rewrites chat history on exit, which can resurrect deleted threads.',
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const findGroupByKey = async (workspaceKey: string) => {
|
|
62
|
+
const { listCursorWorkspaceGroups } = await import('@spiracha/lib/cursor-db');
|
|
63
|
+
const groups = await listCursorWorkspaceGroups();
|
|
64
|
+
const group = groups.find((candidate) => candidate.key === workspaceKey);
|
|
65
|
+
if (!group) {
|
|
66
|
+
throw new Error(`Cursor workspace not found: ${workspaceKey}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return group;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const toSafeExportName = (value: string) => {
|
|
73
|
+
return sanitizeExportFileName(value) || 'cursor-thread';
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const renderCursorZipDownload = async (
|
|
77
|
+
rendered: Array<{ composerId: string; content: string; cwd: string | null; updatedAtMs: number | null }>,
|
|
78
|
+
outputFormat: 'md' | 'txt',
|
|
79
|
+
) => {
|
|
80
|
+
const exportDir = await ensureUiExportDir();
|
|
81
|
+
const exportBaseName =
|
|
82
|
+
rendered.length === 1
|
|
83
|
+
? buildConversationExportBaseName(
|
|
84
|
+
{
|
|
85
|
+
cwd: rendered[0]!.cwd,
|
|
86
|
+
id: rendered[0]!.composerId,
|
|
87
|
+
updatedAtMs: rendered[0]!.updatedAtMs,
|
|
88
|
+
},
|
|
89
|
+
'cursor-thread',
|
|
90
|
+
)
|
|
91
|
+
: buildBatchExportBaseName(rendered, 'cursor');
|
|
92
|
+
const workspaceDir = await mkdtemp(path.join(os.tmpdir(), `${exportBaseName}-`));
|
|
93
|
+
const zipPath = path.join(exportDir, `${exportBaseName}-${randomUUID()}.zip`);
|
|
94
|
+
const usedBaseNames = new Map<string, number>();
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
for (const entry of rendered) {
|
|
98
|
+
const baseName = toSafeExportName(entry.composerId);
|
|
99
|
+
const fileBaseName = resolveUniqueExportFileBaseName(baseName, usedBaseNames);
|
|
100
|
+
await Bun.write(path.join(workspaceDir, `${fileBaseName}.${outputFormat}`), entry.content);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
await zipExportDirectory(workspaceDir, zipPath);
|
|
104
|
+
} finally {
|
|
105
|
+
await rm(workspaceDir, { force: true, recursive: true });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
downloadUrl: buildUiExportDownloadUrl(zipPath),
|
|
110
|
+
fileName: `${exportBaseName}.zip`,
|
|
111
|
+
mimeType: 'application/zip',
|
|
112
|
+
mode: 'download_url' as const,
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
export const findCursorThreadByComposerId = async (composerId: string) => {
|
|
117
|
+
const { listCursorThreadsForGroup, listCursorWorkspaceGroups } = await import('@spiracha/lib/cursor-db');
|
|
118
|
+
for (const group of await listCursorWorkspaceGroups()) {
|
|
119
|
+
const threads = await listCursorThreadsForGroup(group, undefined, { includeTranscriptDirs: false });
|
|
120
|
+
const thread = threads.find((candidate) => candidate.composerId === composerId);
|
|
121
|
+
if (thread) {
|
|
122
|
+
return thread;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return null;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const renderCursorDownload = async (input: {
|
|
130
|
+
composerIds: string[];
|
|
131
|
+
includeCommentary: boolean;
|
|
132
|
+
includeMetadata: boolean;
|
|
133
|
+
includeTools: boolean;
|
|
134
|
+
outputFormat: 'md' | 'txt';
|
|
135
|
+
zipArchive: boolean;
|
|
136
|
+
}) => {
|
|
137
|
+
const { runWithTranscriptLoadLimit } = await import('@spiracha/lib/transcript-load-limiter');
|
|
138
|
+
const { readCursorThreadTranscriptWithAgentFiles } = await import('@spiracha/lib/cursor-db');
|
|
139
|
+
const { getCursorGlobalDbPath } = await import('@spiracha/lib/cursor-exporter-types');
|
|
140
|
+
const { renderCursorTranscript } = await import('@spiracha/lib/cursor-transcript');
|
|
141
|
+
const globalDbPath = getCursorGlobalDbPath();
|
|
142
|
+
const { listCursorWorkspaceGroups } = await import('@spiracha/lib/cursor-db');
|
|
143
|
+
const workspaceGroups = await listCursorWorkspaceGroups();
|
|
144
|
+
const rendered = await Promise.all(
|
|
145
|
+
input.composerIds.map(async (composerId) => {
|
|
146
|
+
const transcript = await runWithTranscriptLoadLimit(
|
|
147
|
+
() => readCursorThreadTranscriptWithAgentFiles(globalDbPath, composerId),
|
|
148
|
+
{
|
|
149
|
+
id: composerId,
|
|
150
|
+
path: globalDbPath,
|
|
151
|
+
source: 'cursor-ui-export',
|
|
152
|
+
},
|
|
153
|
+
);
|
|
154
|
+
if (!transcript) {
|
|
155
|
+
throw new Error(`No transcript found for thread: ${composerId}`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const content = renderCursorTranscript(transcript, {
|
|
159
|
+
includeCommentary: input.includeCommentary,
|
|
160
|
+
includeMetadata: input.includeMetadata,
|
|
161
|
+
includeTools: input.includeTools,
|
|
162
|
+
outputFormat: input.outputFormat,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
if (!content) {
|
|
166
|
+
throw new Error(`Thread has no exportable content: ${composerId}`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const workspace =
|
|
170
|
+
workspaceGroups.find((candidate) =>
|
|
171
|
+
candidate.buckets.some((bucket) => bucket.threadComposerIds.includes(composerId)),
|
|
172
|
+
) ?? null;
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
composerId,
|
|
176
|
+
content,
|
|
177
|
+
cwd: workspace?.folders[0] ?? null,
|
|
178
|
+
updatedAtMs: transcript.head.lastUpdatedAtMs,
|
|
179
|
+
};
|
|
180
|
+
}),
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
if (input.zipArchive || rendered.length > 1) {
|
|
184
|
+
return renderCursorZipDownload(rendered, input.outputFormat);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (rendered.length === 1) {
|
|
188
|
+
return {
|
|
189
|
+
content: rendered[0]!.content,
|
|
190
|
+
fileName: `${buildConversationExportBaseName(
|
|
191
|
+
{
|
|
192
|
+
cwd: rendered[0]!.cwd,
|
|
193
|
+
id: rendered[0]!.composerId,
|
|
194
|
+
updatedAtMs: rendered[0]!.updatedAtMs,
|
|
195
|
+
},
|
|
196
|
+
'cursor-thread',
|
|
197
|
+
)}.${input.outputFormat}`,
|
|
198
|
+
mimeType: getExportMimeType(input.outputFormat),
|
|
199
|
+
mode: 'download' as const,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
throw new Error('No Cursor threads selected for export');
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
export const listCursorWorkspacesFn = createServerFn({ method: 'GET' }).handler(async () => {
|
|
207
|
+
const { listCursorWorkspaceGroups } = await import('@spiracha/lib/cursor-db');
|
|
208
|
+
return listCursorWorkspaceGroups();
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
export const listCursorThreadsFn = createServerFn({ method: 'GET' })
|
|
212
|
+
.validator(workspaceSchema)
|
|
213
|
+
.handler(async ({ data }) => {
|
|
214
|
+
const { listCursorThreadsForGroup } = await import('@spiracha/lib/cursor-db');
|
|
215
|
+
const group = await findGroupByKey(data.workspaceKey);
|
|
216
|
+
return listCursorThreadsForGroup(group, undefined, { includeTranscriptDirs: false });
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
export const getCursorThreadDetailFn = createServerFn({ method: 'GET' })
|
|
220
|
+
.validator(threadSchema)
|
|
221
|
+
.handler(async ({ data }) => {
|
|
222
|
+
const { runWithTranscriptLoadLimit } = await import('@spiracha/lib/transcript-load-limiter');
|
|
223
|
+
const { readCursorThreadTranscriptWithAgentFiles } = await import('@spiracha/lib/cursor-db');
|
|
224
|
+
const { getCursorGlobalDbPath } = await import('@spiracha/lib/cursor-exporter-types');
|
|
225
|
+
const thread = await findCursorThreadByComposerId(data.composerId);
|
|
226
|
+
if (!thread) {
|
|
227
|
+
throw new Error(`Cursor thread not found: ${data.composerId}`);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const transcript = await runWithTranscriptLoadLimit(
|
|
231
|
+
() => readCursorThreadTranscriptWithAgentFiles(getCursorGlobalDbPath(), data.composerId),
|
|
232
|
+
{
|
|
233
|
+
id: data.composerId,
|
|
234
|
+
path: getCursorGlobalDbPath(),
|
|
235
|
+
source: 'cursor-ui-detail',
|
|
236
|
+
},
|
|
237
|
+
);
|
|
238
|
+
return {
|
|
239
|
+
thread,
|
|
240
|
+
transcript,
|
|
241
|
+
};
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
export const exportCursorThreadFn = createServerFn({ method: 'POST' })
|
|
245
|
+
.validator(exportSchema)
|
|
246
|
+
.handler(async ({ data }) => {
|
|
247
|
+
return await renderCursorDownload({
|
|
248
|
+
composerIds: [data.composerId],
|
|
249
|
+
includeCommentary: data.includeCommentary,
|
|
250
|
+
includeMetadata: data.includeMetadata,
|
|
251
|
+
includeTools: data.includeTools,
|
|
252
|
+
outputFormat: data.outputFormat,
|
|
253
|
+
zipArchive: data.zipArchive,
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
export const exportCursorThreadsFn = createServerFn({ method: 'POST' })
|
|
258
|
+
.validator(exportThreadsSchema)
|
|
259
|
+
.handler(async ({ data }) => {
|
|
260
|
+
return await renderCursorDownload({
|
|
261
|
+
composerIds: data.composerIds,
|
|
262
|
+
includeCommentary: data.includeCommentary,
|
|
263
|
+
includeMetadata: data.includeMetadata,
|
|
264
|
+
includeTools: data.includeTools,
|
|
265
|
+
outputFormat: data.outputFormat,
|
|
266
|
+
zipArchive: data.zipArchive,
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
export const recoverCursorWorkspaceFn = createServerFn({ method: 'POST' })
|
|
271
|
+
.validator(recoverSchema)
|
|
272
|
+
.handler(async ({ data }) => {
|
|
273
|
+
const { isCursorRunning, recoverCursorWorkspaceGroup } = await import('@spiracha/lib/cursor-recovery');
|
|
274
|
+
const group = await findGroupByKey(data.workspaceKey);
|
|
275
|
+
// Cursor rewrites composer.composerHeaders on exit, so a write while it is running gets
|
|
276
|
+
// clobbered. Refuse to apply until Cursor is closed.
|
|
277
|
+
if (data.apply && (await isCursorRunning())) {
|
|
278
|
+
throw new Error('Quit Cursor before recovering. It overwrites chat history on exit, undoing the recovery.');
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return recoverCursorWorkspaceGroup(group, data.apply);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
export const deleteCursorThreadsFn = createServerFn({ method: 'POST' })
|
|
285
|
+
.validator(deleteThreadsSchema)
|
|
286
|
+
.handler(async ({ data }) => {
|
|
287
|
+
const { collectCursorThreadsForDeletion, pruneCursorThreads } = await import('@spiracha/lib/cursor-recovery');
|
|
288
|
+
await ensureCursorClosedForWrite();
|
|
289
|
+
const threads = await collectCursorThreadsForDeletion(data.composerIds);
|
|
290
|
+
return pruneCursorThreads(threads, true);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
export const deleteCursorWorkspaceFn = createServerFn({ method: 'POST' })
|
|
294
|
+
.validator(workspaceSchema)
|
|
295
|
+
.handler(async ({ data }) => {
|
|
296
|
+
const { listCursorThreadsForGroup } = await import('@spiracha/lib/cursor-db');
|
|
297
|
+
const { collectCursorThreadsForDeletion, pruneCursorThreads } = await import('@spiracha/lib/cursor-recovery');
|
|
298
|
+
await ensureCursorClosedForWrite();
|
|
299
|
+
const group = await findGroupByKey(data.workspaceKey);
|
|
300
|
+
const threads = await listCursorThreadsForGroup(group, undefined, { includeTranscriptDirs: false });
|
|
301
|
+
const composerIds = threads.map((thread) => thread.composerId);
|
|
302
|
+
if (composerIds.length === 0) {
|
|
303
|
+
return {
|
|
304
|
+
bubblesDeleted: 0,
|
|
305
|
+
composerDataDeleted: 0,
|
|
306
|
+
composerIds: [],
|
|
307
|
+
headersRemoved: 0,
|
|
308
|
+
transcriptDirsRemoved: 0,
|
|
309
|
+
workspaceBucketsUpdated: 0,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const deletable = await collectCursorThreadsForDeletion(composerIds);
|
|
314
|
+
return pruneCursorThreads(deletable, true);
|
|
315
|
+
});
|