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
|
@@ -2,29 +2,24 @@ import { spawn } from "node:child_process";
|
|
|
2
2
|
import { basename } from "node:path";
|
|
3
3
|
import { stat } from "node:fs/promises";
|
|
4
4
|
import { BoundedOutput } from "./bounded-output.mjs";
|
|
5
|
+
import { attachChildProcessSettlement } from "./child-process-settlement.mjs";
|
|
5
6
|
import { executionEnv, workspaceShellCommand } from "./shell.mjs";
|
|
6
7
|
import { MAX_COMMAND_BYTES, validateArgv } from "./process-contract.mjs";
|
|
7
8
|
import { terminateProcessTreeWithEscalation } from "./process-tree.mjs";
|
|
8
9
|
import { BridgeError } from "./errors.mjs";
|
|
9
|
-
import {
|
|
10
|
+
import { delegatedProcessCommand } from "./delegated-process-sandbox.mjs";
|
|
10
11
|
import { ProcessOutputStream } from "./process-output-stream.mjs";
|
|
12
|
+
import { processForegroundTimeoutSeconds, registeredCommandTimeoutSeconds } from "./process-foreground-timeout.mjs";
|
|
11
13
|
import { processFailureMessage, publicProcessToolResult } from "./process-result-projection.mjs";
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
PUBLIC_PROCESS_INLINE_OUTPUT_BYTES,
|
|
20
|
-
} from "./execution-limits.mjs";
|
|
21
|
-
|
|
22
|
-
const PROCESS_OUTPUT_CAPTURE = Symbol("process-output-capture");
|
|
23
|
-
const CONTINUATION_READ_BYTES = 64 * 1024;
|
|
24
|
-
|
|
14
|
+
import { processCancellationFailure, processChildErrorFailure, processPostSpawnFailure, processPreSpawnFailure, processTimeoutFailure } from "./process-nonreplayable-settlement.mjs";
|
|
15
|
+
import { acquireProcessResources, bindProcessResources, releaseProcessResources, releaseProcessResourcesQuietly } from "./resource-process-admission.mjs";
|
|
16
|
+
import { foregroundResourceWaitMs } from "./resource-foreground-wait.mjs";
|
|
17
|
+
import { validateFixedProcessEnvironment } from "./fixed-process-environment.mjs";
|
|
18
|
+
import { EXECUTION_SURFACE, withExecutionSurface } from "./execution-surface.mjs";
|
|
19
|
+
import { DEFAULT_PROCESS_OUTPUT_BYTES, MAX_PROCESS_SESSION_OUTPUT_BYTES, MAX_PROCESS_STDIN_BYTES, PROCESS_SESSION_RETENTION_MS, PUBLIC_PROCESS_INLINE_OUTPUT_BYTES } from "./execution-limits.mjs";
|
|
20
|
+
const PROCESS_OUTPUT_CAPTURE = Symbol("process-output-capture"); const CONTINUATION_READ_BYTES = 64 * 1024;
|
|
25
21
|
function spawnDirectProcess(command, args, options) {
|
|
26
|
-
// Keep the production child_process API
|
|
27
|
-
// injectable test seam and enforce non-shell execution at the final boundary.
|
|
22
|
+
// Keep the production child_process API separate from the injectable test seam and enforce non-shell execution.
|
|
28
23
|
return spawn(command, args, {
|
|
29
24
|
cwd: options.cwd,
|
|
30
25
|
env: options.env,
|
|
@@ -33,55 +28,53 @@ function spawnDirectProcess(command, args, options) {
|
|
|
33
28
|
shell: false,
|
|
34
29
|
});
|
|
35
30
|
}
|
|
36
|
-
|
|
37
31
|
export class ProcessExecutionService {
|
|
38
|
-
constructor({ workspace, policy, policyGate, runtimeDir, processTracker, resolveExistingPath, resolveLocalCommand, displayPath, throwIfCancelled, retainCompletedOutput = null, spawnProcess = spawnDirectProcess, terminateProcess = terminateProcessTreeWithEscalation }) {
|
|
32
|
+
constructor({ workspace, policy, policyGate, policyForContext = null, runtimeDir, processTracker, resourceCoordinator = null, resourceWaitMs = undefined, resolveExistingPath, resolveLocalCommand, displayPath, throwIfCancelled, retainCompletedOutput = null, spawnProcess = spawnDirectProcess, terminateProcess = terminateProcessTreeWithEscalation, childSettlementOptions = {} }) {
|
|
39
33
|
this.workspace = workspace;
|
|
40
34
|
this.policy = policy;
|
|
41
35
|
this.policyGate = policyGate;
|
|
36
|
+
this.policyForContext = typeof policyForContext === "function" ? policyForContext : () => this.policy;
|
|
42
37
|
this.runtimeDir = runtimeDir;
|
|
43
38
|
this.processTracker = processTracker;
|
|
39
|
+
this.resourceCoordinator = resourceCoordinator;
|
|
40
|
+
this.resourceWaitMs = resourceWaitMs;
|
|
44
41
|
this.resolveExistingPath = resolveExistingPath;
|
|
45
42
|
this.resolveLocalCommand = resolveLocalCommand;
|
|
46
43
|
this.displayPath = displayPath;
|
|
47
44
|
this.throwIfCancelled = throwIfCancelled;
|
|
48
45
|
this.retainCompletedOutput = typeof retainCompletedOutput === "function" ? retainCompletedOutput : null;
|
|
49
|
-
this.spawnProcess = spawnProcess;
|
|
50
|
-
this.terminateProcess = terminateProcess;
|
|
46
|
+
this.spawnProcess = spawnProcess; this.terminateProcess = terminateProcess; this.childSettlementOptions = childSettlementOptions;
|
|
51
47
|
}
|
|
52
|
-
|
|
53
48
|
async runDirect(args, context = {}) {
|
|
54
49
|
this.policyGate.assert("run_process");
|
|
55
50
|
const argv = validateArgv(args.argv);
|
|
56
|
-
const cwd = await this.resolveExistingPath(args.cwd || ".");
|
|
51
|
+
const cwd = await this.resolveExistingPath(args.cwd || ".", context);
|
|
57
52
|
if (!(await stat(cwd)).isDirectory()) throw new BridgeError("invalid_request", "cwd is not a directory");
|
|
53
|
+
const timeoutSeconds = processForegroundTimeoutSeconds("run_process", args.timeout_seconds, context);
|
|
58
54
|
const result = await this.runPublic(
|
|
59
|
-
argv[0], argv.slice(1),
|
|
60
|
-
clampInteger(args.timeout_seconds, 120, MIN_PROCESS_TIMEOUT_SECONDS, MAX_PROCESS_TIMEOUT_SECONDS) * 1000,
|
|
55
|
+
argv[0], argv.slice(1), timeoutSeconds * 1000,
|
|
61
56
|
context, cwd,
|
|
62
57
|
);
|
|
63
58
|
return publicProcessToolResult(result);
|
|
64
59
|
}
|
|
65
|
-
|
|
66
60
|
async runRegistered(args, context = {}) {
|
|
67
61
|
this.policyGate.assert("run_local_command");
|
|
68
62
|
const command = await this.resolveLocalCommand(args, context);
|
|
69
63
|
const argv = validateArgv(command.argv);
|
|
70
|
-
const cwd = await this.resolveExistingPath(command.cwd);
|
|
64
|
+
const cwd = await this.resolveExistingPath(command.cwd, context);
|
|
71
65
|
if (!(await stat(cwd)).isDirectory()) throw new BridgeError("invalid_request", "registered command cwd is not a directory");
|
|
72
|
-
const
|
|
73
|
-
? command.timeoutSeconds
|
|
74
|
-
: clampInteger(args.timeout_seconds, command.timeoutSeconds, MIN_PROCESS_TIMEOUT_SECONDS, MAX_PROCESS_TIMEOUT_SECONDS);
|
|
75
|
-
const timeoutSeconds = Math.min(requested, command.timeoutSeconds);
|
|
66
|
+
const timeoutSeconds = registeredCommandTimeoutSeconds(args, command, context);
|
|
76
67
|
const result = await this.runPublic(argv[0], argv.slice(1), timeoutSeconds * 1000, context, cwd);
|
|
77
|
-
return publicProcessToolResult({ name: command.name, cwd: this.displayPath(cwd), timeout_seconds: timeoutSeconds, ...result });
|
|
68
|
+
return publicProcessToolResult({ name: command.name, cwd: this.displayPath(cwd, context), timeout_seconds: timeoutSeconds, ...result });
|
|
78
69
|
}
|
|
79
|
-
|
|
80
|
-
async probeShell(context = {}) {
|
|
70
|
+
async probeShell(context = {}, timeoutMs = 5_000) {
|
|
81
71
|
const shell = workspaceShellCommand(process.platform === "win32" ? "cd" : "pwd");
|
|
82
|
-
return this.run(shell.cmd, shell.args,
|
|
72
|
+
return this.run(shell.cmd, shell.args, timeoutMs, true, 64 * 1024, context, this.workspace, null, { internalFixed: true });
|
|
73
|
+
}
|
|
74
|
+
async runFixedInternal(cmd, args, timeoutMs, allowFailure = false, maxOutputBytes = DEFAULT_PROCESS_OUTPUT_BYTES, context = {}, cwd = this.workspace, stdin = null, environment = {}) {
|
|
75
|
+
const argv = validateArgv([cmd, ...args]);
|
|
76
|
+
return this.run(argv[0], argv.slice(1), timeoutMs, allowFailure, maxOutputBytes, context, cwd, stdin, { internalFixed: true, internalEnvironment: validateFixedProcessEnvironment(environment) });
|
|
83
77
|
}
|
|
84
|
-
|
|
85
78
|
async runShell(command, timeoutSeconds, context = {}) {
|
|
86
79
|
this.policyGate.assert("exec_command");
|
|
87
80
|
if (!command || typeof command !== "string") throw new BridgeError("invalid_request", "command is required");
|
|
@@ -90,16 +83,14 @@ export class ProcessExecutionService {
|
|
|
90
83
|
const shell = workspaceShellCommand(command);
|
|
91
84
|
const result = await this.runPublic(
|
|
92
85
|
shell.cmd, shell.args,
|
|
93
|
-
|
|
86
|
+
processForegroundTimeoutSeconds("exec_command", timeoutSeconds, context) * 1000,
|
|
94
87
|
context, this.workspace,
|
|
95
88
|
);
|
|
96
89
|
return publicProcessToolResult(result);
|
|
97
90
|
}
|
|
98
|
-
|
|
99
91
|
terminateAll(signal = "SIGTERM", escalate = false) {
|
|
100
92
|
this.processTracker.terminateAll(signal, escalate);
|
|
101
93
|
}
|
|
102
|
-
|
|
103
94
|
async runPublic(cmd, args, timeoutMs, context, cwd) {
|
|
104
95
|
const startedAt = Date.now();
|
|
105
96
|
const result = await this.run(
|
|
@@ -114,7 +105,7 @@ export class ProcessExecutionService {
|
|
|
114
105
|
command: basename(cmd), cwd,
|
|
115
106
|
stdout: capture.stdout, stderr: capture.stderr,
|
|
116
107
|
exitCode: result.code, startedAt, closedAt: Date.now(),
|
|
117
|
-
});
|
|
108
|
+
}, context);
|
|
118
109
|
if (session) {
|
|
119
110
|
continuation = {
|
|
120
111
|
output_session_id: session.session_id,
|
|
@@ -141,13 +132,22 @@ export class ProcessExecutionService {
|
|
|
141
132
|
}
|
|
142
133
|
return publicResult;
|
|
143
134
|
}
|
|
144
|
-
|
|
145
135
|
async run(cmd, args, timeoutMs, allowFailure = false, maxOutputBytes = DEFAULT_PROCESS_OUTPUT_BYTES, context = {}, cwd = this.workspace, stdin = null, options = {}) {
|
|
146
136
|
this.throwIfCancelled(context);
|
|
147
137
|
if (stdin !== null && Buffer.byteLength(String(stdin)) > MAX_PROCESS_STDIN_BYTES) {
|
|
148
138
|
throw new BridgeError("limit_exceeded", "process stdin exceeds 1 MiB");
|
|
149
139
|
}
|
|
150
|
-
|
|
140
|
+
const internalFixed = options.internalFixed === true;
|
|
141
|
+
const baseEnvironment = executionEnv(this.workspace, {
|
|
142
|
+
fullEnv: internalFixed ? false : this.policyForContext(context).minimalEnv === false, runtimeDir: this.runtimeDir,
|
|
143
|
+
});
|
|
144
|
+
if (internalFixed) Object.assign(baseEnvironment, options.internalEnvironment || {});
|
|
145
|
+
const executionEnvironment = internalFixed ? baseEnvironment : withExecutionSurface(baseEnvironment, EXECUTION_SURFACE.foregroundProcess);
|
|
146
|
+
const admitted = internalFixed || !this.resourceCoordinator
|
|
147
|
+
? { lease: null, environment: executionEnvironment, command: cmd, args }
|
|
148
|
+
: await acquireProcessResources(this.resourceCoordinator, cmd, args, executionEnvironment, {
|
|
149
|
+
cwd, priority: options.resourcePriority || "interactive", waitMs: options.resourceWaitMs ?? foregroundResourceWaitMs(timeoutMs, this.resourceWaitMs), signal: context.signal,
|
|
150
|
+
});
|
|
151
151
|
return new Promise((resolvePromise, rejectPromise) => {
|
|
152
152
|
const stdout = new BoundedOutput(maxOutputBytes);
|
|
153
153
|
const stderr = new BoundedOutput(maxOutputBytes);
|
|
@@ -155,62 +155,64 @@ export class ProcessExecutionService {
|
|
|
155
155
|
const retainedStderr = options.retainOutput ? new ProcessOutputStream(MAX_PROCESS_SESSION_OUTPUT_BYTES) : null;
|
|
156
156
|
let child;
|
|
157
157
|
try {
|
|
158
|
-
|
|
158
|
+
const launch = internalFixed
|
|
159
|
+
? { command: cmd, args }
|
|
160
|
+
: delegatedProcessCommand({ command: admitted.command, args: admitted.args, workspace: this.workspace, runtimeDir: this.runtimeDir, context });
|
|
161
|
+
child = this.spawnProcess(launch.command, launch.args, {
|
|
159
162
|
cwd,
|
|
160
|
-
env:
|
|
163
|
+
env: admitted.environment,
|
|
161
164
|
detached: process.platform !== "win32",
|
|
162
165
|
windowsHide: true,
|
|
163
166
|
shell: false,
|
|
164
167
|
});
|
|
165
168
|
} catch (error) {
|
|
166
|
-
rejectPromise(
|
|
169
|
+
void releaseProcessResourcesQuietly(admitted.lease).then(() => rejectPromise(
|
|
170
|
+
processPreSpawnFailure(error, options.nonReplayableMutation === true),
|
|
171
|
+
));
|
|
167
172
|
return;
|
|
168
173
|
}
|
|
169
|
-
|
|
170
174
|
this.processTracker.track(child, context.callId);
|
|
175
|
+
let resourceBindError = null; let childError = null;
|
|
176
|
+
const resourceBinding = bindProcessResources(admitted.lease, child).catch((error) => { resourceBindError = error; this.terminateProcess(child); });
|
|
171
177
|
if (stdin !== null) {
|
|
172
178
|
child.stdin?.on?.("error", () => {});
|
|
173
179
|
child.stdin?.end?.(String(stdin));
|
|
174
180
|
}
|
|
175
|
-
|
|
176
181
|
let settled = false;
|
|
177
182
|
let processClosed = false;
|
|
178
183
|
let terminationTimer = null;
|
|
179
184
|
let timeoutTimer = null;
|
|
180
185
|
const signal = context.signal;
|
|
181
|
-
|
|
182
|
-
const cleanupAfterClose = () => {
|
|
186
|
+
const nonReplayableMutation = options.nonReplayableMutation === true;
|
|
187
|
+
const cleanupAfterClose = async () => {
|
|
183
188
|
if (processClosed) return;
|
|
184
189
|
processClosed = true;
|
|
185
190
|
clearTimeout(timeoutTimer);
|
|
186
191
|
signal?.removeEventListener?.("abort", onAbort);
|
|
187
192
|
this.processTracker.untrack(child);
|
|
193
|
+
await resourceBinding;
|
|
194
|
+
await releaseProcessResources(admitted.lease);
|
|
188
195
|
};
|
|
189
|
-
|
|
190
196
|
const settle = (callback) => {
|
|
191
197
|
if (settled) return false;
|
|
192
198
|
settled = true;
|
|
193
199
|
callback();
|
|
194
200
|
return true;
|
|
195
201
|
};
|
|
196
|
-
|
|
197
202
|
const terminate = () => {
|
|
198
203
|
if (terminationTimer || processClosed) return;
|
|
199
204
|
terminationTimer = this.terminateProcess(child);
|
|
200
205
|
};
|
|
201
|
-
|
|
202
206
|
const rejectCancelled = () => {
|
|
203
207
|
terminate();
|
|
204
|
-
|
|
205
|
-
settle(() => rejectPromise(reason instanceof Error ? reason : new BridgeError("cancelled", "tool call cancelled")));
|
|
208
|
+
settle(() => rejectPromise(processCancellationFailure(nonReplayableMutation, signal)));
|
|
206
209
|
};
|
|
207
|
-
|
|
208
210
|
const onAbort = () => rejectCancelled();
|
|
209
211
|
signal?.addEventListener?.("abort", onAbort, { once: true });
|
|
210
212
|
|
|
211
213
|
timeoutTimer = setTimeout(() => {
|
|
212
214
|
terminate();
|
|
213
|
-
settle(() => rejectPromise(
|
|
215
|
+
settle(() => rejectPromise(processTimeoutFailure(nonReplayableMutation, timeoutMs)));
|
|
214
216
|
}, timeoutMs);
|
|
215
217
|
timeoutTimer.unref?.();
|
|
216
218
|
|
|
@@ -223,31 +225,42 @@ export class ProcessExecutionService {
|
|
|
223
225
|
retainedStderr?.append(chunk);
|
|
224
226
|
});
|
|
225
227
|
|
|
226
|
-
child.on("error", (error) => {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
228
|
+
child.on("error", (error) => { childError ||= error; });
|
|
229
|
+
attachChildProcessSettlement(child, {
|
|
230
|
+
...this.childSettlementOptions,
|
|
231
|
+
onSettle: (code) => {
|
|
232
|
+
void cleanupAfterClose().then(() => {
|
|
233
|
+
if (settled) return;
|
|
234
|
+
if (resourceBindError) {
|
|
235
|
+
settle(() => rejectPromise(processPostSpawnFailure(nonReplayableMutation, "resource_binding", resourceBindError)));
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
if (childError) {
|
|
239
|
+
const failure = processResult(127, stdout, childError.message || stderr.text(), retainedStdout, retainedStderr);
|
|
240
|
+
settle(() => {
|
|
241
|
+
if (nonReplayableMutation) rejectPromise(processChildErrorFailure(true, childError, Boolean(child?.pid)));
|
|
242
|
+
else if (allowFailure) resolvePromise(failure);
|
|
243
|
+
else rejectPromise(childError);
|
|
244
|
+
});
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
try { this.throwIfCancelled(context); } catch (error) {
|
|
248
|
+
const trigger = signal?.reason instanceof BridgeError && signal.reason.code === "timeout" ? "timeout" : "cancelled";
|
|
249
|
+
settle(() => rejectPromise(processPostSpawnFailure(nonReplayableMutation, trigger, error)));
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const result = processResult(code, stdout, stderr, retainedStdout, retainedStderr);
|
|
253
|
+
if (code === 0 || allowFailure) settle(() => resolvePromise(result));
|
|
254
|
+
else settle(() => rejectPromise(processPostSpawnFailure(nonReplayableMutation, "nonzero_exit",
|
|
255
|
+
new BridgeError("execution_failed", processFailureMessage(result), { details: { process: result } }), { process: result })));
|
|
256
|
+
}, rejectPromise);
|
|
257
|
+
},
|
|
244
258
|
});
|
|
245
259
|
|
|
246
260
|
if (signal?.aborted) rejectCancelled();
|
|
247
261
|
});
|
|
248
262
|
}
|
|
249
263
|
}
|
|
250
|
-
|
|
251
264
|
function processResult(code, stdout, stderr, retainedStdout = null, retainedStderr = null) {
|
|
252
265
|
const stderrBuffer = stderr instanceof BoundedOutput ? stderr : null;
|
|
253
266
|
const result = {
|
|
@@ -265,7 +278,6 @@ function processResult(code, stdout, stderr, retainedStdout = null, retainedStde
|
|
|
265
278
|
}
|
|
266
279
|
return result;
|
|
267
280
|
}
|
|
268
|
-
|
|
269
281
|
export function boundedErrorMessage(error) {
|
|
270
282
|
const message = error instanceof Error ? error.message : String(error);
|
|
271
283
|
return message.replace(/[\r\n]+/g, " ").slice(0, 4096) || "tool call failed";
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {
|
|
2
|
+
effectiveForegroundTimeoutSeconds,
|
|
3
|
+
remoteForegroundDefaultSeconds,
|
|
4
|
+
remoteDurableProcessTimeoutSeconds,
|
|
5
|
+
} from "../shared/foreground-timeout.mjs";
|
|
6
|
+
import { MAX_PROCESS_TIMEOUT_SECONDS } from "./execution-limits.mjs";
|
|
7
|
+
|
|
8
|
+
// Relay-origin process one-shots should be routed through durable delivery before reaching this module.
|
|
9
|
+
// Keep a narrower defense-in-depth ceiling for any accidental direct foreground entry without advertising
|
|
10
|
+
// that value as a supported remote process delivery contract.
|
|
11
|
+
const REMOTE_PROCESS_FOREGROUND_DEFENSE_MAX_SECONDS = 30;
|
|
12
|
+
|
|
13
|
+
export function processForegroundTimeoutSeconds(tool, value, context = {}) {
|
|
14
|
+
const remote = context.origin === "relay";
|
|
15
|
+
const timeout = effectiveForegroundTimeoutSeconds({
|
|
16
|
+
tool,
|
|
17
|
+
value,
|
|
18
|
+
remote,
|
|
19
|
+
localDefault: 120,
|
|
20
|
+
localMaximum: MAX_PROCESS_TIMEOUT_SECONDS,
|
|
21
|
+
});
|
|
22
|
+
if (remote && timeout > REMOTE_PROCESS_FOREGROUND_DEFENSE_MAX_SECONDS) {
|
|
23
|
+
throw new RangeError(`foreground timeout must be an integer from 1 to ${REMOTE_PROCESS_FOREGROUND_DEFENSE_MAX_SECONDS}`);
|
|
24
|
+
}
|
|
25
|
+
return timeout;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function registeredCommandTimeoutSeconds(args, command, context = {}) {
|
|
29
|
+
const remote = context.origin === "relay";
|
|
30
|
+
const requested = args.timeout_seconds === undefined
|
|
31
|
+
? (remote ? remoteForegroundDefaultSeconds("run_local_command") : command.timeoutSeconds)
|
|
32
|
+
: processForegroundTimeoutSeconds("run_local_command", args.timeout_seconds, context);
|
|
33
|
+
return Math.min(
|
|
34
|
+
requested,
|
|
35
|
+
command.timeoutSeconds,
|
|
36
|
+
remote ? REMOTE_PROCESS_FOREGROUND_DEFENSE_MAX_SECONDS : MAX_PROCESS_TIMEOUT_SECONDS,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function durableProcessExecutionTimeoutSeconds(value) {
|
|
41
|
+
return remoteDurableProcessTimeoutSeconds(value);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function durableRegisteredCommandTimeoutSeconds(args, command) {
|
|
45
|
+
return Math.min(
|
|
46
|
+
remoteDurableProcessTimeoutSeconds(args.timeout_seconds),
|
|
47
|
+
command.timeoutSeconds,
|
|
48
|
+
MAX_PROCESS_TIMEOUT_SECONDS,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spawnSync } from "node:child_process";
|
|
1
|
+
import { execFile, spawnSync } from "node:child_process";
|
|
2
2
|
import { performance } from "node:perf_hooks";
|
|
3
3
|
|
|
4
4
|
const COMMAND_TIMEOUT_MS = 3000;
|
|
@@ -25,15 +25,30 @@ export function processStartTimeMs(pid) {
|
|
|
25
25
|
const parsed = normalizePid(pid);
|
|
26
26
|
if (!parsed) return null;
|
|
27
27
|
if (parsed === process.pid) return currentProcessStartTimeMs();
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
const [command, args] = processStartCommand(parsed);
|
|
29
|
+
const result = runBounded(command, args);
|
|
30
|
+
return result.ok ? parseTime(result.stdout) : null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function processStartTimeMsAsync(pid) {
|
|
34
|
+
const parsed = normalizePid(pid);
|
|
35
|
+
if (!parsed) return null;
|
|
36
|
+
if (parsed === process.pid) return currentProcessStartTimeMs();
|
|
37
|
+
const [command, args] = processStartCommand(parsed);
|
|
38
|
+
const result = await runBoundedAsync(command, args);
|
|
34
39
|
return result.ok ? parseTime(result.stdout) : null;
|
|
35
40
|
}
|
|
36
41
|
|
|
42
|
+
export function processState(pid) {
|
|
43
|
+
const parsed = normalizePid(pid);
|
|
44
|
+
if (!parsed || process.platform === "win32") return "unknown";
|
|
45
|
+
const result = runBounded("ps", ["-p", String(parsed), "-o", "state="]);
|
|
46
|
+
if (!result.ok) return "unknown";
|
|
47
|
+
const state = result.stdout.trim().charAt(0).toUpperCase();
|
|
48
|
+
if (state === "Z") return "zombie";
|
|
49
|
+
return state ? "running" : "unknown";
|
|
50
|
+
}
|
|
51
|
+
|
|
37
52
|
export function processCommandLine(pid) {
|
|
38
53
|
const parsed = normalizePid(pid);
|
|
39
54
|
if (!parsed) return "";
|
|
@@ -119,10 +134,19 @@ export function inspectProcessInstance(owner, options = {}) {
|
|
|
119
134
|
};
|
|
120
135
|
}
|
|
121
136
|
|
|
137
|
+
function processStartCommand(pid) {
|
|
138
|
+
if (process.platform === "win32") {
|
|
139
|
+
const command = `(Get-CimInstance Win32_Process -Filter "ProcessId = ${pid}").CreationDate.ToUniversalTime().ToString('o')`;
|
|
140
|
+
return ["powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", command]];
|
|
141
|
+
}
|
|
142
|
+
return ["ps", ["-p", String(pid), "-o", "lstart="]];
|
|
143
|
+
}
|
|
144
|
+
|
|
122
145
|
function runBounded(command, args) {
|
|
123
146
|
const result = spawnSync(command, args, {
|
|
124
147
|
encoding: "utf8",
|
|
125
148
|
timeout: COMMAND_TIMEOUT_MS,
|
|
149
|
+
killSignal: "SIGKILL",
|
|
126
150
|
maxBuffer: COMMAND_OUTPUT_BYTES,
|
|
127
151
|
windowsHide: true,
|
|
128
152
|
env: process.platform === "win32" ? process.env : { ...process.env, LC_ALL: "C", LANG: "C" },
|
|
@@ -131,6 +155,16 @@ function runBounded(command, args) {
|
|
|
131
155
|
return { ok: !result.error && result.status === 0, stdout: String(result.stdout || "") };
|
|
132
156
|
}
|
|
133
157
|
|
|
158
|
+
function runBoundedAsync(command, args) {
|
|
159
|
+
return new Promise((resolvePromise) => {
|
|
160
|
+
execFile(command, args, {
|
|
161
|
+
encoding: "utf8", timeout: COMMAND_TIMEOUT_MS, killSignal: "SIGKILL",
|
|
162
|
+
maxBuffer: COMMAND_OUTPUT_BYTES, windowsHide: true,
|
|
163
|
+
env: process.platform === "win32" ? process.env : { ...process.env, LC_ALL: "C", LANG: "C" },
|
|
164
|
+
}, (error, stdout) => resolvePromise({ ok: !error, stdout: String(stdout || "") }));
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
134
168
|
function normalizePid(value) {
|
|
135
169
|
const parsed = Number(value);
|
|
136
170
|
return Number.isInteger(parsed) && parsed > 0 ? parsed : 0;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { BridgeError } from "./errors.mjs";
|
|
3
|
+
import { processOutcomeUnknownAfterSpawn } from "./process-result-projection.mjs";
|
|
4
|
+
|
|
5
|
+
/** @param {unknown} error @param {boolean} nonReplayable */
|
|
6
|
+
export function processPreSpawnFailure(error, nonReplayable) {
|
|
7
|
+
if (!nonReplayable || error instanceof BridgeError) return error;
|
|
8
|
+
return new BridgeError("execution_failed", "process failed before spawn", {
|
|
9
|
+
cause: error instanceof Error ? error : undefined,
|
|
10
|
+
retryable: false,
|
|
11
|
+
details: { reason: "process_failed_before_spawn" },
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** @param {boolean} nonReplayable @param {AbortSignal | undefined} signal */
|
|
16
|
+
export function processCancellationFailure(nonReplayable, signal) {
|
|
17
|
+
const reason = signal?.reason;
|
|
18
|
+
const code = reason instanceof BridgeError && reason.code === "timeout" ? "timeout" : "cancelled";
|
|
19
|
+
if (nonReplayable) return processOutcomeUnknownAfterSpawn(code);
|
|
20
|
+
return new BridgeError(code, reason instanceof Error ? reason.message : "tool call cancelled", {
|
|
21
|
+
retryable: false,
|
|
22
|
+
cause: reason instanceof Error ? reason : undefined,
|
|
23
|
+
details: { side_effects_started: true, termination_requested: true, effect_settlement: "pending" },
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** @param {boolean} nonReplayable @param {number} timeoutMs */
|
|
28
|
+
export function processTimeoutFailure(nonReplayable, timeoutMs) {
|
|
29
|
+
if (nonReplayable) return processOutcomeUnknownAfterSpawn("timeout");
|
|
30
|
+
return new BridgeError("timeout", `command timed out after ${timeoutMs}ms`, {
|
|
31
|
+
retryable: false,
|
|
32
|
+
details: { side_effects_started: true, termination_requested: true, effect_settlement: "pending" },
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** @param {boolean} nonReplayable @param {string} trigger @param {unknown} fallback @param {Record<string, unknown>} [details] */
|
|
37
|
+
export function processPostSpawnFailure(nonReplayable, trigger, fallback, details = {}) {
|
|
38
|
+
return nonReplayable ? processOutcomeUnknownAfterSpawn(trigger, details) : fallback;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** @param {boolean} nonReplayable @param {unknown} error @param {boolean} started */
|
|
42
|
+
export function processChildErrorFailure(nonReplayable, error, started) {
|
|
43
|
+
if (!nonReplayable) return error;
|
|
44
|
+
if (started) return processOutcomeUnknownAfterSpawn("process_error");
|
|
45
|
+
return new BridgeError("execution_failed", "process failed before spawn", {
|
|
46
|
+
cause: error instanceof Error ? error : undefined,
|
|
47
|
+
retryable: false,
|
|
48
|
+
details: { reason: "process_failed_before_spawn" },
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
3
|
import { MCP_TEXT_PROJECTION_KEY } from "../shared/result-projection.mjs";
|
|
4
|
+
import { BridgeError } from "./errors.mjs";
|
|
4
5
|
|
|
5
6
|
/** @param {Record<string, any>} value */
|
|
6
7
|
export function publicProcessToolResult(value) {
|
|
@@ -12,6 +13,27 @@ export function publicProcessToolResult(value) {
|
|
|
12
13
|
return { ...value, [MCP_TEXT_PROJECTION_KEY]: summary };
|
|
13
14
|
}
|
|
14
15
|
|
|
16
|
+
/** @param {string} trigger @param {Record<string, unknown>} [details] */
|
|
17
|
+
export function processOutcomeUnknownAfterSpawn(trigger, details = {}) {
|
|
18
|
+
const terminationRequested = trigger === "cancelled" || trigger === "timeout" || trigger === "resource_binding";
|
|
19
|
+
return new BridgeError(
|
|
20
|
+
"execution_failed",
|
|
21
|
+
"command execution may have partially completed; the outcome is unknown. Inspect command effects before retrying.",
|
|
22
|
+
{
|
|
23
|
+
expose: true,
|
|
24
|
+
retryable: false,
|
|
25
|
+
details: {
|
|
26
|
+
...details,
|
|
27
|
+
reason: "process_outcome_unknown_after_spawn",
|
|
28
|
+
trigger: String(trigger || "unknown").slice(0, 64),
|
|
29
|
+
side_effects_started: "unknown",
|
|
30
|
+
termination_requested: terminationRequested,
|
|
31
|
+
effect_settlement: terminationRequested ? "pending" : "unknown",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
15
37
|
/** @param {Record<string, any>} result */
|
|
16
38
|
export function processFailureMessage(result) {
|
|
17
39
|
const source = String(result.stderr || result.stdout || `process exited ${result.code}`)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export function waitForSpawn(child) {
|
|
2
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
3
|
+
const onSpawn = () => { cleanup(); resolvePromise(); };
|
|
4
|
+
const onError = (error) => { cleanup(); rejectPromise(error); };
|
|
5
|
+
const cleanup = () => {
|
|
6
|
+
child.off("spawn", onSpawn);
|
|
7
|
+
child.off("error", onError);
|
|
8
|
+
};
|
|
9
|
+
child.once("spawn", onSpawn);
|
|
10
|
+
child.once("error", onError);
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function sessionHasOutputAfter(session, stdoutOffset, stderrOffset) {
|
|
15
|
+
return session.stdout.totalBytes > stdoutOffset || session.stderr.totalBytes > stderrOffset;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function notifySessionWaiters(session) {
|
|
19
|
+
for (const waiter of [...session.waiters]) waiter();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function waitForSessionChange(session, waitMs, cancellationCheck, options = {}) {
|
|
23
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
24
|
+
let timer;
|
|
25
|
+
const done = () => {
|
|
26
|
+
cleanup();
|
|
27
|
+
try {
|
|
28
|
+
cancellationCheck();
|
|
29
|
+
resolvePromise();
|
|
30
|
+
} catch (error) {
|
|
31
|
+
rejectPromise(error);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const cleanup = () => {
|
|
35
|
+
if (timer) clearTimeout(timer);
|
|
36
|
+
session.waiters.delete(done);
|
|
37
|
+
};
|
|
38
|
+
session.waiters.add(done);
|
|
39
|
+
timer = setTimeout(done, waitMs);
|
|
40
|
+
if (options.keepAlive !== true) timer.unref?.();
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function boundedErrorMessage(error) {
|
|
45
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
46
|
+
return message.replace(/[\r\n]+/g, " ").slice(0, 4096) || "process failed";
|
|
47
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function beginRemoteProcessSessionActivity(context, guard) {
|
|
2
|
+
if (context?.origin !== "relay" || !guard) return false;
|
|
3
|
+
try { guard.beginActivity?.(); } catch { /* Auxiliary power management must not change session startup. */ }
|
|
4
|
+
return true;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function endRemoteProcessSessionActivity(held, guard) {
|
|
8
|
+
if (!held || !guard) return;
|
|
9
|
+
try { guard.endActivity?.(); } catch { /* Auxiliary power management must not change session settlement. */ }
|
|
10
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { BridgeError } from "./errors.mjs";
|
|
2
|
+
import { createMonotonicDeadline } from "./monotonic-deadline.mjs";
|
|
3
|
+
import { notifySessionWaiters, waitForSessionChange } from "./process-session-events.mjs";
|
|
4
|
+
|
|
5
|
+
export async function terminateProcessSessions({
|
|
6
|
+
sessions,
|
|
7
|
+
matches = () => true,
|
|
8
|
+
terminateTree,
|
|
9
|
+
signal = "SIGKILL",
|
|
10
|
+
waitMs = 5_000,
|
|
11
|
+
mode = "shutdown",
|
|
12
|
+
}) {
|
|
13
|
+
const contract = terminationContract(mode);
|
|
14
|
+
let settledCount = 0;
|
|
15
|
+
let failure = null;
|
|
16
|
+
const settlements = [];
|
|
17
|
+
for (const [id, session] of [...sessions.entries()]) {
|
|
18
|
+
if (!matches(session)) continue;
|
|
19
|
+
notifySessionWaiters(session);
|
|
20
|
+
if (session.closedAt !== null) {
|
|
21
|
+
if (sessions.get(id) === session) sessions.delete(id);
|
|
22
|
+
settledCount += 1;
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (!session.child) {
|
|
26
|
+
failure ||= new Error(contract.missingChild);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
let requested = false;
|
|
30
|
+
try { requested = terminateTree(session.child, signal) === true; }
|
|
31
|
+
catch { /* The retained session handle makes failed termination delivery retryable. */ }
|
|
32
|
+
if (!requested) {
|
|
33
|
+
failure ||= new Error(contract.requestFailure);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
settlements.push(settleProcessSession(sessions, id, session, waitMs).then((settled) => {
|
|
37
|
+
if (settled) settledCount += 1;
|
|
38
|
+
else failure ||= new Error(contract.settlementFailure);
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
await Promise.all(settlements);
|
|
42
|
+
if (failure) {
|
|
43
|
+
throw new BridgeError("unavailable", contract.unavailable, { cause: failure, retryable: true });
|
|
44
|
+
}
|
|
45
|
+
return settledCount;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function settleProcessSession(sessions, id, session, waitMs) {
|
|
49
|
+
const deadline = createMonotonicDeadline(waitMs);
|
|
50
|
+
while (session.closedAt === null && !deadline.expired()) {
|
|
51
|
+
await waitForSessionChange(session, Math.max(1, deadline.remainingMs()), () => {}, { keepAlive: true });
|
|
52
|
+
}
|
|
53
|
+
if (session.closedAt === null) return false;
|
|
54
|
+
if (sessions.get(id) === session) sessions.delete(id);
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function terminationContract(mode) {
|
|
59
|
+
if (mode === "authority_revocation") {
|
|
60
|
+
return {
|
|
61
|
+
missingChild: "process-session termination request could not be delivered",
|
|
62
|
+
requestFailure: "process-session termination request could not be delivered",
|
|
63
|
+
settlementFailure: "process-session termination did not settle before the revocation deadline",
|
|
64
|
+
unavailable: "process-session authority revocation was incomplete; retained revocation must be retried",
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
missingChild: "process-session shutdown has no retained child handle",
|
|
69
|
+
requestFailure: "process-session shutdown request could not be delivered",
|
|
70
|
+
settlementFailure: "process-session shutdown did not settle before the runtime teardown deadline",
|
|
71
|
+
unavailable: "process-session shutdown was incomplete; runtime ownership must be retained",
|
|
72
|
+
};
|
|
73
|
+
}
|