borgmcp 1.1.15 → 2.0.2

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 (466) hide show
  1. package/CONTRIBUTING.md +15 -0
  2. package/LICENSE +201 -33
  3. package/NOTICE +2 -0
  4. package/README.md +85 -36
  5. package/SECURITY.md +7 -0
  6. package/dist/agent-runtime.d.ts.map +1 -0
  7. package/dist/agent-runtime.js +63 -1
  8. package/dist/agent-runtime.js.map +1 -0
  9. package/dist/assimilate-cmd.d.ts +96 -17
  10. package/dist/assimilate-cmd.d.ts.map +1 -0
  11. package/dist/assimilate-cmd.js +1396 -48
  12. package/dist/assimilate-cmd.js.map +1 -0
  13. package/dist/assimilate-deps.d.ts.map +1 -0
  14. package/dist/assimilate-deps.js +384 -3
  15. package/dist/assimilate-deps.js.map +1 -0
  16. package/dist/assimilate-guard.d.ts +5 -7
  17. package/dist/assimilate-guard.d.ts.map +1 -0
  18. package/dist/assimilate-guard.js +60 -2
  19. package/dist/assimilate-guard.js.map +1 -0
  20. package/dist/assimilate-welcome.d.ts +1 -1
  21. package/dist/assimilate-welcome.d.ts.map +1 -0
  22. package/dist/assimilate-welcome.js +32 -2
  23. package/dist/assimilate-welcome.js.map +1 -0
  24. package/dist/auth-env.d.ts +7 -50
  25. package/dist/auth-env.d.ts.map +1 -0
  26. package/dist/auth-env.js +22 -1
  27. package/dist/auth-env.js.map +1 -0
  28. package/dist/backends/launch-all-pastelist.d.ts.map +1 -0
  29. package/dist/backends/launch-all-pastelist.js +11 -4
  30. package/dist/backends/launch-all-pastelist.js.map +1 -0
  31. package/dist/backends/launch-all-tmux.d.ts.map +1 -0
  32. package/dist/backends/launch-all-tmux.js +49 -1
  33. package/dist/backends/launch-all-tmux.js.map +1 -0
  34. package/dist/backends/launch-all-windows.d.ts.map +1 -0
  35. package/dist/backends/launch-all-windows.js +86 -12
  36. package/dist/backends/launch-all-windows.js.map +1 -0
  37. package/dist/bare-launch-menu.d.ts.map +1 -0
  38. package/dist/bare-launch-menu.js +83 -6
  39. package/dist/bare-launch-menu.js.map +1 -0
  40. package/dist/claude-launch-args.d.ts.map +1 -0
  41. package/dist/claude-launch-args.js +34 -1
  42. package/dist/claude-launch-args.js.map +1 -0
  43. package/dist/claude.d.ts.map +1 -0
  44. package/dist/claude.js +486 -14
  45. package/dist/claude.js.map +1 -0
  46. package/dist/cleanup-cmd.d.ts +5 -5
  47. package/dist/cleanup-cmd.d.ts.map +1 -0
  48. package/dist/cleanup-cmd.js +453 -14
  49. package/dist/cleanup-cmd.js.map +1 -0
  50. package/dist/clear-rewake-core.d.ts.map +1 -0
  51. package/dist/clear-rewake-core.js +23 -2
  52. package/dist/clear-rewake-core.js.map +1 -0
  53. package/dist/clear-rewake.d.ts.map +1 -0
  54. package/dist/clear-rewake.js +19 -1
  55. package/dist/clear-rewake.js.map +1 -0
  56. package/dist/cli-help.d.ts +5 -0
  57. package/dist/cli-help.d.ts.map +1 -0
  58. package/dist/cli-help.js +104 -56
  59. package/dist/cli-help.js.map +1 -0
  60. package/dist/cli-platform.d.ts.map +1 -0
  61. package/dist/cli-platform.js +106 -4
  62. package/dist/cli-platform.js.map +1 -0
  63. package/dist/cli-tool-approval.d.ts +84 -0
  64. package/dist/cli-tool-approval.d.ts.map +1 -0
  65. package/dist/cli-tool-approval.js +583 -0
  66. package/dist/cli-tool-approval.js.map +1 -0
  67. package/dist/codex-app-server.d.ts.map +1 -0
  68. package/dist/codex-app-server.js +228 -4
  69. package/dist/codex-app-server.js.map +1 -0
  70. package/dist/codex-app-wake.d.ts +9 -11
  71. package/dist/codex-app-wake.d.ts.map +1 -0
  72. package/dist/codex-app-wake.js +436 -2
  73. package/dist/codex-app-wake.js.map +1 -0
  74. package/dist/codex-launch.d.ts.map +1 -0
  75. package/dist/codex-launch.js +102 -2
  76. package/dist/codex-launch.js.map +1 -0
  77. package/dist/codex-remote.d.ts +6 -1
  78. package/dist/codex-remote.d.ts.map +1 -0
  79. package/dist/codex-remote.js +394 -1
  80. package/dist/codex-remote.js.map +1 -0
  81. package/dist/codex-wake-resolve.d.ts +4 -4
  82. package/dist/codex-wake-resolve.d.ts.map +1 -0
  83. package/dist/codex-wake-resolve.js +127 -1
  84. package/dist/codex-wake-resolve.js.map +1 -0
  85. package/dist/config-utils.d.ts +2 -2
  86. package/dist/config-utils.d.ts.map +1 -0
  87. package/dist/config-utils.js +766 -3
  88. package/dist/config-utils.js.map +1 -0
  89. package/dist/config.d.ts +98 -61
  90. package/dist/config.d.ts.map +1 -0
  91. package/dist/config.js +426 -1
  92. package/dist/config.js.map +1 -0
  93. package/dist/console-prefix.d.ts.map +1 -0
  94. package/dist/console-prefix.js +96 -1
  95. package/dist/console-prefix.js.map +1 -0
  96. package/dist/cube-name.d.ts.map +1 -0
  97. package/dist/cube-name.js +67 -1
  98. package/dist/cube-name.js.map +1 -0
  99. package/dist/cubes.d.ts +126 -18
  100. package/dist/cubes.d.ts.map +1 -0
  101. package/dist/cubes.js +412 -4
  102. package/dist/cubes.js.map +1 -0
  103. package/dist/debug.d.ts.map +1 -0
  104. package/dist/debug.js +71 -1
  105. package/dist/debug.js.map +1 -0
  106. package/dist/direct-log.d.ts.map +1 -0
  107. package/dist/direct-log.js +11 -1
  108. package/dist/direct-log.js.map +1 -0
  109. package/dist/docs-sections.d.ts +11 -10
  110. package/dist/docs-sections.d.ts.map +1 -0
  111. package/dist/docs-sections.js +113 -3
  112. package/dist/docs-sections.js.map +1 -0
  113. package/dist/drone-lifecycle.d.ts +5 -21
  114. package/dist/drone-lifecycle.d.ts.map +1 -0
  115. package/dist/drone-lifecycle.js +57 -8
  116. package/dist/drone-lifecycle.js.map +1 -0
  117. package/dist/drone-management.d.ts +29 -0
  118. package/dist/drone-management.d.ts.map +1 -0
  119. package/dist/drone-management.js +109 -0
  120. package/dist/drone-management.js.map +1 -0
  121. package/dist/ensure-mcp-config.d.ts.map +1 -0
  122. package/dist/ensure-mcp-config.js +36 -1
  123. package/dist/ensure-mcp-config.js.map +1 -0
  124. package/dist/evict-drone.d.ts +10 -2
  125. package/dist/evict-drone.d.ts.map +1 -0
  126. package/dist/evict-drone.js +60 -1
  127. package/dist/evict-drone.js.map +1 -0
  128. package/dist/gc-orphan-inboxes.d.ts.map +1 -0
  129. package/dist/gc-orphan-inboxes.js +177 -1
  130. package/dist/gc-orphan-inboxes.js.map +1 -0
  131. package/dist/inbox-monitor.d.ts +1 -1
  132. package/dist/inbox-monitor.d.ts.map +1 -0
  133. package/dist/inbox-monitor.js +856 -3
  134. package/dist/inbox-monitor.js.map +1 -0
  135. package/dist/index.d.ts +6 -4
  136. package/dist/index.d.ts.map +1 -0
  137. package/dist/index.js +1034 -37
  138. package/dist/index.js.map +1 -0
  139. package/dist/launch-all-cmd.d.ts.map +1 -0
  140. package/dist/launch-all-cmd.js +377 -37
  141. package/dist/launch-all-cmd.js.map +1 -0
  142. package/dist/launch-all-command.d.ts.map +1 -0
  143. package/dist/launch-all-command.js +24 -1
  144. package/dist/launch-all-command.js.map +1 -0
  145. package/dist/launch-all-deps.d.ts +3 -8
  146. package/dist/launch-all-deps.d.ts.map +1 -0
  147. package/dist/launch-all-deps.js +99 -1
  148. package/dist/launch-all-deps.js.map +1 -0
  149. package/dist/launch-all-discovery.d.ts +1 -0
  150. package/dist/launch-all-discovery.d.ts.map +1 -0
  151. package/dist/launch-all-discovery.js +90 -4
  152. package/dist/launch-all-discovery.js.map +1 -0
  153. package/dist/launch-all-locks.d.ts.map +1 -0
  154. package/dist/launch-all-locks.js +54 -1
  155. package/dist/launch-all-locks.js.map +1 -0
  156. package/dist/launch-gate.d.ts +2 -2
  157. package/dist/launch-gate.d.ts.map +1 -0
  158. package/dist/launch-gate.js +73 -1
  159. package/dist/launch-gate.js.map +1 -0
  160. package/dist/lifecycle-log-guard.d.ts.map +1 -0
  161. package/dist/lifecycle-log-guard.js +93 -2
  162. package/dist/lifecycle-log-guard.js.map +1 -0
  163. package/dist/list-roles-render.d.ts.map +1 -0
  164. package/dist/list-roles-render.js +40 -6
  165. package/dist/list-roles-render.js.map +1 -0
  166. package/dist/local-log-routing.d.ts +19 -0
  167. package/dist/local-log-routing.d.ts.map +1 -0
  168. package/dist/local-log-routing.js +67 -0
  169. package/dist/local-log-routing.js.map +1 -0
  170. package/dist/local-manage-tool-result.d.ts +8 -0
  171. package/dist/local-manage-tool-result.d.ts.map +1 -0
  172. package/dist/local-manage-tool-result.js +10 -0
  173. package/dist/local-manage-tool-result.js.map +1 -0
  174. package/dist/local-server-cursor.d.ts +34 -0
  175. package/dist/local-server-cursor.d.ts.map +1 -0
  176. package/dist/local-server-cursor.js +151 -0
  177. package/dist/local-server-cursor.js.map +1 -0
  178. package/dist/log-audit.d.ts.map +1 -0
  179. package/dist/log-audit.js +194 -3
  180. package/dist/log-audit.js.map +1 -0
  181. package/dist/log-stream.d.ts +35 -18
  182. package/dist/log-stream.d.ts.map +1 -0
  183. package/dist/log-stream.js +1270 -12
  184. package/dist/log-stream.js.map +1 -0
  185. package/dist/model-presets.d.ts.map +1 -0
  186. package/dist/model-presets.js +19 -1
  187. package/dist/model-presets.js.map +1 -0
  188. package/dist/name-validator.d.ts.map +1 -0
  189. package/dist/name-validator.js +22 -1
  190. package/dist/name-validator.js.map +1 -0
  191. package/dist/opencode-drone.d.ts.map +1 -0
  192. package/dist/opencode-drone.js +438 -4
  193. package/dist/opencode-drone.js.map +1 -0
  194. package/dist/opencode-plugin.d.ts +1 -1
  195. package/dist/opencode-plugin.d.ts.map +1 -0
  196. package/dist/opencode-plugin.js +29 -4
  197. package/dist/opencode-plugin.js.map +1 -0
  198. package/dist/parse-assimilate-args.d.ts +1 -1
  199. package/dist/parse-assimilate-args.d.ts.map +1 -0
  200. package/dist/parse-assimilate-args.js +125 -1
  201. package/dist/parse-assimilate-args.js.map +1 -0
  202. package/dist/parse-launch-all-args.d.ts.map +1 -0
  203. package/dist/parse-launch-all-args.js +70 -1
  204. package/dist/parse-launch-all-args.js.map +1 -0
  205. package/dist/postinstall-banner.d.ts.map +1 -0
  206. package/dist/postinstall-banner.js +27 -2
  207. package/dist/postinstall-banner.js.map +1 -0
  208. package/dist/postinstall.d.ts.map +1 -0
  209. package/dist/postinstall.js +32 -5
  210. package/dist/postinstall.js.map +1 -0
  211. package/dist/readiness-probe.d.ts.map +1 -0
  212. package/dist/readiness-probe.js +9 -1
  213. package/dist/readiness-probe.js.map +1 -0
  214. package/dist/regen-format.d.ts.map +1 -0
  215. package/dist/regen-format.js +545 -13
  216. package/dist/regen-format.js.map +1 -0
  217. package/dist/regen.d.ts.map +1 -0
  218. package/dist/regen.js +141 -5
  219. package/dist/regen.js.map +1 -0
  220. package/dist/remote-client.d.ts +88 -173
  221. package/dist/remote-client.d.ts.map +1 -0
  222. package/dist/remote-client.js +997 -1
  223. package/dist/remote-client.js.map +1 -0
  224. package/dist/reset-local-seat-cmd.d.ts +50 -0
  225. package/dist/reset-local-seat-cmd.d.ts.map +1 -0
  226. package/dist/reset-local-seat-cmd.js +169 -0
  227. package/dist/reset-local-seat-cmd.js.map +1 -0
  228. package/dist/role-match.d.ts.map +1 -0
  229. package/dist/role-match.js +23 -1
  230. package/dist/role-match.js.map +1 -0
  231. package/dist/role-resolver.d.ts.map +1 -0
  232. package/dist/role-resolver.js +60 -1
  233. package/dist/role-resolver.js.map +1 -0
  234. package/dist/roster-render.d.ts.map +1 -0
  235. package/dist/roster-render.js +121 -3
  236. package/dist/roster-render.js.map +1 -0
  237. package/dist/seat-probe.d.ts +32 -13
  238. package/dist/seat-probe.d.ts.map +1 -0
  239. package/dist/seat-probe.js +78 -1
  240. package/dist/seat-probe.js.map +1 -0
  241. package/dist/seat-store.d.ts +76 -0
  242. package/dist/seat-store.d.ts.map +1 -0
  243. package/dist/seat-store.js +309 -0
  244. package/dist/seat-store.js.map +1 -0
  245. package/dist/seats.d.ts +375 -0
  246. package/dist/seats.d.ts.map +1 -0
  247. package/dist/seats.js +670 -0
  248. package/dist/seats.js.map +1 -0
  249. package/dist/self-path.d.ts +27 -0
  250. package/dist/self-path.d.ts.map +1 -0
  251. package/dist/self-path.js +43 -0
  252. package/dist/self-path.js.map +1 -0
  253. package/dist/server-endpoint.d.ts +9 -0
  254. package/dist/server-endpoint.d.ts.map +1 -0
  255. package/dist/server-endpoint.js +38 -0
  256. package/dist/server-endpoint.js.map +1 -0
  257. package/dist/server-errors.d.ts +50 -0
  258. package/dist/server-errors.d.ts.map +1 -0
  259. package/dist/server-errors.js +79 -0
  260. package/dist/server-errors.js.map +1 -0
  261. package/dist/server-handshake.d.ts +197 -0
  262. package/dist/server-handshake.d.ts.map +1 -0
  263. package/dist/server-handshake.js +533 -0
  264. package/dist/server-handshake.js.map +1 -0
  265. package/dist/server-response.d.ts +3 -0
  266. package/dist/server-response.d.ts.map +1 -0
  267. package/dist/server-response.js +67 -0
  268. package/dist/server-response.js.map +1 -0
  269. package/dist/server-trust.d.ts +14 -0
  270. package/dist/server-trust.d.ts.map +1 -0
  271. package/dist/server-trust.js +225 -0
  272. package/dist/server-trust.js.map +1 -0
  273. package/dist/session-continuity.d.ts +33 -0
  274. package/dist/session-continuity.d.ts.map +1 -0
  275. package/dist/session-continuity.js +220 -0
  276. package/dist/session-continuity.js.map +1 -0
  277. package/dist/setup-confirm.d.ts +3 -3
  278. package/dist/setup-confirm.d.ts.map +1 -0
  279. package/dist/setup-confirm.js +126 -2
  280. package/dist/setup-confirm.js.map +1 -0
  281. package/dist/setup.d.ts +5 -3
  282. package/dist/setup.d.ts.map +1 -0
  283. package/dist/setup.js +169 -46
  284. package/dist/setup.js.map +1 -0
  285. package/dist/shell-escape.d.ts.map +1 -0
  286. package/dist/shell-escape.js +22 -1
  287. package/dist/shell-escape.js.map +1 -0
  288. package/dist/spawn.d.ts +2 -2
  289. package/dist/spawn.d.ts.map +1 -0
  290. package/dist/spawn.js +29 -10
  291. package/dist/spawn.js.map +1 -0
  292. package/dist/startup-services.d.ts +0 -1
  293. package/dist/startup-services.d.ts.map +1 -0
  294. package/dist/startup-services.js +26 -1
  295. package/dist/startup-services.js.map +1 -0
  296. package/dist/stream-owner.d.ts +6 -2
  297. package/dist/stream-owner.d.ts.map +1 -0
  298. package/dist/stream-owner.js +547 -2
  299. package/dist/stream-owner.js.map +1 -0
  300. package/dist/stream-status.d.ts +1 -1
  301. package/dist/stream-status.d.ts.map +1 -0
  302. package/dist/stream-status.js +268 -3
  303. package/dist/stream-status.js.map +1 -0
  304. package/dist/sync-roles-render.d.ts.map +1 -0
  305. package/dist/sync-roles-render.js +118 -3
  306. package/dist/sync-roles-render.js.map +1 -0
  307. package/dist/sync.d.ts +1 -1
  308. package/dist/sync.d.ts.map +1 -0
  309. package/dist/sync.js +286 -22
  310. package/dist/sync.js.map +1 -0
  311. package/dist/terminal-title.d.ts.map +1 -0
  312. package/dist/terminal-title.js +68 -1
  313. package/dist/terminal-title.js.map +1 -0
  314. package/dist/token-store.d.ts +17 -111
  315. package/dist/token-store.d.ts.map +1 -0
  316. package/dist/token-store.js +81 -1
  317. package/dist/token-store.js.map +1 -0
  318. package/dist/tool-manifest.d.ts +4 -8
  319. package/dist/tool-manifest.d.ts.map +1 -0
  320. package/dist/tool-manifest.js +554 -1
  321. package/dist/tool-manifest.js.map +1 -0
  322. package/dist/tool-scope.d.ts +8 -12
  323. package/dist/tool-scope.d.ts.map +1 -0
  324. package/dist/tool-scope.js +97 -1
  325. package/dist/tool-scope.js.map +1 -0
  326. package/dist/types.d.ts +1 -5
  327. package/dist/types.d.ts.map +1 -0
  328. package/dist/types.js +5 -0
  329. package/dist/types.js.map +1 -0
  330. package/dist/unknown-subcommand.d.ts +1 -1
  331. package/dist/unknown-subcommand.d.ts.map +1 -0
  332. package/dist/unknown-subcommand.js +37 -1
  333. package/dist/unknown-subcommand.js.map +1 -0
  334. package/dist/version.d.ts +1 -1
  335. package/dist/version.d.ts.map +1 -0
  336. package/dist/version.js +78 -2
  337. package/dist/version.js.map +1 -0
  338. package/dist/working-repo.d.ts.map +1 -0
  339. package/dist/working-repo.js +81 -1
  340. package/dist/working-repo.js.map +1 -0
  341. package/dist/worktree-lifecycle.d.ts.map +1 -0
  342. package/dist/worktree-lifecycle.js +205 -2
  343. package/dist/worktree-lifecycle.js.map +1 -0
  344. package/docs/EXTRACTION_PROVENANCE.md +42 -0
  345. package/docs/LOCAL_SERVER.md +103 -0
  346. package/docs/RELEASING.md +129 -0
  347. package/package.json +52 -34
  348. package/src/agent-runtime.ts +69 -0
  349. package/src/assimilate-cmd.ts +1864 -0
  350. package/src/assimilate-deps.ts +444 -0
  351. package/src/assimilate-guard.ts +84 -0
  352. package/src/assimilate-welcome.ts +39 -0
  353. package/src/auth-env.ts +21 -0
  354. package/src/backends/launch-all-pastelist.ts +18 -0
  355. package/src/backends/launch-all-tmux.ts +75 -0
  356. package/src/backends/launch-all-windows.ts +110 -0
  357. package/src/bare-launch-menu.ts +148 -0
  358. package/src/claude-launch-args.ts +35 -0
  359. package/src/claude.ts +561 -0
  360. package/src/cleanup-cmd.ts +581 -0
  361. package/src/clear-rewake-core.ts +34 -0
  362. package/src/clear-rewake.ts +18 -0
  363. package/src/cli-help.ts +116 -0
  364. package/src/cli-platform.ts +127 -0
  365. package/src/cli-tool-approval.ts +716 -0
  366. package/src/codex-app-server.ts +238 -0
  367. package/src/codex-app-wake.ts +524 -0
  368. package/src/codex-launch.ts +140 -0
  369. package/src/codex-remote.ts +500 -0
  370. package/src/codex-wake-resolve.ts +155 -0
  371. package/src/config-utils.ts +811 -0
  372. package/src/config.ts +595 -0
  373. package/src/console-prefix.ts +99 -0
  374. package/src/cube-name.ts +69 -0
  375. package/src/cubes.ts +583 -0
  376. package/src/debug.ts +73 -0
  377. package/src/direct-log.ts +9 -0
  378. package/src/docs-sections.ts +129 -0
  379. package/src/drone-lifecycle.ts +62 -0
  380. package/src/drone-management.ts +162 -0
  381. package/src/ensure-mcp-config.ts +54 -0
  382. package/src/evict-drone.ts +82 -0
  383. package/src/gc-orphan-inboxes.ts +229 -0
  384. package/src/inbox-monitor.ts +1005 -0
  385. package/src/index.ts +1201 -0
  386. package/src/launch-all-cmd.ts +450 -0
  387. package/src/launch-all-command.ts +30 -0
  388. package/src/launch-all-deps.ts +188 -0
  389. package/src/launch-all-discovery.ts +118 -0
  390. package/src/launch-all-locks.ts +74 -0
  391. package/src/launch-gate.ts +80 -0
  392. package/src/lifecycle-log-guard.ts +144 -0
  393. package/src/list-roles-render.ts +52 -0
  394. package/src/local-log-routing.ts +106 -0
  395. package/src/local-manage-tool-result.ts +10 -0
  396. package/src/local-server-cursor.ts +188 -0
  397. package/src/log-audit.ts +207 -0
  398. package/src/log-stream.ts +1567 -0
  399. package/src/model-presets.ts +22 -0
  400. package/src/name-validator.ts +23 -0
  401. package/src/opencode-drone.ts +528 -0
  402. package/src/opencode-plugin.ts +38 -0
  403. package/src/parse-assimilate-args.ts +125 -0
  404. package/src/parse-launch-all-args.ts +100 -0
  405. package/src/postinstall-banner.ts +37 -0
  406. package/src/postinstall.ts +36 -0
  407. package/src/readiness-probe.ts +12 -0
  408. package/src/regen-format.ts +651 -0
  409. package/src/regen.ts +150 -0
  410. package/src/remote-client.ts +1457 -0
  411. package/src/reset-local-seat-cmd.ts +227 -0
  412. package/src/role-match.ts +29 -0
  413. package/src/role-resolver.ts +93 -0
  414. package/src/roster-render.ts +196 -0
  415. package/src/seat-probe.ts +115 -0
  416. package/src/seat-store.ts +333 -0
  417. package/src/seats.ts +911 -0
  418. package/src/self-path.ts +51 -0
  419. package/src/server-endpoint.ts +38 -0
  420. package/src/server-errors.ts +95 -0
  421. package/src/server-handshake.ts +816 -0
  422. package/src/server-response.ts +76 -0
  423. package/src/server-trust.ts +245 -0
  424. package/src/session-continuity.ts +280 -0
  425. package/src/setup-confirm.ts +171 -0
  426. package/src/setup.ts +186 -0
  427. package/src/shell-escape.ts +22 -0
  428. package/src/spawn.ts +31 -0
  429. package/src/startup-services.ts +36 -0
  430. package/src/stream-owner.ts +686 -0
  431. package/src/stream-status.ts +337 -0
  432. package/src/sync-roles-render.ts +163 -0
  433. package/src/sync.ts +363 -0
  434. package/src/terminal-title.ts +76 -0
  435. package/src/token-store.ts +102 -0
  436. package/src/tool-manifest.ts +610 -0
  437. package/src/tool-scope.ts +110 -0
  438. package/src/types.ts +5 -0
  439. package/src/unknown-subcommand.ts +35 -0
  440. package/src/version.ts +82 -0
  441. package/src/working-repo.ts +99 -0
  442. package/src/worktree-lifecycle.ts +254 -0
  443. package/dist/auth-recovery.d.ts +0 -33
  444. package/dist/auth-recovery.js +0 -1
  445. package/dist/auth.d.ts +0 -103
  446. package/dist/auth.js +0 -39
  447. package/dist/device-auth.d.ts +0 -75
  448. package/dist/device-auth.js +0 -1
  449. package/dist/drone-address.d.ts +0 -26
  450. package/dist/drone-address.js +0 -1
  451. package/dist/get-started.d.ts +0 -20
  452. package/dist/get-started.js +0 -2
  453. package/dist/health-beat.d.ts +0 -134
  454. package/dist/health-beat.js +0 -1
  455. package/dist/role-section.d.ts +0 -110
  456. package/dist/role-section.js +0 -8
  457. package/dist/setup-action.d.ts +0 -20
  458. package/dist/setup-action.js +0 -1
  459. package/dist/stale-version-check.d.ts +0 -40
  460. package/dist/stale-version-check.js +0 -1
  461. package/dist/subscription-retry.d.ts +0 -40
  462. package/dist/subscription-retry.js +0 -1
  463. package/dist/templates.d.ts +0 -107
  464. package/dist/templates.js +0 -359
  465. package/dist/token-crypto.d.ts +0 -50
  466. package/dist/token-crypto.js +0 -1
