@ours.network/fleet 1.2.0-nightly.6 → 1.2.0-nightly.60
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
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
/** Readiness is a separate turn after identity/profile and room admission succeed. */
|
|
3
|
+
export function managedStartupPrompt(stateDir, mode, hasPriorBoot = false) {
|
|
4
|
+
const briefing = join(stateDir, 'briefing.md');
|
|
5
|
+
const continuity = mode === 'resume' || hasPriorBoot
|
|
6
|
+
? ` For a later continuation task, read ${join(stateDir, 'WORKLOG.md')} once for continuity.`
|
|
7
|
+
: '';
|
|
8
|
+
return 'Fleet readiness check. The supervisor has verified your assigned identity, configured Role profile, '
|
|
9
|
+
+ 'and any required room admission. Reply with a short final console acknowledgement that you are ready, '
|
|
10
|
+
+ 'then end this turn. Announce readiness before any tool calls or file reads. '
|
|
11
|
+
+ 'Do not read briefing, routines, worklog, inbox, history, help or config during this readiness turn; '
|
|
12
|
+
+ 'do not perform identity/profile setup or broad tool discovery. Do not claim task work has been done. '
|
|
13
|
+
+ 'The supervisor delivers configured work separately; otherwise wait for a concrete task or supervisor wake. '
|
|
14
|
+
+ `For a later task or mail wake, read ${briefing} once before acting and reuse it within that wake. `
|
|
15
|
+
+ 'Read routines only for concrete work; an idle launch needs no detail reads. '
|
|
16
|
+
+ 'The supervisor delivers initial unread mail and later arrivals as wakes; do not poll the inbox at startup.'
|
|
17
|
+
+ continuity;
|
|
18
|
+
}
|
|
19
|
+
/** Configured work/continuity runs after readiness, never inside the fast turn. */
|
|
20
|
+
export function managedTaskPrompt(stateDir, mode, hasPriorBoot = false) {
|
|
21
|
+
const briefing = join(stateDir, 'briefing.md');
|
|
22
|
+
const continuity = mode === 'resume' || hasPriorBoot
|
|
23
|
+
? ` Read ${join(stateDir, 'WORKLOG.md')} once for continuity.`
|
|
24
|
+
: '';
|
|
25
|
+
return `Read and follow ${briefing} for the assigned work. Readiness has already been announced; `
|
|
26
|
+
+ 'this is a separate task turn, so do not repeat the readiness acknowledgement.' + continuity + ' '
|
|
27
|
+
+ 'Identify the concrete assignment in the briefing or unfinished work in the continuation log. '
|
|
28
|
+
+ 'A role charter or public bio alone is not a new task. If there is no concrete work, end this turn '
|
|
29
|
+
+ 'and await a task without reading routines, inbox, history, or unrelated files. '
|
|
30
|
+
+ 'For concrete work, read any existing routines once, reuse details already read in this wake, '
|
|
31
|
+
+ 'acknowledge the actual task and next action through its assigned reply route, and proceed. '
|
|
32
|
+
+ (mode === 'fresh' && !hasPriorBoot ? 'On a fresh launch, do not read a worklog for initialization. ' : '')
|
|
33
|
+
+ 'Use inbox/history only when a mail wake, unread pagination, or the actual task needs them. '
|
|
34
|
+
+ 'The supervisor delivers initial unread mail; no model-owned startup poll is needed. '
|
|
35
|
+
+ 'Identity/profile setup, help/config exploration and broad tool discovery are not startup rituals.';
|
|
36
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type DaemonGenerationProbe } from './daemon-recovery.js';
|
|
2
|
+
export declare const STARTUP_WAIT_MS = 240000;
|
|
3
|
+
export declare const STARTUP_RETRY_MS = 5000;
|
|
4
|
+
interface ReadinessDeps {
|
|
5
|
+
probe(env: NodeJS.ProcessEnv): Promise<DaemonGenerationProbe>;
|
|
6
|
+
now(): number;
|
|
7
|
+
sleep(ms: number): Promise<void>;
|
|
8
|
+
log(message: string): void;
|
|
9
|
+
env: NodeJS.ProcessEnv;
|
|
10
|
+
}
|
|
11
|
+
/** Read-only preflight: never binds identities, starts a harness or edits its ledger. */
|
|
12
|
+
export declare function waitForRoleDaemon(name: string, configPath?: string, partial?: Partial<ReadinessDeps>, temp?: boolean): Promise<void>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { agentDir } from './paths.js';
|
|
2
|
+
import { findRole, loadConfig } from './config.js';
|
|
3
|
+
import { resolveConfigPath, loadTempRole } from './runner.js';
|
|
4
|
+
import { probeDaemonGeneration } from './daemon-recovery.js';
|
|
5
|
+
export const STARTUP_WAIT_MS = 240_000;
|
|
6
|
+
export const STARTUP_RETRY_MS = 5_000;
|
|
7
|
+
/** Read-only preflight: never binds identities, starts a harness or edits its ledger. */
|
|
8
|
+
export async function waitForRoleDaemon(name, configPath, partial = {}, temp = false) {
|
|
9
|
+
const deps = {
|
|
10
|
+
probe: env => probeDaemonGeneration(((url, init) => globalThis.fetch(url, {
|
|
11
|
+
...init, signal: AbortSignal.timeout(10_000),
|
|
12
|
+
})), env),
|
|
13
|
+
now: Date.now,
|
|
14
|
+
sleep: ms => new Promise(resolve => setTimeout(resolve, ms)),
|
|
15
|
+
log: message => process.stderr.write(message + '\n'),
|
|
16
|
+
env: process.env,
|
|
17
|
+
...partial,
|
|
18
|
+
};
|
|
19
|
+
const path = resolveConfigPath(agentDir(name), configPath);
|
|
20
|
+
const role = temp ? loadTempRole(name) : findRole(loadConfig(path), name);
|
|
21
|
+
const env = { ...deps.env, ...role.env };
|
|
22
|
+
const deadline = deps.now() + STARTUP_WAIT_MS;
|
|
23
|
+
let lastReason = '';
|
|
24
|
+
while (deps.now() < deadline) {
|
|
25
|
+
const result = await deps.probe(env);
|
|
26
|
+
if (result.state === 'ready') {
|
|
27
|
+
deps.log(`[${name}] daemon ready; starting supervised agent`);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// Only structured diagnostic codes, never endpoint responses or credentials.
|
|
31
|
+
const reason = /^[A-Z][A-Z0-9_]*$/.test(result.reason) ? result.reason : 'DAEMON_UNAVAILABLE';
|
|
32
|
+
if (reason !== lastReason)
|
|
33
|
+
deps.log(`[${name}] waiting for configured daemon: ${reason}`);
|
|
34
|
+
lastReason = reason;
|
|
35
|
+
await deps.sleep(Math.min(STARTUP_RETRY_MS, Math.max(0, deadline - deps.now())));
|
|
36
|
+
}
|
|
37
|
+
throw new Error(`Daemon not ready (${lastReason}); service manager will retry startup`);
|
|
38
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ConversationEventV1 } from './session/conversation-types.js';
|
|
2
|
+
/** Body-free startup diagnostics. First output is an observation, not proof of useful work. */
|
|
3
|
+
export declare class StartupTelemetry {
|
|
4
|
+
private readonly now;
|
|
5
|
+
private readonly log;
|
|
6
|
+
private readonly started;
|
|
7
|
+
private promptAt?;
|
|
8
|
+
private promptId?;
|
|
9
|
+
private firstOutput;
|
|
10
|
+
private completed;
|
|
11
|
+
private readonly tools;
|
|
12
|
+
constructor(now: () => number, log: (line: string) => void);
|
|
13
|
+
mark(stage: 'supervisor_started' | 'identity_prepare_started' | 'identity_ready' | 'harness_starting' | 'harness_ready' | 'startup_submitted' | 'readiness_turn_completed'): void;
|
|
14
|
+
observe(event: ConversationEventV1): void;
|
|
15
|
+
finish(): void;
|
|
16
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** Body-free startup diagnostics. First output is an observation, not proof of useful work. */
|
|
2
|
+
export class StartupTelemetry {
|
|
3
|
+
now;
|
|
4
|
+
log;
|
|
5
|
+
started;
|
|
6
|
+
promptAt;
|
|
7
|
+
promptId;
|
|
8
|
+
firstOutput = false;
|
|
9
|
+
completed = false;
|
|
10
|
+
tools = new Set();
|
|
11
|
+
constructor(now, log) {
|
|
12
|
+
this.now = now;
|
|
13
|
+
this.log = log;
|
|
14
|
+
this.started = now();
|
|
15
|
+
}
|
|
16
|
+
mark(stage) {
|
|
17
|
+
const at = this.now();
|
|
18
|
+
if (stage === 'startup_submitted')
|
|
19
|
+
this.promptAt = at;
|
|
20
|
+
this.log(`startup stage=${stage} elapsed_ms=${Math.max(0, at - this.started)}`);
|
|
21
|
+
}
|
|
22
|
+
observe(event) {
|
|
23
|
+
if (this.promptAt === undefined || this.completed || event.source === 'agent_replay')
|
|
24
|
+
return;
|
|
25
|
+
if (event.kind === 'prompt.started' && event.source === 'startup')
|
|
26
|
+
this.promptId = event.promptId;
|
|
27
|
+
if (this.promptId && event.promptId && event.promptId !== this.promptId)
|
|
28
|
+
return;
|
|
29
|
+
if (event.kind === 'tool.upsert' && 'toolCallId' in event.payload && event.payload.toolCallId)
|
|
30
|
+
this.tools.add(event.payload.toolCallId);
|
|
31
|
+
if (!this.firstOutput && ['message.chunk', 'message.replace'].includes(event.kind)
|
|
32
|
+
&& 'role' in event.payload && event.payload.role === 'assistant'
|
|
33
|
+
&& 'content' in event.payload && event.payload.content.type === 'text'
|
|
34
|
+
&& event.payload.content.text.trim()) {
|
|
35
|
+
this.firstOutput = true;
|
|
36
|
+
const at = this.now();
|
|
37
|
+
this.log(`startup stage=first_assistant_output elapsed_ms=${Math.max(0, at - this.started)} model_and_tools_ms=${Math.max(0, at - this.promptAt)} tools=${this.tools.size}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
finish() {
|
|
41
|
+
if (this.completed)
|
|
42
|
+
return;
|
|
43
|
+
this.completed = true;
|
|
44
|
+
this.log(`startup stage=turn_settled elapsed_ms=${Math.max(0, this.now() - this.started)} tools=${this.tools.size} first_output=${this.firstOutput}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin adapters over the official CLIs. Nothing here reads or returns a token
|
|
3
|
+
* value: credentials stay in files the CLIs own, and secrets typed by the user
|
|
4
|
+
* (a Claude paste code) travel only over a child's stdin, never argv or logs.
|
|
5
|
+
*/
|
|
6
|
+
export interface SubscriptionBinaries {
|
|
7
|
+
claude: string;
|
|
8
|
+
codex: string;
|
|
9
|
+
/** `script(1)`, used to give `claude auth login` the terminal it expects. */
|
|
10
|
+
script: string;
|
|
11
|
+
}
|
|
12
|
+
export declare const defaultBinaries: () => SubscriptionBinaries;
|
|
13
|
+
/**
|
|
14
|
+
* Child env for a profile helper: explicit home for added profiles, the CLI
|
|
15
|
+
* default otherwise. Every credential selector either CLI honours is removed,
|
|
16
|
+
* so a helper can only ever see the profile's own login.
|
|
17
|
+
*/
|
|
18
|
+
export declare function profileEnv(homeVar: string, profileId: string, home: string, base?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
19
|
+
export interface ClaudeStatus {
|
|
20
|
+
loggedIn: boolean;
|
|
21
|
+
email?: string;
|
|
22
|
+
plan?: string;
|
|
23
|
+
org?: string;
|
|
24
|
+
authMethod?: string;
|
|
25
|
+
}
|
|
26
|
+
/** `claude auth status --json` (documented: exit 0 when logged in, 1 otherwise). */
|
|
27
|
+
export declare function claudeAuthStatus(bin: string, env: NodeJS.ProcessEnv, timeoutMs?: number): Promise<ClaudeStatus>;
|
|
28
|
+
/**
|
|
29
|
+
* Login expiry timestamps from the Claude credentials file. Field names are
|
|
30
|
+
* observed (Claude Code 2.1.x), not a documented contract, so absence is normal.
|
|
31
|
+
* Only the two numbers are kept; the parsed object is discarded immediately.
|
|
32
|
+
*/
|
|
33
|
+
export declare function claudeLoginExpiry(home: string): {
|
|
34
|
+
accessExpiresAt?: number;
|
|
35
|
+
refreshExpiresAt?: number;
|
|
36
|
+
};
|
|
37
|
+
/** Minimal stdio JSON-RPC client for `codex app-server` (the supported transport). */
|
|
38
|
+
export declare class CodexAppServer {
|
|
39
|
+
private readonly child;
|
|
40
|
+
private id;
|
|
41
|
+
private buffer;
|
|
42
|
+
private readonly pending;
|
|
43
|
+
private readonly listeners;
|
|
44
|
+
private closed;
|
|
45
|
+
private constructor();
|
|
46
|
+
static start(bin: string, env: NodeJS.ProcessEnv, initializeTimeoutMs?: number): Promise<CodexAppServer>;
|
|
47
|
+
call<T = unknown>(method: string, params: unknown, timeoutMs?: number): Promise<T>;
|
|
48
|
+
onNotification(fn: (method: string, params: unknown) => void): () => void;
|
|
49
|
+
close(): void;
|
|
50
|
+
private onData;
|
|
51
|
+
}
|
|
52
|
+
/** One-shot use of an app-server for a profile. */
|
|
53
|
+
export declare function withCodex<T>(bin: string, env: NodeJS.ProcessEnv, fn: (s: CodexAppServer) => Promise<T>, initializeTimeoutMs?: number): Promise<T>;
|
|
54
|
+
/** What a Codex runtime offers the account it is signed in to. An empty list is an answer; unknown is not. */
|
|
55
|
+
export type OfferedModels = {
|
|
56
|
+
state: 'known';
|
|
57
|
+
models: string[];
|
|
58
|
+
} | {
|
|
59
|
+
state: 'unknown';
|
|
60
|
+
reason: string;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Ask this Codex executable, with this profile's environment, which models it offers (`model/list`,
|
|
64
|
+
* the call the ACP adapter itself uses). Nothing is inferred when it does not answer.
|
|
65
|
+
*/
|
|
66
|
+
export declare function codexOfferedModels(bin: string, env: NodeJS.ProcessEnv, budgetMs?: number): Promise<OfferedModels>;
|
|
67
|
+
export interface UsageWindow {
|
|
68
|
+
id: string;
|
|
69
|
+
label: string;
|
|
70
|
+
usedPercent: number;
|
|
71
|
+
windowMins?: number;
|
|
72
|
+
resetsAt?: string;
|
|
73
|
+
}
|
|
74
|
+
interface CodexWindow {
|
|
75
|
+
usedPercent?: number;
|
|
76
|
+
windowDurationMins?: number | null;
|
|
77
|
+
resetsAt?: number | null;
|
|
78
|
+
}
|
|
79
|
+
interface CodexSnapshot {
|
|
80
|
+
limitId?: string | null;
|
|
81
|
+
limitName?: string | null;
|
|
82
|
+
primary?: CodexWindow | null;
|
|
83
|
+
secondary?: CodexWindow | null;
|
|
84
|
+
}
|
|
85
|
+
/** Flatten `account/rateLimits/read` into display windows. */
|
|
86
|
+
export declare function codexUsageWindows(result: {
|
|
87
|
+
rateLimits?: CodexSnapshot;
|
|
88
|
+
rateLimitsByLimitId?: Record<string, CodexSnapshot | undefined> | null;
|
|
89
|
+
}): UsageWindow[];
|
|
90
|
+
export declare function windowName(mins?: number): string;
|
|
91
|
+
export {};
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { CONFLICTING_AUTH_ENV } from './launch.js';
|
|
5
|
+
import { DEFAULT_PROFILE_ID } from './store.js';
|
|
6
|
+
export const defaultBinaries = () => ({
|
|
7
|
+
claude: process.env.OURS_FLEET_CLAUDE_BIN ?? 'claude',
|
|
8
|
+
codex: process.env.OURS_FLEET_CODEX_BIN ?? 'codex',
|
|
9
|
+
script: 'script',
|
|
10
|
+
});
|
|
11
|
+
/**
|
|
12
|
+
* Child env for a profile helper: explicit home for added profiles, the CLI
|
|
13
|
+
* default otherwise. Every credential selector either CLI honours is removed,
|
|
14
|
+
* so a helper can only ever see the profile's own login.
|
|
15
|
+
*/
|
|
16
|
+
export function profileEnv(homeVar, profileId, home, base = process.env) {
|
|
17
|
+
const env = { ...base, BROWSER: 'true', NO_COLOR: '1' };
|
|
18
|
+
for (const key of [...CONFLICTING_AUTH_ENV.claude, ...CONFLICTING_AUTH_ENV.codex, 'ANTHROPIC_BASE_URL', 'OPENAI_BASE_URL'])
|
|
19
|
+
delete env[key];
|
|
20
|
+
if (profileId !== DEFAULT_PROFILE_ID)
|
|
21
|
+
env[homeVar] = home;
|
|
22
|
+
return env;
|
|
23
|
+
}
|
|
24
|
+
/** `claude auth status --json` (documented: exit 0 when logged in, 1 otherwise). */
|
|
25
|
+
export function claudeAuthStatus(bin, env, timeoutMs = 20_000) {
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
const child = spawn(bin, ['auth', 'status', '--json'], { env, stdio: ['ignore', 'pipe', 'ignore'] });
|
|
28
|
+
let out = '';
|
|
29
|
+
const timer = setTimeout(() => { child.kill('SIGKILL'); reject(new Error('claude auth status timed out')); }, timeoutMs);
|
|
30
|
+
child.stdout.on('data', d => { out += d; if (out.length > 64 * 1024)
|
|
31
|
+
child.kill('SIGKILL'); });
|
|
32
|
+
child.once('error', error => { clearTimeout(timer); reject(error); });
|
|
33
|
+
child.once('close', code => {
|
|
34
|
+
clearTimeout(timer);
|
|
35
|
+
let parsed = {};
|
|
36
|
+
try {
|
|
37
|
+
parsed = JSON.parse(out);
|
|
38
|
+
}
|
|
39
|
+
catch { /* non-JSON: rely on exit code */ }
|
|
40
|
+
const str = (v) => typeof v === 'string' && v ? v.slice(0, 120) : undefined;
|
|
41
|
+
resolve({
|
|
42
|
+
loggedIn: code === 0 && parsed.loggedIn !== false,
|
|
43
|
+
email: str(parsed.email), plan: str(parsed.subscriptionType), org: str(parsed.orgName),
|
|
44
|
+
authMethod: str(parsed.authMethod),
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Login expiry timestamps from the Claude credentials file. Field names are
|
|
51
|
+
* observed (Claude Code 2.1.x), not a documented contract, so absence is normal.
|
|
52
|
+
* Only the two numbers are kept; the parsed object is discarded immediately.
|
|
53
|
+
*/
|
|
54
|
+
export function claudeLoginExpiry(home) {
|
|
55
|
+
try {
|
|
56
|
+
const oauth = JSON.parse(readFileSync(join(home, '.credentials.json'), 'utf8')).claudeAiOauth;
|
|
57
|
+
const num = (v) => typeof v === 'number' && Number.isFinite(v) ? v : undefined;
|
|
58
|
+
return { accessExpiresAt: num(oauth?.expiresAt), refreshExpiresAt: num(oauth?.refreshTokenExpiresAt) };
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return {};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** Minimal stdio JSON-RPC client for `codex app-server` (the supported transport). */
|
|
65
|
+
export class CodexAppServer {
|
|
66
|
+
child;
|
|
67
|
+
id = 0;
|
|
68
|
+
buffer = '';
|
|
69
|
+
pending = new Map();
|
|
70
|
+
listeners = new Set();
|
|
71
|
+
closed = false;
|
|
72
|
+
constructor(child) {
|
|
73
|
+
this.child = child;
|
|
74
|
+
child.stdout.on('data', chunk => this.onData(String(chunk)));
|
|
75
|
+
child.stdin.on('error', () => { });
|
|
76
|
+
child.stderr.resume();
|
|
77
|
+
child.once('close', () => {
|
|
78
|
+
this.closed = true;
|
|
79
|
+
for (const p of this.pending.values())
|
|
80
|
+
p.reject(new Error('codex app-server exited'));
|
|
81
|
+
this.pending.clear();
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
static async start(bin, env, initializeTimeoutMs = 20_000) {
|
|
85
|
+
const child = spawn(bin, ['app-server'], { env, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
86
|
+
await new Promise((resolve, reject) => { child.once('spawn', resolve); child.once('error', reject); });
|
|
87
|
+
const server = new CodexAppServer(child);
|
|
88
|
+
// A server that does not answer is not handed out, so nobody else can close it: end it here.
|
|
89
|
+
try {
|
|
90
|
+
await server.call('initialize', { clientInfo: { name: 'ours-fleet', title: 'Ours Fleet', version: '1' } }, initializeTimeoutMs);
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
server.close();
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
child.stdin.write(`${JSON.stringify({ method: 'initialized' })}\n`);
|
|
97
|
+
return server;
|
|
98
|
+
}
|
|
99
|
+
call(method, params, timeoutMs = 20_000) {
|
|
100
|
+
if (this.closed)
|
|
101
|
+
return Promise.reject(new Error('codex app-server exited'));
|
|
102
|
+
const id = ++this.id;
|
|
103
|
+
return new Promise((resolve, reject) => {
|
|
104
|
+
const timer = setTimeout(() => { this.pending.delete(id); reject(new Error(`${method} timed out`)); }, timeoutMs);
|
|
105
|
+
this.pending.set(id, {
|
|
106
|
+
resolve: v => { clearTimeout(timer); resolve(v); },
|
|
107
|
+
reject: e => { clearTimeout(timer); reject(e); },
|
|
108
|
+
});
|
|
109
|
+
this.child.stdin.write(`${JSON.stringify({ id, method, params })}\n`);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
onNotification(fn) {
|
|
113
|
+
this.listeners.add(fn);
|
|
114
|
+
return () => this.listeners.delete(fn);
|
|
115
|
+
}
|
|
116
|
+
close() {
|
|
117
|
+
if (!this.closed)
|
|
118
|
+
this.child.kill();
|
|
119
|
+
}
|
|
120
|
+
onData(chunk) {
|
|
121
|
+
this.buffer += chunk;
|
|
122
|
+
for (let i = this.buffer.indexOf('\n'); i >= 0; i = this.buffer.indexOf('\n')) {
|
|
123
|
+
const line = this.buffer.slice(0, i).trim();
|
|
124
|
+
this.buffer = this.buffer.slice(i + 1);
|
|
125
|
+
if (!line)
|
|
126
|
+
continue;
|
|
127
|
+
let message;
|
|
128
|
+
try {
|
|
129
|
+
message = JSON.parse(line);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (typeof message.id === 'number' && this.pending.has(message.id) && !message.method) {
|
|
135
|
+
const p = this.pending.get(message.id);
|
|
136
|
+
this.pending.delete(message.id);
|
|
137
|
+
if (message.error)
|
|
138
|
+
p.reject(Object.assign(new Error(String(message.error.message ?? 'codex error').slice(0, 300)), { rpcCode: message.error.code }));
|
|
139
|
+
else
|
|
140
|
+
p.resolve(message.result);
|
|
141
|
+
}
|
|
142
|
+
else if (message.method) {
|
|
143
|
+
for (const fn of this.listeners)
|
|
144
|
+
fn(message.method, message.params);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/** One-shot use of an app-server for a profile. */
|
|
150
|
+
export async function withCodex(bin, env, fn, initializeTimeoutMs) {
|
|
151
|
+
const server = await CodexAppServer.start(bin, env, initializeTimeoutMs);
|
|
152
|
+
try {
|
|
153
|
+
return await fn(server);
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
server.close();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Ask this Codex executable, with this profile's environment, which models it offers (`model/list`,
|
|
161
|
+
* the call the ACP adapter itself uses). Nothing is inferred when it does not answer.
|
|
162
|
+
*/
|
|
163
|
+
export async function codexOfferedModels(bin, env, budgetMs = 30_000) {
|
|
164
|
+
// One budget for the whole question: onboarding waits for this answer.
|
|
165
|
+
const deadline = Date.now() + budgetMs;
|
|
166
|
+
const left = () => Math.max(1, deadline - Date.now());
|
|
167
|
+
try {
|
|
168
|
+
return await withCodex(bin, env, async (server) => {
|
|
169
|
+
const models = [];
|
|
170
|
+
let cursor = null;
|
|
171
|
+
while (Date.now() < deadline) {
|
|
172
|
+
const answer = await server.call('model/list', { cursor, limit: null }, left());
|
|
173
|
+
if (!Array.isArray(answer?.data))
|
|
174
|
+
return { state: 'unknown', reason: 'Codex did not return a model list' };
|
|
175
|
+
for (const model of answer.data)
|
|
176
|
+
if (model && typeof model.id === 'string' && model.id && model.hidden !== true)
|
|
177
|
+
models.push(model.id);
|
|
178
|
+
cursor = typeof answer.nextCursor === 'string' && answer.nextCursor ? answer.nextCursor : null;
|
|
179
|
+
if (!cursor)
|
|
180
|
+
return { state: 'known', models: [...new Set(models)] };
|
|
181
|
+
}
|
|
182
|
+
return { state: 'unknown', reason: 'Codex did not finish listing its models in time' };
|
|
183
|
+
}, left());
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
return { state: 'unknown', reason: error instanceof Error ? error.message : String(error) };
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/** Flatten `account/rateLimits/read` into display windows. */
|
|
190
|
+
export function codexUsageWindows(result) {
|
|
191
|
+
const snapshots = result.rateLimitsByLimitId && Object.keys(result.rateLimitsByLimitId).length
|
|
192
|
+
? Object.entries(result.rateLimitsByLimitId) : [['codex', result.rateLimits]];
|
|
193
|
+
const windows = [];
|
|
194
|
+
for (const [limitId, snap] of snapshots) {
|
|
195
|
+
if (!snap)
|
|
196
|
+
continue;
|
|
197
|
+
for (const [slot, w] of [['primary', snap.primary], ['secondary', snap.secondary]]) {
|
|
198
|
+
if (!w || typeof w.usedPercent !== 'number')
|
|
199
|
+
continue;
|
|
200
|
+
windows.push({
|
|
201
|
+
id: `${limitId}:${slot}`,
|
|
202
|
+
label: `${snap.limitName ?? limitId} · ${windowName(w.windowDurationMins ?? undefined)}`,
|
|
203
|
+
usedPercent: w.usedPercent,
|
|
204
|
+
windowMins: w.windowDurationMins ?? undefined,
|
|
205
|
+
resetsAt: typeof w.resetsAt === 'number' ? new Date(w.resetsAt * 1000).toISOString() : undefined,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return windows;
|
|
210
|
+
}
|
|
211
|
+
export function windowName(mins) {
|
|
212
|
+
if (!mins)
|
|
213
|
+
return 'window';
|
|
214
|
+
if (mins === 300)
|
|
215
|
+
return '5h';
|
|
216
|
+
if (mins === 10_080)
|
|
217
|
+
return 'weekly';
|
|
218
|
+
if (mins % 1440 === 0)
|
|
219
|
+
return `${mins / 1440}d`;
|
|
220
|
+
if (mins % 60 === 0)
|
|
221
|
+
return `${mins / 60}h`;
|
|
222
|
+
return `${mins}m`;
|
|
223
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type SubscriptionPin, type SubscriptionProvider } from './store.js';
|
|
2
|
+
/** The subscription provider a harness authenticates with, if Fleet manages it. */
|
|
3
|
+
export declare function providerForHarness(harness: string): SubscriptionProvider | undefined;
|
|
4
|
+
/** Env var that selects the CLI home for each provider. */
|
|
5
|
+
export declare const HOME_ENV: Record<SubscriptionProvider, string>;
|
|
6
|
+
/**
|
|
7
|
+
* Credential sources that would silently outrank the fleet-wide active profile
|
|
8
|
+
* (see Claude Code's documented authentication precedence and Codex's
|
|
9
|
+
* API-key login). While a non-default profile is active they are refused.
|
|
10
|
+
*/
|
|
11
|
+
export declare const CONFLICTING_AUTH_ENV: Record<SubscriptionProvider, string[]>;
|
|
12
|
+
/** Claude managed-settings file (Linux), which outranks user settings. */
|
|
13
|
+
export declare const CLAUDE_MANAGED_SETTINGS = "/etc/claude-code/managed-settings.json";
|
|
14
|
+
/**
|
|
15
|
+
* Competing Claude credential sources declared in settings files: an `env`
|
|
16
|
+
* block entry from CONFLICTING_AUTH_ENV, or an `apiKeyHelper`. Settings are
|
|
17
|
+
* shared into profiles, so they can silently outrank a profile's login.
|
|
18
|
+
*/
|
|
19
|
+
export declare function claudeSettingsAuthSources(home: string, managed?: string): string[];
|
|
20
|
+
export interface LaunchPin {
|
|
21
|
+
pin: SubscriptionPin;
|
|
22
|
+
/** Added to the harness child env last. Empty for the adopted default. */
|
|
23
|
+
env: Record<string, string>;
|
|
24
|
+
/** Host path the sandbox must mount read-write, if any. */
|
|
25
|
+
writablePath?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Resolve the provider's active profile for a launch and record it in the
|
|
29
|
+
* agent's state dir. Resolution happens once, here: a later switch never
|
|
30
|
+
* changes what this session uses.
|
|
31
|
+
*/
|
|
32
|
+
export declare function pinSubscriptionForLaunch(role: {
|
|
33
|
+
name: string;
|
|
34
|
+
harness: string;
|
|
35
|
+
env?: Record<string, string>;
|
|
36
|
+
auth_proxy?: unknown;
|
|
37
|
+
}, stateDir: string, inherited?: NodeJS.ProcessEnv, now?: () => Date): LaunchPin | undefined;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { FleetError } from '../application/errors.js';
|
|
4
|
+
import { DEFAULT_PROFILE_ID, profileHome, readSubscriptionState, writePin, } from './store.js';
|
|
5
|
+
/** The subscription provider a harness authenticates with, if Fleet manages it. */
|
|
6
|
+
export function providerForHarness(harness) {
|
|
7
|
+
if (harness === 'claude-code')
|
|
8
|
+
return 'claude';
|
|
9
|
+
if (harness === 'codex')
|
|
10
|
+
return 'codex';
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
/** Env var that selects the CLI home for each provider. */
|
|
14
|
+
export const HOME_ENV = {
|
|
15
|
+
claude: 'CLAUDE_CONFIG_DIR',
|
|
16
|
+
codex: 'CODEX_HOME',
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Credential sources that would silently outrank the fleet-wide active profile
|
|
20
|
+
* (see Claude Code's documented authentication precedence and Codex's
|
|
21
|
+
* API-key login). While a non-default profile is active they are refused.
|
|
22
|
+
*/
|
|
23
|
+
export const CONFLICTING_AUTH_ENV = {
|
|
24
|
+
claude: [
|
|
25
|
+
'CLAUDE_CONFIG_DIR', 'ANTHROPIC_API_KEY', 'ANTHROPIC_AUTH_TOKEN', 'CLAUDE_CODE_OAUTH_TOKEN',
|
|
26
|
+
'CLAUDE_CODE_OAUTH_REFRESH_TOKEN', 'CLAUDE_CODE_OAUTH_TOKEN_FILE_DESCRIPTOR',
|
|
27
|
+
'CLAUDE_CODE_USE_BEDROCK', 'CLAUDE_CODE_USE_VERTEX', 'CLAUDE_CODE_USE_FOUNDRY', 'ANTHROPIC_PROFILE',
|
|
28
|
+
'ANTHROPIC_FEDERATION_RULE_ID', 'ANTHROPIC_ORGANIZATION_ID', 'ANTHROPIC_IDENTITY_TOKEN_FILE',
|
|
29
|
+
],
|
|
30
|
+
codex: ['CODEX_HOME', 'OPENAI_API_KEY', 'CODEX_API_KEY'],
|
|
31
|
+
};
|
|
32
|
+
/** Claude managed-settings file (Linux), which outranks user settings. */
|
|
33
|
+
export const CLAUDE_MANAGED_SETTINGS = '/etc/claude-code/managed-settings.json';
|
|
34
|
+
/**
|
|
35
|
+
* Competing Claude credential sources declared in settings files: an `env`
|
|
36
|
+
* block entry from CONFLICTING_AUTH_ENV, or an `apiKeyHelper`. Settings are
|
|
37
|
+
* shared into profiles, so they can silently outrank a profile's login.
|
|
38
|
+
*/
|
|
39
|
+
export function claudeSettingsAuthSources(home, managed = CLAUDE_MANAGED_SETTINGS) {
|
|
40
|
+
const found = [];
|
|
41
|
+
for (const file of [join(home, 'settings.json'), managed]) {
|
|
42
|
+
let settings;
|
|
43
|
+
try {
|
|
44
|
+
settings = JSON.parse(readFileSync(file, 'utf8'));
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
for (const key of CONFLICTING_AUTH_ENV.claude)
|
|
50
|
+
if (settings?.env && typeof settings.env === 'object' && key in settings.env)
|
|
51
|
+
found.push(`${key} (${file})`);
|
|
52
|
+
if (settings?.apiKeyHelper)
|
|
53
|
+
found.push(`apiKeyHelper (${file})`);
|
|
54
|
+
}
|
|
55
|
+
return found;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Resolve the provider's active profile for a launch and record it in the
|
|
59
|
+
* agent's state dir. Resolution happens once, here: a later switch never
|
|
60
|
+
* changes what this session uses.
|
|
61
|
+
*/
|
|
62
|
+
export function pinSubscriptionForLaunch(role, stateDir, inherited = process.env, now = () => new Date()) {
|
|
63
|
+
const provider = providerForHarness(role.harness);
|
|
64
|
+
// A loopback auth proxy supplies credentials itself; it is not managed here.
|
|
65
|
+
if (!provider || role.auth_proxy)
|
|
66
|
+
return undefined;
|
|
67
|
+
const profileId = readSubscriptionState().providers[provider].activeProfileId;
|
|
68
|
+
const pin = { provider, profileId, pinnedAt: now().toISOString() };
|
|
69
|
+
if (profileId === DEFAULT_PROFILE_ID) {
|
|
70
|
+
// The adopted default launches exactly as before; credential env that points
|
|
71
|
+
// elsewhere is recorded so the panel can say this session is not managed.
|
|
72
|
+
const unmanaged = CONFLICTING_AUTH_ENV[provider].filter(key => role.env?.[key] !== undefined || inherited[key]);
|
|
73
|
+
if (unmanaged.length)
|
|
74
|
+
pin.unmanaged = unmanaged;
|
|
75
|
+
writePin(stateDir, pin);
|
|
76
|
+
return { pin, env: {} };
|
|
77
|
+
}
|
|
78
|
+
const conflicts = CONFLICTING_AUTH_ENV[provider].filter(key => role.env?.[key] !== undefined || (key !== HOME_ENV[provider] && inherited[key]));
|
|
79
|
+
if (conflicts.length) {
|
|
80
|
+
throw new FleetError('invalid_request', `role '${role.name}': ${conflicts.join(', ')} would bypass the active ${provider} subscription profile; ` +
|
|
81
|
+
'remove it from the role env / supervisor environment or switch back to the default profile');
|
|
82
|
+
}
|
|
83
|
+
const home = profileHome(provider, profileId);
|
|
84
|
+
if (provider === 'claude') {
|
|
85
|
+
const settings = claudeSettingsAuthSources(home);
|
|
86
|
+
if (settings.length) {
|
|
87
|
+
throw new FleetError('invalid_request', `role '${role.name}': Claude settings declare ${settings.join(', ')}, which would bypass the active subscription profile`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
pin.home = home;
|
|
91
|
+
writePin(stateDir, pin);
|
|
92
|
+
return { pin, env: { [HOME_ENV[provider]]: home }, writablePath: home };
|
|
93
|
+
}
|