spiracha 1.3.3 → 1.5.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 +55 -6
- package/README.md +46 -5
- package/apps/ui/AGENTS.md +31 -4
- package/apps/ui/README.md +59 -5
- package/apps/ui/dist/client/assets/analytics-DOy1b6Sh.js +1 -0
- package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-DqySVO2K.js +7 -0
- package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-KAcXtNxI.js +1 -0
- package/apps/ui/dist/client/assets/antigravity-keychain-panel-B77qJz6f.js +1 -0
- package/apps/ui/dist/client/assets/antigravity._workspaceKey-B3NM6gRH.js +1 -0
- package/apps/ui/dist/client/assets/antigravity._workspaceKey-ByitGWki.js +1 -0
- package/apps/ui/dist/client/assets/antigravity.index-DP9JC2P9.js +1 -0
- package/apps/ui/dist/client/assets/antigravity.index-DgW25FEV.js +1 -0
- package/apps/ui/dist/client/assets/badge-C3i4OKs8.js +1 -0
- package/apps/ui/dist/client/assets/checkbox-CdHitXWQ.js +1 -0
- package/apps/ui/dist/client/assets/claude-code-sessions._sessionId-8pQ55ci2.js +1 -0
- package/apps/ui/dist/client/assets/claude-code-sessions._sessionId-DVz5UmtU.js +1 -0
- package/apps/ui/dist/client/assets/claude-code._workspaceKey-BBKskSum.js +1 -0
- package/apps/ui/dist/client/assets/claude-code._workspaceKey-OkikMUFs.js +1 -0
- package/apps/ui/dist/client/assets/claude-code.index-Cwu76dbb.js +1 -0
- package/apps/ui/dist/client/assets/claude-code.index-D6eMd08t.js +1 -0
- package/apps/ui/dist/client/assets/createServerFn-B-KkwmHU.js +3 -0
- package/apps/ui/dist/client/assets/cursor-threads._composerId-CmBFclK8.js +1 -0
- package/apps/ui/dist/client/assets/cursor-threads._composerId-hBWlP1D-.js +1 -0
- package/apps/ui/dist/client/assets/cursor._workspaceKey-BFPkIRi-.js +1 -0
- package/apps/ui/dist/client/assets/cursor._workspaceKey-CaAZjGmm.js +1 -0
- package/apps/ui/dist/client/assets/cursor.index-8y4o20kb.js +2 -0
- package/apps/ui/dist/client/assets/cursor.index-CrnMwDCi.js +1 -0
- package/apps/ui/dist/client/assets/{data-table-DeQq8xdt.js → data-table-Br78_dk3.js} +2 -2
- package/apps/ui/dist/client/assets/delete-confirm-dialog-Bxs07u-j.js +7 -0
- package/apps/ui/dist/client/assets/dist-B9pyYCVU.js +1 -0
- package/apps/ui/dist/client/assets/dist-BrJhN2A0.js +5 -0
- package/apps/ui/dist/client/assets/dist-Cmd3AIfD.js +1 -0
- package/apps/ui/dist/client/assets/dist-DvMS2965.js +1 -0
- package/apps/ui/dist/client/assets/download-Bf0QJKD3.js +1 -0
- package/apps/ui/dist/client/assets/dropdown-menu-D5cvPaZt.js +1 -0
- package/apps/ui/dist/client/assets/es2015-ad6040dw.js +41 -0
- package/apps/ui/dist/client/assets/export-dialog-Dr8d2MqD.js +1 -0
- package/apps/ui/dist/client/assets/formatters-BtqyrX_c.js +1 -0
- package/apps/ui/dist/client/assets/index-C-STgIH4.js +166 -0
- package/apps/ui/dist/client/assets/jsx-runtime-DGeXAQPT.js +1 -0
- package/apps/ui/dist/client/assets/kiro-sessions._sessionId-CKgfJ7GR.js +1 -0
- package/apps/ui/dist/client/assets/kiro-sessions._sessionId-zvIJHtQF.js +1 -0
- package/apps/ui/dist/client/assets/kiro._workspaceKey-DhVuJuZB.js +1 -0
- package/apps/ui/dist/client/assets/kiro._workspaceKey-QArsjkTY.js +1 -0
- package/apps/ui/dist/client/assets/kiro.index-C-fr4m7i.js +1 -0
- package/apps/ui/dist/client/assets/kiro.index-OYgAdtZd.js +1 -0
- package/apps/ui/dist/client/assets/metric-card-Dplm0ZiM.js +1 -0
- package/apps/ui/dist/client/assets/opencode-sessions._sessionId-DLjiTst-.js +1 -0
- package/apps/ui/dist/client/assets/opencode-sessions._sessionId-biBwrRbt.js +8 -0
- package/apps/ui/dist/client/assets/opencode._workspaceKey-C7OIzMOJ.js +1 -0
- package/apps/ui/dist/client/assets/opencode._workspaceKey-sBakX0oI.js +1 -0
- package/apps/ui/dist/client/assets/opencode.index-Bl_kNs9N.js +1 -0
- package/apps/ui/dist/client/assets/opencode.index-CN53bVmb.js +1 -0
- package/apps/ui/dist/client/assets/page-header-Cwa3p6Tl.js +1 -0
- package/apps/ui/dist/client/assets/preload-helper-B8fFrdxC.js +1 -0
- package/apps/ui/dist/client/assets/projects._project-D8WloxQN.js +1 -0
- package/apps/ui/dist/client/assets/projects._project-DSDwH4NF.js +1 -0
- package/apps/ui/dist/client/assets/projects.index-5xRtuJdX.js +3 -0
- package/apps/ui/dist/client/assets/projects.index-DEfhWqa8.js +1 -0
- package/apps/ui/dist/client/assets/react-dom-DL96Jor4.js +1 -0
- package/apps/ui/dist/client/assets/refresh-ccw-BgKjOiA6.js +1 -0
- package/apps/ui/dist/client/assets/reload-error-panel-AHqu0akK.js +1 -0
- package/apps/ui/dist/client/assets/routes-DnzF_9CX.js +1 -0
- package/apps/ui/dist/client/assets/scroll-text-fM0sMOB3.js +1 -0
- package/apps/ui/dist/client/assets/select-CYF6pF37.js +1 -0
- package/apps/ui/dist/client/assets/settings-Dx70jgP_.js +1 -0
- package/apps/ui/dist/client/assets/sqlite-error-B6uZjUe8.js +1 -0
- package/apps/ui/dist/client/assets/styles-B7Zi9EFA.css +1 -0
- package/apps/ui/dist/client/assets/tabs-CmuhkT-c.js +7 -0
- package/apps/ui/dist/client/assets/text-filter-DVAZySUS.js +2 -0
- package/apps/ui/dist/client/assets/thread-transcript-stats-BUNz6u7F.js +1 -0
- package/apps/ui/dist/client/assets/threads._threadId-BB9tTsV4.js +1 -0
- package/apps/ui/dist/client/assets/threads._threadId-BMRSFow3.js +1 -0
- package/apps/ui/dist/client/assets/useMutation-BSFult2i.js +1 -0
- package/apps/ui/dist/client/assets/useQuery-CjINKZf1.js +1 -0
- package/apps/ui/dist/client/icon.svg +2 -1
- package/apps/ui/dist/server/assets/_tanstack-start-manifest_v-CQ8Jjs3B.js +387 -0
- package/apps/ui/dist/server/assets/analytics-CYP_LT5Y.js +16 -0
- package/apps/ui/dist/server/assets/{analytics-Dc_QDZZC.js → analytics-CtpOMVMO.js} +13 -6
- package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-BKOyL2T6.js +613 -0
- package/apps/ui/dist/server/assets/{antigravity-conversations._conversationId-C9z4AeN-.js → antigravity-conversations._conversationId-eS87Dx7g.js} +2 -2
- package/apps/ui/dist/server/assets/{antigravity-db-D9gW1D8G.js → antigravity-db-Bh8_U9uw.js} +11 -7
- package/apps/ui/dist/server/assets/{antigravity-keychain-panel-bpPoxhyp.js → antigravity-keychain-panel-CC7FLmAK.js} +1 -1
- package/apps/ui/dist/server/assets/{antigravity-queries-DVIKyOXi.js → antigravity-queries-CGrJO9Vr.js} +5 -5
- package/apps/ui/dist/server/assets/{antigravity-server-Cp1lc8fp.js → antigravity-server-CHRjVFqN.js} +11 -11
- package/apps/ui/dist/server/assets/{antigravity._workspaceKey-Dl8o1ptg.js → antigravity._workspaceKey-B0QeYUOh.js} +2 -2
- package/apps/ui/dist/server/assets/{antigravity._workspaceKey-BAoa4izU.js → antigravity._workspaceKey-C3kP-qyN.js} +4 -4
- package/apps/ui/dist/server/assets/{antigravity.index-BOlrJasZ.js → antigravity.index-CPCYkqCi.js} +2 -2
- package/apps/ui/dist/server/assets/claude-code-db-Ik3VStKZ.js +361 -0
- package/apps/ui/dist/server/assets/claude-code-queries-Cgzi4K-e.js +37 -0
- package/apps/ui/dist/server/assets/claude-code-server-PfraClXS.js +72 -0
- package/apps/ui/dist/server/assets/claude-code-sessions._sessionId-C55oe6zj.js +18 -0
- package/apps/ui/dist/server/assets/claude-code-sessions._sessionId-D1RYYi3j.js +473 -0
- package/apps/ui/dist/server/assets/claude-code-sessions._sessionId-DSvPLEUL.js +11 -0
- package/apps/ui/dist/server/assets/claude-code-transcript-C-bxh3cS.js +139 -0
- package/apps/ui/dist/server/assets/claude-code-transcript-phase-DmUMc8VU.js +10 -0
- package/apps/ui/dist/server/assets/claude-code._workspaceKey-B5Xg_-tE.js +28 -0
- package/apps/ui/dist/server/assets/claude-code._workspaceKey-BPlzyXPa.js +11 -0
- package/apps/ui/dist/server/assets/claude-code._workspaceKey-DqDve2wl.js +191 -0
- package/apps/ui/dist/server/assets/claude-code.index-B7yYR7dD.js +92 -0
- package/apps/ui/dist/server/assets/claude-code.index-BUHThG1W.js +11 -0
- package/apps/ui/dist/server/assets/{codex-queries-ev_mRUzr.js → codex-queries-CtgeZ7VL.js} +15 -13
- package/apps/ui/dist/server/assets/{codex-server-DIYFLozB.js → codex-server-CGXU4H8-.js} +421 -146
- package/apps/ui/dist/server/assets/concurrency-VXtYvlGh.js +18 -0
- package/apps/ui/dist/server/assets/{createServerRpc-Cg-Zr1rq.js → createServerRpc-CF_DEwnm.js} +1 -1
- package/apps/ui/dist/server/assets/{createSsrRpc-DVnVQhGx.js → createSsrRpc-C754NPki.js} +1 -1
- package/apps/ui/dist/server/assets/{cursor-db-B7agkAvM.js → cursor-db-CZnYy7aT.js} +196 -9
- package/apps/ui/dist/server/assets/{cursor-queries-f7DjBKSD.js → cursor-queries-NCIM0Nat.js} +12 -10
- package/apps/ui/dist/server/assets/{cursor-recovery-9bJLs7vG.js → cursor-recovery-CXpO8KcD.js} +2 -2
- package/apps/ui/dist/server/assets/{cursor-server-nPzojHN3.js → cursor-server-DNu52A3K.js} +71 -42
- package/apps/ui/dist/server/assets/cursor-threads._composerId-Ckn6ItHI.js +582 -0
- package/apps/ui/dist/server/assets/{cursor-threads._composerId-B3qcsh9C.js → cursor-threads._composerId-p9-ARAzc.js} +2 -2
- package/apps/ui/dist/server/assets/{cursor-transcript-2iL3KFSK.js → cursor-transcript-B7tGnqtn.js} +1 -1
- package/apps/ui/dist/server/assets/{cursor._workspaceKey-BL_CQyHQ.js → cursor._workspaceKey-CZuohIUL.js} +2 -2
- package/apps/ui/dist/server/assets/{cursor._workspaceKey-BhQeepXB.js → cursor._workspaceKey-DPp5JSjy.js} +11 -9
- package/apps/ui/dist/server/assets/{cursor.index-CsrdckZD.js → cursor.index-DkWT-mxw.js} +2 -2
- package/apps/ui/dist/server/assets/{delete-confirm-dialog-PCD7S0_M.js → delete-confirm-dialog-BcBNCxWB.js} +5 -1
- package/apps/ui/dist/server/assets/{empty-plugin-adapters-D3qLMPfC.js → empty-plugin-adapters-B3lHh1La.js} +1 -1
- package/apps/ui/dist/server/assets/{export-dialog-DaPlOGFT.js → export-dialog-D88ze9Gy.js} +54 -12
- package/apps/ui/dist/server/assets/kiro-db--fXrED_-.js +552 -0
- package/apps/ui/dist/server/assets/kiro-queries-CzNlM2ok.js +37 -0
- package/apps/ui/dist/server/assets/kiro-server-9WRxg2Au.js +72 -0
- package/apps/ui/dist/server/assets/kiro-sessions._sessionId-CRY2c7YO.js +11 -0
- package/apps/ui/dist/server/assets/kiro-sessions._sessionId-F1UlwxmR.js +449 -0
- package/apps/ui/dist/server/assets/kiro-sessions._sessionId-bvTulT7C.js +18 -0
- package/apps/ui/dist/server/assets/kiro-transcript-DoekQBaH.js +112 -0
- package/apps/ui/dist/server/assets/kiro-transcript-phase-CJG40nQg.js +27 -0
- package/apps/ui/dist/server/assets/kiro._workspaceKey-BDe9sQwo.js +11 -0
- package/apps/ui/dist/server/assets/kiro._workspaceKey-CJsjgNwO.js +28 -0
- package/apps/ui/dist/server/assets/kiro._workspaceKey-D_-4Qb7y.js +193 -0
- package/apps/ui/dist/server/assets/kiro.index-C__-UROk.js +11 -0
- package/apps/ui/dist/server/assets/kiro.index-CmTbqeYj.js +93 -0
- package/apps/ui/dist/server/assets/opencode-db-BM7KjOzc.js +397 -0
- package/apps/ui/dist/server/assets/opencode-queries-BeNYXnQv.js +50 -0
- package/apps/ui/dist/server/assets/opencode-server-BpQ_kRze.js +72 -0
- package/apps/ui/dist/server/assets/opencode-sessions._sessionId-CGQ-t0vD.js +518 -0
- package/apps/ui/dist/server/assets/opencode-sessions._sessionId-D8Q7in_Z.js +18 -0
- package/apps/ui/dist/server/assets/opencode-sessions._sessionId-DKOvi0lV.js +11 -0
- package/apps/ui/dist/server/assets/opencode-think-tags-CAoD-EcZ.js +87 -0
- package/apps/ui/dist/server/assets/opencode-transcript-BNTcGNym.js +141 -0
- package/apps/ui/dist/server/assets/opencode-transcript-phase-OqLkiQD0.js +32 -0
- package/apps/ui/dist/server/assets/opencode._workspaceKey-BdqcWHw1.js +11 -0
- package/apps/ui/dist/server/assets/opencode._workspaceKey-CfH96HTT.js +28 -0
- package/apps/ui/dist/server/assets/opencode._workspaceKey-DdbHPDrO.js +207 -0
- package/apps/ui/dist/server/assets/opencode.index-BbVzkJXP.js +11 -0
- package/apps/ui/dist/server/assets/opencode.index-Vh_Gq_1L.js +93 -0
- package/apps/ui/dist/server/assets/{projects._project-CqqUFr4l.js → projects._project-4io5LO0E.js} +5 -3
- package/apps/ui/dist/server/assets/{projects._project-DahGqNYS.js → projects._project-D6ZoGhJ8.js} +42 -12
- package/apps/ui/dist/server/assets/{projects.index-CkTeFWNU.js → projects.index-BkLiF2FF.js} +21 -7
- package/apps/ui/dist/server/assets/projects.index-DesYXwfi.js +14 -0
- package/apps/ui/dist/server/assets/route-search-ts4W9MJ9.js +38 -0
- package/apps/ui/dist/server/assets/router-Sac2DGk6.js +518 -0
- package/apps/ui/dist/server/assets/{routes-Xix3ZeSo.js → routes-Bsact1uB.js} +2 -2
- package/apps/ui/dist/server/assets/{routes-0x1U0IHq.js → routes-BzkJgq4W.js} +2 -2
- package/apps/ui/dist/server/assets/{shared-CPRNYIql.js → shared-DyhChtHf.js} +4 -1
- package/apps/ui/dist/server/assets/source-session-export-server-DNutCawb.js +38 -0
- package/apps/ui/dist/server/assets/start-B_HFwkkk.js +4 -0
- package/apps/ui/dist/server/assets/tabs-DkaChNVg.js +501 -0
- package/apps/ui/dist/server/assets/thread-transcript-stats-Dh34mt2u.js +45 -0
- package/apps/ui/dist/server/assets/threads._threadId-BeXEc4Lo.js +648 -0
- package/apps/ui/dist/server/assets/{threads._threadId-BLCkGW--.js → threads._threadId-Ci0S4mKU.js} +2 -2
- package/apps/ui/dist/server/assets/ui-export-files-BHLX9bhN.js +83 -0
- package/apps/ui/dist/server/server.js +141 -83
- package/package.json +28 -11
- package/src/lib/antigravity-db.ts +20 -13
- package/src/lib/claude-code-db.ts +655 -0
- package/src/lib/claude-code-exporter-types.ts +110 -0
- package/src/lib/claude-code-transcript-phase.ts +25 -0
- package/src/lib/claude-code-transcript.ts +180 -0
- package/src/lib/codex-analytics.ts +118 -18
- package/src/lib/codex-browser-db.ts +491 -56
- package/src/lib/codex-browser-export.ts +27 -71
- package/src/lib/codex-thread-cache.ts +17 -1
- package/src/lib/concurrency.ts +25 -0
- package/src/lib/cursor-db.ts +318 -7
- package/src/lib/cursor-exporter.ts +2 -2
- package/src/lib/kiro-db.ts +949 -0
- package/src/lib/kiro-exporter-types.ts +100 -0
- package/src/lib/kiro-transcript-phase.ts +41 -0
- package/src/lib/kiro-transcript.ts +115 -0
- package/src/lib/opencode-db.ts +610 -0
- package/src/lib/opencode-exporter-types.ts +129 -0
- package/src/lib/opencode-think-tags.ts +126 -0
- package/src/lib/opencode-transcript-phase.ts +50 -0
- package/src/lib/opencode-transcript.ts +194 -0
- package/src/lib/shared.ts +25 -0
- package/src/lib/ui-cache.ts +14 -1
- package/src/lib/ui-export-archive.ts +61 -0
- package/apps/ui/dist/client/assets/analytics-C83LcWNb.js +0 -1
- package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-DkY8EEpK.js +0 -1
- package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-OraHuz8j.js +0 -1
- package/apps/ui/dist/client/assets/antigravity-keychain-panel-DnDcq1ge.js +0 -1
- package/apps/ui/dist/client/assets/antigravity._workspaceKey-CQaZscA5.js +0 -1
- package/apps/ui/dist/client/assets/antigravity._workspaceKey-DTs99obU.js +0 -1
- package/apps/ui/dist/client/assets/antigravity.index-DbJa715k.js +0 -1
- package/apps/ui/dist/client/assets/antigravity.index-P-rs6w5X.js +0 -1
- package/apps/ui/dist/client/assets/badge-wgTVp-aq.js +0 -1
- package/apps/ui/dist/client/assets/checkbox-BUx0Gkuf.js +0 -1
- package/apps/ui/dist/client/assets/cursor-threads._composerId-D0DFXt8t.js +0 -1
- package/apps/ui/dist/client/assets/cursor-threads._composerId-tFa6Cf6A.js +0 -1
- package/apps/ui/dist/client/assets/cursor._workspaceKey-Df4PfA2k.js +0 -1
- package/apps/ui/dist/client/assets/cursor._workspaceKey-DwrzEtnV.js +0 -1
- package/apps/ui/dist/client/assets/cursor.index-CNBf4ZiR.js +0 -1
- package/apps/ui/dist/client/assets/cursor.index-DP9Hy8X6.js +0 -2
- package/apps/ui/dist/client/assets/delete-confirm-dialog-CPlNsLhM.js +0 -11
- package/apps/ui/dist/client/assets/dist-Ds3kS91l.js +0 -1
- package/apps/ui/dist/client/assets/download-DLuPnxpV.js +0 -1
- package/apps/ui/dist/client/assets/dropdown-menu-lFk6zjoL.js +0 -1
- package/apps/ui/dist/client/assets/es2015-D-H_iZF6.js +0 -41
- package/apps/ui/dist/client/assets/export-dialog-CyqyvwiP.js +0 -1
- package/apps/ui/dist/client/assets/formatters-DbCKP02D.js +0 -1
- package/apps/ui/dist/client/assets/index-C-ZPpKJ0.js +0 -22
- package/apps/ui/dist/client/assets/json-panel-BsngvucB.js +0 -1
- package/apps/ui/dist/client/assets/metadata-section-Dtl8dNag.js +0 -1
- package/apps/ui/dist/client/assets/metric-card-DNzR1Rne.js +0 -1
- package/apps/ui/dist/client/assets/page-header-dha_O7ZL.js +0 -1
- package/apps/ui/dist/client/assets/projects._project-B7HBhbXv.js +0 -1
- package/apps/ui/dist/client/assets/projects._project-DHVvuT7A.js +0 -1
- package/apps/ui/dist/client/assets/projects.index-BUUtx2AZ.js +0 -1
- package/apps/ui/dist/client/assets/projects.index-BojAe1Y4.js +0 -3
- package/apps/ui/dist/client/assets/refresh-ccw-B-ASKHtx.js +0 -1
- package/apps/ui/dist/client/assets/reload-error-panel-DD8M1BIY.js +0 -1
- package/apps/ui/dist/client/assets/routes-q7bzFp35.js +0 -1
- package/apps/ui/dist/client/assets/scroll-text-TvHyiJ_N.js +0 -1
- package/apps/ui/dist/client/assets/select-BFv1HMiT.js +0 -1
- package/apps/ui/dist/client/assets/settings-CvegTo4F.js +0 -1
- package/apps/ui/dist/client/assets/styles-Ch0r3kMZ.css +0 -1
- package/apps/ui/dist/client/assets/text-document-panel-D7Rws41o.js +0 -1
- package/apps/ui/dist/client/assets/text-filter-2q5pEoZh.js +0 -2
- package/apps/ui/dist/client/assets/threads._threadId-LpZVUlPq.js +0 -7
- package/apps/ui/dist/client/assets/threads._threadId-MSmNIBkq.js +0 -1
- package/apps/ui/dist/client/assets/useMutation-BOPJU9e0.js +0 -1
- package/apps/ui/dist/client/assets/useQuery-CbxZiVGF.js +0 -1
- package/apps/ui/dist/server/assets/_tanstack-start-manifest_v-zvGzK-Yx.js +0 -229
- package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-JEhx-f2R.js +0 -212
- package/apps/ui/dist/server/assets/cursor-threads._composerId-BK3iW-3e.js +0 -218
- package/apps/ui/dist/server/assets/json-panel-RYsxWFae.js +0 -16
- package/apps/ui/dist/server/assets/metadata-section-D6Lbc7D6.js +0 -54
- package/apps/ui/dist/server/assets/router-K0m7dK4T.js +0 -394
- package/apps/ui/dist/server/assets/start-bX7bXjzw.js +0 -4
- package/apps/ui/dist/server/assets/text-document-panel-D8JbQWAn.js +0 -23
- package/apps/ui/dist/server/assets/threads._threadId-LjgsGUh-.js +0 -1052
|
@@ -0,0 +1,610 @@
|
|
|
1
|
+
import { constants, Database } from 'bun:sqlite';
|
|
2
|
+
import { stat } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import {
|
|
6
|
+
getDefaultOpenCodeDataDir,
|
|
7
|
+
type OpenCodeModelInfo,
|
|
8
|
+
type OpenCodePartType,
|
|
9
|
+
type OpenCodeSessionSummary,
|
|
10
|
+
type OpenCodeSessionTranscript,
|
|
11
|
+
type OpenCodeStepTokens,
|
|
12
|
+
type OpenCodeTranscriptMessage,
|
|
13
|
+
type OpenCodeTranscriptPart,
|
|
14
|
+
type OpenCodeWorkspaceGroup,
|
|
15
|
+
resolveOpenCodeDbPath,
|
|
16
|
+
} from './opencode-exporter-types';
|
|
17
|
+
import { splitOpenCodeThinkTaggedText } from './opencode-think-tags';
|
|
18
|
+
import {
|
|
19
|
+
asNumber,
|
|
20
|
+
asObject,
|
|
21
|
+
asString,
|
|
22
|
+
isWorkspacePathQuery,
|
|
23
|
+
type JsonValue,
|
|
24
|
+
workspacePathMatchesQuery,
|
|
25
|
+
} from './shared';
|
|
26
|
+
|
|
27
|
+
export { getDefaultOpenCodeDataDir, resolveOpenCodeDbPath };
|
|
28
|
+
|
|
29
|
+
export const OPENCODE_READONLY_DB_OPEN_FLAGS = constants.SQLITE_OPEN_READONLY | constants.SQLITE_OPEN_URI;
|
|
30
|
+
|
|
31
|
+
type WorkspaceRow = {
|
|
32
|
+
archivedSessionCount: number;
|
|
33
|
+
lastActiveMs: number;
|
|
34
|
+
messageCount: number;
|
|
35
|
+
name: string | null;
|
|
36
|
+
partCount: number;
|
|
37
|
+
projectId: string;
|
|
38
|
+
sessionCount: number;
|
|
39
|
+
worktree: string;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
type SessionRow = {
|
|
43
|
+
agent: string | null;
|
|
44
|
+
archivedAtMs: number | null;
|
|
45
|
+
cost: number;
|
|
46
|
+
createdAtMs: number;
|
|
47
|
+
directory: string;
|
|
48
|
+
lastUpdatedAtMs: number;
|
|
49
|
+
messageCount: number;
|
|
50
|
+
model: string | null;
|
|
51
|
+
partCount: number;
|
|
52
|
+
path: string | null;
|
|
53
|
+
permission: string | null;
|
|
54
|
+
projectId: string;
|
|
55
|
+
projectName: string | null;
|
|
56
|
+
renderablePartCount: number;
|
|
57
|
+
sessionId: string;
|
|
58
|
+
slug: string;
|
|
59
|
+
summaryAdditions: number | null;
|
|
60
|
+
summaryDeletions: number | null;
|
|
61
|
+
summaryFiles: number | null;
|
|
62
|
+
textPartCount: number;
|
|
63
|
+
title: string;
|
|
64
|
+
tokensCacheRead: number;
|
|
65
|
+
tokensCacheWrite: number;
|
|
66
|
+
tokensInput: number;
|
|
67
|
+
tokensOutput: number;
|
|
68
|
+
tokensReasoning: number;
|
|
69
|
+
toolPartCount: number;
|
|
70
|
+
worktree: string;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
type MessageRow = {
|
|
74
|
+
data: string;
|
|
75
|
+
messageId: string;
|
|
76
|
+
timeCreated: number;
|
|
77
|
+
timeUpdated: number;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
type PartRow = {
|
|
81
|
+
data: string;
|
|
82
|
+
messageId: string;
|
|
83
|
+
partId: string;
|
|
84
|
+
timeCreated: number;
|
|
85
|
+
timeUpdated: number;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const MAIN_SESSION_FILTER = 's.parent_id IS NULL';
|
|
89
|
+
|
|
90
|
+
const pathExists = async (target: string): Promise<boolean> => {
|
|
91
|
+
return await stat(target)
|
|
92
|
+
.then(() => true)
|
|
93
|
+
.catch(() => false);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export const getOpenCodeReadonlyDbUri = (dbPath: string): string => {
|
|
97
|
+
const url = pathToFileURL(dbPath);
|
|
98
|
+
url.searchParams.set('mode', 'ro');
|
|
99
|
+
return url.href;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export const openOpenCodeReadonlyDb = (dbPath: string): Database => {
|
|
103
|
+
return new Database(getOpenCodeReadonlyDbUri(dbPath), OPENCODE_READONLY_DB_OPEN_FLAGS);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const parseJsonValue = (value: string | null): JsonValue | string | null => {
|
|
107
|
+
if (!value) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
return JSON.parse(value) as JsonValue;
|
|
113
|
+
} catch {
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const parseJsonObject = (value: string): Record<string, JsonValue> => {
|
|
119
|
+
const parsed = parseJsonValue(value);
|
|
120
|
+
return asObject(parsed) ?? {};
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const cleanLabel = (value: string | null | undefined): string | null => {
|
|
124
|
+
const cleaned = value?.replace(/\s+/g, ' ').trim();
|
|
125
|
+
return cleaned ? cleaned : null;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const getWorkspaceLabel = (name: string | null, worktree: string): string => {
|
|
129
|
+
const named = cleanLabel(name);
|
|
130
|
+
if (named) {
|
|
131
|
+
return named;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (worktree === '/') {
|
|
135
|
+
return '(global)';
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return path.basename(worktree.replace(/\/+$/u, '')) || worktree;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const getWorkspaceKey = (projectId: string): string => `project:${projectId}`;
|
|
142
|
+
|
|
143
|
+
const getProjectIdFromWorkspaceKey = (workspaceKey: string): string | null => {
|
|
144
|
+
return workspaceKey.startsWith('project:') ? workspaceKey.slice('project:'.length) : null;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const parseModelInfo = (value: string | null): OpenCodeModelInfo => {
|
|
148
|
+
const parsed = parseJsonValue(value);
|
|
149
|
+
if (typeof parsed === 'string') {
|
|
150
|
+
return {
|
|
151
|
+
id: parsed,
|
|
152
|
+
providerID: null,
|
|
153
|
+
raw: parsed,
|
|
154
|
+
variant: null,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const model = asObject(parsed);
|
|
159
|
+
return {
|
|
160
|
+
id: asString(model?.id ?? null),
|
|
161
|
+
providerID: asString(model?.providerID ?? null),
|
|
162
|
+
raw: parsed,
|
|
163
|
+
variant: asString(model?.variant ?? null),
|
|
164
|
+
};
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const formatModelLabel = (model: OpenCodeModelInfo): string | null => {
|
|
168
|
+
if (typeof model.raw === 'string') {
|
|
169
|
+
return cleanLabel(model.raw);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const id = cleanLabel(model.id);
|
|
173
|
+
const variant = cleanLabel(model.variant);
|
|
174
|
+
if (!id) {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return variant ? `${id} ${variant}` : id;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const toWorkspaceGroup = (row: WorkspaceRow): OpenCodeWorkspaceGroup => {
|
|
182
|
+
const label = getWorkspaceLabel(row.name, row.worktree);
|
|
183
|
+
return {
|
|
184
|
+
archivedSessionCount: row.archivedSessionCount,
|
|
185
|
+
key: getWorkspaceKey(row.projectId),
|
|
186
|
+
label,
|
|
187
|
+
lastActiveMs: row.lastActiveMs,
|
|
188
|
+
messageCount: row.messageCount,
|
|
189
|
+
partCount: row.partCount,
|
|
190
|
+
projectId: row.projectId,
|
|
191
|
+
sessionCount: row.sessionCount,
|
|
192
|
+
uri: pathToFileURL(row.worktree).href,
|
|
193
|
+
worktree: row.worktree,
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const toSessionSummary = (row: SessionRow): OpenCodeSessionSummary => {
|
|
198
|
+
const label = getWorkspaceLabel(row.projectName, row.worktree);
|
|
199
|
+
const model = parseModelInfo(row.model);
|
|
200
|
+
return {
|
|
201
|
+
agent: row.agent,
|
|
202
|
+
archivedAtMs: row.archivedAtMs,
|
|
203
|
+
cost: row.cost,
|
|
204
|
+
createdAtMs: row.createdAtMs,
|
|
205
|
+
directory: row.directory,
|
|
206
|
+
lastUpdatedAtMs: row.lastUpdatedAtMs,
|
|
207
|
+
messageCount: row.messageCount,
|
|
208
|
+
model,
|
|
209
|
+
modelLabel: formatModelLabel(model),
|
|
210
|
+
partCount: row.partCount,
|
|
211
|
+
path: row.path,
|
|
212
|
+
permission: row.permission,
|
|
213
|
+
projectId: row.projectId,
|
|
214
|
+
renderablePartCount: row.renderablePartCount,
|
|
215
|
+
sessionId: row.sessionId,
|
|
216
|
+
slug: row.slug,
|
|
217
|
+
summaryAdditions: row.summaryAdditions,
|
|
218
|
+
summaryDeletions: row.summaryDeletions,
|
|
219
|
+
summaryFiles: row.summaryFiles,
|
|
220
|
+
textPartCount: row.textPartCount,
|
|
221
|
+
title: cleanLabel(row.title) ?? row.sessionId,
|
|
222
|
+
tokensCacheRead: row.tokensCacheRead,
|
|
223
|
+
tokensCacheWrite: row.tokensCacheWrite,
|
|
224
|
+
tokensInput: row.tokensInput,
|
|
225
|
+
tokensOutput: row.tokensOutput,
|
|
226
|
+
tokensReasoning: row.tokensReasoning,
|
|
227
|
+
toolPartCount: row.toolPartCount,
|
|
228
|
+
totalTokens:
|
|
229
|
+
row.tokensInput + row.tokensOutput + row.tokensReasoning + row.tokensCacheRead + row.tokensCacheWrite,
|
|
230
|
+
workspaceKey: getWorkspaceKey(row.projectId),
|
|
231
|
+
workspaceLabel: label,
|
|
232
|
+
worktree: row.worktree,
|
|
233
|
+
};
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const workspaceRowsQuery = `
|
|
237
|
+
SELECT
|
|
238
|
+
p.id AS projectId,
|
|
239
|
+
p.name AS name,
|
|
240
|
+
p.worktree AS worktree,
|
|
241
|
+
(
|
|
242
|
+
SELECT COUNT(*)
|
|
243
|
+
FROM session s
|
|
244
|
+
WHERE s.project_id = p.id AND ${MAIN_SESSION_FILTER}
|
|
245
|
+
) AS sessionCount,
|
|
246
|
+
(
|
|
247
|
+
SELECT COUNT(*)
|
|
248
|
+
FROM session s
|
|
249
|
+
WHERE s.project_id = p.id AND ${MAIN_SESSION_FILTER} AND s.time_archived IS NOT NULL
|
|
250
|
+
) AS archivedSessionCount,
|
|
251
|
+
(
|
|
252
|
+
SELECT COUNT(*)
|
|
253
|
+
FROM message m
|
|
254
|
+
JOIN session s ON s.id = m.session_id
|
|
255
|
+
WHERE s.project_id = p.id AND ${MAIN_SESSION_FILTER}
|
|
256
|
+
) AS messageCount,
|
|
257
|
+
(
|
|
258
|
+
SELECT COUNT(*)
|
|
259
|
+
FROM part prt
|
|
260
|
+
JOIN session s ON s.id = prt.session_id
|
|
261
|
+
WHERE s.project_id = p.id AND ${MAIN_SESSION_FILTER}
|
|
262
|
+
) AS partCount,
|
|
263
|
+
COALESCE((
|
|
264
|
+
SELECT MAX(s.time_updated)
|
|
265
|
+
FROM session s
|
|
266
|
+
WHERE s.project_id = p.id AND ${MAIN_SESSION_FILTER}
|
|
267
|
+
), p.time_updated) AS lastActiveMs
|
|
268
|
+
FROM project p
|
|
269
|
+
`;
|
|
270
|
+
|
|
271
|
+
const sessionSelectQuery = `
|
|
272
|
+
SELECT
|
|
273
|
+
s.id AS sessionId,
|
|
274
|
+
s.project_id AS projectId,
|
|
275
|
+
p.name AS projectName,
|
|
276
|
+
p.worktree AS worktree,
|
|
277
|
+
s.slug AS slug,
|
|
278
|
+
s.directory AS directory,
|
|
279
|
+
s.title AS title,
|
|
280
|
+
s.permission AS permission,
|
|
281
|
+
s.time_created AS createdAtMs,
|
|
282
|
+
s.time_updated AS lastUpdatedAtMs,
|
|
283
|
+
s.time_archived AS archivedAtMs,
|
|
284
|
+
s.path AS path,
|
|
285
|
+
s.agent AS agent,
|
|
286
|
+
s.model AS model,
|
|
287
|
+
s.cost AS cost,
|
|
288
|
+
s.tokens_input AS tokensInput,
|
|
289
|
+
s.tokens_output AS tokensOutput,
|
|
290
|
+
s.tokens_reasoning AS tokensReasoning,
|
|
291
|
+
s.tokens_cache_read AS tokensCacheRead,
|
|
292
|
+
s.tokens_cache_write AS tokensCacheWrite,
|
|
293
|
+
s.summary_files AS summaryFiles,
|
|
294
|
+
s.summary_additions AS summaryAdditions,
|
|
295
|
+
s.summary_deletions AS summaryDeletions,
|
|
296
|
+
(
|
|
297
|
+
SELECT COUNT(*)
|
|
298
|
+
FROM message m
|
|
299
|
+
WHERE m.session_id = s.id
|
|
300
|
+
) AS messageCount,
|
|
301
|
+
(
|
|
302
|
+
SELECT COUNT(*)
|
|
303
|
+
FROM part prt
|
|
304
|
+
WHERE prt.session_id = s.id
|
|
305
|
+
) AS partCount,
|
|
306
|
+
(
|
|
307
|
+
SELECT COUNT(*)
|
|
308
|
+
FROM part prt
|
|
309
|
+
WHERE prt.session_id = s.id AND json_extract(prt.data, '$.type') = 'text'
|
|
310
|
+
) AS textPartCount,
|
|
311
|
+
(
|
|
312
|
+
SELECT COUNT(*)
|
|
313
|
+
FROM part prt
|
|
314
|
+
WHERE prt.session_id = s.id AND json_extract(prt.data, '$.type') = 'tool'
|
|
315
|
+
) AS toolPartCount,
|
|
316
|
+
(
|
|
317
|
+
SELECT COUNT(*)
|
|
318
|
+
FROM part prt
|
|
319
|
+
WHERE prt.session_id = s.id
|
|
320
|
+
AND (
|
|
321
|
+
(
|
|
322
|
+
json_extract(prt.data, '$.type') IN ('text', 'reasoning')
|
|
323
|
+
AND trim(COALESCE(json_extract(prt.data, '$.text'), '')) <> ''
|
|
324
|
+
)
|
|
325
|
+
OR json_extract(prt.data, '$.type') = 'tool'
|
|
326
|
+
)
|
|
327
|
+
) AS renderablePartCount
|
|
328
|
+
FROM session s
|
|
329
|
+
JOIN project p ON p.id = s.project_id
|
|
330
|
+
`;
|
|
331
|
+
|
|
332
|
+
export const listOpenCodeWorkspaceGroups = async (
|
|
333
|
+
dbPath = resolveOpenCodeDbPath(),
|
|
334
|
+
): Promise<OpenCodeWorkspaceGroup[]> => {
|
|
335
|
+
if (!(await pathExists(dbPath))) {
|
|
336
|
+
return [];
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const db = openOpenCodeReadonlyDb(dbPath);
|
|
340
|
+
try {
|
|
341
|
+
const rows = db
|
|
342
|
+
.query(`${workspaceRowsQuery} ORDER BY lastActiveMs DESC, p.worktree ASC`)
|
|
343
|
+
.all() as WorkspaceRow[];
|
|
344
|
+
return rows.map(toWorkspaceGroup);
|
|
345
|
+
} finally {
|
|
346
|
+
db.close();
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
const openCodeWorkspaceMatchesQuery = (workspace: OpenCodeWorkspaceGroup, query: string): boolean => {
|
|
351
|
+
const raw = query.trim();
|
|
352
|
+
if (!raw) {
|
|
353
|
+
return true;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const lowered = raw.toLowerCase();
|
|
357
|
+
if (
|
|
358
|
+
workspace.key.toLowerCase() === lowered ||
|
|
359
|
+
workspace.projectId.toLowerCase() === lowered ||
|
|
360
|
+
workspace.label.toLowerCase() === lowered
|
|
361
|
+
) {
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (isWorkspacePathQuery(raw)) {
|
|
366
|
+
return workspacePathMatchesQuery(workspace.worktree, raw);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return path.basename(workspace.worktree).toLowerCase() === lowered;
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
export const findOpenCodeWorkspaceGroups = (
|
|
373
|
+
groups: OpenCodeWorkspaceGroup[],
|
|
374
|
+
query: string,
|
|
375
|
+
): OpenCodeWorkspaceGroup[] => {
|
|
376
|
+
return groups.filter((group) => openCodeWorkspaceMatchesQuery(group, query));
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
const readSessionSummaries = (db: Database, whereSql: string, params: string[]): OpenCodeSessionSummary[] => {
|
|
380
|
+
const rows = db
|
|
381
|
+
.query(`${sessionSelectQuery} WHERE ${whereSql} ORDER BY s.time_updated DESC, s.title ASC`)
|
|
382
|
+
.all(...params) as SessionRow[];
|
|
383
|
+
return rows.map(toSessionSummary);
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
export const listOpenCodeSessionsForGroup = async (
|
|
387
|
+
workspaceKey: string,
|
|
388
|
+
dbPath = resolveOpenCodeDbPath(),
|
|
389
|
+
): Promise<OpenCodeSessionSummary[]> => {
|
|
390
|
+
const projectId = getProjectIdFromWorkspaceKey(workspaceKey);
|
|
391
|
+
if (!projectId || !(await pathExists(dbPath))) {
|
|
392
|
+
return [];
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const db = openOpenCodeReadonlyDb(dbPath);
|
|
396
|
+
try {
|
|
397
|
+
return readSessionSummaries(db, `s.project_id = ? AND ${MAIN_SESSION_FILTER}`, [projectId]);
|
|
398
|
+
} finally {
|
|
399
|
+
db.close();
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
const readOpenCodeSessionSummary = (db: Database, sessionId: string): OpenCodeSessionSummary | null => {
|
|
404
|
+
return readSessionSummaries(db, `s.id = ? AND ${MAIN_SESSION_FILTER}`, [sessionId])[0] ?? null;
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
const getMessageRole = (raw: Record<string, JsonValue>): string => asString(raw.role ?? null) ?? 'unknown';
|
|
408
|
+
|
|
409
|
+
const toPartType = (value: JsonValue | undefined): OpenCodePartType => {
|
|
410
|
+
switch (value) {
|
|
411
|
+
case 'reasoning':
|
|
412
|
+
case 'step-finish':
|
|
413
|
+
case 'step-start':
|
|
414
|
+
case 'text':
|
|
415
|
+
case 'tool':
|
|
416
|
+
return value;
|
|
417
|
+
default:
|
|
418
|
+
return 'unknown';
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
const formatJsonLike = (value: JsonValue | undefined): string | null => {
|
|
423
|
+
if (value === undefined || value === null) {
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (typeof value === 'string') {
|
|
428
|
+
return value;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return JSON.stringify(value, null, 2);
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
const parseTimeMs = (raw: Record<string, JsonValue>, key: 'end' | 'start'): number | null => {
|
|
435
|
+
const time = asObject(raw.time ?? null);
|
|
436
|
+
return asNumber(time?.[key] ?? null);
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
const parseToolTimeMs = (raw: Record<string, JsonValue>, key: 'end' | 'start'): number | null => {
|
|
440
|
+
const state = asObject(raw.state ?? null);
|
|
441
|
+
const time = asObject(state?.time ?? null) ?? asObject(raw.time ?? null);
|
|
442
|
+
return asNumber(time?.[key] ?? null);
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
const parseStepTokens = (value: JsonValue | undefined): OpenCodeStepTokens | null => {
|
|
446
|
+
const tokens = asObject(value ?? null);
|
|
447
|
+
if (!tokens) {
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const cache = asObject(tokens.cache ?? null);
|
|
452
|
+
return {
|
|
453
|
+
cacheRead: asNumber(cache?.read ?? null) ?? 0,
|
|
454
|
+
cacheWrite: asNumber(cache?.write ?? null) ?? 0,
|
|
455
|
+
input: asNumber(tokens.input ?? null) ?? 0,
|
|
456
|
+
output: asNumber(tokens.output ?? null) ?? 0,
|
|
457
|
+
reasoning: asNumber(tokens.reasoning ?? null) ?? 0,
|
|
458
|
+
total: asNumber(tokens.total ?? null) ?? 0,
|
|
459
|
+
};
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
type BaseOpenCodePart = Omit<OpenCodeTranscriptPart, 'type'> & { type: OpenCodePartType };
|
|
463
|
+
|
|
464
|
+
const parseTextOpenCodePart = (base: BaseOpenCodePart): OpenCodeTranscriptPart => ({
|
|
465
|
+
...base,
|
|
466
|
+
endTimeMs: parseTimeMs(base.raw, 'end'),
|
|
467
|
+
startTimeMs: parseTimeMs(base.raw, 'start'),
|
|
468
|
+
text: asString(base.raw.text ?? null) ?? '',
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
const parseToolOpenCodePart = (base: BaseOpenCodePart): OpenCodeTranscriptPart => {
|
|
472
|
+
const state = asObject(base.raw.state ?? null);
|
|
473
|
+
return {
|
|
474
|
+
...base,
|
|
475
|
+
argumentsText: formatJsonLike(state?.input),
|
|
476
|
+
callId: asString(base.raw.callID ?? null) ?? asString(base.raw.callId ?? null),
|
|
477
|
+
endTimeMs: parseToolTimeMs(base.raw, 'end'),
|
|
478
|
+
outputText: formatJsonLike(state?.output),
|
|
479
|
+
startTimeMs: parseToolTimeMs(base.raw, 'start'),
|
|
480
|
+
status: asString(state?.status ?? null),
|
|
481
|
+
title: asString(state?.title ?? null),
|
|
482
|
+
toolName: asString(base.raw.tool ?? null) ?? 'unknown',
|
|
483
|
+
};
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
const parseStepFinishOpenCodePart = (base: BaseOpenCodePart): OpenCodeTranscriptPart => ({
|
|
487
|
+
...base,
|
|
488
|
+
reason: asString(base.raw.reason ?? null),
|
|
489
|
+
snapshot: asString(base.raw.snapshot ?? null),
|
|
490
|
+
tokens: parseStepTokens(base.raw.tokens),
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
const parseStepStartOpenCodePart = (base: BaseOpenCodePart): OpenCodeTranscriptPart => ({
|
|
494
|
+
...base,
|
|
495
|
+
snapshot: asString(base.raw.snapshot ?? null),
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
const parseOpenCodePartByType = (base: BaseOpenCodePart): OpenCodeTranscriptPart => {
|
|
499
|
+
switch (base.type) {
|
|
500
|
+
case 'reasoning':
|
|
501
|
+
case 'text':
|
|
502
|
+
return parseTextOpenCodePart(base);
|
|
503
|
+
case 'tool':
|
|
504
|
+
return parseToolOpenCodePart(base);
|
|
505
|
+
case 'step-finish':
|
|
506
|
+
return parseStepFinishOpenCodePart(base);
|
|
507
|
+
case 'step-start':
|
|
508
|
+
return parseStepStartOpenCodePart(base);
|
|
509
|
+
case 'unknown':
|
|
510
|
+
return base;
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
const parseOpenCodePart = (row: PartRow, role: string): OpenCodeTranscriptPart => {
|
|
515
|
+
const raw = parseJsonObject(row.data);
|
|
516
|
+
return parseOpenCodePartByType({
|
|
517
|
+
createdAtMs: row.timeCreated,
|
|
518
|
+
messageId: row.messageId,
|
|
519
|
+
partId: row.partId,
|
|
520
|
+
raw,
|
|
521
|
+
role,
|
|
522
|
+
type: toPartType(raw.type),
|
|
523
|
+
updatedAtMs: row.timeUpdated,
|
|
524
|
+
});
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
const isRenderablePart = (part: OpenCodeTranscriptPart): boolean => {
|
|
528
|
+
if (part.type === 'text' || part.type === 'reasoning') {
|
|
529
|
+
const { reasoningBlocks, visibleText } = splitOpenCodeThinkTaggedText(part.text ?? '');
|
|
530
|
+
return Boolean(visibleText.trim() || reasoningBlocks.length > 0);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
if (part.type === 'tool') {
|
|
534
|
+
return Boolean(part.toolName || part.outputText?.trim() || part.argumentsText?.trim());
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
return false;
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
const readMessages = (db: Database, sessionId: string): OpenCodeTranscriptMessage[] => {
|
|
541
|
+
const messageRows = db
|
|
542
|
+
.query(
|
|
543
|
+
`SELECT id AS messageId, time_created AS timeCreated, time_updated AS timeUpdated, data
|
|
544
|
+
FROM message
|
|
545
|
+
WHERE session_id = ?
|
|
546
|
+
ORDER BY time_created ASC, id ASC`,
|
|
547
|
+
)
|
|
548
|
+
.all(sessionId) as MessageRow[];
|
|
549
|
+
const partRows = db
|
|
550
|
+
.query(
|
|
551
|
+
`SELECT id AS partId, message_id AS messageId, time_created AS timeCreated, time_updated AS timeUpdated, data
|
|
552
|
+
FROM part
|
|
553
|
+
WHERE session_id = ?
|
|
554
|
+
ORDER BY time_created ASC, id ASC`,
|
|
555
|
+
)
|
|
556
|
+
.all(sessionId) as PartRow[];
|
|
557
|
+
const partsByMessageId = new Map<string, PartRow[]>();
|
|
558
|
+
for (const part of partRows) {
|
|
559
|
+
const parts = partsByMessageId.get(part.messageId) ?? [];
|
|
560
|
+
parts.push(part);
|
|
561
|
+
partsByMessageId.set(part.messageId, parts);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
return messageRows.map((message): OpenCodeTranscriptMessage => {
|
|
565
|
+
const raw = parseJsonObject(message.data);
|
|
566
|
+
const role = getMessageRole(raw);
|
|
567
|
+
const parts = (partsByMessageId.get(message.messageId) ?? []).map((part) => parseOpenCodePart(part, role));
|
|
568
|
+
return {
|
|
569
|
+
createdAtMs: message.timeCreated,
|
|
570
|
+
messageId: message.messageId,
|
|
571
|
+
parts,
|
|
572
|
+
raw,
|
|
573
|
+
role,
|
|
574
|
+
updatedAtMs: message.timeUpdated,
|
|
575
|
+
};
|
|
576
|
+
});
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
export const readOpenCodeSessionTranscript = async (
|
|
580
|
+
dbPath: string,
|
|
581
|
+
sessionId: string,
|
|
582
|
+
): Promise<OpenCodeSessionTranscript | null> => {
|
|
583
|
+
if (!(await pathExists(dbPath))) {
|
|
584
|
+
return null;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
const db = openOpenCodeReadonlyDb(dbPath);
|
|
588
|
+
try {
|
|
589
|
+
const session = readOpenCodeSessionSummary(db, sessionId);
|
|
590
|
+
if (!session) {
|
|
591
|
+
return null;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const messages = readMessages(db, sessionId);
|
|
595
|
+
const partCount = messages.reduce((total, message) => total + message.parts.length, 0);
|
|
596
|
+
const renderablePartCount = messages.reduce(
|
|
597
|
+
(total, message) => total + message.parts.filter(isRenderablePart).length,
|
|
598
|
+
0,
|
|
599
|
+
);
|
|
600
|
+
|
|
601
|
+
return {
|
|
602
|
+
messages,
|
|
603
|
+
partCount,
|
|
604
|
+
renderablePartCount,
|
|
605
|
+
session,
|
|
606
|
+
};
|
|
607
|
+
} finally {
|
|
608
|
+
db.close();
|
|
609
|
+
}
|
|
610
|
+
};
|