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
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
|
|
3
2
|
import { randomBytes } from "node:crypto";
|
|
4
3
|
import { performance } from "node:perf_hooks";
|
|
5
4
|
import { BridgeError } from "./errors.mjs";
|
|
5
|
+
import { assertCallCapacity, callCapacityConfig, callCapacitySnapshot } from "./call-capacity.mjs";
|
|
6
|
+
import { bindCallPrincipal, callIdsForAuthority } from "./call-authority.mjs";
|
|
7
|
+
import { assertCallRegistryOpen, cancelAllCallsAndWait, notifyCallRegistryChanged } from "./call-registry-drain.mjs";
|
|
6
8
|
|
|
7
9
|
/** @typedef {ReturnType<typeof setTimeout>} TimerHandle */
|
|
8
10
|
/**
|
|
@@ -15,11 +17,15 @@ import { BridgeError } from "./errors.mjs";
|
|
|
15
17
|
* controller: AbortController,
|
|
16
18
|
* cancelReason: string,
|
|
17
19
|
* timer: TimerHandle | null,
|
|
20
|
+
* owner_kind?: string, owner_account_id?: string, owner_account_version?: number,
|
|
21
|
+
* owner_client_id?: string, owner_family_id?: string,
|
|
18
22
|
* }} CallRecord
|
|
19
23
|
*/
|
|
20
24
|
/**
|
|
21
25
|
* @typedef {{
|
|
22
26
|
* maximum?: unknown,
|
|
27
|
+
* reserved?: unknown,
|
|
28
|
+
* reservedTools?: Iterable<string>,
|
|
23
29
|
* now?: () => number,
|
|
24
30
|
* scheduler?: {setTimeout: typeof setTimeout, clearTimeout: typeof clearTimeout},
|
|
25
31
|
* onCancel?: (record: CallRecord) => void,
|
|
@@ -34,7 +40,8 @@ import { BridgeError } from "./errors.mjs";
|
|
|
34
40
|
export class CallRegistry {
|
|
35
41
|
/** @param {CallRegistryOptions} [options] */
|
|
36
42
|
constructor(options = {}) {
|
|
37
|
-
this.
|
|
43
|
+
this.capacity = callCapacityConfig(options.maximum, options.reserved, options.reservedTools);
|
|
44
|
+
this.maximum = this.capacity.maximum;
|
|
38
45
|
this.now = typeof options.now === "function" ? options.now : () => performance.now();
|
|
39
46
|
this.scheduler = options.scheduler || { setTimeout, clearTimeout };
|
|
40
47
|
this.onCancel = typeof options.onCancel === "function" ? options.onCancel : () => {};
|
|
@@ -45,18 +52,18 @@ export class CallRegistry {
|
|
|
45
52
|
|
|
46
53
|
/** @param {OpenCallInput} [input] */
|
|
47
54
|
open({ callId = "", tool = "", origin = "local", timeoutMs = 0 } = {}) {
|
|
55
|
+
assertCallRegistryOpen(this);
|
|
48
56
|
const id = String(callId || `call_${randomBytes(16).toString("hex")}`);
|
|
57
|
+
const toolName = String(tool || "");
|
|
49
58
|
if (this.calls.has(id)) throw new BridgeError("conflict", "duplicate in-flight call id");
|
|
50
|
-
|
|
51
|
-
throw new BridgeError("limit_exceeded", `too many concurrent tool calls (${this.maximum})`, { retryable: true });
|
|
52
|
-
}
|
|
59
|
+
assertCallCapacity(this.calls, this.capacity, toolName);
|
|
53
60
|
const controller = new AbortController();
|
|
54
61
|
const startedAt = this.now();
|
|
55
62
|
const timeout = positiveInteger(timeoutMs, 0);
|
|
56
63
|
/** @type {CallRecord} */
|
|
57
64
|
const record = {
|
|
58
65
|
id,
|
|
59
|
-
tool:
|
|
66
|
+
tool: toolName,
|
|
60
67
|
origin: String(origin || "local"),
|
|
61
68
|
startedAt,
|
|
62
69
|
deadlineAt: timeout ? startedAt + timeout : null,
|
|
@@ -96,7 +103,7 @@ export class CallRegistry {
|
|
|
96
103
|
if (!record) return false;
|
|
97
104
|
if (record.timer) this.scheduler.clearTimeout(record.timer);
|
|
98
105
|
this.calls.delete(id);
|
|
99
|
-
this.onFinish(record);
|
|
106
|
+
try { this.onFinish(record); } finally { notifyCallRegistryChanged(this); }
|
|
100
107
|
return true;
|
|
101
108
|
}
|
|
102
109
|
|
|
@@ -117,12 +124,20 @@ export class CallRegistry {
|
|
|
117
124
|
return cancelled;
|
|
118
125
|
}
|
|
119
126
|
|
|
120
|
-
/** @param {unknown}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
127
|
+
/** @param {unknown} callId @param {unknown} principal */ bindPrincipal(callId, principal) {
|
|
128
|
+
return bindCallPrincipal(this.calls.get(String(callId || "")), principal);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** @param {unknown} revocation */ cancelAuthority(revocation) {
|
|
132
|
+
let cancelled = 0;
|
|
133
|
+
for (const id of callIdsForAuthority(this.calls.values(), revocation))
|
|
134
|
+
if (this.cancel(id, "authority revoked", "authorization_denied")) cancelled += 1;
|
|
135
|
+
return cancelled;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** @param {unknown} [reason] @param {number} [waitMs] */
|
|
139
|
+
async cancelAllAndWait(reason = "runtime stopped", waitMs = 5_000) {
|
|
140
|
+
await cancelAllCallsAndWait(this, reason, waitMs);
|
|
126
141
|
}
|
|
127
142
|
|
|
128
143
|
/** @param {unknown} callId */
|
|
@@ -151,7 +166,7 @@ export class CallRegistry {
|
|
|
151
166
|
snapshot() {
|
|
152
167
|
const now = this.now();
|
|
153
168
|
/** @type {Record<string, number>} */
|
|
154
|
-
const byOrigin =
|
|
169
|
+
const byOrigin = Object.create(null);
|
|
155
170
|
let oldestMs = 0;
|
|
156
171
|
for (const call of this.calls.values()) {
|
|
157
172
|
byOrigin[call.origin] = (byOrigin[call.origin] || 0) + 1;
|
|
@@ -159,7 +174,7 @@ export class CallRegistry {
|
|
|
159
174
|
}
|
|
160
175
|
return {
|
|
161
176
|
active: this.calls.size,
|
|
162
|
-
|
|
177
|
+
...callCapacitySnapshot(this.calls, this.capacity),
|
|
163
178
|
by_origin: byOrigin,
|
|
164
179
|
oldest_ms: oldestMs,
|
|
165
180
|
};
|
|
@@ -34,6 +34,11 @@ export class CapabilityObserver {
|
|
|
34
34
|
matched_commands: Array.isArray(result?.command_matches) ? result.command_matches.length : 0,
|
|
35
35
|
matched_applications: Array.isArray(result?.application_matches) ? result.application_matches.length : 0,
|
|
36
36
|
recommended_tools: Array.isArray(result?.recommended_tools) ? [...result.recommended_tools] : [],
|
|
37
|
+
primary_route: result?.execution_routing?.primary_route?.id || null,
|
|
38
|
+
routing_ambiguity: result?.execution_routing?.ambiguity?.level || "none",
|
|
39
|
+
routing_score_gap: Number.isFinite(Number(result?.execution_routing?.ambiguity?.score_gap))
|
|
40
|
+
? Number(result.execution_routing.ambiguity.score_gap)
|
|
41
|
+
: 0,
|
|
37
42
|
refresh_fingerprint: String(result?.refresh?.fingerprint || ""),
|
|
38
43
|
};
|
|
39
44
|
}
|
|
@@ -67,9 +67,10 @@ export function recommendTools(task, { commandsAvailable, commandRelevant, skill
|
|
|
67
67
|
const tools = ["agent_context"];
|
|
68
68
|
if (skillRelevant) tools.push("load_local_skill");
|
|
69
69
|
if (commandRelevant) tools.push("run_local_command");
|
|
70
|
-
if (/browser|chrome|edge|brave|网页|浏览器|表单|网站/.test(lower)) tools.push("browser_status", "browser_list_tabs", "browser_manage_tabs", "browser_inspect_page", "browser_wait", "browser_action", "browser_fill_form");
|
|
71
|
-
if (/app|application|gui|window|应用|软件|窗口|界面/.test(lower)) tools.push("list_local_applications", "inspect_local_application", "operate_local_application");
|
|
70
|
+
if (/browser|chrome|edge|brave|网页|浏览器|表单|网站/.test(lower)) tools.push("computer_observe", "computer_act", "browser_status", "browser_list_tabs", "browser_manage_tabs", "browser_inspect_page", "browser_wait", "browser_action", "browser_fill_form");
|
|
71
|
+
if (/app|application|gui|window|应用|软件|窗口|界面/.test(lower)) tools.push("computer_observe", "computer_act", "list_local_applications", "inspect_local_application", "operate_local_application");
|
|
72
72
|
if (/git|commit|branch|diff|仓库|提交|分支/.test(lower)) tools.push("git_status", "git_diff");
|
|
73
|
+
if (/commit|提交/.test(lower)) tools.push("git_commit");
|
|
73
74
|
if (/test|build|lint|command|terminal|测试|构建|命令|终端/.test(lower)) tools.push(commandsAvailable ? "run_local_command" : "run_process");
|
|
74
75
|
if (/file|code|source|edit|write|文件|代码|源码|修改|写入/.test(lower)) tools.push("read_file", "search_text", "edit_file", "apply_patch");
|
|
75
76
|
return [...new Set(tools)];
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {{
|
|
5
|
+
* onSettle?: (code: number | null, signal: string | null, source: "close" | "exit_fallback") => void,
|
|
6
|
+
* onFallback?: () => void,
|
|
7
|
+
* schedule?: (callback: () => void, delay: number) => unknown,
|
|
8
|
+
* clearSchedule?: (timer: unknown) => void,
|
|
9
|
+
* fallbackMs?: unknown,
|
|
10
|
+
* readExitState?: () => { code?: number | null, signal?: string | null },
|
|
11
|
+
* }} ChildProcessSettlementOptions
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** @param {ChildProcessSettlementOptions} [options] */
|
|
15
|
+
export function createChildProcessSettlement(options = {}) {
|
|
16
|
+
if (typeof options.onSettle !== "function") throw new TypeError("child settlement requires onSettle");
|
|
17
|
+
const onSettle = options.onSettle;
|
|
18
|
+
/** @type {(callback: () => void, delay: number) => unknown} */
|
|
19
|
+
const defaultSchedule = (callback, delay) => setTimeout(callback, delay);
|
|
20
|
+
/** @type {(timer: unknown) => void} */
|
|
21
|
+
const defaultClearSchedule = timer => clearTimeout(/** @type {ReturnType<typeof setTimeout>} */ (timer));
|
|
22
|
+
const schedule = typeof options.schedule === "function" ? options.schedule : defaultSchedule;
|
|
23
|
+
const clearSchedule = typeof options.clearSchedule === "function" ? options.clearSchedule : defaultClearSchedule;
|
|
24
|
+
const delay = boundedDelay(options.fallbackMs);
|
|
25
|
+
let settled = false;
|
|
26
|
+
/** @type {unknown} */
|
|
27
|
+
let timer = null;
|
|
28
|
+
let timerSet = false;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param {number | null} code
|
|
32
|
+
* @param {string | null} signal
|
|
33
|
+
* @param {"close" | "exit_fallback"} source
|
|
34
|
+
*/
|
|
35
|
+
function settle(code, signal, source) {
|
|
36
|
+
if (settled) return false;
|
|
37
|
+
settled = true;
|
|
38
|
+
if (timerSet) clearSchedule(timer);
|
|
39
|
+
timer = null;
|
|
40
|
+
timerSet = false;
|
|
41
|
+
onSettle(code, signal, source);
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** @param {number | null} code @param {string | null} signal */
|
|
46
|
+
function onClose(code, signal) { return settle(code, signal, "close"); }
|
|
47
|
+
|
|
48
|
+
/** @param {number | null} code @param {string | null} signal */
|
|
49
|
+
function onExit(code, signal) {
|
|
50
|
+
if (settled || timerSet) return false;
|
|
51
|
+
timerSet = true;
|
|
52
|
+
timer = schedule(() => {
|
|
53
|
+
timer = null;
|
|
54
|
+
timerSet = false;
|
|
55
|
+
options.onFallback?.();
|
|
56
|
+
const observed = safeExitState(options.readExitState);
|
|
57
|
+
const settledCode = typeof observed.code === "number" && Number.isInteger(observed.code) ? observed.code : code;
|
|
58
|
+
const settledSignal = observed.signal || signal;
|
|
59
|
+
settle(settledCode, settledSignal, "exit_fallback");
|
|
60
|
+
}, delay);
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function cancel() {
|
|
65
|
+
if (settled) return false;
|
|
66
|
+
settled = true;
|
|
67
|
+
if (timerSet) clearSchedule(timer);
|
|
68
|
+
timer = null;
|
|
69
|
+
timerSet = false;
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return Object.freeze({ onClose, onExit, cancel });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Attach the shared close-preferred settlement policy to one ChildProcess-like object.
|
|
78
|
+
* Residual stdio is destroyed only after exit has been observed and the bounded close grace expired.
|
|
79
|
+
* @param {any} child
|
|
80
|
+
* @param {ChildProcessSettlementOptions} options
|
|
81
|
+
*/
|
|
82
|
+
export function attachChildProcessSettlement(child, options = {}) {
|
|
83
|
+
const settlement = createChildProcessSettlement({
|
|
84
|
+
...options,
|
|
85
|
+
readExitState: options.readExitState || (() => ({ code: child?.exitCode, signal: child?.signalCode })),
|
|
86
|
+
onFallback() {
|
|
87
|
+
for (const stream of [child?.stdin, child?.stdout, child?.stderr]) {
|
|
88
|
+
try { stream?.destroy?.(); }
|
|
89
|
+
catch { /* The process has exited; residual stream destruction is bounded best-effort cleanup. */ }
|
|
90
|
+
}
|
|
91
|
+
try { child?.unref?.(); }
|
|
92
|
+
catch { /* Detachment is advisory after process exit and cannot change the settlement result. */ }
|
|
93
|
+
options.onFallback?.();
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
/** @param {number | null} code @param {string | null} signal */
|
|
97
|
+
const onExit = (code, signal) => settlement.onExit(code, signal);
|
|
98
|
+
/** @param {number | null} code @param {string | null} signal */
|
|
99
|
+
const onClose = (code, signal) => settlement.onClose(code, signal);
|
|
100
|
+
child.once("exit", onExit);
|
|
101
|
+
child.once("close", onClose);
|
|
102
|
+
return settlement;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** @param {unknown} value */
|
|
106
|
+
function boundedDelay(value) {
|
|
107
|
+
if (value === undefined) return 1000;
|
|
108
|
+
const parsed = Number(value);
|
|
109
|
+
if (!Number.isFinite(parsed) || parsed < 0 || parsed > 10_000) {
|
|
110
|
+
throw new TypeError("child settlement fallback must be between 0 and 10000 milliseconds");
|
|
111
|
+
}
|
|
112
|
+
return Math.floor(parsed);
|
|
113
|
+
}
|
|
114
|
+
export function childExitedBeforeTimeout({ exitCode = null, signalCode = null, processState = "unknown" } = {}) {
|
|
115
|
+
return Number.isInteger(exitCode) || Boolean(signalCode) || processState === "zombie";
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** @param {ChildProcessSettlementOptions["readExitState"]} reader @returns {{ code: number | null, signal: string | null }} */
|
|
119
|
+
function safeExitState(reader) {
|
|
120
|
+
if (typeof reader !== "function") return { code: null, signal: null };
|
|
121
|
+
try {
|
|
122
|
+
const value = reader();
|
|
123
|
+
const code = value?.code;
|
|
124
|
+
const signal = value?.signal;
|
|
125
|
+
return {
|
|
126
|
+
code: typeof code === "number" && Number.isInteger(code) ? code : null,
|
|
127
|
+
signal: typeof signal === "string" && signal ? signal : null,
|
|
128
|
+
};
|
|
129
|
+
} catch {
|
|
130
|
+
return { code: null, signal: null };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { AccountAdminClient, accountRoleNames, generateAccountPassword } from "./account-admin.mjs";
|
|
2
|
+
import { createDeviceSessionForRoot } from "./device-root-provider.mjs";
|
|
2
3
|
import { loadState } from "./state.mjs";
|
|
4
|
+
import { packageVersion } from "./package-identity.mjs";
|
|
3
5
|
|
|
4
6
|
export function createAccountCommand({ chooseWorkspace, confirm }) {
|
|
5
7
|
if (typeof chooseWorkspace !== "function" || typeof confirm !== "function") {
|
|
@@ -8,7 +10,7 @@ export function createAccountCommand({ chooseWorkspace, confirm }) {
|
|
|
8
10
|
return async function accountCommand(args) {
|
|
9
11
|
const workspace = await chooseWorkspace({ ...args, _: [] }, { promptOnFirstRun: false, save: false, allowPositional: false });
|
|
10
12
|
const state = loadState(workspace, { stateDir: args.stateDir });
|
|
11
|
-
const client = accountAdminClient(state);
|
|
13
|
+
const client = await accountAdminClient(state);
|
|
12
14
|
const action = String(args._[0] || "list").toLowerCase();
|
|
13
15
|
const result = await performAccountAction({ action, args, client, confirm });
|
|
14
16
|
if (result === null) return;
|
|
@@ -17,15 +19,36 @@ export function createAccountCommand({ chooseWorkspace, confirm }) {
|
|
|
17
19
|
};
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
export function accountAdminClient(state) {
|
|
21
|
-
if (!state.worker?.url || !state.worker?.
|
|
22
|
+
export async function accountAdminClient(state, sessionIdentity = null) {
|
|
23
|
+
if (!state.worker?.url || !state.worker?.deviceIdentity) {
|
|
22
24
|
throw new Error("account administration requires a deployed Worker; run machine-mcp first");
|
|
23
25
|
}
|
|
24
|
-
|
|
26
|
+
const identity = sessionIdentity || await createDeviceSessionForRoot(
|
|
27
|
+
state.worker.deviceIdentity,
|
|
28
|
+
state.worker.url,
|
|
29
|
+
"machine-bridge-mcp",
|
|
30
|
+
currentPackageVersion(),
|
|
31
|
+
{ profileDir: state.paths.profileDir, reason: "Authorize Machine Bridge account administration" },
|
|
32
|
+
);
|
|
33
|
+
return new AccountAdminClient({ workerUrl: state.worker.url, sessionIdentity: identity });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function currentPackageVersion() {
|
|
37
|
+
return packageVersion;
|
|
25
38
|
}
|
|
26
39
|
|
|
27
40
|
async function performAccountAction({ action, args, client, confirm }) {
|
|
28
41
|
if (action === "list") return client.list();
|
|
42
|
+
if (action === "clients") return client.listClients();
|
|
43
|
+
if (action === "revoke-client") {
|
|
44
|
+
const clientId = String(args._[1] || "");
|
|
45
|
+
if (!clientId) throw new Error("account revoke-client requires CLIENT_ID");
|
|
46
|
+
if (!args.yes && !(await confirm(`Revoke OAuth client ${clientId}?`, false))) {
|
|
47
|
+
console.log("Client revocation cancelled.");
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return client.removeClient({ clientId });
|
|
51
|
+
}
|
|
29
52
|
if (action === "add") {
|
|
30
53
|
const name = args._[1];
|
|
31
54
|
const role = args._[2];
|
|
@@ -56,10 +79,21 @@ async function performAccountAction({ action, args, client, confirm }) {
|
|
|
56
79
|
}
|
|
57
80
|
return client.remove({ accountId: account.account_id });
|
|
58
81
|
}
|
|
59
|
-
throw new Error("Unknown account action. Use list, add, role, enable, disable, rotate-password, or remove.");
|
|
82
|
+
throw new Error("Unknown account action. Use list, clients, revoke-client, add, role, enable, disable, rotate-password, or remove.");
|
|
60
83
|
}
|
|
61
84
|
|
|
62
85
|
function printAccountResult(action, result) {
|
|
86
|
+
if (action === "clients") {
|
|
87
|
+
if (!result.clients.length) {
|
|
88
|
+
console.log("No OAuth clients configured.");
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
for (const client of result.clients) {
|
|
92
|
+
const registration = client.registration_current === true ? "current" : "stale";
|
|
93
|
+
console.log(`${client.client_name}\t${client.trusted_role || "untrusted"}\t${client.client_id}\taccess=${client.active_access_tokens} refresh=${client.active_refresh_tokens} registration=${registration}`);
|
|
94
|
+
}
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
63
97
|
if (action === "list") {
|
|
64
98
|
if (!result.accounts.length) {
|
|
65
99
|
console.log("No accounts configured.");
|
|
@@ -75,5 +109,5 @@ function printAccountResult(action, result) {
|
|
|
75
109
|
process.stdout.write(`Password: ${result.password}\n`);
|
|
76
110
|
console.log("Save this password now; it is not stored locally or shown again. Do not share this terminal output.");
|
|
77
111
|
}
|
|
78
|
-
if (result.removed) console.log("Account removed.");
|
|
112
|
+
if (result.removed) console.log(action === "revoke-client" ? "OAuth client revoked." : "Account removed.");
|
|
79
113
|
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { acquireDaemonLockWithTakeover, inspectWorkspaceDaemon } from "./daemon-process.mjs";
|
|
2
|
+
import { effectiveLogFormat, effectiveLogLevel } from "./cli-options.mjs";
|
|
3
|
+
import { createLogger } from "./log.mjs";
|
|
4
|
+
import { activatePersistentRuntime } from "./runtime-activation.mjs";
|
|
5
|
+
import { autostartStatus, installAutostart, startAutostart, stopAutostart } from "./service.mjs";
|
|
6
|
+
import { startOwnedServiceRuntime } from "./service-runtime.mjs";
|
|
7
|
+
import { acquireMachineServiceLockWithWait, acquireStartupLockWithWait, daemonLockPathForState, loadState, readDaemonLockOwner } from "./state.mjs";
|
|
8
|
+
import { workerHealth } from "./worker-health.mjs";
|
|
9
|
+
|
|
10
|
+
export function createActivateCommand({
|
|
11
|
+
chooseWorkspace,
|
|
12
|
+
prepareRemoteState,
|
|
13
|
+
createRemoteRuntime,
|
|
14
|
+
currentPackageVersion,
|
|
15
|
+
assertNodeVersion,
|
|
16
|
+
structuredLogger,
|
|
17
|
+
} = {}) {
|
|
18
|
+
for (const [name, value] of Object.entries({
|
|
19
|
+
chooseWorkspace,
|
|
20
|
+
prepareRemoteState,
|
|
21
|
+
createRemoteRuntime,
|
|
22
|
+
currentPackageVersion,
|
|
23
|
+
assertNodeVersion,
|
|
24
|
+
structuredLogger,
|
|
25
|
+
})) {
|
|
26
|
+
if (typeof value !== "function") throw new TypeError(`activate command requires ${name}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return async function activateCommand(args) {
|
|
30
|
+
assertNodeVersion();
|
|
31
|
+
const logger = createLogger({
|
|
32
|
+
level: args.json ? "error" : effectiveLogLevel(args),
|
|
33
|
+
format: effectiveLogFormat(args),
|
|
34
|
+
component: "activate",
|
|
35
|
+
});
|
|
36
|
+
const workspace = await chooseWorkspace(args, { promptOnFirstRun: false, save: true, allowPositional: true });
|
|
37
|
+
let state = loadState(workspace, { stateDir: args.stateDir });
|
|
38
|
+
if (!state.worker?.url) {
|
|
39
|
+
throw new Error("activate requires an existing deployment; run machine-mcp once interactively before persistent activation");
|
|
40
|
+
}
|
|
41
|
+
const activationArgs = { ...args, noAutostart: true, daemonOnly: false };
|
|
42
|
+
const expectedVersion = currentPackageVersion();
|
|
43
|
+
const result = await activatePersistentRuntime({
|
|
44
|
+
expectedVersion,
|
|
45
|
+
acquireStartupLock: async () => {
|
|
46
|
+
const lock = await acquireStartupLockWithWait(state, { operation: "activate", logger });
|
|
47
|
+
try {
|
|
48
|
+
state = loadState(workspace, { stateDir: args.stateDir });
|
|
49
|
+
if (!state.worker?.url) throw new Error("activate requires an existing deployment; run machine-mcp once interactively before persistent activation");
|
|
50
|
+
return lock;
|
|
51
|
+
} catch (error) {
|
|
52
|
+
lock.release();
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
acquireServiceLock: () => acquireMachineServiceLockWithWait({ operation: "activate", logger }),
|
|
57
|
+
inspectActivationOwnership: async () => {
|
|
58
|
+
const daemon = inspectWorkspaceDaemon(state);
|
|
59
|
+
const owner = daemon.alive && daemon.verified_service_daemon
|
|
60
|
+
? readDaemonLockOwner(daemonLockPathForState(state))
|
|
61
|
+
: null;
|
|
62
|
+
return {
|
|
63
|
+
daemon,
|
|
64
|
+
provider: await autostartStatus(),
|
|
65
|
+
previousRuntime: owner ? { version: owner.version, entryScript: owner.entryScript } : null,
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
stopAutostart: () => stopAutostart({ logger: structuredLogger(true) }),
|
|
69
|
+
acquireDaemonLock: () => acquireDaemonLockWithTakeover(state, {
|
|
70
|
+
takeOverServiceOwner: true,
|
|
71
|
+
ownerMetadata: { mode: "foreground", version: expectedVersion },
|
|
72
|
+
logger,
|
|
73
|
+
}),
|
|
74
|
+
prepareRemoteState: ({ onRemotePrepared } = {}) => prepareRemoteState({
|
|
75
|
+
args: activationArgs,
|
|
76
|
+
workspace,
|
|
77
|
+
state,
|
|
78
|
+
logger,
|
|
79
|
+
onRemotePrepared,
|
|
80
|
+
// Candidate activation requires an existing deployment. Do not repeat
|
|
81
|
+
// first-run account provisioning after Worker convergence: that extra
|
|
82
|
+
// admin-auth round trip can fail independently before relay readiness.
|
|
83
|
+
provisionInitialOwner: false,
|
|
84
|
+
}),
|
|
85
|
+
repairRemoteState: async ({ onRemotePrepared } = {}) => {
|
|
86
|
+
logger.warn("candidate device authentication was rejected; redeploying the same Worker once with the current device identity");
|
|
87
|
+
return prepareRemoteState({
|
|
88
|
+
args: { ...activationArgs, forceWorker: true, rotateSecrets: false },
|
|
89
|
+
workspace,
|
|
90
|
+
state,
|
|
91
|
+
logger,
|
|
92
|
+
onRemotePrepared,
|
|
93
|
+
provisionInitialOwner: false,
|
|
94
|
+
});
|
|
95
|
+
},
|
|
96
|
+
createRuntime: ({ daemonLock, readiness }) => createRemoteRuntime({
|
|
97
|
+
args: activationArgs,
|
|
98
|
+
workspace,
|
|
99
|
+
state,
|
|
100
|
+
daemonLock,
|
|
101
|
+
deviceSessionIdentity: readiness.deviceSessionIdentity,
|
|
102
|
+
exitOnTerminal: false,
|
|
103
|
+
}),
|
|
104
|
+
installAutostart: ({ deferOwnerCommit } = {}) => {
|
|
105
|
+
if (deferOwnerCommit !== true) throw new Error("persistent activation requires deferred service-owner commit");
|
|
106
|
+
return installAutostart({
|
|
107
|
+
workspace,
|
|
108
|
+
stateRoot: state.paths.stateRoot,
|
|
109
|
+
entryScript: process.argv[1],
|
|
110
|
+
version: expectedVersion,
|
|
111
|
+
logger: structuredLogger(true),
|
|
112
|
+
deferOwnerCommit: true,
|
|
113
|
+
});
|
|
114
|
+
},
|
|
115
|
+
startAutostart: ({ owner } = {}) => {
|
|
116
|
+
if (!owner || owner.status !== "pending") throw new Error("persistent activation requires the pending candidate service owner");
|
|
117
|
+
return startOwnedServiceRuntime({ logger: structuredLogger(true), owner });
|
|
118
|
+
},
|
|
119
|
+
startRecoveryAutostart: () => startAutostart({ logger: structuredLogger(true) }),
|
|
120
|
+
restorePreviousAutostart: () => startAutostart({ logger: structuredLogger(true) }),
|
|
121
|
+
inspectCandidateAutostart: () => inspectWorkspaceDaemon(state, {
|
|
122
|
+
expectedVersion, expectedEntryScript: process.argv[1],
|
|
123
|
+
}),
|
|
124
|
+
inspectPreviousAutostart: (identity) => inspectWorkspaceDaemon(state, {
|
|
125
|
+
expectedVersion: identity.version, expectedEntryScript: identity.entryScript,
|
|
126
|
+
}),
|
|
127
|
+
inspectDaemon: async () => inspectWorkspaceDaemon(state, {
|
|
128
|
+
expectedVersion, expectedEntryScript: process.argv[1],
|
|
129
|
+
}),
|
|
130
|
+
checkWorker: async () => workerHealth(state.worker.url, expectedVersion, { expectedWorkerName: state.worker.name }),
|
|
131
|
+
});
|
|
132
|
+
const convergence = result.convergence;
|
|
133
|
+
const payload = {
|
|
134
|
+
ok: true,
|
|
135
|
+
version: expectedVersion,
|
|
136
|
+
workspace: state.workspace.path,
|
|
137
|
+
worker: { name: state.worker.name, url: state.worker.url, health: convergence.worker },
|
|
138
|
+
daemon: convergence.daemon,
|
|
139
|
+
service: result.serviceStart,
|
|
140
|
+
candidate_relay_verified_before_handoff: result.candidateRelayVerified,
|
|
141
|
+
candidate_auth_recovery_redeployed: result.candidateRecoveryRedeployed,
|
|
142
|
+
activation_recovered: result.activationRecovered === true,
|
|
143
|
+
activation_recovery_reason: result.activationRecovered === true ? result.recoveryReason : null,
|
|
144
|
+
activation_recovery_detail: result.activationRecovered === true ? result.recoveryDetail : null,
|
|
145
|
+
};
|
|
146
|
+
if (args.json) console.log(JSON.stringify(payload, null, 2));
|
|
147
|
+
else {
|
|
148
|
+
if (result.activationRecovered === true) {
|
|
149
|
+
logger.warn(`persistent activation completed through verified candidate-service recovery (${result.recoveryReason}): ${result.recoveryDetail}`);
|
|
150
|
+
}
|
|
151
|
+
logger.success(`Persistent runtime activated: ${expectedVersion}`);
|
|
152
|
+
logger.safePlain(` Worker: ${state.worker.name}`);
|
|
153
|
+
logger.safePlain(` Daemon: pid ${convergence.daemon.pid}`);
|
|
154
|
+
logger.safePlain(" Candidate relay readiness was verified before the login service handoff.");
|
|
155
|
+
if (result.activationRecovered === true) {
|
|
156
|
+
logger.safePlain(" The exact candidate login service was independently verified after the foreground candidate ended.");
|
|
157
|
+
}
|
|
158
|
+
logger.safePlain(" The login service now owns the daemon; the activation terminal may close.");
|
|
159
|
+
}
|
|
160
|
+
return payload;
|
|
161
|
+
};
|
|
162
|
+
}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
3
2
|
import { join, resolve } from "node:path";
|
|
4
3
|
import process from "node:process";
|
|
5
4
|
import { createLogger } from "./log.mjs";
|
|
6
5
|
import { inspectResourceFile, loadManagedJobPlan, ManagedJobManager, publicResourceRegistry, validateResourceName } from "./managed-jobs.mjs";
|
|
7
6
|
import { generateRegisteredSshKey } from "./resource-operations.mjs";
|
|
8
|
-
import { readBoundedRegularFileSync } from "./secure-file.mjs";
|
|
9
7
|
import {
|
|
10
|
-
acquireStartupLockWithWait, expandHome, loadState,
|
|
8
|
+
acquireStartupLockWithWait, defaultStateRoot, expandHome, loadState, saveState,
|
|
11
9
|
} from "./state.mjs";
|
|
10
|
+
import { readBrowserPairing, readBrowserPairingPort } from "./browser-pairing-store.mjs";
|
|
11
|
+
import { startBrowserPairingLaunch } from "./browser-pairing-launch.mjs";
|
|
12
|
+
import { browserExtensionPathForRuntime } from "./browser-extension-path.mjs";
|
|
12
13
|
import { resolvePolicy } from "./cli-policy.mjs";
|
|
13
14
|
import { readLoopbackJson } from "./loopback-health.mjs";
|
|
14
15
|
|
|
@@ -18,7 +19,7 @@ export function createLocalAdminCommands(dependencies) {
|
|
|
18
19
|
if (typeof chooseWorkspace !== "function" || typeof confirm !== "function") {
|
|
19
20
|
throw new TypeError("local admin commands require chooseWorkspace and confirm dependencies");
|
|
20
21
|
}
|
|
21
|
-
const context = Object.freeze({ chooseWorkspace, confirm });
|
|
22
|
+
const context = Object.freeze({ chooseWorkspace, confirm, openExternal: dependencies.openExternal || openExternal });
|
|
22
23
|
return Object.freeze({
|
|
23
24
|
resourceCommand: (args) => resourceCommand(args, context),
|
|
24
25
|
browserCommand: (args) => browserCommand(args, context),
|
|
@@ -197,7 +198,8 @@ async function browserCommand(args, dependencies) {
|
|
|
197
198
|
}
|
|
198
199
|
|
|
199
200
|
function browserPathAction(args) {
|
|
200
|
-
const
|
|
201
|
+
const stateRoot = resolve(expandHome(args.stateDir || defaultStateRoot()));
|
|
202
|
+
const extensionPath = browserExtensionPathForRuntime({ stateRoot });
|
|
201
203
|
if (args.json) console.log(JSON.stringify({ extension_path: extensionPath }, null, 2));
|
|
202
204
|
else console.log(extensionPath);
|
|
203
205
|
}
|
|
@@ -207,46 +209,32 @@ async function browserStatusAction(args, { chooseWorkspace }) {
|
|
|
207
209
|
renderBrowserStatus(context.result, args.json === true);
|
|
208
210
|
}
|
|
209
211
|
|
|
210
|
-
async function browserPairAction(args, { chooseWorkspace }) {
|
|
212
|
+
async function browserPairAction(args, { chooseWorkspace, openExternal: openTarget }) {
|
|
211
213
|
const context = await browserCommandContext(args, chooseWorkspace);
|
|
212
214
|
if (!context.result.running) throw new Error("browser bridge is not reachable; keep machine-mcp running and retry");
|
|
213
|
-
|
|
215
|
+
const pairing = readBrowserPairing(context.stateRoot);
|
|
216
|
+
if (!pairing || pairing.port !== context.port) throw new Error("browser pairing state changed; retry setup");
|
|
217
|
+
const launch = await startBrowserPairingLaunch({ brokerPort: pairing.port, extensionToken: pairing.extensionToken });
|
|
218
|
+
try { await openTarget(launch.url); } catch (error) { launch.close(); throw error; }
|
|
214
219
|
if (args.json) {
|
|
215
220
|
console.log(JSON.stringify({ ...context.result, pairing_page_opened: true }, null, 2));
|
|
216
221
|
return;
|
|
217
222
|
}
|
|
218
223
|
console.log(`Extension path: ${context.extensionPath}`);
|
|
219
224
|
console.log("Load this directory in the Chromium profile you use every day; Machine Bridge does not install it into Playwright or a separate automation profile.");
|
|
220
|
-
console.log("Enable Developer mode, choose Load unpacked, and reload the
|
|
225
|
+
console.log("Enable Developer mode, choose Load unpacked once, and reload the same path after each Machine Bridge upgrade. Older local-candidate installs may need one Load unpacked migration to this stable path.");
|
|
221
226
|
console.log(`Pairing page opened: ${context.pairingUrl}`);
|
|
222
227
|
}
|
|
223
228
|
|
|
224
229
|
async function browserCommandContext(args, chooseWorkspace) {
|
|
225
|
-
const extensionPath = resolve(packageRoot, "browser-extension");
|
|
226
230
|
const workspace = await chooseWorkspace({ ...args, _: [] }, { promptOnFirstRun: false, save: false, allowPositional: false });
|
|
227
231
|
const state = loadState(workspace, { stateDir: args.stateDir });
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
const pairingUrl = `http://127.0.0.1:${pairing.port}/pair`;
|
|
235
|
-
const health = await readBrowserHealth(`http://127.0.0.1:${pairing.port}/healthz`);
|
|
236
|
-
return { extensionPath, pairingUrl, result: browserStatusResult(health, extensionPath, pairingUrl) };
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function readBrowserPairingState(pairingFile) {
|
|
240
|
-
let pairing;
|
|
241
|
-
try {
|
|
242
|
-
pairing = JSON.parse(readBoundedRegularFileSync(pairingFile, 64 * 1024).toString("utf8"));
|
|
243
|
-
} catch {
|
|
244
|
-
throw new Error("browser bridge state is invalid; restart machine-mcp to repair it");
|
|
245
|
-
}
|
|
246
|
-
const port = Number(pairing.port);
|
|
247
|
-
if (!/^[A-Za-z0-9_-]{32,100}$/.test(String(pairing.token || ""))) throw new Error("browser bridge state contains an invalid token");
|
|
248
|
-
if (!Number.isInteger(port) || port < 1024 || port > 65535) throw new Error("browser bridge state contains an invalid port");
|
|
249
|
-
return { port };
|
|
232
|
+
const extensionPath = browserExtensionPathForRuntime({ stateRoot: state.paths.stateRoot });
|
|
233
|
+
const port = readBrowserPairingPort(state.paths.stateRoot);
|
|
234
|
+
if (port === null) throw new Error("browser bridge is not initialized; start machine-mcp once, then run this command again");
|
|
235
|
+
const pairingUrl = `http://127.0.0.1:${port}/pair`;
|
|
236
|
+
const health = await readBrowserHealth(`http://127.0.0.1:${port}/healthz`);
|
|
237
|
+
return { extensionPath, pairingUrl, port, stateRoot: state.paths.stateRoot, result: browserStatusResult(health, extensionPath, pairingUrl) };
|
|
250
238
|
}
|
|
251
239
|
|
|
252
240
|
function readBrowserHealth(healthUrl) {
|
|
@@ -302,7 +290,7 @@ function openExternal(target) {
|
|
|
302
290
|
});
|
|
303
291
|
}
|
|
304
292
|
|
|
305
|
-
async function jobCommand(args, { chooseWorkspace
|
|
293
|
+
async function jobCommand(args, { chooseWorkspace }) {
|
|
306
294
|
const action = String(args._[0] || "list").toLowerCase();
|
|
307
295
|
const workspace = await chooseWorkspace({ ...args, _: [] }, { promptOnFirstRun: false, save: false, allowPositional: false });
|
|
308
296
|
const state = loadState(workspace, { stateDir: args.stateDir });
|
|
@@ -320,19 +308,6 @@ async function jobCommand(args, { chooseWorkspace, confirm }) {
|
|
|
320
308
|
else if (action === "read") result = manager.read({ job_id: args._[1] });
|
|
321
309
|
else if (action === "inspect") result = manager.inspectLocal({ job_id: args._[1] });
|
|
322
310
|
else if (action === "cancel") result = manager.cancel({ job_id: args._[1] });
|
|
323
|
-
else if (action === "approve") {
|
|
324
|
-
if (args.json && !args.yes) throw new Error("job approve --json requires --yes");
|
|
325
|
-
const inspection = manager.inspectLocal({ job_id: args._[1] });
|
|
326
|
-
if (!args.yes) {
|
|
327
|
-
console.log(JSON.stringify(inspection, null, 2));
|
|
328
|
-
const approved = await confirm(`Approve and execute managed job ${args._[1]}?`, false);
|
|
329
|
-
if (!approved) {
|
|
330
|
-
console.log("Managed job approval cancelled. Re-run with --yes after review to skip confirmation.");
|
|
331
|
-
return;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
result = manager.approve({ job_id: args._[1] }, { localOperator: true });
|
|
335
|
-
}
|
|
336
311
|
else if (action === "submit") {
|
|
337
312
|
const planPath = args._[1];
|
|
338
313
|
if (!planPath) throw new Error("job submit requires a JSON plan file");
|