@ours.network/fleet 1.2.0-nightly.8 → 1.2.0-nightly.80

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 (423) hide show
  1. package/README.md +279 -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 +19 -0
  11. package/dist/agent-ours/mcp-endpoint.js +98 -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 +24 -1
  15. package/dist/agent-ours/service.js +97 -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 +34 -0
  19. package/dist/application/conversation-history.js +294 -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 +1 -1
  34. package/dist/application/role-repository.js +30 -7
  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 +97 -31
  49. package/dist/build-info.json +9 -4
  50. package/dist/capabilities.d.ts +9 -1
  51. package/dist/capabilities.js +13 -0
  52. package/dist/cli.js +400 -37
  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 +207 -19
  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 +7 -0
  74. package/dist/fleet-command-audit.js +24 -8
  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 +43 -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 +65 -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 +63 -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-service-retirement.d.ts +11 -0
  180. package/dist/rooms-tasks/task-service-retirement.js +21 -0
  181. package/dist/rooms-tasks/task-state.d.ts +43 -5
  182. package/dist/rooms-tasks/task-state.js +244 -22
  183. package/dist/rooms-tasks/types.d.ts +35 -0
  184. package/dist/rooms-tasks/workspace-artifacts.d.ts +6 -0
  185. package/dist/rooms-tasks/workspace-artifacts.js +93 -0
  186. package/dist/rooms-tasks/workspace.d.ts +35 -0
  187. package/dist/rooms-tasks/workspace.js +325 -0
  188. package/dist/runner.d.ts +33 -4
  189. package/dist/runner.js +410 -62
  190. package/dist/session/acp.d.ts +75 -2
  191. package/dist/session/acp.js +345 -82
  192. package/dist/session/arbiter.d.ts +98 -0
  193. package/dist/session/arbiter.js +272 -3
  194. package/dist/session/codex-app-server.d.ts +45 -1
  195. package/dist/session/codex-app-server.js +225 -32
  196. package/dist/session/control.d.ts +17 -3
  197. package/dist/session/control.js +219 -23
  198. package/dist/session/conversation-normalizer.d.ts +2 -1
  199. package/dist/session/conversation-normalizer.js +1 -1
  200. package/dist/session/conversation-observation.d.ts +25 -0
  201. package/dist/session/conversation-observation.js +100 -0
  202. package/dist/session/conversation-replay.d.ts +50 -0
  203. package/dist/session/conversation-replay.js +203 -0
  204. package/dist/session/conversation-store.d.ts +15 -1
  205. package/dist/session/conversation-store.js +213 -26
  206. package/dist/session/conversation-types.d.ts +57 -4
  207. package/dist/session/prompt-batch.d.ts +79 -0
  208. package/dist/session/prompt-batch.js +120 -0
  209. package/dist/session/task-notice.d.ts +14 -0
  210. package/dist/session/task-notice.js +13 -0
  211. package/dist/session/types.d.ts +42 -1
  212. package/dist/setup-tunnel-preflight.d.ts +34 -0
  213. package/dist/setup-tunnel-preflight.js +62 -0
  214. package/dist/socket-path.d.ts +2 -0
  215. package/dist/socket-path.js +22 -0
  216. package/dist/spawn.d.ts +5 -1
  217. package/dist/spawn.js +40 -4
  218. package/dist/startup-prompt.d.ts +15 -0
  219. package/dist/startup-prompt.js +71 -0
  220. package/dist/startup-readiness.d.ts +32 -0
  221. package/dist/startup-readiness.js +101 -0
  222. package/dist/startup-telemetry.d.ts +16 -0
  223. package/dist/startup-telemetry.js +46 -0
  224. package/dist/subscriptions/cli.d.ts +91 -0
  225. package/dist/subscriptions/cli.js +223 -0
  226. package/dist/subscriptions/launch.d.ts +37 -0
  227. package/dist/subscriptions/launch.js +93 -0
  228. package/dist/subscriptions/login.d.ts +77 -0
  229. package/dist/subscriptions/login.js +215 -0
  230. package/dist/subscriptions/observed.d.ts +7 -0
  231. package/dist/subscriptions/observed.js +44 -0
  232. package/dist/subscriptions/probe.d.ts +10 -0
  233. package/dist/subscriptions/probe.js +73 -0
  234. package/dist/subscriptions/service.d.ts +102 -0
  235. package/dist/subscriptions/service.js +364 -0
  236. package/dist/subscriptions/store.d.ts +71 -0
  237. package/dist/subscriptions/store.js +143 -0
  238. package/dist/supervisor/adoption.d.ts +8 -0
  239. package/dist/supervisor/adoption.js +78 -0
  240. package/dist/supervisor/catalog.d.ts +62 -0
  241. package/dist/supervisor/catalog.js +254 -0
  242. package/dist/supervisor/fleet.d.ts +8 -0
  243. package/dist/supervisor/fleet.js +89 -0
  244. package/dist/supervisor/index.js +3 -6
  245. package/dist/supervisor/launchd.d.ts +1 -1
  246. package/dist/supervisor/launchd.js +26 -13
  247. package/dist/supervisor/legacy.d.ts +35 -0
  248. package/dist/supervisor/legacy.js +524 -0
  249. package/dist/supervisor/manager.d.ts +15 -0
  250. package/dist/supervisor/manager.js +270 -0
  251. package/dist/supervisor/registration-refresh.d.ts +45 -0
  252. package/dist/supervisor/registration-refresh.js +266 -0
  253. package/dist/supervisor/scope.d.ts +4 -0
  254. package/dist/supervisor/scope.js +10 -0
  255. package/dist/supervisor/systemd.d.ts +2 -2
  256. package/dist/supervisor/systemd.js +19 -33
  257. package/dist/task-supervision.d.ts +9 -0
  258. package/dist/task-supervision.js +145 -0
  259. package/dist/temp-idle.d.ts +17 -0
  260. package/dist/temp-idle.js +42 -0
  261. package/dist/temp-lifecycle.d.ts +23 -8
  262. package/dist/temp-lifecycle.js +123 -66
  263. package/dist/temp-supervisor-recovery.d.ts +1 -1
  264. package/dist/temp-supervisor-recovery.js +2 -2
  265. package/dist/tunnel-setup-completion.d.ts +7 -0
  266. package/dist/tunnel-setup-completion.js +13 -0
  267. package/dist/tunnel-setup-input.d.ts +19 -0
  268. package/dist/tunnel-setup-input.js +130 -0
  269. package/dist/watchdog/query.d.ts +3 -1
  270. package/dist/watchdog/query.js +2 -1
  271. package/dist/web/agent-attachments.d.ts +29 -0
  272. package/dist/web/agent-attachments.js +162 -0
  273. package/dist/web/auth.d.ts +65 -2
  274. package/dist/web/auth.js +201 -11
  275. package/dist/web/available-port.d.ts +2 -0
  276. package/dist/web/available-port.js +23 -0
  277. package/dist/web/control.d.ts +3 -3
  278. package/dist/web/control.js +10 -8
  279. package/dist/web/conversation-stream-codec.d.ts +23 -0
  280. package/dist/web/conversation-stream-codec.js +110 -0
  281. package/dist/web/conversation-stream.d.ts +26 -0
  282. package/dist/web/conversation-stream.js +239 -0
  283. package/dist/web/events.d.ts +2 -0
  284. package/dist/web/events.js +9 -0
  285. package/dist/web/fleet-config-service.d.ts +7 -1
  286. package/dist/web/fleet-config-service.js +61 -16
  287. package/dist/web/prefix-gateway.d.ts +32 -0
  288. package/dist/web/prefix-gateway.js +248 -0
  289. package/dist/web/resource-views.d.ts +135 -0
  290. package/dist/web/resource-views.js +486 -0
  291. package/dist/web/runtime.d.ts +3 -1
  292. package/dist/web/runtime.js +132 -34
  293. package/dist/web/server.d.ts +19 -0
  294. package/dist/web/server.js +821 -68
  295. package/dist/web/service.d.ts +11 -2
  296. package/dist/web/service.js +30 -4
  297. package/dist/web/workspace-devices.d.ts +107 -0
  298. package/dist/web/workspace-devices.js +277 -0
  299. package/dist/web/workspace-tunnel.d.ts +1 -0
  300. package/dist/web/workspace-tunnel.js +34 -0
  301. package/dist/workspace-enrollment.d.ts +83 -0
  302. package/dist/workspace-enrollment.js +605 -0
  303. package/dist/workspace-replacement.d.ts +66 -0
  304. package/dist/workspace-replacement.js +238 -0
  305. package/docs/acp-files.md +146 -0
  306. package/docs/conversation-stream.md +216 -0
  307. package/docs/fleet-migration.md +54 -0
  308. package/docs/live-voice.md +13 -0
  309. package/docs/room-layouts.md +227 -0
  310. package/docs/self-reminders.md +374 -0
  311. package/docs/task-workspaces.md +225 -0
  312. package/docs/validation/managed-cli-permissions.md +421 -0
  313. package/examples/room-layouts/fresh.yaml +27 -0
  314. package/examples/room-layouts/scoped.yaml +21 -0
  315. package/examples/room-layouts/shared.yaml +19 -0
  316. package/package.json +24 -20
  317. package/presets/README.md +78 -0
  318. package/presets/brain-catalog.json +8 -2
  319. package/presets/fleet/agent_templates/Critic.yaml +1 -1
  320. package/presets/fleet/agent_templates/Developer.yaml +1 -1
  321. package/presets/fleet/agent_templates/Engineer.yaml +5 -0
  322. package/presets/fleet/agent_templates/LocalCoordinator.yaml +4 -2
  323. package/presets/fleet/agents/FleetCoordinator.yaml +1 -1
  324. package/presets/fleet/brains/claude-fable-5-1-high.yaml +5 -0
  325. package/presets/fleet/brains/claude-fable-5-1-low.yaml +5 -0
  326. package/presets/fleet/brains/claude-fable-5-1-max.yaml +5 -0
  327. package/presets/fleet/brains/claude-fable-5-1-medium.yaml +5 -0
  328. package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +5 -0
  329. package/presets/fleet/brains/claude-fable-5-high.yaml +1 -1
  330. package/presets/fleet/brains/claude-fable-5-low.yaml +1 -1
  331. package/presets/fleet/brains/claude-fable-5-max.yaml +1 -1
  332. package/presets/fleet/brains/claude-fable-5-medium.yaml +1 -1
  333. package/presets/fleet/brains/claude-fable-5-xhigh.yaml +1 -1
  334. package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +4 -0
  335. package/presets/fleet/brains/claude-opus-5-5-high.yaml +5 -0
  336. package/presets/fleet/brains/claude-opus-5-5-low.yaml +5 -0
  337. package/presets/fleet/brains/claude-opus-5-5-max.yaml +5 -0
  338. package/presets/fleet/brains/claude-opus-5-5-medium.yaml +5 -0
  339. package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +5 -0
  340. package/presets/fleet/brains/claude-opus-5-high.yaml +1 -1
  341. package/presets/fleet/brains/claude-opus-5-low.yaml +1 -1
  342. package/presets/fleet/brains/claude-opus-5-max.yaml +1 -1
  343. package/presets/fleet/brains/claude-opus-5-medium.yaml +1 -1
  344. package/presets/fleet/brains/claude-opus-5-xhigh.yaml +1 -1
  345. package/presets/fleet/brains/claude-sonnet-5-high.yaml +1 -1
  346. package/presets/fleet/brains/claude-sonnet-5-low.yaml +1 -1
  347. package/presets/fleet/brains/claude-sonnet-5-max.yaml +1 -1
  348. package/presets/fleet/brains/claude-sonnet-5-medium.yaml +1 -1
  349. package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +1 -1
  350. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +1 -1
  351. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +1 -1
  352. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +1 -1
  353. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +1 -1
  354. package/presets/fleet/brains/codex-gpt-5-4-high.yaml +1 -1
  355. package/presets/fleet/brains/codex-gpt-5-4-low.yaml +1 -1
  356. package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +1 -1
  357. package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +1 -1
  358. package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +1 -1
  359. package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +1 -1
  360. package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +1 -1
  361. package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +1 -1
  362. package/presets/fleet/brains/codex-gpt-5-5-high.yaml +1 -1
  363. package/presets/fleet/brains/codex-gpt-5-5-low.yaml +1 -1
  364. package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +1 -1
  365. package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +1 -1
  366. package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +1 -1
  367. package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +1 -1
  368. package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +1 -1
  369. package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +1 -1
  370. package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +1 -1
  371. package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +1 -1
  372. package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +1 -1
  373. package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +1 -1
  374. package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +1 -1
  375. package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +1 -1
  376. package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +1 -1
  377. package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +1 -1
  378. package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +1 -1
  379. package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +1 -1
  380. package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +1 -1
  381. package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +1 -1
  382. package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +1 -1
  383. package/presets/fleet/brains/codex-gpt-6-1-sol-high.yaml +5 -0
  384. package/presets/fleet/brains/codex-gpt-6-1-sol-low.yaml +5 -0
  385. package/presets/fleet/brains/codex-gpt-6-1-sol-max.yaml +5 -0
  386. package/presets/fleet/brains/codex-gpt-6-1-sol-medium.yaml +5 -0
  387. package/presets/fleet/brains/codex-gpt-6-1-sol-ultra.yaml +5 -0
  388. package/presets/fleet/brains/codex-gpt-6-1-sol-xhigh.yaml +5 -0
  389. package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +1 -1
  390. package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +1 -1
  391. package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +1 -1
  392. package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +1 -1
  393. package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +1 -1
  394. package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +1 -1
  395. package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +5 -0
  396. package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +5 -0
  397. package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +5 -0
  398. package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +5 -0
  399. package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +5 -0
  400. package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +5 -0
  401. package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +5 -0
  402. package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +5 -0
  403. package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +5 -0
  404. package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +5 -0
  405. package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +5 -0
  406. package/presets/fleet/roles/Coordinator.yaml +12 -11
  407. package/presets/fleet/roles/Critic.yaml +13 -0
  408. package/presets/fleet/roles/Developer.yaml +13 -0
  409. package/presets/fleet/roles/Engineer.yaml +53 -0
  410. package/presets/fleet/room_layouts/engineering.yaml +25 -0
  411. package/presets/fleet/room_layouts/pair.yaml +24 -0
  412. package/presets/fleet/room_layouts/single.yaml +17 -0
  413. package/presets/fleet/room_layouts/team.yaml +30 -0
  414. package/presets/fleet/room_templates/engineering.yaml +19 -0
  415. package/presets/manifest.json +2 -2
  416. package/dist/web-app/assets/index-59GF-gsZ.css +0 -1
  417. package/dist/web-app/assets/index-C0h3ALvs.js +0 -10
  418. package/dist/web-app/icons/ours-fleet-maskable.svg +0 -4
  419. package/dist/web-app/icons/ours-fleet.svg +0 -4
  420. package/dist/web-app/index.html +0 -17
  421. package/dist/web-app/manifest.webmanifest +0 -15
  422. package/dist/web-app/offline.html +0 -18
  423. package/dist/web-app/sw.js +0 -51
