@moretea-labs/forge 1.8.0 → 1.8.1
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/AGENTS.md +70 -7
- package/CHANGELOG.md +14 -0
- package/CLAUDE.md +17 -2
- package/CONTRIBUTING.md +3 -2
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/adapters/chatgpt/browser-delivery-host.ts +7 -1
- package/adapters/chatgpt/browser-delivery-runtime.ts +529 -56
- package/adapters/chatgpt/controller-binding-store.ts +101 -22
- package/adapters/chatgpt/controller-round-host.ts +15 -25
- package/adapters/chatgpt/controller-round-settlement-store.ts +58 -4
- package/adapters/chatgpt/provider-delivery.ts +228 -1
- package/adapters/chatgpt/work-conversation-binding-store.ts +79 -53
- package/adapters/computer/chatgpt-extension-target.ts +463 -0
- package/adapters/computer/chatgpt-target-router.ts +103 -0
- package/adapters/computer/chrome-extension/background.js +385 -0
- package/adapters/computer/chrome-extension/content.js +378 -0
- package/adapters/computer/chrome-extension/core.js +24 -0
- package/adapters/computer/chrome-extension/manifest.json +30 -0
- package/adapters/computer/desktop-operator-discovery.ts +11 -31
- package/adapters/computer/interaction-target-authority.ts +142 -18
- package/adapters/mcp/auth.ts +0 -1
- package/adapters/mcp/instructions.ts +5 -4
- package/adapters/mcp/multi-repository.ts +3 -19
- package/adapters/mcp/oauth.ts +73 -0
- package/adapters/mcp/paths.ts +4 -1
- package/adapters/mcp/runtime-gateway/automation-receipt-adapter.ts +87 -0
- package/adapters/mcp/runtime-gateway/bounded-result-runtime.ts +62 -0
- package/adapters/mcp/runtime-gateway/cognition-settlement.ts +201 -0
- package/adapters/mcp/runtime-gateway/context-adapter.ts +411 -73
- package/adapters/mcp/runtime-gateway/context-pack-sidecar.ts +91 -0
- package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +20 -5
- package/adapters/mcp/runtime-gateway/core-capability-adapter.ts +258 -0
- package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +10 -27
- package/adapters/mcp/runtime-gateway/engineering-tool-contract.ts +15 -0
- package/adapters/mcp/runtime-gateway/execution-tools.ts +8 -42
- package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +2 -1
- package/adapters/mcp/runtime-gateway/plugin-adapter.ts +117 -45
- package/adapters/mcp/runtime-gateway/process-tools.ts +190 -22
- package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +16 -4
- package/adapters/mcp/runtime-gateway/recovery-adapter.ts +69 -52
- package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +4 -35
- package/adapters/mcp/runtime-gateway/result-adapter.ts +10 -4
- package/adapters/mcp/runtime-gateway/router.ts +18 -504
- package/adapters/mcp/runtime-gateway/routing-policy.ts +499 -0
- package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +14 -65
- package/adapters/mcp/runtime-gateway/runtime-readiness-observation.ts +218 -0
- package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +113 -173
- package/adapters/mcp/runtime-gateway/runtime-tools.ts +15 -5
- package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +1 -0
- package/adapters/mcp/runtime-gateway/shared-adapter.ts +20 -0
- package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +358 -266
- package/adapters/mcp/runtime-gateway/typescript-navigation-sidecar.ts +136 -0
- package/adapters/mcp/runtime-gateway/work-adapter.ts +261 -3122
- package/adapters/mcp/runtime-gateway/work-controller-operations.ts +257 -0
- package/adapters/mcp/runtime-gateway/work-detail-projection.ts +60 -0
- package/adapters/mcp/runtime-gateway/work-input-wire-migration.ts +246 -0
- package/adapters/mcp/runtime-gateway/work-learning-operations.ts +145 -0
- package/adapters/mcp/runtime-gateway/work-list-projection.ts +38 -0
- package/adapters/mcp/runtime-gateway/work-plan-operations.ts +382 -0
- package/adapters/mcp/runtime-gateway/work-repair-adapter.ts +308 -0
- package/adapters/mcp/runtime-gateway/work-requirement-operations.ts +148 -0
- package/adapters/mcp/runtime-gateway/work-semantic-operations.ts +263 -0
- package/adapters/mcp/runtime-gateway/work-workflow-operations.ts +113 -0
- package/adapters/mcp/runtime-gateway/workflow-supervisor-adapter.ts +156 -0
- package/adapters/mcp/server.ts +67 -23
- package/adapters/mcp/setup.ts +1 -3
- package/adapters/mcp/tool-mapping/legacy-tool-service.ts +44 -170
- package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +4 -9
- package/adapters/mcp/tool-mapping/repository-tools.ts +133 -142
- package/adapters/mcp/toolset-names.ts +5 -0
- package/adapters/mcp/toolset.ts +6 -5
- package/adapters/mcp/transports/http-observation.ts +365 -0
- package/adapters/mcp/transports/http.ts +154 -677
- package/adapters/mcp/transports/oauth-http.ts +322 -0
- package/adapters/mcp/transports/session-registry.ts +6 -0
- package/adapters/mcp/tunnels/openai-secure-tunnel.ts +89 -5
- package/assets/plugin-registry.v1.json +6 -4
- package/assets/reference-configs/agentic-development-flow.md +25 -0
- package/assets/reference-configs/global-working-rules.md +19 -0
- package/assets/skill-version.json +2 -2
- package/docs/architecture/CURRENT.md +111 -41
- package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +14 -5
- package/docs/architecture/decisions/20260823-immediate-controller-relay.md +6 -3
- package/docs/architecture/decisions/20260907-assistant-learning-loop.md +2 -0
- package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +19 -4
- package/docs/architecture/decisions/20260916-workflow-supervisor-authority.md +249 -0
- package/docs/architecture/decisions/20260917-cognitive-memory-foundation.md +82 -0
- package/docs/architecture/decisions/20260918-stable-candidate-release-lifecycle.md +221 -0
- package/docs/architecture/decisions/20260919-autonomous-work-liveness.md +62 -0
- package/docs/architecture/decisions/20260924-thin-semantic-working-context.md +72 -0
- package/docs/architecture/decisions/20261001-supervisor-turn-authority.md +237 -0
- package/docs/architecture/decisions/20261005-forge-v3-desktop-client-boundary.md +77 -0
- package/docs/architecture/domains/verification.md +1 -0
- package/docs/architecture/domains/workflow-engine.md +6 -0
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
- package/docs/forge-plugin-management.md +2 -2
- package/docs/operations/controller-performance-and-502.md +17 -0
- package/docs/operations/controller-reliability-runbook.md +2 -2
- package/docs/operations/features.md +1 -1
- package/docs/operations/features.zh-CN.md +1 -1
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/mcp-tool-exposure.md +2 -0
- package/docs/operations/releasing.md +2 -0
- package/docs/operations/standalone-disaster-recovery.md +14 -6
- package/docs/wiki/Implementation.md +1 -1
- package/package.json +25 -14
- package/packages/kernel/cognition/api/index.ts +6 -0
- package/packages/kernel/cognition/application/activation-service.ts +451 -0
- package/packages/kernel/cognition/application/consolidation-service.ts +185 -0
- package/packages/kernel/cognition/application/legacy-experience-adapter.ts +38 -0
- package/packages/kernel/cognition/application/memory-service.ts +64 -0
- package/packages/kernel/cognition/domain/learning.ts +106 -0
- package/packages/kernel/cognition/domain/memory.ts +254 -0
- package/packages/kernel/controller/api/index.ts +1 -0
- package/packages/kernel/controller/application/continuation-service.ts +281 -0
- package/packages/kernel/controller/application/controller-round-service.ts +12 -0
- package/packages/kernel/controller/domain/controller-round-transition-policy.ts +242 -21
- package/packages/kernel/controller/domain/controller-round.ts +4 -1
- package/packages/kernel/controller/domain/execution-quality.ts +3 -1
- package/packages/kernel/controller/infrastructure/controller-round-store.ts +689 -103
- package/packages/kernel/controller/infrastructure/controller-session-store.ts +297 -71
- package/packages/kernel/controller/ports/controller-host.ts +2 -0
- package/packages/kernel/identity/api/index.ts +10 -0
- package/packages/kernel/identity/domain/activity-projection.ts +43 -0
- package/packages/kernel/identity/domain/capability-broker.ts +60 -0
- package/packages/kernel/identity/domain/grant.ts +72 -0
- package/packages/kernel/identity/domain/owned-resource.ts +116 -0
- package/packages/kernel/identity/domain/user-request.ts +93 -0
- package/packages/kernel/identity/infrastructure/activity-projection-store.ts +77 -0
- package/packages/kernel/identity/infrastructure/capability-broker.ts +81 -0
- package/packages/kernel/identity/infrastructure/grant-store.ts +284 -0
- package/packages/kernel/identity/infrastructure/owned-resource-store.ts +259 -0
- package/packages/kernel/identity/infrastructure/user-request-store.ts +182 -0
- package/packages/kernel/scheduler/api/index.ts +0 -2
- package/packages/kernel/scheduler/application/schedule-service.ts +3 -0
- package/packages/kernel/scheduler/application/settlement.ts +0 -15
- package/packages/kernel/scheduler/domain/schedule.ts +87 -0
- package/packages/kernel/scheduler/infrastructure/schedule-store.ts +153 -133
- package/packages/kernel/work/api/index.ts +3 -2
- package/packages/kernel/work/application/work-service.ts +17 -42
- package/packages/kernel/work/domain/current-task-lineage.ts +104 -0
- package/packages/kernel/work/domain/engineering-design.ts +10 -5
- package/packages/kernel/work/domain/engineering-profile.ts +8 -12
- package/packages/kernel/work/domain/evaluation-promotion-receipt.ts +186 -0
- package/packages/kernel/work/domain/execution-concurrency.ts +19 -26
- package/packages/kernel/work/domain/execution-snapshot.ts +0 -31
- package/packages/kernel/work/domain/implementation-review.ts +14 -331
- package/packages/kernel/work/domain/objective-graph.ts +119 -0
- package/packages/kernel/work/domain/project-engineering-contract.ts +23 -1
- package/packages/kernel/work/domain/repository-completion-receipt.ts +1 -1
- package/packages/kernel/work/domain/state-machine.ts +25 -63
- package/packages/kernel/work/domain/types.ts +57 -51
- package/packages/kernel/work/infrastructure/work-contract-store.ts +819 -728
- package/packages/kernel/work/ports/work-contract-store.ts +5 -2
- package/packages/plugin-runtime/browser/session-authority.ts +2 -1
- package/packages/plugin-runtime/computer/chatgpt-identity.ts +24 -0
- package/packages/plugin-runtime/computer/conversation-target.ts +143 -0
- package/packages/plugin-runtime/computer/index.ts +2 -0
- package/packages/plugin-runtime/computer/target-authority.ts +26 -1
- package/packages/plugin-runtime/external/unix-jsonl-transport.ts +142 -18
- package/packages/protocols/computer/contract.ts +27 -1
- package/scripts/activate-source-baseline.ts +7 -9
- package/scripts/canary-controller-semantic-wait.ts +12 -24
- package/scripts/check-mcp-compatibility.ts +48 -136
- package/scripts/check-runtime-architecture.mjs +622 -217
- package/scripts/forge-local-recovery-helper.mjs +93 -15
- package/scripts/lib/project-init-lib.sh +3 -0
- package/scripts/open-source-audit-allowlist.txt +1 -2
- package/scripts/run-bounded-command.mjs +5 -0
- package/scripts/run-governed-gate.ts +0 -1
- package/scripts/run-mobile-monitor-bridge.ts +0 -1
- package/scripts/smoke-control-plane-failure-domain.ts +94 -0
- package/scripts/smoke-mcp-http-runtime.ts +5 -0
- package/scripts/smoke-runtime-recovery.ts +12 -43
- package/scripts/smoke-schedule-engine.ts +23 -15
- package/scripts/smoke-unattended-transport-recovery.ts +223 -0
- package/scripts/smoke-user-request-continuation-restart.ts +232 -0
- package/scripts/smoke-workflow-supervisor-chrome-adapter.ts +92 -0
- package/scripts/smoke-workflow-supervisor-process-restart.ts +276 -0
- package/scripts/smoke-workflow-supervisor-restart-recovery.ts +150 -0
- package/scripts/smoke-workflow-supervisor.ts +103 -0
- package/scripts/verify-forge-runtime.sh +2 -1
- package/src/cli/agent-jobs/executor-health.ts +11 -34
- package/src/cli/agent-jobs/integration.ts +18 -49
- package/src/cli/agent-jobs/job-manager.ts +7 -3
- package/src/cli/commands/brain-assistant.ts +27 -13
- package/src/cli/commands/brain.ts +13 -15
- package/src/cli/commands/chatgpt.ts +1 -163
- package/src/cli/commands/recovery.ts +135 -121
- package/src/cli/commands/runtime.ts +3 -3
- package/src/cli/commands/setup-profile.ts +8 -0
- package/src/cli/controller/check-runner.ts +13 -2
- package/src/cli/controller/completion-backlog.ts +3 -3
- package/src/cli/controller/completion-orchestrator.ts +4 -43
- package/src/cli/controller/completion-target.ts +3 -72
- package/src/cli/controller/context/source-materializer.ts +81 -100
- package/src/cli/controller/context-pack.ts +10 -8
- package/src/cli/controller/execution-completion.ts +4 -7
- package/src/cli/controller/execution-policy.ts +36 -153
- package/src/cli/controller/governance.ts +2 -3
- package/src/cli/controller/issue-store.ts +4 -99
- package/src/cli/controller/launch-agents.ts +59 -18
- package/src/cli/controller/operational-plan.ts +24 -51
- package/src/cli/controller/runtime-config.ts +5 -1
- package/src/cli/controller/stuck-state-migration.ts +2 -3
- package/src/cli/controller/systemd-user.ts +39 -1
- package/src/cli/editing/edit-session.ts +1 -7
- package/src/cli/local-bridge/console-automations.ts +18 -13
- package/src/cli/local-bridge/console-view-models.ts +3 -12
- package/src/cli/local-bridge/facade-api.ts +64 -191
- package/src/cli/local-bridge/server.ts +24 -147
- package/src/cli/local-bridge/types.ts +0 -1
- package/src/cli/mcp/oauth-client-retirement.ts +74 -0
- package/src/cli/repositories/command-executor.ts +29 -23
- package/src/cli/repositories/command-scope.ts +35 -16
- package/src/cli/repositories/controller-home.ts +48 -0
- package/src/cli/repositories/ephemeral-workspace.ts +20 -30
- package/src/cli/repositories/git-command-executor.ts +2 -2
- package/src/cli/repositories/registry.ts +77 -11
- package/src/cli/repositories/structured-git.ts +62 -2
- package/src/cli/repository/session-cache.ts +92 -6
- package/src/runtime/context/assistant-context.ts +20 -7
- package/src/runtime/context/assistant-work-context.ts +89 -27
- package/src/runtime/context/automatic-learning.ts +565 -0
- package/src/runtime/context/cognitive-attention.ts +173 -0
- package/src/runtime/context/cognitive-skill-canary.ts +158 -0
- package/src/runtime/context/context-closure.ts +46 -9
- package/src/runtime/context/context-pack-process.ts +103 -0
- package/src/runtime/context/context-plane.ts +1 -9
- package/src/runtime/context/direct-controller-learning.ts +298 -0
- package/src/runtime/context/semantic-navigation-contract.ts +9 -0
- package/src/runtime/context/semantic-navigation.ts +6 -34
- package/src/runtime/context/typescript-navigation-process.ts +145 -0
- package/src/runtime/context/typescript-navigation.ts +89 -3
- package/src/runtime/control-plane/cleanup-artifact-retention.ts +212 -9
- package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +9 -47
- package/src/runtime/control-plane/execution/controller-authority-recovery.ts +34 -4
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +29 -20
- package/src/runtime/control-plane/execution/execution-identity.ts +23 -3
- package/src/runtime/control-plane/execution/repository-work-attribution.ts +7 -30
- package/src/runtime/control-plane/execution/retained-work-resume.ts +206 -199
- package/src/runtime/control-plane/execution/session-store.ts +31 -1
- package/src/runtime/control-plane/execution/validation.ts +6 -8
- package/src/runtime/control-plane/execution/work-authority-reconciler.ts +13 -42
- package/src/runtime/control-plane/execution/work-check-evidence.ts +137 -0
- package/src/runtime/control-plane/execution/work-completion-authority.ts +30 -85
- package/src/runtime/control-plane/execution/work-delivery-service.ts +497 -0
- package/src/runtime/control-plane/execution/work-evidence-policy.ts +12 -50
- package/src/runtime/control-plane/execution/work-execution-support.ts +50 -149
- package/src/runtime/control-plane/execution/work-handle-authority.ts +188 -109
- package/src/runtime/control-plane/execution/work-handle-store.ts +19 -43
- package/src/runtime/control-plane/execution/work-head-settlement.ts +53 -20
- package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +52 -29
- package/src/runtime/control-plane/execution/work-operation-service.ts +43 -27
- package/src/runtime/control-plane/execution/work-preparation-service.ts +126 -266
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +2 -3
- package/src/runtime/control-plane/execution/work-revision-diff.ts +39 -0
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +173 -186
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +28 -84
- package/src/runtime/control-plane/execution/work-verification-service.ts +406 -50
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +5 -1
- package/src/runtime/control-plane/facade/capability-registry.ts +362 -37
- package/src/runtime/control-plane/facade/check-normalization.ts +3 -9
- package/src/runtime/control-plane/facade/codex-delegation.ts +10 -11
- package/src/runtime/control-plane/facade/controller-connection.ts +135 -0
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +1 -20
- package/src/runtime/control-plane/facade/goal-workloop.ts +281 -1753
- package/src/runtime/control-plane/facade/handoff-inbox-application.ts +12 -25
- package/src/runtime/control-plane/facade/handoff-inbox-store.ts +256 -52
- package/src/runtime/control-plane/facade/index.ts +0 -1
- package/src/runtime/control-plane/facade/operation-digest.ts +12 -15
- package/src/runtime/control-plane/facade/plan-contract-store.ts +451 -991
- package/src/runtime/control-plane/facade/policy-gate.ts +0 -23
- package/src/runtime/control-plane/facade/repository-work-admission.ts +12 -22
- package/src/runtime/control-plane/facade/requirement-authority.ts +102 -199
- package/src/runtime/control-plane/facade/requirement-board.ts +3 -3
- package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +51 -14
- package/src/runtime/control-plane/facade/self-healing-loop.ts +18 -53
- package/src/runtime/control-plane/facade/suggested-actions.ts +15 -18
- package/src/runtime/control-plane/facade/types.ts +49 -108
- package/src/runtime/control-plane/facade/work-continuation.ts +8 -8
- package/src/runtime/control-plane/generated-cache-retention.ts +97 -1
- package/src/runtime/control-plane/global-scheduler/autonomous-continuation.ts +248 -0
- package/src/runtime/control-plane/global-scheduler/durable-admission.ts +1 -1
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +239 -112
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +2 -1
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +66 -40
- package/src/runtime/control-plane/global-scheduler/wake-signal.ts +42 -2
- package/src/runtime/control-plane/governance/access-policy.ts +19 -2
- package/src/runtime/control-plane/governance/authorization.ts +20 -3
- package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +63 -300
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +28 -300
- package/src/runtime/control-plane/launcher/local-conversation-controller.ts +238 -0
- package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +5 -2
- package/src/runtime/control-plane/launcher/thin-launcher.ts +96 -240
- package/src/runtime/control-plane/persistence/cognition-store.ts +820 -0
- package/src/runtime/control-plane/persistence/experience-store.ts +162 -29
- package/src/runtime/control-plane/persistence/requirement-store.ts +216 -74
- package/src/runtime/control-plane/persistence/sqlite-store.ts +113 -6
- package/src/runtime/control-plane/release-retention.ts +135 -47
- package/src/runtime/control-plane/repository-namespace-retention.ts +2 -2
- package/src/runtime/control-plane/runtime-cleanup.ts +290 -21
- package/src/runtime/control-plane/workspace/project-onboarding.ts +80 -0
- package/src/runtime/control-plane/workspace/semantic-sync-service.ts +19 -5
- package/src/runtime/control-plane/workspace/workspace-store.ts +169 -0
- package/src/runtime/diagnostics/incident-repair.ts +313 -146
- package/src/runtime/diagnostics/mcp-timing.ts +114 -0
- package/src/runtime/evidence/artifact-store.ts +158 -40
- package/src/runtime/evidence/evidence-store.ts +112 -8
- package/src/runtime/evidence/output-handle-index.ts +225 -0
- package/src/runtime/evidence/result-store.ts +158 -44
- package/src/runtime/execution/jobs/store.ts +3 -4
- package/src/runtime/execution/managed-workspace.ts +57 -0
- package/src/runtime/execution/process-runtime/check-facade.ts +1 -1
- package/src/runtime/execution/process-runtime/check-result-retention.ts +2 -2
- package/src/runtime/execution/process-runtime/check-result.ts +105 -1
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +18 -8
- package/src/runtime/execution/process-runtime/command-facade.ts +79 -27
- package/src/runtime/execution/process-runtime/gc.ts +15 -2
- package/src/runtime/execution/process-runtime/handle-index.ts +176 -0
- package/src/runtime/execution/process-runtime/host-command-lane.ts +139 -0
- package/src/runtime/execution/process-runtime/index.ts +3 -0
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +251 -72
- package/src/runtime/execution/process-runtime/persisted-check.ts +3 -3
- package/src/runtime/execution/process-runtime/process-runner-entry.ts +47 -5
- package/src/runtime/execution/process-runtime/process-scope.ts +160 -0
- package/src/runtime/execution/process-runtime/resource-claims.ts +65 -61
- package/src/runtime/execution/process-runtime/runtime.ts +35 -2
- package/src/runtime/execution/process-runtime/store.ts +30 -2
- package/src/runtime/execution/process-runtime/types.ts +6 -1
- package/src/runtime/execution/workers/executor.ts +2 -6
- package/src/runtime/platform/service-inventory.ts +130 -0
- package/src/runtime/platform/service-manager.ts +8 -0
- package/src/runtime/plugins/action-application.ts +85 -28
- package/src/runtime/plugins/app-store-connect-adapter.ts +38 -14
- package/src/runtime/plugins/browser-adapter.ts +757 -34
- package/src/runtime/plugins/browser-automation-service.ts +23 -3
- package/src/runtime/plugins/browser-macos-bridge.ts +321 -29
- package/src/runtime/plugins/browser-manifest-surface.ts +45 -11
- package/src/runtime/plugins/browser-native-session-service.ts +63 -13
- package/src/runtime/plugins/browser-registration.ts +16 -0
- package/src/runtime/plugins/browser-runtime.ts +14 -1
- package/src/runtime/plugins/browser-session-authority.ts +2 -2
- package/src/runtime/plugins/browser-session-store.ts +46 -76
- package/src/runtime/plugins/capability-authorization-grants.ts +258 -62
- package/src/runtime/plugins/computer-chatgpt-browser-adapter-installer.ts +219 -0
- package/src/runtime/plugins/computer-chatgpt-macos-target.ts +891 -0
- package/src/runtime/plugins/computer-registration.ts +222 -11
- package/src/runtime/plugins/desktop-operator-external-policy.ts +36 -4
- package/src/runtime/plugins/desktop-operator-registration.ts +12 -4
- package/src/runtime/plugins/external-adapter.ts +42 -6
- package/src/runtime/plugins/external-registration.ts +21 -1
- package/src/runtime/plugins/gmail-adapter.ts +23 -2
- package/src/runtime/plugins/google-calendar-adapter.ts +2 -2
- package/src/runtime/plugins/google-shared.ts +14 -7
- package/src/runtime/plugins/google-tasks-adapter.ts +2 -2
- package/src/runtime/plugins/ios-adapter.ts +1 -1
- package/src/runtime/plugins/lightweight-action.ts +208 -2
- package/src/runtime/plugins/local-system-adapter.ts +12 -2
- package/src/runtime/plugins/managed-process-adapter.ts +12 -5
- package/src/runtime/plugins/plugin-action-sidecar.ts +17 -13
- package/src/runtime/plugins/plugin-management-adapter.ts +1 -1
- package/src/runtime/plugins/store.ts +602 -428
- package/src/runtime/plugins/types.ts +20 -0
- package/src/runtime/plugins/xiaohongshu-publish.ts +16 -0
- package/src/runtime/projections/invalidation.ts +7 -3
- package/src/runtime/projections/materialized-view.ts +80 -36
- package/src/runtime/recovery/actions.ts +3 -3
- package/src/runtime/recovery/capability-action-application.ts +46 -45
- package/src/runtime/recovery/lifecycle-inventory.ts +29 -29
- package/src/runtime/recovery/maintenance-executor.ts +374 -123
- package/src/runtime/recovery/store.ts +29 -11
- package/src/runtime/release/release-coordinator.ts +296 -0
- package/src/runtime/release/release-session.ts +716 -0
- package/src/runtime/release/release-state-migration.ts +25 -0
- package/src/runtime/resources/leases/store.ts +56 -25
- package/src/runtime/root/computer-composition.ts +191 -6
- package/src/runtime/root/controller-progression-composition.ts +192 -0
- package/src/runtime/root/controller-round-composition.ts +20 -1
- package/src/runtime/root/deployment-topology.ts +106 -0
- package/src/runtime/root/entry.ts +4 -3
- package/src/runtime/root/known-good-recovery.ts +184 -0
- package/src/runtime/root/local-bridge.ts +0 -1
- package/src/runtime/root/mcp-transport.ts +111 -18
- package/src/runtime/root/ownership.ts +261 -9
- package/src/runtime/root/package-connector-service.ts +105 -19
- package/src/runtime/root/package-runtime-release.ts +117 -18
- package/src/runtime/root/package-runtime-service.ts +17 -11
- package/src/runtime/root/release-execution-canary.ts +44 -12
- package/src/runtime/root/release-loader.ts +90 -8
- package/src/runtime/root/release-manifest.ts +80 -6
- package/src/runtime/root/release-materialize.ts +372 -28
- package/src/runtime/root/release-store.ts +395 -36
- package/src/runtime/root/runtime-lane.ts +191 -0
- package/src/runtime/root/runtime.ts +159 -27
- package/src/runtime/root/scheduled-controller-composition.ts +28 -3
- package/src/runtime/root/service-runner.ts +2 -2
- package/src/runtime/root/service.ts +36 -33
- package/src/runtime/root/status.ts +48 -0
- package/src/runtime/root/types.ts +32 -3
- package/src/runtime/root/workflow-supervisor-composition.ts +493 -0
- package/src/runtime/root/workflow-supervisor-runtime.ts +209 -0
- package/src/runtime/root/write-fence.ts +50 -2
- package/src/runtime/safe-tooling/google-credential-store.ts +76 -21
- package/src/runtime/safe-tooling/google-oauth-broker.ts +25 -7
- package/src/runtime/safe-tooling/workspace-auth.ts +1 -1
- package/src/runtime/shared/bounded-child-supervisor.ts +9 -1
- package/src/runtime/shared/process-environment.ts +20 -9
- package/src/runtime/shared/response-budget.ts +1 -3
- package/src/runtime/standalone-recovery/controller-home-migration.ts +16 -24
- package/src/runtime/standalone-recovery/core.ts +3702 -342
- package/src/runtime/standalone-recovery/entry.ts +469 -39
- package/src/runtime/standalone-recovery/installer.ts +262 -148
- package/src/runtime/standalone-recovery/mcp-server.ts +29 -193
- package/src/runtime/standalone-recovery/operation-lock.ts +1 -1
- package/src/runtime/standalone-recovery/performance.ts +54 -16
- package/src/runtime/standalone-recovery/release.ts +53 -5
- package/src/runtime/standalone-recovery/service-labels.ts +5 -2
- package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +5 -2
- package/src/runtime/workflow/schedules/browser-probe.ts +17 -6
- package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +10 -3
- package/src/runtime/workflow/schedules/engine.ts +138 -172
- package/src/runtime/workflow/schedules/work-continuation.ts +44 -5
- package/src/runtime/workflows/runtime.ts +15 -19
- package/src/testing/test-governance.ts +5 -1
- package/supervisor/chatgpt-conversation.ts +23 -0
- package/supervisor/client.ts +201 -0
- package/supervisor/control-plane.ts +868 -0
- package/supervisor/entry.ts +15 -0
- package/supervisor/forge-validators.ts +80 -0
- package/supervisor/index.ts +5 -0
- package/supervisor/native-browser-adapter.ts +786 -0
- package/supervisor/native-messaging/host.ts +39 -0
- package/supervisor/native-messaging/manifest.ts +26 -0
- package/supervisor/native-messaging/protocol.ts +27 -0
- package/supervisor/paths.ts +18 -0
- package/supervisor/protocol.ts +202 -0
- package/supervisor/server.ts +404 -0
- package/supervisor/store.ts +1451 -0
- package/supervisor/tsconfig.json +8 -0
- package/supervisor/types.ts +255 -0
- package/adapters/chatgpt/controller-host.ts +0 -119
- package/adapters/mcp/controller-round-compatibility.ts +0 -184
- package/adapters/mcp/frozen-client-semantic-compatibility.ts +0 -292
- package/adapters/mcp/runtime-gateway/work-compat-adapter.ts +0 -502
- package/packages/kernel/progression/api/index.ts +0 -2
- package/packages/kernel/progression/application/projector.ts +0 -211
- package/packages/kernel/progression/domain/types.ts +0 -116
- package/packages/kernel/progression/index.ts +0 -1
- package/packages/kernel/scheduler/application/continuation-service.ts +0 -212
- package/packages/kernel/scheduler/domain/continuation.ts +0 -38
- package/packages/kernel/scheduler/infrastructure/continuation-dispatch-store.ts +0 -38
- package/packages/kernel/work/domain/admission-policy.ts +0 -34
- package/scripts/benchmark-background-check-overlap.ts +0 -228
- package/scripts/benchmark-computer-hotpath.ts +0 -255
- package/scripts/benchmark-controller-hotpath.ts +0 -212
- package/scripts/benchmark-controller-round2.ts +0 -191
- package/scripts/benchmark-ios-agent-device.ts +0 -511
- package/scripts/benchmark-mcp-host.sh +0 -117
- package/scripts/benchmark-mcp-transport.ts +0 -169
- package/scripts/benchmark-operational-memory-activation.ts +0 -235
- package/scripts/benchmark-operational-shadow.ts +0 -28
- package/scripts/benchmark-quality-harness.ts +0 -146
- package/scripts/benchmark-route-session-concurrency.ts +0 -915
- package/scripts/benchmark-thin-harness-gateway-ab.ts +0 -632
- package/scripts/benchmark-thin-harness-mcp-e2e.ts +0 -498
- package/scripts/benchmark-thin-harness.ts +0 -251
- package/scripts/benchmark-typescript-navigation.ts +0 -186
- package/scripts/benchmark-work-efficiency.ts +0 -334
- package/scripts/build-controller-ui.ts +0 -28
- package/scripts/check-npm-release.sh +0 -32
- package/scripts/check-public-release-surface.sh +0 -19
- package/scripts/check-release-published.sh +0 -57
- package/scripts/check-release-readiness.sh +0 -37
- package/scripts/check-release-version.mjs +0 -74
- package/scripts/check-tarball-install-smoke.sh +0 -150
- package/scripts/package-source-archive.sh +0 -147
- package/scripts/public-release-files.txt +0 -39
- package/scripts/public-release.ts +0 -174
- package/scripts/publish-release-tarball.sh +0 -27
- package/scripts/route-nl-vs-ts-eval.ts +0 -679
- package/scripts/stage-runtime-release.ts +0 -44
- package/src/cli/controller/progress.ts +0 -656
- package/src/cli/controller/work-mode.ts +0 -181
- package/src/cli/local-bridge/console-assets.ts +0 -17
- package/src/cli/local-bridge/dashboard.ts +0 -17
- package/src/cli/local-bridge/frontend/src/App.tsx +0 -10
- package/src/cli/local-bridge/frontend/src/components/Button.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/components/DetailPane.tsx +0 -3
- package/src/cli/local-bridge/frontend/src/components/SearchField.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/components/SectionHeader.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/components/Segmented.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/components/StatusText.tsx +0 -3
- package/src/cli/local-bridge/frontend/src/design/icons.tsx +0 -11
- package/src/cli/local-bridge/frontend/src/design/tokens.css +0 -8
- package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +0 -153
- package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +0 -37
- package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +0 -183
- package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +0 -2
- package/src/cli/local-bridge/frontend/src/features/system/SystemView.tsx +0 -2
- package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +0 -4
- package/src/cli/local-bridge/frontend/src/lib/api.ts +0 -30
- package/src/cli/local-bridge/frontend/src/lib/format.ts +0 -4
- package/src/cli/local-bridge/frontend/src/lib/routes.ts +0 -7
- package/src/cli/local-bridge/frontend/src/main.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/shell/AppShell.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +0 -2
- package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +0 -4
- package/src/cli/local-bridge/frontend/src/styles.css +0 -28
- package/src/cli/local-bridge/frontend/src/types.ts +0 -15
- package/src/cli/local-bridge/frontend/src/vite-env.d.ts +0 -1
- package/src/cli/local-bridge/frontend/vite.config.ts +0 -27
- package/src/cli/local-bridge/ui-dist/app.css +0 -1
- package/src/cli/local-bridge/ui-dist/app.js +0 -11228
- package/src/runtime/control-plane/execution/content-equivalent-commit-authority.ts +0 -169
- package/src/runtime/control-plane/execution/direct-canonical-work-reconciliation.ts +0 -85
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +0 -655
- package/src/runtime/control-plane/execution/work-finalization-service.ts +0 -2850
- package/src/runtime/control-plane/execution/work-path-scope.ts +0 -37
- package/src/runtime/control-plane/execution/work-task-receipt.ts +0 -468
- package/src/runtime/control-plane/facade/controller-round-relay.ts +0 -3
- package/src/runtime/control-plane/facade/work-admission-policy.ts +0 -142
- package/src/runtime/control-plane/facade/work-implementation-review.ts +0 -2
- package/src/runtime/control-plane/facade/work-state-machine.ts +0 -2
- package/src/runtime/control-plane/facade/work-task-projection.ts +0 -66
- package/src/runtime/control-plane/routing/index.ts +0 -1
- package/src/runtime/control-plane/routing/route-policy.ts +0 -401
- package/src/runtime/control-plane/routing/workspace-admission.ts +0 -70
|
@@ -26,13 +26,15 @@ import {
|
|
|
26
26
|
installSystemdUserUnit,
|
|
27
27
|
systemdUserAvailable,
|
|
28
28
|
systemdUserServicePid,
|
|
29
|
+
systemdUserUnitName,
|
|
30
|
+
systemdUserUnitPath,
|
|
29
31
|
type SystemdUserUnitInput,
|
|
30
32
|
} from '../../cli/controller/systemd-user';
|
|
31
33
|
import { FORGE_VERSION } from '../../version';
|
|
32
|
-
import { initializeStandaloneRecovery, loadRecoveryConfig, type LaunchdPrimaryConnectorServiceConfig, type LaunchdPublicTunnelServiceConfig, type PrimaryConnectorServiceConfig, type PrimaryRuntimeServiceConfig, type PublicTunnelServiceConfig, type RecoveryConfig, type RecoveryTunnelServiceConfig } from './core';
|
|
33
|
-
import { RECOVERY_GATEWAY_LABEL, RECOVERY_WATCHDOG_LABEL } from './service-labels';
|
|
34
|
+
import { initializeStandaloneRecovery, loadRecoveryConfig, normalizeRecoveryInstallProfile, recoveryInstallProfileRoles, type LaunchdPrimaryConnectorServiceConfig, type LaunchdPublicTunnelServiceConfig, type PrimaryConnectorServiceConfig, type PrimaryRuntimeServiceConfig, type PublicTunnelServiceConfig, type RecoveryConfig, type RecoveryInstallProfile, type RecoveryTunnelServiceConfig } from './core';
|
|
35
|
+
import { RECOVERY_DAEMON_LABEL, RECOVERY_GATEWAY_LABEL, RECOVERY_WATCHDOG_LABEL } from './service-labels';
|
|
34
36
|
import { acquireRecoveryOperationLock } from './operation-lock';
|
|
35
|
-
export { RECOVERY_GATEWAY_LABEL, RECOVERY_WATCHDOG_LABEL } from './service-labels';
|
|
37
|
+
export { RECOVERY_DAEMON_LABEL, RECOVERY_GATEWAY_LABEL, RECOVERY_WATCHDOG_LABEL } from './service-labels';
|
|
36
38
|
import {
|
|
37
39
|
RECOVERY_RELEASE_BINARIES,
|
|
38
40
|
RECOVERY_RELEASE_ROLE_CANARY_ARG,
|
|
@@ -46,6 +48,7 @@ import {
|
|
|
46
48
|
recoveryPreviousPath,
|
|
47
49
|
recoveryReleasesRoot,
|
|
48
50
|
recoveryRoot,
|
|
51
|
+
setRecoveryGatewayDraining,
|
|
49
52
|
writeRecoveryReleaseManifest,
|
|
50
53
|
type RecoveryReleaseDescriptor,
|
|
51
54
|
type RecoveryReleaseManifest,
|
|
@@ -77,7 +80,7 @@ export function retireStaleRecoveryLaunchAgents(
|
|
|
77
80
|
): string[] {
|
|
78
81
|
const generatedRoot = join(recoveryRoot(controllerHome), 'launchd');
|
|
79
82
|
if (!existsSync(generatedRoot)) return [];
|
|
80
|
-
const activeLabels = new Set([
|
|
83
|
+
const activeLabels = new Set([RECOVERY_DAEMON_LABEL]);
|
|
81
84
|
const retired: string[] = [];
|
|
82
85
|
for (const name of readdirSync(generatedRoot)) {
|
|
83
86
|
if (!name.endsWith('.plist')) continue;
|
|
@@ -130,6 +133,7 @@ export interface RecoveryActivationVerification {
|
|
|
130
133
|
ok: boolean;
|
|
131
134
|
expectedReleaseRevision: string;
|
|
132
135
|
failures: string[];
|
|
136
|
+
daemonPid?: number;
|
|
133
137
|
gatewayPid?: number;
|
|
134
138
|
watchdogPid?: number;
|
|
135
139
|
healthStatus?: number;
|
|
@@ -150,10 +154,7 @@ export interface RecoveryActivationResult {
|
|
|
150
154
|
previous?: RecoveryReleaseDescriptor;
|
|
151
155
|
migratedLegacy?: RecoveryReleaseDescriptor;
|
|
152
156
|
noOp?: boolean;
|
|
153
|
-
handoff?:
|
|
154
|
-
gateway: RecoveryServiceHandoffResult;
|
|
155
|
-
watchdog: RecoveryServiceHandoffResult;
|
|
156
|
-
};
|
|
157
|
+
handoff?: Partial<Record<RecoveryRuntimeRole, RecoveryServiceHandoffResult>>;
|
|
157
158
|
verification: RecoveryActivationVerification;
|
|
158
159
|
rollback?: {
|
|
159
160
|
release: RecoveryReleaseDescriptor;
|
|
@@ -163,6 +164,7 @@ export interface RecoveryActivationResult {
|
|
|
163
164
|
|
|
164
165
|
export interface RecoveryInstallResult {
|
|
165
166
|
controllerHome: string;
|
|
167
|
+
profile: RecoveryInstallProfile;
|
|
166
168
|
staged: StagedRecoveryRelease;
|
|
167
169
|
activated?: RecoveryActivationResult;
|
|
168
170
|
config: RecoveryConfig;
|
|
@@ -183,6 +185,7 @@ export interface RecoveryInstallerDependencies {
|
|
|
183
185
|
controllerHome: string;
|
|
184
186
|
config: RecoveryConfig;
|
|
185
187
|
expectedRelease: RecoveryReleaseDescriptor;
|
|
188
|
+
profile?: RecoveryInstallProfile;
|
|
186
189
|
timeoutMs?: number;
|
|
187
190
|
}) => Promise<RecoveryActivationVerification>;
|
|
188
191
|
currentPid?: (controllerHome: string, role: RecoveryRuntimeRole) => number | undefined;
|
|
@@ -307,8 +310,7 @@ export function stageRecoveryRelease(input: {
|
|
|
307
310
|
const runCanary = dependencies.runCanary ?? defaultRunCanary;
|
|
308
311
|
const canaries: Array<{ label: string; binaryPath: string; role?: RecoveryRuntimeRole }> = [
|
|
309
312
|
{ label: 'primary', binaryPath: primary },
|
|
310
|
-
{ label: '
|
|
311
|
-
{ label: 'watchdog', binaryPath: join(staging, 'forge-recovery-watchdog'), role: 'watchdog' },
|
|
313
|
+
{ label: 'daemon', binaryPath: primary, role: 'daemon' },
|
|
312
314
|
];
|
|
313
315
|
const canaryDetails: string[] = [];
|
|
314
316
|
for (const canary of canaries) {
|
|
@@ -393,9 +395,9 @@ export function recoverySystemdUserUnitInput(
|
|
|
393
395
|
env: NodeJS.ProcessEnv = process.env,
|
|
394
396
|
productVersion: string = FORGE_VERSION,
|
|
395
397
|
): SystemdUserUnitInput {
|
|
396
|
-
const binary = role === 'gateway' ? 'forge-recovery-gateway' : 'forge-recovery-watchdog';
|
|
398
|
+
const binary = role === 'daemon' ? 'forge-recovery' : role === 'gateway' ? 'forge-recovery-gateway' : 'forge-recovery-watchdog';
|
|
397
399
|
return {
|
|
398
|
-
description: role === 'gateway' ? 'Forge Standalone Recovery Gateway' : 'Forge Standalone Recovery Watchdog',
|
|
400
|
+
description: role === 'daemon' ? 'Forge Standalone Recovery' : role === 'gateway' ? 'Forge Standalone Recovery Gateway (legacy)' : 'Forge Standalone Recovery Watchdog (legacy)',
|
|
399
401
|
executable: join(recoveryCurrentPath(controllerHome), binary),
|
|
400
402
|
args: [role, '--controller-home', resolve(controllerHome)],
|
|
401
403
|
environment: {
|
|
@@ -412,7 +414,11 @@ export function recoverySystemdUserUnitInput(
|
|
|
412
414
|
}
|
|
413
415
|
|
|
414
416
|
function recoverySystemdLabel(role: RecoveryRuntimeRole): string {
|
|
415
|
-
return role === 'gateway' ? RECOVERY_GATEWAY_LABEL : RECOVERY_WATCHDOG_LABEL;
|
|
417
|
+
return role === 'daemon' ? RECOVERY_DAEMON_LABEL : role === 'gateway' ? RECOVERY_GATEWAY_LABEL : RECOVERY_WATCHDOG_LABEL;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function recoveryRoleServesGateway(role: RecoveryRuntimeRole): boolean {
|
|
421
|
+
return role === 'daemon' || role === 'gateway';
|
|
416
422
|
}
|
|
417
423
|
|
|
418
424
|
function recoveryPlist(input: {
|
|
@@ -446,7 +452,7 @@ export function recoveryLaunchdServicePid(label: string): number | undefined {
|
|
|
446
452
|
}
|
|
447
453
|
|
|
448
454
|
export function recoveryLaunchdPid(role: RecoveryRuntimeRole): number | undefined {
|
|
449
|
-
return recoveryLaunchdServicePid(role
|
|
455
|
+
return recoveryLaunchdServicePid(recoverySystemdLabel(role));
|
|
450
456
|
}
|
|
451
457
|
|
|
452
458
|
function defaultCurrentPid(controllerHome: string, role: RecoveryRuntimeRole): number | undefined {
|
|
@@ -459,8 +465,11 @@ export async function verifyRecoveryReleaseActivation(input: {
|
|
|
459
465
|
controllerHome: string;
|
|
460
466
|
config: RecoveryConfig;
|
|
461
467
|
expectedRelease: RecoveryReleaseDescriptor;
|
|
468
|
+
profile?: RecoveryInstallProfile;
|
|
462
469
|
timeoutMs?: number;
|
|
463
470
|
}, dependencies: Pick<RecoveryInstallerDependencies, 'servicePid' | 'platform' | 'systemdEnv'> = {}): Promise<RecoveryActivationVerification> {
|
|
471
|
+
const profile = normalizeRecoveryInstallProfile(input.profile ?? input.config.installProfile, 'self-healing');
|
|
472
|
+
const selectedRoles = new Set(recoveryInstallProfileRoles(profile));
|
|
464
473
|
const deadline = Date.now() + (input.timeoutMs ?? 60_000);
|
|
465
474
|
let last: RecoveryActivationVerification = {
|
|
466
475
|
ok: false,
|
|
@@ -473,8 +482,6 @@ export async function verifyRecoveryReleaseActivation(input: {
|
|
|
473
482
|
if (!current || current.releasePath !== input.expectedRelease.releasePath || current.manifestSha256 !== input.expectedRelease.manifestSha256) {
|
|
474
483
|
failures.push('Recovery current authority does not match expected release');
|
|
475
484
|
}
|
|
476
|
-
const gateway = readRecoveryRuntimeIdentity(input.controllerHome, 'gateway');
|
|
477
|
-
const watchdog = readRecoveryRuntimeIdentity(input.controllerHome, 'watchdog');
|
|
478
485
|
const observedPids: Partial<Record<RecoveryRuntimeRole, number>> = {};
|
|
479
486
|
const platform = dependencies.platform ?? process.platform;
|
|
480
487
|
const servicePid = dependencies.servicePid ?? ((_controllerHome: string, role: RecoveryRuntimeRole) => (
|
|
@@ -482,8 +489,13 @@ export async function verifyRecoveryReleaseActivation(input: {
|
|
|
482
489
|
? systemdUserServicePid(recoverySystemdLabel(role), dependencies.systemdEnv ?? process.env)
|
|
483
490
|
: recoveryLaunchdPid(role)
|
|
484
491
|
));
|
|
485
|
-
for (const
|
|
492
|
+
for (const role of ['daemon', 'gateway', 'watchdog'] as const) {
|
|
486
493
|
const managedPid = servicePid(input.controllerHome, role);
|
|
494
|
+
if (!selectedRoles.has(role)) {
|
|
495
|
+
if (managedPid) failures.push(`${role} service remains active outside Recovery profile ${profile}`);
|
|
496
|
+
continue;
|
|
497
|
+
}
|
|
498
|
+
const identity = readRecoveryRuntimeIdentity(input.controllerHome, role);
|
|
487
499
|
if (!managedPid) failures.push(`${role} service-owner PID is unavailable`);
|
|
488
500
|
else observedPids[role] = managedPid;
|
|
489
501
|
if (input.expectedRelease.legacy) continue;
|
|
@@ -497,27 +509,30 @@ export async function verifyRecoveryReleaseActivation(input: {
|
|
|
497
509
|
}
|
|
498
510
|
}
|
|
499
511
|
let healthStatus: number | undefined;
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
if (
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
if (
|
|
512
|
+
if ([...selectedRoles].some(recoveryRoleServesGateway)) {
|
|
513
|
+
try {
|
|
514
|
+
const response = await fetch(`http://${input.config.gateway?.host ?? '127.0.0.1'}:${input.config.gateway?.port ?? 8787}/health`, {
|
|
515
|
+
signal: AbortSignal.timeout(3_000),
|
|
516
|
+
});
|
|
517
|
+
healthStatus = response.status;
|
|
518
|
+
const health = await response.json() as { status?: unknown; version?: unknown; releaseRevision?: unknown; manifestSha256?: unknown };
|
|
519
|
+
if (response.status !== 200 || health.status !== 'ok') failures.push(`gateway health HTTP ${response.status}`);
|
|
520
|
+
if (input.expectedRelease.productVersion && health.version !== input.expectedRelease.productVersion) failures.push('gateway health product version mismatch');
|
|
521
|
+
if (!input.expectedRelease.legacy || health.releaseRevision !== undefined) {
|
|
522
|
+
if (health.releaseRevision !== input.expectedRelease.releaseRevision) failures.push('gateway health release revision mismatch');
|
|
523
|
+
}
|
|
524
|
+
if (!input.expectedRelease.legacy || health.manifestSha256 !== undefined) {
|
|
525
|
+
if (health.manifestSha256 !== input.expectedRelease.manifestSha256) failures.push('gateway health manifest hash mismatch');
|
|
526
|
+
}
|
|
527
|
+
} catch (error) {
|
|
528
|
+
failures.push(`gateway health unavailable: ${error instanceof Error ? error.message : String(error)}`);
|
|
513
529
|
}
|
|
514
|
-
} catch (error) {
|
|
515
|
-
failures.push(`gateway health unavailable: ${error instanceof Error ? error.message : String(error)}`);
|
|
516
530
|
}
|
|
517
531
|
last = {
|
|
518
532
|
ok: failures.length === 0,
|
|
519
533
|
expectedReleaseRevision: input.expectedRelease.releaseRevision,
|
|
520
534
|
failures,
|
|
535
|
+
daemonPid: observedPids.daemon,
|
|
521
536
|
gatewayPid: observedPids.gateway,
|
|
522
537
|
watchdogPid: observedPids.watchdog,
|
|
523
538
|
healthStatus,
|
|
@@ -528,128 +543,208 @@ export async function verifyRecoveryReleaseActivation(input: {
|
|
|
528
543
|
return last;
|
|
529
544
|
}
|
|
530
545
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
546
|
+
export const RECOVERY_RELEASE_LOCK_WAIT_MS = 10 * 60_000;
|
|
547
|
+
export const RECOVERY_RELEASE_LOCK_POLL_MS = 2_000;
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* An explicit operator release install must not fail merely because the Recovery
|
|
551
|
+
* daemon is inside its own bounded autonomous mutation (release step, tunnel
|
|
552
|
+
* repair, watchdog escalation). Wait for the single writer to finish, then take
|
|
553
|
+
* the same mutation lock; the lock still guarantees exactly one writer, and the
|
|
554
|
+
* bounded wait still fails closed if the holder never releases.
|
|
555
|
+
*/
|
|
556
|
+
export async function acquireRecoveryReleaseLock(
|
|
557
|
+
controllerHome: string,
|
|
558
|
+
dependencies: {
|
|
559
|
+
acquire?: typeof acquireRecoveryOperationLock;
|
|
560
|
+
sleep?: (ms: number) => Promise<void>;
|
|
561
|
+
now?: () => number;
|
|
562
|
+
waitMs?: number;
|
|
563
|
+
report?: (detail: string) => void;
|
|
564
|
+
} = {},
|
|
565
|
+
): Promise<{ path: string; instanceId: string; close: () => void }> {
|
|
566
|
+
const acquire = dependencies.acquire ?? acquireRecoveryOperationLock;
|
|
567
|
+
const sleep = dependencies.sleep ?? ((ms: number) => new Promise<void>((resolveSleep) => setTimeout(resolveSleep, ms)));
|
|
568
|
+
const now = dependencies.now ?? Date.now;
|
|
569
|
+
const report = dependencies.report ?? ((detail: string) => process.stderr.write(detail));
|
|
570
|
+
const deadline = now() + (dependencies.waitMs ?? RECOVERY_RELEASE_LOCK_WAIT_MS);
|
|
571
|
+
let reported = false;
|
|
572
|
+
for (;;) {
|
|
573
|
+
const attempt = acquire({
|
|
574
|
+
controllerHome,
|
|
575
|
+
action: 'install_recovery_release',
|
|
576
|
+
instanceIdPrefix: 'recovery-release-',
|
|
577
|
+
});
|
|
578
|
+
if (attempt.acquired) {
|
|
579
|
+
return {
|
|
580
|
+
path: attempt.handle.path,
|
|
581
|
+
instanceId: attempt.handle.record.instanceId,
|
|
582
|
+
close: attempt.handle.close,
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
if (now() >= deadline) {
|
|
586
|
+
throw new Error(`RECOVERY_OPERATION_LOCK_BUSY: ${attempt.owner.action} pid=${attempt.owner.pid} acquiredAt=${attempt.owner.acquiredAt}`);
|
|
587
|
+
}
|
|
588
|
+
if (!reported) {
|
|
589
|
+
report(
|
|
590
|
+
`recovery release install waiting for in-flight Recovery mutation: action=${attempt.owner.action} pid=${attempt.owner.pid} acquiredAt=${attempt.owner.acquiredAt}\n`,
|
|
591
|
+
);
|
|
592
|
+
reported = true;
|
|
593
|
+
}
|
|
594
|
+
await sleep(RECOVERY_RELEASE_LOCK_POLL_MS);
|
|
595
|
+
}
|
|
543
596
|
}
|
|
544
597
|
|
|
545
|
-
|
|
598
|
+
function recoveryRoleLabel(role: RecoveryRuntimeRole): string {
|
|
599
|
+
return recoverySystemdLabel(role);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function retireRecoveryRoleService(input: {
|
|
546
603
|
controllerHome: string;
|
|
547
|
-
|
|
548
|
-
expectedRelease: RecoveryReleaseDescriptor;
|
|
604
|
+
role: RecoveryRuntimeRole;
|
|
549
605
|
dependencies: RecoveryInstallerDependencies;
|
|
550
|
-
}):
|
|
606
|
+
}): void {
|
|
551
607
|
const platform = input.dependencies.platform ?? process.platform;
|
|
608
|
+
const label = recoveryRoleLabel(input.role);
|
|
552
609
|
if (platform === 'linux' && !input.dependencies.handoff) {
|
|
553
610
|
const env = input.dependencies.systemdEnv ?? process.env;
|
|
554
|
-
const
|
|
555
|
-
|
|
556
|
-
const
|
|
557
|
-
const gatewayPath = installUnit({
|
|
558
|
-
unitName: RECOVERY_GATEWAY_LABEL,
|
|
559
|
-
unit: recoverySystemdUserUnitInput(input.controllerHome, 'gateway', env, recoveryReleaseProductVersion(input.expectedRelease)),
|
|
611
|
+
const unit = systemdUserUnitName(label);
|
|
612
|
+
const unitPath = systemdUserUnitPath(unit, env);
|
|
613
|
+
const disabled = runProcess('systemctl', ['--user', 'disable', '--now', unit], {
|
|
560
614
|
env,
|
|
561
|
-
|
|
615
|
+
timeoutMs: 30_000,
|
|
616
|
+
maxOutputBytes: 64 * 1024,
|
|
562
617
|
});
|
|
563
|
-
const
|
|
564
|
-
|
|
565
|
-
unit:
|
|
618
|
+
const detail = `${disabled.stderr}\n${disabled.stdout}`;
|
|
619
|
+
if (!disabled.ok && !/not loaded|not found|does not exist|not enabled|no files found/i.test(detail)) {
|
|
620
|
+
throw new Error(`RECOVERY_SYSTEMD_RETIRE_FAILED: ${unit}: ${detail.trim()}`);
|
|
621
|
+
}
|
|
622
|
+
rmSync(unitPath, { force: true });
|
|
623
|
+
const reload = runProcess('systemctl', ['--user', 'daemon-reload'], {
|
|
566
624
|
env,
|
|
567
|
-
|
|
625
|
+
timeoutMs: 30_000,
|
|
626
|
+
maxOutputBytes: 64 * 1024,
|
|
568
627
|
});
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
});
|
|
575
|
-
if (!verification.ok) throw new Error(`RECOVERY_RELEASE_VERIFICATION_FAILED: ${verification.failures.join('; ')}`);
|
|
576
|
-
return {
|
|
577
|
-
gateway: {
|
|
578
|
-
platform: 'systemd-user',
|
|
579
|
-
serviceRegistered: true,
|
|
580
|
-
pidReady: Boolean(verification.gatewayPid),
|
|
581
|
-
portReady: verification.healthStatus === 200,
|
|
582
|
-
servicePath: gatewayPath,
|
|
583
|
-
pid: verification.gatewayPid,
|
|
584
|
-
},
|
|
585
|
-
watchdog: {
|
|
586
|
-
platform: 'systemd-user',
|
|
587
|
-
serviceRegistered: true,
|
|
588
|
-
pidReady: Boolean(verification.watchdogPid),
|
|
589
|
-
portReady: true,
|
|
590
|
-
servicePath: watchdogPath,
|
|
591
|
-
pid: verification.watchdogPid,
|
|
592
|
-
},
|
|
593
|
-
verification,
|
|
594
|
-
};
|
|
628
|
+
if (!reload.ok) throw new Error(`RECOVERY_SYSTEMD_DAEMON_RELOAD_FAILED: ${reload.stderr || reload.stdout}`);
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
if (platform !== 'darwin' && !input.dependencies.handoff) {
|
|
632
|
+
throw new Error(`RECOVERY_RELEASE_ACTIVATION_UNSUPPORTED_PLATFORM: ${platform}`);
|
|
595
633
|
}
|
|
596
|
-
if (platform !== 'darwin' && !input.dependencies.handoff) throw new Error(`RECOVERY_RELEASE_ACTIVATION_UNSUPPORTED_PLATFORM: ${platform}`);
|
|
597
634
|
const uid = (input.dependencies.uid ?? (() => typeof process.getuid === 'function' ? process.getuid() : Number.NaN))();
|
|
598
635
|
if (!Number.isInteger(uid)) throw new Error('RECOVERY_LAUNCHD_UID_UNAVAILABLE');
|
|
599
|
-
const
|
|
600
|
-
|
|
601
|
-
const
|
|
602
|
-
|
|
603
|
-
const
|
|
604
|
-
|
|
605
|
-
|
|
636
|
+
const runner = input.dependencies.launchctlRunner ?? defaultRecoveryLaunchctl;
|
|
637
|
+
const target = `gui/${uid}/${label}`;
|
|
638
|
+
const observed = runner(['print', target]);
|
|
639
|
+
if (observed.ok) {
|
|
640
|
+
const bootout = runner(['bootout', target]);
|
|
641
|
+
if (!bootout.ok && !bootoutAlreadyGone(`${bootout.stderr}\n${bootout.stdout}`)) {
|
|
642
|
+
throw new Error(`RECOVERY_SERVICE_RETIRE_FAILED: ${label}`);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
rmSync(join(recoveryRoot(input.controllerHome), 'launchd', `${label}.plist`), { force: true });
|
|
646
|
+
rmSync(launchAgentPath(label), { force: true });
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
async function handoffRecoveryServices(input: {
|
|
650
|
+
controllerHome: string;
|
|
651
|
+
config: RecoveryConfig;
|
|
652
|
+
expectedRelease: RecoveryReleaseDescriptor;
|
|
653
|
+
profile: RecoveryInstallProfile;
|
|
654
|
+
dependencies: RecoveryInstallerDependencies;
|
|
655
|
+
}): Promise<{ services: Partial<Record<RecoveryRuntimeRole, RecoveryServiceHandoffResult>>; verification: RecoveryActivationVerification }> {
|
|
656
|
+
const platform = input.dependencies.platform ?? process.platform;
|
|
657
|
+
const selectedRoles = new Set(recoveryInstallProfileRoles(input.profile));
|
|
658
|
+
for (const role of ['gateway', 'watchdog'] as const) {
|
|
659
|
+
if (!selectedRoles.has(role)) retireRecoveryRoleService({ controllerHome: input.controllerHome, role, dependencies: input.dependencies });
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
const services: Partial<Record<RecoveryRuntimeRole, RecoveryServiceHandoffResult>> = {};
|
|
663
|
+
if (platform === 'linux' && !input.dependencies.handoff) {
|
|
664
|
+
const env = input.dependencies.systemdEnv ?? process.env;
|
|
665
|
+
const available = input.dependencies.systemdAvailable ?? systemdUserAvailable;
|
|
666
|
+
if (selectedRoles.size > 0 && !available(env)) throw new Error('RECOVERY_SYSTEMD_USER_UNAVAILABLE');
|
|
667
|
+
const installUnit = input.dependencies.installSystemdUnit ?? installSystemdUserUnit;
|
|
668
|
+
for (const role of recoveryInstallProfileRoles(input.profile)) {
|
|
669
|
+
const servicePath = installUnit({
|
|
670
|
+
unitName: recoveryRoleLabel(role),
|
|
671
|
+
unit: recoverySystemdUserUnitInput(input.controllerHome, role, env, recoveryReleaseProductVersion(input.expectedRelease)),
|
|
672
|
+
env,
|
|
673
|
+
errorPrefix: 'RECOVERY_SYSTEMD_INSTALL_FAILED',
|
|
674
|
+
});
|
|
675
|
+
services[role] = {
|
|
676
|
+
platform: 'systemd-user',
|
|
677
|
+
serviceRegistered: true,
|
|
678
|
+
pidReady: false,
|
|
679
|
+
portReady: !recoveryRoleServesGateway(role),
|
|
680
|
+
servicePath,
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
} else {
|
|
684
|
+
if (platform !== 'darwin' && !input.dependencies.handoff) throw new Error(`RECOVERY_RELEASE_ACTIVATION_UNSUPPORTED_PLATFORM: ${platform}`);
|
|
685
|
+
const uid = (input.dependencies.uid ?? (() => typeof process.getuid === 'function' ? process.getuid() : Number.NaN))();
|
|
686
|
+
if (!Number.isInteger(uid)) throw new Error('RECOVERY_LAUNCHD_UID_UNAVAILABLE');
|
|
687
|
+
const domain = `gui/${uid}`;
|
|
688
|
+
retireStaleRecoveryLaunchAgents(input.controllerHome, uid, input.dependencies.launchctlRunner);
|
|
689
|
+
const installAgent = input.dependencies.installAgent ?? installLaunchAgent;
|
|
690
|
+
const currentPid = input.dependencies.currentPid ?? defaultCurrentPid;
|
|
691
|
+
const handoff = input.dependencies.handoff ?? safeLaunchdHandoff;
|
|
692
|
+
const generatedRoot = join(recoveryRoot(input.controllerHome), 'launchd');
|
|
693
|
+
const auditRoot = join(recoveryRoot(input.controllerHome), 'audit');
|
|
606
694
|
mkdirSync(generatedRoot, { recursive: true, mode: 0o700 });
|
|
607
695
|
mkdirSync(auditRoot, { recursive: true, mode: 0o700 });
|
|
608
|
-
const gatewayPath = join(generatedRoot, `${RECOVERY_GATEWAY_LABEL}.plist`);
|
|
609
|
-
const watchdogPath = join(generatedRoot, `${RECOVERY_WATCHDOG_LABEL}.plist`);
|
|
610
696
|
const productVersion = recoveryReleaseProductVersion(input.expectedRelease);
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
697
|
+
for (const role of recoveryInstallProfileRoles(input.profile)) {
|
|
698
|
+
const label = recoveryRoleLabel(role);
|
|
699
|
+
const generatedPath = join(generatedRoot, `${label}.plist`);
|
|
700
|
+
writeFileSync(generatedPath, recoveryPlist({
|
|
701
|
+
label,
|
|
702
|
+
executable: join(recoveryCurrentPath(input.controllerHome), role === 'daemon' ? 'forge-recovery' : role === 'gateway' ? 'forge-recovery-gateway' : 'forge-recovery-watchdog'),
|
|
703
|
+
command: role,
|
|
704
|
+
controllerHome: resolve(input.controllerHome),
|
|
705
|
+
logPath: join(auditRoot, `${role}.log`),
|
|
706
|
+
productVersion,
|
|
707
|
+
}), { mode: 0o600 });
|
|
708
|
+
const installedPath = installAgent(generatedPath, label).path;
|
|
709
|
+
const launched = await handoff({
|
|
710
|
+
label,
|
|
711
|
+
plistPath: installedPath,
|
|
712
|
+
domain,
|
|
713
|
+
oldPid: currentPid(input.controllerHome, role),
|
|
714
|
+
...(recoveryRoleServesGateway(role) ? { port: input.config.gateway?.port } : {}),
|
|
715
|
+
maxBootoutWaitMs: 20_000,
|
|
716
|
+
maxBootstrapRetry: 3,
|
|
717
|
+
});
|
|
718
|
+
if (!launched.serviceRegistered || !launched.pidWaitClean || (recoveryRoleServesGateway(role) && !launched.portWaitClean)) {
|
|
719
|
+
throw new Error(role === 'daemon' ? 'RECOVERY_DAEMON_HANDOFF_FAILED' : role === 'gateway' ? 'RECOVERY_GATEWAY_HANDOFF_FAILED' : 'RECOVERY_WATCHDOG_HANDOFF_FAILED');
|
|
720
|
+
}
|
|
721
|
+
services[role] = {
|
|
722
|
+
platform: 'launchd',
|
|
723
|
+
serviceRegistered: launched.serviceRegistered,
|
|
724
|
+
pidReady: launched.pidWaitClean,
|
|
725
|
+
portReady: recoveryRoleServesGateway(role) ? launched.portWaitClean : true,
|
|
726
|
+
servicePath: installedPath,
|
|
727
|
+
launchd: launched,
|
|
728
|
+
};
|
|
729
|
+
}
|
|
631
730
|
}
|
|
632
|
-
|
|
633
|
-
label: RECOVERY_WATCHDOG_LABEL,
|
|
634
|
-
plistPath: generated.watchdog,
|
|
635
|
-
domain,
|
|
636
|
-
oldPid: currentPid(input.controllerHome, 'watchdog'),
|
|
637
|
-
maxBootoutWaitMs: 20_000,
|
|
638
|
-
maxBootstrapRetry: 3,
|
|
639
|
-
});
|
|
640
|
-
if (!watchdogLaunchd.serviceRegistered || !watchdogLaunchd.pidWaitClean) throw new Error('RECOVERY_WATCHDOG_HANDOFF_FAILED');
|
|
731
|
+
|
|
641
732
|
const verification = await (input.dependencies.verify
|
|
642
733
|
?? ((verifyInput) => verifyRecoveryReleaseActivation(verifyInput, input.dependencies)))({
|
|
643
734
|
controllerHome: input.controllerHome,
|
|
644
735
|
config: input.config,
|
|
645
736
|
expectedRelease: input.expectedRelease,
|
|
737
|
+
profile: input.profile,
|
|
646
738
|
});
|
|
647
739
|
if (!verification.ok) throw new Error(`RECOVERY_RELEASE_VERIFICATION_FAILED: ${verification.failures.join('; ')}`);
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
verification
|
|
652
|
-
|
|
740
|
+
for (const role of recoveryInstallProfileRoles(input.profile)) {
|
|
741
|
+
const service = services[role];
|
|
742
|
+
if (!service) continue;
|
|
743
|
+
service.pid = role === 'daemon' ? verification.daemonPid : role === 'gateway' ? verification.gatewayPid : verification.watchdogPid;
|
|
744
|
+
service.pidReady = Boolean(service.pid);
|
|
745
|
+
if (recoveryRoleServesGateway(role)) service.portReady = verification.healthStatus === 200;
|
|
746
|
+
}
|
|
747
|
+
return { services, verification };
|
|
653
748
|
}
|
|
654
749
|
|
|
655
750
|
function sameRecoveryReleasePayload(left: RecoveryReleaseDescriptor, right: RecoveryReleaseDescriptor): boolean {
|
|
@@ -667,7 +762,8 @@ export async function activateRecoveryRelease(input: {
|
|
|
667
762
|
}, dependencies: RecoveryInstallerDependencies = {}): Promise<RecoveryActivationResult> {
|
|
668
763
|
const controllerHome = resolve(input.controllerHome);
|
|
669
764
|
const config = input.config ?? loadRecoveryConfig(controllerHome);
|
|
670
|
-
const
|
|
765
|
+
const profile = normalizeRecoveryInstallProfile(config.installProfile, 'self-healing');
|
|
766
|
+
const lock = await acquireRecoveryReleaseLock(controllerHome);
|
|
671
767
|
const current = readCurrentRecoveryRelease(controllerHome);
|
|
672
768
|
try {
|
|
673
769
|
if (current && sameRecoveryReleasePayload(current, input.candidate)) {
|
|
@@ -676,6 +772,7 @@ export async function activateRecoveryRelease(input: {
|
|
|
676
772
|
controllerHome,
|
|
677
773
|
config,
|
|
678
774
|
expectedRelease: current,
|
|
775
|
+
profile,
|
|
679
776
|
});
|
|
680
777
|
if (verification.ok) {
|
|
681
778
|
return {
|
|
@@ -688,28 +785,37 @@ export async function activateRecoveryRelease(input: {
|
|
|
688
785
|
}
|
|
689
786
|
const previous = current ?? captureLegacyRecoveryRelease(controllerHome, dependencies);
|
|
690
787
|
const migratedLegacy = previous?.legacy ? previous : undefined;
|
|
691
|
-
|
|
692
|
-
publishRecoveryCompatibilityLinks(controllerHome);
|
|
788
|
+
const drainArmed = Boolean(current);
|
|
693
789
|
try {
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
if (
|
|
698
|
-
publishRecoveryRelease(controllerHome,
|
|
790
|
+
// The shared operation lock already excludes every Recovery mutation.
|
|
791
|
+
// Stop new transport admission during handoff; retained read streams are
|
|
792
|
+
// connections, not effects, and cannot be a second quiescence authority.
|
|
793
|
+
if (drainArmed) setRecoveryGatewayDraining(controllerHome, true);
|
|
794
|
+
publishRecoveryRelease(controllerHome, input.candidate.releasePath, previous?.releasePath);
|
|
699
795
|
publishRecoveryCompatibilityLinks(controllerHome);
|
|
700
|
-
let rollbackResult: Awaited<ReturnType<typeof handoffRecoveryServices>>;
|
|
701
796
|
try {
|
|
702
|
-
|
|
703
|
-
|
|
797
|
+
const activated = await handoffRecoveryServices({ controllerHome, config, expectedRelease: input.candidate, profile, dependencies });
|
|
798
|
+
return { release: input.candidate, previous, migratedLegacy, handoff: activated.services, verification: activated.verification };
|
|
799
|
+
} catch (activationError) {
|
|
800
|
+
if (!previous) throw activationError;
|
|
801
|
+
publishRecoveryRelease(controllerHome, previous.releasePath, input.candidate.releasePath);
|
|
802
|
+
publishRecoveryCompatibilityLinks(controllerHome);
|
|
803
|
+
let rollbackResult: Awaited<ReturnType<typeof handoffRecoveryServices>>;
|
|
804
|
+
try {
|
|
805
|
+
rollbackResult = await handoffRecoveryServices({ controllerHome, config, expectedRelease: previous, profile, dependencies });
|
|
806
|
+
} catch (rollbackError) {
|
|
807
|
+
throw new Error(
|
|
808
|
+
`RECOVERY_RELEASE_ACTIVATION_FAILED: ${activationError instanceof Error ? activationError.message : String(activationError)}; `
|
|
809
|
+
+ `rollback to ${previous.releaseRevision} failed: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`,
|
|
810
|
+
);
|
|
811
|
+
}
|
|
704
812
|
throw new Error(
|
|
705
|
-
`
|
|
706
|
-
+ `
|
|
813
|
+
`RECOVERY_RELEASE_ACTIVATION_FAILED_ROLLED_BACK: ${activationError instanceof Error ? activationError.message : String(activationError)}; `
|
|
814
|
+
+ `restored=${previous.releaseRevision}; recovery=${rollbackResult.verification.daemonPid ?? rollbackResult.verification.gatewayPid ?? 'unknown'}`,
|
|
707
815
|
);
|
|
708
816
|
}
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
+ `restored=${previous.releaseRevision}; gateway=${rollbackResult.verification.gatewayPid ?? 'unknown'}; watchdog=${rollbackResult.verification.watchdogPid ?? 'unknown'}`,
|
|
712
|
-
);
|
|
817
|
+
} finally {
|
|
818
|
+
if (drainArmed) setRecoveryGatewayDraining(controllerHome, false);
|
|
713
819
|
}
|
|
714
820
|
} finally {
|
|
715
821
|
lock.close();
|
|
@@ -797,9 +903,14 @@ export async function installStandaloneRecovery(input: {
|
|
|
797
903
|
primaryPublicTunnelService?: PublicTunnelServiceConfig;
|
|
798
904
|
primaryRuntimeService?: PrimaryRuntimeServiceConfig;
|
|
799
905
|
primaryConnectorService?: PrimaryConnectorServiceConfig;
|
|
906
|
+
profile?: RecoveryInstallProfile;
|
|
907
|
+
/** @deprecated Compatibility alias for profile=manual without config/service activation. */
|
|
800
908
|
stageOnly?: boolean;
|
|
801
909
|
}, dependencies: RecoveryInstallerDependencies = {}): Promise<RecoveryInstallResult> {
|
|
802
910
|
const controllerHome = resolve(input.controllerHome);
|
|
911
|
+
const profile = input.stageOnly
|
|
912
|
+
? 'manual'
|
|
913
|
+
: normalizeRecoveryInstallProfile(input.profile, 'self-healing');
|
|
803
914
|
const sourceRoot = resolve(input.sourceRoot ?? input.repoRoot);
|
|
804
915
|
const primaryRuntimeSourceRoot = resolve(input.repoRoot);
|
|
805
916
|
const primaryRuntimeSourceRepositoryId = input.primaryRuntimeSourceRepositoryId?.trim();
|
|
@@ -836,9 +947,10 @@ export async function installStandaloneRecovery(input: {
|
|
|
836
947
|
}
|
|
837
948
|
const staged = stageRecoveryRelease({ controllerHome, sourceRoot }, dependencies);
|
|
838
949
|
if (input.stageOnly) {
|
|
839
|
-
return { controllerHome, staged, config: loadRecoveryConfig(controllerHome) };
|
|
950
|
+
return { controllerHome, profile: 'manual', staged, config: loadRecoveryConfig(controllerHome) };
|
|
840
951
|
}
|
|
841
952
|
const config = initializeStandaloneRecovery(controllerHome, input.port ?? 8787, {
|
|
953
|
+
installProfile: profile,
|
|
842
954
|
...(input.publicMcpUrl ? { publicMcpUrl: input.publicMcpUrl } : {}),
|
|
843
955
|
...(input.recoveryPublicUrl ? { recoveryPublicUrl: input.recoveryPublicUrl } : {}),
|
|
844
956
|
...(input.recoveryTunnelService ? { recoveryTunnelService: input.recoveryTunnelService } : {}),
|
|
@@ -849,7 +961,7 @@ export async function installStandaloneRecovery(input: {
|
|
|
849
961
|
...(input.primaryConnectorService ? { primaryConnectorService: input.primaryConnectorService } : {}),
|
|
850
962
|
});
|
|
851
963
|
const activated = await activateRecoveryRelease({ controllerHome, config, candidate: staged.release }, dependencies);
|
|
852
|
-
return { controllerHome, staged, activated, config };
|
|
964
|
+
return { controllerHome, profile, staged, activated, config };
|
|
853
965
|
}
|
|
854
966
|
|
|
855
967
|
export function recoveryReleaseAuthoritySnapshot(controllerHome: string): {
|
|
@@ -857,6 +969,7 @@ export function recoveryReleaseAuthoritySnapshot(controllerHome: string): {
|
|
|
857
969
|
previous?: RecoveryReleaseDescriptor;
|
|
858
970
|
currentPath: string;
|
|
859
971
|
previousPath: string;
|
|
972
|
+
daemonLaunchAgent: string;
|
|
860
973
|
gatewayLaunchAgent: string;
|
|
861
974
|
watchdogLaunchAgent: string;
|
|
862
975
|
} {
|
|
@@ -865,6 +978,7 @@ export function recoveryReleaseAuthoritySnapshot(controllerHome: string): {
|
|
|
865
978
|
previous: readPreviousRecoveryRelease(controllerHome),
|
|
866
979
|
currentPath: recoveryCurrentPath(controllerHome),
|
|
867
980
|
previousPath: recoveryPreviousPath(controllerHome),
|
|
981
|
+
daemonLaunchAgent: launchAgentPath(RECOVERY_DAEMON_LABEL),
|
|
868
982
|
gatewayLaunchAgent: launchAgentPath(RECOVERY_GATEWAY_LABEL),
|
|
869
983
|
watchdogLaunchAgent: launchAgentPath(RECOVERY_WATCHDOG_LABEL),
|
|
870
984
|
};
|