@north-light/cli 0.3.561 → 0.3.563

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 (579) 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/build-root.d.ts +1 -0
  23. package/dist/build-root.js +1 -1
  24. package/dist/builtin-memory/crouter-sdk/release-notes.md +4 -0
  25. package/dist/cli.js +7 -7
  26. package/dist/clients/attach/chrome/bash-jobs.d.ts +11 -1
  27. package/dist/clients/attach/chrome/bash-jobs.js +1 -1
  28. package/dist/clients/attach/chrome/canvas-panels.d.ts +1 -1
  29. package/dist/clients/attach/chrome/roster.js +6 -4
  30. package/dist/clients/attach/overlays/auth.js +2 -2
  31. package/dist/clients/attach/render/chat-view.js +1 -1
  32. package/dist/clients/attach/viewer.js +550 -567
  33. package/dist/commands/canvas-config.js +2 -2
  34. package/dist/commands/canvas-use.js +1 -1
  35. package/dist/commands/human.js +2 -2
  36. package/dist/commands/node/bash.js +3 -3
  37. package/dist/commands/node/create.js +2 -2
  38. package/dist/commands/person-settings.d.ts +1 -0
  39. package/dist/commands/person-settings.js +1 -0
  40. package/dist/commands/pkg/market-manage.d.ts +2 -2
  41. package/dist/commands/pkg/market-manage.js +1 -1
  42. package/dist/commands/pkg/plugin-manage.js +13 -13
  43. package/dist/commands/pkg/shared.js +1 -1
  44. package/dist/commands/profile/default.js +1 -1
  45. package/dist/commands/push.js +2 -3
  46. package/dist/commands/sys/config.js +1 -1
  47. package/dist/commands/sys/connect.js +1 -1
  48. package/dist/commands/sys/context/sysprompt.js +2 -2
  49. package/dist/commands/sys/doctor.js +1 -1
  50. package/dist/commands/sys/join.js +1 -1
  51. package/dist/commands/sys/panels/broker-limits-panel.js +1 -1
  52. package/dist/commands/sys/panels/keybindings-panel.js +1 -1
  53. package/dist/commands/sys/panels/models-panel.d.ts +8 -2
  54. package/dist/commands/sys/panels/models-panel.js +1 -1
  55. package/dist/commands/sys/panels/prefs-panel.d.ts +1 -0
  56. package/dist/commands/sys/panels/prefs-panel.js +1 -1
  57. package/dist/commands/sys/panels/profiles-panel.js +1 -1
  58. package/dist/commands/sys/panels/provider-panel.d.ts +4 -0
  59. package/dist/commands/sys/panels/provider-panel.js +1 -1
  60. package/dist/commands/sys/provider-login.js +5 -5
  61. package/dist/commands/sys/setup-core.js +3 -3
  62. package/dist/commands/sys/setup-settings-runner.js +1 -1
  63. package/dist/commands/sys/spend.js +3 -3
  64. package/dist/commands/sys/update.js +1 -1
  65. package/dist/core/auth-file.d.ts +11 -19
  66. package/dist/core/auth-file.js +1 -2
  67. package/dist/core/auto-update.d.ts +1 -1
  68. package/dist/core/auto-update.js +4 -4
  69. package/dist/core/bash-jobs.d.ts +29 -71
  70. package/dist/core/bash-jobs.js +9 -10
  71. package/dist/core/blobs/directory.d.ts +3 -0
  72. package/dist/core/blobs/directory.js +1 -0
  73. package/dist/core/blobs/fault.d.ts +3 -0
  74. package/dist/core/blobs/fault.js +1 -0
  75. package/dist/core/blobs/index.d.ts +21 -0
  76. package/dist/core/blobs/index.js +1 -0
  77. package/dist/core/blobs/r2.d.ts +9 -0
  78. package/dist/core/blobs/r2.js +4 -0
  79. package/dist/core/bootstrap.d.ts +1 -1
  80. package/dist/core/bootstrap.js +2 -2
  81. package/dist/core/canvas/canvas.d.ts +9 -7
  82. package/dist/core/canvas/canvas.js +8 -8
  83. package/dist/core/canvas/crons.d.ts +3 -2
  84. package/dist/core/canvas/crons.js +3 -3
  85. package/dist/core/canvas/daemon-owner.d.ts +5 -0
  86. package/dist/core/canvas/daemon-owner.js +1 -1
  87. package/dist/core/canvas/db.d.ts +10 -9
  88. package/dist/core/canvas/db.js +4 -4
  89. package/dist/core/canvas/executions.d.ts +100 -4
  90. package/dist/core/canvas/executions.js +1 -1
  91. package/dist/core/canvas/export-sqlite.js +1 -1
  92. package/dist/core/canvas/focuses.d.ts +4 -0
  93. package/dist/core/canvas/focuses.js +1 -1
  94. package/dist/core/canvas/history.js +9 -9
  95. package/dist/core/canvas/managed-worktree-sweep-candidates.d.ts +2 -1
  96. package/dist/core/canvas/managed-worktree-sweep-candidates.js +3 -2
  97. package/dist/core/canvas/node-tokens.d.ts +1 -4
  98. package/dist/core/canvas/node-tokens.js +3 -3
  99. package/dist/core/canvas/paths.d.ts +0 -1
  100. package/dist/core/canvas/paths.js +1 -1
  101. package/dist/core/canvas/person-state.d.ts +26 -0
  102. package/dist/core/canvas/person-state.js +1 -0
  103. package/dist/core/canvas/person.d.ts +3 -10
  104. package/dist/core/canvas/person.js +7 -7
  105. package/dist/core/canvas/reconciler-leases.d.ts +12 -0
  106. package/dist/core/canvas/reconciler-leases.js +8 -0
  107. package/dist/core/canvas/recovery.d.ts +4 -0
  108. package/dist/core/canvas/recovery.js +1 -1
  109. package/dist/core/canvas/schema-pg/003-fleet-removals.sql +1 -1
  110. package/dist/core/canvas/schema-pg/005-fleet-blobs.sql +13 -0
  111. package/dist/core/canvas/schema-pg/006-job-rows.sql +20 -0
  112. package/dist/core/canvas/schema-pg/007-ledger-check.sql +117 -0
  113. package/dist/core/canvas/schema-version.d.ts +1 -1
  114. package/dist/core/canvas/schema-version.js +1 -1
  115. package/dist/core/canvas/sessions.d.ts +8 -11
  116. package/dist/core/canvas/sessions.js +1 -4
  117. package/dist/core/canvas/tree.d.ts +4 -2
  118. package/dist/core/canvas/tree.js +6 -6
  119. package/dist/core/canvas/types.d.ts +7 -0
  120. package/dist/core/command-hooks/transport/exec-lifecycle.js +1 -1
  121. package/dist/core/command-plugins/bundle.d.ts +1 -1
  122. package/dist/core/command-plugins/bundle.js +1 -1
  123. package/dist/core/command-plugins/compose.d.ts +1 -1
  124. package/dist/core/command-plugins/discovery.d.ts +0 -1
  125. package/dist/core/command-plugins/discovery.js +1 -1
  126. package/dist/core/command-plugins/help-addenda.js +1 -1
  127. package/dist/core/command-plugins/presence.d.ts +2 -0
  128. package/dist/core/command-plugins/presence.js +1 -1
  129. package/dist/core/config.d.ts +16 -15
  130. package/dist/core/config.js +1 -1
  131. package/dist/core/conversation-store/client-worker.js +1 -1
  132. package/dist/core/conversation-store/client.js +2 -2
  133. package/dist/core/conversation-store/index.d.ts +9 -11
  134. package/dist/core/conversation-store/index.js +1 -1
  135. package/dist/core/conversation-store/patch.js +1 -1
  136. package/dist/core/conversation-store/row-store.d.ts +14 -0
  137. package/dist/core/conversation-store/row-store.js +13 -0
  138. package/dist/core/directory/join.d.ts +5 -3
  139. package/dist/core/directory/join.js +1 -1
  140. package/dist/core/env-name.d.ts +1 -0
  141. package/dist/core/env-name.js +1 -1
  142. package/dist/core/environment-recipe.d.ts +15 -0
  143. package/dist/core/environment-recipe.js +1 -0
  144. package/dist/core/environments.js +1 -1
  145. package/dist/core/events/emit.js +2 -2
  146. package/dist/core/events/run-event-chunks.d.ts +25 -0
  147. package/dist/core/events/run-event-chunks.js +5 -0
  148. package/dist/core/feed/messages.js +5 -5
  149. package/dist/core/grants/mirror.d.ts +15 -0
  150. package/dist/core/grants/mirror.js +15 -3
  151. package/dist/core/grants/on-sync.d.ts +9 -5
  152. package/dist/core/grants/on-sync.js +1 -1
  153. package/dist/core/grants/remove.d.ts +1 -6
  154. package/dist/core/grants/remove.js +1 -1
  155. package/dist/core/grants/sync.js +1 -1
  156. package/dist/core/grants/watch.d.ts +15 -9
  157. package/dist/core/grants/watch.js +1 -1
  158. package/dist/core/graph/custom.js +8 -8
  159. package/dist/core/graph/jobs.d.ts +83 -5
  160. package/dist/core/graph/jobs.js +11 -2
  161. package/dist/core/graph/kinds/job.js +2 -2
  162. package/dist/core/graph/names.js +7 -7
  163. package/dist/core/graph/objects.d.ts +5 -1
  164. package/dist/core/graph/objects.js +11 -11
  165. package/dist/core/graph/reader.d.ts +3 -0
  166. package/dist/core/graph/reader.js +3 -3
  167. package/dist/core/graph/repo-sync/identity.d.ts +3 -1
  168. package/dist/core/graph/repo-sync/identity.js +1 -1
  169. package/dist/core/graph/repo-sync/repos.d.ts +1 -1
  170. package/dist/core/graph/repo-sync/repos.js +2 -2
  171. package/dist/core/graph/repo-sync/sync.js +2 -2
  172. package/dist/core/installed-plugins.js +1 -1
  173. package/dist/core/keybindings/persistence.d.ts +1 -1
  174. package/dist/core/keybindings/persistence.js +1 -1
  175. package/dist/core/layout-migrate/loose-keys.d.ts +1 -1
  176. package/dist/core/layout-migrate/loose-keys.js +1 -1
  177. package/dist/core/ledger/account.d.ts +12 -0
  178. package/dist/core/ledger/account.js +2 -0
  179. package/dist/core/ledger/charges.d.ts +2 -2
  180. package/dist/core/ledger/charges.js +1 -1
  181. package/dist/core/ledger/check.d.ts +5 -1
  182. package/dist/core/ledger/check.js +1 -7
  183. package/dist/core/ledger/chunks.d.ts +8 -0
  184. package/dist/core/ledger/chunks.js +2 -0
  185. package/dist/core/ledger/index.d.ts +2 -1
  186. package/dist/core/ledger/index.js +1 -1
  187. package/dist/core/ledger/infra-allowance.d.ts +9 -0
  188. package/dist/core/ledger/infra-allowance.js +1 -0
  189. package/dist/core/ledger/limits.js +3 -3
  190. package/dist/core/ledger/payer.d.ts +1 -1
  191. package/dist/core/ledger/payer.js +1 -1
  192. package/dist/core/ledger/read.d.ts +14 -4
  193. package/dist/core/ledger/read.js +2 -3
  194. package/dist/core/ledger/totals.d.ts +5 -16
  195. package/dist/core/ledger/totals.js +4 -4
  196. package/dist/core/ledger/types.d.ts +12 -3
  197. package/dist/core/llm/run.js +2 -2
  198. package/dist/core/managed-providers.d.ts +72 -0
  199. package/dist/core/managed-providers.js +1 -0
  200. package/dist/core/model-auth/api-credential-store.d.ts +10 -0
  201. package/dist/core/model-auth/api-credential-store.js +1 -0
  202. package/dist/core/model-auth/daemon-login.d.ts +1 -1
  203. package/dist/core/model-auth/daemon-login.js +1 -1
  204. package/dist/core/model-auth/flows.d.ts +5 -4
  205. package/dist/core/model-auth/flows.js +1 -1
  206. package/dist/core/model-auth/handoff-jti.d.ts +3 -4
  207. package/dist/core/model-auth/handoff-jti.js +1 -1
  208. package/dist/core/model-auth/readiness.js +1 -1
  209. package/dist/core/model-config.d.ts +1 -1
  210. package/dist/core/model-config.js +1 -1
  211. package/dist/core/model-routes-pool.d.ts +14 -0
  212. package/dist/core/model-routes-pool.js +1 -0
  213. package/dist/core/model-routes.d.ts +11 -16
  214. package/dist/core/model-routes.js +1 -1
  215. package/dist/core/peer/ingest.d.ts +2 -0
  216. package/dist/core/peer/ingest.js +1 -1
  217. package/dist/core/peer/stubs.d.ts +8 -0
  218. package/dist/core/peer/stubs.js +1 -1
  219. package/dist/core/permissions/evaluator.d.ts +7 -2
  220. package/dist/core/permissions/evaluator.js +2 -2
  221. package/dist/core/plugin-env.js +1 -1
  222. package/dist/core/pool-credential-store.d.ts +2 -5
  223. package/dist/core/pool-credential-store.js +1 -1
  224. package/dist/core/profiles/app-env-store.d.ts +5 -5
  225. package/dist/core/profiles/app-env-store.js +1 -2
  226. package/dist/core/profiles/default-binding.d.ts +5 -5
  227. package/dist/core/profiles/default-binding.js +1 -1
  228. package/dist/core/profiles/env-store.d.ts +10 -21
  229. package/dist/core/profiles/env-store.js +1 -2
  230. package/dist/core/profiles/manifest.d.ts +14 -18
  231. package/dist/core/profiles/manifest.js +1 -1
  232. package/dist/core/profiles/relocate.js +1 -1
  233. package/dist/core/profiles/select.js +21 -21
  234. package/dist/core/provider-credential.d.ts +1 -1
  235. package/dist/core/provider-credential.js +1 -1
  236. package/dist/core/provider-management.d.ts +3 -4
  237. package/dist/core/provider-management.js +1 -1
  238. package/dist/core/providers/bundles.js +2 -2
  239. package/dist/core/providers/call.d.ts +3 -34
  240. package/dist/core/providers/description.d.ts +34 -0
  241. package/dist/core/providers/description.js +0 -0
  242. package/dist/core/providers/token-cache.d.ts +14 -14
  243. package/dist/core/providers/token-cache.js +1 -1
  244. package/dist/core/review/companion.js +1 -1
  245. package/dist/core/runs/activity.d.ts +1 -1
  246. package/dist/core/runs/activity.js +1 -1
  247. package/dist/core/runs/events.d.ts +7 -5
  248. package/dist/core/runs/events.js +2 -6
  249. package/dist/core/runs/operations.d.ts +1 -1
  250. package/dist/core/runs/operations.js +6 -6
  251. package/dist/core/runtime/bearings.js +1 -1
  252. package/dist/core/runtime/broker/attachments-fetch.d.ts +5 -0
  253. package/dist/core/runtime/broker/attachments-fetch.js +1 -0
  254. package/dist/core/runtime/broker/bash-job-launch.d.ts +4 -2
  255. package/dist/core/runtime/broker/bash-job-launch.js +1 -1
  256. package/dist/core/runtime/broker/boot-state.d.ts +9 -0
  257. package/dist/core/runtime/broker/boot-state.js +1 -0
  258. package/dist/core/runtime/broker/cli-relay.d.ts +2 -1
  259. package/dist/core/runtime/broker/cli-relay.js +1 -1
  260. package/dist/core/runtime/broker/client-registry.d.ts +3 -3
  261. package/dist/core/runtime/broker/daemon-ops.d.ts +10 -1
  262. package/dist/core/runtime/broker/daemon-ops.js +1 -1
  263. package/dist/core/runtime/broker/fault-retry.d.ts +2 -0
  264. package/dist/core/runtime/broker/fault-retry.js +1 -1
  265. package/dist/core/runtime/broker/frame-engine-control.js +1 -1
  266. package/dist/core/runtime/broker/held-deferred-inbox.d.ts +3 -0
  267. package/dist/core/runtime/broker/held-deferred-inbox.js +1 -1
  268. package/dist/core/runtime/broker/job-reporter.d.ts +18 -0
  269. package/dist/core/runtime/broker/job-reporter.js +2 -0
  270. package/dist/core/runtime/broker/model-adapter.d.ts +3 -0
  271. package/dist/core/runtime/broker/model-adapter.js +3 -2
  272. package/dist/core/runtime/broker/signal-stream.js +1 -1
  273. package/dist/core/runtime/broker/spilled-body.d.ts +5 -0
  274. package/dist/core/runtime/broker/spilled-body.js +1 -0
  275. package/dist/core/runtime/broker/summaries-post.d.ts +5 -0
  276. package/dist/core/runtime/broker/summaries-post.js +1 -0
  277. package/dist/core/runtime/broker/tool-groups.d.ts +3 -0
  278. package/dist/core/runtime/broker/tool-groups.js +1 -1
  279. package/dist/core/runtime/broker/turn-replies.d.ts +1 -0
  280. package/dist/core/runtime/broker/turn-replies.js +1 -1
  281. package/dist/core/runtime/broker-persona-guidance.js +2 -2
  282. package/dist/core/runtime/broker.js +1 -1
  283. package/dist/core/runtime/deliver-live.js +1 -1
  284. package/dist/core/runtime/first-boot-rows.d.ts +8 -0
  285. package/dist/core/runtime/first-boot-rows.js +2 -0
  286. package/dist/core/runtime/fleet.d.ts +13 -12
  287. package/dist/core/runtime/front-door-login.js +1 -1
  288. package/dist/core/runtime/front-door.js +6 -6
  289. package/dist/core/runtime/headless-pi.js +1 -1
  290. package/dist/core/runtime/host.d.ts +14 -3
  291. package/dist/core/runtime/host.js +3 -3
  292. package/dist/core/runtime/invocation.d.ts +6 -0
  293. package/dist/core/runtime/kickoff.d.ts +2 -14
  294. package/dist/core/runtime/kickoff.js +17 -19
  295. package/dist/core/runtime/launch-config-worker.js +1 -1
  296. package/dist/core/runtime/launch.js +2 -2
  297. package/dist/core/runtime/model-registry.js +1 -1
  298. package/dist/core/runtime/node-read.d.ts +4 -6
  299. package/dist/core/runtime/node-read.js +3 -3
  300. package/dist/core/runtime/nodes.js +1 -1
  301. package/dist/core/runtime/outcome-document.d.ts +0 -1
  302. package/dist/core/runtime/outcome-document.js +1 -1
  303. package/dist/core/runtime/output-result.d.ts +15 -0
  304. package/dist/core/runtime/output-result.js +5 -0
  305. package/dist/core/runtime/placement.d.ts +17 -1
  306. package/dist/core/runtime/placement.js +1 -1
  307. package/dist/core/runtime/provider-credential-gate.d.ts +6 -2
  308. package/dist/core/runtime/provider-credential-gate.js +2 -2
  309. package/dist/core/runtime/recovery-hold.d.ts +21 -2
  310. package/dist/core/runtime/recovery-hold.js +2 -1
  311. package/dist/core/runtime/recycle.js +1 -1
  312. package/dist/core/runtime/reset.js +1 -1
  313. package/dist/core/runtime/revive.d.ts +2 -2
  314. package/dist/core/runtime/revive.js +2 -2
  315. package/dist/core/runtime/runner/blaxel/client.d.ts +81 -0
  316. package/dist/core/runtime/runner/blaxel/client.js +1 -0
  317. package/dist/core/runtime/runner/blaxel/image-build.d.ts +74 -0
  318. package/dist/core/runtime/runner/blaxel/image-build.js +5 -0
  319. package/dist/core/runtime/runner/blaxel/runner.d.ts +41 -0
  320. package/dist/core/runtime/runner/blaxel/runner.js +3 -0
  321. package/dist/core/runtime/runner/index.d.ts +8 -0
  322. package/dist/core/runtime/runner/index.js +1 -0
  323. package/dist/core/runtime/runner/process.d.ts +11 -0
  324. package/dist/core/runtime/runner/process.js +1 -0
  325. package/dist/core/runtime/runner/types.d.ts +95 -0
  326. package/dist/core/runtime/runner/types.js +0 -0
  327. package/dist/core/runtime/situational-context.d.ts +3 -6
  328. package/dist/core/runtime/situational-context.js +2 -2
  329. package/dist/core/runtime/situational-live.js +1 -1
  330. package/dist/core/runtime/spawn-env-rows.d.ts +5 -0
  331. package/dist/core/runtime/spawn-env-rows.js +1 -0
  332. package/dist/core/runtime/spawn-env.d.ts +4 -18
  333. package/dist/core/runtime/spawn-env.js +1 -1
  334. package/dist/core/runtime/spawn.js +2 -2
  335. package/dist/core/runtime/stop-guard.d.ts +1 -1
  336. package/dist/core/runtime/stop-guard.js +2 -8
  337. package/dist/core/runtime/structured-output.d.ts +0 -67
  338. package/dist/core/runtime/structured-output.js +3 -8
  339. package/dist/core/runtime/tmux-driver.d.ts +8 -0
  340. package/dist/core/runtime/tmux-driver.js +6 -6
  341. package/dist/core/runtime/view-socket.d.ts +20 -0
  342. package/dist/core/runtime/view-socket.js +3 -1
  343. package/dist/core/runtime/warm-pool.d.ts +13 -1
  344. package/dist/core/runtime/warm-pool.js +1 -1
  345. package/dist/core/scoped-state/legacy-profiles.d.ts +11 -0
  346. package/dist/core/scoped-state/legacy-profiles.js +1 -0
  347. package/dist/core/scoped-state/materialized-plugins.d.ts +18 -0
  348. package/dist/core/scoped-state/materialized-plugins.js +1 -0
  349. package/dist/core/scoped-state/migrate.d.ts +8 -1
  350. package/dist/core/scoped-state/migrate.js +3 -3
  351. package/dist/core/scoped-state/person-rows.d.ts +10 -0
  352. package/dist/core/scoped-state/person-rows.js +6 -0
  353. package/dist/core/scoped-state/person-store.d.ts +72 -0
  354. package/dist/core/scoped-state/person-store.js +1 -0
  355. package/dist/core/secrets/blaxel-launch.d.ts +24 -0
  356. package/dist/core/secrets/blaxel-launch.js +4 -0
  357. package/dist/core/secrets/blaxel-register.d.ts +2 -0
  358. package/dist/core/secrets/blaxel-register.js +1 -0
  359. package/dist/core/secrets/env-rows.d.ts +17 -0
  360. package/dist/core/secrets/env-rows.js +4 -0
  361. package/dist/core/secrets/environment-owner.d.ts +8 -0
  362. package/dist/core/secrets/environment-owner.js +1 -0
  363. package/dist/core/secrets/environment-secrets.d.ts +14 -0
  364. package/dist/core/secrets/environment-secrets.js +1 -0
  365. package/dist/core/secrets/index.d.ts +2 -0
  366. package/dist/core/secrets/index.js +1 -0
  367. package/dist/core/secrets/key.d.ts +3 -0
  368. package/dist/core/secrets/key.js +2 -0
  369. package/dist/core/secrets/seal.d.ts +18 -0
  370. package/dist/core/secrets/seal.js +1 -0
  371. package/dist/core/spaces/mounts.d.ts +1 -1
  372. package/dist/core/spaces/mounts.js +1 -1
  373. package/dist/core/spaces/process.d.ts +1 -1
  374. package/dist/core/spaces/stop.d.ts +7 -5
  375. package/dist/core/spaces/stop.js +2 -2
  376. package/dist/core/spaces/users.d.ts +2 -1
  377. package/dist/core/spaces/users.js +1 -1
  378. package/dist/core/storage/attachments.d.ts +11 -8
  379. package/dist/core/storage/attachments.js +1 -1
  380. package/dist/core/storage/tables.d.ts +4 -0
  381. package/dist/core/storage/tables.js +3 -3
  382. package/dist/core/subscription-state.d.ts +34 -72
  383. package/dist/core/subscription-state.js +1 -4
  384. package/dist/core/substrate/delivery/plan.js +1 -1
  385. package/dist/core/user-settings.js +1 -1
  386. package/dist/daemon/api/app-listener.js +10 -10
  387. package/dist/daemon/api/auth/person-bearer.d.ts +14 -0
  388. package/dist/daemon/api/auth/person-bearer.js +1 -0
  389. package/dist/daemon/api/bridge.js +4 -4
  390. package/dist/daemon/api/document-listener.js +3 -3
  391. package/dist/daemon/api/environment-of.d.ts +18 -0
  392. package/dist/daemon/api/environment-of.js +5 -0
  393. package/dist/daemon/api/handlers/analytics.js +1 -1
  394. package/dist/daemon/api/handlers/app-profiles.d.ts +2 -2
  395. package/dist/daemon/api/handlers/app-profiles.js +1 -1
  396. package/dist/daemon/api/handlers/bash-jobs.js +1 -1
  397. package/dist/daemon/api/handlers/bash.js +1 -1
  398. package/dist/daemon/api/handlers/broker-ops.d.ts +3 -0
  399. package/dist/daemon/api/handlers/broker-ops.js +1 -1
  400. package/dist/daemon/api/handlers/broker-recovery.js +1 -1
  401. package/dist/daemon/api/handlers/broker-signals.d.ts +0 -3
  402. package/dist/daemon/api/handlers/broker-signals.js +2 -2
  403. package/dist/daemon/api/handlers/canvas.js +4 -4
  404. package/dist/daemon/api/handlers/charges.d.ts +1 -1
  405. package/dist/daemon/api/handlers/charges.js +1 -1
  406. package/dist/daemon/api/handlers/conversations.d.ts +2 -0
  407. package/dist/daemon/api/handlers/conversations.js +3 -0
  408. package/dist/daemon/api/handlers/crons.js +1 -1
  409. package/dist/daemon/api/handlers/daemon.js +1 -1
  410. package/dist/daemon/api/handlers/delivery.d.ts +1 -1
  411. package/dist/daemon/api/handlers/delivery.js +1 -1
  412. package/dist/daemon/api/handlers/docs.js +1 -1
  413. package/dist/daemon/api/handlers/exec.js +1 -1
  414. package/dist/daemon/api/handlers/execution-start.d.ts +2 -0
  415. package/dist/daemon/api/handlers/execution-start.js +2 -0
  416. package/dist/daemon/api/handlers/files.js +1 -1
  417. package/dist/daemon/api/handlers/git-credential.d.ts +11 -0
  418. package/dist/daemon/api/handlers/git-credential.js +1 -0
  419. package/dist/daemon/api/handlers/hook-exec.d.ts +1 -1
  420. package/dist/daemon/api/handlers/hook-exec.js +1 -1
  421. package/dist/daemon/api/handlers/human-requests.js +1 -1
  422. package/dist/daemon/api/handlers/inbox.js +1 -1
  423. package/dist/daemon/api/handlers/messages.js +1 -1
  424. package/dist/daemon/api/handlers/model-config.js +1 -1
  425. package/dist/daemon/api/handlers/modelauth.d.ts +1 -1
  426. package/dist/daemon/api/handlers/modelauth.js +1 -1
  427. package/dist/daemon/api/handlers/node-events.d.ts +6 -0
  428. package/dist/daemon/api/handlers/node-events.js +3 -3
  429. package/dist/daemon/api/handlers/node-outcomes.js +1 -1
  430. package/dist/daemon/api/handlers/node-records.js +3 -2
  431. package/dist/daemon/api/handlers/nodes.js +1 -1
  432. package/dist/daemon/api/handlers/objects.js +3 -3
  433. package/dist/daemon/api/handlers/profiles.js +1 -1
  434. package/dist/daemon/api/handlers/providers.js +1 -1
  435. package/dist/daemon/api/handlers/reports.js +9 -13
  436. package/dist/daemon/api/handlers/reviews.js +1 -1
  437. package/dist/daemon/api/handlers/runs.js +1 -1
  438. package/dist/daemon/api/handlers/settings.d.ts +2 -0
  439. package/dist/daemon/api/handlers/settings.js +1 -0
  440. package/dist/daemon/api/handlers/structured-requests.js +1 -1
  441. package/dist/daemon/api/handlers/vendor-status.d.ts +1 -1
  442. package/dist/daemon/api/handlers/vendor-status.js +1 -1
  443. package/dist/daemon/api/handlers/worktree.js +1 -1
  444. package/dist/daemon/api/map.d.ts +2 -2
  445. package/dist/daemon/api/map.js +2 -2
  446. package/dist/daemon/api/operations.js +1 -1
  447. package/dist/daemon/api/reader.d.ts +8 -1
  448. package/dist/daemon/api/reader.js +1 -1
  449. package/dist/daemon/api/router.d.ts +4 -2
  450. package/dist/daemon/api/router.js +1 -1
  451. package/dist/daemon/api/server.d.ts +15 -3
  452. package/dist/daemon/api/server.js +1 -1
  453. package/dist/daemon/api/vendor-signin.d.ts +2 -1
  454. package/dist/daemon/api/vendor-signin.js +19 -19
  455. package/dist/daemon/api/viewer-presence.d.ts +20 -6
  456. package/dist/daemon/api/viewer-presence.js +2 -1
  457. package/dist/daemon/boot-settings.d.ts +3 -0
  458. package/dist/daemon/boot-settings.js +1 -0
  459. package/dist/daemon/broker-signals/bus.d.ts +48 -11
  460. package/dist/daemon/broker-signals/bus.js +1 -1
  461. package/dist/daemon/cron/armed-context.js +1 -1
  462. package/dist/daemon/cron/lease-recovery.js +1 -1
  463. package/dist/daemon/cron/live-runs.d.ts +6 -0
  464. package/dist/daemon/cron/live-runs.js +1 -1
  465. package/dist/daemon/cron/run-socket.js +1 -1
  466. package/dist/daemon/cron-run.js +2 -2
  467. package/dist/daemon/crtrd-cli.js +1 -1
  468. package/dist/daemon/crtrd.d.ts +10 -0
  469. package/dist/daemon/crtrd.js +18 -17
  470. package/dist/daemon/delivery/run-delivery.d.ts +1 -1
  471. package/dist/daemon/delivery/run-delivery.js +1 -1
  472. package/dist/daemon/due-sources.d.ts +1 -1
  473. package/dist/daemon/due-sources.js +1 -1
  474. package/dist/daemon/due.js +7 -6
  475. package/dist/daemon/fleet.d.ts +6 -6
  476. package/dist/daemon/fleet.js +9 -9
  477. package/dist/daemon/park-activity.d.ts +2 -1
  478. package/dist/daemon/park-activity.js +1 -1
  479. package/dist/daemon/park-pending.d.ts +20 -13
  480. package/dist/daemon/park-pending.js +3 -1
  481. package/dist/daemon/profile-delete.js +1 -1
  482. package/dist/daemon/reconcilers/bash-deadline.d.ts +15 -10
  483. package/dist/daemon/reconcilers/bash-deadline.js +1 -3
  484. package/dist/daemon/reconcilers/broker-supervision.d.ts +13 -3
  485. package/dist/daemon/reconcilers/broker-supervision.js +4 -4
  486. package/dist/daemon/reconcilers/cron-lane.d.ts +0 -1
  487. package/dist/daemon/reconcilers/cron-lane.js +1 -1
  488. package/dist/daemon/reconcilers/fault-retry-backstop.d.ts +2 -9
  489. package/dist/daemon/reconcilers/fault-retry-backstop.js +1 -1
  490. package/dist/daemon/reconcilers/grant-effects.d.ts +8 -0
  491. package/dist/daemon/reconcilers/grant-effects.js +1 -0
  492. package/dist/daemon/reconcilers/grove-instance-sweep.d.ts +1 -3
  493. package/dist/daemon/reconcilers/grove-instance-sweep.js +1 -1
  494. package/dist/daemon/reconcilers/ledger-r2.d.ts +27 -0
  495. package/dist/daemon/reconcilers/ledger-r2.js +7 -0
  496. package/dist/daemon/reconcilers/ledger-sandbox.d.ts +55 -0
  497. package/dist/daemon/reconcilers/ledger-sandbox.js +9 -0
  498. package/dist/daemon/reconcilers/ledger-storage.d.ts +22 -0
  499. package/dist/daemon/reconcilers/ledger-storage.js +5 -0
  500. package/dist/daemon/reconcilers/live-obligation.d.ts +3 -2
  501. package/dist/daemon/reconcilers/live-obligation.js +1 -1
  502. package/dist/daemon/reconcilers/managed-worktree-sweep.d.ts +0 -5
  503. package/dist/daemon/reconcilers/managed-worktree-sweep.js +1 -1
  504. package/dist/daemon/reconcilers/node-deadline.d.ts +2 -1
  505. package/dist/daemon/reconcilers/node-deadline.js +1 -1
  506. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +1 -2
  507. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +1 -1
  508. package/dist/daemon/reconcilers/node-lifecycle/remote-wake.d.ts +31 -0
  509. package/dist/daemon/reconcilers/node-lifecycle/remote-wake.js +1 -0
  510. package/dist/daemon/reconcilers/node-lifecycle/requested-stops.d.ts +5 -0
  511. package/dist/daemon/reconcilers/node-lifecycle/requested-stops.js +2 -0
  512. package/dist/daemon/reconcilers/node-lifecycle/tick.js +1 -1
  513. package/dist/daemon/reconcilers/pending-review-submit.js +1 -1
  514. package/dist/daemon/reconcilers/recipe-image.d.ts +49 -0
  515. package/dist/daemon/reconcilers/recipe-image.js +7 -0
  516. package/dist/daemon/reconcilers/row-claim.d.ts +19 -0
  517. package/dist/daemon/reconcilers/row-claim.js +2 -0
  518. package/dist/daemon/reconcilers/storage-due-work.d.ts +18 -3
  519. package/dist/daemon/reconcilers/storage-due-work.js +2 -1
  520. package/dist/daemon/reconcilers/storage-maintenance.d.ts +5 -13
  521. package/dist/daemon/reconcilers/storage-maintenance.js +1 -1
  522. package/dist/daemon/reconcilers/structured-request-lane.js +1 -1
  523. package/dist/daemon/reconcilers/throttled-lane.d.ts +11 -0
  524. package/dist/daemon/reconcilers/throttled-lane.js +1 -0
  525. package/dist/daemon/review/finish.js +1 -1
  526. package/dist/daemon/run-notices.d.ts +3 -0
  527. package/dist/daemon/run-notices.js +1 -1
  528. package/dist/daemon/runs/append-section.d.ts +4 -1
  529. package/dist/daemon/runs/append-section.js +2 -2
  530. package/dist/daemon/runs/observer.d.ts +3 -2
  531. package/dist/daemon/runs/observer.js +3 -2
  532. package/dist/daemon/runs/view-transport.d.ts +19 -0
  533. package/dist/daemon/runs/view-transport.js +1 -0
  534. package/dist/daemon/structured-requests/boot.d.ts +2 -1
  535. package/dist/daemon/structured-requests/boot.js +1 -1
  536. package/dist/daemon/structured-requests/effects.d.ts +4 -3
  537. package/dist/daemon/structured-requests/effects.js +1 -1
  538. package/dist/daemon/structured-requests/notifier.d.ts +2 -0
  539. package/dist/daemon/structured-requests/notifier.js +1 -1
  540. package/dist/daemon/structured-requests/sidecar.d.ts +4 -3
  541. package/dist/daemon/structured-requests/sidecar.js +1 -1
  542. package/dist/launcher/validate.js +1 -1
  543. package/dist/migrations/004-canvas-documents/index.js +2 -2
  544. package/dist/pi-extensions/broker-local.d.ts +3 -3
  545. package/dist/pi-extensions/broker-local.js +1 -2
  546. package/dist/pi-extensions/canvas-bash-valve.js +6 -6
  547. package/dist/pi-extensions/canvas-context-intro.js +2 -2
  548. package/dist/pi-extensions/canvas-elapsed.js +2 -2
  549. package/dist/pi-extensions/canvas-goal-capture.js +1 -1
  550. package/dist/pi-extensions/canvas-inbox-watcher.js +2 -2
  551. package/dist/pi-extensions/canvas-review-boundary.d.ts +11 -1
  552. package/dist/pi-extensions/canvas-review-boundary.js +3 -3
  553. package/dist/pi-extensions/canvas-stophook.js +1 -1
  554. package/dist/pi-extensions/canvas-structured-output.d.ts +5 -3
  555. package/dist/pi-extensions/canvas-structured-output.js +6 -11
  556. package/dist/pi-extensions/canvas-tool-guide.d.ts +9 -2
  557. package/dist/pi-extensions/canvas-tool-guide.js +6 -6
  558. package/dist/shared/inbox-entry-body.js +7 -7
  559. package/dist/types.d.ts +12 -0
  560. package/docs/sdk/release-notes.md +4 -0
  561. package/package.json +7 -4
  562. package/packages/crouter-identity/package.json +1 -1
  563. package/runtime.lock.json +13 -13
  564. package/scripts/git-credential-crtr +33 -0
  565. package/scripts/sandbox-bootstrap.sh +54 -0
  566. package/scripts/sandbox-start.mjs +320 -0
  567. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +0 -78
  568. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +0 -1179
  569. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +0 -1406
  570. package/dist/core/conversation-store/file-store.d.ts +0 -37
  571. package/dist/core/conversation-store/file-store.js +0 -9
  572. package/dist/core/conversation-store/listener.d.ts +0 -4
  573. package/dist/core/conversation-store/listener.js +0 -8
  574. package/dist/core/scoped-state/profiles.d.ts +0 -27
  575. package/dist/core/scoped-state/profiles.js +0 -1
  576. package/dist/core/scoped-state/providers.d.ts +0 -24
  577. package/dist/core/scoped-state/providers.js +0 -1
  578. package/dist/core/scoped-state/settings.d.ts +0 -28
  579. package/dist/core/scoped-state/settings.js +0 -1
