@north-light/cli 0.3.561 → 0.3.562

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (573) hide show
  1. package/dist/api/client.d.ts +27 -4
  2. package/dist/api/client.js +3 -3
  3. package/dist/api/contract.d.ts +1 -1
  4. package/dist/api/contract.js +1 -1
  5. package/dist/api/dto/bash-jobs.d.ts +5 -1
  6. package/dist/api/dto/broker-ops.d.ts +25 -1
  7. package/dist/api/dto/charges.d.ts +9 -2
  8. package/dist/api/dto/delivery.d.ts +26 -2
  9. package/dist/api/dto/execution-start.d.ts +57 -0
  10. package/dist/api/dto/execution-start.js +0 -0
  11. package/dist/api/dto/modelauth.d.ts +5 -0
  12. package/dist/api/dto/node-records.d.ts +16 -0
  13. package/dist/api/dto/objects.d.ts +1 -1
  14. package/dist/api/dto/reports.d.ts +0 -3
  15. package/dist/api/dto/run-events.d.ts +7 -1
  16. package/dist/api/dto/settings.d.ts +55 -0
  17. package/dist/api/dto/settings.js +0 -0
  18. package/dist/api/index.d.ts +2 -0
  19. package/dist/api/index.js +1 -1
  20. package/dist/api/routes.d.ts +11 -0
  21. package/dist/api/routes.js +1 -1
  22. package/dist/builtin-memory/crouter-sdk/release-notes.md +4 -0
  23. package/dist/cli.js +7 -7
  24. package/dist/clients/attach/chrome/bash-jobs.d.ts +11 -1
  25. package/dist/clients/attach/chrome/bash-jobs.js +1 -1
  26. package/dist/clients/attach/chrome/canvas-panels.d.ts +1 -1
  27. package/dist/clients/attach/chrome/roster.js +6 -4
  28. package/dist/clients/attach/overlays/auth.js +2 -2
  29. package/dist/clients/attach/render/chat-view.js +1 -1
  30. package/dist/clients/attach/viewer.js +550 -567
  31. package/dist/commands/canvas-config.js +2 -2
  32. package/dist/commands/canvas-use.js +1 -1
  33. package/dist/commands/human.js +2 -2
  34. package/dist/commands/node/bash.js +3 -3
  35. package/dist/commands/node/create.js +2 -2
  36. package/dist/commands/person-settings.d.ts +1 -0
  37. package/dist/commands/person-settings.js +1 -0
  38. package/dist/commands/pkg/market-manage.d.ts +2 -2
  39. package/dist/commands/pkg/market-manage.js +1 -1
  40. package/dist/commands/pkg/plugin-manage.js +13 -13
  41. package/dist/commands/pkg/shared.js +1 -1
  42. package/dist/commands/profile/default.js +1 -1
  43. package/dist/commands/push.js +2 -3
  44. package/dist/commands/sys/config.js +1 -1
  45. package/dist/commands/sys/connect.js +1 -1
  46. package/dist/commands/sys/context/sysprompt.js +2 -2
  47. package/dist/commands/sys/doctor.js +1 -1
  48. package/dist/commands/sys/panels/broker-limits-panel.js +1 -1
  49. package/dist/commands/sys/panels/keybindings-panel.js +1 -1
  50. package/dist/commands/sys/panels/models-panel.d.ts +8 -2
  51. package/dist/commands/sys/panels/models-panel.js +1 -1
  52. package/dist/commands/sys/panels/prefs-panel.d.ts +1 -0
  53. package/dist/commands/sys/panels/prefs-panel.js +1 -1
  54. package/dist/commands/sys/panels/profiles-panel.js +1 -1
  55. package/dist/commands/sys/panels/provider-panel.d.ts +4 -0
  56. package/dist/commands/sys/panels/provider-panel.js +1 -1
  57. package/dist/commands/sys/provider-login.js +5 -5
  58. package/dist/commands/sys/setup-core.js +3 -3
  59. package/dist/commands/sys/setup-settings-runner.js +1 -1
  60. package/dist/commands/sys/spend.js +3 -3
  61. package/dist/commands/sys/update.js +1 -1
  62. package/dist/core/auth-file.d.ts +11 -19
  63. package/dist/core/auth-file.js +1 -2
  64. package/dist/core/auto-update.d.ts +1 -1
  65. package/dist/core/auto-update.js +4 -4
  66. package/dist/core/bash-jobs.d.ts +29 -71
  67. package/dist/core/bash-jobs.js +9 -10
  68. package/dist/core/blobs/directory.d.ts +3 -0
  69. package/dist/core/blobs/directory.js +1 -0
  70. package/dist/core/blobs/fault.d.ts +3 -0
  71. package/dist/core/blobs/fault.js +1 -0
  72. package/dist/core/blobs/index.d.ts +21 -0
  73. package/dist/core/blobs/index.js +1 -0
  74. package/dist/core/blobs/r2.d.ts +9 -0
  75. package/dist/core/blobs/r2.js +4 -0
  76. package/dist/core/bootstrap.d.ts +1 -1
  77. package/dist/core/bootstrap.js +2 -2
  78. package/dist/core/canvas/canvas.d.ts +9 -7
  79. package/dist/core/canvas/canvas.js +8 -8
  80. package/dist/core/canvas/crons.d.ts +3 -2
  81. package/dist/core/canvas/crons.js +3 -3
  82. package/dist/core/canvas/daemon-owner.d.ts +5 -0
  83. package/dist/core/canvas/daemon-owner.js +1 -1
  84. package/dist/core/canvas/db.d.ts +10 -9
  85. package/dist/core/canvas/db.js +4 -4
  86. package/dist/core/canvas/executions.d.ts +100 -4
  87. package/dist/core/canvas/executions.js +1 -1
  88. package/dist/core/canvas/export-sqlite.js +1 -1
  89. package/dist/core/canvas/focuses.d.ts +4 -0
  90. package/dist/core/canvas/focuses.js +1 -1
  91. package/dist/core/canvas/history.js +9 -9
  92. package/dist/core/canvas/managed-worktree-sweep-candidates.d.ts +2 -1
  93. package/dist/core/canvas/managed-worktree-sweep-candidates.js +3 -2
  94. package/dist/core/canvas/node-tokens.d.ts +1 -4
  95. package/dist/core/canvas/node-tokens.js +3 -3
  96. package/dist/core/canvas/paths.d.ts +0 -1
  97. package/dist/core/canvas/paths.js +1 -1
  98. package/dist/core/canvas/person-state.d.ts +26 -0
  99. package/dist/core/canvas/person-state.js +1 -0
  100. package/dist/core/canvas/person.d.ts +3 -10
  101. package/dist/core/canvas/person.js +7 -7
  102. package/dist/core/canvas/reconciler-leases.d.ts +12 -0
  103. package/dist/core/canvas/reconciler-leases.js +8 -0
  104. package/dist/core/canvas/recovery.d.ts +4 -0
  105. package/dist/core/canvas/recovery.js +1 -1
  106. package/dist/core/canvas/schema-pg/003-fleet-removals.sql +1 -1
  107. package/dist/core/canvas/schema-pg/005-fleet-blobs.sql +13 -0
  108. package/dist/core/canvas/schema-pg/006-job-rows.sql +20 -0
  109. package/dist/core/canvas/schema-pg/007-ledger-check.sql +117 -0
  110. package/dist/core/canvas/schema-version.d.ts +1 -1
  111. package/dist/core/canvas/schema-version.js +1 -1
  112. package/dist/core/canvas/sessions.d.ts +8 -11
  113. package/dist/core/canvas/sessions.js +1 -4
  114. package/dist/core/canvas/tree.d.ts +4 -2
  115. package/dist/core/canvas/tree.js +6 -6
  116. package/dist/core/canvas/types.d.ts +7 -0
  117. package/dist/core/command-hooks/transport/exec-lifecycle.js +1 -1
  118. package/dist/core/command-plugins/bundle.d.ts +1 -1
  119. package/dist/core/command-plugins/bundle.js +1 -1
  120. package/dist/core/command-plugins/compose.d.ts +1 -1
  121. package/dist/core/command-plugins/discovery.d.ts +0 -1
  122. package/dist/core/command-plugins/discovery.js +1 -1
  123. package/dist/core/command-plugins/help-addenda.js +1 -1
  124. package/dist/core/command-plugins/presence.d.ts +2 -0
  125. package/dist/core/command-plugins/presence.js +1 -1
  126. package/dist/core/config.d.ts +16 -15
  127. package/dist/core/config.js +1 -1
  128. package/dist/core/conversation-store/client-worker.js +1 -1
  129. package/dist/core/conversation-store/client.js +2 -2
  130. package/dist/core/conversation-store/index.d.ts +9 -11
  131. package/dist/core/conversation-store/index.js +1 -1
  132. package/dist/core/conversation-store/patch.js +1 -1
  133. package/dist/core/conversation-store/row-store.d.ts +14 -0
  134. package/dist/core/conversation-store/row-store.js +13 -0
  135. package/dist/core/directory/join.js +1 -1
  136. package/dist/core/env-name.d.ts +1 -0
  137. package/dist/core/env-name.js +1 -1
  138. package/dist/core/environment-recipe.d.ts +15 -0
  139. package/dist/core/environment-recipe.js +1 -0
  140. package/dist/core/environments.js +1 -1
  141. package/dist/core/events/emit.js +2 -2
  142. package/dist/core/events/run-event-chunks.d.ts +25 -0
  143. package/dist/core/events/run-event-chunks.js +5 -0
  144. package/dist/core/feed/messages.js +5 -5
  145. package/dist/core/grants/mirror.d.ts +15 -0
  146. package/dist/core/grants/mirror.js +15 -3
  147. package/dist/core/grants/on-sync.d.ts +9 -5
  148. package/dist/core/grants/on-sync.js +1 -1
  149. package/dist/core/grants/remove.d.ts +1 -6
  150. package/dist/core/grants/remove.js +1 -1
  151. package/dist/core/grants/sync.js +1 -1
  152. package/dist/core/grants/watch.d.ts +15 -9
  153. package/dist/core/grants/watch.js +1 -1
  154. package/dist/core/graph/custom.js +8 -8
  155. package/dist/core/graph/jobs.d.ts +83 -5
  156. package/dist/core/graph/jobs.js +11 -2
  157. package/dist/core/graph/kinds/job.js +2 -2
  158. package/dist/core/graph/names.js +7 -7
  159. package/dist/core/graph/objects.d.ts +5 -1
  160. package/dist/core/graph/objects.js +11 -11
  161. package/dist/core/graph/reader.d.ts +3 -0
  162. package/dist/core/graph/reader.js +3 -3
  163. package/dist/core/graph/repo-sync/identity.d.ts +3 -1
  164. package/dist/core/graph/repo-sync/identity.js +1 -1
  165. package/dist/core/graph/repo-sync/repos.d.ts +1 -1
  166. package/dist/core/graph/repo-sync/repos.js +2 -2
  167. package/dist/core/graph/repo-sync/sync.js +2 -2
  168. package/dist/core/installed-plugins.js +1 -1
  169. package/dist/core/keybindings/persistence.d.ts +1 -1
  170. package/dist/core/keybindings/persistence.js +1 -1
  171. package/dist/core/layout-migrate/loose-keys.d.ts +1 -1
  172. package/dist/core/layout-migrate/loose-keys.js +1 -1
  173. package/dist/core/ledger/account.d.ts +12 -0
  174. package/dist/core/ledger/account.js +2 -0
  175. package/dist/core/ledger/charges.d.ts +2 -2
  176. package/dist/core/ledger/charges.js +1 -1
  177. package/dist/core/ledger/check.d.ts +5 -1
  178. package/dist/core/ledger/check.js +1 -7
  179. package/dist/core/ledger/chunks.d.ts +8 -0
  180. package/dist/core/ledger/chunks.js +2 -0
  181. package/dist/core/ledger/index.d.ts +2 -1
  182. package/dist/core/ledger/index.js +1 -1
  183. package/dist/core/ledger/infra-allowance.d.ts +9 -0
  184. package/dist/core/ledger/infra-allowance.js +1 -0
  185. package/dist/core/ledger/limits.js +3 -3
  186. package/dist/core/ledger/payer.d.ts +1 -1
  187. package/dist/core/ledger/payer.js +1 -1
  188. package/dist/core/ledger/read.d.ts +14 -4
  189. package/dist/core/ledger/read.js +2 -3
  190. package/dist/core/ledger/totals.d.ts +5 -16
  191. package/dist/core/ledger/totals.js +4 -4
  192. package/dist/core/ledger/types.d.ts +12 -3
  193. package/dist/core/llm/run.js +2 -2
  194. package/dist/core/managed-providers.d.ts +72 -0
  195. package/dist/core/managed-providers.js +1 -0
  196. package/dist/core/model-auth/api-credential-store.d.ts +10 -0
  197. package/dist/core/model-auth/api-credential-store.js +1 -0
  198. package/dist/core/model-auth/daemon-login.d.ts +1 -1
  199. package/dist/core/model-auth/daemon-login.js +1 -1
  200. package/dist/core/model-auth/flows.d.ts +5 -4
  201. package/dist/core/model-auth/flows.js +1 -1
  202. package/dist/core/model-auth/handoff-jti.d.ts +3 -4
  203. package/dist/core/model-auth/handoff-jti.js +1 -1
  204. package/dist/core/model-auth/readiness.js +1 -1
  205. package/dist/core/model-config.d.ts +1 -1
  206. package/dist/core/model-config.js +1 -1
  207. package/dist/core/model-routes-pool.d.ts +14 -0
  208. package/dist/core/model-routes-pool.js +1 -0
  209. package/dist/core/model-routes.d.ts +11 -16
  210. package/dist/core/model-routes.js +1 -1
  211. package/dist/core/peer/ingest.d.ts +2 -0
  212. package/dist/core/peer/ingest.js +1 -1
  213. package/dist/core/peer/stubs.d.ts +8 -0
  214. package/dist/core/peer/stubs.js +1 -1
  215. package/dist/core/permissions/evaluator.d.ts +7 -2
  216. package/dist/core/permissions/evaluator.js +2 -2
  217. package/dist/core/plugin-env.js +1 -1
  218. package/dist/core/pool-credential-store.d.ts +2 -5
  219. package/dist/core/pool-credential-store.js +1 -1
  220. package/dist/core/profiles/app-env-store.d.ts +5 -5
  221. package/dist/core/profiles/app-env-store.js +1 -2
  222. package/dist/core/profiles/default-binding.d.ts +5 -5
  223. package/dist/core/profiles/default-binding.js +1 -1
  224. package/dist/core/profiles/env-store.d.ts +10 -21
  225. package/dist/core/profiles/env-store.js +1 -2
  226. package/dist/core/profiles/manifest.d.ts +14 -18
  227. package/dist/core/profiles/manifest.js +1 -1
  228. package/dist/core/profiles/relocate.js +1 -1
  229. package/dist/core/profiles/select.js +21 -21
  230. package/dist/core/provider-credential.d.ts +1 -1
  231. package/dist/core/provider-credential.js +1 -1
  232. package/dist/core/provider-management.d.ts +3 -4
  233. package/dist/core/provider-management.js +1 -1
  234. package/dist/core/providers/bundles.js +2 -2
  235. package/dist/core/providers/call.d.ts +3 -34
  236. package/dist/core/providers/description.d.ts +34 -0
  237. package/dist/core/providers/description.js +0 -0
  238. package/dist/core/providers/token-cache.d.ts +14 -14
  239. package/dist/core/providers/token-cache.js +1 -1
  240. package/dist/core/review/companion.js +1 -1
  241. package/dist/core/runs/activity.d.ts +1 -1
  242. package/dist/core/runs/activity.js +1 -1
  243. package/dist/core/runs/events.d.ts +7 -5
  244. package/dist/core/runs/events.js +2 -6
  245. package/dist/core/runs/operations.d.ts +1 -1
  246. package/dist/core/runs/operations.js +6 -6
  247. package/dist/core/runtime/bearings.js +1 -1
  248. package/dist/core/runtime/broker/attachments-fetch.d.ts +5 -0
  249. package/dist/core/runtime/broker/attachments-fetch.js +1 -0
  250. package/dist/core/runtime/broker/bash-job-launch.d.ts +4 -2
  251. package/dist/core/runtime/broker/bash-job-launch.js +1 -1
  252. package/dist/core/runtime/broker/boot-state.d.ts +9 -0
  253. package/dist/core/runtime/broker/boot-state.js +1 -0
  254. package/dist/core/runtime/broker/cli-relay.d.ts +2 -1
  255. package/dist/core/runtime/broker/cli-relay.js +1 -1
  256. package/dist/core/runtime/broker/client-registry.d.ts +3 -3
  257. package/dist/core/runtime/broker/daemon-ops.d.ts +10 -1
  258. package/dist/core/runtime/broker/daemon-ops.js +1 -1
  259. package/dist/core/runtime/broker/fault-retry.d.ts +2 -0
  260. package/dist/core/runtime/broker/fault-retry.js +1 -1
  261. package/dist/core/runtime/broker/frame-engine-control.js +1 -1
  262. package/dist/core/runtime/broker/held-deferred-inbox.d.ts +3 -0
  263. package/dist/core/runtime/broker/held-deferred-inbox.js +1 -1
  264. package/dist/core/runtime/broker/job-reporter.d.ts +18 -0
  265. package/dist/core/runtime/broker/job-reporter.js +2 -0
  266. package/dist/core/runtime/broker/model-adapter.d.ts +3 -0
  267. package/dist/core/runtime/broker/model-adapter.js +3 -2
  268. package/dist/core/runtime/broker/signal-stream.js +1 -1
  269. package/dist/core/runtime/broker/spilled-body.d.ts +5 -0
  270. package/dist/core/runtime/broker/spilled-body.js +1 -0
  271. package/dist/core/runtime/broker/summaries-post.d.ts +5 -0
  272. package/dist/core/runtime/broker/summaries-post.js +1 -0
  273. package/dist/core/runtime/broker/tool-groups.d.ts +3 -0
  274. package/dist/core/runtime/broker/tool-groups.js +1 -1
  275. package/dist/core/runtime/broker/turn-replies.d.ts +1 -0
  276. package/dist/core/runtime/broker/turn-replies.js +1 -1
  277. package/dist/core/runtime/broker-persona-guidance.js +2 -2
  278. package/dist/core/runtime/broker.js +1 -1
  279. package/dist/core/runtime/deliver-live.js +1 -1
  280. package/dist/core/runtime/first-boot-rows.d.ts +8 -0
  281. package/dist/core/runtime/first-boot-rows.js +2 -0
  282. package/dist/core/runtime/fleet.d.ts +13 -12
  283. package/dist/core/runtime/front-door-login.js +1 -1
  284. package/dist/core/runtime/front-door.js +6 -6
  285. package/dist/core/runtime/headless-pi.js +1 -1
  286. package/dist/core/runtime/host.d.ts +14 -3
  287. package/dist/core/runtime/host.js +3 -3
  288. package/dist/core/runtime/invocation.d.ts +6 -0
  289. package/dist/core/runtime/kickoff.d.ts +2 -14
  290. package/dist/core/runtime/kickoff.js +17 -19
  291. package/dist/core/runtime/launch-config-worker.js +1 -1
  292. package/dist/core/runtime/launch.js +2 -2
  293. package/dist/core/runtime/model-registry.js +1 -1
  294. package/dist/core/runtime/node-read.d.ts +4 -6
  295. package/dist/core/runtime/node-read.js +3 -3
  296. package/dist/core/runtime/nodes.js +1 -1
  297. package/dist/core/runtime/outcome-document.d.ts +0 -1
  298. package/dist/core/runtime/outcome-document.js +1 -1
  299. package/dist/core/runtime/output-result.d.ts +15 -0
  300. package/dist/core/runtime/output-result.js +5 -0
  301. package/dist/core/runtime/placement.d.ts +17 -1
  302. package/dist/core/runtime/placement.js +1 -1
  303. package/dist/core/runtime/provider-credential-gate.d.ts +6 -2
  304. package/dist/core/runtime/provider-credential-gate.js +2 -2
  305. package/dist/core/runtime/recovery-hold.d.ts +21 -2
  306. package/dist/core/runtime/recovery-hold.js +2 -1
  307. package/dist/core/runtime/recycle.js +1 -1
  308. package/dist/core/runtime/reset.js +1 -1
  309. package/dist/core/runtime/revive.d.ts +2 -2
  310. package/dist/core/runtime/revive.js +2 -2
  311. package/dist/core/runtime/runner/blaxel/client.d.ts +81 -0
  312. package/dist/core/runtime/runner/blaxel/client.js +1 -0
  313. package/dist/core/runtime/runner/blaxel/image-build.d.ts +74 -0
  314. package/dist/core/runtime/runner/blaxel/image-build.js +5 -0
  315. package/dist/core/runtime/runner/blaxel/runner.d.ts +41 -0
  316. package/dist/core/runtime/runner/blaxel/runner.js +3 -0
  317. package/dist/core/runtime/runner/index.d.ts +8 -0
  318. package/dist/core/runtime/runner/index.js +1 -0
  319. package/dist/core/runtime/runner/process.d.ts +11 -0
  320. package/dist/core/runtime/runner/process.js +1 -0
  321. package/dist/core/runtime/runner/types.d.ts +95 -0
  322. package/dist/core/runtime/runner/types.js +0 -0
  323. package/dist/core/runtime/situational-context.d.ts +3 -6
  324. package/dist/core/runtime/situational-context.js +2 -2
  325. package/dist/core/runtime/situational-live.js +1 -1
  326. package/dist/core/runtime/spawn-env-rows.d.ts +5 -0
  327. package/dist/core/runtime/spawn-env-rows.js +1 -0
  328. package/dist/core/runtime/spawn-env.d.ts +4 -18
  329. package/dist/core/runtime/spawn-env.js +1 -1
  330. package/dist/core/runtime/spawn.js +2 -2
  331. package/dist/core/runtime/stop-guard.d.ts +1 -1
  332. package/dist/core/runtime/stop-guard.js +2 -8
  333. package/dist/core/runtime/structured-output.d.ts +0 -67
  334. package/dist/core/runtime/structured-output.js +3 -8
  335. package/dist/core/runtime/tmux-driver.d.ts +8 -0
  336. package/dist/core/runtime/tmux-driver.js +6 -6
  337. package/dist/core/runtime/view-socket.d.ts +20 -0
  338. package/dist/core/runtime/view-socket.js +3 -1
  339. package/dist/core/runtime/warm-pool.d.ts +13 -1
  340. package/dist/core/runtime/warm-pool.js +1 -1
  341. package/dist/core/scoped-state/legacy-profiles.d.ts +11 -0
  342. package/dist/core/scoped-state/legacy-profiles.js +1 -0
  343. package/dist/core/scoped-state/materialized-plugins.d.ts +18 -0
  344. package/dist/core/scoped-state/materialized-plugins.js +1 -0
  345. package/dist/core/scoped-state/migrate.d.ts +8 -1
  346. package/dist/core/scoped-state/migrate.js +3 -3
  347. package/dist/core/scoped-state/person-rows.d.ts +10 -0
  348. package/dist/core/scoped-state/person-rows.js +6 -0
  349. package/dist/core/scoped-state/person-store.d.ts +72 -0
  350. package/dist/core/scoped-state/person-store.js +1 -0
  351. package/dist/core/secrets/blaxel-launch.d.ts +24 -0
  352. package/dist/core/secrets/blaxel-launch.js +4 -0
  353. package/dist/core/secrets/blaxel-register.d.ts +2 -0
  354. package/dist/core/secrets/blaxel-register.js +1 -0
  355. package/dist/core/secrets/env-rows.d.ts +17 -0
  356. package/dist/core/secrets/env-rows.js +4 -0
  357. package/dist/core/secrets/environment-owner.d.ts +8 -0
  358. package/dist/core/secrets/environment-owner.js +1 -0
  359. package/dist/core/secrets/environment-secrets.d.ts +14 -0
  360. package/dist/core/secrets/environment-secrets.js +1 -0
  361. package/dist/core/secrets/index.d.ts +2 -0
  362. package/dist/core/secrets/index.js +1 -0
  363. package/dist/core/secrets/key.d.ts +3 -0
  364. package/dist/core/secrets/key.js +2 -0
  365. package/dist/core/secrets/seal.d.ts +18 -0
  366. package/dist/core/secrets/seal.js +1 -0
  367. package/dist/core/spaces/mounts.d.ts +1 -1
  368. package/dist/core/spaces/mounts.js +1 -1
  369. package/dist/core/spaces/process.d.ts +1 -1
  370. package/dist/core/spaces/stop.d.ts +7 -5
  371. package/dist/core/spaces/stop.js +2 -2
  372. package/dist/core/spaces/users.d.ts +2 -1
  373. package/dist/core/spaces/users.js +1 -1
  374. package/dist/core/storage/attachments.d.ts +11 -8
  375. package/dist/core/storage/attachments.js +1 -1
  376. package/dist/core/storage/tables.d.ts +4 -0
  377. package/dist/core/storage/tables.js +3 -3
  378. package/dist/core/subscription-state.d.ts +34 -72
  379. package/dist/core/subscription-state.js +1 -4
  380. package/dist/core/substrate/delivery/plan.js +1 -1
  381. package/dist/core/user-settings.js +1 -1
  382. package/dist/daemon/api/app-listener.js +10 -10
  383. package/dist/daemon/api/auth/person-bearer.d.ts +14 -0
  384. package/dist/daemon/api/auth/person-bearer.js +1 -0
  385. package/dist/daemon/api/bridge.js +4 -4
  386. package/dist/daemon/api/document-listener.js +3 -3
  387. package/dist/daemon/api/environment-of.d.ts +18 -0
  388. package/dist/daemon/api/environment-of.js +5 -0
  389. package/dist/daemon/api/handlers/analytics.js +1 -1
  390. package/dist/daemon/api/handlers/app-profiles.d.ts +2 -2
  391. package/dist/daemon/api/handlers/app-profiles.js +1 -1
  392. package/dist/daemon/api/handlers/bash-jobs.js +1 -1
  393. package/dist/daemon/api/handlers/bash.js +1 -1
  394. package/dist/daemon/api/handlers/broker-ops.d.ts +3 -0
  395. package/dist/daemon/api/handlers/broker-ops.js +1 -1
  396. package/dist/daemon/api/handlers/broker-recovery.js +1 -1
  397. package/dist/daemon/api/handlers/broker-signals.d.ts +0 -3
  398. package/dist/daemon/api/handlers/broker-signals.js +2 -2
  399. package/dist/daemon/api/handlers/canvas.js +4 -4
  400. package/dist/daemon/api/handlers/charges.d.ts +1 -1
  401. package/dist/daemon/api/handlers/charges.js +1 -1
  402. package/dist/daemon/api/handlers/conversations.d.ts +2 -0
  403. package/dist/daemon/api/handlers/conversations.js +3 -0
  404. package/dist/daemon/api/handlers/crons.js +1 -1
  405. package/dist/daemon/api/handlers/daemon.js +1 -1
  406. package/dist/daemon/api/handlers/delivery.d.ts +1 -1
  407. package/dist/daemon/api/handlers/delivery.js +1 -1
  408. package/dist/daemon/api/handlers/docs.js +1 -1
  409. package/dist/daemon/api/handlers/exec.js +1 -1
  410. package/dist/daemon/api/handlers/execution-start.d.ts +2 -0
  411. package/dist/daemon/api/handlers/execution-start.js +2 -0
  412. package/dist/daemon/api/handlers/files.js +1 -1
  413. package/dist/daemon/api/handlers/git-credential.d.ts +11 -0
  414. package/dist/daemon/api/handlers/git-credential.js +1 -0
  415. package/dist/daemon/api/handlers/hook-exec.d.ts +1 -1
  416. package/dist/daemon/api/handlers/hook-exec.js +1 -1
  417. package/dist/daemon/api/handlers/human-requests.js +1 -1
  418. package/dist/daemon/api/handlers/inbox.js +1 -1
  419. package/dist/daemon/api/handlers/messages.js +1 -1
  420. package/dist/daemon/api/handlers/model-config.js +1 -1
  421. package/dist/daemon/api/handlers/modelauth.d.ts +1 -1
  422. package/dist/daemon/api/handlers/modelauth.js +1 -1
  423. package/dist/daemon/api/handlers/node-events.d.ts +6 -0
  424. package/dist/daemon/api/handlers/node-events.js +3 -3
  425. package/dist/daemon/api/handlers/node-outcomes.js +1 -1
  426. package/dist/daemon/api/handlers/node-records.js +3 -2
  427. package/dist/daemon/api/handlers/nodes.js +1 -1
  428. package/dist/daemon/api/handlers/objects.js +3 -3
  429. package/dist/daemon/api/handlers/profiles.js +1 -1
  430. package/dist/daemon/api/handlers/providers.js +1 -1
  431. package/dist/daemon/api/handlers/reports.js +9 -13
  432. package/dist/daemon/api/handlers/reviews.js +1 -1
  433. package/dist/daemon/api/handlers/runs.js +1 -1
  434. package/dist/daemon/api/handlers/settings.d.ts +2 -0
  435. package/dist/daemon/api/handlers/settings.js +1 -0
  436. package/dist/daemon/api/handlers/structured-requests.js +1 -1
  437. package/dist/daemon/api/handlers/vendor-status.d.ts +1 -1
  438. package/dist/daemon/api/handlers/vendor-status.js +1 -1
  439. package/dist/daemon/api/handlers/worktree.js +1 -1
  440. package/dist/daemon/api/map.d.ts +2 -2
  441. package/dist/daemon/api/map.js +2 -2
  442. package/dist/daemon/api/operations.js +1 -1
  443. package/dist/daemon/api/reader.d.ts +8 -1
  444. package/dist/daemon/api/reader.js +1 -1
  445. package/dist/daemon/api/router.d.ts +4 -2
  446. package/dist/daemon/api/router.js +1 -1
  447. package/dist/daemon/api/server.d.ts +15 -3
  448. package/dist/daemon/api/server.js +1 -1
  449. package/dist/daemon/api/vendor-signin.d.ts +2 -1
  450. package/dist/daemon/api/vendor-signin.js +19 -19
  451. package/dist/daemon/api/viewer-presence.d.ts +20 -6
  452. package/dist/daemon/api/viewer-presence.js +2 -1
  453. package/dist/daemon/broker-signals/bus.d.ts +48 -11
  454. package/dist/daemon/broker-signals/bus.js +1 -1
  455. package/dist/daemon/cron/armed-context.js +1 -1
  456. package/dist/daemon/cron/lease-recovery.js +1 -1
  457. package/dist/daemon/cron/live-runs.d.ts +6 -0
  458. package/dist/daemon/cron/live-runs.js +1 -1
  459. package/dist/daemon/cron/run-socket.js +1 -1
  460. package/dist/daemon/cron-run.js +2 -2
  461. package/dist/daemon/crtrd-cli.js +1 -1
  462. package/dist/daemon/crtrd.d.ts +10 -0
  463. package/dist/daemon/crtrd.js +18 -17
  464. package/dist/daemon/delivery/run-delivery.d.ts +1 -1
  465. package/dist/daemon/delivery/run-delivery.js +1 -1
  466. package/dist/daemon/due-sources.d.ts +1 -1
  467. package/dist/daemon/due-sources.js +1 -1
  468. package/dist/daemon/due.js +7 -6
  469. package/dist/daemon/fleet.d.ts +6 -6
  470. package/dist/daemon/fleet.js +9 -9
  471. package/dist/daemon/park-activity.d.ts +2 -1
  472. package/dist/daemon/park-activity.js +1 -1
  473. package/dist/daemon/park-pending.d.ts +20 -13
  474. package/dist/daemon/park-pending.js +3 -1
  475. package/dist/daemon/profile-delete.js +1 -1
  476. package/dist/daemon/reconcilers/bash-deadline.d.ts +15 -10
  477. package/dist/daemon/reconcilers/bash-deadline.js +1 -3
  478. package/dist/daemon/reconcilers/broker-supervision.d.ts +13 -3
  479. package/dist/daemon/reconcilers/broker-supervision.js +4 -4
  480. package/dist/daemon/reconcilers/cron-lane.d.ts +0 -1
  481. package/dist/daemon/reconcilers/cron-lane.js +1 -1
  482. package/dist/daemon/reconcilers/fault-retry-backstop.d.ts +2 -9
  483. package/dist/daemon/reconcilers/fault-retry-backstop.js +1 -1
  484. package/dist/daemon/reconcilers/grant-effects.d.ts +8 -0
  485. package/dist/daemon/reconcilers/grant-effects.js +1 -0
  486. package/dist/daemon/reconcilers/grove-instance-sweep.d.ts +1 -3
  487. package/dist/daemon/reconcilers/grove-instance-sweep.js +1 -1
  488. package/dist/daemon/reconcilers/ledger-r2.d.ts +27 -0
  489. package/dist/daemon/reconcilers/ledger-r2.js +7 -0
  490. package/dist/daemon/reconcilers/ledger-sandbox.d.ts +55 -0
  491. package/dist/daemon/reconcilers/ledger-sandbox.js +9 -0
  492. package/dist/daemon/reconcilers/ledger-storage.d.ts +22 -0
  493. package/dist/daemon/reconcilers/ledger-storage.js +5 -0
  494. package/dist/daemon/reconcilers/live-obligation.d.ts +3 -2
  495. package/dist/daemon/reconcilers/live-obligation.js +1 -1
  496. package/dist/daemon/reconcilers/managed-worktree-sweep.d.ts +0 -5
  497. package/dist/daemon/reconcilers/managed-worktree-sweep.js +1 -1
  498. package/dist/daemon/reconcilers/node-deadline.d.ts +2 -1
  499. package/dist/daemon/reconcilers/node-deadline.js +1 -1
  500. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +1 -2
  501. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +1 -1
  502. package/dist/daemon/reconcilers/node-lifecycle/remote-wake.d.ts +31 -0
  503. package/dist/daemon/reconcilers/node-lifecycle/remote-wake.js +1 -0
  504. package/dist/daemon/reconcilers/node-lifecycle/requested-stops.d.ts +5 -0
  505. package/dist/daemon/reconcilers/node-lifecycle/requested-stops.js +2 -0
  506. package/dist/daemon/reconcilers/node-lifecycle/tick.js +1 -1
  507. package/dist/daemon/reconcilers/pending-review-submit.js +1 -1
  508. package/dist/daemon/reconcilers/recipe-image.d.ts +49 -0
  509. package/dist/daemon/reconcilers/recipe-image.js +7 -0
  510. package/dist/daemon/reconcilers/row-claim.d.ts +19 -0
  511. package/dist/daemon/reconcilers/row-claim.js +2 -0
  512. package/dist/daemon/reconcilers/storage-due-work.d.ts +18 -3
  513. package/dist/daemon/reconcilers/storage-due-work.js +2 -1
  514. package/dist/daemon/reconcilers/storage-maintenance.d.ts +5 -13
  515. package/dist/daemon/reconcilers/storage-maintenance.js +1 -1
  516. package/dist/daemon/reconcilers/structured-request-lane.js +1 -1
  517. package/dist/daemon/reconcilers/throttled-lane.d.ts +11 -0
  518. package/dist/daemon/reconcilers/throttled-lane.js +1 -0
  519. package/dist/daemon/review/finish.js +1 -1
  520. package/dist/daemon/run-notices.d.ts +3 -0
  521. package/dist/daemon/run-notices.js +1 -1
  522. package/dist/daemon/runs/append-section.d.ts +4 -1
  523. package/dist/daemon/runs/append-section.js +2 -2
  524. package/dist/daemon/runs/observer.d.ts +3 -2
  525. package/dist/daemon/runs/observer.js +3 -2
  526. package/dist/daemon/runs/view-transport.d.ts +19 -0
  527. package/dist/daemon/runs/view-transport.js +1 -0
  528. package/dist/daemon/structured-requests/boot.d.ts +2 -1
  529. package/dist/daemon/structured-requests/boot.js +1 -1
  530. package/dist/daemon/structured-requests/effects.d.ts +4 -3
  531. package/dist/daemon/structured-requests/effects.js +1 -1
  532. package/dist/daemon/structured-requests/notifier.d.ts +2 -0
  533. package/dist/daemon/structured-requests/notifier.js +1 -1
  534. package/dist/daemon/structured-requests/sidecar.d.ts +4 -3
  535. package/dist/daemon/structured-requests/sidecar.js +1 -1
  536. package/dist/launcher/validate.js +1 -1
  537. package/dist/migrations/004-canvas-documents/index.js +2 -2
  538. package/dist/pi-extensions/broker-local.d.ts +3 -3
  539. package/dist/pi-extensions/broker-local.js +1 -2
  540. package/dist/pi-extensions/canvas-bash-valve.js +6 -6
  541. package/dist/pi-extensions/canvas-context-intro.js +2 -2
  542. package/dist/pi-extensions/canvas-elapsed.js +2 -2
  543. package/dist/pi-extensions/canvas-goal-capture.js +1 -1
  544. package/dist/pi-extensions/canvas-inbox-watcher.js +2 -2
  545. package/dist/pi-extensions/canvas-review-boundary.d.ts +11 -1
  546. package/dist/pi-extensions/canvas-review-boundary.js +3 -3
  547. package/dist/pi-extensions/canvas-stophook.js +1 -1
  548. package/dist/pi-extensions/canvas-structured-output.d.ts +5 -3
  549. package/dist/pi-extensions/canvas-structured-output.js +6 -11
  550. package/dist/pi-extensions/canvas-tool-guide.d.ts +9 -2
  551. package/dist/pi-extensions/canvas-tool-guide.js +6 -6
  552. package/dist/shared/inbox-entry-body.js +7 -7
  553. package/dist/types.d.ts +12 -0
  554. package/docs/sdk/release-notes.md +4 -0
  555. package/package.json +7 -4
  556. package/packages/crouter-identity/package.json +1 -1
  557. package/runtime.lock.json +13 -13
  558. package/scripts/git-credential-crtr +33 -0
  559. package/scripts/sandbox-bootstrap.sh +54 -0
  560. package/scripts/sandbox-start.mjs +320 -0
  561. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +0 -78
  562. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +0 -1179
  563. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +0 -1406
  564. package/dist/core/conversation-store/file-store.d.ts +0 -37
  565. package/dist/core/conversation-store/file-store.js +0 -9
  566. package/dist/core/conversation-store/listener.d.ts +0 -4
  567. package/dist/core/conversation-store/listener.js +0 -8
  568. package/dist/core/scoped-state/profiles.d.ts +0 -27
  569. package/dist/core/scoped-state/profiles.js +0 -1
  570. package/dist/core/scoped-state/providers.d.ts +0 -24
  571. package/dist/core/scoped-state/providers.js +0 -1
  572. package/dist/core/scoped-state/settings.d.ts +0 -28
  573. package/dist/core/scoped-state/settings.js +0 -1
