@ours.network/fleet 1.2.0-nightly.4 → 1.2.0-nightly.40

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 (310) hide show
  1. package/README.md +165 -53
  2. package/dist/account-origin.d.ts +2 -0
  3. package/dist/account-origin.js +6 -0
  4. package/dist/agent-ours/bridge.js +9 -0
  5. package/dist/agent-ours/correspondence.d.ts +10 -0
  6. package/dist/agent-ours/correspondence.js +1 -0
  7. package/dist/agent-ours/harness.js +8 -0
  8. package/dist/agent-ours/runtime.d.ts +6 -0
  9. package/dist/agent-ours/runtime.js +36 -0
  10. package/dist/agent-ours/service.js +33 -13
  11. package/dist/application/codex-models.d.ts +15 -0
  12. package/dist/application/codex-models.js +56 -0
  13. package/dist/application/conversation-history.d.ts +25 -0
  14. package/dist/application/conversation-history.js +189 -0
  15. package/dist/application/fleet-query-service.d.ts +1 -1
  16. package/dist/application/fleet-query-service.js +2 -2
  17. package/dist/application/legacy-supervisor-identity.d.ts +7 -0
  18. package/dist/application/legacy-supervisor-identity.js +43 -0
  19. package/dist/application/model-catalog.d.ts +1 -1
  20. package/dist/application/model-catalog.js +5 -2
  21. package/dist/application/preset-provenance.d.ts +19 -0
  22. package/dist/application/preset-provenance.js +59 -0
  23. package/dist/application/role-creation-service.js +1 -1
  24. package/dist/application/room-layout-definitions.d.ts +45 -0
  25. package/dist/application/room-layout-definitions.js +130 -0
  26. package/dist/application/session-control.d.ts +18 -0
  27. package/dist/application/session-control.js +54 -3
  28. package/dist/application/supervisor-ours-tools.d.ts +159 -0
  29. package/dist/application/supervisor-ours-tools.js +113 -0
  30. package/dist/application/task-layouts.d.ts +91 -0
  31. package/dist/application/task-layouts.js +249 -0
  32. package/dist/application/task-room-service.d.ts +99 -3
  33. package/dist/application/task-room-service.js +296 -31
  34. package/dist/atomic-file.d.ts +5 -0
  35. package/dist/atomic-file.js +62 -1
  36. package/dist/build-info.json +6 -4
  37. package/dist/capabilities.d.ts +3 -1
  38. package/dist/capabilities.js +4 -0
  39. package/dist/cli.js +186 -34
  40. package/dist/client-profile.d.ts +5 -16
  41. package/dist/client-profile.js +4 -112
  42. package/dist/config.d.ts +13 -0
  43. package/dist/config.js +19 -4
  44. package/dist/creation.js +2 -2
  45. package/dist/daemon-recovery.d.ts +1 -8
  46. package/dist/daemon-recovery.js +8 -113
  47. package/dist/docs.d.ts +3 -3
  48. package/dist/docs.js +44 -22
  49. package/dist/doctor.d.ts +1 -1
  50. package/dist/doctor.js +46 -59
  51. package/dist/erased-resources.d.ts +9 -0
  52. package/dist/erased-resources.js +87 -0
  53. package/dist/fleet-command-audit.d.ts +1 -0
  54. package/dist/fleet-command-audit.js +11 -6
  55. package/dist/harness/claude-code.js +9 -1
  56. package/dist/harness/claude-runtime.d.ts +12 -0
  57. package/dist/harness/claude-runtime.js +43 -0
  58. package/dist/harness/codex-app-server-proxy.d.ts +4 -1
  59. package/dist/harness/codex-app-server-proxy.js +41 -5
  60. package/dist/harness/codex-runtime.d.ts +11 -1
  61. package/dist/harness/codex-runtime.js +31 -4
  62. package/dist/harness/codex.d.ts +3 -2
  63. package/dist/harness/codex.js +8 -5
  64. package/dist/init-wizard.d.ts +2 -0
  65. package/dist/init-wizard.js +38 -19
  66. package/dist/isolation/policy.js +2 -0
  67. package/dist/isolation/types.d.ts +5 -0
  68. package/dist/minimal-setup.d.ts +2 -0
  69. package/dist/minimal-setup.js +44 -0
  70. package/dist/monitor.d.ts +1 -34
  71. package/dist/monitor.js +24 -151
  72. package/dist/notifications/fleet-producer.d.ts +18 -0
  73. package/dist/notifications/fleet-producer.js +129 -0
  74. package/dist/notifications/outbox.d.ts +42 -0
  75. package/dist/notifications/outbox.js +149 -0
  76. package/dist/owner-channel/channel.d.ts +3 -0
  77. package/dist/owner-channel/channel.js +34 -6
  78. package/dist/owner-channel/commands.d.ts +4 -0
  79. package/dist/owner-channel/commands.js +8 -0
  80. package/dist/owner-channel/lifecycle-outbox.js +4 -3
  81. package/dist/owner-channel/ours-client.js +2 -17
  82. package/dist/paths.d.ts +2 -0
  83. package/dist/paths.js +2 -0
  84. package/dist/preset-migration.js +25 -0
  85. package/dist/rooms-tasks/add-member.d.ts +48 -0
  86. package/dist/rooms-tasks/add-member.js +83 -0
  87. package/dist/rooms-tasks/archived-absence.d.ts +8 -0
  88. package/dist/rooms-tasks/archived-absence.js +48 -0
  89. package/dist/rooms-tasks/cli.js +34 -5
  90. package/dist/rooms-tasks/close.d.ts +5 -2
  91. package/dist/rooms-tasks/close.js +208 -28
  92. package/dist/rooms-tasks/config.d.ts +1 -1
  93. package/dist/rooms-tasks/config.js +11 -4
  94. package/dist/rooms-tasks/cowork-adapter.d.ts +6 -4
  95. package/dist/rooms-tasks/cowork-adapter.js +11 -117
  96. package/dist/rooms-tasks/cowork-http.d.ts +3 -0
  97. package/dist/rooms-tasks/cowork-http.js +70 -0
  98. package/dist/rooms-tasks/deletion.d.ts +1 -1
  99. package/dist/rooms-tasks/deletion.js +74 -5
  100. package/dist/rooms-tasks/erasure.d.ts +6 -0
  101. package/dist/rooms-tasks/erasure.js +169 -0
  102. package/dist/rooms-tasks/external-worker.d.ts +1 -1
  103. package/dist/rooms-tasks/external-worker.js +3 -4
  104. package/dist/rooms-tasks/layout-binding-client.d.ts +11 -0
  105. package/dist/rooms-tasks/layout-binding-client.js +56 -0
  106. package/dist/rooms-tasks/layout-binding-grants.d.ts +21 -0
  107. package/dist/rooms-tasks/layout-binding-grants.js +128 -0
  108. package/dist/rooms-tasks/layout-cli.d.ts +4 -0
  109. package/dist/rooms-tasks/layout-cli.js +107 -0
  110. package/dist/rooms-tasks/layout-config.d.ts +11 -0
  111. package/dist/rooms-tasks/layout-config.js +89 -0
  112. package/dist/rooms-tasks/layout-control.d.ts +21 -0
  113. package/dist/rooms-tasks/layout-control.js +52 -0
  114. package/dist/rooms-tasks/layout-service.d.ts +42 -0
  115. package/dist/rooms-tasks/layout-service.js +185 -0
  116. package/dist/rooms-tasks/layout.d.ts +114 -0
  117. package/dist/rooms-tasks/layout.js +250 -0
  118. package/dist/rooms-tasks/live-readiness.d.ts +18 -0
  119. package/dist/rooms-tasks/live-readiness.js +85 -0
  120. package/dist/rooms-tasks/member-startup.d.ts +1 -0
  121. package/dist/rooms-tasks/member-startup.js +2 -0
  122. package/dist/rooms-tasks/provision.d.ts +14 -0
  123. package/dist/rooms-tasks/provision.js +173 -46
  124. package/dist/rooms-tasks/room-state.d.ts +4 -2
  125. package/dist/rooms-tasks/room-state.js +53 -8
  126. package/dist/rooms-tasks/task-state.d.ts +42 -7
  127. package/dist/rooms-tasks/task-state.js +153 -23
  128. package/dist/rooms-tasks/types.d.ts +28 -0
  129. package/dist/rooms-tasks/workspace-artifacts.d.ts +6 -0
  130. package/dist/rooms-tasks/workspace-artifacts.js +93 -0
  131. package/dist/rooms-tasks/workspace.d.ts +26 -0
  132. package/dist/rooms-tasks/workspace.js +195 -0
  133. package/dist/runner.d.ts +19 -3
  134. package/dist/runner.js +89 -29
  135. package/dist/session/acp.d.ts +7 -1
  136. package/dist/session/acp.js +21 -6
  137. package/dist/session/arbiter.d.ts +3 -0
  138. package/dist/session/arbiter.js +20 -2
  139. package/dist/session/codex-app-server.d.ts +3 -0
  140. package/dist/session/codex-app-server.js +26 -3
  141. package/dist/session/control.d.ts +10 -1
  142. package/dist/session/control.js +37 -2
  143. package/dist/session/conversation-normalizer.d.ts +2 -1
  144. package/dist/session/conversation-normalizer.js +1 -1
  145. package/dist/session/conversation-types.d.ts +5 -3
  146. package/dist/session/task-notice.d.ts +14 -0
  147. package/dist/session/task-notice.js +13 -0
  148. package/dist/session/types.d.ts +3 -0
  149. package/dist/setup-tunnel-preflight.d.ts +32 -0
  150. package/dist/setup-tunnel-preflight.js +58 -0
  151. package/dist/socket-path.d.ts +2 -0
  152. package/dist/socket-path.js +22 -0
  153. package/dist/spawn.js +7 -0
  154. package/dist/startup-readiness.d.ts +13 -0
  155. package/dist/startup-readiness.js +38 -0
  156. package/dist/subscriptions/cli.d.ts +91 -0
  157. package/dist/subscriptions/cli.js +223 -0
  158. package/dist/subscriptions/launch.d.ts +37 -0
  159. package/dist/subscriptions/launch.js +93 -0
  160. package/dist/subscriptions/login.d.ts +75 -0
  161. package/dist/subscriptions/login.js +182 -0
  162. package/dist/subscriptions/observed.d.ts +7 -0
  163. package/dist/subscriptions/observed.js +44 -0
  164. package/dist/subscriptions/probe.d.ts +10 -0
  165. package/dist/subscriptions/probe.js +73 -0
  166. package/dist/subscriptions/service.d.ts +102 -0
  167. package/dist/subscriptions/service.js +364 -0
  168. package/dist/subscriptions/store.d.ts +71 -0
  169. package/dist/subscriptions/store.js +140 -0
  170. package/dist/supervisor/launchd.js +2 -1
  171. package/dist/supervisor/systemd.js +9 -23
  172. package/dist/temp-idle.d.ts +17 -0
  173. package/dist/temp-idle.js +42 -0
  174. package/dist/temp-lifecycle.d.ts +6 -1
  175. package/dist/temp-lifecycle.js +29 -13
  176. package/dist/web/agent-attachments.d.ts +29 -0
  177. package/dist/web/agent-attachments.js +162 -0
  178. package/dist/web/auth.d.ts +29 -1
  179. package/dist/web/auth.js +81 -7
  180. package/dist/web/available-port.d.ts +2 -0
  181. package/dist/web/available-port.js +23 -0
  182. package/dist/web/control.d.ts +3 -3
  183. package/dist/web/control.js +10 -8
  184. package/dist/web/fleet-config-service.d.ts +4 -1
  185. package/dist/web/fleet-config-service.js +46 -15
  186. package/dist/web/prefix-gateway.d.ts +29 -0
  187. package/dist/web/prefix-gateway.js +206 -0
  188. package/dist/web/runtime.d.ts +3 -1
  189. package/dist/web/runtime.js +116 -31
  190. package/dist/web/server.d.ts +16 -0
  191. package/dist/web/server.js +501 -53
  192. package/dist/web/service.d.ts +11 -2
  193. package/dist/web/service.js +30 -4
  194. package/dist/web/workspace-devices.d.ts +29 -0
  195. package/dist/web/workspace-devices.js +84 -0
  196. package/dist/web/workspace-tunnel.d.ts +1 -0
  197. package/dist/web/workspace-tunnel.js +32 -0
  198. package/dist/workspace-enrollment.d.ts +53 -0
  199. package/dist/workspace-enrollment.js +370 -0
  200. package/docs/room-layouts.md +227 -0
  201. package/docs/task-workspaces.md +130 -0
  202. package/examples/room-layouts/fresh.yaml +27 -0
  203. package/examples/room-layouts/scoped.yaml +21 -0
  204. package/examples/room-layouts/shared.yaml +19 -0
  205. package/package.json +13 -18
  206. package/presets/README.md +78 -0
  207. package/presets/brain-catalog.json +8 -2
  208. package/presets/fleet/agent_templates/Critic.yaml +1 -1
  209. package/presets/fleet/agent_templates/Developer.yaml +1 -1
  210. package/presets/fleet/agent_templates/Engineer.yaml +5 -0
  211. package/presets/fleet/agent_templates/LocalCoordinator.yaml +1 -1
  212. package/presets/fleet/agents/FleetCoordinator.yaml +1 -1
  213. package/presets/fleet/brains/claude-fable-5-1-high.yaml +5 -0
  214. package/presets/fleet/brains/claude-fable-5-1-low.yaml +5 -0
  215. package/presets/fleet/brains/claude-fable-5-1-max.yaml +5 -0
  216. package/presets/fleet/brains/claude-fable-5-1-medium.yaml +5 -0
  217. package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +5 -0
  218. package/presets/fleet/brains/claude-fable-5-high.yaml +1 -1
  219. package/presets/fleet/brains/claude-fable-5-low.yaml +1 -1
  220. package/presets/fleet/brains/claude-fable-5-max.yaml +1 -1
  221. package/presets/fleet/brains/claude-fable-5-medium.yaml +1 -1
  222. package/presets/fleet/brains/claude-fable-5-xhigh.yaml +1 -1
  223. package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +4 -0
  224. package/presets/fleet/brains/claude-opus-5-5-high.yaml +5 -0
  225. package/presets/fleet/brains/claude-opus-5-5-low.yaml +5 -0
  226. package/presets/fleet/brains/claude-opus-5-5-max.yaml +5 -0
  227. package/presets/fleet/brains/claude-opus-5-5-medium.yaml +5 -0
  228. package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +5 -0
  229. package/presets/fleet/brains/claude-opus-5-high.yaml +1 -1
  230. package/presets/fleet/brains/claude-opus-5-low.yaml +1 -1
  231. package/presets/fleet/brains/claude-opus-5-max.yaml +1 -1
  232. package/presets/fleet/brains/claude-opus-5-medium.yaml +1 -1
  233. package/presets/fleet/brains/claude-opus-5-xhigh.yaml +1 -1
  234. package/presets/fleet/brains/claude-sonnet-5-high.yaml +1 -1
  235. package/presets/fleet/brains/claude-sonnet-5-low.yaml +1 -1
  236. package/presets/fleet/brains/claude-sonnet-5-max.yaml +1 -1
  237. package/presets/fleet/brains/claude-sonnet-5-medium.yaml +1 -1
  238. package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +1 -1
  239. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +1 -1
  240. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +1 -1
  241. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +1 -1
  242. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +1 -1
  243. package/presets/fleet/brains/codex-gpt-5-4-high.yaml +1 -1
  244. package/presets/fleet/brains/codex-gpt-5-4-low.yaml +1 -1
  245. package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +1 -1
  246. package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +1 -1
  247. package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +1 -1
  248. package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +1 -1
  249. package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +1 -1
  250. package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +1 -1
  251. package/presets/fleet/brains/codex-gpt-5-5-high.yaml +1 -1
  252. package/presets/fleet/brains/codex-gpt-5-5-low.yaml +1 -1
  253. package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +1 -1
  254. package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +1 -1
  255. package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +1 -1
  256. package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +1 -1
  257. package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +1 -1
  258. package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +1 -1
  259. package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +1 -1
  260. package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +1 -1
  261. package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +1 -1
  262. package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +1 -1
  263. package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +1 -1
  264. package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +1 -1
  265. package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +1 -1
  266. package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +1 -1
  267. package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +1 -1
  268. package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +1 -1
  269. package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +1 -1
  270. package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +1 -1
  271. package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +1 -1
  272. package/presets/fleet/brains/codex-gpt-6-1-sol-high.yaml +5 -0
  273. package/presets/fleet/brains/codex-gpt-6-1-sol-low.yaml +5 -0
  274. package/presets/fleet/brains/codex-gpt-6-1-sol-max.yaml +5 -0
  275. package/presets/fleet/brains/codex-gpt-6-1-sol-medium.yaml +5 -0
  276. package/presets/fleet/brains/codex-gpt-6-1-sol-ultra.yaml +5 -0
  277. package/presets/fleet/brains/codex-gpt-6-1-sol-xhigh.yaml +5 -0
  278. package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +1 -1
  279. package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +1 -1
  280. package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +1 -1
  281. package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +1 -1
  282. package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +1 -1
  283. package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +1 -1
  284. package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +5 -0
  285. package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +5 -0
  286. package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +5 -0
  287. package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +5 -0
  288. package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +5 -0
  289. package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +5 -0
  290. package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +5 -0
  291. package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +5 -0
  292. package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +5 -0
  293. package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +5 -0
  294. package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +5 -0
  295. package/presets/fleet/roles/Coordinator.yaml +34 -11
  296. package/presets/fleet/roles/Engineer.yaml +40 -0
  297. package/presets/fleet/room_layouts/engineering.yaml +25 -0
  298. package/presets/fleet/room_layouts/pair.yaml +24 -0
  299. package/presets/fleet/room_layouts/single.yaml +17 -0
  300. package/presets/fleet/room_layouts/team.yaml +30 -0
  301. package/presets/fleet/room_templates/engineering.yaml +19 -0
  302. package/presets/manifest.json +2 -2
  303. package/dist/web-app/assets/index-59GF-gsZ.css +0 -1
  304. package/dist/web-app/assets/index-C0h3ALvs.js +0 -10
  305. package/dist/web-app/icons/ours-fleet-maskable.svg +0 -4
  306. package/dist/web-app/icons/ours-fleet.svg +0 -4
  307. package/dist/web-app/index.html +0 -17
  308. package/dist/web-app/manifest.webmanifest +0 -15
  309. package/dist/web-app/offline.html +0 -18
  310. package/dist/web-app/sw.js +0 -51
