@ours.network/fleet 1.2.0-nightly.3 → 1.2.0-nightly.31

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 (458) hide show
  1. package/README.md +158 -28
  2. package/dist/account-origin.d.ts +2 -0
  3. package/dist/account-origin.js +6 -0
  4. package/dist/agent-ours/bridge.d.ts +3 -0
  5. package/dist/agent-ours/bridge.js +178 -0
  6. package/dist/agent-ours/controller.d.ts +14 -0
  7. package/dist/agent-ours/controller.js +60 -0
  8. package/dist/agent-ours/correspondence.d.ts +10 -0
  9. package/dist/agent-ours/correspondence.js +1 -0
  10. package/dist/agent-ours/file-rpc.d.ts +23 -0
  11. package/dist/agent-ours/file-rpc.js +123 -0
  12. package/dist/agent-ours/harness.d.ts +14 -0
  13. package/dist/agent-ours/harness.js +72 -0
  14. package/dist/agent-ours/mcp-endpoint.d.ts +15 -0
  15. package/dist/agent-ours/mcp-endpoint.js +111 -0
  16. package/dist/agent-ours/runtime.d.ts +59 -0
  17. package/dist/agent-ours/runtime.js +283 -0
  18. package/dist/agent-ours/service.d.ts +23 -0
  19. package/dist/agent-ours/service.js +372 -0
  20. package/dist/agent-ours/state.d.ts +45 -0
  21. package/dist/agent-ours/state.js +95 -0
  22. package/dist/agent-ours/wire.d.ts +14 -0
  23. package/dist/agent-ours/wire.js +55 -0
  24. package/dist/application/conversation-history.d.ts +25 -0
  25. package/dist/application/conversation-history.js +189 -0
  26. package/dist/application/fleet-query-service.d.ts +1 -1
  27. package/dist/application/fleet-query-service.js +2 -2
  28. package/dist/application/legacy-supervisor-identity.d.ts +7 -0
  29. package/dist/application/legacy-supervisor-identity.js +43 -0
  30. package/dist/application/model-catalog.d.ts +1 -1
  31. package/dist/application/model-catalog.js +5 -2
  32. package/dist/application/preset-provenance.d.ts +19 -0
  33. package/dist/application/preset-provenance.js +59 -0
  34. package/dist/application/room-layout-definitions.d.ts +45 -0
  35. package/dist/application/room-layout-definitions.js +130 -0
  36. package/dist/application/session-control.d.ts +18 -0
  37. package/dist/application/session-control.js +54 -3
  38. package/dist/application/supervisor-ours-tools.d.ts +159 -0
  39. package/dist/application/supervisor-ours-tools.js +113 -0
  40. package/dist/application/task-layouts.d.ts +91 -0
  41. package/dist/application/task-layouts.js +249 -0
  42. package/dist/application/task-room-service.d.ts +99 -3
  43. package/dist/application/task-room-service.js +296 -31
  44. package/dist/atomic-file.d.ts +5 -0
  45. package/dist/atomic-file.js +62 -1
  46. package/dist/briefing.js +10 -59
  47. package/dist/build-info.json +6 -4
  48. package/dist/capabilities.d.ts +3 -1
  49. package/dist/capabilities.js +4 -0
  50. package/dist/cli.js +131 -5
  51. package/dist/client-profile.d.ts +5 -16
  52. package/dist/client-profile.js +4 -112
  53. package/dist/config.d.ts +4 -0
  54. package/dist/config.js +7 -0
  55. package/dist/creation.d.ts +1 -1
  56. package/dist/creation.js +5 -16
  57. package/dist/daemon-recovery.d.ts +1 -8
  58. package/dist/daemon-recovery.js +8 -113
  59. package/dist/docs.d.ts +3 -3
  60. package/dist/docs.js +43 -23
  61. package/dist/doctor.d.ts +1 -1
  62. package/dist/doctor.js +46 -59
  63. package/dist/erased-resources.d.ts +9 -0
  64. package/dist/erased-resources.js +87 -0
  65. package/dist/fleet-command-audit.d.ts +1 -0
  66. package/dist/fleet-command-audit.js +12 -7
  67. package/dist/harness/agent-session.d.ts +2 -0
  68. package/dist/harness/claude-code-session.js +4 -2
  69. package/dist/harness/codex-app-server-proxy.d.ts +4 -1
  70. package/dist/harness/codex-app-server-proxy.js +41 -5
  71. package/dist/harness/codex-session.js +9 -2
  72. package/dist/harness/codex.d.ts +3 -2
  73. package/dist/harness/codex.js +4 -3
  74. package/dist/harness/hermes-session.js +7 -1
  75. package/dist/init-wizard.js +20 -9
  76. package/dist/isolation/policy.js +2 -0
  77. package/dist/isolation/types.d.ts +5 -0
  78. package/dist/monitor.d.ts +1 -34
  79. package/dist/monitor.js +24 -151
  80. package/dist/notifications/fleet-producer.d.ts +18 -0
  81. package/dist/notifications/fleet-producer.js +129 -0
  82. package/dist/notifications/outbox.d.ts +31 -0
  83. package/dist/notifications/outbox.js +104 -0
  84. package/dist/owner-channel/channel.d.ts +3 -0
  85. package/dist/owner-channel/channel.js +34 -6
  86. package/dist/owner-channel/commands.d.ts +4 -0
  87. package/dist/owner-channel/commands.js +8 -0
  88. package/dist/owner-channel/lifecycle-outbox.js +4 -3
  89. package/dist/owner-channel/ours-client.js +2 -17
  90. package/dist/preset-migration.js +15 -0
  91. package/dist/rooms-tasks/add-member.d.ts +48 -0
  92. package/dist/rooms-tasks/add-member.js +83 -0
  93. package/dist/rooms-tasks/archived-absence.d.ts +8 -0
  94. package/dist/rooms-tasks/archived-absence.js +48 -0
  95. package/dist/rooms-tasks/cli.js +34 -5
  96. package/dist/rooms-tasks/close.d.ts +5 -2
  97. package/dist/rooms-tasks/close.js +208 -28
  98. package/dist/rooms-tasks/config.js +3 -1
  99. package/dist/rooms-tasks/cowork-adapter.d.ts +6 -4
  100. package/dist/rooms-tasks/cowork-adapter.js +11 -117
  101. package/dist/rooms-tasks/cowork-http.d.ts +3 -0
  102. package/dist/rooms-tasks/cowork-http.js +70 -0
  103. package/dist/rooms-tasks/deletion.d.ts +1 -1
  104. package/dist/rooms-tasks/deletion.js +74 -5
  105. package/dist/rooms-tasks/erasure.d.ts +6 -0
  106. package/dist/rooms-tasks/erasure.js +169 -0
  107. package/dist/rooms-tasks/external-worker.d.ts +1 -1
  108. package/dist/rooms-tasks/external-worker.js +3 -4
  109. package/dist/rooms-tasks/layout-binding-client.d.ts +11 -0
  110. package/dist/rooms-tasks/layout-binding-client.js +56 -0
  111. package/dist/rooms-tasks/layout-binding-grants.d.ts +21 -0
  112. package/dist/rooms-tasks/layout-binding-grants.js +128 -0
  113. package/dist/rooms-tasks/layout-cli.d.ts +4 -0
  114. package/dist/rooms-tasks/layout-cli.js +107 -0
  115. package/dist/rooms-tasks/layout-config.d.ts +11 -0
  116. package/dist/rooms-tasks/layout-config.js +89 -0
  117. package/dist/rooms-tasks/layout-control.d.ts +21 -0
  118. package/dist/rooms-tasks/layout-control.js +52 -0
  119. package/dist/rooms-tasks/layout-service.d.ts +42 -0
  120. package/dist/rooms-tasks/layout-service.js +185 -0
  121. package/dist/rooms-tasks/layout.d.ts +114 -0
  122. package/dist/rooms-tasks/layout.js +250 -0
  123. package/dist/rooms-tasks/live-readiness.d.ts +18 -0
  124. package/dist/rooms-tasks/live-readiness.js +85 -0
  125. package/dist/rooms-tasks/member-startup.d.ts +1 -0
  126. package/dist/rooms-tasks/member-startup.js +2 -0
  127. package/dist/rooms-tasks/provision.d.ts +14 -0
  128. package/dist/rooms-tasks/provision.js +182 -48
  129. package/dist/rooms-tasks/room-state.d.ts +4 -2
  130. package/dist/rooms-tasks/room-state.js +53 -8
  131. package/dist/rooms-tasks/task-state.d.ts +42 -7
  132. package/dist/rooms-tasks/task-state.js +153 -23
  133. package/dist/rooms-tasks/types.d.ts +28 -0
  134. package/dist/rooms-tasks/workspace-artifacts.d.ts +6 -0
  135. package/dist/rooms-tasks/workspace-artifacts.js +93 -0
  136. package/dist/rooms-tasks/workspace.d.ts +26 -0
  137. package/dist/rooms-tasks/workspace.js +195 -0
  138. package/dist/runner.d.ts +23 -3
  139. package/dist/runner.js +581 -494
  140. package/dist/session/acp.d.ts +3 -0
  141. package/dist/session/acp.js +14 -0
  142. package/dist/session/arbiter.d.ts +3 -0
  143. package/dist/session/arbiter.js +20 -2
  144. package/dist/session/codex-app-server-transport.js +1 -1
  145. package/dist/session/codex-app-server.d.ts +3 -0
  146. package/dist/session/codex-app-server.js +25 -1
  147. package/dist/session/control.d.ts +10 -1
  148. package/dist/session/control.js +37 -2
  149. package/dist/session/conversation-normalizer.d.ts +2 -1
  150. package/dist/session/conversation-normalizer.js +1 -1
  151. package/dist/session/conversation-types.d.ts +5 -3
  152. package/dist/session/task-notice.d.ts +14 -0
  153. package/dist/session/task-notice.js +13 -0
  154. package/dist/session/types.d.ts +3 -0
  155. package/dist/socket-path.d.ts +2 -0
  156. package/dist/socket-path.js +22 -0
  157. package/dist/spawn.js +19 -16
  158. package/dist/startup-readiness.d.ts +13 -0
  159. package/dist/startup-readiness.js +38 -0
  160. package/dist/subscriptions/cli.d.ts +78 -0
  161. package/dist/subscriptions/cli.js +186 -0
  162. package/dist/subscriptions/launch.d.ts +37 -0
  163. package/dist/subscriptions/launch.js +93 -0
  164. package/dist/subscriptions/login.d.ts +75 -0
  165. package/dist/subscriptions/login.js +182 -0
  166. package/dist/subscriptions/observed.d.ts +7 -0
  167. package/dist/subscriptions/observed.js +44 -0
  168. package/dist/subscriptions/probe.d.ts +10 -0
  169. package/dist/subscriptions/probe.js +73 -0
  170. package/dist/subscriptions/service.d.ts +102 -0
  171. package/dist/subscriptions/service.js +364 -0
  172. package/dist/subscriptions/store.d.ts +71 -0
  173. package/dist/subscriptions/store.js +140 -0
  174. package/dist/supervisor/launchd.js +2 -1
  175. package/dist/supervisor/systemd.js +9 -23
  176. package/dist/temp-idle.d.ts +17 -0
  177. package/dist/temp-idle.js +42 -0
  178. package/dist/temp-lifecycle.d.ts +6 -1
  179. package/dist/temp-lifecycle.js +29 -13
  180. package/dist/temp-supervisor-recovery.d.ts +3 -0
  181. package/dist/temp-supervisor-recovery.js +46 -0
  182. package/dist/watchdog/briefing.js +3 -16
  183. package/dist/watchdog/run.js +19 -5
  184. package/dist/web/agent-attachments.d.ts +29 -0
  185. package/dist/web/agent-attachments.js +162 -0
  186. package/dist/web/auth.d.ts +28 -2
  187. package/dist/web/auth.js +81 -8
  188. package/dist/web/available-port.d.ts +2 -0
  189. package/dist/web/available-port.js +23 -0
  190. package/dist/web/control.d.ts +3 -2
  191. package/dist/web/control.js +11 -7
  192. package/dist/web/fleet-config-service.d.ts +4 -1
  193. package/dist/web/fleet-config-service.js +46 -15
  194. package/dist/web/prefix-gateway.d.ts +23 -0
  195. package/dist/web/prefix-gateway.js +156 -0
  196. package/dist/web/runtime.d.ts +4 -0
  197. package/dist/web/runtime.js +106 -20
  198. package/dist/web/server.d.ts +11 -0
  199. package/dist/web/server.js +445 -31
  200. package/dist/web/service.d.ts +11 -2
  201. package/dist/web/service.js +30 -4
  202. package/dist/web/workspace-devices.d.ts +29 -0
  203. package/dist/web/workspace-devices.js +84 -0
  204. package/dist/web/workspace-tunnel.d.ts +1 -0
  205. package/dist/web/workspace-tunnel.js +32 -0
  206. package/dist/web-app/assets/FleetApp-D58F4xje.css +1 -0
  207. package/dist/web-app/assets/FleetApp-DP5cBY0o.js +302 -0
  208. package/dist/web-app/assets/FleetApp-DP5cBY0o.js.map +1 -0
  209. package/dist/web-app/assets/abnfDiagram-VCTEODGH-BvJIz7wN.js +2 -0
  210. package/dist/web-app/assets/abnfDiagram-VCTEODGH-BvJIz7wN.js.map +1 -0
  211. package/dist/web-app/assets/arc-AT_BEfpe.js +2 -0
  212. package/dist/web-app/assets/arc-AT_BEfpe.js.map +1 -0
  213. package/dist/web-app/assets/architectureDiagram-5GKGNRK7-vSgJa9Xb.js +37 -0
  214. package/dist/web-app/assets/architectureDiagram-5GKGNRK7-vSgJa9Xb.js.map +1 -0
  215. package/dist/web-app/assets/blockDiagram-I7D4REHJ-DCFk_4Kv.js +130 -0
  216. package/dist/web-app/assets/blockDiagram-I7D4REHJ-DCFk_4Kv.js.map +1 -0
  217. package/dist/web-app/assets/browser-CMIwJ_8L.js +9 -0
  218. package/dist/web-app/assets/browser-CMIwJ_8L.js.map +1 -0
  219. package/dist/web-app/assets/c4Diagram-7LVT6UL2-8J_dVUtF.js +39 -0
  220. package/dist/web-app/assets/c4Diagram-7LVT6UL2-8J_dVUtF.js.map +1 -0
  221. package/dist/web-app/assets/channel-BHCBlfND.js +2 -0
  222. package/dist/web-app/assets/channel-BHCBlfND.js.map +1 -0
  223. package/dist/web-app/assets/chunk-2Q5K7J3B-31RX-jnL.js +2 -0
  224. package/dist/web-app/assets/chunk-2Q5K7J3B-31RX-jnL.js.map +1 -0
  225. package/dist/web-app/assets/chunk-5VM5RSS4-CFF8_ZJH.js +16 -0
  226. package/dist/web-app/assets/chunk-5VM5RSS4-CFF8_ZJH.js.map +1 -0
  227. package/dist/web-app/assets/chunk-F27PBJKO-Dx1l5wLI.js +2 -0
  228. package/dist/web-app/assets/chunk-F27PBJKO-Dx1l5wLI.js.map +1 -0
  229. package/dist/web-app/assets/chunk-IMKFNOWR-Ba2_9bl_.js +232 -0
  230. package/dist/web-app/assets/chunk-IMKFNOWR-Ba2_9bl_.js.map +1 -0
  231. package/dist/web-app/assets/chunk-JWPE2WC7-_mnRB2OH.js +2 -0
  232. package/dist/web-app/assets/chunk-JWPE2WC7-_mnRB2OH.js.map +1 -0
  233. package/dist/web-app/assets/chunk-POPQ4Y6H-B8jQTxQO.js +2 -0
  234. package/dist/web-app/assets/chunk-POPQ4Y6H-B8jQTxQO.js.map +1 -0
  235. package/dist/web-app/assets/chunk-SVP7TREG-BO_Jnb_c.js +89 -0
  236. package/dist/web-app/assets/chunk-SVP7TREG-BO_Jnb_c.js.map +1 -0
  237. package/dist/web-app/assets/chunk-TICWLB2K-ZYZ9hFu5.js +207 -0
  238. package/dist/web-app/assets/chunk-TICWLB2K-ZYZ9hFu5.js.map +1 -0
  239. package/dist/web-app/assets/chunk-XXDRQBXY-ByUAYXI3.js +2 -0
  240. package/dist/web-app/assets/chunk-XXDRQBXY-ByUAYXI3.js.map +1 -0
  241. package/dist/web-app/assets/classDiagram-ZZMXUADV-Ddx7RAuT.js +2 -0
  242. package/dist/web-app/assets/classDiagram-ZZMXUADV-Ddx7RAuT.js.map +1 -0
  243. package/dist/web-app/assets/classDiagram-v2-VYDZK3BY-Ddx7RAuT.js +2 -0
  244. package/dist/web-app/assets/classDiagram-v2-VYDZK3BY-Ddx7RAuT.js.map +1 -0
  245. package/dist/web-app/assets/cose-bilkent-JH36ORCC-BuOGJgHG.js +2 -0
  246. package/dist/web-app/assets/cose-bilkent-JH36ORCC-BuOGJgHG.js.map +1 -0
  247. package/dist/web-app/assets/cynefin-OW5HDTMX-DLksGPh8.js +179 -0
  248. package/dist/web-app/assets/cynefin-OW5HDTMX-DLksGPh8.js.map +1 -0
  249. package/dist/web-app/assets/cynefinDiagram-5FMLGOSQ-nJQb5qZR.js +63 -0
  250. package/dist/web-app/assets/cynefinDiagram-5FMLGOSQ-nJQb5qZR.js.map +1 -0
  251. package/dist/web-app/assets/cytoscape.esm-CtNdOvlO.js +332 -0
  252. package/dist/web-app/assets/cytoscape.esm-CtNdOvlO.js.map +1 -0
  253. package/dist/web-app/assets/dagre-GXQ25YYZ-CCF5O_UD.js +5 -0
  254. package/dist/web-app/assets/dagre-GXQ25YYZ-CCF5O_UD.js.map +1 -0
  255. package/dist/web-app/assets/defaultLocale-DX6XiGOO.js +2 -0
  256. package/dist/web-app/assets/defaultLocale-DX6XiGOO.js.map +1 -0
  257. package/dist/web-app/assets/diagram-S7CK7UJ4-C88au4NC.js +31 -0
  258. package/dist/web-app/assets/diagram-S7CK7UJ4-C88au4NC.js.map +1 -0
  259. package/dist/web-app/assets/diagram-UQ7AKVKN-B4QnPs7U.js +42 -0
  260. package/dist/web-app/assets/diagram-UQ7AKVKN-B4QnPs7U.js.map +1 -0
  261. package/dist/web-app/assets/diagram-VSXAHHWV-DeANdVWA.js +4 -0
  262. package/dist/web-app/assets/diagram-VSXAHHWV-DeANdVWA.js.map +1 -0
  263. package/dist/web-app/assets/diagram-VX7I27RA-YzlB_2AY.js +25 -0
  264. package/dist/web-app/assets/diagram-VX7I27RA-YzlB_2AY.js.map +1 -0
  265. package/dist/web-app/assets/diagram-Z3DM3KII-YY9NFaz_.js +25 -0
  266. package/dist/web-app/assets/diagram-Z3DM3KII-YY9NFaz_.js.map +1 -0
  267. package/dist/web-app/assets/ebnfDiagram-PWID7BFC-Cqu9kHUC.js +2 -0
  268. package/dist/web-app/assets/ebnfDiagram-PWID7BFC-Cqu9kHUC.js.map +1 -0
  269. package/dist/web-app/assets/erDiagram-RLTQ6QDP-IHi93MdG.js +100 -0
  270. package/dist/web-app/assets/erDiagram-RLTQ6QDP-IHi93MdG.js.map +1 -0
  271. package/dist/web-app/assets/flowDiagram-HODETNUW-19bm7W5P.js +169 -0
  272. package/dist/web-app/assets/flowDiagram-HODETNUW-19bm7W5P.js.map +1 -0
  273. package/dist/web-app/assets/ganttDiagram-EL5Y4UJY-BvCqvyiB.js +293 -0
  274. package/dist/web-app/assets/ganttDiagram-EL5Y4UJY-BvCqvyiB.js.map +1 -0
  275. package/dist/web-app/assets/gitGraphDiagram-WWUBYQGX-G4Ubl8Fs.js +107 -0
  276. package/dist/web-app/assets/gitGraphDiagram-WWUBYQGX-G4Ubl8Fs.js.map +1 -0
  277. package/dist/web-app/assets/index-C4BdeFB9.css +1 -0
  278. package/dist/web-app/assets/index-Dtn3ZY6J.js +430 -0
  279. package/dist/web-app/assets/index-Dtn3ZY6J.js.map +1 -0
  280. package/dist/web-app/assets/infoDiagram-27XIBGKW-B6rtfbuC.js +3 -0
  281. package/dist/web-app/assets/infoDiagram-27XIBGKW-B6rtfbuC.js.map +1 -0
  282. package/dist/web-app/assets/init-Gi6I4Gst.js +2 -0
  283. package/dist/web-app/assets/init-Gi6I4Gst.js.map +1 -0
  284. package/dist/web-app/assets/ishikawaDiagram-5VMMS53U-Lp8q1D21.js +71 -0
  285. package/dist/web-app/assets/ishikawaDiagram-5VMMS53U-Lp8q1D21.js.map +1 -0
  286. package/dist/web-app/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  287. package/dist/web-app/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  288. package/dist/web-app/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  289. package/dist/web-app/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  290. package/dist/web-app/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  291. package/dist/web-app/assets/journeyDiagram-3NMN7TZE-Av03B9l3.js +140 -0
  292. package/dist/web-app/assets/journeyDiagram-3NMN7TZE-Av03B9l3.js.map +1 -0
  293. package/dist/web-app/assets/kanban-definition-UXKFOSKX-Ddd3_ejb.js +90 -0
  294. package/dist/web-app/assets/kanban-definition-UXKFOSKX-Ddd3_ejb.js.map +1 -0
  295. package/dist/web-app/assets/katex-HP8lGamR.js +258 -0
  296. package/dist/web-app/assets/katex-HP8lGamR.js.map +1 -0
  297. package/dist/web-app/assets/layout-CNjBeOlp.js +2 -0
  298. package/dist/web-app/assets/layout-CNjBeOlp.js.map +1 -0
  299. package/dist/web-app/assets/linear-DDONU9Ir.js +2 -0
  300. package/dist/web-app/assets/linear-DDONU9Ir.js.map +1 -0
  301. package/dist/web-app/assets/mermaid.core-B9X3C61k.js +313 -0
  302. package/dist/web-app/assets/mermaid.core-B9X3C61k.js.map +1 -0
  303. package/dist/web-app/assets/mindmap-definition-YA3MSWOX-I2UME2Ce.js +97 -0
  304. package/dist/web-app/assets/mindmap-definition-YA3MSWOX-I2UME2Ce.js.map +1 -0
  305. package/dist/web-app/assets/ordinal-Cboi1Yqb.js +2 -0
  306. package/dist/web-app/assets/ordinal-Cboi1Yqb.js.map +1 -0
  307. package/dist/web-app/assets/pegDiagram-XKGWAZYB-BER8lNbi.js +2 -0
  308. package/dist/web-app/assets/pegDiagram-XKGWAZYB-BER8lNbi.js.map +1 -0
  309. package/dist/web-app/assets/pieDiagram-E7YTZNPT-FVWVg8rH.js +40 -0
  310. package/dist/web-app/assets/pieDiagram-E7YTZNPT-FVWVg8rH.js.map +1 -0
  311. package/dist/web-app/assets/quadrantDiagram-AXDQQJYC-YlvT1u77.js +8 -0
  312. package/dist/web-app/assets/quadrantDiagram-AXDQQJYC-YlvT1u77.js.map +1 -0
  313. package/dist/web-app/assets/railroadDiagram-O6MQD6OU-CvxT6dMW.js +2 -0
  314. package/dist/web-app/assets/railroadDiagram-O6MQD6OU-CvxT6dMW.js.map +1 -0
  315. package/dist/web-app/assets/requirementDiagram-BXWQKSXE-Caav4EkP.js +85 -0
  316. package/dist/web-app/assets/requirementDiagram-BXWQKSXE-Caav4EkP.js.map +1 -0
  317. package/dist/web-app/assets/sankeyDiagram-P5KCCOFB-B5Uw70Gn.js +41 -0
  318. package/dist/web-app/assets/sankeyDiagram-P5KCCOFB-B5Uw70Gn.js.map +1 -0
  319. package/dist/web-app/assets/sequenceDiagram-WJ2MYXX4-D2SLKVJl.js +163 -0
  320. package/dist/web-app/assets/sequenceDiagram-WJ2MYXX4-D2SLKVJl.js.map +1 -0
  321. package/dist/web-app/assets/sizeCapture-INFHLROL-BVaBfbwa.js +2 -0
  322. package/dist/web-app/assets/sizeCapture-INFHLROL-BVaBfbwa.js.map +1 -0
  323. package/dist/web-app/assets/stateDiagram-D77RDMKH-BlV9y2tf.js +2 -0
  324. package/dist/web-app/assets/stateDiagram-D77RDMKH-BlV9y2tf.js.map +1 -0
  325. package/dist/web-app/assets/stateDiagram-v2-MP3YSRHH-BU4Tu2pp.js +2 -0
  326. package/dist/web-app/assets/stateDiagram-v2-MP3YSRHH-BU4Tu2pp.js.map +1 -0
  327. package/dist/web-app/assets/swimlanes-42K2YHIH-Pv3Wv8c7.js +2 -0
  328. package/dist/web-app/assets/swimlanes-42K2YHIH-Pv3Wv8c7.js.map +1 -0
  329. package/dist/web-app/assets/swimlanesDiagram-VR7AAH4N-BNKjOvPH.js +9 -0
  330. package/dist/web-app/assets/swimlanesDiagram-VR7AAH4N-BNKjOvPH.js.map +1 -0
  331. package/dist/web-app/assets/timeline-definition-24CTP7MA-C9TOilaR.js +121 -0
  332. package/dist/web-app/assets/timeline-definition-24CTP7MA-C9TOilaR.js.map +1 -0
  333. package/dist/web-app/assets/vennDiagram-4TSXK5OY-DeSDprN4.js +35 -0
  334. package/dist/web-app/assets/vennDiagram-4TSXK5OY-DeSDprN4.js.map +1 -0
  335. package/dist/web-app/assets/wardleyDiagram-VM6X3IG4-SulZn-nm.js +79 -0
  336. package/dist/web-app/assets/wardleyDiagram-VM6X3IG4-SulZn-nm.js.map +1 -0
  337. package/dist/web-app/assets/xychartDiagram-S5SC5T6Z-CC6MrQ0h.js +8 -0
  338. package/dist/web-app/assets/xychartDiagram-S5SC5T6Z-CC6MrQ0h.js.map +1 -0
  339. package/dist/web-app/fleet-index.html +19 -0
  340. package/dist/web-app/fleet.webmanifest +37 -0
  341. package/dist/web-app/icons/apple-touch-icon.png +0 -0
  342. package/dist/web-app/icons/favicon-16.png +0 -0
  343. package/dist/web-app/icons/favicon-32.png +0 -0
  344. package/dist/web-app/icons/icon-192-maskable.png +0 -0
  345. package/dist/web-app/icons/icon-192.png +0 -0
  346. package/dist/web-app/icons/icon-512-maskable.png +0 -0
  347. package/dist/web-app/icons/icon-512.png +0 -0
  348. package/dist/web-app/index.html +9 -7
  349. package/dist/web-app/manifest.webmanifest +10 -8
  350. package/dist/web-app/sw.js +210 -40
  351. package/dist/web-app/version.json +1 -0
  352. package/dist/web-app/workspace-install.sh +148 -0
  353. package/dist/workspace-enrollment.d.ts +28 -0
  354. package/dist/workspace-enrollment.js +129 -0
  355. package/docs/room-layouts.md +227 -0
  356. package/docs/task-workspaces.md +130 -0
  357. package/examples/room-layouts/fresh.yaml +27 -0
  358. package/examples/room-layouts/scoped.yaml +21 -0
  359. package/examples/room-layouts/shared.yaml +19 -0
  360. package/package.json +14 -17
  361. package/presets/README.md +78 -0
  362. package/presets/brain-catalog.json +8 -2
  363. package/presets/fleet/agent_templates/Engineer.yaml +5 -0
  364. package/presets/fleet/brains/claude-fable-5-1-high.yaml +5 -0
  365. package/presets/fleet/brains/claude-fable-5-1-low.yaml +5 -0
  366. package/presets/fleet/brains/claude-fable-5-1-max.yaml +5 -0
  367. package/presets/fleet/brains/claude-fable-5-1-medium.yaml +5 -0
  368. package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +5 -0
  369. package/presets/fleet/brains/claude-fable-5-high.yaml +1 -1
  370. package/presets/fleet/brains/claude-fable-5-low.yaml +1 -1
  371. package/presets/fleet/brains/claude-fable-5-max.yaml +1 -1
  372. package/presets/fleet/brains/claude-fable-5-medium.yaml +1 -1
  373. package/presets/fleet/brains/claude-fable-5-xhigh.yaml +1 -1
  374. package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +4 -0
  375. package/presets/fleet/brains/claude-opus-5-5-high.yaml +5 -0
  376. package/presets/fleet/brains/claude-opus-5-5-low.yaml +5 -0
  377. package/presets/fleet/brains/claude-opus-5-5-max.yaml +5 -0
  378. package/presets/fleet/brains/claude-opus-5-5-medium.yaml +5 -0
  379. package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +5 -0
  380. package/presets/fleet/brains/claude-opus-5-high.yaml +1 -1
  381. package/presets/fleet/brains/claude-opus-5-low.yaml +1 -1
  382. package/presets/fleet/brains/claude-opus-5-max.yaml +1 -1
  383. package/presets/fleet/brains/claude-opus-5-medium.yaml +1 -1
  384. package/presets/fleet/brains/claude-opus-5-xhigh.yaml +1 -1
  385. package/presets/fleet/brains/claude-sonnet-5-high.yaml +1 -1
  386. package/presets/fleet/brains/claude-sonnet-5-low.yaml +1 -1
  387. package/presets/fleet/brains/claude-sonnet-5-max.yaml +1 -1
  388. package/presets/fleet/brains/claude-sonnet-5-medium.yaml +1 -1
  389. package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +1 -1
  390. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +1 -1
  391. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +1 -1
  392. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +1 -1
  393. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +1 -1
  394. package/presets/fleet/brains/codex-gpt-5-4-high.yaml +1 -1
  395. package/presets/fleet/brains/codex-gpt-5-4-low.yaml +1 -1
  396. package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +1 -1
  397. package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +1 -1
  398. package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +1 -1
  399. package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +1 -1
  400. package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +1 -1
  401. package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +1 -1
  402. package/presets/fleet/brains/codex-gpt-5-5-high.yaml +1 -1
  403. package/presets/fleet/brains/codex-gpt-5-5-low.yaml +1 -1
  404. package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +1 -1
  405. package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +1 -1
  406. package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +1 -1
  407. package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +1 -1
  408. package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +1 -1
  409. package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +1 -1
  410. package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +1 -1
  411. package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +1 -1
  412. package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +1 -1
  413. package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +1 -1
  414. package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +1 -1
  415. package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +1 -1
  416. package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +1 -1
  417. package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +1 -1
  418. package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +1 -1
  419. package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +1 -1
  420. package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +1 -1
  421. package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +1 -1
  422. package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +1 -1
  423. package/presets/fleet/brains/codex-gpt-6-1-sol-high.yaml +5 -0
  424. package/presets/fleet/brains/codex-gpt-6-1-sol-low.yaml +5 -0
  425. package/presets/fleet/brains/codex-gpt-6-1-sol-max.yaml +5 -0
  426. package/presets/fleet/brains/codex-gpt-6-1-sol-medium.yaml +5 -0
  427. package/presets/fleet/brains/codex-gpt-6-1-sol-ultra.yaml +5 -0
  428. package/presets/fleet/brains/codex-gpt-6-1-sol-xhigh.yaml +5 -0
  429. package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +1 -1
  430. package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +1 -1
  431. package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +1 -1
  432. package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +1 -1
  433. package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +1 -1
  434. package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +1 -1
  435. package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +5 -0
  436. package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +5 -0
  437. package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +5 -0
  438. package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +5 -0
  439. package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +5 -0
  440. package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +5 -0
  441. package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +5 -0
  442. package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +5 -0
  443. package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +5 -0
  444. package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +5 -0
  445. package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +5 -0
  446. package/presets/fleet/roles/Coordinator.yaml +22 -0
  447. package/presets/fleet/roles/Engineer.yaml +40 -0
  448. package/presets/fleet/room_layouts/engineering.yaml +25 -0
  449. package/presets/fleet/room_layouts/pair.yaml +24 -0
  450. package/presets/fleet/room_layouts/single.yaml +17 -0
  451. package/presets/fleet/room_layouts/team.yaml +30 -0
  452. package/presets/fleet/room_templates/engineering.yaml +19 -0
  453. package/presets/manifest.json +2 -2
  454. package/dist/web-app/assets/index-59GF-gsZ.css +0 -1
  455. package/dist/web-app/assets/index-C0h3ALvs.js +0 -10
  456. package/dist/web-app/icons/ours-fleet-maskable.svg +0 -4
  457. package/dist/web-app/icons/ours-fleet.svg +0 -4
  458. package/dist/web-app/offline.html +0 -18