@@ -1 +1,78 @@
1
- import{whoami as n}from"./remote-client.js";import{DroneEvictedError as o,DroneFrozenError as i}from"./drone-lifecycle.js";async function c(e,t){try{return await n(e,t),"live"}catch(r){return r instanceof o?"evicted":r instanceof i?"frozen":"indeterminate"}}export{c as defaultProbeSeat};
1
+ // Shared per-seat liveness probe (gh#877 reuse).
2
+ //
3
+ // Extracted from cleanup-cmd.ts so BOTH `borg cleanup` (destructive → fail-SAFE)
4
+ // and `borg launch-all` (constructive → fail-OPEN) can classify a saved seat by
5
+ // ITS OWN token, without launch-all having to import cleanup-cmd's chalk/report
6
+ // graph. cleanup-cmd re-exports `SeatStatus` for backwards compatibility.
7
+ import { whoami } from './remote-client.js';
8
+ import { DroneEvictedError } from './drone-lifecycle.js';
9
+ import { BorgServerError, BorgServerHttpError, BorgServerTrustError, BorgServerUnreachableError, } from './server-errors.js';
10
+ // Stable transport-level errno / undici codes (a CODE check, not error-text). A
11
+ // pinned-CA TLS failure and a refused/reset/timeout all surface one of these.
12
+ const TRANSPORT_ERRNOS = new Set([
13
+ 'ECONNREFUSED',
14
+ 'ECONNRESET',
15
+ 'ENOTFOUND',
16
+ 'EHOSTUNREACH',
17
+ 'ENETUNREACH',
18
+ 'ETIMEDOUT',
19
+ 'EAI_AGAIN',
20
+ 'EPIPE',
21
+ 'UND_ERR_CONNECT_TIMEOUT',
22
+ 'UND_ERR_HEADERS_TIMEOUT',
23
+ 'UND_ERR_SOCKET',
24
+ 'ABORT_ERR',
25
+ ]);
26
+ function isTransportFailure(err) {
27
+ if (err instanceof BorgServerUnreachableError)
28
+ return true;
29
+ const e = err;
30
+ if (e?.name === 'AbortError')
31
+ return true;
32
+ const code = e?.code ?? e?.cause?.code;
33
+ return typeof code === 'string' && TRANSPORT_ERRNOS.has(code);
34
+ }
35
+ /**
36
+ * Default seat probe: a lightweight drone-authed `whoami` with the seat's OWN
37
+ * saved token. authedFetch throws TYPED errors on the authoritative outcomes
38
+ * (410→DroneEvictedError; pin-matched typed 401→BorgServerError('SESSION_REJECTED');
39
+ * every other 401→BorgServerError('CREDENTIAL_REJECTED'); a pinned-identity mismatch
40
+ * →BorgServerTrustError; a non-ok status→BorgServerHttpError(status); a transport
41
+ * failure/timeout→BorgServerUnreachableError). Each maps to a STABLE typed verdict
42
+ * so recovery copy is cause-accurate; the cleanup path must NEVER delete on anything
43
+ * but `evicted`; the launch path treats every non-`evicted`/non-terminal cause as
44
+ * fail-OPEN.
45
+ */
46
+ export async function defaultProbeSeat(sessionToken, apiUrl, serverTrustIdentity) {
47
+ try {
48
+ await whoami(sessionToken, apiUrl, serverTrustIdentity);
49
+ return 'live';
50
+ }
51
+ catch (err) {
52
+ if (err instanceof DroneEvictedError)
53
+ return 'evicted';
54
+ // A pinned-identity / CA mismatch is a TERMINAL trust verdict — classified from
55
+ // the error TYPE, never from message text (the security boundary).
56
+ if (err instanceof BorgServerTrustError)
57
+ return 'trust-mismatch';
58
+ if (err instanceof BorgServerError) {
59
+ if (err.code === 'SESSION_REJECTED')
60
+ return 'rejected';
61
+ // Every non-SESSION 401 (bare/untyped or a different typed code) is a
62
+ // credential rejection — non-destructive, distinct from a takeover.
63
+ if (err.code === 'CREDENTIAL_REJECTED')
64
+ return 'credential-rejected';
65
+ }
66
+ if (err instanceof BorgServerHttpError) {
67
+ if (err.status === 404)
68
+ return 'endpoint-mismatch';
69
+ if (err.status >= 500 && err.status <= 599)
70
+ return 'server-failure';
71
+ return 'indeterminate';
72
+ }
73
+ if (isTransportFailure(err))
74
+ return 'unreachable';
75
+ return 'indeterminate';
76
+ }
77
+ }
78
+ //# sourceMappingURL=seat-probe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seat-probe.js","sourceRoot":"","sources":["../src/seat-probe.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,EAAE;AACF,iFAAiF;AACjF,gFAAgF;AAChF,gFAAgF;AAChF,0EAA0E;AAE1E,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,0BAA0B,GAC3B,MAAM,oBAAoB,CAAC;AAqC5B,gFAAgF;AAChF,8EAA8E;AAC9E,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC;IAC/B,cAAc;IACd,YAAY;IACZ,WAAW;IACX,cAAc;IACd,aAAa;IACb,WAAW;IACX,WAAW;IACX,OAAO;IACP,yBAAyB;IACzB,yBAAyB;IACzB,gBAAgB;IAChB,WAAW;CACZ,CAAC,CAAC;AAEH,SAAS,kBAAkB,CAAC,GAAY;IACtC,IAAI,GAAG,YAAY,0BAA0B;QAAE,OAAO,IAAI,CAAC;IAC3D,MAAM,CAAC,GAAG,GAAkE,CAAC;IAC7E,IAAI,CAAC,EAAE,IAAI,KAAK,YAAY;QAAE,OAAO,IAAI,CAAC;IAC1C,MAAM,IAAI,GAAG,CAAC,EAAE,IAAI,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC;IACvC,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAChE,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,YAAoB,EACpB,MAAc,EACd,mBAA4B;IAE5B,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,mBAAmB,CAAC,CAAC;QACxD,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,iBAAiB;YAAE,OAAO,SAAS,CAAC;QACvD,gFAAgF;QAChF,mEAAmE;QACnE,IAAI,GAAG,YAAY,oBAAoB;YAAE,OAAO,gBAAgB,CAAC;QACjE,IAAI,GAAG,YAAY,eAAe,EAAE,CAAC;YACnC,IAAI,GAAG,CAAC,IAAI,KAAK,kBAAkB;gBAAE,OAAO,UAAU,CAAC;YACvD,sEAAsE;YACtE,oEAAoE;YACpE,IAAI,GAAG,CAAC,IAAI,KAAK,qBAAqB;gBAAE,OAAO,qBAAqB,CAAC;QACvE,CAAC;QACD,IAAI,GAAG,YAAY,mBAAmB,EAAE,CAAC;YACvC,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;gBAAE,OAAO,mBAAmB,CAAC;YACnD,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG;gBAAE,OAAO,gBAAgB,CAAC;YACpE,OAAO,eAAe,CAAC;QACzB,CAAC;QACD,IAAI,kBAAkB,CAAC,GAAG,CAAC;YAAE,OAAO,aAAa,CAAC;QAClD,OAAO,eAAe,CAAC;IACzB,CAAC;AACH,CAAC"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * 0600 flocked atomic file store (Queen rescope: replaces the OS keychain).
3
+ *
4
+ * This is the SINGLE storage primitive the collapsed seat/credential model sits
5
+ * on. It provides one advisory lock plus an atomic 0600 read-compare-write so
6
+ * that a record and everything that must stay consistent with it are written as
7
+ * ONE atomic unit (a temp-write + rename either lands wholly or not at all).
8
+ *
9
+ * SR-seven file-store checklist (fb0e446d) implemented here:
10
+ * 1. 0600 AT CREATE — the temp file is opened O_CREAT|O_EXCL mode 0600, and
11
+ * rename preserves the mode, so the secret is never world/group-readable for
12
+ * any window (no write-then-chmod).
13
+ * 2. ATOMIC RENAME — temp in the SAME dir/fs, created 0600, fsync THEN rename;
14
+ * a crash never leaves a torn/readable partial, and the temp is cleaned up
15
+ * on any write failure.
16
+ * 3. PARENT DIR 0700 — created with mode 0700.
17
+ * 4. flock DISCIPLINE — a single advisory lock (O_EXCL lockfile), RULED option
18
+ * (b) (Coordinator cca6957a): NO automatic reclaim, EVER. Acquire is an atomic
19
+ * `open(lockPath,'wx',0o600)`; the whole read-compare-write runs inside ONE
20
+ * continuous hold, released on EVERY path (finally) by unlinking our OWN lock.
21
+ * A pre-existing lock held by a LIVE pid is waited on (bounded) then reported
22
+ * transient-busy; a lock whose recorded pid is DEAD (or whose payload is
23
+ * missing/unparseable) FAILS CLOSED naming the exact lockfile path — Borg NEVER
24
+ * steals or auto-deletes it (rename-claim reclaim is rejected: pathname
25
+ * substitution). No per-account locks, no TOCTOU between acquire and commit.
26
+ *
27
+ * The raw secret rests only in the 0600 file (parity with the server's TLS keys);
28
+ * this module never logs it, and the digest-only observation discipline of the
29
+ * callers keeps the raw bearer from leaving the store owner.
30
+ */
31
+ /**
32
+ * Atomic 0600 write (checklist #1 + #2): write to a same-dir temp opened
33
+ * O_CREAT|O_EXCL at mode 0600 (never write-then-chmod), fsync the data, then
34
+ * rename over the target (atomic; the mode carries across). A failed write
35
+ * cleans up the temp so no leftover file ever holds the secret.
36
+ */
37
+ export declare function atomicWrite0600(filePath: string, data: string): Promise<void>;
38
+ /**
39
+ * Read the store file, or null when it does not exist (ONLY the missing-file
40
+ * no-op path initializes empty). When the file exists, the 0600-store + 0700-parent
41
+ * perms are enforced BEFORE the bytes are read (CR#2) — a loosely-permissioned
42
+ * secret fails closed and is never read.
43
+ */
44
+ export declare function readStoreFile(filePath: string): Promise<string | null>;
45
+ /**
46
+ * Acquire the single advisory lock (checklist #4, RULED option b), run `op`, and
47
+ * release it on EVERY path (finally) by unlinking OUR OWN lock. Acquire is an atomic
48
+ * `open(lockPath,'wx',0o600)`. On EEXIST the lock is held:
49
+ * - holder PID ALIVE → bounded wait/retry (attempts×waitMs), then throw the truthful
50
+ * transient 'Borg seat store is busy' error;
51
+ * - holder PID DEAD, or the payload is missing/unparseable → FAIL CLOSED naming the
52
+ * exact lockfile path + the recorded dead pid/start-time. Borg NEVER auto-deletes
53
+ * or steals it (no reclaim, no rename-claim). The operator clears it by hand only
54
+ * after confirming no borg process is running.
55
+ * Because nothing ever steals a held lock, a plain unlink of our own lock on release
56
+ * is safe (no successor can be holding it).
57
+ */
58
+ export declare function withStoreLock<T>(lockPath: string, op: () => Promise<T>, opts?: {
59
+ attempts?: number;
60
+ waitMs?: number;
61
+ }): Promise<T>;
62
+ /** A locked, in-memory transaction over one store file. */
63
+ export interface StoreTxn<S> {
64
+ /** The mutable in-memory state, loaded under the lock. Mutate then commit(). */
65
+ data: S;
66
+ /** Atomically persist `data` (0600) — the single-write commit of the RCW. */
67
+ commit(): Promise<void>;
68
+ }
69
+ /**
70
+ * Run a read-compare-write transaction over `storePath` under the single store
71
+ * lock: load the current state (or an empty one), hand the caller a mutable view
72
+ * + an atomic commit, and release the lock on every path. The entire read →
73
+ * compare → (mutate) → commit happens inside ONE lock hold — no TOCTOU.
74
+ */
75
+ export declare function withStore<S, T>(storePath: string, emptyState: () => S, parse: (raw: string) => S | null, op: (txn: StoreTxn<S>) => Promise<T>): Promise<T>;
76
+ //# sourceMappingURL=seat-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seat-store.d.ts","sourceRoot":"","sources":["../src/seat-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AA+FH;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBnF;AAkCD;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAe5E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,aAAa,CAAC,CAAC,EACnC,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAChD,OAAO,CAAC,CAAC,CAAC,CAwDZ;AAED,2DAA2D;AAC3D,MAAM,WAAW,QAAQ,CAAC,CAAC;IACzB,gFAAgF;IAChF,IAAI,EAAE,CAAC,CAAC;IACR,6EAA6E;IAC7E,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAAE,CAAC,EAClC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,CAAC,EACnB,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,CAAC,GAAG,IAAI,EAChC,EAAE,EAAE,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GACnC,OAAO,CAAC,CAAC,CAAC,CAgCZ"}
@@ -0,0 +1,309 @@
1
+ /**
2
+ * 0600 flocked atomic file store (Queen rescope: replaces the OS keychain).
3
+ *
4
+ * This is the SINGLE storage primitive the collapsed seat/credential model sits
5
+ * on. It provides one advisory lock plus an atomic 0600 read-compare-write so
6
+ * that a record and everything that must stay consistent with it are written as
7
+ * ONE atomic unit (a temp-write + rename either lands wholly or not at all).
8
+ *
9
+ * SR-seven file-store checklist (fb0e446d) implemented here:
10
+ * 1. 0600 AT CREATE — the temp file is opened O_CREAT|O_EXCL mode 0600, and
11
+ * rename preserves the mode, so the secret is never world/group-readable for
12
+ * any window (no write-then-chmod).
13
+ * 2. ATOMIC RENAME — temp in the SAME dir/fs, created 0600, fsync THEN rename;
14
+ * a crash never leaves a torn/readable partial, and the temp is cleaned up
15
+ * on any write failure.
16
+ * 3. PARENT DIR 0700 — created with mode 0700.
17
+ * 4. flock DISCIPLINE — a single advisory lock (O_EXCL lockfile), RULED option
18
+ * (b) (Coordinator cca6957a): NO automatic reclaim, EVER. Acquire is an atomic
19
+ * `open(lockPath,'wx',0o600)`; the whole read-compare-write runs inside ONE
20
+ * continuous hold, released on EVERY path (finally) by unlinking our OWN lock.
21
+ * A pre-existing lock held by a LIVE pid is waited on (bounded) then reported
22
+ * transient-busy; a lock whose recorded pid is DEAD (or whose payload is
23
+ * missing/unparseable) FAILS CLOSED naming the exact lockfile path — Borg NEVER
24
+ * steals or auto-deletes it (rename-claim reclaim is rejected: pathname
25
+ * substitution). No per-account locks, no TOCTOU between acquire and commit.
26
+ *
27
+ * The raw secret rests only in the 0600 file (parity with the server's TLS keys);
28
+ * this module never logs it, and the digest-only observation discipline of the
29
+ * callers keeps the raw bearer from leaving the store owner.
30
+ */
31
+ import { open, link, mkdir, readFile, rename, stat, unlink } from 'node:fs/promises';
32
+ import { dirname } from 'node:path';
33
+ import { randomBytes } from 'node:crypto';
34
+ const LOCK_WAIT_MS = 10;
35
+ const LOCK_ATTEMPTS = 500;
36
+ /**
37
+ * Liveness check for the alive/dead branch (RULED option b). `process.kill(pid, 0)`
38
+ * sends no signal but validates the target: ESRCH ⇒ no such process (DEAD → fail
39
+ * closed); EPERM ⇒ the process exists but is owned by another user (ALIVE → wait).
40
+ * Any success ⇒ alive. This decides ONLY whether to wait vs. fail closed — it never
41
+ * authorizes a reclaim.
42
+ */
43
+ function isProcessAlive(pid) {
44
+ if (!Number.isInteger(pid) || pid <= 0)
45
+ return false;
46
+ try {
47
+ process.kill(pid, 0);
48
+ return true;
49
+ }
50
+ catch (err) {
51
+ return err.code === 'EPERM';
52
+ }
53
+ }
54
+ /** Approximate wall-clock process start time (Date.now() − uptime). */
55
+ function processStartTime() {
56
+ return new Date(Date.now() - Math.round(process.uptime() * 1000)).toISOString();
57
+ }
58
+ function parseLockPayload(raw) {
59
+ try {
60
+ const parsed = JSON.parse(raw);
61
+ if (typeof parsed.pid === 'number' && Number.isInteger(parsed.pid) && parsed.pid > 0) {
62
+ return {
63
+ pid: parsed.pid,
64
+ startTime: typeof parsed.startTime === 'string' ? parsed.startTime : 'unknown',
65
+ };
66
+ }
67
+ }
68
+ catch {
69
+ /* unparseable — treated as a corrupt/foreign lock by the caller (fail closed) */
70
+ }
71
+ return null;
72
+ }
73
+ /**
74
+ * The fail-closed stale-lock error (RULED option b). Names the EXACT lockfile path
75
+ * plus the recorded dead pid / start time, and tells the operator to delete that
76
+ * file ONLY if no borg process is running. Borg never removes it automatically.
77
+ */
78
+ function staleLockError(lockPath, held) {
79
+ const who = held
80
+ ? `its recorded owner process (pid ${held.pid}, started ${held.startTime}) is no longer running`
81
+ : 'its lock file is missing a valid owner identity or is corrupt';
82
+ return new Error(`Borg seat store lock file ${lockPath} is stale: ${who}. ` +
83
+ 'Borg will NOT remove it automatically. If no borg process is running on this ' +
84
+ `machine, delete ${lockPath} and retry; otherwise wait for the other borg process to finish.`);
85
+ }
86
+ /**
87
+ * Ensure the store's parent directory exists at 0700 (checklist #3) AND repair-or-
88
+ * refuse a loose pre-existing one (CR3c). `mkdir(mode)` does NOT tighten an already-
89
+ * existing directory, so a pre-existing world/group-traversable parent would leave
90
+ * the 0600 credential-at-rest reachable. Fail CLOSED rather than write a secret
91
+ * beneath it.
92
+ */
93
+ async function ensureParentDir(filePath) {
94
+ const dir = dirname(filePath);
95
+ let existing = null;
96
+ try {
97
+ existing = await stat(dir);
98
+ }
99
+ catch (err) {
100
+ if (err.code !== 'ENOENT')
101
+ throw err;
102
+ }
103
+ if (existing) {
104
+ if ((existing.mode & 0o777) !== 0o700) {
105
+ throw new Error(`Borg store directory ${dir} has insecure permissions ` +
106
+ `(0${(existing.mode & 0o777).toString(8)}, expected 0700); refusing to write a credential under it`);
107
+ }
108
+ return;
109
+ }
110
+ await mkdir(dir, { recursive: true, mode: 0o700 });
111
+ }
112
+ /**
113
+ * Atomic 0600 write (checklist #1 + #2): write to a same-dir temp opened
114
+ * O_CREAT|O_EXCL at mode 0600 (never write-then-chmod), fsync the data, then
115
+ * rename over the target (atomic; the mode carries across). A failed write
116
+ * cleans up the temp so no leftover file ever holds the secret.
117
+ */
118
+ export async function atomicWrite0600(filePath, data) {
119
+ await ensureParentDir(filePath);
120
+ const tmp = `${filePath}.${process.pid}.${randomBytes(6).toString('hex')}.tmp`;
121
+ const handle = await open(tmp, 'wx', 0o600);
122
+ try {
123
+ await handle.writeFile(data);
124
+ await handle.sync();
125
+ }
126
+ catch (err) {
127
+ await handle.close().catch(() => { });
128
+ await unlink(tmp).catch(() => { });
129
+ throw err;
130
+ }
131
+ await handle.close();
132
+ try {
133
+ await rename(tmp, filePath);
134
+ }
135
+ catch (err) {
136
+ await unlink(tmp).catch(() => { });
137
+ throw err;
138
+ }
139
+ }
140
+ /**
141
+ * CR#2: enforce the at-rest perms on the READ / no-op paths too (not just
142
+ * atomicWrite). A secret must NEVER be read from a group/other-accessible store
143
+ * file, nor from under a group/other-traversable parent. Fail CLOSED without
144
+ * reading the bytes. Mirrors the server-trust.ts `(mode & 0o077) !== 0` idiom.
145
+ */
146
+ async function assertSecureStorePerms(filePath, fileMode) {
147
+ if ((fileMode & 0o077) !== 0) {
148
+ throw new Error(`Borg seat store file ${filePath} has insecure permissions ` +
149
+ `(0${(fileMode & 0o777).toString(8)}, expected 0600); refusing to read a credential from it`);
150
+ }
151
+ const dir = dirname(filePath);
152
+ let dirStat;
153
+ try {
154
+ dirStat = await stat(dir);
155
+ }
156
+ catch (err) {
157
+ if (err.code === 'ENOENT')
158
+ return; // vanished — readFile handles
159
+ throw err;
160
+ }
161
+ if ((dirStat.mode & 0o077) !== 0) {
162
+ throw new Error(`Borg seat store directory ${dir} has insecure permissions ` +
163
+ `(0${(dirStat.mode & 0o777).toString(8)}, expected 0700); refusing to read a credential under it`);
164
+ }
165
+ }
166
+ /**
167
+ * Read the store file, or null when it does not exist (ONLY the missing-file
168
+ * no-op path initializes empty). When the file exists, the 0600-store + 0700-parent
169
+ * perms are enforced BEFORE the bytes are read (CR#2) — a loosely-permissioned
170
+ * secret fails closed and is never read.
171
+ */
172
+ export async function readStoreFile(filePath) {
173
+ let fileStat;
174
+ try {
175
+ fileStat = await stat(filePath);
176
+ }
177
+ catch (err) {
178
+ if (err.code === 'ENOENT')
179
+ return null;
180
+ throw err;
181
+ }
182
+ await assertSecureStorePerms(filePath, fileStat.mode);
183
+ try {
184
+ return await readFile(filePath, 'utf8');
185
+ }
186
+ catch (err) {
187
+ if (err.code === 'ENOENT')
188
+ return null;
189
+ throw err;
190
+ }
191
+ }
192
+ /**
193
+ * Acquire the single advisory lock (checklist #4, RULED option b), run `op`, and
194
+ * release it on EVERY path (finally) by unlinking OUR OWN lock. Acquire is an atomic
195
+ * `open(lockPath,'wx',0o600)`. On EEXIST the lock is held:
196
+ * - holder PID ALIVE → bounded wait/retry (attempts×waitMs), then throw the truthful
197
+ * transient 'Borg seat store is busy' error;
198
+ * - holder PID DEAD, or the payload is missing/unparseable → FAIL CLOSED naming the
199
+ * exact lockfile path + the recorded dead pid/start-time. Borg NEVER auto-deletes
200
+ * or steals it (no reclaim, no rename-claim). The operator clears it by hand only
201
+ * after confirming no borg process is running.
202
+ * Because nothing ever steals a held lock, a plain unlink of our own lock on release
203
+ * is safe (no successor can be holding it).
204
+ */
205
+ export async function withStoreLock(lockPath, op, opts = {}) {
206
+ const attempts = opts.attempts ?? LOCK_ATTEMPTS;
207
+ const waitMs = opts.waitMs ?? LOCK_WAIT_MS;
208
+ await mkdir(dirname(lockPath), { recursive: true, mode: 0o700 });
209
+ const myPayload = JSON.stringify({ pid: process.pid, startTime: processStartTime() });
210
+ // Stage the FULLY-WRITTEN payload in a same-dir temp, then acquire by atomically
211
+ // hard-linking it into place. `link` is atomic (EEXIST when the lock is held), and
212
+ // because the target appears already carrying the complete payload there is NO
213
+ // empty creation window — a concurrent contender never misreads a just-created
214
+ // lock as a corrupt/missing payload (which under option (b) would wrongly fail
215
+ // closed). A plain `open('wx')` create-then-write leaves exactly that window.
216
+ const tmp = `${lockPath}.${process.pid}.${randomBytes(6).toString('hex')}.acq`;
217
+ const staged = await open(tmp, 'wx', 0o600);
218
+ try {
219
+ await staged.writeFile(myPayload);
220
+ }
221
+ finally {
222
+ await staged.close();
223
+ }
224
+ try {
225
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
226
+ try {
227
+ await link(tmp, lockPath);
228
+ }
229
+ catch (err) {
230
+ if (err.code !== 'EEXIST')
231
+ throw err;
232
+ // The lock is held. Inspect the holder — but NEVER reclaim/steal it.
233
+ let raw;
234
+ try {
235
+ raw = await readFile(lockPath, 'utf8');
236
+ }
237
+ catch (readErr) {
238
+ if (readErr.code === 'ENOENT')
239
+ continue; // released — retry
240
+ throw readErr;
241
+ }
242
+ const held = parseLockPayload(raw);
243
+ if (held && isProcessAlive(held.pid)) {
244
+ // A live-but-slow holder keeps its lock: wait a bounded interval and retry.
245
+ await new Promise((resolvePromise) => setTimeout(resolvePromise, waitMs));
246
+ continue;
247
+ }
248
+ // Dead recorded holder, or a missing/unparseable payload → fail closed. The
249
+ // operator must confirm no borg process is running and delete the file by hand.
250
+ throw staleLockError(lockPath, held);
251
+ }
252
+ // We own the lock (it already carries our payload from the staged temp).
253
+ try {
254
+ return await op();
255
+ }
256
+ finally {
257
+ // No reclaim exists, so nobody can be holding our lock — a plain unlink is safe.
258
+ await unlink(lockPath).catch((e) => {
259
+ if (e.code !== 'ENOENT')
260
+ throw e;
261
+ });
262
+ }
263
+ }
264
+ throw new Error('Borg seat store is busy');
265
+ }
266
+ finally {
267
+ await unlink(tmp).catch(() => { });
268
+ }
269
+ }
270
+ /**
271
+ * Run a read-compare-write transaction over `storePath` under the single store
272
+ * lock: load the current state (or an empty one), hand the caller a mutable view
273
+ * + an atomic commit, and release the lock on every path. The entire read →
274
+ * compare → (mutate) → commit happens inside ONE lock hold — no TOCTOU.
275
+ */
276
+ export async function withStore(storePath, emptyState, parse, op) {
277
+ return withStoreLock(`${storePath}.lock`, async () => {
278
+ const raw = await readStoreFile(storePath);
279
+ // CR4 fail-closed: ONLY a missing file (ENOENT → readStoreFile returns null)
280
+ // may initialize an empty state. A present-but-malformed / wrong-version /
281
+ // schema-invalid store must NEVER be silently mapped to empty and then
282
+ // OVERWRITTEN by a subsequent commit — that erases every seat/credential and
283
+ // silently recreates authority. Throw WITHOUT writing so the corrupt bytes are
284
+ // preserved for recovery (parity with getServerCredentialRecord).
285
+ let data;
286
+ if (raw === null) {
287
+ data = emptyState();
288
+ }
289
+ else {
290
+ let loaded;
291
+ try {
292
+ loaded = parse(raw);
293
+ }
294
+ catch {
295
+ loaded = null;
296
+ }
297
+ if (loaded === null) {
298
+ throw new Error('Borg seat store is malformed or has an unsupported version; refusing to overwrite it');
299
+ }
300
+ data = loaded;
301
+ }
302
+ const txn = {
303
+ data,
304
+ commit: () => atomicWrite0600(storePath, JSON.stringify(data, null, 2) + '\n'),
305
+ };
306
+ return op(txn);
307
+ });
308
+ }
309
+ //# sourceMappingURL=seat-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seat-store.js","sourceRoot":"","sources":["../src/seat-store.ts"],"names":[],"mappings":"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"}