package/dist/cli.js CHANGED
@@ -1,10 +1,21 @@
1
1
  #!/usr/bin/env node
2
+ import { beginWorkspaceReplacement, abandonWorkspaceReplacement, discardRetiredWorkspaceReplacement, clearWorkspaceReplacement, confirmWorkspaceReplacement, finishWorkspaceReplacement, readWorkspaceBinding, readWorkspaceReplacement, requiresWorkspaceReplacement, refreshWorkspaceReplacement } from './workspace-replacement.js';
3
+ import { completeTunnelSetup } from './tunnel-setup-completion.js';
4
+ import { readTunnelSetupFile, readTunnelSetupStdin, readTunnelSetupArgument, redeemTunnelSetup, inspectTunnelSetup, verifyTunnelSetupTarget } from './tunnel-setup-input.js';
5
+ import { ensureMinimalSetup } from './minimal-setup.js';
6
+ import { confirmWorkspaceTarget, checkWorkspaceConnector } from './workspace-enrollment.js';
7
+ import { clearPendingTunnelSetup, readPendingTunnelSetup, requiredPortAfterExpiry, savePendingTunnelSetup, setupTunnelPreflight } from './setup-tunnel-preflight.js';
8
+ import { availableWebPort } from './web/available-port.js';
9
+ import QRCode from 'qrcode';
10
+ import { readWorkspacePayload, enrollWorkspace, checkWorkspaceConfiguration } from './workspace-enrollment.js';
11
+ import { waitForRoleDaemon } from './startup-readiness.js';
2
12
  import { SupervisorOursTools } from './application/supervisor-ours-tools.js';
