airaknit 1.1.2-rc.9

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 (440) hide show
  1. package/LICENSE +84 -0
  2. package/README.md +202 -0
  3. package/bin/airaknit +9 -0
  4. package/bin/airaknit-project +14 -0
  5. package/bin/kanna +9 -0
  6. package/dist/client/assets/CompactSummaryMessage-Yw0BDWEJ.js +1 -0
  7. package/dist/client/assets/ExitPlanModeMessage-DIdkQ4uF.js +1 -0
  8. package/dist/client/assets/LocalFilePreviewDialog-DQx2eiCc.js +3 -0
  9. package/dist/client/assets/LocalProjectsSection-C4xlWkgS.js +1 -0
  10. package/dist/client/assets/TextMessage-B5G39DEJ.js +1 -0
  11. package/dist/client/assets/UserMessage-CIkWk-0L.js +1 -0
  12. package/dist/client/assets/_basePickBy-CVrAFfnZ.js +1 -0
  13. package/dist/client/assets/_baseUniq-JL-aaF4P.js +1 -0
  14. package/dist/client/assets/arc-B07zg7ol.js +1 -0
  15. package/dist/client/assets/architecture-YZFGNWBL-PSLVJL3p.js +1 -0
  16. package/dist/client/assets/architectureDiagram-Q4EWVU46-DfWIF1G_.js +36 -0
  17. package/dist/client/assets/array-BGFCBI0e.js +1 -0
  18. package/dist/client/assets/blockDiagram-DXYQGD6D-CzwIeo_B.js +132 -0
  19. package/dist/client/assets/bundle-mjs-BDE2gWbQ.js +1 -0
  20. package/dist/client/assets/button-DO50qOGv.js +1 -0
  21. package/dist/client/assets/c4Diagram-AHTNJAMY-CR8DCQRE.js +10 -0
  22. package/dist/client/assets/channel-Dj-UUfaF.js +1 -0
  23. package/dist/client/assets/chunk-2KRD3SAO-dznP-cn8.js +1 -0
  24. package/dist/client/assets/chunk-336JU56O-Dqss5Vu6.js +2 -0
  25. package/dist/client/assets/chunk-426QAEUC-CEKis_0_.js +1 -0
  26. package/dist/client/assets/chunk-4BX2VUAB-BYOv0Gm1.js +1 -0
  27. package/dist/client/assets/chunk-4TB4RGXK-BxzubH5S.js +206 -0
  28. package/dist/client/assets/chunk-55IACEB6-BSlTj03a.js +1 -0
  29. package/dist/client/assets/chunk-5FUZZQ4R-9Au93Bi1.js +62 -0
  30. package/dist/client/assets/chunk-5PVQY5BW-BhksHFEZ.js +2 -0
  31. package/dist/client/assets/chunk-67CJDMHE-BFwhz-8t.js +1 -0
  32. package/dist/client/assets/chunk-7N4EOEYR-BDUPds87.js +1 -0
  33. package/dist/client/assets/chunk-AA7GKIK3-CEWTdyXO.js +1 -0
  34. package/dist/client/assets/chunk-BO2N2NFS-D0LvxnhU.js +103 -0
  35. package/dist/client/assets/chunk-BSJP7CBP-BNJnK6sq.js +1 -0
  36. package/dist/client/assets/chunk-Bj-mKKzh.js +1 -0
  37. package/dist/client/assets/chunk-CIAEETIT-CYhfoCeN.js +1 -0
  38. package/dist/client/assets/chunk-EDXVE4YY-C5ovJLc0.js +1 -0
  39. package/dist/client/assets/chunk-ENJZ2VHE-HOhYaeGr.js +10 -0
  40. package/dist/client/assets/chunk-FMBD7UC4-BLCiKcAQ.js +15 -0
  41. package/dist/client/assets/chunk-FOC6F5B3-B6GtY2ek.js +1 -0
  42. package/dist/client/assets/chunk-ICPOFSXX-DPoIZoC5.js +122 -0
  43. package/dist/client/assets/chunk-K5T4RW27-BsKN63rv.js +94 -0
  44. package/dist/client/assets/chunk-KGLVRYIC-BUGn9uuY.js +1 -0
  45. package/dist/client/assets/chunk-LIHQZDEY-DhaZyo03.js +1 -0
  46. package/dist/client/assets/chunk-ORNJ4GCN-DlpeeJyi.js +1 -0
  47. package/dist/client/assets/chunk-OYMX7WX6-Dc9q7aYA.js +231 -0
  48. package/dist/client/assets/chunk-QZHKN3VN-BEdrPoSb.js +1 -0
  49. package/dist/client/assets/chunk-U2HBQHQK-CIB3Bjjd.js +70 -0
  50. package/dist/client/assets/chunk-X2U36JSP-CtB-o8Yp.js +1 -0
  51. package/dist/client/assets/chunk-XPW4576I-C6iHhX_8.js +32 -0
  52. package/dist/client/assets/chunk-YZCP3GAM-CTmKr6ZH.js +1 -0
  53. package/dist/client/assets/chunk-ZZ45TVLE-BgU8A2RF.js +1 -0
  54. package/dist/client/assets/classDiagram-6PBFFD2Q-Bqk5e679.js +1 -0
  55. package/dist/client/assets/classDiagram-v2-HSJHXN6E-6pSaZOkC.js +1 -0
  56. package/dist/client/assets/client-BrKWI4CM.js +1 -0
  57. package/dist/client/assets/client-CGgNRU9w.js +1 -0
  58. package/dist/client/assets/client-DMSLRzg9.js +6 -0
  59. package/dist/client/assets/clone-DWcL7whJ.js +1 -0
  60. package/dist/client/assets/cose-bilkent-S5V4N54A-CrV5wsV_.js +1 -0
  61. package/dist/client/assets/cytoscape.esm--aLzKuep.js +321 -0
  62. package/dist/client/assets/dagre-CuRxWcrj.js +1 -0
  63. package/dist/client/assets/dagre-KV5264BT-BIDiVnkA.js +4 -0
  64. package/dist/client/assets/defaultLocale-CRZydyG6.js +1 -0
  65. package/dist/client/assets/diagram-5BDNPKRD-i1kjKRCB.js +10 -0
  66. package/dist/client/assets/diagram-G4DWMVQ6-9ZSLuhbl.js +24 -0
  67. package/dist/client/assets/diagram-MMDJMWI5-B4_CUjgv.js +43 -0
  68. package/dist/client/assets/diagram-TYMM5635-Ct5eTGS8.js +24 -0
  69. package/dist/client/assets/dist-CuB4kiSK.js +1 -0
  70. package/dist/client/assets/erDiagram-SMLLAGMA-Cy38ercc.js +85 -0
  71. package/dist/client/assets/flowDiagram-DWJPFMVM-CZKuYl0V.js +162 -0
  72. package/dist/client/assets/ganttDiagram-T4ZO3ILL-DLPjCh7a.js +292 -0
  73. package/dist/client/assets/gitGraph-7Q5UKJZL-DqbrtEp9.js +1 -0
  74. package/dist/client/assets/gitGraphDiagram-UUTBAWPF-BoRBkDhQ.js +106 -0
  75. package/dist/client/assets/graphlib-BcQ6qlQh.js +1 -0
  76. package/dist/client/assets/highlighted-body-OFNGDK62-BEpBVDTX.js +1 -0
  77. package/dist/client/assets/index-CetCiuqP.js +105 -0
  78. package/dist/client/assets/index-N29Mip7A.css +1 -0
  79. package/dist/client/assets/info-OMHHGYJF-D98DRBJX.js +1 -0
  80. package/dist/client/assets/infoDiagram-42DDH7IO-BAcdTWbt.js +2 -0
  81. package/dist/client/assets/init-B8gtcn7T.js +1 -0
  82. package/dist/client/assets/isArrayLikeObject-D8SJFmkN.js +1 -0
  83. package/dist/client/assets/isEmpty-BF3YX5Jk.js +1 -0
  84. package/dist/client/assets/ishikawaDiagram-UXIWVN3A-Ynu2VKdC.js +70 -0
  85. package/dist/client/assets/journeyDiagram-VCZTEJTY-BjfhQaN3.js +139 -0
  86. package/dist/client/assets/jsx-runtime-CyI9ICYU.js +1 -0
  87. package/dist/client/assets/kanban-definition-6JOO6SKY-JLXH9zUJ.js +89 -0
  88. package/dist/client/assets/katex-B94qP8b6.js +265 -0
  89. package/dist/client/assets/lib--QVjyxmL.js +29 -0
  90. package/dist/client/assets/lib-B6rgJiZ9.js +1 -0
  91. package/dist/client/assets/line-DCrYfLBn.js +1 -0
  92. package/dist/client/assets/linear-_4upLmeo.js +1 -0
  93. package/dist/client/assets/mermaid-GHXKKRXX-rwJHYUmW.js +1 -0
  94. package/dist/client/assets/mermaid-parser.core-KZinfW8o.js +4 -0
  95. package/dist/client/assets/mermaid.core-QqY9gSNe.js +11 -0
  96. package/dist/client/assets/mindmap-definition-QFDTVHPH-TWgHDAzp.js +96 -0
  97. package/dist/client/assets/ordinal-CCj7PWgZ.js +1 -0
  98. package/dist/client/assets/packet-4T2RLAQJ-DEvfkn3F.js +1 -0
  99. package/dist/client/assets/path-DZF-JdEe.js +1 -0
  100. package/dist/client/assets/pie-ZZUOXDRM-72e6WVjb.js +1 -0
  101. package/dist/client/assets/pieDiagram-DEJITSTG-Cl8PCsoj.js +30 -0
  102. package/dist/client/assets/preload-helper-rov5CBGT.js +1 -0
  103. package/dist/client/assets/pty-client-DZ27IS00.js +1 -0
  104. package/dist/client/assets/ptyInstancesStore-D9ag7SYd.js +1 -0
  105. package/dist/client/assets/quadrantDiagram-34T5L4WZ-CHyVGp9E.js +7 -0
  106. package/dist/client/assets/radar-PYXPWWZC-Cp7xd_EY.js +1 -0
  107. package/dist/client/assets/react-CClhXMB2.js +1 -0
  108. package/dist/client/assets/react-Dd6D81m0.js +1 -0
  109. package/dist/client/assets/react-dom--G6_6fQ_.js +1 -0
  110. package/dist/client/assets/requirementDiagram-MS252O5E-DaanG2iM.js +84 -0
  111. package/dist/client/assets/rough.esm-BsmKo2S5.js +1 -0
  112. package/dist/client/assets/sankeyDiagram-XADWPNL6-B_fhLY36.js +10 -0
  113. package/dist/client/assets/sequenceDiagram-FGHM5R23-C5FNrveI.js +157 -0
  114. package/dist/client/assets/src-DeTlMJAU.js +1 -0
  115. package/dist/client/assets/stateDiagram-FHFEXIEX-nTTcdjjQ.js +1 -0
  116. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-Dw0632j_.js +1 -0
  117. package/dist/client/assets/timeline-definition-GMOUNBTQ-DkQV1yP8.js +120 -0
  118. package/dist/client/assets/treeView-SZITEDCU-ZLIgC7_K.js +1 -0
  119. package/dist/client/assets/treemap-W4RFUUIX-BqaMbB6N.js +1 -0
  120. package/dist/client/assets/uiIdentityOverlay-Ba7GNj7m.js +1 -0
  121. package/dist/client/assets/vennDiagram-DHZGUBPP-DbZ2xgs6.js +34 -0
  122. package/dist/client/assets/wardley-RL74JXVD-DXQS8zf4.js +1 -0
  123. package/dist/client/assets/wardleyDiagram-NUSXRM2D-BzCJ6MAu.js +20 -0
  124. package/dist/client/assets/xychartDiagram-5P7HB3ND-BSlFecop.js +7 -0
  125. package/dist/client/favicon.png +0 -0
  126. package/dist/client/favicon.svg +15 -0
  127. package/dist/client/fonts/body-medium.woff2 +0 -0
  128. package/dist/client/fonts/body-regular-italic.woff2 +0 -0
  129. package/dist/client/fonts/body-regular.woff2 +0 -0
  130. package/dist/client/fonts/body-semibold.woff2 +0 -0
  131. package/dist/client/index.html +31 -0
  132. package/dist/client/manifest.webmanifest +12 -0
  133. package/dist/client/sw.js +32 -0
  134. package/package.json +122 -0
  135. package/src/nats/auth-callout/callout-config.test.ts +93 -0
  136. package/src/nats/auth-callout/callout-config.ts +109 -0
  137. package/src/nats/auth-callout/callout.integration.test.ts +332 -0
  138. package/src/nats/auth-callout/keys.ts +103 -0
  139. package/src/nats/auth-callout/responder.ts +241 -0
  140. package/src/nats/auth-callout/scope-policy.test.ts +159 -0
  141. package/src/nats/auth-callout/scope-policy.ts +210 -0
  142. package/src/nats/auth-callout/token.test.ts +163 -0
  143. package/src/nats/auth-callout/token.ts +157 -0
  144. package/src/nats/nats-daemon-callout.ts +194 -0
  145. package/src/nats/nats-daemon.test.ts +77 -0
  146. package/src/nats/nats-daemon.ts +50 -0
  147. package/src/nats/nats-token.test.ts +61 -0
  148. package/src/nats/nats-token.ts +59 -0
  149. package/src/runner/coordination-mcp-integration.test.ts +134 -0
  150. package/src/runner/nats-coordination-client.test.ts +49 -0
  151. package/src/runner/nats-coordination-client.ts +94 -0
  152. package/src/runner/runner-agent.test.ts +469 -0
  153. package/src/runner/runner-agent.ts +453 -0
  154. package/src/runner/runner-credential.test.ts +93 -0
  155. package/src/runner/runner-credential.ts +82 -0
  156. package/src/runner/runner-nats.test.ts +495 -0
  157. package/src/runner/runner-nats.ts +323 -0
  158. package/src/runner/runner-pair.test.ts +107 -0
  159. package/src/runner/runner-pair.ts +81 -0
  160. package/src/runner/runner.test.ts +135 -0
  161. package/src/runner/runner.ts +212 -0
  162. package/src/runner/turn-factories.test.ts +97 -0
  163. package/src/runner/turn-factories.ts +475 -0
  164. package/src/server/agent-config-journey.test.ts +106 -0
  165. package/src/server/agent.ts +8 -0
  166. package/src/server/auto-continue/auth-error-detector.ts +66 -0
  167. package/src/server/auto-continue/limit-detector.ts +194 -0
  168. package/src/server/bm25.test.ts +92 -0
  169. package/src/server/bm25.ts +101 -0
  170. package/src/server/chat-events-jetstream.test.ts +135 -0
  171. package/src/server/claude-harness.ts +360 -0
  172. package/src/server/claude-pty/agent-normalizers.ts +309 -0
  173. package/src/server/claude-pty/auth.test.ts +38 -0
  174. package/src/server/claude-pty/auth.ts +32 -0
  175. package/src/server/claude-pty/claude-session-registry.adapter.ts +81 -0
  176. package/src/server/claude-pty/claude-session-registry.test.ts +149 -0
  177. package/src/server/claude-pty/driver.test.ts +902 -0
  178. package/src/server/claude-pty/driver.ts +807 -0
  179. package/src/server/claude-pty/jsonl-path.adapter.ts +57 -0
  180. package/src/server/claude-pty/jsonl-path.test.ts +114 -0
  181. package/src/server/claude-pty/jsonl-to-event.test.ts +241 -0
  182. package/src/server/claude-pty/jsonl-to-event.ts +174 -0
  183. package/src/server/claude-pty/output-ring.test.ts +35 -0
  184. package/src/server/claude-pty/output-ring.ts +25 -0
  185. package/src/server/claude-pty/parity-matrix.test.ts +227 -0
  186. package/src/server/claude-pty/pid-registry.adapter.ts +135 -0
  187. package/src/server/claude-pty/pid-registry.test.ts +122 -0
  188. package/src/server/claude-pty/preflight/binary-fingerprint.adapter.ts +20 -0
  189. package/src/server/claude-pty/preflight/binary-fingerprint.test.ts +32 -0
  190. package/src/server/claude-pty/pty-instance-registry.test.ts +177 -0
  191. package/src/server/claude-pty/pty-instance-registry.ts +166 -0
  192. package/src/server/claude-pty/pty-memory-sampler.adapter.test.ts +103 -0
  193. package/src/server/claude-pty/pty-memory-sampler.adapter.ts +85 -0
  194. package/src/server/claude-pty/pty-process.adapter.ts +66 -0
  195. package/src/server/claude-pty/pty-process.test.ts +49 -0
  196. package/src/server/claude-pty/resolve-binary.adapter.ts +106 -0
  197. package/src/server/claude-pty/resolve-binary.test.ts +118 -0
  198. package/src/server/claude-pty/runtime-dir.adapter.ts +19 -0
  199. package/src/server/claude-pty/settings-writer.adapter.ts +27 -0
  200. package/src/server/claude-pty/settings-writer.test.ts +22 -0
  201. package/src/server/claude-pty/smoke-test-io.adapter.ts +28 -0
  202. package/src/server/claude-pty/smoke-test.test.ts +191 -0
  203. package/src/server/claude-pty/smoke-test.ts +185 -0
  204. package/src/server/claude-pty/subagent-orchestrator.ts +887 -0
  205. package/src/server/claude-pty/tool-callback.ts +274 -0
  206. package/src/server/claude-pty/tui-control.test.ts +272 -0
  207. package/src/server/claude-pty/tui-control.ts +182 -0
  208. package/src/server/claude-pty/tui-source.adapter.test.ts +360 -0
  209. package/src/server/claude-pty/tui-source.adapter.ts +343 -0
  210. package/src/server/claude-pty/tunnel-gateway.ts +12 -0
  211. package/src/server/claude-pty-mcp/canonical-args.ts +15 -0
  212. package/src/server/claude-pty-mcp/fs-stat.adapter.ts +8 -0
  213. package/src/server/claude-pty-mcp/history-primer.ts +90 -0
  214. package/src/server/claude-pty-mcp/http-server.adapter.ts +33 -0
  215. package/src/server/claude-pty-mcp/mcp-http.ts +177 -0
  216. package/src/server/claude-pty-mcp/mcp.ts +412 -0
  217. package/src/server/claude-pty-mcp/mention-parser.ts +25 -0
  218. package/src/server/claude-pty-mcp/paths.ts +24 -0
  219. package/src/server/claude-pty-mcp/permission-gate.ts +243 -0
  220. package/src/server/claude-pty-mcp/terminal-pid-registry.adapter.ts +107 -0
  221. package/src/server/claude-pty-mcp/tools/ask-user-question.test.ts +119 -0
  222. package/src/server/claude-pty-mcp/tools/ask-user-question.ts +61 -0
  223. package/src/server/claude-pty-mcp/tools/bash.adapter.ts +76 -0
  224. package/src/server/claude-pty-mcp/tools/bash.test.ts +56 -0
  225. package/src/server/claude-pty-mcp/tools/delegate-subagent.test.ts +155 -0
  226. package/src/server/claude-pty-mcp/tools/delegate-subagent.ts +111 -0
  227. package/src/server/claude-pty-mcp/tools/edit.adapter.ts +95 -0
  228. package/src/server/claude-pty-mcp/tools/edit.test.ts +93 -0
  229. package/src/server/claude-pty-mcp/tools/exit-plan-mode.test.ts +61 -0
  230. package/src/server/claude-pty-mcp/tools/exit-plan-mode.ts +50 -0
  231. package/src/server/claude-pty-mcp/tools/glob.adapter.ts +86 -0
  232. package/src/server/claude-pty-mcp/tools/glob.test.ts +61 -0
  233. package/src/server/claude-pty-mcp/tools/grep.adapter.ts +126 -0
  234. package/src/server/claude-pty-mcp/tools/grep.test.ts +62 -0
  235. package/src/server/claude-pty-mcp/tools/read.adapter.ts +58 -0
  236. package/src/server/claude-pty-mcp/tools/read.test.ts +62 -0
  237. package/src/server/claude-pty-mcp/tools/tool-callback-shim.ts +42 -0
  238. package/src/server/claude-pty-mcp/tools/webfetch.test.ts +81 -0
  239. package/src/server/claude-pty-mcp/tools/webfetch.ts +82 -0
  240. package/src/server/claude-pty-mcp/tools/websearch.test.ts +40 -0
  241. package/src/server/claude-pty-mcp/tools/websearch.ts +42 -0
  242. package/src/server/claude-pty-mcp/tools/write.adapter.ts +60 -0
  243. package/src/server/claude-pty-mcp/tools/write.test.ts +52 -0
  244. package/src/server/claude-pty-mcp/uploads.adapter.ts +98 -0
  245. package/src/server/claude-pty-mcp/uploads.ts +38 -0
  246. package/src/server/claude-turn.test.ts +176 -0
  247. package/src/server/cli-runtime.test.ts +456 -0
  248. package/src/server/cli-runtime.ts +374 -0
  249. package/src/server/cli-supervisor.ts +81 -0
  250. package/src/server/cli.ts +78 -0
  251. package/src/server/client-log-forwarder.test.ts +74 -0
  252. package/src/server/client-log-forwarder.ts +75 -0
  253. package/src/server/codex-app-server-protocol.ts +449 -0
  254. package/src/server/codex-app-server.test.ts +2990 -0
  255. package/src/server/codex-app-server.ts +1713 -0
  256. package/src/server/coordination-integration.test.ts +63 -0
  257. package/src/server/coordination-mcp.test.ts +149 -0
  258. package/src/server/coordination-mcp.ts +197 -0
  259. package/src/server/delegation-coordinator.test.ts +675 -0
  260. package/src/server/delegation-coordinator.ts +454 -0
  261. package/src/server/discovery.test.ts +211 -0
  262. package/src/server/discovery.ts +301 -0
  263. package/src/server/event-store-agent-config.test.ts +124 -0
  264. package/src/server/event-store-coordination.test.ts +149 -0
  265. package/src/server/event-store-profile.test.ts +132 -0
  266. package/src/server/event-store-repo.test.ts +154 -0
  267. package/src/server/event-store-runner-team.test.ts +104 -0
  268. package/src/server/event-store.test.ts +342 -0
  269. package/src/server/event-store.ts +2208 -0
  270. package/src/server/events.ts +379 -0
  271. package/src/server/extension-router.test.ts +183 -0
  272. package/src/server/extension-router.ts +114 -0
  273. package/src/server/extensions/agents/server.test.ts +191 -0
  274. package/src/server/extensions/agents/server.ts +108 -0
  275. package/src/server/extensions/c3/server.test.ts +284 -0
  276. package/src/server/extensions/c3/server.ts +212 -0
  277. package/src/server/extensions/code/server.test.ts +200 -0
  278. package/src/server/extensions/code/server.ts +150 -0
  279. package/src/server/extensions.config.ts +10 -0
  280. package/src/server/external-open.ts +69 -0
  281. package/src/server/generate-fork-context.ts +58 -0
  282. package/src/server/generate-merge-context.test.ts +290 -0
  283. package/src/server/generate-merge-context.ts +141 -0
  284. package/src/server/generate-title.ts +36 -0
  285. package/src/server/git-clone-policy.test.ts +138 -0
  286. package/src/server/git-clone-policy.ts +27 -0
  287. package/src/server/harness-types.ts +1 -0
  288. package/src/server/journey-verification.test.ts +640 -0
  289. package/src/server/journey-verification.ts +195 -0
  290. package/src/server/machine-name.ts +22 -0
  291. package/src/server/nats-auth.test.ts +92 -0
  292. package/src/server/nats-auth.ts +6 -0
  293. package/src/server/nats-bind-guard.test.ts +71 -0
  294. package/src/server/nats-bind-guard.ts +42 -0
  295. package/src/server/nats-bridge.test.ts +141 -0
  296. package/src/server/nats-bridge.ts +111 -0
  297. package/src/server/nats-connector.test.ts +56 -0
  298. package/src/server/nats-connector.ts +71 -0
  299. package/src/server/nats-daemon-manager.test.ts +76 -0
  300. package/src/server/nats-daemon-manager.ts +174 -0
  301. package/src/server/nats-publisher.test.ts +356 -0
  302. package/src/server/nats-publisher.ts +271 -0
  303. package/src/server/nats-responders.test.ts +1018 -0
  304. package/src/server/nats-responders.ts +925 -0
  305. package/src/server/nats-streams.test.ts +112 -0
  306. package/src/server/nats-streams.ts +129 -0
  307. package/src/server/oauth-pool/oauth-responders.ts +185 -0
  308. package/src/server/oauth-pool/oauth-settings-store.ts +173 -0
  309. package/src/server/oauth-pool/oauth-token-pool.ts +303 -0
  310. package/src/server/orchestration.test.ts +1063 -0
  311. package/src/server/orchestration.ts +716 -0
  312. package/src/server/pairing-endpoints.test.ts +171 -0
  313. package/src/server/pairing-store.test.ts +154 -0
  314. package/src/server/pairing-store.ts +124 -0
  315. package/src/server/paths.ts +27 -0
  316. package/src/server/pr3-liveness.test.ts +252 -0
  317. package/src/server/process-utils.ts +10 -0
  318. package/src/server/project-cli.ts +180 -0
  319. package/src/server/provider-catalog.test.ts +177 -0
  320. package/src/server/provider-catalog.ts +146 -0
  321. package/src/server/pty-responders.ts +345 -0
  322. package/src/server/push-notifications.test.ts +234 -0
  323. package/src/server/push-notifications.ts +188 -0
  324. package/src/server/quick-response.test.ts +196 -0
  325. package/src/server/quick-response.ts +154 -0
  326. package/src/server/read-models-agent-config.test.ts +59 -0
  327. package/src/server/read-models-coordination.test.ts +69 -0
  328. package/src/server/read-models.test.ts +332 -0
  329. package/src/server/read-models.ts +258 -0
  330. package/src/server/repo-journey.test.ts +96 -0
  331. package/src/server/repo-manager.test.ts +118 -0
  332. package/src/server/repo-manager.ts +97 -0
  333. package/src/server/repo-status.test.ts +54 -0
  334. package/src/server/repo-status.ts +82 -0
  335. package/src/server/restart.test.ts +27 -0
  336. package/src/server/restart.ts +30 -0
  337. package/src/server/runner-incompatible-gate.test.ts +383 -0
  338. package/src/server/runner-manager.test.ts +72 -0
  339. package/src/server/runner-manager.ts +312 -0
  340. package/src/server/runner-pairing-urls.test.ts +59 -0
  341. package/src/server/runner-pairing-urls.ts +67 -0
  342. package/src/server/runner-proxy.test.ts +456 -0
  343. package/src/server/runner-proxy.ts +494 -0
  344. package/src/server/runner-router.test.ts +429 -0
  345. package/src/server/runner-router.ts +212 -0
  346. package/src/server/runner-routing.test.ts +584 -0
  347. package/src/server/runtime-registry.test.ts +436 -0
  348. package/src/server/runtime-registry.ts +307 -0
  349. package/src/server/sandbox-health.test.ts +127 -0
  350. package/src/server/sandbox-health.ts +94 -0
  351. package/src/server/sandbox-journey.test.ts +232 -0
  352. package/src/server/sandbox-manager.test.ts +146 -0
  353. package/src/server/sandbox-manager.ts +159 -0
  354. package/src/server/server.test.ts +287 -0
  355. package/src/server/server.ts +1108 -0
  356. package/src/server/session-discovery.test.ts +129 -0
  357. package/src/server/session-discovery.ts +475 -0
  358. package/src/server/session-index.test.ts +362 -0
  359. package/src/server/session-index.ts +119 -0
  360. package/src/server/session-seed.ts +288 -0
  361. package/src/server/share.test.ts +108 -0
  362. package/src/server/share.ts +113 -0
  363. package/src/server/skill-discovery.test.ts +201 -0
  364. package/src/server/skill-discovery.ts +77 -0
  365. package/src/server/storage/test-helpers.ts +67 -0
  366. package/src/server/terminal-manager.test.ts +309 -0
  367. package/src/server/terminal-manager.ts +354 -0
  368. package/src/server/transcript-consumer.test.ts +339 -0
  369. package/src/server/transcript-consumer.ts +162 -0
  370. package/src/server/transcript-search.test.ts +193 -0
  371. package/src/server/transcript-search.ts +83 -0
  372. package/src/server/transcript-utils.ts +52 -0
  373. package/src/server/update-manager.test.ts +107 -0
  374. package/src/server/update-manager.ts +230 -0
  375. package/src/server/workflow-engine.test.ts +251 -0
  376. package/src/server/workflow-engine.ts +169 -0
  377. package/src/server/workflow-mcp.ts +49 -0
  378. package/src/server/workflow-store.test.ts +155 -0
  379. package/src/server/workflow-store.ts +139 -0
  380. package/src/server/workspace-agent-integration.test.ts +167 -0
  381. package/src/server/workspace-agent-routes.test.ts +127 -0
  382. package/src/server/workspace-agent-routes.ts +89 -0
  383. package/src/server/workspace-agent.test.ts +103 -0
  384. package/src/server/workspace-agent.ts +102 -0
  385. package/src/server/workspace-cli.test.ts +79 -0
  386. package/src/server/workspace-config-manager.test.ts +109 -0
  387. package/src/server/workspace-config-manager.ts +83 -0
  388. package/src/server/workspace-directory-policy.test.ts +109 -0
  389. package/src/server/workspace-directory-policy.ts +56 -0
  390. package/src/shared/agent-config-types.ts +25 -0
  391. package/src/shared/branding.test.ts +42 -0
  392. package/src/shared/branding.ts +54 -0
  393. package/src/shared/compression.test.ts +85 -0
  394. package/src/shared/compression.ts +42 -0
  395. package/src/shared/coordination-store.test.ts +24 -0
  396. package/src/shared/coordination-store.ts +26 -0
  397. package/src/shared/dev-ports.test.ts +84 -0
  398. package/src/shared/dev-ports.ts +100 -0
  399. package/src/shared/extension-types.ts +45 -0
  400. package/src/shared/fork-presets.ts +54 -0
  401. package/src/shared/harness-types.test.ts +15 -0
  402. package/src/shared/harness-types.ts +21 -0
  403. package/src/shared/log-sink.test.ts +112 -0
  404. package/src/shared/log-sink.ts +185 -0
  405. package/src/shared/mention-pattern.ts +7 -0
  406. package/src/shared/merge-presets.ts +41 -0
  407. package/src/shared/nats-subjects.test.ts +61 -0
  408. package/src/shared/nats-subjects.ts +131 -0
  409. package/src/shared/permission-policy.ts +136 -0
  410. package/src/shared/ports.ts +3 -0
  411. package/src/shared/preset-types.ts +15 -0
  412. package/src/shared/profile-types.ts +49 -0
  413. package/src/shared/projectFileUrl.ts +36 -0
  414. package/src/shared/protocol.ts +153 -0
  415. package/src/shared/pty-instance.ts +43 -0
  416. package/src/shared/puggy/diagnostics/result.ts +18 -0
  417. package/src/shared/puggy/expressions/evaluate.ts +292 -0
  418. package/src/shared/puggy/index.test.ts +101 -0
  419. package/src/shared/puggy/index.ts +69 -0
  420. package/src/shared/puggy/parser/ast.ts +110 -0
  421. package/src/shared/puggy/parser/parser.ts +624 -0
  422. package/src/shared/puggy/renderer/html.ts +447 -0
  423. package/src/shared/runner-protocol.test.ts +277 -0
  424. package/src/shared/runner-protocol.ts +210 -0
  425. package/src/shared/runner-team-types.ts +73 -0
  426. package/src/shared/runtime-types.ts +48 -0
  427. package/src/shared/sandbox-types.ts +53 -0
  428. package/src/shared/tailwind-build.test.ts +12 -0
  429. package/src/shared/tinkaria-system-prompt.ts +101 -0
  430. package/src/shared/tools.test.ts +335 -0
  431. package/src/shared/tools.ts +397 -0
  432. package/src/shared/transcript-entries.ts +27 -0
  433. package/src/shared/transcript-render.test.ts +225 -0
  434. package/src/shared/transcript-render.ts +467 -0
  435. package/src/shared/types.ts +1031 -0
  436. package/src/shared/vite-config.test.ts +47 -0
  437. package/src/shared/web-context.test.ts +110 -0
  438. package/src/shared/web-context.ts +76 -0
  439. package/src/shared/workflow-types.ts +51 -0
  440. package/src/shared/workspace-types.ts +100 -0
