@ours.network/fleet 1.2.0-nightly.6 → 1.2.0-nightly.61

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 (389) hide show
  1. package/README.md +287 -65
  2. package/dist/account-origin.d.ts +2 -0
  3. package/dist/account-origin.js +6 -0
  4. package/dist/agent-ours/bridge.js +35 -9
  5. package/dist/agent-ours/correspondence.d.ts +10 -0
  6. package/dist/agent-ours/correspondence.js +1 -0
  7. package/dist/agent-ours/file-rpc.d.ts +12 -0
  8. package/dist/agent-ours/file-rpc.js +18 -4
  9. package/dist/agent-ours/harness.js +9 -1
  10. package/dist/agent-ours/mcp-endpoint.d.ts +4 -0
  11. package/dist/agent-ours/mcp-endpoint.js +5 -0
  12. package/dist/agent-ours/runtime.d.ts +9 -1
  13. package/dist/agent-ours/runtime.js +50 -2
  14. package/dist/agent-ours/service.d.ts +21 -1
  15. package/dist/agent-ours/service.js +94 -14
  16. package/dist/application/codex-models.d.ts +15 -0
  17. package/dist/application/codex-models.js +56 -0
  18. package/dist/application/conversation-history.d.ts +27 -0
  19. package/dist/application/conversation-history.js +212 -0
  20. package/dist/application/fleet-query-service.d.ts +1 -1
  21. package/dist/application/fleet-query-service.js +2 -2
  22. package/dist/application/legacy-supervisor-identity.d.ts +7 -0
  23. package/dist/application/legacy-supervisor-identity.js +43 -0
  24. package/dist/application/log-service.js +8 -8
  25. package/dist/application/model-catalog.d.ts +1 -1
  26. package/dist/application/model-catalog.js +5 -2
  27. package/dist/application/preset-provenance.d.ts +19 -0
  28. package/dist/application/preset-provenance.js +59 -0
  29. package/dist/application/role-creation-service.js +3 -2
  30. package/dist/application/role-removal-service.js +9 -1
  31. package/dist/application/role-repository.js +19 -2
  32. package/dist/application/room-layout-definitions.d.ts +45 -0
  33. package/dist/application/room-layout-definitions.js +130 -0
  34. package/dist/application/session-control.d.ts +30 -0
  35. package/dist/application/session-control.js +72 -3
  36. package/dist/application/supervisor-ours-tools.d.ts +159 -0
  37. package/dist/application/supervisor-ours-tools.js +115 -0
  38. package/dist/application/task-layouts.d.ts +91 -0
  39. package/dist/application/task-layouts.js +259 -0
  40. package/dist/application/task-room-service.d.ts +99 -3
  41. package/dist/application/task-room-service.js +285 -32
  42. package/dist/application/types.d.ts +2 -0
  43. package/dist/atomic-file.d.ts +5 -0
  44. package/dist/atomic-file.js +62 -1
  45. package/dist/briefing.d.ts +3 -1
  46. package/dist/briefing.js +70 -30
  47. package/dist/build-info.json +7 -4
  48. package/dist/capabilities.d.ts +6 -1
  49. package/dist/capabilities.js +8 -0
  50. package/dist/cli.js +407 -37
  51. package/dist/client-profile.d.ts +5 -17
  52. package/dist/client-profile.js +4 -127
  53. package/dist/config.d.ts +45 -0
  54. package/dist/config.js +57 -6
  55. package/dist/creation.d.ts +1 -1
  56. package/dist/creation.js +4 -4
  57. package/dist/daemon-recovery.d.ts +1 -8
  58. package/dist/daemon-recovery.js +8 -113
  59. package/dist/docs.d.ts +1 -1
  60. package/dist/docs.js +123 -19
  61. package/dist/doctor.d.ts +1 -1
  62. package/dist/doctor.js +55 -60
  63. package/dist/erased-resources.d.ts +9 -0
  64. package/dist/erased-resources.js +87 -0
  65. package/dist/file-delivery/reader.d.ts +15 -0
  66. package/dist/file-delivery/reader.js +63 -0
  67. package/dist/file-delivery/store.d.ts +15 -0
  68. package/dist/file-delivery/store.js +124 -0
  69. package/dist/file-delivery/types.d.ts +19 -0
  70. package/dist/file-delivery/types.js +1 -0
  71. package/dist/fleet-command-audit.d.ts +7 -0
  72. package/dist/fleet-command-audit.js +26 -9
  73. package/dist/harness/agent-session.d.ts +2 -0
  74. package/dist/harness/claude-code-session.js +1 -1
  75. package/dist/harness/claude-code.js +20 -11
  76. package/dist/harness/claude-runtime.d.ts +12 -0
  77. package/dist/harness/claude-runtime.js +43 -0
  78. package/dist/harness/codex-app-server-proxy.d.ts +4 -1
  79. package/dist/harness/codex-app-server-proxy.js +41 -5
  80. package/dist/harness/codex-reasoning.d.ts +5 -0
  81. package/dist/harness/codex-reasoning.js +12 -0
  82. package/dist/harness/codex-runtime.d.ts +11 -1
  83. package/dist/harness/codex-runtime.js +31 -4
  84. package/dist/harness/codex-session.js +2 -2
  85. package/dist/harness/codex.js +23 -12
  86. package/dist/init-wizard.d.ts +2 -0
  87. package/dist/init-wizard.js +38 -19
  88. package/dist/isolation/policy.js +6 -1
  89. package/dist/isolation/types.d.ts +5 -0
  90. package/dist/managed-cli.d.ts +253 -0
  91. package/dist/managed-cli.js +923 -0
  92. package/dist/managed-recovery.d.ts +13 -0
  93. package/dist/managed-recovery.js +31 -0
  94. package/dist/minimal-setup.d.ts +2 -0
  95. package/dist/minimal-setup.js +44 -0
  96. package/dist/monitor.d.ts +8 -34
  97. package/dist/monitor.js +89 -152
  98. package/dist/notifications/fleet-producer.d.ts +19 -0
  99. package/dist/notifications/fleet-producer.js +208 -0
  100. package/dist/notifications/outbox.d.ts +46 -0
  101. package/dist/notifications/outbox.js +158 -0
  102. package/dist/notifications/target-cleanup.d.ts +8 -0
  103. package/dist/notifications/target-cleanup.js +41 -0
  104. package/dist/notifications/task-producer.d.ts +18 -0
  105. package/dist/notifications/task-producer.js +67 -0
  106. package/dist/ops.d.ts +2 -0
  107. package/dist/ops.js +20 -3
  108. package/dist/owner-channel/channel.js +17 -6
  109. package/dist/owner-channel/commands.d.ts +4 -0
  110. package/dist/owner-channel/commands.js +8 -0
  111. package/dist/owner-channel/lifecycle-outbox.js +4 -3
  112. package/dist/owner-channel/ours-client.js +2 -17
  113. package/dist/paths.d.ts +2 -0
  114. package/dist/paths.js +2 -0
  115. package/dist/preset-migration.js +25 -0
  116. package/dist/resolved-plan.js +1 -0
  117. package/dist/rooms-tasks/add-member.d.ts +48 -0
  118. package/dist/rooms-tasks/add-member.js +83 -0
  119. package/dist/rooms-tasks/archived-absence.d.ts +8 -0
  120. package/dist/rooms-tasks/archived-absence.js +48 -0
  121. package/dist/rooms-tasks/cli.js +65 -5
  122. package/dist/rooms-tasks/close.d.ts +6 -2
  123. package/dist/rooms-tasks/close.js +246 -37
  124. package/dist/rooms-tasks/config.d.ts +1 -1
  125. package/dist/rooms-tasks/config.js +11 -4
  126. package/dist/rooms-tasks/cowork-adapter.d.ts +2 -4
  127. package/dist/rooms-tasks/cowork-adapter.js +7 -121
  128. package/dist/rooms-tasks/cowork-http.js +29 -1
  129. package/dist/rooms-tasks/deletion.d.ts +1 -1
  130. package/dist/rooms-tasks/deletion.js +84 -5
  131. package/dist/rooms-tasks/erasure.d.ts +6 -0
  132. package/dist/rooms-tasks/erasure.js +173 -0
  133. package/dist/rooms-tasks/external-worker.d.ts +1 -1
  134. package/dist/rooms-tasks/external-worker.js +3 -4
  135. package/dist/rooms-tasks/layout-binding-client.d.ts +11 -0
  136. package/dist/rooms-tasks/layout-binding-client.js +56 -0
  137. package/dist/rooms-tasks/layout-binding-grants.d.ts +21 -0
  138. package/dist/rooms-tasks/layout-binding-grants.js +128 -0
  139. package/dist/rooms-tasks/layout-cli.d.ts +4 -0
  140. package/dist/rooms-tasks/layout-cli.js +107 -0
  141. package/dist/rooms-tasks/layout-config.d.ts +11 -0
  142. package/dist/rooms-tasks/layout-config.js +89 -0
  143. package/dist/rooms-tasks/layout-control.d.ts +21 -0
  144. package/dist/rooms-tasks/layout-control.js +52 -0
  145. package/dist/rooms-tasks/layout-member-retirement.d.ts +10 -0
  146. package/dist/rooms-tasks/layout-member-retirement.js +90 -0
  147. package/dist/rooms-tasks/layout-service.d.ts +43 -0
  148. package/dist/rooms-tasks/layout-service.js +203 -0
  149. package/dist/rooms-tasks/layout.d.ts +121 -0
  150. package/dist/rooms-tasks/layout.js +285 -0
  151. package/dist/rooms-tasks/legacy-task-member.d.ts +19 -0
  152. package/dist/rooms-tasks/legacy-task-member.js +116 -0
  153. package/dist/rooms-tasks/live-readiness.d.ts +18 -0
  154. package/dist/rooms-tasks/live-readiness.js +85 -0
  155. package/dist/rooms-tasks/member-ownership.d.ts +5 -0
  156. package/dist/rooms-tasks/member-ownership.js +40 -0
  157. package/dist/rooms-tasks/member-startup.d.ts +1 -0
  158. package/dist/rooms-tasks/member-startup.js +2 -0
  159. package/dist/rooms-tasks/provision.d.ts +3 -0
  160. package/dist/rooms-tasks/provision.js +101 -49
  161. package/dist/rooms-tasks/recovery.d.ts +19 -0
  162. package/dist/rooms-tasks/recovery.js +110 -0
  163. package/dist/rooms-tasks/room-state.d.ts +4 -2
  164. package/dist/rooms-tasks/room-state.js +47 -8
  165. package/dist/rooms-tasks/task-service-retirement.d.ts +11 -0
  166. package/dist/rooms-tasks/task-service-retirement.js +21 -0
  167. package/dist/rooms-tasks/task-state.d.ts +51 -8
  168. package/dist/rooms-tasks/task-state.js +245 -25
  169. package/dist/rooms-tasks/types.d.ts +40 -0
  170. package/dist/rooms-tasks/workspace-artifacts.d.ts +6 -0
  171. package/dist/rooms-tasks/workspace-artifacts.js +93 -0
  172. package/dist/rooms-tasks/workspace.d.ts +35 -0
  173. package/dist/rooms-tasks/workspace.js +325 -0
  174. package/dist/runner.d.ts +25 -3
  175. package/dist/runner.js +291 -49
  176. package/dist/session/acp.d.ts +53 -1
  177. package/dist/session/acp.js +216 -40
  178. package/dist/session/arbiter.d.ts +11 -0
  179. package/dist/session/arbiter.js +43 -2
  180. package/dist/session/codex-app-server.d.ts +32 -0
  181. package/dist/session/codex-app-server.js +137 -23
  182. package/dist/session/control.d.ts +12 -1
  183. package/dist/session/control.js +68 -3
  184. package/dist/session/conversation-normalizer.d.ts +2 -1
  185. package/dist/session/conversation-normalizer.js +1 -1
  186. package/dist/session/conversation-store.d.ts +1 -1
  187. package/dist/session/conversation-store.js +7 -11
  188. package/dist/session/conversation-types.d.ts +28 -3
  189. package/dist/session/prompt-batch.d.ts +63 -0
  190. package/dist/session/prompt-batch.js +86 -0
  191. package/dist/session/task-notice.d.ts +14 -0
  192. package/dist/session/task-notice.js +13 -0
  193. package/dist/session/types.d.ts +14 -0
  194. package/dist/setup-tunnel-preflight.d.ts +34 -0
  195. package/dist/setup-tunnel-preflight.js +62 -0
  196. package/dist/socket-path.d.ts +2 -0
  197. package/dist/socket-path.js +22 -0
  198. package/dist/spawn.d.ts +5 -1
  199. package/dist/spawn.js +40 -4
  200. package/dist/startup-prompt.d.ts +4 -0
  201. package/dist/startup-prompt.js +36 -0
  202. package/dist/startup-readiness.d.ts +13 -0
  203. package/dist/startup-readiness.js +38 -0
  204. package/dist/startup-telemetry.d.ts +16 -0
  205. package/dist/startup-telemetry.js +46 -0
  206. package/dist/subscriptions/cli.d.ts +91 -0
  207. package/dist/subscriptions/cli.js +223 -0
  208. package/dist/subscriptions/launch.d.ts +37 -0
  209. package/dist/subscriptions/launch.js +93 -0
  210. package/dist/subscriptions/login.d.ts +75 -0
  211. package/dist/subscriptions/login.js +182 -0
  212. package/dist/subscriptions/observed.d.ts +7 -0
  213. package/dist/subscriptions/observed.js +44 -0
  214. package/dist/subscriptions/probe.d.ts +10 -0
  215. package/dist/subscriptions/probe.js +73 -0
  216. package/dist/subscriptions/service.d.ts +102 -0
  217. package/dist/subscriptions/service.js +364 -0
  218. package/dist/subscriptions/store.d.ts +71 -0
  219. package/dist/subscriptions/store.js +140 -0
  220. package/dist/supervisor/adoption.d.ts +8 -0
  221. package/dist/supervisor/adoption.js +78 -0
  222. package/dist/supervisor/catalog.d.ts +62 -0
  223. package/dist/supervisor/catalog.js +254 -0
  224. package/dist/supervisor/fleet.d.ts +8 -0
  225. package/dist/supervisor/fleet.js +89 -0
  226. package/dist/supervisor/index.js +3 -6
  227. package/dist/supervisor/launchd.d.ts +1 -1
  228. package/dist/supervisor/launchd.js +26 -13
  229. package/dist/supervisor/legacy.d.ts +35 -0
  230. package/dist/supervisor/legacy.js +524 -0
  231. package/dist/supervisor/manager.d.ts +15 -0
  232. package/dist/supervisor/manager.js +270 -0
  233. package/dist/supervisor/scope.d.ts +4 -0
  234. package/dist/supervisor/scope.js +10 -0
  235. package/dist/supervisor/systemd.d.ts +2 -2
  236. package/dist/supervisor/systemd.js +19 -33
  237. package/dist/task-supervision.d.ts +9 -0
  238. package/dist/task-supervision.js +145 -0
  239. package/dist/temp-idle.d.ts +17 -0
  240. package/dist/temp-idle.js +42 -0
  241. package/dist/temp-lifecycle.d.ts +23 -8
  242. package/dist/temp-lifecycle.js +113 -65
  243. package/dist/temp-supervisor-recovery.d.ts +1 -1
  244. package/dist/temp-supervisor-recovery.js +2 -2
  245. package/dist/tunnel-setup-completion.d.ts +7 -0
  246. package/dist/tunnel-setup-completion.js +13 -0
  247. package/dist/tunnel-setup-input.d.ts +19 -0
  248. package/dist/tunnel-setup-input.js +124 -0
  249. package/dist/web/agent-attachments.d.ts +29 -0
  250. package/dist/web/agent-attachments.js +162 -0
  251. package/dist/web/auth.d.ts +63 -2
  252. package/dist/web/auth.js +191 -10
  253. package/dist/web/available-port.d.ts +2 -0
  254. package/dist/web/available-port.js +23 -0
  255. package/dist/web/control.d.ts +3 -3
  256. package/dist/web/control.js +10 -8
  257. package/dist/web/fleet-config-service.d.ts +4 -1
  258. package/dist/web/fleet-config-service.js +46 -15
  259. package/dist/web/prefix-gateway.d.ts +32 -0
  260. package/dist/web/prefix-gateway.js +238 -0
  261. package/dist/web/runtime.d.ts +3 -1
  262. package/dist/web/runtime.js +116 -31
  263. package/dist/web/server.d.ts +16 -0
  264. package/dist/web/server.js +654 -55
  265. package/dist/web/service.d.ts +11 -2
  266. package/dist/web/service.js +30 -4
  267. package/dist/web/workspace-devices.d.ts +107 -0
  268. package/dist/web/workspace-devices.js +277 -0
  269. package/dist/web/workspace-tunnel.d.ts +1 -0
  270. package/dist/web/workspace-tunnel.js +34 -0
  271. package/dist/workspace-enrollment.d.ts +82 -0
  272. package/dist/workspace-enrollment.js +566 -0
  273. package/dist/workspace-replacement.d.ts +66 -0
  274. package/dist/workspace-replacement.js +220 -0
  275. package/docs/acp-files.md +146 -0
  276. package/docs/fleet-migration.md +54 -0
  277. package/docs/live-voice.md +13 -0
  278. package/docs/room-layouts.md +227 -0
  279. package/docs/task-workspaces.md +225 -0
  280. package/docs/validation/managed-cli-permissions.md +421 -0
  281. package/examples/room-layouts/fresh.yaml +27 -0
  282. package/examples/room-layouts/scoped.yaml +21 -0
  283. package/examples/room-layouts/shared.yaml +19 -0
  284. package/package.json +20 -19
  285. package/presets/README.md +78 -0
  286. package/presets/brain-catalog.json +8 -2
  287. package/presets/fleet/agent_templates/Critic.yaml +1 -1
  288. package/presets/fleet/agent_templates/Developer.yaml +1 -1
  289. package/presets/fleet/agent_templates/Engineer.yaml +5 -0
  290. package/presets/fleet/agent_templates/LocalCoordinator.yaml +1 -1
  291. package/presets/fleet/agents/FleetCoordinator.yaml +1 -1
  292. package/presets/fleet/brains/claude-fable-5-1-high.yaml +5 -0
  293. package/presets/fleet/brains/claude-fable-5-1-low.yaml +5 -0
  294. package/presets/fleet/brains/claude-fable-5-1-max.yaml +5 -0
  295. package/presets/fleet/brains/claude-fable-5-1-medium.yaml +5 -0
  296. package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +5 -0
  297. package/presets/fleet/brains/claude-fable-5-high.yaml +1 -1
  298. package/presets/fleet/brains/claude-fable-5-low.yaml +1 -1
  299. package/presets/fleet/brains/claude-fable-5-max.yaml +1 -1
  300. package/presets/fleet/brains/claude-fable-5-medium.yaml +1 -1
  301. package/presets/fleet/brains/claude-fable-5-xhigh.yaml +1 -1
  302. package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +4 -0
  303. package/presets/fleet/brains/claude-opus-5-5-high.yaml +5 -0
  304. package/presets/fleet/brains/claude-opus-5-5-low.yaml +5 -0
  305. package/presets/fleet/brains/claude-opus-5-5-max.yaml +5 -0
  306. package/presets/fleet/brains/claude-opus-5-5-medium.yaml +5 -0
  307. package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +5 -0
  308. package/presets/fleet/brains/claude-opus-5-high.yaml +1 -1
  309. package/presets/fleet/brains/claude-opus-5-low.yaml +1 -1
  310. package/presets/fleet/brains/claude-opus-5-max.yaml +1 -1
  311. package/presets/fleet/brains/claude-opus-5-medium.yaml +1 -1
  312. package/presets/fleet/brains/claude-opus-5-xhigh.yaml +1 -1
  313. package/presets/fleet/brains/claude-sonnet-5-high.yaml +1 -1
  314. package/presets/fleet/brains/claude-sonnet-5-low.yaml +1 -1
  315. package/presets/fleet/brains/claude-sonnet-5-max.yaml +1 -1
  316. package/presets/fleet/brains/claude-sonnet-5-medium.yaml +1 -1
  317. package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +1 -1
  318. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +1 -1
  319. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +1 -1
  320. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +1 -1
  321. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +1 -1
  322. package/presets/fleet/brains/codex-gpt-5-4-high.yaml +1 -1
  323. package/presets/fleet/brains/codex-gpt-5-4-low.yaml +1 -1
  324. package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +1 -1
  325. package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +1 -1
  326. package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +1 -1
  327. package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +1 -1
  328. package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +1 -1
  329. package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +1 -1
  330. package/presets/fleet/brains/codex-gpt-5-5-high.yaml +1 -1
  331. package/presets/fleet/brains/codex-gpt-5-5-low.yaml +1 -1
  332. package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +1 -1
  333. package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +1 -1
  334. package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +1 -1
  335. package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +1 -1
  336. package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +1 -1
  337. package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +1 -1
  338. package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +1 -1
  339. package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +1 -1
  340. package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +1 -1
  341. package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +1 -1
  342. package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +1 -1
  343. package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +1 -1
  344. package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +1 -1
  345. package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +1 -1
  346. package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +1 -1
  347. package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +1 -1
  348. package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +1 -1
  349. package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +1 -1
  350. package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +1 -1
  351. package/presets/fleet/brains/codex-gpt-6-1-sol-high.yaml +5 -0
  352. package/presets/fleet/brains/codex-gpt-6-1-sol-low.yaml +5 -0
  353. package/presets/fleet/brains/codex-gpt-6-1-sol-max.yaml +5 -0
  354. package/presets/fleet/brains/codex-gpt-6-1-sol-medium.yaml +5 -0
  355. package/presets/fleet/brains/codex-gpt-6-1-sol-ultra.yaml +5 -0
  356. package/presets/fleet/brains/codex-gpt-6-1-sol-xhigh.yaml +5 -0
  357. package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +1 -1
  358. package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +1 -1
  359. package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +1 -1
  360. package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +1 -1
  361. package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +1 -1
  362. package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +1 -1
  363. package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +5 -0
  364. package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +5 -0
  365. package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +5 -0
  366. package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +5 -0
  367. package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +5 -0
  368. package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +5 -0
  369. package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +5 -0
  370. package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +5 -0
  371. package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +5 -0
  372. package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +5 -0
  373. package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +5 -0
  374. package/presets/fleet/roles/Coordinator.yaml +34 -11
  375. package/presets/fleet/roles/Engineer.yaml +40 -0
  376. package/presets/fleet/room_layouts/engineering.yaml +25 -0
  377. package/presets/fleet/room_layouts/pair.yaml +24 -0
  378. package/presets/fleet/room_layouts/single.yaml +17 -0
  379. package/presets/fleet/room_layouts/team.yaml +30 -0
  380. package/presets/fleet/room_templates/engineering.yaml +19 -0
  381. package/presets/manifest.json +2 -2
  382. package/dist/web-app/assets/index-59GF-gsZ.css +0 -1
  383. package/dist/web-app/assets/index-C0h3ALvs.js +0 -10
  384. package/dist/web-app/icons/ours-fleet-maskable.svg +0 -4
  385. package/dist/web-app/icons/ours-fleet.svg +0 -4
  386. package/dist/web-app/index.html +0 -17
  387. package/dist/web-app/manifest.webmanifest +0 -15
  388. package/dist/web-app/offline.html +0 -18
  389. package/dist/web-app/sw.js +0 -51
