@north-light/cli 0.3.529 → 0.3.531

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 (509) hide show
  1. package/dist/api/dto/lifecycle.d.ts +3 -1
  2. package/dist/api/dto/messages.d.ts +5 -3
  3. package/dist/builtin-memory/crouter-plugin/bundles-and-memory.md +1 -1
  4. package/dist/builtin-memory/crouter-plugin/commands.md +1 -1
  5. package/dist/clients/attach/session/connection.js +1 -1
  6. package/dist/clients/attach/session/reconnect.js +1 -1
  7. package/dist/clients/attach/viewer.js +508 -508
  8. package/dist/commands/node/message.js +1 -1
  9. package/dist/commands/sys/join.js +1 -1
  10. package/dist/core/async-array.d.ts +7 -0
  11. package/dist/core/async-array.js +1 -0
  12. package/dist/core/audit.d.ts +1 -1
  13. package/dist/core/audit.js +2 -2
  14. package/dist/core/canvas/attention.d.ts +4 -4
  15. package/dist/core/canvas/attention.js +1 -1
  16. package/dist/core/canvas/boot.d.ts +4 -4
  17. package/dist/core/canvas/boot.js +1 -1
  18. package/dist/core/canvas/canvas.d.ts +59 -59
  19. package/dist/core/canvas/canvas.js +19 -19
  20. package/dist/core/canvas/context-strip.js +2 -2
  21. package/dist/core/canvas/crons.d.ts +27 -27
  22. package/dist/core/canvas/crons.js +11 -11
  23. package/dist/core/canvas/daemon-actions.d.ts +10 -10
  24. package/dist/core/canvas/daemon-actions.js +8 -8
  25. package/dist/core/canvas/daemon-owner.d.ts +3 -3
  26. package/dist/core/canvas/daemon-owner.js +2 -2
  27. package/dist/core/canvas/db.d.ts +19 -30
  28. package/dist/core/canvas/db.js +3 -2
  29. package/dist/core/canvas/deliveries.d.ts +16 -16
  30. package/dist/core/canvas/deliveries.js +24 -24
  31. package/dist/core/canvas/executions.d.ts +8 -8
  32. package/dist/core/canvas/executions.js +1 -1
  33. package/dist/core/canvas/focuses.d.ts +8 -8
  34. package/dist/core/canvas/focuses.js +1 -1
  35. package/dist/core/canvas/history.d.ts +3 -3
  36. package/dist/core/canvas/history.js +9 -9
  37. package/dist/core/canvas/install-id.d.ts +1 -1
  38. package/dist/core/canvas/install-id.js +1 -1
  39. package/dist/core/canvas/managed-worktree-sweep-candidates.d.ts +1 -1
  40. package/dist/core/canvas/managed-worktree-sweep-candidates.js +2 -2
  41. package/dist/core/canvas/memory-reads.d.ts +6 -6
  42. package/dist/core/canvas/memory-reads.js +1 -1
  43. package/dist/core/canvas/nav-model.d.ts +13 -13
  44. package/dist/core/canvas/nav-model.js +1 -1
  45. package/dist/core/canvas/node-agent-paths.d.ts +6 -6
  46. package/dist/core/canvas/node-agent-paths.js +1 -1
  47. package/dist/core/canvas/node-tokens.d.ts +7 -7
  48. package/dist/core/canvas/node-tokens.js +3 -3
  49. package/dist/core/canvas/outcome-actions.d.ts +5 -5
  50. package/dist/core/canvas/outcome-actions.js +3 -3
  51. package/dist/core/canvas/recovery.d.ts +26 -26
  52. package/dist/core/canvas/recovery.js +2 -2
  53. package/dist/core/canvas/render-source.d.ts +1 -1
  54. package/dist/core/canvas/render-source.js +1 -1
  55. package/dist/core/canvas/sessions.d.ts +10 -10
  56. package/dist/core/canvas/sessions.js +4 -4
  57. package/dist/core/canvas/source.js +1 -1
  58. package/dist/core/canvas/structured-requests.d.ts +15 -15
  59. package/dist/core/canvas/structured-requests.js +5 -5
  60. package/dist/core/canvas/tree.d.ts +9 -9
  61. package/dist/core/canvas/tree.js +5 -5
  62. package/dist/core/canvas/waits.d.ts +13 -13
  63. package/dist/core/canvas/waits.js +6 -6
  64. package/dist/core/canvas/worktrees.d.ts +15 -15
  65. package/dist/core/canvas/worktrees.js +1 -1
  66. package/dist/core/command-hooks/lifecycle-catalog.d.ts +4 -4
  67. package/dist/core/command-hooks/lifecycle-catalog.js +1 -1
  68. package/dist/core/command-hooks/transport/exec-lifecycle.js +1 -1
  69. package/dist/core/command-plugins/bundle.d.ts +1 -1
  70. package/dist/core/command-plugins/bundle.js +1 -1
  71. package/dist/core/conversation-store/file-store.d.ts +7 -7
  72. package/dist/core/conversation-store/file-store.js +9 -9
  73. package/dist/core/conversation-store/index.d.ts +16 -14
  74. package/dist/core/conversation-store/index.js +1 -1
  75. package/dist/core/conversation-store/listener.js +5 -5
  76. package/dist/core/conversation-store/patch.d.ts +3 -1
  77. package/dist/core/conversation-store/patch.js +1 -1
  78. package/dist/core/directory/join.d.ts +1 -1
  79. package/dist/core/feed/feed.js +3 -3
  80. package/dist/core/feed/inbox.d.ts +2 -2
  81. package/dist/core/feed/inbox.js +1 -1
  82. package/dist/core/feed/messages.d.ts +11 -12
  83. package/dist/core/feed/messages.js +5 -5
  84. package/dist/core/feed/reports.d.ts +4 -4
  85. package/dist/core/feed/reports.js +2 -2
  86. package/dist/core/grants/cover.d.ts +1 -1
  87. package/dist/core/grants/cover.js +1 -1
  88. package/dist/core/grants/local.d.ts +1 -1
  89. package/dist/core/grants/local.js +1 -1
  90. package/dist/core/grants/mirror.d.ts +19 -19
  91. package/dist/core/grants/mirror.js +2 -2
  92. package/dist/core/grants/on-sync.d.ts +1 -1
  93. package/dist/core/grants/on-sync.js +1 -1
  94. package/dist/core/grants/remove.js +1 -1
  95. package/dist/core/grants/sync.js +1 -1
  96. package/dist/core/graph/access.d.ts +17 -17
  97. package/dist/core/graph/access.js +3 -3
  98. package/dist/core/graph/custom.d.ts +16 -16
  99. package/dist/core/graph/custom.js +9 -9
  100. package/dist/core/graph/deletion.d.ts +3 -7
  101. package/dist/core/graph/deletion.js +4 -4
  102. package/dist/core/graph/documents.d.ts +24 -27
  103. package/dist/core/graph/documents.js +7 -7
  104. package/dist/core/graph/edges.d.ts +13 -13
  105. package/dist/core/graph/edges.js +1 -1
  106. package/dist/core/graph/events.d.ts +7 -9
  107. package/dist/core/graph/events.js +8 -8
  108. package/dist/core/graph/exposures.d.ts +6 -6
  109. package/dist/core/graph/exposures.js +4 -4
  110. package/dist/core/graph/jobs.d.ts +6 -8
  111. package/dist/core/graph/jobs.js +2 -2
  112. package/dist/core/graph/kinds/action.js +1 -1
  113. package/dist/core/graph/kinds/custom.js +2 -2
  114. package/dist/core/graph/kinds/document.d.ts +1 -0
  115. package/dist/core/graph/kinds/document.js +4 -4
  116. package/dist/core/graph/kinds/dto.d.ts +2 -2
  117. package/dist/core/graph/kinds/dto.js +1 -1
  118. package/dist/core/graph/kinds/job.js +2 -2
  119. package/dist/core/graph/kinds/kind.d.ts +6 -8
  120. package/dist/core/graph/kinds/kind.js +1 -1
  121. package/dist/core/graph/kinds/node.js +5 -5
  122. package/dist/core/graph/kinds/owner.js +2 -2
  123. package/dist/core/graph/kinds/registry.d.ts +6 -9
  124. package/dist/core/graph/kinds/registry.js +1 -1
  125. package/dist/core/graph/names.d.ts +6 -6
  126. package/dist/core/graph/names.js +3 -3
  127. package/dist/core/graph/object-waits.d.ts +4 -7
  128. package/dist/core/graph/object-waits.js +1 -1
  129. package/dist/core/graph/objects.d.ts +14 -14
  130. package/dist/core/graph/objects.js +9 -9
  131. package/dist/core/graph/package-docs.d.ts +2 -7
  132. package/dist/core/graph/package-docs.js +5 -5
  133. package/dist/core/graph/package-kinds.d.ts +2 -5
  134. package/dist/core/graph/package-kinds.js +1 -1
  135. package/dist/core/graph/package-reconcile.d.ts +2 -5
  136. package/dist/core/graph/package-reconcile.js +1 -1
  137. package/dist/core/graph/reader.d.ts +18 -18
  138. package/dist/core/graph/reader.js +3 -3
  139. package/dist/core/graph/receivers.d.ts +4 -4
  140. package/dist/core/graph/receivers.js +5 -5
  141. package/dist/core/graph/repo-sync/exchange.d.ts +3 -7
  142. package/dist/core/graph/repo-sync/exchange.js +2 -2
  143. package/dist/core/graph/repo-sync/repos.d.ts +7 -5
  144. package/dist/core/graph/repo-sync/repos.js +3 -3
  145. package/dist/core/graph/repo-sync/sync.d.ts +3 -6
  146. package/dist/core/graph/repo-sync/sync.js +2 -2
  147. package/dist/core/graph/rules.d.ts +5 -5
  148. package/dist/core/graph/rules.js +3 -3
  149. package/dist/core/graph/spaces/app.js +1 -1
  150. package/dist/core/graph/spaces/index.d.ts +4 -5
  151. package/dist/core/graph/spaces/index.js +1 -1
  152. package/dist/core/graph/spaces/own-memory.d.ts +4 -4
  153. package/dist/core/graph/spaces/own-memory.js +1 -1
  154. package/dist/core/graph/spaces/person.js +3 -3
  155. package/dist/core/graph/spaces/repo.d.ts +1 -1
  156. package/dist/core/graph/spaces/repo.js +1 -1
  157. package/dist/core/graph/watches.d.ts +15 -15
  158. package/dist/core/graph/watches.js +10 -10
  159. package/dist/core/human/feedback-companion.d.ts +2 -2
  160. package/dist/core/human/feedback-companion.js +1 -1
  161. package/dist/core/human/feedback.d.ts +3 -3
  162. package/dist/core/human/feedback.js +1 -1
  163. package/dist/core/human/requests.d.ts +34 -33
  164. package/dist/core/human/requests.js +20 -20
  165. package/dist/core/human/scan.d.ts +2 -2
  166. package/dist/core/human/scan.js +1 -1
  167. package/dist/core/human/tickets.d.ts +9 -9
  168. package/dist/core/human/tickets.js +1 -1
  169. package/dist/core/llm/app-rungs.d.ts +1 -1
  170. package/dist/core/llm/app-rungs.js +1 -1
  171. package/dist/core/llm/run.js +1 -1
  172. package/dist/core/peer/directory.d.ts +1 -1
  173. package/dist/core/peer/directory.js +1 -1
  174. package/dist/core/peer/envelope.d.ts +3 -3
  175. package/dist/core/peer/envelope.js +1 -1
  176. package/dist/core/peer/home.d.ts +7 -7
  177. package/dist/core/peer/home.js +2 -2
  178. package/dist/core/peer/ingest.d.ts +6 -6
  179. package/dist/core/peer/ingest.js +1 -1
  180. package/dist/core/peer/outbox-store.d.ts +14 -14
  181. package/dist/core/peer/outbox-store.js +5 -5
  182. package/dist/core/peer/receiver.d.ts +2 -2
  183. package/dist/core/peer/receiver.js +1 -1
  184. package/dist/core/peer/stubs.d.ts +10 -10
  185. package/dist/core/peer/stubs.js +1 -1
  186. package/dist/core/providers/approval.d.ts +2 -2
  187. package/dist/core/providers/approval.js +2 -2
  188. package/dist/core/providers/audit.d.ts +3 -3
  189. package/dist/core/providers/audit.js +2 -2
  190. package/dist/core/providers/bundles.js +2 -2
  191. package/dist/core/providers/call.d.ts +2 -2
  192. package/dist/core/providers/call.js +1 -1
  193. package/dist/core/providers/files.d.ts +1 -1
  194. package/dist/core/providers/files.js +1 -1
  195. package/dist/core/providers/mint.js +1 -1
  196. package/dist/core/providers/receipts.d.ts +1 -1
  197. package/dist/core/providers/receipts.js +2 -2
  198. package/dist/core/review/birth.js +1 -1
  199. package/dist/core/review/comments.d.ts +19 -19
  200. package/dist/core/review/comments.js +5 -5
  201. package/dist/core/review/companion.d.ts +1 -1
  202. package/dist/core/review/companion.js +1 -1
  203. package/dist/core/review/document.d.ts +3 -3
  204. package/dist/core/review/document.js +2 -2
  205. package/dist/core/review/fork.js +2 -2
  206. package/dist/core/review/realize.js +1 -1
  207. package/dist/core/review/remap.d.ts +2 -2
  208. package/dist/core/review/remap.js +4 -4
  209. package/dist/core/review/stage.d.ts +1 -1
  210. package/dist/core/review/stage.js +1 -1
  211. package/dist/core/review/store.d.ts +21 -21
  212. package/dist/core/review/store.js +1 -1
  213. package/dist/core/runs/activity.d.ts +1 -1
  214. package/dist/core/runs/activity.js +5 -5
  215. package/dist/core/runs/confinement.d.ts +7 -7
  216. package/dist/core/runs/confinement.js +2 -2
  217. package/dist/core/runs/events.d.ts +12 -12
  218. package/dist/core/runs/events.js +4 -4
  219. package/dist/core/runs/operations.d.ts +11 -11
  220. package/dist/core/runs/operations.js +5 -5
  221. package/dist/core/runs/questions.d.ts +10 -10
  222. package/dist/core/runs/questions.js +3 -3
  223. package/dist/core/runtime/auth-reload.js +1 -1
  224. package/dist/core/runtime/bearings.d.ts +5 -5
  225. package/dist/core/runtime/bearings.js +6 -6
  226. package/dist/core/runtime/broker-inventory.js +1 -1
  227. package/dist/core/runtime/broker-request.js +1 -1
  228. package/dist/core/runtime/broker.js +1 -1
  229. package/dist/core/runtime/close.d.ts +3 -3
  230. package/dist/core/runtime/close.js +1 -1
  231. package/dist/core/runtime/deliver-live.js +1 -1
  232. package/dist/core/runtime/fleet.d.ts +2 -2
  233. package/dist/core/runtime/host.d.ts +3 -3
  234. package/dist/core/runtime/host.js +3 -3
  235. package/dist/core/runtime/kickoff.d.ts +12 -12
  236. package/dist/core/runtime/kickoff.js +17 -17
  237. package/dist/core/runtime/launch-target.d.ts +1 -1
  238. package/dist/core/runtime/launch-target.js +1 -1
  239. package/dist/core/runtime/ledger.d.ts +3 -3
  240. package/dist/core/runtime/ledger.js +3 -3
  241. package/dist/core/runtime/lifecycle.d.ts +1 -1
  242. package/dist/core/runtime/lifecycle.js +2 -2
  243. package/dist/core/runtime/memory-admission.js +1 -1
  244. package/dist/core/runtime/model-swap.d.ts +2 -2
  245. package/dist/core/runtime/model-swap.js +1 -1
  246. package/dist/core/runtime/node-read.d.ts +1 -1
  247. package/dist/core/runtime/node-read.js +2 -2
  248. package/dist/core/runtime/nodes.d.ts +6 -6
  249. package/dist/core/runtime/nodes.js +1 -1
  250. package/dist/core/runtime/outcome-document.d.ts +1 -1
  251. package/dist/core/runtime/outcome-document.js +1 -1
  252. package/dist/core/runtime/persona.d.ts +3 -3
  253. package/dist/core/runtime/persona.js +2 -2
  254. package/dist/core/runtime/placement.d.ts +11 -11
  255. package/dist/core/runtime/placement.js +1 -1
  256. package/dist/core/runtime/promote.d.ts +3 -3
  257. package/dist/core/runtime/promote.js +1 -1
  258. package/dist/core/runtime/recycle.js +1 -1
  259. package/dist/core/runtime/reopen.d.ts +21 -10
  260. package/dist/core/runtime/reopen.js +1 -1
  261. package/dist/core/runtime/reset.d.ts +4 -4
  262. package/dist/core/runtime/reset.js +1 -1
  263. package/dist/core/runtime/revive-all.d.ts +2 -2
  264. package/dist/core/runtime/revive-all.js +1 -1
  265. package/dist/core/runtime/revive.d.ts +4 -4
  266. package/dist/core/runtime/revive.js +2 -2
  267. package/dist/core/runtime/roadmap.d.ts +6 -6
  268. package/dist/core/runtime/roadmap.js +5 -5
  269. package/dist/core/runtime/session-branch.d.ts +1 -1
  270. package/dist/core/runtime/session-branch.js +1 -1
  271. package/dist/core/runtime/session-list-cache.js +1 -1
  272. package/dist/core/runtime/situational-live.js +1 -1
  273. package/dist/core/runtime/spawn.d.ts +2 -2
  274. package/dist/core/runtime/spawn.js +2 -2
  275. package/dist/core/runtime/stop-guard.d.ts +2 -2
  276. package/dist/core/runtime/stop-guard.js +3 -3
  277. package/dist/core/runtime/warm-pool.d.ts +2 -2
  278. package/dist/core/runtime/warm-pool.js +1 -1
  279. package/dist/core/spaces/mounts.d.ts +2 -2
  280. package/dist/core/spaces/mounts.js +1 -1
  281. package/dist/core/spaces/permissions.d.ts +1 -1
  282. package/dist/core/spaces/permissions.js +1 -1
  283. package/dist/core/spaces/process.d.ts +1 -1
  284. package/dist/core/spaces/process.js +1 -1
  285. package/dist/core/spaces/space.js +1 -1
  286. package/dist/core/spaces/stop.d.ts +3 -3
  287. package/dist/core/spaces/stop.js +2 -2
  288. package/dist/core/spaces/users.d.ts +6 -6
  289. package/dist/core/spaces/users.js +1 -1
  290. package/dist/core/storage/attachments.d.ts +2 -2
  291. package/dist/core/storage/attachments.js +1 -1
  292. package/dist/core/storage/shares.d.ts +2 -2
  293. package/dist/core/storage/shares.js +1 -1
  294. package/dist/core/storage/tables.d.ts +21 -21
  295. package/dist/core/storage/tables.js +3 -3
  296. package/dist/core/substrate/delivery/corpus.d.ts +5 -8
  297. package/dist/core/substrate/delivery/corpus.js +3 -3
  298. package/dist/core/substrate/delivery/deliver.d.ts +10 -15
  299. package/dist/core/substrate/delivery/deliver.js +3 -3
  300. package/dist/core/substrate/delivery/plan.d.ts +2 -12
  301. package/dist/core/substrate/delivery/plan.js +3 -3
  302. package/dist/core/substrate/delivery/render-boot.d.ts +4 -4
  303. package/dist/core/substrate/delivery/render-boot.js +18 -18
  304. package/dist/core/substrate/delivery/render-event.d.ts +7 -14
  305. package/dist/core/substrate/delivery/render-event.js +4 -4
  306. package/dist/core/substrate/subject.d.ts +2 -2
  307. package/dist/core/substrate/subject.js +1 -1
  308. package/dist/core/usage/ledger.d.ts +6 -6
  309. package/dist/core/usage/ledger.js +4 -4
  310. package/dist/core/worktree-close.js +4 -4
  311. package/dist/core/worktree-mutation-async.js +7 -7
  312. package/dist/core/worktree-quarantine.js +1 -1
  313. package/dist/core/worktree-sweep.d.ts +1 -1
  314. package/dist/core/worktree-sweep.js +6 -6
  315. package/dist/core/worktree.d.ts +5 -5
  316. package/dist/core/worktree.js +13 -13
  317. package/dist/daemon/analytics/compute.d.ts +2 -2
  318. package/dist/daemon/analytics/compute.js +1 -1
  319. package/dist/daemon/api/app-listener.d.ts +4 -4
  320. package/dist/daemon/api/app-listener.js +13 -13
  321. package/dist/daemon/api/bridge.js +5 -5
  322. package/dist/daemon/api/document-listener.js +6 -6
  323. package/dist/daemon/api/handlers/analytics.js +1 -1
  324. package/dist/daemon/api/handlers/attach.js +1 -1
  325. package/dist/daemon/api/handlers/bash-jobs.js +1 -1
  326. package/dist/daemon/api/handlers/bash.js +1 -1
  327. package/dist/daemon/api/handlers/broker-ops.d.ts +1 -1
  328. package/dist/daemon/api/handlers/broker-ops.js +1 -1
  329. package/dist/daemon/api/handlers/broker-recovery.d.ts +1 -1
  330. package/dist/daemon/api/handlers/broker-recovery.js +1 -1
  331. package/dist/daemon/api/handlers/broker-signals.d.ts +2 -2
  332. package/dist/daemon/api/handlers/broker-signals.js +2 -2
  333. package/dist/daemon/api/handlers/canvas.d.ts +1 -1
  334. package/dist/daemon/api/handlers/canvas.js +4 -4
  335. package/dist/daemon/api/handlers/chat-inventory.js +1 -1
  336. package/dist/daemon/api/handlers/crons.js +1 -1
  337. package/dist/daemon/api/handlers/custom-objects.d.ts +4 -4
  338. package/dist/daemon/api/handlers/custom-objects.js +3 -3
  339. package/dist/daemon/api/handlers/delivery-retry.js +1 -1
  340. package/dist/daemon/api/handlers/delivery.js +1 -1
  341. package/dist/daemon/api/handlers/docs.d.ts +4 -4
  342. package/dist/daemon/api/handlers/docs.js +1 -1
  343. package/dist/daemon/api/handlers/feedback-comments.js +3 -3
  344. package/dist/daemon/api/handlers/files.js +1 -1
  345. package/dist/daemon/api/handlers/focus.js +1 -1
  346. package/dist/daemon/api/handlers/health.js +3 -3
  347. package/dist/daemon/api/handlers/hook-exec.js +1 -1
  348. package/dist/daemon/api/handlers/human-requests.js +1 -1
  349. package/dist/daemon/api/handlers/inbox-page.d.ts +1 -1
  350. package/dist/daemon/api/handlers/inbox-page.js +1 -1
  351. package/dist/daemon/api/handlers/inbox.d.ts +1 -1
  352. package/dist/daemon/api/handlers/inbox.js +1 -1
  353. package/dist/daemon/api/handlers/llm.d.ts +1 -1
  354. package/dist/daemon/api/handlers/llm.js +3 -3
  355. package/dist/daemon/api/handlers/memory-reads.js +1 -1
  356. package/dist/daemon/api/handlers/memory.d.ts +5 -5
  357. package/dist/daemon/api/handlers/memory.js +5 -5
  358. package/dist/daemon/api/handlers/messages.js +2 -2
  359. package/dist/daemon/api/handlers/node-events.d.ts +2 -2
  360. package/dist/daemon/api/handlers/node-events.js +3 -3
  361. package/dist/daemon/api/handlers/node-outcomes.js +1 -1
  362. package/dist/daemon/api/handlers/node-records.js +2 -2
  363. package/dist/daemon/api/handlers/nodes.d.ts +1 -1
  364. package/dist/daemon/api/handlers/nodes.js +1 -1
  365. package/dist/daemon/api/handlers/objects.js +3 -3
  366. package/dist/daemon/api/handlers/package-docs.js +1 -1
  367. package/dist/daemon/api/handlers/plugin-app-token.d.ts +1 -1
  368. package/dist/daemon/api/handlers/plugin-app-token.js +1 -1
  369. package/dist/daemon/api/handlers/profiles.d.ts +3 -3
  370. package/dist/daemon/api/handlers/profiles.js +1 -1
  371. package/dist/daemon/api/handlers/providers.d.ts +1 -1
  372. package/dist/daemon/api/handlers/providers.js +1 -1
  373. package/dist/daemon/api/handlers/questions.js +1 -1
  374. package/dist/daemon/api/handlers/reports.d.ts +1 -1
  375. package/dist/daemon/api/handlers/reports.js +5 -5
  376. package/dist/daemon/api/handlers/review-comments.js +1 -1
  377. package/dist/daemon/api/handlers/reviews.js +1 -1
  378. package/dist/daemon/api/handlers/runs.js +1 -1
  379. package/dist/daemon/api/handlers/structured-requests.js +1 -1
  380. package/dist/daemon/api/handlers/subscriptions.js +1 -1
  381. package/dist/daemon/api/handlers/transfer.js +1 -1
  382. package/dist/daemon/api/map.d.ts +3 -3
  383. package/dist/daemon/api/map.js +1 -1
  384. package/dist/daemon/api/principal.d.ts +1 -1
  385. package/dist/daemon/api/principal.js +1 -1
  386. package/dist/daemon/api/reader.d.ts +5 -5
  387. package/dist/daemon/api/reader.js +1 -1
  388. package/dist/daemon/api/router.d.ts +5 -5
  389. package/dist/daemon/api/router.js +1 -1
  390. package/dist/daemon/api/server.js +1 -1
  391. package/dist/daemon/broker-signals/bus.d.ts +1 -1
  392. package/dist/daemon/broker-signals/bus.js +1 -1
  393. package/dist/daemon/companion-retire.js +1 -1
  394. package/dist/daemon/cron/armed-context.d.ts +2 -2
  395. package/dist/daemon/cron/armed-context.js +1 -1
  396. package/dist/daemon/cron/authority.d.ts +2 -2
  397. package/dist/daemon/cron/authority.js +1 -1
  398. package/dist/daemon/cron/disposition.js +1 -1
  399. package/dist/daemon/cron/ended.d.ts +1 -1
  400. package/dist/daemon/cron/ended.js +2 -2
  401. package/dist/daemon/cron/escalation.js +3 -3
  402. package/dist/daemon/cron/folders.d.ts +1 -1
  403. package/dist/daemon/cron/folders.js +1 -1
  404. package/dist/daemon/cron/held.d.ts +1 -1
  405. package/dist/daemon/cron/held.js +1 -1
  406. package/dist/daemon/cron/lease-recovery.d.ts +1 -1
  407. package/dist/daemon/cron/lease-recovery.js +1 -1
  408. package/dist/daemon/cron/passes.d.ts +2 -2
  409. package/dist/daemon/cron/passes.js +1 -1
  410. package/dist/daemon/cron/run-socket.d.ts +1 -1
  411. package/dist/daemon/cron/run-socket.js +1 -1
  412. package/dist/daemon/cron/settle.js +1 -1
  413. package/dist/daemon/cron/sinks.d.ts +1 -1
  414. package/dist/daemon/cron/sinks.js +2 -2
  415. package/dist/daemon/cron-run.d.ts +12 -3
  416. package/dist/daemon/cron-run.js +2 -2
  417. package/dist/daemon/crtrd.js +6 -6
  418. package/dist/daemon/delivery/run-delivery.js +1 -1
  419. package/dist/daemon/due-sources.d.ts +3 -3
  420. package/dist/daemon/due-sources.js +1 -1
  421. package/dist/daemon/due.d.ts +3 -3
  422. package/dist/daemon/due.js +7 -7
  423. package/dist/daemon/fleet.d.ts +2 -2
  424. package/dist/daemon/fleet.js +11 -11
  425. package/dist/daemon/human/finish.js +3 -3
  426. package/dist/daemon/human/settle.d.ts +1 -1
  427. package/dist/daemon/human/settle.js +1 -1
  428. package/dist/daemon/human/sweep.js +1 -1
  429. package/dist/daemon/joined-owner.d.ts +3 -3
  430. package/dist/daemon/joined-owner.js +1 -1
  431. package/dist/daemon/messaging/node-message.js +1 -1
  432. package/dist/daemon/park-activity.d.ts +2 -2
  433. package/dist/daemon/park-activity.js +1 -1
  434. package/dist/daemon/peer/authority.js +1 -1
  435. package/dist/daemon/peer/forward.d.ts +6 -6
  436. package/dist/daemon/peer/forward.js +1 -1
  437. package/dist/daemon/peer/handlers.js +1 -1
  438. package/dist/daemon/peer/ingress.d.ts +4 -4
  439. package/dist/daemon/peer/ingress.js +1 -1
  440. package/dist/daemon/peer/outbox.d.ts +2 -2
  441. package/dist/daemon/peer/outbox.js +1 -1
  442. package/dist/daemon/peer/relay-service.js +1 -1
  443. package/dist/daemon/plugin-objects/read-leaf.d.ts +2 -2
  444. package/dist/daemon/plugin-objects/read-leaf.js +2 -2
  445. package/dist/daemon/profile-delete.js +1 -1
  446. package/dist/daemon/readiness.d.ts +2 -2
  447. package/dist/daemon/readiness.js +1 -1
  448. package/dist/daemon/reconcilers/action-lane.d.ts +1 -1
  449. package/dist/daemon/reconcilers/action-lane.js +1 -1
  450. package/dist/daemon/reconcilers/bash-deadline.js +2 -2
  451. package/dist/daemon/reconcilers/broker-supervision.d.ts +1 -1
  452. package/dist/daemon/reconcilers/broker-supervision.js +4 -4
  453. package/dist/daemon/reconcilers/controller-death.d.ts +1 -1
  454. package/dist/daemon/reconcilers/controller-death.js +1 -1
  455. package/dist/daemon/reconcilers/cron-lane.d.ts +1 -1
  456. package/dist/daemon/reconcilers/cron-lane.js +1 -1
  457. package/dist/daemon/reconcilers/delivery-lane.d.ts +1 -1
  458. package/dist/daemon/reconcilers/delivery-lane.js +2 -2
  459. package/dist/daemon/reconcilers/fault-retry-backstop.d.ts +3 -3
  460. package/dist/daemon/reconcilers/fault-retry-backstop.js +1 -1
  461. package/dist/daemon/reconcilers/grove-instance-sweep.d.ts +2 -2
  462. package/dist/daemon/reconcilers/grove-instance-sweep.js +1 -1
  463. package/dist/daemon/reconcilers/live-obligation.d.ts +6 -6
  464. package/dist/daemon/reconcilers/live-obligation.js +1 -1
  465. package/dist/daemon/reconcilers/managed-worktree-sweep.js +1 -1
  466. package/dist/daemon/reconcilers/node-deadline.d.ts +1 -1
  467. package/dist/daemon/reconcilers/node-deadline.js +1 -1
  468. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +1 -1
  469. package/dist/daemon/reconcilers/node-lifecycle/respawn-policy.d.ts +3 -3
  470. package/dist/daemon/reconcilers/node-lifecycle/respawn-policy.js +1 -1
  471. package/dist/daemon/reconcilers/node-lifecycle/terminating.js +1 -1
  472. package/dist/daemon/reconcilers/node-lifecycle/tick.js +1 -1
  473. package/dist/daemon/reconcilers/node-lifecycle/wants-execution.d.ts +1 -1
  474. package/dist/daemon/reconcilers/node-lifecycle/wants-execution.js +1 -1
  475. package/dist/daemon/reconcilers/pending-review-submit.d.ts +1 -1
  476. package/dist/daemon/reconcilers/pending-review-submit.js +1 -1
  477. package/dist/daemon/reconcilers/storage-due-work.js +1 -1
  478. package/dist/daemon/reconcilers/storage-maintenance.d.ts +1 -1
  479. package/dist/daemon/reconcilers/storage-maintenance.js +2 -2
  480. package/dist/daemon/reconcilers/structured-request-lane.d.ts +1 -1
  481. package/dist/daemon/reconcilers/structured-request-lane.js +1 -1
  482. package/dist/daemon/review/comment-notify.js +2 -2
  483. package/dist/daemon/review/companion.d.ts +3 -3
  484. package/dist/daemon/review/companion.js +1 -1
  485. package/dist/daemon/review/finish.js +1 -1
  486. package/dist/daemon/review/sweep.js +1 -1
  487. package/dist/daemon/run-notices.d.ts +4 -4
  488. package/dist/daemon/run-notices.js +1 -1
  489. package/dist/daemon/runs/append-section.d.ts +2 -2
  490. package/dist/daemon/runs/append-section.js +2 -2
  491. package/dist/daemon/runs/observer.d.ts +2 -2
  492. package/dist/daemon/runs/observer.js +2 -2
  493. package/dist/daemon/structured-requests/boot.d.ts +1 -1
  494. package/dist/daemon/structured-requests/boot.js +1 -1
  495. package/dist/daemon/structured-requests/effects.js +1 -1
  496. package/dist/daemon/structured-requests/sidecar.d.ts +1 -1
  497. package/dist/daemon/structured-requests/sidecar.js +1 -1
  498. package/dist/launcher/run.js +1 -1
  499. package/dist/migrations/004-canvas-documents/apply.d.ts +3 -3
  500. package/dist/migrations/004-canvas-documents/apply.js +1 -1
  501. package/dist/migrations/004-canvas-documents/index.d.ts +2 -2
  502. package/dist/migrations/004-canvas-documents/index.js +2 -2
  503. package/dist/migrations/activation.js +2 -2
  504. package/docs/plugin/bundles-and-memory.md +1 -1
  505. package/docs/plugin/commands.md +1 -1
  506. package/docs/public-api.md +11 -0
  507. package/package.json +4 -3
  508. package/packages/crouter-identity/package.json +1 -1
  509. package/runtime.lock.json +12 -12
