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,112 @@
1
+ import { afterEach, describe, test, expect } from "bun:test"
2
+ import { NatsServer } from "@lagz0ne/nats-embedded"
3
+ import { connect, type NatsConnection } from "@nats-io/transport-node"
4
+ import { jetstreamManager } from "@nats-io/jetstream"
5
+ import { Kvm } from "@nats-io/kv"
6
+ import {
7
+ ensureTerminalEventsStream,
8
+ TERMINAL_EVENTS_STREAM,
9
+ } from "./nats-streams"
10
+ import { KV_BUCKET, snapshotKvKey } from "../shared/nats-subjects"
11
+
12
+ let server: NatsServer | null = null
13
+ let nc: NatsConnection | null = null
14
+
15
+ afterEach(async () => {
16
+ if (nc) {
17
+ await nc.drain()
18
+ nc = null
19
+ }
20
+ if (server) {
21
+ await server.stop()
22
+ server = null
23
+ }
24
+ })
25
+
26
+ describe("nats-streams", () => {
27
+ test("ensureTerminalEventsStream creates the stream", async () => {
28
+ server = await NatsServer.start({ jetstream: true })
29
+ nc = await connect({ servers: server.url })
30
+
31
+ await ensureTerminalEventsStream(nc)
32
+
33
+ const jsm = await jetstreamManager(nc)
34
+ const info = await jsm.streams.info(TERMINAL_EVENTS_STREAM)
35
+ expect(info.config.name).toBe(TERMINAL_EVENTS_STREAM)
36
+ expect(info.config.subjects).toContain("runtime.evt.terminal.>")
37
+ expect(info.config.storage).toBe("memory")
38
+ })
39
+
40
+ test("ensureTerminalEventsStream is idempotent", async () => {
41
+ server = await NatsServer.start({ jetstream: true })
42
+ nc = await connect({ servers: server.url })
43
+
44
+ await ensureTerminalEventsStream(nc)
45
+ await ensureTerminalEventsStream(nc)
46
+
47
+ const jsm = await jetstreamManager(nc)
48
+ const info = await jsm.streams.info(TERMINAL_EVENTS_STREAM)
49
+ expect(info.config.name).toBe(TERMINAL_EVENTS_STREAM)
50
+ })
51
+
52
+ test("stream retains published terminal events", async () => {
53
+ server = await NatsServer.start({ jetstream: true })
54
+ nc = await connect({ servers: server.url })
55
+
56
+ await ensureTerminalEventsStream(nc)
57
+
58
+ const jsm = await jetstreamManager(nc)
59
+ const encoder = new TextEncoder()
60
+
61
+ // Publish a terminal event
62
+ const { jetstream } = await import("@nats-io/jetstream")
63
+ const js = jetstream(nc)
64
+ await js.publish(
65
+ "runtime.evt.terminal.term-1",
66
+ encoder.encode(JSON.stringify({ type: "terminal.output", terminalId: "term-1", data: "hello" }))
67
+ )
68
+
69
+ const info = await jsm.streams.info(TERMINAL_EVENTS_STREAM)
70
+ expect(info.state.messages).toBe(1)
71
+ })
72
+ })
73
+
74
+ describe("KV snapshot bucket", () => {
75
+ test("can create and read from runtime_snapshots bucket", async () => {
76
+ server = await NatsServer.start({ jetstream: true })
77
+ nc = await connect({ servers: server.url })
78
+
79
+ const kvm = new Kvm(nc)
80
+ const kv = await kvm.create(KV_BUCKET)
81
+ const encoder = new TextEncoder()
82
+
83
+ await kv.put("sidebar", encoder.encode(JSON.stringify({ workspaceGroups: [] })))
84
+ const entry = await kv.get("sidebar")
85
+ expect(entry).not.toBeNull()
86
+ expect(entry!.json() as Record<string, unknown>).toEqual({ workspaceGroups: [] })
87
+ })
88
+
89
+ test("kv dedup: identical puts create new revisions", async () => {
90
+ server = await NatsServer.start({ jetstream: true })
91
+ nc = await connect({ servers: server.url })
92
+
93
+ const kvm = new Kvm(nc)
94
+ const kv = await kvm.create(KV_BUCKET)
95
+ const encoder = new TextEncoder()
96
+ const data = encoder.encode(JSON.stringify({ test: true }))
97
+
98
+ const rev1 = await kv.put("test-key", data)
99
+ const rev2 = await kv.put("test-key", data)
100
+ // NATS KV creates new revision even for identical values
101
+ // (server-side dedup in publisher prevents this)
102
+ expect(rev2).toBeGreaterThan(rev1)
103
+ })
104
+
105
+ test("snapshotKvKey maps topics correctly", () => {
106
+ expect(snapshotKvKey({ type: "sidebar" })).toBe("sidebar")
107
+ expect(snapshotKvKey({ type: "local-workspaces" })).toBe("local-workspaces")
108
+ expect(snapshotKvKey({ type: "chat", chatId: "abc" })).toBe("chat.abc")
109
+ expect(snapshotKvKey({ type: "terminal", terminalId: "t1" })).toBe("terminal.t1")
110
+ })
111
+ })
112
+
@@ -0,0 +1,129 @@
1
+ import { jetstreamManager, RetentionPolicy, StorageType } from "@nats-io/jetstream"
2
+ import { Kvm } from "@nats-io/kv"
3
+ import type { NatsConnection } from "@nats-io/transport-node"
4
+ import { ALL_TERMINAL_EVENTS, ALL_CHAT_MESSAGE_EVENTS, CHAT_MESSAGE_EVENTS_STREAM_NAME, ALL_WORKSPACE_COORDINATION_EVENTS, WORKSPACE_COORDINATION_EVENTS_STREAM_NAME, ALL_SANDBOX_EVENTS, SANDBOX_EVENTS_STREAM_NAME } from "../shared/nats-subjects"
5
+ import { RUNNER_EVENTS_STREAM, ALL_RUNNER_EVENTS, RUNNER_REGISTRY_BUCKET } from "../shared/runner-protocol"
6
+ import { LOG_PREFIX } from "../shared/branding"
7
+
8
+ export const TERMINAL_EVENTS_STREAM = "KANNA_TERMINAL_EVENTS"
9
+ export const CHAT_MESSAGE_EVENTS_STREAM = CHAT_MESSAGE_EVENTS_STREAM_NAME
10
+
11
+ interface StreamConfig {
12
+ name: string
13
+ subjects: string[]
14
+ max_age_ns: number
15
+ max_msgs: number
16
+ max_bytes: number
17
+ storage?: StorageType
18
+ }
19
+
20
+ const FIVE_MINUTES_NS = 5 * 60 * 1_000_000_000
21
+ const THIRTY_MINUTES_NS = 30 * 60 * 1_000_000_000
22
+
23
+ async function ensureStream(nc: NatsConnection, config: StreamConfig): Promise<void> {
24
+ const jsm = await jetstreamManager(nc)
25
+
26
+ const streamConfig = {
27
+ name: config.name,
28
+ subjects: config.subjects,
29
+ retention: RetentionPolicy.Limits,
30
+ storage: config.storage ?? StorageType.Memory,
31
+ max_age: config.max_age_ns,
32
+ max_msgs: config.max_msgs,
33
+ max_bytes: config.max_bytes,
34
+ }
35
+
36
+ try {
37
+ await jsm.streams.info(config.name)
38
+ await jsm.streams.update(config.name, streamConfig)
39
+ console.warn(LOG_PREFIX, `JetStream stream ${config.name} updated`)
40
+ } catch {
41
+ // info() throws when stream doesn't exist — safe to create.
42
+ // Any other error will surface from add().
43
+ await jsm.streams.add(streamConfig)
44
+ console.warn(LOG_PREFIX, `JetStream stream ${config.name} created`)
45
+ }
46
+ }
47
+
48
+ /** Creates or updates the JetStream stream for terminal events (memory-backed, 5 min / 10K msg retention). */
49
+ export function ensureTerminalEventsStream(nc: NatsConnection): Promise<void> {
50
+ return ensureStream(nc, {
51
+ name: TERMINAL_EVENTS_STREAM,
52
+ subjects: [ALL_TERMINAL_EVENTS],
53
+ max_age_ns: FIVE_MINUTES_NS,
54
+ max_msgs: 10_000,
55
+ max_bytes: 64 * 1024 * 1024,
56
+ })
57
+ }
58
+
59
+ /** Creates or updates the JetStream stream for chat message events (memory-backed, 30 min / 50K msg retention). */
60
+ export function ensureChatMessageStream(nc: NatsConnection): Promise<void> {
61
+ return ensureStream(nc, {
62
+ name: CHAT_MESSAGE_EVENTS_STREAM,
63
+ subjects: [ALL_CHAT_MESSAGE_EVENTS],
64
+ max_age_ns: THIRTY_MINUTES_NS,
65
+ max_msgs: 50_000,
66
+ max_bytes: 128 * 1024 * 1024,
67
+ })
68
+ }
69
+
70
+ /** Creates or updates the JetStream stream for runner turn events (file-backed, 30 min / 50K msg retention). */
71
+ export function ensureRunnerEventsStream(nc: NatsConnection): Promise<void> {
72
+ return ensureStream(nc, {
73
+ name: RUNNER_EVENTS_STREAM,
74
+ subjects: [ALL_RUNNER_EVENTS],
75
+ max_age_ns: THIRTY_MINUTES_NS,
76
+ max_msgs: 50_000,
77
+ max_bytes: 128 * 1024 * 1024,
78
+ storage: StorageType.File,
79
+ })
80
+ }
81
+
82
+ /** Creates or updates the JetStream stream for project coordination events (file-backed, 24h / 100K msg retention). */
83
+ export function ensureWorkspaceCoordinationStream(nc: NatsConnection): Promise<void> {
84
+ return ensureStream(nc, {
85
+ name: WORKSPACE_COORDINATION_EVENTS_STREAM_NAME,
86
+ subjects: [ALL_WORKSPACE_COORDINATION_EVENTS],
87
+ max_age_ns: 24 * 60 * 60 * 1_000_000_000,
88
+ max_msgs: 100_000,
89
+ max_bytes: 256 * 1024 * 1024,
90
+ storage: StorageType.File,
91
+ })
92
+ }
93
+
94
+ /** Creates or updates the JetStream stream for sandbox events (memory-backed, 5 min / 5K msg retention). */
95
+ export function ensureSandboxEventsStream(nc: NatsConnection): Promise<void> {
96
+ return ensureStream(nc, {
97
+ name: SANDBOX_EVENTS_STREAM_NAME,
98
+ subjects: [ALL_SANDBOX_EVENTS],
99
+ max_age_ns: FIVE_MINUTES_NS,
100
+ max_msgs: 5_000,
101
+ max_bytes: 10 * 1024 * 1024,
102
+ })
103
+ }
104
+
105
+ /**
106
+ * Ensure the runner registry KV bucket exists.
107
+ *
108
+ * Called by the server (server-admin connection) at startup so that spawned
109
+ * runners — whose NATS scope does not include STREAM.CREATE — can open the
110
+ * bucket immediately without needing to create it themselves.
111
+ *
112
+ * Stage D: narrow the runner's $JS.API.> pub allow to the specific subjects
113
+ * needed for kvm.open + kvStore.put once the exact set is empirically confirmed.
114
+ */
115
+ export async function ensureRunnerRegistryBucket(nc: NatsConnection): Promise<void> {
116
+ const kvm = new Kvm(nc)
117
+ try {
118
+ // Create with idempotent semantics: if the bucket already exists the
119
+ // underlying STREAM.CREATE call is treated as an update by nats-server.
120
+ await kvm.create(RUNNER_REGISTRY_BUCKET, {
121
+ max_bytes: 1024 * 1024,
122
+ })
123
+ console.warn(LOG_PREFIX, `Runner registry KV bucket '${RUNNER_REGISTRY_BUCKET}' ready`)
124
+ } catch (err) {
125
+ const message = err instanceof Error ? err.message : String(err)
126
+ console.warn(LOG_PREFIX, `Runner registry KV bucket ensure failed: ${message}`)
127
+ throw err
128
+ }
129
+ }
@@ -0,0 +1,185 @@
1
+ /**
2
+ * OAuth pool NATS responders.
3
+ *
4
+ * Subjects (request/reply under `runtime.cmd.oauth.*`):
5
+ * - oauth.list -> { ok, settings }
6
+ * - oauth.add { label, token, maxConcurrent? } -> { ok, entry }
7
+ * - oauth.remove { id } -> { ok, removed }
8
+ * - oauth.update { id, label?, maxConcurrent? } -> { ok, entry }
9
+ * - oauth.setConcurrencyDefault { value } -> { ok }
10
+ *
11
+ * Change broadcast: `runtime.evt.oauth.changed` carries snapshot after
12
+ * mutations so clients can refresh without re-requesting.
13
+ *
14
+ * Token values are returned with full secret material — UI only renders
15
+ * masked previews. Same posture as kanna; tinkaria runs on operator's own
16
+ * machine.
17
+ */
18
+
19
+ import type { NatsConnection, Subscription } from "@nats-io/transport-node"
20
+ import { compressPayload, decompressPayload } from "../../shared/compression"
21
+ import {
22
+ ALL_OAUTH_COMMANDS,
23
+ oauthChangedSubject,
24
+ } from "../../shared/nats-subjects"
25
+ import type { ClaudeAuthSettings, OAuthTokenEntry } from "../../shared/types"
26
+ import {
27
+ OAUTH_TOKEN_LABEL_MAX,
28
+ OAUTH_TOKEN_MAX_CONCURRENT_MAX,
29
+ OAUTH_TOKEN_MAX_CONCURRENT_MIN,
30
+ OAUTH_TOKEN_VALUE_MAX,
31
+ } from "../../shared/types"
32
+ import type { OAuthSettingsStore } from "./oauth-settings-store"
33
+
34
+ const LOG_PREFIX = "[oauth-pool]"
35
+ const encoder = new TextEncoder()
36
+ const decoder = new TextDecoder()
37
+
38
+ function encode(data: unknown): Uint8Array {
39
+ return compressPayload(encoder.encode(JSON.stringify(data)))
40
+ }
41
+
42
+ async function decode(data: Uint8Array): Promise<unknown> {
43
+ const raw = await decompressPayload(data)
44
+ return JSON.parse(decoder.decode(raw))
45
+ }
46
+
47
+ export interface OAuthResponderDeps {
48
+ nc: NatsConnection
49
+ store: OAuthSettingsStore
50
+ }
51
+
52
+ export interface OAuthResponderHandle {
53
+ dispose(): void
54
+ }
55
+
56
+ interface CommandResult {
57
+ ok: boolean
58
+ error?: string
59
+ settings?: ClaudeAuthSettings
60
+ entry?: OAuthTokenEntry
61
+ removed?: boolean
62
+ }
63
+
64
+ export function registerOAuthResponders(deps: OAuthResponderDeps): OAuthResponderHandle {
65
+ const { nc, store } = deps
66
+ const sub: Subscription = nc.subscribe(ALL_OAUTH_COMMANDS)
67
+
68
+ ;(async () => {
69
+ for await (const msg of sub) {
70
+ const subject = msg.subject
71
+ const type = subject.replace(/^runtime\.cmd\./, "")
72
+ let payload: Record<string, unknown> = {}
73
+ try {
74
+ payload = (await decode(msg.data)) as Record<string, unknown>
75
+ } catch {
76
+ msg.respond?.(encode({ ok: false, error: "invalid JSON payload" } satisfies CommandResult))
77
+ continue
78
+ }
79
+ try {
80
+ const result = await dispatch(type, payload, store)
81
+ if (result.ok && type !== "oauth.list") {
82
+ try {
83
+ nc.publish(oauthChangedSubject(), encode({ settings: store.getSnapshot() }))
84
+ } catch (err) {
85
+ console.warn(LOG_PREFIX, "changed publish failed:", err instanceof Error ? err.message : String(err))
86
+ }
87
+ }
88
+ msg.respond?.(encode(result))
89
+ } catch (err) {
90
+ const message = err instanceof Error ? err.message : String(err)
91
+ console.warn(LOG_PREFIX, `responder error for ${type}: ${message}`)
92
+ msg.respond?.(encode({ ok: false, error: message } satisfies CommandResult))
93
+ }
94
+ }
95
+ })().catch((err) => {
96
+ console.warn(LOG_PREFIX, "responder loop terminated:", err instanceof Error ? err.message : String(err))
97
+ })
98
+
99
+ return {
100
+ dispose() {
101
+ sub.unsubscribe()
102
+ },
103
+ }
104
+ }
105
+
106
+ async function dispatch(
107
+ type: string,
108
+ payload: Record<string, unknown>,
109
+ store: OAuthSettingsStore,
110
+ ): Promise<CommandResult> {
111
+ switch (type) {
112
+ case "oauth.list":
113
+ return { ok: true, settings: store.getSnapshot() }
114
+
115
+ case "oauth.add": {
116
+ const label = requireString(payload, "label").trim()
117
+ const token = requireString(payload, "token")
118
+ if (label.length === 0 || label.length > OAUTH_TOKEN_LABEL_MAX) {
119
+ return { ok: false, error: `label must be 1..${OAUTH_TOKEN_LABEL_MAX} chars` }
120
+ }
121
+ if (token.length === 0 || token.length > OAUTH_TOKEN_VALUE_MAX) {
122
+ return { ok: false, error: `token must be 1..${OAUTH_TOKEN_VALUE_MAX} chars` }
123
+ }
124
+ const maxConcurrent = normalizeMaxConcurrent(payload.maxConcurrent)
125
+ if (maxConcurrent === "invalid") {
126
+ return { ok: false, error: `maxConcurrent must be in [${OAUTH_TOKEN_MAX_CONCURRENT_MIN}, ${OAUTH_TOKEN_MAX_CONCURRENT_MAX}]` }
127
+ }
128
+ const entry = await store.addToken({
129
+ label,
130
+ token,
131
+ ...(maxConcurrent !== undefined ? { maxConcurrent } : {}),
132
+ })
133
+ return { ok: true, entry }
134
+ }
135
+
136
+ case "oauth.remove": {
137
+ const id = requireString(payload, "id")
138
+ const removed = await store.removeToken(id)
139
+ return { ok: true, removed }
140
+ }
141
+
142
+ case "oauth.update": {
143
+ const id = requireString(payload, "id")
144
+ const patch: { label?: string; maxConcurrent?: number | null } = {}
145
+ if (typeof payload.label === "string") patch.label = payload.label
146
+ if (payload.maxConcurrent === null) patch.maxConcurrent = null
147
+ else if (typeof payload.maxConcurrent === "number") {
148
+ const v = normalizeMaxConcurrent(payload.maxConcurrent)
149
+ if (v === "invalid") return { ok: false, error: "maxConcurrent out of range" }
150
+ if (v !== undefined) patch.maxConcurrent = v
151
+ }
152
+ const entry = await store.updateToken(id, patch)
153
+ if (!entry) return { ok: false, error: `unknown token id: ${id}` }
154
+ return { ok: true, entry }
155
+ }
156
+
157
+ case "oauth.setConcurrencyDefault": {
158
+ const value = payload.value
159
+ if (typeof value !== "number" || value < OAUTH_TOKEN_MAX_CONCURRENT_MIN || value > OAUTH_TOKEN_MAX_CONCURRENT_MAX) {
160
+ return { ok: false, error: `value must be in [${OAUTH_TOKEN_MAX_CONCURRENT_MIN}, ${OAUTH_TOKEN_MAX_CONCURRENT_MAX}]` }
161
+ }
162
+ await store.setConcurrencyDefault(value)
163
+ return { ok: true }
164
+ }
165
+
166
+ default:
167
+ return { ok: false, error: `unknown oauth command: ${type}` }
168
+ }
169
+ }
170
+
171
+ function requireString(payload: Record<string, unknown>, field: string): string {
172
+ const value = payload[field]
173
+ if (typeof value !== "string") {
174
+ throw new Error(`missing required string field: ${field}`)
175
+ }
176
+ return value
177
+ }
178
+
179
+ function normalizeMaxConcurrent(raw: unknown): number | undefined | "invalid" {
180
+ if (raw === undefined || raw === null) return undefined
181
+ if (typeof raw !== "number" || !Number.isFinite(raw)) return "invalid"
182
+ const rounded = Math.round(raw)
183
+ if (rounded < OAUTH_TOKEN_MAX_CONCURRENT_MIN || rounded > OAUTH_TOKEN_MAX_CONCURRENT_MAX) return "invalid"
184
+ return rounded
185
+ }
@@ -0,0 +1,173 @@
1
+ import { promises as fs } from "fs"
2
+ import path from "path"
3
+ import { homedir } from "os"
4
+ import {
5
+ CLAUDE_AUTH_DEFAULTS,
6
+ type ClaudeAuthSettings,
7
+ type OAuthTokenEntry,
8
+ type OAuthTokenStatus,
9
+ } from "../../shared/types"
10
+ import type { TokenStatusPatch } from "./oauth-token-pool"
11
+
12
+ const FILE_NAME = "oauth-tokens.json"
13
+
14
+ function defaultDir(): string {
15
+ return path.join(homedir(), ".tinkaria")
16
+ }
17
+
18
+ function isValidStatus(value: unknown): value is OAuthTokenStatus {
19
+ return value === "active" || value === "limited" || value === "error" || value === "disabled"
20
+ }
21
+
22
+ function normalizeEntry(raw: unknown): OAuthTokenEntry | null {
23
+ if (!raw || typeof raw !== "object") return null
24
+ const r = raw as Record<string, unknown>
25
+ if (typeof r.id !== "string" || typeof r.label !== "string" || typeof r.token !== "string") return null
26
+ const status = isValidStatus(r.status) ? r.status : "active"
27
+ const out: OAuthTokenEntry = {
28
+ id: r.id,
29
+ label: r.label,
30
+ token: r.token,
31
+ status,
32
+ limitedUntil: typeof r.limitedUntil === "number" ? r.limitedUntil : null,
33
+ lastUsedAt: typeof r.lastUsedAt === "number" ? r.lastUsedAt : null,
34
+ lastErrorAt: typeof r.lastErrorAt === "number" ? r.lastErrorAt : null,
35
+ lastErrorMessage: typeof r.lastErrorMessage === "string" ? r.lastErrorMessage : null,
36
+ addedAt: typeof r.addedAt === "number" ? r.addedAt : Date.now(),
37
+ }
38
+ if (typeof r.maxConcurrent === "number" && Number.isFinite(r.maxConcurrent)) {
39
+ out.maxConcurrent = r.maxConcurrent
40
+ }
41
+ return out
42
+ }
43
+
44
+ function normalizeSettings(raw: unknown): ClaudeAuthSettings {
45
+ if (!raw || typeof raw !== "object") return { ...CLAUDE_AUTH_DEFAULTS, tokens: [] }
46
+ const r = raw as Record<string, unknown>
47
+ const tokensRaw = Array.isArray(r.tokens) ? r.tokens : []
48
+ const tokens = tokensRaw.map(normalizeEntry).filter((t): t is OAuthTokenEntry => t !== null)
49
+ const concurrencyDefault = typeof r.concurrencyDefault === "number" && Number.isFinite(r.concurrencyDefault)
50
+ ? r.concurrencyDefault
51
+ : CLAUDE_AUTH_DEFAULTS.concurrencyDefault
52
+ return { tokens, concurrencyDefault }
53
+ }
54
+
55
+ export class OAuthSettingsStore {
56
+ private readonly filePath: string
57
+ private cached: ClaudeAuthSettings = { ...CLAUDE_AUTH_DEFAULTS, tokens: [] }
58
+ private writeChain: Promise<void> = Promise.resolve()
59
+ private loaded = false
60
+
61
+ constructor(dataDir = defaultDir()) {
62
+ this.filePath = path.join(dataDir, FILE_NAME)
63
+ }
64
+
65
+ async load(): Promise<void> {
66
+ try {
67
+ const raw = await fs.readFile(this.filePath, "utf8")
68
+ const parsed = JSON.parse(raw)
69
+ this.cached = normalizeSettings(parsed)
70
+ } catch (err) {
71
+ const e = err as NodeJS.ErrnoException
72
+ if (e.code !== "ENOENT") {
73
+ console.warn("[oauth-pool] failed to load tokens:", e.message)
74
+ }
75
+ this.cached = { ...CLAUDE_AUTH_DEFAULTS, tokens: [] }
76
+ }
77
+ this.loaded = true
78
+ }
79
+
80
+ getSnapshot(): ClaudeAuthSettings {
81
+ return { tokens: [...this.cached.tokens], concurrencyDefault: this.cached.concurrencyDefault }
82
+ }
83
+
84
+ getTokens(): OAuthTokenEntry[] {
85
+ return [...this.cached.tokens]
86
+ }
87
+
88
+ getConcurrencyDefault(): number {
89
+ return this.cached.concurrencyDefault
90
+ }
91
+
92
+ async addToken(input: { label: string; token: string; maxConcurrent?: number }): Promise<OAuthTokenEntry> {
93
+ if (!this.loaded) await this.load()
94
+ const entry: OAuthTokenEntry = {
95
+ id: cryptoRandomId(),
96
+ label: input.label.trim(),
97
+ token: input.token,
98
+ status: "active",
99
+ limitedUntil: null,
100
+ lastUsedAt: null,
101
+ lastErrorAt: null,
102
+ lastErrorMessage: null,
103
+ addedAt: Date.now(),
104
+ }
105
+ if (typeof input.maxConcurrent === "number") entry.maxConcurrent = input.maxConcurrent
106
+ this.cached = { ...this.cached, tokens: [...this.cached.tokens, entry] }
107
+ await this.persist()
108
+ return entry
109
+ }
110
+
111
+ async removeToken(id: string): Promise<boolean> {
112
+ if (!this.loaded) await this.load()
113
+ const next = this.cached.tokens.filter((t) => t.id !== id)
114
+ if (next.length === this.cached.tokens.length) return false
115
+ this.cached = { ...this.cached, tokens: next }
116
+ await this.persist()
117
+ return true
118
+ }
119
+
120
+ async updateToken(id: string, patch: { label?: string; maxConcurrent?: number | null }): Promise<OAuthTokenEntry | null> {
121
+ if (!this.loaded) await this.load()
122
+ let updated: OAuthTokenEntry | null = null
123
+ const next = this.cached.tokens.map((t) => {
124
+ if (t.id !== id) return t
125
+ const merged: OAuthTokenEntry = { ...t }
126
+ if (typeof patch.label === "string") merged.label = patch.label.trim()
127
+ if (patch.maxConcurrent === null) delete merged.maxConcurrent
128
+ else if (typeof patch.maxConcurrent === "number") merged.maxConcurrent = patch.maxConcurrent
129
+ updated = merged
130
+ return merged
131
+ })
132
+ if (!updated) return null
133
+ this.cached = { ...this.cached, tokens: next }
134
+ await this.persist()
135
+ return updated
136
+ }
137
+
138
+ async setConcurrencyDefault(value: number): Promise<void> {
139
+ if (!this.loaded) await this.load()
140
+ this.cached = { ...this.cached, concurrencyDefault: value }
141
+ await this.persist()
142
+ }
143
+
144
+ /** Used by OAuthTokenPool writeStatus callback. Persists asynchronously. */
145
+ mutateTokenStatus(id: string, patch: TokenStatusPatch): void {
146
+ const next = this.cached.tokens.map((t) => {
147
+ if (t.id !== id) return t
148
+ return { ...t, ...patch }
149
+ })
150
+ this.cached = { ...this.cached, tokens: next }
151
+ this.writeChain = this.writeChain.then(() => this.writeFile()).catch((err) => {
152
+ console.warn("[oauth-pool] status write failed:", err instanceof Error ? err.message : String(err))
153
+ })
154
+ }
155
+
156
+ private async persist(): Promise<void> {
157
+ this.writeChain = this.writeChain.then(() => this.writeFile())
158
+ await this.writeChain
159
+ }
160
+
161
+ private async writeFile(): Promise<void> {
162
+ await fs.mkdir(path.dirname(this.filePath), { recursive: true })
163
+ const tmp = `${this.filePath}.tmp`
164
+ await fs.writeFile(tmp, JSON.stringify(this.cached, null, 2), "utf8")
165
+ await fs.rename(tmp, this.filePath)
166
+ }
167
+ }
168
+
169
+ function cryptoRandomId(): string {
170
+ const arr = new Uint8Array(16)
171
+ crypto.getRandomValues(arr)
172
+ return Array.from(arr, (b) => b.toString(16).padStart(2, "0")).join("")
173
+ }