@@ -0,0 +1,374 @@
1
+ import process from "node:process"
2
+ import { spawnSync } from "node:child_process"
3
+ import { hasCommand, spawnDetached } from "./process-utils"
4
+ import { APP_NAME, CLI_COMMAND, getDataDirDisplay, LOG_PREFIX, PACKAGE_NAME } from "../shared/branding"
5
+ import type { UpdateInstallErrorCode } from "../shared/types"
6
+ import { PROD_SERVER_PORT } from "../shared/ports"
7
+ import { CLI_SUPPRESS_OPEN_ONCE_ENV_VAR } from "./restart"
8
+ import { logShareDetails, renderTerminalQr, startShareTunnel, type StartedShareTunnel } from "./share"
9
+
10
+ export interface CliOptions {
11
+ port: number
12
+ host: string
13
+ openBrowser: boolean
14
+ share: boolean
15
+ strictPort: boolean
16
+ sandbox: boolean
17
+ }
18
+
19
+ export interface CliUpdateOptions {
20
+ version: string
21
+ fetchLatestVersion: (packageName: string) => Promise<string>
22
+ installVersion: (packageName: string, version: string) => UpdateInstallAttemptResult
23
+ argv: string[]
24
+ command: string
25
+ }
26
+
27
+ export interface StartedCli {
28
+ kind: "started"
29
+ stop: () => Promise<void>
30
+ }
31
+
32
+ export interface RestartingCli {
33
+ kind: "restarting"
34
+ reason: "startup_update" | "ui_update"
35
+ }
36
+
37
+ export interface ExitedCli {
38
+ kind: "exited"
39
+ code: number
40
+ }
41
+
42
+ export type CliRunResult = StartedCli | RestartingCli | ExitedCli
43
+
44
+ export interface CliRuntimeDeps {
45
+ version: string
46
+ bunVersion: string
47
+ startServer: (options: CliOptions & {
48
+ update: CliUpdateOptions
49
+ onMigrationProgress?: (message: string) => void
50
+ }) => Promise<{ port: number; stop: () => Promise<void> }>
51
+ fetchLatestVersion: (packageName: string) => Promise<string>
52
+ installVersion: (packageName: string, version: string) => UpdateInstallAttemptResult
53
+ openUrl: (url: string) => void
54
+ log: (message: string) => void
55
+ warn: (message: string) => void
56
+ renderShareQr?: (url: string) => Promise<string>
57
+ startShareTunnel?: (localUrl: string) => Promise<StartedShareTunnel>
58
+ }
59
+
60
+ export interface UpdateInstallAttemptResult {
61
+ ok: boolean
62
+ errorCode: UpdateInstallErrorCode | null
63
+ userTitle: string | null
64
+ userMessage: string | null
65
+ }
66
+
67
+ type ParsedArgs =
68
+ | { kind: "run"; options: CliOptions }
69
+ | { kind: "help" }
70
+ | { kind: "version" }
71
+
72
+ const MINIMUM_BUN_VERSION = "1.3.5"
73
+
74
+ function printHelp() {
75
+ console.log(`${APP_NAME} — local-only project chat UI
76
+
77
+ Usage:
78
+ ${CLI_COMMAND} [options]
79
+
80
+ Options:
81
+ --port <number> Port to listen on (default: ${PROD_SERVER_PORT})
82
+ --host <host> Bind to a specific host or IP
83
+ --remote Shortcut for --host 0.0.0.0
84
+ --share Create a public Cloudflare share URL with terminal QR
85
+ --strict-port Fail instead of trying another port
86
+ --no-open Don't open browser automatically
87
+ --sandbox Enable Docker sandbox isolation for workspaces
88
+ --version Print version and exit
89
+ --help Show this help message`)
90
+ }
91
+
92
+ export function parseArgs(argv: string[]): ParsedArgs {
93
+ let port = PROD_SERVER_PORT
94
+ let host = "127.0.0.1"
95
+ let openBrowser = true
96
+ let share = false
97
+ let sawHost = false
98
+ let sawRemote = false
99
+ let strictPort = false
100
+ let sandbox = false
101
+
102
+ for (let index = 0; index < argv.length; index += 1) {
103
+ const arg = argv[index]
104
+ if (arg === "--version" || arg === "-v") {
105
+ return { kind: "version" }
106
+ }
107
+ if (arg === "--help" || arg === "-h") {
108
+ return { kind: "help" }
109
+ }
110
+ if (arg === "--port") {
111
+ const next = argv[index + 1]
112
+ if (!next) throw new Error("Missing value for --port")
113
+ const parsed = Number(next)
114
+ if (!Number.isFinite(parsed)) throw new Error(`Invalid port: ${next}`)
115
+ port = parsed
116
+ index += 1
117
+ continue
118
+ }
119
+ if (arg === "--host") {
120
+ const next = argv[index + 1]
121
+ if (!next || next.startsWith("-")) throw new Error("Missing value for --host")
122
+ if (share) throw new Error("--share cannot be used with --host")
123
+ host = next
124
+ sawHost = true
125
+ index += 1
126
+ continue
127
+ }
128
+ if (arg === "--remote") {
129
+ if (share) throw new Error("--share cannot be used with --remote")
130
+ host = "0.0.0.0"
131
+ sawRemote = true
132
+ continue
133
+ }
134
+ if (arg === "--share") {
135
+ if (sawHost) throw new Error("--share cannot be used with --host")
136
+ if (sawRemote) throw new Error("--share cannot be used with --remote")
137
+ share = true
138
+ continue
139
+ }
140
+ if (arg === "--no-open") {
141
+ openBrowser = false
142
+ continue
143
+ }
144
+ if (arg === "--strict-port") {
145
+ strictPort = true
146
+ continue
147
+ }
148
+ if (arg === "--sandbox") {
149
+ sandbox = true
150
+ continue
151
+ }
152
+ if (!arg.startsWith("-")) throw new Error(`Unexpected positional argument: ${arg}`)
153
+ }
154
+
155
+ return {
156
+ kind: "run",
157
+ options: {
158
+ port,
159
+ host,
160
+ openBrowser,
161
+ share,
162
+ strictPort,
163
+ sandbox,
164
+ },
165
+ }
166
+ }
167
+
168
+ export function compareVersions(currentVersion: string, latestVersion: string) {
169
+ const currentParts = normalizeVersion(currentVersion)
170
+ const latestParts = normalizeVersion(latestVersion)
171
+ const length = Math.max(currentParts.length, latestParts.length)
172
+
173
+ for (let index = 0; index < length; index += 1) {
174
+ const current = currentParts[index] ?? 0
175
+ const latest = latestParts[index] ?? 0
176
+ if (current === latest) continue
177
+ return current < latest ? -1 : 1
178
+ }
179
+
180
+ return 0
181
+ }
182
+
183
+ function normalizeVersion(version: string) {
184
+ return version
185
+ .trim()
186
+ .replace(/^v/i, "")
187
+ .split("-")[0]
188
+ .split(".")
189
+ .map((part) => Number.parseInt(part, 10))
190
+ .filter((part) => Number.isFinite(part))
191
+ }
192
+
193
+ async function maybeSelfUpdate(_argv: string[], deps: CliRuntimeDeps) {
194
+ if (process.env.KANNA_DISABLE_SELF_UPDATE === "1") {
195
+ return null
196
+ }
197
+
198
+ deps.log(`${LOG_PREFIX} checking for updates`)
199
+
200
+ let latestVersion: string
201
+ try {
202
+ latestVersion = await deps.fetchLatestVersion(PACKAGE_NAME)
203
+ }
204
+ catch (error) {
205
+ deps.warn(`${LOG_PREFIX} update check failed, continuing current version`)
206
+ if (error instanceof Error && error.message) {
207
+ deps.warn(`${LOG_PREFIX} ${error.message}`)
208
+ }
209
+ return null
210
+ }
211
+
212
+ if (!latestVersion || compareVersions(deps.version, latestVersion) >= 0) {
213
+ return null
214
+ }
215
+
216
+ deps.log(`${LOG_PREFIX} installing ${PACKAGE_NAME}@${latestVersion}`)
217
+ const installResult = deps.installVersion(PACKAGE_NAME, latestVersion)
218
+ if (!installResult.ok) {
219
+ deps.warn(`${LOG_PREFIX} update failed, continuing current version`)
220
+ if (installResult.userMessage) {
221
+ deps.warn(`${LOG_PREFIX} ${installResult.userMessage}`)
222
+ }
223
+ return null
224
+ }
225
+
226
+ deps.log(`${LOG_PREFIX} restarting into updated version`)
227
+ return "startup_update"
228
+ }
229
+
230
+ export async function runCli(argv: string[], deps: CliRuntimeDeps): Promise<CliRunResult> {
231
+ const parsedArgs = parseArgs(argv)
232
+ if (parsedArgs.kind === "version") {
233
+ deps.log(deps.version)
234
+ return { kind: "exited", code: 0 }
235
+ }
236
+ if (parsedArgs.kind === "help") {
237
+ printHelp()
238
+ return { kind: "exited", code: 0 }
239
+ }
240
+
241
+ if (compareVersions(deps.bunVersion, MINIMUM_BUN_VERSION) < 0) {
242
+ deps.warn(`${LOG_PREFIX} Bun ${MINIMUM_BUN_VERSION}+ is required for the embedded terminal. Current Bun: ${deps.bunVersion}`)
243
+ return { kind: "exited", code: 1 }
244
+ }
245
+
246
+ const shouldRestart = await maybeSelfUpdate(argv, deps)
247
+ if (shouldRestart !== null) {
248
+ return { kind: "restarting", reason: shouldRestart }
249
+ }
250
+
251
+ const { port, stop } = await deps.startServer({
252
+ ...parsedArgs.options,
253
+ onMigrationProgress: deps.log,
254
+ update: {
255
+ version: deps.version,
256
+ fetchLatestVersion: deps.fetchLatestVersion,
257
+ installVersion: deps.installVersion,
258
+ argv,
259
+ command: CLI_COMMAND,
260
+ },
261
+ })
262
+ const bindHost = parsedArgs.options.host
263
+ const displayHost = parsedArgs.options.share || bindHost === "127.0.0.1" || bindHost === "0.0.0.0" ? "localhost" : bindHost
264
+ const launchUrl = `http://${displayHost}:${port}`
265
+ let shareTunnelStop: (() => void) | null = null
266
+
267
+ deps.log(`${LOG_PREFIX} listening on http://${bindHost}:${port}`)
268
+ deps.log(`${LOG_PREFIX} data dir: ${getDataDirDisplay()}`)
269
+
270
+ const suppressOpenBrowser = process.env[CLI_SUPPRESS_OPEN_ONCE_ENV_VAR] === "1"
271
+ if (parsedArgs.options.share) {
272
+ try {
273
+ const shareTunnel = await (deps.startShareTunnel ?? ((localUrl) => startShareTunnel(localUrl, {
274
+ log: (message) => deps.log(`${LOG_PREFIX} ${message}`),
275
+ })))(launchUrl)
276
+ shareTunnelStop = shareTunnel.stop
277
+ await logShareDetails(deps.log, shareTunnel.publicUrl, launchUrl, deps.renderShareQr ?? renderTerminalQr)
278
+ } catch (error) {
279
+ await stop()
280
+ deps.warn(`${LOG_PREFIX} failed to start Cloudflare share tunnel`)
281
+ if (error instanceof Error && error.message) {
282
+ deps.warn(`${LOG_PREFIX} ${error.message}`)
283
+ }
284
+ return { kind: "exited", code: 1 }
285
+ }
286
+ }
287
+
288
+ if (parsedArgs.options.openBrowser && !parsedArgs.options.share && !suppressOpenBrowser) {
289
+ deps.openUrl(launchUrl)
290
+ }
291
+
292
+ return {
293
+ kind: "started",
294
+ stop: async () => {
295
+ shareTunnelStop?.()
296
+ await stop()
297
+ },
298
+ }
299
+ }
300
+
301
+ export function openUrl(url: string) {
302
+ const platform = process.platform
303
+ if (platform === "darwin") {
304
+ spawnDetached("open", [url])
305
+ } else if (platform === "win32") {
306
+ spawnDetached("cmd", ["/c", "start", "", url])
307
+ } else {
308
+ spawnDetached("xdg-open", [url])
309
+ }
310
+ console.log(`${LOG_PREFIX} opened in default browser`)
311
+ }
312
+
313
+ export async function fetchLatestPackageVersion(packageName: string) {
314
+ const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`)
315
+ if (!response.ok) {
316
+ throw new Error(`registry returned ${response.status}`)
317
+ }
318
+
319
+ const payload = await response.json() as { version?: unknown }
320
+ if (typeof payload.version !== "string" || !payload.version.trim()) {
321
+ throw new Error("registry response did not include a version")
322
+ }
323
+
324
+ return payload.version
325
+ }
326
+
327
+ export function classifyInstallVersionFailure(output: string): UpdateInstallAttemptResult {
328
+ const normalizedOutput = output.trim()
329
+ if (/No version matching .* found|failed to resolve/i.test(normalizedOutput)) {
330
+ return {
331
+ ok: false,
332
+ errorCode: "version_not_live_yet",
333
+ userTitle: "Update not live yet",
334
+ userMessage: "This update is still propagating. Try again in a few minutes.",
335
+ }
336
+ }
337
+
338
+ return {
339
+ ok: false,
340
+ errorCode: "install_failed",
341
+ userTitle: "Update failed",
342
+ userMessage: `${APP_NAME} could not install the update. Try again later.`,
343
+ }
344
+ }
345
+
346
+ export function installPackageVersion(packageName: string, version: string) {
347
+ if (!hasCommand("bun")) {
348
+ return {
349
+ ok: false,
350
+ errorCode: "command_missing",
351
+ userTitle: "Bun not found",
352
+ userMessage: `${APP_NAME} could not find Bun to install the update.`,
353
+ } satisfies UpdateInstallAttemptResult
354
+ }
355
+
356
+ const result = spawnSync("bun", ["install", "-g", `${packageName}@${version}`], {
357
+ stdio: ["ignore", "pipe", "pipe"],
358
+ encoding: "utf8",
359
+ })
360
+ const stdout = result.stdout ?? ""
361
+ const stderr = result.stderr ?? ""
362
+ if (stdout) process.stdout.write(stdout)
363
+ if (stderr) process.stderr.write(stderr)
364
+ if (result.status === 0) {
365
+ return {
366
+ ok: true,
367
+ errorCode: null,
368
+ userTitle: null,
369
+ userMessage: null,
370
+ } satisfies UpdateInstallAttemptResult
371
+ }
372
+
373
+ return classifyInstallVersionFailure(`${stdout}\n${stderr}`)
374
+ }
@@ -0,0 +1,81 @@
1
+ import process from "node:process"
2
+ import { spawn } from "node:child_process"
3
+ import { CLI_COMMAND, LOG_PREFIX } from "../shared/branding"
4
+ import {
5
+ CLI_CHILD_ARGS_ENV_VAR,
6
+ CLI_CHILD_COMMAND_ENV_VAR,
7
+ CLI_CHILD_MODE,
8
+ CLI_CHILD_MODE_ENV_VAR,
9
+ CLI_SUPPRESS_OPEN_ONCE_ENV_VAR,
10
+ isUiUpdateRestart,
11
+ parseChildArgsEnv,
12
+ shouldRestartCliProcess,
13
+ } from "./restart"
14
+
15
+ interface ChildExit {
16
+ code: number | null
17
+ signal: NodeJS.Signals | null
18
+ }
19
+
20
+ function getChildProcessSpec() {
21
+ const command = process.env[CLI_CHILD_COMMAND_ENV_VAR] || CLI_COMMAND
22
+ const args = parseChildArgsEnv(process.env[CLI_CHILD_ARGS_ENV_VAR])
23
+ return { command, args }
24
+ }
25
+
26
+ function spawnChild(argv: string[]) {
27
+ const childProcess = getChildProcessSpec()
28
+ const suppressOpenThisChild = suppressOpenOnNextChild
29
+ suppressOpenOnNextChild = false
30
+ return new Promise<ChildExit>((resolve, reject) => {
31
+ const child = spawn(childProcess.command, [...childProcess.args, ...argv], {
32
+ stdio: "inherit",
33
+ env: {
34
+ ...process.env,
35
+ [CLI_CHILD_MODE_ENV_VAR]: CLI_CHILD_MODE,
36
+ ...(suppressOpenThisChild ? { [CLI_SUPPRESS_OPEN_ONCE_ENV_VAR]: "1" } : {}),
37
+ },
38
+ })
39
+
40
+ const forwardSignal = (signal: NodeJS.Signals) => {
41
+ if (child.exitCode !== null) return
42
+ child.kill(signal)
43
+ }
44
+
45
+ const onSigint = () => {
46
+ forwardSignal("SIGINT")
47
+ }
48
+ const onSigterm = () => {
49
+ forwardSignal("SIGTERM")
50
+ }
51
+
52
+ process.on("SIGINT", onSigint)
53
+ process.on("SIGTERM", onSigterm)
54
+
55
+ child.once("error", (error) => {
56
+ process.off("SIGINT", onSigint)
57
+ process.off("SIGTERM", onSigterm)
58
+ reject(error)
59
+ })
60
+
61
+ child.once("exit", (code, signal) => {
62
+ process.off("SIGINT", onSigint)
63
+ process.off("SIGTERM", onSigterm)
64
+ resolve({ code, signal })
65
+ })
66
+ })
67
+ }
68
+
69
+ const argv = process.argv.slice(2)
70
+ let suppressOpenOnNextChild = false
71
+
72
+ while (true) {
73
+ const result = await spawnChild(argv)
74
+ if (shouldRestartCliProcess(result.code, result.signal)) {
75
+ suppressOpenOnNextChild = isUiUpdateRestart(result.code, result.signal)
76
+ console.log(`${LOG_PREFIX} supervisor restarting ${CLI_COMMAND} in the same terminal session`)
77
+ continue
78
+ }
79
+
80
+ process.exit(result.code ?? (result.signal ? 1 : 0))
81
+ }
@@ -0,0 +1,78 @@
1
+ import process from "node:process"
2
+ import { LOG_PREFIX } from "../shared/branding"
3
+ import { installConsoleTee } from "../shared/log-sink"
4
+
5
+ // Observability (decision 0012): tee console output to VictoriaLogs when
6
+ // VICTORIALOGS_URL is set; no-op otherwise. Installed first so all startup
7
+ // logs are captured.
8
+ installConsoleTee(process.env, "server")
9
+
10
+ // Safety net: prevent stray unhandled rejections from crashing the server.
11
+ // Root causes should still be fixed, but this prevents cascading process death
12
+ // when a child process (e.g., Codex, vendored rg) dies unexpectedly.
13
+ process.on("unhandledRejection", (reason) => {
14
+ const message = reason instanceof Error ? reason.message : String(reason)
15
+ console.warn(LOG_PREFIX, "unhandled rejection (swallowed):", message)
16
+ })
17
+ import {
18
+ fetchLatestPackageVersion,
19
+ installPackageVersion,
20
+ openUrl,
21
+ runCli,
22
+ } from "./cli-runtime"
23
+ import { CLI_STARTUP_UPDATE_RESTART_EXIT_CODE, CLI_UI_UPDATE_RESTART_EXIT_CODE } from "./restart"
24
+ import { startServer } from "./server"
25
+
26
+ // Read version from package.json at the package root
27
+ const pkg = await Bun.file(new URL("../../package.json", import.meta.url)).json()
28
+ const VERSION: string = pkg.version ?? "0.0.0"
29
+
30
+ const argv = process.argv.slice(2)
31
+ let resolveExitAction: ((action: "ui_restart" | "exit") => void) | null = null
32
+
33
+ const result = await runCli(argv, {
34
+ version: VERSION,
35
+ bunVersion: Bun.version,
36
+ startServer: async (options) => {
37
+ const started = await startServer(options)
38
+ if (started.updateManager && options.update) {
39
+ started.updateManager.onChange((snapshot) => {
40
+ if (snapshot.status !== "restart_pending") return
41
+ console.log(`${LOG_PREFIX} update installed, shutting down current process for restart`)
42
+ resolveExitAction?.("ui_restart")
43
+ })
44
+ }
45
+
46
+ return started
47
+ },
48
+ fetchLatestVersion: fetchLatestPackageVersion,
49
+ installVersion: installPackageVersion,
50
+ openUrl,
51
+ log: console.log,
52
+ warn: console.warn,
53
+ })
54
+
55
+ if (result.kind === "exited") {
56
+ process.exit(result.code)
57
+ }
58
+
59
+ if (result.kind === "restarting") {
60
+ process.exit(result.reason === "startup_update" ? CLI_STARTUP_UPDATE_RESTART_EXIT_CODE : CLI_UI_UPDATE_RESTART_EXIT_CODE)
61
+ }
62
+
63
+ const exitAction = await new Promise<"ui_restart" | "exit">((resolve) => {
64
+ resolveExitAction = resolve
65
+
66
+ const shutdown = () => {
67
+ resolve("exit")
68
+ }
69
+
70
+ process.once("SIGINT", shutdown)
71
+ process.once("SIGTERM", shutdown)
72
+ })
73
+
74
+ await result.stop()
75
+ if (exitAction === "ui_restart") {
76
+ console.log(`${LOG_PREFIX} current process stopped, handing restart back to supervisor`)
77
+ }
78
+ process.exit(exitAction === "ui_restart" ? CLI_UI_UPDATE_RESTART_EXIT_CODE : 0)
@@ -0,0 +1,74 @@
1
+ import { describe, test, expect } from "bun:test"
2
+ import { sanitizeClientLogs, buildNdjson, forwardClientLogs } from "./client-log-forwarder"
3
+
4
+ describe("sanitizeClientLogs", () => {
5
+ test("keeps valid records and tags source/component", () => {
6
+ const out = sanitizeClientLogs({
7
+ logs: [{ level: "warn", msg: "hi", ts: "2026-05-27T10:00:00.000Z", url: "https://a/b" }],
8
+ })
9
+ expect(out).toEqual([
10
+ { _time: "2026-05-27T10:00:00.000Z", _msg: "hi", level: "warn", source: "frontend", component: "browser", url: "https://a/b" },
11
+ ])
12
+ })
13
+
14
+ test("drops entries with no message; defaults bad level to info; backfills bad ts", () => {
15
+ const now = new Date("2026-05-27T12:00:00.000Z")
16
+ const out = sanitizeClientLogs({ logs: [{ msg: "x", level: "nope", ts: "garbage" }, { level: "error" }] }, now)
17
+ expect(out).toHaveLength(1)
18
+ expect(out[0].level).toBe("info")
19
+ expect(out[0]._time).toBe("2026-05-27T12:00:00.000Z")
20
+ })
21
+
22
+ test("non-array / non-object bodies → empty", () => {
23
+ expect(sanitizeClientLogs(null)).toEqual([])
24
+ expect(sanitizeClientLogs({ logs: "x" })).toEqual([])
25
+ expect(sanitizeClientLogs({})).toEqual([])
26
+ })
27
+
28
+ test("caps record count and truncates long messages", () => {
29
+ const logs = Array.from({ length: 1500 }, () => ({ msg: "a".repeat(20000) }))
30
+ const out = sanitizeClientLogs({ logs })
31
+ expect(out.length).toBe(1000)
32
+ expect(out[0]._msg.length).toBe(8192)
33
+ })
34
+ })
35
+
36
+ describe("buildNdjson", () => {
37
+ test("newline-joins one JSON object per record", () => {
38
+ const recs = sanitizeClientLogs({ logs: [{ msg: "a" }, { msg: "b" }] })
39
+ const out = buildNdjson(recs)
40
+ expect(out.split("\n")).toHaveLength(2)
41
+ expect(JSON.parse(out.split("\n")[1])._msg).toBe("b")
42
+ })
43
+ })
44
+
45
+ describe("forwardClientLogs", () => {
46
+ test("ships ndjson to VL when VICTORIALOGS_URL set", async () => {
47
+ const calls: { url: string; body: string }[] = []
48
+ const fetchFn = (async (u: string, init?: RequestInit) => {
49
+ calls.push({ url: u, body: String(init?.body) }); return new Response(null, { status: 204 })
50
+ }) as unknown as typeof fetch
51
+ const n = await forwardClientLogs(
52
+ { logs: [{ msg: "one", level: "info" }] },
53
+ { VICTORIALOGS_URL: "http://127.0.0.1:9428" } as unknown as NodeJS.ProcessEnv,
54
+ fetchFn,
55
+ )
56
+ expect(n).toBe(1)
57
+ expect(calls[0].url).toBe("http://127.0.0.1:9428/insert/jsonline?_stream_fields=source,component")
58
+ })
59
+
60
+ test("no VL url → counts but does not ship", async () => {
61
+ let shipped = 0
62
+ const fetchFn = (async () => { shipped++; return new Response(null) }) as unknown as typeof fetch
63
+ const n = await forwardClientLogs({ logs: [{ msg: "x" }] }, {} as NodeJS.ProcessEnv, fetchFn)
64
+ expect(n).toBe(1)
65
+ expect(shipped).toBe(0)
66
+ })
67
+
68
+ test("fetch failure is swallowed", async () => {
69
+ const fetchFn = (async () => { throw new Error("down") }) as unknown as typeof fetch
70
+ await expect(
71
+ forwardClientLogs({ logs: [{ msg: "x" }] }, { VICTORIALOGS_URL: "http://x" } as unknown as NodeJS.ProcessEnv, fetchFn),
72
+ ).resolves.toBe(1)
73
+ })
74
+ })
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Forward browser-shipped logs to VictoriaLogs (decision 0012).
3
+ *
4
+ * The frontend POSTs batches to `/api/logs`; this module sanitizes them
5
+ * (caps count + truncates message size, so a misbehaving/malicious client
6
+ * can't flood the store) and ships them to VictoriaLogs as `source=frontend`.
7
+ * VictoriaLogs stays localhost-only — the browser never reaches it directly.
8
+ *
9
+ * Fire-and-forget: VictoriaLogs being down or `VICTORIALOGS_URL` being unset
10
+ * must never make `/api/logs` fail (the client should never error on logging).
11
+ */
12
+
13
+ const MAX_RECORDS = 1000
14
+ const MAX_MSG_LEN = 8192
15
+ const LEVELS = new Set(["debug", "info", "warn", "error"])
16
+
17
+ export interface SanitizedClientLog {
18
+ _time: string
19
+ _msg: string
20
+ level: string
21
+ source: "frontend"
22
+ component: "browser"
23
+ url: string
24
+ }
25
+
26
+ /**
27
+ * Validate + clamp a raw `/api/logs` body into VL records. Pure.
28
+ * Drops malformed entries; never throws.
29
+ */
30
+ export function sanitizeClientLogs(body: unknown, now: Date = new Date()): SanitizedClientLog[] {
31
+ const logs = (body as { logs?: unknown })?.logs
32
+ if (!Array.isArray(logs)) return []
33
+ const out: SanitizedClientLog[] = []
34
+ for (const raw of logs.slice(0, MAX_RECORDS)) {
35
+ if (typeof raw !== "object" || raw === null) continue
36
+ const r = raw as Record<string, unknown>
37
+ const msg = typeof r.msg === "string" ? r.msg.slice(0, MAX_MSG_LEN) : ""
38
+ if (!msg) continue
39
+ const level = typeof r.level === "string" && LEVELS.has(r.level) ? r.level : "info"
40
+ const ts = typeof r.ts === "string" && !Number.isNaN(Date.parse(r.ts)) ? r.ts : now.toISOString()
41
+ const recUrl = typeof r.url === "string" ? r.url.slice(0, 2048) : ""
42
+ out.push({ _time: ts, _msg: msg, level, source: "frontend", component: "browser", url: recUrl })
43
+ }
44
+ return out
45
+ }
46
+
47
+ /** Newline-delimited JSON for VL `/insert/jsonline`. Pure. */
48
+ export function buildNdjson(records: SanitizedClientLog[]): string {
49
+ return records.map((r) => JSON.stringify(r)).join("\n")
50
+ }
51
+
52
+ /**
53
+ * Sanitize and ship a `/api/logs` body to VictoriaLogs. Returns the number of
54
+ * accepted records. Never throws; swallows network errors.
55
+ */
56
+ export async function forwardClientLogs(
57
+ body: unknown,
58
+ env: NodeJS.ProcessEnv = process.env,
59
+ fetchFn: typeof fetch = fetch,
60
+ ): Promise<number> {
61
+ const records = sanitizeClientLogs(body)
62
+ const url = env.VICTORIALOGS_URL?.trim()
63
+ if (records.length === 0 || !url) return records.length
64
+ const endpoint = `${url.replace(/\/$/, "")}/insert/jsonline?_stream_fields=source,component`
65
+ try {
66
+ await fetchFn(endpoint, {
67
+ method: "POST",
68
+ headers: { "Content-Type": "application/x-ndjson" },
69
+ body: buildNdjson(records),
70
+ })
71
+ } catch {
72
+ // observability must never break the ingest endpoint
73
+ }
74
+ return records.length
75
+ }