@@ -0,0 +1,62 @@
1
+ import { type Exec } from '../exec.js';
2
+ import type { TaskSupervisorOwner } from '../temp-lifecycle.js';
3
+ import type { Liveness } from './types.js';
4
+ export interface FleetMember {
5
+ version: 1;
6
+ key: string;
7
+ name: string;
8
+ kind: 'permanent' | 'task' | 'temporary' | 'watchdogs';
9
+ dir: string;
10
+ desired: 'running' | 'stopped';
11
+ retiring?: boolean;
12
+ incarnation: string;
13
+ taskOwner?: TaskSupervisorOwner;
14
+ launchId?: string;
15
+ configPath?: string;
16
+ environment?: Record<string, string>;
17
+ generation?: string;
18
+ pid?: number;
19
+ retryAt?: number;
20
+ }
21
+ /** Private 0600 member state retains exactly the creator's profile selection.
22
+ * Pin the resolved profile path even when the caller relied on HOME defaults. */
23
+ export declare function captureMemberEnvironment(env?: NodeJS.ProcessEnv): Promise<Record<string, string>>;
24
+ export declare function managedMemberEnvironment(member: FleetMember, parent?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
25
+ export declare const catalogRoot: () => string;
26
+ export declare function memberKey(name: string, kind: FleetMember['kind']): string;
27
+ export declare function memberPath(key: string): string;
28
+ export declare const withMemberLock: <T>(key: string, work: () => T | Promise<T>) => Promise<T>;
29
+ export declare function readMember(key: string): FleetMember | undefined;
30
+ export declare function writeMember(record: FleetMember): void;
31
+ export declare function listMemberKeys(): string[];
32
+ export declare function listMembers(): FleetMember[];
33
+ export type MemberRegistration = Pick<FleetMember, 'name' | 'kind' | 'dir' | 'taskOwner' | 'launchId' | 'configPath' | 'environment'>;
34
+ /** Persist intent before starting anything. The parent never discovers members from tmp directories. */
35
+ export declare function registerMember(input: MemberRegistration, options?: {
36
+ initialDesired?: FleetMember['desired'];
37
+ preserveExisting?: boolean;
38
+ }): Promise<{
39
+ created: boolean;
40
+ member: FleetMember;
41
+ }>;
42
+ export interface CatalogDeps {
43
+ exec?: Exec;
44
+ sleep?(ms: number): Promise<void>;
45
+ timeoutMs?: number;
46
+ }
47
+ /** A unique generation in argv fences PID reuse, including the spawn-to-pid-write crash seam. */
48
+ export declare function memberProcess(record: FleetMember, exec?: Exec): Promise<Liveness & {
49
+ pid?: number;
50
+ }>;
51
+ export declare function catalogLiveness(key: string, exec?: Exec): Promise<Liveness>;
52
+ export declare function waitMember(key: string, desired: 'running' | 'stopped', deps?: CatalogDeps): Promise<void>;
53
+ export declare function stopMember(key: string, deps?: CatalogDeps): Promise<void>;
54
+ export declare function unregisterMember(key: string, deps?: CatalogDeps): Promise<boolean>;
55
+ export declare function unregisterTaskMember(role: string, expected: {
56
+ taskId: string;
57
+ creationActionId?: string;
58
+ launchId?: string;
59
+ }, deps?: CatalogDeps): Promise<void>;
60
+ export declare function hasTaskMemberRegistration(role: string): boolean;
61
+ export declare function assertTaskRegistrationsAbsent(taskId: string): void;
62
+ export declare const permanentRegistration: (name: string) => MemberRegistration;
@@ -0,0 +1,254 @@
1
+ import { chmodSync, mkdirSync, lstatSync, readFileSync, readdirSync, rmSync } from 'node:fs';
2
+ import { join, resolve } from 'node:path';
3
+ import { randomUUID } from 'node:crypto';
4
+ import { replaceFileAtomically, withFileLock } from '../atomic-file.js';
5
+ import { agentDir, stateRoot, watchdogsRoot } from '../paths.js';
6
+ import { realExec } from '../exec.js';
7
+ import { assertSafeAncestors } from '../rooms-tasks/workspace.js';
8
+ const MEMBER_ENV_KEYS = ['HOME', 'PATH', 'XDG_RUNTIME_DIR', 'CODEX_HOME', 'CODEX_PATH', 'OURS_PORT', 'OURS_STATE_DIR', 'OURS_API_TOKEN', 'OURS_CONFIG'];
9
+ /** Private 0600 member state retains exactly the creator's profile selection.
10
+ * Pin the resolved profile path even when the caller relied on HOME defaults. */
11
+ export async function captureMemberEnvironment(env = process.env) {
12
+ const { clientConfigPath } = await import('../client-profile.js');
13
+ return Object.fromEntries([...MEMBER_ENV_KEYS.flatMap(key => env[key] === undefined ? [] : [[key, env[key]]]), ['OURS_CONFIG', clientConfigPath(env)]]);
14
+ }
15
+ export function managedMemberEnvironment(member, parent = process.env) {
16
+ const env = { ...parent };
17
+ if (member.environment) {
18
+ for (const key of MEMBER_ENV_KEYS)
19
+ delete env[key];
20
+ Object.assign(env, member.environment);
21
+ }
22
+ return env;
23
+ }
24
+ export const catalogRoot = () => join(stateRoot(), 'supervisor', 'members');
25
+ export function memberKey(name, kind) {
26
+ if (!/^[A-Za-z0-9_-]{1,160}$/.test(name))
27
+ throw Error('FLEET_MEMBER_INVALID_NAME');
28
+ return `${kind}-${name}`;
29
+ }
30
+ export function memberPath(key) {
31
+ if (!/^(permanent|task|temporary|watchdogs)-[A-Za-z0-9_-]{1,160}$/.test(key))
32
+ throw Error('FLEET_MEMBER_INVALID_KEY');
33
+ return join(catalogRoot(), `${key}.json`);
34
+ }
35
+ export const withMemberLock = (key, work) => withFileLock(join(stateRoot(), 'locks', 'fleet-members', memberKeyFromValidatedKey(key)), work);
36
+ function memberKeyFromValidatedKey(key) { memberPath(key); return key; }
37
+ export function readMember(key) {
38
+ const path = memberPath(key);
39
+ assertSafeAncestors(catalogRoot());
40
+ let stat;
41
+ try {
42
+ stat = lstatSync(path);
43
+ }
44
+ catch (error) {
45
+ if (error.code === 'ENOENT')
46
+ return;
47
+ throw error;
48
+ }
49
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 256 * 1024)
50
+ throw Error('FLEET_MEMBER_UNSAFE_RECORD');
51
+ const record = JSON.parse(readFileSync(path, 'utf8'));
52
+ validateMember(record, key);
53
+ return record;
54
+ }
55
+ function validateMember(record, key) {
56
+ const expectedDir = record.kind === 'watchdogs' ? watchdogsRoot()
57
+ : agentDir(record.name, record.kind !== 'permanent');
58
+ if (record.version !== 1 || !['permanent', 'task', 'temporary', 'watchdogs'].includes(record.kind)
59
+ || memberKey(record.name, record.kind) !== key || record.key !== key
60
+ || !['running', 'stopped'].includes(record.desired)
61
+ || !/^[a-f0-9-]{36}$/.test(record.incarnation)
62
+ || record.dir !== resolve(record.dir) || record.dir !== expectedDir
63
+ || (record.environment !== undefined && (typeof record.environment !== 'object' || record.environment === null
64
+ || Object.entries(record.environment).some(([key, value]) => !MEMBER_ENV_KEYS.includes(key) || typeof value !== 'string' || value.includes('\0'))))
65
+ || (record.pid !== undefined && (!Number.isSafeInteger(record.pid) || record.pid <= 0))
66
+ || (record.retryAt !== undefined && (!Number.isFinite(record.retryAt) || record.retryAt < 0))
67
+ || (record.kind === 'task' && (!record.taskOwner?.taskId || !record.taskOwner.creationActionId || !record.launchId)))
68
+ throw Error('FLEET_MEMBER_INVALID_RECORD');
69
+ }
70
+ export function writeMember(record) {
71
+ validateMember(record, record.key);
72
+ assertSafeAncestors(catalogRoot());
73
+ mkdirSync(catalogRoot(), { recursive: true, mode: 0o700 });
74
+ chmodSync(catalogRoot(), 0o700);
75
+ replaceFileAtomically(memberPath(record.key), JSON.stringify(record) + '\n');
76
+ }
77
+ export function listMemberKeys() {
78
+ try {
79
+ return readdirSync(catalogRoot()).filter(name => name.endsWith('.json')).map(name => name.slice(0, -5));
80
+ }
81
+ catch (error) {
82
+ if (error.code === 'ENOENT')
83
+ return [];
84
+ throw error;
85
+ }
86
+ }
87
+ export function listMembers() {
88
+ let entries;
89
+ try {
90
+ entries = readdirSync(catalogRoot(), { withFileTypes: true });
91
+ }
92
+ catch (error) {
93
+ if (error.code === 'ENOENT')
94
+ return [];
95
+ throw error;
96
+ }
97
+ return entries.filter(entry => entry.name.endsWith('.json')).map(entry => {
98
+ const value = readMember(entry.name.slice(0, -5));
99
+ if (!value)
100
+ throw Error('FLEET_MEMBER_DISAPPEARED');
101
+ return value;
102
+ });
103
+ }
104
+ /** Persist intent before starting anything. The parent never discovers members from tmp directories. */
105
+ export async function registerMember(input, options = {}) {
106
+ const key = memberKey(input.name, input.kind);
107
+ return withMemberLock(key, () => {
108
+ const previous = readMember(key);
109
+ if (previous && (previous.dir !== input.dir || previous.launchId !== input.launchId
110
+ || JSON.stringify(previous.taskOwner) !== JSON.stringify(input.taskOwner)))
111
+ throw Error('FLEET_MEMBER_OWNER_COLLISION');
112
+ if (previous && options.preserveExisting)
113
+ return { created: false, member: previous };
114
+ if (previous?.retiring)
115
+ throw Error('FLEET_MEMBER_RETIRING');
116
+ const desired = options.initialDesired ?? 'running';
117
+ const member = previous
118
+ ? { ...previous, desired, configPath: input.configPath ?? previous.configPath,
119
+ environment: previous.kind === 'task' ? previous.environment : input.environment ?? previous.environment, retryAt: 0 }
120
+ : { ...input, version: 1, key, desired, incarnation: randomUUID() };
121
+ writeMember(member);
122
+ return { created: !previous, member };
123
+ });
124
+ }
125
+ /** A unique generation in argv fences PID reuse, including the spawn-to-pid-write crash seam. */
126
+ export async function memberProcess(record, exec = realExec) {
127
+ if (!record.generation)
128
+ return { state: 'stopped', detail: 'no child generation' };
129
+ if (!/^[a-f0-9-]{36}$/.test(record.generation))
130
+ return { state: 'unknown', detail: 'invalid child generation' };
131
+ const matches = (command) => command.split(/\s+/).some((part, index, words) => part === '_run-managed' && words[index + 1] === record.key && words[index + 2] === record.generation);
132
+ if (record.pid) {
133
+ try {
134
+ process.kill(record.pid, 0);
135
+ }
136
+ catch (error) {
137
+ return error.code === 'ESRCH'
138
+ ? { state: 'stopped', detail: 'child pid absent' }
139
+ : { state: 'unknown', detail: 'child pid probe failed' };
140
+ }
141
+ try {
142
+ const command = readFileSync(`/proc/${record.pid}/cmdline`, 'utf8').split('\0').join(' ');
143
+ return matches(command) ? { state: 'running', detail: 'exact child generation', pid: record.pid }
144
+ : { state: 'stopped', detail: 'pid belongs to another generation' };
145
+ }
146
+ catch { /* non-/proc hosts use ps */ }
147
+ }
148
+ const result = await exec('ps', record.pid ? ['-p', String(record.pid), '-o', 'pid=', '-o', 'command=']
149
+ : ['-ax', '-o', 'pid=', '-o', 'command=']);
150
+ if (result.code !== 0)
151
+ return result.code === 1 && record.pid
152
+ ? { state: 'stopped', detail: 'child absent' } : { state: 'unknown', detail: 'process probe unavailable' };
153
+ const pids = result.stdout.split('\n').flatMap(line => {
154
+ const row = /^\s*(\d+)\s+(.*)$/.exec(line);
155
+ return row && matches(row[2]) ? [Number(row[1])] : [];
156
+ });
157
+ if (pids.length > 1)
158
+ return { state: 'unknown', detail: 'ambiguous child generation' };
159
+ return pids.length ? { state: 'running', detail: 'exact child generation', pid: pids[0] }
160
+ : { state: 'stopped', detail: 'child generation absent' };
161
+ }
162
+ export async function catalogLiveness(key, exec = realExec) {
163
+ try {
164
+ const record = readMember(key);
165
+ if (!record)
166
+ return { state: 'stopped', detail: 'member unregistered' };
167
+ const live = await memberProcess(record, exec);
168
+ if (live.state === 'stopped' && record.desired === 'running') {
169
+ const { readRestartLedger } = await import('../runner.js');
170
+ const ledger = readRestartLedger(record.dir);
171
+ if (ledger.circuit === 'open')
172
+ return { ...live, detail: `held after ${ledger.consecutiveImmediateFailures} failures: ${ledger.lastReason}; use up/restart or task recovery to reset` };
173
+ }
174
+ return live;
175
+ }
176
+ catch {
177
+ return { state: 'unknown', detail: 'member record unavailable' };
178
+ }
179
+ }
180
+ export async function waitMember(key, desired, deps = {}) {
181
+ const deadline = Date.now() + (deps.timeoutMs ?? 30_000);
182
+ do {
183
+ const live = await catalogLiveness(key, deps.exec);
184
+ if (live.state === desired)
185
+ return;
186
+ await (deps.sleep ?? (ms => new Promise(resolve => setTimeout(resolve, ms))))(100);
187
+ } while (Date.now() < deadline);
188
+ throw Error(desired === 'stopped' ? 'FLEET_MEMBER_STOP_UNPROVEN' : 'FLEET_MEMBER_START_UNPROVEN');
189
+ }
190
+ export async function stopMember(key, deps = {}) {
191
+ await withMemberLock(key, () => {
192
+ const record = readMember(key);
193
+ if (record)
194
+ writeMember({ ...record, desired: 'stopped' });
195
+ });
196
+ // A stopped parent cannot consume intent, but its IPC children stop on disconnect.
197
+ // Never signal an arbitrary recorded pid or erase a record on timeout.
198
+ await waitMember(key, 'stopped', deps);
199
+ }
200
+ export async function unregisterMember(key, deps = {}) {
201
+ const original = readMember(key);
202
+ if (!original)
203
+ return false;
204
+ await withMemberLock(key, () => {
205
+ const current = readMember(key);
206
+ if (!current || current.incarnation !== original.incarnation)
207
+ throw Error('FLEET_MEMBER_RETIREMENT_CONFLICT');
208
+ writeMember({ ...current, desired: 'stopped', retiring: true });
209
+ });
210
+ await waitMember(key, 'stopped', deps);
211
+ return withMemberLock(key, async () => {
212
+ const current = readMember(key);
213
+ if (!current)
214
+ return false;
215
+ if (current.incarnation !== original.incarnation || current.desired !== 'stopped')
216
+ throw Error('FLEET_MEMBER_RETIREMENT_CONFLICT');
217
+ if ((await memberProcess(current, deps.exec)).state !== 'stopped')
218
+ throw Error('FLEET_MEMBER_STOP_UNPROVEN');
219
+ rmSync(memberPath(key));
220
+ return true;
221
+ });
222
+ }
223
+ export async function unregisterTaskMember(role, expected, deps = {}) {
224
+ const key = memberKey(role, 'task');
225
+ await withMemberLock(key, () => {
226
+ const current = readMember(key);
227
+ if (!current)
228
+ return;
229
+ if (current.kind !== 'task' || current.taskOwner?.taskId !== expected.taskId
230
+ || (!expected.creationActionId && !expected.launchId)
231
+ || (expected.creationActionId !== undefined && current.taskOwner.creationActionId !== expected.creationActionId)
232
+ || (expected.launchId !== undefined && current.launchId !== expected.launchId))
233
+ throw Error('TASK_SERVICE_OWNER_MISMATCH');
234
+ writeMember({ ...current, desired: 'stopped', retiring: true });
235
+ });
236
+ await unregisterMember(key, deps);
237
+ }
238
+ export function hasTaskMemberRegistration(role) {
239
+ const path = memberPath(memberKey(role, 'task'));
240
+ try {
241
+ lstatSync(path);
242
+ return true;
243
+ }
244
+ catch (error) {
245
+ if (error.code === 'ENOENT')
246
+ return false;
247
+ throw error;
248
+ }
249
+ }
250
+ export function assertTaskRegistrationsAbsent(taskId) {
251
+ if (listMembers().some(record => record.taskOwner?.taskId === taskId))
252
+ throw Error('TASK_SERVICES_REMAIN');
253
+ }
254
+ export const permanentRegistration = (name) => ({ name, kind: 'permanent', dir: agentDir(name) });
@@ -0,0 +1,8 @@
1
+ import { type Exec } from '../exec.js';
2
+ import type { SupervisorBackend } from './types.js';
3
+ export declare function fleetHostBackend(exec?: Exec, platform?: NodeJS.Platform): SupervisorBackend;
4
+ /** Registration only starts an explicitly installed parent; it cannot repoint
5
+ * the fleet at the invoking task/development binary or rewrite user settings. */
6
+ export declare function ensureFleetParent(exec?: Exec, platform?: NodeJS.Platform): Promise<void>;
7
+ /** OS operations refer only to the Fleet parent; member operations refer only to its catalog. */
8
+ export declare function makeFleetBackend(exec?: Exec, platform?: NodeJS.Platform): SupervisorBackend;
@@ -0,0 +1,89 @@
1
+ import { join } from 'node:path';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { agentDir } from '../paths.js';
4
+ import { realExec } from '../exec.js';
5
+ import { home } from '../paths.js';
6
+ import { makeSystemdBackend } from './systemd.js';
7
+ import { makeLaunchdBackend } from './launchd.js';
8
+ import { catalogLiveness, memberKey, readMember, registerMember, stopMember, unregisterMember, waitMember } from './catalog.js';
9
+ export function fleetHostBackend(exec = realExec, platform = process.platform) {
10
+ if (platform === 'linux')
11
+ return makeSystemdBackend(exec);
12
+ if (platform === 'darwin')
13
+ return makeLaunchdBackend(exec);
14
+ throw Error('Fleet service requires Linux or macOS');
15
+ }
16
+ /** Registration only starts an explicitly installed parent; it cannot repoint
17
+ * the fleet at the invoking task/development binary or rewrite user settings. */
18
+ export async function ensureFleetParent(exec = realExec, platform = process.platform) {
19
+ const host = fleetHostBackend(exec, platform);
20
+ const path = platform === 'linux' ? join(home(), '.config/systemd/user/ours-fleet.service')
21
+ : join(home(), 'Library/LaunchAgents/network.ours.fleet.plist');
22
+ if (!existsSync(path))
23
+ throw Error('FLEET_SERVICE_NOT_INSTALLED: run ours-fleet init');
24
+ const live = await host.liveness('fleet');
25
+ if (live.state === 'unknown')
26
+ throw Error('FLEET_SERVICE_LIVENESS_UNKNOWN');
27
+ if (live.state === 'stopped')
28
+ await host.install('fleet', '');
29
+ }
30
+ /** OS operations refer only to the Fleet parent; member operations refer only to its catalog. */
31
+ export function makeFleetBackend(exec = realExec, platform = process.platform) {
32
+ const host = fleetHostBackend(exec, platform);
33
+ return {
34
+ id: host.id,
35
+ async init(binPath) {
36
+ const messages = await host.init(binPath);
37
+ const { adoptConfiguredFleet } = await import('./adoption.js');
38
+ await adoptConfiguredFleet(binPath, exec, platform);
39
+ return messages;
40
+ },
41
+ async install(name, binPath) {
42
+ const { adoptConfiguredFleet } = await import('./adoption.js');
43
+ let configPath;
44
+ try {
45
+ configPath = readFileSync(join(agentDir(name), '.config-path'), 'utf8').trim() || undefined;
46
+ }
47
+ catch { /* a new state directory may not carry a config marker yet */ }
48
+ await adoptConfiguredFleet(binPath, exec, platform, configPath, name);
49
+ const result = await registerMember({ name, kind: 'permanent', dir: agentDir(name) });
50
+ try {
51
+ await waitMember(result.member.key, 'running', { exec });
52
+ }
53
+ catch (error) {
54
+ // Preserve existing intent. New registration rollback is exact and waits
55
+ // for process quiescence before its creation evidence can be discarded.
56
+ if (result.created)
57
+ await unregisterMember(result.member.key, { exec });
58
+ throw error;
59
+ }
60
+ return { created: result.created, detail: `Fleet member ${name} registered` };
61
+ },
62
+ async start(name) {
63
+ const key = memberKey(name, 'permanent'), member = readMember(key);
64
+ if (!member)
65
+ throw Error('Fleet member not registered; use up');
66
+ await registerMember(member);
67
+ await ensureFleetParent(exec, platform);
68
+ await waitMember(key, 'running', { exec });
69
+ },
70
+ async stop(name) { await stopMember(memberKey(name, 'permanent'), { exec }); },
71
+ async restart(name) {
72
+ await this.stop(name);
73
+ await this.start(name);
74
+ },
75
+ async status(name) { return (await this.liveness(name)).detail; },
76
+ liveness(name) {
77
+ const key = memberKey(name, 'permanent');
78
+ if (!readMember(key) && existsSync(agentDir(name)))
79
+ return Promise.resolve({ state: 'unknown', detail: 'unmigrated member; preserving context until legacy inspection' });
80
+ return catalogLiveness(key, exec);
81
+ },
82
+ async inspect(name) { return { backend: host.id, ...await this.liveness(name), nativeState: 'fleet-member' }; },
83
+ async uninstall(name) {
84
+ const { retirePermanentRegistration } = await import('./adoption.js');
85
+ return { removed: await retirePermanentRegistration(name, exec), detail: `Fleet member ${name} unregistered` };
86
+ },
87
+ logsArgs(name, follow) { return { cmd: 'tail', args: [follow ? '-f' : '-n', ...(follow ? [] : ['200']), join(agentDir(name), 'supervisor.log')] }; },
88
+ };
89
+ }
@@ -1,6 +1,5 @@
1
1
  import { realExec } from '../exec.js';
