@north-light/cli 0.3.560 → 0.3.562

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 (573) hide show
  1. package/dist/api/client.d.ts +27 -4
  2. package/dist/api/client.js +3 -3
  3. package/dist/api/contract.d.ts +1 -1
  4. package/dist/api/contract.js +1 -1
  5. package/dist/api/dto/bash-jobs.d.ts +5 -1
  6. package/dist/api/dto/broker-ops.d.ts +25 -1
  7. package/dist/api/dto/charges.d.ts +9 -2
  8. package/dist/api/dto/delivery.d.ts +26 -2
  9. package/dist/api/dto/execution-start.d.ts +57 -0
  10. package/dist/api/dto/execution-start.js +0 -0
  11. package/dist/api/dto/modelauth.d.ts +5 -0
  12. package/dist/api/dto/node-records.d.ts +16 -0
  13. package/dist/api/dto/objects.d.ts +1 -1
  14. package/dist/api/dto/reports.d.ts +0 -3
  15. package/dist/api/dto/run-events.d.ts +7 -1
  16. package/dist/api/dto/settings.d.ts +55 -0
  17. package/dist/api/dto/settings.js +0 -0
  18. package/dist/api/index.d.ts +2 -0
  19. package/dist/api/index.js +1 -1
  20. package/dist/api/routes.d.ts +11 -0
  21. package/dist/api/routes.js +1 -1
  22. package/dist/builtin-memory/crouter-sdk/release-notes.md +4 -0
  23. package/dist/cli.js +7 -7
  24. package/dist/clients/attach/chrome/bash-jobs.d.ts +11 -1
  25. package/dist/clients/attach/chrome/bash-jobs.js +1 -1
  26. package/dist/clients/attach/chrome/canvas-panels.d.ts +1 -1
  27. package/dist/clients/attach/chrome/roster.js +6 -4
  28. package/dist/clients/attach/overlays/auth.js +2 -2
  29. package/dist/clients/attach/render/chat-view.js +1 -1
  30. package/dist/clients/attach/viewer.js +550 -567
  31. package/dist/commands/canvas-config.js +2 -2
  32. package/dist/commands/canvas-use.js +1 -1
  33. package/dist/commands/human.js +2 -2
  34. package/dist/commands/node/bash.js +3 -3
  35. package/dist/commands/node/create.js +2 -2
  36. package/dist/commands/person-settings.d.ts +1 -0
  37. package/dist/commands/person-settings.js +1 -0
  38. package/dist/commands/pkg/market-manage.d.ts +2 -2
  39. package/dist/commands/pkg/market-manage.js +1 -1
  40. package/dist/commands/pkg/plugin-manage.js +13 -13
  41. package/dist/commands/pkg/shared.js +1 -1
  42. package/dist/commands/profile/default.js +1 -1
  43. package/dist/commands/push.js +2 -3
  44. package/dist/commands/sys/config.js +1 -1
  45. package/dist/commands/sys/connect.js +1 -1
  46. package/dist/commands/sys/context/sysprompt.js +2 -2
  47. package/dist/commands/sys/doctor.js +1 -1
  48. package/dist/commands/sys/panels/broker-limits-panel.js +1 -1
  49. package/dist/commands/sys/panels/keybindings-panel.js +1 -1
  50. package/dist/commands/sys/panels/models-panel.d.ts +8 -2
  51. package/dist/commands/sys/panels/models-panel.js +1 -1
  52. package/dist/commands/sys/panels/prefs-panel.d.ts +1 -0
  53. package/dist/commands/sys/panels/prefs-panel.js +1 -1
  54. package/dist/commands/sys/panels/profiles-panel.js +1 -1
  55. package/dist/commands/sys/panels/provider-panel.d.ts +4 -0
  56. package/dist/commands/sys/panels/provider-panel.js +1 -1
  57. package/dist/commands/sys/provider-login.js +5 -5
  58. package/dist/commands/sys/setup-core.js +3 -3
  59. package/dist/commands/sys/setup-settings-runner.js +1 -1
  60. package/dist/commands/sys/spend.js +3 -3
  61. package/dist/commands/sys/update.js +1 -1
  62. package/dist/core/auth-file.d.ts +11 -19
  63. package/dist/core/auth-file.js +1 -2
  64. package/dist/core/auto-update.d.ts +1 -1
  65. package/dist/core/auto-update.js +4 -4
  66. package/dist/core/bash-jobs.d.ts +29 -71
  67. package/dist/core/bash-jobs.js +9 -10
  68. package/dist/core/blobs/directory.d.ts +3 -0
  69. package/dist/core/blobs/directory.js +1 -0
  70. package/dist/core/blobs/fault.d.ts +3 -0
  71. package/dist/core/blobs/fault.js +1 -0
  72. package/dist/core/blobs/index.d.ts +21 -0
  73. package/dist/core/blobs/index.js +1 -0
  74. package/dist/core/blobs/r2.d.ts +9 -0
  75. package/dist/core/blobs/r2.js +4 -0
  76. package/dist/core/bootstrap.d.ts +1 -1
  77. package/dist/core/bootstrap.js +2 -2
  78. package/dist/core/canvas/canvas.d.ts +9 -7
  79. package/dist/core/canvas/canvas.js +8 -8
  80. package/dist/core/canvas/crons.d.ts +3 -2
  81. package/dist/core/canvas/crons.js +3 -3
  82. package/dist/core/canvas/daemon-owner.d.ts +5 -0
  83. package/dist/core/canvas/daemon-owner.js +1 -1
  84. package/dist/core/canvas/db.d.ts +10 -9
  85. package/dist/core/canvas/db.js +4 -4
  86. package/dist/core/canvas/executions.d.ts +100 -4
  87. package/dist/core/canvas/executions.js +1 -1
  88. package/dist/core/canvas/export-sqlite.js +1 -1
  89. package/dist/core/canvas/focuses.d.ts +4 -0
  90. package/dist/core/canvas/focuses.js +1 -1
  91. package/dist/core/canvas/history.js +9 -9
  92. package/dist/core/canvas/managed-worktree-sweep-candidates.d.ts +2 -1
  93. package/dist/core/canvas/managed-worktree-sweep-candidates.js +3 -2
  94. package/dist/core/canvas/node-tokens.d.ts +1 -4
  95. package/dist/core/canvas/node-tokens.js +3 -3
  96. package/dist/core/canvas/paths.d.ts +0 -1
  97. package/dist/core/canvas/paths.js +1 -1
  98. package/dist/core/canvas/person-state.d.ts +26 -0
  99. package/dist/core/canvas/person-state.js +1 -0
  100. package/dist/core/canvas/person.d.ts +3 -10
  101. package/dist/core/canvas/person.js +7 -7
  102. package/dist/core/canvas/reconciler-leases.d.ts +12 -0
  103. package/dist/core/canvas/reconciler-leases.js +8 -0
  104. package/dist/core/canvas/recovery.d.ts +4 -0
  105. package/dist/core/canvas/recovery.js +1 -1
  106. package/dist/core/canvas/schema-pg/003-fleet-removals.sql +1 -1
  107. package/dist/core/canvas/schema-pg/005-fleet-blobs.sql +13 -0
  108. package/dist/core/canvas/schema-pg/006-job-rows.sql +20 -0
  109. package/dist/core/canvas/schema-pg/007-ledger-check.sql +117 -0
  110. package/dist/core/canvas/schema-version.d.ts +1 -1
  111. package/dist/core/canvas/schema-version.js +1 -1
  112. package/dist/core/canvas/sessions.d.ts +8 -11
  113. package/dist/core/canvas/sessions.js +1 -4
  114. package/dist/core/canvas/tree.d.ts +4 -2
  115. package/dist/core/canvas/tree.js +6 -6
  116. package/dist/core/canvas/types.d.ts +7 -0
  117. package/dist/core/command-hooks/transport/exec-lifecycle.js +1 -1
  118. package/dist/core/command-plugins/bundle.d.ts +1 -1
  119. package/dist/core/command-plugins/bundle.js +1 -1
  120. package/dist/core/command-plugins/compose.d.ts +1 -1
  121. package/dist/core/command-plugins/discovery.d.ts +0 -1
  122. package/dist/core/command-plugins/discovery.js +1 -1
  123. package/dist/core/command-plugins/help-addenda.js +1 -1
  124. package/dist/core/command-plugins/presence.d.ts +2 -0
  125. package/dist/core/command-plugins/presence.js +1 -1
  126. package/dist/core/config.d.ts +16 -15
  127. package/dist/core/config.js +1 -1
  128. package/dist/core/conversation-store/client-worker.js +1 -1
  129. package/dist/core/conversation-store/client.js +2 -2
  130. package/dist/core/conversation-store/index.d.ts +9 -11
  131. package/dist/core/conversation-store/index.js +1 -1
  132. package/dist/core/conversation-store/patch.js +1 -1
  133. package/dist/core/conversation-store/row-store.d.ts +14 -0
  134. package/dist/core/conversation-store/row-store.js +13 -0
  135. package/dist/core/directory/join.js +1 -1
  136. package/dist/core/env-name.d.ts +1 -0
  137. package/dist/core/env-name.js +1 -1
  138. package/dist/core/environment-recipe.d.ts +15 -0
  139. package/dist/core/environment-recipe.js +1 -0
  140. package/dist/core/environments.js +1 -1
  141. package/dist/core/events/emit.js +2 -2
  142. package/dist/core/events/run-event-chunks.d.ts +25 -0
  143. package/dist/core/events/run-event-chunks.js +5 -0
  144. package/dist/core/feed/messages.js +5 -5
  145. package/dist/core/grants/mirror.d.ts +15 -0
  146. package/dist/core/grants/mirror.js +15 -3
  147. package/dist/core/grants/on-sync.d.ts +9 -5
  148. package/dist/core/grants/on-sync.js +1 -1
  149. package/dist/core/grants/remove.d.ts +1 -6
  150. package/dist/core/grants/remove.js +1 -1
  151. package/dist/core/grants/sync.js +1 -1
  152. package/dist/core/grants/watch.d.ts +15 -9
  153. package/dist/core/grants/watch.js +1 -1
  154. package/dist/core/graph/custom.js +8 -8
  155. package/dist/core/graph/jobs.d.ts +83 -5
  156. package/dist/core/graph/jobs.js +11 -2
  157. package/dist/core/graph/kinds/job.js +2 -2
  158. package/dist/core/graph/names.js +7 -7
  159. package/dist/core/graph/objects.d.ts +5 -1
  160. package/dist/core/graph/objects.js +11 -11
  161. package/dist/core/graph/reader.d.ts +3 -0
  162. package/dist/core/graph/reader.js +3 -3
  163. package/dist/core/graph/repo-sync/identity.d.ts +3 -1
  164. package/dist/core/graph/repo-sync/identity.js +1 -1
  165. package/dist/core/graph/repo-sync/repos.d.ts +1 -1
  166. package/dist/core/graph/repo-sync/repos.js +2 -2
  167. package/dist/core/graph/repo-sync/sync.js +2 -2
  168. package/dist/core/installed-plugins.js +1 -1
  169. package/dist/core/keybindings/persistence.d.ts +1 -1
  170. package/dist/core/keybindings/persistence.js +1 -1
  171. package/dist/core/layout-migrate/loose-keys.d.ts +1 -1
  172. package/dist/core/layout-migrate/loose-keys.js +1 -1
  173. package/dist/core/ledger/account.d.ts +12 -0
  174. package/dist/core/ledger/account.js +2 -0
  175. package/dist/core/ledger/charges.d.ts +2 -2
  176. package/dist/core/ledger/charges.js +1 -1
  177. package/dist/core/ledger/check.d.ts +5 -1
  178. package/dist/core/ledger/check.js +1 -7
  179. package/dist/core/ledger/chunks.d.ts +8 -0
  180. package/dist/core/ledger/chunks.js +2 -0
  181. package/dist/core/ledger/index.d.ts +2 -1
  182. package/dist/core/ledger/index.js +1 -1
  183. package/dist/core/ledger/infra-allowance.d.ts +9 -0
  184. package/dist/core/ledger/infra-allowance.js +1 -0
  185. package/dist/core/ledger/limits.js +3 -3
  186. package/dist/core/ledger/payer.d.ts +1 -1
  187. package/dist/core/ledger/payer.js +1 -1
  188. package/dist/core/ledger/read.d.ts +14 -4
  189. package/dist/core/ledger/read.js +2 -3
  190. package/dist/core/ledger/totals.d.ts +5 -16
  191. package/dist/core/ledger/totals.js +4 -4
  192. package/dist/core/ledger/types.d.ts +12 -3
  193. package/dist/core/llm/run.js +2 -2
  194. package/dist/core/managed-providers.d.ts +72 -0
  195. package/dist/core/managed-providers.js +1 -0
  196. package/dist/core/model-auth/api-credential-store.d.ts +10 -0
  197. package/dist/core/model-auth/api-credential-store.js +1 -0
  198. package/dist/core/model-auth/daemon-login.d.ts +1 -1
  199. package/dist/core/model-auth/daemon-login.js +1 -1
  200. package/dist/core/model-auth/flows.d.ts +5 -4
  201. package/dist/core/model-auth/flows.js +1 -1
  202. package/dist/core/model-auth/handoff-jti.d.ts +3 -4
  203. package/dist/core/model-auth/handoff-jti.js +1 -1
  204. package/dist/core/model-auth/readiness.js +1 -1
  205. package/dist/core/model-config.d.ts +1 -1
  206. package/dist/core/model-config.js +1 -1
  207. package/dist/core/model-routes-pool.d.ts +14 -0
  208. package/dist/core/model-routes-pool.js +1 -0
  209. package/dist/core/model-routes.d.ts +11 -16
  210. package/dist/core/model-routes.js +1 -1
  211. package/dist/core/peer/ingest.d.ts +2 -0
  212. package/dist/core/peer/ingest.js +1 -1
  213. package/dist/core/peer/stubs.d.ts +8 -0
  214. package/dist/core/peer/stubs.js +1 -1
  215. package/dist/core/permissions/evaluator.d.ts +7 -2
  216. package/dist/core/permissions/evaluator.js +2 -2
  217. package/dist/core/plugin-env.js +1 -1
  218. package/dist/core/pool-credential-store.d.ts +2 -5
  219. package/dist/core/pool-credential-store.js +1 -1
  220. package/dist/core/profiles/app-env-store.d.ts +5 -5
  221. package/dist/core/profiles/app-env-store.js +1 -2
  222. package/dist/core/profiles/default-binding.d.ts +5 -5
  223. package/dist/core/profiles/default-binding.js +1 -1
  224. package/dist/core/profiles/env-store.d.ts +10 -21
  225. package/dist/core/profiles/env-store.js +1 -2
  226. package/dist/core/profiles/manifest.d.ts +14 -18
  227. package/dist/core/profiles/manifest.js +1 -1
  228. package/dist/core/profiles/relocate.js +1 -1
  229. package/dist/core/profiles/select.js +21 -21
  230. package/dist/core/provider-credential.d.ts +1 -1
  231. package/dist/core/provider-credential.js +1 -1
  232. package/dist/core/provider-management.d.ts +3 -4
  233. package/dist/core/provider-management.js +1 -1
  234. package/dist/core/providers/bundles.js +2 -2
  235. package/dist/core/providers/call.d.ts +3 -34
  236. package/dist/core/providers/description.d.ts +34 -0
  237. package/dist/core/providers/description.js +0 -0
  238. package/dist/core/providers/token-cache.d.ts +14 -14
  239. package/dist/core/providers/token-cache.js +1 -1
  240. package/dist/core/review/companion.js +1 -1
  241. package/dist/core/runs/activity.d.ts +1 -1
  242. package/dist/core/runs/activity.js +1 -1
  243. package/dist/core/runs/events.d.ts +7 -5
  244. package/dist/core/runs/events.js +2 -6
  245. package/dist/core/runs/operations.d.ts +1 -1
  246. package/dist/core/runs/operations.js +6 -6
  247. package/dist/core/runtime/bearings.js +1 -1
  248. package/dist/core/runtime/broker/attachments-fetch.d.ts +5 -0
  249. package/dist/core/runtime/broker/attachments-fetch.js +1 -0
  250. package/dist/core/runtime/broker/bash-job-launch.d.ts +4 -2
  251. package/dist/core/runtime/broker/bash-job-launch.js +1 -1
  252. package/dist/core/runtime/broker/boot-state.d.ts +9 -0
  253. package/dist/core/runtime/broker/boot-state.js +1 -0
  254. package/dist/core/runtime/broker/cli-relay.d.ts +2 -1
  255. package/dist/core/runtime/broker/cli-relay.js +1 -1
  256. package/dist/core/runtime/broker/client-registry.d.ts +3 -3
  257. package/dist/core/runtime/broker/daemon-ops.d.ts +10 -1
  258. package/dist/core/runtime/broker/daemon-ops.js +1 -1
  259. package/dist/core/runtime/broker/fault-retry.d.ts +2 -0
  260. package/dist/core/runtime/broker/fault-retry.js +1 -1
  261. package/dist/core/runtime/broker/frame-engine-control.js +1 -1
  262. package/dist/core/runtime/broker/held-deferred-inbox.d.ts +3 -0
  263. package/dist/core/runtime/broker/held-deferred-inbox.js +1 -1
  264. package/dist/core/runtime/broker/job-reporter.d.ts +18 -0
  265. package/dist/core/runtime/broker/job-reporter.js +2 -0
  266. package/dist/core/runtime/broker/model-adapter.d.ts +3 -0
  267. package/dist/core/runtime/broker/model-adapter.js +3 -2
  268. package/dist/core/runtime/broker/signal-stream.js +1 -1
  269. package/dist/core/runtime/broker/spilled-body.d.ts +5 -0
  270. package/dist/core/runtime/broker/spilled-body.js +1 -0
  271. package/dist/core/runtime/broker/summaries-post.d.ts +5 -0
  272. package/dist/core/runtime/broker/summaries-post.js +1 -0
  273. package/dist/core/runtime/broker/tool-groups.d.ts +3 -0
  274. package/dist/core/runtime/broker/tool-groups.js +1 -1
  275. package/dist/core/runtime/broker/turn-replies.d.ts +1 -0
  276. package/dist/core/runtime/broker/turn-replies.js +1 -1
  277. package/dist/core/runtime/broker-persona-guidance.js +2 -2
  278. package/dist/core/runtime/broker.js +1 -1
  279. package/dist/core/runtime/deliver-live.js +1 -1
  280. package/dist/core/runtime/first-boot-rows.d.ts +8 -0
  281. package/dist/core/runtime/first-boot-rows.js +2 -0
  282. package/dist/core/runtime/fleet.d.ts +13 -12
  283. package/dist/core/runtime/front-door-login.js +1 -1
  284. package/dist/core/runtime/front-door.js +6 -6
  285. package/dist/core/runtime/headless-pi.js +1 -1
  286. package/dist/core/runtime/host.d.ts +14 -3
  287. package/dist/core/runtime/host.js +3 -3
  288. package/dist/core/runtime/invocation.d.ts +6 -0
  289. package/dist/core/runtime/kickoff.d.ts +2 -14
  290. package/dist/core/runtime/kickoff.js +17 -19
  291. package/dist/core/runtime/launch-config-worker.js +1 -1
  292. package/dist/core/runtime/launch.js +2 -2
  293. package/dist/core/runtime/model-registry.js +1 -1
  294. package/dist/core/runtime/node-read.d.ts +4 -6
  295. package/dist/core/runtime/node-read.js +3 -3
  296. package/dist/core/runtime/nodes.js +1 -1
  297. package/dist/core/runtime/outcome-document.d.ts +0 -1
  298. package/dist/core/runtime/outcome-document.js +1 -1
  299. package/dist/core/runtime/output-result.d.ts +15 -0
  300. package/dist/core/runtime/output-result.js +5 -0
  301. package/dist/core/runtime/placement.d.ts +17 -1
  302. package/dist/core/runtime/placement.js +1 -1
  303. package/dist/core/runtime/provider-credential-gate.d.ts +6 -2
  304. package/dist/core/runtime/provider-credential-gate.js +2 -2
  305. package/dist/core/runtime/recovery-hold.d.ts +21 -2
  306. package/dist/core/runtime/recovery-hold.js +2 -1
  307. package/dist/core/runtime/recycle.js +1 -1
  308. package/dist/core/runtime/reset.js +1 -1
  309. package/dist/core/runtime/revive.d.ts +2 -2
  310. package/dist/core/runtime/revive.js +2 -2
  311. package/dist/core/runtime/runner/blaxel/client.d.ts +81 -0
  312. package/dist/core/runtime/runner/blaxel/client.js +1 -0
  313. package/dist/core/runtime/runner/blaxel/image-build.d.ts +74 -0
  314. package/dist/core/runtime/runner/blaxel/image-build.js +5 -0
  315. package/dist/core/runtime/runner/blaxel/runner.d.ts +41 -0
  316. package/dist/core/runtime/runner/blaxel/runner.js +3 -0
  317. package/dist/core/runtime/runner/index.d.ts +8 -0
  318. package/dist/core/runtime/runner/index.js +1 -0
  319. package/dist/core/runtime/runner/process.d.ts +11 -0
  320. package/dist/core/runtime/runner/process.js +1 -0
  321. package/dist/core/runtime/runner/types.d.ts +95 -0
  322. package/dist/core/runtime/runner/types.js +0 -0
  323. package/dist/core/runtime/situational-context.d.ts +3 -6
  324. package/dist/core/runtime/situational-context.js +2 -2
  325. package/dist/core/runtime/situational-live.js +1 -1
  326. package/dist/core/runtime/spawn-env-rows.d.ts +5 -0
  327. package/dist/core/runtime/spawn-env-rows.js +1 -0
  328. package/dist/core/runtime/spawn-env.d.ts +4 -18
  329. package/dist/core/runtime/spawn-env.js +1 -1
  330. package/dist/core/runtime/spawn.js +2 -2
  331. package/dist/core/runtime/stop-guard.d.ts +1 -1
  332. package/dist/core/runtime/stop-guard.js +2 -8
  333. package/dist/core/runtime/structured-output.d.ts +0 -67
  334. package/dist/core/runtime/structured-output.js +3 -8
  335. package/dist/core/runtime/tmux-driver.d.ts +8 -0
  336. package/dist/core/runtime/tmux-driver.js +6 -6
  337. package/dist/core/runtime/view-socket.d.ts +20 -0
  338. package/dist/core/runtime/view-socket.js +3 -1
  339. package/dist/core/runtime/warm-pool.d.ts +13 -1
  340. package/dist/core/runtime/warm-pool.js +1 -1
  341. package/dist/core/scoped-state/legacy-profiles.d.ts +11 -0
  342. package/dist/core/scoped-state/legacy-profiles.js +1 -0
  343. package/dist/core/scoped-state/materialized-plugins.d.ts +18 -0
  344. package/dist/core/scoped-state/materialized-plugins.js +1 -0
  345. package/dist/core/scoped-state/migrate.d.ts +8 -1
  346. package/dist/core/scoped-state/migrate.js +3 -3
  347. package/dist/core/scoped-state/person-rows.d.ts +10 -0
  348. package/dist/core/scoped-state/person-rows.js +6 -0
  349. package/dist/core/scoped-state/person-store.d.ts +72 -0
  350. package/dist/core/scoped-state/person-store.js +1 -0
  351. package/dist/core/secrets/blaxel-launch.d.ts +24 -0
  352. package/dist/core/secrets/blaxel-launch.js +4 -0
  353. package/dist/core/secrets/blaxel-register.d.ts +2 -0
  354. package/dist/core/secrets/blaxel-register.js +1 -0
  355. package/dist/core/secrets/env-rows.d.ts +17 -0
  356. package/dist/core/secrets/env-rows.js +4 -0
  357. package/dist/core/secrets/environment-owner.d.ts +8 -0
  358. package/dist/core/secrets/environment-owner.js +1 -0
  359. package/dist/core/secrets/environment-secrets.d.ts +14 -0
  360. package/dist/core/secrets/environment-secrets.js +1 -0
  361. package/dist/core/secrets/index.d.ts +2 -0
  362. package/dist/core/secrets/index.js +1 -0
  363. package/dist/core/secrets/key.d.ts +3 -0
  364. package/dist/core/secrets/key.js +2 -0
  365. package/dist/core/secrets/seal.d.ts +18 -0
  366. package/dist/core/secrets/seal.js +1 -0
  367. package/dist/core/spaces/mounts.d.ts +1 -1
  368. package/dist/core/spaces/mounts.js +1 -1
  369. package/dist/core/spaces/process.d.ts +1 -1
  370. package/dist/core/spaces/stop.d.ts +7 -5
  371. package/dist/core/spaces/stop.js +2 -2
  372. package/dist/core/spaces/users.d.ts +2 -1
  373. package/dist/core/spaces/users.js +1 -1
  374. package/dist/core/storage/attachments.d.ts +11 -8
  375. package/dist/core/storage/attachments.js +1 -1
  376. package/dist/core/storage/tables.d.ts +4 -0
  377. package/dist/core/storage/tables.js +3 -3
  378. package/dist/core/subscription-state.d.ts +34 -72
  379. package/dist/core/subscription-state.js +1 -4
  380. package/dist/core/substrate/delivery/plan.js +1 -1
  381. package/dist/core/user-settings.js +1 -1
  382. package/dist/daemon/api/app-listener.js +10 -10
  383. package/dist/daemon/api/auth/person-bearer.d.ts +14 -0
  384. package/dist/daemon/api/auth/person-bearer.js +1 -0
  385. package/dist/daemon/api/bridge.js +4 -4
  386. package/dist/daemon/api/document-listener.js +3 -3
  387. package/dist/daemon/api/environment-of.d.ts +18 -0
  388. package/dist/daemon/api/environment-of.js +5 -0
  389. package/dist/daemon/api/handlers/analytics.js +1 -1
  390. package/dist/daemon/api/handlers/app-profiles.d.ts +2 -2
  391. package/dist/daemon/api/handlers/app-profiles.js +1 -1
  392. package/dist/daemon/api/handlers/bash-jobs.js +1 -1
  393. package/dist/daemon/api/handlers/bash.js +1 -1
  394. package/dist/daemon/api/handlers/broker-ops.d.ts +3 -0
  395. package/dist/daemon/api/handlers/broker-ops.js +1 -1
  396. package/dist/daemon/api/handlers/broker-recovery.js +1 -1
  397. package/dist/daemon/api/handlers/broker-signals.d.ts +0 -3
  398. package/dist/daemon/api/handlers/broker-signals.js +2 -2
  399. package/dist/daemon/api/handlers/canvas.js +4 -4
  400. package/dist/daemon/api/handlers/charges.d.ts +1 -1
  401. package/dist/daemon/api/handlers/charges.js +1 -1
  402. package/dist/daemon/api/handlers/conversations.d.ts +2 -0
  403. package/dist/daemon/api/handlers/conversations.js +3 -0
  404. package/dist/daemon/api/handlers/crons.js +1 -1
  405. package/dist/daemon/api/handlers/daemon.js +1 -1
  406. package/dist/daemon/api/handlers/delivery.d.ts +1 -1
  407. package/dist/daemon/api/handlers/delivery.js +1 -1
  408. package/dist/daemon/api/handlers/docs.js +1 -1
  409. package/dist/daemon/api/handlers/exec.js +1 -1
  410. package/dist/daemon/api/handlers/execution-start.d.ts +2 -0
  411. package/dist/daemon/api/handlers/execution-start.js +2 -0
  412. package/dist/daemon/api/handlers/files.js +1 -1
  413. package/dist/daemon/api/handlers/git-credential.d.ts +11 -0
  414. package/dist/daemon/api/handlers/git-credential.js +1 -0
  415. package/dist/daemon/api/handlers/hook-exec.d.ts +1 -1
  416. package/dist/daemon/api/handlers/hook-exec.js +1 -1
  417. package/dist/daemon/api/handlers/human-requests.js +1 -1
  418. package/dist/daemon/api/handlers/inbox.js +1 -1
  419. package/dist/daemon/api/handlers/messages.js +1 -1
  420. package/dist/daemon/api/handlers/model-config.js +1 -1
  421. package/dist/daemon/api/handlers/modelauth.d.ts +1 -1
  422. package/dist/daemon/api/handlers/modelauth.js +1 -1
  423. package/dist/daemon/api/handlers/node-events.d.ts +6 -0
  424. package/dist/daemon/api/handlers/node-events.js +3 -3
  425. package/dist/daemon/api/handlers/node-outcomes.js +1 -1
  426. package/dist/daemon/api/handlers/node-records.js +3 -2
  427. package/dist/daemon/api/handlers/nodes.js +1 -1
  428. package/dist/daemon/api/handlers/objects.js +3 -3
  429. package/dist/daemon/api/handlers/profiles.js +1 -1
  430. package/dist/daemon/api/handlers/providers.js +1 -1
  431. package/dist/daemon/api/handlers/reports.js +9 -13
  432. package/dist/daemon/api/handlers/reviews.js +1 -1
  433. package/dist/daemon/api/handlers/runs.js +1 -1
  434. package/dist/daemon/api/handlers/settings.d.ts +2 -0
  435. package/dist/daemon/api/handlers/settings.js +1 -0
  436. package/dist/daemon/api/handlers/structured-requests.js +1 -1
  437. package/dist/daemon/api/handlers/vendor-status.d.ts +1 -1
  438. package/dist/daemon/api/handlers/vendor-status.js +1 -1
  439. package/dist/daemon/api/handlers/worktree.js +1 -1
  440. package/dist/daemon/api/map.d.ts +2 -2
  441. package/dist/daemon/api/map.js +2 -2
  442. package/dist/daemon/api/operations.js +1 -1
  443. package/dist/daemon/api/reader.d.ts +8 -1
  444. package/dist/daemon/api/reader.js +1 -1
  445. package/dist/daemon/api/router.d.ts +4 -2
  446. package/dist/daemon/api/router.js +1 -1
  447. package/dist/daemon/api/server.d.ts +15 -3
  448. package/dist/daemon/api/server.js +1 -1
  449. package/dist/daemon/api/vendor-signin.d.ts +2 -1
  450. package/dist/daemon/api/vendor-signin.js +20 -20
  451. package/dist/daemon/api/viewer-presence.d.ts +20 -6
  452. package/dist/daemon/api/viewer-presence.js +2 -1
  453. package/dist/daemon/broker-signals/bus.d.ts +48 -11
  454. package/dist/daemon/broker-signals/bus.js +1 -1
  455. package/dist/daemon/cron/armed-context.js +1 -1
  456. package/dist/daemon/cron/lease-recovery.js +1 -1
  457. package/dist/daemon/cron/live-runs.d.ts +6 -0
  458. package/dist/daemon/cron/live-runs.js +1 -1
  459. package/dist/daemon/cron/run-socket.js +1 -1
  460. package/dist/daemon/cron-run.js +2 -2
  461. package/dist/daemon/crtrd-cli.js +1 -1
  462. package/dist/daemon/crtrd.d.ts +10 -0
  463. package/dist/daemon/crtrd.js +18 -17
  464. package/dist/daemon/delivery/run-delivery.d.ts +1 -1
  465. package/dist/daemon/delivery/run-delivery.js +1 -1
  466. package/dist/daemon/due-sources.d.ts +1 -1
  467. package/dist/daemon/due-sources.js +1 -1
  468. package/dist/daemon/due.js +7 -6
  469. package/dist/daemon/fleet.d.ts +6 -6
  470. package/dist/daemon/fleet.js +9 -9
  471. package/dist/daemon/park-activity.d.ts +2 -1
  472. package/dist/daemon/park-activity.js +1 -1
  473. package/dist/daemon/park-pending.d.ts +20 -13
  474. package/dist/daemon/park-pending.js +3 -1
  475. package/dist/daemon/profile-delete.js +1 -1
  476. package/dist/daemon/reconcilers/bash-deadline.d.ts +15 -10
  477. package/dist/daemon/reconcilers/bash-deadline.js +1 -3
  478. package/dist/daemon/reconcilers/broker-supervision.d.ts +13 -3
  479. package/dist/daemon/reconcilers/broker-supervision.js +4 -4
  480. package/dist/daemon/reconcilers/cron-lane.d.ts +0 -1
  481. package/dist/daemon/reconcilers/cron-lane.js +1 -1
  482. package/dist/daemon/reconcilers/fault-retry-backstop.d.ts +2 -9
  483. package/dist/daemon/reconcilers/fault-retry-backstop.js +1 -1
  484. package/dist/daemon/reconcilers/grant-effects.d.ts +8 -0
  485. package/dist/daemon/reconcilers/grant-effects.js +1 -0
  486. package/dist/daemon/reconcilers/grove-instance-sweep.d.ts +1 -3
  487. package/dist/daemon/reconcilers/grove-instance-sweep.js +1 -1
  488. package/dist/daemon/reconcilers/ledger-r2.d.ts +27 -0
  489. package/dist/daemon/reconcilers/ledger-r2.js +7 -0
  490. package/dist/daemon/reconcilers/ledger-sandbox.d.ts +55 -0
  491. package/dist/daemon/reconcilers/ledger-sandbox.js +9 -0
  492. package/dist/daemon/reconcilers/ledger-storage.d.ts +22 -0
  493. package/dist/daemon/reconcilers/ledger-storage.js +5 -0
  494. package/dist/daemon/reconcilers/live-obligation.d.ts +3 -2
  495. package/dist/daemon/reconcilers/live-obligation.js +1 -1
  496. package/dist/daemon/reconcilers/managed-worktree-sweep.d.ts +0 -5
  497. package/dist/daemon/reconcilers/managed-worktree-sweep.js +1 -1
  498. package/dist/daemon/reconcilers/node-deadline.d.ts +2 -1
  499. package/dist/daemon/reconcilers/node-deadline.js +1 -1
  500. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +1 -2
  501. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +1 -1
  502. package/dist/daemon/reconcilers/node-lifecycle/remote-wake.d.ts +31 -0
  503. package/dist/daemon/reconcilers/node-lifecycle/remote-wake.js +1 -0
  504. package/dist/daemon/reconcilers/node-lifecycle/requested-stops.d.ts +5 -0
  505. package/dist/daemon/reconcilers/node-lifecycle/requested-stops.js +2 -0
  506. package/dist/daemon/reconcilers/node-lifecycle/tick.js +1 -1
  507. package/dist/daemon/reconcilers/pending-review-submit.js +1 -1
  508. package/dist/daemon/reconcilers/recipe-image.d.ts +49 -0
  509. package/dist/daemon/reconcilers/recipe-image.js +7 -0
  510. package/dist/daemon/reconcilers/row-claim.d.ts +19 -0
  511. package/dist/daemon/reconcilers/row-claim.js +2 -0
  512. package/dist/daemon/reconcilers/storage-due-work.d.ts +18 -3
  513. package/dist/daemon/reconcilers/storage-due-work.js +2 -1
  514. package/dist/daemon/reconcilers/storage-maintenance.d.ts +5 -13
  515. package/dist/daemon/reconcilers/storage-maintenance.js +1 -1
  516. package/dist/daemon/reconcilers/structured-request-lane.js +1 -1
  517. package/dist/daemon/reconcilers/throttled-lane.d.ts +11 -0
  518. package/dist/daemon/reconcilers/throttled-lane.js +1 -0
  519. package/dist/daemon/review/finish.js +1 -1
  520. package/dist/daemon/run-notices.d.ts +3 -0
  521. package/dist/daemon/run-notices.js +1 -1
  522. package/dist/daemon/runs/append-section.d.ts +4 -1
  523. package/dist/daemon/runs/append-section.js +2 -2
  524. package/dist/daemon/runs/observer.d.ts +3 -2
  525. package/dist/daemon/runs/observer.js +3 -2
  526. package/dist/daemon/runs/view-transport.d.ts +19 -0
  527. package/dist/daemon/runs/view-transport.js +1 -0
  528. package/dist/daemon/structured-requests/boot.d.ts +2 -1
  529. package/dist/daemon/structured-requests/boot.js +1 -1
  530. package/dist/daemon/structured-requests/effects.d.ts +4 -3
  531. package/dist/daemon/structured-requests/effects.js +1 -1
  532. package/dist/daemon/structured-requests/notifier.d.ts +2 -0
  533. package/dist/daemon/structured-requests/notifier.js +1 -1
  534. package/dist/daemon/structured-requests/sidecar.d.ts +4 -3
  535. package/dist/daemon/structured-requests/sidecar.js +1 -1
  536. package/dist/launcher/validate.js +1 -1
  537. package/dist/migrations/004-canvas-documents/index.js +2 -2
  538. package/dist/pi-extensions/broker-local.d.ts +3 -3
  539. package/dist/pi-extensions/broker-local.js +1 -2
  540. package/dist/pi-extensions/canvas-bash-valve.js +6 -6
  541. package/dist/pi-extensions/canvas-context-intro.js +2 -2
  542. package/dist/pi-extensions/canvas-elapsed.js +2 -2
  543. package/dist/pi-extensions/canvas-goal-capture.js +1 -1
  544. package/dist/pi-extensions/canvas-inbox-watcher.js +2 -2
  545. package/dist/pi-extensions/canvas-review-boundary.d.ts +11 -1
  546. package/dist/pi-extensions/canvas-review-boundary.js +3 -3
  547. package/dist/pi-extensions/canvas-stophook.js +1 -1
  548. package/dist/pi-extensions/canvas-structured-output.d.ts +5 -3
  549. package/dist/pi-extensions/canvas-structured-output.js +6 -11
  550. package/dist/pi-extensions/canvas-tool-guide.d.ts +9 -2
  551. package/dist/pi-extensions/canvas-tool-guide.js +6 -6
  552. package/dist/shared/inbox-entry-body.js +7 -7
  553. package/dist/types.d.ts +12 -0
  554. package/docs/sdk/release-notes.md +4 -0
  555. package/package.json +7 -4
  556. package/packages/crouter-identity/package.json +1 -1
  557. package/runtime.lock.json +13 -13
  558. package/scripts/git-credential-crtr +33 -0
  559. package/scripts/sandbox-bootstrap.sh +54 -0
  560. package/scripts/sandbox-start.mjs +320 -0
  561. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +0 -78
  562. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +0 -1179
  563. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +0 -1406
  564. package/dist/core/conversation-store/file-store.d.ts +0 -37
  565. package/dist/core/conversation-store/file-store.js +0 -9
  566. package/dist/core/conversation-store/listener.d.ts +0 -4
  567. package/dist/core/conversation-store/listener.js +0 -8
  568. package/dist/core/scoped-state/profiles.d.ts +0 -27
  569. package/dist/core/scoped-state/profiles.js +0 -1
  570. package/dist/core/scoped-state/providers.d.ts +0 -24
  571. package/dist/core/scoped-state/providers.js +0 -1
  572. package/dist/core/scoped-state/settings.d.ts +0 -28
  573. package/dist/core/scoped-state/settings.js +0 -1