3
13
  import { runTempSupervisor, TEMP_RECYCLE_EXIT } from './temp-supervisor-recovery.js';
4
14
  import { spawn as spawnChild } from 'node:child_process';
5
15
  import { randomUUID } from 'node:crypto';
6
- import { existsSync, mkdirSync, readFileSync, statSync } from 'node:fs';
16
+ import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync, rmSync } from 'node:fs';
7
17
  import { realpathSync } from 'node:fs';
18
+ import { describeRegistration, refreshServiceRegistrations, registrationDrift, } from './supervisor/registration-refresh.js';
8
19
  import { join as joinPath, resolve as resolvePath } from 'node:path';
9
20
  import { createInterface } from 'node:readline';
10
21
  import { Command } from 'commander';
@@ -13,7 +24,7 @@ import { INIT_COMPLETION_GUIDANCE } from './init-guidance.js';
13
24
  import { migrateLegacyStarterPresets, migratePackagedRoleDefaults } from './preset-migration.js';
14
25
  import { executeInitAnswers, executeInitWizard, isInteractiveTerminal, publishSetup, readInitSettings, TerminalPrompter, } from './init-wizard.js';
15
26
  import { analyzeInstalls, buildInfo, buildLabel, discoverInstalls, runningLabel, } from './provenance.js';
16
- import { agentDir, agentsRoot, tmpRoot, logsRoot, deriveXdgRuntimeDir, defaultConfigPath } from './paths.js';
27
+ import { agentDir, agentsRoot, tmpRoot, logsRoot, deriveXdgRuntimeDir, defaultConfigPath, stateRoot } from './paths.js';
17
28
  import { findRole, loadConfig, ROLE_NAME_RE } from './config.js';
18
29
  import { formatDuration } from './duration.js';
19
30
  import { redactSensitive, resolvedPlan, resolvedRolePlan } from './resolved-plan.js';
@@ -38,7 +49,7 @@ import { controlRequest, controlSocketPath, followControl, livenessNote, } from
38
49
  import { SessionControlError } from './session/types.js';
39
50
  import { readScheduledLoops, storedLoopHealth } from './loops/state.js';
40
51
  import { startWebConsole } from './web/runtime.js';
41
- import { requestWebControl } from './web/control.js';
52
+ import { requestWebControl, webControlPath } from './web/control.js';
42
53
  import { WebServiceManager } from './web/service.js';
43
54
  import { WebAccessStore, passwordAccess, validatePublicOrigin } from './web/access.js';
44
55
  import { FLEET_PROXY_CALLER_ENV, FLEET_PROXY_STATE_DIR_ENV, } from './fleet-proxy.js';