@@ -18,3 +18,23 @@ export declare function waitForBrokerViewSocket(nodeId: string, exited?: Promise
18
18
  code: number | null;
19
19
  signal: NodeJS.Signals | null;
20
20
  }>, waitMs?: number): Promise<boolean>;
21
+ /** The port a sandboxed broker serves `/view` on (127.0.0.1 only; the private preview forwards it). */
22
+ export declare const BROKER_VIEW_PORT = 4180;
23
+ /** The relay's end of one broker view connection: a view.sock {@link Socket}, or {@link connectWssView}'s link. Bytes in
24
+ * both directions are newline-terminated frames; `close` carries whether an error preceded it. */
25
+ export interface BrokerViewLink {
26
+ readonly destroyed: boolean;
27
+ write(line: string): unknown;
28
+ destroy(): unknown;
29
+ on(event: 'connect', listener: () => void): this;
30
+ on(event: 'data', listener: (chunk: Buffer) => void): this;
31
+ on(event: 'error', listener: (error: NodeJS.ErrnoException) => void): this;
32
+ on(event: 'close', listener: (hadError: boolean) => void): this;
33
+ }
34
+ /** A broker's `/view` WebSocket as a {@link BrokerViewLink}. Each broker message is one frame, delivered as one
35
+ * newline-terminated chunk; each line written is sent as one text message. The headers (the preview token) are
36
+ * sent on the handshake only and kept nowhere else. */
37
+ export declare function connectWssView(url: string, headers: Record<string, string>): BrokerViewLink;
38
+ /** Resolve true once the broker's `/view` WebSocket accepts a handshake within `waitMs`, retrying while it refuses;
39
+ * false on the deadline or when the broker's launch handle exits. */
40
+ export declare function waitForWssView(url: string, headers: Record<string, string>, exited?: Promise<unknown>, waitMs?: number): Promise<boolean>;
@@ -1 +1,3 @@
1
- var w=Object.defineProperty;var a=(t,e)=>w(t,"name",{value:e,configurable:!0});import{createConnection as p,Socket as S}from"node:net";import{nodeRunDir as _,viewSocketPath as h}from"../canvas/paths.js";import{isBeneathRefusal as x,socketBeneath as E}from"../spaces/open-beneath.js";const u=3e4,l=100,B=u;function m(t){return new Promise(e=>{setTimeout(e,t)})}a(m,"sleep");function d(t){let e;try{e=E(_(t),h(t))}catch(c){const n=new S;return process.nextTick(()=>n.destroy(c)),n}const i=p(e.path);return i.once("connect",e.release),i.once("close",e.release),i}a(d,"connectViewSocket");function O(t,e){return new Promise((i,c)=>{let n=!1,r=null;const s=a(o=>{n||(n=!0,r!==null&&r.destroy(),o instanceof Error?c(o):i(o))},"finish");r=d(t),r.once("connect",()=>s(!0)),r.once("error",o=>s(x(o)?o:!1)),r.setTimeout(e,()=>s(!1))})}a(O,"probeViewSocket");async function C(t,e,i=u){const c=Date.now()+i,n=e?.then(()=>"exit");for(;Date.now()<=c;){const r=Math.max(0,c-Date.now()),s=O(t,Math.min(l,r)).then(f=>f?"ready":"retry"),o=n===void 0?await s:await Promise.race([s,n]);if(o==="ready")return!0;if(o==="exit")return!1;if(n!==void 0){if(await Promise.race([m(l).then(()=>"sleep"),n])==="exit")return!1}else await m(l)}return!1}a(C,"waitForBrokerViewSocket");export{B as BROKER_VIEW_SOCKET_WAIT_MS,d as connectViewSocket,O as probeViewSocket,C as waitForBrokerViewSocket};
1
+ var m=Object.defineProperty;var a=(n,e)=>m(n,"name",{value:e,configurable:!0});import{EventEmitter as p}from"node:events";import{createConnection as E,Socket as S}from"node:net";import{WebSocket as u}from"ws";import{CLIENT_READ_CAPS as x}from"./broker-protocol.js";import{nodeRunDir as y,viewSocketPath as O}from"../canvas/paths.js";import{isBeneathRefusal as _,socketBeneath as k}from"../spaces/open-beneath.js";const w=3e4,l=100,A=w;function d(n){return new Promise(e=>{setTimeout(e,n)})}a(d,"sleep");function R(n){let e;try{e=k(y(n),O(n))}catch(t){const r=new S;return process.nextTick(()=>r.destroy(t)),r}const s=E(e.path);return s.once("connect",e.release),s.once("close",e.release),s}a(R,"connectViewSocket");function T(n,e){return new Promise((s,t)=>{let r=!1,o=null;const f=a(i=>{r||(r=!0,o!==null&&o.destroy(),i instanceof Error?t(i):s(i))},"finish");o=R(n),o.once("connect",()=>f(!0)),o.once("error",i=>f(_(i)?i:!1)),o.setTimeout(e,()=>f(!1))})}a(T,"probeViewSocket");async function K(n,e,s=w){const t=Date.now()+s,r=e?.then(()=>"exit");for(;Date.now()<=t;){const o=Math.max(0,t-Date.now()),f=T(n,Math.min(l,o)).then(c=>c?"ready":"retry"),i=r===void 0?await f:await Promise.race([f,r]);if(i==="ready")return!0;if(i==="exit")return!1;if(r!==void 0){if(await Promise.race([d(l).then(()=>"sleep"),r])==="exit")return!1}else await d(l)}return!1}a(K,"waitForBrokerViewSocket");const I=4180;function C(n,e){return new B(n,e)}a(C,"connectWssView");class B extends p{static{a(this,"WssViewLink")}ws;failed=!1;ended=!1;terminated=!1;pending=[];constructor(e,s){super(),this.ws=new u(e,{headers:s,maxPayload:x.maxLineBytes}),this.ws.on("open",()=>{for(const t of this.pending.splice(0))this.ws.send(t);this.emit("connect")}),this.ws.on("message",t=>{const r=Array.isArray(t)?Buffer.concat(t):Buffer.isBuffer(t)?t:Buffer.from(new Uint8Array(t));this.emit("data",r.at(-1)===10?r:Buffer.concat([r,Buffer.from(`
2
+ `)]))}),this.ws.on("unexpected-response",(t,r)=>{this.fail(Object.assign(new Error(`broker view refused the upgrade (${r.statusCode??"no status"})`),{code:"ECONNREFUSED"})),this.ws.terminate()}),this.ws.on("error",t=>this.fail(t)),this.ws.on("close",t=>{this.ended||(t===1006&&!this.terminated&&this.fail(Object.assign(new Error("broker view connection dropped (1006)"),{code:"ECONNRESET"})),this.ended=!0,this.emit("close",this.failed))})}get destroyed(){return this.ended||this.ws.readyState===u.CLOSING||this.ws.readyState===u.CLOSED}write(e){const s=e.endsWith(`
3
+ `)?e.slice(0,-1):e;return this.ws.readyState===u.CONNECTING?(this.pending.push(s),!0):this.ws.readyState!==u.OPEN?!1:(this.ws.send(s),!0)}destroy(){return this.terminated=!0,this.ws.terminate(),this}fail(e){this.failed||(this.failed=!0,this.emit("error",e))}}async function M(n,e,s,t=w){const r=Date.now()+t;let o=!1;for(s?.then(()=>{o=!0});Date.now()<=r&&!o;){if(await new Promise(i=>{const c=C(n,e),h=setTimeout(()=>{c.destroy(),i(!1)},Math.max(1,r-Date.now()));c.on("connect",()=>{clearTimeout(h),c.destroy(),i(!0)}),c.on("error",()=>{}),c.on("close",()=>{clearTimeout(h),i(!1)})}))return!0;if(Date.now()+l>r)break;await d(l)}return!1}a(M,"waitForWssView");export{I as BROKER_VIEW_PORT,A as BROKER_VIEW_SOCKET_WAIT_MS,R as connectViewSocket,C as connectWssView,T as probeViewSocket,K as waitForBrokerViewSocket,M as waitForWssView};
@@ -1,4 +1,4 @@
1
- import type { Mode, OutcomeDeliveryBinding } from '../canvas/index.js';
1
+ import type { LaunchSpec, Mode, OutcomeDeliveryBinding } from '../canvas/index.js';
2
2
  /** Kill switch. `CRTR_WARM_POOL=0` makes every create cold again: claims stop,
3
3
  * refills stop, and daemon-start pre-warm no-ops. Read live (not cached at
4
4
  * module load) so flipping it takes effect on the next `crtr sys daemon
@@ -29,6 +29,13 @@ export interface WarmRequest {
29
29
  * claimant's, so provenance survives the warm path. */