@@ -1,35 +1,72 @@
1
1
  import type { BrokerExtensionNodeDTO } from '../../api/dto/broker-ops.js';
2
2
  /** Node-owned mutable fields in extensionNode's projection. Keep this list and
3
3
  * the typed cache view together; object/worktree-owned fields are never cached. */
4
- export declare const BROKER_SIGNAL_NODE_COLUMNS: readonly ["description", "title", "icon", "mode", "lifecycle", "status", "cwd", "parent", "fork_from", "profile_id", "review_binding_json", "persona_ack_json", "intent"];
4
+ export declare const BROKER_SIGNAL_NODE_COLUMNS: readonly ["description", "title", "icon", "mode", "lifecycle", "status", "cwd", "parent", "fork_from", "profile_id", "review_binding_json", "persona_ack_json", "intent", "situational_card"];
5
5
  type ProjectedNodeKey<C extends string> = C extends 'review_binding_json' ? 'review_binding' : C extends 'persona_ack_json' ? 'persona_ack' : C;
6
- export type BrokerSignalCachedNode = Pick<BrokerExtensionNodeDTO, Extract<ProjectedNodeKey<(typeof BROKER_SIGNAL_NODE_COLUMNS)[number]>, keyof BrokerExtensionNodeDTO>>;
6
+ export type BrokerSignalCachedNode = Pick<BrokerExtensionNodeDTO, Extract<ProjectedNodeKey<Exclude<(typeof BROKER_SIGNAL_NODE_COLUMNS)[number], 'situational_card'>>, keyof BrokerExtensionNodeDTO>>;
7
7
  /** Only node-owned columns are suitable for P4's cache. The same columns
8
- * appear in `crtr_sig_node_upd` in schema-pg.sql; keep the two lists together. */
8
+ * appear in `crtr_sig_node_upd` (002-fleet.sql recreates it with `situational_card`); keep the two lists together. */
9
9
  export declare function cachedBrokerNodeFields(node: BrokerExtensionNodeDTO): BrokerSignalCachedNode;
10
- export type BrokerSignalKind = 'mail' | 'node';
11
- export type BrokerSignalSubscriber = (nodeId: string, kind: BrokerSignalKind) => void;
12
- /** Hints, not delivery state: a rolled-back write may mark a node. The consumer
13
- * always re-reads durable state. Pending marks are bounded by distinct node ids. */
10
+ /** What a `crtr_signal` notification names. `mail` and `node` carry a node id; the others name the row that changed
11
+ * (D39): a grant id, run or node id, structured request, queue, limit, or environment object. `fault_retry` carries a node id: the
12
+ * fault-retry backstop publishes it in the commit that claims the nudge, and the API process holding that node's stream turns it
13
+ * into a `fault-retry` line. The writers fire them (the
14
+ * schema's triggers, or the unit that owns the writer); the bus only delivers. */
15
+ export declare const BROKER_SIGNAL_KINDS: readonly ["mail", "node", "grant", "run_event", "report", "structured_request", "queue", "spend_limits", "environment_image", "fault_retry", "settings"];
16
+ export type BrokerSignalKind = (typeof BROKER_SIGNAL_KINDS)[number];
17
+ export type BrokerSignalSubscriber = (id: string, kind: BrokerSignalKind) => void;
18
+ export interface BrokerSignalSubscription {
19
+ /** The kinds this subscriber hears. Default `mail` and `node`, the two a node's broker stream reads. */
20
+ kinds?: readonly BrokerSignalKind[];
21
+ /** Called each time the listen connection starts or restarts (LISTEN is active). Notifications sent while it was down
22
+ * are lost, so a subscriber re-reads its durable state here; that is its catch-up. A subscriber added while the bus
23
+ * is already listening is called once, soon after subscribing. */
24
+ onListenStart?: () => void;
25
+ }
26
+ export interface BrokerSignalBusOptions {
27
+ /** How often the listen connection is checked with `SELECT 1`. Default 30 s. */
28
+ checkIntervalMs?: number;
29
+ /** How long a check may take before the connection counts as lost. Default 10 s. */
30
+ checkTimeoutMs?: number;
31
+ /** Delay before a lost listen connection reconnects. Default 1 s. */
32
+ reconnectMs?: number;
33
+ }
34
+ /** Hints, not delivery state: a rolled-back write may mark an id. The consumer always re-reads durable state. Pending
35
+ * marks are bounded by distinct (kind, id) pairs and delivered once per flush. */
14
36
  export declare class BrokerSignalBus {
15
37
  private readonly dirty;
16
38
  private readonly subscribers;
17
39
  private scheduled;
18
- subscribe(listener: BrokerSignalSubscriber): Promise<() => void>;
40
+ private readonly checkIntervalMs;
41
+ private readonly checkTimeoutMs;
42
+ private readonly reconnectMs;
43
+ constructor(options?: BrokerSignalBusOptions);
44
+ subscribe(listener: BrokerSignalSubscriber, subscription?: BrokerSignalSubscription): Promise<() => void>;
19
45
  markMail(nodeId: string): void;
20
46
  markNode(nodeId: string): void;
47
+ /** Hint any kind, as a notification of that kind would. */
48
+ markSignal(kind: BrokerSignalKind, id: string): void;
21
49
  private mark;
22
50
  private flush;
51
+ private callListenStart;
23
52
  /** Start listening on `crtr_signal` over a second, listen-only connection to `url` (the schema's `pg_notify`
24
- * triggers publish `mail:<node id>` and `node:<node id>` at commit). Resolves once LISTEN is active, so a write
25
- * committed after it returns is heard. A lost connection reconnects with backoff; signals sent while it was down
26
- * are lost, which is safe because they are hints and every consumer re-reads durable state. */
53
+ * triggers publish `<kind>:<id>` at commit). Resolves once LISTEN is active, so a write committed after it returns is
54
+ * heard. A lost connection (an error, an end, or a failed `SELECT 1` check) reconnects with a fixed delay and calls every
55
+ * subscriber's `onListenStart` once LISTEN is active again. Signals sent while it was down are lost, which is safe because
56
+ * they are hints and every consumer re-reads durable state, at `onListenStart` and otherwise. */
27
57
  listen(url: string): Promise<void>;
28
58
  private listener;
29
59
  private listening;
30
60
  private url;
31
61
  private reconnect;
62
+ private check;
32
63
  private connectListener;
64
+ /** `SELECT 1` on the listen connection every interval. A dead TCP connection (a dropped route, a failover) raises no
65
+ * error by itself, so a failed or unanswered check is what tells the bus the connection is lost. */
66
+ private startChecks;
67
+ private stopChecks;
68
+ private dropListener;
69
+ private scheduleReconnect;
33
70
  /** Stop listening and drop pending marks. The returned promise resolves once the connection has closed. */
34
71
  close(): Promise<void>;
35
72
  }
