@north-light/crouter 0.3.349 → 0.3.351

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 (629) hide show
  1. package/bin/crtr-launcher +4 -0
  2. package/bin/runtime-generation-manifest.mjs +12 -2
  3. package/dist/api/client.d.ts +50 -7
  4. package/dist/api/client.js +2 -2
  5. package/dist/api/dto/analytics.d.ts +184 -0
  6. package/dist/api/dto/analytics.js +0 -0
  7. package/dist/api/dto/broker-ops.d.ts +43 -1
  8. package/dist/api/dto/config.d.ts +11 -0
  9. package/dist/api/dto/grants.d.ts +14 -0
  10. package/dist/api/dto/grants.js +0 -0
  11. package/dist/api/dto/human-requests.d.ts +14 -0
  12. package/dist/api/dto/memory.d.ts +6 -0
  13. package/dist/api/dto/modelauth.d.ts +35 -0
  14. package/dist/api/dto/node-records.d.ts +35 -0
  15. package/dist/api/dto/node-records.js +0 -0
  16. package/dist/api/dto/nodes.d.ts +5 -0
  17. package/dist/api/dto/profiles.d.ts +38 -3
  18. package/dist/api/dto/worktree.d.ts +11 -0
  19. package/dist/api/errors.d.ts +23 -1
  20. package/dist/api/errors.js +1 -1
  21. package/dist/api/index.d.ts +2 -0
  22. package/dist/api/index.js +1 -1
  23. package/dist/api/node-transport.js +1 -1
  24. package/dist/api/routes.d.ts +22 -0
  25. package/dist/api/routes.js +1 -1
  26. package/dist/build-root.d.ts +6 -1
  27. package/dist/build-root.js +1 -1
  28. package/dist/builtin-memory/crouter-sdk/client.md +34 -0
  29. package/dist/builtin-memory/crouter-sdk/errors.md +2 -0
  30. package/dist/builtin-memory/crouter-sdk/memory.md +2 -2
  31. package/dist/builtin-memory/crouter-sdk/streaming.md +21 -0
  32. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/claude-plugin-commands.ts +8 -4
  33. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +39 -118
  34. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +6 -1
  35. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +41 -27
  36. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +52 -29
  37. package/dist/cli.js +9 -8
  38. package/dist/clients/attach/chrome/inbox-strip.js +1 -1
  39. package/dist/clients/attach/chrome/roster.js +1 -1
  40. package/dist/clients/attach/config.js +1 -1
  41. package/dist/clients/attach/overlays/auth.js +2 -2
  42. package/dist/clients/attach/overlays/graph.js +1 -1
  43. package/dist/clients/attach/render/tool-calls.js +3 -3
  44. package/dist/clients/attach/session/connection.js +1 -1
  45. package/dist/clients/attach/session/profile-files.js +1 -1
  46. package/dist/clients/attach/viewer.js +1569 -816
  47. package/dist/commands/api-client.js +3 -3
  48. package/dist/commands/broker-permissions.d.ts +2 -0
  49. package/dist/commands/broker-permissions.js +1 -0
  50. package/dist/commands/canvas-analytics/chart.d.ts +52 -0
  51. package/dist/commands/canvas-analytics/chart.js +1 -0
  52. package/dist/commands/canvas-analytics/drilldown.d.ts +34 -0
  53. package/dist/commands/canvas-analytics/drilldown.js +1 -0
  54. package/dist/commands/canvas-analytics/format.d.ts +29 -0
  55. package/dist/commands/canvas-analytics/format.js +1 -0
  56. package/dist/commands/canvas-analytics/graph-detail.d.ts +2 -0
  57. package/dist/commands/canvas-analytics/graph-detail.js +2 -0
  58. package/dist/commands/canvas-analytics/graphs.d.ts +9 -0
  59. package/dist/commands/canvas-analytics/graphs.js +1 -0
  60. package/dist/commands/canvas-analytics/memory.d.ts +2 -0
  61. package/dist/commands/canvas-analytics/memory.js +1 -0
  62. package/dist/commands/canvas-analytics/overview.d.ts +2 -0
  63. package/dist/commands/canvas-analytics/overview.js +1 -0
  64. package/dist/commands/canvas-analytics/shell.d.ts +60 -0
  65. package/dist/commands/canvas-analytics/shell.js +2 -0
  66. package/dist/commands/canvas-analytics/time.d.ts +2 -0
  67. package/dist/commands/canvas-analytics/time.js +1 -0
  68. package/dist/commands/canvas-analytics/usage.d.ts +2 -0
  69. package/dist/commands/canvas-analytics/usage.js +1 -0
  70. package/dist/commands/canvas-analytics/view.d.ts +85 -0
  71. package/dist/commands/canvas-analytics/view.js +1 -0
  72. package/dist/commands/canvas-analytics.d.ts +2 -0
  73. package/dist/commands/canvas-analytics.js +1 -0
  74. package/dist/commands/canvas-prune.js +1 -1
  75. package/dist/commands/canvas.js +1 -1
  76. package/dist/commands/cron.js +5 -5
  77. package/dist/commands/human/components.d.ts +2 -0
  78. package/dist/commands/human/components.js +2 -2
  79. package/dist/commands/human/prompts.js +1 -1
  80. package/dist/commands/human/queue.js +1 -1
  81. package/dist/commands/memory/client.d.ts +11 -0
  82. package/dist/commands/memory/client.js +1 -0
  83. package/dist/commands/memory/delete.js +1 -1
  84. package/dist/commands/memory/edit.js +3 -3
  85. package/dist/commands/memory/find.js +1 -1
  86. package/dist/commands/memory/history.js +4 -4
  87. package/dist/commands/memory/lint.js +1 -1
  88. package/dist/commands/memory/list.d.ts +0 -2
  89. package/dist/commands/memory/list.js +1 -1
  90. package/dist/commands/memory/move.js +1 -1
  91. package/dist/commands/memory/origin.js +1 -1
  92. package/dist/commands/memory/read.d.ts +0 -5
  93. package/dist/commands/memory/read.js +3 -3
  94. package/dist/commands/memory/shared.d.ts +0 -178
  95. package/dist/commands/memory/shared.js +1 -12
  96. package/dist/commands/memory/write.js +5 -5
  97. package/dist/commands/node/bash.js +5 -5
  98. package/dist/commands/node/create.d.ts +0 -17
  99. package/dist/commands/node/create.js +2 -3
  100. package/dist/commands/node/inspect.js +6 -6
  101. package/dist/commands/node/lifecycle.js +2 -2
  102. package/dist/commands/node-context.js +2 -2
  103. package/dist/commands/node-inspect-artifacts.js +6 -6
  104. package/dist/commands/pkg/browse/catalog.js +1 -1
  105. package/dist/commands/pkg/browse/model.d.ts +1 -1
  106. package/dist/commands/pkg/market-manage.js +1 -1
  107. package/dist/commands/pkg/plugin-manage.js +10 -10
  108. package/dist/commands/profile/daemon-profiles.d.ts +13 -0
  109. package/dist/commands/profile/daemon-profiles.js +4 -0
  110. package/dist/commands/profile/delete.js +1 -1
  111. package/dist/commands/profile/env.d.ts +6 -0
  112. package/dist/commands/profile/env.js +2 -2
  113. package/dist/commands/profile/kind.js +1 -2
  114. package/dist/commands/profile/list.js +1 -1
  115. package/dist/commands/profile/meta.js +1 -1
  116. package/dist/commands/profile/new.js +1 -1
  117. package/dist/commands/profile/pause.js +1 -1
  118. package/dist/commands/profile/project.js +1 -1
  119. package/dist/commands/profile/rename.js +1 -1
  120. package/dist/commands/profile/show.js +1 -1
  121. package/dist/commands/profile.js +1 -1
  122. package/dist/commands/push.js +3 -3
  123. package/dist/commands/surface-inspect.js +1 -1
  124. package/dist/commands/sys/branch.js +1 -1
  125. package/dist/commands/sys/config.js +1 -1
  126. package/dist/commands/sys/connect.js +3 -3
  127. package/dist/commands/sys/context/admin/actions.js +1 -1
  128. package/dist/commands/sys/context/admin/model.d.ts +1 -0
  129. package/dist/commands/sys/context/admin/model.js +1 -1
  130. package/dist/commands/sys/context/admin.js +1 -1
  131. package/dist/commands/sys/context/expose.js +2 -1
  132. package/dist/commands/sys/context/prompt-review.js +2 -2
  133. package/dist/commands/sys/context/resolve.js +7 -7
  134. package/dist/commands/sys/context/sysprompt.js +1 -1
  135. package/dist/commands/sys/join.d.ts +1 -0
  136. package/dist/commands/sys/join.js +1 -0
  137. package/dist/commands/sys/migrate.js +1 -1
  138. package/dist/commands/sys/panels/models-panel.js +1 -1
  139. package/dist/commands/sys/panels/profiles-panel.d.ts +0 -1
  140. package/dist/commands/sys/panels/profiles-panel.js +1 -1
  141. package/dist/commands/sys/provider-login.js +6 -6
  142. package/dist/commands/sys/setup-core.d.ts +1 -1
  143. package/dist/commands/sys/setup-core.js +6 -6
  144. package/dist/commands/sys/setup-settings-runner.js +1 -1
  145. package/dist/commands/sys/setup.js +1 -1
  146. package/dist/commands/sys/spaces.d.ts +1 -0
  147. package/dist/commands/sys/spaces.js +2 -0
  148. package/dist/commands/sys/sync-deps.js +2 -2
  149. package/dist/commands/sys/sync-project-guidance.js +4 -4
  150. package/dist/commands/sys/sync-skills.js +2 -2
  151. package/dist/commands/sys/worktrees.d.ts +1 -0
  152. package/dist/commands/sys/worktrees.js +1 -1
  153. package/dist/commands/sys.js +1 -1
  154. package/dist/core/auth-file.js +2 -2
  155. package/dist/core/bash-job-supervisor.d.ts +6 -0
  156. package/dist/core/bash-job-supervisor.js +4 -0
  157. package/dist/core/bash-jobs.d.ts +13 -6
  158. package/dist/core/bash-jobs.js +8 -7
  159. package/dist/core/broker-client/transport-relay.d.ts +6 -3
  160. package/dist/core/broker-client/transport-relay.js +1 -1
  161. package/dist/core/broker-client/transport-socket.d.ts +6 -1
  162. package/dist/core/broker-client/transport-socket.js +1 -1
  163. package/dist/core/canvas/canvas.d.ts +39 -18
  164. package/dist/core/canvas/canvas.js +21 -20
  165. package/dist/core/canvas/context-strip.d.ts +20 -0
  166. package/dist/core/canvas/context-strip.js +5 -0
  167. package/dist/core/canvas/crons.d.ts +12 -20
  168. package/dist/core/canvas/crons.js +15 -13
  169. package/dist/core/canvas/db.js +2 -2
  170. package/dist/core/canvas/deliveries.d.ts +4 -1
  171. package/dist/core/canvas/deliveries.js +1 -1
  172. package/dist/core/canvas/executions.js +1 -1
  173. package/dist/core/canvas/history.d.ts +2 -1
  174. package/dist/core/canvas/history.js +17 -18
  175. package/dist/core/canvas/memory-reads.d.ts +19 -0
  176. package/dist/core/canvas/memory-reads.js +1 -0
  177. package/dist/core/canvas/migrations.d.ts +0 -4
  178. package/dist/core/canvas/migrations.js +99 -32
  179. package/dist/core/canvas/nav-render.js +1 -1
  180. package/dist/core/canvas/node-agent-paths.d.ts +8 -0
  181. package/dist/core/canvas/node-agent-paths.js +1 -0
  182. package/dist/core/canvas/node-recap.d.ts +2 -2
  183. package/dist/core/canvas/node-recap.js +2 -2
  184. package/dist/core/canvas/node-tokens.d.ts +24 -0
  185. package/dist/core/canvas/node-tokens.js +5 -0
  186. package/dist/core/canvas/paths.d.ts +16 -19
  187. package/dist/core/canvas/paths.js +1 -1
  188. package/dist/core/canvas/render-source.d.ts +2 -2
  189. package/dist/core/canvas/render-source.js +6 -9
  190. package/dist/core/canvas/sessions.d.ts +18 -0
  191. package/dist/core/canvas/sessions.js +4 -1
  192. package/dist/core/canvas/types.d.ts +22 -6
  193. package/dist/core/command-hooks/compose.js +1 -1
  194. package/dist/core/command-hooks/discovery.d.ts +15 -1
  195. package/dist/core/command-hooks/discovery.js +1 -1
  196. package/dist/core/command-hooks/help.d.ts +2 -2
  197. package/dist/core/command-hooks/lifecycle-create.d.ts +1 -1
  198. package/dist/core/command-hooks/lifecycle-create.js +1 -1
  199. package/dist/core/command-hooks/transport/exec-invoke.d.ts +2 -0
  200. package/dist/core/command-hooks/transport/exec-invoke.js +1 -1
  201. package/dist/core/command-hooks/transport/exec-lifecycle.d.ts +1 -0
  202. package/dist/core/command-hooks/transport/exec-lifecycle.js +1 -1
  203. package/dist/core/command-hooks/transport/plan.d.ts +3 -0
  204. package/dist/core/command-hooks/transport/plan.js +1 -0
  205. package/dist/core/command-plugins/discovery.js +1 -1
  206. package/dist/core/command.d.ts +6 -2
  207. package/dist/core/command.js +6 -6
  208. package/dist/core/config.d.ts +8 -5
  209. package/dist/core/config.js +1 -1
  210. package/dist/core/conversation-store/client-worker.d.ts +1 -0
  211. package/dist/core/conversation-store/client-worker.js +1 -0
  212. package/dist/core/conversation-store/client.d.ts +2 -0
  213. package/dist/core/conversation-store/client.js +2 -0
  214. package/dist/core/conversation-store/file-store.d.ts +37 -0
  215. package/dist/core/conversation-store/file-store.js +9 -0
  216. package/dist/core/conversation-store/index.d.ts +50 -0
  217. package/dist/core/conversation-store/index.js +1 -0
  218. package/dist/core/conversation-store/listener.d.ts +4 -0
  219. package/dist/core/conversation-store/listener.js +3 -0
  220. package/dist/core/conversation-store/patch.d.ts +1 -0
  221. package/dist/core/conversation-store/patch.js +1 -0
  222. package/dist/core/directory/join.d.ts +27 -0
  223. package/dist/core/directory/join.js +1 -0
  224. package/dist/core/disk-usage.d.ts +4 -0
  225. package/dist/core/disk-usage.js +1 -0
  226. package/dist/core/events/emit.d.ts +2 -0
  227. package/dist/core/events/emit.js +3 -1
  228. package/dist/core/feed/feed.js +4 -4
  229. package/dist/core/feed/messages.d.ts +3 -0
  230. package/dist/core/feed/messages.js +6 -6
  231. package/dist/core/fs-utils.d.ts +2 -0
  232. package/dist/core/fs-utils.js +3 -3
  233. package/dist/core/grants/local.d.ts +4 -0
  234. package/dist/core/grants/local.js +1 -0
  235. package/dist/core/grants/mirror.d.ts +44 -0
  236. package/dist/core/grants/mirror.js +4 -0
  237. package/dist/core/grants/on-sync.d.ts +22 -0
  238. package/dist/core/grants/on-sync.js +1 -0
  239. package/dist/core/grants/remove.d.ts +7 -0
  240. package/dist/core/grants/remove.js +1 -0
  241. package/dist/core/grants/sandboxes.d.ts +7 -0
  242. package/dist/core/grants/sandboxes.js +1 -0
  243. package/dist/core/grants/sync.d.ts +12 -0
  244. package/dist/core/grants/sync.js +1 -0
  245. package/dist/core/help/memory-extensions.d.ts +3 -0
  246. package/dist/core/help/memory-extensions.js +2 -0
  247. package/dist/core/help.d.ts +14 -0
  248. package/dist/core/help.js +7 -7
  249. package/dist/core/host-exports/builtins.js +3 -3
  250. package/dist/core/host-exports/export.js +3 -3
  251. package/dist/core/human/action-binding.d.ts +2 -0
  252. package/dist/core/human/action-binding.js +1 -1
  253. package/dist/core/human/feedback-companion.d.ts +0 -1
  254. package/dist/core/human/feedback-companion.js +1 -1
  255. package/dist/core/human/page-schema.d.ts +1 -1
  256. package/dist/core/inspector/core.d.ts +2 -0
  257. package/dist/core/inspector/core.js +1 -1
  258. package/dist/core/inspector/tui.js +2 -2
  259. package/dist/core/installed-plugins.js +1 -1
  260. package/dist/core/io.js +6 -6
  261. package/dist/core/keybindings/catalog.d.ts +1 -1
  262. package/dist/core/keybindings/catalog.js +1 -1
  263. package/dist/core/layout-migrate/index.d.ts +15 -0
  264. package/dist/core/layout-migrate/index.js +4 -0
  265. package/dist/core/layout-migrate/paths.d.ts +26 -0
  266. package/dist/core/layout-migrate/paths.js +1 -0
  267. package/dist/core/layout-migrate/stored-paths.d.ts +2 -0
  268. package/dist/core/layout-migrate/stored-paths.js +11 -0
  269. package/dist/core/layout-migrate/stores.d.ts +17 -0
  270. package/dist/core/layout-migrate/stores.js +4 -0
  271. package/dist/core/layout.d.ts +26 -0
  272. package/dist/core/layout.js +2 -0
  273. package/dist/core/llm/run.d.ts +60 -0
  274. package/dist/core/llm/run.js +4 -0
  275. package/dist/core/memory/cli-selector.d.ts +13 -0
  276. package/dist/core/memory/cli-selector.js +1 -0
  277. package/dist/core/memory/history.d.ts +5 -4
  278. package/dist/core/memory/history.js +6 -6
  279. package/dist/core/memory/inline-ref-inventory.d.ts +2 -1
  280. package/dist/core/memory/inline-ref-inventory.js +1 -1
  281. package/dist/core/memory/lint.js +2 -2
  282. package/dist/core/memory/mutation-domain.d.ts +2 -2
  283. package/dist/core/memory/mutation-domain.js +8 -7
  284. package/dist/core/memory/mutations.js +1 -1
  285. package/dist/core/memory/service.d.ts +5 -0
  286. package/dist/core/memory/service.js +2 -2
  287. package/dist/core/memory-resolver.d.ts +11 -7
  288. package/dist/core/memory-resolver.js +2 -2
  289. package/dist/core/model-auth/daemon-login.d.ts +13 -0
  290. package/dist/core/model-auth/daemon-login.js +1 -0
  291. package/dist/core/model-auth/flows.d.ts +45 -0
  292. package/dist/core/model-auth/flows.js +1 -0
  293. package/dist/core/model-auth/handoff-jti.d.ts +4 -0
  294. package/dist/core/model-auth/handoff-jti.js +1 -0
  295. package/dist/core/model-routes.js +1 -1
  296. package/dist/core/nested-stores.d.ts +2 -1
  297. package/dist/core/nested-stores.js +1 -2
  298. package/dist/core/preview-registry.js +2 -2
  299. package/dist/core/profiles/env-store.js +2 -2
  300. package/dist/core/profiles/manifest.d.ts +16 -5
  301. package/dist/core/profiles/manifest.js +1 -1
  302. package/dist/core/provider-management.js +1 -1
  303. package/dist/core/review/birth.js +1 -1
  304. package/dist/core/review/companion.d.ts +0 -2
  305. package/dist/core/review/companion.js +1 -1
  306. package/dist/core/review/fork.js +2 -2
  307. package/dist/core/review/realize.js +1 -1
  308. package/dist/core/review/stage.d.ts +1 -1
  309. package/dist/core/review/stage.js +1 -1
  310. package/dist/core/review/types.d.ts +1 -1
  311. package/dist/core/runs/events.d.ts +34 -0
  312. package/dist/core/runs/events.js +1 -0
  313. package/dist/core/runs/operations.d.ts +43 -0
  314. package/dist/core/runs/operations.js +12 -0
  315. package/dist/core/runtime/bearings-render.d.ts +2 -2
  316. package/dist/core/runtime/bearings-render.js +11 -11
  317. package/dist/core/runtime/bearings.js +2 -2
  318. package/dist/core/runtime/broker/cli-relay.d.ts +5 -0
  319. package/dist/core/runtime/broker/cli-relay.js +1 -0
  320. package/dist/core/runtime/broker/client-registry.d.ts +0 -5
  321. package/dist/core/runtime/broker/client-registry.js +1 -1
  322. package/dist/core/runtime/broker/daemon-ops.d.ts +28 -1
  323. package/dist/core/runtime/broker/daemon-ops.js +1 -1
  324. package/dist/core/runtime/broker/engine-drive.d.ts +0 -2
  325. package/dist/core/runtime/broker/engine-drive.js +1 -1
  326. package/dist/core/runtime/broker/fault-retry.d.ts +9 -0
  327. package/dist/core/runtime/broker/fault-retry.js +1 -1
  328. package/dist/core/runtime/broker/frame-dispatch.d.ts +5 -9
  329. package/dist/core/runtime/broker/frame-dispatch.js +1 -1
  330. package/dist/core/runtime/broker/frame-engine-control.d.ts +3 -2
  331. package/dist/core/runtime/broker/frame-engine-control.js +1 -1
  332. package/dist/core/runtime/broker/frame-memory-refs.d.ts +4 -3
  333. package/dist/core/runtime/broker/frame-memory-refs.js +1 -1
  334. package/dist/core/runtime/broker/frame-read.js +1 -1
  335. package/dist/core/runtime/broker/frame-session.js +1 -1
  336. package/dist/core/runtime/broker/held-deferred-inbox.d.ts +3 -1
  337. package/dist/core/runtime/broker/held-deferred-inbox.js +1 -1
  338. package/dist/core/runtime/broker/inbox.d.ts +1 -2
  339. package/dist/core/runtime/broker/inbox.js +7 -10
  340. package/dist/core/runtime/broker/model-adapter.d.ts +9 -0
  341. package/dist/core/runtime/broker/model-adapter.js +2 -0
  342. package/dist/core/runtime/broker/node-token.d.ts +3 -0
  343. package/dist/core/runtime/broker/node-token.js +1 -0
  344. package/dist/core/runtime/broker/read-ops.js +1 -1
  345. package/dist/core/runtime/broker/rebind.d.ts +2 -0
  346. package/dist/core/runtime/broker/rebind.js +1 -1
  347. package/dist/core/runtime/broker/tool-groups.js +1 -1
  348. package/dist/core/runtime/broker/turn-ignition.d.ts +3 -3
  349. package/dist/core/runtime/broker/turn-ignition.js +1 -1
  350. package/dist/core/runtime/broker-cli.js +3 -3
  351. package/dist/core/runtime/broker-extension-render.d.ts +2 -5
  352. package/dist/core/runtime/broker-extension-render.js +4 -4
  353. package/dist/core/runtime/broker-persona-guidance.js +3 -3
  354. package/dist/core/runtime/broker-protocol.d.ts +7 -8
  355. package/dist/core/runtime/broker.js +1 -1
  356. package/dist/core/runtime/canvas-extensions.d.ts +1 -0
  357. package/dist/core/runtime/canvas-extensions.js +1 -1
  358. package/dist/core/runtime/close.d.ts +9 -0
  359. package/dist/core/runtime/close.js +1 -1
  360. package/dist/core/runtime/deliver-live.js +1 -1
  361. package/dist/core/runtime/fleet.d.ts +1 -1
  362. package/dist/core/runtime/front-door-login.js +1 -1
  363. package/dist/core/runtime/front-door.js +2 -2
  364. package/dist/core/runtime/headless-pi.d.ts +1 -1
  365. package/dist/core/runtime/headless-pi.js +1 -1
  366. package/dist/core/runtime/host.d.ts +1 -1
  367. package/dist/core/runtime/host.js +3 -1
  368. package/dist/core/runtime/invocation.d.ts +3 -0
  369. package/dist/core/runtime/kickoff.d.ts +1 -0
  370. package/dist/core/runtime/kickoff.js +16 -16
  371. package/dist/core/runtime/launch-target.d.ts +1 -1
  372. package/dist/core/runtime/launch-target.js +1 -1
  373. package/dist/core/runtime/launch.d.ts +10 -35
  374. package/dist/core/runtime/launch.js +1 -1
  375. package/dist/core/runtime/lifecycle.js +2 -2
  376. package/dist/core/runtime/memory.d.ts +1 -1
  377. package/dist/core/runtime/memory.js +1 -1
  378. package/dist/core/runtime/model-registry.js +1 -1
  379. package/dist/core/runtime/model-swap.d.ts +17 -19
  380. package/dist/core/runtime/model-swap.js +1 -1
  381. package/dist/core/runtime/node-read.js +3 -2
  382. package/dist/core/runtime/nodes.d.ts +7 -0
  383. package/dist/core/runtime/nodes.js +1 -1
  384. package/dist/core/runtime/outcome-document.js +1 -1
  385. package/dist/core/runtime/package-health.js +2 -2
  386. package/dist/core/runtime/persona.js +2 -2
  387. package/dist/core/runtime/pi-vendored.js +1 -1
  388. package/dist/core/runtime/placement-tmux.d.ts +1 -1
  389. package/dist/core/runtime/placement-tmux.js +1 -1
  390. package/dist/core/runtime/placement.js +1 -1
  391. package/dist/core/runtime/promote.js +1 -1
  392. package/dist/core/runtime/provider-credential-gate.js +2 -2
  393. package/dist/core/runtime/recovery-hold.d.ts +2 -0
  394. package/dist/core/runtime/recovery-hold.js +1 -0
  395. package/dist/core/runtime/recycle.js +1 -1
  396. package/dist/core/runtime/reopen.js +1 -1
  397. package/dist/core/runtime/reset.js +1 -1
  398. package/dist/core/runtime/revive.d.ts +1 -1
  399. package/dist/core/runtime/revive.js +2 -3
  400. package/dist/core/runtime/roadmap.js +4 -4
  401. package/dist/core/runtime/run-command.d.ts +6 -0
  402. package/dist/core/runtime/run-command.js +1 -1
  403. package/dist/core/runtime/session-branch.d.ts +2 -8
  404. package/dist/core/runtime/session-branch.js +1 -1
  405. package/dist/core/runtime/session-list-cache.d.ts +6 -22
  406. package/dist/core/runtime/session-list-cache.js +1 -1
  407. package/dist/core/runtime/situational-context.js +2 -2
  408. package/dist/core/runtime/spawn-env.d.ts +6 -6
  409. package/dist/core/runtime/spawn-env.js +1 -1
  410. package/dist/core/runtime/spawn.d.ts +5 -0
  411. package/dist/core/runtime/spawn.js +2 -2
  412. package/dist/core/runtime/stop-guard.js +2 -2
  413. package/dist/core/runtime/stop-signals.d.ts +2 -2
  414. package/dist/core/runtime/stop-signals.js +1 -1
  415. package/dist/core/runtime/structured-output.d.ts +10 -4
  416. package/dist/core/runtime/structured-output.js +4 -4
  417. package/dist/core/runtime/tmux-bindings.js +3 -3
  418. package/dist/core/runtime/tmux-driver.d.ts +4 -0
  419. package/dist/core/runtime/tmux-driver.js +7 -7
  420. package/dist/core/runtime/view-socket.d.ts +9 -2
  421. package/dist/core/runtime/view-socket.js +1 -1
  422. package/dist/core/runtime/warm-pool.js +1 -1
  423. package/dist/core/scope.d.ts +3 -2
  424. package/dist/core/scope.js +1 -1
  425. package/dist/core/scoped-state/db.js +1 -1
  426. package/dist/core/scoped-state/migrate.js +3 -3
  427. package/dist/core/scoped-state/profiles.d.ts +2 -1
  428. package/dist/core/scoped-state/profiles.js +1 -1
  429. package/dist/core/scoped-state/schema.d.ts +2 -1
  430. package/dist/core/scoped-state/schema.js +4 -4
  431. package/dist/core/scopes.d.ts +2 -2
  432. package/dist/core/scopes.js +1 -1
  433. package/dist/core/secrets.d.ts +1 -0
  434. package/dist/core/secrets.js +2 -2
  435. package/dist/core/spaces/mounts.d.ts +32 -0
  436. package/dist/core/spaces/mounts.js +1 -0
  437. package/dist/core/spaces/open-beneath.d.ts +71 -0
  438. package/dist/core/spaces/open-beneath.js +1 -0
  439. package/dist/core/spaces/permissions.d.ts +46 -0
  440. package/dist/core/spaces/permissions.js +1 -0
  441. package/dist/core/spaces/process.d.ts +27 -0
  442. package/dist/core/spaces/process.js +1 -0
  443. package/dist/core/spaces/space.d.ts +8 -0
  444. package/dist/core/spaces/space.js +2 -0
  445. package/dist/core/spaces/stop.d.ts +8 -0
  446. package/dist/core/spaces/stop.js +2 -0
  447. package/dist/core/spaces/users.d.ts +19 -0
  448. package/dist/core/spaces/users.js +1 -0
  449. package/dist/core/subscription-state.d.ts +2 -0
  450. package/dist/core/subscription-state.js +3 -3
  451. package/dist/core/substrate/frontmatter-validation.js +1 -1
  452. package/dist/core/substrate/injected-store.d.ts +28 -3
  453. package/dist/core/substrate/injected-store.js +2 -1
  454. package/dist/core/substrate/listings.js +1 -1
  455. package/dist/core/substrate/memory-events.d.ts +6 -0
  456. package/dist/core/substrate/memory-events.js +1 -0
  457. package/dist/core/substrate/on-read-node.js +1 -1
  458. package/dist/core/substrate/on-read.d.ts +6 -6
  459. package/dist/core/substrate/on-read.js +5 -5
  460. package/dist/core/substrate/plan.js +1 -1
  461. package/dist/core/substrate/render-node.js +1 -1
  462. package/dist/core/substrate/render.d.ts +4 -3
  463. package/dist/core/substrate/render.js +38 -38
  464. package/dist/core/substrate/schema.d.ts +6 -0
  465. package/dist/core/substrate/schema.js +1 -1
  466. package/dist/core/tui/panel.js +1 -1
  467. package/dist/core/user-settings.d.ts +12 -0
  468. package/dist/core/user-settings.js +1 -1
  469. package/dist/core/worktree-mutation-async.js +5 -5
  470. package/dist/core/worktree-quarantine.js +1 -1
  471. package/dist/core/worktree-remove.d.ts +15 -0
  472. package/dist/core/worktree-remove.js +1 -0
  473. package/dist/core/worktree-sweep.js +6 -6
  474. package/dist/daemon/analytics/compute.d.ts +37 -0
  475. package/dist/daemon/analytics/compute.js +1 -0
  476. package/dist/daemon/analytics/scan-worker.d.ts +1 -0
  477. package/dist/daemon/analytics/scan-worker.js +2 -0
  478. package/dist/daemon/analytics/scan.d.ts +37 -0
  479. package/dist/daemon/analytics/scan.js +1 -0
  480. package/dist/daemon/api/app-listener.d.ts +11 -0
  481. package/dist/daemon/api/app-listener.js +21 -0
  482. package/dist/daemon/api/bridge.js +5 -5
  483. package/dist/daemon/api/handlers/analytics.d.ts +2 -0
  484. package/dist/daemon/api/handlers/analytics.js +1 -0
  485. package/dist/daemon/api/handlers/attach.js +1 -1
  486. package/dist/daemon/api/handlers/bash-jobs.js +2 -2
  487. package/dist/daemon/api/handlers/bash.js +1 -1
  488. package/dist/daemon/api/handlers/broker-ops.js +1 -1
  489. package/dist/daemon/api/handlers/canvas.d.ts +1 -1
  490. package/dist/daemon/api/handlers/canvas.js +6 -6
  491. package/dist/daemon/api/handlers/crons.js +1 -1
  492. package/dist/daemon/api/handlers/files.d.ts +1 -1
  493. package/dist/daemon/api/handlers/files.js +1 -1
  494. package/dist/daemon/api/handlers/health.js +2 -2
  495. package/dist/daemon/api/handlers/hook-exec.d.ts +2 -0
  496. package/dist/daemon/api/handlers/hook-exec.js +1 -0
  497. package/dist/daemon/api/handlers/human-requests.js +1 -1
  498. package/dist/daemon/api/handlers/inbox.js +1 -1
  499. package/dist/daemon/api/handlers/llm.d.ts +2 -0
  500. package/dist/daemon/api/handlers/llm.js +3 -0
  501. package/dist/daemon/api/handlers/memory-reads.d.ts +4 -0
  502. package/dist/daemon/api/handlers/memory-reads.js +1 -0
  503. package/dist/daemon/api/handlers/memory.js +5 -1
  504. package/dist/daemon/api/handlers/messages.js +2 -2
  505. package/dist/daemon/api/handlers/modelauth.js +1 -1
  506. package/dist/daemon/api/handlers/node-outcomes.js +1 -1
  507. package/dist/daemon/api/handlers/node-records.d.ts +2 -0
  508. package/dist/daemon/api/handlers/node-records.js +3 -0
  509. package/dist/daemon/api/handlers/nodes.d.ts +1 -1
  510. package/dist/daemon/api/handlers/nodes.js +1 -1
  511. package/dist/daemon/api/handlers/profiles.d.ts +4 -1
  512. package/dist/daemon/api/handlers/profiles.js +1 -1
  513. package/dist/daemon/api/handlers/reports.js +5 -3
  514. package/dist/daemon/api/handlers/reviews.d.ts +1 -1
  515. package/dist/daemon/api/handlers/reviews.js +1 -1
  516. package/dist/daemon/api/handlers/run-routes.d.ts +2 -0
  517. package/dist/daemon/api/handlers/run-routes.js +1 -0
  518. package/dist/daemon/api/handlers/runs.d.ts +9 -0
  519. package/dist/daemon/api/handlers/runs.js +1 -0
  520. package/dist/daemon/api/handlers/subscriptions.js +1 -1
  521. package/dist/daemon/api/handlers/worktree.js +1 -1
  522. package/dist/daemon/api/map.d.ts +10 -4
  523. package/dist/daemon/api/map.js +2 -2
  524. package/dist/daemon/api/operations.d.ts +18 -0
  525. package/dist/daemon/api/operations.js +1 -0
  526. package/dist/daemon/api/principal.d.ts +42 -0
  527. package/dist/daemon/api/principal.js +1 -0
  528. package/dist/daemon/api/router.d.ts +27 -3
  529. package/dist/daemon/api/router.js +1 -1
  530. package/dist/daemon/api/server.d.ts +4 -0
  531. package/dist/daemon/api/server.js +1 -1
  532. package/dist/daemon/api/vendor-signin.d.ts +7 -0
  533. package/dist/daemon/api/vendor-signin.js +77 -0
  534. package/dist/daemon/api/viewer-presence.d.ts +3 -0
  535. package/dist/daemon/api/viewer-presence.js +1 -0
  536. package/dist/daemon/cron/armed-context.d.ts +4 -0
  537. package/dist/daemon/cron/armed-context.js +1 -1
  538. package/dist/daemon/cron/escalation.js +3 -3
  539. package/dist/daemon/cron/lease-recovery.js +1 -1
  540. package/dist/daemon/cron/sinks.js +2 -2
  541. package/dist/daemon/cron-run.d.ts +5 -5
  542. package/dist/daemon/cron-run.js +1 -1
  543. package/dist/daemon/crtrd.js +8 -7
  544. package/dist/daemon/delivery/run-delivery.d.ts +4 -0
  545. package/dist/daemon/delivery/run-delivery.js +1 -1
  546. package/dist/daemon/fleet.d.ts +1 -1
  547. package/dist/daemon/fleet.js +6 -6
  548. package/dist/daemon/human/finish.js +7 -7
  549. package/dist/daemon/joined-owner.d.ts +5 -0
  550. package/dist/daemon/joined-owner.js +1 -0
  551. package/dist/daemon/manage.js +2 -2
  552. package/dist/daemon/messaging/node-message.js +1 -1
  553. package/dist/daemon/park-activity.js +1 -1
  554. package/dist/daemon/reconcilers/bash-deadline.js +1 -1
  555. package/dist/daemon/reconcilers/broker-supervision.d.ts +1 -1
  556. package/dist/daemon/reconcilers/broker-supervision.js +4 -4
  557. package/dist/daemon/reconcilers/delivery-lane.js +1 -1
  558. package/dist/daemon/reconcilers/live-obligation.d.ts +7 -0
  559. package/dist/daemon/reconcilers/live-obligation.js +1 -1
  560. package/dist/daemon/reconcilers/node-lifecycle/tick.js +1 -1
  561. package/dist/daemon/reconcilers/storage-maintenance.d.ts +20 -1
  562. package/dist/daemon/reconcilers/storage-maintenance.js +1 -1
  563. package/dist/daemon/runs/append-section.d.ts +6 -0
  564. package/dist/daemon/runs/append-section.js +2 -0
  565. package/dist/daemon/runs/observer.d.ts +5 -0
  566. package/dist/daemon/runs/observer.js +4 -0
  567. package/dist/launcher/bwrap.d.ts +4 -0
  568. package/dist/launcher/bwrap.js +2 -0
  569. package/dist/launcher/client.d.ts +2 -0
  570. package/dist/launcher/client.js +3 -0
  571. package/dist/launcher/main.d.ts +1 -0
  572. package/dist/launcher/main.js +3 -0
  573. package/dist/launcher/netrule.d.ts +1 -0
  574. package/dist/launcher/netrule.js +1 -0
  575. package/dist/launcher/seccomp.d.ts +1 -0
  576. package/dist/launcher/seccomp.js +1 -0
  577. package/dist/launcher/spec.d.ts +88 -0
  578. package/dist/launcher/spec.js +1 -0
  579. package/dist/launcher/users.d.ts +18 -0
  580. package/dist/launcher/users.js +7 -0
  581. package/dist/launcher/validate.d.ts +22 -0
  582. package/dist/launcher/validate.js +1 -0
  583. package/dist/migrations/corpus.js +3 -3
  584. package/dist/migrations/runner.js +1 -1
  585. package/dist/native/linux/addon.node +0 -0
  586. package/dist/native/linux.d.ts +29 -0
  587. package/dist/native/linux.js +1 -0
  588. package/dist/pi-extensions/broker-local.js +2 -2
  589. package/dist/pi-extensions/canvas-bash-valve.d.ts +1 -3
  590. package/dist/pi-extensions/canvas-bash-valve.js +4 -7
  591. package/dist/pi-extensions/canvas-context-intro.d.ts +1 -2
  592. package/dist/pi-extensions/canvas-context-intro.js +2 -2
  593. package/dist/pi-extensions/canvas-doc-substrate.js +6 -6
  594. package/dist/pi-extensions/canvas-inbox-watcher.d.ts +2 -1
  595. package/dist/pi-extensions/canvas-inbox-watcher.js +2 -1
  596. package/dist/pi-extensions/canvas-passive-context.d.ts +1 -1
  597. package/dist/pi-extensions/canvas-passive-context.js +11 -11
  598. package/dist/pi-extensions/canvas-recap.js +6 -6
  599. package/dist/pi-extensions/canvas-stophook.js +1 -1
  600. package/dist/pi-extensions/canvas-structured-output.js +4 -4
  601. package/dist/pi-extensions/canvas-tool-guide.js +1 -1
  602. package/dist/pi-extensions/system-prompt-entry.d.ts +15 -0
  603. package/dist/pi-extensions/system-prompt-entry.js +1 -0
  604. package/dist/shared/env.d.ts +5 -1
  605. package/dist/shared/env.js +1 -1
  606. package/dist/shared/strip-cd-prefix.d.ts +1 -0
  607. package/dist/shared/strip-cd-prefix.js +1 -0
  608. package/dist/types.d.ts +31 -15
  609. package/docs/cli/memory-and-preferences.md +3 -1
  610. package/docs/sdk/client.md +34 -0
  611. package/docs/sdk/errors.md +2 -0
  612. package/docs/sdk/memory.md +2 -2
  613. package/docs/sdk/streaming.md +21 -0
  614. package/package.json +18 -4
  615. package/packages/crouter-identity/dist/index.d.ts +54 -0
  616. package/packages/crouter-identity/dist/index.js +166 -0
  617. package/packages/crouter-identity/package.json +33 -0
  618. package/runtime.lock.json +34 -8
  619. package/scripts/build-native-linux.mjs +16 -0
  620. package/scripts/install-runtime.mjs +9 -7
  621. package/scripts/lib/isolation-env.mjs +42 -0
  622. package/scripts/runtime-smoke.mjs +49 -0
  623. package/src/native/linux/addon.c +249 -0
  624. package/dist/core/profiles/state-block.d.ts +0 -4
  625. package/dist/core/profiles/state-block.js +0 -3
  626. package/dist/core/runtime/broker/auth-reload.d.ts +0 -41
  627. package/dist/core/runtime/broker/auth-reload.js +0 -1
  628. package/dist/core/substrate/cli-context-delivery.d.ts +0 -22
  629. package/dist/core/substrate/cli-context-delivery.js +0 -1
