spiracha 1.5.0 → 2.0.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.
Files changed (365) hide show
  1. package/AGENTS.md +114 -205
  2. package/README.md +89 -269
  3. package/apps/ui/AGENTS.md +17 -7
  4. package/apps/ui/README.md +34 -8
  5. package/apps/ui/components.json +21 -0
  6. package/apps/ui/package.json +65 -0
  7. package/apps/ui/src/components/antigravity-conversations-table.tsx +206 -0
  8. package/apps/ui/src/components/antigravity-keychain-panel.tsx +76 -0
  9. package/apps/ui/src/components/antigravity-workspaces-table.tsx +63 -0
  10. package/apps/ui/src/components/app-shell.tsx +124 -0
  11. package/apps/ui/src/components/breadcrumbs.tsx +53 -0
  12. package/apps/ui/src/components/claude-code-sessions-table.tsx +114 -0
  13. package/apps/ui/src/components/claude-code-workspaces-table.tsx +55 -0
  14. package/apps/ui/src/components/cursor-threads-table.tsx +189 -0
  15. package/apps/ui/src/components/cursor-workspaces-table.tsx +140 -0
  16. package/apps/ui/src/components/data-table.tsx +241 -0
  17. package/apps/ui/src/components/delete-confirm-dialog.tsx +88 -0
  18. package/apps/ui/src/components/export-dialog.tsx +184 -0
  19. package/apps/ui/src/components/json-panel.tsx +17 -0
  20. package/apps/ui/src/components/kiro-sessions-table.tsx +112 -0
  21. package/apps/ui/src/components/kiro-workspaces-table.tsx +53 -0
  22. package/apps/ui/src/components/list-search-input.tsx +18 -0
  23. package/apps/ui/src/components/loading-panel.tsx +23 -0
  24. package/apps/ui/src/components/metadata-section.tsx +31 -0
  25. package/apps/ui/src/components/metric-card.tsx +19 -0
  26. package/apps/ui/src/components/opencode-sessions-table.tsx +128 -0
  27. package/apps/ui/src/components/opencode-workspaces-table.tsx +57 -0
  28. package/apps/ui/src/components/page-header.tsx +33 -0
  29. package/apps/ui/src/components/projects-loading-state.tsx +74 -0
  30. package/apps/ui/src/components/projects-table.tsx +108 -0
  31. package/apps/ui/src/components/qoder-sessions-table.tsx +112 -0
  32. package/apps/ui/src/components/qoder-workspaces-table.tsx +53 -0
  33. package/apps/ui/src/components/recent-threads-list.tsx +52 -0
  34. package/apps/ui/src/components/reload-error-panel.tsx +20 -0
  35. package/apps/ui/src/components/text-document-panel.tsx +19 -0
  36. package/apps/ui/src/components/theme-toggle.tsx +48 -0
  37. package/apps/ui/src/components/threads-table.tsx +202 -0
  38. package/apps/ui/src/components/transcript-view.tsx +552 -0
  39. package/apps/ui/src/components/ui/alert-dialog.tsx +160 -0
  40. package/apps/ui/src/components/ui/badge.tsx +45 -0
  41. package/apps/ui/src/components/ui/button.tsx +62 -0
  42. package/apps/ui/src/components/ui/checkbox.tsx +29 -0
  43. package/apps/ui/src/components/ui/dialog.tsx +137 -0
  44. package/apps/ui/src/components/ui/dropdown-menu.tsx +217 -0
  45. package/apps/ui/src/components/ui/input.tsx +21 -0
  46. package/apps/ui/src/components/ui/scroll-area.tsx +46 -0
  47. package/apps/ui/src/components/ui/select.tsx +163 -0
  48. package/apps/ui/src/components/ui/separator.tsx +28 -0
  49. package/apps/ui/src/components/ui/sheet.tsx +107 -0
  50. package/apps/ui/src/components/ui/skeleton.tsx +7 -0
  51. package/apps/ui/src/components/ui/table.tsx +76 -0
  52. package/apps/ui/src/components/ui/tabs.tsx +71 -0
  53. package/apps/ui/src/components/ui/tooltip.tsx +44 -0
  54. package/apps/ui/src/integrations/tanstack-query/devtools.tsx +6 -0
  55. package/apps/ui/src/integrations/tanstack-query/root-provider.tsx +10 -0
  56. package/apps/ui/src/lib/antigravity-conversation-state.ts +28 -0
  57. package/apps/ui/src/lib/antigravity-queries.ts +33 -0
  58. package/apps/ui/src/lib/antigravity-server.ts +128 -0
  59. package/apps/ui/src/lib/antigravity-transcript-events.ts +335 -0
  60. package/apps/ui/src/lib/claude-code-queries.ts +26 -0
  61. package/apps/ui/src/lib/claude-code-server.ts +75 -0
  62. package/apps/ui/src/lib/claude-code-transcript-events.ts +154 -0
  63. package/apps/ui/src/lib/codex-queries.ts +66 -0
  64. package/apps/ui/src/lib/codex-server.ts +209 -0
  65. package/apps/ui/src/lib/cursor-queries.ts +37 -0
  66. package/apps/ui/src/lib/cursor-server.ts +272 -0
  67. package/apps/ui/src/lib/cursor-transcript-events.ts +229 -0
  68. package/apps/ui/src/lib/download.ts +166 -0
  69. package/apps/ui/src/lib/formatters.ts +149 -0
  70. package/apps/ui/src/lib/kiro-queries.ts +22 -0
  71. package/apps/ui/src/lib/kiro-server.ts +73 -0
  72. package/apps/ui/src/lib/kiro-transcript-events.ts +113 -0
  73. package/apps/ui/src/lib/opencode-queries.ts +37 -0
  74. package/apps/ui/src/lib/opencode-server.ts +73 -0
  75. package/apps/ui/src/lib/opencode-transcript-events.ts +221 -0
  76. package/apps/ui/src/lib/package-metadata.ts +16 -0
  77. package/apps/ui/src/lib/path-utils.ts +13 -0
  78. package/apps/ui/src/lib/qoder-queries.ts +22 -0
  79. package/apps/ui/src/lib/qoder-server.ts +79 -0
  80. package/apps/ui/src/lib/qoder-transcript-events.ts +139 -0
  81. package/apps/ui/src/lib/route-search.ts +68 -0
  82. package/apps/ui/src/lib/settings-store.tsx +59 -0
  83. package/apps/ui/src/lib/source-session-export-server.ts +56 -0
  84. package/apps/ui/src/lib/text-filter.ts +22 -0
  85. package/apps/ui/src/lib/thread-id.ts +3 -0
  86. package/apps/ui/src/lib/thread-transcript-stats.ts +57 -0
  87. package/apps/ui/src/lib/utils.ts +7 -0
  88. package/apps/ui/src/routeTree.gen.ts +730 -0
  89. package/apps/ui/src/router.tsx +20 -0
  90. package/apps/ui/src/routes/$threadId.tsx +22 -0
  91. package/apps/ui/src/routes/__root.tsx +127 -0
  92. package/apps/ui/src/routes/analytics.tsx +143 -0
  93. package/apps/ui/src/routes/antigravity-conversations.$conversationId.tsx +438 -0
  94. package/apps/ui/src/routes/antigravity.$workspaceKey.tsx +127 -0
  95. package/apps/ui/src/routes/antigravity.index.tsx +51 -0
  96. package/apps/ui/src/routes/api.v1.conversation-query.ts +12 -0
  97. package/apps/ui/src/routes/api.v1.conversations.$source.$id.export.ts +12 -0
  98. package/apps/ui/src/routes/api.v1.conversations.$source.$id.ts +12 -0
  99. package/apps/ui/src/routes/api.v1.conversations.ts +12 -0
  100. package/apps/ui/src/routes/api.v1.resolve.ts +12 -0
  101. package/apps/ui/src/routes/api.v1.sources.ts +12 -0
  102. package/apps/ui/src/routes/claude-code-sessions.$sessionId.tsx +342 -0
  103. package/apps/ui/src/routes/claude-code.$workspaceKey.tsx +135 -0
  104. package/apps/ui/src/routes/claude-code.index.tsx +48 -0
  105. package/apps/ui/src/routes/codex.$project.tsx +390 -0
  106. package/apps/ui/src/routes/codex.index.tsx +116 -0
  107. package/apps/ui/src/routes/cursor-threads.$composerId.tsx +435 -0
  108. package/apps/ui/src/routes/cursor.$workspaceKey.tsx +416 -0
  109. package/apps/ui/src/routes/cursor.index.tsx +120 -0
  110. package/apps/ui/src/routes/index.tsx +124 -0
  111. package/apps/ui/src/routes/kiro-sessions.$sessionId.tsx +345 -0
  112. package/apps/ui/src/routes/kiro.$workspaceKey.tsx +136 -0
  113. package/apps/ui/src/routes/kiro.index.tsx +48 -0
  114. package/apps/ui/src/routes/opencode-sessions.$sessionId.tsx +334 -0
  115. package/apps/ui/src/routes/opencode.$workspaceKey.tsx +135 -0
  116. package/apps/ui/src/routes/opencode.index.tsx +48 -0
  117. package/apps/ui/src/routes/qoder-sessions.$sessionId.tsx +341 -0
  118. package/apps/ui/src/routes/qoder.$workspaceKey.tsx +139 -0
  119. package/apps/ui/src/routes/qoder.index.tsx +48 -0
  120. package/apps/ui/src/routes/settings.tsx +91 -0
  121. package/apps/ui/src/routes/threads.$threadId.tsx +619 -0
  122. package/apps/ui/src/styles.css +122 -0
  123. package/apps/ui/tsconfig.json +29 -0
  124. package/apps/ui/vite.config.ts +84 -0
  125. package/bin/spiracha.ts +32 -0
  126. package/package.json +46 -73
  127. package/src/client.ts +290 -0
  128. package/src/lib/antigravity-db.ts +213 -19
  129. package/src/lib/antigravity-exporter-types.ts +1 -0
  130. package/src/lib/codex-analytics.ts +1 -1
  131. package/src/lib/codex-browser-db.ts +410 -74
  132. package/src/lib/codex-browser-export.ts +6 -14
  133. package/src/lib/codex-browser-types.ts +1 -1
  134. package/src/lib/{codex-exporter-types.ts → codex-thread-types.ts} +2 -30
  135. package/src/lib/{codex-exporter-transcript.ts → codex-transcript-renderer.ts} +64 -28
  136. package/src/lib/conversation-api.ts +792 -0
  137. package/src/lib/conversation-data/adapter-helpers.ts +110 -0
  138. package/src/lib/conversation-data/antigravity-adapter.ts +148 -0
  139. package/src/lib/conversation-data/claude-code-adapter.ts +175 -0
  140. package/src/lib/conversation-data/codex-adapter.ts +245 -0
  141. package/src/lib/conversation-data/cursor-adapter.ts +171 -0
  142. package/src/lib/conversation-data/index.ts +311 -0
  143. package/src/lib/conversation-data/kiro-adapter.ts +140 -0
  144. package/src/lib/conversation-data/message-selector.ts +37 -0
  145. package/src/lib/conversation-data/opencode-adapter.ts +180 -0
  146. package/src/lib/conversation-data/path-match.ts +79 -0
  147. package/src/lib/conversation-data/qoder-adapter.ts +230 -0
  148. package/src/lib/conversation-data/types.ts +116 -0
  149. package/src/lib/cursor-db.ts +67 -21
  150. package/src/lib/cursor-exporter-types.ts +0 -19
  151. package/src/lib/qoder-acp-client.ts +268 -0
  152. package/src/lib/qoder-db.ts +1601 -0
  153. package/src/lib/qoder-exporter-types.ts +114 -0
  154. package/src/lib/qoder-transcript-phase.ts +41 -0
  155. package/src/lib/qoder-transcript.ts +112 -0
  156. package/apps/ui/dist/client/assets/_threadId-CAIeH5mq.js +0 -1
  157. package/apps/ui/dist/client/assets/analytics-DOy1b6Sh.js +0 -1
  158. package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-DqySVO2K.js +0 -7
  159. package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-KAcXtNxI.js +0 -1
  160. package/apps/ui/dist/client/assets/antigravity-keychain-panel-B77qJz6f.js +0 -1
  161. package/apps/ui/dist/client/assets/antigravity._workspaceKey-B3NM6gRH.js +0 -1
  162. package/apps/ui/dist/client/assets/antigravity._workspaceKey-ByitGWki.js +0 -1
  163. package/apps/ui/dist/client/assets/antigravity.index-DP9JC2P9.js +0 -1
  164. package/apps/ui/dist/client/assets/antigravity.index-DgW25FEV.js +0 -1
  165. package/apps/ui/dist/client/assets/badge-C3i4OKs8.js +0 -1
  166. package/apps/ui/dist/client/assets/checkbox-CdHitXWQ.js +0 -1
  167. package/apps/ui/dist/client/assets/claude-code-sessions._sessionId-8pQ55ci2.js +0 -1
  168. package/apps/ui/dist/client/assets/claude-code-sessions._sessionId-DVz5UmtU.js +0 -1
  169. package/apps/ui/dist/client/assets/claude-code._workspaceKey-BBKskSum.js +0 -1
  170. package/apps/ui/dist/client/assets/claude-code._workspaceKey-OkikMUFs.js +0 -1
  171. package/apps/ui/dist/client/assets/claude-code.index-Cwu76dbb.js +0 -1
  172. package/apps/ui/dist/client/assets/claude-code.index-D6eMd08t.js +0 -1
  173. package/apps/ui/dist/client/assets/createServerFn-B-KkwmHU.js +0 -3
  174. package/apps/ui/dist/client/assets/cursor-threads._composerId-CmBFclK8.js +0 -1
  175. package/apps/ui/dist/client/assets/cursor-threads._composerId-hBWlP1D-.js +0 -1
  176. package/apps/ui/dist/client/assets/cursor._workspaceKey-BFPkIRi-.js +0 -1
  177. package/apps/ui/dist/client/assets/cursor._workspaceKey-CaAZjGmm.js +0 -1
  178. package/apps/ui/dist/client/assets/cursor.index-8y4o20kb.js +0 -2
  179. package/apps/ui/dist/client/assets/cursor.index-CrnMwDCi.js +0 -1
  180. package/apps/ui/dist/client/assets/data-table-Br78_dk3.js +0 -4
  181. package/apps/ui/dist/client/assets/delete-confirm-dialog-Bxs07u-j.js +0 -7
  182. package/apps/ui/dist/client/assets/dist-B9pyYCVU.js +0 -1
  183. package/apps/ui/dist/client/assets/dist-BrJhN2A0.js +0 -5
  184. package/apps/ui/dist/client/assets/dist-Cmd3AIfD.js +0 -1
  185. package/apps/ui/dist/client/assets/dist-DvMS2965.js +0 -1
  186. package/apps/ui/dist/client/assets/download-Bf0QJKD3.js +0 -1
  187. package/apps/ui/dist/client/assets/dropdown-menu-D5cvPaZt.js +0 -1
  188. package/apps/ui/dist/client/assets/es2015-ad6040dw.js +0 -41
  189. package/apps/ui/dist/client/assets/export-dialog-Dr8d2MqD.js +0 -1
  190. package/apps/ui/dist/client/assets/formatters-BtqyrX_c.js +0 -1
  191. package/apps/ui/dist/client/assets/index-C-STgIH4.js +0 -166
  192. package/apps/ui/dist/client/assets/jsx-runtime-DGeXAQPT.js +0 -1
  193. package/apps/ui/dist/client/assets/kiro-sessions._sessionId-CKgfJ7GR.js +0 -1
  194. package/apps/ui/dist/client/assets/kiro-sessions._sessionId-zvIJHtQF.js +0 -1
  195. package/apps/ui/dist/client/assets/kiro._workspaceKey-DhVuJuZB.js +0 -1
  196. package/apps/ui/dist/client/assets/kiro._workspaceKey-QArsjkTY.js +0 -1
  197. package/apps/ui/dist/client/assets/kiro.index-C-fr4m7i.js +0 -1
  198. package/apps/ui/dist/client/assets/kiro.index-OYgAdtZd.js +0 -1
  199. package/apps/ui/dist/client/assets/metric-card-Dplm0ZiM.js +0 -1
  200. package/apps/ui/dist/client/assets/opencode-sessions._sessionId-DLjiTst-.js +0 -1
  201. package/apps/ui/dist/client/assets/opencode-sessions._sessionId-biBwrRbt.js +0 -8
  202. package/apps/ui/dist/client/assets/opencode._workspaceKey-C7OIzMOJ.js +0 -1
  203. package/apps/ui/dist/client/assets/opencode._workspaceKey-sBakX0oI.js +0 -1
  204. package/apps/ui/dist/client/assets/opencode.index-Bl_kNs9N.js +0 -1
  205. package/apps/ui/dist/client/assets/opencode.index-CN53bVmb.js +0 -1
  206. package/apps/ui/dist/client/assets/page-header-Cwa3p6Tl.js +0 -1
  207. package/apps/ui/dist/client/assets/preload-helper-B8fFrdxC.js +0 -1
  208. package/apps/ui/dist/client/assets/projects._project-D8WloxQN.js +0 -1
  209. package/apps/ui/dist/client/assets/projects._project-DSDwH4NF.js +0 -1
  210. package/apps/ui/dist/client/assets/projects.index-5xRtuJdX.js +0 -3
  211. package/apps/ui/dist/client/assets/projects.index-DEfhWqa8.js +0 -1
  212. package/apps/ui/dist/client/assets/react-dom-DL96Jor4.js +0 -1
  213. package/apps/ui/dist/client/assets/refresh-ccw-BgKjOiA6.js +0 -1
  214. package/apps/ui/dist/client/assets/reload-error-panel-AHqu0akK.js +0 -1
  215. package/apps/ui/dist/client/assets/routes-DnzF_9CX.js +0 -1
  216. package/apps/ui/dist/client/assets/scroll-text-fM0sMOB3.js +0 -1
  217. package/apps/ui/dist/client/assets/select-CYF6pF37.js +0 -1
  218. package/apps/ui/dist/client/assets/settings-Dx70jgP_.js +0 -1
  219. package/apps/ui/dist/client/assets/sqlite-error-B6uZjUe8.js +0 -1
  220. package/apps/ui/dist/client/assets/styles-B7Zi9EFA.css +0 -1
  221. package/apps/ui/dist/client/assets/tabs-CmuhkT-c.js +0 -7
  222. package/apps/ui/dist/client/assets/text-filter-DVAZySUS.js +0 -2
  223. package/apps/ui/dist/client/assets/thread-transcript-stats-BUNz6u7F.js +0 -1
  224. package/apps/ui/dist/client/assets/threads._threadId-BB9tTsV4.js +0 -1
  225. package/apps/ui/dist/client/assets/threads._threadId-BMRSFow3.js +0 -1
  226. package/apps/ui/dist/client/assets/useMutation-BSFult2i.js +0 -1
  227. package/apps/ui/dist/client/assets/useQuery-CjINKZf1.js +0 -1
  228. package/apps/ui/dist/server/assets/_tanstack-start-manifest_v-CQ8Jjs3B.js +0 -387
  229. package/apps/ui/dist/server/assets/_threadId-B6SrBR9E.js +0 -6
  230. package/apps/ui/dist/server/assets/analytics-CYP_LT5Y.js +0 -16
  231. package/apps/ui/dist/server/assets/analytics-CtpOMVMO.js +0 -146
  232. package/apps/ui/dist/server/assets/antigravity-conversation-state-HgzS302O.js +0 -16
  233. package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-BKOyL2T6.js +0 -613
  234. package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-D426O-64.js +0 -11
  235. package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-eS87Dx7g.js +0 -20
  236. package/apps/ui/dist/server/assets/antigravity-db-Bh8_U9uw.js +0 -580
  237. package/apps/ui/dist/server/assets/antigravity-keychain-DOiuHDwK.js +0 -126
  238. package/apps/ui/dist/server/assets/antigravity-keychain-panel-CC7FLmAK.js +0 -55
  239. package/apps/ui/dist/server/assets/antigravity-queries-CGrJO9Vr.js +0 -37
  240. package/apps/ui/dist/server/assets/antigravity-server-CHRjVFqN.js +0 -114
  241. package/apps/ui/dist/server/assets/antigravity._workspaceKey-3m_MzNFA.js +0 -11
  242. package/apps/ui/dist/server/assets/antigravity._workspaceKey-B0QeYUOh.js +0 -28
  243. package/apps/ui/dist/server/assets/antigravity._workspaceKey-C3kP-qyN.js +0 -210
  244. package/apps/ui/dist/server/assets/antigravity.index-CPCYkqCi.js +0 -104
  245. package/apps/ui/dist/server/assets/antigravity.index-DudTB3Tq.js +0 -11
  246. package/apps/ui/dist/server/assets/badge-EvdhKK_Z.js +0 -26
  247. package/apps/ui/dist/server/assets/button-CmTDnzOn.js +0 -46
  248. package/apps/ui/dist/server/assets/checkbox-C0hovF41.js +0 -19
  249. package/apps/ui/dist/server/assets/claude-code-db-Ik3VStKZ.js +0 -361
  250. package/apps/ui/dist/server/assets/claude-code-queries-Cgzi4K-e.js +0 -37
  251. package/apps/ui/dist/server/assets/claude-code-server-PfraClXS.js +0 -72
  252. package/apps/ui/dist/server/assets/claude-code-sessions._sessionId-C55oe6zj.js +0 -18
  253. package/apps/ui/dist/server/assets/claude-code-sessions._sessionId-D1RYYi3j.js +0 -473
  254. package/apps/ui/dist/server/assets/claude-code-sessions._sessionId-DSvPLEUL.js +0 -11
  255. package/apps/ui/dist/server/assets/claude-code-transcript-C-bxh3cS.js +0 -139
  256. package/apps/ui/dist/server/assets/claude-code-transcript-phase-DmUMc8VU.js +0 -10
  257. package/apps/ui/dist/server/assets/claude-code._workspaceKey-B5Xg_-tE.js +0 -28
  258. package/apps/ui/dist/server/assets/claude-code._workspaceKey-BPlzyXPa.js +0 -11
  259. package/apps/ui/dist/server/assets/claude-code._workspaceKey-DqDve2wl.js +0 -191
  260. package/apps/ui/dist/server/assets/claude-code.index-B7yYR7dD.js +0 -92
  261. package/apps/ui/dist/server/assets/claude-code.index-BUHThG1W.js +0 -11
  262. package/apps/ui/dist/server/assets/codex-queries-CtgeZ7VL.js +0 -99
  263. package/apps/ui/dist/server/assets/codex-server-CGXU4H8-.js +0 -2294
  264. package/apps/ui/dist/server/assets/concurrency-VXtYvlGh.js +0 -18
  265. package/apps/ui/dist/server/assets/createServerRpc-CF_DEwnm.js +0 -12
  266. package/apps/ui/dist/server/assets/createSsrRpc-C754NPki.js +0 -16
  267. package/apps/ui/dist/server/assets/cursor-db-CZnYy7aT.js +0 -830
  268. package/apps/ui/dist/server/assets/cursor-exporter-types-CI3goo-c.js +0 -34
  269. package/apps/ui/dist/server/assets/cursor-queries-NCIM0Nat.js +0 -67
  270. package/apps/ui/dist/server/assets/cursor-recovery-CXpO8KcD.js +0 -361
  271. package/apps/ui/dist/server/assets/cursor-server-DNu52A3K.js +0 -213
  272. package/apps/ui/dist/server/assets/cursor-threads._composerId-BB0Y_Mao.js +0 -11
  273. package/apps/ui/dist/server/assets/cursor-threads._composerId-Ckn6ItHI.js +0 -582
  274. package/apps/ui/dist/server/assets/cursor-threads._composerId-p9-ARAzc.js +0 -18
  275. package/apps/ui/dist/server/assets/cursor-transcript-B7tGnqtn.js +0 -125
  276. package/apps/ui/dist/server/assets/cursor._workspaceKey-CZuohIUL.js +0 -28
  277. package/apps/ui/dist/server/assets/cursor._workspaceKey-DPp5JSjy.js +0 -401
  278. package/apps/ui/dist/server/assets/cursor._workspaceKey-nmg3YIOQ.js +0 -11
  279. package/apps/ui/dist/server/assets/cursor.index-CcsX7DG0.js +0 -11
  280. package/apps/ui/dist/server/assets/cursor.index-DkWT-mxw.js +0 -189
  281. package/apps/ui/dist/server/assets/data-table-Cdct823O.js +0 -189
  282. package/apps/ui/dist/server/assets/delete-confirm-dialog-BcBNCxWB.js +0 -144
  283. package/apps/ui/dist/server/assets/download-DMmiy1xf.js +0 -92
  284. package/apps/ui/dist/server/assets/dropdown-menu-Dy_9t6TN.js +0 -36
  285. package/apps/ui/dist/server/assets/empty-plugin-adapters-B3lHh1La.js +0 -5
  286. package/apps/ui/dist/server/assets/export-dialog-D88ze9Gy.js +0 -240
  287. package/apps/ui/dist/server/assets/formatters-FJaGZgJk.js +0 -91
  288. package/apps/ui/dist/server/assets/kiro-db--fXrED_-.js +0 -552
  289. package/apps/ui/dist/server/assets/kiro-queries-CzNlM2ok.js +0 -37
  290. package/apps/ui/dist/server/assets/kiro-server-9WRxg2Au.js +0 -72
  291. package/apps/ui/dist/server/assets/kiro-sessions._sessionId-CRY2c7YO.js +0 -11
  292. package/apps/ui/dist/server/assets/kiro-sessions._sessionId-F1UlwxmR.js +0 -449
  293. package/apps/ui/dist/server/assets/kiro-sessions._sessionId-bvTulT7C.js +0 -18
  294. package/apps/ui/dist/server/assets/kiro-transcript-DoekQBaH.js +0 -112
  295. package/apps/ui/dist/server/assets/kiro-transcript-phase-CJG40nQg.js +0 -27
  296. package/apps/ui/dist/server/assets/kiro._workspaceKey-BDe9sQwo.js +0 -11
  297. package/apps/ui/dist/server/assets/kiro._workspaceKey-CJsjgNwO.js +0 -28
  298. package/apps/ui/dist/server/assets/kiro._workspaceKey-D_-4Qb7y.js +0 -193
  299. package/apps/ui/dist/server/assets/kiro.index-C__-UROk.js +0 -11
  300. package/apps/ui/dist/server/assets/kiro.index-CmTbqeYj.js +0 -93
  301. package/apps/ui/dist/server/assets/loading-panel-BGFnWseS.js +0 -27
  302. package/apps/ui/dist/server/assets/metric-card-ByEeLu0r.js +0 -23
  303. package/apps/ui/dist/server/assets/model-label-B1NWGc65.js +0 -13
  304. package/apps/ui/dist/server/assets/opencode-db-BM7KjOzc.js +0 -397
  305. package/apps/ui/dist/server/assets/opencode-queries-BeNYXnQv.js +0 -50
  306. package/apps/ui/dist/server/assets/opencode-server-BpQ_kRze.js +0 -72
  307. package/apps/ui/dist/server/assets/opencode-sessions._sessionId-CGQ-t0vD.js +0 -518
  308. package/apps/ui/dist/server/assets/opencode-sessions._sessionId-D8Q7in_Z.js +0 -18
  309. package/apps/ui/dist/server/assets/opencode-sessions._sessionId-DKOvi0lV.js +0 -11
  310. package/apps/ui/dist/server/assets/opencode-think-tags-CAoD-EcZ.js +0 -87
  311. package/apps/ui/dist/server/assets/opencode-transcript-BNTcGNym.js +0 -141
  312. package/apps/ui/dist/server/assets/opencode-transcript-phase-OqLkiQD0.js +0 -32
  313. package/apps/ui/dist/server/assets/opencode._workspaceKey-BdqcWHw1.js +0 -11
  314. package/apps/ui/dist/server/assets/opencode._workspaceKey-CfH96HTT.js +0 -28
  315. package/apps/ui/dist/server/assets/opencode._workspaceKey-DdbHPDrO.js +0 -207
  316. package/apps/ui/dist/server/assets/opencode.index-BbVzkJXP.js +0 -11
  317. package/apps/ui/dist/server/assets/opencode.index-Vh_Gq_1L.js +0 -93
  318. package/apps/ui/dist/server/assets/page-header-VNSaM3xd.js +0 -29
  319. package/apps/ui/dist/server/assets/path-transforms-DL2IwtYd.js +0 -31
  320. package/apps/ui/dist/server/assets/projects._project-4io5LO0E.js +0 -20
  321. package/apps/ui/dist/server/assets/projects._project-Bshqk7JA.js +0 -12
  322. package/apps/ui/dist/server/assets/projects._project-D6ZoGhJ8.js +0 -395
  323. package/apps/ui/dist/server/assets/projects.index-BLXOx5eL.js +0 -12
  324. package/apps/ui/dist/server/assets/projects.index-BkLiF2FF.js +0 -182
  325. package/apps/ui/dist/server/assets/projects.index-DesYXwfi.js +0 -14
  326. package/apps/ui/dist/server/assets/reload-error-panel-BJMxY3U1.js +0 -25
  327. package/apps/ui/dist/server/assets/route-search-ts4W9MJ9.js +0 -38
  328. package/apps/ui/dist/server/assets/router-Sac2DGk6.js +0 -518
  329. package/apps/ui/dist/server/assets/routes-Bsact1uB.js +0 -184
  330. package/apps/ui/dist/server/assets/routes-BzkJgq4W.js +0 -34
  331. package/apps/ui/dist/server/assets/select-GW76p-ld.js +0 -76
  332. package/apps/ui/dist/server/assets/settings-OayxIYQQ.js +0 -100
  333. package/apps/ui/dist/server/assets/settings-store-DpEJEQ7M.js +0 -52
  334. package/apps/ui/dist/server/assets/shared-DyhChtHf.js +0 -137
  335. package/apps/ui/dist/server/assets/source-session-export-server-DNutCawb.js +0 -38
  336. package/apps/ui/dist/server/assets/sqlite-error-LZDrnxdd.js +0 -13
  337. package/apps/ui/dist/server/assets/start-B_HFwkkk.js +0 -4
  338. package/apps/ui/dist/server/assets/tabs-DkaChNVg.js +0 -501
  339. package/apps/ui/dist/server/assets/text-filter-CGKxMCKt.js +0 -36
  340. package/apps/ui/dist/server/assets/thread-transcript-stats-Dh34mt2u.js +0 -45
  341. package/apps/ui/dist/server/assets/threads._threadId-BSSK4nkI.js +0 -26
  342. package/apps/ui/dist/server/assets/threads._threadId-BeXEc4Lo.js +0 -648
  343. package/apps/ui/dist/server/assets/threads._threadId-Ci0S4mKU.js +0 -18
  344. package/apps/ui/dist/server/assets/ui-export-files-BHLX9bhN.js +0 -83
  345. package/apps/ui/dist/server/assets/utils-C_uf36nf.js +0 -8
  346. package/apps/ui/dist/server/server.js +0 -5895
  347. package/bin/codex-chats-claude.js +0 -5
  348. package/bin/codex-chats.js +0 -5
  349. package/bin/spiracha.js +0 -5
  350. package/src/export-chats.ts +0 -120
  351. package/src/export-claude.ts +0 -36
  352. package/src/export-cursor.ts +0 -244
  353. package/src/lib/claude-exporter.ts +0 -864
  354. package/src/lib/codex-exporter-cli.ts +0 -277
  355. package/src/lib/codex-exporter-db.ts +0 -319
  356. package/src/lib/codex-exporter.ts +0 -115
  357. package/src/lib/cursor-exporter.ts +0 -266
  358. package/src/lib/interactive-cli.ts +0 -433
  359. package/src/lib/native-open.ts +0 -54
  360. package/src/mcp-server.ts +0 -137
  361. package/src/spiracha.ts +0 -116
  362. package/src/ui-cli.ts +0 -310
  363. /package/apps/ui/{dist/client → public}/icon.svg +0 -0
  364. /package/apps/ui/{dist/client → public}/manifest.json +0 -0
  365. /package/apps/ui/{dist/client → public}/robots.txt +0 -0
