@moretea-labs/forge 1.8.0 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +70 -7
- package/CHANGELOG.md +14 -0
- package/CLAUDE.md +17 -2
- package/CONTRIBUTING.md +3 -2
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/adapters/chatgpt/browser-delivery-host.ts +7 -1
- package/adapters/chatgpt/browser-delivery-runtime.ts +529 -56
- package/adapters/chatgpt/controller-binding-store.ts +101 -22
- package/adapters/chatgpt/controller-round-host.ts +15 -25
- package/adapters/chatgpt/controller-round-settlement-store.ts +58 -4
- package/adapters/chatgpt/provider-delivery.ts +228 -1
- package/adapters/chatgpt/work-conversation-binding-store.ts +79 -53
- package/adapters/computer/chatgpt-extension-target.ts +463 -0
- package/adapters/computer/chatgpt-target-router.ts +103 -0
- package/adapters/computer/chrome-extension/background.js +385 -0
- package/adapters/computer/chrome-extension/content.js +378 -0
- package/adapters/computer/chrome-extension/core.js +24 -0
- package/adapters/computer/chrome-extension/manifest.json +30 -0
- package/adapters/computer/desktop-operator-discovery.ts +11 -31
- package/adapters/computer/interaction-target-authority.ts +142 -18
- package/adapters/mcp/auth.ts +0 -1
- package/adapters/mcp/instructions.ts +5 -4
- package/adapters/mcp/multi-repository.ts +3 -19
- package/adapters/mcp/oauth.ts +73 -0
- package/adapters/mcp/paths.ts +4 -1
- package/adapters/mcp/runtime-gateway/automation-receipt-adapter.ts +87 -0
- package/adapters/mcp/runtime-gateway/bounded-result-runtime.ts +62 -0
- package/adapters/mcp/runtime-gateway/cognition-settlement.ts +201 -0
- package/adapters/mcp/runtime-gateway/context-adapter.ts +411 -73
- package/adapters/mcp/runtime-gateway/context-pack-sidecar.ts +91 -0
- package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +20 -5
- package/adapters/mcp/runtime-gateway/core-capability-adapter.ts +258 -0
- package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +10 -27
- package/adapters/mcp/runtime-gateway/engineering-tool-contract.ts +15 -0
- package/adapters/mcp/runtime-gateway/execution-tools.ts +8 -42
- package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +2 -1
- package/adapters/mcp/runtime-gateway/plugin-adapter.ts +117 -45
- package/adapters/mcp/runtime-gateway/process-tools.ts +190 -22
- package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +16 -4
- package/adapters/mcp/runtime-gateway/recovery-adapter.ts +69 -52
- package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +4 -35
- package/adapters/mcp/runtime-gateway/result-adapter.ts +10 -4
- package/adapters/mcp/runtime-gateway/router.ts +18 -504
- package/adapters/mcp/runtime-gateway/routing-policy.ts +499 -0
- package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +14 -65
- package/adapters/mcp/runtime-gateway/runtime-readiness-observation.ts +218 -0
- package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +113 -173
- package/adapters/mcp/runtime-gateway/runtime-tools.ts +15 -5
- package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +1 -0
- package/adapters/mcp/runtime-gateway/shared-adapter.ts +20 -0
- package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +358 -266
- package/adapters/mcp/runtime-gateway/typescript-navigation-sidecar.ts +136 -0
- package/adapters/mcp/runtime-gateway/work-adapter.ts +261 -3122
- package/adapters/mcp/runtime-gateway/work-controller-operations.ts +257 -0
- package/adapters/mcp/runtime-gateway/work-detail-projection.ts +60 -0
- package/adapters/mcp/runtime-gateway/work-input-wire-migration.ts +246 -0
- package/adapters/mcp/runtime-gateway/work-learning-operations.ts +145 -0
- package/adapters/mcp/runtime-gateway/work-list-projection.ts +38 -0
- package/adapters/mcp/runtime-gateway/work-plan-operations.ts +382 -0
- package/adapters/mcp/runtime-gateway/work-repair-adapter.ts +308 -0
- package/adapters/mcp/runtime-gateway/work-requirement-operations.ts +148 -0
- package/adapters/mcp/runtime-gateway/work-semantic-operations.ts +263 -0
- package/adapters/mcp/runtime-gateway/work-workflow-operations.ts +113 -0
- package/adapters/mcp/runtime-gateway/workflow-supervisor-adapter.ts +156 -0
- package/adapters/mcp/server.ts +67 -23
- package/adapters/mcp/setup.ts +1 -3
- package/adapters/mcp/tool-mapping/legacy-tool-service.ts +44 -170
- package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +4 -9
- package/adapters/mcp/tool-mapping/repository-tools.ts +133 -142
- package/adapters/mcp/toolset-names.ts +5 -0
- package/adapters/mcp/toolset.ts +6 -5
- package/adapters/mcp/transports/http-observation.ts +365 -0
- package/adapters/mcp/transports/http.ts +154 -677
- package/adapters/mcp/transports/oauth-http.ts +322 -0
- package/adapters/mcp/transports/session-registry.ts +6 -0
- package/adapters/mcp/tunnels/openai-secure-tunnel.ts +89 -5
- package/assets/plugin-registry.v1.json +6 -4
- package/assets/reference-configs/agentic-development-flow.md +25 -0
- package/assets/reference-configs/global-working-rules.md +19 -0
- package/assets/skill-version.json +2 -2
- package/docs/architecture/CURRENT.md +111 -41
- package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +14 -5
- package/docs/architecture/decisions/20260823-immediate-controller-relay.md +6 -3
- package/docs/architecture/decisions/20260907-assistant-learning-loop.md +2 -0
- package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +19 -4
- package/docs/architecture/decisions/20260916-workflow-supervisor-authority.md +249 -0
- package/docs/architecture/decisions/20260917-cognitive-memory-foundation.md +82 -0
- package/docs/architecture/decisions/20260918-stable-candidate-release-lifecycle.md +221 -0
- package/docs/architecture/decisions/20260919-autonomous-work-liveness.md +62 -0
- package/docs/architecture/decisions/20260924-thin-semantic-working-context.md +72 -0
- package/docs/architecture/decisions/20261001-supervisor-turn-authority.md +237 -0
- package/docs/architecture/decisions/20261005-forge-v3-desktop-client-boundary.md +77 -0
- package/docs/architecture/domains/verification.md +1 -0
- package/docs/architecture/domains/workflow-engine.md +6 -0
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
- package/docs/forge-plugin-management.md +2 -2
- package/docs/operations/controller-performance-and-502.md +17 -0
- package/docs/operations/controller-reliability-runbook.md +2 -2
- package/docs/operations/features.md +1 -1
- package/docs/operations/features.zh-CN.md +1 -1
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/mcp-tool-exposure.md +2 -0
- package/docs/operations/releasing.md +2 -0
- package/docs/operations/standalone-disaster-recovery.md +14 -6
- package/docs/wiki/Implementation.md +1 -1
- package/package.json +25 -14
- package/packages/kernel/cognition/api/index.ts +6 -0
- package/packages/kernel/cognition/application/activation-service.ts +451 -0
- package/packages/kernel/cognition/application/consolidation-service.ts +185 -0
- package/packages/kernel/cognition/application/legacy-experience-adapter.ts +38 -0
- package/packages/kernel/cognition/application/memory-service.ts +64 -0
- package/packages/kernel/cognition/domain/learning.ts +106 -0
- package/packages/kernel/cognition/domain/memory.ts +254 -0
- package/packages/kernel/controller/api/index.ts +1 -0
- package/packages/kernel/controller/application/continuation-service.ts +281 -0
- package/packages/kernel/controller/application/controller-round-service.ts +12 -0
- package/packages/kernel/controller/domain/controller-round-transition-policy.ts +242 -21
- package/packages/kernel/controller/domain/controller-round.ts +4 -1
- package/packages/kernel/controller/domain/execution-quality.ts +3 -1
- package/packages/kernel/controller/infrastructure/controller-round-store.ts +689 -103
- package/packages/kernel/controller/infrastructure/controller-session-store.ts +297 -71
- package/packages/kernel/controller/ports/controller-host.ts +2 -0
- package/packages/kernel/identity/api/index.ts +10 -0
- package/packages/kernel/identity/domain/activity-projection.ts +43 -0
- package/packages/kernel/identity/domain/capability-broker.ts +60 -0
- package/packages/kernel/identity/domain/grant.ts +72 -0
- package/packages/kernel/identity/domain/owned-resource.ts +116 -0
- package/packages/kernel/identity/domain/user-request.ts +93 -0
- package/packages/kernel/identity/infrastructure/activity-projection-store.ts +77 -0
- package/packages/kernel/identity/infrastructure/capability-broker.ts +81 -0
- package/packages/kernel/identity/infrastructure/grant-store.ts +284 -0
- package/packages/kernel/identity/infrastructure/owned-resource-store.ts +259 -0
- package/packages/kernel/identity/infrastructure/user-request-store.ts +182 -0
- package/packages/kernel/scheduler/api/index.ts +0 -2
- package/packages/kernel/scheduler/application/schedule-service.ts +3 -0
- package/packages/kernel/scheduler/application/settlement.ts +0 -15
- package/packages/kernel/scheduler/domain/schedule.ts +87 -0
- package/packages/kernel/scheduler/infrastructure/schedule-store.ts +153 -133
- package/packages/kernel/work/api/index.ts +3 -2
- package/packages/kernel/work/application/work-service.ts +17 -42
- package/packages/kernel/work/domain/current-task-lineage.ts +104 -0
- package/packages/kernel/work/domain/engineering-design.ts +10 -5
- package/packages/kernel/work/domain/engineering-profile.ts +8 -12
- package/packages/kernel/work/domain/evaluation-promotion-receipt.ts +186 -0
- package/packages/kernel/work/domain/execution-concurrency.ts +19 -26
- package/packages/kernel/work/domain/execution-snapshot.ts +0 -31
- package/packages/kernel/work/domain/implementation-review.ts +14 -331
- package/packages/kernel/work/domain/objective-graph.ts +119 -0
- package/packages/kernel/work/domain/project-engineering-contract.ts +23 -1
- package/packages/kernel/work/domain/repository-completion-receipt.ts +1 -1
- package/packages/kernel/work/domain/state-machine.ts +25 -63
- package/packages/kernel/work/domain/types.ts +57 -51
- package/packages/kernel/work/infrastructure/work-contract-store.ts +819 -728
- package/packages/kernel/work/ports/work-contract-store.ts +5 -2
- package/packages/plugin-runtime/browser/session-authority.ts +2 -1
- package/packages/plugin-runtime/computer/chatgpt-identity.ts +24 -0
- package/packages/plugin-runtime/computer/conversation-target.ts +143 -0
- package/packages/plugin-runtime/computer/index.ts +2 -0
- package/packages/plugin-runtime/computer/target-authority.ts +26 -1
- package/packages/plugin-runtime/external/unix-jsonl-transport.ts +142 -18
- package/packages/protocols/computer/contract.ts +27 -1
- package/scripts/activate-source-baseline.ts +7 -9
- package/scripts/canary-controller-semantic-wait.ts +12 -24
- package/scripts/check-mcp-compatibility.ts +48 -136
- package/scripts/check-runtime-architecture.mjs +622 -217
- package/scripts/forge-local-recovery-helper.mjs +93 -15
- package/scripts/lib/project-init-lib.sh +3 -0
- package/scripts/open-source-audit-allowlist.txt +1 -2
- package/scripts/run-bounded-command.mjs +5 -0
- package/scripts/run-governed-gate.ts +0 -1
- package/scripts/run-mobile-monitor-bridge.ts +0 -1
- package/scripts/smoke-control-plane-failure-domain.ts +94 -0
- package/scripts/smoke-mcp-http-runtime.ts +5 -0
- package/scripts/smoke-runtime-recovery.ts +12 -43
- package/scripts/smoke-schedule-engine.ts +23 -15
- package/scripts/smoke-unattended-transport-recovery.ts +223 -0
- package/scripts/smoke-user-request-continuation-restart.ts +232 -0
- package/scripts/smoke-workflow-supervisor-chrome-adapter.ts +92 -0
- package/scripts/smoke-workflow-supervisor-process-restart.ts +276 -0
- package/scripts/smoke-workflow-supervisor-restart-recovery.ts +150 -0
- package/scripts/smoke-workflow-supervisor.ts +103 -0
- package/scripts/verify-forge-runtime.sh +2 -1
- package/src/cli/agent-jobs/executor-health.ts +11 -34
- package/src/cli/agent-jobs/integration.ts +18 -49
- package/src/cli/agent-jobs/job-manager.ts +7 -3
- package/src/cli/commands/brain-assistant.ts +27 -13
- package/src/cli/commands/brain.ts +13 -15
- package/src/cli/commands/chatgpt.ts +1 -163
- package/src/cli/commands/recovery.ts +135 -121
- package/src/cli/commands/runtime.ts +3 -3
- package/src/cli/commands/setup-profile.ts +8 -0
- package/src/cli/controller/check-runner.ts +13 -2
- package/src/cli/controller/completion-backlog.ts +3 -3
- package/src/cli/controller/completion-orchestrator.ts +4 -43
- package/src/cli/controller/completion-target.ts +3 -72
- package/src/cli/controller/context/source-materializer.ts +81 -100
- package/src/cli/controller/context-pack.ts +10 -8
- package/src/cli/controller/execution-completion.ts +4 -7
- package/src/cli/controller/execution-policy.ts +36 -153
- package/src/cli/controller/governance.ts +2 -3
- package/src/cli/controller/issue-store.ts +4 -99
- package/src/cli/controller/launch-agents.ts +59 -18
- package/src/cli/controller/operational-plan.ts +24 -51
- package/src/cli/controller/runtime-config.ts +5 -1
- package/src/cli/controller/stuck-state-migration.ts +2 -3
- package/src/cli/controller/systemd-user.ts +39 -1
- package/src/cli/editing/edit-session.ts +1 -7
- package/src/cli/local-bridge/console-automations.ts +18 -13
- package/src/cli/local-bridge/console-view-models.ts +3 -12
- package/src/cli/local-bridge/facade-api.ts +64 -191
- package/src/cli/local-bridge/server.ts +24 -147
- package/src/cli/local-bridge/types.ts +0 -1
- package/src/cli/mcp/oauth-client-retirement.ts +74 -0
- package/src/cli/repositories/command-executor.ts +29 -23
- package/src/cli/repositories/command-scope.ts +35 -16
- package/src/cli/repositories/controller-home.ts +48 -0
- package/src/cli/repositories/ephemeral-workspace.ts +20 -30
- package/src/cli/repositories/git-command-executor.ts +2 -2
- package/src/cli/repositories/registry.ts +77 -11
- package/src/cli/repositories/structured-git.ts +62 -2
- package/src/cli/repository/session-cache.ts +92 -6
- package/src/runtime/context/assistant-context.ts +20 -7
- package/src/runtime/context/assistant-work-context.ts +89 -27
- package/src/runtime/context/automatic-learning.ts +565 -0
- package/src/runtime/context/cognitive-attention.ts +173 -0
- package/src/runtime/context/cognitive-skill-canary.ts +158 -0
- package/src/runtime/context/context-closure.ts +46 -9
- package/src/runtime/context/context-pack-process.ts +103 -0
- package/src/runtime/context/context-plane.ts +1 -9
- package/src/runtime/context/direct-controller-learning.ts +298 -0
- package/src/runtime/context/semantic-navigation-contract.ts +9 -0
- package/src/runtime/context/semantic-navigation.ts +6 -34
- package/src/runtime/context/typescript-navigation-process.ts +145 -0
- package/src/runtime/context/typescript-navigation.ts +89 -3
- package/src/runtime/control-plane/cleanup-artifact-retention.ts +212 -9
- package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +9 -47
- package/src/runtime/control-plane/execution/controller-authority-recovery.ts +34 -4
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +29 -20
- package/src/runtime/control-plane/execution/execution-identity.ts +23 -3
- package/src/runtime/control-plane/execution/repository-work-attribution.ts +7 -30
- package/src/runtime/control-plane/execution/retained-work-resume.ts +206 -199
- package/src/runtime/control-plane/execution/session-store.ts +31 -1
- package/src/runtime/control-plane/execution/validation.ts +6 -8
- package/src/runtime/control-plane/execution/work-authority-reconciler.ts +13 -42
- package/src/runtime/control-plane/execution/work-check-evidence.ts +137 -0
- package/src/runtime/control-plane/execution/work-completion-authority.ts +30 -85
- package/src/runtime/control-plane/execution/work-delivery-service.ts +497 -0
- package/src/runtime/control-plane/execution/work-evidence-policy.ts +12 -50
- package/src/runtime/control-plane/execution/work-execution-support.ts +50 -149
- package/src/runtime/control-plane/execution/work-handle-authority.ts +188 -109
- package/src/runtime/control-plane/execution/work-handle-store.ts +19 -43
- package/src/runtime/control-plane/execution/work-head-settlement.ts +53 -20
- package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +52 -29
- package/src/runtime/control-plane/execution/work-operation-service.ts +43 -27
- package/src/runtime/control-plane/execution/work-preparation-service.ts +126 -266
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +2 -3
- package/src/runtime/control-plane/execution/work-revision-diff.ts +39 -0
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +173 -186
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +28 -84
- package/src/runtime/control-plane/execution/work-verification-service.ts +406 -50
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +5 -1
- package/src/runtime/control-plane/facade/capability-registry.ts +362 -37
- package/src/runtime/control-plane/facade/check-normalization.ts +3 -9
- package/src/runtime/control-plane/facade/codex-delegation.ts +10 -11
- package/src/runtime/control-plane/facade/controller-connection.ts +135 -0
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +1 -20
- package/src/runtime/control-plane/facade/goal-workloop.ts +281 -1753
- package/src/runtime/control-plane/facade/handoff-inbox-application.ts +12 -25
- package/src/runtime/control-plane/facade/handoff-inbox-store.ts +256 -52
- package/src/runtime/control-plane/facade/index.ts +0 -1
- package/src/runtime/control-plane/facade/operation-digest.ts +12 -15
- package/src/runtime/control-plane/facade/plan-contract-store.ts +451 -991
- package/src/runtime/control-plane/facade/policy-gate.ts +0 -23
- package/src/runtime/control-plane/facade/repository-work-admission.ts +12 -22
- package/src/runtime/control-plane/facade/requirement-authority.ts +102 -199
- package/src/runtime/control-plane/facade/requirement-board.ts +3 -3
- package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +51 -14
- package/src/runtime/control-plane/facade/self-healing-loop.ts +18 -53
- package/src/runtime/control-plane/facade/suggested-actions.ts +15 -18
- package/src/runtime/control-plane/facade/types.ts +49 -108
- package/src/runtime/control-plane/facade/work-continuation.ts +8 -8
- package/src/runtime/control-plane/generated-cache-retention.ts +97 -1
- package/src/runtime/control-plane/global-scheduler/autonomous-continuation.ts +248 -0
- package/src/runtime/control-plane/global-scheduler/durable-admission.ts +1 -1
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +239 -112
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +2 -1
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +66 -40
- package/src/runtime/control-plane/global-scheduler/wake-signal.ts +42 -2
- package/src/runtime/control-plane/governance/access-policy.ts +19 -2
- package/src/runtime/control-plane/governance/authorization.ts +20 -3
- package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +63 -300
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +28 -300
- package/src/runtime/control-plane/launcher/local-conversation-controller.ts +238 -0
- package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +5 -2
- package/src/runtime/control-plane/launcher/thin-launcher.ts +96 -240
- package/src/runtime/control-plane/persistence/cognition-store.ts +820 -0
- package/src/runtime/control-plane/persistence/experience-store.ts +162 -29
- package/src/runtime/control-plane/persistence/requirement-store.ts +216 -74
- package/src/runtime/control-plane/persistence/sqlite-store.ts +113 -6
- package/src/runtime/control-plane/release-retention.ts +135 -47
- package/src/runtime/control-plane/repository-namespace-retention.ts +2 -2
- package/src/runtime/control-plane/runtime-cleanup.ts +290 -21
- package/src/runtime/control-plane/workspace/project-onboarding.ts +80 -0
- package/src/runtime/control-plane/workspace/semantic-sync-service.ts +19 -5
- package/src/runtime/control-plane/workspace/workspace-store.ts +169 -0
- package/src/runtime/diagnostics/incident-repair.ts +313 -146
- package/src/runtime/diagnostics/mcp-timing.ts +114 -0
- package/src/runtime/evidence/artifact-store.ts +158 -40
- package/src/runtime/evidence/evidence-store.ts +112 -8
- package/src/runtime/evidence/output-handle-index.ts +225 -0
- package/src/runtime/evidence/result-store.ts +158 -44
- package/src/runtime/execution/jobs/store.ts +3 -4
- package/src/runtime/execution/managed-workspace.ts +57 -0
- package/src/runtime/execution/process-runtime/check-facade.ts +1 -1
- package/src/runtime/execution/process-runtime/check-result-retention.ts +2 -2
- package/src/runtime/execution/process-runtime/check-result.ts +105 -1
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +18 -8
- package/src/runtime/execution/process-runtime/command-facade.ts +79 -27
- package/src/runtime/execution/process-runtime/gc.ts +15 -2
- package/src/runtime/execution/process-runtime/handle-index.ts +176 -0
- package/src/runtime/execution/process-runtime/host-command-lane.ts +139 -0
- package/src/runtime/execution/process-runtime/index.ts +3 -0
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +251 -72
- package/src/runtime/execution/process-runtime/persisted-check.ts +3 -3
- package/src/runtime/execution/process-runtime/process-runner-entry.ts +47 -5
- package/src/runtime/execution/process-runtime/process-scope.ts +160 -0
- package/src/runtime/execution/process-runtime/resource-claims.ts +65 -61
- package/src/runtime/execution/process-runtime/runtime.ts +35 -2
- package/src/runtime/execution/process-runtime/store.ts +30 -2
- package/src/runtime/execution/process-runtime/types.ts +6 -1
- package/src/runtime/execution/workers/executor.ts +2 -6
- package/src/runtime/platform/service-inventory.ts +130 -0
- package/src/runtime/platform/service-manager.ts +8 -0
- package/src/runtime/plugins/action-application.ts +85 -28
- package/src/runtime/plugins/app-store-connect-adapter.ts +38 -14
- package/src/runtime/plugins/browser-adapter.ts +757 -34
- package/src/runtime/plugins/browser-automation-service.ts +23 -3
- package/src/runtime/plugins/browser-macos-bridge.ts +321 -29
- package/src/runtime/plugins/browser-manifest-surface.ts +45 -11
- package/src/runtime/plugins/browser-native-session-service.ts +63 -13
- package/src/runtime/plugins/browser-registration.ts +16 -0
- package/src/runtime/plugins/browser-runtime.ts +14 -1
- package/src/runtime/plugins/browser-session-authority.ts +2 -2
- package/src/runtime/plugins/browser-session-store.ts +46 -76
- package/src/runtime/plugins/capability-authorization-grants.ts +258 -62
- package/src/runtime/plugins/computer-chatgpt-browser-adapter-installer.ts +219 -0
- package/src/runtime/plugins/computer-chatgpt-macos-target.ts +891 -0
- package/src/runtime/plugins/computer-registration.ts +222 -11
- package/src/runtime/plugins/desktop-operator-external-policy.ts +36 -4
- package/src/runtime/plugins/desktop-operator-registration.ts +12 -4
- package/src/runtime/plugins/external-adapter.ts +42 -6
- package/src/runtime/plugins/external-registration.ts +21 -1
- package/src/runtime/plugins/gmail-adapter.ts +23 -2
- package/src/runtime/plugins/google-calendar-adapter.ts +2 -2
- package/src/runtime/plugins/google-shared.ts +14 -7
- package/src/runtime/plugins/google-tasks-adapter.ts +2 -2
- package/src/runtime/plugins/ios-adapter.ts +1 -1
- package/src/runtime/plugins/lightweight-action.ts +208 -2
- package/src/runtime/plugins/local-system-adapter.ts +12 -2
- package/src/runtime/plugins/managed-process-adapter.ts +12 -5
- package/src/runtime/plugins/plugin-action-sidecar.ts +17 -13
- package/src/runtime/plugins/plugin-management-adapter.ts +1 -1
- package/src/runtime/plugins/store.ts +602 -428
- package/src/runtime/plugins/types.ts +20 -0
- package/src/runtime/plugins/xiaohongshu-publish.ts +16 -0
- package/src/runtime/projections/invalidation.ts +7 -3
- package/src/runtime/projections/materialized-view.ts +80 -36
- package/src/runtime/recovery/actions.ts +3 -3
- package/src/runtime/recovery/capability-action-application.ts +46 -45
- package/src/runtime/recovery/lifecycle-inventory.ts +29 -29
- package/src/runtime/recovery/maintenance-executor.ts +374 -123
- package/src/runtime/recovery/store.ts +29 -11
- package/src/runtime/release/release-coordinator.ts +296 -0
- package/src/runtime/release/release-session.ts +716 -0
- package/src/runtime/release/release-state-migration.ts +25 -0
- package/src/runtime/resources/leases/store.ts +56 -25
- package/src/runtime/root/computer-composition.ts +191 -6
- package/src/runtime/root/controller-progression-composition.ts +192 -0
- package/src/runtime/root/controller-round-composition.ts +20 -1
- package/src/runtime/root/deployment-topology.ts +106 -0
- package/src/runtime/root/entry.ts +4 -3
- package/src/runtime/root/known-good-recovery.ts +184 -0
- package/src/runtime/root/local-bridge.ts +0 -1
- package/src/runtime/root/mcp-transport.ts +111 -18
- package/src/runtime/root/ownership.ts +261 -9
- package/src/runtime/root/package-connector-service.ts +105 -19
- package/src/runtime/root/package-runtime-release.ts +117 -18
- package/src/runtime/root/package-runtime-service.ts +17 -11
- package/src/runtime/root/release-execution-canary.ts +44 -12
- package/src/runtime/root/release-loader.ts +90 -8
- package/src/runtime/root/release-manifest.ts +80 -6
- package/src/runtime/root/release-materialize.ts +372 -28
- package/src/runtime/root/release-store.ts +395 -36
- package/src/runtime/root/runtime-lane.ts +191 -0
- package/src/runtime/root/runtime.ts +159 -27
- package/src/runtime/root/scheduled-controller-composition.ts +28 -3
- package/src/runtime/root/service-runner.ts +2 -2
- package/src/runtime/root/service.ts +36 -33
- package/src/runtime/root/status.ts +48 -0
- package/src/runtime/root/types.ts +32 -3
- package/src/runtime/root/workflow-supervisor-composition.ts +493 -0
- package/src/runtime/root/workflow-supervisor-runtime.ts +209 -0
- package/src/runtime/root/write-fence.ts +50 -2
- package/src/runtime/safe-tooling/google-credential-store.ts +76 -21
- package/src/runtime/safe-tooling/google-oauth-broker.ts +25 -7
- package/src/runtime/safe-tooling/workspace-auth.ts +1 -1
- package/src/runtime/shared/bounded-child-supervisor.ts +9 -1
- package/src/runtime/shared/process-environment.ts +20 -9
- package/src/runtime/shared/response-budget.ts +1 -3
- package/src/runtime/standalone-recovery/controller-home-migration.ts +16 -24
- package/src/runtime/standalone-recovery/core.ts +3702 -342
- package/src/runtime/standalone-recovery/entry.ts +469 -39
- package/src/runtime/standalone-recovery/installer.ts +262 -148
- package/src/runtime/standalone-recovery/mcp-server.ts +29 -193
- package/src/runtime/standalone-recovery/operation-lock.ts +1 -1
- package/src/runtime/standalone-recovery/performance.ts +54 -16
- package/src/runtime/standalone-recovery/release.ts +53 -5
- package/src/runtime/standalone-recovery/service-labels.ts +5 -2
- package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +5 -2
- package/src/runtime/workflow/schedules/browser-probe.ts +17 -6
- package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +10 -3
- package/src/runtime/workflow/schedules/engine.ts +138 -172
- package/src/runtime/workflow/schedules/work-continuation.ts +44 -5
- package/src/runtime/workflows/runtime.ts +15 -19
- package/src/testing/test-governance.ts +5 -1
- package/supervisor/chatgpt-conversation.ts +23 -0
- package/supervisor/client.ts +201 -0
- package/supervisor/control-plane.ts +868 -0
- package/supervisor/entry.ts +15 -0
- package/supervisor/forge-validators.ts +80 -0
- package/supervisor/index.ts +5 -0
- package/supervisor/native-browser-adapter.ts +786 -0
- package/supervisor/native-messaging/host.ts +39 -0
- package/supervisor/native-messaging/manifest.ts +26 -0
- package/supervisor/native-messaging/protocol.ts +27 -0
- package/supervisor/paths.ts +18 -0
- package/supervisor/protocol.ts +202 -0
- package/supervisor/server.ts +404 -0
- package/supervisor/store.ts +1451 -0
- package/supervisor/tsconfig.json +8 -0
- package/supervisor/types.ts +255 -0
- package/adapters/chatgpt/controller-host.ts +0 -119
- package/adapters/mcp/controller-round-compatibility.ts +0 -184
- package/adapters/mcp/frozen-client-semantic-compatibility.ts +0 -292
- package/adapters/mcp/runtime-gateway/work-compat-adapter.ts +0 -502
- package/packages/kernel/progression/api/index.ts +0 -2
- package/packages/kernel/progression/application/projector.ts +0 -211
- package/packages/kernel/progression/domain/types.ts +0 -116
- package/packages/kernel/progression/index.ts +0 -1
- package/packages/kernel/scheduler/application/continuation-service.ts +0 -212
- package/packages/kernel/scheduler/domain/continuation.ts +0 -38
- package/packages/kernel/scheduler/infrastructure/continuation-dispatch-store.ts +0 -38
- package/packages/kernel/work/domain/admission-policy.ts +0 -34
- package/scripts/benchmark-background-check-overlap.ts +0 -228
- package/scripts/benchmark-computer-hotpath.ts +0 -255
- package/scripts/benchmark-controller-hotpath.ts +0 -212
- package/scripts/benchmark-controller-round2.ts +0 -191
- package/scripts/benchmark-ios-agent-device.ts +0 -511
- package/scripts/benchmark-mcp-host.sh +0 -117
- package/scripts/benchmark-mcp-transport.ts +0 -169
- package/scripts/benchmark-operational-memory-activation.ts +0 -235
- package/scripts/benchmark-operational-shadow.ts +0 -28
- package/scripts/benchmark-quality-harness.ts +0 -146
- package/scripts/benchmark-route-session-concurrency.ts +0 -915
- package/scripts/benchmark-thin-harness-gateway-ab.ts +0 -632
- package/scripts/benchmark-thin-harness-mcp-e2e.ts +0 -498
- package/scripts/benchmark-thin-harness.ts +0 -251
- package/scripts/benchmark-typescript-navigation.ts +0 -186
- package/scripts/benchmark-work-efficiency.ts +0 -334
- package/scripts/build-controller-ui.ts +0 -28
- package/scripts/check-npm-release.sh +0 -32
- package/scripts/check-public-release-surface.sh +0 -19
- package/scripts/check-release-published.sh +0 -57
- package/scripts/check-release-readiness.sh +0 -37
- package/scripts/check-release-version.mjs +0 -74
- package/scripts/check-tarball-install-smoke.sh +0 -150
- package/scripts/package-source-archive.sh +0 -147
- package/scripts/public-release-files.txt +0 -39
- package/scripts/public-release.ts +0 -174
- package/scripts/publish-release-tarball.sh +0 -27
- package/scripts/route-nl-vs-ts-eval.ts +0 -679
- package/scripts/stage-runtime-release.ts +0 -44
- package/src/cli/controller/progress.ts +0 -656
- package/src/cli/controller/work-mode.ts +0 -181
- package/src/cli/local-bridge/console-assets.ts +0 -17
- package/src/cli/local-bridge/dashboard.ts +0 -17
- package/src/cli/local-bridge/frontend/src/App.tsx +0 -10
- package/src/cli/local-bridge/frontend/src/components/Button.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/components/DetailPane.tsx +0 -3
- package/src/cli/local-bridge/frontend/src/components/SearchField.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/components/SectionHeader.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/components/Segmented.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/components/StatusText.tsx +0 -3
- package/src/cli/local-bridge/frontend/src/design/icons.tsx +0 -11
- package/src/cli/local-bridge/frontend/src/design/tokens.css +0 -8
- package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +0 -153
- package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +0 -37
- package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +0 -183
- package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +0 -2
- package/src/cli/local-bridge/frontend/src/features/system/SystemView.tsx +0 -2
- package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +0 -4
- package/src/cli/local-bridge/frontend/src/lib/api.ts +0 -30
- package/src/cli/local-bridge/frontend/src/lib/format.ts +0 -4
- package/src/cli/local-bridge/frontend/src/lib/routes.ts +0 -7
- package/src/cli/local-bridge/frontend/src/main.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/shell/AppShell.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +0 -2
- package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +0 -4
- package/src/cli/local-bridge/frontend/src/styles.css +0 -28
- package/src/cli/local-bridge/frontend/src/types.ts +0 -15
- package/src/cli/local-bridge/frontend/src/vite-env.d.ts +0 -1
- package/src/cli/local-bridge/frontend/vite.config.ts +0 -27
- package/src/cli/local-bridge/ui-dist/app.css +0 -1
- package/src/cli/local-bridge/ui-dist/app.js +0 -11228
- package/src/runtime/control-plane/execution/content-equivalent-commit-authority.ts +0 -169
- package/src/runtime/control-plane/execution/direct-canonical-work-reconciliation.ts +0 -85
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +0 -655
- package/src/runtime/control-plane/execution/work-finalization-service.ts +0 -2850
- package/src/runtime/control-plane/execution/work-path-scope.ts +0 -37
- package/src/runtime/control-plane/execution/work-task-receipt.ts +0 -468
- package/src/runtime/control-plane/facade/controller-round-relay.ts +0 -3
- package/src/runtime/control-plane/facade/work-admission-policy.ts +0 -142
- package/src/runtime/control-plane/facade/work-implementation-review.ts +0 -2
- package/src/runtime/control-plane/facade/work-state-machine.ts +0 -2
- package/src/runtime/control-plane/facade/work-task-projection.ts +0 -66
- package/src/runtime/control-plane/routing/index.ts +0 -1
- package/src/runtime/control-plane/routing/route-policy.ts +0 -401
- package/src/runtime/control-plane/routing/workspace-admission.ts +0 -70
|
@@ -1,5 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
findActiveCanonicalGrant,
|
|
3
|
+
listCanonicalGrants,
|
|
4
|
+
reconcileCanonicalGrants,
|
|
5
|
+
recordCanonicalGrant,
|
|
6
|
+
revokeCanonicalGrant,
|
|
7
|
+
type Grant,
|
|
8
|
+
} from '../../../packages/kernel/identity/api/index';
|
|
1
9
|
import { randomUUID } from 'crypto';
|
|
2
|
-
import { existsSync,
|
|
10
|
+
import { existsSync, renameSync } from 'fs';
|
|
3
11
|
import { join } from 'path';
|
|
4
12
|
import { controllerSystemRoot } from '../../cli/repositories/controller-home';
|
|
5
13
|
import { ControllerLockContentionError, withControllerLock } from '../../cli/repositories/locks';
|
|
@@ -31,7 +39,7 @@ export interface PluginCapabilityAuthorizationGrant {
|
|
|
31
39
|
schemaVersion: 1;
|
|
32
40
|
grantId: string;
|
|
33
41
|
ownerScope: string;
|
|
34
|
-
repoId
|
|
42
|
+
repoId?: string;
|
|
35
43
|
pluginId: string;
|
|
36
44
|
capabilityId: string;
|
|
37
45
|
target: AssistantPluginAuthorizationTarget;
|
|
@@ -51,7 +59,7 @@ interface PluginCapabilityAuthorizationGrantStore {
|
|
|
51
59
|
|
|
52
60
|
export interface PluginCapabilityAuthorizationQuery {
|
|
53
61
|
ownerScope: string;
|
|
54
|
-
repoId
|
|
62
|
+
repoId?: string;
|
|
55
63
|
pluginId: string;
|
|
56
64
|
capabilityId: string;
|
|
57
65
|
target: AssistantPluginAuthorizationTarget;
|
|
@@ -73,12 +81,19 @@ export interface RevokePluginCapabilityAuthorizationInput {
|
|
|
73
81
|
now?: Date;
|
|
74
82
|
}
|
|
75
83
|
|
|
76
|
-
|
|
77
|
-
readonly
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
export interface ReconcilePluginCapabilityAuthorizationsInput {
|
|
85
|
+
retiredOwnerScopes?: readonly string[];
|
|
86
|
+
now?: Date;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface ReconcilePluginCapabilityAuthorizationsResult {
|
|
90
|
+
removedRetiredOwner: number;
|
|
91
|
+
removedRevoked: number;
|
|
92
|
+
removedExpired: number;
|
|
93
|
+
removedTotal: number;
|
|
94
|
+
remaining: number;
|
|
95
|
+
changed: boolean;
|
|
96
|
+
}
|
|
82
97
|
|
|
83
98
|
const GENERIC_PLUGIN_AUTHORIZATION_ACTORS = new Set(['', 'anonymous', 'plugin_action_execute']);
|
|
84
99
|
|
|
@@ -156,7 +171,10 @@ function validatePersistedGrant(value: unknown, index: number): PluginCapability
|
|
|
156
171
|
schemaVersion: 1,
|
|
157
172
|
grantId: string('grantId'),
|
|
158
173
|
ownerScope: string('ownerScope'),
|
|
159
|
-
repoId
|
|
174
|
+
// Pre-canonical plugin grants could omit repoId for controller/account targets.
|
|
175
|
+
// Normalize that omission once during the one-way migration; authorization
|
|
176
|
+
// against the canonical Grant store is exact after this point.
|
|
177
|
+
repoId: typeof record.repoId === 'string' && record.repoId.trim() ? record.repoId.trim() : 'controller:global',
|
|
160
178
|
pluginId: string('pluginId'),
|
|
161
179
|
capabilityId: string('capabilityId'),
|
|
162
180
|
target: normalizeTarget({
|
|
@@ -180,6 +198,41 @@ export function pluginCapabilityAuthorizationGrantStorePath(controllerHome: stri
|
|
|
180
198
|
return join(controllerSystemRoot(controllerHome), 'plugin-capability-authorizations', 'grants.json');
|
|
181
199
|
}
|
|
182
200
|
|
|
201
|
+
function legacyPluginGrantMigrationMarkerPath(controllerHome: string): string {
|
|
202
|
+
return join(controllerSystemRoot(controllerHome), 'plugin-capability-authorizations', 'migration.json');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Durable proof that this Controller Home already ran the one-way migration, so
|
|
207
|
+
* authorization reads stop consulting the legacy file at all.
|
|
208
|
+
*/
|
|
209
|
+
interface LegacyPluginGrantMigrationMarker {
|
|
210
|
+
schemaVersion: 1;
|
|
211
|
+
migratedAt: string;
|
|
212
|
+
legacyGrantCount: number;
|
|
213
|
+
canonicalGrantCount: number;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function readLegacyPluginGrantMigrationMarker(controllerHome: string): LegacyPluginGrantMigrationMarker | undefined {
|
|
217
|
+
const path = legacyPluginGrantMigrationMarkerPath(controllerHome);
|
|
218
|
+
if (!existsSync(path)) return undefined;
|
|
219
|
+
try {
|
|
220
|
+
const raw = readJsonFile<Record<string, unknown>>(path);
|
|
221
|
+
if (raw?.schemaVersion !== 1
|
|
222
|
+
|| typeof raw.migratedAt !== 'string'
|
|
223
|
+
|| typeof raw.legacyGrantCount !== 'number'
|
|
224
|
+
|| typeof raw.canonicalGrantCount !== 'number') {
|
|
225
|
+
throw new Error('migration marker schema is invalid');
|
|
226
|
+
}
|
|
227
|
+
return raw as unknown as LegacyPluginGrantMigrationMarker;
|
|
228
|
+
} catch (error) {
|
|
229
|
+
throw new PluginCapabilityAuthorizationGrantError(
|
|
230
|
+
'PLUGIN_CAPABILITY_GRANT_STORE_CORRUPT',
|
|
231
|
+
`Plugin capability authorization migration marker is corrupt: ${error instanceof Error ? error.message : String(error)}`,
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
183
236
|
function loadStore(controllerHome: string): PluginCapabilityAuthorizationGrantStore {
|
|
184
237
|
const path = pluginCapabilityAuthorizationGrantStorePath(controllerHome);
|
|
185
238
|
if (!existsSync(path)) return { schemaVersion: 1, grants: [] };
|
|
@@ -197,21 +250,123 @@ function loadStore(controllerHome: string): PluginCapabilityAuthorizationGrantSt
|
|
|
197
250
|
}
|
|
198
251
|
}
|
|
199
252
|
|
|
200
|
-
function
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
253
|
+
function pluginMetadataFromCanonicalGrant(grant: Grant): { pluginId: string; capabilityId: string } | undefined {
|
|
254
|
+
const constraints = grant.constraints;
|
|
255
|
+
if (!constraints || constraints.kind !== 'plugin_capability_authorization') return undefined;
|
|
256
|
+
const pluginId = typeof constraints.pluginId === 'string' ? constraints.pluginId.trim() : '';
|
|
257
|
+
const capabilityId = typeof constraints.capabilityId === 'string' ? constraints.capabilityId.trim() : '';
|
|
258
|
+
return pluginId && capabilityId ? { pluginId, capabilityId } : undefined;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function pluginGrantFromCanonical(
|
|
262
|
+
grant: Grant,
|
|
263
|
+
fallback?: { pluginId: string; capabilityId: string; repoId?: string; target?: AssistantPluginAuthorizationTarget },
|
|
264
|
+
): PluginCapabilityAuthorizationGrant | undefined {
|
|
265
|
+
const metadata = pluginMetadataFromCanonicalGrant(grant)
|
|
266
|
+
?? (fallback ? { pluginId: fallback.pluginId, capabilityId: fallback.capabilityId } : undefined);
|
|
267
|
+
if (!metadata || !grant.target) return undefined;
|
|
268
|
+
if (grant.riskCeiling === 'destructive') return undefined;
|
|
269
|
+
const ownerScope = grant.ownerScope?.trim() || grant.principalId.trim();
|
|
270
|
+
if (!ownerScope) return undefined;
|
|
271
|
+
const target: AssistantPluginAuthorizationTarget = {
|
|
272
|
+
kind: grant.target.kind,
|
|
273
|
+
id: grant.target.id,
|
|
274
|
+
...(grant.target.identityFingerprint ? { identityFingerprint: grant.target.identityFingerprint } : fallback?.target?.identityFingerprint ? { identityFingerprint: fallback.target.identityFingerprint } : {}),
|
|
275
|
+
};
|
|
276
|
+
return {
|
|
277
|
+
schemaVersion: 1,
|
|
278
|
+
grantId: grant.grantId,
|
|
279
|
+
ownerScope,
|
|
280
|
+
repoId: grant.target.repoId ?? fallback?.repoId ?? 'controller:global',
|
|
281
|
+
pluginId: metadata.pluginId,
|
|
282
|
+
capabilityId: metadata.capabilityId,
|
|
283
|
+
target,
|
|
284
|
+
scopes: [...(grant.scopes ?? [])],
|
|
285
|
+
riskCeiling: grant.riskCeiling ?? 'readonly',
|
|
286
|
+
createdAt: grant.createdAt,
|
|
287
|
+
updatedAt: grant.updatedAt,
|
|
288
|
+
expiresAt: grant.expiresAt,
|
|
289
|
+
...(grant.revokedAt ? { revokedAt: grant.revokedAt } : {}),
|
|
290
|
+
...(grant.revokedReason ? { revokedReason: grant.revokedReason } : {}),
|
|
291
|
+
};
|
|
204
292
|
}
|
|
205
293
|
|
|
206
|
-
function
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
294
|
+
function recordCanonicalPluginGrant(
|
|
295
|
+
controllerHome: string,
|
|
296
|
+
grant: PluginCapabilityAuthorizationGrant,
|
|
297
|
+
now?: Date,
|
|
298
|
+
): PluginCapabilityAuthorizationGrant {
|
|
299
|
+
const expiresInMinutes = Math.max(1, Math.ceil((Date.parse(grant.expiresAt) - (now ?? new Date()).getTime()) / 60_000));
|
|
300
|
+
const canonical = recordCanonicalGrant(controllerHome, {
|
|
301
|
+
grantId: grant.grantId,
|
|
302
|
+
principalId: grant.ownerScope,
|
|
303
|
+
ownerScope: grant.ownerScope,
|
|
304
|
+
capabilities: [`${grant.pluginId}:${grant.capabilityId}`, grant.capabilityId],
|
|
305
|
+
target: {
|
|
306
|
+
kind: grant.target.kind,
|
|
307
|
+
id: grant.target.id,
|
|
308
|
+
repoId: grant.repoId,
|
|
309
|
+
...(grant.target.identityFingerprint ? { identityFingerprint: grant.target.identityFingerprint } : {}),
|
|
310
|
+
},
|
|
311
|
+
scopes: grant.scopes,
|
|
312
|
+
riskCeiling: grant.riskCeiling,
|
|
313
|
+
constraints: { kind: 'plugin_capability_authorization', pluginId: grant.pluginId, capabilityId: grant.capabilityId },
|
|
314
|
+
expiresInMinutes,
|
|
315
|
+
now,
|
|
316
|
+
});
|
|
317
|
+
return pluginGrantFromCanonical(canonical, grant)!;
|
|
210
318
|
}
|
|
211
319
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
320
|
+
/**
|
|
321
|
+
* One-way migration input. The legacy plugin grant file is read-only history:
|
|
322
|
+
* every still-authorizable legacy grant is copied into the canonical Grant
|
|
323
|
+
* authority exactly once, and this module never writes the legacy file again.
|
|
324
|
+
* A corrupt migration input still fails closed so un-migrated grants cannot be
|
|
325
|
+
* silently dropped.
|
|
326
|
+
*
|
|
327
|
+
* Removal trigger: once a released Runtime baseline has shipped with the
|
|
328
|
+
* `migration.json` marker (so every install has migrated at least once), delete
|
|
329
|
+
* this migration, `loadStore`/`validatePersistedGrant`, the legacy path getter
|
|
330
|
+
* and the PLUGIN_CAPABILITY_GRANT_STORE_CORRUPT error code in the same slice.
|
|
331
|
+
* The archived `grants.json.migrated-*` file is recoverable history, not input.
|
|
332
|
+
*/
|
|
333
|
+
function migrateLegacyPluginCapabilityGrants(controllerHome: string, at = new Date()): void {
|
|
334
|
+
if (readLegacyPluginGrantMigrationMarker(controllerHome)) return;
|
|
335
|
+
const legacyGrants = loadStore(controllerHome).grants;
|
|
336
|
+
const canonicalById = new Map(listCanonicalGrants(controllerHome).map((grant) => [grant.grantId, grant]));
|
|
337
|
+
const atMs = at.getTime();
|
|
338
|
+
for (const grant of legacyGrants) {
|
|
339
|
+
if (grant.revokedAt) continue;
|
|
340
|
+
if (Date.parse(grant.expiresAt) <= atMs) continue;
|
|
341
|
+
const existing = canonicalById.get(grant.grantId);
|
|
342
|
+
if (existing) {
|
|
343
|
+
// A canonical row written before plugin metadata existed cannot be projected
|
|
344
|
+
// back into a plugin grant. Repair it in place instead of leaving a live grant
|
|
345
|
+
// that neither list nor by-id resolution can see.
|
|
346
|
+
if (existing.revokedAt || pluginMetadataFromCanonicalGrant(existing)) continue;
|
|
347
|
+
recordCanonicalPluginGrant(controllerHome, grant, new Date(grant.createdAt));
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
recordCanonicalPluginGrant(controllerHome, grant, new Date(grant.createdAt));
|
|
351
|
+
canonicalById.set(grant.grantId, { grantId: grant.grantId } as Grant);
|
|
352
|
+
}
|
|
353
|
+
// The migration is complete for this Controller Home: prove it durably, then
|
|
354
|
+
// move the legacy file aside so no authorization read can ever depend on it
|
|
355
|
+
// again. The archive is recoverable; a failed rename leaves an inert file.
|
|
356
|
+
writeJsonAtomic(legacyPluginGrantMigrationMarkerPath(controllerHome), {
|
|
357
|
+
schemaVersion: 1,
|
|
358
|
+
migratedAt: at.toISOString(),
|
|
359
|
+
legacyGrantCount: legacyGrants.length,
|
|
360
|
+
canonicalGrantCount: listCanonicalGrants(controllerHome).length,
|
|
361
|
+
} satisfies LegacyPluginGrantMigrationMarker);
|
|
362
|
+
const legacyPath = pluginCapabilityAuthorizationGrantStorePath(controllerHome);
|
|
363
|
+
if (existsSync(legacyPath)) {
|
|
364
|
+
try {
|
|
365
|
+
renameSync(legacyPath, `${legacyPath}.migrated-${at.getTime()}`);
|
|
366
|
+
} catch {
|
|
367
|
+
// Inert history; the marker already prevents any further legacy read.
|
|
368
|
+
}
|
|
369
|
+
}
|
|
215
370
|
}
|
|
216
371
|
|
|
217
372
|
export function findActivePluginCapabilityAuthorization(
|
|
@@ -220,22 +375,21 @@ export function findActivePluginCapabilityAuthorization(
|
|
|
220
375
|
): PluginCapabilityAuthorizationGrant | undefined {
|
|
221
376
|
if (query.risk === 'destructive') return undefined;
|
|
222
377
|
const ownerScope = required(query.ownerScope, 'ownerScope');
|
|
223
|
-
const repoId =
|
|
378
|
+
const repoId = (query.repoId?.trim()) || 'controller:global';
|
|
224
379
|
const pluginId = required(query.pluginId, 'pluginId');
|
|
225
380
|
const capabilityId = required(query.capabilityId, 'capabilityId');
|
|
226
381
|
const target = normalizeTarget(query.target);
|
|
227
382
|
const scopes = normalizeScopes(query.scopes);
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
.sort((left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt))[0];
|
|
383
|
+
migrateLegacyPluginCapabilityGrants(controllerHome, query.at ?? new Date());
|
|
384
|
+
const canonical = findActiveCanonicalGrant(controllerHome, {
|
|
385
|
+
ownerScope,
|
|
386
|
+
capability: `${pluginId}:${capabilityId}`,
|
|
387
|
+
target: { kind: target.kind, id: target.id, repoId, ...(target.identityFingerprint ? { identityFingerprint: target.identityFingerprint } : {}) },
|
|
388
|
+
scopes,
|
|
389
|
+
risk: query.risk,
|
|
390
|
+
at: query.at,
|
|
391
|
+
});
|
|
392
|
+
return canonical ? pluginGrantFromCanonical(canonical, { pluginId, capabilityId, repoId, target }) : undefined;
|
|
239
393
|
}
|
|
240
394
|
|
|
241
395
|
/**
|
|
@@ -250,9 +404,12 @@ export function findActivePluginCapabilityAuthorizationById(
|
|
|
250
404
|
): PluginCapabilityAuthorizationGrant | undefined {
|
|
251
405
|
const normalizedGrantId = required(grantId, 'grantId');
|
|
252
406
|
const atMs = at.getTime();
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
407
|
+
migrateLegacyPluginCapabilityGrants(controllerHome, at);
|
|
408
|
+
const canonical = listCanonicalGrants(controllerHome).find((entry) => entry.grantId === normalizedGrantId);
|
|
409
|
+
if (canonical && !canonical.revokedAt && Date.parse(canonical.expiresAt) > atMs) {
|
|
410
|
+
return pluginGrantFromCanonical(canonical);
|
|
411
|
+
}
|
|
412
|
+
return undefined;
|
|
256
413
|
}
|
|
257
414
|
|
|
258
415
|
export function recordPluginCapabilityAuthorization(
|
|
@@ -267,7 +424,7 @@ export function recordPluginCapabilityAuthorization(
|
|
|
267
424
|
}
|
|
268
425
|
const now = input.now ?? new Date();
|
|
269
426
|
const ownerScope = required(input.ownerScope, 'ownerScope');
|
|
270
|
-
const repoId =
|
|
427
|
+
const repoId = (input.repoId?.trim()) || 'controller:global';
|
|
271
428
|
const pluginId = required(input.pluginId, 'pluginId');
|
|
272
429
|
const capabilityId = required(input.capabilityId, 'capabilityId');
|
|
273
430
|
const target = normalizeTarget(input.target);
|
|
@@ -278,6 +435,7 @@ export function recordPluginCapabilityAuthorization(
|
|
|
278
435
|
}
|
|
279
436
|
const expiresInMinutes = Math.min(Math.max(1, Math.trunc(rawMinutes)), MAX_PLUGIN_CAPABILITY_GRANT_MINUTES);
|
|
280
437
|
const timestamp = now.toISOString();
|
|
438
|
+
migrateLegacyPluginCapabilityGrants(controllerHome, now);
|
|
281
439
|
const grant: PluginCapabilityAuthorizationGrant = {
|
|
282
440
|
schemaVersion: 1,
|
|
283
441
|
grantId: `plugin-grant-${randomUUID()}`,
|
|
@@ -298,16 +456,19 @@ export function recordPluginCapabilityAuthorization(
|
|
|
298
456
|
{ scope: 'global', resource: 'plugin-capability-authorization-grants' },
|
|
299
457
|
`plugin-capability-grant:${ownerScope}`,
|
|
300
458
|
() => {
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
459
|
+
const canonicalGrant = recordCanonicalGrant(controllerHome, {
|
|
460
|
+
grantId: grant.grantId,
|
|
461
|
+
principalId: grant.ownerScope,
|
|
462
|
+
ownerScope: grant.ownerScope,
|
|
463
|
+
capabilities: [`${grant.pluginId}:${grant.capabilityId}`, grant.capabilityId],
|
|
464
|
+
target: { kind: grant.target.kind, id: grant.target.id, repoId: grant.repoId, ...(grant.target.identityFingerprint ? { identityFingerprint: grant.target.identityFingerprint } : {}) },
|
|
465
|
+
scopes: grant.scopes,
|
|
466
|
+
riskCeiling: grant.riskCeiling,
|
|
467
|
+
constraints: { kind: 'plugin_capability_authorization', pluginId: grant.pluginId, capabilityId: grant.capabilityId },
|
|
468
|
+
expiresInMinutes,
|
|
469
|
+
now,
|
|
470
|
+
});
|
|
471
|
+
return pluginGrantFromCanonical(canonicalGrant, grant)!;
|
|
311
472
|
},
|
|
312
473
|
5_000,
|
|
313
474
|
);
|
|
@@ -333,22 +494,56 @@ export function revokePluginCapabilityAuthorization(
|
|
|
333
494
|
{ scope: 'global', resource: 'plugin-capability-authorization-grants' },
|
|
334
495
|
`plugin-capability-revoke:${ownerScope}`,
|
|
335
496
|
() => {
|
|
336
|
-
const
|
|
337
|
-
|
|
338
|
-
if (index < 0) throw new PluginCapabilityAuthorizationGrantError('PLUGIN_CAPABILITY_GRANT_NOT_FOUND', `Grant ${grantId} was not found.`);
|
|
339
|
-
const current = store.grants[index]!;
|
|
497
|
+
const current = findActivePluginCapabilityAuthorizationById(controllerHome, grantId, now);
|
|
498
|
+
if (!current) throw new PluginCapabilityAuthorizationGrantError('PLUGIN_CAPABILITY_GRANT_NOT_FOUND', `Grant ${grantId} was not found.`);
|
|
340
499
|
if (current.ownerScope !== ownerScope) {
|
|
341
500
|
throw new PluginCapabilityAuthorizationGrantError('PLUGIN_CAPABILITY_GRANT_OWNER_MISMATCH', `Grant ${grantId} belongs to another owner scope.`);
|
|
342
501
|
}
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
502
|
+
const canonical = revokeCanonicalGrant(controllerHome, { grantId, ownerScope, reason, now });
|
|
503
|
+
return pluginGrantFromCanonical(canonical, current)!;
|
|
504
|
+
},
|
|
505
|
+
5_000,
|
|
506
|
+
);
|
|
507
|
+
} catch (error) {
|
|
508
|
+
if (error instanceof ControllerLockContentionError) {
|
|
509
|
+
throw new PluginCapabilityAuthorizationGrantError('PLUGIN_CAPABILITY_GRANT_STORE_BUSY', error.message);
|
|
510
|
+
}
|
|
511
|
+
throw error;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
export function reconcilePluginCapabilityAuthorizations(
|
|
516
|
+
controllerHome: string,
|
|
517
|
+
input: ReconcilePluginCapabilityAuthorizationsInput = {},
|
|
518
|
+
): ReconcilePluginCapabilityAuthorizationsResult {
|
|
519
|
+
const retiredOwnerScopes = new Set(
|
|
520
|
+
(input.retiredOwnerScopes ?? [])
|
|
521
|
+
.map((ownerScope) => required(ownerScope, 'retiredOwnerScopes[]')),
|
|
522
|
+
);
|
|
523
|
+
const nowMs = (input.now ?? new Date()).getTime();
|
|
524
|
+
try {
|
|
525
|
+
return withControllerLock(
|
|
526
|
+
controllerHome,
|
|
527
|
+
{ scope: 'global', resource: 'plugin-capability-authorization-grants' },
|
|
528
|
+
'plugin-capability-reconcile',
|
|
529
|
+
() => {
|
|
530
|
+
void nowMs;
|
|
531
|
+
migrateLegacyPluginCapabilityGrants(controllerHome, input.now ?? new Date());
|
|
532
|
+
const canonical = reconcileCanonicalGrants(controllerHome, {
|
|
533
|
+
retiredOwnerScopes: [...retiredOwnerScopes],
|
|
534
|
+
now: input.now,
|
|
535
|
+
});
|
|
536
|
+
const grants = listCanonicalGrants(controllerHome)
|
|
537
|
+
.map((grant) => pluginGrantFromCanonical(grant))
|
|
538
|
+
.filter((grant): grant is PluginCapabilityAuthorizationGrant => Boolean(grant));
|
|
539
|
+
return {
|
|
540
|
+
removedRetiredOwner: canonical.removedRetiredOwner,
|
|
541
|
+
removedRevoked: canonical.removedRevoked,
|
|
542
|
+
removedExpired: canonical.removedExpired,
|
|
543
|
+
removedTotal: canonical.removedTotal,
|
|
544
|
+
remaining: grants.length,
|
|
545
|
+
changed: canonical.changed,
|
|
348
546
|
};
|
|
349
|
-
store.grants[index] = revoked;
|
|
350
|
-
saveStore(controllerHome, store);
|
|
351
|
-
return revoked;
|
|
352
547
|
},
|
|
353
548
|
5_000,
|
|
354
549
|
);
|
|
@@ -364,8 +559,9 @@ export function listPluginCapabilityAuthorizations(
|
|
|
364
559
|
controllerHome: string,
|
|
365
560
|
ownerScope?: string,
|
|
366
561
|
): PluginCapabilityAuthorizationGrant[] {
|
|
562
|
+
migrateLegacyPluginCapabilityGrants(controllerHome);
|
|
367
563
|
const normalizedOwner = ownerScope?.trim();
|
|
368
|
-
return
|
|
369
|
-
.
|
|
370
|
-
.
|
|
564
|
+
return listCanonicalGrants(controllerHome, normalizedOwner ? { ownerScope: normalizedOwner } : undefined)
|
|
565
|
+
.map((grant) => pluginGrantFromCanonical(grant))
|
|
566
|
+
.filter((grant): grant is PluginCapabilityAuthorizationGrant => Boolean(grant));
|
|
371
567
|
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'crypto';
|
|
2
|
+
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'fs';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
5
|
+
import { resolveControllerHome } from '../../cli/repositories/controller-home';
|
|
6
|
+
import { loadRuntimeReleaseManifest } from '../root/release-manifest';
|
|
7
|
+
import { readRuntimeReleaseAuthority } from '../root/release-store';
|
|
8
|
+
import { ensureBrowserStateInControllerHome } from './browser-session-store';
|
|
9
|
+
import { renderWorkflowSupervisorNativeManifest } from '../../../supervisor/native-messaging/manifest';
|
|
10
|
+
import { WORKFLOW_SUPERVISOR_NATIVE_HOST_NAME } from '../../../supervisor/native-messaging/host';
|
|
11
|
+
|
|
12
|
+
const EXTENSION_FILES = ['manifest.json', 'background.js', 'content.js', 'core.js'] as const;
|
|
13
|
+
const FORGE_NATIVE_MESSAGING_DECLARATION = 'forge-native-messaging-host.json';
|
|
14
|
+
|
|
15
|
+
export interface WorkflowSupervisorBrowserInstallation {
|
|
16
|
+
browser: 'chrome' | 'chrome-for-testing' | 'chromium' | 'vivaldi' | 'forge-managed';
|
|
17
|
+
nativeMessagingRoot: string;
|
|
18
|
+
}
|
|
19
|
+
interface ActiveBrowserAdapterRelease {
|
|
20
|
+
releaseId: string;
|
|
21
|
+
extensionSourcePath: string;
|
|
22
|
+
nativeHostSourcePath: string;
|
|
23
|
+
nativeHostArtifactIdentity: string;
|
|
24
|
+
}
|
|
25
|
+
export interface WorkflowSupervisorBrowserAdapterDependencies {
|
|
26
|
+
homeDir?: string;
|
|
27
|
+
browserInstallations?: readonly WorkflowSupervisorBrowserInstallation[];
|
|
28
|
+
activeRelease?: (controllerHome: string) => ActiveBrowserAdapterRelease;
|
|
29
|
+
repository?: { repoId: string; repoRoot: string };
|
|
30
|
+
}
|
|
31
|
+
export interface WorkflowSupervisorBrowserAdapterStatus {
|
|
32
|
+
state: 'ready' | 'not_installed' | 'projection_stale' | 'runtime_release_incomplete';
|
|
33
|
+
extensionPath: string;
|
|
34
|
+
extensionId?: string;
|
|
35
|
+
nativeHostPath: string;
|
|
36
|
+
activeReleaseId?: string;
|
|
37
|
+
nativeManifestPaths: string[];
|
|
38
|
+
projectionCurrent: boolean;
|
|
39
|
+
detail: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function sha256(path: string): string {
|
|
43
|
+
return createHash('sha256').update(readFileSync(path)).digest('hex');
|
|
44
|
+
}
|
|
45
|
+
export function workflowSupervisorExtensionIdFromManifestKey(value: string): string {
|
|
46
|
+
const key = value.trim();
|
|
47
|
+
if (!/^[A-Za-z0-9+/]+={0,2}$/.test(key)) throw new Error('WORKFLOW_SUPERVISOR_EXTENSION_KEY_INVALID');
|
|
48
|
+
const bytes = Buffer.from(key, 'base64');
|
|
49
|
+
if (bytes.length < 64 || bytes.toString('base64').replace(/=+$/,'') !== key.replace(/=+$/,'')) {
|
|
50
|
+
throw new Error('WORKFLOW_SUPERVISOR_EXTENSION_KEY_INVALID');
|
|
51
|
+
}
|
|
52
|
+
return createHash('sha256').update(bytes).digest('hex').slice(0, 32)
|
|
53
|
+
.replace(/[0-9a-f]/g, (nibble) => String.fromCharCode(97 + Number.parseInt(nibble, 16)));
|
|
54
|
+
}
|
|
55
|
+
function extensionId(path: string): string {
|
|
56
|
+
const manifest = JSON.parse(readFileSync(join(path, 'manifest.json'), 'utf8')) as { key?: unknown };
|
|
57
|
+
if (typeof manifest.key !== 'string') throw new Error('WORKFLOW_SUPERVISOR_EXTENSION_KEY_REQUIRED');
|
|
58
|
+
return workflowSupervisorExtensionIdFromManifestKey(manifest.key);
|
|
59
|
+
}
|
|
60
|
+
function controllerPaths(controllerHome: string) {
|
|
61
|
+
const root = join(resolveControllerHome(controllerHome), 'supervisor', 'browser-adapter');
|
|
62
|
+
return {
|
|
63
|
+
extensionPath: join(root, 'chrome-extension'),
|
|
64
|
+
nativeHostPath: join(root, 'forge-workflow-supervisor-native-host'),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function defaultBrowserInstallations(
|
|
68
|
+
controllerHome: string,
|
|
69
|
+
homeDir = process.env.HOME ?? homedir(),
|
|
70
|
+
repository?: { repoId: string; repoRoot: string },
|
|
71
|
+
): WorkflowSupervisorBrowserInstallation[] {
|
|
72
|
+
const installations: WorkflowSupervisorBrowserInstallation[] = [
|
|
73
|
+
{ browser: 'chrome', nativeMessagingRoot: join(homeDir, 'Library', 'Application Support', 'Google', 'Chrome', 'NativeMessagingHosts') },
|
|
74
|
+
{ browser: 'chrome-for-testing', nativeMessagingRoot: join(homeDir, 'Library', 'Application Support', 'Google', 'ChromeForTesting', 'NativeMessagingHosts') },
|
|
75
|
+
{ browser: 'chromium', nativeMessagingRoot: join(homeDir, 'Library', 'Application Support', 'Chromium', 'NativeMessagingHosts') },
|
|
76
|
+
{ browser: 'vivaldi', nativeMessagingRoot: join(homeDir, 'Library', 'Application Support', 'Vivaldi', 'NativeMessagingHosts') },
|
|
77
|
+
];
|
|
78
|
+
if (repository) {
|
|
79
|
+
const browserStateRoot = ensureBrowserStateInControllerHome(controllerHome, repository.repoId, repository.repoRoot);
|
|
80
|
+
installations.push({ browser: 'forge-managed', nativeMessagingRoot: join(browserStateRoot, 'profiles', 'default', 'NativeMessagingHosts') });
|
|
81
|
+
}
|
|
82
|
+
return installations;
|
|
83
|
+
}
|
|
84
|
+
function activeBrowserAdapterRelease(controllerHome: string): ActiveBrowserAdapterRelease {
|
|
85
|
+
const home = resolveControllerHome(controllerHome);
|
|
86
|
+
const authority = readRuntimeReleaseAuthority(home);
|
|
87
|
+
if (!authority) throw new Error('WORKFLOW_SUPERVISOR_BROWSER_RUNTIME_RELEASE_MISSING');
|
|
88
|
+
const manifest = loadRuntimeReleaseManifest(authority.active.manifestPath, home);
|
|
89
|
+
if (!manifest.packageRoot || !manifest.workflowSupervisorNativeHostEntrypoint || !manifest.workflowSupervisorNativeHostArtifactIdentity) {
|
|
90
|
+
throw new Error('WORKFLOW_SUPERVISOR_BROWSER_RUNTIME_RELEASE_INCOMPLETE');
|
|
91
|
+
}
|
|
92
|
+
const releaseRoot = dirname(authority.active.manifestPath);
|
|
93
|
+
return {
|
|
94
|
+
releaseId: authority.active.releaseId,
|
|
95
|
+
extensionSourcePath: join(releaseRoot, manifest.packageRoot, 'adapters', 'computer', 'chrome-extension'),
|
|
96
|
+
nativeHostSourcePath: join(releaseRoot, manifest.workflowSupervisorNativeHostEntrypoint),
|
|
97
|
+
nativeHostArtifactIdentity: manifest.workflowSupervisorNativeHostArtifactIdentity,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function assertRegularFile(path: string, code: string): void {
|
|
101
|
+
if (!existsSync(path)) throw new Error(code + ': ' + path);
|
|
102
|
+
const status = lstatSync(path);
|
|
103
|
+
if (status.isSymbolicLink() || !status.isFile()) throw new Error(code + ': ' + path);
|
|
104
|
+
}
|
|
105
|
+
function projectExtension(source: string, destination: string): void {
|
|
106
|
+
const staging = destination + '.staging-' + randomUUID().slice(0, 12);
|
|
107
|
+
rmSync(staging, { recursive: true, force: true });
|
|
108
|
+
mkdirSync(staging, { recursive: true, mode: 0o700 });
|
|
109
|
+
try {
|
|
110
|
+
for (const file of EXTENSION_FILES) {
|
|
111
|
+
const sourcePath = join(source, file);
|
|
112
|
+
assertRegularFile(sourcePath, 'WORKFLOW_SUPERVISOR_EXTENSION_SOURCE_INVALID');
|
|
113
|
+
writeFileSync(join(staging, file), readFileSync(sourcePath), { mode: 0o600 });
|
|
114
|
+
}
|
|
115
|
+
rmSync(destination, { recursive: true, force: true });
|
|
116
|
+
renameSync(staging, destination);
|
|
117
|
+
} catch (error) {
|
|
118
|
+
rmSync(staging, { recursive: true, force: true });
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function projectNativeHost(source: string, expectedIdentity: string, destination: string): void {
|
|
123
|
+
assertRegularFile(source, 'WORKFLOW_SUPERVISOR_NATIVE_HOST_SOURCE_INVALID');
|
|
124
|
+
if ('sha256:' + sha256(source) !== expectedIdentity) throw new Error('WORKFLOW_SUPERVISOR_NATIVE_HOST_IDENTITY_MISMATCH');
|
|
125
|
+
mkdirSync(dirname(destination), { recursive: true, mode: 0o700 });
|
|
126
|
+
const temporary = destination + '.' + randomUUID().slice(0, 12) + '.tmp';
|
|
127
|
+
writeFileSync(temporary, readFileSync(source), { mode: 0o700 });
|
|
128
|
+
chmodSync(temporary, 0o700);
|
|
129
|
+
renameSync(temporary, destination);
|
|
130
|
+
}
|
|
131
|
+
function extensionProjectionCurrent(source: string, destination: string): boolean {
|
|
132
|
+
return EXTENSION_FILES.every((file) => {
|
|
133
|
+
const sourcePath = join(source, file), destinationPath = join(destination, file);
|
|
134
|
+
return existsSync(sourcePath) && existsSync(destinationPath) && sha256(sourcePath) === sha256(destinationPath);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
function nativeManifestPath(installation: WorkflowSupervisorBrowserInstallation): string {
|
|
138
|
+
return join(installation.nativeMessagingRoot, WORKFLOW_SUPERVISOR_NATIVE_HOST_NAME + '.json');
|
|
139
|
+
}
|
|
140
|
+
function nativeManifestReady(path: string, nativeHostPath: string, exactExtensionId: string): boolean {
|
|
141
|
+
if (!existsSync(path)) return false;
|
|
142
|
+
try {
|
|
143
|
+
const manifest = JSON.parse(readFileSync(path, 'utf8')) as { path?: unknown; allowed_origins?: unknown };
|
|
144
|
+
return manifest.path === nativeHostPath
|
|
145
|
+
&& JSON.stringify(manifest.allowed_origins) === JSON.stringify(['chrome-extension://' + exactExtensionId + '/']);
|
|
146
|
+
} catch { return false; }
|
|
147
|
+
}
|
|
148
|
+
function inspectWithRelease(
|
|
149
|
+
controllerHome: string,
|
|
150
|
+
release: ActiveBrowserAdapterRelease,
|
|
151
|
+
dependencies: WorkflowSupervisorBrowserAdapterDependencies,
|
|
152
|
+
): WorkflowSupervisorBrowserAdapterStatus {
|
|
153
|
+
const paths = controllerPaths(controllerHome);
|
|
154
|
+
const exactExtensionId = extensionId(release.extensionSourcePath);
|
|
155
|
+
const hostCurrent = existsSync(paths.nativeHostPath) && 'sha256:' + sha256(paths.nativeHostPath) === release.nativeHostArtifactIdentity;
|
|
156
|
+
const extensionCurrent = existsSync(paths.extensionPath)
|
|
157
|
+
&& extensionProjectionCurrent(release.extensionSourcePath, paths.extensionPath)
|
|
158
|
+
&& nativeManifestReady(join(paths.extensionPath, FORGE_NATIVE_MESSAGING_DECLARATION), paths.nativeHostPath, exactExtensionId);
|
|
159
|
+
const installations = dependencies.browserInstallations ?? defaultBrowserInstallations(controllerHome, dependencies.homeDir, dependencies.repository);
|
|
160
|
+
const nativeManifestPaths = installations.map(nativeManifestPath);
|
|
161
|
+
const manifestsCurrent = nativeManifestPaths.every((path) => nativeManifestReady(path, paths.nativeHostPath, exactExtensionId));
|
|
162
|
+
const projectionCurrent = hostCurrent && extensionCurrent && manifestsCurrent;
|
|
163
|
+
return {
|
|
164
|
+
state: projectionCurrent ? 'ready' : (existsSync(paths.extensionPath) || existsSync(paths.nativeHostPath) ? 'projection_stale' : 'not_installed'),
|
|
165
|
+
extensionPath: paths.extensionPath,
|
|
166
|
+
extensionId: exactExtensionId,
|
|
167
|
+
nativeHostPath: paths.nativeHostPath,
|
|
168
|
+
activeReleaseId: release.releaseId,
|
|
169
|
+
nativeManifestPaths,
|
|
170
|
+
projectionCurrent,
|
|
171
|
+
detail: projectionCurrent
|
|
172
|
+
? 'Workflow Supervisor browser adapter is projected from the active Runtime release and Native Messaging is registered for the package-derived extension id. Live browser connectivity is proven separately by Supervisor browser_discovery.'
|
|
173
|
+
: 'Controller browser-adapter projection does not match the active Runtime release.',
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
export function inspectWorkflowSupervisorBrowserAdapter(
|
|
177
|
+
controllerHome: string,
|
|
178
|
+
dependencies: WorkflowSupervisorBrowserAdapterDependencies = {},
|
|
179
|
+
): WorkflowSupervisorBrowserAdapterStatus {
|
|
180
|
+
const paths = controllerPaths(controllerHome);
|
|
181
|
+
try {
|
|
182
|
+
return inspectWithRelease(controllerHome, (dependencies.activeRelease ?? activeBrowserAdapterRelease)(controllerHome), dependencies);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
return {
|
|
185
|
+
state: 'runtime_release_incomplete',
|
|
186
|
+
extensionPath: paths.extensionPath,
|
|
187
|
+
nativeHostPath: paths.nativeHostPath,
|
|
188
|
+
nativeManifestPaths: [],
|
|
189
|
+
projectionCurrent: false,
|
|
190
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
export function installWorkflowSupervisorBrowserAdapter(
|
|
195
|
+
controllerHome: string,
|
|
196
|
+
dependencies: WorkflowSupervisorBrowserAdapterDependencies = {},
|
|
197
|
+
): WorkflowSupervisorBrowserAdapterStatus {
|
|
198
|
+
const release = (dependencies.activeRelease ?? activeBrowserAdapterRelease)(controllerHome);
|
|
199
|
+
const paths = controllerPaths(controllerHome);
|
|
200
|
+
const exactExtensionId = extensionId(release.extensionSourcePath);
|
|
201
|
+
projectExtension(release.extensionSourcePath, paths.extensionPath);
|
|
202
|
+
projectNativeHost(release.nativeHostSourcePath, release.nativeHostArtifactIdentity, paths.nativeHostPath);
|
|
203
|
+
writeFileSync(
|
|
204
|
+
join(paths.extensionPath, FORGE_NATIVE_MESSAGING_DECLARATION),
|
|
205
|
+
renderWorkflowSupervisorNativeManifest({ executablePath: paths.nativeHostPath, extensionId: exactExtensionId }),
|
|
206
|
+
{ mode: 0o600 },
|
|
207
|
+
);
|
|
208
|
+
for (const installation of dependencies.browserInstallations ?? defaultBrowserInstallations(controllerHome, dependencies.homeDir, dependencies.repository)) {
|
|
209
|
+
const path = nativeManifestPath(installation);
|
|
210
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
211
|
+
const temporary = path + '.' + randomUUID().slice(0, 12) + '.tmp';
|
|
212
|
+
writeFileSync(temporary, renderWorkflowSupervisorNativeManifest({
|
|
213
|
+
executablePath: paths.nativeHostPath,
|
|
214
|
+
extensionId: exactExtensionId,
|
|
215
|
+
}), { mode: 0o600 });
|
|
216
|
+
renameSync(temporary, path);
|
|
217
|
+
}
|
|
218
|
+
return inspectWithRelease(controllerHome, release, dependencies);
|
|
219
|
+
}
|