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
package/src/local/cli.mjs CHANGED
@@ -1,31 +1,38 @@
1
- import { readFileSync } from "node:fs";
2
1
  import { join, resolve } from "node:path";
3
2
  import process from "node:process";
4
3
  import readline from "node:readline/promises";
5
4
  import { LocalRuntime } from "./runtime.mjs";
6
- import { acquireDaemonLockWithTakeover, stopWorkspaceServiceDaemon } from "./daemon-process.mjs";
5
+ import { acquireDaemonLockWithTakeover, inspectWorkspaceDaemon, stopWorkspaceServiceDaemon, workspaceDaemonOwnsPlatformAutostart } from "./daemon-process.mjs";
7
6
  import { inspectProcessInstance } from "./process-identity.mjs";
8
7
  import { runStdioServer } from "./stdio.mjs";
9
8
  import { assertCanonicalFullPolicy, POLICY_PROFILES, toolsForPolicy } from "./tools.mjs";
10
9
  import { resolvePolicy } from "./cli-policy.mjs";
11
10
  import { effectiveLogFormat, effectiveLogLevel, normalizeCommand, parseArgs, validateCommandOptions, validateLoggingOptions, validatePositionals } from "./cli-options.mjs";
12
11
  import { createLocalAdminCommands } from "./cli-local-admin.mjs";
13
- import { createApprovalCommand } from "./cli-approval.mjs";
14
12
  import { createServiceCommand } from "./cli-service.mjs";
13
+ import { createActivateCommand } from "./cli-activate.mjs";
15
14
  import { generateAccountPassword } from "./account-admin.mjs";
16
15
  import { accountAdminClient, createAccountCommand } from "./cli-account-admin.mjs";
17
16
  export { resolvePolicy } from "./cli-policy.mjs";
18
17
  export { parseArgs, validateCommandOptions, validateLoggingOptions, validatePositionals } from "./cli-options.mjs";
19
18
  import { classifyOperationalError, createLogger, sanitizeLogText } from "./log.mjs";
19
+ import { formatPolicySummary, printMcpConnection, printStartJson } from "./cli-ready-output.mjs";
20
20
  import { runExecutable, runWrangler } from "./shell.mjs";
21
21
  import { runFullAccessTest } from "./full-access-test.mjs";
22
22
  import { stopAndRemoveAutostart } from "./service-lifecycle.mjs";
23
+ import { stopOwnedPlatformService } from "./service-ownership.mjs";
23
24
  import { loadServiceEnvironment } from "./service-environment.mjs";
24
- import { ensureWorkerDeployment } from "./worker-deployment.mjs";
25
+ import { createDeviceSessionForRoot, deviceRootProviderStatus, ensurePreferredDeviceRoot } from "./device-root-provider.mjs";
26
+ import { convergeRemoteConfiguration } from "./remote-configuration.mjs";
25
27
  import { workerHealth } from "./worker-health.mjs";
28
+ import { DOCTOR_RUNTIME_SCOPE, doctorRuntimeCheckProjection } from "./doctor-reporting.mjs";
29
+ import { supportStateProjection } from "./support-state-projection.mjs";
26
30
  export { workerHealthUserReason } from "./worker-health.mjs";
27
31
  import { activeStateJobs, activeStateLocks, knownProfileStates, knownWorkerNames } from "./state-inventory.mjs";
