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,288 @@
1
+ import type { PresetDefinition } from "../shared/preset-types"
2
+ import type { TranscriptEntry } from "../shared/types"
3
+ import type { QuickResponseAdapter } from "./quick-response"
4
+ import { normalizeGeneratedPrompt, normalizeIntent, toTranscriptLine, truncateLine } from "./transcript-utils"
5
+ import { MAX_MERGE_SESSIONS } from "../shared/merge-presets"
6
+
7
+ const ANALYSIS_SCHEMA = {
8
+ type: "object",
9
+ properties: {
10
+ compactInstruction: { type: "string" },
11
+ nextInstruction: { type: "string" },
12
+ },
13
+ required: ["compactInstruction", "nextInstruction"],
14
+ additionalProperties: false,
15
+ } as const
16
+
17
+ const SUMMARY_SCHEMA = {
18
+ type: "object",
19
+ properties: {
20
+ summary: { type: "string" },
21
+ },
22
+ required: ["summary"],
23
+ additionalProperties: false,
24
+ } as const
25
+
26
+ const MAX_ANALYSIS_CHARS = 1_200
27
+ const MAX_SOURCE_SUMMARY_CHARS = 2_400
28
+ const MAX_FORK_TRANSCRIPT_LINES = 32
29
+ const MAX_FORK_TRANSCRIPT_CHARS = 14_000
30
+ const MAX_FORK_LINE_CHARS = 700
31
+ const MAX_MERGE_TOTAL_CHARS = 14_000
32
+ const MERGE_FLOOR_PER_SESSION_CHARS = 500
33
+ const MAX_MERGE_LINE_CHARS = 700
34
+
35
+ export interface SessionSeedSource {
36
+ chatId: string
37
+ entries: TranscriptEntry[]
38
+ }
39
+
40
+ interface SessionSeedIntentPlan {
41
+ compactInstruction: string
42
+ nextInstruction: string
43
+ }
44
+
45
+ function buildForkTranscriptExcerpt(entries: TranscriptEntry[]): string {
46
+ const lines = entries
47
+ .map((entry) => toTranscriptLine(entry, MAX_FORK_LINE_CHARS))
48
+ .filter((line): line is string => Boolean(line))
49
+
50
+ if (lines.length === 0) return "No prior transcript context was available."
51
+
52
+ const selected = lines.slice(-MAX_FORK_TRANSCRIPT_LINES)
53
+ const omittedCount = lines.length - selected.length
54
+ const header = omittedCount > 0
55
+ ? [`Recent source transcript excerpt. Older relevant lines omitted: ${omittedCount}.`]
56
+ : ["Recent source transcript excerpt:"]
57
+
58
+ const combined = [...header, ...selected]
59
+ let serialized = combined.join("\n")
60
+ if (serialized.length <= MAX_FORK_TRANSCRIPT_CHARS) return serialized
61
+
62
+ const trimmed: string[] = []
63
+ let remaining = MAX_FORK_TRANSCRIPT_CHARS - header[0]!.length - 1
64
+ for (let index = selected.length - 1; index >= 0; index -= 1) {
65
+ const line = selected[index]!
66
+ const cost = line.length + 1
67
+ if (remaining - cost < 0) break
68
+ trimmed.unshift(line)
69
+ remaining -= cost
70
+ }
71
+
72
+ return [...header, ...trimmed].join("\n")
73
+ }
74
+
75
+ function sliceAfterLastContextCleared(entries: TranscriptEntry[]): TranscriptEntry[] {
76
+ let lastClearedIndex = -1
77
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
78
+ if (entries[i]!.kind === "context_cleared") {
79
+ lastClearedIndex = i
80
+ break
81
+ }
82
+ }
83
+ return lastClearedIndex >= 0 ? entries.slice(lastClearedIndex + 1) : entries
84
+ }
85
+
86
+ function buildBudgetedTranscriptExcerpt(entries: TranscriptEntry[], charBudget: number): string {
87
+ const sliced = sliceAfterLastContextCleared(entries)
88
+ const lines = sliced
89
+ .map((entry) => toTranscriptLine(entry, MAX_MERGE_LINE_CHARS))
90
+ .filter((line): line is string => Boolean(line))
91
+
92
+ if (lines.length === 0) return "No prior transcript context was available."
93
+
94
+ const header = "Recent source transcript excerpt:"
95
+ const headerCost = header.length + 1
96
+ const selected: string[] = []
97
+ let remaining = charBudget - headerCost
98
+
99
+ for (let i = lines.length - 1; i >= 0; i -= 1) {
100
+ const line = lines[i]!
101
+ const cost = line.length + 1
102
+ if (remaining - cost < 0) break
103
+ selected.unshift(line)
104
+ remaining -= cost
105
+ }
106
+
107
+ if (selected.length === 0) {
108
+ const lastLine = lines[lines.length - 1]!
109
+ const maxLen = Math.max(0, charBudget - headerCost - 1)
110
+ if (maxLen > 0) {
111
+ selected.push(truncateLine(lastLine, maxLen))
112
+ } else {
113
+ return "No prior transcript context was available."
114
+ }
115
+ }
116
+
117
+ const omittedCount = lines.length - selected.length
118
+ const finalHeader = omittedCount > 0
119
+ ? `Recent source transcript excerpt. Older relevant lines omitted: ${omittedCount}.`
120
+ : header
121
+
122
+ return [finalHeader, ...selected].join("\n")
123
+ }
124
+
125
+ function computeUsableLength(entries: TranscriptEntry[]): number {
126
+ const sliced = sliceAfterLastContextCleared(entries)
127
+ return sliced
128
+ .map((entry) => toTranscriptLine(entry, MAX_MERGE_LINE_CHARS))
129
+ .filter((line): line is string => Boolean(line))
130
+ .reduce((sum, line) => sum + line.length, 0)
131
+ }
132
+
133
+ function allocateSessionBudgets(sources: SessionSeedSource[]): number[] {
134
+ if (sources.length > MAX_MERGE_SESSIONS) {
135
+ throw new Error(`Cannot compact more than ${MAX_MERGE_SESSIONS} sessions (got ${sources.length})`)
136
+ }
137
+
138
+ const usableLengths = sources.map((source) => computeUsableLength(source.entries))
139
+ const nonEmptyCount = usableLengths.filter((length) => length > 0).length
140
+ if (nonEmptyCount === 0) return usableLengths.map(() => 0)
141
+
142
+ const totalFloor = nonEmptyCount * MERGE_FLOOR_PER_SESSION_CHARS
143
+ const remainder = Math.max(0, MAX_MERGE_TOTAL_CHARS - totalFloor)
144
+ const totalUsable = usableLengths.reduce((sum, length) => sum + length, 0)
145
+
146
+ return usableLengths.map((length) => {
147
+ if (length === 0) return 0
148
+ const proportional = totalUsable > 0
149
+ ? Math.floor(remainder * (length / totalUsable))
150
+ : Math.floor(remainder / nonEmptyCount)
151
+ return MERGE_FLOOR_PER_SESSION_CHARS + proportional
152
+ })
153
+ }
154
+
155
+ function getFallbackInstruction(intent: string, preset?: PresetDefinition | null): string {
156
+ return normalizeIntent(intent) || preset?.defaultIntent || "Continue with the most useful verified next step."
157
+ }
158
+
159
+ async function analyzeSessionSeedIntent(args: {
160
+ mode: "fork" | "merge"
161
+ intent: string
162
+ preset?: PresetDefinition | null
163
+ cwd: string
164
+ adapter: QuickResponseAdapter
165
+ }): Promise<SessionSeedIntentPlan> {
166
+ const fallbackInstruction = getFallbackInstruction(args.intent, args.preset)
167
+ const result = await args.adapter.generateStructured<SessionSeedIntentPlan>({
168
+ cwd: args.cwd,
169
+ task: `${args.mode} session seed analysis`,
170
+ prompt: [
171
+ "Analyze the user's instruction for preparing a new coding session.",
172
+ "Return two fields only:",
173
+ "compactInstruction: how to compact the selected source context.",
174
+ "nextInstruction: what the new session should do after compaction.",
175
+ "Keep both concise, concrete, and execution-oriented.",
176
+ "Do not mention orchestration, parent or child sessions, or the compaction step itself.",
177
+ args.preset ? `Selected preset: ${args.preset.label}. ${args.preset.generatorHint}` : "No explicit preset was selected.",
178
+ "",
179
+ `User instruction:\n${fallbackInstruction}`,
180
+ ].join("\n"),
181
+ schema: ANALYSIS_SCHEMA,
182
+ parse: (value) => {
183
+ const output = value && typeof value === "object"
184
+ ? value as { compactInstruction?: unknown; nextInstruction?: unknown }
185
+ : {}
186
+ const compactInstruction = normalizeGeneratedPrompt(output.compactInstruction, MAX_ANALYSIS_CHARS)
187
+ const nextInstruction = normalizeGeneratedPrompt(output.nextInstruction, MAX_ANALYSIS_CHARS)
188
+ if (!compactInstruction || !nextInstruction) return null
189
+ return { compactInstruction, nextInstruction }
190
+ },
191
+ })
192
+
193
+ return result ?? {
194
+ compactInstruction: fallbackInstruction,
195
+ nextInstruction: fallbackInstruction,
196
+ }
197
+ }
198
+
199
+ function buildSourceExcerpt(source: SessionSeedSource, sources: SessionSeedSource[]): string {
200
+ if (sources.length <= 1) return buildForkTranscriptExcerpt(source.entries)
201
+ const budgets = allocateSessionBudgets(sources)
202
+ const sourceIndex = sources.findIndex((candidate) => candidate.chatId === source.chatId)
203
+ return buildBudgetedTranscriptExcerpt(source.entries, budgets[sourceIndex] ?? 0)
204
+ }
205
+
206
+ async function compactSourceContext(args: {
207
+ source: SessionSeedSource
208
+ sources: SessionSeedSource[]
209
+ compactInstruction: string
210
+ cwd: string
211
+ adapter: QuickResponseAdapter
212
+ }): Promise<{ label: string; summary: string }> {
213
+ const excerpt = buildSourceExcerpt(args.source, args.sources)
214
+ const fallbackSummary = excerpt.slice(0, MAX_SOURCE_SUMMARY_CHARS).trim()
215
+
216
+ const result = await args.adapter.generateStructured<string>({
217
+ cwd: args.cwd,
218
+ task: "session context compaction",
219
+ prompt: [
220
+ "Compact this transcript for reuse in a new coding session.",
221
+ `Optimize the compaction for this goal: ${args.compactInstruction}`,
222
+ "Preserve exact file names, symbols, commands, constraints, and open questions when they are present and proven.",
223
+ "Drop filler, repetition, and orchestration wording.",
224
+ "Prefer concise markdown with sections like Relevant Context, Constraints, Evidence, and Open Questions when useful.",
225
+ "",
226
+ excerpt,
227
+ ].join("\n"),
228
+ schema: SUMMARY_SCHEMA,
229
+ parse: (value) => {
230
+ const output = value && typeof value === "object" ? value as { summary?: unknown } : {}
231
+ return normalizeGeneratedPrompt(output.summary, MAX_SOURCE_SUMMARY_CHARS)
232
+ },
233
+ })
234
+
235
+ return {
236
+ label: args.source.chatId,
237
+ summary: result ?? (fallbackSummary || "No prior transcript context was available."),
238
+ }
239
+ }
240
+
241
+ function buildFinalSeedPrompt(nextInstruction: string, summaries: Array<{ label: string; summary: string }>): string {
242
+ const sections = [`## Objective\n${nextInstruction}`]
243
+
244
+ if (summaries.length === 1) {
245
+ sections.push(`## Relevant Context\n${summaries[0]!.summary}`)
246
+ } else {
247
+ sections.push([
248
+ "## Compacted Contexts",
249
+ ...summaries.map((summary) => `### ${summary.label}\n${summary.summary}`),
250
+ ].join("\n\n"))
251
+ }
252
+
253
+ sections.push(
254
+ "## Constraints\nPreserve proven constraints from the context above. Call out contradictions or missing evidence before making risky changes.",
255
+ )
256
+ sections.push("## Next Step\nStart directly on the objective using the compacted context above.")
257
+
258
+ return sections.join("\n\n")
259
+ }
260
+
261
+ export async function buildSessionSeedPrompt(args: {
262
+ mode: "fork" | "merge"
263
+ intent: string
264
+ preset?: PresetDefinition | null
265
+ sources: SessionSeedSource[]
266
+ cwd: string
267
+ adapter: QuickResponseAdapter
268
+ }): Promise<string> {
269
+ const plan = await analyzeSessionSeedIntent({
270
+ mode: args.mode,
271
+ intent: args.intent,
272
+ preset: args.preset,
273
+ cwd: args.cwd,
274
+ adapter: args.adapter,
275
+ })
276
+
277
+ const summaries = await Promise.all(
278
+ args.sources.map((source) => compactSourceContext({
279
+ source,
280
+ sources: args.sources,
281
+ compactInstruction: plan.compactInstruction,
282
+ cwd: args.cwd,
283
+ adapter: args.adapter,
284
+ })),
285
+ )
286
+
287
+ return buildFinalSeedPrompt(plan.nextInstruction, summaries)
288
+ }
@@ -0,0 +1,108 @@
1
+ import { describe, expect, test } from "bun:test"
2
+ import { ensureCloudflaredInstalled, logShareDetails, startShareTunnel } from "./share"
3
+
4
+ describe("ensureCloudflaredInstalled", () => {
5
+ test("returns immediately when the binary already exists", async () => {
6
+ const installCalls: string[] = []
7
+
8
+ const result = await ensureCloudflaredInstalled({
9
+ cloudflaredBin: "/tmp/cloudflared",
10
+ existsSync: () => true,
11
+ installCloudflared: async (to) => {
12
+ installCalls.push(to)
13
+ return to
14
+ },
15
+ })
16
+
17
+ expect(result).toBe("/tmp/cloudflared")
18
+ expect(installCalls).toEqual([])
19
+ })
20
+
21
+ test("installs the binary on demand when it is missing", async () => {
22
+ const installCalls: string[] = []
23
+ const logLines: string[] = []
24
+
25
+ const result = await ensureCloudflaredInstalled({
26
+ cloudflaredBin: "/tmp/cloudflared",
27
+ existsSync: () => false,
28
+ installCloudflared: async (to) => {
29
+ installCalls.push(to)
30
+ return to
31
+ },
32
+ log: (message) => {
33
+ logLines.push(message)
34
+ },
35
+ })
36
+
37
+ expect(result).toBe("/tmp/cloudflared")
38
+ expect(installCalls).toEqual(["/tmp/cloudflared"])
39
+ expect(logLines).toEqual(["installing cloudflared binary"])
40
+ })
41
+ })
42
+
43
+ describe("startShareTunnel", () => {
44
+ test("starts a quick tunnel after ensuring the binary exists", async () => {
45
+ const installCalls: string[] = []
46
+ const quickTunnelUrls: string[] = []
47
+ let stopCalls = 0
48
+
49
+ const shareTunnel = await startShareTunnel("http://localhost:3333", {
50
+ cloudflaredBin: "/tmp/cloudflared",
51
+ existsSync: () => false,
52
+ installCloudflared: async (to) => {
53
+ installCalls.push(to)
54
+ return to
55
+ },
56
+ createQuickTunnel: (localUrl) => {
57
+ quickTunnelUrls.push(localUrl)
58
+ return {
59
+ once(event, listener) {
60
+ if (event === "url") {
61
+ queueMicrotask(() => (listener as (url: string) => void)("https://kanna.trycloudflare.com"))
62
+ }
63
+ return this
64
+ },
65
+ off(_event, _listener) {
66
+ return this
67
+ },
68
+ stop() {
69
+ stopCalls += 1
70
+ return true
71
+ },
72
+ }
73
+ },
74
+ })
75
+
76
+ expect(installCalls).toEqual(["/tmp/cloudflared"])
77
+ expect(quickTunnelUrls).toEqual(["http://localhost:3333"])
78
+ expect(shareTunnel.publicUrl).toBe("https://kanna.trycloudflare.com")
79
+ shareTunnel.stop()
80
+ expect(stopCalls).toBe(1)
81
+ })
82
+ })
83
+
84
+ describe("logShareDetails", () => {
85
+ test("prints qr, public url, and local url in the expected order", async () => {
86
+ const logLines: string[] = []
87
+
88
+ await logShareDetails(
89
+ (message) => {
90
+ logLines.push(message)
91
+ },
92
+ "https://kanna.trycloudflare.com",
93
+ "http://localhost:3333",
94
+ async (url) => `[qr:${url}]\n`,
95
+ )
96
+
97
+ expect(logLines).toEqual([
98
+ "QR Code:",
99
+ "[qr:https://kanna.trycloudflare.com]",
100
+ "",
101
+ "Public URL:",
102
+ "https://kanna.trycloudflare.com",
103
+ "",
104
+ "Local URL:",
105
+ "http://localhost:3333",
106
+ ])
107
+ })
108
+ })
@@ -0,0 +1,113 @@
1
+ import { existsSync } from "node:fs"
2
+ import QRCode from "qrcode"
3
+ import { Tunnel, bin as cloudflaredBin, install as installCloudflared } from "cloudflared"
4
+
5
+ export interface StartedShareTunnel {
6
+ publicUrl: string
7
+ stop: () => void
8
+ }
9
+
10
+ export interface ShareTunnelProcess {
11
+ once(event: "url", listener: (url: string) => void): ShareTunnelProcess
12
+ once(event: "error", listener: (error: Error) => void): ShareTunnelProcess
13
+ once(event: "exit", listener: (code: number | null, signal: NodeJS.Signals | null) => void): ShareTunnelProcess
14
+ off(event: "url", listener: (url: string) => void): ShareTunnelProcess
15
+ off(event: "error", listener: (error: Error) => void): ShareTunnelProcess
16
+ off(event: "exit", listener: (code: number | null, signal: NodeJS.Signals | null) => void): ShareTunnelProcess
17
+ stop(): boolean
18
+ }
19
+
20
+ export interface ShareTunnelDeps {
21
+ cloudflaredBin?: string
22
+ existsSync?: (path: string) => boolean
23
+ installCloudflared?: (to: string) => Promise<string>
24
+ createQuickTunnel?: (localUrl: string) => ShareTunnelProcess
25
+ log?: (message: string) => void
26
+ }
27
+
28
+ export async function renderTerminalQr(url: string) {
29
+ return QRCode.toString(url, {
30
+ type: "terminal",
31
+ small: true,
32
+ errorCorrectionLevel: "M",
33
+ })
34
+ }
35
+
36
+ export async function ensureCloudflaredInstalled(
37
+ deps: ShareTunnelDeps = {},
38
+ ) {
39
+ const resolvedBin = deps.cloudflaredBin ?? cloudflaredBin
40
+ const fileExists = deps.existsSync ?? existsSync
41
+ const installBinary = deps.installCloudflared ?? installCloudflared
42
+
43
+ if (fileExists(resolvedBin)) {
44
+ return resolvedBin
45
+ }
46
+
47
+ deps.log?.("installing cloudflared binary")
48
+ await installBinary(resolvedBin)
49
+ return resolvedBin
50
+ }
51
+
52
+ export async function startShareTunnel(localUrl: string, deps: ShareTunnelDeps = {}): Promise<StartedShareTunnel> {
53
+ await ensureCloudflaredInstalled(deps)
54
+ const tunnel = (deps.createQuickTunnel ?? ((url) => Tunnel.quick(url)))(localUrl)
55
+
56
+ const publicUrl = await new Promise<string>((resolve, reject) => {
57
+ let settled = false
58
+
59
+ const cleanup = () => {
60
+ tunnel.off("url", handleUrl)
61
+ tunnel.off("error", handleError)
62
+ tunnel.off("exit", handleExit)
63
+ }
64
+
65
+ const settle = (callback: () => void) => {
66
+ if (settled) return
67
+ settled = true
68
+ cleanup()
69
+ callback()
70
+ }
71
+
72
+ const handleUrl = (url: string) => {
73
+ settle(() => resolve(url))
74
+ }
75
+
76
+ const handleError = (error: Error) => {
77
+ settle(() => reject(error))
78
+ }
79
+
80
+ const handleExit = (code: number | null, signal: NodeJS.Signals | null) => {
81
+ settle(() => reject(new Error(`Cloudflare tunnel exited before a public URL was ready (code: ${String(code)}, signal: ${String(signal)})`)))
82
+ }
83
+
84
+ tunnel.once("url", handleUrl)
85
+ tunnel.once("error", handleError)
86
+ tunnel.once("exit", handleExit)
87
+ })
88
+
89
+ return {
90
+ publicUrl,
91
+ stop: () => {
92
+ tunnel.stop()
93
+ },
94
+ }
95
+ }
96
+
97
+ export async function logShareDetails(
98
+ log: (message: string) => void,
99
+ publicUrl: string,
100
+ localUrl: string,
101
+ renderShareQrImpl: (url: string) => Promise<string> = renderTerminalQr,
102
+ ) {
103
+ const qrCode = await renderShareQrImpl(publicUrl)
104
+
105
+ log("QR Code:")
106
+ log(qrCode.trimEnd())
107
+ log("")
108
+ log("Public URL:")
109
+ log(publicUrl)
110
+ log("")
111
+ log("Local URL:")
112
+ log(localUrl)
113
+ }