@north-light/cli 0.3.555 → 0.3.556

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.
@@ -1,3 +1,3 @@
1
- var V=Object.defineProperty;var w=(i,a)=>V(i,"name",{value:a,configurable:!0});import{DatabaseSync as W}from"node:sqlite";import{existsSync as Y,mkdirSync as D,mkdtempSync as J,renameSync as X,rmSync as P}from"node:fs";import{tmpdir as Q}from"node:os";import{join as A}from"node:path";import j from"pg";import{migrate as z,MIGRATIONS as I}from"./migrations.js";import{canvasDbPath as Z}from"./paths.js";import{canvasSearchPathOptions as ee}from"./schema-version.js";import{EXPORT_MARK_KEY as F,IMPORT_MARK_KEY as te,OBJECT_COLUMNS as ne,pgColumns as se,quote as c,TABLE_ORDER as C}from"./import-sqlite.js";const oe=5e3,re=new Set(["environments","relations","schemas","charges","charge_totals","limits","viewer_attachments","reconciler_leases","environment_repos","conversation_entries","conversation_parts","charge_chunks","profiles","profile_projects","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"]),ae={exposures:"node, object, channel",rules:"object, position",edges:"type, src, dst",unresolved_links:"src, name",revision_parents:"child, parent",canvas_meta:"key",runtime_state:"key",message_causes:"message_id",peer_ingested:"key",peer_ingested_destinations:"key",message_sequence:"id",messages:"seq",review_comment_events:"seq",audit_rows:"id",peer_outbox:"id",app_users:"uid"},ie={objects:"id",revisions:"rev_id",watches:"watch_id"},H=new Set(["daemon_owner","schema_version","schema_min_reader","ledger_migrated",te,F]);function ce(i){const a=J(A(Q(),"crtr-export-")),s=process.env.CRTR_HOME;process.env.CRTR_HOME=a,D(A(a,"runtime","nodes"),{recursive:!0});try{const o=new W(i);return o.exec("PRAGMA journal_mode = DELETE;"),o.exec("PRAGMA foreign_keys = ON;"),z(o),o.exec("DELETE FROM canvas_meta; DELETE FROM message_sequence;"),o}finally{s===void 0?delete process.env.CRTR_HOME:process.env.CRTR_HOME=s,P(a,{recursive:!0,force:!0})}}w(ce,"createEmptyDatabase");async function Re(i,a=()=>{}){const s=Z();if(Y(s))throw new Error(`${s} already exists; refusing to overwrite it`);D(A(s,".."),{recursive:!0,mode:448});const o=`${s}.exporting-${process.pid}`,p=Date.now(),t=new j.Client({connectionString:i,application_name:`crtr-export-${process.pid}`,options:ee(),types:{getTypeParser:w(((f,r)=>f===20?d=>Number(d):j.types.getTypeParser(f,r)),"getTypeParser")}});await t.connect();let m;try{if((await t.query("SELECT value FROM canvas_meta WHERE key = 'schema_version'").catch(()=>({rows:[]}))).rows.length===0)throw new Error("the Postgres database has no canvas schema; nothing to export");m=ce(o);const r=m;r.exec("PRAGMA foreign_keys = OFF;");const d=await se(t),L=[...d.keys()].filter(e=>!re.has(e)&&!C.includes(e));if(L.length>0)throw new Error(`export: Postgres tables with no export rule: ${L.join(", ")}`);await t.query("BEGIN READ ONLY ISOLATION LEVEL REPEATABLE READ");const E={};for(const[e,S]of Object.entries(ie)){const h=new Map;for(const _ of(await t.query(`SELECT ${S} AS id, seq FROM ${e}`)).rows)h.set(_.id,Number(_.seq));E[e]=h}const B=E.objects;let M=0;r.exec("BEGIN");for(const e of C){const S=Date.now(),h=new Set(r.prepare(`PRAGMA table_info(${c(e)})`).all().map(n=>n.name)),_=d.get(e);if(_===void 0)throw new Error(`export: Postgres has no table ${e}`);const G=_.some(n=>n.name==="seq"),O=_.filter(n=>h.has(n.name)),K=new Set(ne[e]??[]),g=e==="objects"||e==="revisions"||e==="watches"||e==="watch_subscriptions",$=O.map(n=>n.type==="jsonb"?`${c(n.name)}::text AS ${c(n.name)}`:c(n.name));e==="watches"&&$.push("watch_id AS _watch_id"),g&&$.push("seq AS _seq");const R=O.map(n=>c(n.name)),U=r.prepare(`INSERT INTO ${c(e)} (${g?["rowid",...R]:R}) VALUES (${(g?["?",...R]:R).map(()=>"?").join(", ")})`),q=G?"seq":ae[e],b=`export_${e}`;await t.query(`DECLARE ${b} NO SCROLL CURSOR FOR SELECT ${$.join(", ")} FROM ${c(e)}${e==="canvas_meta"?` WHERE key NOT IN (${[...H].map(n=>`'${n}'`).join(", ")})`:""}${q===void 0?"":` ORDER BY ${q}`}`);let T=0;for(;;){const n=await t.query(`FETCH ${oe} FROM ${b}`);if(n.rows.length===0)break;for(const k of n.rows){const N=g?[Number(k._seq)]:[];for(const u of O){let l=k[u.name];e==="revision_parents"&&(u.name==="child"||u.name==="parent")?l=x(E.revisions,"revision",e,u.name,l):e==="watch_subscriptions"&&u.name==="watch"?l=x(E.watches,"watch",e,u.name,l):K.has(u.name)&&(l=x(B,"object",e,u.name,l)),N.push(l??null)}U.run(...N)}T+=n.rows.length}await t.query(`CLOSE ${b}`),M+=T,a(`export ${e}: ${T} rows in ${Date.now()-S} ms`)}r.exec("COMMIT"),await t.query("COMMIT");const y=r.prepare("PRAGMA foreign_key_check").all();if(y.length>0)throw new Error(`export: ${y.length} foreign key violation(s) in the exported canvas.db, first: ${JSON.stringify(y[0])}`);await pe(t,r);const v=r.prepare("PRAGMA user_version").get().user_version;if(v!==I.length)throw new Error(`export: exported canvas.db is at user_version ${v}, expected ${I.length}`);return r.close(),m=void 0,await t.query("INSERT INTO canvas_meta (key, value) VALUES ($1, $2) ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value",[F,JSON.stringify({at:new Date().toISOString(),path:s})]),X(o,s),{path:s,rows:M,ms:Date.now()-p,userVersion:v}}catch(f){await t.query("ROLLBACK").catch(()=>{});try{m?.close()}catch{}for(const r of["","-wal","-shm","-journal"])P(`${o}${r}`,{force:!0});throw f}finally{await t.end().catch(()=>{})}}w(Re,"exportPostgresToSqlite");function x(i,a,s,o,p){if(p==null)return null;const t=i.get(p);if(t===void 0)throw new Error(`export: ${s}.${o} references ${a} ${String(p)}, which was not exported (`);return t}w(x,"mapId");async function pe(i,a){const s=[];for(const o of C){const p=Number((await i.query(`SELECT count(*) AS n FROM ${c(o)}`)).rows[0].n);let t=Number(a.prepare(`SELECT count(*) AS n FROM ${c(o)}`).get().n),m=p;o==="canvas_meta"&&(m=Number((await i.query("SELECT count(*) AS n FROM canvas_meta WHERE key <> ALL($1)",[[...H]])).rows[0].n),t=Number(a.prepare("SELECT count(*) AS n FROM canvas_meta").get().n)),m!==t&&s.push(`${o}: Postgres ${m} rows, SQLite ${t}`)}if(s.length>0)throw new Error(`Postgres \u2192 SQLite export verification failed:
1
+ var V=Object.defineProperty;var w=(i,a)=>V(i,"name",{value:a,configurable:!0});import{DatabaseSync as W}from"node:sqlite";import{existsSync as Y,mkdirSync as D,mkdtempSync as J,renameSync as X,rmSync as P}from"node:fs";import{tmpdir as Q}from"node:os";import{join as A}from"node:path";import j from"pg";import{migrate as z,MIGRATIONS as I}from"./migrations.js";import{canvasDbPath as Z}from"./paths.js";import{canvasSearchPathOptions as ee}from"./schema-version.js";import{EXPORT_MARK_KEY as F,IMPORT_MARK_KEY as te,OBJECT_COLUMNS as ne,pgColumns as se,quote as c,TABLE_ORDER as C}from"./import-sqlite.js";const re=5e3,oe=new Set(["environments","relations","schemas","charges","charge_totals","limits","viewer_attachments","reconciler_leases","environment_repos","conversation_entries","conversation_parts","charge_chunks","profiles","profile_projects","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","usage","grantee_usage","provider_receipts"]),ae={exposures:"node, object, channel",rules:"object, position",edges:"type, src, dst",unresolved_links:"src, name",revision_parents:"child, parent",canvas_meta:"key",runtime_state:"key",message_causes:"message_id",peer_ingested:"key",peer_ingested_destinations:"key",message_sequence:"id",messages:"seq",review_comment_events:"seq",audit_rows:"id",peer_outbox:"id",app_users:"uid"},ie={objects:"id",revisions:"rev_id",watches:"watch_id"},H=new Set(["daemon_owner","schema_version","schema_min_reader","ledger_migrated",te,F]);function ce(i){const a=J(A(Q(),"crtr-export-")),s=process.env.CRTR_HOME;process.env.CRTR_HOME=a,D(A(a,"runtime","nodes"),{recursive:!0});try{const r=new W(i);return r.exec("PRAGMA journal_mode = DELETE;"),r.exec("PRAGMA foreign_keys = ON;"),z(r),r.exec("DELETE FROM canvas_meta; DELETE FROM message_sequence;"),r}finally{s===void 0?delete process.env.CRTR_HOME:process.env.CRTR_HOME=s,P(a,{recursive:!0,force:!0})}}w(ce,"createEmptyDatabase");async function Re(i,a=()=>{}){const s=Z();if(Y(s))throw new Error(`${s} already exists; refusing to overwrite it`);D(A(s,".."),{recursive:!0,mode:448});const r=`${s}.exporting-${process.pid}`,p=Date.now(),t=new j.Client({connectionString:i,application_name:`crtr-export-${process.pid}`,options:ee(),types:{getTypeParser:w(((d,o)=>d===20?f=>Number(f):j.types.getTypeParser(d,o)),"getTypeParser")}});await t.connect();let m;try{if((await t.query("SELECT value FROM canvas_meta WHERE key = 'schema_version'").catch(()=>({rows:[]}))).rows.length===0)throw new Error("the Postgres database has no canvas schema; nothing to export");m=ce(r);const o=m;o.exec("PRAGMA foreign_keys = OFF;");const f=await se(t),L=[...f.keys()].filter(e=>!oe.has(e)&&!C.includes(e));if(L.length>0)throw new Error(`export: Postgres tables with no export rule: ${L.join(", ")}`);await t.query("BEGIN READ ONLY ISOLATION LEVEL REPEATABLE READ");const E={};for(const[e,S]of Object.entries(ie)){const h=new Map;for(const _ of(await t.query(`SELECT ${S} AS id, seq FROM ${e}`)).rows)h.set(_.id,Number(_.seq));E[e]=h}const B=E.objects;let M=0;o.exec("BEGIN");for(const e of C){const S=Date.now(),h=new Set(o.prepare(`PRAGMA table_info(${c(e)})`).all().map(n=>n.name)),_=f.get(e);if(_===void 0)throw new Error(`export: Postgres has no table ${e}`);const G=_.some(n=>n.name==="seq"),O=_.filter(n=>h.has(n.name)),K=new Set(ne[e]??[]),g=e==="objects"||e==="revisions"||e==="watches"||e==="watch_subscriptions",$=O.map(n=>n.type==="jsonb"?`${c(n.name)}::text AS ${c(n.name)}`:c(n.name));e==="watches"&&$.push("watch_id AS _watch_id"),g&&$.push("seq AS _seq");const R=O.map(n=>c(n.name)),U=o.prepare(`INSERT INTO ${c(e)} (${g?["rowid",...R]:R}) VALUES (${(g?["?",...R]:R).map(()=>"?").join(", ")})`),q=G?"seq":ae[e],b=`export_${e}`;await t.query(`DECLARE ${b} NO SCROLL CURSOR FOR SELECT ${$.join(", ")} FROM ${c(e)}${e==="canvas_meta"?` WHERE key NOT IN (${[...H].map(n=>`'${n}'`).join(", ")})`:""}${q===void 0?"":` ORDER BY ${q}`}`);let T=0;for(;;){const n=await t.query(`FETCH ${re} FROM ${b}`);if(n.rows.length===0)break;for(const k of n.rows){const N=g?[Number(k._seq)]:[];for(const u of O){let l=k[u.name];e==="revision_parents"&&(u.name==="child"||u.name==="parent")?l=x(E.revisions,"revision",e,u.name,l):e==="watch_subscriptions"&&u.name==="watch"?l=x(E.watches,"watch",e,u.name,l):K.has(u.name)&&(l=x(B,"object",e,u.name,l)),N.push(l??null)}U.run(...N)}T+=n.rows.length}await t.query(`CLOSE ${b}`),M+=T,a(`export ${e}: ${T} rows in ${Date.now()-S} ms`)}o.exec("COMMIT"),await t.query("COMMIT");const v=o.prepare("PRAGMA foreign_key_check").all();if(v.length>0)throw new Error(`export: ${v.length} foreign key violation(s) in the exported canvas.db, first: ${JSON.stringify(v[0])}`);await pe(t,o);const y=o.prepare("PRAGMA user_version").get().user_version;if(y!==I.length)throw new Error(`export: exported canvas.db is at user_version ${y}, expected ${I.length}`);return o.close(),m=void 0,await t.query("INSERT INTO canvas_meta (key, value) VALUES ($1, $2) ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value",[F,JSON.stringify({at:new Date().toISOString(),path:s})]),X(r,s),{path:s,rows:M,ms:Date.now()-p,userVersion:y}}catch(d){await t.query("ROLLBACK").catch(()=>{});try{m?.close()}catch{}for(const o of["","-wal","-shm","-journal"])P(`${r}${o}`,{force:!0});throw d}finally{await t.end().catch(()=>{})}}w(Re,"exportPostgresToSqlite");function x(i,a,s,r,p){if(p==null)return null;const t=i.get(p);if(t===void 0)throw new Error(`export: ${s}.${r} references ${a} ${String(p)}, which was not exported (`);return t}w(x,"mapId");async function pe(i,a){const s=[];for(const r of C){const p=Number((await i.query(`SELECT count(*) AS n FROM ${c(r)}`)).rows[0].n);let t=Number(a.prepare(`SELECT count(*) AS n FROM ${c(r)}`).get().n),m=p;r==="canvas_meta"&&(m=Number((await i.query("SELECT count(*) AS n FROM canvas_meta WHERE key <> ALL($1)",[[...H]])).rows[0].n),t=Number(a.prepare("SELECT count(*) AS n FROM canvas_meta").get().n)),m!==t&&s.push(`${r}: Postgres ${m} rows, SQLite ${t}`)}if(s.length>0)throw new Error(`Postgres \u2192 SQLite export verification failed:
2
2
  ${s.join(`
3
3
  `)}`)}w(pe,"verify");export{Re as exportPostgresToSqlite};
@@ -5,7 +5,7 @@ export declare const IMPORT_MARK_KEY = "imported_from_sqlite";
5
5
  export declare const EXPORT_MARK_KEY = "exported_to_sqlite";
6
6
  /** Every canvas table, in dependency order (a table follows every table its foreign keys name). The import and the
7
7
  * rollback export both copy exactly this list. */
8
- export declare const TABLE_ORDER: readonly ["objects", "documents", "revisions", "revision_parents", "jobs", "identities", "custom_objects", "context_sessions", "peer_stubs", "object_kinds", "watches", "watch_subscriptions", "exposures", "rules", "edges", "unresolved_links", "nodes", "focuses", "canvas_meta", "runtime_state", "warm_pool", "sessions", "executions", "worktrees", "node_faults", "node_tokens", "node_outcome_deliveries", "deleted_isolated_nodes", "create_requests", "waits", "messages", "message_sequence", "message_causes", "deliveries", "structured_requests", "daemon_actions", "human_requests", "human_action_deliveries", "review_comments", "review_comment_events", "run_events", "idempotency_keys", "grants", "memory_reads", "app_users", "uploads", "upload_uses", "shares", "audit_rows", "grantee_usage", "provider_receipts", "provider_approvals", "usage", "crons", "cron_runs", "peer_outbox", "peer_locations", "peer_ingested", "peer_ingested_destinations"];
8
+ export declare const TABLE_ORDER: readonly ["objects", "documents", "revisions", "revision_parents", "jobs", "identities", "custom_objects", "context_sessions", "peer_stubs", "object_kinds", "watches", "watch_subscriptions", "exposures", "rules", "edges", "unresolved_links", "nodes", "focuses", "canvas_meta", "runtime_state", "warm_pool", "sessions", "executions", "worktrees", "node_faults", "node_tokens", "node_outcome_deliveries", "deleted_isolated_nodes", "create_requests", "waits", "messages", "message_sequence", "message_causes", "deliveries", "structured_requests", "daemon_actions", "human_requests", "human_action_deliveries", "review_comments", "review_comment_events", "run_events", "idempotency_keys", "grants", "memory_reads", "app_users", "uploads", "upload_uses", "shares", "audit_rows", "provider_approvals", "crons", "cron_runs", "peer_outbox", "peer_locations", "peer_ingested", "peer_ingested_destinations"];
9
9
  /** Columns holding `objects.rowid` in SQLite and `objects.id` in Postgres. */
10
10
  export declare const OBJECT_COLUMNS: Record<string, readonly string[]>;
11
11
  /** `optional`: the column may be left out of an INSERT (nullable, or it has a default or is an identity). */
@@ -1,7 +1,19 @@
1
- var X=Object.defineProperty;var p=(t,o)=>X(t,"name",{value:o,configurable:!0});import{DatabaseSync as z}from"node:sqlite";import{createHash as Z,randomBytes as ee}from"node:crypto";import{existsSync as O,mkdirSync as te,renameSync as F}from"node:fs";import{dirname as oe}from"node:path";import se from"pg";import{withExclusiveDirectoryLockAsync as re}from"../exclusive-lock.js";import{DAEMON_STARTUP_WINDOW_MS as ne}from"../../daemon/startup-policy.js";import{migrate as ae}from"./migrations.js";import{readLegacyPrivacy as ie}from"../graph/types.js";import{canvasDbPath as B,onDiskMigrationLockPath as ce}from"./paths.js";import{canvasSearchPathOptions as ue}from"./schema-version.js";const C="imported_from_sqlite",U="exported_to_sqlite",M=1e4,L=["objects","documents","revisions","revision_parents","jobs","identities","custom_objects","context_sessions","peer_stubs","object_kinds","watches","watch_subscriptions","exposures","rules","edges","unresolved_links","nodes","focuses","canvas_meta","runtime_state","warm_pool","sessions","executions","worktrees","node_faults","node_tokens","node_outcome_deliveries","deleted_isolated_nodes","create_requests","waits","messages","message_sequence","message_causes","deliveries","structured_requests","daemon_actions","human_requests","human_action_deliveries","review_comments","review_comment_events","run_events","idempotency_keys","grants","memory_reads","app_users","uploads","upload_uses","shares","audit_rows","grantee_usage","provider_receipts","provider_approvals","usage","crons","cron_runs","peer_outbox","peer_locations","peer_ingested","peer_ingested_destinations"],pe=new Set(["sqlite_sequence","sqlite_stat1","sqlite_stat4","reviews"]),de={objects:["owner"],documents:["object"],revisions:["document","author"],jobs:["object"],identities:["object"],custom_objects:["object"],context_sessions:["node"],peer_stubs:["object"],object_kinds:["owner"],watches:["watcher","object","link_app"],exposures:["node","object"],rules:["object","owner"],edges:["src","dst"],unresolved_links:["src"]},W=new Set(["daemon_owner","schema_version","schema_min_reader","ledger_migrated"]),me={canvas_meta:"key",message_sequence:"id"};function H(){return ee(16).toString("hex")}p(H,"newId");function f(t){return`"${t.replace(/"/g,'""')}"`}p(f,"quote");function fe(t){switch(t){case"bigint":return"bigint[]";case"double precision":return"float8[]";case"bytea":return"bytea[]";case"text":case"jsonb":return"text[]";default:throw new Error(`import: no array type for Postgres column type ${t}`)}}p(fe,"arrayType");async function we(t){const o=await t.query(`SELECT c.relname AS tbl, a.attname AS col, format_type(a.atttypid, a.atttypmod) AS type, a.attidentity <> '' AS identity, (NOT a.attnotnull OR a.atthasdef OR a.attidentity <> '') AS optional
1
+ var X=Object.defineProperty;var p=(s,t)=>X(s,"name",{value:t,configurable:!0});import{DatabaseSync as z}from"node:sqlite";import{createHash as Z,randomBytes as ee}from"node:crypto";import{existsSync as L,mkdirSync as te,renameSync as H}from"node:fs";import{dirname as oe}from"node:path";import se from"pg";import{withExclusiveDirectoryLockAsync as re}from"../exclusive-lock.js";import{DAEMON_STARTUP_WINDOW_MS as ne}from"../../daemon/startup-policy.js";import{migrate as ae}from"./migrations.js";import{readLegacyPrivacy as ie}from"../graph/types.js";import{LEGACY_LOCAL_PERSON as ce}from"./legacy-person.js";import{REBUILD_ALL_TOTALS as ue}from"../ledger/totals.js";import{canvasDbPath as U,onDiskMigrationLockPath as de}from"./paths.js";import{canvasSearchPathOptions as pe}from"./schema-version.js";const k="imported_from_sqlite",B="exported_to_sqlite",M=1e4,N=["objects","documents","revisions","revision_parents","jobs","identities","custom_objects","context_sessions","peer_stubs","object_kinds","watches","watch_subscriptions","exposures","rules","edges","unresolved_links","nodes","focuses","canvas_meta","runtime_state","warm_pool","sessions","executions","worktrees","node_faults","node_tokens","node_outcome_deliveries","deleted_isolated_nodes","create_requests","waits","messages","message_sequence","message_causes","deliveries","structured_requests","daemon_actions","human_requests","human_action_deliveries","review_comments","review_comment_events","run_events","idempotency_keys","grants","memory_reads","app_users","uploads","upload_uses","shares","audit_rows","provider_approvals","crons","cron_runs","peer_outbox","peer_locations","peer_ingested","peer_ingested_destinations"],me=new Set(["sqlite_sequence","sqlite_stat1","sqlite_stat4","reviews","usage","grantee_usage","provider_receipts"]),Ee={objects:["owner"],documents:["object"],revisions:["document","author"],jobs:["object"],identities:["object"],custom_objects:["object"],context_sessions:["node"],peer_stubs:["object"],object_kinds:["owner"],watches:["watcher","object","link_app"],exposures:["node","object"],rules:["object","owner"],edges:["src","dst"],unresolved_links:["src"]},W=new Set(["daemon_owner","schema_version","schema_min_reader","ledger_migrated"]),fe={canvas_meta:"key",message_sequence:"id"};function G(){return ee(16).toString("hex")}p(G,"newId");function f(s){return`"${s.replace(/"/g,'""')}"`}p(f,"quote");function we(s){switch(s){case"bigint":return"bigint[]";case"double precision":return"float8[]";case"bytea":return"bytea[]";case"text":case"jsonb":return"text[]";default:throw new Error(`import: no array type for Postgres column type ${s}`)}}p(we,"arrayType");async function le(s){const t=await s.query(`SELECT c.relname AS tbl, a.attname AS col, format_type(a.atttypid, a.atttypmod) AS type, a.attidentity <> '' AS identity, (NOT a.attnotnull OR a.atthasdef OR a.attidentity <> '') AS optional
2
2
  FROM pg_attribute a JOIN pg_class c ON c.oid = a.attrelid
3
3
  WHERE c.relnamespace = current_schema()::regnamespace AND c.relkind = 'r' AND a.attnum > 0 AND NOT a.attisdropped
4
- ORDER BY c.relname, a.attnum`),e=new Map;for(const a of o.rows){const n=e.get(a.tbl)??[];n.push({name:a.col,type:a.type,identity:a.identity,optional:a.optional}),e.set(a.tbl,n)}return e}p(we,"pgColumns");const le="\uFFFD";function Ee(t){if(!t.includes("\\u0000"))return{text:t,replaced:0};let o;try{o=JSON.parse(t)}catch{return{text:t,replaced:0}}let e=0;const a=p(c=>{const i=c.split("\0");return e+=i.length-1,i.join(le)},"clean"),n=p(c=>typeof c=="string"?a(c):Array.isArray(c)?c.map(n):c!==null&&typeof c=="object"?Object.fromEntries(Object.entries(c).map(([i,_])=>[a(i),n(_)])):c,"walk"),l=n(o);return e===0?{text:t,replaced:e}:{text:JSON.stringify(l),replaced:e}}p(Ee,"scrubNul");async function ye(t,o,e,a,n,l,c){for(let i=0;i<a.length;i++)for(const _ of e){const s=n[l.indexOf(_)][i];if(s!=null){await t.query("SAVEPOINT import_probe");try{await t.query("SELECT $1::jsonb",[s]),await t.query("RELEASE SAVEPOINT import_probe")}catch(u){return await t.query("ROLLBACK TO SAVEPOINT import_probe"),new Error(`import: ${o}.${_.name} at ${c(a[i])} is not a value jsonb accepts: ${u.message}`,{cause:u})}}}}p(ye,"describeJsonbFailure");function j(t,o,e,a,n){if(n==null)return null;const l=t.get(Number(n));if(l===void 0)throw new Error(`import: ${e}.${a} references ${o} rowid ${String(n)}, which does not exist`);return l}p(j,"lookup");async function _e(t,o,e,a,n,l){const c=t.prepare(`PRAGMA table_info(${f(e)})`).all(),i=new Set(c.map(r=>r.name)),_=p(r=>r==="seq"||r==="watch_id"||r==="subscription_id","minted"),s=a.filter(r=>i.has(r.name)||_(r.name)||!r.optional),E=!t.prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?").get(e).sql.toUpperCase().includes("WITHOUT ROWID"),b=new Set(de[e]??[]),$=[];for(const r of s)if(r.name==="seq"&&!i.has("seq")){if(!E)throw new Error(`import: ${e} needs seq from rowid but has none`);$.push("rowid AS seq")}else if(r.name==="watch_id"||r.name==="subscription_id")$.push("NULL AS "+f(r.name));else if(i.has(r.name))$.push(f(r.name));else throw new Error(`import: SQLite table ${e} has no column ${r.name}`);E&&$.push("rowid AS _rowid");const y=e==="canvas_meta"?` WHERE key NOT IN (${[...W].map(r=>`'${r}'`).join(",")})`:"",h=t.prepare(`SELECT ${$.join(", ")} FROM ${f(e)}${y}${E?" ORDER BY rowid":""}`),R=s.map(r=>f(r.name)),T=s.map((r,d)=>`$${d+1}::${fe(r.type)}`),A=s.map((r,d)=>`c${d}`),V=s.map((r,d)=>r.type==="jsonb"?`c${d}::jsonb`:`c${d}`);let v=`INSERT INTO ${f(e)} (${R.join(", ")}) SELECT ${V.join(", ")} FROM unnest(${T.join(", ")}) AS u(${A.join(", ")})`;const N=me[e];if(N!==void 0){const r=s.filter(d=>d.name!==N).map(d=>`${f(d.name)} = EXCLUDED.${f(d.name)}`);v+=` ON CONFLICT (${f(N)}) DO UPDATE SET ${r.join(", ")}`}const I=e==="objects",D=s.filter(r=>r.type==="jsonb"),k=E?"_rowid":s[0].name,J=p(r=>`${E?"rowid":k} ${String(r[k])}`,"key");let x=0,S=[];const P=p(async()=>{if(S.length===0)return;const r=s.map(()=>new Array(S.length));if(S.forEach((d,g)=>{s.forEach((m,Q)=>{let w=d[m.name];if(e==="objects"&&m.name==="privacy"?w=ie(w):m.name==="watch_id"||m.name==="subscription_id"?w=H():e==="revision_parents"&&(m.name==="child"||m.name==="parent")?w=j(n.revisions,"revision",e,m.name,w):e==="watch_subscriptions"&&m.name==="watch"?w=j(n.watches,"watch",e,m.name,w):b.has(m.name)&&(w=I?null:j(n.objects,"object",e,m.name,w)),m.type==="bytea"&&w instanceof Uint8Array&&!Buffer.isBuffer(w)&&(w=Buffer.from(w.buffer,w.byteOffset,w.byteLength)),m.type==="jsonb"&&typeof w=="string"){const q=Ee(w);q.replaced>0&&(w=q.text,l.set(`${e}.${m.name}`,(l.get(`${e}.${m.name}`)??0)+q.replaced))}r[Q][g]=w??null}),e==="watches"&&n.watches.set(Number(d._rowid),r[s.findIndex(m=>m.name==="watch_id")][g])}),D.length===0)await o.query(v,r);else{await o.query("SAVEPOINT import_batch");try{await o.query(v,r)}catch(d){throw await o.query("ROLLBACK TO SAVEPOINT import_batch"),await ye(o,e,D,S,r,s,J)??d}await o.query("RELEASE SAVEPOINT import_batch")}x+=S.length,S=[]},"flush");for(const r of h.iterate())e==="objects"&&n.objects.set(Number(r._rowid),r.id),e==="revisions"&&n.revisions.set(Number(r._rowid),r.rev_id),S.push(r),S.length>=M&&await P();if(await P(),I){const r=t.prepare("SELECT id, owner FROM objects WHERE owner IS NOT NULL").all();for(let d=0;d<r.length;d+=M){const g=r.slice(d,d+M);await o.query("UPDATE objects o SET owner = v.owner FROM unnest($1::text[], $2::text[]) AS v(id, owner) WHERE o.id = v.id",[g.map(m=>m.id),g.map(m=>j(n.objects,"object","objects","owner",m.owner))])}}return x}p(_e,"copyTable");async function he(t,o){for(const[e,a]of o)for(const n of a)n.identity&&await t.query(`SELECT setval(pg_get_serial_sequence($1, $2), COALESCE((SELECT max(${f(n.name)}) FROM ${f(e)}), 1), (SELECT max(${f(n.name)}) IS NOT NULL FROM ${f(e)}))`,[f(e),n.name])}p(he,"advanceIdentities");function G(t,o,e=""){return Number(t.prepare(`SELECT count(*) AS n FROM ${f(o)}${e}`).get().n)}p(G,"sqliteCount");async function $e(t,o,e){const a=[];for(const s of L){if(s==="canvas_meta")continue;const u=G(t,s),E=Number((await o.query(`SELECT count(*) AS n FROM ${f(s)}`)).rows[0].n);u!==E&&a.push(`${s}: SQLite ${u} rows, Postgres ${E}`)}for(const s of t.prepare(`SELECT key, value FROM canvas_meta WHERE key NOT IN (${[...W].map(u=>`'${u}'`).join(",")})`).all())(await o.query("SELECT value FROM canvas_meta WHERE key = $1",[s.key])).rows[0]?.value!==s.value&&a.push(`canvas_meta ${s.key}: value differs`);const n=p(s=>{const u=Z("sha256");for(const E of s)u.update(E).update(`
5
- `);return u.digest("hex")},"hash"),l=t.prepare("SELECT id FROM objects ORDER BY rowid").all().map(s=>s.id),c=(await o.query("SELECT id FROM objects ORDER BY seq")).rows.map(s=>s.id);n(l)!==n(c)&&a.push("objects: ids in insertion order differ");const i=t.prepare("SELECT o.id AS id, p.id AS owner FROM objects o JOIN objects p ON p.rowid = o.owner ORDER BY o.rowid").all(),_=(await o.query("SELECT id, owner FROM objects WHERE owner IS NOT NULL ORDER BY seq")).rows;n(i.map(s=>`${s.id}>${s.owner}`))!==n(_.map(s=>`${s.id}>${s.owner}`))&&a.push("objects: owner links differ");for(const[s,u]of[["run_events","body_json"],["messages","body_json"],["deliveries","last_failure_json"]]){const E=Number(t.prepare(`SELECT COALESCE(sum(length(CAST(${f(u)} AS TEXT))), 0) AS n FROM ${f(s)}`).get().n),b=e.get(s).find(h=>h.name===u).type,$=b==="jsonb"?`${f(u)}::text`:f(u);if(b==="jsonb")continue;const y=Number((await o.query(`SELECT COALESCE(sum(length(${$})), 0)::bigint AS n FROM ${f(s)}`)).rows[0].n);E!==y&&a.push(`${s}.${u}: total length SQLite ${E}, Postgres ${y}`)}if(a.length>0)throw new Error(`SQLite \u2192 Postgres import verification failed (rolled back; canvas.db untouched):
4
+ ORDER BY c.relname, a.attnum`),e=new Map;for(const a of t.rows){const n=e.get(a.tbl)??[];n.push({name:a.col,type:a.type,identity:a.identity,optional:a.optional}),e.set(a.tbl,n)}return e}p(le,"pgColumns");const _e="\uFFFD";function he(s){if(!s.includes("\\u0000"))return{text:s,replaced:0};let t;try{t=JSON.parse(s)}catch{return{text:s,replaced:0}}let e=0;const a=p(d=>{const i=d.split("\0");return e+=i.length-1,i.join(_e)},"clean"),n=p(d=>typeof d=="string"?a(d):Array.isArray(d)?d.map(n):d!==null&&typeof d=="object"?Object.fromEntries(Object.entries(d).map(([i,_])=>[a(i),n(_)])):d,"walk"),c=n(t);return e===0?{text:s,replaced:e}:{text:JSON.stringify(c),replaced:e}}p(he,"scrubNul");async function ye(s,t,e,a,n,c,d){for(let i=0;i<a.length;i++)for(const _ of e){const o=n[c.indexOf(_)][i];if(o!=null){await s.query("SAVEPOINT import_probe");try{await s.query("SELECT $1::jsonb",[o]),await s.query("RELEASE SAVEPOINT import_probe")}catch(u){return await s.query("ROLLBACK TO SAVEPOINT import_probe"),new Error(`import: ${t}.${_.name} at ${d(a[i])} is not a value jsonb accepts: ${u.message}`,{cause:u})}}}}p(ye,"describeJsonbFailure");function v(s,t,e,a,n){if(n==null)return null;const c=s.get(Number(n));if(c===void 0)throw new Error(`import: ${e}.${a} references ${t} rowid ${String(n)}, which does not exist`);return c}p(v,"lookup");async function $e(s,t,e,a,n,c){const d=s.prepare(`PRAGMA table_info(${f(e)})`).all(),i=new Set(d.map(r=>r.name)),_=p(r=>r==="seq"||r==="watch_id"||r==="subscription_id","minted"),o=a.filter(r=>i.has(r.name)||_(r.name)||!r.optional),w=!s.prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?").get(e).sql.toUpperCase().includes("WITHOUT ROWID"),$=new Set(Ee[e]??[]),y=[];for(const r of o)if(r.name==="seq"&&!i.has("seq")){if(!w)throw new Error(`import: ${e} needs seq from rowid but has none`);y.push("rowid AS seq")}else if(r.name==="watch_id"||r.name==="subscription_id")y.push("NULL AS "+f(r.name));else if(i.has(r.name))y.push(f(r.name));else throw new Error(`import: SQLite table ${e} has no column ${r.name}`);w&&y.push("rowid AS _rowid");const h=e==="canvas_meta"?` WHERE key NOT IN (${[...W].map(r=>`'${r}'`).join(",")})`:"",b=s.prepare(`SELECT ${y.join(", ")} FROM ${f(e)}${h}${w?" ORDER BY rowid":""}`),S=o.map(r=>f(r.name)),R=o.map((r,m)=>`$${m+1}::${we(r.type)}`),O=o.map((r,m)=>`c${m}`),A=o.map((r,m)=>r.type==="jsonb"?`c${m}::jsonb`:`c${m}`);let j=`INSERT INTO ${f(e)} (${S.join(", ")}) SELECT ${A.join(", ")} FROM unnest(${R.join(", ")}) AS u(${O.join(", ")})`;const C=fe[e];if(C!==void 0){const r=o.filter(m=>m.name!==C).map(m=>`${f(m.name)} = EXCLUDED.${f(m.name)}`);j+=` ON CONFLICT (${f(C)}) DO UPDATE SET ${r.join(", ")}`}const D=e==="objects",x=o.filter(r=>r.type==="jsonb"),I=w?"_rowid":o[0].name,J=p(r=>`${w?"rowid":I} ${String(r[I])}`,"key");let P=0,g=[];const F=p(async()=>{if(g.length===0)return;const r=o.map(()=>new Array(g.length));if(g.forEach((m,T)=>{o.forEach((E,Q)=>{let l=m[E.name];if(e==="objects"&&E.name==="privacy"?l=ie(l):E.name==="watch_id"||E.name==="subscription_id"?l=G():e==="revision_parents"&&(E.name==="child"||E.name==="parent")?l=v(n.revisions,"revision",e,E.name,l):e==="watch_subscriptions"&&E.name==="watch"?l=v(n.watches,"watch",e,E.name,l):$.has(E.name)&&(l=D?null:v(n.objects,"object",e,E.name,l)),E.type==="bytea"&&l instanceof Uint8Array&&!Buffer.isBuffer(l)&&(l=Buffer.from(l.buffer,l.byteOffset,l.byteLength)),E.type==="jsonb"&&typeof l=="string"){const q=he(l);q.replaced>0&&(l=q.text,c.set(`${e}.${E.name}`,(c.get(`${e}.${E.name}`)??0)+q.replaced))}r[Q][T]=l??null}),e==="watches"&&n.watches.set(Number(m._rowid),r[o.findIndex(E=>E.name==="watch_id")][T])}),x.length===0)await t.query(j,r);else{await t.query("SAVEPOINT import_batch");try{await t.query(j,r)}catch(m){throw await t.query("ROLLBACK TO SAVEPOINT import_batch"),await ye(t,e,x,g,r,o,J)??m}await t.query("RELEASE SAVEPOINT import_batch")}P+=g.length,g=[]},"flush");for(const r of b.iterate())e==="objects"&&n.objects.set(Number(r._rowid),r.id),e==="revisions"&&n.revisions.set(Number(r._rowid),r.rev_id),g.push(r),g.length>=M&&await F();if(await F(),D){const r=s.prepare("SELECT id, owner FROM objects WHERE owner IS NOT NULL").all();for(let m=0;m<r.length;m+=M){const T=r.slice(m,m+M);await t.query("UPDATE objects o SET owner = v.owner FROM unnest($1::text[], $2::text[]) AS v(id, owner) WHERE o.id = v.id",[T.map(E=>E.id),T.map(E=>v(n.objects,"object","objects","owner",E.owner))])}}return P}p($e,"copyTable");async function Se(s,t){for(const[e,a]of t)for(const n of a)n.identity&&await s.query(`SELECT setval(pg_get_serial_sequence($1, $2), COALESCE((SELECT max(${f(n.name)}) FROM ${f(e)}), 1), (SELECT max(${f(n.name)}) IS NOT NULL FROM ${f(e)}))`,[f(e),n.name])}p(Se,"advanceIdentities");function Y(s,t,e=""){return Number(s.prepare(`SELECT count(*) AS n FROM ${f(t)}${e}`).get().n)}p(Y,"sqliteCount");async function ge(s,t,e){const a=[];for(const o of N){if(o==="canvas_meta")continue;const u=Y(s,o),w=Number((await t.query(`SELECT count(*) AS n FROM ${f(o)}`)).rows[0].n);u!==w&&a.push(`${o}: SQLite ${u} rows, Postgres ${w}`)}for(const o of s.prepare(`SELECT key, value FROM canvas_meta WHERE key NOT IN (${[...W].map(u=>`'${u}'`).join(",")})`).all())(await t.query("SELECT value FROM canvas_meta WHERE key = $1",[o.key])).rows[0]?.value!==o.value&&a.push(`canvas_meta ${o.key}: value differs`);const n=p(o=>{const u=Z("sha256");for(const w of o)u.update(w).update(`
5
+ `);return u.digest("hex")},"hash"),c=s.prepare("SELECT id FROM objects ORDER BY rowid").all().map(o=>o.id),d=(await t.query("SELECT id FROM objects ORDER BY seq")).rows.map(o=>o.id);n(c)!==n(d)&&a.push("objects: ids in insertion order differ");const i=s.prepare("SELECT o.id AS id, p.id AS owner FROM objects o JOIN objects p ON p.rowid = o.owner ORDER BY o.rowid").all(),_=(await t.query("SELECT id, owner FROM objects WHERE owner IS NOT NULL ORDER BY seq")).rows;n(i.map(o=>`${o.id}>${o.owner}`))!==n(_.map(o=>`${o.id}>${o.owner}`))&&a.push("objects: owner links differ");for(const[o,u]of[["run_events","body_json"],["messages","body_json"],["deliveries","last_failure_json"]]){const w=Number(s.prepare(`SELECT COALESCE(sum(length(CAST(${f(u)} AS TEXT))), 0) AS n FROM ${f(o)}`).get().n),$=e.get(o).find(b=>b.name===u).type,y=$==="jsonb"?`${f(u)}::text`:f(u);if($==="jsonb")continue;const h=Number((await t.query(`SELECT COALESCE(sum(length(${y})), 0)::bigint AS n FROM ${f(o)}`)).rows[0].n);w!==h&&a.push(`${o}.${u}: total length SQLite ${w}, Postgres ${h}`)}if(a.length>0)throw new Error(`SQLite \u2192 Postgres import verification failed (rolled back; canvas.db untouched):
6
6
  ${a.join(`
7
- `)}`)}p($e,"verify");async function be(t,o,e={}){const a=e.log??(()=>{}),n=Date.now(),l=await we(o),c=t.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all().map(y=>y.name),i=c.filter(y=>!pe.has(y)&&!L.includes(y));if(i.length>0)throw new Error(`import: SQLite tables with no copy rule: ${i.join(", ")}`);const _=L.filter(y=>!l.has(y));if(_.length>0)throw new Error(`import: Postgres tables missing from the schema: ${_.join(", ")}`);const s=c.includes("reviews")?G(t,"reviews"):0;if(s>0)throw new Error(`import: the legacy reviews table still holds ${s} rows after the SQLite migration chain`);const u={objects:new Map,revisions:new Map,watches:new Map},E=[],b=new Map;let $=0;await o.query("BEGIN");try{await o.query("ALTER TABLE nodes DISABLE TRIGGER USER"),await o.query("ALTER TABLE deliveries DISABLE TRIGGER USER");for(const h of L){const R=Date.now(),T=await _e(t,o,h,l.get(h),u,b),A=Date.now()-R;E.push({table:h,rows:T,ms:A}),a(`import ${h}: ${T} rows in ${A} ms`)}await o.query("ALTER TABLE nodes ENABLE TRIGGER USER"),await o.query("ALTER TABLE deliveries ENABLE TRIGGER USER"),await he(o,l);const y=Date.now();await $e(t,o,l),$=Date.now()-y,a(`import verify: ${$} ms`),await o.query("INSERT INTO canvas_meta (key, value) VALUES ($1, $2)",[C,JSON.stringify({at:new Date().toISOString(),rows:E.reduce((h,R)=>h+R.rows,0)})]),await o.query("COMMIT")}catch(y){throw await o.query("ROLLBACK").catch(()=>{}),y}return b.size>0&&a(`import: replaced NUL (\\u0000) with U+FFFD in jsonb values: ${[...b].map(([y,h])=>`${y} ${h}`).join(", ")}`),{tables:E,verifyMs:$,totalMs:Date.now()-n,nulReplaced:Object.fromEntries(b)}}p(be,"copySqliteToPostgres");const Y=["","-wal","-shm"];function K(t=B()){if(O(`${t}.imported`)){const o=`${t}.imported.${Date.now()}`;for(const e of Y)O(`${t}.imported${e}`)&&F(`${t}.imported${e}`,`${o}${e}`)}for(const o of Y)O(`${t}${o}`)&&F(`${t}${o}`,`${t}.imported${o}`)}p(K,"renameImportedDatabase");async function Me(t,o=()=>{}){const e=B();if(!O(e))return null;const a=ce();return te(oe(a),{recursive:!0,mode:448}),re(a,async()=>{if(!O(e))return null;const n=new se.Client({connectionString:t,application_name:`crtr-import-${process.pid}`,options:ue()});await n.connect();try{const l=await n.query("SELECT key FROM canvas_meta WHERE key = ANY($1)",[[C,U]]),c=new Set(l.rows.map(s=>s.key));if(c.has(C)){if(c.has(U))throw new Error(`${e} was written by \`crtr sys db export-sqlite\` and the Postgres database predates it; importing would drop the exported writes. Stop the daemon and delete the Postgres cluster (runtime/pg/data) to import ${e} afresh, or move ${e} aside to keep using Postgres.`);return K(e),o(`import: finished the rename of ${e} (already imported)`),null}const i=new z(e);let _;try{i.exec("PRAGMA busy_timeout = 5000;"),i.exec("PRAGMA foreign_keys = ON;"),ae(i),_=await be(i,n,{log:o})}finally{i.close()}return K(e),o(`import: ${_.tables.reduce((s,u)=>s+u.rows,0)} rows in ${_.totalMs} ms; canvas.db renamed to canvas.db.imported`),_}finally{await n.end().catch(()=>{})}},{timeoutMs:ne,timeoutError:p(()=>new Error(`timed out waiting for the canvas import lock: ${a}`),"timeoutError")})}p(Me,"importSqliteCanvasIfPresent");export{U as EXPORT_MARK_KEY,C as IMPORT_MARK_KEY,de as OBJECT_COLUMNS,L as TABLE_ORDER,be as copySqliteToPostgres,Me as importSqliteCanvasIfPresent,we as pgColumns,f as quote,K as renameImportedDatabase};
7
+ `)}`)}p(ge,"verify");async function be(s,t){const e=s.prepare("SELECT grantee, month, payer, source, served_model, input_tokens, output_tokens, cached_tokens, usd_equivalent FROM usage").all();if(e.length===0)return 0;const n=(await t.query("SELECT value FROM canvas_meta WHERE key = 'person_id'")).rows[0]?.value??ce,c=p((o,u=String)=>e.map(w=>w[o]===null?null:u(w[o])),"column"),d=await t.query(`INSERT INTO charges (id, request, at, month, kind, what, units, usd, payer, funding, sub, grantee, provider)
8
+ SELECT 'legacy:' || u.grantee || ':' || u.month || ':' || u.payer || ':' || u.source || ':' || u.served_model,
9
+ NULL, u.month || '-01T00:00:00.000Z', u.month,
10
+ CASE WHEN u.source = 'model' THEN 'model' ELSE 'tool' END,
11
+ CASE WHEN u.source = 'model' THEN u.served_model ELSE substr(u.source, 10) END,
12
+ CASE WHEN u.source = 'model' THEN jsonb_build_object('input', u.input_tokens, 'output', u.output_tokens, 'cache_read', u.cached_tokens, 'cache_write', 0) END,
13
+ u.usd_equivalent,
14
+ CASE WHEN u.payer = 'app' THEN u.grantee ELSE $1::text END,
15
+ CASE WHEN u.payer = 'app' THEN 'endpoint' WHEN u.source = 'model' THEN 'legacy' ELSE 'bill' END,
16
+ $1::text, u.grantee,
17
+ CASE WHEN u.source = 'model' THEN NULL ELSE 'app:' || substr(u.source, 10) END
18
+ FROM unnest($2::text[], $3::text[], $4::text[], $5::text[], $6::text[], $7::bigint[], $8::bigint[], $9::bigint[], $10::float8[])
19
+ AS u(grantee, month, payer, source, served_model, input_tokens, output_tokens, cached_tokens, usd_equivalent)`,[n,c("grantee"),c("month"),c("payer"),c("source"),c("served_model"),c("input_tokens"),c("output_tokens"),c("cached_tokens"),c("usd_equivalent",Number)]);for(const o of ue)await t.query(o);const i=(await t.query("SELECT count(*)::int AS n, COALESCE(SUM(usd), 0)::float8 AS usd FROM charges WHERE id LIKE 'legacy:%'")).rows[0],_=e.reduce((o,u)=>o+Number(u.usd_equivalent??0),0);if(d.rowCount!==e.length||i.n!==e.length||Math.abs(i.usd-_)>1e-4)throw new Error(`import: the usage \u2192 charges copy does not match (${e.length} usage rows, ${d.rowCount} charges inserted, ${i.n} legacy charges; usd ${_} vs ${i.usd})`);return e.length}p(be,"importLegacyUsage");async function Re(s,t,e={}){const a=e.log??(()=>{}),n=Date.now(),c=await le(t),d=s.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all().map(h=>h.name),i=d.filter(h=>!me.has(h)&&!N.includes(h));if(i.length>0)throw new Error(`import: SQLite tables with no copy rule: ${i.join(", ")}`);const _=N.filter(h=>!c.has(h));if(_.length>0)throw new Error(`import: Postgres tables missing from the schema: ${_.join(", ")}`);const o=d.includes("reviews")?Y(s,"reviews"):0;if(o>0)throw new Error(`import: the legacy reviews table still holds ${o} rows after the SQLite migration chain`);const u={objects:new Map,revisions:new Map,watches:new Map},w=[],$=new Map;let y=0;await t.query("BEGIN");try{await t.query("ALTER TABLE nodes DISABLE TRIGGER USER"),await t.query("ALTER TABLE deliveries DISABLE TRIGGER USER");for(const S of N){const R=Date.now(),O=await $e(s,t,S,c.get(S),u,$),A=Date.now()-R;w.push({table:S,rows:O,ms:A}),a(`import ${S}: ${O} rows in ${A} ms`)}await t.query("ALTER TABLE nodes ENABLE TRIGGER USER"),await t.query("ALTER TABLE deliveries ENABLE TRIGGER USER"),await Se(t,c);const h=await be(s,t);h>0&&a(`import usage: ${h} rows into charges`);const b=Date.now();await ge(s,t,c),y=Date.now()-b,a(`import verify: ${y} ms`),await t.query("INSERT INTO canvas_meta (key, value) VALUES ($1, $2)",[k,JSON.stringify({at:new Date().toISOString(),rows:w.reduce((S,R)=>S+R.rows,0)})]),await t.query("COMMIT")}catch(h){throw await t.query("ROLLBACK").catch(()=>{}),h}return $.size>0&&a(`import: replaced NUL (\\u0000) with U+FFFD in jsonb values: ${[...$].map(([h,b])=>`${h} ${b}`).join(", ")}`),{tables:w,verifyMs:y,totalMs:Date.now()-n,nulReplaced:Object.fromEntries($)}}p(Re,"copySqliteToPostgres");const K=["","-wal","-shm"];function V(s=U()){if(L(`${s}.imported`)){const t=`${s}.imported.${Date.now()}`;for(const e of K)L(`${s}.imported${e}`)&&H(`${s}.imported${e}`,`${t}${e}`)}for(const t of K)L(`${s}${t}`)&&H(`${s}${t}`,`${s}.imported${t}`)}p(V,"renameImportedDatabase");async function Pe(s,t=()=>{}){const e=U();if(!L(e))return null;const a=de();return te(oe(a),{recursive:!0,mode:448}),re(a,async()=>{if(!L(e))return null;const n=new se.Client({connectionString:s,application_name:`crtr-import-${process.pid}`,options:pe()});await n.connect();try{const c=await n.query("SELECT key FROM canvas_meta WHERE key = ANY($1)",[[k,B]]),d=new Set(c.rows.map(o=>o.key));if(d.has(k)){if(d.has(B))throw new Error(`${e} was written by \`crtr sys db export-sqlite\` and the Postgres database predates it; importing would drop the exported writes. Stop the daemon and delete the Postgres cluster (runtime/pg/data) to import ${e} afresh, or move ${e} aside to keep using Postgres.`);return V(e),t(`import: finished the rename of ${e} (already imported)`),null}const i=new z(e);let _;try{i.exec("PRAGMA busy_timeout = 5000;"),i.exec("PRAGMA foreign_keys = ON;"),ae(i),_=await Re(i,n,{log:t})}finally{i.close()}return V(e),t(`import: ${_.tables.reduce((o,u)=>o+u.rows,0)} rows in ${_.totalMs} ms; canvas.db renamed to canvas.db.imported`),_}finally{await n.end().catch(()=>{})}},{timeoutMs:ne,timeoutError:p(()=>new Error(`timed out waiting for the canvas import lock: ${a}`),"timeoutError")})}p(Pe,"importSqliteCanvasIfPresent");export{B as EXPORT_MARK_KEY,k as IMPORT_MARK_KEY,Ee as OBJECT_COLUMNS,N as TABLE_ORDER,Re as copySqliteToPostgres,Pe as importSqliteCanvasIfPresent,le as pgColumns,f as quote,V as renameImportedDatabase};
@@ -0,0 +1,52 @@
1
+ -- 003-fleet-removals.sql — cycle 5 removals (design-fleet "Data model", D72). One transaction; ends by raising schema_min_reader.
2
+ -- The previous release (version 2, the build that shipped 002) opens the result: it reads none of what is removed here (see the
3
+ -- reader floor at the end).
4
+
5
+ -- The version-3 build no longer copies `usage` into `charges` at boot (cycle 4's first boot did; `ledger_migrated` marks it done), so a
6
+ -- database that still holds un-migrated rows in `usage`, `grantee_usage` or `provider_receipts` would strand them. Refuse until a
7
+ -- cycle 4 build has run against it. A fresh database, or one that never had cycle-3 rows, has all three empty, so the chain from the
8
+ -- baseline to here still applies (D71). The three tables stay: the version-2 build still lists them in its SQLite import and export,
9
+ -- and a later delta (004, with the `jobs` columns, once the reader floor reaches 3) drops them.
10
+ DO $$
11
+ BEGIN
12
+ IF NOT EXISTS (SELECT 1 FROM canvas_meta WHERE key = 'ledger_migrated')
13
+ AND (EXISTS (SELECT 1 FROM usage) OR EXISTS (SELECT 1 FROM grantee_usage) OR EXISTS (SELECT 1 FROM provider_receipts)) THEN
14
+ RAISE EXCEPTION 'schema step 3 (003-fleet-removals): usage, grantee_usage or provider_receipts still hold rows and canvas_meta.ledger_migrated is not set: the cycle 4 money ledger step has not run, and this release no longer runs it. Run a cycle 4 build (v0.3.552 or later, whose first boot copies usage into charges) against this database before this release.';
15
+ END IF;
16
+ END $$;
17
+
18
+ -- 002 replaced the unique (grantee, sub) with unique (runtime, sub, grantee) and kept the old one for this release.
19
+ DROP INDEX idx_grants_grantee_sub;
20
+
21
+ -- A machine environment needs its machine id (002 made the column nullable for Blaxel environments; S1 review finding 2).
22
+ ALTER TABLE environments ADD CONSTRAINT environments_machine_id_check CHECK (kind <> 'machine' OR machine_id IS NOT NULL);
23
+
24
+ -- 002's trigger function changed after it shipped (a negative spend_cap now fails the limits CHECK instead of deleting the limit).
25
+ -- A database that applied the earlier 002 text never re-runs it, so the current body is carried here; on a database built from the
26
+ -- current 002 this replaces the function with itself.
27
+ CREATE OR REPLACE FUNCTION crtr_grant_limit() RETURNS trigger LANGUAGE plpgsql AS $$
28
+ BEGIN
29
+ IF TG_OP = 'DELETE' THEN
30
+ DELETE FROM limits WHERE source = 'grant_sync' AND id = 'grant:' || OLD.id;
31
+ RETURN OLD;
32
+ END IF;
33
+ IF NEW.status = 'active' AND NEW.spend_cap IS NOT NULL
34
+ AND NEW.payer IS DISTINCT FROM 'app' AND NEW.grantee <> 'app:terminal' THEN
35
+ INSERT INTO limits (id, payer, grantee, usd_per_month, source, updated_at)
36
+ VALUES ('grant:' || NEW.id, NEW.sub, NEW.grantee, NEW.spend_cap::numeric, 'grant_sync',
37
+ to_char(now() AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'))
38
+ ON CONFLICT (id) DO UPDATE SET payer = EXCLUDED.payer, funding = NULL, grantee = EXCLUDED.grantee, provider = NULL, sub = NULL,
39
+ account = NULL, usd_per_month = EXCLUDED.usd_per_month, source = 'grant_sync', updated_at = EXCLUDED.updated_at
40
+ WHERE limits.source = 'grant_sync';
41
+ ELSE
42
+ DELETE FROM limits WHERE source = 'grant_sync' AND id = 'grant:' || NEW.id;
43
+ END IF;
44
+ RETURN NULL;
45
+ END $$;
46
+
47
+ -- ---------------------------------------------------------------- reader floor
48
+
49
+ -- Version 2 reads nothing removed above: its grant reads name no index and it writes environments with a machine id; the money
50
+ -- tables it reads (boot backfill, SQLite import and export) still exist. Version 1 (before 002) writes environments of kind machine
51
+ -- without a machine id check and lacks the 002 tables, so it is refused.
52
+ INSERT INTO canvas_meta (key, value) VALUES ('schema_min_reader', '2') ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
@@ -1,7 +1,7 @@
1
1
  /** The canvas schema version this build supports: the number of the highest delta in `schema-pg/` (the baseline
2
2
  * `schema-pg.sql` is version 1). `scripts/install-runtime.mjs` reads this module from a candidate generation to refuse
3
3
  * one below the live database's `schema_min_reader`. */
4
- export declare const CANVAS_SCHEMA_VERSION = 2;
4
+ export declare const CANVAS_SCHEMA_VERSION = 3;
5
5
  /** The Postgres schema holding the canvas tables: `CRTR_DATABASE_SCHEMA` (`crtr` in the cloud deploy). Unset or empty
6
6
  * means the role's own search_path (`public` on a Mac and on a VM). A name that is not a plain identifier is an error:
7
7
  * it is put in a `search_path` connection option. */
@@ -1 +1 @@
1
- var m=Object.defineProperty;var r=(n,e)=>m(n,"name",{value:e,configurable:!0});const A=2;function _(n=process.env){const e=n.CRTR_DATABASE_SCHEMA?.trim();if(e){if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(e))throw new Error(`CRTR_DATABASE_SCHEMA must be a plain identifier (letters, digits, underscore); got ${JSON.stringify(e)}`);return e}}r(_,"canvasDatabaseSchema");function h(n=process.env){return n.CRTR_SCHEMA_DATABASE_URL?.trim()||void 0}r(h,"canvasSchemaDatabaseUrl");function E(n=process.env){const e=_(n);return e===void 0?void 0:`-c search_path=${e}`}r(E,"canvasSearchPathOptions");async function v(n,e){const t=e===void 0?"canvas_meta":`"${e.replace(/"/g,'""')}".canvas_meta`;if((await n.query(`SELECT to_regclass('${t.replace(/'/g,"''")}') IS NOT NULL AS found`)).rows[0].found!==!0)return;const u=(await n.query(`SELECT key, value FROM ${t} WHERE key IN ('schema_version', 'schema_min_reader')`)).rows,o=r(c=>{const i=u.find(f=>f.key===c);if(i===void 0)return;const d=Number(i.value);if(!Number.isSafeInteger(d))throw new Error(`canvas_meta.${c} is not an integer: ${String(i.value)}`);return d},"value"),s=o("schema_version");return s===void 0?void 0:{version:s,minReader:o("schema_min_reader")??s}}r(v,"readStoredVersions");async function l(n,e,t){const a=await v(n,t);if(a!==void 0&&a.minReader>e)throw new Error(`refusing to select runtime with canvas schema ${e}: the live database requires readers at version ${a.minReader} or above (it is at ${a.version}); install a descendant build instead`)}r(l,"assertCandidateMayOpen");export{A as CANVAS_SCHEMA_VERSION,l as assertCandidateMayOpen,_ as canvasDatabaseSchema,h as canvasSchemaDatabaseUrl,E as canvasSearchPathOptions,v as readStoredVersions};
1
+ var m=Object.defineProperty;var r=(n,e)=>m(n,"name",{value:e,configurable:!0});const A=3;function _(n=process.env){const e=n.CRTR_DATABASE_SCHEMA?.trim();if(e){if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(e))throw new Error(`CRTR_DATABASE_SCHEMA must be a plain identifier (letters, digits, underscore); got ${JSON.stringify(e)}`);return e}}r(_,"canvasDatabaseSchema");function h(n=process.env){return n.CRTR_SCHEMA_DATABASE_URL?.trim()||void 0}r(h,"canvasSchemaDatabaseUrl");function E(n=process.env){const e=_(n);return e===void 0?void 0:`-c search_path=${e}`}r(E,"canvasSearchPathOptions");async function v(n,e){const t=e===void 0?"canvas_meta":`"${e.replace(/"/g,'""')}".canvas_meta`;if((await n.query(`SELECT to_regclass('${t.replace(/'/g,"''")}') IS NOT NULL AS found`)).rows[0].found!==!0)return;const u=(await n.query(`SELECT key, value FROM ${t} WHERE key IN ('schema_version', 'schema_min_reader')`)).rows,o=r(c=>{const i=u.find(f=>f.key===c);if(i===void 0)return;const d=Number(i.value);if(!Number.isSafeInteger(d))throw new Error(`canvas_meta.${c} is not an integer: ${String(i.value)}`);return d},"value"),s=o("schema_version");return s===void 0?void 0:{version:s,minReader:o("schema_min_reader")??s}}r(v,"readStoredVersions");async function l(n,e,t){const a=await v(n,t);if(a!==void 0&&a.minReader>e)throw new Error(`refusing to select runtime with canvas schema ${e}: the live database requires readers at version ${a.minReader} or above (it is at ${a.version}); install a descendant build instead`)}r(l,"assertCandidateMayOpen");export{A as CANVAS_SCHEMA_VERSION,l as assertCandidateMayOpen,_ as canvasDatabaseSchema,h as canvasSchemaDatabaseUrl,E as canvasSearchPathOptions,v as readStoredVersions};
@@ -5,5 +5,4 @@ export { writeCharge, receiptChargeId, takePct, chargeOf, type WriteResult } fro
5
5
  export { check, findRefusal, refusalError, refusedMark, userActionOf, type Candidate, type CallCandidate, type StartCandidate, type Refusal } from './check.js';
6
6
  export { upsertLimit, deleteLimit, listLimits } from './limits.js';
7
7
  export { usageFor, listCharges, monthTotals, assertReference, type ChargePage, type ListFilter, type MonthTotals } from './read.js';
8
- export { backfillLedger, LEDGER_MIGRATED_KEY, type BackfillResult } from './backfill.js';
9
- export { rebuildTotals, rebuildTotalsFor } from './totals.js';
8
+ export { REBUILD_ALL_TOTALS, rebuildTotalsFor } from './totals.js';
@@ -1 +1 @@
1
- export*from"./types.js";export*from"./months.js";import{payerOf as f}from"./payer.js";import{writeCharge as i,receiptChargeId as s,takePct as m,chargeOf as l}from"./charges.js";import{check as x,findRefusal as c,refusalError as u,refusedMark as d,userActionOf as g}from"./check.js";import{upsertLimit as E,deleteLimit as L,listLimits as k}from"./limits.js";import{usageFor as R,listCharges as T,monthTotals as b,assertReference as C}from"./read.js";import{backfillLedger as A,LEDGER_MIGRATED_KEY as D}from"./backfill.js";import{rebuildTotals as G,rebuildTotalsFor as I}from"./totals.js";export{D as LEDGER_MIGRATED_KEY,C as assertReference,A as backfillLedger,l as chargeOf,x as check,L as deleteLimit,c as findRefusal,T as listCharges,k as listLimits,b as monthTotals,f as payerOf,G as rebuildTotals,I as rebuildTotalsFor,s as receiptChargeId,u as refusalError,d as refusedMark,m as takePct,E as upsertLimit,R as usageFor,g as userActionOf,i as writeCharge};
1
+ export*from"./types.js";export*from"./months.js";import{payerOf as f}from"./payer.js";import{writeCharge as a,receiptChargeId as i,takePct as m,chargeOf as p}from"./charges.js";import{check as x,findRefusal as c,refusalError as u,refusedMark as L,userActionOf as h}from"./check.js";import{upsertLimit as g,deleteLimit as n,listLimits as O}from"./limits.js";import{usageFor as k,listCharges as A,monthTotals as C,assertReference as R}from"./read.js";import{REBUILD_ALL_TOTALS as F,rebuildTotalsFor as I}from"./totals.js";export{F as REBUILD_ALL_TOTALS,R as assertReference,p as chargeOf,x as check,n as deleteLimit,c as findRefusal,A as listCharges,O as listLimits,C as monthTotals,f as payerOf,I as rebuildTotalsFor,i as receiptChargeId,u as refusalError,L as refusedMark,m as takePct,g as upsertLimit,k as usageFor,h as userActionOf,a as writeCharge};
@@ -13,8 +13,9 @@ export declare function addToTotals(db: CanvasDb, charge: {
13
13
  usd: number | null;
14
14
  }): Promise<void>;
15
15
  /** Rebuild the whole rollup from `charges`, `billed_usd` with `usd`: a rebuild that dropped it would zero the billed totals
16
- * 002's trigger keeps, and an older build that may still open the database must not do that either. */
17
- export declare function rebuildTotals(db: CanvasDb): Promise<void>;
16
+ * 002's trigger keeps, and an older build that may still open the database must not do that either. Plain SQL, so the SQLite
17
+ * import runs it on its own client. */
18
+ export declare const REBUILD_ALL_TOTALS: readonly ["DELETE FROM charge_totals", "INSERT INTO charge_totals (month, payer, funding, sub, grantee, provider, usd, billed_usd) SELECT month, payer, funding, COALESCE(sub, ''), grantee, COALESCE(provider, ''), COALESCE(SUM(usd), 0), COALESCE(SUM(billed_usd), 0)\n FROM charges WHERE refused IS NULL AND kind <> 'take' GROUP BY month, payer, funding, COALESCE(sub, ''), grantee, COALESCE(provider, '')"];
18
19
  /** Delete the rollup rows that name `a` or `b` as payer, sub or grantee and rebuild them from `charges`, so two rows that
19
20
  * now share a key are summed once. For person substitution, in the same transaction as the rename. */
20
21
  export declare function rebuildTotalsFor(db: CanvasDb, a: string, b: string): Promise<void>;
@@ -1,4 +1,4 @@
1
- var o=Object.defineProperty;var t=(r,e)=>o(r,"name",{value:e,configurable:!0});const a=t(r=>r??"","absent");async function E(r,e){await r.prepare(`INSERT INTO charge_totals (month, payer, funding, sub, grantee, provider, usd) VALUES (?, ?, ?, ?, ?, ?, ?)
2
- ON CONFLICT (month, payer, funding, sub, grantee, provider) DO UPDATE SET usd = charge_totals.usd + excluded.usd`).run(e.month,e.payer,e.funding,a(e.sub),e.grantee,a(e.provider),e.usd??0)}t(E,"addToTotals");const u=`SELECT month, payer, funding, COALESCE(sub, ''), grantee, COALESCE(provider, ''), COALESCE(SUM(usd), 0), COALESCE(SUM(billed_usd), 0)
3
- FROM charges WHERE refused IS NULL AND kind <> 'take'`,s="GROUP BY month, payer, funding, COALESCE(sub, ''), grantee, COALESCE(provider, '')";async function p(r){await r.prepare("DELETE FROM charge_totals").run(),await r.prepare(`INSERT INTO charge_totals (month, payer, funding, sub, grantee, provider, usd, billed_usd) ${u} ${s}`).run()}t(p,"rebuildTotals");async function i(r,e,n){await r.prepare("DELETE FROM charge_totals WHERE payer IN (?, ?) OR sub IN (?, ?) OR grantee IN (?, ?)").run(e,n,e,n,e,n),await r.prepare(`INSERT INTO charge_totals (month, payer, funding, sub, grantee, provider, usd, billed_usd) ${u}
4
- AND (payer IN (?, ?) OR sub IN (?, ?) OR grantee IN (?, ?)) ${s}`).run(e,n,e,n,e,n)}t(i,"rebuildTotalsFor");export{a as absent,E as addToTotals,p as rebuildTotals,i as rebuildTotalsFor};
1
+ var E=Object.defineProperty;var t=(r,e)=>E(r,"name",{value:e,configurable:!0});const s=t(r=>r??"","absent");async function o(r,e){await r.prepare(`INSERT INTO charge_totals (month, payer, funding, sub, grantee, provider, usd) VALUES (?, ?, ?, ?, ?, ?, ?)
2
+ ON CONFLICT (month, payer, funding, sub, grantee, provider) DO UPDATE SET usd = charge_totals.usd + excluded.usd`).run(e.month,e.payer,e.funding,s(e.sub),e.grantee,s(e.provider),e.usd??0)}t(o,"addToTotals");const a=`SELECT month, payer, funding, COALESCE(sub, ''), grantee, COALESCE(provider, ''), COALESCE(SUM(usd), 0), COALESCE(SUM(billed_usd), 0)
3
+ FROM charges WHERE refused IS NULL AND kind <> 'take'`,u="GROUP BY month, payer, funding, COALESCE(sub, ''), grantee, COALESCE(provider, '')",p=["DELETE FROM charge_totals",`INSERT INTO charge_totals (month, payer, funding, sub, grantee, provider, usd, billed_usd) ${a} ${u}`];async function i(r,e,n){await r.prepare("DELETE FROM charge_totals WHERE payer IN (?, ?) OR sub IN (?, ?) OR grantee IN (?, ?)").run(e,n,e,n,e,n),await r.prepare(`INSERT INTO charge_totals (month, payer, funding, sub, grantee, provider, usd, billed_usd) ${a}
4
+ AND (payer IN (?, ?) OR sub IN (?, ?) OR grantee IN (?, ?)) ${u}`).run(e,n,e,n,e,n)}t(i,"rebuildTotalsFor");export{p as REBUILD_ALL_TOTALS,s as absent,o as addToTotals,i as rebuildTotalsFor};
@@ -1,17 +1,17 @@
1
- var Ae=Object.defineProperty;var i=(n,s)=>Ae(n,"name",{value:s,configurable:!0});import{backfillLedger as Me}from"../core/ledger/backfill.js";import{startPeerOutbox as Ne}from"./peer/outbox.js";import{reportMachineStatus as Fe}from"../core/peer/directory.js";import{createPeerRelay as Ge}from"./peer/relay-service.js";import{writeFileSync as Oe,readFileSync as Be,rmSync as He,mkdirSync as se,existsSync as k,chmodSync as je}from"node:fs";import{createServer as ce}from"node:http";import{dirname as qe}from"node:path";import{envCrtrdTcp as ze,envDocumentPort as We}from"../shared/env.js";import{serveCanvasConversationStore as Ue}from"../core/canvas/sessions.js";import{pollRunObservers as Ke}from"./runs/observer.js";import{randomBytes as de}from"node:crypto";import{apiSocketPath as Je,crtrHome as Ve}from"../core/canvas/paths.js";import{activeAppUsers as Qe}from"../core/spaces/users.js";import{ensureAppSpace as Ye,ensureUserFiles as Xe}from"../core/spaces/space.js";import{LAUNCHER_SOCKET as x}from"../launcher/spec.js";import{launcherRequest as C}from"../launcher/client.js";import{installLauncherEventSink as Ze}from"./launcher-events.js";import{initializeEmptyLayout as er,layoutNeedsMigration as rr,socketDir as le}from"../core/layout.js";import{migrateLooseKeys as tr}from"../core/layout-migrate/loose-keys.js";import{relocateProfileFolders as nr}from"../core/profiles/relocate.js";import{getNode as or,listNodesForSupervision as pe,listParkedResidentsWithWakeMail as ir,setFrozen as me}from"../core/canvas/index.js";import{createApiServer as ar}from"./api/server.js";import{createDocumentListener as sr}from"./api/document-listener.js";import{documentSecret as cr}from"./api/document-secret.js";import{createAppListener as dr}from"./api/app-listener.js";import{registerMachineEnvironment as lr}from"../core/environments.js";import{resolvePerson as pr,retireLocalPerson as mr,setPersonId as fr}from"../core/canvas/person.js";import{backfillNodeApps as ur,runPermissionsBoot as wr}from"../core/permissions/boot.js";import{onRunEventAppended as hr}from"../core/runs/events.js";import{runNotices as vr}from"./run-notices.js";import{createGrantSync as gr,GRANT_SYNC_INTERVAL_S as yr}from"../core/grants/sync.js";import{runRemovalCascades as Sr}from"../core/grants/remove.js";import{sweepSandboxes as _r}from"../core/grants/on-sync.js";import{rebuildCronExecutions as br}from"./cron/run-socket.js";import{hasSyncedGrants as kr}from"../core/grants/mirror.js";import{readConfig as fe}from"../core/config.js";import{reconcileUndeliveredTickets as Pr}from"./human/sweep.js";import{resetHandedOffDeliveries as Er}from"../core/canvas/deliveries.js";import{resetHandedOffDaemonActions as Rr}from"../core/canvas/daemon-actions.js";import{sweepReviews as Ir}from"./review/sweep.js";import{pidfilePath as K}from"./pidfile.js";import{rendererWarning as xr}from"../core/runtime/package-health.js";import{DEFAULT_INTERVAL_MS as Dr}from"./supervise-cadence.js";import{createResumeDetector as Tr}from"./resume-detector.js";import{setFatalFaultAnnouncer as Cr}from"../core/canvas/recovery.js";import{BrokerSupervisionReconciler as Lr}from"./reconcilers/broker-supervision.js";import{ControllerDeathReconciler as $r}from"./reconcilers/controller-death.js";import{NodeLifecycleReconciler as Ar}from"./reconcilers/node-lifecycle/tick.js";import{NodeDeadlineReconciler as Mr}from"./reconcilers/node-deadline.js";import{CronLaneReconciler as Nr}from"./reconcilers/cron-lane.js";import{DeliveryLaneReconciler as Fr}from"./reconcilers/delivery-lane.js";import{ActionLaneReconciler as Gr}from"./reconcilers/action-lane.js";import{endLiveExecution as Or,migrateLegacyExecutionIdentities as Br}from"../core/canvas/executions.js";import{revokePriorEpochNodeTokens as Hr}from"../core/canvas/node-tokens.js";import{PendingReviewSubmitReconciler as jr}from"./reconcilers/pending-review-submit.js";import{StorageMaintenanceReconciler as qr}from"./reconcilers/storage-maintenance.js";import{StorageDueWorkReconciler as zr}from"./reconcilers/storage-due-work.js";import{BashDeadlineReconciler as Wr}from"./reconcilers/bash-deadline.js";import{FaultRetryBackstopReconciler as Ur}from"./reconcilers/fault-retry-backstop.js";import{StructuredRequestReconciler as Kr}from"./reconcilers/structured-request-lane.js";import{reconcileStructuredRequestSidecars as Jr}from"./structured-requests/boot.js";import{captureLivenessSnapshot as ue,capturePidCommand as Vr,captureTeardownSnapshot as we,isPidAlive as Qr,isAnyPidAlive as he,killProcessTreePids as ve}from"../core/canvas/pid.js";import{bindFleet as Yr,boundFleet as Xr}from"../core/runtime/fleet.js";import{bindDaemonControl as Zr,clearDaemonControl as et}from"./control.js";import{execDaemon as rt,spawnDaemon as tt}from"./manage.js";import{reconcileAndMigrateBoot as nt}from"../core/canvas/boot.js";import{resolveInstallId as ot}from"../core/canvas/install-id.js";import{setInstallId as it}from"../core/runtime/nodes.js";import{claimDaemonOwnership as ge,daemonOwnershipClaimed as at,releaseDaemonOwnership as ye}from"../core/canvas/daemon-owner.js";import{canvasDatabaseUrl as st,closeAllDbs as J,openDb as Se,withCanvasWrite as ct}from"../core/canvas/db.js";import{importSqliteCanvasIfPresent as dt}from"../core/canvas/import-sqlite.js";import{ensurePostgres as lt,ensurePostgresHealthy as pt,stopPostgres as mt,withPostgresStartLock as ft}from"./postgres/index.js";import{brokerSignalBus as V}from"./broker-signals/bus.js";import{brokerSignalStreams as ut}from"./api/handlers/broker-signals.js";import{reconcilePackages as wt}from"../core/graph/package-reconcile.js";import{startRepoSync as ht}from"../core/graph/repo-sync/index.js";import{prewarmRecentRecipes as vt,reapStaleSpares as gt}from"../core/runtime/warm-pool.js";import{DaemonFleet as yt}from"./fleet.js";import{BOOT_MIGRATIONS as St}from"./boot-migrations.js";import{emitEvent as m}from"../core/events/emit.js";import{operationIdContext as _}from"../core/events/operation-id.js";import{bindDaemonEventSource as _t}from"../core/events/source.js";import{activateCanvasImport as bt,MigrationBlockedError as kt,runForcedCanvasImport as Pt}from"../migrations/activation.js";import{convertLegacyProviderStore as Et,convertLegacyScopedStore as Rt}from"../core/scoped-state/migrate.js";import{userScopeRoot as It}from"../core/scope.js";import{secretsDir as xt}from"../core/layout.js";function Q(n,s){return n.includes(` --canvas-home ${s} `)}i(Q,"brokerCommandDeclaresCanvasHome");function Y(n){return/ --epoch (\S+) /.exec(n)?.[1]??null}i(Y,"brokerCommandEpoch");const Dt=5e3,_e=150;async function Tt(n){const s=ue();if(s===null)throw m({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness"}}),new Error("prior-epoch broker census could not capture the process table");const l=Ve(),w=[];for(const[a,u]of s.processes){const y=u.command;if(u.zombie||!y.includes("broker-cli.js")||!Q(y,l)||Y(y)===n)continue;const P=y.trim().split(/\s+/).at(-1)??"";P===""||P.endsWith("broker-cli.js")||w.push({pid:a,nodeId:P})}let d=0;const t=[];for(const a of w){const u=Vr(a.pid);if(u===null||!u.includes("broker-cli.js")||!Q(u,l)||Y(u)===n||u.trim().split(/\s+/).at(-1)!==a.nodeId)continue;const y=we(a.pid,null);if(!y.reused){try{process.platform==="linux"&&k(x)?await C({operation:"stop",pid:a.pid}):process.kill(a.pid,"SIGTERM")}catch{continue}d++,t.push({pid:a.pid,snap:y})}}const v=Date.now()+Dt;for(;Date.now()<v&&t.some(a=>he(a.snap.tree.length>0?a.snap.tree:[a.pid]));)await new Promise(a=>setTimeout(a,_e));let h=0;for(const a of t){const u=a.snap.tree.length>0?a.snap.tree:[a.pid];he(u)&&(process.platform==="linux"&&k(x)?await C({operation:"stop",pid:a.pid}):ve(u,"SIGKILL",a.snap.identities),h++)}h>0&&await new Promise(a=>setTimeout(a,_e));const f=ue();if(f===null)throw m({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness",stage:"final"}}),new Error("prior-epoch broker census could not capture the final process table");const c=[];for(const[a,u]of f.processes){const y=u.command;u.zombie||!y.includes("broker-cli.js")||Q(y,l)&&Y(y)!==n&&c.push(a)}if(c.length>0)throw new Error(`prior-epoch broker census could not prove termination of pid(s): ${c.join(", ")}`);m({level:"info",event:"daemon.census.completed",fields:{prior_epoch_brokers:w.length,sigtermed:d,sigkilled:h}})}i(Tt,"reapPriorEpochBrokers");const Ct=5e3;async function Lt(n){const s=Array.from(n.entries());if(s.length===0)return;for(const[t]of s){const v=(await or(t))?.status;(v==="active"||v==="idle")&&await me(t,new Date().toISOString())}const l=s.map(([,t])=>({pid:t.pid,snap:we(t.pid,null),exited:t.handle.exited}));for(const t of l)if(!t.snap.reused)try{process.platform==="linux"&&k(x)?await C({operation:"stop",pid:t.pid}):process.kill(t.pid,"SIGTERM")}catch{}const w=new Promise(t=>{setTimeout(t,Ct).unref()});await Promise.allSettled(l.map(t=>Promise.race([t.exited.then(()=>{}),w])));const d=[];for(const t of l)Qr(t.pid)&&(process.platform==="linux"&&k(x)?await C({operation:"stop",pid:t.pid}):ve(t.snap.tree.length>0?t.snap.tree:[t.pid],"SIGKILL",t.snap.identities),d.push(t));await Promise.all(d.map(t=>t.exited));for(const[t]of s)await Or(t,{reason:"daemon-teardown"});m({level:"info",event:"daemon.fleet_teardown.completed",fields:{brokers:l.length,sigkilled:d.length}})}i(Lt,"teardownFleet");function be(){se(qe(K()),{recursive:!0}),Oe(K(),String(process.pid),"utf8")}i(be,"writePidfile");function ke(n){return typeof n=="object"&&n!==null&&n.code==="ENOENT"}i(ke,"isMissingPathError");function Pe(n){const s=K();let l;try{l=Be(s,"utf8")}catch(w){if(ke(w))return;throw w}if(l.trim()!==String(n))throw new Error(`daemon readiness mirror does not name this invocation (expected pid ${n})`);try{He(s)}catch(w){if(ke(w))return;throw w}}i(Pe,"removeOwnedPidfileMirror");const $t={acceptsDetachedWork:i(()=>!0,"acceptsDetachedWork"),registerDetached:i(n=>{n.catch(()=>{})},"registerDetached")},H=new Lr;Cr(n=>H.handleFatalFault(n,!1));const At=new $r,Mt=new Ar,Nt=new Mr,Ft=new Nr,Gt=new Fr,Ot=new Gr,Bt=new jr,Ht=new qr,jt=new zr,qt=new Wr,zt=new Ur,Wt=new Kr;async function Ut(n=Date.now(),s=$t){const l=Xr(),w=await H.runFleetPreamble(l);let d,t;try{const h=await pe();d=h.filter(c=>c.status==="active"||c.status==="idle"),t=h.filter(c=>c.status==="done"||c.status==="canceled"||c.status==="dead");const f=new Set(t.map(c=>c.node_id));t.push(...(await ir()).filter(c=>!f.has(c.node_id)))}catch(h){m({level:"error",event:"canvas.node_list.failed",error:h});return}await H.run(n,{rows:d,fleet:l,lifecycle:s}),await At.run(n,{rows:d});const v=await Mt.run(n,{rows:[...d,...t],fleet:l});await Nt.run(n),await Wt.run(n),await Ft.run(n,{lifecycle:s}),await Gt.run(n,{lifecycle:s}),await Ot.run(n,{lifecycle:s}),await Bt.run(n,{lifecycle:s}),await Ht.run(n,{lifecycle:s,fleet:l}),jt.run(n,{lifecycle:s}),await Ke(),zt.run(n,{fleet:l,lifecycle:s}),await qt.run(n,{rows:d}),H.warnIfSaturated({fleet:l,lifecycle:s,thresholds:w,frozen:v})}i(Ut,"superviseTick");async function Kt(n){se(le(),{recursive:!0,mode:493}),process.platform==="linux"&&je(le(),493);const s=Je();if(k(s))throw new Error(`layout refusal cannot bind occupied socket: ${s}`);const l=ce((f,c)=>{if(f.method==="GET"&&f.url==="/healthz"){c.writeHead(200,{"content-type":"application/json; charset=utf-8"}),c.end(JSON.stringify({ok:!1,daemon_up:!0,store_accessible:!1,brokers_reconciled:!1,startup_phase:"blocked",startup_blocked:"layout_migration_required"}));return}c.writeHead(503,{"content-type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:{code:"layout_migration_required",message:"run crtr sys migrate while the runtime is offline",type:"server_error",origin:"runtime",retryable:!1}}))});await new Promise((f,c)=>{l.once("error",c),l.listen(s,()=>{l.off("error",c),f()})});let w;const d=n.tcp??ze();if(d){const f=/^(.*):(\d+)$/.exec(d);if(!f||Number(f[2])>65535)throw new Error(`invalid refusal TCP address: ${d}`);const c=ce(l.listeners("request")[0]);await new Promise((a,u)=>{c.once("error",u),c.listen(Number(f[2]),f[1],()=>{c.off("error",u),a()})}),w=c}be();let t;const v=i(()=>(t??=(async()=>{l.closeAllConnections(),w?.closeAllConnections(),await Promise.all([l,w].filter(f=>f!==void 0).map(f=>new Promise((c,a)=>f.close(u=>u?a(u):c())))),Pe(process.pid),process.off("SIGINT",h),process.off("SIGTERM",h)})(),t),"cleanup"),h=i(()=>{v().then(()=>process.exit(0),f=>{process.stderr.write(`[crtrd] layout refusal cleanup failed: ${String(f)}
2
- `),process.exit(1)})},"onSignal");return process.on("SIGINT",h),process.on("SIGTERM",h),v}i(Kt,"serveLayoutRefusal");async function Jt(n={}){if(rr())return Kt(n);er();const s=i(r=>{process.stderr.write(`[crtrd] ${r}
3
- `)},"logPostgres"),l=!!process.env.CRTR_TEST_PG_URL,{database:w,ownership:d}=l?{database:{url:await st()},ownership:await ge(de(16).toString("hex"))}:await ft(async()=>({database:await lt({log:s,startLockHeld:!0}),ownership:await ge(de(16).toString("hex"))}));if(!d.claimed){if(d.reason==="incumbent_alive")process.stderr.write(`[crtrd] already running (pid ${d.incumbentPid??"?"})
1
+ var Ae=Object.defineProperty;var i=(n,s)=>Ae(n,"name",{value:s,configurable:!0});import{startPeerOutbox as Me}from"./peer/outbox.js";import{reportMachineStatus as Ne}from"../core/peer/directory.js";import{createPeerRelay as Fe}from"./peer/relay-service.js";import{writeFileSync as Ge,readFileSync as Oe,rmSync as Be,mkdirSync as se,existsSync as k,chmodSync as He}from"node:fs";import{createServer as ce}from"node:http";import{dirname as je}from"node:path";import{envCrtrdTcp as qe,envDocumentPort as ze}from"../shared/env.js";import{serveCanvasConversationStore as We}from"../core/canvas/sessions.js";import{pollRunObservers as Ue}from"./runs/observer.js";import{randomBytes as de}from"node:crypto";import{apiSocketPath as Ke,crtrHome as Je}from"../core/canvas/paths.js";import{activeAppUsers as Ve}from"../core/spaces/users.js";import{ensureAppSpace as Qe,ensureUserFiles as Ye}from"../core/spaces/space.js";import{LAUNCHER_SOCKET as x}from"../launcher/spec.js";import{launcherRequest as C}from"../launcher/client.js";import{installLauncherEventSink as Xe}from"./launcher-events.js";import{initializeEmptyLayout as Ze,layoutNeedsMigration as er,socketDir as le}from"../core/layout.js";import{migrateLooseKeys as rr}from"../core/layout-migrate/loose-keys.js";import{relocateProfileFolders as tr}from"../core/profiles/relocate.js";import{getNode as nr,listNodesForSupervision as pe,listParkedResidentsWithWakeMail as or,setFrozen as me}from"../core/canvas/index.js";import{createApiServer as ir}from"./api/server.js";import{createDocumentListener as ar}from"./api/document-listener.js";import{documentSecret as sr}from"./api/document-secret.js";import{createAppListener as cr}from"./api/app-listener.js";import{registerMachineEnvironment as dr}from"../core/environments.js";import{resolvePerson as lr,retireLocalPerson as pr,setPersonId as mr}from"../core/canvas/person.js";import{backfillNodeApps as fr,runPermissionsBoot as ur}from"../core/permissions/boot.js";import{onRunEventAppended as wr}from"../core/runs/events.js";import{runNotices as hr}from"./run-notices.js";import{createGrantSync as vr,GRANT_SYNC_INTERVAL_S as gr}from"../core/grants/sync.js";import{runRemovalCascades as yr}from"../core/grants/remove.js";import{sweepSandboxes as Sr}from"../core/grants/on-sync.js";import{rebuildCronExecutions as _r}from"./cron/run-socket.js";import{hasSyncedGrants as br}from"../core/grants/mirror.js";import{readConfig as fe}from"../core/config.js";import{reconcileUndeliveredTickets as kr}from"./human/sweep.js";import{resetHandedOffDeliveries as Pr}from"../core/canvas/deliveries.js";import{resetHandedOffDaemonActions as Er}from"../core/canvas/daemon-actions.js";import{sweepReviews as Rr}from"./review/sweep.js";import{pidfilePath as K}from"./pidfile.js";import{rendererWarning as Ir}from"../core/runtime/package-health.js";import{DEFAULT_INTERVAL_MS as xr}from"./supervise-cadence.js";import{createResumeDetector as Dr}from"./resume-detector.js";import{setFatalFaultAnnouncer as Tr}from"../core/canvas/recovery.js";import{BrokerSupervisionReconciler as Cr}from"./reconcilers/broker-supervision.js";import{ControllerDeathReconciler as Lr}from"./reconcilers/controller-death.js";import{NodeLifecycleReconciler as $r}from"./reconcilers/node-lifecycle/tick.js";import{NodeDeadlineReconciler as Ar}from"./reconcilers/node-deadline.js";import{CronLaneReconciler as Mr}from"./reconcilers/cron-lane.js";import{DeliveryLaneReconciler as Nr}from"./reconcilers/delivery-lane.js";import{ActionLaneReconciler as Fr}from"./reconcilers/action-lane.js";import{endLiveExecution as Gr,migrateLegacyExecutionIdentities as Or}from"../core/canvas/executions.js";import{revokePriorEpochNodeTokens as Br}from"../core/canvas/node-tokens.js";import{PendingReviewSubmitReconciler as Hr}from"./reconcilers/pending-review-submit.js";import{StorageMaintenanceReconciler as jr}from"./reconcilers/storage-maintenance.js";import{StorageDueWorkReconciler as qr}from"./reconcilers/storage-due-work.js";import{BashDeadlineReconciler as zr}from"./reconcilers/bash-deadline.js";import{FaultRetryBackstopReconciler as Wr}from"./reconcilers/fault-retry-backstop.js";import{StructuredRequestReconciler as Ur}from"./reconcilers/structured-request-lane.js";import{reconcileStructuredRequestSidecars as Kr}from"./structured-requests/boot.js";import{captureLivenessSnapshot as ue,capturePidCommand as Jr,captureTeardownSnapshot as we,isPidAlive as Vr,isAnyPidAlive as he,killProcessTreePids as ve}from"../core/canvas/pid.js";import{bindFleet as Qr,boundFleet as Yr}from"../core/runtime/fleet.js";import{bindDaemonControl as Xr,clearDaemonControl as Zr}from"./control.js";import{execDaemon as et,spawnDaemon as rt}from"./manage.js";import{reconcileAndMigrateBoot as tt}from"../core/canvas/boot.js";import{resolveInstallId as nt}from"../core/canvas/install-id.js";import{setInstallId as ot}from"../core/runtime/nodes.js";import{claimDaemonOwnership as ge,daemonOwnershipClaimed as it,releaseDaemonOwnership as ye}from"../core/canvas/daemon-owner.js";import{canvasDatabaseUrl as at,closeAllDbs as J,openDb as Se,withCanvasWrite as st}from"../core/canvas/db.js";import{importSqliteCanvasIfPresent as ct}from"../core/canvas/import-sqlite.js";import{ensurePostgres as dt,ensurePostgresHealthy as lt,stopPostgres as pt,withPostgresStartLock as mt}from"./postgres/index.js";import{brokerSignalBus as V}from"./broker-signals/bus.js";import{brokerSignalStreams as ft}from"./api/handlers/broker-signals.js";import{reconcilePackages as ut}from"../core/graph/package-reconcile.js";import{startRepoSync as wt}from"../core/graph/repo-sync/index.js";import{prewarmRecentRecipes as ht,reapStaleSpares as vt}from"../core/runtime/warm-pool.js";import{DaemonFleet as gt}from"./fleet.js";import{BOOT_MIGRATIONS as yt}from"./boot-migrations.js";import{emitEvent as m}from"../core/events/emit.js";import{operationIdContext as _}from"../core/events/operation-id.js";import{bindDaemonEventSource as St}from"../core/events/source.js";import{activateCanvasImport as _t,MigrationBlockedError as bt,runForcedCanvasImport as kt}from"../migrations/activation.js";import{convertLegacyProviderStore as Pt,convertLegacyScopedStore as Et}from"../core/scoped-state/migrate.js";import{userScopeRoot as Rt}from"../core/scope.js";import{secretsDir as It}from"../core/layout.js";function Q(n,s){return n.includes(` --canvas-home ${s} `)}i(Q,"brokerCommandDeclaresCanvasHome");function Y(n){return/ --epoch (\S+) /.exec(n)?.[1]??null}i(Y,"brokerCommandEpoch");const xt=5e3,_e=150;async function Dt(n){const s=ue();if(s===null)throw m({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness"}}),new Error("prior-epoch broker census could not capture the process table");const l=Je(),w=[];for(const[a,u]of s.processes){const y=u.command;if(u.zombie||!y.includes("broker-cli.js")||!Q(y,l)||Y(y)===n)continue;const P=y.trim().split(/\s+/).at(-1)??"";P===""||P.endsWith("broker-cli.js")||w.push({pid:a,nodeId:P})}let d=0;const t=[];for(const a of w){const u=Jr(a.pid);if(u===null||!u.includes("broker-cli.js")||!Q(u,l)||Y(u)===n||u.trim().split(/\s+/).at(-1)!==a.nodeId)continue;const y=we(a.pid,null);if(!y.reused){try{process.platform==="linux"&&k(x)?await C({operation:"stop",pid:a.pid}):process.kill(a.pid,"SIGTERM")}catch{continue}d++,t.push({pid:a.pid,snap:y})}}const g=Date.now()+xt;for(;Date.now()<g&&t.some(a=>he(a.snap.tree.length>0?a.snap.tree:[a.pid]));)await new Promise(a=>setTimeout(a,_e));let h=0;for(const a of t){const u=a.snap.tree.length>0?a.snap.tree:[a.pid];he(u)&&(process.platform==="linux"&&k(x)?await C({operation:"stop",pid:a.pid}):ve(u,"SIGKILL",a.snap.identities),h++)}h>0&&await new Promise(a=>setTimeout(a,_e));const f=ue();if(f===null)throw m({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness",stage:"final"}}),new Error("prior-epoch broker census could not capture the final process table");const c=[];for(const[a,u]of f.processes){const y=u.command;u.zombie||!y.includes("broker-cli.js")||Q(y,l)&&Y(y)!==n&&c.push(a)}if(c.length>0)throw new Error(`prior-epoch broker census could not prove termination of pid(s): ${c.join(", ")}`);m({level:"info",event:"daemon.census.completed",fields:{prior_epoch_brokers:w.length,sigtermed:d,sigkilled:h}})}i(Dt,"reapPriorEpochBrokers");const Tt=5e3;async function Ct(n){const s=Array.from(n.entries());if(s.length===0)return;for(const[t]of s){const g=(await nr(t))?.status;(g==="active"||g==="idle")&&await me(t,new Date().toISOString())}const l=s.map(([,t])=>({pid:t.pid,snap:we(t.pid,null),exited:t.handle.exited}));for(const t of l)if(!t.snap.reused)try{process.platform==="linux"&&k(x)?await C({operation:"stop",pid:t.pid}):process.kill(t.pid,"SIGTERM")}catch{}const w=new Promise(t=>{setTimeout(t,Tt).unref()});await Promise.allSettled(l.map(t=>Promise.race([t.exited.then(()=>{}),w])));const d=[];for(const t of l)Vr(t.pid)&&(process.platform==="linux"&&k(x)?await C({operation:"stop",pid:t.pid}):ve(t.snap.tree.length>0?t.snap.tree:[t.pid],"SIGKILL",t.snap.identities),d.push(t));await Promise.all(d.map(t=>t.exited));for(const[t]of s)await Gr(t,{reason:"daemon-teardown"});m({level:"info",event:"daemon.fleet_teardown.completed",fields:{brokers:l.length,sigkilled:d.length}})}i(Ct,"teardownFleet");function be(){se(je(K()),{recursive:!0}),Ge(K(),String(process.pid),"utf8")}i(be,"writePidfile");function ke(n){return typeof n=="object"&&n!==null&&n.code==="ENOENT"}i(ke,"isMissingPathError");function Pe(n){const s=K();let l;try{l=Oe(s,"utf8")}catch(w){if(ke(w))return;throw w}if(l.trim()!==String(n))throw new Error(`daemon readiness mirror does not name this invocation (expected pid ${n})`);try{Be(s)}catch(w){if(ke(w))return;throw w}}i(Pe,"removeOwnedPidfileMirror");const Lt={acceptsDetachedWork:i(()=>!0,"acceptsDetachedWork"),registerDetached:i(n=>{n.catch(()=>{})},"registerDetached")},H=new Cr;Tr(n=>H.handleFatalFault(n,!1));const $t=new Lr,At=new $r,Mt=new Ar,Nt=new Mr,Ft=new Nr,Gt=new Fr,Ot=new Hr,Bt=new jr,Ht=new qr,jt=new zr,qt=new Wr,zt=new Ur;async function Wt(n=Date.now(),s=Lt){const l=Yr(),w=await H.runFleetPreamble(l);let d,t;try{const h=await pe();d=h.filter(c=>c.status==="active"||c.status==="idle"),t=h.filter(c=>c.status==="done"||c.status==="canceled"||c.status==="dead");const f=new Set(t.map(c=>c.node_id));t.push(...(await or()).filter(c=>!f.has(c.node_id)))}catch(h){m({level:"error",event:"canvas.node_list.failed",error:h});return}await H.run(n,{rows:d,fleet:l,lifecycle:s}),await $t.run(n,{rows:d});const g=await At.run(n,{rows:[...d,...t],fleet:l});await Mt.run(n),await zt.run(n),await Nt.run(n,{lifecycle:s}),await Ft.run(n,{lifecycle:s}),await Gt.run(n,{lifecycle:s}),await Ot.run(n,{lifecycle:s}),await Bt.run(n,{lifecycle:s,fleet:l}),Ht.run(n,{lifecycle:s}),await Ue(),qt.run(n,{fleet:l,lifecycle:s}),await jt.run(n,{rows:d}),H.warnIfSaturated({fleet:l,lifecycle:s,thresholds:w,frozen:g})}i(Wt,"superviseTick");async function Ut(n){se(le(),{recursive:!0,mode:493}),process.platform==="linux"&&He(le(),493);const s=Ke();if(k(s))throw new Error(`layout refusal cannot bind occupied socket: ${s}`);const l=ce((f,c)=>{if(f.method==="GET"&&f.url==="/healthz"){c.writeHead(200,{"content-type":"application/json; charset=utf-8"}),c.end(JSON.stringify({ok:!1,daemon_up:!0,store_accessible:!1,brokers_reconciled:!1,startup_phase:"blocked",startup_blocked:"layout_migration_required"}));return}c.writeHead(503,{"content-type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:{code:"layout_migration_required",message:"run crtr sys migrate while the runtime is offline",type:"server_error",origin:"runtime",retryable:!1}}))});await new Promise((f,c)=>{l.once("error",c),l.listen(s,()=>{l.off("error",c),f()})});let w;const d=n.tcp??qe();if(d){const f=/^(.*):(\d+)$/.exec(d);if(!f||Number(f[2])>65535)throw new Error(`invalid refusal TCP address: ${d}`);const c=ce(l.listeners("request")[0]);await new Promise((a,u)=>{c.once("error",u),c.listen(Number(f[2]),f[1],()=>{c.off("error",u),a()})}),w=c}be();let t;const g=i(()=>(t??=(async()=>{l.closeAllConnections(),w?.closeAllConnections(),await Promise.all([l,w].filter(f=>f!==void 0).map(f=>new Promise((c,a)=>f.close(u=>u?a(u):c())))),Pe(process.pid),process.off("SIGINT",h),process.off("SIGTERM",h)})(),t),"cleanup"),h=i(()=>{g().then(()=>process.exit(0),f=>{process.stderr.write(`[crtrd] layout refusal cleanup failed: ${String(f)}
2
+ `),process.exit(1)})},"onSignal");return process.on("SIGINT",h),process.on("SIGTERM",h),g}i(Ut,"serveLayoutRefusal");async function Kt(n={}){if(er())return Ut(n);Ze();const s=i(r=>{process.stderr.write(`[crtrd] ${r}
3
+ `)},"logPostgres"),l=!!process.env.CRTR_TEST_PG_URL,{database:w,ownership:d}=l?{database:{url:await at()},ownership:await ge(de(16).toString("hex"))}:await mt(async()=>({database:await dt({log:s,startLockHeld:!0}),ownership:await ge(de(16).toString("hex"))}));if(!d.claimed){if(d.reason==="incumbent_alive")process.stderr.write(`[crtrd] already running (pid ${d.incumbentPid??"?"})
4
4
  `);else{const r=[d.incumbentPid!==void 0?`pid ${d.incumbentPid}`:void 0,d.verdict!==void 0?`verdict ${d.verdict}`:void 0].filter(S=>S!==void 0),e=r.length>0?` (${r.join(", ")})`:"";process.stderr.write(`[crtrd] claim lost: ${d.reason}${e}
5
- `),d.reason==="own_identity_indeterminate"&&process.stderr.write("[crtrd] cannot read its own process identity. crouter needs /proc on Linux, or the `ps` command elsewhere (Debian/Ubuntu: apt-get install procps).\n"),(d.reason==="own_identity_indeterminate"||d.reason==="incumbent_malformed")&&(process.exitCode=1)}return async()=>{}}const t=d.claim;let v=null,h=null,f=null,c="";try{await Tt(t.token),Rt({kind:"user",root:It()});for(const e of nr())process.stderr.write(`[crtrd] ${e}
6
- `);if(process.platform==="darwin"){const{readConfig:e,updateConfig:S}=await import("../core/config.js");if(e("user").paths?.user_files===void 0){const{homedir:o}=await import("node:os");S("user",p=>{p.paths??={},p.paths.user_files??=o()})}}await Et(xt());for(const e of tr())process.stderr.write(`[crtrd] ${e}
7
- `);await Se(),await dt(w.url,s),await V.listen(w.url);const r=await ct(async e=>(await ur(e),await wt(e)));for(const e of r.conflicts)process.stderr.write(`[crtrd] plugin documents not reconciled: ${e.message}
5
+ `),d.reason==="own_identity_indeterminate"&&process.stderr.write("[crtrd] cannot read its own process identity. crouter needs /proc on Linux, or the `ps` command elsewhere (Debian/Ubuntu: apt-get install procps).\n"),(d.reason==="own_identity_indeterminate"||d.reason==="incumbent_malformed")&&(process.exitCode=1)}return async()=>{}}const t=d.claim;let g=null,h=null,f=null,c="";try{await Dt(t.token),Et({kind:"user",root:Rt()});for(const e of tr())process.stderr.write(`[crtrd] ${e}
6
+ `);if(process.platform==="darwin"){const{readConfig:e,updateConfig:S}=await import("../core/config.js");if(e("user").paths?.user_files===void 0){const{homedir:o}=await import("node:os");S("user",p=>{p.paths??={},p.paths.user_files??=o()})}}await Pt(It());for(const e of rr())process.stderr.write(`[crtrd] ${e}
7
+ `);await Se(),await ct(w.url,s),await V.listen(w.url);const r=await st(async e=>(await fr(e),await ut(e)));for(const e of r.conflicts)process.stderr.write(`[crtrd] plugin documents not reconciled: ${e.message}
8
8
  `);for(const e of r.invalid)process.stderr.write(`[crtrd] package file is not a document: ${e.path}: ${e.error}
9
9
  `);for(const e of r.quarantined)process.stderr.write(`[crtrd] plugin "${e.name}" has a damaged ${e.file} (${e.reason}); ${e.movedTo!==void 0?`moved to ${e.movedTo}`:"left in place and skipped"}
10
10
  `);for(const e of r.kinds.refused)process.stderr.write(`[crtrd] plugin "${e.plugin}" kind ${e.kind} not registered: ${e.message}
11
11
  `);for(const e of r.kinds.invalid)process.stderr.write(`[crtrd] plugin "${e.plugin}" declares invalid object kinds or scopes; no kinds registered and no app token: ${e.error}
12
- `);c=await ot(),it(c),v=await pr(),v!==null&&(fr(v.id),h=await mr(),f=await wr(),await Me(v.id)),process.platform==="linux"&&k(x)&&(Ze(),await C({operation:"users.sync",users:await Qe()})),await Hr(t.token)}catch(r){await V.close();try{const{released:e}=await ye(t);if(!e)throw new Error("daemon ownership claim was not released");await J()}catch(e){throw new AggregateError([r,e],"daemon startup failed and cleanup also failed")}throw r}let a=!0,u,y=Promise.resolve(),P=!1,N,D;const F=new Set;let j=!1,X=!1,E,G,q,Z=!1,O,L,$,A,z;const Ee=Tr();let R,g={phase:"initializing",message:"daemon startup is running the canvas import and normal services are unavailable",blockerPaths:[]},T="initial startup activation",Re=null;function Ie(){return g.phase==="ready"?null:{phase:g.phase,message:g.message,blockerPaths:g.blockerPaths}}i(Ie,"startupBlock");function b(r){F.add(r),r.finally(()=>{F.delete(r)}).catch(()=>{})}i(b,"registerDetached");const ee={acceptsDetachedWork:i(()=>a,"acceptsDetachedWork"),registerDetached:b};await ht(ee,Se);async function xe(){try{a=!1,ut.close(),await V.close(),N!==void 0&&(clearInterval(N),N=void 0),$!==void 0&&(clearInterval($),$=void 0),z?.(),z=void 0;let r;do r=y,await r;while(r!==y);for(;F.size>0;)await Promise.allSettled([...F]);if(D!==void 0&&(await Lt(D),await D.exitsSettled(),D.dispose()),q!==void 0&&await q.close(),G!==void 0&&!Z&&(Z=!0,await G.close()),A!==void 0){const e=A;A=void 0,await e.stop()}if(L!==void 0){const e=L;L=void 0,await e.close()}if(O!==void 0){const e=O;O=void 0,await e.close()}if(j||(Pe(process.pid),j=!0),j&&!X){const{released:e}=await ye(t);if(!e)throw new Error("daemon ownership claim was not released");X=!0,await J()}}finally{et(),process.off("SIGINT",ne),process.off("SIGTERM",oe),process.off("exit",ie)}}i(xe,"cleanupAttempt");const B=1e3;let re=!1;function De(){return re?{graceMs:B}:(re=!0,_.fresh(()=>{m({level:"info",event:"daemon.handover.armed",fields:{pid:process.pid,grace_ms:B}})}),setTimeout(()=>{Te()},B),{graceMs:B})}i(De,"requestHandover");async function Te(){try{await I()}catch(r){const e=r instanceof Error?r.stack??r.message:String(r);process.stderr.write(`[crtrd] handover cleanup failed:
12
+ `);c=await nt(),ot(c),g=await lr(),g!==null&&(mr(g.id),h=await pr(),f=await ur()),process.platform==="linux"&&k(x)&&(Xe(),await C({operation:"users.sync",users:await Ve()})),await Br(t.token)}catch(r){await V.close();try{const{released:e}=await ye(t);if(!e)throw new Error("daemon ownership claim was not released");await J()}catch(e){throw new AggregateError([r,e],"daemon startup failed and cleanup also failed")}throw r}let a=!0,u,y=Promise.resolve(),P=!1,N,D;const F=new Set;let j=!1,X=!1,E,G,q,Z=!1,O,L,$,A,z;const Ee=Dr();let R,v={phase:"initializing",message:"daemon startup is running the canvas import and normal services are unavailable",blockerPaths:[]},T="initial startup activation",Re=null;function Ie(){return v.phase==="ready"?null:{phase:v.phase,message:v.message,blockerPaths:v.blockerPaths}}i(Ie,"startupBlock");function b(r){F.add(r),r.finally(()=>{F.delete(r)}).catch(()=>{})}i(b,"registerDetached");const ee={acceptsDetachedWork:i(()=>a,"acceptsDetachedWork"),registerDetached:b};await wt(ee,Se);async function xe(){try{a=!1,ft.close(),await V.close(),N!==void 0&&(clearInterval(N),N=void 0),$!==void 0&&(clearInterval($),$=void 0),z?.(),z=void 0;let r;do r=y,await r;while(r!==y);for(;F.size>0;)await Promise.allSettled([...F]);if(D!==void 0&&(await Ct(D),await D.exitsSettled(),D.dispose()),q!==void 0&&await q.close(),G!==void 0&&!Z&&(Z=!0,await G.close()),A!==void 0){const e=A;A=void 0,await e.stop()}if(L!==void 0){const e=L;L=void 0,await e.close()}if(O!==void 0){const e=O;O=void 0,await e.close()}if(j||(Pe(process.pid),j=!0),j&&!X){const{released:e}=await ye(t);if(!e)throw new Error("daemon ownership claim was not released");X=!0,await J()}}finally{Zr(),process.off("SIGINT",ne),process.off("SIGTERM",oe),process.off("exit",ie)}}i(xe,"cleanupAttempt");const B=1e3;let re=!1;function De(){return re?{graceMs:B}:(re=!0,_.fresh(()=>{m({level:"info",event:"daemon.handover.armed",fields:{pid:process.pid,grace_ms:B}})}),setTimeout(()=>{Te()},B),{graceMs:B})}i(De,"requestHandover");async function Te(){try{await I()}catch(r){const e=r instanceof Error?r.stack??r.message:String(r);process.stderr.write(`[crtrd] handover cleanup failed:
13
13
  ${e}
14
- `),process.exit(1)}try{process.platform==="linux"&&k(x)&&(await C({operation:"daemon.handover"}),process.exit(0)),process.pid===1&&await rt();const r=await tt({waitForJoinLock:!0});_.fresh(()=>{m({level:"info",event:"daemon.handover.completed",fields:{predecessor_pid:process.pid,successor_pid:r.pid??r.existing_pid??null,spawned:r.started}})})}catch(r){_.fresh(()=>{m({level:"error",event:"daemon.handover.successor_failed",error:r})});const e=r instanceof Error?r.message:String(r);process.stderr.write(`[crtrd] handover successor failed to start: ${e}
15
- `),process.exit(1)}process.exit(0)}i(Te,"performHandover");function I(){if(E!==void 0)return E;const r=xe();return E=r,r.then(()=>{E===r&&(E=void 0)},()=>{E===r&&(E=void 0)}),r}i(I,"requestCleanup");async function te(r){try{await I(),await mt({claimed:i(async()=>{try{return await at()}finally{await J()}},"claimed")}),process.exit(0)}catch(e){process.exitCode=1;const S=e instanceof Error?e.stack??`${e.name}: ${e.message}`:String(e);process.stderr.write(`[crtrd] ${r} cleanup failed:
14
+ `),process.exit(1)}try{process.platform==="linux"&&k(x)&&(await C({operation:"daemon.handover"}),process.exit(0)),process.pid===1&&await et();const r=await rt({waitForJoinLock:!0});_.fresh(()=>{m({level:"info",event:"daemon.handover.completed",fields:{predecessor_pid:process.pid,successor_pid:r.pid??r.existing_pid??null,spawned:r.started}})})}catch(r){_.fresh(()=>{m({level:"error",event:"daemon.handover.successor_failed",error:r})});const e=r instanceof Error?r.message:String(r);process.stderr.write(`[crtrd] handover successor failed to start: ${e}
15
+ `),process.exit(1)}process.exit(0)}i(Te,"performHandover");function I(){if(E!==void 0)return E;const r=xe();return E=r,r.then(()=>{E===r&&(E=void 0)},()=>{E===r&&(E=void 0)}),r}i(I,"requestCleanup");async function te(r){try{await I(),await pt({claimed:i(async()=>{try{return await it()}finally{await J()}},"claimed")}),process.exit(0)}catch(e){process.exitCode=1;const S=e instanceof Error?e.stack??`${e.name}: ${e.message}`:String(e);process.stderr.write(`[crtrd] ${r} cleanup failed:
16
16
  ${S}
17
- `),process.exit(1)}}i(te,"handleSignal");function ne(){te("SIGINT")}i(ne,"onSigint");function oe(){te("SIGTERM")}i(oe,"onSigterm");function ie(){I().catch(()=>{})}i(ie,"onExit");async function W(r){a&&(y=y.then(r).then(()=>{},e=>{_.fresh(()=>{m({level:"error",event:"daemon.lane_job.failed",error:e})})}))}i(W,"enqueue");async function ae(){!a||P||(Ee.check()&&R&&(m({level:"info",event:"daemon.resume.detected"}),b(R().catch(()=>{}))),P=!0,await W(async()=>{if(P=!1,!!a)try{l||await pt({log:s}),await Ut(Date.now(),ee)}catch(r){_.fresh(()=>{m({level:"error",event:"daemon.tick.failed",error:r})})}}))}i(ae,"enqueueTick");let M=null;async function Ce(){if(M!==null)throw{status:500,code:"admission_failed",message:`daemon admission previously failed: ${M instanceof Error?M.message:String(M)}`};if(g.phase==="ready")return{admitted:!1,already_admitted:!0,startup_phase:"ready"};if(g.phase!=="prepared"&&g.phase!=="recovering")throw{status:409,code:"not_prepared",message:g.message};const r=g.phase==="prepared";try{return await U(),g={phase:"ready",message:"",blockerPaths:[]},r?{admitted:!0,already_admitted:!1,startup_phase:"ready"}:{admitted:!1,already_admitted:!0,startup_phase:"ready"}}catch(e){throw M=e,{status:500,code:"admission_failed",message:`daemon admission failed: ${e instanceof Error?e.message:String(e)}`}}}i(Ce,"admitStartup");async function Le(r){if(T!==null)throw{status:409,code:"migration_in_progress",message:`the canvas import is already running (${T})`};T="repair";try{const e=await Pt({dryRun:r.dryRun});if(r.dryRun||e.pending||g.phase!=="blocked")return{result:e,resumed:!1};if(n.prepare)return g={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]},{result:e,resumed:!1};try{return await U(),g={phase:"ready",message:"",blockerPaths:[]},{result:e,resumed:!0}}catch(S){const o=S instanceof Error?S.message:String(S);return setTimeout(()=>{I().catch(()=>{})},1e3),{result:e,resumed:!1,resumeError:o}}}finally{T=null}}i(Le,"repairMigrations");async function $e(){u=n.intervalMs??Dr;const r=t.token;D=new yt({epoch:r,enqueue:W}),Yr(D),g={phase:"recovering",message:"daemon startup recovery is running and normal services are unavailable",blockerPaths:[]},_.fresh(()=>{m({level:"info",event:"daemon.started",fields:{pid:process.pid,interval_ms:u}})});const e=c;_.fresh(()=>{m({level:"info",event:"daemon.install_id.resolved",fields:{install_id:e}})});const S=await lr(e);if(_.fresh(()=>{m({level:"info",event:"daemon.environment.registered",fields:{environment_id:S.environmentId,created:S.created}})}),_.fresh(()=>{let o;try{o=xr()}catch{return}o!==null&&m({level:"warn",event:"daemon.startup.warning",fields:{stage:"termrender",warning:o.slice(0,2048)}})}),await _.fresh(async()=>{await Ir(),await Pr(),await Er(Date.now()),await Rr(Date.now())}),await _.fresh(async()=>{const{reconcile:o,migrated:p}=await nt(Br);o.changed&&m({level:"info",event:"daemon.boot_change.reconciled",fields:{cleared_count:o.cleared.length,same_boot_proven:o.sameBootProven}}),p.length>0&&m({level:"info",event:"daemon.pid_identity.migrated",fields:{migrated_count:p.length}})}),await _.fresh(async()=>{for(const o of await pe())o.status!=="active"||o.pi_pid!=null||o.frozen_at!=null||o.respawn_not_before!=null||(await me(o.node_id,new Date().toISOString()),m({level:"warn",event:"daemon.startup.launch_debt_recorded",node_id:o.node_id}))}),await _.fresh(async()=>{await Jr();for(const o of St){const p=o.apply();p.changed&&m({level:"info",event:"daemon.boot_migration.applied",fields:{migration:o.id,...p.summary}})}}),b(br()),R){const o=i(()=>{b(R().catch(()=>{})),b(Fe().then(p=>A?.observe(p)).then(()=>{},p=>{m({level:"warn",event:"daemon.peer.status_failed",error:p})}))},"refresh");b(Sr().catch(()=>{})),b(_r().then(({completion:p})=>p).catch(p=>{m({level:"error",event:"daemon.grants.sweep_failed",error:p})})),o(),$=setInterval(o,yr*1e3),$.unref(),z=Ne({joined:!0,register:b})}await W(async()=>{await gt(),await vt()}),N=setInterval(()=>{ae().catch(o=>m({level:"error",event:"daemon.tick.failed",error:o}))},u),await ae()}i($e,"completeStartup");function U(){return Re??=$e().catch(r=>{throw g={phase:"initializing",message:"daemon startup recovery failed and normal services are unavailable",blockerPaths:[]},r})}i(U,"resumeStartup");try{process.on("SIGINT",ne),process.on("SIGTERM",oe),process.on("exit",ie),_t(),v!==null&&m({level:"info",event:"daemon.person.resolved",fields:{person_id:v.id,source:v.source}}),f!==null&&m({level:"info",event:"daemon.permissions.boot",fields:{...f}}),h!==null&&m({level:"info",event:"daemon.person.retired",fields:{person_id:h.person,joined_from:h.joinedFrom,retired:h.retired,columns:h.columns}}),O=await Ue();const r=t.token;Zr({epoch:r,requestHandover:De,registerDetached:b,startupBlock:Ie,admitStartup:Ce,repairMigrations:Le}),be(),G=ar({tcp:n.tcp}),await G.ready;const e=We()??fe("user").document_port,S=sr(e,cr());try{await S.ready,q=S}catch(p){m({level:"warn",event:"daemon.document_listener.bind_failed",fields:{port:e,error:p instanceof Error?p.message:String(p)}})}const o=fe("user").runtime;o?.issuer&&(R=gr(o.issuer,b),o.unsandboxed===!0&&(A=Ge({syncGrants:R})),hr(p=>vr().appended(p))),o?.unsandboxed===!0&&o.listener!==void 0&&m({level:"warn",event:"daemon.app_listener.refused_unsandboxed",fields:{reason:"runtime.unsandboxed is set; runtime.listener is ignored"}}),o!==void 0&&o.listener!==void 0&&o.unsandboxed!==!0&&(L=await dr(o,async()=>await kr(o.issuer.replace(/\/$/,"")),R),await L.ready);try{await bt()}catch(p){if(T=null,p instanceof kt)return g={phase:"blocked",message:p.message,blockerPaths:p.blockerPaths},I;throw p}process.platform==="linux"&&k(x)&&(Xe(),await Ye("app:terminal"));try{n.prepare?g={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]}:(await U(),g={phase:"ready",message:"",blockerPaths:[]})}finally{T=null}return I}catch(r){try{await I()}catch(e){throw new AggregateError([r,e],"daemon startup failed and cleanup also failed")}throw r}}i(Jt,"runDaemon");var To=Jt;export{Q as brokerCommandDeclaresCanvasHome,Y as brokerCommandEpoch,To as default,Jt as runDaemon,Ut as superviseTick,Lt as teardownFleet};
17
+ `),process.exit(1)}}i(te,"handleSignal");function ne(){te("SIGINT")}i(ne,"onSigint");function oe(){te("SIGTERM")}i(oe,"onSigterm");function ie(){I().catch(()=>{})}i(ie,"onExit");async function W(r){a&&(y=y.then(r).then(()=>{},e=>{_.fresh(()=>{m({level:"error",event:"daemon.lane_job.failed",error:e})})}))}i(W,"enqueue");async function ae(){!a||P||(Ee.check()&&R&&(m({level:"info",event:"daemon.resume.detected"}),b(R().catch(()=>{}))),P=!0,await W(async()=>{if(P=!1,!!a)try{l||await lt({log:s}),await Wt(Date.now(),ee)}catch(r){_.fresh(()=>{m({level:"error",event:"daemon.tick.failed",error:r})})}}))}i(ae,"enqueueTick");let M=null;async function Ce(){if(M!==null)throw{status:500,code:"admission_failed",message:`daemon admission previously failed: ${M instanceof Error?M.message:String(M)}`};if(v.phase==="ready")return{admitted:!1,already_admitted:!0,startup_phase:"ready"};if(v.phase!=="prepared"&&v.phase!=="recovering")throw{status:409,code:"not_prepared",message:v.message};const r=v.phase==="prepared";try{return await U(),v={phase:"ready",message:"",blockerPaths:[]},r?{admitted:!0,already_admitted:!1,startup_phase:"ready"}:{admitted:!1,already_admitted:!0,startup_phase:"ready"}}catch(e){throw M=e,{status:500,code:"admission_failed",message:`daemon admission failed: ${e instanceof Error?e.message:String(e)}`}}}i(Ce,"admitStartup");async function Le(r){if(T!==null)throw{status:409,code:"migration_in_progress",message:`the canvas import is already running (${T})`};T="repair";try{const e=await kt({dryRun:r.dryRun});if(r.dryRun||e.pending||v.phase!=="blocked")return{result:e,resumed:!1};if(n.prepare)return v={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]},{result:e,resumed:!1};try{return await U(),v={phase:"ready",message:"",blockerPaths:[]},{result:e,resumed:!0}}catch(S){const o=S instanceof Error?S.message:String(S);return setTimeout(()=>{I().catch(()=>{})},1e3),{result:e,resumed:!1,resumeError:o}}}finally{T=null}}i(Le,"repairMigrations");async function $e(){u=n.intervalMs??xr;const r=t.token;D=new gt({epoch:r,enqueue:W}),Qr(D),v={phase:"recovering",message:"daemon startup recovery is running and normal services are unavailable",blockerPaths:[]},_.fresh(()=>{m({level:"info",event:"daemon.started",fields:{pid:process.pid,interval_ms:u}})});const e=c;_.fresh(()=>{m({level:"info",event:"daemon.install_id.resolved",fields:{install_id:e}})});const S=await dr(e);if(_.fresh(()=>{m({level:"info",event:"daemon.environment.registered",fields:{environment_id:S.environmentId,created:S.created}})}),_.fresh(()=>{let o;try{o=Ir()}catch{return}o!==null&&m({level:"warn",event:"daemon.startup.warning",fields:{stage:"termrender",warning:o.slice(0,2048)}})}),await _.fresh(async()=>{await Rr(),await kr(),await Pr(Date.now()),await Er(Date.now())}),await _.fresh(async()=>{const{reconcile:o,migrated:p}=await tt(Or);o.changed&&m({level:"info",event:"daemon.boot_change.reconciled",fields:{cleared_count:o.cleared.length,same_boot_proven:o.sameBootProven}}),p.length>0&&m({level:"info",event:"daemon.pid_identity.migrated",fields:{migrated_count:p.length}})}),await _.fresh(async()=>{for(const o of await pe())o.status!=="active"||o.pi_pid!=null||o.frozen_at!=null||o.respawn_not_before!=null||(await me(o.node_id,new Date().toISOString()),m({level:"warn",event:"daemon.startup.launch_debt_recorded",node_id:o.node_id}))}),await _.fresh(async()=>{await Kr();for(const o of yt){const p=o.apply();p.changed&&m({level:"info",event:"daemon.boot_migration.applied",fields:{migration:o.id,...p.summary}})}}),b(_r()),R){const o=i(()=>{b(R().catch(()=>{})),b(Ne().then(p=>A?.observe(p)).then(()=>{},p=>{m({level:"warn",event:"daemon.peer.status_failed",error:p})}))},"refresh");b(yr().catch(()=>{})),b(Sr().then(({completion:p})=>p).catch(p=>{m({level:"error",event:"daemon.grants.sweep_failed",error:p})})),o(),$=setInterval(o,gr*1e3),$.unref(),z=Me({joined:!0,register:b})}await W(async()=>{await vt(),await ht()}),N=setInterval(()=>{ae().catch(o=>m({level:"error",event:"daemon.tick.failed",error:o}))},u),await ae()}i($e,"completeStartup");function U(){return Re??=$e().catch(r=>{throw v={phase:"initializing",message:"daemon startup recovery failed and normal services are unavailable",blockerPaths:[]},r})}i(U,"resumeStartup");try{process.on("SIGINT",ne),process.on("SIGTERM",oe),process.on("exit",ie),St(),g!==null&&m({level:"info",event:"daemon.person.resolved",fields:{person_id:g.id,source:g.source}}),f!==null&&m({level:"info",event:"daemon.permissions.boot",fields:{...f}}),h!==null&&m({level:"info",event:"daemon.person.retired",fields:{person_id:h.person,joined_from:h.joinedFrom,retired:h.retired,columns:h.columns}}),O=await We();const r=t.token;Xr({epoch:r,requestHandover:De,registerDetached:b,startupBlock:Ie,admitStartup:Ce,repairMigrations:Le}),be(),G=ir({tcp:n.tcp}),await G.ready;const e=ze()??fe("user").document_port,S=ar(e,sr());try{await S.ready,q=S}catch(p){m({level:"warn",event:"daemon.document_listener.bind_failed",fields:{port:e,error:p instanceof Error?p.message:String(p)}})}const o=fe("user").runtime;o?.issuer&&(R=vr(o.issuer,b),o.unsandboxed===!0&&(A=Fe({syncGrants:R})),wr(p=>hr().appended(p))),o?.unsandboxed===!0&&o.listener!==void 0&&m({level:"warn",event:"daemon.app_listener.refused_unsandboxed",fields:{reason:"runtime.unsandboxed is set; runtime.listener is ignored"}}),o!==void 0&&o.listener!==void 0&&o.unsandboxed!==!0&&(L=await cr(o,async()=>await br(o.issuer.replace(/\/$/,"")),R),await L.ready);try{await _t()}catch(p){if(T=null,p instanceof bt)return v={phase:"blocked",message:p.message,blockerPaths:p.blockerPaths},I;throw p}process.platform==="linux"&&k(x)&&(Ye(),await Qe("app:terminal"));try{n.prepare?v={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]}:(await U(),v={phase:"ready",message:"",blockerPaths:[]})}finally{T=null}return I}catch(r){try{await I()}catch(e){throw new AggregateError([r,e],"daemon startup failed and cleanup also failed")}throw r}}i(Kt,"runDaemon");var xo=Kt;export{Q as brokerCommandDeclaresCanvasHome,Y as brokerCommandEpoch,xo as default,Kt as runDaemon,Wt as superviseTick,Ct as teardownFleet};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/cli",
3
- "version": "0.3.555",
3
+ "version": "0.3.556",
4
4
  "description": "crtr — agent runtime with memory, plugins, and marketplaces",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -141,7 +141,7 @@
141
141
  },
142
142
  "license": "UNLICENSED",
143
143
  "dependencies": {
144
- "@north-light/identity": "0.3.555",
144
+ "@north-light/identity": "0.3.556",
145
145
  "@earendil-works/pi-agent-core": "1.1.0",
146
146
  "@earendil-works/pi-ai": "1.1.0",
147
147
  "@earendil-works/pi-coding-agent": "1.1.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/identity",
3
- "version": "0.3.555",
3
+ "version": "0.3.556",
4
4
  "description": "Northlight access-token verification and scope rules",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/runtime.lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@north-light/cli",
3
- "version": "0.3.555",
3
+ "version": "0.3.556",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@north-light/cli",
9
- "version": "0.3.555",
9
+ "version": "0.3.556",
10
10
  "hasInstallScript": true,
11
11
  "license": "UNLICENSED",
12
12
  "workspaces": [
@@ -24,7 +24,7 @@
24
24
  "@milkdown/preset-gfm": "^7.22.3",
25
25
  "@milkdown/prose": "^7.22.3",
26
26
  "@milkdown/utils": "^7.22.3",
27
- "@north-light/identity": "0.3.555",
27
+ "@north-light/identity": "0.3.556",
28
28
  "cron-parser": "^5.6.0",
29
29
  "esbuild": "^0.27.7",
30
30
  "jose": "^6.2.12",
@@ -12455,17 +12455,17 @@
12455
12455
  },
12456
12456
  "packages/crouter-api": {
12457
12457
  "name": "@north-light/api",
12458
- "version": "0.3.555",
12458
+ "version": "0.3.556",
12459
12459
  "license": "UNLICENSED"
12460
12460
  },
12461
12461
  "packages/crouter-env-docker": {
12462
12462
  "name": "@north-light/env-docker",
12463
- "version": "0.3.555",
12463
+ "version": "0.3.556",
12464
12464
  "license": "UNLICENSED"
12465
12465
  },
12466
12466
  "packages/crouter-identity": {
12467
12467
  "name": "@north-light/identity",
12468
- "version": "0.3.555",
12468
+ "version": "0.3.556",
12469
12469
  "license": "UNLICENSED",
12470
12470
  "dependencies": {
12471
12471
  "jose": "^6.2.1"
@@ -12473,21 +12473,21 @@
12473
12473
  },
12474
12474
  "packages/crouter-plugin": {
12475
12475
  "name": "@north-light/plugin",
12476
- "version": "0.3.555",
12476
+ "version": "0.3.556",
12477
12477
  "license": "UNLICENSED",
12478
12478
  "dependencies": {
12479
- "@north-light/api": "0.3.555",
12480
- "@north-light/identity": "0.3.555"
12479
+ "@north-light/api": "0.3.556",
12480
+ "@north-light/identity": "0.3.556"
12481
12481
  }
12482
12482
  },
12483
12483
  "packages/crouter-sdk": {
12484
12484
  "name": "@north-light/sdk",
12485
- "version": "0.3.555",
12485
+ "version": "0.3.556",
12486
12486
  "license": "UNLICENSED",
12487
12487
  "dependencies": {
12488
- "@north-light/api": "0.3.555",
12489
- "@north-light/identity": "0.3.555",
12490
- "@north-light/plugin": "0.3.555",
12488
+ "@north-light/api": "0.3.556",
12489
+ "@north-light/identity": "0.3.556",
12490
+ "@north-light/plugin": "0.3.556",
12491
12491
  "jose": "^6.2.1"
12492
12492
  },
12493
12493
  "bin": {
@@ -1,11 +0,0 @@
1
- export declare const LEDGER_MIGRATED_KEY = "ledger_migrated";
2
- /** The id of a migrated `usage` row `u`, as SQL: `legacy:<grantee>:<month>:<payer>:<source>:<served_model>`. */
3
- export declare const LEGACY_ID_SQL = "'legacy:' || u.grantee || ':' || u.month || ':' || u.payer || ':' || u.source || ':' || u.served_model";
4
- export interface BackfillResult {
5
- migrated: boolean;
6
- charges: number;
7
- }
8
- /** Copy `usage` into `charges` as legacy rows and rebuild `charge_totals` from them, then
9
- * verify per `(grantee, month, payer, source, served_model)` that usd and token sums equal the `usage` row's, null kept. A
10
- * second call sees the marker and does nothing. A failed verification rolls the whole thing back and throws. */
11
- export declare function backfillLedger(person: string): Promise<BackfillResult>;
@@ -1,19 +0,0 @@
1
- var E=Object.defineProperty;var t=(u,e)=>E(u,"name",{value:e,configurable:!0});import{withCanvasWrite as i}from"../canvas/db.js";import{rebuildTotals as o}from"./totals.js";const r="ledger_migrated",a="'legacy:' || u.grantee || ':' || u.month || ':' || u.payer || ':' || u.source || ':' || u.served_model";async function p(u){return await i(async e=>{if(await e.prepare("SELECT value FROM canvas_meta WHERE key = ?").get(r)!==void 0)return{migrated:!1,charges:0};const n=await e.prepare(`INSERT INTO charges (id, request, at, month, kind, what, units, usd, payer, funding, sub, grantee, provider)
2
- SELECT ${a},
3
- NULL, u.month || '-01T00:00:00.000Z', u.month,
4
- CASE WHEN u.source = 'model' THEN 'model' ELSE 'tool' END,
5
- CASE WHEN u.source = 'model' THEN u.served_model ELSE substr(u.source, 10) END,
6
- CASE WHEN u.source = 'model' THEN jsonb_build_object('input', u.input_tokens, 'output', u.output_tokens, 'cache_read', u.cached_tokens, 'cache_write', 0) END,
7
- u.usd_equivalent,
8
- CASE WHEN u.payer = 'app' THEN u.grantee ELSE ?::text END,
9
- CASE WHEN u.payer = 'app' THEN 'endpoint' WHEN u.source = 'model' THEN 'legacy' ELSE 'bill' END,
10
- ?::text, u.grantee,
11
- CASE WHEN u.source = 'model' THEN NULL ELSE 'app:' || substr(u.source, 10) END
12
- FROM usage u ON CONFLICT (id) DO NOTHING`).run(u,u);return await o(e),await s(e),await e.prepare("INSERT INTO canvas_meta (key, value) VALUES (?, ?) ON CONFLICT (key) DO UPDATE SET value = excluded.value").run(r,new Date().toISOString()),{migrated:!0,charges:n.changes}})}t(p,"backfillLedger");async function s(u){const e=await u.prepare(`SELECT u.grantee, u.month, u.payer, u.source, u.served_model FROM usage u
13
- LEFT JOIN charges c ON c.id = ${a}
14
- WHERE c.id IS NULL
15
- OR c.usd IS DISTINCT FROM u.usd_equivalent::numeric(14,6)
16
- OR (u.source = 'model' AND (COALESCE((c.units ->> 'input')::bigint, -1) <> u.input_tokens
17
- OR COALESCE((c.units ->> 'output')::bigint, -1) <> u.output_tokens
18
- OR COALESCE((c.units ->> 'cache_read')::bigint, -1) <> u.cached_tokens))
19
- LIMIT 5`).all();if(e.length>0)throw new Error(`ledger backfill verification failed; first differing usage rows: ${JSON.stringify(e)}`)}t(s,"verify");export{r as LEDGER_MIGRATED_KEY,a as LEGACY_ID_SQL,p as backfillLedger};