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,379 @@
1
+ import type { AgentProvider, IndependentWorkspace, ModelOptions, WorkspaceSummary, TranscriptEntry } from "../shared/types"
2
+ import type { WorkspaceTodo, WorkspaceClaim, WorkspaceWorktree, WorkspaceRule } from "../shared/workspace-types"
3
+ import type { AgentConfig, AgentConfigRecord } from "../shared/agent-config-types"
4
+ import type { ProviderProfile, ProviderProfileRecord, WorkspaceProfileOverride } from "../shared/profile-types"
5
+ import type { ExtensionPreference } from "../shared/extension-types"
6
+ import type { TeamMember, RunnerLabelRecord } from "../shared/runner-team-types"
7
+ import type { WorkflowRunState } from "../shared/workflow-types"
8
+ import type { SandboxRecord, SandboxHealthReport, ResourceLimits } from "../shared/sandbox-types"
9
+
10
+ export interface WorkspaceRecord extends WorkspaceSummary {
11
+ deletedAt?: number
12
+ }
13
+
14
+ export interface RepoRecord {
15
+ id: string
16
+ workspaceId: string
17
+ origin: string | null
18
+ localPath: string
19
+ label: string | null
20
+ status: "cloned" | "pending" | "error"
21
+ branch: string | null
22
+ createdAt: number
23
+ updatedAt: number
24
+ }
25
+
26
+ export interface ChatRecord {
27
+ id: string
28
+ workspaceId: string
29
+ repoId: string | null
30
+ title: string
31
+ createdAt: number
32
+ updatedAt: number
33
+ deletedAt?: number
34
+ unread: boolean
35
+ provider: AgentProvider | null
36
+ model?: string | null
37
+ planMode: boolean
38
+ sessionToken: string | null
39
+ lastMessageAt?: number
40
+ lastTurnOutcome: "success" | "failed" | "cancelled" | null
41
+ /** PR5: sticky runner pin. Absent on pre-PR5 chats → treated as no pin. */
42
+ runnerId?: string | null
43
+ }
44
+
45
+ export interface QueuedChatTurnRecord {
46
+ chatId: string
47
+ provider?: AgentProvider
48
+ content: string
49
+ model?: string
50
+ modelOptions?: ModelOptions
51
+ effort?: string
52
+ planMode?: boolean
53
+ updatedAt: number
54
+ }
55
+
56
+ export interface WorkspaceCoordinationState {
57
+ todos: Map<string, WorkspaceTodo>
58
+ claims: Map<string, WorkspaceClaim>
59
+ worktrees: Map<string, WorkspaceWorktree>
60
+ rules: Map<string, WorkspaceRule>
61
+ lastUpdated: string
62
+ }
63
+
64
+ export interface StoreState {
65
+ workspacesById: Map<string, WorkspaceRecord>
66
+ workspaceIdsByPath: Map<string, string>
67
+ independentWorkspacesById: Map<string, IndependentWorkspace>
68
+ chatsById: Map<string, ChatRecord>
69
+ queuedTurnsByChat: Map<string, QueuedChatTurnRecord>
70
+ coordinationByWorkspace: Map<string, WorkspaceCoordinationState>
71
+ agentConfigsByWorkspace: Map<string, Map<string, AgentConfigRecord>>
72
+ reposById: Map<string, RepoRecord>
73
+ reposByPath: Map<string, string>
74
+ workflowRunsByWorkspace: Map<string, Map<string, WorkflowRunState>>
75
+ sandboxByWorkspace: Map<string, SandboxRecord>
76
+ providerProfiles: Map<string, ProviderProfileRecord>
77
+ workspaceProfileOverrides: Map<string, Map<string, WorkspaceProfileOverride>>
78
+ extensionPreferences: Map<string, ExtensionPreference>
79
+ teamMembers: Map<string, TeamMember>
80
+ runnerLabels: Map<string, RunnerLabelRecord>
81
+ }
82
+
83
+ export function createEmptyCoordinationState(): WorkspaceCoordinationState {
84
+ return {
85
+ todos: new Map(),
86
+ claims: new Map(),
87
+ worktrees: new Map(),
88
+ rules: new Map(),
89
+ lastUpdated: new Date(0).toISOString(),
90
+ }
91
+ }
92
+
93
+ export interface SnapshotFile {
94
+ v: 2 | 3
95
+ generatedAt: number
96
+ workspaces: WorkspaceRecord[]
97
+ independentWorkspaces?: IndependentWorkspace[]
98
+ chats: ChatRecord[]
99
+ queuedTurns?: QueuedChatTurnRecord[]
100
+ messages?: Array<{ chatId: string; entries: TranscriptEntry[] }>
101
+ coordination?: Array<{ workspaceId: string; todos: WorkspaceTodo[]; claims: WorkspaceClaim[]; worktrees: WorkspaceWorktree[]; rules: WorkspaceRule[] }>
102
+ agentConfigs?: Array<{ workspaceId: string; records: AgentConfigRecord[] }>
103
+ repos?: RepoRecord[]
104
+ workflowRuns?: Array<{ workspaceId: string; runs: WorkflowRunState[] }>
105
+ sandboxes?: SandboxRecord[]
106
+ providerProfiles?: ProviderProfileRecord[]
107
+ workspaceProfileOverrides?: WorkspaceProfileOverride[]
108
+ extensionPreferences?: ExtensionPreference[]
109
+ teamMembers?: TeamMember[]
110
+ runnerLabels?: RunnerLabelRecord[]
111
+ }
112
+
113
+ export type WorkspaceEvent = {
114
+ v: 3
115
+ type: "workspace_opened"
116
+ timestamp: number
117
+ workspaceId: string
118
+ localPath: string
119
+ title: string
120
+ } | {
121
+ v: 3
122
+ type: "workspace_removed"
123
+ timestamp: number
124
+ workspaceId: string
125
+ } | {
126
+ v: 3
127
+ type: "independent_workspace_created"
128
+ timestamp: number
129
+ workspaceId: string
130
+ name: string
131
+ } | {
132
+ v: 3
133
+ type: "independent_workspace_deleted"
134
+ timestamp: number
135
+ workspaceId: string
136
+ } | {
137
+ v: 3
138
+ type: "independent_workspace_renamed"
139
+ timestamp: number
140
+ workspaceId: string
141
+ name: string
142
+ } | {
143
+ v: 3
144
+ type: "independent_workspace_pin_toggled"
145
+ timestamp: number
146
+ workspaceId: string
147
+ pinned: boolean
148
+ } | {
149
+ v: 3
150
+ type: "independent_workspaces_reordered"
151
+ timestamp: number
152
+ orderedWorkspaceIds: string[]
153
+ }
154
+
155
+ export type ChatEvent =
156
+ | {
157
+ v: 3
158
+ type: "chat_created"
159
+ timestamp: number
160
+ chatId: string
161
+ workspaceId: string
162
+ title: string
163
+ repoId?: string
164
+ }
165
+ | {
166
+ v: 3
167
+ type: "chat_renamed"
168
+ timestamp: number
169
+ chatId: string
170
+ title: string
171
+ }
172
+ | {
173
+ v: 3
174
+ type: "chat_deleted"
175
+ timestamp: number
176
+ chatId: string
177
+ }
178
+ | {
179
+ v: 3
180
+ type: "chat_provider_set"
181
+ timestamp: number
182
+ chatId: string
183
+ provider: AgentProvider
184
+ }
185
+ | {
186
+ v: 3
187
+ type: "chat_model_set"
188
+ timestamp: number
189
+ chatId: string
190
+ model: string | null
191
+ }
192
+ | {
193
+ v: 3
194
+ type: "chat_plan_mode_set"
195
+ timestamp: number
196
+ chatId: string
197
+ planMode: boolean
198
+ }
199
+ | {
200
+ v: 3
201
+ type: "chat_read_state_set"
202
+ timestamp: number
203
+ chatId: string
204
+ unread: boolean
205
+ }
206
+ | {
207
+ v: 3
208
+ type: "chat_runner_set"
209
+ timestamp: number
210
+ chatId: string
211
+ runnerId: string | null
212
+ }
213
+
214
+ export type MessageEvent = {
215
+ v: 3
216
+ type: "message_appended"
217
+ timestamp: number
218
+ chatId: string
219
+ entry: TranscriptEntry
220
+ }
221
+
222
+ export type TurnEvent =
223
+ | {
224
+ v: 3
225
+ type: "turn_started"
226
+ timestamp: number
227
+ chatId: string
228
+ }
229
+ | {
230
+ v: 3
231
+ type: "turn_finished"
232
+ timestamp: number
233
+ chatId: string
234
+ }
235
+ | {
236
+ v: 3
237
+ type: "turn_failed"
238
+ timestamp: number
239
+ chatId: string
240
+ error: string
241
+ }
242
+ | {
243
+ v: 3
244
+ type: "turn_cancelled"
245
+ timestamp: number
246
+ chatId: string
247
+ }
248
+ | {
249
+ v: 3
250
+ type: "session_token_set"
251
+ timestamp: number
252
+ chatId: string
253
+ sessionToken: string | null
254
+ }
255
+ | {
256
+ v: 3
257
+ type: "chat_turn_queued"
258
+ timestamp: number
259
+ chatId: string
260
+ provider?: AgentProvider
261
+ content: string
262
+ model?: string
263
+ modelOptions?: ModelOptions
264
+ effort?: string
265
+ planMode?: boolean
266
+ }
267
+ | {
268
+ v: 3
269
+ type: "chat_queued_turn_cleared"
270
+ timestamp: number
271
+ chatId: string
272
+ }
273
+ | {
274
+ v: 3
275
+ type: "delegation_initiated"
276
+ timestamp: number
277
+ delegationId: string
278
+ parentChatId: string
279
+ childChatId: string
280
+ workspaceId: string
281
+ mode: "blocking" | "background"
282
+ resume: "immediate" | "gate"
283
+ }
284
+ | {
285
+ v: 3
286
+ type: "delegation_completed"
287
+ timestamp: number
288
+ delegationId: string
289
+ parentChatId: string
290
+ childChatId: string
291
+ workspaceId: string
292
+ outcome: "completed" | "failed" | "orphaned" | "stale"
293
+ }
294
+
295
+ export type CoordinationEvent =
296
+ | { v: 3; type: "todo_added"; timestamp: number; workspaceId: string; todoId: string; description: string; priority: "high" | "normal" | "low"; createdBy: string }
297
+ | { v: 3; type: "todo_claimed"; timestamp: number; workspaceId: string; todoId: string; claimedBy: string }
298
+ | { v: 3; type: "todo_completed"; timestamp: number; workspaceId: string; todoId: string; outputs: string[] }
299
+ | { v: 3; type: "todo_abandoned"; timestamp: number; workspaceId: string; todoId: string }
300
+ | { v: 3; type: "claim_created"; timestamp: number; workspaceId: string; claimId: string; intent: string; files: string[]; sessionId: string }
301
+ | { v: 3; type: "claim_released"; timestamp: number; workspaceId: string; claimId: string }
302
+ | { v: 3; type: "claim_conflict_detected"; timestamp: number; workspaceId: string; claimId: string; conflictsWith: string; overlappingFiles: string[] }
303
+ | { v: 3; type: "worktree_created"; timestamp: number; workspaceId: string; worktreeId: string; branch: string; baseBranch: string; path: string }
304
+ | { v: 3; type: "worktree_assigned"; timestamp: number; workspaceId: string; worktreeId: string; sessionId: string }
305
+ | { v: 3; type: "worktree_removed"; timestamp: number; workspaceId: string; worktreeId: string }
306
+ | { v: 3; type: "rule_set"; timestamp: number; workspaceId: string; ruleId: string; content: string; setBy: string }
307
+ | { v: 3; type: "rule_removed"; timestamp: number; workspaceId: string; ruleId: string }
308
+
309
+ export type RepoEvent =
310
+ | { v: 3; type: "repo_added"; timestamp: number; id: string; workspaceId: string; localPath: string; origin: string | null; label: string | null; branch: string | null }
311
+ | { v: 3; type: "repo_clone_started"; timestamp: number; id: string; workspaceId: string; origin: string; targetPath: string; label: string | null }
312
+ | { v: 3; type: "repo_cloned"; timestamp: number; id: string; localPath: string; branch: string | null }
313
+ | { v: 3; type: "repo_clone_failed"; timestamp: number; id: string; error: string }
314
+ | { v: 3; type: "repo_removed"; timestamp: number; id: string; workspaceId: string }
315
+ | { v: 3; type: "repo_label_updated"; timestamp: number; id: string; label: string }
316
+
317
+ export type AgentConfigEvent =
318
+ | { v: 3; type: "agent_config_saved"; timestamp: number; workspaceId: string; agentId: string; config: AgentConfig }
319
+ | { v: 3; type: "agent_config_committed"; timestamp: number; workspaceId: string; agentId: string; commitHash: string }
320
+ | { v: 3; type: "agent_config_removed"; timestamp: number; workspaceId: string; agentId: string }
321
+
322
+ export type WorkflowEvent =
323
+ | { v: 3; type: "workflow_started"; timestamp: number; runId: string; workflowId: string; workspaceId: string; targetRepoIds: string[]; triggeredBy: string }
324
+ | { v: 3; type: "workflow_step_started"; timestamp: number; runId: string; workspaceId: string; stepIndex: number; mcp_tool: string; repoId?: string }
325
+ | { v: 3; type: "workflow_step_completed"; timestamp: number; runId: string; workspaceId: string; stepIndex: number; repoId?: string; output: string }
326
+ | { v: 3; type: "workflow_step_failed"; timestamp: number; runId: string; workspaceId: string; stepIndex: number; repoId?: string; error: string }
327
+ | { v: 3; type: "workflow_completed"; timestamp: number; runId: string; workspaceId: string }
328
+ | { v: 3; type: "workflow_failed"; timestamp: number; runId: string; workspaceId: string; error: string; failedStep: number }
329
+ | { v: 3; type: "workflow_cancelled"; timestamp: number; runId: string; workspaceId: string }
330
+
331
+ export type SandboxEvent =
332
+ | { v: 3; type: "sandbox_created"; timestamp: number; id: string; workspaceId: string; resourceLimits: ResourceLimits }
333
+ | { v: 3; type: "sandbox_started"; timestamp: number; id: string; containerId: string; natsUrl: string }
334
+ | { v: 3; type: "sandbox_stopped"; timestamp: number; id: string; reason: string }
335
+ | { v: 3; type: "sandbox_destroyed"; timestamp: number; id: string }
336
+ | { v: 3; type: "sandbox_error"; timestamp: number; id: string; error: string }
337
+ | { v: 3; type: "sandbox_health_updated"; timestamp: number; id: string; health: SandboxHealthReport }
338
+
339
+ export type ProviderProfileEvent =
340
+ | { v: 3; type: "provider_profile_saved"; timestamp: number; profileId: string; profile: ProviderProfile }
341
+ | { v: 3; type: "provider_profile_removed"; timestamp: number; profileId: string }
342
+ | { v: 3; type: "workspace_profile_override_set"; timestamp: number; workspaceId: string; profileId: string; overrides: Partial<Omit<ProviderProfile, "id" | "provider">> }
343
+ | { v: 3; type: "workspace_profile_override_removed"; timestamp: number; workspaceId: string; profileId: string }
344
+
345
+ export type ExtensionPreferenceEvent =
346
+ | { v: 3; type: "extension_preference_set"; timestamp: number; extensionId: string; enabled: boolean }
347
+
348
+ export type RunnerTeamEvent =
349
+ | { v: 3; type: "team_member_saved"; timestamp: number; memberId: string; member: TeamMember }
350
+ | { v: 3; type: "team_member_removed"; timestamp: number; memberId: string }
351
+ | { v: 3; type: "runner_label_set"; timestamp: number; runnerId: string; name: string | null; memberId: string | null }
352
+ | { v: 3; type: "runner_label_removed"; timestamp: number; runnerId: string }
353
+
354
+ export type StoreEvent = WorkspaceEvent | ChatEvent | MessageEvent | TurnEvent | CoordinationEvent | RepoEvent | AgentConfigEvent | WorkflowEvent | SandboxEvent | ProviderProfileEvent | ExtensionPreferenceEvent | RunnerTeamEvent
355
+
356
+ export function createEmptyState(): StoreState {
357
+ return {
358
+ workspacesById: new Map(),
359
+ workspaceIdsByPath: new Map(),
360
+ independentWorkspacesById: new Map(),
361
+ chatsById: new Map(),
362
+ queuedTurnsByChat: new Map(),
363
+ coordinationByWorkspace: new Map(),
364
+ agentConfigsByWorkspace: new Map(),
365
+ reposById: new Map(),
366
+ reposByPath: new Map(),
367
+ workflowRunsByWorkspace: new Map(),
368
+ sandboxByWorkspace: new Map(),
369
+ providerProfiles: new Map(),
370
+ workspaceProfileOverrides: new Map(),
371
+ extensionPreferences: new Map(),
372
+ teamMembers: new Map(),
373
+ runnerLabels: new Map(),
374
+ }
375
+ }
376
+
377
+ export function cloneTranscriptEntries(entries: TranscriptEntry[]): TranscriptEntry[] {
378
+ return entries.map((entry) => ({ ...entry }))
379
+ }
@@ -0,0 +1,183 @@
1
+ import { describe, test, expect, afterEach } from "bun:test"
2
+ import { mkdtemp, rm, mkdir } from "node:fs/promises"
3
+ import { tmpdir } from "node:os"
4
+ import path from "node:path"
5
+ import { createExtensionRouter } from "./extension-router"
6
+ import type { ServerExtension, DetectionResult } from "../shared/extension-types"
7
+
8
+ let tempDirs: string[] = []
9
+
10
+ afterEach(async () => {
11
+ for (const d of tempDirs) await rm(d, { recursive: true, force: true })
12
+ tempDirs = []
13
+ })
14
+
15
+ async function makeTempDir(): Promise<string> {
16
+ const dir = await mkdtemp(path.join(tmpdir(), "ext-router-test-"))
17
+ tempDirs.push(dir)
18
+ return dir
19
+ }
20
+
21
+ /** Minimal c3 extension stub for testing */
22
+ function makeC3Extension(overrides?: Partial<ServerExtension>): ServerExtension {
23
+ return {
24
+ id: "c3",
25
+ name: "C3 Docs",
26
+ icon: "book",
27
+ detect: [".c3/"],
28
+ routes(ctx) {
29
+ return [
30
+ {
31
+ method: "GET",
32
+ path: "/list",
33
+ handler: async (_req, _params) =>
34
+ new Response(JSON.stringify({ items: [], projectPath: ctx.projectPath }), {
35
+ headers: { "Content-Type": "application/json" },
36
+ }),
37
+ },
38
+ {
39
+ method: "GET",
40
+ path: "/read/:id",
41
+ handler: async (_req, params) =>
42
+ new Response(JSON.stringify({ id: params.id, projectPath: ctx.projectPath }), {
43
+ headers: { "Content-Type": "application/json" },
44
+ }),
45
+ },
46
+ {
47
+ method: "POST",
48
+ path: "/write",
49
+ handler: async (req, _params) => {
50
+ const body = await req.json()
51
+ return new Response(JSON.stringify({ ok: true, body }), {
52
+ headers: { "Content-Type": "application/json" },
53
+ })
54
+ },
55
+ },
56
+ ]
57
+ },
58
+ ...overrides,
59
+ }
60
+ }
61
+
62
+ describe("extension-router", () => {
63
+ describe("GET /api/ext/detect", () => {
64
+ test("detects extensions when probe files exist", async () => {
65
+ const dir = await makeTempDir()
66
+ await mkdir(path.join(dir, ".c3"), { recursive: true })
67
+
68
+ const router = createExtensionRouter([makeC3Extension()])
69
+ const req = new Request(`http://localhost/api/ext/detect?projectPath=${encodeURIComponent(dir)}`)
70
+ const res = await router(req)
71
+
72
+ expect(res.status).toBe(200)
73
+ const body: DetectionResult[] = await res.json()
74
+ expect(body).toHaveLength(1)
75
+ expect(body[0].extensionId).toBe("c3")
76
+ expect(body[0].detected).toBe(true)
77
+ })
78
+
79
+ test("returns detected:false for empty directory", async () => {
80
+ const dir = await makeTempDir()
81
+
82
+ const router = createExtensionRouter([makeC3Extension()])
83
+ const req = new Request(`http://localhost/api/ext/detect?projectPath=${encodeURIComponent(dir)}`)
84
+ const res = await router(req)
85
+
86
+ expect(res.status).toBe(200)
87
+ const body: DetectionResult[] = await res.json()
88
+ expect(body).toHaveLength(1)
89
+ expect(body[0].extensionId).toBe("c3")
90
+ expect(body[0].detected).toBe(false)
91
+ })
92
+
93
+ test("returns 400 when projectPath is missing", async () => {
94
+ const router = createExtensionRouter([makeC3Extension()])
95
+ const req = new Request("http://localhost/api/ext/detect")
96
+ const res = await router(req)
97
+
98
+ expect(res.status).toBe(400)
99
+ const body = await res.json()
100
+ expect(body.error).toBeDefined()
101
+ })
102
+ })
103
+
104
+ describe("extension route dispatch", () => {
105
+ test("GET dispatches to correct extension handler", async () => {
106
+ const dir = await makeTempDir()
107
+ const router = createExtensionRouter([makeC3Extension()])
108
+ const req = new Request(`http://localhost/api/ext/c3/list?projectPath=${encodeURIComponent(dir)}`)
109
+ const res = await router(req)
110
+
111
+ expect(res.status).toBe(200)
112
+ const body = await res.json()
113
+ expect(body.items).toEqual([])
114
+ expect(body.projectPath).toBe(dir)
115
+ })
116
+
117
+ test("GET with path params dispatches correctly", async () => {
118
+ const dir = await makeTempDir()
119
+ const router = createExtensionRouter([makeC3Extension()])
120
+ const req = new Request(`http://localhost/api/ext/c3/read/c3-101?projectPath=${encodeURIComponent(dir)}`)
121
+ const res = await router(req)
122
+
123
+ expect(res.status).toBe(200)
124
+ const body = await res.json()
125
+ expect(body.id).toBe("c3-101")
126
+ expect(body.projectPath).toBe(dir)
127
+ })
128
+
129
+ test("POST route match works", async () => {
130
+ const dir = await makeTempDir()
131
+ const router = createExtensionRouter([makeC3Extension()])
132
+ const req = new Request(`http://localhost/api/ext/c3/write?projectPath=${encodeURIComponent(dir)}`, {
133
+ method: "POST",
134
+ headers: { "Content-Type": "application/json" },
135
+ body: JSON.stringify({ content: "hello" }),
136
+ })
137
+ const res = await router(req)
138
+
139
+ expect(res.status).toBe(200)
140
+ const body = await res.json()
141
+ expect(body.ok).toBe(true)
142
+ expect(body.body.content).toBe("hello")
143
+ })
144
+
145
+ test("returns 404 for unknown extension ID", async () => {
146
+ const dir = await makeTempDir()
147
+ const router = createExtensionRouter([makeC3Extension()])
148
+ const req = new Request(`http://localhost/api/ext/unknown/list?projectPath=${encodeURIComponent(dir)}`)
149
+ const res = await router(req)
150
+
151
+ expect(res.status).toBe(404)
152
+ const body = await res.json()
153
+ expect(body.error).toBeDefined()
154
+ })
155
+
156
+ test("returns 400 when projectPath is missing on extension route", async () => {
157
+ const router = createExtensionRouter([makeC3Extension()])
158
+ const req = new Request("http://localhost/api/ext/c3/list")
159
+ const res = await router(req)
160
+
161
+ expect(res.status).toBe(400)
162
+ })
163
+
164
+ test("returns 404 for method mismatch (GET on POST-only route)", async () => {
165
+ const dir = await makeTempDir()
166
+ const router = createExtensionRouter([makeC3Extension()])
167
+ // /write is POST-only, try GET
168
+ const req = new Request(`http://localhost/api/ext/c3/write?projectPath=${encodeURIComponent(dir)}`)
169
+ const res = await router(req)
170
+
171
+ expect(res.status).toBe(404)
172
+ })
173
+
174
+ test("returns 404 for route not defined on extension", async () => {
175
+ const dir = await makeTempDir()
176
+ const router = createExtensionRouter([makeC3Extension()])
177
+ const req = new Request(`http://localhost/api/ext/c3/nonexistent?projectPath=${encodeURIComponent(dir)}`)
178
+ const res = await router(req)
179
+
180
+ expect(res.status).toBe(404)
181
+ })
182
+ })
183
+ })
@@ -0,0 +1,114 @@
1
+ import { exists } from "node:fs/promises"
2
+ import path from "node:path"
3
+ import type { ServerExtension, DetectionResult } from "../shared/extension-types"
4
+
5
+ function jsonResponse(data: unknown, status = 200): Response {
6
+ return new Response(JSON.stringify(data), {
7
+ status,
8
+ headers: { "Content-Type": "application/json" },
9
+ })
10
+ }
11
+
12
+ function errorResponse(error: string, code: number, detail?: string): Response {
13
+ return jsonResponse({ error, code, detail }, code)
14
+ }
15
+
16
+ /** Match a route pattern (e.g. `/read/:id`) against a request path (e.g. `/read/c3-101`) */
17
+ function matchRoute(
18
+ pattern: string,
19
+ requestPath: string,
20
+ ): Record<string, string> | null {
21
+ const patternParts = pattern.split("/").filter(Boolean)
22
+ const requestParts = requestPath.split("/").filter(Boolean)
23
+
24
+ if (patternParts.length !== requestParts.length) return null
25
+
26
+ const params: Record<string, string> = {}
27
+ for (let i = 0; i < patternParts.length; i++) {
28
+ const pat = patternParts[i]
29
+ const req = requestParts[i]
30
+ if (pat.startsWith(":")) {
31
+ params[pat.slice(1)] = req
32
+ } else if (pat !== req) {
33
+ return null
34
+ }
35
+ }
36
+ return params
37
+ }
38
+
39
+ async function detectExtension(
40
+ ext: ServerExtension,
41
+ projectPath: string,
42
+ ): Promise<DetectionResult> {
43
+ let detected = false
44
+ for (const probe of ext.detect) {
45
+ const probePath = path.join(projectPath, probe)
46
+ if (await exists(probePath)) {
47
+ detected = true
48
+ break
49
+ }
50
+ }
51
+ return {
52
+ extensionId: ext.id,
53
+ name: ext.name,
54
+ icon: ext.icon,
55
+ detected,
56
+ }
57
+ }
58
+
59
+ export function createExtensionRouter(
60
+ extensions: ServerExtension[],
61
+ ): (req: Request) => Promise<Response> {
62
+ const extensionMap = new Map<string, ServerExtension>()
63
+ for (const ext of extensions) {
64
+ extensionMap.set(ext.id, ext)
65
+ }
66
+
67
+ return async (req: Request): Promise<Response> => {
68
+ const url = new URL(req.url)
69
+ const fullPath = url.pathname.replace(/^\/api\/ext/, "")
70
+
71
+ // GET /api/ext/detect?projectPath=...
72
+ if (req.method === "GET" && fullPath === "/detect") {
73
+ const projectPath = url.searchParams.get("projectPath")
74
+ if (!projectPath) {
75
+ return errorResponse("Missing 'projectPath' query parameter", 400)
76
+ }
77
+
78
+ const results = await Promise.all(
79
+ extensions.map((ext) => detectExtension(ext, projectPath)),
80
+ )
81
+ return jsonResponse(results)
82
+ }
83
+
84
+ // Extension route dispatch: /api/ext/:extensionId/...
85
+ const segments = fullPath.split("/").filter(Boolean)
86
+ if (segments.length < 2) {
87
+ return errorResponse("Not found", 404)
88
+ }
89
+
90
+ const extensionId = segments[0]
91
+ const routePath = "/" + segments.slice(1).join("/")
92
+
93
+ const projectPath = url.searchParams.get("projectPath")
94
+ if (!projectPath) {
95
+ return errorResponse("Missing 'projectPath' query parameter", 400)
96
+ }
97
+
98
+ const ext = extensionMap.get(extensionId)
99
+ if (!ext) {
100
+ return errorResponse(`Extension '${extensionId}' not found`, 404)
101
+ }
102
+
103
+ const routes = ext.routes({ projectPath })
104
+ for (const route of routes) {
105
+ if (route.method !== req.method) continue
106
+ const params = matchRoute(route.path, routePath)
107
+ if (params !== null) {
108
+ return route.handler(req, params)
109
+ }
110
+ }
111
+
112
+ return errorResponse("Not found", 404)
113
+ }
114
+ }