package/dist/cli.js CHANGED
@@ -1,8 +1,16 @@
1
1
  #!/usr/bin/env node
2
+ import { ensureMinimalSetup } from './minimal-setup.js';
3
+ import { confirmWorkspaceTarget } from './workspace-enrollment.js';
4
+ import { clearPendingTunnelSetup, readPendingTunnelSetup, requiredPortAfterExpiry, savePendingTunnelSetup, setupTunnelPreflight } from './setup-tunnel-preflight.js';
5
+ import { availableWebPort } from './web/available-port.js';
6
+ import QRCode from 'qrcode';
7
+ import { readWorkspacePayload, enrollWorkspace, checkWorkspaceConfiguration } from './workspace-enrollment.js';
8
+ import { waitForRoleDaemon } from './startup-readiness.js';
9
+ import { SupervisorOursTools } from './application/supervisor-ours-tools.js';
2
10
  import { runTempSupervisor, TEMP_RECYCLE_EXIT } from './temp-supervisor-recovery.js';
3
11
  import { spawn as spawnChild } from 'node:child_process';
4
12
  import { randomUUID } from 'node:crypto';
5
- import { existsSync, mkdirSync, readFileSync, statSync } from 'node:fs';
13
+ import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync, rmSync } from 'node:fs';
6
14
  import { realpathSync } from 'node:fs';
