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.
Files changed (473) hide show
  1. package/CHANGELOG.md +1142 -0
  2. package/CONTRIBUTING.md +23 -25
  3. package/GOVERNANCE.md +2 -2
  4. package/README.md +58 -20
  5. package/SECURITY.md +161 -120
  6. package/SUPPORT.md +1 -1
  7. package/browser-extension/broker-auth.js +83 -0
  8. package/browser-extension/broker-liveness.js +35 -0
  9. package/browser-extension/browser-error-boundary.js +96 -0
  10. package/browser-extension/browser-operations.js +1456 -101
  11. package/browser-extension/devtools-input.js +195 -34
  12. package/browser-extension/devtools-observation.js +429 -0
  13. package/browser-extension/devtools-session.js +48 -0
  14. package/browser-extension/manifest.json +4 -3
  15. package/browser-extension/page-automation.js +578 -118
  16. package/browser-extension/pairing-bootstrap.js +42 -0
  17. package/browser-extension/pairing.js +28 -12
  18. package/browser-extension/service-worker.js +126 -106
  19. package/docs/AGENT_CONTEXT.md +13 -10
  20. package/docs/ARCHITECTURE.md +159 -67
  21. package/docs/AUDIT.md +1302 -4
  22. package/docs/CLIENTS.md +19 -6
  23. package/docs/COMPUTER_USE.md +297 -0
  24. package/docs/ENGINEERING.md +72 -13
  25. package/docs/GETTING_STARTED.md +18 -20
  26. package/docs/LOCAL_AUTHORIZATION.md +115 -72
  27. package/docs/LOCAL_AUTOMATION.md +27 -21
  28. package/docs/LOGGING.md +40 -19
  29. package/docs/MANAGED_JOBS.md +37 -34
  30. package/docs/MULTI_ACCOUNT.md +98 -37
  31. package/docs/OPERATIONS.md +145 -51
  32. package/docs/OVERVIEW.md +14 -11
  33. package/docs/POLICY_REFERENCE.md +71 -3
  34. package/docs/PRIVACY.md +41 -2
  35. package/docs/PROJECT_STANDARDS.md +29 -6
  36. package/docs/RELEASING.md +238 -64
  37. package/docs/TESTING.md +150 -39
  38. package/docs/THREAT_MODEL.md +164 -67
  39. package/docs/TOOL_REFERENCE.md +653 -60
  40. package/docs/UPGRADING.md +78 -18
  41. package/native/macos/MachineBridgeBackgroundInput.swift +1051 -0
  42. package/native/macos/MachineBridgeBackgroundInputSmokeFixture.swift +295 -0
  43. package/native/macos/MachineBridgeTrustBroker.swift +233 -0
  44. package/package.json +94 -16
  45. package/scripts/accepted-candidate-tarball.mjs +77 -0
  46. package/scripts/candidate-runtime-store.mjs +93 -0
  47. package/scripts/check-plan.mjs +73 -3
  48. package/scripts/check-runner.mjs +129 -17
  49. package/scripts/commit-message-check.mjs +5 -2
  50. package/scripts/consumer-package-security.mjs +293 -0
  51. package/scripts/coverage-check.mjs +251 -15
  52. package/scripts/coverage-generation.mjs +41 -0
  53. package/scripts/coverage-range-merge.mjs +41 -0
  54. package/scripts/foreground-daemon-recovery.mjs +88 -0
  55. package/scripts/generate-policy-reference.mjs +28 -5
  56. package/scripts/generate-tool-reference.mjs +1 -1
  57. package/scripts/generate-worker-types.mjs +21 -11
  58. package/scripts/github-backlog.mjs +11 -10
  59. package/scripts/github-push.mjs +62 -16
  60. package/scripts/github-release-asset.mjs +58 -0
  61. package/scripts/github-release.mjs +241 -132
  62. package/scripts/global-package-installation.mjs +68 -0
  63. package/scripts/hardened-npm-session.mjs +42 -0
  64. package/scripts/install-published-prerelease.mjs +198 -0
  65. package/scripts/local-release-acceptance.mjs +103 -45
  66. package/scripts/macos-background-input-smoke.mjs +515 -0
  67. package/scripts/network-retry.mjs +8 -1
  68. package/scripts/npm-global-prefix.mjs +27 -0
  69. package/scripts/npm-publication-policy.mjs +17 -0
  70. package/scripts/official-mcp-conformance.mjs +267 -0
  71. package/scripts/persistent-activation-process.mjs +101 -0
  72. package/scripts/prepare-pinned-npm.mjs +12 -69
  73. package/scripts/prerelease-activation.mjs +160 -0
  74. package/scripts/privacy-check.mjs +14 -6
  75. package/scripts/promotion-digest.mjs +128 -0
  76. package/scripts/publish-npm.mjs +260 -0
  77. package/scripts/published-release.mjs +132 -0
  78. package/scripts/release-acceptance.mjs +62 -54
  79. package/scripts/release-browser-extension-store.mjs +172 -0
  80. package/scripts/release-candidate-manifest.mjs +52 -0
  81. package/scripts/release-channel.mjs +106 -0
  82. package/scripts/release-diagnostic.mjs +41 -0
  83. package/scripts/release-impact-check.mjs +12 -22
  84. package/scripts/release-oauth-canary-core.mjs +325 -0
  85. package/scripts/release-oauth-canary-evidence.mjs +100 -0
  86. package/scripts/release-oauth-canary.mjs +127 -0
  87. package/scripts/release-publication-guard.mjs +67 -0
  88. package/scripts/release-soak.mjs +284 -0
  89. package/scripts/release-state.mjs +1 -1
  90. package/scripts/run-checks.mjs +44 -7
  91. package/scripts/run-worker-dry-run.mjs +21 -0
  92. package/scripts/sarif-security-gate.mjs +2 -1
  93. package/scripts/sbom-check.mjs +106 -0
  94. package/scripts/start-release-candidate.mjs +267 -44
  95. package/scripts/syntax-check.mjs +36 -14
  96. package/scripts/verification-generation-guard.mjs +15 -0
  97. package/scripts/verification-idle-sleep-guard.mjs +37 -0
  98. package/scripts/verification-state.mjs +108 -0
  99. package/scripts/wrangler-command-lifecycle.mjs +115 -0
  100. package/src/local/account-access.mjs +13 -1
  101. package/src/local/account-admin-response.mjs +92 -0
  102. package/src/local/account-admin.mjs +52 -19
  103. package/src/local/agent-context-projection.mjs +26 -7
  104. package/src/local/agent-context.mjs +88 -49
  105. package/src/local/agent-skill-discovery.mjs +16 -5
  106. package/src/local/app-automation-macos-jxa.mjs +350 -0
  107. package/src/local/app-automation.mjs +873 -163
  108. package/src/local/application-capability-projection.mjs +24 -0
  109. package/src/local/authority-context.mjs +106 -0
  110. package/src/local/autostart-log-maintenance.mjs +36 -0
  111. package/src/local/browser-bridge-http.mjs +5 -3
  112. package/src/local/browser-bridge.mjs +109 -31
  113. package/src/local/browser-broker-auth-http.mjs +67 -0
  114. package/src/local/browser-broker-auth.mjs +98 -0
  115. package/src/local/browser-broker-routes.mjs +63 -20
  116. package/src/local/browser-broker-server.mjs +17 -7
  117. package/src/local/browser-command.mjs +79 -30
  118. package/src/local/browser-computer-observation-service.mjs +266 -0
  119. package/src/local/browser-extension-identity.mjs +50 -0
  120. package/src/local/browser-extension-path.mjs +22 -0
  121. package/src/local/browser-extension-protocol.mjs +50 -11
  122. package/src/local/browser-operation-service.mjs +147 -117
  123. package/src/local/browser-pairing-grant.mjs +109 -0
  124. package/src/local/browser-pairing-http.mjs +36 -0
  125. package/src/local/browser-pairing-launch.mjs +58 -0
  126. package/src/local/browser-pairing-store.mjs +85 -48
  127. package/src/local/browser-request-registry.mjs +40 -14
  128. package/src/local/browser-request-settlement.mjs +78 -0
  129. package/src/local/browser-resource-input.mjs +74 -0
  130. package/src/local/browser-trusted-input-health.mjs +63 -0
  131. package/src/local/call-authority.mjs +21 -0
  132. package/src/local/call-capacity.mjs +45 -0
  133. package/src/local/call-registry-drain.mjs +54 -0
  134. package/src/local/call-registry.mjs +30 -15
  135. package/src/local/capability-observer.mjs +5 -0
  136. package/src/local/capability-ranking.mjs +3 -2
  137. package/src/local/child-process-settlement.mjs +132 -0
  138. package/src/local/cli-account-admin.mjs +40 -6
  139. package/src/local/cli-activate.mjs +162 -0
  140. package/src/local/cli-local-admin.mjs +20 -45
  141. package/src/local/cli-options.mjs +12 -13
  142. package/src/local/cli-ready-output.mjs +52 -0
  143. package/src/local/cli-service.mjs +91 -11
  144. package/src/local/cli.mjs +232 -150
  145. package/src/local/computer-use-application-observation.mjs +224 -0
  146. package/src/local/computer-use-arguments.mjs +270 -0
  147. package/src/local/computer-use-deadline.mjs +50 -0
  148. package/src/local/computer-use-dispatch-settlement.mjs +40 -0
  149. package/src/local/computer-use-expectation.mjs +171 -0
  150. package/src/local/computer-use-observation.mjs +769 -0
  151. package/src/local/computer-use-recovery.mjs +82 -0
  152. package/src/local/computer-use-result-budget.mjs +145 -0
  153. package/src/local/computer-use-snapshot-store.mjs +67 -0
  154. package/src/local/computer-use.mjs +2255 -0
  155. package/src/local/daemon-http-relay-auth.mjs +26 -0
  156. package/src/local/daemon-http-relay-connection.mjs +298 -0
  157. package/src/local/daemon-http-relay-request.mjs +64 -0
  158. package/src/local/daemon-http-relay-sequence.mjs +49 -0
  159. package/src/local/daemon-process.mjs +40 -7
  160. package/src/local/delegated-process-sandbox.mjs +148 -0
  161. package/src/local/device-identity.mjs +156 -49
  162. package/src/local/device-root-provider.mjs +81 -0
  163. package/src/local/directory-metadata.mjs +47 -0
  164. package/src/local/doctor-reporting.mjs +23 -0
  165. package/src/local/durable-process-spec.mjs +80 -0
  166. package/src/local/errors.mjs +25 -15
  167. package/src/local/exclusive-file.mjs +74 -26
  168. package/src/local/execution-limits.mjs +9 -0
  169. package/src/local/execution-routing.mjs +276 -0
  170. package/src/local/execution-surface.mjs +23 -0
  171. package/src/local/file-mutation-coordinator.mjs +47 -0
  172. package/src/local/file-snapshot-preservation.mjs +24 -0
  173. package/src/local/filesystem-identity.mjs +54 -0
  174. package/src/local/fixed-process-environment.mjs +21 -0
  175. package/src/local/full-access-test.mjs +31 -13
  176. package/src/local/git-commit.mjs +69 -0
  177. package/src/local/git-config-safety.mjs +55 -0
  178. package/src/local/git-log-parser.mjs +41 -0
  179. package/src/local/git-metadata-boundary.mjs +77 -0
  180. package/src/local/git-metadata-tree-safety.mjs +59 -0
  181. package/src/local/git-operation-state.mjs +19 -0
  182. package/src/local/git-service.mjs +165 -30
  183. package/src/local/hardened-npm-download-timeout.mjs +46 -0
  184. package/src/local/hardened-npm-download.mjs +109 -0
  185. package/src/local/hardened-npm-extract.mjs +20 -0
  186. package/src/local/hardened-npm-verification.mjs +104 -0
  187. package/src/local/hardened-npm.mjs +199 -0
  188. package/src/local/job-runner.mjs +244 -89
  189. package/src/local/lifecycle.mjs +6 -0
  190. package/src/local/log.mjs +19 -45
  191. package/src/local/macos-background-input.mjs +398 -0
  192. package/src/local/macos-idle-sleep-assertion.mjs +65 -0
  193. package/src/local/macos-trust-broker.mjs +404 -0
  194. package/src/local/managed-job-cancellation.mjs +42 -0
  195. package/src/local/managed-job-capacity.mjs +16 -0
  196. package/src/local/managed-job-directory-generation.mjs +60 -0
  197. package/src/local/managed-job-directory.mjs +75 -0
  198. package/src/local/managed-job-durable-process.mjs +49 -0
  199. package/src/local/managed-job-lock.mjs +46 -14
  200. package/src/local/managed-job-plan.mjs +38 -14
  201. package/src/local/managed-job-projection.mjs +23 -15
  202. package/src/local/managed-job-retention.mjs +128 -0
  203. package/src/local/managed-job-runner-claim.mjs +70 -0
  204. package/src/local/managed-job-runner.mjs +35 -10
  205. package/src/local/managed-job-storage.mjs +22 -6
  206. package/src/local/managed-job-terminal-maintenance.mjs +40 -0
  207. package/src/local/managed-job-terminal.mjs +150 -0
  208. package/src/local/managed-jobs.mjs +424 -287
  209. package/src/local/npm-cli.mjs +42 -0
  210. package/src/local/npm-environment.mjs +37 -0
  211. package/src/local/operation-authorization.mjs +68 -324
  212. package/src/local/operation-risk.mjs +51 -4
  213. package/src/local/owner-state-lock.mjs +118 -0
  214. package/src/local/package-identity.mjs +9 -0
  215. package/src/local/patch.mjs +20 -18
  216. package/src/local/path-inspection.mjs +23 -0
  217. package/src/local/policy.mjs +19 -0
  218. package/src/local/private-toolchain-integrity.mjs +54 -0
  219. package/src/local/process-execution.mjs +87 -75
  220. package/src/local/process-foreground-timeout.mjs +50 -0
  221. package/src/local/process-identity.mjs +41 -7
  222. package/src/local/process-nonreplayable-settlement.mjs +50 -0
  223. package/src/local/process-result-projection.mjs +22 -0
  224. package/src/local/process-session-events.mjs +47 -0
  225. package/src/local/process-session-remote-activity.mjs +10 -0
  226. package/src/local/process-session-termination.mjs +73 -0
  227. package/src/local/process-sessions.mjs +144 -112
  228. package/src/local/process-tracker.mjs +140 -13
  229. package/src/local/process-tree-ownership-types.d.ts +37 -0
  230. package/src/local/process-tree-ownership.mjs +57 -0
  231. package/src/local/process-tree-signal.mjs +34 -0
  232. package/src/local/process-tree-snapshot.mjs +77 -0
  233. package/src/local/process-tree-supervisor.mjs +69 -0
  234. package/src/local/process-tree.mjs +11 -53
  235. package/src/local/project-metadata.mjs +19 -6
  236. package/src/local/relay-call-recovery.mjs +58 -38
  237. package/src/local/relay-connection-classification.mjs +131 -0
  238. package/src/local/relay-connection-support.mjs +91 -0
  239. package/src/local/relay-connection.mjs +179 -272
  240. package/src/local/relay-diagnostics.mjs +91 -0
  241. package/src/local/relay-heartbeat.mjs +119 -0
  242. package/src/local/relay-liveness.mjs +109 -0
  243. package/src/local/relay-peer-diagnostics.mjs +29 -0
  244. package/src/local/release-runtime-lock.mjs +18 -0
  245. package/src/local/remote-activity-idle-sleep-guard.mjs +77 -0
  246. package/src/local/remote-configuration.mjs +48 -0
  247. package/src/local/resilient-relay-connection.mjs +178 -0
  248. package/src/local/resource-admission-diagnostics.mjs +52 -0
  249. package/src/local/resource-admission-policy.mjs +115 -0
  250. package/src/local/resource-admission.mjs +339 -0
  251. package/src/local/resource-build-root.mjs +83 -0
  252. package/src/local/resource-cargo-concurrency.mjs +38 -0
  253. package/src/local/resource-cmake-concurrency.mjs +42 -0
  254. package/src/local/resource-command-concurrency.mjs +37 -0
  255. package/src/local/resource-command-profile.mjs +176 -0
  256. package/src/local/resource-coordinator-accounting.mjs +21 -0
  257. package/src/local/resource-cpu-window.mjs +20 -0
  258. package/src/local/resource-disk-headroom.mjs +13 -0
  259. package/src/local/resource-elastic-memory.mjs +19 -0
  260. package/src/local/resource-elastic-request.mjs +36 -0
  261. package/src/local/resource-foreground-wait.mjs +29 -0
  262. package/src/local/resource-go-concurrency.mjs +43 -0
  263. package/src/local/resource-gradle-concurrency.mjs +57 -0
  264. package/src/local/resource-host-cache.mjs +39 -0
  265. package/src/local/resource-host-darwin.mjs +54 -0
  266. package/src/local/resource-host-linux.mjs +35 -0
  267. package/src/local/resource-host-sample-file.mjs +23 -0
  268. package/src/local/resource-host-snapshot.mjs +99 -0
  269. package/src/local/resource-lease-accounting.mjs +119 -0
  270. package/src/local/resource-light-command.mjs +6 -0
  271. package/src/local/resource-make-concurrency.mjs +42 -0
  272. package/src/local/resource-maven-concurrency.mjs +38 -0
  273. package/src/local/resource-ninja-command-concurrency.mjs +20 -0
  274. package/src/local/resource-ninja-concurrency.mjs +28 -0
  275. package/src/local/resource-operations.mjs +26 -10
  276. package/src/local/resource-pressure.mjs +67 -0
  277. package/src/local/resource-probe-command.mjs +35 -0
  278. package/src/local/resource-process-admission.mjs +59 -0
  279. package/src/local/resource-process-ancestry-cache.mjs +43 -0
  280. package/src/local/resource-process-ancestry.mjs +32 -0
  281. package/src/local/resource-process-priority.mjs +15 -0
  282. package/src/local/resource-project-key.mjs +53 -0
  283. package/src/local/resource-pytest-concurrency.mjs +33 -0
  284. package/src/local/resource-release-control-classification.mjs +14 -0
  285. package/src/local/resource-release-control-executable.mjs +70 -0
  286. package/src/local/resource-release-control-workspace.mjs +41 -0
  287. package/src/local/resource-request-contract.mjs +30 -0
  288. package/src/local/resource-script-classification.mjs +44 -0
  289. package/src/local/resource-shell-analysis.mjs +58 -0
  290. package/src/local/resource-staging-recovery.mjs +97 -0
  291. package/src/local/resource-swift-concurrency.mjs +31 -0
  292. package/src/local/resource-transaction-lock.mjs +189 -0
  293. package/src/local/resource-wait.mjs +48 -0
  294. package/src/local/resource-waiters.mjs +139 -0
  295. package/src/local/resource-xcode-command.mjs +7 -0
  296. package/src/local/resource-xcode-concurrency.mjs +34 -0
  297. package/src/local/resource-xcode-non-build.mjs +19 -0
  298. package/src/local/runtime-activation.mjs +700 -0
  299. package/src/local/runtime-activity-projection.mjs +48 -0
  300. package/src/local/runtime-capabilities.mjs +48 -15
  301. package/src/local/runtime-diagnostic-state.mjs +14 -0
  302. package/src/local/runtime-diagnostics.mjs +40 -13
  303. package/src/local/runtime-info-projection.mjs +60 -0
  304. package/src/local/runtime-info-relay-projection.mjs +21 -0
  305. package/src/local/runtime-paths.mjs +2 -1
  306. package/src/local/runtime-process-routing.mjs +33 -0
  307. package/src/local/runtime-relay-connection-options.mjs +44 -0
  308. package/src/local/runtime-relay-control.mjs +98 -0
  309. package/src/local/runtime-relay.mjs +26 -50
  310. package/src/local/runtime-reporting.mjs +37 -21
  311. package/src/local/runtime-resource-service.mjs +77 -0
  312. package/src/local/runtime-tool-handlers.mjs +13 -10
  313. package/src/local/runtime.mjs +244 -208
  314. package/src/local/secure-file.mjs +73 -20
  315. package/src/local/security-audit-dispatch.mjs +35 -0
  316. package/src/local/security-audit-log.mjs +215 -0
  317. package/src/local/security-audit-state.mjs +139 -0
  318. package/src/local/security-audit-storage.mjs +135 -0
  319. package/src/local/security-audit-warning.mjs +31 -0
  320. package/src/local/security-audit-worker.mjs +87 -0
  321. package/src/local/service-convergence.mjs +42 -1
  322. package/src/local/service-definition.mjs +24 -0
  323. package/src/local/service-environment.mjs +18 -10
  324. package/src/local/service-owner.mjs +146 -0
  325. package/src/local/service-ownership.mjs +17 -0
  326. package/src/local/service-restart-handoff.mjs +64 -0
  327. package/src/local/service-restart-scheduler.mjs +49 -0
  328. package/src/local/service-runtime-convergence.mjs +52 -0
  329. package/src/local/service-runtime.mjs +105 -0
  330. package/src/local/service-status.mjs +60 -0
  331. package/src/local/service.mjs +385 -92
  332. package/src/local/shell.mjs +39 -19
  333. package/src/local/ssh-key.mjs +158 -42
  334. package/src/local/state-inventory.mjs +50 -24
  335. package/src/local/state-owner-lock-inventory.mjs +23 -0
  336. package/src/local/state-root-owned-namespaces.mjs +83 -0
  337. package/src/local/state-root-retirement.mjs +89 -0
  338. package/src/local/state.mjs +386 -96
  339. package/src/local/stdio.mjs +96 -66
  340. package/src/local/support-state-projection.mjs +30 -0
  341. package/src/local/system-network-route.mjs +76 -0
  342. package/src/local/systemd-removal.mjs +20 -0
  343. package/src/local/tool-executor.mjs +85 -20
  344. package/src/local/tool-result-boundary.mjs +51 -0
  345. package/src/local/toolchain-operation-lock.mjs +41 -0
  346. package/src/local/tools.mjs +7 -5
  347. package/src/local/trusted-executable.mjs +53 -0
  348. package/src/local/trusted-git-executable.mjs +34 -0
  349. package/src/local/trusted-github-cli.mjs +24 -0
  350. package/src/local/windows-service-convergence.mjs +49 -0
  351. package/src/local/windows-service.mjs +52 -45
  352. package/src/local/worker-deployment-fingerprint.mjs +102 -0
  353. package/src/local/worker-deployment.mjs +48 -60
  354. package/src/local/worker-health.mjs +12 -4
  355. package/src/local/worker-secret-file.mjs +37 -17
  356. package/src/local/workspace-file-service.mjs +156 -239
  357. package/src/local/workspace-file-transaction.mjs +189 -0
  358. package/src/local/workspace-search.mjs +55 -0
  359. package/src/local/wrangler-toolchain/package-lock.json +1505 -0
  360. package/src/local/wrangler-toolchain/package.json +23 -0
  361. package/src/local/wrangler-toolchain-verification.mjs +135 -0
  362. package/src/local/wrangler-toolchain.mjs +188 -0
  363. package/src/shared/access-contract.json +7 -1
  364. package/src/shared/activation-recovery.mjs +49 -0
  365. package/src/shared/admin-auth.d.mts +2 -1
  366. package/src/shared/admin-auth.mjs +3 -3
  367. package/src/shared/authority-revocation.d.mts +9 -0
  368. package/src/shared/authority-revocation.mjs +28 -0
  369. package/src/shared/daemon-auth.d.mts +11 -0
  370. package/src/shared/daemon-auth.mjs +17 -0
  371. package/src/shared/device-session-auth.d.mts +20 -0
  372. package/src/shared/device-session-auth.mjs +51 -0
  373. package/src/shared/foreground-timeout.d.mts +15 -0
  374. package/src/shared/foreground-timeout.mjs +70 -0
  375. package/src/shared/log-redaction.d.mts +9 -0
  376. package/src/shared/log-redaction.mjs +51 -0
  377. package/src/shared/mcp-protocol.d.mts +27 -0
  378. package/src/shared/mcp-protocol.mjs +275 -0
  379. package/src/shared/project-overview-projection.d.mts +4 -0
  380. package/src/shared/project-overview-projection.mjs +96 -0
  381. package/src/shared/relay-contract.json +28 -0
  382. package/src/shared/result-projection.d.mts +2 -1
  383. package/src/shared/result-projection.mjs +13 -2
  384. package/src/shared/server-metadata.json +15 -9
  385. package/src/shared/tool-argument-validation.d.mts +17 -0
  386. package/src/shared/tool-argument-validation.mjs +325 -0
  387. package/src/shared/tool-call-capacity.d.mts +41 -0
  388. package/src/shared/tool-call-capacity.mjs +59 -0
  389. package/src/shared/tool-catalog.json +626 -57
  390. package/src/worker/access.ts +12 -0
  391. package/src/worker/account-admin.ts +55 -33
  392. package/src/worker/authority-revocations.ts +174 -0
  393. package/src/worker/authority.ts +52 -6
  394. package/src/worker/daemon-auth.ts +49 -68
  395. package/src/worker/daemon-channel.ts +36 -0
  396. package/src/worker/daemon-http-auth.ts +63 -0
  397. package/src/worker/daemon-http-channel.ts +120 -0
  398. package/src/worker/daemon-http-controller.ts +163 -0
  399. package/src/worker/daemon-http-protocol.ts +62 -0
  400. package/src/worker/daemon-http-registry.ts +82 -0
  401. package/src/worker/daemon-last-observation.ts +30 -0
  402. package/src/worker/daemon-liveness.ts +8 -6
  403. package/src/worker/daemon-ready-dispatch.ts +17 -0
  404. package/src/worker/daemon-ready-messages.ts +73 -0
  405. package/src/worker/daemon-ready-waiters.ts +79 -0
  406. package/src/worker/daemon-recovery-budget.ts +13 -0
  407. package/src/worker/daemon-registry.ts +58 -0
  408. package/src/worker/daemon-relay-diagnostics.ts +127 -0
  409. package/src/worker/daemon-socket-attachment.ts +55 -0
  410. package/src/worker/daemon-sockets.ts +72 -62
  411. package/src/worker/daemon-status.ts +38 -0
  412. package/src/worker/device-session-verifier.ts +129 -0
  413. package/src/worker/dpop.ts +157 -0
  414. package/src/worker/durable-process-timeout.ts +29 -0
  415. package/src/worker/errors.ts +57 -0
  416. package/src/worker/http.ts +112 -26
  417. package/src/worker/index.ts +446 -440
  418. package/src/worker/mcp-access.ts +57 -0
  419. package/src/worker/mcp-controller.ts +188 -0
  420. package/src/worker/mcp-http-accept.ts +11 -0
  421. package/src/worker/mcp-http-contract.ts +322 -0
  422. package/src/worker/mcp-initialization-compat.ts +141 -0
  423. package/src/worker/mcp-jsonrpc.ts +11 -29
  424. package/src/worker/mcp-removed-protocol.ts +17 -0
  425. package/src/worker/mcp-response-proxy.ts +135 -0
  426. package/src/worker/mcp-response-stream.ts +65 -0
  427. package/src/worker/mcp-stale-schema-compat.ts +57 -0
  428. package/src/worker/mcp-stream-proxy-contract.ts +48 -0
  429. package/src/worker/mcp-tool-call-input.ts +23 -0
  430. package/src/worker/nonce-store.ts +33 -35
  431. package/src/worker/oauth-authorization-page.ts +21 -1
  432. package/src/worker/oauth-client-admin.ts +52 -0
  433. package/src/worker/oauth-client-contract.ts +38 -0
  434. package/src/worker/oauth-controller.ts +107 -47
  435. package/src/worker/oauth-field-contract.ts +31 -0
  436. package/src/worker/oauth-record-contract.ts +7 -0
  437. package/src/worker/oauth-refresh-authority.ts +27 -0
  438. package/src/worker/oauth-refresh-exchange.ts +162 -0
  439. package/src/worker/oauth-refresh-families.ts +130 -27
  440. package/src/worker/oauth-refresh-persistence.ts +116 -0
  441. package/src/worker/oauth-state.ts +33 -18
  442. package/src/worker/oauth-store-validation.ts +131 -0
  443. package/src/worker/oauth-token-derivation.ts +33 -0
  444. package/src/worker/oauth-token-issuance.ts +119 -0
  445. package/src/worker/oauth-tokens.ts +56 -190
  446. package/src/worker/observability.ts +38 -5
  447. package/src/worker/pending-admission.ts +15 -0
  448. package/src/worker/pending-call-capacity.ts +108 -0
  449. package/src/worker/pending-call-contract.ts +42 -5
  450. package/src/worker/pending-call-deadlines.ts +75 -0
  451. package/src/worker/pending-calls.ts +131 -125
  452. package/src/worker/runtime-alarm-storage.ts +37 -0
  453. package/src/worker/runtime-alarm.ts +136 -0
  454. package/src/worker/server-info-activity.ts +36 -0
  455. package/src/worker/server-info-tool-delivery.ts +15 -0
  456. package/src/worker/server-info.ts +168 -0
  457. package/src/worker/tool-catalog.ts +47 -1
  458. package/src/worker/tool-timeout.ts +75 -14
  459. package/src/worker/websocket-protocol.ts +26 -2
  460. package/src/worker/worker-edge-guard.ts +90 -0
  461. package/src/worker/worker-edge-log.ts +65 -0
  462. package/src/worker/worker-entry.ts +52 -0
  463. package/src/worker/worker-mcp-config.ts +23 -0
  464. package/src/worker/worker-metadata.ts +48 -0
  465. package/src/worker/worker-rate-limit-key.ts +30 -0
  466. package/src/worker/worker-runtime-config.ts +20 -0
  467. package/src/worker/worker-static-routes.ts +54 -0
  468. package/src/worker/worker-task-lifetime.ts +18 -0
  469. package/tsconfig.local.json +17 -1
  470. package/wrangler.jsonc +13 -0
  471. package/src/local/cli-approval.mjs +0 -117
  472. package/src/local/operation-state-lock.mjs +0 -92
  473. 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 message = parsed.message;
