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
package/package.json ADDED
@@ -0,0 +1,122 @@
1
+ {
2
+ "name": "airaknit",
3
+ "type": "module",
4
+ "version": "1.1.2-rc.9",
5
+ "description": "A playful web workbench for Claude Code and Codex",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "claude",
9
+ "claude-code",
10
+ "ai",
11
+ "chat",
12
+ "ui",
13
+ "agent",
14
+ "anthropic",
15
+ "bun",
16
+ "react"
17
+ ],
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/lagz0ne/tinkaria.git"
21
+ },
22
+ "bin": {
23
+ "airaknit": "bin/airaknit",
24
+ "airaknit-project": "bin/airaknit-project"
25
+ },
26
+ "packageManager": "bun@1.3.11",
27
+ "files": [
28
+ "bin/",
29
+ "src/nats/",
30
+ "src/runner/",
31
+ "src/server/",
32
+ "src/shared/",
33
+ "dist/client/"
34
+ ],
35
+ "engines": {
36
+ "bun": ">=1.3.5"
37
+ },
38
+ "scripts": {
39
+ "build": "vite build",
40
+ "check": "bunx @typescript/native-preview --noEmit -p tsconfig.json && vite build",
41
+ "lint:rules": "bash .git/hooks/pre-commit --all",
42
+ "dev": "bun run ./scripts/dev.ts",
43
+ "dev:client": "vite --host 0.0.0.0 --port 5174",
44
+ "dev:server": "bun run ./scripts/dev-server.ts --no-open --port 5175",
45
+ "install:dev": "bun install && bun run build && bun install -g .",
46
+ "start": "bun run ./src/server/cli.ts",
47
+ "test": "bun test",
48
+ "verify:journey": "bun run ./scripts/verify-journey.ts",
49
+ "copy:legacy-transcripts": "bun run ./scripts/copy-legacy-transcripts.ts",
50
+ "prepublishOnly": "vite build"
51
+ },
52
+ "dependencies": {
53
+ "@anthropic-ai/claude-agent-sdk": "^0.2.91",
54
+ "@chenglou/pretext": "^0.0.4",
55
+ "@lagz0ne/nats-embedded": "^0.3.1",
56
+ "@modelcontextprotocol/sdk": "^1.29.0",
57
+ "@nats-io/jetstream": "^3.3.1",
58
+ "@nats-io/jwt": "^0.0.10-5",
59
+ "@nats-io/kv": "^3.3.1",
60
+ "@nats-io/nats-core": "^3.3.1",
61
+ "@nats-io/transport-node": "^3.3.1",
62
+ "@radix-ui/react-alert-dialog": "^1.1.15",
63
+ "@radix-ui/react-context-menu": "^2.2.16",
64
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
65
+ "@radix-ui/react-select": "^2.2.6",
66
+ "@tanstack/react-virtual": "^3.13.23",
67
+ "@xterm/addon-fit": "^0.11.0",
68
+ "@xterm/addon-serialize": "^0.14.0",
69
+ "@xterm/addon-web-links": "^0.12.0",
70
+ "@xterm/headless": "^6.0.0",
71
+ "@xterm/xterm": "^6.0.0",
72
+ "cloudflared": "^0.7.1",
73
+ "default-shell": "^2.2.0",
74
+ "diff": "^8.0.4",
75
+ "file-type": "^22.0.1",
76
+ "js-yaml": "^4.1.1",
77
+ "mermaid": "^11.14.0",
78
+ "minimatch": "^10.2.5",
79
+ "qrcode": "^1.5.4",
80
+ "react-resizable-panels": "^4.9.0",
81
+ "shell-quote": "^1.8.4",
82
+ "sonner": "^2.0.7",
83
+ "sugar-high": "^1.0.0",
84
+ "web-push": "^3.6.7",
85
+ "zod": "^4.3.6"
86
+ },
87
+ "devDependencies": {
88
+ "@radix-ui/react-dialog": "^1.1.15",
89
+ "@radix-ui/react-popover": "^1.1.15",
90
+ "@radix-ui/react-tooltip": "^1.2.8",
91
+ "@resvg/resvg-js": "^2.6.2",
92
+ "@tailwindcss/postcss": "^4.2.2",
93
+ "@tailwindcss/typography": "^0.5.19",
94
+ "@types/bun": "^1.3.11",
95
+ "@types/diff": "^8.0.0",
96
+ "@types/js-yaml": "^4.0.9",
97
+ "@types/node": "^25.5.0",
98
+ "@types/qrcode": "^1.5.6",
99
+ "@types/react": "19.2.14",
100
+ "@types/react-dom": "19.2.3",
101
+ "@types/shell-quote": "^1.7.5",
102
+ "@types/web-push": "^3.6.4",
103
+ "@vitejs/plugin-react": "6.0.1",
104
+ "autoprefixer": "^10.4.27",
105
+ "class-variance-authority": "^0.7.1",
106
+ "clsx": "^2.1.1",
107
+ "lucide-react": "^1.7.0",
108
+ "png-to-ico": "^3.0.1",
109
+ "react": "19.2.4",
110
+ "react-dom": "19.2.4",
111
+ "react-markdown": "^10.1.0",
112
+ "react-router-dom": "^7.14.0",
113
+ "remark-gfm": "^4.0.1",
114
+ "sharp": "^0.34.5",
115
+ "streamdown": "^2.5.0",
116
+ "tailwind-merge": "^3.5.0",
117
+ "tailwindcss": "^4.2.2",
118
+ "typescript": "6.0.2",
119
+ "vite": "^8.0.3",
120
+ "zustand": "^5.0.12"
121
+ }
122
+ }
@@ -0,0 +1,93 @@
1
+ import { describe, test, expect } from "bun:test"
2
+ import { buildCalloutConfig, validateCalloutConfigShape } from "./callout-config"
3
+
4
+ const FAKE_ACCOUNT_KEY = "ACPJ3QKPKLBKL6X4FAKEACCOUNTPUBLIC"
5
+ const FAKE_AUTH_USER_KEY = "UDXFAKEUSERKEY3FAKEAUTHUSERPUBLIC"
6
+
7
+ describe("buildCalloutConfig", () => {
8
+ test("includes required auth_callout block with provided keys", () => {
9
+ const config = buildCalloutConfig({
10
+ accountPublicKey: FAKE_ACCOUNT_KEY,
11
+ authUserPublicKey: FAKE_AUTH_USER_KEY,
12
+ })
13
+
14
+ expect(config).toContain("auth_callout")
15
+ expect(config).toContain(`issuer: "${FAKE_ACCOUNT_KEY}"`)
16
+ expect(config).toContain(`"${FAKE_AUTH_USER_KEY}"`)
17
+ expect(config).toContain("jetstream")
18
+ expect(config).toContain("websocket")
19
+ expect(config).toContain("no_tls: true")
20
+ })
21
+
22
+ test("uses default host 127.0.0.1 when not specified", () => {
23
+ const config = buildCalloutConfig({
24
+ accountPublicKey: FAKE_ACCOUNT_KEY,
25
+ authUserPublicKey: FAKE_AUTH_USER_KEY,
26
+ })
27
+ expect(config).toContain('"127.0.0.1"')
28
+ })
29
+
30
+ test("uses provided host", () => {
31
+ const config = buildCalloutConfig({
32
+ accountPublicKey: FAKE_ACCOUNT_KEY,
33
+ authUserPublicKey: FAKE_AUTH_USER_KEY,
34
+ host: "100.64.1.1",
35
+ })
36
+ expect(config).toContain('"100.64.1.1"')
37
+ })
38
+
39
+ test("includes storeDir when provided", () => {
40
+ const config = buildCalloutConfig({
41
+ accountPublicKey: FAKE_ACCOUNT_KEY,
42
+ authUserPublicKey: FAKE_AUTH_USER_KEY,
43
+ storeDir: "/tmp/nats-test-store",
44
+ })
45
+ expect(config).toContain('store_dir: "/tmp/nats-test-store"')
46
+ })
47
+
48
+ test("uses provided port", () => {
49
+ const config = buildCalloutConfig({
50
+ accountPublicKey: FAKE_ACCOUNT_KEY,
51
+ authUserPublicKey: FAKE_AUTH_USER_KEY,
52
+ port: 4567,
53
+ wsPort: 4568,
54
+ })
55
+ expect(config).toContain("port: 4567")
56
+ expect(config).toContain("port: 4568")
57
+ })
58
+
59
+ test("uses custom account name when provided", () => {
60
+ const config = buildCalloutConfig({
61
+ accountPublicKey: FAKE_ACCOUNT_KEY,
62
+ authUserPublicKey: FAKE_AUTH_USER_KEY,
63
+ accountName: "MY_ACCOUNT",
64
+ })
65
+ expect(config).toContain("MY_ACCOUNT")
66
+ })
67
+
68
+ test("no include directives (avoids wrapper mis-resolution bug)", () => {
69
+ const config = buildCalloutConfig({
70
+ accountPublicKey: FAKE_ACCOUNT_KEY,
71
+ authUserPublicKey: FAKE_AUTH_USER_KEY,
72
+ })
73
+ expect(config).not.toContain("include")
74
+ })
75
+ })
76
+
77
+ describe("validateCalloutConfigShape", () => {
78
+ test("passes for a valid config", () => {
79
+ const config = buildCalloutConfig({
80
+ accountPublicKey: FAKE_ACCOUNT_KEY,
81
+ authUserPublicKey: FAKE_AUTH_USER_KEY,
82
+ })
83
+ const { ok, missing } = validateCalloutConfigShape(config)
84
+ expect(ok).toBe(true)
85
+ expect(missing).toHaveLength(0)
86
+ })
87
+
88
+ test("fails when sections are missing", () => {
89
+ const { ok, missing } = validateCalloutConfigShape("just some text")
90
+ expect(ok).toBe(false)
91
+ expect(missing.length).toBeGreaterThan(0)
92
+ })
93
+ })
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Generate a self-contained nats-server config string for auth-callout mode
3
+ * (Stage A, PR1).
4
+ *
5
+ * Key points mirroring the spike caveat in decision 0007:
6
+ * - We do NOT use the wrapper's websocket+config combination (causes include
7
+ * mis-resolution). This config is written to a temp file and passed via -c
8
+ * to the binary directly.
9
+ * - The config includes the websocket{} block, JetStream, and the full
10
+ * accounts + authorization { auth_callout { … } } block.
11
+ */
12
+
13
+ export interface CalloutConfigOptions {
14
+ /** TCP listen port; -1 = nats-server picks a random one. */
15
+ port?: number
16
+ /** WebSocket listen port; -1 = nats-server picks a random one. */
17
+ wsPort?: number
18
+ /** Host to bind. Default: 127.0.0.1 */
19
+ host?: string
20
+ /** JetStream store directory. */
21
+ storeDir?: string
22
+ /**
23
+ * Optional HTTP monitoring port (bound to 127.0.0.1 only). When set, enables
24
+ * the nats-server monitoring endpoints (/connz, /subsz, /varz) for diagnostics.
25
+ * Localhost-only — never exposed beyond the server host. Off when undefined.
26
+ */
27
+ monitorPort?: number
28
+ /** Public key of the callout issuer account. */
29
+ accountPublicKey: string
30
+ /** Public key of the auth-service user (bypasses the callout). */
31
+ authUserPublicKey: string
32
+ /** Name used for the callout account and auth_users entry. */
33
+ accountName?: string
34
+ }
35
+
36
+ /**
37
+ * Return a complete nats-server configuration string for callout mode.
38
+ * The config is self-contained (no `include` directives) so it can be written
39
+ * to any path and passed to nats-server via `-c`.
40
+ */
41
+ export function buildCalloutConfig(opts: CalloutConfigOptions): string {
42
+ const host = opts.host ?? "127.0.0.1"
43
+ const port = opts.port ?? -1
44
+ const wsPort = opts.wsPort ?? -1
45
+ const accountName = opts.accountName ?? "CALLOUT_ACCOUNT"
46
+
47
+ const lines: string[] = [
48
+ `# nats-server config — auth-callout mode (generated by Tinkaria)`,
49
+ `host: "${host}"`,
50
+ `port: ${port}`,
51
+ ``,
52
+ `# JetStream`,
53
+ `jetstream: true`,
54
+ ...(opts.storeDir ? [`store_dir: "${opts.storeDir}"`] : []),
55
+ ``,
56
+ // HTTP monitoring (diagnostics only, localhost-bound). Enables /connz, /subsz,
57
+ // /varz so we can inspect connections + per-connection subscriptions. Off
58
+ // unless monitorPort is provided.
59
+ ...(opts.monitorPort ? [`http: "127.0.0.1:${opts.monitorPort}"`, ``] : []),
60
+ `# WebSocket`,
61
+ `websocket {`,
62
+ ` host: "${host}"`,
63
+ ` port: ${wsPort}`,
64
+ ` no_tls: true`,
65
+ `}`,
66
+ ``,
67
+ `# Accounts — one account holds all connections`,
68
+ `accounts {`,
69
+ ` ${accountName} {`,
70
+ ` jetstream: enabled`,
71
+ ` users [`,
72
+ ` # auth_users: this user bypasses the callout (the responder itself)`,
73
+ ` { nkey: "${opts.authUserPublicKey}" }`,
74
+ ` ]`,
75
+ ` }`,
76
+ `}`,
77
+ ``,
78
+ `# Auth-callout — delegate authorization to the responder`,
79
+ `authorization {`,
80
+ ` auth_callout {`,
81
+ ` issuer: "${opts.accountPublicKey}"`,
82
+ ` auth_users: [ "${opts.authUserPublicKey}" ]`,
83
+ ` account: "${accountName}"`,
84
+ ` }`,
85
+ `}`,
86
+ ]
87
+
88
+ return lines.join("\n") + "\n"
89
+ }
90
+
91
+ /**
92
+ * Validate the generated config shape by checking for required sections.
93
+ * Used in tests; not called in production code.
94
+ */
95
+ export function validateCalloutConfigShape(config: string): {
96
+ ok: boolean
97
+ missing: string[]
98
+ } {
99
+ const required = [
100
+ "auth_callout",
101
+ "issuer:",
102
+ "auth_users:",
103
+ "account:",
104
+ "websocket",
105
+ "jetstream",
106
+ ]
107
+ const missing = required.filter((s) => !config.includes(s))
108
+ return { ok: missing.length === 0, missing }
109
+ }
@@ -0,0 +1,332 @@
1
+ /**
2
+ * Integration test — auth-callout isolation (Stage B, PR1).
3
+ *
4
+ * Proves:
5
+ * 1. A runner-A credential connects and can pub/sub its own scoped subjects.
6
+ * 2. The same runner-A credential is DENIED (permissions violation) on
7
+ * runtime.runner.cmd.B.> — the subject scoped to runner-B.
8
+ * 3. The same runner-A credential is DENIED on runner-B's KV registry key.
9
+ * 4. A ui-client credential is denied on any runtime.runner.cmd subject.
10
+ * 5. Unknown/garbage token is refused at connect time.
11
+ *
12
+ * Stage B: credentials are minted via mintCredentialToken (stateless HMAC-signed
13
+ * tokens) rather than registered in the in-memory registry. All isolation
14
+ * assertions are preserved unchanged.
15
+ */
16
+
17
+ import { describe, test, expect, afterAll, beforeAll } from "bun:test"
18
+ import { spawn, type ChildProcess } from "node:child_process"
19
+ import { mkdtempSync, writeFileSync, rmSync } from "node:fs"
20
+ import { join } from "node:path"
21
+ import { tmpdir } from "node:os"
22
+ import { connect, tokenAuthenticator } from "@nats-io/transport-node"
23
+ import { nkeyAuthenticator } from "@nats-io/nats-core"
24
+ import type { NatsConnection } from "@nats-io/transport-node"
25
+ import { resolveBinary } from "@lagz0ne/nats-embedded"
26
+ import { ensureCalloutKeys } from "./keys"
27
+ import { buildCalloutConfig } from "./callout-config"
28
+ import { CalloutResponder } from "./responder"
29
+ import { runnerKvKeySubject } from "./scope-policy"
30
+ import { mintCredentialToken } from "./token"
31
+
32
+ // ── Test setup ────────────────────────────────────────────────────────────────
33
+
34
+ const RUNNER_A = "runner-A"
35
+ const RUNNER_B = "runner-B"
36
+
37
+ // Tokens are minted from the shared secret in startCalloutServer(); declared
38
+ // here so tests can reference them after setup.
39
+ let SERVER_ADMIN_TOKEN: string
40
+ let UI_CLIENT_TOKEN: string
41
+ let RUNNER_A_TOKEN: string
42
+ let RUNNER_B_TOKEN: string
43
+
44
+ interface ServerInfo {
45
+ natsUrl: string
46
+ wsUrl: string
47
+ wsPort: number
48
+ tcpPort: number
49
+ }
50
+
51
+ let serverProcess: ChildProcess | null = null
52
+ let responder: CalloutResponder | null = null
53
+ let configDir: string | null = null
54
+ let serverInfo: ServerInfo | null = null
55
+
56
+ async function startCalloutServer(): Promise<ServerInfo> {
57
+ const dataDir = mkdtempSync(join(tmpdir(), "nats-callout-test-data-"))
58
+ configDir = mkdtempSync(join(tmpdir(), "nats-callout-test-conf-"))
59
+
60
+ const keys = await ensureCalloutKeys(dataDir)
61
+
62
+ // Stage B: mint stateless signed tokens — no registration needed.
63
+ SERVER_ADMIN_TOKEN = await mintCredentialToken({ class: "server-admin" }, keys.tokenSecret)
64
+ UI_CLIENT_TOKEN = await mintCredentialToken({ class: "ui-client" }, keys.tokenSecret)
65
+ RUNNER_A_TOKEN = await mintCredentialToken({ class: "runner", runnerId: RUNNER_A }, keys.tokenSecret)
66
+ RUNNER_B_TOKEN = await mintCredentialToken({ class: "runner", runnerId: RUNNER_B }, keys.tokenSecret)
67
+
68
+ const config = buildCalloutConfig({
69
+ host: "127.0.0.1",
70
+ port: -1, // random
71
+ wsPort: -1, // random
72
+ accountPublicKey: keys.accountPublicKey,
73
+ authUserPublicKey: keys.authUserPublicKey,
74
+ })
75
+
76
+ const configPath = join(configDir, "test-callout.conf")
77
+ writeFileSync(configPath, config)
78
+
79
+ const binaryPath = resolveBinary()
80
+
81
+ return new Promise<ServerInfo>((resolve, reject) => {
82
+ const proc = spawn(binaryPath, ["-c", configPath], {
83
+ stdio: ["ignore", "ignore", "pipe"],
84
+ })
85
+
86
+ serverProcess = proc
87
+
88
+ const timeout = setTimeout(() => {
89
+ proc.kill("SIGKILL")
90
+ reject(new Error("nats-server did not start within 10s"))
91
+ }, 10_000)
92
+
93
+ proc.on("error", (err) => { clearTimeout(timeout); reject(err) })
94
+ proc.on("exit", (code) => {
95
+ clearTimeout(timeout)
96
+ reject(new Error(`nats-server exited early with code ${code}`))
97
+ })
98
+
99
+ let tcpPort: number | null = null
100
+ let wsPort: number | null = null
101
+ let buffer = ""
102
+
103
+ proc.stderr!.on("data", (chunk: Buffer) => {
104
+ buffer += chunk.toString()
105
+ const lines = buffer.split("\n")
106
+ buffer = lines.pop() ?? ""
107
+
108
+ for (const line of lines) {
109
+ const tcpMatch = line.match(/Listening for client connections on .+:(\d+)/)
110
+ if (tcpMatch) tcpPort = Number(tcpMatch[1])
111
+
112
+ const wsMatch = line.match(/Listening for websocket clients on .+:(\d+)/)
113
+ if (wsMatch) wsPort = Number(wsMatch[1])
114
+
115
+ if (tcpPort !== null && wsPort !== null) {
116
+ clearTimeout(timeout)
117
+ proc.removeAllListeners("exit")
118
+ const natsUrl = `nats://127.0.0.1:${tcpPort}`
119
+ resolve({ natsUrl, wsUrl: `ws://127.0.0.1:${wsPort}`, wsPort, tcpPort })
120
+ return
121
+ }
122
+ }
123
+ })
124
+ }).then(async (info) => {
125
+ // Start the responder — tokenSecret is sufficient; no registration step.
126
+ responder = new CalloutResponder({
127
+ natsUrl: info.natsUrl,
128
+ authUserKp: keys.authUserKp,
129
+ accountKp: keys.accountKp,
130
+ accountPublicKey: keys.accountPublicKey,
131
+ accountName: "CALLOUT_ACCOUNT",
132
+ tokenSecret: keys.tokenSecret,
133
+ })
134
+
135
+ await responder.start()
136
+
137
+ // Brief settle for the responder subscription to be active
138
+ await new Promise((r) => setTimeout(r, 100))
139
+
140
+ return info
141
+ })
142
+ }
143
+
144
+ async function connectWithToken(natsUrl: string, token: string): Promise<NatsConnection> {
145
+ return connect({
146
+ servers: natsUrl,
147
+ authenticator: tokenAuthenticator(token),
148
+ // Short timeout so permission errors surface quickly
149
+ timeout: 5000,
150
+ })
151
+ }
152
+
153
+ // ── Lifecycle ─────────────────────────────────────────────────────────────────
154
+
155
+ beforeAll(async () => {
156
+ serverInfo = await startCalloutServer()
157
+ }, 15_000)
158
+
159
+ afterAll(async () => {
160
+ await responder?.stop()
161
+ serverProcess?.kill("SIGTERM")
162
+ if (configDir) {
163
+ try { rmSync(configDir, { recursive: true }) } catch { /* best-effort */ }
164
+ }
165
+ })
166
+
167
+ // ── Tests ─────────────────────────────────────────────────────────────────────
168
+
169
+ describe("auth-callout isolation", () => {
170
+
171
+ // ── Happy path ──────────────────────────────────────────────────────────────
172
+
173
+ test("runner-A connects with scoped credential", async () => {
174
+ const nc = await connectWithToken(serverInfo!.natsUrl, RUNNER_A_TOKEN)
175
+ expect(nc.isClosed()).toBe(false)
176
+ await nc.drain()
177
+ })
178
+
179
+ test("runner-A can publish its own heartbeat", async () => {
180
+ const nc = await connectWithToken(serverInfo!.natsUrl, RUNNER_A_TOKEN)
181
+ // If the subject is not allowed, NATS will close the connection with a
182
+ // permissions violation. No error here = allowed.
183
+ nc.publish(`runtime.runner.heartbeat.${RUNNER_A}`, new TextEncoder().encode("hb"))
184
+ await nc.flush()
185
+ await nc.drain()
186
+ })
187
+
188
+ test("runner-A can subscribe to its own cmd subject", async () => {
189
+ const nc = await connectWithToken(serverInfo!.natsUrl, RUNNER_A_TOKEN)
190
+ const sub = nc.subscribe(`runtime.runner.cmd.${RUNNER_A}.>`)
191
+ expect(sub).toBeDefined()
192
+ sub.unsubscribe()
193
+ await nc.drain()
194
+ })
195
+
196
+ test("ui-client connects and can subscribe to runner events", async () => {
197
+ const nc = await connectWithToken(serverInfo!.natsUrl, UI_CLIENT_TOKEN)
198
+ const sub = nc.subscribe("runtime.runner.evt.>")
199
+ expect(sub).toBeDefined()
200
+ sub.unsubscribe()
201
+ await nc.drain()
202
+ })
203
+
204
+ // ── Negative — the decisive isolation assertions ────────────────────────────
205
+
206
+ test("runner-A is DENIED on runtime.runner.cmd.B.> (permissions violation)", async () => {
207
+ const nc = await connectWithToken(serverInfo!.natsUrl, RUNNER_A_TOKEN)
208
+
209
+ // Collect status events and the closed reason.
210
+ const statusEvents: string[] = []
211
+ let closedError: Error | null = null
212
+
213
+ // Subscribe before triggering the violation.
214
+ void (async () => {
215
+ for await (const s of nc.status()) {
216
+ statusEvents.push(`${s.type}:${String(s.data)}`)
217
+ }
218
+ })()
219
+
220
+ const closedPromise = nc.closed().then((err) => {
221
+ closedError = err instanceof Error ? err : new Error(String(err ?? "connection closed"))
222
+ })
223
+
224
+ // Attempt to publish to runner-B's command subject (not in runner-A's pub allow list).
225
+ // NATS sends -ERR 'Permissions Violation for Publish' and closes the connection.
226
+ nc.publish(`runtime.runner.cmd.${RUNNER_B}.start`, new TextEncoder().encode("{}"))
227
+ await nc.flush().catch(() => { /* may fail if already closing */ })
228
+
229
+ // Wait for the connection to be closed (NATS closes it on permissions violation).
230
+ await Promise.race([
231
+ closedPromise,
232
+ new Promise((r) => setTimeout(r, 4000)),
233
+ ])
234
+
235
+ // Drain/close cleanup (may already be closed)
236
+ try { await nc.drain() } catch { /* expected */ }
237
+
238
+ // The decisive assertion: NATS must have closed the connection after the
239
+ // permissions violation, or we must have received a permissionsError status.
240
+ const hasPermissionsViolation =
241
+ closedError !== null ||
242
+ statusEvents.some((s) =>
243
+ s.toLowerCase().includes("permission") || s.toLowerCase().includes("violation")
244
+ )
245
+
246
+ const evidenceMsg = closedError
247
+ ? `connection closed by NATS: ${closedError.message}`
248
+ : `status events: ${statusEvents.join(", ")}`
249
+
250
+ console.log(`[TEST] runner-A denied on runtime.runner.cmd.${RUNNER_B}.start — ${evidenceMsg}`)
251
+
252
+ expect(hasPermissionsViolation).toBe(true)
253
+ }, 10_000)
254
+
255
+ test("runner-A is DENIED on runner-B KV registry key", async () => {
256
+ const nc = await connectWithToken(serverInfo!.natsUrl, RUNNER_A_TOKEN)
257
+
258
+ let denied = false
259
+ const errorPromise = new Promise<void>((resolve) => {
260
+ void (async () => {
261
+ for await (const s of nc.status()) {
262
+ if (s.type === "permissionsError" || String(s.data).includes("Permissions Violation")) {
263
+ denied = true
264
+ resolve()
265
+ return
266
+ }
267
+ }
268
+ })()
269
+ nc.closed().then(() => { denied = true; resolve() })
270
+ })
271
+
272
+ // Attempt to write runner-B's KV key directly via NATS publish.
273
+ const runnerBKvSubject = runnerKvKeySubject(RUNNER_B)
274
+ nc.publish(runnerBKvSubject, new TextEncoder().encode("{}"))
275
+
276
+ await Promise.race([
277
+ errorPromise,
278
+ nc.closed(),
279
+ new Promise((r) => setTimeout(r, 3000)),
280
+ ])
281
+
282
+ try { await nc.drain() } catch { /* expected */ }
283
+
284
+ expect(denied).toBe(true)
285
+ console.log(`[TEST] runner-A denied on runner-B KV subject (${runnerBKvSubject})`)
286
+ }, 10_000)
287
+
288
+ test("ui-client is DENIED on runtime.runner.cmd subject", async () => {
289
+ const nc = await connectWithToken(serverInfo!.natsUrl, UI_CLIENT_TOKEN)
290
+
291
+ let denied = false
292
+ const errorPromise = new Promise<void>((resolve) => {
293
+ void (async () => {
294
+ for await (const s of nc.status()) {
295
+ if (s.type === "permissionsError" || String(s.data).includes("Permissions Violation")) {
296
+ denied = true
297
+ resolve()
298
+ return
299
+ }
300
+ }
301
+ })()
302
+ nc.closed().then(() => { denied = true; resolve() })
303
+ })
304
+
305
+ nc.publish(`runtime.runner.cmd.${RUNNER_A}.start`, new TextEncoder().encode("{}"))
306
+
307
+ await Promise.race([
308
+ errorPromise,
309
+ nc.closed(),
310
+ new Promise((r) => setTimeout(r, 3000)),
311
+ ])
312
+
313
+ try { await nc.drain() } catch { /* expected */ }
314
+
315
+ expect(denied).toBe(true)
316
+ console.log("[TEST] ui-client denied on runtime.runner.cmd")
317
+ }, 10_000)
318
+
319
+ // ── Unknown credential is rejected at connect ──────────────────────────────
320
+
321
+ test("unknown token is refused at connect time", async () => {
322
+ let connectError: Error | null = null
323
+ try {
324
+ const nc = await connectWithToken(serverInfo!.natsUrl, "bogus-token-not-registered")
325
+ await nc.drain()
326
+ } catch (err) {
327
+ connectError = err instanceof Error ? err : new Error(String(err))
328
+ }
329
+ expect(connectError).not.toBeNull()
330
+ console.log("[TEST] unknown credential refused:", connectError?.message)
331
+ }, 10_000)
332
+ })