@north-light/cli 0.3.531 → 0.3.533
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 +6 -3
- package/dist/api/client.js +1 -1
- package/dist/api/contract.d.ts +1 -1
- package/dist/api/contract.js +1 -1
- package/dist/api/dto/canvas.d.ts +2 -0
- package/dist/api/dto/crons.d.ts +2 -0
- package/dist/builtin-memory/crouter-sdk/release-notes.md +8 -0
- package/dist/builtin-memory/northlight-apps/providers/README.md +2 -0
- package/dist/builtin-memory/northlight-apps/providers/sms.md +2 -0
- package/dist/clients/attach/chrome/inbox-strip.d.ts +6 -1
- package/dist/clients/attach/chrome/inbox-strip.js +1 -1
- package/dist/clients/attach/chrome/roster.js +1 -1
- package/dist/clients/attach/session/chrome-refresh.d.ts +12 -3
- package/dist/clients/attach/session/chrome-refresh.js +1 -1
- package/dist/clients/attach/session/document-links.js +1 -1
- package/dist/clients/attach/viewer.js +586 -587
- package/dist/commands/api-client.js +3 -3
- package/dist/commands/node/inspect.js +6 -6
- package/dist/commands/sys/context/expose.js +2 -2
- package/dist/commands/sys/db.d.ts +1 -0
- package/dist/commands/sys/db.js +1 -0
- package/dist/commands/sys.js +1 -1
- package/dist/core/bash-guard.js +9 -3
- package/dist/core/canvas/canvas.d.ts +12 -2
- package/dist/core/canvas/canvas.js +23 -20
- package/dist/core/canvas/daemon-actions.d.ts +0 -3
- package/dist/core/canvas/daemon-actions.js +7 -24
- package/dist/core/canvas/daemon-owner.d.ts +3 -0
- package/dist/core/canvas/daemon-owner.js +1 -6
- package/dist/core/canvas/db.d.ts +28 -20
- package/dist/core/canvas/db.js +4 -3
- package/dist/core/canvas/deliveries.d.ts +5 -0
- package/dist/core/canvas/deliveries.js +25 -30
- package/dist/core/canvas/export-sqlite.d.ts +14 -0
- package/dist/core/canvas/export-sqlite.js +3 -0
- package/dist/core/canvas/import-sqlite.d.ts +44 -0
- package/dist/core/canvas/import-sqlite.js +7 -0
- package/dist/core/canvas/node-tokens.js +3 -3
- package/dist/core/canvas/outcome-actions.js +4 -4
- package/dist/core/canvas/recovery.js +1 -1
- package/dist/core/canvas/schema-pg.sql +1022 -0
- package/dist/core/canvas/schema-version.d.ts +4 -0
- package/dist/core/canvas/schema-version.js +1 -0
- package/dist/core/canvas/sessions.js +3 -3
- package/dist/core/canvas/structured-requests.js +2 -2
- package/dist/core/canvas/tree.js +6 -6
- package/dist/core/canvas/waits.js +1 -1
- package/dist/core/feed/inbox.js +1 -1
- package/dist/core/feed/messages.js +5 -5
- package/dist/core/feed/reports.js +2 -2
- package/dist/core/grants/mirror.js +2 -2
- package/dist/core/graph/access.js +7 -7
- package/dist/core/graph/custom.js +10 -10
- package/dist/core/graph/deletion.js +6 -6
- package/dist/core/graph/documents.d.ts +1 -1
- package/dist/core/graph/documents.js +9 -9
- package/dist/core/graph/edges.d.ts +3 -3
- package/dist/core/graph/edges.js +3 -1
- package/dist/core/graph/events.js +7 -7
- package/dist/core/graph/exposures.d.ts +8 -8
- package/dist/core/graph/jobs.d.ts +1 -1
- package/dist/core/graph/jobs.js +2 -2
- package/dist/core/graph/kinds/custom.js +1 -1
- package/dist/core/graph/kinds/document.js +4 -4
- package/dist/core/graph/kinds/dto.js +1 -1
- package/dist/core/graph/kinds/job.js +2 -2
- package/dist/core/graph/kinds/kind.d.ts +2 -2
- package/dist/core/graph/kinds/kind.js +1 -1
- package/dist/core/graph/kinds/live-read.d.ts +5 -0
- package/dist/core/graph/kinds/live-read.js +1 -0
- package/dist/core/graph/kinds/node.js +5 -5
- package/dist/core/graph/kinds/owner.js +2 -2
- package/dist/core/graph/names.js +3 -3
- package/dist/core/graph/objects.d.ts +5 -5
- package/dist/core/graph/objects.js +10 -10
- package/dist/core/graph/package-docs.js +6 -5
- package/dist/core/graph/reader.d.ts +1 -1
- package/dist/core/graph/reader.js +2 -2
- package/dist/core/graph/receivers.js +4 -5
- package/dist/core/graph/repo-sync/exchange.js +2 -2
- package/dist/core/graph/repo-sync/repos.js +3 -3
- package/dist/core/graph/repo-sync/sync.js +1 -1
- package/dist/core/graph/rules.d.ts +3 -4
- package/dist/core/graph/rules.js +3 -3
- package/dist/core/graph/spaces/app.js +1 -1
- package/dist/core/graph/spaces/index.d.ts +1 -1
- package/dist/core/graph/spaces/index.js +1 -1
- package/dist/core/graph/spaces/own-memory.js +1 -1
- package/dist/core/graph/spaces/person.js +3 -3
- package/dist/core/graph/spaces/repo.d.ts +1 -1
- package/dist/core/graph/spaces/repo.js +1 -1
- package/dist/core/graph/types.d.ts +4 -3
- package/dist/core/graph/watches.d.ts +2 -2
- package/dist/core/graph/watches.js +17 -17
- package/dist/core/human/page-schema.js +5 -4
- package/dist/core/human/requests.d.ts +1 -1
- package/dist/core/human/requests.js +20 -23
- package/dist/core/llm/run.js +2 -2
- package/dist/core/peer/home.d.ts +6 -0
- package/dist/core/peer/home.js +2 -2
- package/dist/core/peer/ingest.js +1 -1
- package/dist/core/peer/receiver.js +1 -1
- package/dist/core/peer/stubs.js +1 -1
- package/dist/core/providers/receipts.js +3 -2
- package/dist/core/review/comments.js +5 -4
- package/dist/core/runs/activity.js +4 -5
- package/dist/core/runs/confinement.d.ts +1 -1
- package/dist/core/runs/confinement.js +2 -2
- package/dist/core/runs/events.d.ts +1 -1
- package/dist/core/runs/events.js +5 -5
- package/dist/core/runs/operations.js +6 -4
- package/dist/core/runtime/close.d.ts +3 -0
- package/dist/core/runtime/close.js +1 -1
- package/dist/core/runtime/host.d.ts +5 -1
- package/dist/core/runtime/host.js +3 -3
- package/dist/core/runtime/launch.js +2 -2
- package/dist/core/runtime/ledger.js +3 -3
- package/dist/core/runtime/lifecycle.d.ts +9 -0
- package/dist/core/runtime/lifecycle.js +2 -2
- package/dist/core/runtime/nodes.d.ts +1 -1
- package/dist/core/runtime/nodes.js +1 -1
- package/dist/core/runtime/roadmap.js +1 -1
- package/dist/core/spaces/stop.d.ts +1 -0
- package/dist/core/spaces/stop.js +2 -2
- package/dist/core/statements.d.ts +17 -0
- package/dist/core/statements.js +2 -0
- package/dist/core/substrate/delivery/corpus.js +3 -3
- package/dist/core/substrate/delivery/deliver.js +5 -5
- package/dist/core/substrate/delivery/listings.js +1 -1
- package/dist/core/substrate/delivery/match.d.ts +2 -2
- package/dist/core/substrate/delivery/match.js +1 -1
- package/dist/core/substrate/delivery/plan.js +3 -3
- package/dist/core/substrate/delivery/render-boot.js +5 -5
- package/dist/core/substrate/delivery/render-event.js +6 -6
- package/dist/core/usage/ledger.js +8 -8
- package/dist/daemon/api/bridge.js +4 -4
- package/dist/daemon/api/document-listener.js +1 -1
- package/dist/daemon/api/handlers/broker-ops.js +1 -1
- package/dist/daemon/api/handlers/canvas.js +1 -1
- package/dist/daemon/api/handlers/crons.js +1 -1
- package/dist/daemon/api/handlers/custom-objects.js +3 -3
- package/dist/daemon/api/handlers/docs.js +1 -1
- package/dist/daemon/api/handlers/inbox.js +1 -1
- package/dist/daemon/api/handlers/memory.js +7 -7
- package/dist/daemon/api/handlers/messages.js +1 -1
- package/dist/daemon/api/handlers/nodes.js +1 -1
- package/dist/daemon/api/handlers/objects.js +3 -3
- package/dist/daemon/api/handlers/reviews.js +1 -1
- package/dist/daemon/api/handlers/structured-requests.js +1 -1
- package/dist/daemon/api/reader.js +1 -1
- package/dist/daemon/broker-signals/bus.d.ts +14 -6
- package/dist/daemon/broker-signals/bus.js +1 -10
- package/dist/daemon/crtrd-cli.js +1 -1
- package/dist/daemon/crtrd.js +12 -11
- package/dist/daemon/due.js +1 -1
- package/dist/daemon/export-sqlite-run.d.ts +1 -0
- package/dist/daemon/export-sqlite-run.js +4 -0
- package/dist/daemon/fleet.d.ts +1 -0
- package/dist/daemon/fleet.js +3 -3
- package/dist/daemon/human/settle.js +1 -1
- package/dist/daemon/manage.d.ts +10 -0
- package/dist/daemon/manage.js +3 -2
- package/dist/daemon/peer/forward.d.ts +7 -0
- package/dist/daemon/peer/forward.js +1 -1
- package/dist/daemon/postgres/binary.d.ts +17 -0
- package/dist/daemon/postgres/binary.js +2 -0
- package/dist/daemon/postgres/cluster.d.ts +37 -0
- package/dist/daemon/postgres/cluster.js +6 -0
- package/dist/daemon/postgres/index.d.ts +2 -0
- package/dist/daemon/postgres/index.js +1 -0
- package/dist/daemon/postgres/pins.d.ts +7 -0
- package/dist/daemon/postgres/pins.js +1 -0
- package/dist/daemon/postgres/supervisor.d.ts +95 -0
- package/dist/daemon/postgres/supervisor.js +2 -0
- package/dist/daemon/reconcilers/bash-deadline.js +2 -2
- package/dist/daemon/reconcilers/delivery-lane.js +2 -2
- package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +7 -3
- package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +1 -1
- package/dist/daemon/reconcilers/node-lifecycle/tick.js +1 -1
- package/dist/daemon/reconcilers/storage-maintenance.d.ts +0 -6
- package/dist/daemon/reconcilers/storage-maintenance.js +1 -8
- package/dist/daemon/runs/observer.js +5 -4
- package/dist/migrations/004-canvas-documents/index.js +1 -1
- package/dist/pi-extensions/canvas-doc-substrate.d.ts +2 -0
- package/dist/pi-extensions/canvas-doc-substrate.js +6 -6
- package/dist/pi-extensions/envelope-merge.d.ts +3 -2
- package/dist/pi-extensions/envelope-merge.js +9 -8
- package/dist/shared/env.d.ts +3 -0
- package/dist/shared/env.js +2 -2
- package/docs/apps/providers/README.md +2 -0
- package/docs/apps/providers/sms.md +2 -0
- package/docs/public-api.md +11 -2
- package/docs/sdk/release-notes.md +8 -0
- package/package.json +6 -6
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +13 -26
- package/scripts/install-runtime.mjs +53 -3
- package/scripts/lib/isolation-env.mjs +3 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var
|
|
2
|
-
`).filter(c=>c.trim()!=="").slice(-
|
|
1
|
+
var b=Object.defineProperty;var a=(r,t)=>b(r,"name",{value:t,configurable:!0});import{closeSync as _,fstatSync as S,openSync as g,readSync as v}from"node:fs";import{join as y}from"node:path";import{gunzipSync as C}from"node:zlib";import{ApiError as l}from"../api/index.js";import{localClient as d}from"../api/node-transport.js";import{apiErrorToCliError as N}from"../core/io.js";import{autostartDisabled as h,daemonAutostartStartupFailure as M,ensureDaemon as T}from"../daemon/manage.js";import{DAEMON_STARTUP_WINDOW_MS as p}from"../daemon/startup-policy.js";import{readState as A}from"../core/config.js";import{resolveRemoteCanvasTarget as O}from"../core/remote-canvas-target.js";import{RemoteCanvasSource as R}from"../core/canvas/remote-canvas-source.js";import{crtrHome as I}from"../core/canvas/paths.js";import{envTestNodeSocketWindowMs as x,isSandboxedNode as D}from"../shared/env.js";import{ownerOnly as F}from"../core/errors.js";function u(r){return{...r,fault:r.fault??null}}a(u,"withRowFault");function E(r,t){throw r instanceof l?N(r,t):r}a(E,"rethrowAsCliError");async function tt(r){try{return await m().getNode(r)}catch(t){if(t instanceof l&&t.status===404)return null;E(t)}}a(tt,"getNodeOrNull");const L=4096,k=20;function W(){let r;try{r=g(y(I(),"crtrd.log"),"r");const{size:t}=S(r),s=Math.max(0,t-L),n=t-s;if(n<=0)return;const e=Buffer.alloc(n);v(r,e,0,n,s);const o=e.toString("utf8").split(`
|
|
2
|
+
`).filter(c=>c.trim()!=="").slice(-k).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{_(r)}catch{}}}a(W,"readColdStartDiagnostic");function m(r){return process.env.CRTR_API_SOCKET?d({autostart:!1,coldStartPollWindowMs:(h()?x():void 0)??p,...r?.timeoutMs!==void 0?{timeoutMs:r.timeoutMs}:{}}):h()?d({autostart:!1,...r?.timeoutMs!==void 0?{timeoutMs:r.timeoutMs}:{}}):d({...r?.timeoutMs!==void 0?{timeoutMs:r.timeoutMs}:{},onColdSocket:a(async()=>{T()},"onColdSocket"),coldStartDiagnostic:W,coldStartFailure:M,coldStartPollWindowMs:p})}a(m,"cliClient");const P=2e3;class w{static{a(this,"ApiCanvasSource")}client;rosterCache=null;rosterInFlight=null;constructor(t=m()){this.client=t}async roster(){const t=Date.now();return this.rosterCache!==null&&t-this.rosterCache.at<P?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 f=n.subscribers.get(e.to_id);f===void 0?n.subscribers.set(e.to_id,[o]):f.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 l&&s.status===404)return null;throw s}}async getNode(t){const s=await this.orNull(()=>this.client.getNode(t));return s===null?null:u(s)}async getRow(t){const s=await this.orNull(()=>this.client.getNode(t));return s===null?null:u(s)}async listNodes(t){const s=t?.status;let n;if(Array.isArray(s)){const e=new Set(s);n=(await this.client.listNodes()).filter(o=>e.has(o.status))}else s!==void 0?n=await this.client.listNodes({status:s}):n=await this.client.listNodes();return n.map(e=>u(e))}async browseSnapshot(){const t=await this.client.canvasBrowseCompressed(),s=JSON.parse(C(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=>u(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 et(r,t=D()){const s=r?.scope??"user";if(t){if(r?.canvasName!==void 0)throw F("--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 w}const n=r?.canvasName??A(s).activeCanvas??null;if(!n)return new w;const{previewEndpoint:e,relayToken:i}=O(n,s);return new R(e,i)}a(et,"cliCanvasSource");export{w as ApiCanvasSource,L as COLD_START_DIAGNOSTIC_TAIL_BYTES,et as cliCanvasSource,m as cliClient,tt as getNodeOrNull,W as readColdStartDiagnostic,E 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
|
|
2
|
-
`),"leaf")},run:h(async n=>{const e=h(r=>{const g=n[r];return g!==void 0&&g!==""?g.split(",").map(
|
|
3
|
-
`)}},"run"),render:h(n=>{const e=n.node,o=String(e.node_id),t=e.pi_pid,d=S.has(n)?"remote (liveness not checked locally)":O(t)?"alive":"dead",m=e.intent?`/${String(e.intent)}`:"",c=n.fault,l=[];l.push(`# ${
|
|
4
|
-
`));const a=h((s,f)=>{if(f.length===0)return`${s}: none`;const b=["node_id","name","kind","status","active"],r=`| ${b.join(" | ")} |`,g=`| ${b.map(()=>"---").join(" | ")} |`,
|
|
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 $,cliCanvasSource as x,rethrowAsCliError as w}from"../api-client.js";import{RemoteCanvasSource as q}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(r=>{const g=n[r];return g!==void 0&&g!==""?g.split(",").map(y=>y.trim()).filter(y=>y!==""):void 0},"csv"),o=e("status"),t=e("kind"),u=e("mode"),d=e("lifecycle"),m=n.profile,c=n.name,l=n.cwd,i=n.under,p=x({canvasName:n.canvas}),a=p instanceof q?m:m===void 0?void 0:(await z(m)).id;let s;if(i!==void 0&&i!==""){if(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."});s=new Set([i,...await p.view(i)])}return{nodes:(await p.listNodes(o!==void 0?{status:o}:void 0).catch(w)).filter(r=>!(s!==void 0&&!s.has(r.node_id)||t!==void 0&&!t.includes(r.kind)||u!==void 0&&!u.includes(r.mode)||d!==void 0&&!d.includes(r.lifecycle)||a!==void 0&&r.profile_id!==a||l!==void 0&&l!==""&&r.cwd!==l)).map(r=>({...r,name:_(r)})).filter(r=>c===void 0||c===""||F(c,r.name))}},"run")}),S=new WeakSet;async function j(n,e){const o=await n.getNode(e.node_id).catch(w);return{node_id:e.node_id,name:o?_(o):"(missing)",kind:o?.kind??"?",status:o?o.intent?`${o.status}/${o.intent}`:o.status:"?",active:e.active}}h(j,"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,o=n.canvas,t=x({canvasName:o});if(t instanceof q){const s=await t.rawShow(e);if(s===null)throw new v({error:"not_found",message:`no node: ${e}`,next:"List nodes with `crtr node inspect list --canvas <name>`."});const f={...s};return S.add(f),f}const u=await t.getNode(e).catch(s=>w(s,"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=$(),c=await Promise.all((await t.subscriptionsOf(e)).map(s=>j(t,s))),l=await Promise.all((await t.subscribersOf(e)).map(s=>j(t,s))),i=(await m.listCrons({created_by:e}).catch(w)).filter(s=>s.created_by===e).length,p={context_dir:d.paths.context_dir,...d.pi_session_file!==void 0?{session_file:d.pi_session_file}:{}},a=[];return a.push(`List the documents it owns (reports, plans, notes): \`crtr canvas list --owner ${e}\`.`),a.push(`Browse its context dir (and its subscriptions') in nvim: \`crtr node inspect context ${e}\`.`),p.session_file&&a.push(`Read the raw conversation transcript: ${p.session_file}`),a.push(`On-disk context dir (ls/read directly): ${p.context_dir}`),i>0&&a.push(`It created ${i} pending cron(s): \`crtr cron list\`.`),c.length>0&&a.push(`Drill into a child/report: \`crtr node inspect show <node_id>\` (${c.map(s=>s.node_id).join(", ")}).`),a.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:a.join(`
|
|
3
|
+
`)}},"run"),render:h(n=>{const e=n.node,o=String(e.node_id),t=e.pi_pid,d=S.has(n)?"remote (liveness not checked locally)":O(t)?"alive":"dead",m=e.intent?`/${String(e.intent)}`:"",c=n.fault,l=[];l.push(`# ${_(e)} (${o})`);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 a=h((s,f)=>{if(f.length===0)return`${s}: none`;const b=["node_id","name","kind","status","active"],r=`| ${b.join(" | ")} |`,g=`| ${b.map(()=>"---").join(" | ")} |`,y=f.map(T=>`| ${b.map(N=>String(T[N]??"").replace(/\|/g,"\\|").replace(/\n/g," ")).join(" | ")} |`).join(`
|
|
5
5
|
`);return`${f.length} ${s}:
|
|
6
6
|
|
|
7
7
|
${r}
|
|
8
8
|
${g}
|
|
9
|
-
${
|
|
9
|
+
${y}`},"table");return l.push(a("reports (down \u2014 subscriptions, usually children)",n.reports)),l.push(a("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,o=n.node;if(o!==void 0&&e!==void 0)throw new
|
|
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,o=n.node;if(o!==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=o;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 $().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};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var d=Object.defineProperty;var
|
|
2
|
-
`)}},"run"),render:
|
|
1
|
+
var d=Object.defineProperty;var i=(n,t)=>d(n,"name",{value:t,configurable:!0});import{accessSync as l,constants as c,realpathSync as u,statSync as f}from"node:fs";import{resolve as h}from"node:path";import{defineLeaf as p}from"../../../core/command.js";import{expandTilde as m}from"../../../core/fs-utils.js";import{cliClient as y}from"../../api-client.js";import{envExecutionId as v,envNodeId as g}from"../../../shared/env.js";function w(n){const t=[],o=new Set;for(const s of n)try{const e=u(h(m(s)));if(l(e,c.R_OK),!f(e).isFile()||o.has(e))continue;o.add(e),t.push(e)}catch{}return t}i(w,"readableRegularFiles");const _=p({name:"expose",description:"deliver file-read documents for file content a local command displays",whenToUse:"a local command has successfully rendered file contents and must place the documents their file-read rules deliver in that same output",help:{name:"sys context expose",summary:"deliver file-read documents for files displayed by a command",params:[{kind:"flag",name:"file",type:"path",required:!0,repeatable:!0,constraint:"Files whose contents the caller displays. Relative paths resolve against this process\u2019s cwd; leading ~ expands as for Read. Symlinks resolve to their target. Missing, unreadable, and non-file paths contribute nothing."}],output:[{name:"content",type:"string",required:!1,constraint:"The delivered <auto-loaded-context> blocks, one per displayed file with new matches. Absent when nothing new matches or the caller is not inside a node. Prose output is the blocks alone, without a field label or acknowledgement."}],outputKind:"object",effects:["Delivers through the current node\u2019s file-read activity: records its exposures, and a watch and read event for each document delivered at content. Inherits node identity from CRTR_NODE_ID. The caller must include the returned blocks in the same output as the displayed files; this is not a preview operation."]},run:i(async n=>{const t=g()?.trim();if(t===void 0||t==="")return{};const o=w(n.file);if(o.length===0)return{};const s=v();if(s===void 0||s==="")return{};const e=[];for(const r of o){const a=await y().nodeDelivery(t,{activity:"file-read",subject:r,expected_execution_id:s});a.content!==""&&e.push(a.content)}return e.length===0?{}:{content:e.join(`
|
|
2
|
+
`)}},"run"),render:i(n=>n.content??"","render")});export{_ as exposeLeaf};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const sysDbBranch: import("../../core/command.js").BranchDef;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var n=Object.defineProperty;var a=(e,t)=>n(e,"name",{value:t,configurable:!0});import{existsSync as o}from"node:fs";import{defineBranch as i,defineLeaf as d}from"../../core/command.js";import{general as s}from"../../core/errors.js";import{canvasDbPath as m}from"../../core/canvas/paths.js";import{isDaemonRunning as c}from"../../daemon/pidfile.js";import{runDaemonExportSqlite as p}from"../../daemon/manage.js";const h=d({name:"export-sqlite",description:"write the Postgres canvas database back into a SQLite canvas.db, for rolling back to a generation that predates Postgres",whenToUse:"rolling back to a runtime generation that still uses SQLite, after `crtr sys daemon stop`. Not part of normal operation; the Postgres generation never needs it.",help:{name:"sys db export-sqlite",summary:"copy the Postgres canvas database into a new runtime/canvas.db at the SQLite schema version the previous generation expects",guide:"Rollback sequence: `crtr sys daemon stop` (drains the daemon, releases its claim, stops the supervised Postgres), `crtr sys db export-sqlite` (this command starts the supervised Postgres by itself, copies every table with object ids mapped back to rowids, verifies row counts, and renames the result into place as runtime/canvas.db), select the previous runtime generation, `crtr sys daemon start`. If the export fails before publishing, canvas.db is not created and the Postgres data is untouched (only the export mark may be left, which keeps a later Postgres boot from importing a restored canvas.db over newer data): rename `runtime/canvas.db.imported` back to `runtime/canvas.db` to start the previous generation from the database as it was before the first Postgres boot, which loses every write made since. This command does not do that rename for you.",params:[],output:[{name:"path",type:"string",required:!0,constraint:"The canvas.db that was written."},{name:"rows",type:"number",required:!0,constraint:"Rows copied across all tables."},{name:"user_version",type:"number",required:!0,constraint:"The SQLite schema version of the written file."},{name:"ms",type:"number",required:!0,constraint:"Wall time of the copy."}],outputKind:"object",effects:["Starts the supervised Postgres if it is not running (or uses CRTR_DATABASE_URL), reads it in one read-only transaction, and writes runtime/canvas.db.exporting-<pid>, then renames it to runtime/canvas.db. Stops the Postgres again afterwards if this command started it.","Records an exported_to_sqlite mark in the Postgres canvas_meta before publishing runtime/canvas.db, so a later boot of the Postgres generation refuses to import this canvas.db over newer Postgres data.","Refuses while a daemon is running, and when runtime/canvas.db already exists."]},run:a(async()=>{if(c())throw s("a daemon is running; an export taken under it would miss writes made after the copy",{next:"Run `crtr sys daemon stop`, then `crtr sys db export-sqlite`."});const e=m();if(o(e))throw s(`${e} already exists`,{next:`Move it aside (or delete it if it is a leftover) before exporting; ${e}.imported is the pre-Postgres database kept by the import.`});try{const t=await p();return{path:t.path,rows:t.rows,user_version:t.userVersion,ms:t.ms}}catch(t){const r=`export failed: ${t.message}`;throw o(e)?s(`${r}; ${e} was published before the failure`,{next:`${e} may be complete: inspect it before using it, or move it aside and rerun the export.`}):s(r,{next:`Nothing was written to ${e}. To start the previous generation from the pre-Postgres database instead, rename ${e}.imported to ${e}; every write made since the Postgres import is lost.`})}},"run")}),y=i({name:"db",description:"offline operations on the canvas database",whenToUse:"rolling the canvas database back to SQLite after a Postgres soak fails",help:{name:"sys db",summary:"offline canvas database operations",model:"The canvas lives in a supervised Postgres. These commands run with the daemon stopped and are for migration and rollback only."},children:[h]});export{y as sysDbBranch};
|
package/dist/commands/sys.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var s=Object.defineProperty;var r=(o,m)=>s(o,"name",{value:m,configurable:!0});import{defineSysBranch as t}from"./sys/branch.js";import{configBranch as f}from"./sys/config.js";import{sysEnvBranch as e}from"./sys/env.js";import{daemonBranch as i}from"./sys/daemon.js";import{sysDoctorLeaf as a}from"./sys/doctor.js";import{sysFeedbackLeaf as p}from"./sys/feedback.js";import{sysLogsLeaf as n}from"./sys/logs.js";import{sysRetryDeliveryLeaf as y}from"./sys/retry-delivery.js";import{sysWorktreesLeaf as c}from"./sys/worktrees.js";import{sysSpacesLeaf as L}from"./sys/spaces.js";import{sysLimitsBranch as h}from"./sys/limits.js";import{sysSettingsLeaf as B}from"./sys/settings.js";import{sysConnectLeaf as S}from"./sys/connect.js";import{sysSetupLeaf as g}from"./sys/setup.js";import{tutorialBranch as u}from"./sys/tutorial/branch.js";import{supportBranch as d}from"./sys/support.js";import{contextBranch as
|
|
1
|
+
var s=Object.defineProperty;var r=(o,m)=>s(o,"name",{value:m,configurable:!0});import{defineSysBranch as t}from"./sys/branch.js";import{configBranch as f}from"./sys/config.js";import{sysEnvBranch as e}from"./sys/env.js";import{daemonBranch as i}from"./sys/daemon.js";import{sysDoctorLeaf as a}from"./sys/doctor.js";import{sysFeedbackLeaf as p}from"./sys/feedback.js";import{sysLogsLeaf as n}from"./sys/logs.js";import{sysRetryDeliveryLeaf as y}from"./sys/retry-delivery.js";import{sysWorktreesLeaf as c}from"./sys/worktrees.js";import{sysSpacesLeaf as L}from"./sys/spaces.js";import{sysLimitsBranch as h}from"./sys/limits.js";import{sysSettingsLeaf as B}from"./sys/settings.js";import{sysConnectLeaf as S}from"./sys/connect.js";import{sysSetupLeaf as g}from"./sys/setup.js";import{tutorialBranch as u}from"./sys/tutorial/branch.js";import{supportBranch as d}from"./sys/support.js";import{contextBranch as D}from"./sys/context.js";import{sysSyncBranch as b}from"./sys/sync.js";import{sysMigrateLeaf as k}from"./sys/migrate.js";import{sysJoinLeaf as l}from"./sys/join.js";import{sysDbBranch as v}from"./sys/db.js";import{sysUpdateLeaf as x,sysVersionLeaf as C}from"./sys/update.js";function Y(){return t([B,f,e,S,g,u,a,D,p,n,c,y,L,h,d,x,C,k,l,b,v,i])}r(Y,"registerSys");export{Y as registerSys};
|
package/dist/core/bash-guard.js
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
var
|
|
1
|
+
var M=Object.defineProperty;var a=(t,n)=>M(t,"name",{value:n,configurable:!0});import{homedir as z,userInfo as N}from"node:os";import{isAbsolute as T,resolve as A,sep as $}from"node:path";import{commandSegments as q,stripHeredocs as H}from"./shell-segments.js";const v=10,P={s:1,m:60,h:3600,d:86400};function V(t){const n=/^\s*sleep(\s+[^;&|\n]+)/.exec(t);if(!n)return null;let e=0;for(const r of n[1].trim().split(/\s+/)){const s=/^([0-9]*\.?[0-9]+)([smhd]?)$/.exec(r);if(!s)return null;e+=Number(s[1])*P[s[2]||"s"]}return e}a(V,"leadingSleepSeconds");function Z(t){const n=V(t);return n===null||n<=v?null:`Refused: this command starts by sleeping ${n}s, over the ${v}s inline limit.
|
|
2
2
|
Holding a live window on a clock is never how you wait here. Instead:
|
|
3
3
|
- schedule the wake and end your turn: crtr cron -h (one-shot at a time, or recurring)
|
|
4
4
|
- race an inbox message against a deadline: crtr node wait deadline -h
|
|
5
5
|
- waiting on a command: crtr node bash run '<command>' starts it as a job and wakes you with its exit code
|
|
6
6
|
- waiting on a child, a reply from the user, or any spine event: just stop \u2014 the runtime wakes you.
|
|
7
|
-
If you truly need a short pause inline, sleep ${
|
|
7
|
+
If you truly need a short pause inline, sleep ${v}s or less.`}a(Z,"longSleepRefusal");const B=["/","/Applications","/bin","/dev","/etc","/home","/Library","/opt","/private","/private/etc","/private/tmp","/private/var","/sbin","/System","/tmp","/usr","/Users","/var","/Volumes"];function R(t,n){return t==="/"||t==="/Users"||t==="/home"||t==="/System/Volumes/Data"?"a filesystem root":t===n?"your home directory":t===`${n}/.crouter`?"the canvas home (a multi-gigabyte canvas.db, every node directory and worktree)":null}a(R,"broadSearchReason");function U(t,n,e){return B.includes(t)?"a system directory":t===n?"your home directory":t===`${n}/.crouter`?"the canvas home \u2014 every node\u2019s conversation and state":e===t||!e.startsWith(t.endsWith($)?t:t+$)?null:`a directory containing your working directory (${e})`}a(U,"protectedReason");function G(t,n){const e=N().username;return new Map([["HOME",t],["PWD",n],["USER",e],["LOGNAME",e]])}a(G,"initialVariables");function _(t,n){let e=t;if(e==="~"||e.startsWith("~/")){const r=n.get("HOME");if(r===void 0)return null;e=r+e.slice(1)}else if(e.startsWith("~"))return null;return e=e.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g,(r,s,o)=>n.get(s??o)??r),/[$`*?[]/.test(e)?null:e}a(_,"expandText");function y(t,n,e){const r=_(t.replace(/\/+\*?\.?$/,"")||"/",n);return r===null?null:T(r)?A(r):e===null?null:A(e,r)}a(y,"resolveTarget");function p(t,n){for(const e of n){const r=e.indexOf("=");if(r<=0)continue;const s=e.slice(0,r);if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(s))continue;const o=_(e.slice(r+1),t);o===null?t.delete(s):t.set(s,o)}}a(p,"applyAssignments");const C=/^[0-9]*(&?>>?|<<?|>&|<&)/;function g(t){const n=[];let e=!1,r=!1;for(const s of t.slice(1)){if(r){r=!1;continue}const o=C.exec(s.text);if(o!==null&&!s.opaque){r=s.text===o[0];continue}if(!e&&s.text==="--"){e=!0;continue}!e&&s.text.startsWith("-")&&s.text!=="-"||n.push(s.text)}return n}a(g,"operands");function j(t){const n=new Set,e=new Set;for(const r of t.slice(1)){if(r.text==="--")break;if(r.text.startsWith("--"))e.add(r.text.slice(2));else if(r.text.startsWith("-")&&r.text.length>1)for(const s of r.text.slice(1))n.add(s)}return{letters:n,long:e}}a(j,"flagLetters");function b(t){const{letters:n,long:e}=j(t);return n.has("r")||n.has("R")||e.has("recursive")}a(b,"isRecursive");const F=/^\/dev\/r?(disk|sd[a-z]|nvme|hd[a-z]|vd[a-z]|mmcblk|md)/;function I(t){const n=[];let e=1;for(;e<t.length;){const r=t[e].text;if(r==="--"||/^-[HLPEdsx]$/.test(r))e+=1;else if(r==="-D"||r==="-O")e+=2;else if(r==="-f")e+1<t.length&&n.push(t[e+1].text),e+=2;else break}for(;e<t.length&&!t[e].text.startsWith("-");e+=1)n.push(t[e].text);return n}a(I,"findPaths");const X=new Set(["sudo","doas","command","env","nice","nohup","time","stdbuf","xargs","exec","builtin"]);function Y(t){let n=t;for(;n.length>0&&!n[0].opaque&&X.has(n[0].text);){let e=1;for(;e<n.length&&n[e].text.startsWith("-");){const r=/^-[nu]$/.test(n[e].text);e+=r?2:1}for(;e<n.length&&/^[A-Za-z_][A-Za-z0-9_]*=/.test(n[e].text);)e+=1;if(e>=n.length)return[];n=n.slice(e)}return n}a(Y,"stripCommandPrefixes");function K(t){const n={targets:[],named:null},e=t[0];if(e===void 0||e.opaque)return n;const r=e.text.slice(e.text.lastIndexOf("/")+1),s=g(t);switch(r){case"rm":return b(t)?{targets:s,named:null}:n;case"mv":return s.length>=2?{targets:s.slice(0,-1),named:null}:n;case"chmod":case"chown":case"chgrp":return b(t)&&s.length>=2?{targets:s.slice(1),named:null}:n;case"find":return t.some(i=>!i.opaque&&(i.text==="-delete"||i.text==="-exec"||i.text==="-execdir"))?{targets:I(t),named:null}:n;case"dd":{const o=t.find(l=>l.text.startsWith("of="));if(o===void 0)return n;const i=o.text.slice(3);return F.test(i)?{targets:[],named:i}:i.startsWith("/dev/")?n:{targets:[i],named:null}}default:if(/^(mkfs|newfs)([.-].+)?$/.test(r))return{targets:[],named:r};if(r==="diskutil"){const o=s[0];if(o!==void 0&&/^(erase|reformat|partition|zeroDisk|randomDisk|secureErase)/i.test(o))return{targets:[],named:`diskutil ${o}`}}return n}}a(K,"destructiveTargets");const m={grep:new Set(["-e","-f","-A","-B","-C","-m","-d","-D","--regexp","--file","--max-count","--context","--after-context","--before-context","--include","--exclude","--exclude-dir","--directories","--devices","--label"]),rg:new Set(["-e","-f","-A","-B","-C","-m","-g","-t","-T","-j","-M","-E","-r","--regexp","--file","--max-count","--context","--after-context","--before-context","--glob","--iglob","--type","--type-not","--threads","--max-columns","--encoding","--replace","--max-depth","--max-filesize","--sort","--sortr","--type-add","--pre","--pre-glob","--ignore-file","--color","--colors","--engine"]),ag:new Set(["-A","-B","-C","-m","-G","-g","--depth","--ignore","--file-search-regex","--max-count","--context","--after","--before"])};function O(t,n){const e=[];let r=!1,s=!1;for(let o=1;o<t.length;o+=1){const i=t[o].text,l=C.exec(i);if(l!==null&&!t[o].opaque){i===l[0]&&(o+=1);continue}if(!s&&i==="--"){s=!0;continue}if(!s&&i.startsWith("-")&&i!=="-"){const c=i.includes("=")?i.slice(0,i.indexOf("=")):i;(c==="-e"||c==="-f"||c==="--regexp"||c==="--file"||/^-[^-]*[ef]/.test(i))&&(r=!0),!i.includes("=")&&n.has(i)&&(o+=1);continue}e.push(i)}return{words:e,patternGiven:r}}a(O,"searchOperands");function J(t){const n={roots:[],implicitCwd:!1},e=t[0];if(e===void 0||e.opaque)return n;const r=e.text.slice(e.text.lastIndexOf("/")+1),s=a(o=>{const{words:i,patternGiven:l}=O(t,o),c=l?i:i.slice(1);return{roots:c,implicitCwd:c.length===0}},"patternThenPaths");switch(r){case"grep":case"egrep":case"fgrep":return b(t)?s(m.grep):n;case"rgrep":return s(m.grep);case"rg":if(t.some(o=>o.text==="--files")){const{words:o}=O(t,m.rg);return{roots:o,implicitCwd:o.length===0}}return s(m.rg);case"ag":case"ack":return s(m.ag);case"find":{if(t.some(i=>i.text==="-maxdepth"))return n;const o=I(t);return{roots:o,implicitCwd:o.length===0}}case"du":{const o=g(t);return{roots:o,implicitCwd:o.length===0}}default:return n}}a(J,"searchRoots");function L(t,n,e){return`Refused: \`${t}\` searches ${n} recursively, which is ${e}.
|
|
8
|
+
A sweep that wide reads hundreds of gigabytes, starves crtrd of disk, and has crashed every open viewer on this machine before.
|
|
9
|
+
Search where the answer can actually be:
|
|
10
|
+
- a document (knowledge, preference, spec, report): crtr canvas search "<text>" or crtr canvas read <name> \u2014 documents live in canvas.db, never as files, so grep cannot find them.
|
|
11
|
+
- code: the one repo or directory you are working in, e.g. rg -l '<text>' <repo>.
|
|
12
|
+
- a node's files: its own context dir, e.g. ~/.crouter/apps/<app>/nodes/<id>/context.
|
|
13
|
+
If you believe a whole-tree search is genuinely required, ask the user \u2014 do not work around this refusal.`}a(L,"broadSearchRefusalMessage");function Q(t,n,e){const r=G(e,n);let s=[n];for(const o of q(H(t))){const i=Y(o.tokens),l=i[0];if(l!==void 0){const c=new Map(r);p(c,o.assignments);const f=s[s.length-1];f!==void 0&&c.set("PWD",f);const{targets:W,named:S}=K(i);if(S!==null)return k(o.text,S,"a whole disk or volume \u2014 never a task boundary");for(const d of W)for(const w of s.length===0?[null]:s){const u=y(d,c,w);if(u===null)continue;const h=U(u,e,n);if(h!==null)return k(o.text,u,h)}const E=J(i),D=s.length===0?[null]:s,x=s[s.length-1];if(E.implicitCwd&&x!==void 0){const d=R(x,e);if(d!==null)return L(o.text,x,d)}for(const d of E.roots)for(const w of D){const u=y(d,c,w);if(u===null)continue;const h=R(u,e);if(h!==null)return L(o.text,u,h)}}if(!o.background){if(l===void 0)p(r,o.assignments);else if(!l.opaque&&ee.has(l.text))p(r,o.assignments),p(r,g(i));else if(!l.opaque&&l.text==="cd"){const c=g(i)[0],f=c===void 0?r.get("HOME")??null:y(c,r,s[s.length-1]??null);s=f===null?[]:[...s,f]}}}return null}a(Q,"destructiveCommandRefusal");const ee=new Set(["export","declare","typeset","readonly"]);function k(t,n,e){return`Refused: \`${t}\` targets ${n}, which is ${e}.
|
|
8
14
|
Nothing an agent is asked to do requires destroying that path, and it cannot be undone.
|
|
9
15
|
If you meant somewhere else, name that path explicitly and run the command again. Two traps to check first:
|
|
10
16
|
- a \`VAR=value cmd\` prefix applies ONLY to that one command, so a later \`$VAR\` in the same line is the SHELL's value \u2014 this is exactly how a node deleted a home directory.
|
|
11
17
|
- \`~\` and \`$HOME\` are the real home directory, never a scratch dir; build scratch paths with mktemp -d and hold them in a variable of your own.
|
|
12
|
-
If you believe this path genuinely must be destroyed, ask the user \u2014 do not work around this refusal.`}a(
|
|
18
|
+
If you believe this path genuinely must be destroyed, ask the user \u2014 do not work around this refusal.`}a(k,"refusalMessage");function oe(t,n,e=z()){return Z(t)??Q(t,n,e)}a(oe,"bashCommandRefusal");export{oe as bashCommandRefusal,V as leadingSleepSeconds};
|
|
@@ -158,12 +158,21 @@ export declare function listNodes(filter?: {
|
|
|
158
158
|
* preserve lastActivityMs's timestamp inputs and the teardown event fields;
|
|
159
159
|
* unlike node_rows, the nodes table needs no execution/session/fault joins. */
|
|
160
160
|
export type TerminalPlacementResidue = Pick<NodeRow, 'node_id' | 'created' | 'launched_at' | 'last_settled_at' | 'finalized_at' | 'outcome_at' | 'last_activity_at' | 'pane' | 'window' | 'tmux_session'>;
|
|
161
|
-
export declare const terminalPlacementResidueSql = "SELECT\n node_id, created, launched_at, last_settled_at, finalized_at,\n outcome_at, last_activity_at, pane, \"window\", tmux_session\n FROM nodes
|
|
161
|
+
export declare const terminalPlacementResidueSql = "SELECT\n node_id, created, launched_at, last_settled_at, finalized_at,\n outcome_at, last_activity_at, pane, \"window\", tmux_session\n FROM nodes\n WHERE node_id NOT IN (SELECT node_id FROM warm_pool)\n AND status IN ('done', 'canceled', 'dead')\n AND (pane IS NOT NULL OR \"window\" IS NOT NULL OR tmux_session IS NOT NULL)\n ORDER BY created";
|
|
162
162
|
export declare function listTerminalPlacementResidue(): Promise<TerminalPlacementResidue[]>;
|
|
163
163
|
/** The daemon's complete supervision set. Live rows participate regardless of
|
|
164
164
|
* PID; terminal rows participate only while they retain a PID or freeze mark. */
|
|
165
|
-
export declare const nodesForSupervisionSql = "WITH candidates AS MATERIALIZED (\n SELECT node_id FROM nodes WHERE status IN ('active', 'idle')\n UNION\n SELECT node_id FROM nodes
|
|
165
|
+
export declare const nodesForSupervisionSql = "WITH candidates AS MATERIALIZED (\n SELECT node_id FROM nodes WHERE status IN ('active', 'idle')\n UNION\n SELECT node_id FROM nodes\n WHERE frozen_at IS NOT NULL AND status IN ('done', 'canceled', 'dead')\n UNION\n SELECT node_id FROM executions\n WHERE state != 'exited' AND state = 'running' AND pid IS NOT NULL\n )\n SELECT v.* FROM candidates c CROSS JOIN node_rows v\n WHERE v.node_id = c.node_id\n AND v.node_id NOT IN (SELECT node_id FROM warm_pool)\n AND v.kind != 'human'\n 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)))\n ORDER BY v.created";
|
|
166
166
|
export declare function listNodesForSupervision(): Promise<NodeRow[]>;
|
|
167
|
+
/** A parked resident the runtime may reopen when wake mail reaches it: `done` +
|
|
168
|
+
* `intent='parked'`, resident, no final latch, and not being deleted. Finalized,
|
|
169
|
+
* canceled, dead, terminal-lifecycle and deleting nodes are never reopened by mail.
|
|
170
|
+
* The SQL form of `isParkedResident` (runtime/lifecycle.ts), which must change with it; `n` is a `nodes` row. */
|
|
171
|
+
export declare const PARKED_RESIDENT_SQL: (n: string) => string;
|
|
172
|
+
/** Parked residents with unseen wake mail (`UNSEEN_WAKE_MAIL_SQL`), which nothing else
|
|
173
|
+
* launches: the lifecycle tick reopens them. Each parked resident seeks its own mail
|
|
174
|
+
* through `deliveries_destination`. */
|
|
175
|
+
export declare function listParkedResidentsWithWakeMail(): Promise<NodeRow[]>;
|
|
167
176
|
/** Identity-only projection of every user-visible row: the broker's report
|
|
168
177
|
* resolver needs only "does this sender exist, and what is it called", and a
|
|
169
178
|
* three-column read stays cheap against a canvas of thousands of rows where a
|
|
@@ -241,6 +250,7 @@ export interface RosterRow {
|
|
|
241
250
|
host_kind: 'tmux' | 'broker' | null;
|
|
242
251
|
parent: string | null;
|
|
243
252
|
created: string;
|
|
253
|
+
description: string | null;
|
|
244
254
|
}
|
|
245
255
|
/** Every node's lean roster row, oldest first — a single scan indexed by
|
|
246
256
|
* `idx_nodes_created`, with no per-row hydration or N+1 query. */
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
var Z=Object.defineProperty;var i=(e,n)=>Z(e,"name",{value:n,configurable:!0});import{envNodeId as
|
|
1
|
+
var Z=Object.defineProperty;var i=(e,n)=>Z(e,"name",{value:n,configurable:!0});import{envNodeId as U}from"../../shared/env.js";import{rmdirSync as ee,rmSync as ne,statSync as te}from"node:fs";import{dirname as ae,join as j,resolve as ie}from"node:path";import{removeTreeBeneath as W}from"../spaces/open-beneath.js";import{emitEvent as oe}from"../events/emit.js";import{withCanvasWrite as p,openDb as s}from"./db.js";import{conversationStore as re,ConversationNotFoundError as se}from"../conversation-store/index.js";import{hasUnseenWakeMail as de,UNSEEN_WAKE_MAIL_SQL as le}from"./deliveries.js";import{isAppRun as b}from"./app-run.js";import{registerOutcomeAction as I}from"./outcome-actions.js";import{publish as ce}from"../graph/events.js";import{nodeObject as ue}from"../graph/objects.js";import{findNodeBySessionFileSql as _e}from"./sessions.js";import{hydrateFault as pe}from"./recovery.js";import{hasUnreconciledWorktree as fe,openWorktree as Ee,readWorktree as we,worktreeFromRow as ye}from"./worktrees.js";import{isPidAlive as me,isPidPresent as F,recordedPidLiveness as Ne}from"./pid.js";import{ensureHome as Re,jobDir as Se,nodeDir as Oe,nodeRunDir as H}from"./paths.js";import{appForNode as he,ensureNodeDirs as Te,nodeAgentDir as Le}from"./node-agent-paths.js";import{appSpace as C}from"../layout.js";import{createObject as De,getById as M,ownerForGrantee as O,setObjectKind as ge,setObjectName as Ae}from"../graph/objects.js";import{deleteNodeWithOwned as ve}from"../graph/deletion.js";import{filterAsync as y,mapAsync as h}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 sn(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(sn,"assertRunNotDeleting");async function ke(e,n){const t=await O(n,e.grantee??"app:terminal");await De(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(ke,"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 pe(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 we(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 dn(e,n={}){return Re(),await p(async t=>{await ke(e,t),e.managed_worktree!==void 0&&e.managed_worktree!==null&&await Ee(e.node_id,e.managed_worktree,t),n.outcomeDelivery!==void 0&&await I(t,e.node_id,n.outcomeDelivery),await n.withinCreate?.(t,e)}),await Te(e.node_id),await N(e.node_id)}i(dn,"createNode");async function N(e){const n=await(await s()).prepare("SELECT * FROM node_rows WHERE node_id = ?").get(e);return n===void 0?null:await $(n)}i(N,"getNode");const xe=1440*60*1e3;function q(e){return{requestKey:e.request_key,nodeId:e.node_id,state:e.state}}i(q,"createRequestRecord");async function ln(e){const n=await(await s()).prepareUncached("SELECT request_key, node_id, state FROM create_requests WHERE request_key = ?").get(e);return n===void 0?null:q(n)}i(ln,"findCreateRequest");async function cn(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+xe),{requestKey:e,nodeId:null,state:"reserved"})})}i(cn,"reserveCreateRequest");async function un(e=Date.now()){return Number((await(await s()).prepareUncached("DELETE FROM create_requests WHERE expires_at <= ?").run(e)).changes)}i(un,"pruneExpiredCreateRequests");async function _n(e,n){await(await s()).prepareUncached("UPDATE create_requests SET node_id = ?, state = 'complete' WHERE request_key = ?").run(n,e)}i(_n,"recordCreateRequestNode");async function pn(e,n){await(await s()).prepareUncached("UPDATE create_requests SET node_id = ?, state = 'reserved' WHERE request_key = ?").run(n,e)}i(pn,"reserveCreateRequestNode");async function fn(e,n){await(await s()).prepareUncached("UPDATE create_requests SET state = 'complete' WHERE request_key = ? AND node_id = ?").run(e,n)}i(fn,"completeCreateRequest");async function En(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(En,"abandonCreateRequest");function wn(e){return ie(e)}i(wn,"canonicalSessionFile");async function yn(e){const n=await _e(e);return n===null?null:await N(n)}i(yn,"findNodeBySessionFile");async function mn(e){const n=await(await s()).prepare("SELECT * FROM node_rows WHERE node_id = ?").get(e);return n?f(n):null}i(mn,"getRow");async function Nn(e){const n=await(await s()).prepare("SELECT * FROM node_rows WHERE pane = ?").get(e);return n?f(n):null}i(Nn,"getRowByPane");async function Rn(e){return(await(await s()).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 r=0;for(const d of a){const
|
|
6
|
-
WHERE o.type = 'node' AND i.scheme = 'local-user' AND i.external_id = 'user:local'`,r=await e.prepareUncached(`${a} LIMIT 1`).get()!==void 0?Number((await e.prepareUncached(`UPDATE objects SET owner = ? WHERE
|
|
7
|
-
WHERE o.type = 'node' AND o.reception = 2 AND
|
|
8
|
-
AND i.scheme = 'app' AND i.external_id = 'app:terminal'`,
|
|
4
|
+
ORDER BY deadline_at, created, node_id`).all(e)).map(f)}i(Rn,"dueNodeDeadlines");async function Sn(e,n){return Number((await(await s()).prepare("UPDATE nodes SET deadline_at = ? WHERE node_id = ?").run(n,e)).changes)===1}i(Sn,"setNodeDeadline");async function On(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 r=0;for(const d of a){const c=Date.parse(d.deadline_at);Number.isNaN(c)||(r+=Number((await o.run(new Date(c+n).toISOString(),d.node_id,d.deadline_at)).changes))}return r})}i(On,"shiftProfileNodeDeadlines");async function hn(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'`,r=await e.prepareUncached(`${a} LIMIT 1`).get()!==void 0?Number((await e.prepareUncached(`UPDATE objects SET owner = ? WHERE id IN (${a})`).run((await O(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
|
+
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'`,c=await e.prepareUncached(`${d} LIMIT 1`).get()!==void 0?Number((await e.prepareUncached(`UPDATE objects SET owner = ? WHERE id IN (${d})`).run((await O(e,"user:local")).id)).changes):0;return Number((await e.prepare("UPDATE nodes SET sub = ? WHERE sub = 'user:local'").run(n)).changes)+r+c+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(hn,"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 Tn(e,n){const t=[],a=i((r,d=r)=>{Object.prototype.hasOwnProperty.call(n,r)&&t.push([d,T(n[r],r)])},"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(r=>Object.prototype.hasOwnProperty.call(n,r),"has");return await p(async r=>{const d=await M(r,e);if(d===null||d.type!=="node")throw new Error(`unknown node: ${e}`);if(o("name")&&await Ae(r,d,T(n.name,"name")),o("kind")&&await ge(r,d,T(n.kind,"kind")),t.length===0)return;if((await r.prepareUncached(`UPDATE nodes SET ${t.map(([l])=>`${l} = ?`).join(", ")} WHERE node_id = ?`).run(...t.map(([,l])=>l),e)).changes!==1)throw new Error(`unknown node: ${e}`)}),await N(e)}i(Tn,"updateNode");async function Ln(e,n){await(await s()).prepare("UPDATE nodes SET status = ? WHERE node_id = ?").run(n,e)}i(Ln,"setStatus");async function Dn(e,n){await(await s()).prepare("UPDATE nodes SET intent = ? WHERE node_id = ?").run(n??null,e)}i(Dn,"setIntent");async function gn(e,n,t={}){if(t.outcome===void 0){const o=[],r=[];Object.prototype.hasOwnProperty.call(n,"status")&&(o.push("status = ?"),r.push(n.status??null)),Object.prototype.hasOwnProperty.call(n,"intent")&&(o.push("intent = ?"),r.push(n.intent??null)),Object.prototype.hasOwnProperty.call(n,"terminal_reason")&&(o.push("terminal_reason = ?"),r.push(n.terminal_reason??null));const d=["node_id = ?"];r.push(e),t.fromStatuses!==void 0&&(d.push(`status IN (${t.fromStatuses.map(()=>"?").join(", ")})`),r.push(...t.fromStatuses)),t.requireNoFinalReport===!0&&d.push("final_report IS NULL");const c=await(await s()).prepareUncached(`UPDATE nodes SET ${o.join(", ")} WHERE ${d.join(" AND ")}`).run(...r);return Number(c.changes)}const a=t.outcome;return await p(async o=>{const r=[],d=[];Object.prototype.hasOwnProperty.call(n,"status")&&(r.push("status = ?"),d.push(n.status??null)),Object.prototype.hasOwnProperty.call(n,"intent")&&(r.push("intent = ?"),d.push(n.intent??null)),Object.prototype.hasOwnProperty.call(n,"terminal_reason")&&(r.push("terminal_reason = ?"),d.push(n.terminal_reason??null));const c=i((m,u)=>a.supersede===!0?"?":`CASE WHEN outcome_at IS NULL THEN ? ELSE ${u} END`,"outcomeValue");r.push(`outcome_kind = ${c(a.kind,"outcome_kind")}`,`outcome_reason = ${c(a.reason,"outcome_reason")}`,`outcome_detail_json = ${c(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 = ${c(a.at,"outcome_at")}`),d.push(a.kind,a.reason,a.detailJson,a.at);const l=["node_id = ?"];d.push(e),t.fromStatuses!==void 0&&(l.push(`status IN (${t.fromStatuses.map(()=>"?").join(", ")})`),d.push(...t.fromStatuses)),t.requireNoFinalReport===!0&&l.push("final_report IS NULL");const _=await o.prepareUncached(`UPDATE nodes SET ${r.join(", ")} WHERE ${l.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 ue(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(gn,"transitionStatusIntent");async function An(e){return(await(await s()).prepare("SELECT final_report FROM nodes WHERE node_id = ?").get(e))?.final_report??null}i(An,"getFinalReport");async function vn(e,n){return(await(await s()).prepare("UPDATE nodes SET final_report = NULL, finalized_at = NULL WHERE node_id = ? AND final_report = ?").run(e,n)).changes>0}i(vn,"clearFinalReportIfMatches");async function kn(e,n){await(await s()).prepare('UPDATE nodes SET tmux_session = ?, "window" = ?, pane = ? WHERE node_id = ?').run(n.tmux_session??null,n.window??null,n.pane??null,e)}i(kn,"setPresence");async function xn(e,n){await(await s()).prepare("UPDATE nodes SET launched_at = ?, frozen_at = NULL, respawn_not_before = NULL WHERE node_id = ?").run(n,e)}i(xn,"recordLaunch");async function Un(e,n){await(await s()).prepare("UPDATE nodes SET last_settled_at = ? WHERE node_id = ?").run(n,e)}i(Un,"recordSettledTurn");async function jn(e,n){await(await s()).prepare("UPDATE nodes SET frozen_at = ? WHERE node_id = ?").run(n,e)}i(jn,"setFrozen");async function Wn(e){await(await s()).prepare("UPDATE nodes SET frozen_at = NULL WHERE node_id = ?").run(e)}i(Wn,"clearFrozen");async function bn(e,n){await(await s()).prepare("UPDATE nodes SET respawn_failures = ?, respawn_not_before = ? WHERE node_id = ?").run(n.failures,n.notBefore,e)}i(bn,"recordRespawnAttempt");const w="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 Ue(e){return e.worktree_state===null?null:ye(Object.fromEntries(z.map(n=>[n,e[`worktree_${n}`]])))}i(Ue,"worktreeFromProjection");async function In(e){const n=z.map(a=>`w.${a} AS worktree_${a}`).join(", "),t=await(await s()).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 h(t,async a=>await $(a,Ue(a)))}i(In,"listNodesByProfile");async function Fn(){return(await(await s()).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(Fn,"browseNodes");async function Hn(e,n){await(await s()).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(Hn,"updateNodeTelemetry");async function Cn(){return(await(await s()).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,17 +29,17 @@ 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(Cn,"graphNodes");async function je(e){const n=[w],t=[];if(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 s()).prepareUncached(`SELECT * FROM node_rows WHERE ${n.join(" AND ")} ORDER BY created`).all(...t)).map(f)}i(je,"listNodes");const We=`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
|
-
FROM nodes
|
|
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 Mn(){return await(await s()).prepare(We).all()}i(Mn,"listTerminalPlacementResidue");const be=`WITH candidates AS MATERIALIZED (
|
|
40
40
|
SELECT node_id FROM nodes WHERE status IN ('active', 'idle')
|
|
41
41
|
UNION
|
|
42
|
-
SELECT node_id FROM nodes
|
|
42
|
+
SELECT node_id FROM nodes
|
|
43
43
|
WHERE frozen_at IS NOT NULL AND status IN ('done', 'canceled', 'dead')
|
|
44
44
|
UNION
|
|
45
45
|
SELECT node_id FROM executions
|
|
@@ -50,16 +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
|
|
53
|
+
ORDER BY v.created`;async function Pn(){return(await(await s()).prepare(be).all()).map(f)}i(Pn,"listNodesForSupervision");const Ie=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");async function $n(){return(await(await s()).prepare(`SELECT v.* FROM node_rows v
|
|
54
|
+
WHERE ${Ie("v")}
|
|
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($n,"listParkedResidentsWithWakeMail");async function qn(){return await(await s()).prepare(`SELECT node_id, name, created, grantee FROM node_rows WHERE ${w} ORDER BY created`).all()}i(qn,"listNodeIdentities");async function zn(e,n){await(await s()).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(zn,"registerWarmSpare");async function Bn(e){return(await(await s()).prepare("DELETE FROM warm_pool WHERE node_id = ?").run(e)).changes>0}i(Bn,"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,
|
|
54
57
|
n.status AS status, n.pi_pid AS pi_pid, n.pi_pid_identity AS pi_pid_identity
|
|
55
|
-
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
|
|
56
|
-
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}))}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 Jn(){return await L(await s())}i(Jn,"listWarmSpares");async function Yn(e){return await(await s()).prepare("SELECT 1 FROM warm_pool WHERE node_id = ?").get(e)!==void 0}i(Yn,"isWarmSpare");function B(e){return e.status!=="active"&&e.status!=="idle"?!1:Ne(e.pi_pid,e.pi_pid_identity)==="alive"}i(B,"spareIsClaimable");async function Vn(e,n,t={}){return await p(async a=>{const o=(await L(a,e)).find(B);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 I(a,o.node_id,t.outcomeDelivery),o.node_id)})}i(Vn,"claimWarmSpare");async function Qn(e){return(await L(await s(),e)).filter(B).length}i(Qn,"warmSpareCount");async function Kn(e){return(await(await s()).prepare("SELECT node_id FROM nodes WHERE parent = ? ORDER BY created").all(e)).map(n=>n.node_id)}i(Kn,"childrenOf");async function Gn(){return(await(await s()).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(Gn,"rosterNodes");async function Xn(e){return(await(await s()).prepareUncached(`SELECT * FROM node_rows
|
|
57
60
|
WHERE parent IS NULL AND kind != 'human'
|
|
58
61
|
AND status IN ('active','idle') AND cwd = ?
|
|
59
62
|
AND ${w}
|
|
60
|
-
ORDER BY created DESC`).all(e)).map(f)}i(Xn,"listResumableRoots");export*from"./tree.js";async function
|
|
61
|
-
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 re().delete(o)}catch(r){if(!(r instanceof se))throw r}}i(
|
|
63
|
+
ORDER BY created DESC`).all(e)).map(f)}i(Xn,"listResumableRoots");export*from"./tree.js";async function Zn(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(Zn,"withFreshTerminalGuard");async function R(e){return await fe(e)}i(R,"hasUnreconciledManagedWorktree");async function J(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===U()||b(e)?!1:n.pastTtl===!0||!["crashed","stranded",null].includes(e.terminal_reason??null)}i(J,"isCollectible");async function D(e){if(e.length===0)return[];const n=e.map(()=>"?").join(",");return(await(await s()).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 s(),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 re().delete(o)}catch(r){if(!(r instanceof se))throw r}}i(g,"deleteNodeConversations");async function A(e,n){const t=await M(e,n);return t===null||t.deleted_at!==null?!1:(await ve(e,t),!0)}i(A,"deleteNodeObject");async function v(e){return{nodeId:e,app:await N(e)===null?null:await he(e)}}i(v,"nodeFiles");async function Y({nodeId:e,app:n}){n!==null&&W(C(n).nodes,await Le(e,n)),W(ae(H(e)),H(e))}i(Y,"removeNodeAppTrees");function V({nodeId:e}){return[Oe(e)]}i(V,"nodeOwnedDirs");function Q({nodeId:e,app:n}){if(n!==null)try{ee(j(C(n).conversations,e))}catch(t){if(!["ENOENT","ENOTEMPTY"].includes(t.code??""))throw t}}i(Q,"removeNodeConversationDir");async function k(e){await Y(e);for(const n of V(e))ne(n,{recursive:!0,force:!0});Q(e)}i(k,"removeNodeFiles");async function et(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(et,"deleteNode");async function nt(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 h(n,v),o=await D(n),r=n.map(()=>"?").join(","),d=(await(await s()).prepareUncached(`SELECT node_id FROM nodes WHERE node_id IN (${r}) ORDER BY created`).all(...n)).map(({node_id:l})=>l),c=await y(d,async l=>await p(async _=>await A(_,l)));await g(o);for(const l of c){const _=a[n.indexOf(l)];await x(_,async()=>await k(_))}return c}i(nt,"deleteNodes");async function tt(e){const n=e.dryRun??!1,t=e.includeStale??!1,a=Date.now()-e.ttlDays*864e5,o=new Date(a).toISOString(),r=U()??"",d=await s(),c=i(u=>X(u)<a,"quiet"),l=i(u=>({node_id:u.node_id,status:u.status,created:u.created}),"toPruned"),_=(await y((await d.prepare(`SELECT * FROM node_rows
|
|
62
65
|
WHERE status IN ('dead', 'done', 'canceled') AND created < ?
|
|
63
|
-
ORDER BY created`).all(o)).map(f),async
|
|
66
|
+
ORDER BY created`).all(o)).map(f),async u=>await J(u,{pastTtl:!0})&&c(u)&&!await e.held(u.node_id))).map(l),S=t?(await y((await d.prepare(`SELECT * FROM node_rows
|
|
64
67
|
WHERE status IN ('active', 'idle') AND created < ?
|
|
65
|
-
ORDER BY created`).all(o)).map(f),async
|
|
68
|
+
ORDER BY created`).all(o)).map(f),async u=>u.node_id!==r&&!me(u.pi_pid??null)&&(u.frozen_at??null)===null&&(u.respawn_not_before??null)===null&&u.intent!=="idle-release"&&!b(u)&&!await de(u.node_id)&&!await R(u.node_id)&&c(u)&&!await e.held(u.node_id))).map(l):[],m=await K([..._,...S]);return n||m.length===0?{pruned:m,dryRun:n}:(await G(m,e.removeDirs),{pruned:m,dryRun:n})}i(tt,"pruneNodes");async function K(e){const t=(await s()).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(r=>a.has(r.node_id)))}i(K,"retainRunRootsWithMembers");async function G(e,n){if(e.length===0)return;const t=await h(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 r=>await A(r,o.node_id));if(await g(a),n!==void 0){for(const o of t)await x(o,async()=>{await Y(o),Q(o)});n(t.flatMap(V));return}for(const o of t)await x(o,async()=>await k(o))}i(G,"deletePrunedNodes");async function x(e,n){try{n()}catch(t){oe({level:"error",event:"canvas.node_files.remove_failed",error:t,fields:{node_id:e.nodeId}})}}i(x,"eachNodeBestEffort");function X(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=te(j(Se(e.node_id),"telemetry.json"),{throwIfNoEntry:!1});t!==void 0&&t.mtimeMs>n&&(n=t.mtimeMs)}catch{}return n}i(X,"lastActivityMs");async function at(e){const n=e.dryRun??!1,t=await je(),a=await y(t,async l=>await J(l)&&!await e.held(l.node_id)),o=t.length-a.length,r=Math.max(0,e.maxNodes-o),d=a.map(l=>({row:l,recency:X(l)})).sort((l,_)=>_.recency-l.recency).slice(r).map(({row:l})=>({node_id:l.node_id,status:l.status,created:l.created})),c=await K(d);return n||c.length===0?{pruned:c,dryRun:n}:(await G(c),{pruned:c,dryRun:n})}i(at,"pruneToLimit");export{Ie as PARKED_RESIDENT_SQL,En as abandonCreateRequest,sn as assertRunNotDeleting,Fn as browseNodes,wn as canonicalSessionFile,Kn as childrenOf,Vn as claimWarmSpare,vn as clearFinalReportIfMatches,Wn as clearFrozen,fn as completeCreateRequest,dn as createNode,et as deleteNode,nt as deleteNodes,Rn as dueNodeDeadlines,ln as findCreateRequest,yn as findNodeBySessionFile,An as getFinalReport,N as getNode,mn as getRow,Nn as getRowByPane,Cn as graphNodes,J as isCollectible,Yn as isWarmSpare,X as lastActivityMs,qn as listNodeIdentities,je as listNodes,In as listNodesByProfile,Pn as listNodesForSupervision,$n as listParkedResidentsWithWakeMail,Xn as listResumableRoots,Mn as listTerminalPlacementResidue,Jn as listWarmSpares,v as nodeFiles,be as nodesForSupervisionSql,un as pruneExpiredCreateRequests,tt as pruneNodes,at as pruneToLimit,_n as recordCreateRequestNode,xn as recordLaunch,bn as recordRespawnAttempt,Un as recordSettledTurn,zn as registerWarmSpare,k as removeNodeFiles,cn as reserveCreateRequest,pn as reserveCreateRequestNode,hn as rewriteLocalNodeIdentity,Gn as rosterNodes,jn as setFrozen,Dn as setIntent,Sn as setNodeDeadline,kn as setPresence,Ln as setStatus,On as shiftProfileNodeDeadlines,We as terminalPlacementResidueSql,gn as transitionStatusIntent,Bn as unregisterWarmSpare,Tn as updateNode,Hn as updateNodeTelemetry,Qn as warmSpareCount,Zn as withFreshTerminalGuard};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { type CanvasDb } from './db.js';
|
|
2
1
|
import { type DeliverySettlement } from './deliveries.js';
|
|
3
2
|
import { type DeliveryFailure, type DeliverySettlement as ProcessDeliverySettlement } from './delivery-contract.js';
|
|
4
3
|
export type DaemonActionState = 'pending' | 'handed_off' | 'acknowledged' | 'permanent_failed';
|
|
@@ -29,8 +28,6 @@ export interface DaemonActionRecord {
|
|
|
29
28
|
createdAt: number;
|
|
30
29
|
updatedAt: number;
|
|
31
30
|
}
|
|
32
|
-
/** The table DDL. Shared by the migration that creates it; idempotent. */
|
|
33
|
-
export declare function createDaemonActionsTable(db: CanvasDb): Promise<void>;
|
|
34
31
|
/** Insert one frozen daemon action, due now. A duplicate idempotency key returns the original action id. */
|
|
35
32
|
export declare function enqueueDaemonAction(input: DaemonActionInput): Promise<string>;
|
|
36
33
|
export declare function readDaemonAction(idempotencyKey: string): Promise<DaemonActionRecord | null>;
|