@@ -1,2294 +0,0 @@
1
- import { t as createServerFn } from "../server.js";
2
- import { t as createServerRpc } from "./createServerRpc-CF_DEwnm.js";
3
- import { a as sanitizeExportFileName, n as ensureUiExportDir, o as zipExportDirectory, r as getExportMimeType, s as zipExportFile, t as buildUiExportDownloadUrl } from "./ui-export-files-BHLX9bhN.js";
4
- import { a as cleanExtractedText, c as finalizeExportWriteStream, d as getPortablePathBasename, f as readJsonlObjects$1, g as renderSection, h as renderMetadataBlock, i as asString, l as formatInlineLiteral, m as renderDocumentTitle, n as asNumber, o as cleanInlineTitle, r as asObject, s as createExportWriteStream, u as formatModelLabel } from "./shared-DyhChtHf.js";
5
- import { t as mapWithConcurrency } from "./concurrency-VXtYvlGh.js";
6
- import { t as isRetryableSqliteError } from "./sqlite-error-LZDrnxdd.js";
7
- import { t as applyPathTransforms } from "./path-transforms-DL2IwtYd.js";
8
- import { finished } from "node:stream/promises";
9
- import { z } from "zod";
10
- import { createHash, randomUUID } from "node:crypto";
11
- import { copyFile, mkdir, mkdtemp, readdir, rename, rm, stat, utimes } from "node:fs/promises";
12
- import os from "node:os";
13
- import path from "node:path";
14
- import { Database, constants } from "bun:sqlite";
15
- import { closeSync, createReadStream, openSync, readSync, readdirSync, statSync } from "node:fs";
16
- import { StringDecoder } from "node:string_decoder";
17
- import { pathToFileURL } from "node:url";
18
- //#region ../../src/lib/codex-exporter-types.ts
19
- var DEFAULT_CODEX_DIR = path.join(os.homedir(), ".codex");
20
- var DEFAULT_DB_PATH = path.join(DEFAULT_CODEX_DIR, "state_5.sqlite");
21
- path.join(DEFAULT_CODEX_DIR, "sessions");
22
- path.join(process.cwd(), "exports");
23
- //#endregion
24
- //#region ../../src/lib/codex-thread-parser.ts
25
- var createEmptyStats = () => {
26
- return {
27
- assistantMessageCount: 0,
28
- commentaryCount: 0,
29
- execCommandCount: 0,
30
- finalAnswerCount: 0,
31
- messageCount: 0,
32
- toolCallCount: 0,
33
- toolOutputCount: 0,
34
- userMessageCount: 0,
35
- webSearchEventCount: 0
36
- };
37
- };
38
- var createEmptySessionMeta = () => {
39
- return {
40
- baseInstructions: null,
41
- cli_version: void 0,
42
- cwd: void 0,
43
- dynamicTools: [],
44
- git: null,
45
- id: void 0,
46
- modelProvider: null,
47
- originator: void 0,
48
- source: void 0,
49
- threadSource: null,
50
- timestamp: void 0
51
- };
52
- };
53
- var parseCodexTranscriptFile = async (sessionFile, options = {}) => {
54
- const sessionMeta = createEmptySessionMeta();
55
- const turnContexts = [];
56
- const events = [];
57
- const stats = createEmptyStats();
58
- const includeRaw = options.includeRaw ?? true;
59
- const maxEvents = options.maxEvents ?? Number.POSITIVE_INFINITY;
60
- const maxTurnContexts = options.maxTurnContexts ?? Number.POSITIVE_INFINITY;
61
- let sequence = 0;
62
- for await (const parsed of readJsonlObjects$1(sessionFile)) {
63
- captureSessionMeta$1(parsed, sessionMeta);
64
- if (asString(parsed.type) === "turn_context") {
65
- if (turnContexts.length < maxTurnContexts) captureTurnContext(parsed, turnContexts);
66
- continue;
67
- }
68
- const event = toThreadEvent(parsed, sequence, includeRaw);
69
- if (!event) continue;
70
- events.push(event);
71
- updateTranscriptStats(stats, event);
72
- sequence += 1;
73
- if (events.length >= maxEvents) break;
74
- }
75
- return {
76
- events,
77
- isPartial: Number.isFinite(maxEvents) || Number.isFinite(maxTurnContexts),
78
- rawIncluded: includeRaw,
79
- sessionMeta,
80
- sourceFileSizeBytes: options.sourceFileSizeBytes ?? null,
81
- stats,
82
- statsArePartial: Number.isFinite(maxEvents),
83
- turnContexts
84
- };
85
- };
86
- var captureSessionMeta$1 = (parsed, sessionMeta) => {
87
- if (parsed.type !== "session_meta") return;
88
- const payload = asObject(parsed.payload);
89
- if (!payload) return;
90
- sessionMeta.baseInstructions = payload.base_instructions ?? sessionMeta.baseInstructions;
91
- sessionMeta.cli_version = asString(payload.cli_version) ?? sessionMeta.cli_version;
92
- sessionMeta.cwd = asString(payload.cwd) ?? sessionMeta.cwd;
93
- sessionMeta.dynamicTools = parseDynamicTools(payload.dynamic_tools) ?? sessionMeta.dynamicTools;
94
- sessionMeta.git = asObject(payload.git) ?? sessionMeta.git;
95
- sessionMeta.id = asString(payload.id) ?? sessionMeta.id;
96
- sessionMeta.modelProvider = asString(payload.model_provider) ?? sessionMeta.modelProvider;
97
- sessionMeta.originator = asString(payload.originator) ?? sessionMeta.originator;
98
- sessionMeta.source = asString(payload.source) ?? sessionMeta.source;
99
- sessionMeta.threadSource = asString(payload.thread_source) ?? sessionMeta.threadSource;
100
- sessionMeta.timestamp = asString(payload.timestamp) ?? sessionMeta.timestamp;
101
- };
102
- var parseDynamicTools = (value) => {
103
- if (!Array.isArray(value)) return null;
104
- return value.flatMap((entry) => {
105
- const tool = asObject(entry);
106
- if (!tool) return [];
107
- return [{
108
- deferLoading: tool.deferLoading === true || tool.defer_loading === true,
109
- description: asString(tool.description) ?? "",
110
- inputSchema: asObject(tool.inputSchema) ?? asObject(tool.input_schema) ?? null,
111
- name: asString(tool.name) ?? "unknown",
112
- namespace: asString(tool.namespace)
113
- }];
114
- });
115
- };
116
- var captureTurnContext = (parsed, turnContexts) => {
117
- const payload = asObject(parsed.payload);
118
- if (!payload) return;
119
- turnContexts.push({
120
- payload,
121
- timestamp: asString(parsed.timestamp)
122
- });
123
- };
124
- var toThreadEvent = (parsed, sequence, includeRaw) => {
125
- const payload = asObject(parsed.payload);
126
- if (!payload) return null;
127
- const payloadType = asString(payload.type);
128
- const timestamp = asString(parsed.timestamp);
129
- if (parsed.type === "event_msg") return buildEventMessage(payload, payloadType, includeRaw ? parsed : {}, sequence, timestamp);
130
- if (parsed.type !== "response_item") return null;
131
- return buildResponseItemEvent(payload, payloadType, includeRaw ? parsed : {}, sequence, timestamp);
132
- };
133
- var buildEventMessage = (payload, payloadType, raw, sequence, timestamp) => {
134
- if (payloadType === "task_started") return createTaskStartedEvent(payload, raw, sequence, timestamp);
135
- if (payloadType === "task_complete") return createTaskCompleteEvent(payload, raw, sequence, timestamp);
136
- return null;
137
- };
138
- var buildResponseItemEvent = (payload, payloadType, raw, sequence, timestamp) => {
139
- if (payloadType === "message") return createMessageEvent(payload, raw, sequence, timestamp);
140
- if (payloadType === "user_message") return createUserMessageEvent(payload, raw, sequence, timestamp);
141
- if (payloadType === "agent_message") return createAgentMessageEvent(payload, raw, sequence, timestamp);
142
- if (payloadType === "function_call") return createToolCallEvent(payload, raw, sequence, timestamp);
143
- if (payloadType === "function_call_output") return createToolOutputEvent(payload, raw, sequence, timestamp);
144
- if (payloadType === "reasoning") return createReasoningEvent(payload, raw, sequence, timestamp);
145
- if (payloadType === "token_count") return createTokenCountEvent(payload, raw, sequence, timestamp);
146
- if (payloadType === "web_search_call" || payloadType === "web_search_end") return createWebSearchEvent(payload, raw, sequence, timestamp);
147
- if (payloadType === "task_started") return createTaskStartedEvent(payload, raw, sequence, timestamp);
148
- if (payloadType === "task_complete") return createTaskCompleteEvent(payload, raw, sequence, timestamp);
149
- return null;
150
- };
151
- var createMessageEvent = (payload, raw, sequence, timestamp) => {
152
- const role = asString(payload.role);
153
- const content = payload.content;
154
- if (!role || content === void 0) return null;
155
- return {
156
- isHiddenByDefault: shouldHideTranscriptText(role, extractText$1(content)),
157
- kind: "message",
158
- memoryCitation: null,
159
- model: asString(payload.model),
160
- phase: asString(payload.phase),
161
- raw,
162
- role,
163
- sequence,
164
- text: extractText$1(content),
165
- timestamp,
166
- variant: "message"
167
- };
168
- };
169
- var createUserMessageEvent = (payload, raw, sequence, timestamp) => {
170
- return {
171
- isHiddenByDefault: shouldHideTranscriptText("user", asString(payload.message)?.trim() ?? ""),
172
- kind: "message",
173
- memoryCitation: null,
174
- model: null,
175
- phase: null,
176
- raw,
177
- role: "user",
178
- sequence,
179
- text: asString(payload.message)?.trim() ?? "",
180
- timestamp,
181
- variant: "user_message"
182
- };
183
- };
184
- var createAgentMessageEvent = (payload, raw, sequence, timestamp) => {
185
- return {
186
- isHiddenByDefault: false,
187
- kind: "message",
188
- memoryCitation: payload.memory_citation ?? null,
189
- model: asString(payload.model),
190
- phase: asString(payload.phase),
191
- raw,
192
- role: "assistant",
193
- sequence,
194
- text: asString(payload.message)?.trim() ?? "",
195
- timestamp,
196
- variant: "agent_message"
197
- };
198
- };
199
- var createToolCallEvent = (payload, raw, sequence, timestamp) => {
200
- const name = asString(payload.name) ?? "unknown";
201
- const argumentsText = asString(payload.arguments);
202
- const parsedArguments = parseExecCommandArguments$1(argumentsText);
203
- return {
204
- argumentsParseFailed: parsedArguments.argumentsParseFailed,
205
- argumentsText,
206
- callId: asString(payload.call_id),
207
- command: parsedArguments.cmd,
208
- kind: "tool_call",
209
- name,
210
- raw,
211
- sequence,
212
- timestamp,
213
- workdir: parsedArguments.workdir
214
- };
215
- };
216
- var createToolOutputEvent = (payload, raw, sequence, timestamp) => {
217
- const outputText = asString(payload.output) ?? "";
218
- return {
219
- callId: asString(payload.call_id),
220
- exitCode: parseExitCode(outputText),
221
- kind: "tool_output",
222
- outputText,
223
- raw,
224
- sequence,
225
- summary: formatToolOutputSummary$1(outputText),
226
- timestamp,
227
- wallTime: parseWallTime(outputText)
228
- };
229
- };
230
- var createReasoningEvent = (payload, raw, sequence, timestamp) => {
231
- return {
232
- content: payload.content ?? null,
233
- hasEncryptedContent: Boolean(asString(payload.encrypted_content)),
234
- kind: "reasoning",
235
- raw,
236
- sequence,
237
- summary: toStringArray(payload.summary),
238
- timestamp
239
- };
240
- };
241
- var createTokenCountEvent = (payload, raw, sequence, timestamp) => {
242
- return {
243
- info: payload.info ?? null,
244
- kind: "token_count",
245
- rateLimits: payload.rate_limits ?? null,
246
- raw,
247
- sequence,
248
- timestamp
249
- };
250
- };
251
- var createTaskStartedEvent = (payload, raw, sequence, timestamp) => {
252
- return {
253
- collaborationModeKind: asString(payload.collaboration_mode_kind),
254
- kind: "task_started",
255
- modelContextWindow: asNumber(payload.model_context_window),
256
- raw,
257
- sequence,
258
- startedAt: asNumber(payload.started_at),
259
- timestamp,
260
- turnId: asString(payload.turn_id)
261
- };
262
- };
263
- var createTaskCompleteEvent = (payload, raw, sequence, timestamp) => {
264
- return {
265
- completedAt: asNumber(payload.completed_at),
266
- durationMs: asNumber(payload.duration_ms),
267
- kind: "task_complete",
268
- lastAgentMessage: asString(payload.last_agent_message),
269
- raw,
270
- sequence,
271
- timestamp,
272
- timeToFirstTokenMs: asNumber(payload.time_to_first_token_ms),
273
- turnId: asString(payload.turn_id)
274
- };
275
- };
276
- var createWebSearchEvent = (payload, raw, sequence, timestamp) => {
277
- const payloadType = asString(payload.type);
278
- return {
279
- action: payload.action ?? null,
280
- callId: asString(payload.call_id),
281
- kind: "web_search",
282
- phase: payloadType === "web_search_end" ? "end" : "call",
283
- query: asString(payload.query),
284
- raw,
285
- sequence,
286
- status: asString(payload.status),
287
- timestamp
288
- };
289
- };
290
- var updateTranscriptStats = (stats, event) => {
291
- if (event.kind === "message") {
292
- stats.messageCount += 1;
293
- if (event.role === "assistant") stats.assistantMessageCount += 1;
294
- if (event.role === "user") stats.userMessageCount += 1;
295
- if (event.phase === "commentary") stats.commentaryCount += 1;
296
- if (event.phase === "final_answer") stats.finalAnswerCount += 1;
297
- return;
298
- }
299
- if (event.kind === "tool_call") {
300
- stats.toolCallCount += 1;
301
- if (event.name === "exec_command") stats.execCommandCount += 1;
302
- return;
303
- }
304
- if (event.kind === "tool_output") {
305
- stats.toolOutputCount += 1;
306
- return;
307
- }
308
- if (event.kind === "web_search") stats.webSearchEventCount += 1;
309
- };
310
- var toStringArray = (value) => {
311
- if (!Array.isArray(value)) return [];
312
- return value.map((entry) => asString(entry)).filter((entry) => Boolean(entry));
313
- };
314
- var parseExitCode = (outputText) => {
315
- const match = /Process exited with code (\d+)/u.exec(outputText);
316
- return match ? Number(match[1]) : null;
317
- };
318
- var parseWallTime = (outputText) => {
319
- return /Wall time: ([^\n]+)/u.exec(outputText)?.[1] ?? null;
320
- };
321
- var formatToolOutputSummary$1 = (outputText) => {
322
- return outputText.split("\n").map((line) => line.trim()).filter(Boolean).filter((line) => {
323
- return line.startsWith("Command: ") || line.startsWith("Process exited with code ") || line.startsWith("Wall time: ");
324
- }).join("\n");
325
- };
326
- var parseExecCommandArguments$1 = (argumentsText) => {
327
- if (!argumentsText) return {
328
- argumentsParseFailed: false,
329
- cmd: null,
330
- workdir: null
331
- };
332
- try {
333
- const parsed = JSON.parse(argumentsText);
334
- return {
335
- argumentsParseFailed: false,
336
- cmd: typeof parsed.cmd === "string" ? parsed.cmd : null,
337
- workdir: typeof parsed.workdir === "string" ? parsed.workdir : null
338
- };
339
- } catch {
340
- return {
341
- argumentsParseFailed: true,
342
- cmd: null,
343
- workdir: null
344
- };
345
- }
346
- };
347
- var extractText$1 = (content) => {
348
- if (typeof content === "string") return content.trim();
349
- if (Array.isArray(content)) return content.map((entry) => extractTextPart(entry)).filter(Boolean).join("\n\n").trim();
350
- if (content && typeof content === "object") return asString(content.text)?.trim() ?? "";
351
- return "";
352
- };
353
- var extractTextPart = (entry) => {
354
- const objectValue = asObject(entry);
355
- if (!objectValue) return "";
356
- const type = asString(objectValue.type);
357
- const text = asString(objectValue.text);
358
- if (type === "input_image") return "[Image attached]";
359
- return text ?? "";
360
- };
361
- var shouldHideTranscriptText = (role, text) => {
362
- if (!text) return true;
363
- if (role === "developer") return true;
364
- return text.startsWith("# AGENTS.md instructions for ") || text.startsWith("<permissions instructions>") || text.startsWith("<app-context>") || text.startsWith("<environment_context>") || text.startsWith("<collaboration_mode>") || text.startsWith("<skills_instructions>") || text.startsWith("<plugins_instructions>") || text.includes("Filesystem sandboxing defines which files can be read or written.");
365
- };
366
- //#endregion
367
- //#region ../../src/lib/ui-cache.ts
368
- var CACHE_DIR = path.join(os.tmpdir(), "spiracha-ui-cache");
369
- var CACHE_ENVELOPE_VERSION = 1;
370
- var ensureCacheDir = async () => {
371
- await mkdir(CACHE_DIR, { recursive: true });
372
- };
373
- var toCachePath = (key) => {
374
- const safeKey = key.replace(/[^a-zA-Z0-9._-]/gu, "_");
375
- return path.join(CACHE_DIR, `${safeKey}-${hashCacheKeyParts(key)}.json`);
376
- };
377
- var hashCacheKeyParts = (...parts) => {
378
- return createHash("sha1").update(parts.join("|")).digest("hex");
379
- };
380
- var hashCacheKeyPartsIterable = (parts) => {
381
- const hash = createHash("sha1");
382
- for (const part of parts) {
383
- hash.update(String(part.length));
384
- hash.update(":");
385
- hash.update(part);
386
- hash.update(";");
387
- }
388
- return hash.digest("hex");
389
- };
390
- var getFileFingerprint = async (filePath) => {
391
- const metadata = await stat(filePath);
392
- return `${filePath}:${metadata.size}:${metadata.mtimeMs}`;
393
- };
394
- var getCachedJson = async (key) => {
395
- await ensureCacheDir();
396
- const filePath = toCachePath(key);
397
- const file = Bun.file(filePath);
398
- if (!await file.exists()) return null;
399
- let parsed;
400
- try {
401
- parsed = await file.json();
402
- } catch {
403
- await rm(filePath, { force: true });
404
- return null;
405
- }
406
- if (parsed && typeof parsed === "object" && "version" in parsed && parsed.version === CACHE_ENVELOPE_VERSION && "value" in parsed) return parsed.value;
407
- await rm(filePath, { force: true });
408
- return null;
409
- };
410
- var setCachedJson = async (key, value) => {
411
- await ensureCacheDir();
412
- const filePath = toCachePath(key);
413
- const tempPath = `${filePath}.${randomUUID()}.tmp`;
414
- const envelope = {
415
- value,
416
- version: CACHE_ENVELOPE_VERSION
417
- };
418
- await Bun.write(tempPath, JSON.stringify(envelope));
419
- await rename(tempPath, filePath);
420
- };
421
- var withCachedJson = async (key, loader) => {
422
- const filePath = toCachePath(key);
423
- const existedBeforeRead = await Bun.file(filePath).exists();
424
- const cached = await getCachedJson(key);
425
- if (cached !== null || existedBeforeRead && await Bun.file(filePath).exists()) return cached;
426
- const value = await loader();
427
- await setCachedJson(key, value);
428
- return value;
429
- };
430
- var invalidateCacheByPrefix = async (...prefixes) => {
431
- await ensureCacheDir();
432
- const entries = await readdir(CACHE_DIR);
433
- await Promise.all(entries.filter((entry) => prefixes.some((prefix) => entry.startsWith(prefix))).map((entry) => rm(path.join(CACHE_DIR, entry), { force: true })));
434
- };
435
- //#endregion
436
- //#region ../../src/lib/codex-thread-cache.ts
437
- var LARGE_THREAD_SIZE_BYTES = 100 * 1024 * 1024;
438
- var isMissingFileError$2 = (error) => {
439
- return error instanceof Error && "code" in error && error.code === "ENOENT";
440
- };
441
- var getCachedParsedCodexTranscript = async (sessionFile) => {
442
- const fingerprint = await getFileFingerprint(sessionFile);
443
- return withCachedJson(`thread-${hashCacheKeyParts(path.basename(sessionFile), fingerprint)}`, async () => parseCodexTranscriptFile(sessionFile));
444
- };
445
- var getThreadRolloutLoadState = async (sessionFile, largeTranscriptThresholdBytes = LARGE_THREAD_SIZE_BYTES) => {
446
- let metadata;
447
- try {
448
- metadata = await stat(sessionFile);
449
- } catch (error) {
450
- if (isMissingFileError$2(error)) return {
451
- fileSizeBytes: null,
452
- shouldDeferTranscriptLoad: false
453
- };
454
- throw error;
455
- }
456
- return {
457
- fileSizeBytes: metadata.size,
458
- shouldDeferTranscriptLoad: metadata.size > largeTranscriptThresholdBytes
459
- };
460
- };
461
- var getCachedThreadTranscriptPreview = async (sessionFile, options = {}) => {
462
- const threshold = options.largeTranscriptThresholdBytes ?? 104857600;
463
- const previewEventLimit = options.previewEventLimit ?? 200;
464
- const fingerprint = await getFileFingerprint(sessionFile);
465
- const { fileSizeBytes, shouldDeferTranscriptLoad } = await getThreadRolloutLoadState(sessionFile, threshold);
466
- return withCachedJson(`thread-preview-${hashCacheKeyParts(path.basename(sessionFile), fingerprint, String(threshold), String(previewEventLimit))}`, async () => {
467
- if (!shouldDeferTranscriptLoad) return parseCodexTranscriptFile(sessionFile, { sourceFileSizeBytes: fileSizeBytes });
468
- return parseCodexTranscriptFile(sessionFile, {
469
- includeRaw: false,
470
- maxEvents: previewEventLimit,
471
- maxTurnContexts: 0,
472
- sourceFileSizeBytes: fileSizeBytes
473
- });
474
- });
475
- };
476
- //#endregion
477
- //#region ../../src/lib/sqlite-retry.ts
478
- var DEFAULT_RETRY_DELAYS_MS = [
479
- 40,
480
- 120,
481
- 250
482
- ];
483
- var SLEEP_BUFFER = new Int32Array(new SharedArrayBuffer(4));
484
- var sleepSync = (delayMs) => {
485
- if (delayMs <= 0) return;
486
- Atomics.wait(SLEEP_BUFFER, 0, 0, delayMs);
487
- };
488
- var toRetryExhaustedError = (attemptCount, error) => {
489
- const message = error instanceof Error ? error.message : String(error);
490
- return new Error(`SQLite operation failed after ${attemptCount} attempts: ${message}`, { cause: error });
491
- };
492
- var shouldRetrySqliteError = (error, attempt, delaysMs) => {
493
- return isRetryableSqliteError(error) && attempt < delaysMs.length;
494
- };
495
- var runWithSqliteRetry = ({ action, delaysMs = DEFAULT_RETRY_DELAYS_MS, sleep = sleepSync }) => {
496
- let attempt = 0;
497
- while (true) try {
498
- return action();
499
- } catch (error) {
500
- if (!shouldRetrySqliteError(error, attempt, delaysMs)) {
501
- if (isRetryableSqliteError(error)) throw toRetryExhaustedError(attempt + 1, error);
502
- throw error;
503
- }
504
- sleep(delaysMs[attempt] ?? 0);
505
- attempt += 1;
506
- }
507
- };
508
- //#endregion
509
- //#region ../../src/lib/codex-browser-db.ts
510
- var SQLITE_DELETE_BATCH_SIZE = 400;
511
- var SESSION_FILE_DELETE_CONCURRENCY = 16;
512
- var THREAD_LIST_IO_CONCURRENCY = 8;
513
- var CODEX_READONLY_DB_OPEN_FLAGS = constants.SQLITE_OPEN_READONLY | constants.SQLITE_OPEN_URI;
514
- var JSONL_READ_CHUNK_BYTES = 64 * 1024;
515
- var SESSION_META_READ_LIMIT_BYTES = 4 * 1024 * 1024;
516
- var THREAD_ID_PATTERN = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/iu;
517
- var isSqliteCantOpenError = (error) => {
518
- return error.code === "SQLITE_CANTOPEN";
519
- };
520
- var chunkValues = (values, chunkSize) => {
521
- const chunks = [];
522
- for (let index = 0; index < values.length; index += chunkSize) chunks.push(values.slice(index, index + chunkSize));
523
- return chunks;
524
- };
525
- var isPromiseLike = (value) => {
526
- if (typeof value !== "object" && typeof value !== "function" || value === null) return false;
527
- return "then" in value && typeof value.then === "function";
528
- };
529
- var openReadonlyDb = (dbPath) => {
530
- const db = new Database(dbPath, { readonly: true });
531
- try {
532
- db.query("SELECT name FROM sqlite_master LIMIT 1").get();
533
- return db;
534
- } catch (error) {
535
- db.close();
536
- if (!isSqliteCantOpenError(error)) throw error;
537
- }
538
- return new Database(`${pathToFileURL(dbPath).href}?immutable=1`, CODEX_READONLY_DB_OPEN_FLAGS);
539
- };
540
- var openWritableDb = (dbPath, busyTimeoutMs) => {
541
- const db = new Database(dbPath);
542
- try {
543
- db.exec(`PRAGMA busy_timeout = ${busyTimeoutMs}`);
544
- return db;
545
- } catch (error) {
546
- db.close();
547
- throw error;
548
- }
549
- };
550
- var toTimestampMs = (thread) => {
551
- return thread.updated_at_ms ?? thread.updated_at * 1e3;
552
- };
553
- var parseDynamicToolRow = (row) => {
554
- return {
555
- deferLoading: Number(row.defer_loading ?? 0) === 1,
556
- description: String(row.description ?? ""),
557
- inputSchema: parseJsonSafely$1(typeof row.input_schema === "string" ? row.input_schema : null),
558
- name: String(row.name ?? "unknown"),
559
- namespace: typeof row.namespace === "string" ? row.namespace : null,
560
- position: Number(row.position ?? 0),
561
- threadId: String(row.thread_id)
562
- };
563
- };
564
- var parseJsonSafely$1 = (value) => {
565
- if (!value) return null;
566
- try {
567
- return JSON.parse(value);
568
- } catch {
569
- return null;
570
- }
571
- };
572
- var withReadonlyDb = (dbPath, callback) => {
573
- return runWithSqliteRetry({ action: () => {
574
- const db = openReadonlyDb(dbPath);
575
- try {
576
- const result = callback(db);
577
- if (isPromiseLike(result)) throw new Error("Database callbacks must be synchronous");
578
- return result;
579
- } finally {
580
- db.close();
581
- }
582
- } });
583
- };
584
- var withWritableDb = (dbPath, callback) => {
585
- const db = runWithSqliteRetry({ action: () => {
586
- return openWritableDb(dbPath, 5e3);
587
- } });
588
- try {
589
- const result = callback(db);
590
- if (isPromiseLike(result)) throw new Error("Database callbacks must be synchronous");
591
- return result;
592
- } finally {
593
- db.close();
594
- }
595
- };
596
- var resolveCodexThreadDbPath = () => {
597
- const configuredDbPath = process.env.SPIRACHA_CODEX_DB?.trim();
598
- if (configuredDbPath) return configuredDbPath;
599
- const candidates = [
600
- DEFAULT_DB_PATH,
601
- path.join(DEFAULT_CODEX_DIR, "sqlite", "state_5.sqlite"),
602
- path.join(os.homedir(), ".codex", "state_5.sqlite")
603
- ];
604
- for (const candidate of candidates) try {
605
- if (!statSync(candidate).isFile()) continue;
606
- return candidate;
607
- } catch {}
608
- throw new Error(`Unable to open Codex thread database. Tried: ${candidates.join(", ")}`);
609
- };
610
- var readAllThreads = (dbPath) => {
611
- return withReadonlyDb(dbPath, (db) => {
612
- return db.query("SELECT * FROM threads ORDER BY COALESCE(updated_at_ms, updated_at * 1000) DESC, id DESC").all();
613
- });
614
- };
615
- var resolveCodexDirFromDbPath$1 = (dbPath) => {
616
- const dbDir = path.dirname(dbPath);
617
- return path.basename(dbDir) === "sqlite" ? path.dirname(dbDir) : dbDir;
618
- };
619
- var parseJsonlObject = (line) => {
620
- try {
621
- return JSON.parse(line);
622
- } catch {
623
- return null;
624
- }
625
- };
626
- var emitJsonlLine = (line, onRecord) => {
627
- const trimmed = line.trim();
628
- const parsed = trimmed ? parseJsonlObject(trimmed) : null;
629
- if (parsed) onRecord(parsed);
630
- };
631
- var emitCompleteJsonlLines = (text, onRecord) => {
632
- const lines = text.split(/\r?\n/u);
633
- const pending = lines.pop() ?? "";
634
- for (const line of lines) emitJsonlLine(line, onRecord);
635
- return pending;
636
- };
637
- var readJsonlObjects = (filePath, onRecord) => {
638
- let descriptor = null;
639
- try {
640
- if (!statSync(filePath).isFile()) return;
641
- descriptor = openSync(filePath, "r");
642
- const buffer = Buffer.alloc(JSONL_READ_CHUNK_BYTES);
643
- const decoder = new StringDecoder("utf8");
644
- let position = 0;
645
- let pending = "";
646
- while (true) {
647
- const bytesRead = readSync(descriptor, buffer, 0, buffer.length, position);
648
- if (bytesRead === 0) break;
649
- position += bytesRead;
650
- pending += decoder.write(buffer.subarray(0, bytesRead));
651
- pending = emitCompleteJsonlLines(pending, onRecord);
652
- }
653
- emitJsonlLine(pending + decoder.end(), onRecord);
654
- } catch {
655
- return;
656
- } finally {
657
- if (descriptor !== null) closeSync(descriptor);
658
- }
659
- };
660
- var collectJsonlObjects = (filePath) => {
661
- const records = [];
662
- readJsonlObjects(filePath, (record) => {
663
- records.push(record);
664
- });
665
- return records;
666
- };
667
- var readSessionIndexEntries = (codexDir) => {
668
- return collectJsonlObjects(path.join(codexDir, "session_index.jsonl")).filter((entry) => typeof entry.id === "string" && entry.id.length > 0);
669
- };
670
- var collectSessionFilesByThreadId = (sessionsDir) => {
671
- const sessionFiles = /* @__PURE__ */ new Map();
672
- const visit = (directory) => {
673
- const entries = (() => {
674
- try {
675
- return readdirSync(directory, { withFileTypes: true });
676
- } catch {
677
- return null;
678
- }
679
- })();
680
- if (!entries) return;
681
- for (const entry of entries) {
682
- const entryPath = path.join(directory, entry.name);
683
- if (entry.isDirectory()) {
684
- visit(entryPath);
685
- continue;
686
- }
687
- if (!entry.isFile()) continue;
688
- const threadId = THREAD_ID_PATTERN.exec(entry.name)?.[1];
689
- if (threadId && !sessionFiles.has(threadId)) sessionFiles.set(threadId, entryPath);
690
- }
691
- };
692
- visit(sessionsDir);
693
- return sessionFiles;
694
- };
695
- var readSessionMetaLine = (sessionFile) => {
696
- let descriptor = null;
697
- try {
698
- descriptor = openSync(sessionFile, "r");
699
- const chunks = [];
700
- let totalBytes = 0;
701
- while (totalBytes < SESSION_META_READ_LIMIT_BYTES) {
702
- const buffer = Buffer.alloc(Math.min(64 * 1024, SESSION_META_READ_LIMIT_BYTES - totalBytes));
703
- const bytesRead = readSync(descriptor, buffer, 0, buffer.length, totalBytes);
704
- if (bytesRead === 0) break;
705
- chunks.push(buffer.subarray(0, bytesRead));
706
- totalBytes += bytesRead;
707
- if (buffer.subarray(0, bytesRead).includes(10)) break;
708
- }
709
- return Buffer.concat(chunks).toString("utf8").split(/\r?\n/u)[0]?.trim() || null;
710
- } catch {
711
- return null;
712
- } finally {
713
- if (descriptor !== null) closeSync(descriptor);
714
- }
715
- };
716
- var readFallbackSessionMeta = (sessionFile) => {
717
- const line = readSessionMetaLine(sessionFile);
718
- if (!line) return null;
719
- try {
720
- const record = JSON.parse(line);
721
- return record.type === "session_meta" && record.payload ? record.payload : null;
722
- } catch {
723
- return null;
724
- }
725
- };
726
- var parseIsoMs = (value, fallback) => {
727
- if (!value) return fallback;
728
- const parsed = Date.parse(value);
729
- return Number.isFinite(parsed) ? parsed : fallback;
730
- };
731
- var stringOrNull = (value) => typeof value === "string" && value.length > 0 ? value : null;
732
- var numberOrNull = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
733
- var objectOrNull = (value) => {
734
- return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
735
- };
736
- var isFallbackSubagent = (sessionMeta) => {
737
- return Boolean(sessionMeta.thread_source === "subagent" || stringOrNull(sessionMeta.parent_thread_id) || stringOrNull(sessionMeta.forked_from_id));
738
- };
739
- var readFallbackRolloutStats = (sessionFile) => {
740
- let model = null;
741
- let tokensUsed = 0;
742
- readJsonlObjects(sessionFile, (record) => {
743
- const payload = objectOrNull(record.payload);
744
- if (!payload) return;
745
- if (record.type === "turn_context") {
746
- model = stringOrNull(payload.model) ?? model;
747
- return;
748
- }
749
- const payloadType = stringOrNull(payload.type);
750
- if (payloadType === "message" || payloadType === "agent_message") {
751
- model = stringOrNull(payload.model) ?? model;
752
- return;
753
- }
754
- if (payloadType !== "token_count") return;
755
- tokensUsed = numberOrNull(objectOrNull(objectOrNull(payload.info)?.total_token_usage)?.total_tokens) ?? tokensUsed;
756
- });
757
- return {
758
- model,
759
- tokensUsed
760
- };
761
- };
762
- var buildFallbackThreadRow = (entry, sessionFile, sessionMeta, rolloutStats) => {
763
- const cwd = stringOrNull(sessionMeta.cwd);
764
- if (!cwd) return null;
765
- let mtimeMs = Date.now();
766
- try {
767
- mtimeMs = statSync(sessionFile).mtimeMs;
768
- } catch {}
769
- const updatedAtMs = parseIsoMs(entry.updated_at, mtimeMs);
770
- const createdAtMs = parseIsoMs(sessionMeta.timestamp, updatedAtMs);
771
- const title = entry.thread_name?.trim() || path.basename(sessionFile, ".jsonl");
772
- const source = stringOrNull(sessionMeta.source) ?? "session_file";
773
- return {
774
- agent_nickname: sessionMeta.agent_nickname ?? null,
775
- agent_path: sessionMeta.agent_path ?? null,
776
- agent_role: sessionMeta.agent_role ?? null,
777
- approval_mode: "unknown",
778
- archived: 0,
779
- archived_at: null,
780
- cli_version: sessionMeta.cli_version ?? "",
781
- created_at: Math.floor(createdAtMs / 1e3),
782
- created_at_ms: Math.floor(createdAtMs),
783
- cwd,
784
- first_user_message: title,
785
- git_branch: null,
786
- git_origin_url: null,
787
- git_sha: null,
788
- has_user_event: sessionMeta.thread_source === "user" ? 1 : 0,
789
- id: entry.id,
790
- memory_mode: "enabled",
791
- model: rolloutStats.model,
792
- model_provider: sessionMeta.model_provider ?? "unknown",
793
- preview: title,
794
- reasoning_effort: null,
795
- rollout_path: sessionFile,
796
- sandbox_policy: "{}",
797
- source,
798
- thread_source: sessionMeta.thread_source ?? null,
799
- title,
800
- tokens_used: rolloutStats.tokensUsed,
801
- updated_at: Math.floor(updatedAtMs / 1e3),
802
- updated_at_ms: Math.floor(updatedAtMs)
803
- };
804
- };
805
- var readFallbackThreadRows = (dbPath, existingThreadIds, projectName = null, options = {}) => {
806
- const codexDir = resolveCodexDirFromDbPath$1(dbPath);
807
- const sessionFilesByThreadId = collectSessionFilesByThreadId(path.join(codexDir, "sessions"));
808
- const fallbackThreads = [];
809
- for (const entry of readSessionIndexEntries(codexDir)) {
810
- if (existingThreadIds.has(entry.id)) continue;
811
- const sessionFile = sessionFilesByThreadId.get(entry.id);
812
- if (!sessionFile) continue;
813
- const sessionMeta = readFallbackSessionMeta(sessionFile);
814
- if (!sessionMeta) continue;
815
- if (!options.includeSubagents && isFallbackSubagent(sessionMeta)) continue;
816
- const fallbackThread = buildFallbackThreadRow(entry, sessionFile, sessionMeta, readFallbackRolloutStats(sessionFile));
817
- if (!fallbackThread || projectName && getPortablePathBasename(fallbackThread.cwd) !== projectName) continue;
818
- fallbackThreads.push(fallbackThread);
819
- }
820
- return fallbackThreads;
821
- };
822
- var mergeFallbackThreadRows = (dbPath, threads, projectName = null) => {
823
- const threadIds = new Set(threads.map((thread) => thread.id));
824
- return [...threads, ...readFallbackThreadRows(dbPath, threadIds, projectName)].sort((left, right) => {
825
- const updatedDifference = toTimestampMs(right) - toTimestampMs(left);
826
- if (updatedDifference !== 0) return updatedDifference;
827
- return right.id.localeCompare(left.id);
828
- });
829
- };
830
- var applyRolloutActivityTimestamps = (threads) => {
831
- return threads.map((thread) => {
832
- let rolloutUpdatedAtMs = toTimestampMs(thread);
833
- try {
834
- rolloutUpdatedAtMs = Math.max(rolloutUpdatedAtMs, statSync(thread.rollout_path).mtimeMs);
835
- } catch {}
836
- if (rolloutUpdatedAtMs <= toTimestampMs(thread)) return thread;
837
- return {
838
- ...thread,
839
- updated_at: Math.floor(rolloutUpdatedAtMs / 1e3),
840
- updated_at_ms: Math.floor(rolloutUpdatedAtMs)
841
- };
842
- }).sort((left, right) => {
843
- const updatedDifference = toTimestampMs(right) - toTimestampMs(left);
844
- if (updatedDifference !== 0) return updatedDifference;
845
- return right.id.localeCompare(left.id);
846
- });
847
- };
848
- var buildDashboardRecentThreads = (threads) => {
849
- const bestThreadByProject = /* @__PURE__ */ new Map();
850
- for (const thread of threads) {
851
- const project = getPortablePathBasename(thread.cwd);
852
- if (!project) continue;
853
- const current = bestThreadByProject.get(project);
854
- if (!current || toTimestampMs(thread) > toTimestampMs(current)) bestThreadByProject.set(project, thread);
855
- }
856
- return [...bestThreadByProject.values()].sort((left, right) => {
857
- const updatedDifference = toTimestampMs(right) - toTimestampMs(left);
858
- if (updatedDifference !== 0) return updatedDifference;
859
- return right.id.localeCompare(left.id);
860
- }).slice(0, 5).map((thread) => ({
861
- project: getPortablePathBasename(thread.cwd),
862
- thread: compactThreadListRow(thread)
863
- }));
864
- };
865
- var filterThreadsByProject = (threads, projectName) => {
866
- if (!projectName) return threads;
867
- return threads.filter((thread) => getPortablePathBasename(thread.cwd) === projectName);
868
- };
869
- var buildProjectSummaryMap = (threads) => {
870
- const projectMap = /* @__PURE__ */ new Map();
871
- for (const thread of threads) {
872
- const projectName = getPortablePathBasename(thread.cwd);
873
- if (!projectName) continue;
874
- const current = projectMap.get(projectName) ?? {
875
- archivedThreadCount: 0,
876
- cwdPaths: /* @__PURE__ */ new Set(),
877
- lastUpdatedAtMs: null,
878
- modelNames: /* @__PURE__ */ new Set(),
879
- name: projectName,
880
- threadCount: 0,
881
- totalTokens: 0
882
- };
883
- current.archivedThreadCount += thread.archived ? 1 : 0;
884
- current.cwdPaths.add(thread.cwd);
885
- current.lastUpdatedAtMs = Math.max(current.lastUpdatedAtMs ?? 0, toTimestampMs(thread));
886
- if (thread.model) current.modelNames.add(thread.model);
887
- current.threadCount += 1;
888
- current.totalTokens += thread.tokens_used;
889
- projectMap.set(projectName, current);
890
- }
891
- return projectMap;
892
- };
893
- var mapProjectSummaries = (projectMap) => {
894
- return [...projectMap.values()].map((project) => {
895
- return {
896
- archivedThreadCount: project.archivedThreadCount,
897
- cwdPaths: [...project.cwdPaths].sort(),
898
- lastUpdatedAtMs: project.lastUpdatedAtMs,
899
- modelNames: [...project.modelNames].sort(),
900
- name: project.name,
901
- threadCount: project.threadCount,
902
- totalTokens: project.totalTokens
903
- };
904
- }).sort((left, right) => {
905
- if (left.totalTokens !== right.totalTokens) return right.totalTokens - left.totalTokens;
906
- return left.name.localeCompare(right.name);
907
- });
908
- };
909
- var getRelationsForThread = (db, threadId, existingTableNames) => {
910
- if (!existingTableNames.has("thread_spawn_edges")) return {
911
- childEdges: [],
912
- parentThreadId: null
913
- };
914
- const parentRow = db.query("SELECT parent_thread_id, child_thread_id, status FROM thread_spawn_edges WHERE child_thread_id = ? LIMIT 1").get(threadId);
915
- return {
916
- childEdges: db.query("SELECT parent_thread_id, child_thread_id, status FROM thread_spawn_edges WHERE parent_thread_id = ? ORDER BY child_thread_id ASC").all(threadId),
917
- parentThreadId: parentRow?.parent_thread_id ?? null
918
- };
919
- };
920
- var getExistingTableNames = (db) => {
921
- const rows = db.query("SELECT name FROM sqlite_master WHERE type = ?").all("table");
922
- return new Set(rows.map((row) => row.name));
923
- };
924
- var getThreadDeleteTargets = (db, threadIds) => {
925
- if (threadIds.length === 0) return [];
926
- const targets = [];
927
- for (const threadIdChunk of chunkValues(threadIds, SQLITE_DELETE_BATCH_SIZE)) {
928
- const placeholders = threadIdChunk.map(() => "?").join(", ");
929
- targets.push(...db.query(`SELECT id, rollout_path FROM threads WHERE id IN (${placeholders})`).all(...threadIdChunk));
930
- }
931
- return targets;
932
- };
933
- var deleteThreadIds = (db, threadIds) => {
934
- if (threadIds.length === 0) return {
935
- deletedSessionFiles: [],
936
- deletedThreadIds: []
937
- };
938
- const existingTableNames = getExistingTableNames(db);
939
- const threadTargets = getThreadDeleteTargets(db, threadIds);
940
- const existingIds = threadTargets.map((target) => target.id);
941
- if (existingIds.length === 0) return {
942
- deletedSessionFiles: [],
943
- deletedThreadIds: []
944
- };
945
- db.transaction((ids) => {
946
- for (const threadIdChunk of chunkValues(ids, SQLITE_DELETE_BATCH_SIZE)) {
947
- const placeholders = threadIdChunk.map(() => "?").join(", ");
948
- if (existingTableNames.has("thread_dynamic_tools")) db.query(`DELETE FROM thread_dynamic_tools WHERE thread_id IN (${placeholders})`).run(...threadIdChunk);
949
- if (existingTableNames.has("thread_goals")) db.query(`DELETE FROM thread_goals WHERE thread_id IN (${placeholders})`).run(...threadIdChunk);
950
- if (existingTableNames.has("stage1_outputs")) db.query(`DELETE FROM stage1_outputs WHERE thread_id IN (${placeholders})`).run(...threadIdChunk);
951
- if (existingTableNames.has("thread_spawn_edges")) db.query(`DELETE FROM thread_spawn_edges WHERE parent_thread_id IN (${placeholders}) OR child_thread_id IN (${placeholders})`).run(...threadIdChunk, ...threadIdChunk);
952
- db.query(`DELETE FROM threads WHERE id IN (${placeholders})`).run(...threadIdChunk);
953
- }
954
- })(existingIds);
955
- return {
956
- deletedSessionFiles: threadTargets.map((target) => target.rollout_path),
957
- deletedThreadIds: existingIds
958
- };
959
- };
960
- var deleteThreadSessionFiles = async (sessionFiles) => {
961
- const uniqueSessionFiles = [...new Set(sessionFiles)];
962
- await mapWithConcurrency(uniqueSessionFiles, SESSION_FILE_DELETE_CONCURRENCY, async (sessionFile) => {
963
- await rm(sessionFile, { force: true });
964
- return sessionFile;
965
- });
966
- return uniqueSessionFiles;
967
- };
968
- var listCodexProjects = (dbPath) => {
969
- return mapProjectSummaries(buildProjectSummaryMap(applyRolloutActivityTimestamps(mergeFallbackThreadRows(dbPath, readAllThreads(dbPath)))));
970
- };
971
- var compactThreadListRow = (thread) => {
972
- return {
973
- ...thread,
974
- preview: cleanInlineTitle(thread.preview || thread.first_user_message || ""),
975
- title: cleanInlineTitle(thread.title)
976
- };
977
- };
978
- var listProjectThreads = async (dbPath, projectName, options = {}) => {
979
- return (await mapWithConcurrency(applyRolloutActivityTimestamps(mergeFallbackThreadRows(dbPath, filterThreadsByProject(readAllThreads(dbPath), projectName), projectName)), THREAD_LIST_IO_CONCURRENCY, async (thread) => {
980
- const rollout = await getThreadRolloutLoadState(thread.rollout_path, options.largeTranscriptThresholdBytes);
981
- if (rollout.fileSizeBytes === null) return {
982
- project: projectName,
983
- rolloutSizeBytes: null,
984
- stats: {
985
- deferred: false,
986
- execCommandCount: 0,
987
- toolCallCount: 0,
988
- webSearchEventCount: 0
989
- },
990
- thread: compactThreadListRow(thread)
991
- };
992
- if (rollout.shouldDeferTranscriptLoad) return {
993
- project: projectName,
994
- rolloutSizeBytes: rollout.fileSizeBytes,
995
- stats: {
996
- deferred: true,
997
- execCommandCount: 0,
998
- toolCallCount: 0,
999
- webSearchEventCount: 0
1000
- },
1001
- thread: compactThreadListRow(thread)
1002
- };
1003
- const transcript = await getCachedParsedCodexTranscript(thread.rollout_path);
1004
- return {
1005
- project: projectName,
1006
- rolloutSizeBytes: rollout.fileSizeBytes,
1007
- stats: {
1008
- deferred: false,
1009
- execCommandCount: transcript.stats.execCommandCount,
1010
- toolCallCount: transcript.stats.toolCallCount,
1011
- webSearchEventCount: transcript.stats.webSearchEventCount
1012
- },
1013
- thread: compactThreadListRow(thread)
1014
- };
1015
- })).sort((left, right) => toTimestampMs(right.thread) - toTimestampMs(left.thread));
1016
- };
1017
- var getThreadBrowseData = (dbPath, threadId) => {
1018
- return withReadonlyDb(dbPath, (db) => {
1019
- const existingTableNames = getExistingTableNames(db);
1020
- const dbThread = db.query("SELECT * FROM threads WHERE id = ? LIMIT 1").get(threadId);
1021
- const thread = dbThread ?? readFallbackThreadRows(dbPath, /* @__PURE__ */ new Set(), null, { includeSubagents: true }).find((fallbackThread) => fallbackThread.id === threadId) ?? null;
1022
- if (!thread) throw new Error(`Thread not found: ${threadId}`);
1023
- return {
1024
- dynamicTools: (dbThread && existingTableNames.has("thread_dynamic_tools") ? db.query("SELECT thread_id, position, name, description, input_schema, defer_loading, namespace FROM thread_dynamic_tools WHERE thread_id = ? ORDER BY position ASC").all(threadId) : []).map((row) => parseDynamicToolRow(row)),
1025
- project: getPortablePathBasename(thread.cwd),
1026
- relations: getRelationsForThread(db, threadId, existingTableNames),
1027
- thread
1028
- };
1029
- });
1030
- };
1031
- var getCodexDashboardSummary = (dbPath) => {
1032
- const threads = applyRolloutActivityTimestamps(mergeFallbackThreadRows(dbPath, readAllThreads(dbPath)));
1033
- const projects = mapProjectSummaries(buildProjectSummaryMap(threads));
1034
- const threadsWithRelations = withReadonlyDb(dbPath, (db) => {
1035
- if (!getExistingTableNames(db).has("thread_spawn_edges")) return 0;
1036
- const rows = db.query("SELECT parent_thread_id, child_thread_id FROM thread_spawn_edges").all();
1037
- return new Set(rows.flatMap((row) => [row.parent_thread_id, row.child_thread_id])).size;
1038
- });
1039
- return {
1040
- activeThreads: threads.filter((thread) => !thread.archived).length,
1041
- archivedThreads: threads.filter((thread) => Boolean(thread.archived)).length,
1042
- recentThreads: buildDashboardRecentThreads(threads),
1043
- threadsWithRelations,
1044
- topProjectsByThreadCount: [...projects].sort((left, right) => {
1045
- if (left.threadCount !== right.threadCount) return right.threadCount - left.threadCount;
1046
- return left.name.localeCompare(right.name);
1047
- }).slice(0, 5),
1048
- topProjectsByTokens: projects.slice(0, 5),
1049
- totalProjects: projects.length,
1050
- totalThreads: threads.length,
1051
- totalTokens: threads.reduce((sum, thread) => sum + thread.tokens_used, 0)
1052
- };
1053
- };
1054
- var deleteCodexThread = async (dbPath, threadId, options = {}) => {
1055
- const result = withWritableDb(dbPath, (db) => {
1056
- return deleteThreadIds(db, [threadId]);
1057
- });
1058
- try {
1059
- if (options.deleteSessionFiles) return {
1060
- ...result,
1061
- deletedSessionFiles: await deleteThreadSessionFiles(result.deletedSessionFiles)
1062
- };
1063
- return {
1064
- ...result,
1065
- deletedSessionFiles: []
1066
- };
1067
- } finally {
1068
- await invalidateCodexUiCaches();
1069
- }
1070
- };
1071
- var deleteCodexThreads = async (dbPath, threadIds, options = {}) => {
1072
- const result = withWritableDb(dbPath, (db) => {
1073
- return deleteThreadIds(db, threadIds);
1074
- });
1075
- try {
1076
- if (options.deleteSessionFiles) return {
1077
- ...result,
1078
- deletedSessionFiles: await deleteThreadSessionFiles(result.deletedSessionFiles)
1079
- };
1080
- return {
1081
- ...result,
1082
- deletedSessionFiles: []
1083
- };
1084
- } finally {
1085
- await invalidateCodexUiCaches();
1086
- }
1087
- };
1088
- var deleteCodexProject = async (dbPath, projectName, options = {}) => {
1089
- const result = withWritableDb(dbPath, (db) => {
1090
- return {
1091
- ...deleteThreadIds(db, db.query("SELECT id, cwd FROM threads").all().filter((thread) => getPortablePathBasename(thread.cwd) === projectName).map((thread) => thread.id)),
1092
- projectName
1093
- };
1094
- });
1095
- try {
1096
- if (options.deleteSessionFiles) return {
1097
- ...result,
1098
- deletedSessionFiles: await deleteThreadSessionFiles(result.deletedSessionFiles)
1099
- };
1100
- return {
1101
- ...result,
1102
- deletedSessionFiles: []
1103
- };
1104
- } finally {
1105
- await invalidateCodexUiCaches();
1106
- }
1107
- };
1108
- var listScopedThreads = (dbPath, projectName) => {
1109
- return mergeFallbackThreadRows(dbPath, filterThreadsByProject(readAllThreads(dbPath), projectName), projectName);
1110
- };
1111
- var invalidateCodexUiCaches = async () => {
1112
- await invalidateCacheByPrefix("analytics-", "thread-");
1113
- };
1114
- var resolveAnalyticsTranscriptConcurrency = (configuredValue = process.env.SPIRACHA_ANALYTICS_TRANSCRIPT_CONCURRENCY) => {
1115
- const parsed = Number(configuredValue);
1116
- if (!Number.isInteger(parsed) || parsed < 1) return 8;
1117
- return parsed;
1118
- };
1119
- var roundToTwoDecimals = (value) => {
1120
- return Number(value.toFixed(2));
1121
- };
1122
- var incrementCount = (counts, key) => {
1123
- counts.set(key, (counts.get(key) ?? 0) + 1);
1124
- };
1125
- var toDistribution = (counts) => {
1126
- return [...counts.entries()].map(([label, count]) => ({
1127
- count,
1128
- label
1129
- })).sort((left, right) => {
1130
- if (left.count !== right.count) return right.count - left.count;
1131
- return left.label.localeCompare(right.label);
1132
- });
1133
- };
1134
- var buildModelsByTokens = (threads) => {
1135
- const models = /* @__PURE__ */ new Map();
1136
- for (const thread of threads) {
1137
- const model = thread.model ?? "unknown";
1138
- const current = models.get(model) ?? {
1139
- threadCount: 0,
1140
- totalTokens: 0
1141
- };
1142
- current.threadCount += 1;
1143
- current.totalTokens += thread.tokens_used;
1144
- models.set(model, current);
1145
- }
1146
- return [...models.entries()].map(([model, value]) => ({
1147
- model,
1148
- ...value
1149
- })).sort((left, right) => {
1150
- if (left.totalTokens !== right.totalTokens) return right.totalTokens - left.totalTokens;
1151
- return left.model.localeCompare(right.model);
1152
- });
1153
- };
1154
- var timestampSignature = (thread) => {
1155
- return String(thread.updated_at_ms ?? thread.updated_at * 1e3);
1156
- };
1157
- var threadMetadataCacheKeyParts = (thread) => [
1158
- thread.id,
1159
- thread.rollout_path,
1160
- timestampSignature(thread),
1161
- String(thread.created_at_ms ?? thread.created_at * 1e3),
1162
- String(thread.tokens_used),
1163
- String(thread.archived),
1164
- String(thread.archived_at ?? ""),
1165
- thread.cwd,
1166
- thread.model ?? "",
1167
- thread.model_provider,
1168
- thread.cli_version,
1169
- thread.title,
1170
- thread.preview
1171
- ];
1172
- var buildCodexAnalyticsCacheKey = (dbPath, threads, project) => {
1173
- return `analytics-${hashCacheKeyPartsIterable((function* () {
1174
- yield "v2";
1175
- yield dbPath;
1176
- yield project ?? "all";
1177
- yield String(threads.length);
1178
- for (const thread of threads) yield* threadMetadataCacheKeyParts(thread);
1179
- })())}`;
1180
- };
1181
- var buildThreadAnalyticsCacheKey = (thread) => {
1182
- return `thread-analytics-${hashCacheKeyParts("v1", ...threadMetadataCacheKeyParts(thread))}`;
1183
- };
1184
- var parseThreadAnalyticsFile = async (sessionFile) => {
1185
- const toolNames = [];
1186
- let hasWebSearch = false;
1187
- for await (const parsed of readJsonlObjects$1(sessionFile)) {
1188
- if (parsed.type !== "response_item") continue;
1189
- const payload = asObject(parsed.payload);
1190
- if (!payload) continue;
1191
- const payloadType = asString(payload.type);
1192
- if (payloadType === "function_call") {
1193
- toolNames.push(asString(payload.name) ?? "unknown");
1194
- continue;
1195
- }
1196
- if (payloadType === "web_search_call" || payloadType === "web_search_end") hasWebSearch = true;
1197
- }
1198
- return {
1199
- hasWebSearch,
1200
- toolNames
1201
- };
1202
- };
1203
- var getCachedThreadAnalytics = async (thread) => {
1204
- return withCachedJson(buildThreadAnalyticsCacheKey(thread), () => parseThreadAnalyticsFile(thread.rollout_path));
1205
- };
1206
- var computeCodexAnalyticsFromThreads = async (threads, options = {}) => {
1207
- const totalTokens = threads.reduce((sum, thread) => sum + thread.tokens_used, 0);
1208
- const projectNames = new Set(threads.map((thread) => getPortablePathBasename(thread.cwd)).filter(Boolean));
1209
- const toolUsage = /* @__PURE__ */ new Map();
1210
- let threadsWithWebSearch = 0;
1211
- const loadThreadAnalytics = options.loadThreadAnalytics ?? getCachedThreadAnalytics;
1212
- const threadAnalytics = await mapWithConcurrency(threads, options.transcriptConcurrency ?? resolveAnalyticsTranscriptConcurrency(), (thread) => loadThreadAnalytics(thread));
1213
- for (const analytics of threadAnalytics) {
1214
- if (analytics.hasWebSearch) threadsWithWebSearch += 1;
1215
- for (const toolName of analytics.toolNames) incrementCount(toolUsage, toolName);
1216
- }
1217
- return {
1218
- modelsByTokens: buildModelsByTokens(threads),
1219
- summary: {
1220
- archivedThreads: threads.filter((thread) => Boolean(thread.archived)).length,
1221
- averageTokensPerThread: threads.length === 0 ? 0 : roundToTwoDecimals(totalTokens / threads.length),
1222
- distinctToolNames: toolUsage.size,
1223
- threadsWithWebSearch,
1224
- totalProjects: projectNames.size,
1225
- totalThreads: threads.length,
1226
- totalTokens
1227
- },
1228
- toolUsage: toDistribution(toolUsage).map((item) => ({
1229
- count: item.count,
1230
- name: item.label
1231
- }))
1232
- };
1233
- };
1234
- var getCodexAnalytics = async (input) => {
1235
- const threads = listScopedThreads(input.dbPath, input.project);
1236
- return withCachedJson(buildCodexAnalyticsCacheKey(input.dbPath, threads, input.project), async () => computeCodexAnalyticsFromThreads(threads, { transcriptConcurrency: input.transcriptConcurrency }));
1237
- };
1238
- //#endregion
1239
- //#region ../../src/lib/codex-exporter-db.ts
1240
- var matchesFilters = (value, options) => {
1241
- return matchesCwdFilter(value, options.cwdFilter) && matchesProjectFilter(value, options.projectFilter);
1242
- };
1243
- var toCodexRelativePath = (targetPath) => {
1244
- const codexRoot = path.resolve(DEFAULT_CODEX_DIR);
1245
- const normalized = path.resolve(targetPath);
1246
- if (normalized.startsWith(`${codexRoot}${path.sep}`)) return path.relative(codexRoot, normalized);
1247
- return normalized;
1248
- };
1249
- var matchesCwdFilter = (value, cwdFilter) => {
1250
- if (!cwdFilter) return true;
1251
- return value === cwdFilter;
1252
- };
1253
- var matchesProjectFilter = (value, projectFilter) => {
1254
- if (!projectFilter) return true;
1255
- if (!value) return false;
1256
- return getPortablePathBasename(value) === projectFilter;
1257
- };
1258
- //#endregion
1259
- //#region ../../src/lib/codex-exporter-transcript.ts
1260
- var convertSessionFile = async (target, options) => {
1261
- let transcriptState;
1262
- try {
1263
- transcriptState = await collectCodexTranscript(target.sessionFile, options, target.thread?.model ?? null);
1264
- } catch (error) {
1265
- const message = error instanceof Error ? error.message : String(error);
1266
- throw new Error(`Failed to read Codex transcript ${target.sessionFile}: ${message}`);
1267
- }
1268
- if (!matchesFilters(target.thread?.cwd ?? transcriptState.sessionMeta.cwd ?? null, options)) return null;
1269
- if (transcriptState.sections.length === 0) return null;
1270
- return [
1271
- renderDocumentTitle(getTitle(target, transcriptState.sessionMeta), options.outputFormat),
1272
- "",
1273
- options.includeMetadata ? renderMetadataBlock(buildMetadataEntries(target, transcriptState.sessionMeta, options), options.outputFormat) : "",
1274
- ...transcriptState.sections
1275
- ].filter(Boolean).join("\n").trimEnd() + "\n";
1276
- };
1277
- var writeSessionFileExport = async (target, options, outputPath, transform = (text) => text) => {
1278
- const transcriptOutputPath = `${outputPath}.transcript.tmp`;
1279
- let transcriptStream = null;
1280
- const state = {
1281
- assistantModel: target.thread?.model ?? null,
1282
- sections: [],
1283
- sessionMeta: {}
1284
- };
1285
- let wroteSection = false;
1286
- try {
1287
- transcriptStream = await createExportWriteStream(transcriptOutputPath);
1288
- for await (const parsed of readJsonlObjects$1(target.sessionFile)) {
1289
- captureSessionMeta(parsed, state.sessionMeta);
1290
- const block = renderCodexTranscriptRecord(parsed, options, state);
1291
- if (!block) continue;
1292
- transcriptStream.write(transform(wroteSection ? `${getSectionSeparator()}${block}` : block));
1293
- wroteSection = true;
1294
- }
1295
- await finalizeExportWriteStream(transcriptStream);
1296
- transcriptStream = null;
1297
- if (!matchesFilters(target.thread?.cwd ?? state.sessionMeta.cwd ?? null, options) || !wroteSection) return false;
1298
- const outputStream = await createExportWriteStream(outputPath);
1299
- try {
1300
- const prefix = buildStreamExportPrefix(target, state.sessionMeta, options);
1301
- if (prefix) outputStream.write(transform(prefix));
1302
- const transcriptReadStream = createReadStream(transcriptOutputPath, { encoding: "utf8" });
1303
- transcriptReadStream.pipe(outputStream, { end: false });
1304
- await finished(transcriptReadStream);
1305
- outputStream.write("\n");
1306
- await finalizeExportWriteStream(outputStream);
1307
- } catch (error) {
1308
- outputStream.destroy();
1309
- throw error;
1310
- }
1311
- return true;
1312
- } catch (error) {
1313
- if (transcriptStream) transcriptStream.destroy();
1314
- throw error;
1315
- } finally {
1316
- await rm(transcriptOutputPath, { force: true });
1317
- }
1318
- };
1319
- var collectCodexTranscript = async (sessionFile, options, assistantModel = null) => {
1320
- const state = {
1321
- assistantModel,
1322
- sections: [],
1323
- sessionMeta: {}
1324
- };
1325
- for await (const parsed of readJsonlObjects$1(sessionFile)) processCodexTranscriptRecord(parsed, options, state);
1326
- return state;
1327
- };
1328
- var getSectionSeparator = () => "\n";
1329
- var processCodexTranscriptRecord = (parsed, options, state) => {
1330
- captureSessionMeta(parsed, state.sessionMeta);
1331
- const block = renderCodexTranscriptRecord(parsed, options, state);
1332
- if (block) state.sections.push(block);
1333
- };
1334
- var renderCodexTranscriptRecord = (parsed, options, state) => {
1335
- const message = extractMessageRecord(parsed);
1336
- if (message) return processCodexMessageRecord(message, options, state);
1337
- if (!options.includeTools) return "";
1338
- const tool = extractToolRecord(parsed);
1339
- if (!tool) return "";
1340
- return renderToolBlock(tool, options.outputFormat);
1341
- };
1342
- var processCodexMessageRecord = (message, options, state) => {
1343
- return renderMessageBlock(message, options.outputFormat, state.assistantModel, options.includeCommentary);
1344
- };
1345
- var buildStreamExportPrefix = (target, sessionMeta, options) => {
1346
- if (!options.includeMetadata) return "";
1347
- return `${[
1348
- renderDocumentTitle(getTitle(target, sessionMeta), options.outputFormat),
1349
- "",
1350
- renderMetadataBlock(buildMetadataEntries(target, sessionMeta, options), options.outputFormat)
1351
- ].filter(Boolean).join("\n")}\n`;
1352
- };
1353
- var formatToolOutputSummary = (outputText, outputFormat) => {
1354
- if (!outputText) return "";
1355
- const lines = outputText.split("\n").map((line) => line.trim()).filter(Boolean);
1356
- if (lines.length === 0) return "";
1357
- const summaryLines = [];
1358
- const command = lines.find((line) => line.startsWith("Command: "));
1359
- const exit = lines.find((line) => line.startsWith("Process exited with code "));
1360
- const wall = lines.find((line) => line.startsWith("Wall time: "));
1361
- if (command) summaryLines.push(command);
1362
- if (exit) summaryLines.push(exit);
1363
- if (wall) summaryLines.push(wall);
1364
- if (outputFormat === "md") return summaryLines.map((line) => `*${line}*`).join("\n");
1365
- return summaryLines.join("\n");
1366
- };
1367
- var parseExecCommandArguments = (argumentsText) => {
1368
- if (!argumentsText) return {
1369
- argumentsParseFailed: false,
1370
- cmd: null,
1371
- workdir: null
1372
- };
1373
- try {
1374
- const parsed = JSON.parse(argumentsText);
1375
- return {
1376
- argumentsParseFailed: false,
1377
- cmd: typeof parsed.cmd === "string" ? parsed.cmd : null,
1378
- workdir: typeof parsed.workdir === "string" ? parsed.workdir : null
1379
- };
1380
- } catch {
1381
- return {
1382
- argumentsParseFailed: true,
1383
- cmd: null,
1384
- workdir: null
1385
- };
1386
- }
1387
- };
1388
- var getTitle = (target, sessionMeta) => {
1389
- if (target.thread?.title) return cleanInlineTitle(target.thread.title);
1390
- return sessionMeta.id ?? path.basename(target.sessionFile, ".jsonl");
1391
- };
1392
- var buildMetadataEntries = (target, sessionMeta, options) => {
1393
- return [
1394
- ...buildCodexExportIdentityMetadata(target, sessionMeta),
1395
- ...buildCodexExportPathMetadata(target, options),
1396
- ...buildCodexRelationMetadata(target),
1397
- ...buildCodexThreadMetadata(target, sessionMeta),
1398
- ...buildCodexAgentMetadata(target)
1399
- ];
1400
- };
1401
- var buildCodexExportIdentityMetadata = (target, sessionMeta) => {
1402
- const thread = target.thread;
1403
- return [
1404
- {
1405
- key: "exported_from",
1406
- value: thread ? "thread_db_and_session_jsonl" : "session_jsonl_fallback"
1407
- },
1408
- {
1409
- key: "fallback_reason",
1410
- value: target.fallbackReason
1411
- },
1412
- {
1413
- key: "thread_id",
1414
- value: thread?.id ?? sessionMeta.id ?? null
1415
- },
1416
- {
1417
- key: "title",
1418
- value: thread?.title || null
1419
- }
1420
- ];
1421
- };
1422
- var buildCodexExportPathMetadata = (target, options) => {
1423
- const relativeOutputPath = target.outputRelativePath;
1424
- return [
1425
- {
1426
- key: "source_output_relative_path",
1427
- value: relativeOutputPath
1428
- },
1429
- {
1430
- key: options.outputFormat === "md" ? "source_markdown_path" : "source_text_path",
1431
- value: relativeOutputPath
1432
- },
1433
- {
1434
- key: "rollout_path",
1435
- value: target.sessionFile
1436
- },
1437
- {
1438
- key: "rollout_path_relative_to_codex",
1439
- value: toCodexRelativePath(target.sessionFile)
1440
- }
1441
- ];
1442
- };
1443
- var buildCodexRelationMetadata = (target) => {
1444
- const childThreadIds = target.relations.childEdges.map((edge) => edge.child_thread_id);
1445
- const childEdges = target.relations.childEdges.map((edge) => ({
1446
- child_thread_id: edge.child_thread_id,
1447
- status: edge.status
1448
- }));
1449
- return [
1450
- {
1451
- key: "parent_thread_id",
1452
- value: target.relations.parentThreadId
1453
- },
1454
- {
1455
- key: "child_thread_ids",
1456
- value: childThreadIds
1457
- },
1458
- {
1459
- key: "spawn_edges",
1460
- value: childEdges
1461
- }
1462
- ];
1463
- };
1464
- var buildCodexThreadMetadata = (target, sessionMeta) => {
1465
- return [...buildCodexThreadTimingMetadata(target, sessionMeta), ...buildCodexThreadIdentityMetadata(target, sessionMeta)];
1466
- };
1467
- var buildCodexThreadTimingMetadata = (target, sessionMeta) => {
1468
- const thread = target.thread;
1469
- return [
1470
- {
1471
- key: "created_at_unix",
1472
- value: thread?.created_at ?? null
1473
- },
1474
- {
1475
- key: "created_at_iso",
1476
- value: formatUnixSeconds(thread?.created_at ?? null)
1477
- },
1478
- {
1479
- key: "updated_at_unix",
1480
- value: thread?.updated_at ?? null
1481
- },
1482
- {
1483
- key: "updated_at_iso",
1484
- value: formatUnixSeconds(thread?.updated_at ?? null)
1485
- },
1486
- {
1487
- key: "archived_at_unix",
1488
- value: thread?.archived_at ?? null
1489
- },
1490
- {
1491
- key: "archived_at_iso",
1492
- value: formatUnixSeconds(thread?.archived_at ?? null)
1493
- },
1494
- {
1495
- key: "session_started_at_iso",
1496
- value: sessionMeta.timestamp ?? null
1497
- }
1498
- ];
1499
- };
1500
- var buildCodexThreadIdentityMetadata = (target, sessionMeta) => {
1501
- const thread = target.thread;
1502
- return [
1503
- {
1504
- key: "archived",
1505
- value: thread ? Boolean(thread.archived) : null
1506
- },
1507
- {
1508
- key: "source",
1509
- value: thread?.source ?? sessionMeta.source ?? null
1510
- },
1511
- {
1512
- key: "originator",
1513
- value: sessionMeta.originator ?? null
1514
- },
1515
- {
1516
- key: "model_provider",
1517
- value: thread?.model_provider ?? null
1518
- },
1519
- {
1520
- key: "model",
1521
- value: thread?.model ?? null
1522
- },
1523
- {
1524
- key: "reasoning_effort",
1525
- value: thread?.reasoning_effort ?? null
1526
- },
1527
- {
1528
- key: "cli_version",
1529
- value: thread?.cli_version || sessionMeta.cli_version || null
1530
- },
1531
- {
1532
- key: "cwd",
1533
- value: thread?.cwd || sessionMeta.cwd || null
1534
- },
1535
- {
1536
- key: "approval_mode",
1537
- value: thread?.approval_mode ?? null
1538
- },
1539
- {
1540
- key: "sandbox_policy",
1541
- value: parseJsonSafely(thread?.sandbox_policy ?? null)
1542
- },
1543
- {
1544
- key: "memory_mode",
1545
- value: thread?.memory_mode ?? null
1546
- },
1547
- {
1548
- key: "tokens_used",
1549
- value: thread?.tokens_used ?? null
1550
- },
1551
- {
1552
- key: "has_user_event",
1553
- value: thread ? Boolean(thread.has_user_event) : null
1554
- }
1555
- ];
1556
- };
1557
- var buildCodexAgentMetadata = (target) => {
1558
- const thread = target.thread;
1559
- return [
1560
- {
1561
- key: "git_sha",
1562
- value: thread?.git_sha ?? null
1563
- },
1564
- {
1565
- key: "git_branch",
1566
- value: thread?.git_branch ?? null
1567
- },
1568
- {
1569
- key: "git_origin_url",
1570
- value: thread?.git_origin_url ?? null
1571
- },
1572
- {
1573
- key: "agent_nickname",
1574
- value: thread?.agent_nickname ?? null
1575
- },
1576
- {
1577
- key: "agent_role",
1578
- value: thread?.agent_role ?? null
1579
- },
1580
- {
1581
- key: "agent_path",
1582
- value: thread?.agent_path ?? null
1583
- },
1584
- {
1585
- key: "first_user_message",
1586
- value: thread?.first_user_message || null
1587
- }
1588
- ];
1589
- };
1590
- var parseJsonSafely = (value) => {
1591
- if (!value) return null;
1592
- try {
1593
- return JSON.parse(value);
1594
- } catch {
1595
- return value;
1596
- }
1597
- };
1598
- var formatUnixSeconds = (value) => {
1599
- if (value === null || value === void 0) return null;
1600
- return (/* @__PURE__ */ new Date(value * 1e3)).toISOString();
1601
- };
1602
- var captureSessionMeta = (parsed, meta) => {
1603
- if (parsed.type !== "session_meta") return;
1604
- const payload = asObject(parsed.payload);
1605
- if (!payload) return;
1606
- meta.id = asString(payload.id) ?? meta.id;
1607
- meta.timestamp = asString(payload.timestamp) ?? meta.timestamp;
1608
- meta.cwd = asString(payload.cwd) ?? meta.cwd;
1609
- meta.source = asString(payload.source) ?? meta.source;
1610
- meta.originator = asString(payload.originator) ?? meta.originator;
1611
- meta.cli_version = asString(payload.cli_version) ?? meta.cli_version;
1612
- };
1613
- var extractMessageRecord = (parsed) => {
1614
- if (parsed.type === "message") {
1615
- const directMessage = normalizeMessage(parsed);
1616
- if (directMessage) return directMessage;
1617
- }
1618
- if (parsed.type !== "response_item") return null;
1619
- const payload = asObject(parsed.payload);
1620
- if (!payload) return null;
1621
- if (payload.type !== "message" && payload.type !== "agent_message" && payload.type !== "user_message") return null;
1622
- return normalizeMessage(payload);
1623
- };
1624
- var normalizeMessage = (value) => {
1625
- const type = asString(value.type);
1626
- const role = asString(value.role) ?? (type === "agent_message" ? "assistant" : type === "user_message" ? "user" : null);
1627
- const content = value.content ?? asString(value.message);
1628
- const phase = asString(value.phase);
1629
- if (!role || content === void 0) return null;
1630
- return {
1631
- content,
1632
- model: asString(value.model),
1633
- phase: phase ?? void 0,
1634
- role
1635
- };
1636
- };
1637
- var extractToolRecord = (parsed) => {
1638
- if (parsed.type !== "response_item") return null;
1639
- const payload = asObject(parsed.payload);
1640
- if (!payload) return null;
1641
- if (payload.type === "function_call") {
1642
- const name = asString(payload.name);
1643
- const argumentsText = asString(payload.arguments);
1644
- const callId = asString(payload.call_id);
1645
- if (name !== "exec_command") return null;
1646
- return {
1647
- argumentsText: argumentsText ?? void 0,
1648
- callId,
1649
- kind: "call",
1650
- name
1651
- };
1652
- }
1653
- if (payload.type === "function_call_output") {
1654
- const callId = asString(payload.call_id);
1655
- const outputText = asString(payload.output);
1656
- if (!outputText?.includes("Command: ")) return null;
1657
- return {
1658
- callId,
1659
- kind: "output",
1660
- name: "function_call_output",
1661
- outputText: outputText ?? void 0
1662
- };
1663
- }
1664
- return null;
1665
- };
1666
- var renderMessageBlock = (message, outputFormat, assistantModel, includeCommentary) => {
1667
- if (message.role !== "user" && message.role !== "assistant") return "";
1668
- if (message.role === "assistant" && message.phase === "commentary" && !includeCommentary) return "";
1669
- const text = cleanExtractedText(stripPreviewBlock(extractText(message.content))).trim();
1670
- if (!text || shouldSkipMessage(message.role, text)) return "";
1671
- return renderSection(message.role === "user" ? "User" : formatModelLabel(message.model ?? assistantModel), message.phase ? `Phase: ${message.phase}\n\n${text}` : text, outputFormat);
1672
- };
1673
- var renderToolBlock = (tool, outputFormat) => {
1674
- if (tool.kind === "call") {
1675
- const details = formatToolCallDetails(tool, outputFormat);
1676
- return details ? renderSection("Tool", details, outputFormat) : "";
1677
- }
1678
- const summary = formatToolOutputSummary(tool.outputText ?? "", outputFormat);
1679
- return summary ? renderSection("Tool Output", summary, outputFormat) : "";
1680
- };
1681
- var stripPreviewBlock = (text) => {
1682
- const parts = text.split(/\n{2,}/).map((part) => part.trim()).filter(Boolean);
1683
- if (parts.length < 2) return text.trim();
1684
- const first = parts[0];
1685
- const second = parts[1];
1686
- const isTranscriptHeading = (value) => /^##\s+.+$/i.test(value);
1687
- if (!(!/^([UA]):/i.test(first) && !isTranscriptHeading(first) && /^([UA]):/i.test(second) === false && isTranscriptHeading(second))) return text.trim();
1688
- return parts.slice(1).join("\n\n");
1689
- };
1690
- var shouldSkipMessage = (role, text) => {
1691
- if (text.startsWith("<environment_context>")) return true;
1692
- if (text.startsWith("AGENTS.md instructions for ")) return true;
1693
- if (text.startsWith("# AGENTS.md instructions for ")) return true;
1694
- if (role === "user" && text.includes("<environment_context>")) return true;
1695
- return false;
1696
- };
1697
- var formatToolCallDetails = (tool, outputFormat) => {
1698
- if (tool.name !== "exec_command") return "";
1699
- const details = parseExecCommandArguments(tool.argumentsText);
1700
- return details.cmd ? `Command: ${formatInlineLiteral(details.cmd, outputFormat)}` : "";
1701
- };
1702
- var extractText = (content) => {
1703
- if (typeof content === "string") return content;
1704
- if (Array.isArray(content)) return content.map((item) => extractContentPart(item)).filter((part) => part.length > 0).join("\n\n");
1705
- if (content && typeof content === "object") {
1706
- const text = asString(content.text);
1707
- if (text) return text;
1708
- }
1709
- return "";
1710
- };
1711
- var extractContentPart = (value) => {
1712
- if (!value || typeof value !== "object" || Array.isArray(value)) return "";
1713
- const item = value;
1714
- const type = asString(item.type);
1715
- const text = asString(item.text);
1716
- if ((type === "input_text" || type === "output_text" || type === "text") && text) return text;
1717
- if (type === "input_image") return "[Image attached]";
1718
- return text ?? "";
1719
- };
1720
- //#endregion
1721
- //#region ../../src/lib/codex-browser-export.ts
1722
- var LARGE_BROWSER_EXPORT_THRESHOLD_BYTES = 128 * 1024 * 1024;
1723
- var formatReadableExportDate = (value) => {
1724
- const date = new Date(value);
1725
- return `${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(2, "0")}-${String(date.getUTCDate()).padStart(2, "0")}-${String(date.getUTCHours()).padStart(2, "0")}${String(date.getUTCMinutes()).padStart(2, "0")}`;
1726
- };
1727
- var buildExportBaseName = (thread) => {
1728
- return `${sanitizeExportFileName(getPortablePathBasename(thread.cwd) || "thread") || "thread"}-${formatReadableExportDate(thread.updated_at_ms ?? thread.updated_at * 1e3)}-${thread.id.slice(0, 8)}`;
1729
- };
1730
- var buildBatchExportBaseName = (threads) => {
1731
- const firstThread = threads[0];
1732
- if (!firstThread) throw new Error("No threads selected for export");
1733
- return `${sanitizeExportFileName(getPortablePathBasename(firstThread.cwd) || "threads") || "threads"}-${formatReadableExportDate(Math.max(...threads.map((thread) => thread.updated_at_ms ?? thread.updated_at * 1e3)))}-threads-${threads.length}`;
1734
- };
1735
- var buildUniqueArchivePath = (exportDir, exportBaseName) => {
1736
- return path.join(exportDir, `${exportBaseName}-${randomUUID()}.zip`);
1737
- };
1738
- var buildUniqueBatchEntryBaseName = (baseName, threadId, usedBaseNames) => {
1739
- if (!usedBaseNames.has(baseName)) {
1740
- usedBaseNames.add(baseName);
1741
- return baseName;
1742
- }
1743
- const collisionSafeBaseName = `${baseName}-${threadId}`;
1744
- usedBaseNames.add(collisionSafeBaseName);
1745
- return collisionSafeBaseName;
1746
- };
1747
- var toDownloadOptions = (input) => {
1748
- return {
1749
- cwdFilter: null,
1750
- dbPath: input.dbPath,
1751
- flat: false,
1752
- includeCommentary: input.includeCommentary,
1753
- includeMetadata: input.includeMetadata,
1754
- includeTools: input.includeTools,
1755
- inputDir: "",
1756
- outputDir: "",
1757
- outputFormat: input.outputFormat,
1758
- projectFilter: null,
1759
- threadIds: [input.threadId]
1760
- };
1761
- };
1762
- var resolvePublicExportDir = async (publicExportDir) => {
1763
- if (publicExportDir) {
1764
- await ensureDirectory(publicExportDir);
1765
- return publicExportDir;
1766
- }
1767
- return ensureUiExportDir();
1768
- };
1769
- var ensureDirectory = async (directoryPath) => {
1770
- await mkdir(directoryPath, { recursive: true });
1771
- };
1772
- var createExportWorkspace = async (exportDir, exportBaseName) => {
1773
- return mkdtemp(path.join(exportDir, `${exportBaseName}-`));
1774
- };
1775
- var getRolloutSnapshot = async (rolloutPath) => {
1776
- const metadata = await stat(rolloutPath);
1777
- return {
1778
- mtimeMs: metadata.mtimeMs,
1779
- sizeBytes: metadata.size
1780
- };
1781
- };
1782
- var isMissingFileError$1 = (error) => {
1783
- return error instanceof Error && "code" in error && error.code === "ENOENT";
1784
- };
1785
- var getExistingRolloutSnapshot = async (threadId, rolloutPath) => {
1786
- try {
1787
- return await getRolloutSnapshot(rolloutPath);
1788
- } catch (error) {
1789
- if (isMissingFileError$1(error)) throw new Error(`Thread ${threadId} rollout file is missing: ${rolloutPath}`, { cause: error });
1790
- throw error;
1791
- }
1792
- };
1793
- var logExportEvent = (level, event, details) => {
1794
- console[level](`[spiracha:export] ${event}`, details);
1795
- };
1796
- var logRolloutChangeIfDetected = (threadId, beforeSnapshot, afterSnapshot) => {
1797
- if (beforeSnapshot.mtimeMs === afterSnapshot.mtimeMs && beforeSnapshot.sizeBytes === afterSnapshot.sizeBytes) return;
1798
- logExportEvent("warn", "rollout_changed_during_export", {
1799
- afterMtimeMs: afterSnapshot.mtimeMs,
1800
- afterSizeBytes: afterSnapshot.sizeBytes,
1801
- beforeMtimeMs: beforeSnapshot.mtimeMs,
1802
- beforeSizeBytes: beforeSnapshot.sizeBytes,
1803
- threadId
1804
- });
1805
- };
1806
- var cleanupExportWorkspace = async (workspacePath) => {
1807
- try {
1808
- await rm(workspacePath, {
1809
- force: true,
1810
- recursive: true
1811
- });
1812
- } catch (error) {
1813
- logExportEvent("warn", "workspace_cleanup_failed", {
1814
- error: error instanceof Error ? error.message : String(error),
1815
- workspacePath
1816
- });
1817
- }
1818
- };
1819
- var renderCodexThreadDownload = async (input) => {
1820
- const startedAt = Date.now();
1821
- const browseData = getThreadBrowseData(input.dbPath, input.threadId);
1822
- const extension = input.outputFormat === "md" ? "md" : "txt";
1823
- const fileBaseName = buildExportBaseName(browseData.thread);
1824
- const fileName = `${fileBaseName}.${extension}`;
1825
- const mimeType = getExportMimeType(input.outputFormat);
1826
- const transform = (text) => input.pathDisplaySettings ? applyPathTransforms(text, {
1827
- ...input.pathDisplaySettings,
1828
- projectPath: browseData.thread.cwd
1829
- }) : text;
1830
- const rolloutSnapshotBefore = await getExistingRolloutSnapshot(input.threadId, browseData.thread.rollout_path);
1831
- logExportEvent("info", "single_start", {
1832
- fileName,
1833
- sizeBytes: rolloutSnapshotBefore.sizeBytes,
1834
- threadId: input.threadId
1835
- });
1836
- try {
1837
- if (input.zipArchive || rolloutSnapshotBefore.sizeBytes > (input.largeExportThresholdBytes ?? LARGE_BROWSER_EXPORT_THRESHOLD_BYTES)) {
1838
- const exportBaseName = fileBaseName;
1839
- const exportDir = await resolvePublicExportDir(input.publicExportDir);
1840
- const workspaceDir = await createExportWorkspace(exportDir, exportBaseName);
1841
- const savedPath = path.join(workspaceDir, `${exportBaseName}.${extension}`);
1842
- const zipPath = buildUniqueArchivePath(exportDir, exportBaseName);
1843
- try {
1844
- if (!await writeSessionFileExport({
1845
- fallbackReason: null,
1846
- outputRelativePath: fileName,
1847
- relations: browseData.relations,
1848
- sessionFile: browseData.thread.rollout_path,
1849
- thread: browseData.thread
1850
- }, toDownloadOptions(input), savedPath, transform)) throw new Error(`Thread ${input.threadId} produced no exportable content`);
1851
- await zipExportFile(savedPath, zipPath);
1852
- } finally {
1853
- await cleanupExportWorkspace(workspaceDir);
1854
- }
1855
- const rolloutSnapshotAfter = await getRolloutSnapshot(browseData.thread.rollout_path);
1856
- logRolloutChangeIfDetected(input.threadId, rolloutSnapshotBefore, rolloutSnapshotAfter);
1857
- const zipStat = await Bun.file(zipPath).stat();
1858
- logExportEvent("info", "single_zip_ready", {
1859
- downloadUrl: buildUiExportDownloadUrl(zipPath),
1860
- durationMs: Date.now() - startedAt,
1861
- fileName: `${exportBaseName}.zip`,
1862
- sizeBytes: zipStat.size,
1863
- threadId: input.threadId,
1864
- zipPath
1865
- });
1866
- return {
1867
- downloadUrl: buildUiExportDownloadUrl(zipPath),
1868
- fileName: `${exportBaseName}.zip`,
1869
- mimeType: "application/zip",
1870
- mode: "download_url"
1871
- };
1872
- }
1873
- const content = await convertSessionFile({
1874
- fallbackReason: null,
1875
- outputRelativePath: fileName,
1876
- relations: browseData.relations,
1877
- sessionFile: browseData.thread.rollout_path,
1878
- thread: browseData.thread
1879
- }, toDownloadOptions(input));
1880
- if (!content) throw new Error(`Thread ${input.threadId} produced no exportable content`);
1881
- const rolloutSnapshotAfter = await getRolloutSnapshot(browseData.thread.rollout_path);
1882
- logRolloutChangeIfDetected(input.threadId, rolloutSnapshotBefore, rolloutSnapshotAfter);
1883
- logExportEvent("info", "single_inline_ready", {
1884
- durationMs: Date.now() - startedAt,
1885
- fileName,
1886
- sizeBytes: content.length,
1887
- threadId: input.threadId
1888
- });
1889
- return {
1890
- content: transform(content),
1891
- fileName,
1892
- mimeType,
1893
- mode: "download"
1894
- };
1895
- } catch (error) {
1896
- logExportEvent("error", "single_error", {
1897
- error: error instanceof Error ? error.message : String(error),
1898
- fileName,
1899
- threadId: input.threadId
1900
- });
1901
- throw error;
1902
- }
1903
- };
1904
- var renderCodexThreadsDownload = async (input) => {
1905
- const startedAt = Date.now();
1906
- const threadIds = [...new Set(input.threadIds)];
1907
- if (threadIds.length === 0) throw new Error("No threads selected for export");
1908
- const browseEntries = threadIds.map((threadId) => getThreadBrowseData(input.dbPath, threadId));
1909
- const threads = browseEntries.map((entry) => entry.thread);
1910
- const exportDir = await resolvePublicExportDir(input.publicExportDir);
1911
- const exportBaseName = buildBatchExportBaseName(threads);
1912
- const bundleDirectory = await createExportWorkspace(exportDir, exportBaseName);
1913
- const zipPath = buildUniqueArchivePath(exportDir, exportBaseName);
1914
- const usedBatchEntryBaseNames = /* @__PURE__ */ new Set();
1915
- logExportEvent("info", "batch_start", {
1916
- exportBaseName,
1917
- selectedThreadCount: threadIds.length,
1918
- selectedThreadIds: threadIds,
1919
- zipPath
1920
- });
1921
- try {
1922
- for (const entry of browseEntries) {
1923
- const rolloutSnapshotBefore = await getExistingRolloutSnapshot(entry.thread.id, entry.thread.rollout_path);
1924
- const singleBaseName = buildExportBaseName(entry.thread);
1925
- const uniqueBaseName = buildUniqueBatchEntryBaseName(singleBaseName, entry.thread.id, usedBatchEntryBaseNames);
1926
- const relativeFileName = `${uniqueBaseName}.${input.outputFormat === "md" ? "md" : "txt"}`;
1927
- const savedPath = path.join(bundleDirectory, relativeFileName);
1928
- const transform = (text) => input.pathDisplaySettings ? applyPathTransforms(text, {
1929
- ...input.pathDisplaySettings,
1930
- projectPath: entry.thread.cwd
1931
- }) : text;
1932
- if (uniqueBaseName !== singleBaseName) logExportEvent("warn", "batch_entry_name_collision", {
1933
- resolvedFileName: relativeFileName,
1934
- singleBaseName,
1935
- threadId: entry.thread.id
1936
- });
1937
- if (!await writeSessionFileExport({
1938
- fallbackReason: null,
1939
- outputRelativePath: relativeFileName,
1940
- relations: entry.relations,
1941
- sessionFile: entry.thread.rollout_path,
1942
- thread: entry.thread
1943
- }, {
1944
- ...toDownloadOptions({
1945
- ...input,
1946
- threadId: entry.thread.id
1947
- }),
1948
- threadIds: [entry.thread.id]
1949
- }, savedPath, transform)) throw new Error(`Thread ${entry.thread.id} produced no exportable content`);
1950
- const rolloutSnapshotAfter = await getRolloutSnapshot(entry.thread.rollout_path);
1951
- logRolloutChangeIfDetected(entry.thread.id, rolloutSnapshotBefore, rolloutSnapshotAfter);
1952
- }
1953
- await zipExportDirectory(bundleDirectory, zipPath);
1954
- } catch (error) {
1955
- logExportEvent("error", "batch_error", {
1956
- error: error instanceof Error ? error.message : String(error),
1957
- exportBaseName,
1958
- selectedThreadCount: threadIds.length,
1959
- selectedThreadIds: threadIds,
1960
- zipPath
1961
- });
1962
- throw error;
1963
- } finally {
1964
- await cleanupExportWorkspace(bundleDirectory);
1965
- }
1966
- const zipStat = await Bun.file(zipPath).stat();
1967
- logExportEvent("info", "batch_ready", {
1968
- downloadUrl: buildUiExportDownloadUrl(zipPath),
1969
- durationMs: Date.now() - startedAt,
1970
- fileName: `${exportBaseName}.zip`,
1971
- selectedThreadCount: threadIds.length,
1972
- selectedThreadIds: threadIds,
1973
- sizeBytes: zipStat.size,
1974
- zipPath
1975
- });
1976
- return {
1977
- downloadUrl: buildUiExportDownloadUrl(zipPath),
1978
- fileName: `${exportBaseName}.zip`,
1979
- mimeType: "application/zip",
1980
- mode: "download_url"
1981
- };
1982
- };
1983
- //#endregion
1984
- //#region ../../src/lib/codex-thread-recovery.ts
1985
- var backupFile = async (filePath, label) => {
1986
- const backupPath = `${filePath}.bak-${label}-${(/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "").replace(/\.\d{3}Z$/, "Z").replace("T", "-")}`;
1987
- await copyFile(filePath, backupPath);
1988
- return backupPath;
1989
- };
1990
- var resolveCodexDirFromDbPath = (dbPath) => {
1991
- const dbDir = path.dirname(dbPath);
1992
- return path.basename(dbDir) === "sqlite" ? path.dirname(dbDir) : dbDir;
1993
- };
1994
- var assertRequiredStatePath = async (filePath) => {
1995
- if (!await Bun.file(filePath).exists()) throw new Error(`Required Codex state file not found: ${filePath}`);
1996
- };
1997
- var readGlobalState = async (globalStatePath) => {
1998
- return await Bun.file(globalStatePath).json();
1999
- };
2000
- var writeGlobalState = async (globalStatePath, state) => {
2001
- await Bun.write(globalStatePath, JSON.stringify(state));
2002
- };
2003
- var updateGlobalRoots = (state, projectCwds) => {
2004
- const savedRoots = state["electron-saved-workspace-roots"] ?? [];
2005
- const projectOrder = state["project-order"] ?? [];
2006
- const missingSaved = projectCwds.filter((cwd) => !savedRoots.includes(cwd));
2007
- const missingProjectOrder = projectCwds.filter((cwd) => !projectOrder.includes(cwd));
2008
- if (missingSaved.length === 0 && missingProjectOrder.length === 0) return {
2009
- projectRootsAdded: 0,
2010
- savedRootsAdded: 0,
2011
- state
2012
- };
2013
- state["electron-saved-workspace-roots"] = [...savedRoots, ...missingSaved];
2014
- state["project-order"] = [...projectOrder, ...missingProjectOrder];
2015
- return {
2016
- projectRootsAdded: missingProjectOrder.length,
2017
- savedRootsAdded: missingSaved.length,
2018
- state
2019
- };
2020
- };
2021
- var getProjectTopLevelThreads = (db, projectName) => {
2022
- return db.query("SELECT id, cwd, rollout_path, thread_source FROM threads WHERE archived = 0").all().filter((thread) => {
2023
- return getPortablePathBasename(thread.cwd) === projectName && thread.thread_source !== "subagent";
2024
- });
2025
- };
2026
- var refreshThreadRows = (db, threadIds) => {
2027
- if (threadIds.length === 0) return 0;
2028
- const nowSeconds = Math.floor(Date.now() / 1e3);
2029
- const nowMs = Date.now();
2030
- const placeholders = threadIds.map(() => "?").join(", ");
2031
- const result = db.prepare(`
2032
- UPDATE threads
2033
- SET updated_at = ?1,
2034
- updated_at_ms = ?2,
2035
- has_user_event = 1
2036
- WHERE id IN (${placeholders})
2037
- `).run(nowSeconds, nowMs, ...threadIds);
2038
- return Number(result.changes);
2039
- };
2040
- var refreshSessionIndex = async (sessionIndexPath, threadIds) => {
2041
- if (threadIds.length === 0) return 0;
2042
- const now = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
2043
- const threadIdSet = new Set(threadIds);
2044
- const lines = (await Bun.file(sessionIndexPath).text()).split("\n");
2045
- let updated = 0;
2046
- const rewrittenLines = [];
2047
- for (const line of lines) {
2048
- if (!line.trim()) continue;
2049
- const parsed = JSON.parse(line);
2050
- if (parsed.id && threadIdSet.has(parsed.id)) {
2051
- parsed.updated_at = now;
2052
- updated += 1;
2053
- }
2054
- rewrittenLines.push(JSON.stringify(parsed));
2055
- }
2056
- await Bun.write(sessionIndexPath, `${rewrittenLines.join("\n")}\n`);
2057
- return updated;
2058
- };
2059
- var touchRolloutFiles = async (codexDir, rolloutPaths) => {
2060
- const now = /* @__PURE__ */ new Date();
2061
- let touched = 0;
2062
- for (const rolloutPath of rolloutPaths) {
2063
- const absolutePath = path.isAbsolute(rolloutPath) ? rolloutPath : path.join(codexDir, rolloutPath);
2064
- if (!await Bun.file(absolutePath).exists()) continue;
2065
- await utimes(absolutePath, now, now);
2066
- touched += 1;
2067
- }
2068
- return touched;
2069
- };
2070
- var recoverCodexProjectThreads = async (dbPath, projectName) => {
2071
- const codexDir = resolveCodexDirFromDbPath(dbPath);
2072
- const globalStatePath = path.join(codexDir, ".codex-global-state.json");
2073
- const sessionIndexPath = path.join(codexDir, "session_index.jsonl");
2074
- await assertRequiredStatePath(dbPath);
2075
- await assertRequiredStatePath(globalStatePath);
2076
- await assertRequiredStatePath(sessionIndexPath);
2077
- const backups = {
2078
- globalState: await backupFile(globalStatePath, "recover-project-roots"),
2079
- sessionIndex: await backupFile(sessionIndexPath, "recover-project-session-index"),
2080
- stateDb: await backupFile(dbPath, "recover-project-threads")
2081
- };
2082
- const globalState = await readGlobalState(globalStatePath);
2083
- const db = runWithSqliteRetry({ action: () => {
2084
- const opened = new Database(dbPath);
2085
- opened.exec("PRAGMA busy_timeout = 5000");
2086
- return opened;
2087
- } });
2088
- try {
2089
- const topLevelThreads = getProjectTopLevelThreads(db, projectName);
2090
- const projectCwds = [...new Set(topLevelThreads.map((thread) => thread.cwd))];
2091
- const rootUpdateResult = updateGlobalRoots(globalState, projectCwds);
2092
- await writeGlobalState(globalStatePath, rootUpdateResult.state);
2093
- const threadIds = topLevelThreads.map((thread) => thread.id);
2094
- const rolloutPaths = topLevelThreads.map((thread) => thread.rollout_path);
2095
- const threadDbRowsUpdated = refreshThreadRows(db, threadIds);
2096
- const sessionIndexRowsUpdated = await refreshSessionIndex(sessionIndexPath, threadIds);
2097
- const rolloutFilesTouched = await touchRolloutFiles(codexDir, rolloutPaths);
2098
- return {
2099
- backups,
2100
- projectName,
2101
- projectRootsAdded: rootUpdateResult.projectRootsAdded,
2102
- resolvedCwds: projectCwds,
2103
- rolloutFilesTouched,
2104
- savedRootsAdded: rootUpdateResult.savedRootsAdded,
2105
- sessionIndexRowsUpdated,
2106
- threadDbRowsUpdated,
2107
- topLevelThreadsFound: threadIds.length
2108
- };
2109
- } finally {
2110
- db.close();
2111
- }
2112
- };
2113
- //#endregion
2114
- //#region src/lib/codex-server.ts?tss-serverfn-split
2115
- var projectSchema = z.object({ project: z.string().min(1) });
2116
- var deleteProjectSchema = z.object({
2117
- deleteSessionFiles: z.boolean().default(false),
2118
- project: z.string().min(1)
2119
- });
2120
- var threadSchema = z.object({ threadId: z.string().min(1) });
2121
- var deleteThreadSchema = z.object({
2122
- deleteSessionFiles: z.boolean().default(false),
2123
- threadId: z.string().min(1)
2124
- });
2125
- var deleteThreadsSchema = z.object({
2126
- deleteSessionFiles: z.boolean().default(false),
2127
- threadIds: z.array(z.string().min(1)).min(1)
2128
- });
2129
- var analyticsSchema = z.object({ project: z.string().min(1).nullable() });
2130
- var exportSchema = z.object({
2131
- convertToProjectRoot: z.boolean(),
2132
- includeCommentary: z.boolean(),
2133
- includeMetadata: z.boolean(),
2134
- includeTools: z.boolean(),
2135
- outputFormat: z.enum(["md", "txt"]),
2136
- redactUsername: z.boolean(),
2137
- threadId: z.string().min(1),
2138
- zipArchive: z.boolean().default(false)
2139
- });
2140
- var exportThreadsSchema = z.object({
2141
- convertToProjectRoot: z.boolean(),
2142
- includeCommentary: z.boolean(),
2143
- includeMetadata: z.boolean(),
2144
- includeTools: z.boolean(),
2145
- outputFormat: z.enum(["md", "txt"]),
2146
- redactUsername: z.boolean(),
2147
- threadIds: z.array(z.string().min(1)).min(1),
2148
- zipArchive: z.boolean().default(true)
2149
- });
2150
- var getDbPath = () => process.env.SPIRACHA_CODEX_DB?.trim() || resolveCodexThreadDbPath();
2151
- var isMissingFileError = (error) => {
2152
- return error instanceof Error && /ENOENT|no such file/i.test(error.message);
2153
- };
2154
- var getDashboardSummaryFn_createServerFn_handler = createServerRpc({
2155
- id: "792690638a3b10035a5b7368c3d98bdc70cbfe1e36a4aa5f45b1c49b8b1025b0",
2156
- name: "getDashboardSummaryFn",
2157
- filename: "src/lib/codex-server.ts"
2158
- }, (opts) => getDashboardSummaryFn.__executeServer(opts));
2159
- var getDashboardSummaryFn = createServerFn({ method: "GET" }).handler(getDashboardSummaryFn_createServerFn_handler, async () => {
2160
- return getCodexDashboardSummary(getDbPath());
2161
- });
2162
- var listProjectsFn_createServerFn_handler = createServerRpc({
2163
- id: "ccefccb816ba13508f23db4e31067b3403e750225257592d3ae11071ffc3fd6f",
2164
- name: "listProjectsFn",
2165
- filename: "src/lib/codex-server.ts"
2166
- }, (opts) => listProjectsFn.__executeServer(opts));
2167
- var listProjectsFn = createServerFn({ method: "GET" }).handler(listProjectsFn_createServerFn_handler, async () => {
2168
- return listCodexProjects(getDbPath());
2169
- });
2170
- var listProjectThreadsFn_createServerFn_handler = createServerRpc({
2171
- id: "59fb2cb4d60c8e7d47e0afcc914ee6f9d9f4bf076c8e66eab1693066753655b3",
2172
- name: "listProjectThreadsFn",
2173
- filename: "src/lib/codex-server.ts"
2174
- }, (opts) => listProjectThreadsFn.__executeServer(opts));
2175
- var listProjectThreadsFn = createServerFn({ method: "GET" }).validator(projectSchema).handler(listProjectThreadsFn_createServerFn_handler, async ({ data }) => {
2176
- return listProjectThreads(getDbPath(), data.project);
2177
- });
2178
- var getThreadSnapshotFn_createServerFn_handler = createServerRpc({
2179
- id: "72991e2b6e0adbf8d63bb8b139dad88a00b77b7030ec28ceac36c3cce7846b4c",
2180
- name: "getThreadSnapshotFn",
2181
- filename: "src/lib/codex-server.ts"
2182
- }, (opts) => getThreadSnapshotFn.__executeServer(opts));
2183
- var getThreadSnapshotFn = createServerFn({ method: "GET" }).validator(threadSchema).handler(getThreadSnapshotFn_createServerFn_handler, async ({ data }) => {
2184
- const browseData = getThreadBrowseData(getDbPath(), data.threadId);
2185
- const rollout = await getThreadRolloutLoadState(browseData.thread.rollout_path);
2186
- let transcript = null;
2187
- let transcriptState = rollout.shouldDeferTranscriptLoad ? "deferred" : "available";
2188
- if (!rollout.shouldDeferTranscriptLoad) try {
2189
- transcript = await getCachedParsedCodexTranscript(browseData.thread.rollout_path);
2190
- } catch (error) {
2191
- if (!isMissingFileError(error)) throw error;
2192
- transcriptState = "missing";
2193
- }
2194
- return {
2195
- ...browseData,
2196
- availableTools: browseData.dynamicTools.length > 0 ? browseData.dynamicTools : transcript?.sessionMeta.dynamicTools ?? [],
2197
- rollout,
2198
- transcript,
2199
- transcriptState
2200
- };
2201
- });
2202
- var getThreadTranscriptFn_createServerFn_handler = createServerRpc({
2203
- id: "5da27045f7e28ded6353bc16aace284af7ef1b4010ef04d0186a6feadb466497",
2204
- name: "getThreadTranscriptFn",
2205
- filename: "src/lib/codex-server.ts"
2206
- }, (opts) => getThreadTranscriptFn.__executeServer(opts));
2207
- var getThreadTranscriptFn = createServerFn({ method: "GET" }).validator(threadSchema).handler(getThreadTranscriptFn_createServerFn_handler, async ({ data }) => {
2208
- return getCachedThreadTranscriptPreview(getThreadBrowseData(getDbPath(), data.threadId).thread.rollout_path);
2209
- });
2210
- var getAnalyticsFn_createServerFn_handler = createServerRpc({
2211
- id: "4712520da0f07bbd1f0907e5a162fe518516ff4caca3fd23876cc65539d87d7a",
2212
- name: "getAnalyticsFn",
2213
- filename: "src/lib/codex-server.ts"
2214
- }, (opts) => getAnalyticsFn.__executeServer(opts));
2215
- var getAnalyticsFn = createServerFn({ method: "GET" }).validator(analyticsSchema).handler(getAnalyticsFn_createServerFn_handler, async ({ data }) => {
2216
- return getCodexAnalytics({
2217
- dbPath: getDbPath(),
2218
- project: data.project
2219
- });
2220
- });
2221
- var exportThreadFn_createServerFn_handler = createServerRpc({
2222
- id: "0814663c3bdc58135f97d210d145ef0be5ca54ef9a5f1e3030be9b1bfc901e30",
2223
- name: "exportThreadFn",
2224
- filename: "src/lib/codex-server.ts"
2225
- }, (opts) => exportThreadFn.__executeServer(opts));
2226
- var exportThreadFn = createServerFn({ method: "POST" }).validator(exportSchema).handler(exportThreadFn_createServerFn_handler, async ({ data }) => {
2227
- return renderCodexThreadDownload({
2228
- dbPath: getDbPath(),
2229
- includeCommentary: data.includeCommentary,
2230
- includeMetadata: data.includeMetadata,
2231
- includeTools: data.includeTools,
2232
- outputFormat: data.outputFormat,
2233
- pathDisplaySettings: {
2234
- convertToProjectRoot: data.convertToProjectRoot,
2235
- redactUsername: data.redactUsername
2236
- },
2237
- threadId: data.threadId,
2238
- zipArchive: data.zipArchive
2239
- });
2240
- });
2241
- var exportThreadsFn_createServerFn_handler = createServerRpc({
2242
- id: "b4e15c006e9a277470958bb008f89b5b0acc7256109581de44cf17d587d174a5",
2243
- name: "exportThreadsFn",
2244
- filename: "src/lib/codex-server.ts"
2245
- }, (opts) => exportThreadsFn.__executeServer(opts));
2246
- var exportThreadsFn = createServerFn({ method: "POST" }).validator(exportThreadsSchema).handler(exportThreadsFn_createServerFn_handler, async ({ data }) => {
2247
- return renderCodexThreadsDownload({
2248
- dbPath: getDbPath(),
2249
- includeCommentary: data.includeCommentary,
2250
- includeMetadata: data.includeMetadata,
2251
- includeTools: data.includeTools,
2252
- outputFormat: data.outputFormat,
2253
- pathDisplaySettings: {
2254
- convertToProjectRoot: data.convertToProjectRoot,
2255
- redactUsername: data.redactUsername
2256
- },
2257
- threadIds: data.threadIds,
2258
- zipArchive: data.zipArchive
2259
- });
2260
- });
2261
- var deleteThreadFn_createServerFn_handler = createServerRpc({
2262
- id: "29727b7ad5b8fe42e83817376653e064d9fe8888799f056b2e59296b3396568b",
2263
- name: "deleteThreadFn",
2264
- filename: "src/lib/codex-server.ts"
2265
- }, (opts) => deleteThreadFn.__executeServer(opts));
2266
- var deleteThreadFn = createServerFn({ method: "POST" }).validator(deleteThreadSchema).handler(deleteThreadFn_createServerFn_handler, async ({ data }) => {
2267
- return deleteCodexThread(getDbPath(), data.threadId, { deleteSessionFiles: data.deleteSessionFiles });
2268
- });
2269
- var deleteThreadsFn_createServerFn_handler = createServerRpc({
2270
- id: "96aa60bf7dd9b5bde415bcf3ad6f6955a975eecd9aa0516cf401cc39bebebe6c",
2271
- name: "deleteThreadsFn",
2272
- filename: "src/lib/codex-server.ts"
2273
- }, (opts) => deleteThreadsFn.__executeServer(opts));
2274
- var deleteThreadsFn = createServerFn({ method: "POST" }).validator(deleteThreadsSchema).handler(deleteThreadsFn_createServerFn_handler, async ({ data }) => {
2275
- return deleteCodexThreads(getDbPath(), data.threadIds, { deleteSessionFiles: data.deleteSessionFiles });
2276
- });
2277
- var deleteProjectFn_createServerFn_handler = createServerRpc({
2278
- id: "164ee82cdd565ed96591a64312f0f7bd961040baf066a89d9f5636330d11360b",
2279
- name: "deleteProjectFn",
2280
- filename: "src/lib/codex-server.ts"
2281
- }, (opts) => deleteProjectFn.__executeServer(opts));
2282
- var deleteProjectFn = createServerFn({ method: "POST" }).validator(deleteProjectSchema).handler(deleteProjectFn_createServerFn_handler, async ({ data }) => {
2283
- return deleteCodexProject(getDbPath(), data.project, { deleteSessionFiles: data.deleteSessionFiles });
2284
- });
2285
- var recoverProjectThreadsFn_createServerFn_handler = createServerRpc({
2286
- id: "412c05e00aef3ad43905593fdfd2f566561259da7e1f5d6f1c03428ff33c6867",
2287
- name: "recoverProjectThreadsFn",
2288
- filename: "src/lib/codex-server.ts"
2289
- }, (opts) => recoverProjectThreadsFn.__executeServer(opts));
2290
- var recoverProjectThreadsFn = createServerFn({ method: "POST" }).validator(projectSchema).handler(recoverProjectThreadsFn_createServerFn_handler, async ({ data }) => {
2291
- return recoverCodexProjectThreads(getDbPath(), data.project);
2292
- });
2293
- //#endregion
2294
- export { deleteProjectFn_createServerFn_handler, deleteThreadFn_createServerFn_handler, deleteThreadsFn_createServerFn_handler, exportThreadFn_createServerFn_handler, exportThreadsFn_createServerFn_handler, getAnalyticsFn_createServerFn_handler, getDashboardSummaryFn_createServerFn_handler, getThreadSnapshotFn_createServerFn_handler, getThreadTranscriptFn_createServerFn_handler, listProjectThreadsFn_createServerFn_handler, listProjectsFn_createServerFn_handler, recoverProjectThreadsFn_createServerFn_handler };