2
- import { makeSystemdBackend } from './systemd.js';
3
- import { makeLaunchdBackend } from './launchd.js';
2
+ import { makeFleetBackend } from './fleet.js';
4
3
  import { makeNoneBackend } from './none.js';
5
4
  export { makeSystemdBackend, unitFor } from './systemd.js';
6
5
  export { makeLaunchdBackend, labelFor } from './launchd.js';
@@ -8,9 +7,7 @@ export { makeNoneBackend } from './none.js';
8
7
  export function pickBackend(exec = realExec, platform = process.platform) {
9
8
  if (process.env.OURS_FLEET_SUPERVISOR === 'none')
10
9
  return makeNoneBackend(exec);
11
- if (platform === 'darwin')
12
- return makeLaunchdBackend(exec);
13
- if (platform === 'linux')
14
- return makeSystemdBackend(exec);
10
+ if (platform === 'darwin' || platform === 'linux')
11
+ return makeFleetBackend(exec, platform);
15
12
  throw new Error(`unsupported platform '${platform}' (linux and darwin only)`);
16
13
  }
@@ -1,6 +1,6 @@
1
1
  import { type Exec } from '../exec.js';
2
2
  import type { SupervisorBackend } from './types.js';
3
- export declare const labelFor: (name: string) => string;
3
+ export declare const labelFor: (_name: string) => string;
4
4
  /**
5
5
  * What `launchctl print` said about a job, parsed ONCE so that the two questions
6
6
  * asked of it cannot drift apart. They are not the same question:
@@ -1,8 +1,10 @@
1
+ import { assertNativeFleetScope } from './scope.js';
2
+ import { clientConfigPath } from '../client-profile.js';
1
3
  import { existsSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
2
4
  import { join } from 'node:path';
3
5
  import { home, logsRoot } from '../paths.js';
4
6
  import { realExec } from '../exec.js';
5
- export const labelFor = (name) => `network.ours.fleet.${name}`;
7
+ export const labelFor = (_name) => 'network.ours.fleet';
6
8
  /**
7
9
  * The spelling of the last-exit line is not stable across macOS releases
8
10
  * (`last exit code`, `last exit status`, `last exit reason`), so match the
@@ -21,16 +23,17 @@ const xml = (value) => value
21
23
  .replaceAll('"', '&quot;')
22
24
  .replaceAll("'", '&apos;');
23
25
  function plist(name, binPath) {
24
- const log = join(logsRoot(), `${name}.log`);
25
- const config = process.env.OURS_CONFIG;
26
+ const log = join(logsRoot(), 'fleet.log');
27
+ const config = clientConfigPath(process.env);
28
+ const env = { OURS_CONFIG: config, ...Object.fromEntries(['PATH', 'OURS_FLEET_HOME', 'OURS_FLEET_SOCKET_ROOT', 'CODEX_HOME', 'CODEX_PATH'].flatMap(key => process.env[key] !== undefined ? [[key, process.env[key]]] : [])) };
26
29
  return `<?xml version="1.0" encoding="UTF-8"?>
27
30
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
28
31
  <plist version="1.0">
29
32
  <dict>
30
33
  <key>Label</key><string>${labelFor(name)}</string>
31
34
  <key>ProgramArguments</key>
32
- <array><string>${binPath}</string><string>_run</string><string>${name}</string></array>
33
- ${config ? ` <key>EnvironmentVariables</key><dict><key>OURS_CONFIG</key><string>${xml(config)}</string></dict>\n` : ''} <!-- The runner owns the child-session restart loop. launchd must only
35
+ <array><string>${xml(process.execPath)}</string><string>${xml(binPath)}</string><string>_run-fleet</string></array>
36
+ ${` <key>EnvironmentVariables</key><dict>${Object.entries(env).map(([key, value]) => `<key>${key}</key><string>${xml(value)}</string>`).join('')}</dict>\n`} <!-- The runner owns the child-session restart loop. launchd must only
34
37
  recover the runner PROCESS crashing: a bare KeepAlive would resume the
35
38
  uncounted relaunch loop and restart a deliberately held-down agent. -->
36
39
  <key>KeepAlive</key><dict><key>SuccessfulExit</key><false/></dict>
@@ -79,6 +82,7 @@ export function makeLaunchdBackend(exec = realExec, uid = process.getuid?.() ??
79
82
  const domain = `gui/${uid}`;
80
83
  /** Read `launchctl print` once; both callers classify it for their own question. */
