toolroll 0.5.0

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 (626) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1113 -0
  3. package/THIRD_PARTY_NOTICES.md +449 -0
  4. package/dist/accent-colors.d.ts +48 -0
  5. package/dist/accent-colors.js +122 -0
  6. package/dist/action-ledger.d.ts +26 -0
  7. package/dist/action-ledger.js +75 -0
  8. package/dist/agent-fence.d.ts +42 -0
  9. package/dist/agent-fence.js +183 -0
  10. package/dist/agentconfig.d.ts +216 -0
  11. package/dist/agentconfig.js +454 -0
  12. package/dist/api-tokens.d.ts +16 -0
  13. package/dist/api-tokens.js +27 -0
  14. package/dist/approval-policy.d.ts +69 -0
  15. package/dist/approval-policy.js +137 -0
  16. package/dist/approval-rules-ui.d.ts +25 -0
  17. package/dist/approval-rules-ui.js +39 -0
  18. package/dist/assignment-adapters.d.ts +180 -0
  19. package/dist/assignment-adapters.js +239 -0
  20. package/dist/assignment-brief.d.ts +71 -0
  21. package/dist/assignment-brief.js +129 -0
  22. package/dist/assignment-delivery.d.ts +56 -0
  23. package/dist/assignment-delivery.js +160 -0
  24. package/dist/assignment-presentation.d.ts +26 -0
  25. package/dist/assignment-presentation.js +80 -0
  26. package/dist/assignment-status.d.ts +62 -0
  27. package/dist/assignment-status.js +152 -0
  28. package/dist/assignment-ui.d.ts +71 -0
  29. package/dist/assignment-ui.js +103 -0
  30. package/dist/assignment.d.ts +222 -0
  31. package/dist/assignment.js +399 -0
  32. package/dist/attest.d.ts +56 -0
  33. package/dist/attest.js +153 -0
  34. package/dist/backend.d.ts +97 -0
  35. package/dist/backend.js +166 -0
  36. package/dist/backup-ui.d.ts +22 -0
  37. package/dist/backup-ui.js +59 -0
  38. package/dist/backup.d.ts +84 -0
  39. package/dist/backup.js +421 -0
  40. package/dist/beads.d.ts +34 -0
  41. package/dist/beads.js +135 -0
  42. package/dist/bin.d.ts +2 -0
  43. package/dist/bin.js +22 -0
  44. package/dist/board.d.ts +150 -0
  45. package/dist/board.js +210 -0
  46. package/dist/boot-identity.d.ts +63 -0
  47. package/dist/boot-identity.js +99 -0
  48. package/dist/browser/THIRD_PARTY_NOTICES.txt +2295 -0
  49. package/dist/browser/workspace.css +4 -0
  50. package/dist/browser/workspace.js +225 -0
  51. package/dist/browser-crew.d.ts +11 -0
  52. package/dist/browser-crew.js +50 -0
  53. package/dist/browser-shell.d.ts +8 -0
  54. package/dist/browser-shell.js +60 -0
  55. package/dist/browser-workspace.d.ts +780 -0
  56. package/dist/browser-workspace.js +47 -0
  57. package/dist/budget-alerts.d.ts +19 -0
  58. package/dist/budget-alerts.js +46 -0
  59. package/dist/builder.d.ts +370 -0
  60. package/dist/builder.js +3262 -0
  61. package/dist/capscan.d.ts +35 -0
  62. package/dist/capscan.js +113 -0
  63. package/dist/chat-acceptance.d.ts +71 -0
  64. package/dist/chat-acceptance.js +120 -0
  65. package/dist/chat-actions.d.ts +265 -0
  66. package/dist/chat-actions.js +1273 -0
  67. package/dist/chat-channel.d.ts +109 -0
  68. package/dist/chat-channel.js +652 -0
  69. package/dist/chat-continuity.d.ts +32 -0
  70. package/dist/chat-continuity.js +317 -0
  71. package/dist/chat-controls.d.ts +113 -0
  72. package/dist/chat-controls.js +53 -0
  73. package/dist/chat-delivery-state.d.ts +159 -0
  74. package/dist/chat-delivery-state.js +309 -0
  75. package/dist/chat-delivery.d.ts +38 -0
  76. package/dist/chat-delivery.js +693 -0
  77. package/dist/chat-display.d.ts +1 -0
  78. package/dist/chat-display.js +18 -0
  79. package/dist/chat-evidence.d.ts +123 -0
  80. package/dist/chat-evidence.js +207 -0
  81. package/dist/chat-flow.d.ts +44 -0
  82. package/dist/chat-flow.js +215 -0
  83. package/dist/chat-inbox.d.ts +57 -0
  84. package/dist/chat-inbox.js +123 -0
  85. package/dist/chat-polish.d.ts +16 -0
  86. package/dist/chat-polish.js +135 -0
  87. package/dist/chat-review.d.ts +46 -0
  88. package/dist/chat-review.js +98 -0
  89. package/dist/chat-rooms.d.ts +123 -0
  90. package/dist/chat-rooms.js +216 -0
  91. package/dist/chat-task-actions.d.ts +44 -0
  92. package/dist/chat-task-actions.js +79 -0
  93. package/dist/check-progress.d.ts +51 -0
  94. package/dist/check-progress.js +290 -0
  95. package/dist/child-database.d.ts +12 -0
  96. package/dist/child-database.js +30 -0
  97. package/dist/claim.d.ts +688 -0
  98. package/dist/claim.js +1740 -0
  99. package/dist/cli.d.ts +137 -0
  100. package/dist/cli.js +1461 -0
  101. package/dist/codex-limits.d.ts +15 -0
  102. package/dist/codex-limits.js +145 -0
  103. package/dist/coding-context.d.ts +55 -0
  104. package/dist/coding-context.js +92 -0
  105. package/dist/coding-handoff.d.ts +57 -0
  106. package/dist/coding-handoff.js +277 -0
  107. package/dist/coding-provider.d.ts +72 -0
  108. package/dist/coding-provider.js +424 -0
  109. package/dist/coding-shipping-ui.d.ts +4 -0
  110. package/dist/coding-shipping-ui.js +12 -0
  111. package/dist/coding-types.d.ts +62 -0
  112. package/dist/coding-types.js +1 -0
  113. package/dist/coding-ui.d.ts +17 -0
  114. package/dist/coding-ui.js +364 -0
  115. package/dist/coding-update.d.ts +15 -0
  116. package/dist/coding-update.js +149 -0
  117. package/dist/coding-workspace.d.ts +124 -0
  118. package/dist/coding-workspace.js +768 -0
  119. package/dist/container-state.d.ts +5 -0
  120. package/dist/container-state.js +62 -0
  121. package/dist/containment.d.ts +197 -0
  122. package/dist/containment.js +559 -0
  123. package/dist/contest.d.ts +318 -0
  124. package/dist/contest.js +753 -0
  125. package/dist/control-setup.d.ts +35 -0
  126. package/dist/control-setup.js +40 -0
  127. package/dist/control-ui.d.ts +29 -0
  128. package/dist/control-ui.js +53 -0
  129. package/dist/controller-service.d.ts +1 -0
  130. package/dist/controller-service.js +19 -0
  131. package/dist/controller-supervisor.d.ts +30 -0
  132. package/dist/controller-supervisor.js +104 -0
  133. package/dist/converse.d.ts +304 -0
  134. package/dist/converse.js +849 -0
  135. package/dist/coordinator-proposals.d.ts +29 -0
  136. package/dist/coordinator-proposals.js +137 -0
  137. package/dist/coordinator.d.ts +186 -0
  138. package/dist/coordinator.js +447 -0
  139. package/dist/credentials-ui.d.ts +25 -0
  140. package/dist/credentials-ui.js +48 -0
  141. package/dist/daemon.d.ts +209 -0
  142. package/dist/daemon.js +604 -0
  143. package/dist/decision.d.ts +120 -0
  144. package/dist/decision.js +388 -0
  145. package/dist/demo.d.ts +55 -0
  146. package/dist/demo.js +992 -0
  147. package/dist/desktop-access.d.ts +28 -0
  148. package/dist/desktop-access.js +88 -0
  149. package/dist/desktop-bundle.d.ts +16 -0
  150. package/dist/desktop-bundle.js +74 -0
  151. package/dist/desktop-host.d.ts +54 -0
  152. package/dist/desktop-host.js +508 -0
  153. package/dist/desktop-update-gate.d.ts +12 -0
  154. package/dist/desktop-update-gate.js +91 -0
  155. package/dist/desktop-update-recovery.d.ts +14 -0
  156. package/dist/desktop-update-recovery.js +243 -0
  157. package/dist/desktop-update.d.ts +110 -0
  158. package/dist/desktop-update.js +740 -0
  159. package/dist/discord-api.d.ts +20 -0
  160. package/dist/discord-api.js +138 -0
  161. package/dist/discord-chat.d.ts +16 -0
  162. package/dist/discord-chat.js +381 -0
  163. package/dist/discord-settings.d.ts +7 -0
  164. package/dist/discord-settings.js +75 -0
  165. package/dist/discord.d.ts +10 -0
  166. package/dist/discord.js +169 -0
  167. package/dist/discover.d.ts +75 -0
  168. package/dist/discover.js +150 -0
  169. package/dist/dispatch.d.ts +100 -0
  170. package/dist/dispatch.js +311 -0
  171. package/dist/dispose.d.ts +161 -0
  172. package/dist/dispose.js +803 -0
  173. package/dist/email-settings.d.ts +34 -0
  174. package/dist/email-settings.js +50 -0
  175. package/dist/envelope.d.ts +47 -0
  176. package/dist/envelope.js +105 -0
  177. package/dist/evidence-pack.d.ts +185 -0
  178. package/dist/evidence-pack.js +258 -0
  179. package/dist/evidence.d.ts +345 -0
  180. package/dist/evidence.js +782 -0
  181. package/dist/exec.d.ts +285 -0
  182. package/dist/exec.js +1841 -0
  183. package/dist/exhaustion.d.ts +85 -0
  184. package/dist/exhaustion.js +141 -0
  185. package/dist/export-ui.d.ts +4 -0
  186. package/dist/export-ui.js +17 -0
  187. package/dist/export.d.ts +48 -0
  188. package/dist/export.js +305 -0
  189. package/dist/flow-actions.d.ts +77 -0
  190. package/dist/flow-actions.js +203 -0
  191. package/dist/flow-code.d.ts +50 -0
  192. package/dist/flow-code.js +159 -0
  193. package/dist/flow-draft.d.ts +38 -0
  194. package/dist/flow-draft.js +80 -0
  195. package/dist/flow-engine.d.ts +66 -0
  196. package/dist/flow-engine.js +416 -0
  197. package/dist/flow-insights.d.ts +95 -0
  198. package/dist/flow-insights.js +149 -0
  199. package/dist/flow-live.d.ts +24 -0
  200. package/dist/flow-live.js +150 -0
  201. package/dist/flow-people.d.ts +24 -0
  202. package/dist/flow-people.js +91 -0
  203. package/dist/flow-replies.d.ts +48 -0
  204. package/dist/flow-replies.js +113 -0
  205. package/dist/flow-scripts.d.ts +38 -0
  206. package/dist/flow-scripts.js +73 -0
  207. package/dist/flow-secrets.d.ts +13 -0
  208. package/dist/flow-secrets.js +45 -0
  209. package/dist/flow-sort.d.ts +82 -0
  210. package/dist/flow-sort.js +153 -0
  211. package/dist/flow-steps.d.ts +40 -0
  212. package/dist/flow-steps.js +408 -0
  213. package/dist/flow-triggers.d.ts +244 -0
  214. package/dist/flow-triggers.js +959 -0
  215. package/dist/flows-ui.d.ts +20 -0
  216. package/dist/flows-ui.js +175 -0
  217. package/dist/flows.d.ts +229 -0
  218. package/dist/flows.js +968 -0
  219. package/dist/fonts.d.ts +19 -0
  220. package/dist/fonts.js +19 -0
  221. package/dist/gaps.d.ts +35 -0
  222. package/dist/gaps.js +101 -0
  223. package/dist/gate-failure.d.ts +40 -0
  224. package/dist/gate-failure.js +66 -0
  225. package/dist/git.d.ts +54 -0
  226. package/dist/git.js +94 -0
  227. package/dist/google-mail.d.ts +58 -0
  228. package/dist/google-mail.js +151 -0
  229. package/dist/grant.d.ts +136 -0
  230. package/dist/grant.js +238 -0
  231. package/dist/graph.d.ts +164 -0
  232. package/dist/graph.js +383 -0
  233. package/dist/guides.d.ts +22 -0
  234. package/dist/guides.js +363 -0
  235. package/dist/held.d.ts +150 -0
  236. package/dist/held.js +799 -0
  237. package/dist/invoke.d.ts +112 -0
  238. package/dist/invoke.js +780 -0
  239. package/dist/issues.d.ts +41 -0
  240. package/dist/issues.js +131 -0
  241. package/dist/job-object-helper.ps1 +252 -0
  242. package/dist/jsonl-discriminants.d.ts +11 -0
  243. package/dist/jsonl-discriminants.js +93 -0
  244. package/dist/keys.d.ts +104 -0
  245. package/dist/keys.js +229 -0
  246. package/dist/kits-ui.d.ts +17 -0
  247. package/dist/kits-ui.js +46 -0
  248. package/dist/kits.d.ts +86 -0
  249. package/dist/kits.js +215 -0
  250. package/dist/knowledge-cli.d.ts +101 -0
  251. package/dist/knowledge-cli.js +92 -0
  252. package/dist/knowledge-ui.d.ts +24 -0
  253. package/dist/knowledge-ui.js +40 -0
  254. package/dist/lead-context.d.ts +8 -0
  255. package/dist/lead-context.js +56 -0
  256. package/dist/lead-follow.d.ts +22 -0
  257. package/dist/lead-follow.js +167 -0
  258. package/dist/lead-status.d.ts +75 -0
  259. package/dist/lead-status.js +285 -0
  260. package/dist/ledger-chain.d.ts +46 -0
  261. package/dist/ledger-chain.js +187 -0
  262. package/dist/ledger-csv.d.ts +4 -0
  263. package/dist/ledger-csv.js +11 -0
  264. package/dist/ledger-view.d.ts +24 -0
  265. package/dist/ledger-view.js +58 -0
  266. package/dist/limits-ui.d.ts +26 -0
  267. package/dist/limits-ui.js +67 -0
  268. package/dist/link.d.ts +72 -0
  269. package/dist/link.js +217 -0
  270. package/dist/live.d.ts +96 -0
  271. package/dist/live.js +366 -0
  272. package/dist/liveness.d.ts +30 -0
  273. package/dist/liveness.js +42 -0
  274. package/dist/log.d.ts +7 -0
  275. package/dist/log.js +25 -0
  276. package/dist/mailbox.d.ts +57 -0
  277. package/dist/mailbox.js +150 -0
  278. package/dist/maintenance.d.ts +11 -0
  279. package/dist/maintenance.js +35 -0
  280. package/dist/mate-cli.d.ts +52 -0
  281. package/dist/mate-cli.js +345 -0
  282. package/dist/mate-contract.d.ts +10 -0
  283. package/dist/mate-contract.js +30 -0
  284. package/dist/mate-doors.d.ts +69 -0
  285. package/dist/mate-doors.js +548 -0
  286. package/dist/mate-progress.d.ts +29 -0
  287. package/dist/mate-progress.js +121 -0
  288. package/dist/mate-tools.d.ts +94 -0
  289. package/dist/mate-tools.js +1878 -0
  290. package/dist/mate.d.ts +92 -0
  291. package/dist/mate.js +435 -0
  292. package/dist/mcp-connect.d.ts +103 -0
  293. package/dist/mcp-connect.js +252 -0
  294. package/dist/mcp.d.ts +27 -0
  295. package/dist/mcp.js +651 -0
  296. package/dist/memory-cli.d.ts +466 -0
  297. package/dist/memory-cli.js +111 -0
  298. package/dist/memory-pass.d.ts +160 -0
  299. package/dist/memory-pass.js +409 -0
  300. package/dist/metrics.d.ts +3 -0
  301. package/dist/metrics.js +56 -0
  302. package/dist/mobile-viewport.d.ts +3 -0
  303. package/dist/mobile-viewport.js +37 -0
  304. package/dist/model-catalog.d.ts +118 -0
  305. package/dist/model-catalog.js +376 -0
  306. package/dist/models-cli.d.ts +102 -0
  307. package/dist/models-cli.js +66 -0
  308. package/dist/models-ui.d.ts +34 -0
  309. package/dist/models-ui.js +68 -0
  310. package/dist/modes.d.ts +80 -0
  311. package/dist/modes.js +182 -0
  312. package/dist/monitoring-settings.d.ts +44 -0
  313. package/dist/monitoring-settings.js +173 -0
  314. package/dist/monitoring-ui.d.ts +20 -0
  315. package/dist/monitoring-ui.js +51 -0
  316. package/dist/monitoring.d.ts +34 -0
  317. package/dist/monitoring.js +221 -0
  318. package/dist/names.d.ts +67 -0
  319. package/dist/names.js +102 -0
  320. package/dist/observations.d.ts +40 -0
  321. package/dist/observations.js +211 -0
  322. package/dist/oidc.d.ts +71 -0
  323. package/dist/oidc.js +182 -0
  324. package/dist/onboard.d.ts +108 -0
  325. package/dist/onboard.js +325 -0
  326. package/dist/openrouter-models.d.ts +30 -0
  327. package/dist/openrouter-models.js +120 -0
  328. package/dist/operate.d.ts +118 -0
  329. package/dist/operate.js +11321 -0
  330. package/dist/peek-cli.d.ts +97 -0
  331. package/dist/peek-cli.js +215 -0
  332. package/dist/peek.d.ts +134 -0
  333. package/dist/peek.js +578 -0
  334. package/dist/phase-routing.d.ts +307 -0
  335. package/dist/phase-routing.js +658 -0
  336. package/dist/plan-auto.d.ts +14 -0
  337. package/dist/plan-auto.js +92 -0
  338. package/dist/plan.d.ts +186 -0
  339. package/dist/plan.js +401 -0
  340. package/dist/planner-source.d.ts +251 -0
  341. package/dist/planner-source.js +460 -0
  342. package/dist/planner.d.ts +75 -0
  343. package/dist/planner.js +992 -0
  344. package/dist/policy-ui.d.ts +20 -0
  345. package/dist/policy-ui.js +41 -0
  346. package/dist/policy.d.ts +111 -0
  347. package/dist/policy.js +231 -0
  348. package/dist/prepared-evidence.d.ts +16 -0
  349. package/dist/prepared-evidence.js +96 -0
  350. package/dist/pricing.d.ts +45 -0
  351. package/dist/pricing.js +77 -0
  352. package/dist/principal.d.ts +42 -0
  353. package/dist/principal.js +82 -0
  354. package/dist/probe.d.ts +46 -0
  355. package/dist/probe.js +79 -0
  356. package/dist/process-custody.d.ts +17 -0
  357. package/dist/process-custody.js +91 -0
  358. package/dist/process-liveness.d.ts +10 -0
  359. package/dist/process-liveness.js +81 -0
  360. package/dist/process-recovery-anchor.d.ts +75 -0
  361. package/dist/process-recovery-anchor.js +188 -0
  362. package/dist/process-recovery-coalition.d.ts +25 -0
  363. package/dist/process-recovery-coalition.js +105 -0
  364. package/dist/process-recovery-eligibility.d.ts +178 -0
  365. package/dist/process-recovery-eligibility.js +170 -0
  366. package/dist/process-recovery-native.d.ts +172 -0
  367. package/dist/process-recovery-native.js +592 -0
  368. package/dist/process-recovery-provenance.d.ts +163 -0
  369. package/dist/process-recovery-provenance.js +292 -0
  370. package/dist/process-recovery-services.d.ts +56 -0
  371. package/dist/process-recovery-services.js +191 -0
  372. package/dist/process-recovery-settlement.d.ts +32 -0
  373. package/dist/process-recovery-settlement.js +132 -0
  374. package/dist/process-recovery.d.ts +41 -0
  375. package/dist/process-recovery.js +91 -0
  376. package/dist/process-tree.d.ts +41 -0
  377. package/dist/process-tree.js +264 -0
  378. package/dist/project-access.d.ts +14 -0
  379. package/dist/project-access.js +34 -0
  380. package/dist/project-cli.d.ts +18 -0
  381. package/dist/project-cli.js +104 -0
  382. package/dist/project-delete-ui.d.ts +22 -0
  383. package/dist/project-delete-ui.js +48 -0
  384. package/dist/project-delete.d.ts +55 -0
  385. package/dist/project-delete.js +413 -0
  386. package/dist/project-knowledge.d.ts +111 -0
  387. package/dist/project-knowledge.js +241 -0
  388. package/dist/project-learning.d.ts +100 -0
  389. package/dist/project-learning.js +438 -0
  390. package/dist/project-memory.d.ts +81 -0
  391. package/dist/project-memory.js +264 -0
  392. package/dist/project-skills.d.ts +152 -0
  393. package/dist/project-skills.js +660 -0
  394. package/dist/project-tools.d.ts +219 -0
  395. package/dist/project-tools.js +796 -0
  396. package/dist/project.d.ts +61 -0
  397. package/dist/project.js +125 -0
  398. package/dist/prompt.d.ts +22 -0
  399. package/dist/prompt.js +72 -0
  400. package/dist/proof.d.ts +513 -0
  401. package/dist/proof.js +1140 -0
  402. package/dist/proposal.d.ts +82 -0
  403. package/dist/proposal.js +210 -0
  404. package/dist/provider-connection.d.ts +22 -0
  405. package/dist/provider-connection.js +98 -0
  406. package/dist/provider-limits.d.ts +35 -0
  407. package/dist/provider-limits.js +95 -0
  408. package/dist/provider.d.ts +279 -0
  409. package/dist/provider.js +935 -0
  410. package/dist/publish.d.ts +107 -0
  411. package/dist/publish.js +650 -0
  412. package/dist/pulls.d.ts +118 -0
  413. package/dist/pulls.js +240 -0
  414. package/dist/push.d.ts +95 -0
  415. package/dist/push.js +353 -0
  416. package/dist/quality.d.ts +8 -0
  417. package/dist/quality.js +6 -0
  418. package/dist/recipe-ui.d.ts +18 -0
  419. package/dist/recipe-ui.js +152 -0
  420. package/dist/recipes.d.ts +81 -0
  421. package/dist/recipes.js +332 -0
  422. package/dist/remote.d.ts +67 -0
  423. package/dist/remote.js +122 -0
  424. package/dist/render.d.ts +64 -0
  425. package/dist/render.js +587 -0
  426. package/dist/report-summary.d.ts +13 -0
  427. package/dist/report-summary.js +15 -0
  428. package/dist/repos.d.ts +65 -0
  429. package/dist/repos.js +277 -0
  430. package/dist/repository-context-ui.d.ts +2 -0
  431. package/dist/repository-context-ui.js +10 -0
  432. package/dist/repository-context.d.ts +67 -0
  433. package/dist/repository-context.js +376 -0
  434. package/dist/restart-certification.d.ts +134 -0
  435. package/dist/restart-certification.js +226 -0
  436. package/dist/result-actions.d.ts +28 -0
  437. package/dist/result-actions.js +270 -0
  438. package/dist/result-completion.d.ts +18 -0
  439. package/dist/result-completion.js +66 -0
  440. package/dist/result-review.d.ts +213 -0
  441. package/dist/result-review.js +430 -0
  442. package/dist/retention-ui.d.ts +12 -0
  443. package/dist/retention-ui.js +39 -0
  444. package/dist/retention.d.ts +72 -0
  445. package/dist/retention.js +288 -0
  446. package/dist/review-context.d.ts +243 -0
  447. package/dist/review-context.js +1043 -0
  448. package/dist/review-evidence.d.ts +33 -0
  449. package/dist/review-evidence.js +56 -0
  450. package/dist/reviewer.d.ts +97 -0
  451. package/dist/reviewer.js +202 -0
  452. package/dist/routine.d.ts +228 -0
  453. package/dist/routine.js +764 -0
  454. package/dist/runner.d.ts +266 -0
  455. package/dist/runner.js +399 -0
  456. package/dist/scan.d.ts +32 -0
  457. package/dist/scan.js +92 -0
  458. package/dist/scope.d.ts +695 -0
  459. package/dist/scope.js +1176 -0
  460. package/dist/scout-report.d.ts +42 -0
  461. package/dist/scout-report.js +108 -0
  462. package/dist/scout.d.ts +69 -0
  463. package/dist/scout.js +351 -0
  464. package/dist/serve.d.ts +313 -0
  465. package/dist/serve.js +21776 -0
  466. package/dist/session-brief.d.ts +8 -0
  467. package/dist/session-brief.js +24 -0
  468. package/dist/session-cli.d.ts +13 -0
  469. package/dist/session-cli.js +284 -0
  470. package/dist/session-contract.d.ts +183 -0
  471. package/dist/session-contract.js +122 -0
  472. package/dist/session-http.d.ts +19 -0
  473. package/dist/session-http.js +154 -0
  474. package/dist/session-server.d.ts +12 -0
  475. package/dist/session-server.js +37 -0
  476. package/dist/session-service.d.ts +22 -0
  477. package/dist/session-service.js +157 -0
  478. package/dist/setup-guide.d.ts +39 -0
  479. package/dist/setup-guide.js +93 -0
  480. package/dist/sign-in-guard.d.ts +45 -0
  481. package/dist/sign-in-guard.js +74 -0
  482. package/dist/skills-ui.d.ts +11 -0
  483. package/dist/skills-ui.js +58 -0
  484. package/dist/skills.d.ts +97 -0
  485. package/dist/skills.js +276 -0
  486. package/dist/slack-api.d.ts +55 -0
  487. package/dist/slack-api.js +229 -0
  488. package/dist/slack-chat.d.ts +28 -0
  489. package/dist/slack-chat.js +413 -0
  490. package/dist/slack-settings.d.ts +7 -0
  491. package/dist/slack-settings.js +75 -0
  492. package/dist/slack-state.d.ts +13 -0
  493. package/dist/slack-state.js +9 -0
  494. package/dist/slack.d.ts +13 -0
  495. package/dist/slack.js +167 -0
  496. package/dist/spend-ui.d.ts +28 -0
  497. package/dist/spend-ui.js +95 -0
  498. package/dist/spend.d.ts +142 -0
  499. package/dist/spend.js +313 -0
  500. package/dist/sqlite-runtime.d.ts +3 -0
  501. package/dist/sqlite-runtime.js +6 -0
  502. package/dist/sso-settings.d.ts +33 -0
  503. package/dist/sso-settings.js +103 -0
  504. package/dist/sso-ui.d.ts +22 -0
  505. package/dist/sso-ui.js +39 -0
  506. package/dist/storage.d.ts +15 -0
  507. package/dist/storage.js +84 -0
  508. package/dist/store.d.ts +7754 -0
  509. package/dist/store.js +23279 -0
  510. package/dist/structured-output.d.ts +58 -0
  511. package/dist/structured-output.js +103 -0
  512. package/dist/style-asset.d.ts +7 -0
  513. package/dist/style-asset.js +41 -0
  514. package/dist/subscription-chat.d.ts +46 -0
  515. package/dist/subscription-chat.js +275 -0
  516. package/dist/summary.d.ts +33 -0
  517. package/dist/summary.js +71 -0
  518. package/dist/supervisor.mjs +297 -0
  519. package/dist/surface.d.ts +63 -0
  520. package/dist/surface.js +352 -0
  521. package/dist/sync.d.ts +94 -0
  522. package/dist/sync.js +279 -0
  523. package/dist/task-composer.d.ts +13 -0
  524. package/dist/task-composer.js +52 -0
  525. package/dist/task-control.d.ts +165 -0
  526. package/dist/task-control.js +214 -0
  527. package/dist/task-outcome-cli.d.ts +10 -0
  528. package/dist/task-outcome-cli.js +92 -0
  529. package/dist/task-text.d.ts +30 -0
  530. package/dist/task-text.js +41 -0
  531. package/dist/team-cli.d.ts +15 -0
  532. package/dist/team-cli.js +618 -0
  533. package/dist/team-contract.d.ts +92 -0
  534. package/dist/team-contract.js +1 -0
  535. package/dist/team-http.d.ts +17 -0
  536. package/dist/team-http.js +144 -0
  537. package/dist/team-leads.d.ts +81 -0
  538. package/dist/team-leads.js +565 -0
  539. package/dist/team-runtime.d.ts +27 -0
  540. package/dist/team-runtime.js +264 -0
  541. package/dist/team-ui.d.ts +3 -0
  542. package/dist/team-ui.js +9 -0
  543. package/dist/team-updates.d.ts +8 -0
  544. package/dist/team-updates.js +57 -0
  545. package/dist/teammate-admin.d.ts +53 -0
  546. package/dist/teammate-admin.js +111 -0
  547. package/dist/teammate-desk.d.ts +60 -0
  548. package/dist/teammate-desk.js +154 -0
  549. package/dist/teammate-memory.d.ts +64 -0
  550. package/dist/teammate-memory.js +196 -0
  551. package/dist/teammate-question.d.ts +59 -0
  552. package/dist/teammate-question.js +175 -0
  553. package/dist/teammate-tools.d.ts +108 -0
  554. package/dist/teammate-tools.js +271 -0
  555. package/dist/teammate-week.d.ts +68 -0
  556. package/dist/teammate-week.js +132 -0
  557. package/dist/teammate-work.d.ts +76 -0
  558. package/dist/teammate-work.js +280 -0
  559. package/dist/teammates-ui.d.ts +20 -0
  560. package/dist/teammates-ui.js +166 -0
  561. package/dist/teammates.d.ts +180 -0
  562. package/dist/teammates.js +253 -0
  563. package/dist/teams-api.d.ts +33 -0
  564. package/dist/teams-api.js +204 -0
  565. package/dist/teams-chat.d.ts +26 -0
  566. package/dist/teams-chat.js +215 -0
  567. package/dist/teams-settings.d.ts +12 -0
  568. package/dist/teams-settings.js +57 -0
  569. package/dist/teams.d.ts +21 -0
  570. package/dist/teams.js +124 -0
  571. package/dist/telegram-flow.d.ts +49 -0
  572. package/dist/telegram-flow.js +110 -0
  573. package/dist/telegram-mate.d.ts +137 -0
  574. package/dist/telegram-mate.js +707 -0
  575. package/dist/telegram-progress.d.ts +14 -0
  576. package/dist/telegram-progress.js +129 -0
  577. package/dist/telegram-settings.d.ts +9 -0
  578. package/dist/telegram-settings.js +36 -0
  579. package/dist/telegram-status.d.ts +75 -0
  580. package/dist/telegram-status.js +265 -0
  581. package/dist/telegram-team.d.ts +85 -0
  582. package/dist/telegram-team.js +359 -0
  583. package/dist/telegram.d.ts +230 -0
  584. package/dist/telegram.js +1931 -0
  585. package/dist/templates.d.ts +65 -0
  586. package/dist/templates.js +118 -0
  587. package/dist/tool-launcher.d.ts +1 -0
  588. package/dist/tool-launcher.js +101 -0
  589. package/dist/tools-ui.d.ts +30 -0
  590. package/dist/tools-ui.js +40 -0
  591. package/dist/transitions-recipes.d.ts +1 -0
  592. package/dist/transitions-recipes.js +203 -0
  593. package/dist/tree-proof.d.ts +34 -0
  594. package/dist/tree-proof.js +58 -0
  595. package/dist/verification-evidence.d.ts +44 -0
  596. package/dist/verification-evidence.js +238 -0
  597. package/dist/version.d.ts +2 -0
  598. package/dist/version.js +10 -0
  599. package/dist/webhooks.d.ts +91 -0
  600. package/dist/webhooks.js +283 -0
  601. package/dist/work-index.d.ts +68 -0
  602. package/dist/work-index.js +384 -0
  603. package/dist/work-summary.d.ts +57 -0
  604. package/dist/work-summary.js +116 -0
  605. package/dist/workspace-motion.d.ts +4 -0
  606. package/dist/workspace-motion.js +133 -0
  607. package/dist/workspace-revision.d.ts +19 -0
  608. package/dist/workspace-revision.js +92 -0
  609. package/dist/workspace-ui.d.ts +204 -0
  610. package/dist/workspace-ui.js +411 -0
  611. package/dist/worktree-notices.d.ts +13 -0
  612. package/dist/worktree-notices.js +17 -0
  613. package/dist/worktree.d.ts +249 -0
  614. package/dist/worktree.js +720 -0
  615. package/package.json +122 -0
  616. package/scripts/canary-assertions.mjs +42 -0
  617. package/scripts/crash-canary.mjs +253 -0
  618. package/scripts/fixtures/crash-process.mjs +74 -0
  619. package/scripts/fixtures/pilot-scenarios.mjs +85 -0
  620. package/scripts/fixtures/restart-service.mjs +27 -0
  621. package/scripts/launchd-certification.mjs +143 -0
  622. package/scripts/pilot.mjs +121 -0
  623. package/scripts/proof-preflight.mjs +140 -0
  624. package/scripts/provider-canary.mjs +379 -0
  625. package/scripts/recovery-canary.mjs +135 -0
  626. package/scripts/restart-certification.mjs +98 -0