@@ -46,6 +57,9 @@ import { beginFleetAuditCollection, consumeFleetAuditCollection, FleetCliExit, s
46
57
  import './harness/claude-code.js'; // registers the claude-code adapter
47
58
  import './harness/codex.js'; // registers the codex adapter
48
59
  import './harness/hermes.js';
60
+ import { registerLayoutCommands } from './rooms-tasks/layout-cli.js';
61
+ import { MANAGED_CONFIGURATION_FLAG, assertPinnedConfiguration, enableManagedCli, managedCliReport, parseManagedEntry, } from './managed-cli.js';
62
+ import { MANAGED_CLI_WORKFLOW_IDS } from './config.js';
49
63
  import { registerTemplateCommands, registerTaskCommands, registerRoomCommands } from './rooms-tasks/cli.js';
50
64
  import { RoleCreationService } from './application/role-creation-service.js';
51
65
  import { RoleRemovalService } from './application/role-removal-service.js';
@@ -89,6 +103,22 @@ const program = new Command()
89
103
  .exitOverride()
90
104
  .version(VERSION);
91
105
  const cOpt = (cmd) => cmd.option('-c, --configuration <file>', 'manifest (default: ~/fleet.yaml; documents under ~/fleet/)');
106
+ /**
107
+ * Set only for the pinned managed entry form (see src/managed-cli.ts). Checked
108
+ * against the PARSED option, so every spelling of a later `-c` is covered.
109
+ */
110
+ let pinnedConfiguration;
111
+ program.hook('preAction', (_program, action) => {
112
+ if (pinnedConfiguration === undefined)
113
+ return;
114
+ try {
115
+ assertPinnedConfiguration(pinnedConfiguration, action.opts().configuration);
116
+ }
117
+ catch (error) {
118
+ console.error(`error: ${error.message}`);
119
+ throw new FleetCliExit(1, 'validation', 'not_started');
120
+ }
121
+ });
92
122
  const collect = (value, previous) => [...previous, value];
93
123
  program.command('docs')
94
124
  .alias('man')
@@ -588,7 +618,7 @@ cOpt(program.command('status <name>').description('declared persistent Agent uni
588
618
  + `${ledger.lastTermination.runStartedAt ?? 'unknown'}) died without an orderly exit; `
589
619
  + `observed ${ledger.lastTermination.observedAt}`
590
620
  + `\n abrupt terminations recorded: ${ledger.abruptTerminations ?? 1}`
591
- + `\n cause (signal / OOM-kill): journalctl --user -u ours-fleet-agent@${name}.service`);
621
+ + `\n inspect member output: ours-fleet logs ${name}`);
592
622
  const modelStatus = joinPath(agentDir(name), '.model-status');
593
623
  if (existsSync(modelStatus)) {
594
624
  try {
@@ -1289,6 +1319,78 @@ cOpt(program.command('spawn [name]').description('spawn a new agent (permanent b
1289
1319
  die(e);
1290
1320
  }
1291
1321
  });
1322
+ function renderManagedCliReport(report) {
1323
+ const lines = [
1324
+ `Managed CLI setup for ${report.configuration}`,
1325
+ ` node ${report.node}`, ` cli ${report.cli}`,
1326
+ ` platform ${report.platform.os}${report.platform.declared ? '' : ' (not a declared platform)'}`,
1327
+ ];
1328
+ for (const role of report.roles) {
1329
+ if (role.setup.state === 'not-declared')
1330
+ continue;
1331
+ lines.push('', `${role.role} (${role.harness}/${role.session}) — ${role.workflows.join(', ')}`);
1332
+ lines.push(` setup (static): ${role.setup.state}${role.setup.action ? ` [${role.setup.action}]` : ''} — ${role.setup.detail}`);
1333
+ if (role.setup.artifact)
1334
+ lines.push(` ${role.setup.artifact}`);
1335
+ lines.push(` running session: ${role.session_policy}`);
1336
+ lines.push(` observed (audit ledger): ${role.observed.summary}`);
1337
+ if (role.commandPrefix)
1338
+ lines.push(` invoke as: ${role.commandPrefix} <command>`);
1339
+ for (const line of role.scope)
1340
+ lines.push(` scope: ${line}`);
1341
+ for (const line of role.warnings)
1342
+ lines.push(` note: ${line}`);
1343
+ }
1344
+ if (!report.roles.some(role => role.setup.state !== 'not-declared'))
1345
+ lines.push('', 'No agent declares managed_cli. Nothing was generated and no permission changed.', 'Enable it for one agent with: ours-fleet managed-cli setup --enable <Agent>');
1346
+ for (const item of report.removed)
1347
+ lines.push('', `${item.action}: ${item.path} — ${item.detail ?? ''}`);
1348
+ lines.push('', ...report.disclosure.map(line => `* ${line}`));
1349
+ return lines.join('\n');
1350
+ }
1351
+ const managedCliCommand = program.command('managed-cli')
1352
+ .description('prepare and inspect native harness policy for Fleet commands run from a command sandbox');
1353
+ for (const [name, write, description] of [
1354
+ ['setup', true, 'generate the policy for agents declaring managed_cli; starts no session and no model'],
1355
+ ['status', false, 'compare generated policy and observed supervisor access without changing anything'],
1356
+ ]) {
1357
+ const command = cOpt(managedCliCommand.command(name).description(description)).option('--json', 'JSON output');
1358
+ if (write)
1359
+ command.option('--enable <agent>', 'first add managed_cli: [task-workflow] to this Agent file (repeatable); its permissions are not changed', (value, all) => [...all, value], [])
1360
+ .option('--workflow <id>', `workflow --enable adds (${MANAGED_CLI_WORKFLOW_IDS.join(', ')})`, 'task-workflow');
1361
+ command.action((opts) => {
1362
+ try {
1363
+ const enabled = [];
1364
+ if (opts.enable?.length) {
1365
+ if (!MANAGED_CLI_WORKFLOW_IDS.includes(opts.workflow ?? ''))
1366
+ throw new Error(`unknown workflow '${opts.workflow}'; supported: ${MANAGED_CLI_WORKFLOW_IDS.join(', ')}`);
1367
+ // Validate every name before the first file is touched.
1368
+ const cfg = loadConfig(opts.configuration);
1369
+ for (const agent of opts.enable)
1370
+ findRole(cfg, agent);
1371
+ for (const agent of opts.enable)
1372
+ enabled.push({ agent, ...enableManagedCli(opts.configuration, agent, opts.workflow) });
1373
+ }
1374
+ const report = managedCliReport(loadConfig(opts.configuration), opts.configuration, { write });
1375
+ if (opts.json) {
1376
+ console.log(JSON.stringify({ ...report, enabled }, null, 2));
1377
+ }
1378
+ else {
1379
+ for (const item of enabled)
1380
+ console.log(`${item.changed ? 'Enabled' : 'Already enabled'} ${opts.workflow} for ${item.agent} in ${item.file}`);
1381
+ console.log(renderManagedCliReport(report));
1382
+ }
1383
+ const failing = report.roles.filter(role => ['unsupported', 'conflict', 'stale', 'missing'].includes(role.setup.state));
1384
+ if (failing.length)
1385
+ throw new FleetCliExit(1);
1386
+ }
1387
+ catch (error) {
1388
+ if (error instanceof FleetCliExit)
1389
+ throw error;
1390
+ die(error);
1391
+ }
1392
+ });
1393
+ }
1292
1394
  cOpt(program.command('doctor').description('prerequisite report'))
1293
1395
  .option('--harness <id>', 'check one harness explicitly')
1294
1396
  .option('--yaml-mode <mode>', 'non-plain YAML policy: compat|strict', 'compat')
@@ -1299,12 +1401,41 @@ cOpt(program.command('doctor').description('prerequisite report'))
1299
1401
  harness: opts.harness,
1300
1402
  configPath: opts.configuration,
1301
1403
  yamlMode: opts.yamlMode,
1404
+ serviceRegistration: { binPath },
1302
1405
  });
1303
1406
  for (const c of rep.checks)
1304
1407
  console.log(`${c.ok ? 'ok ' : 'MISS'} ${c.name.padEnd(22)} ${c.detail}`);
1305
1408
  if (!rep.ok)
1306
1409
  throw new FleetCliExit(1);
1307
1410
  });
