@ours.network/fleet 1.2.0-nightly.4 → 1.2.0-nightly.41
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.
- package/README.md +165 -53
- package/dist/account-origin.d.ts +2 -0
- package/dist/account-origin.js +6 -0
- package/dist/agent-ours/bridge.js +9 -0
- package/dist/agent-ours/correspondence.d.ts +10 -0
- package/dist/agent-ours/correspondence.js +1 -0
- package/dist/agent-ours/harness.js +8 -0
- package/dist/agent-ours/runtime.d.ts +6 -0
- package/dist/agent-ours/runtime.js +36 -0
- package/dist/agent-ours/service.js +33 -13
- package/dist/application/codex-models.d.ts +15 -0
- package/dist/application/codex-models.js +56 -0
- package/dist/application/conversation-history.d.ts +25 -0
- package/dist/application/conversation-history.js +189 -0
- package/dist/application/fleet-query-service.d.ts +1 -1
- package/dist/application/fleet-query-service.js +2 -2
- package/dist/application/legacy-supervisor-identity.d.ts +7 -0
- package/dist/application/legacy-supervisor-identity.js +43 -0
- package/dist/application/model-catalog.d.ts +1 -1
- package/dist/application/model-catalog.js +5 -2
- package/dist/application/preset-provenance.d.ts +19 -0
- package/dist/application/preset-provenance.js +59 -0
- package/dist/application/role-creation-service.js +1 -1
- package/dist/application/role-repository.js +2 -2
- package/dist/application/room-layout-definitions.d.ts +45 -0
- package/dist/application/room-layout-definitions.js +130 -0
- package/dist/application/session-control.d.ts +18 -0
- package/dist/application/session-control.js +54 -3
- package/dist/application/supervisor-ours-tools.d.ts +159 -0
- package/dist/application/supervisor-ours-tools.js +113 -0
- package/dist/application/task-layouts.d.ts +91 -0
- package/dist/application/task-layouts.js +249 -0
- package/dist/application/task-room-service.d.ts +99 -3
- package/dist/application/task-room-service.js +296 -31
- package/dist/atomic-file.d.ts +5 -0
- package/dist/atomic-file.js +62 -1
- package/dist/build-info.json +6 -4
- package/dist/capabilities.d.ts +3 -1
- package/dist/capabilities.js +4 -0
- package/dist/cli.js +186 -34
- package/dist/client-profile.d.ts +5 -16
- package/dist/client-profile.js +4 -112
- package/dist/config.d.ts +18 -0
- package/dist/config.js +28 -4
- package/dist/creation.js +2 -2
- package/dist/daemon-recovery.d.ts +1 -8
- package/dist/daemon-recovery.js +8 -113
- package/dist/docs.d.ts +3 -3
- package/dist/docs.js +44 -22
- package/dist/doctor.d.ts +1 -1
- package/dist/doctor.js +46 -59
- package/dist/erased-resources.d.ts +9 -0
- package/dist/erased-resources.js +87 -0
- package/dist/fleet-command-audit.d.ts +1 -0
- package/dist/fleet-command-audit.js +11 -6
- package/dist/harness/claude-code.js +9 -1
- package/dist/harness/claude-runtime.d.ts +12 -0
- package/dist/harness/claude-runtime.js +43 -0
- package/dist/harness/codex-app-server-proxy.d.ts +4 -1
- package/dist/harness/codex-app-server-proxy.js +41 -5
- package/dist/harness/codex-runtime.d.ts +11 -1
- package/dist/harness/codex-runtime.js +31 -4
- package/dist/harness/codex.d.ts +3 -2
- package/dist/harness/codex.js +8 -5
- package/dist/init-wizard.d.ts +2 -0
- package/dist/init-wizard.js +38 -19
- package/dist/isolation/policy.js +2 -0
- package/dist/isolation/types.d.ts +5 -0
- package/dist/minimal-setup.d.ts +2 -0
- package/dist/minimal-setup.js +44 -0
- package/dist/monitor.d.ts +1 -34
- package/dist/monitor.js +24 -151
- package/dist/notifications/fleet-producer.d.ts +18 -0
- package/dist/notifications/fleet-producer.js +129 -0
- package/dist/notifications/outbox.d.ts +42 -0
- package/dist/notifications/outbox.js +149 -0
- package/dist/owner-channel/channel.d.ts +3 -0
- package/dist/owner-channel/channel.js +34 -6
- package/dist/owner-channel/commands.d.ts +4 -0
- package/dist/owner-channel/commands.js +8 -0
- package/dist/owner-channel/lifecycle-outbox.js +4 -3
- package/dist/owner-channel/ours-client.js +2 -17
- package/dist/paths.d.ts +2 -0
- package/dist/paths.js +2 -0
- package/dist/preset-migration.js +25 -0
- package/dist/rooms-tasks/add-member.d.ts +48 -0
- package/dist/rooms-tasks/add-member.js +83 -0
- package/dist/rooms-tasks/archived-absence.d.ts +8 -0
- package/dist/rooms-tasks/archived-absence.js +48 -0
- package/dist/rooms-tasks/cli.js +34 -5
- package/dist/rooms-tasks/close.d.ts +5 -2
- package/dist/rooms-tasks/close.js +208 -28
- package/dist/rooms-tasks/config.d.ts +1 -1
- package/dist/rooms-tasks/config.js +11 -4
- package/dist/rooms-tasks/cowork-adapter.d.ts +6 -4
- package/dist/rooms-tasks/cowork-adapter.js +11 -117
- package/dist/rooms-tasks/cowork-http.d.ts +3 -0
- package/dist/rooms-tasks/cowork-http.js +70 -0
- package/dist/rooms-tasks/deletion.d.ts +1 -1
- package/dist/rooms-tasks/deletion.js +74 -5
- package/dist/rooms-tasks/erasure.d.ts +6 -0
- package/dist/rooms-tasks/erasure.js +169 -0
- package/dist/rooms-tasks/external-worker.d.ts +1 -1
- package/dist/rooms-tasks/external-worker.js +3 -4
- package/dist/rooms-tasks/layout-binding-client.d.ts +11 -0
- package/dist/rooms-tasks/layout-binding-client.js +56 -0
- package/dist/rooms-tasks/layout-binding-grants.d.ts +21 -0
- package/dist/rooms-tasks/layout-binding-grants.js +128 -0
- package/dist/rooms-tasks/layout-cli.d.ts +4 -0
- package/dist/rooms-tasks/layout-cli.js +107 -0
- package/dist/rooms-tasks/layout-config.d.ts +11 -0
- package/dist/rooms-tasks/layout-config.js +89 -0
- package/dist/rooms-tasks/layout-control.d.ts +21 -0
- package/dist/rooms-tasks/layout-control.js +52 -0
- package/dist/rooms-tasks/layout-service.d.ts +42 -0
- package/dist/rooms-tasks/layout-service.js +185 -0
- package/dist/rooms-tasks/layout.d.ts +114 -0
- package/dist/rooms-tasks/layout.js +250 -0
- package/dist/rooms-tasks/live-readiness.d.ts +18 -0
- package/dist/rooms-tasks/live-readiness.js +85 -0
- package/dist/rooms-tasks/member-startup.d.ts +1 -0
- package/dist/rooms-tasks/member-startup.js +2 -0
- package/dist/rooms-tasks/provision.d.ts +14 -0
- package/dist/rooms-tasks/provision.js +173 -46
- package/dist/rooms-tasks/room-state.d.ts +4 -2
- package/dist/rooms-tasks/room-state.js +53 -8
- package/dist/rooms-tasks/task-state.d.ts +42 -7
- package/dist/rooms-tasks/task-state.js +153 -23
- package/dist/rooms-tasks/types.d.ts +28 -0
- package/dist/rooms-tasks/workspace-artifacts.d.ts +6 -0
- package/dist/rooms-tasks/workspace-artifacts.js +93 -0
- package/dist/rooms-tasks/workspace.d.ts +26 -0
- package/dist/rooms-tasks/workspace.js +195 -0
- package/dist/runner.d.ts +19 -3
- package/dist/runner.js +86 -29
- package/dist/session/acp.d.ts +7 -1
- package/dist/session/acp.js +21 -6
- package/dist/session/arbiter.d.ts +3 -0
- package/dist/session/arbiter.js +20 -2
- package/dist/session/codex-app-server.d.ts +3 -0
- package/dist/session/codex-app-server.js +26 -3
- package/dist/session/control.d.ts +10 -1
- package/dist/session/control.js +37 -2
- package/dist/session/conversation-normalizer.d.ts +2 -1
- package/dist/session/conversation-normalizer.js +1 -1
- package/dist/session/conversation-types.d.ts +5 -3
- package/dist/session/task-notice.d.ts +14 -0
- package/dist/session/task-notice.js +13 -0
- package/dist/session/types.d.ts +3 -0
- package/dist/setup-tunnel-preflight.d.ts +32 -0
- package/dist/setup-tunnel-preflight.js +58 -0
- package/dist/socket-path.d.ts +2 -0
- package/dist/socket-path.js +22 -0
- package/dist/spawn.js +7 -0
- package/dist/startup-readiness.d.ts +13 -0
- package/dist/startup-readiness.js +38 -0
- package/dist/subscriptions/cli.d.ts +91 -0
- package/dist/subscriptions/cli.js +223 -0
- package/dist/subscriptions/launch.d.ts +37 -0
- package/dist/subscriptions/launch.js +93 -0
- package/dist/subscriptions/login.d.ts +75 -0
- package/dist/subscriptions/login.js +182 -0
- package/dist/subscriptions/observed.d.ts +7 -0
- package/dist/subscriptions/observed.js +44 -0
- package/dist/subscriptions/probe.d.ts +10 -0
- package/dist/subscriptions/probe.js +73 -0
- package/dist/subscriptions/service.d.ts +102 -0
- package/dist/subscriptions/service.js +364 -0
- package/dist/subscriptions/store.d.ts +71 -0
- package/dist/subscriptions/store.js +140 -0
- package/dist/supervisor/launchd.js +2 -1
- package/dist/supervisor/systemd.js +9 -23
- package/dist/temp-idle.d.ts +17 -0
- package/dist/temp-idle.js +42 -0
- package/dist/temp-lifecycle.d.ts +6 -1
- package/dist/temp-lifecycle.js +29 -13
- package/dist/web/agent-attachments.d.ts +29 -0
- package/dist/web/agent-attachments.js +162 -0
- package/dist/web/auth.d.ts +29 -1
- package/dist/web/auth.js +81 -7
- package/dist/web/available-port.d.ts +2 -0
- package/dist/web/available-port.js +23 -0
- package/dist/web/control.d.ts +3 -3
- package/dist/web/control.js +10 -8
- package/dist/web/fleet-config-service.d.ts +4 -1
- package/dist/web/fleet-config-service.js +46 -15
- package/dist/web/prefix-gateway.d.ts +29 -0
- package/dist/web/prefix-gateway.js +206 -0
- package/dist/web/runtime.d.ts +3 -1
- package/dist/web/runtime.js +116 -31
- package/dist/web/server.d.ts +16 -0
- package/dist/web/server.js +501 -53
- package/dist/web/service.d.ts +11 -2
- package/dist/web/service.js +30 -4
- package/dist/web/workspace-devices.d.ts +29 -0
- package/dist/web/workspace-devices.js +84 -0
- package/dist/web/workspace-tunnel.d.ts +1 -0
- package/dist/web/workspace-tunnel.js +32 -0
- package/dist/workspace-enrollment.d.ts +53 -0
- package/dist/workspace-enrollment.js +370 -0
- package/docs/room-layouts.md +227 -0
- package/docs/task-workspaces.md +130 -0
- package/examples/room-layouts/fresh.yaml +27 -0
- package/examples/room-layouts/scoped.yaml +21 -0
- package/examples/room-layouts/shared.yaml +19 -0
- package/package.json +13 -18
- package/presets/README.md +78 -0
- package/presets/brain-catalog.json +8 -2
- package/presets/fleet/agent_templates/Critic.yaml +1 -1
- package/presets/fleet/agent_templates/Developer.yaml +1 -1
- package/presets/fleet/agent_templates/Engineer.yaml +5 -0
- package/presets/fleet/agent_templates/LocalCoordinator.yaml +1 -1
- package/presets/fleet/agents/FleetCoordinator.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-1-high.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-low.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-max.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-medium.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-high.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-low.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-max.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-medium.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +4 -0
- package/presets/fleet/brains/claude-opus-5-5-high.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-low.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-max.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-medium.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-high.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-low.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-max.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-medium.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-high.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-low.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-max.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-medium.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-1-sol-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-ultra.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +5 -0
- package/presets/fleet/roles/Coordinator.yaml +34 -11
- package/presets/fleet/roles/Engineer.yaml +40 -0
- package/presets/fleet/room_layouts/engineering.yaml +25 -0
- package/presets/fleet/room_layouts/pair.yaml +24 -0
- package/presets/fleet/room_layouts/single.yaml +17 -0
- package/presets/fleet/room_layouts/team.yaml +30 -0
- package/presets/fleet/room_templates/engineering.yaml +19 -0
- package/presets/manifest.json +2 -2
- package/dist/web-app/assets/index-59GF-gsZ.css +0 -1
- package/dist/web-app/assets/index-C0h3ALvs.js +0 -10
- package/dist/web-app/icons/ours-fleet-maskable.svg +0 -4
- package/dist/web-app/icons/ours-fleet.svg +0 -4
- package/dist/web-app/index.html +0 -17
- package/dist/web-app/manifest.webmanifest +0 -15
- package/dist/web-app/offline.html +0 -18
- 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
|
|
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', '
|
|
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
|
-
|
|
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
|
|
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', '
|
|
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
|
-
|
|
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
|
-
|
|
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('
|
|
1487
|
-
process.stdout.write('
|
|
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;
|
package/dist/client-profile.d.ts
CHANGED
|
@@ -1,17 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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;
|
package/dist/client-profile.js
CHANGED
|
@@ -1,115 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
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,22 @@ 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;
|
|
346
|
+
/**
|
|
347
|
+
* Resolve a persisted temporary-role monitor. A snapshot written before
|
|
348
|
+
* monitor.interrupt existed keeps the non-interrupting delivery it ran with.
|
|
349
|
+
*/
|
|
350
|
+
export declare function resolveSnapshotMonitor(monitor: unknown): MonitorConfig;
|
|
334
351
|
export declare function resolveMonitorConfig(defMonitor: unknown, roleMonitor?: Partial<MonitorConfig>, labels?: {
|
|
335
352
|
base?: string;
|
|
336
353
|
file?: string;
|
|
337
354
|
name?: string;
|
|
355
|
+
harness?: string;
|
|
338
356
|
}): MonitorConfig;
|
|
339
357
|
export declare function findRole(cfg: FleetConfig, name: string): ResolvedRole;
|
|
340
358
|
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
|
|
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,23 @@ 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
|
+
}
|
|
1153
|
+
/**
|
|
1154
|
+
* Resolve a persisted temporary-role monitor. A snapshot written before
|
|
1155
|
+
* monitor.interrupt existed keeps the non-interrupting delivery it ran with.
|
|
1156
|
+
*/
|
|
1157
|
+
export function resolveSnapshotMonitor(monitor) {
|
|
1158
|
+
if (!isPlainObject(monitor))
|
|
1159
|
+
return resolveMonitorConfig(undefined, monitor);
|
|
1160
|
+
return resolveMonitorConfig(undefined, { interrupt: false, ...monitor });
|
|
1161
|
+
}
|
|
1138
1162
|
export function resolveMonitorConfig(defMonitor, roleMonitor, labels = {}) {
|
|
1139
1163
|
const where = labels.file && labels.name ? `${labels.file}: role '${labels.name}' ` : '';
|
|
1140
1164
|
if (defMonitor !== undefined && !isPlainObject(defMonitor))
|
|
@@ -1169,7 +1193,7 @@ export function resolveMonitorConfig(defMonitor, roleMonitor, labels = {}) {
|
|
|
1169
1193
|
wake_sources: merged.wake_sources ?? [...DEFAULT_WAKE_SOURCES],
|
|
1170
1194
|
batch_ms: merged.batch_ms ?? MONITOR_DEFAULT_BATCH_MS,
|
|
1171
1195
|
inject: merged.inject ?? 'notification',
|
|
1172
|
-
interrupt: merged.interrupt ??
|
|
1196
|
+
interrupt: merged.interrupt ?? defaultMonitorInterrupt(labels.harness),
|
|
1173
1197
|
...(merged.stall_recovery !== undefined ? { stall_recovery: merged.stall_recovery } : {}),
|
|
1174
1198
|
...(merged.stall_timeout_ms !== undefined ? { stall_timeout_ms: merged.stall_timeout_ms } : {}),
|
|
1175
1199
|
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(
|
|
216
|
+
return attachClient({
|
|
217
217
|
endpoint: profile.endpoint, expectedInstanceId: profile.expectedInstanceId,
|
|
218
218
|
credentialPath: profile.credentialPath, sessionMode: 'external', leaseToken, env: {},
|
|
219
|
-
}
|
|
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?;
|