@north-light/cli 0.3.459 → 0.3.461

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- var a=Object.defineProperty;var n=(t,r)=>a(t,"name",{value:r,configurable:!0});import{defineLeaf as s}from"../../core/command.js";import{existsSync as o}from"node:fs";import{joinDirectory as d}from"../../core/directory/join.js";import{apiSocketPath as u}from"../../core/canvas/paths.js";import{listProcessTable as l}from"../../core/canvas/pid.js";import{daemonAnswers as c}from"../../core/layout-migrate/index.js";import{envNodeId as m}from"../../shared/env.js";import{isDaemonRunning as h}from"../../daemon/pidfile.js";import{findDaemonPids as p,spawnDaemon as f,stopAllDaemons as w}from"../../daemon/manage.js";function y(){const t=l();if(t===null||(m()??"")!=="")return!0;const r=new Map(t.map(e=>[e.pid,e])),i=new Set;for(let e=process.ppid;e>1&&!i.has(e);e=r.get(e)?.ppid??0)if(i.add(e),r.get(e)?.command.includes("broker-cli"))return!0;return!1}n(y,"insideBroker");function g(){if(!(process.platform==="linux"&&o("/run/crtr/launcher.sock")))return{running:n(async()=>h()||p().length>0||await c(u()),"running"),stop:n(async()=>{await w()},"stop"),start:n(async()=>{await f()},"start"),insideNode:y}}n(g,"daemonControl");const D=s({name:"join",description:"register this runtime with its directory using an enrollment token, offline",whenToUse:"on your own computer, with a token from `<issuer>/account/machines`: run it from a plain terminal, with any viewers open. It stops the daemon, joins, and starts it again. Also at a hosted runtime's first boot, before the daemon starts, with the enrollment token provisioning passed in.",help:{name:"sys join",summary:"exchange a directory enrollment token for a new runtime client key and record the returned identity",guide:"Generates a new ES256 client key, sends only its public key to `PUT <directory>/v1/runtimes/registration` with the token as bearer, and learns the runtime id, client id, issuer, owner and URL from the answer. Only after that answer does it write the key file, set the user settings `runtime.issuer` and `runtime.audience`, and rewrite `user:local` profile grantees to `app:terminal`. canvas.db belongs to the daemon: its next start rewrites `user:local` there, before it serves anything. On your own computer a running daemon is no obstacle: join takes a `join.lock` in the runtime directory (so no open viewer's redial can autostart a daemon while it works), registers with the directory while the daemon still serves, stops the daemon, writes the identity, and starts the daemon again, whose start applies the canvas.db rewrite. Run it from a plain terminal, not from inside a node: a node's broker dies with the daemon, so join refuses `join_inside_node` instead. Where the launcher owns the daemon (a hosted container) join keeps the offline contract and refuses `daemon_running`. Presenting the token of the stored successful registration again finishes those local writes without calling the directory. Any other token generates a new key and registers again.",params:[{kind:"flag",name:"directory",type:"string",required:!0,constraint:"Directory base URL (http or https) that minted the token."},{kind:"flag",name:"token",type:"string",required:!0,sensitive:!0,constraint:"Single-use enrollment token. Sensitive: never echoed in output or errors."},{kind:"flag",name:"name",type:"string",required:!1,constraint:"Label shown for this machine on the account page. Default: this computer's hostname."}],output:[{name:"runtime",type:"string",required:!0,constraint:"Runtime principal from the directory, `runtime:<id>`."},{name:"client_id",type:"string",required:!0,constraint:"OAuth client id registered for the new key."},{name:"issuer",type:"string",required:!0,constraint:"Canonical issuer, written to `runtime.issuer`."},{name:"owner",type:"string",required:!0,constraint:"Owner principal, `user:<id>`, that replaces `user:local` subjects."},{name:"canvas_rewrite",type:"string",required:!0,constraint:"Always `next daemon start`: when canvas.db identity fields are rewritten."},{name:"url",type:"string",required:!0,constraint:"Runtime URL, written to `runtime.audience`."},{name:"registered",type:"boolean",required:!0,constraint:"False when the token matched the stored registration and the directory was not called."},{name:"daemon",type:"string",required:!0,constraint:"`restarted` (a daemon was stopped and started on the joined settings), `not_running` (none was running; none was started), or `restart_failed` (joined, but the new daemon did not start: see `daemon_error`, then run `crtr sys daemon start`)."},{name:"daemon_error",type:"string",required:!1,constraint:"Why the new daemon did not start, with `daemon: restart_failed`."}],outputKind:"object",effects:["Holds `<runtime>/join.lock` (its pid) from the start until the daemon is back; `ensureDaemon` and `sys daemon start` (without `--force`) decline to start a daemon while a live process holds it (including a start already in flight when the join began). A lock whose holder died is reclaimed.","A running daemon is stopped (SIGTERM, then SIGKILL, plus strays) after the directory accepts the registration and before the local writes, then started again. Every broker dies with it and is revived by the new daemon. If the join fails after the stop, the daemon is started again anyway. With no daemon running none is started: the next command's autostart does it. Refuses `join_inside_node` when run from inside a node and a daemon is running; refuses `daemon_running` where a container launcher owns the daemon.","On a new token: calls the directory once; on success writes `<runtime>/secrets/runtime-client.json` (mode 0600) with the private key, registration and the token's SHA-256, replacing any earlier key file. A refused or failed call writes nothing.","Sends name, platform, crtr version, installed plugin ids and sandbox state with the public key. Before the request it writes `runtime-client.pending.json` (mode 0600); a rerun with the same token and directory reuses that key, so a lost reply is retried safely. The file is deleted on success or a refusal.","Sets `runtime.issuer` and `runtime.audience` in user settings. When the directory says this machine is not the account's default (`is_default: false`) it replaces the whole `runtime` setting with `{issuer, audience, unsandboxed: true}`: no app listener, and only `app:terminal` nodes run.","Rewrites `user:local` profile grantees to `app:terminal`.","At every later daemon start, before any listener: exact `user:local` subjects in canvas.db nodes and node_tokens become the owner (as do missing subjects on `app:terminal` nodes), `user:local` grantees in nodes, run_events, idempotency_keys and node_tokens become `app:terminal`, references to the unjoined runtime's `local:terminal` grant are cleared, and that local grant mirror is deleted so grant sync installs the directory's. Application grantees and free text are unchanged; a start with nothing left to rewrite changes no rows."]},run:n(async t=>({...await d(t.directory,t.token,t.name||void 0,g()),canvas_rewrite:"next daemon start"}),"run")});export{D as sysJoinLeaf};
1
+ var s=Object.defineProperty;var n=(e,r)=>s(e,"name",{value:r,configurable:!0});import{defineLeaf as o}from"../../core/command.js";import{existsSync as a}from"node:fs";import{join as d}from"node:path";import{pluginsDir as l}from"../../core/scope.js";import{joinDirectory as u}from"../../core/directory/join.js";import{apiSocketPath as c}from"../../core/canvas/paths.js";import{listProcessTable as m}from"../../core/canvas/pid.js";import{daemonAnswers as h}from"../../core/layout-migrate/index.js";import{envNodeId as p}from"../../shared/env.js";import{isDaemonRunning as f}from"../../daemon/pidfile.js";import{findDaemonPids as w,spawnDaemon as y,stopAllDaemons as g}from"../../daemon/manage.js";function k(){const e=m();if(e===null||(p()??"")!=="")return!0;const r=new Map(e.map(t=>[t.pid,t])),i=new Set;for(let t=process.ppid;t>1&&!i.has(t);t=r.get(t)?.ppid??0)if(i.add(t),r.get(t)?.command.includes("broker-cli"))return!0;return!1}n(k,"insideBroker");function b(){if(!(process.platform==="linux"&&a("/run/crtr/launcher.sock")))return{running:n(async()=>f()||w().length>0||await h(c()),"running"),stop:n(async()=>{await g()},"stop"),start:n(async()=>{await y()},"start"),firstSync:n(async()=>{const e=l("user"),r=Date.now()+2e4;for(;e&&Date.now()<r&&!a(d(e,"cloud","commands.json"));)await new Promise(i=>setTimeout(i,250))},"firstSync"),insideNode:k}}n(b,"daemonControl");const T=o({name:"join",description:"register this runtime with its directory using an enrollment token, offline",whenToUse:"on your own computer, with a token from `<issuer>/account/machines`: run it from a plain terminal, with any viewers open. It stops the daemon, joins, and starts it again. Also at a hosted runtime's first boot, before the daemon starts, with the enrollment token provisioning passed in.",help:{name:"sys join",summary:"exchange a directory enrollment token for a new runtime client key and record the returned identity",guide:"Generates a new ES256 client key, sends only its public key to `PUT <directory>/v1/runtimes/registration` with the token as bearer, and learns the runtime id, client id, issuer, owner and URL from the answer. Only after that answer does it write the key file, set the user settings `runtime.issuer` and `runtime.audience`, and rewrite `user:local` profile grantees to `app:terminal`. canvas.db belongs to the daemon: its next start rewrites `user:local` there, before it serves anything. On your own computer a running daemon is no obstacle: join takes a `join.lock` in the runtime directory (so no open viewer's redial can autostart a daemon while it works), registers with the directory while the daemon still serves, stops the daemon, writes the identity, and starts the daemon again, whose start applies the canvas.db rewrite. Run it from a plain terminal, not from inside a node: a node's broker dies with the daemon, so join refuses `join_inside_node` instead. Where the launcher owns the daemon (a hosted container) join keeps the offline contract and refuses `daemon_running`. Presenting the token of the stored successful registration again finishes those local writes without calling the directory. Any other token generates a new key and registers again.",params:[{kind:"flag",name:"directory",type:"string",required:!0,constraint:"Directory base URL (http or https) that minted the token."},{kind:"flag",name:"token",type:"string",required:!0,sensitive:!0,constraint:"Single-use enrollment token. Sensitive: never echoed in output or errors."},{kind:"flag",name:"name",type:"string",required:!1,constraint:"Label shown for this machine on the account page. Default: this computer's hostname."}],output:[{name:"runtime",type:"string",required:!0,constraint:"Runtime principal from the directory, `runtime:<id>`."},{name:"client_id",type:"string",required:!0,constraint:"OAuth client id registered for the new key."},{name:"issuer",type:"string",required:!0,constraint:"Canonical issuer, written to `runtime.issuer`."},{name:"owner",type:"string",required:!0,constraint:"Owner principal, `user:<id>`, that replaces `user:local` subjects."},{name:"canvas_rewrite",type:"string",required:!0,constraint:"Always `next daemon start`: when canvas.db identity fields are rewritten."},{name:"url",type:"string",required:!0,constraint:"Runtime URL, written to `runtime.audience`."},{name:"registered",type:"boolean",required:!0,constraint:"False when the token matched the stored registration and the directory was not called."},{name:"daemon",type:"string",required:!0,constraint:"`restarted` (a daemon was stopped and started on the joined settings), `started` (none was running; join started one and waited, up to 20s, for its first grant sync to install the account's provider plugins, so `crtr cloud \u2026` resolves), `not_running` (none was started: a hosted container's launcher owns its daemon), or `restart_failed` (joined, but the new daemon did not start: see `daemon_error`, then run `crtr sys daemon start`)."},{name:"daemon_error",type:"string",required:!1,constraint:"Why the new daemon did not start, with `daemon: restart_failed`."}],outputKind:"object",effects:["Holds `<runtime>/join.lock` (its pid) from the start until the daemon is back; `ensureDaemon` and `sys daemon start` (without `--force`) decline to start a daemon while a live process holds it (including a start already in flight when the join began). A lock whose holder died is reclaimed.","A running daemon is stopped (SIGTERM, then SIGKILL, plus strays) after the directory accepts the registration and before the local writes, then started again. Every broker dies with it and is revived by the new daemon. If the join fails after the stop, the daemon is started again anyway. With no daemon running join starts one anyway, because a bare `crtr cloud \u2026` is an unknown command (it never autostarts a daemon) until a daemon's grant sync has installed the provider plugins. Refuses `join_inside_node` when run from inside a node and a daemon is running; refuses `daemon_running` where a container launcher owns the daemon.","On a new token: calls the directory once; on success writes `<runtime>/secrets/runtime-client.json` (mode 0600) with the private key, registration and the token's SHA-256, replacing any earlier key file. A refused or failed call writes nothing.","Sends name, platform, crtr version, installed plugin ids and sandbox state with the public key. Before the request it writes `runtime-client.pending.json` (mode 0600); a rerun with the same token and directory reuses that key, so a lost reply is retried safely. The file is deleted on success or a refusal.","Sets `runtime.issuer` and `runtime.audience` in user settings. When the directory says this machine is not the account's default (`is_default: false`) it replaces the whole `runtime` setting with `{issuer, audience, unsandboxed: true}`: no app listener, and only `app:terminal` nodes run.","Rewrites `user:local` profile grantees to `app:terminal`.","At every later daemon start, before any listener: exact `user:local` subjects in canvas.db nodes and node_tokens become the owner (as do missing subjects on `app:terminal` nodes), `user:local` grantees in nodes, run_events, idempotency_keys and node_tokens become `app:terminal`, references to the unjoined runtime's `local:terminal` grant are cleared, and that local grant mirror is deleted so grant sync installs the directory's. Application grantees and free text are unchanged; a start with nothing left to rewrite changes no rows."]},run:n(async e=>({...await u(e.directory,e.token,e.name||void 0,b()),canvas_rewrite:"next daemon start"}),"run")});export{T as sysJoinLeaf};
@@ -1,11 +1,11 @@
1
- var K=Object.defineProperty;var r=(e,n)=>K(e,"name",{value:n,configurable:!0});import{prepared as a}from"../sqlite-statements.js";import{envNodeId as k}from"../../shared/env.js";import{rmdirSync as Z,rmSync as ee,statSync as ne}from"node:fs";import{dirname as te,join as x,resolve as oe}from"node:path";import{removeTreeBeneath as W}from"../spaces/open-beneath.js";import{emitEvent as re}from"../events/emit.js";import{openDb as d,prepared as j,withCanvasWrite as c}from"./db.js";import{conversationStore as ie,ConversationNotFoundError as se}from"../conversation-store/index.js";import{hasUnseenWakeMail as ae}from"./deliveries.js";import{isAppRun as H}from"./app-run.js";import{registerOutcomeAction as F}from"./outcome-actions.js";import{publish as de}from"../graph/events.js";import{nodeObject as le}from"../graph/objects.js";import{findNodeBySessionFileSql as ue}from"./sessions.js";import{hydrateFault as _e}from"./recovery.js";import{hasUnreconciledWorktree as ce,openWorktree as pe,readWorktree as fe,worktreeFromRow as Ee}from"./worktrees.js";import{isPidAlive as me,isPidPresent as b,recordedPidLiveness as Ne}from"./pid.js";import{ensureHome as Re,jobDir as ye,nodeDir as we,nodeRunDir as I}from"./paths.js";import{appForNode as Se,ensureNodeDirs as ge,nodeAgentDir as Oe}from"./node-agent-paths.js";import{appSpace as C}from"../layout.js";import{createObject as Te,getById as U,ownerForGrantee as M,setObjectKind as he,setObjectName as Le}from"../graph/objects.js";import{deleteNodeWithOwned as De}from"../graph/deletion.js";function m(e,n){if(e==null)return null;const t=JSON.stringify(e);if(t===void 0)throw new TypeError(`${n} must be JSON-serializable`);return t}r(m,"jsonValue");function on(e,n){if(a(e,"SELECT deleting_at FROM nodes WHERE node_id = ?").get(n)?.deleting_at)throw new Error("run is deleting")}r(on,"assertRunNotDeleting");function ve(e,n){const t=M(n,e.grantee??"app:terminal");Te(n,{id:e.node_id,type:"node",kind:e.kind,owner:t.rowid,privacy:"public",name:e.name,preview:""}),a(n,`INSERT INTO nodes (node_id, description, title, icon, cycles, mode, lifecycle, cwd, host_kind, profile_id, parent, spawned_by, creator, fork_from, fork_source_file, created, passive_default, cycle_pending, model_override, persona_ack_json, review_binding_json, launch_config_json, scopes_json, isolated, status, intent, waiting_for, final_report, finalized_at, deadline_at, "window", tmux_session, pane, sub, grant_id, client_id, metadata_json, run_id, event_cursor, usage_json, deleting_at) VALUES (${Array(41).fill("?").join(", ")})`).run(e.node_id,e.description??null,e.title??null,e.icon??null,e.cycles??null,e.mode,e.lifecycle,e.cwd,e.host_kind??null,e.profile_id??null,e.parent??null,e.spawned_by??null,e.creator??null,e.fork_from??null,e.fork_source_file??null,e.created,e.passive_default===!0?1:0,e.cycle_pending===!0?1:0,e.model_override??null,m(e.persona_ack,"persona_ack"),m(e.review_binding,"review_binding"),m(e.launch,"launch"),m(e.scopes,"scopes"),e.isolated===!0?1:0,e.status??"active",e.intent??null,e.waiting_for??null,e.final_report??null,e.finalized_at??null,e.deadline_at??null,e.window??null,e.tmux_session??null,e.pane??null,e.sub??null,e.grant_id??null,e.client_id??null,e.metadata_json??null,e.run_id??null,e.event_cursor??null,e.usage_json??null,e.deleting_at??null)}r(ve,"seedRow");function P(e){if(e.fault_id==null)return null;const n=Object.entries(e).filter(([t])=>t.startsWith("fault_")).map(([t,o])=>[t==="fault_id"?t:t.slice(6),o]);return _e(Object.fromEntries(n))}r(P,"faultFrom");function f(e){const n={node_id:e.node_id,name:e.name,description:e.description??void 0,cycles:e.cycles??void 0,kind:e.kind,mode:e.mode,lifecycle:e.lifecycle,status:e.status,cwd:e.cwd,host_kind:e.host_kind??null,profile_id:e.profile_id??null,scopes:e.scopes_json==null?null:JSON.parse(e.scopes_json),isolated:e.isolated===1,grantee:e.grantee,sub:e.sub??null,grant_id:e.grant_id??null,client_id:e.client_id??null,metadata_json:e.metadata_json??null,run_id:e.run_id??null,event_cursor:e.event_cursor??null,last_activity_at:e.last_activity_at??null,usage_json:e.usage_json??null,last_turn_error_json:e.last_turn_error_json??null,deleting_at:e.deleting_at??null,parent:e.parent??null,created:e.created,intent:e.intent??null,waiting_for:e.waiting_for??null,frozen_at:e.frozen_at??null,respawn_failures:e.respawn_failures??0,respawn_not_before:e.respawn_not_before??null,launched_at:e.launched_at??null,last_settled_at:e.last_settled_at??null,terminal_reason:e.terminal_reason??null,final_report:e.final_report??null,finalized_at:e.finalized_at??null,outcome_kind:e.outcome_kind??null,outcome_reason:e.outcome_reason??null,outcome_at:e.outcome_at??null,outcome_revision:e.outcome_revision??0,outcome_detail_json:e.outcome_detail_json??null,deadline_at:e.deadline_at??null,telemetry_tokens_in:e.telemetry_tokens_in??null,telemetry_context_tokens:e.telemetry_context_tokens??null,telemetry_last_activity:e.telemetry_last_activity??null,telemetry_updated_at:e.telemetry_updated_at??null,pi_pid:e.pi_pid??null,pi_pid_identity:e.pi_pid_identity??null,window:e.window??null,tmux_session:e.tmux_session??null,pane:e.pane??null,fault:null,streaming:!1},t=b(n.pi_pid),o=P(e);return n.fault=o?.link==="pi\u2192provider"&&o.retry.by==="daemon"&&o.retry.disposition==="auto"&&!t?null:o,n.streaming=e.turn_open_at!==null&&e.turn_closed_at===null&&t,n}r(f,"rowFrom");function q(e,n=fe(e.node_id)){return{...f(e),title:e.title??void 0,icon:e.icon??void 0,spawned_by:e.spawned_by??null,creator:e.creator??null,fork_from:e.fork_from??null,fork_source_file:e.fork_source_file??null,passive_default:e.passive_default===1,cycle_pending:e.cycle_pending===1,model_override:e.model_override??null,scopes:e.scopes_json==null?null:JSON.parse(e.scopes_json),persona_ack:e.persona_ack_json==null?void 0:JSON.parse(e.persona_ack_json),review_binding:e.review_binding_json==null?void 0:JSON.parse(e.review_binding_json),launch:e.launch_config_json==null?void 0:JSON.parse(e.launch_config_json),pi_session_id:e.pi_session_id??null,pi_session_file:e.pi_session_file??null,...n===null?{}:{managed_worktree:n}}}r(q,"metaFrom");function rn(e,n={}){return Re(),c(t=>{ve(e,t),e.managed_worktree!==void 0&&e.managed_worktree!==null&&pe(e.node_id,e.managed_worktree,t),n.outcomeDelivery!==void 0&&F(t,e.node_id,n.outcomeDelivery),n.withinCreate?.(t,e)}),ge(e.node_id),R(e.node_id)}r(rn,"createNode");function R(e){const n=j(d(),"SELECT * FROM node_rows WHERE node_id = ?").get(e);return n===void 0?null:q(n)}r(R,"getNode");const Ae=1440*60*1e3;function $(e){return{requestKey:e.request_key,nodeId:e.node_id,state:e.state}}r($,"createRequestRecord");function sn(e){const n=d().prepare("SELECT request_key, node_id, state FROM create_requests WHERE request_key = ?").get(e);return n===void 0?null:$(n)}r(sn,"findCreateRequest");function an(e){return c(n=>{const t=Date.now();n.prepare("DELETE FROM create_requests WHERE expires_at <= ?").run(t);const o=n.prepare("SELECT request_key, node_id, state FROM create_requests WHERE request_key = ?").get(e);return o!==void 0?$(o):(n.prepare("INSERT INTO create_requests (request_key, node_id, expires_at, state) VALUES (?, NULL, ?, 'reserved')").run(e,t+Ae),{requestKey:e,nodeId:null,state:"reserved"})})}r(an,"reserveCreateRequest");function dn(e=Date.now()){return c(n=>Number(n.prepare("DELETE FROM create_requests WHERE expires_at <= ?").run(e).changes))}r(dn,"pruneExpiredCreateRequests");function ln(e,n){c(t=>{t.prepare("UPDATE create_requests SET node_id = ?, state = 'complete' WHERE request_key = ?").run(n,e)})}r(ln,"recordCreateRequestNode");function un(e,n){c(t=>{t.prepare("UPDATE create_requests SET node_id = ?, state = 'reserved' WHERE request_key = ?").run(n,e)})}r(un,"reserveCreateRequestNode");function _n(e,n){c(t=>{t.prepare("UPDATE create_requests SET state = 'complete' WHERE request_key = ? AND node_id = ?").run(e,n)})}r(_n,"completeCreateRequest");function cn(e,n){c(t=>{if(n===null){t.prepare("DELETE FROM create_requests WHERE request_key = ? AND node_id IS NULL").run(e);return}t.prepare("DELETE FROM create_requests WHERE request_key = ? AND node_id = ?").run(e,n)})}r(cn,"abandonCreateRequest");function pn(e){return oe(e)}r(pn,"canonicalSessionFile");function fn(e){const n=ue(e);return n===null?null:R(n)}r(fn,"findNodeBySessionFile");function En(e){const n=a(d(),"SELECT * FROM node_rows WHERE node_id = ?").get(e);return n?f(n):null}r(En,"getRow");function mn(e){const n=a(d(),"SELECT * FROM node_rows WHERE pane = ?").get(e);return n?f(n):null}r(mn,"getRowByPane");function Nn(e){return a(d(),`SELECT * FROM node_rows
1
+ var Z=Object.defineProperty;var r=(e,n)=>Z(e,"name",{value:n,configurable:!0});import{prepared as a}from"../sqlite-statements.js";import{envNodeId as k}from"../../shared/env.js";import{rmdirSync as ee,rmSync as ne,statSync as te}from"node:fs";import{dirname as oe,join as x,resolve as re}from"node:path";import{removeTreeBeneath as W}from"../spaces/open-beneath.js";import{emitEvent as ie}from"../events/emit.js";import{openDb as d,prepared as j,withCanvasWrite as p}from"./db.js";import{conversationStore as se,ConversationNotFoundError as ae}from"../conversation-store/index.js";import{hasUnseenWakeMail as de}from"./deliveries.js";import{isAppRun as H}from"./app-run.js";import{registerOutcomeAction as F}from"./outcome-actions.js";import{publish as le}from"../graph/events.js";import{nodeObject as ue}from"../graph/objects.js";import{findNodeBySessionFileSql as _e}from"./sessions.js";import{hydrateFault as ce}from"./recovery.js";import{hasUnreconciledWorktree as pe,openWorktree as fe,readWorktree as Ee,worktreeFromRow as me}from"./worktrees.js";import{isPidAlive as Ne,isPidPresent as b,recordedPidLiveness as Re}from"./pid.js";import{ensureHome as we,jobDir as ye,nodeDir as Se,nodeRunDir as C}from"./paths.js";import{appForNode as ge,ensureNodeDirs as Oe,nodeAgentDir as Te}from"./node-agent-paths.js";import{appSpace as I}from"../layout.js";import{createObject as he,getById as U,ownerForGrantee as M,setObjectKind as Le,setObjectName as De}from"../graph/objects.js";import{deleteNodeWithOwned as ve}from"../graph/deletion.js";function m(e,n){if(e==null)return null;const t=JSON.stringify(e);if(t===void 0)throw new TypeError(`${n} must be JSON-serializable`);return t}r(m,"jsonValue");function rn(e,n){if(a(e,"SELECT deleting_at FROM nodes WHERE node_id = ?").get(n)?.deleting_at)throw new Error("run is deleting")}r(rn,"assertRunNotDeleting");function Ae(e,n){const t=M(n,e.grantee??"app:terminal");he(n,{id:e.node_id,type:"node",kind:e.kind,owner:t.rowid,privacy:"public",name:e.name,preview:""}),a(n,`INSERT INTO nodes (node_id, description, title, icon, cycles, mode, lifecycle, cwd, host_kind, profile_id, parent, spawned_by, creator, fork_from, fork_source_file, created, passive_default, cycle_pending, model_override, persona_ack_json, review_binding_json, launch_config_json, scopes_json, isolated, status, intent, waiting_for, final_report, finalized_at, deadline_at, "window", tmux_session, pane, sub, grant_id, client_id, metadata_json, run_id, event_cursor, usage_json, deleting_at) VALUES (${Array(41).fill("?").join(", ")})`).run(e.node_id,e.description??null,e.title??null,e.icon??null,e.cycles??null,e.mode,e.lifecycle,e.cwd,e.host_kind??null,e.profile_id??null,e.parent??null,e.spawned_by??null,e.creator??null,e.fork_from??null,e.fork_source_file??null,e.created,e.passive_default===!0?1:0,e.cycle_pending===!0?1:0,e.model_override??null,m(e.persona_ack,"persona_ack"),m(e.review_binding,"review_binding"),m(e.launch,"launch"),m(e.scopes,"scopes"),e.isolated===!0?1:0,e.status??"active",e.intent??null,e.waiting_for??null,e.final_report??null,e.finalized_at??null,e.deadline_at??null,e.window??null,e.tmux_session??null,e.pane??null,e.sub??null,e.grant_id??null,e.client_id??null,e.metadata_json??null,e.run_id??null,e.event_cursor??null,e.usage_json??null,e.deleting_at??null)}r(Ae,"seedRow");function P(e){if(e.fault_id==null)return null;const n=Object.entries(e).filter(([t])=>t.startsWith("fault_")).map(([t,o])=>[t==="fault_id"?t:t.slice(6),o]);return ce(Object.fromEntries(n))}r(P,"faultFrom");function E(e){const n={node_id:e.node_id,name:e.name,description:e.description??void 0,cycles:e.cycles??void 0,kind:e.kind,mode:e.mode,lifecycle:e.lifecycle,status:e.status,cwd:e.cwd,host_kind:e.host_kind??null,profile_id:e.profile_id??null,scopes:e.scopes_json==null?null:JSON.parse(e.scopes_json),isolated:e.isolated===1,grantee:e.grantee,sub:e.sub??null,grant_id:e.grant_id??null,client_id:e.client_id??null,metadata_json:e.metadata_json??null,run_id:e.run_id??null,event_cursor:e.event_cursor??null,last_activity_at:e.last_activity_at??null,usage_json:e.usage_json??null,last_turn_error_json:e.last_turn_error_json??null,deleting_at:e.deleting_at??null,parent:e.parent??null,created:e.created,intent:e.intent??null,waiting_for:e.waiting_for??null,frozen_at:e.frozen_at??null,respawn_failures:e.respawn_failures??0,respawn_not_before:e.respawn_not_before??null,launched_at:e.launched_at??null,last_settled_at:e.last_settled_at??null,terminal_reason:e.terminal_reason??null,final_report:e.final_report??null,finalized_at:e.finalized_at??null,outcome_kind:e.outcome_kind??null,outcome_reason:e.outcome_reason??null,outcome_at:e.outcome_at??null,outcome_revision:e.outcome_revision??0,outcome_detail_json:e.outcome_detail_json??null,deadline_at:e.deadline_at??null,telemetry_tokens_in:e.telemetry_tokens_in??null,telemetry_context_tokens:e.telemetry_context_tokens??null,telemetry_last_activity:e.telemetry_last_activity??null,telemetry_updated_at:e.telemetry_updated_at??null,pi_pid:e.pi_pid??null,pi_pid_identity:e.pi_pid_identity??null,window:e.window??null,tmux_session:e.tmux_session??null,pane:e.pane??null,fault:null,streaming:!1},t=b(n.pi_pid),o=P(e);return n.fault=o?.link==="pi\u2192provider"&&o.retry.by==="daemon"&&o.retry.disposition==="auto"&&!t?null:o,n.streaming=e.turn_open_at!==null&&e.turn_closed_at===null&&t,n}r(E,"rowFrom");function q(e,n=Ee(e.node_id)){return{...E(e),title:e.title??void 0,icon:e.icon??void 0,spawned_by:e.spawned_by??null,creator:e.creator??null,fork_from:e.fork_from??null,fork_source_file:e.fork_source_file??null,passive_default:e.passive_default===1,cycle_pending:e.cycle_pending===1,model_override:e.model_override??null,scopes:e.scopes_json==null?null:JSON.parse(e.scopes_json),persona_ack:e.persona_ack_json==null?void 0:JSON.parse(e.persona_ack_json),review_binding:e.review_binding_json==null?void 0:JSON.parse(e.review_binding_json),launch:e.launch_config_json==null?void 0:JSON.parse(e.launch_config_json),pi_session_id:e.pi_session_id??null,pi_session_file:e.pi_session_file??null,...n===null?{}:{managed_worktree:n}}}r(q,"metaFrom");function sn(e,n={}){return we(),p(t=>{Ae(e,t),e.managed_worktree!==void 0&&e.managed_worktree!==null&&fe(e.node_id,e.managed_worktree,t),n.outcomeDelivery!==void 0&&F(t,e.node_id,n.outcomeDelivery),n.withinCreate?.(t,e)}),Oe(e.node_id),R(e.node_id)}r(sn,"createNode");function R(e){const n=j(d(),"SELECT * FROM node_rows WHERE node_id = ?").get(e);return n===void 0?null:q(n)}r(R,"getNode");const ke=1440*60*1e3;function $(e){return{requestKey:e.request_key,nodeId:e.node_id,state:e.state}}r($,"createRequestRecord");function an(e){const n=d().prepare("SELECT request_key, node_id, state FROM create_requests WHERE request_key = ?").get(e);return n===void 0?null:$(n)}r(an,"findCreateRequest");function dn(e){return p(n=>{const t=Date.now();n.prepare("DELETE FROM create_requests WHERE expires_at <= ?").run(t);const o=n.prepare("SELECT request_key, node_id, state FROM create_requests WHERE request_key = ?").get(e);return o!==void 0?$(o):(n.prepare("INSERT INTO create_requests (request_key, node_id, expires_at, state) VALUES (?, NULL, ?, 'reserved')").run(e,t+ke),{requestKey:e,nodeId:null,state:"reserved"})})}r(dn,"reserveCreateRequest");function ln(e=Date.now()){return p(n=>Number(n.prepare("DELETE FROM create_requests WHERE expires_at <= ?").run(e).changes))}r(ln,"pruneExpiredCreateRequests");function un(e,n){p(t=>{t.prepare("UPDATE create_requests SET node_id = ?, state = 'complete' WHERE request_key = ?").run(n,e)})}r(un,"recordCreateRequestNode");function _n(e,n){p(t=>{t.prepare("UPDATE create_requests SET node_id = ?, state = 'reserved' WHERE request_key = ?").run(n,e)})}r(_n,"reserveCreateRequestNode");function cn(e,n){p(t=>{t.prepare("UPDATE create_requests SET state = 'complete' WHERE request_key = ? AND node_id = ?").run(e,n)})}r(cn,"completeCreateRequest");function pn(e,n){p(t=>{if(n===null){t.prepare("DELETE FROM create_requests WHERE request_key = ? AND node_id IS NULL").run(e);return}t.prepare("DELETE FROM create_requests WHERE request_key = ? AND node_id = ?").run(e,n)})}r(pn,"abandonCreateRequest");function fn(e){return re(e)}r(fn,"canonicalSessionFile");function En(e){const n=_e(e);return n===null?null:R(n)}r(En,"findNodeBySessionFile");function mn(e){const n=a(d(),"SELECT * FROM node_rows WHERE node_id = ?").get(e);return n?E(n):null}r(mn,"getRow");function Nn(e){const n=a(d(),"SELECT * FROM node_rows WHERE pane = ?").get(e);return n?E(n):null}r(Nn,"getRowByPane");function Rn(e){return a(d(),`SELECT * FROM node_rows
2
2
  WHERE deadline_at IS NOT NULL AND outcome_at IS NULL
3
3
  AND deadline_at <= ? AND status IN ('active','idle') AND kind != 'human'
4
- ORDER BY deadline_at, created, node_id`).all(e).map(f)}r(Nn,"dueNodeDeadlines");function Rn(e,n){return Number(a(d(),"UPDATE nodes SET deadline_at = ? WHERE node_id = ?").run(n,e).changes)===1}r(Rn,"setNodeDeadline");function yn(e,n){return!Number.isFinite(n)||n<=0?0:c(t=>{const o=a(t,`SELECT node_id, deadline_at FROM nodes
5
- WHERE profile_id = ? AND deadline_at IS NOT NULL AND status IN ('active', 'idle')`).all(e),i=a(t,"UPDATE nodes SET deadline_at = ? WHERE node_id = ? AND deadline_at = ?");let s=0;for(const l of o){const u=Date.parse(l.deadline_at);Number.isNaN(u)||(s+=Number(i.run(new Date(u+n).toISOString(),l.node_id,l.deadline_at).changes))}return s})}r(yn,"shiftProfileNodeDeadlines");function wn(e,n,t){const o=`SELECT o.rowid FROM objects o JOIN identities i ON i.object = o.owner
4
+ ORDER BY deadline_at, created, node_id`).all(e).map(E)}r(Rn,"dueNodeDeadlines");function wn(e,n){return Number(a(d(),"UPDATE nodes SET deadline_at = ? WHERE node_id = ?").run(n,e).changes)===1}r(wn,"setNodeDeadline");function yn(e,n){return!Number.isFinite(n)||n<=0?0:p(t=>{const o=a(t,`SELECT node_id, deadline_at FROM nodes
5
+ WHERE profile_id = ? AND deadline_at IS NOT NULL AND status IN ('active', 'idle')`).all(e),i=a(t,"UPDATE nodes SET deadline_at = ? WHERE node_id = ? AND deadline_at = ?");let s=0;for(const l of o){const c=Date.parse(l.deadline_at);Number.isNaN(c)||(s+=Number(i.run(new Date(c+n).toISOString(),l.node_id,l.deadline_at).changes))}return s})}r(yn,"shiftProfileNodeDeadlines");function Sn(e,n,t){const o=`SELECT o.rowid FROM objects o JOIN identities i ON i.object = o.owner
6
6
  WHERE o.type = 'node' AND i.scheme = 'local-user' AND i.external_id = 'user:local'`,s=e.prepare(`${o} LIMIT 1`).get()!==void 0?Number(e.prepare(`UPDATE objects SET owner = ? WHERE rowid IN (${o})`).run(M(e,"app:terminal").rowid).changes):0;return Number(a(e,"UPDATE nodes SET sub = ? WHERE sub = 'user:local'").run(n).changes)+s+Number(a(e,`UPDATE nodes SET sub = ? WHERE sub IS NULL AND node_id IN (
7
7
  SELECT o.id FROM objects o JOIN identities i ON i.object = o.owner
8
- WHERE o.type = 'node' AND i.scheme = 'app' AND i.external_id = 'app:terminal')`).run(n).changes)+Number(a(e,"UPDATE nodes SET grant_id = NULL WHERE grant_id = ?").run(t).changes)}r(wn,"rewriteLocalNodeIdentity");function g(e,n){if(e==null)return null;if(typeof e=="string"||typeof e=="number"||typeof e=="bigint"||e instanceof Uint8Array)return e;throw new TypeError(`${n} must be a SQLite scalar`)}r(g,"sqlInput");function Sn(e,n){const t=[],o=r((s,l=s)=>{Object.prototype.hasOwnProperty.call(n,s)&&t.push([l,g(n[s],s)])},"add");if(o("description"),o("title"),o("icon"),o("cycles"),o("mode"),o("lifecycle"),o("cwd"),o("host_kind"),o("profile_id"),o("parent"),o("spawned_by"),o("creator"),o("fork_from"),o("fork_source_file"),o("model_override"),Object.prototype.hasOwnProperty.call(n,"passive_default")&&t.push(["passive_default",n.passive_default===!0?1:0]),Object.prototype.hasOwnProperty.call(n,"cycle_pending")&&t.push(["cycle_pending",n.cycle_pending===!0?1:0]),Object.prototype.hasOwnProperty.call(n,"persona_ack")&&t.push(["persona_ack_json",m(n.persona_ack,"persona_ack")]),Object.prototype.hasOwnProperty.call(n,"review_binding")&&t.push(["review_binding_json",m(n.review_binding,"review_binding")]),Object.prototype.hasOwnProperty.call(n,"isolated"))throw new TypeError("isolated cannot change after node creation");Object.prototype.hasOwnProperty.call(n,"scopes")&&t.push(["scopes_json",m(n.scopes,"scopes")]),Object.prototype.hasOwnProperty.call(n,"launch")&&t.push(["launch_config_json",m(n.launch,"launch")]);const i=r(s=>Object.prototype.hasOwnProperty.call(n,s),"has");return c(s=>{const l=U(s,e);if(l===null||l.type!=="node")throw new Error(`unknown node: ${e}`);if(i("name")&&Le(s,l,g(n.name,"name")),i("kind")&&he(s,l,g(n.kind,"kind")),t.length===0)return;if(s.prepare(`UPDATE nodes SET ${t.map(([p])=>`${p} = ?`).join(", ")} WHERE node_id = ?`).run(...t.map(([,p])=>p),e).changes!==1)throw new Error(`unknown node: ${e}`)}),R(e)}r(Sn,"updateNode");function gn(e,n){a(d(),"UPDATE nodes SET status = ? WHERE node_id = ?").run(n,e)}r(gn,"setStatus");function On(e,n){a(d(),"UPDATE nodes SET intent = ? WHERE node_id = ?").run(n??null,e)}r(On,"setIntent");function Tn(e,n,t={}){if(t.outcome===void 0){const i=[],s=[];Object.prototype.hasOwnProperty.call(n,"status")&&(i.push("status = ?"),s.push(n.status??null)),Object.prototype.hasOwnProperty.call(n,"intent")&&(i.push("intent = ?"),s.push(n.intent??null)),Object.prototype.hasOwnProperty.call(n,"terminal_reason")&&(i.push("terminal_reason = ?"),s.push(n.terminal_reason??null));const l=["node_id = ?"];s.push(e),t.fromStatuses!==void 0&&(l.push(`status IN (${t.fromStatuses.map(()=>"?").join(", ")})`),s.push(...t.fromStatuses)),t.requireNoFinalReport===!0&&l.push("final_report IS NULL");const u=d().prepare(`UPDATE nodes SET ${i.join(", ")} WHERE ${l.join(" AND ")}`).run(...s);return Number(u.changes)}const o=t.outcome;return c(i=>{const s=[],l=[];Object.prototype.hasOwnProperty.call(n,"status")&&(s.push("status = ?"),l.push(n.status??null)),Object.prototype.hasOwnProperty.call(n,"intent")&&(s.push("intent = ?"),l.push(n.intent??null)),Object.prototype.hasOwnProperty.call(n,"terminal_reason")&&(s.push("terminal_reason = ?"),l.push(n.terminal_reason??null));const u=r((y,_)=>o.supersede===!0?"?":`CASE WHEN outcome_at IS NULL THEN ? ELSE ${_} END`,"outcomeValue");s.push(`outcome_kind = ${u(o.kind,"outcome_kind")}`,`outcome_reason = ${u(o.reason,"outcome_reason")}`,`outcome_detail_json = ${u(o.detailJson,"outcome_detail_json")}`,`outcome_revision = ${o.supersede===!0?"outcome_revision + 1":"CASE WHEN outcome_at IS NULL THEN outcome_revision + 1 ELSE outcome_revision END"}`,`outcome_at = ${u(o.at,"outcome_at")}`),l.push(o.kind,o.reason,o.detailJson,o.at);const p=["node_id = ?"];l.push(e),t.fromStatuses!==void 0&&(p.push(`status IN (${t.fromStatuses.map(()=>"?").join(", ")})`),l.push(...t.fromStatuses)),t.requireNoFinalReport===!0&&p.push("final_report IS NULL");const E=i.prepare(`UPDATE nodes SET ${s.join(", ")} WHERE ${p.join(" AND ")}`).run(...l);if(Number(E.changes)===0)return 0;const S=a(i,"SELECT outcome_at FROM nodes WHERE node_id = ?").get(e);return(o.supersede===!0||S.outcome_at===o.at)&&o.documentJson!==null&&o.documentJson!==void 0&&de(i,{event:"outcome",object:le(i,e),actor:null,attrs:{revision:Number(a(i,"SELECT outcome_revision FROM nodes WHERE node_id = ?").get(e).outcome_revision)},messageBody:JSON.parse(o.documentJson)}),Number(E.changes)})}r(Tn,"transitionStatusIntent");function hn(e){return a(d(),"SELECT final_report FROM nodes WHERE node_id = ?").get(e)?.final_report??null}r(hn,"getFinalReport");function Ln(e,n){return a(d(),"UPDATE nodes SET final_report = NULL, finalized_at = NULL WHERE node_id = ? AND final_report = ?").run(e,n).changes>0}r(Ln,"clearFinalReportIfMatches");function Dn(e,n){a(d(),'UPDATE nodes SET tmux_session = ?, "window" = ?, pane = ? WHERE node_id = ?').run(n.tmux_session??null,n.window??null,n.pane??null,e)}r(Dn,"setPresence");function vn(e,n){a(d(),"UPDATE nodes SET launched_at = ?, frozen_at = NULL, respawn_not_before = NULL WHERE node_id = ?").run(n,e)}r(vn,"recordLaunch");function An(e,n){a(d(),"UPDATE nodes SET last_settled_at = ? WHERE node_id = ?").run(n,e)}r(An,"recordSettledTurn");function kn(e,n){a(d(),"UPDATE nodes SET frozen_at = ? WHERE node_id = ?").run(n,e)}r(kn,"setFrozen");function xn(e){a(d(),"UPDATE nodes SET frozen_at = NULL WHERE node_id = ?").run(e)}r(xn,"clearFrozen");function Wn(e,n){a(d(),"UPDATE nodes SET respawn_failures = ?, respawn_not_before = ? WHERE node_id = ?").run(n.failures,n.notBefore,e)}r(Wn,"recordRespawnAttempt");const N="node_id NOT IN (SELECT node_id FROM warm_pool)",z=["state","cleanup_state","path","branch","repo_root","base_ref","base_sha","created_at","closed_at","next_attempt_at","attempts","quarantined","sweep_reason","sweep_detail","sweep_last_at","sweep_observed_json","abandoned_json","land_intent_json","disposal_json"];function ke(e){return e.worktree_state===null?null:Ee(Object.fromEntries(z.map(n=>[n,e[`worktree_${n}`]])))}r(ke,"worktreeFromProjection");function jn(e){const n=z.map(o=>`w.${o} AS worktree_${o}`).join(", ");return d().prepare(`SELECT n.*, ${n} FROM node_rows n LEFT JOIN worktrees w ON w.node_id = n.node_id WHERE n.profile_id = ? ORDER BY n.created`).all(e).map(o=>q(o,ke(o)))}r(jn,"listNodesByProfile");function Hn(){const e=d();return j(e,`SELECT
8
+ WHERE o.type = 'node' AND i.scheme = 'app' AND i.external_id = 'app:terminal')`).run(n).changes)+Number(a(e,"UPDATE nodes SET grant_id = NULL WHERE grant_id = ?").run(t).changes)}r(Sn,"rewriteLocalNodeIdentity");function g(e,n){if(e==null)return null;if(typeof e=="string"||typeof e=="number"||typeof e=="bigint"||e instanceof Uint8Array)return e;throw new TypeError(`${n} must be a SQLite scalar`)}r(g,"sqlInput");function gn(e,n){const t=[],o=r((s,l=s)=>{Object.prototype.hasOwnProperty.call(n,s)&&t.push([l,g(n[s],s)])},"add");if(o("description"),o("title"),o("icon"),o("cycles"),o("mode"),o("lifecycle"),o("cwd"),o("host_kind"),o("profile_id"),o("parent"),o("spawned_by"),o("creator"),o("fork_from"),o("fork_source_file"),o("model_override"),Object.prototype.hasOwnProperty.call(n,"passive_default")&&t.push(["passive_default",n.passive_default===!0?1:0]),Object.prototype.hasOwnProperty.call(n,"cycle_pending")&&t.push(["cycle_pending",n.cycle_pending===!0?1:0]),Object.prototype.hasOwnProperty.call(n,"persona_ack")&&t.push(["persona_ack_json",m(n.persona_ack,"persona_ack")]),Object.prototype.hasOwnProperty.call(n,"review_binding")&&t.push(["review_binding_json",m(n.review_binding,"review_binding")]),Object.prototype.hasOwnProperty.call(n,"isolated"))throw new TypeError("isolated cannot change after node creation");Object.prototype.hasOwnProperty.call(n,"scopes")&&t.push(["scopes_json",m(n.scopes,"scopes")]),Object.prototype.hasOwnProperty.call(n,"launch")&&t.push(["launch_config_json",m(n.launch,"launch")]);const i=r(s=>Object.prototype.hasOwnProperty.call(n,s),"has");return p(s=>{const l=U(s,e);if(l===null||l.type!=="node")throw new Error(`unknown node: ${e}`);if(i("name")&&De(s,l,g(n.name,"name")),i("kind")&&Le(s,l,g(n.kind,"kind")),t.length===0)return;if(s.prepare(`UPDATE nodes SET ${t.map(([u])=>`${u} = ?`).join(", ")} WHERE node_id = ?`).run(...t.map(([,u])=>u),e).changes!==1)throw new Error(`unknown node: ${e}`)}),R(e)}r(gn,"updateNode");function On(e,n){a(d(),"UPDATE nodes SET status = ? WHERE node_id = ?").run(n,e)}r(On,"setStatus");function Tn(e,n){a(d(),"UPDATE nodes SET intent = ? WHERE node_id = ?").run(n??null,e)}r(Tn,"setIntent");function hn(e,n,t={}){if(t.outcome===void 0){const i=[],s=[];Object.prototype.hasOwnProperty.call(n,"status")&&(i.push("status = ?"),s.push(n.status??null)),Object.prototype.hasOwnProperty.call(n,"intent")&&(i.push("intent = ?"),s.push(n.intent??null)),Object.prototype.hasOwnProperty.call(n,"terminal_reason")&&(i.push("terminal_reason = ?"),s.push(n.terminal_reason??null));const l=["node_id = ?"];s.push(e),t.fromStatuses!==void 0&&(l.push(`status IN (${t.fromStatuses.map(()=>"?").join(", ")})`),s.push(...t.fromStatuses)),t.requireNoFinalReport===!0&&l.push("final_report IS NULL");const c=d().prepare(`UPDATE nodes SET ${i.join(", ")} WHERE ${l.join(" AND ")}`).run(...s);return Number(c.changes)}const o=t.outcome;return p(i=>{const s=[],l=[];Object.prototype.hasOwnProperty.call(n,"status")&&(s.push("status = ?"),l.push(n.status??null)),Object.prototype.hasOwnProperty.call(n,"intent")&&(s.push("intent = ?"),l.push(n.intent??null)),Object.prototype.hasOwnProperty.call(n,"terminal_reason")&&(s.push("terminal_reason = ?"),l.push(n.terminal_reason??null));const c=r((w,_)=>o.supersede===!0?"?":`CASE WHEN outcome_at IS NULL THEN ? ELSE ${_} END`,"outcomeValue");s.push(`outcome_kind = ${c(o.kind,"outcome_kind")}`,`outcome_reason = ${c(o.reason,"outcome_reason")}`,`outcome_detail_json = ${c(o.detailJson,"outcome_detail_json")}`,`outcome_revision = ${o.supersede===!0?"outcome_revision + 1":"CASE WHEN outcome_at IS NULL THEN outcome_revision + 1 ELSE outcome_revision END"}`,`outcome_at = ${c(o.at,"outcome_at")}`),l.push(o.kind,o.reason,o.detailJson,o.at);const u=["node_id = ?"];l.push(e),t.fromStatuses!==void 0&&(u.push(`status IN (${t.fromStatuses.map(()=>"?").join(", ")})`),l.push(...t.fromStatuses)),t.requireNoFinalReport===!0&&u.push("final_report IS NULL");const f=i.prepare(`UPDATE nodes SET ${s.join(", ")} WHERE ${u.join(" AND ")}`).run(...l);if(Number(f.changes)===0)return 0;const S=a(i,"SELECT outcome_at FROM nodes WHERE node_id = ?").get(e);return(o.supersede===!0||S.outcome_at===o.at)&&o.documentJson!==null&&o.documentJson!==void 0&&le(i,{event:"outcome",object:ue(i,e),actor:null,attrs:{revision:Number(a(i,"SELECT outcome_revision FROM nodes WHERE node_id = ?").get(e).outcome_revision)},messageBody:JSON.parse(o.documentJson)}),Number(f.changes)})}r(hn,"transitionStatusIntent");function Ln(e){return a(d(),"SELECT final_report FROM nodes WHERE node_id = ?").get(e)?.final_report??null}r(Ln,"getFinalReport");function Dn(e,n){return a(d(),"UPDATE nodes SET final_report = NULL, finalized_at = NULL WHERE node_id = ? AND final_report = ?").run(e,n).changes>0}r(Dn,"clearFinalReportIfMatches");function vn(e,n){a(d(),'UPDATE nodes SET tmux_session = ?, "window" = ?, pane = ? WHERE node_id = ?').run(n.tmux_session??null,n.window??null,n.pane??null,e)}r(vn,"setPresence");function An(e,n){a(d(),"UPDATE nodes SET launched_at = ?, frozen_at = NULL, respawn_not_before = NULL WHERE node_id = ?").run(n,e)}r(An,"recordLaunch");function kn(e,n){a(d(),"UPDATE nodes SET last_settled_at = ? WHERE node_id = ?").run(n,e)}r(kn,"recordSettledTurn");function xn(e,n){a(d(),"UPDATE nodes SET frozen_at = ? WHERE node_id = ?").run(n,e)}r(xn,"setFrozen");function Wn(e){a(d(),"UPDATE nodes SET frozen_at = NULL WHERE node_id = ?").run(e)}r(Wn,"clearFrozen");function jn(e,n){a(d(),"UPDATE nodes SET respawn_failures = ?, respawn_not_before = ? WHERE node_id = ?").run(n.failures,n.notBefore,e)}r(jn,"recordRespawnAttempt");const N="node_id NOT IN (SELECT node_id FROM warm_pool)",z=["state","cleanup_state","path","branch","repo_root","base_ref","base_sha","created_at","closed_at","next_attempt_at","attempts","quarantined","sweep_reason","sweep_detail","sweep_last_at","sweep_observed_json","abandoned_json","land_intent_json","disposal_json"];function xe(e){return e.worktree_state===null?null:me(Object.fromEntries(z.map(n=>[n,e[`worktree_${n}`]])))}r(xe,"worktreeFromProjection");function Hn(e){const n=z.map(o=>`w.${o} AS worktree_${o}`).join(", ");return d().prepare(`SELECT n.*, ${n} FROM node_rows n LEFT JOIN worktrees w ON w.node_id = n.node_id WHERE n.profile_id = ? ORDER BY n.created`).all(e).map(o=>q(o,xe(o)))}r(Hn,"listNodesByProfile");function Fn(){const e=d();return j(e,`SELECT
9
9
  node_id, name, description, kind, mode, lifecycle, status, cwd, profile_id,
10
10
  grantee, parent, created, frozen_at, terminal_reason, final_report,
11
11
  telemetry_tokens_in, telemetry_updated_at, pi_pid, turn_open_at, turn_closed_at,
@@ -13,13 +13,13 @@ var K=Object.defineProperty;var r=(e,n)=>K(e,"name",{value:n,configurable:!0});i
13
13
  fault_operation_id, fault_anchor_entry_id, fault_retry_disposition,
14
14
  fault_retry_by, fault_retry_attempt, fault_retry_max, fault_retry_next_at,
15
15
  fault_provider_error_json
16
- FROM node_rows WHERE ${N} ORDER BY created`).all().map(f)}r(Hn,"browseNodes");function Fn(e,n){a(d(),`UPDATE nodes
16
+ FROM node_rows WHERE ${N} ORDER BY created`).all().map(E)}r(Fn,"browseNodes");function bn(e,n){a(d(),`UPDATE nodes
17
17
  SET telemetry_tokens_in = ?,
18
18
  telemetry_context_tokens = COALESCE(?, telemetry_context_tokens),
19
19
  telemetry_last_activity = COALESCE(?, telemetry_last_activity),
20
20
  telemetry_updated_at = ?
21
21
  WHERE node_id = ?
22
- AND (telemetry_updated_at IS NULL OR telemetry_updated_at < ?)`).run(n.tokensIn,n.contextTokens,n.lastActivity,n.updatedAt,e,n.updatedAt)}r(Fn,"updateNodeTelemetry");function bn(){return a(d(),`SELECT
22
+ AND (telemetry_updated_at IS NULL OR telemetry_updated_at < ?)`).run(n.tokensIn,n.contextTokens,n.lastActivity,n.updatedAt,e,n.updatedAt)}r(bn,"updateNodeTelemetry");function Cn(){return a(d(),`SELECT
23
23
  node_id, name, description, cycles, kind, status, frozen_at,
24
24
  telemetry_context_tokens, telemetry_last_activity,
25
25
  pi_pid, turn_open_at, turn_closed_at,
@@ -27,14 +27,14 @@ var K=Object.defineProperty;var r=(e,n)=>K(e,"name",{value:n,configurable:!0});i
27
27
  fault_operation_id, fault_anchor_entry_id, fault_retry_disposition,
28
28
  fault_retry_by, fault_retry_attempt, fault_retry_max, fault_retry_next_at,
29
29
  fault_provider_error_json
30
- FROM node_rows WHERE ${N} ORDER BY created`).all().map(n=>{const t=n.pi_pid??null,o=b(t),i=P(n);return{node_id:n.node_id,name:n.name,description:n.description??null,cycles:n.cycles??null,kind:n.kind,status:n.status,frozen_at:n.frozen_at??null,pi_pid:t,telemetry_context_tokens:n.telemetry_context_tokens??null,telemetry_last_activity:n.telemetry_last_activity??null,fault:i?.link==="pi\u2192provider"&&i.retry.by==="daemon"&&i.retry.disposition==="auto"&&!o?null:i,streaming:n.turn_open_at!==null&&n.turn_closed_at===null&&o}})}r(bn,"graphNodes");function xe(e){const n=[N],t=[];if(e?.status!==void 0){const i=Array.isArray(e.status)?e.status:[e.status];n.push(`status IN (${i.map(()=>"?").join(",")})`),t.push(...i)}return e?.withRecordedPid===!0&&n.push("pi_pid IS NOT NULL"),e?.withRecordedPidOrFrozen===!0&&n.push("(pi_pid IS NOT NULL OR frozen_at IS NOT NULL)"),d().prepare(`SELECT * FROM node_rows WHERE ${n.join(" AND ")} ORDER BY created`).all(...t).map(f)}r(xe,"listNodes");const We=`SELECT
30
+ FROM node_rows WHERE ${N} ORDER BY created`).all().map(n=>{const t=n.pi_pid??null,o=b(t),i=P(n);return{node_id:n.node_id,name:n.name,description:n.description??null,cycles:n.cycles??null,kind:n.kind,status:n.status,frozen_at:n.frozen_at??null,pi_pid:t,telemetry_context_tokens:n.telemetry_context_tokens??null,telemetry_last_activity:n.telemetry_last_activity??null,fault:i?.link==="pi\u2192provider"&&i.retry.by==="daemon"&&i.retry.disposition==="auto"&&!o?null:i,streaming:n.turn_open_at!==null&&n.turn_closed_at===null&&o}})}r(Cn,"graphNodes");function We(e){const n=[N],t=[];if(e?.status!==void 0){const i=Array.isArray(e.status)?e.status:[e.status];n.push(`status IN (${i.map(()=>"?").join(",")})`),t.push(...i)}return e?.withRecordedPid===!0&&n.push("pi_pid IS NOT NULL"),e?.withRecordedPidOrFrozen===!0&&n.push("(pi_pid IS NOT NULL OR frozen_at IS NOT NULL)"),d().prepare(`SELECT * FROM node_rows WHERE ${n.join(" AND ")} ORDER BY created`).all(...t).map(E)}r(We,"listNodes");const je=`SELECT
31
31
  node_id, created, launched_at, last_settled_at, finalized_at,
32
32
  outcome_at, last_activity_at, pane, "window", tmux_session
33
33
  FROM nodes INDEXED BY idx_nodes_terminal_placement_created
34
34
  WHERE ${N}
35
35
  AND status IN ('done', 'canceled', 'dead')
36
36
  AND (pane IS NOT NULL OR "window" IS NOT NULL OR tmux_session IS NOT NULL)
37
- ORDER BY created`;function In(){return a(d(),We).all()}r(In,"listTerminalPlacementResidue");const je=`WITH candidates AS MATERIALIZED (
37
+ ORDER BY created`;function In(){return a(d(),je).all()}r(In,"listTerminalPlacementResidue");const He=`WITH candidates AS MATERIALIZED (
38
38
  SELECT node_id FROM nodes WHERE status IN ('active', 'idle')
39
39
  UNION
40
40
  SELECT node_id FROM nodes INDEXED BY idx_nodes_supervision_frozen
@@ -48,18 +48,18 @@ var K=Object.defineProperty;var r=(e,n)=>K(e,"name",{value:n,configurable:!0});i
48
48
  AND v.node_id NOT IN (SELECT node_id FROM warm_pool)
49
49
  AND v.kind != 'human'
50
50
  AND (v.status IN ('active', 'idle') OR (v.status IN ('done', 'canceled', 'dead') AND (v.pi_pid IS NOT NULL OR v.frozen_at IS NOT NULL)))
51
- ORDER BY v.created`;function Cn(){return a(d(),je).all().map(f)}r(Cn,"listNodesForSupervision");function Un(){return a(d(),`SELECT node_id, name, created, grantee FROM node_rows WHERE ${N} ORDER BY created`).all()}r(Un,"listNodeIdentities");function Mn(e,n){c(t=>{a(t,"INSERT OR REPLACE INTO warm_pool (node_id, recipe_key, created) VALUES (?, ?, ?)").run(e,n,new Date().toISOString())})}r(Mn,"registerWarmSpare");function Pn(e){return c(n=>a(n,"DELETE FROM warm_pool WHERE node_id = ?").run(e).changes>0)}r(Pn,"unregisterWarmSpare");function O(e,n){const t=`SELECT w.node_id AS node_id, w.recipe_key AS recipe_key, w.created AS created,
51
+ ORDER BY v.created`;function Un(){return a(d(),He).all().map(E)}r(Un,"listNodesForSupervision");function Mn(){return a(d(),`SELECT node_id, name, created, grantee FROM node_rows WHERE ${N} ORDER BY created`).all()}r(Mn,"listNodeIdentities");function Pn(e,n){p(t=>{a(t,"INSERT OR REPLACE INTO warm_pool (node_id, recipe_key, created) VALUES (?, ?, ?)").run(e,n,new Date().toISOString())})}r(Pn,"registerWarmSpare");function qn(e){return p(n=>a(n,"DELETE FROM warm_pool WHERE node_id = ?").run(e).changes>0)}r(qn,"unregisterWarmSpare");function O(e,n){const t=`SELECT w.node_id AS node_id, w.recipe_key AS recipe_key, w.created AS created,
52
52
  n.status AS status, n.pi_pid AS pi_pid, n.pi_pid_identity AS pi_pid_identity
53
- FROM warm_pool w JOIN node_rows n ON n.node_id = w.node_id`+(n===void 0?"":" WHERE w.recipe_key = ?")+" ORDER BY w.created";return(n===void 0?e.prepare(t).all():e.prepare(t).all(n)).map(i=>({node_id:i.node_id,recipe_key:i.recipe_key,created:i.created,status:i.status,pi_pid:i.pi_pid??null,pi_pid_identity:i.pi_pid_identity??null}))}r(O,"warmSpareRowsFor");function qn(){return O(d())}r(qn,"listWarmSpares");function $n(e){return a(d(),"SELECT 1 FROM warm_pool WHERE node_id = ?").get(e)!==void 0}r($n,"isWarmSpare");function B(e){return e.status!=="active"&&e.status!=="idle"?!1:Ne(e.pi_pid,e.pi_pid_identity)==="alive"}r(B,"spareIsClaimable");function zn(e,n,t={}){return c(o=>{const i=O(o,e).find(B);return i===void 0||t.requestKey!==void 0&&o.prepare("UPDATE create_requests SET node_id = ?, state = 'warm_claimed' WHERE request_key = ? AND node_id IS NULL").run(i.node_id,t.requestKey).changes===0||a(o,"DELETE FROM warm_pool WHERE node_id = ?").run(i.node_id).changes===0?null:(a(o,"UPDATE nodes SET created = ?, deadline_at = ? WHERE node_id = ?").run(n,t.deadlineAt??null,i.node_id),t.outcomeDelivery!==void 0&&F(o,i.node_id,t.outcomeDelivery),i.node_id)})}r(zn,"claimWarmSpare");function Bn(e){return O(d(),e).filter(B).length}r(Bn,"warmSpareCount");function Jn(e){return a(d(),"SELECT node_id FROM nodes WHERE parent = ? ORDER BY created").all(e).map(n=>n.node_id)}r(Jn,"childrenOf");function Yn(){return d().prepare(`SELECT node_id, name, kind, mode, lifecycle, status, cwd, host_kind, parent, created
54
- FROM node_rows WHERE ${N} ORDER BY created`).all().map(e=>({node_id:e.node_id,name:e.name,kind:e.kind,mode:e.mode,lifecycle:e.lifecycle,status:e.status,cwd:e.cwd,host_kind:e.host_kind??null,parent:e.parent??null,created:e.created}))}r(Yn,"rosterNodes");function Vn(e){return d().prepare(`SELECT * FROM node_rows
53
+ FROM warm_pool w JOIN node_rows n ON n.node_id = w.node_id`+(n===void 0?"":" WHERE w.recipe_key = ?")+" ORDER BY w.created";return(n===void 0?e.prepare(t).all():e.prepare(t).all(n)).map(i=>({node_id:i.node_id,recipe_key:i.recipe_key,created:i.created,status:i.status,pi_pid:i.pi_pid??null,pi_pid_identity:i.pi_pid_identity??null}))}r(O,"warmSpareRowsFor");function $n(){return O(d())}r($n,"listWarmSpares");function zn(e){return a(d(),"SELECT 1 FROM warm_pool WHERE node_id = ?").get(e)!==void 0}r(zn,"isWarmSpare");function B(e){return e.status!=="active"&&e.status!=="idle"?!1:Re(e.pi_pid,e.pi_pid_identity)==="alive"}r(B,"spareIsClaimable");function Bn(e,n,t={}){return p(o=>{const i=O(o,e).find(B);return i===void 0||t.requestKey!==void 0&&o.prepare("UPDATE create_requests SET node_id = ?, state = 'warm_claimed' WHERE request_key = ? AND node_id IS NULL").run(i.node_id,t.requestKey).changes===0||a(o,"DELETE FROM warm_pool WHERE node_id = ?").run(i.node_id).changes===0?null:(a(o,"UPDATE nodes SET created = ?, deadline_at = ? WHERE node_id = ?").run(n,t.deadlineAt??null,i.node_id),t.outcomeDelivery!==void 0&&F(o,i.node_id,t.outcomeDelivery),i.node_id)})}r(Bn,"claimWarmSpare");function Jn(e){return O(d(),e).filter(B).length}r(Jn,"warmSpareCount");function Yn(e){return a(d(),"SELECT node_id FROM nodes WHERE parent = ? ORDER BY created").all(e).map(n=>n.node_id)}r(Yn,"childrenOf");function Vn(){return d().prepare(`SELECT node_id, name, kind, mode, lifecycle, status, cwd, host_kind, parent, created
54
+ FROM node_rows WHERE ${N} ORDER BY created`).all().map(e=>({node_id:e.node_id,name:e.name,kind:e.kind,mode:e.mode,lifecycle:e.lifecycle,status:e.status,cwd:e.cwd,host_kind:e.host_kind??null,parent:e.parent??null,created:e.created}))}r(Vn,"rosterNodes");function Xn(e){return d().prepare(`SELECT * FROM node_rows
55
55
  WHERE parent IS NULL AND kind != 'human'
56
56
  AND status IN ('active','idle') AND cwd = ?
57
57
  AND ${N}
58
- ORDER BY created DESC`).all(e).map(f)}r(Vn,"listResumableRoots");export*from"./tree.js";function Xn(e){const n=R(e)?.waiting_for;if(n==null)return!1;const t=R(n);return t!==null&&(t.status==="active"||t.status==="idle")}r(Xn,"hasLiveMessageWait");function Gn(e,n){a(d(),"UPDATE nodes SET waiting_for = ?, waiting_for_generation = waiting_for_generation + 1 WHERE node_id = ?").run(n,e)}r(Gn,"setMessageWait");function Qn(e,n,t){return c(o=>{const i=a(o,"SELECT waiting_for_generation FROM nodes WHERE node_id = ? AND waiting_for = ?").get(e,n),s=t();return n!==null&&i!==void 0&&a(o,"UPDATE nodes SET waiting_for = NULL WHERE node_id = ? AND waiting_for = ? AND waiting_for_generation = ?").run(e,n,i.waiting_for_generation),s})}r(Qn,"deliverAndSettleMessageWait");function Kn(e,n,t){return c(o=>{const i=a(o,`SELECT waiter.waiting_for, waiter.waiting_for_generation, target.status AS controller_status
58
+ ORDER BY created DESC`).all(e).map(E)}r(Xn,"listResumableRoots");export*from"./tree.js";function Gn(e){const n=R(e)?.waiting_for;if(n==null)return!1;const t=R(n);return t!==null&&(t.status==="active"||t.status==="idle")}r(Gn,"hasLiveMessageWait");function Qn(e,n){a(d(),"UPDATE nodes SET waiting_for = ?, waiting_for_generation = waiting_for_generation + 1 WHERE node_id = ?").run(n,e)}r(Qn,"setMessageWait");function Kn(e,n,t){return p(o=>{const i=a(o,"SELECT waiting_for_generation FROM nodes WHERE node_id = ? AND waiting_for = ?").get(e,n),s=t();return n!==null&&i!==void 0&&a(o,"UPDATE nodes SET waiting_for = NULL WHERE node_id = ? AND waiting_for = ? AND waiting_for_generation = ?").run(e,n,i.waiting_for_generation),s})}r(Kn,"deliverAndSettleMessageWait");function Zn(e,n,t){return p(o=>{const i=a(o,`SELECT waiter.waiting_for, waiter.waiting_for_generation, target.status AS controller_status
59
59
  FROM nodes AS waiter LEFT JOIN nodes AS target ON target.node_id = waiter.waiting_for
60
- WHERE waiter.node_id = ?`).get(e),s=i?.controller_status==="active"||i?.controller_status==="idle";if(i?.waiting_for!==n||s)return null;const l=t();return a(o,"UPDATE nodes SET waiting_for = NULL WHERE node_id = ? AND waiting_for = ? AND waiting_for_generation = ?").run(e,n,i.waiting_for_generation),l})}r(Kn,"settleDeadMessageWait");function Zn(e,n,t){return c(o=>{const i=a(o,"SELECT status, final_report, frozen_at FROM nodes WHERE node_id = ?").get(e);return i===void 0?{kind:"missing"}:n(i)?{kind:"terminal",status:i.status,final_report:i.final_report}:{kind:"ran",result:t()}})}r(Zn,"withFreshTerminalGuard");function w(e){return ce(e)}r(w,"hasUnreconciledManagedWorktree");function J(e,n={}){return!["done","dead","canceled"].includes(e.status)||e.pi_pid!==null||e.frozen_at!==null||w(e.node_id)||e.node_id===k()||H(e)?!1:n.pastTtl===!0||!["crashed","stranded",null].includes(e.terminal_reason??null)}r(J,"isCollectible");function T(e){if(e.length===0)return[];const n=e.map(()=>"?").join(",");return d().prepare(`SELECT DISTINCT session_id FROM sessions WHERE node_id IN (${n}) AND session_id IS NOT NULL`).all(...e).map(({session_id:o})=>o)}r(T,"nodeConversationIds");function h(e){const n=d(),t=a(n,"SELECT 1 FROM sessions WHERE session_id = ? LIMIT 1"),o=a(n,`SELECT 1 FROM nodes n WHERE n.fork_source_file LIKE '%\\_' || ? || '.jsonl' ESCAPE '\\'
61
- AND NOT EXISTS (SELECT 1 FROM sessions s WHERE s.node_id = n.node_id) LIMIT 1`);for(const i of e)if(!(t.get(i)!==void 0||o.get(i)!==void 0))try{ie().delete(i)}catch(s){if(!(s instanceof se))throw s}}r(h,"deleteNodeConversations");function L(e,n){const t=U(e,n);return t===null||t.deleted_at!==null?!1:(De(e,t),!0)}r(L,"deleteNodeObject");function D(e){return{nodeId:e,app:R(e)===null?null:Se(e)}}r(D,"nodeFiles");function Y({nodeId:e,app:n}){n!==null&&W(C(n).nodes,Oe(e,n)),W(te(I(e)),I(e))}r(Y,"removeNodeAppTrees");function V({nodeId:e}){return[we(e)]}r(V,"nodeOwnedDirs");function X({nodeId:e,app:n}){if(n!==null)try{Z(x(C(n).conversations,e))}catch(t){if(!["ENOENT","ENOTEMPTY"].includes(t.code??""))throw t}}r(X,"removeNodeConversationDir");function v(e){Y(e);for(const n of V(e))ee(n,{recursive:!0,force:!0});X(e)}r(v,"removeNodeFiles");function et(e){if(w(e))return{deleted:!1};const n=D(e),t=T([e]),o=c(i=>(L(i,e),{deleted:!0}));return h(t),v(n),o}r(et,"deleteNode");function nt(e){const n=[...new Set(e)];if(n.length===0)return[];const t=n.filter(w);if(t.length>0)throw new Error(`refusing to delete nodes with unreconciled managed worktrees: ${t.join(", ")}`);const o=n.map(D),i=T(n),s=n.map(()=>"?").join(","),u=d().prepare(`SELECT node_id FROM nodes WHERE node_id IN (${s}) ORDER BY created`).all(...n).map(({node_id:p})=>p).filter(p=>c(E=>L(E,p)));h(i);for(const p of u){const E=o[n.indexOf(p)];A(E,()=>v(E))}return u}r(nt,"deleteNodes");function tt(e){const n=e.dryRun??!1,t=e.includeStale??!1,o=Date.now()-e.ttlDays*864e5,i=new Date(o).toISOString(),s=k()??"",l=d(),u=r(_=>Q(_)<o,"quiet"),p=r(_=>({node_id:_.node_id,status:_.status,created:_.created}),"toPruned"),E=a(l,`SELECT * FROM node_rows
60
+ WHERE waiter.node_id = ?`).get(e),s=i?.controller_status==="active"||i?.controller_status==="idle";if(i?.waiting_for!==n||s)return null;const l=t();return a(o,"UPDATE nodes SET waiting_for = NULL WHERE node_id = ? AND waiting_for = ? AND waiting_for_generation = ?").run(e,n,i.waiting_for_generation),l})}r(Zn,"settleDeadMessageWait");function et(e,n,t){return p(o=>{const i=a(o,"SELECT status, final_report, frozen_at FROM nodes WHERE node_id = ?").get(e);return i===void 0?{kind:"missing"}:n(i)?{kind:"terminal",status:i.status,final_report:i.final_report}:{kind:"ran",result:t()}})}r(et,"withFreshTerminalGuard");function y(e){return pe(e)}r(y,"hasUnreconciledManagedWorktree");function J(e,n={}){return!["done","dead","canceled"].includes(e.status)||e.pi_pid!==null||e.frozen_at!==null||y(e.node_id)||e.node_id===k()||H(e)?!1:n.pastTtl===!0||!["crashed","stranded",null].includes(e.terminal_reason??null)}r(J,"isCollectible");function T(e){if(e.length===0)return[];const n=e.map(()=>"?").join(",");return d().prepare(`SELECT DISTINCT session_id FROM sessions WHERE node_id IN (${n}) AND session_id IS NOT NULL`).all(...e).map(({session_id:o})=>o)}r(T,"nodeConversationIds");function h(e){const n=d(),t=a(n,"SELECT 1 FROM sessions WHERE session_id = ? LIMIT 1"),o=a(n,`SELECT 1 FROM nodes n WHERE n.fork_source_file LIKE '%\\_' || ? || '.jsonl' ESCAPE '\\'
61
+ AND NOT EXISTS (SELECT 1 FROM sessions s WHERE s.node_id = n.node_id) LIMIT 1`);for(const i of e)if(!(t.get(i)!==void 0||o.get(i)!==void 0))try{se().delete(i)}catch(s){if(!(s instanceof ae))throw s}}r(h,"deleteNodeConversations");function L(e,n){const t=U(e,n);return t===null||t.deleted_at!==null?!1:(ve(e,t),!0)}r(L,"deleteNodeObject");function D(e){return{nodeId:e,app:R(e)===null?null:ge(e)}}r(D,"nodeFiles");function Y({nodeId:e,app:n}){n!==null&&W(I(n).nodes,Te(e,n)),W(oe(C(e)),C(e))}r(Y,"removeNodeAppTrees");function V({nodeId:e}){return[Se(e)]}r(V,"nodeOwnedDirs");function X({nodeId:e,app:n}){if(n!==null)try{ee(x(I(n).conversations,e))}catch(t){if(!["ENOENT","ENOTEMPTY"].includes(t.code??""))throw t}}r(X,"removeNodeConversationDir");function v(e){Y(e);for(const n of V(e))ne(n,{recursive:!0,force:!0});X(e)}r(v,"removeNodeFiles");function nt(e){if(y(e))return{deleted:!1};const n=D(e),t=T([e]),o=p(i=>(L(i,e),{deleted:!0}));return h(t),v(n),o}r(nt,"deleteNode");function tt(e){const n=[...new Set(e)];if(n.length===0)return[];const t=n.filter(y);if(t.length>0)throw new Error(`refusing to delete nodes with unreconciled managed worktrees: ${t.join(", ")}`);const o=n.map(D),i=T(n),s=n.map(()=>"?").join(","),c=d().prepare(`SELECT node_id FROM nodes WHERE node_id IN (${s}) ORDER BY created`).all(...n).map(({node_id:u})=>u).filter(u=>p(f=>L(f,u)));h(i);for(const u of c){const f=o[n.indexOf(u)];A(f,()=>v(f))}return c}r(tt,"deleteNodes");function ot(e){const n=e.dryRun??!1,t=e.includeStale??!1,o=Date.now()-e.ttlDays*864e5,i=new Date(o).toISOString(),s=k()??"",l=d(),c=r(_=>K(_)<o,"quiet"),u=r(_=>({node_id:_.node_id,status:_.status,created:_.created}),"toPruned"),f=a(l,`SELECT * FROM node_rows
62
62
  WHERE status IN ('dead', 'done', 'canceled') AND created < ?
63
- ORDER BY created`).all(i).map(f).filter(_=>J(_,{pastTtl:!0})&&u(_)&&!e.held(_.node_id)).map(p),S=t?a(l,`SELECT * FROM node_rows
63
+ ORDER BY created`).all(i).map(E).filter(_=>J(_,{pastTtl:!0})&&c(_)&&!e.held(_.node_id)).map(u),S=t?a(l,`SELECT * FROM node_rows
64
64
  WHERE status IN ('active', 'idle') AND created < ?
65
- ORDER BY created`).all(i).map(f).filter(_=>_.node_id!==s&&!me(_.pi_pid??null)&&(_.frozen_at??null)===null&&(_.respawn_not_before??null)===null&&_.intent!=="idle-release"&&!H(_)&&!ae(_.node_id)&&!w(_.node_id)&&u(_)&&!e.held(_.node_id)).map(p):[],y=[...E,...S];return n||y.length===0?{pruned:y,dryRun:n}:(G(y,e.removeDirs),{pruned:y,dryRun:n})}r(tt,"pruneNodes");function G(e,n){if(e.length===0)return;const t=e.map(i=>D(i.node_id)),o=T(e.map(i=>i.node_id));for(const i of e)c(s=>L(s,i.node_id));if(h(o),n!==void 0){for(const i of t)A(i,()=>{Y(i),X(i)});n(t.flatMap(V));return}for(const i of t)A(i,()=>v(i))}r(G,"deletePrunedNodes");function A(e,n){try{n()}catch(t){re({level:"error",event:"canvas.node_files.remove_failed",error:t,fields:{node_id:e.nodeId}})}}r(A,"eachNodeBestEffort");function Q(e){let n=0;for(const t of[e.created,e.launched_at,e.last_settled_at,e.finalized_at,e.outcome_at,e.last_activity_at]){if(t==null)continue;const o=Date.parse(t);Number.isFinite(o)&&o>n&&(n=o)}try{const t=ne(x(ye(e.node_id),"telemetry.json"),{throwIfNoEntry:!1});t!==void 0&&t.mtimeMs>n&&(n=t.mtimeMs)}catch{}return n}r(Q,"lastActivityMs");function ot(e){const n=e.dryRun??!1,t=xe(),o=t.filter(u=>J(u)&&!e.held(u.node_id)),i=t.length-o.length,s=Math.max(0,e.maxNodes-i),l=o.map(u=>({row:u,recency:Q(u)})).sort((u,p)=>p.recency-u.recency).slice(s).map(({row:u})=>({node_id:u.node_id,status:u.status,created:u.created}));return n||l.length===0?{pruned:l,dryRun:n}:(G(l),{pruned:l,dryRun:n})}r(ot,"pruneToLimit");export{cn as abandonCreateRequest,on as assertRunNotDeleting,Hn as browseNodes,pn as canonicalSessionFile,Jn as childrenOf,zn as claimWarmSpare,Ln as clearFinalReportIfMatches,xn as clearFrozen,_n as completeCreateRequest,rn as createNode,et as deleteNode,nt as deleteNodes,Qn as deliverAndSettleMessageWait,Nn as dueNodeDeadlines,sn as findCreateRequest,fn as findNodeBySessionFile,hn as getFinalReport,R as getNode,En as getRow,mn as getRowByPane,bn as graphNodes,Xn as hasLiveMessageWait,J as isCollectible,$n as isWarmSpare,Q as lastActivityMs,Un as listNodeIdentities,xe as listNodes,jn as listNodesByProfile,Cn as listNodesForSupervision,Vn as listResumableRoots,In as listTerminalPlacementResidue,qn as listWarmSpares,D as nodeFiles,je as nodesForSupervisionSql,dn as pruneExpiredCreateRequests,tt as pruneNodes,ot as pruneToLimit,ln as recordCreateRequestNode,vn as recordLaunch,Wn as recordRespawnAttempt,An as recordSettledTurn,Mn as registerWarmSpare,v as removeNodeFiles,an as reserveCreateRequest,un as reserveCreateRequestNode,wn as rewriteLocalNodeIdentity,Yn as rosterNodes,kn as setFrozen,On as setIntent,Gn as setMessageWait,Rn as setNodeDeadline,Dn as setPresence,gn as setStatus,Kn as settleDeadMessageWait,yn as shiftProfileNodeDeadlines,We as terminalPlacementResidueSql,Tn as transitionStatusIntent,Pn as unregisterWarmSpare,Sn as updateNode,Fn as updateNodeTelemetry,Bn as warmSpareCount,Zn as withFreshTerminalGuard};
65
+ ORDER BY created`).all(i).map(E).filter(_=>_.node_id!==s&&!Ne(_.pi_pid??null)&&(_.frozen_at??null)===null&&(_.respawn_not_before??null)===null&&_.intent!=="idle-release"&&!H(_)&&!de(_.node_id)&&!y(_.node_id)&&c(_)&&!e.held(_.node_id)).map(u):[],w=G([...f,...S]);return n||w.length===0?{pruned:w,dryRun:n}:(Q(w,e.removeDirs),{pruned:w,dryRun:n})}r(ot,"pruneNodes");function G(e){const n=d(),t=a(n,"SELECT node_id FROM nodes WHERE run_id = ? AND node_id != ?"),o=new Set(e.map(i=>i.node_id));return e.filter(i=>t.all(i.node_id,i.node_id).every(s=>o.has(s.node_id)))}r(G,"retainRunRootsWithMembers");function Q(e,n){if(e.length===0)return;const t=e.map(i=>D(i.node_id)),o=T(e.map(i=>i.node_id));for(const i of e)p(s=>L(s,i.node_id));if(h(o),n!==void 0){for(const i of t)A(i,()=>{Y(i),X(i)});n(t.flatMap(V));return}for(const i of t)A(i,()=>v(i))}r(Q,"deletePrunedNodes");function A(e,n){try{n()}catch(t){ie({level:"error",event:"canvas.node_files.remove_failed",error:t,fields:{node_id:e.nodeId}})}}r(A,"eachNodeBestEffort");function K(e){let n=0;for(const t of[e.created,e.launched_at,e.last_settled_at,e.finalized_at,e.outcome_at,e.last_activity_at]){if(t==null)continue;const o=Date.parse(t);Number.isFinite(o)&&o>n&&(n=o)}try{const t=te(x(ye(e.node_id),"telemetry.json"),{throwIfNoEntry:!1});t!==void 0&&t.mtimeMs>n&&(n=t.mtimeMs)}catch{}return n}r(K,"lastActivityMs");function rt(e){const n=e.dryRun??!1,t=We(),o=t.filter(u=>J(u)&&!e.held(u.node_id)),i=t.length-o.length,s=Math.max(0,e.maxNodes-i),l=o.map(u=>({row:u,recency:K(u)})).sort((u,f)=>f.recency-u.recency).slice(s).map(({row:u})=>({node_id:u.node_id,status:u.status,created:u.created})),c=G(l);return n||c.length===0?{pruned:c,dryRun:n}:(Q(c),{pruned:c,dryRun:n})}r(rt,"pruneToLimit");export{pn as abandonCreateRequest,rn as assertRunNotDeleting,Fn as browseNodes,fn as canonicalSessionFile,Yn as childrenOf,Bn as claimWarmSpare,Dn as clearFinalReportIfMatches,Wn as clearFrozen,cn as completeCreateRequest,sn as createNode,nt as deleteNode,tt as deleteNodes,Kn as deliverAndSettleMessageWait,Rn as dueNodeDeadlines,an as findCreateRequest,En as findNodeBySessionFile,Ln as getFinalReport,R as getNode,mn as getRow,Nn as getRowByPane,Cn as graphNodes,Gn as hasLiveMessageWait,J as isCollectible,zn as isWarmSpare,K as lastActivityMs,Mn as listNodeIdentities,We as listNodes,Hn as listNodesByProfile,Un as listNodesForSupervision,Xn as listResumableRoots,In as listTerminalPlacementResidue,$n as listWarmSpares,D as nodeFiles,He as nodesForSupervisionSql,ln as pruneExpiredCreateRequests,ot as pruneNodes,rt as pruneToLimit,un as recordCreateRequestNode,An as recordLaunch,jn as recordRespawnAttempt,kn as recordSettledTurn,Pn as registerWarmSpare,v as removeNodeFiles,dn as reserveCreateRequest,_n as reserveCreateRequestNode,Sn as rewriteLocalNodeIdentity,Vn as rosterNodes,xn as setFrozen,Tn as setIntent,Qn as setMessageWait,wn as setNodeDeadline,vn as setPresence,On as setStatus,Zn as settleDeadMessageWait,yn as shiftProfileNodeDeadlines,je as terminalPlacementResidueSql,hn as transitionStatusIntent,qn as unregisterWarmSpare,gn as updateNode,bn as updateNodeTelemetry,Jn as warmSpareCount,et as withFreshTerminalGuard};
@@ -1027,4 +1027,8 @@ CREATE INDEX idx_structured_requests_effects ON structured_requests(effects_done
1027
1027
  key TEXT PRIMARY KEY,
1028
1028
  ingested_at INTEGER NOT NULL
1029
1029
  );
1030
+ `),e=>e.exec(`
1031
+ UPDATE nodes SET run_id = NULL
1032
+ WHERE run_id IS NOT NULL AND run_id != node_id AND isolated = 0
1033
+ AND NOT EXISTS (SELECT 1 FROM nodes r WHERE r.node_id = nodes.run_id);
1030
1034
  `)];function B(e,t,n){const E=e.prepare("PRAGMA foreign_keys").get().foreign_keys;e.exec("PRAGMA foreign_keys = OFF;");try{e.exec("BEGIN IMMEDIATE");try{W(e)===t&&(n(),e.exec(`PRAGMA user_version = ${t+1}`)),e.exec("COMMIT")}catch(T){try{e.exec("ROLLBACK")}catch{}throw T}}finally{e.exec(`PRAGMA foreign_keys = ${E};`)}}o(B,"selfTransactionalMigration");const bt=new Set([3,61,63,66]);function W(e){return e.prepare("PRAGMA user_version").get().user_version}o(W,"currentUserVersion");function se(e,t){F[t](e),e.exec(`PRAGMA user_version = ${t+1}`)}o(se,"applyMigration");function En(e){const t=W(e);if(t>F.length)throw new Error(`unsupported canvas schema version ${t} (latest supported ${F.length})`);if(t===F.length)return;const n=e.prepare("PRAGMA busy_timeout").get().timeout;e.exec("PRAGMA busy_timeout = 60000;");try{Bt(e)}finally{e.exec(`PRAGMA busy_timeout = ${n};`)}}o(En,"migrate");function Bt(e){for(;;){const t=W(e);if(t>=F.length)return;if(bt.has(t)){if(e.isTransaction)throw new Error(`canvas migration v${t+1} requires autocommit`);F[t](e);continue}if(e.isTransaction){se(e,t);continue}e.exec("BEGIN IMMEDIATE");try{W(e)===t&&se(e,t),e.exec("COMMIT")}catch(n){try{e.exec("ROLLBACK")}catch{}throw n}}}o(Bt,"runPendingMigrations");export{F as MIGRATIONS,En as migrate};
@@ -1 +1 @@
1
- var w=Object.defineProperty;var u=(n,e)=>w(n,"name",{value:e,configurable:!0});import{resolve as x}from"node:path";import{envNodeId as C}from"../../shared/env.js";import{cliClient as f}from"../../commands/api-client.js";import{defineBranch as h,defineLeaf as j}from"../command.js";import{resolveCommandRegistry as L}from"../command-manifests/registry.js";import{executeExternalLeaf as U}from"./transport/exec-invoke.js";import{invokeHttpLeaf as M}from"./transport/http-invoke.js";async function T(n){const e=await f().request("GET",`/v1/providers/${encodeURIComponent(n)}/tools`);return new Set(e.groups.flatMap(r=>r.leaves.map(a=>a.tool)))}u(T,"allowedProviderTools");function m(n,e,r){return n.children.some(a=>a.kind==="leaf"?r.has([...e,a.name].join(" ")):m(a,[...e,a.name],r))}u(m,"hasAllowedLeaf");function S(n,e,r){return n.children.filter(a=>a.tier!=="hidden"&&(a.kind==="leaf"?r.has([...e,a.name].join(" ")):m(a,[...e,a.name],r))).length}u(S,"visibleChildCount");function A(n,e,r){return async()=>{if(!C())return n.help;const a=await T(e.id);return{...n.help,listing:n.help.listing?.flatMap(t=>{const i=n.children.find(s=>s.name===t.name);if(i?.kind==="leaf")return a.has([...r,i.name].join(" "))?[t]:[];if(i?.kind!=="branch")return[];const o=[...r,i.name];if(!m(i,o,a))return[];const p=S(i,o,a);return[{...t,...t.subCount!==void 0?{subCount:p}:{}}]})}}}u(A,"providerHelp");const N="Runs on one of your devices. Add `--machine <machine_id>` to pick which; without it the call goes to your only device, and with several the answer is `machine_required` listing each one's machine_id. The `runtime:` prefix on the id is optional.";function g(n,e,r,a=!1){return{outputKind:"object",...a?{machineSelector:!0}:{},run:u(async(t,i)=>{const o={...t};for(const p of r.params){if(!("type"in p)||p.type!=="file")continue;const s=p.name.replace(/-([a-z])/g,(c,k)=>k.toUpperCase()),d=o[s];typeof d=="string"&&(o[s]=x(d))}return f({timeoutMs:0}).request("POST","/v1/provider/call",{provider:n,tool:e,arguments:o,...i?.machine===void 0?{}:{machine:i.machine}},{timeout:0})},"run")}}u(g,"providerLeafAdapter");function v(n,e){return/^[A-Za-z0-9_-]{1,64}$/.test(n)&&n!=="crtr"&&!e.has(n)}u(v,"validRootName");function B(n,e){const r=[],a=[];for(const t of n){const{provider:i,commands:o}=t;if(!Array.isArray(o)||t.status!=="available"&&!v(i,e))continue;if(t.status!=="available"){const s=t.error??"bundle installation pending",d=`Unavailable (${t.status}): ${s}`,c=h({name:i,rootEntry:{concept:t.display_name,desc:d,useWhen:d},help:{name:i,summary:d},children:[]});c.unavailable=d,a.push(c);continue}const p=u(s=>s.kind==="branch"?{...s,children:s.children.map(p)}:{...s,rest:{method:"GET",path:"/",params:{}}},"helpNode");for(const s of o)s.kind!=="branch"||!Array.isArray(s.children)||!v(s.name,e)||r.push({contributor:{name:i,scope:"user"},manifestName:s.name,node:p(s),adaptLeaf:u((d,c)=>g(i,c.slice(1).join(" "),d,t.transport==="machine"),"adaptLeaf")})}return[..._(L(r,e)),...a]}u(B,"composeSandboxProviderSubtrees");function y(n,e){return(r,a)=>{const t=n.transport,i=e(a);if(n.provider!==void 0)return g(n.provider.id,i.slice(1).join(" "),r,t.kind==="machine");if(t.kind==="exec"){const p=t.executable;if(p===void 0)throw new Error(`exec plugin "${n.plugin.name}" has an executable leaf without transport.executable`);return{outputKind:"object",run:u(async s=>U({plugin:n.plugin,executable:p,commandPath:i,output:r.output},s),"run")}}if(t.kind==="machine")throw new Error(`machine plugin "${n.plugin.name}" is not a capability provider`);const o=r;return{outputKind:o.rest.streaming===!0?"jsonl":"object",run:u((p,s)=>M({registration:{name:n.plugin.name,endpoint:t.endpoint,...t.authEnv!==void 0?{authEnv:t.authEnv}:{}},...n.manifest?.baseUrl!==void 0?{baseUrl:n.manifest.baseUrl}:{},...n.manifest?.timeouts!==void 0?{timeouts:n.manifest.timeouts}:{},params:o.params,rest:o.rest,output:o.output,commandPath:i},p,s),"run")}}}u(y,"adaptLeaf");function G(n){return{contributions:n.map(e=>({contributor:{name:e.plugin.name,scope:e.plugin.scope,sourceMarketplace:e.plugin.sourceMarketplace},node:e.node,manifestName:e.manifestName,adaptLeaf:y(e,r=>[e.manifestName,...r.slice(1)]),...e.provider!==void 0?{provider:e.provider}:{}})),coreMounts:[],issues:[]}}u(G,"adaptPluginContributions");function z(n){return n.map(e=>({parent:e.parent,plugin:e.plugin.name,contributor:{name:e.plugin.name,scope:e.plugin.scope,sourceMarketplace:e.plugin.sourceMarketplace},node:e.node,...e.provider!==void 0?{provider:e.provider}:{},adaptLeaf:y({...e,manifestName:e.node.name},r=>e.provider===void 0?[...r]:[e.provider.id,...r.slice(e.parent.length)])}))}u(z,"adaptPluginCoreMounts");function _(n){return n.contributions.map(e=>l(e,e.node,[e.node.name]))}u(_,"composeExternalSubtrees");function D(n,e){return n.filter(r=>r.parent.join("\0")===e.join("\0")).map(r=>r.node.kind==="branch"?l(r,r.node,[...e,r.node.name],r.provider===void 0?1:e.length):E(r,r.node,[...e,r.node.name]))}u(D,"composeCoreBranchMounts");function l(n,e,r,a=1){const t=e.rootEntry===void 0?void 0:{concept:e.rootEntry.concept,desc:e.rootEntry.description,useWhen:e.rootEntry.whenToUse},i=h({name:e.name,description:e.description,whenToUse:e.whenToUse,...e.tier!==void 0?{tier:e.tier}:{},...n.plugin!==void 0?{plugin:n.plugin}:{},...t!==void 0?{rootEntry:t}:{},help:{name:r.join(" "),summary:e.summary,...e.model!==void 0?{model:e.model}:{},...r.length===1&&n.extensionIssue!==void 0?{extensionIssue:n.extensionIssue}:{}},...e.passthrough!==void 0?{passthrough:e.passthrough}:{},children:e.children.map(o=>o.kind==="branch"?l(n,o,[...r,o.name],a):E(n,o,[...r,o.name]))});return n.provider!==void 0&&(i.effectiveHelp=A(i,n.provider,r.slice(a))),i}u(l,"buildBranch");function E(n,e,r){const a=n.adaptLeaf(e,r);return j({name:e.name,description:e.description,whenToUse:e.whenToUse,...e.tier!==void 0?{tier:e.tier}:{},...n.plugin!==void 0?{plugin:n.plugin}:{},...a.machineSelector===!0?{machineSelector:!0}:{},help:{name:r.join(" "),summary:e.summary,...a.machineSelector===!0?{guide:N}:{},params:e.params,output:e.output,outputKind:a.outputKind,effects:[...e.effects]},run:a.run})}u(E,"buildLeaf");export{G as adaptPluginContributions,z as adaptPluginCoreMounts,D as composeCoreBranchMounts,_ as composeExternalSubtrees,B as composeSandboxProviderSubtrees};
1
+ var k=Object.defineProperty;var u=(n,e)=>k(n,"name",{value:e,configurable:!0});import{resolve as x}from"node:path";import{envNodeId as C}from"../../shared/env.js";import{cliClient as f}from"../../commands/api-client.js";import{apiErrorToCliError as j}from"../io.js";import{ApiError as L}from"../../api/index.js";import{defineBranch as h,defineLeaf as T}from"../command.js";import{resolveCommandRegistry as U}from"../command-manifests/registry.js";import{executeExternalLeaf as M}from"./transport/exec-invoke.js";import{invokeHttpLeaf as A}from"./transport/http-invoke.js";async function S(n){const e=await f().request("GET",`/v1/providers/${encodeURIComponent(n)}/tools`);return new Set(e.groups.flatMap(r=>r.leaves.map(a=>a.tool)))}u(S,"allowedProviderTools");function m(n,e,r){return n.children.some(a=>a.kind==="leaf"?r.has([...e,a.name].join(" ")):m(a,[...e,a.name],r))}u(m,"hasAllowedLeaf");function N(n,e,r){return n.children.filter(a=>a.tier!=="hidden"&&(a.kind==="leaf"?r.has([...e,a.name].join(" ")):m(a,[...e,a.name],r))).length}u(N,"visibleChildCount");function _(n,e,r){return async()=>{if(!C())return n.help;const a=await S(e.id);return{...n.help,listing:n.help.listing?.flatMap(t=>{const i=n.children.find(s=>s.name===t.name);if(i?.kind==="leaf")return a.has([...r,i.name].join(" "))?[t]:[];if(i?.kind!=="branch")return[];const o=[...r,i.name];if(!m(i,o,a))return[];const p=N(i,o,a);return[{...t,...t.subCount!==void 0?{subCount:p}:{}}]})}}}u(_,"providerHelp");const b="Runs on one of your devices. Add `--machine <machine_id>` to pick which; without it the call goes to your only device, and with several the answer is `machine_required` listing each one's machine_id. The `runtime:` prefix on the id is optional.";function g(n,e,r,a=!1){return{outputKind:"object",...a?{machineSelector:!0}:{},run:u(async(t,i)=>{const o={...t};for(const p of r.params){if(!("type"in p)||p.type!=="file")continue;const s=p.name.replace(/-([a-z])/g,(c,w)=>w.toUpperCase()),d=o[s];typeof d=="string"&&(o[s]=x(d))}try{return await f({timeoutMs:0}).request("POST","/v1/provider/call",{provider:n,tool:e,arguments:o,...i?.machine===void 0?{}:{machine:i.machine}},{timeout:0})}catch(p){throw p instanceof L?j(p,void 0,{providerCall:!0}):p}},"run")}}u(g,"providerLeafAdapter");function v(n,e){return/^[A-Za-z0-9_-]{1,64}$/.test(n)&&n!=="crtr"&&!e.has(n)}u(v,"validRootName");function O(n,e){const r=[],a=[];for(const t of n){const{provider:i,commands:o}=t;if(!Array.isArray(o)||t.status!=="available"&&!v(i,e))continue;if(t.status!=="available"){const s=t.error??"bundle installation pending",d=`Unavailable (${t.status}): ${s}`,c=h({name:i,rootEntry:{concept:t.display_name,desc:d,useWhen:d},help:{name:i,summary:d},children:[]});c.unavailable=d,a.push(c);continue}const p=u(s=>s.kind==="branch"?{...s,children:s.children.map(p)}:{...s,rest:{method:"GET",path:"/",params:{}}},"helpNode");for(const s of o)s.kind!=="branch"||!Array.isArray(s.children)||!v(s.name,e)||r.push({contributor:{name:i,scope:"user"},manifestName:s.name,node:p(s),adaptLeaf:u((d,c)=>g(i,c.slice(1).join(" "),d,t.transport==="machine"),"adaptLeaf")})}return[...I(U(r,e)),...a]}u(O,"composeSandboxProviderSubtrees");function y(n,e){return(r,a)=>{const t=n.transport,i=e(a);if(n.provider!==void 0)return g(n.provider.id,i.slice(1).join(" "),r,t.kind==="machine");if(t.kind==="exec"){const p=t.executable;if(p===void 0)throw new Error(`exec plugin "${n.plugin.name}" has an executable leaf without transport.executable`);return{outputKind:"object",run:u(async s=>M({plugin:n.plugin,executable:p,commandPath:i,output:r.output},s),"run")}}if(t.kind==="machine")throw new Error(`machine plugin "${n.plugin.name}" is not a capability provider`);const o=r;return{outputKind:o.rest.streaming===!0?"jsonl":"object",run:u((p,s)=>A({registration:{name:n.plugin.name,endpoint:t.endpoint,...t.authEnv!==void 0?{authEnv:t.authEnv}:{}},...n.manifest?.baseUrl!==void 0?{baseUrl:n.manifest.baseUrl}:{},...n.manifest?.timeouts!==void 0?{timeouts:n.manifest.timeouts}:{},params:o.params,rest:o.rest,output:o.output,commandPath:i},p,s),"run")}}}u(y,"adaptLeaf");function W(n){return{contributions:n.map(e=>({contributor:{name:e.plugin.name,scope:e.plugin.scope,sourceMarketplace:e.plugin.sourceMarketplace},node:e.node,manifestName:e.manifestName,adaptLeaf:y(e,r=>[e.manifestName,...r.slice(1)]),...e.provider!==void 0?{provider:e.provider}:{}})),coreMounts:[],issues:[]}}u(W,"adaptPluginContributions");function F(n){return n.map(e=>({parent:e.parent,plugin:e.plugin.name,contributor:{name:e.plugin.name,scope:e.plugin.scope,sourceMarketplace:e.plugin.sourceMarketplace},node:e.node,...e.provider!==void 0?{provider:e.provider}:{},adaptLeaf:y({...e,manifestName:e.node.name},r=>e.provider===void 0?[...r]:[e.provider.id,...r.slice(e.parent.length)])}))}u(F,"adaptPluginCoreMounts");function I(n){return n.contributions.map(e=>l(e,e.node,[e.node.name]))}u(I,"composeExternalSubtrees");function Z(n,e){return n.filter(r=>r.parent.join("\0")===e.join("\0")).map(r=>r.node.kind==="branch"?l(r,r.node,[...e,r.node.name],r.provider===void 0?1:e.length):E(r,r.node,[...e,r.node.name]))}u(Z,"composeCoreBranchMounts");function l(n,e,r,a=1){const t=e.rootEntry===void 0?void 0:{concept:e.rootEntry.concept,desc:e.rootEntry.description,useWhen:e.rootEntry.whenToUse},i=h({name:e.name,description:e.description,whenToUse:e.whenToUse,...e.tier!==void 0?{tier:e.tier}:{},...n.plugin!==void 0?{plugin:n.plugin}:{},...t!==void 0?{rootEntry:t}:{},help:{name:r.join(" "),summary:e.summary,...e.model!==void 0?{model:e.model}:{},...r.length===1&&n.extensionIssue!==void 0?{extensionIssue:n.extensionIssue}:{}},...e.passthrough!==void 0?{passthrough:e.passthrough}:{},children:e.children.map(o=>o.kind==="branch"?l(n,o,[...r,o.name],a):E(n,o,[...r,o.name]))});return n.provider!==void 0&&(i.effectiveHelp=_(i,n.provider,r.slice(a))),i}u(l,"buildBranch");function E(n,e,r){const a=n.adaptLeaf(e,r);return T({name:e.name,description:e.description,whenToUse:e.whenToUse,...e.tier!==void 0?{tier:e.tier}:{},...n.plugin!==void 0?{plugin:n.plugin}:{},...a.machineSelector===!0?{machineSelector:!0}:{},help:{name:r.join(" "),summary:e.summary,...a.machineSelector===!0?{guide:b}:{},params:e.params,output:e.output,outputKind:a.outputKind,effects:[...e.effects]},run:a.run})}u(E,"buildLeaf");export{W as adaptPluginContributions,F as adaptPluginCoreMounts,Z as composeCoreBranchMounts,I as composeExternalSubtrees,O as composeSandboxProviderSubtrees};
@@ -20,8 +20,8 @@ type Registration = Pick<JoinedClient, 'runtime' | 'client_id' | 'issuer' | 'own
20
20
  export interface JoinResult extends Registration {
21
21
  /** False when the token matched the stored successful registration and no directory call was made. */
22
22
  registered: boolean;
23
- /** `restarted`: a daemon was running, join stopped it and started a new one on the joined config. `not_running`: none was running and none was started. `restart_failed`: the join is committed but the new daemon did not start (see `daemon_error`). */
24
- daemon: 'restarted' | 'not_running' | 'restart_failed';
23
+ /** `restarted`: a daemon was running, join stopped it and started a new one on the joined config. `started`: none was running and join started one, whose first grant sync installs the account's provider plugins (`crtr cloud …`). `not_running`: none was started (a hosted container's launcher owns its daemon). `restart_failed`: the join is committed but the new daemon did not start (see `daemon_error`). */
24
+ daemon: 'restarted' | 'started' | 'not_running' | 'restart_failed';
25
25
  daemon_error?: string;
26
26
  }
27
27
  /** How join takes a running daemon down and brings it back. Absent, join keeps
@@ -33,6 +33,8 @@ export interface JoinDaemonControl {
33
33
  stop(): Promise<void>;
34
34
  /** Start a daemon on the settings now on disk and wait until it serves. */
35
35
  start(): Promise<void>;
36
+ /** Waits, bounded, for a daemon that join started with none running to finish its first grant sync, which installs the account's provider plugins. Best effort: the join is committed either way. */
37
+ firstSync?(): Promise<void>;
36
38
  /** Whether this process runs inside a node, whose broker the stop kills. */
37
39
  insideNode(): boolean;
38
40
  }
@@ -1 +1 @@
1
- var J=Object.defineProperty;var s=(r,t)=>J(r,"name",{value:t,configurable:!0});import{createHash as N,generateKeyPairSync as R,randomUUID as U}from"node:crypto";import{existsSync as I,mkdirSync as D,readdirSync as L,rmSync as y}from"node:fs";import{hostname as O}from"node:os";import{join as S}from"node:path";import{InputError as p}from"../io.js";import{network as k}from"../errors.js";import{secretsDir as j}from"../layout.js";import{apiSocketPath as C}from"../canvas/paths.js";import{daemonAnswers as K}from"../layout-migrate/index.js";import{atomicWriteJson as _,readJsonIfExists as x}from"../fs-utils.js";import{updateConfig as T}from"../config.js";import{launcherPresent as A}from"../grants/sandboxes.js";import{crtrVersion as W}from"../../shared/crtr-version.js";import{pluginsDir as q}from"../scope.js";import{userScopeRoot as z}from"../scope.js";import{rewriteLocalProfileGrantees as B}from"../scoped-state/profiles.js";import{withJoinLock as F}from"./join-lock.js";function l(){return S(j(),"runtime-client.json")}s(l,"clientPath");function b(r){if(typeof r!="string")return!1;try{const{protocol:t}=new URL(r);return t==="http:"||t==="https:"}catch{return!1}}s(b,"isUrl");function $(r){const t=r??{},{runtime:u,client_id:e,issuer:a,owner:n,url:o}=t;if(typeof u!="string"||!/^runtime:.+/.test(u)||typeof e!="string"||e===""||!b(a)||typeof n!="string"||!/^user:.+/.test(n)||!b(o))throw k("directory registration answer is missing runtime, client_id, issuer, owner, or url");const{machine_id:i,is_default:c}=t;return{runtime:u,client_id:e,issuer:a,owner:n,url:o,...typeof i=="string"?{machine_id:i}:{},...typeof c=="boolean"?{is_default:c}:{}}}s($,"registration");function w(){return S(j(),"runtime-client.pending.json")}s(w,"pendingPath");function G(){try{const r=q("user");return!r||!I(r)?[]:L(r,{withFileTypes:!0}).filter(t=>t.isDirectory()&&!t.name.startsWith(".")).map(t=>t.name).sort()}catch{return[]}}s(G,"installedPluginIds");async function V(r,t,u,e){const a=`${r.replace(/\/$/,"")}/v1/runtimes/registration`;let n;try{n=await fetch(a,{method:"PUT",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify({jwk:u,name:e,platform:`${process.platform}-${process.arch}`,crtr_version:W(),plugins:G(),sandboxed:A()}),signal:AbortSignal.timeout(3e4)})}catch(i){throw k(`directory registration request to ${a} failed: ${i instanceof Error?i.message:String(i)}`)}const o=await n.text();if(!n.ok)throw n.status<500&&y(w(),{force:!0}),new p({error:"join_refused",message:`directory refused the registration: ${n.status} ${o.slice(0,500)}`,next:`Create a new join token at ${r.replace(/\/$/,"")}/account/machines (tokens are single-use and expire after 10 minutes), and run \`crtr sys join\` again.`});try{return $(JSON.parse(o))}catch(i){throw i instanceof SyntaxError?k("directory registration answer is not JSON"):i}}s(V,"register");function P(r){T("user",t=>{t.runtime=r.is_default===!1?{issuer:r.issuer,audience:r.url,unsandboxed:!0}:{...t.runtime,issuer:r.issuer,audience:r.url}}),B(z())}s(P,"commit");function mr(){const r=x(l());if(r?.token_sha256===void 0)return null;const{owner:t}=r;if(typeof t!="string"||!/^user:.+/.test(t))throw new Error(`${l()} was written by join but has a missing or invalid owner`);return t}s(mr,"joinedOwner");function v(r){return new p({error:"daemon_running",message:`cannot join while a daemon answers on ${r}`,next:"This daemon is managed outside crtr, so join cannot restart it: stop the daemon that owns this volume, then run `crtr sys join` again."})}s(v,"daemonRunningError");async function E(r,t,u,e){const a=x(l());if(a?.token_sha256===e)return{joined:$(a),registered:!1};D(j(),{recursive:!0,mode:448});let n=x(w());if(n?.token_sha256!==e||n.directory!==r||!n.jwk||!n.public_jwk){const{privateKey:h,publicKey:f}=R("ec",{namedCurve:"P-256"}),d=U();n={token_sha256:e,directory:r,jwk:{...h.export({format:"jwk"}),kid:d,alg:"ES256",use:"sig"},public_jwk:{...f.export({format:"jwk"}),kid:d,alg:"ES256",use:"sig"}},_(w(),n,{mode:384})}const{jwk:o,public_jwk:i}=n,c=await V(r,t,i,u);return{joined:c,registered:!0,client:{jwk:o,kid:o.kid,alg:"ES256",...c,token_sha256:e}}}s(E,"obtainRegistration");async function pr(r,t,u=O(),e){if(!b(r))throw new p({error:"invalid_directory",message:"directory must be an http or https URL",received:r,field:"directory",next:"Pass the directory base URL, for example https://directory.example."});if(t==="")throw new p({error:"invalid_token",message:"token is empty",field:"token",next:"Pass the enrollment token provisioning supplied."});const a=C(),n=N("sha256").update(t).digest("hex");if(e===void 0){if(await K(a))throw v(a);const{joined:o,registered:i,client:c}=await E(r,t,u,n);return c&&(_(l(),c,{mode:384}),y(w(),{force:!0})),P(o),{...o,registered:i,daemon:"not_running"}}return F(async()=>{const o=s(()=>new p({error:"join_inside_node",message:"a daemon is running and this command is inside a node: stopping the daemon would kill this node's broker and this command with it",next:"Run `crtr sys join` from a plain terminal outside any crtr node. It stops the daemon, joins, and starts it again for you."}),"refuseInsideNode");if(await e.running()&&e.insideNode())throw o();const{joined:i,registered:c,client:h}=await E(r,t,u,n);let f=!1,d;try{if(await e.running()){if(e.insideNode())throw o();if(f=!0,await e.stop(),await e.running())throw v(a)}if(h&&(_(l(),h,{mode:384}),y(w(),{force:!0})),P(i),await e.running()){if(e.insideNode())throw o();f=!0,await e.stop()}}catch(m){d=m}let g={daemon:"not_running"};if(f)try{await e.start(),g={daemon:"restarted"}}catch(m){g={daemon:"restart_failed",daemon_error:m instanceof Error?m.message:String(m)}}if(d!==void 0)throw d;return{...i,registered:c,...g}})}s(pr,"joinDirectory");export{G as installedPluginIds,pr as joinDirectory,mr as joinedOwner};
1
+ var J=Object.defineProperty;var s=(t,r)=>J(t,"name",{value:r,configurable:!0});import{createHash as N,generateKeyPairSync as R,randomUUID as U}from"node:crypto";import{existsSync as I,mkdirSync as D,readdirSync as L,rmSync as y}from"node:fs";import{hostname as O}from"node:os";import{join as b}from"node:path";import{InputError as p}from"../io.js";import{network as k}from"../errors.js";import{secretsDir as j}from"../layout.js";import{apiSocketPath as C}from"../canvas/paths.js";import{daemonAnswers as K}from"../layout-migrate/index.js";import{atomicWriteJson as _,readJsonIfExists as x}from"../fs-utils.js";import{updateConfig as T}from"../config.js";import{launcherPresent as A}from"../grants/sandboxes.js";import{crtrVersion as W}from"../../shared/crtr-version.js";import{pluginsDir as q}from"../scope.js";import{userScopeRoot as z}from"../scope.js";import{rewriteLocalProfileGrantees as B}from"../scoped-state/profiles.js";import{withJoinLock as F}from"./join-lock.js";function l(){return b(j(),"runtime-client.json")}s(l,"clientPath");function S(t){if(typeof t!="string")return!1;try{const{protocol:r}=new URL(t);return r==="http:"||r==="https:"}catch{return!1}}s(S,"isUrl");function $(t){const r=t??{},{runtime:u,client_id:e,issuer:a,owner:n,url:o}=r;if(typeof u!="string"||!/^runtime:.+/.test(u)||typeof e!="string"||e===""||!S(a)||typeof n!="string"||!/^user:.+/.test(n)||!S(o))throw k("directory registration answer is missing runtime, client_id, issuer, owner, or url");const{machine_id:i,is_default:c}=r;return{runtime:u,client_id:e,issuer:a,owner:n,url:o,...typeof i=="string"?{machine_id:i}:{},...typeof c=="boolean"?{is_default:c}:{}}}s($,"registration");function w(){return b(j(),"runtime-client.pending.json")}s(w,"pendingPath");function G(){try{const t=q("user");return!t||!I(t)?[]:L(t,{withFileTypes:!0}).filter(r=>r.isDirectory()&&!r.name.startsWith(".")).map(r=>r.name).sort()}catch{return[]}}s(G,"installedPluginIds");async function V(t,r,u,e){const a=`${t.replace(/\/$/,"")}/v1/runtimes/registration`;let n;try{n=await fetch(a,{method:"PUT",headers:{authorization:`Bearer ${r}`,"content-type":"application/json"},body:JSON.stringify({jwk:u,name:e,platform:`${process.platform}-${process.arch}`,crtr_version:W(),plugins:G(),sandboxed:A()}),signal:AbortSignal.timeout(3e4)})}catch(i){throw k(`directory registration request to ${a} failed: ${i instanceof Error?i.message:String(i)}`)}const o=await n.text();if(!n.ok)throw n.status<500&&y(w(),{force:!0}),new p({error:"join_refused",message:`directory refused the registration: ${n.status} ${o.slice(0,500)}`,next:`Create a new join token at ${t.replace(/\/$/,"")}/account/machines (tokens are single-use and expire after 10 minutes), and run \`crtr sys join\` again.`});try{return $(JSON.parse(o))}catch(i){throw i instanceof SyntaxError?k("directory registration answer is not JSON"):i}}s(V,"register");function P(t){T("user",r=>{r.runtime=t.is_default===!1?{issuer:t.issuer,audience:t.url,unsandboxed:!0}:{...r.runtime,issuer:t.issuer,audience:t.url}}),B(z())}s(P,"commit");function mt(){const t=x(l());if(t?.token_sha256===void 0)return null;const{owner:r}=t;if(typeof r!="string"||!/^user:.+/.test(r))throw new Error(`${l()} was written by join but has a missing or invalid owner`);return r}s(mt,"joinedOwner");function v(t){return new p({error:"daemon_running",message:`cannot join while a daemon answers on ${t}`,next:"This daemon is managed outside crtr, so join cannot restart it: stop the daemon that owns this volume, then run `crtr sys join` again."})}s(v,"daemonRunningError");async function E(t,r,u,e){const a=x(l());if(a?.token_sha256===e)return{joined:$(a),registered:!1};D(j(),{recursive:!0,mode:448});let n=x(w());if(n?.token_sha256!==e||n.directory!==t||!n.jwk||!n.public_jwk){const{privateKey:h,publicKey:f}=R("ec",{namedCurve:"P-256"}),d=U();n={token_sha256:e,directory:t,jwk:{...h.export({format:"jwk"}),kid:d,alg:"ES256",use:"sig"},public_jwk:{...f.export({format:"jwk"}),kid:d,alg:"ES256",use:"sig"}},_(w(),n,{mode:384})}const{jwk:o,public_jwk:i}=n,c=await V(t,r,i,u);return{joined:c,registered:!0,client:{jwk:o,kid:o.kid,alg:"ES256",...c,token_sha256:e}}}s(E,"obtainRegistration");async function pt(t,r,u=O(),e){if(!S(t))throw new p({error:"invalid_directory",message:"directory must be an http or https URL",received:t,field:"directory",next:"Pass the directory base URL, for example https://directory.example."});if(r==="")throw new p({error:"invalid_token",message:"token is empty",field:"token",next:"Pass the enrollment token provisioning supplied."});const a=C(),n=N("sha256").update(r).digest("hex");if(e===void 0){if(await K(a))throw v(a);const{joined:o,registered:i,client:c}=await E(t,r,u,n);return c&&(_(l(),c,{mode:384}),y(w(),{force:!0})),P(o),{...o,registered:i,daemon:"not_running"}}return F(async()=>{const o=s(()=>new p({error:"join_inside_node",message:"a daemon is running and this command is inside a node: stopping the daemon would kill this node's broker and this command with it",next:"Run `crtr sys join` from a plain terminal outside any crtr node. It stops the daemon, joins, and starts it again for you."}),"refuseInsideNode");if(await e.running()&&e.insideNode())throw o();const{joined:i,registered:c,client:h}=await E(t,r,u,n);let f=!1,d;try{if(await e.running()){if(e.insideNode())throw o();if(f=!0,await e.stop(),await e.running())throw v(a)}if(h&&(_(l(),h,{mode:384}),y(w(),{force:!0})),P(i),await e.running()){if(e.insideNode())throw o();f=!0,await e.stop()}}catch(m){d=m}let g={daemon:"not_running"};if(d===void 0||f)try{await e.start(),g={daemon:f?"restarted":"started"}}catch(m){g={daemon:"restart_failed",daemon_error:m instanceof Error?m.message:String(m)}}if(d!==void 0)throw d;return g.daemon==="started"&&await e.firstSync?.(),{...i,registered:c,...g}})}s(pt,"joinDirectory");export{G as installedPluginIds,pt as joinDirectory,mt as joinedOwner};
@@ -1,6 +1,6 @@
1
- var R=Object.defineProperty;var E=(e,t)=>R(e,"name",{value:t,configurable:!0});import{prepared as n}from"../sqlite-statements.js";import{hasUnreconciledWorktree as f}from"../canvas/worktrees.js";import{deleteDocument as p}from"./documents.js";import{publish as d}from"./events.js";import{getByRowid as w}from"./objects.js";import{rememberDeletedIsolated as h}from"../runs/confinement.js";let a=0;function W(e,t){if(t.type!=="node")throw new Error(`deleteNodeWithOwned: ${t.id} is a ${t.type}, not a node`);if(f(t.id))throw new Error(`refusing to delete node ${t.id}: its managed worktree is not reconciled`);const o=`graph_delete_${++a}`;e.exec(`SAVEPOINT ${o}`);try{S(e,t),e.exec(`RELEASE ${o}`)}catch(i){throw e.exec(`ROLLBACK TO ${o}`),e.exec(`RELEASE ${o}`),i}}E(W,"deleteNodeWithOwned");function j(e,t){const o=n(e,`SELECT o.* FROM identities i JOIN objects o ON o.rowid = i.object
2
- WHERE i.scheme = 'profile' AND i.external_id = ? AND o.type = 'profile' AND o.deleted_at IS NULL`).get(t);if(!o)return!1;const i=`graph_delete_${++a}`;e.exec(`SAVEPOINT ${i}`);try{u(e,o),n(e,"UPDATE objects SET deleted_at = ?, name = ? WHERE rowid = ?").run(new Date().toISOString(),`${o.name}.deleted-${o.rowid}`,o.rowid),d(e,{event:"deleted",object:w(e,o.rowid),actor:null,attrs:{}}),n(e,"DELETE FROM watches WHERE object = ? OR watcher = ?").run(o.rowid,o.rowid),e.exec(`RELEASE ${i}`)}catch(s){throw e.exec(`ROLLBACK TO ${i}`),e.exec(`RELEASE ${i}`),s}return!0}E(j,"deleteProfileWithOwned");function S(e,t){const o=u(e,t);n(e,"UPDATE objects SET deleted_at = ? WHERE rowid = ?").run(o,t.rowid),d(e,{event:"deleted",object:w(e,t.rowid),actor:null,attrs:{}}),n(e,"DELETE FROM watches WHERE object = ? OR watcher = ?").run(t.rowid,t.rowid),h(t.id,e),n(e,"DELETE FROM nodes WHERE node_id = ?").run(t.id)}E(S,"deleteInside");function u(e,t){const o=n(e,`WITH RECURSIVE sub(rowid) AS (
1
+ var R=Object.defineProperty;var E=(e,t)=>R(e,"name",{value:t,configurable:!0});import{prepared as n}from"../sqlite-statements.js";import{hasUnreconciledWorktree as f}from"../canvas/worktrees.js";import{deleteDocument as p}from"./documents.js";import{publish as s}from"./events.js";import{getByRowid as w}from"./objects.js";import{rememberDeletedIsolated as S}from"../runs/confinement.js";let a=0;function I(e,t){if(t.type!=="node")throw new Error(`deleteNodeWithOwned: ${t.id} is a ${t.type}, not a node`);if(f(t.id))throw new Error(`refusing to delete node ${t.id}: its managed worktree is not reconciled`);const o=`graph_delete_${++a}`;e.exec(`SAVEPOINT ${o}`);try{h(e,t),e.exec(`RELEASE ${o}`)}catch(i){throw e.exec(`ROLLBACK TO ${o}`),e.exec(`RELEASE ${o}`),i}}E(I,"deleteNodeWithOwned");function j(e,t){const o=n(e,`SELECT o.* FROM identities i JOIN objects o ON o.rowid = i.object
2
+ WHERE i.scheme = 'profile' AND i.external_id = ? AND o.type = 'profile' AND o.deleted_at IS NULL`).get(t);if(!o)return!1;const i=`graph_delete_${++a}`;e.exec(`SAVEPOINT ${i}`);try{u(e,o),n(e,"UPDATE objects SET deleted_at = ?, name = ? WHERE rowid = ?").run(new Date().toISOString(),`${o.name}.deleted-${o.rowid}`,o.rowid),s(e,{event:"deleted",object:w(e,o.rowid),actor:null,attrs:{}}),n(e,"DELETE FROM watches WHERE object = ? OR watcher = ?").run(o.rowid,o.rowid),e.exec(`RELEASE ${i}`)}catch(d){throw e.exec(`ROLLBACK TO ${i}`),e.exec(`RELEASE ${i}`),d}return!0}E(j,"deleteProfileWithOwned");function h(e,t){const o=u(e,t);n(e,"UPDATE objects SET deleted_at = ? WHERE rowid = ?").run(o,t.rowid),s(e,{event:"deleted",object:w(e,t.rowid),actor:null,attrs:{}}),n(e,"DELETE FROM watches WHERE object = ? OR watcher = ?").run(t.rowid,t.rowid),S(t.id,e),n(e,"UPDATE nodes SET run_id = NULL WHERE run_id = ? AND node_id != ? AND isolated = 0").run(t.id,t.id),n(e,"DELETE FROM nodes WHERE node_id = ?").run(t.id)}E(h,"deleteInside");function u(e,t){const o=n(e,`WITH RECURSIVE sub(rowid) AS (
3
3
  SELECT rowid FROM objects WHERE owner = ?
4
4
  UNION SELECT o.rowid FROM objects o JOIN sub ON o.owner = sub.rowid)
5
- SELECT o.* FROM objects o JOIN sub ON sub.rowid = o.rowid WHERE o.deleted_at IS NULL`).all(t.rowid),i=new Set(o.map(r=>r.rowid)),s=n(e,`SELECT e.src FROM edges e JOIN objects s ON s.rowid = e.src
6
- WHERE e.type = 'links-to' AND e.dst = ? AND s.deleted_at IS NULL`);for(let r=!0;r;){r=!1;for(const c of o)c.type!=="document"||!i.has(c.rowid)||s.all(c.rowid).some(({src:O})=>!i.has(O))&&(i.delete(c.rowid),r=!0)}const l=new Date().toISOString();for(const r of o)if(i.has(r.rowid)){if(r.type==="document"){p(e,null,r);continue}n(e,"UPDATE objects SET deleted_at = ? WHERE rowid = ?").run(l,r.rowid),d(e,{event:"deleted",object:w(e,r.rowid),actor:null,attrs:{}}),n(e,"DELETE FROM watches WHERE object = ? OR watcher = ?").run(r.rowid,r.rowid)}return l}E(u,"deleteOwned");export{W as deleteNodeWithOwned,j as deleteProfileWithOwned};
5
+ SELECT o.* FROM objects o JOIN sub ON sub.rowid = o.rowid WHERE o.deleted_at IS NULL`).all(t.rowid),i=new Set(o.map(r=>r.rowid)),d=n(e,`SELECT e.src FROM edges e JOIN objects s ON s.rowid = e.src
6
+ WHERE e.type = 'links-to' AND e.dst = ? AND s.deleted_at IS NULL`);for(let r=!0;r;){r=!1;for(const c of o)c.type!=="document"||!i.has(c.rowid)||d.all(c.rowid).some(({src:O})=>!i.has(O))&&(i.delete(c.rowid),r=!0)}const l=new Date().toISOString();for(const r of o)if(i.has(r.rowid)){if(r.type==="document"){p(e,null,r);continue}n(e,"UPDATE objects SET deleted_at = ? WHERE rowid = ?").run(l,r.rowid),s(e,{event:"deleted",object:w(e,r.rowid),actor:null,attrs:{}}),n(e,"DELETE FROM watches WHERE object = ? OR watcher = ?").run(r.rowid,r.rowid)}return l}E(u,"deleteOwned");export{I as deleteNodeWithOwned,j as deleteProfileWithOwned};
package/dist/core/io.d.ts CHANGED
@@ -59,6 +59,9 @@ interface ErrorPayloadBase {
59
59
  http_status?: number;
60
60
  /** Seconds the provider asked the caller to wait before retrying (a rate limit). */
61
61
  retry_after_s?: number;
62
+ /** The structured details the runtime attached to a refusal the caller must act on (a `machine_required`
63
+ * answer's `candidates`). Rendered below the message and kept in `--json`. */
64
+ details?: Record<string, unknown>;
62
65
  next: string;
63
66
  }
