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,266 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { clampInt, normalizeBrowserSnapshotIdentity, normalizeImageFormat, optionalBoolean, optionalInteger, optionalString } from "./browser-command.mjs";
|
|
3
|
+
|
|
4
|
+
const MAX_BROWSER_SCREENSHOT_BYTES = 32 * 1024 * 1024;
|
|
5
|
+
|
|
6
|
+
export function normalizeBackendNodeActionMetadata(args = {}) {
|
|
7
|
+
const action = args.action;
|
|
8
|
+
if (typeof action !== "string" || !["click", "double_click", "hover", "drag", "scroll", "press", "type_text", "fill", "check", "uncheck", "submit"].includes(action)) {
|
|
9
|
+
throw new Error("snapshot backend action must be click, double_click, hover, drag, scroll, press, type_text, fill, check, uncheck, or submit");
|
|
10
|
+
}
|
|
11
|
+
const backendNodeId = requiredPositiveInteger(args.backend_node_id, "backend_node_id");
|
|
12
|
+
const destinationBackendNodeId = action === "drag"
|
|
13
|
+
? requiredPositiveInteger(args.destination_backend_node_id, "destination_backend_node_id")
|
|
14
|
+
: null;
|
|
15
|
+
const scrollDeltas = action === "scroll" ? normalizeScrollDeltas(args) : null;
|
|
16
|
+
const timeoutSeconds = clampInt(args.timeout_seconds, 30, 1, 120);
|
|
17
|
+
const payload = {
|
|
18
|
+
tabId: optionalInteger(args.tab_id, "tab_id", 1, Number.MAX_SAFE_INTEGER),
|
|
19
|
+
action,
|
|
20
|
+
backendNodeId,
|
|
21
|
+
extensionFrameId: optionalAuthorityInteger(args.extension_frame_id, "extension_frame_id"),
|
|
22
|
+
expectedDocumentEpoch: optionalEpoch(args.document_epoch),
|
|
23
|
+
expectedFrameDocumentEpoch: optionalEpoch(args.frame_document_epoch),
|
|
24
|
+
expectedFrameUrl: optionalFrameUrl(args.frame_url),
|
|
25
|
+
extensionRef: optionalString(args.extension_ref, "extension_ref", 100),
|
|
26
|
+
expectedIdentity: normalizeBrowserSnapshotIdentity(args.expected_ref_identity),
|
|
27
|
+
...(action === "drag" ? {
|
|
28
|
+
destinationBackendNodeId,
|
|
29
|
+
destinationExtensionFrameId: optionalAuthorityInteger(args.destination_extension_frame_id, "destination_extension_frame_id"),
|
|
30
|
+
destinationExpectedFrameDocumentEpoch: optionalEpoch(args.destination_frame_document_epoch),
|
|
31
|
+
destinationExpectedFrameUrl: optionalFrameUrl(args.destination_frame_url),
|
|
32
|
+
destinationExtensionRef: optionalString(args.destination_extension_ref, "destination_extension_ref", 100),
|
|
33
|
+
destinationExpectedIdentity: normalizeBrowserSnapshotIdentity(args.destination_expected_ref_identity),
|
|
34
|
+
} : {}),
|
|
35
|
+
...(scrollDeltas || {}),
|
|
36
|
+
value: args.value === undefined ? undefined : requiredText(args.value, "value", 131072),
|
|
37
|
+
key: args.key === undefined ? undefined : requiredText(args.key, "key", 100),
|
|
38
|
+
};
|
|
39
|
+
return { payload, timeoutSeconds };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class BrowserComputerObservationService {
|
|
43
|
+
constructor({ authorizeTool, request, bridgeStatus, inspectPage, screenshot }) {
|
|
44
|
+
this.authorizeTool = authorizeTool;
|
|
45
|
+
this.request = request;
|
|
46
|
+
this.bridgeStatus = bridgeStatus;
|
|
47
|
+
this.inspectPage = inspectPage;
|
|
48
|
+
this.screenshot = screenshot;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async documentState(args = {}, context = {}) {
|
|
52
|
+
this.authorizeTool("computer_act");
|
|
53
|
+
return this.request("document_state", {
|
|
54
|
+
tabId: optionalInteger(args.tab_id, "tab_id", 1, Number.MAX_SAFE_INTEGER),
|
|
55
|
+
}, clampInt(args.timeout_seconds, 5, 1, 30), context);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
preflightBackendNodeAction(args = {}) {
|
|
59
|
+
this.authorizeTool("computer_act");
|
|
60
|
+
return normalizeBackendNodeActionMetadata(args);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async backendNodeAction(args = {}, context = {}) {
|
|
64
|
+
this.authorizeTool("computer_act");
|
|
65
|
+
const prepared = normalizeBackendNodeActionMetadata(args);
|
|
66
|
+
return this.request("backend_node_action", prepared.payload, prepared.timeoutSeconds, context);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async pointAction(args = {}, context = {}) {
|
|
70
|
+
this.authorizeTool("computer_act");
|
|
71
|
+
const action = args.action;
|
|
72
|
+
if (typeof action !== "string" || !["click", "double_click", "hover", "drag", "scroll"].includes(action)) throw new Error("visual point action must be click, double_click, hover, drag, or scroll");
|
|
73
|
+
const scrollDeltas = action === "scroll" ? normalizeScrollDeltas(args) : null;
|
|
74
|
+
return this.request("point_action", {
|
|
75
|
+
tabId: optionalInteger(args.tab_id, "tab_id", 1, Number.MAX_SAFE_INTEGER),
|
|
76
|
+
action,
|
|
77
|
+
normalizedX: normalizedCoordinate(args.normalized_x, "normalized_x"),
|
|
78
|
+
normalizedY: normalizedCoordinate(args.normalized_y, "normalized_y"),
|
|
79
|
+
...(action === "drag" ? {
|
|
80
|
+
destinationNormalizedX: normalizedCoordinate(args.destination_normalized_x, "destination_normalized_x"),
|
|
81
|
+
destinationNormalizedY: normalizedCoordinate(args.destination_normalized_y, "destination_normalized_y"),
|
|
82
|
+
} : {}),
|
|
83
|
+
...(scrollDeltas || {}),
|
|
84
|
+
expectedDocumentEpoch: optionalEpoch(args.document_epoch),
|
|
85
|
+
expectedViewport: normalizeExpectedViewport(args.viewport),
|
|
86
|
+
expectedScreenshotSha256: optionalSha256(args.screenshot_sha256),
|
|
87
|
+
screenshotFormat: normalizeImageFormat(args.screenshot_format, "screenshot_format"),
|
|
88
|
+
screenshotQuality: clampInt(args.screenshot_quality, 90, 1, 100),
|
|
89
|
+
}, clampInt(args.timeout_seconds, 30, 1, 120), context);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async observe(args = {}, context = {}) {
|
|
93
|
+
this.authorizeTool("computer_observe");
|
|
94
|
+
const timeoutSeconds = clampInt(args.timeout_seconds, 30, 1, 120);
|
|
95
|
+
const normalized = {
|
|
96
|
+
tab_id: optionalInteger(args.tab_id, "tab_id", 1, Number.MAX_SAFE_INTEGER),
|
|
97
|
+
all_frames: optionalBoolean(args.all_frames, "all_frames", true),
|
|
98
|
+
max_elements: clampInt(args.max_elements, 300, 1, 1000),
|
|
99
|
+
max_ax_nodes: clampInt(args.max_ax_nodes, 600, 1, 2000),
|
|
100
|
+
max_frames: clampInt(args.max_frames, 32, 1, 64),
|
|
101
|
+
ax_depth: clampInt(args.ax_depth, 12, 1, 16),
|
|
102
|
+
include_values: optionalBoolean(args.include_values, "include_values", false),
|
|
103
|
+
include_screenshot: optionalBoolean(args.include_screenshot, "include_screenshot", true),
|
|
104
|
+
screenshot_format: normalizeImageFormat(args.screenshot_format, "screenshot_format"),
|
|
105
|
+
screenshot_quality: clampInt(args.screenshot_quality, 90, 1, 100),
|
|
106
|
+
focus_query: optionalFocusQuery(args.focus_query),
|
|
107
|
+
};
|
|
108
|
+
const capabilities = new Set(this.bridgeStatus().extensionInfo?.capabilities || []);
|
|
109
|
+
if (!capabilities.has("computer_observation_v1")) return this.observeLegacy(normalized, timeoutSeconds, context);
|
|
110
|
+
|
|
111
|
+
const result = await this.request("observe_computer", {
|
|
112
|
+
tabId: normalized.tab_id, allFrames: normalized.all_frames, maxElements: normalized.max_elements, maxAxNodes: normalized.max_ax_nodes,
|
|
113
|
+
maxFrames: normalized.max_frames, axDepth: normalized.ax_depth, includeValues: normalized.include_values, includeScreenshot: normalized.include_screenshot,
|
|
114
|
+
format: normalized.screenshot_format, quality: normalized.screenshot_quality, focusQuery: normalized.focus_query,
|
|
115
|
+
}, timeoutSeconds, context);
|
|
116
|
+
const imageContent = computerObservationImage(result);
|
|
117
|
+
return {
|
|
118
|
+
...result,
|
|
119
|
+
capture: { ...(result.capture || {}), screenshot_sha256: screenshotSha256(imageContent) },
|
|
120
|
+
imageContent,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async observeLegacy(args, timeoutSeconds, context) {
|
|
125
|
+
const inspected = await this.inspectPage({
|
|
126
|
+
tab_id: args.tab_id,
|
|
127
|
+
max_elements: args.max_elements,
|
|
128
|
+
include_values: args.include_values,
|
|
129
|
+
all_frames: args.all_frames,
|
|
130
|
+
timeout_seconds: timeoutSeconds,
|
|
131
|
+
focus_query: args.focus_query,
|
|
132
|
+
}, context);
|
|
133
|
+
let imageContent = [];
|
|
134
|
+
if (args.include_screenshot) {
|
|
135
|
+
const screenshot = await this.screenshot({
|
|
136
|
+
tab_id: inspected.tab_id,
|
|
137
|
+
format: args.screenshot_format,
|
|
138
|
+
quality: args.screenshot_quality,
|
|
139
|
+
timeout_seconds: timeoutSeconds,
|
|
140
|
+
}, context);
|
|
141
|
+
imageContent = Array.isArray(screenshot?.$mcp?.content) ? screenshot.$mcp.content : [];
|
|
142
|
+
}
|
|
143
|
+
const rawSemanticEpoch = inspected.frames?.find?.((frame) => frame.frame_id === 0)?.document?.epoch;
|
|
144
|
+
const semanticEpoch = typeof rawSemanticEpoch === "string" ? rawSemanticEpoch : "";
|
|
145
|
+
return {
|
|
146
|
+
tab_id: inspected.tab_id,
|
|
147
|
+
title: inspected.title || "",
|
|
148
|
+
url: inspected.url || "",
|
|
149
|
+
semantic: inspected,
|
|
150
|
+
accessibility: null,
|
|
151
|
+
viewport: null,
|
|
152
|
+
frame_tree: [],
|
|
153
|
+
document_epoch: semanticEpoch,
|
|
154
|
+
capture: {
|
|
155
|
+
atomic: false,
|
|
156
|
+
navigation_coherent: false,
|
|
157
|
+
semantic_epoch: semanticEpoch,
|
|
158
|
+
cdp_epoch: "",
|
|
159
|
+
cdp: false,
|
|
160
|
+
screenshot_source: imageContent.length ? "capture_visible_tab_legacy" : "none",
|
|
161
|
+
coherence: "legacy_extension_without_computer_observation_v1",
|
|
162
|
+
fallback_reason: "extension_reload_required_for_cdp_observation",
|
|
163
|
+
screenshot_sha256: screenshotSha256(imageContent),
|
|
164
|
+
},
|
|
165
|
+
imageContent,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function computerObservationImage(result) {
|
|
171
|
+
if (!result?.screenshot?.data) return [];
|
|
172
|
+
if (typeof result.screenshot.data !== "string") throw new Error("browser extension returned an invalid computer observation screenshot");
|
|
173
|
+
const match = /^data:(image\/(?:png|jpeg));base64,([A-Za-z0-9+/=]+)$/.exec(result.screenshot.data);
|
|
174
|
+
if (!match || !browserScreenshotBytes(match[2], match[1])) throw new Error("browser extension returned an invalid computer observation screenshot");
|
|
175
|
+
return [{ type: "image", data: match[2], mimeType: match[1] }];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function screenshotSha256(imageContent) {
|
|
179
|
+
const image = (imageContent || []).find((item) => item?.type === "image" && typeof item.data === "string");
|
|
180
|
+
const bytes = image ? browserScreenshotBytes(image.data, image.mimeType) : null;
|
|
181
|
+
return bytes ? createHash("sha256").update(bytes).digest("hex") : "";
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function browserScreenshotBytes(value, mimeType) {
|
|
185
|
+
if (typeof value !== "string" || !value || value.length > Math.ceil(MAX_BROWSER_SCREENSHOT_BYTES / 3) * 4) return null;
|
|
186
|
+
const bytes = Buffer.from(value, "base64");
|
|
187
|
+
if (bytes.length < 3 || bytes.length > MAX_BROWSER_SCREENSHOT_BYTES || bytes.toString("base64") !== value) return null;
|
|
188
|
+
if (mimeType === "image/png") {
|
|
189
|
+
return bytes.length >= 8 && bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4e && bytes[3] === 0x47
|
|
190
|
+
&& bytes[4] === 0x0d && bytes[5] === 0x0a && bytes[6] === 0x1a && bytes[7] === 0x0a ? bytes : null;
|
|
191
|
+
}
|
|
192
|
+
return mimeType === "image/jpeg" && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff ? bytes : null;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function optionalFocusQuery(value) {
|
|
196
|
+
if (value === undefined) return "";
|
|
197
|
+
if (typeof value !== "string" || value.includes("\0") || value.length > 1000) throw new Error("focus_query must be a string of at most 1000 characters without NUL bytes");
|
|
198
|
+
return value.trim();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function normalizedCoordinate(value, label) {
|
|
202
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value >= 1) throw new Error(`${label} must be a number from 0 (inclusive) to 1 (exclusive)`);
|
|
203
|
+
return Object.is(value, -0) ? 0 : value;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function normalizeScrollDeltas(args) {
|
|
207
|
+
const deltaX = scrollDelta(args.delta_x, "delta_x");
|
|
208
|
+
const deltaY = scrollDelta(args.delta_y, "delta_y");
|
|
209
|
+
if (deltaX === 0 && deltaY === 0) throw new Error("scroll requires a non-zero delta_x or delta_y");
|
|
210
|
+
return { deltaX, deltaY };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function scrollDelta(value, label) {
|
|
214
|
+
if (value === undefined) return 0;
|
|
215
|
+
if (typeof value !== "number" || !Number.isFinite(value) || Math.abs(value) > 10_000) {
|
|
216
|
+
throw new Error(`${label} must be a finite number from -10000 to 10000`);
|
|
217
|
+
}
|
|
218
|
+
return Object.is(value, -0) ? 0 : value;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function requiredText(value, label, maxLength) {
|
|
222
|
+
if (typeof value !== "string" || value.includes("\0") || value.length > maxLength) {
|
|
223
|
+
throw new Error(`${label} must be a string of at most ${maxLength} characters without NUL bytes`);
|
|
224
|
+
}
|
|
225
|
+
return value;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function requiredPositiveInteger(value, label) {
|
|
229
|
+
if (!Number.isSafeInteger(value) || value < 1) throw new Error(`${label} must be a positive integer`);
|
|
230
|
+
return value;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function optionalAuthorityInteger(value, label) {
|
|
234
|
+
if (value === undefined) return null;
|
|
235
|
+
if (!Number.isSafeInteger(value) || value < 0) throw new Error(`${label} must be a non-negative integer`);
|
|
236
|
+
return value;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function optionalEpoch(value) {
|
|
240
|
+
if (value === undefined) return "";
|
|
241
|
+
if (typeof value !== "string" || value.includes("\0") || value.length > 9000) throw new Error("document_epoch is invalid");
|
|
242
|
+
return value;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function optionalFrameUrl(value) {
|
|
246
|
+
if (value === undefined) return "";
|
|
247
|
+
if (typeof value !== "string" || value.includes("\0") || value.length > 8192) throw new Error("frame_url is invalid");
|
|
248
|
+
return value;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function optionalSha256(value) {
|
|
252
|
+
if (typeof value !== "string" || !/^[A-Fa-f0-9]{64}$/.test(value)) throw new Error("screenshot_sha256 must be a SHA-256 hex digest");
|
|
253
|
+
return value.toLowerCase();
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function normalizeExpectedViewport(value) {
|
|
257
|
+
if (value === undefined) return null;
|
|
258
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("viewport must be an object");
|
|
259
|
+
const output = {};
|
|
260
|
+
for (const key of ["width", "height", "scale"]) {
|
|
261
|
+
const number = value[key];
|
|
262
|
+
if (typeof number !== "number" || !Number.isFinite(number) || number <= 0) throw new Error(`viewport.${key} must be a positive number`);
|
|
263
|
+
output[key] = number;
|
|
264
|
+
}
|
|
265
|
+
return output;
|
|
266
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { packageRoot } from "./package-identity.mjs";
|
|
5
|
+
|
|
6
|
+
const EXTENSION_ID_PATTERN = /^[a-p]{32}$/;
|
|
7
|
+
const PUBLIC_KEY_PATTERN = /^[A-Za-z0-9+/]+={0,2}$/;
|
|
8
|
+
|
|
9
|
+
const manifest = readExtensionManifest();
|
|
10
|
+
export const EXPECTED_EXTENSION_PUBLIC_KEY = normalizeManifestPublicKey(manifest.key);
|
|
11
|
+
export const EXPECTED_EXTENSION_ID = extensionIdFromPublicKey(EXPECTED_EXTENSION_PUBLIC_KEY);
|
|
12
|
+
|
|
13
|
+
export function extensionIdFromPublicKey(value) {
|
|
14
|
+
const publicKey = normalizeManifestPublicKey(value);
|
|
15
|
+
const bytes = Buffer.from(publicKey, "base64");
|
|
16
|
+
const hexadecimal = createHash("sha256").update(bytes).digest("hex").slice(0, 32);
|
|
17
|
+
const extensionId = [...hexadecimal]
|
|
18
|
+
.map((character) => String.fromCharCode(97 + Number.parseInt(character, 16)))
|
|
19
|
+
.join("");
|
|
20
|
+
if (!EXTENSION_ID_PATTERN.test(extensionId)) throw new Error("browser extension public key produced an invalid extension id");
|
|
21
|
+
return extensionId;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function normalizeExtensionId(value) {
|
|
25
|
+
const extensionId = String(value || "").trim().toLowerCase();
|
|
26
|
+
return EXTENSION_ID_PATTERN.test(extensionId) ? extensionId : "";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function normalizeManifestPublicKey(value) {
|
|
30
|
+
const publicKey = String(value || "").trim();
|
|
31
|
+
if (!PUBLIC_KEY_PATTERN.test(publicKey) || publicKey.length < 128 || publicKey.length > 8192) {
|
|
32
|
+
throw new Error("browser extension manifest must contain a bounded base64 public key");
|
|
33
|
+
}
|
|
34
|
+
let bytes;
|
|
35
|
+
try { bytes = Buffer.from(publicKey, "base64"); }
|
|
36
|
+
catch { throw new Error("browser extension manifest public key is invalid base64"); }
|
|
37
|
+
if (bytes.length < 96 || bytes.length > 6144 || bytes.toString("base64") !== publicKey) {
|
|
38
|
+
throw new Error("browser extension manifest public key is not canonical DER base64");
|
|
39
|
+
}
|
|
40
|
+
return publicKey;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function readExtensionManifest() {
|
|
44
|
+
const path = resolve(packageRoot, "browser-extension", "manifest.json");
|
|
45
|
+
const manifestValue = JSON.parse(readFileSync(path, "utf8"));
|
|
46
|
+
if (!manifestValue || typeof manifestValue !== "object" || Array.isArray(manifestValue)) {
|
|
47
|
+
throw new Error("browser extension manifest is invalid");
|
|
48
|
+
}
|
|
49
|
+
return manifestValue;
|
|
50
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
3
|
+
import { packageRoot } from "./package-identity.mjs";
|
|
4
|
+
import { isCandidateRuntimeDirectoryName } from "./state-root-owned-namespaces.mjs";
|
|
5
|
+
|
|
6
|
+
export const RELEASE_BROWSER_EXTENSION_DIRECTORY = "browser-extension";
|
|
7
|
+
|
|
8
|
+
export function releaseBrowserExtensionPath(stateRoot) {
|
|
9
|
+
return resolve(String(stateRoot || ""), "release-channels", RELEASE_BROWSER_EXTENSION_DIRECTORY);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function browserExtensionPathForRuntime({ stateRoot = "", packageDirectory = packageRoot } = {}) {
|
|
13
|
+
const packaged = resolve(packageDirectory, "browser-extension");
|
|
14
|
+
if (!stateRoot) return packaged;
|
|
15
|
+
const runtimes = resolve(stateRoot, "release-channels", "runtimes");
|
|
16
|
+
const runtimeRelative = relative(runtimes, resolve(packageDirectory));
|
|
17
|
+
if (!runtimeRelative || runtimeRelative === ".." || runtimeRelative.startsWith(`..${sep}`) || isAbsolute(runtimeRelative)) {
|
|
18
|
+
return packaged;
|
|
19
|
+
}
|
|
20
|
+
const [runtimeName] = runtimeRelative.split(sep);
|
|
21
|
+
return isCandidateRuntimeDirectoryName(runtimeName) ? releaseBrowserExtensionPath(stateRoot) : packaged;
|
|
22
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
|
|
3
2
|
import { readFileSync } from "node:fs";
|
|
4
3
|
import { resolve } from "node:path";
|
|
5
|
-
import { packageRoot } from "./
|
|
4
|
+
import { packageRoot } from "./package-identity.mjs";
|
|
5
|
+
import { EXPECTED_EXTENSION_ID, normalizeExtensionId } from "./browser-extension-identity.mjs";
|
|
6
6
|
|
|
7
7
|
export const BROWSER_EXTENSION_PROTOCOL = 3;
|
|
8
8
|
export const EXPECTED_EXTENSION_VERSION = extensionVersion();
|
|
@@ -10,14 +10,28 @@ const REQUIRED_EXTENSION_CAPABILITIES = Object.freeze([
|
|
|
10
10
|
"semantic_snapshot_refs", "actionability_waits", "trusted_input", "tab_management", "explicit_waits",
|
|
11
11
|
]);
|
|
12
12
|
export const MAX_BROWSER_MESSAGE_BYTES = 8 * 1024 * 1024;
|
|
13
|
+
const MUTATING_BROWSER_METHODS = new Set([
|
|
14
|
+
"manage_tabs", "point_action", "backend_node_action", "action", "fill_form", "upload_files", "screenshot",
|
|
15
|
+
]);
|
|
16
|
+
const BROWSER_MUTATION_TRANSPORT_UNKNOWN = "browser mutation request may have been dispatched; the outcome is unknown because its response was not observed. Inspect browser state before retrying.";
|
|
17
|
+
|
|
18
|
+
/** @param {unknown} method */
|
|
19
|
+
export function browserMethodMayMutate(method) {
|
|
20
|
+
return typeof method === "string" && MUTATING_BROWSER_METHODS.has(method);
|
|
21
|
+
}
|
|
13
22
|
|
|
14
|
-
/** @
|
|
23
|
+
/** @param {unknown} method @param {unknown} fallback */
|
|
24
|
+
export function browserPostDispatchTransportFailure(method, fallback) {
|
|
25
|
+
if (browserMethodMayMutate(method)) return BROWSER_MUTATION_TRANSPORT_UNKNOWN;
|
|
26
|
+
return typeof fallback === "string" && fallback ? fallback.slice(0, 2000) : "browser transport failed";
|
|
27
|
+
}
|
|
28
|
+
/** @typedef {{protocol: number, version: string, extension_id: string, capabilities: string[]}} ExtensionInfo */
|
|
15
29
|
/** @typedef {{readyState: number, close: (code?: number, reason?: string) => unknown, send: (value: string) => unknown}} ProtocolSocket */
|
|
16
30
|
|
|
17
31
|
/** @param {unknown} value @returns {ExtensionInfo | null} */
|
|
18
32
|
export function normalizeCompatibleExtensionInfo(value) {
|
|
19
33
|
const info = normalizeExtensionInfo(value);
|
|
20
|
-
if (!info || info.protocol !== BROWSER_EXTENSION_PROTOCOL || info.version !== EXPECTED_EXTENSION_VERSION) return null;
|
|
34
|
+
if (!info || info.protocol !== BROWSER_EXTENSION_PROTOCOL || info.version !== EXPECTED_EXTENSION_VERSION || info.extension_id !== EXPECTED_EXTENSION_ID) return null;
|
|
21
35
|
if (REQUIRED_EXTENSION_CAPABILITIES.some((capability) => !info.capabilities.includes(capability))) return null;
|
|
22
36
|
return info;
|
|
23
37
|
}
|
|
@@ -32,11 +46,13 @@ export function parseExtensionHello(message) {
|
|
|
32
46
|
if (info.version !== EXPECTED_EXTENSION_VERSION) {
|
|
33
47
|
throw new Error(`extension version mismatch; expected ${EXPECTED_EXTENSION_VERSION}; reload the extension`);
|
|
34
48
|
}
|
|
49
|
+
if (info.extension_id !== EXPECTED_EXTENSION_ID) {
|
|
50
|
+
throw new Error(`extension identity mismatch; expected ${EXPECTED_EXTENSION_ID}; reload the packaged extension`);
|
|
51
|
+
}
|
|
35
52
|
const missing = REQUIRED_EXTENSION_CAPABILITIES.filter((capability) => !info.capabilities.includes(capability));
|
|
36
53
|
if (missing.length) throw new Error(`extension capability mismatch; reload the extension (${missing.join(",")})`);
|
|
37
54
|
return info;
|
|
38
55
|
}
|
|
39
|
-
|
|
40
56
|
/**
|
|
41
57
|
* @param {string | Buffer | Uint8Array} data
|
|
42
58
|
* @returns {{ok: true, message: Record<string, unknown>} | {ok: false, code: number, reason: string}}
|
|
@@ -54,10 +70,28 @@ export function parseBrowserSocketMessage(data) {
|
|
|
54
70
|
}
|
|
55
71
|
return { ok: true, message };
|
|
56
72
|
}
|
|
57
|
-
|
|
73
|
+
/** @param {unknown} value */
|
|
74
|
+
export function normalizeRuntimeBrowserRequest(value) {
|
|
75
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
76
|
+
const message = /** @type {Record<string, unknown>} */ (value);
|
|
77
|
+
const id = boundedProtocolString(message.id, 200);
|
|
78
|
+
const method = typeof message.method === "string" && /^[a-z][a-z0-9_]{0,63}$/.test(message.method) ? message.method : "";
|
|
79
|
+
const params = message.params === undefined ? {} : message.params;
|
|
80
|
+
const timeoutMs = message.timeout_ms === undefined ? 30_000 : message.timeout_ms;
|
|
81
|
+
const callId = message.call_id === undefined ? "" : boundedProtocolString(message.call_id, 200);
|
|
82
|
+
if (message.type !== "request" || !id || !method || !params || typeof params !== "object" || Array.isArray(params)
|
|
83
|
+
|| !Number.isSafeInteger(timeoutMs)
|
|
84
|
+
|| (message.call_id !== undefined && !callId)) return null;
|
|
85
|
+
return { type: "request", id, method, params, timeout_ms: timeoutMs, ...(callId ? { call_id: callId } : {}) };
|
|
86
|
+
}
|
|
87
|
+
/** @param {unknown} value @param {number} maximum */
|
|
88
|
+
function boundedProtocolString(value, maximum) {
|
|
89
|
+
return typeof value === "string" && value && value.length <= maximum && !value.includes("\0") ? value : "";
|
|
90
|
+
}
|
|
58
91
|
/** @param {ProtocolSocket} socket @param {number} code @param {string} reason */
|
|
59
92
|
export function closeProtocolSocket(socket, code, reason) {
|
|
60
|
-
try { socket.close(code, reason); }
|
|
93
|
+
try { socket.close(code, reason); }
|
|
94
|
+
catch { /* Protocol rejection is already decided; a concurrently closed socket needs no second failure path. */ }
|
|
61
95
|
}
|
|
62
96
|
|
|
63
97
|
/** @param {ProtocolSocket | null | undefined} socket @param {unknown} value */
|
|
@@ -75,16 +109,21 @@ export function safeSocketSend(socket, value) {
|
|
|
75
109
|
function normalizeExtensionInfo(value) {
|
|
76
110
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
77
111
|
const record = /** @type {Record<string, unknown>} */ (value);
|
|
78
|
-
const protocol =
|
|
112
|
+
const protocol = record.protocol;
|
|
79
113
|
const version = typeof record.version === "string" && record.version.length <= 100 ? record.version : "";
|
|
114
|
+
const extension_id = normalizeExtensionId(record.extension_id);
|
|
80
115
|
const capabilities = Array.isArray(record.capabilities)
|
|
81
116
|
? [...new Set(record.capabilities.filter((entry) => typeof entry === "string" && /^[a-z][a-z0-9_]{0,63}$/.test(entry)))].slice(0, 32)
|
|
82
117
|
: [];
|
|
83
|
-
if (!Number.isInteger(protocol) || protocol < 1 || !version) return null;
|
|
84
|
-
return { protocol, version, capabilities };
|
|
118
|
+
if (typeof protocol !== "number" || !Number.isInteger(protocol) || protocol < 1 || !version || !extension_id) return null;
|
|
119
|
+
return { protocol, version, extension_id, capabilities };
|
|
85
120
|
}
|
|
86
121
|
|
|
87
122
|
function extensionVersion() {
|
|
88
123
|
const manifest = JSON.parse(readFileSync(resolve(packageRoot, "browser-extension", "manifest.json"), "utf8"));
|
|
89
|
-
|
|
124
|
+
const version = manifest.version_name === undefined ? manifest.version : manifest.version_name;
|
|
125
|
+
if (typeof version !== "string" || !version || version.length > 100 || version.includes("\0")) {
|
|
126
|
+
throw new Error("browser extension manifest version is invalid");
|
|
127
|
+
}
|
|
128
|
+
return version;
|
|
90
129
|
}
|