30
30
  creator: string | null;
31
31
  }
32
+ /** The frozen half of a request, resolved. */
33
+ export interface ResolvedRecipe {
34
+ key: string;
35
+ /** The launch spec the CLAIMANT gets — model included. */
36
+ launch: LaunchSpec;
37
+ request: WarmRequest;
38
+ }
32
39
  /** Claim a spare for `request`, reshaped onto its persona — or null when the
33
40
  * pool cannot serve it. The caller falls back to a cold spawn; a refused
34
41
  * create hook leaves the spare untouched and rejects the create. `beforeClaimDelivery`
@@ -53,10 +60,15 @@ export declare function resumeWarmClaim(request: WarmRequest, nodeId: string, be
53
60
  * new owner. Only after winning the marker is the broker killed and the row +
54
61
  * dir deleted. Never throws: a failed reap is retried on the next sweep. */
55
62
  export declare function reapStaleSpares(): Promise<string[]>;
63
+ /** Claims the lease and renews it until the returned release runs; undefined when another holder has it. */
64
+ export declare function holdMintLease(lane: string, key: string): Promise<(() => Promise<void>) | undefined>;
56
65
  /** Bring the pool for `request` back to depth 1, in the background. Never
57
66
  * throws and never blocks a caller: a failed mint just means the next create
58
67
  * for this key is cold again. */
59
68
  export declare function refillWarmPool(request: WarmRequest): void;