@@ -1 +1 @@
1
- var a=Object.defineProperty;var s=(n,e)=>a(n,"name",{value:e,configurable:!0});import h from"pg";const u=["description","title","icon","mode","lifecycle","status","cwd","parent","fork_from","profile_id","review_binding_json","persona_ack_json","intent"],f=s(n=>n==="review_binding_json"?"review_binding":n==="persona_ack_json"?"persona_ack":n,"nodeField");function _(n){const e={};for(const i of u){const t=f(i);e[t]=n[t]}return e}s(_,"cachedBrokerNodeFields");class m{static{s(this,"BrokerSignalBus")}dirty=new Map;subscribers=new Set;scheduled;async subscribe(e){return this.subscribers.add(e),()=>{this.subscribers.delete(e)}}markMail(e){this.mark(e,1)}markNode(e){this.mark(e,2)}mark(e,i){this.dirty.set(e,(this.dirty.get(e)??0)|i),this.scheduled??=setImmediate(()=>this.flush())}flush(){this.scheduled=void 0;const e=[...this.dirty];this.dirty.clear();for(const[i,t]of e)for(const r of this.subscribers)try{t&1&&r(i,"mail"),t&2&&r(i,"node")}catch{}}async listen(e){this.listener!==void 0||this.listening!==void 0||(this.url=e,this.listening=this.connectListener().finally(()=>{this.listening=void 0}),await this.listening)}listener;listening;url;reconnect;async connectListener(){const e=new h.Client({connectionString:this.url,application_name:`crtr-signal-${process.pid}`});e.on("notification",r=>{const c=r.payload??"",o=c.indexOf(":");if(o<0)return;const d=c.slice(0,o),l=c.slice(o+1);d==="mail"?this.markMail(l):d==="node"&&this.markNode(l)});const i=s(()=>{this.listener===e&&(this.listener=void 0,e.removeAllListeners(),e.on("error",()=>{}),e.end().catch(()=>{}),this.url!==void 0&&(this.reconnect=setTimeout(()=>{this.reconnect=void 0,this.connectListener().catch(()=>t())},1e3)))},"lost"),t=s(()=>{this.url===void 0||this.reconnect!==void 0||(this.reconnect=setTimeout(()=>{this.reconnect=void 0,this.connectListener().catch(()=>t())},1e3))},"lostRetry");e.on("error",i),e.on("end",i),await e.connect(),await e.query("LISTEN crtr_signal"),this.listener=e}close(){this.url=void 0,this.reconnect!==void 0&&clearTimeout(this.reconnect),this.reconnect=void 0;const e=this.listener;return this.listener=void 0,this.scheduled!==void 0&&clearImmediate(this.scheduled),this.scheduled=void 0,this.dirty.clear(),this.subscribers.clear(),e===void 0?Promise.resolve():(e.removeAllListeners("notification"),e.end().catch(()=>{}))}}const g=new m;export{u as BROKER_SIGNAL_NODE_COLUMNS,m as BrokerSignalBus,g as brokerSignalBus,_ as cachedBrokerNodeFields};
1
+ var h=Object.defineProperty;var r=(i,e)=>h(i,"name",{value:e,configurable:!0});import d from"pg";const l=["description","title","icon","mode","lifecycle","status","cwd","parent","fork_from","profile_id","review_binding_json","persona_ack_json","intent","situational_card"],a=r(i=>i==="review_binding_json"?"review_binding":i==="persona_ack_json"?"persona_ack":i,"nodeField");function p(i){const e={};for(const n of l){if(n==="situational_card")continue;const t=a(n);e[t]=i[t]}return e}r(p,"cachedBrokerNodeFields");const u=["mail","node","grant","run_event","report","structured_request","queue","spend_limits","environment_image","fault_retry","settings"],f=new Set(u);class k{static{r(this,"BrokerSignalBus")}dirty=new Map;subscribers=new Set;scheduled;checkIntervalMs;checkTimeoutMs;reconnectMs;constructor(e={}){this.checkIntervalMs=e.checkIntervalMs??3e4,this.checkTimeoutMs=e.checkTimeoutMs??1e4,this.reconnectMs=e.reconnectMs??1e3}async subscribe(e,n={}){const t={listener:e,kinds:new Set(n.kinds??["mail","node"]),onListenStart:n.onListenStart};return this.subscribers.add(t),this.listener!==void 0&&t.onListenStart!==void 0&&queueMicrotask(()=>{this.subscribers.has(t)&&this.callListenStart(t)}),()=>{this.subscribers.delete(t)}}markMail(e){this.mark("mail",e)}markNode(e){this.mark("node",e)}markSignal(e,n){this.mark(e,n)}mark(e,n){const t=`${e}:${n}`;this.dirty.has(t)||this.dirty.set(t,{id:n,kind:e}),this.scheduled??=setImmediate(()=>this.flush())}flush(){this.scheduled=void 0;const e=[...this.dirty.values()];this.dirty.clear();for(const{id:n,kind:t}of e)for(const s of this.subscribers)if(s.kinds.has(t))try{s.listener(n,t)}catch{}}callListenStart(e){try{e.onListenStart?.()}catch{}}async listen(e){this.listener!==void 0||this.listening!==void 0||(this.url=e,this.listening=this.connectListener().finally(()=>{this.listening=void 0}),await this.listening)}listener;listening;url;reconnect;check;async connectListener(){const e=new d.Client({connectionString:this.url,application_name:`crtr-signal-${process.pid}`,keepAlive:!0});e.on("notification",t=>{const s=t.payload??"",c=s.indexOf(":");if(c<0)return;const o=s.slice(0,c);f.has(o)&&this.mark(o,s.slice(c+1))});const n=r(()=>{this.listener===e&&(this.dropListener(e),this.scheduleReconnect())},"lost");e.on("error",n),e.on("end",n);try{await e.connect(),await e.query("LISTEN crtr_signal")}catch(t){throw e.removeAllListeners(),e.on("error",()=>{}),e.end().catch(()=>{}),t}if(this.url===void 0){e.removeAllListeners(),e.on("error",()=>{}),e.end().catch(()=>{});return}this.listener=e,this.startChecks(e,n);for(const t of[...this.subscribers])this.callListenStart(t)}startChecks(e,n){this.stopChecks(),this.check=setInterval(()=>{if(this.listener!==e)return;let t;const s=new Promise((c,o)=>{t=setTimeout(()=>o(new Error("listen connection check timed out")),this.checkTimeoutMs)});Promise.race([e.query("SELECT 1"),s]).catch(()=>n()).finally(()=>clearTimeout(t))},this.checkIntervalMs),this.check.unref()}stopChecks(){this.check!==void 0&&clearInterval(this.check),this.check=void 0}dropListener(e){this.listener=void 0,this.stopChecks(),e.removeAllListeners(),e.on("error",()=>{}),e.end().catch(()=>{})}scheduleReconnect(){this.url===void 0||this.reconnect!==void 0||(this.reconnect=setTimeout(()=>{this.reconnect=void 0,this.url!==void 0&&(this.listening=this.connectListener().catch(()=>this.scheduleReconnect()).finally(()=>{this.listening=void 0}))},this.reconnectMs))}close(){this.url=void 0,this.reconnect!==void 0&&clearTimeout(this.reconnect),this.reconnect=void 0,this.stopChecks();const e=this.listener;return this.listener=void 0,this.scheduled!==void 0&&clearImmediate(this.scheduled),this.scheduled=void 0,this.dirty.clear(),this.subscribers.clear(),e===void 0?Promise.resolve():(e.removeAllListeners("notification"),e.removeAllListeners("error"),e.on("error",()=>{}),e.end().catch(()=>{}))}}const b=new k;export{u as BROKER_SIGNAL_KINDS,l as BROKER_SIGNAL_NODE_COLUMNS,k as BrokerSignalBus,b as brokerSignalBus,p as cachedBrokerNodeFields};
@@ -1 +1 @@
1
- var p=Object.defineProperty;var o=(r,e)=>p(r,"name",{value:e,configurable:!0});import{envHomeOverride as m}from"../../shared/env.js";import{loadProfileManifest as h,lookupProfile as v}from"../../core/profiles/manifest.js";import{readProfileEnvVars as g}from"../../core/profiles/env-store.js";import{readAppEnvVars as R}from"../../core/profiles/app-env-store.js";import{ADMITTED_HOST_PATH_ENV as f,buildOperationalEnvBase as w}from"../../core/runtime/spawn-env.js";import{getNode as c}from"../../core/canvas/canvas.js";import{contextDir as y}from"../../core/canvas/paths.js";import{appendRunEvent as O}from"../../core/runs/events.js";import{payerOfGrant as b}from"../../core/grants/mirror.js";import{scopeAllowed as E}from"../../core/scopes.js";import{cronPrincipalOf as C}from"./authority.js";import{personId as x}from"../../core/canvas/person.js";function _(r){if(r.profile==null)return null;let e;try{e=v(r.profile)}catch{return null}if(e.kind==="resolved")return e.entry.profileId;if(e.kind==="absent")return null;throw new l(`the armed profile "${r.profile}" is still on this host but its manifest could not be read (${e.manifestPath}) \u2014 running as root would silently give this cron a different identity, env store, and purview. Repair the manifest, or re-arm the cron under a profile that resolves.`)}o(_,"resolveLiveProfile");function B(r){return{...C(r),cron_id:r.cron_id,origin_run_id:r.origin_run_id,isolated:r.isolated,creator:r.created_by}}o(B,"cronBirthPrincipalOf");async function V(r,e){const n=r.creator===null?null:await c(r.creator);return n!==null&&r.origin_run_id!==null&&n.run_id===r.origin_run_id&&(await c(r.origin_run_id))?.deleting_at==null&&n.grantee===r.grantee&&(n.sub??x())===r.sub&&(n.grant_id??null)===r.grant_id&&(r.scopes===null?n.scopes==null:r.scopes.every(t=>E(n.scopes,t)))?{parent:n.node_id,...r.scopes===null?{}:{scopes:[...r.scopes]}}:{root:!0,rootLifecycle:"terminal",...r.scopes===null?{}:{scopes:[...r.scopes]},run:{grantee:r.grantee,sub:r.sub,grant_id:r.grant_id,client_id:null,metadata_json:JSON.stringify(r.cron_id===null?{exec:!0}:{cron_id:r.cron_id}),run_id:e,event_cursor:1,usage_json:JSON.stringify({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0})},withinCreate:o(async(t,a)=>{await O(t,e,e,"run.started",{scopes:a.scopes??null,payer:await b(r.grant_id),usage:JSON.parse(a.usage_json)})},"withinCreate")}}o(V,"cronBirthPlacement");function F(r){try{return _(r)}catch{return null}}o(F,"resolveLiveProfileOrRoot");function G(r){if(r.profile!=null)try{return h(r.profile).manifest.name}catch{return}}o(G,"resolveLiveProfileName");class l extends Error{static{o(this,"ArmedContextError")}constructor(e){super(e),this.name="ArmedContextError"}}function N(r){if(r==null)return{};let e;try{e=JSON.parse(r)}catch(i){throw new l(`the armed env snapshot is not valid JSON (${i.message}) \u2014 this cron's stored env_json is corrupt, so the run would silently drop every armed variable. Re-arm the cron with a valid --env.`)}if(typeof e!="object"||e===null||Array.isArray(e))throw new l("the armed env snapshot is not a JSON object \u2014 this cron's stored env_json is corrupt. Re-arm the cron with a valid --env.");const n={};for(const[i,t]of Object.entries(e)){if(typeof t!="string")throw new l(`the armed env snapshot's value for "${i}" is not a string \u2014 this cron's stored env_json is corrupt, so the run would silently drop that variable. Re-arm the cron with a valid --env.`);n[i]=t}return n}o(N,"parseArmedEnv");async function U(r,e){const n=N(r.env_json),i=r.grantee==="app:terminal",t=i?_(r):null,a=w({targetCwd:r.cwd,targetProfileId:t,skipSpawnAllow:!i}),s={...a,...R(r.grantee),...i?g(t):{},...n,...t!==null?{CRTR_PROFILE_ID:t}:{},CRTR_CRON_ID:r.cron_id,CRTR_CRON_NAME:r.name,CRTR_RUN_ID:e},u=m();if(u!==void 0&&u!==""&&(s.CRTR_HOME=u),r.anchor_node!=null){s.CRTR_ANCHOR=r.anchor_node;const d=await c(r.anchor_node);d!==null&&(s.CRTR_CONTEXT_DIR=y(d.node_id,d.grantee))}return s[f]=a[f],{profileId:t,env:s}}o(U,"buildArmedRunContext");export{l as ArmedContextError,U as buildArmedRunContext,V as cronBirthPlacement,B as cronBirthPrincipalOf,_ as resolveLiveProfile,G as resolveLiveProfileName,F as resolveLiveProfileOrRoot};
1
+ var p=Object.defineProperty;var o=(e,r)=>p(e,"name",{value:r,configurable:!0});import{envHomeOverride as m}from"../../shared/env.js";import{loadProfileManifest as h,lookupProfile as v}from"../../core/profiles/manifest.js";import{localEnvironmentSecrets as g}from"../../core/secrets/environment-owner.js";import{ADMITTED_HOST_PATH_ENV as f,buildOperationalEnvBase as w}from"../../core/runtime/spawn-env.js";import{getNode as c}from"../../core/canvas/canvas.js";import{contextDir as R}from"../../core/canvas/paths.js";import{appendRunEvent as y}from"../../core/runs/events.js";import{payerOfGrant as O}from"../../core/grants/mirror.js";import{scopeAllowed as b}from"../../core/scopes.js";import{cronPrincipalOf as C}from"./authority.js";import{personId as E}from"../../core/canvas/person.js";function _(e){if(e.profile==null)return null;let r;try{r=v(e.profile)}catch{return null}if(r.kind==="resolved")return r.entry.profileId;if(r.kind==="absent")return null;throw new l(`the armed profile "${e.profile}" is still on this host but its manifest could not be read (${r.manifestPath}) \u2014 running as root would silently give this cron a different identity, env store, and purview. Repair the manifest, or re-arm the cron under a profile that resolves.`)}o(_,"resolveLiveProfile");function M(e){return{...C(e),cron_id:e.cron_id,origin_run_id:e.origin_run_id,isolated:e.isolated,creator:e.created_by}}o(M,"cronBirthPrincipalOf");async function $(e,r){const n=e.creator===null?null:await c(e.creator);return n!==null&&e.origin_run_id!==null&&n.run_id===e.origin_run_id&&(await c(e.origin_run_id))?.deleting_at==null&&n.grantee===e.grantee&&(n.sub??E())===e.sub&&(n.grant_id??null)===e.grant_id&&(e.scopes===null?n.scopes==null:e.scopes.every(t=>b(n.scopes,t)))?{parent:n.node_id,...e.scopes===null?{}:{scopes:[...e.scopes]}}:{root:!0,rootLifecycle:"terminal",...e.scopes===null?{}:{scopes:[...e.scopes]},run:{grantee:e.grantee,sub:e.sub,grant_id:e.grant_id,client_id:null,metadata_json:JSON.stringify(e.cron_id===null?{exec:!0}:{cron_id:e.cron_id}),run_id:r,event_cursor:1,usage_json:JSON.stringify({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0})},withinCreate:o(async(t,a)=>{await y(t,r,r,"run.started",{scopes:a.scopes??null,payer:await O(e.grant_id),usage:JSON.parse(a.usage_json)})},"withinCreate")}}o($,"cronBirthPlacement");function B(e){try{return _(e)}catch{return null}}o(B,"resolveLiveProfileOrRoot");function F(e){if(e.profile!=null)try{return h(e.profile).manifest.name}catch{return}}o(F,"resolveLiveProfileName");class l extends Error{static{o(this,"ArmedContextError")}constructor(r){super(r),this.name="ArmedContextError"}}function x(e){if(e==null)return{};let r;try{r=JSON.parse(e)}catch(i){throw new l(`the armed env snapshot is not valid JSON (${i.message}) \u2014 this cron's stored env_json is corrupt, so the run would silently drop every armed variable. Re-arm the cron with a valid --env.`)}if(typeof r!="object"||r===null||Array.isArray(r))throw new l("the armed env snapshot is not a JSON object \u2014 this cron's stored env_json is corrupt. Re-arm the cron with a valid --env.");const n={};for(const[i,t]of Object.entries(r)){if(typeof t!="string")throw new l(`the armed env snapshot's value for "${i}" is not a string \u2014 this cron's stored env_json is corrupt, so the run would silently drop that variable. Re-arm the cron with a valid --env.`);n[i]=t}return n}o(x,"parseArmedEnv");async function G(e,r){const n=x(e.env_json),i=e.grantee==="app:terminal",t=i?_(e):null,a=w({targetCwd:e.cwd,targetProfileId:t,skipSpawnAllow:!i}),s={...a,...await g(t,e.grantee),...n,...t!==null?{CRTR_PROFILE_ID:t}:{},CRTR_CRON_ID:e.cron_id,CRTR_CRON_NAME:e.name,CRTR_RUN_ID:r},u=m();if(u!==void 0&&u!==""&&(s.CRTR_HOME=u),e.anchor_node!=null){s.CRTR_ANCHOR=e.anchor_node;const d=await c(e.anchor_node);d!==null&&(s.CRTR_CONTEXT_DIR=R(d.node_id,d.grantee))}return s[f]=a[f],{profileId:t,env:s}}o(G,"buildArmedRunContext");export{l as ArmedContextError,G as buildArmedRunContext,$ as cronBirthPlacement,M as cronBirthPrincipalOf,_ as resolveLiveProfile,F as resolveLiveProfileName,B as resolveLiveProfileOrRoot};
@@ -1 +1 @@
1
- var v=Object.defineProperty;var d=(e,r)=>v(e,"name",{value:r,configurable:!0});import{randomUUID as m}from"node:crypto";import{setTimeout as w}from"node:timers/promises";import{CRON_DEATH_POLL_MS as S,CRON_KILL_GRACE_MS as A,EMPTY_STDOUT_SHA256 as f}from"./capture.js";import{cronDaemonInstanceId as C}from"./live-runs.js";import{crashedOneShotSummary as L,escalateFailure as y}from"./escalation.js";import{settleRun as h}from"./settle.js";import{runningCrons as H,stopLeasedCronRun as p,setCronState as M,verifyLeasedCronRun as l}from"../../core/canvas/crons.js";import{emitEvent as c}from"../../core/events/emit.js";const i=new Set;async function K(e,r={}){for(const t of await H()){if(t.run_lease_owner===C||i.has(t.cron_id))continue;if(t.run_pid===null||!l(t)){i.add(t.cron_id),x(t,e,r);continue}const a=t.run_started_at!=null?new Date(t.run_started_at).getTime():e;if(e-a<t.run_timeout_s*1e3)continue;const o={runId:t.run_execution_id??m(),startedAtIso:t.run_started_at??new Date(e).toISOString(),exitCode:-1,timedOut:!0,runTimeoutS:t.run_timeout_s,processErrorMessage:null,stdoutHead:"",stdoutSha256:f,stderrHead:`run timed out after ${t.run_timeout_s}s (recovered stale lease from a prior daemon instance)`};i.add(t.cron_id);const n=(async()=>{await p(t),await g(t),await h(t,{outOfBand:!1,outcome:{kind:"disposed",ctx:o},record:{runId:o.runId,startedAtIso:o.startedAtIso,finishedAtMs:e,durationMs:e-a,exitCode:-1,stdoutHead:null,stderrHead:o.stderrHead},lease:{kind:"clear"}})})().finally(()=>i.delete(t.cron_id));r.register?.(n),n.catch(s=>{c({level:"error",event:"cron.stale_lease.settle_failed",error:s,fields:{cron_id:t.cron_id}})})}}d(K,"recoverStaleCronLeases");async function g(e){if(!await I(e)&&(await p(e,"SIGKILL"),!await I(e)))throw new Error(`cron ${e.cron_id} run ${e.run_pid} survived SIGKILL`)}d(g,"awaitLeasedRunDeath");async function I(e){const r=Date.now()+A;for(;Date.now()<r;){if(!l(e))return!0;await w(S)}return!l(e)}d(I,"pollLeasedRunGone");function x(e,r,t){const a=e.recur==null,o=e.run_started_at!=null?new Date(e.run_started_at).getTime():r,n=e.run_pid===null?"the daemon life that claimed this run ended before its launch pid was recorded \u2014 the command may have started and its outcome is unknown":"the daemon life that owned this run ended before it settled \u2014 the process is gone and its outcome was never observed",s={runId:e.run_execution_id??m(),startedAtIso:e.run_started_at??new Date(r).toISOString(),exitCode:null,timedOut:!1,runTimeoutS:e.run_timeout_s,processErrorMessage:n,stdoutHead:"",stdoutSha256:f,stderrHead:`run outcome unobserved: ${n}`},_=(async()=>{let u=`crashed: ${n}${a?"":" \u2014 the next occurrence is unaffected"}`;a&&(await M(e.cron_id,"paused"),u=e.on_output==="silent"?`crashed: ${n} \u2014 one-shot paused, not re-fired (silent: not escalated)`:`crashed one-shot: ${await y(e,s,L(n))}`),await h(e,{outOfBand:!1,outcome:{kind:"stated",delivered:u,paused:a},record:{runId:s.runId,startedAtIso:s.startedAtIso,finishedAtMs:r,durationMs:Math.max(0,r-o),exitCode:null,stdoutHead:null,stderrHead:s.stderrHead},lease:"release"}),c({level:"warn",event:"cron.run.unobserved",fields:{cron_id:e.cron_id,name:e.name,one_shot:a}})})().finally(()=>i.delete(e.cron_id));t.register?.(_),_.catch(u=>{c({level:"error",event:"cron.crashed_run.settle_failed",error:u,fields:{cron_id:e.cron_id}})})}d(x,"settleCrashedRun");export{K as recoverStaleCronLeases};
1
+ var v=Object.defineProperty;var d=(e,r)=>v(e,"name",{value:r,configurable:!0});import{randomUUID as m}from"node:crypto";import{setTimeout as I}from"node:timers/promises";import{CRON_DEATH_POLL_MS as S,CRON_KILL_GRACE_MS as A,EMPTY_STDOUT_SHA256 as f}from"./capture.js";import{cronDaemonInstanceId as C,cronOwnerAlive as L,ensureCronOwnerLease as O}from"./live-runs.js";import{crashedOneShotSummary as y,escalateFailure as H}from"./escalation.js";import{settleRun as h}from"./settle.js";import{runningCrons as M,stopLeasedCronRun as w,setCronState as g,verifyLeasedCronRun as l}from"../../core/canvas/crons.js";import{emitEvent as c}from"../../core/events/emit.js";const i=new Set;async function B(e,r={}){await O();for(const t of await M()){if(t.run_lease_owner===C||i.has(t.cron_id)||t.run_lease_owner!==null&&await L(t.run_lease_owner))continue;if(t.run_pid===null||!l(t)){i.add(t.cron_id),D(t,e,r);continue}const a=t.run_started_at!=null?new Date(t.run_started_at).getTime():e;if(e-a<t.run_timeout_s*1e3)continue;const o={runId:t.run_execution_id??m(),startedAtIso:t.run_started_at??new Date(e).toISOString(),exitCode:-1,timedOut:!0,runTimeoutS:t.run_timeout_s,processErrorMessage:null,stdoutHead:"",stdoutSha256:f,stderrHead:`run timed out after ${t.run_timeout_s}s (recovered stale lease from a prior daemon instance)`};i.add(t.cron_id);const n=(async()=>{await w(t),await x(t),await h(t,{outOfBand:!1,outcome:{kind:"disposed",ctx:o},record:{runId:o.runId,startedAtIso:o.startedAtIso,finishedAtMs:e,durationMs:e-a,exitCode:-1,stdoutHead:null,stderrHead:o.stderrHead},lease:{kind:"clear"}})})().finally(()=>i.delete(t.cron_id));r.register?.(n),n.catch(s=>{c({level:"error",event:"cron.stale_lease.settle_failed",error:s,fields:{cron_id:t.cron_id}})})}}d(B,"recoverStaleCronLeases");async function x(e){if(!await p(e)&&(await w(e,"SIGKILL"),!await p(e)))throw new Error(`cron ${e.cron_id} run ${e.run_pid} survived SIGKILL`)}d(x,"awaitLeasedRunDeath");async function p(e){const r=Date.now()+A;for(;Date.now()<r;){if(!l(e))return!0;await I(S)}return!l(e)}d(p,"pollLeasedRunGone");function D(e,r,t){const a=e.recur==null,o=e.run_started_at!=null?new Date(e.run_started_at).getTime():r,n=e.run_pid===null?"the daemon life that claimed this run ended before its launch pid was recorded \u2014 the command may have started and its outcome is unknown":"the daemon life that owned this run ended before it settled \u2014 the process is gone and its outcome was never observed",s={runId:e.run_execution_id??m(),startedAtIso:e.run_started_at??new Date(r).toISOString(),exitCode:null,timedOut:!1,runTimeoutS:e.run_timeout_s,processErrorMessage:n,stdoutHead:"",stdoutSha256:f,stderrHead:`run outcome unobserved: ${n}`},_=(async()=>{let u=`crashed: ${n}${a?"":" \u2014 the next occurrence is unaffected"}`;a&&(await g(e.cron_id,"paused"),u=e.on_output==="silent"?`crashed: ${n} \u2014 one-shot paused, not re-fired (silent: not escalated)`:`crashed one-shot: ${await H(e,s,y(n))}`),await h(e,{outOfBand:!1,outcome:{kind:"stated",delivered:u,paused:a},record:{runId:s.runId,startedAtIso:s.startedAtIso,finishedAtMs:r,durationMs:Math.max(0,r-o),exitCode:null,stdoutHead:null,stderrHead:s.stderrHead},lease:"release"}),c({level:"warn",event:"cron.run.unobserved",fields:{cron_id:e.cron_id,name:e.name,one_shot:a}})})().finally(()=>i.delete(e.cron_id));t.register?.(_),_.catch(u=>{c({level:"error",event:"cron.crashed_run.settle_failed",error:u,fields:{cron_id:e.cron_id}})})}d(D,"settleCrashedRun");export{B as recoverStaleCronLeases};
@@ -1,5 +1,11 @@
1
1
  import type { CronRefusalReason } from './authority.js';