32
+ import { pruneRetiredManagedJobDirectories } from "./managed-job-directory-generation.mjs";
33
+ import { withReleaseRuntimeLock } from "./release-runtime-lock.mjs";
28
34
  import {
35
+ acquireMachineServiceLockWithWait,
29
36
  acquireMaintenanceLock,
30
37
  acquireStartupLockWithWait,
31
38
  daemonLockPathForState,
@@ -36,7 +43,6 @@ import {
36
43
  expandHome,
37
44
  loadGlobalConfig,
38
45
  loadState,
39
- packageRoot,
40
46
  readDaemonLockOwner,
41
47
  redactState,
42
48
  removeStateRoot,
@@ -47,14 +53,23 @@ import {
47
53
  selectedWorkspace,
48
54
  setSelectedWorkspace,
49
55
  } from "./state.mjs";
56
+ import { packageName, packageVersion } from "./package-identity.mjs";
50
57
 
51
58
  const localAdminCommands = createLocalAdminCommands({ chooseWorkspace, confirm });
52
59
  const accountCommand = createAccountCommand({ chooseWorkspace, confirm });
53
- const approvalCommand = createApprovalCommand({ chooseWorkspace, confirm });
54
- const serviceCommand = createServiceCommand({ chooseWorkspace, stateRootFromArgs, structuredLogger });
60
+ const serviceCommand = createServiceCommand({ chooseWorkspace, stateRootFromArgs, structuredLogger, acquireMachineServiceLockWithWait, currentPackageVersion });
61
+ const activateCommand = createActivateCommand({
62
+ chooseWorkspace,
63
+ prepareRemoteState,
64
+ createRemoteRuntime,
65
+ currentPackageVersion,
66
+ assertNodeVersion,
67
+ structuredLogger,
68
+ });
55
69
 
56
70
  const COMMAND_HANDLERS = new Map([
57
71
  ["start", startCommand],
72
+ ["activate", activateCommand],
58
73
  ["stdio", stdioCommand],
59
74
  ["client-config", clientConfigCommand],
60
75
  ["status", statusCommand],
@@ -66,7 +81,6 @@ const COMMAND_HANDLERS = new Map([
66
81
  ["rotate-secrets", rotateSecretsCommand],
67
82
  ["resource", localAdminCommands.resourceCommand],
68
83
  ["account", accountCommand],
69
- ["approval", approvalCommand],
70
84
  ["browser", localAdminCommands.browserCommand],
71
85
  ["job", localAdminCommands.jobCommand],
72
86
  ["uninstall", uninstallCommand],
@@ -173,14 +187,18 @@ async function startCommand(args) {
173
187
  assertNodeVersion();
174
188
  const logger = createLogger({ level: args.json ? "error" : effectiveLogLevel(args), format: effectiveLogFormat(args), component: "cli" });
175
189
  const workspace = await chooseWorkspace(args, { promptOnFirstRun: true, save: true, allowPositional: true });
176
- const state = loadState(workspace, { stateDir: args.stateDir });
177
- if (args.daemonOnly) {
178
- const serviceEnvironment = loadServiceEnvironment(state.paths.stateRoot);
179
- logger.debug?.("Loaded persisted service network environment", { keys: serviceEnvironment.keys });
180
- }
181
- const startupLock = await acquireStartupLockWithWait(state, { operation: "start", logger });
190
+ let state = loadState(workspace, { stateDir: args.stateDir });
191
+ let startupLock = null;
192
+ let serviceLock = null;
182
193
 
183
194
  try {
195
+ serviceLock = await acquireRuntimeStartServiceLock(args, acquireMachineServiceLockWithWait, logger);
196
+ startupLock = await acquireStartupLockWithWait(state, { operation: "start", logger });
197
+ state = loadState(workspace, { stateDir: args.stateDir });
198
+ if (args.daemonOnly) {
199
+ const serviceEnvironment = loadServiceEnvironment(state.paths.stateRoot);
200
+ logger.debug?.("Loaded persisted service network environment", { keys: serviceEnvironment.keys });
201
+ }
184
202
  const startMode = await prepareStartMode(args, state, logger);
185
203
  const daemonLock = await acquireDaemonLockWithTakeover(state, {
186
204
  takeOverServiceOwner: startMode.takeOverServiceOwner,
@@ -194,9 +212,12 @@ async function startCommand(args) {
194
212
  reportExistingDaemon(args, state, daemonLock.owner, logger);
195
213
  return;
196
214
  }
215
+ serviceLock?.release?.();
216
+ serviceLock = null;
197
217
  await startRemoteRuntime({ args, workspace, state, daemonLock, logger });
198
218
  } finally {
199
- startupLock.release();
219
+ serviceLock?.release?.();
220
+ startupLock?.release?.();
200
221
  }
201
222
  }
202
223
 
@@ -206,22 +227,38 @@ async function prepareStartMode(args, state, logger) {
206
227
  trimAutostartLogs(state.paths.stateRoot);
207
228
  return { takeOverServiceOwner: false };
208
229
  }
209
- // A normal foreground start first asks the platform service manager to
210
- // unload the job, then independently reclaims a verified daemon-only
211
- // process. The second step handles orphaned service daemons that launchd or
212
- // another service manager no longer tracks.
213
- return stopAutostartBestEffort(logger);
230
+ // The platform service name is machine-global, while state/workspace locks
231
+ // are scoped. Stop the platform service only when the daemon lock proves
232
+ // that the loaded service belongs to this exact state/workspace. An
233
+ // unrelated foreground start (including install smoke tests with isolated
234
+ // HOME/state) must never unload another Machine Bridge deployment.
235
+ const ownership = await stopOwnedPlatformService({
236
+ state,
237
+ inspectWorkspaceDaemon,
238
+ ownsPlatformAutostart: workspaceDaemonOwnsPlatformAutostart,
239
+ stopPlatformService: () => stopAutostartBestEffort(logger),
240
+ });
241
+ if (!ownership.owned) {
242
+ logger.debug?.("foreground startup left unrelated machine autostart untouched", {
243
+ workspace_daemon_present: ownership.daemon.present === true,
244
+ workspace_daemon_alive: ownership.daemon.alive === true,
245
+ identity_reason: ownership.daemon.identity_reason || "not_running",
246
+ });
247
+ return { takeOverServiceOwner: true, provider: null };
248
+ }
249
+ // Once ownership is proven, unload the provider first so KeepAlive cannot
250
+ // respawn the service while the verified process lock is reclaimed.
251
+ return ownership.provider;
214
252
  }
215
253
 
216
254
  function reportExistingDaemon(args, state, owner, logger) {
217
- const pid = owner?.pid ? `pid ${owner.pid}` : "unknown pid";
218
255
  if (isIdempotentDaemonOnlyStart(args)) {
219
256
  logger.debug?.("local daemon already running; daemon-only start completed as an idempotent no-op", { owner_pid_known: Boolean(owner?.pid) });
220
257
  return;
221
258
  }
222
259
  const mode = owner?.mode === "foreground" ? "foreground" : owner?.mode === "service" ? "background service" : "local";
223
260
  const version = owner?.version ? `, version ${owner.version}` : "";
224
- const notice = `${mode} daemon already running for this workspace (${pid}${version}); it was not restarted and requested changes were not applied`;
261
+ const notice = `${mode} daemon already running for this workspace${version}; it was not restarted and requested changes were not applied`;
225
262
  logger.warn(notice);
226
263
  if (args.json) {
227
264
  printStartJson(state, { requestedChangesApplied: false, notice });
@@ -235,6 +272,20 @@ function reportExistingDaemon(args, state, owner, logger) {
235
272
  logger.plain(` Workspace: ${state.workspace.path}`);
236
273
  }
237
274
 
275
+ export function runtimeStartRequiresMachineServiceLock(args = {}) {
276
+ return args.daemonOnly !== true;
277
+ }
278
+
279
+ export async function acquireRuntimeStartServiceLock(args = {}, acquireLock = acquireMachineServiceLockWithWait, logger = console) {
280
+ if (!runtimeStartRequiresMachineServiceLock(args)) return null;
281
+ if (typeof acquireLock !== "function") throw new TypeError("runtime start requires a machine-service lock acquirer");
282
+ const lock = await acquireLock({ operation: "runtime-start", logger });
283
+ if (!lock?.acquired || typeof lock.release !== "function") {
284
+ throw new Error("machine-service operation lock could not be acquired for runtime start");
285
+ }
286
+ return lock;
287
+ }
288
+
238
289
  export function isIdempotentDaemonOnlyStart(args) {
239
290
  if (!args.daemonOnly || args.json) return false;
240
291
  return !Boolean(
@@ -255,44 +306,67 @@ async function startRemoteRuntime({ args, workspace, state, daemonLock, logger }
255
306
  let runtime = null;
256
307
  try {
257
308
  const readiness = await prepareRemoteState({ args, workspace, state, logger });
258
- runtime = createRemoteRuntime({ args, workspace, state, daemonLock });
309
+ runtime = createRemoteRuntime({ args, workspace, state, daemonLock, deviceSessionIdentity: readiness.deviceSessionIdentity });
259
310
  await runtime.start();
260
- reportRemoteReady(args, state, readiness);
311
+ if (typeof daemonLock.update !== "function") throw new Error("daemon lock cannot publish startup readiness");
312
+ daemonLock.update({ startupReady: true, startupReadyAt: new Date().toISOString() });
313
+ reportRemoteReady(args, state, readiness, logger);
314
+ if (args.daemonOnly) {
315
+ const { startAutostartLogMaintenance } = await import("./autostart-log-maintenance.mjs");
316
+ startAutostartLogMaintenance(state.paths.stateRoot, {
317
+ onError(error) {
318
+ logger.event?.("warn", "service.log_maintenance.failed", {
319
+ error_class: classifyOperationalError(error),
320
+ }, "Background log maintenance failed");
321
+ },
322
+ });
323
+ }
261
324
  keepProcessAlive({ daemon: runtime, lock: daemonLock, logger });
262
325
  } catch (error) {
263
- try { runtime?.stop?.(); } catch {}
264
- daemonLock.release();
265
- throw error;
326
+ throw cleanupRuntimeStartFailure(error, runtime, daemonLock);
266
327
  }
267
328
  }
268
329
 
269
- async function prepareRemoteState({ args, workspace, state, logger }) {
270
- const workerName = validateWorkerName(args.workerName);
271
- ensureWorkerSecrets(state, {
272
- rotateSecrets: Boolean(args.rotateSecrets),
273
- workerName,
274
- allowWorkerRename: Boolean(args.forceWorker),
275
- });
276
- state.policy = resolvePolicy(args, state.policy);
277
- state.policy.updatedAt = new Date().toISOString();
278
- saveState(state);
330
+ export function cleanupRuntimeStartFailure(error, runtime, daemonLock) {
331
+ const cleanupErrors = [];
332
+ try { runtime?.stop?.(); } catch (failure) { cleanupErrors.push(failure); }
333
+ try { daemonLock?.release?.(); } catch (failure) { cleanupErrors.push(failure); }
334
+ return cleanupErrors.length
335
+ ? new AggregateError([error, ...cleanupErrors],
336
+ "runtime startup failed and local cleanup was incomplete")
337
+ : error;
338
+ }
279
339
 
280
- let initialOwner = null;
340
+ async function prepareRemoteState({ args, workspace, state, logger, onRemotePrepared, provisionInitialOwner = true }) {
281
341
  if (!args.daemonOnly) {
282
- await ensureWorker(state, args);
283
- initialOwner = await ensureInitialOwnerAccount(state);
342
+ await convergeRemoteConfiguration({ args, state });
343
+ onRemotePrepared?.();
284
344
  } else if (!state.worker.url) {
285
345
  throw new Error("--daemon-only requires an existing Worker URL; run start once without --daemon-only");
346
+ } else if (state.worker.pendingDeviceIdentity) {
347
+ throw new Error("--daemon-only cannot activate a pending device root; run a normal start once to deploy and promote it");
286
348
  }
287
349
 
350
+ const deviceSessionIdentity = await createDeviceSessionForRoot(
351
+ state.worker.deviceIdentity,
352
+ state.worker.url,
353
+ "machine-bridge-mcp",
354
+ currentPackageVersion(),
355
+ { profileDir: state.paths.profileDir, reason: "Authorize Machine Bridge startup" },
356
+ );
357
+ const initialOwner = args.daemonOnly || provisionInitialOwner === false
358
+ ? null
359
+ : await ensureInitialOwnerAccount(state, deviceSessionIdentity);
288
360
  if (!args.daemonOnly && !args.noAutostart) {
289
361
  await installAutostartBestEffort({ workspace, stateRoot: state.paths.stateRoot, entryScript: process.argv[1], logger });
290
362
  }
291
- return { initialOwner };
363
+ return { initialOwner, deviceSessionIdentity };
292
364
  }
293
365
 
294
- async function ensureInitialOwnerAccount(state) {
295
- const client = accountAdminClient(state);
366
+
367
+
368
+ async function ensureInitialOwnerAccount(state, deviceSessionIdentity) {
369
+ const client = await accountAdminClient(state, deviceSessionIdentity);
296
370
  const existing = await client.list();
297
371
  if (existing.accounts.length > 0) return null;
298
372
  const password = generateAccountPassword();
@@ -300,31 +374,43 @@ async function ensureInitialOwnerAccount(state) {
300
374
  return { ...created.account, password };
301
375
  }
302
376
 
303
- function createRemoteRuntime({ args, workspace, state, daemonLock }) {
304
- return new LocalRuntime({
377
+ function createRemoteRuntime({ args, workspace, state, daemonLock, deviceSessionIdentity, exitOnTerminal = true }) {
378
+ const terminalState = { error: null };
379
+ const runtime = new LocalRuntime({
305
380
  workerUrl: state.worker.url,
306
- deviceIdentity: state.worker.deviceIdentity,
381
+ deviceIdentity: deviceSessionIdentity,
307
382
  expectedRelayVersion: currentPackageVersion(),
308
383
  workspace,
309
384
  policy: state.policy,
310
385
  logger: createLogger({ level: args.json ? "error" : effectiveLogLevel(args), format: effectiveLogFormat(args), component: "daemon" }),
311
386
  jobRoot: join(state.paths.profileDir, "jobs"),
312
- approvalRoot: state.paths.profileDir,
387
+ securityStateRoot: state.paths.profileDir,
313
388
  resources: state.resources,
314
389
  resourceStatePath: state.paths.statePath,
315
390
  browserStateRoot: state.paths.stateRoot,
391
+ deviceRootStatus: deviceRootProviderStatus(state.worker.deviceIdentity),
316
392
  onSuperseded: () => {
393
+ if (!exitOnTerminal) {
394
+ terminalState.error ??= new Error("candidate daemon was superseded before service handoff");
395
+ return;
396
+ }
317
397
  daemonLock.release();
318
398
  process.exit(0);
319
399
  },
320
- onFatal: () => {
400
+ onFatal: (error) => {
401
+ if (!exitOnTerminal) {
402
+ terminalState.error ??= error instanceof Error ? error : new Error("candidate relay failed before service handoff");
403
+ return;
404
+ }
321
405
  daemonLock.release();
322
406
  process.exit(1);
323
407
  },
324
408
  });
409
+ if (!exitOnTerminal) runtime.terminalError = () => terminalState.error;
410
+ return runtime;
325
411
  }
326
412
 
327
- function reportRemoteReady(args, state, readiness) {
413
+ function reportRemoteReady(args, state, readiness, logger) {
328
414
  if (args.json) {
329
415
  printStartJson(state, { initialOwner: readiness.initialOwner });
330
416
  return;
@@ -333,6 +419,7 @@ function reportRemoteReady(args, state, readiness) {
333
419
  quiet: Boolean(args.quiet),
334
420
  verbose: Boolean(args.verbose),
335
421
  initialOwner: readiness.initialOwner,
422
+ logger,
336
423
  });
337
424
  }
338
425
 
@@ -350,6 +437,7 @@ async function stdioCommand(args) {
350
437
  resources: state.resources,
351
438
  resourceStatePath: state.paths.statePath,
352
439
  browserStateRoot: state.paths.stateRoot,
440
+ deviceRootStatus: state.worker?.deviceIdentity ? deviceRootProviderStatus(state.worker.deviceIdentity) : null,
353
441
  });
354
442
  }
355
443
 
@@ -378,65 +466,31 @@ async function clientConfigCommand(args) {
378
466
  console.log(codex.trimEnd());
379
467
  }
380
468
  }
381
-
382
- async function ensureWorker(state, args) {
383
- const logger = createLogger({ level: args.json ? "error" : effectiveLogLevel(args), format: effectiveLogFormat(args), component: "worker" });
384
- return ensureWorkerDeployment(state, args, { logger });
385
- }
386
-
387
-
388
- function printStartJson(state, { requestedChangesApplied = true, notice = "", initialOwner = null } = {}) {
389
- createLogger({ component: "ready" }).json({
390
- mcp: {
391
- server_url: state.worker.mcpServerUrl,
392
- worker_url: state.worker.url,
393
- worker_name: state.worker.name,
394
- },
395
- ...(initialOwner ? { initial_owner: initialOwner } : {}),
396
- workspace: state.workspace.path,
397
- state_path: state.paths.statePath,
398
- policy: state.policy,
399
- requested_changes_applied: requestedChangesApplied,
400
- ...(notice ? { notice } : {}),
401
- });
402
- }
403
-
404
- function printMcpConnection(state, { quiet = false, verbose = false, initialOwner = null } = {}) {
405
- const logger = createLogger({ component: "ready", quiet, level: quiet ? "error" : verbose ? "debug" : "info" });
406
- logger.success("Remote MCP bridge is ready");
407
- logger.plain(` MCP Server URL: ${state.worker.mcpServerUrl}`);
408
- if (initialOwner) {
409
- logger.warn("Initial owner account created; save the password now because it is not stored locally or shown again. Do not share this terminal output; rotate the password immediately if it has been exposed.");
410
- logger.plain(` Account: ${initialOwner.name}`);
411
- logger.plain(` Password: ${initialOwner.password}`);
412
- } else {
413
- logger.safePlain(" Use `machine-mcp account` to manage account access.");
414
- }
415
- logger.plain(` Workspace: ${state.workspace.path}`);
416
- logger.safePlain(` Policy: ${formatPolicySummary(state.policy)}`);
417
- if (verbose) logger.plain(` State: ${state.paths.statePath}`);
418
- }
419
-
420
- function formatPolicySummary(policy = {}) {
421
- const scope = policy.unrestrictedPaths ? "all local paths" : "workspace only";
422
- const environment = policy.minimalEnv ? "isolated env" : "full parent env";
423
- return `${policy.profile || "custom"} [${policy.origin || "unknown"}; write=${policy.allowWrite ? "on" : "off"}; exec=${policy.execMode || "off"}; ${scope}; ${environment}; absolute_paths=${policy.exposeAbsolutePaths ? "on" : "off"}]`;
424
- }
425
-
426
-
427
469
  function keepProcessAlive({ daemon = null, lock = null, logger = createLogger({ component: "cli" }) } = {}) {
428
470
  let stopping = false;
429
471
  const stop = async () => {
430
472
  if (stopping) return;
431
473
  stopping = true;
432
474
  logger.info("stopping local services");
433
- try { daemon?.stop?.(); } catch {}
434
- try { lock?.release?.(); } catch {}
475
+ try {
476
+ await daemon?.stop?.();
477
+ } catch (error) {
478
+ stopping = false;
479
+ logger.error("local service shutdown is incomplete; retaining daemon ownership for a later retry", {
480
+ error_class: classifyOperationalError(error),
481
+ });
482
+ return;
483
+ }
484
+ try { lock?.release?.(); }
485
+ catch (error) { logger.warn("daemon lock release failed during final shutdown", { error_class: classifyOperationalError(error) }); }
435
486
  process.exit(0);
436
487
  };
437
- process.once("SIGINT", () => void stop());
438
- process.once("SIGTERM", () => void stop());
439
- process.once("exit", () => lock?.release?.());
488
+ process.on("SIGINT", () => void stop());
489
+ process.on("SIGTERM", () => void stop());
490
+ process.once("exit", () => {
491
+ try { lock?.release?.(); }
492
+ catch { /* Process exit is the final ownership fallback; no in-process recovery remains possible. */ }
493
+ });
440
494
  setInterval(() => {}, 2 ** 31 - 1);
441
495
  }
442
496
 
@@ -454,19 +508,26 @@ async function statusCommand(args) {
454
508
 
455
509
  async function doctorCommand(args) {
456
510
  const workspace = await chooseWorkspace(args, { promptOnFirstRun: false, save: false, allowPositional: true });
511
+ const state = loadState(workspace, { stateDir: args.stateDir });
512
+ state.policy = resolvePolicy({}, state.policy);
457
513
  const checks = [];
458
514
  checks.push({ name: "node", ok: isSupportedNodeVersion(), detail: process.version });
459
515
  const npmCommand = npmVersionCommand();
460
516
  const npm = await runExecutable(npmCommand.file, npmCommand.args, { capture: true, allowFailure: true, timeoutMs: 10_000 });
461
517
  const npmDetail = sanitizeLines(npm.stdout || npm.stderr);
462
518
  checks.push({ name: "npm", ok: npm.code === 0 && isSupportedNpmVersion(npmDetail), detail: npmDetail || "unavailable" });
463
- const wrangler = await runWrangler(["--version"], { capture: true, allowFailure: true });
519
+ const wrangler = await runWrangler(["--version"], { capture: true, allowFailure: true, stateRoot: state.paths.stateRoot });
464
520
  checks.push({ name: "wrangler", ok: wrangler.code === 0, detail: (wrangler.stdout || wrangler.stderr).trim() });
465
- const whoami = await runWrangler(["whoami"], { capture: true, allowFailure: true });
521
+ const whoami = await runWrangler(["whoami"], { capture: true, allowFailure: true, stateRoot: state.paths.stateRoot });
466
522
  checks.push({ name: "cloudflare-login", ok: whoami.code === 0, detail: whoami.code === 0 ? "authenticated" : sanitizeLines(whoami.stderr || whoami.stdout) });
467
- const state = loadState(workspace, { stateDir: args.stateDir });
468
- state.policy = resolvePolicy({}, state.policy);
469
523
  checks.push({ name: "policy", ok: true, detail: formatPolicySummary(state.policy) });
524
+ checks.push({
525
+ name: "authorization-model",
526
+ ok: true,
527
+ detail: state.policy.profile === "full"
528
+ ? "local owner operations execute automatically without per-operation prompts; shell, browser, and application automation use the daemon OS user's ambient authority"
529
+ : "operations allowed by the selected local policy execute automatically without per-operation prompts",
530
+ });
470
531
  if (state.policy.profile === "full") {
471
532
  try {
472
533
  assertCanonicalFullPolicy(state.policy);
@@ -476,7 +537,7 @@ async function doctorCommand(args) {
476
537
  }
477
538
  }
478
539
  const health = state.worker?.url ? await workerHealth(state.worker.url, currentPackageVersion(), { expectedWorkerName: state.worker.name }) : { ok: false, error: "no worker url" };
479
- checks.push({ name: "worker-health", ok: health.ok, detail: health.ok ? state.worker.url : health.error });
540
+ checks.push({ name: "worker-health", ok: health.ok, detail: health.ok ? "reachable" : health.error });
480
541
  const diagnosticRuntime = new LocalRuntime({
481
542
  workspace,
482
543
  policy: state.policy,
@@ -492,18 +553,12 @@ async function doctorCommand(args) {
492
553
  } finally {
493
554
  diagnosticRuntime.stop();
494
555
  }
495
- for (const check of runtimeDiagnostics.checks) {
496
- checks.push({
497
- name: `runtime:${check.layer}`,
498
- ok: check.skipped === true || check.ok === true,
499
- detail: check.skipped ? `skipped (${check.error_class || "not applicable"})` : check.ok ? "ok" : check.error_class || "failed",
500
- });
501
- }
556
+ for (const check of runtimeDiagnostics.checks) checks.push(doctorRuntimeCheckProjection(check));
502
557
  console.log(JSON.stringify({
503
558
  ok: checks.every(check => check.ok),
504
559
  checks,
505
- runtimeDiagnostics,
506
- state: redactState(state),
560
+ diagnosticScope: DOCTOR_RUNTIME_SCOPE,
561
+ state: supportStateProjection(state),
507
562
  }, null, 2));
508
563
  }
509
564
 
@@ -518,11 +573,20 @@ async function fullTestCommand(args) {
518
573
 
519
574
  async function rotateSecretsCommand(args) {
520
575
  const workspace = await chooseWorkspace(args, { promptOnFirstRun: false, save: false, allowPositional: true });
521
- const state = loadState(workspace, { stateDir: args.stateDir });
576
+ let state = loadState(workspace, { stateDir: args.stateDir });
522
577
  const operationLogger = createLogger({ level: args.quiet ? "error" : "warn", component: "service" });
523
- const startupLock = await acquireStartupLockWithWait(state, { operation: "rotate-secrets", logger: operationLogger });
578
+ let startupLock = null;
579
+ let serviceLock = null;
524
580
  try {
525
- await stopAutostartBestEffort(operationLogger);
581
+ serviceLock = await acquireMachineServiceLockWithWait({ operation: "rotate-secrets", logger: operationLogger });
582
+ startupLock = await acquireStartupLockWithWait(state, { operation: "rotate-secrets", logger: operationLogger });
583
+ state = loadState(workspace, { stateDir: args.stateDir });
584
+ await stopOwnedPlatformService({
585
+ state,
586
+ inspectWorkspaceDaemon,
587
+ ownsPlatformAutostart: workspaceDaemonOwnsPlatformAutostart,
588
+ stopPlatformService: () => stopAutostartBestEffort(operationLogger),
589
+ });
526
590
  const stopped = await stopWorkspaceServiceDaemon(state, { logger: operationLogger, reason: "secret rotation" });
527
591
  if (stopped.found && !stopped.ok) {
528
592
  const pid = stopped.pid ? `pid ${stopped.pid}` : "unknown pid";
@@ -534,19 +598,35 @@ async function rotateSecretsCommand(args) {
534
598
  if (daemonIdentity?.current) {
535
599
  throw new Error(`refusing to rotate secrets while the daemon is active (pid ${daemonOwner.pid}); stop the foreground daemon and retry`);
536
600
  }
537
- ensureWorkerSecrets(state, { rotateSecrets: true });
601
+ if (state.worker.pendingDeviceIdentity) throw new Error("a device-root rotation is already pending; run machine-mcp to deploy it before rotating again");
602
+ ensureWorkerSecrets(state, { rotateSecrets: true, deferDeviceRotation: true });
603
+ state.worker.pendingDeviceIdentity = await ensurePreferredDeviceRoot({
604
+ profileDir: state.paths.profileDir,
605
+ workspaceHash: state.workspace.hash,
606
+ existing: null,
607
+ rotate: true,
608
+ });
538
609
  saveState(state);
539
- console.log("Rotated account administration, daemon, and global token-version secrets.");
540
- console.log("All account access tokens are invalid. Run machine-mcp to redeploy, then reconnect clients.");
610
+ console.log("Prepared a two-phase rotation for account administration, device root, and token-version secrets.");
611
+ console.log("All account access tokens are invalid. Run machine-mcp to deploy, verify, and atomically promote the pending device root.");
541
612
  } finally {
542
- startupLock.release();
613
+ startupLock?.release?.();
614
+ serviceLock?.release?.();
543
615
  }
544
616
  }
545
617
 
546
- async function installAutostartBestEffort({ workspace, stateRoot, entryScript, logger }) {
618
+ export async function installAutostartBestEffort({ workspace, stateRoot, entryScript, logger }, options = {}) {
619
+ let serviceLock = null;
547
620
  try {
548
- const { installAutostart } = await import("./service.mjs");
549
- const result = await installAutostart({ workspace, stateRoot, entryScript, logger: structuredLogger(true) });
621
+ const acquireLock = options.acquireServiceLock || acquireMachineServiceLockWithWait;
622
+ const acquired = await acquireLock({ operation: "runtime-start-autostart", logger });
623
+ if (!acquired?.acquired || typeof acquired.release !== "function") {
624
+ throw new Error("machine-service operation lock could not be acquired for automatic autostart installation");
625
+ }
626
+ serviceLock = acquired;
627
+ const install = options.installAutostart || (await import("./service.mjs")).installAutostart;
628
+ const makeLogger = options.structuredLogger || structuredLogger;
629
+ const result = await install({ workspace, stateRoot, entryScript, version: currentPackageVersion(), logger: makeLogger(true) });
550
630
  if (result?.ok) logger.info("Autostart installed for future logins", { provider: result.provider });
551
631
  else logger.warn("Autostart installation reported a problem; run `machine-mcp service status` for details", {
552
632
  provider: result?.provider || "unknown",
@@ -554,6 +634,8 @@ async function installAutostartBestEffort({ workspace, stateRoot, entryScript, l
554
634
  });
555
635
  } catch (error) {
556
636
  logger.warn("Autostart installation skipped", { error_class: classifyOperationalError(error) });
637
+ } finally {
638
+ serviceLock?.release?.();
557
639
  }
558
640
  }
559
641
 
@@ -585,13 +667,19 @@ async function uninstallCommand(args) {
585
667
  console.log("Uninstall cancelled. Re-run with `machine-mcp uninstall --yes` to skip confirmation.");
586
668
  return;
587
669
  }
670
+ await withReleaseRuntimeLock(stateRoot, () => uninstallStateRoot({ stateRoot, deleteRemote }));
671
+ }
672
+
673
+ async function uninstallStateRoot({ stateRoot, deleteRemote }) {
588
674
  const currentValidation = validateStateRootForRemoval(stateRoot);
589
675
  const maintenance = currentValidation.exists ? acquireMaintenanceLock(stateRoot, { operation: "uninstall" }) : null;
590
676
  if (maintenance && !maintenance.acquired) {
591
677
  const pid = maintenance.owner?.pid ? `pid ${maintenance.owner.pid}` : "another process";
592
678
  throw new Error(`another state maintenance operation is active (${pid})`);
593
679
  }
680
+ let serviceLock = null;
594
681
  try {
682
+ serviceLock = await acquireMachineServiceLockWithWait({ operation: "uninstall" });
595
683
  if (currentValidation.exists) validateStateRootForRemoval(stateRoot);
596
684
  assertNoActiveJobsForUninstall(stateRoot);
597
685
  const autostartRemoved = await removeAutostartBestEffort(stateRoot);
@@ -608,16 +696,24 @@ async function uninstallCommand(args) {
608
696
  console.log("If installed globally, remove the npm package with:");
609
697
  console.log(" npm uninstall -g machine-bridge-mcp");
610
698
  } finally {
699
+ serviceLock?.release?.();
611
700
  maintenance?.release?.();
612
701
  }
613
702
  }
614
703
 
615
704
  function assertNoActiveJobsForUninstall(stateRoot) {
705
+ for (const state of knownProfileStates(stateRoot)) {
706
+ pruneRetiredManagedJobDirectories(join(state.paths.profileDir, "jobs"), { warn() {} });
707
+ }
616
708
  const activeJobs = activeStateJobs(stateRoot);
617
709
  if (!activeJobs.length) return;
618
- const detail = activeJobs.slice(0, 5).map((item) => `${item.job_id}:${item.status}`).join(", ");
710
+ const retiredBlocked = activeJobs.some((item) => item.state_kind === "retired_managed_job");
711
+ const detail = activeJobs.slice(0, 5).map((item) => item.state_kind === "retired_managed_job" ? `retired-managed-job:${item.status}` : `${item.job_id}:${item.status}`).join(", ");
619
712
  const suffix = activeJobs.length > 5 ? `, and ${activeJobs.length - 5} more` : "";
620
- throw new Error(`refusing to uninstall while managed jobs are active (${detail}${suffix}); inspect or cancel them with machine-mcp job list/cancel`);
713
+ const guidance = retiredBlocked
714
+ ? "run machine-mcp job list to retry safe retired cleanup; if retired state remains unreadable, inspect the owner-only state root instead of deleting it blindly; cancel any active jobs before retrying"
715
+ : "inspect with machine-mcp job list/read and cancel active jobs before retrying";
716
+ throw new Error(`refusing to uninstall while managed-job state is active or requires inspection (${detail}${suffix}); ${guidance}`);
621
717
  }
622
718
 
623
719
  function assertNoActiveLocksForUninstall(stateRoot) {
@@ -635,9 +731,9 @@ async function deleteKnownWorkers(stateRoot) {
635
731
  }
636
732
  const failures = [];
637
733
  for (const name of names) {
638
- const result = await runWrangler(["delete", name, "--force"], { capture: true, allowFailure: true });
734
+ const result = await runWrangler(["delete", name, "--force"], { capture: true, allowFailure: true, stateRoot });
639
735
  const detail = (result.stderr || result.stdout || "unknown error").trim();
640
- if (result.code === 0 || /not found|does not exist|could not find/i.test(detail)) {
736
+ if (result.code === 0) {
641
737
  console.log(`Deleted Worker: ${name}`);
642
738
  } else {
643
739
  failures.push({ name, detail: sanitizeLines(detail) || "unknown error" });
@@ -694,14 +790,7 @@ function escapeRegExp(value) {
694
790
  return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
695
791
  }
696
792
 
697
- function validateWorkerName(value) {
698
- if (value === undefined || value === null || value === false) return undefined;
699
- const name = String(value).trim();
700
- if (!/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(name)) {
701
- throw new Error("--worker-name must be 1-63 lowercase letters, digits, or hyphens, and cannot start or end with a hyphen");
702
- }
703
- return name;
704
- }
793
+
705
794
 
706
795
  export function isSupportedNodeVersion(version = process.versions.node) {
707
796
  const major = Number(String(version || "").replace(/^v/, "").split(".")[0]);
@@ -737,28 +826,23 @@ Usage:
737
826
 
738
827
  Commands:
739
828
  start Deploy/update Worker, take over autostart, run foreground daemon
829
+ activate Deploy/update Worker, replace autostart, start and verify background daemon
740
830
  stdio Run a local MCP stdio server for Claude, Cursor, Codex, and compatible clients
741
831
  client-config Print stdio client configuration snippets
742
832
  workspace show Show remembered workspace
743
833
  workspace set Re-select workspace; prompts with current/default path
744
834
  service status Show autostart status
745
835
  service install Install login autostart for remembered/current workspace
746
- service start Start the installed autostart service now
836
+ service start Ensure the installed autostart service is running (idempotent)
837
+ service restart Schedule an explicit service-manager restart and return before handoff
747
838
  service stop Stop the installed autostart service
748
839
  service uninstall Remove only the autostart entry
749
840
  status Print redacted local profile state and Worker health
750
841
  doctor Check Node, Wrangler, Cloudflare login, Worker health
751
842
  full-test Run real local full-profile capability tests in a temporary sandbox
752
843
  rotate-secrets Rotate account-admin, device identity, and global token-version secrets
753
- account list|add|role|enable|disable|rotate-password|remove
754
- Manage isolated remote accounts and targeted revocation
755
- approval list Show pending high-impact operations and active capability leases
756
- approval approve APPROVAL_ID [--duration 1h] [--full]
757
- Approve its scope, or open an explicit full window (maximum 8h)
758
- approval grant SCOPE --account ACCOUNT_ID --client CLIENT_ID [--duration 1h]
759
- Pre-authorize a bounded scope; use * values only intentionally
760
- approval revoke LEASE_ID | approval clear
761
- Revoke one or all local capability leases
844
+ account list|clients|revoke-client|add|role|enable|disable|rotate-password|remove
845
+ Manage remote accounts, trusted clients, and targeted revocation
762
846
  resource generate-ssh-key NAME [PATH]
763
847
  Generate/reuse an Ed25519 key locally and register its private file by alias
764
848
  browser status Show browser-extension bridge and connection status
@@ -797,13 +881,11 @@ Uninstall options:
797
881
  }
798
882
 
799
883
  function currentPackageVersion() {
800
- const pkg = JSON.parse(readFileSync(resolve(packageRoot, "package.json"), "utf8"));
801
- return String(pkg.version);
884
+ return packageVersion;
802
885
  }
803
886
 
804
887
  function version() {
805
- const pkg = JSON.parse(readFileSync(resolve(packageRoot, "package.json"), "utf8"));
806
- console.log(`${pkg.name} ${pkg.version}`);
888
+ console.log(`${packageName} ${packageVersion}`);
807
889
  }
808
890
 
809
891
  function sleep(ms) {