64
67
  export type ErrorPayload = ErrorPayloadBase & ({
@@ -116,9 +119,16 @@ export declare function diag(message: string): void;
116
119
  * under the daemon's coarse `provider_*` code and the provider's HTTP
117
120
  * status. The provider's code, `next`, `field` and `Retry-After` reach the
118
121
  * caller unchanged; the status→code map would flatten them to `conflict`.
119
- * 4. Otherwise map status→code (`STATUS_TO_CLI_CODE`) with the server message
122
+ * 4. A refusal the runtime itself rendered for `provider.call` (`options.providerCall`):
123
+ * its envelope code came from the runtime's own table (`machine_required`,
124
+ * `device_offline`, …), not from the HTTP status, so it is kept together with
125
+ * its `details`. The caller of `provider.call` is the only one who knows the
126
+ * envelope is that endpoint's, which is why this is an option and not inferred.
127
+ * 5. Otherwise map status→code (`STATUS_TO_CLI_CODE`) with the server message
120
128
  * and a caller-supplied-or-default `next`. */
121
- export declare function apiErrorToCliError(err: ApiError, next?: string): CrtrError;
129
+ export declare function apiErrorToCliError(err: ApiError, next?: string, options?: {
130
+ providerCall?: boolean;
131
+ }): CrtrError;
122
132
  /** Terminal error handler. Command-level failures (bad input, not-found,
123
133
  * ambiguous) surface as the JSON response on stdout so the caller parses one
124
134
  * contract. A daemon `ApiError` is translated to that same clean contract
package/dist/core/io.js CHANGED
@@ -1,7 +1,7 @@
1
- var x=Object.defineProperty;var s=(e,t)=>x(e,"name",{value:t,configurable:!0});import{appendFileSync as _,statSync as w}from"node:fs";import{CrtrError as a}from"./errors.js";import{ExitCode as u}from"../types.js";import{PREVIEW_RESULT_PATH_ENV as y}from"./preview-result-path.js";import{renderError as E}from"./render.js";import{ApiError as b}from"../api/index.js";let p=!1,o;const T=256*1024;function k(e){const t=process.env[y];o=t===void 0||t===""?void 0:{path:e,jsonl:[]}}s(k,"beginPreview");function l(e){const t=process.env[y];if(t===void 0||t==="")return;const n=`${JSON.stringify({path:o?.path??"",...e})}
2
- `;try{if((w(t,{throwIfNoEntry:!1})?.size??0)+Buffer.byteLength(n,"utf8")>T)return;_(t,n,"utf8")}catch{}}s(l,"publishPreview");function B(e){o!==void 0&&(e!=null?l({result:e}):(o.jsonl.length>0||o.error!==void 0)&&l({...o.jsonl.length>0?{jsonl:o.jsonl}:{},...o.error===void 0?{}:{error:o.error}}))}s(B,"publishPreviewResult");function m(e){o?.jsonl.push(e)}s(m,"recordPreviewLine");function q(e){try{const t=JSON.parse(e);t!==null&&typeof t=="object"&&!Array.isArray(t)&&m(t)}catch{}}s(q,"recordPreviewJsonLine");function F(e){o!==void 0&&(o.error=e)}s(F,"recordPreviewError");function G(e){p=e}s(G,"setJsonOutput");function K(){return p}s(K,"isJsonOutput");class h extends a{static{s(this,"InputError")}payload;constructor(t,n=u.USAGE){super(t.error,t.message,n,{...t}),this.name="InputError",this.payload=t}}async function z(){if(process.stdin.isTTY)return"";const e=[];for await(const t of process.stdin)e.push(t);return Buffer.concat(e).toString("utf8")}s(z,"readStdinRaw");async function D(e=200){return process.stdin.isTTY?"":await new Promise(t=>{const n=[];let r=!1;const i=s(()=>{r||(r=!0,clearTimeout(g),process.stdin.removeListener("data",f),process.stdin.removeListener("end",d),process.stdin.removeListener("error",c),process.stdin.pause(),t(Buffer.concat(n).toString("utf8")))},"finish"),f=s(S=>n.push(S),"onData"),d=s(()=>i(),"onEnd"),c=s(()=>i(),"onError"),g=setTimeout(i,e);process.stdin.on("data",f),process.stdin.on("end",d),process.stdin.on("error",c),process.stdin.resume()})}s(D,"peekStdinRaw");function V(e){process.stdout.write(JSON.stringify(e,null,2)+`
3
- `)}s(V,"emit");function Y(e){m(e),process.stdout.write(JSON.stringify(e)+`
4
- `)}s(Y,"emitLine");function H(e){return new Promise(t=>{let n=!1;const r=s(f=>{f.code==="EPIPE"&&i(!1)},"onErr"),i=s(f=>{n||(n=!0,process.stdout.off("error",r),t(f))},"finish");process.stdout.on("error",r);try{process.stdout.write(e,f=>i(f==null))}catch{i(!1)}})}s(H,"writeStdout");function M(e){process.stderr.write(e+`
5
- `)}s(M,"diag");function v(e){if(e instanceof h)return e.payload;const t=e.details!==void 0?e.details:{},n=t.primaryCompleted===!0&&t.result!==null&&typeof t.result=="object"&&!Array.isArray(t.result)?A(t.result):void 0,r=t.next!==void 0?t.next:"Inspect the error and adjust the call. See -h for the schema.",i={error:e.code,message:e.message,received:t.received,field:t.field,...typeof t.http_status=="number"?{http_status:t.http_status}:{},...typeof t.retry_after_s=="number"?{retry_after_s:t.retry_after_s}:{}};return n===void 0?{...i,next:r}:{...i,primaryCompleted:!0,result:n,next:r}}s(v,"payloadOf");function A(e){const t=new WeakSet;try{const n=JSON.stringify(e,(r,i)=>{if(typeof i=="bigint")return{$crtrType:"bigint",value:i.toString()};if(typeof i=="number"&&!Number.isFinite(i))return{$crtrType:"number",value:String(i)};if(typeof i>"u"||typeof i=="function"||typeof i=="symbol")return{$crtrType:typeof i};if(i!==null&&typeof i=="object"){if(t.has(i))return{$crtrType:"circular"};t.add(i)}return i});if(n!==void 0){const r=JSON.parse(n);if(r!==null&&typeof r=="object"&&!Array.isArray(r))return r}}catch{}return{$crtrType:"unavailable",message:"The completed primary result could not be represented as JSON."}}s(A,"jsonSafeResult");const O={400:"invalid_request",404:"not_found",409:"conflict",422:"unprocessable"};function N(e,t){if(e.code==="daemon_restarting")return new a("daemon_restarting",e.message,u.NETWORK,{next:"crtrd is now on the new generation \u2014 retry the command."});if(e.code==="layout_migration_required")return new a("layout_migration_required",e.message,u.NETWORK,{next:"Run `crtr sys migrate` while the runtime is offline, then restart it."});if(e.code==="daemon_unavailable")return new a("daemon_unavailable",e.message,u.NETWORK,{next:"Start it with `crtr sys daemon start`, then retry."});const n=e.details,r=R(e,n);if(e.status===503){const d=r.code??e.code,c={next:r.next??t??"Retry the command; if it persists, inspect the unavailable service named in the error."};return r.code!==void 0&&(r.field!==void 0&&(c.field=r.field),r.received!==void 0&&(c.received=r.received),r.retryAfterS!==void 0&&(c.retry_after_s=r.retryAfterS)),new a(d,e.message,u.NETWORK,c)}const i=Array.isArray(n?.violations)?n.violations.map(d=>`${String(d.field)} ${String(d.problem)}${d.received!==void 0?` (received ${JSON.stringify(d.received)})`:""}`):[],f=n&&typeof n.error=="string"&&typeof n.message=="string"&&typeof n.next=="string"?n:{error:r.code??O[e.status]??e.code,...r.field!==void 0?{field:r.field}:{},...r.received!==void 0?{received:r.received}:{},...r.retryAfterS!==void 0?{retry_after_s:r.retryAfterS}:{},message:i.length>0?`${e.message}: ${i.join("; ")}`:e.message.trim()!==""?e.message:`request refused (HTTP ${e.status}, ${e.code}) with no message`,next:r.next??t??(r.retryAfterS!==void 0?`Wait ${r.retryAfterS}s, then retry; do not retry sooner.`:e.status>=500?"This is a crtr bug, not a bad call. Retry; if it persists, report it (the daemon log holds the full cause).":e.status===404?"List nodes with `crtr node inspect list`.":"Inspect the error and adjust the call. See -h for the schema.")};return new h(f,P(f.error,e.status))}s(N,"apiErrorToCliError");function R(e,t){if(e.origin!=="provider"||t===void 0||t===null||typeof t!="object")return{};const n={};return typeof t.status=="number"&&typeof t.code=="string"&&/^[a-z][a-z0-9_]*$/.test(t.code)&&(n.code=t.code),typeof t.next=="string"&&t.next.trim()!==""&&(n.next=t.next),typeof t.field=="string"&&t.field!==""?n.field=t.field:e.param!==void 0&&(n.field=e.param),t.received!==void 0&&t.received!==null&&(n.received=t.received),typeof e.retryAfterS=="number"&&(n.retryAfterS=e.retryAfterS),n}s(R,"providerSupplied");function P(e,t){return e==="not_found"||t===404?u.NOT_FOUND:e==="ambiguous"?u.AMBIGUOUS:u.USAGE}s(P,"exitForCliCode");function $(e){if(e instanceof b){$(N(e));return}if(e instanceof a){const r=v(e);o!==void 0&&l({error:r});const i=p?JSON.stringify(r,null,2):E(r);process.stdout.write(i+`
6
- `),process.exitCode=e.exitCode;return}const t=e,n=t!=null&&typeof t.message=="string"?t.message:String(e);process.stderr.write(JSON.stringify({error:"internal",message:n,next:"This is a crtr bug, not a bad call. Retry; if it persists, report it."},null,2)+`
7
- `),process.exitCode=u.GENERAL}s($,"handle");export{h as InputError,T as PREVIEW_RESULT_MAX_BYTES,N as apiErrorToCliError,k as beginPreview,M as diag,V as emit,Y as emitLine,$ as handle,K as isJsonOutput,D as peekStdinRaw,B as publishPreviewResult,z as readStdinRaw,F as recordPreviewError,q as recordPreviewJsonLine,m as recordPreviewLine,G as setJsonOutput,H as writeStdout};
1
+ var b=Object.defineProperty;var i=(e,t)=>b(e,"name",{value:t,configurable:!0});import{appendFileSync as _,statSync as w}from"node:fs";import{CrtrError as c}from"./errors.js";import{ExitCode as u}from"../types.js";import{PREVIEW_RESULT_PATH_ENV as m}from"./preview-result-path.js";import{renderError as E}from"./render.js";import{ApiError as v}from"../api/index.js";let p=!1,d;const A=256*1024;function B(e){const t=process.env[m];d=t===void 0||t===""?void 0:{path:e,jsonl:[]}}i(B,"beginPreview");function y(e){const t=process.env[m];if(t===void 0||t==="")return;const s=`${JSON.stringify({path:d?.path??"",...e})}
2
+ `;try{if((w(t,{throwIfNoEntry:!1})?.size??0)+Buffer.byteLength(s,"utf8")>A)return;_(t,s,"utf8")}catch{}}i(y,"publishPreview");function q(e){d!==void 0&&(e!=null?y({result:e}):(d.jsonl.length>0||d.error!==void 0)&&y({...d.jsonl.length>0?{jsonl:d.jsonl}:{},...d.error===void 0?{}:{error:d.error}}))}i(q,"publishPreviewResult");function h(e){d?.jsonl.push(e)}i(h,"recordPreviewLine");function F(e){try{const t=JSON.parse(e);t!==null&&typeof t=="object"&&!Array.isArray(t)&&h(t)}catch{}}i(F,"recordPreviewJsonLine");function G(e){d!==void 0&&(d.error=e)}i(G,"recordPreviewError");function K(e){p=e}i(K,"setJsonOutput");function z(){return p}i(z,"isJsonOutput");class g extends c{static{i(this,"InputError")}payload;constructor(t,s=u.USAGE){super(t.error,t.message,s,{...t}),this.name="InputError",this.payload=t}}async function D(){if(process.stdin.isTTY)return"";const e=[];for await(const t of process.stdin)e.push(t);return Buffer.concat(e).toString("utf8")}i(D,"readStdinRaw");async function V(e=200){return process.stdin.isTTY?"":await new Promise(t=>{const s=[];let n=!1;const r=i(()=>{n||(n=!0,clearTimeout(a),process.stdin.removeListener("data",o),process.stdin.removeListener("end",l),process.stdin.removeListener("error",f),process.stdin.pause(),t(Buffer.concat(s).toString("utf8")))},"finish"),o=i(x=>s.push(x),"onData"),l=i(()=>r(),"onEnd"),f=i(()=>r(),"onError"),a=setTimeout(r,e);process.stdin.on("data",o),process.stdin.on("end",l),process.stdin.on("error",f),process.stdin.resume()})}i(V,"peekStdinRaw");function Y(e){process.stdout.write(JSON.stringify(e,null,2)+`
3
+ `)}i(Y,"emit");function H(e){h(e),process.stdout.write(JSON.stringify(e)+`
4
+ `)}i(H,"emitLine");function M(e){return new Promise(t=>{let s=!1;const n=i(o=>{o.code==="EPIPE"&&r(!1)},"onErr"),r=i(o=>{s||(s=!0,process.stdout.off("error",n),t(o))},"finish");process.stdout.on("error",n);try{process.stdout.write(e,o=>r(o==null))}catch{r(!1)}})}i(M,"writeStdout");function X(e){process.stderr.write(e+`
5
+ `)}i(X,"diag");function S(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}i(S,"isRecord");function T(e){if(e instanceof g)return e.payload;const t=e.details!==void 0?e.details:{},s=t.primaryCompleted===!0&&t.result!==null&&typeof t.result=="object"&&!Array.isArray(t.result)?O(t.result):void 0,n=t.next!==void 0?t.next:"Inspect the error and adjust the call. See -h for the schema.",r={error:e.code,message:e.message,received:t.received,field:t.field,...typeof t.http_status=="number"?{http_status:t.http_status}:{},...typeof t.retry_after_s=="number"?{retry_after_s:t.retry_after_s}:{},...S(t.details)?{details:t.details}:{}};return s===void 0?{...r,next:n}:{...r,primaryCompleted:!0,result:s,next:n}}i(T,"payloadOf");function O(e){const t=new WeakSet;try{const s=JSON.stringify(e,(n,r)=>{if(typeof r=="bigint")return{$crtrType:"bigint",value:r.toString()};if(typeof r=="number"&&!Number.isFinite(r))return{$crtrType:"number",value:String(r)};if(typeof r>"u"||typeof r=="function"||typeof r=="symbol")return{$crtrType:typeof r};if(r!==null&&typeof r=="object"){if(t.has(r))return{$crtrType:"circular"};t.add(r)}return r});if(s!==void 0){const n=JSON.parse(s);if(n!==null&&typeof n=="object"&&!Array.isArray(n))return n}}catch{}return{$crtrType:"unavailable",message:"The completed primary result could not be represented as JSON."}}i(O,"jsonSafeResult");const N={400:"invalid_request",404:"not_found",409:"conflict",422:"unprocessable"};function R(e,t,s={}){if(e.code==="daemon_restarting")return new c("daemon_restarting",e.message,u.NETWORK,{next:"crtrd is now on the new generation \u2014 retry the command."});if(e.code==="layout_migration_required")return new c("layout_migration_required",e.message,u.NETWORK,{next:"Run `crtr sys migrate` while the runtime is offline, then restart it."});if(e.code==="daemon_unavailable")return new c("daemon_unavailable",e.message,u.NETWORK,{next:"Start it with `crtr sys daemon start`, then retry."});const n=e.details,r=j(e,n,s.providerCall===!0);if(e.status===503||s.providerCall===!0&&e.status===504&&r.code!==void 0){const f=r.code??e.code,a={next:r.next??t??"Retry the command; if it persists, inspect the unavailable service named in the error."};return r.details!==void 0&&(a.details=r.details),r.code!==void 0&&(r.field!==void 0&&(a.field=r.field),r.received!==void 0&&(a.received=r.received),r.retryAfterS!==void 0&&(a.retry_after_s=r.retryAfterS)),new c(f,e.message,u.NETWORK,a)}const o=Array.isArray(n?.violations)?n.violations.map(f=>`${String(f.field)} ${String(f.problem)}${f.received!==void 0?` (received ${JSON.stringify(f.received)})`:""}`):[],l=n&&typeof n.error=="string"&&typeof n.message=="string"&&typeof n.next=="string"?n:{error:r.code??N[e.status]??e.code,...r.field!==void 0?{field:r.field}:{},...r.received!==void 0?{received:r.received}:{},...r.retryAfterS!==void 0?{retry_after_s:r.retryAfterS}:{},...r.details!==void 0?{details:r.details}:{},message:o.length>0?`${e.message}: ${o.join("; ")}`:e.message.trim()!==""?e.message:`request refused (HTTP ${e.status}, ${e.code}) with no message`,next:r.next??t??(r.retryAfterS!==void 0?`Wait ${r.retryAfterS}s, then retry; do not retry sooner.`:e.status>=500?"This is a crtr bug, not a bad call. Retry; if it persists, report it (the daemon log holds the full cause).":e.status===404?"List nodes with `crtr node inspect list`.":"Inspect the error and adjust the call. See -h for the schema.")};return new g(l,P(l.error,e.status))}i(R,"apiErrorToCliError");function j(e,t,s){const n=i(o=>typeof o=="string"&&/^[a-z][a-z0-9_]*$/.test(o),"snake");if(s&&e.origin!==void 0&&e.origin!=="provider"&&n(e.code)){const o={code:e.code};return(e.status===503||e.status===504)&&(o.next=e.retryable===!0?"Retry the command; if it persists, the machine or service named in the error is unavailable.":"Read the error before retrying: the action may already have run."),S(e.details)&&(o.details=e.details),e.param!==void 0&&(o.field=e.param),typeof e.retryAfterS=="number"&&(o.retryAfterS=e.retryAfterS),o}if(e.origin!=="provider"||t===void 0||t===null||typeof t!="object")return{};const r={};return typeof t.status=="number"&&n(t.code)&&(r.code=t.code),typeof t.next=="string"&&t.next.trim()!==""&&(r.next=t.next),typeof t.field=="string"&&t.field!==""?r.field=t.field:e.param!==void 0&&(r.field=e.param),t.received!==void 0&&t.received!==null&&(r.received=t.received),typeof e.retryAfterS=="number"&&(r.retryAfterS=e.retryAfterS),r}i(j,"providerSupplied");function P(e,t){return e==="not_found"||t===404?u.NOT_FOUND:e==="ambiguous"?u.AMBIGUOUS:u.USAGE}i(P,"exitForCliCode");function $(e){if(e instanceof v){$(R(e));return}if(e instanceof c){const n=T(e);d!==void 0&&y({error:n});const r=p?JSON.stringify(n,null,2):E(n);process.stdout.write(r+`
6
+ `),process.exitCode=e.exitCode;return}const t=e,s=t!=null&&typeof t.message=="string"?t.message:String(e);process.stderr.write(JSON.stringify({error:"internal",message:s,next:"This is a crtr bug, not a bad call. Retry; if it persists, report it."},null,2)+`
7
+ `),process.exitCode=u.GENERAL}i($,"handle");export{g as InputError,A as PREVIEW_RESULT_MAX_BYTES,R as apiErrorToCliError,B as beginPreview,X as diag,Y as emit,H as emitLine,$ as handle,z as isJsonOutput,V as peekStdinRaw,q as publishPreviewResult,D as readStdinRaw,G as recordPreviewError,F as recordPreviewJsonLine,h as recordPreviewLine,K as setJsonOutput,M as writeStdout};
@@ -1 +1 @@
1
- var _=Object.defineProperty;var a=(e,i)=>_(e,"name",{value:i,configurable:!0});import{directory as c,normalizeMachineId as h}from"../peer/directory.js";import{peerCall as m}from"../peer/client.js";import{ProviderError as t}from"./errors.js";const l={current:{list:a(()=>c.list(),"list"),lastList:a(()=>c.lastList(),"lastList"),call:a((e,i,n)=>m(e,i,n),"call")}},p="/v1/device/call",D=6e4,f=a(e=>({machine_id:e.machine_id,name:e.name,last_seen:e.last_seen}),"candidateView");function s(e,i){const n=e.map(r=>`${r.name} (${r.machine_id})`).join(", ");return new t("machine_required",i===void 0?`This account has ${e.length} devices: ${n}. Pick one with --machine <machine_id>.`:`${i} is not one of this account's devices. Devices: ${n}. Pick one with --machine <machine_id>.`,{param:"machine",details:{candidates:e.map(f)}})}a(s,"machineRequired");async function x(e,i=l.current){let n;try{n=await i.list()}catch(o){const d=i.lastList();if(d===null)throw new t("provider_unavailable",`The directory could not be reached to find your devices: ${o instanceof Error?o.message:String(o)}`,{origin:"directory"});n=d}const r=n.filter(o=>o.kind==="device");if(r.length===0)throw new t("provider_not_connected","No device is connected to this account. Install Northlight Desktop on your Mac and sign in.",{origin:"directory"});if(e!==void 0){const o=h(e),d=r.find(u=>u.machine_id===o);if(!d)throw s(r,e);return d}if(r.length>1)throw s(r,void 0);return r[0]}a(x,"selectDevice");function v(e){return{tool:e.tool,arguments:e.arguments,version:e.version,request_id:e.requestId,...e.runId===null?{}:{run_id:e.runId},deadline_at:e.deadlineAt,...e.upload===null?{}:{upload:e.upload}}}a(v,"deviceBody");async function A(e,i,n,r,o=l.current){const d=Math.max(1,i.deadlineAt-Date.now());return o.call(e.machine_id,{method:"POST",path:p,body:v(i)},{envelope:n,device:!0,signal:AbortSignal.any([r,AbortSignal.timeout(d)])})}a(A,"sendDeviceCall");function E(e,i,n){switch(i){case"timeout":return new t("device_timeout",`${e.name} did not answer in time.`,{origin:"runtime",details:{machine_id:e.machine_id}});case"directory_unreachable":case"no_directory":return new t("provider_unavailable",n,{origin:"directory"});case"unknown_machine":return new t("provider_not_connected",`${e.name} is no longer on this account.`,{origin:"directory"});case"not_a_crtr_peer":return new t("server_error",n);default:return new t("device_offline",`${e.name} is offline. ${n}`.trim(),{origin:"runtime",details:{machine_id:e.machine_id}})}}a(E,"deviceUnreachable");function b(e,i,n,r,o){if(i===401)return new t("device_offline",`${e.name}: Desktop is signed out. Sign in to Northlight Desktop again.`,{origin:"runtime",details:{machine_id:e.machine_id}});if(i===426){const d=o?.error?.details?.supported;return new t("peer_version_too_old",r??`${e.name} does not serve this device major. Update Northlight Desktop.`,{origin:"runtime",details:{machine_id:e.machine_id,...Array.isArray(d)?{supported:d}:{}}})}return i===502&&n==="machine_disconnected"?new t("device_disconnected",`${e.name} disconnected during the call; the action may have run.`,{origin:"runtime",details:{machine_id:e.machine_id}}):n==="payload_too_large"||n==="response_too_large"?new t("provider_error",r??"The call or its answer was too large for the relay (16 MiB).",{origin:"runtime",status:413,details:{code:n,message:r??null,status:i}}):null}a(b,"deviceFailure");export{D as DEFAULT_DEVICE_REQUEST_MS,p as DEVICE_CALL_PATH,v as deviceBody,l as deviceDeps,b as deviceFailure,E as deviceUnreachable,x as selectDevice,A as sendDeviceCall};
1
+ var _=Object.defineProperty;var a=(e,n)=>_(e,"name",{value:n,configurable:!0});import{directory as c,normalizeMachineId as h}from"../peer/directory.js";import{peerCall as m}from"../peer/client.js";import{ProviderError as t}from"./errors.js";const l={current:{list:a(()=>c.list(),"list"),lastList:a(()=>c.lastList(),"lastList"),call:a((e,n,i)=>m(e,n,i),"call")}},p="/v1/device/call",D=6e4,f=a(e=>({machine_id:e.machine_id,name:e.name,last_seen:e.last_seen}),"candidateView");function s(e,n){const i=e.map(r=>`${r.name} (${r.machine_id})`).join(", ");return new t("machine_required",n===void 0?`This account has ${e.length} devices: ${i}. Pick one with --machine <machine_id>.`:`${n} is not one of this account's devices. Devices: ${i}. Pick one with --machine <machine_id>.`,{param:"machine",details:{candidates:e.map(f)}})}a(s,"machineRequired");async function x(e,n=l.current){let i;try{i=await n.list()}catch(o){const d=n.lastList();if(d===null)throw new t("provider_unavailable",`The directory could not be reached to find your devices: ${o instanceof Error?o.message:String(o)}`,{origin:"directory"});i=d}const r=i.filter(o=>o.kind==="device");if(r.length===0)throw new t("provider_not_connected","No device is connected to this account. Install Northlight Desktop on your Mac and sign in.",{origin:"directory"});if(e!==void 0){const o=h(e),d=r.find(u=>u.machine_id===o);if(!d)throw s(r,e);return d}if(r.length>1)throw s(r,void 0);return r[0]}a(x,"selectDevice");function v(e){return{tool:e.tool,arguments:e.arguments,version:e.version,request_id:e.requestId,...e.runId===null?{}:{run_id:e.runId},deadline_at:e.deadlineAt,...e.upload===null?{}:{upload:e.upload}}}a(v,"deviceBody");async function A(e,n,i,r,o=l.current){const d=Math.max(1,n.deadlineAt-Date.now());return o.call(e.machine_id,{method:"POST",path:p,body:v(n)},{envelope:i,device:!0,signal:AbortSignal.any([r,AbortSignal.timeout(d)])})}a(A,"sendDeviceCall");function E(e,n,i){switch(n){case"timeout":return new t("device_timeout",`${e.name} did not answer in time.`,{origin:"runtime",details:{machine_id:e.machine_id}});case"directory_unreachable":case"no_directory":return new t("provider_unavailable",i,{origin:"directory"});case"unknown_machine":return new t("provider_not_connected",`${e.name} is no longer on this account.`,{origin:"directory"});case"not_a_crtr_peer":return new t("server_error",i);default:return new t("device_offline",i.trim()===""?`${e.name} is offline.`:i,{origin:"runtime",details:{machine_id:e.machine_id}})}}a(E,"deviceUnreachable");function b(e,n,i,r,o){if(n===401)return new t("device_offline",`${e.name}: Desktop is signed out. Sign in to Northlight Desktop again.`,{origin:"runtime",details:{machine_id:e.machine_id}});if(n===426){const d=o?.error?.details?.supported;return new t("peer_version_too_old",r??`${e.name} does not serve this device major. Update Northlight Desktop.`,{origin:"runtime",details:{machine_id:e.machine_id,...Array.isArray(d)?{supported:d}:{}}})}return n===502&&i==="machine_disconnected"?new t("device_disconnected",`${e.name} disconnected during the call; the action may have run.`,{origin:"runtime",details:{machine_id:e.machine_id}}):i==="payload_too_large"||i==="response_too_large"?new t("provider_error",r??"The call or its answer was too large for the relay (16 MiB).",{origin:"runtime",status:413,details:{code:i,message:r??null,status:n}}):null}a(b,"deviceFailure");export{D as DEFAULT_DEVICE_REQUEST_MS,p as DEVICE_CALL_PATH,v as deviceBody,l as deviceDeps,b as deviceFailure,E as deviceUnreachable,x as selectDevice,A as sendDeviceCall};
@@ -5,7 +5,7 @@ import type { ErrorPayload } from './io.js';
5
5
  * after. Scalars (and nested objects) list as `- name: value` bullets, prose
6
6
  * fields render as paragraphs, and arrays become a count lead-in plus a table
7
7
  * or bullets. No root tag — the result is read as a continuation of the prompt. */
8
- export declare function renderResult(result: Record<string, unknown>, help: LeafHelp): string;
8
+ export declare function renderResult(result: Record<string, unknown>, help: Pick<LeafHelp, 'output'>): string;
9
9
  /** Render a structured failure as an instruction-shaped block: what broke, what
10
10
  * was received, and the recovery road sign — the same recovery info the JSON
11
11
  * payload carries, shaped for the model to act on. */
@@ -1,6 +1,6 @@
1
- var h=Object.defineProperty;var u=(e,n)=>h(e,"name",{value:n,configurable:!0});const $=new Set(["follow_up","digest","message","next","result","content","body","guide","summary","note","reason","detail","details","hint","instruction","instructions","prompt","roadmap","report","answer","response","text"]);function y(e){return String(e).replace(/"/g,"'").replace(/\n/g," ")}u(y,"attrEsc");function f(e){return e==null?"":Array.isArray(e)?e.map(f).join(", "):typeof e=="object"?JSON.stringify(e):String(e)}u(f,"scalarStr");function m(e){const n=JSON.stringify(e,null,2);let s="```";for(;n.includes(s);)s+="`";return`${s}json
1
+ var h=Object.defineProperty;var u=(e,n)=>h(e,"name",{value:n,configurable:!0});const y=new Set(["follow_up","digest","message","next","result","content","body","guide","summary","note","reason","detail","details","hint","instruction","instructions","prompt","roadmap","report","answer","response","text"]);function $(e){return String(e).replace(/"/g,"'").replace(/\n/g," ")}u($,"attrEsc");function f(e){return e==null?"":Array.isArray(e)?e.map(f).join(", "):typeof e=="object"?JSON.stringify(e):String(e)}u(f,"scalarStr");function m(e){const n=JSON.stringify(e,null,2);let s="```";for(;n.includes(s);)s+="`";return`${s}json
2
2
  ${n}
3
- ${s}`}u(m,"fencedJson");function g(e,n){return typeof n!="string"?!1:$.has(e)?!0:n.includes(`
3
+ ${s}`}u(m,"fencedJson");function g(e,n){return typeof n!="string"?!1:y.has(e)?!0:n.includes(`
4
4
  `)||n.length>80}u(g,"isProse");function j(e){return f(e).replace(/\|/g,"\\|").replace(/\n/g," ")}u(j,"cellEsc");function b(e,n){if(n.length===0)return`0 ${e}.`;const s=`${n.length} ${e}:`;if(n.every(o=>o!==null&&typeof o=="object"&&!Array.isArray(o))){const o=n,i=[];for(const p of o)for(const a of Object.keys(p))i.includes(a)||i.push(a);const d=`| ${i.join(" | ")} |`,t=`| ${i.map(()=>"---").join(" | ")} |`,r=o.map(p=>`| ${i.map(a=>j(p[a])).join(" | ")} |`).join(`
5
5
  `);return`${s}
6
6
 
@@ -8,11 +8,11 @@ ${d}
8
8
  ${t}
9
9
  ${r}`}const c=n.map(o=>`- ${f(o)}`).join(`
10
10
  `);return`${s}
11
- ${c}`}u(b,"renderArray");function _(e,n){const s=n.output.map(t=>t.name);for(const t of Object.keys(e))s.includes(t)||s.push(t);const l=[],c=[],o=[];for(const t of s){const r=e[t];if(r!=null){if(Array.isArray(r)){o.push(b(t,r));continue}if(g(t,r)){c.push({name:t,text:r});continue}l.push(`- ${t}: ${f(r)}`)}}const i=[];l.length>0&&i.push(l.join(`
11
+ ${c}`}u(b,"renderArray");function O(e,n){const s=n.output.map(t=>t.name);for(const t of Object.keys(e))s.includes(t)||s.push(t);const l=[],c=[],o=[];for(const t of s){const r=e[t];if(r!=null){if(Array.isArray(r)){o.push(b(t,r));continue}if(g(t,r)){c.push({name:t,text:r});continue}l.push(`- ${t}: ${f(r)}`)}}const i=[];l.length>0&&i.push(l.join(`
12
12
  `));const d=c.length>1;for(const{name:t,text:r}of c)i.push(d?`**${t}:** ${r}`:r);for(const t of o)i.push(t);return i.join(`
13
13
 
14
- `)}u(_,"renderResult");function A(e){const n=[e.message];return e.received!==void 0&&e.received!==null&&e.received!==""&&n.push(`received: ${f(e.received)}`),e.field!==void 0&&n.push(`field: ${e.field}`),e.http_status!==void 0&&n.push(`http status: ${e.http_status}`),e.retry_after_s!==void 0&&n.push(`retry after: ${e.retry_after_s}s`),"primaryCompleted"in e&&e.primaryCompleted===!0&&(n.push("primaryCompleted: true"),n.push(`Primary result (the command already completed):
15
- ${m(e.result)}`)),n.push(`Next: ${e.next}`),`<error code="${y(e.error)}">
14
+ `)}u(O,"renderResult");function _(e){const n=[e.message];return e.received!==void 0&&e.received!==null&&e.received!==""&&n.push(`received: ${f(e.received)}`),e.field!==void 0&&n.push(`field: ${e.field}`),e.http_status!==void 0&&n.push(`http status: ${e.http_status}`),e.retry_after_s!==void 0&&n.push(`retry after: ${e.retry_after_s}s`),e.details!==void 0&&Object.keys(e.details).length>0&&n.push(O(e.details,{output:[]})),"primaryCompleted"in e&&e.primaryCompleted===!0&&(n.push("primaryCompleted: true"),n.push(`Primary result (the command already completed):
15
+ ${m(e.result)}`)),n.push(`Next: ${e.next}`),`<error code="${$(e.error)}">
16
16
  ${n.join(`
17
17
  `)}
18
- </error>`}u(A,"renderError");export{A as renderError,_ as renderResult};
18
+ </error>`}u(_,"renderError");export{_ as renderError,O as renderResult};