@@ -1,4 +1,4 @@
1
- import type { DatabaseSync } from 'node:sqlite';
1
+ import { type CanvasDb } from '../canvas/db.js';
2
2
  import type { SpendUsageDTO } from '../../api/dto/usage.js';
3
3
  /** The person's own agent. Its grant carries the directory's default cap, but the
4
4
  * cap bounds what an app spends of the person's subscription, never the person's
@@ -23,15 +23,15 @@ export interface UsageRecord {
23
23
  usd_equivalent: number | null;
24
24
  }
25
25
  /** Add one completion's or receipt's usage to its grantee's month, inside the caller's write. */
26
- export declare function addUsage(db: DatabaseSync, record: UsageRecord, at?: Date): void;
26
+ export declare function addUsage(db: CanvasDb, record: UsageRecord, at?: Date): Promise<void>;
27
27
  /** Record one model completion for `grantee`. */
28
28
  export declare function recordModelUsage(grantee: string, payer: Payer, servedModel: string, usage: {
29
29
  input: number;
30
30
  output: number;
31
31
  cacheRead: number;
32
- }, usdEquivalent: number | null): void;
32
+ }, usdEquivalent: number | null): Promise<void>;
33
33
  /** The grantee's user-paid month-to-date model spend; provider receipts are not in it. */
34
- export declare function monthSpend(grantee: string, month?: string): number;
34
+ export declare function monthSpend(grantee: string, month?: string): Promise<number>;
35
35
  /** Who a user-paid call bills, and the cap that bounds it (`null` = uncapped). */