2
2
  export declare const cronDaemonInstanceId: `${string}-${string}-${string}-${string}-${string}`;
3
+ export declare const CRON_OWNER_HEARTBEAT_TTL_MS = 15000;
4
+ /** Call before naming this process the owner of a cron run lease. Keeps the heartbeat alive from then on; the heartbeat stops
5
+ * itself if a beat fails, and the next call restarts it. */
6
+ export declare function ensureCronOwnerLease(): Promise<void>;
7
+ /** Whether `owner` (another process's instance id) has a live heartbeat. */
8
+ export declare function cronOwnerAlive(owner: string): Promise<boolean>;
3
9
  export interface LiveRun {
4
10
  runId: string;
5
11
  replaced: boolean;
@@ -1 +1 @@
1
- var i=Object.defineProperty;var n=(e,t)=>i(e,"name",{value:t,configurable:!0});import{randomUUID as c}from"node:crypto";const x=c(),o=new Map;function d(e){return o.get(e)}n(d,"liveRunFor");function l(e,t){o.set(e,t)}n(l,"registerLiveRun");function p(e,t){const r=o.get(e);r!==void 0&&r.runId===t&&o.delete(e)}n(p,"clearLiveRun");function a(e,t){const r=o.get(e);return r===void 0||r.runId!==t?!1:(r.selfCanceled=!0,!0)}n(a,"markSelfCanceledCronRun");const u=new Map;function v(e){u.set(e.executionId,e)}n(v,"registerCronExecution");function g(e){return u.get(e)}n(g,"cronExecutionFor");function m(e){u.delete(e)}n(m,"dropCronExecution");function C(){return[...u.values()]}n(C,"cronExecutions");export{p as clearLiveRun,x as cronDaemonInstanceId,g as cronExecutionFor,C as cronExecutions,m as dropCronExecution,d as liveRunFor,a as markSelfCanceledCronRun,v as registerCronExecution,l as registerLiveRun};
1
+ var d=Object.defineProperty;var n=(e,t)=>d(e,"name",{value:t,configurable:!0});import{randomUUID as x}from"node:crypto";import{claimReconcilerLease as E,renewReconcilerLease as R}from"../../core/canvas/reconciler-leases.js";import{openDb as w}from"../../core/canvas/db.js";const a=x(),u="cron-owner",f=15e3,l=5e3;let o,s=0;async function p(){if(await R(u,a,a,f)){s=Date.now();return}await E(u,a,a,f),s=Date.now()}n(p,"beat");async function C(){o!==void 0&&Date.now()-s<l||(await p(),o===void 0&&(o=setInterval(()=>{p().catch(()=>{clearInterval(o),o=void 0})},l),o.unref()))}n(C,"ensureCronOwnerLease");async function O(e){return await(await w()).prepare("SELECT 1 AS alive FROM reconciler_leases WHERE lane = ? AND key = ? AND expires_at > ?").get(u,e,new Date().toISOString())!==void 0}n(O,"cronOwnerAlive");const i=new Map;function D(e){return i.get(e)}n(D,"liveRunFor");function N(e,t){i.set(e,t)}n(N,"registerLiveRun");function g(e,t){const r=i.get(e);r!==void 0&&r.runId===t&&i.delete(e)}n(g,"clearLiveRun");function I(e,t){const r=i.get(e);return r===void 0||r.runId!==t?!1:(r.selfCanceled=!0,!0)}n(I,"markSelfCanceledCronRun");const c=new Map;function L(e){c.set(e.executionId,e)}n(L,"registerCronExecution");function S(e){return c.get(e)}n(S,"cronExecutionFor");function T(e){c.delete(e)}n(T,"dropCronExecution");function M(){return[...c.values()]}n(M,"cronExecutions");export{f as CRON_OWNER_HEARTBEAT_TTL_MS,g as clearLiveRun,a as cronDaemonInstanceId,S as cronExecutionFor,M as cronExecutions,O as cronOwnerAlive,T as dropCronExecution,C as ensureCronOwnerLease,D as liveRunFor,I as markSelfCanceledCronRun,L as registerCronExecution,N as registerLiveRun};
@@ -1 +1 @@
1
- var R=Object.defineProperty;var i=(r,n)=>R(r,"name",{value:n,configurable:!0});import{createServer as F}from"node:http";import{chmodSync as x,existsSync as g,mkdirSync as A,readdirSync as O,rmSync as _,unlinkSync as z}from"node:fs";import{dirname as H,join as f}from"node:path";import{peerUid as M}from"../../native/linux.js";import{nodeRunDir as s}from"../../core/canvas/paths.js";import{readConfig as U,readMergedLaunchConfig as j}from"../../core/config.js";import{readRotationConfig as q}from"../../core/subscription-state.js";import{runningCrons as v}from"../../core/canvas/crons.js";import{emitEvent as u}from"../../core/events/emit.js";import{cronLaunchId as a}from"../../core/spaces/process.js";import{ensureAppSpace as B}from"../../core/spaces/space.js";import{launcherPresent as y,listSandboxes as b}from"../../core/grants/sandboxes.js";import{uidForApp as J}from"../../core/spaces/users.js";import{setModeBeneath as L,writeFileBeneath as N}from"../../core/spaces/open-beneath.js";import{cronExecutionFor as w,cronExecutions as D,dropCronExecution as S,registerCronExecution as C}from"./live-runs.js";let p=null;function sr(r){p=r}i(sr,"setCronRequestHandler");const E=i(r=>process.platform==="linux"?M(r.socket):null,"linuxPeerUid");let k=E;function dr(r){k=r??E}i(dr,"__setCronPeerUidForTest");const m=new Map;function T(r){return f(s(a(r)),"cli.sock")}i(T,"cronSocketPath");function W(r){const n={class:"cron",app:r.grantee,sub:r.sub,grant:r.grantId,scopes:r.scopes,execution:r.executionId,from_exec:r.fromExec,origin_run:r.originRunId,isolated:r.isolated,anchor:r.anchor,uid:r.uid};return r.kind==="cron"?{...n,kind:"cron",cron:r.cronId}:{...n,kind:"exec",cron:null}}i(W,"cronPrincipalClassOf");function d(r,n,t,o){r.writeHead(n,{"content-type":"application/json; charset=utf-8"}),r.end(JSON.stringify({error:{code:t,message:o,type:"invalid_request_error",origin:"runtime",retryable:!1}}))}i(d,"refuse");function G(r){return(n,t)=>{try{const o=w(r);if(o===void 0){d(t,401,"unauthorized","this cron execution has ended");return}const e=k(n);if(e!==null&&e!==o.uid){u({event:"api.principal.denied",level:"warn",fields:{method:n.method,execution:r}}),d(t,401,"unauthorized","missing or invalid bearer token");return}if(p===null){d(t,503,"daemon_unavailable","the API server is not running");return}p(n,t,W(o))}catch(o){u({event:"api.principal.failed",level:"error",error:o instanceof Error?o:new Error(String(o))}),t.headersSent||d(t,401,"unauthorized","missing or invalid bearer token")}}}i(G,"onCronRequest");async function I(r){const n=T(r);g(n)&&z(n);const t=F(G(r));await new Promise((o,e)=>{t.once("error",e),t.listen(n,()=>{t.off("error",e),o()})}),x(n,438),m.set(r,t)}i(I,"listen");async function lr(r,n,t){const o=s(a(r.executionId));C(r);try{A(o,{recursive:!0,mode:504}),x(o,504);const e={user:U("user"),launch:j(n,t),rotation:q()},c=f(o,"cron-launch.json");if(N(o,c,JSON.stringify({configSnapshot:e}),{atomic:!0,mode:416}),y()){const l=await B(r.grantee);L(o,c,416,{uid:process.getuid(),gid:l.gid})}await I(r.executionId)}catch(e){throw P(r.executionId),e}}i(lr,"openCronExecution");function P(r){S(r);const n=m.get(r);m.delete(r),n!==void 0&&(n.closeAllConnections(),n.close());try{_(s(a(r)),{recursive:!0,force:!0})}catch{}}i(P,"closeCronExecution");let h=Promise.resolve();function fr(){return h}i(fr,"cronExecutionsReady");function pr(r=b){return h=(async()=>{if(r===b&&!y())return;const t=new Set((await r()).map(e=>e.nodeId));for(const e of await v()){const c=e.run_execution_id;if(!(c==null||!t.has(a(c))||w(c)!==void 0))try{C({executionId:c,kind:"cron",cronId:e.cron_id,fromExec:e.armed_from_exec,grantee:e.grantee,sub:e.sub,grantId:e.grant_id,scopes:e.scopes,originRunId:e.origin_run_id,isolated:e.isolated,anchor:e.anchor_node,uid:await J(e.grantee),rebuilt:!0,refusal:null}),await I(c),u({level:"info",event:"cron.execution.rebuilt",fields:{cron_id:e.cron_id,execution_id:c}})}catch(l){S(c),u({level:"error",event:"cron.execution.rebuild_failed",error:l,fields:{cron_id:e.cron_id,execution_id:c}})}}if(process.platform!=="linux")return;const o=H(s(a("x")));if(g(o)){for(const e of O(o))if(!(!e.startsWith("cron-")||t.has(e)))try{_(f(o,e),{recursive:!0,force:!0})}catch{}}})().catch(t=>{u({level:"error",event:"cron.execution.rebuild_failed",error:t})}),h}i(pr,"rebuildCronExecutions");async function mr(){const r=new Set((await v()).map(n=>n.run_execution_id));for(const n of D())n.rebuilt&&!r.has(n.executionId)&&P(n.executionId)}i(mr,"reapRebuiltCronExecutions");export{dr as __setCronPeerUidForTest,P as closeCronExecution,fr as cronExecutionsReady,W as cronPrincipalClassOf,T as cronSocketPath,lr as openCronExecution,mr as reapRebuiltCronExecutions,pr as rebuildCronExecutions,sr as setCronRequestHandler};
1
+ var R=Object.defineProperty;var i=(r,n)=>R(r,"name",{value:n,configurable:!0});import{createServer as F}from"node:http";import{chmodSync as x,existsSync as g,mkdirSync as A,readdirSync as O,rmSync as _,unlinkSync as z}from"node:fs";import{dirname as H,join as f}from"node:path";import{peerUid as M}from"../../native/linux.js";import{nodeRunDir as s}from"../../core/canvas/paths.js";import{readConfig as U,readMergedLaunchConfig as j}from"../../core/config.js";import{loadRotationConfig as q}from"../../core/subscription-state.js";import{runningCrons as v}from"../../core/canvas/crons.js";import{emitEvent as u}from"../../core/events/emit.js";import{cronLaunchId as a}from"../../core/spaces/process.js";import{ensureAppSpace as B}from"../../core/spaces/space.js";import{launcherPresent as y,listSandboxes as w}from"../../core/grants/sandboxes.js";import{uidForApp as J}from"../../core/spaces/users.js";import{setModeBeneath as L,writeFileBeneath as N}from"../../core/spaces/open-beneath.js";import{cronExecutionFor as b,cronExecutions as D,dropCronExecution as S,registerCronExecution as C}from"./live-runs.js";let p=null;function sr(r){p=r}i(sr,"setCronRequestHandler");const E=i(r=>process.platform==="linux"?M(r.socket):null,"linuxPeerUid");let k=E;function dr(r){k=r??E}i(dr,"__setCronPeerUidForTest");const m=new Map;function T(r){return f(s(a(r)),"cli.sock")}i(T,"cronSocketPath");function W(r){const n={class:"cron",app:r.grantee,sub:r.sub,grant:r.grantId,scopes:r.scopes,execution:r.executionId,from_exec:r.fromExec,origin_run:r.originRunId,isolated:r.isolated,anchor:r.anchor,uid:r.uid};return r.kind==="cron"?{...n,kind:"cron",cron:r.cronId}:{...n,kind:"exec",cron:null}}i(W,"cronPrincipalClassOf");function d(r,n,t,o){r.writeHead(n,{"content-type":"application/json; charset=utf-8"}),r.end(JSON.stringify({error:{code:t,message:o,type:"invalid_request_error",origin:"runtime",retryable:!1}}))}i(d,"refuse");function G(r){return(n,t)=>{try{const o=b(r);if(o===void 0){d(t,401,"unauthorized","this cron execution has ended");return}const e=k(n);if(e!==null&&e!==o.uid){u({event:"api.principal.denied",level:"warn",fields:{method:n.method,execution:r}}),d(t,401,"unauthorized","missing or invalid bearer token");return}if(p===null){d(t,503,"daemon_unavailable","the API server is not running");return}p(n,t,W(o))}catch(o){u({event:"api.principal.failed",level:"error",error:o instanceof Error?o:new Error(String(o))}),t.headersSent||d(t,401,"unauthorized","missing or invalid bearer token")}}}i(G,"onCronRequest");async function I(r){const n=T(r);g(n)&&z(n);const t=F(G(r));await new Promise((o,e)=>{t.once("error",e),t.listen(n,()=>{t.off("error",e),o()})}),x(n,438),m.set(r,t)}i(I,"listen");async function lr(r,n,t){const o=s(a(r.executionId));C(r);try{A(o,{recursive:!0,mode:504}),x(o,504);const e={user:U("user"),launch:j(n,t),rotation:await q()},c=f(o,"cron-launch.json");if(N(o,c,JSON.stringify({configSnapshot:e}),{atomic:!0,mode:416}),y()){const l=await B(r.grantee);L(o,c,416,{uid:process.getuid(),gid:l.gid})}await I(r.executionId)}catch(e){throw P(r.executionId),e}}i(lr,"openCronExecution");function P(r){S(r);const n=m.get(r);m.delete(r),n!==void 0&&(n.closeAllConnections(),n.close());try{_(s(a(r)),{recursive:!0,force:!0})}catch{}}i(P,"closeCronExecution");let h=Promise.resolve();function fr(){return h}i(fr,"cronExecutionsReady");function pr(r=w){return h=(async()=>{if(r===w&&!y())return;const t=new Set((await r()).map(e=>e.nodeId));for(const e of await v()){const c=e.run_execution_id;if(!(c==null||!t.has(a(c))||b(c)!==void 0))try{C({executionId:c,kind:"cron",cronId:e.cron_id,fromExec:e.armed_from_exec,grantee:e.grantee,sub:e.sub,grantId:e.grant_id,scopes:e.scopes,originRunId:e.origin_run_id,isolated:e.isolated,anchor:e.anchor_node,uid:await J(e.grantee),rebuilt:!0,refusal:null}),await I(c),u({level:"info",event:"cron.execution.rebuilt",fields:{cron_id:e.cron_id,execution_id:c}})}catch(l){S(c),u({level:"error",event:"cron.execution.rebuild_failed",error:l,fields:{cron_id:e.cron_id,execution_id:c}})}}if(process.platform!=="linux")return;const o=H(s(a("x")));if(g(o)){for(const e of O(o))if(!(!e.startsWith("cron-")||t.has(e)))try{_(f(o,e),{recursive:!0,force:!0})}catch{}}})().catch(t=>{u({level:"error",event:"cron.execution.rebuild_failed",error:t})}),h}i(pr,"rebuildCronExecutions");async function mr(){const r=new Set((await v()).map(n=>n.run_execution_id));for(const n of D())n.rebuilt&&!r.has(n.executionId)&&P(n.executionId)}i(mr,"reapRebuiltCronExecutions");export{dr as __setCronPeerUidForTest,P as closeCronExecution,fr as cronExecutionsReady,W as cronPrincipalClassOf,T as cronSocketPath,lr as openCronExecution,mr as reapRebuiltCronExecutions,pr as rebuildCronExecutions,sr as setCronRequestHandler};
@@ -1,2 +1,2 @@
1
- var N=Object.defineProperty;var i=(e,t)=>N(e,"name",{value:t,configurable:!0});import{randomUUID as Y}from"node:crypto";import{CRON_OUTPUT_HEAD_BYTES as E,EMPTY_STDOUT_SHA256 as q,formatCronOutputHead as A}from"./cron/capture.js";import{clearLiveRun as g,cronDaemonInstanceId as $,registerLiveRun as J}from"./cron/live-runs.js";import{closeCronExecution as V,openCronExecution as X}from"./cron/run-socket.js";import{appProcessSandboxed as z}from"../core/spaces/process.js";import{nodeAgentDir as G}from"../core/canvas/paths.js";import{uidForApp as K}from"../core/spaces/users.js";import{buildArmedRunContext as Q}from"./cron/armed-context.js";import{ensureCronFolder as W}from"./cron/folders.js";import{checkCronFire as Z,cronPrincipalOf as j}from"./cron/authority.js";import{refusalError as ee}from"../core/ledger/index.js";import{startRefusal as te}from"../core/runtime/start-check.js";import{decideHeldSettle as re,EXIT_HELD as ne,HELD_DELIVERED as oe}from"./cron/held.js";import{settleRun as O}from"./cron/settle.js";import{applyOverlapPolicies as ae,expireDueCrons as se}from"./cron/passes.js";import{dueClockCrons as de,acquireCronRunLease as ie,releaseCronRunLease as le,attachCronRunPid as ue,advanceCron as ce,setCronState as fe}from"../core/canvas/crons.js";import{emitEvent as L}from"../core/events/emit.js";import{runAppCommand as me}from"../core/runtime/run-command.js";import{nextSlotAfter as pe}from"../core/wake.js";import{CRON_OUTPUT_HEAD_BYTES as Ne}from"./cron/capture.js";import{cronDaemonInstanceId as qe,markSelfCanceledCronRun as Je}from"./cron/live-runs.js";import{recoverStaleCronLeases as Xe}from"./cron/lease-recovery.js";async function he(e,t){const{runId:a,outOfBand:s,startedAtIso:u,finishedAtMs:c,code:d,timedOut:n,processError:f}=t,w=!s&&!n&&f==null&&d===ne&&!t.wasReplaced&&!t.wasSelfCanceled;let p,r;return t.wasReplaced?(p={kind:"stated",delivered:"replaced (overlap=replace)"},r="release"):w?(p={kind:"stated",delivered:oe},r=await re(e,u,c)??"release"):(p={kind:"disposed",ctx:{runId:a,startedAtIso:u,exitCode:n?-1:d,timedOut:n,runTimeoutS:e.run_timeout_s,processErrorMessage:f!=null?f.message:null,stdoutHead:t.stdoutHead,stdoutSha256:t.stdoutSha256,stderrHead:t.stderrHead},selfCanceled:t.wasSelfCanceled},r=s?"release":{kind:"clear"}),O(e,{outOfBand:s,outcome:p,record:{runId:a,startedAtIso:u,finishedAtMs:c,durationMs:c-t.startedAtMs,exitCode:n?-1:d,stdoutHead:t.stdoutHead===""?null:t.stdoutHead,stderrHead:f!=null?`run process error: ${f.message}`.slice(0,E):t.stderrHead===""?null:t.stderrHead},lease:r})}i(he,"settleClosedRun");function R(e,t,a,s){const u=Date.now(),c=`${a}: ${s.message}`.slice(0,E);return O(e,{outOfBand:t.outOfBand,outcome:{kind:"disposed",ctx:{runId:t.runId,startedAtIso:t.startedAtIso,exitCode:null,timedOut:!1,runTimeoutS:e.run_timeout_s,processErrorMessage:s.message,stdoutHead:"",stdoutSha256:q,stderrHead:c}},record:{runId:t.runId,startedAtIso:t.startedAtIso,finishedAtMs:u,durationMs:u-t.startedAtMs,exitCode:null,stdoutHead:null,stderrHead:c},lease:"release"})}i(R,"settleUnspawnedRun");async function P(e,t){let a;try{a=pe(e.recur,new Date(t))}catch(s){return L({level:"error",event:"cron.recur.unparseable",error:s,fields:{cron_id:e.cron_id,name:e.name,recur:e.recur}}),await fe(e.cron_id,"paused"),new Error(`unparseable recurrence ${JSON.stringify(e.recur)}: ${s.message} \u2014 this cron cannot be rescheduled until its recurrence is fixed (\`crtr cron cancel\` and re-arm it), so it has been paused.`)}return await ce(e.cron_id,a),null}i(P,"advanceRecurrence");function Pe(e,t){return B(e,t).settled}i(Pe,"executeCron");function B(e,t){const{outOfBand:a}=t;let s=i(()=>{},"admit");const u=new Promise(d=>{s=d}),c=new Promise((d,n)=>{let f=!1,w=i(()=>{},"undo");const p=i(r=>{if(w(),!f){n(r);return}f=!1,le(e.cron_id).then(()=>n(r),m=>{L({level:"error",event:"cron.lease.release_failed",error:m,fields:{cron_id:e.cron_id}}),n(r)})},"fail");(async()=>{const r=Y(),m=Date.now(),C=new Date(m).toISOString(),S={runId:r,outOfBand:a,startedAtIso:C,startedAtMs:m};if(!await ie(e.cron_id,$,C,a,r)){d(null);return}f=!0;let h=await Z(j(e));if(h.allowed){const o=await te({grantee:e.grantee,sub:e.sub,grant_id:e.grant_id});o!==null&&(h={allowed:!1,reason:"limit_reached",message:ee(o).message})}if(!h.allowed){try{if(!a&&e.recur!=null){const _=await P(e,m);if(_!==null)throw _}}catch(_){R(e,S,"run could not be armed",_).then(d,n);return}const o=Date.now();O(e,{outOfBand:a,outcome:{kind:"refused",reason:h.reason,message:h.message},record:{runId:r,startedAtIso:C,finishedAtMs:o,durationMs:o-m,exitCode:null,stdoutHead:null,stderrHead:`${h.reason}: ${h.message}`.slice(0,E)},lease:"release"}).then(d,n);return}let M;try{if(e.isolated&&W(e.grantee,e.cron_id),{env:M}=await Q(e,r),!a&&e.recur!=null){const o=await P(e,m);if(o!==null)throw o}}catch(o){R(e,S,"run could not be armed",o).then(d,n);return}let b=null,T=!1;const D={runId:r,replaced:!1,selfCanceled:!1,kill:i(()=>{b!==null?b():T=!0},"kill")};J(e.cron_id,D),w=i(()=>g(e.cron_id,r),"undo"),s();const x=z()?{executionId:r,kind:"cron",cronId:e.cron_id,fromExec:e.armed_from_exec,grantee:e.grantee,sub:e.sub,grantId:e.grant_id,scopes:e.scopes,originRunId:e.origin_run_id,isolated:e.isolated,anchor:e.anchor_node,uid:await K(e.grantee),rebuilt:!1,refusal:null}:null,I=i(()=>{x!==null&&V(r)},"closeExecution");w=i(()=>{g(e.cron_id,r),I()},"undo"),i(async()=>(x!==null&&await X(x,e.cwd,e.profile),me({command:e.command,cwd:e.cwd,env:M,app:e.grantee,isolated:e.isolated,principal:{sub:e.sub,grantId:e.grant_id,scopes:e.scopes},...e.isolated?{folder:e.cwd}:{},...x!==null?{cron:{executionId:r,...e.anchor_node===null||e.isolated?{}:{anchorDir:G(e.anchor_node,e.grantee)}}}:e.anchor_node===null||e.isolated?{}:{nodeId:e.anchor_node},timeoutMs:e.run_timeout_s*1e3,headBytes:E})),"launch")().then(async o=>{if(o.pid===null){g(e.cron_id,r),I(),o.completion.then(l=>{R(e,S,"run failed to spawn",l.processError??new Error("bash did not provide a process id")).then(d,n)},n);return}w=i(()=>{g(e.cron_id,r),I(),o.terminate()},"undo");let _=!1;const v=i(()=>{o.completion.then(l=>{const k=D.replaced,U=D.selfCanceled,H=x?.refusal??null;if(g(e.cron_id,r),I(),_){d(null);return}if(H!==null&&!k){const y=Date.now(),F=A(l.stderr,l.stderrTruncated);O(e,{outOfBand:a,outcome:{kind:"refused",reason:H.reason,message:H.message},record:{runId:r,startedAtIso:C,finishedAtMs:y,durationMs:y-m,exitCode:l.exitCode,stdoutHead:A(l.stdout,l.stdoutTruncated)||null,stderrHead:`${H.reason}: ${H.message}
2
- ${F}`.trim().slice(0,E)},lease:"release"}).then(d,n);return}he(e,{runId:r,outOfBand:a,startedAtIso:C,startedAtMs:m,finishedAtMs:Date.now(),code:l.exitCode,timedOut:l.timedOut,processError:l.processError,stdoutHead:A(l.stdout,l.stdoutTruncated),stderrHead:A(l.stderr,l.stderrTruncated),stdoutSha256:l.stdoutSha256,wasReplaced:k,wasSelfCanceled:U}).then(d,n)},n)},"settleCommand");if(!await ue(e.cron_id,$,C,o.pid)&&!D.selfCanceled){_=!0,o.terminate(),v();return}b=o.terminate,T&&o.terminate(),v()},o=>{g(e.cron_id,r),I(),R(e,S,"run failed to spawn",o).then(d,n)}).catch(p)})().catch(p)});return c.then(s,s),{admitted:u,settled:c}}i(B,"startCron");async function Be(e,t={}){const a=new Date(e).toISOString();await se(a),await ae(e,a);const s=[];for(const u of await de(a)){const{admitted:c,settled:d}=B(u,{outOfBand:!1}),n=d.then(()=>{});n.catch(()=>{}),await c,s.push(n),t.register?.(n)}for(const u of s)u.catch(()=>{});return s}i(Be,"runDueCrons");export{Ne as CRON_OUTPUT_HEAD_BYTES,qe as cronDaemonInstanceId,Pe as executeCron,Je as markSelfCanceledCronRun,Xe as recoverStaleCronLeases,Be as runDueCrons,B as startCron};
1
+ var N=Object.defineProperty;var i=(e,t)=>N(e,"name",{value:t,configurable:!0});import{randomUUID as Y}from"node:crypto";import{CRON_OUTPUT_HEAD_BYTES as E,EMPTY_STDOUT_SHA256 as q,formatCronOutputHead as A}from"./cron/capture.js";import{clearLiveRun as g,cronDaemonInstanceId as L,ensureCronOwnerLease as J,registerLiveRun as V}from"./cron/live-runs.js";import{closeCronExecution as X,openCronExecution as z}from"./cron/run-socket.js";import{appProcessSandboxed as G}from"../core/spaces/process.js";import{nodeAgentDir as K}from"../core/canvas/paths.js";import{uidForApp as Q}from"../core/spaces/users.js";import{buildArmedRunContext as W}from"./cron/armed-context.js";import{ensureCronFolder as Z}from"./cron/folders.js";import{checkCronFire as j,cronPrincipalOf as ee}from"./cron/authority.js";import{refusalError as te}from"../core/ledger/index.js";import{startRefusal as re}from"../core/runtime/start-check.js";import{decideHeldSettle as ne,EXIT_HELD as oe,HELD_DELIVERED as ae}from"./cron/held.js";import{settleRun as O}from"./cron/settle.js";import{applyOverlapPolicies as se,expireDueCrons as de}from"./cron/passes.js";import{dueClockCrons as ie,acquireCronRunLease as le,releaseCronRunLease as ue,attachCronRunPid as ce,advanceCron as fe,setCronState as me}from"../core/canvas/crons.js";import{emitEvent as $}from"../core/events/emit.js";import{runAppCommand as pe}from"../core/runtime/run-command.js";import{nextSlotAfter as he}from"../core/wake.js";import{CRON_OUTPUT_HEAD_BYTES as Ye}from"./cron/capture.js";import{cronDaemonInstanceId as Je,markSelfCanceledCronRun as Ve}from"./cron/live-runs.js";import{recoverStaleCronLeases as ze}from"./cron/lease-recovery.js";async function we(e,t){const{runId:a,outOfBand:s,startedAtIso:u,finishedAtMs:c,code:d,timedOut:n,processError:f}=t,_=!s&&!n&&f==null&&d===oe&&!t.wasReplaced&&!t.wasSelfCanceled;let p,r;return t.wasReplaced?(p={kind:"stated",delivered:"replaced (overlap=replace)"},r="release"):_?(p={kind:"stated",delivered:ae},r=await ne(e,u,c)??"release"):(p={kind:"disposed",ctx:{runId:a,startedAtIso:u,exitCode:n?-1:d,timedOut:n,runTimeoutS:e.run_timeout_s,processErrorMessage:f!=null?f.message:null,stdoutHead:t.stdoutHead,stdoutSha256:t.stdoutSha256,stderrHead:t.stderrHead},selfCanceled:t.wasSelfCanceled},r=s?"release":{kind:"clear"}),O(e,{outOfBand:s,outcome:p,record:{runId:a,startedAtIso:u,finishedAtMs:c,durationMs:c-t.startedAtMs,exitCode:n?-1:d,stdoutHead:t.stdoutHead===""?null:t.stdoutHead,stderrHead:f!=null?`run process error: ${f.message}`.slice(0,E):t.stderrHead===""?null:t.stderrHead},lease:r})}i(we,"settleClosedRun");function R(e,t,a,s){const u=Date.now(),c=`${a}: ${s.message}`.slice(0,E);return O(e,{outOfBand:t.outOfBand,outcome:{kind:"disposed",ctx:{runId:t.runId,startedAtIso:t.startedAtIso,exitCode:null,timedOut:!1,runTimeoutS:e.run_timeout_s,processErrorMessage:s.message,stdoutHead:"",stdoutSha256:q,stderrHead:c}},record:{runId:t.runId,startedAtIso:t.startedAtIso,finishedAtMs:u,durationMs:u-t.startedAtMs,exitCode:null,stdoutHead:null,stderrHead:c},lease:"release"})}i(R,"settleUnspawnedRun");async function P(e,t){let a;try{a=he(e.recur,new Date(t))}catch(s){return $({level:"error",event:"cron.recur.unparseable",error:s,fields:{cron_id:e.cron_id,name:e.name,recur:e.recur}}),await me(e.cron_id,"paused"),new Error(`unparseable recurrence ${JSON.stringify(e.recur)}: ${s.message} \u2014 this cron cannot be rescheduled until its recurrence is fixed (\`crtr cron cancel\` and re-arm it), so it has been paused.`)}return await fe(e.cron_id,a),null}i(P,"advanceRecurrence");function Be(e,t){return B(e,t).settled}i(Be,"executeCron");function B(e,t){const{outOfBand:a}=t;let s=i(()=>{},"admit");const u=new Promise(d=>{s=d}),c=new Promise((d,n)=>{let f=!1,_=i(()=>{},"undo");const p=i(r=>{if(_(),!f){n(r);return}f=!1,ue(e.cron_id).then(()=>n(r),m=>{$({level:"error",event:"cron.lease.release_failed",error:m,fields:{cron_id:e.cron_id}}),n(r)})},"fail");(async()=>{const r=Y(),m=Date.now(),C=new Date(m).toISOString(),S={runId:r,outOfBand:a,startedAtIso:C,startedAtMs:m};if(await J(),!await le(e.cron_id,L,C,a,r)){d(null);return}f=!0;let h=await j(ee(e));if(h.allowed){const o=await re({grantee:e.grantee,sub:e.sub,grant_id:e.grant_id});o!==null&&(h={allowed:!1,reason:"limit_reached",message:te(o).message})}if(!h.allowed){try{if(!a&&e.recur!=null){const w=await P(e,m);if(w!==null)throw w}}catch(w){R(e,S,"run could not be armed",w).then(d,n);return}const o=Date.now();O(e,{outOfBand:a,outcome:{kind:"refused",reason:h.reason,message:h.message},record:{runId:r,startedAtIso:C,finishedAtMs:o,durationMs:o-m,exitCode:null,stdoutHead:null,stderrHead:`${h.reason}: ${h.message}`.slice(0,E)},lease:"release"}).then(d,n);return}let M;try{if(e.isolated&&Z(e.grantee,e.cron_id),{env:M}=await W(e,r),!a&&e.recur!=null){const o=await P(e,m);if(o!==null)throw o}}catch(o){R(e,S,"run could not be armed",o).then(d,n);return}let b=null,T=!1;const D={runId:r,replaced:!1,selfCanceled:!1,kill:i(()=>{b!==null?b():T=!0},"kill")};V(e.cron_id,D),_=i(()=>g(e.cron_id,r),"undo"),s();const x=G()?{executionId:r,kind:"cron",cronId:e.cron_id,fromExec:e.armed_from_exec,grantee:e.grantee,sub:e.sub,grantId:e.grant_id,scopes:e.scopes,originRunId:e.origin_run_id,isolated:e.isolated,anchor:e.anchor_node,uid:await Q(e.grantee),rebuilt:!1,refusal:null}:null,I=i(()=>{x!==null&&X(r)},"closeExecution");_=i(()=>{g(e.cron_id,r),I()},"undo"),i(async()=>(x!==null&&await z(x,e.cwd,e.profile),pe({command:e.command,cwd:e.cwd,env:M,app:e.grantee,isolated:e.isolated,principal:{sub:e.sub,grantId:e.grant_id,scopes:e.scopes},...e.isolated?{folder:e.cwd}:{},...x!==null?{cron:{executionId:r,...e.anchor_node===null||e.isolated?{}:{anchorDir:K(e.anchor_node,e.grantee)}}}:e.anchor_node===null||e.isolated?{}:{nodeId:e.anchor_node},timeoutMs:e.run_timeout_s*1e3,headBytes:E})),"launch")().then(async o=>{if(o.pid===null){g(e.cron_id,r),I(),o.completion.then(l=>{R(e,S,"run failed to spawn",l.processError??new Error("bash did not provide a process id")).then(d,n)},n);return}_=i(()=>{g(e.cron_id,r),I(),o.terminate()},"undo");let w=!1;const v=i(()=>{o.completion.then(l=>{const k=D.replaced,U=D.selfCanceled,H=x?.refusal??null;if(g(e.cron_id,r),I(),w){d(null);return}if(H!==null&&!k){const y=Date.now(),F=A(l.stderr,l.stderrTruncated);O(e,{outOfBand:a,outcome:{kind:"refused",reason:H.reason,message:H.message},record:{runId:r,startedAtIso:C,finishedAtMs:y,durationMs:y-m,exitCode:l.exitCode,stdoutHead:A(l.stdout,l.stdoutTruncated)||null,stderrHead:`${H.reason}: ${H.message}
2
+ ${F}`.trim().slice(0,E)},lease:"release"}).then(d,n);return}we(e,{runId:r,outOfBand:a,startedAtIso:C,startedAtMs:m,finishedAtMs:Date.now(),code:l.exitCode,timedOut:l.timedOut,processError:l.processError,stdoutHead:A(l.stdout,l.stdoutTruncated),stderrHead:A(l.stderr,l.stderrTruncated),stdoutSha256:l.stdoutSha256,wasReplaced:k,wasSelfCanceled:U}).then(d,n)},n)},"settleCommand");if(!await ce(e.cron_id,L,C,o.pid)&&!D.selfCanceled){w=!0,o.terminate(),v();return}b=o.terminate,T&&o.terminate(),v()},o=>{g(e.cron_id,r),I(),R(e,S,"run failed to spawn",o).then(d,n)}).catch(p)})().catch(p)});return c.then(s,s),{admitted:u,settled:c}}i(B,"startCron");async function Ue(e,t={}){const a=new Date(e).toISOString();await de(a),await se(e,a);const s=[];for(const u of await ie(a)){const{admitted:c,settled:d}=B(u,{outOfBand:!1}),n=d.then(()=>{});n.catch(()=>{}),await c,s.push(n),t.register?.(n)}for(const u of s)u.catch(()=>{});return s}i(Ue,"runDueCrons");export{Ye as CRON_OUTPUT_HEAD_BYTES,Je as cronDaemonInstanceId,Be as executeCron,Ve as markSelfCanceledCronRun,ze as recoverStaleCronLeases,Ue as runDueCrons,B as startCron};
@@ -1 +1 @@
1
- var p=Object.defineProperty;var i=(e,r)=>p(e,"name",{value:r,configurable:!0});import"../suppress-experimental-warnings.js";import{runDaemon as o}from"./crtrd.js";import{configureDaemonHttpDispatcher as c}from"./http-dispatcher.js";function s(e){for(let r=0;r<e.length;r++){const t=e[r];if(t==="--tcp")return e[r+1];if(t!==void 0&&t.startsWith("--tcp="))return t.slice(6)}}i(s,"parseTcpArg");function a(e){return e.includes("--prepare")}i(a,"parsePrepareArg");const n=process.argv.slice(2);if(n[0]==="export-sqlite"){const{runExportSqlite:e}=await import("./export-sqlite-run.js");process.exitCode=await e()}else c(),await o({tcp:s(n),prepare:a(n)});
1
+ var c=Object.defineProperty;var i=(e,r)=>c(e,"name",{value:r,configurable:!0});import"../suppress-experimental-warnings.js";import{runDaemon as p}from"./crtrd.js";import{configureDaemonHttpDispatcher as s}from"./http-dispatcher.js";function l(e){for(let r=0;r<e.length;r++){const t=e[r];if(t==="--tcp")return e[r+1];if(t!==void 0&&t.startsWith("--tcp="))return t.slice(6)}}i(l,"parseTcpArg");function u(e){for(let r=0;r<e.length;r++){const t=e[r],n=t==="--role"?e[r+1]:t?.startsWith("--role=")?t.slice(7):void 0;if(n!==void 0){if(n!=="api"&&n!=="reconcile")throw new Error(`--role must be api or reconcile, got ${n}`);return n}}}i(u,"parseRoleArg");function a(e){return e.includes("--prepare")}i(a,"parsePrepareArg");const o=process.argv.slice(2);if(o[0]==="export-sqlite"){const{runExportSqlite:e}=await import("./export-sqlite-run.js");process.exitCode=await e()}else s(),await p({tcp:l(o),prepare:a(o),role:u(o)});
@@ -17,7 +17,17 @@ export interface DaemonOpts {
17
17
  tcp?: string;
18
18
  /** Hold after storage preparation until `POST /v1/daemon/admit`. */
19
19
  prepare?: boolean;
20
+ /** `api`: serves requests, runs no reconciler lane. `reconcile`: runs the lanes and serves only the probe API.
21
+ * Absent (a Mac): one process doing both, holding the `daemon_owner` claim. */
22
+ role?: DaemonRole;
20
23
  }
24
+ export type DaemonRole = 'api' | 'reconcile';
25
+ /** Whether a process of this role runs the reconciler lanes and their boot sweeps (an api process does not). */
26
+ export declare function roleRunsLanes(role: DaemonRole | undefined): boolean;
27
+ /** Whether a process of this role serves conversations, documents and the app listener (a reconcile process does not). */
28
+ export declare function roleServesRequests(role: DaemonRole | undefined): boolean;
29
+ /** How long an api process lets in-flight requests run after SIGTERM; `/v1/llm` takes up to 610 s. */
30
+ export declare const API_DRAIN_GRACE_MS = 660000;
21
31
  /** Start the supervisor loop after winning the authoritative canvas claim.
22
32
  *
23
33
  * A losing claimant stays unbound and returns an inert async teardown so its
@@ -1,17 +1,18 @@
1
- var Ae=Object.defineProperty;var i=(n,s)=>Ae(n,"name",{value:s,configurable:!0});import{startPeerOutbox as Me}from"./peer/outbox.js";import{reportMachineStatus as Ne}from"../core/peer/directory.js";import{createPeerRelay as Fe}from"./peer/relay-service.js";import{writeFileSync as Ge,readFileSync as Oe,rmSync as Be,mkdirSync as se,existsSync as k,chmodSync as He}from"node:fs";import{createServer as ce}from"node:http";import{dirname as je}from"node:path";import{envCrtrdTcp as qe,envDocumentPort as ze}from"../shared/env.js";import{serveCanvasConversationStore as We}from"../core/canvas/sessions.js";import{pollRunObservers as Ue}from"./runs/observer.js";import{randomBytes as de}from"node:crypto";import{apiSocketPath as Ke,crtrHome as Je}from"../core/canvas/paths.js";import{activeAppUsers as Ve}from"../core/spaces/users.js";import{ensureAppSpace as Qe,ensureUserFiles as Ye}from"../core/spaces/space.js";import{LAUNCHER_SOCKET as x}from"../launcher/spec.js";import{launcherRequest as C}from"../launcher/client.js";import{installLauncherEventSink as Xe}from"./launcher-events.js";import{initializeEmptyLayout as Ze,layoutNeedsMigration as er,socketDir as le}from"../core/layout.js";import{migrateLooseKeys as rr}from"../core/layout-migrate/loose-keys.js";import{relocateProfileFolders as tr}from"../core/profiles/relocate.js";import{getNode as nr,listNodesForSupervision as pe,listParkedResidentsWithWakeMail as or,setFrozen as me}from"../core/canvas/index.js";import{createApiServer as ir}from"./api/server.js";import{createDocumentListener as ar}from"./api/document-listener.js";import{documentSecret as sr}from"./api/document-secret.js";import{createAppListener as cr}from"./api/app-listener.js";import{registerMachineEnvironment as dr}from"../core/environments.js";import{resolvePerson as lr,retireLocalPerson as pr,setPersonId as mr}from"../core/canvas/person.js";import{backfillNodeApps as fr,runPermissionsBoot as ur}from"../core/permissions/boot.js";import{onRunEventAppended as wr}from"../core/runs/events.js";import{runNotices as hr}from"./run-notices.js";import{createGrantSync as vr,GRANT_SYNC_INTERVAL_S as gr}from"../core/grants/sync.js";import{runRemovalCascades as yr}from"../core/grants/remove.js";import{sweepSandboxes as Sr}from"../core/grants/on-sync.js";import{rebuildCronExecutions as _r}from"./cron/run-socket.js";import{hasSyncedGrants as br}from"../core/grants/mirror.js";import{readConfig as fe}from"../core/config.js";import{reconcileUndeliveredTickets as kr}from"./human/sweep.js";import{resetHandedOffDeliveries as Pr}from"../core/canvas/deliveries.js";import{resetHandedOffDaemonActions as Er}from"../core/canvas/daemon-actions.js";import{sweepReviews as Rr}from"./review/sweep.js";import{pidfilePath as K}from"./pidfile.js";import{rendererWarning as Ir}from"../core/runtime/package-health.js";import{DEFAULT_INTERVAL_MS as xr}from"./supervise-cadence.js";import{createResumeDetector as Dr}from"./resume-detector.js";import{setFatalFaultAnnouncer as Tr}from"../core/canvas/recovery.js";import{BrokerSupervisionReconciler as Cr}from"./reconcilers/broker-supervision.js";import{ControllerDeathReconciler as Lr}from"./reconcilers/controller-death.js";import{NodeLifecycleReconciler as $r}from"./reconcilers/node-lifecycle/tick.js";import{NodeDeadlineReconciler as Ar}from"./reconcilers/node-deadline.js";import{CronLaneReconciler as Mr}from"./reconcilers/cron-lane.js";import{DeliveryLaneReconciler as Nr}from"./reconcilers/delivery-lane.js";import{ActionLaneReconciler as Fr}from"./reconcilers/action-lane.js";import{endLiveExecution as Gr,migrateLegacyExecutionIdentities as Or}from"../core/canvas/executions.js";import{revokePriorEpochNodeTokens as Br}from"../core/canvas/node-tokens.js";import{PendingReviewSubmitReconciler as Hr}from"./reconcilers/pending-review-submit.js";import{StorageMaintenanceReconciler as jr}from"./reconcilers/storage-maintenance.js";import{StorageDueWorkReconciler as qr}from"./reconcilers/storage-due-work.js";import{BashDeadlineReconciler as zr}from"./reconcilers/bash-deadline.js";import{FaultRetryBackstopReconciler as Wr}from"./reconcilers/fault-retry-backstop.js";import{StructuredRequestReconciler as Ur}from"./reconcilers/structured-request-lane.js";import{reconcileStructuredRequestSidecars as Kr}from"./structured-requests/boot.js";import{captureLivenessSnapshot as ue,capturePidCommand as Jr,captureTeardownSnapshot as we,isPidAlive as Vr,isAnyPidAlive as he,killProcessTreePids as ve}from"../core/canvas/pid.js";import{bindFleet as Qr,boundFleet as Yr}from"../core/runtime/fleet.js";import{bindDaemonControl as Xr,clearDaemonControl as Zr}from"./control.js";import{execDaemon as et,spawnDaemon as rt}from"./manage.js";import{reconcileAndMigrateBoot as tt}from"../core/canvas/boot.js";import{resolveInstallId as nt}from"../core/canvas/install-id.js";import{setInstallId as ot}from"../core/runtime/nodes.js";import{claimDaemonOwnership as ge,daemonOwnershipClaimed as it,releaseDaemonOwnership as ye}from"../core/canvas/daemon-owner.js";import{canvasDatabaseUrl as at,closeAllDbs as J,openDb as Se,withCanvasWrite as st}from"../core/canvas/db.js";import{importSqliteCanvasIfPresent as ct}from"../core/canvas/import-sqlite.js";import{ensurePostgres as dt,ensurePostgresHealthy as lt,stopPostgres as pt,withPostgresStartLock as mt}from"./postgres/index.js";import{brokerSignalBus as V}from"./broker-signals/bus.js";import{brokerSignalStreams as ft}from"./api/handlers/broker-signals.js";import{reconcilePackages as ut}from"../core/graph/package-reconcile.js";import{startRepoSync as wt}from"../core/graph/repo-sync/index.js";import{prewarmRecentRecipes as ht,reapStaleSpares as vt}from"../core/runtime/warm-pool.js";import{DaemonFleet as gt}from"./fleet.js";import{BOOT_MIGRATIONS as yt}from"./boot-migrations.js";import{emitEvent as m}from"../core/events/emit.js";import{operationIdContext as _}from"../core/events/operation-id.js";import{bindDaemonEventSource as St}from"../core/events/source.js";import{activateCanvasImport as _t,MigrationBlockedError as bt,runForcedCanvasImport as kt}from"../migrations/activation.js";import{convertLegacyProviderStore as Pt,convertLegacyScopedStore as Et}from"../core/scoped-state/migrate.js";import{userScopeRoot as Rt}from"../core/scope.js";import{secretsDir as It}from"../core/layout.js";function Q(n,s){return n.includes(` --canvas-home ${s} `)}i(Q,"brokerCommandDeclaresCanvasHome");function Y(n){return/ --epoch (\S+) /.exec(n)?.[1]??null}i(Y,"brokerCommandEpoch");const xt=5e3,_e=150;async function Dt(n){const s=ue();if(s===null)throw m({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness"}}),new Error("prior-epoch broker census could not capture the process table");const l=Je(),w=[];for(const[a,u]of s.processes){const y=u.command;if(u.zombie||!y.includes("broker-cli.js")||!Q(y,l)||Y(y)===n)continue;const P=y.trim().split(/\s+/).at(-1)??"";P===""||P.endsWith("broker-cli.js")||w.push({pid:a,nodeId:P})}let d=0;const t=[];for(const a of w){const u=Jr(a.pid);if(u===null||!u.includes("broker-cli.js")||!Q(u,l)||Y(u)===n||u.trim().split(/\s+/).at(-1)!==a.nodeId)continue;const y=we(a.pid,null);if(!y.reused){try{process.platform==="linux"&&k(x)?await C({operation:"stop",pid:a.pid}):process.kill(a.pid,"SIGTERM")}catch{continue}d++,t.push({pid:a.pid,snap:y})}}const g=Date.now()+xt;for(;Date.now()<g&&t.some(a=>he(a.snap.tree.length>0?a.snap.tree:[a.pid]));)await new Promise(a=>setTimeout(a,_e));let h=0;for(const a of t){const u=a.snap.tree.length>0?a.snap.tree:[a.pid];he(u)&&(process.platform==="linux"&&k(x)?await C({operation:"stop",pid:a.pid}):ve(u,"SIGKILL",a.snap.identities),h++)}h>0&&await new Promise(a=>setTimeout(a,_e));const f=ue();if(f===null)throw m({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness",stage:"final"}}),new Error("prior-epoch broker census could not capture the final process table");const c=[];for(const[a,u]of f.processes){const y=u.command;u.zombie||!y.includes("broker-cli.js")||Q(y,l)&&Y(y)!==n&&c.push(a)}if(c.length>0)throw new Error(`prior-epoch broker census could not prove termination of pid(s): ${c.join(", ")}`);m({level:"info",event:"daemon.census.completed",fields:{prior_epoch_brokers:w.length,sigtermed:d,sigkilled:h}})}i(Dt,"reapPriorEpochBrokers");const Tt=5e3;async function Ct(n){const s=Array.from(n.entries());if(s.length===0)return;for(const[t]of s){const g=(await nr(t))?.status;(g==="active"||g==="idle")&&await me(t,new Date().toISOString())}const l=s.map(([,t])=>({pid:t.pid,snap:we(t.pid,null),exited:t.handle.exited}));for(const t of l)if(!t.snap.reused)try{process.platform==="linux"&&k(x)?await C({operation:"stop",pid:t.pid}):process.kill(t.pid,"SIGTERM")}catch{}const w=new Promise(t=>{setTimeout(t,Tt).unref()});await Promise.allSettled(l.map(t=>Promise.race([t.exited.then(()=>{}),w])));const d=[];for(const t of l)Vr(t.pid)&&(process.platform==="linux"&&k(x)?await C({operation:"stop",pid:t.pid}):ve(t.snap.tree.length>0?t.snap.tree:[t.pid],"SIGKILL",t.snap.identities),d.push(t));await Promise.all(d.map(t=>t.exited));for(const[t]of s)await Gr(t,{reason:"daemon-teardown"});m({level:"info",event:"daemon.fleet_teardown.completed",fields:{brokers:l.length,sigkilled:d.length}})}i(Ct,"teardownFleet");function be(){se(je(K()),{recursive:!0}),Ge(K(),String(process.pid),"utf8")}i(be,"writePidfile");function ke(n){return typeof n=="object"&&n!==null&&n.code==="ENOENT"}i(ke,"isMissingPathError");function Pe(n){const s=K();let l;try{l=Oe(s,"utf8")}catch(w){if(ke(w))return;throw w}if(l.trim()!==String(n))throw new Error(`daemon readiness mirror does not name this invocation (expected pid ${n})`);try{Be(s)}catch(w){if(ke(w))return;throw w}}i(Pe,"removeOwnedPidfileMirror");const Lt={acceptsDetachedWork:i(()=>!0,"acceptsDetachedWork"),registerDetached:i(n=>{n.catch(()=>{})},"registerDetached")},H=new Cr;Tr(n=>H.handleFatalFault(n,!1));const $t=new Lr,At=new $r,Mt=new Ar,Nt=new Mr,Ft=new Nr,Gt=new Fr,Ot=new Hr,Bt=new jr,Ht=new qr,jt=new zr,qt=new Wr,zt=new Ur;async function Wt(n=Date.now(),s=Lt){const l=Yr(),w=await H.runFleetPreamble(l);let d,t;try{const h=await pe();d=h.filter(c=>c.status==="active"||c.status==="idle"),t=h.filter(c=>c.status==="done"||c.status==="canceled"||c.status==="dead");const f=new Set(t.map(c=>c.node_id));t.push(...(await or()).filter(c=>!f.has(c.node_id)))}catch(h){m({level:"error",event:"canvas.node_list.failed",error:h});return}await H.run(n,{rows:d,fleet:l,lifecycle:s}),await $t.run(n,{rows:d});const g=await At.run(n,{rows:[...d,...t],fleet:l});await Mt.run(n),await zt.run(n),await Nt.run(n,{lifecycle:s}),await Ft.run(n,{lifecycle:s}),await Gt.run(n,{lifecycle:s}),await Ot.run(n,{lifecycle:s}),await Bt.run(n,{lifecycle:s,fleet:l}),Ht.run(n,{lifecycle:s}),await Ue(),qt.run(n,{fleet:l,lifecycle:s}),await jt.run(n,{rows:d}),H.warnIfSaturated({fleet:l,lifecycle:s,thresholds:w,frozen:g})}i(Wt,"superviseTick");async function Ut(n){se(le(),{recursive:!0,mode:493}),process.platform==="linux"&&He(le(),493);const s=Ke();if(k(s))throw new Error(`layout refusal cannot bind occupied socket: ${s}`);const l=ce((f,c)=>{if(f.method==="GET"&&f.url==="/healthz"){c.writeHead(200,{"content-type":"application/json; charset=utf-8"}),c.end(JSON.stringify({ok:!1,daemon_up:!0,store_accessible:!1,brokers_reconciled:!1,startup_phase:"blocked",startup_blocked:"layout_migration_required"}));return}c.writeHead(503,{"content-type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:{code:"layout_migration_required",message:"run crtr sys migrate while the runtime is offline",type:"server_error",origin:"runtime",retryable:!1}}))});await new Promise((f,c)=>{l.once("error",c),l.listen(s,()=>{l.off("error",c),f()})});let w;const d=n.tcp??qe();if(d){const f=/^(.*):(\d+)$/.exec(d);if(!f||Number(f[2])>65535)throw new Error(`invalid refusal TCP address: ${d}`);const c=ce(l.listeners("request")[0]);await new Promise((a,u)=>{c.once("error",u),c.listen(Number(f[2]),f[1],()=>{c.off("error",u),a()})}),w=c}be();let t;const g=i(()=>(t??=(async()=>{l.closeAllConnections(),w?.closeAllConnections(),await Promise.all([l,w].filter(f=>f!==void 0).map(f=>new Promise((c,a)=>f.close(u=>u?a(u):c())))),Pe(process.pid),process.off("SIGINT",h),process.off("SIGTERM",h)})(),t),"cleanup"),h=i(()=>{g().then(()=>process.exit(0),f=>{process.stderr.write(`[crtrd] layout refusal cleanup failed: ${String(f)}
2
- `),process.exit(1)})},"onSignal");return process.on("SIGINT",h),process.on("SIGTERM",h),g}i(Ut,"serveLayoutRefusal");async function Kt(n={}){if(er())return Ut(n);Ze();const s=i(r=>{process.stderr.write(`[crtrd] ${r}
3
- `)},"logPostgres"),l=!!process.env.CRTR_TEST_PG_URL,{database:w,ownership:d}=l?{database:{url:await at()},ownership:await ge(de(16).toString("hex"))}:await mt(async()=>({database:await dt({log:s,startLockHeld:!0}),ownership:await ge(de(16).toString("hex"))}));if(!d.claimed){if(d.reason==="incumbent_alive")process.stderr.write(`[crtrd] already running (pid ${d.incumbentPid??"?"})
4
- `);else{const r=[d.incumbentPid!==void 0?`pid ${d.incumbentPid}`:void 0,d.verdict!==void 0?`verdict ${d.verdict}`:void 0].filter(S=>S!==void 0),e=r.length>0?` (${r.join(", ")})`:"";process.stderr.write(`[crtrd] claim lost: ${d.reason}${e}
5
- `),d.reason==="own_identity_indeterminate"&&process.stderr.write("[crtrd] cannot read its own process identity. crouter needs /proc on Linux, or the `ps` command elsewhere (Debian/Ubuntu: apt-get install procps).\n"),(d.reason==="own_identity_indeterminate"||d.reason==="incumbent_malformed")&&(process.exitCode=1)}return async()=>{}}const t=d.claim;let g=null,h=null,f=null,c="";try{await Dt(t.token),Et({kind:"user",root:Rt()});for(const e of tr())process.stderr.write(`[crtrd] ${e}
6
- `);if(process.platform==="darwin"){const{readConfig:e,updateConfig:S}=await import("../core/config.js");if(e("user").paths?.user_files===void 0){const{homedir:o}=await import("node:os");S("user",p=>{p.paths??={},p.paths.user_files??=o()})}}await Pt(It());for(const e of rr())process.stderr.write(`[crtrd] ${e}
7
- `);await Se(),await ct(w.url,s),await V.listen(w.url);const r=await st(async e=>(await fr(e),await ut(e)));for(const e of r.conflicts)process.stderr.write(`[crtrd] plugin documents not reconciled: ${e.message}
8
- `);for(const e of r.invalid)process.stderr.write(`[crtrd] package file is not a document: ${e.path}: ${e.error}
9
- `);for(const e of r.quarantined)process.stderr.write(`[crtrd] plugin "${e.name}" has a damaged ${e.file} (${e.reason}); ${e.movedTo!==void 0?`moved to ${e.movedTo}`:"left in place and skipped"}
10
- `);for(const e of r.kinds.refused)process.stderr.write(`[crtrd] plugin "${e.plugin}" kind ${e.kind} not registered: ${e.message}
11
- `);for(const e of r.kinds.invalid)process.stderr.write(`[crtrd] plugin "${e.plugin}" declares invalid object kinds or scopes; no kinds registered and no app token: ${e.error}
12
- `);c=await nt(),ot(c),g=await lr(),g!==null&&(mr(g.id),h=await pr(),f=await ur()),process.platform==="linux"&&k(x)&&(Xe(),await C({operation:"users.sync",users:await Ve()})),await Br(t.token)}catch(r){await V.close();try{const{released:e}=await ye(t);if(!e)throw new Error("daemon ownership claim was not released");await J()}catch(e){throw new AggregateError([r,e],"daemon startup failed and cleanup also failed")}throw r}let a=!0,u,y=Promise.resolve(),P=!1,N,D;const F=new Set;let j=!1,X=!1,E,G,q,Z=!1,O,L,$,A,z;const Ee=Dr();let R,v={phase:"initializing",message:"daemon startup is running the canvas import and normal services are unavailable",blockerPaths:[]},T="initial startup activation",Re=null;function Ie(){return v.phase==="ready"?null:{phase:v.phase,message:v.message,blockerPaths:v.blockerPaths}}i(Ie,"startupBlock");function b(r){F.add(r),r.finally(()=>{F.delete(r)}).catch(()=>{})}i(b,"registerDetached");const ee={acceptsDetachedWork:i(()=>a,"acceptsDetachedWork"),registerDetached:b};await wt(ee,Se);async function xe(){try{a=!1,ft.close(),await V.close(),N!==void 0&&(clearInterval(N),N=void 0),$!==void 0&&(clearInterval($),$=void 0),z?.(),z=void 0;let r;do r=y,await r;while(r!==y);for(;F.size>0;)await Promise.allSettled([...F]);if(D!==void 0&&(await Ct(D),await D.exitsSettled(),D.dispose()),q!==void 0&&await q.close(),G!==void 0&&!Z&&(Z=!0,await G.close()),A!==void 0){const e=A;A=void 0,await e.stop()}if(L!==void 0){const e=L;L=void 0,await e.close()}if(O!==void 0){const e=O;O=void 0,await e.close()}if(j||(Pe(process.pid),j=!0),j&&!X){const{released:e}=await ye(t);if(!e)throw new Error("daemon ownership claim was not released");X=!0,await J()}}finally{Zr(),process.off("SIGINT",ne),process.off("SIGTERM",oe),process.off("exit",ie)}}i(xe,"cleanupAttempt");const B=1e3;let re=!1;function De(){return re?{graceMs:B}:(re=!0,_.fresh(()=>{m({level:"info",event:"daemon.handover.armed",fields:{pid:process.pid,grace_ms:B}})}),setTimeout(()=>{Te()},B),{graceMs:B})}i(De,"requestHandover");async function Te(){try{await I()}catch(r){const e=r instanceof Error?r.stack??r.message:String(r);process.stderr.write(`[crtrd] handover cleanup failed:
13
- ${e}
14
- `),process.exit(1)}try{process.platform==="linux"&&k(x)&&(await C({operation:"daemon.handover"}),process.exit(0)),process.pid===1&&await et();const r=await rt({waitForJoinLock:!0});_.fresh(()=>{m({level:"info",event:"daemon.handover.completed",fields:{predecessor_pid:process.pid,successor_pid:r.pid??r.existing_pid??null,spawned:r.started}})})}catch(r){_.fresh(()=>{m({level:"error",event:"daemon.handover.successor_failed",error:r})});const e=r instanceof Error?r.message:String(r);process.stderr.write(`[crtrd] handover successor failed to start: ${e}
15
- `),process.exit(1)}process.exit(0)}i(Te,"performHandover");function I(){if(E!==void 0)return E;const r=xe();return E=r,r.then(()=>{E===r&&(E=void 0)},()=>{E===r&&(E=void 0)}),r}i(I,"requestCleanup");async function te(r){try{await I(),await pt({claimed:i(async()=>{try{return await it()}finally{await J()}},"claimed")}),process.exit(0)}catch(e){process.exitCode=1;const S=e instanceof Error?e.stack??`${e.name}: ${e.message}`:String(e);process.stderr.write(`[crtrd] ${r} cleanup failed:
16
- ${S}
17
- `),process.exit(1)}}i(te,"handleSignal");function ne(){te("SIGINT")}i(ne,"onSigint");function oe(){te("SIGTERM")}i(oe,"onSigterm");function ie(){I().catch(()=>{})}i(ie,"onExit");async function W(r){a&&(y=y.then(r).then(()=>{},e=>{_.fresh(()=>{m({level:"error",event:"daemon.lane_job.failed",error:e})})}))}i(W,"enqueue");async function ae(){!a||P||(Ee.check()&&R&&(m({level:"info",event:"daemon.resume.detected"}),b(R().catch(()=>{}))),P=!0,await W(async()=>{if(P=!1,!!a)try{l||await lt({log:s}),await Wt(Date.now(),ee)}catch(r){_.fresh(()=>{m({level:"error",event:"daemon.tick.failed",error:r})})}}))}i(ae,"enqueueTick");let M=null;async function Ce(){if(M!==null)throw{status:500,code:"admission_failed",message:`daemon admission previously failed: ${M instanceof Error?M.message:String(M)}`};if(v.phase==="ready")return{admitted:!1,already_admitted:!0,startup_phase:"ready"};if(v.phase!=="prepared"&&v.phase!=="recovering")throw{status:409,code:"not_prepared",message:v.message};const r=v.phase==="prepared";try{return await U(),v={phase:"ready",message:"",blockerPaths:[]},r?{admitted:!0,already_admitted:!1,startup_phase:"ready"}:{admitted:!1,already_admitted:!0,startup_phase:"ready"}}catch(e){throw M=e,{status:500,code:"admission_failed",message:`daemon admission failed: ${e instanceof Error?e.message:String(e)}`}}}i(Ce,"admitStartup");async function Le(r){if(T!==null)throw{status:409,code:"migration_in_progress",message:`the canvas import is already running (${T})`};T="repair";try{const e=await kt({dryRun:r.dryRun});if(r.dryRun||e.pending||v.phase!=="blocked")return{result:e,resumed:!1};if(n.prepare)return v={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]},{result:e,resumed:!1};try{return await U(),v={phase:"ready",message:"",blockerPaths:[]},{result:e,resumed:!0}}catch(S){const o=S instanceof Error?S.message:String(S);return setTimeout(()=>{I().catch(()=>{})},1e3),{result:e,resumed:!1,resumeError:o}}}finally{T=null}}i(Le,"repairMigrations");async function $e(){u=n.intervalMs??xr;const r=t.token;D=new gt({epoch:r,enqueue:W}),Qr(D),v={phase:"recovering",message:"daemon startup recovery is running and normal services are unavailable",blockerPaths:[]},_.fresh(()=>{m({level:"info",event:"daemon.started",fields:{pid:process.pid,interval_ms:u}})});const e=c;_.fresh(()=>{m({level:"info",event:"daemon.install_id.resolved",fields:{install_id:e}})});const S=await dr(e);if(_.fresh(()=>{m({level:"info",event:"daemon.environment.registered",fields:{environment_id:S.environmentId,created:S.created}})}),_.fresh(()=>{let o;try{o=Ir()}catch{return}o!==null&&m({level:"warn",event:"daemon.startup.warning",fields:{stage:"termrender",warning:o.slice(0,2048)}})}),await _.fresh(async()=>{await Rr(),await kr(),await Pr(Date.now()),await Er(Date.now())}),await _.fresh(async()=>{const{reconcile:o,migrated:p}=await tt(Or);o.changed&&m({level:"info",event:"daemon.boot_change.reconciled",fields:{cleared_count:o.cleared.length,same_boot_proven:o.sameBootProven}}),p.length>0&&m({level:"info",event:"daemon.pid_identity.migrated",fields:{migrated_count:p.length}})}),await _.fresh(async()=>{for(const o of await pe())o.status!=="active"||o.pi_pid!=null||o.frozen_at!=null||o.respawn_not_before!=null||(await me(o.node_id,new Date().toISOString()),m({level:"warn",event:"daemon.startup.launch_debt_recorded",node_id:o.node_id}))}),await _.fresh(async()=>{await Kr();for(const o of yt){const p=o.apply();p.changed&&m({level:"info",event:"daemon.boot_migration.applied",fields:{migration:o.id,...p.summary}})}}),b(_r()),R){const o=i(()=>{b(R().catch(()=>{})),b(Ne().then(p=>A?.observe(p)).then(()=>{},p=>{m({level:"warn",event:"daemon.peer.status_failed",error:p})}))},"refresh");b(yr().catch(()=>{})),b(Sr().then(({completion:p})=>p).catch(p=>{m({level:"error",event:"daemon.grants.sweep_failed",error:p})})),o(),$=setInterval(o,gr*1e3),$.unref(),z=Me({joined:!0,register:b})}await W(async()=>{await vt(),await ht()}),N=setInterval(()=>{ae().catch(o=>m({level:"error",event:"daemon.tick.failed",error:o}))},u),await ae()}i($e,"completeStartup");function U(){return Re??=$e().catch(r=>{throw v={phase:"initializing",message:"daemon startup recovery failed and normal services are unavailable",blockerPaths:[]},r})}i(U,"resumeStartup");try{process.on("SIGINT",ne),process.on("SIGTERM",oe),process.on("exit",ie),St(),g!==null&&m({level:"info",event:"daemon.person.resolved",fields:{person_id:g.id,source:g.source}}),f!==null&&m({level:"info",event:"daemon.permissions.boot",fields:{...f}}),h!==null&&m({level:"info",event:"daemon.person.retired",fields:{person_id:h.person,joined_from:h.joinedFrom,retired:h.retired,columns:h.columns}}),O=await We();const r=t.token;Xr({epoch:r,requestHandover:De,registerDetached:b,startupBlock:Ie,admitStartup:Ce,repairMigrations:Le}),be(),G=ir({tcp:n.tcp}),await G.ready;const e=ze()??fe("user").document_port,S=ar(e,sr());try{await S.ready,q=S}catch(p){m({level:"warn",event:"daemon.document_listener.bind_failed",fields:{port:e,error:p instanceof Error?p.message:String(p)}})}const o=fe("user").runtime;o?.issuer&&(R=vr(o.issuer,b),o.unsandboxed===!0&&(A=Fe({syncGrants:R})),wr(p=>hr().appended(p))),o?.unsandboxed===!0&&o.listener!==void 0&&m({level:"warn",event:"daemon.app_listener.refused_unsandboxed",fields:{reason:"runtime.unsandboxed is set; runtime.listener is ignored"}}),o!==void 0&&o.listener!==void 0&&o.unsandboxed!==!0&&(L=await cr(o,async()=>await br(o.issuer.replace(/\/$/,"")),R),await L.ready);try{await _t()}catch(p){if(T=null,p instanceof bt)return v={phase:"blocked",message:p.message,blockerPaths:p.blockerPaths},I;throw p}process.platform==="linux"&&k(x)&&(Ye(),await Qe("app:terminal"));try{n.prepare?v={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]}:(await U(),v={phase:"ready",message:"",blockerPaths:[]})}finally{T=null}return I}catch(r){try{await I()}catch(e){throw new AggregateError([r,e],"daemon startup failed and cleanup also failed")}throw r}}i(Kt,"runDaemon");var xo=Kt;export{Q as brokerCommandDeclaresCanvasHome,Y as brokerCommandEpoch,xo as default,Kt as runDaemon,Wt as superviseTick,Ct as teardownFleet};
1
+ var er=Object.defineProperty;var i=(e,l)=>er(e,"name",{value:l,configurable:!0});import{startPeerOutbox as rr}from"./peer/outbox.js";import{reportMachineStatus as tr}from"../core/peer/directory.js";import{createPeerRelay as nr}from"./peer/relay-service.js";import{writeFileSync as or,readFileSync as ir,rmSync as ar,mkdirSync as ye,existsSync as _,unlinkSync as be,chmodSync as _e}from"node:fs";import{createServer as J}from"node:http";import{dirname as sr}from"node:path";import{envCrtrdTcp as ke,envDocumentPort as cr}from"../shared/env.js";import{serveCanvasConversationStore as lr}from"../core/canvas/sessions.js";import{pollRunObservers as dr}from"./runs/observer.js";import{randomBytes as Re}from"node:crypto";import{apiSocketPath as Pe,crtrHome as pr}from"../core/canvas/paths.js";import{activeAppUsers as mr}from"../core/spaces/users.js";import{ensureAppSpace as fr,ensureUserFiles as ur}from"../core/spaces/space.js";import{LAUNCHER_SOCKET as x}from"../launcher/spec.js";import{launcherRequest as L}from"../launcher/client.js";import{installLauncherEventSink as wr}from"./launcher-events.js";import{initializeEmptyLayout as hr,layoutNeedsMigration as vr,socketDir as Ee}from"../core/layout.js";import{migrateLooseKeys as gr}from"../core/layout-migrate/loose-keys.js";import{relocateProfileFolders as Sr}from"../core/profiles/relocate.js";import{getNode as yr,listNodesForSupervision as Ie,listParkedResidentsWithWakeMail as br,setFrozen as xe}from"../core/canvas/index.js";import{createApiServer as _r,closeServer as Te}from"./api/server.js";import{Router as kr}from"./api/router.js";import{healthRoutes as Rr}from"./api/handlers/health.js";import{createDocumentListener as Pr}from"./api/document-listener.js";import{documentSecret as Er}from"./api/document-secret.js";import{createAppListener as Ir}from"./api/app-listener.js";import{registerMachineEnvironment as xr}from"../core/environments.js";import{registerBlaxelRunnerFromEnv as Tr}from"../core/secrets/blaxel-register.js";import{markCloudRole as Dr,resolvePerson as Cr,retireLocalPerson as Lr,setPersonId as $r}from"../core/canvas/person.js";import{backfillNodeApps as Ar,runPermissionsBoot as Nr}from"../core/permissions/boot.js";import{startRunNotices as Mr}from"./run-notices.js";import{startGrantEffects as Gr}from"./reconcilers/grant-effects.js";import{createGrantSync as Or,GRANT_SYNC_INTERVAL_S as Fr}from"../core/grants/sync.js";import{sweepSandboxes as Br}from"../core/grants/on-sync.js";import{rebuildCronExecutions as jr}from"./cron/run-socket.js";import{hasSyncedGrants as zr}from"../core/grants/mirror.js";import{readConfig as se}from"../core/config.js";import{reconcileUndeliveredTickets as Hr}from"./human/sweep.js";import{resetHandedOffDeliveries as qr}from"../core/canvas/deliveries.js";import{resetHandedOffDaemonActions as Ur}from"../core/canvas/daemon-actions.js";import{sweepReviews as Wr}from"./review/sweep.js";import{pidfilePath as ce}from"./pidfile.js";import{rendererWarning as Jr}from"../core/runtime/package-health.js";import{DEFAULT_INTERVAL_MS as Kr}from"./supervise-cadence.js";import{createResumeDetector as Vr}from"./resume-detector.js";import{setFatalFaultAnnouncer as Qr}from"../core/canvas/recovery.js";import{BrokerSupervisionReconciler as Yr}from"./reconcilers/broker-supervision.js";import{ControllerDeathReconciler as Xr}from"./reconcilers/controller-death.js";import{NodeLifecycleReconciler as Zr}from"./reconcilers/node-lifecycle/tick.js";import{NodeDeadlineReconciler as et}from"./reconcilers/node-deadline.js";import{CronLaneReconciler as rt}from"./reconcilers/cron-lane.js";import{DeliveryLaneReconciler as tt}from"./reconcilers/delivery-lane.js";import{ActionLaneReconciler as nt}from"./reconcilers/action-lane.js";import{endLiveExecution as ot,migrateLegacyExecutionIdentities as it}from"../core/canvas/executions.js";import{revokePriorEpochNodeTokens as at}from"../core/canvas/node-tokens.js";import{PendingReviewSubmitReconciler as st}from"./reconcilers/pending-review-submit.js";import{StorageMaintenanceReconciler as ct}from"./reconcilers/storage-maintenance.js";import{StorageDueWorkReconciler as lt}from"./reconcilers/storage-due-work.js";import{LedgerSandboxReconciler as dt}from"./reconcilers/ledger-sandbox.js";import{LedgerStorageReconciler as pt}from"./reconcilers/ledger-storage.js";import{LedgerR2Reconciler as mt}from"./reconcilers/ledger-r2.js";import{BashDeadlineReconciler as ft}from"./reconcilers/bash-deadline.js";import{FaultRetryBackstopReconciler as ut}from"./reconcilers/fault-retry-backstop.js";import{StructuredRequestReconciler as wt}from"./reconcilers/structured-request-lane.js";import{RecipeImageReconciler as ht,subscribeImageBuilt as vt}from"./reconcilers/recipe-image.js";import{reconcileStructuredRequestSidecars as gt}from"./structured-requests/boot.js";import{captureLivenessSnapshot as De,capturePidCommand as St,captureTeardownSnapshot as Ce,isPidAlive as yt,isAnyPidAlive as Le,killProcessTreePids as $e}from"../core/canvas/pid.js";import{bindFleet as bt,boundFleet as _t}from"../core/runtime/fleet.js";import{bindDaemonControl as kt,clearDaemonControl as Rt}from"./control.js";import{execDaemon as Pt,spawnDaemon as Et}from"./manage.js";import{reconcileAndMigrateBoot as It}from"../core/canvas/boot.js";import{resolveInstallId as xt}from"../core/canvas/install-id.js";import{setInstallId as Tt}from"../core/runtime/nodes.js";import{claimDaemonOwnership as Dt,daemonOwnershipApplies as Ct,daemonOwnershipClaimed as Lt,releaseDaemonOwnership as Ae}from"../core/canvas/daemon-owner.js";import{canvasDatabaseUrl as $t,closeAllDbs as le,openDb as Ne,withCanvasWrite as At}from"../core/canvas/db.js";import{importSqliteCanvasIfPresent as Nt}from"../core/canvas/import-sqlite.js";import{ensurePostgres as Mt,ensurePostgresHealthy as Gt,stopPostgres as Ot,withPostgresStartLock as Ft}from"./postgres/index.js";import{brokerSignalBus as F}from"./broker-signals/bus.js";import{brokerSignalStreams as Bt}from"./api/handlers/broker-signals.js";import{setRequestsStartRunObservers as jt}from"./api/handlers/broker-ops.js";import{reconcilePackages as zt}from"../core/graph/package-reconcile.js";import{startRepoSync as Ht}from"../core/graph/repo-sync/index.js";import{prewarmRecentRecipes as qt,reapStaleSpares as Ut}from"../core/runtime/warm-pool.js";import{DaemonFleet as Wt}from"./fleet.js";import{copyNodeFilesToRows as Jt}from"../core/runtime/first-boot-rows.js";import{BOOT_MIGRATIONS as Kt}from"./boot-migrations.js";import{emitEvent as u}from"../core/events/emit.js";import{operationIdContext as b}from"../core/events/operation-id.js";import{bindDaemonEventSource as Vt}from"../core/events/source.js";import{activateCanvasImport as Qt,MigrationBlockedError as Yt,runForcedCanvasImport as Xt}from"../migrations/activation.js";import{adoptInstallPerson as Zt,convertLegacyScopedStore as en,personStoresCopied as rn}from"../core/scoped-state/migrate.js";import{readLegacyUserSnapshot as tn}from"../core/scoped-state/legacy-profiles.js";import{heldPersons as Me,installPreludeSnapshot as nn}from"../core/scoped-state/person-store.js";import{loadPersonSettings as Ge}from"../core/scoped-state/person-rows.js";import{userScopeRoot as Oe}from"../core/scope.js";function de(e,l){return e.includes(` --canvas-home ${l} `)}i(de,"brokerCommandDeclaresCanvasHome");function pe(e){return/ --epoch (\S+) /.exec(e)?.[1]??null}i(pe,"brokerCommandEpoch");const on=5e3,Fe=150;async function an(e){const l=De();if(l===null)throw u({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness"}}),new Error("prior-epoch broker census could not capture the process table");const f=pr(),m=[];for(const[n,d]of l.processes){const y=d.command;if(d.zombie||!y.includes("broker-cli.js")||!de(y,f)||pe(y)===e)continue;const k=y.trim().split(/\s+/).at(-1)??"";k===""||k.endsWith("broker-cli.js")||m.push({pid:n,nodeId:k})}let v=0;const a=[];for(const n of m){const d=St(n.pid);if(d===null||!d.includes("broker-cli.js")||!de(d,f)||pe(d)===e||d.trim().split(/\s+/).at(-1)!==n.nodeId)continue;const y=Ce(n.pid,null);if(!y.reused){try{process.platform==="linux"&&_(x)?await L({operation:"stop",pid:n.pid}):process.kill(n.pid,"SIGTERM")}catch{continue}v++,a.push({pid:n.pid,snap:y})}}const w=Date.now()+on;for(;Date.now()<w&&a.some(n=>Le(n.snap.tree.length>0?n.snap.tree:[n.pid]));)await new Promise(n=>setTimeout(n,Fe));let g=0;for(const n of a){const d=n.snap.tree.length>0?n.snap.tree:[n.pid];Le(d)&&(process.platform==="linux"&&_(x)?await L({operation:"stop",pid:n.pid}):$e(d,"SIGKILL",n.snap.identities),g++)}g>0&&await new Promise(n=>setTimeout(n,Fe));const p=De();if(p===null)throw u({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness",stage:"final"}}),new Error("prior-epoch broker census could not capture the final process table");const c=[];for(const[n,d]of p.processes){const y=d.command;d.zombie||!y.includes("broker-cli.js")||de(y,f)&&pe(y)!==e&&c.push(n)}if(c.length>0)throw new Error(`prior-epoch broker census could not prove termination of pid(s): ${c.join(", ")}`);u({level:"info",event:"daemon.census.completed",fields:{prior_epoch_brokers:m.length,sigtermed:v,sigkilled:g}})}i(an,"reapPriorEpochBrokers");const sn=5e3;async function cn(e){const l=Array.from(e.entries());if(l.length===0)return;for(const[a]of l){const w=(await yr(a))?.status;(w==="active"||w==="idle")&&await xe(a,new Date().toISOString())}const f=l.map(([,a])=>({pid:a.pid,snap:Ce(a.pid,null),exited:a.handle.exited}));for(const a of f)if(!a.snap.reused)try{process.platform==="linux"&&_(x)?await L({operation:"stop",pid:a.pid}):process.kill(a.pid,"SIGTERM")}catch{}const m=new Promise(a=>{setTimeout(a,sn).unref()});await Promise.allSettled(f.map(a=>Promise.race([a.exited.then(()=>{}),m])));const v=[];for(const a of f)yt(a.pid)&&(process.platform==="linux"&&_(x)?await L({operation:"stop",pid:a.pid}):$e(a.snap.tree.length>0?a.snap.tree:[a.pid],"SIGKILL",a.snap.identities),v.push(a));await Promise.all(v.map(a=>a.exited));for(const[a]of l)await ot(a,{reason:"daemon-teardown"});u({level:"info",event:"daemon.fleet_teardown.completed",fields:{brokers:f.length,sigkilled:v.length}})}i(cn,"teardownFleet");function Be(){ye(sr(ce()),{recursive:!0}),or(ce(),String(process.pid),"utf8")}i(Be,"writePidfile");function je(e){return typeof e=="object"&&e!==null&&e.code==="ENOENT"}i(je,"isMissingPathError");function ze(e){const l=ce();let f;try{f=ir(l,"utf8")}catch(m){if(je(m))return;throw m}if(f.trim()!==String(e))throw new Error(`daemon readiness mirror does not name this invocation (expected pid ${e})`);try{ar(l)}catch(m){if(je(m))return;throw m}}i(ze,"removeOwnedPidfileMirror");const ln={acceptsDetachedWork:i(()=>!0,"acceptsDetachedWork"),registerDetached:i(e=>{e.catch(()=>{})},"registerDetached")},K=new Yr;Qr(e=>K.handleFatalFault(e,!1));const dn=new Xr,pn=new Zr,mn=new et,fn=new rt,un=new tt,wn=new nt,hn=new st,vn=new ct,gn=new lt,Sn=new dt,yn=new pt,bn=new mt,_n=new ft,kn=new ut,Rn=new wt,Pn=new ht;async function En(e=Date.now(),l=ln){const f=_t(),m=await K.runFleetPreamble(f);let v,a;try{const g=await Ie();v=g.filter(c=>c.status==="active"||c.status==="idle"),a=g.filter(c=>c.status==="done"||c.status==="canceled"||c.status==="dead");const p=new Set(a.map(c=>c.node_id));a.push(...(await br()).filter(c=>!p.has(c.node_id)))}catch(g){u({level:"error",event:"canvas.node_list.failed",error:g});return}await K.run(e,{rows:v,fleet:f,lifecycle:l}),await dn.run(e,{rows:v});const w=await pn.run(e,{rows:[...v,...a],fleet:f});await mn.run(e),await Rn.run(e),await Pn.run(e,{lifecycle:l}),await fn.run(e,{lifecycle:l}),await un.run(e,{lifecycle:l}),await wn.run(e,{lifecycle:l}),await hn.run(e,{lifecycle:l}),await vn.run(e,{lifecycle:l,fleet:f}),gn.run(e,{lifecycle:l}),Sn.run(e,{lifecycle:l}),yn.run(e,{lifecycle:l}),bn.run(e,{lifecycle:l}),await dr(),kn.run(e,{fleet:f,lifecycle:l}),await _n.run(e,{rows:v}),K.warnIfSaturated({fleet:f,lifecycle:l,thresholds:m,frozen:w})}i(En,"superviseTick");function V(e){return e!=="api"}i(V,"roleRunsLanes");function $(e){return e!=="reconcile"}i($,"roleServesRequests");const In=66e4;function xn(e){const l=new kr().registerAll(Rr(()=>null).filter(n=>n.pattern==="/healthz")),f=i((n,d)=>{const y=(n.url??"/").split("?")[0];if((n.method??"GET").toUpperCase()==="GET"&&y==="/healthz"){l.handle(n,d,null,{class:"person"});return}d.writeHead(404,{"content-type":"application/json; charset=utf-8"}),d.end(JSON.stringify({error:{code:"not_found",message:"this process serves only /healthz",origin:"runtime"}}))},"handler"),m=Pe();_(m)&&be(m);const v=J(f),a=new Promise((n,d)=>{v.once("error",d),v.listen(m,()=>{v.off("error",d),_e(m,384),n()})});let w,g=null;const p=e.tcp??ke()??se("user").api.tcp;if(p!==void 0&&p!==""){const n=/^(.*):(\d+)$/.exec(p.trim());if(n===null||Number(n[2])>65535)throw new Error(`invalid reconcile probe TCP address: ${p}`);const d=J(f);d.on("error",y=>u({event:"api.server.failed",level:"error",error:y})),d.listen(Number(n[2]),n[1]===""?"0.0.0.0":n[1],()=>{g=`${n[1]===""?"0.0.0.0":n[1]}:${n[2]}`}),w=d}let c=!1;return{ready:a,tcpAddress:i(()=>g,"tcpAddress"),async close(n={}){if(!c){c=!0,await Promise.all([Te(v,new Set,n.graceMs),w===void 0?Promise.resolve():Te(w,new Set,n.graceMs)]);try{_(m)&&be(m)}catch{}}}}}i(xn,"createProbeServer");const Tn="daemon is draining for shutdown and admits no new work";async function Dn(e){ye(Ee(),{recursive:!0,mode:493}),process.platform==="linux"&&_e(Ee(),493);const l=Pe();if(_(l))throw new Error(`layout refusal cannot bind occupied socket: ${l}`);const f=J((p,c)=>{if(p.method==="GET"&&p.url==="/healthz"){c.writeHead(200,{"content-type":"application/json; charset=utf-8"}),c.end(JSON.stringify({ok:!1,daemon_up:!0,store_accessible:!1,brokers_reconciled:!1,startup_phase:"blocked",startup_blocked:"layout_migration_required"}));return}c.writeHead(503,{"content-type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:{code:"layout_migration_required",message:"run crtr sys migrate while the runtime is offline",type:"server_error",origin:"runtime",retryable:!1}}))});await new Promise((p,c)=>{f.once("error",c),f.listen(l,()=>{f.off("error",c),p()})});let m;const v=e.tcp??ke();if(v){const p=/^(.*):(\d+)$/.exec(v);if(!p||Number(p[2])>65535)throw new Error(`invalid refusal TCP address: ${v}`);const c=J(f.listeners("request")[0]);await new Promise((n,d)=>{c.once("error",d),c.listen(Number(p[2]),p[1],()=>{c.off("error",d),n()})}),m=c}Be();let a;const w=i(()=>(a??=(async()=>{f.closeAllConnections(),m?.closeAllConnections(),await Promise.all([f,m].filter(p=>p!==void 0).map(p=>new Promise((c,n)=>p.close(d=>d?n(d):c())))),ze(process.pid),process.off("SIGINT",g),process.off("SIGTERM",g)})(),a),"cleanup"),g=i(()=>{w().then(()=>process.exit(0),p=>{process.stderr.write(`[crtrd] layout refusal cleanup failed: ${String(p)}
2
+ `),process.exit(1)})},"onSignal");return process.on("SIGINT",g),process.on("SIGTERM",g),w}i(Dn,"serveLayoutRefusal");async function Cn(e={}){if(vr())return Dn(e);hr();const l=i(r=>{process.stderr.write(`[crtrd] ${r}
3
+ `)},"logPostgres"),f=!!process.env.CRTR_TEST_PG_URL,m=Ct(e.role);jt(V(e.role));const v=i(async()=>m?await Dt(Re(16).toString("hex")):{claimed:!0,claim:{pid:process.pid,pid_identity:"",token:Re(16).toString("hex")}},"claimOwnership"),{database:a,ownership:w}=f?{database:{url:await $t()},ownership:await v()}:await Ft(async()=>({database:await Mt({log:l,startLockHeld:!0}),ownership:await v()}));if(!w.claimed){if(w.reason==="incumbent_alive")process.stderr.write(`[crtrd] already running (pid ${w.incumbentPid??"?"})
4
+ `);else{const r=[w.incumbentPid!==void 0?`pid ${w.incumbentPid}`:void 0,w.verdict!==void 0?`verdict ${w.verdict}`:void 0].filter(h=>h!==void 0),o=r.length>0?` (${r.join(", ")})`:"";process.stderr.write(`[crtrd] claim lost: ${w.reason}${o}
5
+ `),w.reason==="own_identity_indeterminate"&&process.stderr.write("[crtrd] cannot read its own process identity. crouter needs /proc on Linux, or the `ps` command elsewhere (Debian/Ubuntu: apt-get install procps).\n"),(w.reason==="own_identity_indeterminate"||w.reason==="incumbent_malformed")&&(process.exitCode=1)}return async()=>{}}const g=w.claim;let p=null,c=null,n=null,d,y="";try{m&&await an(g.token);for(const t of Sr())process.stderr.write(`[crtrd] ${t}
6
+ `);for(const t of await gr())process.stderr.write(`[crtrd] ${t}
7
+ `);await Ne(),await Nt(a.url,l);const r=e.role===void 0&&!await rn();let o=null;r&&(en({kind:"user",root:Oe()}),o=tn(Oe())),e.role===void 0&&nn(o??{profiles:[],defaults:{},settings:{},state:{}}),await F.listen(a.url);const h=i(t=>{Ge(t).catch(O=>u({level:"error",event:"daemon.settings.reload_failed",error:O}))},"reloadPerson");d=await F.subscribe(t=>{Me().includes(t)&&h(t)},{kinds:["settings"],onListenStart:i(()=>{for(const t of Me())h(t)},"onListenStart")}),e.role!==void 0&&Dr();const s=await At(async t=>(await Ar(t),await zt(t)));for(const t of s.conflicts)process.stderr.write(`[crtrd] plugin documents not reconciled: ${t.message}
8
+ `);for(const t of s.invalid)process.stderr.write(`[crtrd] package file is not a document: ${t.path}: ${t.error}
9
+ `);for(const t of s.quarantined)process.stderr.write(`[crtrd] plugin "${t.name}" has a damaged ${t.file} (${t.reason}); ${t.movedTo!==void 0?`moved to ${t.movedTo}`:"left in place and skipped"}
10
+ `);for(const t of s.kinds.refused)process.stderr.write(`[crtrd] plugin "${t.plugin}" kind ${t.kind} not registered: ${t.message}
11
+ `);for(const t of s.kinds.invalid)process.stderr.write(`[crtrd] plugin "${t.plugin}" declares invalid object kinds or scopes; no kinds registered and no app token: ${t.error}
12
+ `);if(y=await xt(),Tt(y),e.role===void 0&&(p=await Cr(),p!==null)){if($r(p.id),await Zt(p.id),process.platform==="darwin"){const{readConfig:t,updateConfig:O}=await import("../core/config.js");if(t("user").paths?.user_files===void 0){const{homedir:Ze}=await import("node:os");await O("user",Se=>{Se.paths??={},Se.paths.user_files??=Ze()})}}c=await Lr(),c.joinedFrom!==null&&await Ge(c.person),n=await Nr()}if(process.platform==="linux"&&_(x)&&(wr(),await L({operation:"users.sync",users:await mr()})),m){await at(g.token);const t=await Jt();Object.values(t).some(O=>O>0)&&process.stderr.write(`[crtrd] node files copied into rows: ${JSON.stringify(t)}
13
+ `)}}catch(r){await F.close();try{if(m){const{released:o}=await Ae(g);if(!o)throw new Error("daemon ownership claim was not released")}await le()}catch(o){throw new AggregateError([r,o],"daemon startup failed and cleanup also failed")}throw r}let k=!0,Q,B=Promise.resolve(),Y=!1,j,X,Z,T;const z=new Set;let ee=!1,me=!1,P,D,re,H=!1,q,A,N,M,te,ne,oe;const He=Vr();let E,S={phase:"initializing",message:"daemon startup is running the canvas import and normal services are unavailable",blockerPaths:[]},C="initial startup activation",qe=null;function Ue(){return S.phase==="ready"?null:{phase:S.phase,message:S.message,blockerPaths:S.blockerPaths}}i(Ue,"startupBlock");function R(r){z.add(r),r.finally(()=>{z.delete(r)}).catch(()=>{})}i(R,"registerDetached");const fe={acceptsDetachedWork:i(()=>k,"acceptsDetachedWork"),registerDetached:R};V(e.role)&&await Ht(fe,Ne);async function We(){try{k=!1,Bt.close(),X?.(),X=void 0,Z?.(),d?.(),Z=void 0,ne?.(),ne=void 0,await F.close(),j!==void 0&&(clearInterval(j),j=void 0),N!==void 0&&(clearInterval(N),N=void 0),oe?.(),oe=void 0;let r;do r=B,await r;while(r!==B);for(;z.size>0;)await Promise.allSettled([...z]);if(T!==void 0&&(m&&await cn(T),await T.exitsSettled(),T.dispose()),re!==void 0&&await re.close(),D!==void 0&&!H&&(H=!0,await D.close()),te?.(),te=void 0,M!==void 0){const o=M;M=void 0,await o.stop()}if(A!==void 0){const o=A;A=void 0,await o.close()}if(q!==void 0){const o=q;q=void 0,await o.close()}if(ee||(ze(process.pid),ee=!0),ee&&!me){if(m){const{released:o}=await Ae(g);if(!o)throw new Error("daemon ownership claim was not released")}me=!0,await le()}}finally{Rt(),process.off("SIGINT",he),process.off("SIGTERM",ve),process.off("exit",ge)}}i(We,"cleanupAttempt");const U=1e3;let ue=!1;function Je(){return ue?{graceMs:U}:(ue=!0,b.fresh(()=>{u({level:"info",event:"daemon.handover.armed",fields:{pid:process.pid,grace_ms:U}})}),setTimeout(()=>{Ke()},U),{graceMs:U})}i(Je,"requestHandover");async function Ke(){try{await I()}catch(r){const o=r instanceof Error?r.stack??r.message:String(r);process.stderr.write(`[crtrd] handover cleanup failed:
14
+ ${o}
15
+ `),process.exit(1)}try{process.platform==="linux"&&_(x)&&(await L({operation:"daemon.handover"}),process.exit(0)),process.pid===1&&await Pt();const r=await Et({waitForJoinLock:!0});b.fresh(()=>{u({level:"info",event:"daemon.handover.completed",fields:{predecessor_pid:process.pid,successor_pid:r.pid??r.existing_pid??null,spawned:r.started}})})}catch(r){b.fresh(()=>{u({level:"error",event:"daemon.handover.successor_failed",error:r})});const o=r instanceof Error?r.message:String(r);process.stderr.write(`[crtrd] handover successor failed to start: ${o}
16
+ `),process.exit(1)}process.exit(0)}i(Ke,"performHandover");function I(){if(P!==void 0)return P;const r=We();return P=r,r.then(()=>{P===r&&(P=void 0)},()=>{P===r&&(P=void 0)}),r}i(I,"requestCleanup");async function Ve(){S={phase:"initializing",message:Tn,blockerPaths:[]},D!==void 0&&!H&&(H=!0,await D.close({graceMs:In}))}i(Ve,"drainApi");async function we(r){try{r==="SIGTERM"&&e.role==="api"&&await Ve(),await I(),await Ot({claimed:i(async()=>{try{return m&&await Lt()}finally{await le()}},"claimed")}),process.exit(0)}catch(o){process.exitCode=1;const h=o instanceof Error?o.stack??`${o.name}: ${o.message}`:String(o);process.stderr.write(`[crtrd] ${r} cleanup failed:
17
+ ${h}
18
+ `),process.exit(1)}}i(we,"handleSignal");function he(){we("SIGINT")}i(he,"onSigint");function ve(){we("SIGTERM")}i(ve,"onSigterm");function ge(){I().catch(()=>{})}i(ge,"onExit");async function ie(r){k&&(B=B.then(r).then(()=>{},o=>{b.fresh(()=>{u({level:"error",event:"daemon.lane_job.failed",error:o})})}))}i(ie,"enqueue");async function W(){!k||Y||(He.check()&&E&&(u({level:"info",event:"daemon.resume.detected"}),R(E().catch(()=>{}))),Y=!0,await ie(async()=>{if(Y=!1,!!k)try{f||await Gt({log:l}),await En(Date.now(),fe)}catch(r){b.fresh(()=>{u({level:"error",event:"daemon.tick.failed",error:r})})}}))}i(W,"enqueueTick");let G=null;async function Qe(){if(G!==null)throw{status:500,code:"admission_failed",message:`daemon admission previously failed: ${G instanceof Error?G.message:String(G)}`};if(S.phase==="ready")return{admitted:!1,already_admitted:!0,startup_phase:"ready"};if(S.phase!=="prepared"&&S.phase!=="recovering")throw{status:409,code:"not_prepared",message:S.message};const r=S.phase==="prepared";try{return await ae(),S={phase:"ready",message:"",blockerPaths:[]},r?{admitted:!0,already_admitted:!1,startup_phase:"ready"}:{admitted:!1,already_admitted:!0,startup_phase:"ready"}}catch(o){throw G=o,{status:500,code:"admission_failed",message:`daemon admission failed: ${o instanceof Error?o.message:String(o)}`}}}i(Qe,"admitStartup");async function Ye(r){if(C!==null)throw{status:409,code:"migration_in_progress",message:`the canvas import is already running (${C})`};C="repair";try{const o=await Xt({dryRun:r.dryRun});if(r.dryRun||o.pending||S.phase!=="blocked")return{result:o,resumed:!1};if(e.prepare)return S={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]},{result:o,resumed:!1};try{return await ae(),S={phase:"ready",message:"",blockerPaths:[]},{result:o,resumed:!0}}catch(h){const s=h instanceof Error?h.message:String(h);return setTimeout(()=>{I().catch(()=>{})},1e3),{result:o,resumed:!1,resumeError:s}}}finally{C=null}}i(Ye,"repairMigrations");async function Xe(){Q=e.intervalMs??Kr;const r=g.token;T=new Wt({epoch:r,enqueue:ie}),bt(T),S={phase:"recovering",message:"daemon startup recovery is running and normal services are unavailable",blockerPaths:[]},b.fresh(()=>{u({level:"info",event:"daemon.started",fields:{pid:process.pid,interval_ms:Q}})});const o=y;b.fresh(()=>{u({level:"info",event:"daemon.install_id.resolved",fields:{install_id:o}})});const h=await xr(o);if(b.fresh(()=>{u({level:"info",event:"daemon.environment.registered",fields:{environment_id:h.environmentId,created:h.created}})}),b.fresh(()=>{let s;try{s=Jr()}catch{return}s!==null&&u({level:"warn",event:"daemon.startup.warning",fields:{stage:"termrender",warning:s.slice(0,2048)}})}),e.role!==void 0&&Tr(),!V(e.role)){S={phase:"ready",message:"",blockerPaths:[]};return}if(await b.fresh(async()=>{await Wr(),await Hr(),await qr(Date.now()),await Ur(Date.now())}),await b.fresh(async()=>{const{reconcile:s,migrated:t}=await It(it);s.changed&&u({level:"info",event:"daemon.boot_change.reconciled",fields:{cleared_count:s.cleared.length,same_boot_proven:s.sameBootProven}}),t.length>0&&u({level:"info",event:"daemon.pid_identity.migrated",fields:{migrated_count:t.length}})}),await b.fresh(async()=>{for(const s of await Ie())s.status!=="active"||s.pi_pid!=null||s.frozen_at!=null||s.respawn_not_before!=null||(await xe(s.node_id,new Date().toISOString()),u({level:"warn",event:"daemon.startup.launch_debt_recorded",node_id:s.node_id}))}),await b.fresh(async()=>{await gt();for(const s of Kt){const t=s.apply();t.changed&&u({level:"info",event:"daemon.boot_migration.applied",fields:{migration:s.id,...t.summary}})}}),R(jr()),E){const s=i(()=>{R(E().catch(()=>{})),R(tr().then(t=>M?.observe(t)).then(()=>{},t=>{u({level:"warn",event:"daemon.peer.status_failed",error:t})}))},"refresh");R(Br().then(({completion:t})=>t).catch(t=>{u({level:"error",event:"daemon.grants.sweep_failed",error:t})})),s(),N=setInterval(s,Fr*1e3),N.unref(),oe=rr({joined:!0,register:R})}await ie(async()=>{await Ut(),await qt()}),j=setInterval(()=>{W().catch(s=>u({level:"error",event:"daemon.tick.failed",error:s}))},Q),X=await vt(()=>{W().catch(s=>u({level:"error",event:"daemon.tick.failed",error:s}))}),Z=await F.subscribe(()=>{W().catch(s=>u({level:"error",event:"daemon.tick.failed",error:s}))},{kinds:["spend_limits"]}),await W()}i(Xe,"completeStartup");function ae(){return qe??=Xe().catch(r=>{throw S={phase:"initializing",message:"daemon startup recovery failed and normal services are unavailable",blockerPaths:[]},r})}i(ae,"resumeStartup");try{process.on("SIGINT",he),process.on("SIGTERM",ve),process.on("exit",ge),Vt(),p!==null&&u({level:"info",event:"daemon.person.resolved",fields:{person_id:p.id,source:p.source}}),n!==null&&u({level:"info",event:"daemon.permissions.boot",fields:{...n}}),c!==null&&u({level:"info",event:"daemon.person.retired",fields:{person_id:c.person,joined_from:c.joinedFrom,retired:c.retired,columns:c.columns}}),$(e.role)&&(q=await lr());const r=g.token;kt({epoch:r,requestHandover:Je,registerDetached:R,startupBlock:Ue,admitStartup:Qe,repairMigrations:Ye}),Be(),D=$(e.role)?_r({tcp:e.tcp}):xn(e),await D.ready;const o=cr()??se("user").document_port;if($(e.role)){const s=Pr(o,Er());try{await s.ready,re=s}catch(t){u({level:"warn",event:"daemon.document_listener.bind_failed",fields:{port:o,error:t instanceof Error?t.message:String(t)}})}}const h=se("user").runtime;if(h?.issuer&&(E=Or(h.issuer,R),h.unsandboxed===!0&&(M=nr({syncGrants:E}))),$(e.role)&&(te=await Mr()),ne=await Gr({serves:$(e.role),runsLanes:V(e.role)}),h?.unsandboxed===!0&&h.listener!==void 0&&u({level:"warn",event:"daemon.app_listener.refused_unsandboxed",fields:{reason:"runtime.unsandboxed is set; runtime.listener is ignored"}}),$(e.role)&&h!==void 0&&h.listener!==void 0&&h.unsandboxed!==!0&&(A=await Ir(h,async()=>await zr(h.issuer.replace(/\/$/,"")),E),await A.ready),e.role===void 0)try{await Qt()}catch(s){if(C=null,s instanceof Yt)return S={phase:"blocked",message:s.message,blockerPaths:s.blockerPaths},I;throw s}process.platform==="linux"&&_(x)&&(ur(),await fr("app:terminal"));try{e.prepare?S={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]}:(await ae(),S={phase:"ready",message:"",blockerPaths:[]})}finally{C=null}return I}catch(r){try{await I()}catch(o){throw new AggregateError([r,o],"daemon startup failed and cleanup also failed")}throw r}}i(Cn,"runDaemon");var bi=Cn;export{In as API_DRAIN_GRACE_MS,de as brokerCommandDeclaresCanvasHome,pe as brokerCommandEpoch,bi as default,V as roleRunsLanes,$ as roleServesRequests,Cn as runDaemon,En as superviseTick,cn as teardownFleet};
@@ -31,4 +31,4 @@ export interface DeliveryJob {
31
31
  }
32
32
  /** Spawn and settle one already-claimed delivery. `settle` writes the outcome
33
33
  * to whichever durable row owns this attempt. */
34
- export declare function runDelivery(job: DeliveryJob, settle: (settlement: DeliverySettlement) => void): Promise<void>;
34
+ export declare function runDelivery(job: DeliveryJob, settle: (settlement: DeliverySettlement) => void | Promise<void>): Promise<void>;
@@ -1 +1 @@
1
- var C=Object.defineProperty;var l=(e,t)=>C(e,"name",{value:t,configurable:!0});import{spawn as T}from"node:child_process";import{envHomeOverride as _}from"../../shared/env.js";import{deliveryBackoffMs as g}from"../../core/canvas/delivery-contract.js";import{killProcessGroup as x}from"../../core/canvas/pid.js";import{buildOperationalEnvBase as y}from"../../core/runtime/spawn-env.js";import{getNode as R}from"../../core/canvas/canvas.js";import{appProcessSandboxed as A,launchAppProcess as O}from"../../core/spaces/process.js";const I=12e4,h=2e3,E=4*1024;function w(e,t,r){return e.spawnError!==void 0?{state:"pending",nextAttemptAt:r+g(t),failure:m("spawn_error",{message:e.spawnError.message,stderr:e.stderr})}:e.timedOut?{state:"pending",nextAttemptAt:r+g(t),failure:m("timeout",{...e.signal===null?{}:{signal:e.signal},stderr:e.stderr})}:e.signal!==null?{state:"pending",nextAttemptAt:r+g(t),failure:m("signal",{signal:e.signal,stderr:e.stderr})}:e.exitCode===0?{state:"accepted"}:e.exitCode===78?{state:"permanent_failed",failure:m("exit",{exitCode:78,stderr:e.stderr})}:{state:"pending",nextAttemptAt:r+g(t),failure:m("exit",{exitCode:e.exitCode??void 0,stderr:e.stderr})}}l(w,"deliverySettlementForOutcome");function m(e,t){return{kind:e,...t}}l(m,"failure");function D(){let e=Buffer.alloc(0);return{take(t){e=t.length>=E?t.subarray(t.length-E):Buffer.concat([e,t]).subarray(Math.max(0,e.length+t.length-E))},decode(){return e.toString("utf8")}}}l(D,"stderrTailCapture");function S(e,t,r){if(r)return JSON.stringify(e);const d=e,n=d.delivery;return JSON.stringify({...d,delivery:{...n,attempt:t}})}l(S,"documentForAttempt");function L(e,t){const r=y({targetCwd:e,targetProfileId:null}),d=_();return d!==void 0&&d!==""&&(r.CRTR_HOME=d),{...r,...t}}l(L,"deliveryEnv");async function H(e,t){const r=D();let d;try{d=S(e.document,e.attempt,e.custom!==void 0)}catch(s){t(w({exitCode:null,signal:null,timedOut:!1,spawnError:s,stderr:r.decode()},e.attempt,Date.now()));return}if(process.platform==="linux"){let s;try{if(!A())throw new Error("root launcher is required for delivery");const u=e.nodeId===null||e.nodeId===void 0?null:await R(e.nodeId),f=e.app??u?.grantee??(e.nodeId==null?"app:terminal":void 0);if(!f)throw new Error(`delivery origin ${e.nodeId} was deleted without a stored app`);const o=await O({...u?{nodeId:e.nodeId}:{},app:f,command:e.argv,cwd:e.cwd,env:{...y({targetCwd:e.cwd,targetProfileId:null,skipSpawnAllow:!0}),...e.custom?{CRTR_DELIVERY_ID:e.custom.deliveryId,CRTR_CAUSE_EVENT:e.custom.messageId}:{}},stdin:d,stdoutBytes:0,stderrBytes:E,stderrTail:!0});let a,p=!1;try{a=setTimeout(()=>{p=!0,o.stop().catch(i=>{console.error(`delivery process ${o.pid} could not be stopped:`,i)})},I),a.unref?.();const c=await o.completion;s={exitCode:c.exitCode,signal:c.signal,timedOut:p,stderr:c.stderr}}finally{a!==void 0&&clearTimeout(a)}}catch(u){s={exitCode:null,signal:null,timedOut:!1,spawnError:u,stderr:""}}t(w(s,e.attempt,Date.now()));return}let n;try{n=T(e.argv[0],e.argv.slice(1),{cwd:e.cwd,env:L(e.cwd,e.custom?{CRTR_DELIVERY_ID:e.custom.deliveryId,CRTR_CAUSE_EVENT:e.custom.messageId}:void 0),detached:!0,stdio:["pipe","pipe","pipe"]})}catch(s){t(w({exitCode:null,signal:null,timedOut:!1,spawnError:s,stderr:r.decode()},e.attempt,Date.now()));return}await new Promise(s=>{let u=!1,f=!1,o,a;const p=l(i=>{u||(u=!0,a!==void 0&&clearTimeout(a),!i.timedOut&&o!==void 0&&clearTimeout(o),n.stdin?.destroy(),n.stdout?.destroy(),n.stderr?.destroy(),t(w(i,e.attempt,Date.now())),s())},"finish");n.stdout?.on("data",()=>{}),n.stderr?.on("data",i=>r.take(i)),n.stdin?.on("error",()=>{}),n.on("error",i=>{p({exitCode:null,signal:null,timedOut:!1,spawnError:i,stderr:r.decode()})}),n.on("exit",(i,v)=>{p({exitCode:i,signal:v,timedOut:f,stderr:r.decode()})});const c=l(()=>{n.pid!==void 0&&(x(n.pid),o=setTimeout(()=>x(n.pid,"SIGKILL"),h),o.unref?.())},"killWithEscalation");a=setTimeout(()=>{f=!0,c()},I),a.unref?.(),n.stdin?.end(d,"utf8")})}l(H,"runDelivery");export{E as DELIVERY_STDERR_TAIL_BYTES,I as DELIVERY_TIMEOUT_MS,w as deliverySettlementForOutcome,H as runDelivery};
1
+ var T=Object.defineProperty;var a=(e,t)=>T(e,"name",{value:t,configurable:!0});import{spawn as _}from"node:child_process";import{envHomeOverride as R}from"../../shared/env.js";import{deliveryBackoffMs as g}from"../../core/canvas/delivery-contract.js";import{killProcessGroup as y}from"../../core/canvas/pid.js";import{buildOperationalEnvBase as v}from"../../core/runtime/spawn-env.js";import{getNode as h}from"../../core/canvas/canvas.js";import{appProcessSandboxed as A,launchAppProcess as O}from"../../core/spaces/process.js";const I=12e4,D=2e3,w=4*1024;function E(e,t,r){return e.spawnError!==void 0?{state:"pending",nextAttemptAt:r+g(t),failure:m("spawn_error",{message:e.spawnError.message,stderr:e.stderr})}:e.timedOut?{state:"pending",nextAttemptAt:r+g(t),failure:m("timeout",{...e.signal===null?{}:{signal:e.signal},stderr:e.stderr})}:e.signal!==null?{state:"pending",nextAttemptAt:r+g(t),failure:m("signal",{signal:e.signal,stderr:e.stderr})}:e.exitCode===0?{state:"accepted"}:e.exitCode===78?{state:"permanent_failed",failure:m("exit",{exitCode:78,stderr:e.stderr})}:{state:"pending",nextAttemptAt:r+g(t),failure:m("exit",{exitCode:e.exitCode??void 0,stderr:e.stderr})}}a(E,"deliverySettlementForOutcome");function m(e,t){return{kind:e,...t}}a(m,"failure");function S(){let e=Buffer.alloc(0);return{take(t){e=t.length>=w?t.subarray(t.length-w):Buffer.concat([e,t]).subarray(Math.max(0,e.length+t.length-w))},decode(){return e.toString("utf8")}}}a(S,"stderrTailCapture");function L(e,t,r){if(r)return JSON.stringify(e);const i=e,n=i.delivery;return JSON.stringify({...i,delivery:{...n,attempt:t}})}a(L,"documentForAttempt");function B(e,t){const r=v({targetCwd:e,targetProfileId:null}),i=R();return i!==void 0&&i!==""&&(r.CRTR_HOME=i),{...r,...t}}a(B,"deliveryEnv");async function U(e,t){const r=S();let i;try{i=L(e.document,e.attempt,e.custom!==void 0)}catch(d){await t(E({exitCode:null,signal:null,timedOut:!1,spawnError:d,stderr:r.decode()},e.attempt,Date.now()));return}if(process.platform==="linux"){let d;try{if(!A())throw new Error("root launcher is required for delivery");const u=e.nodeId===null||e.nodeId===void 0?null:await h(e.nodeId),c=e.app??u?.grantee??(e.nodeId==null?"app:terminal":void 0);if(!c)throw new Error(`delivery origin ${e.nodeId} was deleted without a stored app`);const f=await O({...u?{nodeId:e.nodeId}:{},app:c,command:e.argv,cwd:e.cwd,env:{...v({targetCwd:e.cwd,targetProfileId:null,skipSpawnAllow:!0}),...e.custom?{CRTR_DELIVERY_ID:e.custom.deliveryId,CRTR_CAUSE_EVENT:e.custom.messageId}:{}},stdin:i,stdoutBytes:0,stderrBytes:w,stderrTail:!0});let s,o=!1;try{s=setTimeout(()=>{o=!0,f.stop().catch(x=>{console.error(`delivery process ${f.pid} could not be stopped:`,x)})},I),s.unref?.();const p=await f.completion;d={exitCode:p.exitCode,signal:p.signal,timedOut:o,stderr:p.stderr}}finally{s!==void 0&&clearTimeout(s)}}catch(u){d={exitCode:null,signal:null,timedOut:!1,spawnError:u,stderr:""}}await t(E(d,e.attempt,Date.now()));return}let n;try{n=_(e.argv[0],e.argv.slice(1),{cwd:e.cwd,env:B(e.cwd,e.custom?{CRTR_DELIVERY_ID:e.custom.deliveryId,CRTR_CAUSE_EVENT:e.custom.messageId}:void 0),detached:!0,stdio:["pipe","pipe","pipe"]})}catch(d){await t(E({exitCode:null,signal:null,timedOut:!1,spawnError:d,stderr:r.decode()},e.attempt,Date.now()));return}await new Promise((d,u)=>{let c=!1,f=!1,s,o;const p=a(l=>{c||(c=!0,o!==void 0&&clearTimeout(o),!l.timedOut&&s!==void 0&&clearTimeout(s),n.stdin?.destroy(),n.stdout?.destroy(),n.stderr?.destroy(),Promise.resolve(t(E(l,e.attempt,Date.now()))).then(d,u))},"finish");n.stdout?.on("data",()=>{}),n.stderr?.on("data",l=>r.take(l)),n.stdin?.on("error",()=>{}),n.on("error",l=>{p({exitCode:null,signal:null,timedOut:!1,spawnError:l,stderr:r.decode()})}),n.on("exit",(l,C)=>{p({exitCode:l,signal:C,timedOut:f,stderr:r.decode()})});const x=a(()=>{n.pid!==void 0&&(y(n.pid),s=setTimeout(()=>y(n.pid,"SIGKILL"),D),s.unref?.())},"killWithEscalation");o=setTimeout(()=>{f=!0,x()},I),o.unref?.(),n.stdin?.end(i,"utf8")})}a(U,"runDelivery");export{w as DELIVERY_STDERR_TAIL_BYTES,I as DELIVERY_TIMEOUT_MS,E as deliverySettlementForOutcome,U as runDelivery};
@@ -1,5 +1,5 @@
1
1
  /** Every lane `superviseTick` runs, named as in crtrd.ts. Keep in tick order. */
2
- export declare const TICK_LANES: readonly ["brokerSupervision", "controllerDeath", "nodeLifecycle", "nodeDeadline", "structuredRequests", "cronLane", "deliveryLane", "actionLane", "pendingReviewSubmit", "storageMaintenance", "storageDueWork", "pollRunObservers", "faultRetryBackstop", "bashDeadline"];
2
+ export declare const TICK_LANES: readonly ["brokerSupervision", "controllerDeath", "nodeLifecycle", "nodeDeadline", "structuredRequests", "recipeImage", "cronLane", "deliveryLane", "actionLane", "pendingReviewSubmit", "storageMaintenance", "storageDueWork", "pollRunObservers", "faultRetryBackstop", "bashDeadline", "ledgerSandbox", "ledgerStorage", "ledgerR2"];
3
3
  export interface DueSource {
4
4
  /** Epoch ms of the earliest timed work this lane owes; `now` when work is due already; null when none. */
5
5
  nextDue(now: number): Promise<number | null>;
@@ -1 +1 @@
1
- var f=Object.defineProperty;var r=(e,n)=>f(e,"name",{value:n,configurable:!0});const d=["brokerSupervision","controllerDeath","nodeLifecycle","nodeDeadline","structuredRequests","cronLane","deliveryLane","actionLane","pendingReviewSubmit","storageMaintenance","storageDueWork","pollRunObservers","faultRetryBackstop","bashDeadline"],u=new Map,c=new Map;function p(e,n){u.set(e,n)}r(p,"registerDueSource");function x(e,n){c.set(e,n)}r(x,"declareBusyOnly");function D(){return[...u.keys()]}r(D,"dueSourceLanes");function b(){return[...c.keys()]}r(b,"busyOnlyLanes");async function L(e,n=()=>{}){let o=null,s=!1;for(const[a,i]of u){let t;try{t=await i.nextDue(e)}catch(l){n(a,l),t=e}t===null||!Number.isFinite(t)||(t<=e&&(s=!0),(o===null||t<o)&&(o=t))}return{dueNow:s,nextDueMs:o}}r(L,"collectDue");export{d as TICK_LANES,b as busyOnlyLanes,L as collectDue,x as declareBusyOnly,D as dueSourceLanes,p as registerDueSource};
1
+ var d=Object.defineProperty;var r=(e,n)=>d(e,"name",{value:n,configurable:!0});const p=["brokerSupervision","controllerDeath","nodeLifecycle","nodeDeadline","structuredRequests","recipeImage","cronLane","deliveryLane","actionLane","pendingReviewSubmit","storageMaintenance","storageDueWork","pollRunObservers","faultRetryBackstop","bashDeadline","ledgerSandbox","ledgerStorage","ledgerR2"],u=new Map,a=new Map;function y(e,n){u.set(e,n)}r(y,"registerDueSource");function x(e,n){a.set(e,n)}r(x,"declareBusyOnly");function g(){return[...u.keys()]}r(g,"dueSourceLanes");function b(){return[...a.keys()]}r(b,"busyOnlyLanes");async function D(e,n=()=>{}){let o=null,s=!1;for(const[c,l]of u){let t;try{t=await l.nextDue(e)}catch(i){n(c,i),t=e}t===null||!Number.isFinite(t)||(t<=e&&(s=!0),(o===null||t<o)&&(o=t))}return{dueNow:s,nextDueMs:o}}r(D,"collectDue");export{p as TICK_LANES,b as busyOnlyLanes,D as collectDue,x as declareBusyOnly,g as dueSourceLanes,y as registerDueSource};