@north-light/cli 0.3.580 → 0.3.582

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 (237) hide show
  1. package/dist/api/client.d.ts +6 -1
  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/execution-start.d.ts +14 -0
  6. package/dist/api/dto/health.d.ts +22 -0
  7. package/dist/api/dto/imports.d.ts +142 -0
  8. package/dist/api/dto/imports.js +0 -0
  9. package/dist/api/error-codes.d.ts +5 -0
  10. package/dist/api/error-codes.js +1 -1
  11. package/dist/api/errors.js +1 -1
  12. package/dist/api/index.d.ts +1 -0
  13. package/dist/api/index.js +1 -1
  14. package/dist/api/routes.d.ts +2 -0
  15. package/dist/api/routes.js +1 -1
  16. package/dist/build-root.d.ts +11 -4
  17. package/dist/build-root.js +1 -1
  18. package/dist/cli.js +7 -7
  19. package/dist/clients/attach/session/connection.d.ts +3 -1
  20. package/dist/clients/attach/session/connection.js +1 -1
  21. package/dist/clients/attach/viewer.js +529 -529
  22. package/dist/commands/api-client.d.ts +12 -4
  23. package/dist/commands/api-client.js +3 -3
  24. package/dist/commands/sys/daemon.js +1 -1
  25. package/dist/commands/sys/desktop.d.ts +5 -0
  26. package/dist/commands/sys/desktop.js +1 -0
  27. package/dist/commands/sys/import/move.d.ts +55 -0
  28. package/dist/commands/sys/import/move.js +8 -0
  29. package/dist/commands/sys/import/resume.d.ts +1 -0
  30. package/dist/commands/sys/import/resume.js +1 -0
  31. package/dist/commands/sys/import/start.d.ts +4 -0
  32. package/dist/commands/sys/import/start.js +1 -0
  33. package/dist/commands/sys/join.d.ts +2 -0
  34. package/dist/commands/sys/join.js +1 -1
  35. package/dist/commands/sys/machine/rotate.d.ts +1 -0
  36. package/dist/commands/sys/machine/rotate.js +1 -0
  37. package/dist/commands/sys/signin.d.ts +7 -0
  38. package/dist/commands/sys/signin.js +2 -0
  39. package/dist/commands/sys.js +1 -1
  40. package/dist/core/blobs/r2.d.ts +4 -0
  41. package/dist/core/blobs/r2.js +7 -4
  42. package/dist/core/canvas/daemon-owner.d.ts +1 -1
  43. package/dist/core/canvas/executions.d.ts +1 -1
  44. package/dist/core/canvas/node-tokens.d.ts +2 -0
  45. package/dist/core/canvas/node-tokens.js +3 -3
  46. package/dist/core/canvas/pid.d.ts +6 -0
  47. package/dist/core/canvas/pid.js +5 -5
  48. package/dist/core/command-plugins/compose.js +1 -1
  49. package/dist/core/command.d.ts +17 -8
  50. package/dist/core/command.js +9 -9
  51. package/dist/core/directory/issuer.d.ts +3 -0
  52. package/dist/core/directory/issuer.js +1 -1
  53. package/dist/core/directory/person-credential.d.ts +61 -0
  54. package/dist/core/directory/person-credential.js +1 -0
  55. package/dist/core/directory/reach.d.ts +3 -0
  56. package/dist/core/directory/reach.js +1 -0
  57. package/dist/core/grants/mirror.d.ts +3 -0
  58. package/dist/core/grants/mirror.js +2 -2
  59. package/dist/core/graph/bodies-boot-copy.d.ts +16 -2
  60. package/dist/core/graph/bodies-boot-copy.js +1 -1
  61. package/dist/core/graph/bodies.d.ts +13 -4
  62. package/dist/core/graph/bodies.js +1 -1
  63. package/dist/core/graph/deletion.d.ts +4 -1
  64. package/dist/core/graph/deletion.js +3 -3
  65. package/dist/core/graph/documents.d.ts +4 -0
  66. package/dist/core/graph/documents.js +9 -9
  67. package/dist/core/graph/reader.js +2 -2
  68. package/dist/core/graph/repo-sync/exchange.js +2 -2
  69. package/dist/core/graph/repo-sync/ref-format.d.ts +7 -2
  70. package/dist/core/graph/repo-sync/ref-format.js +2 -2
  71. package/dist/core/import/bundle/blob-keys.d.ts +10 -0
  72. package/dist/core/import/bundle/blob-keys.js +1 -0
  73. package/dist/core/import/bundle/layout.d.ts +90 -0
  74. package/dist/core/import/bundle/layout.js +5 -0
  75. package/dist/core/import/export/archive.d.ts +11 -0
  76. package/dist/core/import/export/archive.js +1 -0
  77. package/dist/core/import/export/cloud.d.ts +23 -0
  78. package/dist/core/import/export/cloud.js +1 -0
  79. package/dist/core/import/export/dump.d.ts +34 -0
  80. package/dist/core/import/export/dump.js +3 -0
  81. package/dist/core/import/export/files.d.ts +22 -0
  82. package/dist/core/import/export/files.js +1 -0
  83. package/dist/core/import/export/inline.d.ts +3 -0
  84. package/dist/core/import/export/inline.js +1 -0
  85. package/dist/core/import/export/source.d.ts +2 -0
  86. package/dist/core/import/export/source.js +1 -0
  87. package/dist/core/import/export/stores.d.ts +9 -0
  88. package/dist/core/import/export/stores.js +1 -0
  89. package/dist/core/import/load/bind.d.ts +19 -0
  90. package/dist/core/import/load/bind.js +15 -0
  91. package/dist/core/import/load/blobs.d.ts +10 -0
  92. package/dist/core/import/load/blobs.js +1 -0
  93. package/dist/core/import/load/catalog.d.ts +32 -0
  94. package/dist/core/import/load/catalog.js +9 -0
  95. package/dist/core/import/load/copy.d.ts +26 -0
  96. package/dist/core/import/load/copy.js +6 -0
  97. package/dist/core/import/load/path-map.d.ts +7 -0
  98. package/dist/core/import/load/path-map.js +1 -0
  99. package/dist/core/import/load/prepare.d.ts +37 -0
  100. package/dist/core/import/load/prepare.js +10 -0
  101. package/dist/core/import/load/rewrite.d.ts +39 -0
  102. package/dist/core/import/load/rewrite.js +13 -0
  103. package/dist/core/import/load/rollback.d.ts +3 -0
  104. package/dist/core/import/load/rollback.js +6 -0
  105. package/dist/core/import/load/run.d.ts +22 -0
  106. package/dist/core/import/load/run.js +3 -0
  107. package/dist/core/import/load/stage.d.ts +15 -0
  108. package/dist/core/import/load/stage.js +1 -0
  109. package/dist/core/import/load/stores.d.ts +24 -0
  110. package/dist/core/import/load/stores.js +2 -0
  111. package/dist/core/import/load/verify.d.ts +4 -0
  112. package/dist/core/import/load/verify.js +12 -0
  113. package/dist/core/io.js +4 -4
  114. package/dist/core/northlight/previews.d.ts +22 -0
  115. package/dist/core/northlight/previews.js +1 -0
  116. package/dist/core/providers/call.js +1 -1
  117. package/dist/core/providers/device.js +1 -1
  118. package/dist/core/providers/files.d.ts +2 -1
  119. package/dist/core/providers/files.js +1 -1
  120. package/dist/core/runtime/broker/cli-relay.js +1 -1
  121. package/dist/core/runtime/broker/model-adapter.js +3 -3
  122. package/dist/core/runtime/broker-ownership.d.ts +1 -0
  123. package/dist/core/runtime/broker-ownership.js +1 -0
  124. package/dist/core/runtime/broker.js +1 -1
  125. package/dist/core/runtime/first-run-offer.d.ts +7 -2
  126. package/dist/core/runtime/first-run-offer.js +10 -2
  127. package/dist/core/runtime/front-door.js +4 -4
  128. package/dist/core/runtime/revive.d.ts +5 -2
  129. package/dist/core/runtime/revive.js +2 -2
  130. package/dist/core/runtime/runner/blaxel/runner.js +3 -3
  131. package/dist/core/runtime/runner/index.d.ts +1 -0
  132. package/dist/core/runtime/runner/index.js +1 -1
  133. package/dist/core/runtime/runner/machine/call.d.ts +8 -0
  134. package/dist/core/runtime/runner/machine/call.js +3 -0
  135. package/dist/core/runtime/runner/machine/environment.d.ts +1 -0
  136. package/dist/core/runtime/runner/machine/environment.js +1 -0
  137. package/dist/core/runtime/runner/machine/executor.d.ts +29 -0
  138. package/dist/core/runtime/runner/machine/executor.js +1 -0
  139. package/dist/core/runtime/runner/machine/machine-executor.test.d.ts +1 -0
  140. package/dist/core/runtime/runner/machine/machine-executor.test.js +4 -0
  141. package/dist/core/runtime/runner/machine/ops.d.ts +127 -0
  142. package/dist/core/runtime/runner/machine/ops.js +1 -0
  143. package/dist/core/runtime/runner/machine/runner.d.ts +12 -0
  144. package/dist/core/runtime/runner/machine/runner.js +1 -0
  145. package/dist/core/runtime/runner/types.d.ts +7 -1
  146. package/dist/core/runtime/warm-pool.d.ts +2 -0
  147. package/dist/core/runtime/warm-pool.js +1 -1
  148. package/dist/core/secrets/env-rows.d.ts +1 -0
  149. package/dist/core/secrets/env-rows.js +1 -1
  150. package/dist/core/storage/purge.d.ts +6 -0
  151. package/dist/core/storage/purge.js +4 -0
  152. package/dist/core/subscription-state.d.ts +1 -0
  153. package/dist/core/subscription-state.js +1 -1
  154. package/dist/daemon/api/bridge.js +3 -3
  155. package/dist/daemon/api/handlers/attach.js +1 -1
  156. package/dist/daemon/api/handlers/crons.js +1 -1
  157. package/dist/daemon/api/handlers/daemon.js +1 -1
  158. package/dist/daemon/api/handlers/execution-start.js +2 -2
  159. package/dist/daemon/api/handlers/files.js +1 -1
  160. package/dist/daemon/api/handlers/imports.d.ts +2 -0
  161. package/dist/daemon/api/handlers/imports.js +1 -0
  162. package/dist/daemon/api/handlers/machine-credential.d.ts +2 -0
  163. package/dist/daemon/api/handlers/machine-credential.js +1 -0
  164. package/dist/daemon/api/handlers/profiles.js +1 -1
  165. package/dist/daemon/api/handlers/providers.js +1 -1
  166. package/dist/daemon/api/handlers/runs.js +1 -1
  167. package/dist/daemon/api/import-auth.d.ts +6 -0
  168. package/dist/daemon/api/import-auth.js +1 -0
  169. package/dist/daemon/api/operations.d.ts +2 -2
  170. package/dist/daemon/api/operations.js +1 -1
  171. package/dist/daemon/api/router.d.ts +7 -1
  172. package/dist/daemon/api/router.js +1 -1
  173. package/dist/daemon/api/server.js +1 -1
  174. package/dist/daemon/boot-migrations.js +1 -1
  175. package/dist/daemon/broker-signals/bus.d.ts +3 -2
  176. package/dist/daemon/broker-signals/bus.js +1 -1
  177. package/dist/daemon/cron/armed-context.d.ts +1 -0
  178. package/dist/daemon/cron/armed-context.js +1 -1
  179. package/dist/daemon/cron/closed.d.ts +31 -0
  180. package/dist/daemon/cron/closed.js +1 -0
  181. package/dist/daemon/cron/ended.js +2 -2
  182. package/dist/daemon/cron/lease-recovery.js +1 -1
  183. package/dist/daemon/cron/machine-exec.d.ts +11 -0
  184. package/dist/daemon/cron/machine-exec.js +1 -0
  185. package/dist/daemon/cron/machine-fire.d.ts +26 -0
  186. package/dist/daemon/cron/machine-fire.js +1 -0
  187. package/dist/daemon/cron/recurrence.d.ts +14 -0
  188. package/dist/daemon/cron/recurrence.js +1 -0
  189. package/dist/daemon/cron/run-token.d.ts +7 -0
  190. package/dist/daemon/cron/run-token.js +1 -0
  191. package/dist/daemon/cron-run.js +2 -2
  192. package/dist/daemon/crtrd-cli.js +1 -1
  193. package/dist/daemon/crtrd.d.ts +3 -3
  194. package/dist/daemon/crtrd.js +14 -14
  195. package/dist/daemon/drain.d.ts +25 -0
  196. package/dist/daemon/drain.js +9 -0
  197. package/dist/daemon/due-sources.d.ts +1 -1
  198. package/dist/daemon/due-sources.js +1 -1
  199. package/dist/daemon/due.js +8 -8
  200. package/dist/daemon/fleet.js +5 -5
  201. package/dist/daemon/import-dump-run.d.ts +1 -0
  202. package/dist/daemon/import-dump-run.js +5 -0
  203. package/dist/daemon/machine-daemon.d.ts +10 -0
  204. package/dist/daemon/machine-daemon.js +1 -0
  205. package/dist/daemon/machine-link/client.d.ts +29 -0
  206. package/dist/daemon/machine-link/client.js +1 -0
  207. package/dist/daemon/machine-link/config.d.ts +45 -0
  208. package/dist/daemon/machine-link/config.js +3 -0
  209. package/dist/daemon/machine-link/credential.d.ts +7 -0
  210. package/dist/daemon/machine-link/credential.js +2 -0
  211. package/dist/daemon/machine-link/forward.d.ts +16 -0
  212. package/dist/daemon/machine-link/forward.js +1 -0
  213. package/dist/daemon/machine-link/frames.d.ts +49 -0
  214. package/dist/daemon/machine-link/frames.js +1 -0
  215. package/dist/daemon/machine-link/holder.d.ts +73 -0
  216. package/dist/daemon/machine-link/holder.js +9 -0
  217. package/dist/daemon/machine-link/move-notify.d.ts +4 -0
  218. package/dist/daemon/machine-link/move-notify.js +1 -0
  219. package/dist/daemon/manage.d.ts +19 -3
  220. package/dist/daemon/manage.js +3 -3
  221. package/dist/daemon/reconcilers/cron-lane.js +1 -1
  222. package/dist/daemon/reconcilers/delivery-lane.js +2 -2
  223. package/dist/daemon/reconcilers/import-lane.d.ts +9 -0
  224. package/dist/daemon/reconcilers/import-lane.js +1 -0
  225. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +1 -1
  226. package/dist/daemon/reconcilers/node-lifecycle/remote-wake.d.ts +3 -0
  227. package/dist/daemon/reconcilers/node-lifecycle/remote-wake.js +1 -1
  228. package/dist/daemon/reconcilers/node-lifecycle/tick.js +1 -1
  229. package/dist/daemon/reconcilers/purge-due-work.d.ts +13 -0
  230. package/dist/daemon/reconcilers/purge-due-work.js +1 -0
  231. package/dist/daemon/reconcilers/row-claim.d.ts +1 -0
  232. package/dist/daemon/reconcilers/row-claim.js +2 -2
  233. package/dist/daemon/runs/observer.js +3 -3
  234. package/package.json +2 -2
  235. package/packages/crouter-identity/package.json +1 -1
  236. package/runtime.lock.json +13 -13
  237. package/scripts/sandbox-start.mjs +185 -2
