@ours.network/fleet 1.2.0-nightly.6 → 1.2.0-nightly.61
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +287 -65
- package/dist/account-origin.d.ts +2 -0
- package/dist/account-origin.js +6 -0
- package/dist/agent-ours/bridge.js +35 -9
- package/dist/agent-ours/correspondence.d.ts +10 -0
- package/dist/agent-ours/correspondence.js +1 -0
- package/dist/agent-ours/file-rpc.d.ts +12 -0
- package/dist/agent-ours/file-rpc.js +18 -4
- package/dist/agent-ours/harness.js +9 -1
- package/dist/agent-ours/mcp-endpoint.d.ts +4 -0
- package/dist/agent-ours/mcp-endpoint.js +5 -0
- package/dist/agent-ours/runtime.d.ts +9 -1
- package/dist/agent-ours/runtime.js +50 -2
- package/dist/agent-ours/service.d.ts +21 -1
- package/dist/agent-ours/service.js +94 -14
- package/dist/application/codex-models.d.ts +15 -0
- package/dist/application/codex-models.js +56 -0
- package/dist/application/conversation-history.d.ts +27 -0
- package/dist/application/conversation-history.js +212 -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/log-service.js +8 -8
- 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 +3 -2
- package/dist/application/role-removal-service.js +9 -1
- package/dist/application/role-repository.js +19 -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 +30 -0
- package/dist/application/session-control.js +72 -3
- package/dist/application/supervisor-ours-tools.d.ts +159 -0
- package/dist/application/supervisor-ours-tools.js +115 -0
- package/dist/application/task-layouts.d.ts +91 -0
- package/dist/application/task-layouts.js +259 -0
- package/dist/application/task-room-service.d.ts +99 -3
- package/dist/application/task-room-service.js +285 -32
- package/dist/application/types.d.ts +2 -0
- package/dist/atomic-file.d.ts +5 -0
- package/dist/atomic-file.js +62 -1
- package/dist/briefing.d.ts +3 -1
- package/dist/briefing.js +70 -30
- package/dist/build-info.json +7 -4
- package/dist/capabilities.d.ts +6 -1
- package/dist/capabilities.js +8 -0
- package/dist/cli.js +407 -37
- package/dist/client-profile.d.ts +5 -17
- package/dist/client-profile.js +4 -127
- package/dist/config.d.ts +45 -0
- package/dist/config.js +57 -6
- package/dist/creation.d.ts +1 -1
- package/dist/creation.js +4 -4
- package/dist/daemon-recovery.d.ts +1 -8
- package/dist/daemon-recovery.js +8 -113
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +123 -19
- package/dist/doctor.d.ts +1 -1
- package/dist/doctor.js +55 -60
- package/dist/erased-resources.d.ts +9 -0
- package/dist/erased-resources.js +87 -0
- package/dist/file-delivery/reader.d.ts +15 -0
- package/dist/file-delivery/reader.js +63 -0
- package/dist/file-delivery/store.d.ts +15 -0
- package/dist/file-delivery/store.js +124 -0
- package/dist/file-delivery/types.d.ts +19 -0
- package/dist/file-delivery/types.js +1 -0
- package/dist/fleet-command-audit.d.ts +7 -0
- package/dist/fleet-command-audit.js +26 -9
- package/dist/harness/agent-session.d.ts +2 -0
- package/dist/harness/claude-code-session.js +1 -1
- package/dist/harness/claude-code.js +20 -11
- 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-reasoning.d.ts +5 -0
- package/dist/harness/codex-reasoning.js +12 -0
- package/dist/harness/codex-runtime.d.ts +11 -1
- package/dist/harness/codex-runtime.js +31 -4
- package/dist/harness/codex-session.js +2 -2
- package/dist/harness/codex.js +23 -12
- package/dist/init-wizard.d.ts +2 -0
- package/dist/init-wizard.js +38 -19
- package/dist/isolation/policy.js +6 -1
- package/dist/isolation/types.d.ts +5 -0
- package/dist/managed-cli.d.ts +253 -0
- package/dist/managed-cli.js +923 -0
- package/dist/managed-recovery.d.ts +13 -0
- package/dist/managed-recovery.js +31 -0
- package/dist/minimal-setup.d.ts +2 -0
- package/dist/minimal-setup.js +44 -0
- package/dist/monitor.d.ts +8 -34
- package/dist/monitor.js +89 -152
- package/dist/notifications/fleet-producer.d.ts +19 -0
- package/dist/notifications/fleet-producer.js +208 -0
- package/dist/notifications/outbox.d.ts +46 -0
- package/dist/notifications/outbox.js +158 -0
- package/dist/notifications/target-cleanup.d.ts +8 -0
- package/dist/notifications/target-cleanup.js +41 -0
- package/dist/notifications/task-producer.d.ts +18 -0
- package/dist/notifications/task-producer.js +67 -0
- package/dist/ops.d.ts +2 -0
- package/dist/ops.js +20 -3
- package/dist/owner-channel/channel.js +17 -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/resolved-plan.js +1 -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 +65 -5
- package/dist/rooms-tasks/close.d.ts +6 -2
- package/dist/rooms-tasks/close.js +246 -37
- 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 +2 -4
- package/dist/rooms-tasks/cowork-adapter.js +7 -121
- package/dist/rooms-tasks/cowork-http.js +29 -1
- package/dist/rooms-tasks/deletion.d.ts +1 -1
- package/dist/rooms-tasks/deletion.js +84 -5
- package/dist/rooms-tasks/erasure.d.ts +6 -0
- package/dist/rooms-tasks/erasure.js +173 -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-member-retirement.d.ts +10 -0
- package/dist/rooms-tasks/layout-member-retirement.js +90 -0
- package/dist/rooms-tasks/layout-service.d.ts +43 -0
- package/dist/rooms-tasks/layout-service.js +203 -0
- package/dist/rooms-tasks/layout.d.ts +121 -0
- package/dist/rooms-tasks/layout.js +285 -0
- package/dist/rooms-tasks/legacy-task-member.d.ts +19 -0
- package/dist/rooms-tasks/legacy-task-member.js +116 -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-ownership.d.ts +5 -0
- package/dist/rooms-tasks/member-ownership.js +40 -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 +3 -0
- package/dist/rooms-tasks/provision.js +101 -49
- package/dist/rooms-tasks/recovery.d.ts +19 -0
- package/dist/rooms-tasks/recovery.js +110 -0
- package/dist/rooms-tasks/room-state.d.ts +4 -2
- package/dist/rooms-tasks/room-state.js +47 -8
- package/dist/rooms-tasks/task-service-retirement.d.ts +11 -0
- package/dist/rooms-tasks/task-service-retirement.js +21 -0
- package/dist/rooms-tasks/task-state.d.ts +51 -8
- package/dist/rooms-tasks/task-state.js +245 -25
- package/dist/rooms-tasks/types.d.ts +40 -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 +35 -0
- package/dist/rooms-tasks/workspace.js +325 -0
- package/dist/runner.d.ts +25 -3
- package/dist/runner.js +291 -49
- package/dist/session/acp.d.ts +53 -1
- package/dist/session/acp.js +216 -40
- package/dist/session/arbiter.d.ts +11 -0
- package/dist/session/arbiter.js +43 -2
- package/dist/session/codex-app-server.d.ts +32 -0
- package/dist/session/codex-app-server.js +137 -23
- package/dist/session/control.d.ts +12 -1
- package/dist/session/control.js +68 -3
- package/dist/session/conversation-normalizer.d.ts +2 -1
- package/dist/session/conversation-normalizer.js +1 -1
- package/dist/session/conversation-store.d.ts +1 -1
- package/dist/session/conversation-store.js +7 -11
- package/dist/session/conversation-types.d.ts +28 -3
- package/dist/session/prompt-batch.d.ts +63 -0
- package/dist/session/prompt-batch.js +86 -0
- package/dist/session/task-notice.d.ts +14 -0
- package/dist/session/task-notice.js +13 -0
- package/dist/session/types.d.ts +14 -0
- package/dist/setup-tunnel-preflight.d.ts +34 -0
- package/dist/setup-tunnel-preflight.js +62 -0
- package/dist/socket-path.d.ts +2 -0
- package/dist/socket-path.js +22 -0
- package/dist/spawn.d.ts +5 -1
- package/dist/spawn.js +40 -4
- package/dist/startup-prompt.d.ts +4 -0
- package/dist/startup-prompt.js +36 -0
- package/dist/startup-readiness.d.ts +13 -0
- package/dist/startup-readiness.js +38 -0
- package/dist/startup-telemetry.d.ts +16 -0
- package/dist/startup-telemetry.js +46 -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/adoption.d.ts +8 -0
- package/dist/supervisor/adoption.js +78 -0
- package/dist/supervisor/catalog.d.ts +62 -0
- package/dist/supervisor/catalog.js +254 -0
- package/dist/supervisor/fleet.d.ts +8 -0
- package/dist/supervisor/fleet.js +89 -0
- package/dist/supervisor/index.js +3 -6
- package/dist/supervisor/launchd.d.ts +1 -1
- package/dist/supervisor/launchd.js +26 -13
- package/dist/supervisor/legacy.d.ts +35 -0
- package/dist/supervisor/legacy.js +524 -0
- package/dist/supervisor/manager.d.ts +15 -0
- package/dist/supervisor/manager.js +270 -0
- package/dist/supervisor/scope.d.ts +4 -0
- package/dist/supervisor/scope.js +10 -0
- package/dist/supervisor/systemd.d.ts +2 -2
- package/dist/supervisor/systemd.js +19 -33
- package/dist/task-supervision.d.ts +9 -0
- package/dist/task-supervision.js +145 -0
- package/dist/temp-idle.d.ts +17 -0
- package/dist/temp-idle.js +42 -0
- package/dist/temp-lifecycle.d.ts +23 -8
- package/dist/temp-lifecycle.js +113 -65
- package/dist/temp-supervisor-recovery.d.ts +1 -1
- package/dist/temp-supervisor-recovery.js +2 -2
- package/dist/tunnel-setup-completion.d.ts +7 -0
- package/dist/tunnel-setup-completion.js +13 -0
- package/dist/tunnel-setup-input.d.ts +19 -0
- package/dist/tunnel-setup-input.js +124 -0
- package/dist/web/agent-attachments.d.ts +29 -0
- package/dist/web/agent-attachments.js +162 -0
- package/dist/web/auth.d.ts +63 -2
- package/dist/web/auth.js +191 -10
- 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 +32 -0
- package/dist/web/prefix-gateway.js +238 -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 +654 -55
- package/dist/web/service.d.ts +11 -2
- package/dist/web/service.js +30 -4
- package/dist/web/workspace-devices.d.ts +107 -0
- package/dist/web/workspace-devices.js +277 -0
- package/dist/web/workspace-tunnel.d.ts +1 -0
- package/dist/web/workspace-tunnel.js +34 -0
- package/dist/workspace-enrollment.d.ts +82 -0
- package/dist/workspace-enrollment.js +566 -0
- package/dist/workspace-replacement.d.ts +66 -0
- package/dist/workspace-replacement.js +220 -0
- package/docs/acp-files.md +146 -0
- package/docs/fleet-migration.md +54 -0
- package/docs/live-voice.md +13 -0
- package/docs/room-layouts.md +227 -0
- package/docs/task-workspaces.md +225 -0
- package/docs/validation/managed-cli-permissions.md +421 -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 +20 -19
- 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/temp-lifecycle.d.ts
CHANGED
|
@@ -4,8 +4,18 @@ export declare const TEMP_TERMINATION_FILE = "termination.jsonl";
|
|
|
4
4
|
export declare const TEMP_STOP_REQUEST_FILE = ".temp-stop-request.json";
|
|
5
5
|
export declare const TEMP_RECLAIM_BATCH = 32;
|
|
6
6
|
export declare const TEMP_LAUNCH_GRACE_MS = 60000;
|
|
7
|
-
export type TempSupervisorKind = 'systemd-transient' | 'launchd-transient' | 'detached';
|
|
8
|
-
export type TempTerminationReason = 'identity-closed' | 'session-ended' | 'operator-stop' | 'supervisor-signal' | 'supervisor-recycle' | 'startup-failure' | 'stale-supervisor';
|
|
7
|
+
export type TempSupervisorKind = 'fleet-managed' | 'systemd-transient' | 'launchd-transient' | 'detached';
|
|
8
|
+
export type TempTerminationReason = 'idle-timeout' | 'identity-closed' | 'session-ended' | 'operator-stop' | 'supervisor-signal' | 'supervisor-recycle' | 'startup-failure' | 'stale-supervisor';
|
|
9
|
+
export interface TaskSupervisorOwner {
|
|
10
|
+
taskId: string;
|
|
11
|
+
roomId?: string;
|
|
12
|
+
roomIdentityCid?: string;
|
|
13
|
+
layout?: {
|
|
14
|
+
runId: string;
|
|
15
|
+
participant: string;
|
|
16
|
+
};
|
|
17
|
+
creationActionId: string;
|
|
18
|
+
}
|
|
9
19
|
export interface TempSupervisorRecord {
|
|
10
20
|
version: 1;
|
|
11
21
|
role: string;
|
|
@@ -16,6 +26,7 @@ export interface TempSupervisorRecord {
|
|
|
16
26
|
target?: string;
|
|
17
27
|
pid?: number;
|
|
18
28
|
binPath?: string;
|
|
29
|
+
taskOwner?: TaskSupervisorOwner;
|
|
19
30
|
}
|
|
20
31
|
export interface TempTerminationRecord {
|
|
21
32
|
version: 1;
|
|
@@ -29,13 +40,12 @@ export interface TempTerminationRecord {
|
|
|
29
40
|
export type SupervisorLauncher = (binPath: string, args: string[], dir: string) => void | Promise<void>;
|
|
30
41
|
export declare const tempSystemdUnit: (name: string) => string;
|
|
31
42
|
export declare const tempLaunchdLabel: (name: string) => string;
|
|
32
|
-
export declare function prepareTempSupervisor(dir: string, role: string): TempSupervisorRecord;
|
|
43
|
+
export declare function prepareTempSupervisor(dir: string, role: string, taskOwner?: TaskSupervisorOwner): TempSupervisorRecord;
|
|
33
44
|
export declare function readTempSupervisor(dir: string): TempSupervisorRecord | undefined;
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
*/
|
|
45
|
+
/** Broad scans retain unsupported entries instead of blocking other members. */
|
|
46
|
+
export declare function readTempSupervisorForScan(dir: string): TempSupervisorRecord | undefined;
|
|
47
|
+
export declare function updateTempSupervisor(dir: string, update: Partial<Omit<TempSupervisorRecord, 'version' | 'role' | 'launchId' | 'createdAt'>>): Promise<TempSupervisorRecord | undefined>;
|
|
48
|
+
/** Register managed members under Fleet; explicit manual mode uses a detached child. */
|
|
39
49
|
export declare function makeTempSupervisorLauncher(options?: {
|
|
40
50
|
exec?: Exec;
|
|
41
51
|
platform?: NodeJS.Platform;
|
|
@@ -44,6 +54,11 @@ export declare function makeTempSupervisorLauncher(options?: {
|
|
|
44
54
|
}): SupervisorLauncher;
|
|
45
55
|
export declare function markTempSupervisorActive(dir: string, pid?: number): Promise<void>;
|
|
46
56
|
export declare function requestedTempStopReason(dir: string): 'operator-stop' | undefined;
|
|
57
|
+
/** Explicit erasure removes only exact retired role/launch events. */
|
|
58
|
+
export declare function eraseTerminationEvents(launches: ReadonlyArray<{
|
|
59
|
+
role: string;
|
|
60
|
+
launchId: string;
|
|
61
|
+
}>): void;
|
|
47
62
|
/** Move retired state out of the live roster without deleting any evidence. */
|
|
48
63
|
export declare function archiveTempState(role: string, reason: TempTerminationReason, outcome: TempTerminationRecord['outcome'], detail: string, now?: Date): string | undefined;
|
|
49
64
|
/** Resolve only an exact role+launch archive; names and timestamps are never proof. */
|
package/dist/temp-lifecycle.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, } from 'node:fs';
|
|
2
|
+
import { parse } from 'yaml';
|
|
2
3
|
import { randomUUID } from 'node:crypto';
|
|
3
4
|
import { basename, join } from 'node:path';
|
|
4
|
-
import { replaceFileAtomically, withFileLock } from './atomic-file.js';
|
|
5
|
+
import { replaceFileAtomically, withFileLock, withSynchronousFileLock } from './atomic-file.js';
|
|
5
6
|
import { realExec } from './exec.js';
|
|
7
|
+
import { captureMemberEnvironment, catalogLiveness, memberKey, readMember, registerMember, unregisterMember, unregisterTaskMember } from './supervisor/catalog.js';
|
|
8
|
+
import { ensureFleetParent } from './supervisor/fleet.js';
|
|
6
9
|
import { stateRoot, tmpRoot } from './paths.js';
|
|
7
10
|
export const TEMP_SUPERVISOR_FILE = '.temp-supervisor.json';
|
|
8
11
|
export const TEMP_TERMINATION_FILE = 'termination.jsonl';
|
|
@@ -13,26 +16,40 @@ export const TEMP_LAUNCH_GRACE_MS = 60_000;
|
|
|
13
16
|
const metadataPath = (dir) => join(dir, TEMP_SUPERVISOR_FILE);
|
|
14
17
|
export const tempSystemdUnit = (name) => `ours-fleet-temp-${name}.service`;
|
|
15
18
|
export const tempLaunchdLabel = (name) => `network.ours.fleet.temp.${name}`;
|
|
16
|
-
export function prepareTempSupervisor(dir, role) {
|
|
19
|
+
export function prepareTempSupervisor(dir, role, taskOwner) {
|
|
17
20
|
const record = {
|
|
18
|
-
version: 1, role, launchId: randomUUID(), createdAt: new Date().toISOString(), phase: 'launching',
|
|
21
|
+
version: 1, role, launchId: randomUUID(), createdAt: new Date().toISOString(), phase: 'launching', ...(taskOwner ? { taskOwner } : {}),
|
|
19
22
|
};
|
|
20
23
|
replaceFileAtomically(metadataPath(dir), JSON.stringify(record, null, 2) + '\n');
|
|
21
24
|
return record;
|
|
22
25
|
}
|
|
23
26
|
export function readTempSupervisor(dir) {
|
|
27
|
+
let value;
|
|
24
28
|
try {
|
|
25
|
-
|
|
26
|
-
if (value.version !== 1 || typeof value.role !== 'string' || typeof value.launchId !== 'string')
|
|
27
|
-
return undefined;
|
|
28
|
-
return value;
|
|
29
|
+
value = JSON.parse(readFileSync(metadataPath(dir), 'utf8'));
|
|
29
30
|
}
|
|
30
31
|
catch {
|
|
31
32
|
return undefined;
|
|
32
33
|
}
|
|
34
|
+
if (!value || typeof value !== 'object' || value.version !== 1 || typeof value.role !== 'string' || typeof value.launchId !== 'string')
|
|
35
|
+
return undefined;
|
|
36
|
+
if (value.kind !== undefined && !['fleet-managed', 'systemd-transient', 'launchd-transient', 'detached'].includes(value.kind))
|
|
37
|
+
throw Error('TEMP_SUPERVISOR_KIND_UNSUPPORTED');
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
/** Broad scans retain unsupported entries instead of blocking other members. */
|
|
41
|
+
export function readTempSupervisorForScan(dir) {
|
|
42
|
+
try {
|
|
43
|
+
return readTempSupervisor(dir);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
if (error instanceof Error && error.message === 'TEMP_SUPERVISOR_KIND_UNSUPPORTED')
|
|
47
|
+
return undefined;
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
33
50
|
}
|
|
34
51
|
const metadataLockPath = (dir) => join(stateRoot(), 'locks', 'temp-supervisors', encodeURIComponent(basename(dir)));
|
|
35
|
-
async function updateTempSupervisor(dir, update) {
|
|
52
|
+
export async function updateTempSupervisor(dir, update) {
|
|
36
53
|
// The launcher and the just-started supervisor are separate processes. Lock
|
|
37
54
|
// their read/merge/write updates so neither can discard the other's kind,
|
|
38
55
|
// target, pid or phase. The lock lives outside the role directory so an
|
|
@@ -48,54 +65,22 @@ async function updateTempSupervisor(dir, update) {
|
|
|
48
65
|
return next;
|
|
49
66
|
});
|
|
50
67
|
}
|
|
51
|
-
/**
|
|
52
|
-
* Launch a temp supervisor outside the caller's service-manager ownership
|
|
53
|
-
* boundary. `detached: true` creates a new process group but does not escape a
|
|
54
|
-
* systemd cgroup; a transient unit does, and is not enabled across reboot.
|
|
55
|
-
*/
|
|
68
|
+
/** Register managed members under Fleet; explicit manual mode uses a detached child. */
|
|
56
69
|
export function makeTempSupervisorLauncher(options = {}) {
|
|
57
70
|
const exec = options.exec ?? realExec;
|
|
58
71
|
const platform = options.platform ?? process.platform;
|
|
59
72
|
const supervisor = options.supervisor ?? process.env.OURS_FLEET_SUPERVISOR;
|
|
60
73
|
return async (binPath, args, dir) => {
|
|
61
|
-
const inherited = [
|
|
62
|
-
'HOME', 'PATH', 'XDG_RUNTIME_DIR', 'OURS_FLEET_HOME', 'CODEX_HOME',
|
|
63
|
-
// Preserve runtime selection across the service-manager boundary, including
|
|
64
|
-
// an explicit empty value which selects the bundle over manager defaults.
|
|
65
|
-
'CODEX_PATH',
|
|
66
|
-
// The child supervisor performs daemon identity and wake probes itself;
|
|
67
|
-
// it must resolve the same ours profile as the spawning supervisor.
|
|
68
|
-
'OURS_PORT', 'OURS_STATE_DIR', 'OURS_API_TOKEN', 'OURS_CONFIG',
|
|
69
|
-
]
|
|
70
|
-
.flatMap(key => process.env[key] !== undefined ? [`${key}=${process.env[key]}`] : []);
|
|
71
74
|
const role = args.at(-1);
|
|
72
75
|
if (!role)
|
|
73
76
|
throw new Error('temporary supervisor launch requires a role name');
|
|
74
|
-
const
|
|
75
|
-
if (supervisor !== 'none'
|
|
76
|
-
const
|
|
77
|
-
await updateTempSupervisor(dir, { phase: 'launching', kind: '
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
`--property=StandardOutput=append:${log}`, `--property=StandardError=append:${log}`,
|
|
82
|
-
...inherited.map(value => `--setenv=${value}`),
|
|
83
|
-
process.execPath, binPath, ...args,
|
|
84
|
-
]);
|
|
85
|
-
if (result.code !== 0)
|
|
86
|
-
throw new Error(`systemd-run ${target} failed: ${result.stderr.trim() || `exit ${result.code}`}`);
|
|
87
|
-
await updateTempSupervisor(dir, { phase: 'active' });
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
if (supervisor !== 'none' && platform === 'darwin') {
|
|
91
|
-
const target = tempLaunchdLabel(role);
|
|
92
|
-
await updateTempSupervisor(dir, { phase: 'launching', kind: 'launchd-transient', target, binPath });
|
|
93
|
-
const result = await exec('launchctl', [
|
|
94
|
-
'submit', '-l', target, '-o', log, '-e', log, '--',
|
|
95
|
-
'/usr/bin/env', ...inherited, process.execPath, binPath, ...args,
|
|
96
|
-
]);
|
|
97
|
-
if (result.code !== 0)
|
|
98
|
-
throw new Error(`launchctl submit ${target} failed: ${result.stderr.trim() || `exit ${result.code}`}`);
|
|
77
|
+
const metadata = readTempSupervisor(dir);
|
|
78
|
+
if (supervisor !== 'none') {
|
|
79
|
+
const kind = metadata?.taskOwner ? 'task' : 'temporary';
|
|
80
|
+
await updateTempSupervisor(dir, { phase: 'launching', kind: 'fleet-managed', target: memberKey(role, kind), binPath });
|
|
81
|
+
await ensureFleetParent(exec, platform);
|
|
82
|
+
await registerMember({ name: role, kind, dir, environment: await captureMemberEnvironment(),
|
|
83
|
+
...(metadata?.taskOwner ? { taskOwner: metadata.taskOwner, launchId: metadata.launchId } : {}) });
|
|
99
84
|
await updateTempSupervisor(dir, { phase: 'active' });
|
|
100
85
|
return;
|
|
101
86
|
}
|
|
@@ -133,18 +118,34 @@ function appendTermination(dir, record) {
|
|
|
133
118
|
replaceFileAtomically(join(dir, TEMP_GLOBAL_TERMINATION_MARKER), line);
|
|
134
119
|
}
|
|
135
120
|
function appendGlobalTermination(line, checkExisting = false) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
121
|
+
withSynchronousFileLock(join(stateRoot(), 'locks', 'termination-journal'), () => {
|
|
122
|
+
mkdirSync(archiveRoot(), { recursive: true, mode: 0o700 });
|
|
123
|
+
const path = join(archiveRoot(), 'terminations.jsonl');
|
|
124
|
+
// Recovery may revisit a .retiring directory after a crash between the
|
|
125
|
+
// global append and final rename. Avoid duplicating that exact event.
|
|
126
|
+
if (checkExisting) {
|
|
127
|
+
try {
|
|
128
|
+
if (readFileSync(path, 'utf8').split('\n').includes(line.trimEnd()))
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
catch { /* the journal does not exist yet */ }
|
|
144
132
|
}
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
|
|
133
|
+
appendFileSync(path, line, { mode: 0o600 });
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
/** Explicit erasure removes only exact retired role/launch events. */
|
|
137
|
+
export function eraseTerminationEvents(launches) {
|
|
138
|
+
withSynchronousFileLock(join(stateRoot(), 'locks', 'termination-journal'), () => {
|
|
139
|
+
const path = join(archiveRoot(), 'terminations.jsonl');
|
|
140
|
+
if (!existsSync(path))
|
|
141
|
+
return;
|
|
142
|
+
const lines = readFileSync(path, 'utf8').split('\n').filter(Boolean);
|
|
143
|
+
const kept = lines.filter(line => {
|
|
144
|
+
const row = JSON.parse(line);
|
|
145
|
+
return !launches.some(launch => launch.role === row.role && launch.launchId === row.launchId);
|
|
146
|
+
});
|
|
147
|
+
replaceFileAtomically(path, kept.length ? kept.join('\n') + '\n' : '');
|
|
148
|
+
});
|
|
148
149
|
}
|
|
149
150
|
/** Pick a sibling path without overwriting evidence from an earlier attempt. */
|
|
150
151
|
function collisionSafeArchivePaths(targetBase, retiringBase) {
|
|
@@ -195,7 +196,7 @@ export function archiveTempState(role, reason, outcome, detail, now = new Date()
|
|
|
195
196
|
return paths.target;
|
|
196
197
|
}
|
|
197
198
|
function isArchiveForLaunch(path, role, launchId) {
|
|
198
|
-
const supervisor =
|
|
199
|
+
const supervisor = readTempSupervisorForScan(path);
|
|
199
200
|
if (supervisor?.role !== role || supervisor.launchId !== launchId)
|
|
200
201
|
return false;
|
|
201
202
|
try {
|
|
@@ -240,7 +241,7 @@ export function tempArchiveForCreationAction(role, creationActionId) {
|
|
|
240
241
|
return undefined;
|
|
241
242
|
}
|
|
242
243
|
const matches = entries.flatMap(path => {
|
|
243
|
-
const supervisor =
|
|
244
|
+
const supervisor = readTempSupervisorForScan(path);
|
|
244
245
|
if (!supervisor || supervisor.role !== role || !isArchiveForLaunch(path, role, supervisor.launchId))
|
|
245
246
|
return [];
|
|
246
247
|
try {
|
|
@@ -301,7 +302,15 @@ function recoverInterruptedArchives(now) {
|
|
|
301
302
|
const recovered = [];
|
|
302
303
|
for (const name of names) {
|
|
303
304
|
const source = join(archiveRoot(), name);
|
|
304
|
-
|
|
305
|
+
let supervisor;
|
|
306
|
+
try {
|
|
307
|
+
supervisor = readTempSupervisor(source);
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
if (error instanceof Error && error.message === 'TEMP_SUPERVISOR_KIND_UNSUPPORTED')
|
|
311
|
+
continue;
|
|
312
|
+
throw error;
|
|
313
|
+
}
|
|
305
314
|
let line;
|
|
306
315
|
try {
|
|
307
316
|
line = readFileSync(join(source, TEMP_TERMINATION_FILE), 'utf8')
|
|
@@ -377,11 +386,13 @@ export async function tempSupervisorLiveness(dir, deps = {}) {
|
|
|
377
386
|
// A concurrent spawn writes the target before systemd-run/launchctl has
|
|
378
387
|
// registered it. A not-yet-created unit looks exactly like an inactive one,
|
|
379
388
|
// so launch phase plus its bounded grace is not cleanup authority.
|
|
380
|
-
if (record.phase === 'launching'
|
|
389
|
+
if (record.phase === 'launching' && !requestedTempStopReason(dir)
|
|
381
390
|
&& (!Number.isFinite(age) || age < TEMP_LAUNCH_GRACE_MS))
|
|
382
391
|
return 'unknown';
|
|
383
392
|
// Older writers could lose `kind` in an unlocked metadata RMW while retaining
|
|
384
393
|
// the supervisor pid. Exact argv ownership is stronger than the missing tag.
|
|
394
|
+
if (record.kind === 'fleet-managed')
|
|
395
|
+
return (await catalogLiveness(record.target ?? memberKey(record.role, record.taskOwner ? 'task' : 'temporary'), exec)).state;
|
|
385
396
|
if (record.pid && (!record.kind || record.kind === 'detached'))
|
|
386
397
|
return detachedProcessLiveness(record, deps);
|
|
387
398
|
if (record.kind === 'systemd-transient' && record.target) {
|
|
@@ -445,6 +456,13 @@ export async function stopTempSupervisor(role, deps = {}) {
|
|
|
445
456
|
replaceFileAtomically(join(dir, TEMP_STOP_REQUEST_FILE), JSON.stringify({
|
|
446
457
|
version: 1, role, reason: 'operator-stop', requestedAt: new Date().toISOString(),
|
|
447
458
|
}) + '\n');
|
|
459
|
+
if (record.kind === 'fleet-managed') {
|
|
460
|
+
if (record.taskOwner)
|
|
461
|
+
await unregisterTaskMember(role, { ...record.taskOwner, launchId: record.launchId }, { exec, sleep: deps.sleep });
|
|
462
|
+
else
|
|
463
|
+
await unregisterMember(record.target ?? memberKey(role, 'temporary'), { exec, sleep: deps.sleep });
|
|
464
|
+
return 'stopped';
|
|
465
|
+
}
|
|
448
466
|
if (record.kind === 'systemd-transient' && record.target) {
|
|
449
467
|
const result = await exec('systemctl', ['--user', 'stop', record.target]);
|
|
450
468
|
if (result.code !== 0
|
|
@@ -495,6 +513,26 @@ export async function stopTempSupervisor(role, deps = {}) {
|
|
|
495
513
|
(deps.kill ?? process.kill)(record.pid, 'SIGTERM');
|
|
496
514
|
return 'stopped';
|
|
497
515
|
}
|
|
516
|
+
/** Legacy task snapshots remain recovery evidence even before explicit migration. */
|
|
517
|
+
function hasRetainedTaskReference(dir) {
|
|
518
|
+
try {
|
|
519
|
+
if (basename(dir).startsWith('layout-')) {
|
|
520
|
+
const provenance = JSON.parse(readFileSync(join(dir, 'creation.json'), 'utf8'));
|
|
521
|
+
if (typeof provenance.creationActionId === 'string' && provenance.creationActionId.startsWith('task-'))
|
|
522
|
+
return true;
|
|
523
|
+
}
|
|
524
|
+
const startup = parse(readFileSync(join(dir, 'role.yaml'), 'utf8'))?.roomMemberStartup;
|
|
525
|
+
if (!startup?.room_id || !/^[A-Za-z0-9_-]{1,120}$/.test(startup.room_id))
|
|
526
|
+
return false;
|
|
527
|
+
const room = JSON.parse(readFileSync(join(stateRoot(), 'rooms', `${startup.room_id}.json`), 'utf8'));
|
|
528
|
+
return typeof room.task_id === 'string'
|
|
529
|
+
&& room.member_seats.some((seat) => seat.role_name === basename(dir));
|
|
530
|
+
}
|
|
531
|
+
catch (error) {
|
|
532
|
+
// An unreadable role/room is uncertain ownership, never permission to reclaim.
|
|
533
|
+
return error.code !== 'ENOENT';
|
|
534
|
+
}
|
|
535
|
+
}
|
|
498
536
|
/**
|
|
499
537
|
* Move a bounded batch of definitely-dead temp state into the evidence archive.
|
|
500
538
|
* Unknown/legacy/live entries are preserved; absence of proof is never cleanup authority.
|
|
@@ -516,8 +554,18 @@ export async function reclaimStaleTempState(deps = {}) {
|
|
|
516
554
|
const archived = [...recovered];
|
|
517
555
|
for (const entry of entries) {
|
|
518
556
|
const dir = join(tmpRoot(), entry.name);
|
|
519
|
-
|
|
520
|
-
|
|
557
|
+
const supervisor = readTempSupervisorForScan(dir);
|
|
558
|
+
if (!supervisor || supervisor.taskOwner || hasRetainedTaskReference(dir))
|
|
559
|
+
continue;
|
|
560
|
+
if (supervisor?.kind === 'fleet-managed' && supervisor.target) {
|
|
561
|
+
try {
|
|
562
|
+
if (readMember(supervisor.target)?.desired === 'running')
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
catch {
|
|
566
|
+
continue;
|
|
567
|
+
} // uncertain catalog intent is never reclamation authority
|
|
568
|
+
}
|
|
521
569
|
if (await tempSupervisorLiveness(dir, deps) !== 'stopped')
|
|
522
570
|
continue;
|
|
523
571
|
const target = archiveTempState(entry.name, 'stale-supervisor', 'reclaimed', 'supervisor is definitively stopped; state moved from the live roster without deletion', now);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export declare const TEMP_RECYCLE_EXIT = 75;
|
|
2
2
|
/** Keep the service's main process alive while replacing a PID-scoped worker. */
|
|
3
|
-
export declare function runTempSupervisor(name: string, entrypoint: string, initial?: () => Promise<void
|
|
3
|
+
export declare function runTempSupervisor(name: string, entrypoint: string, initial?: () => Promise<void>, suspendOnStop?: boolean): Promise<void>;
|
|
@@ -2,7 +2,7 @@ import { spawn } from 'node:child_process';
|
|
|
2
2
|
import { runTemp, SupervisorRecycleRequiredError } from './runner.js';
|
|
3
3
|
export const TEMP_RECYCLE_EXIT = 75;
|
|
4
4
|
/** Keep the service's main process alive while replacing a PID-scoped worker. */
|
|
5
|
-
export async function runTempSupervisor(name, entrypoint, initial = () => runTemp(name)) {
|
|
5
|
+
export async function runTempSupervisor(name, entrypoint, initial = () => runTemp(name), suspendOnStop = false) {
|
|
6
6
|
try {
|
|
7
7
|
await initial();
|
|
8
8
|
return;
|
|
@@ -12,7 +12,7 @@ export async function runTempSupervisor(name, entrypoint, initial = () => runTem
|
|
|
12
12
|
throw error;
|
|
13
13
|
}
|
|
14
14
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
15
|
-
const child = spawn(process.execPath, [entrypoint, '_run-temp-worker', name], {
|
|
15
|
+
const child = spawn(process.execPath, [entrypoint, '_run-temp-worker', name, ...(suspendOnStop ? ['--suspend-on-stop'] : [])], {
|
|
16
16
|
stdio: 'inherit',
|
|
17
17
|
env: process.env,
|
|
18
18
|
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Keep the resumable record until both the QR and ordinary connection code were emitted. */
|
|
2
|
+
export declare function completeTunnelSetup(effects: {
|
|
3
|
+
link: () => Promise<unknown>;
|
|
4
|
+
qr: (code: string) => Promise<string>;
|
|
5
|
+
write: (text: string) => unknown;
|
|
6
|
+
clear: () => void;
|
|
7
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Keep the resumable record until both the QR and ordinary connection code were emitted. */
|
|
2
|
+
export async function completeTunnelSetup(effects) {
|
|
3
|
+
try {
|
|
4
|
+
const link = await effects.link();
|
|
5
|
+
const code = Buffer.from(JSON.stringify(link)).toString('base64url');
|
|
6
|
+
const qr = await effects.qr(code);
|
|
7
|
+
await effects.write('Tunnel configured. Scan the QR or paste this private single-use connection code into the App.\n' + qr + '\n' + code + '\n');
|
|
8
|
+
effects.clear();
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
throw Error('Tunnel is configured but its connection code could not be printed. Run ours-fleet setup-tunnel --resume to retry.');
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type WorkspacePayload } from './workspace-enrollment.js';
|
|
2
|
+
/** Read the same private-file transport as enrollment, without consuming the grant. */
|
|
3
|
+
export declare function readTunnelSetupFile(file: string): string;
|
|
4
|
+
export declare function readTunnelSetupStdin(input?: AsyncIterable<Buffer | string>): Promise<string>;
|
|
5
|
+
/** Only a minimal one-use v2 grant is allowed in shell arguments. */
|
|
6
|
+
export declare function readTunnelSetupArgument(input: string): string;
|
|
7
|
+
/** Redeem the grant by HTTPS POST; long-lived connector credentials remain off argv and URLs. */
|
|
8
|
+
export declare function redeemTunnelSetup(input: string, request?: typeof fetch): Promise<WorkspacePayload>;
|
|
9
|
+
/** Inspect only public destination metadata; this never consumes a one-use grant. */
|
|
10
|
+
export declare function inspectTunnelSetup(input: string, request?: typeof fetch): Promise<{
|
|
11
|
+
workspaceId: string;
|
|
12
|
+
serverCid: string;
|
|
13
|
+
appOrigin: string;
|
|
14
|
+
} | undefined>;
|
|
15
|
+
export declare function verifyTunnelSetupTarget(target: {
|
|
16
|
+
workspaceId: string;
|
|
17
|
+
serverCid: string;
|
|
18
|
+
appOrigin: string;
|
|
19
|
+
} | undefined, payload: WorkspacePayload): void;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { lstatSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { decodeWorkspacePayload } from './workspace-enrollment.js';
|
|
3
|
+
/** Read the same private-file transport as enrollment, without consuming the grant. */
|
|
4
|
+
export function readTunnelSetupFile(file) {
|
|
5
|
+
const stat = lstatSync(file);
|
|
6
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1 || stat.uid !== process.getuid?.() || (stat.mode & 0o077) !== 0 || stat.size > 32768)
|
|
7
|
+
throw Error('Setup payload requires an owned private regular file (chmod 600)');
|
|
8
|
+
return readFileSync(file, 'utf8');
|
|
9
|
+
}
|
|
10
|
+
export async function readTunnelSetupStdin(input = process.stdin) {
|
|
11
|
+
let value = '';
|
|
12
|
+
for await (const chunk of input) {
|
|
13
|
+
value += chunk.toString();
|
|
14
|
+
if (Buffer.byteLength(value) > 32768)
|
|
15
|
+
throw Error('Tunnel setup payload is too large');
|
|
16
|
+
}
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
/** Only a minimal one-use v2 grant is allowed in shell arguments. */
|
|
20
|
+
export function readTunnelSetupArgument(input) {
|
|
21
|
+
decodeTunnelSetupGrant(input);
|
|
22
|
+
return input;
|
|
23
|
+
}
|
|
24
|
+
function decodeTunnelSetupGrant(input) {
|
|
25
|
+
if (!/^[A-Za-z0-9_-]{1,32768}$/.test(input.trim()))
|
|
26
|
+
throw Error('Invalid tunnel setup payload');
|
|
27
|
+
let grant;
|
|
28
|
+
try {
|
|
29
|
+
grant = JSON.parse(Buffer.from(input.trim(), 'base64url').toString());
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
throw Error('Invalid tunnel setup payload');
|
|
33
|
+
}
|
|
34
|
+
if (grant?.version !== 2 || Object.keys(grant).sort().join(',') !== 'appOrigin,code,expiresAt,version'
|
|
35
|
+
|| !['https://app.ours.network', 'https://app.ours-tunnel.com'].includes(grant.appOrigin)
|
|
36
|
+
|| !/^[A-Za-z0-9_-]{43}$/.test(grant.code) || !Number.isSafeInteger(grant.expiresAt)
|
|
37
|
+
|| grant.expiresAt <= Date.now() || grant.expiresAt > Date.now() + 16 * 60000)
|
|
38
|
+
throw Error('Tunnel setup grant is invalid or expired; get a fresh command from the App');
|
|
39
|
+
return grant;
|
|
40
|
+
}
|
|
41
|
+
/** Redeem the grant by HTTPS POST; long-lived connector credentials remain off argv and URLs. */
|
|
42
|
+
export async function redeemTunnelSetup(input, request = fetch) {
|
|
43
|
+
if (!/^[A-Za-z0-9_-]{1,32768}$/.test(input.trim()))
|
|
44
|
+
throw Error('Invalid tunnel setup payload');
|
|
45
|
+
// v1 remains supported exclusively by the private file/stdin CLI transports.
|
|
46
|
+
let version;
|
|
47
|
+
try {
|
|
48
|
+
version = JSON.parse(Buffer.from(input.trim(), 'base64url').toString())?.version;
|
|
49
|
+
}
|
|
50
|
+
catch { }
|
|
51
|
+
if (version === 1)
|
|
52
|
+
return decodeWorkspacePayload(input);
|
|
53
|
+
const grant = decodeTunnelSetupGrant(input);
|
|
54
|
+
let response;
|
|
55
|
+
try {
|
|
56
|
+
response = await request(grant.appOrigin + '/account-api/workspace-install-redeem', {
|
|
57
|
+
method: 'POST', redirect: 'error', credentials: 'omit', signal: AbortSignal.timeout(30000),
|
|
58
|
+
headers: { 'Content-Type': 'application/json', Origin: grant.appOrigin }, body: JSON.stringify({ code: grant.code }),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
throw Error('Could not reach the account service; setup grant redemption may be unconfirmed. Check the App before requesting a fresh command');
|
|
63
|
+
}
|
|
64
|
+
if (!response.ok) {
|
|
65
|
+
await response.body?.cancel();
|
|
66
|
+
throw Error(response.status === 410 ? 'Tunnel setup grant expired or was already used; get a fresh command from the App'
|
|
67
|
+
: `Tunnel setup grant redemption failed (HTTP ${response.status}); check the App before retrying`);
|
|
68
|
+
}
|
|
69
|
+
let result;
|
|
70
|
+
try {
|
|
71
|
+
result = await response.json();
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
throw Error('Invalid tunnel setup response');
|
|
75
|
+
}
|
|
76
|
+
if (typeof result?.payload !== 'string' || !/^[A-Za-z0-9_-]{1,32768}$/.test(result.payload))
|
|
77
|
+
throw Error('Invalid tunnel setup response');
|
|
78
|
+
const payload = decodeWorkspacePayload(result.payload);
|
|
79
|
+
if (payload.appOrigin !== grant.appOrigin)
|
|
80
|
+
throw Error('Tunnel setup response origin mismatch');
|
|
81
|
+
return payload;
|
|
82
|
+
}
|
|
83
|
+
/** Inspect only public destination metadata; this never consumes a one-use grant. */
|
|
84
|
+
export async function inspectTunnelSetup(input, request = fetch) {
|
|
85
|
+
let version;
|
|
86
|
+
try {
|
|
87
|
+
version = JSON.parse(Buffer.from(input.trim(), 'base64url').toString()).version;
|
|
88
|
+
}
|
|
89
|
+
catch { }
|
|
90
|
+
if (version === 1) {
|
|
91
|
+
const p = decodeWorkspacePayload(input);
|
|
92
|
+
return { workspaceId: p.challenge.workspaceId, serverCid: p.serverCid, appOrigin: p.appOrigin };
|
|
93
|
+
}
|
|
94
|
+
const grant = decodeTunnelSetupGrant(input);
|
|
95
|
+
let response;
|
|
96
|
+
try {
|
|
97
|
+
response = await request(grant.appOrigin + '/account-api/workspace-install-inspect', { method: 'POST', redirect: 'error', credentials: 'omit', signal: AbortSignal.timeout(15000), headers: { Origin: grant.appOrigin, 'Content-Type': 'application/json' }, body: JSON.stringify({ code: grant.code }) });
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
throw Error('Could not inspect the account setup command; existing setup and grant preserved');
|
|
101
|
+
}
|
|
102
|
+
if (response.status === 404 || response.status === 405) {
|
|
103
|
+
await response.body?.cancel();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
await response.body?.cancel();
|
|
108
|
+
throw Error('Setup command inspection failed; existing setup and grant preserved');
|
|
109
|
+
}
|
|
110
|
+
let target;
|
|
111
|
+
try {
|
|
112
|
+
target = await response.json();
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
throw Error('Invalid setup command inspection response');
|
|
116
|
+
}
|
|
117
|
+
if (!target || Object.keys(target).sort().join(',') !== 'serverCid,workspaceId' || typeof target.workspaceId !== 'string' || !/^[A-Za-z0-9_-]{43}$/.test(target.workspaceId) || typeof target.serverCid !== 'string' || !/^[a-f0-9]{64}$/i.test(target.serverCid))
|
|
118
|
+
throw Error('Invalid setup command inspection response');
|
|
119
|
+
return { ...target, appOrigin: grant.appOrigin };
|
|
120
|
+
}
|
|
121
|
+
export function verifyTunnelSetupTarget(target, payload) {
|
|
122
|
+
if (target && (target.workspaceId !== payload.challenge.workspaceId || target.serverCid.toUpperCase() !== payload.serverCid.toUpperCase() || target.appOrigin !== payload.appOrigin))
|
|
123
|
+
throw Error('Redeemed setup command differs from inspected destination; existing setup preserved');
|
|
124
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export declare const MAX_ATTACHMENT_BYTES: number;
|
|
2
|
+
export declare const MAX_ATTACHMENT_BODY: number;
|
|
3
|
+
export interface AgentAttachment {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
mimeType: string;
|
|
7
|
+
size: number;
|
|
8
|
+
sha256: string;
|
|
9
|
+
generation: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function storeAgentAttachment(stateDir: string, generation: string, input: unknown): AgentAttachment;
|
|
12
|
+
export declare function attachmentPrompt(stateDir: string, generation: string, ids: unknown, text: string): string;
|
|
13
|
+
export declare function readAgentAttachment(stateDir: string, id: string): {
|
|
14
|
+
record: AgentAttachment;
|
|
15
|
+
bytes: Buffer;
|
|
16
|
+
};
|
|
17
|
+
interface AttachmentPresentation {
|
|
18
|
+
generation: string;
|
|
19
|
+
commandId: string;
|
|
20
|
+
text: string;
|
|
21
|
+
transportDigest: string;
|
|
22
|
+
ids: string[];
|
|
23
|
+
}
|
|
24
|
+
/** Written before admission so a lost HTTP response cannot lose the visible message. */
|
|
25
|
+
export declare function storeAttachmentPresentation(stateDir: string, value: AttachmentPresentation): void;
|
|
26
|
+
export declare function prepareAttachmentPresentation(stateDir: string, generation: string, commandId: string, ids: string[], text: string, transport: string): void;
|
|
27
|
+
/** Never infer attachments by stripping user text or interpreting a marker. */
|
|
28
|
+
export declare function presentAttachmentEvent<T>(stateDir: string | undefined, roleId: string, event: T): T;
|
|
29
|
+
export {};
|