@moretea-labs/forge 1.7.2 → 1.8.0
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 +58 -62
- package/CHANGELOG.md +26 -0
- package/CLAUDE.md +42 -62
- package/README.md +2 -2
- package/SKILL.md +0 -1
- package/THIRD_PARTY_NOTICES.md +5 -2
- package/adapters/browser/index.ts +2 -0
- package/adapters/chatgpt/browser-delivery-host.ts +71 -0
- package/adapters/chatgpt/browser-delivery-runtime.ts +783 -0
- package/adapters/chatgpt/controller-binding-store.ts +55 -0
- package/adapters/chatgpt/controller-host.ts +119 -0
- package/adapters/chatgpt/controller-round-host.ts +78 -0
- package/adapters/chatgpt/controller-round-settlement-store.ts +45 -0
- package/adapters/chatgpt/provider-delivery.ts +113 -0
- package/adapters/chatgpt/work-conversation-binding-store.ts +192 -0
- package/adapters/chatgpt/wsl-bridge-delivery-host.ts +63 -0
- package/adapters/computer/desktop-operator-contract.ts +7 -0
- package/adapters/computer/desktop-operator-discovery.ts +201 -0
- package/adapters/computer/desktop-operator-negotiation.ts +174 -0
- package/adapters/computer/desktop-operator-provider.ts +312 -0
- package/adapters/computer/index.ts +1 -0
- package/adapters/computer/interaction-target-authority.ts +944 -0
- package/adapters/controller-process/binding-store.ts +34 -0
- package/adapters/controller-process/controller-host.ts +26 -0
- package/adapters/mcp/access-mode.ts +132 -0
- package/adapters/mcp/audit.ts +32 -0
- package/adapters/mcp/auth.ts +407 -0
- package/adapters/mcp/controller-round-compatibility.ts +184 -0
- package/adapters/mcp/frozen-client-semantic-compatibility.ts +292 -0
- package/adapters/mcp/instructions.ts +36 -0
- package/adapters/mcp/legacy-context.ts +18 -0
- package/adapters/mcp/multi-repository.ts +437 -0
- package/adapters/mcp/oauth.ts +391 -0
- package/adapters/mcp/paths.ts +140 -0
- package/adapters/mcp/policy.ts +232 -0
- package/adapters/mcp/proxy-env.ts +62 -0
- package/adapters/mcp/readiness-projection.ts +24 -0
- package/adapters/mcp/redaction.ts +21 -0
- package/adapters/mcp/repo.ts +24 -0
- package/adapters/mcp/runtime-gateway/artifact-adapter.ts +52 -0
- package/adapters/mcp/runtime-gateway/context-adapter.ts +829 -0
- package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +191 -0
- package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +147 -0
- package/adapters/mcp/runtime-gateway/execution-control-adapter.ts +28 -0
- package/adapters/mcp/runtime-gateway/execution-tools.ts +207 -0
- package/adapters/mcp/runtime-gateway/filesystem-adapter.ts +38 -0
- package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +411 -0
- package/adapters/mcp/runtime-gateway/legacy-ios-tool-adapter.ts +105 -0
- package/adapters/mcp/runtime-gateway/maintenance-adapter.ts +44 -0
- package/adapters/mcp/runtime-gateway/model-adapter.ts +63 -0
- package/adapters/mcp/runtime-gateway/persisted-check-process.ts +3 -0
- package/adapters/mcp/runtime-gateway/plugin-adapter.ts +308 -0
- package/adapters/mcp/runtime-gateway/process-tools.ts +289 -0
- package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +76 -0
- package/adapters/mcp/runtime-gateway/recovery-adapter.ts +268 -0
- package/adapters/mcp/runtime-gateway/recovery-client-adapter.ts +70 -0
- package/adapters/mcp/runtime-gateway/recovery-tool-contract.ts +99 -0
- package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +110 -0
- package/adapters/mcp/runtime-gateway/result-adapter.ts +108 -0
- package/adapters/mcp/runtime-gateway/router.ts +728 -0
- package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +1251 -0
- package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +634 -0
- package/adapters/mcp/runtime-gateway/runtime-tool-shared.ts +27 -0
- package/adapters/mcp/runtime-gateway/runtime-tools.ts +68 -0
- package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +186 -0
- package/adapters/mcp/runtime-gateway/shared-adapter.ts +25 -0
- package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +870 -0
- package/adapters/mcp/runtime-gateway/work-adapter.ts +3186 -0
- package/adapters/mcp/runtime-gateway/work-compat-adapter.ts +502 -0
- package/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +1 -0
- package/adapters/mcp/server.ts +1048 -0
- package/adapters/mcp/setup.ts +791 -0
- package/adapters/mcp/tool-capability-cache.ts +87 -0
- package/adapters/mcp/tool-mapping/access-tools.ts +387 -0
- package/adapters/mcp/tool-mapping/legacy-tool-service.ts +5265 -0
- package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +150 -0
- package/adapters/mcp/tool-mapping/repository-tools.ts +1691 -0
- package/adapters/mcp/tool-mapping/tools.ts +9 -0
- package/adapters/mcp/tool-mapping/umbrella-tools.ts +117 -0
- package/adapters/mcp/toolset-names.ts +74 -0
- package/adapters/mcp/toolset-selection.ts +41 -0
- package/adapters/mcp/toolset.ts +336 -0
- package/adapters/mcp/transports/http.ts +1456 -0
- package/adapters/mcp/transports/session-registry.ts +513 -0
- package/adapters/mcp/transports/stdio.ts +8 -0
- package/adapters/mcp/tunnels/openai-secure-tunnel.ts +139 -0
- package/adapters/mcp/types.ts +41 -0
- package/assets/AGENTS.md +2 -16
- package/assets/CLAUDE.md +2 -16
- package/assets/hooks/AGENTS.md +3 -14
- package/assets/hooks/CLAUDE.md +3 -14
- package/assets/hooks/hook-input.sh +9 -2
- package/assets/hooks/lib/workflow-state.sh +28 -10
- package/assets/hooks/prompt-guard.sh +63 -37
- package/assets/hooks/security-sentinel.sh +2 -1
- package/assets/hooks/session-start-context.sh +7 -5
- package/assets/hooks/stop-orchestrator.sh +13 -3
- package/assets/initializer-question-pack.v4.json +11 -7
- package/assets/partials/05-workflow.partial.md +18 -73
- package/assets/partials/07-footer.partial.md +1 -1
- package/assets/partials-agents/02-operating-mode.partial.md +1 -2
- package/assets/partials-agents/04-task-protocol.partial.md +14 -59
- package/assets/partials-agents/06-quality-safety.partial.md +11 -16
- package/assets/partials-agents/08-deep-docs.partial.md +1 -2
- package/assets/plan-map.json +11 -11
- package/assets/plugin-registry.v1.json +9 -3
- package/assets/plugin-trust-policy.v1.json +6 -0
- package/assets/recovery/ForgeRecovery.ps1 +100 -0
- package/assets/recovery/forge-wsl-rescue +271 -0
- package/assets/reference-configs/development-protocol.md +11 -15
- package/assets/reference-configs/document-generation.md +1 -1
- package/assets/reference-configs/global-working-rules.md +9 -1
- package/assets/reference-configs/harness-overview.md +22 -112
- package/assets/reference-configs/hook-operations.md +1 -1
- package/assets/reference-configs/sprint-contracts.md +2 -2
- package/assets/skill-version.json +2 -2
- package/assets/templates/helpers/architecture-event.ts +3 -34
- package/assets/templates/helpers/architecture-queue.sh +61 -10
- package/assets/templates/helpers/archive-workflow.sh +90 -180
- package/assets/templates/helpers/capability-config.ts +8 -11
- package/assets/templates/helpers/capability-resolver.ts +37 -19
- package/assets/templates/helpers/capture-plan.sh +2 -2
- package/assets/templates/helpers/check-architecture-sync.sh +1 -9
- package/assets/templates/helpers/check-task-sync.sh +82 -11
- package/assets/templates/helpers/check-task-workflow.sh +1 -3
- package/assets/templates/helpers/context-contract-sync.sh +1 -12
- package/assets/templates/helpers/contract-worktree.sh +26 -24
- package/assets/templates/helpers/ensure-task-workflow.sh +6 -8
- package/assets/templates/helpers/inspect-project-state.ts +1 -1
- package/assets/templates/helpers/migrate-project-template.sh +1 -1
- package/assets/templates/helpers/migrate-workflow-docs.ts +53 -239
- package/assets/templates/helpers/new-plan.sh +2 -2
- package/assets/templates/helpers/plan-to-todo.sh +3 -31
- package/assets/templates/helpers/sprint-backlog.sh +1 -1
- package/assets/templates/helpers/switch-plan.sh +1 -1
- package/assets/templates/helpers/workflow-contract.ts +1 -0
- package/assets/workflow-contract.v1.json +63 -171
- package/assets/workflows/xiaohongshu/xiaohongshu.collect-metrics.v1.draft.json +118 -0
- package/assets/workflows/xiaohongshu/xiaohongshu.publish-image-note.v1.draft.json +316 -0
- package/assets/workflows/xiaohongshu/xiaohongshu.publish-long-text.v1.draft.json +324 -0
- package/docs/architecture/CURRENT.md +148 -13
- package/docs/architecture/EVOLUTION.md +13 -0
- package/docs/architecture/decisions/20260801-controller-home-sqlite-state.md +2 -1
- package/docs/architecture/decisions/20260831-independent-windows-wsl-host-rescue.md +59 -0
- package/docs/architecture/decisions/20260904-ios-device-migration-baseline.md +120 -0
- package/docs/architecture/decisions/20260904-repository-runtime-data-boundary.md +42 -0
- package/docs/architecture/decisions/20260907-assistant-learning-loop.md +35 -0
- package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +296 -0
- package/docs/architecture/decisions/20260907-maintenance-diagnosis-bounded-read.md +39 -0
- package/docs/architecture/domains/verification.md +7 -0
- package/docs/architecture/domains/workflow-engine.md +1 -1
- package/docs/architecture/index.md +3 -3
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +19 -8
- package/docs/architecture/requests/2026-09-04-computer-plugin-workflow-taxonomy.md +124 -0
- package/docs/forge-chatgpt-mcp-setup.md +5 -3
- package/docs/forge-plugin-management.md +20 -4
- package/docs/forge-runtime-self-healing-loop.md +2 -0
- package/docs/operations/benchmark-cross-version-freeze.md +38 -0
- package/docs/operations/controller-browser-plugin.md +3 -1
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/platform-support.md +16 -8
- package/docs/operations/platform-support.zh-CN.md +2 -2
- package/docs/operations/standalone-disaster-recovery.md +26 -1
- package/docs/operations/uu-remote-rescue.md +41 -0
- package/docs/operations/windows-wsl-host-rescue.md +133 -0
- package/docs/public-usage-guide.md +1 -1
- package/docs/public-usage-guide.zh-CN.md +1 -1
- package/docs/tutorials/01-install-and-start.md +1 -1
- package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
- package/docs/tutorials/02-connect-chatgpt.md +4 -4
- package/docs/tutorials/02-connect-chatgpt.zh-CN.md +4 -4
- package/docs/wiki/Implementation.md +1 -1
- package/package.json +23 -14
- package/packages/kernel/controller/api/index.ts +2 -0
- package/packages/kernel/controller/application/controller-round-service.ts +29 -0
- package/packages/kernel/controller/application/controller-service.ts +30 -0
- package/packages/kernel/controller/domain/controller-round-transition-policy.ts +308 -0
- package/packages/kernel/controller/domain/controller-round.ts +107 -0
- package/packages/kernel/controller/domain/execution-quality.ts +133 -0
- package/packages/kernel/controller/domain/types.ts +56 -0
- package/packages/kernel/controller/index.ts +1 -0
- package/packages/kernel/controller/infrastructure/controller-binding-store.ts +84 -0
- package/packages/kernel/controller/infrastructure/controller-round-store.ts +1384 -0
- package/packages/kernel/controller/infrastructure/controller-session-store.ts +578 -0
- package/packages/kernel/controller/ports/controller-host.ts +16 -0
- package/packages/kernel/goal/api/index.ts +1 -0
- package/packages/kernel/goal/domain/types.ts +40 -0
- package/packages/kernel/goal/index.ts +1 -0
- package/packages/kernel/identity/api/index.ts +5 -0
- package/packages/kernel/identity/application/identity-service.ts +67 -0
- package/packages/kernel/identity/domain/scope.ts +63 -0
- package/packages/kernel/identity/domain/semantic-sync.ts +81 -0
- package/packages/kernel/identity/domain/types.ts +44 -0
- package/packages/kernel/identity/domain/workspace.ts +56 -0
- package/packages/kernel/identity/index.ts +1 -0
- package/packages/kernel/identity/infrastructure/identity-store.ts +75 -0
- package/packages/kernel/memory/api/index.ts +5 -0
- package/packages/kernel/memory/application/experience-service.ts +118 -0
- package/packages/kernel/memory/application/outcome-service.ts +77 -0
- package/packages/kernel/memory/domain/experience.ts +103 -0
- package/packages/kernel/memory/domain/operational-prior.ts +92 -0
- package/packages/kernel/memory/index.ts +1 -0
- package/packages/kernel/progression/api/index.ts +2 -0
- package/packages/kernel/progression/application/projector.ts +211 -0
- package/packages/kernel/progression/domain/types.ts +116 -0
- package/packages/kernel/progression/index.ts +1 -0
- package/packages/kernel/scheduler/api/index.ts +6 -0
- package/packages/kernel/scheduler/application/continuation-service.ts +212 -0
- package/packages/kernel/scheduler/application/eligibility.ts +252 -0
- package/packages/kernel/scheduler/application/schedule-service.ts +24 -0
- package/packages/kernel/scheduler/application/settlement.ts +197 -0
- package/packages/kernel/scheduler/domain/continuation.ts +38 -0
- package/packages/kernel/scheduler/domain/schedule.ts +149 -0
- package/packages/kernel/scheduler/index.ts +1 -0
- package/packages/kernel/scheduler/infrastructure/continuation-dispatch-store.ts +38 -0
- package/packages/kernel/scheduler/infrastructure/schedule-store.ts +785 -0
- package/packages/kernel/work/api/index.ts +15 -0
- package/packages/kernel/work/application/work-service.ts +148 -0
- package/packages/kernel/work/domain/admission-policy.ts +34 -0
- package/packages/kernel/work/domain/check-receipt.ts +43 -0
- package/packages/kernel/work/domain/context-closure-receipt.ts +32 -0
- package/packages/kernel/work/domain/engineering-contracts.ts +157 -0
- package/packages/kernel/work/domain/engineering-design.ts +281 -0
- package/packages/kernel/work/domain/engineering-profile.ts +242 -0
- package/packages/kernel/work/domain/execution-concurrency.ts +254 -0
- package/packages/kernel/work/domain/execution-snapshot.ts +33 -0
- package/packages/kernel/work/domain/implementation-review.ts +510 -0
- package/packages/kernel/work/domain/project-engineering-contract.ts +202 -0
- package/packages/kernel/work/domain/repository-completion-receipt.ts +73 -0
- package/packages/kernel/work/domain/state-machine.ts +259 -0
- package/packages/kernel/work/domain/types.ts +528 -0
- package/packages/kernel/work/index.ts +1 -0
- package/packages/kernel/work/infrastructure/work-contract-store.ts +1876 -0
- package/packages/kernel/work/ports/work-contract-store.ts +18 -0
- package/packages/plugin-runtime/browser/index.ts +3 -0
- package/packages/plugin-runtime/browser/provider-registry.ts +110 -0
- package/packages/plugin-runtime/browser/runtime-contract.ts +150 -0
- package/packages/plugin-runtime/browser/session-authority.ts +61 -0
- package/packages/plugin-runtime/computer/index.ts +6 -0
- package/packages/plugin-runtime/computer/provider-error.ts +26 -0
- package/packages/plugin-runtime/computer/provider-registration.ts +52 -0
- package/packages/plugin-runtime/computer/provider-registry.ts +63 -0
- package/packages/plugin-runtime/computer/provider.ts +19 -0
- package/packages/plugin-runtime/computer/target-authority.ts +190 -0
- package/packages/plugin-runtime/computer/target-persistence.ts +54 -0
- package/packages/plugin-runtime/external/index.ts +1 -0
- package/packages/plugin-runtime/external/unix-jsonl-transport.ts +457 -0
- package/packages/plugin-runtime/index.ts +3 -0
- package/packages/protocols/browser/index.ts +1 -0
- package/packages/protocols/browser/session.ts +98 -0
- package/packages/protocols/computer/contract.ts +199 -0
- package/packages/protocols/computer/index.ts +1 -0
- package/packages/protocols/failure/contract.ts +158 -0
- package/packages/protocols/failure/index.ts +1 -0
- package/packages/protocols/handoff/index.ts +1 -0
- package/packages/protocols/handoff/status.ts +22 -0
- package/packages/protocols/mcp/execution-context.ts +11 -0
- package/packages/protocols/mcp/index.ts +2 -0
- package/packages/protocols/mcp/tool-contract.ts +42 -0
- package/packages/workflow-runtime/api/index.ts +2 -0
- package/packages/workflow-runtime/application/interpreter.ts +342 -0
- package/packages/workflow-runtime/domain/workflow-asset.ts +223 -0
- package/packages/workflow-runtime/index.ts +1 -0
- package/scripts/assemble-template.ts +1 -1
- package/scripts/benchmark-computer-hotpath.ts +255 -0
- package/scripts/benchmark-controller-round2.ts +54 -2
- package/scripts/benchmark-ios-agent-device.ts +3 -6
- package/scripts/benchmark-mcp-transport.ts +3 -4
- package/scripts/benchmark-operational-memory-activation.ts +235 -0
- package/scripts/benchmark-operational-shadow.ts +28 -0
- package/scripts/bootstrap-runtime-maintenance-recovery.sh +4 -1
- package/scripts/canary-chatgpt-provider-delivery.ts +97 -0
- package/scripts/canary-controller-semantic-wait.ts +195 -0
- package/scripts/canary-global-hook.sh +2 -2
- package/scripts/check-mcp-compatibility.ts +173 -7
- package/scripts/check-platform-support.mjs +28 -2
- package/scripts/check-public-docs.sh +1 -1
- package/scripts/check-release-readiness.sh +1 -1
- package/scripts/check-repository-hygiene.mjs +92 -0
- package/scripts/check-runtime-architecture.mjs +1206 -95
- package/scripts/check-source-duplication.mjs +9 -1
- package/scripts/check-tarball-install-smoke.sh +98 -15
- package/scripts/controller-ngrok-rotation.sh +5 -4
- package/scripts/create-project-dirs.sh +10 -74
- package/scripts/forge-local-recovery-helper.mjs +204 -0
- package/scripts/forge-uu-remote-rescue-helper.d.mts +31 -0
- package/scripts/forge-uu-remote-rescue-helper.mjs +409 -0
- package/scripts/forge-windows-host-recovery-helper.mjs +136 -0
- package/scripts/hook-shim.sh +2 -2
- package/scripts/init-project.sh +12 -70
- package/scripts/install-desktop-operator-registration.ts +4 -2
- package/scripts/install-independent-windows-wsl-recovery.ts +125 -0
- package/scripts/install-standalone-recovery.ts +122 -10
- package/scripts/install-uu-remote-rescue-registration.ts +74 -0
- package/scripts/install-windows-host-recovery-registration.ts +22 -0
- package/scripts/lib/controller-home-env.sh +6 -3
- package/scripts/lib/project-init-lib.sh +39 -582
- package/scripts/migrate-chatgpt-automation-schedules.ts +2 -2
- package/scripts/migrate-windows-wsl-controller-home.ts +306 -0
- package/scripts/open-source-audit-allowlist.txt +12 -1
- package/scripts/portability-bindings.mjs +70 -0
- package/scripts/run-bounded-command.mjs +55 -0
- package/scripts/run-bun-test-file.ts +22 -6
- package/scripts/run-governed-gate.ts +4 -1
- package/scripts/smoke-mcp-http-runtime.ts +1 -1
- package/scripts/smoke-runtime-recovery.ts +34 -1
- package/scripts/smoke-schedule-engine.ts +1 -1
- package/scripts/stage-runtime-release.ts +6 -1
- package/scripts/test-governance.ts +51 -2
- package/src/cli/agent-jobs/job-manager.ts +10 -1
- package/src/cli/chatgpt-browser/bind-server.ts +2 -1
- package/src/cli/chatgpt-browser/binding.ts +20 -10
- package/src/cli/chatgpt-browser/bridge-extension.ts +75 -0
- package/src/cli/chatgpt-browser/bridge-provider.ts +376 -11
- package/src/cli/chatgpt-browser/engine.ts +10 -11
- package/src/cli/chatgpt-browser/native-provider.ts +24 -83
- package/src/cli/chatgpt-browser/oracle-provider.ts +5 -3
- package/src/cli/chatgpt-browser/types.ts +1 -0
- package/src/cli/commands/bootstrap-control.ts +334 -0
- package/src/cli/commands/brain-assistant.ts +38 -0
- package/src/cli/commands/brain.ts +193 -40
- package/src/cli/commands/chatgpt.ts +127 -0
- package/src/cli/commands/computer.ts +437 -0
- package/src/cli/commands/doctor.ts +18 -1
- package/src/cli/commands/init-hook.ts +71 -61
- package/src/cli/commands/init.ts +3 -1
- package/src/cli/commands/mcp.ts +16 -10
- package/src/cli/commands/plugin.ts +38 -12
- package/src/cli/commands/recovery.ts +493 -80
- package/src/cli/commands/runtime.ts +9 -4
- package/src/cli/commands/setup-profile.ts +89 -39
- package/src/cli/commands/status.ts +2 -1
- package/src/cli/controller/check-runner.ts +487 -98
- package/src/cli/controller/context/types.ts +7 -1
- package/src/cli/controller/context-pack.ts +23 -3
- package/src/cli/controller/issue-store.ts +1 -1
- package/src/cli/controller/systemd-user.ts +110 -0
- package/src/cli/controller/types.ts +10 -70
- package/src/cli/controller/worklog.ts +67 -13
- package/src/cli/editing/edit-operation-contract.ts +92 -0
- package/src/cli/editing/edit-session.ts +289 -13
- package/src/cli/github/plugin.ts +17 -12
- package/src/cli/hook/route-registry.ts +3 -5
- package/src/cli/hook/runtime.ts +49 -3
- package/src/cli/index.ts +3 -0
- package/src/cli/local-bridge/connector-freshness.ts +2 -2
- package/src/cli/local-bridge/console-automations.ts +2 -2
- package/src/cli/local-bridge/facade-api.ts +41 -14
- package/src/cli/local-bridge/job-store.ts +1 -1
- package/src/cli/local-bridge/server.ts +24 -27
- package/src/cli/mcp/access-mode.ts +2 -132
- package/src/cli/mcp/access-tools.ts +2 -387
- package/src/cli/mcp/audit.ts +2 -32
- package/src/cli/mcp/auth.ts +2 -404
- package/src/cli/mcp/instructions.ts +2 -34
- package/src/cli/mcp/legacy-context.ts +2 -16
- package/src/cli/mcp/legacy-tool-service.ts +2 -5308
- package/src/cli/mcp/multi-repository.ts +2 -436
- package/src/cli/mcp/oauth.ts +2 -375
- package/src/cli/mcp/openai-secure-tunnel.ts +2 -0
- package/src/cli/mcp/paths.ts +2 -140
- package/src/cli/mcp/policy.ts +2 -232
- package/src/cli/mcp/post-finalize-work-attribution.ts +2 -150
- package/src/cli/mcp/proxy-env.ts +2 -62
- package/src/cli/mcp/readiness-projection.ts +2 -24
- package/src/cli/mcp/redaction.ts +2 -21
- package/src/cli/mcp/repo.ts +2 -24
- package/src/cli/mcp/repository-tools.ts +2 -1613
- package/src/cli/mcp/server.ts +2 -951
- package/src/cli/mcp/setup.ts +2 -737
- package/src/cli/mcp/tool-capability-cache.ts +2 -87
- package/src/cli/mcp/tool-contract.ts +2 -14
- package/src/cli/mcp/tools.ts +2 -9
- package/src/cli/mcp/toolset-names.ts +2 -70
- package/src/cli/mcp/toolset-selection.ts +2 -41
- package/src/cli/mcp/toolset.ts +2 -336
- package/src/cli/mcp/transports/AGENTS.md +1 -10
- package/src/cli/mcp/transports/CLAUDE.md +1 -10
- package/src/cli/mcp/transports/http.ts +2 -1414
- package/src/cli/mcp/transports/session-registry.ts +2 -490
- package/src/cli/mcp/transports/stdio.ts +2 -8
- package/src/cli/mcp/types.ts +2 -41
- package/src/cli/mcp/umbrella-tools.ts +2 -117
- package/src/cli/repositories/command-executor.ts +64 -18
- package/src/cli/repositories/command-scope.ts +117 -1
- package/src/cli/repositories/controller-home.ts +119 -1
- package/src/cli/repositories/identity.ts +13 -0
- package/src/cli/repositories/registry.ts +68 -49
- package/src/cli/repositories/repository-snapshot.ts +53 -7
- package/src/cli/repositories/runtime-storage.ts +27 -2
- package/src/cli/repositories/safe-patch.ts +34 -38
- package/src/cli/repositories/selected-path-actions.ts +22 -2
- package/src/cli/repositories/structured-git.ts +105 -25
- package/src/cli/runtime/helper-runner.ts +7 -0
- package/src/cli/runtime-invocation.ts +8 -1
- package/src/cli/tools/codegraph.ts +44 -25
- package/src/core/adoption/manifest-templates.ts +1 -1
- package/src/core/adoption/plan.ts +3 -26
- package/src/core/adoption/workflow-contract-plan.ts +17 -9
- package/src/effects/process-runner.ts +8 -14
- package/src/runtime/context/assistant-context.ts +154 -0
- package/src/runtime/context/assistant-work-context.ts +147 -0
- package/src/runtime/context/codegraph-cache-boundary.ts +255 -0
- package/src/runtime/context/codegraph-read-provider.ts +55 -22
- package/src/runtime/context/codegraph-sidecar.cjs +8 -1
- package/src/runtime/context/context-closure.ts +268 -0
- package/src/runtime/context/context-plane.ts +211 -0
- package/src/runtime/context/project-engineering-contract.ts +60 -0
- package/src/runtime/control-plane/bootstrap/capability-provider.ts +74 -0
- package/src/runtime/control-plane/bootstrap/control-api.ts +57 -0
- package/src/runtime/control-plane/bootstrap/index.ts +5 -0
- package/src/runtime/control-plane/bootstrap/state-machine.ts +141 -0
- package/src/runtime/control-plane/bootstrap/store.ts +21 -0
- package/src/runtime/control-plane/bootstrap/types.ts +87 -0
- package/src/runtime/control-plane/cleanup-artifact-retention.ts +328 -0
- package/src/runtime/control-plane/codegraph-cache-retention.ts +164 -0
- package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +320 -0
- package/src/runtime/control-plane/execution/content-equivalent-commit-authority.ts +169 -0
- package/src/runtime/control-plane/execution/controller-authority-recovery.ts +350 -0
- package/src/runtime/control-plane/execution/direct-canonical-work-reconciliation.ts +85 -0
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +369 -99
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +103 -15
- package/src/runtime/control-plane/execution/implementation-review-content.ts +95 -0
- package/src/runtime/control-plane/execution/repository-work-attribution.ts +2 -2
- package/src/runtime/control-plane/execution/retained-work-resume.ts +336 -6
- package/src/runtime/control-plane/execution/validation.ts +28 -3
- package/src/runtime/control-plane/execution/work-authority-reconciler.ts +116 -0
- package/src/runtime/control-plane/execution/work-completion-authority.ts +44 -2
- package/src/runtime/control-plane/execution/work-evidence-policy.ts +34 -3
- package/src/runtime/control-plane/execution/work-execution-support.ts +42 -22
- package/src/runtime/control-plane/execution/work-finalization-service.ts +1103 -166
- package/src/runtime/control-plane/execution/work-handle-authority.ts +219 -22
- package/src/runtime/control-plane/execution/work-handle-store.ts +77 -0
- package/src/runtime/control-plane/execution/work-head-settlement.ts +75 -0
- package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +145 -23
- package/src/runtime/control-plane/execution/work-operation-service.ts +49 -13
- package/src/runtime/control-plane/execution/work-preparation-service.ts +31 -15
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +1 -1
- package/src/runtime/control-plane/execution/work-task-receipt.ts +20 -1
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +407 -76
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +88 -18
- package/src/runtime/control-plane/execution/work-verification-context.ts +1 -1
- package/src/runtime/control-plane/execution/work-verification-service.ts +258 -4
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +200 -15
- package/src/runtime/control-plane/facade/capability-registry.ts +21 -10
- package/src/runtime/control-plane/facade/controller-round-relay.ts +3 -1118
- package/src/runtime/control-plane/facade/controller-session-store.ts +2 -488
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +6 -3
- package/src/runtime/control-plane/facade/goal-workloop.ts +972 -137
- package/src/runtime/control-plane/facade/handoff-inbox-application.ts +158 -0
- package/src/runtime/control-plane/facade/handoff-inbox-store.ts +10 -0
- package/src/runtime/control-plane/facade/index.ts +4 -2
- package/src/runtime/control-plane/facade/operation-digest.ts +22 -58
- package/src/runtime/control-plane/facade/plan-contract-store.ts +1004 -38
- package/src/runtime/control-plane/facade/repository-work-admission.ts +19 -1
- package/src/runtime/control-plane/facade/requirement-authority.ts +218 -0
- package/src/runtime/control-plane/facade/requirement-board.ts +6 -1
- package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +48 -0
- package/src/runtime/control-plane/facade/semantic-admission.ts +8 -16
- package/src/runtime/control-plane/facade/suggested-actions.ts +4 -1
- package/src/runtime/control-plane/facade/types.ts +189 -532
- package/src/runtime/control-plane/facade/work-admission-policy.ts +84 -35
- package/src/runtime/control-plane/facade/work-contract-store.ts +2 -1043
- package/src/runtime/control-plane/facade/work-implementation-review.ts +2 -0
- package/src/runtime/control-plane/facade/work-state-machine.ts +2 -250
- package/src/runtime/control-plane/generated-cache-retention.ts +239 -18
- package/src/runtime/control-plane/global-scheduler/durable-admission.ts +12 -1
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +76 -13
- package/src/runtime/control-plane/global-scheduler/periodic-cleanup-entry.ts +55 -0
- package/src/runtime/control-plane/global-scheduler/periodic-cleanup-process.ts +98 -0
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +1 -1
- package/src/runtime/control-plane/global-scheduler/scheduler-worker-entry.ts +10 -0
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +173 -81
- package/src/runtime/control-plane/global-scheduler/worker-launch.ts +18 -6
- package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +397 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +2 -177
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +301 -560
- package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +20 -4
- package/src/runtime/control-plane/launcher/thin-launcher.ts +50 -4
- package/src/runtime/control-plane/lifecycle-retention-metrics.ts +60 -0
- package/src/runtime/control-plane/persistence/experience-store.ts +169 -0
- package/src/runtime/control-plane/persistence/operational-prior-store.ts +348 -0
- package/src/runtime/control-plane/persistence/requirement-store.ts +30 -22
- package/src/runtime/control-plane/persistence/sqlite-store.ts +545 -76
- package/src/runtime/control-plane/persistence/workflow-content-store.ts +98 -0
- package/src/runtime/control-plane/persistence/workflow-registry-store.ts +186 -0
- package/src/runtime/control-plane/persistence/workflow-run-store.ts +123 -0
- package/src/runtime/control-plane/release-retention.ts +56 -16
- package/src/runtime/control-plane/repository-namespace-retention.ts +208 -0
- package/src/runtime/control-plane/routing/route-policy.ts +19 -0
- package/src/runtime/control-plane/runtime-cleanup.ts +574 -37
- package/src/runtime/control-plane/runtime-generation.ts +6 -0
- package/src/runtime/control-plane/workspace/semantic-sync-service.ts +465 -0
- package/src/runtime/control-plane/workspace/workspace-store.ts +80 -0
- package/src/runtime/diagnostics/incident-repair.ts +406 -0
- package/src/runtime/diagnostics/mcp-timing.ts +125 -6
- package/src/runtime/diagnostics/performance.ts +6 -14
- package/src/runtime/diagnostics/read-only-tool.ts +1 -1
- package/src/runtime/evidence/artifact-store.ts +65 -2
- package/src/runtime/evidence/operational-shadow.ts +38 -0
- package/src/runtime/evidence/process-check-receipt.ts +5 -43
- package/src/runtime/evidence/sensitive-output.ts +4 -4
- package/src/runtime/execution/jobs/receipt-recovery.ts +1 -1
- package/src/runtime/execution/jobs/store.ts +121 -2
- package/src/runtime/execution/managed-workspace.ts +55 -5
- package/src/runtime/execution/process-runtime/canary.ts +19 -5
- package/src/runtime/execution/process-runtime/check-classification.ts +48 -0
- package/src/runtime/execution/process-runtime/check-facade.ts +23 -9
- package/src/runtime/execution/process-runtime/check-receipt.ts +1 -1
- package/src/runtime/execution/process-runtime/check-result-retention.ts +132 -0
- package/src/runtime/execution/process-runtime/check-result.ts +73 -2
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +42 -3
- package/src/runtime/execution/process-runtime/check-storage.ts +187 -0
- package/src/runtime/execution/process-runtime/command-facade.ts +38 -87
- package/src/runtime/execution/process-runtime/index.ts +1 -0
- package/src/runtime/execution/process-runtime/interactive-admission.ts +2 -1
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +32 -1
- package/src/runtime/execution/process-runtime/persisted-check.ts +128 -21
- package/src/runtime/execution/process-runtime/resource-claims.ts +60 -3
- package/src/runtime/execution/process-runtime/runtime.ts +132 -15
- package/src/runtime/execution/process-runtime/types.ts +22 -0
- package/src/runtime/execution/workers/executor.ts +16 -13
- package/src/runtime/execution/workers/worker-entry.ts +1 -1
- package/src/runtime/gateway/mcp/execution-tools.ts +2 -207
- package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +2 -61
- package/src/runtime/gateway/mcp/persisted-check-process.ts +2 -3
- package/src/runtime/gateway/mcp/process-tools.ts +2 -289
- package/src/runtime/gateway/mcp/router.ts +2 -1125
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +2 -565
- package/src/runtime/gateway/mcp/runtime-tools.ts +2 -6655
- package/src/runtime/gateway/mcp/work-validation-reconciler.ts +2 -1
- package/src/runtime/maintenance/cleanup.ts +38 -24
- package/src/runtime/platform/browser-product-discovery.ts +174 -0
- package/src/runtime/platform/computer-platform.ts +14 -0
- package/src/runtime/platform/executable-discovery.ts +54 -0
- package/src/runtime/platform/fetch-http-server.ts +90 -0
- package/src/runtime/platform/service-manager.ts +138 -0
- package/src/runtime/platform/windows-host-discovery.ts +118 -0
- package/src/runtime/platform/windows-recovery-deployment.ts +67 -0
- package/src/runtime/plugins/action-application.ts +135 -0
- package/src/runtime/plugins/app-store-connect-adapter.ts +9 -21
- package/src/runtime/plugins/browser-adapter.ts +140 -100
- package/src/runtime/plugins/browser-automation-service.ts +15 -36
- package/src/runtime/plugins/browser-macos-bridge.ts +69 -21
- package/src/runtime/plugins/browser-manifest-surface.ts +30 -6
- package/src/runtime/plugins/browser-native-session-service.ts +1 -1
- package/src/runtime/plugins/browser-provider-registry.ts +2 -109
- package/src/runtime/plugins/browser-registration.ts +16 -0
- package/src/runtime/plugins/browser-runtime-contract.ts +2 -154
- package/src/runtime/plugins/browser-runtime.ts +41 -9
- package/src/runtime/plugins/browser-session-authority.ts +101 -291
- package/src/runtime/plugins/browser-session-legacy-migration.ts +203 -0
- package/src/runtime/plugins/browser-session-store.ts +229 -36
- package/src/runtime/plugins/browser-session-types.ts +2 -98
- package/src/runtime/plugins/capability-authorization-grants.ts +17 -0
- package/src/runtime/plugins/catalog-trust.ts +82 -0
- package/src/runtime/plugins/computer-registration.ts +839 -0
- package/src/runtime/plugins/config-store.ts +119 -0
- package/src/runtime/plugins/desktop-operator-external-policy.ts +932 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +111 -8
- package/src/runtime/plugins/errors.ts +8 -1
- package/src/runtime/plugins/external-adapter.ts +45 -322
- package/src/runtime/plugins/external-provider-policy.ts +48 -0
- package/src/runtime/plugins/external-registration.ts +9 -1
- package/src/runtime/plugins/external-unix-socket-probe.cjs +9 -3
- package/src/runtime/plugins/external-unix-socket.ts +64 -193
- package/src/runtime/plugins/first-party-external-registration.ts +163 -0
- package/src/runtime/plugins/first-party-registry.ts +3 -1
- package/src/runtime/plugins/github-adapter.ts +1 -2
- package/src/runtime/plugins/gmail-adapter.ts +6 -6
- package/src/runtime/plugins/google-calendar-adapter.ts +6 -6
- package/src/runtime/plugins/google-shared.ts +31 -46
- package/src/runtime/plugins/google-tasks-adapter.ts +6 -6
- package/src/runtime/plugins/ios-adapter.ts +1 -2
- package/src/runtime/plugins/local-system-adapter.ts +180 -0
- package/src/runtime/plugins/macos-capability-broker.ts +15 -105
- package/src/runtime/plugins/resend-adapter.ts +15 -24
- package/src/runtime/plugins/store.ts +493 -51
- package/src/runtime/plugins/types.ts +8 -1
- package/src/runtime/plugins/uu-remote-rescue-registration.ts +155 -0
- package/src/runtime/plugins/windows-host-recovery-install.ts +68 -0
- package/src/runtime/plugins/windows-host-recovery-registration.ts +85 -0
- package/src/runtime/plugins/xiaohongshu-publish.ts +86 -430
- package/src/runtime/projections/controller-context.ts +12 -6
- package/src/runtime/projections/git-status-sampler.ts +73 -22
- package/src/runtime/projections/materialized-view.ts +14 -8
- package/src/runtime/recovery/capability-action-application.ts +228 -0
- package/src/runtime/recovery/index.ts +3 -0
- package/src/runtime/recovery/lifecycle-inventory.ts +338 -0
- package/src/runtime/recovery/local-jobs-repair.ts +14 -14
- package/src/runtime/recovery/maintenance-executor.ts +358 -76
- package/src/runtime/recovery/quarantine-retention.ts +256 -0
- package/src/runtime/release/identity.ts +6 -0
- package/src/runtime/resources/leases/store.ts +1 -1
- package/src/runtime/root/active-execution-power-assertion.ts +139 -0
- package/src/runtime/root/assistant-learning-loop.ts +45 -0
- package/src/runtime/root/browser-session-composition.ts +59 -0
- package/src/runtime/root/computer-composition.ts +191 -0
- package/src/runtime/root/computer-target-composition.ts +16 -0
- package/src/runtime/root/computer-target-persistence.ts +39 -0
- package/src/runtime/root/controller-round-composition.ts +107 -0
- package/src/runtime/root/controller-services.ts +2 -0
- package/src/runtime/root/entry.ts +4 -3
- package/src/runtime/root/gateway-adapter.ts +3 -3
- package/src/runtime/root/local-bridge.ts +5 -2
- package/src/runtime/root/mcp-transport.ts +5 -5
- package/src/runtime/root/package-connector-service.ts +108 -56
- package/src/runtime/root/package-runtime-release.ts +85 -18
- package/src/runtime/root/package-runtime-service.ts +60 -97
- package/src/runtime/root/release-execution-canary.ts +71 -0
- package/src/runtime/root/release-loader.ts +68 -0
- package/src/runtime/root/release-manifest.ts +106 -2
- package/src/runtime/root/release-materialize.ts +103 -4
- package/src/runtime/root/release-store.ts +31 -2
- package/src/runtime/root/runtime.ts +240 -25
- package/src/runtime/root/scheduled-controller-composition.ts +51 -0
- package/src/runtime/root/scheduler.ts +12 -4
- package/src/runtime/root/service-runner.ts +1 -1
- package/src/runtime/root/service.ts +41 -16
- package/src/runtime/root/status.ts +1 -0
- package/src/runtime/root/types.ts +24 -1
- package/src/runtime/shared/local-bridge-surface.ts +1 -1
- package/src/runtime/shared/process-environment.ts +19 -4
- package/src/runtime/shared/storage-capacity.ts +68 -0
- package/src/runtime/standalone-recovery/controller-home-migration.ts +1116 -0
- package/src/runtime/standalone-recovery/core.ts +984 -230
- package/src/runtime/standalone-recovery/entry.ts +182 -51
- package/src/runtime/standalone-recovery/independent-host-rescue.ts +172 -0
- package/src/runtime/standalone-recovery/installer.ts +212 -104
- package/src/runtime/standalone-recovery/mcp-server.ts +243 -0
- package/src/runtime/standalone-recovery/mutation-identity-contract.ts +21 -0
- package/src/runtime/standalone-recovery/operation-lock.ts +160 -0
- package/src/runtime/standalone-recovery/performance.ts +135 -0
- package/src/runtime/standalone-recovery/release.ts +5 -0
- package/src/runtime/watchdog/workflow-watchdog.ts +1 -1
- package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +1 -1
- package/src/runtime/workflow/schedules/engine.ts +191 -343
- package/src/runtime/workflow/schedules/pending-activation.ts +1 -1
- package/src/runtime/workflow/schedules/settlement.ts +2 -191
- package/src/runtime/workflow/schedules/store.ts +2 -607
- package/src/runtime/workflow/schedules/types.ts +2 -127
- package/src/runtime/workflow/schedules/work-continuation.ts +180 -15
- package/src/runtime/workflows/first-party/xiaohongshu.ts +122 -0
- package/src/runtime/workflows/runtime.ts +486 -0
- package/src/testing/test-governance.ts +90 -29
- package/assets/templates/helpers/archive-architecture-request.sh +0 -244
- package/docs/architecture/history/README.md +0 -7
- package/docs/architecture/history/chatgpt-handoff-facade.md +0 -397
- package/docs/architecture/history/global-hook-runtime.md +0 -166
- package/docs/architecture/history/ios-semantic-automation-provider-v2.md +0 -170
- package/docs/architecture/requests/archive/2026/20260528-154740-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-154816-assets-hooks-assets-hooks-hook-input-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-161851-ai-harness-workflow-contract-json-ai-harness-workflow-contract-json.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-161853-scripts-check-task-workflow-sh-scripts-check-task-workflow-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-161911-assets-workflow-contract-v1-json-assets-workflow-contract-v1-json.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222416-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222444-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222457-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222648-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222705-ai-hooks-ai-hooks-worktree-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222726-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222727-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-224736-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233259-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233335-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233456-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233457-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233637-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233638-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260529-000322-root-package-json.md +0 -59
- package/docs/architecture/requests/archive/2026/20260529-004238-ai-hooks-ai-hooks-lib-workflow-state-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260529-020654-ai-harness-policy-json-ai-harness-policy-json.md +0 -63
- package/docs/architecture/requests/archive/2026/20260529-094236-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094323-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094331-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094339-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094430-assets-hooks-assets-hooks-session-start-context-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094446-ai-hooks-ai-hooks-session-start-context-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260613-002040-runtime-harness-hook-adapters.md +0 -68
- package/docs/architecture/requests/archive/2026/root.md +0 -68
- package/docs/architecture/requests/archive/2026/runtime-harness-hook-adapters.md +0 -76
- package/docs/architecture/requests/archive/2026/verification-evals-checks.md +0 -68
- package/docs/architecture/requests/archive/2026/workflow-engine-contract-assets.md +0 -69
- package/scripts/archive-architecture-request.sh +0 -4
- package/scripts/run-skill-evals.ts +0 -1022
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
import { createHash, randomBytes } from 'crypto';
|
|
2
|
+
import { spawnSync } from 'child_process';
|
|
2
3
|
import { existsSync } from 'fs';
|
|
3
|
-
import {
|
|
4
|
+
import { hostname } from 'os';
|
|
5
|
+
import { isAbsolute, join, resolve } from 'path';
|
|
4
6
|
import { Command } from 'commander';
|
|
5
|
-
import { readMcpServiceOAuthPassphrase } from '
|
|
6
|
-
import {
|
|
7
|
+
import { readMcpServiceOAuthPassphrase } from '../../../adapters/mcp/auth';
|
|
8
|
+
import {
|
|
9
|
+
openAiSecureTunnelConnectArgs,
|
|
10
|
+
openAiSecureTunnelStatusArgs,
|
|
11
|
+
parseOpenAiSecureTunnelRuntimeStatus,
|
|
12
|
+
type OpenAiSecureTunnelRuntimeObservation,
|
|
13
|
+
} from '../../../adapters/mcp/tunnels/openai-secure-tunnel';
|
|
14
|
+
import { findRegisteredRepositoryByCheckoutRoot } from '../repositories/registry';
|
|
15
|
+
import {
|
|
16
|
+
resolveControllerHome,
|
|
17
|
+
rollbackStoppedControllerHomeAuthorityRelocation,
|
|
18
|
+
type StoppedControllerHomeAuthorityRelocation,
|
|
19
|
+
} from '../repositories/controller-home';
|
|
7
20
|
import { FORGE_VERSION } from '../../version';
|
|
8
21
|
import {
|
|
9
22
|
RECOVERY_GATEWAY_LABEL,
|
|
@@ -16,11 +29,16 @@ import {
|
|
|
16
29
|
} from '../../runtime/standalone-recovery/installer';
|
|
17
30
|
import {
|
|
18
31
|
RECOVERY_TOOLS,
|
|
32
|
+
RECOVERY_VERIFIER_OAUTH_CLIENT_ID,
|
|
33
|
+
RECOVERY_VERIFIER_OAUTH_CLIENT_NAME,
|
|
34
|
+
RECOVERY_VERIFIER_OAUTH_REDIRECT_URI,
|
|
19
35
|
} from '../../runtime/standalone-recovery/entry';
|
|
20
36
|
import {
|
|
21
37
|
activateRuntimeRelease,
|
|
38
|
+
defaultPrimaryRuntimeServiceConfig,
|
|
22
39
|
loadRecoveryConfig,
|
|
23
40
|
recoverPrimaryRuntime,
|
|
41
|
+
recoveryMachineIdentity,
|
|
24
42
|
restartPrimaryConnector,
|
|
25
43
|
restartPrimaryRuntime,
|
|
26
44
|
restartRecoveryGateway,
|
|
@@ -28,12 +46,25 @@ import {
|
|
|
28
46
|
rollbackPrevious,
|
|
29
47
|
runtimeStatus,
|
|
30
48
|
verifyStableRuntime,
|
|
49
|
+
type OpenAiSecureTunnelServiceConfig,
|
|
31
50
|
type PrimaryConnectorServiceConfig,
|
|
51
|
+
type RecoveryMachineIdentity,
|
|
32
52
|
type PublicTunnelServiceConfig,
|
|
53
|
+
type RecoveryTunnelServiceConfig,
|
|
54
|
+
type SystemdUserRecoveryTunnelServiceConfig,
|
|
33
55
|
} from '../../runtime/standalone-recovery/core';
|
|
34
56
|
import { readRecoveryRuntimeIdentity } from '../../runtime/standalone-recovery/release';
|
|
57
|
+
import {
|
|
58
|
+
assertRecoveryControllerHomeMigrationReady,
|
|
59
|
+
recoveryControllerHomeMigrationPreflight,
|
|
60
|
+
runLinuxControllerHomeMigrationRequest,
|
|
61
|
+
scheduleLinuxControllerHomeMigration,
|
|
62
|
+
type RecoveryControllerHomeMigrationPreflight,
|
|
63
|
+
} from '../../runtime/standalone-recovery/controller-home-migration';
|
|
35
64
|
import { configureCodegraph, ensureCodegraph } from '../tools/codegraph';
|
|
36
65
|
import { isProcessAlive } from '../../runtime/shared/process-tree';
|
|
66
|
+
import { systemdUserServicePid, systemdUserUnitName, systemdUserUnitPath } from '../controller/systemd-user';
|
|
67
|
+
import { installWindowsHostRecoveryPlugin } from '../../runtime/plugins/windows-host-recovery-install';
|
|
37
68
|
|
|
38
69
|
function output(value: unknown, json = true): void {
|
|
39
70
|
console.log(json ? JSON.stringify(value, null, 2) : String(value));
|
|
@@ -55,15 +86,80 @@ function launchdService(label?: string, plistPath?: string, role = 'RECOVERY_TUN
|
|
|
55
86
|
return { platform: 'launchd', label, ...(plistPath ? { plistPath } : {}) };
|
|
56
87
|
}
|
|
57
88
|
|
|
89
|
+
function systemdRecoveryTunnelService(unitName?: string): SystemdUserRecoveryTunnelServiceConfig | undefined {
|
|
90
|
+
if (!unitName) return undefined;
|
|
91
|
+
const normalized = systemdUserUnitName(unitName);
|
|
92
|
+
if (!/^[A-Za-z0-9_.@-]{1,180}\.service$/.test(normalized)) throw new Error('RECOVERY_TUNNEL_SYSTEMD_UNIT_INVALID');
|
|
93
|
+
return { platform: 'systemd-user', unitName: normalized };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function recoveryOpenAiTunnelDefaultAlias(
|
|
97
|
+
controllerHome: string,
|
|
98
|
+
platform: NodeJS.Platform = process.platform,
|
|
99
|
+
host = hostname(),
|
|
100
|
+
): string {
|
|
101
|
+
const suffix = createHash('sha256').update(`${host}\0${platform}\0${resolve(controllerHome)}`).digest('hex').slice(0, 12);
|
|
102
|
+
return `forge-recovery-${suffix}`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function assertDistinctRecoveryOpenAiTunnelIdentity(
|
|
106
|
+
recovery: RecoveryTunnelServiceConfig | undefined,
|
|
107
|
+
primary: PublicTunnelServiceConfig | undefined,
|
|
108
|
+
): void {
|
|
109
|
+
if (recovery?.platform !== 'openai-secure-tunnel' || primary?.platform !== 'openai-secure-tunnel') return;
|
|
110
|
+
if (recovery.alias === primary.alias) throw new Error('RECOVERY_OPENAI_TUNNEL_ALIAS_CONFLICT');
|
|
111
|
+
if (recovery.tunnelId === primary.tunnelId) throw new Error('RECOVERY_OPENAI_TUNNEL_ID_CONFLICT');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function openAiTunnelService(input: {
|
|
115
|
+
tunnelId?: string;
|
|
116
|
+
alias?: string;
|
|
117
|
+
runtimeApiKeyRef?: string;
|
|
118
|
+
profile?: string;
|
|
119
|
+
profileDir?: string;
|
|
120
|
+
adminProfile?: string;
|
|
121
|
+
mcpServerUrl: string;
|
|
122
|
+
defaultAlias: string;
|
|
123
|
+
role: string;
|
|
124
|
+
}): OpenAiSecureTunnelServiceConfig | undefined {
|
|
125
|
+
const supplied = Boolean(input.tunnelId || input.alias || input.runtimeApiKeyRef || input.profile || input.profileDir || input.adminProfile);
|
|
126
|
+
if (!supplied) return undefined;
|
|
127
|
+
if (!input.tunnelId) throw new Error(`${input.role}_OPENAI_TUNNEL_ID_REQUIRED`);
|
|
128
|
+
if (input.profileDir && !isAbsolute(input.profileDir)) throw new Error(`${input.role}_OPENAI_PROFILE_DIR_ABSOLUTE_REQUIRED`);
|
|
129
|
+
const service: OpenAiSecureTunnelServiceConfig = {
|
|
130
|
+
platform: 'openai-secure-tunnel',
|
|
131
|
+
alias: input.alias?.trim() || input.defaultAlias,
|
|
132
|
+
tunnelId: input.tunnelId.trim(),
|
|
133
|
+
mcpServerUrl: input.mcpServerUrl,
|
|
134
|
+
runtimeApiKeyRef: input.runtimeApiKeyRef?.trim(),
|
|
135
|
+
profile: input.profile?.trim(),
|
|
136
|
+
profileDir: input.profileDir ? resolve(input.profileDir) : undefined,
|
|
137
|
+
adminProfile: input.adminProfile?.trim(),
|
|
138
|
+
};
|
|
139
|
+
try { openAiSecureTunnelConnectArgs(service); } catch (error) {
|
|
140
|
+
throw new Error(`${input.role}_${error instanceof Error ? error.message : 'OPENAI_TUNNEL_CONFIG_INVALID'}`);
|
|
141
|
+
}
|
|
142
|
+
return service;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
export { recoveryControllerHomeMigrationPreflight };
|
|
147
|
+
export type { RecoveryControllerHomeMigrationPreflight };
|
|
148
|
+
|
|
58
149
|
export interface RecoveryConnectorDependencies {
|
|
150
|
+
platform?: NodeJS.Platform;
|
|
59
151
|
pathExists?: (path: string) => boolean;
|
|
60
152
|
launchdPid?: (role: 'gateway' | 'watchdog') => number | undefined;
|
|
153
|
+
systemdPid?: (role: 'gateway' | 'watchdog') => number | undefined;
|
|
61
154
|
tunnelLaunchdPid?: (label: string) => number | undefined;
|
|
155
|
+
tunnelSystemdPid?: (unitName: string) => number | undefined;
|
|
156
|
+
openAiTunnelStatus?: (service: OpenAiSecureTunnelServiceConfig) => OpenAiSecureTunnelRuntimeObservation;
|
|
62
157
|
processAlive?: (pid: number) => boolean;
|
|
63
158
|
}
|
|
64
159
|
|
|
65
160
|
export interface RecoveryConnectorDescriptor {
|
|
66
161
|
name: 'Forge Recovery';
|
|
162
|
+
identity: RecoveryMachineIdentity;
|
|
67
163
|
transport: 'streamable_http';
|
|
68
164
|
url: string;
|
|
69
165
|
public: boolean;
|
|
@@ -78,9 +174,23 @@ export interface RecoveryConnectorDescriptor {
|
|
|
78
174
|
};
|
|
79
175
|
healthUrl: string;
|
|
80
176
|
services: {
|
|
81
|
-
gateway: { label: string; plistInstalled: boolean; running: boolean; pid?: number };
|
|
82
|
-
watchdog: { label: string; plistInstalled: boolean; running: boolean; pid?: number };
|
|
83
|
-
tunnel: {
|
|
177
|
+
gateway: { label: string; platform: 'launchd' | 'systemd-user'; serviceInstalled: boolean; plistInstalled: boolean; running: boolean; pid?: number };
|
|
178
|
+
watchdog: { label: string; platform: 'launchd' | 'systemd-user'; serviceInstalled: boolean; plistInstalled: boolean; running: boolean; pid?: number };
|
|
179
|
+
tunnel: {
|
|
180
|
+
configured: boolean;
|
|
181
|
+
platform?: 'launchd' | 'systemd-user' | 'openai-secure-tunnel';
|
|
182
|
+
label?: string;
|
|
183
|
+
unitName?: string;
|
|
184
|
+
alias?: string;
|
|
185
|
+
tunnelId?: string;
|
|
186
|
+
serviceInstalled?: boolean;
|
|
187
|
+
plistInstalled: boolean;
|
|
188
|
+
restartSafe: boolean;
|
|
189
|
+
running: boolean;
|
|
190
|
+
healthy?: boolean;
|
|
191
|
+
ready?: boolean;
|
|
192
|
+
pid?: number;
|
|
193
|
+
};
|
|
84
194
|
};
|
|
85
195
|
tools: string[];
|
|
86
196
|
warnings: string[];
|
|
@@ -100,25 +210,66 @@ export function recoveryConnectorDescriptor(
|
|
|
100
210
|
const passphraseConfigured = Boolean(readMcpServiceOAuthPassphrase(home));
|
|
101
211
|
const gatewayIdentity = readRecoveryRuntimeIdentity(home, 'gateway');
|
|
102
212
|
const watchdogIdentity = readRecoveryRuntimeIdentity(home, 'watchdog');
|
|
213
|
+
const platform = dependencies.platform ?? process.platform;
|
|
214
|
+
const servicePlatform: 'launchd' | 'systemd-user' = platform === 'linux' ? 'systemd-user' : 'launchd';
|
|
103
215
|
const pathExists = dependencies.pathExists ?? existsSync;
|
|
104
216
|
const launchdPid = dependencies.launchdPid ?? recoveryLaunchdPid;
|
|
217
|
+
const systemdPid = dependencies.systemdPid ?? ((role: 'gateway' | 'watchdog') => systemdUserServicePid(role === 'gateway' ? RECOVERY_GATEWAY_LABEL : RECOVERY_WATCHDOG_LABEL));
|
|
218
|
+
const managedPid = (role: 'gateway' | 'watchdog') => servicePlatform === 'systemd-user' ? systemdPid(role) : launchdPid(role);
|
|
105
219
|
const processAlive = dependencies.processAlive ?? isProcessAlive;
|
|
106
|
-
const
|
|
220
|
+
const configuredTunnel = config.recoveryTunnelService;
|
|
221
|
+
const tunnelService = configuredTunnel?.platform === 'launchd' ? configuredTunnel : undefined;
|
|
222
|
+
const systemdTunnelService = configuredTunnel?.platform === 'systemd-user' ? configuredTunnel : undefined;
|
|
223
|
+
const openAiTunnelService = configuredTunnel?.platform === 'openai-secure-tunnel' ? configuredTunnel : undefined;
|
|
107
224
|
const tunnelContract = tunnelService ? inspectRecoveryTunnelLaunchdContract(tunnelService) : undefined;
|
|
108
225
|
const tunnelLaunchdPid = tunnelService
|
|
109
226
|
? (dependencies.tunnelLaunchdPid ?? recoveryLaunchdServicePid)(tunnelService.label)
|
|
110
227
|
: undefined;
|
|
111
228
|
const tunnelPlistInstalled = Boolean(tunnelContract?.plistPath && pathExists(tunnelContract.plistPath));
|
|
112
|
-
const
|
|
113
|
-
const
|
|
229
|
+
const systemdTunnelUnitName = systemdTunnelService?.unitName.trim();
|
|
230
|
+
const systemdTunnelIdentityValid = Boolean(systemdTunnelUnitName && /^[A-Za-z0-9_.@-]{1,180}\.service$/.test(systemdTunnelUnitName));
|
|
231
|
+
const systemdTunnelInstalled = Boolean(systemdTunnelIdentityValid && pathExists(systemdUserUnitPath(systemdTunnelUnitName!)));
|
|
232
|
+
const systemdTunnelPid = systemdTunnelIdentityValid
|
|
233
|
+
? (dependencies.tunnelSystemdPid ?? ((unitName: string) => systemdUserServicePid(unitName)))(systemdTunnelUnitName!)
|
|
234
|
+
: undefined;
|
|
235
|
+
const openAiTunnelStatus = openAiTunnelService
|
|
236
|
+
? (dependencies.openAiTunnelStatus ?? ((service: OpenAiSecureTunnelServiceConfig) => {
|
|
237
|
+
const status = spawnSync('tunnel-client', openAiSecureTunnelStatusArgs(service.alias, service), {
|
|
238
|
+
encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 15_000,
|
|
239
|
+
});
|
|
240
|
+
return parseOpenAiSecureTunnelRuntimeStatus(status.stdout || '{}', { alias: service.alias, tunnelId: service.tunnelId, mcpServerUrl: service.mcpServerUrl });
|
|
241
|
+
}))(openAiTunnelService)
|
|
242
|
+
: undefined;
|
|
243
|
+
let openAiRestartSafe = false;
|
|
244
|
+
if (openAiTunnelService) {
|
|
245
|
+
try { openAiSecureTunnelConnectArgs(openAiTunnelService); openAiRestartSafe = true; } catch { openAiRestartSafe = false; }
|
|
246
|
+
}
|
|
247
|
+
const tunnelRestartSafe = openAiTunnelService
|
|
248
|
+
? openAiRestartSafe
|
|
249
|
+
: systemdTunnelService
|
|
250
|
+
? systemdTunnelIdentityValid && systemdTunnelInstalled
|
|
251
|
+
: Boolean(tunnelContract?.restartSafe);
|
|
252
|
+
const tunnelRunning = openAiTunnelService
|
|
253
|
+
? openAiTunnelStatus?.running === true
|
|
254
|
+
: systemdTunnelService
|
|
255
|
+
? Boolean(systemdTunnelPid && processAlive(systemdTunnelPid))
|
|
256
|
+
: Boolean(tunnelLaunchdPid && processAlive(tunnelLaunchdPid));
|
|
257
|
+
const tunnelHealthy = openAiTunnelService ? openAiTunnelStatus?.healthy === true : tunnelRunning;
|
|
258
|
+
const tunnelReady = openAiTunnelService ? openAiTunnelStatus?.ok === true : tunnelRestartSafe && tunnelRunning;
|
|
114
259
|
const gatewayPlistInstalled = pathExists(authority.gatewayLaunchAgent);
|
|
115
260
|
const watchdogPlistInstalled = pathExists(authority.watchdogLaunchAgent);
|
|
116
|
-
const
|
|
117
|
-
|
|
261
|
+
const gatewayServiceInstalled = servicePlatform === 'systemd-user'
|
|
262
|
+
? pathExists(systemdUserUnitPath(RECOVERY_GATEWAY_LABEL))
|
|
263
|
+
: gatewayPlistInstalled;
|
|
264
|
+
const watchdogServiceInstalled = servicePlatform === 'systemd-user'
|
|
265
|
+
? pathExists(systemdUserUnitPath(RECOVERY_WATCHDOG_LABEL))
|
|
266
|
+
: watchdogPlistInstalled;
|
|
267
|
+
const gatewayManagedPid = managedPid('gateway');
|
|
268
|
+
const watchdogManagedPid = managedPid('watchdog');
|
|
118
269
|
const gatewayRunning = Boolean(
|
|
119
270
|
gatewayIdentity
|
|
120
|
-
&&
|
|
121
|
-
&& gatewayIdentity.pid ===
|
|
271
|
+
&& gatewayManagedPid
|
|
272
|
+
&& gatewayIdentity.pid === gatewayManagedPid
|
|
122
273
|
&& processAlive(gatewayIdentity.pid)
|
|
123
274
|
&& authority.current
|
|
124
275
|
&& gatewayIdentity.releaseRevision === authority.current.releaseRevision
|
|
@@ -126,33 +277,40 @@ export function recoveryConnectorDescriptor(
|
|
|
126
277
|
);
|
|
127
278
|
const watchdogRunning = Boolean(
|
|
128
279
|
watchdogIdentity
|
|
129
|
-
&&
|
|
130
|
-
&& watchdogIdentity.pid ===
|
|
280
|
+
&& watchdogManagedPid
|
|
281
|
+
&& watchdogIdentity.pid === watchdogManagedPid
|
|
131
282
|
&& processAlive(watchdogIdentity.pid)
|
|
132
283
|
&& authority.current
|
|
133
284
|
&& watchdogIdentity.releaseRevision === authority.current.releaseRevision
|
|
134
285
|
&& watchdogIdentity.manifestSha256 === authority.current.manifestSha256,
|
|
135
286
|
);
|
|
136
|
-
const installed = Boolean(authority.current &&
|
|
287
|
+
const installed = Boolean(authority.current && gatewayServiceInstalled && watchdogServiceInstalled);
|
|
137
288
|
const publicEndpoint = Boolean(configuredUrl?.startsWith('https://'));
|
|
138
289
|
const warnings: string[] = [];
|
|
139
290
|
if (!authority.current) warnings.push('No current immutable Forge Recovery release is installed. Run forge recovery install.');
|
|
140
|
-
if (!
|
|
291
|
+
if (!gatewayServiceInstalled || !watchdogServiceInstalled) warnings.push(`Forge Recovery ${servicePlatform} services are not fully installed. Run forge recovery install.`);
|
|
141
292
|
if (!gatewayRunning || !watchdogRunning) warnings.push('Forge Recovery Gateway or Watchdog is not running on the current Recovery release.');
|
|
142
|
-
if (!
|
|
143
|
-
else if (
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
else if (!
|
|
293
|
+
if (!configuredTunnel) warnings.push('Recovery is loopback-only. Configure a dedicated OpenAI Secure MCP Tunnel or an HTTPS tunnel service before adding it to ChatGPT.');
|
|
294
|
+
else if (openAiTunnelService) {
|
|
295
|
+
if (!tunnelRestartSafe) warnings.push('The dedicated OpenAI Recovery tunnel must use a valid alias, tunnel id, loopback MCP endpoint, and env:/file: runtime API key reference.');
|
|
296
|
+
else if (!tunnelRunning) warnings.push(`The dedicated OpenAI Recovery tunnel runtime ${openAiTunnelService.alias} is not running.`);
|
|
297
|
+
else if (!tunnelHealthy || !tunnelReady) warnings.push(`The dedicated OpenAI Recovery tunnel runtime ${openAiTunnelService.alias} is not healthy and ready.`);
|
|
298
|
+
} else if (!configuredUrl) warnings.push('The configured Recovery tunnel requires --recovery-public-url.');
|
|
299
|
+
else if (!publicEndpoint) warnings.push('ChatGPT Recovery Connector requires an HTTPS public endpoint for managed public tunnels.');
|
|
300
|
+
else if (systemdTunnelService && !systemdTunnelInstalled) warnings.push(`The dedicated Forge Recovery systemd-user tunnel unit ${systemdTunnelUnitName ?? systemdTunnelService.unitName} is not installed.`);
|
|
301
|
+
else if (systemdTunnelService && !tunnelRunning) warnings.push(`The dedicated Forge Recovery systemd-user tunnel unit ${systemdTunnelUnitName} is not running.`);
|
|
302
|
+
else if (!systemdTunnelService && !tunnelPlistInstalled) warnings.push('The dedicated Forge Recovery tunnel plist is not installed.');
|
|
303
|
+
else if (!systemdTunnelService && !tunnelRestartSafe) warnings.push('The dedicated Forge Recovery tunnel plist must use RunAtLoad=true and unconditional KeepAlive=true.');
|
|
304
|
+
else if (!systemdTunnelService && !tunnelRunning) warnings.push('The dedicated Forge Recovery tunnel service is not running.');
|
|
148
305
|
if (!passphraseConfigured) warnings.push('MCP OAuth passphrase is not configured. Run forge mcp setup chatgpt first.');
|
|
149
306
|
|
|
150
307
|
return {
|
|
151
308
|
name: 'Forge Recovery',
|
|
309
|
+
identity: recoveryMachineIdentity(config, { platform }),
|
|
152
310
|
transport: 'streamable_http',
|
|
153
311
|
url,
|
|
154
312
|
public: publicEndpoint,
|
|
155
|
-
readyForChatGPT: installed && gatewayRunning && watchdogRunning &&
|
|
313
|
+
readyForChatGPT: installed && gatewayRunning && watchdogRunning && tunnelReady && (openAiTunnelService ? true : publicEndpoint) && passphraseConfigured,
|
|
156
314
|
installed,
|
|
157
315
|
currentRelease: authority.current?.releaseRevision,
|
|
158
316
|
previousRelease: authority.previous?.releaseRevision,
|
|
@@ -165,23 +323,32 @@ export function recoveryConnectorDescriptor(
|
|
|
165
323
|
services: {
|
|
166
324
|
gateway: {
|
|
167
325
|
label: RECOVERY_GATEWAY_LABEL,
|
|
326
|
+
platform: servicePlatform,
|
|
327
|
+
serviceInstalled: gatewayServiceInstalled,
|
|
168
328
|
plistInstalled: gatewayPlistInstalled,
|
|
169
329
|
running: gatewayRunning,
|
|
170
330
|
...(gatewayIdentity ? { pid: gatewayIdentity.pid } : {}),
|
|
171
331
|
},
|
|
172
332
|
watchdog: {
|
|
173
333
|
label: RECOVERY_WATCHDOG_LABEL,
|
|
334
|
+
platform: servicePlatform,
|
|
335
|
+
serviceInstalled: watchdogServiceInstalled,
|
|
174
336
|
plistInstalled: watchdogPlistInstalled,
|
|
175
337
|
running: watchdogRunning,
|
|
176
338
|
...(watchdogIdentity ? { pid: watchdogIdentity.pid } : {}),
|
|
177
339
|
},
|
|
178
340
|
tunnel: {
|
|
179
|
-
configured: Boolean(
|
|
341
|
+
configured: Boolean(configuredTunnel),
|
|
342
|
+
...(configuredTunnel ? { platform: configuredTunnel.platform } : {}),
|
|
180
343
|
...(tunnelService ? { label: tunnelService.label } : {}),
|
|
344
|
+
...(systemdTunnelService ? { unitName: systemdTunnelUnitName ?? systemdTunnelService.unitName } : {}),
|
|
345
|
+
...(openAiTunnelService ? { alias: openAiTunnelService.alias, tunnelId: openAiTunnelService.tunnelId } : {}),
|
|
346
|
+
serviceInstalled: openAiTunnelService ? openAiRestartSafe : systemdTunnelService ? systemdTunnelInstalled : tunnelPlistInstalled,
|
|
181
347
|
plistInstalled: tunnelPlistInstalled,
|
|
182
348
|
restartSafe: tunnelRestartSafe,
|
|
183
349
|
running: tunnelRunning,
|
|
184
|
-
...(
|
|
350
|
+
...(openAiTunnelService ? { healthy: tunnelHealthy, ready: tunnelReady } : {}),
|
|
351
|
+
...(systemdTunnelPid ? { pid: systemdTunnelPid } : tunnelLaunchdPid ? { pid: tunnelLaunchdPid } : {}),
|
|
185
352
|
},
|
|
186
353
|
},
|
|
187
354
|
tools: RECOVERY_TOOLS.map((tool) => tool.name),
|
|
@@ -189,6 +356,12 @@ export function recoveryConnectorDescriptor(
|
|
|
189
356
|
};
|
|
190
357
|
}
|
|
191
358
|
|
|
359
|
+
export function recoveryConnectorHasExternalTransport(
|
|
360
|
+
connector: Pick<RecoveryConnectorDescriptor, 'public' | 'services'>,
|
|
361
|
+
): boolean {
|
|
362
|
+
return connector.public || connector.services.tunnel.platform === 'openai-secure-tunnel';
|
|
363
|
+
}
|
|
364
|
+
|
|
192
365
|
export interface RecoveryConnectorVerificationDependencies {
|
|
193
366
|
fetcher?: typeof fetch;
|
|
194
367
|
random?: (size: number) => Buffer;
|
|
@@ -203,7 +376,15 @@ export interface RecoveryConnectorVerificationResult {
|
|
|
203
376
|
authorizationMetadata: { ok: boolean; status?: number };
|
|
204
377
|
protectedResourceMetadata: { ok: boolean; status?: number };
|
|
205
378
|
unauthenticatedChallenge: { ok: boolean; status?: number };
|
|
206
|
-
oauthPkce: {
|
|
379
|
+
oauthPkce: {
|
|
380
|
+
ok: boolean;
|
|
381
|
+
registrationStatus?: number;
|
|
382
|
+
authorizationStatus?: number;
|
|
383
|
+
tokenStatus?: number;
|
|
384
|
+
clientOwner?: 'recovery_verifier';
|
|
385
|
+
clientReused?: boolean;
|
|
386
|
+
credentialRetirement?: 'shared_recovery_token_not_individually_revocable';
|
|
387
|
+
};
|
|
207
388
|
mcp: {
|
|
208
389
|
ok: boolean;
|
|
209
390
|
initializeStatus?: number;
|
|
@@ -224,7 +405,25 @@ function jsonObject(value: unknown): Record<string, unknown> {
|
|
|
224
405
|
}
|
|
225
406
|
|
|
226
407
|
async function responseJson(response: Response): Promise<Record<string, unknown>> {
|
|
227
|
-
try {
|
|
408
|
+
try {
|
|
409
|
+
const text = await response.text();
|
|
410
|
+
if (!text.trim()) return {};
|
|
411
|
+
if (/text\/event-stream/i.test(response.headers.get('content-type') ?? '')) {
|
|
412
|
+
for (const line of text.split(/\r?\n/)) {
|
|
413
|
+
if (!line.startsWith('data:')) continue;
|
|
414
|
+
try {
|
|
415
|
+
const payload = jsonObject(JSON.parse(line.slice('data:'.length).trim()));
|
|
416
|
+
if (Object.keys(payload).length > 0) return payload;
|
|
417
|
+
} catch {
|
|
418
|
+
// Ignore non-JSON SSE fields and keep looking for the MCP data event.
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
return {};
|
|
422
|
+
}
|
|
423
|
+
return jsonObject(JSON.parse(text));
|
|
424
|
+
} catch {
|
|
425
|
+
return {};
|
|
426
|
+
}
|
|
228
427
|
}
|
|
229
428
|
|
|
230
429
|
export async function verifyRecoveryConnector(
|
|
@@ -250,7 +449,9 @@ export async function verifyRecoveryConnector(
|
|
|
250
449
|
});
|
|
251
450
|
|
|
252
451
|
if (!connector.readyForChatGPT) failures.push(...connector.warnings.map((warning) => `readiness: ${warning}`));
|
|
253
|
-
if (!connector
|
|
452
|
+
if (!recoveryConnectorHasExternalTransport(connector)) {
|
|
453
|
+
failures.push('readiness: Recovery Connector has neither an HTTPS public endpoint nor a dedicated OpenAI Secure MCP Tunnel.');
|
|
454
|
+
}
|
|
254
455
|
|
|
255
456
|
const origin = new URL(connector.url).origin;
|
|
256
457
|
try {
|
|
@@ -325,12 +526,13 @@ export async function verifyRecoveryConnector(
|
|
|
325
526
|
failures.push('oauthPkce: MCP OAuth passphrase is not configured.');
|
|
326
527
|
} else {
|
|
327
528
|
try {
|
|
328
|
-
const redirectUri =
|
|
529
|
+
const redirectUri = RECOVERY_VERIFIER_OAUTH_REDIRECT_URI;
|
|
329
530
|
const registration = await request(`${origin}/recovery/oauth/register`, {
|
|
330
531
|
method: 'POST',
|
|
331
532
|
headers: { 'content-type': 'application/json' },
|
|
332
533
|
body: JSON.stringify({
|
|
333
|
-
|
|
534
|
+
client_id: RECOVERY_VERIFIER_OAUTH_CLIENT_ID,
|
|
535
|
+
client_name: RECOVERY_VERIFIER_OAUTH_CLIENT_NAME,
|
|
334
536
|
redirect_uris: [redirectUri],
|
|
335
537
|
grant_types: ['authorization_code'],
|
|
336
538
|
response_types: ['code'],
|
|
@@ -339,7 +541,11 @@ export async function verifyRecoveryConnector(
|
|
|
339
541
|
});
|
|
340
542
|
const registrationBody = await responseJson(registration);
|
|
341
543
|
const clientId = typeof registrationBody.client_id === 'string' ? registrationBody.client_id : '';
|
|
342
|
-
|
|
544
|
+
const clientOwner = registrationBody.forge_client_owner;
|
|
545
|
+
const clientReused = registrationBody.forge_client_reused === true;
|
|
546
|
+
if (registration.status !== 201 || clientId !== RECOVERY_VERIFIER_OAUTH_CLIENT_ID || clientOwner !== 'recovery_verifier') {
|
|
547
|
+
throw new Error(`dynamic verifier registration HTTP ${registration.status}`);
|
|
548
|
+
}
|
|
343
549
|
|
|
344
550
|
const verifier = random(32).toString('base64url');
|
|
345
551
|
const challenge = createHash('sha256').update(verifier).digest('base64url');
|
|
@@ -388,8 +594,16 @@ export async function verifyRecoveryConnector(
|
|
|
388
594
|
registrationStatus: registration.status,
|
|
389
595
|
authorizationStatus: authorization.status,
|
|
390
596
|
tokenStatus: tokenResponse.status,
|
|
597
|
+
clientOwner: 'recovery_verifier',
|
|
598
|
+
clientReused,
|
|
599
|
+
// Recovery MCP currently uses one configured shared bearer token, so
|
|
600
|
+
// there is no verifier-specific credential that can be revoked without
|
|
601
|
+
// revoking Recovery itself. The reserved client slot keeps verifier
|
|
602
|
+
// registration state bounded across success and failure loops.
|
|
603
|
+
credentialRetirement: 'shared_recovery_token_not_individually_revocable',
|
|
391
604
|
};
|
|
392
605
|
|
|
606
|
+
let mcpSessionId: string | undefined;
|
|
393
607
|
const rpc = async (id: number | undefined, method: string, params?: Record<string, unknown>) => {
|
|
394
608
|
const response = await request(connector.url, {
|
|
395
609
|
method: 'POST',
|
|
@@ -398,53 +612,84 @@ export async function verifyRecoveryConnector(
|
|
|
398
612
|
accept: 'application/json, text/event-stream',
|
|
399
613
|
'content-type': 'application/json',
|
|
400
614
|
'mcp-protocol-version': '2025-06-18',
|
|
615
|
+
...(mcpSessionId ? { 'mcp-session-id': mcpSessionId } : {}),
|
|
401
616
|
},
|
|
402
617
|
body: JSON.stringify({ jsonrpc: '2.0', ...(id === undefined ? {} : { id }), method, ...(params ? { params } : {}) }),
|
|
403
618
|
});
|
|
404
|
-
if (method === 'notifications/initialized')
|
|
619
|
+
if (method === 'notifications/initialized') {
|
|
620
|
+
if (response.status !== 202) throw new Error(`${method} HTTP ${response.status}`);
|
|
621
|
+
return { response, result: {} as Record<string, unknown> };
|
|
622
|
+
}
|
|
405
623
|
const body = await responseJson(response);
|
|
406
624
|
const rpcError = jsonObject(body.error);
|
|
407
625
|
if (response.status !== 200 || Object.keys(rpcError).length > 0) {
|
|
408
626
|
throw new Error(`${method} HTTP ${response.status}${typeof rpcError.message === 'string' ? `: ${rpcError.message}` : ''}`);
|
|
409
627
|
}
|
|
628
|
+
if (method === 'initialize') {
|
|
629
|
+
const returnedSessionId = response.headers.get('mcp-session-id')?.trim();
|
|
630
|
+
if (!returnedSessionId) throw new Error('initialize HTTP 200: Mcp-Session-Id missing');
|
|
631
|
+
mcpSessionId = returnedSessionId;
|
|
632
|
+
}
|
|
410
633
|
return { response, result: jsonObject(body.result) };
|
|
411
634
|
};
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
&& initializedNotification.response.status === 202
|
|
430
|
-
&& initializedResult.protocolVersion === '2025-06-18'
|
|
431
|
-
&& serverInfo.name === 'forge-standalone-recovery'
|
|
432
|
-
&& serverInfo.version === FORGE_VERSION
|
|
433
|
-
&& JSON.stringify(tools) === JSON.stringify(expectedTools)
|
|
434
|
-
&& runtimeStatusCall.response.status === 200
|
|
435
|
-
&& listReleasesCall.response.status === 200;
|
|
436
|
-
probes.mcp = {
|
|
437
|
-
ok: mcpOk,
|
|
438
|
-
initializeStatus: initialized.response.status,
|
|
439
|
-
initializedNotificationStatus: initializedNotification.response.status,
|
|
440
|
-
protocolVersion: typeof initializedResult.protocolVersion === 'string' ? initializedResult.protocolVersion : undefined,
|
|
441
|
-
serverName: typeof serverInfo.name === 'string' ? serverInfo.name : undefined,
|
|
442
|
-
serverVersion: typeof serverInfo.version === 'string' ? serverInfo.version : undefined,
|
|
443
|
-
tools,
|
|
444
|
-
runtimeStatusCall: runtimeStatusCall.response.status === 200,
|
|
445
|
-
listReleasesCall: listReleasesCall.response.status === 200,
|
|
635
|
+
const closeMcpSession = async (): Promise<void> => {
|
|
636
|
+
const sessionId = mcpSessionId;
|
|
637
|
+
if (!sessionId) return;
|
|
638
|
+
mcpSessionId = undefined;
|
|
639
|
+
try {
|
|
640
|
+
await request(connector.url, {
|
|
641
|
+
method: 'DELETE',
|
|
642
|
+
headers: {
|
|
643
|
+
authorization: `Bearer ${accessToken}`,
|
|
644
|
+
accept: 'application/json, text/event-stream',
|
|
645
|
+
'mcp-protocol-version': '2025-06-18',
|
|
646
|
+
'mcp-session-id': sessionId,
|
|
647
|
+
},
|
|
648
|
+
});
|
|
649
|
+
} catch {
|
|
650
|
+
// Session cleanup is best-effort and must not hide the verifier result.
|
|
651
|
+
}
|
|
446
652
|
};
|
|
447
|
-
|
|
653
|
+
|
|
654
|
+
try {
|
|
655
|
+
const initialized = await rpc(11, 'initialize', {
|
|
656
|
+
protocolVersion: '2025-06-18',
|
|
657
|
+
capabilities: {},
|
|
658
|
+
clientInfo: { name: 'forge-recovery-verifier', version: FORGE_VERSION },
|
|
659
|
+
});
|
|
660
|
+
const initializedNotification = await rpc(undefined, 'notifications/initialized');
|
|
661
|
+
const listed = await rpc(12, 'tools/list');
|
|
662
|
+
const tools = Array.isArray(listed.result.tools)
|
|
663
|
+
? listed.result.tools.map((tool) => jsonObject(tool).name).filter((name): name is string => typeof name === 'string')
|
|
664
|
+
: [];
|
|
665
|
+
const expectedTools = RECOVERY_TOOLS.map((tool) => tool.name);
|
|
666
|
+
const runtimeStatusCall = await rpc(13, 'tools/call', { name: 'runtime_status', arguments: {} });
|
|
667
|
+
const listReleasesCall = await rpc(14, 'tools/call', { name: 'list_releases', arguments: {} });
|
|
668
|
+
const initializedResult = initialized.result;
|
|
669
|
+
const serverInfo = jsonObject(initializedResult.serverInfo);
|
|
670
|
+
const mcpOk = initialized.response.status === 200
|
|
671
|
+
&& initializedNotification.response.status === 202
|
|
672
|
+
&& initializedResult.protocolVersion === '2025-06-18'
|
|
673
|
+
&& serverInfo.name === 'forge-standalone-recovery'
|
|
674
|
+
&& serverInfo.version === FORGE_VERSION
|
|
675
|
+
&& JSON.stringify(tools) === JSON.stringify(expectedTools)
|
|
676
|
+
&& runtimeStatusCall.response.status === 200
|
|
677
|
+
&& listReleasesCall.response.status === 200;
|
|
678
|
+
probes.mcp = {
|
|
679
|
+
ok: mcpOk,
|
|
680
|
+
initializeStatus: initialized.response.status,
|
|
681
|
+
initializedNotificationStatus: initializedNotification.response.status,
|
|
682
|
+
protocolVersion: typeof initializedResult.protocolVersion === 'string' ? initializedResult.protocolVersion : undefined,
|
|
683
|
+
serverName: typeof serverInfo.name === 'string' ? serverInfo.name : undefined,
|
|
684
|
+
serverVersion: typeof serverInfo.version === 'string' ? serverInfo.version : undefined,
|
|
685
|
+
tools,
|
|
686
|
+
runtimeStatusCall: runtimeStatusCall.response.status === 200,
|
|
687
|
+
listReleasesCall: listReleasesCall.response.status === 200,
|
|
688
|
+
};
|
|
689
|
+
if (!mcpOk) failures.push('mcp: initialize, Forge version, tool surface, or read-only calls did not match the Recovery contract.');
|
|
690
|
+
} finally {
|
|
691
|
+
await closeMcpSession();
|
|
692
|
+
}
|
|
448
693
|
} catch (error) {
|
|
449
694
|
failures.push(`oauthPkce/mcp: ${error instanceof Error ? error.message : String(error)}`);
|
|
450
695
|
}
|
|
@@ -534,7 +779,7 @@ export function buildRecoveryCommand(): Command {
|
|
|
534
779
|
recoveryPublicUrl: current.recoveryPublicUrl,
|
|
535
780
|
recoveryTunnelService: current.recoveryTunnelService,
|
|
536
781
|
primaryPublicTunnelService: current.primaryPublicTunnelService,
|
|
537
|
-
primaryRuntimeService: current.primaryRuntimeService ??
|
|
782
|
+
primaryRuntimeService: current.primaryRuntimeService ?? defaultPrimaryRuntimeServiceConfig(),
|
|
538
783
|
primaryConnectorService,
|
|
539
784
|
});
|
|
540
785
|
const refreshed = loadRecoveryConfig(home);
|
|
@@ -620,6 +865,92 @@ export function buildRecoveryCommand(): Command {
|
|
|
620
865
|
if (!result.ok) process.exitCode = 1;
|
|
621
866
|
});
|
|
622
867
|
|
|
868
|
+
command.command('migrate-controller-home')
|
|
869
|
+
.description('Transactionally relocate Forge Controller Home on Linux/WSL through an independent transient systemd worker')
|
|
870
|
+
.requiredOption('--from <path>', 'Current Controller Home authority')
|
|
871
|
+
.requiredOption('--controller-home <path>', 'Destination user-level Controller Home')
|
|
872
|
+
.option('--archive-suffix <suffix>', 'Stable suffix for an authority-free destination shell archive')
|
|
873
|
+
.option('--timeout-ms <ms>', 'Overall migration transaction timeout in milliseconds', '240000')
|
|
874
|
+
.action(async (opts: { from: string; controllerHome: string; archiveSuffix?: string; timeoutMs: string }) => {
|
|
875
|
+
if (process.platform !== 'linux') throw new Error('RECOVERY_CONTROLLER_HOME_MIGRATION_LINUX_ONLY');
|
|
876
|
+
const timeoutMs = Number(opts.timeoutMs);
|
|
877
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs < 30_000 || timeoutMs > 900_000) {
|
|
878
|
+
throw new Error('RECOVERY_CONTROLLER_HOME_MIGRATION_TIMEOUT_INVALID');
|
|
879
|
+
}
|
|
880
|
+
const scheduled = scheduleLinuxControllerHomeMigration({
|
|
881
|
+
sourceHome: resolve(opts.from),
|
|
882
|
+
destinationHome: resolveControllerHome(opts.controllerHome),
|
|
883
|
+
...(opts.archiveSuffix ? { archiveSuffix: opts.archiveSuffix } : {}),
|
|
884
|
+
timeoutMs,
|
|
885
|
+
});
|
|
886
|
+
output({
|
|
887
|
+
status: 'migration_scheduled',
|
|
888
|
+
operationId: scheduled.request.operationId,
|
|
889
|
+
sourceHome: scheduled.request.sourceHome,
|
|
890
|
+
destinationHome: scheduled.request.destinationHome,
|
|
891
|
+
workerUnit: scheduled.request.workerUnit,
|
|
892
|
+
receiptPath: scheduled.request.receiptPath,
|
|
893
|
+
logPath: scheduled.request.logPath,
|
|
894
|
+
preflight: scheduled.preflight,
|
|
895
|
+
});
|
|
896
|
+
});
|
|
897
|
+
|
|
898
|
+
command.command('migrate-controller-home-worker')
|
|
899
|
+
.description('Internal Linux/WSL transient worker for a durable Controller Home migration request')
|
|
900
|
+
.requiredOption('--request <path>', 'Durable migration request written before service cutover')
|
|
901
|
+
.action(async (opts: { request: string }) => {
|
|
902
|
+
if (process.platform !== 'linux') throw new Error('RECOVERY_CONTROLLER_HOME_MIGRATION_LINUX_ONLY');
|
|
903
|
+
const receipt = await runLinuxControllerHomeMigrationRequest(resolve(opts.request));
|
|
904
|
+
// A durable terminal receipt, including failed-after-rollback, is a completed worker transaction.
|
|
905
|
+
// Only an unhandled process failure before a terminal receipt should trigger systemd Restart=on-failure.
|
|
906
|
+
output(receipt);
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
command.command('rollback-controller-home-migration')
|
|
910
|
+
.description('Rollback a stopped Controller Home relocation before any writer is restarted')
|
|
911
|
+
.requiredOption('--from <path>', 'Original Controller Home path')
|
|
912
|
+
.requiredOption('--controller-home <path>', 'Relocated user-level Controller Home path')
|
|
913
|
+
.option('--archived-destination-home <path>', 'Archived authority-free destination shell returned by migration')
|
|
914
|
+
.action(async (opts: { from: string; controllerHome: string; archivedDestinationHome?: string }) => {
|
|
915
|
+
if (process.platform !== 'linux') throw new Error('RECOVERY_CONTROLLER_HOME_MIGRATION_LINUX_ONLY');
|
|
916
|
+
const sourceHome = resolve(opts.from);
|
|
917
|
+
const destinationHome = resolveControllerHome(opts.controllerHome);
|
|
918
|
+
const preflight = recoveryControllerHomeMigrationPreflight(destinationHome, sourceHome);
|
|
919
|
+
if (preflight.liveOwners.length > 0) {
|
|
920
|
+
throw new Error(`RECOVERY_CONTROLLER_HOME_MIGRATION_OWNERS_LIVE: ${preflight.liveOwners.map((entry) => `${entry.label}:${entry.pid}`).join(',')}`);
|
|
921
|
+
}
|
|
922
|
+
const relocation: StoppedControllerHomeAuthorityRelocation = {
|
|
923
|
+
migrated: true,
|
|
924
|
+
sourceHome,
|
|
925
|
+
destinationHome,
|
|
926
|
+
...(opts.archivedDestinationHome ? { archivedDestinationHome: resolve(opts.archivedDestinationHome) } : {}),
|
|
927
|
+
};
|
|
928
|
+
rollbackStoppedControllerHomeAuthorityRelocation(relocation);
|
|
929
|
+
output({ status: 'rolled_back', relocation });
|
|
930
|
+
});
|
|
931
|
+
|
|
932
|
+
command.command('windows-host-register')
|
|
933
|
+
.description('Register the Windows-host Recovery provider using discovered or explicitly bound host paths')
|
|
934
|
+
.option('--controller-home <path>', 'Override Controller Home')
|
|
935
|
+
.option('--rescue-root <path>', 'Override the local Recovery helper root')
|
|
936
|
+
.option('--recovery-script-host <path>', 'Host-visible path to ForgeRecovery.ps1')
|
|
937
|
+
.option('--recovery-script-windows <path>', 'Windows path to ForgeRecovery.ps1')
|
|
938
|
+
.option('--powershell-host <path>', 'Host-visible path to powershell.exe')
|
|
939
|
+
.option('--powershell-windows <path>', 'Windows path to powershell.exe')
|
|
940
|
+
.option('--disabled', 'Register the provider disabled')
|
|
941
|
+
.action((opts) => {
|
|
942
|
+
const result = installWindowsHostRecoveryPlugin({
|
|
943
|
+
controllerHome: resolveControllerHome(opts.controllerHome),
|
|
944
|
+
rescueRoot: opts.rescueRoot,
|
|
945
|
+
recoveryScriptHostPath: opts.recoveryScriptHost,
|
|
946
|
+
recoveryScriptWindowsPath: opts.recoveryScriptWindows,
|
|
947
|
+
powershellHostPath: opts.powershellHost,
|
|
948
|
+
powershellWindowsPath: opts.powershellWindows,
|
|
949
|
+
enabled: opts.disabled !== true,
|
|
950
|
+
});
|
|
951
|
+
output({ status: 'registered', ...result });
|
|
952
|
+
});
|
|
953
|
+
|
|
623
954
|
command.command('install')
|
|
624
955
|
.description('Build and activate the independent Forge Recovery Gateway and Watchdog release')
|
|
625
956
|
.requiredOption('--controller-home <path>', 'Explicit Controller Home')
|
|
@@ -628,11 +959,25 @@ export function buildRecoveryCommand(): Command {
|
|
|
628
959
|
.option('--recovery-public-url <url>', 'Dedicated Forge Recovery MCP public URL')
|
|
629
960
|
.option('--recovery-tunnel-service-label <label>', 'Dedicated Recovery tunnel launchd label')
|
|
630
961
|
.option('--recovery-tunnel-service-plist <path>', 'Absolute Recovery tunnel launchd plist path')
|
|
962
|
+
.option('--recovery-tunnel-systemd-unit <unit>', 'Dedicated Recovery systemd --user tunnel unit (Linux only)')
|
|
963
|
+
.option('--recovery-openai-tunnel-id <id>', 'Dedicated Recovery OpenAI Secure MCP Tunnel id')
|
|
964
|
+
.option('--recovery-openai-tunnel-alias <alias>', 'Dedicated Recovery tunnel-client runtime alias (default: machine-specific forge-recovery-<id>)')
|
|
965
|
+
.option('--recovery-openai-runtime-api-key-ref <ref>', 'Recovery tunnel runtime API key reference (env:NAME or file:/absolute/path)')
|
|
966
|
+
.option('--recovery-openai-profile <profile>', 'Optional tunnel-client runtime profile name for Recovery')
|
|
967
|
+
.option('--recovery-openai-profile-dir <path>', 'Optional absolute tunnel-client profile directory for Recovery')
|
|
968
|
+
.option('--recovery-openai-admin-profile <profile>', 'Optional tunnel-client admin profile used for Recovery runtime connect')
|
|
631
969
|
.option('--primary-connector-service-label <label>', 'Primary OAuth/Connector launchd label managed by standalone Recovery')
|
|
632
970
|
.option('--primary-connector-service-plist <path>', 'Absolute primary OAuth/Connector launchd plist path')
|
|
633
971
|
.option('--primary-connector-local-url <url>', 'Local primary OAuth/Connector MCP endpoint used to distinguish Connector from tunnel failures')
|
|
634
972
|
.option('--primary-tunnel-service-label <label>', 'Primary public MCP tunnel launchd label managed by standalone Recovery')
|
|
635
973
|
.option('--primary-tunnel-service-plist <path>', 'Absolute primary public MCP tunnel launchd plist path')
|
|
974
|
+
.option('--primary-openai-tunnel-id <id>', 'Primary OpenAI Secure MCP Tunnel id managed by Recovery')
|
|
975
|
+
.option('--primary-openai-tunnel-alias <alias>', 'Primary tunnel-client runtime alias (default: forge)')
|
|
976
|
+
.option('--primary-openai-runtime-api-key-ref <ref>', 'Primary tunnel runtime API key reference (env:NAME or file:/absolute/path)')
|
|
977
|
+
.option('--primary-openai-profile <profile>', 'Optional tunnel-client runtime profile name for primary Forge')
|
|
978
|
+
.option('--primary-openai-profile-dir <path>', 'Optional absolute tunnel-client profile directory for primary Forge')
|
|
979
|
+
.option('--primary-openai-admin-profile <profile>', 'Optional tunnel-client admin profile used for primary runtime connect')
|
|
980
|
+
.option('--primary-runtime-source-root <path>', 'Stable canonical/package source used by Recovery for future Runtime staging')
|
|
636
981
|
.option('--stage-only', 'Build and canary the Recovery release without activating services')
|
|
637
982
|
.action(async (opts: {
|
|
638
983
|
controllerHome: string;
|
|
@@ -641,36 +986,104 @@ export function buildRecoveryCommand(): Command {
|
|
|
641
986
|
recoveryPublicUrl?: string;
|
|
642
987
|
recoveryTunnelServiceLabel?: string;
|
|
643
988
|
recoveryTunnelServicePlist?: string;
|
|
989
|
+
recoveryTunnelSystemdUnit?: string;
|
|
990
|
+
recoveryOpenaiTunnelId?: string;
|
|
991
|
+
recoveryOpenaiTunnelAlias?: string;
|
|
992
|
+
recoveryOpenaiRuntimeApiKeyRef?: string;
|
|
993
|
+
recoveryOpenaiProfile?: string;
|
|
994
|
+
recoveryOpenaiProfileDir?: string;
|
|
995
|
+
recoveryOpenaiAdminProfile?: string;
|
|
644
996
|
primaryConnectorServiceLabel?: string;
|
|
645
997
|
primaryConnectorServicePlist?: string;
|
|
646
998
|
primaryConnectorLocalUrl?: string;
|
|
647
999
|
primaryTunnelServiceLabel?: string;
|
|
648
1000
|
primaryTunnelServicePlist?: string;
|
|
1001
|
+
primaryOpenaiTunnelId?: string;
|
|
1002
|
+
primaryOpenaiTunnelAlias?: string;
|
|
1003
|
+
primaryOpenaiRuntimeApiKeyRef?: string;
|
|
1004
|
+
primaryOpenaiProfile?: string;
|
|
1005
|
+
primaryOpenaiProfileDir?: string;
|
|
1006
|
+
primaryOpenaiAdminProfile?: string;
|
|
1007
|
+
primaryRuntimeSourceRoot?: string;
|
|
649
1008
|
stageOnly?: boolean;
|
|
650
1009
|
}) => {
|
|
651
1010
|
const home = resolveControllerHome(opts.controllerHome);
|
|
652
1011
|
const port = Number(opts.port);
|
|
653
1012
|
if (!Number.isInteger(port) || port < 1024 || port > 65535) throw new Error('RECOVERY_PORT_INVALID');
|
|
654
|
-
const
|
|
655
|
-
const
|
|
1013
|
+
const recoveryLaunchdTunnel = launchdService(opts.recoveryTunnelServiceLabel, opts.recoveryTunnelServicePlist, 'RECOVERY_TUNNEL') as PublicTunnelServiceConfig | undefined;
|
|
1014
|
+
const recoverySystemdTunnel = systemdRecoveryTunnelService(opts.recoveryTunnelSystemdUnit);
|
|
1015
|
+
if (recoveryLaunchdTunnel && process.platform === 'linux') throw new Error('RECOVERY_TUNNEL_LAUNCHD_UNSUPPORTED_ON_LINUX');
|
|
1016
|
+
if (recoverySystemdTunnel && process.platform !== 'linux') throw new Error('RECOVERY_TUNNEL_SYSTEMD_UNSUPPORTED_ON_THIS_PLATFORM');
|
|
1017
|
+
const recoveryLocalMcpUrl = `http://127.0.0.1:${port}/recovery/mcp`;
|
|
1018
|
+
const recoveryOpenAiTunnel = openAiTunnelService({
|
|
1019
|
+
tunnelId: opts.recoveryOpenaiTunnelId,
|
|
1020
|
+
alias: opts.recoveryOpenaiTunnelAlias,
|
|
1021
|
+
runtimeApiKeyRef: opts.recoveryOpenaiRuntimeApiKeyRef,
|
|
1022
|
+
profile: opts.recoveryOpenaiProfile,
|
|
1023
|
+
profileDir: opts.recoveryOpenaiProfileDir,
|
|
1024
|
+
adminProfile: opts.recoveryOpenaiAdminProfile,
|
|
1025
|
+
mcpServerUrl: recoveryLocalMcpUrl,
|
|
1026
|
+
defaultAlias: recoveryOpenAiTunnelDefaultAlias(home),
|
|
1027
|
+
role: 'RECOVERY',
|
|
1028
|
+
});
|
|
1029
|
+
const recoveryTunnelOwners = [recoveryLaunchdTunnel, recoverySystemdTunnel, recoveryOpenAiTunnel].filter(Boolean);
|
|
1030
|
+
if (recoveryTunnelOwners.length > 1) throw new Error('RECOVERY_TUNNEL_OWNER_CONFLICT');
|
|
1031
|
+
const recoveryTunnelService: RecoveryTunnelServiceConfig | undefined = recoveryOpenAiTunnel ?? recoverySystemdTunnel ?? recoveryLaunchdTunnel;
|
|
1032
|
+
|
|
1033
|
+
const primaryConnectorLaunchd = launchdService(opts.primaryConnectorServiceLabel, opts.primaryConnectorServicePlist, 'RECOVERY_PRIMARY_CONNECTOR') as PrimaryConnectorServiceConfig | undefined;
|
|
656
1034
|
const primaryConnectorLocalUrl = endpoint(opts.primaryConnectorLocalUrl, 'RECOVERY_PRIMARY_CONNECTOR_LOCAL_URL');
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
: undefined;
|
|
661
|
-
|
|
1035
|
+
let primaryConnectorService: PrimaryConnectorServiceConfig | undefined;
|
|
1036
|
+
if (process.platform === 'linux') {
|
|
1037
|
+
if (primaryConnectorLaunchd) throw new Error('RECOVERY_PRIMARY_CONNECTOR_LAUNCHD_UNSUPPORTED_ON_LINUX');
|
|
1038
|
+
primaryConnectorService = primaryConnectorLocalUrl ? { platform: 'systemd-user', localMcpUrl: primaryConnectorLocalUrl } : undefined;
|
|
1039
|
+
} else {
|
|
1040
|
+
if (primaryConnectorLocalUrl && !primaryConnectorLaunchd) throw new Error('RECOVERY_PRIMARY_CONNECTOR_LABEL_REQUIRED_FOR_LOCAL_URL');
|
|
1041
|
+
primaryConnectorService = primaryConnectorLaunchd
|
|
1042
|
+
? { ...primaryConnectorLaunchd, ...(primaryConnectorLocalUrl ? { localMcpUrl: primaryConnectorLocalUrl } : {}) }
|
|
1043
|
+
: undefined;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
const primaryLaunchdTunnel = launchdService(opts.primaryTunnelServiceLabel, opts.primaryTunnelServicePlist, 'RECOVERY_PRIMARY_TUNNEL') as PublicTunnelServiceConfig | undefined;
|
|
1047
|
+
const primaryOpenAiTunnel = primaryConnectorLocalUrl ? openAiTunnelService({
|
|
1048
|
+
tunnelId: opts.primaryOpenaiTunnelId,
|
|
1049
|
+
alias: opts.primaryOpenaiTunnelAlias,
|
|
1050
|
+
runtimeApiKeyRef: opts.primaryOpenaiRuntimeApiKeyRef,
|
|
1051
|
+
profile: opts.primaryOpenaiProfile,
|
|
1052
|
+
profileDir: opts.primaryOpenaiProfileDir,
|
|
1053
|
+
adminProfile: opts.primaryOpenaiAdminProfile,
|
|
1054
|
+
mcpServerUrl: primaryConnectorLocalUrl,
|
|
1055
|
+
defaultAlias: 'forge',
|
|
1056
|
+
role: 'RECOVERY_PRIMARY',
|
|
1057
|
+
}) : undefined;
|
|
1058
|
+
const primaryOpenAiOptionsSupplied = Boolean(opts.primaryOpenaiTunnelId || opts.primaryOpenaiTunnelAlias || opts.primaryOpenaiRuntimeApiKeyRef || opts.primaryOpenaiProfile || opts.primaryOpenaiProfileDir || opts.primaryOpenaiAdminProfile);
|
|
1059
|
+
if (primaryOpenAiOptionsSupplied && !primaryConnectorLocalUrl) throw new Error('RECOVERY_PRIMARY_CONNECTOR_LOCAL_URL_REQUIRED_FOR_OPENAI_TUNNEL');
|
|
1060
|
+
if (primaryLaunchdTunnel && primaryOpenAiTunnel) throw new Error('RECOVERY_PRIMARY_TUNNEL_OWNER_CONFLICT');
|
|
1061
|
+
const primaryPublicTunnelService = primaryOpenAiTunnel ?? primaryLaunchdTunnel;
|
|
662
1062
|
if (primaryPublicTunnelService && !primaryConnectorService) throw new Error('RECOVERY_PRIMARY_CONNECTOR_REQUIRED_FOR_PRIMARY_TUNNEL');
|
|
1063
|
+
assertDistinctRecoveryOpenAiTunnelIdentity(recoveryTunnelService, primaryPublicTunnelService);
|
|
1064
|
+
|
|
663
1065
|
const recoveryPublicUrl = endpoint(opts.recoveryPublicUrl, 'RECOVERY_PUBLIC_URL');
|
|
664
|
-
if (
|
|
665
|
-
|
|
666
|
-
|
|
1066
|
+
if (recoveryOpenAiTunnel && recoveryPublicUrl) throw new Error('RECOVERY_OPENAI_TUNNEL_PUBLIC_URL_CONFLICT');
|
|
1067
|
+
if ((recoveryLaunchdTunnel || recoverySystemdTunnel) && !recoveryPublicUrl) throw new Error('RECOVERY_PUBLIC_URL_AND_TUNNEL_SERVICE_MUST_BE_CONFIGURED_TOGETHER');
|
|
1068
|
+
if (!recoveryTunnelService && recoveryPublicUrl) throw new Error('RECOVERY_PUBLIC_URL_AND_TUNNEL_SERVICE_MUST_BE_CONFIGURED_TOGETHER');
|
|
1069
|
+
|
|
667
1070
|
const packageRoot = resolve(import.meta.dir, '..', '..', '..');
|
|
1071
|
+
const primaryRuntimeSourceRoot = opts.primaryRuntimeSourceRoot ? resolve(opts.primaryRuntimeSourceRoot) : packageRoot;
|
|
1072
|
+
const primaryRuntimeSourceRepository = findRegisteredRepositoryByCheckoutRoot(primaryRuntimeSourceRoot, home);
|
|
1073
|
+
if (!opts.stageOnly && !primaryRuntimeSourceRepository) {
|
|
1074
|
+
throw new Error(`RECOVERY_PRIMARY_RUNTIME_SOURCE_REPOSITORY_NOT_REGISTERED: ${primaryRuntimeSourceRoot}`);
|
|
1075
|
+
}
|
|
1076
|
+
if (!opts.stageOnly && /[\\/]\.forge[\\/]managed-worktrees[\\/]/.test(primaryRuntimeSourceRoot)) {
|
|
1077
|
+
throw new Error('RECOVERY_PRIMARY_RUNTIME_SOURCE_ROOT_DURABLE_REQUIRED');
|
|
1078
|
+
}
|
|
668
1079
|
const result = await installStandaloneRecovery({
|
|
669
1080
|
controllerHome: home,
|
|
670
|
-
repoRoot:
|
|
1081
|
+
repoRoot: primaryRuntimeSourceRoot,
|
|
1082
|
+
...(primaryRuntimeSourceRepository ? { primaryRuntimeSourceRepositoryId: primaryRuntimeSourceRepository.repoId } : {}),
|
|
671
1083
|
sourceRoot: packageRoot,
|
|
672
1084
|
port,
|
|
673
1085
|
stageOnly: opts.stageOnly === true,
|
|
1086
|
+
primaryRuntimeService: defaultPrimaryRuntimeServiceConfig(),
|
|
674
1087
|
publicMcpUrl: endpoint(opts.publicMcpUrl, 'PUBLIC_MCP_URL'),
|
|
675
1088
|
recoveryPublicUrl,
|
|
676
1089
|
recoveryTunnelService,
|