machine-bridge-mcp 2.0.0 → 3.0.0-beta.102

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (473) hide show
  1. package/CHANGELOG.md +1142 -0
  2. package/CONTRIBUTING.md +23 -25
  3. package/GOVERNANCE.md +2 -2
  4. package/README.md +58 -20
  5. package/SECURITY.md +161 -120
  6. package/SUPPORT.md +1 -1
  7. package/browser-extension/broker-auth.js +83 -0
  8. package/browser-extension/broker-liveness.js +35 -0
  9. package/browser-extension/browser-error-boundary.js +96 -0
  10. package/browser-extension/browser-operations.js +1456 -101
  11. package/browser-extension/devtools-input.js +195 -34
  12. package/browser-extension/devtools-observation.js +429 -0
  13. package/browser-extension/devtools-session.js +48 -0
  14. package/browser-extension/manifest.json +4 -3
  15. package/browser-extension/page-automation.js +578 -118
  16. package/browser-extension/pairing-bootstrap.js +42 -0
  17. package/browser-extension/pairing.js +28 -12
  18. package/browser-extension/service-worker.js +126 -106
  19. package/docs/AGENT_CONTEXT.md +13 -10
  20. package/docs/ARCHITECTURE.md +159 -67
  21. package/docs/AUDIT.md +1302 -4
  22. package/docs/CLIENTS.md +19 -6
  23. package/docs/COMPUTER_USE.md +297 -0
  24. package/docs/ENGINEERING.md +72 -13
  25. package/docs/GETTING_STARTED.md +18 -20
  26. package/docs/LOCAL_AUTHORIZATION.md +115 -72
  27. package/docs/LOCAL_AUTOMATION.md +27 -21
  28. package/docs/LOGGING.md +40 -19
  29. package/docs/MANAGED_JOBS.md +37 -34
  30. package/docs/MULTI_ACCOUNT.md +98 -37
  31. package/docs/OPERATIONS.md +145 -51
  32. package/docs/OVERVIEW.md +14 -11
  33. package/docs/POLICY_REFERENCE.md +71 -3
  34. package/docs/PRIVACY.md +41 -2
  35. package/docs/PROJECT_STANDARDS.md +29 -6
  36. package/docs/RELEASING.md +238 -64
  37. package/docs/TESTING.md +150 -39
  38. package/docs/THREAT_MODEL.md +164 -67
  39. package/docs/TOOL_REFERENCE.md +653 -60
  40. package/docs/UPGRADING.md +78 -18
  41. package/native/macos/MachineBridgeBackgroundInput.swift +1051 -0
  42. package/native/macos/MachineBridgeBackgroundInputSmokeFixture.swift +295 -0
  43. package/native/macos/MachineBridgeTrustBroker.swift +233 -0
  44. package/package.json +94 -16
  45. package/scripts/accepted-candidate-tarball.mjs +77 -0
  46. package/scripts/candidate-runtime-store.mjs +93 -0
  47. package/scripts/check-plan.mjs +73 -3
  48. package/scripts/check-runner.mjs +129 -17
  49. package/scripts/commit-message-check.mjs +5 -2
  50. package/scripts/consumer-package-security.mjs +293 -0
  51. package/scripts/coverage-check.mjs +251 -15
  52. package/scripts/coverage-generation.mjs +41 -0
  53. package/scripts/coverage-range-merge.mjs +41 -0
  54. package/scripts/foreground-daemon-recovery.mjs +88 -0
  55. package/scripts/generate-policy-reference.mjs +28 -5
  56. package/scripts/generate-tool-reference.mjs +1 -1
  57. package/scripts/generate-worker-types.mjs +21 -11
  58. package/scripts/github-backlog.mjs +11 -10
  59. package/scripts/github-push.mjs +62 -16
  60. package/scripts/github-release-asset.mjs +58 -0
  61. package/scripts/github-release.mjs +241 -132
  62. package/scripts/global-package-installation.mjs +68 -0
  63. package/scripts/hardened-npm-session.mjs +42 -0
  64. package/scripts/install-published-prerelease.mjs +198 -0
  65. package/scripts/local-release-acceptance.mjs +103 -45
  66. package/scripts/macos-background-input-smoke.mjs +515 -0
  67. package/scripts/network-retry.mjs +8 -1
  68. package/scripts/npm-global-prefix.mjs +27 -0
  69. package/scripts/npm-publication-policy.mjs +17 -0
  70. package/scripts/official-mcp-conformance.mjs +267 -0
  71. package/scripts/persistent-activation-process.mjs +101 -0
  72. package/scripts/prepare-pinned-npm.mjs +12 -69
  73. package/scripts/prerelease-activation.mjs +160 -0
  74. package/scripts/privacy-check.mjs +14 -6
  75. package/scripts/promotion-digest.mjs +128 -0
  76. package/scripts/publish-npm.mjs +260 -0
  77. package/scripts/published-release.mjs +132 -0
  78. package/scripts/release-acceptance.mjs +62 -54
  79. package/scripts/release-browser-extension-store.mjs +172 -0
  80. package/scripts/release-candidate-manifest.mjs +52 -0
  81. package/scripts/release-channel.mjs +106 -0
  82. package/scripts/release-diagnostic.mjs +41 -0
  83. package/scripts/release-impact-check.mjs +12 -22
  84. package/scripts/release-oauth-canary-core.mjs +325 -0
  85. package/scripts/release-oauth-canary-evidence.mjs +100 -0
  86. package/scripts/release-oauth-canary.mjs +127 -0
  87. package/scripts/release-publication-guard.mjs +67 -0
  88. package/scripts/release-soak.mjs +284 -0
  89. package/scripts/release-state.mjs +1 -1
  90. package/scripts/run-checks.mjs +44 -7
  91. package/scripts/run-worker-dry-run.mjs +21 -0
  92. package/scripts/sarif-security-gate.mjs +2 -1
  93. package/scripts/sbom-check.mjs +106 -0
  94. package/scripts/start-release-candidate.mjs +267 -44
  95. package/scripts/syntax-check.mjs +36 -14
  96. package/scripts/verification-generation-guard.mjs +15 -0
  97. package/scripts/verification-idle-sleep-guard.mjs +37 -0
  98. package/scripts/verification-state.mjs +108 -0
  99. package/scripts/wrangler-command-lifecycle.mjs +115 -0
  100. package/src/local/account-access.mjs +13 -1
  101. package/src/local/account-admin-response.mjs +92 -0
  102. package/src/local/account-admin.mjs +52 -19
  103. package/src/local/agent-context-projection.mjs +26 -7
  104. package/src/local/agent-context.mjs +88 -49
  105. package/src/local/agent-skill-discovery.mjs +16 -5
  106. package/src/local/app-automation-macos-jxa.mjs +350 -0
  107. package/src/local/app-automation.mjs +873 -163
  108. package/src/local/application-capability-projection.mjs +24 -0
  109. package/src/local/authority-context.mjs +106 -0
  110. package/src/local/autostart-log-maintenance.mjs +36 -0
  111. package/src/local/browser-bridge-http.mjs +5 -3
  112. package/src/local/browser-bridge.mjs +109 -31
  113. package/src/local/browser-broker-auth-http.mjs +67 -0
  114. package/src/local/browser-broker-auth.mjs +98 -0
  115. package/src/local/browser-broker-routes.mjs +63 -20
  116. package/src/local/browser-broker-server.mjs +17 -7
  117. package/src/local/browser-command.mjs +79 -30
  118. package/src/local/browser-computer-observation-service.mjs +266 -0
  119. package/src/local/browser-extension-identity.mjs +50 -0
  120. package/src/local/browser-extension-path.mjs +22 -0
  121. package/src/local/browser-extension-protocol.mjs +50 -11
  122. package/src/local/browser-operation-service.mjs +147 -117
  123. package/src/local/browser-pairing-grant.mjs +109 -0
  124. package/src/local/browser-pairing-http.mjs +36 -0
  125. package/src/local/browser-pairing-launch.mjs +58 -0
  126. package/src/local/browser-pairing-store.mjs +85 -48
  127. package/src/local/browser-request-registry.mjs +40 -14
  128. package/src/local/browser-request-settlement.mjs +78 -0
  129. package/src/local/browser-resource-input.mjs +74 -0
  130. package/src/local/browser-trusted-input-health.mjs +63 -0
  131. package/src/local/call-authority.mjs +21 -0
  132. package/src/local/call-capacity.mjs +45 -0
  133. package/src/local/call-registry-drain.mjs +54 -0
  134. package/src/local/call-registry.mjs +30 -15
  135. package/src/local/capability-observer.mjs +5 -0
  136. package/src/local/capability-ranking.mjs +3 -2
  137. package/src/local/child-process-settlement.mjs +132 -0
  138. package/src/local/cli-account-admin.mjs +40 -6
  139. package/src/local/cli-activate.mjs +162 -0
  140. package/src/local/cli-local-admin.mjs +20 -45
  141. package/src/local/cli-options.mjs +12 -13
  142. package/src/local/cli-ready-output.mjs +52 -0
  143. package/src/local/cli-service.mjs +91 -11
  144. package/src/local/cli.mjs +232 -150
  145. package/src/local/computer-use-application-observation.mjs +224 -0
  146. package/src/local/computer-use-arguments.mjs +270 -0
  147. package/src/local/computer-use-deadline.mjs +50 -0
  148. package/src/local/computer-use-dispatch-settlement.mjs +40 -0
  149. package/src/local/computer-use-expectation.mjs +171 -0
  150. package/src/local/computer-use-observation.mjs +769 -0
  151. package/src/local/computer-use-recovery.mjs +82 -0
  152. package/src/local/computer-use-result-budget.mjs +145 -0
  153. package/src/local/computer-use-snapshot-store.mjs +67 -0
  154. package/src/local/computer-use.mjs +2255 -0
  155. package/src/local/daemon-http-relay-auth.mjs +26 -0
  156. package/src/local/daemon-http-relay-connection.mjs +298 -0
  157. package/src/local/daemon-http-relay-request.mjs +64 -0
  158. package/src/local/daemon-http-relay-sequence.mjs +49 -0
  159. package/src/local/daemon-process.mjs +40 -7
  160. package/src/local/delegated-process-sandbox.mjs +148 -0
  161. package/src/local/device-identity.mjs +156 -49
  162. package/src/local/device-root-provider.mjs +81 -0
  163. package/src/local/directory-metadata.mjs +47 -0
  164. package/src/local/doctor-reporting.mjs +23 -0
  165. package/src/local/durable-process-spec.mjs +80 -0
  166. package/src/local/errors.mjs +25 -15
  167. package/src/local/exclusive-file.mjs +74 -26
  168. package/src/local/execution-limits.mjs +9 -0
  169. package/src/local/execution-routing.mjs +276 -0
  170. package/src/local/execution-surface.mjs +23 -0
  171. package/src/local/file-mutation-coordinator.mjs +47 -0
  172. package/src/local/file-snapshot-preservation.mjs +24 -0
  173. package/src/local/filesystem-identity.mjs +54 -0
  174. package/src/local/fixed-process-environment.mjs +21 -0
  175. package/src/local/full-access-test.mjs +31 -13
  176. package/src/local/git-commit.mjs +69 -0
  177. package/src/local/git-config-safety.mjs +55 -0
  178. package/src/local/git-log-parser.mjs +41 -0
  179. package/src/local/git-metadata-boundary.mjs +77 -0
  180. package/src/local/git-metadata-tree-safety.mjs +59 -0
  181. package/src/local/git-operation-state.mjs +19 -0
  182. package/src/local/git-service.mjs +165 -30
  183. package/src/local/hardened-npm-download-timeout.mjs +46 -0
  184. package/src/local/hardened-npm-download.mjs +109 -0
  185. package/src/local/hardened-npm-extract.mjs +20 -0
  186. package/src/local/hardened-npm-verification.mjs +104 -0
  187. package/src/local/hardened-npm.mjs +199 -0
  188. package/src/local/job-runner.mjs +244 -89
  189. package/src/local/lifecycle.mjs +6 -0
  190. package/src/local/log.mjs +19 -45
  191. package/src/local/macos-background-input.mjs +398 -0
  192. package/src/local/macos-idle-sleep-assertion.mjs +65 -0
  193. package/src/local/macos-trust-broker.mjs +404 -0
  194. package/src/local/managed-job-cancellation.mjs +42 -0
  195. package/src/local/managed-job-capacity.mjs +16 -0
  196. package/src/local/managed-job-directory-generation.mjs +60 -0
  197. package/src/local/managed-job-directory.mjs +75 -0
  198. package/src/local/managed-job-durable-process.mjs +49 -0
  199. package/src/local/managed-job-lock.mjs +46 -14
  200. package/src/local/managed-job-plan.mjs +38 -14
  201. package/src/local/managed-job-projection.mjs +23 -15
  202. package/src/local/managed-job-retention.mjs +128 -0
  203. package/src/local/managed-job-runner-claim.mjs +70 -0
  204. package/src/local/managed-job-runner.mjs +35 -10
  205. package/src/local/managed-job-storage.mjs +22 -6
  206. package/src/local/managed-job-terminal-maintenance.mjs +40 -0
  207. package/src/local/managed-job-terminal.mjs +150 -0
  208. package/src/local/managed-jobs.mjs +424 -287
  209. package/src/local/npm-cli.mjs +42 -0
  210. package/src/local/npm-environment.mjs +37 -0
  211. package/src/local/operation-authorization.mjs +68 -324
  212. package/src/local/operation-risk.mjs +51 -4
  213. package/src/local/owner-state-lock.mjs +118 -0
  214. package/src/local/package-identity.mjs +9 -0
  215. package/src/local/patch.mjs +20 -18
  216. package/src/local/path-inspection.mjs +23 -0
  217. package/src/local/policy.mjs +19 -0
  218. package/src/local/private-toolchain-integrity.mjs +54 -0
  219. package/src/local/process-execution.mjs +87 -75
  220. package/src/local/process-foreground-timeout.mjs +50 -0
  221. package/src/local/process-identity.mjs +41 -7
  222. package/src/local/process-nonreplayable-settlement.mjs +50 -0
  223. package/src/local/process-result-projection.mjs +22 -0
  224. package/src/local/process-session-events.mjs +47 -0
  225. package/src/local/process-session-remote-activity.mjs +10 -0
  226. package/src/local/process-session-termination.mjs +73 -0
  227. package/src/local/process-sessions.mjs +144 -112
  228. package/src/local/process-tracker.mjs +140 -13
  229. package/src/local/process-tree-ownership-types.d.ts +37 -0
  230. package/src/local/process-tree-ownership.mjs +57 -0
  231. package/src/local/process-tree-signal.mjs +34 -0
  232. package/src/local/process-tree-snapshot.mjs +77 -0
  233. package/src/local/process-tree-supervisor.mjs +69 -0
  234. package/src/local/process-tree.mjs +11 -53
  235. package/src/local/project-metadata.mjs +19 -6
  236. package/src/local/relay-call-recovery.mjs +58 -38
  237. package/src/local/relay-connection-classification.mjs +131 -0
  238. package/src/local/relay-connection-support.mjs +91 -0
  239. package/src/local/relay-connection.mjs +179 -272
  240. package/src/local/relay-diagnostics.mjs +91 -0
  241. package/src/local/relay-heartbeat.mjs +119 -0
  242. package/src/local/relay-liveness.mjs +109 -0
  243. package/src/local/relay-peer-diagnostics.mjs +29 -0
  244. package/src/local/release-runtime-lock.mjs +18 -0
  245. package/src/local/remote-activity-idle-sleep-guard.mjs +77 -0
  246. package/src/local/remote-configuration.mjs +48 -0
  247. package/src/local/resilient-relay-connection.mjs +178 -0
  248. package/src/local/resource-admission-diagnostics.mjs +52 -0
  249. package/src/local/resource-admission-policy.mjs +115 -0
  250. package/src/local/resource-admission.mjs +339 -0
  251. package/src/local/resource-build-root.mjs +83 -0
  252. package/src/local/resource-cargo-concurrency.mjs +38 -0
  253. package/src/local/resource-cmake-concurrency.mjs +42 -0
  254. package/src/local/resource-command-concurrency.mjs +37 -0
  255. package/src/local/resource-command-profile.mjs +176 -0
  256. package/src/local/resource-coordinator-accounting.mjs +21 -0
  257. package/src/local/resource-cpu-window.mjs +20 -0
  258. package/src/local/resource-disk-headroom.mjs +13 -0
  259. package/src/local/resource-elastic-memory.mjs +19 -0
  260. package/src/local/resource-elastic-request.mjs +36 -0
  261. package/src/local/resource-foreground-wait.mjs +29 -0
  262. package/src/local/resource-go-concurrency.mjs +43 -0
  263. package/src/local/resource-gradle-concurrency.mjs +57 -0
  264. package/src/local/resource-host-cache.mjs +39 -0
  265. package/src/local/resource-host-darwin.mjs +54 -0
  266. package/src/local/resource-host-linux.mjs +35 -0
  267. package/src/local/resource-host-sample-file.mjs +23 -0
  268. package/src/local/resource-host-snapshot.mjs +99 -0
  269. package/src/local/resource-lease-accounting.mjs +119 -0
  270. package/src/local/resource-light-command.mjs +6 -0
  271. package/src/local/resource-make-concurrency.mjs +42 -0
  272. package/src/local/resource-maven-concurrency.mjs +38 -0
  273. package/src/local/resource-ninja-command-concurrency.mjs +20 -0
  274. package/src/local/resource-ninja-concurrency.mjs +28 -0
  275. package/src/local/resource-operations.mjs +26 -10
  276. package/src/local/resource-pressure.mjs +67 -0
  277. package/src/local/resource-probe-command.mjs +35 -0
  278. package/src/local/resource-process-admission.mjs +59 -0
  279. package/src/local/resource-process-ancestry-cache.mjs +43 -0
  280. package/src/local/resource-process-ancestry.mjs +32 -0
  281. package/src/local/resource-process-priority.mjs +15 -0
  282. package/src/local/resource-project-key.mjs +53 -0
  283. package/src/local/resource-pytest-concurrency.mjs +33 -0
  284. package/src/local/resource-release-control-classification.mjs +14 -0
  285. package/src/local/resource-release-control-executable.mjs +70 -0
  286. package/src/local/resource-release-control-workspace.mjs +41 -0
  287. package/src/local/resource-request-contract.mjs +30 -0
  288. package/src/local/resource-script-classification.mjs +44 -0
  289. package/src/local/resource-shell-analysis.mjs +58 -0
  290. package/src/local/resource-staging-recovery.mjs +97 -0
  291. package/src/local/resource-swift-concurrency.mjs +31 -0
  292. package/src/local/resource-transaction-lock.mjs +189 -0
  293. package/src/local/resource-wait.mjs +48 -0
  294. package/src/local/resource-waiters.mjs +139 -0
  295. package/src/local/resource-xcode-command.mjs +7 -0
  296. package/src/local/resource-xcode-concurrency.mjs +34 -0
  297. package/src/local/resource-xcode-non-build.mjs +19 -0
  298. package/src/local/runtime-activation.mjs +700 -0
  299. package/src/local/runtime-activity-projection.mjs +48 -0
  300. package/src/local/runtime-capabilities.mjs +48 -15
  301. package/src/local/runtime-diagnostic-state.mjs +14 -0
  302. package/src/local/runtime-diagnostics.mjs +40 -13
  303. package/src/local/runtime-info-projection.mjs +60 -0
  304. package/src/local/runtime-info-relay-projection.mjs +21 -0
  305. package/src/local/runtime-paths.mjs +2 -1
  306. package/src/local/runtime-process-routing.mjs +33 -0
  307. package/src/local/runtime-relay-connection-options.mjs +44 -0
  308. package/src/local/runtime-relay-control.mjs +98 -0
  309. package/src/local/runtime-relay.mjs +26 -50
  310. package/src/local/runtime-reporting.mjs +37 -21
  311. package/src/local/runtime-resource-service.mjs +77 -0
  312. package/src/local/runtime-tool-handlers.mjs +13 -10
  313. package/src/local/runtime.mjs +244 -208
  314. package/src/local/secure-file.mjs +73 -20
  315. package/src/local/security-audit-dispatch.mjs +35 -0
  316. package/src/local/security-audit-log.mjs +215 -0
  317. package/src/local/security-audit-state.mjs +139 -0
  318. package/src/local/security-audit-storage.mjs +135 -0
  319. package/src/local/security-audit-warning.mjs +31 -0
  320. package/src/local/security-audit-worker.mjs +87 -0
  321. package/src/local/service-convergence.mjs +42 -1
  322. package/src/local/service-definition.mjs +24 -0
  323. package/src/local/service-environment.mjs +18 -10
  324. package/src/local/service-owner.mjs +146 -0
  325. package/src/local/service-ownership.mjs +17 -0
  326. package/src/local/service-restart-handoff.mjs +64 -0
  327. package/src/local/service-restart-scheduler.mjs +49 -0
  328. package/src/local/service-runtime-convergence.mjs +52 -0
  329. package/src/local/service-runtime.mjs +105 -0
  330. package/src/local/service-status.mjs +60 -0
  331. package/src/local/service.mjs +385 -92
  332. package/src/local/shell.mjs +39 -19
  333. package/src/local/ssh-key.mjs +158 -42
  334. package/src/local/state-inventory.mjs +50 -24
  335. package/src/local/state-owner-lock-inventory.mjs +23 -0
  336. package/src/local/state-root-owned-namespaces.mjs +83 -0
  337. package/src/local/state-root-retirement.mjs +89 -0
  338. package/src/local/state.mjs +386 -96
  339. package/src/local/stdio.mjs +96 -66
  340. package/src/local/support-state-projection.mjs +30 -0
  341. package/src/local/system-network-route.mjs +76 -0
  342. package/src/local/systemd-removal.mjs +20 -0
  343. package/src/local/tool-executor.mjs +85 -20
  344. package/src/local/tool-result-boundary.mjs +51 -0
  345. package/src/local/toolchain-operation-lock.mjs +41 -0
  346. package/src/local/tools.mjs +7 -5
  347. package/src/local/trusted-executable.mjs +53 -0
  348. package/src/local/trusted-git-executable.mjs +34 -0
  349. package/src/local/trusted-github-cli.mjs +24 -0
  350. package/src/local/windows-service-convergence.mjs +49 -0
  351. package/src/local/windows-service.mjs +52 -45
  352. package/src/local/worker-deployment-fingerprint.mjs +102 -0
  353. package/src/local/worker-deployment.mjs +48 -60
  354. package/src/local/worker-health.mjs +12 -4
  355. package/src/local/worker-secret-file.mjs +37 -17
  356. package/src/local/workspace-file-service.mjs +156 -239
  357. package/src/local/workspace-file-transaction.mjs +189 -0
  358. package/src/local/workspace-search.mjs +55 -0
  359. package/src/local/wrangler-toolchain/package-lock.json +1505 -0
  360. package/src/local/wrangler-toolchain/package.json +23 -0
  361. package/src/local/wrangler-toolchain-verification.mjs +135 -0
  362. package/src/local/wrangler-toolchain.mjs +188 -0
  363. package/src/shared/access-contract.json +7 -1
  364. package/src/shared/activation-recovery.mjs +49 -0
  365. package/src/shared/admin-auth.d.mts +2 -1
  366. package/src/shared/admin-auth.mjs +3 -3
  367. package/src/shared/authority-revocation.d.mts +9 -0
  368. package/src/shared/authority-revocation.mjs +28 -0
  369. package/src/shared/daemon-auth.d.mts +11 -0
  370. package/src/shared/daemon-auth.mjs +17 -0
  371. package/src/shared/device-session-auth.d.mts +20 -0
  372. package/src/shared/device-session-auth.mjs +51 -0
  373. package/src/shared/foreground-timeout.d.mts +15 -0
  374. package/src/shared/foreground-timeout.mjs +70 -0
  375. package/src/shared/log-redaction.d.mts +9 -0
  376. package/src/shared/log-redaction.mjs +51 -0
  377. package/src/shared/mcp-protocol.d.mts +27 -0
  378. package/src/shared/mcp-protocol.mjs +275 -0
  379. package/src/shared/project-overview-projection.d.mts +4 -0
  380. package/src/shared/project-overview-projection.mjs +96 -0
  381. package/src/shared/relay-contract.json +28 -0
  382. package/src/shared/result-projection.d.mts +2 -1
  383. package/src/shared/result-projection.mjs +13 -2
  384. package/src/shared/server-metadata.json +15 -9
  385. package/src/shared/tool-argument-validation.d.mts +17 -0
  386. package/src/shared/tool-argument-validation.mjs +325 -0
  387. package/src/shared/tool-call-capacity.d.mts +41 -0
  388. package/src/shared/tool-call-capacity.mjs +59 -0
  389. package/src/shared/tool-catalog.json +626 -57
  390. package/src/worker/access.ts +12 -0
  391. package/src/worker/account-admin.ts +55 -33
  392. package/src/worker/authority-revocations.ts +174 -0
  393. package/src/worker/authority.ts +52 -6
  394. package/src/worker/daemon-auth.ts +49 -68
  395. package/src/worker/daemon-channel.ts +36 -0
  396. package/src/worker/daemon-http-auth.ts +63 -0
  397. package/src/worker/daemon-http-channel.ts +120 -0
  398. package/src/worker/daemon-http-controller.ts +163 -0
  399. package/src/worker/daemon-http-protocol.ts +62 -0
  400. package/src/worker/daemon-http-registry.ts +82 -0
  401. package/src/worker/daemon-last-observation.ts +30 -0
  402. package/src/worker/daemon-liveness.ts +8 -6
  403. package/src/worker/daemon-ready-dispatch.ts +17 -0
  404. package/src/worker/daemon-ready-messages.ts +73 -0
  405. package/src/worker/daemon-ready-waiters.ts +79 -0
  406. package/src/worker/daemon-recovery-budget.ts +13 -0
  407. package/src/worker/daemon-registry.ts +58 -0
  408. package/src/worker/daemon-relay-diagnostics.ts +127 -0
  409. package/src/worker/daemon-socket-attachment.ts +55 -0
  410. package/src/worker/daemon-sockets.ts +72 -62
  411. package/src/worker/daemon-status.ts +38 -0
  412. package/src/worker/device-session-verifier.ts +129 -0
  413. package/src/worker/dpop.ts +157 -0
  414. package/src/worker/durable-process-timeout.ts +29 -0
  415. package/src/worker/errors.ts +57 -0
  416. package/src/worker/http.ts +112 -26
  417. package/src/worker/index.ts +446 -440
  418. package/src/worker/mcp-access.ts +57 -0
  419. package/src/worker/mcp-controller.ts +188 -0
  420. package/src/worker/mcp-http-accept.ts +11 -0
  421. package/src/worker/mcp-http-contract.ts +322 -0
  422. package/src/worker/mcp-initialization-compat.ts +141 -0
  423. package/src/worker/mcp-jsonrpc.ts +11 -29
  424. package/src/worker/mcp-removed-protocol.ts +17 -0
  425. package/src/worker/mcp-response-proxy.ts +135 -0
  426. package/src/worker/mcp-response-stream.ts +65 -0
  427. package/src/worker/mcp-stale-schema-compat.ts +57 -0
  428. package/src/worker/mcp-stream-proxy-contract.ts +48 -0
  429. package/src/worker/mcp-tool-call-input.ts +23 -0
  430. package/src/worker/nonce-store.ts +33 -35
  431. package/src/worker/oauth-authorization-page.ts +21 -1
  432. package/src/worker/oauth-client-admin.ts +52 -0
  433. package/src/worker/oauth-client-contract.ts +38 -0
  434. package/src/worker/oauth-controller.ts +107 -47
  435. package/src/worker/oauth-field-contract.ts +31 -0
  436. package/src/worker/oauth-record-contract.ts +7 -0
  437. package/src/worker/oauth-refresh-authority.ts +27 -0
  438. package/src/worker/oauth-refresh-exchange.ts +162 -0
  439. package/src/worker/oauth-refresh-families.ts +130 -27
  440. package/src/worker/oauth-refresh-persistence.ts +116 -0
  441. package/src/worker/oauth-state.ts +33 -18
  442. package/src/worker/oauth-store-validation.ts +131 -0
  443. package/src/worker/oauth-token-derivation.ts +33 -0
  444. package/src/worker/oauth-token-issuance.ts +119 -0
  445. package/src/worker/oauth-tokens.ts +56 -190
  446. package/src/worker/observability.ts +38 -5
  447. package/src/worker/pending-admission.ts +15 -0
  448. package/src/worker/pending-call-capacity.ts +108 -0
  449. package/src/worker/pending-call-contract.ts +42 -5
  450. package/src/worker/pending-call-deadlines.ts +75 -0
  451. package/src/worker/pending-calls.ts +131 -125
  452. package/src/worker/runtime-alarm-storage.ts +37 -0
  453. package/src/worker/runtime-alarm.ts +136 -0
  454. package/src/worker/server-info-activity.ts +36 -0
  455. package/src/worker/server-info-tool-delivery.ts +15 -0
  456. package/src/worker/server-info.ts +168 -0
  457. package/src/worker/tool-catalog.ts +47 -1
  458. package/src/worker/tool-timeout.ts +75 -14
  459. package/src/worker/websocket-protocol.ts +26 -2
  460. package/src/worker/worker-edge-guard.ts +90 -0
  461. package/src/worker/worker-edge-log.ts +65 -0
  462. package/src/worker/worker-entry.ts +52 -0
  463. package/src/worker/worker-mcp-config.ts +23 -0
  464. package/src/worker/worker-metadata.ts +48 -0
  465. package/src/worker/worker-rate-limit-key.ts +30 -0
  466. package/src/worker/worker-runtime-config.ts +20 -0
  467. package/src/worker/worker-static-routes.ts +54 -0
  468. package/src/worker/worker-task-lifetime.ts +18 -0
  469. package/tsconfig.local.json +17 -1
  470. package/wrangler.jsonc +13 -0
  471. package/src/local/cli-approval.mjs +0 -117
  472. package/src/local/operation-state-lock.mjs +0 -92
  473. package/src/worker/mcp-session.ts +0 -72