7
15
  import { join as joinPath, resolve as resolvePath } from 'node:path';
8
16
  import { createInterface } from 'node:readline';
@@ -12,7 +20,7 @@ import { INIT_COMPLETION_GUIDANCE } from './init-guidance.js';
12
20
  import { migrateLegacyStarterPresets, migratePackagedRoleDefaults } from './preset-migration.js';
13
21
  import { executeInitAnswers, executeInitWizard, isInteractiveTerminal, publishSetup, readInitSettings, TerminalPrompter, } from './init-wizard.js';
14
22
  import { analyzeInstalls, buildInfo, buildLabel, discoverInstalls, runningLabel, } from './provenance.js';
15
- import { agentDir, agentsRoot, tmpRoot, logsRoot, deriveXdgRuntimeDir, defaultConfigPath } from './paths.js';
23
+ import { agentDir, agentsRoot, tmpRoot, logsRoot, deriveXdgRuntimeDir, defaultConfigPath, stateRoot } from './paths.js';
16
24
  import { findRole, loadConfig, ROLE_NAME_RE } from './config.js';
17
25
  import { formatDuration } from './duration.js';
18
26
  import { redactSensitive, resolvedPlan, resolvedRolePlan } from './resolved-plan.js';
@@ -37,7 +45,7 @@ import { controlRequest, controlSocketPath, followControl, livenessNote, } from
37
45
  import { SessionControlError } from './session/types.js';
