@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
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createHash } from 'crypto';
|
|
3
|
+
import { spawnSync } from 'child_process';
|
|
4
|
+
import { existsSync, readFileSync } from 'fs';
|
|
5
|
+
import { createConnection } from 'net';
|
|
6
|
+
import { isAbsolute, join } from 'path';
|
|
7
|
+
import { pathToFileURL } from 'url';
|
|
8
|
+
|
|
9
|
+
export const PLUGIN_ID = 'uu_remote_rescue';
|
|
10
|
+
export const PLUGIN_VERSION = '0.1.1';
|
|
11
|
+
export const PROTOCOL_VERSION = 1;
|
|
12
|
+
export const CAPABILITIES = [
|
|
13
|
+
'uu_remote.device_identity.v1',
|
|
14
|
+
'uu_remote.terminal_transport.v1',
|
|
15
|
+
'forge_wsl.health.v1',
|
|
16
|
+
'forge_wsl.service_recovery.v1',
|
|
17
|
+
'forge_wsl.host_recovery.v1',
|
|
18
|
+
];
|
|
19
|
+
export const ACTIONS = [
|
|
20
|
+
'device_status', 'wsl_status', 'forge_health',
|
|
21
|
+
'runtime_start', 'runtime_restart', 'connector_start', 'connector_restart',
|
|
22
|
+
'recovery_start', 'recovery_restart', 'runtime_recover',
|
|
23
|
+
'host_tunnel_restart_dispatch', 'host_full_recover_dispatch',
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const MUTATING_ACTIONS = new Set(['runtime_start', 'runtime_restart', 'connector_start', 'connector_restart', 'recovery_start', 'recovery_restart', 'runtime_recover']);
|
|
27
|
+
const HOST_RECOVERY_DISPATCH_ACTIONS = new Map([
|
|
28
|
+
['host_tunnel_restart_dispatch', 'tunnel_restart'],
|
|
29
|
+
['host_full_recover_dispatch', 'full_recover'],
|
|
30
|
+
]);
|
|
31
|
+
const TERMINAL_ACTIONS = new Set(ACTIONS.filter((action) => action !== 'device_status'));
|
|
32
|
+
const TERMINAL_WORD = /terminal|powershell|cmd|wsl|shell|终端/i;
|
|
33
|
+
|
|
34
|
+
function rescueError(code, message, retryable = false, details) {
|
|
35
|
+
const error = new Error(message);
|
|
36
|
+
error.code = code;
|
|
37
|
+
error.retryable = retryable;
|
|
38
|
+
if (details) error.details = details;
|
|
39
|
+
return error;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function boundedString(value, field, max = 512) {
|
|
43
|
+
if (typeof value !== 'string' || !value.trim() || value.length > max || /[\0\r\n]/.test(value)) {
|
|
44
|
+
throw rescueError('UU_RESCUE_CONFIG_INVALID', `Invalid ${field}.`);
|
|
45
|
+
}
|
|
46
|
+
return value.trim();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function validateConfig(value) {
|
|
50
|
+
if (!value || typeof value !== 'object' || Array.isArray(value) || value.schemaVersion !== 1) {
|
|
51
|
+
throw rescueError('UU_RESCUE_CONFIG_INVALID', 'UU Remote rescue config schema is invalid.');
|
|
52
|
+
}
|
|
53
|
+
const device = value.device && typeof value.device === 'object' && !Array.isArray(value.device) ? value.device : {};
|
|
54
|
+
const wsl = value.wsl && typeof value.wsl === 'object' && !Array.isArray(value.wsl) ? value.wsl : {};
|
|
55
|
+
const id = boundedString(device.id, 'device.id', 64);
|
|
56
|
+
const name = boundedString(device.name, 'device.name', 128);
|
|
57
|
+
const platform = boundedString(device.platform, 'device.platform', 32).toLowerCase();
|
|
58
|
+
const distro = boundedString(wsl.distro, 'wsl.distro', 128);
|
|
59
|
+
const controllerHome = boundedString(wsl.controllerHome, 'wsl.controllerHome', 1024);
|
|
60
|
+
const uuycCliPath = boundedString(value.uuycCliPath, 'uuycCliPath', 1024);
|
|
61
|
+
const desktopOperatorSocketPath = boundedString(value.desktopOperatorSocketPath, 'desktopOperatorSocketPath', 1024);
|
|
62
|
+
const uuBundleId = boundedString(value.uuBundleId, 'uuBundleId', 128);
|
|
63
|
+
if (!/^[A-Za-z0-9_-]{4,64}$/.test(id)) throw rescueError('UU_RESCUE_CONFIG_INVALID', 'Configured device id is invalid.');
|
|
64
|
+
if (platform !== 'windows') throw rescueError('UU_RESCUE_CONFIG_INVALID', 'Configured rescue target must be Windows.');
|
|
65
|
+
if (!/^[A-Za-z0-9._ -]{1,128}$/.test(distro)) throw rescueError('UU_RESCUE_CONFIG_INVALID', 'Configured WSL distro is invalid.');
|
|
66
|
+
if (!controllerHome.startsWith('/')) throw rescueError('UU_RESCUE_CONFIG_INVALID', 'Configured remote Controller Home must be an absolute WSL path.');
|
|
67
|
+
if (!isAbsolute(uuycCliPath) || !isAbsolute(desktopOperatorSocketPath)) throw rescueError('UU_RESCUE_CONFIG_INVALID', 'Configured local IPC paths must be absolute.');
|
|
68
|
+
return { schemaVersion: 1, device: { id, name, platform }, wsl: { distro, controllerHome }, uuycCliPath, desktopOperatorSocketPath, uuBundleId };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function serviceUnits(controllerHome) {
|
|
72
|
+
const suffix = createHash('sha256').update(controllerHome).digest('hex').slice(0, 12);
|
|
73
|
+
return {
|
|
74
|
+
runtime: `com.moretea.forge.runtime.${suffix}.service`,
|
|
75
|
+
connector: `com.moretea.forge.mcp-gateway.${suffix}.service`,
|
|
76
|
+
recoveryGateway: 'com.moretea.forge-recovery-gateway.service',
|
|
77
|
+
recoveryWatchdog: 'com.moretea.forge-recovery-watchdog.service',
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function minimalEnv() {
|
|
82
|
+
return Object.fromEntries(Object.entries({
|
|
83
|
+
PATH: '/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin',
|
|
84
|
+
HOME: process.env.HOME,
|
|
85
|
+
TMPDIR: process.env.TMPDIR,
|
|
86
|
+
LANG: process.env.LANG,
|
|
87
|
+
LC_ALL: process.env.LC_ALL,
|
|
88
|
+
}).filter(([, value]) => typeof value === 'string'));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function parseJson(text, code) {
|
|
92
|
+
try { return JSON.parse(text); } catch { throw rescueError(code, 'Provider returned invalid JSON.', true); }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function runCliDefault(config, args) {
|
|
96
|
+
if (!existsSync(config.uuycCliPath)) throw rescueError('UU_RESCUE_UUYC_CLI_UNAVAILABLE', 'Configured uuyc-cli is unavailable.');
|
|
97
|
+
const result = spawnSync(config.uuycCliPath, args, { encoding: 'utf8', env: minimalEnv(), timeout: 15_000, maxBuffer: 256 * 1024, shell: false, windowsHide: true });
|
|
98
|
+
if (result.error) throw rescueError('UU_RESCUE_UUYC_CLI_FAILED', result.error.message, true);
|
|
99
|
+
if (result.status !== 0) throw rescueError('UU_RESCUE_UUYC_CLI_FAILED', String(result.stderr || result.stdout || 'uuyc-cli failed').slice(-1000), true);
|
|
100
|
+
const parsed = parseJson(String(result.stdout || '').trim(), 'UU_RESCUE_UUYC_PROTOCOL_INVALID');
|
|
101
|
+
if (parsed?.success !== true) throw rescueError('UU_RESCUE_UUYC_OPERATION_FAILED', 'uuyc-cli reported failure.', true);
|
|
102
|
+
return parsed;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function desktopCallDefault(config, requestId, action, args) {
|
|
106
|
+
const socketPath = config.desktopOperatorSocketPath;
|
|
107
|
+
const envelope = `${JSON.stringify({ id: requestId, method: 'execute', params: { action, arguments: args } })}\n`;
|
|
108
|
+
return await new Promise((resolve, reject) => {
|
|
109
|
+
let socket;
|
|
110
|
+
let settled = false;
|
|
111
|
+
let buffer = Buffer.alloc(0);
|
|
112
|
+
const timer = setTimeout(() => finish(() => reject(rescueError('UU_RESCUE_DESKTOP_TIMEOUT', 'Desktop Operator request timed out.', true))), 12_000);
|
|
113
|
+
const finish = (callback) => {
|
|
114
|
+
if (settled) return;
|
|
115
|
+
settled = true;
|
|
116
|
+
clearTimeout(timer);
|
|
117
|
+
if (socket && !socket.destroyed) socket.destroy();
|
|
118
|
+
callback();
|
|
119
|
+
};
|
|
120
|
+
try { socket = createConnection({ path: socketPath }); } catch (error) { finish(() => reject(error)); return; }
|
|
121
|
+
socket.once('connect', () => socket.write(envelope));
|
|
122
|
+
socket.on('data', (chunk) => {
|
|
123
|
+
buffer = Buffer.concat([buffer, chunk]);
|
|
124
|
+
if (buffer.length > 1024 * 1024) return finish(() => reject(rescueError('UU_RESCUE_DESKTOP_RESPONSE_TOO_LARGE', 'Desktop Operator response exceeded the bounded limit.')));
|
|
125
|
+
const newline = buffer.indexOf(0x0a);
|
|
126
|
+
if (newline < 0) return;
|
|
127
|
+
try {
|
|
128
|
+
const response = parseJson(buffer.subarray(0, newline).toString('utf8'), 'UU_RESCUE_DESKTOP_PROTOCOL_INVALID');
|
|
129
|
+
if (response.id !== requestId || typeof response.ok !== 'boolean') throw rescueError('UU_RESCUE_DESKTOP_PROTOCOL_INVALID', 'Desktop Operator returned a mismatched response.');
|
|
130
|
+
if (!response.ok) throw rescueError(response.error?.code || 'UU_RESCUE_DESKTOP_ACTION_FAILED', response.error?.message || 'Desktop Operator action failed.', response.error?.retryable === true);
|
|
131
|
+
finish(() => resolve(response.result && typeof response.result === 'object' ? response.result : {}));
|
|
132
|
+
} catch (error) { finish(() => reject(error)); }
|
|
133
|
+
});
|
|
134
|
+
socket.once('error', (error) => finish(() => reject(rescueError('UU_RESCUE_DESKTOP_UNAVAILABLE', error.message, true))));
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function collectStrings(value, output = []) {
|
|
139
|
+
if (typeof value === 'string') output.push(value);
|
|
140
|
+
else if (Array.isArray(value)) value.forEach((entry) => collectStrings(entry, output));
|
|
141
|
+
else if (value && typeof value === 'object') Object.values(value).forEach((entry) => collectStrings(entry, output));
|
|
142
|
+
return output;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function findStringKey(value, keys) {
|
|
146
|
+
if (!value || typeof value !== 'object') return undefined;
|
|
147
|
+
if (!Array.isArray(value)) {
|
|
148
|
+
for (const key of keys) if (typeof value[key] === 'string') return value[key];
|
|
149
|
+
for (const nested of Object.values(value)) { const found = findStringKey(nested, keys); if (found !== undefined) return found; }
|
|
150
|
+
} else {
|
|
151
|
+
for (const nested of value) { const found = findStringKey(nested, keys); if (found !== undefined) return found; }
|
|
152
|
+
}
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function accessibilityWindows(value) {
|
|
157
|
+
const windows = [];
|
|
158
|
+
const visit = (node) => {
|
|
159
|
+
if (!node || typeof node !== 'object') return;
|
|
160
|
+
if (Array.isArray(node)) { node.forEach(visit); return; }
|
|
161
|
+
if (node.role === 'AXWindow') {
|
|
162
|
+
windows.push({
|
|
163
|
+
title: typeof node.title === 'string' ? node.title : '',
|
|
164
|
+
identifier: typeof node.identifier === 'string' ? node.identifier : '',
|
|
165
|
+
focused: node.focused === true,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
Object.values(node).forEach(visit);
|
|
169
|
+
};
|
|
170
|
+
visit(value);
|
|
171
|
+
return windows;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function exactTerminalWindows(observation, config) {
|
|
175
|
+
return accessibilityWindows(observation).filter((window) =>
|
|
176
|
+
window.title.includes(config.device.name) && TERMINAL_WORD.test(window.title));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function ensureTerminalWindowFocused(config, requestId, interactionId, initialObservation, deps) {
|
|
180
|
+
let observation = initialObservation;
|
|
181
|
+
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
182
|
+
const terminals = exactTerminalWindows(observation, config);
|
|
183
|
+
if (terminals.length > 1) {
|
|
184
|
+
throw rescueError('UU_RESCUE_TERMINAL_WINDOW_AMBIGUOUS', 'More than one exact configured-device UU terminal window is visible; refusing remote input.');
|
|
185
|
+
}
|
|
186
|
+
if (terminals.length === 1 && terminals[0].focused) return observation;
|
|
187
|
+
if (attempt === 5) break;
|
|
188
|
+
if (terminals.length === 1) {
|
|
189
|
+
await deps.desktopCall(config, `${requestId}:focus-cycle:${attempt}`, 'desktop_key', { interaction_id: interactionId, keys: ['COMMAND', '`'] });
|
|
190
|
+
}
|
|
191
|
+
await deps.sleep(terminals.length === 0 ? 250 : 150);
|
|
192
|
+
observation = await deps.desktopCall(config, `${requestId}:focus-observe:${attempt}`, 'desktop_observe', { interaction_id: interactionId, max_depth: 8, max_nodes: 1200, include_values: true, include_actions: false, include_windows: true });
|
|
193
|
+
}
|
|
194
|
+
throw rescueError('UU_RESCUE_TERMINAL_FOCUS_UNPROVEN', 'The exact configured-device UU terminal window could not be proven focused; refusing remote input.', true);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function markerPair(requestId) {
|
|
198
|
+
const suffix = createHash('sha256').update(requestId).digest('hex').slice(0, 16);
|
|
199
|
+
return { begin: `__FORGE_UU_RESCUE_BEGIN_${suffix}__`, end: `__FORGE_UU_RESCUE_END_${suffix}__` };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function extractMarkedText(observation, markers) {
|
|
203
|
+
const candidates = [...collectStrings(observation), collectStrings(observation).join('\n')];
|
|
204
|
+
for (let index = candidates.length - 1; index >= 0; index -= 1) {
|
|
205
|
+
const text = candidates[index];
|
|
206
|
+
const start = text.lastIndexOf(markers.begin);
|
|
207
|
+
if (start < 0) continue;
|
|
208
|
+
const end = text.indexOf(markers.end, start + markers.begin.length);
|
|
209
|
+
if (end < 0) continue;
|
|
210
|
+
return text.slice(start + markers.begin.length, end).trim();
|
|
211
|
+
}
|
|
212
|
+
return undefined;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function b64(value) { return Buffer.from(String(value), 'utf8').toString('base64'); }
|
|
216
|
+
function decodeB64(value) { try { return Buffer.from(value, 'base64').toString('utf8'); } catch { return ''; } }
|
|
217
|
+
|
|
218
|
+
export function buildWslStatusCommand(markers) {
|
|
219
|
+
return `$ErrorActionPreference='Continue'; $d=@(wsl.exe --list --quiet 2>$null | ForEach-Object { ($_ -replace [char]0,'').Trim() } | Where-Object { $_ }); $s=(wsl.exe --status 2>&1 | Out-String).Trim(); Write-Output '${markers.begin}'; foreach($x in $d){ Write-Output ('distro_b64|'+[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($x))) }; Write-Output ('status_b64|'+[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($s))); Write-Output '${markers.end}'`;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function buildHostRecoveryDispatchCommand(action) {
|
|
223
|
+
const recoveryAction = HOST_RECOVERY_DISPATCH_ACTIONS.get(action);
|
|
224
|
+
if (!recoveryAction) throw rescueError('UU_RESCUE_ACTION_UNSUPPORTED', 'Unsupported fixed host Recovery dispatch action.');
|
|
225
|
+
const recoveryScript = String.raw`C:\ProgramData\ForgeRecovery\ForgeRecovery.ps1`;
|
|
226
|
+
const powershellSuffix = String.raw`System32\WindowsPowerShell\v1.0\powershell.exe`;
|
|
227
|
+
return `$recoveryScript='${recoveryScript}'; $powershell=Join-Path $env:SystemRoot '${powershellSuffix}'; if (-not (Test-Path -LiteralPath $recoveryScript -PathType Leaf)) { throw 'FORGE_RECOVERY_SCRIPT_MISSING' }; if (-not (Test-Path -LiteralPath $powershell -PathType Leaf)) { throw 'WINDOWS_POWERSHELL_MISSING' }; Start-Process -WindowStyle Hidden -FilePath $powershell -ArgumentList @('-NoProfile','-NonInteractive','-ExecutionPolicy','Bypass','-File',$recoveryScript,'${recoveryAction}')`;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function buildForgeCommand(action, config, markers) {
|
|
231
|
+
const units = serviceUnits(config.wsl.controllerHome);
|
|
232
|
+
const operation = {
|
|
233
|
+
forge_health: '',
|
|
234
|
+
runtime_start: `systemctl --user start '${units.runtime}'; emit action_exit "$?"`,
|
|
235
|
+
runtime_restart: `systemctl --user restart '${units.runtime}'; emit action_exit "$?"`,
|
|
236
|
+
connector_start: `systemctl --user start '${units.connector}'; emit action_exit "$?"`,
|
|
237
|
+
connector_restart: `systemctl --user restart '${units.connector}'; emit action_exit "$?"`,
|
|
238
|
+
recovery_start: `systemctl --user start '${units.recoveryGateway}' '${units.recoveryWatchdog}'; emit action_exit "$?"`,
|
|
239
|
+
recovery_restart: `systemctl --user restart '${units.recoveryGateway}' '${units.recoveryWatchdog}'; emit action_exit "$?"`,
|
|
240
|
+
runtime_recover: `if command -v forge >/dev/null 2>&1; then recovery_out="$(forge recovery recover --controller-home "$CH" 2>&1)"; recovery_code="$?"; emit recovery_output_b64 "$(printf %s "$recovery_out" | base64 | tr -d '\\n')"; emit action_exit "$recovery_code"; else emit recovery_output_b64 "$(printf %s 'forge CLI unavailable' | base64 | tr -d '\\n')"; emit action_exit 127; fi`,
|
|
241
|
+
}[action];
|
|
242
|
+
if (operation === undefined) throw rescueError('UU_RESCUE_ACTION_UNSUPPORTED', 'Unsupported fixed Forge rescue action.');
|
|
243
|
+
const script = `set +e\nCH="$(printf %s '${b64(config.wsl.controllerHome)}' | base64 -d)"\nemit(){ printf '%s|%s\\n' "$1" "$2"; }\nstatus(){ unit="$2"; load="$(systemctl --user show "$unit" -p LoadState --value 2>/dev/null)"; active="$(systemctl --user show "$unit" -p ActiveState --value 2>/dev/null)"; sub="$(systemctl --user show "$unit" -p SubState --value 2>/dev/null)"; pid="$(systemctl --user show "$unit" -p MainPID --value 2>/dev/null)"; printf 'service|%s|%s|%s|%s|%s\\n' "$1" "\${load:-unknown}" "\${active:-unknown}" "\${sub:-unknown}" "\${pid:-0}"; }\nprintf '%s\\n' '${markers.begin}'\nemit distro_b64 "$(printf %s "\${WSL_DISTRO_NAME:-}" | base64 | tr -d '\\n')"\nemit controller_home_present "$([ -d "$CH" ] && echo 1 || echo 0)"\nemit control_plane_present "$([ -f "$CH/control-plane.sqlite" ] && echo 1 || echo 0)"\nemit runtime_owner_present "$([ -f "$CH/runtime/active-runtime-owner.json" ] && echo 1 || echo 0)"\nemit runtime_status_present "$([ -f "$CH/runtime/status.json" ] && echo 1 || echo 0)"\nemit connector_authority_present "$([ -f "$CH/runtime/connector-service/authority.json" ] && echo 1 || echo 0)"\nemit recovery_config_present "$([ -d "$CH/recovery/config" ] && echo 1 || echo 0)"\nif command -v sqlite3 >/dev/null 2>&1 && [ -f "$CH/control-plane.sqlite" ]; then migration="$(sqlite3 -json "$CH/control-plane.sqlite" \"select json_extract(payload,'$.status') as status,json_extract(payload,'$.sourceHome') as sourceHome,json_extract(payload,'$.destinationHome') as destinationHome,updated_at as updatedAt from control_plane_records where namespace='controller_home_migration' order by updated_at desc limit 1;\" 2>/dev/null)"; emit migration_json_b64 "$(printf %s "$migration" | base64 | tr -d '\\n')"; else emit migration_unavailable_b64 "$(printf %s 'sqlite3 unavailable or control-plane missing' | base64 | tr -d '\\n')"; fi\n${operation}\nstatus runtime '${units.runtime}'\nstatus connector '${units.connector}'\nstatus recoveryGateway '${units.recoveryGateway}'\nstatus recoveryWatchdog '${units.recoveryWatchdog}'\nprintf '%s\\n' '${markers.end}'`;
|
|
244
|
+
const payload = b64(script);
|
|
245
|
+
const distro = config.wsl.distro.replaceAll("'", "''");
|
|
246
|
+
return `wsl.exe -d '${distro}' -- bash -lc "printf %s '${payload}' | base64 -d | bash"`;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function parseRecords(text) {
|
|
250
|
+
const result = { services: {} };
|
|
251
|
+
for (const rawLine of text.split(/\r?\n/)) {
|
|
252
|
+
const line = rawLine.trim();
|
|
253
|
+
if (!line) continue;
|
|
254
|
+
const parts = line.split('|');
|
|
255
|
+
if (parts[0] === 'service' && parts.length >= 6) {
|
|
256
|
+
result.services[parts[1]] = { unitLoadState: parts[2], activeState: parts[3], subState: parts[4], mainPid: Number(parts[5]) || 0 };
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
const key = parts.shift();
|
|
260
|
+
const value = parts.join('|');
|
|
261
|
+
if (!key) continue;
|
|
262
|
+
if (key.endsWith('_b64')) result[key.slice(0, -4)] = decodeB64(value);
|
|
263
|
+
else if (key === 'action_exit') result.actionExit = Number(value);
|
|
264
|
+
else if (['controller_home_present', 'control_plane_present', 'runtime_owner_present', 'runtime_status_present', 'connector_authority_present', 'recovery_config_present'].includes(key)) result[key] = value === '1';
|
|
265
|
+
else result[key] = value;
|
|
266
|
+
}
|
|
267
|
+
if (typeof result.migration_json === 'string' && result.migration_json.trim()) {
|
|
268
|
+
try { result.migration = JSON.parse(result.migration_json); } catch { result.migration = { parseError: true }; }
|
|
269
|
+
delete result.migration_json;
|
|
270
|
+
}
|
|
271
|
+
return result;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async function verifyDevice(config, deps, requireOnline) {
|
|
275
|
+
const response = await deps.runCli(config, ['device', 'info', config.device.id]);
|
|
276
|
+
const data = response?.data || {};
|
|
277
|
+
const item = data.matchedItem || {};
|
|
278
|
+
if (data.isUniqueMatch !== true || item.deviceId !== config.device.id || item.deviceName !== config.device.name || String(item.platform || '').toLowerCase() !== 'windows') {
|
|
279
|
+
throw rescueError('UU_RESCUE_DEVICE_MISMATCH', 'UU Remote device identity did not match the registration-bound Windows target.');
|
|
280
|
+
}
|
|
281
|
+
if (requireOnline && item.isOnline !== true) throw rescueError('UU_RESCUE_DEVICE_OFFLINE', 'The registration-bound UU Remote device is offline.', true);
|
|
282
|
+
return { deviceId: item.deviceId, deviceName: item.deviceName, platform: 'windows', isOnline: item.isOnline === true };
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async function runTerminalCommand(config, requestId, command, deps, options = {}) {
|
|
286
|
+
await verifyDevice(config, deps, true);
|
|
287
|
+
await deps.runCli(config, ['term', 'open', config.device.id]);
|
|
288
|
+
await deps.sleep(700);
|
|
289
|
+
let interactionId;
|
|
290
|
+
let originalClipboard;
|
|
291
|
+
let clipboardCaptured = false;
|
|
292
|
+
try {
|
|
293
|
+
const opened = await deps.desktopCall(config, `${requestId}:session`, 'desktop_session_open', { bundle_id: config.uuBundleId, launch: false, activate: true });
|
|
294
|
+
interactionId = findStringKey(opened, ['interaction_id', 'interactionId']);
|
|
295
|
+
if (!interactionId) throw rescueError('UU_RESCUE_TERMINAL_SESSION_UNBOUND', 'Desktop Operator did not return a bound UURemote interaction session.');
|
|
296
|
+
const observed = await deps.desktopCall(config, `${requestId}:preflight`, 'desktop_observe', { interaction_id: interactionId, max_depth: 8, max_nodes: 1200, include_values: true, include_actions: false, include_windows: true });
|
|
297
|
+
await ensureTerminalWindowFocused(config, requestId, interactionId, observed, deps);
|
|
298
|
+
const clipboard = await deps.desktopCall(config, `${requestId}:clipboard-read`, 'desktop_clipboard_read', {});
|
|
299
|
+
originalClipboard = findStringKey(clipboard, ['text', 'value']) ?? '';
|
|
300
|
+
clipboardCaptured = true;
|
|
301
|
+
await deps.desktopCall(config, `${requestId}:clipboard-command`, 'desktop_clipboard_write', { text: command });
|
|
302
|
+
await deps.desktopCall(config, `${requestId}:paste`, 'desktop_paste', { interaction_id: interactionId });
|
|
303
|
+
await deps.desktopCall(config, `${requestId}:return`, 'desktop_key', { interaction_id: interactionId, keys: ['RETURN'] });
|
|
304
|
+
if (options.awaitResult === false) {
|
|
305
|
+
await deps.sleep(300);
|
|
306
|
+
return undefined;
|
|
307
|
+
}
|
|
308
|
+
const markers = markerPair(requestId);
|
|
309
|
+
for (let attempt = 0; attempt < 24; attempt += 1) {
|
|
310
|
+
await deps.sleep(attempt === 0 ? 300 : 250);
|
|
311
|
+
const next = await deps.desktopCall(config, `${requestId}:observe:${attempt}`, 'desktop_observe', { interaction_id: interactionId, max_depth: 10, max_nodes: 2500, include_values: true, include_actions: false, include_windows: false });
|
|
312
|
+
const payload = extractMarkedText(next, markers);
|
|
313
|
+
if (payload !== undefined) return payload;
|
|
314
|
+
}
|
|
315
|
+
throw rescueError('UU_RESCUE_REMOTE_RESULT_TIMEOUT', 'The fixed UU Remote terminal command did not produce its bounded result marker.', true);
|
|
316
|
+
} finally {
|
|
317
|
+
if (clipboardCaptured) {
|
|
318
|
+
try { await deps.desktopCall(config, `${requestId}:clipboard-restore`, 'desktop_clipboard_write', { text: originalClipboard }); } catch { /* never expose prior clipboard */ }
|
|
319
|
+
}
|
|
320
|
+
if (interactionId) {
|
|
321
|
+
try { await deps.desktopCall(config, `${requestId}:session-close`, 'desktop_session_close', { interaction_id: interactionId }); } catch { /* best effort */ }
|
|
322
|
+
}
|
|
323
|
+
try { await deps.runCli(config, ['term', 'exit', config.device.id, '--clear']); } catch { /* best effort */ }
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export async function executeAction(actionId, input, configInput, injected = {}) {
|
|
328
|
+
const config = validateConfig(configInput);
|
|
329
|
+
if (!ACTIONS.includes(actionId)) throw rescueError('UU_RESCUE_ACTION_UNSUPPORTED', 'Unsupported UU Remote rescue action.');
|
|
330
|
+
if (!input || typeof input !== 'object' || Array.isArray(input) || Object.keys(input).length !== 0) throw rescueError('UU_RESCUE_ARGUMENTS_FORBIDDEN', 'UU Remote rescue actions do not accept caller-provided commands, service names, devices, paths, or shell arguments.');
|
|
331
|
+
const deps = { runCli: runCliDefault, desktopCall: desktopCallDefault, sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)), ...injected };
|
|
332
|
+
if (actionId === 'device_status') return { device: await verifyDevice(config, deps, false) };
|
|
333
|
+
const effectiveRequestId = injected.requestId || `${actionId}:${Date.now()}`;
|
|
334
|
+
if (HOST_RECOVERY_DISPATCH_ACTIONS.has(actionId)) {
|
|
335
|
+
const command = buildHostRecoveryDispatchCommand(actionId);
|
|
336
|
+
await runTerminalCommand(config, effectiveRequestId, command, deps, { awaitResult: false });
|
|
337
|
+
return {
|
|
338
|
+
device: config.device,
|
|
339
|
+
wsl: { configuredDistro: config.wsl.distro },
|
|
340
|
+
dispatch: {
|
|
341
|
+
accepted: true,
|
|
342
|
+
action: HOST_RECOVERY_DISPATCH_ACTIONS.get(actionId),
|
|
343
|
+
authority: 'C:\\ProgramData\\ForgeRecovery\\ForgeRecovery.ps1',
|
|
344
|
+
recoveryStatus: 'unverified',
|
|
345
|
+
verificationRequired: 'Forge Cloud connectivity',
|
|
346
|
+
},
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
const markers = markerPair(effectiveRequestId);
|
|
350
|
+
const command = actionId === 'wsl_status' ? buildWslStatusCommand(markers) : buildForgeCommand(actionId, config, markers);
|
|
351
|
+
const payload = await runTerminalCommand(config, effectiveRequestId, command, deps);
|
|
352
|
+
if (actionId === 'wsl_status') {
|
|
353
|
+
const records = parseRecords(payload);
|
|
354
|
+
const distros = payload.split(/\r?\n/).filter((line) => line.startsWith('distro_b64|')).map((line) => decodeB64(line.slice('distro_b64|'.length))).filter(Boolean);
|
|
355
|
+
return { device: config.device, configuredDistro: config.wsl.distro, distros, status: records.status || '' };
|
|
356
|
+
}
|
|
357
|
+
const result = parseRecords(payload);
|
|
358
|
+
result.device = config.device;
|
|
359
|
+
result.wsl = { configuredDistro: config.wsl.distro, observedDistro: result.distro || '' };
|
|
360
|
+
result.controllerHome = config.wsl.controllerHome;
|
|
361
|
+
result.units = serviceUnits(config.wsl.controllerHome);
|
|
362
|
+
if (MUTATING_ACTIONS.has(actionId) && result.actionExit !== 0) throw rescueError('UU_RESCUE_REMOTE_ACTION_FAILED', `Fixed remote action ${actionId} exited with code ${String(result.actionExit ?? 'unknown')}.`, false, { actionId, actionExit: result.actionExit });
|
|
363
|
+
return result;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function loadConfig() {
|
|
367
|
+
const path = join(process.cwd(), 'config.json');
|
|
368
|
+
if (!existsSync(path)) throw rescueError('UU_RESCUE_CONFIG_MISSING', 'UU Remote rescue config is missing.');
|
|
369
|
+
return validateConfig(parseJson(readFileSync(path, 'utf8'), 'UU_RESCUE_CONFIG_INVALID'));
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
async function providerAction(request) {
|
|
373
|
+
const config = loadConfig();
|
|
374
|
+
if (request.actionId === 'manifest') return { id: PLUGIN_ID, name: 'Forge UU Remote Rescue', version: PLUGIN_VERSION, protocolVersion: '1.0', mode: 'external', scope: 'controller', provider: 'local-macos', capabilities: CAPABILITIES, actions: ACTIONS };
|
|
375
|
+
if (request.actionId === 'health') {
|
|
376
|
+
const system = runCliDefault(config, ['status']);
|
|
377
|
+
let device;
|
|
378
|
+
try { device = await verifyDevice(config, { runCli: runCliDefault }, false); } catch (error) { return { state: 'degraded', warnings: [error.code || 'UU_RESCUE_DEVICE_UNAVAILABLE'], xpcServiceStatus: system?.data?.xpcServiceStatus, networkStatus: system?.data?.networkStatus }; }
|
|
379
|
+
const ready = system?.data?.xpcServiceStatus === 'running' && system?.data?.isLoggedIn === true && device.isOnline;
|
|
380
|
+
return { state: ready ? 'ready' : 'degraded', warnings: ready ? [] : ['Configured UU Remote target is not currently ready for rescue.'], device, xpcServiceStatus: system?.data?.xpcServiceStatus, networkStatus: system?.data?.networkStatus };
|
|
381
|
+
}
|
|
382
|
+
return await executeAction(request.actionId, request.input, config, { requestId: request.requestId });
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export async function runManagedHelper() {
|
|
386
|
+
process.stdout.write(`${JSON.stringify({ schemaVersion: 1, type: 'handshake', protocolVersion: PROTOCOL_VERSION, pluginId: PLUGIN_ID, helperVersion: PLUGIN_VERSION, capabilities: CAPABILITIES })}\n`);
|
|
387
|
+
let raw = '';
|
|
388
|
+
for await (const chunk of process.stdin) raw += chunk;
|
|
389
|
+
const line = raw.split(/\r?\n/).find((entry) => entry.trim());
|
|
390
|
+
if (!line) throw rescueError('UU_RESCUE_REQUEST_MISSING', 'Managed plugin request is missing.');
|
|
391
|
+
const request = parseJson(line, 'UU_RESCUE_REQUEST_INVALID');
|
|
392
|
+
if (request.schemaVersion !== 1 || request.type !== 'execute' || typeof request.requestId !== 'string' || typeof request.actionId !== 'string' || !request.input || typeof request.input !== 'object' || Array.isArray(request.input)) {
|
|
393
|
+
throw rescueError('UU_RESCUE_REQUEST_INVALID', 'Managed plugin request envelope is invalid.');
|
|
394
|
+
}
|
|
395
|
+
try {
|
|
396
|
+
const result = await providerAction(request);
|
|
397
|
+
process.stdout.write(`${JSON.stringify({ schemaVersion: 1, type: 'result', requestId: request.requestId, ok: true, result })}\n`);
|
|
398
|
+
} catch (error) {
|
|
399
|
+
process.stdout.write(`${JSON.stringify({ schemaVersion: 1, type: 'result', requestId: request.requestId, ok: false, error: { code: error?.code || 'UU_RESCUE_FAILED', message: String(error?.message || 'UU Remote rescue failed.').replace(/(token|secret|password|authorization)\s*[=:]\s*\S+/gi, '$1=[REDACTED]').slice(0, 1000), retryable: error?.retryable === true, ...(error?.details ? { details: error.details } : {}) } })}\n`);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const invokedDirectly = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
404
|
+
if (invokedDirectly) {
|
|
405
|
+
runManagedHelper().catch((error) => {
|
|
406
|
+
process.stderr.write(`UU_RESCUE_HELPER_FATAL: ${String(error?.message || error).replace(/(token|secret|password|authorization)\s*[=:]\s*\S+/gi, '$1=[REDACTED]').slice(0, 1000)}\n`);
|
|
407
|
+
process.exitCode = 1;
|
|
408
|
+
});
|
|
409
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createHash } from 'crypto';
|
|
3
|
+
import { existsSync, readFileSync } from 'fs';
|
|
4
|
+
import { spawnSync } from 'child_process';
|
|
5
|
+
import { pathToFileURL } from 'url';
|
|
6
|
+
|
|
7
|
+
export const PLUGIN_ID = 'windows_host_recovery';
|
|
8
|
+
export const PLUGIN_VERSION = '0.1.4';
|
|
9
|
+
export const PROTOCOL_VERSION = 1;
|
|
10
|
+
export const CAPABILITIES = [
|
|
11
|
+
'windows_host.identity.v1',
|
|
12
|
+
'windows_host.recovery_task.v1',
|
|
13
|
+
'forge_wsl.host_recovery.v1',
|
|
14
|
+
];
|
|
15
|
+
export const ACTIONS = [
|
|
16
|
+
'host_status', 'task_status', 'task_install', 'task_run',
|
|
17
|
+
'wsl_status', 'wsl_start',
|
|
18
|
+
'forge_source_status', 'controller_status',
|
|
19
|
+
'runtime_status', 'runtime_start', 'runtime_restart',
|
|
20
|
+
'connector_status', 'connector_start', 'connector_restart',
|
|
21
|
+
'recovery_status', 'recovery_start', 'recovery_restart',
|
|
22
|
+
'tunnel_status', 'tunnel_start', 'tunnel_restart',
|
|
23
|
+
'forge_cloud_verify', 'full_recover',
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const READONLY_ACTIONS = new Set([
|
|
27
|
+
'host_status', 'task_status', 'wsl_status', 'forge_source_status', 'controller_status',
|
|
28
|
+
'runtime_status', 'connector_status', 'recovery_status', 'tunnel_status', 'forge_cloud_verify',
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
function providerError(code, message, retryable = false, details) {
|
|
32
|
+
const error = new Error(message);
|
|
33
|
+
error.code = code;
|
|
34
|
+
error.retryable = retryable;
|
|
35
|
+
if (details) error.details = details;
|
|
36
|
+
return error;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function parseConfig(raw) {
|
|
40
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw) || raw.schemaVersion !== 1) {
|
|
41
|
+
throw providerError('WINDOWS_RECOVERY_CONFIG_INVALID', 'Windows host recovery config schema is invalid.');
|
|
42
|
+
}
|
|
43
|
+
const expectedScriptSha256 = typeof raw.expectedScriptSha256 === 'string' ? raw.expectedScriptSha256.trim().toLowerCase() : '';
|
|
44
|
+
if (!/^[a-f0-9]{64}$/.test(expectedScriptSha256)) {
|
|
45
|
+
throw providerError('WINDOWS_RECOVERY_CONFIG_INVALID', 'Expected Recovery script digest is invalid.');
|
|
46
|
+
}
|
|
47
|
+
const deployment = raw.deployment;
|
|
48
|
+
const required = ['recoveryScriptHostPath', 'recoveryScriptWindowsPath', 'powershellHostPath', 'powershellWindowsPath', 'rescueRoot'];
|
|
49
|
+
if (!deployment || deployment.schemaVersion !== 1 || required.some((key) => typeof deployment[key] !== 'string' || !deployment[key].trim())) {
|
|
50
|
+
throw providerError('WINDOWS_RECOVERY_DEPLOYMENT_BINDING_INVALID', 'Windows host Recovery deployment binding is missing or invalid.');
|
|
51
|
+
}
|
|
52
|
+
return { schemaVersion: 1, expectedScriptSha256, deployment };
|
|
53
|
+
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function sha256(path) {
|
|
57
|
+
return createHash('sha256').update(readFileSync(path)).digest('hex');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function assertTrustedAssets(config) {
|
|
61
|
+
const { powershellHostPath, recoveryScriptHostPath } = config.deployment;
|
|
62
|
+
if (!existsSync(powershellHostPath)) throw providerError('WINDOWS_RECOVERY_POWERSHELL_MISSING', 'Bound Windows PowerShell executable is unavailable.');
|
|
63
|
+
if (!existsSync(recoveryScriptHostPath)) throw providerError('WINDOWS_RECOVERY_SCRIPT_MISSING', 'Bound ForgeRecovery.ps1 is unavailable.');
|
|
64
|
+
const actual = sha256(recoveryScriptHostPath);
|
|
65
|
+
if (actual !== config.expectedScriptSha256) {
|
|
66
|
+
throw providerError('WINDOWS_RECOVERY_SCRIPT_IDENTITY_MISMATCH', 'Installed ForgeRecovery.ps1 does not match the registered immutable identity.', false, { expected: config.expectedScriptSha256, actual });
|
|
67
|
+
}
|
|
68
|
+
return actual;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function minimalEnv() {
|
|
72
|
+
return Object.fromEntries(Object.entries({
|
|
73
|
+
PATH: '/usr/local/bin:/usr/bin:/bin', HOME: process.env.HOME, LANG: process.env.LANG, LC_ALL: process.env.LC_ALL,
|
|
74
|
+
}).filter(([, value]) => typeof value === 'string'));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function executeAction(actionId, input, configInput) {
|
|
78
|
+
if (!ACTIONS.includes(actionId)) throw providerError('WINDOWS_RECOVERY_ACTION_UNSUPPORTED', 'Unsupported fixed Windows host recovery action.');
|
|
79
|
+
if (!input || typeof input !== 'object' || Array.isArray(input) || Object.keys(input).length !== 0) {
|
|
80
|
+
throw providerError('WINDOWS_RECOVERY_ARGUMENTS_FORBIDDEN', 'Windows host recovery actions accept no caller-provided commands, paths, task names, service names, or shell arguments.');
|
|
81
|
+
}
|
|
82
|
+
const config = parseConfig(configInput);
|
|
83
|
+
const scriptSha256 = assertTrustedAssets(config);
|
|
84
|
+
const result = spawnSync(config.deployment.powershellHostPath, [
|
|
85
|
+
'-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-File', config.deployment.recoveryScriptWindowsPath, actionId,
|
|
86
|
+
], {
|
|
87
|
+
encoding: 'utf8', env: minimalEnv(), timeout: actionId === 'full_recover' ? 120_000 : 30_000,
|
|
88
|
+
maxBuffer: 256 * 1024, shell: false, windowsHide: true,
|
|
89
|
+
});
|
|
90
|
+
if (result.error) throw providerError('WINDOWS_RECOVERY_EXECUTION_FAILED', result.error.message, true);
|
|
91
|
+
if (result.status !== 0) {
|
|
92
|
+
throw providerError('WINDOWS_RECOVERY_ACTION_FAILED', String(result.stderr || result.stdout || 'fixed Windows recovery action failed').slice(-2000), false, { actionId, exitCode: result.status });
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
actionId,
|
|
96
|
+
readonly: READONLY_ACTIONS.has(actionId),
|
|
97
|
+
scriptSha256,
|
|
98
|
+
output: String(result.stdout || '').trim().slice(0, 32 * 1024),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function loadConfig() {
|
|
103
|
+
const path = new URL('./config.json', pathToFileURL(`${process.cwd()}/`).href);
|
|
104
|
+
return parseConfig(JSON.parse(readFileSync(path, 'utf8')));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export async function handleRequest(request, config = loadConfig()) {
|
|
108
|
+
if (request.actionId === 'manifest') {
|
|
109
|
+
return { id: PLUGIN_ID, name: 'Forge Windows Host Recovery', version: PLUGIN_VERSION, protocolVersion: '1.0', mode: 'external', scope: 'controller', provider: 'local-wsl-windows', capabilities: CAPABILITIES, actions: ACTIONS };
|
|
110
|
+
}
|
|
111
|
+
if (request.actionId === 'health') {
|
|
112
|
+
const scriptSha256 = assertTrustedAssets(config);
|
|
113
|
+
return { state: 'ready', powershellPath: config.deployment.powershellHostPath, recoveryScriptPath: config.deployment.recoveryScriptHostPath, deployment: config.deployment, scriptSha256, warnings: [] };
|
|
114
|
+
}
|
|
115
|
+
return executeAction(request.actionId, request.input, config);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function main() {
|
|
119
|
+
process.stdout.write(`${JSON.stringify({ schemaVersion: 1, type: 'handshake', protocolVersion: PROTOCOL_VERSION, pluginId: PLUGIN_ID, helperVersion: PLUGIN_VERSION, capabilities: CAPABILITIES })}\n`);
|
|
120
|
+
let raw = '';
|
|
121
|
+
for await (const chunk of process.stdin) raw += chunk;
|
|
122
|
+
let request;
|
|
123
|
+
try { request = JSON.parse(raw); } catch { throw providerError('WINDOWS_RECOVERY_PROTOCOL_INVALID', 'Request JSON is invalid.'); }
|
|
124
|
+
if (request.schemaVersion !== 1 || request.type !== 'execute' || typeof request.requestId !== 'string' || typeof request.actionId !== 'string' || !request.input || typeof request.input !== 'object' || Array.isArray(request.input)) {
|
|
125
|
+
throw providerError('WINDOWS_RECOVERY_PROTOCOL_INVALID', 'Request envelope is invalid.');
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
const result = await handleRequest(request);
|
|
129
|
+
process.stdout.write(`${JSON.stringify({ schemaVersion: 1, type: 'result', requestId: request.requestId, ok: true, result })}\n`);
|
|
130
|
+
} catch (error) {
|
|
131
|
+
process.stdout.write(`${JSON.stringify({ schemaVersion: 1, type: 'result', requestId: request.requestId, ok: false, error: { code: error?.code || 'WINDOWS_RECOVERY_FAILED', message: String(error?.message || 'Windows host recovery failed.').slice(0, 2000), retryable: error?.retryable === true, ...(error?.details ? { details: error.details } : {}) } })}\n`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const invokedDirectly = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
136
|
+
if (invokedDirectly) main().catch((error) => { process.stderr.write(`${String(error?.stack || error)}\n`); process.exitCode = 1; });
|
package/scripts/hook-shim.sh
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
# c. central bundle: ${FORGE_HOME}/hooks (installed by `install`)
|
|
23
23
|
# d. fallback: <repo>/.ai/hooks (vendored copy, pre-bundle installs)
|
|
24
24
|
#
|
|
25
|
-
# Opt-in marker: .ai/harness/workflow-contract.json
|
|
25
|
+
# Opt-in marker: forge.config.json; legacy .ai/harness/workflow-contract.json is read-only migration fallback
|
|
26
26
|
# Trust file: ${FORGE_HOME:-~/.forge}/trusted-repos — one primary
|
|
27
27
|
# repo root per line. Linked worktrees inherit trust from their
|
|
28
28
|
# primary repo so contract worktrees keep working. Manage with
|
|
@@ -41,7 +41,7 @@ TRUST_FILE="$FORGE_HOME/trusted-repos"
|
|
|
41
41
|
CENTRAL_HOOKS_DIR="$FORGE_HOME/hooks"
|
|
42
42
|
|
|
43
43
|
repo=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0
|
|
44
|
-
[ -f "$repo/.ai/harness/workflow-contract.json" ] || exit 0
|
|
44
|
+
[ -f "$repo/forge.config.json" ] || [ -f "$repo/.ai/harness/workflow-contract.json" ] || exit 0
|
|
45
45
|
|
|
46
46
|
# Safety: defer to project-level if it still exists (prevents double-fire on
|
|
47
47
|
# non-migrated repos). After `forge migrate <repo>` removes the project
|