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
@@ -1,68 +1,115 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { spawn, spawnSync } from "node:child_process";
4
- import { mkdirSync, readFileSync, rmSync } from "node:fs";
4
+ import { createHash } from "node:crypto";
5
+ import { readFileSync, rmSync } from "node:fs";
5
6
  import { dirname, join, resolve } from "node:path";
6
7
  import { fileURLToPath } from "node:url";
8
+ import { defaultStateRoot, expandHome, selectedWorkspace } from "../src/local/state.mjs";
9
+ import { ensureOwnerOnlyDirectorySync } from "../src/local/secure-file.mjs";
10
+ import { createCandidateRuntimePrefix, isNonBlockingCandidateRuntimeCleanupError, pruneInactiveCandidateRuntimes } from "./candidate-runtime-store.mjs";
11
+ import { ACTIVATION_SCHEMA_VERSION, writePrereleaseActivation } from "./prerelease-activation.mjs";
7
12
  import { verifyTarball } from "./release-acceptance.mjs";
13
+ import { parseReleaseVersion } from "./release-channel.mjs";
14
+ import { discoverForegroundDaemonRecovery } from "./foreground-daemon-recovery.mjs";
15
+ import { assertPersistentActivationExecutionSurface, persistentActivationSpawnOptions, persistentCandidateFailureMessage, preflightPersistentActivationWorkerAuth, validateActivationRecoveryPayload } from "./persistent-activation-process.mjs";
16
+ import { assertCandidateMatchesCurrentSource, validateCandidateManifest } from "./release-candidate-manifest.mjs";
17
+ import { computePromotionContentDigest } from "./promotion-digest.mjs";
18
+ import { createHardenedNpmSession, settleHardenedNpmSession } from "./hardened-npm-session.mjs";
19
+ import { nestedNpmEnvironment } from "../src/local/npm-environment.mjs";
20
+ import { withReleaseRuntimeLock } from "../src/local/release-runtime-lock.mjs";
21
+ import { releaseCommandFailure, releaseDiagnostic, releaseDiagnosticEvent } from "./release-diagnostic.mjs";
22
+ import { inspectGlobalPackageInstallation } from "./global-package-installation.mjs";
23
+ import { resolveNpmGlobalPrefix } from "./npm-global-prefix.mjs";
24
+ import { resolveNpmCli } from "../src/local/npm-cli.mjs";
25
+ import { publishReleaseBrowserExtension } from "./release-browser-extension-store.mjs";
8
26
 
9
27
  const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
10
28
  const candidateDirectory = join(root, ".release-candidate");
11
29
  const manifestPath = join(candidateDirectory, "manifest.json");
12
- const installPrefix = join(candidateDirectory, "runtime");
13
- const npmCli = process.env.npm_execpath;
14
-
15
- if (!npmCli) fail("candidate startup must run through npm so npm_execpath is available");
30
+ const foregroundInstallPrefix = join(candidateDirectory, "runtime");
31
+ const installOnly = process.argv.includes("--install-only");
32
+ const activateService = process.argv.includes("--activate-service");
33
+ const allowWorkerDeploy = process.argv.includes("--allow-worker-deploy");
34
+ const persistentActivation = activateService && !installOnly;
35
+ const lifecycleNpmCli = process.env.npm_execpath;
36
+ let sourceNpmCli = lifecycleNpmCli;
37
+ let npmCli = "";
38
+ let npmSession = null;
39
+ let persistentExecutionSurface = "local";
16
40
 
