machine-bridge-mcp 2.0.0 → 3.0.0-beta.102
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1142 -0
- package/CONTRIBUTING.md +23 -25
- package/GOVERNANCE.md +2 -2
- package/README.md +58 -20
- package/SECURITY.md +161 -120
- package/SUPPORT.md +1 -1
- package/browser-extension/broker-auth.js +83 -0
- package/browser-extension/broker-liveness.js +35 -0
- package/browser-extension/browser-error-boundary.js +96 -0
- package/browser-extension/browser-operations.js +1456 -101
- package/browser-extension/devtools-input.js +195 -34
- package/browser-extension/devtools-observation.js +429 -0
- package/browser-extension/devtools-session.js +48 -0
- package/browser-extension/manifest.json +4 -3
- package/browser-extension/page-automation.js +578 -118
- package/browser-extension/pairing-bootstrap.js +42 -0
- package/browser-extension/pairing.js +28 -12
- package/browser-extension/service-worker.js +126 -106
- package/docs/AGENT_CONTEXT.md +13 -10
- package/docs/ARCHITECTURE.md +159 -67
- package/docs/AUDIT.md +1302 -4
- package/docs/CLIENTS.md +19 -6
- package/docs/COMPUTER_USE.md +297 -0
- package/docs/ENGINEERING.md +72 -13
- package/docs/GETTING_STARTED.md +18 -20
- package/docs/LOCAL_AUTHORIZATION.md +115 -72
- package/docs/LOCAL_AUTOMATION.md +27 -21
- package/docs/LOGGING.md +40 -19
- package/docs/MANAGED_JOBS.md +37 -34
- package/docs/MULTI_ACCOUNT.md +98 -37
- package/docs/OPERATIONS.md +145 -51
- package/docs/OVERVIEW.md +14 -11
- package/docs/POLICY_REFERENCE.md +71 -3
- package/docs/PRIVACY.md +41 -2
- package/docs/PROJECT_STANDARDS.md +29 -6
- package/docs/RELEASING.md +238 -64
- package/docs/TESTING.md +150 -39
- package/docs/THREAT_MODEL.md +164 -67
- package/docs/TOOL_REFERENCE.md +653 -60
- package/docs/UPGRADING.md +78 -18
- package/native/macos/MachineBridgeBackgroundInput.swift +1051 -0
- package/native/macos/MachineBridgeBackgroundInputSmokeFixture.swift +295 -0
- package/native/macos/MachineBridgeTrustBroker.swift +233 -0
- package/package.json +94 -16
- package/scripts/accepted-candidate-tarball.mjs +77 -0
- package/scripts/candidate-runtime-store.mjs +93 -0
- package/scripts/check-plan.mjs +73 -3
- package/scripts/check-runner.mjs +129 -17
- package/scripts/commit-message-check.mjs +5 -2
- package/scripts/consumer-package-security.mjs +293 -0
- package/scripts/coverage-check.mjs +251 -15
- package/scripts/coverage-generation.mjs +41 -0
- package/scripts/coverage-range-merge.mjs +41 -0
- package/scripts/foreground-daemon-recovery.mjs +88 -0
- package/scripts/generate-policy-reference.mjs +28 -5
- package/scripts/generate-tool-reference.mjs +1 -1
- package/scripts/generate-worker-types.mjs +21 -11
- package/scripts/github-backlog.mjs +11 -10
- package/scripts/github-push.mjs +62 -16
- package/scripts/github-release-asset.mjs +58 -0
- package/scripts/github-release.mjs +241 -132
- package/scripts/global-package-installation.mjs +68 -0
- package/scripts/hardened-npm-session.mjs +42 -0
- package/scripts/install-published-prerelease.mjs +198 -0
- package/scripts/local-release-acceptance.mjs +103 -45
- package/scripts/macos-background-input-smoke.mjs +515 -0
- package/scripts/network-retry.mjs +8 -1
- package/scripts/npm-global-prefix.mjs +27 -0
- package/scripts/npm-publication-policy.mjs +17 -0
- package/scripts/official-mcp-conformance.mjs +267 -0
- package/scripts/persistent-activation-process.mjs +101 -0
- package/scripts/prepare-pinned-npm.mjs +12 -69
- package/scripts/prerelease-activation.mjs +160 -0
- package/scripts/privacy-check.mjs +14 -6
- package/scripts/promotion-digest.mjs +128 -0
- package/scripts/publish-npm.mjs +260 -0
- package/scripts/published-release.mjs +132 -0
- package/scripts/release-acceptance.mjs +62 -54
- package/scripts/release-browser-extension-store.mjs +172 -0
- package/scripts/release-candidate-manifest.mjs +52 -0
- package/scripts/release-channel.mjs +106 -0
- package/scripts/release-diagnostic.mjs +41 -0
- package/scripts/release-impact-check.mjs +12 -22
- package/scripts/release-oauth-canary-core.mjs +325 -0
- package/scripts/release-oauth-canary-evidence.mjs +100 -0
- package/scripts/release-oauth-canary.mjs +127 -0
- package/scripts/release-publication-guard.mjs +67 -0
- package/scripts/release-soak.mjs +284 -0
- package/scripts/release-state.mjs +1 -1
- package/scripts/run-checks.mjs +44 -7
- package/scripts/run-worker-dry-run.mjs +21 -0
- package/scripts/sarif-security-gate.mjs +2 -1
- package/scripts/sbom-check.mjs +106 -0
- package/scripts/start-release-candidate.mjs +267 -44
- package/scripts/syntax-check.mjs +36 -14
- package/scripts/verification-generation-guard.mjs +15 -0
- package/scripts/verification-idle-sleep-guard.mjs +37 -0
- package/scripts/verification-state.mjs +108 -0
- package/scripts/wrangler-command-lifecycle.mjs +115 -0
- package/src/local/account-access.mjs +13 -1
- package/src/local/account-admin-response.mjs +92 -0
- package/src/local/account-admin.mjs +52 -19
- package/src/local/agent-context-projection.mjs +26 -7
- package/src/local/agent-context.mjs +88 -49
- package/src/local/agent-skill-discovery.mjs +16 -5
- package/src/local/app-automation-macos-jxa.mjs +350 -0
- package/src/local/app-automation.mjs +873 -163
- package/src/local/application-capability-projection.mjs +24 -0
- package/src/local/authority-context.mjs +106 -0
- package/src/local/autostart-log-maintenance.mjs +36 -0
- package/src/local/browser-bridge-http.mjs +5 -3
- package/src/local/browser-bridge.mjs +109 -31
- package/src/local/browser-broker-auth-http.mjs +67 -0
- package/src/local/browser-broker-auth.mjs +98 -0
- package/src/local/browser-broker-routes.mjs +63 -20
- package/src/local/browser-broker-server.mjs +17 -7
- package/src/local/browser-command.mjs +79 -30
- package/src/local/browser-computer-observation-service.mjs +266 -0
- package/src/local/browser-extension-identity.mjs +50 -0
- package/src/local/browser-extension-path.mjs +22 -0
- package/src/local/browser-extension-protocol.mjs +50 -11
- package/src/local/browser-operation-service.mjs +147 -117
- package/src/local/browser-pairing-grant.mjs +109 -0
- package/src/local/browser-pairing-http.mjs +36 -0
- package/src/local/browser-pairing-launch.mjs +58 -0
- package/src/local/browser-pairing-store.mjs +85 -48
- package/src/local/browser-request-registry.mjs +40 -14
- package/src/local/browser-request-settlement.mjs +78 -0
- package/src/local/browser-resource-input.mjs +74 -0
- package/src/local/browser-trusted-input-health.mjs +63 -0
- package/src/local/call-authority.mjs +21 -0
- package/src/local/call-capacity.mjs +45 -0
- package/src/local/call-registry-drain.mjs +54 -0
- package/src/local/call-registry.mjs +30 -15
- package/src/local/capability-observer.mjs +5 -0
- package/src/local/capability-ranking.mjs +3 -2
- package/src/local/child-process-settlement.mjs +132 -0
- package/src/local/cli-account-admin.mjs +40 -6
- package/src/local/cli-activate.mjs +162 -0
- package/src/local/cli-local-admin.mjs +20 -45
- package/src/local/cli-options.mjs +12 -13
- package/src/local/cli-ready-output.mjs +52 -0
- package/src/local/cli-service.mjs +91 -11
- package/src/local/cli.mjs +232 -150
- package/src/local/computer-use-application-observation.mjs +224 -0
- package/src/local/computer-use-arguments.mjs +270 -0
- package/src/local/computer-use-deadline.mjs +50 -0
- package/src/local/computer-use-dispatch-settlement.mjs +40 -0
- package/src/local/computer-use-expectation.mjs +171 -0
- package/src/local/computer-use-observation.mjs +769 -0
- package/src/local/computer-use-recovery.mjs +82 -0
- package/src/local/computer-use-result-budget.mjs +145 -0
- package/src/local/computer-use-snapshot-store.mjs +67 -0
- package/src/local/computer-use.mjs +2255 -0
- package/src/local/daemon-http-relay-auth.mjs +26 -0
- package/src/local/daemon-http-relay-connection.mjs +298 -0
- package/src/local/daemon-http-relay-request.mjs +64 -0
- package/src/local/daemon-http-relay-sequence.mjs +49 -0
- package/src/local/daemon-process.mjs +40 -7
- package/src/local/delegated-process-sandbox.mjs +148 -0
- package/src/local/device-identity.mjs +156 -49
- package/src/local/device-root-provider.mjs +81 -0
- package/src/local/directory-metadata.mjs +47 -0
- package/src/local/doctor-reporting.mjs +23 -0
- package/src/local/durable-process-spec.mjs +80 -0
- package/src/local/errors.mjs +25 -15
- package/src/local/exclusive-file.mjs +74 -26
- package/src/local/execution-limits.mjs +9 -0
- package/src/local/execution-routing.mjs +276 -0
- package/src/local/execution-surface.mjs +23 -0
- package/src/local/file-mutation-coordinator.mjs +47 -0
- package/src/local/file-snapshot-preservation.mjs +24 -0
- package/src/local/filesystem-identity.mjs +54 -0
- package/src/local/fixed-process-environment.mjs +21 -0
- package/src/local/full-access-test.mjs +31 -13
- package/src/local/git-commit.mjs +69 -0
- package/src/local/git-config-safety.mjs +55 -0
- package/src/local/git-log-parser.mjs +41 -0
- package/src/local/git-metadata-boundary.mjs +77 -0
- package/src/local/git-metadata-tree-safety.mjs +59 -0
- package/src/local/git-operation-state.mjs +19 -0
- package/src/local/git-service.mjs +165 -30
- package/src/local/hardened-npm-download-timeout.mjs +46 -0
- package/src/local/hardened-npm-download.mjs +109 -0
- package/src/local/hardened-npm-extract.mjs +20 -0
- package/src/local/hardened-npm-verification.mjs +104 -0
- package/src/local/hardened-npm.mjs +199 -0
- package/src/local/job-runner.mjs +244 -89
- package/src/local/lifecycle.mjs +6 -0
- package/src/local/log.mjs +19 -45
- package/src/local/macos-background-input.mjs +398 -0
- package/src/local/macos-idle-sleep-assertion.mjs +65 -0
- package/src/local/macos-trust-broker.mjs +404 -0
- package/src/local/managed-job-cancellation.mjs +42 -0
- package/src/local/managed-job-capacity.mjs +16 -0
- package/src/local/managed-job-directory-generation.mjs +60 -0
- package/src/local/managed-job-directory.mjs +75 -0
- package/src/local/managed-job-durable-process.mjs +49 -0
- package/src/local/managed-job-lock.mjs +46 -14
- package/src/local/managed-job-plan.mjs +38 -14
- package/src/local/managed-job-projection.mjs +23 -15
- package/src/local/managed-job-retention.mjs +128 -0
- package/src/local/managed-job-runner-claim.mjs +70 -0
- package/src/local/managed-job-runner.mjs +35 -10
- package/src/local/managed-job-storage.mjs +22 -6
- package/src/local/managed-job-terminal-maintenance.mjs +40 -0
- package/src/local/managed-job-terminal.mjs +150 -0
- package/src/local/managed-jobs.mjs +424 -287
- package/src/local/npm-cli.mjs +42 -0
- package/src/local/npm-environment.mjs +37 -0
- package/src/local/operation-authorization.mjs +68 -324
- package/src/local/operation-risk.mjs +51 -4
- package/src/local/owner-state-lock.mjs +118 -0
- package/src/local/package-identity.mjs +9 -0
- package/src/local/patch.mjs +20 -18
- package/src/local/path-inspection.mjs +23 -0
- package/src/local/policy.mjs +19 -0
- package/src/local/private-toolchain-integrity.mjs +54 -0
- package/src/local/process-execution.mjs +87 -75
- package/src/local/process-foreground-timeout.mjs +50 -0
- package/src/local/process-identity.mjs +41 -7
- package/src/local/process-nonreplayable-settlement.mjs +50 -0
- package/src/local/process-result-projection.mjs +22 -0
- package/src/local/process-session-events.mjs +47 -0
- package/src/local/process-session-remote-activity.mjs +10 -0
- package/src/local/process-session-termination.mjs +73 -0
- package/src/local/process-sessions.mjs +144 -112
- package/src/local/process-tracker.mjs +140 -13
- package/src/local/process-tree-ownership-types.d.ts +37 -0
- package/src/local/process-tree-ownership.mjs +57 -0
- package/src/local/process-tree-signal.mjs +34 -0
- package/src/local/process-tree-snapshot.mjs +77 -0
- package/src/local/process-tree-supervisor.mjs +69 -0
- package/src/local/process-tree.mjs +11 -53
- package/src/local/project-metadata.mjs +19 -6
- package/src/local/relay-call-recovery.mjs +58 -38
- package/src/local/relay-connection-classification.mjs +131 -0
- package/src/local/relay-connection-support.mjs +91 -0
- package/src/local/relay-connection.mjs +179 -272
- package/src/local/relay-diagnostics.mjs +91 -0
- package/src/local/relay-heartbeat.mjs +119 -0
- package/src/local/relay-liveness.mjs +109 -0
- package/src/local/relay-peer-diagnostics.mjs +29 -0
- package/src/local/release-runtime-lock.mjs +18 -0
- package/src/local/remote-activity-idle-sleep-guard.mjs +77 -0
- package/src/local/remote-configuration.mjs +48 -0
- package/src/local/resilient-relay-connection.mjs +178 -0
- package/src/local/resource-admission-diagnostics.mjs +52 -0
- package/src/local/resource-admission-policy.mjs +115 -0
- package/src/local/resource-admission.mjs +339 -0
- package/src/local/resource-build-root.mjs +83 -0
- package/src/local/resource-cargo-concurrency.mjs +38 -0
- package/src/local/resource-cmake-concurrency.mjs +42 -0
- package/src/local/resource-command-concurrency.mjs +37 -0
- package/src/local/resource-command-profile.mjs +176 -0
- package/src/local/resource-coordinator-accounting.mjs +21 -0
- package/src/local/resource-cpu-window.mjs +20 -0
- package/src/local/resource-disk-headroom.mjs +13 -0
- package/src/local/resource-elastic-memory.mjs +19 -0
- package/src/local/resource-elastic-request.mjs +36 -0
- package/src/local/resource-foreground-wait.mjs +29 -0
- package/src/local/resource-go-concurrency.mjs +43 -0
- package/src/local/resource-gradle-concurrency.mjs +57 -0
- package/src/local/resource-host-cache.mjs +39 -0
- package/src/local/resource-host-darwin.mjs +54 -0
- package/src/local/resource-host-linux.mjs +35 -0
- package/src/local/resource-host-sample-file.mjs +23 -0
- package/src/local/resource-host-snapshot.mjs +99 -0
- package/src/local/resource-lease-accounting.mjs +119 -0
- package/src/local/resource-light-command.mjs +6 -0
- package/src/local/resource-make-concurrency.mjs +42 -0
- package/src/local/resource-maven-concurrency.mjs +38 -0
- package/src/local/resource-ninja-command-concurrency.mjs +20 -0
- package/src/local/resource-ninja-concurrency.mjs +28 -0
- package/src/local/resource-operations.mjs +26 -10
- package/src/local/resource-pressure.mjs +67 -0
- package/src/local/resource-probe-command.mjs +35 -0
- package/src/local/resource-process-admission.mjs +59 -0
- package/src/local/resource-process-ancestry-cache.mjs +43 -0
- package/src/local/resource-process-ancestry.mjs +32 -0
- package/src/local/resource-process-priority.mjs +15 -0
- package/src/local/resource-project-key.mjs +53 -0
- package/src/local/resource-pytest-concurrency.mjs +33 -0
- package/src/local/resource-release-control-classification.mjs +14 -0
- package/src/local/resource-release-control-executable.mjs +70 -0
- package/src/local/resource-release-control-workspace.mjs +41 -0
- package/src/local/resource-request-contract.mjs +30 -0
- package/src/local/resource-script-classification.mjs +44 -0
- package/src/local/resource-shell-analysis.mjs +58 -0
- package/src/local/resource-staging-recovery.mjs +97 -0
- package/src/local/resource-swift-concurrency.mjs +31 -0
- package/src/local/resource-transaction-lock.mjs +189 -0
- package/src/local/resource-wait.mjs +48 -0
- package/src/local/resource-waiters.mjs +139 -0
- package/src/local/resource-xcode-command.mjs +7 -0
- package/src/local/resource-xcode-concurrency.mjs +34 -0
- package/src/local/resource-xcode-non-build.mjs +19 -0
- package/src/local/runtime-activation.mjs +700 -0
- package/src/local/runtime-activity-projection.mjs +48 -0
- package/src/local/runtime-capabilities.mjs +48 -15
- package/src/local/runtime-diagnostic-state.mjs +14 -0
- package/src/local/runtime-diagnostics.mjs +40 -13
- package/src/local/runtime-info-projection.mjs +60 -0
- package/src/local/runtime-info-relay-projection.mjs +21 -0
- package/src/local/runtime-paths.mjs +2 -1
- package/src/local/runtime-process-routing.mjs +33 -0
- package/src/local/runtime-relay-connection-options.mjs +44 -0
- package/src/local/runtime-relay-control.mjs +98 -0
- package/src/local/runtime-relay.mjs +26 -50
- package/src/local/runtime-reporting.mjs +37 -21
- package/src/local/runtime-resource-service.mjs +77 -0
- package/src/local/runtime-tool-handlers.mjs +13 -10
- package/src/local/runtime.mjs +244 -208
- package/src/local/secure-file.mjs +73 -20
- package/src/local/security-audit-dispatch.mjs +35 -0
- package/src/local/security-audit-log.mjs +215 -0
- package/src/local/security-audit-state.mjs +139 -0
- package/src/local/security-audit-storage.mjs +135 -0
- package/src/local/security-audit-warning.mjs +31 -0
- package/src/local/security-audit-worker.mjs +87 -0
- package/src/local/service-convergence.mjs +42 -1
- package/src/local/service-definition.mjs +24 -0
- package/src/local/service-environment.mjs +18 -10
- package/src/local/service-owner.mjs +146 -0
- package/src/local/service-ownership.mjs +17 -0
- package/src/local/service-restart-handoff.mjs +64 -0
- package/src/local/service-restart-scheduler.mjs +49 -0
- package/src/local/service-runtime-convergence.mjs +52 -0
- package/src/local/service-runtime.mjs +105 -0
- package/src/local/service-status.mjs +60 -0
- package/src/local/service.mjs +385 -92
- package/src/local/shell.mjs +39 -19
- package/src/local/ssh-key.mjs +158 -42
- package/src/local/state-inventory.mjs +50 -24
- package/src/local/state-owner-lock-inventory.mjs +23 -0
- package/src/local/state-root-owned-namespaces.mjs +83 -0
- package/src/local/state-root-retirement.mjs +89 -0
- package/src/local/state.mjs +386 -96
- package/src/local/stdio.mjs +96 -66
- package/src/local/support-state-projection.mjs +30 -0
- package/src/local/system-network-route.mjs +76 -0
- package/src/local/systemd-removal.mjs +20 -0
- package/src/local/tool-executor.mjs +85 -20
- package/src/local/tool-result-boundary.mjs +51 -0
- package/src/local/toolchain-operation-lock.mjs +41 -0
- package/src/local/tools.mjs +7 -5
- package/src/local/trusted-executable.mjs +53 -0
- package/src/local/trusted-git-executable.mjs +34 -0
- package/src/local/trusted-github-cli.mjs +24 -0
- package/src/local/windows-service-convergence.mjs +49 -0
- package/src/local/windows-service.mjs +52 -45
- package/src/local/worker-deployment-fingerprint.mjs +102 -0
- package/src/local/worker-deployment.mjs +48 -60
- package/src/local/worker-health.mjs +12 -4
- package/src/local/worker-secret-file.mjs +37 -17
- package/src/local/workspace-file-service.mjs +156 -239
- package/src/local/workspace-file-transaction.mjs +189 -0
- package/src/local/workspace-search.mjs +55 -0
- package/src/local/wrangler-toolchain/package-lock.json +1505 -0
- package/src/local/wrangler-toolchain/package.json +23 -0
- package/src/local/wrangler-toolchain-verification.mjs +135 -0
- package/src/local/wrangler-toolchain.mjs +188 -0
- package/src/shared/access-contract.json +7 -1
- package/src/shared/activation-recovery.mjs +49 -0
- package/src/shared/admin-auth.d.mts +2 -1
- package/src/shared/admin-auth.mjs +3 -3
- package/src/shared/authority-revocation.d.mts +9 -0
- package/src/shared/authority-revocation.mjs +28 -0
- package/src/shared/daemon-auth.d.mts +11 -0
- package/src/shared/daemon-auth.mjs +17 -0
- package/src/shared/device-session-auth.d.mts +20 -0
- package/src/shared/device-session-auth.mjs +51 -0
- package/src/shared/foreground-timeout.d.mts +15 -0
- package/src/shared/foreground-timeout.mjs +70 -0
- package/src/shared/log-redaction.d.mts +9 -0
- package/src/shared/log-redaction.mjs +51 -0
- package/src/shared/mcp-protocol.d.mts +27 -0
- package/src/shared/mcp-protocol.mjs +275 -0
- package/src/shared/project-overview-projection.d.mts +4 -0
- package/src/shared/project-overview-projection.mjs +96 -0
- package/src/shared/relay-contract.json +28 -0
- package/src/shared/result-projection.d.mts +2 -1
- package/src/shared/result-projection.mjs +13 -2
- package/src/shared/server-metadata.json +15 -9
- package/src/shared/tool-argument-validation.d.mts +17 -0
- package/src/shared/tool-argument-validation.mjs +325 -0
- package/src/shared/tool-call-capacity.d.mts +41 -0
- package/src/shared/tool-call-capacity.mjs +59 -0
- package/src/shared/tool-catalog.json +626 -57
- package/src/worker/access.ts +12 -0
- package/src/worker/account-admin.ts +55 -33
- package/src/worker/authority-revocations.ts +174 -0
- package/src/worker/authority.ts +52 -6
- package/src/worker/daemon-auth.ts +49 -68
- package/src/worker/daemon-channel.ts +36 -0
- package/src/worker/daemon-http-auth.ts +63 -0
- package/src/worker/daemon-http-channel.ts +120 -0
- package/src/worker/daemon-http-controller.ts +163 -0
- package/src/worker/daemon-http-protocol.ts +62 -0
- package/src/worker/daemon-http-registry.ts +82 -0
- package/src/worker/daemon-last-observation.ts +30 -0
- package/src/worker/daemon-liveness.ts +8 -6
- package/src/worker/daemon-ready-dispatch.ts +17 -0
- package/src/worker/daemon-ready-messages.ts +73 -0
- package/src/worker/daemon-ready-waiters.ts +79 -0
- package/src/worker/daemon-recovery-budget.ts +13 -0
- package/src/worker/daemon-registry.ts +58 -0
- package/src/worker/daemon-relay-diagnostics.ts +127 -0
- package/src/worker/daemon-socket-attachment.ts +55 -0
- package/src/worker/daemon-sockets.ts +72 -62
- package/src/worker/daemon-status.ts +38 -0
- package/src/worker/device-session-verifier.ts +129 -0
- package/src/worker/dpop.ts +157 -0
- package/src/worker/durable-process-timeout.ts +29 -0
- package/src/worker/errors.ts +57 -0
- package/src/worker/http.ts +112 -26
- package/src/worker/index.ts +446 -440
- package/src/worker/mcp-access.ts +57 -0
- package/src/worker/mcp-controller.ts +188 -0
- package/src/worker/mcp-http-accept.ts +11 -0
- package/src/worker/mcp-http-contract.ts +322 -0
- package/src/worker/mcp-initialization-compat.ts +141 -0
- package/src/worker/mcp-jsonrpc.ts +11 -29
- package/src/worker/mcp-removed-protocol.ts +17 -0
- package/src/worker/mcp-response-proxy.ts +135 -0
- package/src/worker/mcp-response-stream.ts +65 -0
- package/src/worker/mcp-stale-schema-compat.ts +57 -0
- package/src/worker/mcp-stream-proxy-contract.ts +48 -0
- package/src/worker/mcp-tool-call-input.ts +23 -0
- package/src/worker/nonce-store.ts +33 -35
- package/src/worker/oauth-authorization-page.ts +21 -1
- package/src/worker/oauth-client-admin.ts +52 -0
- package/src/worker/oauth-client-contract.ts +38 -0
- package/src/worker/oauth-controller.ts +107 -47
- package/src/worker/oauth-field-contract.ts +31 -0
- package/src/worker/oauth-record-contract.ts +7 -0
- package/src/worker/oauth-refresh-authority.ts +27 -0
- package/src/worker/oauth-refresh-exchange.ts +162 -0
- package/src/worker/oauth-refresh-families.ts +130 -27
- package/src/worker/oauth-refresh-persistence.ts +116 -0
- package/src/worker/oauth-state.ts +33 -18
- package/src/worker/oauth-store-validation.ts +131 -0
- package/src/worker/oauth-token-derivation.ts +33 -0
- package/src/worker/oauth-token-issuance.ts +119 -0
- package/src/worker/oauth-tokens.ts +56 -190
- package/src/worker/observability.ts +38 -5
- package/src/worker/pending-admission.ts +15 -0
- package/src/worker/pending-call-capacity.ts +108 -0
- package/src/worker/pending-call-contract.ts +42 -5
- package/src/worker/pending-call-deadlines.ts +75 -0
- package/src/worker/pending-calls.ts +131 -125
- package/src/worker/runtime-alarm-storage.ts +37 -0
- package/src/worker/runtime-alarm.ts +136 -0
- package/src/worker/server-info-activity.ts +36 -0
- package/src/worker/server-info-tool-delivery.ts +15 -0
- package/src/worker/server-info.ts +168 -0
- package/src/worker/tool-catalog.ts +47 -1
- package/src/worker/tool-timeout.ts +75 -14
- package/src/worker/websocket-protocol.ts +26 -2
- package/src/worker/worker-edge-guard.ts +90 -0
- package/src/worker/worker-edge-log.ts +65 -0
- package/src/worker/worker-entry.ts +52 -0
- package/src/worker/worker-mcp-config.ts +23 -0
- package/src/worker/worker-metadata.ts +48 -0
- package/src/worker/worker-rate-limit-key.ts +30 -0
- package/src/worker/worker-runtime-config.ts +20 -0
- package/src/worker/worker-static-routes.ts +54 -0
- package/src/worker/worker-task-lifetime.ts +18 -0
- package/tsconfig.local.json +17 -1
- package/wrangler.jsonc +13 -0
- package/src/local/cli-approval.mjs +0 -117
- package/src/local/operation-state-lock.mjs +0 -92
- package/src/worker/mcp-session.ts +0 -72
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { readBoundedRegularFileSync } from "./secure-file.mjs";
|
|
2
|
+
|
|
3
|
+
const HOST_SAMPLE_MAX_BYTES = 32 * 1024;
|
|
4
|
+
const HOST_SAMPLE_READ_ATTEMPTS = 4;
|
|
5
|
+
|
|
6
|
+
export function readResourceHostSample(file, options = {}) {
|
|
7
|
+
const readFile = options.readFile || readBoundedRegularFileSync;
|
|
8
|
+
for (let attempt = 1; attempt <= HOST_SAMPLE_READ_ATTEMPTS; attempt += 1) {
|
|
9
|
+
try {
|
|
10
|
+
const text = readFile(file, HOST_SAMPLE_MAX_BYTES, "resource host sample", {
|
|
11
|
+
verifyPathIdentity: true,
|
|
12
|
+
rejectMultipleLinks: true,
|
|
13
|
+
}).toString("utf8");
|
|
14
|
+
const value = JSON.parse(text);
|
|
15
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
16
|
+
} catch (error) {
|
|
17
|
+
if (options.optional === true && (error?.code === "ENOENT" || error?.cause?.code === "ENOENT")) return null;
|
|
18
|
+
const identityChanged = error?.code === "MBM_IDENTITY_CHANGED" || error?.cause?.code === "MBM_IDENTITY_CHANGED";
|
|
19
|
+
if (!identityChanged || attempt === HOST_SAMPLE_READ_ATTEMPTS) throw error;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
throw new Error("resource host sample generation did not settle");
|
|
23
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { availableParallelism, cpus, freemem, loadavg, totalmem } from "node:os";
|
|
2
|
+
import { statfsSync } from "node:fs";
|
|
3
|
+
import { statfs } from "node:fs/promises";
|
|
4
|
+
import { sampleDarwinHost, sampleDarwinHostAsync } from "./resource-host-darwin.mjs";
|
|
5
|
+
import { sampleLinuxHost } from "./resource-host-linux.mjs";
|
|
6
|
+
const CPU_BUSY_SAMPLE_MS = 50;
|
|
7
|
+
export function sampleResourceHost(options = {}) {
|
|
8
|
+
const cwd = options.cwd || process.cwd();
|
|
9
|
+
const platform = String(options.platform || process.platform);
|
|
10
|
+
const cpuCores = cpuCount(options);
|
|
11
|
+
const sample = baseSample(cpuCores, platform, options);
|
|
12
|
+
Object.assign(sample, diskStatsSync(cwd));
|
|
13
|
+
if (platform === "darwin") sampleDarwinHost(sample, options);
|
|
14
|
+
else if (platform === "linux") sampleLinuxHost(sample, options);
|
|
15
|
+
sample.sampled_at_ms = Date.now();
|
|
16
|
+
return sample;
|
|
17
|
+
}
|
|
18
|
+
export async function sampleResourceHostAsync(options = {}) {
|
|
19
|
+
const cwd = options.cwd || process.cwd();
|
|
20
|
+
const platform = String(options.platform || process.platform);
|
|
21
|
+
const cpuCores = cpuCount(options);
|
|
22
|
+
const sample = baseSample(cpuCores, platform, options);
|
|
23
|
+
const [cpu, filesystem, darwin] = await Promise.all([
|
|
24
|
+
sampleCpuBusyCoresAsync(cpuCores, sample, options),
|
|
25
|
+
diskStatsAsync(cwd),
|
|
26
|
+
platform === "darwin" ? sampleDarwinHostAsync(options) : Promise.resolve(null),
|
|
27
|
+
]);
|
|
28
|
+
sample.cpu_busy_cores = cpu.busy;
|
|
29
|
+
sample.cpu_time_ms_total = cpu.total;
|
|
30
|
+
sample.cpu_idle_ms_total = cpu.idle;
|
|
31
|
+
Object.assign(sample, filesystem);
|
|
32
|
+
if (darwin) Object.assign(sample, darwin);
|
|
33
|
+
else if (platform === "linux") sampleLinuxHost(sample, options);
|
|
34
|
+
sample.sampled_at_ms = Date.now();
|
|
35
|
+
return sample;
|
|
36
|
+
}
|
|
37
|
+
function baseSample(cpuCores, platform, options) {
|
|
38
|
+
const totalMemoryBytes = totalmem();
|
|
39
|
+
const cpuTotals = cpuTimeTotals(options.cpuTimes);
|
|
40
|
+
return {
|
|
41
|
+
sampled_at_ms: 0, platform, cpu_cores: cpuCores,
|
|
42
|
+
total_memory_mb: Math.round(totalMemoryBytes / (1024 * 1024)),
|
|
43
|
+
cpu_busy_cores: null, load1: Number(loadavg()[0]) || 0,
|
|
44
|
+
memory_free_percent: platform === "win32" && totalMemoryBytes > 0
|
|
45
|
+
? Math.max(0, Math.min(100, freemem() * 100 / totalMemoryBytes))
|
|
46
|
+
: null,
|
|
47
|
+
cpu_time_ms_total: cpuTotals?.total ?? null, cpu_idle_ms_total: cpuTotals?.idle ?? null,
|
|
48
|
+
pageouts_total: null, swapouts_total: null,
|
|
49
|
+
disk_mb_per_s: null, disk_iops: null, disk_free_bytes: null, disk_total_bytes: null,
|
|
50
|
+
thermal_warning: false, io_sampled: false, io_sampled_at_ms: null,
|
|
51
|
+
psi_cpu_some_avg10: null, psi_memory_some_avg10: null, psi_memory_full_avg10: null,
|
|
52
|
+
psi_io_some_avg10: null, psi_io_full_avg10: null,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function cpuCount(options) {
|
|
56
|
+
return Math.max(1, Number(options.cpuCores ?? availableParallelism()) || 1);
|
|
57
|
+
}
|
|
58
|
+
function cpuTimeTotals(readCpus = cpus) {
|
|
59
|
+
let total = 0;
|
|
60
|
+
let idle = 0;
|
|
61
|
+
for (const cpu of readCpus()) {
|
|
62
|
+
const times = cpu?.times || {};
|
|
63
|
+
const values = [times.user, times.nice, times.sys, times.idle, times.irq].map(Number);
|
|
64
|
+
if (!values.every(Number.isFinite)) return null;
|
|
65
|
+
total += values.reduce((sum, value) => sum + value, 0);
|
|
66
|
+
idle += Number(times.idle);
|
|
67
|
+
}
|
|
68
|
+
return total > 0 && idle >= 0 && idle <= total ? { total, idle } : null;
|
|
69
|
+
}
|
|
70
|
+
async function sampleCpuBusyCoresAsync(cpuCores, startSample, options) {
|
|
71
|
+
if (options.previous) {
|
|
72
|
+
const immediate = cpuBusyWindow(cpuCores, options.previous, { total: startSample.cpu_time_ms_total, idle: startSample.cpu_idle_ms_total });
|
|
73
|
+
if (immediate.busy !== null) return immediate;
|
|
74
|
+
}
|
|
75
|
+
const sleep = options.cpuSampleSleep || ((ms) => new Promise((resolvePromise) => { setTimeout(resolvePromise, ms); }));
|
|
76
|
+
await sleep(CPU_BUSY_SAMPLE_MS);
|
|
77
|
+
const end = cpuTimeTotals(options.cpuTimes);
|
|
78
|
+
return cpuBusyWindow(cpuCores, startSample, end);
|
|
79
|
+
}
|
|
80
|
+
function cpuBusyWindow(cpuCores, startSample, end) {
|
|
81
|
+
const startTotal = Number(startSample?.cpu_time_ms_total); const startIdle = Number(startSample?.cpu_idle_ms_total);
|
|
82
|
+
if (!end || !Number.isFinite(startTotal) || !Number.isFinite(startIdle)) return { busy: null, total: end?.total ?? null, idle: end?.idle ?? null };
|
|
83
|
+
const totalDelta = Number(end.total) - startTotal; const idleDelta = Number(end.idle) - startIdle;
|
|
84
|
+
const busy = totalDelta > 0 && idleDelta >= 0 && idleDelta <= totalDelta
|
|
85
|
+
? Math.max(0, Math.min(cpuCores, cpuCores * (1 - idleDelta / totalDelta))) : null;
|
|
86
|
+
return { busy, total: Number(end.total), idle: Number(end.idle) };
|
|
87
|
+
}
|
|
88
|
+
function diskStatsSync(cwd) {
|
|
89
|
+
try {
|
|
90
|
+
const value = statfsSync(cwd);
|
|
91
|
+
return { disk_free_bytes: Number(value.bavail) * Number(value.bsize), disk_total_bytes: Number(value.blocks) * Number(value.bsize) };
|
|
92
|
+
} catch { return { disk_free_bytes: null, disk_total_bytes: null }; }
|
|
93
|
+
}
|
|
94
|
+
async function diskStatsAsync(cwd) {
|
|
95
|
+
try {
|
|
96
|
+
const value = await statfs(cwd);
|
|
97
|
+
return { disk_free_bytes: Number(value.bavail) * Number(value.bsize), disk_total_bytes: Number(value.blocks) * Number(value.bsize) };
|
|
98
|
+
} catch { return { disk_free_bytes: null, disk_total_bytes: null }; }
|
|
99
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
const RESOURCE_KEYS = Object.freeze(["cpu", "io", "memory_mb", "disk_reserve_bytes"]);
|
|
2
|
+
|
|
3
|
+
export function resourceLeaseAccountingContext(leases, processParents, requesterPid) {
|
|
4
|
+
const empty = { parentByLeaseId: {}, parentLeaseId: null, ancestorLeaseIds: [] };
|
|
5
|
+
if (!processParents || typeof processParents !== "object" || Array.isArray(processParents)) return empty;
|
|
6
|
+
const ownerByPid = new Map();
|
|
7
|
+
for (const lease of leases || []) {
|
|
8
|
+
const id = leaseId(lease); const pid = processOwnerPid(lease);
|
|
9
|
+
if (!id || !pid) continue;
|
|
10
|
+
if (ownerByPid.has(pid)) return empty;
|
|
11
|
+
ownerByPid.set(pid, id);
|
|
12
|
+
}
|
|
13
|
+
const parentByLeaseId = {};
|
|
14
|
+
for (const lease of leases || []) {
|
|
15
|
+
const id = leaseId(lease); const pid = processOwnerPid(lease);
|
|
16
|
+
if (!id || !pid) continue;
|
|
17
|
+
const parent = nearestLease(processParents, ownerByPid, parentPid(processParents, pid), id);
|
|
18
|
+
if (parent) parentByLeaseId[id] = parent;
|
|
19
|
+
}
|
|
20
|
+
if (!validParentForest(leases, parentByLeaseId)) return empty;
|
|
21
|
+
const parentLeaseId = nearestLease(processParents, ownerByPid, normalizePid(requesterPid), null);
|
|
22
|
+
return {
|
|
23
|
+
parentByLeaseId,
|
|
24
|
+
parentLeaseId,
|
|
25
|
+
ancestorLeaseIds: leaseAncestors(parentByLeaseId, parentLeaseId),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function aggregateResourceLeases(leases, accounting = {}) {
|
|
30
|
+
return aggregateEffective(leases || [], accounting.parentByLeaseId || {});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function resourceRequestIncrement(leases, request, accounting = {}) {
|
|
34
|
+
const parentByLeaseId = accounting.parentByLeaseId || {};
|
|
35
|
+
const before = aggregateEffective(leases || [], parentByLeaseId);
|
|
36
|
+
const pendingId = "__pending_resource_request__";
|
|
37
|
+
const pending = { lease_id: pendingId, request };
|
|
38
|
+
const afterParents = { ...parentByLeaseId };
|
|
39
|
+
if (accounting.parentLeaseId) afterParents[pendingId] = accounting.parentLeaseId;
|
|
40
|
+
const after = aggregateEffective([...(leases || []), pending], afterParents);
|
|
41
|
+
return Object.fromEntries(RESOURCE_KEYS.map((key) => [key, Math.max(0, after[key] - before[key])]));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function aggregateEffective(leases, parentByLeaseId) {
|
|
45
|
+
const indexed = indexLeases(leases);
|
|
46
|
+
if (!indexed || !validParentForest(leases, parentByLeaseId)) return naiveAggregate(leases);
|
|
47
|
+
const children = new Map([...indexed.keys()].map((id) => [id, []]));
|
|
48
|
+
for (const [child, parent] of Object.entries(parentByLeaseId || {})) {
|
|
49
|
+
if (children.has(child) && children.has(parent)) children.get(parent).push(child);
|
|
50
|
+
}
|
|
51
|
+
const roots = [...indexed.keys()].filter((id) => !parentByLeaseId?.[id] || !indexed.has(parentByLeaseId[id]));
|
|
52
|
+
const memo = new Map();
|
|
53
|
+
const effective = (id) => {
|
|
54
|
+
if (memo.has(id)) return memo.get(id);
|
|
55
|
+
const own = requestVector(indexed.get(id)?.request);
|
|
56
|
+
const childSum = emptyVector();
|
|
57
|
+
for (const child of children.get(id) || []) addVector(childSum, effective(child));
|
|
58
|
+
const value = Object.fromEntries(RESOURCE_KEYS.map((key) => [key, Math.max(own[key], childSum[key])])) ;
|
|
59
|
+
memo.set(id, value); return value;
|
|
60
|
+
};
|
|
61
|
+
const total = emptyAggregate();
|
|
62
|
+
for (const root of roots) { addVector(total, effective(root)); total.heavy_leases += 1; }
|
|
63
|
+
return total;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function indexLeases(leases) {
|
|
67
|
+
const result = new Map();
|
|
68
|
+
for (let index = 0; index < leases.length; index += 1) {
|
|
69
|
+
const lease = leases[index];
|
|
70
|
+
const id = leaseId(lease) || `__anonymous_${index}`;
|
|
71
|
+
if (result.has(id)) return null;
|
|
72
|
+
result.set(id, lease);
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
function naiveAggregate(leases) {
|
|
77
|
+
const total = emptyAggregate();
|
|
78
|
+
for (const lease of leases || []) {
|
|
79
|
+
if (lease?.request?.heavy === false) continue;
|
|
80
|
+
addVector(total, requestVector(lease?.request)); total.heavy_leases += 1;
|
|
81
|
+
}
|
|
82
|
+
return total;
|
|
83
|
+
}
|
|
84
|
+
function validParentForest(leases, parentByLeaseId) {
|
|
85
|
+
const ids = new Set((leases || []).map(leaseId).filter(Boolean));
|
|
86
|
+
for (const start of Object.keys(parentByLeaseId || {})) {
|
|
87
|
+
if (!ids.has(start)) continue;
|
|
88
|
+
const seen = new Set(); let current = start;
|
|
89
|
+
while (current && ids.has(current)) {
|
|
90
|
+
if (seen.has(current)) return false;
|
|
91
|
+
seen.add(current); current = parentByLeaseId[current] || null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
function nearestLease(parents, ownerByPid, startPid, excludedLeaseId) {
|
|
97
|
+
let pid = normalizePid(startPid); const seen = new Set();
|
|
98
|
+
for (let depth = 0; pid && depth < 2048 && !seen.has(pid); depth += 1) {
|
|
99
|
+
seen.add(pid);
|
|
100
|
+
const id = ownerByPid.get(pid);
|
|
101
|
+
if (id && id !== excludedLeaseId) return id;
|
|
102
|
+
pid = parentPid(parents, pid);
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
function leaseAncestors(parentByLeaseId, first) {
|
|
107
|
+
const result = []; const seen = new Set(); let current = first;
|
|
108
|
+
while (current && !seen.has(current)) { seen.add(current); result.push(current); current = parentByLeaseId[current] || null; }
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
function processOwnerPid(lease) { return lease?.owner?.kind === "process" ? normalizePid(lease.owner.pid) : 0; }
|
|
112
|
+
function leaseId(lease) { return typeof lease?.lease_id === "string" && lease.lease_id ? lease.lease_id : null; }
|
|
113
|
+
function parentPid(parents, pid) { return normalizePid(parents?.[String(pid)] ?? parents?.[pid]); }
|
|
114
|
+
function normalizePid(value) { const parsed = Number(value); return Number.isInteger(parsed) && parsed > 0 ? parsed : 0; }
|
|
115
|
+
function requestVector(request = {}) { return Object.fromEntries(RESOURCE_KEYS.map((key) => [key, Math.max(0, finite(request[key]))])); }
|
|
116
|
+
function finite(value) { const parsed = Number(value); return Number.isFinite(parsed) ? parsed : 0; }
|
|
117
|
+
function emptyVector() { return { cpu: 0, io: 0, memory_mb: 0, disk_reserve_bytes: 0 }; }
|
|
118
|
+
function emptyAggregate() { return { ...emptyVector(), heavy_leases: 0 }; }
|
|
119
|
+
function addVector(target, value) { for (const key of RESOURCE_KEYS) target[key] += value[key] || 0; return target; }
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
const TRUSTED_LIGHT_EXECUTABLES = new Set([
|
|
2
|
+
"/bin/echo", "/bin/false", "/bin/ps", "/bin/pwd", "/bin/sleep", "/bin/true", "/bin/uptime",
|
|
3
|
+
"/usr/bin/echo", "/usr/bin/false", "/usr/bin/ps", "/usr/bin/pwd", "/usr/bin/sleep", "/usr/bin/true", "/usr/bin/uptime",
|
|
4
|
+
]);
|
|
5
|
+
|
|
6
|
+
export function isTrustedLightExecutable(command) { return TRUSTED_LIGHT_EXECUTABLES.has(String(command || "")); }
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export function makeCommandJobPlan(argv, maximum = 1024) {
|
|
2
|
+
return makeTokensPlan((argv || []).slice(1).map(String), maximum, false).plan;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function makeFlagsJobPlan(environment = {}, maximum = 1024) {
|
|
6
|
+
let plan = null; let jobserver = false;
|
|
7
|
+
for (const key of ["GNUMAKEFLAGS", "MAKEFLAGS"]) {
|
|
8
|
+
if (!Object.prototype.hasOwnProperty.call(environment || {}, key)) continue;
|
|
9
|
+
const tokens = words(environment[key]);
|
|
10
|
+
if (tokens[0] && !tokens[0].startsWith("-") && !tokens[0].includes("=")) tokens[0] = `-${tokens[0]}`;
|
|
11
|
+
const parsed = makeTokensPlan(tokens, maximum, true);
|
|
12
|
+
if (parsed.plan) plan = parsed.plan;
|
|
13
|
+
jobserver ||= parsed.jobserver;
|
|
14
|
+
}
|
|
15
|
+
return plan || (jobserver ? unboundedPlan(true) : null);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function makeTokensPlan(tokens, maximum, preserve) {
|
|
19
|
+
let plan = null; let jobserver = false;
|
|
20
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
21
|
+
const current = tokens[index];
|
|
22
|
+
if (["-j", "--jobs"].includes(current)) {
|
|
23
|
+
const next = tokens[index + 1];
|
|
24
|
+
if (/^\d+$/.test(next || "")) { plan = numericPlan(next, maximum, preserve); index += 1; }
|
|
25
|
+
else plan = unboundedPlan(preserve);
|
|
26
|
+
} else if (current.startsWith("--jobs=")) plan = numericPlan(current.slice("--jobs=".length), maximum, preserve);
|
|
27
|
+
else if (/^-[A-Za-z]*j/.test(current)) plan = compactPlan(current, maximum, preserve);
|
|
28
|
+
if (current.startsWith("--jobserver-auth=") || current.startsWith("--jobserver-fds=")) jobserver = true;
|
|
29
|
+
}
|
|
30
|
+
return { plan, jobserver };
|
|
31
|
+
}
|
|
32
|
+
function compactPlan(value, maximum, preserve) {
|
|
33
|
+
const suffix = /^-[A-Za-z]*j(.*)$/.exec(value)?.[1] ?? "";
|
|
34
|
+
return suffix ? numericPlan(suffix, maximum, preserve) : unboundedPlan(preserve);
|
|
35
|
+
}
|
|
36
|
+
function numericPlan(value, maximum, preserve) {
|
|
37
|
+
const parsed = Number(value);
|
|
38
|
+
return Number.isSafeInteger(parsed) && parsed >= 1 && parsed <= maximum
|
|
39
|
+
? { jobs: parsed, elastic: false, unbounded: false, preserve } : unboundedPlan(preserve);
|
|
40
|
+
}
|
|
41
|
+
function unboundedPlan(preserve) { return { jobs: null, elastic: false, unbounded: true, preserve }; }
|
|
42
|
+
function words(value) { return String(value || "").trim().split(/\s+/).filter(Boolean); }
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { availableParallelism } from "node:os";
|
|
2
|
+
|
|
3
|
+
export function mavenJobPlan(argv, environment = {}, maximum = 1024) {
|
|
4
|
+
const envArgs = Object.prototype.hasOwnProperty.call(environment || {}, "MAVEN_ARGS") ? words(environment.MAVEN_ARGS) : [];
|
|
5
|
+
const envValue = mavenThreadValue(["mvn", ...envArgs]);
|
|
6
|
+
if (envValue !== null) return mavenThreadPlan(envValue, maximum, true);
|
|
7
|
+
const cliValue = mavenThreadValue(argv);
|
|
8
|
+
return cliValue === null ? null : mavenThreadPlan(cliValue, maximum, false);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function mavenCoreMultiplierPlan(argv, maximum = 1024) {
|
|
12
|
+
const value = mavenThreadValue(argv);
|
|
13
|
+
return value?.endsWith("C") ? multiplierPlan(value, maximum, false) : null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function mavenThreadPlan(value, maximum, preserve) {
|
|
17
|
+
if (value?.endsWith("C")) return multiplierPlan(value, maximum, preserve);
|
|
18
|
+
const jobs = Number(value);
|
|
19
|
+
return Number.isSafeInteger(jobs) && jobs >= 1 && jobs <= maximum
|
|
20
|
+
? { jobs, elastic: false, unbounded: false, preserve } : unboundedPlan(preserve);
|
|
21
|
+
}
|
|
22
|
+
function multiplierPlan(value, maximum, preserve) {
|
|
23
|
+
const multiplier = Number(String(value).slice(0, -1));
|
|
24
|
+
if (!Number.isFinite(multiplier) || multiplier <= 0) return unboundedPlan(preserve);
|
|
25
|
+
const jobs = Math.max(1, Math.trunc(multiplier * availableParallelism()));
|
|
26
|
+
return jobs <= maximum ? { jobs, elastic: false, unbounded: false, preserve } : unboundedPlan(preserve);
|
|
27
|
+
}
|
|
28
|
+
function mavenThreadValue(argv) {
|
|
29
|
+
for (let index = 1; index < (argv || []).length; index += 1) {
|
|
30
|
+
const value = String(argv[index] || "");
|
|
31
|
+
if (["-T", "--threads"].includes(value)) return String(argv[index + 1] || "").trim();
|
|
32
|
+
if (value.startsWith("--threads=")) return value.slice("--threads=".length).trim();
|
|
33
|
+
if (value.startsWith("-T") && value.length > 2) return value.slice(2).trim();
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
function unboundedPlan(preserve = false) { return { jobs: null, elastic: false, unbounded: true, preserve }; }
|
|
38
|
+
function words(value) { return String(value || "").trim().split(/\s+/).filter(Boolean); }
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const NINJA_INT_MAX = 2_147_483_647;
|
|
2
|
+
|
|
3
|
+
export function ninjaCommandJobPlan(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 === "--" || value === "-t" || value.startsWith("-t")) break;
|
|
7
|
+
if (value === "-j") { plan = ninjaNumericPlan(argv[index + 1], maximum); index += 1; }
|
|
8
|
+
else if (value.startsWith("-j") && value.length > 2) plan = ninjaNumericPlan(value.slice(2), maximum);
|
|
9
|
+
else if (value === "--jobs" || value.startsWith("--jobs=")) return invalidPlan();
|
|
10
|
+
if (plan?.invalid) return plan;
|
|
11
|
+
}
|
|
12
|
+
return plan;
|
|
13
|
+
}
|
|
14
|
+
function ninjaNumericPlan(value, maximum) {
|
|
15
|
+
const text = String(value ?? ""); if (!/^\d+$/.test(text)) return invalidPlan();
|
|
16
|
+
const parsed = Number(text); if (!Number.isSafeInteger(parsed) || parsed === 0 || parsed >= NINJA_INT_MAX || parsed > maximum) return unboundedPlan();
|
|
17
|
+
return { jobs: parsed, elastic: false, unbounded: false, preserve: false };
|
|
18
|
+
}
|
|
19
|
+
function invalidPlan() { return { jobs: null, elastic: false, unbounded: false, preserve: false, invalid: true }; }
|
|
20
|
+
function unboundedPlan() { return { jobs: null, elastic: false, unbounded: true, preserve: false }; }
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function ninjaJobserverPlan(argv, environment = {}, _maximum = 1024, platform = process.platform) {
|
|
2
|
+
if ((argv || []).slice(1).some((value) => String(value) === "-n")) return null;
|
|
3
|
+
if (!Object.prototype.hasOwnProperty.call(environment || {}, "MAKEFLAGS")) return null;
|
|
4
|
+
const flags = String(environment.MAKEFLAGS || "");
|
|
5
|
+
return validNinjaJobserver(flags, platform) ? cooperativePlan() : null;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function validNinjaJobserver(flags, platform) {
|
|
9
|
+
const args = String(flags).trim().split(/\s+/).filter(Boolean);
|
|
10
|
+
if (args[0] && !args[0].startsWith("-") && args[0].includes("n")) return false;
|
|
11
|
+
let mode = null; let resource = "";
|
|
12
|
+
for (const arg of args) {
|
|
13
|
+
if (arg.startsWith("--jobserver-auth=")) [mode, resource] = authMode(arg.slice("--jobserver-auth=".length));
|
|
14
|
+
else if (arg.startsWith("--jobserver-fds=")) {
|
|
15
|
+
const pair = descriptorPair(arg.slice("--jobserver-fds=".length)); if (pair === null) return false;
|
|
16
|
+
mode = pair ? "pipe" : "none"; resource = "";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return platform === "win32" ? mode === "semaphore" && resource.length > 0 : mode === "fifo" && resource.length > 0;
|
|
20
|
+
}
|
|
21
|
+
function authMode(value) {
|
|
22
|
+
const pair = descriptorPair(value); if (pair !== null) return [pair ? "pipe" : "none", ""];
|
|
23
|
+
return value.startsWith("fifo:") ? ["fifo", value.slice(5)] : ["semaphore", value];
|
|
24
|
+
}
|
|
25
|
+
function descriptorPair(value) {
|
|
26
|
+
const match = /^(-?\d+),(-?\d+)/.exec(value); return match ? Number(match[1]) >= 0 && Number(match[2]) >= 0 : null;
|
|
27
|
+
}
|
|
28
|
+
function cooperativePlan() { return { jobs: null, elastic: false, unbounded: true, preserve: true }; }
|
|
@@ -1,37 +1,52 @@
|
|
|
1
1
|
import { realpathSync } from "node:fs";
|
|
2
|
-
import { rm } from "node:fs/promises";
|
|
3
2
|
import { resolve } from "node:path";
|
|
4
3
|
import { inspectResourceFile, validateResourceName } from "./managed-jobs.mjs";
|
|
5
|
-
import { generateSshKeyPair } from "./ssh-key.mjs";
|
|
4
|
+
import { generateSshKeyPair, removeGeneratedSshKeyPair } from "./ssh-key.mjs";
|
|
6
5
|
import { acquireStartupLockWithWait, loadState, saveState } from "./state.mjs";
|
|
7
6
|
|
|
8
|
-
export async function generateRegisteredSshKey(
|
|
7
|
+
export async function generateRegisteredSshKey(
|
|
8
|
+
{ workspace, stateDir, name: rawName, targetPath, comment = "" },
|
|
9
|
+
options = {},
|
|
10
|
+
) {
|
|
9
11
|
const name = validateResourceName(rawName);
|
|
10
12
|
if (typeof targetPath !== "string" || !targetPath.trim()) throw new Error("SSH private key target path is required");
|
|
11
13
|
const target = resolve(targetPath);
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
+
const loadStateFn = options.loadState || loadState;
|
|
15
|
+
const acquireLockFn = options.acquireStartupLockWithWait || acquireStartupLockWithWait;
|
|
16
|
+
const generateKeyFn = options.generateSshKeyPair || generateSshKeyPair;
|
|
17
|
+
const inspectResourceFn = options.inspectResourceFile || inspectResourceFile;
|
|
18
|
+
const saveStateFn = options.saveState || saveState;
|
|
19
|
+
const rollbackKeyFn = options.removeGeneratedSshKeyPair || removeGeneratedSshKeyPair;
|
|
20
|
+
const lockState = loadStateFn(workspace, { stateDir });
|
|
21
|
+
const lock = await acquireLockFn(lockState, { operation: "generate-ssh-key" });
|
|
14
22
|
let key = null;
|
|
15
23
|
try {
|
|
24
|
+
const state = loadStateFn(workspace, { stateDir });
|
|
16
25
|
state.resources ||= {};
|
|
17
26
|
const existing = Object.hasOwn(state.resources, name) ? state.resources[name] : null;
|
|
18
27
|
if (existing?.path && !samePathIdentity(existing.path, target)) {
|
|
19
28
|
throw new Error(`local resource ${name} is already registered to a different file; remove it first`);
|
|
20
29
|
}
|
|
21
30
|
if (!existing && Object.keys(state.resources).length >= 64) throw new Error("local resource registry limit reached (64)");
|
|
22
|
-
key = await
|
|
31
|
+
key = await generateKeyFn({
|
|
23
32
|
privateKeyPath: target,
|
|
24
33
|
type: "ed25519",
|
|
25
34
|
comment: comment || `machine-mcp:${name}`,
|
|
26
35
|
});
|
|
27
|
-
const inspected =
|
|
36
|
+
const inspected = inspectResourceFn(key.privateKeyPath);
|
|
28
37
|
state.resources[name] = inspected;
|
|
29
38
|
try {
|
|
30
|
-
|
|
39
|
+
saveStateFn(state);
|
|
31
40
|
} catch (error) {
|
|
32
41
|
if (key.created) {
|
|
33
|
-
|
|
34
|
-
|
|
42
|
+
try {
|
|
43
|
+
await rollbackKeyFn(key);
|
|
44
|
+
} catch (cleanupError) {
|
|
45
|
+
throw new AggregateError(
|
|
46
|
+
[error, cleanupError],
|
|
47
|
+
"SSH key registration failed and generated-key rollback was incomplete",
|
|
48
|
+
);
|
|
49
|
+
}
|
|
35
50
|
}
|
|
36
51
|
throw error;
|
|
37
52
|
}
|
|
@@ -53,6 +68,7 @@ export async function generateRegisteredSshKey({ workspace, stateDir, name: rawN
|
|
|
53
68
|
}
|
|
54
69
|
}
|
|
55
70
|
|
|
71
|
+
|
|
56
72
|
function samePathIdentity(left, right) {
|
|
57
73
|
const a = canonicalIfExisting(left);
|
|
58
74
|
const b = canonicalIfExisting(right);
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { resourceDiskHardFloorBytes, resourceDiskSoftFloorBytes } from "./resource-disk-headroom.mjs";
|
|
2
|
+
|
|
3
|
+
export function resourcePressureState(host, used) {
|
|
4
|
+
const cores = Math.max(1, Number(host?.cpu_cores) || 1);
|
|
5
|
+
const reasons = [];
|
|
6
|
+
const criticalReasons = [];
|
|
7
|
+
let level = 0;
|
|
8
|
+
const mark = (next, reason) => {
|
|
9
|
+
if (next > level) level = next;
|
|
10
|
+
reasons.push(reason);
|
|
11
|
+
if (next === 2) criticalReasons.push(reason);
|
|
12
|
+
};
|
|
13
|
+
if (host?.thermal_warning === true) mark(2, "thermal_warning");
|
|
14
|
+
thresholdPressure(optionalResourceNumber(host?.memory_free_percent), { critical: 8, warning: 18, lowerIsWorse: true }, mark, "memory_pressure");
|
|
15
|
+
thresholdPressure(optionalResourceNumber(host?.disk_mb_per_s), { critical: 220, warning: 100 }, mark, "disk_throughput");
|
|
16
|
+
thresholdPressure(optionalResourceNumber(host?.disk_iops), { critical: 5000, warning: 2500 }, mark, "disk_iops");
|
|
17
|
+
thresholdPressure(optionalResourceNumber(host?.pageouts_per_s), { critical: 1024, warning: 256 }, mark, "pageout_rate");
|
|
18
|
+
thresholdPressure(optionalResourceNumber(host?.swapouts_per_s), { critical: 512, warning: 128 }, mark, "swapout_rate");
|
|
19
|
+
markDiskHeadroom(host, mark);
|
|
20
|
+
markPsiPressure(host, mark);
|
|
21
|
+
markCpuLoadPressure(host, cores, mark);
|
|
22
|
+
if (used.heavy_leases > cores * 2) mark(1, "heavy_root_count");
|
|
23
|
+
return { state: level === 2 ? "red" : level === 1 ? "yellow" : "green", reasons, critical_reasons: criticalReasons };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function optionalResourceNumber(value) {
|
|
27
|
+
if (value === null || value === undefined || value === "") return null;
|
|
28
|
+
const parsed = Number(value);
|
|
29
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function markDiskHeadroom(host, mark) {
|
|
33
|
+
const free = optionalResourceNumber(host?.disk_free_bytes); const total = optionalResourceNumber(host?.disk_total_bytes);
|
|
34
|
+
if (free === null || total === null || total <= 0) return;
|
|
35
|
+
const hardFloor = resourceDiskHardFloorBytes(total); const softFloor = resourceDiskSoftFloorBytes(total);
|
|
36
|
+
if (free < hardFloor) mark(2, "disk_free_headroom_critical");
|
|
37
|
+
else if (free < softFloor) mark(1, "disk_free_headroom");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function markPsiPressure(host, mark) {
|
|
41
|
+
const memoryFull = optionalResourceNumber(host?.psi_memory_full_avg10);
|
|
42
|
+
thresholdPressure(memoryFull, { critical: 60, warning: 10 }, mark, "psi_memory_full");
|
|
43
|
+
for (const [value, reason] of [
|
|
44
|
+
[host?.psi_cpu_some_avg10, "psi_cpu_some"], [host?.psi_memory_some_avg10, "psi_memory_some"],
|
|
45
|
+
[host?.psi_io_some_avg10, "psi_io_some"], [host?.psi_io_full_avg10, "psi_io_full"],
|
|
46
|
+
]) if ((optionalResourceNumber(value) ?? 0) >= 10) mark(1, reason);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function markCpuLoadPressure(host, cores, mark) {
|
|
50
|
+
const cpu = optionalResourceNumber(host?.cpu_busy_cores); const load = optionalResourceNumber(host?.load1);
|
|
51
|
+
const ioMb = optionalResourceNumber(host?.disk_mb_per_s); const iops = optionalResourceNumber(host?.disk_iops);
|
|
52
|
+
if (cpu !== null && cpu >= cores * 0.9) mark(1, "cpu_busy");
|
|
53
|
+
if (load === null) return;
|
|
54
|
+
const psiBacklog = [host?.psi_cpu_some_avg10, host?.psi_memory_some_avg10, host?.psi_io_some_avg10, host?.psi_io_full_avg10]
|
|
55
|
+
.some((value) => (optionalResourceNumber(value) ?? 0) >= 10);
|
|
56
|
+
const corroborated = (cpu ?? 0) >= cores * 0.70 || (ioMb ?? 0) >= 50 || (iops ?? 0) >= 1250 || psiBacklog;
|
|
57
|
+
if (load >= cores * 1.25 && (host?.platform !== "darwin" || corroborated)) mark(1, "load_backlog");
|
|
58
|
+
if (load >= cores * 2 && ((cpu ?? 0) >= cores * 0.75 || (ioMb ?? 0) >= 100 || (iops ?? 0) >= 2500)) mark(2, "load_backlog_critical");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function thresholdPressure(value, thresholds, mark, label) {
|
|
62
|
+
if (value === null) return;
|
|
63
|
+
const critical = thresholds.lowerIsWorse ? value < thresholds.critical : value >= thresholds.critical;
|
|
64
|
+
if (critical) { mark(2, `${label}_critical`); return; }
|
|
65
|
+
const warning = thresholds.lowerIsWorse ? value < thresholds.warning : value >= thresholds.warning;
|
|
66
|
+
if (warning) mark(1, label);
|
|
67
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { execFile, spawnSync } from "node:child_process";
|
|
2
|
+
|
|
3
|
+
export function runResourceProbeSync(command, args, options = {}) {
|
|
4
|
+
const result = spawnSync(command, args, {
|
|
5
|
+
encoding: "utf8",
|
|
6
|
+
timeout: positive(options.timeoutMs, 2_500),
|
|
7
|
+
killSignal: "SIGKILL",
|
|
8
|
+
maxBuffer: positive(options.maxOutputBytes, 256 * 1024),
|
|
9
|
+
windowsHide: true,
|
|
10
|
+
env: probeEnvironment(),
|
|
11
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
12
|
+
});
|
|
13
|
+
return { ok: !result.error && result.status === 0, stdout: String(result.stdout || "") };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function runResourceProbeAsync(command, args, options = {}) {
|
|
17
|
+
return new Promise((resolvePromise) => {
|
|
18
|
+
execFile(command, args, {
|
|
19
|
+
encoding: "utf8",
|
|
20
|
+
timeout: positive(options.timeoutMs, 2_500),
|
|
21
|
+
killSignal: "SIGKILL",
|
|
22
|
+
maxBuffer: positive(options.maxOutputBytes, 256 * 1024),
|
|
23
|
+
windowsHide: true,
|
|
24
|
+
env: probeEnvironment(),
|
|
25
|
+
}, (error, stdout) => resolvePromise({ ok: !error, stdout: String(stdout || "") }));
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function probeEnvironment() {
|
|
30
|
+
return process.platform === "win32" ? process.env : { ...process.env, LC_ALL: "C", LANG: "C" };
|
|
31
|
+
}
|
|
32
|
+
function positive(value, fallback) {
|
|
33
|
+
const parsed = Number(value);
|
|
34
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
35
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { applyResourceProfileEnv, resourceCommandEffectiveCwd, resourceCommandProfile } from "./resource-command-profile.mjs";
|
|
2
|
+
import { ResourceAdmissionError } from "./resource-admission.mjs";
|
|
3
|
+
import { BridgeError } from "./errors.mjs";
|
|
4
|
+
import { prepareResourceBuildCommand } from "./resource-build-root.mjs";
|
|
5
|
+
import { applyResourceProcessPriority } from "./resource-process-priority.mjs";
|
|
6
|
+
import { releaseControlWorkspaceForCommand } from "./resource-release-control-workspace.mjs";
|
|
7
|
+
export async function acquireProcessResources(coordinator, command, args, environment, options = {}) {
|
|
8
|
+
if (!coordinator) return { lease: null, environment, request: null, command, args };
|
|
9
|
+
const request = resourceCommandProfile(command, args, { priority: options.priority, environment, releaseControlWorkspace: await releaseControlWorkspaceForCommand(command, args, options.cwd, environment) });
|
|
10
|
+
const resourceCwd = resourceCommandEffectiveCwd(command, args, options.cwd, request);
|
|
11
|
+
let lease;
|
|
12
|
+
try {
|
|
13
|
+
lease = await coordinator.acquire(request, {
|
|
14
|
+
cwd: resourceCwd,
|
|
15
|
+
waitMs: options.waitMs,
|
|
16
|
+
signal: options.signal,
|
|
17
|
+
cancelCheck: options.cancelCheck,
|
|
18
|
+
});
|
|
19
|
+
} catch (error) {
|
|
20
|
+
const coordinatorBusy = ["MBM_RESOURCE_TRANSACTION_BUSY", "MBM_RESOURCE_STAGING_BUSY"].includes(error?.code);
|
|
21
|
+
if (!(error instanceof ResourceAdmissionError) && !coordinatorBusy) throw error;
|
|
22
|
+
const decision = error instanceof ResourceAdmissionError ? error.decision : { state: "unknown", reason: "coordinator_busy" };
|
|
23
|
+
const retryable = error instanceof ResourceAdmissionError ? error.retryable !== false : true;
|
|
24
|
+
const message = retryable ? "local heavy-resource capacity is temporarily unavailable" : "local heavy-resource request exceeds the configured launch capacity; reduce explicit parallelism or resource demand";
|
|
25
|
+
throw new BridgeError("unavailable", message, {
|
|
26
|
+
retryable,
|
|
27
|
+
details: {
|
|
28
|
+
reason: "resource_admission",
|
|
29
|
+
pressure_state: decision?.state || "unknown",
|
|
30
|
+
admission_reason: decision?.reason || "resource_busy",
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
const effectiveRequest = lease?.request ? { ...request, ...lease.request } : request;
|
|
35
|
+
try {
|
|
36
|
+
const buildRoot = environment?.AGENT_BUILD_ROOT || process.env.AGENT_BUILD_ROOT || "";
|
|
37
|
+
const prepared = prepareResourceBuildCommand(
|
|
38
|
+
command, args, applyResourceProfileEnv(environment, effectiveRequest), effectiveRequest, resourceCwd,
|
|
39
|
+
buildRoot ? { root: buildRoot } : {},
|
|
40
|
+
);
|
|
41
|
+
const prioritized = applyResourceProcessPriority(prepared.command, prepared.args, effectiveRequest);
|
|
42
|
+
return { lease, request: effectiveRequest, ...prepared, ...prioritized };
|
|
43
|
+
} catch (error) {
|
|
44
|
+
await releaseProcessResources(lease);
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export function bindProcessResources(lease, child) {
|
|
49
|
+
return lease?.bindProcess?.(child, { processGroupIsolated: process.platform !== "win32" }) ?? Promise.resolve();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function releaseProcessResources(lease) {
|
|
53
|
+
return lease?.release?.() ?? Promise.resolve(false);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function releaseProcessResourcesQuietly(lease) {
|
|
57
|
+
try { return await releaseProcessResources(lease); }
|
|
58
|
+
catch { return false; }
|
|
59
|
+
}
|