81
84
  const printJob = async (name) => {
85
+ assertNativeFleetScope(exec);
82
86
  const r = await exec('launchctl', ['print', `${domain}/${labelFor(name)}`]);
83
87
  const out = `${r.stdout}\n${r.stderr}`;
84
88
  if (r.code !== 0)
@@ -96,21 +100,26 @@ export function makeLaunchdBackend(exec = realExec, uid = process.getuid?.() ??
96
100
  };
97
101
  return {
98
102
  id: 'launchd',
99
- async init() {
103
+ async init(binPath) {
104
+ assertNativeFleetScope(exec);
100
105
  mkdirSync(agentsDir(), { recursive: true });
101
106
  mkdirSync(logsRoot(), { recursive: true });
107
+ writeFileSync(plistPath('fleet'), plist('fleet', binPath));
102
108
  return [
103
109
  `LaunchAgents dir ready: ${agentsDir()}`,
104
110
  'note: launchd agents start at login (macOS has no linger equivalent)',
105
111
  ];
106
112
  },
107
113
  async install(name, binPath) {
108
- mkdirSync(agentsDir(), { recursive: true });
109
- mkdirSync(logsRoot(), { recursive: true });
110
- // The plist's prior existence is the record of whether we created this.
111
- const existed = existsSync(plistPath(name));
112
- writeFileSync(plistPath(name), plist(name, binPath));
113
- await exec('launchctl', ['bootout', `${domain}/${labelFor(name)}`]); // best-effort refresh
114
+ assertNativeFleetScope(exec);
115
+ if (!existsSync(plistPath(name)))
116
+ throw Error('FLEET_SERVICE_NOT_INSTALLED: run ours-fleet init');
117
+ const existed = true;
118
+ const loaded = await printJob(name);
119
+ if (loaded.loaded)
120
+ return { created: !existed, detail: 'Fleet service already loaded' };
121
+ if (!loaded.notFound)
122
+ throw new Error('Fleet launchd liveness unknown');
114
123
  // Undo only what WE wrote. A plist that was already there belongs to
115
124
  // whoever put it there, and rollback may never remove it.
116
125
  const undo = async () => {
@@ -153,16 +162,19 @@ export function makeLaunchdBackend(exec = realExec, uid = process.getuid?.() ??
153
162
  : { created: true, detail: `installed ${labelFor(name)} (${start.detail})` };
154
163
  },
155
164
  async start(name) {
165
+ assertNativeFleetScope(exec);
156
166
  const r = await exec('launchctl', ['bootstrap', domain, plistPath(name)]);
157
167
  if (r.code !== 0)
158
168
  await exec('launchctl', ['kickstart', `${domain}/${labelFor(name)}`]);
159
169
  },
160
170
  async stop(name) {
171
+ assertNativeFleetScope(exec);
161
172
  const r = await exec('launchctl', ['bootout', `${domain}/${labelFor(name)}`]);
162
173
  if (r.code !== 0)
163
174
  throw new Error(`launchctl bootout ${labelFor(name)} failed: ${r.stderr.trim()}`);
164
175
  },
165
176
  async restart(name) {
177
+ assertNativeFleetScope(exec);
166
178
  const r = await exec('launchctl', ['kickstart', '-k', `${domain}/${labelFor(name)}`]);
167
179
  if (r.code !== 0)
168
180
  throw new Error(`launchctl kickstart ${labelFor(name)} failed: ${r.stderr.trim()}`);
@@ -202,6 +214,7 @@ export function makeLaunchdBackend(exec = realExec, uid = process.getuid?.() ??
202
214
  };
203
215
  },
204
216
  async uninstall(name) {
217
+ assertNativeFleetScope(exec);
205
218
  const existed = existsSync(plistPath(name));
206
219
  await exec('launchctl', ['bootout', `${domain}/${labelFor(name)}`]); // idempotent
207
220
  rmSync(plistPath(name), { force: true });
@@ -210,7 +223,7 @@ export function makeLaunchdBackend(exec = realExec, uid = process.getuid?.() ??
210
223
  : { removed: false, detail: `${labelFor(name)} was not installed` };
211
224
  },
212
225
  logsArgs(name, follow) {
213
- const log = join(logsRoot(), `${name}.log`);
226
+ const log = join(logsRoot(), 'fleet.log');
214
227
  return { cmd: 'tail', args: follow ? ['-f', log] : ['-n', '200', log] };
215
228
  },
216
229
  };
@@ -0,0 +1,35 @@
1
+ import { type Exec } from '../exec.js';
2
+ import type { ResolvedRole } from '../config.js';
3
+ type Desired = 'running' | 'stopped';
4
+ export interface LegacyReadinessGate {
5
+ path: string;
6
+ argv: string[];
7
+ timeoutStartSec: number;
8
+ restartSec: number;
9
+ }
10
+ export interface LegacyPermanentMigrationInput {
11
+ roles: Pick<ResolvedRole, 'name' | 'identity'>[];
12
+ binPath: string;
13
+ platform: NodeJS.Platform;
14
+ configPath?: string;
15
+ exec?: Exec;
16
+ /** All-role preflight precedes this hook. Ensure the singleton is running and
17
+ * preserves every gate before returning; failure prevents native retirement. */
18
+ prepareParent(gates: LegacyReadinessGate[]): Promise<void>;
19
+ /** Isolated process-environment seam; production reads only OURS_FLEET_HOME. */
20
+ processHome?(pid: number): string;
21
+ /** Create if absent; an existing registration's desired state must survive retry. */
22
+ register(name: string, desired: Desired, configPath?: string): Promise<void>;
23
+ }
24
+ export interface LegacyPermanentMigrationResult {
25
+ name: string;
26
+ status: 'migrated' | 'already-migrated' | 'not-installed' | 'disabled';
27
+ }
28
+ /** Start the common parent after all-role preflight and before retirement. Only configured,
29
+ * identity-proven legacy registrations transfer; no identity/session writes.
30
+ * A receipt survives removal of the old native job and preserves its original
31
+ * running/stopped intent across retries. Native services are never inferred
32
+ * from role names alone. Uncertain or conflicting proof fails closed.
33
+ */
34
+ export declare function migrateLegacyPermanentMembers(input: LegacyPermanentMigrationInput): Promise<LegacyPermanentMigrationResult[]>;
35
+ export {};