38
46
  import { readScheduledLoops, storedLoopHealth } from './loops/state.js';
39
47
  import { startWebConsole } from './web/runtime.js';
40
- import { requestWebControl } from './web/control.js';
48
+ import { requestWebControl, webControlPath } from './web/control.js';
41
49
  import { WebServiceManager } from './web/service.js';
42
50
  import { WebAccessStore, passwordAccess, validatePublicOrigin } from './web/access.js';
43
51
  import { FLEET_PROXY_CALLER_ENV, FLEET_PROXY_STATE_DIR_ENV, } from './fleet-proxy.js';
@@ -45,6 +53,7 @@ import { beginFleetAuditCollection, consumeFleetAuditCollection, FleetCliExit, s
45
53
  import './harness/claude-code.js'; // registers the claude-code adapter
46
54
  import './harness/codex.js'; // registers the codex adapter
47
55
  import './harness/hermes.js';
56
+ import { registerLayoutCommands } from './rooms-tasks/layout-cli.js';
48
57
  import { registerTemplateCommands, registerTaskCommands, registerRoomCommands } from './rooms-tasks/cli.js';
49
58
  import { RoleCreationService } from './application/role-creation-service.js';
50
59
  import { RoleRemovalService } from './application/role-removal-service.js';
@@ -496,6 +505,40 @@ program.command('peek <name> [lines]').description('pane snapshot without attach
496
505
  die(controlFailure(name, 'peek', e));
497
506
  }
498
507
  });
508
+ const oursToolsCommand = program.command('ours').description('invoke tools through a named agent supervisor and its fixed identity');
509
+ oursToolsCommand.command('tools <agent>').description('list the same tools and schemas exposed to the agent MCP')
510
+ .action(async (agent) => {
511
+ try {
512
+ console.log(JSON.stringify(await new SupervisorOursTools().list(agent), null, 2));
513
+ }
514
+ catch (error) {
515
+ die(error);
516
+ }
517
+ });
518
+ oursToolsCommand.command('call <agent> <tool>').description('call a supervisor MCP tool without prompting the agent')
519
+ .option('--args-file <path>', 'JSON object arguments from a private file; omitted means {}')
520
+ .action(async (agent, tool, options) => {
521
+ try {
522
+ let args = {};
523
+ if (options.argsFile) {
524
+ try {
525
+ args = JSON.parse(readFileSync(options.argsFile, 'utf8'));
526
+ }
527
+ catch {
528
+ throw new Error('cannot read tool arguments: expected a readable JSON object file');
529
+ }
530
+ }
531
+ const response = await new SupervisorOursTools().call(agent, { tool, arguments: args });
532
+ console.log(JSON.stringify(response, null, 2));
533
+ if ('isError' in response.result && response.result.isError)
534
+ throw new FleetCliExit(1, 'runtime', 'unknown');
535
+ }
536
+ catch (error) {
537
+ if (error instanceof FleetCliExit)
538
+ throw error;
539
+ die(error);
540
+ }
541
+ });
499
542
  program.command('send <name> [text...]').description("type into the agent's console")
500
543
  .action(async (name, text, opts) => {
501
544
  const stateDir = acpStateDir(name);
@@ -1351,7 +1394,7 @@ cOpt(program.command('migrate-role-defaults')
1351
1394
  die(error);
1352
1395
  }
1353
1396
  });
1354
- const webCommand = cOpt(program.command('web').description('start or open the secure localhost fleet web console'))
1397
+ const webCommand = cOpt(program.command('web').description('start the Fleet HTTP service used by the App'))
1355
1398
  .enablePositionalOptions()