48
- if (message.type === "ping") return;
49
- if (message.type === "cancel" && typeof message.id === "string") {
50
- this.cancelClientRequest(socket, message.id);
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, { type: "response", id: message.id, ok: false, error: "browser broker request timed out" });
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
- this.routes.set(routedId, { socket, id: message.id, timeout });
81
- if (this.sendExtension({ ...message, id: routedId, timeout_ms: timeoutMs })) return;
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, { type: "response", id: message.id, ok: false, error: "browser extension send failed" });
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
- safeSocketSend(route.socket, { ...message, id: route.id });
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, { type: "response", id: route.id, ok: false, error: message });
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"); } catch {}
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-store.mjs";
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, token, maxPayload, onHttp, onSocket }) {
6
- const server = createServer(onHttp);
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" && protocol === `mbm.${token}` && isAllowedExtensionOrigin(origin)) role = "extension";
20
- if (url.pathname === "/runtime" && protocol === `mbm-runtime.${token}` && !origin) role = "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(); } catch {}
42
- try { server.close(); } catch {}
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
- const action = String(value || "").trim();
13
- if (!NAVIGATION_ACTIONS.has(action) && !PAGE_ACTIONS.has(action)) throw new Error("unsupported browser action");
14
- return action;
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
- const action = String(value || "").trim();
19
- if (!FORM_ACTIONS.has(action)) throw new Error("unsupported form field action");
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 || value === null || value === "") return "auto";
46
- const mode = String(value);
47
- if (!INPUT_MODES.has(mode)) throw new Error("input_mode must be auto, trusted, or dom");
48
- return mode;
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 || value === null || value === "") return "none";
53
- const wait = String(value);
54
- if (!["none", ...LOAD_STATES].includes(wait)) throw new Error("wait_for must be none, domcontentloaded, or complete");
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 || args.state === null || args.state === ""
61
- ? (selector ? "visible" : "")
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 || args.load_state === null || args.load_state === "" ? "" : String(args.load_state);
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 = String(args.action || "").trim();
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 || value === null || value === "") return "";
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 || value === null || value === "") return null;
116
- const number = Number(value);
117
- if (!Number.isInteger(number) || number < min || number > max) throw new Error(`${label} must be an integer from ${min} to ${max}`);
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 || value === null || value === "") return fallback;
123
- const number = Number(value);
124
- if (!Number.isInteger(number) || number < min || number > max) throw new Error(`expected an integer from ${min} to ${max}`);
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
  }