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,325 @@
1
+ import { createHash, randomBytes } from "node:crypto";
2
+ import { MCP_PROTOCOL_VERSION } from "../src/shared/mcp-protocol.mjs";
3
+ import { generateAccountPassword } from "../src/local/account-admin.mjs";
4
+
5
+ const REQUEST_TIMEOUT_MS = 15_000;
6
+ const SMALL_JSON_LIMIT = 64 * 1024;
7
+ const MCP_RESPONSE_LIMIT = 1024 * 1024;
8
+ const CANARY_ACCOUNT_DISPLAY_NAME = "Machine Bridge Release OAuth Canary";
9
+ export const RELEASE_OAUTH_CANARY_CALLBACK = "https://oauth-canary.invalid/callback";
10
+
11
+ export async function runReleaseOAuthCanaryFlow({
12
+ admin,
13
+ workerUrl,
14
+ packageName,
15
+ packageVersion,
16
+ fetchImpl = fetch,
17
+ randomBytesImpl = randomBytes,
18
+ }) {
19
+ if (!admin || typeof admin.create !== "function" || typeof admin.remove !== "function" || typeof admin.removeClient !== "function"
20
+ || typeof admin.list !== "function" || typeof admin.listClients !== "function") {
21
+ throw new TypeError("release OAuth canary requires an account-admin client");
22
+ }
23
+ await cleanupStaleCanaryState(admin);
24
+ const origin = normalizeCanaryWorkerUrl(workerUrl);
25
+ const name = requiredPackageName(packageName);
26
+ const version = requiredPackageVersion(packageVersion);
27
+ const accountName = `release-canary-${randomBytesImpl(8).toString("hex")}`;
28
+ const password = generateAccountPassword();
29
+ const verifier = randomBytesImpl(32).toString("base64url");
30
+ if (!/^[A-Za-z0-9._~-]{43,128}$/.test(verifier)) throw new Error("release OAuth canary generated an invalid PKCE verifier");
31
+ const challenge = createHash("sha256").update(verifier).digest("base64url");
32
+ const stateValue = randomBytesImpl(24).toString("base64url");
33
+ const scope = `${name} offline_access`;
34
+ const resource = `${origin}/mcp`;
35
+ let accountId = "";
36
+ let clientId = "";
37
+ let primaryError = null;
38
+ let workerVersion = "";
39
+
40
+ try {
41
+ const created = await canaryAdmin("temporary account creation", () => admin.create({
42
+ name: accountName, displayName: CANARY_ACCOUNT_DISPLAY_NAME, role: "reviewer", password,
43
+ }));
44
+ accountId = String(created?.account?.account_id || "");
45
+ if (!/^acct_[A-Za-z0-9_-]{20,96}$/.test(accountId)) throw new Error("release OAuth canary account creation returned an invalid account identity");
46
+
47
+ const registration = await fetchBoundedJson(fetchImpl, "dynamic registration", `${origin}/oauth/register`, {
48
+ method: "POST",
49
+ headers: { "content-type": "application/json", "user-agent": "machine-bridge-mcp-release-canary" },
50
+ body: JSON.stringify({
51
+ client_name: "Machine Bridge release OAuth canary",
52
+ redirect_uris: [RELEASE_OAUTH_CANARY_CALLBACK],
53
+ grant_types: ["authorization_code", "refresh_token"],
54
+ response_types: ["code"],
55
+ token_endpoint_auth_method: "none",
56
+ }),
57
+ }, SMALL_JSON_LIMIT, 201);
58
+ clientId = String(registration.client_id || "");
59
+ if (!/^mcp_client_[A-Za-z0-9_-]{43}$/.test(clientId)) throw new Error("release OAuth canary registration returned an invalid client identity");
60
+
61
+ const authorizeResponse = await fetchBounded(fetchImpl, "authorization", `${origin}/oauth/authorize`, {
62
+ method: "POST",
63
+ headers: { "content-type": "application/x-www-form-urlencoded", "user-agent": "machine-bridge-mcp-release-canary" },
64
+ body: new URLSearchParams({
65
+ response_type: "code",
66
+ client_id: clientId,
67
+ redirect_uri: RELEASE_OAUTH_CANARY_CALLBACK,
68
+ code_challenge: challenge,
69
+ code_challenge_method: "S256",
70
+ scope,
71
+ resource,
72
+ state: stateValue,
73
+ account_name: accountName,
74
+ account_password: password,
75
+ }),
76
+ redirect: "manual",
77
+ });
78
+ if (authorizeResponse.status !== 303) throw new Error(`release OAuth canary authorization failed with HTTP ${authorizeResponse.status}`);
79
+ const location = authorizeResponse.headers.get("location");
80
+ if (!location) throw new Error("release OAuth canary authorization omitted the redirect location");
81
+ const redirect = new URL(location);
82
+ const code = redirect.searchParams.get("code") || "";
83
+ if (redirect.origin + redirect.pathname !== RELEASE_OAUTH_CANARY_CALLBACK || redirect.searchParams.get("state") !== stateValue
84
+ || redirect.searchParams.get("iss") !== origin || !/^mcp_code_[A-Za-z0-9_-]{43}$/.test(code)) {
85
+ throw new Error("release OAuth canary authorization redirect failed validation");
86
+ }
87
+
88
+ const token = await fetchBoundedJson(fetchImpl, "authorization-code token exchange", `${origin}/oauth/token`, {
89
+ method: "POST",
90
+ headers: { "content-type": "application/x-www-form-urlencoded", "user-agent": "machine-bridge-mcp-release-canary" },
91
+ body: new URLSearchParams({
92
+ grant_type: "authorization_code",
93
+ code,
94
+ client_id: clientId,
95
+ redirect_uri: RELEASE_OAUTH_CANARY_CALLBACK,
96
+ code_verifier: verifier,
97
+ resource,
98
+ }),
99
+ }, SMALL_JSON_LIMIT, 200);
100
+ const accessToken = requiredToken(token.access_token, "access");
101
+ const refreshToken = requiredToken(token.refresh_token, "refresh");
102
+ if (token.token_type !== "Bearer" || token.scope !== scope) throw new Error("release OAuth canary token response changed the expected public-client contract");
103
+
104
+ const firstInfo = await callServerInfo(fetchImpl, origin, accessToken, 1);
105
+ workerVersion = String(firstInfo?.version || "");
106
+ if (workerVersion !== version) throw new Error("release OAuth canary authenticated MCP reached the wrong Worker version");
107
+
108
+ const refreshed = await fetchBoundedJson(fetchImpl, "refresh-token exchange", `${origin}/oauth/token`, {
109
+ method: "POST",
110
+ headers: { "content-type": "application/x-www-form-urlencoded", "user-agent": "machine-bridge-mcp-release-canary" },
111
+ body: new URLSearchParams({
112
+ grant_type: "refresh_token",
113
+ refresh_token: refreshToken,
114
+ client_id: clientId,
115
+ resource,
116
+ scope,
117
+ }),
118
+ }, SMALL_JSON_LIMIT, 200);
119
+ const refreshedAccess = requiredToken(refreshed.access_token, "refreshed access");
120
+ const refreshedToken = requiredToken(refreshed.refresh_token, "refreshed refresh");
121
+ if (refreshedAccess === accessToken || refreshedToken === refreshToken) {
122
+ throw new Error("release OAuth canary refresh did not rotate both credentials");
123
+ }
124
+ const refreshedInfo = await callServerInfo(fetchImpl, origin, refreshedAccess, 2);
125
+ if (String(refreshedInfo?.version || "") !== version) {
126
+ throw new Error("release OAuth canary refreshed credential reached the wrong Worker version");
127
+ }
128
+ } catch (error) {
129
+ primaryError = error;
130
+ }
131
+
132
+ const cleanupErrors = [];
133
+ if (clientId) {
134
+ try { await admin.removeClient({ clientId }); }
135
+ catch (error) { cleanupErrors.push(new Error("release OAuth canary temporary client cleanup failed", { cause: error })); }
136
+ }
137
+ if (accountId) {
138
+ try { await admin.remove({ accountId }); }
139
+ catch (error) { cleanupErrors.push(new Error("release OAuth canary temporary account cleanup failed", { cause: error })); }
140
+ }
141
+ if (primaryError && cleanupErrors.length) {
142
+ throw new AggregateError([primaryError, ...cleanupErrors], "release OAuth canary failed and temporary state cleanup was incomplete");
143
+ }
144
+ if (primaryError) throw primaryError;
145
+ if (cleanupErrors.length) throw new AggregateError(cleanupErrors, "release OAuth canary temporary state cleanup was incomplete");
146
+
147
+ return Object.freeze({
148
+ workerVersion,
149
+ authorizationCodeExchange: true,
150
+ authenticatedMcp: true,
151
+ refreshRotation: true,
152
+ refreshedMcp: true,
153
+ cleanupCompleted: true,
154
+ });
155
+ }
156
+
157
+ async function cleanupStaleCanaryState(admin) {
158
+ const accounts = await canaryAdmin("stale account discovery", () => admin.list());
159
+ const accountList = Array.isArray(accounts?.accounts) ? accounts.accounts : [];
160
+ if (!accountList.some((account) => account?.active === true && account?.role === "owner")) {
161
+ throw new Error("release OAuth canary requires an existing active owner so synthetic reviewer state remains fully removable");
162
+ }
163
+ const staleAccounts = [];
164
+ for (const account of Array.isArray(accounts?.accounts) ? accounts.accounts : []) {
165
+ if (!/^release-canary-[0-9a-f]{16}$/.test(String(account?.name || ""))) continue;
166
+ if (account?.role !== "reviewer" || account?.display_name !== CANARY_ACCOUNT_DISPLAY_NAME) {
167
+ throw new Error("release OAuth canary refused to remove a synthetic-name account without the synthetic reviewer marker");
168
+ }
169
+ const accountId = String(account.account_id || "");
170
+ if (!/^acct_[A-Za-z0-9_-]{20,96}$/.test(accountId)) {
171
+ throw new Error("release OAuth canary found malformed stale synthetic account state");
172
+ }
173
+ staleAccounts.push(accountId);
174
+ }
175
+ if (staleAccounts.length === 0) return;
176
+ const staleAccountIds = new Set(staleAccounts);
177
+ const clients = await canaryAdmin("stale client discovery", () => admin.listClients());
178
+ for (const client of Array.isArray(clients?.clients) ? clients.clients : []) {
179
+ if (client?.client_name !== "Machine Bridge release OAuth canary"
180
+ || !staleAccountIds.has(String(client?.trusted_account_id || ""))) continue;
181
+ const clientId = String(client.client_id || "");
182
+ if (!/^mcp_client_[A-Za-z0-9_-]{43}$/.test(clientId)) {
183
+ throw new Error("release OAuth canary found malformed stale synthetic client state");
184
+ }
185
+ await canaryAdmin("stale client cleanup", () => admin.removeClient({ clientId }));
186
+ }
187
+ for (const accountId of staleAccounts) {
188
+ await canaryAdmin("stale account cleanup", () => admin.remove({ accountId }));
189
+ }
190
+ }
191
+
192
+ async function canaryAdmin(stage, callback) {
193
+ try { return await callback(); }
194
+ catch (error) { throw new Error(`release OAuth canary ${stage} failed`, { cause: error }); }
195
+ }
196
+
197
+ export function normalizeCanaryWorkerUrl(value) {
198
+ const url = new URL(String(value || ""));
199
+ if (url.protocol !== "https:" || url.username || url.password || url.pathname !== "/" || url.search || url.hash) {
200
+ throw new Error("release OAuth canary requires a credential-free HTTPS Worker origin");
201
+ }
202
+ return url.origin;
203
+ }
204
+
205
+ async function callServerInfo(fetchImpl, workerUrl, accessToken, id) {
206
+ const response = await fetchBounded(fetchImpl, "authenticated MCP", `${workerUrl}/mcp`, {
207
+ method: "POST",
208
+ headers: {
209
+ "content-type": "application/json",
210
+ accept: "application/json, text/event-stream",
211
+ authorization: `Bearer ${accessToken}`,
212
+ "MCP-Protocol-Version": MCP_PROTOCOL_VERSION,
213
+ "Mcp-Method": "tools/call",
214
+ "Mcp-Name": "server_info",
215
+ "user-agent": "machine-bridge-mcp-release-canary",
216
+ },
217
+ body: JSON.stringify({
218
+ jsonrpc: "2.0",
219
+ id,
220
+ method: "tools/call",
221
+ params: {
222
+ name: "server_info",
223
+ arguments: { detail: "summary" },
224
+ _meta: {
225
+ "io.modelcontextprotocol/protocolVersion": MCP_PROTOCOL_VERSION,
226
+ "io.modelcontextprotocol/clientCapabilities": {},
227
+ "io.modelcontextprotocol/clientInfo": { name: "machine-bridge-mcp-release-canary", version: "1" },
228
+ },
229
+ },
230
+ }),
231
+ });
232
+ if (response.status !== 200) throw new Error(`release OAuth canary authenticated MCP failed with HTTP ${response.status}`);
233
+ const payload = await readMcpPayload(response);
234
+ if (payload?.error || !payload?.result?.structuredContent) throw new Error("release OAuth canary authenticated MCP returned an invalid result");
235
+ return payload.result.structuredContent;
236
+ }
237
+
238
+ async function readMcpPayload(response) {
239
+ const text = await readBoundedText(response, MCP_RESPONSE_LIMIT, "authenticated MCP response");
240
+ if (response.headers.get("content-type")?.toLowerCase().startsWith("text/event-stream")) {
241
+ let last = null;
242
+ for (const line of text.split(/\r?\n/)) {
243
+ if (!line.startsWith("data: ")) continue;
244
+ try { last = JSON.parse(line.slice(6)); }
245
+ catch { throw new Error("release OAuth canary MCP event stream contained invalid JSON"); }
246
+ }
247
+ if (!last) throw new Error("release OAuth canary MCP event stream contained no result");
248
+ return last;
249
+ }
250
+ try { return JSON.parse(text); }
251
+ catch { throw new Error("release OAuth canary MCP response was not valid JSON"); }
252
+ }
253
+
254
+ async function fetchBoundedJson(fetchImpl, stage, url, init, maximumBytes, expectedStatus) {
255
+ const response = await fetchBounded(fetchImpl, stage, url, init);
256
+ if (response.status !== expectedStatus) throw new Error(`release OAuth canary ${stage} failed with HTTP ${response.status}`);
257
+ const text = await readBoundedText(response, maximumBytes, `${stage} response`);
258
+ let value;
259
+ try { value = JSON.parse(text); }
260
+ catch { throw new Error(`release OAuth canary ${stage} response was not valid JSON`); }
261
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
262
+ throw new Error(`release OAuth canary ${stage} response was not a JSON object`);
263
+ }
264
+ return value;
265
+ }
266
+
267
+ async function fetchBounded(fetchImpl, stage, url, init) {
268
+ try {
269
+ return await fetchImpl(url, { redirect: "error", ...init, signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS), cache: "no-store" });
270
+ } catch (error) {
271
+ throw new Error(`release OAuth canary ${stage} request failed`, { cause: error });
272
+ }
273
+ }
274
+
275
+ async function readBoundedText(response, maximumBytes, label) {
276
+ const declared = Number(response.headers.get("content-length") || "0");
277
+ if (Number.isFinite(declared) && declared > maximumBytes) {
278
+ try { await response.body?.cancel(); } catch { /* cleanup only */ }
279
+ throw new Error(`release OAuth canary ${label} exceeded the size limit`);
280
+ }
281
+ if (!response.body) throw new Error(`release OAuth canary ${label} was empty`);
282
+ const reader = response.body.getReader();
283
+ const chunks = [];
284
+ let bytes = 0;
285
+ try {
286
+ for (;;) {
287
+ const { done, value } = await reader.read();
288
+ if (done) break;
289
+ if (!value) continue;
290
+ bytes += value.byteLength;
291
+ if (bytes > maximumBytes) {
292
+ try { await reader.cancel("response size limit reached"); } catch { /* cleanup only */ }
293
+ throw new Error(`release OAuth canary ${label} exceeded the size limit`);
294
+ }
295
+ chunks.push(value);
296
+ }
297
+ } finally {
298
+ reader.releaseLock();
299
+ }
300
+ const output = new Uint8Array(bytes);
301
+ let offset = 0;
302
+ for (const chunk of chunks) { output.set(chunk, offset); offset += chunk.byteLength; }
303
+ try { return new TextDecoder("utf-8", { fatal: true }).decode(output); }
304
+ catch { throw new Error(`release OAuth canary ${label} was not valid UTF-8`); }
305
+ }
306
+
307
+ function requiredToken(value, label) {
308
+ const token = String(value || "");
309
+ if (!/^mcp_(?:at|rt)_[A-Za-z0-9_-]{43}$/.test(token)) throw new Error(`release OAuth canary ${label} token was invalid`);
310
+ return token;
311
+ }
312
+
313
+ function requiredPackageName(value) {
314
+ const name = String(value || "");
315
+ if (name !== "machine-bridge-mcp") throw new Error("release OAuth canary package name is invalid");
316
+ return name;
317
+ }
318
+
319
+ function requiredPackageVersion(value) {
320
+ const version = String(value || "");
321
+ if (!version || version.length > 128 || /[\r\n\t\u0000-\u001f\u007f]/.test(version)) {
322
+ throw new Error("release OAuth canary package version is invalid");
323
+ }
324
+ return version;
325
+ }
@@ -0,0 +1,100 @@
1
+ import { join } from "node:path";
2
+ import { replaceFileAtomicallySync } from "../src/local/exclusive-file.mjs";
3
+ import { readBoundedRegularFileSync } from "../src/local/secure-file.mjs";
4
+
5
+ export const RELEASE_OAUTH_CANARY_SCHEMA_VERSION = 1;
6
+ const MAX_EVIDENCE_BYTES = 16 * 1024;
7
+ const EVIDENCE_FILENAME = "oauth-canary.json";
8
+ const FIELDS = new Set([
9
+ "schema_version", "result", "package_name", "package_version", "shasum", "integrity",
10
+ "promotion_content_sha256", "worker_version", "authorization_code_exchange",
11
+ "authenticated_mcp", "refresh_rotation", "refreshed_mcp", "cleanup_completed", "completed_at",
12
+ ]);
13
+
14
+ export function releaseOAuthCanaryPath(root) {
15
+ return join(root, ".release-candidate", EVIDENCE_FILENAME);
16
+ }
17
+
18
+ export function writeReleaseOAuthCanaryEvidence(root, value) {
19
+ const normalized = validateReleaseOAuthCanaryEvidence(value);
20
+ const path = releaseOAuthCanaryPath(root);
21
+ replaceFileAtomicallySync(path, `${JSON.stringify(normalized, null, 2)}\n`, { mode: 0o600 });
22
+ return path;
23
+ }
24
+
25
+ export function readReleaseOAuthCanaryEvidence(root, expected = {}) {
26
+ const path = releaseOAuthCanaryPath(root);
27
+ let bytes;
28
+ try {
29
+ bytes = readBoundedRegularFileSync(path, MAX_EVIDENCE_BYTES, "release OAuth canary evidence", {
30
+ verifyPathIdentity: true,
31
+ rejectMultipleLinks: true,
32
+ });
33
+ } catch (error) {
34
+ if (error?.code === "ENOENT") throw new Error("release OAuth canary evidence is missing; run the deployed OAuth canary before acceptance", { cause: error });
35
+ throw new Error("release OAuth canary evidence is unavailable", { cause: error });
36
+ }
37
+ let parsed;
38
+ try { parsed = JSON.parse(bytes.toString("utf8")); }
39
+ catch (error) { throw new Error("release OAuth canary evidence is invalid JSON", { cause: error }); }
40
+ const evidence = validateReleaseOAuthCanaryEvidence(parsed);
41
+ for (const [field, value] of Object.entries(expected)) {
42
+ if (value !== undefined && evidence[field] !== value) {
43
+ throw new Error(`release OAuth canary evidence does not match the candidate: ${field}`);
44
+ }
45
+ }
46
+ return evidence;
47
+ }
48
+
49
+ export function validateReleaseOAuthCanaryEvidence(value) {
50
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
51
+ throw new Error("release OAuth canary evidence must be an object");
52
+ }
53
+ const unknown = Object.keys(value).filter((key) => !FIELDS.has(key));
54
+ if (unknown.length) throw new Error(`release OAuth canary evidence contains unsupported fields: ${unknown.join(", ")}`);
55
+ if (value.schema_version !== RELEASE_OAUTH_CANARY_SCHEMA_VERSION || value.result !== "passed") {
56
+ throw new Error("release OAuth canary evidence is not passed current-schema state");
57
+ }
58
+ if (value.package_name !== "machine-bridge-mcp") throw new Error("release OAuth canary package name is invalid");
59
+ const packageVersion = boundedString(value.package_version, 128, "package version");
60
+ const workerVersion = boundedString(value.worker_version, 128, "Worker version");
61
+ if (workerVersion !== packageVersion) throw new Error("release OAuth canary Worker version does not match the package");
62
+ if (!/^[0-9a-f]{40}$/.test(String(value.shasum || ""))) throw new Error("release OAuth canary SHA-1 is invalid");
63
+ if (!/^sha512-[A-Za-z0-9+/]+={0,2}$/.test(String(value.integrity || ""))) {
64
+ throw new Error("release OAuth canary integrity is invalid");
65
+ }
66
+ if (!/^[0-9a-f]{64}$/.test(String(value.promotion_content_sha256 || ""))) {
67
+ throw new Error("release OAuth canary promotion digest is invalid");
68
+ }
69
+ for (const field of [
70
+ "authorization_code_exchange", "authenticated_mcp", "refresh_rotation", "refreshed_mcp", "cleanup_completed",
71
+ ]) {
72
+ if (value[field] !== true) throw new Error(`release OAuth canary required check did not pass: ${field}`);
73
+ }
74
+ const completedAt = Date.parse(String(value.completed_at || ""));
75
+ if (!Number.isFinite(completedAt)) throw new Error("release OAuth canary completion timestamp is invalid");
76
+ return Object.freeze({
77
+ schema_version: RELEASE_OAUTH_CANARY_SCHEMA_VERSION,
78
+ result: "passed",
79
+ package_name: value.package_name,
80
+ package_version: packageVersion,
81
+ shasum: String(value.shasum),
82
+ integrity: String(value.integrity),
83
+ promotion_content_sha256: String(value.promotion_content_sha256),
84
+ worker_version: workerVersion,
85
+ authorization_code_exchange: true,
86
+ authenticated_mcp: true,
87
+ refresh_rotation: true,
88
+ refreshed_mcp: true,
89
+ cleanup_completed: true,
90
+ completed_at: new Date(completedAt).toISOString(),
91
+ });
92
+ }
93
+
94
+ function boundedString(value, maximum, label) {
95
+ const text = String(value || "");
96
+ if (!text || text.length > maximum || /[\r\n\t\u0000-\u001f\u007f]/.test(text)) {
97
+ throw new Error(`release OAuth canary ${label} is invalid`);
98
+ }
99
+ return text;
100
+ }
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFileSync } from "node:fs";
4
+ import { dirname, join, resolve } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { accountAdminClient } from "../src/local/cli-account-admin.mjs";
7
+ import { inspectWorkspaceDaemon } from "../src/local/daemon-process.mjs";
8
+ import { resolveNpmCli } from "../src/local/npm-cli.mjs";
9
+ import { releaseControlEnvironmentIsTrusted } from "../src/local/resource-release-control-executable.mjs";
10
+ import { defaultStateRoot, expandHome, loadState, selectedWorkspace } from "../src/local/state.mjs";
11
+ import { computePromotionContentDigest } from "./promotion-digest.mjs";
12
+ import { assertPrereleaseActivationRuntimeRoot, readPrereleaseActivation } from "./prerelease-activation.mjs";
13
+ import { parseReleaseVersion } from "./release-channel.mjs";
14
+ import { assertCandidateMatchesCurrentSource, validateCandidateManifest } from "./release-candidate-manifest.mjs";
15
+ import { releaseDiagnosticEvent } from "./release-diagnostic.mjs";
16
+ import { normalizeCanaryWorkerUrl, runReleaseOAuthCanaryFlow } from "./release-oauth-canary-core.mjs";
17
+ import {
18
+ RELEASE_OAUTH_CANARY_SCHEMA_VERSION,
19
+ writeReleaseOAuthCanaryEvidence,
20
+ } from "./release-oauth-canary-evidence.mjs";
21
+
22
+ const runtimeRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
23
+ const root = resolve(process.cwd());
24
+ const candidateManifestPath = join(root, ".release-candidate", "manifest.json");
25
+ const CANARY_FLAG = "--allow-live-oauth-canary";
26
+
27
+ try {
28
+ const canaryArgs = process.argv.slice(2);
29
+ if (canaryArgs.length !== 1 || canaryArgs[0] !== CANARY_FLAG) {
30
+ throw new Error(`release OAuth canary requires exact argv: ${CANARY_FLAG}`);
31
+ }
32
+ if (process.execArgv.length !== 0) {
33
+ throw new Error("release OAuth canary refuses Node CLI startup options; run exact direct Node argv with no preload, loader, debugger, profiler, or runtime override flags");
34
+ }
35
+ if (!releaseControlEnvironmentIsTrusted(process.env)) {
36
+ throw new Error("release OAuth canary refused Node/native debugging, loader, profiling, or TLS environment overrides; run it from a clean terminal environment");
37
+ }
38
+ const evidencePath = await runCanary();
39
+ console.log("Deployed OAuth canary passed: authorization-code exchange, authenticated MCP, refresh rotation, refreshed MCP, and cleanup all succeeded.");
40
+ console.log(`OAuth canary evidence: ${evidencePath}`);
41
+ } catch (error) {
42
+ fail(error?.message || error);
43
+ }
44
+
45
+ async function runCanary() {
46
+ const runtimePackage = readJson(join(runtimeRoot, "package.json"), "runtime package.json");
47
+ const pkg = readJson(join(root, "package.json"), "package.json");
48
+ if (pkg.name !== runtimePackage.name || pkg.version !== runtimePackage.version) {
49
+ throw new Error("release OAuth canary source package identity does not match the activated runtime package");
50
+ }
51
+ const manifest = validateCandidateManifest(readJson(candidateManifestPath, "release candidate manifest"), {
52
+ packageName: runtimePackage.name,
53
+ packageVersion: runtimePackage.version,
54
+ });
55
+
56
+ const stateRoot = resolve(expandHome(defaultStateRoot()));
57
+ const workspace = resolve(expandHome(selectedWorkspace(stateRoot)));
58
+ const state = loadState(workspace, { stateDir: stateRoot });
59
+ const daemon = inspectWorkspaceDaemon(state, {
60
+ expectedVersion: manifest.package_version,
61
+ expectedEntryScript: join(runtimeRoot, "bin", "machine-mcp.mjs"),
62
+ expectedNodeExecutable: process.execPath,
63
+ expectedNodeVersion: process.versions.node,
64
+ });
65
+ if (!daemon.verified_service_daemon || !daemon.startup_readiness_verified) {
66
+ throw new Error(`release OAuth canary requires the exact activated service daemon (${daemon.identity_reason || "startup_not_ready"})`);
67
+ }
68
+ const workerUrl = normalizeCanaryWorkerUrl(state.worker?.url);
69
+ if (state.worker?.deployedVersion !== manifest.package_version) {
70
+ throw new Error("release OAuth canary refused a Worker whose recorded deployed version does not match the candidate");
71
+ }
72
+ const parsedVersion = parseReleaseVersion(manifest.package_version);
73
+ if (parsedVersion.prerelease) verifyPrereleaseActivation(manifest, stateRoot);
74
+
75
+ const npmCli = resolveNpmCli({ allowLifecycleNpmCli: false, allowFallbackLocations: false });
76
+ assertCandidateMatchesCurrentSource(manifest, {
77
+ packageName: pkg.name,
78
+ packageVersion: pkg.version,
79
+ promotionDigest: computePromotionContentDigest(root, { npmCli }),
80
+ });
81
+
82
+ let admin;
83
+ try { admin = await accountAdminClient(state); }
84
+ catch (error) { throw new Error("release OAuth canary account-administration session setup failed", { cause: error }); }
85
+ const result = await runReleaseOAuthCanaryFlow({
86
+ admin,
87
+ workerUrl,
88
+ packageName: manifest.package_name,
89
+ packageVersion: manifest.package_version,
90
+ });
91
+ return writeReleaseOAuthCanaryEvidence(root, {
92
+ schema_version: RELEASE_OAUTH_CANARY_SCHEMA_VERSION,
93
+ result: "passed",
94
+ package_name: manifest.package_name,
95
+ package_version: manifest.package_version,
96
+ shasum: manifest.shasum,
97
+ integrity: manifest.integrity,
98
+ promotion_content_sha256: manifest.promotion_content_sha256,
99
+ worker_version: result.workerVersion,
100
+ authorization_code_exchange: result.authorizationCodeExchange,
101
+ authenticated_mcp: result.authenticatedMcp,
102
+ refresh_rotation: result.refreshRotation,
103
+ refreshed_mcp: result.refreshedMcp,
104
+ cleanup_completed: result.cleanupCompleted,
105
+ completed_at: new Date().toISOString(),
106
+ });
107
+ }
108
+
109
+ function verifyPrereleaseActivation(manifest, stateRoot) {
110
+ const activation = readPrereleaseActivation(manifest.package_version, stateRoot);
111
+ assertPrereleaseActivationRuntimeRoot(activation, runtimeRoot);
112
+ for (const field of ["shasum", "integrity", "promotion_content_sha256"]) {
113
+ if (activation[field] !== manifest[field]) {
114
+ throw new Error(`release OAuth canary activation record does not match the candidate: ${field}`);
115
+ }
116
+ }
117
+ }
118
+
119
+ function readJson(path, label) {
120
+ try { return JSON.parse(readFileSync(path, "utf8")); }
121
+ catch (error) { throw new Error(`${label} is unavailable or invalid`, { cause: error }); }
122
+ }
123
+
124
+ function fail(message) {
125
+ console.error(JSON.stringify(releaseDiagnosticEvent("release.oauth_canary.failed", message, 1200)));
126
+ process.exit(1);
127
+ }
@@ -0,0 +1,67 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { realpathSync } from "node:fs";
3
+ import { join, resolve } from "node:path";
4
+ import { withOwnerStateLock } from "../src/local/owner-state-lock.mjs";
5
+ import { resolveTrustedGitExecutable } from "../src/local/trusted-git-executable.mjs";
6
+
7
+ const CONFIRMATION_FLAG = "--owner-confirm";
8
+ const LOCK_DIRECTORY = "machine-bridge-release-state";
9
+ const LOCK_FILE = "github-publication.lock";
10
+ const GIT_METADATA_TIMEOUT_MS = 30_000;
11
+
12
+ export function assertGithubPublicationAuthorized(options = {}) {
13
+ let argv = process.argv.slice(2);
14
+ if (Array.isArray(options.argv)) argv = options.argv.map(String);
15
+ const stdin = options.stdin === undefined ? process.stdin : options.stdin;
16
+ const stdout = options.stdout === undefined ? process.stdout : options.stdout;
17
+ const stderr = options.stderr === undefined ? process.stderr : options.stderr;
18
+ if (!argv.includes(CONFIRMATION_FLAG)) {
19
+ throw new Error(`GitHub publication requires explicit owner authorization represented by ${CONFIRMATION_FLAG}`);
20
+ }
21
+ return Object.freeze({
22
+ confirmation_flag: CONFIRMATION_FLAG,
23
+ interactive_terminal: stdin.isTTY === true && stdout.isTTY === true && stderr.isTTY === true,
24
+ });
25
+ }
26
+
27
+ export function withGithubPublicationLock(root, callback, options = {}) {
28
+ const stateRoot = options.stateRoot
29
+ ? resolve(String(options.stateRoot))
30
+ : resolveGithubPublicationStateRoot(root);
31
+ return withOwnerStateLock(stateRoot, callback, {
32
+ purpose: "github-publication",
33
+ fileName: LOCK_FILE,
34
+ label: "GitHub publication",
35
+ timeoutMs: options.timeoutMs ?? 1_000,
36
+ pollMs: options.pollMs ?? 25,
37
+ maxAgeMs: options.maxAgeMs ?? 6 * 60 * 60 * 1_000,
38
+ });
39
+ }
40
+
41
+ export function resolveGithubPublicationStateRoot(root) {
42
+ const repositoryRoot = resolve(String(root || ""));
43
+ const git = resolveTrustedGitExecutable({ workspace: repositoryRoot });
44
+ const result = spawnSync(git, ["rev-parse", "--git-common-dir"], {
45
+ cwd: repositoryRoot,
46
+ encoding: "utf8",
47
+ timeout: GIT_METADATA_TIMEOUT_MS,
48
+ killSignal: "SIGKILL",
49
+ windowsHide: true,
50
+ });
51
+ return githubPublicationStateRootFromGitResult(repositoryRoot, result);
52
+ }
53
+
54
+ export function githubPublicationStateRootFromGitResult(repositoryRoot, result) {
55
+ if (result?.error || result?.status !== 0) throw new Error("could not resolve the common Git publication state directory");
56
+ const path = String(result?.stdout ?? "").trim();
57
+ if (!path || path.includes("\0")) throw new Error("Git returned an invalid common publication state directory");
58
+ const commonDirectory = resolve(repositoryRoot, path);
59
+ try {
60
+ return join(realpathSync.native(commonDirectory), LOCK_DIRECTORY);
61
+ } catch {
62
+ throw new Error("common Git publication state directory is unavailable");
63
+ }
64
+ }
65
+
66
+ export const githubPublicationConfirmationFlag = CONFIRMATION_FLAG;
67
+ export const githubPublicationGitTimeoutMs = GIT_METADATA_TIMEOUT_MS;