@north-light/cli 0.3.534 → 0.3.536
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/client.d.ts +3 -0
- package/dist/api/client.js +3 -3
- package/dist/api/contract.d.ts +1 -1
- package/dist/api/contract.js +1 -1
- package/dist/api/dto/nodes.d.ts +2 -0
- package/dist/api/dto/recovery.d.ts +2 -1
- package/dist/builtin-memory/crouter-sdk/release-notes.md +1 -1
- package/dist/clients/attach/viewer.js +406 -406
- package/dist/commands/api-client.d.ts +1 -0
- package/dist/commands/api-client.js +3 -3
- package/dist/commands/node/inspect.js +9 -9
- package/dist/core/bash-jobs.d.ts +4 -0
- package/dist/core/bash-jobs.js +8 -8
- package/dist/core/canvas/canvas.d.ts +26 -2
- package/dist/core/canvas/canvas.js +19 -19
- package/dist/core/canvas/crons.d.ts +4 -2
- package/dist/core/canvas/crons.js +1 -1
- package/dist/core/canvas/paths.d.ts +5 -0
- package/dist/core/canvas/paths.js +1 -1
- package/dist/core/canvas/recovery.d.ts +7 -0
- package/dist/core/canvas/recovery.js +2 -2
- package/dist/core/canvas/remote-canvas-source.d.ts +1 -0
- package/dist/core/canvas/remote-canvas-source.js +1 -1
- package/dist/core/canvas/source.d.ts +4 -0
- package/dist/core/canvas/source.js +1 -1
- package/dist/core/canvas/tree.d.ts +2 -0
- package/dist/core/canvas/tree.js +12 -5
- package/dist/core/feed/feed.js +3 -3
- package/dist/core/feed/messages.d.ts +6 -0
- package/dist/core/feed/messages.js +5 -5
- package/dist/core/graph/kinds/node.js +5 -5
- package/dist/core/graph/watches.d.ts +0 -2
- package/dist/core/graph/watches.js +7 -12
- package/dist/core/providers/mint.d.ts +2 -0
- package/dist/core/providers/mint.js +1 -1
- package/dist/core/runtime/broker/cli-relay.js +1 -1
- package/dist/core/runtime/broker/fault-retry.d.ts +18 -0
- package/dist/core/runtime/broker/fault-retry.js +1 -1
- package/dist/core/runtime/broker/run-turn-end.d.ts +3 -2
- package/dist/core/runtime/broker/run-turn-end.js +1 -1
- package/dist/core/runtime/launch.js +2 -2
- package/dist/core/runtime/lifecycle.d.ts +4 -0
- package/dist/core/runtime/lifecycle.js +2 -2
- package/dist/core/runtime/nodes.js +1 -1
- package/dist/core/runtime/revive.d.ts +1 -1
- package/dist/core/runtime/revive.js +1 -1
- package/dist/core/worktree-mutation-async.d.ts +10 -0
- package/dist/core/worktree-mutation-async.js +5 -5
- package/dist/core/worktree-sweep.js +6 -6
- package/dist/core/worktree.d.ts +1 -0
- package/dist/core/worktree.js +1 -1
- package/dist/daemon/api/handlers/broker-recovery.js +1 -1
- package/dist/daemon/api/handlers/messages.js +2 -2
- package/dist/daemon/api/handlers/nodes.js +1 -1
- package/dist/daemon/api/handlers/objects.js +3 -3
- package/dist/daemon/messaging/node-message.js +1 -1
- package/dist/daemon/peer/forward.js +1 -1
- package/dist/daemon/reconcilers/bash-deadline.d.ts +6 -0
- package/dist/daemon/reconcilers/bash-deadline.js +3 -2
- package/dist/daemon/runs/observer.d.ts +2 -0
- package/dist/daemon/runs/observer.js +2 -2
- package/docs/sdk/release-notes.md +1 -1
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +12 -12
- package/dist/core/canvas/nav-model.d.ts +0 -44
- package/dist/core/canvas/nav-model.js +0 -1
|
@@ -67,6 +67,7 @@ export declare class ApiCanvasSource implements CanvasSource {
|
|
|
67
67
|
getRow(nodeId: string): Promise<NodeRow | null>;
|
|
68
68
|
listNodes(filter?: {
|
|
69
69
|
status?: NodeStatus | NodeStatus[];
|
|
70
|
+
under?: string;
|
|
70
71
|
}): Promise<NodeRow[]>;
|
|
71
72
|
/** The browse screen uses one slim, compressed read for rows AND topology.
|
|
72
73
|
* The same roster index feeds subscriptionsOf/view until its normal TTL ends. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var
|
|
2
|
-
`).filter(c=>c.trim()!=="").slice(-
|
|
1
|
+
var y=Object.defineProperty;var a=(r,t)=>y(r,"name",{value:t,configurable:!0});import{closeSync as g,existsSync as v,fstatSync as C,openSync as N,readFileSync as m,readSync as R}from"node:fs";import{dirname as T,join as l}from"node:path";import{gunzipSync as E}from"node:zlib";import{ApiError as u}from"../api/index.js";import{localClient as f}from"../api/node-transport.js";import{apiErrorToCliError as p}from"../core/io.js";import{autostartDisabled as w,daemonAutostartStartupFailure as A,ensureDaemon as M}from"../daemon/manage.js";import{DAEMON_STARTUP_WINDOW_MS as b}from"../daemon/startup-policy.js";import{readState as O}from"../core/config.js";import{resolveRemoteCanvasTarget as I}from"../core/remote-canvas-target.js";import{RemoteCanvasSource as x}from"../core/canvas/remote-canvas-source.js";import{BROKER_RELEASED_MARKER as D,BROKER_RELAY_GENERATION as F,crtrHome as k}from"../core/canvas/paths.js";import{envTestNodeSocketWindowMs as L,isSandboxedNode as B}from"../shared/env.js";import{ownerOnly as W}from"../core/errors.js";function d(r){return{...r,fault:r.fault??null}}a(d,"withRowFault");function K(r,t){throw r instanceof u?p(r,t):r}a(K,"rethrowAsCliError");async function it(r){try{return await _().getNode(r)}catch(t){if(t instanceof u&&t.status===404)return null;K(t)}}a(it,"getNodeOrNull");const P=4096,j=20;function G(){let r;try{r=N(l(k(),"crtrd.log"),"r");const{size:t}=C(r),s=Math.max(0,t-P),n=t-s;if(n<=0)return;const e=Buffer.alloc(n);R(r,e,0,n,s);const o=e.toString("utf8").split(`
|
|
2
|
+
`).filter(c=>c.trim()!=="").slice(-j).join(`
|
|
3
3
|
`).trim();return o===""?void 0:`crtrd.log (tail):
|
|
4
|
-
${o}`}catch{return}finally{if(r!==void 0)try{
|
|
4
|
+
${o}`}catch{return}finally{if(r!==void 0)try{g(r)}catch{}}}a(G,"readColdStartDiagnostic");function _(r){const t=process.env.CRTR_API_SOCKET;if(t){const s=a(()=>{if(!v(t))try{const e=T(t),i=m(l(e,D),"utf8"),o=m(l(e,F),"utf8");if(i===o&&i!=="")return new u(503,"broker_released","This node's broker has been released, so crtr commands from its background jobs are not available.")}catch(e){if(e.code!=="ENOENT")throw e}},"availabilityFailure"),n=s();if(n!==void 0)throw p(n,"Run the command from the node's own turn instead.");return f({autostart:!1,availabilityFailure:s,coldStartPollWindowMs:(w()?L():void 0)??b,...r?.timeoutMs!==void 0?{timeoutMs:r.timeoutMs}:{}})}return w()?f({autostart:!1,...r?.timeoutMs!==void 0?{timeoutMs:r.timeoutMs}:{}}):f({...r?.timeoutMs!==void 0?{timeoutMs:r.timeoutMs}:{},onColdSocket:a(async()=>{M()},"onColdSocket"),coldStartDiagnostic:G,coldStartFailure:A,coldStartPollWindowMs:b})}a(_,"cliClient");const z=2e3;class S{static{a(this,"ApiCanvasSource")}client;rosterCache=null;rosterInFlight=null;constructor(t=_()){this.client=t}async roster(){const t=Date.now();return this.rosterCache!==null&&t-this.rosterCache.at<z?this.rosterCache.index:this.rosterInFlight!==null?this.rosterInFlight:(this.rosterInFlight=this.client.canvasRoster().then(s=>{const n={snapshot:s,subscriptions:new Map,subscribers:new Map};for(const e of s.edges){const i={node_id:e.to_id,active:e.active,created:e.created},o={node_id:e.from_id,active:e.active,created:e.created},c=n.subscriptions.get(e.from_id);c===void 0?n.subscriptions.set(e.from_id,[i]):c.push(i);const h=n.subscribers.get(e.to_id);h===void 0?n.subscribers.set(e.to_id,[o]):h.push(o)}return this.rosterCache={at:Date.now(),index:n},n}).finally(()=>{this.rosterInFlight=null}),this.rosterInFlight)}async orNull(t){try{return await t()}catch(s){if(s instanceof u&&s.status===404)return null;throw s}}async getNode(t){const s=await this.orNull(()=>this.client.getNode(t));return s===null?null:d(s)}async getRow(t){const s=await this.orNull(()=>this.client.getNode(t));return s===null?null:d(s)}async listNodes(t){const s=t?.status,n=t?.under===void 0?{}:{under:t.under,under_inclusive:!0};let e;if(Array.isArray(s)){const i=new Set(s);e=(await this.client.listNodes(n)).filter(c=>i.has(c.status))}else e=await this.client.listNodes({...s!==void 0?{status:s}:{},...n});return e.map(i=>d(i))}async browseSnapshot(){const t=await this.client.canvasBrowseCompressed(),s=JSON.parse(E(t).toString("utf8")),n={subscriptions:new Map,subscribers:new Map};for(const e of s.edges){const i=n.subscriptions.get(e.from_id)??[];i.push({node_id:e.to_id,active:e.active,created:e.created}),n.subscriptions.set(e.from_id,i);const o=n.subscribers.get(e.to_id)??[];o.push({node_id:e.from_id,active:e.active,created:e.created}),n.subscribers.set(e.to_id,o)}return this.rosterCache={at:Date.now(),index:n},s.nodes.map(e=>d(e))}async panelRoster(){const t=await this.roster();return t.snapshot!==void 0?t.snapshot:(this.rosterCache=null,(await this.roster()).snapshot)}async subscriptionsOf(t){return this.roster().then(s=>s.subscriptions.get(t)??[])}async subscribersOf(t){return this.roster().then(s=>s.subscribers.get(t)??[])}async view(t){const s=new Set([t]),n=[],e=(await this.subscriptionsOf(t)).map(i=>i.node_id);for(;e.length>0;){const i=e.shift();if(!s.has(i)){s.add(i),n.push(i);for(const o of await this.subscriptionsOf(i))s.has(o.node_id)||e.push(o.node_id)}}return n}async ticketCountsForNodes(t){return(await this.client.attentionCounts([...t])).counts}async ticketCountsForView(t){return this.ticketCountsForNodes([t,...await this.view(t)])}async newestReport(t){return(await this.client.getReports(t,{limit:1}))[0]??null}async hasActiveLiveSubscription(t){const s=(await this.subscriptionsOf(t)).filter(n=>n.active);for(const n of s){const e=await this.getRow(n.node_id);if(e&&(e.status==="active"||e.status==="idle"))return!0}return!1}}function ot(r,t=B()){const s=r?.scope??"user";if(t){if(r?.canvasName!==void 0)throw W("--canvas selects the person's remote canvas targets, which an app's node cannot reach","Omit --canvas; this node reads its own runtime's canvas.");return new S}const n=r?.canvasName??O(s).activeCanvas??null;if(!n)return new S;const{previewEndpoint:e,relayToken:i}=I(n,s);return new x(e,i)}a(ot,"cliCanvasSource");export{S as ApiCanvasSource,P as COLD_START_DIAGNOSTIC_TAIL_BYTES,ot as cliCanvasSource,_ as cliClient,it as getNodeOrNull,G as readColdStartDiagnostic,K as rethrowAsCliError};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
var R=Object.defineProperty;var h=(n,e)=>R(n,"name",{value:e,configurable:!0});import{defineBranch as P,defineLeaf as k}from"../../core/command.js";import{InputError as v}from"../../core/io.js";import{requireScope as I}from"../../core/errors.js";import{isPidAlive as O}from"../../core/canvas/pid.js";import{fullName as
|
|
2
|
-
`),"leaf")},run:h(async n=>{const e=h(
|
|
3
|
-
`)}},"run"),render:h(n=>{const e=n.node,
|
|
4
|
-
`));const
|
|
5
|
-
`);return`${f.length} ${
|
|
1
|
+
var R=Object.defineProperty;var h=(n,e)=>R(n,"name",{value:e,configurable:!0});import{defineBranch as P,defineLeaf as k}from"../../core/command.js";import{InputError as v}from"../../core/io.js";import{requireScope as I}from"../../core/errors.js";import{isPidAlive as O}from"../../core/canvas/pid.js";import{fullName as $}from"../../core/canvas/labels.js";import{fuzzyMatch as F}from"../../core/tui/fuzzy.js";import{nodeSnapshotLeaf as L}from"../node-snapshot.js";import{nodeTranscriptLeaf as A}from"../node-transcript.js";import{nodeContextLeaf as C}from"../node-context.js";import{cliClient as x,cliCanvasSource as q,rethrowAsCliError as w}from"../api-client.js";import{RemoteCanvasSource as S}from"../../core/canvas/remote-canvas-source.js";import{kindsStateBlock as U,profilesStateBlock as D,resolveProfileViaDaemon as z}from"../profile/daemon-profiles.js";import{focusViewer as E,nodeInPane as B}from"../surface/node/placement.js";const M=k({name:"list",description:"list nodes on the canvas",whenToUse:"you want a flat roster of the nodes on the canvas, sliced by displayed name, status, kind, mode, lifecycle, profile, cwd, and graph scope (--under a node, for that node plus its subscription descendants). The filters AND together. Use `node inspect show` instead to drill into one node and its spine neighbors, `canvas dashboard` for the tree shape, and `canvas attention` to find nodes blocked on the user",help:{name:"node inspect list",summary:"list nodes on the canvas, filtered by any combination of attribute slices and a graph-scope (--under) that AND together",params:[{kind:"flag",name:"status",type:"string",required:!1,constraint:"Filter status (comma-separated for several; accepted values: active, idle, done, dead, canceled). NOTE: `active` means the engine process is live (never closed), NOT that the node is generating right now \u2014 an active node is usually dormant between turns. To tell working-vs-idle, check the pi session-file mtime or CPU, not status."},{kind:"flag",name:"kind",type:"string",required:!1,constraint:"Filter by persona kind (comma-separated for several). The <kinds> list below names every top-level installable kind; a registered sub-kind filters too by exact path even though it is not listed."},{kind:"flag",name:"mode",type:"string",required:!1,constraint:"Filter by mode (comma-separated for several; accepted values: base, orchestrator)."},{kind:"flag",name:"lifecycle",type:"string",required:!1,constraint:"Filter by lifecycle (comma-separated for several; accepted values: terminal, resident)."},{kind:"flag",name:"profile",type:"string",required:!1,constraint:"Filter by profile. On the local canvas, accepts a profile id or manifest name from the <profiles> list below, or a close-enough name that resolves to the nearest profile. Against a remote canvas, pass the exact profile id carried by its node rows."},{kind:"flag",name:"name",type:"string",required:!1,constraint:"Filter by the displayed node label (handle plus generated description) with the canvas browser\u2019s case-insensitive fuzzy subsequence match."},{kind:"flag",name:"cwd",type:"string",required:!1,constraint:"Filter to nodes pinned to this exact cwd (absolute path). Roster is canvas-wide (all cwds) by default."},{kind:"flag",name:"under",type:"string",required:!1,constraint:'Graph scope: restrict to this node and its subscription descendants (the sub-DAG reachable downward \u2014 its reports, theirs, \u2026). This is "one initiative / my subtree". ANDs with every attribute filter above, narrowing any of them to this subtree.'},{kind:"flag",name:"canvas",type:"string",required:!1,constraint:"Read from the named remote canvas target (see `crtr canvas config list`) instead of the local canvas. Overrides `crtr canvas use`."}],output:[{name:"nodes",type:"object[]",required:!0,constraint:"Rows include {node_id, name, kind, mode, lifecycle, status, cwd, profile_id, parent, created}. `name` is the displayed node label searched by --name and shown in canvas views. `profile_id` is the stable profile-directory id (null only for historical no-profile nodes)."}],outputKind:"object",effects:["Read-only: lists nodes through the daemon API."]},helpState:{leaf:h(async()=>(await Promise.all([U(),D()])).join(`
|
|
2
|
+
`),"leaf")},run:h(async n=>{const e=h(a=>{const g=n[a];return g!==void 0&&g!==""?g.split(",").map(y=>y.trim()).filter(y=>y!==""):void 0},"csv"),s=e("status"),t=e("kind"),u=e("mode"),d=e("lifecycle"),m=n.profile,c=n.name,l=n.cwd,i=n.under,p=q({canvasName:n.canvas}),r=p instanceof S?m:m===void 0?void 0:(await z(m)).id,o=i!==void 0&&i!=="";if(o&&await p.getNode(i).catch(w)===null)throw new v({error:"not_found",message:`no node: ${i}`,next:"List nodes with `crtr node inspect list`, then pass a real id to --under."});return{nodes:(await p.listNodes({...s!==void 0?{status:s}:{},...o?{under:i}:{}}).catch(w)).filter(a=>!(t!==void 0&&!t.includes(a.kind)||u!==void 0&&!u.includes(a.mode)||d!==void 0&&!d.includes(a.lifecycle)||r!==void 0&&a.profile_id!==r||l!==void 0&&l!==""&&a.cwd!==l)).map(a=>({...a,name:$(a)})).filter(a=>c===void 0||c===""||F(c,a.name))}},"run")}),j=new WeakSet;async function T(n,e){const s=await n.getNode(e.node_id).catch(w);return{node_id:e.node_id,name:s?$(s):"(missing)",kind:s?.kind??"?",status:s?s.intent?`${s.status}/${s.intent}`:s.status:"?",active:e.active}}h(T,"enrichNeighbor");const K=k({name:"show",description:"show a node + its spine neighbors and drill-in pointers",whenToUse:"you want one node in depth \u2014 the debugging entrypoint: a glanceable meta slice, named spine neighbors, a pending-cron count, and exact drill-in paths. Use `node inspect list` for the flat roster or `canvas dashboard` for the whole-tree shape",help:{name:"node inspect show",summary:"show a node in depth \u2014 meta slice, named neighbors, pending-cron count, and drill-in pointers",params:[{kind:"positional",name:"node",required:!0,constraint:"Node id."},{kind:"flag",name:"canvas",type:"string",required:!1,constraint:"Read from the named remote canvas target (see `crtr canvas config list`) instead of the local canvas. Overrides `crtr canvas use`."}],output:[{name:"node",type:"object",required:!0,constraint:"The full node meta (system prompt + every telemetry field). The render surfaces a glanceable slice; `--json` carries the complete dump."},{name:"fault",type:"object",required:!0,constraint:"The active runtime fault projection, or null: {link, kind, retry: {disposition}, since}."},{name:"reports",type:"object[]",required:!0,constraint:"Who this node subscribes to (its reports/down), each enriched with {node_id, name, kind, status, active} \u2014 these are typically its spawned children."},{name:"managers",type:"object[]",required:!0,constraint:"Who subscribes to this node (its managers/up), same enriched shape."},{name:"pending_crons",type:"number",required:!0,constraint:"How many pending crons were created by this node. Inspect with `crtr cron list`."},{name:"paths",type:"object",required:!0,constraint:"Resolved on-disk locations: {context_dir, session_file} \u2014 read these directly to debug. Reports are documents: `crtr canvas read <node>/reports/<name>`."},{name:"follow_up",type:"string",required:!0,constraint:"The concrete next commands to drill deeper."}],outputKind:"object",effects:["Read-only: reads the node, its edges, and pending crons through crtrd."],preview:{suppressOutput:!0}},run:h(async n=>{const e=n.node,s=n.canvas,t=q({canvasName:s});if(t instanceof S){const o=await t.rawShow(e);if(o===null)throw new v({error:"not_found",message:`no node: ${e}`,next:"List nodes with `crtr node inspect list --canvas <name>`."});const f={...o};return j.add(f),f}const u=await t.getNode(e).catch(o=>w(o,"List nodes with `crtr node inspect list`."));if(u===null)throw new v({error:"not_found",message:`no node: ${e}`,next:"List nodes with `crtr node inspect list`."});const d=u,m=x(),c=await Promise.all((await t.subscriptionsOf(e)).map(o=>T(t,o))),l=await Promise.all((await t.subscribersOf(e)).map(o=>T(t,o))),i=(await m.listCrons({created_by:e}).catch(w)).filter(o=>o.created_by===e).length,p={context_dir:d.paths.context_dir,...d.pi_session_file!==void 0?{session_file:d.pi_session_file}:{}},r=[];return r.push(`List the documents it owns (reports, plans, notes): \`crtr canvas list --owner ${e}\`.`),r.push(`Browse its context dir (and its subscriptions') in nvim: \`crtr node inspect context ${e}\`.`),p.session_file&&r.push(`Read the raw conversation transcript: ${p.session_file}`),r.push(`On-disk context dir (ls/read directly): ${p.context_dir}`),i>0&&r.push(`It created ${i} pending cron(s): \`crtr cron list\`.`),c.length>0&&r.push(`Drill into a child/report: \`crtr node inspect show <node_id>\` (${c.map(o=>o.node_id).join(", ")}).`),r.push(`Full machine dump (system prompt + all telemetry): \`crtr --json node inspect show ${e}\`.`),{node:d,fault:d.fault??null,reports:c,managers:l,pending_crons:i,paths:p,follow_up:r.join(`
|
|
3
|
+
`)}},"run"),render:h(n=>{const e=n.node,s=String(e.node_id),t=e.pi_pid,d=j.has(n)?"remote (liveness not checked locally)":O(t)?"alive":"dead",m=e.intent?`/${String(e.intent)}`:"",c=n.fault,l=[];l.push(`# ${$(e)} (${s})`);const i=[`- kind/mode/lifecycle: ${e.kind} / ${e.mode} / ${e.lifecycle}`,`- status: ${e.status}${m}`,...c==null?[]:[`- fault: ${String(c.kind)} / ${String(c.retry?.disposition)} since ${String(c.since)}`],`- broker pid: ${t??"(none)"} \u2014 ${d}`,`- cwd: ${e.cwd}`,`- host: ${e.host_kind}`+(e.profile_id?` \xB7 profile ${e.profile_id}`:""),`- cycles: ${e.cycles} \xB7 created ${e.created}`],p=e.machine;p&&i.push(`- machine: ${p.name??p.id}`),e.parent&&i.push(`- parent: ${e.parent}`),i.push(`- creator: ${e.creator??"an external process"}`),e.waiting_for&&i.push(`- waiting for message: ${e.waiting_for}`),l.push(i.join(`
|
|
4
|
+
`));const r=h((o,f)=>{if(f.length===0)return`${o}: none`;const b=["node_id","name","kind","status","active"],_=`| ${b.join(" | ")} |`,a=`| ${b.map(()=>"---").join(" | ")} |`,g=f.map(y=>`| ${b.map(N=>String(y[N]??"").replace(/\|/g,"\\|").replace(/\n/g," ")).join(" | ")} |`).join(`
|
|
5
|
+
`);return`${f.length} ${o}:
|
|
6
6
|
|
|
7
|
-
${
|
|
8
|
-
${
|
|
9
|
-
${
|
|
7
|
+
${_}
|
|
8
|
+
${a}
|
|
9
|
+
${g}`},"table");return l.push(r("reports (down \u2014 subscriptions, usually children)",n.reports)),l.push(r("managers (up \u2014 subscribers)",n.managers)),l.push(`${n.pending_crons} pending cron(s)`),l.push(`Drill deeper:
|
|
10
10
|
${String(n.follow_up)}`),l.join(`
|
|
11
11
|
|
|
12
|
-
`)},"render")}),ie=k({name:"fork",description:"fork a node into an independent idle root with the same conversation and durable context",whenToUse:"a conversation has already accumulated the context you want, and you want a new user-driven branch beside it without sending a prompt or inheriting any graph-management responsibility. This copies the conversation and durable context, then waits for the fork\u2019s first real user message to supply its mandate. Use `node new --fork-from` instead when spawning a managed worker with an immediate task",help:{name:"node fork",summary:"create an independent resident root from a node\u2019s conversation and durable context, with no kickoff prompt or model turn",params:[{kind:"flag",name:"node",type:"string",required:!1,constraint:"Source node id. Mutually exclusive with --pane; one of them is required."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane showing the source node. Mutually exclusive with --node; one of them is required. The prefix menu passes this for you and the new viewer splits beside this pane."}],output:[{name:"node_id",type:"string",required:!0,constraint:"The new independent root id."},{name:"focused",type:"boolean",required:!0,constraint:"True when a new viewer pane opened beside the caller; false outside tmux."},{name:"session",type:"string",required:!1,constraint:"The tmux session containing the new viewer, when one opened."}],outputKind:"object",effects:["Creates a resident base-mode root with the source node\u2019s copied pi conversation, cwd, profile, kind, model override, and durable context files. Top-level initial-prompt.md, yield-message.md, result.json, and the jobs/ folder of background bash jobs are omitted.","Copies no parent, subscriptions, subscribers, children, crons, worktree, situational context, or lifecycle responsibility. Sends no prompt and consumes no model turn; the first real user message becomes the fork\u2019s mandate.","Inside tmux, opens and focuses a surface-attach viewer as an even-horizontal split in the source pane\u2019s window. Outside tmux, the root is still created but no interactive fallback opens."]},run:h(async n=>{I("crtr:act","crtr node fork");const e=n.pane,
|
|
12
|
+
`)},"render")}),ie=k({name:"fork",description:"fork a node into an independent idle root with the same conversation and durable context",whenToUse:"a conversation has already accumulated the context you want, and you want a new user-driven branch beside it without sending a prompt or inheriting any graph-management responsibility. This copies the conversation and durable context, then waits for the fork\u2019s first real user message to supply its mandate. Use `node new --fork-from` instead when spawning a managed worker with an immediate task",help:{name:"node fork",summary:"create an independent resident root from a node\u2019s conversation and durable context, with no kickoff prompt or model turn",params:[{kind:"flag",name:"node",type:"string",required:!1,constraint:"Source node id. Mutually exclusive with --pane; one of them is required."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane showing the source node. Mutually exclusive with --node; one of them is required. The prefix menu passes this for you and the new viewer splits beside this pane."}],output:[{name:"node_id",type:"string",required:!0,constraint:"The new independent root id."},{name:"focused",type:"boolean",required:!0,constraint:"True when a new viewer pane opened beside the caller; false outside tmux."},{name:"session",type:"string",required:!1,constraint:"The tmux session containing the new viewer, when one opened."}],outputKind:"object",effects:["Creates a resident base-mode root with the source node\u2019s copied pi conversation, cwd, profile, kind, model override, and durable context files. Top-level initial-prompt.md, yield-message.md, result.json, and the jobs/ folder of background bash jobs are omitted.","Copies no parent, subscriptions, subscribers, children, crons, worktree, situational context, or lifecycle responsibility. Sends no prompt and consumes no model turn; the first real user message becomes the fork\u2019s mandate.","Inside tmux, opens and focuses a surface-attach viewer as an even-horizontal split in the source pane\u2019s window. Outside tmux, the root is still created but no interactive fallback opens."]},run:h(async n=>{I("crtr:act","crtr node fork");const e=n.pane,s=n.node;if(s!==void 0&&e!==void 0)throw new v({error:"conflicting_target",message:"--node and --pane are mutually exclusive",next:"Pass the source node id or its viewer pane, not both."});let t=s;if(t===void 0&&e!==void 0&&(t=B(e)),t===void 0||t==="")throw new v({error:"no_node",message:"no source node was provided or found in the pane",next:"Pass --node <id>, or --pane <pane-id> for a pane showing a node."});const u=await x().forkNode(t).catch(w),d=await E(u.node_id,{cwd:u.cwd,name:u.name,...e!==void 0?{pane:e}:{},newPane:!0});return{node_id:u.node_id,focused:d.focused,session:d.session}},"run"),render:h(n=>{const e=n.focused===!0?` Viewer opened beside the source${n.session?` in session ${n.session}`:""}.`:" No viewer opened because no tmux pane was available.";return`Forked independent idle root ${n.node_id}.${e} It will make no model call until its first real user message.`},"render")}),re=P({name:"inspect",description:"read one node \u2014 its graph position, conversation, and context dir",whenToUse:"reading everything about ONE node you can name: enumerate the roster (`list`), inspect a node and its spine neighbors (`show`), or browse its on-disk context dir (`context`). The documents a node owns are `crtr canvas list --owner <id>`. Use `canvas dashboard` instead to render the whole tree SHAPE, or `canvas attention` to find which nodes are blocked on the user",help:{name:"node inspect",summary:"read one node (or the roster) \u2014 topology, conversation, and context dir",model:"Inspection is read-only and never revives a node. The roster is the selection surface; one-node metadata points to progressively heavier views: a parsed broker snapshot, rendered conversation, or context browser."},children:[M,K,L,A,C]});export{ie as nodeFork,re as nodeInspect};
|
package/dist/core/bash-jobs.d.ts
CHANGED
|
@@ -81,6 +81,10 @@ export declare function clearJobDeadline(paths: BashJobPaths): void;
|
|
|
81
81
|
/** Launcher jobs have a root-owned bwrap pid, not a process group; direct
|
|
82
82
|
* jobs keep their detached supervisor process group. */
|
|
83
83
|
export declare function isBashJobProcessGroupAlive(pgid: number, identity?: string): boolean;
|
|
84
|
+
/** Whether the job's process is CONFIRMED gone: a direct job's process group has no member
|
|
85
|
+
* (signal 0 answers ESRCH), a launcher job's pid is dead. EPERM, any other error, and a missing
|
|
86
|
+
* pgid or identity are not confirmation, so a job that cannot be read is never declared gone. */
|
|
87
|
+
export declare function isBashJobProcessConfirmedGone(pgid: number | undefined, identity: string | undefined): boolean;
|
|
84
88
|
/** Best-effort cancellation for a detached bash supervisor. It snapshots the
|
|
85
89
|
* observable descendant tree while the supervisor is still alive, signals its
|
|
86
90
|
* leaves before their parents, then uses the shared identity-guarded KILL
|
package/dist/core/bash-jobs.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
var
|
|
2
|
-
`)}o(
|
|
3
|
-
`)}o(Q,"writeJobDeadlineMs");function
|
|
4
|
-
`)}catch(
|
|
5
|
-
`)}catch(b){if(b.code!=="EEXIST")throw b}return{kind:"stopped",signaled:
|
|
6
|
-
`)+1):
|
|
7
|
-
`);return h[h.length-1]===""&&h.pop(),h.slice(-
|
|
8
|
-
`).map(n=>n.trim()).find(n=>n!=="")??"").replace(/\s+/g," ")}o(
|
|
1
|
+
var $=Object.defineProperty;var o=(e,t)=>$(e,"name",{value:t,configurable:!0});import{randomBytes as R}from"node:crypto";import{closeSync as B,openSync as j,readSync as y,statSync as T}from"node:fs";import{dirname as g,join as a}from"node:path";import{captureTeardownSnapshot as I,isAnyPidAlive as _,isPidAlive as S,killProcessTreePids as x}from"./canvas/pid.js";import{launcherRequest as C}from"../launcher/client.js";import{LaunchRefused as F}from"../launcher/spec.js";import{createFileBeneath as P,existsBeneath as O,readdirBeneath as v,readFileBeneath as z,renameBeneath as G,unlinkBeneath as H,writeFileBeneath as E}from"./spaces/open-beneath.js";const X=280;function q(e){if(typeof e!="string"||/[\p{Cc}\p{Cf}]/u.test(e))return null;const t=e.trim();return t===""?null:Buffer.byteLength(t,"utf8")<=X?t:null}o(q,"normalizeBashJobPurpose");function m(e){return a(e,"jobs")}o(m,"bashJobsDir");function p(e,t){return z(g(e),t).toString("utf8")}o(p,"readJobText");function J(e){return v(g(e),m(e)).map(t=>t.name)}o(J,"jobIds");function c(e,t){return O(e.root,t)}o(c,"has");function M(e,t){try{H(e,t)}catch(n){if(n.code!=="ENOENT")throw n}}o(M,"unlinkIfPresent");function ae(e){return typeof e=="string"&&e!==""&&e!=="."&&e!==".."&&/^[A-Za-z0-9._~-]+$/u.test(e)&&Buffer.byteLength(e,"utf8")<=128}o(ae,"isSafeBashJobId");function l(e,t){const n=a(m(e),t);return{jobId:t,root:g(e),dir:n,cmdSh:a(n,"cmd.sh"),jobLog:a(n,"job.log"),jobExit:a(n,"job.exit"),jobRun:a(n,"job.run"),jobBg:a(n,"job.bg"),jobDone:a(n,"job.done"),jobPgid:a(n,"job.pgid"),jobPgidIdentity:a(n,"job.pgid-identity"),jobPurpose:a(n,"purpose"),jobDeadline:a(n,"job.deadline")}}o(l,"bashJobPaths");function U(e,t){try{const n=Number(p(e,l(e,t).jobPgid).trim());return Number.isInteger(n)&&n>1?n:void 0}catch{return}}o(U,"readJobPgid");function W(e,t){try{const n=p(e,l(e,t).jobPgidIdentity).trim();return n===""?void 0:n}catch{return}}o(W,"readJobPgidIdentity");function K(e,t){try{return q(p(e,l(e,t).jobPurpose))}catch{return null}}o(K,"readJobPurpose");const A=10,Y=19;function k(e){return a(m(e),"niceness")}o(k,"bashNicenessPath");function ce(e){try{const t=Number(p(e,k(e)).trim());return!Number.isInteger(t)||t<0||t>Y?A:t}catch{return A}}o(ce,"readBashNiceness");function fe(e,t){E(g(e),k(e),`${Math.trunc(t)}
|
|
2
|
+
`)}o(fe,"writeBashNiceness");function Z(e,t){try{const n=Number(p(e,l(e,t).jobDeadline).trim());return Number.isFinite(n)?n:void 0}catch{return}}o(Z,"readJobDeadlineMs");function Q(e,t){E(e.root,e.jobDeadline,`${Math.trunc(t)}
|
|
3
|
+
`)}o(Q,"writeJobDeadlineMs");function de(e){try{M(e.root,e.jobDeadline)}catch{}}o(de,"clearJobDeadline");function le(e,t){if(t===`launcher:${e}`)return S(e);try{return process.kill(-e,0),!0}catch(n){return n.code==="EPERM"}}o(le,"isBashJobProcessGroupAlive");function he(e,t){if(e===void 0||t===void 0)return!1;if(t===`launcher:${e}`)return!S(e);try{return process.kill(-e,0),!1}catch(n){return n.code==="ESRCH"}}o(he,"isBashJobProcessConfirmedGone");async function V(e,t){const n=I(e,t);if(n.reused||n.tree.length===0)return!1;const r=[...n.tree].reverse(),u=_(r);return x(r,"SIGTERM",n.identities),await new Promise(i=>setTimeout(i,400)),x(r,"SIGKILL",n.identities),u}o(V,"cancelBashJobProcessTree");async function be(e,t,n=!1,r,u){const i=l(e,t);if(!c(i,i.dir))return{kind:"missing"};if(!c(i,i.jobBg)&&!(n&&c(i,i.jobRun))||c(i,i.jobExit))return{kind:"retired",logPath:i.jobLog};const s=U(e,t),f=W(e,t);if(s===void 0||f===void 0)return{kind:"not-stoppable",logPath:i.jobLog};const h=a(i.dir,"job.stop");try{P(i.root,h,u===void 0?"":`${u}
|
|
4
|
+
`)}catch(d){if(d.code==="EEXIST")return{kind:"retired",logPath:i.jobLog};throw d}try{let d;if(f===`launcher:${s}`){if(r===void 0)throw new Error("launcher job stop requires its owning node");try{if(!("ok"in await C({operation:"stop",pid:s,nodeId:r})))throw new Error("launcher did not stop job");d=!0}catch(b){if(!(b instanceof F&&b.refusal==="invalid_spec"))throw b;d=!1}}else d=await V(s,f);try{P(i.root,i.jobExit,`143
|
|
5
|
+
`)}catch(b){if(b.code!=="EEXIST")throw b}return{kind:"stopped",signaled:d,pgid:s,logPath:i.jobLog}}catch(d){throw M(i.root,h),d}}o(be,"stopBackgroundBashJob");function pe(e,t){if(t<=0)return[];const n=64*1024;let r;try{const u=T(e).size,i=Math.max(0,u-n),s=Buffer.alloc(Math.min(u,n));r=j(e,"r"),y(r,s,0,s.length,i);const f=s.toString("utf8"),h=(i>0?f.slice(f.indexOf(`
|
|
6
|
+
`)+1):f).split(`
|
|
7
|
+
`);return h[h.length-1]===""&&h.pop(),h.slice(-t)}catch{return[]}finally{r!==void 0&&B(r)}}o(pe,"tailJobLog");function ge(e){let t;try{t=j(e,"r");const n=Buffer.alloc(64*1024);let r=0,u=0;for(;;){const i=y(t,n,0,n.length,null);if(i===0)break;for(let s=0;s<i;s+=1)n[s]===10&&(r+=1);u=n[i-1]}return u!==0&&u!==10?r+1:r}catch{return 0}finally{t!==void 0&&B(t)}}o(ge,"countJobLogLines");function me(){return`${Date.now()}-${R(4).toString("hex")}`}o(me,"newBashJobId");function w(e){const t=e.indexOf("-");if(t<=0||t===e.length-1)return;const n=Number(e.slice(0,t));return Number.isFinite(n)?n:void 0}o(w,"jobStartedAtMs");function Be(e){const t=Math.max(0,Math.floor(e/1e3));if(t<60)return`${t}s`;const n=Math.floor(t/60),r=t%60;if(n<60)return`${n}m ${String(r).padStart(2,"0")}s`;const u=Math.floor(n/60),i=n%60;return`${u}h ${String(i).padStart(2,"0")}m`}o(Be,"formatBashElapsed");function je(e){return(e.split(`
|
|
8
|
+
`).map(n=>n.trim()).find(n=>n!=="")??"").replace(/\s+/g," ")}o(je,"bashJobCommandLine");function N(e,t=!1){let n;try{n=J(e)}catch{return[]}const r=[];for(const u of n){const i=w(u);if(i===void 0)continue;const s=l(e,u);let f;try{if(!c(s,s.jobBg)&&!(t&&c(s,s.jobRun))||c(s,s.jobExit))continue;f=p(e,s.cmdSh)}catch{continue}r.push({jobId:u,command:f,purpose:K(e,u),startedAtMs:i,logPath:s.jobLog,deadlineAtMs:Z(e,u)??null})}return r.sort((u,i)=>u.startedAtMs-i.startedAtMs)}o(N,"activeBackgroundBashJobs");function ye(e,t){return N(e).filter(n=>n.deadlineAtMs!==null&&t>=n.deadlineAtMs)}o(ye,"expiredAutoBackgroundBashJobs");function Se(e,t){if(!Number.isFinite(t)||t<=0)return 0;let n=0;for(const r of N(e))if(r.deadlineAtMs!==null)try{Q(l(e,r.jobId),r.deadlineAtMs+t),n+=1}catch{}return n}o(Se,"shiftAutoBackgroundBashDeadlines");function D(e){try{return G(e.root,e.jobRun,e.jobBg),"backgrounded"}catch(t){if(t.code!=="ENOENT")throw t;return c(e,e.jobBg)?"already-backgrounded":c(e,e.jobDone)?"finished":"missing"}}o(D,"backgroundBashJob");function L(e){let t;try{t=J(e)}catch{return[]}return t.flatMap(n=>{const r=l(e,n);try{return c(r,r.cmdSh)&&c(r,r.jobLog)&&c(r,r.jobRun)?[r]:[]}catch{return[]}})}o(L,"runningBashJobs");function xe(e){let t;for(const n of L(e)){const r=w(n.jobId);r!==void 0&&(t===void 0||r<t)&&(t=r)}return t}o(xe,"oldestRunningBashJobStartedAtMs");function Pe(e){const t=[];for(const n of L(e))D(n)==="backgrounded"&&t.push(n);return t}o(Pe,"backgroundRunningBashJobs");export{A as DEFAULT_BASH_NICENESS,Y as MAX_BASH_NICENESS,N as activeBackgroundBashJobs,D as backgroundBashJob,Pe as backgroundRunningBashJobs,je as bashJobCommandLine,l as bashJobPaths,m as bashJobsDir,k as bashNicenessPath,V as cancelBashJobProcessTree,de as clearJobDeadline,ge as countJobLogLines,ye as expiredAutoBackgroundBashJobs,Be as formatBashElapsed,he as isBashJobProcessConfirmedGone,le as isBashJobProcessGroupAlive,ae as isSafeBashJobId,w as jobStartedAtMs,me as newBashJobId,q as normalizeBashJobPurpose,xe as oldestRunningBashJobStartedAtMs,ce as readBashNiceness,Z as readJobDeadlineMs,U as readJobPgid,W as readJobPgidIdentity,K as readJobPurpose,Se as shiftAutoBackgroundBashDeadlines,be as stopBackgroundBashJob,pe as tailJobLog,fe as writeBashNiceness,Q as writeJobDeadlineMs};
|
|
@@ -149,11 +149,25 @@ export interface GraphNodeRow {
|
|
|
149
149
|
* needs. This keeps the interactive graph off the full node-summary projection
|
|
150
150
|
* and its unused runtime, outcome, and placement fields. */
|
|
151
151
|
export declare function graphNodes(): Promise<GraphNodeRow[]>;
|
|
152
|
-
export
|
|
152
|
+
export interface ListNodesFilter {
|
|
153
153
|
status?: NodeStatus | NodeStatus[];
|
|
154
154
|
withRecordedPid?: boolean;
|
|
155
155
|
withRecordedPidOrFrozen?: boolean;
|
|
156
|
-
|
|
156
|
+
/** Only the navigable nodes below this node (excluding it), resolved by one recursive query. */
|
|
157
|
+
under?: string;
|
|
158
|
+
/** With `under`: the node itself plus every node below it, human ask nodes included (the scope `node inspect list --under` shows). */
|
|
159
|
+
underInclusive?: boolean;
|
|
160
|
+
lifecycle?: string;
|
|
161
|
+
kind?: string;
|
|
162
|
+
mode?: string;
|
|
163
|
+
profileId?: string;
|
|
164
|
+
profilePrefix?: string;
|
|
165
|
+
parent?: string;
|
|
166
|
+
topLevel?: boolean;
|
|
167
|
+
/** Only nodes whose manager has no node row. */
|
|
168
|
+
hanging?: boolean;
|
|
169
|
+
}
|
|
170
|
+
export declare function listNodes(filter?: ListNodesFilter): Promise<NodeRow[]>;
|
|
157
171
|
/** Placement-only terminal rows for the daemon's residue pass. These columns
|
|
158
172
|
* preserve lastActivityMs's timestamp inputs and the teardown event fields;
|
|
159
173
|
* unlike node_rows, the nodes table needs no execution/session/fault joins. */
|
|
@@ -169,6 +183,16 @@ export declare function listNodesForSupervision(): Promise<NodeRow[]>;
|
|
|
169
183
|
* canceled, dead, terminal-lifecycle and deleting nodes are never reopened by mail.
|
|
170
184
|
* The SQL form of `isParkedResident` (runtime/lifecycle.ts), which must change with it; `n` is a `nodes` row. */
|
|
171
185
|
export declare const PARKED_RESIDENT_SQL: (n: string) => string;
|
|
186
|
+
/** A node the runtime can still bring back to life, so its cwd must keep existing: a live row
|
|
187
|
+
* (`active`/`idle`), a parked resident the lifecycle tick reopens when wake mail arrives
|
|
188
|
+
* (`PARKED_RESIDENT_SQL`), or a terminal row with no final latch that still carries a frozen
|
|
189
|
+
* mark, which the tick thaws (`node-lifecycle/tick.ts` `classifyRow`). `n` is a `nodes` row. */
|
|
190
|
+
export declare const RESUMABLE_NODE_SQL: (n: string) => string;
|
|
191
|
+
/** Every resumable node (`RESUMABLE_NODE_SQL`) other than `ownerId`, with the cwd it will run in. */
|
|
192
|
+
export declare function resumableNodeCwds(ownerId: string): Promise<Array<{
|
|
193
|
+
node_id: string;
|
|
194
|
+
cwd: string;
|
|
195
|
+
}>>;
|
|
172
196
|
/** Parked residents with unseen wake mail (`UNSEEN_WAKE_MAIL_SQL`), which nothing else
|
|
173
197
|
* launches: the lifecycle tick reopens them. Each parked resident seeks its own mail
|
|
174
198
|
* through `deliveries_destination`. */
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
var
|
|
1
|
+
var ee=Object.defineProperty;var i=(e,n)=>ee(e,"name",{value:n,configurable:!0});import{envNodeId as x}from"../../shared/env.js";import{rmdirSync as ne,rmSync as te,statSync as ae}from"node:fs";import{dirname as ie,join as I,resolve as oe}from"node:path";import{removeTreeBeneath as j}from"../spaces/open-beneath.js";import{emitEvent as re}from"../events/emit.js";import{withCanvasWrite as p,openDb as r}from"./db.js";import{conversationStore as se,ConversationNotFoundError as de}from"../conversation-store/index.js";import{hasUnseenWakeMail as ue,UNSEEN_WAKE_MAIL_SQL as le}from"./deliveries.js";import{isAppRun as W}from"./app-run.js";import{registerOutcomeAction as b}from"./outcome-actions.js";import{publish as ce}from"../graph/events.js";import{nodeObject as _e}from"../graph/objects.js";import{findNodeBySessionFileSql as pe}from"./sessions.js";import{hydrateFault as fe}from"./recovery.js";import{hasUnreconciledWorktree as Ee,openWorktree as we,readWorktree as ye,worktreeFromRow as me}from"./worktrees.js";import{isPidAlive as Ne,isPidPresent as F,recordedPidLiveness as Re}from"./pid.js";import{ensureHome as Se,jobDir as he,nodeDir as Oe,nodeRunDir as C}from"./paths.js";import{appForNode as Te,ensureNodeDirs as Le,nodeAgentDir as De}from"./node-agent-paths.js";import{appSpace as H}from"../layout.js";import{createObject as ge,getById as M,ownerForGrantee as h,setObjectKind as Ae,setObjectName as ve}from"../graph/objects.js";import{deleteNodeWithOwned as ke}from"../graph/deletion.js";import{SUBTREE_ALL_IDS_SQL as Ue,SUBTREE_IDS_SQL as xe}from"./tree.js";import{filterAsync as y,mapAsync as O}from"../async-array.js";function E(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}i(E,"jsonValue");async function cn(e,n){if((await e.prepare("SELECT deleting_at FROM nodes WHERE node_id = ?").get(n))?.deleting_at)throw new Error("run is deleting")}i(cn,"assertRunNotDeleting");async function Ie(e,n){const t=await h(n,e.grantee??"app:terminal");await ge(n,{id:e.node_id,type:"node",kind:e.kind,owner:t.id,privacy:"public",name:e.name,preview:""}),await n.prepare(`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, 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(40).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,E(e.persona_ack,"persona_ack"),E(e.review_binding,"review_binding"),E(e.launch,"launch"),E(e.scopes,"scopes"),e.isolated===!0?1:0,e.status??"active",e.intent??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)}i(Ie,"seedRow");function P(e){if(e.fault_id==null)return null;const n=Object.entries(e).filter(([t])=>t.startsWith("fault_")).map(([t,a])=>[t==="fault_id"?t:t.slice(6),a]);return fe(Object.fromEntries(n))}i(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,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=F(n.pi_pid),a=P(e);return n.fault=a?.link==="pi\u2192provider"&&a.retry.by==="daemon"&&a.retry.disposition==="auto"&&!t?null:a,n.streaming=e.turn_open_at!==null&&e.turn_closed_at===null&&t,n}i(f,"rowFrom");async function $(e,n){return n??=await ye(e.node_id),{...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}}}i($,"metaFrom");async function _n(e,n={}){return Se(),await p(async t=>{await Ie(e,t),e.managed_worktree!==void 0&&e.managed_worktree!==null&&await we(e.node_id,e.managed_worktree,t),n.outcomeDelivery!==void 0&&await b(t,e.node_id,n.outcomeDelivery),await n.withinCreate?.(t,e)}),await Le(e.node_id),await N(e.node_id)}i(_n,"createNode");async function N(e){const n=await(await r()).prepare("SELECT * FROM node_rows WHERE node_id = ?").get(e);return n===void 0?null:await $(n)}i(N,"getNode");const je=1440*60*1e3;function q(e){return{requestKey:e.request_key,nodeId:e.node_id,state:e.state}}i(q,"createRequestRecord");async function pn(e){const n=await(await r()).prepareUncached("SELECT request_key, node_id, state FROM create_requests WHERE request_key = ?").get(e);return n===void 0?null:q(n)}i(pn,"findCreateRequest");async function fn(e){return await p(async n=>{const t=Date.now();await n.prepareUncached("DELETE FROM create_requests WHERE expires_at <= ?").run(t);const a=await n.prepareUncached("SELECT request_key, node_id, state FROM create_requests WHERE request_key = ?").get(e);return a!==void 0?q(a):(await n.prepareUncached("INSERT INTO create_requests (request_key, node_id, expires_at, state) VALUES (?, NULL, ?, 'reserved')").run(e,t+je),{requestKey:e,nodeId:null,state:"reserved"})})}i(fn,"reserveCreateRequest");async function En(e=Date.now()){return Number((await(await r()).prepareUncached("DELETE FROM create_requests WHERE expires_at <= ?").run(e)).changes)}i(En,"pruneExpiredCreateRequests");async function wn(e,n){await(await r()).prepareUncached("UPDATE create_requests SET node_id = ?, state = 'complete' WHERE request_key = ?").run(n,e)}i(wn,"recordCreateRequestNode");async function yn(e,n){await(await r()).prepareUncached("UPDATE create_requests SET node_id = ?, state = 'reserved' WHERE request_key = ?").run(n,e)}i(yn,"reserveCreateRequestNode");async function mn(e,n){await(await r()).prepareUncached("UPDATE create_requests SET state = 'complete' WHERE request_key = ? AND node_id = ?").run(e,n)}i(mn,"completeCreateRequest");async function Nn(e,n){await p(async t=>{if(n===null){await t.prepareUncached("DELETE FROM create_requests WHERE request_key = ? AND node_id IS NULL").run(e);return}await t.prepareUncached("DELETE FROM create_requests WHERE request_key = ? AND node_id = ?").run(e,n)})}i(Nn,"abandonCreateRequest");function Rn(e){return oe(e)}i(Rn,"canonicalSessionFile");async function Sn(e){const n=await pe(e);return n===null?null:await N(n)}i(Sn,"findNodeBySessionFile");async function hn(e){const n=await(await r()).prepare("SELECT * FROM node_rows WHERE node_id = ?").get(e);return n?f(n):null}i(hn,"getRow");async function On(e){const n=await(await r()).prepare("SELECT * FROM node_rows WHERE pane = ?").get(e);return n?f(n):null}i(On,"getRowByPane");async function Tn(e){return(await(await r()).prepare(`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)}i(
|
|
5
|
-
WHERE profile_id = ? AND deadline_at IS NOT NULL AND status IN ('active', 'idle')`).all(e),o=t.prepare("UPDATE nodes SET deadline_at = ? WHERE node_id = ? AND deadline_at = ?");let
|
|
6
|
-
WHERE o.type = 'node' AND i.scheme = 'local-user' AND i.external_id = 'user:local'`,
|
|
4
|
+
ORDER BY deadline_at, created, node_id`).all(e)).map(f)}i(Tn,"dueNodeDeadlines");async function Ln(e,n){return Number((await(await r()).prepare("UPDATE nodes SET deadline_at = ? WHERE node_id = ?").run(n,e)).changes)===1}i(Ln,"setNodeDeadline");async function Dn(e,n){return!Number.isFinite(n)||n<=0?0:await p(async t=>{const a=await t.prepare(`SELECT node_id, deadline_at FROM nodes
|
|
5
|
+
WHERE profile_id = ? AND deadline_at IS NOT NULL AND status IN ('active', 'idle')`).all(e),o=t.prepare("UPDATE nodes SET deadline_at = ? WHERE node_id = ? AND deadline_at = ?");let s=0;for(const d of a){const l=Date.parse(d.deadline_at);Number.isNaN(l)||(s+=Number((await o.run(new Date(l+n).toISOString(),d.node_id,d.deadline_at)).changes))}return s})}i(Dn,"shiftProfileNodeDeadlines");async function gn(e,n,t){const a=`SELECT o.id FROM objects o JOIN identities i ON i.object = o.owner JOIN nodes n ON n.node_id = o.id
|
|
6
|
+
WHERE o.type = 'node' AND i.scheme = 'local-user' AND i.external_id = 'user:local'`,s=await e.prepareUncached(`${a} LIMIT 1`).get()!==void 0?Number((await e.prepareUncached(`UPDATE objects SET owner = ? WHERE id IN (${a})`).run((await h(e,"app:terminal")).id)).changes):0,d=`SELECT o.id FROM objects o JOIN peer_stubs s ON s.object = o.id JOIN identities i ON i.object = o.owner
|
|
7
7
|
WHERE o.type = 'node' AND o.reception = 2 AND s.envelope_json ->> 'app' IS NULL
|
|
8
|
-
AND i.scheme = 'app' AND i.external_id = 'app:terminal'`,
|
|
8
|
+
AND i.scheme = 'app' AND i.external_id = 'app:terminal'`,l=await e.prepareUncached(`${d} LIMIT 1`).get()!==void 0?Number((await e.prepareUncached(`UPDATE objects SET owner = ? WHERE id IN (${d})`).run((await h(e,"user:local")).id)).changes):0;return Number((await e.prepare("UPDATE nodes SET sub = ? WHERE sub = 'user:local'").run(n)).changes)+s+l+Number((await e.prepare(`UPDATE nodes SET sub = ? WHERE sub IS NULL AND node_id IN (
|
|
9
9
|
SELECT o.id FROM objects o JOIN identities i ON i.object = o.owner
|
|
10
|
-
WHERE o.type = 'node' AND i.scheme = 'app' AND i.external_id = 'app:terminal')`).run(n)).changes)+Number((await e.prepare("UPDATE nodes SET grant_id = NULL WHERE grant_id = ?").run(t)).changes)}i(
|
|
10
|
+
WHERE o.type = 'node' AND i.scheme = 'app' AND i.external_id = 'app:terminal')`).run(n)).changes)+Number((await e.prepare("UPDATE nodes SET grant_id = NULL WHERE grant_id = ?").run(t)).changes)}i(gn,"rewriteLocalNodeIdentity");function T(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`)}i(T,"sqlInput");async function An(e,n){const t=[],a=i((s,d=s)=>{Object.prototype.hasOwnProperty.call(n,s)&&t.push([d,T(n[s],s)])},"add");if(a("description"),a("title"),a("icon"),a("cycles"),a("mode"),a("lifecycle"),a("cwd"),a("host_kind"),a("profile_id"),a("parent"),a("spawned_by"),a("creator"),a("fork_from"),a("fork_source_file"),a("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",E(n.persona_ack,"persona_ack")]),Object.prototype.hasOwnProperty.call(n,"review_binding")&&t.push(["review_binding_json",E(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",E(n.scopes,"scopes")]),Object.prototype.hasOwnProperty.call(n,"launch")&&t.push(["launch_config_json",E(n.launch,"launch")]);const o=i(s=>Object.prototype.hasOwnProperty.call(n,s),"has");return await p(async s=>{const d=await M(s,e);if(d===null||d.type!=="node")throw new Error(`unknown node: ${e}`);if(o("name")&&await ve(s,d,T(n.name,"name")),o("kind")&&await Ae(s,d,T(n.kind,"kind")),t.length===0)return;if((await s.prepareUncached(`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}`)}),await N(e)}i(An,"updateNode");async function vn(e,n){await(await r()).prepare("UPDATE nodes SET status = ? WHERE node_id = ?").run(n,e)}i(vn,"setStatus");async function kn(e,n){await(await r()).prepare("UPDATE nodes SET intent = ? WHERE node_id = ?").run(n??null,e)}i(kn,"setIntent");async function Un(e,n,t={}){if(t.outcome===void 0){const o=[],s=[];Object.prototype.hasOwnProperty.call(n,"status")&&(o.push("status = ?"),s.push(n.status??null)),Object.prototype.hasOwnProperty.call(n,"intent")&&(o.push("intent = ?"),s.push(n.intent??null)),Object.prototype.hasOwnProperty.call(n,"terminal_reason")&&(o.push("terminal_reason = ?"),s.push(n.terminal_reason??null));const d=["node_id = ?"];s.push(e),t.fromStatuses!==void 0&&(d.push(`status IN (${t.fromStatuses.map(()=>"?").join(", ")})`),s.push(...t.fromStatuses)),t.requireNoFinalReport===!0&&d.push("final_report IS NULL");const l=await(await r()).prepareUncached(`UPDATE nodes SET ${o.join(", ")} WHERE ${d.join(" AND ")}`).run(...s);return Number(l.changes)}const a=t.outcome;return await p(async o=>{const s=[],d=[];Object.prototype.hasOwnProperty.call(n,"status")&&(s.push("status = ?"),d.push(n.status??null)),Object.prototype.hasOwnProperty.call(n,"intent")&&(s.push("intent = ?"),d.push(n.intent??null)),Object.prototype.hasOwnProperty.call(n,"terminal_reason")&&(s.push("terminal_reason = ?"),d.push(n.terminal_reason??null));const l=i((m,c)=>a.supersede===!0?"?":`CASE WHEN outcome_at IS NULL THEN ? ELSE ${c} END`,"outcomeValue");s.push(`outcome_kind = ${l(a.kind,"outcome_kind")}`,`outcome_reason = ${l(a.reason,"outcome_reason")}`,`outcome_detail_json = ${l(a.detailJson,"outcome_detail_json")}`,`outcome_revision = ${a.supersede===!0?"outcome_revision + 1":"CASE WHEN outcome_at IS NULL THEN outcome_revision + 1 ELSE outcome_revision END"}`,`outcome_at = ${l(a.at,"outcome_at")}`),d.push(a.kind,a.reason,a.detailJson,a.at);const u=["node_id = ?"];d.push(e),t.fromStatuses!==void 0&&(u.push(`status IN (${t.fromStatuses.map(()=>"?").join(", ")})`),d.push(...t.fromStatuses)),t.requireNoFinalReport===!0&&u.push("final_report IS NULL");const _=await o.prepareUncached(`UPDATE nodes SET ${s.join(", ")} WHERE ${u.join(" AND ")}`).run(...d);if(Number(_.changes)===0)return 0;const S=await o.prepare("SELECT outcome_at FROM nodes WHERE node_id = ?").get(e);return(a.supersede===!0||S.outcome_at===a.at)&&a.documentJson!==null&&a.documentJson!==void 0&&await ce(o,{event:"outcome",object:await _e(o,e),actor:null,attrs:{revision:Number((await o.prepare("SELECT outcome_revision FROM nodes WHERE node_id = ?").get(e)).outcome_revision)},messageBody:JSON.parse(a.documentJson)}),Number(_.changes)})}i(Un,"transitionStatusIntent");async function xn(e){return(await(await r()).prepare("SELECT final_report FROM nodes WHERE node_id = ?").get(e))?.final_report??null}i(xn,"getFinalReport");async function In(e,n){return(await(await r()).prepare("UPDATE nodes SET final_report = NULL, finalized_at = NULL WHERE node_id = ? AND final_report = ?").run(e,n)).changes>0}i(In,"clearFinalReportIfMatches");async function jn(e,n){await(await r()).prepare('UPDATE nodes SET tmux_session = ?, "window" = ?, pane = ? WHERE node_id = ?').run(n.tmux_session??null,n.window??null,n.pane??null,e)}i(jn,"setPresence");async function Wn(e,n){await(await r()).prepare("UPDATE nodes SET launched_at = ?, frozen_at = NULL, respawn_not_before = NULL WHERE node_id = ?").run(n,e)}i(Wn,"recordLaunch");async function bn(e,n){await(await r()).prepare("UPDATE nodes SET last_settled_at = ? WHERE node_id = ?").run(n,e)}i(bn,"recordSettledTurn");async function Fn(e,n){await(await r()).prepare("UPDATE nodes SET frozen_at = ? WHERE node_id = ?").run(n,e)}i(Fn,"setFrozen");async function Cn(e){await(await r()).prepare("UPDATE nodes SET frozen_at = NULL WHERE node_id = ?").run(e)}i(Cn,"clearFrozen");async function Hn(e,n){await(await r()).prepare("UPDATE nodes SET respawn_failures = ?, respawn_not_before = ? WHERE node_id = ?").run(n.failures,n.notBefore,e)}i(Hn,"recordRespawnAttempt");const w="node_id NOT IN (SELECT node_id FROM warm_pool)",B=["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 We(e){return e.worktree_state===null?null:me(Object.fromEntries(B.map(n=>[n,e[`worktree_${n}`]])))}i(We,"worktreeFromProjection");async function Mn(e){const n=B.map(a=>`w.${a} AS worktree_${a}`).join(", "),t=await(await r()).prepareUncached(`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);return await O(t,async a=>await $(a,We(a)))}i(Mn,"listNodesByProfile");async function Pn(){return(await(await r()).prepare(`SELECT
|
|
11
11
|
node_id, name, description, kind, mode, lifecycle, status, cwd, profile_id,
|
|
12
12
|
grantee, parent, created, frozen_at, terminal_reason, final_report,
|
|
13
13
|
telemetry_tokens_in, telemetry_updated_at, pi_pid, turn_open_at, turn_closed_at,
|
|
@@ -15,13 +15,13 @@ var Z=Object.defineProperty;var i=(e,n)=>Z(e,"name",{value:n,configurable:!0});i
|
|
|
15
15
|
fault_operation_id, fault_anchor_entry_id, fault_retry_disposition,
|
|
16
16
|
fault_retry_by, fault_retry_attempt, fault_retry_max, fault_retry_next_at,
|
|
17
17
|
fault_provider_error_json
|
|
18
|
-
FROM node_rows WHERE ${w} ORDER BY created`).all()).map(f)}i(
|
|
18
|
+
FROM node_rows WHERE ${w} ORDER BY created`).all()).map(f)}i(Pn,"browseNodes");async function $n(e,n){await(await r()).prepare(`UPDATE nodes
|
|
19
19
|
SET telemetry_tokens_in = ?,
|
|
20
20
|
telemetry_context_tokens = COALESCE(?, telemetry_context_tokens),
|
|
21
21
|
telemetry_last_activity = COALESCE(?, telemetry_last_activity),
|
|
22
22
|
telemetry_updated_at = ?
|
|
23
23
|
WHERE node_id = ?
|
|
24
|
-
AND (telemetry_updated_at IS NULL OR telemetry_updated_at < ?)`).run(n.tokensIn,n.contextTokens,n.lastActivity,n.updatedAt,e,n.updatedAt)}i(
|
|
24
|
+
AND (telemetry_updated_at IS NULL OR telemetry_updated_at < ?)`).run(n.tokensIn,n.contextTokens,n.lastActivity,n.updatedAt,e,n.updatedAt)}i($n,"updateNodeTelemetry");async function qn(){return(await(await r()).prepare(`SELECT
|
|
25
25
|
node_id, name, description, cycles, kind, status, frozen_at,
|
|
26
26
|
telemetry_context_tokens, telemetry_last_activity,
|
|
27
27
|
pi_pid, turn_open_at, turn_closed_at,
|
|
@@ -29,14 +29,14 @@ var Z=Object.defineProperty;var i=(e,n)=>Z(e,"name",{value:n,configurable:!0});i
|
|
|
29
29
|
fault_operation_id, fault_anchor_entry_id, fault_retry_disposition,
|
|
30
30
|
fault_retry_by, fault_retry_attempt, fault_retry_max, fault_retry_next_at,
|
|
31
31
|
fault_provider_error_json
|
|
32
|
-
FROM node_rows WHERE ${w} ORDER BY created`).all()).map(n=>{const t=n.pi_pid??null,a=F(t),o=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:o?.link==="pi\u2192provider"&&o.retry.by==="daemon"&&o.retry.disposition==="auto"&&!a?null:o,streaming:n.turn_open_at!==null&&n.turn_closed_at===null&&a}})}i(
|
|
32
|
+
FROM node_rows WHERE ${w} ORDER BY created`).all()).map(n=>{const t=n.pi_pid??null,a=F(t),o=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:o?.link==="pi\u2192provider"&&o.retry.by==="daemon"&&o.retry.disposition==="auto"&&!a?null:o,streaming:n.turn_open_at!==null&&n.turn_closed_at===null&&a}})}i(qn,"graphNodes");async function be(e){const n=[w],t=[];if(e?.under!==void 0&&(e.underInclusive===!0?(n.push(`(node_id IN (${Ue}) OR node_id = ?)`),t.push(e.under,e.under,e.under)):(n.push(`node_id IN (${xe})`),t.push(e.under,e.under))),e?.lifecycle!==void 0&&(n.push("lifecycle = ?"),t.push(e.lifecycle)),e?.kind!==void 0&&(n.push("kind = ?"),t.push(e.kind)),e?.mode!==void 0&&(n.push("mode = ?"),t.push(e.mode)),e?.profileId!==void 0&&(n.push("profile_id = ?"),t.push(e.profileId)),e?.profilePrefix!==void 0&&(n.push("profile_id LIKE ? ESCAPE '\\'"),t.push(`${e.profilePrefix.replace(/[\\%_]/g,o=>`\\${o}`)}%`)),e?.parent!==void 0&&(n.push("parent = ?"),t.push(e.parent)),e?.topLevel===!0&&n.push("parent IS NULL"),e?.hanging===!0&&n.push("parent IS NOT NULL AND parent NOT IN (SELECT node_id FROM node_rows)"),e?.status!==void 0){const o=Array.isArray(e.status)?e.status:[e.status];n.push(`status IN (${o.map(()=>"?").join(",")})`),t.push(...o)}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)"),(await(await r()).prepareUncached(`SELECT * FROM node_rows WHERE ${n.join(" AND ")} ORDER BY created`).all(...t)).map(f)}i(be,"listNodes");const Fe=`SELECT
|
|
33
33
|
node_id, created, launched_at, last_settled_at, finalized_at,
|
|
34
34
|
outcome_at, last_activity_at, pane, "window", tmux_session
|
|
35
35
|
FROM nodes
|
|
36
36
|
WHERE ${w}
|
|
37
37
|
AND status IN ('done', 'canceled', 'dead')
|
|
38
38
|
AND (pane IS NOT NULL OR "window" IS NOT NULL OR tmux_session IS NOT NULL)
|
|
39
|
-
ORDER BY created`;async function
|
|
39
|
+
ORDER BY created`;async function Bn(){return await(await r()).prepare(Fe).all()}i(Bn,"listTerminalPlacementResidue");const Ce=`WITH candidates AS MATERIALIZED (
|
|
40
40
|
SELECT node_id FROM nodes WHERE status IN ('active', 'idle')
|
|
41
41
|
UNION
|
|
42
42
|
SELECT node_id FROM nodes
|
|
@@ -50,19 +50,19 @@ var Z=Object.defineProperty;var i=(e,n)=>Z(e,"name",{value:n,configurable:!0});i
|
|
|
50
50
|
AND v.node_id NOT IN (SELECT node_id FROM warm_pool)
|
|
51
51
|
AND v.kind != 'human'
|
|
52
52
|
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)))
|
|
53
|
-
ORDER BY v.created`;async function
|
|
54
|
-
WHERE ${
|
|
53
|
+
ORDER BY v.created`;async function zn(){return(await(await r()).prepare(Ce).all()).map(f)}i(zn,"listNodesForSupervision");const z=i(e=>`(${e}.status = 'done' AND ${e}.intent = 'parked' AND ${e}.lifecycle = 'resident' AND ${e}.final_report IS NULL AND ${e}.deleting_at IS NULL)`,"PARKED_RESIDENT_SQL"),He=i(e=>`(${e}.status IN ('active', 'idle') OR ${z(e)} OR (${e}.status IN ('done', 'canceled', 'dead') AND ${e}.frozen_at IS NOT NULL AND ${e}.final_report IS NULL))`,"RESUMABLE_NODE_SQL");async function Jn(e){return await(await r()).prepare(`SELECT node_id, cwd FROM nodes WHERE ${He("nodes")} AND node_id != ?`).all(e)}i(Jn,"resumableNodeCwds");async function Yn(){return(await(await r()).prepare(`SELECT v.* FROM node_rows v
|
|
54
|
+
WHERE ${z("v")}
|
|
55
55
|
AND EXISTS (SELECT 1 FROM deliveries d WHERE d.destination_id = v.node_id AND ${le("d")}) AND v.kind != 'human' AND v.node_id NOT IN (SELECT node_id FROM warm_pool)
|
|
56
|
-
ORDER BY v.created`).all()).map(f)}i(
|
|
56
|
+
ORDER BY v.created`).all()).map(f)}i(Yn,"listParkedResidentsWithWakeMail");async function Qn(){return await(await r()).prepare(`SELECT node_id, name, created, grantee FROM node_rows WHERE ${w} ORDER BY created`).all()}i(Qn,"listNodeIdentities");async function Vn(e,n){await(await r()).prepare("INSERT INTO warm_pool (node_id, recipe_key, created) VALUES (?, ?, ?) ON CONFLICT (node_id) DO UPDATE SET recipe_key = excluded.recipe_key, created = excluded.created").run(e,n,new Date().toISOString())}i(Vn,"registerWarmSpare");async function Kn(e){return(await(await r()).prepare("DELETE FROM warm_pool WHERE node_id = ?").run(e)).changes>0}i(Kn,"unregisterWarmSpare");async function L(e,n){const t=`SELECT w.node_id AS node_id, w.recipe_key AS recipe_key, w.created AS created,
|
|
57
57
|
n.status AS status, n.pi_pid AS pi_pid, n.pi_pid_identity AS pi_pid_identity
|
|
58
|
-
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?await e.prepareUncached(t).all():await e.prepareUncached(t).all(n)).map(o=>({node_id:o.node_id,recipe_key:o.recipe_key,created:o.created,status:o.status,pi_pid:o.pi_pid??null,pi_pid_identity:o.pi_pid_identity??null}))}i(L,"warmSpareRowsFor");async function
|
|
59
|
-
FROM node_rows WHERE ${w} 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,description:e.description??null}))}i(
|
|
58
|
+
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?await e.prepareUncached(t).all():await e.prepareUncached(t).all(n)).map(o=>({node_id:o.node_id,recipe_key:o.recipe_key,created:o.created,status:o.status,pi_pid:o.pi_pid??null,pi_pid_identity:o.pi_pid_identity??null}))}i(L,"warmSpareRowsFor");async function Gn(){return await L(await r())}i(Gn,"listWarmSpares");async function Xn(e){return await(await r()).prepare("SELECT 1 FROM warm_pool WHERE node_id = ?").get(e)!==void 0}i(Xn,"isWarmSpare");function J(e){return e.status!=="active"&&e.status!=="idle"?!1:Re(e.pi_pid,e.pi_pid_identity)==="alive"}i(J,"spareIsClaimable");async function Zn(e,n,t={}){return await p(async a=>{const o=(await L(a,e)).find(J);return o===void 0||t.requestKey!==void 0&&(await a.prepareUncached("UPDATE create_requests SET node_id = ?, state = 'warm_claimed' WHERE request_key = ? AND node_id IS NULL").run(o.node_id,t.requestKey)).changes===0||(await a.prepare("DELETE FROM warm_pool WHERE node_id = ?").run(o.node_id)).changes===0?null:(await a.prepare("UPDATE nodes SET created = ?, deadline_at = ? WHERE node_id = ?").run(n,t.deadlineAt??null,o.node_id),t.outcomeDelivery!==void 0&&await b(a,o.node_id,t.outcomeDelivery),o.node_id)})}i(Zn,"claimWarmSpare");async function et(e){return(await L(await r(),e)).filter(J).length}i(et,"warmSpareCount");async function nt(e){return(await(await r()).prepare("SELECT node_id FROM nodes WHERE parent = ? ORDER BY created").all(e)).map(n=>n.node_id)}i(nt,"childrenOf");async function tt(){return(await(await r()).prepareUncached(`SELECT node_id, name, kind, mode, lifecycle, status, cwd, host_kind, parent, created, description
|
|
59
|
+
FROM node_rows WHERE ${w} 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,description:e.description??null}))}i(tt,"rosterNodes");async function at(e){return(await(await r()).prepareUncached(`SELECT * FROM node_rows
|
|
60
60
|
WHERE parent IS NULL AND kind != 'human'
|
|
61
61
|
AND status IN ('active','idle') AND cwd = ?
|
|
62
62
|
AND ${w}
|
|
63
|
-
ORDER BY created DESC`).all(e)).map(f)}i(
|
|
64
|
-
AND NOT EXISTS (SELECT 1 FROM sessions s WHERE s.node_id = n.node_id) LIMIT 1`);for(const o of e)if(!(await t.get(o)!==void 0||await a.get(o)!==void 0))try{await
|
|
63
|
+
ORDER BY created DESC`).all(e)).map(f)}i(at,"listResumableRoots");export*from"./tree.js";async function it(e,n,t){return await p(async a=>{const o=await a.prepare("SELECT status, final_report, frozen_at FROM nodes WHERE node_id = ?").get(e);return o===void 0?{kind:"missing"}:n(o)?{kind:"terminal",status:o.status,final_report:o.final_report}:{kind:"ran",result:await t()}})}i(it,"withFreshTerminalGuard");async function R(e){return await Ee(e)}i(R,"hasUnreconciledManagedWorktree");async function Y(e,n={}){return!["done","dead","canceled"].includes(e.status)||e.pi_pid!==null||e.frozen_at!==null||await R(e.node_id)||e.node_id===x()||W(e)?!1:n.pastTtl===!0||!["crashed","stranded",null].includes(e.terminal_reason??null)}i(Y,"isCollectible");async function D(e){if(e.length===0)return[];const n=e.map(()=>"?").join(",");return(await(await r()).prepareUncached(`SELECT DISTINCT session_id FROM sessions WHERE node_id IN (${n}) AND session_id IS NOT NULL`).all(...e)).map(({session_id:a})=>a)}i(D,"nodeConversationIds");async function g(e){const n=await r(),t=n.prepare("SELECT 1 FROM sessions WHERE session_id = ? LIMIT 1"),a=n.prepare(`SELECT 1 FROM nodes n WHERE n.fork_source_file LIKE '%\\_' || ? || '.jsonl' ESCAPE '\\'
|
|
64
|
+
AND NOT EXISTS (SELECT 1 FROM sessions s WHERE s.node_id = n.node_id) LIMIT 1`);for(const o of e)if(!(await t.get(o)!==void 0||await a.get(o)!==void 0))try{await se().delete(o)}catch(s){if(!(s instanceof de))throw s}}i(g,"deleteNodeConversations");async function A(e,n){const t=await M(e,n);return t===null||t.deleted_at!==null?!1:(await ke(e,t),!0)}i(A,"deleteNodeObject");async function v(e){return{nodeId:e,app:await N(e)===null?null:await Te(e)}}i(v,"nodeFiles");async function Q({nodeId:e,app:n}){n!==null&&j(H(n).nodes,await De(e,n)),j(ie(C(e)),C(e))}i(Q,"removeNodeAppTrees");function V({nodeId:e}){return[Oe(e)]}i(V,"nodeOwnedDirs");function K({nodeId:e,app:n}){if(n!==null)try{ne(I(H(n).conversations,e))}catch(t){if(!["ENOENT","ENOTEMPTY"].includes(t.code??""))throw t}}i(K,"removeNodeConversationDir");async function k(e){await Q(e);for(const n of V(e))te(n,{recursive:!0,force:!0});K(e)}i(k,"removeNodeFiles");async function ot(e){if(await R(e))return{deleted:!1};const n=await v(e),t=await D([e]),a=await p(async o=>(await A(o,e),{deleted:!0}));return await g(t),await k(n),a}i(ot,"deleteNode");async function rt(e){const n=[...new Set(e)];if(n.length===0)return[];const t=await y(n,R);if(t.length>0)throw new Error(`refusing to delete nodes with unreconciled managed worktrees: ${t.join(", ")}`);const a=await O(n,v),o=await D(n),s=n.map(()=>"?").join(","),d=(await(await r()).prepareUncached(`SELECT node_id FROM nodes WHERE node_id IN (${s}) ORDER BY created`).all(...n)).map(({node_id:u})=>u),l=await y(d,async u=>await p(async _=>await A(_,u)));await g(o);for(const u of l){const _=a[n.indexOf(u)];await U(_,async()=>await k(_))}return l}i(rt,"deleteNodes");async function st(e){const n=e.dryRun??!1,t=e.includeStale??!1,a=Date.now()-e.ttlDays*864e5,o=new Date(a).toISOString(),s=x()??"",d=await r(),l=i(c=>Z(c)<a,"quiet"),u=i(c=>({node_id:c.node_id,status:c.status,created:c.created}),"toPruned"),_=(await y((await d.prepare(`SELECT * FROM node_rows
|
|
65
65
|
WHERE status IN ('dead', 'done', 'canceled') AND created < ?
|
|
66
|
-
ORDER BY created`).all(o)).map(f),async
|
|
66
|
+
ORDER BY created`).all(o)).map(f),async c=>await Y(c,{pastTtl:!0})&&l(c)&&!await e.held(c.node_id))).map(u),S=t?(await y((await d.prepare(`SELECT * FROM node_rows
|
|
67
67
|
WHERE status IN ('active', 'idle') AND created < ?
|
|
68
|
-
ORDER BY created`).all(o)).map(f),async
|
|
68
|
+
ORDER BY created`).all(o)).map(f),async c=>c.node_id!==s&&!Ne(c.pi_pid??null)&&(c.frozen_at??null)===null&&(c.respawn_not_before??null)===null&&c.intent!=="idle-release"&&!W(c)&&!await ue(c.node_id)&&!await R(c.node_id)&&l(c)&&!await e.held(c.node_id))).map(u):[],m=await G([..._,...S]);return n||m.length===0?{pruned:m,dryRun:n}:(await X(m,e.removeDirs),{pruned:m,dryRun:n})}i(st,"pruneNodes");async function G(e){const t=(await r()).prepare("SELECT node_id FROM nodes WHERE run_id = ? AND node_id != ?"),a=new Set(e.map(o=>o.node_id));return await y(e,async o=>(await t.all(o.node_id,o.node_id)).every(s=>a.has(s.node_id)))}i(G,"retainRunRootsWithMembers");async function X(e,n){if(e.length===0)return;const t=await O(e,async o=>await v(o.node_id)),a=await D(e.map(o=>o.node_id));for(const o of e)await p(async s=>await A(s,o.node_id));if(await g(a),n!==void 0){for(const o of t)await U(o,async()=>{await Q(o),K(o)});n(t.flatMap(V));return}for(const o of t)await U(o,async()=>await k(o))}i(X,"deletePrunedNodes");async function U(e,n){try{n()}catch(t){re({level:"error",event:"canvas.node_files.remove_failed",error:t,fields:{node_id:e.nodeId}})}}i(U,"eachNodeBestEffort");function Z(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 a=Date.parse(t);Number.isFinite(a)&&a>n&&(n=a)}try{const t=ae(I(he(e.node_id),"telemetry.json"),{throwIfNoEntry:!1});t!==void 0&&t.mtimeMs>n&&(n=t.mtimeMs)}catch{}return n}i(Z,"lastActivityMs");async function dt(e){const n=e.dryRun??!1,t=await be(),a=await y(t,async u=>await Y(u)&&!await e.held(u.node_id)),o=t.length-a.length,s=Math.max(0,e.maxNodes-o),d=a.map(u=>({row:u,recency:Z(u)})).sort((u,_)=>_.recency-u.recency).slice(s).map(({row:u})=>({node_id:u.node_id,status:u.status,created:u.created})),l=await G(d);return n||l.length===0?{pruned:l,dryRun:n}:(await X(l),{pruned:l,dryRun:n})}i(dt,"pruneToLimit");export{z as PARKED_RESIDENT_SQL,He as RESUMABLE_NODE_SQL,Nn as abandonCreateRequest,cn as assertRunNotDeleting,Pn as browseNodes,Rn as canonicalSessionFile,nt as childrenOf,Zn as claimWarmSpare,In as clearFinalReportIfMatches,Cn as clearFrozen,mn as completeCreateRequest,_n as createNode,ot as deleteNode,rt as deleteNodes,Tn as dueNodeDeadlines,pn as findCreateRequest,Sn as findNodeBySessionFile,xn as getFinalReport,N as getNode,hn as getRow,On as getRowByPane,qn as graphNodes,Y as isCollectible,Xn as isWarmSpare,Z as lastActivityMs,Qn as listNodeIdentities,be as listNodes,Mn as listNodesByProfile,zn as listNodesForSupervision,Yn as listParkedResidentsWithWakeMail,at as listResumableRoots,Bn as listTerminalPlacementResidue,Gn as listWarmSpares,v as nodeFiles,Ce as nodesForSupervisionSql,En as pruneExpiredCreateRequests,st as pruneNodes,dt as pruneToLimit,wn as recordCreateRequestNode,Wn as recordLaunch,Hn as recordRespawnAttempt,bn as recordSettledTurn,Vn as registerWarmSpare,k as removeNodeFiles,fn as reserveCreateRequest,yn as reserveCreateRequestNode,Jn as resumableNodeCwds,gn as rewriteLocalNodeIdentity,tt as rosterNodes,Fn as setFrozen,kn as setIntent,Ln as setNodeDeadline,jn as setPresence,vn as setStatus,Dn as shiftProfileNodeDeadlines,Fe as terminalPlacementResidueSql,Un as transitionStatusIntent,Kn as unregisterWarmSpare,An as updateNode,$n as updateNodeTelemetry,et as warmSpareCount,it as withFreshTerminalGuard};
|
|
@@ -214,8 +214,10 @@ export declare function hasPendingCronWake(nodeId: string): Promise<boolean>;
|
|
|
214
214
|
* wakes and leave the deadline armed. Terminal
|
|
215
215
|
* lifecycle transitions also clear the wait. Standing declarative crons are
|
|
216
216
|
* untouched. Kills an in-flight run process group first (the cancelCron
|
|
217
|
-
* discipline), under one canvas write boundary. Returns removed
|
|
218
|
-
|
|
217
|
+
* discipline), under one canvas write boundary. Returns the removed crons. A wake must
|
|
218
|
+
* go through `consumeDeadlineOnWake` (feed/messages.ts), which also tells the
|
|
219
|
+
* node; ending a node calls this directly because nobody is left to tell. */
|
|
220
|
+
export declare function cancelCronsOnWake(anchor_node: string): Promise<Cron[]>;
|
|
219
221
|
/** UPDATE the next fire time — recurrence advancement. The daemon settles a
|
|
220
222
|
* recurring row to its next slot BEFORE enacting the run (crash-safe). */
|
|
221
223
|
export declare function advanceCron(cron_id: string, nextFireIso: string): Promise<void>;
|
|
@@ -11,7 +11,7 @@ var f=Object.defineProperty;var a=(n,r)=>f(n,"name",{value:r,configurable:!0});i
|
|
|
11
11
|
run_state = 'running'
|
|
12
12
|
OR (run_state = 'idle' AND state = 'active' AND (expires_at IS NULL OR expires_at > ?))
|
|
13
13
|
)
|
|
14
|
-
)`).all(n,new Date().toISOString());for(const e of r){if(e.anchor_node===n&&Number(e.cancel_on_wake)!==0)return!0;const t=p(e.sink);if(t?.kind==="node"&&t.node===n)return!0}return!1}a(j,"hasPendingCronWake");async function q(n){return await c(async r=>{const e=(await r.prepare("SELECT * FROM crons WHERE anchor_node = ? AND cancel_on_wake = 1").all(n)).map(s);for(const t of e)t.run_state==="running"&&_(t),await r.prepare("DELETE FROM crons WHERE cron_id = ?").run(t.cron_id);return e
|
|
14
|
+
)`).all(n,new Date().toISOString());for(const e of r){if(e.anchor_node===n&&Number(e.cancel_on_wake)!==0)return!0;const t=p(e.sink);if(t?.kind==="node"&&t.node===n)return!0}return!1}a(j,"hasPendingCronWake");async function q(n){return await c(async r=>{const e=(await r.prepare("SELECT * FROM crons WHERE anchor_node = ? AND cancel_on_wake = 1").all(n)).map(s);for(const t of e)t.run_state==="running"&&_(t),await r.prepare("DELETE FROM crons WHERE cron_id = ?").run(t.cron_id);return e})}a(q,"cancelCronsOnWake");async function z(n,r){await(await i()).prepare("UPDATE crons SET fire_at = ?, updated = ? WHERE cron_id = ?").run(r,new Date().toISOString(),n)}a(z,"advanceCron");async function G(n){return(await(await i()).prepare(`SELECT * FROM crons
|
|
15
15
|
WHERE state = 'active' AND run_state = 'idle' AND fire_at <= ?
|
|
16
16
|
ORDER BY fire_at, created, cron_id`).all(n)).map(s).filter(r=>!h(r.profile))}a(G,"dueClockCrons");async function J(n,r,e,t,o=null){const u=await(await i()).prepare(`UPDATE crons
|
|
17
17
|
SET run_state = 'running', run_started_at = ?, run_pid = NULL, run_pid_identity = NULL,
|
|
@@ -39,6 +39,11 @@ export declare function isolatedNodeHome(nodeId: string, grantee: string): strin
|
|
|
39
39
|
/** The HOME a node's processes get: its own home in an isolated run, else the app's. */
|
|
40
40
|
export declare function nodeHome(nodeId: string, grantee: string, isolated: boolean): string;
|
|
41
41
|
export declare function nodeRunDir(nodeId: string): string;
|
|
42
|
+
/** File the daemon leaves in a node's run folder when its broker was released on purpose (idle-release, park,
|
|
43
|
+
* finish, cancel) and removes when the broker is launched again. A background job's `crtr` call that finds its
|
|
44
|
+
* relay socket gone reads it, without asking the daemon, to tell a release from a restart. */
|
|
45
|
+
export declare const BROKER_RELEASED_MARKER = "broker-released";
|
|
46
|
+
export declare const BROKER_RELAY_GENERATION = "cli-relay-generation";
|
|
42
47
|
/** The run's scope allow-list as a JSON array, beside `broker-launch.json`. */
|
|
43
48
|
export declare function scopesFilePath(nodeId: string): string;
|
|
44
49
|
export declare function contextDir(nodeId: string, grantee?: string): string;
|