@@ -0,0 +1,44 @@
1
+ import { join } from 'node:path';
2
+ import { replaceFileAtomically } from '../atomic-file.js';
3
+ import { OBSERVED_USAGE_FILE } from './store.js';
4
+ const WINDOW_LABELS = {
5
+ five_hour: { label: '5h', mins: 300 },
6
+ seven_day: { label: 'weekly', mins: 10_080 },
7
+ seven_day_opus: { label: 'weekly · Opus', mins: 10_080 },
8
+ seven_day_sonnet: { label: 'weekly · Sonnet', mins: 10_080 },
9
+ };
10
+ /**
11
+ * Persist the Claude subscription rate-limit snapshot an agent session just
12
+ * received (SDK `rate_limit_event`, forwarded by claude-agent-acp as
13
+ * `usage_update._meta["_claude/rateLimit"]`). Only numbers and window names
14
+ * are kept. The web backend attributes it to the agent's pinned profile.
15
+ */
16
+ export function recordClaudeRateLimit(stateDir, info, now = new Date()) {
17
+ if (!info || typeof info !== 'object')
18
+ return false;
19
+ const raw = info;
20
+ const entries = raw.unifiedWindows && typeof raw.unifiedWindows === 'object'
21
+ ? Object.entries(raw.unifiedWindows)
22
+ : typeof raw.rateLimitType === 'string' ? [[raw.rateLimitType, raw]] : [];
23
+ const windows = [];
24
+ for (const [id, w] of entries) {
25
+ if (!/^[a-z0-9_]{1,40}$/.test(id) || !w || typeof w.utilization !== 'number' || !Number.isFinite(w.utilization))
26
+ continue;
27
+ const known = WINDOW_LABELS[id];
28
+ windows.push({
29
+ id, label: known?.label ?? id.replace(/_/g, ' '),
30
+ usedPercent: Math.round(Math.max(0, w.utilization) * 1000) / 10,
31
+ windowMins: known?.mins,
32
+ resetsAt: typeof w.resetsAt === 'number' ? new Date(w.resetsAt * 1000).toISOString() : undefined,
33
+ });
34
+ }
35
+ if (!windows.length)
36
+ return false;
37
+ try {
38
+ replaceFileAtomically(join(stateDir, OBSERVED_USAGE_FILE), `${JSON.stringify({ windows, observedAt: now.toISOString(), source: 'agent' })}\n`, 0o600);
39
+ return true;
40
+ }
41
+ catch {
42
+ return false;
43
+ }
44
+ }
@@ -0,0 +1,10 @@
1
+ export interface ClaudeProbeResult {
2
+ rateLimit?: unknown;
3
+ exhausted: boolean;
4
+ }
5
+ /**
6
+ * Ask Haiku for one short reply in the selected Claude home. The JSONL stream
7
+ * can contain a rate_limit_event; prompts, replies and CLI stderr are discarded.
8
+ * This runs only after an explicit UI action and has a time and spend bound.
9
+ */
10
+ export declare function probeClaudeRateLimit(bin: string, profileId: string, timeoutMs?: number): Promise<ClaudeProbeResult | undefined>;
@@ -0,0 +1,73 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { mkdirSync } from 'node:fs';
3
+ import { profileEnv } from './cli.js';
4
+ import { profileHome, subscriptionsRoot } from './store.js';
5
+ const LIMIT_ERROR = /\b(?:hit (?:your |the )?limit|reached (?:your |the )?(?:usage )?limit|usage limits? (?:reached|exceeded)|rate limits? (?:reached|exceeded)|rate_limit_error|quota exceeded|out of (?:usage|credits)|too many requests|429)\b/i;
6
+ /**
7
+ * Ask Haiku for one short reply in the selected Claude home. The JSONL stream
8
+ * can contain a rate_limit_event; prompts, replies and CLI stderr are discarded.
9
+ * This runs only after an explicit UI action and has a time and spend bound.
10
+ */
11
+ export function probeClaudeRateLimit(bin, profileId, timeoutMs = 75_000) {
12
+ const cwd = subscriptionsRoot();
13
+ mkdirSync(cwd, { recursive: true, mode: 0o700 });
14
+ return new Promise(resolve => {
15
+ const child = spawn(bin, [
16
+ '-p', 'Reply OK.', '--model', 'haiku', '--output-format', 'stream-json', '--verbose',
17
+ '--no-session-persistence', '--strict-mcp-config', '--tools', '',
18
+ '--system-prompt', 'Reply OK.', '--max-budget-usd', '0.05',
19
+ ], {
20
+ cwd, env: profileEnv('CLAUDE_CONFIG_DIR', profileId, profileHome('claude', profileId)),
21
+ stdio: ['ignore', 'pipe', 'pipe'],
22
+ });
23
+ let pending = '';
24
+ let rateLimit;
25
+ let exhausted = false;
26
+ let done = false;
27
+ const finish = (value) => {
28
+ if (done)
29
+ return;
30
+ done = true;
31
+ clearTimeout(timer);
32
+ resolve(value);
33
+ };
34
+ const timer = setTimeout(() => { child.kill('SIGKILL'); finish(); }, timeoutMs);
35
+ child.stdout.on('data', chunk => {
36
+ pending += String(chunk);
37
+ if (pending.length > 64 * 1024 && !pending.includes('\n')) {
38
+ child.kill('SIGKILL');
39
+ finish();
40
+ return;
41
+ }
42
+ for (let end = pending.indexOf('\n'); end >= 0; end = pending.indexOf('\n')) {
43
+ const line = pending.slice(0, end);
44
+ pending = pending.slice(end + 1);
45
+ if (line.length > 64 * 1024)
46
+ continue;
47
+ try {
48
+ const event = JSON.parse(line);
49
+ if (event.type === 'rate_limit_event')
50
+ rateLimit = event.rate_limit_info ?? event.rateLimitInfo ?? event.rate_limit ?? event.rateLimit ?? event;
51
+ if ((event.type === 'result' && event.is_error === true) || event.type === 'error') {
52
+ const error = event.error && typeof event.error === 'object' ? event.error : undefined;
53
+ const message = typeof event.result === 'string' ? event.result
54
+ : typeof event.error === 'string' ? event.error : error?.message;
55
+ if (typeof message === 'string' && LIMIT_ERROR.test(message.slice(0, 4000)))
56
+ exhausted = true;
57
+ }
58
+ }
59
+ catch { /* Other CLI output is never retained. */ }
60
+ }
61
+ });
62
+ // Some CLI failures are reported only on stderr. Inspect a small rolling
63
+ // fragment for a limit signal; never retain or return the raw text.
64
+ let stderrTail = '';
65
+ child.stderr.on('data', chunk => {
66
+ stderrTail = (stderrTail + String(chunk)).slice(-4000);
67
+ if (LIMIT_ERROR.test(stderrTail))
68
+ exhausted = true;
69
+ });
70
+ child.once('error', () => finish());
71
+ child.once('close', () => finish(rateLimit !== undefined || exhausted ? { rateLimit, exhausted } : undefined));
72
+ });
73
+ }
@@ -0,0 +1,102 @@
1
+ import { type SubscriptionBinaries, type UsageWindow } from './cli.js';
2
+ import { type LoginHandle, type LoginView } from './login.js';
3
+ import { type SubscriptionProvider } from './store.js';
4
+ export type HealthState = 'ok' | 'expiring' | 'expired' | 'signed_out' | 'error' | 'unknown';
5
+ export interface ProfileHealth {
6
+ state: HealthState;
7
+ checkedAt?: string;
8
+ detail?: string;
9
+ /** When the stored login stops refreshing (Claude, observed field). */
10
+ loginExpiresAt?: string;
11
+ }
12
+ export interface ProfileUsage {
13
+ windows: UsageWindow[];
14
+ observedAt?: string;
15
+ /** `pull`: asked the provider. `agent`: last value an agent session reported. */
16
+ source: 'pull' | 'agent' | 'probe' | 'none';
17
+ /** A probe received an explicit limit error, even if no percentages were sent. */
18
+ exhausted?: boolean;
19
+ }
20
+ export interface ProfileView {
21
+ id: string;
22
+ label: string;
23
+ active: boolean;
24
+ account?: {
25
+ email?: string;
26
+ plan?: string;
27
+ org?: string;
28
+ };
29
+ health: ProfileHealth;
30
+ usage: ProfileUsage;
31
+ /** Running agents whose session is pinned to this profile. */
32
+ agents: string[];
33
+ }
34
+ export interface ProviderView {
35
+ provider: SubscriptionProvider;
36
+ activeProfileId: string;
37
+ profiles: ProfileView[];
38
+ /** Running agents still on a profile other than the active one: restart to switch. */
39
+ staleAgents: Array<{
40
+ roleId: string;
41
+ profileId: string;
42
+ }>;
43
+ /** Running agents whose credentials come from env, not a Fleet profile. */
44
+ unmanagedAgents: Array<{
45
+ roleId: string;
46
+ via: string[];
47
+ }>;
48
+ }
49
+ export interface AgentRef {
50
+ roleId: string;
51
+ stateDir?: string;
52
+ running: boolean;
53
+ }
54
+ export interface SubscriptionServiceOptions {
55
+ agents(): Promise<AgentRef[]>;
56
+ binaries?: SubscriptionBinaries;
57
+ now?: () => number;
58
+ onChange?: () => void;
59
+ }
60
+ interface CheckResult {
61
+ health: ProfileHealth;
62
+ account?: ProfileView['account'];
63
+ usage?: ProfileUsage;
64
+ }
65
+ export declare class SubscriptionService {
66
+ private readonly options;
67
+ private readonly bins;
68
+ private readonly now;
69
+ private readonly checks;
70
+ private readonly running;
71
+ private readonly logins;
72
+ private readonly probes;
73
+ constructor(options: SubscriptionServiceOptions);
74
+ list(): Promise<ProviderView[]>;
75
+ /** Health + account + (Codex) usage for one profile. Concurrent calls share one run. */
76
+ check(provider: SubscriptionProvider, profileId: string): Promise<CheckResult>;
77
+ private checkClaude;
78
+ private checkCodex;
79
+ startLogin(provider: SubscriptionProvider, owner: string): Promise<LoginView>;
80
+ private completeLogin;
81
+ /** Query the official CLI for each existing profile, including the adopted default. */
82
+ private accountAlreadyAdded;
83
+ login(loginId: string, owner: string): LoginHandle;
84
+ setActive(provider: SubscriptionProvider, profileId: string): Promise<ProviderView>;
85
+ rename(provider: SubscriptionProvider, profileId: string, label: string): Promise<void>;
86
+ /**
87
+ * Remove a profile and its login. Refused while it is active or while any
88
+ * running agent is still pinned to it; the adopted default is never removed.
89
+ */
90
+ remove(provider: SubscriptionProvider, profileId: string): Promise<void>;
91
+ private profile;
92
+ /** An explicit, bounded Haiku request can refresh Claude usage for an idle profile. */
93
+ probeClaudeUsage(profileId: string): Promise<ProfileUsage>;
94
+ private probeUsageDir;
95
+ private probedUsage;
96
+ private pins;
97
+ /** Latest Claude rate-limit snapshot any agent on this profile reported. */
98
+ private observedUsage;
99
+ }
100
+ /** Read what `recordClaudeRateLimit` stored, dropping windows that already reset. */
101
+ export declare function readObservedClaudeUsage(stateDir: string, now?: number): ProfileUsage | undefined;
102
+ export {};
@@ -0,0 +1,364 @@
1
+ import { existsSync, mkdirSync, readFileSync, rmSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { FleetError } from '../application/errors.js';
4
+ import { replaceFileAtomically } from '../atomic-file.js';
5
+ import { claudeAuthStatus, claudeLoginExpiry, codexUsageWindows, defaultBinaries, profileEnv, withCodex, } from './cli.js';
6
+ import { HOME_ENV } from './launch.js';
7
+ import { ClaudeLogin, CodexDeviceLogin } from './login.js';
8
+ import { recordClaudeRateLimit } from './observed.js';
9
+ import { probeClaudeRateLimit } from './probe.js';
10
+ import { DEFAULT_PROFILE_ID, OBSERVED_USAGE_FILE, SUBSCRIPTION_PROVIDERS, createProfileHome, profileHome, readPin, readSubscriptionState, removeProfileHome, subscriptionsRoot, updateSubscriptionState, } from './store.js';
11
+ const EXPIRING_MS = 3 * 24 * 3600_000;
12
+ const STALE_CHECK_MS = 10 * 60_000;
13
+ export class SubscriptionService {
14
+ options;
15
+ bins;
16
+ now;
17
+ checks = new Map();
18
+ running = new Map();
19
+ logins = new Map();
20
+ probes = new Map();
21
+ constructor(options) {
22
+ this.options = options;
23
+ this.bins = options.binaries ?? defaultBinaries();
24
+ this.now = options.now ?? Date.now;
25
+ }
26
+ async list() {
27
+ const state = readSubscriptionState();
28
+ const pins = await this.pins();
29
+ return SUBSCRIPTION_PROVIDERS.map(provider => {
30
+ const p = state.providers[provider];
31
+ const profiles = p.profiles.map(profile => {
32
+ const key = `${provider}/${profile.id}`;
33
+ const cached = this.checks.get(key);
34
+ if (!cached || this.now() - Date.parse(cached.health.checkedAt ?? '0') > STALE_CHECK_MS)
35
+ void this.check(provider, profile.id).catch(() => { });
36
+ const observed = provider === 'claude' ? this.observedUsage(pins, profile.id) : undefined;
37
+ const probed = provider === 'claude' ? this.probedUsage(profile.id) : undefined;
38
+ const usage = observed && probed
39
+ ? (observed.observedAt ?? '') >= (probed.observedAt ?? '') ? observed : probed
40
+ : observed ?? probed;
41
+ return {
42
+ id: profile.id, label: profile.label, active: profile.id === p.activeProfileId,
43
+ account: cached?.account,
44
+ health: cached?.health ?? { state: 'unknown' },
45
+ usage: usage ?? cached?.usage ?? { windows: [], source: 'none' },
46
+ agents: pins.filter(x => x.running && x.provider === provider && x.profileId === profile.id).map(x => x.roleId),
47
+ };
48
+ });
49
+ return {
50
+ provider, activeProfileId: p.activeProfileId, profiles,
51
+ staleAgents: pins.filter(x => x.running && x.provider === provider && x.profileId !== p.activeProfileId)
52
+ .map(x => ({ roleId: x.roleId, profileId: x.profileId })),
53
+ unmanagedAgents: pins.filter(x => x.running && x.provider === provider && x.unmanaged?.length)
54
+ .map(x => ({ roleId: x.roleId, via: x.unmanaged })),
55
+ };
56
+ });
57
+ }
58
+ /** Health + account + (Codex) usage for one profile. Concurrent calls share one run. */
59
+ check(provider, profileId) {
60
+ this.profile(provider, profileId);
61
+ const key = `${provider}/${profileId}`;
62
+ const inflight = this.running.get(key);
63
+ if (inflight)
64
+ return inflight;
65
+ const run = (provider === 'claude' ? this.checkClaude(profileId) : this.checkCodex(profileId))
66
+ .catch((error) => ({
67
+ health: { state: 'error', checkedAt: new Date(this.now()).toISOString(), detail: error?.message?.includes('prerequisite') ? 'subscription state unreadable' : 'health check failed' },
68
+ }))
69
+ .then(result => { this.checks.set(key, result); this.options.onChange?.(); return result; })
70
+ .finally(() => this.running.delete(key));
71
+ this.running.set(key, run);
72
+ return run;
73
+ }
74
+ async checkClaude(profileId) {
75
+ const home = profileHome('claude', profileId);
76
+ const status = await claudeAuthStatus(this.bins.claude, profileEnv(HOME_ENV.claude, profileId, home));
77
+ const checkedAt = new Date(this.now()).toISOString();
78
+ const { refreshExpiresAt } = claudeLoginExpiry(home);
79
+ const loginExpiresAt = refreshExpiresAt ? new Date(refreshExpiresAt).toISOString() : undefined;
80
+ let state = status.loggedIn ? 'ok' : 'signed_out';
81
+ // The CLI reports the credential it would actually use (env, settings, helper…).
82
+ if (status.loggedIn && status.authMethod && status.authMethod !== 'claude.ai') {
83
+ return {
84
+ health: { state: 'error', checkedAt, detail: `not using a Claude subscription login (${status.authMethod.slice(0, 40)})` },
85
+ account: { email: status.email, plan: status.authMethod, org: status.org },
86
+ };
87
+ }
88
+ if (status.loggedIn && refreshExpiresAt !== undefined) {
89
+ if (refreshExpiresAt <= this.now())
90
+ state = 'expired';
91
+ else if (refreshExpiresAt - this.now() < EXPIRING_MS)
92
+ state = 'expiring';
93
+ }
94
+ return {
95
+ health: { state, checkedAt, loginExpiresAt },
96
+ account: status.loggedIn ? { email: status.email, plan: status.plan, org: status.org } : undefined,
97
+ };
98
+ }
99
+ async checkCodex(profileId) {
100
+ const home = profileHome('codex', profileId);
101
+ return withCodex(this.bins.codex, profileEnv(HOME_ENV.codex, profileId, home), async (server) => {
102
+ const checkedAt = new Date(this.now()).toISOString();
103
+ // refreshToken:false — a health check must not rotate a token agents use.
104
+ const read = await server.call('account/read', { refreshToken: false });
105
+ if (!read.account)
106
+ return { health: { state: 'signed_out', checkedAt } };
107
+ if (read.account.type !== 'chatgpt') {
108
+ return {
109
+ health: { state: 'error', checkedAt, detail: 'not a ChatGPT subscription login' },
110
+ account: { plan: read.account.type === 'apiKey' ? 'API key' : read.account.type },
111
+ };
112
+ }
113
+ const account = { email: read.account.email ?? undefined, plan: read.account.planType ?? 'chatgpt' };
114
+ try {
115
+ const limits = await server.call('account/rateLimits/read', {});
116
+ return {
117
+ health: { state: 'ok', checkedAt }, account,
118
+ usage: { windows: codexUsageWindows(limits), observedAt: checkedAt, source: 'pull' },
119
+ };
120
+ }
121
+ catch (error) {
122
+ const expired = /401|unauthori[sz]ed|expired|sign in again|log in again/i.test(String(error.message));
123
+ return { health: { state: expired ? 'expired' : 'error', checkedAt, detail: expired ? 'login expired: sign in again' : 'usage limits unavailable' }, account };
124
+ }
125
+ });
126
+ }
127
+ async startLogin(provider, owner) {
128
+ for (const [id, login] of this.logins) {
129
+ const view = login.view();
130
+ if (view.provider !== provider)
131
+ continue;
132
+ if (view.state === 'starting' || view.state === 'awaiting_user' || view.state === 'verifying')
133
+ login.cancel();
134
+ this.logins.delete(id);
135
+ }
136
+ const { id: profileId, home } = createProfileHome(provider);
137
+ const login = provider === 'claude'
138
+ ? new ClaudeLogin(owner, this.bins, home, profileId)
139
+ : new CodexDeviceLogin(owner, this.bins, home, profileId);
140
+ const loginId = login.view().loginId;
141
+ this.logins.set(loginId, login);
142
+ void this.completeLogin(provider, profileId, login);
143
+ // Give the CLI a moment to print its URL so the first response is useful.
144
+ const deadline = this.now() + 8_000;
145
+ while (login.view().state === 'starting' && this.now() < deadline)
146
+ await new Promise(resolve => setTimeout(resolve, 100));
147
+ return login.view();
148
+ }
149
+ async completeLogin(provider, profileId, login) {
150
+ let label;
151
+ let email;
152
+ let error = 'sign-in did not complete';
153
+ try {
154
+ if (login instanceof ClaudeLogin) {
155
+ const code = await login.exited;
156
+ if (code === 0 && login.view().state === 'verifying') {
157
+ const status = await claudeAuthStatus(this.bins.claude, profileEnv(HOME_ENV.claude, profileId, profileHome('claude', profileId)));
158
+ if (status.loggedIn) {
159
+ email = status.email;
160
+ label = status.email ?? 'Claude account';
161
+ }
162
+ }
163
+ else if (login.view().state === 'verifying')
164
+ error = 'the sign-in code was not accepted';
165
+ }
166
+ else if (await login.completed) {
167
+ const result = await this.checkCodex(profileId);
168
+ // Only a verified ChatGPT subscription login becomes a profile.
169
+ if (result.health.state === 'ok') {
170
+ email = result.account?.email;
171
+ label = email ?? 'Codex account';
172
+ this.checks.set(`${provider}/${profileId}`, result);
173
+ }
174
+ }
175
+ if (label !== undefined) {
176
+ if (!email?.trim()) {
177
+ label = undefined;
178
+ error = 'the CLI did not provide an account email';
179
+ }
180
+ else if (await this.accountAlreadyAdded(provider, email)) {
181
+ label = undefined;
182
+ error = 'this account is already added';
183
+ }
184
+ }
185
+ }
186
+ catch {
187
+ label = undefined;
188
+ error = 'sign-in could not be verified';
189
+ }
190
+ let registered = false;
191
+ if (label !== undefined) {
192
+ try {
193
+ // Registration and the login's terminal state change together inside the
194
+ // lock callback (synchronous), so a cancel or timeout that ran during the
195
+ // verification awaits wins and nothing is registered.
196
+ await updateSubscriptionState(state => {
197
+ if (login.view().state !== 'verifying' || !existsSync(profileHome(provider, profileId)))
198
+ return;
199
+ state.providers[provider].profiles.push({ id: profileId, label: label.slice(0, 80), createdAt: new Date(this.now()).toISOString() });
200
+ login.complete(true);
201
+ registered = true;
202
+ });
203
+ }
204
+ catch {
205
+ error = 'sign-in could not be saved';
206
+ }
207
+ }
208
+ if (!registered) {
209
+ login.complete(false, error);
210
+ removeProfileHome(provider, profileId);
211
+ this.checks.delete(`${provider}/${profileId}`);
212
+ }
213
+ this.options.onChange?.();
214
+ }
215
+ /** Query the official CLI for each existing profile, including the adopted default. */
216
+ async accountAlreadyAdded(provider, email) {
217
+ const wanted = email.trim().toLowerCase();
218
+ for (const profile of readSubscriptionState().providers[provider].profiles) {
219
+ const home = profileHome(provider, profile.id);
220
+ let existing;
221
+ if (provider === 'claude') {
222
+ const status = await claudeAuthStatus(this.bins.claude, profileEnv(HOME_ENV.claude, profile.id, home));
223
+ if (status.loggedIn)
224
+ existing = status.email;
225
+ }
226
+ else {
227
+ existing = await withCodex(this.bins.codex, profileEnv(HOME_ENV.codex, profile.id, home), async (server) => {
228
+ const read = await server.call('account/read', { refreshToken: false });
229
+ return read.account?.type === 'chatgpt' ? read.account.email ?? undefined : undefined;
230
+ });
231
+ }
232
+ if (existing?.trim().toLowerCase() === wanted)
233
+ return true;
234
+ }
235
+ return false;
236
+ }
237
+ login(loginId, owner) {
238
+ const login = this.logins.get(loginId);
239
+ // Unknown and foreign logins look the same: no probing of other sessions' logins.
240
+ if (!login || login.owner !== owner)
241
+ throw new FleetError('resource_not_found', 'login not found');
242
+ return login;
243
+ }
244
+ async setActive(provider, profileId) {
245
+ this.profile(provider, profileId);
246
+ await updateSubscriptionState(state => {
247
+ if (!state.providers[provider].profiles.some(p => p.id === profileId))
248
+ throw new FleetError('resource_not_found', 'profile not found');
249
+ state.providers[provider].activeProfileId = profileId;
250
+ });
251
+ this.options.onChange?.();
252
+ return (await this.list()).find(p => p.provider === provider);
253
+ }
254
+ async rename(provider, profileId, label) {
255
+ const clean = label.replace(/[\u0000-\u001f\u007f]/g, '').trim().slice(0, 80);
256
+ if (!clean)
257
+ throw new FleetError('invalid_request', 'label is required');
258
+ await updateSubscriptionState(state => {
259
+ const profile = state.providers[provider].profiles.find(p => p.id === profileId);
260
+ if (!profile)
261
+ throw new FleetError('resource_not_found', 'profile not found');
262
+ profile.label = clean;
263
+ });
264
+ this.options.onChange?.();
265
+ }
266
+ /**
267
+ * Remove a profile and its login. Refused while it is active or while any
268
+ * running agent is still pinned to it; the adopted default is never removed.
269
+ */
270
+ async remove(provider, profileId) {
271
+ if (profileId === DEFAULT_PROFILE_ID)
272
+ throw new FleetError('conflict', 'the default profile is your existing CLI login and cannot be removed here');
273
+ const pinned = (await this.pins()).filter(x => x.running && x.provider === provider && x.profileId === profileId);
274
+ if (pinned.length)
275
+ throw new FleetError('conflict', `profile is in use by running agents: ${pinned.map(x => x.roleId).join(', ')}; restart or stop them first`);
276
+ await updateSubscriptionState(state => {
277
+ const p = state.providers[provider];
278
+ if (!p.profiles.some(x => x.id === profileId))
279
+ throw new FleetError('resource_not_found', 'profile not found');
280
+ if (p.activeProfileId === profileId)
281
+ throw new FleetError('conflict', 'switch to another profile before removing the active one');
282
+ p.profiles = p.profiles.filter(x => x.id !== profileId);
283
+ });
284
+ removeProfileHome(provider, profileId);
285
+ if (provider === 'claude')
286
+ rmSync(this.probeUsageDir(profileId), { recursive: true, force: true });
287
+ this.checks.delete(`${provider}/${profileId}`);
288
+ this.options.onChange?.();
289
+ }
290
+ profile(provider, profileId) {
291
+ if (!readSubscriptionState().providers[provider].profiles.some(p => p.id === profileId))
292
+ throw new FleetError('resource_not_found', 'profile not found');
293
+ }
294
+ /** An explicit, bounded Haiku request can refresh Claude usage for an idle profile. */
295
+ probeClaudeUsage(profileId) {
296
+ this.profile('claude', profileId);
297
+ const inFlight = this.probes.get(profileId);
298
+ if (inFlight)
299
+ return inFlight;
300
+ const run = (async () => {
301
+ const dir = this.probeUsageDir(profileId);
302
+ const result = await probeClaudeRateLimit(this.bins.claude, profileId);
303
+ if (result) {
304
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
305
+ const observedAt = new Date(this.now()).toISOString();
306
+ const windows = result.rateLimit && recordClaudeRateLimit(dir, result.rateLimit, new Date(this.now()))
307
+ ? readObservedClaudeUsage(dir, this.now())?.windows ?? [] : [];
308
+ if (windows.length || result.exhausted) {
309
+ const usage = { windows, observedAt, source: 'probe', ...(result.exhausted ? { exhausted: true } : {}) };
310
+ replaceFileAtomically(join(dir, OBSERVED_USAGE_FILE), `${JSON.stringify(usage)}\n`, 0o600);
311
+ this.options.onChange?.();
312
+ return usage;
313
+ }
314
+ }
315
+ rmSync(join(dir, OBSERVED_USAGE_FILE), { force: true });
316
+ this.options.onChange?.();
317
+ return { windows: [], source: 'none' };
318
+ })().catch(() => ({ windows: [], source: 'none' }))
319
+ .finally(() => this.probes.delete(profileId));
320
+ this.probes.set(profileId, run);
321
+ return run;
322
+ }
323
+ probeUsageDir(profileId) {
324
+ return join(subscriptionsRoot(), 'observed', 'claude', profileId);
325
+ }
326
+ probedUsage(profileId) {
327
+ const usage = readObservedClaudeUsage(this.probeUsageDir(profileId), this.now());
328
+ return usage && (usage.windows.length || usage.exhausted) ? { ...usage, source: 'probe' } : undefined;
329
+ }
330
+ async pins() {
331
+ const result = [];
332
+ for (const agent of await this.options.agents()) {
333
+ if (!agent.stateDir)
334
+ continue;
335
+ const pin = readPin(agent.stateDir);
336
+ if (pin)
337
+ result.push({ roleId: agent.roleId, stateDir: agent.stateDir, running: agent.running, provider: pin.provider, profileId: pin.profileId, unmanaged: pin.unmanaged });
338
+ }
339
+ return result;
340
+ }
341
+ /** Latest Claude rate-limit snapshot any agent on this profile reported. */
342
+ observedUsage(pins, profileId) {
343
+ let best;
344
+ for (const pin of pins) {
345
+ if (pin.provider !== 'claude' || pin.profileId !== profileId)
346
+ continue;
347
+ const observed = readObservedClaudeUsage(pin.stateDir);
348
+ if (observed && (!best || (observed.observedAt ?? '') > (best.observedAt ?? '')))
349
+ best = observed;
350
+ }
351
+ return best;
352
+ }
353
+ }
354
+ /** Read what `recordClaudeRateLimit` stored, dropping windows that already reset. */
355
+ export function readObservedClaudeUsage(stateDir, now = Date.now()) {
356
+ try {
357
+ const raw = JSON.parse(readFileSync(join(stateDir, OBSERVED_USAGE_FILE), 'utf8'));
358
+ const windows = (raw.windows ?? []).filter(w => !w.resetsAt || Date.parse(w.resetsAt) > now);
359
+ return { windows, observedAt: raw.observedAt, source: 'agent', ...(raw.exhausted === true ? { exhausted: true } : {}) };
360
+ }
361
+ catch {
362
+ return undefined;
363
+ }
364
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Fleet-wide Claude Code / Codex subscription profiles.
3
+ *
4
+ * A profile is one CLI home directory holding one account's login. Fleet never
5
+ * reads or returns token values; the official CLIs own the credential files.
6
+ * The `default` profile adopts the operator's existing `~/.claude` / `~/.codex`
7
+ * in place, so a fleet that never adds a profile launches exactly as before.
8
+ * Exactly one profile per provider is active; switching only affects agents
9
+ * launched afterwards (running sessions stay pinned until restarted).
10
+ */
11
+ export type SubscriptionProvider = 'claude' | 'codex';
12
+ export declare const SUBSCRIPTION_PROVIDERS: SubscriptionProvider[];
13
+ export declare const DEFAULT_PROFILE_ID = "default";
14
+ export interface SubscriptionProfile {
15
+ id: string;
16
+ label: string;
17
+ createdAt: string;
18
+ }
19
+ export interface ProviderState {
20
+ activeProfileId: string;
21
+ profiles: SubscriptionProfile[];
22
+ }
23
+ export interface SubscriptionState {
24
+ version: 1;
25
+ providers: Record<SubscriptionProvider, ProviderState>;
26
+ }
27
+ /** Recorded in an agent's state dir at launch: which profile the session uses. */
28
+ export interface SubscriptionPin {
29
+ provider: SubscriptionProvider;
30
+ profileId: string;
31
+ /** Explicit CLI home, absent for the adopted default (the CLI's own default). */
32
+ home?: string;
33
+ /**
34
+ * Default profile only: credential env (role or supervisor) that makes this
35
+ * session authenticate outside Fleet's profiles. Reported, not managed.
36
+ */
37
+ unmanaged?: string[];
38
+ pinnedAt: string;
39
+ }
40
+ export declare const PIN_FILE = ".subscription-pin.json";
41
+ export declare const OBSERVED_USAGE_FILE = ".subscription-usage.json";
42
+ export declare const PROFILE_ID_RE: RegExp;
43
+ export declare const subscriptionsRoot: () => string;
44
+ /**
45
+ * The CLI's own default home, adopted as the `default` profile. This follows
46
+ * `$HOME` like the CLIs do, not OURS_FLEET_HOME (which may relocate Fleet state).
47
+ */
48
+ export declare const baseHome: (provider: SubscriptionProvider) => string;
49
+ export declare function isProvider(value: unknown): value is SubscriptionProvider;
50
+ export declare function profileHome(provider: SubscriptionProvider, profileId: string): string;
51
+ /**
52
+ * Read the state. The file is only ever replaced by an atomic rename, so a
53
+ * reader (including a launching agent) sees either the old or the new active
54
+ * profile, never a torn write.
55
+ */
56
+ export declare function readSubscriptionState(): SubscriptionState;
57
+ /** Serialised read-modify-write of the state under a cross-process lock. */
58
+ export declare function updateSubscriptionState<T>(fn: (state: SubscriptionState) => T): Promise<T>;
59
+ /**
60
+ * Create an empty profile home (not yet registered). Shared, non-history
61
+ * configuration is symlinked from the base home; credentials and session
62
+ * history stay per profile.
63
+ */
64
+ export declare function createProfileHome(provider: SubscriptionProvider): {
65
+ id: string;
66
+ home: string;
67
+ };
68
+ /** Remove a non-default profile home. Never touches the adopted default. */
69
+ export declare function removeProfileHome(provider: SubscriptionProvider, profileId: string): void;
70
+ export declare function readPin(stateDir: string, provider?: SubscriptionProvider): SubscriptionPin | undefined;
71
+ export declare function writePin(stateDir: string, pin: SubscriptionPin): void;