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
|
@@ -3,9 +3,11 @@ import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { readBoundedRegularFileSync } from "../src/local/secure-file.mjs";
|
|
6
|
+
import { createTrustedGitResolver } from "../src/local/trusted-git-executable.mjs";
|
|
6
7
|
|
|
7
8
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
8
9
|
const selfPath = "scripts/privacy-check.mjs";
|
|
10
|
+
const gitExecutable = createTrustedGitResolver({ workspace: root });
|
|
9
11
|
const requestedOptions = new Set(process.argv.slice(2));
|
|
10
12
|
for (const option of requestedOptions) {
|
|
11
13
|
if (option !== "--history") throw new Error(`unknown privacy-check option: ${option}`);
|
|
@@ -68,11 +70,17 @@ process.stderr.write(`privacy check ok (${candidates.length} tracked/unignored f
|
|
|
68
70
|
|
|
69
71
|
function collectCandidateFiles(directory) {
|
|
70
72
|
try {
|
|
71
|
-
|
|
73
|
+
const listed = execFileSync(gitExecutable(), ["-C", directory, "ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
|
|
72
74
|
encoding: "buffer",
|
|
73
75
|
maxBuffer: 32 * 1024 * 1024,
|
|
74
76
|
stdio: ["ignore", "pipe", "ignore"],
|
|
75
|
-
}).toString("utf8").split("\0").filter(Boolean)
|
|
77
|
+
}).toString("utf8").split("\0").filter(Boolean);
|
|
78
|
+
const deleted = new Set(execFileSync(gitExecutable(), ["-C", directory, "ls-files", "-z", "--deleted"], {
|
|
79
|
+
encoding: "buffer",
|
|
80
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
81
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
82
|
+
}).toString("utf8").split("\0").filter(Boolean));
|
|
83
|
+
return listed.filter((relativePath) => !deleted.has(relativePath)).sort();
|
|
76
84
|
} catch {
|
|
77
85
|
const excluded = new Set([".git", ".wrangler", "node_modules"]);
|
|
78
86
|
const files = [];
|
|
@@ -98,7 +106,7 @@ function collectCandidateFiles(directory) {
|
|
|
98
106
|
function scanReachableHistory(directory, entries, out) {
|
|
99
107
|
let listing;
|
|
100
108
|
try {
|
|
101
|
-
listing = execFileSync(
|
|
109
|
+
listing = execFileSync(gitExecutable(), ["-C", directory, "rev-list", "--objects", "--all", "-z"], {
|
|
102
110
|
encoding: "buffer",
|
|
103
111
|
maxBuffer: 64 * 1024 * 1024,
|
|
104
112
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -130,7 +138,7 @@ function scanReachableHistory(directory, entries, out) {
|
|
|
130
138
|
const hashes = [...objectPaths.keys()];
|
|
131
139
|
let metadata;
|
|
132
140
|
try {
|
|
133
|
-
metadata = execFileSync(
|
|
141
|
+
metadata = execFileSync(gitExecutable(), ["-C", directory, "cat-file", "--batch-check=%(objectname) %(objecttype) %(objectsize)"], {
|
|
134
142
|
input: `${hashes.join("\n")}\n`,
|
|
135
143
|
encoding: "utf8",
|
|
136
144
|
maxBuffer: 64 * 1024 * 1024,
|
|
@@ -162,7 +170,7 @@ function scanReachableHistory(directory, entries, out) {
|
|
|
162
170
|
}
|
|
163
171
|
let buffer;
|
|
164
172
|
try {
|
|
165
|
-
buffer = execFileSync(
|
|
173
|
+
buffer = execFileSync(gitExecutable(), ["-C", directory, "cat-file", "blob", hash], {
|
|
166
174
|
encoding: "buffer",
|
|
167
175
|
maxBuffer: 6 * 1024 * 1024,
|
|
168
176
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -194,7 +202,7 @@ function scanReachableHistory(directory, entries, out) {
|
|
|
194
202
|
function scanReachableCommitMessages(directory, entries, out) {
|
|
195
203
|
let output;
|
|
196
204
|
try {
|
|
197
|
-
output = execFileSync(
|
|
205
|
+
output = execFileSync(gitExecutable(), ["-C", directory, "log", "--all", "--format=%H%x00%B%x00"], {
|
|
198
206
|
encoding: "buffer",
|
|
199
207
|
maxBuffer: 64 * 1024 * 1024,
|
|
200
208
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { spawnSync } from "node:child_process";
|
|
5
|
+
import { readBoundedRegularFileSync } from "../src/local/secure-file.mjs";
|
|
6
|
+
import { normalizePackRecord } from "./release-acceptance.mjs";
|
|
7
|
+
import { parseReleaseVersion } from "./release-channel.mjs";
|
|
8
|
+
import { nestedNpmEnvironment } from "../src/local/npm-environment.mjs";
|
|
9
|
+
import { releaseCommandFailure } from "./release-diagnostic.mjs";
|
|
10
|
+
|
|
11
|
+
const MAX_FILE_BYTES = 16 * 1024 * 1024;
|
|
12
|
+
const MAX_TOTAL_BYTES = 96 * 1024 * 1024;
|
|
13
|
+
const VERSION_PLACEHOLDER = "<machine-bridge-release-version>";
|
|
14
|
+
|
|
15
|
+
export function computePromotionContentDigest(root, options = {}) {
|
|
16
|
+
const pkg = options.packageJson || JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
|
|
17
|
+
const version = parseReleaseVersion(pkg.version).raw;
|
|
18
|
+
const record = options.packRecord || packageDryRun(root, pkg.name, options.npmCli);
|
|
19
|
+
if (!Array.isArray(record.files) || record.files.length < 1) throw new Error("npm pack dry-run omitted the package file inventory");
|
|
20
|
+
const entries = record.files.map((item) => ({
|
|
21
|
+
path: String(item?.path || ""),
|
|
22
|
+
mode: normalizePackMode(item?.mode),
|
|
23
|
+
})).sort((left, right) => left.path.localeCompare(right.path));
|
|
24
|
+
if (!entries.length || entries.some((entry) => entry.path.startsWith("/") || entry.path.includes("\\") || entry.path.split("/").includes(".."))) {
|
|
25
|
+
throw new Error("npm pack dry-run returned an invalid package path");
|
|
26
|
+
}
|
|
27
|
+
for (let index = 1; index < entries.length; index += 1) {
|
|
28
|
+
if (entries[index - 1].path === entries[index].path) throw new Error(`npm pack dry-run returned duplicate path: ${entries[index].path}`);
|
|
29
|
+
}
|
|
30
|
+
const hash = createHash("sha256");
|
|
31
|
+
let total = 0;
|
|
32
|
+
for (const entry of entries) {
|
|
33
|
+
const relative = entry.path;
|
|
34
|
+
const bytes = readBoundedRegularFileSync(join(root, relative), MAX_FILE_BYTES, `promotion package file ${relative}`, { verifyPathIdentity: true });
|
|
35
|
+
total += bytes.length;
|
|
36
|
+
if (total > MAX_TOTAL_BYTES) throw new Error(`promotion package content exceeds ${MAX_TOTAL_BYTES} bytes`);
|
|
37
|
+
const normalized = normalizeReleaseMetadata(relative, bytes, version);
|
|
38
|
+
hash.update(`${relative}\0${entry.mode}\0${normalized.length}\0`, "utf8");
|
|
39
|
+
hash.update(normalized);
|
|
40
|
+
hash.update("\0", "utf8");
|
|
41
|
+
}
|
|
42
|
+
return hash.digest("hex");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
function normalizePackMode(value) {
|
|
47
|
+
const numeric = Number(value);
|
|
48
|
+
const mode = Number.isSafeInteger(numeric) ? numeric & 0o777 : NaN;
|
|
49
|
+
if (![0o644, 0o755].includes(mode)) throw new Error(`npm pack dry-run returned an invalid file mode: ${String(value)}`);
|
|
50
|
+
return mode.toString(8).padStart(4, "0");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function packageDryRun(root, packageName, npmCli = process.env.npm_execpath) {
|
|
54
|
+
if (!npmCli) throw new Error("promotion digest must run through npm so npm_execpath is available");
|
|
55
|
+
const result = spawnSync(process.execPath, [
|
|
56
|
+
npmCli,
|
|
57
|
+
"pack",
|
|
58
|
+
"--workspaces=false",
|
|
59
|
+
"--global=false",
|
|
60
|
+
"--prefix", root,
|
|
61
|
+
"--ignore-scripts",
|
|
62
|
+
"--silent",
|
|
63
|
+
"--dry-run",
|
|
64
|
+
"--json",
|
|
65
|
+
], {
|
|
66
|
+
cwd: root,
|
|
67
|
+
encoding: "utf8",
|
|
68
|
+
env: nestedNpmEnvironment(process.env),
|
|
69
|
+
timeout: 5 * 60 * 1000,
|
|
70
|
+
killSignal: "SIGKILL",
|
|
71
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
72
|
+
windowsHide: true,
|
|
73
|
+
});
|
|
74
|
+
if (result.error || result.status !== 0) throw new Error(releaseCommandFailure("npm", ["pack"], result));
|
|
75
|
+
let parsed;
|
|
76
|
+
try { parsed = JSON.parse(result.stdout); } catch { throw new Error("npm pack dry-run did not return valid JSON"); }
|
|
77
|
+
const record = normalizePackRecord(parsed, packageName);
|
|
78
|
+
if (!record) throw new Error("npm pack dry-run did not return package metadata");
|
|
79
|
+
return record;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function normalizeReleaseMetadata(relative, bytes, version) {
|
|
83
|
+
if (relative === "package.json") {
|
|
84
|
+
const value = parseJson(bytes, relative);
|
|
85
|
+
value.version = VERSION_PLACEHOLDER;
|
|
86
|
+
return canonicalJson(value);
|
|
87
|
+
}
|
|
88
|
+
if (relative === "package-lock.json") {
|
|
89
|
+
const value = parseJson(bytes, relative);
|
|
90
|
+
value.version = VERSION_PLACEHOLDER;
|
|
91
|
+
if (value.packages?.[""] && typeof value.packages[""] === "object") value.packages[""].version = VERSION_PLACEHOLDER;
|
|
92
|
+
return canonicalJson(value);
|
|
93
|
+
}
|
|
94
|
+
if (relative === "browser-extension/manifest.json") {
|
|
95
|
+
const value = parseJson(bytes, relative);
|
|
96
|
+
value.version = "0.0.0";
|
|
97
|
+
value.version_name = VERSION_PLACEHOLDER;
|
|
98
|
+
return canonicalJson(value);
|
|
99
|
+
}
|
|
100
|
+
if (relative === "src/worker/index.ts") {
|
|
101
|
+
const text = bytes.toString("utf8");
|
|
102
|
+
const normalized = text.replace(/const SERVER_VERSION = "[^"]+";/, `const SERVER_VERSION = "${VERSION_PLACEHOLDER}";`);
|
|
103
|
+
if (normalized === text && !text.includes(`const SERVER_VERSION = "${VERSION_PLACEHOLDER}";`)) {
|
|
104
|
+
throw new Error("Worker source omitted the synchronized SERVER_VERSION constant");
|
|
105
|
+
}
|
|
106
|
+
return Buffer.from(normalized, "utf8");
|
|
107
|
+
}
|
|
108
|
+
if (relative === "CHANGELOG.md") {
|
|
109
|
+
return Buffer.from(bytes.toString("utf8").split(version).join(VERSION_PLACEHOLDER), "utf8");
|
|
110
|
+
}
|
|
111
|
+
return bytes;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function parseJson(bytes, label) {
|
|
115
|
+
try { return JSON.parse(bytes.toString("utf8")); } catch (error) {
|
|
116
|
+
throw new Error(`${label} is not valid JSON: ${error.message}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function canonicalJson(value) {
|
|
121
|
+
return Buffer.from(`${JSON.stringify(sortValue(value))}\n`, "utf8");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function sortValue(value) {
|
|
125
|
+
if (Array.isArray(value)) return value.map(sortValue);
|
|
126
|
+
if (!value || typeof value !== "object") return value;
|
|
127
|
+
return Object.fromEntries(Object.keys(value).sort().map((key) => [key, sortValue(value[key])]));
|
|
128
|
+
}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
import { spawnSync } from "node:child_process";
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
7
|
+
import { parseReleaseVersion, requiresSoakForStable } from "./release-channel.mjs";
|
|
8
|
+
import { verifyCurrentStableSoak } from "./release-soak.mjs";
|
|
9
|
+
import { nestedNpmEnvironment } from "../src/local/npm-environment.mjs";
|
|
10
|
+
import { createHardenedNpmSession } from "./hardened-npm-session.mjs";
|
|
11
|
+
import { normalizePackRecord, verifyCurrentReleaseAcceptance } from "./release-acceptance.mjs";
|
|
12
|
+
import { stageAcceptedCandidateTarball } from "./accepted-candidate-tarball.mjs";
|
|
13
|
+
import { readPublishedNpmPrereleaseIfPresent } from "./published-release.mjs";
|
|
14
|
+
import { isTransientNetworkFailure } from "./network-retry.mjs";
|
|
15
|
+
import { releaseCommandFailure, releaseDiagnosticEvent } from "./release-diagnostic.mjs";
|
|
16
|
+
|
|
17
|
+
const root = resolve(fileURLToPath(new URL("..", import.meta.url)));
|
|
18
|
+
|
|
19
|
+
export function npmPublishArguments(version, mode) {
|
|
20
|
+
const parsed = parseReleaseVersion(version);
|
|
21
|
+
if (mode === "prerelease" && !parsed.prerelease) throw new Error("prerelease publication requires a dev, beta, or rc version");
|
|
22
|
+
if (mode === "stable" && parsed.prerelease) throw new Error("stable publication requires a version without a prerelease suffix");
|
|
23
|
+
if (!new Set(["prerelease", "stable"]).has(mode)) throw new Error("publication mode must be prerelease or stable");
|
|
24
|
+
return ["publish", "--dry-run=false", "--workspaces=false", "--global=false", "--ignore-scripts=true", "--if-present=false", "--access=public", "--tag", parsed.npmTag];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function publishCurrentNpmPackage(repositoryRoot, mode, options = {}) {
|
|
28
|
+
const repository = resolve(repositoryRoot);
|
|
29
|
+
const pkg = JSON.parse(readFileSync(resolve(repository, "package.json"), "utf8"));
|
|
30
|
+
const parsed = parseReleaseVersion(pkg.version);
|
|
31
|
+
const publishArgs = npmPublishArguments(parsed.raw, mode);
|
|
32
|
+
const explicitNpmCli = String(options.npmCli || "").trim();
|
|
33
|
+
const lifecycleNpmCli = String(options.lifecycleNpmCli || process.env.npm_execpath || explicitNpmCli).trim();
|
|
34
|
+
if (!lifecycleNpmCli) throw new Error("npm publication must run through npm so npm_execpath is available");
|
|
35
|
+
if (!parsed.prerelease && requiresSoakForStable(parsed.raw)) {
|
|
36
|
+
verifyCurrentStableSoak(repository, { npmCli: lifecycleNpmCli });
|
|
37
|
+
}
|
|
38
|
+
const acceptance = options.acceptance || verifyCurrentReleaseAcceptance(repository, {
|
|
39
|
+
npmCli: lifecycleNpmCli,
|
|
40
|
+
env: options.env || process.env,
|
|
41
|
+
});
|
|
42
|
+
if (acceptance.required !== true) throw new Error("npm publication requires current local candidate acceptance");
|
|
43
|
+
const prepareCandidate = options.prepareCandidate || stageAcceptedCandidateTarball;
|
|
44
|
+
const candidate = prepareCandidate(repository, acceptance, options);
|
|
45
|
+
if (!candidate?.path) throw new Error("npm publication candidate tarball path is missing");
|
|
46
|
+
|
|
47
|
+
const createSession = options.createSession || createHardenedNpmSession;
|
|
48
|
+
const run = options.run || spawnSync;
|
|
49
|
+
const processOptions = {
|
|
50
|
+
cwd: repository,
|
|
51
|
+
encoding: "utf8",
|
|
52
|
+
env: nestedNpmEnvironment(options.env || process.env),
|
|
53
|
+
stdio: options.capture ? "pipe" : "inherit",
|
|
54
|
+
timeout: 10 * 60 * 1000,
|
|
55
|
+
killSignal: "SIGKILL",
|
|
56
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
57
|
+
windowsHide: true,
|
|
58
|
+
};
|
|
59
|
+
let session = null;
|
|
60
|
+
let publication = null;
|
|
61
|
+
let primaryError = null;
|
|
62
|
+
try {
|
|
63
|
+
session = explicitNpmCli ? null : await createSession(options);
|
|
64
|
+
const npmCli = explicitNpmCli || session.cli;
|
|
65
|
+
runNpmStage(run, npmCli, [
|
|
66
|
+
"run", "--dry-run=false", "--workspaces=false", "--global=false", "--ignore-scripts=false",
|
|
67
|
+
"--if-present=false", "--tag", parsed.npmTag, "--prefix", repository, "prepublishOnly",
|
|
68
|
+
], processOptions, "npm prepublication verification");
|
|
69
|
+
const preflightArgs = [
|
|
70
|
+
publishArgs[0], candidate.path,
|
|
71
|
+
...publishArgs.slice(1).map((value) => value === "--dry-run=false" ? "--dry-run=true" : value),
|
|
72
|
+
"--json", "--prefix", repository,
|
|
73
|
+
];
|
|
74
|
+
const preflight = runNpmStage(run, npmCli, preflightArgs, { ...processOptions, stdio: "pipe" }, "npm publish dry-run");
|
|
75
|
+
validateNpmPublishDryRun(preflight.stdout, acceptance.metadata);
|
|
76
|
+
const readPublished = options.readPublished || ((name, version, tag) => readPublishedNpmPrereleaseIfPresent(
|
|
77
|
+
name, version, tag, { npmCli, env: options.env || process.env },
|
|
78
|
+
));
|
|
79
|
+
const preexisting = await readPublished(acceptance.metadata.package_name, parsed.raw, parsed.npmTag);
|
|
80
|
+
if (preexisting) {
|
|
81
|
+
assertPublishedCandidate(preexisting, acceptance.metadata, parsed.npmTag);
|
|
82
|
+
publication = publicationResult(parsed, acceptance.metadata, { alreadyPublished: true });
|
|
83
|
+
} else {
|
|
84
|
+
let uploadError = null;
|
|
85
|
+
try {
|
|
86
|
+
runNpmStage(run, npmCli, [
|
|
87
|
+
publishArgs[0], candidate.path, ...publishArgs.slice(1), "--prefix", repository,
|
|
88
|
+
], processOptions, "npm publish");
|
|
89
|
+
} catch (error) {
|
|
90
|
+
uploadError = error;
|
|
91
|
+
}
|
|
92
|
+
let reconciled = null;
|
|
93
|
+
let reconciliationError = null;
|
|
94
|
+
try {
|
|
95
|
+
reconciled = await waitForPublishedCandidate({
|
|
96
|
+
readPublished,
|
|
97
|
+
packageName: acceptance.metadata.package_name,
|
|
98
|
+
version: parsed.raw,
|
|
99
|
+
tag: parsed.npmTag,
|
|
100
|
+
metadata: acceptance.metadata,
|
|
101
|
+
attempts: options.reconciliationAttempts,
|
|
102
|
+
wait: options.wait,
|
|
103
|
+
});
|
|
104
|
+
} catch (error) {
|
|
105
|
+
reconciliationError = error;
|
|
106
|
+
}
|
|
107
|
+
if (reconciled) {
|
|
108
|
+
publication = publicationResult(parsed, acceptance.metadata, {
|
|
109
|
+
recovered: Boolean(uploadError),
|
|
110
|
+
recoveryWarning: uploadError
|
|
111
|
+
? "npm upload returned an error, but the registry now exposes the exact accepted bytes"
|
|
112
|
+
: "",
|
|
113
|
+
});
|
|
114
|
+
} else if (uploadError && reconciliationError) {
|
|
115
|
+
throw new AggregateError(
|
|
116
|
+
[uploadError, reconciliationError],
|
|
117
|
+
"npm publication outcome is ambiguous because upload and registry reconciliation both failed",
|
|
118
|
+
);
|
|
119
|
+
} else if (uploadError) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
"npm publication outcome is ambiguous: the upload command returned an error and the exact version is not yet visible; do not retry until registry state is checked",
|
|
122
|
+
{ cause: uploadError },
|
|
123
|
+
);
|
|
124
|
+
} else if (reconciliationError) {
|
|
125
|
+
throw new Error(
|
|
126
|
+
"npm upload returned success, but registry identity verification failed; publication state is ambiguous and must be checked before retrying",
|
|
127
|
+
{ cause: reconciliationError },
|
|
128
|
+
);
|
|
129
|
+
} else {
|
|
130
|
+
throw new Error("npm upload returned success, but the exact accepted version is not yet visible in the registry; do not retry until registry state is checked");
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
} catch (error) {
|
|
134
|
+
primaryError = error;
|
|
135
|
+
}
|
|
136
|
+
const cleanupErrors = disposePublicationResources([session, candidate]);
|
|
137
|
+
if (primaryError) {
|
|
138
|
+
if (cleanupErrors.length) {
|
|
139
|
+
throw new AggregateError(
|
|
140
|
+
[primaryError, ...cleanupErrors],
|
|
141
|
+
"npm publication failed and temporary cleanup was incomplete",
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
throw primaryError;
|
|
145
|
+
}
|
|
146
|
+
if (cleanupErrors.length) publication.cleanupWarning = cleanupErrors.map(boundedCleanupWarning).join("; ").slice(0, 600);
|
|
147
|
+
return publication;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function disposePublicationResources(resources) {
|
|
151
|
+
const errors = [];
|
|
152
|
+
for (const resource of resources) {
|
|
153
|
+
try { resource?.dispose?.(); } catch (error) { errors.push(error); }
|
|
154
|
+
}
|
|
155
|
+
return errors;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function validateNpmPublishDryRun(stdout, metadata) {
|
|
159
|
+
let value;
|
|
160
|
+
try { value = JSON.parse(String(stdout || "")); }
|
|
161
|
+
catch { throw new Error("npm publish dry-run did not return valid JSON"); }
|
|
162
|
+
const record = normalizePackRecord(value, metadata.package_name);
|
|
163
|
+
if (!record) throw new Error("npm publish dry-run did not return package metadata");
|
|
164
|
+
const expected = {
|
|
165
|
+
name: metadata.package_name,
|
|
166
|
+
version: metadata.package_version,
|
|
167
|
+
filename: metadata.filename,
|
|
168
|
+
shasum: metadata.shasum,
|
|
169
|
+
integrity: metadata.integrity,
|
|
170
|
+
};
|
|
171
|
+
for (const [key, expectedValue] of Object.entries(expected)) {
|
|
172
|
+
if (String(record[key] || "") !== expectedValue) {
|
|
173
|
+
throw new Error(`npm publish dry-run ${key} does not match the accepted candidate`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return Object.freeze(expected);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function runNpmStage(run, npmCli, args, options, label) {
|
|
180
|
+
const result = run(process.execPath, [npmCli, ...args], options);
|
|
181
|
+
if (result.error || result.status !== 0) {
|
|
182
|
+
const error = new Error(`${label} failed: ${releaseCommandFailure("npm", args, result, { maxChars: 1200 })}`, result.error ? { cause: result.error } : undefined);
|
|
183
|
+
error.transient = isTransientNetworkFailure(result);
|
|
184
|
+
error.commandStatus = result.status;
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
187
|
+
return result;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function publicationResult(parsed, metadata, options = {}) {
|
|
191
|
+
return {
|
|
192
|
+
version: parsed.raw,
|
|
193
|
+
tag: parsed.npmTag,
|
|
194
|
+
shasum: metadata.shasum,
|
|
195
|
+
integrity: metadata.integrity,
|
|
196
|
+
alreadyPublished: options.alreadyPublished === true,
|
|
197
|
+
recovered: options.recovered === true,
|
|
198
|
+
recoveryWarning: String(options.recoveryWarning || ""),
|
|
199
|
+
cleanupWarning: "",
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async function waitForPublishedCandidate({
|
|
204
|
+
readPublished, packageName, version, tag, metadata, attempts = 5, wait = defaultPublicationWait,
|
|
205
|
+
}) {
|
|
206
|
+
const maximumAttempts = Number.isSafeInteger(Number(attempts))
|
|
207
|
+
? Math.min(Math.max(Number(attempts), 1), 10)
|
|
208
|
+
: 5;
|
|
209
|
+
const pause = typeof wait === "function" ? wait : defaultPublicationWait;
|
|
210
|
+
let lastTransientError = null;
|
|
211
|
+
for (let attempt = 1; attempt <= maximumAttempts; attempt += 1) {
|
|
212
|
+
try {
|
|
213
|
+
const published = await readPublished(packageName, version, tag);
|
|
214
|
+
if (published) return assertPublishedCandidate(published, metadata, tag);
|
|
215
|
+
} catch (error) {
|
|
216
|
+
const eventuallyConsistent = new Set(["npm_dist_tag_mismatch", "npm_publication_metadata_incomplete"]);
|
|
217
|
+
if (error?.transient !== true && !eventuallyConsistent.has(String(error?.code || ""))) throw error;
|
|
218
|
+
lastTransientError = error;
|
|
219
|
+
}
|
|
220
|
+
if (attempt < maximumAttempts) await pause(attempt);
|
|
221
|
+
}
|
|
222
|
+
if (lastTransientError) throw lastTransientError;
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function assertPublishedCandidate(published, metadata, tag) {
|
|
227
|
+
if (published.version !== metadata.package_version
|
|
228
|
+
|| published.shasum !== metadata.shasum
|
|
229
|
+
|| published.integrity !== metadata.integrity
|
|
230
|
+
|| published.distTag !== tag) {
|
|
231
|
+
throw new Error("npm registry version or dist-tag does not match the exact accepted candidate");
|
|
232
|
+
}
|
|
233
|
+
return published;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function defaultPublicationWait(attempt) {
|
|
237
|
+
return new Promise((resolvePromise) => { setTimeout(resolvePromise, Math.min(4_000, attempt * 1_000)); });
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function boundedCleanupWarning(error) {
|
|
241
|
+
return String(error?.message || error || "temporary cleanup failed")
|
|
242
|
+
.replace(/[\r\n\t]+/g, " ")
|
|
243
|
+
.slice(0, 600);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async function main() {
|
|
247
|
+
const mode = process.argv[2] || "";
|
|
248
|
+
const result = await publishCurrentNpmPackage(root, mode);
|
|
249
|
+
if (result.recoveryWarning) console.warn(result.recoveryWarning);
|
|
250
|
+
if (result.cleanupWarning) console.warn(`npm publication succeeded but temporary hardened npm cleanup was incomplete: ${result.cleanupWarning}`);
|
|
251
|
+
const disposition = result.alreadyPublished ? "already matched" : result.recovered ? "reconciled" : "completed";
|
|
252
|
+
console.log(`npm publication ${disposition}: ${result.version} with dist-tag ${result.tag}`);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (process.argv[1] && pathToFileURL(resolve(process.argv[1])).href === import.meta.url) {
|
|
256
|
+
main().catch((error) => {
|
|
257
|
+
console.error(JSON.stringify(releaseDiagnosticEvent("npm.publish.failed", error?.message || error, 1600)));
|
|
258
|
+
process.exitCode = 1;
|
|
259
|
+
});
|
|
260
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { resolveTrustedGithubCli } from "../src/local/trusted-github-cli.mjs";
|
|
2
|
+
import { nestedNpmEnvironment } from "../src/local/npm-environment.mjs";
|
|
3
|
+
import { githubReleaseByTagEndpoint, normalizeGithubReleaseAsset } from "./github-release-asset.mjs";
|
|
4
|
+
import { isTransientNetworkFailure, runNetworkCommand } from "./network-retry.mjs";
|
|
5
|
+
import { releaseCommandFailure } from "./release-diagnostic.mjs";
|
|
6
|
+
|
|
7
|
+
export function readPublishedNpmPrereleaseIfPresent(name, version, distTag, options = {}) {
|
|
8
|
+
try {
|
|
9
|
+
return readPublishedNpmPrerelease(name, version, distTag, options);
|
|
10
|
+
} catch (error) {
|
|
11
|
+
if (error?.code === "npm_version_not_found") return null;
|
|
12
|
+
throw error;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function readPublishedNpmPrerelease(name, version, distTag, options = {}) {
|
|
17
|
+
const run = options.run || ((args) => runNpmView(args, options));
|
|
18
|
+
const actualVersion = run([`${name}@${version}`, "version", "--json"]);
|
|
19
|
+
const integrity = run([`${name}@${version}`, "dist.integrity", "--json"]);
|
|
20
|
+
const shasum = run([`${name}@${version}`, "dist.shasum", "--json"]);
|
|
21
|
+
const tags = run([name, "dist-tags", "--json"]);
|
|
22
|
+
const times = run([name, "time", "--json"]);
|
|
23
|
+
return normalizePublishedNpmPrerelease({
|
|
24
|
+
requestedVersion: version,
|
|
25
|
+
requestedTag: distTag,
|
|
26
|
+
actualVersion,
|
|
27
|
+
integrity,
|
|
28
|
+
shasum,
|
|
29
|
+
tags,
|
|
30
|
+
times,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function normalizePublishedNpmPrerelease(value) {
|
|
35
|
+
const actualVersion = unwrapNpmJsonValue(value.actualVersion);
|
|
36
|
+
const tags = unwrapNpmJsonValue(value.tags);
|
|
37
|
+
const times = unwrapNpmJsonValue(value.times);
|
|
38
|
+
const version = String(actualVersion || "");
|
|
39
|
+
const requestedVersion = String(value.requestedVersion || "");
|
|
40
|
+
const distTag = String(value.requestedTag || "");
|
|
41
|
+
const integrity = String(unwrapNpmJsonValue(value.integrity) || "");
|
|
42
|
+
const shasum = String(unwrapNpmJsonValue(value.shasum) || "");
|
|
43
|
+
const publishedAt = String(times?.[requestedVersion] || "");
|
|
44
|
+
if (version !== requestedVersion) throw publicationMetadataError("npm registry returned another prerelease version", "npm_version_mismatch");
|
|
45
|
+
if (tags?.[distTag] !== requestedVersion) {
|
|
46
|
+
throw publicationMetadataError(`npm dist-tag ${distTag} does not point to ${requestedVersion}`, "npm_dist_tag_mismatch");
|
|
47
|
+
}
|
|
48
|
+
if (!/^sha512-[A-Za-z0-9+/]+={0,2}$/.test(integrity)) throw publicationMetadataError("npm prerelease integrity is invalid", "npm_integrity_invalid");
|
|
49
|
+
if (!/^[0-9a-f]{40}$/.test(shasum)) throw publicationMetadataError("npm prerelease SHA-1 is invalid", "npm_shasum_invalid");
|
|
50
|
+
if (!Number.isFinite(Date.parse(publishedAt))) {
|
|
51
|
+
throw publicationMetadataError("npm prerelease publication timestamp is invalid", "npm_publication_metadata_incomplete");
|
|
52
|
+
}
|
|
53
|
+
return Object.freeze({ version, integrity, shasum, distTag, publishedAt: new Date(Date.parse(publishedAt)).toISOString() });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function publicationMetadataError(message, code) {
|
|
57
|
+
const error = new Error(message);
|
|
58
|
+
error.code = code;
|
|
59
|
+
return error;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function unwrapNpmJsonValue(value) {
|
|
63
|
+
return Array.isArray(value) && value.length === 1 ? value[0] : value;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function readGithubPrerelease(version, options = {}) {
|
|
67
|
+
const run = options.run || runGh;
|
|
68
|
+
const tag = `v${version}`;
|
|
69
|
+
const value = run(["release", "view", tag, "--json", "tagName,isPrerelease,publishedAt,assets"]);
|
|
70
|
+
const normalized = normalizeGithubPrerelease(value, version);
|
|
71
|
+
const expectedSha256 = String(options.expectedArtifactSha256 || "");
|
|
72
|
+
if (!expectedSha256) return normalized;
|
|
73
|
+
const assetRun = options.assetRun || runGh;
|
|
74
|
+
const release = assetRun(["api", githubReleaseByTagEndpoint(tag)]);
|
|
75
|
+
const verifiedAsset = normalizeGithubReleaseAsset(release, {
|
|
76
|
+
tag,
|
|
77
|
+
assetName: normalized.asset.name,
|
|
78
|
+
expectedSha256,
|
|
79
|
+
});
|
|
80
|
+
if (verifiedAsset.size !== normalized.asset.size) {
|
|
81
|
+
throw new Error("GitHub prerelease asset size differs between release metadata and REST digest evidence");
|
|
82
|
+
}
|
|
83
|
+
return Object.freeze({ ...normalized, asset: verifiedAsset });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function normalizeGithubPrerelease(value, version) {
|
|
87
|
+
const tag = String(value?.tagName || "");
|
|
88
|
+
const publishedAt = String(value?.publishedAt || "");
|
|
89
|
+
if (tag !== `v${version}` || value?.isPrerelease !== true) throw new Error("GitHub prerelease is missing or not marked as a prerelease");
|
|
90
|
+
if (!Number.isFinite(Date.parse(publishedAt))) throw new Error("GitHub prerelease publication timestamp is invalid");
|
|
91
|
+
if (!Array.isArray(value?.assets)) throw new Error("GitHub prerelease asset inventory is unavailable");
|
|
92
|
+
const expectedAsset = `machine-bridge-mcp-${version}.tgz`;
|
|
93
|
+
const asset = value.assets.find((entry) => entry && typeof entry === "object" && entry.name === expectedAsset);
|
|
94
|
+
if (!asset) throw new Error(`GitHub prerelease is missing ${expectedAsset}`);
|
|
95
|
+
const size = Number(asset.size);
|
|
96
|
+
if (!Number.isSafeInteger(size) || size < 1) throw new Error("GitHub prerelease package asset size is invalid");
|
|
97
|
+
return Object.freeze({
|
|
98
|
+
tag,
|
|
99
|
+
isPrerelease: true,
|
|
100
|
+
publishedAt: new Date(Date.parse(publishedAt)).toISOString(),
|
|
101
|
+
asset: Object.freeze({ name: expectedAsset, size }),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function runNpmView(args, options = {}) {
|
|
106
|
+
const npmCli = options.npmCli || process.env.npm_execpath;
|
|
107
|
+
if (!npmCli) throw new Error("npm registry verification must run through npm");
|
|
108
|
+
const result = runNetworkCommand(process.execPath, [npmCli, "view", "--workspaces=false", "--global=false", ...args], {
|
|
109
|
+
env: nestedNpmEnvironment(options.env || process.env),
|
|
110
|
+
timeoutMs: options.timeoutMs,
|
|
111
|
+
});
|
|
112
|
+
if (result.error || result.status !== 0) {
|
|
113
|
+
const detail = String(result.stderr || result.stdout || result.error?.message || "");
|
|
114
|
+
const error = new Error(releaseCommandFailure("npm", ["view"], result));
|
|
115
|
+
if (/\bE404\b|is not in this registry/i.test(detail)) error.code = "npm_version_not_found";
|
|
116
|
+
else {
|
|
117
|
+
error.code = "npm_registry_query_failed";
|
|
118
|
+
error.transient = isTransientNetworkFailure(result);
|
|
119
|
+
}
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
try { return JSON.parse(result.stdout); } catch { throw new Error(`npm view ${args[0]} returned invalid JSON`); }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function runGh(args) {
|
|
126
|
+
const result = runNetworkCommand(resolveTrustedGithubCli({ workspace: process.cwd() }), args, {
|
|
127
|
+
env: process.env,
|
|
128
|
+
timeoutMs: 120_000,
|
|
129
|
+
});
|
|
130
|
+
if (result.error || result.status !== 0) throw new Error(releaseCommandFailure("gh", args, result));
|
|
131
|
+
try { return JSON.parse(result.stdout); } catch { throw new Error("GitHub prerelease query returned invalid JSON"); }
|
|
132
|
+
}
|