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
@@ -13,14 +13,25 @@ import { runNetworkCommand } from "./network-retry.mjs";
13
13
  import { requireSuccessfulWorkflowRun } from "./release-ci.mjs";
14
14
  import { tagSyncError } from "./release-state.mjs";
15
15
  import { verifyCurrentReleaseAcceptance } from "./release-acceptance.mjs";
16
+ import { stageAcceptedCandidateTarball } from "./accepted-candidate-tarball.mjs";
17
+ import { createHardenedNpmSession } from "./hardened-npm-session.mjs";
18
+ import { nestedNpmEnvironment } from "../src/local/npm-environment.mjs";
19
+ import { resolveTrustedGitExecutable } from "../src/local/trusted-git-executable.mjs";
20
+ import { resolveTrustedGithubCli } from "../src/local/trusted-github-cli.mjs";
21
+ import { githubReleaseByTagEndpoint, waitForGithubReleaseAsset } from "./github-release-asset.mjs";
22
+ import { parseReleaseVersion, requiresSoakForStable } from "./release-channel.mjs";
23
+ import { verifyCurrentStableSoak } from "./release-soak.mjs";
24
+ import { assertGithubPublicationAuthorized, withGithubPublicationLock } from "./release-publication-guard.mjs";
25
+ import { releaseCommandFailure, releaseDiagnostic, releaseDiagnosticEvent } from "./release-diagnostic.mjs";
16
26
  import { fileURLToPath } from "node:url";
17
27
 
18
28
  const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
29
+ const git = resolveTrustedGitExecutable({ workspace: root });
30
+ const gh = resolveTrustedGithubCli({ workspace: root });
19
31
  process.chdir(root);
20
32
 
21
33
  function fail(message) {
22
- console.error(`release error: ${message}`);
23
- process.exit(1);
34
+ throw new Error(String(message || "release failed"));
24
35
  }
25
36
 
26
37
  function run(command, args, options = {}) {
@@ -28,17 +39,14 @@ function run(command, args, options = {}) {
28
39
  cwd: root,
29
40
  encoding: "utf8",
30
41
  stdio: options.capture ? "pipe" : "inherit",
31
- env: process.env,
42
+ env: options.env || process.env,
43
+ timeout: options.timeoutMs || 20 * 60 * 1000,
44
+ killSignal: "SIGKILL",
45
+ maxBuffer: 8 * 1024 * 1024,
32
46
  });
33
47
 
34
- if (result.error) {
35
- fail(`${command} could not be started: ${result.error.message}`);
36
- }
37
- if (result.status !== 0 && !options.allowFailure) {
38
- const detail = options.capture
39
- ? `${result.stdout ?? ""}${result.stderr ?? ""}`.trim()
40
- : "";
41
- fail(`${command} ${args.join(" ")} failed${detail ? `: ${detail}` : ""}`);
48
+ if ((result.error || result.status !== 0) && !options.allowFailure) {
49
+ fail(releaseCommandFailure(command, args, options.capture ? result : { ...result, stdout: "", stderr: "" }));
42
50
  }
43
51
  return result;
44
52
  }
@@ -50,9 +58,7 @@ function runNetwork(command, args, options = {}) {
50
58
  if (result.stdout) process.stdout.write(result.stdout);
51
59
  if (result.stderr) process.stderr.write(result.stderr);
52
60
  }
53
- if (result.error && !options.allowFailure) {
54
- fail(`${command} could not be started: ${result.error.message}`);
55
- }
61
+ if (result.error && !options.allowFailure) fail(releaseCommandFailure(command, args, result));
56
62
  if (result.status !== 0 && !options.allowFailure) failCommandResult(command, args, result);
57
63
  return result;
58
64
  }
@@ -63,8 +69,7 @@ function outputNetwork(command, args, options = {}) {
63
69
  }
64
70
 
65
71
  function failCommandResult(command, args, result) {
66
- const detail = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
67
- fail(`${command} ${args.join(" ")} failed${detail ? `: ${detail}` : ""}`);
72
+ fail(releaseCommandFailure(command, args, result));
68
73
  }
69
74
 
