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,98 @@
|
|
|
1
|
+
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { createMonotonicDeadline } from "./monotonic-deadline.mjs";
|
|
3
|
+
|
|
4
|
+
const CHALLENGE = /^[A-Za-z0-9_-]{32}$/;
|
|
5
|
+
const PROOF = /^[A-Za-z0-9_-]{43}$/;
|
|
6
|
+
const TOKEN = /^[A-Za-z0-9_-]{32,100}$/;
|
|
7
|
+
const AUTH_TTL_MS = 5_000;
|
|
8
|
+
const MAX_PENDING_AUTH = 64;
|
|
9
|
+
const ROLES = new Set(["extension", "runtime"]);
|
|
10
|
+
|
|
11
|
+
export const BROKER_AUTH_REQUEST_HEADER = "x-machine-bridge-broker-auth";
|
|
12
|
+
export const BROKER_AUTH_REQUEST_VALUE = "machine-bridge-browser-v2";
|
|
13
|
+
|
|
14
|
+
export function createBrokerAuthChallenge() {
|
|
15
|
+
return randomBytes(24).toString("base64url");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function createBrokerInitProof(token, role, clientChallenge) {
|
|
19
|
+
assertToken(token); assertRole(role);
|
|
20
|
+
if (!CHALLENGE.test(String(clientChallenge || ""))) throw new Error("browser broker authentication challenge is invalid");
|
|
21
|
+
return hmac(token, `machine-bridge-browser-${role}-init-v2\0${clientChallenge}`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function createBrokerServerProof(token, role, clientChallenge, serverNonce) {
|
|
25
|
+
return authProof(token, role, "server", clientChallenge, serverNonce);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function verifyBrokerServerProof(token, role, clientChallenge, serverNonce, proof) {
|
|
29
|
+
if (!PROOF.test(String(proof || ""))) return false;
|
|
30
|
+
return safeEqual(createBrokerServerProof(token, role, clientChallenge, serverNonce), proof);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function createBrokerClientProtocol(token, role, clientChallenge, serverNonce) {
|
|
34
|
+
assertRole(role);
|
|
35
|
+
const proof = authProof(token, role, "client", clientChallenge, serverNonce);
|
|
36
|
+
return `mbm-${role}-v2.${clientChallenge}.${serverNonce}.${proof}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function createBrokerAuthRegistry(token, role, options = {}) {
|
|
40
|
+
assertToken(token);
|
|
41
|
+
assertRole(role);
|
|
42
|
+
const now = typeof options.now === "function" ? options.now : undefined;
|
|
43
|
+
const pending = new Map();
|
|
44
|
+
return {
|
|
45
|
+
issue(clientChallenge, initProof) {
|
|
46
|
+
if (!CHALLENGE.test(String(clientChallenge || "")) || !PROOF.test(String(initProof || ""))) return null;
|
|
47
|
+
if (!safeEqual(createBrokerInitProof(token, role, clientChallenge), initProof)) return null;
|
|
48
|
+
prunePending(pending);
|
|
49
|
+
const existing = pending.get(clientChallenge);
|
|
50
|
+
if (existing && !existing.deadline.expired()) {
|
|
51
|
+
return { serverNonce: existing.serverNonce, serverProof: createBrokerServerProof(token, role, clientChallenge, existing.serverNonce) };
|
|
52
|
+
}
|
|
53
|
+
while (pending.size >= MAX_PENDING_AUTH) pending.delete(pending.keys().next().value);
|
|
54
|
+
const serverNonce = createBrokerAuthChallenge();
|
|
55
|
+
pending.set(clientChallenge, { serverNonce, deadline: createMonotonicDeadline(AUTH_TTL_MS, now) });
|
|
56
|
+
return { serverNonce, serverProof: createBrokerServerProof(token, role, clientChallenge, serverNonce) };
|
|
57
|
+
},
|
|
58
|
+
consume(protocol) {
|
|
59
|
+
const parsed = parseBrokerProtocol(protocol, role);
|
|
60
|
+
if (!parsed) return false;
|
|
61
|
+
const issued = pending.get(parsed.clientChallenge);
|
|
62
|
+
pending.delete(parsed.clientChallenge);
|
|
63
|
+
if (!issued || issued.deadline.expired() || issued.serverNonce !== parsed.serverNonce) return false;
|
|
64
|
+
return safeEqual(createBrokerClientProtocol(token, role, parsed.clientChallenge, parsed.serverNonce), protocol);
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function parseBrokerAuthResponse(headers) {
|
|
70
|
+
const serverNonce = String(headers?.get?.("x-machine-bridge-broker-nonce") || "");
|
|
71
|
+
const serverProof = String(headers?.get?.("x-machine-bridge-broker-proof") || "");
|
|
72
|
+
return CHALLENGE.test(serverNonce) && PROOF.test(serverProof) ? { serverNonce, serverProof } : null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function parseBrokerProtocol(value, role) {
|
|
76
|
+
const parts = String(value || "").split(".");
|
|
77
|
+
if (parts.length !== 4 || parts[0] !== `mbm-${role}-v2`) return null;
|
|
78
|
+
const [, clientChallenge, serverNonce, proof] = parts;
|
|
79
|
+
if (!CHALLENGE.test(clientChallenge) || !CHALLENGE.test(serverNonce) || !PROOF.test(proof)) return null;
|
|
80
|
+
return { clientChallenge, serverNonce };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function authProof(token, role, direction, clientChallenge, serverNonce) {
|
|
84
|
+
assertToken(token); assertRole(role);
|
|
85
|
+
if (!CHALLENGE.test(String(clientChallenge || "")) || !CHALLENGE.test(String(serverNonce || ""))) {
|
|
86
|
+
throw new Error("browser broker authentication challenge is invalid");
|
|
87
|
+
}
|
|
88
|
+
return hmac(token, `machine-bridge-browser-${role}-${direction}-v2\0${clientChallenge}\0${serverNonce}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function hmac(token, message) { return createHmac("sha256", token).update(message).digest("base64url"); }
|
|
92
|
+
function assertToken(value) { if (!TOKEN.test(String(value || ""))) throw new Error("browser broker credential is invalid"); }
|
|
93
|
+
function assertRole(value) { if (!ROLES.has(value)) throw new Error("browser broker authentication role is invalid"); }
|
|
94
|
+
function safeEqual(left, right) {
|
|
95
|
+
const a = Buffer.from(String(left)); const b = Buffer.from(String(right));
|
|
96
|
+
return a.length === b.length && timingSafeEqual(a, b);
|
|
97
|
+
}
|
|
98
|
+
function prunePending(pending) { for (const [key, issued] of pending) if (issued.deadline.expired()) pending.delete(key); }
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
2
|
import { clampInt } from "./browser-command.mjs";
|
|
3
|
-
import { closeProtocolSocket, parseBrowserSocketMessage, safeSocketSend } from "./browser-extension-protocol.mjs";
|
|
4
|
-
|
|
3
|
+
import { browserPostDispatchTransportFailure, closeProtocolSocket, normalizeRuntimeBrowserRequest, parseBrowserSocketMessage, safeSocketSend } from "./browser-extension-protocol.mjs";
|
|
5
4
|
export class BrowserBrokerRoutes {
|
|
6
|
-
constructor({ maximum, getExtensionSocket, extensionConnected, extensionStatusInfo, extensionReloadRequired }) {
|
|
5
|
+
constructor({ maximum, getExtensionSocket, extensionConnected, extensionStatusInfo, extensionReloadRequired, extensionGeneration = () => 0 }) {
|
|
7
6
|
this.maximum = maximum;
|
|
8
7
|
this.getExtensionSocket = getExtensionSocket;
|
|
9
8
|
this.extensionConnected = extensionConnected;
|
|
10
9
|
this.extensionStatusInfo = extensionStatusInfo;
|
|
11
|
-
this.extensionReloadRequired = extensionReloadRequired;
|
|
10
|
+
this.extensionReloadRequired = extensionReloadRequired; this.extensionGeneration = extensionGeneration;
|
|
12
11
|
this.clients = new Set();
|
|
13
12
|
this.routes = new Map();
|
|
14
13
|
}
|
|
15
|
-
|
|
14
|
+
snapshot() {
|
|
15
|
+
return Object.freeze({ runtime_clients: this.clients.size, routed_requests: this.routes.size });
|
|
16
|
+
}
|
|
16
17
|
acceptClient(socket) {
|
|
17
18
|
this.clients.add(socket);
|
|
18
19
|
socket.on("message", (data) => this.handleClientMessage(socket, data));
|
|
@@ -25,6 +26,7 @@ export class BrowserBrokerRoutes {
|
|
|
25
26
|
extension_connected: this.extensionConnected(),
|
|
26
27
|
extension_info: this.extensionStatusInfo(),
|
|
27
28
|
extension_reload_required: this.extensionReloadRequired(),
|
|
29
|
+
extension_generation: this.extensionGeneration(),
|
|
28
30
|
});
|
|
29
31
|
}
|
|
30
32
|
|
|
@@ -44,16 +46,14 @@ export class BrowserBrokerRoutes {
|
|
|
44
46
|
closeProtocolSocket(socket, parsed.code, parsed.reason);
|
|
45
47
|
return;
|
|
46
48
|
}
|
|
47
|
-
const
|
|
48
|
-
if (
|
|
49
|
-
if (
|
|
50
|
-
this.cancelClientRequest(socket,
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
if (message.type !== "request" || typeof message.id !== "string" || typeof message.method !== "string") {
|
|
54
|
-
closeProtocolSocket(socket, 1002, "invalid runtime protocol message");
|
|
49
|
+
const incoming = parsed.message;
|
|
50
|
+
if (incoming.type === "ping") return;
|
|
51
|
+
if (incoming.type === "cancel" && typeof incoming.id === "string") {
|
|
52
|
+
this.cancelClientRequest(socket, incoming.id);
|
|
55
53
|
return;
|
|
56
54
|
}
|
|
55
|
+
const message = normalizeRuntimeBrowserRequest(incoming);
|
|
56
|
+
if (!message) { closeProtocolSocket(socket, 1002, "invalid runtime protocol message"); return; }
|
|
57
57
|
if (!this.extensionConnected()) {
|
|
58
58
|
safeSocketSend(socket, { type: "response", id: message.id, ok: false, error: "browser extension is not connected" });
|
|
59
59
|
return;
|
|
@@ -73,15 +73,24 @@ export class BrowserBrokerRoutes {
|
|
|
73
73
|
const route = this.routes.get(routedId);
|
|
74
74
|
if (!route) return;
|
|
75
75
|
this.routes.delete(routedId);
|
|
76
|
-
safeSocketSend(socket, {
|
|
76
|
+
safeSocketSend(socket, {
|
|
77
|
+
type: "response", id: message.id, ok: false,
|
|
78
|
+
error: postDispatchRouteFailure(route, "browser broker request timed out"),
|
|
79
|
+
});
|
|
77
80
|
this.sendExtension({ type: "cancel", id: routedId });
|
|
78
81
|
}, timeoutMs);
|
|
79
82
|
timeout.unref?.();
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
const route = { socket, id: message.id, timeout, method: message.method, dispatchAttempted: false };
|
|
84
|
+
this.routes.set(routedId, route);
|
|
85
|
+
const send = this.sendExtensionRequest({ ...message, id: routedId, timeout_ms: timeoutMs });
|
|
86
|
+
route.dispatchAttempted = send.attempted;
|
|
87
|
+
if (send.ok) return;
|
|
82
88
|
clearTimeout(timeout);
|
|
83
89
|
this.routes.delete(routedId);
|
|
84
|
-
safeSocketSend(socket, {
|
|
90
|
+
safeSocketSend(socket, {
|
|
91
|
+
type: "response", id: message.id, ok: false,
|
|
92
|
+
error: postDispatchRouteFailure(route, "browser extension send failed"),
|
|
93
|
+
});
|
|
85
94
|
}
|
|
86
95
|
|
|
87
96
|
cancelClientRequest(socket, requestId) {
|
|
@@ -98,7 +107,16 @@ export class BrowserBrokerRoutes {
|
|
|
98
107
|
if (!route) return false;
|
|
99
108
|
clearTimeout(route.timeout);
|
|
100
109
|
this.routes.delete(message.id);
|
|
101
|
-
|
|
110
|
+
if (message.ok === true && message.result && typeof message.result === "object" && !Array.isArray(message.result)) {
|
|
111
|
+
safeSocketSend(route.socket, { type: "response", id: route.id, ok: true, result: message.result });
|
|
112
|
+
} else if (message.ok === false && typeof message.error === "string" && message.error && !message.error.includes("\0") && message.error.length <= 2000) {
|
|
113
|
+
safeSocketSend(route.socket, { type: "response", id: route.id, ok: false, error: message.error });
|
|
114
|
+
} else {
|
|
115
|
+
safeSocketSend(route.socket, {
|
|
116
|
+
type: "response", id: route.id, ok: false,
|
|
117
|
+
error: postDispatchRouteFailure(route, "browser extension response was malformed"),
|
|
118
|
+
});
|
|
119
|
+
}
|
|
102
120
|
return true;
|
|
103
121
|
}
|
|
104
122
|
|
|
@@ -108,6 +126,7 @@ export class BrowserBrokerRoutes {
|
|
|
108
126
|
extension_connected: connected,
|
|
109
127
|
extension_info: connected ? this.extensionStatusInfo() : null,
|
|
110
128
|
extension_reload_required: this.extensionReloadRequired(),
|
|
129
|
+
extension_generation: connected ? this.extensionGeneration() : 0,
|
|
111
130
|
};
|
|
112
131
|
for (const client of this.clients) safeSocketSend(client, message);
|
|
113
132
|
}
|
|
@@ -115,7 +134,10 @@ export class BrowserBrokerRoutes {
|
|
|
115
134
|
rejectAll(message) {
|
|
116
135
|
for (const [id, route] of this.routes) {
|
|
117
136
|
clearTimeout(route.timeout);
|
|
118
|
-
safeSocketSend(route.socket, {
|
|
137
|
+
safeSocketSend(route.socket, {
|
|
138
|
+
type: "response", id: route.id, ok: false,
|
|
139
|
+
error: postDispatchRouteFailure(route, message),
|
|
140
|
+
});
|
|
119
141
|
this.routes.delete(id);
|
|
120
142
|
}
|
|
121
143
|
}
|
|
@@ -123,7 +145,8 @@ export class BrowserBrokerRoutes {
|
|
|
123
145
|
close(message) {
|
|
124
146
|
this.rejectAll(message);
|
|
125
147
|
for (const client of this.clients) {
|
|
126
|
-
try { client.close(1001, "runtime stopped"); }
|
|
148
|
+
try { client.close(1001, "runtime stopped"); }
|
|
149
|
+
catch { /* Runtime shutdown is already authoritative; a concurrently closed broker client needs no retry. */ }
|
|
127
150
|
}
|
|
128
151
|
this.clients.clear();
|
|
129
152
|
}
|
|
@@ -133,4 +156,24 @@ export class BrowserBrokerRoutes {
|
|
|
133
156
|
if (!socket || socket.readyState !== 1) return false;
|
|
134
157
|
return safeSocketSend(socket, message);
|
|
135
158
|
}
|
|
159
|
+
|
|
160
|
+
sendExtensionRequest(message) {
|
|
161
|
+
const socket = this.getExtensionSocket();
|
|
162
|
+
if (!socket || socket.readyState !== 1) return { ok: false, attempted: false };
|
|
163
|
+
let payload;
|
|
164
|
+
try { payload = JSON.stringify(message); }
|
|
165
|
+
catch { return { ok: false, attempted: false }; }
|
|
166
|
+
try {
|
|
167
|
+
socket.send(payload);
|
|
168
|
+
return { ok: true, attempted: true };
|
|
169
|
+
} catch {
|
|
170
|
+
return { ok: false, attempted: true };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function postDispatchRouteFailure(route, fallback) {
|
|
176
|
+
return route?.dispatchAttempted
|
|
177
|
+
? browserPostDispatchTransportFailure(route.method, fallback)
|
|
178
|
+
: String(fallback || "browser transport failed");
|
|
136
179
|
}
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import { createServer } from "node:http";
|
|
2
2
|
import { WebSocketServer } from "ws";
|
|
3
|
-
import { isAllowedExtensionOrigin, isAllowedLoopbackHost } from "./browser-pairing-
|
|
3
|
+
import { isAllowedExtensionOrigin, isAllowedLoopbackHost } from "./browser-pairing-http.mjs";
|
|
4
|
+
import { EXPECTED_EXTENSION_ID } from "./browser-extension-identity.mjs";
|
|
5
|
+
import { createBrokerAuthRegistry } from "./browser-broker-auth.mjs";
|
|
6
|
+
import { createBrowserBrokerAuthHttpHandler } from "./browser-broker-auth-http.mjs";
|
|
4
7
|
|
|
5
|
-
export async function startBrowserBrokerServer({ port,
|
|
6
|
-
const
|
|
8
|
+
export async function startBrowserBrokerServer({ port, extensionToken, runtimeToken, maxPayload, onHttp, onSocket }) {
|
|
9
|
+
const runtimeAuth = createBrokerAuthRegistry(runtimeToken, "runtime");
|
|
10
|
+
const extensionAuth = createBrokerAuthRegistry(extensionToken, "extension");
|
|
11
|
+
const handleAuthHttp = createBrowserBrokerAuthHttpHandler({ port, extensionToken, runtimeAuth, extensionAuth });
|
|
12
|
+
const server = createServer((request, response) => {
|
|
13
|
+
if (!handleAuthHttp(request, response)) onHttp(request, response);
|
|
14
|
+
});
|
|
7
15
|
const wss = new WebSocketServer({ noServer: true, maxPayload });
|
|
8
16
|
server.on("upgrade", (request, socket, head) => {
|
|
9
17
|
try {
|
|
@@ -16,8 +24,8 @@ export async function startBrowserBrokerServer({ port, token, maxPayload, onHttp
|
|
|
16
24
|
const protocol = String(request.headers["sec-websocket-protocol"] || "");
|
|
17
25
|
const origin = String(request.headers.origin || "");
|
|
18
26
|
let role = "";
|
|
19
|
-
if (url.pathname === "/extension" &&
|
|
20
|
-
if (url.pathname === "/runtime" &&
|
|
27
|
+
if (url.pathname === "/extension" && isAllowedExtensionOrigin(origin, EXPECTED_EXTENSION_ID) && extensionAuth.consume(protocol)) role = "extension";
|
|
28
|
+
if (url.pathname === "/runtime" && !origin && runtimeAuth.consume(protocol)) role = "runtime";
|
|
21
29
|
if (!role) {
|
|
22
30
|
rejectUpgrade(socket, "401 Unauthorized");
|
|
23
31
|
return;
|
|
@@ -38,8 +46,10 @@ export async function startBrowserBrokerServer({ port, token, maxPayload, onHttp
|
|
|
38
46
|
};
|
|
39
47
|
const onError = (error) => {
|
|
40
48
|
cleanup();
|
|
41
|
-
try { wss.close(); }
|
|
42
|
-
|
|
49
|
+
try { wss.close(); }
|
|
50
|
+
catch { /* Startup failure already owns the result; closing an unopened/half-open WebSocket server is best-effort. */ }
|
|
51
|
+
try { server.close(); }
|
|
52
|
+
catch { /* Startup failure already owns the result; closing an unopened/half-open HTTP server is best-effort. */ }
|
|
43
53
|
rejectPromise(error);
|
|
44
54
|
};
|
|
45
55
|
const onListening = () => {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BridgeError } from "./errors.mjs";
|
|
2
|
+
|
|
1
3
|
const NAVIGATION_ACTIONS = new Set(["navigate", "reload", "back", "forward"]);
|
|
2
4
|
const PAGE_ACTIONS = new Set([
|
|
3
5
|
"click", "double_click", "hover", "fill", "type_text", "select", "check", "uncheck",
|
|
@@ -7,17 +9,37 @@ const FORM_ACTIONS = new Set(["fill", "select", "check", "uncheck", "click"]);
|
|
|
7
9
|
const WAIT_STATES = new Set(["attached", "detached", "visible", "hidden", "enabled", "editable", "checked", "unchecked"]);
|
|
8
10
|
const LOAD_STATES = new Set(["domcontentloaded", "complete"]);
|
|
9
11
|
const INPUT_MODES = new Set(["auto", "trusted", "dom"]);
|
|
12
|
+
const SNAPSHOT_IDENTITY_STRING_FIELDS = Object.freeze([
|
|
13
|
+
"tag", "type", "role", "name", "id", "field_name", "label", "placeholder", "href",
|
|
14
|
+
]);
|
|
15
|
+
const SNAPSHOT_IDENTITY_BOOLEAN_FIELDS = Object.freeze(["sensitive", "in_shadow_dom"]);
|
|
10
16
|
|
|
11
17
|
export function normalizeBrowserAction(value) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
if (typeof value !== "string" || (!NAVIGATION_ACTIONS.has(value) && !PAGE_ACTIONS.has(value))) throw new Error("unsupported browser action");
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function browserPairingLaunchCommand(url, platform = process.platform) {
|
|
23
|
+
if (platform === "darwin") return { cmd: "open", argv: [url] };
|
|
24
|
+
if (platform === "win32") return { cmd: "cmd.exe", argv: ["/d", "/s", "/c", "start", "", url] };
|
|
25
|
+
return { cmd: "xdg-open", argv: [url] };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function browserPairingLaunchUnknown() {
|
|
29
|
+
return new BridgeError("execution_failed", "browser pairing page may have been opened; the launch outcome is unknown. Inspect the browser before retrying.", {
|
|
30
|
+
expose: true, retryable: false,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function browserPairingLaunchUnavailable() {
|
|
35
|
+
return new BridgeError("unavailable", "browser pairing launcher unavailable before dispatch", {
|
|
36
|
+
retryable: true, details: { reason: "browser_pairing_launcher_unavailable_before_dispatch" },
|
|
37
|
+
});
|
|
15
38
|
}
|
|
16
39
|
|
|
17
40
|
export function normalizeFormAction(value) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return action;
|
|
41
|
+
if (typeof value !== "string" || !FORM_ACTIONS.has(value)) throw new Error("unsupported form field action");
|
|
42
|
+
return value;
|
|
21
43
|
}
|
|
22
44
|
|
|
23
45
|
export function normalizeBrowserSelector(value, action = "") {
|
|
@@ -42,29 +64,45 @@ export function normalizeBrowserSelector(value, action = "") {
|
|
|
42
64
|
}
|
|
43
65
|
|
|
44
66
|
export function normalizeInputMode(value) {
|
|
45
|
-
if (value === undefined
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
67
|
+
if (value === undefined) return "auto";
|
|
68
|
+
if (typeof value !== "string" || !INPUT_MODES.has(value)) throw new Error("input_mode must be auto, trusted, or dom");
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function normalizeBrowserSnapshotIdentity(value) {
|
|
73
|
+
if (value === undefined || value === null) return null;
|
|
74
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("expected_ref_identity must be an object");
|
|
75
|
+
const allowed = new Set([...SNAPSHOT_IDENTITY_STRING_FIELDS, ...SNAPSHOT_IDENTITY_BOOLEAN_FIELDS]);
|
|
76
|
+
for (const key of Object.keys(value)) if (!allowed.has(key)) throw new Error(`unknown expected_ref_identity field: ${key}`);
|
|
77
|
+
const output = {};
|
|
78
|
+
for (const key of SNAPSHOT_IDENTITY_STRING_FIELDS) {
|
|
79
|
+
if (!Object.hasOwn(value, key)) continue;
|
|
80
|
+
output[key] = optionalString(value[key], `expected_ref_identity.${key}`, key === "href" ? 8192 : 2000);
|
|
81
|
+
}
|
|
82
|
+
for (const key of SNAPSHOT_IDENTITY_BOOLEAN_FIELDS) {
|
|
83
|
+
if (!Object.hasOwn(value, key)) continue;
|
|
84
|
+
if (typeof value[key] !== "boolean") throw new Error(`expected_ref_identity.${key} must be a boolean`);
|
|
85
|
+
output[key] = value[key];
|
|
86
|
+
}
|
|
87
|
+
return Object.keys(output).length ? output : null;
|
|
49
88
|
}
|
|
50
89
|
|
|
51
90
|
export function normalizeNavigationWait(value) {
|
|
52
|
-
if (value === undefined
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return wait;
|
|
91
|
+
if (value === undefined) return "none";
|
|
92
|
+
if (typeof value !== "string" || !["none", ...LOAD_STATES].includes(value)) throw new Error("wait_for must be none, domcontentloaded, or complete");
|
|
93
|
+
return value;
|
|
56
94
|
}
|
|
57
95
|
|
|
58
96
|
export function normalizeBrowserWait(args = {}) {
|
|
59
97
|
const selector = args.selector === undefined ? null : normalizeBrowserSelector(args.selector, "wait");
|
|
60
|
-
const state = args.state === undefined
|
|
61
|
-
|
|
62
|
-
: String(args.state);
|
|
98
|
+
const state = args.state === undefined ? (selector ? "visible" : "") : args.state;
|
|
99
|
+
if (typeof state !== "string") throw new Error("state is not a supported browser wait state");
|
|
63
100
|
if (state && !WAIT_STATES.has(state)) throw new Error("state is not a supported browser wait state");
|
|
64
101
|
if (state && !selector) throw new Error("state requires selector");
|
|
65
102
|
const text = optionalString(args.text, "text", 4000);
|
|
66
103
|
const urlContains = optionalString(args.url_contains, "url_contains", 32768);
|
|
67
|
-
const loadState = args.load_state === undefined
|
|
104
|
+
const loadState = args.load_state === undefined ? "" : args.load_state;
|
|
105
|
+
if (typeof loadState !== "string") throw new Error("load_state must be domcontentloaded or complete");
|
|
68
106
|
if (loadState && !LOAD_STATES.has(loadState)) throw new Error("load_state must be domcontentloaded or complete");
|
|
69
107
|
if (!selector && !text && !urlContains && !loadState) throw new Error("browser_wait requires selector, text, url_contains, or load_state");
|
|
70
108
|
const timeoutSeconds = clampInt(args.timeout_seconds, 30, 1, 120);
|
|
@@ -81,12 +119,13 @@ export function normalizeBrowserWait(args = {}) {
|
|
|
81
119
|
}
|
|
82
120
|
|
|
83
121
|
export function normalizeTabCommand(args = {}) {
|
|
84
|
-
const action =
|
|
85
|
-
if (!["new", "activate", "close"].includes(action)) throw new Error("browser tab action must be new, activate, or close");
|
|
122
|
+
const action = args.action;
|
|
123
|
+
if (typeof action !== "string" || !["new", "activate", "close"].includes(action)) throw new Error("browser tab action must be new, activate, or close");
|
|
86
124
|
const tabId = optionalInteger(args.tab_id, "tab_id", 1, Number.MAX_SAFE_INTEGER);
|
|
87
125
|
const rawUrl = optionalString(args.url, "url", 32768);
|
|
88
126
|
if (["activate", "close"].includes(action) && !tabId) throw new Error(`${action} requires tab_id`);
|
|
89
127
|
if (action !== "new" && rawUrl) throw new Error("url is only valid for new tabs");
|
|
128
|
+
if (args.active !== undefined && typeof args.active !== "boolean") throw new Error("active must be boolean");
|
|
90
129
|
return {
|
|
91
130
|
action,
|
|
92
131
|
tabId,
|
|
@@ -96,15 +135,27 @@ export function normalizeTabCommand(args = {}) {
|
|
|
96
135
|
}
|
|
97
136
|
|
|
98
137
|
export function validateNavigationUrl(value) {
|
|
99
|
-
if (!value) throw new Error("navigate requires url");
|
|
138
|
+
if (typeof value !== "string" || !value) throw new Error("navigate requires url");
|
|
100
139
|
let parsed;
|
|
101
140
|
try { parsed = new URL(value); } catch { throw new Error("url must be an absolute URL"); }
|
|
102
141
|
if (!["http:", "https:", "file:"].includes(parsed.protocol)) throw new Error("url protocol must be http, https, or file");
|
|
103
142
|
return parsed.href;
|
|
104
143
|
}
|
|
105
144
|
|
|
145
|
+
export function optionalBoolean(value, label, fallback) {
|
|
146
|
+
if (value === undefined) return fallback;
|
|
147
|
+
if (typeof value !== "boolean") throw new Error(`${label} must be boolean`);
|
|
148
|
+
return value;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function normalizeImageFormat(value, label = "format") {
|
|
152
|
+
if (value === undefined) return "png";
|
|
153
|
+
if (typeof value !== "string" || !["png", "jpeg"].includes(value)) throw new Error(`${label} must be png or jpeg`);
|
|
154
|
+
return value;
|
|
155
|
+
}
|
|
156
|
+
|
|
106
157
|
export function optionalString(value, label, maxLength) {
|
|
107
|
-
if (value === undefined
|
|
158
|
+
if (value === undefined) return "";
|
|
108
159
|
if (typeof value !== "string" || value.includes("\0") || value.length > maxLength) {
|
|
109
160
|
throw new Error(`${label} must be a string of at most ${maxLength} characters without NUL bytes`);
|
|
110
161
|
}
|
|
@@ -112,15 +163,13 @@ export function optionalString(value, label, maxLength) {
|
|
|
112
163
|
}
|
|
113
164
|
|
|
114
165
|
export function optionalInteger(value, label, min, max) {
|
|
115
|
-
if (value === undefined
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
return number;
|
|
166
|
+
if (value === undefined) return null;
|
|
167
|
+
if (!Number.isSafeInteger(value) || value < min || value > max) throw new Error(`${label} must be an integer from ${min} to ${max}`);
|
|
168
|
+
return value;
|
|
119
169
|
}
|
|
120
170
|
|
|
121
171
|
export function clampInt(value, fallback, min, max) {
|
|
122
|
-
if (value === undefined
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
return number;
|
|
172
|
+
if (value === undefined) return fallback;
|
|
173
|
+
if (!Number.isSafeInteger(value) || value < min || value > max) throw new Error(`expected an integer from ${min} to ${max}`);
|
|
174
|
+
return value;
|
|
126
175
|
}
|