package/dist/daemon.js ADDED
@@ -0,0 +1,604 @@
1
+ /**
2
+ * The daemon manager: the loop as a service, with no crontab in sight.
3
+ *
4
+ * `toolroll daemon install` writes the platform's own supervision unit —
5
+ * a launchd LaunchAgent on macOS, a systemd user unit on Linux, a Task
6
+ * Scheduler task on Windows, chosen by process.platform — pointed at
7
+ * `toolroll watch`, and loads it. The OS keeps it alive across crashes
8
+ * and reboots; watch's incarnation recovery is what makes those restarts
9
+ * safe, so the two halves were built for each other.
10
+ *
11
+ * The runner token never enters the unit file. It is written 0600 to a
12
+ * credential file beside the database (the same discipline as the Telegram
13
+ * bot token), and the unit runs `watch --token-file <path>`. A unit file is
14
+ * the kind of thing that ends up in a dotfiles repo; a credential file
15
+ * beside the database is the kind of thing backup tooling already treats
16
+ * as sensitive.
17
+ *
18
+ * The lifecycle (OS containment and login recovery plan) is ONE contract
19
+ * for the CLI daemon and the desktop service, which share the launchd road
20
+ * below:
21
+ * - an installed always-on controller restarts after an unexpected
22
+ * clean exit as well as a crash (launchd KeepAlive=true, systemd
23
+ * Restart=always, a cmd restart loop under Task Scheduler);
24
+ * - start is idempotent — a healthy running service is never killed
25
+ * because the installer or the app window ran again;
26
+ * - a CHANGED definition (runtime, entry, flags) really reloads: the old
27
+ * job is booted out, its disappearance is awaited (the pending-bootout
28
+ * race), and the new unit is bootstrapped;
29
+ * - explicit stop unloads AND disables, so nothing relaunches it until a
30
+ * person installs again — which re-enables;
31
+ * - status tells a loaded definition from a fresh working controller,
32
+ * and names a missing runtime, a missing entry, or a disabled service.
33
+ *
34
+ * Everything here is generation plus one supervisor invocation, both
35
+ * injectable — the tests read the generated unit and script `launchctl`,
36
+ * and never touch the machine's real supervision.
37
+ */
38
+ import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
39
+ import { createHash } from "node:crypto";
40
+ import { homedir } from "node:os";
41
+ import { fileURLToPath } from "node:url";
42
+ import { dirname, join } from "node:path";
43
+ import { isAlive } from "./runner.js";
44
+ import { envTwins } from "./names.js";
45
+ /** A stable fingerprint of a unit's text: the changed-definition test. */
46
+ export function definitionDigest(unitContent) {
47
+ return createHash("sha256").update(unitContent).digest("hex").slice(0, 16);
48
+ }
49
+ /**
50
+ * `launchctl bootout` can return before the old label has disappeared from
51
+ * the user domain. Bootstrapping the replacement during that short window
52
+ * appears to succeed, then the pending bootout removes the new job too. Wait
53
+ * for the observable supervisor boundary before loading the replacement.
54
+ */
55
+ async function waitForLaunchdBootout(run, service, attempts = 50) {
56
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
57
+ const status = await run("launchctl", ["print", service]);
58
+ if (status.code !== 0)
59
+ return true;
60
+ await new Promise(resolve => setTimeout(resolve, 100));
61
+ }
62
+ return false;
63
+ }
64
+ /**
65
+ * Pin a service to the Node runtime that is executing Toolroll.
66
+ *
67
+ * launchd does not inherit an interactive shell's PATH, so invoking a JS
68
+ * package bin through `#!/usr/bin/env node` can install successfully and then
69
+ * fail before the CLI starts. The service instead runs the absolute Node
70
+ * binary with the package entry as its first argument. An explicitly supplied
71
+ * non-JavaScript executable remains an escape hatch for wrappers and packaged
72
+ * binaries.
73
+ */
74
+ export function daemonLaunchCommand(args) {
75
+ if (args.explicitBin !== undefined) {
76
+ return /\.(?:c|m)?js$/i.test(args.explicitBin)
77
+ ? { bin: args.execPath, binArgs: [args.explicitBin] }
78
+ : { bin: args.explicitBin, binArgs: [] };
79
+ }
80
+ if (args.entry === undefined || /\.(?:c|m)?tsx?$/i.test(args.entry))
81
+ return null;
82
+ return { bin: args.execPath, binArgs: [args.entry] };
83
+ }
84
+ /** One service per repo, named so two repos' watches never collide. */
85
+ export function labelFor(repo) {
86
+ return `com.toolroll.watch.${watchSlug(repo)}`;
87
+ }
88
+ /** The label a watch installed before the rename to Toolroll runs under: still found, stopped and uninstalled. */
89
+ export function legacyLabelFor(repo) {
90
+ return `com.standing-orders.watch.${watchSlug(repo)}`;
91
+ }
92
+ function watchSlug(repo) {
93
+ const slug = repo
94
+ .replace(/[^A-Za-z0-9]+/g, "-")
95
+ .replace(/^-+|-+$/g, "")
96
+ .toLowerCase()
97
+ .slice(-40);
98
+ return slug === "" ? "root" : slug;
99
+ }
100
+ /** The uid launchd's gui domain is keyed by. */
101
+ function launchdUid() {
102
+ return typeof process.getuid === "function" ? process.getuid() : 501;
103
+ }
104
+ /**
105
+ * The launchd unit shared by the CLI daemon and the desktop service.
106
+ * KeepAlive requests relaunch after either clean exit or crash. macOS may
107
+ * defer nondemand launches; the desktop's live controller supervisor covers
108
+ * controller exits independently. Login/OS-service recovery requires the
109
+ * physical certification, not just a valid plist.
110
+ */
111
+ export function launchdPlist(args) {
112
+ if (args.startInterval !== undefined && (!Number.isSafeInteger(args.startInterval) || args.startInterval < 1))
113
+ throw Error("Invalid launchd start interval.");
114
+ if (args.associatedBundleId !== undefined && !/^[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+$/.test(args.associatedBundleId))
115
+ throw new Error("Invalid associated app bundle identifier.");
116
+ const escaped = args.command.map(part => ` <string>${xml(part)}</string>`).join("\n");
117
+ const digest = createHash("sha256").update(JSON.stringify(args));
118
+ // Include executable and package code identity so an update at the same
119
+ // path reloads the service. Files are only read; credentials are excluded.
120
+ for (const file of args.command.slice(0, 2)) {
121
+ try {
122
+ if (file && existsSync(file))
123
+ digest.update(readFileSync(file));
124
+ }
125
+ catch {
126
+ digest.update("unreadable-runtime");
127
+ }
128
+ }
129
+ const entry = args.command[1];
130
+ if (entry?.endsWith(".js")) {
131
+ try {
132
+ for (const file of readdirSync(dirname(entry)).filter(name => /\.(js|ps1)$/.test(name)).sort())
133
+ digest.update(file).update(readFileSync(join(dirname(entry), file)));
134
+ }
135
+ catch {
136
+ digest.update("unreadable-package");
137
+ }
138
+ }
139
+ const environment = Object.entries({ PATH: args.pathEnv, ...(args.environment ?? {}), ...envTwins("SERVICE_DIGEST", digest.digest("hex")) })
140
+ .map(([key, value]) => ` <key>${xml(key)}</key>\n <string>${xml(value)}</string>`)
141
+ .join("\n");
142
+ return `<?xml version="1.0" encoding="UTF-8"?>
143
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
144
+ <plist version="1.0">
145
+ <dict>
146
+ <key>Label</key>
147
+ <string>${xml(args.label)}</string>
148
+ ${args.associatedBundleId ? ` <key>AssociatedBundleIdentifiers</key>\n <array><string>${xml(args.associatedBundleId)}</string></array>` : ""}
149
+ <key>ProgramArguments</key>
150
+ <array>
151
+ ${escaped}
152
+ </array>
153
+ <key>WorkingDirectory</key>
154
+ <string>${xml(args.workingDirectory)}</string>
155
+ <key>EnvironmentVariables</key>
156
+ <dict>
157
+ ${environment}
158
+ </dict>
159
+ <key>RunAtLoad</key>
160
+ <true/>
161
+ <key>KeepAlive</key>
162
+ <${args.keepAlive === false ? "false" : "true"}/>${args.startInterval === undefined ? "" : `\n <key>StartInterval</key>\n <integer>${args.startInterval}</integer>`}
163
+ <key>ThrottleInterval</key>
164
+ <integer>15</integer>
165
+ <key>ExitTimeOut</key>
166
+ <integer>60</integer>
167
+ <key>StandardOutPath</key>
168
+ <string>${xml(args.logPath)}</string>
169
+ <key>StandardErrorPath</key>
170
+ <string>${xml(args.logPath)}</string>
171
+ </dict>
172
+ </plist>
173
+ `;
174
+ }
175
+ /**
176
+ * Everything `install` would do, computed without doing it — the unit text,
177
+ * where it goes, where logs land. `--dry-run` prints exactly this.
178
+ */
179
+ export function planDaemon(args) {
180
+ const { platform, bin, binArgs, runner, repo, configDir, watchFlags } = args;
181
+ const home = args.home ?? homedir();
182
+ const pathEnv = args.pathEnv ?? process.env["PATH"] ?? "";
183
+ if (platform !== "darwin" && platform !== "linux" && platform !== "win32") {
184
+ return {
185
+ error: `no supervisor template for ${platform} — run \`toolroll watch\` under your own service manager`,
186
+ };
187
+ }
188
+ const label = args.label ?? labelFor(repo);
189
+ const logDir = join(configDir, "logs");
190
+ const logPath = join(logDir, `${label}.log`);
191
+ const tokenFile = join(configDir, "runner-token");
192
+ const entry = binArgs[0] !== undefined && /\.(?:c|m)?js$/i.test(binArgs[0]) ? binArgs[0] : null;
193
+ const command = [
194
+ bin,
195
+ ...binArgs,
196
+ "watch",
197
+ "--runner",
198
+ runner,
199
+ "--token-file",
200
+ tokenFile,
201
+ "--repo",
202
+ repo,
203
+ ...watchFlags,
204
+ ];
205
+ if (platform === "darwin") {
206
+ return {
207
+ platform,
208
+ label,
209
+ unitPath: join(home, "Library", "LaunchAgents", `${label}.plist`),
210
+ unitContent: launchdPlist({ label, command: [process.execPath, fileURLToPath(new URL("./controller-service.js", import.meta.url)), ...command], workingDirectory: repo, pathEnv, logPath }),
211
+ logPath,
212
+ tokenFile,
213
+ bin,
214
+ entry,
215
+ };
216
+ }
217
+ if (platform === "win32") {
218
+ // Task Scheduler is Windows' launchd: a logon-triggered task, restarted
219
+ // on failure, created from an XML definition — no admin, no Service
220
+ // wrapper. schtasks does not redirect output, so the action runs
221
+ // through cmd with an append redirection into the same log file the
222
+ // other platforms use. RestartOnFailure covers a crash only; a clean
223
+ // exit would end the task, so the action itself is a bounded restart
224
+ // loop (15 s between runs, like ThrottleInterval) — `schtasks /End`
225
+ // (explicit stop) ends the loop's whole tree.
226
+ const inner = [quoteWin(bin), ...binArgs.map(quoteWin), "watch",
227
+ "--runner", quoteWin(runner), "--token-file", quoteWin(tokenFile),
228
+ "--repo", quoteWin(repo), ...watchFlags.map(quoteWin)].join(" ");
229
+ const pathPrefix = pathEnv === "" ? "" : `set "PATH=${pathEnv};%PATH%" && `;
230
+ const cmdArguments = `/d /s /c "${pathPrefix}for /L %i in (1,0,2) do (${inner} >> ${quoteWin(logPath)} 2>&1 & timeout /t 15 /nobreak >nul)"`;
231
+ const unitContent = `<?xml version="1.0" encoding="UTF-16"?>
232
+ <Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
233
+ <RegistrationInfo>
234
+ <Description>toolroll watch — ${xml(repo)}</Description>
235
+ </RegistrationInfo>
236
+ <Triggers>
237
+ <LogonTrigger>
238
+ <Enabled>true</Enabled>
239
+ </LogonTrigger>
240
+ </Triggers>
241
+ <Settings>
242
+ <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
243
+ <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
244
+ <StartWhenAvailable>true</StartWhenAvailable>
245
+ <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
246
+ <RestartOnFailure>
247
+ <Interval>PT1M</Interval>
248
+ <Count>999</Count>
249
+ </RestartOnFailure>
250
+ <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
251
+ </Settings>
252
+ <Actions Context="Author">
253
+ <Exec>
254
+ <Command>cmd.exe</Command>
255
+ <Arguments>${xml(cmdArguments)}</Arguments>
256
+ </Exec>
257
+ </Actions>
258
+ </Task>
259
+ `;
260
+ return {
261
+ platform,
262
+ label,
263
+ unitPath: join(configDir, "daemon", `${label}.xml`),
264
+ unitContent,
265
+ logPath,
266
+ tokenFile,
267
+ bin,
268
+ entry,
269
+ };
270
+ }
271
+ // Restart=always: a clean exit the operator did not ask for comes back
272
+ // too; `systemctl --user stop` / `disable --now` (explicit stop) does
273
+ // not trigger a restart. Whether the user manager itself survives logout
274
+ // is linger (`loginctl enable-linger`) — documented, not assumed.
275
+ const unitContent = `[Unit]
276
+ Description=toolroll watch — ${repo}
277
+
278
+ [Service]
279
+ Environment=${systemdEscape(`PATH=${pathEnv}`)}
280
+ ExecStart=${command.map(systemdEscape).join(" ")}
281
+ WorkingDirectory=${systemdEscape(repo)}
282
+ Restart=always
283
+ RestartSec=15
284
+
285
+ StandardOutput=append:${logPath}
286
+ StandardError=append:${logPath}
287
+
288
+ [Install]
289
+ WantedBy=default.target
290
+ `;
291
+ return {
292
+ platform,
293
+ label,
294
+ unitPath: join(home, ".config", "systemd", "user", `${label}.service`),
295
+ unitContent,
296
+ logPath,
297
+ tokenFile,
298
+ bin,
299
+ entry,
300
+ };
301
+ }
302
+ /** The desktop shell's always-on controller: the same launchd contract, pointed at the desktop host. */
303
+ export function planDesktopService(args) {
304
+ const home = args.home ?? homedir();
305
+ const logPath = join(args.stateDir, "service.log");
306
+ return {
307
+ platform: "darwin",
308
+ label: args.label,
309
+ unitPath: join(home, "Library", "LaunchAgents", `${args.label}.plist`),
310
+ unitContent: launchdPlist({
311
+ label: args.label,
312
+ command: [args.node, args.helper, "serve", "--state", args.stateDir],
313
+ workingDirectory: args.stateDir,
314
+ pathEnv: args.pathEnv ?? process.env["PATH"] ?? "",
315
+ logPath,
316
+ associatedBundleId: args.associatedBundleId ?? "com.standing-orders.desktop",
317
+ ...(args.environment === undefined ? {} : { environment: args.environment }),
318
+ }),
319
+ logPath,
320
+ bin: args.node,
321
+ entry: args.helper,
322
+ };
323
+ }
324
+ /** The file on disk does not identify the definition launchd has loaded. */
325
+ function loadedLaunchdMatches(unit, status) {
326
+ // Either name: a unit written before the rename carries only the older one.
327
+ const wanted = /<key>(?:TOOLROLL|STANDING_ORDERS)_SERVICE_DIGEST<\/key>\s*<string>([a-f0-9]{64})<\/string>/.exec(unit)?.[1];
328
+ const actual = /(?:TOOLROLL|STANDING_ORDERS)_SERVICE_DIGEST\s*=>\s*([a-f0-9]{64})/.exec(status)?.[1];
329
+ return wanted !== undefined && actual === wanted;
330
+ }
331
+ /**
332
+ * The shared launchd road. Idempotent start: a loaded job whose definition
333
+ * is unchanged is kickstarted WITHOUT -k (a running one is left alone; a
334
+ * stopped one starts); a loaded job whose definition changed is booted out,
335
+ * awaited, and bootstrapped afresh; an unloaded label is enabled (an
336
+ * explicit stop had disabled it) and bootstrapped. Nothing here kills a
337
+ * healthy service to prove it can.
338
+ */
339
+ export async function installLaunchdService(definition, run) {
340
+ const uid = launchdUid();
341
+ const service = `gui/${uid}/${definition.label}`;
342
+ const before = existsSync(definition.unitPath) ? readFileSync(definition.unitPath, "utf8") : null;
343
+ let changed = before !== definition.unitContent;
344
+ mkdirSync(join(definition.logPath, ".."), { recursive: true });
345
+ mkdirSync(join(definition.unitPath, ".."), { recursive: true });
346
+ writeFileSync(definition.unitPath, definition.unitContent, { mode: 0o644 });
347
+ const status = await run("launchctl", ["print", service]);
348
+ const loaded = status.code === 0;
349
+ if (loaded)
350
+ changed ||= !loadedLaunchdMatches(definition.unitContent, status.stdout);
351
+ if (loaded && !changed) {
352
+ const started = await run("launchctl", ["kickstart", service]);
353
+ if (started.code !== 0)
354
+ return { ok: false, message: `loaded, but launchctl could not start the worker: ${firstLine(started.stderr) || `exit ${started.code}`}` };
355
+ return { ok: true, changed: false, action: "started" };
356
+ }
357
+ if (loaded) {
358
+ const replaced = await run("launchctl", ["bootout", service]);
359
+ if (replaced.code !== 0)
360
+ return { ok: false, message: `launchctl could not boot out the previous definition: ${firstLine(replaced.stderr) || `exit ${replaced.code}`}` };
361
+ if (!(await waitForLaunchdBootout(run, service))) {
362
+ return { ok: false, message: "launchd did not finish stopping the previous worker; try the install again" };
363
+ }
364
+ }
365
+ // An explicit stop disables the label; installing again is the only road back.
366
+ const enabled = await run("launchctl", ["enable", service]);
367
+ if (enabled.code !== 0)
368
+ return { ok: false, message: `launchctl could not enable the service: ${firstLine(enabled.stderr) || enabled.code}` };
369
+ const modern = await run("launchctl", ["bootstrap", `gui/${uid}`, definition.unitPath]);
370
+ if (modern.code === 0) {
371
+ // RunAtLoad starts the job; the kickstart (no -k: never a restart) gives
372
+ // the installer a synchronous boundary instead of "the plist parsed".
373
+ const started = await run("launchctl", ["kickstart", service]);
374
+ if (started.code === 0)
375
+ return { ok: true, changed, action: loaded ? "reloaded" : "bootstrapped" };
376
+ return { ok: false, message: `loaded, but launchctl could not start the worker: ${firstLine(started.stderr) || `exit ${started.code}`}` };
377
+ }
378
+ // Modern first, legacy fallback: `bootstrap` replaced `load` but older
379
+ // macOS answers only to the old verb.
380
+ const legacy = await run("launchctl", ["load", "-w", definition.unitPath]);
381
+ if (legacy.code === 0)
382
+ return { ok: true, changed, action: loaded ? "reloaded" : "bootstrapped" };
383
+ return { ok: false, message: `launchctl refused the unit: ${firstLine(modern.stderr) || firstLine(legacy.stderr) || `exit ${legacy.code}`}` };
384
+ }
385
+ /** Explicit stop: unload AND disable, so nothing relaunches it until a person installs again. */
386
+ export async function stopLaunchdService(definition, run) {
387
+ const service = `gui/${launchdUid()}/${definition.label}`;
388
+ const disabled = await run("launchctl", ["disable", service]);
389
+ if (disabled.code !== 0)
390
+ return { ok: false, message: `launchctl could not disable ${definition.label}: ${firstLine(disabled.stderr) || disabled.code}` };
391
+ const modern = await run("launchctl", ["bootout", service]);
392
+ if (modern.code !== 0)
393
+ await run("launchctl", ["unload", definition.unitPath]);
394
+ if (!(await waitForLaunchdBootout(run, service)))
395
+ return { ok: false, message: `launchctl did not stop ${definition.label}; the service is still loaded` };
396
+ return { ok: true, wasLoaded: modern.code === 0 };
397
+ }
398
+ /**
399
+ * The same repo's watch as installed under the label it had before the
400
+ * rename to Toolroll, when its unit is still on disk; null otherwise.
401
+ * Installing the new one stops and removes it, so two watches never run.
402
+ */
403
+ export function installedLegacyDaemon(args) {
404
+ const legacy = planDaemon({ ...args, label: legacyLabelFor(args.repo) });
405
+ return "error" in legacy || !existsSync(legacy.unitPath) ? null : legacy;
406
+ }
407
+ /** Write the token 0600 and the unit, then hand the unit to the supervisor. */
408
+ export async function installDaemon(plan, token, run) {
409
+ writeFileSync(plan.tokenFile, `${token}\n`, { mode: 0o600 });
410
+ chmodSync(plan.tokenFile, 0o600);
411
+ if (plan.platform === "darwin")
412
+ return installLaunchdService(plan, run);
413
+ const before = existsSync(plan.unitPath) ? readFileSync(plan.unitPath, "utf8") : null;
414
+ const changed = before !== plan.unitContent;
415
+ mkdirSync(join(plan.logPath, ".."), { recursive: true });
416
+ mkdirSync(join(plan.unitPath, ".."), { recursive: true });
417
+ writeFileSync(plan.unitPath, plan.unitContent, { mode: 0o644 });
418
+ if (plan.platform === "win32") {
419
+ // /F replaces an existing definition, so re-install is idempotent. A
420
+ // task already running under an UNCHANGED definition is left alone; a
421
+ // changed one is ended and run again so the new action takes effect.
422
+ const created = await run("schtasks", ["/Create", "/TN", plan.label, "/XML", plan.unitPath, "/F"]);
423
+ if (created.code !== 0) {
424
+ return { ok: false, message: `schtasks /Create failed: ${firstLine(created.stderr) || `exit ${created.code}`}` };
425
+ }
426
+ const query = await run("schtasks", ["/Query", "/TN", plan.label, "/FO", "LIST", "/V"]);
427
+ const running = query.code === 0 && /Status:\s*Running/i.test(query.stdout);
428
+ if (running && !changed)
429
+ return { ok: true, changed: false, action: "running" };
430
+ if (running)
431
+ await run("schtasks", ["/End", "/TN", plan.label]);
432
+ const started = await run("schtasks", ["/Run", "/TN", plan.label]);
433
+ if (started.code !== 0) {
434
+ return { ok: false, message: `created, but schtasks /Run failed: ${firstLine(started.stderr)}` };
435
+ }
436
+ return { ok: true, changed, action: running ? "reloaded" : "started" };
437
+ }
438
+ const reload = await run("systemctl", ["--user", "daemon-reload"]);
439
+ if (reload.code !== 0) {
440
+ return { ok: false, message: `systemctl daemon-reload failed: ${firstLine(reload.stderr)}` };
441
+ }
442
+ const active = (await run("systemctl", ["--user", "is-active", plan.label])).stdout.trim() === "active";
443
+ if (active && changed) {
444
+ // `enable --now` leaves a running service on its OLD definition; only a
445
+ // restart loads the new one.
446
+ const restarted = await run("systemctl", ["--user", "restart", plan.label]);
447
+ if (restarted.code !== 0)
448
+ return { ok: false, message: `systemctl restart failed: ${firstLine(restarted.stderr)}` };
449
+ }
450
+ const enable = await run("systemctl", ["--user", "enable", "--now", plan.label]);
451
+ if (enable.code !== 0) {
452
+ return { ok: false, message: `systemctl enable failed: ${firstLine(enable.stderr)}` };
453
+ }
454
+ return { ok: true, changed, action: active ? (changed ? "reloaded" : "running") : "started" };
455
+ }
456
+ export async function uninstallDaemon(plan, run) {
457
+ if (plan.platform === "darwin") {
458
+ const stopped = await stopLaunchdService(plan, run);
459
+ if (!stopped.ok)
460
+ throw new Error(stopped.message);
461
+ }
462
+ else if (plan.platform === "win32") {
463
+ await run("schtasks", ["/End", "/TN", plan.label]);
464
+ await run("schtasks", ["/Delete", "/TN", plan.label, "/F"]);
465
+ }
466
+ else {
467
+ await run("systemctl", ["--user", "disable", "--now", plan.label]);
468
+ }
469
+ let existed = true;
470
+ try {
471
+ rmSync(plan.unitPath);
472
+ }
473
+ catch {
474
+ existed = false;
475
+ }
476
+ return { ok: true, existed };
477
+ }
478
+ /**
479
+ * The definition a label is INSTALLED under, read back from the unit file
480
+ * (the certificate's view: whatever is on disk is the baseline, so `stale`
481
+ * is false by construction and the runtime/entry are the installed ones).
482
+ * Null when no unit file exists for the label.
483
+ */
484
+ export function installedServiceDefinition(args) {
485
+ const platform = args.platform ?? process.platform;
486
+ if (platform !== "darwin" && platform !== "linux" && platform !== "win32")
487
+ return null;
488
+ const home = args.home ?? homedir();
489
+ const unitPath = platform === "darwin" ? join(home, "Library", "LaunchAgents", `${args.label}.plist`)
490
+ : platform === "linux" ? join(home, ".config", "systemd", "user", `${args.label}.service`)
491
+ : join(args.configDir ?? home, "daemon", `${args.label}.xml`);
492
+ if (!existsSync(unitPath))
493
+ return null;
494
+ const unitContent = readFileSync(unitPath, "utf8");
495
+ let bin = "";
496
+ let entry = null;
497
+ if (platform === "darwin") {
498
+ const strings = [...(/<key>ProgramArguments<\/key>\s*<array>([\s\S]*?)<\/array>/.exec(unitContent)?.[1] ?? "").matchAll(/<string>([^<]*)<\/string>/g)].map(match => unxml(match[1] ?? ""));
499
+ bin = strings[0] ?? "";
500
+ entry = strings[1] !== undefined && /\.(?:c|m)?js$/i.test(strings[1]) ? strings[1] : null;
501
+ }
502
+ else if (platform === "linux") {
503
+ const exec = /^ExecStart=(.*)$/m.exec(unitContent)?.[1] ?? "";
504
+ const parts = exec.match(/"(?:[^"\\]|\\.)*"|\S+/g)?.map(part => part.startsWith('"') ? part.slice(1, -1).replace(/\\(["\\])/g, "$1") : part) ?? [];
505
+ bin = parts[0] ?? "";
506
+ entry = parts[1] !== undefined && /\.(?:c|m)?js$/i.test(parts[1]) ? parts[1] : null;
507
+ }
508
+ const logPath = args.logPath ?? (/<key>StandardOutPath<\/key>\s*<string>([^<]*)<\/string>/.exec(unitContent)?.[1] ?? /^StandardOutput=append:(.*)$/m.exec(unitContent)?.[1] ?? "");
509
+ return { platform, label: args.label, unitPath, unitContent, logPath: unxml(logPath), bin, entry };
510
+ }
511
+ function unxml(text) {
512
+ return text.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
513
+ }
514
+ /** Runtime facts that make a loaded definition unable to become a working controller. */
515
+ export function runtimeProblems(definition) {
516
+ const problems = [];
517
+ if (!existsSync(definition.bin))
518
+ problems.push(`the service's runtime ${definition.bin} does not exist — reinstall to pin the current Node binary`);
519
+ if (definition.entry !== null && !existsSync(definition.entry))
520
+ problems.push(`the service's entry ${definition.entry} does not exist — reinstall from the current package`);
521
+ return problems;
522
+ }
523
+ export async function daemonStatus(plan, run) {
524
+ const problems = runtimeProblems(plan);
525
+ const installed = existsSync(plan.unitPath) ? readFileSync(plan.unitPath, "utf8") : null;
526
+ const facts = { problems, installedDigest: installed === null ? null : definitionDigest(installed), stale: installed !== null && installed !== plan.unitContent };
527
+ if (plan.platform === "darwin") {
528
+ const service = `gui/${launchdUid()}/${plan.label}`;
529
+ const answer = await run("launchctl", ["print", service]);
530
+ if (answer.code !== 0) {
531
+ const disabled = await run("launchctl", ["print-disabled", `gui/${launchdUid()}`]);
532
+ const pattern = new RegExp(`"${plan.label.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}"\\s*=>\\s*(disabled|true)`);
533
+ if (disabled.code === 0 && pattern.test(disabled.stdout)) {
534
+ return { state: "disabled", pid: null, detail: "disabled by an explicit stop; install again to re-enable", ...facts };
535
+ }
536
+ return { state: "not-installed", pid: null, detail: "launchd does not know the label", ...facts };
537
+ }
538
+ facts.stale ||= !loadedLaunchdMatches(plan.unitContent, answer.stdout);
539
+ const pid = /pid = (\d+)/.exec(answer.stdout)?.[1];
540
+ return pid === undefined
541
+ ? { state: "loaded", pid: null, detail: "loaded, not currently running", ...facts }
542
+ : { state: "running", pid: Number(pid), detail: `running as pid ${pid}`, ...facts };
543
+ }
544
+ if (plan.platform === "win32") {
545
+ const answer = await run("schtasks", ["/Query", "/TN", plan.label, "/FO", "LIST", "/V"]);
546
+ if (answer.code !== 0) {
547
+ return { state: "not-installed", pid: null, detail: "the scheduler does not know the task", ...facts };
548
+ }
549
+ if (/Status:\s*Running/i.test(answer.stdout))
550
+ return { state: "running", pid: null, detail: "running (Task Scheduler does not expose the pid)", ...facts };
551
+ if (/Status:\s*Disabled/i.test(answer.stdout) || /Scheduled Task State:\s*Disabled/i.test(answer.stdout)) {
552
+ return { state: "disabled", pid: null, detail: "the task is disabled", ...facts };
553
+ }
554
+ return { state: "loaded", pid: null, detail: "installed, not currently running", ...facts };
555
+ }
556
+ const answer = await run("systemctl", ["--user", "is-active", plan.label]);
557
+ const active = answer.stdout.trim() === "active";
558
+ if (answer.code !== 0 && !active) {
559
+ const enabled = await run("systemctl", ["--user", "is-enabled", plan.label]);
560
+ const word = enabled.stdout.trim();
561
+ if (word === "disabled")
562
+ return { state: "disabled", pid: null, detail: "disabled by an explicit stop; install again to re-enable", ...facts };
563
+ if (word === "enabled" || word === "linked")
564
+ return { state: "loaded", pid: null, detail: answer.stdout.trim() || "inactive", ...facts };
565
+ return { state: "not-installed", pid: null, detail: answer.stdout.trim() || "inactive", ...facts };
566
+ }
567
+ const pidAnswer = await run("systemctl", ["--user", "show", "--property=MainPID", plan.label]);
568
+ const pid = Number(/MainPID=(\d+)/.exec(pidAnswer.stdout)?.[1] ?? 0);
569
+ return active
570
+ ? { state: "running", pid: pid > 0 ? pid : null, detail: "active", ...facts }
571
+ : { state: "loaded", pid: null, detail: "installed, inactive", ...facts };
572
+ }
573
+ /**
574
+ * A supervisor PID is necessary but not sufficient: macOS can leave a
575
+ * process stuck behind a protected-folder access check before it ever
576
+ * opens the queue. The credentialed runner heartbeat is the end-to-end
577
+ * readiness receipt that proves a service reached the work loop — and a
578
+ * FRESH one, newer than what stood before the start, so a stale row from
579
+ * the previous incarnation cannot pass for the new controller.
580
+ */
581
+ export async function awaitFreshHeartbeat(store, runnerName, heartbeatBefore, deadlineMs = 5_000, sleep = ms => new Promise(resolve => setTimeout(resolve, ms))) {
582
+ const deadline = Date.now() + deadlineMs;
583
+ for (;;) {
584
+ const live = store.getRunner(runnerName)?.runner ?? null;
585
+ if (live !== null && live.heartbeatAt !== heartbeatBefore && isAlive(live, new Date()))
586
+ return { ok: true, heartbeatAt: live.heartbeatAt };
587
+ if (Date.now() >= deadline)
588
+ return { ok: false };
589
+ await sleep(100);
590
+ }
591
+ }
592
+ function xml(text) {
593
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
594
+ }
595
+ /** cmd.exe quoting: wrap anything with spaces; embedded quotes are not survivable in cmd — refuse them upstream by construction (paths and flags here never carry them). */
596
+ function quoteWin(part) {
597
+ return /[\s&|<>^]/.test(part) ? `"${part}"` : part;
598
+ }
599
+ function systemdEscape(part) {
600
+ return /[\s"'\\]/.test(part) ? `"${part.replace(/(["\\])/g, "\\$1")}"` : part;
601
+ }
602
+ function firstLine(text) {
603
+ return text.trim().split("\n")[0] ?? "";
604
+ }