1356
1399
  .option('--port <port>', 'loopback service port (default: 49271)', value => {
1357
1400
  const port = Number(value);
@@ -1359,7 +1402,7 @@ const webCommand = cOpt(program.command('web').description('start or open the se
1359
1402
  throw new Error('invalid port');
1360
1403
  return port;
1361
1404
  })
1362
- .option('--no-open', 'do not open a browser automatically')
1405
+ .option('--no-open', 'accepted for existing service units; Fleet opens no browser')
1363
1406
  .option('--bind <address>', 'explicit listen address (default: 127.0.0.1)')
1364
1407
  .option('--public-origin <url>', 'browser origin served by an explicit reverse proxy')
1365
1408
  .option('--password-file <path>', 'configure password protection from an owner-readable file')
@@ -1376,12 +1419,7 @@ const webCommand = cOpt(program.command('web').description('start or open the se
1376
1419
  if (accessNotice)
1377
1420
  process.stdout.write(accessNotice + '\n');
1378
1421
  await manager.start();
1379
- if (opts.open !== false) {
1380
- await requestWebControlWhenReady('open');
1381
- process.stdout.write('Control-panel authentication opened in the browser.\n');
1382
- }
1383
- else
1384
- process.stdout.write('Web service started; run `ours-fleet web open` to pair a browser.\n');
1422
+ process.stdout.write('Fleet service started.\n');
1385
1423
  }
1386
1424
  catch (e) {
1387
1425
  die(e);
@@ -1394,7 +1432,7 @@ const webPort = (command) => cOpt(command)
1394
1432
  throw new Error('invalid port');
1395
1433
  return port;
1396
1434
  });
1397
- const webServe = cOpt(webCommand.command('serve').description('run the web console in the foreground'))
1435
+ const webServe = cOpt(webCommand.command('serve').description('run the Fleet HTTP service in the foreground'))
1398
1436
  .option('--port <port>', 'loopback port (default: 49271; 0 chooses a free port)', value => {
1399
1437
  const port = Number(value);
1400
1438
  if (!Number.isInteger(port) || port < 0 || port > 65_535)
@@ -1402,7 +1440,7 @@ const webServe = cOpt(webCommand.command('serve').description('run the web conso
1402
1440
  return port;
1403
1441
  });
1404
1442
  webServe
1405
- .option('--no-open', 'do not open a browser automatically')
1443
+ .option('--no-open', 'accepted for existing service units; Fleet opens no browser')
1406
1444
  .option('--bind <address>', 'explicit listen address (default: 127.0.0.1)')
1407
1445
  .option('--public-origin <url>', 'browser origin served by an explicit reverse proxy')
1408
1446
  .option('--password-file <path>', 'configure password protection from an owner-readable file')
@@ -1413,15 +1451,12 @@ webServe
1413
1451
  const accessNotice = configureWebAccess(opts);
1414
1452
  const consoleServer = await startWebConsole({
1415
1453
  configPath: opts.configuration, port: opts.port,
1416
- open: opts.open !== false, binPath, bind: opts.bind, publicOrigin: opts.publicOrigin,
1454
+ binPath, bind: opts.bind, publicOrigin: opts.publicOrigin,
1417
1455
  log: line => process.stderr.write(line + '\n'),
1418
1456
  });
1419
1457
  if (accessNotice)
1420
1458
  process.stdout.write(accessNotice + '\n');
1421
1459
  process.stdout.write(`ours-fleet web listening on ${consoleServer.address}\n`);
1422
- process.stdout.write(opts.open !== false
1423
- ? 'Control-panel authentication opened in the browser.\n'
1424
- : 'Run `ours-fleet web open` to authenticate a browser.\n');
1425
1460
  const shutdown = async () => { await consoleServer.close(); process.exit(0); };
1426
1461
  process.once('SIGINT', () => { void shutdown(); });
1427
1462
  process.once('SIGTERM', () => { void shutdown(); });
@@ -1480,11 +1515,129 @@ webCommand.command('uninstall').description('stop and uninstall the owner web se
1480
1515
  die(e);
1481
1516
  }
1482
1517
  });
1483
- webCommand.command('open').description('securely open or re-pair a browser with the running console')
1518
+ async function workspaceEnrollmentCommand(opts, setupTunnel = false) {
1519
+ try {
1520
+ if (setupTunnel && opts.check && opts.resume)
1521
+ throw Error('Use either --check or --resume');
1522
+ if (setupTunnel && (opts.check || opts.resume) && (opts.file || opts.migrateAppOrigin))
1523
+ throw Error('--check and --resume take no payload file or migration flag');
1524
+ if (setupTunnel && opts.check) {
1525
+ await setupTunnelPreflight();
1526
+ await checkWorkspaceConfiguration(opts.configuration ?? defaultConfigPath());
1527
+ process.stdout.write('Tunnel setup prerequisites are available.\n');
1528
+ return;
1529
+ }
1530
+ let pending;
1531
+ if (setupTunnel && opts.resume) {
1532
+ pending = readPendingTunnelSetup();
1533
+ if (opts.configuration && resolvePath(opts.configuration) !== pending.configuration)
1534
+ throw Error(`This setup started with configuration ${pending.configuration}; resume it without --configuration`);
1535
+ await setupTunnelPreflight();
1536
+ }
1537
+ else {
1538
+ if (!opts.file)
1539
+ throw Error('setup-tunnel requires --file <path> (or --resume to finish an interrupted setup)');
1540
+ const payload = readWorkspacePayload(opts.file);
1541
+ if (setupTunnel) {
1542
+ await setupTunnelPreflight();
1543
+ await checkWorkspaceConfiguration(opts.configuration ?? defaultConfigPath());
1544
+ await ensureMinimalSetup(opts.configuration ?? defaultConfigPath());
1545
+ }
1546
+ const result = await enrollWorkspace(payload, opts.configuration, { migrateAppOrigin: opts.migrateAppOrigin, preserveProfile: setupTunnel || opts.preserveProfile });
1547
+ pending = { appOrigin: payload.appOrigin, origin: result.origin, hostWorkspaceId: result.hostWorkspaceId, rootCid: result.rootCid, challenge: payload.challenge, configuration: resolvePath(opts.configuration ?? defaultConfigPath()) };
1548
+ // The proof is one-time: keep what the remaining steps need so they can be finished without a new command.
1549
+ if (setupTunnel)
1550
+ savePendingTunnelSetup(pending);
1551
+ }
1552
+ try {
1553
+ await finishWorkspaceService(pending, opts.resume ? pending.configuration : opts.configuration, setupTunnel);
1554
+ }
1555
+ catch (error) {
1556
+ if (!setupTunnel)
1557
+ throw error;
1558
+ 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`);
1559
+ }
1560
+ clearPendingTunnelSetup();
1561
+ if (setupTunnel) {
1562
+ process.stdout.write('Tunnel setup submitted. Check verified binding and tunnel health in the App, then run ours-fleet link-device and paste its private code. Configure Fleet agents and models in the App after linking.\n');
1563
+ return;
1564
+ }
1565
+ const link = await requestWebControl('link-device');
1566
+ const code = Buffer.from(JSON.stringify(link)).toString('base64url');
1567
+ process.stdout.write('Root proof submitted; account setup is ready only after tunnel health and binding verification.\nPrivate single-use device code:\n');
1568
+ process.stdout.write(await QRCode.toString(code, { type: 'terminal', small: true }));
1569
+ process.stdout.write('\n' + code + '\n');
1570
+ }
1571
+ catch (error) {
1572
+ die(error);
1573
+ }
1574
+ }
1575
+ /** Everything after the root proof: supervised web service, its bound port, and the tunnel target. Safe to repeat. */
1576
+ async function finishWorkspaceService(pending, configuration, record) {
1577
+ if (!existsSync(new WebAccessStore().path))
1578
+ configureWebAccess({ pairing: true });
1579
+ // Once the challenge has expired the account can no longer retarget the tunnel. Decide before touching a running service.
1580
+ const expired = pending.challenge.expiresAt <= Date.now();
1581
+ const requiredPort = expired ? requiredPortAfterExpiry(pending) : undefined;
1582
+ const manager = new WebServiceManager(), previous = manager.readMetadata();
1583
+ if (previous)
1584
+ await manager.stop();
1585
+ const selectedPort = await availableWebPort(requiredPort ?? previous?.port ?? 49271);
1586
+ mkdirSync(joinPath(stateRoot(), 'web'), { recursive: true, mode: 0o700 });
1587
+ writeFileSync(joinPath(stateRoot(), 'web', 'port-selection.json'), JSON.stringify({ expiresAt: Date.now() + 60000 }) + '\n', { mode: 0o600 });
1588
+ const notes = await manager.install(binPath, selectedPort, configuration, { bind: '127.0.0.1', publicOrigin: pending.origin });
1589
+ await manager.restart();
1590
+ // Without linger the user service, and with it the tunnel, stops at logout and does not start at boot.
1591
+ for (const note of notes)
1592
+ if (note.startsWith('warning:'))
1593
+ process.stderr.write(`${note}\nUntil then the tunnel stops when you log out of this host.\n`);
1594
+ const waitForControl = async () => {
1595
+ const controlDeadline = Date.now() + 20000;
1596
+ while (!existsSync(webControlPath())) {
1597
+ if (Date.now() >= controlDeadline)
1598
+ 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');
1599
+ await new Promise(resolve => setTimeout(resolve, 200));
1600
+ }
1601
+ };
1602
+ await waitForControl();
1603
+ const bound = JSON.parse(readFileSync(joinPath(stateRoot(), 'web', 'workspace-port.json'), 'utf8'));
1604
+ if (!Number.isInteger(bound.port) || bound.port < 1 || bound.port > 65535)
1605
+ throw Error('Invalid workspace listener port');
1606
+ if (bound.port !== selectedPort) {
1607
+ await manager.retainBoundPort(binPath, bound.port, configuration, { bind: '127.0.0.1', publicOrigin: pending.origin });
1608
+ await waitForControl();
1609
+ }
1610
+ if (requiredPort === undefined) {
1611
+ // Record the request first: if its answer is lost, a later resume must not assume the default target.
1612
+ if (record) {
1613
+ pending = { ...pending, requestedPorts: [...(pending.requestedPorts ?? []), bound.port] };
1614
+ savePendingTunnelSetup(pending);
1615
+ }
1616
+ 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`); });
1617
+ }
1618
+ else if (bound.port !== requiredPort)
1619
+ 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`);
1620
+ rmSync(joinPath(stateRoot(), 'web', 'port-selection.json'), { force: true });
1621
+ }
1622
+ cOpt(program.command('workspace-enroll').description('enroll this host using a private account setup file'))
1623
+ .requiredOption('--file <path>', 'owned private payload file')
1624
+ .option('--preserve-profile', 'retain the existing human profile without creating or rewriting it')
1625
+ .option('--migrate-app-origin', 'explicitly migrate the same workspace to its new account origin after verified signed proof')
1626
+ .action(async (opts) => workspaceEnrollmentCommand(opts));
1627
+ cOpt(program.command('setup-tunnel').description('connect an installed host using scoped expiring App credentials; preserves identities and agent configuration'))
1628
+ .option('--file <path>', 'owned private App payload file (chmod 600)')
1629
+ .option('--check', 'only verify host prerequisites; changes nothing and needs no payload')
1630
+ .option('--resume', 'finish a setup that stopped after this host was bound; needs no new payload')
1631
+ .option('--migrate-app-origin', 'explicitly migrate the same workspace to its new account origin after verified signed proof')
1632
+ .action(async (opts) => workspaceEnrollmentCommand(opts, true));
1633
+ program.command('link-device').description('create a single-use workspace device connection code (expires in five minutes)')
1484
1634
  .action(async () => {
1485
1635
  try {
1486
- await requestWebControl('open');
1487
- process.stdout.write('Control-panel authentication opened in the browser.\n');
1636
+ const link = await requestWebControl('link-device');
1637
+ process.stdout.write('Private connection code; share only with the intended device.\n');
1638
+ const code = Buffer.from(JSON.stringify(link)).toString('base64url');
1639
+ process.stdout.write(await QRCode.toString(code, { type: 'terminal', small: true }));
1640
+ process.stdout.write('\n' + code + '\n');
1488
1641
  }
1489
1642
  catch (e) {
1490
1643
  die(e);
@@ -1500,20 +1653,6 @@ webCommand.command('revoke-all').description('revoke all trusted browsers and ac
1500
1653
  die(e);
1501
1654
  }
1502
1655
  });
1503
- async function requestWebControlWhenReady(command) {
1504
- let last;
1505
- for (let attempt = 0; attempt < 40; attempt++) {
1506
- try {
1507
- await requestWebControl(command, undefined, 500);
1508
- return;
1509
- }
1510
- catch (error) {
1511
- last = error;
1512
- await new Promise(resolve => setTimeout(resolve, 125));
1513
- }
1514
- }
1515
- throw last;
1516
- }
1517
1656
  function configureWebAccess(opts) {
1518
1657
  if (opts.publicOrigin)
1519
1658
  validatePublicOrigin(opts.publicOrigin);
@@ -1541,9 +1680,20 @@ function configureWebAccess(opts) {
1541
1680
  return undefined;
1542
1681
  }
1543
1682
  // ── rooms & tasks ──────────────────────────────────────────────────────────
1683
+ registerLayoutCommands(program, cOpt);
1544
1684
  registerTemplateCommands(program, cOpt);
1545
1685
  registerTaskCommands(program, cOpt);
1546
1686
  registerRoomCommands(program, cOpt);
1687
+ program.command('_wait-daemon <name>', { hidden: true }).description('internal: service startup readiness')
1688
+ .option('-c, --configuration <file>')
1689
+ .action(async (name, opts) => {
1690
+ try {
1691
+ await waitForRoleDaemon(name, opts.configuration);
1692
+ }
1693
+ catch (e) {
1694
+ die(e);
1695
+ }
1696
+ });
1547
1697
  program.command('_run <name>', { hidden: true }).description('internal: supervisor entrypoint')
1548
1698
  .option('-c, --configuration <file>')
1549
1699
  .action(async (name, opts) => {
@@ -1612,6 +1762,8 @@ async function parseFleetCli() {
1612
1762
  await program.parseAsync(process.argv);
1613
1763
  }
1614
1764
  catch (error) {
1765
+ if (error instanceof FleetCliExit)
1766
+ throw error;
1615
1767
  const commander = error;
1616
1768
  if (commander.exitCode === 0)
1617
1769
  return;
@@ -1,17 +1,6 @@
1
- export interface ExplicitClientProfile {
2
- readonly endpoint: string;
3
- readonly expectedInstanceId: string;
4
- readonly credentialPath: string;
5
- readonly configPath: string;
6
- }
7
- export declare class ClientProfileError extends Error {
8
- constructor(message: string);
9
- }
10
- export declare function clientConfigPath(env: NodeJS.ProcessEnv): string;
11
- /**
12
- * Read Fleet's selected host-client profile without opening its credential.
13
- * A legacy daemon config remains legacy only when none of the profile fields is
14
- * present. Explicit and managed selections fail closed on invalid input.
15
- */
16
- 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;
17
6
  export declare function clientProfileKey(profile: ExplicitClientProfile): string;
@@ -1,115 +1,7 @@
1
- import { lstatSync, readFileSync, statSync } from 'node:fs';
2
- import { homedir } from 'node:os';
3
- import { isAbsolute, join } from 'node:path';
4
- const PROFILE_FIELDS = ['endpoint', 'expectedInstanceId', 'credentialPath'];
5
- const LOWERCASE_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
6
- const LEGACY_CONFIG_FIELDS = ['apiToken', 'stateDir', 'port'];
7
- const LEGACY_ENV_FIELDS = ['OURS_API_TOKEN', 'OURS_STATE_DIR', 'OURS_PORT', 'OURS_DAEMON_ID'];
8
- export class ClientProfileError extends Error {
9
- constructor(message) {
10
- super(message);
11
- this.name = 'ClientProfileError';
12
- }
13
- }
14
- export function clientConfigPath(env) {
15
- if (env.OURS_CONFIG !== undefined)
16
- return env.OURS_CONFIG;
17
- const managedPath = join(env.HOME || homedir(), '.ours-client', 'profile.json');
18
- try {
19
- lstatSync(managedPath);
20
- return managedPath;
21
- }
22
- catch (error) {
23
- if (error.code !== 'ENOENT')
24
- throw invalid(managedPath, 'could not be read');
25
- return join(homedir(), '.ours', 'config.json');
26
- }
27
- }
28
- function invalid(path, detail) {
29
- return new ClientProfileError(`ours client profile ${JSON.stringify(path)} ${detail}`);
30
- }
31
- /**
32
- * Read Fleet's selected host-client profile without opening its credential.
33
- * A legacy daemon config remains legacy only when none of the profile fields is
34
- * present. Explicit and managed selections fail closed on invalid input.
35
- */
36
- export function readClientProfile(env) {
37
- const configPath = clientConfigPath(env);
38
- const managed = configPath === join(env.HOME || homedir(), '.ours-client', 'profile.json');
39
- const named = env.OURS_CONFIG !== undefined || managed;
40
- let text;
41
- try {
42
- text = readFileSync(configPath, 'utf8');
43
- }
44
- catch {
45
- if (named)
46
- throw invalid(configPath, 'could not be read');
47
- return undefined;
48
- }
49
- let parsed;
50
- try {
51
- parsed = JSON.parse(text);
52
- }
53
- catch {
54
- if (named)
55
- throw invalid(configPath, 'is not valid JSON');
56
- return undefined;
57
- }
58
- if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
59
- if (named)
60
- throw invalid(configPath, 'must contain a JSON object');
61
- return undefined;
62
- }
63
- const row = parsed;
64
- if (!PROFILE_FIELDS.some(field => Object.prototype.hasOwnProperty.call(row, field))) {
65
- if (managed)
66
- throw invalid(configPath, 'must contain a complete client profile');
67
- return undefined;
68
- }
69
- let metadata;
70
- try {
71
- metadata = statSync(configPath);
72
- }
73
- catch {
74
- throw invalid(configPath, 'could not be inspected');
75
- }
76
- if (!metadata.isFile())
77
- throw invalid(configPath, 'must be a regular file');
78
- const getuid = process.getuid;
79
- if (getuid && metadata.uid !== getuid())
80
- throw invalid(configPath, 'must be owned by this user');
81
- if ((metadata.mode & 0o077) !== 0)
82
- throw invalid(configPath, 'must not be accessible by group or other users');
83
- for (const field of PROFILE_FIELDS)
84
- if (typeof row[field] !== 'string' || !row[field].trim())
85
- throw invalid(configPath, `has an invalid or missing ${field}`);
86
- for (const field of LEGACY_CONFIG_FIELDS)
87
- if (Object.prototype.hasOwnProperty.call(row, field))
88
- throw invalid(configPath, `cannot combine ${field} with explicit profile selection`);
89
- for (const field of LEGACY_ENV_FIELDS)
90
- if (env[field]?.trim())
91
- throw invalid(configPath, `cannot combine ${field} with explicit profile selection`);
92
- const endpoint = row.endpoint.trim();
93
- let url;
94
- try {
95
- url = new URL(endpoint);
96
- }
97
- catch {
98
- throw invalid(configPath, 'has an invalid endpoint');
99
- }
100
- if ((url.protocol !== 'http:' && url.protocol !== 'https:') || url.username || url.password || url.pathname !== '/'
101
- || url.search || url.hash)
102
- throw invalid(configPath, 'endpoint must be an HTTP or HTTPS origin');
103
- const expectedInstanceId = row.expectedInstanceId.trim();
104
- if (!LOWERCASE_UUID.test(expectedInstanceId))
105
- throw invalid(configPath, 'expectedInstanceId must be a lowercase UUID');
106
- const credentialPath = row.credentialPath.trim();
107
- if (!isAbsolute(credentialPath))
108
- throw invalid(configPath, 'credentialPath must be absolute');
109
- return Object.freeze({
110
- endpoint: url.origin, expectedInstanceId, credentialPath, configPath,
111
- });
112
- }
1
+ import { clientProfilePath, readGatewayClientProfile } from '@ours.network/sdk/client';
2
+ export { ClientProfileError } from '@ours.network/sdk/client';
3
+ export const clientConfigPath = clientProfilePath;
4
+ export const readClientProfile = readGatewayClientProfile;
113
5
  export function clientProfileKey(profile) {
114
6
  return `${profile.endpoint}#${profile.expectedInstanceId}`;
115
7
  }