17
41
  try {
18
- const manifest = readJson(manifestPath, "release candidate manifest");
19
- if (manifest.result !== "pending") throw new Error("release candidate manifest is not pending");
20
- const tarball = join(candidateDirectory, String(manifest.filename || ""));
42
+ if (persistentActivation) persistentExecutionSurface = assertPersistentActivationExecutionSurface(process.env);
43
+ if (installOnly) sourceNpmCli = resolveNpmCli({ allowLifecycleNpmCli: false, allowFallbackLocations: false });
44
+ if (!sourceNpmCli) throw new Error("candidate live startup must run through npm so npm_execpath is available");
45
+ const currentPackage = readJson(join(root, "package.json"), "current package");
46
+ const manifest = validateCandidateManifest(
47
+ readJson(manifestPath, "release candidate manifest"),
48
+ { packageName: currentPackage.name, packageVersion: currentPackage.version },
49
+ );
50
+ assertCandidateMatchesCurrentSource(manifest, {
51
+ packageName: currentPackage.name,
52
+ packageVersion: currentPackage.version,
53
+ promotionDigest: computePromotionContentDigest(root, { npmCli: sourceNpmCli }),
54
+ });
55
+ const tarball = join(candidateDirectory, manifest.filename);
21
56
  verifyTarball(tarball, manifest);
57
+ if (!installOnly && !allowWorkerDeploy) {
58
+ throw new Error("candidate activation may update the configured same-name Worker; rerun with --allow-worker-deploy to authorize that live candidate deployment");
59
+ }
60
+ const globalPrefix = persistentActivation
61
+ ? resolveNpmGlobalPrefix(lifecycleNpmCli, { cwd: root, env: process.env })
62
+ : "";
63
+ npmSession = await createHardenedNpmSession();
64
+ npmCli = npmSession.cli;
22
65
 
23
66
  const npmVersion = runNpm(["--version"], root).stdout.trim();
24
- if (Number(npmVersion.split(".")[0]) < 12) {
25
- throw new Error(`candidate startup requires npm 12 or newer; current ${npmVersion}`);
67
+ if (npmVersion !== npmSession.version) {
68
+ throw new Error(`candidate startup hardened npm reported ${npmVersion || "no version"}, expected ${npmSession.version}`);
26
69
  }
27
70
 
28
- rmSync(installPrefix, { recursive: true, force: true });
29
- mkdirSync(installPrefix, { recursive: true });
30
- runNpm([
31
- "install",
32
- "--global",
33
- "--prefix", installPrefix,
34
- "--omit=optional",
35
- "--allow-scripts=esbuild,workerd,sharp,fsevents",
36
- tarball,
37
- ], root);
38
-
39
- const globalRoot = runNpm(["root", "--global", "--prefix", installPrefix], root).stdout.trim();
40
- const installedPackage = join(globalRoot, manifest.package_name);
41
- const installed = readJson(join(installedPackage, "package.json"), "installed candidate package");
42
- if (installed.version !== manifest.package_version) {
43
- throw new Error(`installed candidate version ${installed.version} does not match ${manifest.package_version}`);
71
+ const stateRoot = resolve(expandHome(argumentValue("--state-dir") || defaultStateRoot()));
72
+ const forwardedArgs = process.argv.slice(2).filter((value) => ![
73
+ "--install-only", "--allow-worker-deploy", "--activate-service",
74
+ ].includes(value));
75
+ if (persistentActivation) {
76
+ await preflightPersistentActivationWorkerAuth({
77
+ surface: persistentExecutionSurface,
78
+ stateRoot,
79
+ packageRoot: root,
80
+ npmCli,
81
+ env: process.env,
82
+ });
83
+ await withReleaseRuntimeLock(stateRoot, async () => {
84
+ const installPrefix = createCandidateRuntimePrefix({ stateRoot, version: manifest.package_version, shasum: manifest.shasum });
85
+ const installedPackage = installCandidateRuntime({ installPrefix, manifest, tarball });
86
+ const previousInstallation = persistentActivation
87
+ ? currentGlobalInstallation(manifest.package_name, globalPrefix, npmCli)
88
+ : null;
89
+ disposeNpmSession();
90
+ activatePersistentCandidate({
91
+ manifest, installedPackage, installPrefix, stateRoot, forwardedArgs, previousInstallation,
92
+ });
93
+ });
94
+ process.exit(0);
44
95
  }
45
96
 
46
- console.log(`Verified candidate tarball: ${manifest.filename} (${manifest.shasum})`);
47
- console.log(`Installed isolated candidate: ${installedPackage}`);
48
- const installOnly = process.argv.includes("--install-only");
49
- const allowWorkerDeploy = process.argv.includes("--allow-worker-deploy");
97
+ const installPrefix = foregroundInstallPrefix;
98
+ rmSync(installPrefix, { recursive: true, force: true });
99
+ const installedPackage = installCandidateRuntime({ installPrefix, manifest, tarball });
100
+ disposeNpmSession();
50
101
  if (installOnly) {
51
- console.log("Candidate installation check passed; startup was skipped by --install-only.");
102
+ rmSync(installPrefix, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
103
+ console.log("Candidate installation check passed; temporary runtime was removed and startup was skipped by --install-only.");
52
104
  process.exit(0);
53
105
  }
54
106
 
55
- if (!allowWorkerDeploy) {
56
- throw new Error("foreground candidate startup may update the configured same-name Worker; rerun with --allow-worker-deploy to authorize that live candidate deployment");
57
- }
58
-
59
- const forwardedArgs = process.argv.slice(2).filter((value) => value !== "--install-only" && value !== "--allow-worker-deploy");
60
107
  const cli = join(installedPackage, "bin", "machine-mcp.mjs");
61
108
  console.log("Authorized live candidate deployment: startup may update the configured same-name Worker when its version or deployment hash differs.");
62
109
  console.log("Starting the exact candidate in the foreground. Leave this process running while the coding agent verifies the Worker, relay, and local runtime end to end.");
63
110
  const child = spawn(process.execPath, [cli, ...forwardedArgs], {
64
111
  cwd: root,
65
- env: process.env,
112
+ env: nestedNpmEnvironment(process.env),
66
113
  stdio: "inherit",
67
114
  windowsHide: true,
68
115
  });
@@ -83,22 +130,198 @@ try {
83
130
  process.exitCode = code ?? 1;
84
131
  });
85
132
  } catch (error) {
86
- fail(error?.message || error);
133
+ const settled = settleNpmSession(error);
134
+ fail(settled?.message || settled);
135
+ }
136
+
137
+ function installCandidateRuntime({ installPrefix, manifest, tarball }) {
138
+ ensureOwnerOnlyDirectorySync(installPrefix);
139
+ runNpm([
140
+ "install",
141
+ "--dry-run=false",
142
+ "--workspaces=false",
143
+ "--ignore-scripts=false",
144
+ "--global",
145
+ "--prefix", installPrefix,
146
+ "--omit=optional",
147
+ "--include=prod",
148
+ "--package-lock-only=false",
149
+ "--allow-scripts=esbuild,workerd,sharp,fsevents",
150
+ tarball,
151
+ ], root);
152
+ const globalRoot = runNpm(["root", "--json=false", "--parseable=false", "--workspaces=false", "--global", "--prefix", installPrefix], root).stdout.trim();
153
+ const installedPackage = join(globalRoot, manifest.package_name);
154
+ const installed = readJson(join(installedPackage, "package.json"), "installed candidate package");
155
+ if (installed.version !== manifest.package_version) {
156
+ throw new Error(`installed candidate version ${installed.version} does not match ${manifest.package_version}`);
157
+ }
158
+ console.log(`Verified candidate tarball: ${manifest.filename} (${manifest.shasum})`);
159
+ console.log(`Installed isolated candidate: ${installedPackage}`);
160
+ return installedPackage;
161
+ }
162
+
163
+ function activatePersistentCandidate({
164
+ manifest, installedPackage, installPrefix, stateRoot, forwardedArgs, previousInstallation,
165
+ }) {
166
+ const releaseVersion = parseReleaseVersion(manifest.package_version);
167
+ const cli = join(installedPackage, "bin", "machine-mcp.mjs");
168
+ const args = ["activate", ...withoutManagedFlags(forwardedArgs), "--state-dir", stateRoot, "--json"];
169
+ console.log("Activating the exact prerelease as the persistent login daemon. Portable-root startup does not prompt; a separately provisioned Secure Enclave broker may request one user-presence operation.");
170
+ const result = spawnSync(
171
+ process.execPath,
172
+ [cli, ...args],
173
+ persistentActivationSpawnOptions({ cwd: root, env: nestedNpmEnvironment(process.env) }),
174
+ );
175
+ if (result.error) throw result.error;
176
+ if (result.status !== 0) {
177
+ const output = result.stderr || result.stdout;
178
+ const requestedWorkspace = argumentListValue("--workspace", withoutManagedFlags(forwardedArgs))
179
+ || selectedWorkspace(stateRoot);
180
+ const previousRuntime = discoverForegroundDaemonRecovery({
181
+ output, stateRoot, workspace: requestedWorkspace,
182
+ });
183
+ throw new Error(persistentCandidateFailureMessage(output, { cli, stateRoot, previousRuntime }));
184
+ }
185
+ let activation;
186
+ try { activation = JSON.parse(result.stdout); } catch { throw new Error("persistent candidate activation did not return valid JSON"); }
187
+ if (
188
+ activation.ok !== true
189
+ || activation.version !== manifest.package_version
190
+ || activation.daemon?.version !== manifest.package_version
191
+ || activation.worker?.health?.version !== manifest.package_version
192
+ ) {
193
+ throw new Error("persistent candidate activation did not converge on the exact candidate version");
194
+ }
195
+ const recovery = validateActivationRecoveryPayload(activation);
196
+
197
+ let publishedBrowserExtension;
198
+ try {
199
+ publishedBrowserExtension = publishReleaseBrowserExtension({
200
+ stateRoot,
201
+ sourceDirectory: join(installedPackage, "browser-extension"),
202
+ expectedVersion: manifest.package_version,
203
+ });
204
+ } catch (error) {
205
+ throw new Error(
206
+ "persistent candidate activation converged, but the stable browser extension could not be published; the candidate Worker/service is live and must be inspected before retrying",
207
+ { cause: error },
208
+ );
209
+ }
210
+
211
+ let removedRuntimes = [];
212
+ let runtimeCleanupWarning = "";
213
+ try {
214
+ removedRuntimes = pruneInactiveCandidateRuntimes({ stateRoot, activePrefix: installPrefix });
215
+ } catch (error) {
216
+ if (!isNonBlockingCandidateRuntimeCleanupError(error)) throw error;
217
+ runtimeCleanupWarning = boundedCleanupWarning(error);
218
+ }
219
+ let recordPath = "";
220
+ if (releaseVersion.prerelease) {
221
+ recordPath = writePrereleaseActivation({
222
+ schema_version: ACTIVATION_SCHEMA_VERSION,
223
+ package_name: manifest.package_name,
224
+ package_version: manifest.package_version,
225
+ source: "local-candidate",
226
+ shasum: manifest.shasum,
227
+ integrity: manifest.integrity,
228
+ promotion_content_sha256: manifest.promotion_content_sha256,
229
+ activated_at: new Date().toISOString(),
230
+ workspace_hash: workspaceHash(activation.workspace),
231
+ runtime_entry: cli,
232
+ ...(recovery.recovered ? {
233
+ activation_recovered: true,
234
+ activation_recovery_reason: recovery.reason,
235
+ activation_recovery_detail: recovery.detail,
236
+ } : {}),
237
+ ...(previousInstallation ? { global_package_rollback_baseline: previousInstallation } : {}),
238
+ }, stateRoot);
239
+ }
240
+ if (recovery.recovered) {
241
+ console.warn(`Persistent activation used verified candidate-service recovery (${recovery.reason}): ${recovery.detail}`);
242
+ }
243
+ console.log(`Persistent release candidate activated: ${manifest.package_version}`);
244
+ console.log(`Stable browser extension published: ${publishedBrowserExtension.path}`);
245
+ if (recordPath) console.log(`Activation record: ${recordPath}`);
246
+ if (removedRuntimes.length) console.log(`Removed ${removedRuntimes.length} inactive candidate runtime(s).`);
247
+ if (runtimeCleanupWarning) console.warn(`Candidate activation succeeded but inactive runtime cleanup was incomplete: ${runtimeCleanupWarning}`);
248
+ console.log("The Worker and login daemon now run the exact candidate. The terminal may close; the coding agent should verify the live deployment through Machine Bridge.");
249
+ if (previousInstallation?.version) {
250
+ console.log(`Global package rollback baseline retained: ${previousInstallation.version}.`);
251
+ }
252
+ }
253
+
254
+
255
+ function currentGlobalInstallation(packageName, globalPrefix, npmExecutable) {
256
+ const globalRoot = runNpm(
257
+ ["root", "--json=false", "--parseable=false", "--workspaces=false", "--global", "--prefix", globalPrefix],
258
+ root,
259
+ npmExecutable,
260
+ ).stdout.trim();
261
+ return inspectGlobalPackageInstallation(globalRoot, packageName);
262
+ }
263
+
264
+ function withoutManagedFlags(args) {
265
+ const out = [];
266
+ for (let index = 0; index < args.length; index += 1) {
267
+ const value = args[index];
268
+ if (value === "--json" || value.startsWith("--json=")) continue;
269
+ if (value === "--state-dir") { index += 1; continue; }
270
+ if (value.startsWith("--state-dir=")) continue;
271
+ out.push(value);
272
+ }
273
+ return out;
274
+ }
275
+
276
+ function argumentListValue(name, args) {
277
+ const exact = args.find(value => value.startsWith(`${name}=`));
278
+ if (exact) return exact.slice(name.length + 1);
279
+ const index = args.indexOf(name);
280
+ return index >= 0 ? args[index + 1] || "" : "";
281
+ }
282
+
283
+ function argumentValue(name) {
284
+ const exact = process.argv.find((value) => value.startsWith(`${name}=`));
285
+ if (exact) return exact.slice(name.length + 1);
286
+ const index = process.argv.indexOf(name);
287
+ return index >= 0 ? process.argv[index + 1] : "";
288
+ }
289
+
290
+ function workspaceHash(workspace) {
291
+ const value = process.platform === "win32" ? String(workspace).toLowerCase() : String(workspace);
292
+ return createHash("sha256").update(value).digest("hex").slice(0, 24);
87
293
  }
88
294
 
89
- function runNpm(args, cwd) {
90
- const result = spawnSync(process.execPath, [npmCli, ...args], {
295
+ function runNpm(args, cwd, npmExecutable = npmCli) {
296
+ const cli = String(npmExecutable || "");
297
+ if (!cli) throw new Error("nested npm CLI is unavailable");
298
+ const result = spawnSync(process.execPath, [cli, ...args], {
91
299
  cwd,
92
300
  encoding: "utf8",
93
- env: process.env,
301
+ env: nestedNpmEnvironment(process.env),
94
302
  timeout: 300_000,
303
+ killSignal: "SIGKILL",
95
304
  windowsHide: true,
96
305
  });
97
- if (result.error) throw result.error;
98
- if (result.status !== 0) throw new Error(`npm ${args[0]} failed: ${result.stderr || result.stdout}`);
306
+ if (result.error || result.status !== 0) throw new Error(releaseCommandFailure("npm", args, result));
99
307
  return result;
100
308
  }
101
309
 
310
+ function disposeNpmSession() {
311
+ const error = settleNpmSession();
312
+ if (error) throw error;
313
+ }
314
+
315
+ function settleNpmSession(primaryError = null) {
316
+ const session = npmSession;
317
+ npmSession = null;
318
+ return settleHardenedNpmSession(session, primaryError, "release candidate failed and hardened npm temporary cleanup was incomplete");
319
+ }
320
+
321
+ function boundedCleanupWarning(error) {
322
+ return releaseDiagnostic(error?.message || error || "candidate runtime cleanup failed", 600);
323
+ }
324
+
102
325
  function readJson(path, label) {
103
326
  try {
104
327
  return JSON.parse(readFileSync(path, "utf8"));
@@ -108,6 +331,6 @@ function readJson(path, label) {
108
331
  }
109
332
 
110
333
  function fail(message) {
111
- console.error(`release candidate startup failed: ${message}`);
334
+ console.error(JSON.stringify(releaseDiagnosticEvent("release.candidate.failed", message, 1200)));
112
335
  process.exit(1);
113
336
  }
@@ -1,28 +1,49 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { spawnSync } from "node:child_process";
4
- import { readdirSync } from "node:fs";
4
+ import { readFileSync, readdirSync } from "node:fs";
5
5
  import { dirname, extname, join, relative, resolve } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
+ import * as vm from "node:vm";
7
8
 
8
9
  const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
9
- const roots = ["bin", "src/local", "scripts", "tests", "browser-extension"];
10
+ const roots = ["bin", "src/local", "src/shared", "scripts", "tests", "browser-extension", ".github/scripts"];
10
11
  const files = roots.flatMap((entry) => collect(join(root, entry)))
11
12
  .filter((file) => [".js", ".mjs"].includes(extname(file)))
12
13
  .sort();
14
+ const parserMode = process.argv.includes("--vm-parse");
13
15
 
14
- for (const file of files) {
15
- const result = spawnSync(process.execPath, ["--check", file], {
16
- cwd: root,
17
- encoding: "utf8",
18
- windowsHide: true,
19
- stdio: ["ignore", "pipe", "pipe"],
20
- });
21
- if (result.error) throw result.error;
22
- if (result.status !== 0) {
23
- process.stderr.write(`syntax check failed: ${relative(root, file)}\n${result.stderr || result.stdout}`);
24
- process.exit(1);
16
+ if (parserMode) {
17
+ if (typeof vm.SourceTextModule !== "function") throw new Error("VM module parser is unavailable");
18
+ for (const file of files) {
19
+ try {
20
+ let source = readFileSync(file, "utf8");
21
+ if (source.startsWith("#!")) source = source.replace(/^#![^\n]*(?:\n|$)/, "\n");
22
+ new vm.SourceTextModule(source, { identifier: relative(root, file) });
23
+ } catch (error) {
24
+ process.stderr.write(`syntax check failed: ${relative(root, file)}\n${String(error?.stack || error)}\n`);
25
+ process.exit(1);
26
+ }
25
27
  }
28
+ process.exit(0);
29
+ }
30
+
31
+ const parser = spawnSync(process.execPath, [
32
+ "--experimental-vm-modules",
33
+ "--no-warnings",
34
+ fileURLToPath(import.meta.url),
35
+ "--vm-parse",
36
+ ], {
37
+ cwd: root,
38
+ encoding: "utf8",
39
+ windowsHide: true,
40
+ stdio: ["ignore", "pipe", "pipe"],
41
+ shell: false,
42
+ });
43
+ if (parser.error) throw parser.error;
44
+ if (parser.status !== 0) {
45
+ process.stderr.write(parser.stderr || parser.stdout || "syntax parser failed without output\n");
46
+ process.exit(parser.status ?? 1);
26
47
  }
27
48
 
28
49
  if (process.platform !== "win32") {
@@ -31,6 +52,7 @@ if (process.platform !== "win32") {
31
52
  encoding: "utf8",
32
53
  windowsHide: true,
33
54
  stdio: ["ignore", "pipe", "pipe"],
55
+ shell: false,
34
56
  });
35
57
  if (shell.error) throw shell.error;
36
58
  if (shell.status !== 0) {
@@ -39,7 +61,7 @@ if (process.platform !== "win32") {
39
61
  }
40
62
  }
41
63
 
42
- console.log(`syntax check ok (${files.length} JavaScript files${process.platform === "win32" ? "" : "; mbm shell wrapper"})`);
64
+ console.log(`syntax check ok (${files.length} JavaScript files in one parser process${process.platform === "win32" ? "" : "; mbm shell wrapper"})`);
43
65
 
44
66
  function collect(directory) {
45
67
  const files = [];
@@ -0,0 +1,15 @@
1
+ export async function runWithStableGeneration({ captureGeneration, run, label = "verification inputs" }) {
2
+ if (typeof captureGeneration !== "function") throw new TypeError("captureGeneration must be a function");
3
+ if (typeof run !== "function") throw new TypeError("run must be a function");
4
+ const before = captureGeneration();
5
+ let result;
6
+ let runError;
7
+ try { result = await run(); }
8
+ catch (error) { runError = error; }
9
+ const after = captureGeneration();
10
+ if (after !== before) {
11
+ throw new Error(`${label} changed during verification; discard this run (${before} -> ${after})`);
12
+ }
13
+ if (runError) throw runError;
14
+ return result;
15
+ }
@@ -0,0 +1,37 @@
1
+ import { spawn } from "node:child_process";
2
+ import process from "node:process";
3
+
4
+ export const VERIFICATION_IDLE_SLEEP_GUARD_ENV = "MBM_CHECK_IDLE_SLEEP_GUARD";
5
+
6
+ export async function rerunVerificationUnderIdleSleepGuard(options = {}) {
7
+ const platform = options.platform ?? process.platform;
8
+ const env = options.env ?? process.env;
9
+ if (platform !== "darwin" || env[VERIFICATION_IDLE_SLEEP_GUARD_ENV] === "1") return null;
10
+
11
+ const executable = String(options.execPath || process.execPath);
12
+ const argv = Array.isArray(options.argv) ? options.argv.map(String) : process.argv.slice(1);
13
+ if (!executable || argv.length === 0 || !argv[0]) {
14
+ throw new Error("verification idle-sleep guard requires the current Node executable and entrypoint");
15
+ }
16
+ const spawnProcess = typeof options.spawnProcess === "function" ? options.spawnProcess : spawn;
17
+ let child;
18
+ try {
19
+ child = spawnProcess("/usr/bin/caffeinate", ["-i", executable, ...argv], {
20
+ env: { ...env, [VERIFICATION_IDLE_SLEEP_GUARD_ENV]: "1" },
21
+ stdio: "inherit",
22
+ shell: false,
23
+ windowsHide: true,
24
+ });
25
+ } catch (error) {
26
+ throw new Error("could not start the macOS verification idle-sleep guard", { cause: error });
27
+ }
28
+ return await new Promise((resolvePromise, rejectPromise) => {
29
+ child.once("error", (error) => {
30
+ rejectPromise(new Error("could not start the macOS verification idle-sleep guard", { cause: error }));
31
+ });
32
+ child.once("close", (code, signal) => {
33
+ if (Number.isInteger(code)) resolvePromise(code);
34
+ else rejectPromise(new Error(`macOS verification idle-sleep guard ended by signal ${signal || "unknown"}`));
35
+ });
36
+ });
37
+ }
@@ -0,0 +1,108 @@
1
+ import { mkdirSync, readFileSync, rmSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { captureCoverageGeneration } from "./coverage-generation.mjs";
4
+ import { replaceFileAtomicallySync } from "../src/local/exclusive-file.mjs";
5
+ import { readBoundedRegularFileWithInfoSync } from "../src/local/secure-file.mjs";
6
+
7
+ const RECEIPT_SCHEMA_VERSION = 1;
8
+ const RECEIPT_MAX_BYTES = 16 * 1024;
9
+ export const FULL_VERIFICATION_RECEIPT_MAX_AGE_MS = 6 * 60 * 60 * 1000;
10
+
11
+ const VERIFICATION_RUN_GENERATION_OPTIONS = {
12
+ roots: ["src", "scripts", "tests", "browser-extension", ".github", "docs", "bin", "native", "release-acceptance", ".release-candidate"],
13
+ files: [
14
+ "package.json", "package-lock.json", "tsconfig.json", "tsconfig.local.json", "wrangler.jsonc", "eslint.config.mjs",
15
+ ".npmrc", ".node-version", ".nvmrc", ".gitattributes", ".gitignore", ".privacy-denylist", ".privacy-denylist.example",
16
+ "mbm", "mbm.cmd", "README.md", "CHANGELOG.md", "CONTRIBUTING.md", "SECURITY.md", "LICENSE",
17
+ "CODE_OF_CONDUCT.md", "GOVERNANCE.md", "SUPPORT.md", "AGENTS.md",
18
+ ],
19
+ };
20
+
21
+ const VERIFIED_SOURCE_GENERATION_OPTIONS = {
22
+ ...VERIFICATION_RUN_GENERATION_OPTIONS,
23
+ roots: VERIFICATION_RUN_GENERATION_OPTIONS.roots.filter((name) => name !== ".release-candidate"),
24
+ };
25
+
26
+ export function captureVerificationRunGeneration(root) {
27
+ return captureCoverageGeneration(root, VERIFICATION_RUN_GENERATION_OPTIONS);
28
+ }
29
+
30
+ export function captureVerifiedSourceGeneration(root) {
31
+ return captureCoverageGeneration(root, VERIFIED_SOURCE_GENERATION_OPTIONS);
32
+ }
33
+
34
+ export function fullVerificationReceiptPath(root) {
35
+ return join(root, ".project-local", "full-verification.json");
36
+ }
37
+
38
+ export function clearFullVerificationReceipt(root) {
39
+ rmSync(fullVerificationReceiptPath(root), { force: true });
40
+ }
41
+
42
+ export function writeFullVerificationReceipt(root, generation, options = {}) {
43
+ if (!/^[0-9a-f]{64}$/.test(String(generation || ""))) throw new TypeError("full verification generation must be a SHA-256 digest");
44
+ const pkg = readPackage(root);
45
+ const verifiedAtMs = Number(options.now ?? Date.now());
46
+ if (!Number.isFinite(verifiedAtMs)) throw new TypeError("full verification receipt timestamp must be finite");
47
+ const receipt = {
48
+ schema_version: RECEIPT_SCHEMA_VERSION,
49
+ mode: "full",
50
+ generation_sha256: generation,
51
+ package_name: pkg.name,
52
+ package_version: pkg.version,
53
+ node_version: process.version,
54
+ platform: process.platform,
55
+ arch: process.arch,
56
+ verified_at: new Date(verifiedAtMs).toISOString(),
57
+ };
58
+ const path = fullVerificationReceiptPath(root);
59
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
60
+ replaceFileAtomicallySync(path, `${JSON.stringify(receipt, null, 2)}\n`, { mode: 0o600 });
61
+ return receipt;
62
+ }
63
+
64
+ export function assertFreshFullVerificationReceipt(root, options = {}) {
65
+ const path = fullVerificationReceiptPath(root);
66
+ let receipt;
67
+ try {
68
+ const snapshot = readBoundedRegularFileWithInfoSync(path, RECEIPT_MAX_BYTES, "full verification receipt", {
69
+ verifyPathIdentity: true,
70
+ rejectMultipleLinks: true,
71
+ });
72
+ receipt = JSON.parse(snapshot.buffer.toString("utf8"));
73
+ } catch (error) {
74
+ if (error?.code === "ENOENT") throw staleReceiptError("full verification receipt is missing");
75
+ throw staleReceiptError(`full verification receipt is unreadable or invalid: ${error.message}`);
76
+ }
77
+ const pkg = readPackage(root);
78
+ const generation = (options.captureGeneration || captureVerifiedSourceGeneration)(root);
79
+ const now = Number(options.now ?? Date.now());
80
+ const verifiedAt = Date.parse(String(receipt?.verified_at || ""));
81
+ const maximumAgeMs = Number(options.maximumAgeMs ?? FULL_VERIFICATION_RECEIPT_MAX_AGE_MS);
82
+ const valid = receipt?.schema_version === RECEIPT_SCHEMA_VERSION
83
+ && receipt?.mode === "full"
84
+ && receipt?.generation_sha256 === generation
85
+ && receipt?.package_name === pkg.name
86
+ && receipt?.package_version === pkg.version
87
+ && receipt?.node_version === process.version
88
+ && receipt?.platform === process.platform
89
+ && receipt?.arch === process.arch
90
+ && Number.isFinite(verifiedAt)
91
+ && Number.isFinite(now)
92
+ && Number.isFinite(maximumAgeMs)
93
+ && maximumAgeMs >= 0
94
+ && now >= verifiedAt
95
+ && now - verifiedAt <= maximumAgeMs;
96
+ if (!valid) throw staleReceiptError("full verification receipt does not match the current source, runtime, or freshness window");
97
+ return receipt;
98
+ }
99
+
100
+ function readPackage(root) {
101
+ return JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
102
+ }
103
+
104
+ function staleReceiptError(reason) {
105
+ const error = new Error(`${reason}; run npm run check:full on the frozen tree before release:candidate`);
106
+ error.code = "MBM_FULL_VERIFICATION_REQUIRED";
107
+ return error;
108
+ }