@@ -1,8 +1,10 @@
1
1
  // @ts-check
2
-
3
2
  import { randomBytes } from "node:crypto";
4
3
  import { performance } from "node:perf_hooks";
5
4
  import { BridgeError } from "./errors.mjs";
5
+ import { assertCallCapacity, callCapacityConfig, callCapacitySnapshot } from "./call-capacity.mjs";
6
+ import { bindCallPrincipal, callIdsForAuthority } from "./call-authority.mjs";
7
+ import { assertCallRegistryOpen, cancelAllCallsAndWait, notifyCallRegistryChanged } from "./call-registry-drain.mjs";
6
8
 
7
9
  /** @typedef {ReturnType<typeof setTimeout>} TimerHandle */
8
10
  /**
@@ -15,11 +17,15 @@ import { BridgeError } from "./errors.mjs";
15
17
  * controller: AbortController,
16
18
  * cancelReason: string,
17
19
  * timer: TimerHandle | null,
20
+ * owner_kind?: string, owner_account_id?: string, owner_account_version?: number,
21
+ * owner_client_id?: string, owner_family_id?: string,
18
22
  * }} CallRecord
19
23
  */
20
24
  /**
21
25
  * @typedef {{
22
26
  * maximum?: unknown,
27
+ * reserved?: unknown,
28
+ * reservedTools?: Iterable<string>,
23
29
  * now?: () => number,
24
30
  * scheduler?: {setTimeout: typeof setTimeout, clearTimeout: typeof clearTimeout},
25
31
  * onCancel?: (record: CallRecord) => void,
@@ -34,7 +40,8 @@ import { BridgeError } from "./errors.mjs";
34
40
  export class CallRegistry {
35
41
  /** @param {CallRegistryOptions} [options] */
