@north-light/crouter 0.3.351 → 0.3.353
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.
- package/dist/clients/attach/viewer.js +924 -908
- package/dist/core/canvas/migrations.js +42 -26
- package/dist/core/canvas/recovery.js +1 -1
- package/dist/core/grants/remove.js +1 -1
- package/dist/core/grants/sync.d.ts +21 -0
- package/dist/core/grants/sync.js +1 -1
- package/dist/core/human/page-schema.d.ts +1 -1
- package/dist/core/human/requests.d.ts +3 -0
- package/dist/core/human/requests.js +15 -14
- package/dist/core/human/scan.js +1 -1
- package/dist/core/runs/operations.d.ts +24 -3
- package/dist/core/runs/operations.js +5 -5
- package/dist/core/runs/questions.d.ts +54 -0
- package/dist/core/runs/questions.js +4 -0
- package/dist/core/storage/attachments.d.ts +33 -0
- package/dist/core/storage/attachments.js +1 -0
- package/dist/core/storage/client.d.ts +67 -0
- package/dist/core/storage/client.js +3 -0
- package/dist/core/storage/download.d.ts +23 -0
- package/dist/core/storage/download.js +1 -0
- package/dist/core/storage/shares.d.ts +32 -0
- package/dist/core/storage/shares.js +1 -0
- package/dist/core/storage/tables.d.ts +59 -0
- package/dist/core/storage/tables.js +3 -0
- package/dist/daemon/api/app-listener.js +12 -12
- package/dist/daemon/api/handlers/app-profiles.d.ts +17 -0
- package/dist/daemon/api/handlers/app-profiles.js +1 -0
- package/dist/daemon/api/handlers/modelauth.d.ts +3 -0
- package/dist/daemon/api/handlers/modelauth.js +1 -1
- package/dist/daemon/api/handlers/questions.d.ts +7 -0
- package/dist/daemon/api/handlers/questions.js +1 -0
- package/dist/daemon/api/handlers/runs.js +1 -1
- package/dist/daemon/api/handlers/transfer.d.ts +15 -0
- package/dist/daemon/api/handlers/transfer.js +1 -0
- package/dist/daemon/api/handlers/vendor-status.d.ts +8 -0
- package/dist/daemon/api/handlers/vendor-status.js +1 -0
- package/dist/daemon/api/operations.d.ts +2 -1
- package/dist/daemon/api/operations.js +1 -1
- package/dist/daemon/api/principal.d.ts +5 -0
- package/dist/daemon/api/principal.js +1 -1
- package/dist/daemon/cron-run.js +1 -1
- package/dist/daemon/crtrd.js +7 -7
- package/dist/daemon/human/finish.d.ts +5 -0
- package/dist/daemon/human/finish.js +3 -3
- package/dist/daemon/human/settle.js +1 -1
- package/dist/daemon/reconcilers/storage-due-work.d.ts +32 -0
- package/dist/daemon/reconcilers/storage-due-work.js +1 -0
- package/dist/daemon/runs/append-section.js +2 -2
- package/dist/daemon/runs/observer.js +2 -2
- package/dist/types.d.ts +6 -0
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +11 -11
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var te=Object.defineProperty;var o=(e,n)=>te(e,"name",{value:n,configurable:!0});import{parseScope as oe}from"@north-light/crouter-identity";import{importNodeSqlAuthority as ie,recreateNodeRowsView as
|
|
1
|
+
var te=Object.defineProperty;var o=(e,n)=>te(e,"name",{value:n,configurable:!0});import{parseScope as oe}from"@north-light/crouter-identity";import{importNodeSqlAuthority as ie,recreateNodeRowsView as Te}from"./node-sql-migration.js";import{migrateRecoveryWitnesses as Ee}from"./recovery.js";import{migrateMessagingState as re}from"./deliveries.js";import{migrateHumanRequestsState as se}from"../human/requests.js";import{existsSync as p,readFileSync as U,readdirSync as S,realpathSync as ae,statSync as de}from"node:fs";import{basename as _e,join as x,resolve as H}from"node:path";import{crtrHome as ce,isSafeNodeId as Ne,nodesRoot as f,nodeMetaPath as I}from"./paths.js";import{STATE_MIGRATIONS as ue}from"../../migrations/registry.js";import{customExtensionPaths as Le}from"./extensions.js";import{readMetaObject as P,updateJsonFileDurably as Y,withMetaLock as le,writeMetaObjectLocked as pe}from"./meta-file.js";function Oe(e){e.exec(`
|
|
2
2
|
CREATE TABLE IF NOT EXISTS nodes (
|
|
3
3
|
node_id TEXT PRIMARY KEY,
|
|
4
4
|
name TEXT NOT NULL,
|
|
@@ -23,7 +23,7 @@ CREATE TABLE IF NOT EXISTS edges (
|
|
|
23
23
|
CREATE INDEX IF NOT EXISTS idx_edges_to ON edges(type, to_id);
|
|
24
24
|
CREATE INDEX IF NOT EXISTS idx_edges_from ON edges(type, from_id);
|
|
25
25
|
CREATE INDEX IF NOT EXISTS idx_nodes_status ON nodes(status);
|
|
26
|
-
`)}o(
|
|
26
|
+
`)}o(Oe,"baselineSchema");function w(e,n){return new Set(e.prepare(`PRAGMA table_info(${n})`).all().map(t=>t.name))}o(w,"tableColumns");function W(e,n,t,i){n.has(t)||(e.exec(i),n.add(t))}o(W,"addColumnIfMissing");function u(e){return w(e,"nodes")}o(u,"nodeColumns");const s=W;function Ae(e){const n=u(e);s(e,n,"intent","ALTER TABLE nodes ADD COLUMN intent TEXT;"),s(e,n,"pi_pid","ALTER TABLE nodes ADD COLUMN pi_pid INTEGER;"),s(e,n,"window","ALTER TABLE nodes ADD COLUMN window TEXT;"),s(e,n,"tmux_session","ALTER TABLE nodes ADD COLUMN tmux_session TEXT;")}o(Ae,"addRuntimeColumns");function Re(e){const n=f();if(!p(n))return;const t=e.prepare('UPDATE nodes SET intent = ?, pi_pid = ?, "window" = ?, tmux_session = ? WHERE node_id = ?');for(const i of S(n)){const r=I(i);if(!p(r))continue;let T;try{T=JSON.parse(U(r,"utf8"))}catch{continue}t.run(T.intent??null,T.pi_pid??null,T.window??null,T.tmux_session??null,i)}}o(Re,"backfillRuntime");function Xe(e){if(!(e.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'edges'").get()!==void 0)){e.exec(`
|
|
27
27
|
CREATE TABLE edges (
|
|
28
28
|
type TEXT NOT NULL,
|
|
29
29
|
from_id TEXT NOT NULL,
|
|
@@ -57,14 +57,14 @@ DROP TABLE edges;
|
|
|
57
57
|
ALTER TABLE edges_new RENAME TO edges;
|
|
58
58
|
CREATE INDEX IF NOT EXISTS idx_edges_to ON edges(type, to_id);
|
|
59
59
|
CREATE INDEX IF NOT EXISTS idx_edges_from ON edges(type, from_id);
|
|
60
|
-
`);const i=e.prepare("SELECT COUNT(*) AS n FROM edges").get().n;if(i!==t)throw new Error(`edges FK rebuild lost rows: expected ${t} non-orphan edge(s), got ${i}`);const
|
|
60
|
+
`);const i=e.prepare("SELECT COUNT(*) AS n FROM edges").get().n;if(i!==t)throw new Error(`edges FK rebuild lost rows: expected ${t} non-orphan edge(s), got ${i}`);const r=e.prepare("PRAGMA foreign_key_check").all();if(r.length>0)throw new Error(`edges FK rebuild left ${r.length} foreign-key violation(s)`);e.exec("COMMIT")}catch(i){throw e.exec("ROLLBACK"),i}}finally{e.exec("PRAGMA foreign_keys = ON;")}}o(Xe,"edgesForeignKeyCascade");function me(e){const n=u(e);s(e,n,"pane","ALTER TABLE nodes ADD COLUMN pane TEXT;")}o(me,"addPaneColumn");function fe(e){e.exec(`
|
|
61
61
|
CREATE TABLE IF NOT EXISTS focuses (
|
|
62
62
|
focus_id TEXT PRIMARY KEY, -- stable internal id for the viewport
|
|
63
63
|
pane TEXT, -- the durable %pane_id realizing the focus
|
|
64
64
|
session TEXT, -- derived cache of the user session (reconciled from pane)
|
|
65
65
|
node_id TEXT NOT NULL UNIQUE -- the node shown; UNIQUE \u2192 a node occupies <=1 focus
|
|
66
66
|
);
|
|
67
|
-
`)}o(
|
|
67
|
+
`)}o(fe,"addFocusesTable");function Ie(e){e.exec(`
|
|
68
68
|
CREATE TABLE IF NOT EXISTS wakeups (
|
|
69
69
|
wakeup_id TEXT PRIMARY KEY,
|
|
70
70
|
node_id TEXT, -- TARGET + node-anchored cancel anchor; NULL = canvas-detached
|
|
@@ -131,12 +131,12 @@ SELECT
|
|
|
131
131
|
FROM wakeups;
|
|
132
132
|
|
|
133
133
|
DROP TABLE wakeups;
|
|
134
|
-
`)}o(Ue,"migrateWakeupsToTriggers");function Se(e){const n=u(e);
|
|
134
|
+
`)}o(Ue,"migrateWakeupsToTriggers");function Se(e){const n=u(e);s(e,n,"host_kind","ALTER TABLE nodes ADD COLUMN host_kind TEXT;")}o(Se,"addHostKindColumn");function Ce(e){e.exec(`
|
|
135
135
|
CREATE TABLE IF NOT EXISTS canvas_meta (
|
|
136
136
|
key TEXT PRIMARY KEY,
|
|
137
137
|
value TEXT NOT NULL
|
|
138
138
|
);
|
|
139
|
-
`)}o(Ce,"addCanvasMetaTable");function De(e){const n=u(e);
|
|
139
|
+
`)}o(Ce,"addCanvasMetaTable");function De(e){const n=u(e);s(e,n,"profile_id","ALTER TABLE nodes ADD COLUMN profile_id TEXT;")}o(De,"addProfileIdColumn");function ge(e){const n=u(e);s(e,n,"pi_pid_identity","ALTER TABLE nodes ADD COLUMN pi_pid_identity TEXT;")}o(ge,"addPidIdentityColumn");function he(e){const n=u(e);s(e,n,"waiting_for","ALTER TABLE nodes ADD COLUMN waiting_for TEXT;")}o(he,"addMessageWaitColumn");function K(e){const n=u(e);if(n.size===0)return;s(e,n,"final_report","ALTER TABLE nodes ADD COLUMN final_report TEXT;"),s(e,n,"finalized_at","ALTER TABLE nodes ADD COLUMN finalized_at TEXT;");const t=e.prepare("SELECT node_id FROM nodes WHERE status = 'done' AND final_report IS NULL").all(),i=e.prepare("UPDATE nodes SET final_report = ?, finalized_at = ? WHERE node_id = ? AND final_report IS NULL");for(const{node_id:r}of t){const T=x(f(),r,"reports");if(!p(T))continue;let E=null;for(const a of S(T))if(a.endsWith(".md"))try{const d=U(x(T,a),"utf8"),_=/^---\nnode: [^\n]+\nkind: final\nts: ([^\n]+)\n---\n/.exec(d);if(_===null||Number.isNaN(Date.parse(_[1])))continue;const c={name:a,ts:_[1]};(E===null||c.ts>E.ts||c.ts===E.ts&&c.name>E.name)&&(E=c)}catch{}E!==null&&i.run(E.name,E.ts,r)}}o(K,"addCanonicalFinalColumns");function xe(e){const n=f();if(p(n))for(const t of S(n)){const i=I(t);p(i)&&Y(i,r=>{const T=r.launch;if(typeof T!="object"||T===null||Array.isArray(T))return!1;const E=T.extensions;if(!Array.isArray(E)||!E.every(d=>typeof d=="string"))return!1;const a=Le(E);return a.length===E.length?!1:(T.extensions=a,!0)})}}o(xe,"normalizePersistedLaunchExtensions");function we(e){const n=u(e);s(e,n,"waiting_for_generation","ALTER TABLE nodes ADD COLUMN waiting_for_generation INTEGER NOT NULL DEFAULT 0;")}o(we,"addMessageWaitGenerationColumn");function ye(e){e.exec(`
|
|
140
140
|
CREATE TABLE IF NOT EXISTS consult_outbox (
|
|
141
141
|
node_id TEXT PRIMARY KEY, -- legacy work node id
|
|
142
142
|
dir TEXT NOT NULL, -- legacy ticket directory
|
|
@@ -150,7 +150,7 @@ CREATE INDEX IF NOT EXISTS idx_consult_outbox_status ON consult_outbox(status);
|
|
|
150
150
|
CREATE INDEX IF NOT EXISTS idx_consult_outbox_dir ON consult_outbox(dir);
|
|
151
151
|
CREATE INDEX IF NOT EXISTS idx_consult_outbox_dir_request_status ON consult_outbox(dir, request_id, status);
|
|
152
152
|
CREATE INDEX IF NOT EXISTS idx_consult_outbox_dir_status ON consult_outbox(dir, status);
|
|
153
|
-
`)}o(ye,"addHumanWorkOutboxTable");function ve(e){const n=
|
|
153
|
+
`)}o(ye,"addHumanWorkOutboxTable");function ve(e){const n=w(e,"consult_outbox"),t=o((i,r)=>W(e,n,i,r),"add");t("work_kind","ALTER TABLE consult_outbox ADD COLUMN work_kind TEXT NOT NULL DEFAULT 'follow_up';"),t("interaction_id","ALTER TABLE consult_outbox ADD COLUMN interaction_id TEXT;"),t("generation_id","ALTER TABLE consult_outbox ADD COLUMN generation_id TEXT;"),t("interaction_snapshot","ALTER TABLE consult_outbox ADD COLUMN interaction_snapshot TEXT;"),t("handler_command","ALTER TABLE consult_outbox ADD COLUMN handler_command TEXT;"),t("handler_args_json","ALTER TABLE consult_outbox ADD COLUMN handler_args_json TEXT;"),t("claim_token","ALTER TABLE consult_outbox ADD COLUMN claim_token TEXT;"),e.exec(`
|
|
154
154
|
CREATE INDEX IF NOT EXISTS idx_consult_outbox_human_work_status ON consult_outbox(work_kind, status);
|
|
155
155
|
CREATE INDEX IF NOT EXISTS idx_consult_outbox_human_work_dir_status ON consult_outbox(dir, work_kind, status);
|
|
156
156
|
CREATE INDEX IF NOT EXISTS idx_consult_outbox_human_work_request_status ON consult_outbox(dir, request_id, work_kind, status);
|
|
@@ -216,28 +216,28 @@ CREATE INDEX IF NOT EXISTS idx_cron_runs_cron ON cron_runs(cron_id, started);
|
|
|
216
216
|
`)}o(ke,"addCronsTables");function R(e){return`'${e.replace(/'/g,`'"'"'`)}'`}o(R,"shellSingleQuote");function Me(e,n){let t=0;for(;;){const i=`${n}_${t}`;if(!e.split(`
|
|
217
217
|
`).includes(i))return i;t++}}o(Me,"uniqueHeredocDelimiter");function b(e,n,t){const i=Me(n,t);return`${e} <<'${i}'
|
|
218
218
|
${n}
|
|
219
|
-
${i}`}o(b,"heredocCommand");function Be(e){if(typeof e!="string")return{};try{const n=JSON.parse(e);return typeof n!="object"||n===null||Array.isArray(n)?{}:n}catch{return{}}}o(Be,"migrationPayload");function l(e,n,t){const i=e[n];if(typeof i!="string"||i.trim()==="")throw new Error(`trigger ${t} payload requires non-empty ${n}`);return i}o(l,"requiredString");function C(e,n){const t=e[n];return typeof t=="string"&&t.trim()!==""?t:void 0}o(C,"optionalString");function
|
|
219
|
+
${i}`}o(b,"heredocCommand");function Be(e){if(typeof e!="string")return{};try{const n=JSON.parse(e);return typeof n!="object"||n===null||Array.isArray(n)?{}:n}catch{return{}}}o(Be,"migrationPayload");function l(e,n,t){const i=e[n];if(typeof i!="string"||i.trim()==="")throw new Error(`trigger ${t} payload requires non-empty ${n}`);return i}o(l,"requiredString");function C(e,n){const t=e[n];return typeof t=="string"&&t.trim()!==""?t:void 0}o(C,"optionalString");function X(e,n){const t=e[n];return typeof t=="string"&&t.trim()!==""?t:void 0}o(X,"optionalColumn");function q(e,n,t,i){return b(`crtr node message send --to ${R(e)} --tier ${R(n)}`,t,`CRTR_MIGRATED_${i.replace(/[^A-Za-z0-9]/g,"_")}`)}o(q,"migratedMessageCommand");function Ge(e,n){return`printf '%s\\n' "$CRTR_GATE_STDOUT" | crtr node message send --to ${R(e)} --tier ${R(n)}`}o(Ge,"migratedGateStdoutMessageCommand");function He(e,n){const t=[`crtr node new --kind ${R(l(e,"kind",n))}`],i=[["mode","mode"],["cwd","cwd"],["name","name"],["parent","parent"],["worktreeBase","worktree"],["forkFrom","fork-from"],["model","model"],["profile","profile"],["situationalContext","situational-context"]];for(const[T,E]of i){const a=C(e,T);a!==void 0&&t.push(`--${E} ${R(a)}`)}e.root===!0&&t.push("--root");const r=l(e,"prompt",n);return b(t.join(" "),r,`CRTR_MIGRATED_${n.replace(/[^A-Za-z0-9]/g,"_")}`)}o(He,"migratedSpawnCommand");const $="migration.v22.skipped";function Pe(e){const n=e.prepare("SELECT cwd FROM nodes WHERE parent IS NULL ORDER BY created LIMIT 1").get();return(n!==void 0?X(n,"cwd"):void 0)??process.cwd()}o(Pe,"ambientMigrationCwd");function Ye(e){let n;try{n=e.prepare("SELECT eval_pid FROM triggers WHERE eval_state = 'running' AND eval_pid IS NOT NULL").all()}catch{return}for(const t of n){const i=t.eval_pid;if(!(typeof i!="number"||!Number.isInteger(i)||i<=1))try{process.kill(-i,"SIGTERM")}catch{}}}o(Ye,"reapRunningPredicateEvaluations");function We(e){const n=[],t=e.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'triggers'").get()!==void 0;t&&Ye(e);let i=[];if(t)try{i=e.prepare("SELECT * FROM triggers ORDER BY created, trigger_id").all()}catch(E){n.push({trigger_id:"<unreadable table>",reason:`triggers table could not be read: ${E instanceof Error?E.message:String(E)}`})}const r=Pe(e),T=e.prepare(`
|
|
220
220
|
INSERT OR REPLACE INTO crons (
|
|
221
221
|
cron_id, name, created_by, command, fire_at, recur, tz, expires_at,
|
|
222
222
|
anchor_node, cancel_on_wake, cwd, env_json, profile, scope,
|
|
223
223
|
run_timeout_s, overlap, on_output, sink, tier, created, updated
|
|
224
|
-
) VALUES (?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, NULL, ?, 'profile', ?, 'skip', 'on-failure', '{}', ?, ?, ?)`);for(const
|
|
224
|
+
) VALUES (?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, NULL, ?, 'profile', ?, 'skip', 'on-failure', '{}', ?, ?, ?)`);for(const E of i){const a=X(E,"trigger_id");if(a===void 0){n.push({trigger_id:"<unidentified row>",reason:"row carries no trigger_id"});continue}try{Ke(e,T,E,a,r)}catch(d){n.push({trigger_id:a,reason:d instanceof Error?d.message:String(d)})}}n.length>0&&(e.prepare("INSERT INTO canvas_meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run($,JSON.stringify(n)),console.warn(`[canvas migration v22] ${n.length} retired schedule(s) could not be converted to crons and were dropped; see canvas_meta['${$}']: `+n.map(E=>`${E.trigger_id} (${E.reason})`).join("; "))),e.exec(`
|
|
225
225
|
DROP INDEX IF EXISTS idx_triggers_fire;
|
|
226
226
|
DROP INDEX IF EXISTS idx_triggers_node;
|
|
227
227
|
DROP INDEX IF EXISTS idx_triggers_owner;
|
|
228
228
|
DROP INDEX IF EXISTS idx_triggers_eval;
|
|
229
229
|
DROP INDEX IF EXISTS idx_triggers_deadline;
|
|
230
230
|
DROP TABLE IF EXISTS triggers;
|
|
231
|
-
`)}o(We,"migrateTriggersToCrons");function Ke(e,n,t,i,
|
|
232
|
-
${
|
|
231
|
+
`)}o(We,"migrateTriggersToCrons");function Ke(e,n,t,i,r){const T=l(t,"owner_id",i),E=e.prepare("SELECT cwd, profile_id FROM nodes WHERE node_id = ?").get(T),a=(E===void 0?void 0:X(E,"cwd"))??r,d=E===void 0?null:X(E,"profile_id")??null,_=X(t,"node_id")??null,c=l(t,"action",i),N=l(t,"action_mode",i),L=l(t,"trigger_type",i),m=Be(t.payload),M=X(t,"created")??new Date().toISOString(),g=N==="deadline_message"?"urgent":C(m,"tier")??"normal",y=C(m,"label");let O,h,B=!1;if(c==="msg"&&(N==="inbox_message"||N==="deadline_message")){if(_===null)throw new Error("has no message target");const A=C(m,"body");A!==void 0?O=q(_,g,A,i):L==="when"?(B=!0,O=Ge(_,g)):O=q(_,g,y??"Scheduled message (no body was recorded when this schedule was armed).",i),h=y??(N==="deadline_message"?"deadline":"scheduled message")}else if(c==="msg"&&N==="fresh_revive"){if(_===null)throw new Error("has no revive target");O=`crtr node lifecycle revive ${R(_)} --fresh`,h=y??"scheduled fresh revive"}else if(c==="new"&&N==="node_birth")O=He(m,i),h=C(m,"name")??`scheduled ${l(m,"kind",i)} node`;else throw new Error(`has unsupported action ${c}/${N}`);let v,F,G=null;if(L==="when"){v=l(t,"next_eval_at",i),F=l(t,"eval_every",i),G=X(t,"timeout_at")??null;const A=l(t,"when_script",i);O=`${B?`if ! CRTR_GATE_STDOUT="$(
|
|
232
|
+
${A}
|
|
233
233
|
)"; then`:`if ! (
|
|
234
|
-
${
|
|
234
|
+
${A}
|
|
235
235
|
); then`}
|
|
236
236
|
exit 0
|
|
237
237
|
fi
|
|
238
238
|
{
|
|
239
|
-
${
|
|
240
|
-
} && crtr cron cancel "$CRTR_CRON_ID"`}else if(L==="at"||L==="every"||L==="until")v=l(t,"fire_at",i),F=L==="every"?l(t,"recur",i):null;else throw new Error(`has unknown timing type ${L}`);if(N==="deadline_message"&&_!==null){const
|
|
239
|
+
${O}
|
|
240
|
+
} && crtr cron cancel "$CRTR_CRON_ID"`}else if(L==="at"||L==="every"||L==="until")v=l(t,"fire_at",i),F=L==="every"?l(t,"recur",i):null;else throw new Error(`has unknown timing type ${L}`);if(N==="deadline_message"&&_!==null){const A=e.prepare("SELECT cron_id FROM crons WHERE anchor_node = ? AND cancel_on_wake = 1 AND cron_id <> ?").get(_,i);if(A!==void 0)throw new Error(`node ${_} already has a migrated deadline (${String(A.cron_id)}); only one is allowed`)}n.run(i,h,T,O,v,F,G,_,N==="deadline_message"?1:0,a,d,300,g,M,M)}o(Ke,"convertTriggerRow");function be(e){const n=w(e,"crons");n.size===0||n.has("run_pid_identity")||e.exec("ALTER TABLE crons ADD COLUMN run_pid_identity TEXT;")}o(be,"addCronRunPidIdentityColumn");function qe(e){const n=w(e,"crons");n.size!==0&&(n.has("run_out_of_band")||e.exec("ALTER TABLE crons ADD COLUMN run_out_of_band INTEGER NOT NULL DEFAULT 0;"),n.has("queued")&&e.exec("ALTER TABLE crons DROP COLUMN queued;"))}o(qe,"addCronOutOfBandColumn");function $e(e){e.exec(`
|
|
241
241
|
CREATE TABLE IF NOT EXISTS warm_pool (
|
|
242
242
|
node_id TEXT PRIMARY KEY REFERENCES nodes(node_id) ON DELETE CASCADE,
|
|
243
243
|
recipe_key TEXT NOT NULL, -- frozen launch tuple this spare was minted for
|
|
@@ -256,7 +256,7 @@ CREATE TABLE consult_outbox_new (
|
|
|
256
256
|
);
|
|
257
257
|
`);const t=e.prepare(`
|
|
258
258
|
INSERT INTO consult_outbox_new (node_id, ticket_node_id, request_id, work_kind, status, final_report, updated_at)
|
|
259
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)`);let i=0;for(const
|
|
259
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`);let i=0;for(const r of n){const T=_e(r.dir);if(!(r.root===H(r.root)&&r.dir===x(r.root,T))||!Ne(T)){i+=1;continue}t.run(r.node_id,T,r.request_id,r.work_kind,r.status,r.final_report,r.updated_at)}i>0&&console.warn(`[canvas migration v26] dropped ${i} consult_outbox row(s) whose legacy ticket path cannot safely map to a ticket node id`),e.exec(`
|
|
260
260
|
DROP TABLE consult_outbox;
|
|
261
261
|
ALTER TABLE consult_outbox_new RENAME TO consult_outbox;
|
|
262
262
|
|
|
@@ -378,18 +378,18 @@ CREATE TABLE human_action_deliveries (
|
|
|
378
378
|
updated_at INTEGER NOT NULL
|
|
379
379
|
);
|
|
380
380
|
CREATE INDEX idx_human_action_deliveries_due ON human_action_deliveries(state, next_attempt_at);
|
|
381
|
-
`)}o(nn,"addHumanActionDeliveries");function tn(e){const n=u(e);
|
|
381
|
+
`)}o(nn,"addHumanActionDeliveries");function tn(e){const n=u(e);s(e,n,"pi_session_file","ALTER TABLE nodes ADD COLUMN pi_session_file TEXT;");for(const t of S(f(),{withFileTypes:!0}))if(t.isDirectory())try{const i=JSON.parse(U(I(t.name),"utf8"));typeof i.pi_session_file=="string"&&e.prepare("UPDATE nodes SET pi_session_file = ? WHERE node_id = ?").run(i.pi_session_file,t.name)}catch{}e.exec("CREATE INDEX IF NOT EXISTS idx_nodes_pi_session_file ON nodes(pi_session_file);")}o(tn,"addNodeSessionFileIndex");function j(e,n){const t=o(a=>{const d=H(a);return p(d)?ae(d):d},"canonical"),i=e.prepare("UPDATE nodes SET pi_session_file = ? WHERE node_id = ?");e.exec("DROP INDEX IF EXISTS idx_nodes_pi_session_file;");const r=e.prepare("SELECT node_id FROM nodes ORDER BY created ASC, node_id ASC").all();for(const{node_id:a}of r){const d=I(a);if(P(d)===null){i.run(null,a);continue}const _=le(d,()=>{const c=P(d);if(c===null)throw new Error(`node ${a} session metadata changed during ownership migration`);if(typeof c.pi_session_file!="string")return null;const N=c.pi_session_file,L=t(N);return L!==N&&(c.pi_session_file=L,pe(d,c)),L});i.run(_,a)}const T=e.prepare(`
|
|
382
382
|
SELECT pi_session_file
|
|
383
383
|
FROM nodes
|
|
384
384
|
WHERE pi_session_file IS NOT NULL
|
|
385
385
|
GROUP BY pi_session_file
|
|
386
386
|
HAVING COUNT(*) > 1
|
|
387
|
-
`).all();if(!n&&
|
|
387
|
+
`).all();if(!n&&T.length>0){const a=T.map(({pi_session_file:d})=>d).join(", ");throw new Error(`cannot restore unique Pi session ownership: authoritative metadata has concurrent claimants for ${a}`)}const E=e.prepare("UPDATE nodes SET pi_session_file = NULL WHERE node_id = ?");for(const{pi_session_file:a}of T){const d=e.prepare(`
|
|
388
388
|
SELECT node_id
|
|
389
389
|
FROM nodes
|
|
390
390
|
WHERE pi_session_file = ?
|
|
391
391
|
ORDER BY created ASC, node_id ASC
|
|
392
|
-
`).all(a);for(const{node_id:_}of d.slice(1)){if(!Y(I(_),N=>{const L=N.pi_session_file;if(typeof L!="string"||t(L)!==a)throw new Error(`node ${_} session metadata changed during ownership migration`);return N.pi_session_id=null,N.pi_session_file=null,!0}))throw new Error(`node ${_} session metadata changed during ownership migration`);
|
|
392
|
+
`).all(a);for(const{node_id:_}of d.slice(1)){if(!Y(I(_),N=>{const L=N.pi_session_file;if(typeof L!="string"||t(L)!==a)throw new Error(`node ${_} session metadata changed during ownership migration`);return N.pi_session_id=null,N.pi_session_file=null,!0}))throw new Error(`node ${_} session metadata changed during ownership migration`);E.run(_)}}e.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_nodes_pi_session_file ON nodes(pi_session_file);")}o(j,"reconcileNodeSessionFileOwners");function on(e){j(e,!0)}o(on,"makeNodeSessionFileUnique");function Tn(e){j(e,!1)}o(Tn,"repairNodeSessionFileUniqueIndex");function En(e){const n=u(e);s(e,n,"frozen_at","ALTER TABLE nodes ADD COLUMN frozen_at TEXT;"),s(e,n,"respawn_failures","ALTER TABLE nodes ADD COLUMN respawn_failures INTEGER NOT NULL DEFAULT 0;"),s(e,n,"respawn_not_before","ALTER TABLE nodes ADD COLUMN respawn_not_before TEXT;"),s(e,n,"launched_at","ALTER TABLE nodes ADD COLUMN launched_at TEXT;"),s(e,n,"last_settled_at","ALTER TABLE nodes ADD COLUMN last_settled_at TEXT;"),s(e,n,"terminal_reason","ALTER TABLE nodes ADD COLUMN terminal_reason TEXT;"),e.exec(`
|
|
393
393
|
UPDATE nodes SET terminal_reason = CASE
|
|
394
394
|
WHEN status = 'done' AND final_report IS NOT NULL THEN 'finalized'
|
|
395
395
|
WHEN status = 'done' AND intent = 'parked' THEN 'parked'
|
|
@@ -398,7 +398,7 @@ UPDATE nodes SET terminal_reason = CASE
|
|
|
398
398
|
WHEN status = 'dead' THEN 'crashed'
|
|
399
399
|
END
|
|
400
400
|
WHERE status IN ('done','canceled','dead') AND terminal_reason IS NULL;
|
|
401
|
-
`)}o(
|
|
401
|
+
`)}o(En,"addNodeLifecycleColumns");function rn(e){const n=u(e);s(e,n,"description","ALTER TABLE nodes ADD COLUMN description TEXT;"),s(e,n,"cycles","ALTER TABLE nodes ADD COLUMN cycles INTEGER;");const t=f();if(!p(t))return;const i=e.prepare("UPDATE nodes SET description = ?, cycles = ? WHERE node_id = ?");for(const r of S(t)){const T=I(r);if(!p(T))continue;let E;try{E=JSON.parse(U(T,"utf8"))}catch{continue}const a=typeof E.description=="string"?E.description:null,d=typeof E.cycles=="number"?E.cycles:null;i.run(a,d,r)}}o(rn,"addNodeLabelColumns");function sn(e){const n=u(e);s(e,n,"outcome_kind","ALTER TABLE nodes ADD COLUMN outcome_kind TEXT;"),s(e,n,"outcome_reason","ALTER TABLE nodes ADD COLUMN outcome_reason TEXT;"),s(e,n,"outcome_at","ALTER TABLE nodes ADD COLUMN outcome_at TEXT;"),s(e,n,"outcome_revision","ALTER TABLE nodes ADD COLUMN outcome_revision INTEGER NOT NULL DEFAULT 0;"),s(e,n,"outcome_detail_json","ALTER TABLE nodes ADD COLUMN outcome_detail_json TEXT;"),s(e,n,"deadline_at","ALTER TABLE nodes ADD COLUMN deadline_at TEXT;"),e.exec(`
|
|
402
402
|
CREATE TABLE IF NOT EXISTS node_outcome_deliveries (
|
|
403
403
|
node_id TEXT PRIMARY KEY,
|
|
404
404
|
state TEXT NOT NULL,
|
|
@@ -431,17 +431,17 @@ UPDATE nodes SET outcome_kind='failure',
|
|
|
431
431
|
outcome_reason=COALESCE(terminal_reason,'crashed'),
|
|
432
432
|
outcome_at=COALESCE(finalized_at, created), outcome_revision=1
|
|
433
433
|
WHERE final_report IS NULL AND status IN ('done','dead','canceled') AND outcome_at IS NULL;
|
|
434
|
-
`)}o(
|
|
434
|
+
`)}o(sn,"addNodeOutcomeAndDeadline");function an(e){const n=u(e);s(e,n,"telemetry_tokens_in","ALTER TABLE nodes ADD COLUMN telemetry_tokens_in INTEGER;"),s(e,n,"telemetry_context_tokens","ALTER TABLE nodes ADD COLUMN telemetry_context_tokens INTEGER;"),s(e,n,"telemetry_last_activity","ALTER TABLE nodes ADD COLUMN telemetry_last_activity TEXT;"),s(e,n,"telemetry_updated_at","ALTER TABLE nodes ADD COLUMN telemetry_updated_at TEXT;");const t=e.prepare(`UPDATE nodes
|
|
435
435
|
SET telemetry_tokens_in = ?, telemetry_context_tokens = ?,
|
|
436
436
|
telemetry_last_activity = ?, telemetry_updated_at = ?
|
|
437
|
-
WHERE node_id = ?`);for(const{node_id:i}of e.prepare("SELECT node_id FROM nodes").all()){const
|
|
437
|
+
WHERE node_id = ?`);for(const{node_id:i}of e.prepare("SELECT node_id FROM nodes").all()){const r=x(f(),i,"job","telemetry.json");if(p(r))try{const T=JSON.parse(U(r,"utf8")),E=typeof T.tokens_in=="number"&&Number.isFinite(T.tokens_in)?T.tokens_in:null,a=typeof T.context_tokens=="number"&&Number.isFinite(T.context_tokens)?T.context_tokens:null,d=typeof T.last_activity=="string"?T.last_activity:null,_=de(r).mtime.toISOString();t.run(E,a,d,_,i)}catch{}}}o(an,"addNodeTelemetryColumns");function dn(e){e.exec(`
|
|
438
438
|
CREATE INDEX IF NOT EXISTS deliveries_external_due_order
|
|
439
439
|
ON deliveries(state, next_attempt_at, created_at, delivery_id)
|
|
440
440
|
WHERE destination_kind = 'external_action' AND canceled_at IS NULL;
|
|
441
441
|
DROP INDEX IF EXISTS deliveries_external_due;
|
|
442
442
|
CREATE INDEX IF NOT EXISTS idx_nodes_parent_created ON nodes(parent, created);
|
|
443
443
|
CREATE INDEX IF NOT EXISTS idx_edges_type_created ON edges(type, created);
|
|
444
|
-
`)}o(dn,"addRecurringLaneIndexes");const _n=ue.filter(e=>e.lane==="journaled").map(e=>n=>e.apply(n,ce()));function cn(e){
|
|
444
|
+
`)}o(dn,"addRecurringLaneIndexes");const _n=ue.filter(e=>e.lane==="journaled").map(e=>n=>e.apply(n,ce()));function cn(e){Te(e)}o(cn,"projectLiveNodeState");function Nn(e){s(e,u(e),"scopes_json","ALTER TABLE nodes ADD COLUMN scopes_json TEXT;")}o(Nn,"addNodeScopes");function un(e){const n=u(e);s(e,n,"grantee","ALTER TABLE nodes ADD COLUMN grantee TEXT NOT NULL DEFAULT 'user:local';"),s(e,n,"sub","ALTER TABLE nodes ADD COLUMN sub TEXT;"),s(e,n,"grant_id","ALTER TABLE nodes ADD COLUMN grant_id TEXT;"),s(e,n,"client_id","ALTER TABLE nodes ADD COLUMN client_id TEXT;"),s(e,n,"metadata_json","ALTER TABLE nodes ADD COLUMN metadata_json TEXT;"),s(e,n,"run_id","ALTER TABLE nodes ADD COLUMN run_id TEXT;"),s(e,n,"event_cursor","ALTER TABLE nodes ADD COLUMN event_cursor INTEGER;"),s(e,n,"usage_json","ALTER TABLE nodes ADD COLUMN usage_json TEXT;"),s(e,n,"deleting_at","ALTER TABLE nodes ADD COLUMN deleting_at TEXT;"),e.exec(`
|
|
445
445
|
ALTER TABLE sessions ADD COLUMN event_offset INTEGER;
|
|
446
446
|
CREATE INDEX idx_nodes_run_id ON nodes(run_id);
|
|
447
447
|
-- Child nodes may be pruned while their root run and event history remain.
|
|
@@ -478,13 +478,13 @@ CREATE INDEX IF NOT EXISTS idx_edges_type_created ON edges(type, created);
|
|
|
478
478
|
);
|
|
479
479
|
CREATE UNIQUE INDEX idx_grants_grantee_sub ON grants(grantee, sub);
|
|
480
480
|
CREATE TABLE runtime_state (key TEXT PRIMARY KEY, value TEXT NOT NULL);
|
|
481
|
-
`)}o(ln,"addGrantMirror");function pn(e){e.exec("UPDATE nodes SET grantee = 'app:terminal' WHERE grantee = 'user:local';")}o(pn,"migrateTerminalGrantees");function
|
|
481
|
+
`)}o(ln,"addGrantMirror");function pn(e){e.exec("UPDATE nodes SET grantee = 'app:terminal' WHERE grantee = 'user:local';")}o(pn,"migrateTerminalGrantees");function On(e){const n={llm:"crtr:llm",act:"crtr:act",schedule:"crtr:schedule","memory:read":"crtr:memory:read:user","memory:write":"crtr:memory:rw:user"},t=e.prepare("SELECT node_id, scopes_json FROM nodes WHERE scopes_json IS NOT NULL").all(),i=e.prepare("UPDATE nodes SET scopes_json = ? WHERE node_id = ?");for(const r of t){const T=JSON.parse(r.scopes_json);if(!Array.isArray(T))throw new Error(`invalid launched scopes for node ${r.node_id}`);const E=[];for(const a of T){if(typeof a!="string")continue;const d=n[a]??a;try{oe(d),E.push(d)}catch(_){if(!(_ instanceof TypeError))throw _}}i.run(JSON.stringify([...new Set(E)]),r.node_id)}}o(On,"migrateLaunchedScopes");function An(e){e.exec(`CREATE TABLE node_tokens (
|
|
482
482
|
broker_process_id TEXT PRIMARY KEY REFERENCES executions(execution_id) ON DELETE CASCADE,
|
|
483
483
|
node_id TEXT NOT NULL REFERENCES nodes(node_id) ON DELETE CASCADE,
|
|
484
484
|
token_hash TEXT NOT NULL UNIQUE, grantee TEXT NOT NULL, uid INTEGER NOT NULL,
|
|
485
485
|
sub TEXT NOT NULL, grant TEXT, client_id TEXT, scopes_json TEXT,
|
|
486
486
|
issued_at TEXT NOT NULL, revoked_at TEXT
|
|
487
|
-
);`)}o(
|
|
487
|
+
);`)}o(An,"addNodeTokens");function z(e,n){return e.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?").get(n)!==void 0}o(z,"hasTable");function J(e,n,t){return e.prepare(`PRAGMA table_info(${n})`).all().some(i=>i.name===t)}o(J,"hasColumn");function Q(e){z(e,"memory_reads")||e.exec(`CREATE TABLE memory_reads (
|
|
488
488
|
node_id TEXT NOT NULL REFERENCES nodes(node_id) ON DELETE CASCADE,
|
|
489
489
|
name TEXT NOT NULL,
|
|
490
490
|
scope TEXT NOT NULL,
|
|
@@ -498,7 +498,7 @@ CREATE INDEX IF NOT EXISTS idx_edges_type_created ON edges(type, created);
|
|
|
498
498
|
created TEXT NOT NULL, status TEXT NOT NULL CHECK (status IN ('active', 'retired'))
|
|
499
499
|
);`)}o(Z,"addAppUsers");function ee(e){J(e,"crons","grantee")||(e.exec("ALTER TABLE crons ADD COLUMN grantee TEXT NOT NULL DEFAULT 'app:terminal'"),e.exec(`UPDATE crons SET grantee = COALESCE(
|
|
500
500
|
(SELECT COALESCE(nodes.grantee, 'app:terminal') FROM nodes WHERE nodes.node_id = crons.anchor_node),
|
|
501
|
-
'app:terminal') WHERE anchor_node IS NOT NULL`))}o(ee,"addCronGrantee");function Rn(e){Q(e),V(e),Z(e),ee(e)}o(Rn,"convergeSharedVersions");function
|
|
501
|
+
'app:terminal') WHERE anchor_node IS NOT NULL`))}o(ee,"addCronGrantee");function Rn(e){Q(e),V(e),Z(e),ee(e)}o(Rn,"convergeSharedVersions");function Xn(e){(e.prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'app_users'").get()?.sql??"").includes("'removed'")||e.exec(`CREATE TABLE app_users_new (
|
|
502
502
|
uid INTEGER PRIMARY KEY, app TEXT NOT NULL,
|
|
503
503
|
created TEXT NOT NULL, status TEXT NOT NULL CHECK (status IN ('active', 'removed')),
|
|
504
504
|
removed_at TEXT
|
|
@@ -508,4 +508,20 @@ CREATE INDEX IF NOT EXISTS idx_edges_type_created ON edges(type, created);
|
|
|
508
508
|
CASE status WHEN 'active' THEN NULL ELSE created END FROM app_users;
|
|
509
509
|
DROP TABLE app_users;
|
|
510
510
|
ALTER TABLE app_users_new RENAME TO app_users;
|
|
511
|
-
CREATE UNIQUE INDEX idx_app_users_active_app ON app_users(app) WHERE status = 'active';`)}o(
|
|
511
|
+
CREATE UNIQUE INDEX idx_app_users_active_app ON app_users(app) WHERE status = 'active';`)}o(Xn,"appUsersRemoved");function mn(e){e.exec(`CREATE TABLE IF NOT EXISTS uploads (
|
|
512
|
+
upload_id TEXT PRIMARY KEY, grantee TEXT NOT NULL, name TEXT NOT NULL, media_type TEXT NOT NULL,
|
|
513
|
+
size INTEGER NOT NULL, created_at TEXT NOT NULL, delete_pending_since TEXT
|
|
514
|
+
);
|
|
515
|
+
CREATE INDEX IF NOT EXISTS idx_uploads_grantee ON uploads(grantee);
|
|
516
|
+
CREATE INDEX IF NOT EXISTS idx_uploads_delete_pending ON uploads(delete_pending_since) WHERE delete_pending_since IS NOT NULL;
|
|
517
|
+
CREATE TABLE IF NOT EXISTS upload_uses (
|
|
518
|
+
upload_id TEXT NOT NULL, run_id TEXT NOT NULL, path TEXT NOT NULL,
|
|
519
|
+
PRIMARY KEY (run_id, path)
|
|
520
|
+
);
|
|
521
|
+
CREATE INDEX IF NOT EXISTS idx_upload_uses_upload ON upload_uses(upload_id);
|
|
522
|
+
CREATE TABLE IF NOT EXISTS shares (
|
|
523
|
+
share_id TEXT PRIMARY KEY, source_space TEXT NOT NULL, source_path TEXT NOT NULL, storage_key TEXT NOT NULL,
|
|
524
|
+
url TEXT NOT NULL, created_by TEXT NOT NULL, created_at TEXT NOT NULL, expires_at TEXT,
|
|
525
|
+
status TEXT NOT NULL CHECK (status IN ('on', 'off')), object_deleted_at TEXT
|
|
526
|
+
);
|
|
527
|
+
CREATE INDEX IF NOT EXISTS idx_shares_created_by ON shares(created_by);`)}o(mn,"addFileTransfer");const D=[Oe,Ae,Re,Xe,me,fe,Ie,Se,Ue,Ce,De,ge,he,K,xe,K,we,ye,ve,Fe,ke,We,be,qe,$e,je,ze,Je,Qe,Ve,Ze,en,nn,tn,En,on,Tn,rn,sn,..._n,e=>{ie(e),Ee(e),re(e),se(e)},dn,cn,an,Nn,un,Ln,ln,An,pn,On,Q,V,Z,ee,Rn,Xn,mn],fn=new Set([3]);function k(e){return e.prepare("PRAGMA user_version").get().user_version}o(k,"currentUserVersion");function ne(e,n){D[n](e),e.exec(`PRAGMA user_version = ${n+1}`)}o(ne,"applyMigration");function Bn(e){const n=k(e);if(n>D.length)throw new Error(`unsupported canvas schema version ${n} (latest supported ${D.length})`);if(n===D.length)return;const t=e.prepare("PRAGMA busy_timeout").get().timeout;e.exec("PRAGMA busy_timeout = 60000;");try{In(e)}finally{e.exec(`PRAGMA busy_timeout = ${t};`)}}o(Bn,"migrate");function In(e){for(;;){const n=k(e);if(n>=D.length)return;if(e.isTransaction||fn.has(n)){ne(e,n);continue}e.exec("BEGIN IMMEDIATE");try{k(e)===n&&ne(e,n),e.exec("COMMIT")}catch(t){try{e.exec("ROLLBACK")}catch{}throw t}}}o(In,"runPendingMigrations");export{D as MIGRATIONS,Bn as migrate};
|
|
@@ -23,4 +23,4 @@ CREATE TABLE IF NOT EXISTS node_faults (
|
|
|
23
23
|
);
|
|
24
24
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_node_faults_active ON node_faults(node_id) WHERE active = 1;
|
|
25
25
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_node_faults_episode ON node_faults(node_id) WHERE episode_state IS NOT NULL;
|
|
26
|
-
`)}o(ye,"createFaultTable");function y(e,t){return typeof t=="string"&&e.includes(t)}o(y,"contains");function b(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}o(b,"isRecord");function B(e){if(!b(e)||typeof e.provider!="string"||e.provider===""||typeof e.model!="string"||e.model==="")return;const t=typeof e.status=="number"&&Number.isFinite(e.status)?e.status:void 0,r=typeof e.code=="string"||typeof e.code=="number"?e.code:void 0,n=typeof e.errno=="string"?e.errno:void 0,i=typeof e.body=="string"?e.body:void 0,d=Array.isArray(e.causes)?e.causes.flatMap(s=>{if(!b(s))return[];const c={...typeof s.message=="string"?{message:s.message}:{},...typeof s.status=="number"&&Number.isFinite(s.status)?{status:s.status}:{},...typeof s.code=="string"||typeof s.code=="number"?{code:s.code}:{},...typeof s.errno=="string"?{errno:s.errno}:{},...typeof s.body=="string"?{body:s.body}:{}};return Object.keys(c).length===0?[]:[c]}):void 0;return{provider:e.provider,model:e.model,...t===void 0?{}:{status:t},...r===void 0?{}:{code:r},...n===void 0?{}:{errno:n},...i===void 0?{}:{body:i},...d===void 0||d.length===0?{}:{causes:d}}}o(B,"parseProviderError");function K(e){try{const t=JSON.parse(e);if(!y(ce,t.link)||typeof t.op!="string"||t.op===""||!y(pe,t.kind)||typeof t.message!="string"||typeof t.since!="string")return null;const r=X(t.operation_id);if(!b(t.retry)||!y(_e,t.retry.disposition)||t.retry.by!==void 0&&!y(le,t.retry.by)||t.retry.attempt!==void 0&&typeof t.retry.attempt!="number"||t.retry.max!==void 0&&typeof t.retry.max!="number"||t.retry.nextAt!==void 0&&typeof t.retry.nextAt!="string"||t.anchorEntryId!==void 0&&(typeof t.anchorEntryId!="string"||t.anchorEntryId===""))return null;const n=t.providerError===void 0?void 0:B(t.providerError);return t.providerError!==void 0&&n===void 0?null:{link:t.link,op:t.op,kind:t.kind,retry:{disposition:t.retry.disposition,...t.retry.by===void 0?{}:{by:t.retry.by},...t.retry.attempt===void 0?{}:{attempt:t.retry.attempt},...t.retry.max===void 0?{}:{max:t.retry.max},...t.retry.nextAt===void 0?{}:{nextAt:t.retry.nextAt}},message:t.message,since:t.since,operation_id:r,...t.anchorEntryId===void 0?{}:{anchorEntryId:t.anchorEntryId},...n===void 0?{}:{providerError:n}}}catch{return null}}o(K,"parseLegacyFault");function Te(e){const t=de(e);try{return oe(t)}catch{return t}}o(Te,"canonicalPath");function ge(e){try{const t=JSON.parse(e);if(!y(Ee,t.state)||typeof t.sessionFile!="string"||t.sessionFile==="")return null;const r=JSON.stringify(t.fault),n=K(r);if(n===null||n.link!=="pi\u2192provider"||n.retry.disposition!=="auto"||n.retry.by!=="daemon")return null;const i=t.anchorEntryId;return i!==null&&typeof i!="string"||(n.anchorEntryId??null)!==i?null:{state:t.state,sessionFile:Te(t.sessionFile),fault:n}}catch{return null}}o(ge,"parseLegacyEpisode");function J(e){return e!==void 0?X(e):M.current()??M.fresh(r=>r)}o(J,"resolveOperationId");function Ne(e){return e.slice(0,ue)}o(Ne,"clampMessage");function A(e){return e.link==="pi\u2192provider"&&e.retry.disposition==="auto"&&e.retry.by==="daemon"?3:e.retry.disposition==="fatal"?2:1}o(A,"faultPriority");function Y(e){return{link:e.link,op:e.op,kind:e.kind,message:e.message,since:e.since,operationId:e.operation_id,anchorEntryId:e.anchorEntryId??null,retryDisposition:e.retry.disposition,retryBy:e.retry.by??null,retryAttempt:e.retry.attempt??null,retryMax:e.retry.max??null,retryNextAt:e.retry.nextAt??null,providerError:e.providerError===void 0?null:JSON.stringify(e.providerError)}}o(Y,"encodeFault");function G(e){const t=e.provider_error_json,r=typeof t=="string"?B(JSON.parse(t)):void 0;if(typeof t=="string"&&r===void 0)throw new Error(`invalid provider error on stored fault ${e.fault_id}`);const n={disposition:e.retry_disposition,...e.retry_by===null?{}:{by:e.retry_by},...e.retry_attempt===null?{}:{attempt:e.retry_attempt},...e.retry_max===null?{}:{max:e.retry_max},...e.retry_next_at===null?{}:{nextAt:e.retry_next_at}};return{faultId:e.fault_id,link:e.link,op:e.op,kind:e.kind,message:e.message,since:e.since,operation_id:e.operation_id,retry:n,...e.anchor_entry_id===null?{}:{anchorEntryId:e.anchor_entry_id},...r===void 0?{}:{providerError:r}}}o(G,"hydrateFault");function I(e,t){const r=e.prepare("SELECT * FROM node_faults WHERE node_id = ? AND active = 1").get(t);return r===void 0?null:G(r)}o(I,"activeFault");function V(e){if(!y(j,e.link))throw new Error(`fault link is not durable: ${e.link}`);return{link:e.link,op:e.op,kind:e.kind,retry:e.retry,message:Ne(e.message),since:e.since??a(),operation_id:J(e.operation_id),...typeof e.anchorEntryId=="string"&&e.anchorEntryId!==""?{anchorEntryId:e.anchorEntryId}:{},...e.providerError===void 0?{}:{providerError:e.providerError}}}o(V,"storedFault");function Q(e,t){w({level:t.retry.disposition==="fatal"?"error":"warn",event:"fault.recorded",operation_id:t.operation_id,node_id:e,error_class:P(t),fields:{link:t.link,op:t.op,kind:t.kind,retry:t.retry,message:t.message,...t.anchorEntryId===void 0?{}:{anchor_entry_id:t.anchorEntryId},...t.providerError===void 0?{}:{provider_error:t.providerError}}})}o(Q,"emitRecordedFault");function F(e,t,r,n,i){const d=k(),s=Y(r),c=a();return e.prepare("INSERT INTO node_faults (fault_id, node_id, link, op, kind, message, since, operation_id, anchor_entry_id, retry_disposition, retry_by, retry_attempt, retry_max, retry_next_at, provider_error_json, active, episode_state, episode_binding_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run(d,t,s.link,s.op,s.kind,s.message,s.since,s.operationId,s.anchorEntryId,s.retryDisposition,s.retryBy,s.retryAttempt,s.retryMax,s.retryNextAt,s.providerError,n?1:0,i?.state??null,i?.bindingId??null,c,c),{...r,faultId:d}}o(F,"insertFault");function q(e,t,r,n){const i=Y(r);return e.prepare("UPDATE node_faults SET link = ?, op = ?, kind = ?, message = ?, since = ?, operation_id = ?, anchor_entry_id = ?, retry_disposition = ?, retry_by = ?, retry_attempt = ?, retry_max = ?, retry_next_at = ?, provider_error_json = ?, episode_state = COALESCE(?, episode_state), episode_binding_id = COALESCE(?, episode_binding_id), updated_at = ? WHERE fault_id = ?").run(i.link,i.op,i.kind,i.message,i.since,i.operationId,i.anchorEntryId,i.retryDisposition,i.retryBy,i.retryAttempt,i.retryMax,i.retryNextAt,i.providerError,n?.state??null,n?.bindingId??null,a(),t),{...r,faultId:t}}o(q,"updateFault");function R(e,t){e.prepare("DELETE FROM node_faults WHERE fault_id = ? AND active = 0 AND episode_state IS NULL").run(t)}o(R,"deleteIfUnretained");function z(e,t,r,n){const i=I(e,t);if(i===null){const d=e.prepare("SELECT fault_id, link, kind, since, operation_id FROM node_faults WHERE node_id = ? AND active = 0 AND episode_state IS NOT NULL").get(t);return d!==void 0&&d.link===r.link&&d.kind===r.kind&&d.since===r.since&&d.operation_id===r.operation_id?(e.prepare("UPDATE node_faults SET active = 1, updated_at = ? WHERE fault_id = ?").run(a(),d.fault_id),q(e,d.fault_id,r)):F(e,t,r,!0,n)}if(i.link===r.link&&i.kind===r.kind){const d={...i.retry,...r.retry};return r.retry.disposition==="fatal"&&!("nextAt"in r.retry)&&delete d.nextAt,q(e,i.faultId,{...r,since:i.since,anchorEntryId:i.anchorEntryId??r.anchorEntryId,retry:d},n)}return A(i)>A(r)?null:(e.prepare("UPDATE node_faults SET active = 0, updated_at = ? WHERE fault_id = ?").run(a(),i.faultId),R(e,i.faultId),F(e,t,r,!0,n))}o(z,"replaceActiveFault");function g(e,t,r){return e.prepare("SELECT binding_id FROM sessions WHERE node_id = ? AND session_id = ? AND unbound_at IS NULL").get(t,r)?.binding_id??null}o(g,"liveBinding");function Ce(e,t){return p(r=>{const n=a();return r.prepare("UPDATE executions SET turn_progress_at = ? WHERE execution_id = ? AND state != 'exited' AND turn_open_at IS NOT NULL AND turn_closed_at IS NULL").run(n,e).changes>0?!0:r.prepare("UPDATE executions SET turn_open_at = ?, turn_progress_at = ?, turn_closed_at = NULL, turn_end_reason = NULL WHERE execution_id = ? AND state != 'exited'").run(n,n,e).changes>0})}o(Ce,"openTurn");function be(e){return p(t=>t.prepare("UPDATE executions SET turn_progress_at = ? WHERE execution_id = ? AND state != 'exited' AND turn_open_at IS NOT NULL AND turn_closed_at IS NULL").run(a(),e).changes>0)}o(be,"recordTurnProgress");function Fe(e,t){return p(r=>r.prepare("UPDATE executions SET turn_closed_at = ?, turn_end_reason = ? WHERE execution_id = ? AND state != 'exited' AND turn_open_at IS NOT NULL AND turn_closed_at IS NULL").run(a(),t.endReason,e).changes>0)}o(Fe,"closeTurn");function He(e,t){if(!Number.isInteger(t.consecutivePasses)||t.consecutivePasses<1||t.routes.length===0||!t.routes.every(r=>r!==""))throw new Error("invalid transport-poison verdict");return p(r=>r.prepare("UPDATE executions SET exit_diagnostic_json = ? WHERE execution_id = ? AND state != 'exited'").run(JSON.stringify({schema:"crtr.execution-exit-diagnostic/v1",transport_poison:{consecutive_passes:t.consecutivePasses,routes:t.routes}}),e).changes>0)}o(He,"recordTransportPoisonVerdict");function We(e){const t=T().prepare("SELECT exit_diagnostic_json FROM executions WHERE node_id = ? ORDER BY rowid DESC LIMIT 1").get(e);if(t===void 0||t.exit_diagnostic_json===null)return null;try{const r=JSON.parse(t.exit_diagnostic_json),n=r.transport_poison;return r.schema!=="crtr.execution-exit-diagnostic/v1"||n===void 0||!Number.isInteger(n.consecutive_passes)||n.consecutive_passes<1||!Array.isArray(n.routes)||n.routes.length===0||!n.routes.every(i=>typeof i=="string"&&i!=="")?null:{consecutivePasses:n.consecutive_passes,routes:n.routes}}catch{return null}}o(We,"readTransportPoisonVerdict");function Pe(e){const t=T().prepare("SELECT turn_open_at, turn_closed_at, turn_progress_at FROM executions WHERE node_id = ? AND state != 'exited'").get(e);return t===void 0?null:{busy:t.turn_open_at!==null&&t.turn_closed_at===null,progressAt:t.turn_progress_at}}o(Pe,"readLiveTurn");function we(e){const t=T(),r=t.prepare("SELECT turn_closed_at, turn_progress_at FROM executions WHERE node_id = ? AND turn_open_at IS NOT NULL ORDER BY rowid DESC LIMIT 1").get(e);if(r===void 0)return null;const n=t.prepare("SELECT turn_end_reason FROM executions WHERE node_id = ? ORDER BY rowid DESC LIMIT 1").get(e);return{interrupted:r.turn_closed_at===null||n?.turn_end_reason==="teardown-aborted",progressAt:r.turn_progress_at}}o(we,"readInterruptedTurn");function Me(e,t){const r=V(t);return Q(e,r),p(n=>z(n,e,r))}o(Me,"recordFault");function Xe(e,t){return p(r=>{const n=I(r,e);if(n===null||t?.link!==void 0&&n.link!==t.link)return!1;const i=r.prepare("SELECT episode_state FROM node_faults WHERE fault_id = ?").get(n.faultId);r.prepare("UPDATE node_faults SET active = 0, updated_at = ? WHERE fault_id = ?").run(a(),n.faultId),i.episode_state==="pending"&&t?.preserveEpisode!==!0&&r.prepare("UPDATE node_faults SET episode_state = NULL, episode_binding_id = NULL, updated_at = ? WHERE fault_id = ?").run(a(),n.faultId),R(r,n.faultId);const d=J();return w({level:"info",event:"fault.cleared",operation_id:d,node_id:e,error_class:P(n),fields:{cleared_fault_operation_id:n.operation_id,link:n.link,op:n.op,kind:n.kind,retry:n.retry,message:n.message,since:n.since,...n.anchorEntryId===void 0?{}:{anchor_entry_id:n.anchorEntryId}}}),!0})}o(Xe,"clearFault");function Z(e){return I(T(),e)}o(Z,"readFault");function je(e,t){const r=V(t.input);if(r.link!=="pi\u2192provider"||r.retry.disposition!=="auto"||r.retry.by!=="daemon")throw new Error("provider retry episode requires an automatic daemon provider fault");return Q(e,r),p(n=>{const i=g(n,e,t.sessionId);if(i===null)return null;const d=I(n,e);if(d!==null&&(d.link!==r.link||d.kind!==r.kind)&&A(d)>A(r))return null;const s=n.prepare("SELECT fault_id FROM node_faults WHERE node_id = ? AND episode_state IS NOT NULL").all(e);n.prepare("UPDATE node_faults SET episode_state = NULL, episode_binding_id = NULL, updated_at = ? WHERE node_id = ? AND episode_state IS NOT NULL").run(a(),e);for(const N of s)R(n,N.fault_id);const c=z(n,e,r,{state:"pending",bindingId:i});return c===null?null:(n.prepare("UPDATE node_faults SET episode_state = ?, episode_binding_id = ?, updated_at = ? WHERE fault_id = ?").run("pending",i,a(),c.faultId),c)})}o(je,"recordPendingEpisode");function $e(e,t){return p(r=>{const n=g(r,e,t.sessionId);return n===null?!1:r.prepare("UPDATE node_faults SET episode_state = 'admitted', retry_disposition = ?, retry_by = ?, retry_attempt = ?, retry_max = ?, retry_next_at = ?, updated_at = ? WHERE node_id = ? AND fault_id = ? AND episode_state = 'pending' AND episode_binding_id = ?").run(t.retry.disposition,t.retry.by??null,t.retry.attempt??null,t.retry.max??null,t.retry.nextAt??null,a(),e,t.faultId,n).changes>0})}o($e,"admitEpisode");function Be(e,t){return p(r=>{const n=g(r,e,t.sessionId);return n===null?!1:r.prepare("UPDATE node_faults SET episode_state = 'pending', updated_at = ? WHERE node_id = ? AND episode_state = 'admitted' AND episode_binding_id = ?").run(a(),e,n).changes>0})}o(Be,"reopenEpisode");function Ke(e,t){return p(r=>{const n=g(r,e,t.sessionId);return n===null?!1:r.prepare("UPDATE node_faults SET episode_state = 'invalidated', updated_at = ? WHERE node_id = ? AND episode_state != 'invalidated' AND episode_state IS NOT NULL AND episode_binding_id = ?").run(a(),e,n).changes>0})}o(Ke,"invalidateEpisode");function Je(e,t){return p(r=>{const n=g(r,e,t.sessionId);if(n===null)return!1;const i=r.prepare("SELECT fault_id FROM node_faults WHERE node_id = ? AND episode_binding_id = ? AND episode_state IS NOT NULL").all(e,n);if(i.length===0)return!1;r.prepare("UPDATE node_faults SET episode_state = NULL, episode_binding_id = NULL, updated_at = ? WHERE node_id = ? AND episode_binding_id = ? AND episode_state IS NOT NULL").run(a(),e,n);for(const d of i)R(r,d.fault_id);return!0})}o(Je,"clearEpisode");function Ye(e){const t=T().prepare("SELECT f.*, s.session_id FROM node_faults f JOIN sessions s ON s.binding_id = f.episode_binding_id WHERE f.node_id = ? AND f.episode_state IS NOT NULL").get(e);return t===void 0?null:{state:t.episode_state,sessionId:t.session_id,fault:G(t)}}o(Ye,"readEpisode");function Ge(e){const t=Z(e)===null;return{explanation(){if(!t)return null;const r=Z(e);if(r===null||r.link!=="crtr\u2192pi"||r.retry.disposition!=="fatal")return null;const n=r.op.trim(),i=r.message.trim();return n===""||i===""?null:`${n}: ${i}`}}}o(Ge,"beginBootFaultAttempt");function ee(e,t){try{return se(e).mtime.toISOString()}catch{return t}}o(ee,"markerMtime");function H(e,t){if(!_(e))return null;try{return ie(e,"utf8")}catch(r){if(!t)return null;throw new Error(`cannot import recovery input ${e}: ${r.message}`)}}o(H,"legacyFile");function Le(e,t){return e.prepare("SELECT execution_id, started_at FROM executions WHERE node_id = ? ORDER BY rowid DESC LIMIT 1").get(t)??null}o(Le,"latestExecution");function me(e,t){const n=$(e,"nodes").has("launched_at")?e.prepare("SELECT COALESCE(launched_at, created) AS started_at FROM nodes WHERE node_id = ?").get(t):e.prepare("SELECT created AS started_at FROM nodes WHERE node_id = ?").get(t);if(n===void 0)throw new Error(`cannot import recovery witness for unknown node ${t}`);const i=k();return e.prepare("INSERT INTO executions (execution_id, node_id, daemon_epoch, pid, process_identity, state, started_at, ended_at, exit_reason) VALUES (?, ?, NULL, NULL, NULL, 'exited', ?, ?, 'pre-consolidation')").run(i,t,n.started_at,n.started_at),{execution_id:i,started_at:n.started_at}}o(me,"syntheticExecution");function te(e,t,r,n,i){return F(e,t,r,n,i)}o(te,"insertMigratedFault");function xe(e,t,r,n){const i=e.prepare("SELECT binding_id, node_id FROM sessions WHERE transcript_path = ? AND unbound_at IS NULL").get(r);if(i!==void 0){if(i.node_id!==t)throw new Error(`cannot import provider retry ${n}: transcript is live-owned by ${i.node_id}`);return i.binding_id}const d=e.prepare("SELECT binding_id FROM sessions WHERE node_id = ? AND transcript_path = ? ORDER BY rowid DESC LIMIT 1").get(t,r);if(d!==void 0)return d.binding_id;const s=e.prepare("SELECT created FROM nodes WHERE node_id = ?").get(t);if(s===void 0)throw new Error(`cannot import provider retry ${n}: unknown node ${t}`);const c=k();return e.prepare("INSERT INTO sessions (binding_id, session_id, node_id, transcript_path, source_session_id, fork_entry_id, created_at, bound_at, unbound_at) VALUES (?, NULL, ?, ?, NULL, NULL, ?, ?, ?)").run(c,t,r,s.created,s.created,a()),c}o(xe,"migrationBinding");function Ve(e){if(fe(e),ye(e),!_(C()))return;const t=ne(C(),{withFileTypes:!0}).filter(n=>n.isDirectory()).sort((n,i)=>n.name.localeCompare(i.name)),r=e.prepare("SELECT 1 FROM nodes WHERE node_id = ?");for(const n of t){const i=n.name,d=ae(i),s=f(d,"busy"),c=f(d,"clean-abort"),N=f(d,"transport-poison.json"),h=f(d,"fault"),L=f(d,"provider-retry"),D=_(s),U=_(c);if(!D&&!U&&!_(N)&&!_(h)&&!_(L)||!_(f(C(),i,"meta.json"))&&r.get(i)===void 0)continue;const O=H(N,!1),m=H(h,!0),x=H(L,!0);if(!D&&!U&&O===null&&m===null&&x===null)continue;let l=Le(e,i);if(l===null&&(l=me(e,i)),D){const u=ee(s,l.started_at);e.prepare("UPDATE executions SET turn_open_at = ?, turn_progress_at = ?, turn_closed_at = NULL, turn_end_reason = NULL WHERE execution_id = ?").run(u,u,l.execution_id)}else if(U){const u=ee(c,l.started_at);e.prepare("UPDATE executions SET turn_open_at = ?, turn_progress_at = ?, turn_closed_at = ?, turn_end_reason = 'teardown-aborted' WHERE execution_id = ?").run(u,u,u,l.execution_id)}if(O!==null)try{const u=JSON.parse(O);Number.isInteger(u.consecutivePasses)&&u.consecutivePasses>=1&&Array.isArray(u.routes)&&u.routes.length>0&&u.routes.every(W=>typeof W=="string"&&W!=="")&&e.prepare("UPDATE executions SET exit_diagnostic_json = ? WHERE execution_id = ?").run(JSON.stringify({schema:"crtr.execution-exit-diagnostic/v1",transport_poison:{consecutive_passes:u.consecutivePasses,routes:u.routes}}),l.execution_id)}catch{}const S=m===null?null:K(m);if(m!==null&&S===null)throw new Error(`cannot import recovery fault ${h}`);const E=x===null?null:ge(x);if(x!==null&&E===null)throw new Error(`cannot import provider retry ${L}`);let v=S===null||S.link==="broker\u2194crtrd"?null:te(e,i,S,!0);if(E!==null){const u=xe(e,i,E.sessionFile,L);v!==null&&v.since===E.fault.since?e.prepare("UPDATE node_faults SET episode_state = ?, episode_binding_id = ?, updated_at = ? WHERE fault_id = ?").run(E.state,u,a(),v.faultId):te(e,i,E.fault,!1,{state:E.state,bindingId:u})}}}o(Ve,"migrateRecoveryWitnesses");export{ke as FAULT_QUIET_MS,$e as admitEpisode,Ge as beginBootFaultAttempt,Je as clearEpisode,Xe as clearFault,Fe as closeTurn,G as hydrateFault,Ke as invalidateEpisode,Ve as migrateRecoveryWitnesses,Ce as openTurn,B as parseProviderError,Ye as readEpisode,Z as readFault,we as readInterruptedTurn,Pe as readLiveTurn,We as readTransportPoisonVerdict,Me as recordFault,je as recordPendingEpisode,He as recordTransportPoisonVerdict,be as recordTurnProgress,Be as reopenEpisode};
|
|
26
|
+
`)}o(ye,"createFaultTable");function y(e,t){return typeof t=="string"&&e.includes(t)}o(y,"contains");function F(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}o(F,"isRecord");function B(e){if(!F(e)||typeof e.provider!="string"||e.provider===""||typeof e.model!="string"||e.model==="")return;const t=typeof e.status=="number"&&Number.isFinite(e.status)?e.status:void 0,r=typeof e.code=="string"||typeof e.code=="number"?e.code:void 0,n=typeof e.errno=="string"?e.errno:void 0,i=typeof e.body=="string"?e.body:void 0,d=Array.isArray(e.causes)?e.causes.flatMap(s=>{if(!F(s))return[];const c={...typeof s.message=="string"?{message:s.message}:{},...typeof s.status=="number"&&Number.isFinite(s.status)?{status:s.status}:{},...typeof s.code=="string"||typeof s.code=="number"?{code:s.code}:{},...typeof s.errno=="string"?{errno:s.errno}:{},...typeof s.body=="string"?{body:s.body}:{}};return Object.keys(c).length===0?[]:[c]}):void 0;return{provider:e.provider,model:e.model,...t===void 0?{}:{status:t},...r===void 0?{}:{code:r},...n===void 0?{}:{errno:n},...i===void 0?{}:{body:i},...d===void 0||d.length===0?{}:{causes:d}}}o(B,"parseProviderError");function K(e){try{const t=JSON.parse(e);if(!y(ce,t.link)||typeof t.op!="string"||t.op===""||!y(pe,t.kind)||typeof t.message!="string"||typeof t.since!="string")return null;const r=X(t.operation_id);if(!F(t.retry)||!y(_e,t.retry.disposition)||t.retry.by!==void 0&&!y(le,t.retry.by)||t.retry.attempt!==void 0&&typeof t.retry.attempt!="number"||t.retry.max!==void 0&&typeof t.retry.max!="number"||t.retry.nextAt!==void 0&&typeof t.retry.nextAt!="string"||t.anchorEntryId!==void 0&&(typeof t.anchorEntryId!="string"||t.anchorEntryId===""))return null;const n=t.providerError===void 0?void 0:B(t.providerError);return t.providerError!==void 0&&n===void 0?null:{link:t.link,op:t.op,kind:t.kind,retry:{disposition:t.retry.disposition,...t.retry.by===void 0?{}:{by:t.retry.by},...t.retry.attempt===void 0?{}:{attempt:t.retry.attempt},...t.retry.max===void 0?{}:{max:t.retry.max},...t.retry.nextAt===void 0?{}:{nextAt:t.retry.nextAt}},message:t.message,since:t.since,operation_id:r,...t.anchorEntryId===void 0?{}:{anchorEntryId:t.anchorEntryId},...n===void 0?{}:{providerError:n}}}catch{return null}}o(K,"parseLegacyFault");function Te(e){const t=de(e);try{return oe(t)}catch{return t}}o(Te,"canonicalPath");function ge(e){try{const t=JSON.parse(e);if(!y(Ee,t.state)||typeof t.sessionFile!="string"||t.sessionFile==="")return null;const r=JSON.stringify(t.fault),n=K(r);if(n===null||n.link!=="pi\u2192provider"||n.retry.disposition!=="auto"||n.retry.by!=="daemon")return null;const i=t.anchorEntryId;return i!==null&&typeof i!="string"||(n.anchorEntryId??null)!==i?null:{state:t.state,sessionFile:Te(t.sessionFile),fault:n}}catch{return null}}o(ge,"parseLegacyEpisode");function J(e){return e!==void 0?X(e):M.current()??M.fresh(r=>r)}o(J,"resolveOperationId");function Ne(e){return e.slice(0,ue)}o(Ne,"clampMessage");function A(e){return e.link==="pi\u2192provider"&&e.retry.disposition==="auto"&&e.retry.by==="daemon"?3:e.retry.disposition==="fatal"?2:1}o(A,"faultPriority");function Y(e){return{link:e.link,op:e.op,kind:e.kind,message:e.message,since:e.since,operationId:e.operation_id,anchorEntryId:e.anchorEntryId??null,retryDisposition:e.retry.disposition,retryBy:e.retry.by??null,retryAttempt:e.retry.attempt??null,retryMax:e.retry.max??null,retryNextAt:e.retry.nextAt??null,providerError:e.providerError===void 0?null:JSON.stringify(e.providerError)}}o(Y,"encodeFault");function G(e){const t=e.provider_error_json,r=typeof t=="string"?B(JSON.parse(t)):void 0;if(typeof t=="string"&&r===void 0)throw new Error(`invalid provider error on stored fault ${e.fault_id}`);const n={disposition:e.retry_disposition,...e.retry_by===null?{}:{by:e.retry_by},...e.retry_attempt===null?{}:{attempt:e.retry_attempt},...e.retry_max===null?{}:{max:e.retry_max},...e.retry_next_at===null?{}:{nextAt:e.retry_next_at}};return{faultId:e.fault_id,link:e.link,op:e.op,kind:e.kind,message:e.message,since:e.since,operation_id:e.operation_id,retry:n,...e.anchor_entry_id===null?{}:{anchorEntryId:e.anchor_entry_id},...r===void 0?{}:{providerError:r}}}o(G,"hydrateFault");function I(e,t){const r=e.prepare("SELECT * FROM node_faults WHERE node_id = ? AND active = 1").get(t);return r===void 0?null:G(r)}o(I,"activeFault");function V(e){if(!y(j,e.link))throw new Error(`fault link is not durable: ${e.link}`);return{link:e.link,op:e.op,kind:e.kind,retry:e.retry,message:Ne(e.message),since:e.since??a(),operation_id:J(e.operation_id),...typeof e.anchorEntryId=="string"&&e.anchorEntryId!==""?{anchorEntryId:e.anchorEntryId}:{},...e.providerError===void 0?{}:{providerError:e.providerError}}}o(V,"storedFault");function Q(e,t){w({level:t.retry.disposition==="fatal"?"error":"warn",event:"fault.recorded",operation_id:t.operation_id,node_id:e,error_class:P(t),fields:{link:t.link,op:t.op,kind:t.kind,retry:t.retry,message:t.message,...t.anchorEntryId===void 0?{}:{anchor_entry_id:t.anchorEntryId},...t.providerError===void 0?{}:{provider_error:t.providerError}}})}o(Q,"emitRecordedFault");function b(e,t,r,n,i){const d=k(),s=Y(r),c=a();return e.prepare("INSERT INTO node_faults (fault_id, node_id, link, op, kind, message, since, operation_id, anchor_entry_id, retry_disposition, retry_by, retry_attempt, retry_max, retry_next_at, provider_error_json, active, episode_state, episode_binding_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run(d,t,s.link,s.op,s.kind,s.message,s.since,s.operationId,s.anchorEntryId,s.retryDisposition,s.retryBy,s.retryAttempt,s.retryMax,s.retryNextAt,s.providerError,n?1:0,i?.state??null,i?.bindingId??null,c,c),{...r,faultId:d}}o(b,"insertFault");function q(e,t,r,n){const i=Y(r);return e.prepare("UPDATE node_faults SET link = ?, op = ?, kind = ?, message = ?, since = ?, operation_id = ?, anchor_entry_id = ?, retry_disposition = ?, retry_by = ?, retry_attempt = ?, retry_max = ?, retry_next_at = ?, provider_error_json = ?, episode_state = COALESCE(?, episode_state), episode_binding_id = COALESCE(?, episode_binding_id), updated_at = ? WHERE fault_id = ?").run(i.link,i.op,i.kind,i.message,i.since,i.operationId,i.anchorEntryId,i.retryDisposition,i.retryBy,i.retryAttempt,i.retryMax,i.retryNextAt,i.providerError,n?.state??null,n?.bindingId??null,a(),t),{...r,faultId:t}}o(q,"updateFault");function R(e,t){e.prepare("DELETE FROM node_faults WHERE fault_id = ? AND active = 0 AND episode_state IS NULL").run(t)}o(R,"deleteIfUnretained");function z(e,t,r,n){const i=I(e,t);if(i===null){const d=e.prepare("SELECT fault_id, link, kind, since, operation_id FROM node_faults WHERE node_id = ? AND active = 0 AND episode_state IS NOT NULL").get(t);return d!==void 0&&d.link===r.link&&d.kind===r.kind&&d.since===r.since&&d.operation_id===r.operation_id?(e.prepare("UPDATE node_faults SET active = 1, updated_at = ? WHERE fault_id = ?").run(a(),d.fault_id),q(e,d.fault_id,r)):b(e,t,r,!0,n)}if(i.link===r.link&&i.kind===r.kind){const d={...i.retry,...r.retry};return r.retry.disposition==="fatal"&&!("nextAt"in r.retry)&&delete d.nextAt,q(e,i.faultId,{...r,since:i.since,anchorEntryId:i.anchorEntryId??r.anchorEntryId,retry:d},n)}return A(i)>A(r)?null:(e.prepare("UPDATE node_faults SET active = 0, updated_at = ? WHERE fault_id = ?").run(a(),i.faultId),R(e,i.faultId),b(e,t,r,!0,n))}o(z,"replaceActiveFault");function g(e,t,r){return e.prepare("SELECT binding_id FROM sessions WHERE node_id = ? AND session_id = ? AND unbound_at IS NULL").get(t,r)?.binding_id??null}o(g,"liveBinding");function Ce(e,t){return p(r=>{const n=a();return r.prepare("UPDATE executions SET turn_progress_at = ? WHERE execution_id = ? AND state != 'exited' AND turn_open_at IS NOT NULL AND turn_closed_at IS NULL").run(n,e).changes>0?!0:r.prepare("UPDATE executions SET turn_open_at = ?, turn_progress_at = ?, turn_closed_at = NULL, turn_end_reason = NULL WHERE execution_id = ? AND state != 'exited'").run(n,n,e).changes>0})}o(Ce,"openTurn");function Fe(e){return p(t=>t.prepare("UPDATE executions SET turn_progress_at = ? WHERE execution_id = ? AND state != 'exited' AND turn_open_at IS NOT NULL AND turn_closed_at IS NULL").run(a(),e).changes>0)}o(Fe,"recordTurnProgress");function be(e,t){return p(r=>r.prepare("UPDATE executions SET turn_closed_at = ?, turn_end_reason = ? WHERE execution_id = ? AND state != 'exited' AND turn_open_at IS NOT NULL AND turn_closed_at IS NULL").run(a(),t.endReason,e).changes>0)}o(be,"closeTurn");function He(e,t){if(!Number.isInteger(t.consecutivePasses)||t.consecutivePasses<1||t.routes.length===0||!t.routes.every(r=>r!==""))throw new Error("invalid transport-poison verdict");return p(r=>r.prepare("UPDATE executions SET exit_diagnostic_json = ? WHERE execution_id = ? AND state != 'exited'").run(JSON.stringify({schema:"crtr.execution-exit-diagnostic/v1",transport_poison:{consecutive_passes:t.consecutivePasses,routes:t.routes}}),e).changes>0)}o(He,"recordTransportPoisonVerdict");function We(e){const t=T().prepare("SELECT exit_diagnostic_json FROM executions WHERE node_id = ? ORDER BY rowid DESC LIMIT 1").get(e);if(t===void 0||t.exit_diagnostic_json===null)return null;try{const r=JSON.parse(t.exit_diagnostic_json),n=r.transport_poison;return r.schema!=="crtr.execution-exit-diagnostic/v1"||n===void 0||!Number.isInteger(n.consecutive_passes)||n.consecutive_passes<1||!Array.isArray(n.routes)||n.routes.length===0||!n.routes.every(i=>typeof i=="string"&&i!=="")?null:{consecutivePasses:n.consecutive_passes,routes:n.routes}}catch{return null}}o(We,"readTransportPoisonVerdict");function Pe(e){const t=T().prepare("SELECT turn_open_at, turn_closed_at, turn_progress_at FROM executions WHERE node_id = ? AND state != 'exited'").get(e);return t===void 0?null:{busy:t.turn_open_at!==null&&t.turn_closed_at===null,progressAt:t.turn_progress_at}}o(Pe,"readLiveTurn");function we(e){const t=T(),r=t.prepare("SELECT turn_closed_at, turn_progress_at FROM executions WHERE node_id = ? AND turn_open_at IS NOT NULL ORDER BY rowid DESC LIMIT 1").get(e);if(r===void 0)return null;const n=t.prepare("SELECT turn_end_reason FROM executions WHERE node_id = ? ORDER BY rowid DESC LIMIT 1").get(e);return{interrupted:r.turn_closed_at===null||n?.turn_end_reason==="teardown-aborted",progressAt:r.turn_progress_at}}o(we,"readInterruptedTurn");function Me(e,t){const r=V(t);return Q(e,r),p(n=>z(n,e,r))}o(Me,"recordFault");function Xe(e,t){return p(r=>{const n=I(r,e);if(n===null||t?.link!==void 0&&n.link!==t.link)return!1;const i=r.prepare("SELECT episode_state FROM node_faults WHERE fault_id = ?").get(n.faultId);r.prepare("UPDATE node_faults SET active = 0, updated_at = ? WHERE fault_id = ?").run(a(),n.faultId),i.episode_state==="pending"&&t?.preserveEpisode!==!0&&r.prepare("UPDATE node_faults SET episode_state = NULL, episode_binding_id = NULL, updated_at = ? WHERE fault_id = ?").run(a(),n.faultId),R(r,n.faultId);const d=J();return w({level:"info",event:"fault.cleared",operation_id:d,node_id:e,error_class:P(n),fields:{cleared_fault_operation_id:n.operation_id,link:n.link,op:n.op,kind:n.kind,retry:n.retry,message:n.message,since:n.since,...n.anchorEntryId===void 0?{}:{anchor_entry_id:n.anchorEntryId}}}),!0})}o(Xe,"clearFault");function Z(e){return I(T(),e)}o(Z,"readFault");function je(e,t){const r=V(t.input);if(r.link!=="pi\u2192provider"||r.retry.disposition!=="auto"||r.retry.by!=="daemon")throw new Error("provider retry episode requires an automatic daemon provider fault");return Q(e,r),p(n=>{const i=g(n,e,t.sessionId);if(i===null)return null;const d=I(n,e);if(d!==null&&(d.link!==r.link||d.kind!==r.kind)&&A(d)>A(r))return null;const s=n.prepare("SELECT fault_id FROM node_faults WHERE node_id = ? AND episode_state IS NOT NULL").all(e);n.prepare("UPDATE node_faults SET episode_state = NULL, episode_binding_id = NULL, updated_at = ? WHERE node_id = ? AND episode_state IS NOT NULL").run(a(),e);for(const N of s)R(n,N.fault_id);const c=z(n,e,r,{state:"pending",bindingId:i});return c===null?null:(n.prepare("UPDATE node_faults SET episode_state = ?, episode_binding_id = ?, updated_at = ? WHERE fault_id = ?").run("pending",i,a(),c.faultId),c)})}o(je,"recordPendingEpisode");function $e(e,t){return p(r=>{if(r.prepare("SELECT 1 FROM nodes WHERE node_id = ? AND intent IS 'refresh'").get(e)!==void 0)return!1;const n=g(r,e,t.sessionId);return n===null?!1:r.prepare("UPDATE node_faults SET episode_state = 'admitted', retry_disposition = ?, retry_by = ?, retry_attempt = ?, retry_max = ?, retry_next_at = ?, updated_at = ? WHERE node_id = ? AND fault_id = ? AND episode_state = 'pending' AND episode_binding_id = ?").run(t.retry.disposition,t.retry.by??null,t.retry.attempt??null,t.retry.max??null,t.retry.nextAt??null,a(),e,t.faultId,n).changes>0})}o($e,"admitEpisode");function Be(e,t){return p(r=>{const n=g(r,e,t.sessionId);return n===null?!1:r.prepare("UPDATE node_faults SET episode_state = 'pending', updated_at = ? WHERE node_id = ? AND episode_state = 'admitted' AND episode_binding_id = ?").run(a(),e,n).changes>0})}o(Be,"reopenEpisode");function Ke(e,t){return p(r=>{const n=g(r,e,t.sessionId);return n===null?!1:r.prepare("UPDATE node_faults SET episode_state = 'invalidated', updated_at = ? WHERE node_id = ? AND episode_state != 'invalidated' AND episode_state IS NOT NULL AND episode_binding_id = ?").run(a(),e,n).changes>0})}o(Ke,"invalidateEpisode");function Je(e,t){return p(r=>{const n=g(r,e,t.sessionId);if(n===null)return!1;const i=r.prepare("SELECT fault_id FROM node_faults WHERE node_id = ? AND episode_binding_id = ? AND episode_state IS NOT NULL").all(e,n);if(i.length===0)return!1;r.prepare("UPDATE node_faults SET episode_state = NULL, episode_binding_id = NULL, updated_at = ? WHERE node_id = ? AND episode_binding_id = ? AND episode_state IS NOT NULL").run(a(),e,n);for(const d of i)R(r,d.fault_id);return!0})}o(Je,"clearEpisode");function Ye(e){const t=T().prepare("SELECT f.*, s.session_id FROM node_faults f JOIN sessions s ON s.binding_id = f.episode_binding_id WHERE f.node_id = ? AND f.episode_state IS NOT NULL").get(e);return t===void 0?null:{state:t.episode_state,sessionId:t.session_id,fault:G(t)}}o(Ye,"readEpisode");function Ge(e){const t=Z(e)===null;return{explanation(){if(!t)return null;const r=Z(e);if(r===null||r.link!=="crtr\u2192pi"||r.retry.disposition!=="fatal")return null;const n=r.op.trim(),i=r.message.trim();return n===""||i===""?null:`${n}: ${i}`}}}o(Ge,"beginBootFaultAttempt");function ee(e,t){try{return se(e).mtime.toISOString()}catch{return t}}o(ee,"markerMtime");function H(e,t){if(!_(e))return null;try{return ie(e,"utf8")}catch(r){if(!t)return null;throw new Error(`cannot import recovery input ${e}: ${r.message}`)}}o(H,"legacyFile");function Le(e,t){return e.prepare("SELECT execution_id, started_at FROM executions WHERE node_id = ? ORDER BY rowid DESC LIMIT 1").get(t)??null}o(Le,"latestExecution");function me(e,t){const n=$(e,"nodes").has("launched_at")?e.prepare("SELECT COALESCE(launched_at, created) AS started_at FROM nodes WHERE node_id = ?").get(t):e.prepare("SELECT created AS started_at FROM nodes WHERE node_id = ?").get(t);if(n===void 0)throw new Error(`cannot import recovery witness for unknown node ${t}`);const i=k();return e.prepare("INSERT INTO executions (execution_id, node_id, daemon_epoch, pid, process_identity, state, started_at, ended_at, exit_reason) VALUES (?, ?, NULL, NULL, NULL, 'exited', ?, ?, 'pre-consolidation')").run(i,t,n.started_at,n.started_at),{execution_id:i,started_at:n.started_at}}o(me,"syntheticExecution");function te(e,t,r,n,i){return b(e,t,r,n,i)}o(te,"insertMigratedFault");function xe(e,t,r,n){const i=e.prepare("SELECT binding_id, node_id FROM sessions WHERE transcript_path = ? AND unbound_at IS NULL").get(r);if(i!==void 0){if(i.node_id!==t)throw new Error(`cannot import provider retry ${n}: transcript is live-owned by ${i.node_id}`);return i.binding_id}const d=e.prepare("SELECT binding_id FROM sessions WHERE node_id = ? AND transcript_path = ? ORDER BY rowid DESC LIMIT 1").get(t,r);if(d!==void 0)return d.binding_id;const s=e.prepare("SELECT created FROM nodes WHERE node_id = ?").get(t);if(s===void 0)throw new Error(`cannot import provider retry ${n}: unknown node ${t}`);const c=k();return e.prepare("INSERT INTO sessions (binding_id, session_id, node_id, transcript_path, source_session_id, fork_entry_id, created_at, bound_at, unbound_at) VALUES (?, NULL, ?, ?, NULL, NULL, ?, ?, ?)").run(c,t,r,s.created,s.created,a()),c}o(xe,"migrationBinding");function Ve(e){if(fe(e),ye(e),!_(C()))return;const t=ne(C(),{withFileTypes:!0}).filter(n=>n.isDirectory()).sort((n,i)=>n.name.localeCompare(i.name)),r=e.prepare("SELECT 1 FROM nodes WHERE node_id = ?");for(const n of t){const i=n.name,d=ae(i),s=f(d,"busy"),c=f(d,"clean-abort"),N=f(d,"transport-poison.json"),h=f(d,"fault"),L=f(d,"provider-retry"),D=_(s),U=_(c);if(!D&&!U&&!_(N)&&!_(h)&&!_(L)||!_(f(C(),i,"meta.json"))&&r.get(i)===void 0)continue;const O=H(N,!1),m=H(h,!0),x=H(L,!0);if(!D&&!U&&O===null&&m===null&&x===null)continue;let l=Le(e,i);if(l===null&&(l=me(e,i)),D){const u=ee(s,l.started_at);e.prepare("UPDATE executions SET turn_open_at = ?, turn_progress_at = ?, turn_closed_at = NULL, turn_end_reason = NULL WHERE execution_id = ?").run(u,u,l.execution_id)}else if(U){const u=ee(c,l.started_at);e.prepare("UPDATE executions SET turn_open_at = ?, turn_progress_at = ?, turn_closed_at = ?, turn_end_reason = 'teardown-aborted' WHERE execution_id = ?").run(u,u,u,l.execution_id)}if(O!==null)try{const u=JSON.parse(O);Number.isInteger(u.consecutivePasses)&&u.consecutivePasses>=1&&Array.isArray(u.routes)&&u.routes.length>0&&u.routes.every(W=>typeof W=="string"&&W!=="")&&e.prepare("UPDATE executions SET exit_diagnostic_json = ? WHERE execution_id = ?").run(JSON.stringify({schema:"crtr.execution-exit-diagnostic/v1",transport_poison:{consecutive_passes:u.consecutivePasses,routes:u.routes}}),l.execution_id)}catch{}const S=m===null?null:K(m);if(m!==null&&S===null)throw new Error(`cannot import recovery fault ${h}`);const E=x===null?null:ge(x);if(x!==null&&E===null)throw new Error(`cannot import provider retry ${L}`);let v=S===null||S.link==="broker\u2194crtrd"?null:te(e,i,S,!0);if(E!==null){const u=xe(e,i,E.sessionFile,L);v!==null&&v.since===E.fault.since?e.prepare("UPDATE node_faults SET episode_state = ?, episode_binding_id = ?, updated_at = ? WHERE fault_id = ?").run(E.state,u,a(),v.faultId):te(e,i,E.fault,!1,{state:E.state,bindingId:u})}}}o(Ve,"migrateRecoveryWitnesses");export{ke as FAULT_QUIET_MS,$e as admitEpisode,Ge as beginBootFaultAttempt,Je as clearEpisode,Xe as clearFault,be as closeTurn,G as hydrateFault,Ke as invalidateEpisode,Ve as migrateRecoveryWitnesses,Ce as openTurn,B as parseProviderError,Ye as readEpisode,Z as readFault,we as readInterruptedTurn,Pe as readLiveTurn,We as readTransportPoisonVerdict,Me as recordFault,je as recordPendingEpisode,He as recordTransportPoisonVerdict,Fe as recordTurnProgress,Be as reopenEpisode};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var R=Object.defineProperty;var d=(r,i)=>R(r,"name",{value:i,configurable:!0});import{rmSync as
|
|
1
|
+
var R=Object.defineProperty;var d=(r,i)=>R(r,"name",{value:i,configurable:!0});import{rmSync as h}from"node:fs";import{join as S}from"node:path";import{deleteNodes as w}from"../canvas/canvas.js";import{openDb as f,withCanvasWrite as c}from"../canvas/db.js";import{cancelCron as O}from"../canvas/crons.js";import{emitEvent as m}from"../events/emit.js";import{appSpace as D,appsRoot as p,secretsDir as T}from"../layout.js";import{notifyRunDeleted as C}from"../runs/events.js";import{cancelOpenQuestions as N}from"../runs/questions.js";import{removeTreeBeneath as L}from"../spaces/open-beneath.js";import{markGranteeUploadsDeletePending as W,releaseRunUploads as b,turnSharesOffFor as F}from"../storage/tables.js";import{stopNodeSandboxes as H}from"../spaces/stop.js";import{activeAppUser as E,markAppUserRemoved as A}from"../spaces/users.js";import{launcherRequest as M}from"../../launcher/client.js";import{LaunchRefused as y}from"../../launcher/spec.js";import{allGrants as I}from"./mirror.js";import{launcherPresent as u,listSandboxes as P,stopSandboxPid as U}from"./sandboxes.js";let g=Promise.resolve();function te(r=new Set){const i=g.then(async()=>{const n=I(),o=new Set(n.filter(t=>t.status==="removed").map(t=>t.grantee));for(const t of o)if(!n.some(a=>a.grantee===t&&a.status==="active")&&!(E(t)===null&&!r.has(t)))try{await x(t)}catch(a){m({level:"error",event:"daemon.grants.removal_failed",error:a,fields:{grantee:t}})}});return g=i.catch(()=>{}),i}d(te,"runRemovalCascades");async function x(r){if(r==="app:terminal")throw new Error("app:terminal cannot be removed");const i=D(r),n=E(r);m({level:"info",event:"daemon.grants.removal_started",fields:{grantee:r,uid:n?.uid??null}}),await N({grantee:r},"the app was removed");const o=c(e=>(e.prepare("UPDATE nodes SET deleting_at = COALESCE(deleting_at, ?) WHERE grantee = ?").run(new Date().toISOString(),r),e.prepare("SELECT node_id FROM nodes WHERE grantee = ? ORDER BY created DESC").all(r).map(s=>s.node_id)));for(const{cron_id:e}of f().prepare("SELECT cron_id FROM crons WHERE grantee = ?").all(r))O(e);const a=(await Promise.all(o.map(e=>H(e,"app_removed",{deleting:!0})))).filter(e=>!e.confirmed);if(a.length)throw new Error(`sandbox teardown could not be confirmed for ${a.length} node(s) of ${r}`);if(n&&u())for(const e of await P())e.uid===n.uid&&await U(e.pid);const v=o.filter(e=>f().prepare("SELECT run_id FROM nodes WHERE node_id = ?").get(e)?.run_id===e);c(e=>{if(F(r,e),b(o,e),W(r,e),!o.length)return;const s=o.map(()=>"?").join(","),_=e.prepare(`SELECT message_id FROM messages WHERE sender_node_id IN (${s}) UNION SELECT message_id FROM deliveries WHERE destination_kind = 'node_inbox' AND destination_id IN (${s})`).all(...o,...o);e.prepare(`DELETE FROM deliveries WHERE destination_kind = 'node_inbox' AND destination_id IN (${s})`).run(...o);for(const{message_id:l}of _)e.prepare("DELETE FROM messages WHERE message_id = ? AND NOT EXISTS (SELECT 1 FROM deliveries WHERE message_id = ?)").run(l,l)}),w(o);for(const e of v)C(e);if(h(S(T(),"env",i.root.slice(p().length+1)),{recursive:!0,force:!0}),L(p(),i.root),n&&u())try{await M({operation:"user.delete",uid:n.uid})}catch(e){if(!(e instanceof y&&e.refusal==="invalid_uid"))throw e}n&&A(n.uid),m({level:"info",event:"daemon.grants.removal_completed",fields:{grantee:r,uid:n?.uid??null,nodes:o.length}})}d(x,"removeApp");export{x as removeApp,te as runRemovalCascades};
|
|
@@ -1,5 +1,26 @@
|
|
|
1
|
+
import { type JsonWebKey } from 'node:crypto';
|
|
1
2
|
import { type Detach } from './on-sync.js';
|
|
2
3
|
export declare const GRANT_SYNC_INTERVAL_S = 300;
|
|
4
|
+
export interface RuntimeClient {
|
|
5
|
+
jwk: JsonWebKey & {
|
|
6
|
+
kid: string;
|
|
7
|
+
};
|
|
8
|
+
kid: string;
|
|
9
|
+
alg: 'ES256';
|
|
10
|
+
client_id: string;
|
|
11
|
+
runtime: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function runtimeClient(): RuntimeClient;
|
|
14
|
+
/** The runtime's id as storage names it: the directory principal id without `runtime:`. */
|
|
15
|
+
export declare function runtimeId(client?: RuntimeClient): string;
|
|
16
|
+
export interface DirectoryToken {
|
|
17
|
+
access_token: string;
|
|
18
|
+
expires_at: number;
|
|
19
|
+
}
|
|
20
|
+
/** A `client_credentials` token for the runtime client. With `audience`, the
|
|
21
|
+
* directory issues it for that audience (the storage service); without, for
|
|
22
|
+
* the directory's own `/v1`. `expires_at` is epoch ms. */
|
|
23
|
+
export declare function mintDirectoryToken(issuer: string, audience?: string): Promise<DirectoryToken>;
|
|
3
24
|
export type GrantSync = (options?: {
|
|
4
25
|
fresh?: boolean;
|
|
5
26
|
}) => Promise<void>;
|
package/dist/core/grants/sync.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var g=Object.defineProperty;var o=(e,r)=>g(e,"name",{value:r,configurable:!0});import{createPrivateKey as _,randomUUID as k,sign as w}from"node:crypto";import{readFileSync as h}from"node:fs";import{join as v}from"node:path";import{crtrHome as S}from"../canvas/paths.js";import{emitEvent as y}from"../events/emit.js";import{allGrants as b,replaceGrants as E}from"./mirror.js";import{onGrantSync as j}from"./on-sync.js";const C=300;function A(e,r){const t=Math.floor(Date.now()/1e3),i=Buffer.from(JSON.stringify({alg:"ES256",typ:"JWT",kid:e.kid})).toString("base64url"),a=Buffer.from(JSON.stringify({iss:e.client_id,sub:e.client_id,aud:r,iat:t,exp:t+300,jti:k()})).toString("base64url"),n=`${i}.${a}`,s=w("sha256",Buffer.from(n),{key:_({key:e.jwk,format:"jwk"}),dsaEncoding:"ieee-p1363"});return`${n}.${s.toString("base64url")}`}o(A,"assertion");function l(){const e=JSON.parse(h(v(S(),"secrets","runtime-client.json"),"utf8"));if(!e.jwk||e.alg!=="ES256"||!e.kid||!e.client_id||!e.runtime||e.jwk.kid!==e.kid||e.jwk.kty!=="EC"||e.jwk.crv!=="P-256"||!e.jwk.d)throw new TypeError("invalid runtime client key");return e}o(l,"runtimeClient");function F(e=l()){return e.runtime.startsWith("runtime:")?e.runtime.slice(8):e.runtime}o(F,"runtimeId");async function $(e,r){const t=l(),i=`${e.replace(/\/$/,"")}/token`,a=new URLSearchParams({grant_type:"client_credentials",client_id:t.client_id,client_assertion_type:"urn:ietf:params:oauth:client-assertion-type:jwt-bearer",client_assertion:A(t,i),...r===void 0?{}:{audience:r}}),n=await fetch(i,{method:"POST",body:a,signal:AbortSignal.timeout(1e4)});if(!n.ok)throw new Error(`directory token request failed: ${n.status}`);const s=await n.json();if(typeof s.access_token!="string")throw new TypeError("directory token response has no access_token");return{access_token:s.access_token,expires_at:T(s.access_token,s.expires_in)}}o($,"mintDirectoryToken");function T(e,r){if(typeof r=="number"&&Number.isFinite(r)&&r>0)return Date.now()+r*1e3;try{const t=JSON.parse(Buffer.from(e.split(".")[1]??"","base64url").toString("utf8"));if(typeof t.exp=="number")return t.exp*1e3}catch{}return Date.now()+3e5}o(T,"tokenExpiry");function x(e,r){if(!e||typeof e!="object")throw new TypeError("invalid grant sync response");const t=e;if(t.runtime!==(r.runtime.startsWith("runtime:")?r.runtime:`runtime:${r.runtime}`)||typeof t.id!="string"||typeof t.sub!="string"||typeof t.grantee!="string"||!Number.isInteger(t.rev)||!Array.isArray(t.scopes)||!Array.isArray(t.resolved_scopes)||!["active","revoked","removed"].includes(t.status??"")||typeof t.synced_at!="string")throw new TypeError("invalid grant sync response");const a=e.revisions?.find(n=>n.rev===t.rev)?.approved_at;if(typeof a!="string")throw new TypeError("grant revision has no approval time");return{...t,spend_cap:t.spend_cap??null,expires_at:t.expires_at??null,revoked_at:t.revoked_at??null,approved_at:a,payer:"user"}}o(x,"responseGrant");function U(e,r=t=>{}){let t;const i=e.replace(/\/$/,""),a=o(async()=>{const c=l(),u=await $(i),f=await fetch(`${i}/v1/runtime/grants`,{headers:{Authorization:`Bearer ${u.access_token}`},signal:AbortSignal.timeout(1e4)});if(!f.ok)throw new Error(`directory grant sync failed: ${f.status}`);const p=await f.json();if(!Array.isArray(p.grants)||typeof p.synced_at!="string")throw new TypeError("invalid grant sync response");const m=b();E(p.grants.map(d=>x(d,c)),p.synced_at,i),y({level:"info",event:"daemon.grants.synced",fields:{grants:p.grants.length,synced_at:p.synced_at}});try{await j(m,r)}catch(d){y({level:"error",event:"daemon.grants.on_sync_failed",error:d})}},"run");let n;const s=o(()=>{const c=a().catch(u=>{throw y({level:"error",event:"daemon.grants.sync_failed",error:u}),u}).finally(()=>{t===c&&(t=void 0)});return t=c,c},"start");return(c={})=>t?c.fresh?(n??=t.catch(()=>{}).then(()=>(n=void 0,t??s())),n):t:s()}o(U,"createGrantSync");export{C as GRANT_SYNC_INTERVAL_S,U as createGrantSync,$ as mintDirectoryToken,l as runtimeClient,F as runtimeId};
|
|
@@ -186,8 +186,8 @@ export declare const chartDataRowsSchema: z.ZodArray<z.ZodRecord<z.ZodString, z.
|
|
|
186
186
|
export declare const chartConfigSchema: z.ZodObject<{
|
|
187
187
|
kind: z.ZodEnum<{
|
|
188
188
|
line: "line";
|
|
189
|
-
area: "area";
|
|
190
189
|
bar: "bar";
|
|
190
|
+
area: "area";
|
|
191
191
|
}>;
|
|
192
192
|
x: z.ZodString;
|
|
193
193
|
y: z.ZodArray<z.ZodObject<{
|
|
@@ -95,6 +95,9 @@ export declare function getRequestByInboxTicket(ticketId: string): HumanRequestR
|
|
|
95
95
|
export declare function getRequestByDirectory(dir: string): HumanRequestRecord | null;
|
|
96
96
|
export declare function getRequestByCompanion(nodeId: string): HumanRequestRecord | null;
|
|
97
97
|
export declare function getRequestByIdempotencyKey(key: string): HumanRequestRecord | null;
|
|
98
|
+
/** A page asked from a run of an app other than the terminal is that app's to show
|
|
99
|
+
* (daemon-operations §question.*: the terminal shows `app:terminal` runs' questions). */
|
|
100
|
+
export declare const NOT_OTHER_APP_QUESTION = "NOT (kind = 'page' AND EXISTS (SELECT 1 FROM nodes n WHERE n.node_id = human_requests.requester_node_id\n AND n.run_id IS NOT NULL AND n.grantee IS NOT NULL AND n.grantee <> 'app:terminal'))";
|
|
98
101
|
export declare function listOpenInboxRequests(): HumanRequestRecord[];
|
|
99
102
|
export declare function openRequestsByRequester(nodeId: string): HumanRequestRecord[];
|
|
100
103
|
export declare function hasOpenReplyBearingRequestByRequester(nodeId: string): boolean;
|