@north-light/cli 0.3.554 → 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.
@@ -3,8 +3,8 @@
3
3
  -- identity columns for insertion order, jsonb where JSON functions run, bytea blobs, PL/pgSQL
4
4
  -- triggers, and pg_notify triggers for the broker-signal bus.
5
5
  --
6
- -- Applied once, in one transaction, by db.ts when canvas_meta has no `schema_version` row. Every
7
- -- later change is a numbered delta file, never an edit of this file's tables once released.
6
+ -- Frozen: this file is schema version 1. schema-chain.ts applies it once, in one transaction, when canvas_meta has no
7
+ -- `schema_version` row, then each numbered delta in schema-pg/. Every later change is a delta, never an edit of this file.
8
8
  -- Types: SQLite INTEGER (64-bit) is bigint everywhere, including 0/1 flags and epoch-ms columns.
9
9
 
10
10
  -- ---------------------------------------------------------------- objects and their satellites
@@ -73,16 +73,14 @@ CREATE TABLE jobs (
73
73
  log_path text NOT NULL
74
74
  );
75
75
 
76
- -- The environments an object can run in (design: Environment module). Added after schema version 1 shipped, so
77
- -- `ensureSchema` also runs this block alone against a database that predates it; version 1 stays.
78
- -- environments:begin
76
+ -- The environments an object can run in (design: Environment module). Added after schema version 1 shipped; delta
77
+ -- 002 opens with this block's text so a version-1 database that predates it gains it.
79
78
  CREATE TABLE IF NOT EXISTS environments (
80
79
  object text PRIMARY KEY REFERENCES objects(id) ON DELETE CASCADE,
81
80
  kind text NOT NULL CHECK (kind IN ('machine')),
82
81
  machine_id text NOT NULL UNIQUE,
83
82
  last_seen_at text NOT NULL
84
83
  );
85
- -- environments:end
86
84
 