36
42
  constructor(options = {}) {
37
- this.maximum = positiveInteger(options.maximum, 16);
43
+ this.capacity = callCapacityConfig(options.maximum, options.reserved, options.reservedTools);
44
+ this.maximum = this.capacity.maximum;
38
45
  this.now = typeof options.now === "function" ? options.now : () => performance.now();
39
46
  this.scheduler = options.scheduler || { setTimeout, clearTimeout };
40
47
  this.onCancel = typeof options.onCancel === "function" ? options.onCancel : () => {};
@@ -45,18 +52,18 @@ export class CallRegistry {
45
52
 
46
53
  /** @param {OpenCallInput} [input] */
47
54
  open({ callId = "", tool = "", origin = "local", timeoutMs = 0 } = {}) {
55
+ assertCallRegistryOpen(this);
48
56
  const id = String(callId || `call_${randomBytes(16).toString("hex")}`);
57
+ const toolName = String(tool || "");
49
58
  if (this.calls.has(id)) throw new BridgeError("conflict", "duplicate in-flight call id");
50
- if (this.calls.size >= this.maximum) {
51
- throw new BridgeError("limit_exceeded", `too many concurrent tool calls (${this.maximum})`, { retryable: true });
52
- }
59
+ assertCallCapacity(this.calls, this.capacity, toolName);
53
60
  const controller = new AbortController();
54
61
  const startedAt = this.now();
55
62
  const timeout = positiveInteger(timeoutMs, 0);
56
63
  /** @type {CallRecord} */
57
64
  const record = {
58
65
  id,
59
- tool: String(tool || ""),
66
+ tool: toolName,
60
67
  origin: String(origin || "local"),
61
68
  startedAt,
62
69
  deadlineAt: timeout ? startedAt + timeout : null,
@@ -96,7 +103,7 @@ export class CallRegistry {
96
103
  if (!record) return false;
97
104
  if (record.timer) this.scheduler.clearTimeout(record.timer);
98
105
  this.calls.delete(id);
99
- this.onFinish(record);
106
+ try { this.onFinish(record); } finally { notifyCallRegistryChanged(this); }
100
107
  return true;
101
108
  }
102
109
 
@@ -117,12 +124,20 @@ export class CallRegistry {
117
124
  return cancelled;
118
125
  }
119
126
 
120
- /** @param {unknown} [reason] */
121
- cancelAll(reason = "runtime stopped") {
122
- for (const id of [...this.calls.keys()]) {
123
- this.cancel(id, reason);
124
- this.finish(id);
125
- }
127
+ /** @param {unknown} callId @param {unknown} principal */ bindPrincipal(callId, principal) {
128
+ return bindCallPrincipal(this.calls.get(String(callId || "")), principal);
129
+ }
130
+
131
+ /** @param {unknown} revocation */ cancelAuthority(revocation) {
132
+ let cancelled = 0;
133
+ for (const id of callIdsForAuthority(this.calls.values(), revocation))
134
+ if (this.cancel(id, "authority revoked", "authorization_denied")) cancelled += 1;
135
+ return cancelled;
136
+ }
137
+
138
+ /** @param {unknown} [reason] @param {number} [waitMs] */
139
+ async cancelAllAndWait(reason = "runtime stopped", waitMs = 5_000) {
140
+ await cancelAllCallsAndWait(this, reason, waitMs);
126
141
  }
127
142
 
128
143
  /** @param {unknown} callId */
@@ -151,7 +166,7 @@ export class CallRegistry {
151
166
  snapshot() {
152
167
  const now = this.now();
153
168
  /** @type {Record<string, number>} */
154
- const byOrigin = {};
169
+ const byOrigin = Object.create(null);
155
170
  let oldestMs = 0;
156
171
  for (const call of this.calls.values()) {
157
172
  byOrigin[call.origin] = (byOrigin[call.origin] || 0) + 1;
@@ -159,7 +174,7 @@ export class CallRegistry {
159
174
  }
160
175
  return {
161
176
  active: this.calls.size,
162
- maximum: this.maximum,
177
+ ...callCapacitySnapshot(this.calls, this.capacity),
163
178
  by_origin: byOrigin,
164
179
  oldest_ms: oldestMs,
165
180
  };
@@ -34,6 +34,11 @@ export class CapabilityObserver {
34
34
  matched_commands: Array.isArray(result?.command_matches) ? result.command_matches.length : 0,
35
35
  matched_applications: Array.isArray(result?.application_matches) ? result.application_matches.length : 0,
36
36
  recommended_tools: Array.isArray(result?.recommended_tools) ? [...result.recommended_tools] : [],
37
+ primary_route: result?.execution_routing?.primary_route?.id || null,
38
+ routing_ambiguity: result?.execution_routing?.ambiguity?.level || "none",
39
+ routing_score_gap: Number.isFinite(Number(result?.execution_routing?.ambiguity?.score_gap))
40
+ ? Number(result.execution_routing.ambiguity.score_gap)
41
+ : 0,
37
42
  refresh_fingerprint: String(result?.refresh?.fingerprint || ""),
38
43
  };
39
44
  }
@@ -67,9 +67,10 @@ export function recommendTools(task, { commandsAvailable, commandRelevant, skill
67
67
  const tools = ["agent_context"];
68
68
  if (skillRelevant) tools.push("load_local_skill");
69
69
  if (commandRelevant) tools.push("run_local_command");
70
- if (/browser|chrome|edge|brave|网页|浏览器|表单|网站/.test(lower)) tools.push("browser_status", "browser_list_tabs", "browser_manage_tabs", "browser_inspect_page", "browser_wait", "browser_action", "browser_fill_form");
71
- if (/app|application|gui|window|应用|软件|窗口|界面/.test(lower)) tools.push("list_local_applications", "inspect_local_application", "operate_local_application");
70
+ if (/browser|chrome|edge|brave|网页|浏览器|表单|网站/.test(lower)) tools.push("computer_observe", "computer_act", "browser_status", "browser_list_tabs", "browser_manage_tabs", "browser_inspect_page", "browser_wait", "browser_action", "browser_fill_form");
71
+ if (/app|application|gui|window|应用|软件|窗口|界面/.test(lower)) tools.push("computer_observe", "computer_act", "list_local_applications", "inspect_local_application", "operate_local_application");
72
72
  if (/git|commit|branch|diff|仓库|提交|分支/.test(lower)) tools.push("git_status", "git_diff");
73
+ if (/commit|提交/.test(lower)) tools.push("git_commit");
73
74
  if (/test|build|lint|command|terminal|测试|构建|命令|终端/.test(lower)) tools.push(commandsAvailable ? "run_local_command" : "run_process");
74
75
  if (/file|code|source|edit|write|文件|代码|源码|修改|写入/.test(lower)) tools.push("read_file", "search_text", "edit_file", "apply_patch");
75
76
  return [...new Set(tools)];
@@ -0,0 +1,132 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * @typedef {{
5
+ * onSettle?: (code: number | null, signal: string | null, source: "close" | "exit_fallback") => void,
6
+ * onFallback?: () => void,
7
+ * schedule?: (callback: () => void, delay: number) => unknown,
8
+ * clearSchedule?: (timer: unknown) => void,
9
+ * fallbackMs?: unknown,
10
+ * readExitState?: () => { code?: number | null, signal?: string | null },
11
+ * }} ChildProcessSettlementOptions
12
+ */
13
+
14
+ /** @param {ChildProcessSettlementOptions} [options] */
15
+ export function createChildProcessSettlement(options = {}) {
16
+ if (typeof options.onSettle !== "function") throw new TypeError("child settlement requires onSettle");
17
+ const onSettle = options.onSettle;
18
+ /** @type {(callback: () => void, delay: number) => unknown} */
19
+ const defaultSchedule = (callback, delay) => setTimeout(callback, delay);
20
+ /** @type {(timer: unknown) => void} */
21
+ const defaultClearSchedule = timer => clearTimeout(/** @type {ReturnType<typeof setTimeout>} */ (timer));
22
+ const schedule = typeof options.schedule === "function" ? options.schedule : defaultSchedule;
23
+ const clearSchedule = typeof options.clearSchedule === "function" ? options.clearSchedule : defaultClearSchedule;
24
+ const delay = boundedDelay(options.fallbackMs);
25
+ let settled = false;
26
+ /** @type {unknown} */
27
+ let timer = null;
28
+ let timerSet = false;
29
+
30
+ /**
31
+ * @param {number | null} code
32
+ * @param {string | null} signal
33
+ * @param {"close" | "exit_fallback"} source
34
+ */
35
+ function settle(code, signal, source) {
36
+ if (settled) return false;
37
+ settled = true;
38
+ if (timerSet) clearSchedule(timer);
39
+ timer = null;
40
+ timerSet = false;
41
+ onSettle(code, signal, source);
42
+ return true;
43
+ }
44
+
45
+ /** @param {number | null} code @param {string | null} signal */
46
+ function onClose(code, signal) { return settle(code, signal, "close"); }
47
+
48
+ /** @param {number | null} code @param {string | null} signal */
49
+ function onExit(code, signal) {
50
+ if (settled || timerSet) return false;
51
+ timerSet = true;
52
+ timer = schedule(() => {
53
+ timer = null;
54
+ timerSet = false;
55
+ options.onFallback?.();
56
+ const observed = safeExitState(options.readExitState);
57
+ const settledCode = typeof observed.code === "number" && Number.isInteger(observed.code) ? observed.code : code;
58
+ const settledSignal = observed.signal || signal;
59
+ settle(settledCode, settledSignal, "exit_fallback");
60
+ }, delay);
61
+ return true;
62
+ }
63
+
64
+ function cancel() {
65
+ if (settled) return false;
66
+ settled = true;
67
+ if (timerSet) clearSchedule(timer);
68
+ timer = null;
69
+ timerSet = false;
70
+ return true;
71
+ }
72
+
73
+ return Object.freeze({ onClose, onExit, cancel });
74
+ }
75
+
76
+ /**
77
+ * Attach the shared close-preferred settlement policy to one ChildProcess-like object.
78
+ * Residual stdio is destroyed only after exit has been observed and the bounded close grace expired.
79
+ * @param {any} child
80
+ * @param {ChildProcessSettlementOptions} options
81
+ */
82
+ export function attachChildProcessSettlement(child, options = {}) {
83
+ const settlement = createChildProcessSettlement({
84
+ ...options,
85
+ readExitState: options.readExitState || (() => ({ code: child?.exitCode, signal: child?.signalCode })),
86
+ onFallback() {
87
+ for (const stream of [child?.stdin, child?.stdout, child?.stderr]) {
88
+ try { stream?.destroy?.(); }
89
+ catch { /* The process has exited; residual stream destruction is bounded best-effort cleanup. */ }
90
+ }
91
+ try { child?.unref?.(); }
92
+ catch { /* Detachment is advisory after process exit and cannot change the settlement result. */ }
93
+ options.onFallback?.();
94
+ },
95
+ });
96
+ /** @param {number | null} code @param {string | null} signal */
97
+ const onExit = (code, signal) => settlement.onExit(code, signal);
98
+ /** @param {number | null} code @param {string | null} signal */
99
+ const onClose = (code, signal) => settlement.onClose(code, signal);
100
+ child.once("exit", onExit);
101
+ child.once("close", onClose);
102
+ return settlement;
103
+ }
104
+
105
+ /** @param {unknown} value */
106
+ function boundedDelay(value) {
107
+ if (value === undefined) return 1000;
108
+ const parsed = Number(value);
109
+ if (!Number.isFinite(parsed) || parsed < 0 || parsed > 10_000) {
110
+ throw new TypeError("child settlement fallback must be between 0 and 10000 milliseconds");
111
+ }
112
+ return Math.floor(parsed);
113
+ }
114
+ export function childExitedBeforeTimeout({ exitCode = null, signalCode = null, processState = "unknown" } = {}) {
115
+ return Number.isInteger(exitCode) || Boolean(signalCode) || processState === "zombie";
116
+ }
117
+
118
+ /** @param {ChildProcessSettlementOptions["readExitState"]} reader @returns {{ code: number | null, signal: string | null }} */
119
+ function safeExitState(reader) {
120
+ if (typeof reader !== "function") return { code: null, signal: null };
121
+ try {
122
+ const value = reader();
123
+ const code = value?.code;
124
+ const signal = value?.signal;
125
+ return {
126
+ code: typeof code === "number" && Number.isInteger(code) ? code : null,
127
+ signal: typeof signal === "string" && signal ? signal : null,
128
+ };
129
+ } catch {
130
+ return { code: null, signal: null };
131
+ }
132
+ }
@@ -1,5 +1,7 @@
1
1
  import { AccountAdminClient, accountRoleNames, generateAccountPassword } from "./account-admin.mjs";
2
+ import { createDeviceSessionForRoot } from "./device-root-provider.mjs";
2
3
  import { loadState } from "./state.mjs";
4
+ import { packageVersion } from "./package-identity.mjs";
3
5
 
4
6
  export function createAccountCommand({ chooseWorkspace, confirm }) {
5
7
  if (typeof chooseWorkspace !== "function" || typeof confirm !== "function") {
@@ -8,7 +10,7 @@ export function createAccountCommand({ chooseWorkspace, confirm }) {
8
10
  return async function accountCommand(args) {
9
11
  const workspace = await chooseWorkspace({ ...args, _: [] }, { promptOnFirstRun: false, save: false, allowPositional: false });
10
12
  const state = loadState(workspace, { stateDir: args.stateDir });
11
- const client = accountAdminClient(state);
13
+ const client = await accountAdminClient(state);
12
14
  const action = String(args._[0] || "list").toLowerCase();
13
15
  const result = await performAccountAction({ action, args, client, confirm });
14
16
  if (result === null) return;
@@ -17,15 +19,36 @@ export function createAccountCommand({ chooseWorkspace, confirm }) {
17
19
  };
18
20
  }
19
21
 
20
- export function accountAdminClient(state) {
21
- if (!state.worker?.url || !state.worker?.accountAdminSecret) {
22
+ export async function accountAdminClient(state, sessionIdentity = null) {
23
+ if (!state.worker?.url || !state.worker?.deviceIdentity) {
22
24
  throw new Error("account administration requires a deployed Worker; run machine-mcp first");
23
25
  }
24
- return new AccountAdminClient({ workerUrl: state.worker.url, adminSecret: state.worker.accountAdminSecret });
26
+ const identity = sessionIdentity || await createDeviceSessionForRoot(
27
+ state.worker.deviceIdentity,
28
+ state.worker.url,
29
+ "machine-bridge-mcp",
30
+ currentPackageVersion(),
31
+ { profileDir: state.paths.profileDir, reason: "Authorize Machine Bridge account administration" },
32
+ );
33
+ return new AccountAdminClient({ workerUrl: state.worker.url, sessionIdentity: identity });
34
+ }
35
+
36
+ function currentPackageVersion() {
37
+ return packageVersion;
25
38
  }
26
39
 
27
40
  async function performAccountAction({ action, args, client, confirm }) {
28
41
  if (action === "list") return client.list();
42
+ if (action === "clients") return client.listClients();
43
+ if (action === "revoke-client") {
44
+ const clientId = String(args._[1] || "");
45
+ if (!clientId) throw new Error("account revoke-client requires CLIENT_ID");
46
+ if (!args.yes && !(await confirm(`Revoke OAuth client ${clientId}?`, false))) {
47
+ console.log("Client revocation cancelled.");
48
+ return null;
49
+ }
50
+ return client.removeClient({ clientId });
51
+ }
29
52
  if (action === "add") {
30
53
  const name = args._[1];
31
54
  const role = args._[2];
@@ -56,10 +79,21 @@ async function performAccountAction({ action, args, client, confirm }) {
56
79
  }
57
80
  return client.remove({ accountId: account.account_id });
58
81
  }
59
- throw new Error("Unknown account action. Use list, add, role, enable, disable, rotate-password, or remove.");
82
+ throw new Error("Unknown account action. Use list, clients, revoke-client, add, role, enable, disable, rotate-password, or remove.");
60
83
  }
61
84
 
62
85
  function printAccountResult(action, result) {
86
+ if (action === "clients") {
87
+ if (!result.clients.length) {
88
+ console.log("No OAuth clients configured.");
89
+ return;
90
+ }
91
+ for (const client of result.clients) {
92
+ const registration = client.registration_current === true ? "current" : "stale";
93
+ console.log(`${client.client_name}\t${client.trusted_role || "untrusted"}\t${client.client_id}\taccess=${client.active_access_tokens} refresh=${client.active_refresh_tokens} registration=${registration}`);
94
+ }
95
+ return;
96
+ }
63
97
  if (action === "list") {
64
98
  if (!result.accounts.length) {
65
99
  console.log("No accounts configured.");
@@ -75,5 +109,5 @@ function printAccountResult(action, result) {
75
109
  process.stdout.write(`Password: ${result.password}\n`);
76
110
  console.log("Save this password now; it is not stored locally or shown again. Do not share this terminal output.");
77
111
  }
78
- if (result.removed) console.log("Account removed.");
112
+ if (result.removed) console.log(action === "revoke-client" ? "OAuth client revoked." : "Account removed.");
79
113
  }
@@ -0,0 +1,162 @@
1
+ import { acquireDaemonLockWithTakeover, inspectWorkspaceDaemon } from "./daemon-process.mjs";
2
+ import { effectiveLogFormat, effectiveLogLevel } from "./cli-options.mjs";
3
+ import { createLogger } from "./log.mjs";
4
+ import { activatePersistentRuntime } from "./runtime-activation.mjs";
5
+ import { autostartStatus, installAutostart, startAutostart, stopAutostart } from "./service.mjs";
6
+ import { startOwnedServiceRuntime } from "./service-runtime.mjs";
7
+ import { acquireMachineServiceLockWithWait, acquireStartupLockWithWait, daemonLockPathForState, loadState, readDaemonLockOwner } from "./state.mjs";
8
+ import { workerHealth } from "./worker-health.mjs";
9
+
10
+ export function createActivateCommand({
11
+ chooseWorkspace,
12
+ prepareRemoteState,
13
+ createRemoteRuntime,
14
+ currentPackageVersion,
15
+ assertNodeVersion,
16
+ structuredLogger,
17
+ } = {}) {
18
+ for (const [name, value] of Object.entries({
19
+ chooseWorkspace,
20
+ prepareRemoteState,
21
+ createRemoteRuntime,
22
+ currentPackageVersion,
23
+ assertNodeVersion,
24
+ structuredLogger,
25
+ })) {
26
+ if (typeof value !== "function") throw new TypeError(`activate command requires ${name}`);
27
+ }
28
+
29
+ return async function activateCommand(args) {
30
+ assertNodeVersion();
31
+ const logger = createLogger({
32
+ level: args.json ? "error" : effectiveLogLevel(args),
33
+ format: effectiveLogFormat(args),
34
+ component: "activate",
35
+ });
36
+ const workspace = await chooseWorkspace(args, { promptOnFirstRun: false, save: true, allowPositional: true });
37
+ let state = loadState(workspace, { stateDir: args.stateDir });
38
+ if (!state.worker?.url) {
39
+ throw new Error("activate requires an existing deployment; run machine-mcp once interactively before persistent activation");
40
+ }
41
+ const activationArgs = { ...args, noAutostart: true, daemonOnly: false };
42
+ const expectedVersion = currentPackageVersion();
43
+ const result = await activatePersistentRuntime({
44
+ expectedVersion,
45
+ acquireStartupLock: async () => {
46
+ const lock = await acquireStartupLockWithWait(state, { operation: "activate", logger });
47
+ try {
48
+ state = loadState(workspace, { stateDir: args.stateDir });
49
+ if (!state.worker?.url) throw new Error("activate requires an existing deployment; run machine-mcp once interactively before persistent activation");
50
+ return lock;
51
+ } catch (error) {
52
+ lock.release();
53
+ throw error;
54
+ }
55
+ },
56
+ acquireServiceLock: () => acquireMachineServiceLockWithWait({ operation: "activate", logger }),
57
+ inspectActivationOwnership: async () => {
58
+ const daemon = inspectWorkspaceDaemon(state);
59
+ const owner = daemon.alive && daemon.verified_service_daemon
60
+ ? readDaemonLockOwner(daemonLockPathForState(state))
61
+ : null;
62
+ return {
63
+ daemon,
64
+ provider: await autostartStatus(),
65
+ previousRuntime: owner ? { version: owner.version, entryScript: owner.entryScript } : null,
66
+ };
67
+ },
68
+ stopAutostart: () => stopAutostart({ logger: structuredLogger(true) }),
69
+ acquireDaemonLock: () => acquireDaemonLockWithTakeover(state, {
70
+ takeOverServiceOwner: true,
71
+ ownerMetadata: { mode: "foreground", version: expectedVersion },
72
+ logger,
73
+ }),
74
+ prepareRemoteState: ({ onRemotePrepared } = {}) => prepareRemoteState({
75
+ args: activationArgs,
76
+ workspace,
77
+ state,
78
+ logger,
79
+ onRemotePrepared,
80
+ // Candidate activation requires an existing deployment. Do not repeat
81
+ // first-run account provisioning after Worker convergence: that extra
82
+ // admin-auth round trip can fail independently before relay readiness.
83
+ provisionInitialOwner: false,
84
+ }),
85
+ repairRemoteState: async ({ onRemotePrepared } = {}) => {
86
+ logger.warn("candidate device authentication was rejected; redeploying the same Worker once with the current device identity");
87
+ return prepareRemoteState({
88
+ args: { ...activationArgs, forceWorker: true, rotateSecrets: false },
89
+ workspace,
90
+ state,
91
+ logger,
92
+ onRemotePrepared,
93
+ provisionInitialOwner: false,
94
+ });
95
+ },
96
+ createRuntime: ({ daemonLock, readiness }) => createRemoteRuntime({
97
+ args: activationArgs,
98
+ workspace,
99
+ state,
100
+ daemonLock,
101
+ deviceSessionIdentity: readiness.deviceSessionIdentity,
102
+ exitOnTerminal: false,
103
+ }),
104
+ installAutostart: ({ deferOwnerCommit } = {}) => {
105
+ if (deferOwnerCommit !== true) throw new Error("persistent activation requires deferred service-owner commit");
106
+ return installAutostart({
107
+ workspace,
108
+ stateRoot: state.paths.stateRoot,
109
+ entryScript: process.argv[1],
110
+ version: expectedVersion,
111
+ logger: structuredLogger(true),
112
+ deferOwnerCommit: true,
113
+ });
114
+ },
115
+ startAutostart: ({ owner } = {}) => {
116
+ if (!owner || owner.status !== "pending") throw new Error("persistent activation requires the pending candidate service owner");
117
+ return startOwnedServiceRuntime({ logger: structuredLogger(true), owner });
118
+ },
119
+ startRecoveryAutostart: () => startAutostart({ logger: structuredLogger(true) }),
120
+ restorePreviousAutostart: () => startAutostart({ logger: structuredLogger(true) }),
121
+ inspectCandidateAutostart: () => inspectWorkspaceDaemon(state, {
122
+ expectedVersion, expectedEntryScript: process.argv[1],
123
+ }),
124
+ inspectPreviousAutostart: (identity) => inspectWorkspaceDaemon(state, {
125
+ expectedVersion: identity.version, expectedEntryScript: identity.entryScript,
126
+ }),
127
+ inspectDaemon: async () => inspectWorkspaceDaemon(state, {
128
+ expectedVersion, expectedEntryScript: process.argv[1],
129
+ }),
130
+ checkWorker: async () => workerHealth(state.worker.url, expectedVersion, { expectedWorkerName: state.worker.name }),
131
+ });
132
+ const convergence = result.convergence;
133
+ const payload = {
134
+ ok: true,
135
+ version: expectedVersion,
136
+ workspace: state.workspace.path,
137
+ worker: { name: state.worker.name, url: state.worker.url, health: convergence.worker },
138
+ daemon: convergence.daemon,
139
+ service: result.serviceStart,
140
+ candidate_relay_verified_before_handoff: result.candidateRelayVerified,
141
+ candidate_auth_recovery_redeployed: result.candidateRecoveryRedeployed,
142
+ activation_recovered: result.activationRecovered === true,
143
+ activation_recovery_reason: result.activationRecovered === true ? result.recoveryReason : null,
144
+ activation_recovery_detail: result.activationRecovered === true ? result.recoveryDetail : null,
145
+ };
146
+ if (args.json) console.log(JSON.stringify(payload, null, 2));
147
+ else {
148
+ if (result.activationRecovered === true) {
149
+ logger.warn(`persistent activation completed through verified candidate-service recovery (${result.recoveryReason}): ${result.recoveryDetail}`);
150
+ }
151
+ logger.success(`Persistent runtime activated: ${expectedVersion}`);
152
+ logger.safePlain(` Worker: ${state.worker.name}`);
153
+ logger.safePlain(` Daemon: pid ${convergence.daemon.pid}`);
154
+ logger.safePlain(" Candidate relay readiness was verified before the login service handoff.");
155
+ if (result.activationRecovered === true) {
156
+ logger.safePlain(" The exact candidate login service was independently verified after the foreground candidate ended.");
157
+ }
158
+ logger.safePlain(" The login service now owns the daemon; the activation terminal may close.");
159
+ }
160
+ return payload;
161
+ };
162
+ }
@@ -1,14 +1,15 @@
1
1
  import { spawn } from "node:child_process";
2
- import { existsSync } from "node:fs";
3
2
  import { join, resolve } from "node:path";
4
3
  import process from "node:process";
5
4
  import { createLogger } from "./log.mjs";
6
5
  import { inspectResourceFile, loadManagedJobPlan, ManagedJobManager, publicResourceRegistry, validateResourceName } from "./managed-jobs.mjs";
7
6
  import { generateRegisteredSshKey } from "./resource-operations.mjs";
8
- import { readBoundedRegularFileSync } from "./secure-file.mjs";
9
7
  import {
10
- acquireStartupLockWithWait, expandHome, loadState, ownerOnlyFile, packageRoot, saveState,
8
+ acquireStartupLockWithWait, defaultStateRoot, expandHome, loadState, saveState,
11
9
  } from "./state.mjs";
10
+ import { readBrowserPairing, readBrowserPairingPort } from "./browser-pairing-store.mjs";
11
+ import { startBrowserPairingLaunch } from "./browser-pairing-launch.mjs";
12
+ import { browserExtensionPathForRuntime } from "./browser-extension-path.mjs";
12
13
  import { resolvePolicy } from "./cli-policy.mjs";
13
14
  import { readLoopbackJson } from "./loopback-health.mjs";
14
15
 
@@ -18,7 +19,7 @@ export function createLocalAdminCommands(dependencies) {
18
19
  if (typeof chooseWorkspace !== "function" || typeof confirm !== "function") {
19
20
  throw new TypeError("local admin commands require chooseWorkspace and confirm dependencies");
20
21
  }
21
- const context = Object.freeze({ chooseWorkspace, confirm });
22
+ const context = Object.freeze({ chooseWorkspace, confirm, openExternal: dependencies.openExternal || openExternal });
22
23
  return Object.freeze({
23
24
  resourceCommand: (args) => resourceCommand(args, context),
24
25
  browserCommand: (args) => browserCommand(args, context),
@@ -197,7 +198,8 @@ async function browserCommand(args, dependencies) {
197
198
  }
198
199
 
199
200
  function browserPathAction(args) {
200
- const extensionPath = resolve(packageRoot, "browser-extension");
201
+ const stateRoot = resolve(expandHome(args.stateDir || defaultStateRoot()));
202
+ const extensionPath = browserExtensionPathForRuntime({ stateRoot });
201
203
  if (args.json) console.log(JSON.stringify({ extension_path: extensionPath }, null, 2));
202
204
  else console.log(extensionPath);
203
205
  }
@@ -207,46 +209,32 @@ async function browserStatusAction(args, { chooseWorkspace }) {
207
209
  renderBrowserStatus(context.result, args.json === true);
208
210
  }
209
211
 
210
- async function browserPairAction(args, { chooseWorkspace }) {
212
+ async function browserPairAction(args, { chooseWorkspace, openExternal: openTarget }) {
211
213
  const context = await browserCommandContext(args, chooseWorkspace);
212
214
  if (!context.result.running) throw new Error("browser bridge is not reachable; keep machine-mcp running and retry");
213
- await openExternal(context.pairingUrl);
215
+ const pairing = readBrowserPairing(context.stateRoot);
216
+ if (!pairing || pairing.port !== context.port) throw new Error("browser pairing state changed; retry setup");
217
+ const launch = await startBrowserPairingLaunch({ brokerPort: pairing.port, extensionToken: pairing.extensionToken });
218
+ try { await openTarget(launch.url); } catch (error) { launch.close(); throw error; }
214
219
  if (args.json) {
215
220
  console.log(JSON.stringify({ ...context.result, pairing_page_opened: true }, null, 2));
216
221
  return;
217
222
  }
218
223
  console.log(`Extension path: ${context.extensionPath}`);
219
224
  console.log("Load this directory in the Chromium profile you use every day; Machine Bridge does not install it into Playwright or a separate automation profile.");
220
- console.log("Enable Developer mode, choose Load unpacked, and reload the extension after each Machine Bridge upgrade.");
225
+ console.log("Enable Developer mode, choose Load unpacked once, and reload the same path after each Machine Bridge upgrade. Older local-candidate installs may need one Load unpacked migration to this stable path.");
221
226
  console.log(`Pairing page opened: ${context.pairingUrl}`);
222
227
  }
223
228
 
224
229
  async function browserCommandContext(args, chooseWorkspace) {
225
- const extensionPath = resolve(packageRoot, "browser-extension");
226
230
  const workspace = await chooseWorkspace({ ...args, _: [] }, { promptOnFirstRun: false, save: false, allowPositional: false });
227
231
  const state = loadState(workspace, { stateDir: args.stateDir });
228
- const pairingFile = join(state.paths.stateRoot, "browser-bridge.json");
229
- if (!existsSync(pairingFile)) {
230
- throw new Error("browser bridge is not initialized; start machine-mcp once, then run this command again");
231
- }
232
- ownerOnlyFile(pairingFile);
233
- const pairing = readBrowserPairingState(pairingFile);
234
- const pairingUrl = `http://127.0.0.1:${pairing.port}/pair`;
235
- const health = await readBrowserHealth(`http://127.0.0.1:${pairing.port}/healthz`);
236
- return { extensionPath, pairingUrl, result: browserStatusResult(health, extensionPath, pairingUrl) };
237
- }
238
-
239
- function readBrowserPairingState(pairingFile) {
240
- let pairing;
241
- try {
242
- pairing = JSON.parse(readBoundedRegularFileSync(pairingFile, 64 * 1024).toString("utf8"));
243
- } catch {
244
- throw new Error("browser bridge state is invalid; restart machine-mcp to repair it");
245
- }
246
- const port = Number(pairing.port);
247
- if (!/^[A-Za-z0-9_-]{32,100}$/.test(String(pairing.token || ""))) throw new Error("browser bridge state contains an invalid token");
248
- if (!Number.isInteger(port) || port < 1024 || port > 65535) throw new Error("browser bridge state contains an invalid port");
249
- return { port };
232
+ const extensionPath = browserExtensionPathForRuntime({ stateRoot: state.paths.stateRoot });
233
+ const port = readBrowserPairingPort(state.paths.stateRoot);
234
+ if (port === null) throw new Error("browser bridge is not initialized; start machine-mcp once, then run this command again");
235
+ const pairingUrl = `http://127.0.0.1:${port}/pair`;
236
+ const health = await readBrowserHealth(`http://127.0.0.1:${port}/healthz`);
237
+ return { extensionPath, pairingUrl, port, stateRoot: state.paths.stateRoot, result: browserStatusResult(health, extensionPath, pairingUrl) };
250
238
  }
251
239
 
252
240
  function readBrowserHealth(healthUrl) {
@@ -302,7 +290,7 @@ function openExternal(target) {
302
290
  });
303
291
  }
304
292
 
305
- async function jobCommand(args, { chooseWorkspace, confirm }) {
293
+ async function jobCommand(args, { chooseWorkspace }) {
306
294
  const action = String(args._[0] || "list").toLowerCase();
307
295
  const workspace = await chooseWorkspace({ ...args, _: [] }, { promptOnFirstRun: false, save: false, allowPositional: false });
308
296
  const state = loadState(workspace, { stateDir: args.stateDir });
@@ -320,19 +308,6 @@ async function jobCommand(args, { chooseWorkspace, confirm }) {
320
308
  else if (action === "read") result = manager.read({ job_id: args._[1] });
321
309
  else if (action === "inspect") result = manager.inspectLocal({ job_id: args._[1] });
322
310
  else if (action === "cancel") result = manager.cancel({ job_id: args._[1] });
323
- else if (action === "approve") {
324
- if (args.json && !args.yes) throw new Error("job approve --json requires --yes");
325
- const inspection = manager.inspectLocal({ job_id: args._[1] });
326
- if (!args.yes) {
327
- console.log(JSON.stringify(inspection, null, 2));
328
- const approved = await confirm(`Approve and execute managed job ${args._[1]}?`, false);
329
- if (!approved) {
330
- console.log("Managed job approval cancelled. Re-run with --yes after review to skip confirmation.");
331
- return;
332
- }
333
- }
334
- result = manager.approve({ job_id: args._[1] }, { localOperator: true });
335
- }
336
311
  else if (action === "submit") {
337
312
  const planPath = args._[1];
338
313
  if (!planPath) throw new Error("job submit requires a JSON plan file");