@@ -0,0 +1,3 @@
1
+ var K=Object.defineProperty;var s=(t,n)=>K(t,"name",{value:n,configurable:!0});import{mkdirSync as v,readFileSync as x,writeFileSync as H}from"node:fs";import{join as f}from"node:path";import{openDb as Q,withCanvasWrite as Z}from"../../canvas/db.js";import{hashOf as W,storable as ee}from"../../graph/bodies.js";import{personIdAsync as te}from"../../canvas/person.js";import{CANVAS_SCHEMA_VERSION as ne}from"../../canvas/schema-version.js";import{crtrVersion as re}from"../../../shared/crtr-version.js";import{BODIES_TABLE as C,DUMPED_TABLES as P,RowFileWriter as ae,SEQ_TABLES as se,bundleKeys as oe,encodeRows as ie,rowLine as ce}from"../bundle/layout.js";import{parseFrontmatterGeneric as le}from"../../frontmatter.js";import{inlineMessageBody as he}from"./inline.js";const pe=16*1024*1024,de=8*1024*1024,D=32*1024*1024,q=5e3,M="bodies-pre.json",F=s(t=>`"${t.replace(/"/g,'""')}"`,"q");function ue(t){try{const n=x(t,"utf8");return n.trim()===""?null:n}catch(n){if(n.code==="ENOENT")return null;throw n}}s(ue,"readText");function Ee(t){try{return x(t,"utf8")}catch(n){if(n.code==="ENOENT")return null;throw n}}s(Ee,"readFileOrNull");function J(t,n){return new Error(`revision text ${t} is ${n} bytes; the import carries at most ${D} bytes of text per body`)}s(J,"tooLarge");function fe(t){const n=W(t.text);if(n!==t.hash)throw new Error(`body ${t.hash} does not hash to its key (its text hashes to ${n}); upgrade and restart the daemon so its boot repair rekeys the row, then export again`)}s(fe,"assertCanonical");async function U(t,n,o,r,m){const w=f(n,"rows",C);v(w,{recursive:!0});const _=[];let i=null;const O=s(async()=>{const a=i;i=null,a!==null&&await a.close()},"close"),b=s(async a=>{fe(a);const h=ce(a),c=Buffer.byteLength(h,"utf8");if(i!==null&&i.bytes>0&&i.bytes+c>pe&&await O(),i===null){const p=`${o}-${_.length}`;i=new ae(f(w,`${p}.jsonl.zst`)),_.push(p)}await i.write(h,c)},"add");try{for(let a=0;a<r.length;){const h=await t.prepare("SELECT hash, octet_length(text) AS n FROM bodies WHERE hash = ANY(?::text[]) ORDER BY hash").all(r.slice(a,a+q));let c=[],p=0;const R=s(async()=>{if(c.length===0)return;const d=await t.prepare("SELECT hash, text FROM bodies WHERE hash = ANY(?::text[]) ORDER BY hash").all(c);for(const y of d)await b(y);c=[],p=0},"fetchPage");for(const{hash:d,n:y}of h){const E=Number(y);if(E>D)throw J(d,E);c.length>0&&p+E>de&&await R(),c.push(d),p+=E}await R(),a+=q}for(const a of m){const h=Buffer.byteLength(a.text,"utf8");if(h>D)throw J(a.hash,h);await b(a)}await O()}catch(a){throw i?.destroy(),a}return _}s(U,"writeBodyParts");const V=s(async t=>(await t.prepare("SELECT hash FROM bodies ORDER BY hash").all()).map(n=>n.hash),"allHashes");async function Be(t){const n=await Q(),o=await V(n),r=await U(n,t,"pre",o,[]),m={hashes:o,parts:r,count:o.length,canonical:!0};return H(f(t,M),JSON.stringify(m)),{parts:r,count:o.length}}s(Be,"dumpBodiesPre");function me(t){try{return JSON.parse(x(f(t,M),"utf8"))}catch(n){if(n.code==="ENOENT")return{hashes:[],parts:[],count:0};throw n}}s(me,"readPre");function we(t,n){if(t.count===0&&t.parts.length===0)return;if(t.canonical!==!0)throw new Error(`${M} was written before bodies were checked against their keys; discard the export directory and run the pre-pause dump again`);const o=t.hashes.filter(r=>!n.has(r));if(o.length>0)throw new Error(`the pre-pause dump holds ${o.length} body row(s) the database no longer has (first: ${o[0]}); the boot repair ran after it. Discard the export directory and run the pre-pause dump again`)}s(we,"assertPreCurrent");async function xe(t,n){const o=await te();return v(f(t,"rows"),{recursive:!0}),Z(async r=>{await r.exec("SET TRANSACTION ISOLATION LEVEL REPEATABLE READ");const m=new Set((await r.prepareUncached("SELECT table_name FROM information_schema.tables WHERE table_schema = current_schema()").all()).map(e=>e.table_name)),w=P.filter(e=>!m.has(e));if(w.length>0)throw new Error(`the canvas database lacks tables the bundle needs: ${w.join(", ")}`);const _=await r.prepareUncached(`SELECT c.conrelid::regclass::text AS tbl, (SELECT array_agg(a.attname::text ORDER BY k.ord) FROM unnest(c.conkey) WITH ORDINALITY k(attnum, ord)
2
+ JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = k.attnum) AS cols FROM pg_constraint c WHERE c.contype = 'p' AND c.connamespace = current_schema()::regnamespace`).all(),i=new Map(_.map(e=>[e.tbl.replace(/"/g,"").replace(/^.*\./,""),e.cols])),O=await r.prepareUncached("SELECT table_name, column_name FROM information_schema.columns WHERE table_schema = current_schema() ORDER BY table_name, ordinal_position").all(),b=new Set(se),a={};let h=0,c=0;for(const e of P.filter(u=>u!==C)){const u=b.has(e)?["seq"]:i.get(e),g=u!==void 0&&u.length>0?u.map(l=>`t.${F(l)}`):O.filter(l=>l.table_name===e).map(l=>`t.${F(l.column_name)}::text`);if(g.length===0)throw new Error(`table ${e} has no columns to order its dump by`);let N=(await r.prepareUncached(`SELECT row_to_json(t)::text AS j FROM ${F(e)} t ORDER BY ${g.join(", ")}`).all()).map(l=>JSON.parse(l.j));e==="messages"&&(N=N.map(l=>{const Y=l.body_ref,j=he(l,ue);return typeof Y=="string"&&Y.startsWith("/")&&(j===l?c+=1:h+=1),j})),a[e]=N.length,H(f(t,oe.rows("",e).replace(/^imports\/\//,"")),ie(N))}const p=(await r.prepare("SELECT value FROM canvas_meta WHERE key = 'install_id'").get())?.value;if(p===void 0)throw new Error("this canvas database has no install id");const R=await r.prepare("SELECT object FROM environments WHERE kind = 'machine' ORDER BY object LIMIT 1").get(),d={installId:p,kind:n,person:o,release:re(),schemaVersion:ne,counts:a,...R===void 0?{}:{environmentObject:R.object}},y=await r.prepare("SELECT request_id, request_dir, file FROM human_requests ORDER BY request_id").all(),E=await r.prepare("SELECT object, source_path FROM documents WHERE source_path IS NOT NULL ORDER BY object").all(),I={},L=new Map;for(const e of E){const u=Ee(e.source_path);if(u===null)continue;const g=ee(le(u).body),T=W(g);I[e.object]=T,L.set(T,{hash:T,text:g})}const S=me(t),A=new Set(S.hashes),$=await V(r);we(S,new Set($));const B=$.filter(e=>!A.has(e)),k=new Set(B),z=await U(r,t,"pause",B,[...L.values()].filter(e=>!A.has(e.hash)&&!k.has(e.hash))),G=S.count+B.length+[...L.values()].filter(e=>!A.has(e.hash)&&!k.has(e.hash)).length;a[C]=G,d.bodyParts=[...S.parts,...z];const X=await r.prepare(`SELECT n.node_id, r.grantee, n.cwd, w.path AS worktree FROM nodes n JOIN node_rows r ON r.node_id = n.node_id LEFT JOIN worktrees w ON w.node_id = n.node_id AND w.state = 'open'
3
+ WHERE n.status IN ('active', 'idle') ORDER BY n.node_id`).all();return d.shippedBodies=I,{source:d,requests:y.map(e=>({id:e.request_id,dir:e.request_dir,file:e.file})),runningNodes:X.map(e=>({node:e.node_id,app:e.grantee,cwd:e.cwd,worktree:e.worktree})),bodiesInlined:h,bodiesMissing:c}})}s(xe,"dumpRows");export{D as BODY_MAX_BYTES,pe as PART_JSON_BYTES,Be as dumpBodiesPre,xe as dumpRows};
@@ -0,0 +1,22 @@
1
+ import type { ImportCloud } from './cloud.js';
2
+ export interface LocalObject {
3
+ key: string;
4
+ path: string;
5
+ }
6
+ /** Every file the runtime holds that the cloud serves from a blob key. */
7
+ export declare function localObjects(): LocalObject[];
8
+ /** The reviewed file of a review that lies outside the review's own directory, at the key the lane rewrites `file` to. */
9
+ export declare function reviewSourceObject(requestId: string, requestDir: string, file: string | null): LocalObject | null;
10
+ export declare class Uploader {
11
+ private readonly cloud;
12
+ private readonly run;
13
+ private readonly progress;
14
+ private readonly sent;
15
+ uploaded: number;
16
+ missing: string[];
17
+ constructor(cloud: ImportCloud, run: string, progress: (line: string) => void);
18
+ /** Upload source blobs, each to `imports/<run>/blobs/<its key>`: the upload route signs only under the run's own prefix, and the lane moves each blob to its key. */
19
+ upload(objects: readonly LocalObject[]): Promise<void>;
20
+ /** Upload bytes held in memory (row files, source.json, node archives). */
21
+ uploadBytes(key: string, data: Buffer): Promise<void>;
22
+ }
@@ -0,0 +1 @@
1
+ var f=Object.defineProperty;var a=(s,o)=>f(s,"name",{value:o,configurable:!0});import{readFileSync as y,readdirSync as w,statSync as g}from"node:fs";import{basename as b,join as l,relative as k,sep as $}from"node:path";import{runtimeRoot as j}from"../../layout.js";import{bundleKeys as p}from"../bundle/layout.js";import{runBlobKey as d}from"../bundle/blob-keys.js";function c(s,o,t=s){let r;try{r=w(s,{withFileTypes:!0})}catch(e){if(e.code==="ENOENT")return;throw e}for(const e of r){const n=l(s,e.name);e.isDirectory()?c(n,o,t):e.isFile()&&!e.name.endsWith(".tmp")&&!e.name.includes(".tmp-")&&o(n,k(t,n).split($).join("/"))}}a(c,"walk");function S(){const s=j(),o=[];c(l(s,"blobs"),(t,r)=>o.push({key:r,path:t}));for(const t of["requests","reviews"])c(l(s,t),(r,e)=>{const[n,...u]=e.split("/");u.length>0&&o.push({key:p.request(n,u.join("/")),path:r})});return o}a(S,"localObjects");function W(s,o,t){return t===null||!t.startsWith("/")||t.startsWith(o.endsWith("/")?o:`${o}/`)?null:{key:p.request(s,`source/${b(t)}`),path:t}}a(W,"reviewSourceObject");class F{static{a(this,"Uploader")}cloud;run;progress;sent=new Map;uploaded=0;missing=[];constructor(o,t,r){this.cloud=o,this.run=t,this.progress=r}async upload(o){const t=[];for(const r of o){let e;try{e=g(r.path)}catch(u){if(u.code==="ENOENT"){this.missing.push(r.path);continue}throw u}const n=`${e.size}:${e.mtimeMs}`;this.sent.get(r.key)!==n&&t.push({...r,stamp:n})}for(let r=0;r<t.length;r+=100){const e=t.slice(r,r+100),{urls:n}=await this.cloud.uploadUrls(this.run,e.map(i=>d(this.run,i.key)));let u=0;const m=a(async()=>{for(let i=e[u++];i!==void 0;i=e[u++]){const h=n[d(this.run,i.key)];if(h===void 0)throw new Error(`no upload URL returned for ${i.key}`);await this.cloud.put(h,y(i.path)),this.sent.set(i.key,i.stamp),this.uploaded+=1}},"worker");await Promise.all(Array.from({length:Math.min(8,e.length)},m)),this.progress(`uploaded ${Math.min(r+100,t.length)}/${t.length}`)}}async uploadBytes(o,t){const{urls:r}=await this.cloud.uploadUrls(this.run,[o]),e=r[o];if(e===void 0)throw new Error(`no upload URL returned for ${o}`);await this.cloud.put(e,t)}}export{F as Uploader,S as localObjects,W as reviewSourceObject};
@@ -0,0 +1,3 @@
1
+ import type { RowObject } from '../bundle/layout.js';
2
+ /** `read` returns the file's text, or null when the file is absent or empty. */
3
+ export declare function inlineMessageBody(row: RowObject, read: (path: string) => string | null): RowObject;
@@ -0,0 +1 @@
1
+ var f=Object.defineProperty;var r=(t,e)=>f(t,"name",{value:e,configurable:!0});function i(t,e){const o=t.body_ref;if(typeof o!="string"||!o.startsWith("/"))return t;const s=e(o);if(s===null)return t;const n=t.body_json,y=n!==null&&typeof n=="object"&&!Array.isArray(n)?n:{};return{...t,body_ref:null,body_json:{...y,body_full:s}}}r(i,"inlineMessageBody");export{i as inlineMessageBody};
@@ -0,0 +1,2 @@
1
+ import type { PathPrefix, SourceJson } from '../bundle/layout.js';
2
+ export declare function buildSourceJson(dump: SourceJson, prefixes: readonly PathPrefix[]): SourceJson;
@@ -0,0 +1 @@
1
+ var o=Object.defineProperty;var e=(t,n)=>o(t,"name",{value:n,configurable:!0});function u(t,n){return{...t,...n.length>0?{pathPrefixes:[...n]}:{}}}e(u,"buildSourceJson");export{u as buildSourceJson};
@@ -0,0 +1,9 @@
1
+ import type { ImportAuthStoreDTO, ImportCredentialsStoreDTO, ImportEnvStoreDTO, ImportPluginsStoreDTO, ImportSettingsStoreDTO } from '../../../api/dto/imports.js';
2
+ export interface ExportedStores {
3
+ auth: ImportAuthStoreDTO;
4
+ credentials: ImportCredentialsStoreDTO;
5
+ env: ImportEnvStoreDTO;
6
+ settings: ImportSettingsStoreDTO;
7
+ plugins: ImportPluginsStoreDTO;
8
+ }
9
+ export declare function readStores(): Promise<ExportedStores>;
@@ -0,0 +1 @@
1
+ var k=Object.defineProperty;var _=(a,t)=>k(a,"name",{value:t,configurable:!0});import{openDb as R}from"../../canvas/db.js";import{personIdAsync as h}from"../../canvas/person.js";import{loadSecretsKey as y,open as c}from"../../secrets/index.js";import{aadOf as v}from"../../subscription-state.js";import{aadOf as O}from"../../secrets/env-rows.js";const r=_(a=>a==null?null:String(a),"str"),o=_(a=>Number(a??0),"num");async function M(){const a=await h(),t=await R(),d=y(),u={};for(const e of await t.prepare("SELECT provider_id, credential_sealed, key_id FROM model_api_keys WHERE person = ? ORDER BY provider_id").all(a)){const n=String(e.provider_id);u[n]=JSON.parse(c(e.credential_sealed,String(e.key_id),d,`model_api_keys|${a}|${n}`))}const w=await t.prepare("SELECT provider_id, account_id, family, rate_limited_until, last_rate_limited_at FROM model_account_cooldowns WHERE person = ? ORDER BY family").all(a),E=[];for(const e of await t.prepare("SELECT * FROM model_accounts WHERE person = ? ORDER BY provider_id, position").all(a)){const n=String(e.provider_id),l=String(e.id);if(e.refresh_sealed===null||e.access_sealed===null||e.key_id===null)throw new Error(`managed ${n} account ${l} has no credential record`);const g=String(e.key_id);E.push({provider:n,id:l,label:String(e.label),labelKey:String(e.label_key),position:o(e.position),lastAttemptAt:o(e.last_attempt_at),lastRateLimitedAt:o(e.last_rate_limited_at),rateLimitedUntil:o(e.rate_limited_until),...e.auth_failure==="invalid_grant"?{authFailure:"invalid_grant"}:{},refresh:c(e.refresh_sealed,g,d,v(a,n,l,"refresh")),access:c(e.access_sealed,g,d,v(a,n,l,"access")),expires:o(e.expires),...r(e.vendor_account_id)===null?{}:{accountId:r(e.vendor_account_id)},...r(e.client_id)===null?{}:{clientId:r(e.client_id)},cooldowns:w.filter(s=>s.provider_id===n&&s.account_id===l).map(s=>({family:String(s.family),until:o(s.rate_limited_until),at:o(s.last_rate_limited_at)}))})}const i=await t.prepare("SELECT default_fallback_strength, revert_when_available, openai_device_id FROM model_settings WHERE person = ?").get(a),S={accounts:E,...i===void 0||i.default_fallback_strength===null&&i.revert_when_available===null?{}:{rotation:{defaultFallbackStrength:r(i.default_fallback_strength),revertWhenAvailable:i.revert_when_available===null?null:o(i.revert_when_available)===1}},...i===void 0||r(i.openai_device_id)===null?{}:{openaiDeviceId:r(i.openai_device_id)}},m={};for(const e of await t.prepare("SELECT owner_id, name, value_sealed, key_id FROM env_vars WHERE person = ? AND owner_kind = 'profile' ORDER BY owner_id, name").all(a)){const n=String(e.owner_id),l=String(e.name);(m[n]??={})[l]=c(e.value_sealed,String(e.key_id),d,O({person:a,kind:"profile",id:n},l))}const p={settings:{},state:{},profileProjects:{},defaults:{}};for(const e of await t.prepare("SELECT owner_key, raw_json FROM scope_settings WHERE person = ?").all(a))p.settings[String(e.owner_key)]=JSON.parse(String(e.raw_json));for(const e of await t.prepare("SELECT owner_key, last_self_check, active_canvas FROM scope_bookkeeping WHERE person = ?").all(a))p.state[String(e.owner_key)]={last_self_check:r(e.last_self_check),activeCanvas:r(e.active_canvas)};for(const e of await t.prepare("SELECT pp.profile_id, pp.path FROM profile_projects pp JOIN profiles p ON p.id = pp.profile_id WHERE p.person = ? ORDER BY pp.profile_id, pp.position").all(a))(p.profileProjects[String(e.profile_id)]??=[]).push(String(e.path));for(const e of await t.prepare("SELECT directory, profile_id FROM profile_defaults WHERE person = ?").all(a))p.defaults[String(e.directory)]=String(e.profile_id);const f={marketplaces:[],plugins:[]};for(const e of await t.prepare("SELECT * FROM scope_marketplaces WHERE person = ? ORDER BY owner_key, name").all(a))f.marketplaces.push({owner:String(e.owner_key),name:String(e.name),url:r(e.url),ref:r(e.ref),installed_at:r(e.installed_at),last_updated:r(e.last_updated)});for(const e of await t.prepare("SELECT * FROM scope_plugins WHERE person = ? ORDER BY owner_key, name").all(a))f.plugins.push({owner:String(e.owner_key),name:String(e.name),enabled:o(e.enabled)===1,source_marketplace:r(e.source_marketplace),version:r(e.version),source_path:r(e.source_path),last_updated:r(e.last_updated),etag:r(e.etag),checked_at:r(e.checked_at)});return{auth:{apiKeys:u},credentials:S,env:{profiles:m},settings:p,plugins:f}}_(M,"readStores");export{M as readStores};
@@ -0,0 +1,19 @@
1
+ import type { CanvasDb } from '../../canvas/db.js';
2
+ import { type RewriteInput } from './rewrite.js';
3
+ /** A grant's or row's `sub` as the run sees it: the source's person is the run's, no other person is allowed. */
4
+ export declare function boundSub(sub: string, source: string, run: string, where: string): string;
5
+ /** Before anything is staged: the bundle's person has the shape of a person, and when it differs from the run's it is not already someone else in the target. */
6
+ export declare function checkSourcePerson(db: CanvasDb, target: string, source: string, run: string): Promise<void>;
7
+ /** After staging, before rewrite 1: every person the staged rows name is the bundle's or the run's. Drops the directory's own team facts
8
+ * (`crtr/group` relations). Returns rows dropped per table. */
9
+ export declare function checkStagedPersons(input: RewriteInput): Promise<Record<string, number>>;
10
+ /** After rewrite 2 and the drop: every reference from a staged row to a target row reaches a row the run's person owns or acts for (an app, a shipped
11
+ * document, the person's own object, an object with an `acts_for` fact for the person, or anything below those that is not directly under an app), and no profile or grant the
12
+ * rows name belongs to another person. */
13
+ export declare function checkStagedReferences(input: RewriteInput): Promise<void>;
14
+ /** After staging, before rewrite 1: every blob key a staged row names has the shape its row would produce and an owner (node) that is in this bundle.
15
+ * A key naming another person's blob is `person_mismatch`; a key of no known shape is `bundle_invalid`. Without this, the importer's own node would read, and
16
+ * compaction would delete, a blob of someone else's conversation. (Human requests are checked by `checkStagedRequestKeys`, after their dirs are rewritten to blob keys.) */
17
+ export declare function checkStagedBlobKeys(input: RewriteInput): Promise<void>;
18
+ /** After rewrite 4b: a human request's `request_dir`, `presentation_dir` and `file` that are blob keys (`requests/…`) are under the request's own `requests/<its id>`. */
19
+ export declare function checkStagedRequestKeys(input: RewriteInput): Promise<void>;
@@ -0,0 +1,15 @@
1
+ var g=Object.defineProperty;var u=(s,t)=>g(s,"name",{value:t,configurable:!0});import{parseBlobKey as L}from"../bundle/blob-keys.js";import{q as E}from"./catalog.js";import{objectIdColumns as R}from"./rewrite.js";import{ImportLaneError as m}from"./stage.js";const w=/^user:[A-Za-z0-9._-]+$/,S=u(s=>s.replace(/^user:/,""),"bare"),I=u((s,t)=>`${E(s)}.${E(t)}`,"sq"),$=u(async(s,t,...r)=>await s.prepareUncached(t).all(...r),"all");function _(s,t){return new m("bind","person_mismatch",s,t)}u(_,"mismatch");function D(s,t,r,a){if(s===t||s===r)return r;throw _(`${a} names ${s}, which is neither the bundle's person nor the importing person`,{where:a,value:s})}u(D,"boundSub");async function k(s,t,r,a){if(!w.test(r))throw _(`the bundle's person ${JSON.stringify(r)} is not a user:<id> principal`,{where:"source.json person",value:r});if(r===a)return;const i=u(e=>I(t,e),"T"),n=await s.prepareUncached(`SELECT 'identities' AS tbl FROM ${i("identities")} WHERE scheme IN ('user', 'local-user') AND external_id = ?
2
+ UNION ALL SELECT 'grants' FROM ${i("grants")} WHERE sub = ? UNION ALL SELECT 'profiles' FROM ${i("profiles")} WHERE person = ?
3
+ UNION ALL SELECT 'nodes' FROM ${i("nodes")} WHERE sub = ? LIMIT 1`).get(r,r,r,r);if(n!==void 0)throw _(`the bundle's person ${r} is already a person in the target (${n.tbl}), not the importing person ${a}`,{where:`${n.tbl}`,value:r})}u(k,"checkSourcePerson");const v=[["nodes","sub"],["crons","sub"],["grants","sub"],["profiles","person"],["charges","sub"],["limits","sub"],["audit_rows","sub"],["provider_approvals","sub"]],C=[["charges","payer"],["limits","payer"],["audit_rows","payer"],["audit_rows","grantee"],["charges","grantee"],["run_events","grantee"],["idempotency_keys","grantee"],["uploads","grantee"],["provider_approvals","grantee"],["profiles","grantee"],["crons","grantee"]];async function H(s){const{db:t,schema:r,sourcePerson:a,targetPerson:i}=s,n=u(p=>I(r,p),"S"),e=u((p,d)=>{throw _(`${p} names ${d.join(", ")}, who is neither the bundle's person ${a} nor the importing person ${i}`,{where:p,values:d})},"refuse");for(const[p,d]of v){const o=await $(t,`SELECT DISTINCT ${E(d)} AS v FROM ${n(p)} WHERE ${E(d)} IS NOT NULL AND ${E(d)} <> '' AND ${E(d)} NOT IN (?, ?) LIMIT 5`,a,i);o.length>0&&e(`${p}.${d}`,o.map(l=>l.v))}for(const[p,d]of C){const o=await $(t,`SELECT DISTINCT ${E(d)} AS v FROM ${n(p)} WHERE ${E(d)} LIKE 'user:%' AND ${E(d)} NOT IN (?, ?) LIMIT 5`,a,i);o.length>0&&e(`${p}.${d}`,o.map(l=>l.v))}const c=await $(t,`SELECT DISTINCT external_id AS v FROM ${n("identities")} WHERE scheme IN ('user', 'local-user') AND external_id NOT IN (?, ?) LIMIT 5`,a,i);c.length>0&&e("identities.external_id",c.map(p=>p.v));const f=await $(t,`SELECT DISTINCT adapter_json::jsonb #>> '{principal,sub}' AS v FROM ${n("daemon_actions")}
4
+ WHERE adapter_json::jsonb #>> '{principal,sub}' IS NOT NULL AND adapter_json::jsonb #>> '{principal,sub}' NOT IN (?, ?) LIMIT 5`,a,i);f.length>0&&e("daemon_actions principal.sub",f.map(p=>p.v));const h=await $(t,`SELECT DISTINCT subject_id AS v FROM ${n("relations")} WHERE relation = 'acts_for' AND subject_type = 'user' AND subject_id NOT IN (?, ?) LIMIT 5`,S(a),S(i));h.length>0&&e("relations acts_for subject",h.map(p=>p.v));const N=Number((await t.prepareUncached(`DELETE FROM ${n("relations")} WHERE resource_type = 'crtr/group'`).run()).changes);return N>0?{relations:N}:{}}u(H,"checkStagedPersons");async function W(s){const{db:t,schema:r,target:a,catalog:i,targetPerson:n}=s,e=u(o=>I(r,o),"S"),c=u(o=>I(a,o),"T"),f=S(n);await t.exec(`CREATE TABLE ${e("_refs")} (tbl text NOT NULL, col text NOT NULL, value text NOT NULL)`);const h=[];for(const[o,l]of R(i))if(!(!i.tables.has(o)||o==="relations"))for(const b of l)!(o==="nodes"&&b==="profile_id")&&!(o==="crons"&&b==="profile")&&h.push([o,b,""]);h.push(["relations","resource_id","WHERE resource_type = 'crtr/object'"]);for(const[o,l,b]of h)await t.prepareUncached(`INSERT INTO ${e("_refs")} SELECT ?, ?, ${E(l)}::text FROM ${e(o)} ${b===""?"WHERE":`${b} AND`} ${E(l)} IS NOT NULL
5
+ AND ${E(l)}::text NOT IN (SELECT id FROM ${e("objects")}) AND ${E(l)}::text IN (SELECT id FROM ${c("objects")})`).run(o,l);const N=await $(t,`WITH RECURSIVE chain(start, id) AS (
6
+ SELECT DISTINCT value, value FROM ${e("_refs")}
7
+ UNION SELECT c.start, o.owner FROM chain c JOIN ${c("objects")} o ON o.id = c.id JOIN ${c("objects")} up ON up.id = o.owner AND up.type <> 'app'),
8
+ ok(id) AS (
9
+ SELECT o.id FROM ${c("objects")} o WHERE o.type = 'app'
10
+ UNION SELECT d.object FROM ${c("documents")} d WHERE d.source_path IS NOT NULL
11
+ UNION SELECT i.object FROM ${c("identities")} i WHERE i.scheme IN ('user', 'local-user') AND i.external_id = ?
12
+ UNION SELECT r.resource_id FROM ${c("relations")} r WHERE r.resource_type = 'crtr/object' AND r.relation = 'acts_for' AND r.subject_type = 'user' AND r.subject_id = ?)
13
+ SELECT r.tbl, r.col, r.value FROM ${e("_refs")} r
14
+ WHERE NOT EXISTS (SELECT 1 FROM chain c JOIN ok ON ok.id = c.id WHERE c.start = r.value) LIMIT 10`,n,f);if(N.length>0)throw _(`the bundle refers to objects that are not ${n}'s: ${N.map(o=>`${o.tbl}.${o.col}=${o.value}`).join(", ")}`,{references:N});const p=[["nodes","profile_id"],["crons","profile"],["profiles","id"]];for(const[o,l]of p){const b=await $(t,`SELECT DISTINCT s.${E(l)} AS v FROM ${e(o)} s JOIN ${c("profiles")} p ON p.id = s.${E(l)} WHERE p.person <> ? LIMIT 5`,n);if(b.length>0)throw _(`${o}.${l} names profiles that belong to another person: ${b.map(T=>T.v).join(", ")}`,{where:`${o}.${l}`,values:b.map(T=>T.v)})}for(const[o,l]of[["nodes","grant_id"],["crons","grant_id"]]){const b=await $(t,`SELECT DISTINCT s.${E(l)} AS v FROM ${e(o)} s JOIN ${c("grants")} g ON g.id = s.${E(l)} WHERE g.sub <> ? LIMIT 5`,n);if(b.length>0)throw _(`${o}.${l} names grants that belong to another person: ${b.map(T=>T.v).join(", ")}`,{where:`${o}.${l}`,values:b.map(T=>T.v)})}const d=await $(t,`SELECT DISTINCT d.adapter_json::jsonb #>> '{principal,grant_id}' AS v FROM ${e("daemon_actions")} d
15
+ JOIN ${c("grants")} g ON g.id = d.adapter_json::jsonb #>> '{principal,grant_id}' WHERE g.sub <> ? LIMIT 5`,n);if(d.length>0)throw _(`a queued action names grants that belong to another person: ${d.map(o=>o.v).join(", ")}`,{where:"daemon_actions principal.grant_id",values:d.map(o=>o.v)})}u(W,"checkStagedReferences");const O=u((s,t)=>new m("bind","bundle_invalid",s,t),"bundleInvalid");async function U(s){const{db:t,schema:r}=s,a=u(e=>I(r,e),"S"),i=new Set((await $(t,`SELECT node_id AS v FROM ${a("nodes")}`)).map(e=>e.v)),n=u((e,c,f,h)=>{const N=L(c);if(N===null||N.kind!==f||h!==void 0&&c!==h)throw O(`${e} holds ${JSON.stringify(c)}, which is not a ${f} blob key${h===void 0?"":` of the shape ${h}`}`,{where:e,value:c});if(!i.has(N.owner))throw _(`${e} holds ${c}, which belongs to node ${N.owner}, who is not in the bundle`,{where:e,value:c})},"check");for(const e of await $(t,`SELECT conversation_id, seq::text AS seq, blob_key FROM ${a("conversation_parts")}`))if(n("conversation_parts.blob_key",e.blob_key,"conversation"),e.blob_key.split("/")[2]!==e.conversation_id)throw O(`conversation_parts.blob_key ${e.blob_key} is not under its conversation ${e.conversation_id}`,{where:"conversation_parts.blob_key",value:e.blob_key});for(const e of await $(t,`SELECT blob_key FROM ${a("upload_uses")} WHERE blob_key IS NOT NULL`))n("upload_uses.blob_key",e.blob_key,"attachment");for(const e of await $(t,`SELECT object, output_key FROM ${a("jobs")} WHERE output_key IS NOT NULL`)){const c=e.output_key.split("/")[1]??"";n("jobs.output_key",e.output_key,"job",`jobs/${c}/${e.object}/output`)}}u(U,"checkStagedBlobKeys");async function q(s){const{db:t,schema:r}=s;for(const a of await $(t,`SELECT request_id, request_dir, presentation_dir, file FROM ${I(r,"human_requests")}`))for(const i of["request_dir","presentation_dir","file"]){const n=a[i];if(n===null||!n.startsWith("requests/"))continue;const e=L(n.includes("/",9)?n:`${n}/x`);if(e===null||e.kind!=="request")throw O(`human_requests.${i} holds ${JSON.stringify(n)}, which is not a request blob key`,{where:`human_requests.${i}`,value:n});if(e.owner!==a.request_id)throw _(`human_requests.${i} of ${a.request_id} holds ${n}, which belongs to request ${e.owner}`,{where:`human_requests.${i}`,value:n})}}u(q,"checkStagedRequestKeys");export{D as boundSub,k as checkSourcePerson,U as checkStagedBlobKeys,H as checkStagedPersons,W as checkStagedReferences,q as checkStagedRequestKeys};
@@ -0,0 +1,10 @@
1
+ import type { BlobStore } from '../../blobs/index.js';
2
+ import type { CanvasDb } from '../../canvas/db.js';
3
+ export interface MovedBlobs {
4
+ moved: number;
5
+ left: number;
6
+ }
7
+ /** `created` collects the keys this call wrote, so the caller can delete them if the run fails before its copy commits. */
8
+ export declare function moveRunBlobs(db: CanvasDb, schema: string, blobs: BlobStore, runId: string, created: string[]): Promise<MovedBlobs>;
9
+ /** Delete the run's uploaded copies (after the run is done). */
10
+ export declare function deleteRunBlobs(blobs: BlobStore, runId: string): Promise<void>;
@@ -0,0 +1 @@
1
+ var S=Object.defineProperty;var r=(e,n)=>S(e,"name",{value:n,configurable:!0});import{parseBlobKey as q,runBlobPrefix as f}from"../bundle/blob-keys.js";import{q as m}from"./catalog.js";import{ImportLaneError as h}from"./stage.js";const E=8;async function k(e,n,o,g,y){const v={nodes:new Set((await e.prepareUncached(`SELECT node_id AS v FROM ${m(n)}.nodes`).all()).map(t=>t.v)),human_requests:new Set((await e.prepareUncached(`SELECT request_id AS v FROM ${m(n)}.human_requests`).all()).map(t=>t.v))},w=f(g),s=await o.list(w),i={moved:0,left:0};let p=0;const x=r(async()=>{for(let t=p++;t<s.length;t=p++){const l=s[t],a=l.slice(w.length),c=q(a);if(c===null||!v[c.ownerTable].has(c.owner)){i.left+=1;continue}const u=await o.get(l);if(u===null)throw new h("blobs","bundle_invalid",`${l} was listed and is gone`);const d=await o.get(a);if(d===null)await o.put(a,u),y.push(a);else if(!d.equals(u))throw new h("blobs","unique_conflict",`${a} already exists with other contents; the import does not replace it`,{key:a});i.moved+=1}},"worker");return await Promise.all(Array.from({length:Math.min(E,s.length)},x)),i}r(k,"moveRunBlobs");async function A(e,n){for(const o of await e.list(f(n)))await e.delete(o)}r(A,"deleteRunBlobs");export{A as deleteRunBlobs,k as moveRunBlobs};
@@ -0,0 +1,32 @@
1
+ import type { CanvasDb } from '../../canvas/db.js';
2
+ export interface ForeignKey {
3
+ table: string;
4
+ columns: string[];
5
+ parent: string;
6
+ parentColumns: string[];
7
+ }
8
+ export interface UniqueKey {
9
+ table: string;
10
+ name: string;
11
+ columns: string[];
12
+ predicate: string | null;
13
+ }
14
+ /** The columns that identify a row of `info`: its primary key, or every column in order for a table with none (`memory_reads`). */
15
+ export declare const rowKey: (info: TableInfo) => string[];
16
+ export interface TableInfo {
17
+ name: string;
18
+ columns: string[];
19
+ primaryKey: string[];
20
+ identity: string[];
21
+ }
22
+ export interface Catalog {
23
+ schema: string;
24
+ tables: Map<string, TableInfo>;
25
+ foreignKeys: ForeignKey[];
26
+ uniqueKeys: UniqueKey[];
27
+ }
28
+ export declare const q: (name: string) => string;
29
+ /** Read the catalog of `schema` for `tables`. */
30
+ export declare function readCatalog(db: CanvasDb, schema: string, tables: readonly string[]): Promise<Catalog>;
31
+ /** Parents before children. Cycles (none in the schema today) are broken by the table list's order. */
32
+ export declare function topologicalOrder(catalog: Catalog, tables: readonly string[]): string[];
@@ -0,0 +1,9 @@
1
+ var g=Object.defineProperty;var r=(a,n)=>g(a,"name",{value:n,configurable:!0});const x=r(a=>a.primaryKey.length>0?a.primaryKey:a.columns,"rowKey"),y=r(a=>`"${a.replace(/"/g,'""')}"`,"q");async function O(a,n,p){const i=[...p],d=await a.prepareUncached(`SELECT table_name, column_name, is_identity FROM information_schema.columns
2
+ WHERE table_schema = ? AND table_name = ANY(?::text[]) ORDER BY table_name, ordinal_position`).all(n,`{${i.map(e=>`"${e}"`).join(",")}}`),o=new Map;for(const e of i)o.set(e,{name:e,columns:[],primaryKey:[],identity:[]});for(const e of d){const s=o.get(e.table_name);s.columns.push(e.column_name),e.is_identity==="YES"&&s.identity.push(e.column_name)}const l=await a.prepareUncached(`SELECT c.conrelid::regclass::text AS tbl, c.contype, c.conname,
3
+ (SELECT array_agg(a.attname::text ORDER BY k.ord) FROM unnest(c.conkey) WITH ORDINALITY k(attnum, ord) JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = k.attnum) AS cols,
4
+ c.confrelid::regclass::text AS ptbl,
5
+ (SELECT array_agg(a.attname::text ORDER BY k.ord) FROM unnest(c.confkey) WITH ORDINALITY k(attnum, ord) JOIN pg_attribute a ON a.attrelid = c.confrelid AND a.attnum = k.attnum) AS pcols
6
+ FROM pg_constraint c WHERE c.connamespace = ?::regnamespace AND c.contype IN ('p', 'f')`).all(n),c=r(e=>e.replace(/^.*\./,"").replace(/"/g,""),"bare"),t=[];for(const e of l){const s=c(e.tbl);o.has(s)&&(e.contype==="p"?o.get(s).primaryKey=e.cols:o.has(c(e.ptbl))&&t.push({table:s,columns:e.cols,parent:c(e.ptbl),parentColumns:e.pcols}))}const u=(await a.prepareUncached(`SELECT t.relname AS tbl, i.relname AS name, pg_get_expr(x.indpred, x.indrelid) AS predicate, x.indexprs IS NOT NULL AS has_expr,
7
+ (SELECT array_agg(a.attname::text ORDER BY k.ord) FROM unnest(x.indkey::int2[]) WITH ORDINALITY k(attnum, ord) JOIN pg_attribute a ON a.attrelid = x.indrelid AND a.attnum = k.attnum) AS cols
8
+ FROM pg_index x JOIN pg_class t ON t.oid = x.indrelid JOIN pg_class i ON i.oid = x.indexrelid
9
+ WHERE t.relnamespace = ?::regnamespace AND x.indisunique AND NOT x.indisprimary`).all(n)).filter(e=>o.has(e.tbl)&&!e.has_expr).map(e=>({table:e.tbl,name:e.name,columns:e.cols,predicate:e.predicate}));return{schema:n,tables:o,foreignKeys:t,uniqueKeys:u}}r(O,"readCatalog");function _(a,n){const p=new Set(n),i=new Map(n.map(t=>[t,new Set]));for(const t of a.foreignKeys)p.has(t.table)&&p.has(t.parent)&&t.table!==t.parent&&i.get(t.table).add(t.parent);const d=[],o=new Set,l=new Set,c=r(t=>{if(!(o.has(t)||l.has(t))){l.add(t);for(const m of[...i.get(t)].sort())c(m);l.delete(t),o.add(t),d.push(t)}},"visit");for(const t of n)c(t);return d}r(_,"topologicalOrder");export{y as q,O as readCatalog,x as rowKey,_ as topologicalOrder};
@@ -0,0 +1,26 @@
1
+ import type { CanvasDb } from '../../canvas/db.js';
2
+ import { type PathMap } from '../bundle/layout.js';
3
+ import { type Catalog } from './catalog.js';
4
+ import type { IdMap } from './rewrite.js';
5
+ import type { SourceJson } from '../bundle/layout.js';
6
+ export interface CopyResult {
7
+ inserted: Record<string, number>;
8
+ mapped: Record<string, number>;
9
+ }
10
+ /** `import_manifest.inserted_keys`: a zstd-compressed JSON array of primary-key tuples (each value as text). */
11
+ export declare const encodeKeys: (keys: string[][]) => Buffer;
12
+ export declare const decodeKeys: (data: Buffer) => string[][];
13
+ export interface CopyInput {
14
+ db: CanvasDb;
15
+ runId: string;
16
+ schema: string;
17
+ target: string;
18
+ catalog: Catalog;
19
+ source: SourceJson;
20
+ person: string;
21
+ idMap: IdMap;
22
+ pathMap: PathMap;
23
+ }
24
+ export declare const copyOrder: (catalog: Catalog) => string[];
25
+ /** Must run inside `withCanvasWrite`. */
26
+ export declare function copyRows(input: CopyInput): Promise<CopyResult>;
@@ -0,0 +1,6 @@
1
+ var b=Object.defineProperty;var c=(n,r)=>b(n,"name",{value:r,configurable:!0});import{zstdCompressSync as D,zstdDecompressSync as M}from"node:zlib";import{BODIES_TABLE as y,COPIED_TABLES as U,KEY_MAPPED_TABLES as C}from"../bundle/layout.js";import{q as s,rowKey as k,topologicalOrder as F}from"./catalog.js";import{fillEnvironments as H}from"./prepare.js";import{ImportLaneError as j}from"./stage.js";const L=c((n,r)=>`${s(n)}.${s(r)}`,"sq"),q=c(n=>D(Buffer.from(JSON.stringify(n))),"encodeKeys"),V=c(n=>JSON.parse(M(n).toString("utf8")),"decodeKeys"),v=c(n=>F(n,U),"copyOrder");async function Y(n){const{db:r,runId:T,schema:I,target:f,catalog:u,source:N}=n,o=c(e=>L(I,e),"S"),E=c(e=>L(f,e),"T"),m={inserted:{},mapped:{}},h=c(async(e,a)=>{m.inserted[e]=a.length,await r.prepare("INSERT INTO import_manifest (run_id, table_name, inserted_keys) VALUES (?, ?, ?)").run(T,e,q(a))},"manifest");await H(r,I,f,N,n.person);for(const[e,a]of Object.entries(C)){const i=u.tables.get(e).columns.filter(p=>p!==a).map(s).join(", "),d=[],R=await r.prepareUncached(`SELECT ${s(a)}::text AS key FROM ${o(e)} ORDER BY ${s(a)}::bigint`).all();for(const{key:p}of R){const l=await r.prepareUncached(`INSERT INTO ${E(e)} (${i}) SELECT ${i} FROM ${o(e)} WHERE ${s(a)}::text = ? RETURNING ${s(a)}::text AS key`).get(p);await r.prepare("INSERT INTO import_key_map (source_install_id, table_name, source_key, target_key) VALUES (?, ?, ?, ?)").run(N.installId,e,p,l.key),d.push([l.key])}await h(e,d)}await r.prepareUncached(`UPDATE ${o("review_comments")} c SET created_seq = m.target_key::bigint FROM import_key_map m
2
+ WHERE m.source_install_id = ? AND m.table_name = 'review_comment_events' AND m.source_key = c.created_seq::text`).run(N.installId);for(const e of v(u)){if(e in C)continue;const a=u.tables.get(e),O=Number((await r.prepareUncached(`SELECT count(*) AS n FROM ${o(e)}`).get()).n);if(O===0){m.inserted[e]=0;continue}const i=k(a),d=a.columns.filter(t=>!a.identity.includes(t)),R=a.identity.length>0?a.identity.map(t=>`s.${s(t)}`).join(", "):i.map(t=>`s.${s(t)}`).join(", ");let p=d.map(t=>`s.${s(t)}`),l="";if(e==="messages"){const t=`NOT EXISTS (SELECT 1 FROM ${E("messages")} t WHERE t.message_id = s.message_id)`,S=Number((await r.prepareUncached(`SELECT count(*) AS n FROM ${o("messages")} s WHERE ${t}`).get()).n);if(l=` WHERE ${t}`,S===0){m.inserted[e]=0,await h(e,[]);continue}const A=Number((await r.prepare("UPDATE message_sequence SET seq = seq + ? WHERE id = 1 RETURNING seq").get(S)).seq);p=d.map($=>$==="seq"?`${A-S} + row_number() OVER (ORDER BY s.seq)`:`s.${s($)}`)}const w=i.map(t=>`${s(t)}::text`).join(", "),g=a.primaryKey.length===0;g&&(l=` WHERE NOT EXISTS (SELECT 1 FROM ${E(e)} t WHERE ${i.map(t=>`t.${s(t)} IS NOT DISTINCT FROM s.${s(t)}`).join(" AND ")})`);const _=await r.prepareUncached(`INSERT INTO ${E(e)} (${d.map(s).join(", ")}) SELECT ${p.join(", ")} FROM ${o(e)} s${l}
3
+ ORDER BY ${R}${g?"":` ON CONFLICT (${i.map(s).join(", ")}) DO NOTHING`} RETURNING ${w}`).all();if(e===y){const t=await r.prepareUncached(`SELECT s.hash FROM ${o(e)} s JOIN ${E(e)} t ON t.hash = s.hash WHERE t.text IS DISTINCT FROM s.text LIMIT 10`).all();if(t.length>0)throw new j("copy","unique_conflict",`the target holds other text under body ${t[0].hash}; its boot repair has not rekeyed that row`,{table:e,hashes:t.map(S=>S.hash)})}e===y?m.inserted[e]=_.length:await h(e,_.map(t=>Object.values(t))),m.mapped[e]=O-_.length}await r.prepareUncached(`DELETE FROM ${E("charge_totals")} t WHERE (t.month, t.payer) IN (SELECT DISTINCT month, payer FROM ${o("charges")})`).run(),await r.prepareUncached(`INSERT INTO ${E("charge_totals")} (month, payer, funding, sub, grantee, provider, usd, billed_usd)
4
+ SELECT month, payer, funding, COALESCE(sub, ''), grantee, COALESCE(provider, ''), COALESCE(SUM(usd), 0), COALESCE(SUM(billed_usd), 0)
5
+ FROM ${E("charges")} WHERE refused IS NULL AND kind <> 'take' AND (month, payer) IN (SELECT DISTINCT month, payer FROM ${o("charges")})
6
+ GROUP BY month, payer, funding, COALESCE(sub, ''), grantee, COALESCE(provider, '')`).run();for(const[e,a]of[["object",n.idMap.object],["grant",n.idMap.grant],["kind",n.idMap.kind]])for(const[O,i]of a)await r.prepare("INSERT INTO import_id_map (run_id, kind, source_value, target_value) VALUES (?, ?, ?, ?) ON CONFLICT DO NOTHING").run(T,e,O,i);for(const e of n.pathMap)await r.prepare("INSERT INTO import_id_map (run_id, kind, source_value, target_value) VALUES (?, 'path', ?, ?) ON CONFLICT DO NOTHING").run(T,e.from,e.to);return await r.prepare("UPDATE import_runs SET state = 'committed', committed_at = ?, step = 'copy' WHERE run_id = ?").run(new Date().toISOString(),T),m}c(Y,"copyRows");export{v as copyOrder,Y as copyRows,V as decodeKeys,q as encodeKeys};
@@ -0,0 +1,7 @@
1
+ import type { CanvasDb } from '../../canvas/db.js';
2
+ import { type PathMap, type PathPrefix, type SourceJson } from '../bundle/layout.js';
3
+ /** A directory two nodes share (a cwd outside both worktrees) maps once, to the first node's folder: both nodes' rows read the same target and each
4
+ * sandbox unpacks its own archive there. */
5
+ export declare function sandboxPathMap(prefixes: readonly PathPrefix[]): PathMap;
6
+ /** One `import_node_files` row per hosted running node the exporter archived. Runs in the copy transaction, after the nodes are copied. */
7
+ export declare function writeNodeFiles(tx: CanvasDb, schema: string, runId: string, source: SourceJson): Promise<number>;
@@ -0,0 +1 @@
1
+ var f=Object.defineProperty;var d=(o,t)=>f(o,"name",{value:t,configurable:!0});import{bundleKeys as l}from"../bundle/layout.js";import{q as c}from"./catalog.js";import{ImportLaneError as s}from"./stage.js";const p="/var/lib/crtr",u="/work/imported";function b(o){const t=o.map(e=>{if(!e.from.startsWith("/")||e.from.split("/").includes(".."))throw new s("stage","bundle_invalid",`path prefix ${e.from} of node ${e.node} is not an absolute path`);const n=e.app.replace(/^app:/,"");if(!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(n)||!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(e.node))throw new s("stage","bundle_invalid",`path prefix names app ${e.app} and node ${e.node}`);const r=`${p}/apps/${n}/nodes/${e.node}`,i=e.role==="context"?`${r}/context`:e.role==="worktree"?`${r}/worktree`:`${u}/${e.node}`;return{from:e.from,to:i}});return t.filter((e,n)=>t.findIndex(r=>r.from===e.from)===n)}d(b,"sandboxPathMap");async function _(o,t,e,n){if(n.kind!=="hosted")return 0;const r=[...new Set((n.pathPrefixes??[]).map(a=>a.node))];let i=0;for(const a of r){if(await o.prepareUncached(`SELECT 1 AS found FROM ${c(t)}.nodes WHERE node_id = ? AND status IN ('active', 'idle')`).get(a)===void 0)throw new s("copy","bundle_invalid",`an archive was uploaded for node ${a}, which is not a running node of the bundle`);i+=Number((await o.prepare("INSERT INTO import_node_files (node, blob_key) VALUES (?, ?) ON CONFLICT DO NOTHING").run(a,l.nodeFiles(e,a))).changes)}return i}d(_,"writeNodeFiles");export{b as sandboxPathMap,_ as writeNodeFiles};
@@ -0,0 +1,37 @@
1
+ import type { CanvasDb } from '../../canvas/db.js';
2
+ import { type PathMap, type SourceJson } from '../bundle/layout.js';
3
+ import { type Catalog } from './catalog.js';
4
+ /** A revision of a document the target already holds, to be added through repo sync's own arrival path. */
5
+ export interface ArrivingRevision {
6
+ document: string;
7
+ rev_id: string;
8
+ parents: string[];
9
+ body_hash: string;
10
+ rationale: string | null;
11
+ created_at: string;
12
+ }
13
+ /** Rewrite 4: for rows of running nodes of a hosted source, path columns go through the run's map. Ended nodes' rows stay as historical text. */
14
+ export declare function rewritePaths(db: CanvasDb, schema: string, map: PathMap): Promise<number>;
15
+ /** Rewrite 4b: a human request's files live in the blob store under `requests/<id>/` (the exporter uploaded the request dir there). `request_dir` becomes that
16
+ * prefix; a `file` or `presentation_dir` inside the old dir keeps its relative path, one outside it was uploaded to `source/<basename>`. A path that is not
17
+ * absolute is already a blob path and stays. */
18
+ export declare function rewriteRequestPaths(db: CanvasDb, schema: string): Promise<number>;
19
+ /** A profile whose name the person already uses for another profile is renamed `<name>-<install id>` (staging only; runs before the pre-check). */
20
+ export declare function renameClashingProfiles(db: CanvasDb, schema: string, target: string, installId: string, person: string): Promise<number>;
21
+ /** Fill: a staged profile with no environment gets the source's machine environment (a Mac) or the person's Blaxel environment (hosted, created if
22
+ * absent). Runs in the copy transaction: it writes target objects. */
23
+ export declare function fillEnvironments(tx: CanvasDb, schema: string, target: string, source: SourceJson, person: string): Promise<number>;
24
+ /** Staged edits that need nothing from the target but its key map: limits the directory or an operator wrote stay out, a hosted source's own
25
+ * machine environment is marked for the drop step, claims of the old runtime's lanes are cleared, and rows of the identity-keyed tables already in
26
+ * `import_key_map` are not staged again. Returns the rows removed per table. */
27
+ export declare function pruneStaged(db: CanvasDb, schema: string, catalog: Catalog, source: SourceJson): Promise<Record<string, number>>;
28
+ /** A staged document whose id the target already holds (repo sync keeps the id across runtimes) is not inserted: its revisions arrive through
29
+ * `insertImportedRevisions`. Removes its staged object, document, revision and parent rows and returns the revisions. Runs before the pre-check. */
30
+ export declare function takeRepoSyncedDocuments(db: CanvasDb, schema: string, target: string): Promise<ArrivingRevision[]>;
31
+ export interface Collision {
32
+ table: string;
33
+ key: string;
34
+ values: string[];
35
+ }
36
+ /** Pre-check (I9): every non-primary unique key and every text primary key the target already holds. Throws `unique_conflict` naming the first 10 values. */
37
+ export declare function precheck(db: CanvasDb, schema: string, target: string, catalog: Catalog, tables: readonly string[], skip: ReadonlySet<string>, seqLike: ReadonlyMap<string, string[]>): Promise<void>;
@@ -0,0 +1,10 @@
1
+ var y=Object.defineProperty;var d=(n,o)=>y(n,"name",{value:o,configurable:!0});import{createProfileBlaxelEnvironment as v}from"../../environments.js";import{KEY_MAPPED_TABLES as U,mapPath as S}from"../bundle/layout.js";import{ImportLaneError as O}from"./stage.js";import{q as l}from"./catalog.js";const $=d((n,o)=>`${l(n)}.${l(o)}`,"sq");async function F(n,o,c){if(c.length===0)return 0;const e=d(s=>$(o,s),"S"),t=`(SELECT node_id FROM ${e("nodes")} WHERE status IN ('active', 'idle'))`;let a=0;const E=d(async(s,r,i,u=!1)=>{const _=await n.prepareUncached(`SELECT ${l(r)}::text AS value FROM ${e(s)} WHERE ${i} AND ${l(r)} IS NOT NULL`).all();for(const{value:R}of _){const T=u?H(R,c):S(c,R);T!==R&&(await n.prepareUncached(`UPDATE ${e(s)} SET ${l(r)} = ? WHERE ${l(r)}::text = ? AND ${i}`).run(T,R),a+=1)}},"apply");return await E("nodes","cwd",`node_id IN ${t}`),await E("worktrees","path",`node_id IN ${t}`),await E("worktrees","repo_root",`node_id IN ${t}`),await E("crons","cwd",`anchor_node IN ${t}`),await E("provider_approvals","prepared_files","TRUE",!0),await E("upload_uses","path",`run_id IN ${t}`),a}d(F,"rewritePaths");async function g(n,o){const c=$(o,"human_requests"),e=d(a=>`CASE WHEN ${a} IS NULL OR ${a} NOT LIKE '/%' THEN ${a}
2
+ WHEN rtrim(${a}, '/') = rtrim(request_dir, '/') THEN 'requests/' || request_id
3
+ WHEN ${a} LIKE rtrim(request_dir, '/') || '/%' THEN 'requests/' || request_id || '/' || substr(${a}, length(rtrim(request_dir, '/')) + 2)
4
+ ELSE 'requests/' || request_id || '/source/' || regexp_replace(${a}, '^.*/', '') END`,"moved"),t=await n.prepareUncached(`UPDATE ${c} SET file = ${e("file")}, presentation_dir = ${e("presentation_dir")},
5
+ request_dir = CASE WHEN request_dir LIKE '/%' THEN 'requests/' || request_id ELSE request_dir END`).run();return Number(t.changes)}d(g,"rewriteRequestPaths");function H(n,o){const c=d(e=>typeof e=="string"?S(o,e):Array.isArray(e)?e.map(c):e!==null&&typeof e=="object"?Object.fromEntries(Object.entries(e).map(([t,a])=>[t,c(a)])):e,"walk");try{return JSON.stringify(c(JSON.parse(n)))}catch{return S(o,n)}}d(H,"rewriteJsonPaths");async function j(n,o,c,e,t){const a=d(r=>$(o,r),"S"),E=await n.prepareUncached(`SELECT id, name FROM ${a("profiles")} ORDER BY id`).all();let s=0;for(const r of E)await n.prepareUncached(`SELECT 1 AS found FROM ${$(c,"profiles")} WHERE person = ? AND name = ? AND id <> ?`).get(t,r.name,r.id)!==void 0&&(await n.prepareUncached(`UPDATE ${a("profiles")} SET name = ? WHERE id = ?`).run(`${r.name}-${e}`,r.id),s+=1);return s}d(j,"renameClashingProfiles");async function q(n,o,c,e,t){const a=d(r=>$(o,r),"S");let E=0;const s=await n.prepareUncached(`SELECT id, grantee FROM ${a("profiles")} WHERE environment_id IS NULL ORDER BY id`).all();for(const r of s){let i=e.kind==="machine"?e.environmentObject??null:null;if(e.kind==="hosted"&&(i=(await n.prepareUncached(`SELECT environment_id FROM ${$(c,"profiles")} WHERE id = ?`).get(r.id))?.environment_id??await v(n,{profileId:r.id,grantee:r.grantee??"app:terminal",person:t})),i===null)throw new O("fill","bundle_invalid",`profile ${r.id} has no environment and the source names none`);await n.prepareUncached(`UPDATE ${a("profiles")} SET environment_id = ? WHERE id = ?`).run(i,r.id),E+=1}return E}d(q,"fillEnvironments");async function x(n,o,c,e){const t=d(s=>$(o,s),"S"),a={},E=d(async(s,r,...i)=>{const u=Number((await n.prepareUncached(r).run(...i)).changes);u>0&&(a[s]=(a[s]??0)+u)},"del");await E("limits",`DELETE FROM ${t("limits")} WHERE source <> 'person'`),e.kind==="hosted"&&(await n.prepareUncached(`UPDATE ${t("profiles")} SET environment_id = NULL WHERE environment_id IN (SELECT object FROM ${t("environments")})`).run(),await n.prepareUncached(`UPDATE ${t("objects")} SET home_machine = 'import-hosted-source' WHERE id IN (SELECT object FROM ${t("environments")})`).run());for(const[s,r]of Object.entries(U))await E(s,`DELETE FROM ${t(s)} WHERE ${l(r)}::text IN (SELECT source_key FROM import_key_map WHERE source_install_id = ? AND table_name = ?)`,e.installId,s);for(const s of c.tables.values())s.columns.includes("claimed_by")&&s.columns.includes("lease_expires_at")&&await n.prepareUncached(`UPDATE ${t(s.name)} SET claimed_by = NULL, lease_expires_at = NULL WHERE claimed_by IS NOT NULL OR lease_expires_at IS NOT NULL`).run();return a}d(x,"pruneStaged");async function b(n,o,c){const e=d(E=>$(o,E),"S"),t=`(SELECT d.object FROM ${e("documents")} d WHERE EXISTS (SELECT 1 FROM ${$(c,"documents")} t WHERE t.object = d.object))`;await n.exec(`CREATE TABLE ${e("_arr_revisions")} AS SELECT r.* FROM ${e("revisions")} r WHERE r.document IN ${t};
6
+ CREATE TABLE ${e("_arr_parents")} AS SELECT p.* FROM ${e("revision_parents")} p WHERE p.child IN (SELECT rev_id FROM ${e("revisions")} WHERE document IN ${t})`);const a=await n.prepareUncached(`SELECT r.document, r.rev_id, r.body_hash, r.rationale, r.created_at,
7
+ COALESCE((SELECT array_agg(p.parent) FROM ${e("revision_parents")} p WHERE p.child = r.rev_id), ARRAY[]::text[]) AS parents
8
+ FROM ${e("revisions")} r WHERE r.document IN ${t} ORDER BY r.document, r.number`).all();return await n.prepareUncached(`DELETE FROM ${e("revision_parents")} WHERE child IN (SELECT rev_id FROM ${e("revisions")} WHERE document IN ${t})`).run(),await n.prepareUncached(`DELETE FROM ${e("revisions")} WHERE document IN ${t}`).run(),await n.prepareUncached(`DELETE FROM ${e("objects")} WHERE id IN ${t}`).run(),await n.prepareUncached(`DELETE FROM ${e("documents")} WHERE object IN ${t}`).run(),a.map(E=>({...E}))}d(b,"takeRepoSyncedDocuments");async function k(n,o,c,e,t,a,E){const s=d(i=>[...E.get(i)??[],...i==="profiles"?["environment_id"]:[],"claimed_by","lease_expires_at"],"ignored"),r=[];for(const i of t){const u=e.tables.get(i);if(u===void 0||a.has(i)||u.primaryKey.length===0)continue;const _=u.primaryKey,R=_.some(p=>u.identity.includes(p)),T=_.map(p=>`s.${l(p)} = t.${l(p)}`).join(" AND "),w=R?"TRUE":_.map(p=>`s.${l(p)} IS DISTINCT FROM t.${l(p)}`).join(" OR "),I=new Set([...u.identity,...E.get(i)??[]]);for(const p of e.uniqueKeys.filter(f=>f.table===i&&!f.columns.every(m=>I.has(m)))){const f=p.predicate===null?"":` WHERE ${p.predicate}`,m=p.columns.map(h=>`s.${l(h)} = t.${l(h)}`).join(" AND "),L=await n.prepareUncached(`SELECT ${p.columns.map(h=>`s.${l(h)}::text`).join(" || ',' || ")} AS value
9
+ FROM (SELECT * FROM ${$(o,i)}${f}) s JOIN (SELECT * FROM ${$(c,i)}${f}) t ON ${m} AND (${w}) LIMIT 10`).all();L.length>0&&r.push({table:i,key:p.name,values:L.map(h=>h.value)})}const N=u.columns.filter(p=>!u.identity.includes(p)&&!s(i).includes(p));if(N.length>0&&!R){const p=N.map(m=>`s.${l(m)} IS DISTINCT FROM t.${l(m)}`).join(" OR "),f=await n.prepareUncached(`SELECT ${_.map(m=>`s.${l(m)}::text`).join(" || ',' || ")} AS value
10
+ FROM ${$(o,i)} s JOIN ${$(c,i)} t ON ${T} WHERE ${p} LIMIT 10`).all();f.length>0&&r.push({table:i,key:"primary key payload",values:f.map(m=>m.value)})}}if(r.length>0){const i=r[0];throw new O("pre-check","unique_conflict",`${i.table} ${i.key} collides with the target: ${i.values.join(", ")}`,{collisions:r})}}d(k,"precheck");export{q as fillEnvironments,k as precheck,x as pruneStaged,j as renameClashingProfiles,F as rewritePaths,g as rewriteRequestPaths,b as takeRepoSyncedDocuments};
@@ -0,0 +1,39 @@
1
+ import { type CanvasDb } from '../../canvas/db.js';
2
+ import { ImportLaneError } from './stage.js';
3
+ import { type Catalog } from './catalog.js';
4
+ export interface RewriteInput {
5
+ db: CanvasDb;
6
+ /** The staging schema. */
7
+ schema: string;
8
+ /** The crouter schema the rows are copied into. */
9
+ target: string;
10
+ catalog: Catalog;
11
+ sourcePerson: string;
12
+ targetPerson: string;
13
+ /** The control plane's fleet runtime principal (`CRTR_RUNTIME`), the key of the directory's grants. */
14
+ fleetRuntime: string | null;
15
+ /** `source.shippedBodies`: shipped document id → hash of its body, which is a `bodies` row of the bundle (copied with the table). */
16
+ shippedBodies: Readonly<Record<string, string>>;
17
+ }
18
+ /** Source id → target id, per kind. Persisted into `import_id_map` in the copy transaction. */
19
+ export interface IdMap {
20
+ object: Map<string, string>;
21
+ grant: Map<string, string>;
22
+ kind: Map<string, string>;
23
+ }
24
+ /** Object-id columns that carry no foreign key (design-import, "Id columns"). */
25
+ export declare const OBJECT_ID_COLUMNS: ReadonlyArray<[string, string]>;
26
+ /** Every column that holds an object id, per table: the foreign keys onto `objects` in the target's catalog, then the ones without a foreign key. */
27
+ export declare function objectIdColumns(catalog: Catalog): Map<string, Set<string>>;
28
+ /** Rewrite 1: `substitutePerson` against the staging schema (every table it touches is staged, the ones the bundle does not dump empty). */
29
+ export declare function rewritePerson(input: RewriteInput): Promise<void>;
30
+ /** The target's grant for (fleet runtime, sub, grantee), the key the directory writes an importing person's grants under. The lane's wait for those
31
+ * grants (`run.ts`) and rewrite 2 both look a grant up here. A null fleet runtime (`CRTR_RUNTIME` unset) holds none. */
32
+ export declare function findFleetGrant(db: CanvasDb, target: string, fleetRuntime: string | null, sub: string, grantee: string): Promise<string | undefined>;
33
+ /** The `grant_missing` failure, for the grantees the target holds no grant for. */
34
+ export declare function grantMissing(grantees: readonly string[]): ImportLaneError;
35
+ /** Rewrite 2 (id map): build the map, drop the staged rows of mapped objects, rewrite every id column through it. */
36
+ export declare function rewriteIds(input: RewriteInput): Promise<IdMap>;
37
+ /** The drop step: federation stubs' objects and every staged row that points at a dropped row, then the assertion that no staged foreign
38
+ * key points at a row that is neither staged nor in the target. Returns rows dropped per table. */
39
+ export declare function dropStubRows(input: RewriteInput): Promise<Record<string, number>>;
@@ -0,0 +1,13 @@
1
+ var g=Object.defineProperty;var p=(n,e)=>g(n,"name",{value:e,configurable:!0});import{substitutePerson as w}from"../../canvas/person.js";import{withCanvasWrite as S}from"../../canvas/db.js";import{ImportLaneError as f}from"./stage.js";import{q as m}from"./catalog.js";const h=[["rules","owner"],["messages","object_id"],["messages","actor_id"],["custom_objects","registered_by"],["nodes","app"],["nodes","profile_id"],["revisions","acting_app"],["relations","resource_id"],["relations","subject_id"],["crons","profile"],["profiles","environment_id"]],I=[["nodes","grant_id"],["crons","grant_id"]],j={identities:"object",documents:"object",jobs:"object",custom_objects:"object",context_sessions:"node",rules:"object"},D=["app","person","profile","repo"];function A(n){const e=new Map,E=p((i,o)=>{e.has(i)||e.set(i,new Set),e.get(i).add(o)},"add");for(const i of n.foreignKeys)i.parent==="objects"&&i.columns.length===1&&E(i.table,i.columns[0]);for(const[i,o]of h)E(i,o);return e}p(A,"objectIdColumns");const _=p((n,e)=>`${m(n)}.${m(e)}`,"sq"),d=p(async(n,e,...E)=>Number((await n.prepareUncached(e).run(...E)).changes),"run"),O=p(async(n,e,...E)=>await n.prepareUncached(e).all(...E),"rows");async function k(n){n.sourcePerson!==n.targetPerson&&await S(async e=>{await e.exec(`SET LOCAL search_path TO ${m(n.schema)}`),await w(e,n.sourcePerson,n.targetPerson)})}p(k,"rewritePerson");async function M(n,e,E,i,o){return E===null?void 0:(await n.prepareUncached(`SELECT id FROM ${_(e,"grants")} WHERE runtime = ? AND sub = ? AND grantee = ?`).get(E,i,o))?.id}p(M,"findFleetGrant");function C(n){return new f("id_map","grant_missing",`no grant in the target for: ${n.slice(0,10).join(", ")}; approve the app, then rerun`,{grantees:[...n]})}p(C,"grantMissing");async function x(n){const{db:e,schema:E,target:i}=n,o=p(t=>_(E,t),"S"),r=p(t=>_(i,t),"T"),b={object:new Map,grant:new Map,kind:new Map},T=n.targetPerson.replace(/^user:/,"");for(const t of D){const c=t==="person"?"t.external_id = ?":t==="app"?"?::text IS NOT NULL":`EXISTS (SELECT 1 FROM ${r("relations")} r WHERE r.resource_type = 'crtr/object' AND r.resource_id = tobj.id AND r.relation = 'acts_for' AND r.subject_type = 'user' AND r.subject_id = ?)`,u=await O(e,`SELECT i.object AS src, t.object AS dst FROM ${o("identities")} i
2
+ JOIN ${o("objects")} so ON so.id = i.object AND so.type = ?
3
+ JOIN ${r("identities")} t ON t.scheme = i.scheme AND t.external_id = i.external_id
4
+ JOIN ${r("objects")} tobj ON tobj.id = t.object AND tobj.type = so.type
5
+ WHERE ${c}`,t,t==="person"?n.targetPerson:T);for(const{src:N,dst:$}of u)N!==$&&b.object.set(N,$)}const R=await O(e,`SELECT o.id, o.owner, o.name, o.created_at FROM ${o("objects")} o JOIN ${o("documents")} d ON d.object = o.id WHERE d.source_path IS NOT NULL`);for(const t of R){const c=await e.prepareUncached(`SELECT o.id FROM ${r("objects")} o JOIN ${r("documents")} d ON d.object = o.id
6
+ WHERE o.owner = ? AND o.name = ? AND o.type = 'document' AND o.deleted_at IS NULL AND d.source_path IS NOT NULL`).get(b.object.get(t.owner)??t.owner,t.name);if(c!==void 0){b.object.set(t.id,c.id);continue}const u=n.shippedBodies[t.id];if(u===void 0)throw new f("id_map","bundle_invalid",`shipped document ${t.name} (${t.id}) has no counterpart in the target and source.json has no body for it`);const N=await e.prepareUncached(`SELECT o.id FROM ${r("objects")} o JOIN ${r("identities")} i ON i.object = o.id
7
+ WHERE o.type = 'person' AND i.scheme = 'user' AND i.external_id = ?
8
+ UNION ALL SELECT o.id FROM ${o("objects")} o JOIN ${o("identities")} i ON i.object = o.id
9
+ WHERE o.type = 'person' AND i.scheme = 'user' AND i.external_id = ? LIMIT 1`).get(n.targetPerson,n.targetPerson);if(N===void 0)throw new f("id_map","bundle_invalid",`no person object for ${n.targetPerson}; cannot import shipped document ${t.name}`);await d(e,`UPDATE ${o("objects")} SET owner = ? WHERE id = ?`,N.id,t.id),await d(e,`DELETE FROM ${o("relations")} WHERE resource_type = 'crtr/object' AND resource_id = ? AND relation = 'reader' AND subject_type = 'user' AND subject_id = '*' AND subject_relation = ''`,t.id),await d(e,`INSERT INTO ${o("relations")} (resource_type, resource_id, relation, subject_type, subject_id, subject_relation, written_by, created_at)
10
+ VALUES ('crtr/object', ?, 'acts_for', 'user', ?, '', 'crtr', ?) ON CONFLICT DO NOTHING`,t.id,T,t.created_at),await d(e,`UPDATE ${o("documents")} SET source_path = NULL WHERE object = ?`,t.id),await d(e,`UPDATE ${o("revisions")} r SET body_hash = ? WHERE r.document = ? AND NOT EXISTS (SELECT 1 FROM ${o("revision_parents")} p WHERE p.parent = r.rev_id)`,u,t.id)}const s=await O(e,`SELECT id, sub, grantee FROM ${o("grants")} ORDER BY id`),a=[];for(const t of s){const c=await M(e,i,n.fleetRuntime,t.sub,t.grantee);c===void 0?a.push(t.grantee):b.grant.set(t.id,c)}if(a.length>0)throw C(a);await e.exec(`CREATE TABLE ${o("_idmap")} (kind text NOT NULL, source text NOT NULL, target text NOT NULL, PRIMARY KEY (kind, source))`);for(const[t,c]of[["object",b.object],["grant",b.grant]])for(const[u,N]of c)await d(e,`INSERT INTO ${o("_idmap")} (kind, source, target) VALUES (?, ?, ?)`,t,u,N);const l=`(SELECT source FROM ${o("_idmap")} WHERE kind = 'object')`;for(const[t,c]of Object.entries(j))await d(e,`DELETE FROM ${o(t)} WHERE ${c} IN ${l}`);await d(e,`DELETE FROM ${o("revision_parents")} WHERE child IN (SELECT rev_id FROM ${o("revisions")} WHERE document IN ${l})`),await d(e,`DELETE FROM ${o("revisions")} WHERE document IN ${l}`);for(const[t,c]of A(n.catalog))if(n.catalog.tables.has(t))for(const u of c)await L(e,o(t),u,"object",o("_idmap"));for(const[t,c]of I)await L(e,o(t),c,"grant",o("_idmap"));await d(e,`UPDATE ${o("daemon_actions")} d SET adapter_json = jsonb_set(d.adapter_json::jsonb, '{principal,grant_id}', to_jsonb(m.target))::text
11
+ FROM ${o("_idmap")} m WHERE m.kind = 'grant' AND d.adapter_json::jsonb ->> 'adapter' = 'cron_spawn' AND d.adapter_json::jsonb #>> '{principal,grant_id}' = m.source`),await d(e,`DELETE FROM ${o("objects")} WHERE id IN ${l}`);for(const{name:t}of await O(e,`SELECT k.name FROM ${o("object_kinds")} k WHERE EXISTS (SELECT 1 FROM ${r("object_kinds")} t WHERE t.name = k.name)`))b.kind.set(t,t);return await d(e,`DELETE FROM ${o("object_kinds")} k WHERE EXISTS (SELECT 1 FROM ${r("object_kinds")} t WHERE t.name = k.name)`),b}p(x,"rewriteIds");async function L(n,e,E,i,o){await d(n,`UPDATE ${e} t SET ${m(E)} = m.target FROM ${o} m WHERE m.kind = ? AND t.${m(E)} = m.source`,i)}p(L,"remap");async function P(n){const{db:e,schema:E,target:i,catalog:o}=n,r=p(s=>_(E,s),"S"),b={},T=p((s,a)=>{a>0&&(b[s]=(b[s]??0)+a)},"count");await e.exec(`CREATE TABLE ${r("_dropped")} (tbl text NOT NULL, key text NOT NULL, PRIMARY KEY (tbl, key))`);const R=`(SELECT id FROM ${r("objects")} WHERE home_machine IS NOT NULL)`;await d(e,`INSERT INTO ${r("_dropped")} SELECT 'objects', id FROM ${r("objects")} WHERE home_machine IS NOT NULL`);for(const[s,a]of Object.entries(j))T(s,await d(e,`DELETE FROM ${r(s)} WHERE ${a} IN ${R}`));for(T("objects",await d(e,`DELETE FROM ${r("objects")} WHERE home_machine IS NOT NULL`));;){let s=0;for(const a of o.foreignKeys){if(a.columns.length!==1||a.parentColumns.length!==1||!n.catalog.tables.has(a.table))continue;const l=o.tables.get(a.table),t=m(a.columns[0]),c=l.primaryKey.length===1?m(l.primaryKey[0]):null;c!==null&&await d(e,`INSERT INTO ${r("_dropped")} SELECT ?, ${c}::text FROM ${r(a.table)} WHERE ${t} IN (SELECT key FROM ${r("_dropped")} WHERE tbl = ?) ON CONFLICT DO NOTHING`,a.table,a.parent);const u=await d(e,`DELETE FROM ${r(a.table)} WHERE ${t} IN (SELECT key FROM ${r("_dropped")} WHERE tbl = ?)`,a.parent);T(a.table,u),s+=u}if(s===0)break}for(const s of o.foreignKeys){if(s.columns.length!==1||!o.tables.has(s.table))continue;const a=m(s.columns[0]),l=m(s.parentColumns[0]),t=await O(e,`SELECT DISTINCT c.${a}::text AS value FROM ${r(s.table)} c
12
+ WHERE c.${a} IS NOT NULL AND NOT EXISTS (SELECT 1 FROM ${r(s.parent)} p WHERE p.${l} = c.${a})
13
+ AND NOT EXISTS (SELECT 1 FROM ${_(i,s.parent)} p WHERE p.${l} = c.${a}) LIMIT 10`);if(t.length>0)throw new f("drop","bundle_invalid",`${s.table}.${s.columns[0]} names rows that exist nowhere: ${t.map(c=>c.value).join(", ")}`,{table:s.table,column:s.columns[0],values:t.map(c=>c.value)})}return b}p(P,"dropStubRows");export{h as OBJECT_ID_COLUMNS,P as dropStubRows,M as findFleetGrant,C as grantMissing,A as objectIdColumns,x as rewriteIds,k as rewritePerson};
@@ -0,0 +1,3 @@
1
+ import type { BlobStore } from '../../blobs/index.js';
2
+ import type { ImportRollbackDTO } from '../../../api/dto/imports.js';
3
+ export declare function rollbackRun(runId: string, blobs: BlobStore): Promise<ImportRollbackDTO>;
@@ -0,0 +1,6 @@
1
+ var y=Object.defineProperty;var m=(n,l)=>y(n,"name",{value:l,configurable:!0});import{openDb as C,withCanvasWrite as D}from"../../canvas/db.js";import{bundleKeys as A}from"../bundle/layout.js";import{q as r,readCatalog as $,rowKey as T,topologicalOrder as h}from"./catalog.js";import{decodeKeys as M}from"./copy.js";import{OBJECT_ID_COLUMNS as W}from"./rewrite.js";import{targetSchema as g}from"./stage.js";class O extends Error{static{m(this,"Refused")}}async function B(n,l){const _=await C(),c=await _.prepare("SELECT source_install_id, source_kind, state, created_at FROM import_runs WHERE run_id = ?").get(n);if(c===void 0)throw new Error(`no import run ${n}`);if(c.state!=="done"&&c.state!=="committed"&&c.state!=="failed")throw new Error(`import run ${n} is ${c.state}, not rollbackable`);const N=(await _.prepare("SELECT blob_key FROM import_node_files WHERE blob_key LIKE ?").all(`imports/${n}/node-files/%`)).map(t=>t.blob_key),E={run_id:n,rolled_back:!1,dependents:{},deleted:{}};try{await D(async t=>{const i=await g(t),L=(await t.prepareUncached("SELECT table_name FROM information_schema.tables WHERE table_schema = ? AND table_type = 'BASE TABLE'").all(i)).map(e=>e.table_name),d=await $(t,i,L);d.tables.get("env_vars").primaryKey=["person","owner_kind","owner_id","name"],await t.exec("CREATE TEMP TABLE _rb (tbl text NOT NULL, k jsonb NOT NULL) ON COMMIT DROP");const R=await t.prepare("SELECT table_name, inserted_keys FROM import_manifest WHERE run_id = ?").all(n);for(const{table_name:e,inserted_keys:o}of R){const a=M(o);for(let s=0;s<a.length;s+=5e3)await t.prepare("INSERT INTO _rb SELECT ?, e FROM jsonb_array_elements(?::jsonb) e").run(e,JSON.stringify(a.slice(s,s+5e3)))}const b=new Set(R.map(e=>e.table_name)),u=m((e,o,a)=>`jsonb_build_array(${o.map(s=>`${a}.${r(s)}::text`).join(", ")})`,"keyOf");for(const e of d.foreignKeys){const o=d.tables.get(e.parent),a=d.tables.get(e.table);if(e.table==="import_node_files"||!b.has(e.parent)||o===void 0||a===void 0||e.parentColumns.join()!==o.primaryKey.join())continue;const s=b.has(e.table)?`AND NOT EXISTS (SELECT 1 FROM _rb o WHERE o.tbl = '${e.table}' AND o.k = ${u(e.table,T(a),"c")})`:"",p=Number((await t.prepareUncached(`SELECT count(*) AS n FROM ${r(i)}.${r(e.table)} c WHERE ${e.columns.map(k=>`c.${r(k)} IS NOT NULL`).join(" AND ")}
2
+ AND ${u(e.parent,e.columns,"c")} IN (SELECT k FROM _rb WHERE tbl = '${e.parent}') ${s}`).get()).n);p>0&&(E.dependents[e.table]=(E.dependents[e.table]??0)+p)}for(const[e,o]of W){const a=d.tables.get(e);if(a===void 0||!a.columns.includes(o)||a.primaryKey.length===0)continue;const s=b.has(e)?`AND NOT EXISTS (SELECT 1 FROM _rb o WHERE o.tbl = '${e}' AND o.k = ${u(e,a.primaryKey,"c")})`:"",p=Number((await t.prepareUncached(`SELECT count(*) AS n FROM ${r(i)}.${r(e)} c WHERE c.${r(o)} IN (SELECT k ->> 0 FROM _rb WHERE tbl = 'objects') ${s}`).get()).n);p>0&&(E.dependents[`${e}.${o}`]=p)}const f=Number((await t.prepare("SELECT count(*) AS n FROM import_runs WHERE source_install_id = ? AND run_id <> ? AND created_at > ? AND (state IN ('loading', 'committed', 'done') OR (state = 'failed' AND committed_at IS NOT NULL))").get(c.source_install_id,n,c.created_at)).n);f>0&&(E.dependents.import_runs=f);const S=Number((await t.prepare("SELECT count(*) AS n FROM import_id_map m WHERE m.run_id <> ? AND m.kind = 'object' AND m.target_value IN (SELECT k ->> 0 FROM _rb WHERE tbl = 'objects')").get(n)).n);if(S>0&&(E.dependents.import_id_map=S),Object.keys(E.dependents).length>0)throw new O;const w=(await t.prepareUncached(`SELECT DISTINCT r.document FROM ${r(i)}.revisions r WHERE jsonb_build_array(r.rev_id) IN (SELECT k FROM _rb WHERE tbl = 'revisions')
3
+ AND NOT EXISTS (SELECT 1 FROM _rb o WHERE o.tbl = 'objects' AND o.k = jsonb_build_array(r.document))`).all()).map(e=>e.document);for(const e of h(d,[...b]).reverse()){const o=d.tables.get(e);if(o===void 0)continue;const a=await t.prepareUncached(`DELETE FROM ${r(i)}.${r(e)} t WHERE ${u(e,T(o),"t")} IN (SELECT k FROM _rb WHERE tbl = '${e}')`).run();E.deleted[e]=a.changes}for(const e of w)await t.prepareUncached(`UPDATE ${r(i)}.documents d SET
4
+ heads_key = COALESCE((SELECT string_agg(r.rev_id, ',' ORDER BY r.rev_id) FROM ${r(i)}.revisions r WHERE r.document = d.object AND NOT EXISTS (SELECT 1 FROM ${r(i)}.revision_parents p WHERE p.parent = r.rev_id)), ''),
5
+ revision = COALESCE((SELECT max(r.number) FROM ${r(i)}.revisions r WHERE r.document = d.object AND NOT EXISTS (SELECT 1 FROM ${r(i)}.revision_parents p WHERE p.parent = r.rev_id)), 0)
6
+ WHERE d.object = ?`).run(e);await t.prepareUncached("DELETE FROM import_key_map km WHERE km.source_install_id = ? AND EXISTS (SELECT 1 FROM _rb r WHERE r.tbl = km.table_name AND r.k = jsonb_build_array(km.target_key))").run(c.source_install_id),await t.prepare("DELETE FROM import_id_map WHERE run_id = ?").run(n),await t.prepare("DELETE FROM import_manifest WHERE run_id = ?").run(n),await t.prepare("UPDATE import_runs SET state = 'rolled_back', step = 'rolled_back', finished_at = ? WHERE run_id = ?").run(new Date().toISOString(),n),await F(t,c)})}catch(t){if(t instanceof O)return E;throw t}await l.delete(A.manifest(n));for(const t of N)await l.delete(t);return E.rolled_back=!0,E}m(B,"rollbackRun");async function F(n,l){if(l.source_kind!=="machine")return;const _=await n.prepare("SELECT object FROM environments WHERE machine_id = ? AND kind = 'machine'").get(l.source_install_id);_!==void 0&&await n.prepare("UPDATE machine_credentials SET revoked_at = ? WHERE environment = ? AND revoked_at IS NULL").run(new Date().toISOString(),_.object)}m(F,"revokeFor");export{B as rollbackRun};
@@ -0,0 +1,22 @@
1
+ import { type BlobStore } from '../../blobs/index.js';
2
+ import { type RowClaim } from '../../../daemon/reconcilers/row-claim.js';
3
+ export interface RunRow {
4
+ run_id: string;
5
+ source_install_id: string;
6
+ source_kind: 'machine' | 'hosted';
7
+ person: string;
8
+ release: string;
9
+ state: string;
10
+ loaded_at?: string | null;
11
+ }
12
+ export interface RunOptions {
13
+ blobs?: BlobStore;
14
+ /** The control plane's fleet runtime principal, the key of the directory's grants (`CRTR_RUNTIME`). */
15
+ fleetRuntime?: string | null;
16
+ /** Test hook: called when a step is reached, before it runs. A throw there is that step failing. */
17
+ atStep?: (step: string) => void | Promise<void>;
18
+ }
19
+ /** How long a loading run waits for the directory to copy the person's grants onto the fleet runtime before it fails `grant_missing` (design-import-fleet-grants G10). */
20
+ export declare const GRANT_WAIT_MS: number;
21
+ /** Process one claimed run. Never throws: the outcome is the row's state. */
22
+ export declare function processRun(row: RunRow, claim: RowClaim, options?: RunOptions): Promise<void>;
@@ -0,0 +1,3 @@
1
+ var J=Object.defineProperty;var u=(e,s)=>J(e,"name",{value:s,configurable:!0});import{zstdCompressSync as q}from"node:zlib";import{blobStore as G}from"../../blobs/index.js";import{openDb as E,withCanvasWrite as $}from"../../canvas/db.js";import{insertImportedRevisions as z}from"../../graph/documents.js";import{getById as Q}from"../../graph/objects.js";import{bundleKeys as y,decodeRows as V,rowKeys as Y,SEQ_TABLES as X}from"../bundle/layout.js";import{readCatalog as Z}from"./catalog.js";import{copyRows as x,decodeKeys as I,encodeKeys as A}from"./copy.js";import{pruneStaged as ee,precheck as te,renameClashingProfiles as ae,rewritePaths as ne,rewriteRequestPaths as ie,takeRepoSyncedDocuments as se}from"./prepare.js";import{boundSub as re,checkSourcePerson as oe,checkStagedBlobKeys as de,checkStagedPersons as ce,checkStagedReferences as pe,checkStagedRequestKeys as ue}from"./bind.js";import{deleteRunBlobs as fe,moveRunBlobs as me}from"./blobs.js";import{dropStubRows as le,findFleetGrant as _e,grantMissing as we,rewriteIds as ge,rewritePerson as be}from"./rewrite.js";import{dropStaging as M,ImportLaneError as g,stage as he,stagingSchema as Ee,targetSchema as ye}from"./stage.js";import{verifyRun as Se}from"./verify.js";import{sandboxPathMap as ve,writeNodeFiles as Re}from"./path-map.js";import{allStoreKeys as Oe,writeStores as Te}from"./stores.js";import{COPIED_TABLES as ke}from"../bundle/layout.js";import{ClaimLostError as Pe}from"../../../daemon/reconcilers/row-claim.js";const je=10*6e4,S=u(()=>new Date().toISOString(),"now"),N=u(async(e,s)=>{await(await E()).prepare("UPDATE import_runs SET step = ? WHERE run_id = ?").run(s,e)},"setStep");async function De(e,s){const n=await e.get(y.source(s));if(n===null)throw new g("stage","bundle_invalid","source.json is missing");return JSON.parse(n.toString("utf8"))}u(De,"readSource");async function Ie(e,s,n,r,i,d){const a=await e.get(y.rows(n.run_id,"grants")),f=a===null?[]:V(a),m=[];for(const t of f){const c=re(t.sub,r,n.person,"grants.sub");await _e(s,i,d,c,t.grantee)===void 0&&m.push(t.grantee)}if(m.length===0)return!0;if(n.loaded_at===void 0||n.loaded_at===null)throw new g("stage","import_failed","a loading run has no loaded_at");if(Date.now()-Date.parse(n.loaded_at)>=je)throw we(m);return!1}u(Ie,"grantsReady");async function tt(e,s,n={}){const r=n.blobs??await G(),i=await E(),d=Ee(e.run_id),a=u(async t=>{await s.assertOwned(),await N(e.run_id,t),await n.atStep?.(t)},"at");let f=e.state==="committed";const m=[];try{const t=await De(r,e.run_id);if(e.source_kind==="machine"&&t.installId!==e.source_install_id)throw new g("stage","bundle_invalid",`source.json names install ${t.installId}, the run is for ${e.source_install_id}`);t.installId=e.source_install_id;const c=await ye(i),b=[...ke],w=await Z(i,c,[...b,"grants"]);if(!f){const l={},O=[],T=n.fleetRuntime??process.env.CRTR_RUNTIME??null;if(await oe(i,c,t.person,e.person),!await Ie(r,i,e,t.person,c,T)){await s.assertOwned(),await N(e.run_id,"waiting_for_grants");return}await a("stage");const W=await he(i,e.run_id,t.counts,o=>Y(e.run_id,o,t),o=>r.get(o));for(const[o,p]of Object.entries(W))l[o]={staged:p,inserted:0,mapped:0,dropped:0};const _={db:i,schema:d,target:c,catalog:w,sourcePerson:t.person,targetPerson:e.person,fleetRuntime:T,shippedBodies:t.shippedBodies??{}};await a("bind-staged");for(const[o,p]of Object.entries(await ce(_)))v(l,o,p);await de(_),await a("rewrite-person"),await be(_),await a("prune");const B=await ee(i,d,w,t),k=t.kind==="hosted"?ve(t.pathPrefixes??[]):[];await a("rewrite-paths"),await ne(i,d,k),await ie(i,d),await ue(_),await ae(i,d,c,t.installId,e.person),await a("rewrite-ids");const P=await ge(_);await a("drop");const L=await se(i,d,c),H=await le(_);for(const[o,p]of Object.entries({...B}))v(l,o,p);for(const[o,p]of Object.entries(H))v(l,o,p);await pe(_),await a("pre-check");const K=new Map([["messages",["seq"]],...X.map(o=>[o,["seq"]])]);await te(i,d,c,w,b,new Set,K),await a("blobs"),await me(i,d,r,e.run_id,m),await a("copy"),await $(async o=>{await s.assertOwned();const p=await x({db:o,runId:e.run_id,schema:d,target:c,catalog:w,source:t,person:e.person,idMap:P,pathMap:k}),j=await Ne(o,e.run_id,L);j>0&&(p.inserted.revisions=(p.inserted.revisions??0)+j),await Re(o,d,e.run_id,t);const D=await Te({db:o,blobs:r,runId:e.run_id,person:e.person,objectMap:P.object});for(const[U,F]of Object.entries(D.inserted))p.inserted[U]=F.length;return O.push(...D.unresolvedPlugins),await o.prepare("UPDATE import_runs SET counts_json = ?, loaded_at = COALESCE(loaded_at, ?) WHERE run_id = ?").run(JSON.stringify({...Me(l,p),unresolved_plugins:O}),S(),e.run_id),p}),f=!0,await n.atStep?.("committed")}await a("verify");const h=await Se(i,e.run_id,d,c,w,t.installId);if(await i.prepare("UPDATE import_runs SET verify_json = ? WHERE run_id = ?").run(JSON.stringify(h),e.run_id),!h.ok)throw new g("verify","verify_failed","the committed rows differ from the staged rows or an invariant failed",{differences:h.differences,invariants:h.invariants});await a("manifest");const R=y.manifest(e.run_id);await r.put(R,await Ce(e.run_id)),await M(i,e.run_id);for(const l of Oe(e.run_id))await r.delete(l);await fe(r,e.run_id),await i.prepare("UPDATE import_runs SET state = 'done', step = 'done', manifest_key = ?, finished_at = ? WHERE run_id = ?").run(R,S(),e.run_id)}catch(t){if(t instanceof Pe)throw t;const c=Ae(t);if(await M(i,e.run_id).catch(()=>{}),!f)for(const b of m)await r.delete(b).catch(()=>{});await i.prepare("UPDATE import_runs SET state = 'failed', failure_json = ?, finished_at = ? WHERE run_id = ?").run(JSON.stringify(c),S(),e.run_id)}}u(tt,"processRun");function Ae(e){if(e instanceof g)return{step:e.step,code:e.code,message:e.message,...e.detail===void 0?{}:{detail:e.detail}};const s=e.code;return{step:"unknown",code:typeof s=="string"?s:"import_failed",message:e instanceof Error?e.message:String(e)}}u(Ae,"failureOf");function v(e,s,n){(e[s]??={staged:0,inserted:0,mapped:0,dropped:0}).dropped+=n}u(v,"addDropped");function Me(e,s){for(const[n,r]of Object.entries(s.inserted))(e[n]??={staged:0,inserted:0,mapped:0,dropped:0}).inserted=r;for(const[n,r]of Object.entries(s.mapped))(e[n]??={staged:0,inserted:0,mapped:0,dropped:0}).mapped=r;return e}u(Me,"withInserted");async function Ne(e,s,n){const r=new Set((await e.prepareUncached("SELECT rev_id FROM revisions WHERE rev_id IN (SELECT jsonb_array_elements_text(?::jsonb))").all(JSON.stringify(n.map(a=>a.rev_id)))).map(a=>a.rev_id)),i=n.filter(a=>!r.has(a.rev_id)),d=new Map;for(const a of n)d.set(a.document,[...d.get(a.document)??[],a]);for(const[a,f]of d){const m=await Q(e,a);m!==null&&await z(e,m,f.map(t=>({rev_id:t.rev_id,parents:t.parents,body_hash:t.body_hash,rationale:t.rationale,created_at:t.created_at})))}return await C(e,s,"revisions",i.map(a=>[a.rev_id])),await C(e,s,"revision_parents",i.flatMap(a=>a.parents.map(f=>[a.rev_id,f]))),i.length}u(Ne,"arrive");async function C(e,s,n,r){if(r.length===0)return;const i=await e.prepare("SELECT inserted_keys FROM import_manifest WHERE run_id = ? AND table_name = ?").get(s,n);i===void 0?await e.prepare("INSERT INTO import_manifest (run_id, table_name, inserted_keys) VALUES (?, ?, ?)").run(s,n,A(r)):await e.prepare("UPDATE import_manifest SET inserted_keys = ? WHERE run_id = ? AND table_name = ?").run(A([...I(i.inserted_keys),...r]),s,n)}u(C,"appendManifest");async function Ce(e){const n=await(await E()).prepare("SELECT table_name, inserted_keys FROM import_manifest WHERE run_id = ? ORDER BY table_name").all(e);return q(Buffer.from(n.map(r=>JSON.stringify({table:r.table_name,keys:I(r.inserted_keys)})).join(`
2
+ `)+`
3
+ `))}u(Ce,"manifestFile");export{je as GRANT_WAIT_MS,tt as processRun};
@@ -0,0 +1,15 @@
1
+ import type { CanvasDb } from '../../canvas/db.js';
2
+ /** Tables `substitutePerson` and `rebuildTotalsFor` read or write that the bundle does not dump: staged empty. */
3
+ export declare const EMPTY_STAGED_TABLES: readonly ["node_tokens", "charge_chunks", "charge_totals", "profile_defaults", "scope_settings", "scope_bookkeeping", "scope_marketplaces", "scope_plugins", "person_settings", "model_accounts", "model_account_cooldowns", "model_api_keys", "model_settings", "model_sign_in_flows", "env_vars"];
4
+ export declare const stagingSchema: (run: string) => string;
5
+ /** The target schema's name (the transaction's current schema). */
6
+ export declare function targetSchema(db: CanvasDb): Promise<string>;
7
+ export declare function dropStaging(db: CanvasDb, run: string): Promise<void>;
8
+ /** Create the staging schema and load every row file. `rowKeys(table)` names the table's row files (one file, or the parts of `bodies`) and `get` reads one from R2. */
9
+ export declare function stage(db: CanvasDb, run: string, counts: Record<string, number>, rowKeys: (table: string) => string[], get: (key: string) => Promise<Buffer | null>): Promise<Record<string, number>>;
10
+ export declare class ImportLaneError extends Error {
11
+ readonly step: string;
12
+ readonly code: string;
13
+ readonly detail?: Record<string, unknown> | undefined;
14
+ constructor(step: string, code: string, message: string, detail?: Record<string, unknown> | undefined);
15
+ }
@@ -0,0 +1 @@
1
+ var m=Object.defineProperty;var a=(t,s)=>m(t,"name",{value:s,configurable:!0});import{BODIES_TABLE as L,DUMPED_TABLES as E,rowLines as x}from"../bundle/layout.js";import{hashOf as T}from"../../graph/bodies.js";const A=["node_tokens","charge_chunks","charge_totals","profile_defaults","scope_settings","scope_bookkeeping","scope_marketplaces","scope_plugins","person_settings","model_accounts","model_account_cooldowns","model_api_keys","model_settings","model_sign_in_flows","env_vars"],y=16*1024*1024,I=2e3,$=a(t=>`import_${t.replace(/[^A-Za-z0-9_]/g,"_")}`,"stagingSchema"),n=a(t=>`"${t.replace(/"/g,'""')}"`,"q");async function C(t){return(await t.prepare("SELECT current_schema() AS s").get()).s}a(C,"targetSchema");async function D(t,s){await t.exec(`DROP SCHEMA IF EXISTS ${n($(s))} CASCADE`)}a(D,"dropStaging");async function U(t,s,e,i,f){const l=$(s),S=await C(t);await D(t,s),await t.exec(`CREATE SCHEMA ${n(l)}`);const _={};for(const o of[...E,...A])await t.exec(`CREATE UNLOGGED TABLE ${n(l)}.${n(o)} (LIKE ${n(S)}.${n(o)} INCLUDING DEFAULTS)`);for(const o of E){let p=0;for(const d of i(o)){const u=await f(d);if(u===null){if((e[o]??0)!==0)throw c(`row file ${d} is missing`);continue}let r=[],g=0;const w=a(async()=>{r.length!==0&&(await t.prepareUncached(`INSERT INTO ${n(l)}.${n(o)} SELECT * FROM json_populate_recordset(NULL::${n(l)}.${n(o)}, ?::json)`).run(`[${r.join(",")}]`),p+=r.length,r=[],g=0)},"insert");for await(const h of x(u)){if(!h.startsWith("{"))throw c(`row file ${d} has a line that is not a JSON object`);o===L&&N(d,h),r.length>0&&(r.length>=I||g+h.length>y)&&await w(),r.push(h),g+=h.length}await w()}if(p!==(e[o]??0))throw c(`row file for ${o} has ${p} rows; source.json says ${e[o]??0}`);_[o]=p}return _}a(U,"stage");function N(t,s){const e=JSON.parse(s);if(typeof e.hash!="string"||typeof e.text!="string")throw c(`row file ${t} has a bodies row without a hash and a text`);if(e.text.includes("\0"))throw c(`body ${e.hash} in ${t} holds a NUL character; export the source again after its daemon has been upgraded and restarted`);const i=T(e.text);if(i!==e.hash)throw c(`body ${e.hash} in ${t} does not hash to its key (its text hashes to ${i}); export the source again after its daemon has been upgraded and restarted`)}a(N,"checkBodyLine");class O extends Error{static{a(this,"ImportLaneError")}step;code;detail;constructor(s,e,i,f){super(i),this.step=s,this.code=e,this.detail=f}}function c(t){return new O("stage","bundle_invalid",t)}a(c,"bundleInvalid");export{A as EMPTY_STAGED_TABLES,O as ImportLaneError,D as dropStaging,U as stage,$ as stagingSchema,C as targetSchema};
@@ -0,0 +1,24 @@
1
+ import type { BlobStore } from '../../blobs/index.js';
2
+ import type { CanvasDb } from '../../canvas/db.js';
3
+ import type { ImportStoreNameDTO } from '../../../api/dto/imports.js';
4
+ export declare const STORE_NAMES: readonly ImportStoreNameDTO[];
5
+ export declare const storeKey: (run: string, name: ImportStoreNameDTO) => string;
6
+ /** Seal one uploaded body under the target key and store it. The plaintext lives only in this call. */
7
+ export declare function putStore(blobs: BlobStore, run: string, name: ImportStoreNameDTO, body: unknown): Promise<void>;
8
+ export declare class StoreBodyError extends Error {
9
+ }
10
+ /** Every store's blob key under a run, for deletion once the run is done. */
11
+ export declare const allStoreKeys: (run: string) => string[];
12
+ export interface StoreInput {
13
+ db: CanvasDb;
14
+ blobs: BlobStore;
15
+ runId: string;
16
+ person: string;
17
+ objectMap: ReadonlyMap<string, string>;
18
+ }
19
+ export interface StoreResult {
20
+ inserted: Record<string, string[][]>;
21
+ unresolvedPlugins: string[];
22
+ }
23
+ /** Must run inside the copy transaction (after the profile rows exist). */
24
+ export declare function writeStores(input: StoreInput): Promise<StoreResult>;
@@ -0,0 +1,2 @@
1
+ var R=Object.defineProperty;var l=(s,o)=>R(s,"name",{value:o,configurable:!0});import{loadSecretsKey as g,open as U,seal as I}from"../../secrets/index.js";import{insertAccount as j,upsertModelCooldown as $}from"../../subscription-state.js";import{validateEnvVarName as C,isReservedEnvName as D}from"../../env-name.js";import{encodeKeys as V}from"./copy.js";const x=["auth","credentials","env","settings","plugins"],O=l((s,o)=>`imports/${s}/stores/${o}.sealed`,"storeKey"),h=l((s,o)=>`import_store|${s}|${o}`,"aadOf");async function B(s,o,i,r){if(r===null||typeof r!="object"||Array.isArray(r))throw new F(`the ${i} store must be a JSON object`);const{sealed:t,keyId:_}=I(JSON.stringify(r),g(),h(o,i));await s.put(O(o,i),Buffer.concat([Buffer.from(`${_}
2
+ `),t]))}l(B,"putStore");class F extends Error{static{l(this,"StoreBodyError")}}async function E(s,o,i){const r=await s.get(O(o,i));if(r===null)return null;const t=r.indexOf(10);return JSON.parse(U(r.subarray(t+1),r.subarray(0,t).toString("utf8"),g(),h(o,i)))}l(E,"readStore");const H=l(s=>x.map(o=>O(s,o)),"allStoreKeys");async function G(s){const{db:o,blobs:i,runId:r,person:t}=s,_=l(e=>s.objectMap.get(e)??e,"mapId"),f=l(e=>e.startsWith("profile:")?`profile:${_(e.slice(8))}`:e,"mapOwner"),m={},N=[],v=l((e,n)=>{(m[e]??=[]).push(n)},"record"),p=l(async(e,n,a,c)=>{await o.prepareUncached(`${n} ON CONFLICT DO NOTHING RETURNING 1 AS ok`).get(...a)!==void 0&&v(e,c)},"insert"),y=g(),k=new Date().toISOString(),d=await E(i,r,"credentials");if(d!==null){for(const e of d.accounts)if(await o.prepare("SELECT 1 FROM model_accounts WHERE person = ? AND provider_id = ? AND (id = ? OR label_key = ?)").get(t,e.provider,e.id,e.labelKey)===void 0){await j(o,t,e.provider,{providerId:e.provider,id:e.id,label:e.label,labelKey:e.labelKey,position:e.position,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil,...e.authFailure?{authFailure:e.authFailure}:{}},{refresh:e.refresh,access:e.access,expires:e.expires,...e.accountId?{accountId:e.accountId}:{},...e.clientId?{clientId:e.clientId}:{}}),v("model_accounts",[t,e.provider,e.id]);for(const a of e.cooldowns)await $(o,t,e.provider,e.id,a.family,a.until,a.at),v("model_account_cooldowns",[t,e.provider,e.id,a.family])}d.rotation!==void 0&&await p("model_settings","INSERT INTO model_settings (person, default_fallback_strength, revert_when_available) VALUES (?, ?, ?)",[t,d.rotation.defaultFallbackStrength,d.rotation.revertWhenAvailable===null?null:d.rotation.revertWhenAvailable?1:0],[t]),d.openaiDeviceId!==void 0&&/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(d.openaiDeviceId)&&await o.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(t,d.openaiDeviceId.toLowerCase())}const T=await E(i,r,"auth");if(T!==null)for(const[e,n]of Object.entries(T.apiKeys)){const{sealed:a,keyId:c}=I(JSON.stringify(n),y,`model_api_keys|${t}|${e}`);await p("model_api_keys","INSERT INTO model_api_keys (person, provider_id, credential_sealed, key_id, updated_at) VALUES (?, ?, ?, ?, ?)",[t,e,a,c,k],[t,e])}const b=await E(i,r,"env");if(b!==null)for(const[e,n]of Object.entries(b.profiles)){const a=_(e);for(const[c,u]of Object.entries(n)){if(!C(c).isValid||D(c)||typeof u!="string"||u.includes("\0"))continue;const{sealed:A,keyId:L}=I(u,y,`env_vars|${t}|profile|${a}|${c}`);await p("env_vars","INSERT INTO env_vars (person, owner_kind, owner_id, name, value_sealed, key_id, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)",[t,"profile",a,c,A,L,k],[t,"profile",a,c])}}const w=await E(i,r,"settings");if(w!==null){for(const[e,n]of Object.entries(w.settings))await p("scope_settings","INSERT INTO scope_settings (person, owner_key, raw_json) VALUES (?, ?, ?)",[t,f(e),JSON.stringify(n)],[t,f(e)]);for(const[e,n]of Object.entries(w.state))await p("scope_bookkeeping","INSERT INTO scope_bookkeeping (person, owner_key, last_self_check, active_canvas) VALUES (?, ?, ?, ?)",[t,f(e),n.last_self_check??null,n.activeCanvas??null],[t,f(e)]);for(const[e,n]of Object.entries(w.profileProjects)){const a=_(e);if(await o.prepare("SELECT 1 FROM profile_projects WHERE profile_id = ?").get(a)!==void 0)continue;let c=0;for(const u of n)await p("profile_projects","INSERT INTO profile_projects (profile_id, path, position) VALUES (?, ?, ?)",[a,u,c++],[a,u])}for(const[e,n]of Object.entries(w.defaults))await p("profile_defaults","INSERT INTO profile_defaults (person, directory, profile_id) VALUES (?, ?, ?)",[t,e,_(n)],[t,e])}const S=await E(i,r,"plugins");if(S!==null){for(const e of S.marketplaces)await p("scope_marketplaces","INSERT INTO scope_marketplaces (person, owner_key, name, last_updated, url, ref, installed_at) VALUES (?, ?, ?, ?, ?, ?, ?)",[t,f(e.owner),e.name,e.last_updated??null,e.url??null,e.ref??null,e.installed_at??null],[t,f(e.owner),e.name]);for(const e of S.plugins){if(e.source_path!=null&&(e.source_marketplace??null)===null){N.push(`${e.owner}:${e.name}`);continue}await p("scope_plugins","INSERT INTO scope_plugins (person, owner_key, name, last_updated, etag, checked_at, enabled, source_marketplace, version, source_path) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",[t,f(e.owner),e.name,e.last_updated??null,e.etag??null,e.checked_at??null,e.enabled?1:0,e.source_marketplace??null,e.version??null,e.source_path??null],[t,f(e.owner),e.name])}(m.scope_plugins??[]).length+(m.scope_marketplaces??[]).length>0&&await p("person_settings","INSERT INTO person_settings (person, plugins_revision) VALUES (?, 1)",[t],[t])}for(const[e,n]of Object.entries(m))await o.prepare("INSERT INTO import_manifest (run_id, table_name, inserted_keys) VALUES (?, ?, ?) ON CONFLICT (run_id, table_name) DO UPDATE SET inserted_keys = EXCLUDED.inserted_keys").run(r,e,V(n));return Object.keys(m).length>0&&await o.prepare("SELECT pg_notify('crtr_signal', ?)").get(`settings:${t}`),{inserted:m,unresolvedPlugins:N}}l(G,"writeStores");export{x as STORE_NAMES,F as StoreBodyError,H as allStoreKeys,B as putStore,O as storeKey,G as writeStores};
@@ -0,0 +1,4 @@
1
+ import type { CanvasDb } from '../../canvas/db.js';
2
+ import type { ImportVerifyDTO } from '../../../api/dto/imports.js';
3
+ import { type Catalog } from './catalog.js';
4
+ export declare function verifyRun(db: CanvasDb, runId: string, schema: string, target: string, catalog: Catalog, installId: string): Promise<ImportVerifyDTO>;