package/dist/config.d.ts CHANGED
@@ -2,6 +2,7 @@ import { type ConfigDiagnostic, type YamlMode } from './config-yaml.js';
2
2
  import type { IsolationConfig, WrapContext } from './isolation/types.js';
3
3
  import type { ResolvedWatchdog } from './watchdog/config.js';
4
4
  import type { AgentLoopsConfig, ResolvedLoop, ResolvedRoleLoop } from './loops/config.js';
5
+ import { validateRoomsConfig } from './rooms-tasks/config.js';
5
6
  export interface OverseeEntry {
6
7
  agent: string;
7
8
  interval: string;
@@ -178,6 +179,7 @@ export interface AgentTemplateInstance {
178
179
  }
179
180
  /** Internal first-boot payload for a Fleet-provisioned Cowork room member. */
180
181
  export interface RoomMemberStartup {
182
+ workspace?: import('./rooms-tasks/workspace.js').OwnedWorkspace;
181
183
  room_id: string;
182
184
  room_identity_cid: string;
183
185
  identity_name: string;
@@ -298,6 +300,8 @@ export declare function validateBrainValue(value: BarePreset, file: string, poin
298
300
  export declare function validateEffectiveAgentTemplate(definition: AgentTemplateDefinition, id?: string): AgentTemplateDefinition;
299
301
  export declare function assertBareKeys(value: unknown, allowed: string[], label: string): BarePreset;
300
302
  export declare function splitRootFor(base: string): string;
303
+ /** Read only room connection/owner settings for an existing layout. */
304
+ export declare function loadRoomsConfig(configPath?: string): ReturnType<typeof validateRoomsConfig>;
301
305
  /** Load a v2 manifest and bare Agent/Role/Brain documents from its stem directory. */
302
306
  export declare function loadConfig(configPath?: string, options?: {
303
307
  yamlMode?: YamlMode;
@@ -307,6 +311,8 @@ export declare function loadConfig(configPath?: string, options?: {
307
311
  temporary?: boolean;
308
312
  };
309
313
  skipWatchdogs?: boolean;
314
+ /** The one Owner invitation file that tunnel setup writes itself and that may therefore still be absent. */
315
+ deferredOwnerInviteFile?: string;
310
316
  }): FleetConfig;
311
317
  /** Full read-only Agent resolution used by launch dry-runs and room plan validation. */
312
318
  export declare function resolveAgentDefinitionDryRun(configPath: string, id: string, definition: AgentDefinition): ResolvedRole;
@@ -331,10 +337,17 @@ export declare function resolvePermissions(defaults: unknown, role: Partial<Comm
331
337
  * malformed block so a typo fails loudly rather than silently disarming a monitor.
332
338
  * Exported so temp-spawn (which builds a ResolvedRole by hand) resolves identically.
333
339
  */
340
+ /**
341
+ * A monitor without an explicit `interrupt` steers wakes at the next tool
342
+ * boundary. Hermes cannot, so its default stays non-interrupting; sessions
343
+ * without tool-boundary steering fall back to non-cancelling delivery.
344
+ */
345
+ export declare function defaultMonitorInterrupt(harness: string | undefined): MonitorInterrupt;
334
346
  export declare function resolveMonitorConfig(defMonitor: unknown, roleMonitor?: Partial<MonitorConfig>, labels?: {
335
347
  base?: string;
336
348
  file?: string;
337
349
  name?: string;
350
+ harness?: string;
338
351
  }): MonitorConfig;
339
352
  export declare function findRole(cfg: FleetConfig, name: string): ResolvedRole;
340
353
  export {};
package/dist/config.js CHANGED
@@ -575,6 +575,13 @@ function deepSub(v, vars) {
575
575
  return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, deepSub(x, vars)]));
576
576
  return v;
577
577
  }
578
+ /** Read only room connection/owner settings for an existing layout. */
579
+ export function loadRoomsConfig(configPath = defaultConfigPath()) {
580
+ assertTrustedPath(configPath, 'file');
581
+ const doc = parseFleetDocument(configPath, readFileSync(configPath, 'utf8')).value;
582
+ const vars = (doc.vars ?? {});
583
+ return validateRoomsConfig(deepSub(doc.rooms, vars), vars, configPath);
584
+ }
578
585
  /** Load a v2 manifest and bare Agent/Role/Brain documents from its stem directory. */
579
586
  export function loadConfig(configPath, options = {}) {
580
587
  const base = configPath ?? defaultConfigPath();
@@ -649,11 +656,11 @@ export function loadConfig(configPath, options = {}) {
649
656
  if (problems.length)
650
657
  throw new ConfigError(`${file}: role '${name}' ${problems.join('; ')}`);
651
658
  }
652
- const monitor = resolveMonitorConfig(defaults.monitor, r.monitor, { base, file, name });
659
+ const harness = r.harness ?? defaults.harness ?? 'claude-code';
660
+ const monitor = resolveMonitorConfig(defaults.monitor, r.monitor, { base, file, name, harness });
653
661
  const ownerChannel = resolveOwnerChannelConfig(defaults.owner_channel, r.owner_channel, session, file, name);
654
662
  const worklog = resolveWorklogPolicy(defaults.worklog, r.worklog, file, name);
655
663
  const authProxy = resolveAuthProxy(defaults.auth_proxy, r.auth_proxy, file, name);
656
- const harness = r.harness ?? defaults.harness ?? 'claude-code';
657
664
  if (session === 'codex-app-server' && harness !== 'codex')
658
665
  throw new ConfigError(`${file}: role '${name}' session: codex-app-server requires harness: codex`);
659
666
  const defaultHarness = defaults.harness ?? 'claude-code';
@@ -757,7 +764,7 @@ export function loadConfig(configPath, options = {}) {
757
764
  if (doc.rooms !== undefined) {
758
765
  if (rooms)
759
766
  throw new ConfigError(`rooms: defined in multiple files; last: ${file}`);
760
- const validated = validateRoomsConfig(deepSub(doc.rooms, vars), vars, file);
767
+ const validated = validateRoomsConfig(deepSub(doc.rooms, vars), vars, file, options.deferredOwnerInviteFile);
761
768
  ownerInviteFingerprint = validated._invite?.fingerprint;
762
769
  ownerInvite = validated._invite?.value;
763
770
  const { _invite: _, ...clean } = validated;
@@ -1135,6 +1142,14 @@ function resolveStartStaggerMs(raw, base) {
1135
1142
  * malformed block so a typo fails loudly rather than silently disarming a monitor.
1136
1143
  * Exported so temp-spawn (which builds a ResolvedRole by hand) resolves identically.
1137
1144
  */
1145
+ /**
1146
+ * A monitor without an explicit `interrupt` steers wakes at the next tool
1147
+ * boundary. Hermes cannot, so its default stays non-interrupting; sessions
1148
+ * without tool-boundary steering fall back to non-cancelling delivery.
1149
+ */
1150
+ export function defaultMonitorInterrupt(harness) {
1151
+ return harness === 'hermes' ? false : 'after_tool';
1152
+ }
1138
1153
  export function resolveMonitorConfig(defMonitor, roleMonitor, labels = {}) {
1139
1154
  const where = labels.file && labels.name ? `${labels.file}: role '${labels.name}' ` : '';
1140
1155
  if (defMonitor !== undefined && !isPlainObject(defMonitor))
@@ -1169,7 +1184,7 @@ export function resolveMonitorConfig(defMonitor, roleMonitor, labels = {}) {
1169
1184
  wake_sources: merged.wake_sources ?? [...DEFAULT_WAKE_SOURCES],
1170
1185
  batch_ms: merged.batch_ms ?? MONITOR_DEFAULT_BATCH_MS,
1171
1186
  inject: merged.inject ?? 'notification',
1172
- interrupt: merged.interrupt ?? false,
1187
+ interrupt: merged.interrupt ?? defaultMonitorInterrupt(labels.harness),
1173
1188
  ...(merged.stall_recovery !== undefined ? { stall_recovery: merged.stall_recovery } : {}),
1174
1189
  ...(merged.stall_timeout_ms !== undefined ? { stall_timeout_ms: merged.stall_timeout_ms } : {}),
1175
1190
  turn_fail_threshold: merged.turn_fail_threshold ?? MONITOR_DEFAULT_TURN_FAIL_THRESHOLD,
package/dist/creation.js CHANGED
@@ -213,10 +213,10 @@ export async function ensureIdentity(name, profile, provisioner, log = () => { }
213
213
  export function daemonIdentityProvisioner(env = process.env, attachClient = attachOursClient, options = {}) {
214
214
  const connect = (leaseToken) => {
215
215
  const profile = readClientProfile(env);
216
- return attachClient(profile ? {
216
+ return attachClient({
217
217
  endpoint: profile.endpoint, expectedInstanceId: profile.expectedInstanceId,
218
218
  credentialPath: profile.credentialPath, sessionMode: 'external', leaseToken, env: {},
219
- } : { env, leaseToken, clientPid: process.pid });
219
+ });
220
220
  };
221
221
  const provisioner = {
222
222
  async exists(name) {
@@ -34,14 +34,7 @@ interface GenerationProbeDeps {
34
34
  canonicalize?(path: string): string;
35
35
  attachClient?(options: AttachOursClientOptions): Pick<OursClient, 'version' | 'identities' | 'close'> | Promise<Pick<OursClient, 'version' | 'identities' | 'close'>>;
36
36
  }
37
- /**
38
- * Corroborate the loopback daemon's unauthenticated `/info`, its credentialed
39
- * identity-index readiness route, and its local boot-generation record. The
40
- * index enforces auth when the daemon visibility requires it; open visibility
41
- * deliberately does not. No source is sufficient
42
- * alone: `/info` has no boot id, the identity index has no generation, and a
43
- * stale `ready` file can outlive the process that wrote it.
44
- */
37
+ /** Read generation and readiness through the selected gateway. Server paths are opaque. */
45
38
  export declare function probeDaemonGeneration(fetch: FetchLike, env: NodeJS.ProcessEnv, deps?: GenerationProbeDeps): Promise<DaemonGenerationProbe>;
46
39
  export declare class DaemonGenerationObserver {
47
40
  private current?;