machine-bridge-mcp 2.0.0 → 3.0.0-beta.102
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/CHANGELOG.md +1142 -0
- package/CONTRIBUTING.md +23 -25
- package/GOVERNANCE.md +2 -2
- package/README.md +58 -20
- package/SECURITY.md +161 -120
- package/SUPPORT.md +1 -1
- package/browser-extension/broker-auth.js +83 -0
- package/browser-extension/broker-liveness.js +35 -0
- package/browser-extension/browser-error-boundary.js +96 -0
- package/browser-extension/browser-operations.js +1456 -101
- package/browser-extension/devtools-input.js +195 -34
- package/browser-extension/devtools-observation.js +429 -0
- package/browser-extension/devtools-session.js +48 -0
- package/browser-extension/manifest.json +4 -3
- package/browser-extension/page-automation.js +578 -118
- package/browser-extension/pairing-bootstrap.js +42 -0
- package/browser-extension/pairing.js +28 -12
- package/browser-extension/service-worker.js +126 -106
- package/docs/AGENT_CONTEXT.md +13 -10
- package/docs/ARCHITECTURE.md +159 -67
- package/docs/AUDIT.md +1302 -4
- package/docs/CLIENTS.md +19 -6
- package/docs/COMPUTER_USE.md +297 -0
- package/docs/ENGINEERING.md +72 -13
- package/docs/GETTING_STARTED.md +18 -20
- package/docs/LOCAL_AUTHORIZATION.md +115 -72
- package/docs/LOCAL_AUTOMATION.md +27 -21
- package/docs/LOGGING.md +40 -19
- package/docs/MANAGED_JOBS.md +37 -34
- package/docs/MULTI_ACCOUNT.md +98 -37
- package/docs/OPERATIONS.md +145 -51
- package/docs/OVERVIEW.md +14 -11
- package/docs/POLICY_REFERENCE.md +71 -3
- package/docs/PRIVACY.md +41 -2
- package/docs/PROJECT_STANDARDS.md +29 -6
- package/docs/RELEASING.md +238 -64
- package/docs/TESTING.md +150 -39
- package/docs/THREAT_MODEL.md +164 -67
- package/docs/TOOL_REFERENCE.md +653 -60
- package/docs/UPGRADING.md +78 -18
- package/native/macos/MachineBridgeBackgroundInput.swift +1051 -0
- package/native/macos/MachineBridgeBackgroundInputSmokeFixture.swift +295 -0
- package/native/macos/MachineBridgeTrustBroker.swift +233 -0
- package/package.json +94 -16
- package/scripts/accepted-candidate-tarball.mjs +77 -0
- package/scripts/candidate-runtime-store.mjs +93 -0
- package/scripts/check-plan.mjs +73 -3
- package/scripts/check-runner.mjs +129 -17
- package/scripts/commit-message-check.mjs +5 -2
- package/scripts/consumer-package-security.mjs +293 -0
- package/scripts/coverage-check.mjs +251 -15
- package/scripts/coverage-generation.mjs +41 -0
- package/scripts/coverage-range-merge.mjs +41 -0
- package/scripts/foreground-daemon-recovery.mjs +88 -0
- package/scripts/generate-policy-reference.mjs +28 -5
- package/scripts/generate-tool-reference.mjs +1 -1
- package/scripts/generate-worker-types.mjs +21 -11
- package/scripts/github-backlog.mjs +11 -10
- package/scripts/github-push.mjs +62 -16
- package/scripts/github-release-asset.mjs +58 -0
- package/scripts/github-release.mjs +241 -132
- package/scripts/global-package-installation.mjs +68 -0
- package/scripts/hardened-npm-session.mjs +42 -0
- package/scripts/install-published-prerelease.mjs +198 -0
- package/scripts/local-release-acceptance.mjs +103 -45
- package/scripts/macos-background-input-smoke.mjs +515 -0
- package/scripts/network-retry.mjs +8 -1
- package/scripts/npm-global-prefix.mjs +27 -0
- package/scripts/npm-publication-policy.mjs +17 -0
- package/scripts/official-mcp-conformance.mjs +267 -0
- package/scripts/persistent-activation-process.mjs +101 -0
- package/scripts/prepare-pinned-npm.mjs +12 -69
- package/scripts/prerelease-activation.mjs +160 -0
- package/scripts/privacy-check.mjs +14 -6
- package/scripts/promotion-digest.mjs +128 -0
- package/scripts/publish-npm.mjs +260 -0
- package/scripts/published-release.mjs +132 -0
- package/scripts/release-acceptance.mjs +62 -54
- package/scripts/release-browser-extension-store.mjs +172 -0
- package/scripts/release-candidate-manifest.mjs +52 -0
- package/scripts/release-channel.mjs +106 -0
- package/scripts/release-diagnostic.mjs +41 -0
- package/scripts/release-impact-check.mjs +12 -22
- package/scripts/release-oauth-canary-core.mjs +325 -0
- package/scripts/release-oauth-canary-evidence.mjs +100 -0
- package/scripts/release-oauth-canary.mjs +127 -0
- package/scripts/release-publication-guard.mjs +67 -0
- package/scripts/release-soak.mjs +284 -0
- package/scripts/release-state.mjs +1 -1
- package/scripts/run-checks.mjs +44 -7
- package/scripts/run-worker-dry-run.mjs +21 -0
- package/scripts/sarif-security-gate.mjs +2 -1
- package/scripts/sbom-check.mjs +106 -0
- package/scripts/start-release-candidate.mjs +267 -44
- package/scripts/syntax-check.mjs +36 -14
- package/scripts/verification-generation-guard.mjs +15 -0
- package/scripts/verification-idle-sleep-guard.mjs +37 -0
- package/scripts/verification-state.mjs +108 -0
- package/scripts/wrangler-command-lifecycle.mjs +115 -0
- package/src/local/account-access.mjs +13 -1
- package/src/local/account-admin-response.mjs +92 -0
- package/src/local/account-admin.mjs +52 -19
- package/src/local/agent-context-projection.mjs +26 -7
- package/src/local/agent-context.mjs +88 -49
- package/src/local/agent-skill-discovery.mjs +16 -5
- package/src/local/app-automation-macos-jxa.mjs +350 -0
- package/src/local/app-automation.mjs +873 -163
- package/src/local/application-capability-projection.mjs +24 -0
- package/src/local/authority-context.mjs +106 -0
- package/src/local/autostart-log-maintenance.mjs +36 -0
- package/src/local/browser-bridge-http.mjs +5 -3
- package/src/local/browser-bridge.mjs +109 -31
- package/src/local/browser-broker-auth-http.mjs +67 -0
- package/src/local/browser-broker-auth.mjs +98 -0
- package/src/local/browser-broker-routes.mjs +63 -20
- package/src/local/browser-broker-server.mjs +17 -7
- package/src/local/browser-command.mjs +79 -30
- package/src/local/browser-computer-observation-service.mjs +266 -0
- package/src/local/browser-extension-identity.mjs +50 -0
- package/src/local/browser-extension-path.mjs +22 -0
- package/src/local/browser-extension-protocol.mjs +50 -11
- package/src/local/browser-operation-service.mjs +147 -117
- package/src/local/browser-pairing-grant.mjs +109 -0
- package/src/local/browser-pairing-http.mjs +36 -0
- package/src/local/browser-pairing-launch.mjs +58 -0
- package/src/local/browser-pairing-store.mjs +85 -48
- package/src/local/browser-request-registry.mjs +40 -14
- package/src/local/browser-request-settlement.mjs +78 -0
- package/src/local/browser-resource-input.mjs +74 -0
- package/src/local/browser-trusted-input-health.mjs +63 -0
- package/src/local/call-authority.mjs +21 -0
- package/src/local/call-capacity.mjs +45 -0
- package/src/local/call-registry-drain.mjs +54 -0
- package/src/local/call-registry.mjs +30 -15
- package/src/local/capability-observer.mjs +5 -0
- package/src/local/capability-ranking.mjs +3 -2
- package/src/local/child-process-settlement.mjs +132 -0
- package/src/local/cli-account-admin.mjs +40 -6
- package/src/local/cli-activate.mjs +162 -0
- package/src/local/cli-local-admin.mjs +20 -45
- package/src/local/cli-options.mjs +12 -13
- package/src/local/cli-ready-output.mjs +52 -0
- package/src/local/cli-service.mjs +91 -11
- package/src/local/cli.mjs +232 -150
- package/src/local/computer-use-application-observation.mjs +224 -0
- package/src/local/computer-use-arguments.mjs +270 -0
- package/src/local/computer-use-deadline.mjs +50 -0
- package/src/local/computer-use-dispatch-settlement.mjs +40 -0
- package/src/local/computer-use-expectation.mjs +171 -0
- package/src/local/computer-use-observation.mjs +769 -0
- package/src/local/computer-use-recovery.mjs +82 -0
- package/src/local/computer-use-result-budget.mjs +145 -0
- package/src/local/computer-use-snapshot-store.mjs +67 -0
- package/src/local/computer-use.mjs +2255 -0
- package/src/local/daemon-http-relay-auth.mjs +26 -0
- package/src/local/daemon-http-relay-connection.mjs +298 -0
- package/src/local/daemon-http-relay-request.mjs +64 -0
- package/src/local/daemon-http-relay-sequence.mjs +49 -0
- package/src/local/daemon-process.mjs +40 -7
- package/src/local/delegated-process-sandbox.mjs +148 -0
- package/src/local/device-identity.mjs +156 -49
- package/src/local/device-root-provider.mjs +81 -0
- package/src/local/directory-metadata.mjs +47 -0
- package/src/local/doctor-reporting.mjs +23 -0
- package/src/local/durable-process-spec.mjs +80 -0
- package/src/local/errors.mjs +25 -15
- package/src/local/exclusive-file.mjs +74 -26
- package/src/local/execution-limits.mjs +9 -0
- package/src/local/execution-routing.mjs +276 -0
- package/src/local/execution-surface.mjs +23 -0
- package/src/local/file-mutation-coordinator.mjs +47 -0
- package/src/local/file-snapshot-preservation.mjs +24 -0
- package/src/local/filesystem-identity.mjs +54 -0
- package/src/local/fixed-process-environment.mjs +21 -0
- package/src/local/full-access-test.mjs +31 -13
- package/src/local/git-commit.mjs +69 -0
- package/src/local/git-config-safety.mjs +55 -0
- package/src/local/git-log-parser.mjs +41 -0
- package/src/local/git-metadata-boundary.mjs +77 -0
- package/src/local/git-metadata-tree-safety.mjs +59 -0
- package/src/local/git-operation-state.mjs +19 -0
- package/src/local/git-service.mjs +165 -30
- package/src/local/hardened-npm-download-timeout.mjs +46 -0
- package/src/local/hardened-npm-download.mjs +109 -0
- package/src/local/hardened-npm-extract.mjs +20 -0
- package/src/local/hardened-npm-verification.mjs +104 -0
- package/src/local/hardened-npm.mjs +199 -0
- package/src/local/job-runner.mjs +244 -89
- package/src/local/lifecycle.mjs +6 -0
- package/src/local/log.mjs +19 -45
- package/src/local/macos-background-input.mjs +398 -0
- package/src/local/macos-idle-sleep-assertion.mjs +65 -0
- package/src/local/macos-trust-broker.mjs +404 -0
- package/src/local/managed-job-cancellation.mjs +42 -0
- package/src/local/managed-job-capacity.mjs +16 -0
- package/src/local/managed-job-directory-generation.mjs +60 -0
- package/src/local/managed-job-directory.mjs +75 -0
- package/src/local/managed-job-durable-process.mjs +49 -0
- package/src/local/managed-job-lock.mjs +46 -14
- package/src/local/managed-job-plan.mjs +38 -14
- package/src/local/managed-job-projection.mjs +23 -15
- package/src/local/managed-job-retention.mjs +128 -0
- package/src/local/managed-job-runner-claim.mjs +70 -0
- package/src/local/managed-job-runner.mjs +35 -10
- package/src/local/managed-job-storage.mjs +22 -6
- package/src/local/managed-job-terminal-maintenance.mjs +40 -0
- package/src/local/managed-job-terminal.mjs +150 -0
- package/src/local/managed-jobs.mjs +424 -287
- package/src/local/npm-cli.mjs +42 -0
- package/src/local/npm-environment.mjs +37 -0
- package/src/local/operation-authorization.mjs +68 -324
- package/src/local/operation-risk.mjs +51 -4
- package/src/local/owner-state-lock.mjs +118 -0
- package/src/local/package-identity.mjs +9 -0
- package/src/local/patch.mjs +20 -18
- package/src/local/path-inspection.mjs +23 -0
- package/src/local/policy.mjs +19 -0
- package/src/local/private-toolchain-integrity.mjs +54 -0
- package/src/local/process-execution.mjs +87 -75
- package/src/local/process-foreground-timeout.mjs +50 -0
- package/src/local/process-identity.mjs +41 -7
- package/src/local/process-nonreplayable-settlement.mjs +50 -0
- package/src/local/process-result-projection.mjs +22 -0
- package/src/local/process-session-events.mjs +47 -0
- package/src/local/process-session-remote-activity.mjs +10 -0
- package/src/local/process-session-termination.mjs +73 -0
- package/src/local/process-sessions.mjs +144 -112
- package/src/local/process-tracker.mjs +140 -13
- package/src/local/process-tree-ownership-types.d.ts +37 -0
- package/src/local/process-tree-ownership.mjs +57 -0
- package/src/local/process-tree-signal.mjs +34 -0
- package/src/local/process-tree-snapshot.mjs +77 -0
- package/src/local/process-tree-supervisor.mjs +69 -0
- package/src/local/process-tree.mjs +11 -53
- package/src/local/project-metadata.mjs +19 -6
- package/src/local/relay-call-recovery.mjs +58 -38
- package/src/local/relay-connection-classification.mjs +131 -0
- package/src/local/relay-connection-support.mjs +91 -0
- package/src/local/relay-connection.mjs +179 -272
- package/src/local/relay-diagnostics.mjs +91 -0
- package/src/local/relay-heartbeat.mjs +119 -0
- package/src/local/relay-liveness.mjs +109 -0
- package/src/local/relay-peer-diagnostics.mjs +29 -0
- package/src/local/release-runtime-lock.mjs +18 -0
- package/src/local/remote-activity-idle-sleep-guard.mjs +77 -0
- package/src/local/remote-configuration.mjs +48 -0
- package/src/local/resilient-relay-connection.mjs +178 -0
- package/src/local/resource-admission-diagnostics.mjs +52 -0
- package/src/local/resource-admission-policy.mjs +115 -0
- package/src/local/resource-admission.mjs +339 -0
- package/src/local/resource-build-root.mjs +83 -0
- package/src/local/resource-cargo-concurrency.mjs +38 -0
- package/src/local/resource-cmake-concurrency.mjs +42 -0
- package/src/local/resource-command-concurrency.mjs +37 -0
- package/src/local/resource-command-profile.mjs +176 -0
- package/src/local/resource-coordinator-accounting.mjs +21 -0
- package/src/local/resource-cpu-window.mjs +20 -0
- package/src/local/resource-disk-headroom.mjs +13 -0
- package/src/local/resource-elastic-memory.mjs +19 -0
- package/src/local/resource-elastic-request.mjs +36 -0
- package/src/local/resource-foreground-wait.mjs +29 -0
- package/src/local/resource-go-concurrency.mjs +43 -0
- package/src/local/resource-gradle-concurrency.mjs +57 -0
- package/src/local/resource-host-cache.mjs +39 -0
- package/src/local/resource-host-darwin.mjs +54 -0
- package/src/local/resource-host-linux.mjs +35 -0
- package/src/local/resource-host-sample-file.mjs +23 -0
- package/src/local/resource-host-snapshot.mjs +99 -0
- package/src/local/resource-lease-accounting.mjs +119 -0
- package/src/local/resource-light-command.mjs +6 -0
- package/src/local/resource-make-concurrency.mjs +42 -0
- package/src/local/resource-maven-concurrency.mjs +38 -0
- package/src/local/resource-ninja-command-concurrency.mjs +20 -0
- package/src/local/resource-ninja-concurrency.mjs +28 -0
- package/src/local/resource-operations.mjs +26 -10
- package/src/local/resource-pressure.mjs +67 -0
- package/src/local/resource-probe-command.mjs +35 -0
- package/src/local/resource-process-admission.mjs +59 -0
- package/src/local/resource-process-ancestry-cache.mjs +43 -0
- package/src/local/resource-process-ancestry.mjs +32 -0
- package/src/local/resource-process-priority.mjs +15 -0
- package/src/local/resource-project-key.mjs +53 -0
- package/src/local/resource-pytest-concurrency.mjs +33 -0
- package/src/local/resource-release-control-classification.mjs +14 -0
- package/src/local/resource-release-control-executable.mjs +70 -0
- package/src/local/resource-release-control-workspace.mjs +41 -0
- package/src/local/resource-request-contract.mjs +30 -0
- package/src/local/resource-script-classification.mjs +44 -0
- package/src/local/resource-shell-analysis.mjs +58 -0
- package/src/local/resource-staging-recovery.mjs +97 -0
- package/src/local/resource-swift-concurrency.mjs +31 -0
- package/src/local/resource-transaction-lock.mjs +189 -0
- package/src/local/resource-wait.mjs +48 -0
- package/src/local/resource-waiters.mjs +139 -0
- package/src/local/resource-xcode-command.mjs +7 -0
- package/src/local/resource-xcode-concurrency.mjs +34 -0
- package/src/local/resource-xcode-non-build.mjs +19 -0
- package/src/local/runtime-activation.mjs +700 -0
- package/src/local/runtime-activity-projection.mjs +48 -0
- package/src/local/runtime-capabilities.mjs +48 -15
- package/src/local/runtime-diagnostic-state.mjs +14 -0
- package/src/local/runtime-diagnostics.mjs +40 -13
- package/src/local/runtime-info-projection.mjs +60 -0
- package/src/local/runtime-info-relay-projection.mjs +21 -0
- package/src/local/runtime-paths.mjs +2 -1
- package/src/local/runtime-process-routing.mjs +33 -0
- package/src/local/runtime-relay-connection-options.mjs +44 -0
- package/src/local/runtime-relay-control.mjs +98 -0
- package/src/local/runtime-relay.mjs +26 -50
- package/src/local/runtime-reporting.mjs +37 -21
- package/src/local/runtime-resource-service.mjs +77 -0
- package/src/local/runtime-tool-handlers.mjs +13 -10
- package/src/local/runtime.mjs +244 -208
- package/src/local/secure-file.mjs +73 -20
- package/src/local/security-audit-dispatch.mjs +35 -0
- package/src/local/security-audit-log.mjs +215 -0
- package/src/local/security-audit-state.mjs +139 -0
- package/src/local/security-audit-storage.mjs +135 -0
- package/src/local/security-audit-warning.mjs +31 -0
- package/src/local/security-audit-worker.mjs +87 -0
- package/src/local/service-convergence.mjs +42 -1
- package/src/local/service-definition.mjs +24 -0
- package/src/local/service-environment.mjs +18 -10
- package/src/local/service-owner.mjs +146 -0
- package/src/local/service-ownership.mjs +17 -0
- package/src/local/service-restart-handoff.mjs +64 -0
- package/src/local/service-restart-scheduler.mjs +49 -0
- package/src/local/service-runtime-convergence.mjs +52 -0
- package/src/local/service-runtime.mjs +105 -0
- package/src/local/service-status.mjs +60 -0
- package/src/local/service.mjs +385 -92
- package/src/local/shell.mjs +39 -19
- package/src/local/ssh-key.mjs +158 -42
- package/src/local/state-inventory.mjs +50 -24
- package/src/local/state-owner-lock-inventory.mjs +23 -0
- package/src/local/state-root-owned-namespaces.mjs +83 -0
- package/src/local/state-root-retirement.mjs +89 -0
- package/src/local/state.mjs +386 -96
- package/src/local/stdio.mjs +96 -66
- package/src/local/support-state-projection.mjs +30 -0
- package/src/local/system-network-route.mjs +76 -0
- package/src/local/systemd-removal.mjs +20 -0
- package/src/local/tool-executor.mjs +85 -20
- package/src/local/tool-result-boundary.mjs +51 -0
- package/src/local/toolchain-operation-lock.mjs +41 -0
- package/src/local/tools.mjs +7 -5
- package/src/local/trusted-executable.mjs +53 -0
- package/src/local/trusted-git-executable.mjs +34 -0
- package/src/local/trusted-github-cli.mjs +24 -0
- package/src/local/windows-service-convergence.mjs +49 -0
- package/src/local/windows-service.mjs +52 -45
- package/src/local/worker-deployment-fingerprint.mjs +102 -0
- package/src/local/worker-deployment.mjs +48 -60
- package/src/local/worker-health.mjs +12 -4
- package/src/local/worker-secret-file.mjs +37 -17
- package/src/local/workspace-file-service.mjs +156 -239
- package/src/local/workspace-file-transaction.mjs +189 -0
- package/src/local/workspace-search.mjs +55 -0
- package/src/local/wrangler-toolchain/package-lock.json +1505 -0
- package/src/local/wrangler-toolchain/package.json +23 -0
- package/src/local/wrangler-toolchain-verification.mjs +135 -0
- package/src/local/wrangler-toolchain.mjs +188 -0
- package/src/shared/access-contract.json +7 -1
- package/src/shared/activation-recovery.mjs +49 -0
- package/src/shared/admin-auth.d.mts +2 -1
- package/src/shared/admin-auth.mjs +3 -3
- package/src/shared/authority-revocation.d.mts +9 -0
- package/src/shared/authority-revocation.mjs +28 -0
- package/src/shared/daemon-auth.d.mts +11 -0
- package/src/shared/daemon-auth.mjs +17 -0
- package/src/shared/device-session-auth.d.mts +20 -0
- package/src/shared/device-session-auth.mjs +51 -0
- package/src/shared/foreground-timeout.d.mts +15 -0
- package/src/shared/foreground-timeout.mjs +70 -0
- package/src/shared/log-redaction.d.mts +9 -0
- package/src/shared/log-redaction.mjs +51 -0
- package/src/shared/mcp-protocol.d.mts +27 -0
- package/src/shared/mcp-protocol.mjs +275 -0
- package/src/shared/project-overview-projection.d.mts +4 -0
- package/src/shared/project-overview-projection.mjs +96 -0
- package/src/shared/relay-contract.json +28 -0
- package/src/shared/result-projection.d.mts +2 -1
- package/src/shared/result-projection.mjs +13 -2
- package/src/shared/server-metadata.json +15 -9
- package/src/shared/tool-argument-validation.d.mts +17 -0
- package/src/shared/tool-argument-validation.mjs +325 -0
- package/src/shared/tool-call-capacity.d.mts +41 -0
- package/src/shared/tool-call-capacity.mjs +59 -0
- package/src/shared/tool-catalog.json +626 -57
- package/src/worker/access.ts +12 -0
- package/src/worker/account-admin.ts +55 -33
- package/src/worker/authority-revocations.ts +174 -0
- package/src/worker/authority.ts +52 -6
- package/src/worker/daemon-auth.ts +49 -68
- package/src/worker/daemon-channel.ts +36 -0
- package/src/worker/daemon-http-auth.ts +63 -0
- package/src/worker/daemon-http-channel.ts +120 -0
- package/src/worker/daemon-http-controller.ts +163 -0
- package/src/worker/daemon-http-protocol.ts +62 -0
- package/src/worker/daemon-http-registry.ts +82 -0
- package/src/worker/daemon-last-observation.ts +30 -0
- package/src/worker/daemon-liveness.ts +8 -6
- package/src/worker/daemon-ready-dispatch.ts +17 -0
- package/src/worker/daemon-ready-messages.ts +73 -0
- package/src/worker/daemon-ready-waiters.ts +79 -0
- package/src/worker/daemon-recovery-budget.ts +13 -0
- package/src/worker/daemon-registry.ts +58 -0
- package/src/worker/daemon-relay-diagnostics.ts +127 -0
- package/src/worker/daemon-socket-attachment.ts +55 -0
- package/src/worker/daemon-sockets.ts +72 -62
- package/src/worker/daemon-status.ts +38 -0
- package/src/worker/device-session-verifier.ts +129 -0
- package/src/worker/dpop.ts +157 -0
- package/src/worker/durable-process-timeout.ts +29 -0
- package/src/worker/errors.ts +57 -0
- package/src/worker/http.ts +112 -26
- package/src/worker/index.ts +446 -440
- package/src/worker/mcp-access.ts +57 -0
- package/src/worker/mcp-controller.ts +188 -0
- package/src/worker/mcp-http-accept.ts +11 -0
- package/src/worker/mcp-http-contract.ts +322 -0
- package/src/worker/mcp-initialization-compat.ts +141 -0
- package/src/worker/mcp-jsonrpc.ts +11 -29
- package/src/worker/mcp-removed-protocol.ts +17 -0
- package/src/worker/mcp-response-proxy.ts +135 -0
- package/src/worker/mcp-response-stream.ts +65 -0
- package/src/worker/mcp-stale-schema-compat.ts +57 -0
- package/src/worker/mcp-stream-proxy-contract.ts +48 -0
- package/src/worker/mcp-tool-call-input.ts +23 -0
- package/src/worker/nonce-store.ts +33 -35
- package/src/worker/oauth-authorization-page.ts +21 -1
- package/src/worker/oauth-client-admin.ts +52 -0
- package/src/worker/oauth-client-contract.ts +38 -0
- package/src/worker/oauth-controller.ts +107 -47
- package/src/worker/oauth-field-contract.ts +31 -0
- package/src/worker/oauth-record-contract.ts +7 -0
- package/src/worker/oauth-refresh-authority.ts +27 -0
- package/src/worker/oauth-refresh-exchange.ts +162 -0
- package/src/worker/oauth-refresh-families.ts +130 -27
- package/src/worker/oauth-refresh-persistence.ts +116 -0
- package/src/worker/oauth-state.ts +33 -18
- package/src/worker/oauth-store-validation.ts +131 -0
- package/src/worker/oauth-token-derivation.ts +33 -0
- package/src/worker/oauth-token-issuance.ts +119 -0
- package/src/worker/oauth-tokens.ts +56 -190
- package/src/worker/observability.ts +38 -5
- package/src/worker/pending-admission.ts +15 -0
- package/src/worker/pending-call-capacity.ts +108 -0
- package/src/worker/pending-call-contract.ts +42 -5
- package/src/worker/pending-call-deadlines.ts +75 -0
- package/src/worker/pending-calls.ts +131 -125
- package/src/worker/runtime-alarm-storage.ts +37 -0
- package/src/worker/runtime-alarm.ts +136 -0
- package/src/worker/server-info-activity.ts +36 -0
- package/src/worker/server-info-tool-delivery.ts +15 -0
- package/src/worker/server-info.ts +168 -0
- package/src/worker/tool-catalog.ts +47 -1
- package/src/worker/tool-timeout.ts +75 -14
- package/src/worker/websocket-protocol.ts +26 -2
- package/src/worker/worker-edge-guard.ts +90 -0
- package/src/worker/worker-edge-log.ts +65 -0
- package/src/worker/worker-entry.ts +52 -0
- package/src/worker/worker-mcp-config.ts +23 -0
- package/src/worker/worker-metadata.ts +48 -0
- package/src/worker/worker-rate-limit-key.ts +30 -0
- package/src/worker/worker-runtime-config.ts +20 -0
- package/src/worker/worker-static-routes.ts +54 -0
- package/src/worker/worker-task-lifetime.ts +18 -0
- package/tsconfig.local.json +17 -1
- package/wrangler.jsonc +13 -0
- package/src/local/cli-approval.mjs +0 -117
- package/src/local/operation-state-lock.mjs +0 -92
- package/src/worker/mcp-session.ts +0 -72
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "machine-bridge-mcp-wrangler-toolchain",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "Integrity-locked private Wrangler control-plane toolchain for Machine Bridge.",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=26.0.0",
|
|
8
|
+
"npm": ">=12.0.0"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"wrangler": "4.120.0"
|
|
12
|
+
},
|
|
13
|
+
"overrides": {
|
|
14
|
+
"sharp": "0.35.3",
|
|
15
|
+
"undici": "7.29.0"
|
|
16
|
+
},
|
|
17
|
+
"allowScripts": {
|
|
18
|
+
"esbuild@0.28.1": true,
|
|
19
|
+
"fsevents": false,
|
|
20
|
+
"sharp@0.35.3": true,
|
|
21
|
+
"workerd@1.20260801.1": true
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { lstatSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { replaceFileAtomicallySync } from "./exclusive-file.mjs";
|
|
4
|
+
import {
|
|
5
|
+
isPrivateToolchainIntegrityError,
|
|
6
|
+
privateToolchainIntegrityError,
|
|
7
|
+
throwOperationalOrIntegrity,
|
|
8
|
+
} from "./private-toolchain-integrity.mjs";
|
|
9
|
+
import { ensureOwnerOnlyDirectorySync, readBoundedRegularFileSync } from "./secure-file.mjs";
|
|
10
|
+
|
|
11
|
+
const TOOLCHAIN_SCHEMA_VERSION = 1;
|
|
12
|
+
const TOOLCHAIN_MARKER = ".machine-bridge-mcp-toolchain.json";
|
|
13
|
+
const MAX_TEMPLATE_BYTES = 2 * 1024 * 1024;
|
|
14
|
+
const MAX_TREE_NODES = 20_000;
|
|
15
|
+
|
|
16
|
+
export async function verifyWranglerToolchain(descriptor, execute, required = false) {
|
|
17
|
+
try {
|
|
18
|
+
let info;
|
|
19
|
+
try { info = lstatSync(descriptor.root); }
|
|
20
|
+
catch (error) { throwOperationalOrIntegrity(error, "Wrangler toolchain directory is missing or structurally invalid"); }
|
|
21
|
+
if (info.isSymbolicLink() || !info.isDirectory()) {
|
|
22
|
+
throw privateToolchainIntegrityError("Wrangler toolchain path is not a real directory");
|
|
23
|
+
}
|
|
24
|
+
ensureOwnerOnlyDirectorySync(descriptor.root);
|
|
25
|
+
requireExactFile(path.join(descriptor.root, "package.json"), descriptor.packageBytes, "Wrangler toolchain package manifest");
|
|
26
|
+
requireExactFile(path.join(descriptor.root, "package-lock.json"), descriptor.lockBytes, "Wrangler toolchain lockfile");
|
|
27
|
+
const versionResult = await execute(["--version"]);
|
|
28
|
+
if (Number(String(versionResult.stdout).trim().split(".")[0]) < 12) throw new Error("Wrangler toolchain requires npm 12 or newer");
|
|
29
|
+
const treeResult = await execute(
|
|
30
|
+
["ls", "wrangler", "undici", "sharp", "--workspaces=false", "--all", "--json"],
|
|
31
|
+
true,
|
|
32
|
+
);
|
|
33
|
+
if (treeResult.code === 124) {
|
|
34
|
+
const timeout = new Error("Wrangler toolchain npm ls timed out");
|
|
35
|
+
timeout.code = "ETIMEDOUT";
|
|
36
|
+
throw timeout;
|
|
37
|
+
}
|
|
38
|
+
let tree;
|
|
39
|
+
try { tree = JSON.parse(treeResult.stdout); }
|
|
40
|
+
catch {
|
|
41
|
+
throw new Error(`Wrangler toolchain npm ls failed (${treeResult.code}): ${String(treeResult.stderr || "invalid JSON").slice(0, 600)}`);
|
|
42
|
+
}
|
|
43
|
+
validateInstalledTree(tree, descriptor.versions);
|
|
44
|
+
if (treeResult.code !== 0) throw new Error(`Wrangler toolchain npm ls exited ${treeResult.code} without dependency problems`);
|
|
45
|
+
return true;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if (required || !isPrivateToolchainIntegrityError(error)) throw error;
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function readWranglerToolchainMarker(root) {
|
|
53
|
+
let bytes;
|
|
54
|
+
try {
|
|
55
|
+
bytes = readBoundedRegularFileSync(
|
|
56
|
+
path.join(root, TOOLCHAIN_MARKER), 16 * 1024, "Wrangler toolchain marker",
|
|
57
|
+
{ verifyPathIdentity: true, rejectMultipleLinks: true },
|
|
58
|
+
);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
if (error?.code === "ENOENT") return null;
|
|
61
|
+
throwOperationalOrIntegrity(error, "Wrangler toolchain marker is structurally invalid");
|
|
62
|
+
}
|
|
63
|
+
try { return parseJsonObject(bytes, "Wrangler toolchain marker"); }
|
|
64
|
+
catch (error) { throw privateToolchainIntegrityError(error.message, error); }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function wranglerToolchainMarkerMatches(marker, descriptor) {
|
|
68
|
+
return Boolean(marker)
|
|
69
|
+
&& marker.schema_version === TOOLCHAIN_SCHEMA_VERSION
|
|
70
|
+
&& marker.digest === descriptor.digest
|
|
71
|
+
&& marker.wrangler === descriptor.versions.wrangler
|
|
72
|
+
&& marker.undici === descriptor.versions.undici
|
|
73
|
+
&& marker.sharp === descriptor.versions.sharp
|
|
74
|
+
&& Number.isFinite(Date.parse(String(marker.audited_at || "")));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function writeWranglerToolchainMarker(descriptor, nowMs) {
|
|
78
|
+
const auditedAt = new Date(Number(nowMs)).toISOString();
|
|
79
|
+
if (!Number.isFinite(Date.parse(auditedAt))) throw new Error("Wrangler toolchain audit timestamp is invalid");
|
|
80
|
+
replaceFileAtomicallySync(path.join(descriptor.root, TOOLCHAIN_MARKER), `${JSON.stringify({
|
|
81
|
+
schema_version: TOOLCHAIN_SCHEMA_VERSION,
|
|
82
|
+
digest: descriptor.digest,
|
|
83
|
+
wrangler: descriptor.versions.wrangler,
|
|
84
|
+
undici: descriptor.versions.undici,
|
|
85
|
+
sharp: descriptor.versions.sharp,
|
|
86
|
+
audited_at: auditedAt,
|
|
87
|
+
}, null, 2)}\n`, { mode: 0o600 });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function validateInstalledTree(tree, versions) {
|
|
91
|
+
if (!tree || typeof tree !== "object" || Array.isArray(tree)) throw privateToolchainIntegrityError("Wrangler toolchain dependency tree is invalid");
|
|
92
|
+
if (Array.isArray(tree.problems) && tree.problems.length) throw privateToolchainIntegrityError("Wrangler toolchain dependency tree contains invalid edges");
|
|
93
|
+
const found = new Map([["wrangler", []], ["undici", []], ["sharp", []]]);
|
|
94
|
+
const counter = { value: 0 };
|
|
95
|
+
visitDependencyTree(tree.dependencies, found, counter, 0);
|
|
96
|
+
for (const [name, expected] of Object.entries(versions)) {
|
|
97
|
+
const actual = found.get(name) || [];
|
|
98
|
+
if (!actual.length || actual.some((version) => version !== expected)) {
|
|
99
|
+
throw privateToolchainIntegrityError(`Wrangler toolchain ${name} versions ${actual.join(",") || "missing"} do not match ${expected}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function visitDependencyTree(dependencies, found, counter, depth) {
|
|
105
|
+
if (!dependencies || typeof dependencies !== "object" || Array.isArray(dependencies)) return;
|
|
106
|
+
if (depth > 64) throw privateToolchainIntegrityError("Wrangler toolchain dependency tree exceeds the depth limit");
|
|
107
|
+
for (const [name, value] of Object.entries(dependencies)) {
|
|
108
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
109
|
+
counter.value += 1;
|
|
110
|
+
if (counter.value > MAX_TREE_NODES) throw privateToolchainIntegrityError("Wrangler toolchain dependency tree exceeds the node limit");
|
|
111
|
+
if (found.has(name)) found.get(name).push(String(value.version || ""));
|
|
112
|
+
visitDependencyTree(value.dependencies, found, counter, depth + 1);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function requireExactFile(file, expected, label) {
|
|
117
|
+
let actual;
|
|
118
|
+
try {
|
|
119
|
+
actual = readBoundedRegularFileSync(file, MAX_TEMPLATE_BYTES, label, {
|
|
120
|
+
verifyPathIdentity: true,
|
|
121
|
+
rejectMultipleLinks: true,
|
|
122
|
+
});
|
|
123
|
+
} catch (error) {
|
|
124
|
+
throwOperationalOrIntegrity(error, `${label} is missing or structurally invalid`);
|
|
125
|
+
}
|
|
126
|
+
if (!actual.equals(expected)) throw privateToolchainIntegrityError(`${label} does not match the packaged template`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function parseJsonObject(bytes, label) {
|
|
130
|
+
let value;
|
|
131
|
+
try { value = JSON.parse(Buffer.from(bytes).toString("utf8")); }
|
|
132
|
+
catch { throw new Error(`${label} is not valid JSON`); }
|
|
133
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${label} must be an object`);
|
|
134
|
+
return value;
|
|
135
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import {
|
|
3
|
+
existsSync,
|
|
4
|
+
rmSync,
|
|
5
|
+
writeFileSync,
|
|
6
|
+
} from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { ensureHardenedNpm } from "./hardened-npm.mjs";
|
|
9
|
+
import { withOwnerStateLock } from "./owner-state-lock.mjs";
|
|
10
|
+
import { nestedNpmEnvironment } from "./npm-environment.mjs";
|
|
11
|
+
import { resolveNpmCli } from "./npm-cli.mjs";
|
|
12
|
+
import { isPrivateToolchainIntegrityError } from "./private-toolchain-integrity.mjs";
|
|
13
|
+
import {
|
|
14
|
+
readWranglerToolchainMarker,
|
|
15
|
+
verifyWranglerToolchain,
|
|
16
|
+
wranglerToolchainMarkerMatches,
|
|
17
|
+
writeWranglerToolchainMarker,
|
|
18
|
+
} from "./wrangler-toolchain-verification.mjs";
|
|
19
|
+
import { ensureOwnerOnlyDirectorySync, readBoundedRegularFileSync } from "./secure-file.mjs";
|
|
20
|
+
import { defaultStateRoot } from "./state.mjs";
|
|
21
|
+
import { withToolchainOperationLock } from "./toolchain-operation-lock.mjs";
|
|
22
|
+
import { packageRoot as defaultPackageRoot } from "./package-identity.mjs";
|
|
23
|
+
|
|
24
|
+
const TOOLCHAIN_DIRECTORY = "toolchains";
|
|
25
|
+
const TOOLCHAIN_LOCK = "wrangler-toolchain.lock";
|
|
26
|
+
const DEFAULT_AUDIT_MAX_AGE_MS = 24 * 60 * 60 * 1000;
|
|
27
|
+
const MAX_CLOCK_SKEW_MS = 5 * 60 * 1000;
|
|
28
|
+
const INSTALL_TIMEOUT_MS = 10 * 60 * 1000;
|
|
29
|
+
const AUDIT_TIMEOUT_MS = 2 * 60 * 1000;
|
|
30
|
+
const MAX_TEMPLATE_BYTES = 2 * 1024 * 1024, MAX_COMMAND_OUTPUT_BYTES = 4 * 1024 * 1024;
|
|
31
|
+
|
|
32
|
+
export async function ensureWranglerToolchain(options = {}) {
|
|
33
|
+
const runCommand = options.runCommand;
|
|
34
|
+
if (typeof runCommand !== "function") throw new TypeError("Wrangler toolchain requires an executable runner");
|
|
35
|
+
const descriptor = wranglerToolchainDescriptor(options);
|
|
36
|
+
const explicitNpmCli = options.npmCli ? resolveNpmCli(options) : "";
|
|
37
|
+
const now = typeof options.now === "function" ? options.now : Date.now;
|
|
38
|
+
const auditMaxAgeMs = positiveInteger(options.auditMaxAgeMs, DEFAULT_AUDIT_MAX_AGE_MS);
|
|
39
|
+
const parent = path.join(descriptor.stateRoot, TOOLCHAIN_DIRECTORY);
|
|
40
|
+
|
|
41
|
+
return withToolchainOperationLock(descriptor.stateRoot, async () => {
|
|
42
|
+
ensureOwnerOnlyDirectorySync(parent);
|
|
43
|
+
return withOwnerStateLock(parent, async () => {
|
|
44
|
+
const npmCli = explicitNpmCli || (await ensureHardenedNpm(parent, options.hardenedNpm || {})).cli;
|
|
45
|
+
let marker = null;
|
|
46
|
+
let installed = false;
|
|
47
|
+
try {
|
|
48
|
+
marker = readWranglerToolchainMarker(descriptor.root);
|
|
49
|
+
installed = await verifyWranglerToolchain(descriptor, (args, allowFailure = false) => runNpm(npmCli, args, descriptor.root, runCommand, options, AUDIT_TIMEOUT_MS, allowFailure));
|
|
50
|
+
} catch (error) {
|
|
51
|
+
if (!isPrivateToolchainIntegrityError(error)) throw error;
|
|
52
|
+
}
|
|
53
|
+
if (!installed) {
|
|
54
|
+
installToolchain(descriptor);
|
|
55
|
+
await runNpm(npmCli, [
|
|
56
|
+
"ci",
|
|
57
|
+
"--dry-run=false",
|
|
58
|
+
"--workspaces=false",
|
|
59
|
+
"--ignore-scripts=false",
|
|
60
|
+
"--package-lock=true",
|
|
61
|
+
"--package-lock-only=false",
|
|
62
|
+
"--omit=dev",
|
|
63
|
+
"--no-fund",
|
|
64
|
+
"--audit=false",
|
|
65
|
+
], descriptor.root, runCommand, options, INSTALL_TIMEOUT_MS);
|
|
66
|
+
await verifyWranglerToolchain(descriptor, (args, allowFailure = false) => runNpm(npmCli, args, descriptor.root, runCommand, options, AUDIT_TIMEOUT_MS, allowFailure), true);
|
|
67
|
+
await auditToolchain(descriptor, npmCli, runCommand, options);
|
|
68
|
+
writeWranglerToolchainMarker(descriptor, now());
|
|
69
|
+
return descriptor.root;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const checkedAt = now();
|
|
73
|
+
const auditAgeMs = checkedAt - Date.parse(String(marker?.audited_at || ""));
|
|
74
|
+
if (!wranglerToolchainMarkerMatches(marker, descriptor) || auditAgeMs < -MAX_CLOCK_SKEW_MS || auditAgeMs >= auditMaxAgeMs) {
|
|
75
|
+
await auditToolchain(descriptor, npmCli, runCommand, options);
|
|
76
|
+
writeWranglerToolchainMarker(descriptor, checkedAt);
|
|
77
|
+
}
|
|
78
|
+
return descriptor.root;
|
|
79
|
+
}, {
|
|
80
|
+
purpose: "wrangler-toolchain",
|
|
81
|
+
fileName: TOOLCHAIN_LOCK,
|
|
82
|
+
label: "Wrangler toolchain",
|
|
83
|
+
timeoutMs: options.lockTimeoutMs,
|
|
84
|
+
});
|
|
85
|
+
}, {
|
|
86
|
+
controlRoot: options.controlRoot,
|
|
87
|
+
timeoutMs: options.operationLockTimeoutMs,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
export function wranglerToolchainDescriptor(options = {}) {
|
|
91
|
+
const packageRoot = path.resolve(String(options.packageRoot || defaultPackageRoot));
|
|
92
|
+
const stateRoot = path.resolve(String(options.stateRoot || defaultStateRoot()));
|
|
93
|
+
const templateRoot = path.join(packageRoot, "src", "local", "wrangler-toolchain");
|
|
94
|
+
const packageBytes = readBoundedRegularFileSync(
|
|
95
|
+
path.join(templateRoot, "package.json"), MAX_TEMPLATE_BYTES, "Wrangler toolchain package manifest",
|
|
96
|
+
{ verifyPathIdentity: true, rejectMultipleLinks: true },
|
|
97
|
+
);
|
|
98
|
+
const lockBytes = readBoundedRegularFileSync(
|
|
99
|
+
path.join(templateRoot, "package-lock.json"), MAX_TEMPLATE_BYTES, "Wrangler toolchain lockfile",
|
|
100
|
+
{ verifyPathIdentity: true, rejectMultipleLinks: true },
|
|
101
|
+
);
|
|
102
|
+
const manifest = parseJsonObject(packageBytes, "Wrangler toolchain package manifest");
|
|
103
|
+
const lock = parseJsonObject(lockBytes, "Wrangler toolchain lockfile");
|
|
104
|
+
validateTemplate(manifest, lock);
|
|
105
|
+
const digest = createHash("sha256").update(packageBytes).update("\0").update(lockBytes).digest("hex");
|
|
106
|
+
const root = path.join(stateRoot, TOOLCHAIN_DIRECTORY, `wrangler-${manifest.dependencies.wrangler}-${digest.slice(0, 16)}`);
|
|
107
|
+
return Object.freeze({
|
|
108
|
+
packageRoot,
|
|
109
|
+
stateRoot,
|
|
110
|
+
templateRoot,
|
|
111
|
+
root,
|
|
112
|
+
digest,
|
|
113
|
+
packageBytes,
|
|
114
|
+
lockBytes,
|
|
115
|
+
versions: Object.freeze({
|
|
116
|
+
wrangler: String(manifest.dependencies.wrangler),
|
|
117
|
+
undici: String(manifest.overrides.undici),
|
|
118
|
+
sharp: String(manifest.overrides.sharp),
|
|
119
|
+
}),
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
function installToolchain(descriptor) {
|
|
123
|
+
const parent = path.dirname(descriptor.root);
|
|
124
|
+
ensureOwnerOnlyDirectorySync(parent);
|
|
125
|
+
if (existsSync(descriptor.root)) rmSync(descriptor.root, { recursive: true, force: true });
|
|
126
|
+
ensureOwnerOnlyDirectorySync(descriptor.root);
|
|
127
|
+
writeFileSync(path.join(descriptor.root, "package.json"), descriptor.packageBytes, { mode: 0o600 });
|
|
128
|
+
writeFileSync(path.join(descriptor.root, "package-lock.json"), descriptor.lockBytes, { mode: 0o600 });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function auditToolchain(descriptor, npmCli, runCommand, options) {
|
|
132
|
+
const audit = await runNpm(npmCli, ["audit", "--workspaces=false", "--omit=dev", "--audit-level=low", "--json"], descriptor.root, runCommand, options, AUDIT_TIMEOUT_MS, true);
|
|
133
|
+
let report;
|
|
134
|
+
try { report = JSON.parse(audit.stdout); } catch { throw new Error("Wrangler toolchain npm audit did not return valid JSON"); }
|
|
135
|
+
const vulnerabilities = report?.metadata?.vulnerabilities;
|
|
136
|
+
const total = Number(vulnerabilities?.total);
|
|
137
|
+
if (audit.code !== 0 || !Number.isFinite(total) || total !== 0) {
|
|
138
|
+
throw new Error(`Wrangler toolchain dependency audit failed (${auditSummary(vulnerabilities)})`);
|
|
139
|
+
}
|
|
140
|
+
await runNpm(npmCli, ["audit", "signatures", "--workspaces=false"], descriptor.root, runCommand, options, AUDIT_TIMEOUT_MS);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function runNpm(npmCli, args, cwd, runCommand, options, timeoutMs, allowFailure = false) {
|
|
144
|
+
return runCommand(process.execPath, [npmCli, ...args], {
|
|
145
|
+
cwd,
|
|
146
|
+
env: nestedNpmEnvironment(options.env || process.env),
|
|
147
|
+
capture: true,
|
|
148
|
+
allowFailure,
|
|
149
|
+
timeoutMs,
|
|
150
|
+
maxOutputBytes: MAX_COMMAND_OUTPUT_BYTES,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function validateTemplate(manifest, lock) {
|
|
155
|
+
if (manifest.private !== true || manifest.dependencies?.wrangler !== "4.120.0") {
|
|
156
|
+
throw new Error("Wrangler toolchain manifest lost its exact private Wrangler dependency");
|
|
157
|
+
}
|
|
158
|
+
if (manifest.overrides?.undici !== "7.29.0" || manifest.overrides?.sharp !== "0.35.3") {
|
|
159
|
+
throw new Error("Wrangler toolchain manifest lost its security overrides");
|
|
160
|
+
}
|
|
161
|
+
const expectedScripts = { "esbuild@0.28.1": true, fsevents: false, "sharp@0.35.3": true, "workerd@1.20260801.1": true };
|
|
162
|
+
if (JSON.stringify(manifest.allowScripts) !== JSON.stringify(expectedScripts)) {
|
|
163
|
+
throw new Error("Wrangler toolchain manifest lost its exact install-script policy");
|
|
164
|
+
}
|
|
165
|
+
if (lock.lockfileVersion !== 3
|
|
166
|
+
|| lock.packages?.["node_modules/wrangler"]?.version !== manifest.dependencies.wrangler
|
|
167
|
+
|| lock.packages?.["node_modules/undici"]?.version !== manifest.overrides.undici
|
|
168
|
+
|| lock.packages?.["node_modules/sharp"]?.version !== manifest.overrides.sharp) {
|
|
169
|
+
throw new Error("Wrangler toolchain lockfile does not match the exact security contract");
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function parseJsonObject(bytes, label) {
|
|
174
|
+
let value;
|
|
175
|
+
try { value = JSON.parse(Buffer.from(bytes).toString("utf8")); } catch { throw new Error(`${label} is not valid JSON`); }
|
|
176
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${label} must be an object`);
|
|
177
|
+
return value;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function auditSummary(value = {}) {
|
|
181
|
+
return ["critical", "high", "moderate", "low", "info"]
|
|
182
|
+
.map((key) => `${key}=${Number(value?.[key]) || 0}`).join(", ");
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function positiveInteger(value, fallback) {
|
|
186
|
+
const number = Number(value);
|
|
187
|
+
return Number.isFinite(number) && number > 0 ? Math.floor(number) : fallback;
|
|
188
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/** @type {Readonly<Record<string, string>>} */
|
|
4
|
+
const DETAIL_BY_REASON = Object.freeze({
|
|
5
|
+
relay_authentication_failed: "candidate relay authentication failed after readiness",
|
|
6
|
+
autostart_install_failed: "candidate autostart installation failed after readiness",
|
|
7
|
+
autostart_start_failed: "candidate autostart start failed after readiness",
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
/** @param {unknown} value */
|
|
11
|
+
export function isActivationRecoveryReason(value) {
|
|
12
|
+
return typeof value === "string" && Object.hasOwn(DETAIL_BY_REASON, value);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** @param {unknown} reason */
|
|
16
|
+
export function canonicalActivationRecoveryDetail(reason) {
|
|
17
|
+
if (typeof reason !== "string") throw new Error("activation recovery reason is invalid");
|
|
18
|
+
const detail = DETAIL_BY_REASON[reason];
|
|
19
|
+
if (!detail) throw new Error("activation recovery reason is invalid");
|
|
20
|
+
return detail;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Normalize recovered-activation metadata without retaining lower-layer error text.
|
|
25
|
+
* `detail` is still validated so historical schema-2 records fail closed when their
|
|
26
|
+
* bounded shape is corrupt, but every accepted record/result projects the fixed
|
|
27
|
+
* reason-owned detail instead of replaying the stored text.
|
|
28
|
+
* @param {{recovered: unknown, reason?: unknown, detail?: unknown}} value
|
|
29
|
+
*/
|
|
30
|
+
export function normalizeActivationRecovery(value) {
|
|
31
|
+
if (typeof value.recovered !== "boolean") throw new Error("activation recovery flag is invalid");
|
|
32
|
+
const reason = value.reason === null || value.reason === undefined ? "" : value.reason;
|
|
33
|
+
const detail = value.detail === null || value.detail === undefined ? "" : value.detail;
|
|
34
|
+
if (!value.recovered) {
|
|
35
|
+
if (reason || detail) throw new Error("activation recovery metadata is inconsistent");
|
|
36
|
+
return Object.freeze({ recovered: false, reason: "", detail: "" });
|
|
37
|
+
}
|
|
38
|
+
if (typeof reason !== "string") throw new Error("activation recovery reason is invalid");
|
|
39
|
+
if (typeof detail !== "string") throw new Error("activation recovery detail is invalid");
|
|
40
|
+
if (!isActivationRecoveryReason(reason)) throw new Error("activation recovery reason is invalid");
|
|
41
|
+
if (!detail || detail.length > 600 || /[\r\n\t]/.test(detail)) {
|
|
42
|
+
throw new Error("activation recovery detail is invalid");
|
|
43
|
+
}
|
|
44
|
+
return Object.freeze({
|
|
45
|
+
recovered: true,
|
|
46
|
+
reason,
|
|
47
|
+
detail: canonicalActivationRecoveryDetail(reason),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
export const ADMIN_AUTH_SCHEME: "
|
|
1
|
+
export const ADMIN_AUTH_SCHEME: "device-admin-signature-v1";
|
|
2
2
|
export const ADMIN_AUTH_TTL_SECONDS: number;
|
|
3
3
|
export function adminAuthTranscript(input: {
|
|
4
4
|
origin: unknown;
|
|
5
5
|
method: unknown;
|
|
6
6
|
pathname: unknown;
|
|
7
7
|
bodyHash: unknown;
|
|
8
|
+
keyId: unknown;
|
|
8
9
|
issuedAt: unknown;
|
|
9
10
|
nonce: unknown;
|
|
10
11
|
}): string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const ADMIN_AUTH_SCHEME = "
|
|
1
|
+
export const ADMIN_AUTH_SCHEME = "device-admin-signature-v1";
|
|
2
2
|
export const ADMIN_AUTH_TTL_SECONDS = 5 * 60;
|
|
3
3
|
|
|
4
4
|
export function adminAuthTranscript(input = {}) {
|
|
@@ -6,12 +6,12 @@ export function adminAuthTranscript(input = {}) {
|
|
|
6
6
|
const method = requiredToken(input.method, "method", /^[A-Z]{3,10}$/);
|
|
7
7
|
const pathname = requiredToken(input.pathname, "pathname", /^\/[A-Za-z0-9/_-]{1,255}$/);
|
|
8
8
|
const bodyHash = requiredToken(input.bodyHash, "body hash", /^[a-f0-9]{64}$/);
|
|
9
|
+
const keyId = requiredToken(input.keyId, "session key id", /^device_[A-Za-z0-9_-]{32}$/);
|
|
9
10
|
const issuedAt = requiredInteger(input.issuedAt, "issued at");
|
|
10
11
|
const nonce = requiredToken(input.nonce, "nonce", /^[A-Za-z0-9_-]{32,128}$/);
|
|
11
|
-
return [ADMIN_AUTH_SCHEME, origin, method, pathname, bodyHash, issuedAt, nonce].join("\0");
|
|
12
|
+
return [ADMIN_AUTH_SCHEME, origin, method, pathname, bodyHash, keyId, issuedAt, nonce].join("\0");
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
|
|
15
15
|
function requiredOrigin(value) {
|
|
16
16
|
let url;
|
|
17
17
|
try { url = new URL(String(value || "")); } catch { throw new Error("admin authentication origin is invalid"); }
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type AuthorityRevocation = Readonly<{
|
|
2
|
+
accountId: string;
|
|
3
|
+
accountVersion: number;
|
|
4
|
+
clientId?: string;
|
|
5
|
+
familyId?: string;
|
|
6
|
+
}>;
|
|
7
|
+
|
|
8
|
+
export function normalizeAuthorityRevocation(value: unknown): AuthorityRevocation | null;
|
|
9
|
+
export function recordMatchesAuthorityRevocation(record: object, revocation: AuthorityRevocation): boolean;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const ACCOUNT_ID = /^acct_[A-Za-z0-9_-]{20,96}$/;
|
|
2
|
+
const CLIENT_ID = /^mcp_client_[A-Za-z0-9_-]{43}$/;
|
|
3
|
+
const FAMILY_ID = /^mcp_family_[A-Za-z0-9_-]{43}$/;
|
|
4
|
+
|
|
5
|
+
export function normalizeAuthorityRevocation(value) {
|
|
6
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
7
|
+
const accountId = String(value.account_id || "");
|
|
8
|
+
const accountVersion = Number(value.account_version);
|
|
9
|
+
const clientId = value.client_id === undefined ? "" : String(value.client_id || "");
|
|
10
|
+
const familyId = value.family_id === undefined ? "" : String(value.family_id || "");
|
|
11
|
+
if (!ACCOUNT_ID.test(accountId) || !Number.isSafeInteger(accountVersion) || accountVersion <= 0) return null;
|
|
12
|
+
if (clientId && !CLIENT_ID.test(clientId)) return null;
|
|
13
|
+
if (familyId && (!clientId || !FAMILY_ID.test(familyId))) return null;
|
|
14
|
+
return Object.freeze({
|
|
15
|
+
accountId,
|
|
16
|
+
accountVersion,
|
|
17
|
+
...(clientId ? { clientId } : {}),
|
|
18
|
+
...(familyId ? { familyId } : {}),
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function recordMatchesAuthorityRevocation(record, revocation) {
|
|
23
|
+
if (!record || !revocation || record.owner_kind !== "account") return false;
|
|
24
|
+
if (record.owner_account_id !== revocation.accountId || record.owner_account_version !== revocation.accountVersion) return false;
|
|
25
|
+
if (revocation.clientId && record.owner_client_id !== revocation.clientId) return false;
|
|
26
|
+
if (revocation.familyId && record.owner_family_id !== revocation.familyId) return false;
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export const DAEMON_AUTH_SCHEME: "device-signature-v1";
|
|
2
2
|
export const DAEMON_PREFLIGHT_SCHEME: "device-preflight-v1";
|
|
3
|
+
export const DAEMON_HTTP_RELAY_SCHEME: "device-http-relay-v1";
|
|
3
4
|
export const DAEMON_AUTH_CHALLENGE_TTL_SECONDS: number;
|
|
4
5
|
export const DAEMON_PREFLIGHT_TTL_SECONDS: number;
|
|
6
|
+
export const DAEMON_HTTP_RELAY_TTL_SECONDS: number;
|
|
5
7
|
export function daemonAuthTranscript(input: {
|
|
6
8
|
challenge: unknown;
|
|
7
9
|
workerOrigin: unknown;
|
|
@@ -18,3 +20,12 @@ export function daemonPreflightTranscript(input: {
|
|
|
18
20
|
nonce: unknown;
|
|
19
21
|
issuedAt: unknown;
|
|
20
22
|
}): string;
|
|
23
|
+
|
|
24
|
+
export function daemonHttpRelayTranscript(input: {
|
|
25
|
+
workerOrigin: unknown;
|
|
26
|
+
server: unknown;
|
|
27
|
+
version: unknown;
|
|
28
|
+
nonce: unknown;
|
|
29
|
+
issuedAt: unknown;
|
|
30
|
+
bodySha256: unknown;
|
|
31
|
+
}): string;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export const DAEMON_AUTH_SCHEME = "device-signature-v1";
|
|
2
2
|
export const DAEMON_PREFLIGHT_SCHEME = "device-preflight-v1";
|
|
3
|
+
export const DAEMON_HTTP_RELAY_SCHEME = "device-http-relay-v1";
|
|
3
4
|
export const DAEMON_AUTH_CHALLENGE_TTL_SECONDS = 30;
|
|
4
5
|
export const DAEMON_PREFLIGHT_TTL_SECONDS = 5 * 60;
|
|
6
|
+
export const DAEMON_HTTP_RELAY_TTL_SECONDS = 30;
|
|
5
7
|
|
|
6
8
|
|
|
7
9
|
export function daemonPreflightTranscript(input = {}) {
|
|
@@ -29,6 +31,21 @@ export function daemonAuthTranscript(input = {}) {
|
|
|
29
31
|
return values.join("\0");
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
export function daemonHttpRelayTranscript(input = {}) {
|
|
35
|
+
const values = [
|
|
36
|
+
DAEMON_HTTP_RELAY_SCHEME,
|
|
37
|
+
"POST",
|
|
38
|
+
"/daemon/http",
|
|
39
|
+
requiredOrigin(input.workerOrigin),
|
|
40
|
+
requiredText(input.server, "server", 1, 128),
|
|
41
|
+
requiredText(input.version, "version", 1, 64),
|
|
42
|
+
requiredText(input.nonce, "HTTP relay nonce", 24, 128),
|
|
43
|
+
requiredInteger(input.issuedAt, "HTTP relay issued at"),
|
|
44
|
+
requiredText(input.bodySha256, "HTTP relay body SHA-256", 43, 43),
|
|
45
|
+
];
|
|
46
|
+
return values.join("\0");
|
|
47
|
+
}
|
|
48
|
+
|
|
32
49
|
function requiredText(value, label, minimum, maximum) {
|
|
33
50
|
const text = String(value || "");
|
|
34
51
|
if (text.length < minimum || text.length > maximum || /[\0\r\n]/.test(text)) {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const DEVICE_SESSION_CERTIFICATE_SCHEME: "device-session-certificate-v1";
|
|
2
|
+
export const DEVICE_SESSION_MAX_LIFETIME_SECONDS: number;
|
|
3
|
+
|
|
4
|
+
export function deviceSessionCertificateTranscript(input?: {
|
|
5
|
+
workerOrigin?: unknown;
|
|
6
|
+
server?: unknown;
|
|
7
|
+
version?: unknown;
|
|
8
|
+
rootKeyId?: unknown;
|
|
9
|
+
publicJwk?: unknown;
|
|
10
|
+
issuedAt?: unknown;
|
|
11
|
+
expiresAt?: unknown;
|
|
12
|
+
nonce?: unknown;
|
|
13
|
+
}): string;
|
|
14
|
+
|
|
15
|
+
export function canonicalPublicJwk(value: unknown): {
|
|
16
|
+
kty: "EC";
|
|
17
|
+
crv: "P-256";
|
|
18
|
+
x: string;
|
|
19
|
+
y: string;
|
|
20
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export const DEVICE_SESSION_CERTIFICATE_SCHEME = "device-session-certificate-v1";
|
|
2
|
+
export const DEVICE_SESSION_MAX_LIFETIME_SECONDS = 24 * 60 * 60;
|
|
3
|
+
|
|
4
|
+
export function deviceSessionCertificateTranscript(input = {}) {
|
|
5
|
+
const publicJwk = canonicalPublicJwk(input.publicJwk);
|
|
6
|
+
return [
|
|
7
|
+
DEVICE_SESSION_CERTIFICATE_SCHEME,
|
|
8
|
+
requiredOrigin(input.workerOrigin),
|
|
9
|
+
requiredText(input.server, "server", 1, 128),
|
|
10
|
+
requiredText(input.version, "version", 1, 64),
|
|
11
|
+
requiredText(input.rootKeyId, "root key id", 16, 128),
|
|
12
|
+
JSON.stringify(publicJwk),
|
|
13
|
+
requiredInteger(input.issuedAt, "issued at"),
|
|
14
|
+
requiredInteger(input.expiresAt, "expires at"),
|
|
15
|
+
requiredText(input.nonce, "nonce", 24, 128),
|
|
16
|
+
].join("\0");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function canonicalPublicJwk(value) {
|
|
20
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("device session public key is invalid");
|
|
21
|
+
if (value.kty !== "EC" || value.crv !== "P-256" || typeof value.x !== "string" || typeof value.y !== "string" || value.d !== undefined) {
|
|
22
|
+
throw new Error("device session public key is invalid");
|
|
23
|
+
}
|
|
24
|
+
if (!/^[A-Za-z0-9_-]{42,44}$/.test(value.x) || !/^[A-Za-z0-9_-]{42,44}$/.test(value.y)) {
|
|
25
|
+
throw new Error("device session public key is invalid");
|
|
26
|
+
}
|
|
27
|
+
return { crv: "P-256", kty: "EC", x: value.x, y: value.y };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function requiredOrigin(value) {
|
|
31
|
+
let url;
|
|
32
|
+
try { url = new URL(String(value || "")); } catch { throw new Error("device session Worker origin is invalid"); }
|
|
33
|
+
const secure = url.protocol === "https:";
|
|
34
|
+
const loopback = url.protocol === "http:" && ["127.0.0.1", "localhost", "[::1]"].includes(url.hostname);
|
|
35
|
+
if ((!secure && !loopback) || url.username || url.password || url.pathname !== "/" || url.search || url.hash) {
|
|
36
|
+
throw new Error("device session Worker origin is invalid");
|
|
37
|
+
}
|
|
38
|
+
return url.origin;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function requiredText(value, label, minimum, maximum) {
|
|
42
|
+
const text = String(value || "");
|
|
43
|
+
if (text.length < minimum || text.length > maximum || /[\0\r\n]/.test(text)) throw new Error(`device session ${label} is invalid`);
|
|
44
|
+
return text;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function requiredInteger(value, label) {
|
|
48
|
+
const number = Number(value);
|
|
49
|
+
if (!Number.isSafeInteger(number) || number <= 0) throw new Error(`device session ${label} is invalid`);
|
|
50
|
+
return String(number);
|
|
51
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const REMOTE_FOREGROUND_TIMEOUT_SECONDS: number;
|
|
2
|
+
export const REMOTE_DURABLE_PROCESS_DEFAULT_TIMEOUT_SECONDS: number;
|
|
3
|
+
export const REMOTE_DURABLE_PROCESS_MAXIMUM_TIMEOUT_SECONDS: number;
|
|
4
|
+
export function isConfigurableForegroundTool(name: string): boolean;
|
|
5
|
+
export function isRemoteDurableProcessTool(name: string): boolean;
|
|
6
|
+
export function remoteDurableProcessTimeoutSeconds(value: unknown): number;
|
|
7
|
+
export function remoteForegroundDefaultSeconds(name: string): number;
|
|
8
|
+
export function remoteForegroundMaximumSeconds(name: string): number;
|
|
9
|
+
export function effectiveForegroundTimeoutSeconds(input: Readonly<{
|
|
10
|
+
tool: string;
|
|
11
|
+
value: unknown;
|
|
12
|
+
remote: boolean;
|
|
13
|
+
localDefault: number;
|
|
14
|
+
localMaximum: number;
|
|
15
|
+
}>): number;
|