@ours.network/fleet 1.2.0-nightly.9 → 2.0.0

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 (429) hide show
  1. package/README.md +280 -68
  2. package/dist/account-origin.d.ts +5 -0
  3. package/dist/account-origin.js +31 -0
  4. package/dist/agent-ours/bridge.js +26 -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 +22 -0
  11. package/dist/agent-ours/mcp-endpoint.js +101 -25
  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 +27 -1
  15. package/dist/agent-ours/service.js +107 -13
  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 +31 -0
  19. package/dist/application/conversation-history.js +339 -0
  20. package/dist/application/conversation-role-observer.d.ts +62 -0
  21. package/dist/application/conversation-role-observer.js +206 -0
  22. package/dist/application/fleet-query-service.d.ts +1 -1
  23. package/dist/application/fleet-query-service.js +3 -3
  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/resource-observer.d.ts +43 -0
  30. package/dist/application/resource-observer.js +323 -0
  31. package/dist/application/role-creation-service.js +3 -2
  32. package/dist/application/role-removal-service.js +17 -3
  33. package/dist/application/role-repository.d.ts +3 -1
  34. package/dist/application/role-repository.js +89 -54
  35. package/dist/application/room-layout-definitions.d.ts +45 -0
  36. package/dist/application/room-layout-definitions.js +130 -0
  37. package/dist/application/session-control.d.ts +63 -17
  38. package/dist/application/session-control.js +221 -23
  39. package/dist/application/supervisor-ours-tools.js +5 -11
  40. package/dist/application/task-layouts.d.ts +91 -0
  41. package/dist/application/task-layouts.js +261 -0
  42. package/dist/application/task-room-service.d.ts +101 -3
  43. package/dist/application/task-room-service.js +286 -33
  44. package/dist/application/types.d.ts +2 -0
  45. package/dist/atomic-file.d.ts +34 -1
  46. package/dist/atomic-file.js +161 -18
  47. package/dist/briefing.d.ts +3 -1
  48. package/dist/briefing.js +120 -31
  49. package/dist/build-info.json +10 -5
  50. package/dist/capabilities.d.ts +9 -1
  51. package/dist/capabilities.js +13 -0
  52. package/dist/cli.js +391 -38
  53. package/dist/client-profile.d.ts +5 -17
  54. package/dist/client-profile.js +4 -127
  55. package/dist/config.d.ts +45 -0
  56. package/dist/config.js +57 -6
  57. package/dist/creation.d.ts +1 -1
  58. package/dist/creation.js +4 -4
  59. package/dist/daemon-recovery.d.ts +14 -9
  60. package/dist/daemon-recovery.js +124 -115
  61. package/dist/docs.d.ts +1 -1
  62. package/dist/docs.js +214 -20
  63. package/dist/doctor.d.ts +4 -1
  64. package/dist/doctor.js +75 -60
  65. package/dist/erased-resources.d.ts +9 -0
  66. package/dist/erased-resources.js +87 -0
  67. package/dist/file-delivery/reader.d.ts +15 -0
  68. package/dist/file-delivery/reader.js +63 -0
  69. package/dist/file-delivery/store.d.ts +15 -0
  70. package/dist/file-delivery/store.js +124 -0
  71. package/dist/file-delivery/types.d.ts +19 -0
  72. package/dist/file-delivery/types.js +1 -0
  73. package/dist/fleet-command-audit.d.ts +13 -0
  74. package/dist/fleet-command-audit.js +31 -9
  75. package/dist/harness/agent-session.d.ts +4 -0
  76. package/dist/harness/claude-code-session.js +1 -1
  77. package/dist/harness/claude-code.js +21 -11
  78. package/dist/harness/claude-runtime.d.ts +12 -0
  79. package/dist/harness/claude-runtime.js +43 -0
  80. package/dist/harness/codex-app-server-proxy.d.ts +4 -1
  81. package/dist/harness/codex-app-server-proxy.js +41 -5
  82. package/dist/harness/codex-reasoning.d.ts +5 -0
  83. package/dist/harness/codex-reasoning.js +12 -0
  84. package/dist/harness/codex-runtime.d.ts +11 -1
  85. package/dist/harness/codex-runtime.js +31 -4
  86. package/dist/harness/codex-session.js +2 -2
  87. package/dist/harness/codex.js +24 -12
  88. package/dist/init-wizard.d.ts +2 -0
  89. package/dist/init-wizard.js +38 -19
  90. package/dist/isolation/policy.js +6 -1
  91. package/dist/isolation/types.d.ts +5 -0
  92. package/dist/managed-cli.d.ts +253 -0
  93. package/dist/managed-cli.js +923 -0
  94. package/dist/managed-recovery.d.ts +13 -0
  95. package/dist/managed-recovery.js +31 -0
  96. package/dist/minimal-setup.d.ts +2 -0
  97. package/dist/minimal-setup.js +44 -0
  98. package/dist/monitor.d.ts +9 -34
  99. package/dist/monitor.js +90 -153
  100. package/dist/notifications/fleet-producer.d.ts +31 -0
  101. package/dist/notifications/fleet-producer.js +351 -0
  102. package/dist/notifications/outbox.d.ts +59 -0
  103. package/dist/notifications/outbox.js +199 -0
  104. package/dist/notifications/target-cleanup.d.ts +16 -0
  105. package/dist/notifications/target-cleanup.js +75 -0
  106. package/dist/notifications/task-producer.d.ts +20 -0
  107. package/dist/notifications/task-producer.js +100 -0
  108. package/dist/ops.d.ts +2 -0
  109. package/dist/ops.js +20 -3
  110. package/dist/owner-channel/channel.js +17 -6
  111. package/dist/owner-channel/commands.d.ts +4 -0
  112. package/dist/owner-channel/commands.js +8 -0
  113. package/dist/owner-channel/lifecycle-outbox.js +4 -3
  114. package/dist/owner-channel/ours-client.js +2 -17
  115. package/dist/paths.d.ts +5 -0
  116. package/dist/paths.js +5 -0
  117. package/dist/preset-migration.js +55 -0
  118. package/dist/process-incarnation.d.ts +23 -0
  119. package/dist/process-incarnation.js +42 -0
  120. package/dist/reminders/manager.d.ts +75 -0
  121. package/dist/reminders/manager.js +278 -0
  122. package/dist/reminders/runtime.d.ts +27 -0
  123. package/dist/reminders/runtime.js +32 -0
  124. package/dist/reminders/schedule.d.ts +37 -0
  125. package/dist/reminders/schedule.js +104 -0
  126. package/dist/reminders/store.d.ts +341 -0
  127. package/dist/reminders/store.js +762 -0
  128. package/dist/reminders/tools.d.ts +20 -0
  129. package/dist/reminders/tools.js +114 -0
  130. package/dist/resolved-plan.js +1 -0
  131. package/dist/rooms-tasks/add-member.d.ts +48 -0
  132. package/dist/rooms-tasks/add-member.js +84 -0
  133. package/dist/rooms-tasks/archived-absence.d.ts +4 -0
  134. package/dist/rooms-tasks/archived-absence.js +11 -0
  135. package/dist/rooms-tasks/cli.js +182 -5
  136. package/dist/rooms-tasks/close.d.ts +5 -2
  137. package/dist/rooms-tasks/close.js +224 -36
  138. package/dist/rooms-tasks/config.d.ts +1 -1
  139. package/dist/rooms-tasks/config.js +11 -4
  140. package/dist/rooms-tasks/cowork-adapter.d.ts +12 -5
  141. package/dist/rooms-tasks/cowork-adapter.js +15 -121
  142. package/dist/rooms-tasks/cowork-http.js +56 -6
  143. package/dist/rooms-tasks/deletion.d.ts +1 -1
  144. package/dist/rooms-tasks/deletion.js +66 -10
  145. package/dist/rooms-tasks/erasure.d.ts +6 -0
  146. package/dist/rooms-tasks/erasure.js +173 -0
  147. package/dist/rooms-tasks/external-worker.d.ts +1 -1
  148. package/dist/rooms-tasks/external-worker.js +3 -4
  149. package/dist/rooms-tasks/layout-binding-client.d.ts +11 -0
  150. package/dist/rooms-tasks/layout-binding-client.js +56 -0
  151. package/dist/rooms-tasks/layout-binding-grants.d.ts +21 -0
  152. package/dist/rooms-tasks/layout-binding-grants.js +128 -0
  153. package/dist/rooms-tasks/layout-cli.d.ts +4 -0
  154. package/dist/rooms-tasks/layout-cli.js +107 -0
  155. package/dist/rooms-tasks/layout-config.d.ts +11 -0
  156. package/dist/rooms-tasks/layout-config.js +89 -0
  157. package/dist/rooms-tasks/layout-control.d.ts +21 -0
  158. package/dist/rooms-tasks/layout-control.js +52 -0
  159. package/dist/rooms-tasks/layout-member-retirement.d.ts +10 -0
  160. package/dist/rooms-tasks/layout-member-retirement.js +90 -0
  161. package/dist/rooms-tasks/layout-service.d.ts +43 -0
  162. package/dist/rooms-tasks/layout-service.js +203 -0
  163. package/dist/rooms-tasks/layout.d.ts +121 -0
  164. package/dist/rooms-tasks/layout.js +286 -0
  165. package/dist/rooms-tasks/legacy-task-member.d.ts +19 -0
  166. package/dist/rooms-tasks/legacy-task-member.js +116 -0
  167. package/dist/rooms-tasks/live-readiness.d.ts +18 -0
  168. package/dist/rooms-tasks/live-readiness.js +85 -0
  169. package/dist/rooms-tasks/member-ownership.d.ts +5 -0
  170. package/dist/rooms-tasks/member-ownership.js +40 -0
  171. package/dist/rooms-tasks/member-startup.d.ts +1 -0
  172. package/dist/rooms-tasks/member-startup.js +2 -0
  173. package/dist/rooms-tasks/provision.d.ts +3 -0
  174. package/dist/rooms-tasks/provision.js +101 -49
  175. package/dist/rooms-tasks/recovery.d.ts +19 -0
  176. package/dist/rooms-tasks/recovery.js +110 -0
  177. package/dist/rooms-tasks/room-state.d.ts +4 -2
  178. package/dist/rooms-tasks/room-state.js +47 -8
  179. package/dist/rooms-tasks/task-review.d.ts +113 -0
  180. package/dist/rooms-tasks/task-review.js +561 -0
  181. package/dist/rooms-tasks/task-service-retirement.d.ts +11 -0
  182. package/dist/rooms-tasks/task-service-retirement.js +21 -0
  183. package/dist/rooms-tasks/task-state.d.ts +51 -5
  184. package/dist/rooms-tasks/task-state.js +358 -50
  185. package/dist/rooms-tasks/task-tools.d.ts +35 -0
  186. package/dist/rooms-tasks/task-tools.js +134 -0
  187. package/dist/rooms-tasks/types.d.ts +78 -0
  188. package/dist/rooms-tasks/workspace-artifacts.d.ts +6 -0
  189. package/dist/rooms-tasks/workspace-artifacts.js +93 -0
  190. package/dist/rooms-tasks/workspace.d.ts +35 -0
  191. package/dist/rooms-tasks/workspace.js +325 -0
  192. package/dist/runner.d.ts +23 -4
  193. package/dist/runner.js +409 -61
  194. package/dist/session/acp.d.ts +75 -2
  195. package/dist/session/acp.js +345 -82
  196. package/dist/session/arbiter.d.ts +98 -0
  197. package/dist/session/arbiter.js +272 -3
  198. package/dist/session/codex-app-server.d.ts +45 -1
  199. package/dist/session/codex-app-server.js +225 -32
  200. package/dist/session/control.d.ts +17 -3
  201. package/dist/session/control.js +219 -23
  202. package/dist/session/conversation-normalizer.d.ts +2 -1
  203. package/dist/session/conversation-normalizer.js +1 -1
  204. package/dist/session/conversation-observation.d.ts +25 -0
  205. package/dist/session/conversation-observation.js +100 -0
  206. package/dist/session/conversation-replay.d.ts +50 -0
  207. package/dist/session/conversation-replay.js +203 -0
  208. package/dist/session/conversation-store.d.ts +15 -1
  209. package/dist/session/conversation-store.js +213 -26
  210. package/dist/session/conversation-types.d.ts +57 -4
  211. package/dist/session/prompt-batch.d.ts +79 -0
  212. package/dist/session/prompt-batch.js +120 -0
  213. package/dist/session/task-notice.d.ts +14 -0
  214. package/dist/session/task-notice.js +13 -0
  215. package/dist/session/types.d.ts +42 -1
  216. package/dist/setup-tunnel-preflight.d.ts +34 -0
  217. package/dist/setup-tunnel-preflight.js +62 -0
  218. package/dist/socket-path.d.ts +2 -0
  219. package/dist/socket-path.js +22 -0
  220. package/dist/spawn.d.ts +5 -1
  221. package/dist/spawn.js +40 -4
  222. package/dist/startup-prompt.d.ts +15 -0
  223. package/dist/startup-prompt.js +71 -0
  224. package/dist/startup-readiness.d.ts +20 -1
  225. package/dist/startup-readiness.js +66 -3
  226. package/dist/startup-telemetry.d.ts +16 -0
  227. package/dist/startup-telemetry.js +46 -0
  228. package/dist/subscriptions/cli.d.ts +91 -0
  229. package/dist/subscriptions/cli.js +223 -0
  230. package/dist/subscriptions/launch.d.ts +37 -0
  231. package/dist/subscriptions/launch.js +93 -0
  232. package/dist/subscriptions/login.d.ts +77 -0
  233. package/dist/subscriptions/login.js +215 -0
  234. package/dist/subscriptions/observed.d.ts +7 -0
  235. package/dist/subscriptions/observed.js +44 -0
  236. package/dist/subscriptions/probe.d.ts +10 -0
  237. package/dist/subscriptions/probe.js +73 -0
  238. package/dist/subscriptions/service.d.ts +102 -0
  239. package/dist/subscriptions/service.js +364 -0
  240. package/dist/subscriptions/store.d.ts +71 -0
  241. package/dist/subscriptions/store.js +143 -0
  242. package/dist/supervisor/adoption.d.ts +8 -0
  243. package/dist/supervisor/adoption.js +78 -0
  244. package/dist/supervisor/catalog.d.ts +62 -0
  245. package/dist/supervisor/catalog.js +254 -0
  246. package/dist/supervisor/fleet.d.ts +8 -0
  247. package/dist/supervisor/fleet.js +89 -0
  248. package/dist/supervisor/index.js +3 -6
  249. package/dist/supervisor/launchd.d.ts +1 -1
  250. package/dist/supervisor/launchd.js +26 -13
  251. package/dist/supervisor/legacy.d.ts +35 -0
  252. package/dist/supervisor/legacy.js +524 -0
  253. package/dist/supervisor/manager.d.ts +15 -0
  254. package/dist/supervisor/manager.js +270 -0
  255. package/dist/supervisor/registration-refresh.d.ts +45 -0
  256. package/dist/supervisor/registration-refresh.js +266 -0
  257. package/dist/supervisor/scope.d.ts +4 -0
  258. package/dist/supervisor/scope.js +10 -0
  259. package/dist/supervisor/systemd.d.ts +2 -2
  260. package/dist/supervisor/systemd.js +18 -37
  261. package/dist/task-supervision.d.ts +9 -0
  262. package/dist/task-supervision.js +145 -0
  263. package/dist/temp-idle.d.ts +17 -0
  264. package/dist/temp-idle.js +42 -0
  265. package/dist/temp-lifecycle.d.ts +23 -8
  266. package/dist/temp-lifecycle.js +123 -66
  267. package/dist/temp-supervisor-recovery.d.ts +1 -1
  268. package/dist/temp-supervisor-recovery.js +2 -2
  269. package/dist/tunnel-setup-completion.d.ts +7 -0
  270. package/dist/tunnel-setup-completion.js +13 -0
  271. package/dist/tunnel-setup-input.d.ts +19 -0
  272. package/dist/tunnel-setup-input.js +130 -0
  273. package/dist/watchdog/query.d.ts +3 -1
  274. package/dist/watchdog/query.js +2 -1
  275. package/dist/web/agent-attachments.d.ts +29 -0
  276. package/dist/web/agent-attachments.js +162 -0
  277. package/dist/web/auth.d.ts +65 -2
  278. package/dist/web/auth.js +201 -11
  279. package/dist/web/available-port.d.ts +2 -0
  280. package/dist/web/available-port.js +23 -0
  281. package/dist/web/control.d.ts +3 -3
  282. package/dist/web/control.js +10 -8
  283. package/dist/web/conversation-stream-codec.d.ts +23 -0
  284. package/dist/web/conversation-stream-codec.js +110 -0
  285. package/dist/web/conversation-stream.d.ts +26 -0
  286. package/dist/web/conversation-stream.js +239 -0
  287. package/dist/web/events.d.ts +2 -0
  288. package/dist/web/events.js +9 -0
  289. package/dist/web/fleet-config-service.d.ts +7 -1
  290. package/dist/web/fleet-config-service.js +61 -16
  291. package/dist/web/prefix-gateway.d.ts +32 -0
  292. package/dist/web/prefix-gateway.js +248 -0
  293. package/dist/web/resource-views.d.ts +138 -0
  294. package/dist/web/resource-views.js +519 -0
  295. package/dist/web/runtime.d.ts +3 -1
  296. package/dist/web/runtime.js +132 -34
  297. package/dist/web/server.d.ts +19 -0
  298. package/dist/web/server.js +893 -71
  299. package/dist/web/service.d.ts +11 -2
  300. package/dist/web/service.js +30 -4
  301. package/dist/web/workspace-devices.d.ts +107 -0
  302. package/dist/web/workspace-devices.js +277 -0
  303. package/dist/web/workspace-tunnel.d.ts +1 -0
  304. package/dist/web/workspace-tunnel.js +34 -0
  305. package/dist/workspace-enrollment.d.ts +83 -0
  306. package/dist/workspace-enrollment.js +605 -0
  307. package/dist/workspace-replacement.d.ts +66 -0
  308. package/dist/workspace-replacement.js +238 -0
  309. package/docs/acp-files.md +146 -0
  310. package/docs/conversation-stream.md +216 -0
  311. package/docs/fleet-migration.md +54 -0
  312. package/docs/live-voice.md +13 -0
  313. package/docs/room-layouts.md +227 -0
  314. package/docs/self-reminders.md +374 -0
  315. package/docs/task-progress-review.md +143 -0
  316. package/docs/task-workspaces.md +225 -0
  317. package/docs/validation/managed-cli-permissions.md +421 -0
  318. package/examples/room-layouts/fresh.yaml +27 -0
  319. package/examples/room-layouts/scoped.yaml +21 -0
  320. package/examples/room-layouts/shared.yaml +19 -0
  321. package/package.json +25 -20
  322. package/presets/README.md +78 -0
  323. package/presets/brain-catalog.json +8 -2
  324. package/presets/fleet/agent_templates/Critic.yaml +1 -1
  325. package/presets/fleet/agent_templates/Developer.yaml +1 -1
  326. package/presets/fleet/agent_templates/Engineer.yaml +5 -0
  327. package/presets/fleet/agent_templates/LocalCoordinator.yaml +6 -3
  328. package/presets/fleet/agents/FleetCoordinator.yaml +1 -1
  329. package/presets/fleet/brains/claude-fable-5-1-high.yaml +5 -0
  330. package/presets/fleet/brains/claude-fable-5-1-low.yaml +5 -0
  331. package/presets/fleet/brains/claude-fable-5-1-max.yaml +5 -0
  332. package/presets/fleet/brains/claude-fable-5-1-medium.yaml +5 -0
  333. package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +5 -0
  334. package/presets/fleet/brains/claude-fable-5-high.yaml +1 -1
  335. package/presets/fleet/brains/claude-fable-5-low.yaml +1 -1
  336. package/presets/fleet/brains/claude-fable-5-max.yaml +1 -1
  337. package/presets/fleet/brains/claude-fable-5-medium.yaml +1 -1
  338. package/presets/fleet/brains/claude-fable-5-xhigh.yaml +1 -1
  339. package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +4 -0
  340. package/presets/fleet/brains/claude-opus-5-5-high.yaml +5 -0
  341. package/presets/fleet/brains/claude-opus-5-5-low.yaml +5 -0
  342. package/presets/fleet/brains/claude-opus-5-5-max.yaml +5 -0
  343. package/presets/fleet/brains/claude-opus-5-5-medium.yaml +5 -0
  344. package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +5 -0
  345. package/presets/fleet/brains/claude-opus-5-high.yaml +1 -1
  346. package/presets/fleet/brains/claude-opus-5-low.yaml +1 -1
  347. package/presets/fleet/brains/claude-opus-5-max.yaml +1 -1
  348. package/presets/fleet/brains/claude-opus-5-medium.yaml +1 -1
  349. package/presets/fleet/brains/claude-opus-5-xhigh.yaml +1 -1
  350. package/presets/fleet/brains/claude-sonnet-5-high.yaml +1 -1
  351. package/presets/fleet/brains/claude-sonnet-5-low.yaml +1 -1
  352. package/presets/fleet/brains/claude-sonnet-5-max.yaml +1 -1
  353. package/presets/fleet/brains/claude-sonnet-5-medium.yaml +1 -1
  354. package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +1 -1
  355. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +1 -1
  356. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +1 -1
  357. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +1 -1
  358. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +1 -1
  359. package/presets/fleet/brains/codex-gpt-5-4-high.yaml +1 -1
  360. package/presets/fleet/brains/codex-gpt-5-4-low.yaml +1 -1
  361. package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +1 -1
  362. package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +1 -1
  363. package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +1 -1
  364. package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +1 -1
  365. package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +1 -1
  366. package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +1 -1
  367. package/presets/fleet/brains/codex-gpt-5-5-high.yaml +1 -1
  368. package/presets/fleet/brains/codex-gpt-5-5-low.yaml +1 -1
  369. package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +1 -1
  370. package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +1 -1
  371. package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +1 -1
  372. package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +1 -1
  373. package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +1 -1
  374. package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +1 -1
  375. package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +1 -1
  376. package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +1 -1
  377. package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +1 -1
  378. package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +1 -1
  379. package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +1 -1
  380. package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +1 -1
  381. package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +1 -1
  382. package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +1 -1
  383. package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +1 -1
  384. package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +1 -1
  385. package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +1 -1
  386. package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +1 -1
  387. package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +1 -1
  388. package/presets/fleet/brains/codex-gpt-6-1-sol-high.yaml +5 -0
  389. package/presets/fleet/brains/codex-gpt-6-1-sol-low.yaml +5 -0
  390. package/presets/fleet/brains/codex-gpt-6-1-sol-max.yaml +5 -0
  391. package/presets/fleet/brains/codex-gpt-6-1-sol-medium.yaml +5 -0
  392. package/presets/fleet/brains/codex-gpt-6-1-sol-ultra.yaml +5 -0
  393. package/presets/fleet/brains/codex-gpt-6-1-sol-xhigh.yaml +5 -0
  394. package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +1 -1
  395. package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +1 -1
  396. package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +1 -1
  397. package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +1 -1
  398. package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +1 -1
  399. package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +1 -1
  400. package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +5 -0
  401. package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +5 -0
  402. package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +5 -0
  403. package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +5 -0
  404. package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +5 -0
  405. package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +5 -0
  406. package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +5 -0
  407. package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +5 -0
  408. package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +5 -0
  409. package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +5 -0
  410. package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +5 -0
  411. package/presets/fleet/roles/Coordinator.yaml +21 -13
  412. package/presets/fleet/roles/Critic.yaml +24 -0
  413. package/presets/fleet/roles/Developer.yaml +32 -0
  414. package/presets/fleet/roles/Engineer.yaml +72 -0
  415. package/presets/fleet/roles/LocalCoordinator.yaml +17 -0
  416. package/presets/fleet/room_layouts/engineering.yaml +25 -0
  417. package/presets/fleet/room_layouts/pair.yaml +24 -0
  418. package/presets/fleet/room_layouts/single.yaml +17 -0
  419. package/presets/fleet/room_layouts/team.yaml +30 -0
  420. package/presets/fleet/room_templates/engineering.yaml +19 -0
  421. package/presets/manifest.json +2 -2
  422. package/dist/web-app/assets/index-59GF-gsZ.css +0 -1
  423. package/dist/web-app/assets/index-C0h3ALvs.js +0 -10
  424. package/dist/web-app/icons/ours-fleet-maskable.svg +0 -4
  425. package/dist/web-app/icons/ours-fleet.svg +0 -4
  426. package/dist/web-app/index.html +0 -17
  427. package/dist/web-app/manifest.webmanifest +0 -15
  428. package/dist/web-app/offline.html +0 -18
  429. package/dist/web-app/sw.js +0 -51
