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,43 @@
|
|
|
1
|
+
const DEFAULT_CACHE_MS = 1_000;
|
|
2
|
+
|
|
3
|
+
export function cachedResourceProcessParentSampler(sample, now = Date.now, cacheMs = DEFAULT_CACHE_MS) {
|
|
4
|
+
const ttl = Math.max(0, Number(cacheMs) || 0);
|
|
5
|
+
let sampledAt = null;
|
|
6
|
+
let cached = null;
|
|
7
|
+
return () => {
|
|
8
|
+
const current = Number(now());
|
|
9
|
+
if (cacheFresh(sampledAt, current, ttl)) return cached;
|
|
10
|
+
cached = normalizeSnapshot(sample());
|
|
11
|
+
sampledAt = finishedAt(now, current);
|
|
12
|
+
return cached;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function cachedResourceProcessParentSamplerAsync(sample, now = Date.now, cacheMs = DEFAULT_CACHE_MS) {
|
|
17
|
+
const ttl = Math.max(0, Number(cacheMs) || 0);
|
|
18
|
+
let sampledAt = null;
|
|
19
|
+
let cached = null;
|
|
20
|
+
let inFlight = null;
|
|
21
|
+
return async () => {
|
|
22
|
+
const current = Number(now());
|
|
23
|
+
if (cacheFresh(sampledAt, current, ttl)) return cached;
|
|
24
|
+
if (inFlight) return inFlight;
|
|
25
|
+
inFlight = Promise.resolve().then(() => sample()).then((value) => {
|
|
26
|
+
cached = normalizeSnapshot(value);
|
|
27
|
+
sampledAt = finishedAt(now, current);
|
|
28
|
+
return cached;
|
|
29
|
+
}).finally(() => { inFlight = null; });
|
|
30
|
+
return inFlight;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function normalizeSnapshot(value) {
|
|
35
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
36
|
+
}
|
|
37
|
+
function cacheFresh(sampledAt, current, ttl) {
|
|
38
|
+
return sampledAt !== null && Number.isFinite(current) && current >= sampledAt && current - sampledAt <= ttl;
|
|
39
|
+
}
|
|
40
|
+
function finishedAt(now, fallback) {
|
|
41
|
+
const finished = Number(now());
|
|
42
|
+
return Number.isFinite(finished) ? finished : Number.isFinite(fallback) ? fallback : null;
|
|
43
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { runResourceProbeAsync, runResourceProbeSync } from "./resource-probe-command.mjs";
|
|
2
|
+
|
|
3
|
+
const COMMAND_OPTIONS = { timeoutMs: 3_000, maxOutputBytes: 1024 * 1024 };
|
|
4
|
+
|
|
5
|
+
export function sampleResourceProcessParents(options = {}) {
|
|
6
|
+
const run = options.run || ((command, args) => runResourceProbeSync(command, args, COMMAND_OPTIONS));
|
|
7
|
+
const result = processParentCommand(run);
|
|
8
|
+
return result?.ok ? parseResourceProcessParents(result.stdout) : null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function sampleResourceProcessParentsAsync(options = {}) {
|
|
12
|
+
const run = options.run || ((command, args) => runResourceProbeAsync(command, args, COMMAND_OPTIONS));
|
|
13
|
+
const result = await processParentCommand(run);
|
|
14
|
+
return result?.ok ? parseResourceProcessParents(result.stdout) : null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function parseResourceProcessParents(value) {
|
|
18
|
+
const parents = {};
|
|
19
|
+
for (const line of String(value || "").split(/\r?\n/)) {
|
|
20
|
+
const match = /^\s*([1-9][0-9]*)\s+([0-9]+)\s*$/.exec(line);
|
|
21
|
+
if (!match) continue;
|
|
22
|
+
const pid = Number(match[1]); const parent = Number(match[2]);
|
|
23
|
+
if (Number.isInteger(pid) && pid > 0 && Number.isInteger(parent) && parent >= 0) parents[String(pid)] = parent;
|
|
24
|
+
}
|
|
25
|
+
return Object.keys(parents).length ? parents : null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function processParentCommand(run) {
|
|
29
|
+
return process.platform === "win32"
|
|
30
|
+
? run("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", "Get-CimInstance Win32_Process | ForEach-Object { \"$($_.ProcessId) $($_.ParentProcessId)\" }"])
|
|
31
|
+
: run("ps", ["-axo", "pid=,ppid="]);
|
|
32
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
|
|
3
|
+
const POSIX_NICE = "/usr/bin/nice";
|
|
4
|
+
const BACKGROUND_NICE_INCREMENT = 5;
|
|
5
|
+
const UNBOUNDED_NICE_INCREMENT = 2;
|
|
6
|
+
|
|
7
|
+
export function applyResourceProcessPriority(command, args, request, options = {}) {
|
|
8
|
+
const values = args.map(String);
|
|
9
|
+
if (process.platform === "win32" || request?.heavy !== true) return { command, args: values };
|
|
10
|
+
const increment = request?.priority === "background" ? BACKGROUND_NICE_INCREMENT : request?.unbounded === true ? UNBOUNDED_NICE_INCREMENT : 0;
|
|
11
|
+
if (!increment) return { command, args: values };
|
|
12
|
+
const nice = String(options.niceExecutable || POSIX_NICE);
|
|
13
|
+
if (!existsSync(nice)) return { command, args: values };
|
|
14
|
+
return { command: nice, args: ["-n", String(increment), String(command), ...values] };
|
|
15
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { realpathSync } from "node:fs";
|
|
3
|
+
import { basename, dirname, resolve } from "node:path";
|
|
4
|
+
export function resourceRequestForProject(request, cwd) {
|
|
5
|
+
const result = { ...request };
|
|
6
|
+
if (result.serialize_project !== true) return result;
|
|
7
|
+
const project = normalizeResourceProjectIdentity(canonicalResourceProjectPath(cwd));
|
|
8
|
+
return { ...result, contention_key: resourceProjectContentionKey(result.family, project) };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function resourceProjectContentionKey(family, projectIdentity) {
|
|
12
|
+
const material = `agent-resource-contention-v1\0${family || "unknown"}\0${String(projectIdentity || "")}`;
|
|
13
|
+
return createHash("sha256").update(material).digest("hex").slice(0, 32);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function resourceProjectIdentityHash(projectIdentity) {
|
|
17
|
+
return createHash("sha256").update(String(projectIdentity || "")).digest("hex").slice(0, 24);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function resourceProjectHash(cwd) {
|
|
21
|
+
const identity = normalizeResourceProjectIdentity(canonicalResourceProjectPath(cwd));
|
|
22
|
+
return resourceProjectIdentityHash(identity);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function canonicalResourceProjectPath(cwd) {
|
|
26
|
+
const target = resolve(cwd || process.cwd());
|
|
27
|
+
const missing = [];
|
|
28
|
+
let current = target;
|
|
29
|
+
while (true) {
|
|
30
|
+
try {
|
|
31
|
+
const canonical = realpathSync.native ? realpathSync.native(current) : realpathSync(current);
|
|
32
|
+
return missing.reduceRight((value, name) => resolve(value, name), canonical);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
if (error?.code !== "ENOENT" && error?.code !== "ENOTDIR") throw error;
|
|
35
|
+
const parent = dirname(current);
|
|
36
|
+
if (parent === current) return target;
|
|
37
|
+
missing.push(basename(current));
|
|
38
|
+
current = parent;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function normalizeResourceProjectIdentity(value, platform = process.platform) {
|
|
44
|
+
let text = String(value || "");
|
|
45
|
+
if (platform !== "win32") return text;
|
|
46
|
+
text = text.replaceAll("/", "\\");
|
|
47
|
+
const folded = text.toLowerCase();
|
|
48
|
+
const extendedUnc = "\\\\?\\unc\\";
|
|
49
|
+
const extended = "\\\\?\\";
|
|
50
|
+
if (folded.startsWith(extendedUnc)) text = `\\\\${text.slice(extendedUnc.length)}`;
|
|
51
|
+
else if (folded.startsWith(extended)) text = text.slice(extended.length);
|
|
52
|
+
return text.toLowerCase();
|
|
53
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export function pytestWorkerPlan(argv, text, maximum = 1024) {
|
|
2
|
+
const requested = optionValue(argv, text, ["-n", "--numprocesses"]);
|
|
3
|
+
if (requested === null) return { jobs: 1, elastic: false, unbounded: false };
|
|
4
|
+
if (requested === "0") return { jobs: 1, elastic: false, unbounded: false };
|
|
5
|
+
if (/^\d+$/.test(requested)) return numericPlan(requested, maximum);
|
|
6
|
+
if (!["auto", "logical"].includes(requested.toLowerCase())) return unboundedPlan();
|
|
7
|
+
const cap = optionValue(argv, text, ["--maxprocesses"]);
|
|
8
|
+
return cap !== null && /^\d+$/.test(cap) ? numericPlan(cap, maximum) : unboundedPlan();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function optionValue(argv, text, flags) {
|
|
12
|
+
for (let index = 1; index < argv.length; index += 1) {
|
|
13
|
+
const value = String(argv[index] || "");
|
|
14
|
+
for (const flag of flags) {
|
|
15
|
+
if (value === flag) return String(argv[index + 1] || "").trim();
|
|
16
|
+
if (value.startsWith(`${flag}=`)) return value.slice(flag.length + 1).trim();
|
|
17
|
+
if (flag === "-n" && value.startsWith("-n") && value.length > 2) return value.slice(2).trim();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
if (flags.includes("-n")) {
|
|
21
|
+
const compact = /(?:^|\s)-n([^\s]+)/i.exec(text);
|
|
22
|
+
if (compact) return compact[1];
|
|
23
|
+
}
|
|
24
|
+
const names = flags.map((flag) => flag.replace(/^-+/, "")).join("|");
|
|
25
|
+
const match = new RegExp(`(?:^|\\s)--?(?:${names})[=\\s]+([^\\s]+)`, "i").exec(text);
|
|
26
|
+
return match ? match[1] : null;
|
|
27
|
+
}
|
|
28
|
+
function numericPlan(value, maximum) {
|
|
29
|
+
const parsed = Number(value);
|
|
30
|
+
return Number.isSafeInteger(parsed) && parsed >= 1 && parsed <= maximum
|
|
31
|
+
? { jobs: parsed, elastic: false, unbounded: false } : unboundedPlan();
|
|
32
|
+
}
|
|
33
|
+
function unboundedPlan() { return { jobs: null, elastic: false, unbounded: true }; }
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
const NODE_TARGET_NAMES = new Set(["node", "node.exe"]);
|
|
4
|
+
const CANARY_ENTRY_NAME = "release-oauth-canary.mjs";
|
|
5
|
+
const CANARY_FLAG = "--allow-live-oauth-canary";
|
|
6
|
+
|
|
7
|
+
export function releaseControlCommandIsLight(base, args = []) {
|
|
8
|
+
const head = String(base || "").toLowerCase();
|
|
9
|
+
if (!NODE_TARGET_NAMES.has(head)) return false;
|
|
10
|
+
const values = args.map((value) => String(value));
|
|
11
|
+
if (values.length !== 2 || values[1] !== CANARY_FLAG) return false;
|
|
12
|
+
const entry = values[0];
|
|
13
|
+
return path.isAbsolute(entry) && path.basename(entry).toLowerCase() === CANARY_ENTRY_NAME;
|
|
14
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { constants as fsConstants } from "node:fs";
|
|
2
|
+
import { access, realpath, stat } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
const RELEASE_CONTROL_UNSAFE_ENVIRONMENT_KEYS = new Set([
|
|
6
|
+
"NODE_OPTIONS", "NODE_PATH", "NODE_V8_COVERAGE", "NODE_COMPILE_CACHE", "NODE_ICU_DATA",
|
|
7
|
+
"NODE_DEBUG", "NODE_DEBUG_NATIVE", "NODE_REDIRECT_WARNINGS", "NODE_EXTRA_CA_CERTS", "NODE_PRESERVE_SYMLINKS",
|
|
8
|
+
"NODE_TLS_REJECT_UNAUTHORIZED", "SSL_CERT_FILE", "SSL_CERT_DIR", "SSLKEYLOGFILE", "OPENSSL_CONF",
|
|
9
|
+
"UV_THREADPOOL_SIZE", "LD_PRELOAD", "LD_AUDIT", "LD_LIBRARY_PATH",
|
|
10
|
+
"DYLD_INSERT_LIBRARIES", "DYLD_LIBRARY_PATH", "DYLD_FRAMEWORK_PATH",
|
|
11
|
+
"DYLD_FALLBACK_LIBRARY_PATH", "DYLD_FALLBACK_FRAMEWORK_PATH",
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
export function releaseControlEnvironmentIsTrusted(environment = {}) {
|
|
15
|
+
if (!environment || typeof environment !== "object" || Array.isArray(environment)) return false;
|
|
16
|
+
return !Object.keys(environment).some((key) => RELEASE_CONTROL_UNSAFE_ENVIRONMENT_KEYS.has(String(key).toUpperCase()));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function releaseControlExecutableIsTrusted(command, environment = {}, options = {}) {
|
|
20
|
+
if (!releaseControlEnvironmentIsTrusted(environment)) return false;
|
|
21
|
+
const platform = String(options.platform || process.platform);
|
|
22
|
+
const pathApi = platform === "win32" ? path.win32 : path.posix;
|
|
23
|
+
const cwd = pathApi.resolve(String(options.cwd || "."));
|
|
24
|
+
const lookupOptions = { ...options, cwd, platform, pathApi };
|
|
25
|
+
const invoked = await resolveInvokedExecutable(command, environment, lookupOptions);
|
|
26
|
+
if (!invoked) return false;
|
|
27
|
+
const runtime = await inspectExecutable(options.runtimeExecutable || process.execPath, lookupOptions);
|
|
28
|
+
if (!runtime) return false;
|
|
29
|
+
return samePath(invoked.path, runtime.path, platform);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function resolveInvokedExecutable(command, environment, options) {
|
|
33
|
+
const raw = String(command || "");
|
|
34
|
+
if (!raw) return null;
|
|
35
|
+
const { cwd, platform, pathApi } = options;
|
|
36
|
+
if (pathApi.isAbsolute(raw) || raw.includes("/") || raw.includes("\\")) {
|
|
37
|
+
return inspectExecutable(pathApi.resolve(cwd, raw), options);
|
|
38
|
+
}
|
|
39
|
+
const search = String(environment?.PATH || environment?.Path || "");
|
|
40
|
+
if (!search) return null;
|
|
41
|
+
const roots = search.split(pathApi.delimiter);
|
|
42
|
+
if (platform === "win32") roots.unshift(cwd);
|
|
43
|
+
const names = platform === "win32" ? windowsNames(raw, environment?.PATHEXT, pathApi) : [raw];
|
|
44
|
+
for (const root of roots) {
|
|
45
|
+
const directory = root ? (pathApi.isAbsolute(root) ? pathApi.resolve(root) : pathApi.resolve(cwd, root)) : cwd;
|
|
46
|
+
for (const name of names) {
|
|
47
|
+
const result = await inspectExecutable(pathApi.join(directory, name), options);
|
|
48
|
+
if (result) return result;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function inspectExecutable(candidate, options) {
|
|
55
|
+
try {
|
|
56
|
+
if (options.platform !== "win32") await (options.access || access)(candidate, fsConstants.X_OK);
|
|
57
|
+
const canonical = await (options.realpath || realpath)(candidate);
|
|
58
|
+
const info = await (options.stat || stat)(canonical);
|
|
59
|
+
return info.isFile() ? { path: canonical } : null;
|
|
60
|
+
} catch { return null; }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function windowsNames(command, value, pathApi) {
|
|
64
|
+
const extensions = String(value || ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean);
|
|
65
|
+
return pathApi.extname(command) ? [command] : [command, ...extensions.map((extension) => `${command}${extension.toLowerCase()}`)];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function samePath(left, right, platform) {
|
|
69
|
+
return platform === "win32" ? left.toLowerCase() === right.toLowerCase() : left === right;
|
|
70
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { realpath } from "node:fs/promises";
|
|
2
|
+
import { basename, join, resolve } from "node:path";
|
|
3
|
+
import { packageName, packageRoot, packageVersion } from "./package-identity.mjs";
|
|
4
|
+
import { readOptionalRegularUtf8 } from "./project-metadata.mjs";
|
|
5
|
+
import { releaseControlCommandIsLight } from "./resource-release-control-classification.mjs";
|
|
6
|
+
import { releaseControlExecutableIsTrusted } from "./resource-release-control-executable.mjs";
|
|
7
|
+
|
|
8
|
+
const PACKAGE_BYTES = 1024 * 1024, ENTRY_BYTES = 256 * 1024;
|
|
9
|
+
const CANARY_ENTRY = join("scripts", "release-oauth-canary.mjs");
|
|
10
|
+
|
|
11
|
+
export async function releaseControlWorkspaceForCommand(command, args = [], cwd = "", environment = {}) {
|
|
12
|
+
const base = basename(String(command || "")).toLowerCase();
|
|
13
|
+
const values = args.map((value) => String(value));
|
|
14
|
+
if (!releaseControlCommandIsLight(base, values)) return false;
|
|
15
|
+
if (!await releaseControlExecutableIsTrusted(command, environment, { cwd })) return false;
|
|
16
|
+
if (!await releaseControlRuntimeEntrypointMatches(values[0])) return false;
|
|
17
|
+
return releaseControlWorkspaceMatches(cwd);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function releaseControlWorkspaceMatches(cwd) {
|
|
21
|
+
if (typeof cwd !== "string" || !cwd.trim()) return false;
|
|
22
|
+
try {
|
|
23
|
+
const root = resolve(cwd);
|
|
24
|
+
const packageText = await readOptionalRegularUtf8(join(root, "package.json"), PACKAGE_BYTES);
|
|
25
|
+
if (!packageText) return false;
|
|
26
|
+
const manifest = JSON.parse(packageText);
|
|
27
|
+
if (manifest?.name !== packageName || manifest?.version !== packageVersion) return false;
|
|
28
|
+
const [target, runtime] = await Promise.all([
|
|
29
|
+
readOptionalRegularUtf8(join(root, CANARY_ENTRY), ENTRY_BYTES),
|
|
30
|
+
readOptionalRegularUtf8(join(packageRoot, CANARY_ENTRY), ENTRY_BYTES),
|
|
31
|
+
]);
|
|
32
|
+
return target !== null && target === runtime;
|
|
33
|
+
} catch { return false; }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function releaseControlRuntimeEntrypointMatches(entry) {
|
|
37
|
+
try {
|
|
38
|
+
const [target, runtime] = await Promise.all([realpath(String(entry || "")), realpath(join(packageRoot, CANARY_ENTRY))]);
|
|
39
|
+
return process.platform === "win32" ? target.toLowerCase() === runtime.toLowerCase() : target === runtime;
|
|
40
|
+
} catch { return false; }
|
|
41
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const FAMILY = /^[a-z][a-z0-9-]{0,47}$/;
|
|
2
|
+
const CONTENTION_KEY = /^[a-f0-9]{32}$/;
|
|
3
|
+
const RESOURCE_CLASSES = new Set(["adaptive", "cpu", "io", "mixed", "unbounded"]);
|
|
4
|
+
const PRIORITIES = new Set(["interactive", "ordinary", "background"]);
|
|
5
|
+
|
|
6
|
+
export function validateResourceRequest(request) {
|
|
7
|
+
if (!request || typeof request !== "object" || Array.isArray(request)) throw new Error("resource request is invalid");
|
|
8
|
+
if (!FAMILY.test(String(request.family || "")) || !RESOURCE_CLASSES.has(request.resource_class)
|
|
9
|
+
|| !PRIORITIES.has(request.priority) || request.heavy !== true
|
|
10
|
+
|| typeof request.unbounded !== "boolean" || typeof request.serialize_project !== "boolean") {
|
|
11
|
+
throw new Error("resource request contract is invalid");
|
|
12
|
+
}
|
|
13
|
+
finiteRange(request.cpu, 0, 1024, "cpu");
|
|
14
|
+
finiteRange(request.io, 0, 1024, "io");
|
|
15
|
+
finiteRange(request.memory_mb, 0, 16 * 1024 * 1024, "memory");
|
|
16
|
+
finiteRange(request.disk_reserve_bytes, 0, 1024 ** 5, "disk reservation");
|
|
17
|
+
if (request.compiler_jobs !== null && (!Number.isInteger(request.compiler_jobs) || request.compiler_jobs < 1 || request.compiler_jobs > 1024)) {
|
|
18
|
+
throw new Error("resource request compiler jobs are invalid");
|
|
19
|
+
}
|
|
20
|
+
if (request.contention_key !== null && !CONTENTION_KEY.test(String(request.contention_key || ""))) {
|
|
21
|
+
throw new Error("resource request contention key is invalid");
|
|
22
|
+
}
|
|
23
|
+
return request;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function finiteRange(value, minimum, maximum, label) {
|
|
27
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < minimum || value > maximum) {
|
|
28
|
+
throw new Error(`resource request ${label} is invalid`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { basename } from "node:path";
|
|
2
|
+
const HEAVY_SCRIPT_MARKER = /(?:^|[._-])(?:check|checks|build|test|tests|testing|verify|verification|gate|release|archive|fuzz|coverage|benchmark|bench|smoke)(?:$|[._-])/i;
|
|
3
|
+
const HEAVY_PACKAGE_SEGMENT = /(?:^|[:._-])(?:check|build|test|tests|verify|gate|release|archive|fuzz|coverage|benchmark|bench|smoke)(?:$|[:._-])/i;
|
|
4
|
+
const SCRIPT_EXTENSION = /\.(?:sh|bash|zsh|js|mjs|cjs|py|rb|pl)$/i;
|
|
5
|
+
const PACKAGE_MANAGERS = new Set(["npm", "pnpm", "yarn", "bun"]);
|
|
6
|
+
export function heavyScriptName(value) {
|
|
7
|
+
const raw = String(value || "");
|
|
8
|
+
const name = basename(raw).toLowerCase();
|
|
9
|
+
const scriptLike = raw.includes("/") || raw.includes("\\") || SCRIPT_EXTENSION.test(name);
|
|
10
|
+
return scriptLike && HEAVY_SCRIPT_MARKER.test(name);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function directInterpreterHeavyScript(base, args = []) {
|
|
14
|
+
const node = base === "node" || base === "node.exe";
|
|
15
|
+
const python = /^python(?:3(?:\.\d+)?)?(?:\.exe)?$/.test(base);
|
|
16
|
+
if (!node && !python) return false;
|
|
17
|
+
const values = args.map(String);
|
|
18
|
+
if (python && values.some((value) => ["-c", "-m"].includes(value))) return false;
|
|
19
|
+
if (node && values.some((value) => ["-e", "--eval", "-p", "--print"].includes(value))) return false;
|
|
20
|
+
const operand = values.find((value) => !value.startsWith("-")) || "";
|
|
21
|
+
return Boolean(operand && heavyScriptName(operand));
|
|
22
|
+
}
|
|
23
|
+
export function verificationPlanCommand(base, args = []) {
|
|
24
|
+
const values = args.map(String);
|
|
25
|
+
if ((base === "node" || base === "node.exe") && basename(values.find((value) => !value.startsWith("-")) || "").toLowerCase() === "run-checks.mjs") return true;
|
|
26
|
+
if (!PACKAGE_MANAGERS.has(base)) return false;
|
|
27
|
+
const first = String(values[0] || "").toLowerCase();
|
|
28
|
+
const script = ["run", "run-script"].includes(first) ? String(values[1] || "").toLowerCase() : first;
|
|
29
|
+
return ["check", "check:fast", "check:full", "check:platform"].includes(script);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function packageManagerTokensHeavy(tokens = []) {
|
|
33
|
+
const head = String(tokens[0] || "").toLowerCase();
|
|
34
|
+
if (!PACKAGE_MANAGERS.has(head)) return false;
|
|
35
|
+
const args = tokens.slice(1).map((value) => String(value));
|
|
36
|
+
const first = String(args[0] || "").toLowerCase();
|
|
37
|
+
if (["install", "ci", "build", "test"].includes(first)) return true;
|
|
38
|
+
if (["run", "run-script"].includes(first)) return heavyPackageScriptName(args[1]);
|
|
39
|
+
return ["pnpm", "yarn", "bun"].includes(head) && heavyPackageScriptName(first);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function heavyPackageScriptName(value) {
|
|
43
|
+
return HEAVY_PACKAGE_SEGMENT.test(String(value || "").toLowerCase());
|
|
44
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { basename } from "node:path";
|
|
2
|
+
import { heavyScriptName } from "./resource-script-classification.mjs";
|
|
3
|
+
|
|
4
|
+
export function shellPayload(base, args) {
|
|
5
|
+
if (!["zsh", "bash", "sh", "powershell.exe", "pwsh", "cmd.exe"].includes(base)) return "";
|
|
6
|
+
const index = args.findIndex((entry) => ["-c", "-command", "/c"].includes(entry.toLowerCase()));
|
|
7
|
+
return index >= 0 ? String(args[index + 1] || "") : args.join(" ");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function shellSegments(text) {
|
|
11
|
+
return String(text || "").split(/&&|\|\||[;|()\n]/).map((value) => value.trim()).filter(Boolean);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function commandTokens(segment) {
|
|
15
|
+
const tokens = String(segment).trim().split(/\s+/).filter(Boolean); let index = 0;
|
|
16
|
+
while (index < tokens.length) {
|
|
17
|
+
const token = tokens[index];
|
|
18
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(token) || ["env", "command", "exec", "time"].includes(token)) { index += 1; continue; }
|
|
19
|
+
if (token === "nice") { index += 1; if (tokens[index] === "-n") index += 2; else if (/^-?\d+$/.test(tokens[index] || "")) index += 1; continue; }
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
return tokens.slice(index).map((value, tokenIndex) => tokenIndex ? value : basename(value).toLowerCase());
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function shellCommandHeads(text) {
|
|
26
|
+
return shellSegments(text).map((segment) => commandTokens(segment)[0]).filter(Boolean);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function commandHeadIs(text, base, command) {
|
|
30
|
+
return base === command || shellCommandHeads(text).includes(command);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function commandHeadIn(text, base, commands) {
|
|
34
|
+
return commands.includes(base) || shellCommandHeads(text).some((value) => commands.includes(value));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function pythonModuleHeadIs(text, base, module) {
|
|
38
|
+
if (/^python(?:3(?:\.\d+)?)?$/.test(base)) return String(text).includes(`-m ${module}`);
|
|
39
|
+
return shellSegments(text).some((segment) => pythonModuleTokens(commandTokens(segment), module));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function pythonModuleTokens(tokens, module) {
|
|
43
|
+
return /^python(?:3(?:\.\d+)?)?$/.test(tokens[0] || "") && tokens[1] === "-m" && tokens[2] === module;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function heavyShellScript(args, text) {
|
|
47
|
+
const commandString = args.some((value) => String(value).toLowerCase() === "-c");
|
|
48
|
+
const direct = commandString ? null : args.find((value) => !String(value).startsWith("-"));
|
|
49
|
+
if (direct && heavyScriptName(direct)) return true;
|
|
50
|
+
return shellSegments(text).some((segment) => {
|
|
51
|
+
const tokens = commandTokens(segment);
|
|
52
|
+
if (!tokens.length) return false;
|
|
53
|
+
if (heavyScriptName(tokens[0])) return true;
|
|
54
|
+
if (!["sh", "bash", "zsh"].includes(tokens[0])) return false;
|
|
55
|
+
const script = tokens.slice(1).find((token) => !String(token).startsWith("-"));
|
|
56
|
+
return Boolean(script && heavyScriptName(script));
|
|
57
|
+
});
|
|
58
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { lstatSync, readdirSync, unlinkSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { filesystemIdentity, sameFilesystemIdentity } from "./filesystem-identity.mjs";
|
|
4
|
+
import { inspectProcessInstance } from "./process-identity.mjs";
|
|
5
|
+
|
|
6
|
+
const STAGING = /^\.(?<target>(?<kind>lease|wait)_[a-f0-9]{32}\.json)\.(?<pid>[1-9][0-9]*)\.(?<nonce>[a-f0-9]{16})\.tmp$/;
|
|
7
|
+
const OWNER_STAGING = /^\.owner\.json\.(?<pid>[1-9][0-9]*)\.(?<nonce>[a-f0-9]{16})\.tmp$/;
|
|
8
|
+
const FINAL = Object.freeze({ lease: /^lease_[a-f0-9]{32}\.json$/, wait: /^wait_[a-f0-9]{32}\.json$/ });
|
|
9
|
+
export const RESOURCE_STAGING_BUSY_CODE = "MBM_RESOURCE_STAGING_BUSY";
|
|
10
|
+
|
|
11
|
+
export function recoverResourceTransactionOwnerStaging(dir) {
|
|
12
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
13
|
+
if (!entries.length) return true;
|
|
14
|
+
if (entries.some((entry) => entry.name === "owner.json")) return false;
|
|
15
|
+
if (entries.length !== 1) throw new Error("resource transaction lock contains unexpected owner-publication state");
|
|
16
|
+
const entry = entries[0];
|
|
17
|
+
const match = entry.isFile() ? OWNER_STAGING.exec(entry.name) : null;
|
|
18
|
+
if (!match) throw new Error("resource transaction lock contains unexpected owner-publication state");
|
|
19
|
+
const stagingPath = join(dir, entry.name);
|
|
20
|
+
const staging = inspect(stagingPath, "resource transaction owner staging file");
|
|
21
|
+
if (stagingPublisherMayBeCurrent(Number(match.groups.pid), staging)) return false;
|
|
22
|
+
if (staging.nlink !== 1n) throw new Error("resource transaction owner staging file has unexpected links");
|
|
23
|
+
const current = inspect(stagingPath, "resource transaction owner staging file");
|
|
24
|
+
if (current.nlink !== 1n || !sameFilesystemIdentity(staging.identity, current.identity)) {
|
|
25
|
+
throw new Error("resource transaction owner staging file changed during recovery");
|
|
26
|
+
}
|
|
27
|
+
unlinkSync(stagingPath);
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function recoverResourceDirectoryStaging(dir, entries, kind) {
|
|
32
|
+
const expected = FINAL[kind];
|
|
33
|
+
if (!expected) throw new Error("resource staging kind is invalid");
|
|
34
|
+
let recovered = false;
|
|
35
|
+
for (const entry of entries) {
|
|
36
|
+
if (entry.isFile() && expected.test(entry.name)) continue;
|
|
37
|
+
const match = entry.isFile() ? STAGING.exec(entry.name) : null;
|
|
38
|
+
if (!match || match.groups?.kind !== kind) throw new Error(`resource coordinator ${kind} directory contains an unexpected entry`);
|
|
39
|
+
recoverStagingAlias(dir, entry.name, match.groups.target, Number(match.groups.pid));
|
|
40
|
+
recovered = true;
|
|
41
|
+
}
|
|
42
|
+
return recovered;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function recoverStagingAlias(dir, stagingName, targetName, publisherPid) {
|
|
46
|
+
const stagingPath = join(dir, stagingName);
|
|
47
|
+
const targetPath = join(dir, targetName);
|
|
48
|
+
const staging = inspect(stagingPath, "resource coordinator staging file");
|
|
49
|
+
const target = inspectOptional(targetPath, "resource coordinator published file");
|
|
50
|
+
if (target) {
|
|
51
|
+
const sameObject = sameFilesystemIdentity(staging.identity, target.identity);
|
|
52
|
+
if (sameObject) {
|
|
53
|
+
if (staging.nlink !== 2n || target.nlink !== 2n) throw new Error("resource coordinator committed staging alias has unexpected links");
|
|
54
|
+
verifyPair(stagingPath, targetPath, staging, target, true);
|
|
55
|
+
unlinkSync(stagingPath);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (stagingPublisherMayBeCurrent(publisherPid, staging)) throw Object.assign(new Error("resource coordinator staging file is still owned by a live publisher"), { code: RESOURCE_STAGING_BUSY_CODE });
|
|
59
|
+
if (staging.nlink !== 1n || target.nlink !== 1n) throw new Error("resource coordinator uncommitted replacement staging has unexpected links");
|
|
60
|
+
verifyPair(stagingPath, targetPath, staging, target, false);
|
|
61
|
+
unlinkSync(stagingPath);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (stagingPublisherMayBeCurrent(publisherPid, staging)) throw Object.assign(new Error("resource coordinator staging file is still owned by a live publisher"), { code: RESOURCE_STAGING_BUSY_CODE });
|
|
65
|
+
if (staging.nlink !== 1n) throw new Error("resource coordinator uncommitted staging file has unexpected links");
|
|
66
|
+
const current = inspect(stagingPath, "resource coordinator staging file");
|
|
67
|
+
if (current.nlink !== 1n || !sameFilesystemIdentity(staging.identity, current.identity)) {
|
|
68
|
+
throw new Error("resource coordinator staging file changed during recovery");
|
|
69
|
+
}
|
|
70
|
+
unlinkSync(stagingPath);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function verifyPair(stagingPath, targetPath, staging, target, requireSameObject) {
|
|
74
|
+
const currentStaging = inspect(stagingPath, "resource coordinator staging file");
|
|
75
|
+
const currentTarget = inspect(targetPath, "resource coordinator published file");
|
|
76
|
+
if (currentStaging.nlink !== staging.nlink || currentTarget.nlink !== target.nlink
|
|
77
|
+
|| !sameFilesystemIdentity(staging.identity, currentStaging.identity)
|
|
78
|
+
|| !sameFilesystemIdentity(target.identity, currentTarget.identity)
|
|
79
|
+
|| (requireSameObject && !sameFilesystemIdentity(currentStaging.identity, currentTarget.identity))) {
|
|
80
|
+
throw new Error("resource coordinator staging file changed during recovery");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function inspect(file, label) {
|
|
85
|
+
const info = lstatSync(file, { bigint: true });
|
|
86
|
+
if (info.isSymbolicLink() || !info.isFile()) throw new Error(`${label} must be a regular file`);
|
|
87
|
+
return { nlink: info.nlink, identity: filesystemIdentity(info, label), modified_at: Number(info.mtimeMs) };
|
|
88
|
+
}
|
|
89
|
+
function inspectOptional(file, label) {
|
|
90
|
+
try { return inspect(file, label); }
|
|
91
|
+
catch (error) { if (error?.code === "ENOENT") return null; throw error; }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function stagingPublisherMayBeCurrent(pid, staging) {
|
|
95
|
+
const status = inspectProcessInstance({ pid, startedAt: staging.modified_at });
|
|
96
|
+
return status.alive === true && status.reclaimable !== true;
|
|
97
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const SWIFT_UINT32_MAX = 4_294_967_295;
|
|
2
|
+
|
|
3
|
+
export function swiftJobPlan(argv, maximum = 1024) {
|
|
4
|
+
let plan = null;
|
|
5
|
+
for (let index = 1; index < (argv || []).length; index += 1) {
|
|
6
|
+
const value = String(argv[index] || ""); if (value === "--") break;
|
|
7
|
+
let current = null;
|
|
8
|
+
if (["-j", "--jobs"].includes(value)) {
|
|
9
|
+
if (index + 1 >= argv.length) return invalidPlan();
|
|
10
|
+
current = numericPlan(argv[index + 1], maximum); index += 1;
|
|
11
|
+
} else if (value.startsWith("--jobs=")) {
|
|
12
|
+
const attached = value.slice("--jobs=".length); if (!attached && index + 1 >= argv.length) return invalidPlan(); current = numericPlan(attached || argv[++index], maximum);
|
|
13
|
+
} else if (value.startsWith("-j=")) {
|
|
14
|
+
const attached = value.slice("-j=".length); if (!attached) return invalidPlan(); current = numericPlan(attached, maximum);
|
|
15
|
+
} else if (value.startsWith("-j") && value !== "-j") return invalidPlan();
|
|
16
|
+
if (current?.invalid) return current;
|
|
17
|
+
if (current) plan = current;
|
|
18
|
+
}
|
|
19
|
+
return plan;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function numericPlan(value, maximum) {
|
|
23
|
+
const text = String(value ?? "");
|
|
24
|
+
if (!/^\+?\d+$/.test(text)) return invalidPlan();
|
|
25
|
+
const parsed = Number(text);
|
|
26
|
+
if (!Number.isSafeInteger(parsed) || parsed > SWIFT_UINT32_MAX) return invalidPlan();
|
|
27
|
+
if (parsed === 0) return unboundedPlan();
|
|
28
|
+
return parsed <= maximum ? { jobs: parsed, elastic: false, unbounded: false } : unboundedPlan();
|
|
29
|
+
}
|
|
30
|
+
function invalidPlan() { return { jobs: null, elastic: false, unbounded: false, invalid: true }; }
|
|
31
|
+
function unboundedPlan() { return { jobs: null, elastic: false, unbounded: true }; }
|