69
+ /** Births one spare as node `nodeId`. The node's mint lease is held before the node exists and until its birth settles, so no
70
+ * process's reaper can take the spare for a dead broker; when the lease is not obtained nothing is born. */
71
+ export declare function birthSpare({ key, request }: ResolvedRecipe, nodeId: string): Promise<void>;
60
72
  /** Mint a spare for the most recently used profiles so the FIRST create after
61
73
  * crtrd boots is warm too, not only the second. The profile is the whole
62
74
  * recipe: it supplies the cwd, and everything else a claim reshapes live.
@@ -1 +1 @@
1
- var _=Object.defineProperty;var n=(e,r)=>_(e,"name",{value:r,configurable:!0});import{createHash as x}from"node:crypto";import{claimWarmSpare as N,deleteNode as $,getNode as p,listNodes as C,listWarmSpares as b,registerWarmSpare as R,unregisterWarmSpare as W,updateNode as P,warmSpareCount as w}from"../canvas/index.js";import{recordedPidLiveness as L}from"../canvas/pid.js";import{nowIso as y}from"../fs-utils.js";import{admitNodeCreate as M}from"../command-hooks/lifecycle-create.js";import{spawnChildPrepared as A}from"./spawn.js";import{checkStart as E}from"./start-check.js";import{buildLaunchSpecAsync as O}from"./launch.js";import{setModelLive as I}from"./model-swap.js";import{setSituationalContextLive as T}from"./situational-live.js";import{deliverBearingsLive as D}from"./deliver-live.js";import{closeNode as h}from"./close.js";function g(){return process.env.CRTR_WARM_POOL!=="0"}n(g,"warmPoolEnabled");function j(e,r,t){const a=Object.entries(t.env??{}).sort(([s],[i])=>s<i?-1:s>i?1:0),o=JSON.stringify([e,r,t.tools??[],t.extensions??[],t.systemPrompt??null,a]);return x("sha256").update(o).digest("hex").slice(0,32)}n(j,"warmRecipeKey");async function d(e){const{launch:r}=await O(e.kind,e.mode,{lifecycle:"resident",hasManager:!1,...e.model!==null?{model:e.model}:{},cwd:e.cwd,profileId:e.profileId});return{key:j(e.cwd,e.profileId,r),launch:r,request:e}}n(d,"resolveRecipe");async function k(e){const r=e.creator===null?null:await p(e.creator);await E({grantee:r?.grantee??"app:terminal",sub:r?.sub,grant_id:r?.grant_id})}n(k,"checkClaimantStart");async function re(e,r={},t){await k(e);const a=await d(e);if(await w(a.key)===0)return null;const o=y(),s=await M({kind:e.kind,mode:e.mode,cwd:e.cwd,profile:e.profileId,root:!0,parentId:null,requestingNodeId:e.creator,lifecycle:"resident",replaces:null},e.creator===null?"app:terminal":await(async()=>{const c=await p(e.creator);if(!c?.grantee)throw new Error("warm node creator has no app");return c.grantee})());let i;try{i=await N(a.key,o,r)}finally{s()}return i===null?null:v(i,a,o,t)}n(re,"claimWarmNode");async function te(e,r,t){return await k(e),v(r,await d(e),y(),t)}n(te,"resumeWarmClaim");async function v(e,r,t,a){const{request:o}=r,i=(await p(e))?.launch?.model;await P(e,{created:t,kind:o.kind,mode:o.mode,name:o.name??o.kind,creator:o.creator,launch:r.launch,persona_ack:{kind:o.kind,mode:o.mode,lifecycle:"resident"},...o.model!==null?{model_override:o.model}:{}});try{a?.(e)}catch(l){return await m(e,"birth preparation",l)}try{await D(e)}catch(l){return await m(e,"bearings delivery",l)}const c=r.launch.model;if(c!==void 0&&c!==i)try{await I(e,c,r.launch.modelIntent)}catch(l){return await m(e,"model swap",l)}if(o.situationalContext!==null)try{await T(e,o.situationalContext)}catch(l){return await m(e,"situational-context delivery",l)}return e}n(v,"completeWarmClaim");async function m(e,r,t){console.error(`[warm-pool] ${r} failed on claim; closing the spare: ${t.message}`);try{await h(e)}catch{}return null}n(m,"abandonSpare");const B=3600*1e3;async function ae(){const e=[];let r;try{r=await b()}catch(a){return console.error(`[warm-pool] spare scan failed: ${a.message}`),e}const t=Date.now()-B;for(const a of r){if(u.has(a.node_id))continue;const o=Date.parse(a.created),s=Number.isNaN(o)||o<t,i=L(a.pi_pid,a.pi_pid_identity),c=(a.status==="active"||a.status==="idle")&&i==="alive";if(!(c&&!s)&&!(i==="indeterminate"&&!s))try{if(!await W(a.node_id))continue;if(c)try{await h(a.node_id)}catch{}await $(a.node_id),e.push(a.node_id)}catch(l){console.error(`[warm-pool] reap failed for ${a.node_id}: ${l.message}`)}}return e}n(ae,"reapStaleSpares");const f=new Set,u=new Set;function oe(e){if(g())try{d({...e,model:null,situationalContext:null}).then(S).catch(r=>console.error(`[warm-pool] recipe resolution failed: ${r.message}`))}catch(r){console.error(`[warm-pool] recipe resolution failed: ${r.message}`)}}n(oe,"refillWarmPool");async function S(e){const{key:r,request:t}=e;f.has(r)||await w(r)>0||(f.add(r),(async()=>{let a=null;try{await A({kind:t.kind,mode:t.mode,cwd:t.cwd,prompt:"",root:!0,readiness:"await",profile:t.profileId},async o=>{a=o,u.add(o),await R(o,r)})}catch(o){console.error(`[warm-pool] mint failed for ${t.kind}: ${o.message}`)}finally{a!==null&&u.delete(a),f.delete(r)}})())}n(S,"mint");const F=2;async function ne(){if(!g())return;let e;try{const r=new Set;e=[];for(const t of(await C({status:["active","idle"]})).reverse()){if(t.parent!==null||t.kind==="human")continue;const a=t.profile_id??null;if(!r.has(a??"")&&(r.add(a??""),e.push({cwd:t.cwd,profileId:a}),e.length>=F))break}}catch(r){console.error(`[warm-pool] prewarm scan failed: ${r.message}`);return}for(const r of e)try{d({kind:"general",mode:"base",cwd:r.cwd,profileId:r.profileId,model:null,situationalContext:null,name:null,creator:null}).then(S).catch(t=>console.error(`[warm-pool] prewarm failed for ${r.cwd}: ${t.message}`))}catch(t){console.error(`[warm-pool] prewarm failed for ${r.cwd}: ${t.message}`)}}n(ne,"prewarmRecentRecipes");export{re as claimWarmNode,ne as prewarmRecentRecipes,ae as reapStaleSpares,oe as refillWarmPool,te as resumeWarmClaim,g as warmPoolEnabled};
1
+ var k=Object.defineProperty;var n=(e,r)=>k(e,"name",{value:r,configurable:!0});import{createHash as x,randomUUID as N}from"node:crypto";import{openDb as R}from"../canvas/db.js";import{claimReconcilerLease as $,releaseReconcilerLease as L,renewReconcilerLease as C}from"../canvas/reconciler-leases.js";import{claimWarmSpare as b,deleteNode as E,getNode as p,listNodes as W,listWarmSpares as M,registerWarmSpare as P,unregisterWarmSpare as A,updateNode as I,warmSpareCount as w}from"../canvas/index.js";import{recordedPidLiveness as D}from"../canvas/pid.js";import{nowIso as u}from"../fs-utils.js";import{admitNodeCreate as O}from"../command-hooks/lifecycle-create.js";import{spawnChildPrepared as T}from"./spawn.js";import{newNodeId as H}from"./nodes.js";import{checkStart as F}from"./start-check.js";import{buildLaunchSpecAsync as K}from"./launch.js";import{setModelLive as U}from"./model-swap.js";import{setSituationalContextLive as j}from"./situational-live.js";import{deliverBearingsLive as B}from"./deliver-live.js";import{closeNode as y}from"./close.js";function h(){return process.env.CRTR_WARM_POOL!=="0"}n(h,"warmPoolEnabled");function G(e,r,a){const t=Object.entries(a.env??{}).sort(([s],[i])=>s<i?-1:s>i?1:0),o=JSON.stringify([e,r,a.tools??[],a.extensions??[],a.systemPrompt??null,t]);return x("sha256").update(o).digest("hex").slice(0,32)}n(G,"warmRecipeKey");async function d(e){const{launch:r}=await K(e.kind,e.mode,{lifecycle:"resident",hasManager:!1,...e.model!==null?{model:e.model}:{},cwd:e.cwd,profileId:e.profileId});return{key:G(e.cwd,e.profileId,r),launch:r,request:e}}n(d,"resolveRecipe");async function g(e){const r=e.creator===null?null:await p(e.creator);await F({grantee:r?.grantee??"app:terminal",sub:r?.sub,grant_id:r?.grant_id})}n(g,"checkClaimantStart");async function pe(e,r={},a){await g(e);const t=await d(e);if(await w(t.key)===0)return null;const o=u(),s=await O({kind:e.kind,mode:e.mode,cwd:e.cwd,profile:e.profileId,root:!0,parentId:null,requestingNodeId:e.creator,lifecycle:"resident",replaces:null},e.creator===null?"app:terminal":await(async()=>{const c=await p(e.creator);if(!c?.grantee)throw new Error("warm node creator has no app");return c.grantee})());let i;try{i=await b(t.key,o,r)}finally{s()}return i===null?null:v(i,t,o,a)}n(pe,"claimWarmNode");async function fe(e,r,a){return await g(e),v(r,await d(e),u(),a)}n(fe,"resumeWarmClaim");async function v(e,r,a,t){const{request:o}=r,i=(await p(e))?.launch?.model;await I(e,{created:a,kind:o.kind,mode:o.mode,name:o.name??o.kind,creator:o.creator,launch:r.launch,persona_ack:{kind:o.kind,mode:o.mode,lifecycle:"resident"},...o.model!==null?{model_override:o.model}:{}});try{t?.(e)}catch(l){return await m(e,"birth preparation",l)}try{await B(e)}catch(l){return await m(e,"bearings delivery",l)}const c=r.launch.model;if(c!==void 0&&c!==i)try{await U(e,c,r.launch.modelIntent)}catch(l){return await m(e,"model swap",l)}if(o.situationalContext!==null)try{await j(e,o.situationalContext)}catch(l){return await m(e,"situational-context delivery",l)}return e}n(v,"completeWarmClaim");async function m(e,r,a){console.error(`[warm-pool] ${r} failed on claim; closing the spare: ${a.message}`);try{await y(e)}catch{}return null}n(m,"abandonSpare");const J=3600*1e3;async function we(){const e=[];let r;try{r=await M()}catch(t){return console.error(`[warm-pool] spare scan failed: ${t.message}`),e}const a=Date.now()-J;for(const t of r){if(await z(t.node_id))continue;const o=Date.parse(t.created),s=Number.isNaN(o)||o<a,i=D(t.pi_pid,t.pi_pid_identity),c=(t.status==="active"||t.status==="idle")&&i==="alive";if(!(c&&!s)&&!(i==="indeterminate"&&!s))try{if(!await A(t.node_id))continue;if(c)try{await y(t.node_id)}catch{}await E(t.node_id),e.push(t.node_id)}catch(l){console.error(`[warm-pool] reap failed for ${t.node_id}: ${l.message}`)}}return e}n(we,"reapStaleSpares");const f=6e4;async function _(e,r){const a=N();if(await $(e,r,a,f)===void 0)return;const t=setInterval(()=>{C(e,r,a,f).catch(()=>{})},f/3);return t.unref(),async()=>{clearInterval(t),await L(e,r,a)}}n(_,"holdMintLease");async function z(e){return await(await R()).prepare("SELECT 1 FROM reconciler_leases WHERE lane = ? AND key = ? AND expires_at > ?").get("warm-pool-mint",e,new Date().toISOString())!==void 0}n(z,"mintLeaseHeld");function ue(e){if(h())try{d({...e,model:null,situationalContext:null}).then(S).catch(r=>console.error(`[warm-pool] recipe resolution failed: ${r.message}`))}catch(r){console.error(`[warm-pool] recipe resolution failed: ${r.message}`)}}n(ue,"refillWarmPool");async function S(e){const{key:r}=e,a=await _("warm-pool",r);if(a!==void 0){if(await w(r)>0){await a();return}Q(e,await H()).finally(()=>a().catch(()=>{}))}}n(S,"mint");async function Q({key:e,request:r},a){const t=await _("warm-pool-mint",a);if(t===void 0){console.error(`[warm-pool] mint refused for ${r.kind}: the mint lease of ${a} is held elsewhere`);return}try{await T({nodeId:a,kind:r.kind,mode:r.mode,cwd:r.cwd,prompt:"",root:!0,readiness:"await",profile:r.profileId},async o=>{await P(o,e)})}catch(o){console.error(`[warm-pool] mint failed for ${r.kind}: ${o.message}`)}finally{await t().catch(()=>{})}}n(Q,"birthSpare");const V=2;async function ye(){if(!h())return;let e;try{const r=new Set;e=[];for(const a of(await W({status:["active","idle"]})).reverse()){if(a.parent!==null||a.kind==="human")continue;const t=a.profile_id??null;if(!r.has(t??"")&&(r.add(t??""),e.push({cwd:a.cwd,profileId:t}),e.length>=V))break}}catch(r){console.error(`[warm-pool] prewarm scan failed: ${r.message}`);return}for(const r of e)try{d({kind:"general",mode:"base",cwd:r.cwd,profileId:r.profileId,model:null,situationalContext:null,name:null,creator:null}).then(S).catch(a=>console.error(`[warm-pool] prewarm failed for ${r.cwd}: ${a.message}`))}catch(a){console.error(`[warm-pool] prewarm failed for ${r.cwd}: ${a.message}`)}}n(ye,"prewarmRecentRecipes");export{Q as birthSpare,pe as claimWarmNode,_ as holdMintLease,ye as prewarmRecentRecipes,we as reapStaleSpares,ue as refillWarmPool,fe as resumeWarmClaim,h as warmPoolEnabled};
@@ -0,0 +1,11 @@
1
+ import type { PersonSnapshot } from './person-store.js';
2
+ /** The old user store's contents in the shape of a person's snapshot, or null when this install never had a store. */
3
+ export declare function readLegacyUserSnapshot(root: string): PersonSnapshot | null;
4
+ /** Pre-canvas per-project memory caps (`none|name|preview`) the canvas import has not converted yet. */
5
+ export declare function listLegacyProjectCaps(root: string): Array<{
6
+ profileId: string;
7
+ path: string;
8
+ memory: string;
9
+ }>;
10
+ /** Delete every legacy cap row once the canvas import has stored the converted limits on the profile rows. */
11
+ export declare function deleteLegacyProjectCaps(root: string): void;
@@ -0,0 +1 @@
1
+ var y=Object.defineProperty;var r=(s,a)=>y(s,"name",{value:a,configurable:!0});import{prepared as o}from"../sqlite-statements.js";import{userStore as c}from"./paths.js";import{openScopedStore as u,scopedStoreExists as i,withScopedWrite as g}from"./db.js";function S(s){const a=c(s);if(!i(a))return null;const t=u(a),m=o(t,"SELECT profile_id, path FROM profile_projects ORDER BY profile_id, position").all(),E=o(t,"SELECT id, name, created_at, last_used_at, paused_at, default_kind, grantee, metadata_json, delivery_limits_json, environment_id FROM profiles ORDER BY rowid").all().map(e=>{const n=JSON.parse(e.metadata_json),f=JSON.parse(e.delivery_limits_json);return{profileId:e.id,manifest:{schema_version:1,name:e.name,projects:m.filter(_=>_.profile_id===e.id).map(_=>({path:_.path})),paused_at:e.paused_at,...e.default_kind===null?{}:{default_kind:e.default_kind},...e.grantee===null?{}:{grantee:e.grantee},...e.environment_id===null?{}:{environment_id:e.environment_id},...Object.keys(f).length===0?{}:{delivery_limits:f},...Object.keys(n).length===0?{}:{metadata:n},created_at:e.created_at,last_used_at:e.last_used_at}}}),k=Object.fromEntries(o(t,"SELECT directory, profile_id FROM profile_defaults").all().map(e=>[e.directory,e.profile_id])),p={};for(const e of o(t,"SELECT owner_key, raw_json FROM scope_settings WHERE owner_key != 'project'").all())p[e.owner_key]=JSON.parse(e.raw_json);const d={},l=r(e=>d[e]??={marketplaces:{},plugins:{},activeCanvas:null},"stateOf");for(const e of o(t,"SELECT owner_key, last_self_check, active_canvas FROM scope_bookkeeping WHERE owner_key != 'project'").all()){const n=l(e.owner_key);e.last_self_check!==null&&(n.last_self_check=e.last_self_check),n.activeCanvas=e.active_canvas}for(const e of o(t,"SELECT owner_key, name, last_updated FROM scope_marketplaces WHERE owner_key != 'project'").all())l(e.owner_key).marketplaces[e.name]=e.last_updated===null?{}:{last_updated:e.last_updated};for(const e of o(t,"SELECT owner_key, name, last_updated, etag, checked_at FROM scope_plugins WHERE owner_key != 'project'").all())l(e.owner_key).plugins[e.name]={...e.last_updated===null?{}:{last_updated:e.last_updated},...e.etag===null?{}:{etag:e.etag},...e.checked_at===null?{}:{checked_at:e.checked_at}};return{profiles:E,defaults:k,settings:p,state:d}}r(S,"readLegacyUserSnapshot");function v(s){const a=c(s);return i(a)?o(u(a),"SELECT profile_id, path, memory FROM legacy_project_caps ORDER BY profile_id, path").all().map(t=>({profileId:t.profile_id,path:t.path,memory:t.memory})):[]}r(v,"listLegacyProjectCaps");function w(s){const a=c(s);i(a)&&g(a,t=>{o(t,"DELETE FROM legacy_project_caps").run()})}r(w,"deleteLegacyProjectCaps");export{w as deleteLegacyProjectCaps,v as listLegacyProjectCaps,S as readLegacyUserSnapshot};
@@ -0,0 +1,18 @@
1
+ /** One enabled plugin row of the person's user scope, with its marketplace's url and ref. */
2
+ export interface PluginInstall {
3
+ name: string;
4
+ version: string | null;
5
+ sourcePath: string | null;
6
+ marketplace: {
7
+ name: string;
8
+ url: string | null;
9
+ ref: string | null;
10
+ } | null;
11
+ }
12
+ /** The directory holding this person's materialized plugins as `<dir>/<plugin name>`; undefined when none were materialized. */
13
+ export declare function materializedPluginsDir(person: string): string | undefined;
14
+ /** Install `installs` for `person` at `revision` into this process's plugin cache, once per (person, revision). When a plugin cannot
15
+ * be installed — a source on another machine, a marketplace that does not list it, a failed clone — the failure is held for that person
16
+ * and thrown by the next read of their plugins (`materializedPluginsDir`), so a request that never reads plugins, such as the one that
17
+ * fixes the settings, still works; the next load retries. */
18
+ export declare function materializePlugins(person: string, revision: number, installs: readonly PluginInstall[]): Promise<void>;
@@ -0,0 +1 @@
1
+ var z=Object.defineProperty;var u=(e,n)=>z(e,"name",{value:n,configurable:!0});import{cpSync as S,existsSync as h,mkdirSync as b,renameSync as M,rmSync as w}from"node:fs";import{tmpdir as A}from"node:os";import{isAbsolute as $,join as l,relative as P,resolve as v}from"node:path";import{CrtrError as c}from"../errors.js";import{gitAsync as x}from"../git.js";import{readMarketplaceManifest as D}from"../manifest.js";const m=new Map,d=new Map;function q(e){const n=m.get(e);if(n!==void 0&&"failure"in n)throw n.failure;return n?.dir}u(q,"materializedPluginsDir");function y(e){return l(A(),"crtr-plugin-cache",e.replace(/[^A-Za-z0-9._-]/g,"_"))}u(y,"cacheRoot");async function _(e,n,o,r){const t=await x(["clone","--depth","1",...n?["--branch",n]:[],e,o]);if(t.status!==0)throw new c("plugin_install_failed",`${r}: git clone ${e} failed: ${t.stderr.trim()}`)}u(_,"cloneInto");async function R(e,n){const o=new Map;b(l(e,"plugins"),{recursive:!0});for(const r of n){if(r.sourcePath!==null)throw new c("plugin_source_unreachable",`${r.name}: its source is ${r.sourcePath} on the machine that registered it, which the control plane cannot read`);const t=r.marketplace;if(t===null)throw new c("plugin_source_unreachable",`${r.name}: it names neither a marketplace nor a path`);if(t.url===null||t.url==="")throw new c("plugin_source_unreachable",`${r.name}: its marketplace ${t.name} has no url`);const s=`${t.url}\0${t.ref??""}`;let a=o.get(s);a===void 0&&(a=l(e,"marketplaces",String(o.size)),await _(t.url,t.ref,a,`${r.name}: marketplace ${t.name}`),o.set(s,a));const i=D(a)?.plugins.find(k=>k.name===r.name);if(i===void 0)throw new c("plugin_install_failed",`${r.name}: marketplace ${t.name} (${t.url}) does not list it`);const f=l(e,"plugins",r.name);if($(i.source)||/^[a-z][a-z0-9+.-]*:|^git@/i.test(i.source)||i.source.endsWith(".git")){await _(i.source,null,f,r.name);continue}const p=v(a,i.source),g=P(a,p);if(g.startsWith("..")||$(g))throw new c("plugin_install_failed",`${r.name}: its source ${i.source} leaves the marketplace`);if(!h(p))throw new c("plugin_install_failed",`${r.name}: its source ${i.source} is not in marketplace ${t.name}`);S(p,f,{recursive:!0})}w(l(e,"marketplaces"),{recursive:!0,force:!0})}u(R,"installAll");async function B(e,n,o){const r=m.get(e);if(r?.revision===n&&!("failure"in r))return;const t=l(y(e),String(n)),s=`${e}\0${n}`;let a=d.get(s);a===void 0&&(a=(async()=>{if(!h(l(t,"plugins"))){const i=l(y(e),`${n}.${process.pid}.${Date.now()}`);try{await R(i,o);try{M(i,t)}catch(f){if(!h(l(t,"plugins")))throw f}}finally{w(i,{recursive:!0,force:!0})}}return l(t,"plugins")})().finally(()=>d.delete(s)),d.set(s,a));try{m.set(e,{revision:n,dir:await a})}catch(i){if(!(i instanceof c))throw i;m.set(e,{revision:n,failure:i})}}u(B,"materializePlugins");export{B as materializePlugins,q as materializedPluginsDir};
@@ -1,4 +1,4 @@
1
- export { initializeFreshScopedStore } from './db.js';
1
+ import { type PersonSnapshot } from './person-store.js';
2
2
  /** Conversion is intentionally explicit. Deployment coordination must prove all legacy writers at these shared roots are stopped before calling it. */