@@ -1,10 +1,10 @@
1
- var T=Object.defineProperty;var o=(e,r)=>T(e,"name",{value:r,configurable:!0});import{existsSync as p,mkdirSync as F,readFileSync as H,realpathSync as R,rmSync as O,writeFileSync as I}from"node:fs";import{dirname as B,join as N,resolve as j}from"node:path";import{abandonWorktreeAndRepoint as q,getNode as _}from"./canvas/index.js";import{isPidAlive as G}from"./canvas/pid.js";import{CONFIG_FILE as K,CRTR_DIR_NAME as $,STATE_FILE as z}from"../types.js";import{withExclusiveDirectoryLockAsync as A}from"./exclusive-lock.js";import{gitAsync as s}from"./git.js";import{WORKTREE_LOCK_WAIT_MS as J,checkoutPathForBranchInList as Q,isManagedWorktreeBase as U,managedWorktreePath as V,persistClosedWorktreeState as b,worktreeLockPathForCommonDir as S,WorktreeError as h}from"./worktree.js";import{proveBranchContained as X}from"./worktree-containment.js";import{refDeleteTransactionStdin as Y}from"./worktree-landing.js";const x=Math.min(J,25e3);function D(e){return(e.stderr.trim()||e.stdout.trim()).trim()}o(D,"detailOf");async function c(e,r,a,n){const t=await s(r,e);if(t.status!==0)throw new h(a,`git ${r.join(" ")} failed`,n,D(t));return t.stdout.trim()}o(c,"runGit");async function E(e){return R(j(e,await c(e,["rev-parse","--git-common-dir"],"git_common_dir_failed","Inspect the repository git metadata, then retry.")))}o(E,"commonGitDir");async function k(e,r){const a=await E(e);return A(S(a),r,{timeoutMs:x,timeoutError:o(()=>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")})}o(k,"withRepositoryLock");async function g(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 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(n.status!==0)throw new h("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 c(e,["rev-parse","--verify",a],"base_branch_resolve_failed","Inspect the local base branch, then retry.")}o(g,"resolveLandableBase");function Z(e,r){try{const a=N(r,"info","exclude");F(B(a),{recursive:!0});let n="";try{n=H(a,"utf8")}catch{}const t=[`/${$}/${K}`,`/${$}/${z}`];if(t.every(i=>n.split(`
3
- `).map(l=>l.trim()).includes(i)))return;I(a,`${n}${n!==""&&!n.endsWith(`
1
+ var F=Object.defineProperty;var o=(e,r)=>F(e,"name",{value:r,configurable:!0});import{existsSync as p,mkdirSync as H,readFileSync as R,realpathSync as O,rmSync as B,writeFileSync as N}from"node:fs";import{dirname as j,join as q,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 S,STATE_FILE as J}from"../types.js";import{withExclusiveDirectoryLockAsync as x}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 b,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 x(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=q(r,"info","exclude");H(j(a),{recursive:!0});let n="";try{n=R(a,"utf8")}catch{}const t=[`/${S}/${z}`,`/${S}/${J}`];if(t.every(i=>n.split(`
3
+ `).map(l=>l.trim()).includes(i)))return;N(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
6
  ${t.filter(i=>!n.split(`
7
7
  `).map(l=>l.trim()).includes(i)).join(`
8
8
  `)}
9
- `)}catch{}}o(Z,"seedExclude");async function me(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{O(e.path,{recursive:!0,force:!0})}catch{return}await s(["update-ref","-d",`refs/heads/${e.branch}`,e.base_sha],e.repo_root)}})}o(me,"rollbackManagedWorktreeAsync");async function _e(e,r,a="main"){const n=await c(e,["rev-parse","--show-toplevel"],"not_git_repo","Pass --cwd <real repo> pointing inside a git repository.");if(U(n))throw new h("managed_worktree_base",`cwd ${n} is a managed worktree and cannot be used as a managed worktree base`,"Pass --cwd <real repo> pointing at the repository\u2019s primary checkout.");const t=await E(n);return A(S(t),async()=>{const i=await g(n,a),l=V(r),u=`crtr/${r}`;if(p(l))throw new h("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 h("worktree_branch_exists",`managed worktree branch already exists: ${u}`,"Remove the stale branch or choose a fresh node id.");const w={state:"open",path:l,branch:u,repo_root:n,base_ref:a,base_sha:i,created:new Date().toISOString()};await c(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 h("worktree_add_failed","git worktree add failed","Inspect the git error, fix the repository/worktree state, then retry spawning with --worktree.",D(f));return Z(n,t),w},{timeoutMs:x,timeoutError:o(()=>new h("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(_e,"createManagedWorktreeAsync");async function W(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(W,"cleanTip");async function M(e,r){try{const a=await g(e.path,e.base_ref);return(await s(["merge-base","--is-ancestor",r,a],e.path)).status===0}catch(a){if(a instanceof h&&a.code==="missing_base_branch"&&r===e.base_sha)return!0;throw a}}o(M,"containedInLocalBase");function ee(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(ee,"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||Q(i.stdout,e.branch)!==null)return null;const l=await X(e.repo_root,e,t);return l===null?null:{branchSha:t,containment:l}}o(L,"proveAbsentPathManagedWorktree");async function te(e,r,a){return p(r.path)?null:a.branchSha===null?(b(e,r,"complete"),{node_id:e,branch:r.branch,worktree_path:r.path,worktree_removed:!0,branch_deleted:!0}):a.containment===void 0?null:(b(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(te,"autoDropAbsentPathManagedWorktreeLocked");async function re(e){const r=_(e)?.managed_worktree;return r==null||r.state!=="open"||p(r.path)||await L(r)===null?null:k(r.repo_root,async()=>{const a=_(e)?.managed_worktree;if(a==null||a.state!=="open")return null;const n=await L(a);if(n===null)return null;const t=await te(e,a,n);return t===null?null:{dropped:t,landedSha:n.branchSha??a.base_sha}})}o(re,"concludeAbsentManagedWorktreeAsync");async function we(e){const r=_(e)?.managed_worktree;if(r==null||r.state!=="open")return null;if(!p(r.path))return(await re(e))?.dropped??null;const a=await W(r);return a===null||!await M(r,a)?null:k(r.path,async()=>{const n=_(e)?.managed_worktree;if(n==null||n.state!=="open")return null;const t=await W(n);return t===null||t!==a||!await M(n,t)?null:(b(e,n,"pending"),{...ee(n),node_id:e})})}o(we,"autoDropCleanManagedWorktreeAsync");async function C(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(C,"rebaseInProgress");function y(e,r,a,n,t,i=r.branch){q(e,{cwd:r.repo_root,abandoned:{branch:i,tip_sha:a,at:n,by:t}})}o(y,"persistAbandoned");async function be(e,r){const a=_(e)?.managed_worktree;if(a==null||a.state!=="open")throw new h("no_managed_worktree",`node ${e} has no open managed worktree`,"Nothing to abandon for this node.");return k(a.path,async()=>{const n=_(e),t=n?.managed_worktree;if(n===null||t===void 0||t===null||t.state!=="open")throw new h("no_managed_worktree",`node ${e} has no open managed worktree`,"Nothing to abandon for this node.");if(n.status==="active"||n.status==="idle"||G(n.pi_pid))throw new h("node_running",`node ${e} is still running`,"Stop the node before abandoning its managed worktree.");if(!p(t.path))throw new h("worktree_missing",`managed worktree path is missing: ${t.path}`,"Inspect the branch and worktree registration, then retry.");const i=await c(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 h("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 C(t.path)||await c(t.path,["status","--porcelain"],"status_failed","Inspect the worktree manually, then retry.")!=="")throw new h("dirty_worktree","managed worktree has uncommitted changes",`Commit or stash them in ${t.path}, then rerun \`crtr node worktree abandon\`.`);const d=await c(t.path,["rev-parse","HEAD"],"abandon_tip_failed","Inspect the worktree manually, then retry.");await c(t.repo_root,["worktree","remove",t.path],"worktree_remove_failed","Inspect the worktree manually, then retry.");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 c(t.path,["rev-parse","HEAD"],"abandon_tip_failed","Inspect the branch manually, then retry.");let w=null;try{const d=await g(t.path,t.base_ref);(await s(["merge-base","--is-ancestor",u,d],t.path)).status===0&&(w=d)}catch(d){if(!(d instanceof h)||d.code!=="missing_base_branch")throw d}const f=new Date().toISOString(),P=await c(t.path,["status","--porcelain"],"status_failed","Inspect the worktree manually, then retry.");if(w!==null&&u===t.base_sha){const d=await c(t.repo_root,["rev-parse",`${u}^{tree}`],"abandon_tree_failed","Inspect the branch manually, then retry."),m=await c(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 c(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 c(t.repo_root,["worktree","remove",t.path],"worktree_remove_failed","Inspect the worktree manually, then retry."),(await s(["update-ref","--stdin"],t.repo_root,Y(t.branch,m,`refs/heads/${t.base_ref}`,w))).status===0?(b(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 C(t.path)&&P==="")return await c(t.repo_root,["worktree","remove",t.path],"worktree_remove_failed","Inspect the worktree manually, then retry."),y(e,t,u,f,r),{node_id:e,branch:t.branch,tip_sha:u,worktree_path:t.path,branch_deleted:!1};await c(t.path,["add","--all"],"abandon_stage_failed","Inspect the worktree manually, then retry."),await c(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 v=await c(t.path,["rev-parse","HEAD"],"abandon_tip_failed","Inspect the branch manually, then retry.");return await c(t.repo_root,["worktree","remove",t.path],"worktree_remove_failed","Inspect the worktree manually, then retry."),y(e,t,v,f,r),{node_id:e,branch:t.branch,tip_sha:v,worktree_path:t.path,branch_deleted:!1}})}o(be,"abandonManagedWorktreeAsync");export{be as abandonManagedWorktreeAsync,we as autoDropCleanManagedWorktreeAsync,re as concludeAbsentManagedWorktreeAsync,_e as createManagedWorktreeAsync,me as rollbackManagedWorktreeAsync};
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 <real repo> pointing at the repository\u2019s primary checkout.");const t=await W(n);return x(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 _={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),_},{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?(b(e,r,"complete"),{node_id:e,branch:r.branch,worktree_path:r.path,worktree_removed:!0,branch_deleted:!0}):a.containment===void 0?null:(b(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:(b(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`,"Stop the node before abandoning its managed worktree.");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 _=null;try{const d=await $(t.path,t.base_ref);(await s(["merge-base","--is-ancestor",u,d],t.path)).status===0&&(_=d)}catch(d){if(!(d instanceof c)||d.code!=="missing_base_branch")throw d}const f=new Date().toISOString(),P=await h(t.path,["status","--porcelain"],"status_failed","Inspect the worktree manually, then retry.");if(_!==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}`,_))).status===0?(b(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)&&P==="")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};
@@ -1 +1 @@
1
- var i=Object.defineProperty;var r=(o,e)=>i(o,"name",{value:e,configurable:!0});import{spawn as d}from"node:child_process";import{existsSync as p}from"node:fs";import{getNode as l,listNodes as u}from"./canvas/index.js";async function m(o){return p(o)?new Promise(e=>{const t=d("du",["-sk",o]);let a="";t.stdout.on("data",n=>a+=n.toString()),t.stderr.on("data",()=>{}),t.on("error",()=>e(null)),t.on("close",n=>{if(n!==0)return e(null);const s=Number(a.trim().split(/\s+/)[0]);e(Number.isSafeInteger(s)?s*1024:null)})}):null}r(m,"directorySizeBytes");async function b(){const o=[];for(const e of u()){const t=l(e.node_id)?.managed_worktree;t?.sweep?.quarantined===!0&&o.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 o.sort((e,t)=>e.last_attempt.localeCompare(t.last_attempt)),Promise.all(o.map(async e=>({...e,size_bytes:await m(e.path)})))}r(b,"listQuarantinedManagedWorktrees");export{b as listQuarantinedManagedWorktrees};
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};
@@ -0,0 +1,15 @@
1
+ import { type GitResult } from './git.js';
2
+ export interface CheckoutRemoval {
3
+ result: GitResult;
4
+ /** The checkout's files, now in the trash; delete with `deleteTrashAsync` after releasing the lock. */
5
+ trash?: string;
6
+ }
7
+ /** `git worktree remove <path>` (no `--force`) that leaves the checkout's files
8
+ * in the trash instead of deleting them. Call with the repository lock held,
9
+ * from `cwd` outside the checkout. */
10
+ export declare function removeWorktreeCheckoutAsync(cwd: string, path: string): Promise<CheckoutRemoval>;
11
+ /** Delete `trash` (a removal's own entry, when given) plus any leftovers
12
+ * beside `path` from an interrupted earlier delete. Runs `rm` as a child
13
+ * process so the delete occupies neither the event loop nor libuv's
14
+ * threadpool. Never throws. */
15
+ export declare function deleteTrashAsync(path: string, trash?: string): Promise<void>;
@@ -0,0 +1 @@
1
+ var w=Object.defineProperty;var u=(t,r)=>w(t,"name",{value:r,configurable:!0});import{execFile as k}from"node:child_process";import{randomUUID as g}from"node:crypto";import{existsSync as f,mkdirSync as v,readdirSync as b,renameSync as d}from"node:fs";import{basename as $,dirname as l,join as c}from"node:path";import{gitAsync as a}from"./git.js";function y(t){return c(l(t),".crtr-trash")}u(y,"trashDirFor");function m(t){return{status:1,stdout:"",stderr:t}}u(m,"refused");const D=600*1e3;async function T(t,r){const n=await a(["rev-parse","--absolute-git-dir"],r);if(n.status!==0)return{result:n};if(f(c(n.stdout.trim(),"locked")))return{result:m(`fatal: cannot remove a locked working tree: ${r}`)};if(f(c(n.stdout.trim(),"modules")))return{result:m(`fatal: working trees containing submodules cannot be moved or removed: ${r}`)};const o=await a(["status","--porcelain","--ignore-submodules=none"],r);if(o.status!==0)return{result:o};if(o.stdout.trim()!=="")return{result:m(`fatal: '${r}' contains modified or untracked files, use --force to delete it`)};const s=c(y(r),`${Date.now()}-${$(r)}-${g()}`);try{v(l(s),{recursive:!0}),d(r,s)}catch{return{result:await a(["worktree","remove",r],t)}}const e=await a(["worktree","remove",r],t);if(e.status===0)return{result:e,trash:s};try{d(s,r)}catch{}return{result:e}}u(T,"removeWorktreeCheckoutAsync");async function U(t,r){const n=y(t);let o=[];try{o=b(n)}catch{}const s=Date.now()-D,e=o.filter(i=>Number(i.split("-",1)[0])<s).map(i=>c(n,i));r!==void 0&&!e.includes(r)&&e.push(r),e.length!==0&&await new Promise(i=>{k("rm",["-rf","--",...e],()=>i())})}u(U,"deleteTrashAsync");export{U as deleteTrashAsync,T as removeWorktreeCheckoutAsync};
@@ -1,6 +1,6 @@
1
- var L=Object.defineProperty;var c=(e,t)=>L(e,"name",{value:t,configurable:!0});import{createHash as M}from"node:crypto";import{existsSync as w,realpathSync as W}from"node:fs";import{resolve as T}from"node:path";import{completeCleanup as F,getNode as v,recordSweepAttempt as q}from"./canvas/index.js";import{withExclusiveDirectoryLockAsync as z}from"./exclusive-lock.js";import{gitAsync as u}from"./git.js";import{proveBranchContained as A,upstreamTip as K}from"./worktree-containment.js";import{parseStatusPorcelain as U,planLandIntoCheckout as Q,refDeleteTransactionStdin as V}from"./worktree-landing.js";import{shellQuote as j}from"../shared/shell-quote.js";import{WORKTREE_LOCK_WAIT_MS as C,checkoutPathForBranchInList as H,samePath as O,worktreeLockPathForCommonDir as G}from"./worktree.js";const E=[2,5,15,30,60],I=E.length;function D(e){return e==null||e.state==="abandoned"?!1:e.state==="open"||e.cleanup!=="complete"}c(D,"isReconcilable");function ke(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}c(ke,"isDueForSweep");async function b(e,t){const r=await u(["rev-parse","--verify","--quiet",t],e);return r.status===0?r.stdout.trim():null}c(b,"revParseRef");function d(e){return(e.stderr.trim()||e.stdout.trim()).trim()}c(d,"detailOf");async function N(e){const t=await u(["rev-parse","--git-common-dir"],e);if(t.status!==0)return null;try{return W(T(e,t.stdout.trim()))}catch{return null}}c(N,"commonGitDirAsync");async function J(e){for(const t of["rebase-merge","rebase-apply"]){const r=await u(["rev-parse","--git-path",t],e);if(r.status!==0)return null;if(w(T(e,r.stdout.trim())))return!0}return!1}c(J,"isRebaseInProgressAsync");function X(e){for(const t of e.split(`
2
- `))if(t.startsWith("worktree "))return t.slice(9).trim();return null}c(X,"mainWorktreePath");function Y(e,t,r){let a=null;for(const n of e.split(`
3
- `))if(n.startsWith("worktree "))a=n.slice(9).trim();else if(a!==null&&n===`branch refs/heads/${r}`&&O(a,t))return a;return null}c(Y,"registeredCheckoutPath");async function Z(e){const t=w(e.path),r=t?e.path:e.repo_root,a=await b(r,`refs/heads/${e.base_ref}`),n=await K(r,e.base_ref),i=await b(r,`refs/heads/${e.branch}`);if(!t)return{base:a,upstream:n?.sha??null,branch:i,checkout:"absent"};const o=await u(["rev-parse","HEAD"],e.path),l=await u(["status","--porcelain"],e.path),s=o.status!==0||l.status!==0?null:M("sha256").update(`${o.stdout.trim()}
4
- ${l.stdout}`).digest("hex");return{base:a,upstream:n?.sha??null,branch:i,checkout:s}}c(Z,"observe");function ee(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}c(ee,"sameObservation");function te(e,t,r,a,n,i){if(r.status!=="refused"&&r.status!=="failed"||!i.stillEligible())return;const o=(t.sweep?.attempts??0)+1,l=E[Math.min(o,E.length)-1]??E[0],s=v(e)?.managed_worktree;s==null||!D(s)||q(e,{reason:r.reason,...r.detail===void 0?{}:{detail:r.detail},observed:a,attempts:o,lastAttemptAt:new Date(n).toISOString(),nextAttemptAt:new Date(n+l*6e4).toISOString(),quarantined:o>=I})}c(te,"recordUnfinished");async function ae(e,t,r,a,n){const i=await u(["status","--porcelain"],e);if(i.status!==0)return{ok:!1,detail:d(i)};const o=await u(["diff","--name-only",r,a],e);if(o.status!==0)return{ok:!1,detail:d(o)};const l=o.stdout.split(`
5
- `).map(m=>m.trim()).filter(m=>m!==""),s=Q(U(i.stdout),l);if(s.kind==="refuse")return{ok:!1,detail:`untracked files in ${e} would be overwritten by the incoming commits: ${s.collisions.join(", ")}`};if(!n.stillEligible())return{ok:!1,detail:"node is no longer terminal",ineligible:!0};const h=c(async()=>{const m=await u(["merge","--ff-only",a],e);return m.status===0?{ok:!0}:{ok:!1,detail:d(m)}},"merge");if(s.kind==="direct")return h();const $=await b(e,"refs/stash"),p=await u(["stash","push","--message",`crtr landing ${t}`],e);if(p.status!==0)return{ok:!1,detail:d(p)};const f=await b(e,"refs/stash");if(f===null||f===$)return h();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 h();return _.ok?(await u(["stash","apply",f],e)).status===0?(await R(e,f),{ok:!0}):(await u(["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 u(["stash","apply",f],e)).status===0?(await R(e,f),_):{ok:!1,detail:`${_.detail}
6
- ${k}`}}c(ae,"landIntoBaseCheckoutAsync");async function R(e,t){await b(e,"refs/stash")===t&&await u(["stash","drop"],e)}c(R,"dropStashIfStillTip");async function re(e,t,r){const a=await b(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 n=await u(["rebase",a],e.path);if(n.status!==0)return await u(["rebase","--abort"],e.path),{ok:!1,detail:d(n)};const i=await b(e.path,"HEAD");if(i===null)return{ok:!1,detail:"could not read the rebased worktree tip"};if((await u(["merge-base","--is-ancestor",a,i],e.path)).status!==0)return{ok:!1,detail:`the rebased branch is not a fast-forward of '${e.base_ref}'`};const o=H(t,e.base_ref);if(o===null){if(!r.stillEligible())return{ok:!1,detail:"node is no longer terminal",ineligible:!0};const h=await u(["update-ref",`refs/heads/${e.base_ref}`,i,a],e.path);return h.status===0?{ok:!0,landedSha:i}:{ok:!1,detail:d(h)}}const l=await u(["rev-parse","HEAD"],o);if(l.status!==0||l.stdout.trim()!==a)return{ok:!1,detail:`base checkout ${o} is not at ${a}`};const s=await ae(o,e.branch,a,i,r);return s.ok?{ok:!0,landedSha:i,...s.stash===void 0?{}:{stash:s.stash}}:s}c(re,"finishBlockedLand");const y={status:"skipped",reason:"node_no_longer_terminal"};async function _e(e,t,r=Date.now()){const a=v(e)?.managed_worktree;if(!D(a)||a==null)return{status:"skipped",reason:"already_reconciled"};const n=w(a.path)?a.path:a.repo_root;if(!w(n))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 i=await N(n);if(i===null)return S(e,a,{status:"failed",reason:"git_dir_unreadable",detail:`could not resolve the git common dir from ${n}`},{base:null,upstream:null,branch:null,checkout:null},r,t);let o={base:null,upstream:null,branch:null,checkout:null},l;try{l=await z(G(i),async()=>{if(!t.stillEligible())return y;const s=v(e)?.managed_worktree;return!D(s)||s==null?{status:"skipped",reason:"already_reconciled"}:(o=await Z(s),s.sweep!==void 0&&ee(s.sweep.observed,o)?{status:"refused",reason:s.sweep.reason,...s.sweep.detail===void 0?{}:{detail:s.sweep.detail},unchanged:!0}:w(s.path)?await ne(e,s,t):await se(e,s,o,t))},{timeoutMs:C,timeoutError:c(()=>new Error(`timed out waiting for the managed-worktree lock on ${i}`),"timeoutError")})}catch(s){l={status:"failed",reason:"lock_unavailable",detail:s instanceof Error?s.message:String(s)}}return S(e,a,l,o,r,t)}c(_e,"reconcileManagedWorktree");function S(e,t,r,a,n,i){return te(e,t,r,a,n,i),r}c(S,"finish");function x(e,t,r){if(!r.stillEligible())return y;const a=v(e)?.managed_worktree;return a==null||a.path!==t.path||a.branch!==t.branch?{status:"skipped",reason:"record_changed"}:(F(e),{status:"complete"})}c(x,"persistComplete");async function se(e,t,r,a){const n=t.repo_root;if(r.branch===null){const s=await u(["show-ref","--verify","--quiet",`refs/heads/${t.branch}`],n);return s.status!==1?{status:"failed",reason:"branch_inspection_failed",detail:d(s)}:x(e,t,a)}const i=await u(["worktree","list","--porcelain"],n);if(i.status!==0)return{status:"failed",reason:"worktree_list_failed",detail:d(i)};const o=H(i.stdout,t.branch);if(o!==null)return{status:"refused",reason:"branch_checked_out_elsewhere",detail:`${t.branch} is checked out at ${o}`};const l=await A(n,t,r.branch);return l===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`}:B(e,t,n,r.branch,l,a)}c(se,"concludeAbsentCheckout");async function ne(e,t,r){const a=await u(["worktree","list","--porcelain"],t.path);if(a.status!==0)return{status:"failed",reason:"worktree_list_failed",detail:d(a)};const n=t.disposal,i=n?.observed_branch??t.branch,o=Y(a.stdout,t.path,i);if(o===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 l=X(a.stdout);if(l===null||O(l,o)||!w(l))return{status:"refused",reason:"repo_unavailable",detail:`the repository's main checkout is unavailable, so ${t.path} cannot be removed from outside itself`};const s=await J(t.path);if(s===null)return{status:"failed",reason:"git_path_failed",detail:`could not inspect in-progress operations in ${t.path}`};if(s)return{status:"refused",reason:"rebase_in_progress",detail:`${t.path} is mid-rebase`};const h=await u(["rev-parse","--abbrev-ref","HEAD"],t.path);if(h.status!==0)return{status:"failed",reason:"branch_check_failed",detail:d(h)};if(h.stdout.trim()!==i)return{status:"refused",reason:"wrong_branch",detail:`expected ${i}, found ${h.stdout.trim()==="HEAD"?"detached HEAD":h.stdout.trim()}`};const $=await u(["status","--porcelain"],t.path);if($.status!==0)return{status:"failed",reason:"status_failed",detail:d($)};if($.stdout.trim()!=="")return{status:"refused",reason:"dirty_worktree",detail:`${t.path} has uncommitted changes`};let p=await b(t.path,"HEAD");if(p===null)return{status:"failed",reason:"head_unreadable",detail:`could not read HEAD in ${t.path}`};if(n!==void 0){if(await N(t.path)!==n.common_dir||p!==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 y;const P=await u(["worktree","remove",o],l);return P.status!==0?{status:"refused",reason:"worktree_remove_failed",detail:d(P)}:x(e,t,r)}let f=await A(t.path,t,p),k;if(f===null&&t.land_intent!==void 0){const g=await re(t,a.stdout,r);if(!g.ok)return g.ineligible===!0?y:{status:"refused",reason:"land_blocked",detail:g.detail};p=g.landedSha,k=g.stash,f=await A(t.path,t,p)}if(f===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 y;const _=await u(["worktree","remove",o],l);if(_.status!==0)return{status:"refused",reason:"worktree_remove_failed",detail:d(_)};const m=await B(e,t,l,p,f,r);return m.status==="complete"&&k!==void 0?{status:"complete",stash:k}:m}c(ne,"reconcilePresentCheckout");async function B(e,t,r,a,n,i){if(!i.stillEligible())return y;const o=V(t.branch,a,n.containingRef,n.containingSha),l=await u(["update-ref","--stdin"],r,o);return l.status!==0?{status:"refused",reason:"branch_delete_failed",detail:d(l)}:x(e,t,i)}c(B,"deleteBranchAndComplete");export{ke as isDueForSweep,D as isReconcilable,_e as reconcileManagedWorktree};
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};
@@ -0,0 +1,37 @@
1
+ import type { AnalyticsDTO, AnalyticsWindow } from '../../api/dto/analytics.js';
2
+ import type { ScanInput, ScanResult } from './scan.js';
3
+ export declare const WINDOW_MS: Record<AnalyticsWindow, number>;
4
+ export interface AgentInput {
5
+ node_id: string;
6
+ name: string;
7
+ title: string | null;
8
+ kind: string;
9
+ parent: string | null;
10
+ cwd: string;
11
+ created: string;
12
+ finalized_at: string | null;
13
+ final_report: string | null;
14
+ lifecycle: 'resident' | 'terminal';
15
+ transcripts: string[];
16
+ }
17
+ export interface MemoryReadInput {
18
+ node_id: string;
19
+ name: string;
20
+ scope: string;
21
+ at: string;
22
+ type: 'read' | 'loaded';
23
+ }
24
+ export interface ComputeInput {
25
+ asOfMs: number;
26
+ window: AnalyticsWindow;
27
+ project: string | null;
28
+ /** Non-`human` node rows only. */
29
+ agents: AgentInput[];
30
+ scan: (input: ScanInput) => Promise<ScanResult>;
31
+ /** Throws when the memory-read log cannot be read. */
32
+ memory: (fromIso: string, toIso: string) => {
33
+ rows: MemoryReadInput[];
34
+ startedAt: string | null;
35
+ };
36
+ }
37
+ export declare function computeAnalytics(input: ComputeInput): Promise<AnalyticsDTO>;
@@ -0,0 +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};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ var b=Object.defineProperty;var d=(e,a)=>b(e,"name",{value:a,configurable:!0});import{createReadStream as M}from"node:fs";import{stat as k}from"node:fs/promises";import{parentPort as N,workerData as S}from"node:worker_threads";const p=d(e=>typeof e=="number"&&Number.isFinite(e),"num");async function O(e){const a=[],i=[],c=new Set;let f=0;const o=[],m=d((t,h,u)=>{if(!u||typeof u!="object")return;const{type:l,message:r}=u;if(l!=="message"||!r||r.role!=="assistant"||r.stopReason==="error")return;const n=r.timestamp;if(!p(n)||n<e.windowStartMs||n>=e.asOfMs||n<h)return;const w=r.responseId;if(typeof w=="string"&&w!==""){if(c.has(w))return;c.add(w)}const s=r.usage,y=s?.cost?.total,{provider:_,model:g}=r;if(typeof _!="string"||_===""||typeof g!="string"||g===""||!s||!p(s.input)||!p(s.output)||!p(s.cacheRead)||!p(s.cacheWrite)||!p(y)){o.push({node_id:t,at_ms:n});return}a.push({node_id:t,at_ms:n,provider:_,model:g,input:s.input,output:s.output,cache_read:s.cacheRead,cache_write:s.cacheWrite,cost:y})},"take");for(const t of e.nodes){const h=Date.parse(t.created);if(!Number.isFinite(h))throw new Error(`node ${t.node_id} has an unparseable created time: ${t.created}`);for(const u of t.transcripts){f++;let l;try{l=(await k(u)).mtimeMs}catch(n){i.push({node_id:t.node_id,reason:n.code==="ENOENT"?"file missing":"unreadable"});continue}if(l<e.windowStartMs)continue;const r=await R(u,n=>m(t.node_id,h,n));r&&i.push({node_id:t.node_id,reason:r})}}return{responses:a,transcripts:{total:f,read:f-i.length,unreadable:i},unknown:o.length,unknown_at:o}}d(O,"run");async function R(e,a){let i=!1,c="";const f=d(o=>{if(o.trim()!=="")try{a(JSON.parse(o))}catch{i=!0}},"parse");try{for await(const o of M(e,{encoding:"utf8"})){const m=(c+o).split(`
2
+ `);c=m.pop()??"";for(const t of m)f(t)}}catch{return"unreadable"}if(c.trim()!=="")try{a(JSON.parse(c))}catch{}return i?"parse error":null}d(R,"scanFile"),N?.postMessage(await O(S));
@@ -0,0 +1,37 @@
1
+ import type { AnalyticsDTO } from '../../api/dto/analytics.js';
2
+ export interface ResponseRecord {
3
+ node_id: string;
4
+ at_ms: number;
5
+ provider: string;
6
+ model: string;
7
+ input: number;
8
+ output: number;
9
+ cache_read: number;
10
+ cache_write: number;
11
+ cost: number;
12
+ }
13
+ export interface ScanInput {
14
+ nodes: Array<{
15
+ node_id: string;
16
+ created: string;
17
+ transcripts: string[];
18
+ }>;
19
+ windowStartMs: number;
20
+ asOfMs: number;
21
+ }
22
+ /** `unknown_at` lists each response with no model or usage (still a model response: it makes its agent active). */
23
+ export interface ScanResult {
24
+ responses: ResponseRecord[];
25
+ transcripts: AnalyticsDTO['transcripts'];
26
+ unknown: number;
27
+ unknown_at: Array<{
28
+ node_id: string;
29
+ at_ms: number;
30
+ }>;
31
+ }
32
+ /**
33
+ * Reads every transcript in one worker thread so the daemon's event loop never parses JSONL. At most MAX_WORKERS run
34
+ * daemon-wide; the rest queue. Aborting `signal` drops a queued scan or terminates its worker. Rejects when the worker
35
+ * fails or the scan is aborted.
36
+ */
37
+ export declare function scanResponses(input: ScanInput, signal?: AbortSignal): Promise<ScanResult>;
@@ -0,0 +1 @@
1
+ var b=Object.defineProperty;var t=(u,e)=>b(u,"name",{value:e,configurable:!0});import{Worker as v}from"node:worker_threads";const E=4;let c=0;const a=[];function k(){c--,a.shift()?.()}t(k,"release");function L(u,e){return new Promise((p,n)=>{const i=t(()=>new Error("transcript scan aborted: the analytics request was closed"),"aborted");if(e?.aborted){n(i());return}const d=t(()=>{if(e?.aborted){n(i());return}c++;const o=new v(new URL(import.meta.url.endsWith(".ts")?"./scan-worker.ts":"./scan-worker.js",import.meta.url),{workerData:u});let r=!1;const m=t(()=>{r||(r=!0,n(i()),o.terminate())},"onAbort");e?.addEventListener("abort",m,{once:!0}),o.once("message",s=>{r||(r=!0,p(s))}),o.once("error",s=>{r||(r=!0,n(s))}),o.once("exit",s=>{e?.removeEventListener("abort",m),k(),r||(r=!0,n(new Error(`transcript scan worker exited with ${s} before replying`)))})},"start");if(c<E){d();return}const f=t(()=>{e?.removeEventListener("abort",w),d()},"queued"),w=t(()=>{const o=a.indexOf(f);o>=0&&a.splice(o,1),n(i())},"dropQueued");e?.addEventListener("abort",w,{once:!0}),a.push(f)})}t(L,"scanResponses");export{L as scanResponses};
@@ -0,0 +1,11 @@
1
+ import { type IncomingMessage, type ServerResponse } from 'node:http';
2
+ import { type RequestPrincipal } from './router.js';
3
+ import type { AppPrincipal } from './principal.js';
4
+ import type { ScopeConfig } from '../../types.js';
5
+ import type { GrantSync } from '../../core/grants/sync.js';
6
+ export declare function cursor(req: IncomingMessage, query: URLSearchParams): number;
7
+ export declare function streamRunEvents(req: IncomingMessage, res: ServerResponse, id: string, principal: AppPrincipal | RequestPrincipal, after: number, requestId: string, framing: 'sse' | 'ndjson'): Promise<void>;
8
+ export declare function createAppListener(config: NonNullable<ScopeConfig['runtime']>, grantsReady: () => boolean, syncGrants: GrantSync): {
9
+ ready: Promise<void>;
10
+ close: () => Promise<void>;
11
+ };
@@ -0,0 +1,21 @@
1
+ var x=Object.defineProperty;var s=(e,r)=>x(e,"name",{value:r,configurable:!0});import{randomUUID as H}from"node:crypto";import{createServer as J}from"node:http";import{Router as P}from"./router.js";import{OPERATIONS as T}from"./operations.js";import{AppPrincipalResolver as I}from"./principal.js";import{handleRunOperation as $,integer as j}from"./handlers/runs.js";import{parseLlmRequest as z,runLlm as B}from"../../core/llm/run.js";import{RunError as i}from"../../core/runs/operations.js";import{requireRun as k}from"./router.js";import{readRunEvents as O,registerGrantStream as G,subscribeRunEvents as L}from"../../core/runs/events.js";import{activeGrant as U}from"../../core/grants/mirror.js";import{emitEvent as X}from"../../core/events/emit.js";import{handleVendorSignIn as D,isVendorSignInPath as M}from"./vendor-signin.js";const V={other_app_run:"permission_error",unauthorized:"authentication_error",grant_revoked:"authentication_error",scope_missing:"permission_error",owner_only:"permission_error",invalid_request:"invalid_request_error",not_found:"invalid_request_error",model_unavailable:"invalid_request_error",substitution_refused:"invalid_request_error",payer_not_app:"invalid_request_error",all_routes_exhausted:"rate_limit_error",idempotency_conflict:"invalid_request_error",run_settled:"invalid_request_error",stream_gap:"invalid_request_error",stream_dropped:"server_error",runtime_starting:"server_error",server_error:"server_error"};function q(e,r,n){return{error:{code:e.code,message:e.message,...e.details===void 0?{}:{details:e.details},type:V[e.code]??"api_error",...e.param?{param:e.param}:{},origin:"runtime",retryable:e.retryable||e.code==="stream_dropped",request_id:r,...n?{run_id:n}:{},...e.scope?{scope:e.scope}:{},...e.code==="unauthorized"?{user_action:"reconnect"}:{},...e.code==="all_routes_exhausted"?{retry_after_s:e.details.retry_after_s}:{}}}}s(q,"envelope");function F(e,r,n,a){const u=r instanceof i?r:new i(500,"server_error","runtime request failed");if(r instanceof i||console.error("[app-listener]",r),e.headersSent){e.end(`event: error
2
+ data: ${JSON.stringify(q(u,n,a))}
3
+
4
+ `);return}e.writeHead(u.status,{"Content-Type":"application/json; charset=utf-8","Request-Id":n,...u.code==="all_routes_exhausted"?{"Retry-After":String(u.details.retry_after_s)}:{}}),e.end(JSON.stringify(q(u,n,a)))}s(F,"fail");function E(e,r,n){e.writeHead(r,{"Content-Type":"application/json; charset=utf-8"}),e.end(JSON.stringify(n))}s(E,"json");async function A(e){const r=[];let n=0;for await(const a of e){if(n+=a.length,n>4*1024*1024)throw new i(400,"invalid_request","request body too large");r.push(a)}if(n)try{return JSON.parse(Buffer.concat(r).toString("utf8"))}catch{throw new i(400,"invalid_request","invalid JSON body")}}s(A,"bodyOf");function Y(e,r){const n=e.headers["last-event-id"]??r.get("after");if(Array.isArray(n))throw new i(400,"invalid_request","invalid cursor","after");return j(n??null,"after",0,Number.MAX_SAFE_INTEGER,0)}s(Y,"cursor");function K(e){return`id: ${e.sequence_number}
5
+ event: ${e.type}
6
+ data: ${JSON.stringify(e)}
7
+
8
+ `}s(K,"frame");function Q(e,r,n,a,u,y,c){k(a,n);let f=O(n,u);if(u>f.latest_sequence)throw new i(400,"invalid_request","cursor is beyond the run","after");if(u<f.latest_sequence&&(f.earliest_sequence===null||u<f.earliest_sequence-1))throw new i(409,"stream_gap","events have expired",void 0,{earliest_sequence:f.earliest_sequence,latest_sequence:f.latest_sequence});if(r.destroyed||e.destroyed)return r.destroy(),Promise.resolve();r.writeHead(200,{"Content-Type":c==="sse"?"text/event-stream":"application/x-ndjson","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),c==="sse"?r.write(`: connected
9
+
10
+ `):r.flushHeaders();let o=u,t=!1,d=!1,m=!1,_;const g=new Promise(p=>{_=p}),l=s(()=>{t||(t=!0,clearInterval(C),R(),N(),r.off("close",l),r.off("drain",w),r.end(),_())},"end"),w=s(()=>{m=!1,h()},"onDrain"),v=s(p=>{t||r.write(c==="sse"?`event: error
11
+ data: ${JSON.stringify(q(p,y,n))}
12
+
13
+ `:`${JSON.stringify({type:"error",...q(p,y,n)})}
14
+ `),l()},"error"),h=s(()=>{if(!(t||d)){d=!0;try{if(!S()){b();return}if(k(a,n),f=O(n,o),f.backlog_count>1e3||f.backlog_bytes>32*1024*1024){v(new i(503,"stream_dropped","stream reader fell behind",void 0,{last_sequence:o}));return}for(const p of f.events){if(m)break;m=!r.write(c==="sse"?K(p):`${JSON.stringify(p)}
15
+ `),o=p.sequence_number}}catch(p){v(p instanceof i?p:new i(500,"server_error","stream failed"))}finally{d=!1}}},"flush"),b=s(()=>v(new i(401,"grant_revoked","grant is not active")),"revoked"),S=s(()=>"class"in a||U(a.grant)!==null,"grantActive"),R=L(n,h),N="class"in a?()=>{}:G(a.grant,b),C=setInterval(()=>{!t&&!m&&c==="sse"&&r.write(`: keepalive
16
+
17
+ `)},15e3);return r.on("drain",w),r.on("close",l),h(),g}s(Q,"streamRunEvents");function fe(e,r,n){if(!e.issuer||!e.audience||!e.listener?.address||!Number.isInteger(e.listener.port)||e.listener.port<1||e.listener.port>65535)throw new Error("runtime issuer, audience and listener address/port are required");if(["0.0.0.0","::","[::]"].includes(e.listener.address))throw new Error("runtime listener must bind a specific address");const a=new I(e.issuer,e.audience,n,e.jwksUrl),u=new P,y=new Map;for(const o of T.filter(t=>t.stage!==void 0)){const t=s(()=>({status:500}),"handler");u.register({method:o.method,pattern:o.path,handler:t}),y.set(t,o)}const c=J((o,t)=>{(async()=>{const d=H();t.setHeader("Request-Id",d);let m;try{const _=new URL(o.url??"/","http://runtime.local");if(M(_.pathname)){await D(o,t,_,a);return}if((o.method!=="GET"||_.pathname!=="/v1/health")&&(!a.isReady()||!r()))throw new i(503,"runtime_starting","runtime_starting");const g=u.match(o.method??"GET",_.pathname);if(!g)throw new i(404,"not_found","unknown operation");const l=y.get(g.handler);if(!l)throw new i(404,"not_found","unknown operation");if(m=g.params.run_id,l.name==="health"){E(t,200,{status:"ok",api_version:"2026-09-23"});return}if(l.name==="runtime.sync-notice"){if(!await a.verifySyncNotice(o.headers.authorization)){t.writeHead(204),t.end();return}X({level:"info",event:"daemon.grants.sync_notice",fields:{request_id:d}});try{await n({fresh:!0})}catch{throw new i(503,"server_error","grant sync failed")}t.writeHead(204),t.end();return}const w=await a.resolve(o.headers.authorization,l.scope);if(t.setHeader("Request-Id",d),m&&k(w,m,["run.message","run.interrupt","run.cancel","run.rename","run.delete"].includes(l.name),l.name==="run.delete"),l.stream){Q(o,t,m,w,Y(o,_.searchParams),d,"sse");return}if(l.name==="llm"){const h=z(await A(o)),b=new AbortController,S=s(()=>b.abort(),"abort");t.on("close",S);try{const R=await B(h,b.signal,h.stream?N=>{t.headersSent||t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),t.write(`data: ${JSON.stringify(N)}
18
+
19
+ `)}:void 0);h.stream?(t.headersSent||t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),t.end(`data: [DONE]
20
+
21
+ `)):E(t,200,R.completion)}finally{t.off("close",S)}return}const v=await $(l,w,m,_.searchParams,await A(o),o.headers["idempotency-key"]);E(t,v.status,v.body)}catch(_){F(t,_,d,m)}})()});return{ready:new Promise((o,t)=>{c.once("error",t),c.listen(e.listener.port,e.listener.address,()=>{c.off("error",t),o()})}),close:s(async()=>{a.close(),c.closeAllConnections(),await new Promise((o,t)=>c.close(d=>d?t(d):o()))},"close")}}s(fe,"createAppListener");export{fe as createAppListener,Y as cursor,Q as streamRunEvents};
@@ -1,8 +1,8 @@
1
- var A=Object.defineProperty;var c=(r,e)=>A(r,"name",{value:e,configurable:!0});import{createConnection as E}from"node:net";import{WebSocketServer as _}from"ws";import{isSafeNodeId as g,viewSocketPath as b}from"../../core/canvas/paths.js";import{getNode as w}from"../../core/canvas/index.js";import{BROKER_READ_CAPS as S,CLIENT_READ_CAPS as $,FrameDecoder as R,FrameOverflowError as T}from"../../core/runtime/broker-protocol.js";import{classify as v}from"../../core/fault-classifier.js";import{clearFault as h,recordFault as p}from"../../core/canvas/recovery.js";import{decideAttachRevive as x}from"../../core/runtime/revive.js";import{probeViewSocket as C,waitForBrokerViewSocket as P}from"../../core/runtime/view-socket.js";const B=new _({noServer:!0,maxPayload:S.maxLineBytes}),N=25e3,O=6e4;function F(r,e=N,s=O){let t=!0,a=!1,n=Date.now();const f=c(()=>{t=!0},"acknowledge"),u=c(()=>{a||(a=!0,clearInterval(m),r.off("pong",f))},"stop");r.on("pong",f);const m=setInterval(()=>{if(Date.now()-n>s){t=!0;return}if(!t){u(),r.terminate();return}t=!1;try{r.ping()}catch{u(),r.terminate()}},e);return m.unref(),{stop:u,noteActivity:c(()=>{n=Date.now()},"noteActivity")}}c(F,"startRemoteAttachHeartbeat");function k(r){const e=Buffer.from(r,"utf8");return e.byteLength<=123?r:e.subarray(0,123).toString("utf8")}c(k,"clampReason");function d(r,e,s,t,a){const n=JSON.stringify({error:{code:t,message:a}}),f=`HTTP/1.1 ${e} ${s}\r
1
+ var E=Object.defineProperty;var s=(t,e)=>E(t,"name",{value:e,configurable:!0});import{WebSocketServer as $}from"ws";import{isSafeNodeId as A}from"../../core/canvas/paths.js";import{getNode as S}from"../../core/canvas/index.js";import{BROKER_READ_CAPS as R,CLIENT_READ_CAPS as T,FrameDecoder as B,FrameOverflowError as x}from"../../core/runtime/broker-protocol.js";import{classify as k}from"../../core/fault-classifier.js";import{clearFault as p,recordFault as g}from"../../core/canvas/recovery.js";import{decideAttachRevive as C}from"../../core/runtime/revive.js";import{connectViewSocket as N,probeViewSocket as O,waitForBrokerViewSocket as P}from"../../core/runtime/view-socket.js";import{isBeneathRefusal as F}from"../../core/spaces/open-beneath.js";import{attachViewer as _,detachViewer as U}from"./viewer-presence.js";const D=new $({noServer:!0,maxPayload:R.maxLineBytes}),L=25e3,H=6e4;function V(t,e=L,i=H){let o=!0,n=!1,a=Date.now();const f=s(()=>{o=!0},"acknowledge"),m=s(()=>{n||(n=!0,clearInterval(h),t.off("pong",f))},"stop");t.on("pong",f);const h=setInterval(()=>{if(Date.now()-a>i){o=!0;return}if(!o){m(),t.terminate();return}o=!1;try{t.ping()}catch{m(),t.terminate()}},e);return h.unref(),{stop:m,noteActivity:s(()=>{a=Date.now()},"noteActivity")}}s(V,"startRemoteAttachHeartbeat");function b(t){const e=Buffer.from(t,"utf8");return e.byteLength<=123?t:e.subarray(0,123).toString("utf8")}s(b,"clampReason");function u(t,e,i,o,n){const a=JSON.stringify({error:{code:o,message:n}}),f=`HTTP/1.1 ${e} ${i}\r
2
2
  content-type: application/json; charset=utf-8\r
3
- content-length: ${Buffer.byteLength(n)}\r
3
+ content-length: ${Buffer.byteLength(a)}\r
4
4
  connection: close\r
5
5
  \r
6
- `+n;try{r.end(f)}catch{try{r.destroy()}catch{}}}c(d,"refuseUpgrade");function U(r){if(new URL(r.url??"/","http://127.0.0.1").searchParams.get("revive")==="0")return!0;const s=r.headers["x-crtr-attach-revive"];return(Array.isArray(s)?s[0]:s)==="0"}c(U,"reviveOptedOut");function D(r){return new URL(r.url??"/","http://127.0.0.1").searchParams.get("wakeParked")==="1"}c(D,"wakeParkedRequested");function L(r,e){if(!g(e)){try{r.close(1008,k(`invalid node id: ${e}`))}catch{}return}if(w(e)===null){try{r.close(1011,k(`no node ${e}`))}catch{}return}h(e,{link:"viewer\u2194crtrd"});const s=b(e),t=E(s),a=new R($);let n=!1,f=!1,u;t.on("connect",()=>{h(e,{link:"relay\u2194broker"})});const m=c((o,l)=>{if(!n){if(n=!0,u?.stop(),!t.destroyed)try{t.destroy()}catch{}try{r.close(o,k(l))}catch{}}},"teardown");u=F(r),t.on("data",o=>{u?.noteActivity();let l;try{l=a.push(o)}catch(i){const y=i instanceof T?`broker sent an oversized frame (${i.message})`:`failed to decode a broker frame for ${e}`;m(1009,y);return}for(const i of l)r.readyState===r.OPEN&&r.send(JSON.stringify(i))}),t.on("error",o=>{const l=v("relay\u2194broker",o);p(e,{link:"relay\u2194broker",op:"bridge socket",kind:l.kind,retry:{disposition:"manual"},message:o.message});const i=o.code==="ECONNREFUSED"||o.code==="ENOENT"?`no running broker for ${e}`:`view socket error for ${e}: ${o.message}`;m(1011,i)}),t.on("close",o=>{n||o||(h(e,{link:"relay\u2194broker"}),m(1e3,"broker socket closed"))}),r.on("close",(o,l)=>{const i=l.toString("utf8");if(n){o===1e3&&h(e,{link:"viewer\u2194crtrd"});return}if(o===1e3)h(e,{link:"viewer\u2194crtrd"});else if(!f){f=!0;const y=v("viewer\u2194crtrd",{code:o,reason:i});p(e,{link:"viewer\u2194crtrd",op:"remote attach websocket",kind:y.kind,retry:{disposition:"auto"},message:i||`remote attach closed (${o})`})}m(o,i||`remote attach closed (${o})`)}),r.on("error",()=>{if(!f){f=!0;const o=v("viewer\u2194crtrd",{code:1006,reason:"remote attach websocket error"});p(e,{link:"viewer\u2194crtrd",op:"remote attach websocket",kind:o.kind,retry:{disposition:"auto"},message:"remote attach websocket error"})}m(1011,"remote attach websocket error")}),r.on("message",o=>{if(n||t.destroyed)return;u?.noteActivity();const i=(Array.isArray(o)?Buffer.concat(o):Buffer.isBuffer(o)?o:Buffer.from(new Uint8Array(o))).toString("utf8"),y=i.endsWith(`
7
- `)?i:i+`
8
- `;try{t.write(y)}catch{}})}c(L,"bridgeConnection");async function X(r,e,s,t){e.on("error",()=>{});try{if(!g(t)){d(e,400,"Bad Request","invalid_node_id",`invalid node id: ${t}`);return}if(w(t)===null){d(e,404,"Not Found","node_not_found",`no node ${t}`);return}if(U(r)){if(!await C(b(t),250)){d(e,409,"Conflict","node_dormant",`node ${t} is dormant and revive was opted out (?revive=0)`);return}}else{let a;try{const n=await x(t,{resume:!0,capacity:"refuse"},D(r));if(n.parked){d(e,409,"Conflict","node_dormant",`node ${t} is parked; attach with ?wakeParked=1 to wake it`);return}a=n.result.launch?.exited}catch(n){const f=n instanceof Error?n.message:String(n);d(e,409,"Conflict","attach_revive_failed",`attach revive failed: ${f}`);return}if(!await P(t,a)){d(e,409,"Conflict","broker_not_ready",`broker view socket for ${t} did not become listenable after revive`);return}}if(e.destroyed)return;B.handleUpgrade(r,e,s,a=>L(a,t))}catch(a){const n=a instanceof Error?a.message:String(a);d(e,500,"Internal Server Error","internal",`attach upgrade failed: ${n}`)}}c(X,"handleAttachUpgrade");export{N as REMOTE_ATTACH_HEARTBEAT_MS,O as REMOTE_ATTACH_QUIET_MS,X as handleAttachUpgrade,d as refuseUpgrade,F as startRemoteAttachHeartbeat};
6
+ `+a;try{t.end(f)}catch{try{t.destroy()}catch{}}}s(u,"refuseUpgrade");function M(t){if(new URL(t.url??"/","http://127.0.0.1").searchParams.get("revive")==="0")return!0;const i=t.headers["x-crtr-attach-revive"];return(Array.isArray(i)?i[0]:i)==="0"}s(M,"reviveOptedOut");function J(t){return new URL(t.url??"/","http://127.0.0.1").searchParams.get("wakeParked")==="1"}s(J,"wakeParkedRequested");function W(t,e){if(!A(e)){try{t.close(1008,b(`invalid node id: ${e}`))}catch{}return}if(S(e)===null){try{t.close(1011,b(`no node ${e}`))}catch{}return}p(e,{link:"viewer\u2194crtrd"});const i=N(e),o=new B(T);let n=!1,a=!1,f,m=!1,h=!1,v=!1;i.on("connect",()=>{m=!0,!n&&v&&_(e,t),p(e,{link:"relay\u2194broker"})});const y=s((r,l)=>{if(!n){if(n=!0,U(e,t),f?.stop(),!i.destroyed)try{i.destroy()}catch{}try{t.close(r,b(l))}catch{}}},"teardown");f=V(t),i.on("data",r=>{f?.noteActivity();let l;try{l=o.push(r)}catch(c){const d=c instanceof x?`broker sent an oversized frame (${c.message})`:`failed to decode a broker frame for ${e}`;y(1009,d);return}for(const c of l)t.readyState===t.OPEN&&t.send(JSON.stringify(c))}),i.on("error",r=>{const l=k("relay\u2194broker",r);g(e,{link:"relay\u2194broker",op:"bridge socket",kind:l.kind,retry:{disposition:"manual"},message:r.message});const c=r.code==="ECONNREFUSED"||r.code==="ENOENT"?`no running broker for ${e}`:`view socket error for ${e}: ${r.message}`;y(1011,c)}),i.on("close",r=>{n||r||(p(e,{link:"relay\u2194broker"}),y(1e3,"broker socket closed"))}),t.on("close",(r,l)=>{const c=l.toString("utf8");if(n){r===1e3&&p(e,{link:"viewer\u2194crtrd"});return}if(r===1e3)p(e,{link:"viewer\u2194crtrd"});else if(!a){a=!0;const d=k("viewer\u2194crtrd",{code:r,reason:c});g(e,{link:"viewer\u2194crtrd",op:"remote attach websocket",kind:d.kind,retry:{disposition:"auto"},message:c||`remote attach closed (${r})`})}y(r,c||`remote attach closed (${r})`)}),t.on("error",()=>{if(!a){a=!0;const r=k("viewer\u2194crtrd",{code:1006,reason:"remote attach websocket error"});g(e,{link:"viewer\u2194crtrd",op:"remote attach websocket",kind:r.kind,retry:{disposition:"auto"},message:"remote attach websocket error"})}y(1011,"remote attach websocket error")}),t.on("message",r=>{if(n||i.destroyed)return;f?.noteActivity();const c=(Array.isArray(r)?Buffer.concat(r):Buffer.isBuffer(r)?r:Buffer.from(new Uint8Array(r))).toString("utf8");if(!h)try{const w=JSON.parse(c);w?.type==="hello"&&(h=!0,v=w.attends!==!1,v&&m&&_(e,t))}catch{}const d=c.endsWith(`
7
+ `)?c:c+`
8
+ `;try{i.write(d)}catch{}})}s(W,"bridgeConnection");async function te(t,e,i,o){e.on("error",()=>{});try{if(!A(o)){u(e,400,"Bad Request","invalid_node_id",`invalid node id: ${o}`);return}if(S(o)===null){u(e,404,"Not Found","node_not_found",`no node ${o}`);return}if(M(t)){if(!await O(o,250)){u(e,409,"Conflict","node_dormant",`node ${o} is dormant and revive was opted out (?revive=0)`);return}}else{let n;try{const a=await C(o,{resume:!0,capacity:"refuse"},J(t));if(a.parked){u(e,409,"Conflict","node_dormant",`node ${o} is parked; attach with ?wakeParked=1 to wake it`);return}n=a.result.launch?.exited}catch(a){const f=a instanceof Error?a.message:String(a);u(e,409,"Conflict","attach_revive_failed",`attach revive failed: ${f}`);return}if(!await P(o,n)){u(e,409,"Conflict","broker_not_ready",`broker view socket for ${o} did not become listenable after revive`);return}}if(e.destroyed)return;D.handleUpgrade(t,e,i,n=>W(n,o))}catch(n){const a=n instanceof Error?n.message:String(n);if(F(n)){u(e,400,"Bad Request","invalid_request",a);return}u(e,500,"Internal Server Error","internal",`attach upgrade failed: ${a}`)}}s(te,"handleAttachUpgrade");export{L as REMOTE_ATTACH_HEARTBEAT_MS,H as REMOTE_ATTACH_QUIET_MS,te as handleAttachUpgrade,u as refuseUpgrade,V as startRemoteAttachHeartbeat};
@@ -0,0 +1,2 @@
1
+ import type { RouteTable } from '../router.js';
2
+ export declare const analyticsRoutes: RouteTable;
@@ -0,0 +1 @@
1
+ var u=Object.defineProperty;var n=(e,o)=>u(e,"name",{value:o,configurable:!0});import{listNodes as _}from"../../../core/canvas/canvas.js";import{openDb as l}from"../../../core/canvas/db.js";import{listMemoryReads as y,memoryReadsStartedAt as h}from"../../../core/canvas/memory-reads.js";import{usage as r}from"../../../core/errors.js";import{computeAnalytics as g,WINDOW_MS as b}from"../../analytics/compute.js";import{scanResponses as O}from"../../analytics/scan.js";const R=new Set(["as_of","window","project"]);async function S(e){for(const[t]of e.query)if(!R.has(t))throw r(`unknown analytics query parameter: ${t}`);const o=e.query.get("as_of")??"",i=Date.parse(o);if(!Number.isFinite(i))throw r(`as_of must be an ISO 8601 time, got "${o}"`);const a=e.query.get("window")??"";if(!Object.hasOwn(b,a))throw r(`window must be 24h or 7d, got "${a}"`);const d=e.query.get("project");if(d==="")throw r("project must be a non-empty directory path when given");const s=new Map,p=l().prepare("SELECT node_id, transcript_path FROM sessions WHERE transcript_path IS NOT NULL").all();for(const t of p)s.set(t.node_id,(s.get(t.node_id)??new Set).add(t.transcript_path));const m=new Map(l().prepare("SELECT node_id, title FROM node_rows WHERE title IS NOT NULL").all().map(t=>[t.node_id,t.title])),w=_().filter(t=>t.kind!=="human").map(t=>({node_id:t.node_id,name:t.name,title:m.get(t.node_id)??null,kind:t.kind,parent:t.parent??null,cwd:t.cwd,created:t.created,finalized_at:t.finalized_at??null,final_report:t.final_report??null,lifecycle:t.lifecycle,transcripts:[...s.get(t.node_id)??[]]})),c=new AbortController;return e.res.once("close",()=>c.abort()),{status:200,body:await g({asOfMs:i,window:a,project:d??null,agents:w,scan:n(t=>O(t,c.signal),"scan"),memory:n((t,f)=>({rows:y(t,f),startedAt:h()}),"memory")})}}n(S,"handleAnalytics");const k=[{method:"GET",pattern:"/v1/canvas/analytics",handler:S}];export{k as analyticsRoutes};
@@ -1 +1 @@
1
- var c=Object.defineProperty;var u=(t,e)=>c(t,"name",{value:e,configurable:!0});import{getNode as m}from"../../../core/canvas/canvas.js";import{decideAttachRevive as k}from"../../../core/runtime/revive.js";import{viewSocketPath as l}from"../../../core/canvas/paths.js";import{waitForBrokerViewSocket as v}from"../../../core/runtime/placement-tmux.js";import{notFound as p,usage as n}from"../../../core/errors.js";import{ownerOnly as w}from"../router.js";function h(t){if(t==null)return{resume:!0,revive:!0,wakeParked:!0};if(typeof t!="object")throw n("attach body must be a JSON object { resume?, revive?, wakeParked? }");const e=t.resume,r=t.revive,o=t.wakeParked;if(e!==void 0&&typeof e!="boolean")throw n("resume must be a boolean");if(r!==void 0&&typeof r!="boolean")throw n("revive must be a boolean");if(o!==void 0&&typeof o!="boolean")throw n("wakeParked must be a boolean");return{resume:e??!0,revive:r??!0,wakeParked:o??!0}}u(h,"parseAttachBody");async function b(t){const e=t.params.id;if(m(e)===null)throw p(`unknown node: ${e}`,{received:e});const{resume:r,revive:o,wakeParked:f}=h(t.body);let i=!1,d=!1,s=!1;if(o){const a=await k(e,{resume:r,capacity:"refuse"},f);s=a.parked,a.parked||(i=a.result.launch!==void 0,d=a.result.resumed,i&&await v(e))}return{status:200,body:{node_id:e,socket_path:l(e),revived:i,resumed:d,parked:s}}}u(b,"handleAttach");const F=w([{method:"POST",pattern:"/v1/nodes/:id/attach",handler:b}]);export{F as attachRoutes};
1
+ var c=Object.defineProperty;var i=(t,e)=>c(t,"name",{value:e,configurable:!0});import{getNode as m}from"../../../core/canvas/canvas.js";import{decideAttachRevive as k}from"../../../core/runtime/revive.js";import{viewSocketPath as l}from"../../../core/canvas/paths.js";import{waitForBrokerViewSocket as v}from"../../../core/runtime/placement-tmux.js";import{notFound as p,usage as n}from"../../../core/errors.js";import{ownerOnly as w}from"../router.js";function h(t){if(t==null)return{resume:!0,revive:!0,wakeParked:!0};if(typeof t!="object")throw n("attach body must be a JSON object { resume?, revive?, wakeParked? }");const e=t.resume,r=t.revive,o=t.wakeParked;if(e!==void 0&&typeof e!="boolean")throw n("resume must be a boolean");if(r!==void 0&&typeof r!="boolean")throw n("revive must be a boolean");if(o!==void 0&&typeof o!="boolean")throw n("wakeParked must be a boolean");return{resume:e??!0,revive:r??!0,wakeParked:o??!0}}i(h,"parseAttachBody");async function b(t){const e=t.params.id;if(m(e)===null)throw p(`unknown node: ${e}`,{received:e});const{resume:r,revive:o,wakeParked:f}=h(t.body);let u=!1,d=!1,s=!1;if(o){const a=await k(e,{resume:r,capacity:"refuse"},f);s=a.parked,a.parked||(u=a.result.launch!==void 0,d=a.result.resumed,await v(e))}return{status:200,body:{node_id:e,socket_path:l(e),revived:u,resumed:d,parked:s}}}i(b,"handleAttach");const F=w([{method:"POST",pattern:"/v1/nodes/:id/attach",handler:b}]);export{F as attachRoutes};
@@ -1,2 +1,2 @@
1
- var c=Object.defineProperty;var a=(d,o)=>c(d,"name",{value:o,configurable:!0});import{enqueueNodeMail as f}from"../../../core/feed/messages.js";import{contextDir as b,isSafeNodeId as m}from"../../../core/canvas/paths.js";import{getNode as p}from"../../../core/canvas/canvas.js";import{isBrokerLive as h}from"../../../core/runtime/model-swap.js";import{assertNotFinalized as g}from"../../../core/runtime/reopen.js";import{reviveNode as w}from"../../../core/runtime/revive.js";import{activeBackgroundBashJobs as _,bashJobPaths as j,isBashJobProcessGroupAlive as I,readJobPgid as v,isSafeBashJobId as k,stopBackgroundBashJob as y}from"../../../core/bash-jobs.js";import{ApiError as s}from"../../../api/index.js";import{notFound as J}from"../../../core/errors.js";function l(d){const o=d.params.id;if(o===void 0||!m(o))throw new s(400,"invalid_node_id",`invalid node id: ${JSON.stringify(o)}`);if(p(o)===null)throw J(`unknown node: ${o}`,{received:o});const n=d.params.jobId;if(n!==void 0){if(!k(n))throw new s(400,"invalid_job_id",`invalid background job id: ${JSON.stringify(n)}`);return{nodeId:o,jobId:n,dir:b(o)}}return{nodeId:o,dir:b(o)}}a(l,"requireTarget");function $(d){const{nodeId:o,dir:e}=l(d),n=Date.now();return{status:200,body:_(e).map(t=>{const i=v(e,t.jobId)??null;return{job_id:t.jobId,command:t.command,purpose:t.purpose,started_at:new Date(t.startedAtMs).toISOString(),elapsed_ms:Math.max(0,n-t.startedAtMs),pgid:i,pgid_alive:i!==null&&I(i)}})}}a($,"handleList");async function B(d){const{nodeId:o,jobId:e,dir:n}=l(d);if(e===void 0)throw new s(400,"invalid_request","a job id is required");const r=await y(n,e);if(r.kind==="missing")throw new s(404,"job_not_found",`no background job ${e} for ${o}`);if(r.kind==="retired")throw new s(404,"job_not_found",`background job ${e} for ${o} is no longer running`,{log_path:r.logPath});if(r.kind==="not-stoppable")throw new s(409,"job_not_stoppable",`job ${e} recorded no trusted process identity`,{log_path:r.logPath});g(o);const t=j(n,e),i=`Background bash job ${e} was stopped by the user before it finished. Log: ${t.jobLog} Command: ${t.cmdSh}`;f(o,{senderNodeId:null,urgency:"urgent",wake:1,kind:"message",label:i.split(`
2
- `)[0].slice(0,120),bodyJson:{body:i}});const u=p(o);if(u!==null&&!h(u))try{await w(o,{resume:!0,capacity:"freeze"})}catch{}return{status:200,body:{node_id:o,job_id:e,signaled:r.signaled}}}a(B,"handleStop");const x=[{method:"GET",pattern:"/v1/nodes/:id/jobs",handler:$},{method:"DELETE",pattern:"/v1/nodes/:id/jobs/:jobId",handler:B}];export{x as bashJobRoutes};
1
+ var I=Object.defineProperty;var l=(t,o)=>I(t,"name",{value:o,configurable:!0});import{enqueueNodeMail as S}from"../../../core/feed/messages.js";import{join as k}from"node:path";import{createFileBeneath as f,existsBeneath as B,unlinkBeneath as E}from"../../../core/spaces/open-beneath.js";import{launchAppProcess as J,appProcessSandboxed as A}from"../../../core/spaces/process.js";import{BASH_JOB_SUPERVISOR_SCRIPT as P}from"../../../core/bash-job-supervisor.js";import{resolveAdmittedHostCommand as $}from"../../../core/runtime/spawn-env.js";import{requireCurrentExecution as w}from"./broker-recovery.js";import{isSafeNodeId as N}from"../../../core/canvas/paths.js";import{contextDir as y}from"../../../core/canvas/node-agent-paths.js";import{getNode as m}from"../../../core/canvas/canvas.js";import{isBrokerLive as q}from"../../../core/runtime/model-swap.js";import{assertNotFinalized as O}from"../../../core/runtime/reopen.js";import{reviveNode as L}from"../../../core/runtime/revive.js";import{activeBackgroundBashJobs as T,bashJobPaths as j,isBashJobProcessGroupAlive as R,readJobPgid as C,readJobPgidIdentity as D,isSafeBashJobId as _,readBashNiceness as x,stopBackgroundBashJob as z}from"../../../core/bash-jobs.js";import{ApiError as d}from"../../../api/index.js";import{notFound as M}from"../../../core/errors.js";function g(t){const o=t.params.id;if(o===void 0||!N(o))throw new d(400,"invalid_node_id",`invalid node id: ${JSON.stringify(o)}`);if(m(o)===null)throw M(`unknown node: ${o}`,{received:o});const i=t.params.jobId;if(i!==void 0){if(!_(i))throw new d(400,"invalid_job_id",`invalid background job id: ${JSON.stringify(i)}`);return{nodeId:o,jobId:i,dir:y(o)}}return{nodeId:o,dir:y(o)}}l(g,"requireTarget");async function F(t){const{nodeId:o,dir:n}=g(t);if(t.principal.class!=="broker"||t.principal.node!==o)throw new d(403,"permission_error","only this node\u2019s broker may start a job");const i=t.body;if(!i||typeof i!="object"||Array.isArray(i))throw new d(400,"invalid_request","job.start requires a JSON body");const c=i;w(o,c.expected_execution_id);const e=c.job_id,a=c.cwd,b=c.env;if(typeof e!="string"||!_(e)||typeof a!="string"||!b||typeof b!="object"||Array.isArray(b)||Object.entries(b).some(([s,p])=>!/^[A-Za-z_][A-Za-z0-9_]*$/.test(s)||typeof p!="string"))throw new d(400,"invalid_request","invalid job id, cwd or environment");if(!A())throw new d(400,"invalid_request","job.start is only used with the root launcher");const r=j(n,e);if(![r.jobRun,r.cmdSh,r.jobLog].every(s=>B(r.root,s)))throw new d(404,"job_not_found","job was not prepared by this node");const h=k(r.dir,"job.starting");try{f(r.root,h,"")}catch(s){throw s.code==="EEXIST"?new d(409,"job_already_started","job was already started"):s}let u;try{const s=b,p=$("bash",s,a),v=m(o);return u=await J({nodeId:o,app:v.grantee??"app:terminal",cwd:a,env:s,capture:!1,command:[p,"-c",P,"--",r.cmdSh,r.jobLog,r.jobExit,r.jobRun,r.jobDone,r.jobBg,e,o,p,String(x(n)),process.execPath,"daemon"]}),f(r.root,r.jobPgid,String(u.pid),384),f(r.root,r.jobPgidIdentity,`launcher:${u.pid}`,384),{status:201,body:{pid:u.pid}}}catch(s){if(u!==void 0)try{await u.stop()}catch(p){throw new AggregateError([s,p],`job ${e} launched but could not be recorded or stopped`)}try{E(r.root,h)}catch(p){if(p.code!=="ENOENT")throw p}throw s}}l(F,"handleStart");function G(t){const{nodeId:o,dir:n}=g(t),i=Date.now();return{status:200,body:T(n).map(e=>{const a=C(n,e.jobId)??null;return{job_id:e.jobId,command:e.command,purpose:e.purpose,started_at:new Date(e.startedAtMs).toISOString(),elapsed_ms:Math.max(0,i-e.startedAtMs),pgid:a,pgid_alive:a!==null&&R(a,D(n,e.jobId))}})}}l(G,"handleList");async function H(t){const{nodeId:o,jobId:n,dir:i}=g(t);if(n===void 0)throw new d(400,"invalid_request","a job id is required");const c=t.query.get("foreground")==="1";if(c){if(t.principal.class!=="broker"||t.principal.node!==o)throw new d(403,"permission_error","only this node\u2019s broker may abort its foreground job");w(o,t.body?.expected_execution_id)}const e=await z(i,n,c,o);if(e.kind==="missing")throw new d(404,"job_not_found",`no background job ${n} for ${o}`);if(e.kind==="retired")throw new d(404,"job_not_found",`background job ${n} for ${o} is no longer running`,{log_path:e.logPath});if(e.kind==="not-stoppable")throw new d(409,"job_not_stoppable",`job ${n} recorded no trusted process identity`,{log_path:e.logPath});if(c)return{status:200,body:{node_id:o,job_id:n,signaled:e.signaled}};O(o);const a=j(i,n),b=`Background bash job ${n} was stopped by the user before it finished. Log: ${a.jobLog} Command: ${a.cmdSh}`;S(o,{senderNodeId:null,urgency:"urgent",wake:1,kind:"message",label:b.split(`
2
+ `)[0].slice(0,120),bodyJson:{body:b}});const r=m(o);if(r!==null&&!q(r))try{await L(o,{resume:!0,capacity:"freeze"})}catch{}return{status:200,body:{node_id:o,job_id:n,signaled:e.signaled}}}l(H,"handleStop");const po=[{method:"POST",pattern:"/v1/nodes/:id/jobs/start",handler:F},{method:"GET",pattern:"/v1/nodes/:id/jobs",handler:G},{method:"DELETE",pattern:"/v1/nodes/:id/jobs/:jobId",handler:H}];export{po as bashJobRoutes};
@@ -1 +1 @@
1
- var m=Object.defineProperty;var d=(r,t)=>m(r,"name",{value:t,configurable:!0});import{statSync as u}from"node:fs";import{isAbsolute as f}from"node:path";import{ApiError as a}from"../../../api/index.js";import{buildOperationalEnvBase as l}from"../../../core/runtime/spawn-env.js";import{runCommandOnce as p}from"../../../core/runtime/run-command.js";import{readProfileEnvVars as w}from"../../../core/profiles/env-store.js";import{usage as s}from"../../../core/errors.js";const _=60,c=600,y=256*1024;function b(r){if(typeof r!="object"||r===null||Array.isArray(r))throw s("bash requires an object body");const t=r;if(typeof t.command!="string"||t.command==="")throw s("bash requires a non-empty `command`");if(typeof t.cwd!="string"||!f(t.cwd))throw new a(404,"cwd_not_found","cwd must name an absolute directory");try{if(!u(t.cwd).isDirectory())throw new Error("not a directory")}catch{throw new a(404,"cwd_not_found",`cwd not found: ${t.cwd}`)}const e=t.timeout_s??_;if(typeof e!="number"||!Number.isFinite(e)||e<=0||e>c)throw new a(422,"invalid_timeout",`timeout_s must be greater than 0 and at most ${c}`);if(t.profile!==void 0&&typeof t.profile!="string")throw s("profile must be a string");let n;if(t.env!==void 0){if(typeof t.env!="object"||t.env===null||Array.isArray(t.env))throw s("env must be an object of string values");n={};for(const[i,o]of Object.entries(t.env)){if(typeof o!="string")throw s("env must be an object of string values");n[i]=o}}return{command:t.command,cwd:t.cwd,timeout_s:e,env:n,profile:t.profile}}d(b,"parseRequest");async function T(r){const t=b(r.body),e=t.profile??null,n=l({targetCwd:t.cwd,targetProfileId:e}),i={...n,...w(e),...t.env,...e===null?{}:{CRTR_PROFILE_ID:e},CRTR_ADMITTED_HOST_PATH:n.CRTR_ADMITTED_HOST_PATH},o=await p({command:t.command,cwd:t.cwd,env:i,timeoutMs:t.timeout_s*1e3,headBytes:y}).completion;if(o.processError!==null)throw o.processError;return{status:200,body:{exit_code:o.timedOut?null:o.exitCode,signal:o.signal,stdout:o.stdout,stdout_truncated:o.stdoutTruncated,stderr:o.stderr,stderr_truncated:o.stderrTruncated,timed_out:o.timedOut,duration_ms:o.durationMs}}}d(T,"handleBash");const C=[{method:"POST",pattern:"/v1/bash",scope:"act",handler:T}];export{C as bashRoutes};
1
+ var m=Object.defineProperty;var c=(o,t)=>m(o,"name",{value:t,configurable:!0});import{statSync as u}from"node:fs";import{isAbsolute as l}from"node:path";import{ApiError as d}from"../../../api/index.js";import{buildOperationalEnvBase as f}from"../../../core/runtime/spawn-env.js";import{runAppCommand as w}from"../../../core/runtime/run-command.js";import{readProfileEnvVars as _}from"../../../core/profiles/env-store.js";import{usage as i}from"../../../core/errors.js";const b=60,p=600,y=256*1024;function h(o){if(typeof o!="object"||o===null||Array.isArray(o))throw i("bash requires an object body");const t=o;if(typeof t.command!="string"||t.command==="")throw i("bash requires a non-empty `command`");if(typeof t.cwd!="string"||!l(t.cwd))throw new d(404,"cwd_not_found","cwd must name an absolute directory");try{if(!u(t.cwd).isDirectory())throw new Error("not a directory")}catch{throw new d(404,"cwd_not_found",`cwd not found: ${t.cwd}`)}const r=t.timeout_s??b;if(typeof r!="number"||!Number.isFinite(r)||r<=0||r>p)throw new d(422,"invalid_timeout",`timeout_s must be greater than 0 and at most ${p}`);if(t.profile!==void 0&&typeof t.profile!="string")throw i("profile must be a string");let n;if(t.env!==void 0){if(typeof t.env!="object"||t.env===null||Array.isArray(t.env))throw i("env must be an object of string values");n={};for(const[s,a]of Object.entries(t.env)){if(typeof a!="string")throw i("env must be an object of string values");n[s]=a}}return{command:t.command,cwd:t.cwd,timeout_s:r,env:n,profile:t.profile}}c(h,"parseRequest");async function T(o){const t=h(o.body),r=o.principal.class==="person"?"app:terminal":o.principal.app;if(r!=="app:terminal"&&t.profile!==void 0)throw new d(403,"permission_error","app bash cannot select an owner profile");const n=r==="app:terminal"?t.profile??null:null,s=f({targetCwd:t.cwd,targetProfileId:n,skipSpawnAllow:r!=="app:terminal"}),a={...s,...r==="app:terminal"?_(n):{},...t.env,...n===null?{}:{CRTR_PROFILE_ID:n},CRTR_ADMITTED_HOST_PATH:s.CRTR_ADMITTED_HOST_PATH},e=await(await w({app:r,...o.principal.class==="broker"?{nodeId:o.principal.node}:{},command:t.command,cwd:t.cwd,env:a,timeoutMs:t.timeout_s*1e3,headBytes:y})).completion;if(e.processError!==null)throw e.processError;return{status:200,body:{exit_code:e.timedOut?null:e.exitCode,signal:e.signal,stdout:e.stdout,stdout_truncated:e.stdoutTruncated,stderr:e.stderr,stderr_truncated:e.stderrTruncated,timed_out:e.timedOut,duration_ms:e.durationMs}}}c(T,"handleBash");const C=[{method:"POST",pattern:"/v1/bash",scope:"crtr:act",handler:T}];export{C as bashRoutes};