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,284 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { mkdirSync, readFileSync } from "node:fs";
4
+ import { dirname, join, resolve } from "node:path";
5
+ import { spawnSync } from "node:child_process";
6
+ import { fileURLToPath } from "node:url";
7
+ import { readBoundedRegularFileSync } from "../src/local/secure-file.mjs";
8
+ import { replaceFileAtomicallySync } from "../src/local/exclusive-file.mjs";
9
+ import { readPrereleaseActivation } from "./prerelease-activation.mjs";
10
+ import { computePromotionContentDigest } from "./promotion-digest.mjs";
11
+ import {
12
+ SOAK_CONFIRMATION,
13
+ assertSoakEligiblePrerelease,
14
+ assertStablePromotion,
15
+ compareReleaseVersions,
16
+ formatSoakDuration,
17
+ minimumSoakSeconds,
18
+ parseReleaseVersion,
19
+ requiresSoakForStable,
20
+ } from "./release-channel.mjs";
21
+ import { verifyCurrentReleaseAcceptance } from "./release-acceptance.mjs";
22
+ import { resolveTrustedGitExecutable } from "../src/local/trusted-git-executable.mjs";
23
+ import { readGithubPrerelease, readPublishedNpmPrerelease } from "./published-release.mjs";
24
+ import { createHardenedNpmSession } from "./hardened-npm-session.mjs";
25
+ import { releaseCommandFailure, releaseDiagnosticEvent } from "./release-diagnostic.mjs";
26
+
27
+ export const SOAK_SCHEMA_VERSION = 1;
28
+ const MAX_SOAK_RECORD_BYTES = 64 * 1024;
29
+ const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
30
+
31
+ if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
32
+ main().catch((error) => {
33
+ console.error(JSON.stringify(releaseDiagnosticEvent("release.soak.failed", error?.message || error, 1400)));
34
+ process.exitCode = 1;
35
+ });
36
+ }
37
+
38
+ async function main() {
39
+ const mode = process.argv[2] || "--verify";
40
+ if (mode === "--record") {
41
+ await recordCurrentPrereleaseSoak(root, { confirmation: argumentValue("--confirm"), stateRoot: argumentValue("--state-dir") || undefined });
42
+ } else if (mode === "--verify") verifyCurrentStableSoak(root);
43
+ else if (mode === "--status") printStatus(root);
44
+ else throw new Error("usage: node scripts/release-soak.mjs [--record --confirm TEXT [--state-dir DIR]|--verify|--status]");
45
+ }
46
+
47
+ export function soakRecordPath(repositoryRoot, stableVersion) {
48
+ const stable = parseReleaseVersion(stableVersion);
49
+ if (stable.prerelease) throw new Error("soak record path requires a stable version");
50
+ return join(repositoryRoot, "release-soak", `v${stable.raw}.json`);
51
+ }
52
+
53
+ export async function recordCurrentPrereleaseSoak(repositoryRoot, options = {}) {
54
+ const pkg = readPackage(repositoryRoot);
55
+ const prerelease = assertSoakEligiblePrerelease(pkg.version);
56
+ const acceptance = verifyCurrentReleaseAcceptance(repositoryRoot);
57
+ if (!acceptance.required) throw new Error("prerelease package is missing local candidate acceptance policy");
58
+ const activation = readPrereleaseActivation(prerelease.raw, options.stateRoot);
59
+ if (activation.source !== "npm-prerelease") {
60
+ throw new Error("formal soak starts only after installing the published npm beta/rc; a local candidate activation is insufficient");
61
+ }
62
+ if (activation.integrity !== acceptance.metadata.integrity || activation.shasum !== acceptance.metadata.shasum) {
63
+ throw new Error("activated prerelease bytes do not match the locally accepted prerelease package");
64
+ }
65
+ const promotionDigest = computePromotionContentDigest(repositoryRoot);
66
+ if (activation.promotion_content_sha256 !== promotionDigest) {
67
+ throw new Error("activated prerelease promotion digest does not match the current source tree");
68
+ }
69
+ const previousStable = options.previousStableVersion || latestStableTag(repositoryRoot, prerelease.baseVersion);
70
+ const requiredSeconds = minimumSoakSeconds(prerelease.baseVersion, previousStable);
71
+ const activatedAt = Date.parse(activation.activated_at);
72
+ const acceptedAt = Number.isFinite(options.now) ? Number(options.now) : Date.now();
73
+ const observedSeconds = Math.floor((acceptedAt - activatedAt) / 1000);
74
+ if (observedSeconds < requiredSeconds) {
75
+ throw new Error(`prerelease soak is incomplete: observed ${formatSoakDuration(Math.max(0, observedSeconds))}, required ${formatSoakDuration(requiredSeconds)}`);
76
+ }
77
+ const expected = soakConfirmationPhrase(pkg.name, prerelease.raw, requiredSeconds);
78
+ if (options.confirmation !== expected) throw new Error(`soak confirmation must exactly match: ${expected}`);
79
+
80
+ const published = options.published || await readPublishedWithHardenedNpm(pkg.name, prerelease);
81
+ if (
82
+ published.version !== prerelease.raw
83
+ || published.integrity !== acceptance.metadata.integrity
84
+ || published.shasum !== acceptance.metadata.shasum
85
+ || published.distTag !== prerelease.npmTag
86
+ || published.publishedAt !== activation.published_at
87
+ ) {
88
+ throw new Error("published npm prerelease metadata does not match the accepted and activated package");
89
+ }
90
+ const github = options.github || readGithubPrerelease(prerelease.raw, {
91
+ expectedArtifactSha256: acceptance.artifactSha256,
92
+ });
93
+ if (github.tag !== `v${prerelease.raw}` || github.isPrerelease !== true) {
94
+ throw new Error("GitHub prerelease is missing or not marked as a prerelease");
95
+ }
96
+ const record = validateSoakRecord({
97
+ schema_version: SOAK_SCHEMA_VERSION,
98
+ result: "passed",
99
+ confirmation: SOAK_CONFIRMATION,
100
+ package_name: pkg.name,
101
+ stable_version: prerelease.baseVersion,
102
+ prerelease_version: prerelease.raw,
103
+ prerelease_channel: prerelease.channel,
104
+ prerelease_shasum: acceptance.metadata.shasum,
105
+ prerelease_integrity: acceptance.metadata.integrity,
106
+ promotion_content_sha256: promotionDigest,
107
+ activated_at: activation.activated_at,
108
+ published_at: published.publishedAt,
109
+ accepted_at: new Date(acceptedAt).toISOString(),
110
+ minimum_soak_seconds: requiredSeconds,
111
+ observed_soak_seconds: observedSeconds,
112
+ npm_dist_tag: prerelease.npmTag,
113
+ github_prerelease_tag: github.tag,
114
+ previous_stable_version: previousStable,
115
+ known_blocking_issues: 0,
116
+ });
117
+ const file = soakRecordPath(repositoryRoot, prerelease.baseVersion);
118
+ mkdirSync(dirname(file), { recursive: true });
119
+ replaceFileAtomicallySync(file, `${JSON.stringify(record, null, 2)}\n`, { mode: 0o644 });
120
+ console.log(`Published prerelease soak accepted: ${file}`);
121
+ console.log("Stable promotion may change only synchronized version metadata; any functional package change requires a new prerelease and a new soak window.");
122
+ return record;
123
+ }
124
+
125
+ async function readPublishedWithHardenedNpm(packageName, prerelease) {
126
+ const session = await createHardenedNpmSession();
127
+ let result = null;
128
+ let primaryError = null;
129
+ try {
130
+ result = readPublishedNpmPrerelease(packageName, prerelease.raw, prerelease.npmTag, {
131
+ npmCli: session.cli,
132
+ env: process.env,
133
+ });
134
+ } catch (error) {
135
+ primaryError = error;
136
+ }
137
+ let cleanupError = null;
138
+ try { session.dispose(); } catch (error) { cleanupError = error; }
139
+ if (primaryError && cleanupError) {
140
+ throw new AggregateError([primaryError, cleanupError], "npm prerelease verification failed and hardened npm cleanup was incomplete");
141
+ }
142
+ if (primaryError) throw primaryError;
143
+ if (cleanupError) throw cleanupError;
144
+ return result;
145
+ }
146
+
147
+ export function verifyCurrentStableSoak(repositoryRoot, options = {}) {
148
+ const pkg = readPackage(repositoryRoot);
149
+ if (!requiresSoakForStable(pkg.version)) return { required: false, version: pkg.version };
150
+ const file = soakRecordPath(repositoryRoot, pkg.version);
151
+ const readRecord = options.readBoundedRegularFileSync || readBoundedRegularFileSync;
152
+ let bytes;
153
+ try {
154
+ bytes = readRecord(file, MAX_SOAK_RECORD_BYTES, "release soak record", {
155
+ verifyPathIdentity: true,
156
+ rejectMultipleLinks: true,
157
+ });
158
+ } catch (error) {
159
+ if (error?.code === "ENOENT") throw new Error(`stable release soak record is missing: ${file}`, { cause: error });
160
+ throw new Error("release soak record is unavailable", { cause: error });
161
+ }
162
+ let parsed;
163
+ try { parsed = JSON.parse(bytes.toString("utf8")); }
164
+ catch (error) { throw new Error("release soak record is invalid", { cause: error }); }
165
+ const promotionDigest = computePromotionContentDigest(repositoryRoot, options);
166
+ const record = validateSoakRecord(parsed, { stableVersion: pkg.version, promotionDigest });
167
+ return { required: true, version: pkg.version, record, promotionDigest };
168
+ }
169
+
170
+ export function validateSoakRecord(value, options = {}) {
171
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("release soak record must be an object");
172
+ const allowed = new Set([
173
+ "schema_version", "result", "confirmation", "package_name", "stable_version", "prerelease_version",
174
+ "prerelease_channel", "prerelease_shasum", "prerelease_integrity", "promotion_content_sha256",
175
+ "activated_at", "published_at", "accepted_at", "minimum_soak_seconds", "observed_soak_seconds",
176
+ "npm_dist_tag", "github_prerelease_tag", "previous_stable_version", "known_blocking_issues",
177
+ ]);
178
+ const unknown = Object.keys(value).filter((key) => !allowed.has(key));
179
+ if (unknown.length) throw new Error(`release soak record contains unsupported fields: ${unknown.join(", ")}`);
180
+ if (value.schema_version !== SOAK_SCHEMA_VERSION) throw new Error("unsupported release soak schema");
181
+ if (value.result !== "passed" || value.confirmation !== SOAK_CONFIRMATION) throw new Error("release soak is not accepted");
182
+ if (value.package_name !== "machine-bridge-mcp") throw new Error("release soak package name is invalid");
183
+ const { stable, prerelease } = assertStablePromotion(value.stable_version, value.prerelease_version);
184
+ if (value.prerelease_channel !== prerelease.channel) throw new Error("release soak prerelease channel is inconsistent");
185
+ if (value.npm_dist_tag !== prerelease.npmTag) throw new Error("release soak npm dist-tag is inconsistent");
186
+ if (value.github_prerelease_tag !== `v${prerelease.raw}`) throw new Error("release soak GitHub prerelease tag is inconsistent");
187
+ if (!/^[0-9a-f]{40}$/.test(String(value.prerelease_shasum || ""))) throw new Error("release soak prerelease SHA-1 is invalid");
188
+ if (!/^sha512-[A-Za-z0-9+/]+={0,2}$/.test(String(value.prerelease_integrity || ""))) throw new Error("release soak prerelease integrity is invalid");
189
+ if (!/^[0-9a-f]{64}$/.test(String(value.promotion_content_sha256 || ""))) throw new Error("release soak promotion digest is invalid");
190
+ const activatedAt = Date.parse(String(value.activated_at || ""));
191
+ const publishedAt = Date.parse(String(value.published_at || ""));
192
+ const acceptedAt = Date.parse(String(value.accepted_at || ""));
193
+ if (![activatedAt, publishedAt, acceptedAt].every(Number.isFinite)) throw new Error("release soak timestamps are invalid");
194
+ if (publishedAt > activatedAt + 5 * 60 * 1000 || activatedAt > acceptedAt) throw new Error("release soak timestamp ordering is invalid");
195
+ if (!Number.isSafeInteger(value.minimum_soak_seconds) || value.minimum_soak_seconds < 60) throw new Error("release soak minimum duration is invalid");
196
+ if (!Number.isSafeInteger(value.observed_soak_seconds) || value.observed_soak_seconds < value.minimum_soak_seconds) throw new Error("release soak observed duration is insufficient");
197
+ const elapsed = Math.floor((acceptedAt - activatedAt) / 1000);
198
+ if (value.observed_soak_seconds > elapsed + 5 || value.observed_soak_seconds < elapsed - 5) throw new Error("release soak observed duration does not match its timestamps");
199
+ if (value.known_blocking_issues !== 0) throw new Error("stable release is blocked by known prerelease issues");
200
+ const previous = parseReleaseVersion(value.previous_stable_version);
201
+ if (previous.prerelease || compareReleaseVersions(stable, previous) <= 0) throw new Error("release soak previous stable version is invalid");
202
+ const expectedMinimum = minimumSoakSeconds(stable.raw, previous.raw);
203
+ if (value.minimum_soak_seconds < expectedMinimum) throw new Error("release soak duration is below the active policy minimum");
204
+ if (options.stableVersion && stable.raw !== parseReleaseVersion(options.stableVersion).raw) throw new Error("release soak record targets another stable version");
205
+ if (options.promotionDigest && value.promotion_content_sha256 !== options.promotionDigest) {
206
+ throw new Error("stable package content differs from the accepted prerelease; publish a new prerelease and restart soak");
207
+ }
208
+ return Object.freeze({
209
+ schema_version: SOAK_SCHEMA_VERSION,
210
+ result: "passed",
211
+ confirmation: SOAK_CONFIRMATION,
212
+ package_name: "machine-bridge-mcp",
213
+ stable_version: stable.raw,
214
+ prerelease_version: prerelease.raw,
215
+ prerelease_channel: prerelease.channel,
216
+ prerelease_shasum: String(value.prerelease_shasum),
217
+ prerelease_integrity: String(value.prerelease_integrity),
218
+ promotion_content_sha256: String(value.promotion_content_sha256),
219
+ activated_at: new Date(activatedAt).toISOString(),
220
+ published_at: new Date(publishedAt).toISOString(),
221
+ accepted_at: new Date(acceptedAt).toISOString(),
222
+ minimum_soak_seconds: Number(value.minimum_soak_seconds),
223
+ observed_soak_seconds: Number(value.observed_soak_seconds),
224
+ npm_dist_tag: prerelease.npmTag,
225
+ github_prerelease_tag: `v${prerelease.raw}`,
226
+ previous_stable_version: previous.raw,
227
+ known_blocking_issues: 0,
228
+ });
229
+ }
230
+
231
+ export function soakConfirmationPhrase(name, prereleaseVersion, requiredSeconds) {
232
+ return `I SOAK-TESTED ${name} ${prereleaseVersion} FOR AT LEAST ${formatSoakDuration(requiredSeconds)} WITH NO BLOCKING ISSUES`;
233
+ }
234
+
235
+ function latestStableTag(repositoryRoot, targetBaseVersion) {
236
+ const target = parseReleaseVersion(targetBaseVersion);
237
+ const git = resolveTrustedGitExecutable({ workspace: repositoryRoot });
238
+ const result = run(git, ["tag", "--list", "v*"], { cwd: repositoryRoot });
239
+ const versions = result.stdout.split(/\r?\n/).map((tag) => tag.trim().replace(/^v/, "")).filter(Boolean)
240
+ .map((value) => { try { return parseReleaseVersion(value); } catch { return null; } })
241
+ .filter((value) => value && !value.prerelease && compareReleaseVersions(value, target) < 0)
242
+ .sort(compareReleaseVersions);
243
+ const previous = versions.at(-1);
244
+ if (!previous) throw new Error(`could not determine the previous stable version before ${target.raw}`);
245
+ return previous.raw;
246
+ }
247
+
248
+ function printStatus(repositoryRoot) {
249
+ const pkg = readPackage(repositoryRoot);
250
+ const parsed = parseReleaseVersion(pkg.version);
251
+ if (parsed.prerelease) {
252
+ console.log(JSON.stringify({ version: parsed.raw, channel: parsed.channel, stable_target: parsed.baseVersion, soak_record: soakRecordPath(repositoryRoot, parsed.baseVersion) }, null, 2));
253
+ return;
254
+ }
255
+ const result = verifyCurrentStableSoak(repositoryRoot);
256
+ console.log(JSON.stringify(result, null, 2));
257
+ }
258
+
259
+ function readPackage(repositoryRoot) {
260
+ const value = JSON.parse(readFileSync(join(repositoryRoot, "package.json"), "utf8"));
261
+ if (value.name !== "machine-bridge-mcp") throw new Error("unexpected package name");
262
+ parseReleaseVersion(value.version);
263
+ return value;
264
+ }
265
+
266
+ function run(file, args, options = {}) {
267
+ const result = spawnSync(file, args, {
268
+ cwd: options.cwd || root,
269
+ encoding: "utf8",
270
+ env: process.env,
271
+ timeout: 30_000,
272
+ killSignal: "SIGKILL",
273
+ windowsHide: true,
274
+ });
275
+ if (result.error || result.status !== 0) throw new Error(releaseCommandFailure(file, args, result));
276
+ return result;
277
+ }
278
+
279
+ function argumentValue(name) {
280
+ const exact = process.argv.find((value) => value.startsWith(`${name}=`));
281
+ if (exact) return exact.slice(name.length + 1);
282
+ const index = process.argv.indexOf(name);
283
+ return index >= 0 ? process.argv[index + 1] : "";
284
+ }
@@ -1,7 +1,7 @@
1
1
  export function tagSyncError({ scope = "local", tag, head, commit }) {
2
2
  const label = scope === "remote" ? "remote tag" : "local tag";
3
3
  if (!commit) {
4
- return `${label} ${tag} is missing; run npm run release before npm publish`;
4
+ return `${label} ${tag} is missing; run the applicable explicitly authorized GitHub release command with --owner-confirm before npm publish`;
5
5
  }
6
6
  if (commit !== head) {
7
7
  return `${label} ${tag} points to ${commit}, not HEAD ${head}`;
@@ -1,19 +1,56 @@
1
- import { checkTasks } from "./check-plan.mjs";
1
+ import { readFileSync } from "node:fs";
2
+ import { availableParallelism } from "node:os";
3
+ import { fileURLToPath } from "node:url";
4
+ import { FAST_CHECK_TASKS, SERIAL_FAST_CHECK_TASKS, checkTasks } from "./check-plan.mjs";
2
5
  import { runVerificationPlan } from "./check-runner.mjs";
6
+ import { rerunVerificationUnderIdleSleepGuard } from "./verification-idle-sleep-guard.mjs";
7
+ import { runWithStableGeneration } from "./verification-generation-guard.mjs";
8
+ import {
9
+ captureVerificationRunGeneration,
10
+ captureVerifiedSourceGeneration,
11
+ clearFullVerificationReceipt,
12
+ writeFullVerificationReceipt,
13
+ } from "./verification-state.mjs";
14
+
15
+ const guardedExitCode = await rerunVerificationUnderIdleSleepGuard();
16
+ if (guardedExitCode !== null) process.exit(guardedExitCode);
3
17
 
4
18
  const mode = process.argv[2] || "full";
19
+ const root = fileURLToPath(new URL("../", import.meta.url));
5
20
  const tasks = checkTasks(mode);
6
-
21
+ const packageScripts = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).scripts || {};
22
+ const serialFastTasks = new Set(SERIAL_FAST_CHECK_TASKS);
23
+ const parallelFastTasks = new Set(FAST_CHECK_TASKS.filter((task) => !serialFastTasks.has(task)));
7
24
  try {
8
- await runVerificationPlan({
9
- mode,
10
- tasks,
11
- npmCli: process.env.npm_execpath,
12
- verbose: process.env.MBM_CHECK_VERBOSE === "1",
25
+ if (mode === "full") clearFullVerificationReceipt(root);
26
+ await runWithStableGeneration({
27
+ label: `${mode} verification inputs`,
28
+ captureGeneration: () => captureVerificationRunGeneration(root),
29
+ run: () => runVerificationPlan({
30
+ mode,
31
+ tasks,
32
+ npmCli: process.env.npm_execpath,
33
+ verbose: process.env.MBM_CHECK_VERBOSE === "1",
34
+ concurrency: checkConcurrency(process.env.MBM_CHECK_CONCURRENCY),
35
+ parallelTaskNames: parallelFastTasks,
36
+ packageScripts,
37
+ }),
13
38
  });
39
+ if (mode === "full") writeFullVerificationReceipt(root, captureVerifiedSourceGeneration(root));
14
40
  } catch (error) {
15
41
  if (error?.message && !String(error.message).startsWith("verification task failed:")) {
16
42
  console.error(error.message);
17
43
  }
18
44
  process.exit(Number(error?.exitCode) || 1);
19
45
  }
46
+
47
+ function checkConcurrency(value) {
48
+ if (value !== undefined && value !== "") {
49
+ const configured = Number(value);
50
+ if (!Number.isSafeInteger(configured) || configured < 1 || configured > 16) {
51
+ throw new Error("MBM_CHECK_CONCURRENCY must be an integer from 1 to 16");
52
+ }
53
+ return configured;
54
+ }
55
+ return Math.max(1, Math.min(4, availableParallelism()));
56
+ }
@@ -0,0 +1,21 @@
1
+ import { dirname, resolve } from "node:path";
2
+ import process from "node:process";
3
+ import { fileURLToPath } from "node:url";
4
+ import { runCompletedWranglerCommand } from "./wrangler-command-lifecycle.mjs";
5
+
6
+ const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
7
+ const wrangler = resolve(root, "node_modules", "wrangler", "bin", "wrangler.js");
8
+ export const WRANGLER_DRY_RUN_COMPLETION_MARKER = "--dry-run: exiting now.";
9
+
10
+ export async function runWorkerDryRun(options = {}) {
11
+ await runCompletedWranglerCommand({
12
+ ...options,
13
+ cwd: options.cwd || root,
14
+ wranglerPath: options.wranglerPath || wrangler,
15
+ args: ["deploy", "--dry-run"],
16
+ label: "wrangler deploy --dry-run",
17
+ completionMarker: WRANGLER_DRY_RUN_COMPLETION_MARKER,
18
+ });
19
+ }
20
+
21
+ if (resolve(process.argv[1] || "") === fileURLToPath(import.meta.url)) await runWorkerDryRun();
@@ -69,7 +69,8 @@ function primaryLocation(result) {
69
69
 
70
70
  function normalizeSarifPath(value, run, sarifFile) {
71
71
  let path = String(value || "<unknown>").replaceAll("\\", "/");
72
- try { path = decodeURIComponent(path); } catch {}
72
+ try { path = decodeURIComponent(path); }
73
+ catch { /* Malformed percent-encoding remains a literal path and is still normalized below. */ }
73
74
  if (path.startsWith("file://")) path = fileURLToPath(path).replaceAll("\\", "/");
74
75
  const baseId = run.originalUriBaseIds?.["%SRCROOT%"]?.uri;
75
76
  if (baseId && path.startsWith(String(baseId))) path = path.slice(String(baseId).length);
@@ -0,0 +1,106 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { homedir } from "node:os";
3
+ import { dirname, resolve } from "node:path";
4
+ import { fileURLToPath, pathToFileURL } from "node:url";
5
+ import packageJson from "../package.json" with { type: "json" };
6
+ import { nestedNpmEnvironment } from "../src/local/npm-environment.mjs";
7
+ import { releaseCommandFailure } from "./release-diagnostic.mjs";
8
+
9
+ const MAX_SBOM_BYTES = 4 * 1024 * 1024;
10
+ const SBOM_TIMEOUT_MS = 30_000;
11
+ const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
12
+
13
+ export function generateAndValidateSbom(options = {}) {
14
+ const npmCli = String(options.npmCli || process.env.npm_execpath || "").trim();
15
+ if (!npmCli) throw new Error("sbom check must run through an npm lifecycle so npm_execpath is available");
16
+ const cwd = resolve(options.cwd || root);
17
+ const result = spawnSync(process.execPath, [npmCli, "sbom", "--workspaces=false", "--global=false", "--prefix", root, "--sbom-format", "cyclonedx"], {
18
+ cwd,
19
+ env: nestedNpmEnvironment(options.env || process.env),
20
+ encoding: "utf8",
21
+ maxBuffer: MAX_SBOM_BYTES,
22
+ timeout: SBOM_TIMEOUT_MS,
23
+ killSignal: "SIGKILL",
24
+ windowsHide: true,
25
+ });
26
+ if (result.error || result.status !== 0) throw new Error(releaseCommandFailure("npm", ["sbom"], result));
27
+ if (Buffer.byteLength(result.stdout) > MAX_SBOM_BYTES) throw new Error("npm sbom output exceeds the fixed byte budget");
28
+ let document;
29
+ try { document = JSON.parse(result.stdout); }
30
+ catch { throw new Error("npm sbom did not return valid JSON"); }
31
+ return validateCycloneDxSbom(document, {
32
+ packageName: options.packageName || packageJson.name,
33
+ packageVersion: options.packageVersion || packageJson.version,
34
+ forbiddenPaths: options.forbiddenPaths || [cwd, homedir()],
35
+ });
36
+ }
37
+
38
+ export function validateCycloneDxSbom(document, options = {}) {
39
+ if (!isRecord(document)) throw new Error("SBOM root must be an object");
40
+ if (document.bomFormat !== "CycloneDX" || document.specVersion !== "1.5") {
41
+ throw new Error("SBOM must be CycloneDX 1.5");
42
+ }
43
+ const component = isRecord(document.metadata) && isRecord(document.metadata.component)
44
+ ? document.metadata.component
45
+ : null;
46
+ const packageName = String(options.packageName || "");
47
+ const packageVersion = String(options.packageVersion || "");
48
+ if (!component || component.name !== packageName || component.version !== packageVersion) {
49
+ throw new Error("SBOM metadata component does not match the current package identity");
50
+ }
51
+ if (!Array.isArray(document.components) || document.components.length < 1 || document.components.length > 10_000) {
52
+ throw new Error("SBOM components must be a non-empty bounded array");
53
+ }
54
+ if (!Array.isArray(document.dependencies) || document.dependencies.length < 1 || document.dependencies.length > 20_000) {
55
+ throw new Error("SBOM dependencies must be a non-empty bounded array");
56
+ }
57
+ const rootReference = String(component["bom-ref"] || "");
58
+ if (!rootReference) throw new Error("SBOM root component reference is missing");
59
+ const references = new Set([rootReference]);
60
+ for (const item of document.components) {
61
+ if (!isRecord(item) || typeof item["bom-ref"] !== "string" || !item["bom-ref"]
62
+ || typeof item.name !== "string" || !item.name || typeof item.version !== "string" || !item.version) {
63
+ throw new Error("SBOM contains an invalid component record");
64
+ }
65
+ if (references.has(item["bom-ref"])) throw new Error("SBOM contains duplicate component references");
66
+ references.add(item["bom-ref"]);
67
+ }
68
+ const dependencyByReference = new Map();
69
+ for (const entry of document.dependencies) {
70
+ if (!isRecord(entry) || typeof entry.ref !== "string" || !references.has(entry.ref)
71
+ || dependencyByReference.has(entry.ref) || !Array.isArray(entry.dependsOn)
72
+ || entry.dependsOn.some((reference) => typeof reference !== "string" || !references.has(reference))) {
73
+ throw new Error("SBOM dependency graph contains an invalid reference");
74
+ }
75
+ dependencyByReference.set(entry.ref, entry.dependsOn);
76
+ }
77
+ if (dependencyByReference.size !== references.size
78
+ || [...references].some((reference) => !dependencyByReference.has(reference))) {
79
+ throw new Error("SBOM dependency graph omits a component reference");
80
+ }
81
+ const serialized = JSON.stringify(document);
82
+ for (const path of options.forbiddenPaths || []) {
83
+ const candidate = String(path || "");
84
+ if (candidate && candidate.length >= 2 && serialized.includes(candidate)) {
85
+ throw new Error("SBOM contains a local filesystem path");
86
+ }
87
+ }
88
+ return Object.freeze({
89
+ bom_format: document.bomFormat,
90
+ spec_version: document.specVersion,
91
+ package: `${packageName}@${packageVersion}`,
92
+ components: document.components.length,
93
+ dependencies: document.dependencies.length,
94
+ });
95
+ }
96
+
97
+ function isRecord(value) {
98
+ return value !== null && typeof value === "object" && !Array.isArray(value);
99
+ }
100
+
101
+ async function main() {
102
+ const summary = generateAndValidateSbom();
103
+ process.stdout.write(`CycloneDX SBOM validated (${summary.components} components, ${summary.dependencies} dependencies)\n`);
104
+ }
105
+
106
+ if (import.meta.url === pathToFileURL(process.argv[1] || "").href) await main();