3
3
  export declare function convertLegacyScopedStore(input: {
4
4
  kind: 'user' | 'project' | 'provider';
@@ -6,3 +6,10 @@ export declare function convertLegacyScopedStore(input: {
6
6
  }): void;
7
7
  /** Converts legacy provider pools only after the activation owner has stopped every writer sharing this agent directory. */
8
8
  export declare function convertLegacyProviderStore(agentDir: string): Promise<void>;
9
+ export declare function personStoresCopied(): Promise<boolean>;
10
+ /** Copy this install's files into the person's rows, once: profiles, scope settings and bookkeeping, the plugin and marketplace
11
+ * lists, managed model accounts with their cooldowns, rotation settings, the OpenAI device id, `auth.json` API keys and the profile, app and deployment env files (sealed into `env_vars`). One
12
+ * transaction, skipping any key a row already holds, ending with `canvas_meta.person_stores_copied`. The files stay, unread. */
13
+ export declare function copyPersonStoresToRows(person: string, legacyUserSnapshot: PersonSnapshot | null, agentDir: string): Promise<void>;
14
+ /** The install's person just became known: copy the old local files into their rows once, then load their snapshot. */
15
+ export declare function adoptInstallPerson(person: string): Promise<void>;
@@ -1,3 +1,3 @@
1
- var U=Object.defineProperty;var f=(e,r)=>U(e,"name",{value:r,configurable:!0});import{prepared as p}from"../sqlite-statements.js";import{randomUUID as x}from"node:crypto";import{chmodSync as I,copyFileSync as P,existsSync as m,mkdirSync as w,readFileSync as _,readdirSync as V,statSync as N}from"node:fs";import{dirname as L,join as u,relative as J}from"node:path";import W from"proper-lockfile";import{CONFIG_FILE as b,PROFILE_DIR as D,STATE_FILE as v}from"../../types.js";import{LockedJsonFile as C}from"../locked-file.js";import{assertScopedStoreReady as q,createScopedStore as j,discardIncompleteScopedStore as z,legacyScopedStorePaths as O,PROFILE_ID_SHAPE as G,scopedStoreExists as g,withProjectStateLock as H,writeOwnerRoot as K}from"./db.js";import{canonicalRoot as M,providerStore as T,projectStore as X,userStore as B}from"./paths.js";import{profileSpace as Q}from"../layout.js";import{createScopedSchema as F}from"./schema.js";function h(e){return JSON.parse(_(e,"utf8"))}f(h,"json");function R(e){return e.some(m)}f(R,"existsLegacy");function E(e,r){const o=u(e,".legacy-scoped-state");try{w(o,{mode:448})}catch(a){if(a.code!=="EEXIST"||!N(o).isDirectory())throw a}I(o,448);for(const a of r)if(m(a)&&N(a).isFile()){const n=J(e,a);if(n===""||n.startsWith(".."))throw new Error(`legacy state is outside its owning root: ${a}`);const t=u(o,n);L(t)!==o&&w(L(t),{recursive:!0,mode:448}),P(a,t),I(t,384)}}f(E,"preserve");import{initializeFreshScopedStore as ge}from"./db.js";function me(e){const r=e.kind==="user"?B(e.root):e.kind==="project"?X(e.root):T(e.root);if(e.kind!=="project"&&g(r))return;if(e.kind==="provider")throw new Error("provider conversion requires convertLegacyProviderStore() under coordinated shared-agent-dir activation");const o=O(r),a=f(()=>{e.kind==="project"&&z(r),!g(r)&&(e.kind==="project"&&R(o)&&E(r.root,o),j(r,n=>{F(n,e.kind);const t=e.kind==="user"?"user":"project";K(n,t,r.root),e.kind==="user"?re(n,r.root):k(n,t,u(r.root,v))}),e.kind==="user"&&R(o)&&E(r.root,o))},"convert");e.kind==="project"?H(r.root,()=>{a(),q(r)}):a()}f(me,"convertLegacyScopedStore");function Y(e){if(!m(e))return[];const r=JSON.parse(_(e,"utf8"));if(!Array.isArray(r))throw new Error(`legacy subscription pool is malformed: ${e}`);const o=new Set;return r.map((a,n)=>{if(a===null||typeof a!="object"||Array.isArray(a))throw new Error(`legacy subscription pool entry ${n+1} is malformed: ${e}`);const t=a,s=typeof t.label=="string"?t.label.trim():"",c=typeof t.refresh=="string"?t.refresh.trim():"",i=typeof t.access=="string"?t.access.trim():"",d=Number(t.expires);if(!s||!c||!i||!Number.isFinite(d))throw new Error(`legacy subscription pool entry ${n+1} is incomplete: ${e}`);const y=s.toLowerCase();if(o.has(y))throw new Error(`legacy subscription pool has duplicate label "${s}": ${e}`);o.add(y);const l=f(A=>Number.isFinite(Number(t[A]))?Number(t[A]):0,"timing"),S=typeof t.accountId=="string"&&t.accountId.trim()!==""?t.accountId.trim():void 0;return{label:s,refresh:c,access:i,expires:d,rateLimitedUntil:l("rateLimitedUntil"),lastAttemptAt:l("lastAttemptAt"),lastRateLimitedAt:l("lastRateLimitedAt"),...t.authFailure==="invalid_grant"?{authFailure:"invalid_grant"}:{},...S?{accountId:S}:{}}})}f(Y,"parseLegacyProviderPool");function Z(e){if(!m(e))return{};const r=JSON.parse(_(e,"utf8"));if(r===null||typeof r!="object"||Array.isArray(r))throw new Error(`legacy provider rotation settings are malformed: ${e}`);const o=r;return{...o.defaultFallbackStrength==="ultra"||o.defaultFallbackStrength==="strong"||o.defaultFallbackStrength==="medium"||o.defaultFallbackStrength==="light"?{defaultFallbackStrength:o.defaultFallbackStrength}:{},...typeof o.revertWhenAvailable=="boolean"?{revertWhenAvailable:o.revertWhenAvailable}:{}}}f(Z,"parseLegacyRotation");async function ee(e,r){const o=[];try{for(const a of e)m(a)&&o.push(await W.lock(a,{realpath:!1,retries:0,stale:3e4,update:1e4}));return await r()}finally{await Promise.allSettled(o.reverse().map(a=>a()))}}f(ee,"withLegacyProviderLocks");async function ye(e){const r=T(e);if(g(r))return;const o=O(r);await ee(o,async()=>{if(g(r))return;const a=new Map(o.slice(0,2).map((t,s)=>[s===0?"anthropic":"openai-codex",Y(t)])),n=Z(o[2]);w(r.root,{recursive:!0,mode:448}),E(r.root,o);for(const[t,s]of a){const c=Object.fromEntries(s.map(d=>[x(),{refresh:d.refresh,access:d.access,expires:d.expires,...d.accountId?{accountId:d.accountId}:{}}]));await new C(u(r.root,`${t}-credentials.json`),!0,`{"version":1,"accounts":{}}
2
- `).withLockAsync(async(d,y,l)=>(l(`${JSON.stringify({version:1,accounts:c})}
3
- `),{result:void 0}))}j(r,t=>{F(t,"provider");for(const[s,c]of a){const i=JSON.parse(_(u(r.root,`${s}-credentials.json`),"utf8")),d=Object.keys(i.accounts),y=p(t,"INSERT INTO managed_accounts(provider_id,id,label,label_key,position,last_attempt_at,last_rate_limited_at,rate_limited_until,auth_failure) VALUES(?,?,?,?,?,?,?,?,?)");c.forEach((l,S)=>y.run(s,d[S],l.label,l.label.toLowerCase(),S,l.lastAttemptAt,l.lastRateLimitedAt,l.rateLimitedUntil,l.authFailure??null))}(n.defaultFallbackStrength!==void 0||n.revertWhenAvailable!==void 0)&&p(t,"INSERT INTO rotation_settings(singleton,default_fallback_strength,revert_when_available) VALUES(1,?,?)").run(n.defaultFallbackStrength??null,n.revertWhenAvailable===void 0?null:n.revertWhenAvailable?1:0)})})}f(ye,"convertLegacyProviderStore");function $(e,r,o){if(!m(o))return;const a=h(o);if(a===null||typeof a!="object"||Array.isArray(a))throw new Error(`legacy config is malformed: ${o}`);p(e,"INSERT INTO scope_settings(owner_key,raw_json) VALUES(?,?)").run(r,JSON.stringify(a))}f($,"importSettings");function k(e,r,o){if(!m(o))return;const a=h(o);if(a===null||typeof a!="object"||Array.isArray(a))throw new Error(`legacy state is malformed: ${o}`);const n=a;p(e,"INSERT INTO scope_bookkeeping(owner_key,last_self_check,active_canvas) VALUES(?,?,?)").run(r,n.last_self_check??null,n.activeCanvas??null);const t=p(e,"INSERT INTO scope_marketplaces(owner_key,name,last_updated) VALUES(?,?,?)");for(const[c,i]of Object.entries(n.marketplaces??{}))t.run(r,c,i.last_updated??null);const s=p(e,"INSERT INTO scope_plugins(owner_key,name,last_updated,etag,checked_at) VALUES(?,?,?,?,?)");for(const[c,i]of Object.entries(n.plugins??{}))s.run(r,c,i.last_updated??null,i.etag??null,i.checked_at??null)}f(k,"importState");function re(e,r){$(e,"user",u(r,b)),k(e,"user",u(r,v));const o=u(r,D);if(m(o))for(const n of V(o,{withFileTypes:!0})){if(!n.isDirectory()||!G.test(n.name))continue;const t=n.name,s=u(o,t),c=u(s,"profile.json");if(!m(c))throw new Error(`legacy profile manifest is missing: ${c}`);const i=h(c);if(!i||typeof i!="object"||!Array.isArray(i.projects)||typeof i.name!="string"||typeof i.created_at!="string")throw new Error(`legacy profile manifest is malformed: ${c}`);p(e,"INSERT INTO profiles(id,name,created_at,last_used_at,paused_at,default_kind,grantee,metadata_json) VALUES(?,?,?,?,?,?,?,?)").run(t,i.name,i.created_at,i.last_used_at??null,i.paused_at??null,i.default_kind??null,i.grantee??null,JSON.stringify(i.metadata??{})),p(e,"INSERT INTO scope_owners(owner_key,kind,root,profile_id) VALUES(?,?,?,?)").run(`profile:${t}`,"profile",M(Q(i.grantee??"app:terminal",t).dir),t);const d=p(e,"INSERT INTO profile_projects(profile_id,path,position) VALUES(?,?,?)"),y=p(e,"INSERT INTO legacy_project_caps(profile_id,path,memory) VALUES(?,?,?)");i.projects.forEach((l,S)=>{d.run(t,l.path,S),l.memory!==void 0&&l.memory!=="content"&&y.run(t,l.path,l.memory)}),$(e,`profile:${t}`,u(s,b)),k(e,`profile:${t}`,u(s,v))}const a=u(r,"profile-defaults.json");if(m(a)){const n=h(a);if(n===null||typeof n!="object"||Array.isArray(n))throw new Error(`legacy profile defaults are malformed: ${a}`);const t=p(e,"INSERT INTO profile_defaults(directory,profile_id) VALUES(?,?)");for(const[s,c]of Object.entries(n)){if(typeof c!="string")throw new Error(`legacy profile default is malformed: ${a}`);t.run(s,c)}}}f(re,"importUser");export{ye as convertLegacyProviderStore,me as convertLegacyScopedStore,ge as initializeFreshScopedStore};
1
+ var K=Object.defineProperty;var p=(e,t)=>K(e,"name",{value:t,configurable:!0});import{prepared as E}from"../sqlite-statements.js";import{randomUUID as Y}from"node:crypto";import{chmodSync as R,copyFileSync as G,existsSync as y,mkdirSync as g,readFileSync as S,readdirSync as b,statSync as I}from"node:fs";import{basename as B,dirname as j,join as m,relative as X}from"node:path";import q from"proper-lockfile";import{CONFIG_FILE as F,PROFILE_DIR as z,STATE_FILE as k}from"../../types.js";import{LockedJsonFile as Q}from"../locked-file.js";import{assertScopedStoreReady as Z,createScopedStore as C,openScopedStore as $,discardIncompleteScopedStore as ee,legacyScopedStorePaths as D,PROFILE_ID_SHAPE as te,scopedStoreExists as v,withProjectStateLock as re,writeOwnerRoot as oe}from"./db.js";import{canonicalRoot as ae,providerStore as h,projectStore as ie,userStore as ne}from"./paths.js";import{profileSpace as P,appSpace as se}from"../layout.js";import{createScopedSchema as x}from"./schema.js";import{openDb as ce,withCanvasWrite as le}from"../canvas/db.js";import{loadSecretsKey as fe,seal as de}from"../secrets/index.js";import{putEnvRowIfAbsent as pe}from"../secrets/env-rows.js";import{DEPLOYMENT_ID as ue}from"../secrets/environment-secrets.js";import{isReservedEnvName as V,validateEnvVarName as me}from"../env-name.js";import{insertAccount as _e,upsertModelCooldown as ye}from"../subscription-state.js";import{userScopeRoot as Ee}from"../scope.js";import{secretsDir as U}from"../layout.js";import{readLegacyUserSnapshot as we}from"./legacy-profiles.js";import{revisionOf as ve}from"./person-store.js";import{loadPersonSettings as Se,applyOpsToRows as he,readSnapshotFromRows as ge}from"./person-rows.js";function w(e){return JSON.parse(S(e,"utf8"))}p(w,"json");function W(e){return e.some(y)}p(W,"existsLegacy");function O(e,t){const o=m(e,".legacy-scoped-state");try{g(o,{mode:448})}catch(a){if(a.code!=="EEXIST"||!I(o).isDirectory())throw a}R(o,448);for(const a of t)if(y(a)&&I(a).isFile()){const i=X(e,a);if(i===""||i.startsWith(".."))throw new Error(`legacy state is outside its owning root: ${a}`);const r=m(o,i);j(r)!==o&&g(j(r),{recursive:!0,mode:448}),G(a,r),R(r,384)}}p(O,"preserve");function Ie(e){const t=e.kind==="user"?ne(e.root):e.kind==="project"?ie(e.root):h(e.root);if(e.kind!=="project"&&v(t))return;if(e.kind==="provider")throw new Error("provider conversion requires convertLegacyProviderStore() under coordinated shared-agent-dir activation");const o=D(t),a=p(()=>{e.kind==="project"&&ee(t),!v(t)&&(e.kind==="project"&&W(o)&&O(t.root,o),C(t,i=>{x(i,e.kind);const r=e.kind==="user"?"user":"project";oe(i,r,t.root),e.kind==="user"?Ae(i,t.root):N(i,r,m(t.root,k))}),e.kind==="user"&&W(o)&&O(t.root,o))},"convert");e.kind==="project"?re(t.root,()=>{a(),Z(t)}):a()}p(Ie,"convertLegacyScopedStore");function ke(e){if(!y(e))return[];const t=JSON.parse(S(e,"utf8"));if(!Array.isArray(t))throw new Error(`legacy subscription pool is malformed: ${e}`);const o=new Set;return t.map((a,i)=>{if(a===null||typeof a!="object"||Array.isArray(a))throw new Error(`legacy subscription pool entry ${i+1} is malformed: ${e}`);const r=a,l=typeof r.label=="string"?r.label.trim():"",d=typeof r.refresh=="string"?r.refresh.trim():"",n=typeof r.access=="string"?r.access.trim():"",s=Number(r.expires);if(!l||!d||!n||!Number.isFinite(s))throw new Error(`legacy subscription pool entry ${i+1} is incomplete: ${e}`);const c=l.toLowerCase();if(o.has(c))throw new Error(`legacy subscription pool has duplicate label "${l}": ${e}`);o.add(c);const f=p(u=>Number.isFinite(Number(r[u]))?Number(r[u]):0,"timing"),_=typeof r.accountId=="string"&&r.accountId.trim()!==""?r.accountId.trim():void 0;return{label:l,refresh:d,access:n,expires:s,rateLimitedUntil:f("rateLimitedUntil"),lastAttemptAt:f("lastAttemptAt"),lastRateLimitedAt:f("lastRateLimitedAt"),...r.authFailure==="invalid_grant"?{authFailure:"invalid_grant"}:{},..._?{accountId:_}:{}}})}p(ke,"parseLegacyProviderPool");function Oe(e){if(!y(e))return{};const t=JSON.parse(S(e,"utf8"));if(t===null||typeof t!="object"||Array.isArray(t))throw new Error(`legacy provider rotation settings are malformed: ${e}`);const o=t;return{...o.defaultFallbackStrength==="ultra"||o.defaultFallbackStrength==="strong"||o.defaultFallbackStrength==="medium"||o.defaultFallbackStrength==="light"?{defaultFallbackStrength:o.defaultFallbackStrength}:{},...typeof o.revertWhenAvailable=="boolean"?{revertWhenAvailable:o.revertWhenAvailable}:{}}}p(Oe,"parseLegacyRotation");async function Ne(e,t){const o=[];try{for(const a of e)y(a)&&o.push(await q.lock(a,{realpath:!1,retries:0,stale:3e4,update:1e4}));return await t()}finally{await Promise.allSettled(o.reverse().map(a=>a()))}}p(Ne,"withLegacyProviderLocks");async function rt(e){const t=h(e);if(v(t))return;const o=D(t);await Ne(o,async()=>{if(v(t))return;const a=new Map(o.slice(0,2).map((r,l)=>[l===0?"anthropic":"openai-codex",ke(r)])),i=Oe(o[2]);g(t.root,{recursive:!0,mode:448}),O(t.root,o);for(const[r,l]of a){const d=Object.fromEntries(l.map(s=>[Y(),{refresh:s.refresh,access:s.access,expires:s.expires,...s.accountId?{accountId:s.accountId}:{}}]));await new Q(m(t.root,`${r}-credentials.json`),!0,`{"version":1,"accounts":{}}
2
+ `).withLockAsync(async(s,c,f)=>(f(`${JSON.stringify({version:1,accounts:d})}
3
+ `),{result:void 0}))}C(t,r=>{x(r,"provider");for(const[l,d]of a){const n=JSON.parse(S(m(t.root,`${l}-credentials.json`),"utf8")),s=Object.keys(n.accounts),c=E(r,"INSERT INTO managed_accounts(provider_id,id,label,label_key,position,last_attempt_at,last_rate_limited_at,rate_limited_until,auth_failure) VALUES(?,?,?,?,?,?,?,?,?)");d.forEach((f,_)=>c.run(l,s[_],f.label,f.label.toLowerCase(),_,f.lastAttemptAt,f.lastRateLimitedAt,f.rateLimitedUntil,f.authFailure??null))}(i.defaultFallbackStrength!==void 0||i.revertWhenAvailable!==void 0)&&E(r,"INSERT INTO rotation_settings(singleton,default_fallback_strength,revert_when_available) VALUES(1,?,?)").run(i.defaultFallbackStrength??null,i.revertWhenAvailable===void 0?null:i.revertWhenAvailable?1:0)})})}p(rt,"convertLegacyProviderStore");function M(e,t,o){if(!y(o))return;const a=w(o);if(a===null||typeof a!="object"||Array.isArray(a))throw new Error(`legacy config is malformed: ${o}`);E(e,"INSERT INTO scope_settings(owner_key,raw_json) VALUES(?,?)").run(t,JSON.stringify(a))}p(M,"importSettings");function N(e,t,o){if(!y(o))return;const a=w(o);if(a===null||typeof a!="object"||Array.isArray(a))throw new Error(`legacy state is malformed: ${o}`);const i=a;E(e,"INSERT INTO scope_bookkeeping(owner_key,last_self_check,active_canvas) VALUES(?,?,?)").run(t,i.last_self_check??null,i.activeCanvas??null);const r=E(e,"INSERT INTO scope_marketplaces(owner_key,name,last_updated) VALUES(?,?,?)");for(const[d,n]of Object.entries(i.marketplaces??{}))r.run(t,d,n.last_updated??null);const l=E(e,"INSERT INTO scope_plugins(owner_key,name,last_updated,etag,checked_at) VALUES(?,?,?,?,?)");for(const[d,n]of Object.entries(i.plugins??{}))l.run(t,d,n.last_updated??null,n.etag??null,n.checked_at??null)}p(N,"importState");function Ae(e,t){M(e,"user",m(t,F)),N(e,"user",m(t,k));const o=m(t,z);if(y(o))for(const i of b(o,{withFileTypes:!0})){if(!i.isDirectory()||!te.test(i.name))continue;const r=i.name,l=m(o,r),d=m(l,"profile.json");if(!y(d))throw new Error(`legacy profile manifest is missing: ${d}`);const n=w(d);if(!n||typeof n!="object"||!Array.isArray(n.projects)||typeof n.name!="string"||typeof n.created_at!="string")throw new Error(`legacy profile manifest is malformed: ${d}`);E(e,"INSERT INTO profiles(id,name,created_at,last_used_at,paused_at,default_kind,grantee,metadata_json) VALUES(?,?,?,?,?,?,?,?)").run(r,n.name,n.created_at,n.last_used_at??null,n.paused_at??null,n.default_kind??null,n.grantee??null,JSON.stringify(n.metadata??{})),E(e,"INSERT INTO scope_owners(owner_key,kind,root,profile_id) VALUES(?,?,?,?)").run(`profile:${r}`,"profile",ae(P(n.grantee??"app:terminal",r).dir),r);const s=E(e,"INSERT INTO profile_projects(profile_id,path,position) VALUES(?,?,?)"),c=E(e,"INSERT INTO legacy_project_caps(profile_id,path,memory) VALUES(?,?,?)");n.projects.forEach((f,_)=>{s.run(r,f.path,_),f.memory!==void 0&&f.memory!=="content"&&c.run(r,f.path,f.memory)}),M(e,`profile:${r}`,m(l,F)),N(e,`profile:${r}`,m(l,k))}const a=m(t,"profile-defaults.json");if(y(a)){const i=w(a);if(i===null||typeof i!="object"||Array.isArray(i))throw new Error(`legacy profile defaults are malformed: ${a}`);const r=E(e,"INSERT INTO profile_defaults(directory,profile_id) VALUES(?,?)");for(const[l,d]of Object.entries(i)){if(typeof d!="string")throw new Error(`legacy profile default is malformed: ${a}`);r.run(l,d)}}}p(Ae,"importUser");const A="person_stores_copied",L={anthropic:"anthropic-credentials.json","openai-codex":"openai-codex-credentials.json",openai:"openai-credentials.json"};async function Le(){return await(await ce()).prepare("SELECT value FROM canvas_meta WHERE key = ?").get(A)!==void 0}p(Le,"personStoresCopied");function Te(e){const t=h(e);if(!v(t))return[];const o=$(t),a=[];for(const i of Object.keys(L)){const r=m(e,L[i]);if(!y(r))continue;const l=w(r),d=E(o,"SELECT account_id,family,rate_limited_until,last_rate_limited_at FROM managed_account_model_cooldowns WHERE provider_id=?").all(i);for(const n of E(o,"SELECT id,label,label_key,position,last_attempt_at,last_rate_limited_at,rate_limited_until,auth_failure FROM managed_accounts WHERE provider_id=? ORDER BY position").all(i)){const s=l.accounts?.[n.id];s!==void 0&&a.push({provider:i,id:n.id,row:{providerId:i,id:n.id,label:n.label,labelKey:n.label_key,position:n.position,lastAttemptAt:n.last_attempt_at,lastRateLimitedAt:n.last_rate_limited_at,rateLimitedUntil:n.rate_limited_until,...n.auth_failure?{authFailure:n.auth_failure}:{}},tokens:{refresh:s.refresh,access:s.access,expires:s.expires,...s.accountId?{accountId:s.accountId}:{},...s.clientId?{clientId:s.clientId}:{}},cooldowns:d.filter(c=>c.account_id===n.id).map(c=>({family:c.family,until:c.rate_limited_until,at:c.last_rate_limited_at}))})}}return a}p(Te,"readLegacyAccounts");function T(e,t){if(!y(e)||!I(e).isFile())return{};let o;try{o=w(e)}catch{return{}}const a={};if(o===null||typeof o!="object"||o.vars===null||typeof o.vars!="object"||Array.isArray(o.vars))return a;for(const[i,r]of Object.entries(o.vars))me(i).isValid&&t(i)&&typeof r=="string"&&!r.includes("\0")&&(a[i]=r);return a}p(T,"readLegacyEnvFile");const Re=new Set(["CRTR_FEEDBACK_ENDPOINT","CRTR_FEEDBACK_TOKEN"]);async function be(e,t,o){const a=m(U(),"env");if(!y(a))return;const i=p(async(r,l)=>{for(const[d,n]of Object.entries(l))await pe(e,r,d,n)},"put");for(const r of o)await i({person:t,kind:"profile",id:r.profileId},T(P(r.manifest.grantee??"app:terminal",r.profileId).env,l=>!V(l)));for(const r of b(a,{withFileTypes:!0})){if(!r.isDirectory())continue;const l=`app:${r.name}`;B(se(l).root)===r.name&&await i({person:t,kind:"app",id:l},T(m(a,r.name,"_app"),d=>!V(d)))}await i({person:null,kind:"deployment",id:ue},T(m(a,"_deployment"),r=>Re.has(r)))}p(be,"copyLegacyEnvFiles");async function je(e,t,o){const a=Te(o),i=(()=>{const s=h(o);if(v(s))return E($(s),"SELECT default_fallback_strength,revert_when_available FROM rotation_settings WHERE singleton=1").get()})(),r=m(o,"openai-device-id"),l=y(r)?S(r,"utf8").trim():"",d=m(o,"auth.json"),n=y(d)?Object.entries(w(d)).filter(([s])=>!(s in L)):[];await le(async s=>{if(await s.prepare("SELECT 1 FROM canvas_meta WHERE key = ?").get(A)===void 0){if(t!==null){const c=await ge(s,e),f=[],_=new Set(c.profiles.map(u=>u.profileId));for(const u of t.profiles)_.has(u.profileId)||f.push({op:"profile.insert",profile:u});for(const u of Object.keys(t.settings))c.settings[u]===void 0&&f.push({op:"settings.replace",owner:u,raw:t.settings[u],ifRevision:ve(void 0)});for(const u of Object.keys(t.state)){const H=t.state[u];c.state[u]===void 0&&(u==="user"||_.has(u.replace(/^profile:/,""))||t.profiles.some(J=>`profile:${J.profileId}`===u))&&f.push({op:"state.write",owner:u,state:H})}Object.keys(c.defaults).length===0&&Object.keys(t.defaults).length>0&&f.push({op:"defaults.replace",defaults:t.defaults}),f.length>0&&await he(s,e,f)}for(const c of a)if(await s.prepare("SELECT 1 FROM model_accounts WHERE person=? AND provider_id=? AND (id=? OR label_key=?)").get(e,c.provider,c.id,c.row.labelKey)===void 0){await _e(s,e,c.provider,c.row,c.tokens);for(const _ of c.cooldowns)await ye(s,e,c.provider,c.id,_.family,_.until,_.at)}i!==void 0&&await s.prepare("INSERT INTO model_settings(person,default_fallback_strength,revert_when_available) VALUES(?,?,?) ON CONFLICT(person) DO NOTHING").run(e,i.default_fallback_strength,i.revert_when_available),/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(l)&&await s.prepare("INSERT INTO model_settings(person,openai_device_id) VALUES(?,?) ON CONFLICT(person) DO UPDATE SET openai_device_id=COALESCE(model_settings.openai_device_id,EXCLUDED.openai_device_id)").run(e,l.toLowerCase());for(const[c,f]of n){const{sealed:_,keyId:u}=de(JSON.stringify(f),fe(),`model_api_keys|${e}|${c}`);await s.prepare("INSERT INTO model_api_keys(person,provider_id,credential_sealed,key_id,updated_at) VALUES(?,?,?,?,?) ON CONFLICT(person,provider_id) DO NOTHING").run(e,c,_,u,new Date().toISOString())}await be(s,e,t?.profiles??[]),await s.prepare("INSERT INTO canvas_meta(key,value) VALUES(?,?)").run(A,new Date().toISOString())}})}p(je,"copyPersonStoresToRows");async function ot(e){if(!await Le()){const t=Ee();Ie({kind:"user",root:t}),await je(e,we(t),U())}await Se(e)}p(ot,"adoptInstallPerson");export{ot as adoptInstallPerson,rt as convertLegacyProviderStore,Ie as convertLegacyScopedStore,je as copyPersonStoresToRows,Le as personStoresCopied};
@@ -0,0 +1,10 @@
1
+ import { type CanvasDb } from '../canvas/db.js';
2
+ import { type PersonSnapshot, type SettingsOp, type SettingsTransport } from './person-store.js';
3
+ export declare function readSnapshotFromRows(db: CanvasDb, person: string): Promise<PersonSnapshot>;
4
+ /** Load (or reload) one person's snapshot from the rows into this process. */
5
+ export declare function loadPersonSettings(person: string): Promise<PersonSnapshot>;
6
+ /** Apply ops to the rows inside the caller's transaction. Bumps `person_settings.plugins_revision` when a plugin or marketplace
7
+ * block changed, and signals `settings:<person>` in the same transaction so other processes holding the person reload. */
8
+ export declare function applyOpsToRows(db: CanvasDb, person: string, ops: readonly SettingsOp[]): Promise<void>;
9
+ /** The process holds the database: write the rows in one transaction, then reload the person from the committed rows. */
10
+ export declare const rowsTransport: SettingsTransport;
@@ -0,0 +1,6 @@
1
+ var O=Object.defineProperty;var _=(a,n)=>O(a,"name",{value:n,configurable:!0});import{openDb as S,withCanvasWrite as N}from"../canvas/db.js";import{isCloudRole as R}from"../canvas/person-state.js";import{materializePlugins as T}from"./materialized-plugins.js";import{revisionOf as y,setPersonSnapshot as D,setSettingsReloader as v,setSettingsTransport as L,SettingsRevisionConflictError as C}from"./person-store.js";const s=_(a=>typeof a=="string"?a:void 0,"s");async function w(a,n){const o=await a.prepare("SELECT * FROM profiles WHERE person = ? AND deleting_since IS NULL ORDER BY created_at, id").all(n),f=await a.prepare("SELECT p.profile_id, p.path FROM profile_projects p JOIN profiles f ON f.id = p.profile_id WHERE f.person = ? ORDER BY p.profile_id, p.position").all(n),r=new Map;for(const e of f)(r.get(e.profile_id)??r.set(e.profile_id,[]).get(e.profile_id)).push({path:e.path});const t=o.map(e=>{const l=JSON.parse(String(e.metadata_json)),p=JSON.parse(String(e.delivery_limits_json));return{profileId:String(e.id),manifest:{schema_version:1,name:String(e.name),projects:r.get(String(e.id))??[],paused_at:s(e.paused_at)??null,...s(e.default_kind)===void 0?{}:{default_kind:s(e.default_kind)},...s(e.grantee)===void 0?{}:{grantee:s(e.grantee)},...s(e.environment_id)===void 0?{}:{environment_id:s(e.environment_id)},...Object.keys(p).length===0?{}:{delivery_limits:p},...Object.keys(l).length===0?{}:{metadata:l},created_at:String(e.created_at),last_used_at:s(e.last_used_at)??null}}}),i=Object.fromEntries((await a.prepare("SELECT directory, profile_id FROM profile_defaults WHERE person = ?").all(n)).map(e=>[e.directory,e.profile_id])),u={};for(const e of await a.prepare("SELECT owner_key, raw_json FROM scope_settings WHERE person = ?").all(n))u[e.owner_key]=JSON.parse(e.raw_json);const m={},E=_(e=>m[e]??={marketplaces:{},plugins:{},activeCanvas:null},"stateOf");for(const e of await a.prepare("SELECT owner_key, last_self_check, active_canvas FROM scope_bookkeeping WHERE person = ?").all(n)){const l=E(e.owner_key);e.last_self_check!==null&&(l.last_self_check=e.last_self_check),l.activeCanvas=e.active_canvas}for(const e of await a.prepare("SELECT * FROM scope_marketplaces WHERE person = ? ORDER BY owner_key, name").all(n)){const l=String(e.owner_key),p=String(e.name);if(E(l).marketplaces[p]=s(e.last_updated)===void 0?{}:{last_updated:s(e.last_updated)},s(e.url)!==void 0){const d=u[l]??={},c=d.marketplaces??={};c[p]={url:s(e.url),ref:s(e.ref)??"",installed_at:s(e.installed_at)??""}}}for(const e of await a.prepare("SELECT * FROM scope_plugins WHERE person = ? ORDER BY owner_key, name").all(n)){const l=String(e.owner_key),p=String(e.name);E(l).plugins[p]={...s(e.last_updated)===void 0?{}:{last_updated:s(e.last_updated)},...s(e.etag)===void 0?{}:{etag:s(e.etag)},...s(e.checked_at)===void 0?{}:{checked_at:s(e.checked_at)}};const d=u[l]??={},c=d.plugins??={};c[p]={enabled:Number(e.enabled)===1,...s(e.source_marketplace)===void 0?{}:{source_marketplace:s(e.source_marketplace)},...s(e.version)===void 0?{}:{version:s(e.version)}}}return{profiles:t,defaults:i,settings:u,state:m}}_(w,"readSnapshotFromRows");async function g(a){const n=await S(),o=await w(n,a);return R()&&await I(n,a),D(a,o),o}_(g,"loadPersonSettings");async function I(a,n){const o=await a.prepare(`SELECT p.name, p.version, p.source_path, p.source_marketplace, m.url, m.ref FROM scope_plugins p
2
+ LEFT JOIN scope_marketplaces m ON m.person = p.person AND m.owner_key = p.owner_key AND m.name = p.source_marketplace
3
+ WHERE p.person = ? AND p.owner_key = 'user' AND p.enabled = 1 ORDER BY p.name`).all(n),f=await a.prepare("SELECT plugins_revision FROM person_settings WHERE person = ?").get(n),r=o.map(t=>({name:String(t.name),version:s(t.version)??null,sourcePath:s(t.source_path)??null,marketplace:s(t.source_marketplace)===void 0?null:{name:s(t.source_marketplace),url:s(t.url)??null,ref:s(t.ref)??null}}));await T(n,f===void 0?0:Number(f.plugins_revision),r)}_(I,"materializePersonPlugins");async function h(a,n,o){return(await w(a,n)).settings[o]}_(h,"currentRaw");async function j(a,n,o){let f=!1;for(const r of o)if(r.op==="settings.replace"){const t=await h(a,n,r.owner);if(r.ifRevision!==y(t))throw new C;const{plugins:i,marketplaces:u,...m}=r.raw,E=JSON.stringify(m);await a.prepare("INSERT INTO scope_settings (person, owner_key, raw_json) VALUES (?, ?, ?) ON CONFLICT (person, owner_key) DO UPDATE SET raw_json = EXCLUDED.raw_json").run(n,r.owner,E);const e=i??{},l=u??{},p=JSON.stringify([t?.plugins,t?.marketplaces]);p!==JSON.stringify([i,u])&&!(p==="[null,null]"&&i===void 0&&u===void 0)&&(f=!0),await a.prepare("DELETE FROM scope_plugins WHERE person = ? AND owner_key = ? AND NOT (name IN (SELECT jsonb_array_elements_text(?::jsonb)))").run(n,r.owner,JSON.stringify(Object.keys(e)));for(const[d,c]of Object.entries(e))await a.prepare(`INSERT INTO scope_plugins (person, owner_key, name, enabled, source_marketplace, version) VALUES (?, ?, ?, ?, ?, ?)
4
+ ON CONFLICT (person, owner_key, name) DO UPDATE SET enabled = EXCLUDED.enabled, source_marketplace = EXCLUDED.source_marketplace, version = EXCLUDED.version`).run(n,r.owner,d,c.enabled===!1?0:1,c.source_marketplace??null,c.version??null);await a.prepare("DELETE FROM scope_marketplaces WHERE person = ? AND owner_key = ? AND NOT (name IN (SELECT jsonb_array_elements_text(?::jsonb)))").run(n,r.owner,JSON.stringify(Object.keys(l)));for(const[d,c]of Object.entries(l))await a.prepare(`INSERT INTO scope_marketplaces (person, owner_key, name, url, ref, installed_at) VALUES (?, ?, ?, ?, ?, ?)
5
+ ON CONFLICT (person, owner_key, name) DO UPDATE SET url = EXCLUDED.url, ref = EXCLUDED.ref, installed_at = EXCLUDED.installed_at`).run(n,r.owner,d,c.url??null,c.ref??null,c.installed_at??null)}else if(r.op==="state.write"){await a.prepare(`INSERT INTO scope_bookkeeping (person, owner_key, last_self_check, active_canvas) VALUES (?, ?, ?, ?)
6
+ ON CONFLICT (person, owner_key) DO UPDATE SET last_self_check = EXCLUDED.last_self_check, active_canvas = EXCLUDED.active_canvas`).run(n,r.owner,r.state.last_self_check??null,r.state.activeCanvas??null);for(const[t,i]of Object.entries(r.state.marketplaces))await a.prepare("UPDATE scope_marketplaces SET last_updated = ? WHERE person = ? AND owner_key = ? AND name = ?").run(i.last_updated??null,n,r.owner,t);for(const[t,i]of Object.entries(r.state.plugins))await a.prepare("UPDATE scope_plugins SET last_updated = ?, etag = ?, checked_at = ? WHERE person = ? AND owner_key = ? AND name = ?").run(i.last_updated??null,i.etag??null,i.checked_at??null,n,r.owner,t)}else if(r.op==="profile.insert"){const t=r.profile.manifest;await a.prepare("INSERT INTO profiles (id, person, name, created_at, last_used_at, paused_at, default_kind, grantee, metadata_json, delivery_limits_json, environment_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run(r.profile.profileId,n,t.name,t.created_at,t.last_used_at,t.paused_at,t.default_kind??null,t.grantee??null,JSON.stringify(t.metadata??{}),JSON.stringify(t.delivery_limits??{}),t.environment_id??null),await k(a,r.profile)}else if(r.op==="profile.update"){const t=r.profile.manifest;if((await a.prepare("UPDATE profiles SET name = ?, last_used_at = ?, paused_at = ?, default_kind = ?, grantee = ?, metadata_json = ?, delivery_limits_json = ?, environment_id = ? WHERE id = ? AND person = ?").run(t.name,t.last_used_at,t.paused_at,t.default_kind??null,t.grantee??null,JSON.stringify(t.metadata??{}),JSON.stringify(t.delivery_limits??{}),t.environment_id??null,r.profile.profileId,n)).changes!==1)throw new Error(`no stored profile ${r.profile.profileId}`);await k(a,r.profile)}else if(r.op==="profile.delete"){const t=`profile:${r.profileId}`;for(const i of["scope_settings","scope_bookkeeping","scope_marketplaces","scope_plugins"])await a.prepare(`DELETE FROM ${i} WHERE person = ? AND owner_key = ?`).run(n,t);await a.prepare("DELETE FROM profiles WHERE id = ? AND person = ?").run(r.profileId,n)}else{await a.prepare("DELETE FROM profile_defaults WHERE person = ?").run(n);for(const[t,i]of Object.entries(r.defaults))await a.prepare("INSERT INTO profile_defaults (person, directory, profile_id) VALUES (?, ?, ?)").run(n,t,i)}f&&await a.prepare("INSERT INTO person_settings (person, plugins_revision) VALUES (?, 1) ON CONFLICT (person) DO UPDATE SET plugins_revision = person_settings.plugins_revision + 1").run(n),await a.prepare("SELECT pg_notify('crtr_signal', ?)").get(`settings:${n}`)}_(j,"applyOpsToRows");async function k(a,n){await a.prepare("DELETE FROM profile_projects WHERE profile_id = ?").run(n.profileId);let o=0;for(const f of n.manifest.projects)await a.prepare("INSERT INTO profile_projects (profile_id, path, position) VALUES (?, ?, ?)").run(n.profileId,f.path,o++)}_(k,"replaceProjects");const A=_(async(a,n)=>(await N(o=>j(o,a,n)),await g(a)),"rowsTransport");L(A),v(g);export{j as applyOpsToRows,g as loadPersonSettings,w as readSnapshotFromRows,A as rowsTransport};
@@ -0,0 +1,72 @@
1
+ import type { ProfileManifest, ScopeState } from '../../types.js';
2
+ export type StoredProfile = {
3
+ profileId: string;
4
+ manifest: ProfileManifest;
5
+ };
6
+ /** Everything one person's rows hold, in the shape the synchronous readers want. `settings` is keyed by owner (`user`,
7
+ * `profile:<id>`) and carries the rebuilt `plugins` and `marketplaces` blocks; `state` carries the bookkeeping. */
8
+ export interface PersonSnapshot {
9
+ profiles: StoredProfile[];
10
+ defaults: Record<string, string>;
11
+ settings: Record<string, Record<string, unknown>>;
12
+ state: Record<string, ScopeState>;
13
+ }
14
+ export type SettingsOp = {
15
+ op: 'settings.replace';
16
+ owner: string;
17
+ raw: Record<string, unknown>;
18
+ ifRevision: string;
19
+ } | {
20
+ op: 'state.write';
21
+ owner: string;
22
+ state: ScopeState;
23
+ } | {
24
+ op: 'profile.insert';
25
+ profile: StoredProfile;
26
+ } | {
27
+ op: 'profile.update';
28
+ profile: StoredProfile;
29
+ } | {
30
+ op: 'profile.delete';
31
+ profileId: string;
32
+ } | {
33
+ op: 'defaults.replace';
34
+ defaults: Record<string, string>;
35
+ };
36
+ export declare class SettingsRevisionConflictError extends Error {
37
+ constructor();
38
+ }
39
+ export declare class PersonSettingsNotLoadedError extends Error {
40
+ constructor(person: string);
41
+ }
42
+ declare const BLOCKS: readonly ["plugins", "marketplaces"];
43
+ export declare function revisionOf(raw: Record<string, unknown> | undefined): string;
44
+ export declare function installPersonSnapshot(person: string, snapshot: PersonSnapshot): void;
45
+ export declare function heldPersons(): string[];
46
+ export declare function dropPersonSnapshots(): void;
47
+ /** Before the install's person is resolved (a Mac's first boot reads its config to resolve it), reads answer from this snapshot of
48
+ * the old local store; `loadPersonSettings` replaces it once the person is known. */
49
+ export declare function installPreludeSnapshot(snapshot: PersonSnapshot | undefined): void;
50
+ /** The current person's snapshot. A miss is an error: a person whose rows exist never reads as defaults. A cloud role outside a
51
+ * request has no person and no install settings: it reads defaults. */
52
+ export declare function currentSnapshot(): PersonSnapshot;
53
+ export declare function personHasSnapshot(): boolean;
54
+ export type SettingsTransport = (person: string, ops: SettingsOp[]) => Promise<PersonSnapshot>;
55
+ export declare function setSettingsTransport(next: SettingsTransport): void;
56
+ /** Compute ops from the cached snapshot and send them; on a revision conflict reload (the transport returned or the rows hold a newer
57
+ * snapshot) and compute again, up to three times. A failed write throws and leaves the cache as it was. */
58
+ export declare function writeSettings(compute: (snapshot: PersonSnapshot) => SettingsOp[]): Promise<PersonSnapshot>;
59
+ export declare function setPersonSnapshot(person: string, snapshot: PersonSnapshot): void;
60
+ export declare function setSettingsReloader(next: (person: string) => Promise<PersonSnapshot>): void;
61
+ export declare function ownerKeyOf(profileId?: string): string;
62
+ export declare function readCachedSettings(owner: string): {
63
+ settings: Record<string, unknown> | null;
64
+ revision: string;
65
+ };
66
+ export declare function readCachedState(owner: string): ScopeState;
67
+ export { BLOCKS as SETTINGS_ROW_BLOCKS };
68
+ export declare function listCachedProfiles(): StoredProfile[];
69
+ export declare function getCachedProfile(profileId: string): StoredProfile | null;
70
+ export declare function cachedProfilePaused(profileId: string): boolean;
71
+ export declare function cachedPausedProfileIds(): Set<string>;
72
+ export declare function cachedDefaults(): Record<string, string>;
@@ -0,0 +1 @@
1
+ var l=Object.defineProperty;var n=(e,t)=>l(e,"name",{value:t,configurable:!0});import{createHash as h}from"node:crypto";import{PersonOutsideRequestError as x,PersonUnresolvedError as g,personId as u}from"../canvas/person-state.js";class S extends Error{static{n(this,"SettingsRevisionConflictError")}constructor(){super("settings changed since the supplied revision"),this.name="SettingsRevisionConflictError"}}class w extends Error{static{n(this,"PersonSettingsNotLoadedError")}constructor(t){super(`settings for ${t} are not loaded in this process`),this.name="PersonSettingsNotLoadedError"}}const P=["plugins","marketplaces"],s=new Map;function m(e){return e===void 0?"absent":h("sha256").update(JSON.stringify(e)).digest("hex")}n(m,"revisionOf");function O(e,t){s.set(e,t)}n(O,"installPersonSnapshot");function I(){return[...s.keys()]}n(I,"heldPersons");function R(){s.clear()}n(R,"dropPersonSnapshots");const f=n(()=>({profiles:[],defaults:{},settings:{},state:{}}),"emptySnapshot");let a;function L(e){a=e}n(L,"installPreludeSnapshot");function r(){let e;try{e=u()}catch(o){if(o instanceof x)return f();if(o instanceof g)return a??f();throw o}const t=s.get(e);if(t!==void 0)return t;if(a!==void 0)return a;throw new w(e)}n(r,"currentSnapshot");function N(){try{return s.has(u())}catch{return!1}}n(N,"personHasSnapshot");let d=n(async()=>{throw new Error("no settings transport installed in this process")},"transport");function T(e){d=e}n(T,"setSettingsTransport");async function _(e){const t=u();for(let o=0;;o++){const c=e(r());if(c.length===0)return r();try{const i=await d(t,c);return s.set(t,i),i}catch(i){if(!(i instanceof S)||o>=2)throw i;await C(t)}}}n(_,"writeSettings");function k(e,t){s.set(e,t),a=void 0}n(k,"setPersonSnapshot");let p;function K(e){p=e}n(K,"setSettingsReloader");async function C(e){s.set(e,await(p??(async()=>{throw new Error("no settings reloader installed in this process")}))(e))}n(C,"reloadViaTransport");function B(e){return e===void 0?"user":`profile:${e}`}n(B,"ownerKeyOf");function H(e){const t=r().settings[e];return{settings:t===void 0?null:structuredClone(t),revision:m(t)}}n(H,"readCachedSettings");function $(e){const t=r().state[e];return t===void 0?{marketplaces:{},plugins:{},activeCanvas:null}:structuredClone(t)}n($,"readCachedState");function b(){return structuredClone(r().profiles)}n(b,"listCachedProfiles");function q(e){const t=r().profiles.find(o=>o.profileId===e);return t===void 0?null:structuredClone(t)}n(q,"getCachedProfile");function D(e){return r().profiles.find(t=>t.profileId===e)?.manifest.paused_at!=null}n(D,"cachedProfilePaused");function G(){return new Set(r().profiles.filter(e=>e.manifest.paused_at!==null).map(e=>e.profileId))}n(G,"cachedPausedProfileIds");function J(){return{...r().defaults}}n(J,"cachedDefaults");export{w as PersonSettingsNotLoadedError,P as SETTINGS_ROW_BLOCKS,S as SettingsRevisionConflictError,J as cachedDefaults,G as cachedPausedProfileIds,D as cachedProfilePaused,r as currentSnapshot,R as dropPersonSnapshots,q as getCachedProfile,I as heldPersons,O as installPersonSnapshot,L as installPreludeSnapshot,b as listCachedProfiles,B as ownerKeyOf,N as personHasSnapshot,H as readCachedSettings,$ as readCachedState,m as revisionOf,k as setPersonSnapshot,K as setSettingsReloader,T as setSettingsTransport,_ as writeSettings};
@@ -0,0 +1,24 @@
1
+ import type { BlaxelLaunchContext } from '../runtime/runner/blaxel/runner.js';
2
+ import type { RunnerStartInput } from '../runtime/runner/types.js';
3
+ export declare const BASE_IMAGE = "blaxel/base-image:latest";
4
+ /** How long the signed release URL stays valid. */
5
+ export declare const RELEASE_URL_TTL_S: number;
6
+ export interface ReleaseSigner {
7
+ bucket: string;
8
+ endpoint: string;
9
+ accessKeyId: string;
10
+ secretAccessKey: string;
11
+ }
12
+ export declare function releaseSignerFromEnv(env?: NodeJS.ProcessEnv): ReleaseSigner;
13
+ /** A SigV4 query-signed GET (path style, region `auto`) of `key`, valid `ttlS` seconds from `now`. */
14
+ export declare function presignGet(signer: ReleaseSigner, key: string, ttlS: number, now?: Date): string;
15
+ export interface BlaxelLaunchDeps {
16
+ apiUrl: string;
17
+ signer: ReleaseSigner;
18
+ /** The sha256 text of `releases/<version>/runtime.tar.gz.sha256`; default: the blob store. */
19
+ releaseSha?: (version: string) => Promise<string | null>;
20
+ now?: () => Date;
21
+ }
22
+ /** Reads the control plane's settings from the environment: `CRTR_PUBLIC_API_URL` (where a sandbox reaches `/v1`) and the four `CRTR_R2_*` variables. */
23
+ export declare function blaxelLaunchDepsFromEnv(env?: NodeJS.ProcessEnv): BlaxelLaunchDeps;
24
+ export declare function createBlaxelLaunchContext(deps: BlaxelLaunchDeps): (input: RunnerStartInput) => Promise<BlaxelLaunchContext>;
@@ -0,0 +1,4 @@
1
+ var S=Object.defineProperty;var t=(r,e)=>S(r,"name",{value:e,configurable:!0});import{createHash as A,createHmac as E}from"node:crypto";import{openDb as C}from"../canvas/db.js";import{blobStore as $}from"../blobs/index.js";import{environmentOfNode as I}from"../../daemon/api/environment-of.js";import{EnvironmentImagePending as b}from"../../daemon/reconcilers/node-lifecycle/remote-wake.js";import{resolveEnvironmentImage as L}from"../../daemon/reconcilers/recipe-image.js";import{environmentSecrets as U}from"./environment-secrets.js";import{environmentOwnerProfile as x,profileInApp as y}from"./environment-owner.js";const T="blaxel/base-image:latest",v=900;function z(r=process.env){const e=t(o=>{const n=r[o];if(!n)throw new Error(`${o} is required to sign release URLs`);return n},"need");return{bucket:e("CRTR_R2_BUCKET"),endpoint:e("CRTR_R2_ENDPOINT"),accessKeyId:e("CRTR_R2_ACCESS_KEY_ID"),secretAccessKey:e("CRTR_R2_SECRET_ACCESS_KEY")}}t(z,"releaseSignerFromEnv");const D=t(r=>A("sha256").update(r).digest("hex"),"sha256hex"),f=t((r,e)=>E("sha256",r).update(e).digest(),"hmac"),h=t(r=>encodeURIComponent(r).replace(/[!'()*]/g,e=>`%${e.charCodeAt(0).toString(16).toUpperCase()}`),"encode");function H(r,e,o,n=new Date){const c=new URL(r.endpoint),i=n.toISOString().replace(/[:-]|\.\d{3}/g,""),s=i.slice(0,8),a=`${s}/auto/s3/aws4_request`,p=`/${h(r.bucket)}/${e.split("/").map(h).join("/")}`,m=[["X-Amz-Algorithm","AWS4-HMAC-SHA256"],["X-Amz-Credential",`${r.accessKeyId}/${a}`],["X-Amz-Date",i],["X-Amz-Expires",String(o)],["X-Amz-SignedHeaders","host"]].sort(([u],[w])=>u<w?-1:1).map(([u,w])=>`${h(u)}=${h(w)}`).join("&"),d=["GET",p,m,`host:${c.host}
2
+ `,"host","UNSIGNED-PAYLOAD"].join(`
3
+ `),g=["AWS4-HMAC-SHA256",i,a,D(d)].join(`
4
+ `),_=f(f(f(f(`AWS4${r.secretAccessKey}`,s),"auto"),"s3"),"aws4_request"),R=E("sha256",_).update(g).digest("hex");return`${c.origin}${p}?${m}&X-Amz-Signature=${R}`}t(H,"presignGet");function F(r=process.env){const e=r.CRTR_PUBLIC_API_URL;if(!e||!/^https?:\/\//.test(e))throw new Error("CRTR_PUBLIC_API_URL must be an http(s) URL for the Blaxel runner");return{apiUrl:e,signer:z(r)}}t(F,"blaxelLaunchDepsFromEnv");async function P(r){const e=await(await $()).get(`releases/${r}/runtime.tar.gz.sha256`);return e===null?null:e.toString("utf8")}t(P,"defaultReleaseSha");function G(r){return async e=>{const o=await C(),n=await o.prepare("SELECT profile_id, grantee FROM node_rows WHERE node_id = ?").get(e.nodeId);if(n===void 0)throw new Error(`no node ${e.nodeId}`);if(n.profile_id===null)throw new Error(`node ${e.nodeId} has no profile, so it has no person`);const c=await o.prepare("SELECT person FROM profiles WHERE id = ?").get(n.profile_id);if(c===void 0)throw new Error(`profile ${n.profile_id} has no row`);const i=n.grantee??"app:terminal",s=await I(n.profile_id),a=await L(s);if(a.state==="pending")throw new b(s,a.error);const p=await o.prepare("SELECT memory_mb FROM environments WHERE object = ?").get(s);if(p===void 0)throw new Error(`no environment ${s}`);const l=await x(s),m=l!==null&&await y(l,i),d=(await(r.releaseSha??P)(e.release)??"").trim().split(/\s+/)[0]??"";if(!/^[0-9a-f]{64}$/.test(d))throw new Error(`release ${e.release} has no sha256 object (releases/${e.release}/runtime.tar.gz.sha256)`);return{image:a.state==="image"?a.ref:T,memoryMb:Number(p.memory_mb),apiUrl:r.apiUrl,releaseUrl:H(r.signer,`releases/${e.release}/runtime.tar.gz`,v,r.now?.()),releaseSha256:d,secrets:await U({person:c.person,app:i,ownerProfile:l,ownerProfileInApp:m,includeDeployment:!1})}}}t(G,"createBlaxelLaunchContext");export{T as BASE_IMAGE,v as RELEASE_URL_TTL_S,F as blaxelLaunchDepsFromEnv,G as createBlaxelLaunchContext,H as presignGet,z as releaseSignerFromEnv};
@@ -0,0 +1,2 @@
1
+ /** Registers the Blaxel runner when `BLAXEL_API_KEY` is set; returns whether it did. Any other missing setting throws, so a half-configured control plane fails at boot. */
2
+ export declare function registerBlaxelRunnerFromEnv(env?: NodeJS.ProcessEnv): boolean;
@@ -0,0 +1 @@
1
+ var t=Object.defineProperty;var o=(r,e)=>t(r,"name",{value:e,configurable:!0});import{readFileSync as n}from"node:fs";import{join as l}from"node:path";import{emitEvent as a}from"../events/emit.js";import{assetRoot as i}from"../asset-root.js";import{createBlaxelClient as m,blaxelConfigFromEnv as s}from"../runtime/runner/blaxel/client.js";import{registerBlaxelRunner as p}from"../runtime/runner/index.js";import{createBlaxelRunner as f}from"../runtime/runner/blaxel/runner.js";import{blaxelLaunchDepsFromEnv as u,createBlaxelLaunchContext as c}from"./blaxel-launch.js";function C(r=process.env){if(!r.BLAXEL_API_KEY)return!1;const e=n(l(i(),"..","scripts","sandbox-bootstrap.sh"),"utf8");return p(f({client:m(s(r)),launchContext:c(u(r)),bootstrap:o(()=>e,"bootstrap")})),a({level:"info",event:"daemon.blaxel_runner.registered",fields:{workspace:r.BLAXEL_WORKSPACE??null,region:r.BLAXEL_REGION??null}}),!0}o(C,"registerBlaxelRunnerFromEnv");export{C as registerBlaxelRunnerFromEnv};
@@ -0,0 +1,17 @@
1
+ import { type CanvasDb } from '../canvas/db.js';
2
+ import { type SecretsKey } from './index.js';
3
+ export type EnvOwnerKind = 'profile' | 'app' | 'deployment';
4
+ export interface EnvOwner {
5
+ person: string | null;
6
+ kind: EnvOwnerKind;
7
+ id: string;
8
+ }
9
+ /** Insert or replace one value. Returns true when the name was new. */
10
+ export declare function putEnvRow(owner: EnvOwner, name: string, value: string, key?: SecretsKey): Promise<boolean>;
11
+ /** Seal one value into an open transaction, keeping a row that already holds the name. Used by the one-time boot copy. */
12
+ export declare function putEnvRowIfAbsent(db: CanvasDb, owner: EnvOwner, name: string, value: string, key?: SecretsKey): Promise<void>;
13
+ export declare function removeEnvRow(owner: EnvOwner, name: string): Promise<boolean>;
14
+ /** Names only: no value is opened. */
15
+ export declare function listEnvRowNames(owner: EnvOwner): Promise<string[]>;
16
+ /** Every value of one owner, opened. Throws `SecretsError` when any row fails to open: a wrong key never yields an empty or partial map. */
17
+ export declare function readEnvRows(owner: EnvOwner, key?: SecretsKey): Promise<Record<string, string>>;
@@ -0,0 +1,4 @@
1
+ var u=Object.defineProperty;var t=(e,n)=>u(e,"name",{value:n,configurable:!0});import{openDb as o}from"../canvas/db.js";import{loadSecretsKey as i,open as l,seal as c}from"./index.js";const E=t((e,n)=>`env_vars|${e.person??""}|${e.kind}|${e.id}|${n}`,"aadOf"),p=t(e=>e.person??"","personKey");async function D(e,n,a,d=i()){const{sealed:r,keyId:s}=c(a,d,E(e,n));return(await(await o()).prepare(`INSERT INTO env_vars (person, owner_kind, owner_id, name, value_sealed, key_id, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)
2
+ ON CONFLICT (COALESCE(person, ''), owner_kind, owner_id, name) DO UPDATE SET value_sealed = EXCLUDED.value_sealed, key_id = EXCLUDED.key_id, updated_at = EXCLUDED.updated_at
3
+ RETURNING (xmax = 0) AS created`).get(e.person,e.kind,e.id,n,r,s,new Date().toISOString())).created}t(D,"putEnvRow");async function N(e,n,a,d,r=i()){const{sealed:s,keyId:_}=c(d,r,E(n,a));await e.prepare(`INSERT INTO env_vars (person, owner_kind, owner_id, name, value_sealed, key_id, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)
4
+ ON CONFLICT (COALESCE(person, ''), owner_kind, owner_id, name) DO NOTHING`).run(n.person,n.kind,n.id,a,s,_,new Date().toISOString())}t(N,"putEnvRowIfAbsent");async function C(e,n){return(await(await o()).prepare("DELETE FROM env_vars WHERE COALESCE(person, '') = ? AND owner_kind = ? AND owner_id = ? AND name = ?").run(p(e),e.kind,e.id,n)).changes>0}t(C,"removeEnvRow");async function S(e){return(await(await o()).prepare("SELECT name FROM env_vars WHERE COALESCE(person, '') = ? AND owner_kind = ? AND owner_id = ? ORDER BY name").all(p(e),e.kind,e.id)).map(a=>a.name)}t(S,"listEnvRowNames");async function m(e,n=i()){const a=await(await o()).prepare("SELECT name, value_sealed, key_id FROM env_vars WHERE COALESCE(person, '') = ? AND owner_kind = ? AND owner_id = ?").all(p(e),e.kind,e.id),d=Object.create(null);for(const r of a)d[r.name]=l(r.value_sealed,r.key_id,n,E(e,r.name));return d}t(m,"readEnvRows");export{S as listEnvRowNames,D as putEnvRow,N as putEnvRowIfAbsent,m as readEnvRows,C as removeEnvRow};
@@ -0,0 +1,8 @@
1
+ /** The profile that owns `environmentId` (an environment object whose owner carries a `profile` identity), or null when an app owns it. */
2
+ export declare function environmentOwnerProfile(environmentId: string): Promise<string | null>;
3
+ /** Whether profile `profileId` belongs to `app`; a profile of another app adds no secrets. */
4
+ export declare function profileInApp(profileId: string, app: string): Promise<boolean>;
5
+ /** The secrets a local process, bash command or cron of `app` under `profileId` receives: the app's rows, then the rows of the profile that owns the
6
+ * profile's environment (a profile pointing at another profile's environment gets that profile's, not its own), plus the deployment rows.
7
+ * A profile on the machine environment (owned by the app, not a profile) is its own owner. */
8
+ export declare function localEnvironmentSecrets(profileId: string | null, app: string): Promise<Record<string, string>>;
@@ -0,0 +1 @@
1
+ var p=Object.defineProperty;var t=(e,n)=>p(e,"name",{value:n,configurable:!0});import{openDb as o}from"../canvas/db.js";import{personIdAsync as l}from"../canvas/person.js";import{environmentSecrets as c}from"./environment-secrets.js";async function m(e){return(await(await o()).prepare("SELECT i.external_id AS profile FROM objects o JOIN identities i ON i.object = o.owner AND i.scheme = 'profile' WHERE o.id = ?").get(e))?.profile??null}t(m,"environmentOwnerProfile");async function s(e,n){return((await(await o()).prepare("SELECT grantee FROM profiles WHERE id = ?").get(e))?.grantee??"app:terminal")===n}t(s,"profileInApp");async function v(e,n){let a=await l(),i=null;if(e!==null&&e!==""){const r=await(await o()).prepare("SELECT person, environment_id FROM profiles WHERE id = ?").get(e);r!==void 0&&(a=r.person),i=r?.environment_id==null||(await(await o()).prepare("SELECT kind FROM environments WHERE object = ?").get(r.environment_id))?.kind==="machine"?e:await m(r.environment_id)}return c({person:a,app:n,ownerProfile:i,ownerProfileInApp:i!==null&&await s(i,n),includeDeployment:!0})}t(v,"localEnvironmentSecrets");export{m as environmentOwnerProfile,v as localEnvironmentSecrets,s as profileInApp};
@@ -0,0 +1,14 @@
1
+ import type { SecretsKey } from './index.js';
2
+ /** `owner_id` of every deployment row. */
3
+ export declare const DEPLOYMENT_ID = "_deployment";
4
+ export interface EnvironmentSecretsInput {
5
+ person: string;
6
+ app: string;
7
+ /** The environment's owning profile, or null when the owner is the app. */
8
+ ownerProfile: string | null;
9
+ /** Whether that profile's grantee is `app`; a profile of another app adds nothing. */
10
+ ownerProfileInApp: boolean;
11
+ includeDeployment: boolean;
12
+ key?: SecretsKey;
13
+ }
14
+ export declare function environmentSecrets(input: EnvironmentSecretsInput): Promise<Record<string, string>>;
@@ -0,0 +1 @@
1
+ var p=Object.defineProperty;var i=(e,n)=>p(e,"name",{value:n,configurable:!0});import{readEnvRows as r}from"./env-rows.js";const t="_deployment";async function s(e){const{person:n,app:l,key:o}=e;return{...await r({person:n,kind:"app",id:l},o),...e.includeDeployment?await r({person:null,kind:"deployment",id:t},o):{},...e.ownerProfile!==null&&e.ownerProfileInApp?await r({person:n,kind:"profile",id:e.ownerProfile},o):{}}}i(s,"environmentSecrets");export{t as DEPLOYMENT_ID,s as environmentSecrets};
@@ -0,0 +1,2 @@
1
+ export { SecretsError, keyIdOf, open, parseSecretsKey, seal, type Sealed, type SecretsKey } from './seal.js';
2
+ export { loadSecretsKey, secretsKeyFile } from './key.js';
@@ -0,0 +1 @@
1
+ import{SecretsError as o,keyIdOf as s,open as t,parseSecretsKey as c,seal as p}from"./seal.js";import{loadSecretsKey as a,secretsKeyFile as f}from"./key.js";export{o as SecretsError,s as keyIdOf,a as loadSecretsKey,t as open,c as parseSecretsKey,p as seal,f as secretsKeyFile};
@@ -0,0 +1,3 @@
1
+ import { type SecretsKey } from './seal.js';
2
+ export declare const secretsKeyFile: () => string;
3
+ export declare function loadSecretsKey(): SecretsKey;
@@ -0,0 +1,2 @@
1
+ var i=Object.defineProperty;var s=(r,e)=>i(r,"name",{value:e,configurable:!0});import{chmodSync as f,existsSync as m,readFileSync as p,writeFileSync as y}from"node:fs";import{randomBytes as u}from"node:crypto";import{join as t}from"node:path";import{secretsDir as l}from"../layout.js";import{ensureDir as S}from"../fs-utils.js";import{parseSecretsKey as c,SecretsError as a}from"./seal.js";const n="CRTR_SECRETS_KEY",d=s(()=>t(l(),"secrets.key"),"secretsKeyFile"),E=s(()=>(process.env.CRTR_DATABASE_URL??"")!=="","isCloudProcess");let o;function x(){const r=process.env[n];if(r!==void 0&&r!=="")return o?.source!==`env:${r}`&&(o={source:`env:${r}`,key:c(r)}),o.key;if(E())throw new a("secrets_key_missing",`${n} is not set; a cloud process cannot seal or open secrets without it`);const e=d();return o?.source!==`file:${e}`&&(m(e)||(S(t(e,"..")),y(e,`${u(32).toString("hex")}
2
+ `,{mode:384,flag:"wx"})),f(e,384),o={source:`file:${e}`,key:c(p(e,"utf8"))}),o.key}s(x,"loadSecretsKey");export{x as loadSecretsKey,d as secretsKeyFile};
@@ -0,0 +1,18 @@
1
+ export declare class SecretsError extends Error {
2
+ readonly code: 'secrets_key_missing' | 'secrets_key_invalid' | 'secrets_open_failed';
3
+ constructor(code: SecretsError['code'], message: string);
4
+ }
5
+ export interface SecretsKey {
6
+ id: string;
7
+ key: Buffer;
8
+ }
9
+ export interface Sealed {
10
+ sealed: Buffer;
11
+ keyId: string;
12
+ }
13
+ /** The id a key is known by in `key_id` columns: a prefix of its SHA-256, never reversible to the key. */
14
+ export declare function keyIdOf(key: Buffer): string;
15
+ /** Parse a 32-byte key written as 64 hex characters or as base64. */
16
+ export declare function parseSecretsKey(text: string): SecretsKey;
17
+ export declare function seal(plaintext: string, key: SecretsKey, aad: string): Sealed;
18
+ export declare function open(sealed: Buffer | Uint8Array, keyId: string, key: SecretsKey, aad: string): string;
@@ -0,0 +1 @@
1
+ var u=Object.defineProperty;var a=(r,e)=>u(r,"name",{value:e,configurable:!0});import{createCipheriv as d,createDecipheriv as h,createHash as l,randomBytes as p}from"node:crypto";const n=12,i=16;class f extends Error{static{a(this,"SecretsError")}code;constructor(e,t){super(t),this.name="SecretsError",this.code=e}}function y(r){return l("sha256").update(r).digest("hex").slice(0,16)}a(y,"keyIdOf");function B(r){const e=r.trim(),t=/^[0-9a-fA-F]{64}$/.test(e)?Buffer.from(e,"hex"):Buffer.from(e,"base64");if(t.length!==32)throw new f("secrets_key_invalid","the secrets key must be 32 bytes (64 hex characters or base64)");return{id:y(t),key:t}}a(B,"parseSecretsKey");function b(r,e,t){const c=p(n),s=d("aes-256-gcm",e.key,c);s.setAAD(Buffer.from(t,"utf8"));const o=Buffer.concat([s.update(r,"utf8"),s.final()]);return{sealed:Buffer.concat([c,s.getAuthTag(),o]),keyId:e.id}}a(b,"seal");function g(r,e,t,c){if(e!==t.id)throw new f("secrets_open_failed",`sealed value was written under key ${e}, this process holds key ${t.id}`);const s=Buffer.from(r);if(s.length<n+i)throw new f("secrets_open_failed","sealed value is truncated");try{const o=h("aes-256-gcm",t.key,s.subarray(0,n));return o.setAAD(Buffer.from(c,"utf8")),o.setAuthTag(s.subarray(n,n+i)),Buffer.concat([o.update(s.subarray(n+i)),o.final()]).toString("utf8")}catch{throw new f("secrets_open_failed","sealed value failed authentication (wrong key, altered bytes or another row)")}}a(g,"open");export{f as SecretsError,y as keyIdOf,g as open,B as parseSecretsKey,b as seal};
@@ -16,7 +16,7 @@ export interface LaunchNode {
16
16
  folder?: string;
17
17
  cwd?: string;
18
18
  /** A cron execution (launch id `cron-<execution_id>`): its only daemon path is `cli.sock` in its own run folder
19
- * `nodeRunDir(nodeId)`, so neither `api.sock` nor `store.sock` is mounted. `anchorDir` is an anchored cron's anchor agent folder. */
19
+ * `nodeRunDir(nodeId)`, so `api.sock` is not mounted. `anchorDir` is an anchored cron's anchor agent folder. */
20
20
  cron?: {
21
21
  anchorDir?: string;
22
22
  };
@@ -1 +1 @@
1
- var F=Object.defineProperty;var a=(t,o)=>F(t,"name",{value:o,configurable:!0});import{lstatSync as W}from"node:fs";import{join as u,relative as f,sep as c}from"node:path";import{appSpace as d,appsRoot as S,runtimeRoot as h,socketDir as m,userFilesRoot as R}from"../layout.js";import{nodeAgentDir as I,nodeRunDir as k}from"../canvas/paths.js";import{uidForApp as A}from"./users.js";import{permissionsForRun as E}from"./permissions.js";function x(t){const o=[];for(const e of t.otherFiles){const s=e.holder==="user"?R():d(e.holder).files;o.push({source:e.path==="."?s:u(s,e.path),writable:e.writable})}for(const e of t.otherNodes)o.push({source:d(e).nodes,writable:!1});return o}a(x,"grantedMounts");function w(t,o){const e=f(t,o);return e===""||e!==".."&&!e.startsWith(`..${c}`)&&!e.startsWith(c)}a(w,"beneath");function M(t,o){return w(R(),o)||w(S(),o)&&!w(d(t).root,o)}a(M,"isGrantedSource");function j(t,o){return o.some(e=>w(e.source,t.source)&&(e.writable||!t.writable))}a(j,"mountAllowed");async function z(t,o){try{const e=x(await E(t.app,t.sub,t.grantId,t.scopes));return o.mounts.every(s=>!M(t.app,s.source)||j(s,e))}catch{return!1}}a(z,"launchStillGranted");async function B(t){const o=d(t.app),e=await E(t.app,t.sub,t.grantId,t.scopes);if(t.isolated&&t.withoutNode&&t.folder===void 0)throw new Error("isolated launch requires a node");const s=t.folder??(t.isolated?I(t.nodeId,t.app):void 0);if(s&&t.cwd!==void 0&&!w(s,t.cwd))throw new Error("isolated cwd must be inside the node folder");const p=new Map,i=a((r,n,l=!1,v=!1)=>{if(l)try{W(r)}catch(b){if(b.code!=="ENOENT")throw b;v&&t.auditMissing(r);return}p.set(r,n||(p.get(r)??!1))},"add");for(const r of["/usr","/bin","/lib","/lib64","/opt/crouter","/etc/passwd","/etc/group","/etc/resolv.conf","/etc/hosts","/etc/ssl","/etc/ca-certificates"])i(r,!1,!0);t.withoutNode||i(I(t.nodeId,t.app),!0),t.folder!==void 0&&i(t.folder,!0),t.cron?.anchorDir!==void 0&&i(t.cron.anchorDir,!0),t.isolated||i(o.home,!0),t.isolated||i(o.files,!0),i(o.cache,!0,!0),(!t.withoutNode||t.cron!==void 0)&&i(k(t.nodeId),!0),t.cron===void 0&&(i(u(m(),"api.sock"),!0),i(u(m(),"store.sock"),!0));for(const r of x(e))i(r.source,r.writable,!0,!0);if(t.pluginRoot!==void 0){const r=a(l=>t.pluginRoot===l||f(l,t.pluginRoot)!==".."&&!f(l,t.pluginRoot).startsWith(`..${c}`),"within"),n=u(h(),"plugins");if([...p].some(([l])=>r(l))){if(p.get(t.pluginRoot)===!0)throw new Error("hook root cannot also be writable");i(t.pluginRoot,!1)}else if(r(n)&&t.pluginRoot!==n)i(t.pluginRoot,!1);else if(r("/opt/crouter"))i(t.pluginRoot,!1);else throw new Error("hook root is not readable with this app\u2019s mounts")}const y=t.cwd??s??o.files,g=[...p].sort(([r],[n])=>r.split(c).length-n.split(c).length||r.localeCompare(n)),D=u(h(),"nodes",t.nodeId,"job","broker.log"),N=process.platform!=="linux"?[k(t.nodeId),u(m(),"api.sock"),u(m(),"store.sock")]:[];if(g.some(([r])=>r!==t.pluginRoot&&!N.includes(r)&&(r===h()||!f(h(),r).startsWith("..")&&!f(h(),r).startsWith(c))))throw new Error("runtime records cannot be mounted");return{nodeId:t.nodeId,uid:await A(t.app),mounts:g.map(([r,n])=>({source:r,writable:n})),cwd:y,env:t.env,command:t.command,log:D,...t.isolated?{network:"none"}:{}}}a(B,"compileLaunch");export{B as compileLaunch,x as grantedMounts,M as isGrantedSource,z as launchStillGranted,j as mountAllowed};
1
+ var F=Object.defineProperty;var a=(t,o)=>F(t,"name",{value:o,configurable:!0});import{lstatSync as W}from"node:fs";import{join as p,relative as f,sep as u}from"node:path";import{appSpace as d,appsRoot as S,runtimeRoot as h,socketDir as b,userFilesRoot as R}from"../layout.js";import{nodeAgentDir as I,nodeRunDir as k}from"../canvas/paths.js";import{uidForApp as A}from"./users.js";import{permissionsForRun as E}from"./permissions.js";function x(t){const o=[];for(const e of t.otherFiles){const s=e.holder==="user"?R():d(e.holder).files;o.push({source:e.path==="."?s:p(s,e.path),writable:e.writable})}for(const e of t.otherNodes)o.push({source:d(e).nodes,writable:!1});return o}a(x,"grantedMounts");function w(t,o){const e=f(t,o);return e===""||e!==".."&&!e.startsWith(`..${u}`)&&!e.startsWith(u)}a(w,"beneath");function M(t,o){return w(R(),o)||w(S(),o)&&!w(d(t).root,o)}a(M,"isGrantedSource");function j(t,o){return o.some(e=>w(e.source,t.source)&&(e.writable||!t.writable))}a(j,"mountAllowed");async function z(t,o){try{const e=x(await E(t.app,t.sub,t.grantId,t.scopes));return o.mounts.every(s=>!M(t.app,s.source)||j(s,e))}catch{return!1}}a(z,"launchStillGranted");async function B(t){const o=d(t.app),e=await E(t.app,t.sub,t.grantId,t.scopes);if(t.isolated&&t.withoutNode&&t.folder===void 0)throw new Error("isolated launch requires a node");const s=t.folder??(t.isolated?I(t.nodeId,t.app):void 0);if(s&&t.cwd!==void 0&&!w(s,t.cwd))throw new Error("isolated cwd must be inside the node folder");const c=new Map,i=a((r,n,l=!1,v=!1)=>{if(l)try{W(r)}catch(g){if(g.code!=="ENOENT")throw g;v&&t.auditMissing(r);return}c.set(r,n||(c.get(r)??!1))},"add");for(const r of["/usr","/bin","/lib","/lib64","/opt/crouter","/etc/passwd","/etc/group","/etc/resolv.conf","/etc/hosts","/etc/ssl","/etc/ca-certificates"])i(r,!1,!0);t.withoutNode||i(I(t.nodeId,t.app),!0),t.folder!==void 0&&i(t.folder,!0),t.cron?.anchorDir!==void 0&&i(t.cron.anchorDir,!0),t.isolated||i(o.home,!0),t.isolated||i(o.files,!0),i(o.cache,!0,!0),(!t.withoutNode||t.cron!==void 0)&&i(k(t.nodeId),!0),t.cron===void 0&&i(p(b(),"api.sock"),!0);for(const r of x(e))i(r.source,r.writable,!0,!0);if(t.pluginRoot!==void 0){const r=a(l=>t.pluginRoot===l||f(l,t.pluginRoot)!==".."&&!f(l,t.pluginRoot).startsWith(`..${u}`),"within"),n=p(h(),"plugins");if([...c].some(([l])=>r(l))){if(c.get(t.pluginRoot)===!0)throw new Error("hook root cannot also be writable");i(t.pluginRoot,!1)}else if(r(n)&&t.pluginRoot!==n)i(t.pluginRoot,!1);else if(r("/opt/crouter"))i(t.pluginRoot,!1);else throw new Error("hook root is not readable with this app\u2019s mounts")}const y=t.cwd??s??o.files,m=[...c].sort(([r],[n])=>r.split(u).length-n.split(u).length||r.localeCompare(n)),D=p(h(),"nodes",t.nodeId,"job","broker.log"),N=process.platform!=="linux"?[k(t.nodeId),p(b(),"api.sock")]:[];if(m.some(([r])=>r!==t.pluginRoot&&!N.includes(r)&&(r===h()||!f(h(),r).startsWith("..")&&!f(h(),r).startsWith(u))))throw new Error("runtime records cannot be mounted");return{nodeId:t.nodeId,uid:await A(t.app),mounts:m.map(([r,n])=>({source:r,writable:n})),cwd:y,env:t.env,command:t.command,log:D,...t.isolated?{network:"none"}:{}}}a(B,"compileLaunch");export{B as compileLaunch,x as grantedMounts,M as isGrantedSource,z as launchStillGranted,j as mountAllowed};