87
85
  CREATE TABLE identities (
88
86
  object text PRIMARY KEY REFERENCES objects(id) ON DELETE CASCADE,
@@ -942,11 +940,10 @@ CREATE TABLE peer_ingested_destinations (
942
940
  -- ---------------------------------------------------------------- money ledger (cycle 4)
943
941
 
944
942
  -- One `charges` row per charge, each naming its cause (`caused_by`); one `charge_totals` rollup upserted in the same
945
- -- transaction as each charge; one `limits` row shape. Added after schema version 1 shipped: `ensureSchema` runs this block
946
- -- alone against an older database (idempotent, additive; the version stays 1). It sits BEFORE the permissions block on
943
+ -- transaction as each charge; one `limits` row shape. Added after schema version 1 shipped; delta 002 opens with
944
+ -- this block's text (idempotent, additive). It sits BEFORE the permissions block on
947
945
  -- purpose: that block drops and recreates `node_rows` (`SELECT n.*`), which only carries `nodes.caused_by` when the column
948
946
  -- exists first. The whole block is one multi-statement query, so it applies entirely or not at all.
949
- -- ledger:begin
950
947
  CREATE TABLE IF NOT EXISTS charges (
951
948
  id text PRIMARY KEY,
952
949
  request text,
@@ -998,15 +995,13 @@ CREATE TABLE IF NOT EXISTS limits (
998
995
  CREATE INDEX IF NOT EXISTS limits_payer ON limits(payer);
999
996
 
1000
997
  ALTER TABLE nodes ADD COLUMN IF NOT EXISTS caused_by text;
1001
- -- ledger:end
1002
998
 
1003
999
  -- ---------------------------------------------------------------- permissions (cycle 3)
1004
1000
 
1005
1001
  -- One permission model: relation facts, app schemas, the columns the evaluator and owner tree need, the narrowed privacy
1006
1002
  -- levels, `relation_source` (facts plus each live object's, and each reaped node's, owner presented as its `parent`) and `node_rows` with `grantee` read
1007
- -- from `nodes.app`. Added after schema version 1 shipped: `ensureSchema` runs this block alone against an older database
1008
- -- (idempotent; the version stays 1). The whole block is one multi-statement query, so it applies entirely or not at all.
1009
- -- permissions:begin
1003
+ -- from `nodes.app`. Added after schema version 1 shipped; delta 002 opens with this block's text
1004
+ -- (idempotent). The whole block is one multi-statement query, so it applies entirely or not at all.
1010
1005
  CREATE TABLE IF NOT EXISTS relations (
1011
1006
  resource_type text NOT NULL,
1012
1007
  resource_id text NOT NULL,
@@ -1078,7 +1073,6 @@ SELECT n.*,
1078
1073
  LEFT JOIN executions e ON e.node_id = n.node_id AND e.state != 'exited'
1079
1074
  LEFT JOIN sessions s ON s.node_id = n.node_id AND s.unbound_at IS NULL
1080
1075
  LEFT JOIN node_faults f ON f.node_id = n.node_id AND f.active = 1;
1081
- -- permissions:end
1082
1076
 
1083
1077
  -- ---------------------------------------------------------------- triggers
1084
1078
 
@@ -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 = 1;
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=1;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};
@@ -108,8 +108,8 @@ export interface TeamRecord {
108
108
  }
109
109
  /** The `teams` a grant sync answer carried; absent (an unjoined install, an older directory) reads as none. */
110
110
  export declare function teamsOf(value: unknown): TeamRecord[];
111
- /** Replace the mirror with `grants`, the ledger's `grant:<id>` limit rows from their caps, and the `written_by = 'directory'` team
112
- * `member` facts with `teams`, in one transaction. */
111
+ /** Replace the mirror with `grants` and the `written_by = 'directory'` team `member` facts with `teams`, in one transaction. The
112
+ * ledger's `grant:<id>` limit rows follow from the `grants` trigger `crtr_grant_limit` (schema delta 002). */
113
113
  export declare function replaceGrants(grants: readonly GrantRecord[], syncedAt: string, source: string, teams?: readonly TeamRecord[]): Promise<void>;
114
114
  /** Bumped by every `replaceGrants`. A launch compiled against one generation
115
115
  * and started under another may hold mounts the new mirror no longer grants. */
@@ -1,4 +1,4 @@
1
- var m=Object.defineProperty;var t=(e,r)=>m(e,"name",{value:r,configurable:!0});import{createHash as S}from"node:crypto";import{parseScope as T}from"@north-light/identity";import{openDb as p,withCanvasWrite as w}from"../canvas/db.js";import{isUnsandboxedMachine as O}from"../runtime/unsandboxed.js";import{replaceWrittenBy as v,userSubject as R}from"../permissions/facts.js";import{GROUP_TYPE as x}from"../permissions/builtin.js";import{replaceGrantLimits as A}from"../ledger/index.js";const N=["not_started","pending","ready","failed","unavailable"],h=["ultra","strong","medium","light"];function u(e){if(!Array.isArray(e))throw new TypeError("grant scopes must be a list");return e.map(r=>{if(!r||typeof r!="object"||typeof r.scope!="string"||!["always","ask"].includes(r.mode))throw new TypeError("invalid grant scope entry");return{scope:r.scope,mode:r.mode}})}t(u,"entries");function L(e){if(!e||typeof e!="object"||Array.isArray(e))return null;const{state:r,message:n,next:i,step:o}=e;return typeof r!="string"||!N.includes(r)||n!==void 0&&typeof n!="string"||o!==void 0&&typeof o!="string"||typeof i!="string"||!i?null:{state:r,...n===void 0?{}:{message:n},next:i,...o===void 0?{}:{step:o}}}t(L,"dependencyOf");function E(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const r={};for(const[n,i]of Object.entries(e)){const o=L(i);o&&(r[n]=o)}return r}t(E,"dependenciesOf");function b(e){if(!e||typeof e!="object"||Array.isArray(e))return null;const{base_url:r,rungs:n}=e;if(typeof r!="string"||!r||!n||typeof n!="object"||Array.isArray(n))return null;const i={};for(const o of h){const s=n[o];typeof s=="string"&&s&&(i[o]=s)}return Object.keys(i).length?{base_url:r.replace(/\/+$/,""),rungs:i}:null}t(b,"inferenceEndpointOf");function l(e){return{id:e.id,runtime:e.runtime,sub:e.sub,grantee:e.grantee,rev:e.rev,scopes:u(JSON.parse(e.scopes_json)),resolved_scopes:u(JSON.parse(e.resolved_scopes_json)),spend_cap:e.spend_cap,expires_at:e.expires_at,status:e.status,revoked_at:e.revoked_at,approved_at:e.approved_at,payer:e.payer,dependencies:E(JSON.parse(e.dependencies_json)),run_notice_url:e.run_notice_url??null,inference_endpoint:e.inference_endpoint_json?b(JSON.parse(e.inference_endpoint_json)):null,synced_at:e.synced_at}}t(l,"record");function f(e){if(!e||e.status!=="active")return null;try{return j(l(e))}catch(r){if(r instanceof TypeError||r instanceof SyntaxError)return null;throw r}}t(f,"active");function d(e){return e.grantee!=="app:terminal"&&O()}t(d,"mirrorOnly");async function P(){return(await F()).filter(e=>!d(e))}t(P,"localGrants");async function F(){return(await(await p()).prepare("SELECT * FROM grants").all()).flatMap(e=>{try{return[l(e)]}catch(r){if(r instanceof TypeError||r instanceof SyntaxError)return[];throw r}})}t(F,"allGrants");function M(e){return e.expires_at!==null&&!(Date.parse(e.expires_at)>Date.now())}t(M,"grantExpired");function j(e){if(e.status!=="active"||M(e))return null;try{const r=e.resolved_scopes.map(({scope:n})=>T(n));return{...e,resolved:e.resolved_scopes.flatMap(({mode:n},i)=>n==="always"?[r[i]]:[])}}catch(r){if(r instanceof TypeError||r instanceof SyntaxError)return null;throw r}}t(j,"activeRecord");function y(e){return Array.isArray(e)?`[${e.map(y).join(",")}]`:e&&typeof e=="object"?`{${Object.keys(e).sort().filter(r=>e[r]!==void 0).map(r=>`${JSON.stringify(r)}:${y(e[r])}`).join(",")}}`:JSON.stringify(e)??"null"}t(y,"canonicalJson");function V(e){const r={rev:e.rev,resolved_scopes:e.resolved_scopes,spend_cap:e.spend_cap,payer:e.payer,dependencies:e.dependencies??{},...e.inference_endpoint?{inference_endpoint:e.inference_endpoint}:{}};return S("sha256").update(y(r)).digest("base64url").slice(0,22)}t(V,"grantVersion");async function B(e){return!!await(await p()).prepare("SELECT 1 FROM grants WHERE id = ?").get(e)}t(B,"hasGrant");async function X(e){return await(await p()).prepare("SELECT rev, status FROM grants WHERE id = ?").get(e)??null}t(X,"grantSyncState");async function Y(e,r){return await(await p()).prepare("SELECT rev, status FROM grants WHERE grantee = ? AND sub = ?").get(e,r)??null}t(Y,"grantSyncStateFor");async function C(e){const r=await(await p()).prepare("SELECT * FROM grants WHERE id = ?").get(e);if(!r)return null;try{return l(r)}catch(n){if(n instanceof TypeError||n instanceof SyntaxError)return null;throw n}}t(C,"grantRecord");async function q(e){return(e?(await C(e))?.payer:null)??"user"}t(q,"payerOfGrant");async function z(e){return f(await(await p()).prepare("SELECT * FROM grants WHERE id = ?").get(e))}t(z,"activeGrant");async function K(e,r){return f(await(await p()).prepare("SELECT * FROM grants WHERE grantee = ? AND sub = ?").get(e,r))}t(K,"grantFor");async function Q(e){if(d({grantee:e}))return null;const r=await(await p()).prepare("SELECT * FROM grants WHERE grantee = ? AND status = 'active'").all(e);if(r.length>1)throw new Error(`multiple active grants for ${e}`);return f(r[0])}t(Q,"activeGrantForApp");async function Z(e){const r=await(await p()).prepare("SELECT run_notice_url AS url, sub, grantee FROM grants WHERE id = ? AND status = 'active'").get(e);return r&&r.url&&!d(r)?{url:r.url,sub:r.sub,grantee:r.grantee}:null}t(Z,"runNoticeTarget");async function ee(){const e=await(await p()).prepare("SELECT * FROM grants WHERE grantee = 'app:terminal' AND status = 'active'").all();if(e.length>1)throw new Error("multiple active terminal grants");return f(e[0])}t(ee,"terminalGrant");async function re(){return(await(await p()).prepare("SELECT value FROM runtime_state WHERE key = 'grants_source'").get())?.value??null}t(re,"grantsSource");async function te(e){return(await(await p()).prepare("SELECT value FROM runtime_state WHERE key = 'grants_source'").get())?.value===e}t(te,"hasSyncedGrants");async function ne(e){return(await e.prepare("SELECT value FROM runtime_state WHERE key = 'grants_source'").get())?.value!=="local"?0:(await e.prepare("DELETE FROM runtime_state WHERE key = 'grants_source'").run(),Number((await e.prepare("DELETE FROM grants").run()).changes))}t(ne,"dropLocalGrantMirror");function ae(e){if(e===void 0)return[];if(!Array.isArray(e))throw new TypeError("invalid grant sync response");return e.map(r=>{const{team:n,members:i}=r??{};if(typeof n!="string"||!/^team:\S+$/.test(n)||!Array.isArray(i)||i.some(o=>typeof o!="string"||!/^user:[^\s*]+$/.test(o)))throw new TypeError("invalid grant sync response");return{team:n,members:i}})}t(ae,"teamsOf");function k(e){return e.status!=="active"||e.spend_cap===null||e.payer==="app"||e.grantee==="app:terminal"?null:{payer:e.sub,grantee:e.grantee,usd_per_month:e.spend_cap}}t(k,"grantLimit");async function se(e,r,n,i=[]){const o=new Set;for(const s of e){if(o.has(s.id)||!s.id||!s.runtime||!s.sub||!s.grantee||!Number.isInteger(s.rev)||!["active","revoked","removed"].includes(s.status))throw new TypeError("invalid grant sync response");o.add(s.id),u(s.scopes),u(s.resolved_scopes)}await w(async s=>{await s.exec("DELETE FROM grants");const _=s.prepare(`INSERT INTO grants
1
+ var m=Object.defineProperty;var t=(e,r)=>m(e,"name",{value:r,configurable:!0});import{createHash as S}from"node:crypto";import{parseScope as T}from"@north-light/identity";import{openDb as c,withCanvasWrite as O}from"../canvas/db.js";import{isUnsandboxedMachine as v}from"../runtime/unsandboxed.js";import{replaceWrittenBy as w,userSubject as x}from"../permissions/facts.js";import{GROUP_TYPE as R}from"../permissions/builtin.js";const A=["not_started","pending","ready","failed","unavailable"],N=["ultra","strong","medium","light"];function p(e){if(!Array.isArray(e))throw new TypeError("grant scopes must be a list");return e.map(r=>{if(!r||typeof r!="object"||typeof r.scope!="string"||!["always","ask"].includes(r.mode))throw new TypeError("invalid grant scope entry");return{scope:r.scope,mode:r.mode}})}t(p,"entries");function h(e){if(!e||typeof e!="object"||Array.isArray(e))return null;const{state:r,message:n,next:i,step:o}=e;return typeof r!="string"||!A.includes(r)||n!==void 0&&typeof n!="string"||o!==void 0&&typeof o!="string"||typeof i!="string"||!i?null:{state:r,...n===void 0?{}:{message:n},next:i,...o===void 0?{}:{step:o}}}t(h,"dependencyOf");function y(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const r={};for(const[n,i]of Object.entries(e)){const o=h(i);o&&(r[n]=o)}return r}t(y,"dependenciesOf");function b(e){if(!e||typeof e!="object"||Array.isArray(e))return null;const{base_url:r,rungs:n}=e;if(typeof r!="string"||!r||!n||typeof n!="object"||Array.isArray(n))return null;const i={};for(const o of N){const a=n[o];typeof a=="string"&&a&&(i[o]=a)}return Object.keys(i).length?{base_url:r.replace(/\/+$/,""),rungs:i}:null}t(b,"inferenceEndpointOf");function f(e){return{id:e.id,runtime:e.runtime,sub:e.sub,grantee:e.grantee,rev:e.rev,scopes:p(JSON.parse(e.scopes_json)),resolved_scopes:p(JSON.parse(e.resolved_scopes_json)),spend_cap:e.spend_cap,expires_at:e.expires_at,status:e.status,revoked_at:e.revoked_at,approved_at:e.approved_at,payer:e.payer,dependencies:y(JSON.parse(e.dependencies_json)),run_notice_url:e.run_notice_url??null,inference_endpoint:e.inference_endpoint_json?b(JSON.parse(e.inference_endpoint_json)):null,synced_at:e.synced_at}}t(f,"record");function u(e){if(!e||e.status!=="active")return null;try{return F(f(e))}catch(r){if(r instanceof TypeError||r instanceof SyntaxError)return null;throw r}}t(u,"active");function l(e){return e.grantee!=="app:terminal"&&v()}t(l,"mirrorOnly");async function I(){return(await L()).filter(e=>!l(e))}t(I,"localGrants");async function L(){return(await(await c()).prepare("SELECT * FROM grants").all()).flatMap(e=>{try{return[f(e)]}catch(r){if(r instanceof TypeError||r instanceof SyntaxError)return[];throw r}})}t(L,"allGrants");function j(e){return e.expires_at!==null&&!(Date.parse(e.expires_at)>Date.now())}t(j,"grantExpired");function F(e){if(e.status!=="active"||j(e))return null;try{const r=e.resolved_scopes.map(({scope:n})=>T(n));return{...e,resolved:e.resolved_scopes.flatMap(({mode:n},i)=>n==="always"?[r[i]]:[])}}catch(r){if(r instanceof TypeError||r instanceof SyntaxError)return null;throw r}}t(F,"activeRecord");function d(e){return Array.isArray(e)?`[${e.map(d).join(",")}]`:e&&typeof e=="object"?`{${Object.keys(e).sort().filter(r=>e[r]!==void 0).map(r=>`${JSON.stringify(r)}:${d(e[r])}`).join(",")}}`:JSON.stringify(e)??"null"}t(d,"canonicalJson");function U(e){const r={rev:e.rev,resolved_scopes:e.resolved_scopes,spend_cap:e.spend_cap,payer:e.payer,dependencies:e.dependencies??{},...e.inference_endpoint?{inference_endpoint:e.inference_endpoint}:{}};return S("sha256").update(d(r)).digest("base64url").slice(0,22)}t(U,"grantVersion");async function $(e){return!!await(await c()).prepare("SELECT 1 FROM grants WHERE id = ?").get(e)}t($,"hasGrant");async function P(e){return await(await c()).prepare("SELECT rev, status FROM grants WHERE id = ?").get(e)??null}t(P,"grantSyncState");async function V(e,r){return await(await c()).prepare("SELECT rev, status FROM grants WHERE grantee = ? AND sub = ?").get(e,r)??null}t(V,"grantSyncStateFor");async function M(e){const r=await(await c()).prepare("SELECT * FROM grants WHERE id = ?").get(e);if(!r)return null;try{return f(r)}catch(n){if(n instanceof TypeError||n instanceof SyntaxError)return null;throw n}}t(M,"grantRecord");async function B(e){return(e?(await M(e))?.payer:null)??"user"}t(B,"payerOfGrant");async function X(e){return u(await(await c()).prepare("SELECT * FROM grants WHERE id = ?").get(e))}t(X,"activeGrant");async function Y(e,r){return u(await(await c()).prepare("SELECT * FROM grants WHERE grantee = ? AND sub = ?").get(e,r))}t(Y,"grantFor");async function q(e){if(l({grantee:e}))return null;const r=await(await c()).prepare("SELECT * FROM grants WHERE grantee = ? AND status = 'active'").all(e);if(r.length>1)throw new Error(`multiple active grants for ${e}`);return u(r[0])}t(q,"activeGrantForApp");async function z(e){const r=await(await c()).prepare("SELECT run_notice_url AS url, sub, grantee FROM grants WHERE id = ? AND status = 'active'").get(e);return r&&r.url&&!l(r)?{url:r.url,sub:r.sub,grantee:r.grantee}:null}t(z,"runNoticeTarget");async function K(){const e=await(await c()).prepare("SELECT * FROM grants WHERE grantee = 'app:terminal' AND status = 'active'").all();if(e.length>1)throw new Error("multiple active terminal grants");return u(e[0])}t(K,"terminalGrant");async function Q(){return(await(await c()).prepare("SELECT value FROM runtime_state WHERE key = 'grants_source'").get())?.value??null}t(Q,"grantsSource");async function Z(e){return(await(await c()).prepare("SELECT value FROM runtime_state WHERE key = 'grants_source'").get())?.value===e}t(Z,"hasSyncedGrants");async function ee(e){return(await e.prepare("SELECT value FROM runtime_state WHERE key = 'grants_source'").get())?.value!=="local"?0:(await e.prepare("DELETE FROM runtime_state WHERE key = 'grants_source'").run(),Number((await e.prepare("DELETE FROM grants").run()).changes))}t(ee,"dropLocalGrantMirror");function re(e){if(e===void 0)return[];if(!Array.isArray(e))throw new TypeError("invalid grant sync response");return e.map(r=>{const{team:n,members:i}=r??{};if(typeof n!="string"||!/^team:\S+$/.test(n)||!Array.isArray(i)||i.some(o=>typeof o!="string"||!/^user:[^\s*]+$/.test(o)))throw new TypeError("invalid grant sync response");return{team:n,members:i}})}t(re,"teamsOf");async function te(e,r,n,i=[]){const o=new Set;for(const a of e){if(o.has(a.id)||!a.id||!a.runtime||!a.sub||!a.grantee||!Number.isInteger(a.rev)||!["active","revoked","removed"].includes(a.status))throw new TypeError("invalid grant sync response");o.add(a.id),p(a.scopes),p(a.resolved_scopes)}await O(async a=>{await a.exec("DELETE FROM grants");const E=a.prepare(`INSERT INTO grants
2
2
  (id, runtime, sub, grantee, rev, scopes_json, resolved_scopes_json, spend_cap, expires_at,
3
3
  status, revoked_at, approved_at, payer, dependencies_json, run_notice_url, inference_endpoint_json, synced_at)
4
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);for(const a of e)await _.run(a.id,a.runtime,a.sub,a.grantee,a.rev,JSON.stringify(a.scopes),JSON.stringify(a.resolved_scopes),a.spend_cap,a.expires_at,a.status,a.revoked_at,a.approved_at,a.payer,JSON.stringify(E(a.dependencies)),a.run_notice_url??null,a.inference_endpoint?JSON.stringify(a.inference_endpoint):null,a.synced_at);await s.prepare("DELETE FROM limits WHERE source = 'grant_sync'").run();for(const a of e){const c=k(a);c&&await A(a.id,[c],s)}await v(s,"directory",x,"member",i.flatMap(({team:a,members:c})=>c.map(g=>({resourceId:a,subject:R(g)})))),await s.prepare("INSERT INTO runtime_state (key, value) VALUES ('grants_source', ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(n),await s.prepare("INSERT INTO runtime_state (key, value) VALUES ('grants_generation', '1') ON CONFLICT(key) DO UPDATE SET value = CAST(CAST(runtime_state.value AS INTEGER) + 1 AS TEXT)").run()})}t(se,"replaceGrants");async function ie(){const e=await(await p()).prepare("SELECT value FROM runtime_state WHERE key = 'grants_generation'").get();return e?Number(e.value):0}t(ie,"grantsGeneration");export{z as activeGrant,Q as activeGrantForApp,j as activeRecord,F as allGrants,E as dependenciesOf,ne as dropLocalGrantMirror,M as grantExpired,K as grantFor,C as grantRecord,X as grantSyncState,Y as grantSyncStateFor,V as grantVersion,ie as grantsGeneration,re as grantsSource,B as hasGrant,te as hasSyncedGrants,b as inferenceEndpointOf,P as localGrants,d as mirrorOnly,q as payerOfGrant,se as replaceGrants,Z as runNoticeTarget,ae as teamsOf,ee as terminalGrant};
4
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);for(const s of e)await E.run(s.id,s.runtime,s.sub,s.grantee,s.rev,JSON.stringify(s.scopes),JSON.stringify(s.resolved_scopes),s.spend_cap,s.expires_at,s.status,s.revoked_at,s.approved_at,s.payer,JSON.stringify(y(s.dependencies)),s.run_notice_url??null,s.inference_endpoint?JSON.stringify(s.inference_endpoint):null,s.synced_at);await w(a,"directory",R,"member",i.flatMap(({team:s,members:_})=>_.map(g=>({resourceId:s,subject:x(g)})))),await a.prepare("INSERT INTO runtime_state (key, value) VALUES ('grants_source', ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(n),await a.prepare("INSERT INTO runtime_state (key, value) VALUES ('grants_generation', '1') ON CONFLICT(key) DO UPDATE SET value = CAST(CAST(runtime_state.value AS INTEGER) + 1 AS TEXT)").run()})}t(te,"replaceGrants");async function ne(){const e=await(await c()).prepare("SELECT value FROM runtime_state WHERE key = 'grants_generation'").get();return e?Number(e.value):0}t(ne,"grantsGeneration");export{X as activeGrant,q as activeGrantForApp,F as activeRecord,L as allGrants,y as dependenciesOf,ee as dropLocalGrantMirror,j as grantExpired,Y as grantFor,M as grantRecord,P as grantSyncState,V as grantSyncStateFor,U as grantVersion,ne as grantsGeneration,Q as grantsSource,$ as hasGrant,Z as hasSyncedGrants,b as inferenceEndpointOf,I as localGrants,l as mirrorOnly,B as payerOfGrant,te as replaceGrants,z as runNoticeTarget,re as teamsOf,K as terminalGrant};
@@ -3,7 +3,6 @@ export * from './months.js';
3
3
  export { payerOf, type Payer, type PayerCall } from './payer.js';
4
4
  export { writeCharge, receiptChargeId, takePct, chargeOf, type WriteResult } from './charges.js';
5
5
  export { check, findRefusal, refusalError, refusedMark, userActionOf, type Candidate, type CallCandidate, type StartCandidate, type Refusal } from './check.js';
6
- export { upsertLimit, deleteLimit, replaceGrantLimits, listLimits, grantLimitId } from './limits.js';
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 i}from"./payer.js";import{writeCharge as f,receiptChargeId as s,takePct as m,chargeOf as l}from"./charges.js";import{check as c,findRefusal as x,refusalError as d,refusedMark as u,userActionOf as g}from"./check.js";import{upsertLimit as h,deleteLimit as n,replaceGrantLimits as E,listLimits as k,grantLimitId as R}from"./limits.js";import{usageFor as b,listCharges as C,monthTotals as G,assertReference as I}from"./read.js";import{backfillLedger as A,LEDGER_MIGRATED_KEY as D}from"./backfill.js";import{rebuildTotals as M,rebuildTotalsFor as _}from"./totals.js";export{D as LEDGER_MIGRATED_KEY,I as assertReference,A as backfillLedger,l as chargeOf,c as check,n as deleteLimit,x as findRefusal,R as grantLimitId,C as listCharges,k as listLimits,G as monthTotals,i as payerOf,M as rebuildTotals,_ as rebuildTotalsFor,s as receiptChargeId,d as refusalError,u as refusedMark,E as replaceGrantLimits,m as takePct,h as upsertLimit,b as usageFor,g as userActionOf,f 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};
@@ -1,13 +1,8 @@
1
- import { type CanvasDb } from '../canvas/db.js';
2
1
  import type { Limit, LimitInput, LimitSource } from './types.js';
3
- export declare const grantLimitId: (grantId: string) => string;
4
2
  /** Insert or replace one limit row by id. */
5
3
  export declare function upsertLimit(input: LimitInput): Promise<Limit>;
6
4
  /** Delete one limit row; true when it existed. */
7
5
  export declare function deleteLimit(id: string): Promise<boolean>;
8
- /** A grant's limit rows in one transaction: delete every `grant_sync` row of this grant, then write `rows`. The first row's id
9
- * is `grant:<grant id>`, later ones `grant:<grant id>#<n>`. Only `grant_sync` rows are touched. */
10
- export declare function replaceGrantLimits(grantId: string, rows: ReadonlyArray<Omit<LimitInput, 'id' | 'source'>>, db?: CanvasDb): Promise<void>;
11
6
  /** Limit rows, optionally of one source or one payer, in id order. */
12
7
  export declare function listLimits(filter?: {
13
8
  source?: LimitSource;
@@ -1,3 +1,3 @@
1
- var l=Object.defineProperty;var a=(n,e)=>l(n,"name",{value:e,configurable:!0});import{openDb as m,withCanvasWrite as i}from"../canvas/db.js";const u=a(n=>`grant:${n}`,"grantLimitId"),s="id, payer, funding, grantee, provider, sub, usd_per_month, source, updated_at";async function E(n){if(!Number.isFinite(n.usd_per_month)||n.usd_per_month<0)throw new RangeError("usd_per_month must be a number of at least 0");const e={funding:null,grantee:null,provider:null,sub:null,...n,updated_at:new Date().toISOString()};return await i(async r=>{await r.prepare(`INSERT INTO limits (${s}) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
1
+ var d=Object.defineProperty;var t=(r,e)=>d(r,"name",{value:e,configurable:!0});import{openDb as i,withCanvasWrite as u}from"../canvas/db.js";const a="id, payer, funding, grantee, provider, sub, usd_per_month, source, updated_at";async function o(r){if(!Number.isFinite(r.usd_per_month)||r.usd_per_month<0)throw new RangeError("usd_per_month must be a number of at least 0");const e={funding:null,grantee:null,provider:null,sub:null,...r,updated_at:new Date().toISOString()};return await u(async n=>{await n.prepare(`INSERT INTO limits (${a}) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
2
2
  ON CONFLICT (id) DO UPDATE SET payer = excluded.payer, funding = excluded.funding, grantee = excluded.grantee,
3
- provider = excluded.provider, sub = excluded.sub, usd_per_month = excluded.usd_per_month, source = excluded.source, updated_at = excluded.updated_at`).run(e.id,e.payer,e.funding,e.grantee,e.provider,e.sub,e.usd_per_month,e.source,e.updated_at)}),e}a(E,"upsertLimit");async function w(n){return await i(async e=>(await e.prepare("DELETE FROM limits WHERE id = ?").run(n)).changes>0)}a(w,"deleteLimit");async function f(n,e,r){const d=a(async o=>{await o.prepare("DELETE FROM limits WHERE source = 'grant_sync' AND (id = ? OR starts_with(id, ?))").run(u(n),`${u(n)}#`);const c=new Date().toISOString();for(const[p,t]of e.entries()){if(!Number.isFinite(t.usd_per_month)||t.usd_per_month<0)throw new RangeError("usd_per_month must be a number of at least 0");await o.prepare(`INSERT INTO limits (${s}) VALUES (?, ?, ?, ?, ?, ?, ?, 'grant_sync', ?)`).run(p===0?u(n):`${u(n)}#${p}`,t.payer,t.funding??null,t.grantee??null,t.provider??null,t.sub??null,t.usd_per_month,c)}},"body");r!==void 0&&r.inTransaction?await d(r):await i(d)}a(f,"replaceGrantLimits");async function g(n={}){const e=[],r=[];return n.source!==void 0&&(e.push("source = ?"),r.push(n.source)),n.payer!==void 0&&(e.push("payer = ?"),r.push(n.payer)),await(await m()).prepareUncached(`SELECT ${s} FROM limits${e.length?` WHERE ${e.join(" AND ")}`:""} ORDER BY id`).all(...r)}a(g,"listLimits");export{w as deleteLimit,u as grantLimitId,g as listLimits,f as replaceGrantLimits,E as upsertLimit};
3
+ provider = excluded.provider, sub = excluded.sub, usd_per_month = excluded.usd_per_month, source = excluded.source, updated_at = excluded.updated_at`).run(e.id,e.payer,e.funding,e.grantee,e.provider,e.sub,e.usd_per_month,e.source,e.updated_at)}),e}t(o,"upsertLimit");async function c(r){return await u(async e=>(await e.prepare("DELETE FROM limits WHERE id = ?").run(r)).changes>0)}t(c,"deleteLimit");async function l(r={}){const e=[],n=[];return r.source!==void 0&&(e.push("source = ?"),n.push(r.source)),r.payer!==void 0&&(e.push("payer = ?"),n.push(r.payer)),await(await i()).prepareUncached(`SELECT ${a} FROM limits${e.length?` WHERE ${e.join(" AND ")}`:""} ORDER BY id`).all(...n)}t(l,"listLimits");export{c as deleteLimit,l as listLimits,o as upsertLimit};
@@ -12,8 +12,10 @@ export declare function addToTotals(db: CanvasDb, charge: {
12
12
  provider: string | null;
13
13
  usd: number | null;
14
14
  }): Promise<void>;
15
- /** Rebuild the whole rollup from `charges`. */
16
- export declare function rebuildTotals(db: CanvasDb): Promise<void>;
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. 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, '')"];
17
19
  /** Delete the rollup rows that name `a` or `b` as payer, sub or grantee and rebuild them from `charges`, so two rows that
18
20
  * now share a key are summed once. For person substitution, in the same transaction as the rename. */
19
21
  export declare function rebuildTotalsFor(db: CanvasDb, a: string, b: string): Promise<void>;
@@ -1,4 +1,4 @@
1
- var s=Object.defineProperty;var t=(r,e)=>s(r,"name",{value:e,configurable:!0});const a=t(r=>r??"","absent");async function p(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(p,"addToTotals");const u=`SELECT month, payer, funding, COALESCE(sub, ''), grantee, COALESCE(provider, ''), COALESCE(SUM(usd), 0)
3
- FROM charges WHERE refused IS NULL AND kind <> 'take'`,o="GROUP BY month, payer, funding, COALESCE(sub, ''), grantee, COALESCE(provider, '')";async function d(r){await r.prepare("DELETE FROM charge_totals").run(),await r.prepare(`INSERT INTO charge_totals (month, payer, funding, sub, grantee, provider, usd) ${u} ${o}`).run()}t(d,"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) ${u}
4
- AND (payer IN (?, ?) OR sub IN (?, ?) OR grantee IN (?, ?)) ${o}`).run(e,n,e,n,e,n)}t(i,"rebuildTotalsFor");export{a as absent,p as addToTotals,d 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.554",
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.554",
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.554",
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.554",
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.554",
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.554",
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.554",
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.554",
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.554",
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.554",
12476
+ "version": "0.3.556",
12477
12477
  "license": "UNLICENSED",
12478
12478
  "dependencies": {
12479
- "@north-light/api": "0.3.554",
12480
- "@north-light/identity": "0.3.554"
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.554",
12485
+ "version": "0.3.556",
12486
12486
  "license": "UNLICENSED",
12487
12487
  "dependencies": {
12488
- "@north-light/api": "0.3.554",
12489
- "@north-light/identity": "0.3.554",
12490
- "@north-light/plugin": "0.3.554",
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,12 +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
- limits: number;
8
- }
9
- /** Copy `usage` into `charges` as legacy rows, rebuild `charge_totals` from them and fill `limits` from the grant mirror, then
10
- * verify per `(grantee, month, payer, source, served_model)` that usd and token sums equal the `usage` row's, null kept. A
11
- * second call sees the marker and does nothing. A failed verification rolls the whole thing back and throws. */
12
- export declare function backfillLedger(person: string): Promise<BackfillResult>;
@@ -1,22 +0,0 @@
1
- var s=Object.defineProperty;var u=(t,e)=>s(t,"name",{value:e,configurable:!0});import{withCanvasWrite as E}from"../canvas/db.js";import{rebuildTotals as o}from"./totals.js";const a="ledger_migrated",r="'legacy:' || u.grantee || ':' || u.month || ':' || u.payer || ':' || u.source || ':' || u.served_model";async function L(t){return await E(async e=>{if(await e.prepare("SELECT value FROM canvas_meta WHERE key = ?").get(a)!==void 0)return{migrated:!1,charges:0,limits:0};const n=await e.prepare(`INSERT INTO charges (id, request, at, month, kind, what, units, usd, payer, funding, sub, grantee, provider)
2
- SELECT ${r},
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(t,t);await o(e);const i=await c(e);return await d(e),await e.prepare("INSERT INTO canvas_meta (key, value) VALUES (?, ?) ON CONFLICT (key) DO UPDATE SET value = excluded.value").run(a,new Date().toISOString()),{migrated:!0,charges:n.changes,limits:i}})}u(L,"backfillLedger");async function c(t){return(await t.prepare(`INSERT INTO limits (id, payer, funding, grantee, provider, sub, usd_per_month, source, updated_at)
13
- SELECT 'grant:' || id, sub, NULL, grantee, NULL, NULL, spend_cap, 'grant_sync', ?
14
- FROM grants WHERE status = 'active' AND spend_cap IS NOT NULL AND COALESCE(payer, 'user') <> 'app' AND grantee <> 'app:terminal'
15
- ON CONFLICT (id) DO NOTHING`).run(new Date().toISOString())).changes}u(c,"backfillLimits");async function d(t){const e=await t.prepare(`SELECT u.grantee, u.month, u.payer, u.source, u.served_model FROM usage u
16
- LEFT JOIN charges c ON c.id = ${r}
17
- WHERE c.id IS NULL
18
- OR c.usd IS DISTINCT FROM u.usd_equivalent::numeric(14,6)
19
- OR (u.source = 'model' AND (COALESCE((c.units ->> 'input')::bigint, -1) <> u.input_tokens
20
- OR COALESCE((c.units ->> 'output')::bigint, -1) <> u.output_tokens
21
- OR COALESCE((c.units ->> 'cache_read')::bigint, -1) <> u.cached_tokens))
22
- LIMIT 5`).all();if(e.length>0)throw new Error(`ledger backfill verification failed; first differing usage rows: ${JSON.stringify(e)}`)}u(d,"verify");export{a as LEDGER_MIGRATED_KEY,r as LEGACY_ID_SQL,L as backfillLedger};