1411
+ const serviceCommand = program.command('service')
1412
+ .description('installed Fleet service definitions (supervisor, web, watchdogs)');
1413
+ const printRegistrations = (reports, json) => {
1414
+ if (json)
1415
+ console.log(JSON.stringify({ binPath, services: reports }, null, 2));
1416
+ else
1417
+ for (const report of reports)
1418
+ console.log(describeRegistration(report));
1419
+ };
1420
+ serviceCommand.command('check')
1421
+ .description('report whether installed definitions and running services use this release; changes nothing')
1422
+ .option('--json', 'machine-readable report')
1423
+ .action(async (opts) => {
1424
+ const reports = await refreshServiceRegistrations({ binPath, write: false });
1425
+ printRegistrations(reports, opts.json);
1426
+ if (registrationDrift(reports).length)
1427
+ throw new FleetCliExit(1);
1428
+ });
1429
+ serviceCommand.command('refresh')
1430
+ .description('point installed definitions at this release; never creates, enables, starts, stops or restarts a service')
1431
+ .option('--json', 'machine-readable report')
1432
+ .action(async (opts) => {
1433
+ const reports = await refreshServiceRegistrations({ binPath, write: true });
1434
+ printRegistrations(reports, opts.json);
1435
+ if (!opts.json && reports.some(report => report.running === 'stale'))
1436
+ console.log('A running service still uses another release. Restart it when convenient, '
1437
+ + 'e.g. systemctl --user restart ours-fleet.service; agents resume through their supervisor.');
1438
+ });
1308
1439
  cOpt(program.command('init').description('add missing Fleet defaults while preserving existing configuration')
1309
1440
  .option('--settings <file>', 'validated InitAnswers JSON; runs without interactive questions'))
1310
1441
  .action(async (opts) => {
@@ -1386,7 +1517,7 @@ cOpt(program.command('migrate-role-defaults')
1386
1517
  die(error);
1387
1518
  }
1388
1519
  });
1389
- const webCommand = cOpt(program.command('web').description('start or open the secure localhost fleet web console'))
1520
+ const webCommand = cOpt(program.command('web').description('start the Fleet HTTP service used by the App'))
1390
1521
  .enablePositionalOptions()