@@ -0,0 +1,270 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { closeSync, existsSync, openSync, rmSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { randomUUID } from 'node:crypto';
5
+ import { producerConfig } from '../notifications/outbox.js';
6
+ import { TaskNotificationProducer } from '../notifications/task-producer.js';
7
+ import { acquireOwnerBinderLease } from '../owner-channel/binder.js';
8
+ import { stateRoot } from '../paths.js';
9
+ import { readRestartLedger, writeRestartLedger, backoffFor, RESTART_FAIL_THRESHOLD } from '../runner.js';
10
+ import { realExec } from '../exec.js';
11
+ import { listMemberKeys, managedMemberEnvironment, memberPath, memberProcess, readMember, withMemberLock, writeMember } from './catalog.js';
12
+ /** One parent owns process creation for every kind of Fleet member. */
13
+ export async function runFleetManager(entrypoint, deps = {}) {
14
+ const lease = await acquireOwnerBinderLease(join(stateRoot(), 'supervisor'), 'fleet-manager', 'fleet-manager');
15
+ const children = new Map();
16
+ const now = deps.now ?? Date.now;
17
+ const log = deps.log ?? (message => console.error(message));
18
+ const exec = deps.exec ?? realExec;
19
+ const sleep = deps.sleep ?? (ms => new Promise(resolve => setTimeout(resolve, ms)));
20
+ let stopping = false;
21
+ const stop = () => { stopping = true; };
22
+ process.on('SIGTERM', stop);
23
+ process.on('SIGINT', stop);
24
+ const spawnChild = deps.spawnChild ?? ((member, generation) => {
25
+ if (!existsSync(member.dir))
26
+ throw Error('Fleet member state missing');
27
+ const fd = openSync(join(member.dir, 'supervisor.log'), 'a', 0o600);
28
+ try {
29
+ return spawn(process.execPath, [entrypoint, '_run-managed', member.key, generation], {
30
+ // A process group permits bounded cleanup of a hung runner and its harness.
31
+ // IPC couples its lifetime to this parent even if the parent is SIGKILLed.
32
+ detached: true, stdio: ['ignore', fd, fd, 'ipc'], env: managedMemberEnvironment(member),
33
+ });
34
+ }
35
+ finally {
36
+ closeSync(fd);
37
+ }
38
+ });
39
+ let notifications;
40
+ async function reconcile(key) {
41
+ await withMemberLock(key, async () => {
42
+ const member = readMember(key);
43
+ const owned = children.get(key);
44
+ if (!member) {
45
+ if (owned && !owned.stoppingAt) {
46
+ owned.stoppingAt = Date.now();
47
+ owned.child.kill('SIGTERM');
48
+ }
49
+ return;
50
+ }
51
+ // Our ChildProcess supplies exit notification for steady owned members.
52
+ // Probe argv only for predecessor discovery or before an actual stop;
53
+ // macOS otherwise spawned one ps process per member every 250ms.
54
+ if (owned && owned.generation === member.generation && member.desired === 'running' && !stopping && !owned.stoppingAt)
55
+ return;
56
+ const live = await memberProcess(member, exec);
57
+ if (member.desired === 'stopped' || stopping) {
58
+ if (live.state === 'running' && live.pid) {
59
+ if (owned && owned.generation === member.generation) {
60
+ if (!owned.stoppingAt) {
61
+ owned.stoppingAt = Date.now();
62
+ owned.child.kill('SIGTERM');
63
+ }
64
+ else if (Date.now() - owned.stoppingAt >= 15_000) {
65
+ // Re-check exact generation immediately before a group signal.
66
+ try {
67
+ process.kill(-live.pid, 'SIGKILL');
68
+ }
69
+ catch { /* next probe proves absence */ }
70
+ }
71
+ }
72
+ else {
73
+ // A predecessor's child has lost IPC and is shutting down. Never adopt
74
+ // or launch alongside it; its worker also holds the member lifetime lease.
75
+ log(`[fleet] waiting for predecessor ${key}`);
76
+ }
77
+ }
78
+ return;
79
+ }
80
+ if (live.state !== 'stopped' || owned || (member.retryAt ?? 0) > Date.now() || readRestartLedger(member.dir).circuit === 'open')
81
+ return;
82
+ const generation = randomUUID();
83
+ writeMember({ ...member, generation, pid: undefined });
84
+ let child;
85
+ try {
86
+ child = spawnChild(member, generation);
87
+ }
88
+ catch {
89
+ if (!existsSync(member.dir)) {
90
+ writeMember({ ...member, generation: undefined, pid: undefined, retryAt: Date.now() + 30_000 });
91
+ return;
92
+ }
93
+ const ledger = readRestartLedger(member.dir);
94
+ const failures = ledger.consecutiveImmediateFailures + 1;
95
+ writeRestartLedger(member.dir, { ...ledger, consecutiveImmediateFailures: failures, circuit: failures >= RESTART_FAIL_THRESHOLD ? 'open' : 'closed', nextDelayMs: backoffFor(failures), lastReason: 'Fleet child spawn failed', updatedAt: new Date().toISOString() });
96
+ writeMember({ ...member, generation: undefined, pid: undefined, retryAt: Date.now() + backoffFor(failures) });
97
+ return;
98
+ }
99
+ const childState = { child, generation, startedAt: now() };
100
+ children.set(key, childState);
101
+ writeMember({ ...member, generation, pid: child.pid });
102
+ let finished = false;
103
+ const finish = (failed) => {
104
+ if (finished)
105
+ return;
106
+ finished = true;
107
+ if (children.get(key) === childState)
108
+ children.delete(key);
109
+ void withMemberLock(key, async () => {
110
+ const current = readMember(key);
111
+ if (!current || current.generation !== generation)
112
+ return;
113
+ if (current.kind === 'temporary' && !existsSync(current.dir)) {
114
+ rmSync(memberPath(key));
115
+ return;
116
+ }
117
+ let taskCompleted = false;
118
+ if (!failed && !stopping && !childState.stoppingAt && current.kind === 'task') {
119
+ try {
120
+ taskCompleted = !(await import('../task-supervision.js')).taskSupervisorMayRun(current.name);
121
+ }
122
+ catch {
123
+ failed = true;
124
+ } // uncertainty consumes the existing bounded failure budget
125
+ }
126
+ const ledger = readRestartLedger(current.dir);
127
+ let previousFailures = ledger.consecutiveImmediateFailures;
128
+ if (failed && !stopping && !childState.stoppingAt) {
129
+ const { stableSupervisorWindow } = await import('../runner.js');
130
+ if (now() - childState.startedAt >= stableSupervisorWindow(current.name, current.kind !== 'permanent', current.configPath) * 1000)
131
+ previousFailures = 0;
132
+ }
133
+ const failures = failed && !stopping && !childState.stoppingAt ? previousFailures + 1 : ledger.consecutiveImmediateFailures;
134
+ if (failed && !stopping && !childState.stoppingAt && existsSync(current.dir))
135
+ writeRestartLedger(current.dir, {
136
+ ...ledger, consecutiveImmediateFailures: failures, lastReason: 'Fleet member runner exited unexpectedly',
137
+ circuit: failures >= RESTART_FAIL_THRESHOLD ? 'open' : 'closed', nextDelayMs: backoffFor(failures),
138
+ updatedAt: new Date().toISOString(), ...(failures >= RESTART_FAIL_THRESHOLD ? { openedAt: new Date().toISOString() } : {}),
139
+ });
140
+ writeMember({ ...current, pid: undefined, generation: undefined,
141
+ retryAt: Date.now() + backoffFor(failures),
142
+ // A completed task/transient worker must not become an endless launch loop.
143
+ desired: !failed && !stopping && !childState.stoppingAt && (current.kind === 'temporary' || taskCompleted) ? 'stopped' : current.desired });
144
+ }).catch(() => log(`[fleet] child completion record unavailable: ${key}`));
145
+ };
146
+ child.once('error', () => finish(true));
147
+ child.once('exit', (code, signal) => {
148
+ // The tracked child created this private process group; reclaim any harness
149
+ // descendants even when the runner itself crashed before cleaning them.
150
+ if (child.pid) {
151
+ try {
152
+ process.kill(-child.pid, 'SIGKILL');
153
+ }
154
+ catch { /* already absent */ }
155
+ }
156
+ finish(code !== 0 || signal !== null);
157
+ });
158
+ });
159
+ }
160
+ try {
161
+ try {
162
+ const config = producerConfig(process.env, undefined, log);
163
+ if (config)
164
+ notifications = new TaskNotificationProducer(config, log);
165
+ }
166
+ catch {
167
+ log('[fleet] task notifications unavailable; committed events retained');
168
+ }
169
+ try {
170
+ const { resumeFleetTransfers } = await import('./adoption.js');
171
+ await resumeFleetTransfers(entrypoint, exec);
172
+ }
173
+ catch (error) {
174
+ // Existing catalog members continue; uncertain transfers remain held with
175
+ // their receipts. Operator init/up reports the same actionable failure.
176
+ log(`[fleet] transfer pending: ${error instanceof Error ? error.message : 'proof unavailable'}`);
177
+ }
178
+ while (!stopping && !deps.shouldStop?.()) {
179
+ for (const key of listMemberKeys()) {
180
+ try {
181
+ await reconcile(key);
182
+ }
183
+ catch {
184
+ log(`[fleet] reconciliation held: ${key}`);
185
+ }
186
+ }
187
+ await sleep(250);
188
+ }
189
+ }
190
+ finally {
191
+ stopping = true;
192
+ await notifications?.close();
193
+ // Shutdown preserves desired intent; the successor resumes the same catalog.
194
+ const deadline = Date.now() + 17_000;
195
+ while (children.size && Date.now() < deadline) {
196
+ for (const key of children.keys())
197
+ await reconcile(key);
198
+ await sleep(100);
199
+ }
200
+ process.off('SIGTERM', stop);
201
+ process.off('SIGINT', stop);
202
+ lease.release();
203
+ }
204
+ }
205
+ /** Workers cannot outlive their parent or overlap another generation of this member. */
206
+ export async function runManagedMember(key, generation) {
207
+ let stopping = false;
208
+ const stop = () => { stopping = true; };
209
+ const disconnected = () => {
210
+ stopping = true;
211
+ process.kill(process.pid, 'SIGTERM');
212
+ setTimeout(() => process.kill(-process.pid, 'SIGKILL'), 15_000).unref();
213
+ };
214
+ process.on('SIGTERM', stop);
215
+ process.on('SIGINT', stop);
216
+ process.on('disconnect', disconnected);
217
+ if (!process.connected)
218
+ throw Error('FLEET_MEMBER_PARENT_MISSING');
219
+ const lease = await acquireOwnerBinderLease(join(stateRoot(), 'supervisor', 'workers', key), 'fleet-member', key);
220
+ try {
221
+ const member = readMember(key);
222
+ if (!member || member.generation !== generation || member.desired !== 'running' || stopping)
223
+ return;
224
+ const { runSupervised } = await import('../runner.js');
225
+ if (member.kind === 'permanent') {
226
+ const { waitForRoleDaemon } = await import('../startup-readiness.js');
227
+ // Waiting for a daemon is readiness, not a failed harness attempt.
228
+ while (!stopping) {
229
+ try {
230
+ await waitForRoleDaemon(member.name, member.configPath);
231
+ break;
232
+ }
233
+ catch {
234
+ if (!stopping)
235
+ await new Promise(resolve => setTimeout(resolve, 5000));
236
+ }
237
+ }
238
+ if (!stopping)
239
+ await runSupervised(member.name, { configPath: member.configPath }, { shouldStop: () => stopping });
240
+ }
241
+ else if (member.kind === 'watchdogs') {
242
+ const { runScheduler } = await import('../watchdog/scheduler.js');
243
+ await runScheduler(member.configPath, {
244
+ now: () => new Date(), sleep: ms => new Promise(resolve => setTimeout(resolve, ms)),
245
+ log: message => console.log(message), binPath: process.argv[1], shouldStop: () => stopping,
246
+ });
247
+ }
248
+ else {
249
+ const { runTempSupervisor } = await import('../temp-supervisor-recovery.js');
250
+ const { runTemp } = await import('../runner.js');
251
+ await runTempSupervisor(member.name, process.argv[1], () => runTemp(member.name, {
252
+ shouldStop: () => stopping, suspendOnStop: true,
253
+ }), true);
254
+ }
255
+ }
256
+ finally {
257
+ lease.release();
258
+ process.off('SIGTERM', stop);
259
+ process.off('SIGINT', stop);
260
+ process.off('disconnect', disconnected);
261
+ if (process.connected)
262
+ process.disconnect();
263
+ else if (stopping) {
264
+ try {
265
+ process.kill(-process.pid, 'SIGKILL');
266
+ }
267
+ catch { /* no private group */ }
268
+ }
269
+ }
270
+ }
@@ -0,0 +1,45 @@
1
+ import { type Exec } from '../exec.js';
2
+ /** The release tree that owns a Fleet CLI script, if it is a packaged one. */
3
+ export declare function releaseRoot(script: string): string | undefined;
4
+ export type RegistrationState =
5
+ /** No definition installed; refresh never creates one. */
6
+ 'absent'
7
+ /** Already runs the invoking release, or a stable launcher outside any release tree. */
8
+ | 'current'
9
+ /** Pins another release tree. */
10
+ | 'stale'
11
+ /** Rewritten to the invoking release by this refresh. */
12
+ | 'refreshed'
13
+ /** Rewritten, but the service manager did not reload it; it still starts the old release. */
14
+ | 'reload-failed'
15
+ /** Not a definition Fleet generated, or overridden; left untouched. */
16
+ | 'unrecognized';
17
+ export type RunningState = 'current' | 'stale' | 'stopped' | 'unknown';
18
+ export interface RegistrationReport {
19
+ service: 'supervisor' | 'web' | 'watchdogs';
20
+ path: string;
21
+ state: RegistrationState;
22
+ /** Release tree the definition pinned before this call. */
23
+ from?: string;
24
+ to?: string;
25
+ /** What the live process runs, which a definition rewrite does not change. */
26
+ running: RunningState;
27
+ detail?: string;
28
+ }
29
+ export interface RefreshOptions {
30
+ /** realpath of the invoking CLI script. */
31
+ binPath: string;
32
+ /** Rewrite stale definitions; false only reports. */
33
+ write: boolean;
34
+ platform?: NodeJS.Platform;
35
+ homeDir?: string;
36
+ exec?: Exec;
37
+ exists?(path: string): boolean;
38
+ /** argv of a live process, for comparing what it runs with its definition. */
39
+ processArgv?(pid: number): string[] | undefined;
40
+ }
41
+ /** Report, and with `write` refresh, each installed Fleet service definition. */
42
+ export declare function refreshServiceRegistrations(options: RefreshOptions): Promise<RegistrationReport[]>;
43
+ /** One operator line per definition, including the restart a refresh cannot do. */
44
+ export declare function describeRegistration(report: RegistrationReport): string;
45
+ export declare const registrationDrift: (reports: RegistrationReport[]) => RegistrationReport[];
@@ -0,0 +1,266 @@
1
+ import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
2
+ import { join, sep } from 'node:path';
3
+ import { replaceFileAtomically } from '../atomic-file.js';
4
+ import { realExec } from '../exec.js';
5
+ import { home } from '../paths.js';
6
+ import { WATCHDOG_LAUNCHD_LABEL, WATCHDOG_SYSTEMD_UNIT } from '../watchdog/service.js';
7
+ import { WEB_LAUNCHD_LABEL, WEB_SYSTEMD_UNIT } from '../web/service.js';
8
+ import { labelFor } from './launchd.js';
9
+ import { UNIT_TEMPLATE } from './systemd.js';
10
+ /**
11
+ * Service definitions written by `init`, `web install` and the watchdog
12
+ * installer pin the realpath of the CLI that wrote them: an immutable,
13
+ * per-release package tree. An update installs a new tree and moves the
14
+ * `ours-fleet` command, but a definition is only rewritten when that command
15
+ * runs again, so after the next reboot the OS starts the previous release's
16
+ * supervisor while agents, web and CLI run the new one.
17
+ *
18
+ * Refresh re-points an existing Fleet-generated definition at the invoking
19
+ * release and changes nothing else: environment, profile, ports and drop-ins
20
+ * are kept byte for byte, and no unit is created, enabled, started, stopped
21
+ * or restarted. A running process keeps its old code until its operator
22
+ * restarts it, which the report states rather than doing.
23
+ */
24
+ const PACKAGE_SCRIPT = ['node_modules', '@ours.network', 'fleet', 'dist', 'cli.js'].join(sep);
25
+ /** The release tree that owns a Fleet CLI script, if it is a packaged one. */
26
+ export function releaseRoot(script) {
27
+ const suffix = sep + PACKAGE_SCRIPT;
28
+ return script.endsWith(suffix) ? script.slice(0, -suffix.length) : undefined;
29
+ }
30
+ function definitions(platform, homeDir) {
31
+ if (platform === 'linux') {
32
+ const dir = join(homeDir, '.config', 'systemd', 'user');
33
+ return [
34
+ { service: 'supervisor', unit: UNIT_TEMPLATE, path: join(dir, UNIT_TEMPLATE), entry: '_run-fleet',
35
+ marker: 'ours-fleet process manager' },
36
+ { service: 'web', unit: WEB_SYSTEMD_UNIT, path: join(dir, WEB_SYSTEMD_UNIT), entry: 'web',
37
+ marker: 'ours-fleet localhost web console' },
38
+ { service: 'watchdogs', unit: WATCHDOG_SYSTEMD_UNIT, path: join(dir, WATCHDOG_SYSTEMD_UNIT), entry: '_run-watchdogs',
39
+ marker: 'ours-fleet watchdog scheduler' },
40
+ ];
41
+ }
42
+ if (platform === 'darwin') {
43
+ const dir = join(homeDir, 'Library', 'LaunchAgents');
44
+ return [
45
+ { service: 'supervisor', path: join(dir, `${labelFor('fleet')}.plist`), entry: '_run-fleet', marker: labelFor('fleet') },
46
+ { service: 'web', path: join(dir, `${WEB_LAUNCHD_LABEL}.plist`), entry: 'web', marker: WEB_LAUNCHD_LABEL },
47
+ { service: 'watchdogs', path: join(dir, `${WATCHDOG_LAUNCHD_LABEL}.plist`), entry: '_run-watchdogs',
48
+ marker: WATCHDOG_LAUNCHD_LABEL },
49
+ ];
50
+ }
51
+ return [];
52
+ }
53
+ const NODE_RUNTIME = /^node(js)?[0-9.]*$/;
54
+ const systemdQuote = (value) => `"${value.replace(/[%\\"]/g, char => char === '%' ? '%%' : `\\${char}`)}"`;
55
+ const xml = (value) => value.replace(/[&<>"']/g, char => ({
56
+ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&apos;',
57
+ }[char]));
58
+ const unxml = (value) => value.replaceAll('&quot;', '"').replaceAll('&apos;', "'")
59
+ .replaceAll('&lt;', '<').replaceAll('&gt;', '>').replaceAll('&amp;', '&');
60
+ function parseGenerated(text, platform, definition) {
61
+ if (platform === 'linux') {
62
+ const lines = text.split('\n');
63
+ const only = (pattern) => {
64
+ const found = lines.flatMap((line, index) => pattern.test(line) ? [index] : []);
65
+ return found.length === 1 ? found[0] : undefined;
66
+ };
67
+ if (lines[only(/^Description=/) ?? -1] !== `Description=${definition.marker}`)
68
+ return undefined;
69
+ if (lines[only(/^Type=/) ?? -1] !== 'Type=simple')
70
+ return undefined;
71
+ const exec = only(/^\s*ExecStart\s*=/);
72
+ if (exec === undefined || !lines[exec].startsWith('ExecStart="'))
73
+ return undefined;
74
+ const args = [...lines[exec].slice('ExecStart='.length).matchAll(/"((?:[^"\\]|\\.)*)"|(\S+)/g)]
75
+ .map(match => match[1] !== undefined ? match[1].replace(/\\(.)/g, '$1').replaceAll('%%', '%') : match[2]);
76
+ const [runtime, script, entry] = args;
77
+ if (!runtime?.startsWith('/') || !NODE_RUNTIME.test(runtime.split('/').at(-1)) || entry !== definition.entry || !script)
78
+ return undefined;
79
+ const quoted = `${systemdQuote(runtime)} ${systemdQuote(script)} `;
80
+ if (!lines[exec].startsWith(`ExecStart=${quoted}`))
81
+ return undefined;
82
+ const pathLine = only(/^Environment="PATH=/);
83
+ return {
84
+ script,
85
+ rewrite(next, path) {
86
+ const out = [...lines];
87
+ out[exec] = `ExecStart=${systemdQuote(runtime)} ${systemdQuote(next)} ${lines[exec].slice(`ExecStart=${quoted}`.length)}`;
88
+ if (pathLine !== undefined) {
89
+ const value = /^Environment="PATH=((?:[^"\\]|\\.)*)"$/.exec(lines[pathLine])?.[1];
90
+ if (value !== undefined && !/[\\%]/.test(value))
91
+ out[pathLine] = `Environment="PATH=${value.split(':').map(path).join(':')}"`;
92
+ }
93
+ return out.join('\n');
94
+ },
95
+ };
96
+ }
97
+ if (!new RegExp(`<key>Label</key>\\s*<string>${definition.marker.replaceAll('.', '\\.')}</string>`).test(text))
98
+ return undefined;
99
+ const program = /<key>ProgramArguments<\/key>\s*<array>\s*<string>([^<]*)<\/string>\s*<string>([^<]*)<\/string>\s*<string>([^<]*)<\/string>/.exec(text);
100
+ if (!program)
101
+ return undefined;
102
+ const [, runtime, script, entry] = program.map(unxml);
103
+ if (!runtime.startsWith('/') || !NODE_RUNTIME.test(runtime.split('/').at(-1)) || entry !== definition.entry)
104
+ return undefined;
105
+ return {
106
+ script,
107
+ rewrite(next, path) {
108
+ const head = program[0];
109
+ const moved = head.replace(`<string>${program[2]}</string>`, `<string>${xml(next)}</string>`);
110
+ let out = text.replace(head, moved);
111
+ out = out.replace(/(<key>PATH<\/key>\s*<string>)([^<]*)(<\/string>)/, (_all, open, value, close) => `${open}${xml(unxml(value).split(':').map(path).join(':'))}${close}`);
112
+ return out;
113
+ },
114
+ };
115
+ }
116
+ /** A systemd drop-in that sets ExecStart owns the command line; Fleet must not fight it. */
117
+ function overriddenByDropIn(path) {
118
+ const dir = `${path}.d`;
119
+ try {
120
+ return readdirSync(dir).some(name => name.endsWith('.conf')
121
+ && /^\s*ExecStart\s*=/m.test(readFileSync(join(dir, name), 'utf8')));
122
+ }
123
+ catch {
124
+ return false;
125
+ }
126
+ }
127
+ const procArgv = (pid) => {
128
+ try {
129
+ return readFileSync(`/proc/${pid}/cmdline`, 'utf8').split('\0').filter(Boolean);
130
+ }
131
+ catch {
132
+ return undefined;
133
+ }
134
+ };
135
+ async function runningState(definition, options, platform) {
136
+ if (platform !== 'linux' || !definition.unit)
137
+ return 'unknown';
138
+ const exec = options.exec ?? realExec;
139
+ const shown = await exec('systemctl', ['--user', 'show', '-p', 'MainPID', '--value', definition.unit]);
140
+ if (shown.code !== 0)
141
+ return 'unknown';
142
+ const pid = Number(shown.stdout.trim());
143
+ if (!Number.isSafeInteger(pid) || pid < 0)
144
+ return 'unknown';
145
+ if (pid === 0)
146
+ return 'stopped';
147
+ const argv = (options.processArgv ?? procArgv)(pid);
148
+ const script = argv?.[1];
149
+ if (!script)
150
+ return 'unknown';
151
+ // A stable launcher resolves to whichever release it pointed at when started.
152
+ if (!releaseRoot(script))
153
+ return 'unknown';
154
+ return script === options.binPath ? 'current' : 'stale';
155
+ }
156
+ /**
157
+ * Whether systemd still runs an older load of this unit than the file on disk.
158
+ * A rewrite whose daemon-reload failed looks current on the next pass; this is
159
+ * what keeps that pending reload visible instead of losing it.
160
+ */
161
+ async function reloadPending(definition, options, platform) {
162
+ if (platform !== 'linux' || !definition.unit)
163
+ return false;
164
+ const shown = await (options.exec ?? realExec)('systemctl', ['--user', 'show', '-p', 'NeedDaemonReload', '--value', definition.unit]);
165
+ return shown.code === 0 && shown.stdout.trim() === 'yes';
166
+ }
167
+ /** Report, and with `write` refresh, each installed Fleet service definition. */
168
+ export async function refreshServiceRegistrations(options) {
169
+ const platform = options.platform ?? process.platform;
170
+ const homeDir = options.homeDir ?? home();
171
+ const exists = options.exists ?? existsSync;
172
+ const target = releaseRoot(options.binPath);
173
+ const reports = [];
174
+ let rewrote = false;
175
+ for (const definition of definitions(platform, homeDir)) {
176
+ const base = { service: definition.service, path: definition.path };
177
+ if (!exists(definition.path)) {
178
+ reports.push({ ...base, state: 'absent', running: 'stopped' });
179
+ continue;
180
+ }
181
+ const running = await runningState(definition, options, platform);
182
+ const text = readFileSync(definition.path, 'utf8');
183
+ const generated = parseGenerated(text, platform, definition);
184
+ if (!generated) {
185
+ reports.push({ ...base, state: 'unrecognized', running,
186
+ detail: 'not the definition Fleet generates for this service; left untouched' });
187
+ continue;
188
+ }
189
+ const from = releaseRoot(generated.script);
190
+ if (!from || generated.script === options.binPath) {
191
+ if (await reloadPending(definition, options, platform)) {
192
+ // Normally an earlier refresh whose daemon-reload failed: the file is
193
+ // right, the loaded unit is not. Reload now, or keep reporting drift.
194
+ if (options.write) {
195
+ rewrote = true;
196
+ reports.push({ ...base, state: 'refreshed', running, ...(from ? { from } : {}),
197
+ detail: 'definition on disk was not loaded yet; reloaded' });
198
+ }
199
+ else
200
+ reports.push({ ...base, state: 'reload-failed', running, ...(from ? { from } : {}),
201
+ detail: 'systemd has not loaded the definition on disk; run systemctl --user daemon-reload' });
202
+ continue;
203
+ }
204
+ reports.push({ ...base, state: 'current', running, ...(from ? { from } : {}) });
205
+ continue;
206
+ }
207
+ if (!target) {
208
+ reports.push({ ...base, state: 'stale', running, from,
209
+ detail: 'the invoking CLI is not a packaged release; nothing to point the definition at' });
210
+ continue;
211
+ }
212
+ if (platform === 'linux' && overriddenByDropIn(definition.path)) {
213
+ reports.push({ ...base, state: 'unrecognized', running, from,
214
+ detail: `a drop-in in ${definition.path}.d sets ExecStart; left untouched` });
215
+ continue;
216
+ }
217
+ if (!options.write) {
218
+ reports.push({ ...base, state: 'stale', running, from, to: target });
219
+ continue;
220
+ }
221
+ // Only bundled tool directories on PATH follow the release; every other
222
+ // setting, including a profile or home stored under a release tree, stays.
223
+ const kept = [];
224
+ const next = generated.rewrite(options.binPath, entry => {
225
+ if (!entry.startsWith(from + '/'))
226
+ return entry;
227
+ const moved = target + entry.slice(from.length);
228
+ if (exists(moved))
229
+ return moved;
230
+ kept.push(entry);
231
+ return entry;
232
+ });
233
+ if (parseGenerated(next, platform, definition)?.script !== options.binPath) {
234
+ reports.push({ ...base, state: 'unrecognized', running, from,
235
+ detail: 'the rewritten definition would not launch the invoking release; left untouched' });
236
+ continue;
237
+ }
238
+ replaceFileAtomically(definition.path, next, statSync(definition.path).mode & 0o777);
239
+ rewrote = true;
240
+ reports.push({ ...base, state: 'refreshed', running, from, to: target,
241
+ ...(kept.length ? { detail: `kept PATH entries absent from the new release: ${kept.join(', ')}` } : {}) });
242
+ }
243
+ if (rewrote && platform === 'linux') {
244
+ const reload = await (options.exec ?? realExec)('systemctl', ['--user', 'daemon-reload']);
245
+ // The file is rewritten, but systemd keeps the loaded unit until a reload,
246
+ // so a failed reload is not a refresh.
247
+ if (reload.code !== 0)
248
+ for (const report of reports)
249
+ if (report.state === 'refreshed') {
250
+ report.state = 'reload-failed';
251
+ report.detail = [report.detail, `systemctl --user daemon-reload failed: ${reload.stderr.trim() || `exit ${reload.code}`}; `
252
+ + 'run it, then restart the service when convenient'].filter(Boolean).join('; ');
253
+ }
254
+ }
255
+ return reports;
256
+ }
257
+ /** One operator line per definition, including the restart a refresh cannot do. */
258
+ export function describeRegistration(report) {
259
+ const what = `${report.service.padEnd(10)} ${report.state}`;
260
+ const pins = report.from ? ` ${report.from}${report.to ? ` -> ${report.to}` : ''}` : '';
261
+ const restart = report.running === 'stale'
262
+ ? `; the running process still uses ${report.state === 'refreshed' ? 'the previous' : 'another'} release until restarted`
263
+ : '';
264
+ return `${what}${pins}${restart}${report.detail ? ` (${report.detail})` : ''}`;
265
+ }
266
+ export const registrationDrift = (reports) => reports.filter(report => report.state === 'stale' || report.state === 'reload-failed' || report.running === 'stale');
@@ -0,0 +1,4 @@
1
+ import { type Exec } from '../exec.js';
2
+ /** OS user services have one namespace. Isolated homes must not hijack it.
3
+ * Injected executors exercise isolated definitions without touching a user manager. */
4
+ export declare function assertNativeFleetScope(exec: Exec): void;
@@ -0,0 +1,10 @@
1
+ import { homedir } from 'node:os';
2
+ import { resolve } from 'node:path';
3
+ import { home } from '../paths.js';
4
+ import { realExec } from '../exec.js';
5
+ /** OS user services have one namespace. Isolated homes must not hijack it.
6
+ * Injected executors exercise isolated definitions without touching a user manager. */
7
+ export function assertNativeFleetScope(exec) {
8
+ if (exec === realExec && resolve(home()) !== resolve(homedir()))
9
+ throw Error('FLEET_SERVICE_HOME_CONFLICT: managed OS supervision requires the OS user home; use OURS_FLEET_SUPERVISOR=none for isolated fleets');
10
+ }
@@ -1,6 +1,6 @@
1
1
  import { type Exec } from '../exec.js';
2
2
  import type { LivenessState, SupervisorBackend } from './types.js';
3
- export declare const UNIT_TEMPLATE = "ours-fleet-agent@.service";
3
+ export declare const UNIT_TEMPLATE = "ours-fleet.service";
4
4
  /**
5
5
  * Actionable hint when systemctl cannot reach the user bus. After the cli.ts
6
6
  * XDG_RUNTIME_DIR fallback this branch stays reachable only when
@@ -8,7 +8,7 @@ export declare const UNIT_TEMPLATE = "ours-fleet-agent@.service";
8
8
  * active — so pointing at linger is the correct first hint. (#9)
9
9
  */
10
10
  export declare const busHint: (stderr: string) => string;
11
- export declare const unitFor: (name: string) => string;
11
+ export declare const unitFor: (_name: string) => string;
12
12
  /**
13
13
  * systemd's own ActiveState vocabulary, classified. `activating` covers
14
14
  * `auto-restart` — the unit is mid-restart, not stopped, so its context stands.