36
36
  export interface SpendTarget {
37
37
  grantee: string;
@@ -42,8 +42,8 @@ export declare function effectiveCap(target: SpendTarget): number | null;
42
42
  /** Refuse a user-paid call once the grantee's month spend reached its cap (SPD-2).
43
43
  * The check runs before the call; one response may overshoot the cap (no
44
44
  * reserve-then-settle), and the next call is refused. */
45
- export declare function assertUnderCap(target: SpendTarget | undefined, at?: Date): void;
45
+ export declare function assertUnderCap(target: SpendTarget | undefined, at?: Date): Promise<void>;
46
46
  /** `usage.get` for one grantee and month. */
47
- export declare function usageFor(grantee: string, spendCap: number | null, month?: string): SpendUsageDTO;
47
+ export declare function usageFor(grantee: string, spendCap: number | null, month?: string): Promise<SpendUsageDTO>;
48
48
  /** A `month` query value: `YYYY-MM` with a real month, years 1000-9999; absent means the current month. */
49
49
  export declare function parseUsageMonth(value: string | null): string;
@@ -1,10 +1,10 @@
1
- var q=Object.defineProperty;var s=(e,t)=>q(e,"name",{value:t,configurable:!0});import{prepared as i}from"../sqlite-statements.js";import{openDb as _,withCanvasWrite as k}from"../canvas/db.js";import{RunError as l}from"../runs/operations.js";const N="app:terminal";function p(e=new Date){return e.toISOString().slice(0,7)}s(p,"usageMonth");function c(e){const[t,n]=e.split("-").map(Number);return new Date(Date.UTC(t,n,1)).toISOString()}s(c,"resetAt");function S(e,t,n=new Date){const u=p(n);i(e,`INSERT INTO usage (grantee, month, payer, source, served_model, input_tokens, output_tokens, cached_tokens, usd_equivalent)
1
+ var h=Object.defineProperty;var a=(e,t)=>h(e,"name",{value:t,configurable:!0});import{withCanvasWrite as q,openDb as d}from"../canvas/db.js";import{RunError as _}from"../runs/operations.js";const w="app:terminal";function r(e=new Date){return e.toISOString().slice(0,7)}a(r,"usageMonth");function l(e){const[t,n]=e.split("-").map(Number);return new Date(Date.UTC(t,n,1)).toISOString()}a(l,"resetAt");async function k(e,t,n=new Date){const u=r(n);await e.prepare(`INSERT INTO usage (grantee, month, payer, source, served_model, input_tokens, output_tokens, cached_tokens, usd_equivalent)
2
2
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
3
3
  ON CONFLICT(grantee, month, payer, source, served_model) DO UPDATE SET
4
4
  input_tokens = input_tokens + excluded.input_tokens,
5
5
  output_tokens = output_tokens + excluded.output_tokens,
6
6
  cached_tokens = cached_tokens + excluded.cached_tokens,
7
7
  usd_equivalent = CASE WHEN usd_equivalent IS NULL AND excluded.usd_equivalent IS NULL THEN NULL
8
- ELSE COALESCE(usd_equivalent, 0) + COALESCE(excluded.usd_equivalent, 0) END`).run(t.grantee,u,t.payer,t.source,t.served_model,t.input_tokens,t.output_tokens,t.cached_tokens,t.usd_equivalent),t.payer==="user"&&t.source==="model"&&t.usd_equivalent!==null&&t.usd_equivalent>0&&i(e,`INSERT INTO grantee_usage (grantee, month, usd_equivalent) VALUES (?, ?, ?)
9
- ON CONFLICT(grantee, month) DO UPDATE SET usd_equivalent = usd_equivalent + excluded.usd_equivalent`).run(t.grantee,u,t.usd_equivalent)}s(S,"addUsage");function L(e,t,n,u,o){k(d=>S(d,{grantee:e,payer:t,source:"model",served_model:n,input_tokens:u.input,output_tokens:u.output,cached_tokens:u.cacheRead,usd_equivalent:o}))}s(L,"recordModelUsage");function m(e,t=p()){return i(_(),"SELECT usd_equivalent FROM grantee_usage WHERE grantee = ? AND month = ?").get(e,t)?.usd_equivalent??0}s(m,"monthSpend");function E(e){return e.grantee===N?null:e.spend_cap}s(E,"effectiveCap");function O(e,t=new Date){if(!e)return;const n=E(e);if(n===null)return;const u=p(t),o=m(e.grantee,u);if(!(o<n))throw new l(402,"cap_exhausted",`${e.grantee} reached its monthly usage limit`,void 0,{spend_cap:n,usd_equivalent:o,reset_at:c(u)})}s(O,"assertUnderCap");function T(e,t,n=p()){const u=i(_(),`SELECT payer, source, served_model, input_tokens, output_tokens, usd_equivalent
10
- FROM usage WHERE grantee = ? AND month = ? ORDER BY served_model`).all(e,n),o=s(r=>u.filter(a=>a.payer===r&&a.source==="model").map(({served_model:a,input_tokens:v,output_tokens:f,usd_equivalent:h})=>({served_model:a,input_tokens:v,output_tokens:f,usd_equivalent:h})),"byModel"),d=u.filter(r=>r.payer==="app").reduce((r,a)=>r+(a.usd_equivalent??0),0);return{grantee:e,month:n,usd_equivalent:m(e,n),by_model:o("user"),app_paid:{usd_equivalent:d,by_model:o("app")},spend_cap:E({grantee:e,spend_cap:t}),reset_at:c(n)}}s(T,"usageFor");function A(e){if(e===null)return p();if(!/^[1-9]\d{3}-(0[1-9]|1[0-2])$/.test(e))throw new l(400,"invalid_request","month must be YYYY-MM","month");return e}s(A,"parseUsageMonth");export{N as UNCAPPED_GRANTEE,S as addUsage,O as assertUnderCap,E as effectiveCap,m as monthSpend,A as parseUsageMonth,L as recordModelUsage,c as resetAt,T as usageFor,p as usageMonth};
8
+ ELSE COALESCE(usd_equivalent, 0) + COALESCE(excluded.usd_equivalent, 0) END`).run(t.grantee,u,t.payer,t.source,t.served_model,t.input_tokens,t.output_tokens,t.cached_tokens,t.usd_equivalent),t.payer==="user"&&t.source==="model"&&t.usd_equivalent!==null&&t.usd_equivalent>0&&await e.prepare(`INSERT INTO grantee_usage (grantee, month, usd_equivalent) VALUES (?, ?, ?)
9
+ ON CONFLICT(grantee, month) DO UPDATE SET usd_equivalent = usd_equivalent + excluded.usd_equivalent`).run(t.grantee,u,t.usd_equivalent)}a(k,"addUsage");async function g(e,t,n,u,s){await q(async p=>await k(p,{grantee:e,payer:t,source:"model",served_model:n,input_tokens:u.input,output_tokens:u.output,cached_tokens:u.cacheRead,usd_equivalent:s}))}a(g,"recordModelUsage");async function c(e,t=r()){return(await(await d()).prepare("SELECT usd_equivalent FROM grantee_usage WHERE grantee = ? AND month = ?").get(e,t))?.usd_equivalent??0}a(c,"monthSpend");function m(e){return e.grantee===w?null:e.spend_cap}a(m,"effectiveCap");async function x(e,t=new Date){if(!e)return;const n=m(e);if(n===null)return;const u=r(t),s=await c(e.grantee,u);if(!(s<n))throw new _(402,"cap_exhausted",`${e.grantee} reached its monthly usage limit`,void 0,{spend_cap:n,usd_equivalent:s,reset_at:l(u)})}a(x,"assertUnderCap");async function C(e,t,n=r()){const u=await(await d()).prepare(`SELECT payer, source, served_model, input_tokens, output_tokens, usd_equivalent
10
+ FROM usage WHERE grantee = ? AND month = ? ORDER BY served_model`).all(e,n),s=a(i=>u.filter(o=>o.payer===i&&o.source==="model").map(({served_model:o,input_tokens:E,output_tokens:v,usd_equivalent:f})=>({served_model:o,input_tokens:E,output_tokens:v,usd_equivalent:f})),"byModel"),p=u.filter(i=>i.payer==="app").reduce((i,o)=>i+(o.usd_equivalent??0),0);return{grantee:e,month:n,usd_equivalent:await c(e,n),by_model:s("user"),app_paid:{usd_equivalent:p,by_model:s("app")},spend_cap:m({grantee:e,spend_cap:t}),reset_at:l(n)}}a(C,"usageFor");function D(e){if(e===null)return r();if(!/^[1-9]\d{3}-(0[1-9]|1[0-2])$/.test(e))throw new _(400,"invalid_request","month must be YYYY-MM","month");return e}a(D,"parseUsageMonth");export{w as UNCAPPED_GRANTEE,k as addUsage,x as assertUnderCap,m as effectiveCap,c as monthSpend,D as parseUsageMonth,g as recordModelUsage,l as resetAt,C as usageFor,r as usageMonth};
@@ -1,6 +1,6 @@
1
- var P=Object.defineProperty;var c=(t,r)=>P(t,"name",{value:r,configurable:!0});import{existsSync as $,realpathSync as R}from"node:fs";import{resolve as q}from"node:path";import{getNode as v,recordDisposalGrant as B,recordLandIntent as M}from"./canvas/index.js";import{withExclusiveDirectoryLockAsync as O}from"./exclusive-lock.js";import{gitAsync as i}from"./git.js";import{proveBranchContained as x}from"./worktree-containment.js";import{concludeAbsentManagedWorktreeAsync as F}from"./worktree-mutation-async.js";import{parseStatusPorcelain as G,planLandIntoCheckout as I}from"./worktree-landing.js";import{WORKTREE_LOCK_WAIT_MS as T,checkoutPathForBranchInList as N,missingWorktreeRecoveryHint as j,persistClosedWorktreeState as A,samePath as K,worktreeLockPathForCommonDir as z,WorktreeError as h}from"./worktree.js";import{shellQuote as E}from"../shared/shell-quote.js";function p(t){return(t.stderr.trim()||t.stdout.trim()).trim()}c(p,"detailOf");async function u(t,r,e,o){const a=await i(r,t);if(a.status!==0)throw new h(e,`git ${r.join(" ")} failed`,o,p(a));return a.stdout.trim()}c(u,"runGit");async function H(t){const r=await u(t,["rev-parse","--git-common-dir"],"git_common_dir_failed","Inspect the repository git metadata, then retry.");return R(q(t,r))}c(H,"commonGitDir");async function Q(t,r){const e=`refs/heads/${r}`,o=await i(["show-ref","--verify","--quiet",e],t);if(o.status===1&&o.executionFailed!==!0)throw new h("missing_base_branch",`managed worktree base '${r}' does not name an existing local branch`,"The base must be a local branch that landing can fast-forward \u2014 freshen your local branch from origin first (rebase-not-merge), then base on it.");if(o.status!==0){const a=o.processError===void 0?"":`
1
+ var P=Object.defineProperty;var c=(t,r)=>P(t,"name",{value:r,configurable:!0});import{existsSync as $,realpathSync as R}from"node:fs";import{resolve as q}from"node:path";import{getNode as v,recordDisposalGrant as B,recordLandIntent as M}from"./canvas/index.js";import{withExclusiveDirectoryLockAsync as O}from"./exclusive-lock.js";import{gitAsync as i}from"./git.js";import{proveBranchContained as x}from"./worktree-containment.js";import{concludeAbsentManagedWorktreeAsync as F}from"./worktree-mutation-async.js";import{parseStatusPorcelain as G,planLandIntoCheckout as I}from"./worktree-landing.js";import{WORKTREE_LOCK_WAIT_MS as T,checkoutPathForBranchInList as N,missingWorktreeRecoveryHint as j,persistClosedWorktreeState as A,samePath as K,worktreeLockPathForCommonDir as z,WorktreeError as h}from"./worktree.js";import{shellQuote as E}from"../shared/shell-quote.js";function w(t){return(t.stderr.trim()||t.stdout.trim()).trim()}c(w,"detailOf");async function u(t,r,e,o){const a=await i(r,t);if(a.status!==0)throw new h(e,`git ${r.join(" ")} failed`,o,w(a));return a.stdout.trim()}c(u,"runGit");async function H(t){const r=await u(t,["rev-parse","--git-common-dir"],"git_common_dir_failed","Inspect the repository git metadata, then retry.");return R(q(t,r))}c(H,"commonGitDir");async function Q(t,r){const e=`refs/heads/${r}`,o=await i(["show-ref","--verify","--quiet",e],t);if(o.status===1&&o.executionFailed!==!0)throw new h("missing_base_branch",`managed worktree base '${r}' does not name an existing local branch`,"The base must be a local branch that landing can fast-forward \u2014 freshen your local branch from origin first (rebase-not-merge), then base on it.");if(o.status!==0){const a=o.processError===void 0?"":`
2
2
  process error: ${o.processError}`;throw new h("base_branch_inspection_failed",`could not inspect local base branch '${r}' (git show-ref exited with status ${o.status})`,"Inspect the repository and its Git metadata, then retry.",`status: ${o.status}
3
3
  stderr: ${o.stderr.trim()||"<empty>"}${a}`)}return u(t,["rev-parse","--verify",e],"base_branch_resolve_failed","Inspect the local base branch, then retry.")}c(Q,"resolveLandableBase");async function J(t){for(const r of["rebase-merge","rebase-apply"]){const e=await u(t,["rev-parse","--git-path",r],"git_path_failed","Inspect the worktree manually, then retry.");if($(e))return!0}return!1}c(J,"rebaseInProgress");async function L(t,r,e){const o=await i(["worktree","list","--porcelain"],t);if(o.status!==0)return!1;let a=null;for(const n of o.stdout.split(`
4
- `))if(n.startsWith("worktree "))a=n.slice(9).trim();else if(a!==null&&K(a,r)&&n===`branch refs/heads/${e}`)return!0;return!1}c(L,"registeredWorktree");function D(t){return{worktree_removed:!1,worktree_remove_error:`cleanup pending: the caller checkout at ${t.path} remains intact. After its node exits, run \`git -C ${E(t.repo_root)} worktree remove ${E(t.path)}\` after confirming it is safe to delete.`,branch_deleted:!1,branch_delete_error:`cleanup pending: local branch ${t.branch} remains checked out at ${t.path}; delete it only after that checkout is removed and its landed commit is still contained in ${t.base_ref}.`}}c(D,"deferredCleanupResult");function U(t,r){const e=v(t)?.managed_worktree;e==null||e.state!=="open"||M(t,{at:new Date().toISOString(),blocked:r})}c(U,"recordBlockedLandIntent");async function W(t,r){const e=await i(["rev-parse","--verify","--quiet","refs/stash"],t);e.status===0&&e.stdout.trim()===r&&await i(["stash","drop"],t)}c(W,"dropStashIfStillTip");async function V(t,r,e,o){const a=await i(["status","--porcelain"],t);if(a.status!==0)return{ok:!1,detail:p(a)};const n=await i(["diff","--name-only",e,o],t);if(n.status!==0)return{ok:!1,detail:p(n)};const f=n.stdout.split(`
5
- `).map(_=>_.trim()).filter(_=>_!==""),k=I(G(a.stdout),f);if(k.kind==="refuse")return{ok:!1,detail:`untracked files in ${t} would be overwritten by the incoming commits: ${k.collisions.join(", ")}`};const w=c(async()=>{const _=await i(["merge","--ff-only",o],t);return _.status===0?{ok:!0}:{ok:!1,detail:p(_)}},"merge");if(k.kind==="direct")return w();const b=await i(["rev-parse","--verify","--quiet","refs/stash"],t),g=b.status===0?b.stdout.trim():null,y=await i(["stash","push","--message",`crtr landing ${r}`],t);if(y.status!==0)return{ok:!1,detail:p(y)};const m=await i(["rev-parse","--verify","--quiet","refs/stash"],t),l=m.status===0?m.stdout.trim():null;if(l===null||l===g)return w();const s=`local changes from ${t} are retained in stash ${l}; recover them with \`git -C ${E(t)} stash apply ${l}\` and resolve any conflict.`,d=await w();return d.ok?(await i(["stash","apply",l],t)).status===0?(await W(t,l),{ok:!0}):(await i(["reset","--hard",o],t),{ok:!0,stash:`the shared base checkout was reset to landed commit ${o} because local changes conflicted with it. ${s}`}):(await i(["stash","apply",l],t)).status===0?(await W(t,l),d):{ok:!1,detail:`${d.detail}
6
- ${s}`}}c(V,"landIntoBaseCheckout");async function S(t){const r=v(t);if(r===null)throw new h("unknown_node",`unknown node: ${t}`,"Run this from a live node, or pass a real node id once that command supports it.");const e=r?.managed_worktree?.state==="open"?r.managed_worktree:null;if(e===null)throw new h("no_managed_worktree",`node ${t} has no open managed worktree`,"Nothing to close for this node.");if(!$(e.path))throw new h("worktree_missing",`managed worktree path is missing: ${e.path}`,j(e));if(await J(e.path))throw new h("rebase_in_progress","this managed worktree is mid-rebase",`Resolve conflicts in ${e.path}, run \`git rebase --continue\` there, then rerun \`crtr node worktree close\`.`);if(await u(e.path,["status","--porcelain"],"status_failed","Inspect the worktree manually, then retry.")!=="")throw new h("dirty_worktree","managed worktree has uncommitted changes",`Commit the work in ${e.path}, then rerun \`crtr node worktree close\`.`);const a=await u(e.path,["rev-parse","--abbrev-ref","HEAD"],"branch_check_failed","Inspect the worktree manually, then retry."),n=await u(e.path,["rev-parse","HEAD"],"landed_sha_failed","Inspect the worktree checkout manually, then rerun `crtr node worktree close`.");if(a!==e.branch){if((await i(["show-ref","--verify","--quiet",`refs/heads/${e.branch}`],e.path)).status===0||a==="HEAD"||!await L(e.repo_root,e.path,a))throw new h("wrong_branch",`managed worktree is not on its recorded branch (expected ${e.branch}, found ${a==="HEAD"?"detached HEAD":a})`,`Inspect ${e.path}, then rerun \`crtr node worktree close\`.`);if(await x(e.path,e,n)===null)throw new h("branch_moved_unlanded",`managed worktree's recorded branch ${e.branch} no longer exists; its checkout is now on ${a} at ${n}, which carries content local base branch '${e.base_ref}' does not yet contain`,`Land or push ${a} through its own workflow before closing.`);const d=await H(e.path);if(!await L(e.repo_root,e.path,a)||await u(e.path,["rev-parse","--abbrev-ref","HEAD"],"branch_check_failed","Inspect the worktree manually, then retry.")!==a||await u(e.path,["rev-parse","HEAD"],"branch_check_failed","Inspect the worktree manually, then retry.")!==n)throw new h("wrong_branch",`managed worktree at ${e.path} changed while closing`,`Inspect ${e.path}, then rerun \`crtr node worktree close\`.`);return B(t,{observed_branch:a,observed_sha:n,common_dir:d,authorized:new Date().toISOString()}),A(t,e,"pending"),{node_id:t,branch:a,worktree_path:e.path,landed_sha:n,...D(e),branch_delete_error:`${a} is not a crtr-managed branch and will never be deleted automatically; it is preserved once the checkout above is removed.`}}if(await x(e.path,e,n)!==null)return A(t,e,"pending"),{node_id:t,branch:e.branch,worktree_path:e.path,landed_sha:n,...D(e)};let f;try{f=await Q(e.path,e.base_ref)}catch(s){if(!(s instanceof h)||s.code!=="missing_base_branch"||n!==e.base_sha)throw s;return A(t,e,"pending"),{node_id:t,branch:e.branch,worktree_path:e.path,landed_sha:e.base_sha,...D(e)}}const k=await i(["rebase",f],e.path);if(k.status!==0)throw new h("rebase_failed",`rebase onto local base branch '${e.base_ref}' did not complete`,`Resolve conflicts in ${e.path}, run \`git rebase --continue\` there, then rerun \`crtr node worktree close\`.`,p(k));const w=await u(e.path,["rev-parse","HEAD"],"landed_sha_failed","Inspect the worktree checkout manually, then rerun `crtr node worktree close`."),b=c((s,d,_)=>(U(t,s),new h("land_base_failed",s,d,_)),"blocked"),g=await i(["merge-base","--is-ancestor",f,w],e.path);if(g.status!==0)throw b(`rebased worktree is not a fast-forward of local base branch '${e.base_ref}'`,"Ask the user how to land this managed worktree.",p(g));const y=await u(e.path,["worktree","list","--porcelain"],"worktree_list_failed","Inspect the repository worktrees manually, then retry."),m=N(y,e.base_ref);let l;if(m!==null){const s=await i(["rev-parse","HEAD"],m);if(s.status!==0||s.stdout.trim()!==f)throw b(`local base branch '${e.base_ref}' changed while this worktree was closing`,`Reconcile the base checkout at ${m}, then rerun \`crtr node worktree close\`.`,s.status!==0?p(s):`expected ${f}, found ${s.stdout.trim()}`);const d=await V(m,e.branch,f,w);if(!d.ok)throw b(`could not fast-forward local base branch '${e.base_ref}' to this worktree`,`Local base branch '${e.base_ref}' is checked out at ${m} in a state that blocks a fast-forward.`,d.detail);l=d.stash}else{const s=await i(["update-ref",`refs/heads/${e.base_ref}`,w,f],e.path);if(s.status!==0)throw b(`could not fast-forward local base branch '${e.base_ref}' to this worktree`,"Ask the user how to land this managed worktree.",p(s))}return A(t,e,"pending"),{node_id:t,branch:e.branch,worktree_path:e.path,landed_sha:w,...D(e),...l===void 0?{}:{base_checkout_stash:l}}}c(S,"closeLocked");async function ie(t){const r=v(t)?.managed_worktree;if(r==null||r.state!=="open")return S(t);if(!$(r.path)){const n=await F(t);if(n!==null)return{node_id:t,branch:n.dropped.branch,worktree_path:n.dropped.worktree_path,landed_sha:n.landedSha,worktree_removed:!0,branch_deleted:n.dropped.branch_deleted,...n.dropped.branch_delete_error===void 0?{}:{branch_delete_error:n.dropped.branch_delete_error}}}const e=v(t)?.managed_worktree;if(e==null||e.state!=="open")return S(t);const o=$(e.path)?e.path:e.repo_root,a=await H(o);return O(z(a),()=>S(t),{timeoutMs:Math.min(T,25e3),timeoutError:c(()=>new h("worktree_lock_timeout",`timed out waiting to create a managed worktree in ${a}`,"Wait for the other managed-worktree operation to finish, then retry."),"timeoutError")})}c(ie,"closeManagedWorktreeAsync");export{ie as closeManagedWorktreeAsync};
4
+ `))if(n.startsWith("worktree "))a=n.slice(9).trim();else if(a!==null&&K(a,r)&&n===`branch refs/heads/${e}`)return!0;return!1}c(L,"registeredWorktree");function D(t){return{worktree_removed:!1,worktree_remove_error:`cleanup pending: the caller checkout at ${t.path} remains intact. After its node exits, run \`git -C ${E(t.repo_root)} worktree remove ${E(t.path)}\` after confirming it is safe to delete.`,branch_deleted:!1,branch_delete_error:`cleanup pending: local branch ${t.branch} remains checked out at ${t.path}; delete it only after that checkout is removed and its landed commit is still contained in ${t.base_ref}.`}}c(D,"deferredCleanupResult");async function U(t,r){const e=(await v(t))?.managed_worktree;e==null||e.state!=="open"||await M(t,{at:new Date().toISOString(),blocked:r})}c(U,"recordBlockedLandIntent");async function W(t,r){const e=await i(["rev-parse","--verify","--quiet","refs/stash"],t);e.status===0&&e.stdout.trim()===r&&await i(["stash","drop"],t)}c(W,"dropStashIfStillTip");async function V(t,r,e,o){const a=await i(["status","--porcelain"],t);if(a.status!==0)return{ok:!1,detail:w(a)};const n=await i(["diff","--name-only",e,o],t);if(n.status!==0)return{ok:!1,detail:w(n)};const f=n.stdout.split(`
5
+ `).map(_=>_.trim()).filter(_=>_!==""),k=I(G(a.stdout),f);if(k.kind==="refuse")return{ok:!1,detail:`untracked files in ${t} would be overwritten by the incoming commits: ${k.collisions.join(", ")}`};const p=c(async()=>{const _=await i(["merge","--ff-only",o],t);return _.status===0?{ok:!0}:{ok:!1,detail:w(_)}},"merge");if(k.kind==="direct")return p();const b=await i(["rev-parse","--verify","--quiet","refs/stash"],t),g=b.status===0?b.stdout.trim():null,y=await i(["stash","push","--message",`crtr landing ${r}`],t);if(y.status!==0)return{ok:!1,detail:w(y)};const m=await i(["rev-parse","--verify","--quiet","refs/stash"],t),l=m.status===0?m.stdout.trim():null;if(l===null||l===g)return p();const s=`local changes from ${t} are retained in stash ${l}; recover them with \`git -C ${E(t)} stash apply ${l}\` and resolve any conflict.`,d=await p();return d.ok?(await i(["stash","apply",l],t)).status===0?(await W(t,l),{ok:!0}):(await i(["reset","--hard",o],t),{ok:!0,stash:`the shared base checkout was reset to landed commit ${o} because local changes conflicted with it. ${s}`}):(await i(["stash","apply",l],t)).status===0?(await W(t,l),d):{ok:!1,detail:`${d.detail}
6
+ ${s}`}}c(V,"landIntoBaseCheckout");async function S(t){const r=await v(t);if(r===null)throw new h("unknown_node",`unknown node: ${t}`,"Run this from a live node, or pass a real node id once that command supports it.");const e=r?.managed_worktree?.state==="open"?r.managed_worktree:null;if(e===null)throw new h("no_managed_worktree",`node ${t} has no open managed worktree`,"Nothing to close for this node.");if(!$(e.path))throw new h("worktree_missing",`managed worktree path is missing: ${e.path}`,j(e));if(await J(e.path))throw new h("rebase_in_progress","this managed worktree is mid-rebase",`Resolve conflicts in ${e.path}, run \`git rebase --continue\` there, then rerun \`crtr node worktree close\`.`);if(await u(e.path,["status","--porcelain"],"status_failed","Inspect the worktree manually, then retry.")!=="")throw new h("dirty_worktree","managed worktree has uncommitted changes",`Commit the work in ${e.path}, then rerun \`crtr node worktree close\`.`);const a=await u(e.path,["rev-parse","--abbrev-ref","HEAD"],"branch_check_failed","Inspect the worktree manually, then retry."),n=await u(e.path,["rev-parse","HEAD"],"landed_sha_failed","Inspect the worktree checkout manually, then rerun `crtr node worktree close`.");if(a!==e.branch){if((await i(["show-ref","--verify","--quiet",`refs/heads/${e.branch}`],e.path)).status===0||a==="HEAD"||!await L(e.repo_root,e.path,a))throw new h("wrong_branch",`managed worktree is not on its recorded branch (expected ${e.branch}, found ${a==="HEAD"?"detached HEAD":a})`,`Inspect ${e.path}, then rerun \`crtr node worktree close\`.`);if(await x(e.path,e,n)===null)throw new h("branch_moved_unlanded",`managed worktree's recorded branch ${e.branch} no longer exists; its checkout is now on ${a} at ${n}, which carries content local base branch '${e.base_ref}' does not yet contain`,`Land or push ${a} through its own workflow before closing.`);const d=await H(e.path);if(!await L(e.repo_root,e.path,a)||await u(e.path,["rev-parse","--abbrev-ref","HEAD"],"branch_check_failed","Inspect the worktree manually, then retry.")!==a||await u(e.path,["rev-parse","HEAD"],"branch_check_failed","Inspect the worktree manually, then retry.")!==n)throw new h("wrong_branch",`managed worktree at ${e.path} changed while closing`,`Inspect ${e.path}, then rerun \`crtr node worktree close\`.`);return await B(t,{observed_branch:a,observed_sha:n,common_dir:d,authorized:new Date().toISOString()}),await A(t,e,"pending"),{node_id:t,branch:a,worktree_path:e.path,landed_sha:n,...D(e),branch_delete_error:`${a} is not a crtr-managed branch and will never be deleted automatically; it is preserved once the checkout above is removed.`}}if(await x(e.path,e,n)!==null)return await A(t,e,"pending"),{node_id:t,branch:e.branch,worktree_path:e.path,landed_sha:n,...D(e)};let f;try{f=await Q(e.path,e.base_ref)}catch(s){if(!(s instanceof h)||s.code!=="missing_base_branch"||n!==e.base_sha)throw s;return await A(t,e,"pending"),{node_id:t,branch:e.branch,worktree_path:e.path,landed_sha:e.base_sha,...D(e)}}const k=await i(["rebase",f],e.path);if(k.status!==0)throw new h("rebase_failed",`rebase onto local base branch '${e.base_ref}' did not complete`,`Resolve conflicts in ${e.path}, run \`git rebase --continue\` there, then rerun \`crtr node worktree close\`.`,w(k));const p=await u(e.path,["rev-parse","HEAD"],"landed_sha_failed","Inspect the worktree checkout manually, then rerun `crtr node worktree close`."),b=c(async(s,d,_)=>(await U(t,s),new h("land_base_failed",s,d,_)),"blocked"),g=await i(["merge-base","--is-ancestor",f,p],e.path);if(g.status!==0)throw await b(`rebased worktree is not a fast-forward of local base branch '${e.base_ref}'`,"Ask the user how to land this managed worktree.",w(g));const y=await u(e.path,["worktree","list","--porcelain"],"worktree_list_failed","Inspect the repository worktrees manually, then retry."),m=N(y,e.base_ref);let l;if(m!==null){const s=await i(["rev-parse","HEAD"],m);if(s.status!==0||s.stdout.trim()!==f)throw await b(`local base branch '${e.base_ref}' changed while this worktree was closing`,`Reconcile the base checkout at ${m}, then rerun \`crtr node worktree close\`.`,s.status!==0?w(s):`expected ${f}, found ${s.stdout.trim()}`);const d=await V(m,e.branch,f,p);if(!d.ok)throw await b(`could not fast-forward local base branch '${e.base_ref}' to this worktree`,`Local base branch '${e.base_ref}' is checked out at ${m} in a state that blocks a fast-forward.`,d.detail);l=d.stash}else{const s=await i(["update-ref",`refs/heads/${e.base_ref}`,p,f],e.path);if(s.status!==0)throw await b(`could not fast-forward local base branch '${e.base_ref}' to this worktree`,"Ask the user how to land this managed worktree.",w(s))}return await A(t,e,"pending"),{node_id:t,branch:e.branch,worktree_path:e.path,landed_sha:p,...D(e),...l===void 0?{}:{base_checkout_stash:l}}}c(S,"closeLocked");async function ie(t){const r=(await v(t))?.managed_worktree;if(r==null||r.state!=="open")return S(t);if(!$(r.path)){const n=await F(t);if(n!==null)return{node_id:t,branch:n.dropped.branch,worktree_path:n.dropped.worktree_path,landed_sha:n.landedSha,worktree_removed:!0,branch_deleted:n.dropped.branch_deleted,...n.dropped.branch_delete_error===void 0?{}:{branch_delete_error:n.dropped.branch_delete_error}}}const e=(await v(t))?.managed_worktree;if(e==null||e.state!=="open")return S(t);const o=$(e.path)?e.path:e.repo_root,a=await H(o);return O(z(a),()=>S(t),{timeoutMs:Math.min(T,25e3),timeoutError:c(()=>new h("worktree_lock_timeout",`timed out waiting to create a managed worktree in ${a}`,"Wait for the other managed-worktree operation to finish, then retry."),"timeoutError")})}c(ie,"closeManagedWorktreeAsync");export{ie as closeManagedWorktreeAsync};
@@ -1,10 +1,10 @@
1
- var P=Object.defineProperty;var o=(e,r)=>P(e,"name",{value:r,configurable:!0});import{existsSync as p,mkdirSync as H,readFileSync as R,realpathSync as O,rmSync as B,writeFileSync as q}from"node:fs";import{dirname as N,join as j,resolve as G}from"node:path";import{abandonWorktreeAndRepoint as I,getNode as w}from"./canvas/index.js";import{isPidAlive as K}from"./canvas/pid.js";import{CONFIG_FILE as z,CRTR_DIR_NAME as x,STATE_FILE as J}from"../types.js";import{withExclusiveDirectoryLockAsync as S}from"./exclusive-lock.js";import{gitAsync as s}from"./git.js";import{WORKTREE_LOCK_WAIT_MS as Q,checkoutPathForBranchInList as U,isManagedWorktreeBase as V,managedWorktreePath as X,persistClosedWorktreeState as _,worktreeLockPathForCommonDir as D,WorktreeError as c}from"./worktree.js";import{proveBranchContained as Y}from"./worktree-containment.js";import{refDeleteTransactionStdin as Z}from"./worktree-landing.js";import{deleteTrashAsync as ee,removeWorktreeCheckoutAsync as te}from"./worktree-remove.js";const E=Math.min(Q,25e3);function v(e){return(e.stderr.trim()||e.stdout.trim()).trim()}o(v,"detailOf");async function h(e,r,a,n){const t=await s(r,e);if(t.status!==0)throw new c(a,`git ${r.join(" ")} failed`,n,v(t));return t.stdout.trim()}o(h,"runGit");async function W(e){return O(G(e,await h(e,["rev-parse","--git-common-dir"],"git_common_dir_failed","Inspect the repository git metadata, then retry.")))}o(W,"commonGitDir");async function k(e,r){const a=await W(e);return S(D(a),r,{timeoutMs:E,timeoutError:o(()=>new c("worktree_lock_timeout",`timed out waiting to create a managed worktree in ${a}`,"Wait for the other managed-worktree operation to finish, then retry."),"timeoutError")})}o(k,"withRepositoryLock");async function $(e,r){const a=`refs/heads/${r}`,n=await s(["show-ref","--verify","--quiet",a],e);if(n.status===1&&n.executionFailed!==!0)throw new c("missing_base_branch",`managed worktree base '${r}' does not name an existing local branch`,"The base must be a local branch that landing can fast-forward \u2014 freshen your local branch from origin first (rebase-not-merge), then base on it.");if(n.status!==0)throw new c("base_branch_inspection_failed",`could not inspect local base branch '${r}' (git show-ref exited with status ${n.status})`,"Inspect the repository and its Git metadata, then retry.",`status: ${n.status}
2
- stderr: ${n.stderr.trim()||"<empty>"}`);return h(e,["rev-parse","--verify",a],"base_branch_resolve_failed","Inspect the local base branch, then retry.")}o($,"resolveLandableBase");function re(e,r){try{const a=j(r,"info","exclude");H(N(a),{recursive:!0});let n="";try{n=R(a,"utf8")}catch{}const t=[`/${x}/${z}`,`/${x}/${J}`];if(t.every(i=>n.split(`
3
- `).map(l=>l.trim()).includes(i)))return;q(a,`${n}${n!==""&&!n.endsWith(`
1
+ var P=Object.defineProperty;var o=(e,r)=>P(e,"name",{value:r,configurable:!0});import{existsSync as p,mkdirSync as H,readFileSync as R,realpathSync as O,rmSync as B,writeFileSync as q}from"node:fs";import{dirname as N,join as j,resolve as G}from"node:path";import{abandonWorktreeAndRepoint as I,getNode as m}from"./canvas/index.js";import{isPidAlive as K}from"./canvas/pid.js";import{CONFIG_FILE as z,CRTR_DIR_NAME as x,STATE_FILE as J}from"../types.js";import{withExclusiveDirectoryLockAsync as S}from"./exclusive-lock.js";import{gitAsync as i}from"./git.js";import{WORKTREE_LOCK_WAIT_MS as Q,checkoutPathForBranchInList as U,isManagedWorktreeBase as V,managedWorktreePath as X,persistClosedWorktreeState as _,worktreeLockPathForCommonDir as D,WorktreeError as c}from"./worktree.js";import{proveBranchContained as Y}from"./worktree-containment.js";import{refDeleteTransactionStdin as Z}from"./worktree-landing.js";import{deleteTrashAsync as ee,removeWorktreeCheckoutAsync as te}from"./worktree-remove.js";const E=Math.min(Q,25e3);function v(e){return(e.stderr.trim()||e.stdout.trim()).trim()}o(v,"detailOf");async function h(e,r,a,n){const t=await i(r,e);if(t.status!==0)throw new c(a,`git ${r.join(" ")} failed`,n,v(t));return t.stdout.trim()}o(h,"runGit");async function W(e){return O(G(e,await h(e,["rev-parse","--git-common-dir"],"git_common_dir_failed","Inspect the repository git metadata, then retry.")))}o(W,"commonGitDir");async function k(e,r){const a=await W(e);return S(D(a),r,{timeoutMs:E,timeoutError:o(()=>new c("worktree_lock_timeout",`timed out waiting to create a managed worktree in ${a}`,"Wait for the other managed-worktree operation to finish, then retry."),"timeoutError")})}o(k,"withRepositoryLock");async function $(e,r){const a=`refs/heads/${r}`,n=await i(["show-ref","--verify","--quiet",a],e);if(n.status===1&&n.executionFailed!==!0)throw new c("missing_base_branch",`managed worktree base '${r}' does not name an existing local branch`,"The base must be a local branch that landing can fast-forward \u2014 freshen your local branch from origin first (rebase-not-merge), then base on it.");if(n.status!==0)throw new c("base_branch_inspection_failed",`could not inspect local base branch '${r}' (git show-ref exited with status ${n.status})`,"Inspect the repository and its Git metadata, then retry.",`status: ${n.status}
2
+ stderr: ${n.stderr.trim()||"<empty>"}`);return h(e,["rev-parse","--verify",a],"base_branch_resolve_failed","Inspect the local base branch, then retry.")}o($,"resolveLandableBase");function re(e,r){try{const a=j(r,"info","exclude");H(N(a),{recursive:!0});let n="";try{n=R(a,"utf8")}catch{}const t=[`/${x}/${z}`,`/${x}/${J}`];if(t.every(s=>n.split(`
3
+ `).map(l=>l.trim()).includes(s)))return;q(a,`${n}${n!==""&&!n.endsWith(`
4
4
  `)?`
5
5
  `:""}# crtr: exclude crtr's own untracked project-scope bookkeeping (auto-added at managed-worktree creation)
6
- ${t.filter(i=>!n.split(`
7
- `).map(l=>l.trim()).includes(i)).join(`
6
+ ${t.filter(s=>!n.split(`
7
+ `).map(l=>l.trim()).includes(s)).join(`
8
8
  `)}
9
- `)}catch{}}o(re,"seedExclude");async function ye(e){await k(e.repo_root,async()=>{const r=await s(["rev-parse","--verify",`refs/heads/${e.branch}`],e.repo_root);if(r.status!==0||r.stdout.trim()!==e.base_sha)return;const a=await s(["worktree","list","--porcelain"],e.repo_root);if(a.status===0){if(a.stdout.split(`
10
- `).some(n=>n.trim()===`branch refs/heads/${e.branch}`)){if((await s(["worktree","remove","--force",e.path],e.repo_root)).status!==0)return}else if(p(e.path))try{B(e.path,{recursive:!0,force:!0})}catch{return}await s(["update-ref","-d",`refs/heads/${e.branch}`,e.base_sha],e.repo_root)}})}o(ye,"rollbackManagedWorktreeAsync");async function ge(e,r,a="main"){const n=await h(e,["rev-parse","--show-toplevel"],"not_git_repo","Pass --cwd <real repo> pointing inside a git repository.");if(V(n))throw new c("managed_worktree_base",`cwd ${n} is a managed worktree and cannot be used as a managed worktree base`,"Pass `--cwd` pointing at the repository\u2019s primary checkout and keep `--worktree` set to the desired existing local base branch, including the parent branch.");const t=await W(n);return S(D(t),async()=>{const i=await $(n,a),l=X(r),u=`crtr/${r}`;if(p(l))throw new c("worktree_path_exists",`managed worktree path already exists: ${l}`,"Remove the stale path or choose a fresh node id.");if((await s(["show-ref","--verify","--quiet",`refs/heads/${u}`],n)).status===0)throw new c("worktree_branch_exists",`managed worktree branch already exists: ${u}`,"Remove the stale branch or choose a fresh node id.");const b={state:"open",path:l,branch:u,repo_root:n,base_ref:a,base_sha:i,created:new Date().toISOString()};await h(n,["branch",u,i],"worktree_branch_create_failed","Inspect the git error, fix the repository/worktree state, then retry spawning with --worktree.");const f=await s(["worktree","add",l,u],n);if(f.status!==0)throw await s(["update-ref","-d",`refs/heads/${u}`,i],n),new c("worktree_add_failed","git worktree add failed","Inspect the git error, fix the repository/worktree state, then retry spawning with --worktree.",v(f));return re(n,t),b},{timeoutMs:E,timeoutError:o(()=>new c("worktree_lock_timeout",`timed out waiting to create a managed worktree in ${t}`,"Wait for the other managed-worktree operation to finish, then retry."),"timeoutError")})}o(ge,"createManagedWorktreeAsync");async function M(e){if(e.state!=="open"||!p(e.path))return null;const r=await s(["rev-parse","--abbrev-ref","HEAD"],e.path),a=await s(["status","--porcelain"],e.path),n=await s(["rev-parse","HEAD"],e.path);if(r.status!==0||r.stdout.trim()!==e.branch||a.status!==0||a.stdout.trim()!==""||n.status!==0)return null;const t=await s(["rev-parse","--git-path","rebase-merge"],e.path);return t.status!==0||p(t.stdout.trim())?null:n.stdout.trim()}o(M,"cleanTip");async function C(e,r){try{const a=await $(e.path,e.base_ref);return(await s(["merge-base","--is-ancestor",r,a],e.path)).status===0}catch(a){if(a instanceof c&&a.code==="missing_base_branch"&&r===e.base_sha)return!0;throw a}}o(C,"containedInLocalBase");function ae(e){return{node_id:"",branch:e.branch,worktree_path:e.path,worktree_removed:!1,worktree_remove_error:`cleanup pending: the caller checkout at ${e.path} remains intact.`,branch_deleted:!1,branch_delete_error:`cleanup pending: local branch ${e.branch} remains checked out at ${e.path}.`}}o(ae,"deferred");async function L(e){if(e.state!=="open"||p(e.path))return null;const r=`refs/heads/${e.branch}`,a=await s(["show-ref","--verify","--quiet",r],e.repo_root);if(a.status===1&&a.executionFailed!==!0)return{branchSha:null};if(a.status!==0)return null;const n=await s(["rev-parse","--verify",r],e.repo_root);if(n.status!==0)return null;const t=n.stdout.trim(),i=await s(["worktree","list","--porcelain"],e.repo_root);if(i.status!==0||U(i.stdout,e.branch)!==null)return null;const l=await Y(e.repo_root,e,t);return l===null?null:{branchSha:t,containment:l}}o(L,"proveAbsentPathManagedWorktree");async function ne(e,r,a){return p(r.path)?null:a.branchSha===null?(_(e,r,"complete"),{node_id:e,branch:r.branch,worktree_path:r.path,worktree_removed:!0,branch_deleted:!0}):a.containment===void 0?null:(_(e,r,"complete"),{node_id:e,branch:r.branch,worktree_path:r.path,worktree_removed:!0,branch_deleted:!1,branch_delete_error:`local branch ${r.branch} is delivered but retained because its checkout status can change outside crtr's repository lock.`})}o(ne,"autoDropAbsentPathManagedWorktreeLocked");async function oe(e){const r=w(e)?.managed_worktree;return r==null||r.state!=="open"||p(r.path)||await L(r)===null?null:k(r.repo_root,async()=>{const a=w(e)?.managed_worktree;if(a==null||a.state!=="open")return null;const n=await L(a);if(n===null)return null;const t=await ne(e,a,n);return t===null?null:{dropped:t,landedSha:n.branchSha??a.base_sha}})}o(oe,"concludeAbsentManagedWorktreeAsync");async function ve(e){const r=w(e)?.managed_worktree;if(r==null||r.state!=="open")return null;if(!p(r.path))return(await oe(e))?.dropped??null;const a=await M(r);return a===null||!await C(r,a)?null:k(r.path,async()=>{const n=w(e)?.managed_worktree;if(n==null||n.state!=="open")return null;const t=await M(n);return t===null||t!==a||!await C(n,t)?null:(_(e,n,"pending"),{...ae(n),node_id:e})})}o(ve,"autoDropCleanManagedWorktreeAsync");async function T(e){for(const r of["rebase-merge","rebase-apply"]){const a=await s(["rev-parse","--git-path",r],e);if(a.status!==0||p(a.stdout.trim()))return!0}return!1}o(T,"rebaseInProgress");function y(e,r,a,n,t,i=r.branch){I(e,{cwd:r.repo_root,abandoned:{branch:i,tip_sha:a,at:n,by:t}})}o(y,"persistAbandoned");async function $e(e,r){const a=w(e)?.managed_worktree;if(a==null||a.state!=="open")throw new c("no_managed_worktree",`node ${e} has no open managed worktree`,"Nothing to abandon for this node.");const n=[];try{return await k(a.path,()=>se(e,r,n))}finally{await ee(a.path,n[0])}}o($e,"abandonManagedWorktreeAsync");async function g(e,r){const{result:a,trash:n}=await te(e.repo_root,e.path);if(n!==void 0&&r.push(n),a.status!==0)throw new c("worktree_remove_failed",`git worktree remove ${e.path} failed`,"Inspect the worktree manually, then retry.",v(a))}o(g,"removeCheckout");async function se(e,r,a){const n=w(e),t=n?.managed_worktree;if(n===null||t===void 0||t===null||t.state!=="open")throw new c("no_managed_worktree",`node ${e} has no open managed worktree`,"Nothing to abandon for this node.");if(n.status==="active"||n.status==="idle"||K(n.pi_pid))throw new c("node_running",`node ${e} is still running`,"Abandon requires a stopped node with no live broker process. From the running owner, use `crtr node worktree close` to land locally, or make the checkout clean, push its current tip to origin, and rerun `crtr push final`.");if(!p(t.path))throw new c("worktree_missing",`managed worktree path is missing: ${t.path}`,"Inspect the branch and worktree registration, then retry.");const i=await h(t.path,["rev-parse","--abbrev-ref","HEAD"],"branch_check_failed","Inspect the worktree manually, then retry."),l=await s(["show-ref","--verify","--quiet",`refs/heads/${t.branch}`],t.path);if(i!==t.branch){if(l.status===0||i==="HEAD")throw new c("wrong_branch",`managed worktree is not on its recorded branch (expected ${t.branch}, found ${i==="HEAD"?"detached HEAD":i})`,`Inspect ${t.path}, then rerun \`crtr node worktree abandon\`.`);if(await T(t.path)||await h(t.path,["status","--porcelain"],"status_failed","Inspect the worktree manually, then retry.")!=="")throw new c("dirty_worktree","managed worktree has uncommitted changes",`Commit or stash them in ${t.path}, then rerun \`crtr node worktree abandon\`.`);const d=await h(t.path,["rev-parse","HEAD"],"abandon_tip_failed","Inspect the worktree manually, then retry.");await g(t,a);const m=new Date().toISOString();return y(e,t,d,m,r,i),{node_id:e,branch:i,tip_sha:d,worktree_path:t.path,branch_deleted:!1}}const u=await h(t.path,["rev-parse","HEAD"],"abandon_tip_failed","Inspect the branch manually, then retry.");let b=null;try{const d=await $(t.path,t.base_ref);(await s(["merge-base","--is-ancestor",u,d],t.path)).status===0&&(b=d)}catch(d){if(!(d instanceof c)||d.code!=="missing_base_branch")throw d}const f=new Date().toISOString(),F=await h(t.path,["status","--porcelain"],"status_failed","Inspect the worktree manually, then retry.");if(b!==null&&u===t.base_sha){const d=await h(t.repo_root,["rev-parse",`${u}^{tree}`],"abandon_tree_failed","Inspect the branch manually, then retry."),m=await h(t.repo_root,["-c","user.name=crtr-abandon","-c","user.email=crtr-abandon@localhost","commit-tree",d,"-p",u,"-m","crtr-abandon","-m",`Crtr-Abandon: ${e} ${f}`],"abandon_commit_failed","Inspect the branch manually, then retry.");return await h(t.repo_root,["update-ref",`refs/heads/${t.branch}`,m,u],"abandon_marker_failed","The branch changed while abandon was starting; inspect the worktree, then retry."),await g(t,a),(await s(["update-ref","--stdin"],t.repo_root,Z(t.branch,m,`refs/heads/${t.base_ref}`,b))).status===0?(_(e,t,"complete"),{node_id:e,branch:t.branch,tip_sha:m,worktree_path:t.path,branch_deleted:!0}):(y(e,t,m,f,r),{node_id:e,branch:t.branch,tip_sha:m,worktree_path:t.path,branch_deleted:!1})}if(!await T(t.path)&&F==="")return await g(t,a),y(e,t,u,f,r),{node_id:e,branch:t.branch,tip_sha:u,worktree_path:t.path,branch_deleted:!1};await h(t.path,["add","--all"],"abandon_stage_failed","Inspect the worktree manually, then retry."),await h(t.path,["-c","user.name=crtr-abandon","-c","user.email=crtr-abandon@localhost","commit","--allow-empty","-m","crtr-abandon","-m",`Crtr-Abandon: ${e} ${f}`],"abandon_commit_failed","Inspect the worktree manually, then retry.");const A=await h(t.path,["rev-parse","HEAD"],"abandon_tip_failed","Inspect the branch manually, then retry.");return await g(t,a),y(e,t,A,f,r),{node_id:e,branch:t.branch,tip_sha:A,worktree_path:t.path,branch_deleted:!1}}o(se,"abandonLocked");export{$e as abandonManagedWorktreeAsync,ve as autoDropCleanManagedWorktreeAsync,oe as concludeAbsentManagedWorktreeAsync,ge as createManagedWorktreeAsync,ye as rollbackManagedWorktreeAsync};
9
+ `)}catch{}}o(re,"seedExclude");async function ye(e){await k(e.repo_root,async()=>{const r=await i(["rev-parse","--verify",`refs/heads/${e.branch}`],e.repo_root);if(r.status!==0||r.stdout.trim()!==e.base_sha)return;const a=await i(["worktree","list","--porcelain"],e.repo_root);if(a.status===0){if(a.stdout.split(`
10
+ `).some(n=>n.trim()===`branch refs/heads/${e.branch}`)){if((await i(["worktree","remove","--force",e.path],e.repo_root)).status!==0)return}else if(p(e.path))try{B(e.path,{recursive:!0,force:!0})}catch{return}await i(["update-ref","-d",`refs/heads/${e.branch}`,e.base_sha],e.repo_root)}})}o(ye,"rollbackManagedWorktreeAsync");async function ge(e,r,a="main"){const n=await h(e,["rev-parse","--show-toplevel"],"not_git_repo","Pass --cwd <real repo> pointing inside a git repository.");if(V(n))throw new c("managed_worktree_base",`cwd ${n} is a managed worktree and cannot be used as a managed worktree base`,"Pass `--cwd` pointing at the repository\u2019s primary checkout and keep `--worktree` set to the desired existing local base branch, including the parent branch.");const t=await W(n);return S(D(t),async()=>{const s=await $(n,a),l=X(r),u=`crtr/${r}`;if(p(l))throw new c("worktree_path_exists",`managed worktree path already exists: ${l}`,"Remove the stale path or choose a fresh node id.");if((await i(["show-ref","--verify","--quiet",`refs/heads/${u}`],n)).status===0)throw new c("worktree_branch_exists",`managed worktree branch already exists: ${u}`,"Remove the stale branch or choose a fresh node id.");const b={state:"open",path:l,branch:u,repo_root:n,base_ref:a,base_sha:s,created:new Date().toISOString()};await h(n,["branch",u,s],"worktree_branch_create_failed","Inspect the git error, fix the repository/worktree state, then retry spawning with --worktree.");const f=await i(["worktree","add",l,u],n);if(f.status!==0)throw await i(["update-ref","-d",`refs/heads/${u}`,s],n),new c("worktree_add_failed","git worktree add failed","Inspect the git error, fix the repository/worktree state, then retry spawning with --worktree.",v(f));return re(n,t),b},{timeoutMs:E,timeoutError:o(()=>new c("worktree_lock_timeout",`timed out waiting to create a managed worktree in ${t}`,"Wait for the other managed-worktree operation to finish, then retry."),"timeoutError")})}o(ge,"createManagedWorktreeAsync");async function M(e){if(e.state!=="open"||!p(e.path))return null;const r=await i(["rev-parse","--abbrev-ref","HEAD"],e.path),a=await i(["status","--porcelain"],e.path),n=await i(["rev-parse","HEAD"],e.path);if(r.status!==0||r.stdout.trim()!==e.branch||a.status!==0||a.stdout.trim()!==""||n.status!==0)return null;const t=await i(["rev-parse","--git-path","rebase-merge"],e.path);return t.status!==0||p(t.stdout.trim())?null:n.stdout.trim()}o(M,"cleanTip");async function C(e,r){try{const a=await $(e.path,e.base_ref);return(await i(["merge-base","--is-ancestor",r,a],e.path)).status===0}catch(a){if(a instanceof c&&a.code==="missing_base_branch"&&r===e.base_sha)return!0;throw a}}o(C,"containedInLocalBase");function ae(e){return{node_id:"",branch:e.branch,worktree_path:e.path,worktree_removed:!1,worktree_remove_error:`cleanup pending: the caller checkout at ${e.path} remains intact.`,branch_deleted:!1,branch_delete_error:`cleanup pending: local branch ${e.branch} remains checked out at ${e.path}.`}}o(ae,"deferred");async function L(e){if(e.state!=="open"||p(e.path))return null;const r=`refs/heads/${e.branch}`,a=await i(["show-ref","--verify","--quiet",r],e.repo_root);if(a.status===1&&a.executionFailed!==!0)return{branchSha:null};if(a.status!==0)return null;const n=await i(["rev-parse","--verify",r],e.repo_root);if(n.status!==0)return null;const t=n.stdout.trim(),s=await i(["worktree","list","--porcelain"],e.repo_root);if(s.status!==0||U(s.stdout,e.branch)!==null)return null;const l=await Y(e.repo_root,e,t);return l===null?null:{branchSha:t,containment:l}}o(L,"proveAbsentPathManagedWorktree");async function ne(e,r,a){return p(r.path)?null:a.branchSha===null?(await _(e,r,"complete"),{node_id:e,branch:r.branch,worktree_path:r.path,worktree_removed:!0,branch_deleted:!0}):a.containment===void 0?null:(await _(e,r,"complete"),{node_id:e,branch:r.branch,worktree_path:r.path,worktree_removed:!0,branch_deleted:!1,branch_delete_error:`local branch ${r.branch} is delivered but retained because its checkout status can change outside crtr's repository lock.`})}o(ne,"autoDropAbsentPathManagedWorktreeLocked");async function oe(e){const r=(await m(e))?.managed_worktree;return r==null||r.state!=="open"||p(r.path)||await L(r)===null?null:k(r.repo_root,async()=>{const a=(await m(e))?.managed_worktree;if(a==null||a.state!=="open")return null;const n=await L(a);if(n===null)return null;const t=await ne(e,a,n);return t===null?null:{dropped:t,landedSha:n.branchSha??a.base_sha}})}o(oe,"concludeAbsentManagedWorktreeAsync");async function ve(e){const r=(await m(e))?.managed_worktree;if(r==null||r.state!=="open")return null;if(!p(r.path))return(await oe(e))?.dropped??null;const a=await M(r);return a===null||!await C(r,a)?null:k(r.path,async()=>{const n=(await m(e))?.managed_worktree;if(n==null||n.state!=="open")return null;const t=await M(n);return t===null||t!==a||!await C(n,t)?null:(await _(e,n,"pending"),{...ae(n),node_id:e})})}o(ve,"autoDropCleanManagedWorktreeAsync");async function T(e){for(const r of["rebase-merge","rebase-apply"]){const a=await i(["rev-parse","--git-path",r],e);if(a.status!==0||p(a.stdout.trim()))return!0}return!1}o(T,"rebaseInProgress");async function y(e,r,a,n,t,s=r.branch){await I(e,{cwd:r.repo_root,abandoned:{branch:s,tip_sha:a,at:n,by:t}})}o(y,"persistAbandoned");async function $e(e,r){const a=(await m(e))?.managed_worktree;if(a==null||a.state!=="open")throw new c("no_managed_worktree",`node ${e} has no open managed worktree`,"Nothing to abandon for this node.");const n=[];try{return await k(a.path,()=>ie(e,r,n))}finally{await ee(a.path,n[0])}}o($e,"abandonManagedWorktreeAsync");async function g(e,r){const{result:a,trash:n}=await te(e.repo_root,e.path);if(n!==void 0&&r.push(n),a.status!==0)throw new c("worktree_remove_failed",`git worktree remove ${e.path} failed`,"Inspect the worktree manually, then retry.",v(a))}o(g,"removeCheckout");async function ie(e,r,a){const n=await m(e),t=n?.managed_worktree;if(n===null||t===void 0||t===null||t.state!=="open")throw new c("no_managed_worktree",`node ${e} has no open managed worktree`,"Nothing to abandon for this node.");if(n.status==="active"||n.status==="idle"||K(n.pi_pid))throw new c("node_running",`node ${e} is still running`,"Abandon requires a stopped node with no live broker process. From the running owner, use `crtr node worktree close` to land locally, or make the checkout clean, push its current tip to origin, and rerun `crtr push final`.");if(!p(t.path))throw new c("worktree_missing",`managed worktree path is missing: ${t.path}`,"Inspect the branch and worktree registration, then retry.");const s=await h(t.path,["rev-parse","--abbrev-ref","HEAD"],"branch_check_failed","Inspect the worktree manually, then retry."),l=await i(["show-ref","--verify","--quiet",`refs/heads/${t.branch}`],t.path);if(s!==t.branch){if(l.status===0||s==="HEAD")throw new c("wrong_branch",`managed worktree is not on its recorded branch (expected ${t.branch}, found ${s==="HEAD"?"detached HEAD":s})`,`Inspect ${t.path}, then rerun \`crtr node worktree abandon\`.`);if(await T(t.path)||await h(t.path,["status","--porcelain"],"status_failed","Inspect the worktree manually, then retry.")!=="")throw new c("dirty_worktree","managed worktree has uncommitted changes",`Commit or stash them in ${t.path}, then rerun \`crtr node worktree abandon\`.`);const d=await h(t.path,["rev-parse","HEAD"],"abandon_tip_failed","Inspect the worktree manually, then retry.");await g(t,a);const w=new Date().toISOString();return await y(e,t,d,w,r,s),{node_id:e,branch:s,tip_sha:d,worktree_path:t.path,branch_deleted:!1}}const u=await h(t.path,["rev-parse","HEAD"],"abandon_tip_failed","Inspect the branch manually, then retry.");let b=null;try{const d=await $(t.path,t.base_ref);(await i(["merge-base","--is-ancestor",u,d],t.path)).status===0&&(b=d)}catch(d){if(!(d instanceof c)||d.code!=="missing_base_branch")throw d}const f=new Date().toISOString(),F=await h(t.path,["status","--porcelain"],"status_failed","Inspect the worktree manually, then retry.");if(b!==null&&u===t.base_sha){const d=await h(t.repo_root,["rev-parse",`${u}^{tree}`],"abandon_tree_failed","Inspect the branch manually, then retry."),w=await h(t.repo_root,["-c","user.name=crtr-abandon","-c","user.email=crtr-abandon@localhost","commit-tree",d,"-p",u,"-m","crtr-abandon","-m",`Crtr-Abandon: ${e} ${f}`],"abandon_commit_failed","Inspect the branch manually, then retry.");return await h(t.repo_root,["update-ref",`refs/heads/${t.branch}`,w,u],"abandon_marker_failed","The branch changed while abandon was starting; inspect the worktree, then retry."),await g(t,a),(await i(["update-ref","--stdin"],t.repo_root,Z(t.branch,w,`refs/heads/${t.base_ref}`,b))).status===0?(await _(e,t,"complete"),{node_id:e,branch:t.branch,tip_sha:w,worktree_path:t.path,branch_deleted:!0}):(await y(e,t,w,f,r),{node_id:e,branch:t.branch,tip_sha:w,worktree_path:t.path,branch_deleted:!1})}if(!await T(t.path)&&F==="")return await g(t,a),await y(e,t,u,f,r),{node_id:e,branch:t.branch,tip_sha:u,worktree_path:t.path,branch_deleted:!1};await h(t.path,["add","--all"],"abandon_stage_failed","Inspect the worktree manually, then retry."),await h(t.path,["-c","user.name=crtr-abandon","-c","user.email=crtr-abandon@localhost","commit","--allow-empty","-m","crtr-abandon","-m",`Crtr-Abandon: ${e} ${f}`],"abandon_commit_failed","Inspect the worktree manually, then retry.");const A=await h(t.path,["rev-parse","HEAD"],"abandon_tip_failed","Inspect the branch manually, then retry.");return await g(t,a),await y(e,t,A,f,r),{node_id:e,branch:t.branch,tip_sha:A,worktree_path:t.path,branch_deleted:!1}}o(ie,"abandonLocked");export{$e as abandonManagedWorktreeAsync,ve as autoDropCleanManagedWorktreeAsync,oe as concludeAbsentManagedWorktreeAsync,ge as createManagedWorktreeAsync,ye as rollbackManagedWorktreeAsync};
@@ -1 +1 @@
1
- var s=Object.defineProperty;var o=(a,t)=>s(a,"name",{value:t,configurable:!0});import{directorySizeBytes as r}from"./disk-usage.js";import{getNode as n,listNodes as p}from"./canvas/index.js";async function l(){const a=[];for(const t of p()){const e=n(t.node_id)?.managed_worktree;e?.sweep?.quarantined===!0&&a.push({node_id:t.node_id,path:e.path,branch:e.branch,repo_root:e.repo_root,reason:e.sweep.reason,...e.sweep.detail===void 0?{}:{detail:e.sweep.detail},attempts:e.sweep.attempts,last_attempt:e.sweep.last_attempt})}return a.sort((t,e)=>t.last_attempt.localeCompare(e.last_attempt)),Promise.all(a.map(async t=>({...t,size_bytes:await r(t.path)})))}o(l,"listQuarantinedManagedWorktrees");export{l as listQuarantinedManagedWorktrees};
1
+ var s=Object.defineProperty;var o=(a,e)=>s(a,"name",{value:e,configurable:!0});import{directorySizeBytes as r}from"./disk-usage.js";import{getNode as n,listNodes as i}from"./canvas/index.js";async function l(){const a=[];for(const e of await i()){const t=(await n(e.node_id))?.managed_worktree;t?.sweep?.quarantined===!0&&a.push({node_id:e.node_id,path:t.path,branch:t.branch,repo_root:t.repo_root,reason:t.sweep.reason,...t.sweep.detail===void 0?{}:{detail:t.sweep.detail},attempts:t.sweep.attempts,last_attempt:t.sweep.last_attempt})}return a.sort((e,t)=>e.last_attempt.localeCompare(t.last_attempt)),Promise.all(a.map(async e=>({...e,size_bytes:await r(e.path)})))}o(l,"listQuarantinedManagedWorktrees");export{l as listQuarantinedManagedWorktrees};
@@ -40,7 +40,7 @@ export interface ReconcileGuard {
40
40
  * every destructive step re-asks, and a false answer stops the pass where it
41
41
  * stands, leaving the record incomplete for a later pass to resume. The
42
42
  * implementation must not spawn a subprocess: this runs inside crtrd. */
43
- stillEligible: () => boolean;
43
+ stillEligible: () => Promise<boolean>;
44
44
  }
45
45
  /** Dispose of one node's managed worktree, or explain why it must survive.
46
46
  *
@@ -1,6 +1,6 @@
1
- var q=Object.defineProperty;var d=(e,t)=>q(e,"name",{value:t,configurable:!0});import{createHash as z}from"node:crypto";import{existsSync as y,realpathSync as K}from"node:fs";import{resolve as N}from"node:path";import{completeCleanup as U,getNode as v,recordSweepAttempt as C}from"./canvas/index.js";import{withExclusiveDirectoryLockAsync as Q}from"./exclusive-lock.js";import{gitAsync as c}from"./git.js";import{proveBranchContained as A,upstreamTip as V}from"./worktree-containment.js";import{parseStatusPorcelain as j,planLandIntoCheckout as G,refDeleteTransactionStdin as I}from"./worktree-landing.js";import{shellQuote as J}from"../shared/shell-quote.js";import{deleteTrashAsync as X,removeWorktreeCheckoutAsync as R}from"./worktree-remove.js";import{WORKTREE_LOCK_WAIT_MS as Y,checkoutPathForBranchInList as B,samePath as W,worktreeLockPathForCommonDir as Z}from"./worktree.js";const E=[2,5,15,30,60],ee=E.length;function D(e){return e==null||e.state==="abandoned"?!1:e.state==="open"||e.cleanup!=="complete"}d(D,"isReconcilable");function ve(e,t){const r=e.sweep?.next_attempt;if(r===void 0)return!0;const a=Date.parse(r);return Number.isNaN(a)||a<=t}d(ve,"isDueForSweep");async function m(e,t){const r=await c(["rev-parse","--verify","--quiet",t],e);return r.status===0?r.stdout.trim():null}d(m,"revParseRef");function h(e){return(e.stderr.trim()||e.stdout.trim()).trim()}d(h,"detailOf");async function L(e){const t=await c(["rev-parse","--git-common-dir"],e);if(t.status!==0)return null;try{return K(N(e,t.stdout.trim()))}catch{return null}}d(L,"commonGitDirAsync");async function te(e){for(const t of["rebase-merge","rebase-apply"]){const r=await c(["rev-parse","--git-path",t],e);if(r.status!==0)return null;if(y(N(e,r.stdout.trim())))return!0}return!1}d(te,"isRebaseInProgressAsync");function ae(e){for(const t of e.split(`
2
- `))if(t.startsWith("worktree "))return t.slice(9).trim();return null}d(ae,"mainWorktreePath");function re(e,t,r){let a=null;for(const s of e.split(`
3
- `))if(s.startsWith("worktree "))a=s.slice(9).trim();else if(a!==null&&s===`branch refs/heads/${r}`&&W(a,t))return a;return null}d(re,"registeredCheckoutPath");async function se(e){const t=y(e.path),r=t?e.path:e.repo_root,a=await m(r,`refs/heads/${e.base_ref}`),s=await V(r,e.base_ref),n=await m(r,`refs/heads/${e.branch}`);if(!t)return{base:a,upstream:s?.sha??null,branch:n,checkout:"absent"};const i=await c(["rev-parse","HEAD"],e.path),u=await c(["status","--porcelain"],e.path),o=i.status!==0||u.status!==0?null:z("sha256").update(`${i.stdout.trim()}
4
- ${u.stdout}`).digest("hex");return{base:a,upstream:s?.sha??null,branch:n,checkout:o}}d(se,"observe");function ne(e,t){return e.checkout===null||t.checkout===null?!1:e.base===t.base&&e.upstream===t.upstream&&e.branch===t.branch&&e.checkout===t.checkout}d(ne,"sameObservation");function ie(e,t,r,a,s,n){if(r.status!=="refused"&&r.status!=="failed"||!n.stillEligible())return;const i=(t.sweep?.attempts??0)+1,u=E[Math.min(i,E.length)-1]??E[0],o=v(e)?.managed_worktree;o==null||!D(o)||C(e,{reason:r.reason,...r.detail===void 0?{}:{detail:r.detail},observed:a,attempts:i,lastAttemptAt:new Date(s).toISOString(),nextAttemptAt:new Date(s+u*6e4).toISOString(),quarantined:i>=ee})}d(ie,"recordUnfinished");async function oe(e,t,r,a,s){const n=await c(["status","--porcelain"],e);if(n.status!==0)return{ok:!1,detail:h(n)};const i=await c(["diff","--name-only",r,a],e);if(i.status!==0)return{ok:!1,detail:h(i)};const u=i.stdout.split(`
5
- `).map(p=>p.trim()).filter(p=>p!==""),o=G(j(n.stdout),u);if(o.kind==="refuse")return{ok:!1,detail:`untracked files in ${e} would be overwritten by the incoming commits: ${o.collisions.join(", ")}`};if(!s.stillEligible())return{ok:!1,detail:"node is no longer terminal",ineligible:!0};const l=d(async()=>{const p=await c(["merge","--ff-only",a],e);return p.status===0?{ok:!0}:{ok:!1,detail:h(p)}},"merge");if(o.kind==="direct")return l();const b=await m(e,"refs/stash"),g=await c(["stash","push","--message",`crtr landing ${t}`],e);if(g.status!==0)return{ok:!1,detail:h(g)};const f=await m(e,"refs/stash");if(f===null||f===b)return l();const k=`local changes from ${e} are retained in stash ${f}; recover them with \`git -C ${J(e)} stash apply ${f}\` and resolve any conflict.`,_=await l();return _.ok?(await c(["stash","apply",f],e)).status===0?(await M(e,f),{ok:!0}):(await c(["reset","--hard",a],e),{ok:!0,stash:`the shared base checkout was reset to landed commit ${a} because local changes conflicted with it. ${k}`}):(await c(["stash","apply",f],e)).status===0?(await M(e,f),_):{ok:!1,detail:`${_.detail}
6
- ${k}`}}d(oe,"landIntoBaseCheckoutAsync");async function M(e,t){await m(e,"refs/stash")===t&&await c(["stash","drop"],e)}d(M,"dropStashIfStillTip");async function le(e,t,r){const a=await m(e.path,`refs/heads/${e.base_ref}`);if(a===null)return{ok:!1,detail:`local base branch '${e.base_ref}' does not resolve`};if(!r.stillEligible())return{ok:!1,detail:"node is no longer terminal",ineligible:!0};const s=await c(["rebase",a],e.path);if(s.status!==0)return await c(["rebase","--abort"],e.path),{ok:!1,detail:h(s)};const n=await m(e.path,"HEAD");if(n===null)return{ok:!1,detail:"could not read the rebased worktree tip"};if((await c(["merge-base","--is-ancestor",a,n],e.path)).status!==0)return{ok:!1,detail:`the rebased branch is not a fast-forward of '${e.base_ref}'`};const i=B(t,e.base_ref);if(i===null){if(!r.stillEligible())return{ok:!1,detail:"node is no longer terminal",ineligible:!0};const l=await c(["update-ref",`refs/heads/${e.base_ref}`,n,a],e.path);return l.status===0?{ok:!0,landedSha:n}:{ok:!1,detail:h(l)}}const u=await c(["rev-parse","HEAD"],i);if(u.status!==0||u.stdout.trim()!==a)return{ok:!1,detail:`base checkout ${i} is not at ${a}`};const o=await oe(i,e.branch,a,n,r);return o.ok?{ok:!0,landedSha:n,...o.stash===void 0?{}:{stash:o.stash}}:o}d(le,"finishBlockedLand");const $={status:"skipped",reason:"node_no_longer_terminal"};async function Ee(e,t,r=Date.now()){const a=v(e)?.managed_worktree;if(!D(a)||a==null)return{status:"skipped",reason:"already_reconciled"};const s=y(a.path)?a.path:a.repo_root;if(!y(s))return S(e,a,{status:"refused",reason:"repo_unavailable",detail:`neither the recorded checkout nor repository root exists, so ${a.branch} cannot be proved absent`},{base:null,upstream:null,branch:null,checkout:null},r,t);const n=await L(s);if(n===null)return S(e,a,{status:"failed",reason:"git_dir_unreadable",detail:`could not resolve the git common dir from ${s}`},{base:null,upstream:null,branch:null,checkout:null},r,t);let i={base:null,upstream:null,branch:null,checkout:null},u;const o=[];try{u=await Q(Z(n),async()=>{if(!t.stillEligible())return $;const l=v(e)?.managed_worktree;return!D(l)||l==null?{status:"skipped",reason:"already_reconciled"}:(i=await se(l),l.sweep!==void 0&&ne(l.sweep.observed,i)?{status:"refused",reason:l.sweep.reason,...l.sweep.detail===void 0?{}:{detail:l.sweep.detail},unchanged:!0}:y(l.path)?await ce(e,l,t,o):await ue(e,l,i,t))},{timeoutMs:Y,timeoutError:d(()=>new Error(`timed out waiting for the managed-worktree lock on ${n}`),"timeoutError")})}catch(l){u={status:"failed",reason:"lock_unavailable",detail:l instanceof Error?l.message:String(l)}}return await X(a.path,o[0]),S(e,a,u,i,r,t)}d(Ee,"reconcileManagedWorktree");function S(e,t,r,a,s,n){return ie(e,t,r,a,s,n),r}d(S,"finish");function x(e,t,r){if(!r.stillEligible())return $;const a=v(e)?.managed_worktree;return a==null||a.path!==t.path||a.branch!==t.branch?{status:"skipped",reason:"record_changed"}:(U(e),{status:"complete"})}d(x,"persistComplete");async function ue(e,t,r,a){const s=t.repo_root;if(r.branch===null){const o=await c(["show-ref","--verify","--quiet",`refs/heads/${t.branch}`],s);return o.status!==1?{status:"failed",reason:"branch_inspection_failed",detail:h(o)}:x(e,t,a)}const n=await c(["worktree","list","--porcelain"],s);if(n.status!==0)return{status:"failed",reason:"worktree_list_failed",detail:h(n)};const i=B(n.stdout,t.branch);if(i!==null)return{status:"refused",reason:"branch_checked_out_elsewhere",detail:`${t.branch} is checked out at ${i}`};const u=await A(s,t,r.branch);return u===null?{status:"refused",reason:"unlanded_work",detail:`${t.branch} carries content no base or upstream ref contains; its checkout is already gone, so the branch is the only copy`}:F(e,t,s,r.branch,u,a)}d(ue,"concludeAbsentCheckout");async function ce(e,t,r,a){const s=await c(["worktree","list","--porcelain"],t.path);if(s.status!==0)return{status:"failed",reason:"worktree_list_failed",detail:h(s)};const n=t.disposal,i=n?.observed_branch??t.branch,u=re(s.stdout,t.path,i);if(u===null)return{status:"refused",reason:"not_registered",detail:`${t.path} is not registered to refs/heads/${i}; crtr will not act on a path it cannot prove it owns`};const o=ae(s.stdout);if(o===null||W(o,u)||!y(o))return{status:"refused",reason:"repo_unavailable",detail:`the repository's main checkout is unavailable, so ${t.path} cannot be removed from outside itself`};const l=await te(t.path);if(l===null)return{status:"failed",reason:"git_path_failed",detail:`could not inspect in-progress operations in ${t.path}`};if(l)return{status:"refused",reason:"rebase_in_progress",detail:`${t.path} is mid-rebase`};const b=await c(["rev-parse","--abbrev-ref","HEAD"],t.path);if(b.status!==0)return{status:"failed",reason:"branch_check_failed",detail:h(b)};if(b.stdout.trim()!==i)return{status:"refused",reason:"wrong_branch",detail:`expected ${i}, found ${b.stdout.trim()==="HEAD"?"detached HEAD":b.stdout.trim()}`};const g=await c(["status","--porcelain"],t.path);if(g.status!==0)return{status:"failed",reason:"status_failed",detail:h(g)};if(g.stdout.trim()!=="")return{status:"refused",reason:"dirty_worktree",detail:`${t.path} has uncommitted changes`};let f=await m(t.path,"HEAD");if(f===null)return{status:"failed",reason:"head_unreadable",detail:`could not read HEAD in ${t.path}`};if(n!==void 0){if(await L(t.path)!==n.common_dir||f!==n.observed_sha)return{status:"refused",reason:"disposal_authorization_stale",detail:`${t.path} changed since \`close\` authorized its disposal; rerun \`crtr node worktree close\` from that node to re-authorize, or ask the user with \`crtr human send\``};if(!r.stillEligible())return $;const{result:H,trash:O}=await R(o,u);return O!==void 0&&a.push(O),H.status!==0?{status:"refused",reason:"worktree_remove_failed",detail:h(H)}:x(e,t,r)}let k=await A(t.path,t,f),_;if(k===null&&t.land_intent!==void 0){const w=await le(t,s.stdout,r);if(!w.ok)return w.ineligible===!0?$:{status:"refused",reason:"land_blocked",detail:w.detail};f=w.landedSha,_=w.stash,k=await A(t.path,t,f)}if(k===null)return{status:"refused",reason:"unlanded_work",detail:`${t.branch} carries commits its base does not contain, and this node never asked to land them`};if(!r.stillEligible())return $;const{result:p,trash:P}=await R(o,u);if(P!==void 0&&a.push(P),p.status!==0)return{status:"refused",reason:"worktree_remove_failed",detail:h(p)};const T=await F(e,t,o,f,k,r);return T.status==="complete"&&_!==void 0?{status:"complete",stash:_}:T}d(ce,"reconcilePresentCheckout");async function F(e,t,r,a,s,n){if(!n.stillEligible())return $;const i=I(t.branch,a,s.containingRef,s.containingSha),u=await c(["update-ref","--stdin"],r,i);return u.status!==0?{status:"refused",reason:"branch_delete_failed",detail:h(u)}:x(e,t,n)}d(F,"deleteBranchAndComplete");export{ve as isDueForSweep,D as isReconcilable,Ee as reconcileManagedWorktree};
1
+ var q=Object.defineProperty;var d=(t,e)=>q(t,"name",{value:e,configurable:!0});import{createHash as z}from"node:crypto";import{existsSync as y,realpathSync as K}from"node:fs";import{resolve as N}from"node:path";import{completeCleanup as U,getNode as v,recordSweepAttempt as C}from"./canvas/index.js";import{withExclusiveDirectoryLockAsync as Q}from"./exclusive-lock.js";import{gitAsync as c}from"./git.js";import{proveBranchContained as A,upstreamTip as V}from"./worktree-containment.js";import{parseStatusPorcelain as j,planLandIntoCheckout as G,refDeleteTransactionStdin as I}from"./worktree-landing.js";import{shellQuote as J}from"../shared/shell-quote.js";import{deleteTrashAsync as X,removeWorktreeCheckoutAsync as R}from"./worktree-remove.js";import{WORKTREE_LOCK_WAIT_MS as Y,checkoutPathForBranchInList as B,samePath as W,worktreeLockPathForCommonDir as Z}from"./worktree.js";const E=[2,5,15,30,60],tt=E.length;function D(t){return t==null||t.state==="abandoned"?!1:t.state==="open"||t.cleanup!=="complete"}d(D,"isReconcilable");function vt(t,e){const r=t.sweep?.next_attempt;if(r===void 0)return!0;const a=Date.parse(r);return Number.isNaN(a)||a<=e}d(vt,"isDueForSweep");async function m(t,e){const r=await c(["rev-parse","--verify","--quiet",e],t);return r.status===0?r.stdout.trim():null}d(m,"revParseRef");function h(t){return(t.stderr.trim()||t.stdout.trim()).trim()}d(h,"detailOf");async function L(t){const e=await c(["rev-parse","--git-common-dir"],t);if(e.status!==0)return null;try{return K(N(t,e.stdout.trim()))}catch{return null}}d(L,"commonGitDirAsync");async function et(t){for(const e of["rebase-merge","rebase-apply"]){const r=await c(["rev-parse","--git-path",e],t);if(r.status!==0)return null;if(y(N(t,r.stdout.trim())))return!0}return!1}d(et,"isRebaseInProgressAsync");function at(t){for(const e of t.split(`
2
+ `))if(e.startsWith("worktree "))return e.slice(9).trim();return null}d(at,"mainWorktreePath");function rt(t,e,r){let a=null;for(const s of t.split(`
3
+ `))if(s.startsWith("worktree "))a=s.slice(9).trim();else if(a!==null&&s===`branch refs/heads/${r}`&&W(a,e))return a;return null}d(rt,"registeredCheckoutPath");async function st(t){const e=y(t.path),r=e?t.path:t.repo_root,a=await m(r,`refs/heads/${t.base_ref}`),s=await V(r,t.base_ref),n=await m(r,`refs/heads/${t.branch}`);if(!e)return{base:a,upstream:s?.sha??null,branch:n,checkout:"absent"};const i=await c(["rev-parse","HEAD"],t.path),u=await c(["status","--porcelain"],t.path),o=i.status!==0||u.status!==0?null:z("sha256").update(`${i.stdout.trim()}
4
+ ${u.stdout}`).digest("hex");return{base:a,upstream:s?.sha??null,branch:n,checkout:o}}d(st,"observe");function nt(t,e){return t.checkout===null||e.checkout===null?!1:t.base===e.base&&t.upstream===e.upstream&&t.branch===e.branch&&t.checkout===e.checkout}d(nt,"sameObservation");async function it(t,e,r,a,s,n){if(r.status!=="refused"&&r.status!=="failed"||!await n.stillEligible())return;const i=(e.sweep?.attempts??0)+1,u=E[Math.min(i,E.length)-1]??E[0],o=(await v(t))?.managed_worktree;o==null||!D(o)||await C(t,{reason:r.reason,...r.detail===void 0?{}:{detail:r.detail},observed:a,attempts:i,lastAttemptAt:new Date(s).toISOString(),nextAttemptAt:new Date(s+u*6e4).toISOString(),quarantined:i>=tt})}d(it,"recordUnfinished");async function ot(t,e,r,a,s){const n=await c(["status","--porcelain"],t);if(n.status!==0)return{ok:!1,detail:h(n)};const i=await c(["diff","--name-only",r,a],t);if(i.status!==0)return{ok:!1,detail:h(i)};const u=i.stdout.split(`
5
+ `).map(p=>p.trim()).filter(p=>p!==""),o=G(j(n.stdout),u);if(o.kind==="refuse")return{ok:!1,detail:`untracked files in ${t} would be overwritten by the incoming commits: ${o.collisions.join(", ")}`};if(!await s.stillEligible())return{ok:!1,detail:"node is no longer terminal",ineligible:!0};const l=d(async()=>{const p=await c(["merge","--ff-only",a],t);return p.status===0?{ok:!0}:{ok:!1,detail:h(p)}},"merge");if(o.kind==="direct")return l();const b=await m(t,"refs/stash"),w=await c(["stash","push","--message",`crtr landing ${e}`],t);if(w.status!==0)return{ok:!1,detail:h(w)};const f=await m(t,"refs/stash");if(f===null||f===b)return l();const k=`local changes from ${t} are retained in stash ${f}; recover them with \`git -C ${J(t)} stash apply ${f}\` and resolve any conflict.`,_=await l();return _.ok?(await c(["stash","apply",f],t)).status===0?(await M(t,f),{ok:!0}):(await c(["reset","--hard",a],t),{ok:!0,stash:`the shared base checkout was reset to landed commit ${a} because local changes conflicted with it. ${k}`}):(await c(["stash","apply",f],t)).status===0?(await M(t,f),_):{ok:!1,detail:`${_.detail}
6
+ ${k}`}}d(ot,"landIntoBaseCheckoutAsync");async function M(t,e){await m(t,"refs/stash")===e&&await c(["stash","drop"],t)}d(M,"dropStashIfStillTip");async function lt(t,e,r){const a=await m(t.path,`refs/heads/${t.base_ref}`);if(a===null)return{ok:!1,detail:`local base branch '${t.base_ref}' does not resolve`};if(!await r.stillEligible())return{ok:!1,detail:"node is no longer terminal",ineligible:!0};const s=await c(["rebase",a],t.path);if(s.status!==0)return await c(["rebase","--abort"],t.path),{ok:!1,detail:h(s)};const n=await m(t.path,"HEAD");if(n===null)return{ok:!1,detail:"could not read the rebased worktree tip"};if((await c(["merge-base","--is-ancestor",a,n],t.path)).status!==0)return{ok:!1,detail:`the rebased branch is not a fast-forward of '${t.base_ref}'`};const i=B(e,t.base_ref);if(i===null){if(!await r.stillEligible())return{ok:!1,detail:"node is no longer terminal",ineligible:!0};const l=await c(["update-ref",`refs/heads/${t.base_ref}`,n,a],t.path);return l.status===0?{ok:!0,landedSha:n}:{ok:!1,detail:h(l)}}const u=await c(["rev-parse","HEAD"],i);if(u.status!==0||u.stdout.trim()!==a)return{ok:!1,detail:`base checkout ${i} is not at ${a}`};const o=await ot(i,t.branch,a,n,r);return o.ok?{ok:!0,landedSha:n,...o.stash===void 0?{}:{stash:o.stash}}:o}d(lt,"finishBlockedLand");const $={status:"skipped",reason:"node_no_longer_terminal"};async function Et(t,e,r=Date.now()){const a=(await v(t))?.managed_worktree;if(!D(a)||a==null)return{status:"skipped",reason:"already_reconciled"};const s=y(a.path)?a.path:a.repo_root;if(!y(s))return await S(t,a,{status:"refused",reason:"repo_unavailable",detail:`neither the recorded checkout nor repository root exists, so ${a.branch} cannot be proved absent`},{base:null,upstream:null,branch:null,checkout:null},r,e);const n=await L(s);if(n===null)return await S(t,a,{status:"failed",reason:"git_dir_unreadable",detail:`could not resolve the git common dir from ${s}`},{base:null,upstream:null,branch:null,checkout:null},r,e);let i={base:null,upstream:null,branch:null,checkout:null},u;const o=[];try{u=await Q(Z(n),async()=>{if(!await e.stillEligible())return $;const l=(await v(t))?.managed_worktree;return!D(l)||l==null?{status:"skipped",reason:"already_reconciled"}:(i=await st(l),l.sweep!==void 0&&nt(l.sweep.observed,i)?{status:"refused",reason:l.sweep.reason,...l.sweep.detail===void 0?{}:{detail:l.sweep.detail},unchanged:!0}:y(l.path)?await ct(t,l,e,o):await ut(t,l,i,e))},{timeoutMs:Y,timeoutError:d(()=>new Error(`timed out waiting for the managed-worktree lock on ${n}`),"timeoutError")})}catch(l){u={status:"failed",reason:"lock_unavailable",detail:l instanceof Error?l.message:String(l)}}return await X(a.path,o[0]),await S(t,a,u,i,r,e)}d(Et,"reconcileManagedWorktree");async function S(t,e,r,a,s,n){return await it(t,e,r,a,s,n),r}d(S,"finish");async function x(t,e,r){if(!await r.stillEligible())return $;const a=(await v(t))?.managed_worktree;return a==null||a.path!==e.path||a.branch!==e.branch?{status:"skipped",reason:"record_changed"}:(await U(t),{status:"complete"})}d(x,"persistComplete");async function ut(t,e,r,a){const s=e.repo_root;if(r.branch===null){const o=await c(["show-ref","--verify","--quiet",`refs/heads/${e.branch}`],s);return o.status!==1?{status:"failed",reason:"branch_inspection_failed",detail:h(o)}:await x(t,e,a)}const n=await c(["worktree","list","--porcelain"],s);if(n.status!==0)return{status:"failed",reason:"worktree_list_failed",detail:h(n)};const i=B(n.stdout,e.branch);if(i!==null)return{status:"refused",reason:"branch_checked_out_elsewhere",detail:`${e.branch} is checked out at ${i}`};const u=await A(s,e,r.branch);return u===null?{status:"refused",reason:"unlanded_work",detail:`${e.branch} carries content no base or upstream ref contains; its checkout is already gone, so the branch is the only copy`}:F(t,e,s,r.branch,u,a)}d(ut,"concludeAbsentCheckout");async function ct(t,e,r,a){const s=await c(["worktree","list","--porcelain"],e.path);if(s.status!==0)return{status:"failed",reason:"worktree_list_failed",detail:h(s)};const n=e.disposal,i=n?.observed_branch??e.branch,u=rt(s.stdout,e.path,i);if(u===null)return{status:"refused",reason:"not_registered",detail:`${e.path} is not registered to refs/heads/${i}; crtr will not act on a path it cannot prove it owns`};const o=at(s.stdout);if(o===null||W(o,u)||!y(o))return{status:"refused",reason:"repo_unavailable",detail:`the repository's main checkout is unavailable, so ${e.path} cannot be removed from outside itself`};const l=await et(e.path);if(l===null)return{status:"failed",reason:"git_path_failed",detail:`could not inspect in-progress operations in ${e.path}`};if(l)return{status:"refused",reason:"rebase_in_progress",detail:`${e.path} is mid-rebase`};const b=await c(["rev-parse","--abbrev-ref","HEAD"],e.path);if(b.status!==0)return{status:"failed",reason:"branch_check_failed",detail:h(b)};if(b.stdout.trim()!==i)return{status:"refused",reason:"wrong_branch",detail:`expected ${i}, found ${b.stdout.trim()==="HEAD"?"detached HEAD":b.stdout.trim()}`};const w=await c(["status","--porcelain"],e.path);if(w.status!==0)return{status:"failed",reason:"status_failed",detail:h(w)};if(w.stdout.trim()!=="")return{status:"refused",reason:"dirty_worktree",detail:`${e.path} has uncommitted changes`};let f=await m(e.path,"HEAD");if(f===null)return{status:"failed",reason:"head_unreadable",detail:`could not read HEAD in ${e.path}`};if(n!==void 0){if(await L(e.path)!==n.common_dir||f!==n.observed_sha)return{status:"refused",reason:"disposal_authorization_stale",detail:`${e.path} changed since \`close\` authorized its disposal; rerun \`crtr node worktree close\` from that node to re-authorize, or ask the user with \`crtr human send\``};if(!await r.stillEligible())return $;const{result:H,trash:O}=await R(o,u);return O!==void 0&&a.push(O),H.status!==0?{status:"refused",reason:"worktree_remove_failed",detail:h(H)}:await x(t,e,r)}let k=await A(e.path,e,f),_;if(k===null&&e.land_intent!==void 0){const g=await lt(e,s.stdout,r);if(!g.ok)return g.ineligible===!0?$:{status:"refused",reason:"land_blocked",detail:g.detail};f=g.landedSha,_=g.stash,k=await A(e.path,e,f)}if(k===null)return{status:"refused",reason:"unlanded_work",detail:`${e.branch} carries commits its base does not contain, and this node never asked to land them`};if(!await r.stillEligible())return $;const{result:p,trash:P}=await R(o,u);if(P!==void 0&&a.push(P),p.status!==0)return{status:"refused",reason:"worktree_remove_failed",detail:h(p)};const T=await F(t,e,o,f,k,r);return T.status==="complete"&&_!==void 0?{status:"complete",stash:_}:T}d(ct,"reconcilePresentCheckout");async function F(t,e,r,a,s,n){if(!await n.stillEligible())return $;const i=I(e.branch,a,s.containingRef,s.containingSha),u=await c(["update-ref","--stdin"],r,i);return u.status!==0?{status:"refused",reason:"branch_delete_failed",detail:h(u)}:await x(t,e,n)}d(F,"deleteBranchAndComplete");export{vt as isDueForSweep,D as isReconcilable,Et as reconcileManagedWorktree};
@@ -13,7 +13,7 @@ export declare class WorktreeError extends Error {
13
13
  export declare function managedWorktreesRoot(): string;
14
14
  export declare function managedWorktreePath(nodeId: string): string;
15
15
  export declare function hasOpenManagedWorktree(node: NodeMeta | null | undefined): boolean;
16
- export declare function openManagedWorktreeForNode(nodeId: string): ManagedWorktree | null;
16
+ export declare function openManagedWorktreeForNode(nodeId: string): Promise<ManagedWorktree | null>;
17
17
  export declare function isManagedWorktreeBase(repoRoot: string): boolean;
18
18
  /** Shared parse for a `git worktree list --porcelain` blob already in hand —
19
19
  * callers that must not throw on a read failure (an absent-path proof that
@@ -67,8 +67,8 @@ export interface CloseManagedWorktreeResult {
67
67
  * revive launch fresh instead. Both closing paths MUST go through this helper
68
68
  * instead of hand-rolling the `updateNode` call, so neither can drift out of
69
69
  * sync. */
70
- export declare function persistClosedWorktreeState(nodeId: string, wt: ManagedWorktree, cleanup?: 'pending' | 'complete'): void;
71
- export declare function closeManagedWorktree(nodeId: string): CloseManagedWorktreeResult;
70
+ export declare function persistClosedWorktreeState(nodeId: string, wt: ManagedWorktree, cleanup?: 'pending' | 'complete'): Promise<void>;
71
+ export declare function closeManagedWorktree(nodeId: string): Promise<CloseManagedWorktreeResult>;
72
72
  export interface AbandonManagedWorktreeResult {
73
73
  node_id: string;
74
74
  branch: string;
@@ -114,7 +114,7 @@ export interface AutoDroppedWorktree {
114
114
  * inside the owner broker, so it has the same destructive-cwd boundary as
115
115
  * explicit close: removal and branch deletion are deferred until that owner
116
116
  * has exited. */
117
- export declare function autoDropCleanManagedWorktreeLocked(nodeId: string, wt: ManagedWorktree, proof: CleanEmptyProof): AutoDroppedWorktree | null;
117
+ export declare function autoDropCleanManagedWorktreeLocked(nodeId: string, wt: ManagedWorktree, proof: CleanEmptyProof): Promise<AutoDroppedWorktree | null>;
118
118
  export declare function branchIsPushedAtCurrentTip(wt: ManagedWorktree): Promise<boolean>;
119
- export declare function autoDropCleanManagedWorktree(nodeId: string): AutoDroppedWorktree | null;
119
+ export declare function autoDropCleanManagedWorktree(nodeId: string): Promise<AutoDroppedWorktree | null>;
120
120
  export {};
@@ -1,18 +1,18 @@
1
- var J=Object.defineProperty;var a=(e,r)=>J(e,"name",{value:r,configurable:!0});import{existsSync as k,mkdirSync as H,readFileSync as V,realpathSync as N,rmSync as Y,writeFileSync as Z}from"node:fs";import{createHash as ee}from"node:crypto";import{dirname as te,isAbsolute as re,join as ne,relative as oe,resolve as C,sep as se}from"node:path";import{gitAsync as ae,gitSync as i}from"./git.js";import{crtrHome as O}from"./canvas/paths.js";import{closeWorktreeAndRepoint as ie,getNode as v,recordLandIntent as ce}from"./canvas/index.js";import{CONFIG_FILE as he,CRTR_DIR_NAME as F,STATE_FILE as ue}from"../types.js";import{withExclusiveDirectoryLock as le}from"./exclusive-lock.js";import{parseStatusPorcelain as de,planLandIntoCheckout as fe,refDeleteTransactionStdin as pe}from"./worktree-landing.js";import{shellQuote as w}from"../shared/shell-quote.js";class f extends Error{static{a(this,"WorktreeError")}code;next;detail;constructor(r,t,n,o){super(t),this.name="WorktreeError",this.code=r,this.next=n,this.detail=o}}function _(e,r,t,n){const o=i(r,e);if(o.status!==0){const s=(o.stderr.trim()||o.stdout.trim()).trim();throw new f(t,`git ${r.join(" ")} failed`,n,s)}return o.stdout.trim()}a(_,"runGit");function I(){return`${O()}/worktrees`}a(I,"managedWorktreesRoot");function me(e){return`${I()}/${e}`}a(me,"managedWorktreePath");function Ke(e){return e?.managed_worktree?.state==="open"}a(Ke,"hasOpenManagedWorktree");function x(e){const t=v(e)?.managed_worktree;return t?.state==="open"?t:null}a(x,"openManagedWorktreeForNode");function be(e){const r=i(["rev-parse","--show-toplevel"],e);if(r.status!==0){const t=(r.stderr.trim()||r.stdout.trim()).trim();throw new f("not_git_repo",`cwd ${e} is not a git repository`,"Pass --cwd <real repo> pointing inside a git repository.",t)}return r.stdout.trim()}a(be,"repoRootFor");function ke(e){const r=oe(S(I()),S(e));return r!==""&&r!==".."&&!r.startsWith(`..${se}`)&&!re(r)}a(ke,"isManagedWorktreeBase");function _e(e,r){const t=`refs/heads/${r}`,n=i(["show-ref","--verify","--quiet",t],e);if(n.status===0)return!0;if(n.status===1&&n.executionFailed!==!0)return!1;const o=n.stderr.trim()||"<empty>",s=n.processError===void 0?"":`
1
+ var Z=Object.defineProperty;var o=(e,r)=>Z(e,"name",{value:r,configurable:!0});import{existsSync as k,mkdirSync as P,readFileSync as ee,realpathSync as H,rmSync as te,writeFileSync as re}from"node:fs";import{createHash as ne}from"node:crypto";import{dirname as oe,isAbsolute as ae,join as se,relative as ie,resolve as N,sep as ce}from"node:path";import{gitAsync as he,gitSync as i}from"./git.js";import{crtrHome as C}from"./canvas/paths.js";import{closeWorktreeAndRepoint as ue,getNode as v,recordLandIntent as le}from"./canvas/index.js";import{CONFIG_FILE as de,CRTR_DIR_NAME as O,STATE_FILE as fe}from"../types.js";import{withExclusiveDirectoryLock as pe,withExclusiveDirectoryLockAsync as me}from"./exclusive-lock.js";import{parseStatusPorcelain as be,planLandIntoCheckout as ke,refDeleteTransactionStdin as _e}from"./worktree-landing.js";import{shellQuote as w}from"../shared/shell-quote.js";class f extends Error{static{o(this,"WorktreeError")}code;next;detail;constructor(r,t,n,a){super(t),this.name="WorktreeError",this.code=r,this.next=n,this.detail=a}}function _(e,r,t,n){const a=i(r,e);if(a.status!==0){const s=(a.stderr.trim()||a.stdout.trim()).trim();throw new f(t,`git ${r.join(" ")} failed`,n,s)}return a.stdout.trim()}o(_,"runGit");function F(){return`${C()}/worktrees`}o(F,"managedWorktreesRoot");function ge(e){return`${F()}/${e}`}o(ge,"managedWorktreePath");function Xe(e){return e?.managed_worktree?.state==="open"}o(Xe,"hasOpenManagedWorktree");async function x(e){const t=(await v(e))?.managed_worktree;return t?.state==="open"?t:null}o(x,"openManagedWorktreeForNode");function we(e){const r=i(["rev-parse","--show-toplevel"],e);if(r.status!==0){const t=(r.stderr.trim()||r.stdout.trim()).trim();throw new f("not_git_repo",`cwd ${e} is not a git repository`,"Pass --cwd <real repo> pointing inside a git repository.",t)}return r.stdout.trim()}o(we,"repoRootFor");function ye(e){const r=ie(E(F()),E(e));return r!==""&&r!==".."&&!r.startsWith(`..${ce}`)&&!ae(r)}o(ye,"isManagedWorktreeBase");function $e(e,r){const t=`refs/heads/${r}`,n=i(["show-ref","--verify","--quiet",t],e);if(n.status===0)return!0;if(n.status===1&&n.executionFailed!==!0)return!1;const a=n.stderr.trim()||"<empty>",s=n.processError===void 0?"":`
2
2
  process error: ${n.processError}`;throw new f("recorded_branch_inspection_failed",`could not inspect recorded branch '${r}' (git show-ref exited with status ${n.status})`,"Inspect the repository and its Git metadata, then retry.",`status: ${n.status}
3
- stderr: ${o}${s}`)}a(_e,"recordedBranchExists");function A(e,r){const t=`refs/heads/${r}`,n=i(["show-ref","--verify","--quiet",t],e);if(n.status===1&&n.executionFailed!==!0)throw new f("missing_base_branch",`managed worktree base '${r}' does not name an existing local branch`,"The base must be a local branch that landing can fast-forward \u2014 freshen your local branch from origin first (rebase-not-merge), then base on it.");if(n.status!==0){const o=n.stderr.trim()||"<empty>",s=n.processError===void 0?"":`
3
+ stderr: ${a}${s}`)}o($e,"recordedBranchExists");function A(e,r){const t=`refs/heads/${r}`,n=i(["show-ref","--verify","--quiet",t],e);if(n.status===1&&n.executionFailed!==!0)throw new f("missing_base_branch",`managed worktree base '${r}' does not name an existing local branch`,"The base must be a local branch that landing can fast-forward \u2014 freshen your local branch from origin first (rebase-not-merge), then base on it.");if(n.status!==0){const a=n.stderr.trim()||"<empty>",s=n.processError===void 0?"":`
4
4
  process error: ${n.processError}`;throw new f("base_branch_inspection_failed",`could not inspect local base branch '${r}' (git show-ref exited with status ${n.status})`,"Inspect the repository and its Git metadata, then retry.",`status: ${n.status}
5
- stderr: ${o}${s}`)}return _(e,["rev-parse","--verify",t],"base_branch_resolve_failed","Inspect the local base branch, then retry.")}a(A,"resolveLandableBase");function ge(e,r){const t=_(e,["worktree","list","--porcelain"],"worktree_list_failed","Inspect the repository worktrees manually, then retry.");return q(t,r)}a(ge,"checkoutPathForBranch");function q(e,r){let t=null;for(const n of e.split(`
6
- `))if(n.startsWith("worktree "))t=n.slice(9).trim();else if(n===`branch refs/heads/${r}`)return t;return null}a(q,"checkoutPathForBranchInList");function we(e,r){return e===r||S(e)===S(r)}a(we,"samePath");function S(e){try{return N.native(e)}catch{return C(e)}}a(S,"canonicalPath");const $e=3e4;function ye(e){const r=`${O()}/worktree-locks`;return H(r,{recursive:!0}),`${r}/${ee("sha256").update(e).digest("hex")}`}a(ye,"worktreeLockPathForCommonDir");function R(e){const r=_(e,["rev-parse","--git-common-dir"],"git_common_dir_failed","Inspect the repository git metadata, then retry.");return N(C(e,r))}a(R,"commonGitDir");function E(e,r){const t=R(e);return le(ye(t),r,{timeoutMs:$e,timeoutError:a(()=>new f("worktree_lock_timeout",`timed out waiting to create a managed worktree in ${t}`,"Wait for the other managed-worktree operation to finish, then retry."),"timeoutError")})}a(E,"withRepositoryWorktreeLock");function D(e,r,t){const n=i(["worktree","list","--porcelain"],e);if(n.status!==0)return null;let o=null;for(const s of n.stdout.split(`
7
- `))if(s.startsWith("worktree "))o=s.slice(9).trim();else if(o!==null&&we(o,r)&&s===`branch refs/heads/${t}`)return!0;return!1}a(D,"registeredWorktree");function ve(e,r,t){const n=[],o=`refs/heads/${r}`,s=i(["rev-parse","--verify","--quiet",o],e);s.status===0&&n.push({ref:o,sha:s.stdout.trim()});const u=i(["rev-parse","--symbolic-full-name","--verify",`${r}@{upstream}`],e),h=u.status===0&&u.stdout.trim().startsWith("refs/")?u.stdout.trim():`refs/remotes/origin/${r}`,m=i(["rev-parse","--verify","--quiet",h],e);if(m.status===0&&n.push({ref:h,sha:m.stdout.trim()}),n.length===0)return null;for(const d of n)if(i(["merge-base","--is-ancestor",t,d.sha],e).status===0)return{containingRef:d.ref,containingSha:d.sha};for(const d of n){const l=i(["cherry",d.sha,t],e);if(l.status!==0)continue;if(l.stdout.split(`
5
+ stderr: ${a}${s}`)}return _(e,["rev-parse","--verify",t],"base_branch_resolve_failed","Inspect the local base branch, then retry.")}o(A,"resolveLandableBase");function ve(e,r){const t=_(e,["worktree","list","--porcelain"],"worktree_list_failed","Inspect the repository worktrees manually, then retry.");return I(t,r)}o(ve,"checkoutPathForBranch");function I(e,r){let t=null;for(const n of e.split(`
6
+ `))if(n.startsWith("worktree "))t=n.slice(9).trim();else if(n===`branch refs/heads/${r}`)return t;return null}o(I,"checkoutPathForBranchInList");function xe(e,r){return e===r||E(e)===E(r)}o(xe,"samePath");function E(e){try{return H.native(e)}catch{return N(e)}}o(E,"canonicalPath");const q=3e4;function G(e){const r=`${C()}/worktree-locks`;return P(r,{recursive:!0}),`${r}/${ne("sha256").update(e).digest("hex")}`}o(G,"worktreeLockPathForCommonDir");function S(e){const r=_(e,["rev-parse","--git-common-dir"],"git_common_dir_failed","Inspect the repository git metadata, then retry.");return H(N(e,r))}o(S,"commonGitDir");function B(e,r){const t=S(e);return pe(G(t),r,{timeoutMs:q,timeoutError:o(()=>new f("worktree_lock_timeout",`timed out waiting to create a managed worktree in ${t}`,"Wait for the other managed-worktree operation to finish, then retry."),"timeoutError")})}o(B,"withRepositoryWorktreeLock");async function j(e,r){const t=S(e);return me(G(t),r,{timeoutMs:q,timeoutError:o(()=>new f("worktree_lock_timeout",`timed out waiting to create a managed worktree in ${t}`,"Wait for the other managed-worktree operation to finish, then retry."),"timeoutError")})}o(j,"withRepositoryWorktreeLockAsync");function D(e,r,t){const n=i(["worktree","list","--porcelain"],e);if(n.status!==0)return null;let a=null;for(const s of n.stdout.split(`
7
+ `))if(s.startsWith("worktree "))a=s.slice(9).trim();else if(a!==null&&xe(a,r)&&s===`branch refs/heads/${t}`)return!0;return!1}o(D,"registeredWorktree");function Ee(e,r,t){const n=[],a=`refs/heads/${r}`,s=i(["rev-parse","--verify","--quiet",a],e);s.status===0&&n.push({ref:a,sha:s.stdout.trim()});const u=i(["rev-parse","--symbolic-full-name","--verify",`${r}@{upstream}`],e),h=u.status===0&&u.stdout.trim().startsWith("refs/")?u.stdout.trim():`refs/remotes/origin/${r}`,m=i(["rev-parse","--verify","--quiet",h],e);if(m.status===0&&n.push({ref:h,sha:m.stdout.trim()}),n.length===0)return null;for(const d of n)if(i(["merge-base","--is-ancestor",t,d.sha],e).status===0)return{containingRef:d.ref,containingSha:d.sha};for(const d of n){const l=i(["cherry",d.sha,t],e);if(l.status!==0)continue;if(l.stdout.split(`
8
8
  `).map(c=>c.trim()).filter(c=>c!=="").every(c=>c.startsWith("-")))return{containingRef:d.ref,containingSha:d.sha}}for(const d of n){const l=i(["rev-parse","--verify",`${d.sha}^{tree}`],e);if(l.status!==0)continue;const b=i(["merge-tree","--write-tree",d.sha,t],e);if(b.status===0&&b.stdout.split(`
9
- `)[0]?.trim()===l.stdout.trim())return{containingRef:d.ref,containingSha:d.sha}}return null}a(ve,"proveBranchContained");function G(e,r){const t=i(["rev-parse","--verify",`refs/heads/${e.branch}`],e.repo_root);if(t.status!==0||t.stdout.trim()!==e.base_sha)return;const n=D(e.repo_root,e.path,e.branch);if(n!==null){if(n){if(i(["worktree","remove","--force",e.path],e.repo_root).status!==0||D(e.repo_root,e.path,e.branch)!==!1)return}else if(r&&k(e.path))try{Y(e.path,{recursive:!0,force:!0})}catch{return}try{i(["update-ref","-d",`refs/heads/${e.branch}`,e.base_sha],e.repo_root)}catch{}}}a(G,"rollbackManagedWorktreeLocked");function Ue(e){E(e.repo_root,()=>G(e,!1))}a(Ue,"rollbackManagedWorktree");const xe="# crtr: exclude crtr's own untracked project-scope bookkeeping (auto-added at managed-worktree creation)",Se=[`/${F}/${he}`,`/${F}/${ue}`];function Ee(e){try{const r=ne(R(e),"info","exclude");H(te(r),{recursive:!0});let t="";try{t=V(r,"utf8")}catch{}const n=new Set(t.split(`
10
- `).map(h=>h.trim())),o=Se.filter(h=>!n.has(h));if(o.length===0)return;const u=(t.length>0&&!t.endsWith(`
9
+ `)[0]?.trim()===l.stdout.trim())return{containingRef:d.ref,containingSha:d.sha}}return null}o(Ee,"proveBranchContained");function K(e,r){const t=i(["rev-parse","--verify",`refs/heads/${e.branch}`],e.repo_root);if(t.status!==0||t.stdout.trim()!==e.base_sha)return;const n=D(e.repo_root,e.path,e.branch);if(n!==null){if(n){if(i(["worktree","remove","--force",e.path],e.repo_root).status!==0||D(e.repo_root,e.path,e.branch)!==!1)return}else if(r&&k(e.path))try{te(e.path,{recursive:!0,force:!0})}catch{return}try{i(["update-ref","-d",`refs/heads/${e.branch}`,e.base_sha],e.repo_root)}catch{}}}o(K,"rollbackManagedWorktreeLocked");function ze(e){B(e.repo_root,()=>K(e,!1))}o(ze,"rollbackManagedWorktree");const Se="# crtr: exclude crtr's own untracked project-scope bookkeeping (auto-added at managed-worktree creation)",De=[`/${O}/${de}`,`/${O}/${fe}`];function We(e){try{const r=se(S(e),"info","exclude");P(oe(r),{recursive:!0});let t="";try{t=ee(r,"utf8")}catch{}const n=new Set(t.split(`
10
+ `).map(h=>h.trim())),a=De.filter(h=>!n.has(h));if(a.length===0)return;const u=(t.length>0&&!t.endsWith(`
11
11
  `)?`
12
- `:"")+xe+`
13
- `+o.join(`
12
+ `:"")+Se+`
13
+ `+a.join(`
14
14
  `)+`
15
- `;Z(r,t+u)}catch{}}a(Ee,"seedManagedWorktreeExclude");function Xe(e,r,t="main"){const n=be(e);if(ke(n))throw new f("managed_worktree_base",`cwd ${n} is a managed worktree and cannot be used as a managed worktree base`,"Pass `--cwd` pointing at the repository\u2019s primary checkout and keep `--worktree` set to the desired existing local base branch, including the parent branch.");return E(n,()=>{const o=A(n,t),s=me(r),u=`crtr/${r}`;if(k(s))throw new f("worktree_path_exists",`managed worktree path already exists: ${s}`,"Remove the stale path or choose a fresh node id.");if(i(["show-ref","--verify","--quiet",`refs/heads/${u}`],n).status===0)throw new f("worktree_branch_exists",`managed worktree branch already exists: ${u}`,"Remove the stale branch or choose a fresh node id.");const h={state:"open",path:s,branch:u,repo_root:n,base_ref:t,base_sha:o,created:new Date().toISOString()};_(n,["branch",u,o],"worktree_branch_create_failed","Inspect the git error, fix the repository/worktree state, then retry spawning with --worktree.");try{return _(n,["worktree","add",s,u],"worktree_add_failed","Inspect the git error, fix the repository/worktree state, then retry spawning with --worktree."),Ee(n),h}catch(m){throw G(h,!0),m}})}a(Xe,"createManagedWorktree");function De(e){return _(e,["status","--porcelain"],"status_failed","Inspect the worktree manually, then retry.")}a(De,"statusPorcelain");function B(e){return _(e,["rev-parse","--abbrev-ref","HEAD"],"branch_check_failed","Inspect the worktree manually, then retry.")}a(B,"currentBranch");function j(e,r){return _(e,["rev-parse","--git-path",r],"git_path_failed","Inspect the worktree manually, then retry.")}a(j,"gitPath");function M(e){return k(j(e,"rebase-merge"))||k(j(e,"rebase-apply"))}a(M,"isRebaseInProgress");function y(e,r,t="pending"){const n=v(e);ie(e,{cwd:r.repo_root,cleanup:t,closedAt:new Date().toISOString(),unbindSession:n?.pi_session_id!=null||n?.pi_session_file!=null})}a(y,"persistClosedWorktreeState");function We(e,r){const t=v(e)?.managed_worktree;t==null||t.state!=="open"||ce(e,{at:new Date().toISOString(),blocked:r})}a(We,"recordBlockedLandIntent");function Ae(e,r,t,n){const o=i(["status","--porcelain"],e);if(o.status!==0)return{ok:!1,detail:(o.stderr.trim()||o.stdout.trim()).trim()};const s=i(["diff","--name-only",t,n],e);if(s.status!==0)return{ok:!1,detail:(s.stderr.trim()||s.stdout.trim()).trim()};const u=s.stdout.split(`
16
- `).map(g=>g.trim()).filter(g=>g!==""),h=fe(de(o.stdout),u);if(h.kind==="refuse")return{ok:!1,detail:`untracked files in ${e} would be overwritten by the incoming commits: ${h.collisions.join(", ")}`};const m=a(()=>{const g=i(["merge","--ff-only",n],e);return g.status===0?{ok:!0}:{ok:!1,detail:(g.stderr.trim()||g.stdout.trim()).trim()}},"merge");if(h.kind==="direct")return m();const d=i(["rev-parse","--verify","--quiet","refs/stash"],e),l=d.status===0?d.stdout.trim():null,b=i(["stash","push","--message",`crtr landing ${r}`],e);if(b.status!==0)return{ok:!1,detail:(b.stderr.trim()||b.stdout.trim()).trim()};const c=i(["rev-parse","--verify","--quiet","refs/stash"],e),p=c.status===0?c.stdout.trim():null;if(p===null||p===l)return m();const $=`local changes from ${e} are retained in stash ${p}; recover them with \`git -C ${w(e)} stash apply ${p}\` and resolve any conflict.`,W=m();return W.ok?i(["stash","apply",p],e).status===0?(K(e,p),{ok:!0}):(i(["reset","--hard",n],e),{ok:!0,stash:`the shared base checkout was reset to landed commit ${n} because local changes conflicted with it. ${$}`}):i(["stash","apply",p],e).status===0?(K(e,p),W):{ok:!1,detail:`${W.detail}
17
- ${$}`}}a(Ae,"landIntoBaseCheckout");function K(e,r){const t=i(["rev-parse","--verify","--quiet","refs/stash"],e);t.status===0&&t.stdout.trim()===r&&i(["stash","drop"],e)}a(K,"dropStashIfStillTip");function T(e){const r=`git -C ${w(e.repo_root)} worktree remove ${w(e.path)}`;return{worktree_removed:!1,worktree_remove_error:`cleanup pending: the caller checkout at ${e.path} remains intact. After its node exits, run \`${r}\` after confirming it is safe to delete.`,branch_deleted:!1,branch_delete_error:`cleanup pending: local branch ${e.branch} remains checked out at ${e.path}; delete it only after that checkout is removed and its landed commit is still contained in ${e.base_ref}.`}}a(T,"deferredCleanupResult");function Re(e,r,t){if(D(r.repo_root,r.path,t)!==!0)throw new f("wrong_branch",`managed worktree's recorded branch ${r.branch} no longer exists, and its checkout at ${r.path} is not registered to ${t} either`,`Inspect ${r.path} and its git worktree registration, then ask the user with \`crtr human send\` if you are unsure how to proceed.`);const n=_(r.path,["rev-parse","HEAD"],"branch_check_failed","Inspect the worktree manually, then retry.");if(ve(r.path,r.base_ref,n)===null)throw new f("branch_moved_unlanded",`managed worktree's recorded branch ${r.branch} no longer exists; its checkout is now on ${t} at ${n}, which carries content local base branch '${r.base_ref}' does not yet contain`,`Land or push ${t} through its own workflow (merge its PR, or fast-forward '${r.base_ref}' to include it) before closing, or ask the user with \`crtr human send\` if you are unsure how ${t} should be delivered.`);const s=R(r.repo_root),u=D(r.repo_root,r.path,t)===!0,h=B(r.path)===t,m=_(r.path,["rev-parse","HEAD"],"branch_check_failed","Inspect the worktree manually, then retry.")===n;if(!u||!h||!m)throw new f("wrong_branch",`managed worktree at ${r.path} changed while closing`,`Inspect ${r.path}, then rerun \`crtr node worktree close\`.`);const d={observed_branch:t,observed_sha:n,common_dir:s,authorized:new Date().toISOString()};y(e,{...r,disposal:d},"pending");const l=`git -C ${w(r.repo_root)} worktree remove ${w(r.path)}`;return{node_id:e,branch:t,worktree_path:r.path,landed_sha:n,worktree_removed:!1,worktree_remove_error:`cleanup pending: the caller checkout at ${r.path} remains intact. After its node exits, run \`${l}\` after confirming it is safe to delete.`,branch_deleted:!1,branch_delete_error:`${t} is not a crtr-managed branch and will never be deleted automatically; it is preserved once the checkout above is removed.`}}a(Re,"closeBranchMovedWorktree");function Me(e,r){const t=v(e)?.managed_worktree;if(t==null)return{message:`node ${e} has no managed worktree`,next:`Nothing to ${r} for this node.`};if(t.state==="abandoned"){const n=t.abandoned?.branch??t.branch;return{message:`node ${e}'s managed worktree was already abandoned`,next:`Nothing to ${r} for this node; its work was retained on branch ${n}.`}}return t.state==="closed"?t.cleanup==="complete"?{message:`node ${e}'s managed worktree is already closed and its cleanup is complete`,next:`Nothing to ${r} for this node.`}:{message:`node ${e}'s managed worktree is already closed; this is not lost ownership \u2014 its checkout at ${t.path} is scheduled for automatic cleanup once that is safe`,next:`Nothing to ${r} for this node. The daemon sweep finishes removing ${t.path} on its own; inspect it directly only if it still needs manual attention.`}:{message:`node ${e} has no open managed worktree`,next:`Nothing to ${r} for this node.`}}a(Me,"noManagedWorktreeMessage");function U(e){if(v(e)===null)throw new f("unknown_node",`unknown node: ${e}`,"Run this from a live node, or pass a real node id once that command supports it.");const t=x(e);if(t===null){const{message:c,next:p}=Me(e,"close");throw new f("no_managed_worktree",c,p)}if(!k(t.path)){const c=P(t);if(c===null)throw new f("worktree_missing",`managed worktree path is missing: ${t.path}`,X(t));if(z(e,t,c)===null)throw new f("worktree_missing",`managed worktree path is missing: ${t.path}`,X(t));return{node_id:e,branch:t.branch,worktree_path:t.path,landed_sha:c.branchSha??t.base_sha,worktree_removed:!0,branch_deleted:!0}}if(M(t.path))throw new f("rebase_in_progress","this managed worktree is mid-rebase",`Resolve conflicts in ${t.path}, run \`git rebase --continue\` there, then rerun \`crtr node worktree close\`.`);if(De(t.path)!=="")throw new f("dirty_worktree","managed worktree has uncommitted changes",`Commit the work in ${t.path} (or ask the user with \`crtr human send\` if you do not know whether to keep it), then rerun \`crtr node worktree close\`.`);const o=B(t.path);if(o!==t.branch){const c=o==="HEAD"?"detached HEAD":o,p=_e(t.path,t.branch);if(p||o==="HEAD")throw new f("wrong_branch",`managed worktree is not on its recorded branch (expected ${t.branch}, found ${c})`,p?`Check out ${t.branch} in ${t.path} before closing, or ask the user with \`crtr human send\` if you are unsure why it changed, then rerun \`crtr node worktree close\`.`:`The recorded branch ${t.branch} no longer exists, and ${t.path} is in detached HEAD, so there is no branch to preserve. Check out or create a branch there before closing, or ask the user with \`crtr human send\` if you are unsure how to proceed.`);return Re(e,t,o)}let s;try{s=A(t.path,t.base_ref)}catch(c){const p=_(t.path,["rev-parse","HEAD"],"landed_sha_failed","Inspect the worktree checkout manually, then rerun `crtr node worktree close`.");if(!(c instanceof f)||c.code!=="missing_base_branch"||p!==t.base_sha)throw c;return y(e,t,"pending"),{node_id:e,branch:t.branch,worktree_path:t.path,landed_sha:t.base_sha,...T(t)}}const u=i(["rebase",s],t.path);if(u.status!==0){const c=(u.stderr.trim()||u.stdout.trim()).trim();throw new f("rebase_failed",`rebase onto local base branch '${t.base_ref}' did not complete`,`Resolve conflicts in ${t.path}, run \`git rebase --continue\` there, then rerun \`crtr node worktree close\`. If the blocker is not mechanical, ask the user with \`crtr human send\`.`,c)}const h=_(t.path,["rev-parse","HEAD"],"landed_sha_failed","Inspect the worktree checkout manually, then rerun `crtr node worktree close`."),m=a((c,p,$)=>(We(e,c),new f("land_base_failed",c,p,$)),"blocked"),d=i(["merge-base","--is-ancestor",s,h],t.path);if(d.status!==0)throw m(`rebased worktree is not a fast-forward of local base branch '${t.base_ref}'`,"Ask the user with `crtr human send` how to land this managed worktree.",(d.stderr.trim()||d.stdout.trim()).trim());const l=ge(t.path,t.base_ref);if(l!==null){const c=i(["rev-parse","HEAD"],l),p=c.stdout.trim();if(c.status!==0||p!==s){const $=c.status!==0?(c.stderr.trim()||p).trim():`expected ${s}, found ${p}`;throw m(`local base branch '${t.base_ref}' changed while this worktree was closing`,`Reconcile the base checkout at ${l}, then rerun \`crtr node worktree close\`. If you are unsure, ask the user with \`crtr human send\`.`,$)}}let b;if(l!==null){const c=Ae(l,t.branch,s,h);if(!c.ok)throw m(`could not fast-forward local base branch '${t.base_ref}' to this worktree`,`Local base branch '${t.base_ref}' is checked out at ${l} in a state that blocks a fast-forward. Reconcile it there, then rerun \`crtr node worktree close\`. If you are unsure, ask the user with \`crtr human send\`.`,c.detail);b=c.stash}else{const c=i(["update-ref",`refs/heads/${t.base_ref}`,h,s],t.path);if(c.status!==0)throw m(`could not fast-forward local base branch '${t.base_ref}' to this worktree`,"Ask the user with `crtr human send` how to land this managed worktree.",(c.stderr.trim()||c.stdout.trim()).trim())}return y(e,t,"pending"),{node_id:e,branch:t.branch,worktree_path:t.path,landed_sha:h,...T(t),...b===void 0?{}:{base_checkout_stash:b}}}a(U,"closeManagedWorktreeLocked");function ze(e){const r=x(e);return r===null?U(e):E(k(r.path)?r.path:r.repo_root,()=>U(e))}a(ze,"closeManagedWorktree");function Te(e,r,t,n){const o=pe(e.branch,r,t,n),s=i(["update-ref","--stdin"],e.repo_root,o),u=s.status===0;if(u)return{branchDeleted:u};const h=(s.stderr.trim()||s.stdout.trim()).trim();return{branchDeleted:u,branchDeleteError:`local branch ${e.branch} could not be deleted automatically (${h}); run \`git -C ${w(e.repo_root)} branch -D ${w(e.branch)}\` after confirming it is merged.`}}a(Te,"bestEffortDeleteBranchWithContainmentProof");function P(e){if(e.state!=="open"||k(e.path))return null;const r=`refs/heads/${e.branch}`,t=i(["show-ref","--verify","--quiet",r],e.repo_root);if(t.status===1)return{branchSha:null};if(t.status!==0)return null;const n=i(["rev-parse","--verify",r],e.repo_root);if(n.status!==0)return null;const o=n.stdout.trim(),s=i(["worktree","list","--porcelain"],e.repo_root);if(s.status!==0||q(s.stdout,e.branch)!==null)return null;const u=[],h=`refs/heads/${e.base_ref}`,m=i(["show-ref","--verify","--quiet",h],e.repo_root);if(m.status===0){const l=i(["rev-parse","--verify",h],e.repo_root);if(l.status!==0)return null;u.push({ref:h,sha:l.stdout.trim()})}else if(m.status!==1)return null;const d=i(["rev-parse","--symbolic-full-name","--verify",`${e.base_ref}@{upstream}`],e.repo_root);if(d.status===0){const l=d.stdout.trim(),b=i(["rev-parse","--verify",l],e.repo_root);l.startsWith("refs/")&&b.status===0&&u.push({ref:l,sha:b.stdout.trim()})}else{const l=`refs/remotes/origin/${e.base_ref}`,b=i(["show-ref","--verify","--quiet",l],e.repo_root);if(b.status===0){const c=i(["rev-parse","--verify",l],e.repo_root);c.status===0&&u.push({ref:l,sha:c.stdout.trim()})}else if(b.status!==1)return null}for(const l of u)if(i(["merge-base","--is-ancestor",o,l.sha],e.repo_root).status===0)return{branchSha:o,containingRef:l.ref,containingSha:l.sha};return null}a(P,"proveAbsentPathManagedWorktree");function X(e){return`The recorded checkout path is gone, but crtr cannot prove ${e.branch} is already delivered. Push or land ${e.branch}, then rerun \`crtr push final\`; if you cannot establish what happened to the branch, ask the user with \`crtr human send\`.`}a(X,"missingWorktreeRecoveryHint");function L(e){if(e.state!=="open"||!k(e.path)||M(e.path))return null;const r=i(["rev-parse","--abbrev-ref","HEAD"],e.path);if(r.status!==0||r.stdout.trim()!==e.branch)return null;const t=i(["status","--porcelain"],e.path);if(t.status!==0||t.stdout.trim()!=="")return null;const n=i(["rev-parse","HEAD"],e.path);if(n.status!==0)return null;const o=n.stdout.trim();let s;try{s=A(e.path,e.base_ref)}catch(h){if(h instanceof f&&h.code==="missing_base_branch"&&o===e.base_sha)return{branchSha:o,baseSha:e.base_sha};throw h}return i(["merge-base","--is-ancestor",o,s],e.path).status!==0?null:{branchSha:o,baseSha:s}}a(L,"proveCleanEmptyManagedWorktree");function z(e,r,t){if(k(r.path))return null;if(t.branchSha===null)return y(e,r,"complete"),{node_id:e,branch:r.branch,worktree_path:r.path,worktree_removed:!0,branch_deleted:!0};if(t.containingRef===void 0||t.containingSha===void 0)return null;const{branchDeleted:n}=Te(r,t.branchSha,t.containingRef,t.containingSha);return n?(y(e,r,"complete"),{node_id:e,branch:r.branch,worktree_path:r.path,worktree_removed:!0,branch_deleted:!0}):null}a(z,"autoDropAbsentPathManagedWorktreeLocked");function Pe(e,r,t){const n=L(r);return n===null||n.branchSha!==t.branchSha?null:(y(e,r,"pending"),{node_id:e,branch:r.branch,worktree_path:r.path,...T(r)})}a(Pe,"autoDropCleanManagedWorktreeLocked");function Q(e){if(e.state!=="open"||!k(e.path)||M(e.path))return null;const r=i(["status","--porcelain"],e.path);if(r.status!==0||r.stdout.trim()!=="")return null;const t=i(["rev-parse","HEAD"],e.path);return t.status===0?t.stdout.trim():null}a(Q,"cleanCurrentCheckoutTip");async function Qe(e){const r=Q(e);if(r===null)return!1;const t=process.env.GIT_SSH_COMMAND??process.env.GIT_SSH??"ssh",n=await ae(["ls-remote","--exit-code","--heads","origin"],e.path,void 0,{timeoutMs:1e4,env:{...process.env,GIT_TERMINAL_PROMPT:"0",GIT_SSH_COMMAND:`${t} -o BatchMode=yes`}});return n.status!==0||!n.stdout.split(`
18
- `).some(s=>{const[u,h]=s.split(" ",2);return u===r&&h?.startsWith("refs/heads/")===!0})?!1:Q(e)===r}a(Qe,"branchIsPushedAtCurrentTip");function Je(e){const r=x(e);if(r===null)return null;const t=!k(r.path);return(t?P(r)===null:L(r)===null)?null:E(t?r.repo_root:r.path,()=>{const n=x(e);if(n===null)return null;if(!k(n.path)){const s=P(n);return s===null?null:z(e,n,s)}const o=L(n);return o===null?null:Pe(e,n,o)})}a(Je,"autoDropCleanManagedWorktree");export{$e as WORKTREE_LOCK_WAIT_MS,f as WorktreeError,Je as autoDropCleanManagedWorktree,Pe as autoDropCleanManagedWorktreeLocked,Qe as branchIsPushedAtCurrentTip,q as checkoutPathForBranchInList,ze as closeManagedWorktree,Xe as createManagedWorktree,Ke as hasOpenManagedWorktree,ke as isManagedWorktreeBase,M as isRebaseInProgress,me as managedWorktreePath,I as managedWorktreesRoot,X as missingWorktreeRecoveryHint,x as openManagedWorktreeForNode,y as persistClosedWorktreeState,L as proveCleanEmptyManagedWorktree,Ue as rollbackManagedWorktree,we as samePath,ye as worktreeLockPathForCommonDir};
15
+ `;re(r,t+u)}catch{}}o(We,"seedManagedWorktreeExclude");function Qe(e,r,t="main"){const n=we(e);if(ye(n))throw new f("managed_worktree_base",`cwd ${n} is a managed worktree and cannot be used as a managed worktree base`,"Pass `--cwd` pointing at the repository\u2019s primary checkout and keep `--worktree` set to the desired existing local base branch, including the parent branch.");return B(n,()=>{const a=A(n,t),s=ge(r),u=`crtr/${r}`;if(k(s))throw new f("worktree_path_exists",`managed worktree path already exists: ${s}`,"Remove the stale path or choose a fresh node id.");if(i(["show-ref","--verify","--quiet",`refs/heads/${u}`],n).status===0)throw new f("worktree_branch_exists",`managed worktree branch already exists: ${u}`,"Remove the stale branch or choose a fresh node id.");const h={state:"open",path:s,branch:u,repo_root:n,base_ref:t,base_sha:a,created:new Date().toISOString()};_(n,["branch",u,a],"worktree_branch_create_failed","Inspect the git error, fix the repository/worktree state, then retry spawning with --worktree.");try{return _(n,["worktree","add",s,u],"worktree_add_failed","Inspect the git error, fix the repository/worktree state, then retry spawning with --worktree."),We(n),h}catch(m){throw K(h,!0),m}})}o(Qe,"createManagedWorktree");function Ae(e){return _(e,["status","--porcelain"],"status_failed","Inspect the worktree manually, then retry.")}o(Ae,"statusPorcelain");function U(e){return _(e,["rev-parse","--abbrev-ref","HEAD"],"branch_check_failed","Inspect the worktree manually, then retry.")}o(U,"currentBranch");function X(e,r){return _(e,["rev-parse","--git-path",r],"git_path_failed","Inspect the worktree manually, then retry.")}o(X,"gitPath");function R(e){return k(X(e,"rebase-merge"))||k(X(e,"rebase-apply"))}o(R,"isRebaseInProgress");async function $(e,r,t="pending"){const n=await v(e);await ue(e,{cwd:r.repo_root,cleanup:t,closedAt:new Date().toISOString(),unbindSession:n?.pi_session_id!=null||n?.pi_session_file!=null})}o($,"persistClosedWorktreeState");async function Re(e,r){const t=(await v(e))?.managed_worktree;t==null||t.state!=="open"||await le(e,{at:new Date().toISOString(),blocked:r})}o(Re,"recordBlockedLandIntent");function Me(e,r,t,n){const a=i(["status","--porcelain"],e);if(a.status!==0)return{ok:!1,detail:(a.stderr.trim()||a.stdout.trim()).trim()};const s=i(["diff","--name-only",t,n],e);if(s.status!==0)return{ok:!1,detail:(s.stderr.trim()||s.stdout.trim()).trim()};const u=s.stdout.split(`
16
+ `).map(g=>g.trim()).filter(g=>g!==""),h=ke(be(a.stdout),u);if(h.kind==="refuse")return{ok:!1,detail:`untracked files in ${e} would be overwritten by the incoming commits: ${h.collisions.join(", ")}`};const m=o(()=>{const g=i(["merge","--ff-only",n],e);return g.status===0?{ok:!0}:{ok:!1,detail:(g.stderr.trim()||g.stdout.trim()).trim()}},"merge");if(h.kind==="direct")return m();const d=i(["rev-parse","--verify","--quiet","refs/stash"],e),l=d.status===0?d.stdout.trim():null,b=i(["stash","push","--message",`crtr landing ${r}`],e);if(b.status!==0)return{ok:!1,detail:(b.stderr.trim()||b.stdout.trim()).trim()};const c=i(["rev-parse","--verify","--quiet","refs/stash"],e),p=c.status===0?c.stdout.trim():null;if(p===null||p===l)return m();const y=`local changes from ${e} are retained in stash ${p}; recover them with \`git -C ${w(e)} stash apply ${p}\` and resolve any conflict.`,W=m();return W.ok?i(["stash","apply",p],e).status===0?(z(e,p),{ok:!0}):(i(["reset","--hard",n],e),{ok:!0,stash:`the shared base checkout was reset to landed commit ${n} because local changes conflicted with it. ${y}`}):i(["stash","apply",p],e).status===0?(z(e,p),W):{ok:!1,detail:`${W.detail}
17
+ ${y}`}}o(Me,"landIntoBaseCheckout");function z(e,r){const t=i(["rev-parse","--verify","--quiet","refs/stash"],e);t.status===0&&t.stdout.trim()===r&&i(["stash","drop"],e)}o(z,"dropStashIfStillTip");function M(e){const r=`git -C ${w(e.repo_root)} worktree remove ${w(e.path)}`;return{worktree_removed:!1,worktree_remove_error:`cleanup pending: the caller checkout at ${e.path} remains intact. After its node exits, run \`${r}\` after confirming it is safe to delete.`,branch_deleted:!1,branch_delete_error:`cleanup pending: local branch ${e.branch} remains checked out at ${e.path}; delete it only after that checkout is removed and its landed commit is still contained in ${e.base_ref}.`}}o(M,"deferredCleanupResult");async function Le(e,r,t){if(D(r.repo_root,r.path,t)!==!0)throw new f("wrong_branch",`managed worktree's recorded branch ${r.branch} no longer exists, and its checkout at ${r.path} is not registered to ${t} either`,`Inspect ${r.path} and its git worktree registration, then ask the user with \`crtr human send\` if you are unsure how to proceed.`);const n=_(r.path,["rev-parse","HEAD"],"branch_check_failed","Inspect the worktree manually, then retry.");if(Ee(r.path,r.base_ref,n)===null)throw new f("branch_moved_unlanded",`managed worktree's recorded branch ${r.branch} no longer exists; its checkout is now on ${t} at ${n}, which carries content local base branch '${r.base_ref}' does not yet contain`,`Land or push ${t} through its own workflow (merge its PR, or fast-forward '${r.base_ref}' to include it) before closing, or ask the user with \`crtr human send\` if you are unsure how ${t} should be delivered.`);const s=S(r.repo_root),u=D(r.repo_root,r.path,t)===!0,h=U(r.path)===t,m=_(r.path,["rev-parse","HEAD"],"branch_check_failed","Inspect the worktree manually, then retry.")===n;if(!u||!h||!m)throw new f("wrong_branch",`managed worktree at ${r.path} changed while closing`,`Inspect ${r.path}, then rerun \`crtr node worktree close\`.`);const d={observed_branch:t,observed_sha:n,common_dir:s,authorized:new Date().toISOString()};await $(e,{...r,disposal:d},"pending");const l=`git -C ${w(r.repo_root)} worktree remove ${w(r.path)}`;return{node_id:e,branch:t,worktree_path:r.path,landed_sha:n,worktree_removed:!1,worktree_remove_error:`cleanup pending: the caller checkout at ${r.path} remains intact. After its node exits, run \`${l}\` after confirming it is safe to delete.`,branch_deleted:!1,branch_delete_error:`${t} is not a crtr-managed branch and will never be deleted automatically; it is preserved once the checkout above is removed.`}}o(Le,"closeBranchMovedWorktree");async function Te(e,r){const t=(await v(e))?.managed_worktree;if(t==null)return{message:`node ${e} has no managed worktree`,next:`Nothing to ${r} for this node.`};if(t.state==="abandoned"){const n=t.abandoned?.branch??t.branch;return{message:`node ${e}'s managed worktree was already abandoned`,next:`Nothing to ${r} for this node; its work was retained on branch ${n}.`}}return t.state==="closed"?t.cleanup==="complete"?{message:`node ${e}'s managed worktree is already closed and its cleanup is complete`,next:`Nothing to ${r} for this node.`}:{message:`node ${e}'s managed worktree is already closed; this is not lost ownership \u2014 its checkout at ${t.path} is scheduled for automatic cleanup once that is safe`,next:`Nothing to ${r} for this node. The daemon sweep finishes removing ${t.path} on its own; inspect it directly only if it still needs manual attention.`}:{message:`node ${e} has no open managed worktree`,next:`Nothing to ${r} for this node.`}}o(Te,"noManagedWorktreeMessage");async function Q(e){if(await v(e)===null)throw new f("unknown_node",`unknown node: ${e}`,"Run this from a live node, or pass a real node id once that command supports it.");const t=await x(e);if(t===null){const{message:c,next:p}=await Te(e,"close");throw new f("no_managed_worktree",c,p)}if(!k(t.path)){const c=L(t);if(c===null)throw new f("worktree_missing",`managed worktree path is missing: ${t.path}`,J(t));if(await V(e,t,c)===null)throw new f("worktree_missing",`managed worktree path is missing: ${t.path}`,J(t));return{node_id:e,branch:t.branch,worktree_path:t.path,landed_sha:c.branchSha??t.base_sha,worktree_removed:!0,branch_deleted:!0}}if(R(t.path))throw new f("rebase_in_progress","this managed worktree is mid-rebase",`Resolve conflicts in ${t.path}, run \`git rebase --continue\` there, then rerun \`crtr node worktree close\`.`);if(Ae(t.path)!=="")throw new f("dirty_worktree","managed worktree has uncommitted changes",`Commit the work in ${t.path} (or ask the user with \`crtr human send\` if you do not know whether to keep it), then rerun \`crtr node worktree close\`.`);const a=U(t.path);if(a!==t.branch){const c=a==="HEAD"?"detached HEAD":a,p=$e(t.path,t.branch);if(p||a==="HEAD")throw new f("wrong_branch",`managed worktree is not on its recorded branch (expected ${t.branch}, found ${c})`,p?`Check out ${t.branch} in ${t.path} before closing, or ask the user with \`crtr human send\` if you are unsure why it changed, then rerun \`crtr node worktree close\`.`:`The recorded branch ${t.branch} no longer exists, and ${t.path} is in detached HEAD, so there is no branch to preserve. Check out or create a branch there before closing, or ask the user with \`crtr human send\` if you are unsure how to proceed.`);return await Le(e,t,a)}let s;try{s=A(t.path,t.base_ref)}catch(c){const p=_(t.path,["rev-parse","HEAD"],"landed_sha_failed","Inspect the worktree checkout manually, then rerun `crtr node worktree close`.");if(!(c instanceof f)||c.code!=="missing_base_branch"||p!==t.base_sha)throw c;return await $(e,t,"pending"),{node_id:e,branch:t.branch,worktree_path:t.path,landed_sha:t.base_sha,...M(t)}}const u=i(["rebase",s],t.path);if(u.status!==0){const c=(u.stderr.trim()||u.stdout.trim()).trim();throw new f("rebase_failed",`rebase onto local base branch '${t.base_ref}' did not complete`,`Resolve conflicts in ${t.path}, run \`git rebase --continue\` there, then rerun \`crtr node worktree close\`. If the blocker is not mechanical, ask the user with \`crtr human send\`.`,c)}const h=_(t.path,["rev-parse","HEAD"],"landed_sha_failed","Inspect the worktree checkout manually, then rerun `crtr node worktree close`."),m=o(async(c,p,y)=>(await Re(e,c),new f("land_base_failed",c,p,y)),"blocked"),d=i(["merge-base","--is-ancestor",s,h],t.path);if(d.status!==0)throw await m(`rebased worktree is not a fast-forward of local base branch '${t.base_ref}'`,"Ask the user with `crtr human send` how to land this managed worktree.",(d.stderr.trim()||d.stdout.trim()).trim());const l=ve(t.path,t.base_ref);if(l!==null){const c=i(["rev-parse","HEAD"],l),p=c.stdout.trim();if(c.status!==0||p!==s){const y=c.status!==0?(c.stderr.trim()||p).trim():`expected ${s}, found ${p}`;throw await m(`local base branch '${t.base_ref}' changed while this worktree was closing`,`Reconcile the base checkout at ${l}, then rerun \`crtr node worktree close\`. If you are unsure, ask the user with \`crtr human send\`.`,y)}}let b;if(l!==null){const c=Me(l,t.branch,s,h);if(!c.ok)throw await m(`could not fast-forward local base branch '${t.base_ref}' to this worktree`,`Local base branch '${t.base_ref}' is checked out at ${l} in a state that blocks a fast-forward. Reconcile it there, then rerun \`crtr node worktree close\`. If you are unsure, ask the user with \`crtr human send\`.`,c.detail);b=c.stash}else{const c=i(["update-ref",`refs/heads/${t.base_ref}`,h,s],t.path);if(c.status!==0)throw await m(`could not fast-forward local base branch '${t.base_ref}' to this worktree`,"Ask the user with `crtr human send` how to land this managed worktree.",(c.stderr.trim()||c.stdout.trim()).trim())}return await $(e,t,"pending"),{node_id:e,branch:t.branch,worktree_path:t.path,landed_sha:h,...M(t),...b===void 0?{}:{base_checkout_stash:b}}}o(Q,"closeManagedWorktreeLocked");async function Je(e){const r=await x(e);return r===null?await Q(e):await j(k(r.path)?r.path:r.repo_root,async()=>await Q(e))}o(Je,"closeManagedWorktree");function Pe(e,r,t,n){const a=_e(e.branch,r,t,n),s=i(["update-ref","--stdin"],e.repo_root,a),u=s.status===0;if(u)return{branchDeleted:u};const h=(s.stderr.trim()||s.stdout.trim()).trim();return{branchDeleted:u,branchDeleteError:`local branch ${e.branch} could not be deleted automatically (${h}); run \`git -C ${w(e.repo_root)} branch -D ${w(e.branch)}\` after confirming it is merged.`}}o(Pe,"bestEffortDeleteBranchWithContainmentProof");function L(e){if(e.state!=="open"||k(e.path))return null;const r=`refs/heads/${e.branch}`,t=i(["show-ref","--verify","--quiet",r],e.repo_root);if(t.status===1)return{branchSha:null};if(t.status!==0)return null;const n=i(["rev-parse","--verify",r],e.repo_root);if(n.status!==0)return null;const a=n.stdout.trim(),s=i(["worktree","list","--porcelain"],e.repo_root);if(s.status!==0||I(s.stdout,e.branch)!==null)return null;const u=[],h=`refs/heads/${e.base_ref}`,m=i(["show-ref","--verify","--quiet",h],e.repo_root);if(m.status===0){const l=i(["rev-parse","--verify",h],e.repo_root);if(l.status!==0)return null;u.push({ref:h,sha:l.stdout.trim()})}else if(m.status!==1)return null;const d=i(["rev-parse","--symbolic-full-name","--verify",`${e.base_ref}@{upstream}`],e.repo_root);if(d.status===0){const l=d.stdout.trim(),b=i(["rev-parse","--verify",l],e.repo_root);l.startsWith("refs/")&&b.status===0&&u.push({ref:l,sha:b.stdout.trim()})}else{const l=`refs/remotes/origin/${e.base_ref}`,b=i(["show-ref","--verify","--quiet",l],e.repo_root);if(b.status===0){const c=i(["rev-parse","--verify",l],e.repo_root);c.status===0&&u.push({ref:l,sha:c.stdout.trim()})}else if(b.status!==1)return null}for(const l of u)if(i(["merge-base","--is-ancestor",a,l.sha],e.repo_root).status===0)return{branchSha:a,containingRef:l.ref,containingSha:l.sha};return null}o(L,"proveAbsentPathManagedWorktree");function J(e){return`The recorded checkout path is gone, but crtr cannot prove ${e.branch} is already delivered. Push or land ${e.branch}, then rerun \`crtr push final\`; if you cannot establish what happened to the branch, ask the user with \`crtr human send\`.`}o(J,"missingWorktreeRecoveryHint");function T(e){if(e.state!=="open"||!k(e.path)||R(e.path))return null;const r=i(["rev-parse","--abbrev-ref","HEAD"],e.path);if(r.status!==0||r.stdout.trim()!==e.branch)return null;const t=i(["status","--porcelain"],e.path);if(t.status!==0||t.stdout.trim()!=="")return null;const n=i(["rev-parse","HEAD"],e.path);if(n.status!==0)return null;const a=n.stdout.trim();let s;try{s=A(e.path,e.base_ref)}catch(h){if(h instanceof f&&h.code==="missing_base_branch"&&a===e.base_sha)return{branchSha:a,baseSha:e.base_sha};throw h}return i(["merge-base","--is-ancestor",a,s],e.path).status!==0?null:{branchSha:a,baseSha:s}}o(T,"proveCleanEmptyManagedWorktree");async function V(e,r,t){if(k(r.path))return null;if(t.branchSha===null)return await $(e,r,"complete"),{node_id:e,branch:r.branch,worktree_path:r.path,worktree_removed:!0,branch_deleted:!0};if(t.containingRef===void 0||t.containingSha===void 0)return null;const{branchDeleted:n}=Pe(r,t.branchSha,t.containingRef,t.containingSha);return n?(await $(e,r,"complete"),{node_id:e,branch:r.branch,worktree_path:r.path,worktree_removed:!0,branch_deleted:!0}):null}o(V,"autoDropAbsentPathManagedWorktreeLocked");async function He(e,r,t){const n=T(r);return n===null||n.branchSha!==t.branchSha?null:(await $(e,r,"pending"),{node_id:e,branch:r.branch,worktree_path:r.path,...M(r)})}o(He,"autoDropCleanManagedWorktreeLocked");function Y(e){if(e.state!=="open"||!k(e.path)||R(e.path))return null;const r=i(["status","--porcelain"],e.path);if(r.status!==0||r.stdout.trim()!=="")return null;const t=i(["rev-parse","HEAD"],e.path);return t.status===0?t.stdout.trim():null}o(Y,"cleanCurrentCheckoutTip");async function Ve(e){const r=Y(e);if(r===null)return!1;const t=process.env.GIT_SSH_COMMAND??process.env.GIT_SSH??"ssh",n=await he(["ls-remote","--exit-code","--heads","origin"],e.path,void 0,{timeoutMs:1e4,env:{...process.env,GIT_TERMINAL_PROMPT:"0",GIT_SSH_COMMAND:`${t} -o BatchMode=yes`}});return n.status!==0||!n.stdout.split(`
18
+ `).some(s=>{const[u,h]=s.split(" ",2);return u===r&&h?.startsWith("refs/heads/")===!0})?!1:Y(e)===r}o(Ve,"branchIsPushedAtCurrentTip");async function Ye(e){const r=await x(e);if(r===null)return null;const t=!k(r.path);return(t?L(r)===null:T(r)===null)?null:await j(t?r.repo_root:r.path,async()=>{const n=await x(e);if(n===null)return null;if(!k(n.path)){const s=L(n);return s===null?null:await V(e,n,s)}const a=T(n);return a===null?null:await He(e,n,a)})}o(Ye,"autoDropCleanManagedWorktree");export{q as WORKTREE_LOCK_WAIT_MS,f as WorktreeError,Ye as autoDropCleanManagedWorktree,He as autoDropCleanManagedWorktreeLocked,Ve as branchIsPushedAtCurrentTip,I as checkoutPathForBranchInList,Je as closeManagedWorktree,Qe as createManagedWorktree,Xe as hasOpenManagedWorktree,ye as isManagedWorktreeBase,R as isRebaseInProgress,ge as managedWorktreePath,F as managedWorktreesRoot,J as missingWorktreeRecoveryHint,x as openManagedWorktreeForNode,$ as persistClosedWorktreeState,T as proveCleanEmptyManagedWorktree,ze as rollbackManagedWorktree,xe as samePath,G as worktreeLockPathForCommonDir};
@@ -29,9 +29,9 @@ export interface ComputeInput {
29
29
  agents: AgentInput[];
30
30
  scan: (input: ScanInput) => Promise<ScanResult>;
31
31
  /** Throws when the memory-read log cannot be read. */
32
- memory: (fromIso: string, toIso: string) => {
32
+ memory: (fromIso: string, toIso: string) => Promise<{
33
33
  rows: MemoryReadInput[];
34
34
  startedAt: string | null;
35
- };
35
+ }>;
36
36
  }
37
37
  export declare function computeAnalytics(input: ComputeInput): Promise<AnalyticsDTO>;
@@ -1 +1 @@
1
- var L=Object.defineProperty;var u=(o,_)=>L(o,"name",{value:_,configurable:!0});const oe=36e5,Q={"24h":864e5,"7d":6048e5};function b(o,_){return o[Math.max(0,Math.ceil(_/100*o.length)-1)]}u(b,"rank");function P(o){const _=[...o].sort((g,h)=>g-h);return{count:_.length,p10_ms:b(_,10),median_ms:b(_,50),p90_ms:b(_,90),max_ms:_[_.length-1]}}u(P,"stats");const C=u(o=>o.input+o.output+o.cache_read+o.cache_write,"tokensOf"),X=u(o=>o.input+o.cache_read+o.cache_write,"contextOf"),y=u(o=>`${o.provider}\0${o.model}`,"modelKey");function v(o,_,g){const h=o.get(_);h?h.push(g):o.set(_,[g])}u(v,"push");const W=u(o=>o instanceof Error?o.message:String(o),"reason");function Y(o){const _=new Map(o.map(i=>[i.node_id,i])),g=new Map,h=u((i,w)=>{const p=g.get(i.node_id);if(p)return p;const l=i.parent===null?void 0:_.get(i.parent);let f;const r={a:i,createdMs:Date.parse(i.created),finMs:i.finalized_at===null?null:Date.parse(i.finalized_at)};if(!l||w.has(l.node_id)){if(l)throw new Error(`parent cycle through node ${i.node_id}`);f={...r,graph:i.node_id,parent:null,depth:0}}else{w.add(i.node_id);const t=h(l,w);f={...r,graph:t.graph,parent:l.node_id,depth:t.depth+1}}return g.set(i.node_id,f),f},"place");for(const i of o)h(i,new Set);return g}u(Y,"placeAgents");async function ae(o){const _=o.asOfMs,g=_-Q[o.window],h=u(e=>e!==null&&e>=g&&e<_,"inW"),i=Y(o.agents),w=u(e=>i.get(e.graph),"rootOf"),p=u(e=>w(e).a.cwd,"projectOf"),l=new Map;for(const e of i.values()){const a=p(e);v(l,a,e)}let f=null,r="";try{const e=[...l.keys()],a=await Promise.all(e.map(m=>o.scan({nodes:l.get(m).map(S=>({node_id:S.a.node_id,created:S.a.created,transcripts:S.a.transcripts})),windowStartMs:g,asOfMs:_})));f=new Map(e.map((m,S)=>[m,a[S]]))}catch(e){r=`transcript scan failed: ${W(e)}`}const t=new Map;for(const e of f?.values()??[])for(const a of e.responses)v(t,a.node_id,a);const c=u(e=>(t.get(e)??[]).reduce((a,m)=>a+m.cost,0),"priceOf"),s=u(e=>(t.get(e)??[]).reduce((a,m)=>a+C(m),0),"tokensOfAgent"),n=new Map;for(const e of f?.values()??[]){for(const a of e.responses)v(n,a.node_id,a.at_ms);for(const a of e.unknown_at)v(n,a.node_id,a.at_ms)}const d=u(e=>(n.get(e)?.length??0)>0,"isActive"),k=new Map,A=new Set;for(const e of i.values()){if(!d(e.a.node_id))continue;const a=p(e);A.add(a),k.set(a,(k.get(a)??0)+c(e.a.node_id))}const T=[...A].map(e=>({cwd:e,api_price_estimate:k.get(e)??0})).sort((e,a)=>a.api_price_estimate-e.api_price_estimate||e.cwd.localeCompare(a.cwd)),M=[...i.values()].filter(e=>o.project===null||p(e)===o.project),F=new Set(M.map(e=>e.a.node_id)),j=u(e=>e.parent!==null&&h(e.createdMs),"spawned"),z=u(e=>h(e.finMs),"finished"),D=new Set,I=new Set,R=u((e,a)=>{for(let m=e;m&&!a.has(m.a.node_id);m=m.parent===null?void 0:i.get(m.parent))a.add(m.a.node_id)},"climb");for(const e of M)d(e.a.node_id)&&R(e,D),(d(e.a.node_id)||j(e)||z(e))&&R(e,I);const N=M.filter(e=>I.has(e.a.node_id)).map(e=>({node_id:e.a.node_id,name:e.a.name,kind:e.a.kind,graph_id:e.graph,in_graph_detail:D.has(e.a.node_id),parent:e.parent,depth:e.depth,created:e.a.created,finalized_at:e.a.finalized_at,lifecycle:e.a.lifecycle,active:d(e.a.node_id),spawned_in_window:j(e),finished_in_window:z(e),agent_time_ms:e.finMs===null?null:e.finMs-e.createdMs,api_price_estimate:c(e.a.node_id),tokens:s(e.a.node_id),responses:n.get(e.a.node_id)?.length??0,response_times_ms:[...n.get(e.a.node_id)??[]].sort((a,m)=>a-m),final_report_ref:e.a.final_report?`${e.a.node_id}:reports/${e.a.final_report}`:null})).sort((e,a)=>e.created.localeCompare(a.created)||e.node_id.localeCompare(a.node_id)),V=M.reduce((e,a)=>e===null||a.a.created<e?a.a.created:e,null),q=M.flatMap(e=>(n.get(e.a.node_id)??[]).map(a=>({node_id:e.a.node_id,at_ms:a}))),x=M.filter(e=>d(e.a.node_id)).length;let H=null,U=null;if(f){const e=[...f.entries()].filter(([a])=>o.project===null||a===o.project).map(([,a])=>a);H={total:e.reduce((a,m)=>a+m.transcripts.total,0),read:e.reduce((a,m)=>a+m.transcripts.read,0),unreadable:e.flatMap(a=>a.transcripts.unreadable)},U=e.reduce((a,m)=>a+m.unknown,0)}const O=u(()=>({ok:!1,reason:r}),"fail"),B=f?{ok:!0,value:Z(M,i,d,c,s,j)}:O(),G=f?{ok:!0,value:ee(M,z,q,o.window,_)}:O(),J=f?{ok:!0,value:te(M,t)}:O();let $,K=null;try{const e=o.memory(new Date(g).toISOString(),new Date(_).toISOString());K=e.startedAt;const a=e.rows.filter(m=>F.has(m.node_id));$=f?{ok:!0,value:{read:E(a.filter(m=>m.type==="read"),i,x,d),loaded:E(a.filter(m=>m.type==="loaded"),i,x,d)}}:O()}catch(e){$={ok:!1,reason:`memory-read log unavailable: ${W(e)}`}}return{as_of:new Date(_).toISOString(),window_start:new Date(g).toISOString(),window:o.window,project:o.project,projects:T,transcripts:H,unknown_responses:U,oldest_agent_created:V,memory_log_started_at:K,agents:N,graphs:B,time:G,usage:J,memory:$}}u(ae,"computeAnalytics");function Z(o,_,g,h,i,w){const p=new Map,l=new Map;for(const n of o){if(w(n)&&l.set(n.graph,(l.get(n.graph)??new Set).add(n.a.kind)),!g(n.a.node_id))continue;let d=p.get(n.graph);if(!d){const k=_.get(n.graph).a;d={graph_id:n.graph,name:k.title||k.name,root_deleted:k.parent!==null,cwd:k.cwd,root_kind:k.kind,root_lifecycle:k.lifecycle,root_created:k.created,active_agents:0,depth:0,api_price_estimate:0,tokens:0,spawned_kinds:[]},p.set(n.graph,d)}d.active_agents++,d.depth=Math.max(d.depth,n.depth),d.api_price_estimate+=h(n.a.node_id),d.tokens+=i(n.a.node_id)}for(const n of p.values())n.spawned_kinds=[...l.get(n.graph_id)??[]].sort();const f=[...p.values()].sort((n,d)=>d.api_price_estimate-n.api_price_estimate||n.graph_id.localeCompare(d.graph_id)),r=u(n=>n<=1?0:n<=5?1:n<=10?2:n<=20?3:4,"bucket"),t=[0,0,0,0,0];for(const n of f)t[r(n.active_agents)]++;const c=new Map;for(const n of o)w(n)&&c.set(n.a.kind,(c.get(n.a.kind)??0)+1);const s=["1","2\u20135","6\u201310","11\u201320","21+"];return{count:f.length,spawned:[...c.values()].reduce((n,d)=>n+d,0),deepest:f.reduce((n,d)=>Math.max(n,d.depth),0),graphs:f,size_buckets:s.map((n,d)=>({label:n,graphs:t[d]})),spawned_by_kind:[...c].map(([n,d])=>({kind:n,agents:d})).sort((n,d)=>d.agents-n.agents||n.kind.localeCompare(d.kind))}}u(Z,"graphsMeasure");function ee(o,_,g,h,i){const w=o.filter(_),p=new Map;for(const s of w)v(p,s.a.kind,s.finMs-s.createdMs);const l=h==="24h"?36e5:24*36e5,f=h==="24h"?24:7,r=i-f*l,t=Array.from({length:f},()=>new Set);for(const s of g)t[Math.floor((s.at_ms-r)/l)].add(s.node_id);const c=t.map((s,n)=>({start:new Date(r+n*l).toISOString(),agents:s.size}));return{all:w.length===0?null:P(w.map(s=>s.finMs-s.createdMs)),by_kind:[...p].map(([s,n])=>({kind:s,...P(n)})).sort((s,n)=>n.count-s.count||s.kind.localeCompare(n.kind)),activity:{bucket_ms:l,buckets:c,max:c.reduce((s,n)=>Math.max(s,n.agents),0)}}}u(ee,"timeMeasure");function te(o,_){const g=new Map,h=new Map,i=[];let w=0,p=0;for(const r of o)for(const t of _.get(r.a.node_id)??[]){const c=y(t);let s=g.get(c);s||(s={row:{provider:t.provider,model:t.model,responses:0,api_price_estimate:0,tokens:0,tokens_by_type:{cache_read:0,cache_write:0,input:0,output:0},cache_read_share:0,context:{median:0,p90:0,max:0,buckets:[0,0,0,0,0]}},contexts:[]},g.set(c,s));const n=X(t);s.row.responses++,s.row.api_price_estimate+=t.cost,s.row.tokens+=C(t),s.row.tokens_by_type.cache_read+=t.cache_read,s.row.tokens_by_type.cache_write+=t.cache_write,s.row.tokens_by_type.input+=t.input,s.row.tokens_by_type.output+=t.output,s.row.context.buckets[Math.min(4,Math.floor(n/5e4))]++,s.contexts.push(n),i.push(n),w+=t.cost,p+=C(t);const d=h.get(r.a.kind)??{price:0,tokens:0,byModel:new Map};d.price+=t.cost,d.tokens+=C(t),d.byModel.set(c,(d.byModel.get(c)??0)+1),h.set(r.a.kind,d)}const l=[...g.values()].map(({row:r,contexts:t})=>{const c=t.sort((n,d)=>n-d),s=r.tokens_by_type.input+r.tokens_by_type.cache_read+r.tokens_by_type.cache_write;return r.cache_read_share=s===0?0:r.tokens_by_type.cache_read/s,r.context.median=b(c,50),r.context.p90=b(c,90),r.context.max=c[c.length-1],r}),f=u(r=>{const[t,c]=r.split("\0");return{provider:t,model:c}},"keyOf");return i.sort((r,t)=>r-t),{api_price_estimate:w,tokens:p,median_context:i.length===0?null:b(i,50),models:l.sort((r,t)=>t.api_price_estimate-r.api_price_estimate||y(r).localeCompare(y(t))),legend:[...l].sort((r,t)=>t.responses-r.responses||t.api_price_estimate-r.api_price_estimate||y(r).localeCompare(y(t))).slice(0,4).map(r=>({provider:r.provider,model:r.model})),kinds:[...h].map(([r,t])=>({kind:r,api_price_estimate:t.price,tokens:t.tokens,responses_by_model:[...t.byModel].map(([c,s])=>({...f(c),responses:s})).sort((c,s)=>s.responses-c.responses||y(c).localeCompare(y(s)))})).sort((r,t)=>t.api_price_estimate-r.api_price_estimate||r.kind.localeCompare(t.kind))}}u(te,"usageMeasure");function E(o,_,g,h){const i=new Map;for(const p of o){const l=`${p.name}\0${p.scope}`;v(i,l,p)}const w=[...i.values()].map(p=>{const l=new Map;for(const t of p){const c=l.get(t.node_id);c?(c.reads++,t.at<c.first_at&&(c.first_at=t.at)):l.set(t.node_id,{reads:1,first_at:t.at})}const f=[...l].map(([t,c])=>{const s=_.get(t);return{node_id:t,kind:s.a.kind,name:s.a.name,graph_id:s.graph,reads:c.reads,first_at:c.first_at}}).sort((t,c)=>c.reads-t.reads||t.first_at.localeCompare(c.first_at)||t.node_id.localeCompare(c.node_id)),r=new Map;for(const t of f)r.set(t.kind,(r.get(t.kind)??0)+1);return{name:p[0].name,scope:p[0].scope,reads:p.length,agents:f.length,graphs:new Set(f.map(t=>t.graph_id)).size,readers_by_kind:[...r].map(([t,c])=>({kind:t,agents:c})).sort((t,c)=>c.agents-t.agents||t.kind.localeCompare(c.kind)),readers:f}}).sort((p,l)=>l.reads-p.reads||l.agents-p.agents||p.name.localeCompare(l.name)||p.scope.localeCompare(l.scope));return{entries:o.length,documents:w.length,agents_with_entry:new Set(o.filter(p=>h(p.node_id)).map(p=>p.node_id)).size,active_agents:g,rows:w}}u(E,"memoryView");export{Q as WINDOW_MS,ae as computeAnalytics};
1
+ var L=Object.defineProperty;var u=(o,_)=>L(o,"name",{value:_,configurable:!0});const oe=36e5,Q={"24h":864e5,"7d":6048e5};function b(o,_){return o[Math.max(0,Math.ceil(_/100*o.length)-1)]}u(b,"rank");function P(o){const _=[...o].sort((g,h)=>g-h);return{count:_.length,p10_ms:b(_,10),median_ms:b(_,50),p90_ms:b(_,90),max_ms:_[_.length-1]}}u(P,"stats");const C=u(o=>o.input+o.output+o.cache_read+o.cache_write,"tokensOf"),X=u(o=>o.input+o.cache_read+o.cache_write,"contextOf"),y=u(o=>`${o.provider}\0${o.model}`,"modelKey");function v(o,_,g){const h=o.get(_);h?h.push(g):o.set(_,[g])}u(v,"push");const W=u(o=>o instanceof Error?o.message:String(o),"reason");function Y(o){const _=new Map(o.map(i=>[i.node_id,i])),g=new Map,h=u((i,w)=>{const p=g.get(i.node_id);if(p)return p;const l=i.parent===null?void 0:_.get(i.parent);let f;const r={a:i,createdMs:Date.parse(i.created),finMs:i.finalized_at===null?null:Date.parse(i.finalized_at)};if(!l||w.has(l.node_id)){if(l)throw new Error(`parent cycle through node ${i.node_id}`);f={...r,graph:i.node_id,parent:null,depth:0}}else{w.add(i.node_id);const t=h(l,w);f={...r,graph:t.graph,parent:l.node_id,depth:t.depth+1}}return g.set(i.node_id,f),f},"place");for(const i of o)h(i,new Set);return g}u(Y,"placeAgents");async function ae(o){const _=o.asOfMs,g=_-Q[o.window],h=u(e=>e!==null&&e>=g&&e<_,"inW"),i=Y(o.agents),w=u(e=>i.get(e.graph),"rootOf"),p=u(e=>w(e).a.cwd,"projectOf"),l=new Map;for(const e of i.values()){const a=p(e);v(l,a,e)}let f=null,r="";try{const e=[...l.keys()],a=await Promise.all(e.map(m=>o.scan({nodes:l.get(m).map(S=>({node_id:S.a.node_id,created:S.a.created,transcripts:S.a.transcripts})),windowStartMs:g,asOfMs:_})));f=new Map(e.map((m,S)=>[m,a[S]]))}catch(e){r=`transcript scan failed: ${W(e)}`}const t=new Map;for(const e of f?.values()??[])for(const a of e.responses)v(t,a.node_id,a);const c=u(e=>(t.get(e)??[]).reduce((a,m)=>a+m.cost,0),"priceOf"),s=u(e=>(t.get(e)??[]).reduce((a,m)=>a+C(m),0),"tokensOfAgent"),n=new Map;for(const e of f?.values()??[]){for(const a of e.responses)v(n,a.node_id,a.at_ms);for(const a of e.unknown_at)v(n,a.node_id,a.at_ms)}const d=u(e=>(n.get(e)?.length??0)>0,"isActive"),k=new Map,A=new Set;for(const e of i.values()){if(!d(e.a.node_id))continue;const a=p(e);A.add(a),k.set(a,(k.get(a)??0)+c(e.a.node_id))}const T=[...A].map(e=>({cwd:e,api_price_estimate:k.get(e)??0})).sort((e,a)=>a.api_price_estimate-e.api_price_estimate||e.cwd.localeCompare(a.cwd)),M=[...i.values()].filter(e=>o.project===null||p(e)===o.project),F=new Set(M.map(e=>e.a.node_id)),j=u(e=>e.parent!==null&&h(e.createdMs),"spawned"),z=u(e=>h(e.finMs),"finished"),D=new Set,I=new Set,R=u((e,a)=>{for(let m=e;m&&!a.has(m.a.node_id);m=m.parent===null?void 0:i.get(m.parent))a.add(m.a.node_id)},"climb");for(const e of M)d(e.a.node_id)&&R(e,D),(d(e.a.node_id)||j(e)||z(e))&&R(e,I);const N=M.filter(e=>I.has(e.a.node_id)).map(e=>({node_id:e.a.node_id,name:e.a.name,kind:e.a.kind,graph_id:e.graph,in_graph_detail:D.has(e.a.node_id),parent:e.parent,depth:e.depth,created:e.a.created,finalized_at:e.a.finalized_at,lifecycle:e.a.lifecycle,active:d(e.a.node_id),spawned_in_window:j(e),finished_in_window:z(e),agent_time_ms:e.finMs===null?null:e.finMs-e.createdMs,api_price_estimate:c(e.a.node_id),tokens:s(e.a.node_id),responses:n.get(e.a.node_id)?.length??0,response_times_ms:[...n.get(e.a.node_id)??[]].sort((a,m)=>a-m),final_report_ref:e.a.final_report?`${e.a.node_id}:reports/${e.a.final_report}`:null})).sort((e,a)=>e.created.localeCompare(a.created)||e.node_id.localeCompare(a.node_id)),V=M.reduce((e,a)=>e===null||a.a.created<e?a.a.created:e,null),q=M.flatMap(e=>(n.get(e.a.node_id)??[]).map(a=>({node_id:e.a.node_id,at_ms:a}))),x=M.filter(e=>d(e.a.node_id)).length;let H=null,U=null;if(f){const e=[...f.entries()].filter(([a])=>o.project===null||a===o.project).map(([,a])=>a);H={total:e.reduce((a,m)=>a+m.transcripts.total,0),read:e.reduce((a,m)=>a+m.transcripts.read,0),unreadable:e.flatMap(a=>a.transcripts.unreadable)},U=e.reduce((a,m)=>a+m.unknown,0)}const O=u(()=>({ok:!1,reason:r}),"fail"),B=f?{ok:!0,value:Z(M,i,d,c,s,j)}:O(),G=f?{ok:!0,value:ee(M,z,q,o.window,_)}:O(),J=f?{ok:!0,value:te(M,t)}:O();let $,K=null;try{const e=await o.memory(new Date(g).toISOString(),new Date(_).toISOString());K=e.startedAt;const a=e.rows.filter(m=>F.has(m.node_id));$=f?{ok:!0,value:{read:E(a.filter(m=>m.type==="read"),i,x,d),loaded:E(a.filter(m=>m.type==="loaded"),i,x,d)}}:O()}catch(e){$={ok:!1,reason:`memory-read log unavailable: ${W(e)}`}}return{as_of:new Date(_).toISOString(),window_start:new Date(g).toISOString(),window:o.window,project:o.project,projects:T,transcripts:H,unknown_responses:U,oldest_agent_created:V,memory_log_started_at:K,agents:N,graphs:B,time:G,usage:J,memory:$}}u(ae,"computeAnalytics");function Z(o,_,g,h,i,w){const p=new Map,l=new Map;for(const n of o){if(w(n)&&l.set(n.graph,(l.get(n.graph)??new Set).add(n.a.kind)),!g(n.a.node_id))continue;let d=p.get(n.graph);if(!d){const k=_.get(n.graph).a;d={graph_id:n.graph,name:k.title||k.name,root_deleted:k.parent!==null,cwd:k.cwd,root_kind:k.kind,root_lifecycle:k.lifecycle,root_created:k.created,active_agents:0,depth:0,api_price_estimate:0,tokens:0,spawned_kinds:[]},p.set(n.graph,d)}d.active_agents++,d.depth=Math.max(d.depth,n.depth),d.api_price_estimate+=h(n.a.node_id),d.tokens+=i(n.a.node_id)}for(const n of p.values())n.spawned_kinds=[...l.get(n.graph_id)??[]].sort();const f=[...p.values()].sort((n,d)=>d.api_price_estimate-n.api_price_estimate||n.graph_id.localeCompare(d.graph_id)),r=u(n=>n<=1?0:n<=5?1:n<=10?2:n<=20?3:4,"bucket"),t=[0,0,0,0,0];for(const n of f)t[r(n.active_agents)]++;const c=new Map;for(const n of o)w(n)&&c.set(n.a.kind,(c.get(n.a.kind)??0)+1);const s=["1","2\u20135","6\u201310","11\u201320","21+"];return{count:f.length,spawned:[...c.values()].reduce((n,d)=>n+d,0),deepest:f.reduce((n,d)=>Math.max(n,d.depth),0),graphs:f,size_buckets:s.map((n,d)=>({label:n,graphs:t[d]})),spawned_by_kind:[...c].map(([n,d])=>({kind:n,agents:d})).sort((n,d)=>d.agents-n.agents||n.kind.localeCompare(d.kind))}}u(Z,"graphsMeasure");function ee(o,_,g,h,i){const w=o.filter(_),p=new Map;for(const s of w)v(p,s.a.kind,s.finMs-s.createdMs);const l=h==="24h"?36e5:24*36e5,f=h==="24h"?24:7,r=i-f*l,t=Array.from({length:f},()=>new Set);for(const s of g)t[Math.floor((s.at_ms-r)/l)].add(s.node_id);const c=t.map((s,n)=>({start:new Date(r+n*l).toISOString(),agents:s.size}));return{all:w.length===0?null:P(w.map(s=>s.finMs-s.createdMs)),by_kind:[...p].map(([s,n])=>({kind:s,...P(n)})).sort((s,n)=>n.count-s.count||s.kind.localeCompare(n.kind)),activity:{bucket_ms:l,buckets:c,max:c.reduce((s,n)=>Math.max(s,n.agents),0)}}}u(ee,"timeMeasure");function te(o,_){const g=new Map,h=new Map,i=[];let w=0,p=0;for(const r of o)for(const t of _.get(r.a.node_id)??[]){const c=y(t);let s=g.get(c);s||(s={row:{provider:t.provider,model:t.model,responses:0,api_price_estimate:0,tokens:0,tokens_by_type:{cache_read:0,cache_write:0,input:0,output:0},cache_read_share:0,context:{median:0,p90:0,max:0,buckets:[0,0,0,0,0]}},contexts:[]},g.set(c,s));const n=X(t);s.row.responses++,s.row.api_price_estimate+=t.cost,s.row.tokens+=C(t),s.row.tokens_by_type.cache_read+=t.cache_read,s.row.tokens_by_type.cache_write+=t.cache_write,s.row.tokens_by_type.input+=t.input,s.row.tokens_by_type.output+=t.output,s.row.context.buckets[Math.min(4,Math.floor(n/5e4))]++,s.contexts.push(n),i.push(n),w+=t.cost,p+=C(t);const d=h.get(r.a.kind)??{price:0,tokens:0,byModel:new Map};d.price+=t.cost,d.tokens+=C(t),d.byModel.set(c,(d.byModel.get(c)??0)+1),h.set(r.a.kind,d)}const l=[...g.values()].map(({row:r,contexts:t})=>{const c=t.sort((n,d)=>n-d),s=r.tokens_by_type.input+r.tokens_by_type.cache_read+r.tokens_by_type.cache_write;return r.cache_read_share=s===0?0:r.tokens_by_type.cache_read/s,r.context.median=b(c,50),r.context.p90=b(c,90),r.context.max=c[c.length-1],r}),f=u(r=>{const[t,c]=r.split("\0");return{provider:t,model:c}},"keyOf");return i.sort((r,t)=>r-t),{api_price_estimate:w,tokens:p,median_context:i.length===0?null:b(i,50),models:l.sort((r,t)=>t.api_price_estimate-r.api_price_estimate||y(r).localeCompare(y(t))),legend:[...l].sort((r,t)=>t.responses-r.responses||t.api_price_estimate-r.api_price_estimate||y(r).localeCompare(y(t))).slice(0,4).map(r=>({provider:r.provider,model:r.model})),kinds:[...h].map(([r,t])=>({kind:r,api_price_estimate:t.price,tokens:t.tokens,responses_by_model:[...t.byModel].map(([c,s])=>({...f(c),responses:s})).sort((c,s)=>s.responses-c.responses||y(c).localeCompare(y(s)))})).sort((r,t)=>t.api_price_estimate-r.api_price_estimate||r.kind.localeCompare(t.kind))}}u(te,"usageMeasure");function E(o,_,g,h){const i=new Map;for(const p of o){const l=`${p.name}\0${p.scope}`;v(i,l,p)}const w=[...i.values()].map(p=>{const l=new Map;for(const t of p){const c=l.get(t.node_id);c?(c.reads++,t.at<c.first_at&&(c.first_at=t.at)):l.set(t.node_id,{reads:1,first_at:t.at})}const f=[...l].map(([t,c])=>{const s=_.get(t);return{node_id:t,kind:s.a.kind,name:s.a.name,graph_id:s.graph,reads:c.reads,first_at:c.first_at}}).sort((t,c)=>c.reads-t.reads||t.first_at.localeCompare(c.first_at)||t.node_id.localeCompare(c.node_id)),r=new Map;for(const t of f)r.set(t.kind,(r.get(t.kind)??0)+1);return{name:p[0].name,scope:p[0].scope,reads:p.length,agents:f.length,graphs:new Set(f.map(t=>t.graph_id)).size,readers_by_kind:[...r].map(([t,c])=>({kind:t,agents:c})).sort((t,c)=>c.agents-t.agents||t.kind.localeCompare(c.kind)),readers:f}}).sort((p,l)=>l.reads-p.reads||l.agents-p.agents||p.name.localeCompare(l.name)||p.scope.localeCompare(l.scope));return{entries:o.length,documents:w.length,agents_with_entry:new Set(o.filter(p=>h(p.node_id)).map(p=>p.node_id)).size,active_agents:g,rows:w}}u(E,"memoryView");export{Q as WINDOW_MS,ae as computeAnalytics};