1391
1522
  .option('--port <port>', 'loopback service port (default: 49271)', value => {
1392
1523
  const port = Number(value);
@@ -1394,7 +1525,7 @@ const webCommand = cOpt(program.command('web').description('start or open the se
1394
1525
  throw new Error('invalid port');
1395
1526
  return port;
1396
1527
  })
1397
- .option('--no-open', 'do not open a browser automatically')
1528
+ .option('--no-open', 'accepted for existing service units; Fleet opens no browser')
1398
1529
  .option('--bind <address>', 'explicit listen address (default: 127.0.0.1)')
1399
1530
  .option('--public-origin <url>', 'browser origin served by an explicit reverse proxy')
1400
1531
  .option('--password-file <path>', 'configure password protection from an owner-readable file')
@@ -1411,12 +1542,7 @@ const webCommand = cOpt(program.command('web').description('start or open the se
1411
1542
  if (accessNotice)
1412
1543
  process.stdout.write(accessNotice + '\n');
1413
1544
  await manager.start();
1414
- if (opts.open !== false) {
1415
- await requestWebControlWhenReady('open');
1416
- process.stdout.write('Control-panel authentication opened in the browser.\n');
1417
- }
1418
- else
1419
- process.stdout.write('Web service started; run `ours-fleet web open` to pair a browser.\n');
1545
+ process.stdout.write('Fleet service started.\n');
1420
1546
  }
1421
1547
  catch (e) {
1422
1548
  die(e);
@@ -1429,7 +1555,7 @@ const webPort = (command) => cOpt(command)
1429
1555
  throw new Error('invalid port');
1430
1556
  return port;
1431
1557
  });
1432
- const webServe = cOpt(webCommand.command('serve').description('run the web console in the foreground'))
1558
+ const webServe = cOpt(webCommand.command('serve').description('run the Fleet HTTP service in the foreground'))
1433
1559
  .option('--port <port>', 'loopback port (default: 49271; 0 chooses a free port)', value => {
1434
1560
  const port = Number(value);
1435
1561
  if (!Number.isInteger(port) || port < 0 || port > 65_535)
@@ -1437,7 +1563,7 @@ const webServe = cOpt(webCommand.command('serve').description('run the web conso
1437
1563
  return port;
1438
1564
  });
1439
1565
  webServe
1440
- .option('--no-open', 'do not open a browser automatically')
1566
+ .option('--no-open', 'accepted for existing service units; Fleet opens no browser')
1441
1567
  .option('--bind <address>', 'explicit listen address (default: 127.0.0.1)')
1442
1568
  .option('--public-origin <url>', 'browser origin served by an explicit reverse proxy')
1443
1569
  .option('--password-file <path>', 'configure password protection from an owner-readable file')
@@ -1448,15 +1574,12 @@ webServe
1448
1574
  const accessNotice = configureWebAccess(opts);
1449
1575
  const consoleServer = await startWebConsole({
1450
1576
  configPath: opts.configuration, port: opts.port,
1451
- open: opts.open !== false, binPath, bind: opts.bind, publicOrigin: opts.publicOrigin,
1577
+ binPath, bind: opts.bind, publicOrigin: opts.publicOrigin,
1452
1578
  log: line => process.stderr.write(line + '\n'),
1453
1579
  });
1454
1580
  if (accessNotice)
1455
1581
  process.stdout.write(accessNotice + '\n');
1456
1582
  process.stdout.write(`ours-fleet web listening on ${consoleServer.address}\n`);
1457
- process.stdout.write(opts.open !== false
1458
- ? 'Control-panel authentication opened in the browser.\n'
1459
- : 'Run `ours-fleet web open` to authenticate a browser.\n');
1460
1583
  const shutdown = async () => { await consoleServer.close(); process.exit(0); };
1461
1584
  process.once('SIGINT', () => { void shutdown(); });
1462
1585
  process.once('SIGTERM', () => { void shutdown(); });
@@ -1515,11 +1638,209 @@ webCommand.command('uninstall').description('stop and uninstall the owner web se
1515
1638
  die(e);
1516
1639
  }
1517
1640
  });
1518
- webCommand.command('open').description('securely open or re-pair a browser with the running console')
1641
+ async function workspaceEnrollmentCommand(opts, setupTunnel = false, argument) {
1642
+ try {
1643
+ if (opts.discardRetiredReplacement) {
1644
+ if (!setupTunnel || argument !== undefined || opts.file || opts.stdin || opts.check || opts.resume || opts.migrateAppOrigin || opts.replaceRegistration || opts.configuration || opts.abandonReplacement)
1645
+ throw Error('--discard-retired-replacement takes no payload or other setup options');
1646
+ discardRetiredWorkspaceReplacement();
1647
+ process.stdout.write('Retired replacement recovery discarded. This host has no working registration: the old service was stopped and linked devices revoked. After removing the failed successor in the App, use a fresh command to complete a new registration. Installation data and the old local binding are preserved.\n');
1648
+ return;
1649
+ }
1650
+ if (opts.abandonReplacement) {
1651
+ if (!setupTunnel || argument !== undefined || opts.file || opts.stdin || opts.check || opts.resume || opts.migrateAppOrigin || opts.replaceRegistration || opts.configuration)
1652
+ throw Error('--abandon-replacement takes no payload or other setup options');
1653
+ await abandonWorkspaceReplacement();
1654
+ process.stdout.write('Unsent or rejected replacement abandoned; existing local setup preserved. Obtain a fresh App command for another setup.\n');
1655
+ return;
1656
+ }
1657
+ if (setupTunnel && opts.check && opts.resume)
1658
+ throw Error('Use either --check or --resume');
1659
+ if (setupTunnel && (opts.check || opts.resume) && (argument !== undefined || opts.file || opts.stdin || opts.migrateAppOrigin || opts.replaceRegistration))
1660
+ throw Error('--check and --resume take no payload or migration flag');
1661
+ if (setupTunnel && opts.check) {
1662
+ await setupTunnelPreflight();
1663
+ await checkWorkspaceConfiguration(opts.configuration ?? defaultConfigPath());
1664
+ process.stdout.write('Tunnel setup prerequisites are available.\n');
1665
+ return;
1666
+ }
1667
+ let pending;
1668
+ if (setupTunnel && opts.resume) {
1669
+ const replacement = readWorkspaceReplacement();
1670
+ if (replacement) {
1671
+ if (opts.configuration && resolvePath(opts.configuration) !== replacement.configuration)
1672
+ throw Error('Resume replacement with its original configuration');
1673
+ await setupTunnelPreflight();
1674
+ const result = await finishWorkspaceReplacement(replacement);
1675
+ pending = { appOrigin: replacement.payload.appOrigin, origin: result.origin, hostWorkspaceId: result.hostWorkspaceId, rootCid: result.rootCid, challenge: replacement.payload.challenge, configuration: replacement.configuration };
1676
+ savePendingTunnelSetup(pending);
1677
+ clearWorkspaceReplacement();
1678
+ }
1679
+ else {
1680
+ pending = readPendingTunnelSetup();
1681
+ if (opts.configuration && resolvePath(opts.configuration) !== pending.configuration)
1682
+ throw Error(`This setup started with configuration ${pending.configuration}; resume it without --configuration`);
1683
+ await setupTunnelPreflight();
1684
+ }
1685
+ }
1686
+ else {
1687
+ if (argument === undefined && !opts.file && !opts.stdin)
1688
+ throw Error('setup-tunnel requires an App grant argument, --file <path>, or --stdin (or --resume to finish an interrupted setup)');
1689
+ if (Number(argument !== undefined) + Number(!!opts.file) + Number(!!opts.stdin) > 1)
1690
+ throw Error('Use one payload argument, --file, or --stdin');
1691
+ if (opts.stdin && process.stdin.isTTY)
1692
+ throw Error('--stdin requires a pipe; use an owned private --file instead');
1693
+ const input = setupTunnel ? (argument !== undefined ? readTunnelSetupArgument(argument) : opts.stdin ? await readTunnelSetupStdin() : readTunnelSetupFile(opts.file)) : undefined;
1694
+ const previous = setupTunnel ? readWorkspaceBinding() : undefined;
1695
+ const replacement = setupTunnel ? readWorkspaceReplacement() : undefined;
1696
+ if (replacement && !opts.replaceRegistration)
1697
+ throw Error('An unfinished replacement exists. Run setup-tunnel --resume, use a fresh command for the same target with --replace-registration, or --abandon-replacement when retirement was never sent or definitively rejected. If retirement and cleanup completed but the successor cannot be recovered, remove the successor in the App and use --discard-retired-replacement');
1698
+ if (replacement && resolvePath(opts.configuration ?? defaultConfigPath()) !== replacement.configuration)
1699
+ throw Error('Replacement requires its original configuration');
1700
+ const target = previous && !replacement ? await inspectTunnelSetup(input) : undefined;
1701
+ const different = previous && !replacement && (!target || requiresWorkspaceReplacement(previous, target, opts.migrateAppOrigin));
1702
+ if (different && !(await confirmWorkspaceReplacement(previous, opts.replaceRegistration))) {
1703
+ if (!process.stdin.isTTY)
1704
+ throw Error('Replacement confirmation required; existing setup and grant preserved');
1705
+ return;
1706
+ }
1707
+ if (setupTunnel) {
1708
+ await setupTunnelPreflight();
1709
+ await checkWorkspaceConfiguration(opts.configuration ?? defaultConfigPath());
1710
+ }
1711
+ let payload = setupTunnel ? await redeemTunnelSetup(input) : readWorkspacePayload(opts.file);
1712
+ verifyTunnelSetupTarget(target, payload);
1713
+ const replace = previous && !replacement && requiresWorkspaceReplacement(previous, { workspaceId: payload.challenge.workspaceId, serverCid: payload.serverCid, appOrigin: payload.appOrigin }, opts.migrateAppOrigin);
1714
+ if (replacement) {
1715
+ refreshWorkspaceReplacement(replacement, payload, opts.configuration ?? defaultConfigPath());
1716
+ payload = replacement.payload;
1717
+ }
1718
+ checkWorkspaceConnector(payload);
1719
+ if (setupTunnel)
1720
+ await ensureMinimalSetup(opts.configuration ?? defaultConfigPath());
1721
+ let result;
1722
+ if (replacement) {
1723
+ result = await finishWorkspaceReplacement(replacement);
1724
+ }
1725
+ else if (replace) {
1726
+ const record = await beginWorkspaceReplacement(previous, payload, opts.configuration ?? defaultConfigPath());
1727
+ result = await finishWorkspaceReplacement(record);
1728
+ }
1729
+ else
1730
+ result = await enrollWorkspace(payload, opts.configuration, { migrateAppOrigin: opts.migrateAppOrigin, preserveProfile: setupTunnel || opts.preserveProfile });
1731
+ pending = { appOrigin: payload.appOrigin, origin: result.origin, hostWorkspaceId: result.hostWorkspaceId, rootCid: result.rootCid, challenge: payload.challenge, configuration: resolvePath(opts.configuration ?? defaultConfigPath()) };
1732
+ // Keep both the replacement capability and the proof until the next recovery record is durable.
1733
+ if (setupTunnel) {
1734
+ savePendingTunnelSetup(pending);
1735
+ clearWorkspaceReplacement();
1736
+ }
1737
+ }
1738
+ try {
1739
+ await finishWorkspaceService(pending, opts.resume ? pending.configuration : opts.configuration, setupTunnel);
1740
+ }
1741
+ catch (error) {
1742
+ if (!setupTunnel)
1743
+ throw error;
1744
+ throw Error(`${error instanceof Error ? error.message : String(error)}\nThis host is already bound to the workspace; do not request a new command. Fix the problem above, then run: ours-fleet setup-tunnel --resume`);
1745
+ }
1746
+ if (setupTunnel) {
1747
+ await completeTunnelSetup({ link: () => requestWebControl('link-device'), qr: code => QRCode.toString(code, { type: 'terminal', small: true }), write: text => new Promise((resolve, reject) => {
1748
+ const onError = (error) => reject(error);
1749
+ process.stdout.once('error', onError);
1750
+ process.stdout.write(text, error => { if (error)
1751
+ reject(error);
1752
+ else {
1753
+ process.stdout.removeListener('error', onError);
1754
+ resolve();
1755
+ } });
1756
+ }), clear: clearPendingTunnelSetup });
1757
+ }
1758
+ else {
1759
+ const link = await requestWebControl('link-device');
1760
+ const code = Buffer.from(JSON.stringify(link)).toString('base64url');
1761
+ process.stdout.write('Root proof submitted; account setup is ready only after tunnel health and binding verification.\nPrivate single-use device code:\n');
1762
+ process.stdout.write(await QRCode.toString(code, { type: 'terminal', small: true }));
1763
+ process.stdout.write('\n' + code + '\n');
1764
+ clearPendingTunnelSetup();
1765
+ }
1766
+ }
1767
+ catch (error) {
1768
+ die(error);
1769
+ }
1770
+ }
1771
+ /** Everything after the root proof: supervised web service, its bound port, and the tunnel target. Safe to repeat. */
1772
+ async function finishWorkspaceService(pending, configuration, record) {
1773
+ if (!existsSync(new WebAccessStore().path))
1774
+ configureWebAccess({ pairing: true });
1775
+ // Once the challenge has expired the account can no longer retarget the tunnel. Decide before touching a running service.
1776
+ const expired = pending.challenge.expiresAt <= Date.now();
1777
+ const requiredPort = expired ? requiredPortAfterExpiry(pending) : undefined;
1778
+ const manager = new WebServiceManager(), previous = manager.readMetadata();
1779
+ if (previous)
1780
+ await manager.stop();
1781
+ const selectedPort = await availableWebPort(requiredPort ?? previous?.port ?? 49271);
1782
+ mkdirSync(joinPath(stateRoot(), 'web'), { recursive: true, mode: 0o700 });
1783
+ writeFileSync(joinPath(stateRoot(), 'web', 'port-selection.json'), JSON.stringify({ expiresAt: Date.now() + 60000 }) + '\n', { mode: 0o600 });
1784
+ const notes = await manager.install(binPath, selectedPort, configuration, { bind: '127.0.0.1', publicOrigin: pending.origin });
1785
+ await manager.restart();
1786
+ // Without linger the user service, and with it the tunnel, stops at logout and does not start at boot.
1787
+ for (const note of notes)
1788
+ if (note.startsWith('warning:'))
1789
+ process.stderr.write(`${note}\nUntil then the tunnel stops when you log out of this host.\n`);
1790
+ const waitForControl = async () => {
1791
+ const controlDeadline = Date.now() + 20000;
1792
+ while (!existsSync(webControlPath())) {
1793
+ if (Date.now() >= controlDeadline)
1794
+ throw Error('Fleet backend did not become ready within 20 seconds. Run ours-fleet web status; on Linux inspect journalctl --user -u ours-fleet-web.service -n 25 --no-pager. Resolve the startup error before running ours-fleet link-device');
1795
+ await new Promise(resolve => setTimeout(resolve, 200));
1796
+ }
1797
+ };
1798
+ await waitForControl();
1799
+ const bound = JSON.parse(readFileSync(joinPath(stateRoot(), 'web', 'workspace-port.json'), 'utf8'));
1800
+ if (!Number.isInteger(bound.port) || bound.port < 1 || bound.port > 65535)
1801
+ throw Error('Invalid workspace listener port');
1802
+ if (bound.port !== selectedPort) {
1803
+ await manager.retainBoundPort(binPath, bound.port, configuration, { bind: '127.0.0.1', publicOrigin: pending.origin });
1804
+ await waitForControl();
1805
+ }
1806
+ if (requiredPort === undefined) {
1807
+ // Record the request first: if its answer is lost, a later resume must not assume the default target.
1808
+ if (record) {
1809
+ pending = { ...pending, confirmedPort: undefined, requestedPorts: [...(pending.requestedPorts ?? []), bound.port] };
1810
+ savePendingTunnelSetup(pending);
1811
+ }
1812
+ await confirmWorkspaceTarget(pending, pending.hostWorkspaceId, pending.rootCid, bound.port, error => { process.stderr.write(`The enrollment contact could not be removed from Messenger (${error instanceof Error ? error.message : String(error)}); it can be removed there by hand.\n`); });
1813
+ if (record)
1814
+ savePendingTunnelSetup({ ...pending, confirmedPort: bound.port });
1815
+ }
1816
+ else if (bound.port !== requiredPort)
1817
+ throw Error(`The setup window has expired and the tunnel points at port ${requiredPort}, but this host could only bind port ${bound.port}. Free port ${requiredPort} on this host`);
1818
+ rmSync(joinPath(stateRoot(), 'web', 'port-selection.json'), { force: true });
1819
+ }
1820
+ cOpt(program.command('workspace-enroll').description('enroll this host using a private account setup file'))
1821
+ .requiredOption('--file <path>', 'owned private payload file')
1822
+ .option('--preserve-profile', 'retain the existing human profile without creating or rewriting it')
1823
+ .option('--migrate-app-origin', 'explicitly migrate the same workspace to its new account origin after verified signed proof')
1824
+ .action(async (opts) => workspaceEnrollmentCommand(opts));
1825
+ cOpt(program.command('setup-tunnel').argument('[payload]', 'minimal one-use v2 App grant (appears in shell history and process listings)')
1826
+ .configureOutput({ outputError: (_message, write) => write('Invalid setup-tunnel command; run ours-fleet setup-tunnel --help.\n') }).description('connect an installed host using scoped expiring App credentials; preserves identities and agent configuration'))
1827
+ .option('--file <path>', 'owned private App grant or legacy payload file (chmod 600)')
1828
+ .option('--stdin', 'read the private App grant or legacy payload from a pipe')
1829
+ .option('--check', 'only verify host prerequisites; changes nothing and needs no payload')
1830
+ .option('--resume', 'finish an interrupted replacement or setup; needs no new payload')
1831
+ .option('--discard-retired-replacement', 'forget a failed successor after retirement and local cleanup; remove it in the App first')
1832
+ .option('--abandon-replacement', 'discard an unsent or definitively rejected replacement after checking its private receipt')
1833
+ .option('--replace-registration', 'confirm retirement of the existing account registration when the new one differs')
1834
+ .option('--migrate-app-origin', 'explicitly migrate the same workspace to its new account origin after verified signed proof')
1835
+ .action(async (payload, opts) => workspaceEnrollmentCommand(opts, true, payload));
1836
+ program.command('link-device').description('create a single-use workspace device connection code (expires in five minutes)')
1519
1837
  .action(async () => {
1520
1838
  try {
1521
- await requestWebControl('open');
1522
- process.stdout.write('Control-panel authentication opened in the browser.\n');
1839
+ const link = await requestWebControl('link-device');
1840
+ process.stdout.write('Private connection code; share only with the intended device.\n');
1841
+ const code = Buffer.from(JSON.stringify(link)).toString('base64url');
1842
+ process.stdout.write(await QRCode.toString(code, { type: 'terminal', small: true }));
1843
+ process.stdout.write('\n' + code + '\n');
1523
1844
  }
1524
1845
  catch (e) {
1525
1846
  die(e);
@@ -1535,20 +1856,6 @@ webCommand.command('revoke-all').description('revoke all trusted browsers and ac
1535
1856
  die(e);
1536
1857
  }
1537
1858
  });
1538
- async function requestWebControlWhenReady(command) {
1539
- let last;
1540
- for (let attempt = 0; attempt < 40; attempt++) {
1541
- try {
1542
- await requestWebControl(command, undefined, 500);
1543
- return;
1544
- }
1545
- catch (error) {
1546
- last = error;
1547
- await new Promise(resolve => setTimeout(resolve, 125));
1548
- }
1549
- }
1550
- throw last;
1551
- }
1552
1859
  function configureWebAccess(opts) {
1553
1860
  if (opts.publicOrigin)
1554
1861
  validatePublicOrigin(opts.publicOrigin);
@@ -1576,9 +1883,47 @@ function configureWebAccess(opts) {
1576
1883
  return undefined;
1577
1884
  }
1578
1885
  // ── rooms & tasks ──────────────────────────────────────────────────────────
1886
+ registerLayoutCommands(program, cOpt);
1579
1887
  registerTemplateCommands(program, cOpt);
1580
1888
  registerTaskCommands(program, cOpt);
1581
1889
  registerRoomCommands(program, cOpt);
1890
+ program.command('_wait-daemon <name>', { hidden: true }).description('internal: service startup readiness')
1891
+ .option('-c, --configuration <file>')
1892
+ .action(async (name, opts) => {
1893
+ try {
1894
+ await waitForRoleDaemon(name, opts.configuration);
1895
+ }
1896
+ catch (e) {
1897
+ die(e);
1898
+ }
1899
+ });
1900
+ program.command('_wait-temp-daemon <name>', { hidden: true })
1901
+ .action(async (name) => {
1902
+ try {
1903
+ await waitForRoleDaemon(name, undefined, {}, true);
1904
+ }
1905
+ catch (error) {
1906
+ die(error);
1907
+ }
1908
+ });
1909
+ program.command('_run-fleet', { hidden: true }).action(async () => {
1910
+ try {
1911
+ const { runFleetManager } = await import('./supervisor/manager.js');
1912
+ await runFleetManager(process.argv[1]);
1913
+ }
1914
+ catch (error) {
1915
+ die(error);
1916
+ }
1917
+ });
1918
+ program.command('_run-managed <key> <generation>', { hidden: true }).action(async (key, generation) => {
1919
+ try {
1920
+ const { runManagedMember } = await import('./supervisor/manager.js');
1921
+ await runManagedMember(key, generation);
1922
+ }
1923
+ catch (error) {
1924
+ die(error);
1925
+ }
1926
+ });
1582
1927
  program.command('_run <name>', { hidden: true }).description('internal: supervisor entrypoint')
1583
1928
  .option('-c, --configuration <file>')
1584
1929
  .action(async (name, opts) => {
@@ -1601,9 +1946,10 @@ program.command('_run-temp <name>', { hidden: true }).description('internal: tem
1601
1946
  }
1602
1947
  });
1603
1948
  program.command('_run-temp-worker <name>', { hidden: true })
1604
- .action(async (name) => {
1949
+ .option('--suspend-on-stop')
1950
+ .action(async (name, options) => {
1605
1951
  try {
1606
- await runTemp(name);
1952
+ await runTemp(name, { suspendOnStop: options.suspendOnStop === true });
1607
1953
  }
1608
1954
  catch (error) {
1609
1955
  if (error instanceof SupervisorRecycleRequiredError)
@@ -1660,7 +2006,15 @@ async function parseFleetCli() {
1660
2006
  async function runFleetCli() {
1661
2007
  const stateDir = process.env[FLEET_PROXY_STATE_DIR_ENV];
1662
2008
  const caller = process.env[FLEET_PROXY_CALLER_ENV];
2009
+ const entry = parseManagedEntry(process.argv.slice(2));
1663
2010
  if (!stateDir || !caller) {
2011
+ // The pinned form exists for managed sessions; outside one there is no
2012
+ // supervisor to audit the call, so it is refused rather than run unaudited.
2013
+ if (entry.kind !== 'none') {
2014
+ console.error(`error: ${entry.kind === 'error' ? entry.message
2015
+ : `${MANAGED_CONFIGURATION_FLAG} is only valid inside a Fleet-managed agent session, where the supervisor audits the call`}`);
2016
+ throw new FleetCliExit(1, 'validation', 'not_started');
2017
+ }
1664
2018
  await parseFleetCli();
1665
2019
  return;
1666
2020
  }
@@ -1695,6 +2049,15 @@ async function runFleetCli() {
1695
2049
  const originalExit = process.exit;
1696
2050
  process.exit = ((code) => { throw new FleetCliExit(code ?? 0); });
1697
2051
  try {
2052
+ if (entry.kind === 'error') {
2053
+ console.error(`error: ${entry.message}`);
2054
+ throw new FleetCliExit(1, 'validation', 'not_started');
2055
+ }
2056
+ if (entry.kind === 'pinned') {
2057
+ // The supervisor audited the argv as typed; Commander parses the ordinary form.
2058
+ pinnedConfiguration = entry.configuration;
2059
+ process.argv = [process.argv[0], process.argv[1], ...entry.argv];
2060
+ }
1698
2061
  await parseFleetCli();
1699
2062
  }
1700
2063
  catch (error) {
@@ -1,18 +1,6 @@
1
- export interface ExplicitClientProfile {
2
- readonly endpoint: string;
3
- readonly expectedInstanceId: string;
4
- readonly credentialPath: string;
5
- readonly configPath: string;
6
- readonly serverUrl?: string;
7
- }
8
- export declare class ClientProfileError extends Error {
9
- constructor(message: string);
10
- }
11
- export declare function clientConfigPath(env: NodeJS.ProcessEnv): string;
12
- /**
13
- * Read Fleet's selected host-client profile without opening its credential.
14
- * A legacy daemon config remains legacy only when none of the profile fields is
15
- * present. Explicit and managed selections fail closed on invalid input.
16
- */
17
- export declare function readClientProfile(env: NodeJS.ProcessEnv): ExplicitClientProfile | undefined;
1
+ import { clientProfilePath, readGatewayClientProfile } from '@ours.network/sdk/client';
2
+ export { ClientProfileError } from '@ours.network/sdk/client';
3
+ export type ExplicitClientProfile = ReturnType<typeof readGatewayClientProfile>;
4
+ export declare const clientConfigPath: typeof clientProfilePath;
5
+ export declare const readClientProfile: typeof readGatewayClientProfile;
18
6
  export declare function clientProfileKey(profile: ExplicitClientProfile): string;