@@ -1 +1 @@
1
- var X=Object.defineProperty;var t=(e,n)=>X(e,"name",{value:n,configurable:!0});import{randomUUID as nn}from"node:crypto";import{relative as en,resolve as z,sep as G}from"node:path";import{ApiError as c}from"../../../api/errors.js";import{armCron as rn,parseCronSink as on,getCron as v,listCrons as P,listCronRuns as F,pokeHeldCrons as tn,setCronState as L}from"../../../core/canvas/crons.js";import{getNode as _}from"../../../core/canvas/canvas.js";import{appSpace as an,cronFolder as sn}from"../../../core/layout.js";import{appProcessesSandboxed as cn,cwdIsMounted as ln}from"../../../core/spaces/process.js";import{activeGrant as dn,activeGrantForApp as un,grantFor as pn,terminalGrant as fn}from"../../../core/grants/mirror.js";import{assertScope as Z}from"../../../core/scopes.js";import{InputError as s}from"../../../core/io.js";import{nextSlotAfter as wn}from"../../../core/wake.js";import{executeCron as _n,markSelfCanceledCronRun as hn}from"../../cron-run.js";import{toCronDTO as k,toCronRunDTO as H}from"../map.js";import{appOf as T,assertCeiling as mn,canReadNode as gn}from"../router.js";import{checkCronBirth as yn,checkCronFire as bn,cronRefusalError as vn}from"../../cron/authority.js";import{endCron as kn}from"../../cron/ended.js";import{requireUsableProfile as Sn}from"./profiles.js";import{filterAsync as $n,mapAsync as On}from"../../../core/async-array.js";import{personId as Cn}from"../../../core/canvas/person.js";function E(e,n){const r=e[n];if(typeof r!="string"||r.trim()==="")throw new s({error:"missing_field",message:`cron arm request requires a non-empty "${n}"`,field:n,next:`Send a non-empty string "${n}".`});return r}t(E,"requireString");function O(e,n,r,o){if(e==null)return o;if(typeof e=="string"&&r.includes(e))return e;throw new s({error:`bad_${n}`,message:`"${String(e)}" is not a valid ${n}`,field:n,next:`Use one of: ${r.join(", ")}.`})}t(O,"requireEnum");async function In(e){if(e==="human")return JSON.stringify({human:!0});if(e.startsWith("node:")){const n=e.slice(5).trim();if(n==="")throw new s({error:"bad_sink",message:"node sink requires a node id",field:"sink",next:'Use sink "node:<node-id>".'});if(await _(n)===null)throw new s({error:"sink_node_not_found",message:`sink node ${n} does not exist on the canvas`,field:"sink",next:"Name a live node id, or use spawn:<kind> for a delivery that outlives any one node."});return JSON.stringify({node:n})}if(e.startsWith("spawn:")){const n=e.slice(6).trim();if(n==="")throw new s({error:"bad_sink",message:"spawn sink requires a node kind",field:"sink",next:'Use sink "spawn:<kind>" (e.g. spawn:general).'});return JSON.stringify({spawn:n})}throw new s({error:"bad_sink",message:`"${e}" is not a valid sink`,field:"sink",next:"Use node:<node-id>, spawn:<kind>, or human."})}t(In,"parseSinkSpec");const qn=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;function M(e,n){if(!qn.test(e)||Number.isNaN(Date.parse(e)))throw new s({error:`bad_${n}`,message:`"${e}" is not a UTC ISO instant`,field:n,next:'Send a resolved UTC instant like "2026-06-07T09:00:00.000Z" (the client resolves the time grammar).'});return e}t(M,"requireUtcIso");function Nn(e){try{wn(e,new Date)}catch(n){throw new s({error:"bad_recur",message:`recur is not advanceable: ${n instanceof Error?n.message:String(n)}`,field:"recur",next:'Send recur JSON the daemon can advance: {"every":"6h"} or {"cron":"0 9 * * *","tz":"America/New_York"}.'})}return e}t(Nn,"requireRecur");function jn(e){let n;try{n=JSON.parse(e)}catch{throw new s({error:"bad_env_json",message:"env_json is not valid JSON",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'})}if(typeof n!="object"||n===null||Array.isArray(n))throw new s({error:"bad_env_json",message:"env_json must be a JSON object",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'});for(const[r,o]of Object.entries(n))if(typeof o!="string")throw new s({error:"bad_env_json",message:`env_json value for "${r}" is not a string`,field:"env_json",next:"Every env value must be a string."});return e}t(jn,"requireEnvJson");function U(e){const n=e.query.get("profile");return n!==null&&n.trim()!==""?n.trim():null}t(U,"callerProfile");function Y(e,n){return n===null||e.scope==="global"||e.profile===n}t(Y,"inScope");function K(e){return e.principal.class==="person"?null:T(e.principal)}t(K,"callerApp");async function J(e,n){return await gn(e.principal,n.grantee,n.created_by??void 0)}t(J,"visibleTo");function An(e){return typeof e.anchor_node=="string"&&e.anchor_node!==""?e.anchor_node:null}t(An,"anchorNodeOf");async function Pn(e,n,r,o,i,d){const u=typeof e.cwd=="string"&&e.cwd.trim()!==""?z(e.cwd):null;if(!cn()){if(u===null)throw new s({error:"missing_field",message:'cron arm request requires a non-empty "cwd"',field:"cwd",next:'Send a non-empty string "cwd".'});return u}const h=o!==null&&await _(o)!==null?o:void 0,l=t(async q=>await ln(n,q,h,i,void 0,d),"reaches");if(u!==null&&await l(u))return u;const a=an(n).files;if(u!==null&&e.cwd_is_default!==!0)throw new c(400,"invalid_request",`cwd ${u} is not inside a directory this app's cron runs can see, so every run would fail to start; use a directory under ${a}`,{cwd:u,default:a},void 0,{param:"cwd"});const p=r===null?null:(await _(r))?.cwd??null;return p!==null&&await l(p)?p:a}t(Pn,"resolveArmedCwd");async function Tn(e,n,r){const o=e.principal;if(o.class==="person"){const a=await fn();return{sub:a?.sub??Cn(),grant_id:a?.id??null,scopes:null,origin_run_id:null}}if(o.class==="app"){const a=await un(n);if(a===null)throw new c(401,"grant_revoked",`no active grant for ${n}`);const p=e.ceiling??o.scopes;return{sub:a.sub,grant_id:a.id,scopes:p===null?null:[...p],origin_run_id:null}}if(o.class==="cron"){const a=await bn({grantee:o.app,sub:o.sub,grant_id:o.grant,scopes:o.scopes});if(!a.allowed)throw new c(a.reason==="scope_missing"?403:401,a.reason==="scope_missing"?"scope_missing":"grant_revoked",a.message);return{sub:o.sub,grant_id:o.grant,scopes:o.scopes===null?null:[...o.scopes],origin_run_id:o.origin_run,armed_from_exec:o.from_exec}}if(o.class!=="broker")throw new c(401,"unauthorized","this caller cannot arm a cron");const i=r??await _(o.node),d=i?.grant_id??o.grant,u=i?.sub??o.sub,h=d!=null?await dn(d):await pn(n,u);if(h===null)throw new c(401,"grant_revoked","the node grant is not active");const l=i?.scopes??o.scopes;return{sub:h.sub,grant_id:h.id,scopes:l===null?null:[...l],origin_run_id:i?.run_id??o.run}}t(Tn,"armPrincipal");async function En(e){const n=e.body??{},r=E(n,"name"),o=E(n,"command"),i=M(E(n,"fire_at"),"fire_at"),d=typeof n.recur=="string"&&n.recur!==""?Nn(n.recur):null,u=typeof n.expires_at=="string"&&n.expires_at!==""?M(n.expires_at,"expires_at"):null,h=typeof n.env_json=="string"&&n.env_json!==""?jn(n.env_json):null,l=typeof n.sink=="string"&&n.sink.trim()!==""?n.sink.trim():null,a=O(n.on_output,"on_output",["silent","on-failure","always","on-change"],l?.startsWith("node:")?"on-change":"on-failure"),p=O(n.overlap,"overlap",["skip","queue","replace"],"skip"),q=O(n.tier,"tier",["normal","urgent","critical"],"normal"),x=a==="always"||a==="on-change";if(x&&l===null)throw new s({error:"sink_required",message:`on_output "${a}" delivers stdout every qualifying run \u2014 it needs a sink`,field:"sink",next:"Pass sink node:<node-id> (a living node's inbox), spawn:<kind> (birth a fresh node per delivery), or human (the humanloop inbox)."});if(!x&&l!==null)throw new s({error:"sink_unused",message:`on_output "${a}" never delivers stdout \u2014 a sink would be dead config`,field:"sink",next:'Drop the sink, or use on_output "always"/"on-change" if delivery is the point.'});const Q=l!==null?await In(l):"{}";if(l?.startsWith("spawn:")&&(mn(e.ceiling,"crtr:act"),e.principal.class==="cron")){const y=await yn({grantee:e.principal.app,sub:e.principal.sub,grant_id:e.principal.grant,scopes:e.principal.scopes,from_exec:e.principal.from_exec},{kind:l.slice(6)});if(!y.allowed)throw vn(y)}const g=T(e.principal)??"app:terminal",b=e.principal.class==="broker"?e.principal.node:e.principal.class==="cron"?null:typeof n.created_by=="string"&&n.created_by!==""?n.created_by:null;if(e.principal.class==="broker"&&n.created_by!=null&&n.created_by!==b)throw new c(403,"permission_error","a broker may only arm a cron as its own node");const m=b===null?null:await _(b);if(m!==null){if(m.grantee!==g)throw new s({error:"creator_app_mismatch",message:"creator node belongs to another app",field:"created_by",next:"Use a node of the calling app."});Z(m,"crtr:schedule"),l?.startsWith("spawn:")&&Z(m,"crtr:act")}if(n.isolated!==void 0&&typeof n.isolated!="boolean")throw new c(400,"invalid_request","isolated must be a boolean");if(m?.isolated&&n.isolated===!1)throw new c(403,"isolated_run","a cron created by an isolated node cannot escape isolation");const D=m?.isolated===!0||e.principal.class==="cron"&&e.principal.isolated;if(D&&n.isolated===!1)throw new c(403,"isolated_run","a cron created by an isolated caller cannot escape isolation");const N=D||n.isolated===!0,f=await Tn(e,g,m),j=nn(),S=N?sn(g,j):null;if(S!==null&&n.cwd_is_default!==!0){const y=en(S,typeof n.cwd=="string"&&n.cwd.trim()!==""?z(n.cwd):S);if(y===".."||y.startsWith(`..${G}`)||y.startsWith(G))throw new c(400,"invalid_request","an isolated cron must run in its own cron folder",{param:"cwd"})}const V=S??await Pn(n,g,b,An(n),N,{sub:f.sub,grantId:f.grant_id,scopes:f.scopes}),w=typeof n.anchor_node=="string"&&n.anchor_node!==""?n.anchor_node:null;if(w!==null&&await _(w)!==null&&(await _(w)).grantee!==g)throw new s({error:"anchor_app_mismatch",message:"anchor node belongs to another app",field:"anchor_node",next:"Use an anchor of the calling app."});if(w!==null&&await _(w)===null)throw new s({error:"anchor_not_found",message:`anchor node ${w} does not exist on the canvas`,field:"anchor_node",next:"Name a live node id, or drop the anchor \u2014 an unanchored cron outlives every node."});const W=n.cancel_on_wake===!0;if(W&&w===null)throw new s({error:"cancel_on_wake_needs_anchor",message:"cancel_on_wake couples the cron to a node's dormancy \u2014 it requires an anchor node",field:"anchor_node",next:"Pass anchor_node with the node whose wake should cancel this cron."});const $=n.run_timeout_s??300;if(typeof $!="number"||!Number.isInteger($)||$<=0)throw new s({error:"bad_run_timeout",message:`run_timeout_s must be a positive integer (got ${String(n.run_timeout_s)})`,field:"run_timeout_s",next:"Send the timeout in whole seconds, e.g. 300."});const A=typeof n.profile=="string"&&n.profile!==""?(await Sn(e,n.profile)).profileId:null,B=O(n.scope,"scope",["profile","global"],A!==null?"profile":"global");if(B==="profile"&&A===null)throw new s({error:"scope_needs_profile",message:'scope "profile" names the profile the cron lists and cancels within \u2014 it requires a profile',field:"profile",next:'Send a profile, or use scope "global" for a canvas-home-wide cron.'});return await rn({cron_id:j,name:r,created_by:b,grantee:g,sub:f.sub,grant_id:f.grant_id,scopes:f.scopes,origin_run_id:f.origin_run_id,armed_from_exec:f.armed_from_exec===!0,isolated:N,command:o,fire_at:i,recur:d,tz:typeof n.tz=="string"?n.tz:null,expires_at:u,anchor_node:w,cancel_on_wake:W,cwd:V,env_json:h,profile:A,scope:B,run_timeout_s:$,overlap:p,on_output:a,sink:Q,tier:q}),{status:201,body:k(await v(j),null)}}t(En,"handleArm");async function Un(e){const n=U(e),r=e.query.get("created_by");if(r==="")throw new c(400,"invalid_request","created_by must be nonempty when present");const o=await $n((n===null?await P({all:!0}):[...await P({profile:n}),...await P({global:!0})].sort((i,d)=>i.fire_at<d.fire_at?-1:i.fire_at>d.fire_at?1:0)).filter(i=>r===null||i.created_by===r),async i=>await J(e,i));return{status:200,body:await On(o,async i=>k(i,await R(i)))}}t(Un,"handleList");async function R(e){return(await F(e.cron_id))[0]??null}t(R,"lastRunOf");async function C(e){const n=e.params.cronId,r=await v(n);if(r===null||!await J(e,r))throw new c(404,"cron_not_found",`no cron ${n}`);if(!Y(r,U(e)))throw new c(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);return r}t(C,"requireCron");function I(e,n){const r=K(e);if(r!==null&&n.grantee!==r)throw new c(403,"other_app_run",`cron ${n.cron_id} belongs to another app`);return n}t(I,"requireOwnCron");async function Jn(e){const n=await C(e),r=await F(n.cron_id);return{status:200,body:{cron:k(n,r[0]??null),runs:r.map(H)}}}t(Jn,"handleShow");async function Rn(e){const n=I(e,await C(e));await L(n.cron_id,"paused");const r=await v(n.cron_id);return{status:200,body:k(r,await R(r))}}t(Rn,"handlePause");async function xn(e){const n=I(e,await C(e));await L(n.cron_id,"active");const r=await v(n.cron_id);return{status:200,body:k(r,await R(r))}}t(xn,"handleResume");async function Dn(e){const n=I(e,await C(e));if(n.run_state==="running")throw new c(409,"cron_running",`cron ${n.cron_id} has a run in flight \u2014 wait for it to settle`);const r=await _n(n,{outOfBand:!0});if(r===null)throw new c(409,"cron_running",`cron ${n.cron_id} was claimed by a concurrent run \u2014 check \`cron show\` for its result`);return{status:200,body:H(r)}}t(Dn,"handleRun");async function Wn(e){const n=new Date().toISOString();return{status:200,body:{unparked:await tn(n,K(e)),at:n}}}t(Wn,"handlePoke");function Bn(e,n){const r=e.principal;if(r.class==="cron")return r.kind==="exec"?{kind:"canceled",by:r.app}:r.cron===n.cron_id?null:{kind:"canceled",by:`cron ${r.cron}`};if(r.class==="broker"){const i=on(n.sink);return i?.kind==="node"&&i.node===r.node?null:{kind:"canceled",by:`node ${r.node}`}}const o=T(r);return{kind:"canceled",by:o===null||o==="app:terminal"?"the person":o}}t(Bn,"cancelNotice");async function zn(e){const n=e.params.cronId,r=await v(n);if(r!==null&&!await J(e,r))throw new c(404,"cron_not_found",`no cron ${n}`);if(r!==null&&!Y(r,U(e)))throw new c(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);r!==null&&I(e,r);const o=e.query.get("run_id"),i=e.principal.class==="cron"?e.principal.kind==="cron"&&o===e.principal.execution&&n===e.principal.cron:!0,d=o!==null&&i&&hn(n,o);return r!==null&&await kn(r,d?null:Bn(e,r),{preserveRunningProcess:d}),{status:204}}t(zn,"handleCancel");const le=[{method:"POST",pattern:"/v1/crons",scope:"crtr:schedule",handler:En},{method:"GET",pattern:"/v1/crons",handler:Un},{method:"GET",pattern:"/v1/crons/:cronId",handler:Jn},{method:"POST",pattern:"/v1/crons/:cronId/pause",scope:"crtr:schedule",handler:Rn},{method:"POST",pattern:"/v1/crons/:cronId/resume",scope:"crtr:schedule",handler:xn},{method:"POST",pattern:"/v1/crons/poke",scope:"crtr:schedule",handler:Wn},{method:"POST",pattern:"/v1/crons/:cronId/run",scope:"crtr:schedule",handler:Dn},{method:"DELETE",pattern:"/v1/crons/:cronId",scope:"crtr:schedule",handler:zn}];export{le as cronRoutes};
1
+ var X=Object.defineProperty;var t=(e,n)=>X(e,"name",{value:n,configurable:!0});import{randomUUID as nn}from"node:crypto";import{relative as en,resolve as G,sep as F}from"node:path";import{ApiError as s}from"../../../api/errors.js";import{armCron as rn,parseCronSink as on,getCron as v,listCrons as P,listCronRuns as L,pokeHeldCrons as tn,setCronState as Z}from"../../../core/canvas/crons.js";import{getNode as _}from"../../../core/canvas/canvas.js";import{appSpace as an,cronFolder as sn}from"../../../core/layout.js";import{appProcessesSandboxed as cn,cwdIsMounted as ln}from"../../../core/spaces/process.js";import{activeGrant as dn,activeGrantForApp as un,grantFor as pn,terminalGrant as fn}from"../../../core/grants/mirror.js";import{assertScope as H}from"../../../core/scopes.js";import{InputError as c}from"../../../core/io.js";import{nextSlotAfter as wn}from"../../../core/wake.js";import{executeCron as _n,markSelfCanceledCronRun as hn}from"../../cron-run.js";import{toCronDTO as k,toCronRunDTO as M}from"../map.js";import{appOf as T,assertCeiling as mn,canReadNode as gn}from"../router.js";import{checkCronBirth as yn,checkCronFire as bn,cronRefusalError as vn}from"../../cron/authority.js";import{endCron as kn}from"../../cron/ended.js";import{requireUsableProfile as Sn}from"./profiles.js";import{filterAsync as $n,mapAsync as On}from"../../../core/async-array.js";import{personId as Cn}from"../../../core/canvas/person.js";function E(e,n){const r=e[n];if(typeof r!="string"||r.trim()==="")throw new c({error:"missing_field",message:`cron arm request requires a non-empty "${n}"`,field:n,next:`Send a non-empty string "${n}".`});return r}t(E,"requireString");function O(e,n,r,o){if(e==null)return o;if(typeof e=="string"&&r.includes(e))return e;throw new c({error:`bad_${n}`,message:`"${String(e)}" is not a valid ${n}`,field:n,next:`Use one of: ${r.join(", ")}.`})}t(O,"requireEnum");async function In(e){if(e==="human")return JSON.stringify({human:!0});if(e.startsWith("node:")){const n=e.slice(5).trim();if(n==="")throw new c({error:"bad_sink",message:"node sink requires a node id",field:"sink",next:'Use sink "node:<node-id>".'});if(await _(n)===null)throw new c({error:"sink_node_not_found",message:`sink node ${n} does not exist on the canvas`,field:"sink",next:"Name a live node id, or use spawn:<kind> for a delivery that outlives any one node."});return JSON.stringify({node:n})}if(e.startsWith("spawn:")){const n=e.slice(6).trim();if(n==="")throw new c({error:"bad_sink",message:"spawn sink requires a node kind",field:"sink",next:'Use sink "spawn:<kind>" (e.g. spawn:general).'});return JSON.stringify({spawn:n})}throw new c({error:"bad_sink",message:`"${e}" is not a valid sink`,field:"sink",next:"Use node:<node-id>, spawn:<kind>, or human."})}t(In,"parseSinkSpec");const qn=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;function Y(e,n){if(!qn.test(e)||Number.isNaN(Date.parse(e)))throw new c({error:`bad_${n}`,message:`"${e}" is not a UTC ISO instant`,field:n,next:'Send a resolved UTC instant like "2026-06-07T09:00:00.000Z" (the client resolves the time grammar).'});return e}t(Y,"requireUtcIso");function Nn(e){try{wn(e,new Date)}catch(n){throw new c({error:"bad_recur",message:`recur is not advanceable: ${n instanceof Error?n.message:String(n)}`,field:"recur",next:'Send recur JSON the daemon can advance: {"every":"6h"} or {"cron":"0 9 * * *","tz":"America/New_York"}.'})}return e}t(Nn,"requireRecur");function jn(e){let n;try{n=JSON.parse(e)}catch{throw new c({error:"bad_env_json",message:"env_json is not valid JSON",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'})}if(typeof n!="object"||n===null||Array.isArray(n))throw new c({error:"bad_env_json",message:"env_json must be a JSON object",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'});for(const[r,o]of Object.entries(n))if(typeof o!="string")throw new c({error:"bad_env_json",message:`env_json value for "${r}" is not a string`,field:"env_json",next:"Every env value must be a string."});return e}t(jn,"requireEnvJson");function U(e){const n=e.query.get("profile");return n!==null&&n.trim()!==""?n.trim():null}t(U,"callerProfile");function K(e,n){return n===null||e.scope==="global"||e.profile===n}t(K,"inScope");function J(e){if(e.principal.class==="person"&&e.principal.sub!==void 0)throw new s(403,"permission_error","schedules are managed by the install's person, a node or an app");return e.principal.class==="person"?null:T(e.principal)}t(J,"callerApp");async function R(e,n){return await gn(e.principal,n.grantee,n.created_by??void 0)}t(R,"visibleTo");function An(e){return typeof e.anchor_node=="string"&&e.anchor_node!==""?e.anchor_node:null}t(An,"anchorNodeOf");async function Pn(e,n,r,o,i,d){const u=typeof e.cwd=="string"&&e.cwd.trim()!==""?G(e.cwd):null;if(!cn()){if(u===null)throw new c({error:"missing_field",message:'cron arm request requires a non-empty "cwd"',field:"cwd",next:'Send a non-empty string "cwd".'});return u}const h=o!==null&&await _(o)!==null?o:void 0,l=t(async q=>await ln(n,q,h,i,void 0,d),"reaches");if(u!==null&&await l(u))return u;const a=an(n).files;if(u!==null&&e.cwd_is_default!==!0)throw new s(400,"invalid_request",`cwd ${u} is not inside a directory this app's cron runs can see, so every run would fail to start; use a directory under ${a}`,{cwd:u,default:a},void 0,{param:"cwd"});const p=r===null?null:(await _(r))?.cwd??null;return p!==null&&await l(p)?p:a}t(Pn,"resolveArmedCwd");async function Tn(e,n,r){const o=e.principal;if(o.class==="person"){if(o.sub!==void 0)throw new s(403,"permission_error","schedules are managed by the install's person, a node or an app");const a=await fn();return{sub:a?.sub??Cn(),grant_id:a?.id??null,scopes:null,origin_run_id:null}}if(o.class==="app"){const a=await un(n);if(a===null)throw new s(401,"grant_revoked",`no active grant for ${n}`);const p=e.ceiling??o.scopes;return{sub:a.sub,grant_id:a.id,scopes:p===null?null:[...p],origin_run_id:null}}if(o.class==="cron"){const a=await bn({grantee:o.app,sub:o.sub,grant_id:o.grant,scopes:o.scopes});if(!a.allowed)throw new s(a.reason==="scope_missing"?403:401,a.reason==="scope_missing"?"scope_missing":"grant_revoked",a.message);return{sub:o.sub,grant_id:o.grant,scopes:o.scopes===null?null:[...o.scopes],origin_run_id:o.origin_run,armed_from_exec:o.from_exec}}if(o.class!=="broker")throw new s(401,"unauthorized","this caller cannot arm a cron");const i=r??await _(o.node),d=i?.grant_id??o.grant,u=i?.sub??o.sub,h=d!=null?await dn(d):await pn(n,u);if(h===null)throw new s(401,"grant_revoked","the node grant is not active");const l=i?.scopes??o.scopes;return{sub:h.sub,grant_id:h.id,scopes:l===null?null:[...l],origin_run_id:i?.run_id??o.run}}t(Tn,"armPrincipal");async function En(e){const n=e.body??{},r=E(n,"name"),o=E(n,"command"),i=Y(E(n,"fire_at"),"fire_at"),d=typeof n.recur=="string"&&n.recur!==""?Nn(n.recur):null,u=typeof n.expires_at=="string"&&n.expires_at!==""?Y(n.expires_at,"expires_at"):null,h=typeof n.env_json=="string"&&n.env_json!==""?jn(n.env_json):null,l=typeof n.sink=="string"&&n.sink.trim()!==""?n.sink.trim():null,a=O(n.on_output,"on_output",["silent","on-failure","always","on-change"],l?.startsWith("node:")?"on-change":"on-failure"),p=O(n.overlap,"overlap",["skip","queue","replace"],"skip"),q=O(n.tier,"tier",["normal","urgent","critical"],"normal"),W=a==="always"||a==="on-change";if(W&&l===null)throw new c({error:"sink_required",message:`on_output "${a}" delivers stdout every qualifying run \u2014 it needs a sink`,field:"sink",next:"Pass sink node:<node-id> (a living node's inbox), spawn:<kind> (birth a fresh node per delivery), or human (the humanloop inbox)."});if(!W&&l!==null)throw new c({error:"sink_unused",message:`on_output "${a}" never delivers stdout \u2014 a sink would be dead config`,field:"sink",next:'Drop the sink, or use on_output "always"/"on-change" if delivery is the point.'});const Q=l!==null?await In(l):"{}";if(l?.startsWith("spawn:")&&(mn(e.ceiling,"crtr:act"),e.principal.class==="cron")){const y=await yn({grantee:e.principal.app,sub:e.principal.sub,grant_id:e.principal.grant,scopes:e.principal.scopes,from_exec:e.principal.from_exec},{kind:l.slice(6)});if(!y.allowed)throw vn(y)}const g=T(e.principal)??"app:terminal",b=e.principal.class==="broker"?e.principal.node:e.principal.class==="cron"?null:typeof n.created_by=="string"&&n.created_by!==""?n.created_by:null;if(e.principal.class==="broker"&&n.created_by!=null&&n.created_by!==b)throw new s(403,"permission_error","a broker may only arm a cron as its own node");const m=b===null?null:await _(b);if(m!==null){if(m.grantee!==g)throw new c({error:"creator_app_mismatch",message:"creator node belongs to another app",field:"created_by",next:"Use a node of the calling app."});H(m,"crtr:schedule"),l?.startsWith("spawn:")&&H(m,"crtr:act")}if(n.isolated!==void 0&&typeof n.isolated!="boolean")throw new s(400,"invalid_request","isolated must be a boolean");if(m?.isolated&&n.isolated===!1)throw new s(403,"isolated_run","a cron created by an isolated node cannot escape isolation");const x=m?.isolated===!0||e.principal.class==="cron"&&e.principal.isolated;if(x&&n.isolated===!1)throw new s(403,"isolated_run","a cron created by an isolated caller cannot escape isolation");const N=x||n.isolated===!0,f=await Tn(e,g,m),j=nn(),S=N?sn(g,j):null;if(S!==null&&n.cwd_is_default!==!0){const y=en(S,typeof n.cwd=="string"&&n.cwd.trim()!==""?G(n.cwd):S);if(y===".."||y.startsWith(`..${F}`)||y.startsWith(F))throw new s(400,"invalid_request","an isolated cron must run in its own cron folder",{param:"cwd"})}const V=S??await Pn(n,g,b,An(n),N,{sub:f.sub,grantId:f.grant_id,scopes:f.scopes}),w=typeof n.anchor_node=="string"&&n.anchor_node!==""?n.anchor_node:null;if(w!==null&&await _(w)!==null&&(await _(w)).grantee!==g)throw new c({error:"anchor_app_mismatch",message:"anchor node belongs to another app",field:"anchor_node",next:"Use an anchor of the calling app."});if(w!==null&&await _(w)===null)throw new c({error:"anchor_not_found",message:`anchor node ${w} does not exist on the canvas`,field:"anchor_node",next:"Name a live node id, or drop the anchor \u2014 an unanchored cron outlives every node."});const B=n.cancel_on_wake===!0;if(B&&w===null)throw new c({error:"cancel_on_wake_needs_anchor",message:"cancel_on_wake couples the cron to a node's dormancy \u2014 it requires an anchor node",field:"anchor_node",next:"Pass anchor_node with the node whose wake should cancel this cron."});const $=n.run_timeout_s??300;if(typeof $!="number"||!Number.isInteger($)||$<=0)throw new c({error:"bad_run_timeout",message:`run_timeout_s must be a positive integer (got ${String(n.run_timeout_s)})`,field:"run_timeout_s",next:"Send the timeout in whole seconds, e.g. 300."});const A=typeof n.profile=="string"&&n.profile!==""?(await Sn(e,n.profile)).profileId:null,z=O(n.scope,"scope",["profile","global"],A!==null?"profile":"global");if(z==="profile"&&A===null)throw new c({error:"scope_needs_profile",message:'scope "profile" names the profile the cron lists and cancels within \u2014 it requires a profile',field:"profile",next:'Send a profile, or use scope "global" for a canvas-home-wide cron.'});return await rn({cron_id:j,name:r,created_by:b,grantee:g,sub:f.sub,grant_id:f.grant_id,scopes:f.scopes,origin_run_id:f.origin_run_id,armed_from_exec:f.armed_from_exec===!0,isolated:N,command:o,fire_at:i,recur:d,tz:typeof n.tz=="string"?n.tz:null,expires_at:u,anchor_node:w,cancel_on_wake:B,cwd:V,env_json:h,profile:A,scope:z,run_timeout_s:$,overlap:p,on_output:a,sink:Q,tier:q}),{status:201,body:k(await v(j),null)}}t(En,"handleArm");async function Un(e){J(e);const n=U(e),r=e.query.get("created_by");if(r==="")throw new s(400,"invalid_request","created_by must be nonempty when present");const o=await $n((n===null?await P({all:!0}):[...await P({profile:n}),...await P({global:!0})].sort((i,d)=>i.fire_at<d.fire_at?-1:i.fire_at>d.fire_at?1:0)).filter(i=>r===null||i.created_by===r),async i=>await R(e,i));return{status:200,body:await On(o,async i=>k(i,await D(i)))}}t(Un,"handleList");async function D(e){return(await L(e.cron_id))[0]??null}t(D,"lastRunOf");async function C(e){const n=e.params.cronId,r=await v(n);if(r===null||!await R(e,r))throw new s(404,"cron_not_found",`no cron ${n}`);if(!K(r,U(e)))throw new s(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);return r}t(C,"requireCron");function I(e,n){const r=J(e);if(r!==null&&n.grantee!==r)throw new s(403,"other_app_run",`cron ${n.cron_id} belongs to another app`);return n}t(I,"requireOwnCron");async function Jn(e){const n=await C(e),r=await L(n.cron_id);return{status:200,body:{cron:k(n,r[0]??null),runs:r.map(M)}}}t(Jn,"handleShow");async function Rn(e){const n=I(e,await C(e));await Z(n.cron_id,"paused");const r=await v(n.cron_id);return{status:200,body:k(r,await D(r))}}t(Rn,"handlePause");async function Dn(e){const n=I(e,await C(e));await Z(n.cron_id,"active");const r=await v(n.cron_id);return{status:200,body:k(r,await D(r))}}t(Dn,"handleResume");async function Wn(e){const n=I(e,await C(e));if(n.run_state==="running")throw new s(409,"cron_running",`cron ${n.cron_id} has a run in flight \u2014 wait for it to settle`);const r=await _n(n,{outOfBand:!0});if(r===null)throw new s(409,"cron_running",`cron ${n.cron_id} was claimed by a concurrent run \u2014 check \`cron show\` for its result`);return{status:200,body:M(r)}}t(Wn,"handleRun");async function xn(e){const n=new Date().toISOString();return{status:200,body:{unparked:await tn(n,J(e)),at:n}}}t(xn,"handlePoke");function Bn(e,n){const r=e.principal;if(r.class==="cron")return r.kind==="exec"?{kind:"canceled",by:r.app}:r.cron===n.cron_id?null:{kind:"canceled",by:`cron ${r.cron}`};if(r.class==="broker"){const i=on(n.sink);return i?.kind==="node"&&i.node===r.node?null:{kind:"canceled",by:`node ${r.node}`}}const o=T(r);return{kind:"canceled",by:o===null||o==="app:terminal"?"the person":o}}t(Bn,"cancelNotice");async function zn(e){const n=e.params.cronId,r=await v(n);if(r!==null&&!await R(e,r))throw new s(404,"cron_not_found",`no cron ${n}`);if(r!==null&&!K(r,U(e)))throw new s(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);r!==null&&I(e,r);const o=e.query.get("run_id"),i=e.principal.class==="cron"?e.principal.kind==="cron"&&o===e.principal.execution&&n===e.principal.cron:!0,d=o!==null&&i&&hn(n,o);return r!==null&&await kn(r,d?null:Bn(e,r),{preserveRunningProcess:d}),{status:204}}t(zn,"handleCancel");const le=[{method:"POST",pattern:"/v1/crons",scope:"crtr:schedule",handler:En},{method:"GET",pattern:"/v1/crons",handler:Un},{method:"GET",pattern:"/v1/crons/:cronId",handler:Jn},{method:"POST",pattern:"/v1/crons/:cronId/pause",scope:"crtr:schedule",handler:Rn},{method:"POST",pattern:"/v1/crons/:cronId/resume",scope:"crtr:schedule",handler:Dn},{method:"POST",pattern:"/v1/crons/poke",scope:"crtr:schedule",handler:xn},{method:"POST",pattern:"/v1/crons/:cronId/run",scope:"crtr:schedule",handler:Wn},{method:"DELETE",pattern:"/v1/crons/:cronId",scope:"crtr:schedule",handler:zn}];export{le as cronRoutes};
@@ -1 +1 @@
1
- var i=Object.defineProperty;var r=(e,t)=>i(e,"name",{value:t,configurable:!0});import{toMigrateStateDTO as u}from"../map.js";import{migrateLooseKeys as m}from"../../../core/layout-migrate/loose-keys.js";import{boundDaemonControl as s}from"../../control.js";import{ownerOnly as c}from"../router.js";function l(){const e=s();if(e===null)throw{status:503,code:"daemon_control_unavailable",message:"this API server is not hosted by a live daemon and cannot hand over"};const{graceMs:t}=e.requestHandover();return{status:202,body:{accepted:!0,predecessor_pid:process.pid,predecessor_epoch:e.epoch,grace_ms:t}}}r(l,"handleRestart");async function y(e){if(e.body!==void 0)throw{status:400,code:"invalid_request",message:"admission request must not include a body"};const t=s();if(t===null)throw{status:503,code:"daemon_control_unavailable",message:"this API server is not hosted by a live daemon and cannot admit normal work"};return{status:200,body:await t.admitStartup()}}r(y,"handleAdmit");async function h(e){const t=s();if(t===null)throw{status:503,code:"daemon_control_unavailable",message:"this API server is not hosted by a live daemon and cannot run the canvas import"};const o=e.body;if(o!==void 0&&(typeof o!="object"||o===null||Array.isArray(o)))throw{status:400,code:"invalid_request",message:"migration request body must be an object"};const n=o??{};if(n.dry_run!==void 0&&typeof n.dry_run!="boolean")throw{status:400,code:"invalid_request",message:"dry_run must be a boolean"};const d=m(n.dry_run===!0),a=await t.repairMigrations({dryRun:n.dry_run===!0});return{status:200,body:{...u(a.result,{resumed:a.resumed,resumeError:a.resumeError}),loose_keys:d}}}r(h,"handleMigrate");const g=c([{method:"POST",pattern:"/v1/daemon/restart",handler:r(e=>l(),"handler")},{method:"POST",pattern:"/v1/daemon/migrate",handler:h},{method:"POST",pattern:"/v1/daemon/admit",handler:y}]);export{g as daemonRoutes};
1
+ var i=Object.defineProperty;var r=(e,t)=>i(e,"name",{value:t,configurable:!0});import{toMigrateStateDTO as u}from"../map.js";import{migrateLooseKeys as m}from"../../../core/layout-migrate/loose-keys.js";import{boundDaemonControl as s}from"../../control.js";import{ownerOnly as c}from"../router.js";function l(){const e=s();if(e===null)throw{status:503,code:"daemon_control_unavailable",message:"this API server is not hosted by a live daemon and cannot hand over"};const{graceMs:t}=e.requestHandover();return{status:202,body:{accepted:!0,predecessor_pid:process.pid,predecessor_epoch:e.epoch,grace_ms:t}}}r(l,"handleRestart");async function y(e){if(e.body!==void 0)throw{status:400,code:"invalid_request",message:"admission request must not include a body"};const t=s();if(t===null)throw{status:503,code:"daemon_control_unavailable",message:"this API server is not hosted by a live daemon and cannot admit normal work"};return{status:200,body:await t.admitStartup()}}r(y,"handleAdmit");async function h(e){const t=s();if(t===null)throw{status:503,code:"daemon_control_unavailable",message:"this API server is not hosted by a live daemon and cannot run the canvas import"};const o=e.body;if(o!==void 0&&(typeof o!="object"||o===null||Array.isArray(o)))throw{status:400,code:"invalid_request",message:"migration request body must be an object"};const n=o??{};if(n.dry_run!==void 0&&typeof n.dry_run!="boolean")throw{status:400,code:"invalid_request",message:"dry_run must be a boolean"};const d=await m(n.dry_run===!0),a=await t.repairMigrations({dryRun:n.dry_run===!0});return{status:200,body:{...u(a.result,{resumed:a.resumed,resumeError:a.resumeError}),loose_keys:d}}}r(h,"handleMigrate");const g=c([{method:"POST",pattern:"/v1/daemon/restart",handler:r(e=>l(),"handler")},{method:"POST",pattern:"/v1/daemon/migrate",handler:h},{method:"POST",pattern:"/v1/daemon/admit",handler:y}]);export{g as daemonRoutes};
@@ -1,2 +1,2 @@
1
- import type { RouteTable } from '../router.js';
1
+ import { type RouteTable } from '../router.js';
2
2
  export declare const deliveryRoutes: RouteTable;
@@ -1 +1 @@
1
- var _=Object.defineProperty;var c=(e,n)=>_(e,"name",{value:n,configurable:!0});import{ApiError as r}from"../../../api/errors.js";import{withCanvasWrite as f}from"../../../core/canvas/db.js";import{readerForNode as l}from"../../../core/graph/reader.js";import{noteNodeBooting as j}from"../../../core/graph/repo-sync/index.js";import{checkoutsForCwd as h,warmCheckoutIdentities as k}from"../../../core/graph/repo-sync/repos.js";import{getNode as N}from"../../../core/canvas/canvas.js";import{DRY_RUN_ONLY as q,deliver as A,hypotheticalFrom as C,hypotheticalReader as I,inspect as p}from"../../../core/substrate/delivery/deliver.js";import{requireCurrentExecution as w}from"./broker-recovery.js";const O=["boot","system-prompt","compact","persona","file-read","command","pre-command","slash-command","names",...q];function m(e){if(typeof e!="object"||e===null||Array.isArray(e))throw new r(400,"usage","delivery requires a JSON body");const n=e;if(!O.includes(n.activity))throw new r(400,"usage",`invalid delivery activity: ${String(n.activity)}`);if(n.subject!==void 0&&typeof n.subject!="string")throw new r(400,"usage","delivery subject must be a string");return n}c(m,"parse");function b(e){if(e.principal.class!=="person"&&e.principal.class!=="broker")throw new r(403,"permission_error","a delivery dry run is available to a person or a node only")}c(b,"requirePerson");function R(e){if(typeof e!="object"||e===null||Array.isArray(e))throw new r(400,"usage","hypothetical must be an object");const n=c((o,s)=>{if(o!==void 0){if(typeof o!="string"||o==="")throw new r(400,"usage",`hypothetical.${s} must be a non-empty string`);return o}},"str"),t=n(e.cwd,"cwd");if(t!==void 0&&!t.startsWith("/"))throw new r(400,"usage","hypothetical.cwd must be an absolute path");if(e.mode!==void 0&&e.mode!=="base"&&e.mode!=="orchestrator")throw new r(400,"usage","hypothetical.mode must be base or orchestrator");if(e.lifecycle!==void 0&&e.lifecycle!=="terminal"&&e.lifecycle!=="resident")throw new r(400,"usage","hypothetical.lifecycle must be terminal or resident");if(e.has_manager!==void 0&&typeof e.has_manager!="boolean")throw new r(400,"usage","hypothetical.has_manager must be a boolean");if(e.depth!==void 0&&(!Number.isInteger(e.depth)||e.depth<0))throw new r(400,"usage","hypothetical.depth must be a non-negative integer");const i={};t!==void 0&&(i.cwd=t),e.profile!==void 0&&(i.profile=e.profile===null?null:n(e.profile,"profile"));const d=n(e.grantee,"grantee");d!==void 0&&(i.grantee=d);const a=n(e.kind,"kind");return a!==void 0&&(i.kind=a),e.mode!==void 0&&(i.mode=e.mode),e.lifecycle!==void 0&&(i.lifecycle=e.lifecycle),e.has_manager!==void 0&&(i.hasManager=e.has_manager),e.depth!==void 0&&(i.depth=e.depth),i}c(R,"hypotheticalOf");async function y(e,n){const t=e===void 0?void 0:await N(e);for(const i of[t?.cwd,n])i&&k(i)}c(y,"warmDeliveryCwds");async function S(e){b(e);const n=m(e.body);if(n.node!==void 0&&typeof n.node!="string")throw new r(400,"usage","node must be a node id");const t={activity:n.activity,...n.subject!==void 0?{subject:n.subject}:{}};await y(n.node,n.hypothetical?.cwd);const{result:i,plan:d}=await f(async a=>{if(n.hypothetical===void 0){if(n.node===void 0)throw new r(400,"usage","a delivery dry run names a node, a hypothetical node, or both");return await p(a,await l(a,n.node,async v=>await h(a,v)),t,n.subject_override)}const o=R(n.hypothetical),s=n.node!==void 0?await C(n.node,o):{...o,cwd:o.cwd??""};if(s.cwd==="")throw new r(400,"usage","a hypothetical node needs a cwd");const{reader:u,subject:g}=await I(a,s);return await p(a,u,t,n.subject_override,g)},{rollback:!0});return{status:200,body:{...i,plan:d}}}c(S,"handleDryRun");async function D(e){const n=e.params.id,t=m(e.body),i={activity:t.activity,...t.subject!==void 0?{subject:t.subject}:{},...t.inherited===!0?{inherited:!0}:{}};if(t.dry_run===!0){b(e),await y(n);const{result:a,plan:o}=await f(async s=>await p(s,await l(s,n,async u=>await h(s,u)),i,t.subject_override),{rollback:!0});return{status:200,body:{...a,plan:o}}}if(e.principal.class==="broker"&&e.principal.node!==n)throw new r(403,"permission_error","delivery is performed by the node's own broker");return await w(n,t.expected_execution_id),t.activity==="boot"&&(await j(n),await w(n,t.expected_execution_id)),await y(n),{status:200,body:await f(async a=>await A(a,await l(a,n,async o=>await h(a,o)),i))}}c(D,"handleDelivery");const L=[{method:"POST",pattern:"/v1/nodes/:id/delivery",handler:D},{method:"POST",pattern:"/v1/delivery/dry-run",handler:S}];export{L as deliveryRoutes};
1
+ var _=Object.defineProperty;var c=(e,t)=>_(e,"name",{value:t,configurable:!0});import{ApiError as n}from"../../../api/errors.js";import{openDb as j,withCanvasWrite as f}from"../../../core/canvas/db.js";import{readerForNode as l,warmNodeRepos as k}from"../../../core/graph/reader.js";import{noteNodeBooting as A}from"../../../core/graph/repo-sync/index.js";import{checkoutsForCwd as h,warmCheckoutIdentities as N}from"../../../core/graph/repo-sync/repos.js";import{DRY_RUN_ONLY as q,deliver as C,hypotheticalFrom as R,hypotheticalReader as D,inspect as p}from"../../../core/substrate/delivery/deliver.js";import{actsAsPerson as I}from"../router.js";import{requireCurrentExecution as w}from"./broker-recovery.js";const O=["boot","system-prompt","compact","persona","file-read","command","pre-command","slash-command","names",...q];function m(e){if(typeof e!="object"||e===null||Array.isArray(e))throw new n(400,"usage","delivery requires a JSON body");const t=e;if(!O.includes(t.activity))throw new n(400,"usage",`invalid delivery activity: ${String(t.activity)}`);if(t.subject!==void 0&&typeof t.subject!="string")throw new n(400,"usage","delivery subject must be a string");return t}c(m,"parse");function b(e){if(!I(e.principal)&&e.principal.class!=="broker")throw new n(403,"permission_error","a delivery dry run is available to a person or a node only")}c(b,"requirePerson");function S(e){if(typeof e!="object"||e===null||Array.isArray(e))throw new n(400,"usage","hypothetical must be an object");const t=c((o,s)=>{if(o!==void 0){if(typeof o!="string"||o==="")throw new n(400,"usage",`hypothetical.${s} must be a non-empty string`);return o}},"str"),i=t(e.cwd,"cwd");if(i!==void 0&&!i.startsWith("/"))throw new n(400,"usage","hypothetical.cwd must be an absolute path");if(e.mode!==void 0&&e.mode!=="base"&&e.mode!=="orchestrator")throw new n(400,"usage","hypothetical.mode must be base or orchestrator");if(e.lifecycle!==void 0&&e.lifecycle!=="terminal"&&e.lifecycle!=="resident")throw new n(400,"usage","hypothetical.lifecycle must be terminal or resident");if(e.has_manager!==void 0&&typeof e.has_manager!="boolean")throw new n(400,"usage","hypothetical.has_manager must be a boolean");if(e.depth!==void 0&&(!Number.isInteger(e.depth)||e.depth<0))throw new n(400,"usage","hypothetical.depth must be a non-negative integer");const r={};i!==void 0&&(r.cwd=i),e.profile!==void 0&&(r.profile=e.profile===null?null:t(e.profile,"profile"));const u=t(e.grantee,"grantee");u!==void 0&&(r.grantee=u);const a=t(e.kind,"kind");return a!==void 0&&(r.kind=a),e.mode!==void 0&&(r.mode=e.mode),e.lifecycle!==void 0&&(r.lifecycle=e.lifecycle),e.has_manager!==void 0&&(r.hasManager=e.has_manager),e.depth!==void 0&&(r.depth=e.depth),r}c(S,"hypotheticalOf");async function y(e,t){e!==void 0&&await k(await j(),e),t&&await N(t)}c(y,"warmDeliveryCwds");async function P(e){b(e);const t=m(e.body);if(t.node!==void 0&&typeof t.node!="string")throw new n(400,"usage","node must be a node id");const i={activity:t.activity,...t.subject!==void 0?{subject:t.subject}:{}};await y(t.node,t.hypothetical?.cwd);const{result:r,plan:u}=await f(async a=>{if(t.hypothetical===void 0){if(t.node===void 0)throw new n(400,"usage","a delivery dry run names a node, a hypothetical node, or both");return await p(a,await l(a,t.node,async v=>await h(a,v)),i,t.subject_override)}const o=S(t.hypothetical),s=t.node!==void 0?await R(t.node,o):{...o,cwd:o.cwd??""};if(s.cwd==="")throw new n(400,"usage","a hypothetical node needs a cwd");const{reader:d,subject:g}=await D(a,s);return await p(a,d,i,t.subject_override,g)},{rollback:!0});return{status:200,body:{...r,plan:u}}}c(P,"handleDryRun");async function T(e){const t=e.params.id,i=m(e.body),r={activity:i.activity,...i.subject!==void 0?{subject:i.subject}:{},...i.inherited===!0?{inherited:!0}:{}};if(i.dry_run===!0){b(e),await y(t);const{result:a,plan:o}=await f(async s=>await p(s,await l(s,t,async d=>await h(s,d)),r,i.subject_override),{rollback:!0});return{status:200,body:{...a,plan:o}}}if(e.principal.class==="broker"&&e.principal.node!==t)throw new n(403,"permission_error","delivery is performed by the node's own broker");return await w(t,i.expected_execution_id),i.activity==="boot"&&(await A(t),await w(t,i.expected_execution_id)),await y(t),{status:200,body:await f(async a=>await C(a,await l(a,t,async o=>await h(a,o)),r))}}c(T,"handleDelivery");const U=[{method:"POST",pattern:"/v1/nodes/:id/delivery",handler:T},{method:"POST",pattern:"/v1/delivery/dry-run",handler:P}];export{U as deliveryRoutes};
@@ -1 +1 @@
1
- var oe=Object.defineProperty;var u=(n,e)=>oe(n,"name",{value:e,configurable:!0});import{ApiError as k}from"../../../api/errors.js";import{DOC_KINDS as P,DOC_UNSET_FIELDS as H}from"../../../api/dto/docs.js";import{PRIVACY_LEVELS_DTO as U}from"../../../api/dto/objects.js";import{withCanvasWrite as A}from"../../../core/canvas/db.js";import{CrtrError as re}from"../../../core/errors.js";import{emitEvent as ie}from"../../../core/events/emit.js";import{canChange as ae,spaceOf as se}from"../../../core/graph/access.js";import{get as $}from"../../../core/graph/bodies.js";import{deleteDocument as ce,documentRecord as I,editDocument as de,heads as x,history as ue,linksIn as V,moveDocument as fe,readBase as le,setDocumentRecord as me,withCurrentNames as pe,writeDocument as he}from"../../../core/graph/documents.js";import{resolveName as K}from"../../../core/graph/names.js";import{getById as L}from"../../../core/graph/objects.js";import{noteRepoDocumentsChanged as we}from"../../../core/graph/repo-sync/index.js";import{checkoutsForCwd as ye}from"../../../core/graph/repo-sync/repos.js";import{lintRules as ge,rulesOf as ve}from"../../../core/graph/rules.js";import{unifiedDiff as _e}from"../../../core/graph/diff.js";import{ownerForGrantee as be}from"../../../core/graph/objects.js";import{ownerFromFlag as W,readerFor as E,resolveRef as G}from"../reader.js";import{appCaller as X,asApiError as ke,requireOwnNamespace as Ae}from"./schemas.js";import{filterAsync as Ee,flatMapAsync as Oe}from"../../../core/async-array.js";function g(n,e,t,o,a={}){return new k(n,e,t,{...a,error:e,message:t,next:o})}u(g,"refuse");const b=u((n,e)=>new k(400,"invalid_request",n,e),"invalid");function R(n){const e=n.body;if(e===void 0)return{};if(typeof e!="object"||e===null||Array.isArray(e))throw b("the request body must be a JSON object");return e}u(R,"bodyOf");const C=u(async n=>async e=>await ye(n,e),"reposFnFor");function N(n,e){if(n instanceof re){const t=n.details;if(n.code==="read_first")throw g(409,"read_first",n.message,`Read it first with \`crtr canvas read ${e?.id??"<ref>"}\`, then rerun the edit.`,t??{});if(n.code==="conflict")throw new k(409,"name_taken",n.message,t);if(n.code==="forbidden")throw typeof t?.next=="string"?g(403,"forbidden",n.message,t.next,t):new k(403,"forbidden",n.message,t);if(n.code==="scope_missing")throw g(403,"scope_missing",n.message,`Launch the run with \`${String(t?.scope)}\` in its scopes list.`,t??{});if(t?.rule==="repo-public-only")throw g(422,"repo_documents_are_public",n.message,'Own it as `user` and add a rule with `if: {"repo": "<handle>"}` so it loads only in that repo.')}throw ke(n)}u(N,"mapGraphError");async function M(n,e,t){const o=await G(n,e,decodeURIComponent(t));if("remote"in o)throw g(404,"not_found",z(o.remote),"Check the name with `crtr canvas search`.");if("deleted"in o)throw new k(404,"not_found",`${o.deleted.name} is deleted`,{id:o.deleted.id,deleted:!0});if(o.type!=="document")throw b(`${t} is a ${o.type}, not a document`);return o}u(M,"refDocument");function z(n){return`no object named ${n} on this runtime; it may be on another runtime, or the name may be misspelled`}u(z,"remoteMessage");async function $e(n,e){const t=e.owner===null?null:await L(n,e.owner);return t?t.type==="node"?t.id:t.name:""}u($e,"ownerHandle");async function j(n,e){const t=await $e(n,e);return t?`${t}/${e.name}`:e.name}u(j,"canonicalName");async function je(n,e){const t=e.owner===null?null:await L(n,e.owner),o=t?{id:t.id,type:t.type,handle:t.type==="node"?t.id:t.name}:null;return{id:e.id,type:e.type,kind:e.kind,owner:o,privacy:e.privacy,name:e.name,preview:e.preview??"",created_at:e.created_at,deleted_at:e.deleted_at,...e.type==="document"?{schema_type:e.schema_type??null}:{}}}u(je,"objectDTO");async function De(n,e){const t=e.owner===null?null:await L(n,e.owner);return t?.type==="repo"?t:null}u(De,"repoOwner");async function S(n,e,t,o){const a=[...new Map(await Oe(t,async f=>{const r=await De(n,f);return r?[[r.id,r]]:[]})).values()],c=e.class==="node"?e.cwd:o,s=e.class==="node"?[...e.checkouts,...e.projectCheckouts].map(f=>f.root):[];return{repos:a,dirs:[...new Set([...c!==void 0?[c]:[],...s])]}}u(S,"repoChange");async function T(n){for(const e of n.repos)try{await we(e,n.dirs)}catch(t){ie({level:"warn",event:"repo_sync.failed",outcome:"failed",error:t,fields:{repo:e.name,step:"export",reason:t instanceof Error?t.message:String(t)}})}}u(T,"syncChanged");const Y=/^[A-Za-z0-9_-]+(\/[A-Za-z0-9_-]+)*$/;function Re(n,e){if(n===void 0)return;if(!Array.isArray(n)){e.push("`rules` must be a list");return}const t=n.map(a=>{if(typeof a!="object"||a===null||Array.isArray(a))return a;const{match_frontmatter:c,...s}=a;return"matchFrontmatter"in s?{...s,match_frontmatter:s.matchFrontmatter}:c===void 0?s:{...s,matchFrontmatter:c}}),o=ge(t);if(o){e.push(o.replaceAll("matchFrontmatter","match_frontmatter"));return}return t}u(Re,"rulesFromDTO");function Z(n,e){const t={},o=u(r=>{const d=n[r];if(d!==void 0){if(typeof d!="string"){e.push(`\`${r}\` must be a string`);return}return d}},"str"),a=o("preview");a!==void 0&&(a.trim()===""||/[\r\n]/.test(a)?e.push("`preview` must be one non-empty line"):t.preview=a);const c=o("summary");c!==void 0&&(t.summary=c);const s=o("why_it_exists");if(s!==void 0&&(t.whyItExists=s),n.privacy!==void 0&&(U.includes(n.privacy)?t.privacy=n.privacy:e.push(`\`privacy\` must be one of ${U.join(", ")}`)),n.unlisted!==void 0&&(typeof n.unlisted!="boolean"?e.push("`unlisted` must be a boolean"):t.unlisted=n.unlisted),n.lint_ignore!==void 0){const r=n.lint_ignore;!Array.isArray(r)||r.some(d=>typeof d!="string")?e.push("`lint_ignore` must be a list of strings"):t.lintIgnore=r}if(n.extensions!==void 0){const r=n.extensions;typeof r!="object"||r===null||Array.isArray(r)||Object.values(r).some(d=>typeof d!="object"||d===null||Array.isArray(d))?e.push("`extensions` must map each plugin to an object of fields"):t.extensions=r}const f=Re(n.rules,e);return f!==void 0&&(t.rules=f),t}u(Z,"fieldsFrom");function J(n){if(n.length>0)throw b(n.join("; "),{errors:n})}u(J,"failOn");async function Q(n,e,t,o){const a=await Ee(V(t),async c=>"remote"in await K(n,e,c));return{...o.length>0?{unresolved_links:o}:{},...a.length>0?{remote_links:a}:{}}}u(Q,"linkReport");async function Ce(n){const e=R(n),t=[],o=e.name;(typeof o!="string"||!Y.test(o))&&t.push("`name` must be segments of letters, digits, `_` or `-` joined by `/`"),typeof e.body!="string"&&t.push("`body` must be a string"),typeof e.preview!="string"&&t.push("`preview` is required");const a=e.kind??"knowledge";P.includes(a)||t.push(a==="report"?"reports are written only by `crtr push`":`\`kind\` must be one of ${P.join(", ")}`);const c=Z(e,t);e.cwd!==void 0&&typeof e.cwd!="string"&&t.push("`cwd` must be a string");const s=e.schema_type;s!==void 0&&typeof s!="string"&&t.push("`schema_type` must be a string");const f=e.relations;f!==void 0&&(!Array.isArray(f)||f.some(i=>typeof i!="object"||i===null||typeof i.relation!="string"||typeof i.subject!="string"))&&t.push("`relations` must be a list of {relation, subject} strings"),J(t);const r=e,{dto:d,change:w}=await A(async i=>{const l=await E(i,n);r.schema_type!==void 0?await Ae(i,X(n).app,r.schema_type):r.relations!==void 0&&X(n);const m=r.owner==="user"&&l.class==="app"&&l.sub!==void 0?await be(i,l.sub.startsWith("user:")?l.sub:`user:${l.sub}`):await W(i,l,r.owner,r.cwd);try{const{object:h,unresolved:_}=await he(i,l,{...c,owner:m,name:r.name,kind:a,preview:c.preview,privacy:c.privacy??"public",...r.schema_type!==void 0?{schemaType:r.schema_type}:{},...r.relations!==void 0?{relations:r.relations}:{}},r.body,await C(i));return{dto:{id:h.id,name:await j(i,h),...await Q(i,l,r.body,_)},change:await S(i,l,[h],r.cwd)}}catch(h){N(h)}});return await T(w),d}u(Ce,"write");function Ne(n,e){let t=n;e.forEach(({old:o,new:a},c)=>{const s=o===""?-1:t.indexOf(o);if(s<0)throw g(422,"replace_no_match",`replace[${c}]: old text not found in the revision you read`,"Copy the old text exactly from your last read and rerun.",{index:c});if(t.indexOf(o,s+1)>=0)throw g(422,"replace_ambiguous",`replace[${c}]: old text occurs more than once`,"Include more surrounding text so the old text matches once, and rerun.",{index:c});t=t.slice(0,s)+a+t.slice(s+o.length)})}u(Ne,"checkReplacements");async function Se(n){const e=R(n),t=[],o=e.body!==void 0,a=e.replace!==void 0;o&&a&&t.push("give `body` or `replace`, not both"),o&&typeof e.body!="string"&&t.push("`body` must be a string");const c=e.replace;a&&(!Array.isArray(c)||c.length===0||c.some(i=>typeof i!="object"||i===null||typeof i.old!="string"||typeof i.new!="string"))&&t.push("`replace` must be a non-empty list of {old, new} strings"),(typeof e.rationale!="string"||e.rationale.trim()==="")&&t.push("`rationale` is required"),e.verbatim!==void 0&&typeof e.verbatim!="boolean"&&t.push("`verbatim` must be a boolean"),e.base!==void 0&&(typeof e.base!="string"||e.base==="")&&t.push("`base` must be a non-empty heads key"),e.base!==void 0&&!o&&!a&&t.push("`base` applies only to a `body` or `replace` edit");const s=Z(e,t),f=e.unset??[];if(!Array.isArray(f)||f.some(i=>!H.includes(i)))t.push(`\`unset\` takes only ${H.join(", ")}`);else for(const i of f)i==="summary"&&(s.summary=null),i==="why_it_exists"&&(s.whyItExists=null),i==="unlisted"&&(s.unlisted=!1),i==="lint_ignore"&&(s.lintIgnore=null),i==="extensions"&&(s.extensions=null),i==="rules"&&(s.rules=[]);t.length===0&&!o&&!a&&Object.keys(s).length===0&&t.push("nothing to change: give `body`, `replace`, or a field"),J(t);const r=e,{dto:d,synced:w}=await A(async i=>{const l=await E(i,n),m=await M(i,l,n.params.ref),h=await j(i,m),_=await S(i,l,[m]);try{if(!o&&!a)return await me(i,l,m,s,r.rationale),{dto:{id:m.id,name:h,revision:(await I(i,m)).revision},synced:_};const v=r.base??await le(i,l,m);if(a&&v!==null){const q=await I(i,m),B=await x(i,m);q.source_path===null&&q.heads_key===v&&B.length===1&&Ne(await pe(i,m,$(B[0].body_hash)),r.replace)}const D=r.verbatim===!0,p=o?{body:r.body,verbatim:D}:{replace:r.replace,verbatim:D},y=await de(i,l,m,p,r.rationale,v,s,await C(i));if("stale"in y)throw g(409,"stale_revision",`${h} changed after your read; it is now at revision ${y.stale.revision}`,`Read it again with \`crtr canvas read ${h}\`, then redo your change on the current text.`,{stale:y.stale});const F=$((await x(i,m))[0].body_hash);return{dto:{id:m.id,name:h,revision:y.revision,...await Q(i,l,F,y.unresolved)},synced:_}}catch(v){N(v,m)}});return await T(w),d}u(Se,"edit");async function Te(n){const e=R(n);if(e.new_name===void 0&&e.owner===void 0)throw b("give a new name, an owner, or both");if(e.new_name!==void 0&&(typeof e.new_name!="string"||!Y.test(e.new_name)))throw b("`new_name` must be segments of letters, digits, `_` or `-` joined by `/`");const{dto:t,change:o}=await A(async a=>{const c=await E(a,n),s=await M(a,c,n.params.ref),f=e.owner===void 0?void 0:await W(a,c,e.owner,e.cwd);try{const r=await fe(a,c,s,{...e.new_name!==void 0?{name:e.new_name}:{},...f?{owner:f}:{}},await C(a));return{dto:{id:r.id,name:await j(a,r)},change:await S(a,c,[s,r],e.cwd)}}catch(r){N(r,s)}});return await T(o),t}u(Te,"move");async function Fe(n){const{dto:e,change:t}=await A(async o=>{const a=await E(o,n),c=await M(o,a,n.params.ref),s=await j(o,c),f=await S(o,a,[c]);try{await ce(o,a,c,await C(o))}catch(r){N(r,c)}return{dto:{id:c.id,name:s},change:f}});return await T(t),e}u(Fe,"remove");async function Ie(n){const e=n.query.get("diff"),t=e===null?void 0:Number(e);if(t!==void 0&&!(Number.isInteger(t)&&t>0))throw b("`diff` must be a revision number");return await A(async o=>{const a=await E(o,n),c=await G(o,a,decodeURIComponent(n.params.ref));if("remote"in c)throw g(404,"not_found",z(c.remote),"Check the name with `crtr canvas search`.");const s="deleted"in c?c.deleted:c;if(s.type!=="document")throw b(`${s.name} is a ${s.type}, not a document`);const f=await ue(o,a,s),r={object:await je(o,s),revisions:f.map(d=>({rev_id:d.rev_id,number:d.number,author_id:d.author,rationale:d.rationale,verbatim:d.verbatim,created_at:d.created_at,parents:d.parents,acting_app:d.acting_app,acting_for:d.acting_for}))};if(t!==void 0){const d=f.find(i=>i.number===t);if(!d)throw new k(404,"not_found",`${s.name} has no revision ${t}`);const w=d.parents[0]===void 0?void 0:f.find(i=>i.rev_id===d.parents[0]);r.diff=_e(w?$(w.body_hash):"",$(d.body_hash))}return r})}u(Ie,"revisionHistory");const ee=1e3,te=1e3,ne=3e3,xe=/^When\b.+\bbecause\b.+/,Le=u(n=>{const e=n.trim();return e===""?0:e.split(/\s+/).length},"words");function We(n,e){if(n.some(o=>o.on==="boot"&&o.deliver==="content")&&e>ee)return`body is ${e} words, over the ${ee}-word cap for a boot rule at content (the whole body enters every node's context). Keep the core here and move separate subjects into [[linked]] documents.`;const t=[...new Set(n.filter(o=>o.on!=="boot"&&o.deliver==="content").map(o=>o.on))];return t.length>0&&e>te?`body is ${e} words, over the ${te}-word cap for a content rule on ${t.join("/")}. Keep the core here and move separate subjects into [[linked]] documents.`:n.some(o=>o.on==="boot"&&o.deliver==="preview")&&e>ne?`body is ${e} words, over the ${ne}-word cap for a boot rule at preview. Keep the core here and move separate subjects into [[linked]] documents.`:null}u(We,"lengthFinding");async function Me(n){const e=R(n);return await A(async t=>{const o=await E(t,n),a=e.owner===void 0?null:await W(t,o,e.owner,e.cwd),c=await t.prepare("SELECT * FROM objects WHERE type = 'document' AND deleted_at IS NULL AND home_machine IS NULL AND kind != 'report' ORDER BY name").all(),s=[],f=new Map;for(const r of c){if(a&&r.owner!==a.id||!await ae(t,o,r))continue;const d=await I(t,r),w=new Set(d.lint_ignore??[]),i=await j(t,r),l=u((p,y,F)=>{w.has(p)||s.push({name:i,severity:y,rule:p,message:F})},"add"),m=await ve(t,r),h=(await x(t,r))[0],_=h?$(h.body_hash):"",v=We(m,Le(_));v&&l("length","error",v),m.some(p=>p.deliver==="preview")&&!xe.test(r.preview??"")&&l("preview-shape","error",`a rule delivers this document at preview, so the preview must read "When <situation in the reader's work>, read this because <what the reader gains>"`);const D=new Set((await t.prepare("SELECT written_as FROM edges WHERE type = 'links-to' AND src = ?").all(r.id)).map(p=>p.written_as));for(const p of V(_)){if(D.has(p))continue;const y=await K(t,o,p);"remote"in y||"object"in y||"deleted"in y||l("links","warning",`[[${p}]] names no object in your view`)}if(m.some(p=>p.on==="workspace-open"&&p.deliver==="content")){const p=await se(t,r);p.type==="repo"&&!w.has("workspace-open")&&f.set(p.name,[...f.get(p.name)??[],i])}}for(const[r,d]of f)if(!(d.length<2))for(const w of d)s.push({name:w,severity:"warning",rule:"workspace-open",message:`repo ${r} has ${d.length} documents delivered at content on workspace-open (${d.join(", ")}); keep one and link the rest from it`});return{findings:s}})}u(Me,"lint");const O=u((n,e=200)=>async t=>({status:e,body:await n(t)}),"ok"),at=[{method:"POST",pattern:"/v1/docs",handler:O(Ce,201)},{method:"PATCH",pattern:"/v1/docs/:ref",handler:O(Se)},{method:"POST",pattern:"/v1/docs/:ref/move",handler:O(Te)},{method:"DELETE",pattern:"/v1/docs/:ref",handler:O(Fe)},{method:"GET",pattern:"/v1/docs/:ref/history",handler:O(Ie)},{method:"POST",pattern:"/v1/docs/lint",handler:O(Me)}];export{j as canonicalName,at as docRoutes,S as repoChange,T as syncChanged};
1
+ var ie=Object.defineProperty;var u=(n,e)=>ie(n,"name",{value:e,configurable:!0});import{ApiError as k}from"../../../api/errors.js";import{DOC_KINDS as x,DOC_UNSET_FIELDS as U}from"../../../api/dto/docs.js";import{PRIVACY_LEVELS_DTO as V}from"../../../api/dto/objects.js";import{withCanvasWrite as A}from"../../../core/canvas/db.js";import{CrtrError as re}from"../../../core/errors.js";import{emitEvent as ae}from"../../../core/events/emit.js";import{canChange as se,spaceOf as ce}from"../../../core/graph/access.js";import{get as j}from"../../../core/graph/bodies.js";import{deleteDocument as de,documentRecord as L,editDocument as ue,heads as W,history as fe,linksIn as K,moveDocument as pe,readBase as le,setDocumentRecord as me,withCurrentNames as we,writeDocument as he}from"../../../core/graph/documents.js";import{resolveName as G}from"../../../core/graph/names.js";import{getById as M}from"../../../core/graph/objects.js";import{noteRepoDocumentsChanged as ye}from"../../../core/graph/repo-sync/index.js";import{checkoutsForCwd as ge}from"../../../core/graph/repo-sync/repos.js";import{lintRules as ve,rulesOf as _e}from"../../../core/graph/rules.js";import{unifiedDiff as be}from"../../../core/graph/diff.js";import{ownerForGrantee as ke}from"../../../core/graph/objects.js";import{ownerFromFlag as q,readerFor as E,resolveRef as X,warmReaderRepos as O}from"../reader.js";import{appCaller as z,asApiError as Ae,requireOwnNamespace as Ee}from"./schemas.js";import{filterAsync as Oe,flatMapAsync as $e}from"../../../core/async-array.js";function g(n,e,t,o,a={}){return new k(n,e,t,{...a,error:e,message:t,next:o})}u(g,"refuse");const b=u((n,e)=>new k(400,"invalid_request",n,e),"invalid");function C(n){const e=n.body;if(e===void 0)return{};if(typeof e!="object"||e===null||Array.isArray(e))throw b("the request body must be a JSON object");return e}u(C,"bodyOf");const N=u(async n=>async e=>await ge(n,e),"reposFnFor");function S(n,e){if(n instanceof re){const t=n.details;if(n.code==="read_first")throw g(409,"read_first",n.message,`Read it first with \`crtr canvas read ${e?.id??"<ref>"}\`, then rerun the edit.`,t??{});if(n.code==="conflict")throw new k(409,"name_taken",n.message,t);if(n.code==="forbidden")throw typeof t?.next=="string"?g(403,"forbidden",n.message,t.next,t):new k(403,"forbidden",n.message,t);if(n.code==="scope_missing")throw g(403,"scope_missing",n.message,`Launch the run with \`${String(t?.scope)}\` in its scopes list.`,t??{});if(t?.rule==="repo-public-only")throw g(422,"repo_documents_are_public",n.message,'Own it as `user` and add a rule with `if: {"repo": "<handle>"}` so it loads only in that repo.')}throw Ae(n)}u(S,"mapGraphError");async function B(n,e,t){const o=await X(n,e,decodeURIComponent(t));if("remote"in o)throw g(404,"not_found",Y(o.remote),"Check the name with `crtr canvas search`.");if("deleted"in o)throw new k(404,"not_found",`${o.deleted.name} is deleted`,{id:o.deleted.id,deleted:!0});if(o.type!=="document")throw b(`${t} is a ${o.type}, not a document`);return o}u(B,"refDocument");function Y(n){return`no object named ${n} on this runtime; it may be on another runtime, or the name may be misspelled`}u(Y,"remoteMessage");async function je(n,e){const t=e.owner===null?null:await M(n,e.owner);return t?t.type==="node"?t.id:t.name:""}u(je,"ownerHandle");async function R(n,e){const t=await je(n,e);return t?`${t}/${e.name}`:e.name}u(R,"canonicalName");async function Re(n,e){const t=e.owner===null?null:await M(n,e.owner),o=t?{id:t.id,type:t.type,handle:t.type==="node"?t.id:t.name}:null;return{id:e.id,type:e.type,kind:e.kind,owner:o,privacy:e.privacy,name:e.name,preview:e.preview??"",created_at:e.created_at,deleted_at:e.deleted_at,...e.type==="document"?{schema_type:e.schema_type??null}:{}}}u(Re,"objectDTO");async function De(n,e){const t=e.owner===null?null:await M(n,e.owner);return t?.type==="repo"?t:null}u(De,"repoOwner");async function T(n,e,t,o){const a=[...new Map(await $e(t,async f=>{const i=await De(n,f);return i?[[i.id,i]]:[]})).values()],c=e.class==="node"?e.cwd:o,s=e.class==="node"?[...e.checkouts,...e.projectCheckouts].map(f=>f.root):[];return{repos:a,dirs:[...new Set([...c!==void 0?[c]:[],...s])]}}u(T,"repoChange");async function F(n){for(const e of n.repos)try{await ye(e,n.dirs)}catch(t){ae({level:"warn",event:"repo_sync.failed",outcome:"failed",error:t,fields:{repo:e.name,step:"export",reason:t instanceof Error?t.message:String(t)}})}}u(F,"syncChanged");const Z=/^[A-Za-z0-9_-]+(\/[A-Za-z0-9_-]+)*$/;function Ce(n,e){if(n===void 0)return;if(!Array.isArray(n)){e.push("`rules` must be a list");return}const t=n.map(a=>{if(typeof a!="object"||a===null||Array.isArray(a))return a;const{match_frontmatter:c,...s}=a;return"matchFrontmatter"in s?{...s,match_frontmatter:s.matchFrontmatter}:c===void 0?s:{...s,matchFrontmatter:c}}),o=ve(t);if(o){e.push(o.replaceAll("matchFrontmatter","match_frontmatter"));return}return t}u(Ce,"rulesFromDTO");function J(n,e){const t={},o=u(i=>{const d=n[i];if(d!==void 0){if(typeof d!="string"){e.push(`\`${i}\` must be a string`);return}return d}},"str"),a=o("preview");a!==void 0&&(a.trim()===""||/[\r\n]/.test(a)?e.push("`preview` must be one non-empty line"):t.preview=a);const c=o("summary");c!==void 0&&(t.summary=c);const s=o("why_it_exists");if(s!==void 0&&(t.whyItExists=s),n.privacy!==void 0&&(V.includes(n.privacy)?t.privacy=n.privacy:e.push(`\`privacy\` must be one of ${V.join(", ")}`)),n.unlisted!==void 0&&(typeof n.unlisted!="boolean"?e.push("`unlisted` must be a boolean"):t.unlisted=n.unlisted),n.lint_ignore!==void 0){const i=n.lint_ignore;!Array.isArray(i)||i.some(d=>typeof d!="string")?e.push("`lint_ignore` must be a list of strings"):t.lintIgnore=i}if(n.extensions!==void 0){const i=n.extensions;typeof i!="object"||i===null||Array.isArray(i)||Object.values(i).some(d=>typeof d!="object"||d===null||Array.isArray(d))?e.push("`extensions` must map each plugin to an object of fields"):t.extensions=i}const f=Ce(n.rules,e);return f!==void 0&&(t.rules=f),t}u(J,"fieldsFrom");function Q(n){if(n.length>0)throw b(n.join("; "),{errors:n})}u(Q,"failOn");async function ee(n,e,t,o){const a=await Oe(K(t),async c=>"remote"in await G(n,e,c));return{...o.length>0?{unresolved_links:o}:{},...a.length>0?{remote_links:a}:{}}}u(ee,"linkReport");async function Ne(n){const e=C(n),t=[],o=e.name;(typeof o!="string"||!Z.test(o))&&t.push("`name` must be segments of letters, digits, `_` or `-` joined by `/`"),typeof e.body!="string"&&t.push("`body` must be a string"),typeof e.preview!="string"&&t.push("`preview` is required");const a=e.kind??"knowledge";x.includes(a)||t.push(a==="report"?"reports are written only by `crtr push`":`\`kind\` must be one of ${x.join(", ")}`);const c=J(e,t);e.cwd!==void 0&&typeof e.cwd!="string"&&t.push("`cwd` must be a string");const s=e.schema_type;s!==void 0&&typeof s!="string"&&t.push("`schema_type` must be a string");const f=e.relations;f!==void 0&&(!Array.isArray(f)||f.some(r=>typeof r!="object"||r===null||typeof r.relation!="string"||typeof r.subject!="string"))&&t.push("`relations` must be a list of {relation, subject} strings"),Q(t);const i=e;await O(n.principal,i.cwd);const{dto:d,change:h}=await A(async r=>{const p=await E(r,n);i.schema_type!==void 0?await Ee(r,z(n).app,i.schema_type):i.relations!==void 0&&z(n);const l=i.owner==="user"&&p.class==="app"&&p.sub!==void 0?await ke(r,p.sub.startsWith("user:")?p.sub:`user:${p.sub}`):await q(r,p,i.owner,i.cwd);try{const{object:w,unresolved:_}=await he(r,p,{...c,owner:l,name:i.name,kind:a,preview:c.preview,privacy:c.privacy??"public",...i.schema_type!==void 0?{schemaType:i.schema_type}:{},...i.relations!==void 0?{relations:i.relations}:{}},i.body,await N(r));return{dto:{id:w.id,name:await R(r,w),...await ee(r,p,i.body,_)},change:await T(r,p,[w],i.cwd)}}catch(w){S(w)}});return await F(h),d}u(Ne,"write");function Se(n,e){let t=n;e.forEach(({old:o,new:a},c)=>{const s=o===""?-1:t.indexOf(o);if(s<0)throw g(422,"replace_no_match",`replace[${c}]: old text not found in the revision you read`,"Copy the old text exactly from your last read and rerun.",{index:c});if(t.indexOf(o,s+1)>=0)throw g(422,"replace_ambiguous",`replace[${c}]: old text occurs more than once`,"Include more surrounding text so the old text matches once, and rerun.",{index:c});t=t.slice(0,s)+a+t.slice(s+o.length)})}u(Se,"checkReplacements");async function Te(n){const e=C(n),t=[],o=e.body!==void 0,a=e.replace!==void 0;o&&a&&t.push("give `body` or `replace`, not both"),o&&typeof e.body!="string"&&t.push("`body` must be a string");const c=e.replace;a&&(!Array.isArray(c)||c.length===0||c.some(r=>typeof r!="object"||r===null||typeof r.old!="string"||typeof r.new!="string"))&&t.push("`replace` must be a non-empty list of {old, new} strings"),(typeof e.rationale!="string"||e.rationale.trim()==="")&&t.push("`rationale` is required"),e.verbatim!==void 0&&typeof e.verbatim!="boolean"&&t.push("`verbatim` must be a boolean"),e.base!==void 0&&(typeof e.base!="string"||e.base==="")&&t.push("`base` must be a non-empty heads key"),e.base!==void 0&&!o&&!a&&t.push("`base` applies only to a `body` or `replace` edit");const s=J(e,t),f=e.unset??[];if(!Array.isArray(f)||f.some(r=>!U.includes(r)))t.push(`\`unset\` takes only ${U.join(", ")}`);else for(const r of f)r==="summary"&&(s.summary=null),r==="why_it_exists"&&(s.whyItExists=null),r==="unlisted"&&(s.unlisted=!1),r==="lint_ignore"&&(s.lintIgnore=null),r==="extensions"&&(s.extensions=null),r==="rules"&&(s.rules=[]);t.length===0&&!o&&!a&&Object.keys(s).length===0&&t.push("nothing to change: give `body`, `replace`, or a field"),Q(t);const i=e;await O(n.principal);const{dto:d,synced:h}=await A(async r=>{const p=await E(r,n),l=await B(r,p,n.params.ref),w=await R(r,l),_=await T(r,p,[l]);try{if(!o&&!a)return await me(r,p,l,s,i.rationale),{dto:{id:l.id,name:w,revision:(await L(r,l)).revision},synced:_};const v=i.base??await le(r,p,l);if(a&&v!==null){const P=await L(r,l),H=await W(r,l);P.source_path===null&&P.heads_key===v&&H.length===1&&Se(await we(r,l,j(H[0].body_hash)),i.replace)}const D=i.verbatim===!0,m=o?{body:i.body,verbatim:D}:{replace:i.replace,verbatim:D},y=await ue(r,p,l,m,i.rationale,v,s,await N(r));if("stale"in y)throw g(409,"stale_revision",`${w} changed after your read; it is now at revision ${y.stale.revision}`,`Read it again with \`crtr canvas read ${w}\`, then redo your change on the current text.`,{stale:y.stale});const I=j((await W(r,l))[0].body_hash);return{dto:{id:l.id,name:w,revision:y.revision,...await ee(r,p,I,y.unresolved)},synced:_}}catch(v){S(v,l)}});return await F(h),d}u(Te,"edit");async function Fe(n){const e=C(n);if(e.new_name===void 0&&e.owner===void 0)throw b("give a new name, an owner, or both");if(e.new_name!==void 0&&(typeof e.new_name!="string"||!Z.test(e.new_name)))throw b("`new_name` must be segments of letters, digits, `_` or `-` joined by `/`");await O(n.principal,e.cwd);const{dto:t,change:o}=await A(async a=>{const c=await E(a,n),s=await B(a,c,n.params.ref),f=e.owner===void 0?void 0:await q(a,c,e.owner,e.cwd);try{const i=await pe(a,c,s,{...e.new_name!==void 0?{name:e.new_name}:{},...f?{owner:f}:{}},await N(a));return{dto:{id:i.id,name:await R(a,i)},change:await T(a,c,[s,i],e.cwd)}}catch(i){S(i,s)}});return await F(o),t}u(Fe,"move");async function Ie(n){await O(n.principal);const{dto:e,change:t}=await A(async o=>{const a=await E(o,n),c=await B(o,a,n.params.ref),s=await R(o,c),f=await T(o,a,[c]);try{await de(o,a,c,await N(o))}catch(i){S(i,c)}return{dto:{id:c.id,name:s},change:f}});return await F(t),e}u(Ie,"remove");async function Le(n){const e=n.query.get("diff"),t=e===null?void 0:Number(e);if(t!==void 0&&!(Number.isInteger(t)&&t>0))throw b("`diff` must be a revision number");return await O(n.principal),await A(async o=>{const a=await E(o,n),c=await X(o,a,decodeURIComponent(n.params.ref));if("remote"in c)throw g(404,"not_found",Y(c.remote),"Check the name with `crtr canvas search`.");const s="deleted"in c?c.deleted:c;if(s.type!=="document")throw b(`${s.name} is a ${s.type}, not a document`);const f=await fe(o,a,s),i={object:await Re(o,s),revisions:f.map(d=>({rev_id:d.rev_id,number:d.number,author_id:d.author,rationale:d.rationale,verbatim:d.verbatim,created_at:d.created_at,parents:d.parents,acting_app:d.acting_app,acting_for:d.acting_for}))};if(t!==void 0){const d=f.find(r=>r.number===t);if(!d)throw new k(404,"not_found",`${s.name} has no revision ${t}`);const h=d.parents[0]===void 0?void 0:f.find(r=>r.rev_id===d.parents[0]);i.diff=be(h?j(h.body_hash):"",j(d.body_hash))}return i})}u(Le,"revisionHistory");const te=1e3,ne=1e3,oe=3e3,We=/^When\b.+\bbecause\b.+/,Me=u(n=>{const e=n.trim();return e===""?0:e.split(/\s+/).length},"words");function qe(n,e){if(n.some(o=>o.on==="boot"&&o.deliver==="content")&&e>te)return`body is ${e} words, over the ${te}-word cap for a boot rule at content (the whole body enters every node's context). Keep the core here and move separate subjects into [[linked]] documents.`;const t=[...new Set(n.filter(o=>o.on!=="boot"&&o.deliver==="content").map(o=>o.on))];return t.length>0&&e>ne?`body is ${e} words, over the ${ne}-word cap for a content rule on ${t.join("/")}. Keep the core here and move separate subjects into [[linked]] documents.`:n.some(o=>o.on==="boot"&&o.deliver==="preview")&&e>oe?`body is ${e} words, over the ${oe}-word cap for a boot rule at preview. Keep the core here and move separate subjects into [[linked]] documents.`:null}u(qe,"lengthFinding");async function Be(n){const e=C(n);return await O(n.principal,e.cwd),await A(async t=>{const o=await E(t,n),a=e.owner===void 0?null:await q(t,o,e.owner,e.cwd),c=await t.prepare("SELECT * FROM objects WHERE type = 'document' AND deleted_at IS NULL AND home_machine IS NULL AND kind != 'report' ORDER BY name").all(),s=[],f=new Map;for(const i of c){if(a&&i.owner!==a.id||!await se(t,o,i))continue;const d=await L(t,i),h=new Set(d.lint_ignore??[]),r=await R(t,i),p=u((m,y,I)=>{h.has(m)||s.push({name:r,severity:y,rule:m,message:I})},"add"),l=await _e(t,i),w=(await W(t,i))[0],_=w?j(w.body_hash):"",v=qe(l,Me(_));v&&p("length","error",v),l.some(m=>m.deliver==="preview")&&!We.test(i.preview??"")&&p("preview-shape","error",`a rule delivers this document at preview, so the preview must read "When <situation in the reader's work>, read this because <what the reader gains>"`);const D=new Set((await t.prepare("SELECT written_as FROM edges WHERE type = 'links-to' AND src = ?").all(i.id)).map(m=>m.written_as));for(const m of K(_)){if(D.has(m))continue;const y=await G(t,o,m);"remote"in y||"object"in y||"deleted"in y||p("links","warning",`[[${m}]] names no object in your view`)}if(l.some(m=>m.on==="workspace-open"&&m.deliver==="content")){const m=await ce(t,i);m.type==="repo"&&!h.has("workspace-open")&&f.set(m.name,[...f.get(m.name)??[],r])}}for(const[i,d]of f)if(!(d.length<2))for(const h of d)s.push({name:h,severity:"warning",rule:"workspace-open",message:`repo ${i} has ${d.length} documents delivered at content on workspace-open (${d.join(", ")}); keep one and link the rest from it`});return{findings:s}})}u(Be,"lint");const $=u((n,e=200)=>async t=>({status:e,body:await n(t)}),"ok"),st=[{method:"POST",pattern:"/v1/docs",handler:$(Ne,201)},{method:"PATCH",pattern:"/v1/docs/:ref",handler:$(Te)},{method:"POST",pattern:"/v1/docs/:ref/move",handler:$(Fe)},{method:"DELETE",pattern:"/v1/docs/:ref",handler:$(Ie)},{method:"GET",pattern:"/v1/docs/:ref/history",handler:$(Le)},{method:"POST",pattern:"/v1/docs/lint",handler:$(Be)}];export{R as canonicalName,st as docRoutes,T as repoChange,F as syncChanged};
@@ -1 +1 @@
1
- var k=Object.defineProperty;var o=(t,u)=>k(t,"name",{value:u,configurable:!0});import{randomUUID as V}from"node:crypto";import{covers as Y}from"@north-light/identity";import{appSpace as j}from"../../../core/layout.js";import{emitEvent as M}from"../../../core/events/emit.js";import{RunError as i}from"../../../core/runs/operations.js";import{readAppEnvVars as F}from"../../../core/profiles/app-env-store.js";import{ADMITTED_HOST_PATH_ENV as R,buildOperationalEnvBase as $,resolveAdmittedHostCommand as J}from"../../../core/runtime/spawn-env.js";import{appProcessesSandboxed as G,launchAppProcess as K}from"../../../core/spaces/process.js";import{uidForApp as z}from"../../../core/spaces/users.js";import{envHomeOverride as Q}from"../../../shared/env.js";import{closeCronExecution as W,openCronExecution as Z}from"../../cron/run-socket.js";const ee=60,U=100,C=32*1024,pe=C+1024,q=256*1024,L=4,l=new Map;function te(t){if(typeof t!="object"||t===null||Array.isArray(t))throw new i(400,"invalid_request","exec requires an object body");const u=t,r=u.argv;if(!Array.isArray(r)||r.length===0||!r.every(e=>typeof e=="string"))throw new i(400,"invalid_request","argv must be a non-empty array of strings","argv");if(r.some(e=>e.includes("\0")))throw new i(400,"invalid_request","argv strings cannot contain NUL","argv");if(Buffer.byteLength(JSON.stringify(r))>=C)throw new i(400,"invalid_request","argv must be less than 32 KB as JSON","argv");const s=u.timeout_s??ee;if(typeof s!="number"||!Number.isFinite(s)||s<=0||s>U)throw new i(400,"invalid_request",`timeout_s must be greater than 0 and at most ${U}`,"timeout_s");return{argv:r,timeout_s:s}}o(te,"parse");async function _e(t,u,r){if(!t.resolved.some(n=>Y(n,"crtr:schedule")))throw new i(403,"scope_missing","missing scope crtr:schedule",void 0,void 0,"crtr:schedule");const s=te(u);if(!G())throw new i(503,"sandbox_unavailable","this runtime has no root launcher, so it cannot sandbox an app process",void 0,void 0,void 0,!1);const e=t.grantee,h=l.get(e)??0;if(h>=L)throw new i(429,"busy",`${e} already has ${L} exec processes running; retry when one finishes`,void 0,void 0,void 0,!0);l.set(e,h+1);const f=V(),p=Date.now();let a,b=!1,E=!1;const y=o(()=>{if(E)return;E=!0,b&&W(f);const n=(l.get(e)??1)-1;n>0?l.set(e,n):l.delete(e),M({level:"info",event:"exec.run",fields:{app:e,argv:s.argv.join(" ").slice(0,200),exit_code:a?.exit_code??null,duration_ms:Date.now()-p}})},"release");let c=null,x=o(()=>{},"wake");const A=new Promise(n=>{x=o(()=>n(null),"wake")}),_=o(n=>{c===null&&(c=n,x())},"cancel"),H=setTimeout(()=>_("timeout"),s.timeout_s*1e3),T=o(()=>_("abort"),"onAbort");r.addEventListener("abort",T,{once:!0}),r.aborted&&_("abort");const I=o(()=>({exit_code:null,signal:null,stdout:"",stdout_truncated:!1,stderr:"",stderr_truncated:!1,timed_out:c==="timeout",duration_ms:Date.now()-p}),"unstarted");let D=!1;try{const n=j(e).files,O=$({targetCwd:n,targetProfileId:null,skipSpawnAllow:!0}),g=Q(),S={...O,...F(e),CRTR_RUN_ID:f,...g===void 0||g===""?{}:{CRTR_HOME:g},[R]:O[R]},N=J("crtr",S,n),v={executionId:f,kind:"exec",cronId:null,fromExec:!0,grantee:e,sub:t.sub,grantId:t.grant,scopes:null,originRunId:null,isolated:!1,anchor:null,uid:await z(e),rebuilt:!1,refusal:null};if(await Z(v,n,null),b=!0,c!==null)return a=I(),a;const P=K({app:e,principal:{sub:t.sub,grantId:t.grant,scopes:null},cron:{executionId:f},command:[N,...s.argv],cwd:n,env:S,stdoutBytes:q,stderrBytes:q}),m=await Promise.race([P,A]);if(m===null)return D=!0,P.then(w=>w.stop()).catch(()=>{}).finally(y),a=I(),a;const X=o(()=>{m.stop().catch(w=>M({level:"error",event:"exec.stop_failed",error:w,fields:{app:e,pid:m.pid}}))},"stop");A.then(X);const d=await m.completion;if(v.refusal!==null)throw new i(401,"grant_revoked",v.refusal.message);const B=c==="timeout";return a={exit_code:B?null:d.exitCode,signal:d.signal,stdout:d.stdout,stdout_truncated:d.stdoutTruncated,stderr:d.stderr,stderr_truncated:d.stderrTruncated,timed_out:B,duration_ms:Date.now()-p},a}finally{clearTimeout(H),r.removeEventListener("abort",T),D||y()}}o(_e,"handleExec");export{pe as EXEC_BODY_MAX_BYTES,_e as handleExec};
1
+ var k=Object.defineProperty;var o=(t,u)=>k(t,"name",{value:u,configurable:!0});import{randomUUID as V}from"node:crypto";import{covers as Y}from"@north-light/identity";import{appSpace as j}from"../../../core/layout.js";import{emitEvent as M}from"../../../core/events/emit.js";import{RunError as i}from"../../../core/runs/operations.js";import{readAppEnvVars as F}from"../../../core/profiles/app-env-store.js";import{ADMITTED_HOST_PATH_ENV as R,buildOperationalEnvBase as $,resolveAdmittedHostCommand as J}from"../../../core/runtime/spawn-env.js";import{appProcessesSandboxed as G,launchAppProcess as K}from"../../../core/spaces/process.js";import{uidForApp as z}from"../../../core/spaces/users.js";import{envHomeOverride as Q}from"../../../shared/env.js";import{closeCronExecution as W,openCronExecution as Z}from"../../cron/run-socket.js";const ee=60,U=100,C=32*1024,pe=C+1024,q=256*1024,L=4,l=new Map;function te(t){if(typeof t!="object"||t===null||Array.isArray(t))throw new i(400,"invalid_request","exec requires an object body");const u=t,r=u.argv;if(!Array.isArray(r)||r.length===0||!r.every(e=>typeof e=="string"))throw new i(400,"invalid_request","argv must be a non-empty array of strings","argv");if(r.some(e=>e.includes("\0")))throw new i(400,"invalid_request","argv strings cannot contain NUL","argv");if(Buffer.byteLength(JSON.stringify(r))>=C)throw new i(400,"invalid_request","argv must be less than 32 KB as JSON","argv");const s=u.timeout_s??ee;if(typeof s!="number"||!Number.isFinite(s)||s<=0||s>U)throw new i(400,"invalid_request",`timeout_s must be greater than 0 and at most ${U}`,"timeout_s");return{argv:r,timeout_s:s}}o(te,"parse");async function _e(t,u,r){if(!t.resolved.some(n=>Y(n,"crtr:schedule")))throw new i(403,"scope_missing","missing scope crtr:schedule",void 0,void 0,"crtr:schedule");const s=te(u);if(!G())throw new i(503,"sandbox_unavailable","this runtime has no root launcher, so it cannot sandbox an app process",void 0,void 0,void 0,!1);const e=t.grantee,h=l.get(e)??0;if(h>=L)throw new i(429,"busy",`${e} already has ${L} exec processes running; retry when one finishes`,void 0,void 0,void 0,!0);l.set(e,h+1);const f=V(),p=Date.now();let a,b=!1,E=!1;const y=o(()=>{if(E)return;E=!0,b&&W(f);const n=(l.get(e)??1)-1;n>0?l.set(e,n):l.delete(e),M({level:"info",event:"exec.run",fields:{app:e,argv:s.argv.join(" ").slice(0,200),exit_code:a?.exit_code??null,duration_ms:Date.now()-p}})},"release");let c=null,x=o(()=>{},"wake");const A=new Promise(n=>{x=o(()=>n(null),"wake")}),_=o(n=>{c===null&&(c=n,x())},"cancel"),H=setTimeout(()=>_("timeout"),s.timeout_s*1e3),T=o(()=>_("abort"),"onAbort");r.addEventListener("abort",T,{once:!0}),r.aborted&&_("abort");const I=o(()=>({exit_code:null,signal:null,stdout:"",stdout_truncated:!1,stderr:"",stderr_truncated:!1,timed_out:c==="timeout",duration_ms:Date.now()-p}),"unstarted");let D=!1;try{const n=j(e).files,O=$({targetCwd:n,targetProfileId:null,skipSpawnAllow:!0}),g=Q(),S={...O,...await F(e),CRTR_RUN_ID:f,...g===void 0||g===""?{}:{CRTR_HOME:g},[R]:O[R]},N=J("crtr",S,n),v={executionId:f,kind:"exec",cronId:null,fromExec:!0,grantee:e,sub:t.sub,grantId:t.grant,scopes:null,originRunId:null,isolated:!1,anchor:null,uid:await z(e),rebuilt:!1,refusal:null};if(await Z(v,n,null),b=!0,c!==null)return a=I(),a;const P=K({app:e,principal:{sub:t.sub,grantId:t.grant,scopes:null},cron:{executionId:f},command:[N,...s.argv],cwd:n,env:S,stdoutBytes:q,stderrBytes:q}),m=await Promise.race([P,A]);if(m===null)return D=!0,P.then(w=>w.stop()).catch(()=>{}).finally(y),a=I(),a;const X=o(()=>{m.stop().catch(w=>M({level:"error",event:"exec.stop_failed",error:w,fields:{app:e,pid:m.pid}}))},"stop");A.then(X);const d=await m.completion;if(v.refusal!==null)throw new i(401,"grant_revoked",v.refusal.message);const B=c==="timeout";return a={exit_code:B?null:d.exitCode,signal:d.signal,stdout:d.stdout,stdout_truncated:d.stdoutTruncated,stderr:d.stderr,stderr_truncated:d.stderrTruncated,timed_out:B,duration_ms:Date.now()-p},a}finally{clearTimeout(H),r.removeEventListener("abort",T),D||y()}}o(_e,"handleExec");export{pe as EXEC_BODY_MAX_BYTES,_e as handleExec};
@@ -0,0 +1,2 @@
1
+ import type { RouteTable } from '../router.js';
2
+ export declare const executionStartRoutes: RouteTable;
@@ -0,0 +1,2 @@
1
+ var w=Object.defineProperty;var l=(r,n)=>w(r,"name",{value:n,configurable:!0});import{ApiError as s}from"../../../api/errors.js";import{getNode as f}from"../../../core/canvas/canvas.js";import{openDb as d}from"../../../core/canvas/db.js";import{environmentOfNode as h,recipeOf as g}from"../environment-of.js";import{mintInstallationToken as E}from"./git-credential.js";async function k(r,n){const i=await d(),u=n===null?["user"]:["user",`profile:${n}`],t=await i.prepare(`SELECT owner_key, name, version, source_marketplace, source_path FROM scope_plugins
2
+ WHERE person = ? AND enabled = 1 AND owner_key IN (${u.map(()=>"?").join(", ")}) ORDER BY name`).all(r,...u),o=new Map;for(const e of t)(!o.has(e.name)||e.owner_key!=="user")&&o.set(e.name,e);const a=[];for(const e of o.values()){if(e.source_path!==null){a.push({name:e.name,source:{path:e.source_path},version:e.version});continue}if(e.source_marketplace===null)throw new s(500,"plugin_source_missing",`plugin ${e.name} names neither a marketplace nor a path`);const c=await i.prepare("SELECT url, ref FROM scope_marketplaces WHERE person = ? AND owner_key = ? AND name = ?").get(r,e.owner_key,e.source_marketplace);a.push({name:e.name,source:{marketplace:{url:c?.url??null,ref:c?.ref??null}},version:e.version})}return a}l(k,"plugins");async function b(r){const{principal:n}=r;if(n.class!=="broker")throw new s(403,"broker_only","the start bundle is read with a node token");const i=r.params.execution_id??"",u=await d(),t=await u.prepare("SELECT node_id, state, launch_json FROM executions WHERE execution_id = ?").get(i);if(t===void 0||t.node_id!==n.node)throw new s(403,"not_your_execution",`execution ${i} is not an execution of this node`);if(t.state!=="launching"&&t.state!=="running")throw new s(403,"execution_ended",`execution ${i} is ${t.state}`);if(t.launch_json===null)throw new s(409,"launch_missing",`execution ${i} carries no launch recipe`);const o=await f(n.node);if(o===null)throw new s(404,"node_not_found",`no node ${n.node}`);const a=await u.prepare("SELECT plugins_revision FROM person_settings WHERE person = ?").get(n.sub),e=await h(o.profile_id??null),c=await g(e),p=c?.repositories.map(m=>m.github_full_name)??[],_=p.length===0?null:(await E(n.sub,p)).author;return{status:200,body:{launch:JSON.parse(t.launch_json),node:{id:o.node_id,app:n.app,cwd:o.cwd,profile:o.profile_id??null,isolated:o.isolated===!0,environment_id:e},recipe:c,git_identity:_,plugins_revision:a===void 0?0:Number(a.plugins_revision),plugins:await k(n.sub,o.profile_id??null)}}}l(b,"handleStart");const S=[{method:"GET",pattern:"/v1/executions/:execution_id/start",handler:b}];export{S as executionStartRoutes};
@@ -1 +1 @@
1
- var R=Object.defineProperty;var s=(e,t)=>R(e,"name",{value:t,configurable:!0});import{closeSync as T,constants as q,fstatSync as B,readSync as v}from"node:fs";import{isAbsolute as L,relative as F,resolve as b,sep as I}from"node:path";import{filterAsync as M}from"../../../core/async-array.js";import{appSpace as g}from"../../../core/layout.js";import{ApiError as d}from"../../../api/index.js";import{notFound as E,usage as c}from"../../../core/errors.js";import{InputError as k}from"../../../core/io.js";import{RunError as m}from"../../../core/runs/operations.js";import{isBeneathRefusal as z,listBeneath as N,openBeneath as O,writeFileBeneath as D}from"../../../core/spaces/open-beneath.js";import{fileRoot as P}from"../../../core/spaces/permissions.js";import{openDb as C}from"../../../core/canvas/db.js";import{canReadNode as $,resolvedScopes as S}from"../router.js";const y=512*1024,A=1024*1024,W=1e3;function h(e,t){if(e===null||e==="")throw c(`a file ${t} requires a \`path\` ${t==="write"?"body field":"query param"}`);if(!L(e))throw c(`file ${t} path must be absolute`,{received:e});return e}s(h,"requireAbsolutePath");function _(e){if(e==null)return"utf8";if(e==="utf8"||e==="base64")return e;throw c("encoding must be `utf8` or `base64`",{received:e})}s(_,"parseEncoding");function p(e,t,i){if(e instanceof d||e instanceof k||e instanceof m)throw e;if(z(e))throw new m(400,"invalid_request",`${t} meets a symlink, leaves its permitted root, or is not a regular file`,"path");const n=e.code;throw n==="ENOENT"?E(`no such file: ${t}`,{received:t}):n==="EACCES"||n==="EPERM"?new d(403,"forbidden",`permission denied ${i} ${t}`):new d(500,"internal",`cannot ${i} ${t}: ${e instanceof Error?e.message:String(e)}`)}s(p,"mapFilesystemError");async function Y(e,t,i,n){const r=await(await C()).prepare("SELECT isolated FROM nodes WHERE node_id = ?").get(t),[l,f]=F(g(i).root,b(n)).split(I),a=l==="nodes"&&f?await $(e.principal,i,f):!1;return{isolated:r?.isolated===1,reaches:s(()=>a,"reaches")}}s(Y,"runConfinementFor");async function w(e,t,i){const n=e.principal;if(n.class==="forwarded"||n.class==="machine")throw new d(403,"permission_error","this operation is not available to the caller");const o=n.class==="person"?null:n.class==="app"?{app:n.app,resolved:n.scopes,launched:null}:n.class==="cron"?{app:n.app,resolved:await S(n),launched:n.scopes}:{app:n.app,resolved:await S(n),launched:n.scopes,run:await Y(e,n.node,n.app,t)},r=P(o,t,i);if("root"in r)return r.root;throw"hidden"in r?E(`no such file: ${t}`,{received:t}):new m(403,"scope_missing",r.missing?`missing scope ${r.missing}`:`no permission names ${t}`,void 0,void 0,r.missing??void 0)}s(w,"rootFor");function Z(e,t,i){let n;try{n=O(e,t,q.O_RDONLY)}catch(o){return p(o,t,"reading")}try{const o=B(n);if(o.isDirectory())throw c(`file peek path is a directory: ${t}`,{received:t});const r=Buffer.allocUnsafe(Math.min(o.size,y)+1),l=v(n,r,0,r.length,0),f=l>y,a=r.subarray(0,Math.min(l,y)).toString(i);return{path:t,content:a,truncated:f}}catch(o){return p(o,t,"reading")}finally{T(n)}}s(Z,"readHead");async function j(e){const t=h(e.query.get("path"),"peek");return{status:200,body:Z(await w(e,t,"read"),t,_(e.query.get("encoding")))}}s(j,"handlePeek");function G(e){return e.length%4===0&&/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(e)}s(G,"isBase64");function H(e){if(typeof e!="object"||e===null||Array.isArray(e))throw c("file write requires an object body");const t=e,i=h(typeof t.path=="string"?t.path:null,"write");if(typeof t.content!="string")throw c("file write requires a string `content` body field");const n=_(t.encoding);if(n==="base64"&&!G(t.content))throw c("base64 file content must be valid base64");return{path:i,content:t.content,encoding:n}}s(H,"parseWriteBody");function U(e,t,i){if(i.byteLength>A)throw new d(413,"file_too_large",`file content exceeds the ${A} byte limit`);try{return D(e,t,i,{mode:process.platform==="linux"?432:384,atomic:!0}),{path:t,bytes_written:i.byteLength}}catch(n){return p(n,t,"writing")}}s(U,"writeAtomically");async function X(e){const{path:t,content:i,encoding:n}=H(e.body);return{status:200,body:U(await w(e,t,"rw"),t,Buffer.from(i,n))}}s(X,"handleWrite");function K(e){if(e===null)return W;const t=Number(e);if(!Number.isInteger(t)||t<1)throw c("file list limit must be a positive integer",{received:e});return t}s(K,"parseLimit");async function J(e){const t=h(e.query.get("path"),"list"),i=K(e.query.get("limit")),n=await w(e,t,"read");let o;try{o=N(n,t,i+1)}catch(a){return p(a,t,"listing")}const r=e.principal;r.class==="broker"&&b(t)===g(r.app).nodes&&(o=await M(o,async({name:a})=>await $(r,r.app,a)));const l=o.slice(0,i).map(({name:a,stat:u})=>({name:a,type:u.isFile()?"file":u.isDirectory()?"dir":"other",size:u.size,modified:u.mtime.toISOString()}));return{status:200,body:{path:t,entries:l,truncated:o.length>i}}}s(J,"handleList");const fe=[{method:"GET",pattern:"/v1/files/peek",scope:"crtr:act",handler:j},{method:"POST",pattern:"/v1/files/write",scope:"crtr:act",handler:X},{method:"GET",pattern:"/v1/files/list",scope:"crtr:act",handler:J}];export{fe as fileRoutes};
1
+ var R=Object.defineProperty;var s=(e,t)=>R(e,"name",{value:t,configurable:!0});import{closeSync as T,constants as q,fstatSync as B,readSync as v}from"node:fs";import{isAbsolute as L,relative as F,resolve as b,sep as I}from"node:path";import{filterAsync as M}from"../../../core/async-array.js";import{appSpace as g}from"../../../core/layout.js";import{ApiError as f}from"../../../api/index.js";import{notFound as E,usage as c}from"../../../core/errors.js";import{InputError as k}from"../../../core/io.js";import{RunError as m}from"../../../core/runs/operations.js";import{isBeneathRefusal as z,listBeneath as N,openBeneath as O,writeFileBeneath as D}from"../../../core/spaces/open-beneath.js";import{fileRoot as P}from"../../../core/spaces/permissions.js";import{openDb as C}from"../../../core/canvas/db.js";import{canReadNode as $,resolvedScopes as S}from"../router.js";const h=512*1024,_=1024*1024,W=1e3;function y(e,t){if(e===null||e==="")throw c(`a file ${t} requires a \`path\` ${t==="write"?"body field":"query param"}`);if(!L(e))throw c(`file ${t} path must be absolute`,{received:e});return e}s(y,"requireAbsolutePath");function A(e){if(e==null)return"utf8";if(e==="utf8"||e==="base64")return e;throw c("encoding must be `utf8` or `base64`",{received:e})}s(A,"parseEncoding");function p(e,t,r){if(e instanceof f||e instanceof k||e instanceof m)throw e;if(z(e))throw new m(400,"invalid_request",`${t} meets a symlink, leaves its permitted root, or is not a regular file`,"path");const n=e.code;throw n==="ENOENT"?E(`no such file: ${t}`,{received:t}):n==="EACCES"||n==="EPERM"?new f(403,"forbidden",`permission denied ${r} ${t}`):new f(500,"internal",`cannot ${r} ${t}: ${e instanceof Error?e.message:String(e)}`)}s(p,"mapFilesystemError");async function Y(e,t,r,n){const i=await(await C()).prepare("SELECT isolated FROM nodes WHERE node_id = ?").get(t),[l,d]=F(g(r).root,b(n)).split(I),a=l==="nodes"&&d?await $(e.principal,r,d):!1;return{isolated:i?.isolated===1,reaches:s(()=>a,"reaches")}}s(Y,"runConfinementFor");async function w(e,t,r){const n=e.principal;if(n.class==="forwarded"||n.class==="machine")throw new f(403,"permission_error","this operation is not available to the caller");if(n.class==="person"&&n.sub!==void 0)throw new f(403,"permission_error","files are read by the install's person or a node");const o=n.class==="person"?null:n.class==="app"?{app:n.app,resolved:n.scopes,launched:null}:n.class==="cron"?{app:n.app,resolved:await S(n),launched:n.scopes}:{app:n.app,resolved:await S(n),launched:n.scopes,run:await Y(e,n.node,n.app,t)},i=P(o,t,r);if("root"in i)return i.root;throw"hidden"in i?E(`no such file: ${t}`,{received:t}):new m(403,"scope_missing",i.missing?`missing scope ${i.missing}`:`no permission names ${t}`,void 0,void 0,i.missing??void 0)}s(w,"rootFor");function Z(e,t,r){let n;try{n=O(e,t,q.O_RDONLY)}catch(o){return p(o,t,"reading")}try{const o=B(n);if(o.isDirectory())throw c(`file peek path is a directory: ${t}`,{received:t});const i=Buffer.allocUnsafe(Math.min(o.size,h)+1),l=v(n,i,0,i.length,0),d=l>h,a=i.subarray(0,Math.min(l,h)).toString(r);return{path:t,content:a,truncated:d}}catch(o){return p(o,t,"reading")}finally{T(n)}}s(Z,"readHead");async function j(e){const t=y(e.query.get("path"),"peek");return{status:200,body:Z(await w(e,t,"read"),t,A(e.query.get("encoding")))}}s(j,"handlePeek");function G(e){return e.length%4===0&&/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(e)}s(G,"isBase64");function H(e){if(typeof e!="object"||e===null||Array.isArray(e))throw c("file write requires an object body");const t=e,r=y(typeof t.path=="string"?t.path:null,"write");if(typeof t.content!="string")throw c("file write requires a string `content` body field");const n=A(t.encoding);if(n==="base64"&&!G(t.content))throw c("base64 file content must be valid base64");return{path:r,content:t.content,encoding:n}}s(H,"parseWriteBody");function U(e,t,r){if(r.byteLength>_)throw new f(413,"file_too_large",`file content exceeds the ${_} byte limit`);try{return D(e,t,r,{mode:process.platform==="linux"?432:384,atomic:!0}),{path:t,bytes_written:r.byteLength}}catch(n){return p(n,t,"writing")}}s(U,"writeAtomically");async function X(e){const{path:t,content:r,encoding:n}=H(e.body);return{status:200,body:U(await w(e,t,"rw"),t,Buffer.from(r,n))}}s(X,"handleWrite");function K(e){if(e===null)return W;const t=Number(e);if(!Number.isInteger(t)||t<1)throw c("file list limit must be a positive integer",{received:e});return t}s(K,"parseLimit");async function J(e){const t=y(e.query.get("path"),"list"),r=K(e.query.get("limit")),n=await w(e,t,"read");let o;try{o=N(n,t,r+1)}catch(a){return p(a,t,"listing")}const i=e.principal;i.class==="broker"&&b(t)===g(i.app).nodes&&(o=await M(o,async({name:a})=>await $(i,i.app,a)));const l=o.slice(0,r).map(({name:a,stat:u})=>({name:a,type:u.isFile()?"file":u.isDirectory()?"dir":"other",size:u.size,modified:u.mtime.toISOString()}));return{status:200,body:{path:t,entries:l,truncated:o.length>r}}}s(J,"handleList");const fe=[{method:"GET",pattern:"/v1/files/peek",scope:"crtr:act",handler:j},{method:"POST",pattern:"/v1/files/write",scope:"crtr:act",handler:X},{method:"GET",pattern:"/v1/files/list",scope:"crtr:act",handler:J}];export{fe as fileRoutes};
@@ -0,0 +1,11 @@
1
+ import type { RouteTable } from '../router.js';
2
+ /** The GitHub app's internal route: a token limited to `repositories` for the person, and the commit author for their GitHub link (null when unlinked). */
3
+ export declare function mintInstallationToken(sub: string, repositories: string[]): Promise<{
4
+ token: string;
5
+ expires_at: string;
6
+ author: {
7
+ name: string;
8
+ email: string;
9
+ } | null;
10
+ }>;
11
+ export declare const gitCredentialRoutes: RouteTable;
@@ -0,0 +1 @@
1
+ var p=Object.defineProperty;var u=(o,t)=>p(o,"name",{value:t,configurable:!0});import{ApiError as n}from"../../../api/errors.js";import{getNode as f}from"../../../core/canvas/canvas.js";import{environmentOfNode as m,recipeOf as _}from"../environment-of.js";import{invalidRequest as w,objectBody as g,optionalString as b,requiredString as v}from"./validate.js";function l(o){const t=process.env[o];if(t===void 0||t==="")throw new n(500,"github_service_unconfigured",`${o} is not set on this control plane`);return t}u(l,"requiredEnv");async function y(o){const{principal:t}=o;if(t.class!=="broker")throw new n(403,"broker_only","git credentials are issued to a node token");const i=o.params.environment_id??"",a=await f(t.node);if(a===null)throw new n(404,"node_not_found",`no node ${t.node}`);if(await m(a.profile_id??null)!==i)throw new n(403,"not_your_environment",`this node does not run in environment ${i}`);const e=[],r=g(o.body,["host","path"],e),s=v(r,"host",e);if(b(r,"path",e),e.length>0&&w("git credential request",e),s!=="github.com")throw new n(400,"unsupported_git_host",`only github.com credentials are issued, not ${s}`);const d=(await _(i))?.repositories.map(h=>h.github_full_name)??[];if(d.length===0)throw new n(409,"no_recipe_repositories",`environment ${i} has no recipe repositories`);const c=await k(t.sub,d);return{status:200,body:{username:"x-access-token",password:c.token,expiry:c.expires_at}}}u(y,"handleGitCredential");async function k(o,t){const i=await fetch(`${l("GITHUB_SERVICE_URL").replace(/\/$/,"")}/internal/installation-token`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${l("CROUTER_SERVICE_TOKEN")}`},body:JSON.stringify({sub:o,repositories:t})}),a=await i.text(),e=a===""?{}:JSON.parse(a);if(!i.ok){const s=typeof e.error=="object"?e.error?.code:e.code,d=(typeof e.error=="object"?e.error?.message:e.message)??`the GitHub service answered ${i.status}`;throw i.status===409&&(s==="repositories_span_installations"||s==="github_not_connected")?new n(409,s,d,{repositories:t}):new n(502,"github_service_failed",d,{status:i.status,...s===void 0?{}:{code:s}})}if(typeof e.token!="string"||typeof e.expires_at!="string")throw new n(502,"github_service_failed","the GitHub service answered without a token and expiry");const r=e.author;if(r===void 0)throw new n(502,"github_service_failed","the GitHub service answered without an author field");if(r!==null&&(typeof r.name!="string"||typeof r.email!="string"))throw new n(502,"github_service_failed","the GitHub service answered a malformed author");return{token:e.token,expires_at:e.expires_at,author:r===null?null:{name:r.name,email:r.email}}}u(k,"mintInstallationToken");const R=[{method:"POST",pattern:"/v1/environments/:environment_id/git-credential",handler:y}];export{R as gitCredentialRoutes,k as mintInstallationToken};
@@ -1,2 +1,2 @@
1
- import type { RouteTable } from '../router.js';
1
+ import { type RouteTable } from '../router.js';
2
2
  export declare const hookExecRoutes: RouteTable;
@@ -1 +1 @@
1
- var x=Object.defineProperty;var u=(o,n)=>x(o,"name",{value:n,configurable:!0});import{realpathSync as E}from"node:fs";import{isAbsolute as y,relative as _,sep as h}from"node:path";import{ApiError as r}from"../../../api/index.js";import{coreHookCatalog as T}from"../../../build-root.js";import{getNode as I}from"../../../core/canvas/canvas.js";import{isSafeNodeId as O,nodeAgentDir as P}from"../../../core/canvas/paths.js";import{discoverHookRegistry as A}from"../../../core/command-hooks/discovery.js";import{EXEC_HOOK_TIMEOUT_MS as S}from"../../../core/command-hooks/transport/exec-invoke.js";import{buildAppProcessEnv as H}from"../../../core/runtime/spawn-env.js";import{compileLaunch as N}from"../../../core/spaces/mounts.js";import{appProcessSandboxed as M,launchAppProcess as R}from"../../../core/spaces/process.js";import{requireCurrentExecution as B}from"./broker-recovery.js";import{personId as C}from"../../../core/canvas/person.js";async function k(o,n,t){const i=o.params.id;if(i===void 0?o.principal.class!=="person":!O(i)||o.principal.class!=="broker"||o.principal.node!==i)throw new r(403,"permission_error","hook execution requires its current node, or the person for the global route");const e=i===void 0?null:await I(i);if(i!==void 0&&!e)throw new r(404,"not_found","node not found");if(i!==void 0&&await B(i,n),typeof t!="string"||!y(t))throw new r(400,"invalid_request","hook cwd must be absolute");if(!M())throw new r(400,"invalid_request","hook routes require the root launcher");let d;try{d=E(t)}catch(p){throw p.code==="ENOENT"?new r(400,"invalid_request","hook cwd does not exist"):p}if(e){if(e.isolated===!0){const a=P(i,e.grantee??"app:terminal"),s=_(a,d);if(s===".."||s.startsWith(`..${h}`)||s.startsWith(h))throw new r(403,"permission_error","hook cwd is outside the isolated node folder")}const p=await N({nodeId:i,app:o.principal.class==="broker"?o.principal.app:"app:terminal",sub:e.sub??C(),grantId:e.grant_id??null,scopes:e.scopes??null,isolated:e.isolated===!0,command:[],env:{},auditMissing:u(()=>{},"auditMissing")}),f=u(a=>{const s=_(a,d);return s===""||s!==".."&&!s.startsWith(`..${h}`)&&!y(s)},"within");if(!p.mounts.some(a=>f(a.source)))throw new r(403,"permission_error","hook cwd is outside the node mounts")}return{id:i,node:e,cwd:d,app:e&&o.principal.class==="broker"?o.principal.app:"app:terminal"}}u(k,"checkedContext");async function b(o,n){const t=A(await T(),o,n);if(!t.valid)throw new r(422,"hook_registry_invalid","the effective hook registry is invalid");return t}u(b,"currentRegistry");async function W(o){const{node:n,cwd:t}=await k(o,o.query.get("expected_execution_id"),o.query.get("cwd"));return{status:200,body:{hooks:(await b(t,n.profile_id??null)).hooks.filter(e=>e.plugin.scope==="user").map(e=>({id:e.id,plugin:e.plugin.name,target:e.target,phase:e.phase,op:e.op,description:e.description,effects:e.effects,declarationIndex:e.declarationIndex}))}}}u(W,"handleHookPlan");async function v(o){const n=o.body;if(!n||typeof n!="object"||Array.isArray(n))throw new r(400,"invalid_request","hook execution requires a JSON body");const t=n,{id:i,node:e,cwd:d,app:p}=await k(o,t.expected_execution_id,t.cwd);if(typeof t.hook_id!="string"||typeof t.request!="string")throw new r(400,"invalid_request","hook execution requires hook_id and request");const a=(await b(d,e?.profile_id??null)).hooks.find(c=>c.id===t.hook_id);if(!a)throw new r(404,"hook_not_registered","hook id is not registered for this cwd");const s=H(d,e?.profile_id??null,p),m=await R({...i===void 0?{}:{nodeId:i},app:p,command:[a.executable,"--crtr-hook-protocol","1"],cwd:d,pluginRoot:a.plugin.root,env:s,stdin:t.request,stdoutBytes:10*1024*1024,stderrBytes:64*1024});let l,w=!1;try{const c=await Promise.race([m.completion,new Promise(($,g)=>{l=setTimeout(()=>{w=!0,m.stop().then(()=>g(new r(504,"hook_timeout","hook executable timed out")),q=>g(q))},S)})]);if(w)throw new r(504,"hook_timeout","hook executable timed out");if(c.stdoutTruncated)throw new r(502,"hook_output_limit","hook stdout exceeded the 10 MiB cap");return{status:200,body:{stdout:c.stdout,status:c.exitCode,signal:c.signal}}}finally{l&&clearTimeout(l)}}u(v,"handleHookExec");const ee=[{method:"GET",pattern:"/v1/nodes/:id/hooks/plan",handler:W},{method:"POST",pattern:"/v1/nodes/:id/hooks/exec",handler:v},{method:"POST",pattern:"/v1/hooks/exec",handler:v}];export{ee as hookExecRoutes};
1
+ var x=Object.defineProperty;var u=(o,n)=>x(o,"name",{value:n,configurable:!0});import{realpathSync as E}from"node:fs";import{isAbsolute as y,relative as _,sep as h}from"node:path";import{ApiError as r}from"../../../api/index.js";import{coreHookCatalog as P}from"../../../build-root.js";import{getNode as T}from"../../../core/canvas/canvas.js";import{isSafeNodeId as A,nodeAgentDir as I}from"../../../core/canvas/paths.js";import{discoverHookRegistry as O}from"../../../core/command-hooks/discovery.js";import{EXEC_HOOK_TIMEOUT_MS as S}from"../../../core/command-hooks/transport/exec-invoke.js";import{buildAppProcessEnv as H}from"../../../core/runtime/spawn-env-rows.js";import{compileLaunch as N}from"../../../core/spaces/mounts.js";import{appProcessSandboxed as M,launchAppProcess as R}from"../../../core/spaces/process.js";import{requireCurrentExecution as B}from"./broker-recovery.js";import{actsAsPerson as C}from"../router.js";import{personId as W}from"../../../core/canvas/person.js";async function k(o,n,t){const i=o.params.id;if(i===void 0?!C(o.principal):!A(i)||o.principal.class!=="broker"||o.principal.node!==i)throw new r(403,"permission_error","hook execution requires its current node, or the person for the global route");const e=i===void 0?null:await T(i);if(i!==void 0&&!e)throw new r(404,"not_found","node not found");if(i!==void 0&&await B(i,n),typeof t!="string"||!y(t))throw new r(400,"invalid_request","hook cwd must be absolute");if(!M())throw new r(400,"invalid_request","hook routes require the root launcher");let d;try{d=E(t)}catch(p){throw p.code==="ENOENT"?new r(400,"invalid_request","hook cwd does not exist"):p}if(e){if(e.isolated===!0){const a=I(i,e.grantee??"app:terminal"),s=_(a,d);if(s===".."||s.startsWith(`..${h}`)||s.startsWith(h))throw new r(403,"permission_error","hook cwd is outside the isolated node folder")}const p=await N({nodeId:i,app:o.principal.class==="broker"?o.principal.app:"app:terminal",sub:e.sub??W(),grantId:e.grant_id??null,scopes:e.scopes??null,isolated:e.isolated===!0,command:[],env:{},auditMissing:u(()=>{},"auditMissing")}),f=u(a=>{const s=_(a,d);return s===""||s!==".."&&!s.startsWith(`..${h}`)&&!y(s)},"within");if(!p.mounts.some(a=>f(a.source)))throw new r(403,"permission_error","hook cwd is outside the node mounts")}return{id:i,node:e,cwd:d,app:e&&o.principal.class==="broker"?o.principal.app:"app:terminal"}}u(k,"checkedContext");async function b(o,n){const t=O(await P(),o,n);if(!t.valid)throw new r(422,"hook_registry_invalid","the effective hook registry is invalid");return t}u(b,"currentRegistry");async function $(o){const{node:n,cwd:t}=await k(o,o.query.get("expected_execution_id"),o.query.get("cwd"));return{status:200,body:{hooks:(await b(t,n.profile_id??null)).hooks.filter(e=>e.plugin.scope==="user").map(e=>({id:e.id,plugin:e.plugin.name,target:e.target,phase:e.phase,op:e.op,description:e.description,effects:e.effects,declarationIndex:e.declarationIndex}))}}}u($,"handleHookPlan");async function v(o){const n=o.body;if(!n||typeof n!="object"||Array.isArray(n))throw new r(400,"invalid_request","hook execution requires a JSON body");const t=n,{id:i,node:e,cwd:d,app:p}=await k(o,t.expected_execution_id,t.cwd);if(typeof t.hook_id!="string"||typeof t.request!="string")throw new r(400,"invalid_request","hook execution requires hook_id and request");const a=(await b(d,e?.profile_id??null)).hooks.find(c=>c.id===t.hook_id);if(!a)throw new r(404,"hook_not_registered","hook id is not registered for this cwd");const s=await H(d,e?.profile_id??null,p),m=await R({...i===void 0?{}:{nodeId:i},app:p,command:[a.executable,"--crtr-hook-protocol","1"],cwd:d,pluginRoot:a.plugin.root,env:s,stdin:t.request,stdoutBytes:10*1024*1024,stderrBytes:64*1024});let l,w=!1;try{const c=await Promise.race([m.completion,new Promise((D,g)=>{l=setTimeout(()=>{w=!0,m.stop().then(()=>g(new r(504,"hook_timeout","hook executable timed out")),q=>g(q))},S)})]);if(w)throw new r(504,"hook_timeout","hook executable timed out");if(c.stdoutTruncated)throw new r(502,"hook_output_limit","hook stdout exceeded the 10 MiB cap");return{status:200,body:{stdout:c.stdout,status:c.exitCode,signal:c.signal}}}finally{l&&clearTimeout(l)}}u(v,"handleHookExec");const te=[{method:"GET",pattern:"/v1/nodes/:id/hooks/plan",handler:$},{method:"POST",pattern:"/v1/nodes/:id/hooks/exec",handler:v},{method:"POST",pattern:"/v1/hooks/exec",handler:v}];export{te as hookExecRoutes};
@@ -1 +1 @@
1
- var P=Object.defineProperty;var o=(e,t)=>P(e,"name",{value:t,configurable:!0});import{statSync as N}from"node:fs";import{isAbsolute as T}from"node:path";import{ApiError as s}from"../../../api/index.js";import{readDaemonActionById as C}from"../../../core/canvas/daemon-actions.js";import{isSafeNodeId as I}from"../../../core/canvas/paths.js";import{getNode as b}from"../../../core/canvas/canvas.js";import{assertScope as $}from"../../../core/scopes.js";import{resolvePageComponents as g}from"../../../core/config.js";import{resolveHumanAction as D}from"../../../core/human-actions.js";import{HUMAN_ACTION_NAME_PATTERN as x}from"../../../core/human/action-binding.js";import{createPageRequest as J,getRequest as F}from"../../../core/human/requests.js";import{preparePageSource as M}from"../../../core/human/page-authoring.js";import{PageCompilerError as B}from"../../../core/human/page-errors.js";import{publishPage as G,validatePageTicketResponses as H}from"../../../core/human/tickets.js";import{cancelHumanTicket as L,resolvePageTicket as U}from"../../human/finish.js";import{inboxPageProjection as K}from"./inbox-page.js";import{appOf as X}from"../router.js";const z=1e3,q=200,m={placement:"inline",inbox:!0,reply:!1},v=["sessionName","askedBy","emittedAt","profileName","nodeId"];function c(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}o(c,"isPlainObject");function l(e,t,n){for(const r of Object.keys(e))if(!t.includes(r))throw new s(400,"invalid_request",`unexpected field in ${n}: ${r}`)}o(l,"requireNoExtraKeys");function i(e){return new s(400,"invalid_request",e)}o(i,"invalid");function w(e){if(!c(e))throw i("`page` must be an object");if(l(e,["dialect","source"],"`page`"),e.dialect!=="jsx"&&e.dialect!=="html")throw i('`page.dialect` must be "jsx" or "html"');if(typeof e.source!="string"||e.source.trim()==="")throw i("`page.source` must be the authored page text");return{dialect:e.dialect,source:e.source}}o(w,"validatePage");function S(e,t){const n=e??t;if(n!=="inline"&&n!=="panel")throw i('`delivery.placement` must be "inline" or "panel"');return n}o(S,"validatePlacement");function V(e,t){if(e===void 0)return m;if(!c(e))throw i("`delivery` must be an object");l(e,["placement","inbox","reply"],"`delivery`");const n=e.inbox??m.inbox,r=e.reply??m.reply;if(typeof n!="boolean")throw i("`delivery.inbox` must be boolean");if(typeof r!="boolean")throw i("`delivery.reply` must be boolean");if(r&&t===null)throw i("`delivery.reply` requires `requester_node_id`");return{placement:S(e.placement,m.placement),inbox:n,reply:r}}o(V,"validateCreateDelivery");function Y(e,t){if(e===void 0)return t;if(!c(e))throw i("`delivery` must be an object");return l(e,["placement"],"`delivery`"),{...t,placement:S(e.placement,t.placement)}}o(Y,"validateReplaceDelivery");function Q(e){if(e===void 0)return;if(!c(e))throw i("`source` must be an object");l(e,v,"`source`");const t={};for(const n of v){const r=e[n];if(r!==void 0){if(typeof r!="string"||r==="")throw i(`\`source.${n}\` must be a nonempty string`);t[n]=r}}return t}o(Q,"validateSource");function W(e){if(typeof e!="string"||e==="")throw i("`creator_cwd` is required");if(!T(e))throw i("`creator_cwd` must be an absolute path");try{if(!N(e).isDirectory())throw i(`\`creator_cwd\` is not a directory: ${e}`)}catch(t){throw t instanceof s?t:i(`\`creator_cwd\` does not exist: ${e}`)}return e}o(W,"validateCreatorCwd");function Z(e){if(e!==void 0){if(!c(e))throw i("`action` must be an object");if(l(e,["name","payload"],"`action`"),typeof e.name!="string"||!x.test(e.name))throw i("`action.name` must be a declared action name");return{name:e.name,payload:e.payload??null}}}o(Z,"validateActionSelection");function ee(e){if(e==null)return null;if(typeof e!="string"||!I(e))throw i("`requester_node_id` must be a safe node id or null");return e}o(ee,"validateRequesterNodeId");function te(e){if(!c(e))throw i("request body is required");l(e,["page","delivery","source","requester_node_id","creator_cwd","action"],"the request body");const t=Q(e.source),n=ee(e.requester_node_id),r=Z(e.action);return{page:w(e.page),delivery:V(e.delivery,n),...t===void 0?{}:{source:t},requesterNodeId:n,creatorCwd:W(e.creator_cwd),...r===void 0?{}:{action:r}}}o(te,"validateCreateRequest");function ne(e,t){if(!c(e))throw i("request body is required");return l(e,["page","delivery"],"the request body"),{page:w(e.page),delivery:Y(e.delivery,t)}}o(ne,"validateReplaceRequest");function _(e,t,n){if(e!==void 0){if(typeof e!="string"||e.trim()===""||e.length>n)throw i(`\`${t}\` must be nonempty after trim and at most ${n} characters`);return e}}o(_,"optionalBoundedString");function re(e){if(!c(e))throw i("request body is required");if(l(e,["responses","actor"],"the request body"),!c(e.responses))throw i("`responses` must be an object");const t=_(e.actor,"actor",q);return{responses:e.responses,...t===void 0?{}:{actor:t}}}o(re,"validateRespondRequest");function oe(e){if(e===void 0)return{};if(!c(e))throw i("request body must be an object");l(e,["reason","actor"],"the request body");const t=_(e.reason,"reason",z),n=_(e.actor,"actor",q);return{...t===void 0?{}:{reason:t},...n===void 0?{}:{actor:n}}}o(oe,"validateSettleRequest");function h(e){const t=e.params.request_id;if(typeof t!="string"||t==="")throw i("request id is required");return t}o(h,"requestId");async function f(e){const t=await F(e);if(t===null||t.kind!=="page")throw new s(404,"not_found","no request with that id");return t}o(f,"requirePageRequest");function A(e){try{const t=JSON.parse(e.specification_json);if(!c(t))throw new Error("not an object");return t}catch{throw new s(500,"internal","the request specification could not be read")}}o(A,"manifest");function ie(e){if(e.result_json===null)return null;try{return JSON.parse(e.result_json)}catch{throw new s(500,"internal","the request result could not be read")}}o(ie,"terminalResult");function ae(e){if(e.action_json===null)return null;try{const t=JSON.parse(e.action_json);if(t.schema!=="crtr.human-action-binding/v1"||typeof t.name!="string")throw new Error("invalid action");return t}catch{throw new s(500,"internal","the request action could not be read")}}o(ae,"frozenAction");function se(e){return e.state!=="settled"?"pending":e.outcome==="answered"?"answered":e.outcome==="dismissed"?"dismissed":"canceled"}o(se,"requestState");async function de(e){if(e.action_delivery_id===null)return{state:"none",attempt:0};const t=await C(e.action_delivery_id);if(t===null)return{state:"none",attempt:0};const n=t.state==="acknowledged"?"accepted":t.state==="permanent_failed"?"permanent_failed":t.state==="handed_off"?"running":"pending",r={state:n,attempt:t.attempts};return n==="pending"&&t.nextAttemptAt!==null&&(r.next_attempt_at=new Date(t.nextAttemptAt).toISOString()),t.acknowledgedAt!==null&&(r.accepted_at=new Date(t.acknowledgedAt).toISOString()),t.failedAt!==null&&(r.permanent_failed_at=new Date(t.failedAt).toISOString()),t.lastFailure!==null&&(r.last_failure=t.lastFailure),r}o(de,"deliveryDetail");async function y(e){const t=A(e),n=ie(e),r=se(e),a={request_id:e.request_id,inbox_ticket_id:e.inbox_ticket_id,request_dir:e.request_dir,kind:e.kind,state:r,title:e.title,source:t.source??{},emitted_at:t.source?.emittedAt??e.created_at};e.subtitle!==null&&(a.subtitle=e.subtitle),e.kind==="page"&&(a.page=await K(e)),n!==null&&(a.settled_at=e.settled_at??e.updated_at,n.kind==="page"&&r==="answered"&&(a.responses=n.responses),n.kind==="canceled"&&n.reason!==void 0&&(a.reason=n.reason),n.kind==="page"&&n.actor!==void 0&&(a.actor=n.actor),n.kind==="canceled"&&n.actor!==void 0&&(a.actor=n.actor));const u=ae(e);return u!==null&&(a.action={name:u.name,payload:u.payload},a.delivery=await de(e)),a}o(y,"requestToDTO");function k(e,t,n,r){try{return M({dialect:e,document:t,source:n,delivery:r,productKinds:g()})}catch(a){throw a instanceof B?a:i(a instanceof Error?a.message:"the page could not be prepared")}}o(k,"prepareOrReject");async function ce(e){const t=te(e.body),n=t.requesterNodeId??t.source?.nodeId;if(n!=null){const d=await b(n);d!==null&&$(d,"crtr:act")}let r;if(t.action!==void 0){const d=D(t.action.name,t.creatorCwd);if("kind"in d){const E=d.kind==="unknown_action"?`no action named ${d.name} is declared from ${t.creatorCwd}`:`${d.name} is declared in ${d.configOrigin} but unusable: ${d.reason}`;throw new s(400,d.kind,E)}const p=n==null?null:await b(n);if(n&&!p)throw new s(404,"not_found","action requester node not found");if(p&&e.principal.class!=="person"&&p.grantee!==X(e.principal))throw new s(403,"permission_error","action requester belongs to another app");r=JSON.stringify({schema:"crtr.human-action-binding/v1",name:t.action.name,payload:t.action.payload,creatorCwd:t.creatorCwd,configOrigin:d.configOrigin,argv:d.argv,cwd:d.cwd,app:p?.grantee??(e.principal.class==="broker"?e.principal.app:"app:terminal")})}const a=k(t.page.dialect,t.page.source,t.source,t.delivery),u=await J({prepared:a,requester_node_id:t.requesterNodeId,...r===void 0?{}:{action_json:r}});return{status:201,body:{request_id:u.request_id,request_dir:u.request_dir,state:"pending",...t.action===void 0?{}:{action:{name:t.action.name}},delivery_state:"none"}}}o(ce,"handleCreate");async function ue(e){return{status:200,body:await y(await f(h(e)))}}o(ue,"handleGet");async function le(e){const t=await f(h(e));if(t.state==="settled")throw new s(409,"already_settled","the request already has a terminal result");const n=A(t),r=ne(e.body,n.delivery),a=k(r.page.dialect,r.page.source,n.source,r.delivery);try{await G(t.request_dir,a)}catch(u){throw u instanceof Error&&u.message===`page request is not open: ${t.request_id}`?new s(409,"already_settled","the request already has a terminal result"):u}return{status:200,body:await y(await f(t.request_id))}}o(le,"handleReplace");function O(e){return e instanceof s?e:e instanceof Error&&e.message.includes("unknown page request")?new s(404,"not_found","no request with that id"):e instanceof Error&&e.message==="page_has_no_response_lifecycle"?new s(409,"not_settleable","this request carries no response lifecycle"):new s(500,"internal","the request could not be settled")}o(O,"settlementError");function R(e){if(e.state==="settled")throw new s(409,"already_settled","the request already has a terminal result");if(!e.settleable)throw new s(409,"not_settleable","this request carries no response lifecycle")}o(R,"requireSettleable");async function fe(e){const t=await f(h(e)),n=re(e.body);R(t);try{await H(t.request_dir,n.responses)}catch(r){throw i(r instanceof Error?r.message:"the response map is invalid")}try{await U(t.request_id,n.responses,n.actor===void 0?{}:{actor:n.actor})}catch(r){throw O(r)}return{status:200,body:await y(await f(t.request_id))}}o(fe,"handleRespond");async function j(e,t){const n=await f(h(e)),r=oe(e.body);R(n);try{await L(n.request_id,{...r,disposition:t})}catch(a){throw O(a)}return{status:200,body:await y(await f(n.request_id))}}o(j,"settle");function pe(){return{status:200,body:{components:[...g()]}}}o(pe,"handleComponents");const Te=[{method:"GET",pattern:"/v1/human/components",handler:o(()=>pe(),"handler")},{method:"POST",pattern:"/v1/human/requests",scope:"crtr:act",handler:ce},{method:"GET",pattern:"/v1/human/requests/:request_id",handler:ue},{method:"POST",pattern:"/v1/human/requests/:request_id/replace",handler:le},{method:"POST",pattern:"/v1/human/requests/:request_id/respond",handler:fe},{method:"POST",pattern:"/v1/human/requests/:request_id/dismiss",handler:o(e=>j(e,"dismissed"),"handler")},{method:"POST",pattern:"/v1/human/requests/:request_id/cancel",handler:o(e=>j(e,"canceled"),"handler")}];export{Te as humanRequestRoutes};
1
+ var P=Object.defineProperty;var o=(e,t)=>P(e,"name",{value:t,configurable:!0});import{statSync as N}from"node:fs";import{isAbsolute as T}from"node:path";import{ApiError as s}from"../../../api/index.js";import{readDaemonActionById as C}from"../../../core/canvas/daemon-actions.js";import{isSafeNodeId as I}from"../../../core/canvas/paths.js";import{getNode as b}from"../../../core/canvas/canvas.js";import{assertScope as $}from"../../../core/scopes.js";import{resolvePageComponents as g}from"../../../core/config.js";import{resolveHumanAction as D}from"../../../core/human-actions.js";import{HUMAN_ACTION_NAME_PATTERN as x}from"../../../core/human/action-binding.js";import{createPageRequest as J,getRequest as F}from"../../../core/human/requests.js";import{preparePageSource as M}from"../../../core/human/page-authoring.js";import{PageCompilerError as B}from"../../../core/human/page-errors.js";import{publishPage as G,validatePageTicketResponses as H}from"../../../core/human/tickets.js";import{cancelHumanTicket as L,resolvePageTicket as U}from"../../human/finish.js";import{inboxPageProjection as K}from"./inbox-page.js";import{actsAsPerson as X,appOf as z}from"../router.js";const V=1e3,q=200,m={placement:"inline",inbox:!0,reply:!1},v=["sessionName","askedBy","emittedAt","profileName","nodeId"];function c(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}o(c,"isPlainObject");function l(e,t,n){for(const r of Object.keys(e))if(!t.includes(r))throw new s(400,"invalid_request",`unexpected field in ${n}: ${r}`)}o(l,"requireNoExtraKeys");function i(e){return new s(400,"invalid_request",e)}o(i,"invalid");function w(e){if(!c(e))throw i("`page` must be an object");if(l(e,["dialect","source"],"`page`"),e.dialect!=="jsx"&&e.dialect!=="html")throw i('`page.dialect` must be "jsx" or "html"');if(typeof e.source!="string"||e.source.trim()==="")throw i("`page.source` must be the authored page text");return{dialect:e.dialect,source:e.source}}o(w,"validatePage");function A(e,t){const n=e??t;if(n!=="inline"&&n!=="panel")throw i('`delivery.placement` must be "inline" or "panel"');return n}o(A,"validatePlacement");function Y(e,t){if(e===void 0)return m;if(!c(e))throw i("`delivery` must be an object");l(e,["placement","inbox","reply"],"`delivery`");const n=e.inbox??m.inbox,r=e.reply??m.reply;if(typeof n!="boolean")throw i("`delivery.inbox` must be boolean");if(typeof r!="boolean")throw i("`delivery.reply` must be boolean");if(r&&t===null)throw i("`delivery.reply` requires `requester_node_id`");return{placement:A(e.placement,m.placement),inbox:n,reply:r}}o(Y,"validateCreateDelivery");function Q(e,t){if(e===void 0)return t;if(!c(e))throw i("`delivery` must be an object");return l(e,["placement"],"`delivery`"),{...t,placement:A(e.placement,t.placement)}}o(Q,"validateReplaceDelivery");function W(e){if(e===void 0)return;if(!c(e))throw i("`source` must be an object");l(e,v,"`source`");const t={};for(const n of v){const r=e[n];if(r!==void 0){if(typeof r!="string"||r==="")throw i(`\`source.${n}\` must be a nonempty string`);t[n]=r}}return t}o(W,"validateSource");function Z(e){if(typeof e!="string"||e==="")throw i("`creator_cwd` is required");if(!T(e))throw i("`creator_cwd` must be an absolute path");try{if(!N(e).isDirectory())throw i(`\`creator_cwd\` is not a directory: ${e}`)}catch(t){throw t instanceof s?t:i(`\`creator_cwd\` does not exist: ${e}`)}return e}o(Z,"validateCreatorCwd");function ee(e){if(e!==void 0){if(!c(e))throw i("`action` must be an object");if(l(e,["name","payload"],"`action`"),typeof e.name!="string"||!x.test(e.name))throw i("`action.name` must be a declared action name");return{name:e.name,payload:e.payload??null}}}o(ee,"validateActionSelection");function te(e){if(e==null)return null;if(typeof e!="string"||!I(e))throw i("`requester_node_id` must be a safe node id or null");return e}o(te,"validateRequesterNodeId");function ne(e){if(!c(e))throw i("request body is required");l(e,["page","delivery","source","requester_node_id","creator_cwd","action"],"the request body");const t=W(e.source),n=te(e.requester_node_id),r=ee(e.action);return{page:w(e.page),delivery:Y(e.delivery,n),...t===void 0?{}:{source:t},requesterNodeId:n,creatorCwd:Z(e.creator_cwd),...r===void 0?{}:{action:r}}}o(ne,"validateCreateRequest");function re(e,t){if(!c(e))throw i("request body is required");return l(e,["page","delivery"],"the request body"),{page:w(e.page),delivery:Q(e.delivery,t)}}o(re,"validateReplaceRequest");function _(e,t,n){if(e!==void 0){if(typeof e!="string"||e.trim()===""||e.length>n)throw i(`\`${t}\` must be nonempty after trim and at most ${n} characters`);return e}}o(_,"optionalBoundedString");function oe(e){if(!c(e))throw i("request body is required");if(l(e,["responses","actor"],"the request body"),!c(e.responses))throw i("`responses` must be an object");const t=_(e.actor,"actor",q);return{responses:e.responses,...t===void 0?{}:{actor:t}}}o(oe,"validateRespondRequest");function ie(e){if(e===void 0)return{};if(!c(e))throw i("request body must be an object");l(e,["reason","actor"],"the request body");const t=_(e.reason,"reason",V),n=_(e.actor,"actor",q);return{...t===void 0?{}:{reason:t},...n===void 0?{}:{actor:n}}}o(ie,"validateSettleRequest");function h(e){const t=e.params.request_id;if(typeof t!="string"||t==="")throw i("request id is required");return t}o(h,"requestId");async function f(e){const t=await F(e);if(t===null||t.kind!=="page")throw new s(404,"not_found","no request with that id");return t}o(f,"requirePageRequest");function S(e){try{const t=JSON.parse(e.specification_json);if(!c(t))throw new Error("not an object");return t}catch{throw new s(500,"internal","the request specification could not be read")}}o(S,"manifest");function ae(e){if(e.result_json===null)return null;try{return JSON.parse(e.result_json)}catch{throw new s(500,"internal","the request result could not be read")}}o(ae,"terminalResult");function se(e){if(e.action_json===null)return null;try{const t=JSON.parse(e.action_json);if(t.schema!=="crtr.human-action-binding/v1"||typeof t.name!="string")throw new Error("invalid action");return t}catch{throw new s(500,"internal","the request action could not be read")}}o(se,"frozenAction");function de(e){return e.state!=="settled"?"pending":e.outcome==="answered"?"answered":e.outcome==="dismissed"?"dismissed":"canceled"}o(de,"requestState");async function ce(e){if(e.action_delivery_id===null)return{state:"none",attempt:0};const t=await C(e.action_delivery_id);if(t===null)return{state:"none",attempt:0};const n=t.state==="acknowledged"?"accepted":t.state==="permanent_failed"?"permanent_failed":t.state==="handed_off"?"running":"pending",r={state:n,attempt:t.attempts};return n==="pending"&&t.nextAttemptAt!==null&&(r.next_attempt_at=new Date(t.nextAttemptAt).toISOString()),t.acknowledgedAt!==null&&(r.accepted_at=new Date(t.acknowledgedAt).toISOString()),t.failedAt!==null&&(r.permanent_failed_at=new Date(t.failedAt).toISOString()),t.lastFailure!==null&&(r.last_failure=t.lastFailure),r}o(ce,"deliveryDetail");async function y(e){const t=S(e),n=ae(e),r=de(e),a={request_id:e.request_id,inbox_ticket_id:e.inbox_ticket_id,request_dir:e.request_dir,kind:e.kind,state:r,title:e.title,source:t.source??{},emitted_at:t.source?.emittedAt??e.created_at};e.subtitle!==null&&(a.subtitle=e.subtitle),e.kind==="page"&&(a.page=await K(e)),n!==null&&(a.settled_at=e.settled_at??e.updated_at,n.kind==="page"&&r==="answered"&&(a.responses=n.responses),n.kind==="canceled"&&n.reason!==void 0&&(a.reason=n.reason),n.kind==="page"&&n.actor!==void 0&&(a.actor=n.actor),n.kind==="canceled"&&n.actor!==void 0&&(a.actor=n.actor));const u=se(e);return u!==null&&(a.action={name:u.name,payload:u.payload},a.delivery=await ce(e)),a}o(y,"requestToDTO");function k(e,t,n,r){try{return M({dialect:e,document:t,source:n,delivery:r,productKinds:g()})}catch(a){throw a instanceof B?a:i(a instanceof Error?a.message:"the page could not be prepared")}}o(k,"prepareOrReject");async function ue(e){const t=ne(e.body),n=t.requesterNodeId??t.source?.nodeId;if(n!=null){const d=await b(n);d!==null&&$(d,"crtr:act")}let r;if(t.action!==void 0){const d=D(t.action.name,t.creatorCwd);if("kind"in d){const E=d.kind==="unknown_action"?`no action named ${d.name} is declared from ${t.creatorCwd}`:`${d.name} is declared in ${d.configOrigin} but unusable: ${d.reason}`;throw new s(400,d.kind,E)}const p=n==null?null:await b(n);if(n&&!p)throw new s(404,"not_found","action requester node not found");if(p&&!X(e.principal)&&p.grantee!==z(e.principal))throw new s(403,"permission_error","action requester belongs to another app");r=JSON.stringify({schema:"crtr.human-action-binding/v1",name:t.action.name,payload:t.action.payload,creatorCwd:t.creatorCwd,configOrigin:d.configOrigin,argv:d.argv,cwd:d.cwd,app:p?.grantee??(e.principal.class==="broker"?e.principal.app:"app:terminal")})}const a=k(t.page.dialect,t.page.source,t.source,t.delivery),u=await J({prepared:a,requester_node_id:t.requesterNodeId,...r===void 0?{}:{action_json:r}});return{status:201,body:{request_id:u.request_id,request_dir:u.request_dir,state:"pending",...t.action===void 0?{}:{action:{name:t.action.name}},delivery_state:"none"}}}o(ue,"handleCreate");async function le(e){return{status:200,body:await y(await f(h(e)))}}o(le,"handleGet");async function fe(e){const t=await f(h(e));if(t.state==="settled")throw new s(409,"already_settled","the request already has a terminal result");const n=S(t),r=re(e.body,n.delivery),a=k(r.page.dialect,r.page.source,n.source,r.delivery);try{await G(t.request_dir,a)}catch(u){throw u instanceof Error&&u.message===`page request is not open: ${t.request_id}`?new s(409,"already_settled","the request already has a terminal result"):u}return{status:200,body:await y(await f(t.request_id))}}o(fe,"handleReplace");function O(e){return e instanceof s?e:e instanceof Error&&e.message.includes("unknown page request")?new s(404,"not_found","no request with that id"):e instanceof Error&&e.message==="page_has_no_response_lifecycle"?new s(409,"not_settleable","this request carries no response lifecycle"):new s(500,"internal","the request could not be settled")}o(O,"settlementError");function R(e){if(e.state==="settled")throw new s(409,"already_settled","the request already has a terminal result");if(!e.settleable)throw new s(409,"not_settleable","this request carries no response lifecycle")}o(R,"requireSettleable");async function pe(e){const t=await f(h(e)),n=oe(e.body);R(t);try{await H(t.request_dir,n.responses)}catch(r){throw i(r instanceof Error?r.message:"the response map is invalid")}try{await U(t.request_id,n.responses,n.actor===void 0?{}:{actor:n.actor})}catch(r){throw O(r)}return{status:200,body:await y(await f(t.request_id))}}o(pe,"handleRespond");async function j(e,t){const n=await f(h(e)),r=ie(e.body);R(n);try{await L(n.request_id,{...r,disposition:t})}catch(a){throw O(a)}return{status:200,body:await y(await f(n.request_id))}}o(j,"settle");function me(){return{status:200,body:{components:[...g()]}}}o(me,"handleComponents");const Ce=[{method:"GET",pattern:"/v1/human/components",handler:o(()=>me(),"handler")},{method:"POST",pattern:"/v1/human/requests",scope:"crtr:act",handler:ue},{method:"GET",pattern:"/v1/human/requests/:request_id",handler:le},{method:"POST",pattern:"/v1/human/requests/:request_id/replace",handler:fe},{method:"POST",pattern:"/v1/human/requests/:request_id/respond",handler:pe},{method:"POST",pattern:"/v1/human/requests/:request_id/dismiss",handler:o(e=>j(e,"dismissed"),"handler")},{method:"POST",pattern:"/v1/human/requests/:request_id/cancel",handler:o(e=>j(e,"canceled"),"handler")}];export{Ce as humanRequestRoutes};
@@ -1 +1 @@
1
- var q=Object.defineProperty;var o=(e,i)=>q(e,"name",{value:i,configurable:!0});import{ApiError as t}from"../../../api/index.js";import{inboxPageProjection as T}from"./inbox-page.js";import{getRequestByInboxTicket as p}from"../../../core/human/requests.js";import{getNode as b}from"../../../core/canvas/canvas.js";import{canReadNode as g}from"../router.js";import{parsePage as A}from"../../../core/human/page.js";import{scanInbox as I,scanPageHistory as P}from"../../../core/human/scan.js";import{readPageTicket as h,readTicketResult as k,selectedPageDir as E,validatePageTicketResponses as x,writePageProgress as R}from"../../../core/human/tickets.js";import{describePageAnswer as S}from"../../../core/human/answer.js";import{cancelHumanTicket as O,resolvePageTicket as N}from"../../human/finish.js";import{filterAsync as y}from"../../../core/async-array.js";const C="Canceled from the inbox.",D=1e3,j=/^[a-f0-9]{64}$/;async function c(e){const i=await p(e);return i===null?null:{ticketId:i.request_id,dir:i.request_dir,kind:i.kind}}o(c,"resolveTicket");function l(e){const i=e.ticket_id;if(i===void 0||!j.test(i))throw new t(400,"invalid_request","invalid ticket id");return i}o(l,"requireTicketId");function u(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}o(u,"isPlainObject");function f(e,i){for(const n of Object.keys(e))if(!i.includes(n))throw new t(400,"invalid_request",`unexpected field: ${n}`)}o(f,"requireNoExtraKeys");function G(e){if(!u(e))throw new t(400,"invalid_request","respond body is required");if(f(e,["responses"]),!u(e.responses))throw new t(400,"invalid_request","`responses` must be an object");return{responses:e.responses}}o(G,"validateRespondRequest");function L(e){if(!u(e))throw new t(400,"invalid_request","progress body is required");if(f(e,["responses"]),!u(e.responses))throw new t(400,"invalid_request","`responses` must be an object");return{responses:e.responses}}o(L,"validateProgressRequest");function H(e){if(e===void 0)return{};if(!u(e))throw new t(400,"invalid_request","cancel body must be an object");if(f(e,["reason"]),e.reason===void 0)return{};const i=e.reason;if(typeof i!="string")throw new t(400,"invalid_request","`reason` must be a string");if(i.trim()===""||i.length>D)throw new t(400,"invalid_request","`reason` must be nonempty after trim and at most 1000 characters");return{reason:i}}o(H,"validateCancelRequest");const K=[/^slot ".*?" /,/^response names unknown or non-response-bearing slot id/,/^final page response is missing response-bearing slot id/];function _(e){return e instanceof Error&&K.some(i=>i.test(e.message))}o(_,"isSlotValidationError");function v(e){const i={request_id:e.id,request_dir:e.dir,ticket_id:e.inboxTicketId,kind:"page",title:e.title,placement:e.placement,dialect:e.dialect,steps:e.steps,emitted_at:e.emittedAt,source:e.source,slot_kinds:e.slotKinds,inbox:e.inbox,awaits_response:e.awaitsResponse,state:e.state};return e.subtitle!==""&&(i.subtitle=e.subtitle),e.answerDigest!==void 0&&(i.answer_digest=e.answerDigest),e.recommendedOptions.length>0&&(i.recommended_options=e.recommendedOptions.map(n=>({slot_id:n.slotId,option_id:n.optionId,label:n.label}))),e.fastAction!==void 0&&(i.fast_action={kind:e.fastAction.kind,responses:e.fastAction.responses}),i}o(v,"pageSummaryToDTO");async function $(e){return{status:200,body:{tickets:(await y(await I(),async n=>{if(e.principal.class==="person")return!0;const s=await p(n.inboxTicketId),r=s?.requester_node_id?await b(s.requester_node_id):null;return r!==null&&await g(e.principal,r.grantee??"app:terminal",r.node_id)})).map(n=>{if(n.kind==="page")return v(n);if(n.kind==="review")return{request_id:n.id,request_dir:n.dir,ticket_id:n.inboxTicketId,kind:"review",title:n.title,subtitle:n.subtitle,blocked_since:n.blockedSince,source:n.source};throw new Error(`unknown ticket kind: ${n.kind}`)}).sort((n,s)=>{const r=o(d=>d.kind==="page"?d.emitted_at:d.blocked_since,"pageTime"),a=Date.parse(r(s))-Date.parse(r(n));return a!==0?a:n.ticket_id.localeCompare(s.ticket_id)})}}}o($,"handleList");const V=new Set(["pending","resolved","canceled","passive"]);async function z(e){const i=e.query.get("node_id");if(i==="")throw new t(400,"invalid_request","node_id must be nonempty when present");const n=e.query.get("state");if(n!==null&&!V.has(n))throw new t(400,"invalid_request","state must be one of pending, resolved, canceled, passive");const s=(await P(i??void 0)).filter(d=>n===null||d.state===n);return{status:200,body:{tickets:(await y(s,async d=>{if(e.principal.class==="person")return!0;const m=await p(d.inboxTicketId),w=m?.requester_node_id?await b(m.requester_node_id):null;return w!==null&&await g(e.principal,w.grantee??"app:terminal",w.node_id)})).map(v)}}}o(z,"handleHistory");async function B(e){const i=l(e.params),n=await c(i);if(n===null)throw new t(404,"ticket_not_found","no matching ticket");if(n.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets have no page read operation");const s=await p(i);if(s===null||s.kind!=="page")throw new t(404,"ticket_not_found","no matching ticket");try{return{status:200,body:await T(s)}}catch{throw new t(500,"internal","the ticket could not be read")}}o(B,"handleGetPage");async function F(e){const i=l(e.params),n=G(e.body),s=await c(i);if(s===null)throw new t(404,"ticket_not_found","no matching ticket");if(s.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets submit through their own path");if((await h(s.dir)).state==="passive")throw new t(409,"page_has_no_response_lifecycle","this page has no response lifecycle");const r=await k(s.dir);if(r!==null&&r.kind!=="canceled")throw new t(409,"ticket_already_resolved","ticket was already resolved");if(r?.kind==="canceled")throw new t(409,"ticket_canceled","canceled tickets cannot be answered");try{await x(s.dir,n.responses)}catch(a){throw _(a)?new t(400,"invalid_ticket_response",a.message):new t(500,"internal","validation failed")}try{const a=await N(s.ticketId,n.responses);if(a.kind!=="page")throw new t(409,"ticket_kind_mismatch","unexpected result kind");return{status:200,body:a}}catch(a){throw a instanceof t?a:_(a)?new t(400,"invalid_ticket_response",a.message):new t(500,"internal","the ticket could not be finalized")}}o(F,"handleRespond");async function M(e){const i=l(e.params),n=L(e.body),s=await c(i);if(s===null)throw new t(404,"ticket_not_found","no matching ticket");if(s.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets do not support progress autosave");if((await h(s.dir)).state==="passive")throw new t(409,"page_has_no_response_lifecycle","this page has no response lifecycle");const r=await k(s.dir);if(r!==null&&r.kind!=="canceled")throw new t(409,"ticket_already_resolved","ticket was already resolved");try{return await R(s.dir,n.responses),{status:200,body:{}}}catch(a){throw _(a)?new t(400,"invalid_ticket_response",a.message):new t(500,"internal","progress could not be saved")}}o(M,"handleProgress");async function U(e){const i=l(e.params),n=H(e.body),s=await c(i);if(s===null)throw new t(404,"ticket_not_found","no matching ticket");if(s.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets have their own cancel path");if((await h(s.dir)).state==="passive")throw new t(409,"page_has_no_response_lifecycle","this page has no response lifecycle");try{const r=await O(s.ticketId,{reason:n.reason??C,actor:"human",disposition:"dismissed"});if(r.outcome==="already_settled"||r.result.kind!=="canceled")throw new t(409,"ticket_already_resolved","ticket was already resolved");return{status:200,body:r.result}}catch(r){throw r instanceof t?r:new t(500,"internal","the ticket could not be canceled")}}o(U,"handleCancel");async function X(e){const i=l(e.params),n=await c(i);if(n===null)throw new t(404,"ticket_not_found","no matching ticket");if(n.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets fetch responses through their own path");const s=await k(n.dir);if(s===null)throw new t(404,"ticket_pending","ticket has no response yet");if(s.kind==="canceled")throw new t(404,"ticket_canceled","canceled tickets have no response");if(s.kind!=="page")throw new t(409,"ticket_kind_mismatch","unexpected result kind");try{const r=A(await E(n.dir));return{status:200,body:{...s,answer:S(r,s.responses,{completedAt:s.completedAt})}}}catch{throw new t(500,"internal","the ticket answer could not be read")}}o(X,"handleGetResponse");const ae=[{method:"GET",pattern:"/v1/human/inbox",handler:$},{method:"GET",pattern:"/v1/human/inbox/history",handler:z},{method:"GET",pattern:"/v1/human/inbox/:ticket_id",handler:B},{method:"GET",pattern:"/v1/human/inbox/:ticket_id/response",handler:X},{method:"POST",pattern:"/v1/human/inbox/:ticket_id/respond",handler:F},{method:"POST",pattern:"/v1/human/inbox/:ticket_id/progress",handler:M},{method:"POST",pattern:"/v1/human/inbox/:ticket_id/cancel",handler:U}];export{ae as inboxRoutes,l as requireTicketId,c as resolveTicket};
1
+ var T=Object.defineProperty;var o=(e,i)=>T(e,"name",{value:i,configurable:!0});import{ApiError as t}from"../../../api/index.js";import{inboxPageProjection as A}from"./inbox-page.js";import{getRequestByInboxTicket as p}from"../../../core/human/requests.js";import{getNode as b}from"../../../core/canvas/canvas.js";import{actsAsPerson as g,canReadNode as y}from"../router.js";import{parsePage as I}from"../../../core/human/page.js";import{scanInbox as P,scanPageHistory as E}from"../../../core/human/scan.js";import{readPageTicket as h,readTicketResult as k,selectedPageDir as x,validatePageTicketResponses as R,writePageProgress as S}from"../../../core/human/tickets.js";import{describePageAnswer as O}from"../../../core/human/answer.js";import{cancelHumanTicket as N,resolvePageTicket as C}from"../../human/finish.js";import{filterAsync as v}from"../../../core/async-array.js";const D="Canceled from the inbox.",j=1e3,G=/^[a-f0-9]{64}$/;async function c(e){const i=await p(e);return i===null?null:{ticketId:i.request_id,dir:i.request_dir,kind:i.kind}}o(c,"resolveTicket");function l(e){const i=e.ticket_id;if(i===void 0||!G.test(i))throw new t(400,"invalid_request","invalid ticket id");return i}o(l,"requireTicketId");function u(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}o(u,"isPlainObject");function f(e,i){for(const n of Object.keys(e))if(!i.includes(n))throw new t(400,"invalid_request",`unexpected field: ${n}`)}o(f,"requireNoExtraKeys");function L(e){if(!u(e))throw new t(400,"invalid_request","respond body is required");if(f(e,["responses"]),!u(e.responses))throw new t(400,"invalid_request","`responses` must be an object");return{responses:e.responses}}o(L,"validateRespondRequest");function H(e){if(!u(e))throw new t(400,"invalid_request","progress body is required");if(f(e,["responses"]),!u(e.responses))throw new t(400,"invalid_request","`responses` must be an object");return{responses:e.responses}}o(H,"validateProgressRequest");function K(e){if(e===void 0)return{};if(!u(e))throw new t(400,"invalid_request","cancel body must be an object");if(f(e,["reason"]),e.reason===void 0)return{};const i=e.reason;if(typeof i!="string")throw new t(400,"invalid_request","`reason` must be a string");if(i.trim()===""||i.length>j)throw new t(400,"invalid_request","`reason` must be nonempty after trim and at most 1000 characters");return{reason:i}}o(K,"validateCancelRequest");const $=[/^slot ".*?" /,/^response names unknown or non-response-bearing slot id/,/^final page response is missing response-bearing slot id/];function _(e){return e instanceof Error&&$.some(i=>i.test(e.message))}o(_,"isSlotValidationError");function q(e){const i={request_id:e.id,request_dir:e.dir,ticket_id:e.inboxTicketId,kind:"page",title:e.title,placement:e.placement,dialect:e.dialect,steps:e.steps,emitted_at:e.emittedAt,source:e.source,slot_kinds:e.slotKinds,inbox:e.inbox,awaits_response:e.awaitsResponse,state:e.state};return e.subtitle!==""&&(i.subtitle=e.subtitle),e.answerDigest!==void 0&&(i.answer_digest=e.answerDigest),e.recommendedOptions.length>0&&(i.recommended_options=e.recommendedOptions.map(n=>({slot_id:n.slotId,option_id:n.optionId,label:n.label}))),e.fastAction!==void 0&&(i.fast_action={kind:e.fastAction.kind,responses:e.fastAction.responses}),i}o(q,"pageSummaryToDTO");async function V(e){return{status:200,body:{tickets:(await v(await P(),async n=>{if(g(e.principal))return!0;const s=await p(n.inboxTicketId),r=s?.requester_node_id?await b(s.requester_node_id):null;return r!==null&&await y(e.principal,r.grantee??"app:terminal",r.node_id)})).map(n=>{if(n.kind==="page")return q(n);if(n.kind==="review")return{request_id:n.id,request_dir:n.dir,ticket_id:n.inboxTicketId,kind:"review",title:n.title,subtitle:n.subtitle,blocked_since:n.blockedSince,source:n.source};throw new Error(`unknown ticket kind: ${n.kind}`)}).sort((n,s)=>{const r=o(d=>d.kind==="page"?d.emitted_at:d.blocked_since,"pageTime"),a=Date.parse(r(s))-Date.parse(r(n));return a!==0?a:n.ticket_id.localeCompare(s.ticket_id)})}}}o(V,"handleList");const z=new Set(["pending","resolved","canceled","passive"]);async function B(e){const i=e.query.get("node_id");if(i==="")throw new t(400,"invalid_request","node_id must be nonempty when present");const n=e.query.get("state");if(n!==null&&!z.has(n))throw new t(400,"invalid_request","state must be one of pending, resolved, canceled, passive");const s=(await E(i??void 0)).filter(d=>n===null||d.state===n);return{status:200,body:{tickets:(await v(s,async d=>{if(g(e.principal))return!0;const m=await p(d.inboxTicketId),w=m?.requester_node_id?await b(m.requester_node_id):null;return w!==null&&await y(e.principal,w.grantee??"app:terminal",w.node_id)})).map(q)}}}o(B,"handleHistory");async function F(e){const i=l(e.params),n=await c(i);if(n===null)throw new t(404,"ticket_not_found","no matching ticket");if(n.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets have no page read operation");const s=await p(i);if(s===null||s.kind!=="page")throw new t(404,"ticket_not_found","no matching ticket");try{return{status:200,body:await A(s)}}catch{throw new t(500,"internal","the ticket could not be read")}}o(F,"handleGetPage");async function M(e){const i=l(e.params),n=L(e.body),s=await c(i);if(s===null)throw new t(404,"ticket_not_found","no matching ticket");if(s.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets submit through their own path");if((await h(s.dir)).state==="passive")throw new t(409,"page_has_no_response_lifecycle","this page has no response lifecycle");const r=await k(s.dir);if(r!==null&&r.kind!=="canceled")throw new t(409,"ticket_already_resolved","ticket was already resolved");if(r?.kind==="canceled")throw new t(409,"ticket_canceled","canceled tickets cannot be answered");try{await R(s.dir,n.responses)}catch(a){throw _(a)?new t(400,"invalid_ticket_response",a.message):new t(500,"internal","validation failed")}try{const a=await C(s.ticketId,n.responses);if(a.kind!=="page")throw new t(409,"ticket_kind_mismatch","unexpected result kind");return{status:200,body:a}}catch(a){throw a instanceof t?a:_(a)?new t(400,"invalid_ticket_response",a.message):new t(500,"internal","the ticket could not be finalized")}}o(M,"handleRespond");async function U(e){const i=l(e.params),n=H(e.body),s=await c(i);if(s===null)throw new t(404,"ticket_not_found","no matching ticket");if(s.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets do not support progress autosave");if((await h(s.dir)).state==="passive")throw new t(409,"page_has_no_response_lifecycle","this page has no response lifecycle");const r=await k(s.dir);if(r!==null&&r.kind!=="canceled")throw new t(409,"ticket_already_resolved","ticket was already resolved");try{return await S(s.dir,n.responses),{status:200,body:{}}}catch(a){throw _(a)?new t(400,"invalid_ticket_response",a.message):new t(500,"internal","progress could not be saved")}}o(U,"handleProgress");async function X(e){const i=l(e.params),n=K(e.body),s=await c(i);if(s===null)throw new t(404,"ticket_not_found","no matching ticket");if(s.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets have their own cancel path");if((await h(s.dir)).state==="passive")throw new t(409,"page_has_no_response_lifecycle","this page has no response lifecycle");try{const r=await N(s.ticketId,{reason:n.reason??D,actor:"human",disposition:"dismissed"});if(r.outcome==="already_settled"||r.result.kind!=="canceled")throw new t(409,"ticket_already_resolved","ticket was already resolved");return{status:200,body:r.result}}catch(r){throw r instanceof t?r:new t(500,"internal","the ticket could not be canceled")}}o(X,"handleCancel");async function J(e){const i=l(e.params),n=await c(i);if(n===null)throw new t(404,"ticket_not_found","no matching ticket");if(n.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets fetch responses through their own path");const s=await k(n.dir);if(s===null)throw new t(404,"ticket_pending","ticket has no response yet");if(s.kind==="canceled")throw new t(404,"ticket_canceled","canceled tickets have no response");if(s.kind!=="page")throw new t(409,"ticket_kind_mismatch","unexpected result kind");try{const r=I(await x(n.dir));return{status:200,body:{...s,answer:O(r,s.responses,{completedAt:s.completedAt})}}}catch{throw new t(500,"internal","the ticket answer could not be read")}}o(J,"handleGetResponse");const de=[{method:"GET",pattern:"/v1/human/inbox",handler:V},{method:"GET",pattern:"/v1/human/inbox/history",handler:B},{method:"GET",pattern:"/v1/human/inbox/:ticket_id",handler:F},{method:"GET",pattern:"/v1/human/inbox/:ticket_id/response",handler:J},{method:"POST",pattern:"/v1/human/inbox/:ticket_id/respond",handler:M},{method:"POST",pattern:"/v1/human/inbox/:ticket_id/progress",handler:U},{method:"POST",pattern:"/v1/human/inbox/:ticket_id/cancel",handler:X}];export{de as inboxRoutes,l as requireTicketId,c as resolveTicket};
@@ -1,2 +1,2 @@
1
1
  var ce=Object.defineProperty;var u=(t,e)=>ce(t,"name",{value:e,configurable:!0});import{randomBytes as ue}from"node:crypto";import{claimNodeMail as le,acknowledgeNodeMail as fe}from"../../../core/canvas/deliveries.js";import{nodeMailEntry as me}from"../../../core/feed/inbox.js";import{enqueueNodeMail as D,cancelPendingNodeMail as pe}from"../../../core/feed/messages.js";import{reviveNode as L}from"../../../core/runtime/revive.js";import{BrokerUnreachableError as U}from"../../../core/runtime/broker-request.js";import{isBrokerLive as R}from"../../../core/runtime/model-swap.js";import{deliverLive as ye,interruptLive as he}from"../../../core/runtime/interactive-deliver.js";import{isProfilePaused as we}from"../../../core/profiles/manifest.js";import{getNode as O,withFreshTerminalGuard as be}from"../../../core/canvas/canvas.js";import{getById as G,nodeObject as F,ownerForGrantee as ge}from"../../../core/graph/objects.js";import{recordActedOn as ve}from"../../../core/graph/edges.js";import{recordWatch as H}from"../../../core/graph/watches.js";import{publish as V}from"../../../core/graph/events.js";import{readerFor as K}from"../reader.js";import{canRead as _e}from"../../../core/graph/access.js";import{withCanvasWrite as k}from"../../../core/canvas/db.js";import{getPendingStructuredRequestForNode as Q}from"../../../core/canvas/structured-requests.js";import{consumeDeadlineOnWake as X}from"../../../core/feed/messages.js";import{assertNotFinalized as M,assertFinalizedForReopen as ke,commitReopenResident as P}from"../../../core/runtime/reopen.js";import{admitLegacyStructuredOutput as Se}from"../../structured-requests/sidecar.js";import{assertOutputSchemaAccepted as $e}from"../../../core/runtime/structured-output.js";import{appendSituationalContext as E,formatSituationalProse as Ne}from"../../../core/runtime/situational-context.js";import{formatDataCard as Y}from"../../../shared/generated-context.js";import{hasNoNaturalCycle as je}from"../../../core/runtime/revive-all.js";import{recordPendingParkActivity as Ae}from"../../park-pending.js";import{wantsExecution as qe}from"../../reconcilers/node-lifecycle/wants-execution.js";import{hasFreshGroundState as Oe}from"../../../core/runtime/revive.js";import{CrtrError as Ce,notFound as C,usage as c}from"../../../core/errors.js";import{isStartRefusal as xe}from"../../../core/runtime/start-check.js";import{emitEvent as Re}from"../../../core/events/emit.js";import{assertScope as Fe}from"../../../core/scopes.js";import{InputError as I}from"../../../core/io.js";import{ApiError as Me}from"../../../api/index.js";import{describeObject as Pe,stubOf as Ee}from"../../../core/peer/stubs.js";import{machineNameOf as Z}from"../../../core/peer/names.js";import{callerNode as Ie,callerStub as Te,forwardVerb as ee,relayError as te,remoteStub as Be,unknownError as re,withMachine as ne}from"../../peer/forward.js";import{ownerOnly as ze}from"../router.js";import{personId as We}from"../../../core/canvas/person.js";const Je=["critical","urgent","normal","deferred"];function oe(t,e){if(typeof t!="object"||t===null||Array.isArray(t))throw c(`${e} must be an object { kind, facts?, body? }`);const a=t,n=a.kind;if(typeof n!="string"||n.trim()==="")throw c(`${e}.kind is required`);if(!n.includes(":"))throw c(`${e}.kind must be namespaced (contain ':'): ${n}`);const r={kind:n},o=a.facts;if(o!==void 0){if(typeof o!="object"||o===null||Array.isArray(o))throw c(`${e}.facts must be an object of string or number values`);const d={};for(const[l,m]of Object.entries(o)){if(typeof m!="string"&&typeof m!="number")throw c(`${e}.facts.${l} must be a string or number`);d[l]=m}r.facts=d}const s=a.body;if(s!==void 0){if(typeof s!="string")throw c(`${e}.body must be a string`);r.body=s}return r}u(oe,"parseCardRequest");function ae(t,e){try{return Y(t.kind,t.facts??{},t.body??"")}catch(a){throw c(`${e}: ${a instanceof Error?a.message:String(a)}`)}}u(ae,"renderCard");function De(t){if(typeof t!="object"||t===null)throw c("a message requires a JSON body { body, tier?, fresh? }");const e=t,a=e.body,n=e.fresh===!0,r=e.tier,o=e.situational_context,s=e.output_schema,d=typeof a=="string"&&a.trim()!=="",l=typeof o=="string"&&o.trim()!=="",m=typeof s=="string"&&s.trim()!=="",v=e.situational_card,S=e.context_cards,b=v===void 0?void 0:oe(v,"situational_card");if(S!==void 0&&!Array.isArray(S))throw c("context_cards must be an array of { kind, facts?, body? }");const p=S?.map((N,$)=>oe(N,`context_cards[${$}]`));if(b!==void 0&&typeof o=="string"&&o!=="")throw c("situational_card and situational_context are mutually exclusive");const w=b!==void 0||p!==void 0&&p.length>0;if(!n&&!d&&!l&&!m&&!w)throw c("a message requires a non-empty body (or fresh: true, situational_context, a runtime card, or output_schema)");if(n&&p!==void 0&&p.length>0)throw c("context_cards cannot ride a fresh revive (no inbox entry carries them)");if(r!==void 0&&!Je.includes(r))throw c(`invalid tier: ${String(r)} (expected critical|urgent|normal|deferred)`);const f={body:typeof a=="string"?a:""};r!==void 0&&(f.tier=r),n&&(f.fresh=!0),e.reopen===!0&&(f.reopen=!0),typeof o=="string"&&(f.situational_context=o),b!==void 0&&(f.situational_card=b),p!==void 0&&(f.context_cards=p),typeof s=="string"&&(f.output_schema=s),typeof e.from=="string"&&(f.from=e.from);const y=e.delivery;if(y!==void 0&&y!=="interactive")throw c(`invalid delivery: ${String(y)} (expected interactive)`);if(y==="interactive"){if(n||f.situational_context!==void 0||f.output_schema!==void 0)throw c("interactive delivery supports a body, runtime cards, and reopen only (no fresh, situational_context, or output_schema)");if(!d)throw c("interactive delivery requires a non-empty body");if(f.tier==="deferred")throw c("interactive delivery cannot be deferred");f.delivery="interactive"}return f}u(De,"parseSendBody");function Le(t){let e;try{e=JSON.parse(t)}catch{throw c("output_schema must be a JSON schema string")}if(typeof e!="object"||e===null||Array.isArray(e))throw c("output_schema must be a JSON schema object");return e}u(Le,"parseSchema");function Ue(t){return t.split(`
2
- `)[0].slice(0,120)}u(Ue,"label");function ie(t,e){return e===null?"":Y("reopen-final-report",{report:`${t}/${e}`},"This node previously completed work. Read this final report before acting on the new instruction.")}u(ie,"previousFinalReportCard");async function Ge(t,e,a){if(a!==null&&a!=="human"&&(await G(t,a))?.type==="node"){const o=await F(t,a);return{actor:o,senderNode:o}}if(a==="human")return{actor:await ge(t,We()),senderNode:null};const n=await K(t,e);return{actor:n.class==="person"?n.person:n.class==="app"?n.app:n.node,senderNode:n.class==="node"?n.node:null}}u(Ge,"messageActor");async function se(t,e,a,n,r,o){const s=await F(t,a),{actor:d,senderNode:l}=await Ge(t,e,n),m=r===null?{}:{pointer:r};await ve(t,d,s),l!==null&&l.id!==s.id&&await H(t,l,s,"message",{endsOn:o&&r!==null?`result:${r}`:"final"}),await V(t,{event:"messaged",object:s,actor:d,attrs:{},...m}),l!==null&&await V(t,{event:"sent",object:l,actor:l,attrs:{to:s.id},...m})}u(se,"recordMessage");function He(t,e){return new I({error:"deferred_no_natural_cycle",message:`${t} is ${e} with no natural next cycle ahead of it \u2014 a deferred message would never be delivered.`,next:"Use --tier normal, urgent, or critical to wake it now, or --reopen (with a body or --fresh) to re-task it first."})}u(He,"deferredNoNaturalCycle");async function Ve(t){if(!await Oe(t))throw new Me(422,"unprocessable",`node ${t} has no goal/roadmap on disk \u2014 a fresh revive would resume amnesiac.`)}u(Ve,"assertRecoverableForFresh");function Ke(t,e){return t instanceof I?{error:t.payload.error,message:t.payload.message,next:t.payload.next,message_id:e}:t instanceof Ce?{error:t.code,message:t.message,message_id:e}:xe(t)?{error:t.code,message:t.message,message_id:e}:{error:"wake_failed",message:"the target could not be woken; see the daemon log",message_id:e}}u(Ke,"wakeFailure");async function Qe(t,e){const a=typeof t.body=="object"&&t.body!==null&&!Array.isArray(t.body)?{...t.body}:t.body;typeof a=="object"&&a!==null&&delete a.from;const n=await ee(t,e,{method:"POST",path:`/v1/nodes/${encodeURIComponent(e)}/messages`,body:a});if(n.kind==="local")return null;if(n.kind==="unknown")throw re(e,n.unreachable);if(n.status>=400)throw te(n.status,n.body);return await k(async r=>{const o=await Ie(r,t);if(o===null)return;const s=n.body?.object,d=await Be(r,e,"node",n.machine.id,s);await H(r,o,d,"message",{endsOn:"final"})}),{status:n.status,body:ne(n.body,n.machine)}}u(Qe,"forwardMessage");async function Xe(t){const e=await Ye(t);if(t.principal.class!=="forwarded"||typeof e.body!="object"||e.body===null)return e;const a=await k(async n=>{const r=await F(n,t.params.id);return await _e(n,await K(n,t),r)?await Pe(n,r):{id:r.id,type:r.type,kind:null,name:r.id,owner:null}});return{...e,body:{...e.body,object:a}}}u(Xe,"handleMessage");async function Ye(t){const e=t.params.id,a=await Qe(t,e);if(a!==null)return a;const n=await O(e);if(n===null)throw C(`unknown node: ${e}`,{received:e});const r=De(t.body),o=r.tier??"normal",s=we(n.profile_id),d=t.principal.class==="forwarded"?await k(async i=>(await Te(i,t))?.id??null):r.from??null;if(d!==null){const i=await O(d);i!==null&&Fe(i,"crtr:act")}const l=d===null?null:await k(async i=>{const h=await G(i,d);return h===null?null:(await Ee(i,h))?.home_machine??null}),m=l===null?void 0:`${d} on ${Z(l)}`,v=t.principal.class==="broker"?t.principal.node:null,S=l===null?{}:{from_machine:Z(l)},b=r.body.trim()!=="",p=r.situational_card!==void 0?ae(r.situational_card,"situational_card"):r.situational_context!==void 0?Ne(r.situational_context.trim()):"",w=p!=="",f=(r.context_cards??[]).map((i,h)=>ae(i,`context_cards[${h}]`)),y=r.output_schema!==void 0&&r.output_schema.trim()!==""?Le(r.output_schema):null;y!==null&&await $e(y);let N=!1;const $=u(async i=>{if(!(y===null||N)){if(await Q(e)!==null)throw new I({error:"structured_request_conflict",message:`node ${e} already has a pending structured request`,next:"Wait for the current request to settle before requesting another structured result."});await Se(e,"oneoff",y,i),N=!0}},"ensureSchema");if(r.fresh){const i=r.reopen?await ke(e):null;r.reopen||await M(e),await Ve(e),await $(),w&&E(e,p),r.reopen&&await P(e,i,v);const h=await L(e,{resume:!1,capacity:"refuse"});return{status:201,body:{node_id:e,delivered:!1,revived:h.launch!==void 0}}}r.delivery==="interactive"&&!r.reopen&&await M(e);let T=!1,_="";if(r.delivery==="interactive"&&r.reopen&&(_=ie(e,await P(e,void 0,v)),T=!0),r.delivery==="interactive"&&await $(),r.delivery==="interactive"&&!s&&!n.run_id&&R(n))try{return w&&E(e,p),await ye(e,r.body,{cards:[..._===""?[]:[_],...w?[p]:[],...f],origin:d==="human"?{type:"human"}:{type:"runtime",kind:"inbox",entries:[{...d===null?{}:{from:d},...m===void 0?{}:{fromName:m},kind:"message",tier:o}]}}),await k(async h=>await se(h,t,e,d,null,!1)),await X(e),{status:201,body:{node_id:e,delivered:!0,revived:!1,delivered_via:"engine"}}}catch(i){if(!(i instanceof U))throw i}const B=u(async()=>{r.reopen||await M(e);const i=ue(16).toString("hex"),h=u(async q=>await se(q,t,e,d,i,y!==null),"withinEnqueue");await $(i),w&&E(e,p),r.reopen&&!T&&(_=ie(e,await P(e,void 0,v)));const x=[..._===""?[]:[_],...f],J=x.length>0?{cards:x}:{};if(b||y!==null){const q=b?r.body:`A one-off structured-output request has been granted. Submit a result matching the requested schema with the \`submit_result\` tool (\`request_id: "${(await Q(e))?.request_id??"<request-id>"}"\`) before you stop; after it is accepted, continue normally.`;return await D(e,{senderNodeId:d,...m===void 0?{}:{senderName:m},urgency:o,wake:1,kind:"message",messageId:i,withinEnqueue:h,label:Ue(q),bodyJson:{body:q,...S,...w?{situational:!0}:{},...J}})}return await D(e,{senderNodeId:d,...m===void 0?{}:{senderName:m},urgency:o,wake:1,kind:"message",messageId:i,withinEnqueue:h,label:w?"(ambient context updated)":"(context delivered)",bodyJson:{...w?{situational:!0}:{},situationalOnly:!0,...J}})},"deliverImmediate"),g=await u(async()=>{if(o==="deferred"){const i=await be(e,je,B);if(i.kind==="missing")throw C(`unknown node: ${e}`,{received:e});if(i.kind==="terminal")throw He(e,i.status);return i.result}return await B()},"deliver")();g.urgency!=="deferred"&&await k(async()=>Ae(e));const j=await O(e);if(j===null)throw C(`unknown node: ${e}`,{received:e});n.frozen_at!==null&&await X(e);let z=!1,W=!1,A;if((r.reopen||j.lifecycle==="terminal"||!await qe(j))&&g.urgency!=="deferred"&&!s&&!R(j))try{const i=await L(e,{resume:!0,capacity:"freeze"});z=i.launch!==void 0,W=i.outcome==="frozen"}catch(i){A=Ke(i,g.messageId),Re({level:"error",event:"message.wake_failed",node_id:e,error:i,fields:{message_id:g.messageId,error_code:A.error}})}return{status:201,body:{node_id:e,delivered:!0,revived:z,...W?{not_revived_reason:"capacity_frozen"}:{},...A===void 0?{}:{wake_failed:A},delivered_at:g.createdAt,...g.urgency===o?{}:{delivered_tier:g.urgency},...r.delivery==="interactive"?{delivered_via:"inbox"}:{}}}}u(Ye,"handleMessageHere");async function Ze(t){const e=t.params.id,a=await ee(t,e,{method:"POST",path:`/v1/nodes/${encodeURIComponent(e)}/interrupt`,body:t.body});if(a.kind==="unknown")throw re(e,a.unreachable);if(a.kind==="remote"){if(a.status>=400)throw te(a.status,a.body);return{status:a.status,body:ne(a.body,a.machine)}}const n=await O(e);if(n===null)throw C(`unknown node: ${e}`,{received:e});const r=await pe(e,{senderNodeId:"human",kind:"message"});let o=!1;if(R(n))try{o=await he(e)!=="idle"}catch(d){if(!(d instanceof U))throw d}return{status:200,body:{node_id:e,outcome:o?"aborted_turn":r.length>0?"canceled_pending":"idle",aborted_turn:o,canceled_pending:r.length}}}u(Ze,"handleInterrupt");function de(t){if(typeof t.body!="object"||t.body===null||Array.isArray(t.body))throw c("mail requires an object body");const e=t.body;if(typeof e.execution_id!="string"||e.execution_id.trim()==="")throw c("execution_id is required");return e}u(de,"mailBody");async function et(t){const e=de(t),a=e.channel;if(a!=="wake"&&a!=="passive"&&a!=="deferred_run")throw c("channel must be wake, passive, or deferred_run");const n=await le(t.params.id,e.execution_id,a);return{status:200,body:{action:n.action,deliveries:n.deliveries.map(o=>({delivery_id:o.deliveryId,attempts:o.attempts,handoff:o.handoff,entry:{...me(o),seq:o.message.seq}}))}}}u(et,"handleMailClaim");async function tt(t){const e=de(t);if(!Array.isArray(e.deliveries))throw c("deliveries must be an array");const a=e.deliveries.map(o=>{if(typeof o!="object"||o===null||Array.isArray(o))throw c("each delivery must be an object");const s=o;if(typeof s.delivery_id!="string"||s.delivery_id==="")throw c("delivery_id is required");if(!Number.isSafeInteger(s.attempts)||s.attempts<1)throw c("attempts must be a positive integer");if(s.disposition!=="acknowledged"&&s.disposition!=="released")throw c("disposition must be acknowledged or released");return{delivery_id:s.delivery_id,attempts:s.attempts,disposition:s.disposition}}),n=await fe(t.params.id,e.execution_id,a.map(o=>({deliveryId:o.delivery_id,attempts:o.attempts,disposition:o.disposition})));return{status:200,body:{action:n.action,results:n.results.map(o=>({delivery_id:o.deliveryId,outcome:o.outcome}))}}}u(tt,"handleMailAcknowledge");const Ut=ze([{method:"POST",pattern:"/v1/nodes/:id/mail/claim",handler:et},{method:"POST",pattern:"/v1/nodes/:id/mail/acknowledge",handler:tt}]),Gt=[{method:"POST",pattern:"/v1/nodes/:id/messages",scope:"crtr:act",handler:Xe},{method:"POST",pattern:"/v1/nodes/:id/interrupt",scope:"crtr:act",handler:Ze}];export{Ut as brokerMailRoutes,Xe as handleMessage,Gt as messageRoutes};
2
+ `)[0].slice(0,120)}u(Ue,"label");function ie(t,e){return e===null?"":Y("reopen-final-report",{report:`${t}/${e}`},"This node previously completed work. Read this final report before acting on the new instruction.")}u(ie,"previousFinalReportCard");async function Ge(t,e,a){if(a!==null&&a!=="human"&&(await G(t,a))?.type==="node"){const o=await F(t,a);return{actor:o,senderNode:o}}if(a==="human")return{actor:await ge(t,We()),senderNode:null};const n=await K(t,e);return{actor:n.class==="person"?n.person:n.class==="app"?n.app:n.node,senderNode:n.class==="node"?n.node:null}}u(Ge,"messageActor");async function se(t,e,a,n,r,o){const s=await F(t,a),{actor:d,senderNode:l}=await Ge(t,e,n),m=r===null?{}:{pointer:r};await ve(t,d,s),l!==null&&l.id!==s.id&&await H(t,l,s,"message",{endsOn:o&&r!==null?`result:${r}`:"final"}),await V(t,{event:"messaged",object:s,actor:d,attrs:{},...m}),l!==null&&await V(t,{event:"sent",object:l,actor:l,attrs:{to:s.id},...m})}u(se,"recordMessage");function He(t,e){return new I({error:"deferred_no_natural_cycle",message:`${t} is ${e} with no natural next cycle ahead of it \u2014 a deferred message would never be delivered.`,next:"Use --tier normal, urgent, or critical to wake it now, or --reopen (with a body or --fresh) to re-task it first."})}u(He,"deferredNoNaturalCycle");async function Ve(t){if(!await Oe(t))throw new Me(422,"unprocessable",`node ${t} has no goal/roadmap on disk \u2014 a fresh revive would resume amnesiac.`)}u(Ve,"assertRecoverableForFresh");function Ke(t,e){return t instanceof I?{error:t.payload.error,message:t.payload.message,next:t.payload.next,message_id:e}:t instanceof Ce?{error:t.code,message:t.message,message_id:e}:xe(t)?{error:t.code,message:t.message,message_id:e}:{error:"wake_failed",message:"the target could not be woken; see the daemon log",message_id:e}}u(Ke,"wakeFailure");async function Qe(t,e){const a=typeof t.body=="object"&&t.body!==null&&!Array.isArray(t.body)?{...t.body}:t.body;typeof a=="object"&&a!==null&&delete a.from;const n=await ee(t,e,{method:"POST",path:`/v1/nodes/${encodeURIComponent(e)}/messages`,body:a});if(n.kind==="local")return null;if(n.kind==="unknown")throw re(e,n.unreachable);if(n.status>=400)throw te(n.status,n.body);return await k(async r=>{const o=await Ie(r,t);if(o===null)return;const s=n.body?.object,d=await Be(r,e,"node",n.machine.id,s);await H(r,o,d,"message",{endsOn:"final"})}),{status:n.status,body:ne(n.body,n.machine)}}u(Qe,"forwardMessage");async function Xe(t){const e=await Ye(t);if(t.principal.class!=="forwarded"||typeof e.body!="object"||e.body===null)return e;const a=await k(async n=>{const r=await F(n,t.params.id);return await _e(n,await K(n,t),r)?await Pe(n,r):{id:r.id,type:r.type,kind:null,name:r.id,owner:null}});return{...e,body:{...e.body,object:a}}}u(Xe,"handleMessage");async function Ye(t){const e=t.params.id,a=await Qe(t,e);if(a!==null)return a;const n=await O(e);if(n===null)throw C(`unknown node: ${e}`,{received:e});const r=De(t.body),o=r.tier??"normal",s=we(n.profile_id),d=t.principal.class==="forwarded"?await k(async i=>(await Te(i,t))?.id??null):r.from??null;if(d!==null){const i=await O(d);i!==null&&Fe(i,"crtr:act")}const l=d===null?null:await k(async i=>{const h=await G(i,d);return h===null?null:(await Ee(i,h))?.home_machine??null}),m=l===null?void 0:`${d} on ${Z(l)}`,v=t.principal.class==="broker"?t.principal.node:null,S=l===null?{}:{from_machine:Z(l)},b=r.body.trim()!=="",p=r.situational_card!==void 0?ae(r.situational_card,"situational_card"):r.situational_context!==void 0?Ne(r.situational_context.trim()):"",w=p!=="",f=(r.context_cards??[]).map((i,h)=>ae(i,`context_cards[${h}]`)),y=r.output_schema!==void 0&&r.output_schema.trim()!==""?Le(r.output_schema):null;y!==null&&await $e(y);let N=!1;const $=u(async i=>{if(!(y===null||N)){if(await Q(e)!==null)throw new I({error:"structured_request_conflict",message:`node ${e} already has a pending structured request`,next:"Wait for the current request to settle before requesting another structured result."});await Se(e,"oneoff",y,i),N=!0}},"ensureSchema");if(r.fresh){const i=r.reopen?await ke(e):null;r.reopen||await M(e),await Ve(e),await $(),w&&await E(e,p),r.reopen&&await P(e,i,v);const h=await L(e,{resume:!1,capacity:"refuse"});return{status:201,body:{node_id:e,delivered:!1,revived:h.launch!==void 0}}}r.delivery==="interactive"&&!r.reopen&&await M(e);let T=!1,_="";if(r.delivery==="interactive"&&r.reopen&&(_=ie(e,await P(e,void 0,v)),T=!0),r.delivery==="interactive"&&await $(),r.delivery==="interactive"&&!s&&!n.run_id&&R(n))try{return w&&await E(e,p),await ye(e,r.body,{cards:[..._===""?[]:[_],...w?[p]:[],...f],origin:d==="human"?{type:"human"}:{type:"runtime",kind:"inbox",entries:[{...d===null?{}:{from:d},...m===void 0?{}:{fromName:m},kind:"message",tier:o}]}}),await k(async h=>await se(h,t,e,d,null,!1)),await X(e),{status:201,body:{node_id:e,delivered:!0,revived:!1,delivered_via:"engine"}}}catch(i){if(!(i instanceof U))throw i}const B=u(async()=>{r.reopen||await M(e);const i=ue(16).toString("hex"),h=u(async q=>await se(q,t,e,d,i,y!==null),"withinEnqueue");await $(i),w&&await E(e,p),r.reopen&&!T&&(_=ie(e,await P(e,void 0,v)));const x=[..._===""?[]:[_],...f],J=x.length>0?{cards:x}:{};if(b||y!==null){const q=b?r.body:`A one-off structured-output request has been granted. Submit a result matching the requested schema with the \`submit_result\` tool (\`request_id: "${(await Q(e))?.request_id??"<request-id>"}"\`) before you stop; after it is accepted, continue normally.`;return await D(e,{senderNodeId:d,...m===void 0?{}:{senderName:m},urgency:o,wake:1,kind:"message",messageId:i,withinEnqueue:h,label:Ue(q),bodyJson:{body:q,...S,...w?{situational:!0}:{},...J}})}return await D(e,{senderNodeId:d,...m===void 0?{}:{senderName:m},urgency:o,wake:1,kind:"message",messageId:i,withinEnqueue:h,label:w?"(ambient context updated)":"(context delivered)",bodyJson:{...w?{situational:!0}:{},situationalOnly:!0,...J}})},"deliverImmediate"),g=await u(async()=>{if(o==="deferred"){const i=await be(e,je,B);if(i.kind==="missing")throw C(`unknown node: ${e}`,{received:e});if(i.kind==="terminal")throw He(e,i.status);return i.result}return await B()},"deliver")();g.urgency!=="deferred"&&await k(async()=>Ae(e));const j=await O(e);if(j===null)throw C(`unknown node: ${e}`,{received:e});n.frozen_at!==null&&await X(e);let z=!1,W=!1,A;if((r.reopen||j.lifecycle==="terminal"||!await qe(j))&&g.urgency!=="deferred"&&!s&&!R(j))try{const i=await L(e,{resume:!0,capacity:"freeze"});z=i.launch!==void 0,W=i.outcome==="frozen"}catch(i){A=Ke(i,g.messageId),Re({level:"error",event:"message.wake_failed",node_id:e,error:i,fields:{message_id:g.messageId,error_code:A.error}})}return{status:201,body:{node_id:e,delivered:!0,revived:z,...W?{not_revived_reason:"capacity_frozen"}:{},...A===void 0?{}:{wake_failed:A},delivered_at:g.createdAt,...g.urgency===o?{}:{delivered_tier:g.urgency},...r.delivery==="interactive"?{delivered_via:"inbox"}:{}}}}u(Ye,"handleMessageHere");async function Ze(t){const e=t.params.id,a=await ee(t,e,{method:"POST",path:`/v1/nodes/${encodeURIComponent(e)}/interrupt`,body:t.body});if(a.kind==="unknown")throw re(e,a.unreachable);if(a.kind==="remote"){if(a.status>=400)throw te(a.status,a.body);return{status:a.status,body:ne(a.body,a.machine)}}const n=await O(e);if(n===null)throw C(`unknown node: ${e}`,{received:e});const r=await pe(e,{senderNodeId:"human",kind:"message"});let o=!1;if(R(n))try{o=await he(e)!=="idle"}catch(d){if(!(d instanceof U))throw d}return{status:200,body:{node_id:e,outcome:o?"aborted_turn":r.length>0?"canceled_pending":"idle",aborted_turn:o,canceled_pending:r.length}}}u(Ze,"handleInterrupt");function de(t){if(typeof t.body!="object"||t.body===null||Array.isArray(t.body))throw c("mail requires an object body");const e=t.body;if(typeof e.execution_id!="string"||e.execution_id.trim()==="")throw c("execution_id is required");return e}u(de,"mailBody");async function et(t){const e=de(t),a=e.channel;if(a!=="wake"&&a!=="passive"&&a!=="deferred_run")throw c("channel must be wake, passive, or deferred_run");const n=await le(t.params.id,e.execution_id,a);return{status:200,body:{action:n.action,deliveries:n.deliveries.map(o=>({delivery_id:o.deliveryId,attempts:o.attempts,handoff:o.handoff,entry:{...me(o),seq:o.message.seq}}))}}}u(et,"handleMailClaim");async function tt(t){const e=de(t);if(!Array.isArray(e.deliveries))throw c("deliveries must be an array");const a=e.deliveries.map(o=>{if(typeof o!="object"||o===null||Array.isArray(o))throw c("each delivery must be an object");const s=o;if(typeof s.delivery_id!="string"||s.delivery_id==="")throw c("delivery_id is required");if(!Number.isSafeInteger(s.attempts)||s.attempts<1)throw c("attempts must be a positive integer");if(s.disposition!=="acknowledged"&&s.disposition!=="released")throw c("disposition must be acknowledged or released");return{delivery_id:s.delivery_id,attempts:s.attempts,disposition:s.disposition}}),n=await fe(t.params.id,e.execution_id,a.map(o=>({deliveryId:o.delivery_id,attempts:o.attempts,disposition:o.disposition})));return{status:200,body:{action:n.action,results:n.results.map(o=>({delivery_id:o.deliveryId,outcome:o.outcome}))}}}u(tt,"handleMailAcknowledge");const Ut=ze([{method:"POST",pattern:"/v1/nodes/:id/mail/claim",handler:et},{method:"POST",pattern:"/v1/nodes/:id/mail/acknowledge",handler:tt}]),Gt=[{method:"POST",pattern:"/v1/nodes/:id/messages",scope:"crtr:act",handler:Xe},{method:"POST",pattern:"/v1/nodes/:id/interrupt",scope:"crtr:act",handler:Ze}];export{Ut as brokerMailRoutes,Xe as handleMessage,Gt as messageRoutes};
@@ -1 +1 @@
1
- var f=Object.defineProperty;var i=(e,o)=>f(e,"name",{value:o,configurable:!0});import{ApiError as t}from"../../../api/errors.js";import{validateCoreModelConfig as a,replaceCoreModelConfig as c}from"../../../core/model-config.js";import{parseModelSpec as d}from"../../../core/model-routes.js";import{openModelRegistry as l}from"../../../core/runtime/model-registry.js";import{ownerOnly as m}from"../router.js";function g(e){if(e===null||typeof e!="object"||Array.isArray(e))throw new t(400,"invalid_request","model config must be an object");const o=e;if(typeof o.config_version!="string"||o.config_version.trim()==="")throw new t(400,"invalid_request","config_version must be a non-empty string");try{return{configVersion:o.config_version,config:a(o)}}catch(r){throw new t(400,"invalid_request",r instanceof Error?r.message:String(r))}}i(g,"parseRequest");async function u(e){const o=await l();for(const r of Object.values(e.modelRoutes))for(const n of Object.values(r.models)){const s=d(n);if(s===void 0||o.find(s.providerId,s.modelId)===void 0)throw new t(422,"unprocessable",`route ref ${n} is absent from the baked model registry`)}}i(u,"validateCatalog");async function p(e){const{configVersion:o,config:r}=g(e.body);await u(r);try{c(r)}catch(n){throw new t(500,"model_config_write_failed",n instanceof Error?n.message:String(n))}return{status:200,body:{config_version:o,delivered:!0}}}i(p,"handlePut");const C=m([{method:"PUT",pattern:"/v1/model-config",handler:p}]);export{C as modelConfigRoutes};
1
+ var a=Object.defineProperty;var i=(e,o)=>a(e,"name",{value:o,configurable:!0});import{ApiError as t}from"../../../api/errors.js";import{validateCoreModelConfig as f,replaceCoreModelConfig as c}from"../../../core/model-config.js";import{parseModelSpec as d}from"../../../core/model-routes.js";import{openModelRegistry as l}from"../../../core/runtime/model-registry.js";import{ownerOnly as m}from"../router.js";function g(e){if(e===null||typeof e!="object"||Array.isArray(e))throw new t(400,"invalid_request","model config must be an object");const o=e;if(typeof o.config_version!="string"||o.config_version.trim()==="")throw new t(400,"invalid_request","config_version must be a non-empty string");try{return{configVersion:o.config_version,config:f(o)}}catch(r){throw new t(400,"invalid_request",r instanceof Error?r.message:String(r))}}i(g,"parseRequest");async function u(e){const o=await l();for(const r of Object.values(e.modelRoutes))for(const n of Object.values(r.models)){const s=d(n);if(s===void 0||o.find(s.providerId,s.modelId)===void 0)throw new t(422,"unprocessable",`route ref ${n} is absent from the baked model registry`)}}i(u,"validateCatalog");async function p(e){const{configVersion:o,config:r}=g(e.body);await u(r);try{await c(r)}catch(n){throw new t(500,"model_config_write_failed",n instanceof Error?n.message:String(n))}return{status:200,body:{config_version:o,delivered:!0}}}i(p,"handlePut");const C=m([{method:"PUT",pattern:"/v1/model-config",handler:p}]);export{C as modelConfigRoutes};