70
75
  function output(command, args, options = {}) {
@@ -72,6 +77,14 @@ function output(command, args, options = {}) {
72
77
  return (result.stdout ?? "").trim();
73
78
  }
74
79
 
80
+
81
+ function runNpmScript(npmCli, task) {
82
+ return run(process.execPath, [
83
+ npmCli, "run", "--workspaces=false", "--global=false", "--ignore-scripts=false",
84
+ "--if-present=false", "--prefix", root, task,
85
+ ], { env: nestedNpmEnvironment(process.env) });
86
+ }
87
+
75
88
  function packageMetadata() {
76
89
  const data = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
77
90
  if (typeof data.name !== "string" || typeof data.version !== "string") {
@@ -95,16 +108,16 @@ function changelogBody(version) {
95
108
  }
96
109
 
97
110
  function ensureClean() {
98
- const status = output("git", ["status", "--porcelain"]);
111
+ const status = output(git, ["status", "--porcelain"]);
99
112
  if (status) fail(`working tree is not clean:\n${status}`);
100
113
  }
101
114
 
102
115
  function fetchRemote() {
103
- runNetwork("git", ["fetch", "origin", "main", "--tags", "--prune"]);
116
+ runNetwork(git, ["fetch", "origin", "main", "--tags", "--prune"]);
104
117
  }
105
118
 
106
119
  function localTagCommit(tag) {
107
- const result = run("git", ["rev-list", "-n", "1", tag], {
120
+ const result = run(git, ["rev-list", "-n", "1", tag], {
108
121
  capture: true,
109
122
  allowFailure: true,
110
123
  });
@@ -112,7 +125,7 @@ function localTagCommit(tag) {
112
125
  }
113
126
 
114
127
  function remoteTagCommit(tag) {
115
- const text = outputNetwork("git", [
128
+ const text = outputNetwork(git, [
116
129
  "ls-remote",
117
130
  "--tags",
118
131
  "origin",
@@ -132,10 +145,11 @@ function assertSuccessfulCi(head) {
132
145
  [".github/workflows/codeql.yml", "CodeQL"],
133
146
  [".github/workflows/governance.yml", "Governance"],
134
147
  [".github/workflows/scorecard.yml", "OpenSSF Scorecard"],
148
+ [".github/workflows/workflow-policy.yml", "Workflow Policy Gate"],
135
149
  ];
136
150
  const verified = [];
137
151
  for (const [workflow, name] of required) {
138
- const text = outputNetwork("gh", [
152
+ const text = outputNetwork(gh, [
139
153
  "run",
140
154
  "list",
141
155
  "--workflow",
@@ -162,27 +176,38 @@ function assertSuccessfulCi(head) {
162
176
  }
163
177
 
164
178
  function releaseInfo(tag) {
165
- const result = runNetwork(
166
- "gh",
167
- [
168
- "release",
169
- "view",
170
- tag,
171
- "--json",
172
- "tagName,name,targetCommitish,isDraft,isPrerelease,assets,url",
173
- ],
174
- { capture: true, allowFailure: true },
175
- );
176
- if (result.status !== 0) return null;
177
- return JSON.parse(result.stdout);
179
+ const args = ["api", githubReleaseByTagEndpoint(tag)];
180
+ const result = runNetwork(gh, args, { capture: true, allowFailure: true });
181
+ if (result.status !== 0 || result.error) {
182
+ const detail = `${result.stdout ?? ""}${result.stderr ?? ""}${result.error?.message ?? ""}`.trim();
183
+ if (/\bHTTP 404\b|\bstatus 404\b|\b404 Not Found\b/i.test(detail)) return null;
184
+ throw commandResultError(gh, args, result);
185
+ }
186
+ let value;
187
+ try { value = JSON.parse(result.stdout); }
188
+ catch { throw new Error(`GitHub REST release query returned invalid JSON for ${tag}`); }
189
+ if (value?.tag_name !== tag || typeof value.draft !== "boolean" || typeof value.prerelease !== "boolean") {
190
+ throw new Error(`GitHub REST release metadata is invalid for ${tag}`);
191
+ }
192
+ return {
193
+ tagName: value.tag_name,
194
+ name: String(value.name || ""),
195
+ targetCommitish: String(value.target_commitish || ""),
196
+ isDraft: value.draft,
197
+ isPrerelease: value.prerelease,
198
+ assets: Array.isArray(value.assets) ? value.assets : [],
199
+ url: String(value.html_url || ""),
200
+ };
178
201
  }
179
202
 
180
- function assertCoreSync({ requireReleaseAsset }) {
203
+ async function assertCoreSync({ requireReleaseAsset }) {
181
204
  const pkg = packageMetadata();
182
- assertLocalAcceptance();
205
+ const parsedVersion = parseReleaseVersion(pkg.version);
206
+ const acceptance = assertLocalAcceptance();
207
+ if (requiresSoakForStable(pkg.version)) assertStableSoak();
183
208
  const tag = `v${pkg.version}`;
184
- const head = output("git", ["rev-parse", "HEAD"]);
185
- const originMain = output("git", ["rev-parse", "origin/main"]);
209
+ const head = output(git, ["rev-parse", "HEAD"]);
210
+ const originMain = output(git, ["rev-parse", "origin/main"]);
186
211
  if (head !== originMain) {
187
212
  fail(`HEAD ${head} does not match origin/main ${originMain}`);
188
213
  }
@@ -196,17 +221,11 @@ function assertCoreSync({ requireReleaseAsset }) {
196
221
  const remoteTagError = tagSyncError({ scope: "remote", tag, head, commit: remoteCommit });
197
222
  if (remoteTagError) fail(remoteTagError);
198
223
 
199
- const release = releaseInfo(tag);
200
- if (!release || release.isDraft || release.isPrerelease) {
201
- fail(`published GitHub Release ${tag} is missing or not final`);
202
- }
224
+ await waitForPublishedReleaseState(tag, parsedVersion.prerelease);
203
225
 
204
226
  if (requireReleaseAsset) {
205
227
  const expectedAsset = `${pkg.name}-${pkg.version}.tgz`;
206
- const names = new Set((release.assets ?? []).map((asset) => asset.name));
207
- if (!names.has(expectedAsset)) {
208
- fail(`GitHub Release ${tag} is missing ${expectedAsset}`);
209
- }
228
+ await releaseAssetInfo(tag, expectedAsset, acceptance.artifactSha256);
210
229
  }
211
230
 
212
231
  console.log(`GitHub source, tag, release, and package asset are in sync: ${tag}`);
@@ -220,41 +239,20 @@ function writeNotesFile(directory, version) {
220
239
  return path;
221
240
  }
222
241
 
223
- function packReleaseAsset(directory, pkg) {
224
- const result = run(
225
- "npm",
226
- ["pack", "--silent", "--json", "--pack-destination", directory],
227
- { capture: true },
228
- );
229
- let records;
230
- try {
231
- records = JSON.parse(result.stdout);
232
- } catch {
233
- fail("npm pack did not return valid JSON");
234
- }
235
- const record = normalizePackRecord(records, pkg.name);
236
- const filename = record?.filename;
237
- if (typeof filename !== "string") fail("npm pack did not report a filename");
238
- const path = join(directory, filename);
239
- const expected = `${pkg.name.replaceAll("/", "-").replace(/^@/, "")}-${pkg.version}.tgz`;
240
- if (filename !== expected) {
241
- fail(`unexpected npm package filename ${filename}; expected ${expected}`);
242
- }
243
- return path;
244
- }
245
-
246
- function normalizePackRecord(value, packageName) {
247
- if (Array.isArray(value)) return value[0] ?? null;
248
- if (!value || typeof value !== "object") return null;
249
- if (value[packageName] && typeof value[packageName] === "object") return value[packageName];
250
- return Object.values(value).find((item) => item && typeof item === "object") ?? null;
242
+ async function releaseAssetInfo(tag, assetName, expectedSha256) {
243
+ return waitForGithubReleaseAsset(() => {
244
+ const text = outputNetwork(gh, ["api", githubReleaseByTagEndpoint(tag)]);
245
+ try { return JSON.parse(text); }
246
+ catch { throw new Error(`GitHub REST release query returned invalid JSON for ${tag}`); }
247
+ }, { tag, assetName, expectedSha256 });
251
248
  }
252
249
 
253
- function ensureRelease(tag, version, assetPath, latest) {
250
+ function ensureRelease(tag, version, assetPath, { latest, prerelease }) {
254
251
  const temp = dirname(assetPath);
255
252
  const notes = writeNotesFile(temp, version);
256
253
  const existing = releaseInfo(tag);
257
254
  const title = `machine-bridge-mcp ${tag}`;
255
+ const failures = [];
258
256
 
259
257
  if (!existing) {
260
258
  const args = [
@@ -266,97 +264,186 @@ function ensureRelease(tag, version, assetPath, latest) {
266
264
  "--title",
267
265
  title,
268
266
  latest ? "--latest" : "--latest=false",
267
+ prerelease ? "--prerelease" : "--prerelease=false",
269
268
  ];
270
269
  if (notes) args.push("--notes-file", notes);
271
270
  else args.push("--generate-notes");
272
- const created = runNetwork("gh", args, { allowFailure: true });
273
- if (created.status !== 0 && !releaseInfo(tag)) failCommandResult("gh", args, created);
271
+ const created = runNetwork(gh, args, { allowFailure: true });
272
+ if (created.status !== 0) failures.push(commandResultError(gh, args, created));
274
273
  } else {
275
- runNetwork("gh", [
276
- "release",
277
- "edit",
278
- tag,
279
- "--title",
280
- title,
274
+ const editArgs = [
275
+ "release", "edit", tag, "--title", title,
281
276
  latest ? "--latest" : "--latest=false",
277
+ prerelease ? "--prerelease" : "--prerelease=false",
282
278
  ...(notes ? ["--notes-file", notes] : []),
283
- ]);
284
- runNetwork("gh", ["release", "upload", tag, assetPath, "--clobber"]);
279
+ ];
280
+ const edited = runNetwork(gh, editArgs, { allowFailure: true });
281
+ if (edited.status !== 0) failures.push(commandResultError(gh, editArgs, edited));
282
+ const uploadArgs = ["release", "upload", tag, assetPath, "--clobber"];
283
+ const uploaded = runNetwork(gh, uploadArgs, { allowFailure: true });
284
+ if (uploaded.status !== 0) failures.push(commandResultError(gh, uploadArgs, uploaded));
285
285
  }
286
+ if (!failures.length) return null;
287
+ return failures.length === 1
288
+ ? failures[0]
289
+ : new AggregateError(failures, "GitHub release mutation commands returned multiple errors");
286
290
  }
287
291
 
288
- function publishCurrent() {
292
+ function commandResultError(command, args, result) {
293
+ return new Error(releaseCommandFailure(command, args, result, { maxChars: 1200 }));
294
+ }
295
+
296
+ async function waitForPublishedReleaseState(tag, prerelease, options = {}) {
297
+ const attempts = Number.isSafeInteger(Number(options.attempts))
298
+ ? Math.min(Math.max(Number(options.attempts), 1), 10)
299
+ : 5;
300
+ const wait = typeof options.wait === "function" ? options.wait : defaultReleaseStateWait;
301
+ let release = null;
302
+ for (let attempt = 1; attempt <= attempts; attempt += 1) {
303
+ release = releaseInfo(tag);
304
+ if (release && !release.isDraft && release.isPrerelease === prerelease) return release;
305
+ if (attempt < attempts) await wait(attempt);
306
+ }
307
+ throw new Error(`GitHub release ${tag} metadata did not converge after publication`);
308
+ }
309
+
310
+ function defaultReleaseStateWait(attempt) {
311
+ return new Promise((resolvePromise) => { setTimeout(resolvePromise, Math.min(4_000, attempt * 1_000)); });
312
+ }
313
+
314
+ async function publishCurrent({ prereleaseMode = false } = {}) {
289
315
  ensureClean();
290
316
  fetchRemote();
291
317
 
292
- const branch = output("git", ["branch", "--show-current"]);
318
+ const branch = output(git, ["branch", "--show-current"]);
293
319
  if (branch !== "main") fail(`release must run from main, not ${branch || "detached HEAD"}`);
294
320
 
295
321
  const pkg = packageMetadata();
322
+ const parsedVersion = parseReleaseVersion(pkg.version);
323
+ if (prereleaseMode !== parsedVersion.prerelease) {
324
+ fail(parsedVersion.prerelease
325
+ ? "prerelease versions must use npm run prerelease:release -- --owner-confirm"
326
+ : "stable versions must use npm run release -- --owner-confirm");
327
+ }
296
328
  const tag = `v${pkg.version}`;
297
329
  if (!changelogBody(pkg.version)) {
298
330
  fail(`CHANGELOG.md has no section for ${pkg.version}`);
299
331
  }
300
332
 
301
- run("npm", ["run", "check"]);
302
- run("npm", ["run", "version:check"]);
303
- ensureClean();
304
- assertLocalAcceptance();
333
+ const npmSession = await createHardenedNpmSession();
334
+ let acceptance;
335
+ let verificationError = null;
336
+ try {
337
+ runNpmScript(npmSession.cli, "check");
338
+ runNpmScript(npmSession.cli, "version:check");
339
+ ensureClean();
340
+ acceptance = assertLocalAcceptance(npmSession.cli);
341
+ if (!parsedVersion.prerelease && requiresSoakForStable(pkg.version)) assertStableSoak(npmSession.cli);
342
+ } catch (error) {
343
+ verificationError = error;
344
+ }
345
+ let npmCleanupError = null;
346
+ try { npmSession.dispose(); } catch (error) { npmCleanupError = error; }
347
+ if (verificationError && npmCleanupError) {
348
+ throw new AggregateError([verificationError, npmCleanupError], "GitHub release verification failed and hardened npm cleanup was incomplete");
349
+ }
350
+ if (verificationError) throw verificationError;
351
+ if (npmCleanupError) throw npmCleanupError;
305
352
 
306
- const head = output("git", ["rev-parse", "HEAD"]);
307
- const originMain = output("git", ["rev-parse", "origin/main"]);
353
+ const head = output(git, ["rev-parse", "HEAD"]);
354
+ const originMain = output(git, ["rev-parse", "origin/main"]);
308
355
  if (head !== originMain) {
309
356
  fail("HEAD does not match origin/main; local acceptance must be committed, pushed through npm run github:push, reviewed, and merged before release publication");
310
357
  }
311
358
  assertSuccessfulCi(head);
312
359
 
313
- const existingLocal = localTagCommit(tag);
314
- if (existingLocal && existingLocal !== head) {
315
- fail(`local ${tag} points to ${existingLocal}, not ${head}`);
316
- }
317
- if (!existingLocal) {
318
- run("git", ["tag", "-a", tag, "-m", `Release ${pkg.version}`]);
319
- }
360
+ const candidate = stageAcceptedCandidateTarball(root, acceptance);
361
+ let primaryError = null;
362
+ let releaseVerified = false;
363
+ try {
364
+ const existingLocal = localTagCommit(tag);
365
+ if (existingLocal && existingLocal !== head) {
366
+ fail(`local ${tag} points to ${existingLocal}, not ${head}`);
367
+ }
368
+ if (!existingLocal) {
369
+ run(git, ["tag", "-a", tag, "-m", `Release ${pkg.version}`]);
370
+ }
371
+
372
+ const existingRemote = remoteTagCommit(tag);
373
+ if (existingRemote && existingRemote !== head) {
374
+ fail(`remote ${tag} points to ${existingRemote}, not ${head}`);
375
+ }
376
+ if (!existingRemote) {
377
+ runNetwork(git, ["push", "origin", tag]);
378
+ }
320
379
 
321
- const existingRemote = remoteTagCommit(tag);
322
- if (existingRemote && existingRemote !== head) {
323
- fail(`remote ${tag} points to ${existingRemote}, not ${head}`);
380
+ const mutationError = ensureRelease(tag, pkg.version, candidate.path, {
381
+ latest: !parsedVersion.prerelease,
382
+ prerelease: parsedVersion.prerelease,
383
+ });
384
+ let assetError = null;
385
+ try {
386
+ await releaseAssetInfo(tag, acceptance.metadata.filename, acceptance.artifactSha256);
387
+ await waitForPublishedReleaseState(tag, parsedVersion.prerelease);
388
+ } catch (error) {
389
+ assetError = error;
390
+ }
391
+ if (mutationError && assetError) {
392
+ throw new AggregateError([mutationError, assetError], "GitHub release mutation and remote-state reconciliation both failed");
393
+ }
394
+ if (assetError) throw assetError;
395
+ if (mutationError) {
396
+ console.warn("GitHub release mutation returned an error, but the exact release metadata and accepted asset bytes were verified remotely");
397
+ }
398
+ releaseVerified = true;
399
+ } catch (error) {
400
+ primaryError = error;
324
401
  }
325
- if (!existingRemote) {
326
- runNetwork("git", ["push", "origin", tag]);
402
+ let cleanupError = null;
403
+ try { candidate.dispose(); } catch (error) { cleanupError = error; }
404
+ if (primaryError) {
405
+ if (cleanupError) throw new AggregateError([primaryError, cleanupError], "GitHub release publication failed and candidate staging cleanup was incomplete");
406
+ throw primaryError;
327
407
  }
328
-
329
- const temp = mkdtempSync(join(tmpdir(), "machine-bridge-mcp-release-"));
330
- try {
331
- const assetPath = packReleaseAsset(temp, pkg);
332
- ensureRelease(tag, pkg.version, assetPath, true);
333
- } finally {
334
- rmSync(temp, { recursive: true, force: true });
408
+ if (cleanupError) {
409
+ if (!releaseVerified) throw cleanupError;
410
+ console.warn(`GitHub release bytes were verified but candidate staging cleanup was incomplete: ${releaseDiagnostic(cleanupError?.message || cleanupError, 600)}`);
335
411
  }
336
412
 
337
413
  fetchRemote();
338
- assertCoreSync({ requireReleaseAsset: true });
414
+ await assertCoreSync({ requireReleaseAsset: true });
339
415
  }
340
416
 
341
- function assertLocalAcceptance() {
417
+ function assertStableSoak(npmCli = process.env.npm_execpath) {
342
418
  try {
343
- const result = verifyCurrentReleaseAcceptance(root);
419
+ const result = verifyCurrentStableSoak(root, { npmCli });
344
420
  if (result.required) {
345
- console.log(`Interactive local candidate acceptance matches ${result.metadata.filename} (${result.metadata.shasum}).`);
421
+ console.log(`Stable promotion matches soaked ${result.record.prerelease_version} (${result.promotionDigest}).`);
346
422
  }
347
423
  } catch (error) {
348
424
  fail(String(error?.message || error));
349
425
  }
350
426
  }
351
427
 
428
+ function assertLocalAcceptance(npmCli = process.env.npm_execpath) {
429
+ try {
430
+ const result = verifyCurrentReleaseAcceptance(root, { npmCli });
431
+ if (!result.required) fail("GitHub release publication requires current local candidate acceptance");
432
+ console.log(`Local candidate acceptance matches ${result.metadata.filename} (${result.metadata.shasum}).`);
433
+ return result;
434
+ } catch (error) {
435
+ fail(String(error?.message || error));
436
+ }
437
+ }
438
+
352
439
  function backfillMissingReleases() {
353
440
  ensureClean();
354
441
  fetchRemote();
355
442
 
356
- const tags = output("git", ["tag", "--sort=version:refname"])
443
+ const tags = output(git, ["tag", "--sort=version:refname"])
357
444
  .split("\n")
358
445
  .filter((tag) => /^v\d+\.\d+\.\d+$/.test(tag));
359
- const releases = JSON.parse(outputNetwork("gh", [
446
+ const releases = JSON.parse(outputNetwork(gh, [
360
447
  "release",
361
448
  "list",
362
449
  "--limit",
@@ -373,6 +460,8 @@ function backfillMissingReleases() {
373
460
  }
374
461
 
375
462
  const temp = mkdtempSync(join(tmpdir(), "machine-bridge-mcp-backfill-"));
463
+ let primaryError = null;
464
+ let completed = 0;
376
465
  try {
377
466
  for (const tag of missing) {
378
467
  if (!remoteTagCommit(tag)) fail(`remote tag ${tag} is missing`);
@@ -389,25 +478,45 @@ function backfillMissingReleases() {
389
478
  ];
390
479
  if (notes) args.push("--notes-file", notes);
391
480
  else args.push("--generate-notes");
392
- const created = runNetwork("gh", args, { allowFailure: true });
393
- if (created.status !== 0 && !releaseInfo(tag)) failCommandResult("gh", args, created);
481
+ const created = runNetwork(gh, args, { allowFailure: true });
482
+ if (created.status !== 0 && !releaseInfo(tag)) failCommandResult(gh, args, created);
483
+ completed += 1;
394
484
  }
395
- } finally {
396
- rmSync(temp, { recursive: true, force: true });
485
+ } catch (error) {
486
+ primaryError = error;
487
+ }
488
+ let cleanupError = null;
489
+ try { rmSync(temp, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 }); }
490
+ catch (error) { cleanupError = error; }
491
+ if (primaryError) {
492
+ if (cleanupError) throw new AggregateError([primaryError, cleanupError], "GitHub Release backfill failed and temporary cleanup was incomplete");
493
+ throw primaryError;
494
+ }
495
+ if (cleanupError) {
496
+ if (!completed) throw cleanupError;
497
+ console.warn(`GitHub Release backfill completed but temporary cleanup was incomplete: ${releaseDiagnostic(cleanupError?.message || cleanupError, 600)}`);
397
498
  }
398
499
 
399
500
  console.log(`Backfilled ${missing.length} GitHub Release(s): ${missing.join(", ")}`);
400
501
  }
401
502
 
402
503
  const mode = process.argv[2] ?? "--check";
403
- if (mode === "--check") {
404
- ensureClean();
405
- fetchRemote();
406
- assertCoreSync({ requireReleaseAsset: true });
407
- } else if (mode === "--publish") {
408
- publishCurrent();
409
- } else if (mode === "--backfill") {
410
- backfillMissingReleases();
411
- } else {
412
- fail("usage: node scripts/github-release.mjs [--check|--publish|--backfill]");
504
+ try {
505
+ if (mode === "--check") {
506
+ ensureClean();
507
+ fetchRemote();
508
+ await assertCoreSync({ requireReleaseAsset: true });
509
+ } else if (mode === "--publish" || mode === "--publish-prerelease" || mode === "--backfill") {
510
+ assertGithubPublicationAuthorized();
511
+ await withGithubPublicationLock(root, async () => {
512
+ if (mode === "--publish") await publishCurrent({ prereleaseMode: false });
513
+ else if (mode === "--publish-prerelease") await publishCurrent({ prereleaseMode: true });
514
+ else backfillMissingReleases();
515
+ });
516
+ } else {
517
+ fail("usage: node scripts/github-release.mjs [--check|--publish|--publish-prerelease|--backfill] [--owner-confirm]");
518
+ }
519
+ } catch (error) {
520
+ console.error(JSON.stringify(releaseDiagnosticEvent("github.release.failed", error?.message || error, 1600)));
521
+ process.exitCode = 1;
413
522
  }
@@ -0,0 +1,68 @@
1
+ import { lstatSync, realpathSync } from "node:fs";
2
+ import { isAbsolute, join, relative, resolve, sep } from "node:path";
3
+ import { readBoundedRegularFileSync } from "../src/local/secure-file.mjs";
4
+
5
+ const MAX_PACKAGE_MANIFEST_BYTES = 2 * 1024 * 1024;
6
+
7
+ export function inspectGlobalPackageInstallation(globalRoot, packageName) {
8
+ const rootInput = String(globalRoot || "").trim();
9
+ const name = String(packageName || "");
10
+ if (!rootInput || !isAbsolute(rootInput) || !name || name.includes("/") || name.includes("\\") || name.includes("\0")) {
11
+ throw new TypeError("global package inspection requires a valid package name and absolute root");
12
+ }
13
+ let root;
14
+ try { root = realpathSync(resolve(rootInput)); }
15
+ catch (error) {
16
+ if (error?.code === "ENOENT") return null;
17
+ throw error;
18
+ }
19
+ const packageRoot = join(root, name);
20
+ let packageInfo;
21
+ try { packageInfo = lstatSync(packageRoot); }
22
+ catch (error) {
23
+ if (error?.code === "ENOENT") return null;
24
+ throw error;
25
+ }
26
+ if (packageInfo.isSymbolicLink() || !packageInfo.isDirectory()) {
27
+ throw new Error("globally installed package root must be a real directory");
28
+ }
29
+ const canonicalPackageRoot = realpathSync(packageRoot);
30
+ const packageRelative = relative(root, canonicalPackageRoot);
31
+ if (!packageRelative || packageRelative.startsWith(`..${sep}`) || packageRelative === ".." || isAbsolute(packageRelative)) {
32
+ throw new Error("globally installed package root escapes the npm global root");
33
+ }
34
+ const packagePath = join(canonicalPackageRoot, "package.json");
35
+ let bytes;
36
+ try {
37
+ bytes = readBoundedRegularFileSync(packagePath, MAX_PACKAGE_MANIFEST_BYTES, "globally installed package manifest", {
38
+ verifyPathIdentity: true,
39
+ rejectMultipleLinks: true,
40
+ });
41
+ } catch (error) {
42
+ if (error?.code === "ENOENT") throw new Error("globally installed package manifest is missing", { cause: error });
43
+ throw error;
44
+ }
45
+ let value;
46
+ try { value = JSON.parse(bytes.toString("utf8")); }
47
+ catch { throw new Error("globally installed package manifest is not valid JSON"); }
48
+ const version = String(value?.version || "");
49
+ if (value?.name !== name || !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(version)) {
50
+ throw new Error("globally installed package identity is invalid");
51
+ }
52
+ const entry = join(canonicalPackageRoot, "bin", "machine-mcp.mjs");
53
+ let info;
54
+ try { info = lstatSync(entry); }
55
+ catch (error) {
56
+ if (error?.code === "ENOENT") throw new Error("globally installed package CLI is missing", { cause: error });
57
+ throw error;
58
+ }
59
+ if (info.isSymbolicLink() || !info.isFile() || Number(info.nlink) !== 1 || (process.platform !== "win32" && (Number(info.mode) & 0o022) !== 0)) {
60
+ throw new Error("globally installed package CLI must be a private regular file");
61
+ }
62
+ const canonicalEntry = realpathSync(entry);
63
+ const entryRelative = relative(canonicalPackageRoot, canonicalEntry);
64
+ if (!entryRelative || entryRelative.startsWith(`..${sep}`) || entryRelative === ".." || isAbsolute(entryRelative)) {
65
+ throw new Error("globally installed package CLI escapes the package root");
66
+ }
67
+ return Object.freeze({ version, entry: canonicalEntry });
68
+ }
@@ -0,0 +1,42 @@
1
+ import { mkdtempSync, rmSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join, resolve } from "node:path";
4
+ import { prepareHardenedNpm } from "../src/local/hardened-npm.mjs";
5
+
6
+ export async function createHardenedNpmSession(options = {}) {
7
+ const parent = resolve(String(options.tempRoot || tmpdir()));
8
+ const root = mkdtempSync(join(parent, "mbm-hardened-npm-session-"));
9
+ let disposed = false;
10
+ try {
11
+ const prepared = await prepareHardenedNpm(join(root, "runtime"), options.hardenedNpm || {});
12
+ return Object.freeze({
13
+ cli: prepared.cli,
14
+ version: prepared.version,
15
+ undiciVersion: prepared.undiciVersion,
16
+ braceExpansionVersion: prepared.braceExpansionVersion,
17
+ dispose() {
18
+ if (disposed) return;
19
+ rmSync(root, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
20
+ disposed = true;
21
+ },
22
+ });
23
+ } catch (error) {
24
+ try { rmSync(root, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 }); }
25
+ catch (cleanupError) { throw new AggregateError([error, cleanupError], "hardened npm session preparation failed and temporary cleanup was incomplete"); }
26
+ throw error;
27
+ }
28
+ }
29
+
30
+ export function settleHardenedNpmSession(
31
+ session,
32
+ primaryError = null,
33
+ aggregateMessage = "hardened npm session failed and temporary cleanup was incomplete",
34
+ ) {
35
+ try { session?.dispose(); }
36
+ catch (cleanupError) {
37
+ return primaryError
38
+ ? new AggregateError([primaryError, cleanupError], aggregateMessage)
39
+ : cleanupError;
40
+ }
41
+ return primaryError;
42
+ }