@north-light/cli 0.3.440 → 0.3.442

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.
@@ -39,6 +39,8 @@ export declare function isolatedNodeHome(nodeId: string, grantee: string): strin
39
39
  /** The HOME a node's processes get: its own home in an isolated run, else the app's. */
40
40
  export declare function nodeHome(nodeId: string, grantee: string, isolated: boolean): string;
41
41
  export declare function nodeRunDir(nodeId: string): string;
42
+ /** The run's scope allow-list as a JSON array, beside `broker-launch.json`. */
43
+ export declare function scopesFilePath(nodeId: string): string;
42
44
  export declare function contextDir(nodeId: string, grantee?: string): string;
43
45
  /** Replace the literal env-var tokens `$CRTR_CONTEXT_DIR` / `$CRTR_NODE_ID` in
44
46
  * agent-facing doc text with the node's real values, so a doc surfaced INTO a
@@ -1 +1 @@
1
- var P=Object.defineProperty;var r=(t,n)=>P(t,"name",{value:n,configurable:!0});import{join as e}from"node:path";import{mkdirSync as s}from"node:fs";import{appSpace as f,runtimeRoot as k,socketDir as m}from"../layout.js";import{envGrantee as R,envNodeId as w}from"../../shared/env.js";import{withDirBeneath as h}from"../spaces/open-beneath.js";function i(){return k()}r(i,"crtrHome");function H(){return e(i(),"canvas.db")}r(H,"canvasDbPath");function B(){return e(i(),"on-disk-migration.lock")}r(B,"onDiskMigrationLockPath");function L(){return e(i(),"on-disk-migrations.json")}r(L,"onDiskMigrationMarkerPath");function O(t){return e(a(t),"view.sock")}r(O,"viewSocketPath");function D(){return e(i(),"nodes")}r(D,"nodesRoot");function b(){return e(i(),"reviews")}r(b,"reviewsRoot");function l(t){return c(t),e(b(),t)}r(l,"reviewDir");function $(t){return e(l(t),"result.json")}r($,"reviewResultPath");function X(t){s(l(t),{recursive:!0,mode:448})}r(X,"ensureReviewDir");function F(){return e(m(),"api.sock")}r(F,"apiSocketPath");function G(){return e(i(),"session-list-cache.json")}r(G,"sessionListCachePath");const j=128;function g(t){return typeof t=="string"&&t!==""&&t!=="."&&t!==".."&&!/[\\/\0]/u.test(t)&&Buffer.byteLength(t,"utf8")<=j}r(g,"isSafeNodeId");function c(t){if(!g(t))throw new TypeError("node_id must be a non-empty path segment of at most 128 UTF-8 bytes")}r(c,"assertSafeNodeId");function p(t){return c(t),e(D(),t)}r(p,"nodeRecordDir");const u=p;function x(t,n){c(t);const o=n??(w()===t?R():void 0);if(!o)throw new Error(`node ${t} has no app assignment`);return e(f(o).nodes,t)}r(x,"nodeAgentDir");function N(t,n){return e(x(t,n),"home")}r(N,"isolatedNodeHome");function J(t,n,o){return o?N(t,n):f(n).home}r(J,"nodeHome");function a(t){return c(t),process.platform==="linux"?e(m(),"nodes",t):e(p(t),"run")}r(a,"nodeRunDir");function _(t,n){return e(x(t,n),"context")}r(_,"contextDir");function U(t,n,o){return t.replaceAll("$CRTR_CONTEXT_DIR",_(n,o)).replaceAll("$CRTR_NODE_ID",n)}r(U,"interpolateNodePaths");function S(t){return e(p(t),"job")}r(S,"jobDir");function T(t){return e(a(t),"job")}r(T,"nodeRunJobDir");function Y(t){return e(u(t),"messages")}r(Y,"messagesDir");function q(t){return e(u(t),"meta.json")}r(q,"nodeMetaPath");function z(t){return e(u(t),"inbox.jsonl")}r(z,"inboxPath");function K(t){return e(a(t),"situational-context.md")}r(K,"situationalContextPath");function Q(t){return e(u(t),"passive.jsonl")}r(Q,"passivePath");function V(t){return e(u(t),"transcript.jsonl")}r(V,"transcriptPath");function W(t){return e(u(t),"session.ptr")}r(W,"sessionPtrPath");function Z(t,n){const o=x(t,n);s(p(t),{recursive:!0}),s(S(t),{recursive:!0});for(const v of["context","reports","attachments","worktree"])h(f(n).nodes,e(o,v),()=>{},!0);h(a(t),T(t),()=>{},!0)}r(Z,"ensureNodeDirs");function I(){s(D(),{recursive:!0})}r(I,"ensureHome");export{F as apiSocketPath,H as canvasDbPath,_ as contextDir,i as crtrHome,I as ensureHome,Z as ensureNodeDirs,X as ensureReviewDir,z as inboxPath,U as interpolateNodePaths,g as isSafeNodeId,N as isolatedNodeHome,S as jobDir,Y as messagesDir,x as nodeAgentDir,u as nodeDir,J as nodeHome,q as nodeMetaPath,p as nodeRecordDir,a as nodeRunDir,T as nodeRunJobDir,D as nodesRoot,B as onDiskMigrationLockPath,L as onDiskMigrationMarkerPath,Q as passivePath,l as reviewDir,$ as reviewResultPath,b as reviewsRoot,G as sessionListCachePath,W as sessionPtrPath,K as situationalContextPath,V as transcriptPath,O as viewSocketPath};
1
+ var P=Object.defineProperty;var e=(t,n)=>P(t,"name",{value:n,configurable:!0});import{join as r}from"node:path";import{mkdirSync as c}from"node:fs";import{appSpace as f,runtimeRoot as k,socketDir as m}from"../layout.js";import{envGrantee as R,envNodeId as j}from"../../shared/env.js";import{withDirBeneath as h}from"../spaces/open-beneath.js";function i(){return k()}e(i,"crtrHome");function H(){return r(i(),"canvas.db")}e(H,"canvasDbPath");function B(){return r(i(),"on-disk-migration.lock")}e(B,"onDiskMigrationLockPath");function L(){return r(i(),"on-disk-migrations.json")}e(L,"onDiskMigrationMarkerPath");function O(t){return r(s(t),"view.sock")}e(O,"viewSocketPath");function D(){return r(i(),"nodes")}e(D,"nodesRoot");function w(){return r(i(),"reviews")}e(w,"reviewsRoot");function l(t){return p(t),r(w(),t)}e(l,"reviewDir");function $(t){return r(l(t),"result.json")}e($,"reviewResultPath");function F(t){c(l(t),{recursive:!0,mode:448})}e(F,"ensureReviewDir");function X(){return r(m(),"api.sock")}e(X,"apiSocketPath");function G(){return r(i(),"session-list-cache.json")}e(G,"sessionListCachePath");const b=128;function g(t){return typeof t=="string"&&t!==""&&t!=="."&&t!==".."&&!/[\\/\0]/u.test(t)&&Buffer.byteLength(t,"utf8")<=b}e(g,"isSafeNodeId");function p(t){if(!g(t))throw new TypeError("node_id must be a non-empty path segment of at most 128 UTF-8 bytes")}e(p,"assertSafeNodeId");function a(t){return p(t),r(D(),t)}e(a,"nodeRecordDir");const u=a;function x(t,n){p(t);const o=n??(j()===t?R():void 0);if(!o)throw new Error(`node ${t} has no app assignment`);return r(f(o).nodes,t)}e(x,"nodeAgentDir");function N(t,n){return r(x(t,n),"home")}e(N,"isolatedNodeHome");function J(t,n,o){return o?N(t,n):f(n).home}e(J,"nodeHome");function s(t){return p(t),process.platform==="linux"?r(m(),"nodes",t):r(a(t),"run")}e(s,"nodeRunDir");function U(t){return r(s(t),"scopes.json")}e(U,"scopesFilePath");function _(t,n){return r(x(t,n),"context")}e(_,"contextDir");function Y(t,n,o){return t.replaceAll("$CRTR_CONTEXT_DIR",_(n,o)).replaceAll("$CRTR_NODE_ID",n)}e(Y,"interpolateNodePaths");function S(t){return r(a(t),"job")}e(S,"jobDir");function T(t){return r(s(t),"job")}e(T,"nodeRunJobDir");function q(t){return r(u(t),"messages")}e(q,"messagesDir");function z(t){return r(u(t),"meta.json")}e(z,"nodeMetaPath");function K(t){return r(u(t),"inbox.jsonl")}e(K,"inboxPath");function Q(t){return r(s(t),"situational-context.md")}e(Q,"situationalContextPath");function V(t){return r(u(t),"passive.jsonl")}e(V,"passivePath");function W(t){return r(u(t),"transcript.jsonl")}e(W,"transcriptPath");function Z(t){return r(u(t),"session.ptr")}e(Z,"sessionPtrPath");function I(t,n){const o=x(t,n);c(a(t),{recursive:!0}),c(S(t),{recursive:!0});for(const v of["context","reports","attachments","worktree"])h(f(n).nodes,r(o,v),()=>{},!0);h(s(t),T(t),()=>{},!0)}e(I,"ensureNodeDirs");function tt(){c(D(),{recursive:!0})}e(tt,"ensureHome");export{X as apiSocketPath,H as canvasDbPath,_ as contextDir,i as crtrHome,tt as ensureHome,I as ensureNodeDirs,F as ensureReviewDir,K as inboxPath,Y as interpolateNodePaths,g as isSafeNodeId,N as isolatedNodeHome,S as jobDir,q as messagesDir,x as nodeAgentDir,u as nodeDir,J as nodeHome,z as nodeMetaPath,a as nodeRecordDir,s as nodeRunDir,T as nodeRunJobDir,D as nodesRoot,B as onDiskMigrationLockPath,L as onDiskMigrationMarkerPath,V as passivePath,l as reviewDir,$ as reviewResultPath,w as reviewsRoot,U as scopesFilePath,G as sessionListCachePath,Z as sessionPtrPath,Q as situationalContextPath,W as transcriptPath,O as viewSocketPath};
@@ -27,6 +27,13 @@ export declare function syncDirectory(path: string): void;
27
27
  * tmux inbox watcher, or crtrd serving a ticket read — sees either the old
28
28
  * bytes or the new ones and never a truncated file mid-write. */
29
29
  export declare function atomicWriteText(path: string, content: string | Uint8Array, opts?: AtomicWriteOptions): void;
30
+ /** Publish `path` with `content` so it is never observable empty or partial, and
31
+ * never replaces an existing file. The bytes reach stable storage under a
32
+ * temporary name first; a hard link then makes them appear at `path` in one
33
+ * step and fails with EEXIST if another initializer won. The containing
34
+ * directory is fsynced so the new entry survives a volume handoff or power loss.
35
+ * Returns false when `path` already existed. */
36
+ export declare function publishFileExclusive(path: string, content: string, mode: number): boolean;
30
37
  /** Fsync every file and directory under `root` (and `root` itself), so a tree about to be renamed into place
31
38
  * reaches stable storage before the rename can. */
32
39
  export declare function syncTree(root: string): void;
@@ -1,3 +1,3 @@
1
- var E=Object.defineProperty;var e=(r,t)=>E(r,"name",{value:t,configurable:!0});import{existsSync as c,lstatSync as W,mkdirSync as b,readdirSync as f,readFileSync as l,rmSync as S,statSync as w,symlinkSync as g,writeFileSync as a,cpSync as T,readlinkSync as I,renameSync as $,chmodSync as L,realpathSync as j,closeSync as F,fsyncSync as J,openSync as O}from"node:fs";import{dirname as u,join as x,relative as C,sep as M}from"node:path";import{homedir as h,platform as k}from"node:os";function D(r){try{return j(r)}catch{return r}}e(D,"realpathOrSelf");function V(r,t=h()){return r==="~"?t:r.startsWith("~/")||k()==="win32"&&r.startsWith("~\\")?x(t,r.slice(2)):r}e(V,"expandTilde");function X(r,t=h()){return r===t?"~":r.startsWith(t+M)?"~"+r.slice(t.length):r}e(X,"tildify");function y(r){b(r,{recursive:!0})}e(y,"ensureDir");function Y(r,t){y(u(r)),a(r,JSON.stringify(t,null,2)+`
2
- `,"utf8")}e(Y,"writeJson");function Z(r,t,n){q(r,JSON.stringify(t,null,2)+`
3
- `,n)}e(Z,"atomicWriteJson");function N(r){const t=O(r,"r");try{J(t)}finally{F(t)}}e(N,"syncFile");function v(r){const t=O(r,"r");try{J(t)}finally{F(t)}}e(v,"syncDirectory");function q(r,t,n){n?.createParent!==!1&&y(u(r));const i=`${r}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`,o=n?.mode??(c(r)?w(r).mode&511:384);try{a(i,t,{mode:o}),L(i,o),n?.durable===!0&&N(i),$(i,r),n?.durable===!0&&v(u(r))}finally{c(i)&&S(i,{force:!0})}}e(q,"atomicWriteText");function z(r){for(const t of f(r,{withFileTypes:!0})){const n=x(r,t.name);t.isDirectory()?z(n):t.isFile()&&N(n)}v(r)}e(z,"syncTree");function A(r){return JSON.parse(l(r,"utf8"))}e(A,"readJson");function _(r){try{return A(r)}catch(t){if(t.code==="ENOENT")return null;throw t}}e(_,"readJsonIfExists");function P(r){try{return JSON.parse(l(r,"utf8"))}catch{return null}}e(P,"readJsonOrNull");function rr(r){return c(r)?l(r,"utf8"):null}e(rr,"readTextIfExists");function tr(r){return l(r,"utf8")}e(tr,"readText");function er(r,t){y(u(r)),a(r,t,"utf8")}e(er,"writeText");function B(r){try{return w(r).isDirectory()}catch{return!1}}e(B,"isDir");function G(r){try{return W(r).isSymbolicLink()}catch{return!1}}e(G,"isSymlink");function nr(r){return c(r)}e(nr,"pathExists");function ir(r){return c(r)?f(r,{withFileTypes:!0}).filter(t=>t.isDirectory()||t.isSymbolicLink()).map(t=>t.name):[]}e(ir,"listDirs");function or(r){return c(r)?f(r):[]}e(or,"listEntries");function H(r){!c(r)&&!G(r)||S(r,{recursive:!0,force:!0})}e(H,"removePath");function cr(r,t,n={}){y(u(t)),H(t);const i=k()==="win32";if(!n.noSymlink&&!i){const o=C(D(u(t)),D(r));try{return g(o,t,B(r)?"dir":"file"),"symlink"}catch{return T(r,t,{recursive:!0}),"copy"}}return T(r,t,{recursive:!0}),"copy"}e(cr,"linkOrCopy");function ur(r){try{return I(r)}catch{return null}}e(ur,"readSymlinkTarget");function sr(r,t=()=>!0,n=()=>!1){const i=[];if(!c(r))return i;const o=[r];for(;o.length;){const m=o.pop();let p;try{p=f(m,{withFileTypes:!0})}catch{continue}for(const s of p){const d=x(m,s.name);s.isDirectory()?n(s.name)||o.push(d):s.isFile()&&t(s.name)&&i.push(d)}}return i}e(sr,"walkFiles");function fr(){return new Date().toISOString()}e(fr,"nowIso");export{Z as atomicWriteJson,q as atomicWriteText,y as ensureDir,V as expandTilde,B as isDir,G as isSymlink,cr as linkOrCopy,ir as listDirs,or as listEntries,fr as nowIso,nr as pathExists,A as readJson,_ as readJsonIfExists,P as readJsonOrNull,ur as readSymlinkTarget,tr as readText,rr as readTextIfExists,D as realpathOrSelf,H as removePath,v as syncDirectory,N as syncFile,z as syncTree,X as tildify,sr as walkFiles,Y as writeJson,er as writeText};
1
+ var D=Object.defineProperty;var e=(r,t)=>D(r,"name",{value:t,configurable:!0});import{existsSync as u,lstatSync as N,mkdirSync as $,readdirSync as y,readFileSync as a,rmSync as F,statSync as k,symlinkSync as b,writeFileSync as m,cpSync as E,readlinkSync as W,renameSync as I,chmodSync as L,realpathSync as M,closeSync as p,fsyncSync as S,openSync as d,linkSync as j,unlinkSync as B,writeSync as C}from"node:fs";import{dirname as f,join as w,relative as X,sep as q}from"node:path";import{homedir as J,platform as O}from"node:os";function g(r){try{return M(r)}catch{return r}}e(g,"realpathOrSelf");function Z(r,t=J()){return r==="~"?t:r.startsWith("~/")||O()==="win32"&&r.startsWith("~\\")?w(t,r.slice(2)):r}e(Z,"expandTilde");function _(r,t=J()){return r===t?"~":r.startsWith(t+q)?"~"+r.slice(t.length):r}e(_,"tildify");function x(r){$(r,{recursive:!0})}e(x,"ensureDir");function P(r,t){x(f(r)),m(r,JSON.stringify(t,null,2)+`
2
+ `,"utf8")}e(P,"writeJson");function rr(r,t,i){z(r,JSON.stringify(t,null,2)+`
3
+ `,i)}e(rr,"atomicWriteJson");function v(r){const t=d(r,"r");try{S(t)}finally{p(t)}}e(v,"syncFile");function h(r){const t=d(r,"r");try{S(t)}finally{p(t)}}e(h,"syncDirectory");function z(r,t,i){i?.createParent!==!1&&x(f(r));const n=`${r}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`,o=i?.mode??(u(r)?k(r).mode&511:384);try{m(n,t,{mode:o}),L(n,o),i?.durable===!0&&v(n),I(n,r),i?.durable===!0&&h(f(r))}finally{u(n)&&F(n,{force:!0})}}e(z,"atomicWriteText");function tr(r,t,i){const n=`${r}.${process.pid}.${Math.random().toString(16).slice(2)}.tmp`,o=d(n,"wx",i);try{try{const c=Buffer.from(t,"utf8");for(let s=0;s<c.length;)s+=C(o,c,s,c.length-s);S(o)}finally{p(o)}try{j(n,r)}catch(c){if(c.code==="EEXIST")return!1;throw c}return h(f(r)),!0}finally{try{B(n)}catch{}}}e(tr,"publishFileExclusive");function A(r){for(const t of y(r,{withFileTypes:!0})){const i=w(r,t.name);t.isDirectory()?A(i):t.isFile()&&v(i)}h(r)}e(A,"syncTree");function G(r){return JSON.parse(a(r,"utf8"))}e(G,"readJson");function er(r){try{return G(r)}catch(t){if(t.code==="ENOENT")return null;throw t}}e(er,"readJsonIfExists");function nr(r){try{return JSON.parse(a(r,"utf8"))}catch{return null}}e(nr,"readJsonOrNull");function ir(r){return u(r)?a(r,"utf8"):null}e(ir,"readTextIfExists");function or(r){return a(r,"utf8")}e(or,"readText");function cr(r,t){x(f(r)),m(r,t,"utf8")}e(cr,"writeText");function H(r){try{return k(r).isDirectory()}catch{return!1}}e(H,"isDir");function K(r){try{return N(r).isSymbolicLink()}catch{return!1}}e(K,"isSymlink");function ur(r){return u(r)}e(ur,"pathExists");function sr(r){return u(r)?y(r,{withFileTypes:!0}).filter(t=>t.isDirectory()||t.isSymbolicLink()).map(t=>t.name):[]}e(sr,"listDirs");function fr(r){return u(r)?y(r):[]}e(fr,"listEntries");function Q(r){!u(r)&&!K(r)||F(r,{recursive:!0,force:!0})}e(Q,"removePath");function lr(r,t,i={}){x(f(t)),Q(t);const n=O()==="win32";if(!i.noSymlink&&!n){const o=X(g(f(t)),g(r));try{return b(o,t,H(r)?"dir":"file"),"symlink"}catch{return E(r,t,{recursive:!0}),"copy"}}return E(r,t,{recursive:!0}),"copy"}e(lr,"linkOrCopy");function yr(r){try{return W(r)}catch{return null}}e(yr,"readSymlinkTarget");function ar(r,t=()=>!0,i=()=>!1){const n=[];if(!u(r))return n;const o=[r];for(;o.length;){const c=o.pop();let s;try{s=y(c,{withFileTypes:!0})}catch{continue}for(const l of s){const T=w(c,l.name);l.isDirectory()?i(l.name)||o.push(T):l.isFile()&&t(l.name)&&n.push(T)}}return n}e(ar,"walkFiles");function xr(){return new Date().toISOString()}e(xr,"nowIso");export{rr as atomicWriteJson,z as atomicWriteText,x as ensureDir,Z as expandTilde,H as isDir,K as isSymlink,lr as linkOrCopy,sr as listDirs,fr as listEntries,xr as nowIso,ur as pathExists,tr as publishFileExclusive,G as readJson,er as readJsonIfExists,nr as readJsonOrNull,yr as readSymlinkTarget,or as readText,ir as readTextIfExists,g as realpathOrSelf,Q as removePath,h as syncDirectory,v as syncFile,A as syncTree,_ as tildify,ar as walkFiles,P as writeJson,cr as writeText};
@@ -1,3 +1,3 @@
1
- var b=Object.defineProperty;var s=(o,e)=>b(o,"name",{value:e,configurable:!0});import{prepared as f}from"../sqlite-statements.js";import{covers as O,parseScope as x}from"@north-light/identity";import{activeGrant as _,grantFor as y}from"../grants/mirror.js";import{prepared as R}from"../canvas/db.js";import{readConfig as C}from"../config.js";import{SCOPE_VOCABULARY as m}from"../scopes.js";import{loadExactProfileManifest as L}from"../profiles/manifest.js";import{ensureOwner as w,getByRowid as v,nodeObject as M}from"./objects.js";function F(o){return{repos:o.map(e=>e.repo),checkouts:o}}s(F,"inRepos");function V(o,e,a){if(e.class==="anonymous")throw new Error("a publish token cannot read as a principal");if(e.class==="person")return{class:"person",person:h(o)};if(e.class==="app")return{class:"app",app:H(o,e.app),scopes:e.scopes};if(e.class==="cron"){const i=e.grant?_(e.grant)?.resolved??[]:m,c=e.scopes;return{class:"app",app:H(o,e.app),scopes:c===null?i:c.filter(u=>i.some(p=>O(p,u))),place:{run:e.origin_run??"",isolated:e.isolated}}}const n=d(o),r=M(o,e.node),t=r.owner===null?null:n.object(r.owner);if(!t)throw new Error(`node ${e.node} has no owner`);const l=f(o,"SELECT cwd, profile_id FROM nodes WHERE node_id = ?").get(e.node);return{class:"node",node:r,app:t,scopes:S(e.app,e.sub,e.grant,e.scopes,n),cwd:l.cwd,...F(a(l.cwd)),profile:l.profile_id===null?null:n.profile(l.profile_id,t)}}s(V,"readerFor");function $(o,e,a,n=d(o),r=!1){const t=R(o,"SELECT o.* FROM objects o WHERE o.id = ? AND o.type = 'node'").get(e),l=R(o,`SELECT (SELECT external_id FROM identities WHERE object = ?) AS grantee,
2
- sub, grant_id, scopes_json, cwd, profile_id FROM nodes WHERE node_id = ?`).get(t?.owner??null,e);if(!t||!l)throw new Error(`no node ${e}`);const{grantee:i,sub:c,grant_id:u,scopes_json:p,cwd:g,profile_id:j}=l,E=t.owner===null?null:n.object(t.owner);if(!E)throw new Error(`node ${e} has no owner`);const N=p===null?null:JSON.parse(p);return{class:"node",node:t,app:E,scopes:S(i,c??"",u,N,n,r),cwd:g,...F(a(g)),profile:j===null?null:n.profile(j,E)}}s($,"readerForNode");function Y(o,e,a=()=>[],n=d(o)){if(e.type==="node")return $(o,e.id,a,n,!0);const r=e.owner===null?null:n.object(e.owner);if(!r)throw new Error(`watcher ${e.id} has no owner`);const l=(r.type==="app"?r:r.type==="profile"&&r.owner!==null?n.object(r.owner):null)??f(o,"SELECT o.* FROM identities i JOIN objects o ON o.rowid = i.object WHERE i.external_id = 'app:terminal' AND i.scheme = 'app'").get()??f(o,"SELECT * FROM objects WHERE type = 'person' AND name = 'user'").get();if(!l)throw new Error("no terminal app or person object");const i=f(o,"SELECT external_id FROM identities WHERE object = ? AND scheme = 'app'").get(l.rowid)?.external_id??"app:terminal",c=f(o,"SELECT resolved_scopes_json FROM grants WHERE grantee = ? AND status = 'active'").all(i),u=r.type==="person"?null:c.length===0?i==="app:terminal"&&!n.terminalHasIssuer()?m:null:c.length===1?JSON.parse(c[0].resolved_scopes_json).filter(p=>p.mode==="always").map(p=>x(p.scope)):null;return{class:"object",object:e,node:e,owner:r,app:l,scopes:u,repos:r.type==="repo"?[r]:[]}}s(Y,"readerForObject");function d(o){return{grantById:_,grantFor:y,terminalHasIssuer:s(()=>!!C("user").runtime?.issuer,"terminalHasIssuer"),object:s(e=>v(o,e),"object"),profile:s((e,a)=>A(o,e,a),"profile")}}s(d,"liveLookups");function q(o){const e=d(o),a=new Map,n=s((r,t)=>(a.has(r)||a.set(r,t()),a.get(r)),"memo");return{grantById:s(r=>n(`g\0${r}`,()=>e.grantById(r)),"grantById"),grantFor:s((r,t)=>n(`a\0${r}\0${t}`,()=>e.grantFor(r,t)),"grantFor"),terminalHasIssuer:s(()=>n("issuer",()=>e.terminalHasIssuer()),"terminalHasIssuer"),object:s(r=>n(`o\0${r}`,()=>e.object(r)),"object"),profile:s((r,t)=>n(`p\0${r}\0${t.rowid}`,()=>e.profile(r,t)),"profile")}}s(q,"publishLookups");function S(o,e,a,n,r,t=!1){const l=a?r.grantById(a):r.grantFor(o,e),i=l&&l.grantee===o?l.resolved:o==="app:terminal"&&!r.terminalHasIssuer()?m:null;if(i===null){if(t)return null;throw new Error(`the grant of ${o} is not active`)}return n===null?i:n.filter(c=>i.some(u=>O(u,c)))}s(S,"nodeScopes");function h(o){return w(o,"person","local-user","user:local","user")}s(h,"personObject");function H(o,e){return e==="user:local"?h(o):w(o,"app","app",e,e.replace(/^app:/,""))}s(H,"appObject");function A(o,e,a){return f(o,`SELECT o.* FROM identities i JOIN objects o ON o.rowid = i.object
3
- WHERE i.scheme = 'profile' AND i.external_id = ?`).get(e)??w(o,"profile","profile",e,L(e).manifest.name,a)}s(A,"profileObject");export{d as liveLookups,A as profileObject,q as publishLookups,V as readerFor,$ as readerForNode,Y as readerForObject};
1
+ var N=Object.defineProperty;var s=(o,e)=>N(o,"name",{value:e,configurable:!0});import{prepared as u}from"../sqlite-statements.js";import{coveredByAny as O}from"../../shared/env.js";import{parseScope as b}from"@north-light/identity";import{activeGrant as _,grantFor as x}from"../grants/mirror.js";import{prepared as R}from"../canvas/db.js";import{readConfig as C}from"../config.js";import{SCOPE_VOCABULARY as m}from"../scopes.js";import{loadExactProfileManifest as L}from"../profiles/manifest.js";import{ensureOwner as w,getByRowid as v,nodeObject as A}from"./objects.js";function F(o){return{repos:o.map(e=>e.repo),checkouts:o}}s(F,"inRepos");function Y(o,e,a){if(e.class==="anonymous")throw new Error("a publish token cannot read as a principal");if(e.class==="person")return{class:"person",person:h(o)};if(e.class==="app")return{class:"app",app:y(o,e.app),scopes:e.scopes};if(e.class==="cron"){const i=e.grant?_(e.grant)?.resolved??[]:m,p=e.scopes;return{class:"app",app:y(o,e.app),scopes:p===null?i:p.filter(f=>O(i,f)),place:{run:e.origin_run??"",isolated:e.isolated}}}const n=d(o),r=A(o,e.node),t=r.owner===null?null:n.object(r.owner);if(!t)throw new Error(`node ${e.node} has no owner`);const l=u(o,"SELECT cwd, profile_id FROM nodes WHERE node_id = ?").get(e.node);return{class:"node",node:r,app:t,scopes:S(e.app,e.sub,e.grant,e.scopes,n),cwd:l.cwd,...F(a(l.cwd)),profile:l.profile_id===null?null:n.profile(l.profile_id,t)}}s(Y,"readerFor");function M(o,e,a,n=d(o),r=!1){const t=R(o,"SELECT o.* FROM objects o WHERE o.id = ? AND o.type = 'node'").get(e),l=R(o,`SELECT (SELECT external_id FROM identities WHERE object = ?) AS grantee,
2
+ sub, grant_id, scopes_json, cwd, profile_id FROM nodes WHERE node_id = ?`).get(t?.owner??null,e);if(!t||!l)throw new Error(`no node ${e}`);const{grantee:i,sub:p,grant_id:f,scopes_json:c,cwd:g,profile_id:j}=l,E=t.owner===null?null:n.object(t.owner);if(!E)throw new Error(`node ${e} has no owner`);const H=c===null?null:JSON.parse(c);return{class:"node",node:t,app:E,scopes:S(i,p??"",f,H,n,r),cwd:g,...F(a(g)),profile:j===null?null:n.profile(j,E)}}s(M,"readerForNode");function q(o,e,a=()=>[],n=d(o)){if(e.type==="node")return M(o,e.id,a,n,!0);const r=e.owner===null?null:n.object(e.owner);if(!r)throw new Error(`watcher ${e.id} has no owner`);const l=(r.type==="app"?r:r.type==="profile"&&r.owner!==null?n.object(r.owner):null)??u(o,"SELECT o.* FROM identities i JOIN objects o ON o.rowid = i.object WHERE i.external_id = 'app:terminal' AND i.scheme = 'app'").get()??u(o,"SELECT * FROM objects WHERE type = 'person' AND name = 'user'").get();if(!l)throw new Error("no terminal app or person object");const i=u(o,"SELECT external_id FROM identities WHERE object = ? AND scheme = 'app'").get(l.rowid)?.external_id??"app:terminal",p=u(o,"SELECT resolved_scopes_json FROM grants WHERE grantee = ? AND status = 'active'").all(i),f=r.type==="person"?null:p.length===0?i==="app:terminal"&&!n.terminalHasIssuer()?m:null:p.length===1?JSON.parse(p[0].resolved_scopes_json).filter(c=>c.mode==="always").map(c=>b(c.scope)):null;return{class:"object",object:e,node:e,owner:r,app:l,scopes:f,repos:r.type==="repo"?[r]:[]}}s(q,"readerForObject");function d(o){return{grantById:_,grantFor:x,terminalHasIssuer:s(()=>!!C("user").runtime?.issuer,"terminalHasIssuer"),object:s(e=>v(o,e),"object"),profile:s((e,a)=>$(o,e,a),"profile")}}s(d,"liveLookups");function z(o){const e=d(o),a=new Map,n=s((r,t)=>(a.has(r)||a.set(r,t()),a.get(r)),"memo");return{grantById:s(r=>n(`g\0${r}`,()=>e.grantById(r)),"grantById"),grantFor:s((r,t)=>n(`a\0${r}\0${t}`,()=>e.grantFor(r,t)),"grantFor"),terminalHasIssuer:s(()=>n("issuer",()=>e.terminalHasIssuer()),"terminalHasIssuer"),object:s(r=>n(`o\0${r}`,()=>e.object(r)),"object"),profile:s((r,t)=>n(`p\0${r}\0${t.rowid}`,()=>e.profile(r,t)),"profile")}}s(z,"publishLookups");function S(o,e,a,n,r,t=!1){const l=a?r.grantById(a):r.grantFor(o,e),i=l&&l.grantee===o?l.resolved:o==="app:terminal"&&!r.terminalHasIssuer()?m:null;if(i===null){if(t)return null;throw new Error(`the grant of ${o} is not active`)}return n===null?i:n.filter(p=>O(i,p))}s(S,"nodeScopes");function h(o){return w(o,"person","local-user","user:local","user")}s(h,"personObject");function y(o,e){return e==="user:local"?h(o):w(o,"app","app",e,e.replace(/^app:/,""))}s(y,"appObject");function $(o,e,a){return u(o,`SELECT o.* FROM identities i JOIN objects o ON o.rowid = i.object
3
+ WHERE i.scheme = 'profile' AND i.external_id = ?`).get(e)??w(o,"profile","profile",e,L(e).manifest.name,a)}s($,"profileObject");export{d as liveLookups,$ as profileObject,z as publishLookups,Y as readerFor,M as readerForNode,q as readerForObject};
@@ -1,4 +1,4 @@
1
- var X=Object.defineProperty;var l=(t,e)=>X(t,"name",{value:e,configurable:!0});import{DatabaseSync as _}from"node:sqlite";import{randomUUID as N}from"node:crypto";import{symlinkSync as Z,chmodSync as j,chownSync as M,closeSync as $,cpSync as ee,existsSync as d,fsyncSync as R,lchownSync as te,lstatSync as f,mkdirSync as w,openSync as k,readFileSync as y,readlinkSync as D,readSync as U,readdirSync as h,renameSync as b,rmSync as L,rmdirSync as g,unlinkSync as A,writeSync as q}from"node:fs";import{request as ne}from"node:http";import{basename as T,dirname as m,isAbsolute as re,join as c,relative as S,resolve as oe}from"node:path";import{LAYOUT_VERSION as C}from"../layout.js";import{InputError as O}from"../io.js";import{oldToNewPathMap as se}from"./paths.js";import{conversations as ie,memoryAndProfiles as ce,previewStores as ae,profileFolders as ue,repairMovedRepositories as le,worktrees as de}from"./stores.js";import{storedPaths as fe}from"./stored-paths.js";import{OldToNewPathMap as Ce,oldToNewPathMap as Oe}from"./paths.js";const pe=new Set([".DS_Store","Thumbs.db","desktop.ini"]);function he(t){return pe.has(t)||t.startsWith("._")}l(he,"isJunkFile");function F(t,e,n){const o=c(t.map.old.root,"runtime","set-aside",e);let r=c(o,n);for(let i=2;d(r);i++)r=c(o,`${n}.${i}`);return r}l(F,"setAsidePath");function I(t,e,n,o=T(t)){const r=F(n,e,o);return w(m(r),{recursive:!0}),b(t,r),n.moved++,n.changes.push(`${t} -> ${r} (set aside)`),r}l(I,"setAside");function me(t){const e=f(t);return e.isDirectory()?h(t).length===0:e.isFile()&&e.size===0}l(me,"empty");function W(t,e,n,o=!1){const r=f(t),i=f(e);if(r.isDirectory()&&i.isDirectory()&&!o)return{action:"merge"};if(r.isFile()&&i.isFile()&&Q(t,e))return{action:"drop-source"};if(me(e))return{action:"remove-destination",line:`stray-destination: removed empty ${i.isDirectory()?"folder":"file"} ${e} that predates the migration`};const s=F(n,"replaced",S(n.map.old.root,e));return{action:"set-aside-destination",line:`stray-destination: ${e} predates the migration; moved it to ${s} and put ${t} in its place`}}l(W,"claim");function E(t,e,n,o=!1){if(d(t)&&t!==e){if(d(e)){const r=W(t,e,n,o);if(r.line&&n.resolutions.push(r.line),r.action==="merge"){for(const i of h(t))E(c(t,i),c(e,i),n);g(t);return}if(r.action==="drop-source"){L(t,{recursive:!0}),n.changes.push(`${t} (identical to ${e}; removed)`);return}r.action==="remove-destination"?(L(e,{recursive:!0}),n.changes.push(`${e} (removed)`)):I(e,"replaced",n,S(n.map.old.root,e))}w(m(e),{recursive:!0}),b(t,e),n.moved++,n.changes.push(`${t} -> ${e}`)}}l(E,"movePath");function z(t,e,n,o=!1){if(!d(t)||t===e||!d(e))return;const r=W(t,e,n,o);if(r.line&&n.resolutions.push(r.line),r.action==="merge")for(const i of h(t))z(c(t,i),c(e,i),n)}l(z,"previewMove");function B(t){const{map:e}=t,n=c(e.old.canvas,"nodes"),o=c(e.old.canvas,"canvas.db"),r=c(e.old.root,"runtime","canvas.db"),i=d(o)?o:d(r)&&f(r).size>0?r:"";if(!i){if(d(n))throw new Error("old node records exist without canvas.db");return null}const s=new _(i,{readOnly:t.dryRun||i!==o});try{if(i===o&&!t.dryRun&&s.prepare("PRAGMA wal_checkpoint(TRUNCATE)").get().busy!==0)throw new Error("old canvas WAL is busy; stop every database user before migrating");return new Map(s.prepare("SELECT node_id, grantee FROM nodes").all().map(a=>{const u=a.grantee==="user:local"?"terminal":a.grantee.startsWith("app:")?a.grantee.slice(4):"";if(!u)throw new Error(`node ${a.node_id} has no app assignment`);return[a.node_id,u]}))}finally{s.close()}}l(B,"canvasNodeApps");function J(t,e){const n=c(t.map.old.canvas,"nodes");if(!d(n))return[];const o=[];for(const r of h(n)){const i=c(n,r),s=f(i).isDirectory();e.has(r)&&s||o.push({path:i,remove:!s&&he(r)})}return o}l(J,"orphanNodeEntries");function G(t,e){return t.remove?`node-leftover: deleted ${t.path} (not a node record)`:`node-leftover: ${t.path} has no node row; moved to ${F(e,"nodes",T(t.path))}`}l(G,"orphanLine");function ye(t){const{map:e}=t,n=c(e.old.canvas,"nodes"),o=B(t);if(o){for(const[r,i]of o)e.node(r,i);if(d(n)){for(const r of J(t,o)){if(t.resolutions.push(G(r,t)),r.remove){A(r.path),t.changes.push(`${r.path} (removed)`);continue}const i=I(r.path,"nodes",t),s=c(e.old.targetRoot,S(e.old.root,i));for(const a of new Set([e.old.storedCanvas,e.old.storedScope,c(e.old.storedScope,"canvas")]))e.add(c(a,"nodes",T(r.path)),s)}for(const[r,i]of o){const s=c(n,r);if(d(s)){for(const a of["context","reports","attachments"])E(c(s,a),c(e.old.root,"apps",i,"nodes",r,a),t);for(const a of h(s))["worktree","view.sock","broker-launch.json","system-prompt.md","situational-context.md","injected-docs.json"].includes(a)||E(c(s,a),c(e.old.root,"runtime","nodes",r,a),t);h(s).length===0&&g(s)}}h(n).length===0&&g(n)}}}l(ye,"records");const H=["auth.json","anthropic-credentials.json","openai-codex-credentials.json","openai-credentials.json","crouter-operations.db"];function ve(t,e,n){if(!d(t))return;if(!f(t).isFile())throw new Error(`credential source is not a regular file: ${t}`);const o=y(t);if(d(e)){(!f(e).isFile()||!y(e).equals(o))&&n.resolutions.push(`credential: kept the existing ${e}; the older ${t} differs and stays where it is`);return}w(m(e),{recursive:!0});const r=c(m(e),`.${N()}.tmp`);try{const i=k(r,"wx",384);try{for(let s=0;s<o.length;)s+=q(i,o,s,o.length-s);R(i)}finally{$(i)}if(b(r,e),!y(e).equals(o))throw new Error(`credential verification failed: ${e}`)}finally{d(r)&&A(r)}n.moved++,n.changes.push(`${t} -> ${e} (source retained)`)}l(ve,"copyCredential");function K(t){if(!d(t))return;const e=new _(t);try{if(e.prepare("PRAGMA wal_checkpoint(TRUNCATE)").get().busy!==0)throw new Error(`SQLite WAL is busy: ${t}`)}finally{e.close()}}l(K,"checkpointDb");function Q(t,e){if(f(t).size!==f(e).size)return!1;const n=k(t,"r"),o=k(e,"r"),r=Buffer.allocUnsafe(64*1024),i=Buffer.allocUnsafe(r.length);try{for(;;){const s=U(n,r,0,r.length,null);if(s===0)return!0;let a=0;for(;a<s;){const u=U(o,i,a,s-a,null);if(u===0)return!1;a+=u}if(!r.subarray(0,s).equals(i.subarray(0,s)))return!1}}finally{$(n),$(o)}}l(Q,"sameFile");function P(t,e,n=!1){let o;try{o=f(e)}catch(u){if(u.code==="ENOENT")return!1;throw u}const r=f(t);if(r.isSymbolicLink())return o.isSymbolicLink()&&D(t)===D(e);if(r.isFile())return o.isFile()&&Q(t,e);if(!r.isDirectory()||!o.isDirectory())return!1;const i=h(t).filter(u=>!n||u!==".git"),s=h(e).filter(u=>!n||u!==".git"),a=new Set(s);return i.length===s.length&&i.every(u=>a.has(u)&&P(c(t,u),c(e,u),n))}l(P,"sameTree");function we(t,e,n){if(!d(t))return;if(!f(t).isDirectory())throw new Error(`hosted work is not a directory: ${t}`);if(d(e)){if(P(t,e,!0))return;if(!f(e).isDirectory()||h(e).length!==0){n.resolutions.push(`hosted-work: kept the existing ${e}; the old ${t} differs and stays where it is`);return}g(e)}w(m(e),{recursive:!0});const o=c(m(e),`.work-${N()}.migrating`);try{if(ee(t,o,{recursive:!0,verbatimSymlinks:!0,preserveTimestamps:!0}),!P(t,o))throw new Error(`hosted work copy verification failed: ${t}`);b(o,e),n.moved++,n.changes.push(`${t} -> ${e} (source retained)`)}finally{d(o)&&L(o,{recursive:!0,force:!0})}}l(we,"copyHostedWork");function V(t){const{old:e}=t.map,n=c(e.root,"runtime"),o=c(n,"scope"),r=[];for(const i of["canvas.db","canvas.db-wal","canvas.db-shm","on-disk-migrations.json","requests","reviews","plugins","marketplaces","crtrd.log","crtrd.err","cache"])r.push([c(e.canvas,i),c(n,i)]);for(const i of["prompts","personas","config.json","profile-defaults.json","operations.db","operations.db-wal","operations.db-shm"])r.push([c(e.scope,i),c(o,i)]);for(const i of["support","prompt-reviews","plugins","marketplaces"])r.push([c(e.scope,i),c(n,i)]);return r.push([c(e.canvas,"secrets"),c(n,"secrets")]),r}l(V,"topLevelMoves");function be(t){const{old:e}=t.map,n=c(e.root,"runtime");K(c(e.scope,"operations.db"));for(const[o,r]of V(t)){const i=ge(o);E(o,r,t,!0),$e(i,o,r,t)}K(c(e.agent,"crouter-operations.db"));for(const o of H)ve(c(e.agent,o),c(n,"secrets",o),t);process.platform==="linux"&&we(e.work,c(e.root,"user","files"),t)}l(be,"topLevel");function ge(t){if(!d(t)||!f(t).isDirectory())return[];const e=[];for(const n of h(t)){const o=c(t,n);if(!f(o).isSymbolicLink())continue;const r=D(o);re(r)||e.push({name:n,target:r})}return e}l(ge,"relativeLinks");function $e(t,e,n,o){if(!(t.length===0||!d(n)))for(const{name:r,target:i}of t){const s=c(n,r);if(!ke(s))continue;const a=o.map.mapPath(oe(e,i)),u=S(n,a);u!==i&&(A(s),Z(u,s),o.changes.push(`${s}: link ${i} -> ${u}`))}}l($e,"retargetLinks");function ke(t){try{return f(t),!0}catch{return!1}}l(ke,"lstatExists");function Se(t){if(process.platform!=="linux")return;if(process.getuid?.()!==0)throw new Error("Linux layout migration requires root for the ownership pass");const e=y("/etc/passwd","utf8").split(`
2
- `).find(s=>s.startsWith("crtrd:")),n=y("/etc/group","utf8").split(`
3
- `).find(s=>s.startsWith("crtr-data:"));if(!e||!n)throw new Error("layout migration requires crtrd user and crtr-data group");const o=Number(e.split(":")[2]),r=Number(n.split(":")[2]);if(!Number.isInteger(o)||!Number.isInteger(r))throw new Error("invalid migration user or group");const i=l((s,a)=>{if(!d(s))return;const u=f(s);if(u.isSymbolicLink()){te(s,o,a?r:u.gid);return}if(M(s,o,a?r:u.gid),u.isDirectory()){j(s,a?1528:448);for(const v of h(s))i(c(s,v),a)}else a&&j(s,u.mode&73?504:432)},"walk");i(c(t,"runtime"),!1),i(c(t,"user"),!0),i(c(t,"apps"),!0)}l(Se,"ownership");function Ee(t){return new Promise(e=>{const n=ne({socketPath:t,path:"/healthz",method:"GET",timeout:1e3},o=>{o.resume(),e(!0)});n.on("timeout",()=>n.destroy(new Error("daemon probe timed out"))),n.on("error",o=>{o.code==="ENOENT"||o.code==="ECONNREFUSED"?e(!1):e(!0)}),n.end()})}l(Ee,"daemonAnswers");async function Re(t){const e=se(),n=e.old,o=c(n.root,"runtime","layout");if(d(o)){if(y(o,"utf8").trim()!==C)throw new Error("unsupported runtime layout version");return null}const r=new Set([c(n.canvas,"crtrd.sock"),c(n.scope,"crtrd.sock"),process.platform==="linux"?"/run/crtr/api.sock":c(n.root,"runtime","api.sock")]);for(const p of r)if(await Ee(p))throw new O({error:"daemon_running",message:`cannot migrate while a daemon answers on ${p}`,next:"Stop the daemon that owns this volume, then run `crtr sys migrate` again."});if(process.platform==="linux"&&!d(n.agent))throw new O({error:"credential_volume_missing",message:`the old credential volume is not mounted at ${n.agent}`,next:`Mount the old provider-auth volume at ${n.agent}, then run \`crtr sys migrate\` again.`});const i=H.map(p=>c(n.agent,p)).filter(p=>!d(p)),s={map:e,moved:0,changes:[],resolutions:[],bodyPaths:[],absentCredentials:i,dryRun:t};if(t){const p=B(s);if(p)for(const x of J(s,p))s.resolutions.push(G(x,s));for(const[x,Y]of V(s))z(x,Y,s,!0);return ae(s),s}ue(s);const a=[ye,de,be,le,ce,ie,fe];for(const p of a)p(s);n.canvas!==n.root&&d(n.canvas)&&h(n.canvas).length===0&&g(n.canvas),Se(n.root),w(m(o),{recursive:!0});const u=`${o}.${N()}.tmp`,v=k(u,"wx",384);try{if(process.platform==="linux"){const p=f(m(o));M(u,p.uid,p.gid)}q(v,`${C}
4
- `),R(v)}finally{$(v)}return b(u,o),s}l(Re,"migrateLayout");export{Ce as OldToNewPathMap,W as claim,Ee as daemonAnswers,he as isJunkFile,Re as migrateLayout,E as movePath,Oe as oldToNewPathMap,z as previewMove,Q as sameFile,I as setAside,F as setAsidePath};
1
+ var Y=Object.defineProperty;var l=(t,e)=>Y(t,"name",{value:e,configurable:!0});import{DatabaseSync as M}from"node:sqlite";import{randomUUID as D}from"node:crypto";import{symlinkSync as Z,linkSync as ee,chmodSync as _,chownSync as j,closeSync as g,cpSync as te,existsSync as d,fsyncSync as R,lchownSync as ne,lstatSync as f,mkdirSync as v,openSync as S,readFileSync as w,readlinkSync as L,readSync as U,readdirSync as h,renameSync as k,rmSync as T,rmdirSync as b,unlinkSync as $,writeSync as q}from"node:fs";import{request as re}from"node:http";import{basename as A,dirname as y,isAbsolute as oe,join as c,relative as E,resolve as se}from"node:path";import{LAYOUT_VERSION as ie,assertSupportedLayoutMarker as C}from"../layout.js";import{InputError as I}from"../io.js";import{syncDirectory as ce}from"../fs-utils.js";import{oldToNewPathMap as ae}from"./paths.js";import{conversations as ue,memoryAndProfiles as le,previewStores as de,profileFolders as fe,repairMovedRepositories as pe,worktrees as he}from"./stores.js";import{storedPaths as ye}from"./stored-paths.js";import{OldToNewPathMap as ze,oldToNewPathMap as Be}from"./paths.js";const me=new Set([".DS_Store","Thumbs.db","desktop.ini"]);function ve(t){return me.has(t)||t.startsWith("._")}l(ve,"isJunkFile");function F(t,e,n){const r=c(t.map.old.root,"runtime","set-aside",e);let o=c(r,n);for(let i=2;d(o);i++)o=c(r,`${n}.${i}`);return o}l(F,"setAsidePath");function O(t,e,n,r=A(t)){const o=F(n,e,r);return v(y(o),{recursive:!0}),k(t,o),n.moved++,n.changes.push(`${t} -> ${o} (set aside)`),o}l(O,"setAside");function we(t){const e=f(t);return e.isDirectory()?h(t).length===0:e.isFile()&&e.size===0}l(we,"empty");function W(t,e,n,r=!1){const o=f(t),i=f(e);if(o.isDirectory()&&i.isDirectory()&&!r)return{action:"merge"};if(o.isFile()&&i.isFile()&&Q(t,e))return{action:"drop-source"};if(we(e))return{action:"remove-destination",line:`stray-destination: removed empty ${i.isDirectory()?"folder":"file"} ${e} that predates the migration`};const s=F(n,"replaced",E(n.map.old.root,e));return{action:"set-aside-destination",line:`stray-destination: ${e} predates the migration; moved it to ${s} and put ${t} in its place`}}l(W,"claim");function x(t,e,n,r=!1){if(d(t)&&t!==e){if(d(e)){const o=W(t,e,n,r);if(o.line&&n.resolutions.push(o.line),o.action==="merge"){for(const i of h(t))x(c(t,i),c(e,i),n);b(t);return}if(o.action==="drop-source"){T(t,{recursive:!0}),n.changes.push(`${t} (identical to ${e}; removed)`);return}o.action==="remove-destination"?(T(e,{recursive:!0}),n.changes.push(`${e} (removed)`)):O(e,"replaced",n,E(n.map.old.root,e))}v(y(e),{recursive:!0}),k(t,e),n.moved++,n.changes.push(`${t} -> ${e}`)}}l(x,"movePath");function z(t,e,n,r=!1){if(!d(t)||t===e||!d(e))return;const o=W(t,e,n,r);if(o.line&&n.resolutions.push(o.line),o.action==="merge")for(const i of h(t))z(c(t,i),c(e,i),n)}l(z,"previewMove");function B(t){const{map:e}=t,n=c(e.old.canvas,"nodes"),r=c(e.old.canvas,"canvas.db"),o=c(e.old.root,"runtime","canvas.db"),i=d(r)?r:d(o)&&f(o).size>0?o:"";if(!i){if(d(n))throw new Error("old node records exist without canvas.db");return null}const s=new M(i,{readOnly:t.dryRun||i!==r});try{if(i===r&&!t.dryRun&&s.prepare("PRAGMA wal_checkpoint(TRUNCATE)").get().busy!==0)throw new Error("old canvas WAL is busy; stop every database user before migrating");return new Map(s.prepare("SELECT node_id, grantee FROM nodes").all().map(a=>{const u=a.grantee==="user:local"?"terminal":a.grantee.startsWith("app:")?a.grantee.slice(4):"";if(!u)throw new Error(`node ${a.node_id} has no app assignment`);return[a.node_id,u]}))}finally{s.close()}}l(B,"canvasNodeApps");function J(t,e){const n=c(t.map.old.canvas,"nodes");if(!d(n))return[];const r=[];for(const o of h(n)){const i=c(n,o),s=f(i).isDirectory();e.has(o)&&s||r.push({path:i,remove:!s&&ve(o)})}return r}l(J,"orphanNodeEntries");function G(t,e){return t.remove?`node-leftover: deleted ${t.path} (not a node record)`:`node-leftover: ${t.path} has no node row; moved to ${F(e,"nodes",A(t.path))}`}l(G,"orphanLine");function be(t){const{map:e}=t,n=c(e.old.canvas,"nodes"),r=B(t);if(r){for(const[o,i]of r)e.node(o,i);if(d(n)){for(const o of J(t,r)){if(t.resolutions.push(G(o,t)),o.remove){$(o.path),t.changes.push(`${o.path} (removed)`);continue}const i=O(o.path,"nodes",t),s=c(e.old.targetRoot,E(e.old.root,i));for(const a of new Set([e.old.storedCanvas,e.old.storedScope,c(e.old.storedScope,"canvas")]))e.add(c(a,"nodes",A(o.path)),s)}for(const[o,i]of r){const s=c(n,o);if(d(s)){for(const a of["context","reports","attachments"])x(c(s,a),c(e.old.root,"apps",i,"nodes",o,a),t);for(const a of h(s))["worktree","view.sock","broker-launch.json","system-prompt.md","situational-context.md","injected-docs.json"].includes(a)||x(c(s,a),c(e.old.root,"runtime","nodes",o,a),t);h(s).length===0&&b(s)}}h(n).length===0&&b(n)}}}l(be,"records");const H=["auth.json","anthropic-credentials.json","openai-codex-credentials.json","openai-credentials.json","crouter-operations.db"];function ge(t,e,n){if(!d(t))return;if(!f(t).isFile())throw new Error(`credential source is not a regular file: ${t}`);const r=w(t);if(d(e)){(!f(e).isFile()||!w(e).equals(r))&&n.resolutions.push(`credential: kept the existing ${e}; the older ${t} differs and stays where it is`);return}v(y(e),{recursive:!0});const o=c(y(e),`.${D()}.tmp`);try{const i=S(o,"wx",384);try{for(let s=0;s<r.length;)s+=q(i,r,s,r.length-s);R(i)}finally{g(i)}if(k(o,e),!w(e).equals(r))throw new Error(`credential verification failed: ${e}`)}finally{d(o)&&$(o)}n.moved++,n.changes.push(`${t} -> ${e} (source retained)`)}l(ge,"copyCredential");function K(t){if(!d(t))return;const e=new M(t);try{if(e.prepare("PRAGMA wal_checkpoint(TRUNCATE)").get().busy!==0)throw new Error(`SQLite WAL is busy: ${t}`)}finally{e.close()}}l(K,"checkpointDb");function Q(t,e){if(f(t).size!==f(e).size)return!1;const n=S(t,"r"),r=S(e,"r"),o=Buffer.allocUnsafe(64*1024),i=Buffer.allocUnsafe(o.length);try{for(;;){const s=U(n,o,0,o.length,null);if(s===0)return!0;let a=0;for(;a<s;){const u=U(r,i,a,s-a,null);if(u===0)return!1;a+=u}if(!o.subarray(0,s).equals(i.subarray(0,s)))return!1}}finally{g(n),g(r)}}l(Q,"sameFile");function P(t,e,n=!1){let r;try{r=f(e)}catch(u){if(u.code==="ENOENT")return!1;throw u}const o=f(t);if(o.isSymbolicLink())return r.isSymbolicLink()&&L(t)===L(e);if(o.isFile())return r.isFile()&&Q(t,e);if(!o.isDirectory()||!r.isDirectory())return!1;const i=h(t).filter(u=>!n||u!==".git"),s=h(e).filter(u=>!n||u!==".git"),a=new Set(s);return i.length===s.length&&i.every(u=>a.has(u)&&P(c(t,u),c(e,u),n))}l(P,"sameTree");function Se(t,e,n){if(!d(t))return;if(!f(t).isDirectory())throw new Error(`hosted work is not a directory: ${t}`);if(d(e)){if(P(t,e,!0))return;if(!f(e).isDirectory()||h(e).length!==0){n.resolutions.push(`hosted-work: kept the existing ${e}; the old ${t} differs and stays where it is`);return}b(e)}v(y(e),{recursive:!0});const r=c(y(e),`.work-${D()}.migrating`);try{if(te(t,r,{recursive:!0,verbatimSymlinks:!0,preserveTimestamps:!0}),!P(t,r))throw new Error(`hosted work copy verification failed: ${t}`);k(r,e),n.moved++,n.changes.push(`${t} -> ${e} (source retained)`)}finally{d(r)&&T(r,{recursive:!0,force:!0})}}l(Se,"copyHostedWork");function V(t){const{old:e}=t.map,n=c(e.root,"runtime"),r=c(n,"scope"),o=[];for(const i of["canvas.db","canvas.db-wal","canvas.db-shm","on-disk-migrations.json","requests","reviews","plugins","marketplaces","crtrd.log","crtrd.err","cache"])o.push([c(e.canvas,i),c(n,i)]);for(const i of["prompts","personas","config.json","profile-defaults.json","operations.db","operations.db-wal","operations.db-shm"])o.push([c(e.scope,i),c(r,i)]);for(const i of["support","prompt-reviews","plugins","marketplaces"])o.push([c(e.scope,i),c(n,i)]);return o.push([c(e.canvas,"secrets"),c(n,"secrets")]),o}l(V,"topLevelMoves");function ke(t){const{old:e}=t.map,n=c(e.root,"runtime");K(c(e.scope,"operations.db"));for(const[r,o]of V(t)){const i=$e(r);x(r,o,t,!0),Ee(i,r,o,t)}K(c(e.agent,"crouter-operations.db"));for(const r of H)ge(c(e.agent,r),c(n,"secrets",r),t);process.platform==="linux"&&Se(e.work,c(e.root,"user","files"),t)}l(ke,"topLevel");function $e(t){if(!d(t)||!f(t).isDirectory())return[];const e=[];for(const n of h(t)){const r=c(t,n);if(!f(r).isSymbolicLink())continue;const o=L(r);oe(o)||e.push({name:n,target:o})}return e}l($e,"relativeLinks");function Ee(t,e,n,r){if(!(t.length===0||!d(n)))for(const{name:o,target:i}of t){const s=c(n,o);if(!xe(s))continue;const a=r.map.mapPath(se(e,i)),u=E(n,a);u!==i&&($(s),Z(u,s),r.changes.push(`${s}: link ${i} -> ${u}`))}}l(Ee,"retargetLinks");function xe(t){try{return f(t),!0}catch{return!1}}l(xe,"lstatExists");function Ne(t){if(process.platform!=="linux")return;if(process.getuid?.()!==0)throw new Error("Linux layout migration requires root for the ownership pass");const e=w("/etc/passwd","utf8").split(`
2
+ `).find(s=>s.startsWith("crtrd:")),n=w("/etc/group","utf8").split(`
3
+ `).find(s=>s.startsWith("crtr-data:"));if(!e||!n)throw new Error("layout migration requires crtrd user and crtr-data group");const r=Number(e.split(":")[2]),o=Number(n.split(":")[2]);if(!Number.isInteger(r)||!Number.isInteger(o))throw new Error("invalid migration user or group");const i=l((s,a)=>{if(!d(s))return;const u=f(s);if(u.isSymbolicLink()){ne(s,r,a?o:u.gid);return}if(j(s,r,a?o:u.gid),u.isDirectory()){_(s,a?1528:448);for(const m of h(s))i(c(s,m),a)}else a&&_(s,u.mode&73?504:432)},"walk");i(c(t,"runtime"),!1),i(c(t,"user"),!0),i(c(t,"apps"),!0)}l(Ne,"ownership");function De(t){return new Promise(e=>{const n=re({socketPath:t,path:"/healthz",method:"GET",timeout:1e3},r=>{r.resume(),e(!0)});n.on("timeout",()=>n.destroy(new Error("daemon probe timed out"))),n.on("error",r=>{r.code==="ENOENT"||r.code==="ECONNREFUSED"?e(!1):e(!0)}),n.end()})}l(De,"daemonAnswers");async function Ie(t){const e=ae(),n=e.old,r=c(n.root,"runtime","layout");if(d(r))return C(r),null;const o=new Set([c(n.canvas,"crtrd.sock"),c(n.scope,"crtrd.sock"),process.platform==="linux"?"/run/crtr/api.sock":c(n.root,"runtime","api.sock")]);for(const p of o)if(await De(p))throw new I({error:"daemon_running",message:`cannot migrate while a daemon answers on ${p}`,next:"Stop the daemon that owns this volume, then run `crtr sys migrate` again."});if(process.platform==="linux"&&!d(n.agent))throw new I({error:"credential_volume_missing",message:`the old credential volume is not mounted at ${n.agent}`,next:`Mount the old provider-auth volume at ${n.agent}, then run \`crtr sys migrate\` again.`});const i=H.map(p=>c(n.agent,p)).filter(p=>!d(p)),s={map:e,moved:0,changes:[],resolutions:[],bodyPaths:[],absentCredentials:i,dryRun:t};if(t){const p=B(s);if(p)for(const N of J(s,p))s.resolutions.push(G(N,s));for(const[N,X]of V(s))z(N,X,s,!0);return de(s),s}fe(s);const a=[be,he,ke,pe,le,ue,ye];for(const p of a)p(s);n.canvas!==n.root&&d(n.canvas)&&h(n.canvas).length===0&&b(n.canvas),Ne(n.root),v(y(r),{recursive:!0});const u=`${r}.${D()}.tmp`,m=S(u,"wx",384);try{try{if(process.platform==="linux"){const p=f(y(r));j(u,p.uid,p.gid)}q(m,`${ie}
4
+ `),R(m)}finally{g(m)}try{ee(u,r)}catch(p){if(p.code==="EEXIST")C(r);else throw p}}finally{$(u)}return ce(y(r)),s}l(Ie,"migrateLayout");export{ze as OldToNewPathMap,W as claim,De as daemonAnswers,ve as isJunkFile,Ie as migrateLayout,x as movePath,Be as oldToNewPathMap,z as previewMove,Q as sameFile,O as setAside,F as setAsidePath};
@@ -31,6 +31,14 @@ export declare function cronFolder(app: string, cronId: string): string;
31
31
  * and the terminal wherever a sandbox exists, keeps its own app home. Callers
32
32
  * keep `PI_CODING_AGENT_DIR` in the app home either way. */
33
33
  export declare function appProcessHome(app: string): string;
34
+ /** The marker's byte length and an escaped, truncated preview, safe to print: a
35
+ * corrupt marker (empty, truncated, NUL-filled by a lost write) is otherwise
36
+ * indistinguishable from an intentional newer layout in a crash log. */
37
+ export declare function describeLayoutMarker(bytes: Uint8Array): string;
38
+ /** Refuse any marker other than the supported version. A non-matching marker is
39
+ * never rewritten: an empty or damaged one means the volume lost a write, and
40
+ * stamping the current version over it would assert a layout nobody verified. */
41
+ export declare function assertSupportedLayoutMarker(path: string): void;
34
42
  /** A missing version on an occupied volume is never initialized by daemon startup. */
35
43
  export declare function layoutNeedsMigration(): boolean;
36
44
  export declare function initializeEmptyLayout(): void;
@@ -1,2 +1,2 @@
1
- var w=Object.defineProperty;var t=(r,o)=>w(r,"name",{value:o,configurable:!0});import{existsSync as i,mkdirSync as m,readFileSync as E,statSync as R,writeFileSync as _}from"node:fs";import{homedir as S,userInfo as T}from"node:os";import{basename as l,dirname as A,isAbsolute as O,join as e,resolve as f}from"node:path";import{envHomeOverride as $}from"../shared/env.js";import{readConfig as b}from"./config.js";const d="1";function s(){const r=f($()||(process.platform==="linux"?"/":e(S(),".crouter")));if(g()&&r===z())throw new Error(`refusing to use the account's real crouter root ${r} from a node --test process; point CRTR_HOME (not only HOME) at a temporary directory`);return r}t(s,"layoutRoot");function g(){return process.env.NODE_TEST_CONTEXT!==void 0||process.execArgv.includes("--test")}t(g,"runningUnderNodeTest");let c;function z(){if(c===void 0)try{c=f(T().homedir,".crouter")}catch{c=null}return c}t(z,"accountLayoutRoot");function u(){return e(s(),"runtime")}t(u,"runtimeRoot");function h(){return e(s(),"user")}t(h,"userRoot");function x(){return e(s(),"apps")}t(x,"appsRoot");function y(){return e(u(),"secrets")}t(y,"secretsDir");function D(r,o){if(!/^[a-z0-9]+(?:-[a-z0-9]+)*-[0-9a-f]{8}$/.test(o))throw new Error(`invalid profile id: ${o}`);const n=p(r);return{memory:e(n.memory,"profiles",o),env:e(y(),"env",l(n.root),o),home:n.home}}t(D,"profileSpace");function v(){return process.platform==="linux"?"/run/crtr":u()}t(v,"socketDir");function a(){return e(u(),"layout")}t(a,"layoutVersionFile");function F(){const r=b("user").paths?.user_files??(process.platform==="linux"?e(h(),"files"):A(s()));if(!O(r))throw new Error("paths.user_files must be absolute");return f(r)}t(F,"userFilesRoot");function p(r){const o=r.startsWith("app:")?r.slice(4):r;if(!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(o)||o==="."||o==="..")throw new Error(`invalid app id: ${r}`);const n=e(x(),o);return{root:n,memory:e(n,"memory"),conversations:e(n,"conversations"),nodes:e(n,"nodes"),files:e(n,"files"),home:e(n,"home"),cache:e(n,"cache")}}t(p,"appSpace");function M(r,o){if(!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(o))throw new Error(`invalid cron id: ${o}`);return e(p(r).root,"crons",o)}t(M,"cronFolder");function V(r){const o=p(r);return process.platform==="linux"?o.home:l(o.root)==="terminal"?F():o.home}t(V,"appProcessHome");function C(){if(process.env.CRTR_LAYOUT_REFUSE==="1")return!0;if(i(a())){if(E(a(),"utf8").trim()!==d)throw new Error("unsupported runtime layout version");return!1}const r=s(),o=process.platform==="linux"&&r==="/"&&i("/volume")?"/volume":r;return i(e(o,"canvas"))||i(e(o,"canvas.db"))?!0:i(e(u(),"canvas.db"))}t(C,"layoutNeedsMigration");function Y(){if(C())throw new Error("layout_migration_required");for(const r of[u(),h(),x(),y()])m(r,{recursive:!0,mode:448});if(m(v(),{recursive:!0,mode:493}),process.platform==="linux"&&(R(v()).mode&511)!==493)throw new Error("launcher socket directory must be mode 0755");i(a())||_(a(),`${d}
2
- `,{flag:"wx",mode:384})}t(Y,"initializeEmptyLayout");export{d as LAYOUT_VERSION,V as appProcessHome,p as appSpace,x as appsRoot,M as cronFolder,Y as initializeEmptyLayout,C as layoutNeedsMigration,s as layoutRoot,a as layoutVersionFile,D as profileSpace,u as runtimeRoot,y as secretsDir,v as socketDir,F as userFilesRoot,h as userRoot};
1
+ var $=Object.defineProperty;var n=(r,o)=>$(r,"name",{value:o,configurable:!0});import{existsSync as i,mkdirSync as d,readFileSync as S,statSync as R}from"node:fs";import{homedir as _,userInfo as g}from"node:os";import{basename as x,dirname as f,isAbsolute as A,join as t,resolve as p}from"node:path";import{envHomeOverride as T}from"../shared/env.js";import{readConfig as O}from"./config.js";import{publishFileExclusive as z,syncDirectory as h}from"./fs-utils.js";const m="1";function s(){const r=p(T()||(process.platform==="linux"?"/":t(_(),".crouter")));if(C()&&r===L())throw new Error(`refusing to use the account's real crouter root ${r} from a node --test process; point CRTR_HOME (not only HOME) at a temporary directory`);return r}n(s,"layoutRoot");function C(){return process.env.NODE_TEST_CONTEXT!==void 0||process.execArgv.includes("--test")}n(C,"runningUnderNodeTest");let c;function L(){if(c===void 0)try{c=p(g().homedir,".crouter")}catch{c=null}return c}n(L,"accountLayoutRoot");function u(){return t(s(),"runtime")}n(u,"runtimeRoot");function v(){return t(s(),"user")}n(v,"userRoot");function y(){return t(s(),"apps")}n(y,"appsRoot");function w(){return t(u(),"secrets")}n(w,"secretsDir");function Y(r,o){if(!/^[a-z0-9]+(?:-[a-z0-9]+)*-[0-9a-f]{8}$/.test(o))throw new Error(`invalid profile id: ${o}`);const e=l(r);return{memory:t(e.memory,"profiles",o),env:t(w(),"env",x(e.root),o),home:e.home}}n(Y,"profileSpace");function E(){return process.platform==="linux"?"/run/crtr":u()}n(E,"socketDir");function a(){return t(u(),"layout")}n(a,"layoutVersionFile");function k(){const r=O("user").paths?.user_files??(process.platform==="linux"?t(v(),"files"):f(s()));if(!A(r))throw new Error("paths.user_files must be absolute");return p(r)}n(k,"userFilesRoot");function l(r){const o=r.startsWith("app:")?r.slice(4):r;if(!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(o)||o==="."||o==="..")throw new Error(`invalid app id: ${r}`);const e=t(y(),o);return{root:e,memory:t(e,"memory"),conversations:t(e,"conversations"),nodes:t(e,"nodes"),files:t(e,"files"),home:t(e,"home"),cache:t(e,"cache")}}n(l,"appSpace");function q(r,o){if(!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(o))throw new Error(`invalid cron id: ${o}`);return t(l(r).root,"crons",o)}n(q,"cronFolder");function P(r){const o=l(r);return process.platform==="linux"?o.home:x(o.root)==="terminal"?k():o.home}n(P,"appProcessHome");function F(r){const o=Array.from(r.subarray(0,32),e=>e===92?"\\\\":e===34?'\\"':e===10?"\\n":e===13?"\\r":e===9?"\\t":e>=32&&e<127?String.fromCharCode(e):`\\x${e.toString(16).padStart(2,"0")}`).join("");return`${r.length} bytes, preview "${o}"${r.length>32?"\u2026":""}`}n(F,"describeLayoutMarker");function M(r){const o=S(r);if(o.toString("utf8").trim()!==m)throw new Error(`unsupported runtime layout version: ${r} holds ${F(o)}; expected "${m}"`)}n(M,"assertSupportedLayoutMarker");function N(){if(process.env.CRTR_LAYOUT_REFUSE==="1")return!0;if(i(a()))return M(a()),!1;const r=s(),o=process.platform==="linux"&&r==="/"&&i("/volume")?"/volume":r;return i(t(o,"canvas"))||i(t(o,"canvas.db"))?!0:i(t(u(),"canvas.db"))}n(N,"layoutNeedsMigration");function W(){if(N())throw new Error("layout_migration_required");for(const r of[u(),v(),y(),w()]){const o=d(r,{recursive:!0,mode:448});if(o!==void 0){for(let e=r;h(e),e!==o;e=f(e));h(f(o))}}if(d(E(),{recursive:!0,mode:493}),process.platform==="linux"&&(R(E()).mode&511)!==493)throw new Error("launcher socket directory must be mode 0755");i(a())||z(a(),`${m}
2
+ `,384)}n(W,"initializeEmptyLayout");export{m as LAYOUT_VERSION,P as appProcessHome,l as appSpace,y as appsRoot,M as assertSupportedLayoutMarker,q as cronFolder,F as describeLayoutMarker,W as initializeEmptyLayout,N as layoutNeedsMigration,s as layoutRoot,a as layoutVersionFile,Y as profileSpace,u as runtimeRoot,w as secretsDir,E as socketDir,k as userFilesRoot,v as userRoot};
@@ -1 +1 @@
1
- var T=Object.defineProperty;var r=(c,t)=>T(c,"name",{value:t,configurable:!0});import{chmodSync as f,existsSync as a,fsyncSync as u,mkdirSync as F,openSync as m,readFileSync as y,renameSync as L,writeFileSync as S,closeSync as w}from"node:fs";import{dirname as _}from"node:path";import x from"proper-lockfile";const b={encoding:"utf-8",mode:384},E=3e4,O=5e3;class K{static{r(this,"LockedJsonFile")}path;durable;initialContent;constructor(t,e=!1,i="{}"){this.path=t,this.durable=e,this.initialContent=i}readUnlocked(){return a(this.path)?y(this.path,"utf-8"):void 0}ensureFile(){const t=_(this.path);a(t)||F(t,{recursive:!0,mode:448}),a(this.path)||(S(this.path,this.initialContent,b),f(this.path,384))}async withLockAsync(t){this.ensureFile();let e,i;const s=r(()=>{if(i)throw i},"throwIfCompromised");try{e=await x.lock(this.path,{realpath:!1,retries:{retries:10,factor:2,minTimeout:100,maxTimeout:1e4,randomize:!0},stale:E,update:O,onCompromised:r(h=>{i=h},"onCompromised")}),s();const k=a(this.path)?y(this.path,"utf-8"):void 0;let l=!1;const d=r(h=>{if(l)throw new Error(`locked file was published twice: ${this.path}`);s();const o=`${this.path}.${process.pid}.${Date.now()}.tmp`;if(S(o,h,b),f(o,384),this.durable){const n=m(o,"r");try{u(n)}finally{w(n)}}if(L(o,this.path),this.durable){const n=m(_(this.path),"r");try{u(n)}finally{w(n)}}l=!0},"publish"),{result:C,next:p}=await t(k,s,d);return s(),p!==void 0&&d(p),s(),C}finally{if(e)try{await e()}catch{}}}}export{K as LockedJsonFile};
1
+ var k=Object.defineProperty;var o=(c,t)=>k(c,"name",{value:t,configurable:!0});import{chmodSync as f,existsSync as a,fsyncSync as u,mkdirSync as C,openSync as m,readFileSync as y,renameSync as F,writeFileSync as T,closeSync as S}from"node:fs";import{dirname as w}from"node:path";import x from"proper-lockfile";import{publishFileExclusive as E}from"./fs-utils.js";const L={encoding:"utf-8",mode:384},O=3e4,$=5e3;class M{static{o(this,"LockedJsonFile")}path;durable;initialContent;constructor(t,i=!1,e="{}"){this.path=t,this.durable=i,this.initialContent=e}readUnlocked(){return a(this.path)?y(this.path,"utf-8"):void 0}ensureFile(){const t=w(this.path);a(t)||C(t,{recursive:!0,mode:448}),a(this.path)||(E(this.path,this.initialContent,384),f(this.path,384))}async withLockAsync(t){this.ensureFile();let i,e;const s=o(()=>{if(e)throw e},"throwIfCompromised");try{i=await x.lock(this.path,{realpath:!1,retries:{retries:10,factor:2,minTimeout:100,maxTimeout:1e4,randomize:!0},stale:O,update:$,onCompromised:o(h=>{e=h},"onCompromised")}),s();const b=a(this.path)?y(this.path,"utf-8"):void 0;let l=!1;const p=o(h=>{if(l)throw new Error(`locked file was published twice: ${this.path}`);s();const r=`${this.path}.${process.pid}.${Date.now()}.tmp`;if(T(r,h,L),f(r,384),this.durable){const n=m(r,"r");try{u(n)}finally{S(n)}}if(F(r,this.path),this.durable){const n=m(w(this.path),"r");try{u(n)}finally{S(n)}}l=!0},"publish"),{result:_,next:d}=await t(b,s,p);return s(),d!==void 0&&p(d),s(),_}finally{if(i)try{await i()}catch{}}}}export{M as LockedJsonFile};
@@ -1,12 +1,12 @@
1
- var Se=Object.defineProperty;var u=(e,t)=>Se(e,"name",{value:t,configurable:!0});import{prepared as E}from"../sqlite-statements.js";import{createHash as $}from"node:crypto";import{ApiError as Ne}from"../../api/errors.js";import{isAbsolute as J,relative as ve,resolve as Oe,sep as be,join as De}from"node:path";import{SessionManager as Te}from"@earendil-works/pi-coding-agent";import{covers as L}from"@north-light/identity";import{openDb as Y,withCanvasWrite as A}from"../canvas/db.js";import{deleteNodeWithOwned as Ie}from"../graph/deletion.js";import{getById as Me}from"../graph/objects.js";import{repairRunConversations as G}from"../canvas/sessions.js";import{getNode as S,nodeFiles as Le,removeNodeFiles as Ae,updateNode as Ce}from"../canvas/canvas.js";import{fullName as xe}from"../canvas/labels.js";import{renameWindow as ke}from"../runtime/placement.js";import{liveExecutionForNode as X}from"../canvas/executions.js";import{conversationStore as z,isConversationNotFound as We}from"../conversation-store/index.js";import{enqueueNodeMail as Fe}from"../feed/messages.js";import{newestReportBody as He}from"../feed/reports.js";import{ensureAppProfile as qe,loadExactProfileManifest as Ue}from"../profiles/manifest.js";import{stopNodeSandboxes as Q}from"../spaces/stop.js";import{BrokerUnreachableError as Pe}from"../runtime/broker-request.js";import{interruptLive as Be}from"../runtime/interactive-deliver.js";import{isBrokerLive as V}from"../runtime/model-swap.js";import{newNodeId as je}from"../runtime/nodes.js";import{writeGoal as $e}from"../runtime/kickoff.js";import{reviveNode as F}from"../runtime/revive.js";import{spawnChildPrepared as Je}from"../runtime/spawn.js";import{isolatedBrokerLauncherAvailable as Ye}from"../runtime/host.js";import{resolveRunKind as Ge}from"../runtime/launch-target.js";import{cycleAwareMessages as Xe}from"../runtime/session-cycles.js";import{originFor as Z}from"../runtime/stamp/protocol.js";import{readOutputRequest as ze,readOutputResult as K}from"../runtime/structured-output.js";import{admitLegacyStructuredOutput as ee}from"../../daemon/structured-requests/sidecar.js";import{runSchemaRepromptPending as Qe}from"../runtime/stop-guard.js";import{toRunOutcomeDTO as Ve}from"../../daemon/api/map.js";import{openAssistantMessageStartSequence as Ze}from"../../daemon/runs/observer.js";import{closeNode as te}from"../runtime/close.js";import{parseScope as Ke}from"@north-light/identity";import{appendRunEvent as ne,subscribeRunEvents as et}from"./events.js";import{runActivity as tt}from"./activity.js";import{cancelOpenQuestions as se,openQuestionIds as re}from"./questions.js";import{appSpace as oe}from"../layout.js";import{ensureAppSpace as nt}from"../spaces/space.js";import{assertUploadsUsable as ie,resolveStorageUrls as st}from"../storage/attachments.js";import{storageClient as rt,storageConfig as ot}from"../storage/client.js";import{deleteUploadRow as it,insertUploadUses as ae,releaseRunUploads as at}from"../storage/tables.js";import{abortProviderCalls as de}from"../providers/inflight.js";const ce=u((e,t,r)=>st(t,e,r),"defaultResolver"),dt=3e3;function rn(e){return Number(E(e,"UPDATE idempotency_keys SET grantee = 'app:terminal' WHERE grantee = 'user:local'").run().changes)}u(rn,"rewriteLocalIdempotencyGrantee");class R extends Error{static{u(this,"RunError")}status;code;param;details;scope;retryable;constructor(t,r,n,i,c,a,o=t===503){super(n),this.status=t,this.code=r,this.param=i,this.details=c,this.scope=a,this.retryable=o}}const O=u((e,t)=>new R(400,"invalid_request",e,t),"invalid"),H=u(()=>new R(404,"not_found","run not found"),"missing"),q=u(()=>({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}),"usage"),b=u(()=>Y(),"db");function C(e,t,r=!1){const n=S(e);if(!n||n.parent!==null||n.run_id!==e||n.grantee!==t||!r&&n.deleting_at)throw H();return n}u(C,"ownedRun");const U=["waiting_on_user","idle","settled"];function I(e){if(e.status==="canceled"||e.status==="dead"||e.status==="done"&&e.intent!=="parked")return"settled";const t=E(b(),`SELECT 1 FROM node_rows n WHERE n.run_id = ? AND n.status = 'active' AND
1
+ var Se=Object.defineProperty;var u=(e,t)=>Se(e,"name",{value:t,configurable:!0});import{prepared as E}from"../sqlite-statements.js";import{coveredByAny as Ne}from"../../shared/env.js";import{createHash as $}from"node:crypto";import{ApiError as ve}from"../../api/errors.js";import{isAbsolute as J,relative as Oe,resolve as be,sep as De,join as Te}from"node:path";import{SessionManager as Ie}from"@earendil-works/pi-coding-agent";import{covers as W}from"@north-light/identity";import{openDb as Y,withCanvasWrite as A}from"../canvas/db.js";import{deleteNodeWithOwned as Me}from"../graph/deletion.js";import{getById as Ae}from"../graph/objects.js";import{repairRunConversations as G}from"../canvas/sessions.js";import{getNode as S,nodeFiles as Le,removeNodeFiles as Ce,updateNode as xe}from"../canvas/canvas.js";import{fullName as ke}from"../canvas/labels.js";import{renameWindow as We}from"../runtime/placement.js";import{liveExecutionForNode as X}from"../canvas/executions.js";import{conversationStore as z,isConversationNotFound as Fe}from"../conversation-store/index.js";import{enqueueNodeMail as He}from"../feed/messages.js";import{newestReportBody as qe}from"../feed/reports.js";import{ensureAppProfile as Ue,loadExactProfileManifest as Be}from"../profiles/manifest.js";import{stopNodeSandboxes as Q}from"../spaces/stop.js";import{BrokerUnreachableError as Pe}from"../runtime/broker-request.js";import{interruptLive as je}from"../runtime/interactive-deliver.js";import{isBrokerLive as V}from"../runtime/model-swap.js";import{newNodeId as $e}from"../runtime/nodes.js";import{writeGoal as Je}from"../runtime/kickoff.js";import{reviveNode as F}from"../runtime/revive.js";import{spawnChildPrepared as Ye}from"../runtime/spawn.js";import{isolatedBrokerLauncherAvailable as Ge}from"../runtime/host.js";import{resolveRunKind as Xe}from"../runtime/launch-target.js";import{cycleAwareMessages as ze}from"../runtime/session-cycles.js";import{originFor as Z}from"../runtime/stamp/protocol.js";import{readOutputRequest as Qe,readOutputResult as K}from"../runtime/structured-output.js";import{admitLegacyStructuredOutput as ee}from"../../daemon/structured-requests/sidecar.js";import{runSchemaRepromptPending as Ve}from"../runtime/stop-guard.js";import{toRunOutcomeDTO as Ze}from"../../daemon/api/map.js";import{openAssistantMessageStartSequence as Ke}from"../../daemon/runs/observer.js";import{closeNode as te}from"../runtime/close.js";import{parseScope as et}from"@north-light/identity";import{appendRunEvent as ne,subscribeRunEvents as tt}from"./events.js";import{runActivity as nt}from"./activity.js";import{cancelOpenQuestions as se,openQuestionIds as re}from"./questions.js";import{appSpace as oe}from"../layout.js";import{ensureAppSpace as st}from"../spaces/space.js";import{assertUploadsUsable as ie,resolveStorageUrls as rt}from"../storage/attachments.js";import{storageClient as ot,storageConfig as it}from"../storage/client.js";import{deleteUploadRow as at,insertUploadUses as ae,releaseRunUploads as dt}from"../storage/tables.js";import{abortProviderCalls as de}from"../providers/inflight.js";const ce=u((e,t,r)=>rt(t,e,r),"defaultResolver"),ct=3e3;function an(e){return Number(E(e,"UPDATE idempotency_keys SET grantee = 'app:terminal' WHERE grantee = 'user:local'").run().changes)}u(an,"rewriteLocalIdempotencyGrantee");class R extends Error{static{u(this,"RunError")}status;code;param;details;scope;retryable;constructor(t,r,n,i,c,a,o=t===503){super(n),this.status=t,this.code=r,this.param=i,this.details=c,this.scope=a,this.retryable=o}}const O=u((e,t)=>new R(400,"invalid_request",e,t),"invalid"),H=u(()=>new R(404,"not_found","run not found"),"missing"),q=u(()=>({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}),"usage"),b=u(()=>Y(),"db");function L(e,t,r=!1){const n=S(e);if(!n||n.parent!==null||n.run_id!==e||n.grantee!==t||!r&&n.deleting_at)throw H();return n}u(L,"ownedRun");const U=["waiting_on_user","idle","settled"];function I(e){if(e.status==="canceled"||e.status==="dead"||e.status==="done"&&e.intent!=="parked")return"settled";const t=E(b(),`SELECT 1 FROM node_rows n WHERE n.run_id = ? AND n.status = 'active' AND
2
2
  (n.last_settled_at IS NULL OR EXISTS (
3
3
  SELECT 1 FROM run_events e WHERE e.run_id = ? AND e.node_id = n.node_id AND e.type = 'node.turn.started'
4
4
  AND e.sequence_number > COALESCE((SELECT MAX(c.sequence_number) FROM run_events c
5
5
  WHERE c.run_id = e.run_id AND c.node_id = e.node_id AND c.type = 'node.turn.completed'), 0)
6
- )) LIMIT 1`).get(e.node_id,e.node_id),r=E(b(),"SELECT 1 FROM deliveries WHERE destination_id IN (SELECT node_id FROM nodes WHERE run_id = ?) AND state IN ('armed', 'pending') AND wake = 1 LIMIT 1").get(e.node_id);return t||r||!e.last_settled_at||Qe(e)?"running":re(e.node_id).length?"waiting_on_user":"idle"}u(I,"runStatus");function on(e){const t=S(e);return t!==null&&I(t)==="running"}u(on,"runIsRunning");function M(e){G(e.node_id),e=S(e.node_id)??e;const t=I(e),r=t==="settled"?[]:re(e.node_id),n=He(e.node_id)||void 0,i=t==="settled"?Ve(e):null,c=i?.kind==="result"?K(e.node_id,e.grantee):void 0;return{run_id:e.node_id,name:e.name,status:t,...i?{outcome:i}:{},...c===void 0?{}:{result:c},...n!==void 0?{last_report:n}:{},pending_question_ids:r,...e.last_turn_error_json?{last_turn_error:JSON.parse(e.last_turn_error_json)}:{},usage:JSON.parse(e.usage_json??JSON.stringify(q())),payer:"user",scopes:e.scopes,metadata:e.metadata_json?JSON.parse(e.metadata_json):{},created_at:e.created,...t==="settled"&&e.outcome_at?{settled_at:e.outcome_at}:{},latest_sequence:(e.event_cursor??1)-1,last_activity_at:e.last_activity_at,activity:tt(e.node_id,t)}}u(M,"runObject");const P=864e5,ue=255;function an(e){if(e!==void 0){if(typeof e!="string"||!e.length||e.length>ue)throw O(`Idempotency-Key must be 1-${ue} characters`);return e}}u(an,"idempotencyKeyOf");function x(e,t,r,n=Y()){const i=E(n,"SELECT body_hash, run_id, response_json, created_at FROM idempotency_keys WHERE grantee = ? AND key = ?").get(e,t);if(!(!i||Date.now()-Date.parse(i.created_at)>=P)){if(i.body_hash!==r)throw new R(409,"idempotency_conflict","key used with a different request");return{run_id:i.run_id,response:JSON.parse(i.response_json)}}}u(x,"findIdempotent");function me(e,t,r,n,i,c,a){E(e,"DELETE FROM idempotency_keys WHERE grantee = ? AND key = ? AND created_at <= ?").run(t,r,new Date(Date.now()-P).toISOString()),E(e,"INSERT INTO idempotency_keys (grantee, key, body_hash, run_id, response_json, created_at) VALUES (?, ?, ?, ?, ?, ?)").run(t,r,n,i,JSON.stringify(c),a)}u(me,"recordIdempotent");function dn(e=Date.now()){return A(t=>Number(E(t,"DELETE FROM idempotency_keys WHERE created_at <= ?").run(new Date(e-P).toISOString()).changes))}u(dn,"pruneExpiredIdempotencyKeys");function le(e,t,r,n,i){const c=n.text;let a=0;return{messageId:Fe(t,{kind:"message",label:c.slice(0,120),senderNodeId:"human",senderName:r,bodyJson:i?{body:c}:{body:c,start_turn:!1},urgency:i?"normal":"deferred",wake:i?1:0,withinEnqueue:u((d,p)=>{n.uses.length&&(ie(r,n.uses,d),ae(n.uses.map(({upload_id:y,path:g})=>({upload_id:y,path:g,run_id:t})),d)),a=ne(d,t,t,"node.message.accepted",{message_id:p,start_turn:i}).sequence_number},"withinEnqueue")}).messageId,sequence:a}}u(le,"acceptRunMessage");class _e extends Error{static{u(this,"IdempotentReplay")}winner;constructor(t){super("idempotent replay"),this.winner=t}}async function cn(e,t,r,n=ce){if(t.isolated&&!Ye())throw new R(503,"isolated_unavailable","isolated runs require the Linux root launcher","isolated");const i=$("sha256").update(JSON.stringify(t)).digest("hex");if(r){const s=x(e.grantee,r,i);if(s){const m=C(s.run_id,e.grantee);return!m.pi_pid&&I(m)==="running"&&(t.output_schema!==void 0&&!m.outcome_at&&ze(m.node_id).state==="absent"&&K(m.node_id,m.grantee)===void 0&&ee(m.node_id,"terminal",t.output_schema),await F(m.node_id,{resume:!0,capacity:"freeze"})),s.response}}await nt(e.grantee);const c=t.profile===void 0?qe(e.grantee):Ue(t.profile),a=c.manifest.grantee??"app:terminal";if(a!==e.grantee){const s=`crtr:memory:rw:${a}:profile:${c.profileId}`;if(!e.resolved.some(m=>L(m,s))||t.scopes!==null&&!t.scopes.some(m=>L(m,s)))throw new R(403,"scope_missing",`missing scope ${s}`,"profile",void 0,s)}const o=je(),d=De(oe(e.grantee).nodes,o);if(t.isolated&&t.cwd!==void 0){const s=ve(d,Oe(t.cwd));if(!J(t.cwd)||s===".."||s.startsWith(`..${be}`)||J(s))throw O("cwd must be inside the isolated root node folder","cwd")}const p=await n(o,e.grantee,t.prompt),y=p.text;let g;try{g=t.message===void 0?void 0:await n(o,e.grantee,t.message)}catch(s){throw p.cleanup(),s}let f;try{await Je({kind:Ge(c),mode:"base",root:!0,rootLifecycle:"resident",readiness:"launch",cwd:t.isolated?t.cwd??d:oe(e.grantee).files,profile:c.profileId,prompt:y,nodeId:o,isolated:t.isolated??!1,...t.scopes===null?{}:{scopes:t.scopes},run:{grantee:e.grantee,sub:e.sub,grant_id:e.grant,client_id:e.client_id,metadata_json:JSON.stringify(t.metadata),run_id:o,event_cursor:1,usage_json:JSON.stringify(q())},withinCreate:u((s,m)=>{if(r){const w=x(e.grantee,r,i,s);if(w)throw new _e(w)}const h=p.uses;if(h.length&&(ie(e.grantee,h,s),ae(h.map(({upload_id:w,path:k})=>({upload_id:w,path:k,run_id:o})),s)),$e(o,y),f={run_id:o,node_id:o,grantee:e.grantee,grant:e.grant,rev:e.rev,scopes:t.scopes,payer:"user",status:"running",created_at:m.created,metadata:t.metadata,sequence_number:0},ne(s,o,o,"run.started",{scopes:t.scopes,payer:"user",usage:q()}),g){const w=le(s,o,e.grantee,g,!1);f.message={run_id:o,message_id:w.messageId,sequence_number:w.sequence}}r&&me(s,e.grantee,r,i,o,f,m.created)},"withinCreate")},t.output_schema===void 0?void 0:s=>{ee(s,"terminal",t.output_schema)})}catch(s){if(S(o)||(p.cleanup(),g?.cleanup()),s instanceof _e)return s.winner.response;if(s instanceof Ne&&(s.code==="isolated_run"||s.code==="invalid_request")){const m=s.details&&typeof s.details=="object"&&"param"in s.details&&typeof s.details.param=="string"?s.details.param:void 0;throw new R(s.status,s.code,s.message,m)}throw s}if(!f)throw new Error("run creation did not commit");return f}u(cn,"startRun");function un(e,t){let r;if(t.after)try{const o=JSON.parse(Buffer.from(t.after,"base64url").toString());if(!o||typeof o!="object"||!("activity"in o)||typeof o.activity!="string"||!("id"in o)||typeof o.id!="string")throw new Error("invalid cursor");r={activity:o.activity,id:o.id}}catch{throw O("invalid list cursor","after")}const i=b().prepare(`SELECT node_id, COALESCE(last_activity_at, created) AS activity FROM node_rows
6
+ )) LIMIT 1`).get(e.node_id,e.node_id),r=E(b(),"SELECT 1 FROM deliveries WHERE destination_id IN (SELECT node_id FROM nodes WHERE run_id = ?) AND state IN ('armed', 'pending') AND wake = 1 LIMIT 1").get(e.node_id);return t||r||!e.last_settled_at||Ve(e)?"running":re(e.node_id).length?"waiting_on_user":"idle"}u(I,"runStatus");function dn(e){const t=S(e);return t!==null&&I(t)==="running"}u(dn,"runIsRunning");function M(e){G(e.node_id),e=S(e.node_id)??e;const t=I(e),r=t==="settled"?[]:re(e.node_id),n=qe(e.node_id)||void 0,i=t==="settled"?Ze(e):null,c=i?.kind==="result"?K(e.node_id,e.grantee):void 0;return{run_id:e.node_id,name:e.name,status:t,...i?{outcome:i}:{},...c===void 0?{}:{result:c},...n!==void 0?{last_report:n}:{},pending_question_ids:r,...e.last_turn_error_json?{last_turn_error:JSON.parse(e.last_turn_error_json)}:{},usage:JSON.parse(e.usage_json??JSON.stringify(q())),payer:"user",scopes:e.scopes,metadata:e.metadata_json?JSON.parse(e.metadata_json):{},created_at:e.created,...t==="settled"&&e.outcome_at?{settled_at:e.outcome_at}:{},latest_sequence:(e.event_cursor??1)-1,last_activity_at:e.last_activity_at,activity:nt(e.node_id,t)}}u(M,"runObject");const B=864e5,ue=255;function cn(e){if(e!==void 0){if(typeof e!="string"||!e.length||e.length>ue)throw O(`Idempotency-Key must be 1-${ue} characters`);return e}}u(cn,"idempotencyKeyOf");function C(e,t,r,n=Y()){const i=E(n,"SELECT body_hash, run_id, response_json, created_at FROM idempotency_keys WHERE grantee = ? AND key = ?").get(e,t);if(!(!i||Date.now()-Date.parse(i.created_at)>=B)){if(i.body_hash!==r)throw new R(409,"idempotency_conflict","key used with a different request");return{run_id:i.run_id,response:JSON.parse(i.response_json)}}}u(C,"findIdempotent");function me(e,t,r,n,i,c,a){E(e,"DELETE FROM idempotency_keys WHERE grantee = ? AND key = ? AND created_at <= ?").run(t,r,new Date(Date.now()-B).toISOString()),E(e,"INSERT INTO idempotency_keys (grantee, key, body_hash, run_id, response_json, created_at) VALUES (?, ?, ?, ?, ?, ?)").run(t,r,n,i,JSON.stringify(c),a)}u(me,"recordIdempotent");function un(e=Date.now()){return A(t=>Number(E(t,"DELETE FROM idempotency_keys WHERE created_at <= ?").run(new Date(e-B).toISOString()).changes))}u(un,"pruneExpiredIdempotencyKeys");function le(e,t,r,n,i){const c=n.text;let a=0;return{messageId:He(t,{kind:"message",label:c.slice(0,120),senderNodeId:"human",senderName:r,bodyJson:i?{body:c}:{body:c,start_turn:!1},urgency:i?"normal":"deferred",wake:i?1:0,withinEnqueue:u((d,p)=>{n.uses.length&&(ie(r,n.uses,d),ae(n.uses.map(({upload_id:y,path:g})=>({upload_id:y,path:g,run_id:t})),d)),a=ne(d,t,t,"node.message.accepted",{message_id:p,start_turn:i}).sequence_number},"withinEnqueue")}).messageId,sequence:a}}u(le,"acceptRunMessage");class _e extends Error{static{u(this,"IdempotentReplay")}winner;constructor(t){super("idempotent replay"),this.winner=t}}async function mn(e,t,r,n=ce){if(t.isolated&&!Ge())throw new R(503,"isolated_unavailable","isolated runs require the Linux root launcher","isolated");const i=$("sha256").update(JSON.stringify(t)).digest("hex");if(r){const s=C(e.grantee,r,i);if(s){const m=L(s.run_id,e.grantee);return!m.pi_pid&&I(m)==="running"&&(t.output_schema!==void 0&&!m.outcome_at&&Qe(m.node_id).state==="absent"&&K(m.node_id,m.grantee)===void 0&&ee(m.node_id,"terminal",t.output_schema),await F(m.node_id,{resume:!0,capacity:"freeze"})),s.response}}await st(e.grantee);const c=t.profile===void 0?Ue(e.grantee):Be(t.profile),a=c.manifest.grantee??"app:terminal";if(a!==e.grantee){const s=`crtr:memory:rw:${a}:profile:${c.profileId}`;if(!e.resolved.some(m=>W(m,s))||t.scopes!==null&&!t.scopes.some(m=>W(m,s)))throw new R(403,"scope_missing",`missing scope ${s}`,"profile",void 0,s)}const o=$e(),d=Te(oe(e.grantee).nodes,o);if(t.isolated&&t.cwd!==void 0){const s=Oe(d,be(t.cwd));if(!J(t.cwd)||s===".."||s.startsWith(`..${De}`)||J(s))throw O("cwd must be inside the isolated root node folder","cwd")}const p=await n(o,e.grantee,t.prompt),y=p.text;let g;try{g=t.message===void 0?void 0:await n(o,e.grantee,t.message)}catch(s){throw p.cleanup(),s}let f;try{await Ye({kind:Xe(c),mode:"base",root:!0,rootLifecycle:"resident",readiness:"launch",cwd:t.isolated?t.cwd??d:oe(e.grantee).files,profile:c.profileId,prompt:y,nodeId:o,isolated:t.isolated??!1,...t.scopes===null?{}:{scopes:t.scopes},run:{grantee:e.grantee,sub:e.sub,grant_id:e.grant,client_id:e.client_id,metadata_json:JSON.stringify(t.metadata),run_id:o,event_cursor:1,usage_json:JSON.stringify(q())},withinCreate:u((s,m)=>{if(r){const w=C(e.grantee,r,i,s);if(w)throw new _e(w)}const h=p.uses;if(h.length&&(ie(e.grantee,h,s),ae(h.map(({upload_id:w,path:x})=>({upload_id:w,path:x,run_id:o})),s)),Je(o,y),f={run_id:o,node_id:o,grantee:e.grantee,grant:e.grant,rev:e.rev,scopes:t.scopes,payer:"user",status:"running",created_at:m.created,metadata:t.metadata,sequence_number:0},ne(s,o,o,"run.started",{scopes:t.scopes,payer:"user",usage:q()}),g){const w=le(s,o,e.grantee,g,!1);f.message={run_id:o,message_id:w.messageId,sequence_number:w.sequence}}r&&me(s,e.grantee,r,i,o,f,m.created)},"withinCreate")},t.output_schema===void 0?void 0:s=>{ee(s,"terminal",t.output_schema)})}catch(s){if(S(o)||(p.cleanup(),g?.cleanup()),s instanceof _e)return s.winner.response;if(s instanceof ve&&(s.code==="isolated_run"||s.code==="invalid_request")){const m=s.details&&typeof s.details=="object"&&"param"in s.details&&typeof s.details.param=="string"?s.details.param:void 0;throw new R(s.status,s.code,s.message,m)}throw s}if(!f)throw new Error("run creation did not commit");return f}u(mn,"startRun");function ln(e,t){let r;if(t.after)try{const o=JSON.parse(Buffer.from(t.after,"base64url").toString());if(!o||typeof o!="object"||!("activity"in o)||typeof o.activity!="string"||!("id"in o)||typeof o.id!="string")throw new Error("invalid cursor");r={activity:o.activity,id:o.id}}catch{throw O("invalid list cursor","after")}const i=b().prepare(`SELECT node_id, COALESCE(last_activity_at, created) AS activity FROM node_rows
7
7
  WHERE run_id = node_id AND deleting_at IS NULL ${e===null?"":"AND grantee = ?"}
8
- ORDER BY activity DESC, node_id DESC`).all(...e===null?[]:[e]).filter(({node_id:o})=>!t.visible||t.visible(o)).filter(({activity:o,node_id:d})=>!r||o<r.activity||o===r.activity&&d<r.id).map(o=>({root:S(o.node_id),activity:o.activity})).filter(({root:o})=>!t.status||I(o)===t.status),c=i.slice(0,t.limit),a=c.at(-1);return{runs:c.map(({root:o})=>M(o)),next:i.length>t.limit&&a?Buffer.from(JSON.stringify({activity:a.activity,id:a.root.node_id})).toString("base64url"):null}}u(un,"listRuns");async function mn(e,t,r){const n=C(e,t),i=u(()=>M(C(e,t)),"read");return!r||U.includes(I(n))?i():new Promise((c,a)=>{const o=u(()=>{clearTimeout(y),clearInterval(p),d();try{c(i())}catch(g){a(g)}},"finish"),d=et(e,()=>{try{U.includes(String(i().status))&&o()}catch(g){clearTimeout(y),clearInterval(p),d(),a(g)}}),p=setInterval(()=>{U.includes(String(i().status))&&o()},250),y=setTimeout(o,r*1e3)})}u(mn,"waitRun");async function ln(e,t,r=ce,n=!0,i){const c=e.status==="canceled"||e.outcome_reason==="closed",a=e.grantee??"app:terminal",o=$("sha256").update(JSON.stringify(["run.message",e.node_id,t,n])).digest("hex"),d=u(async f=>{const s=S(e.node_id),m=E(b(),"SELECT 1 FROM deliveries WHERE message_id = ? AND state = 'pending' AND canceled_at IS NULL").get(String(f.message_id));return s&&!c&&n&&!s.pi_pid&&m&&await F(e.node_id,{resume:!0,capacity:"freeze"}),f},"replay");if(i){const f=x(a,i,o);if(f)return d(f.response)}if(c)throw new R(409,"run_settled","canceled run cannot receive a message");const p=await r(e.node_id,a,t),y=n&&e.final_report&&!e.pi_pid?(await import("../runtime/reopen.js")).commitReopenResident:void 0;let g;try{g=A(f=>{if(i){const h=x(a,i,o,f);if(h)return{replayed:h.response}}y?.(e.node_id);const s=le(f,e.node_id,a,p,n),m={run_id:e.node_id,message_id:s.messageId,sequence_number:s.sequence};return i&&me(f,a,i,o,e.node_id,m,new Date().toISOString()),{result:m}})}catch(f){throw p.cleanup(),f}return"replayed"in g?(p.cleanup(),d(g.replayed)):(n&&!e.pi_pid&&await F(e.node_id,{resume:!0,capacity:"freeze"}),g.result)}u(ln,"messageRun");async function ct(e){const t=u(()=>new R(503,"service_unavailable",`node ${e} broker was unavailable before the interrupt reached it`),"unavailable");for(let r=0;r<2;r++){const n=S(e);if(!n)throw H();if(!V(n)){if(n.streaming)throw t();return}const i=X(e)?.executionId;try{await Be(e)==="turn"&&de({nodes:[e]});return}catch(c){if(!(c instanceof Pe))throw c;const a=S(e);if(!a)throw H();if(!a.streaming)return;if(r===0&&V(a)&&X(e)?.executionId!==i)continue;throw t()}}throw t()}u(ct,"interruptNode");async function _n(e,t="stop"){const r=t==="stop"?E(b(),"SELECT node_id FROM nodes WHERE run_id = ?").all(e.node_id).map(({node_id:n})=>n):[e.node_id];return await Promise.all(r.map(n=>ct(n))),M(S(e.node_id)??e)}u(_n,"interruptRun");async function pn(e,t={}){const r=E(b(),"SELECT node_id FROM nodes WHERE run_id = ? ORDER BY created DESC").all(e.node_id).map(a=>a.node_id);de({run:e.node_id});const n=[];if(I(e)!=="settled")n.push(...te(e.node_id,{preserveEmpty:!0,stopReason:"run_canceled"}).closed);else for(const a of r){if(a===e.node_id)continue;const o=S(a);o&&o.status!=="done"&&o.status!=="canceled"&&o.status!=="dead"&&n.push(...te(a,{preserveEmpty:!0,stopReason:"run_canceled"}).closed)}if(await se({run_id:e.node_id},"the run was canceled"),(await Promise.all([...new Set([e.node_id,...n])].map(a=>Q(a,"run_canceled")))).some(a=>!a.confirmed))throw new R(500,"server_error","run process teardown could not be confirmed",void 0,void 0,void 0,!0);const c=S(e.node_id);return M(c??e)}u(pn,"cancelRun");const pe=200;function fn(e,t){if(typeof t!="string"||!t.trim())throw O("name is required","name");const r=t.trim();if(r.length>pe)throw O(`name exceeds ${pe} characters`,"name");const n=Ce(e.node_id,{name:r});return n.window&&ke(n.window,xe(n)),M(n)}u(fn,"renameRun");async function gn(e,t={config:ot,client:rt}){await se({run_id:e.node_id},"the run was deleted");const r=A(d=>(E(d,"UPDATE nodes SET deleting_at = COALESCE(deleting_at, ?) WHERE node_id = ?").run(new Date().toISOString(),e.node_id),E(d,"SELECT node_id FROM nodes WHERE run_id = ? ORDER BY created DESC").all(e.node_id).map(p=>p.node_id)));if((await Promise.all(r.map(d=>Q(d,"run_deleted",{deleting:!0})))).some(d=>!d.confirmed))throw new R(500,"server_error","run process teardown could not be confirmed",void 0,void 0,void 0,!0);const i=r.map(d=>{const p=Le(d);if(p.app===null)throw new Error(`run node missing during deletion: ${d}`);return p}),c=E(b(),"SELECT DISTINCT session_id FROM sessions WHERE session_id IS NOT NULL AND node_id IN (SELECT node_id FROM nodes WHERE run_id = ?)").all(e.node_id);for(const{session_id:d}of c)try{z().delete(d)}catch(p){if(!We(p))throw p}const a=A(d=>{for(const f of r){const s=Me(d,f);s!==null&&s.deleted_at===null&&Ie(d,s)}const p=at([e.node_id],d);E(d,"DELETE FROM run_events WHERE run_id = ?").run(e.node_id),E(d,"DELETE FROM idempotency_keys WHERE run_id = ?").run(e.node_id);const y=r.map(()=>"?").join(","),g=d.prepare(`SELECT message_id FROM messages WHERE sender_node_id IN (${y}) UNION SELECT message_id FROM deliveries WHERE destination_id IN (${y})`).all(...r,...r);d.prepare(`DELETE FROM deliveries WHERE destination_id IN (${y})`).run(...r);for(const{message_id:f}of g)E(d,"DELETE FROM messages WHERE message_id = ? AND NOT EXISTS (SELECT 1 FROM deliveries WHERE message_id = ?)").run(f,f);return p});for(const d of i)Ae(d);const{notifyRunDeleted:o}=await import("./events.js");return o(e.node_id),{uploads_pending:await ut(a,t)}}u(gn,"deleteRun");async function ut(e,t){if(!e.length||!t.config())return e.length;let r;try{r=t.client()}catch{return e.length}const n=new Set,i=e.map(a=>r.deleteUpload(a).then(()=>{it(a),n.add(a)},()=>{}));let c;return await Promise.race([Promise.all(i),new Promise(a=>{c=setTimeout(a,t.budgetMs??dt),c.unref?.()})]),clearTimeout(c),e.length-n.size}u(ut,"deleteReleasedUploads");function En(e,t,r){C(e,t),G(e);const n=b();n.exec("BEGIN");try{const c=E(n,"SELECT node_id, parent FROM nodes WHERE run_id = ? ORDER BY CASE WHEN node_id = ? THEN 0 ELSE 1 END, created, node_id").all(e,e).map(s=>({node_id:s.node_id,parent_node_id:s.parent,conversations:E(n,"SELECT DISTINCT session_id FROM sessions WHERE node_id = ? ORDER BY created_at").all(s.node_id).map(({session_id:m})=>{const{header:h,entries:w}=z().read(m);if(r==="entries")return{conversation_id:m,header:h,entries:w};const k=Te.inMemory(h.cwd,void 0,[h,...w]),fe=Xe(k)??[],B=new Map(w.map((_,N)=>[_.id,N])),ge=w.filter(_=>_.type==="custom"&&_.customType==="crtr-system-prompt").map(_=>({id:_.id,message:{role:"custom",customType:"crtr-system-prompt",data:"data"in _?_.data:void 0,timestamp:_.timestamp}})),j=w.filter(_=>_.type==="custom_message"&&_.customType==="app_message").map(_=>({id:_.id,message:{role:"custom",customType:"app_message",content:"content"in _?_.content:"",timestamp:_.timestamp}})),Ee=s.node_id===e?w.find(_=>{if(_.type!=="message"||_.message.role!=="user")return!1;const N=Z(w,_.message.timestamp);return N?.type==="runtime"&&N.kind==="kickoff"})?.id:void 0,we=[...fe.filter(({id:_})=>!j.some(N=>N.id===_)),...ge,...j].sort((_,N)=>(B.get(_.id)??Number.MAX_SAFE_INTEGER)-(B.get(N.id)??Number.MAX_SAFE_INTEGER)).map(({id:_,message:N})=>{const l=N,D=l.usage,W=l.role==="user"?Z(w,Number(l.timestamp)):null,ye=W?.type==="human"||_===Ee,T=l.role==="toolResult"?"tool_result":l.role==="bashExecution"?"bash_execution":l.role==="compactionSummary"?"compaction_summary":l.role==="branchSummary"?"branch_summary":l.role==="custom"&&l.customType==="crtr-system-prompt"?"system":l.role==="user"?ye?"user":"custom":l.role,he=T==="custom"?l.role==="custom"?l.customType:W?.type==="runtime"?W.kind:"unknown":void 0,Re=T==="system"&&l.customType==="crtr-system-prompt"?[{type:"text",text:l.data?.text??""}]:typeof l.content=="string"?[{type:"text",text:l.content}]:Array.isArray(l.content)?l.content.map(v=>v.type==="toolCall"?{type:"tool_call",tool_call_id:v.id,name:v.name,arguments:v.arguments}:v.type==="image"?{type:"image",mime_type:v.mimeType}:v.type==="thinking"?{type:"thinking",text:v.thinking}:v):[];return{entry_id:_,role:T,...T==="custom"?{custom_type:he}:{},time:typeof l.timestamp=="string"?l.timestamp:new Date(Number(l.timestamp??Date.now())).toISOString(),content:Re,...T==="assistant"?{model:l.model,provider:l.provider,stop_reason:l.stopReason,...l.errorMessage?{error_message:l.errorMessage}:{},...D?{usage:{input_tokens:D.input??0,output_tokens:D.output??0,cache_read_tokens:D.cacheRead??0,cache_write_tokens:D.cacheWrite??0,cost_usd:D.cost?.total??0}}:{}}:{},...T==="tool_result"?{tool_call_id:l.toolCallId,tool_name:l.toolName,is_error:l.isError}:{}}})??[];return{conversation_id:m,messages:we}})})),o=E(n,"SELECT event_cursor FROM nodes WHERE node_id = ?").get(e).event_cursor-1,d=new Map(c.map(s=>[s.node_id,Ze(s.node_id)])),p=E(n,`SELECT node_id, sequence_number FROM run_events
8
+ ORDER BY activity DESC, node_id DESC`).all(...e===null?[]:[e]).filter(({node_id:o})=>!t.visible||t.visible(o)).filter(({activity:o,node_id:d})=>!r||o<r.activity||o===r.activity&&d<r.id).map(o=>({root:S(o.node_id),activity:o.activity})).filter(({root:o})=>!t.status||I(o)===t.status),c=i.slice(0,t.limit),a=c.at(-1);return{runs:c.map(({root:o})=>M(o)),next:i.length>t.limit&&a?Buffer.from(JSON.stringify({activity:a.activity,id:a.root.node_id})).toString("base64url"):null}}u(ln,"listRuns");async function _n(e,t,r){const n=L(e,t),i=u(()=>M(L(e,t)),"read");return!r||U.includes(I(n))?i():new Promise((c,a)=>{const o=u(()=>{clearTimeout(y),clearInterval(p),d();try{c(i())}catch(g){a(g)}},"finish"),d=tt(e,()=>{try{U.includes(String(i().status))&&o()}catch(g){clearTimeout(y),clearInterval(p),d(),a(g)}}),p=setInterval(()=>{U.includes(String(i().status))&&o()},250),y=setTimeout(o,r*1e3)})}u(_n,"waitRun");async function pn(e,t,r=ce,n=!0,i){const c=e.status==="canceled"||e.outcome_reason==="closed",a=e.grantee??"app:terminal",o=$("sha256").update(JSON.stringify(["run.message",e.node_id,t,n])).digest("hex"),d=u(async f=>{const s=S(e.node_id),m=E(b(),"SELECT 1 FROM deliveries WHERE message_id = ? AND state = 'pending' AND canceled_at IS NULL").get(String(f.message_id));return s&&!c&&n&&!s.pi_pid&&m&&await F(e.node_id,{resume:!0,capacity:"freeze"}),f},"replay");if(i){const f=C(a,i,o);if(f)return d(f.response)}if(c)throw new R(409,"run_settled","canceled run cannot receive a message");const p=await r(e.node_id,a,t),y=n&&e.final_report&&!e.pi_pid?(await import("../runtime/reopen.js")).commitReopenResident:void 0;let g;try{g=A(f=>{if(i){const h=C(a,i,o,f);if(h)return{replayed:h.response}}y?.(e.node_id);const s=le(f,e.node_id,a,p,n),m={run_id:e.node_id,message_id:s.messageId,sequence_number:s.sequence};return i&&me(f,a,i,o,e.node_id,m,new Date().toISOString()),{result:m}})}catch(f){throw p.cleanup(),f}return"replayed"in g?(p.cleanup(),d(g.replayed)):(n&&!e.pi_pid&&await F(e.node_id,{resume:!0,capacity:"freeze"}),g.result)}u(pn,"messageRun");async function ut(e){const t=u(()=>new R(503,"service_unavailable",`node ${e} broker was unavailable before the interrupt reached it`),"unavailable");for(let r=0;r<2;r++){const n=S(e);if(!n)throw H();if(!V(n)){if(n.streaming)throw t();return}const i=X(e)?.executionId;try{await je(e)==="turn"&&de({nodes:[e]});return}catch(c){if(!(c instanceof Pe))throw c;const a=S(e);if(!a)throw H();if(!a.streaming)return;if(r===0&&V(a)&&X(e)?.executionId!==i)continue;throw t()}}throw t()}u(ut,"interruptNode");async function fn(e,t="stop"){const r=t==="stop"?E(b(),"SELECT node_id FROM nodes WHERE run_id = ?").all(e.node_id).map(({node_id:n})=>n):[e.node_id];return await Promise.all(r.map(n=>ut(n))),M(S(e.node_id)??e)}u(fn,"interruptRun");async function gn(e,t={}){const r=E(b(),"SELECT node_id FROM nodes WHERE run_id = ? ORDER BY created DESC").all(e.node_id).map(a=>a.node_id);de({run:e.node_id});const n=[];if(I(e)!=="settled")n.push(...te(e.node_id,{preserveEmpty:!0,stopReason:"run_canceled"}).closed);else for(const a of r){if(a===e.node_id)continue;const o=S(a);o&&o.status!=="done"&&o.status!=="canceled"&&o.status!=="dead"&&n.push(...te(a,{preserveEmpty:!0,stopReason:"run_canceled"}).closed)}if(await se({run_id:e.node_id},"the run was canceled"),(await Promise.all([...new Set([e.node_id,...n])].map(a=>Q(a,"run_canceled")))).some(a=>!a.confirmed))throw new R(500,"server_error","run process teardown could not be confirmed",void 0,void 0,void 0,!0);const c=S(e.node_id);return M(c??e)}u(gn,"cancelRun");const pe=200;function En(e,t){if(typeof t!="string"||!t.trim())throw O("name is required","name");const r=t.trim();if(r.length>pe)throw O(`name exceeds ${pe} characters`,"name");const n=xe(e.node_id,{name:r});return n.window&&We(n.window,ke(n)),M(n)}u(En,"renameRun");async function wn(e,t={config:it,client:ot}){await se({run_id:e.node_id},"the run was deleted");const r=A(d=>(E(d,"UPDATE nodes SET deleting_at = COALESCE(deleting_at, ?) WHERE node_id = ?").run(new Date().toISOString(),e.node_id),E(d,"SELECT node_id FROM nodes WHERE run_id = ? ORDER BY created DESC").all(e.node_id).map(p=>p.node_id)));if((await Promise.all(r.map(d=>Q(d,"run_deleted",{deleting:!0})))).some(d=>!d.confirmed))throw new R(500,"server_error","run process teardown could not be confirmed",void 0,void 0,void 0,!0);const i=r.map(d=>{const p=Le(d);if(p.app===null)throw new Error(`run node missing during deletion: ${d}`);return p}),c=E(b(),"SELECT DISTINCT session_id FROM sessions WHERE session_id IS NOT NULL AND node_id IN (SELECT node_id FROM nodes WHERE run_id = ?)").all(e.node_id);for(const{session_id:d}of c)try{z().delete(d)}catch(p){if(!Fe(p))throw p}const a=A(d=>{for(const f of r){const s=Ae(d,f);s!==null&&s.deleted_at===null&&Me(d,s)}const p=dt([e.node_id],d);E(d,"DELETE FROM run_events WHERE run_id = ?").run(e.node_id),E(d,"DELETE FROM idempotency_keys WHERE run_id = ?").run(e.node_id);const y=r.map(()=>"?").join(","),g=d.prepare(`SELECT message_id FROM messages WHERE sender_node_id IN (${y}) UNION SELECT message_id FROM deliveries WHERE destination_id IN (${y})`).all(...r,...r);d.prepare(`DELETE FROM deliveries WHERE destination_id IN (${y})`).run(...r);for(const{message_id:f}of g)E(d,"DELETE FROM messages WHERE message_id = ? AND NOT EXISTS (SELECT 1 FROM deliveries WHERE message_id = ?)").run(f,f);return p});for(const d of i)Ce(d);const{notifyRunDeleted:o}=await import("./events.js");return o(e.node_id),{uploads_pending:await mt(a,t)}}u(wn,"deleteRun");async function mt(e,t){if(!e.length||!t.config())return e.length;let r;try{r=t.client()}catch{return e.length}const n=new Set,i=e.map(a=>r.deleteUpload(a).then(()=>{at(a),n.add(a)},()=>{}));let c;return await Promise.race([Promise.all(i),new Promise(a=>{c=setTimeout(a,t.budgetMs??ct),c.unref?.()})]),clearTimeout(c),e.length-n.size}u(mt,"deleteReleasedUploads");function yn(e,t,r){L(e,t),G(e);const n=b();n.exec("BEGIN");try{const c=E(n,"SELECT node_id, parent FROM nodes WHERE run_id = ? ORDER BY CASE WHEN node_id = ? THEN 0 ELSE 1 END, created, node_id").all(e,e).map(s=>({node_id:s.node_id,parent_node_id:s.parent,conversations:E(n,"SELECT DISTINCT session_id FROM sessions WHERE node_id = ? ORDER BY created_at").all(s.node_id).map(({session_id:m})=>{const{header:h,entries:w}=z().read(m);if(r==="entries")return{conversation_id:m,header:h,entries:w};const x=Ie.inMemory(h.cwd,void 0,[h,...w]),fe=ze(x)??[],P=new Map(w.map((_,N)=>[_.id,N])),ge=w.filter(_=>_.type==="custom"&&_.customType==="crtr-system-prompt").map(_=>({id:_.id,message:{role:"custom",customType:"crtr-system-prompt",data:"data"in _?_.data:void 0,timestamp:_.timestamp}})),j=w.filter(_=>_.type==="custom_message"&&_.customType==="app_message").map(_=>({id:_.id,message:{role:"custom",customType:"app_message",content:"content"in _?_.content:"",timestamp:_.timestamp}})),Ee=s.node_id===e?w.find(_=>{if(_.type!=="message"||_.message.role!=="user")return!1;const N=Z(w,_.message.timestamp);return N?.type==="runtime"&&N.kind==="kickoff"})?.id:void 0,we=[...fe.filter(({id:_})=>!j.some(N=>N.id===_)),...ge,...j].sort((_,N)=>(P.get(_.id)??Number.MAX_SAFE_INTEGER)-(P.get(N.id)??Number.MAX_SAFE_INTEGER)).map(({id:_,message:N})=>{const l=N,D=l.usage,k=l.role==="user"?Z(w,Number(l.timestamp)):null,ye=k?.type==="human"||_===Ee,T=l.role==="toolResult"?"tool_result":l.role==="bashExecution"?"bash_execution":l.role==="compactionSummary"?"compaction_summary":l.role==="branchSummary"?"branch_summary":l.role==="custom"&&l.customType==="crtr-system-prompt"?"system":l.role==="user"?ye?"user":"custom":l.role,he=T==="custom"?l.role==="custom"?l.customType:k?.type==="runtime"?k.kind:"unknown":void 0,Re=T==="system"&&l.customType==="crtr-system-prompt"?[{type:"text",text:l.data?.text??""}]:typeof l.content=="string"?[{type:"text",text:l.content}]:Array.isArray(l.content)?l.content.map(v=>v.type==="toolCall"?{type:"tool_call",tool_call_id:v.id,name:v.name,arguments:v.arguments}:v.type==="image"?{type:"image",mime_type:v.mimeType}:v.type==="thinking"?{type:"thinking",text:v.thinking}:v):[];return{entry_id:_,role:T,...T==="custom"?{custom_type:he}:{},time:typeof l.timestamp=="string"?l.timestamp:new Date(Number(l.timestamp??Date.now())).toISOString(),content:Re,...T==="assistant"?{model:l.model,provider:l.provider,stop_reason:l.stopReason,...l.errorMessage?{error_message:l.errorMessage}:{},...D?{usage:{input_tokens:D.input??0,output_tokens:D.output??0,cache_read_tokens:D.cacheRead??0,cache_write_tokens:D.cacheWrite??0,cost_usd:D.cost?.total??0}}:{}}:{},...T==="tool_result"?{tool_call_id:l.toolCallId,tool_name:l.toolName,is_error:l.isError}:{}}})??[];return{conversation_id:m,messages:we}})})),o=E(n,"SELECT event_cursor FROM nodes WHERE node_id = ?").get(e).event_cursor-1,d=new Map(c.map(s=>[s.node_id,Ke(s.node_id)])),p=E(n,`SELECT node_id, sequence_number FROM run_events
9
9
  WHERE run_id = ? AND sequence_number <= ? AND type IN ('node.thinking.done', 'node.output_text.done', 'usage')
10
10
  ORDER BY sequence_number`).all(e,o);for(const s of p){const m=d.get(s.node_id);m!=null&&s.sequence_number>=m&&d.set(s.node_id,null)}const y=E(n,`SELECT node_id, sequence_number, type, body_json FROM run_events
11
11
  WHERE run_id = ? AND sequence_number <= ? AND type IN ('node.thinking.delta', 'node.output_text.delta')
12
- ORDER BY sequence_number`).all(e,o),g=new Map;for(const s of y){const m=d.get(s.node_id);if(m==null||s.sequence_number<m)continue;const h=JSON.parse(s.body_json),w=g.get(s.node_id)??{thinking:"",text:""};s.type==="node.thinking.delta"?w.thinking+=h.delta:w.text+=h.delta,g.set(s.node_id,w)}const f=c.map(s=>({...s,in_progress_message:d.get(s.node_id)==null?null:g.get(s.node_id)??{thinking:"",text:""}}));return n.exec("COMMIT"),{run_id:e,latest_sequence:o,nodes:f}}catch(i){throw n.exec("ROLLBACK"),i}}u(En,"traceRun");function wn(e,t,r){if(t===void 0){if(!e.some(n=>L(n,"crtr:llm")))throw new R(403,"scope_missing","missing scope crtr:llm",void 0,void 0,"crtr:llm");return null}if(!Array.isArray(t)||t.some(n=>typeof n!="string"))throw O("scopes must be a list of scope strings","scopes");for(const n of t){try{Ke(n)}catch{throw O(`invalid scope ${n}`,"scopes")}if(/^crtr:files:(?:read|rw):app:/.test(n)&&n.split(":")[4]===r.slice(4))throw O(`own-app files cannot narrow a run: ${n}`,"scopes");if(!(/^crtr:memory:(?:read|rw):app:/.test(n)&&n.split(":")[4]===r.slice(4))&&!e.some(c=>L(c,n)))throw new R(403,"scope_missing",`missing scope ${n}`,void 0,void 0,n)}if(!t.includes("crtr:llm"))throw new R(403,"scope_missing","missing scope crtr:llm",void 0,void 0,"crtr:llm");return t}u(wn,"requestedScopes");export{P as IDEMPOTENCY_WINDOW_MS,dt as RUN_DELETE_STORAGE_BUDGET_MS,pe as RUN_NAME_MAX,R as RunError,pn as cancelRun,gn as deleteRun,an as idempotencyKeyOf,_n as interruptRun,un as listRuns,ln as messageRun,C as ownedRun,dn as pruneExpiredIdempotencyKeys,fn as renameRun,wn as requestedScopes,rn as rewriteLocalIdempotencyGrantee,on as runIsRunning,M as runObject,cn as startRun,En as traceRun,mn as waitRun};
12
+ ORDER BY sequence_number`).all(e,o),g=new Map;for(const s of y){const m=d.get(s.node_id);if(m==null||s.sequence_number<m)continue;const h=JSON.parse(s.body_json),w=g.get(s.node_id)??{thinking:"",text:""};s.type==="node.thinking.delta"?w.thinking+=h.delta:w.text+=h.delta,g.set(s.node_id,w)}const f=c.map(s=>({...s,in_progress_message:d.get(s.node_id)==null?null:g.get(s.node_id)??{thinking:"",text:""}}));return n.exec("COMMIT"),{run_id:e,latest_sequence:o,nodes:f}}catch(i){throw n.exec("ROLLBACK"),i}}u(yn,"traceRun");function hn(e,t,r){if(t===void 0){if(!e.some(n=>W(n,"crtr:llm")))throw new R(403,"scope_missing","missing scope crtr:llm",void 0,void 0,"crtr:llm");return null}if(!Array.isArray(t)||t.some(n=>typeof n!="string"))throw O("scopes must be a list of scope strings","scopes");for(const n of t){try{et(n)}catch{throw O(`invalid scope ${n}`,"scopes")}if(/^crtr:files:(?:read|rw):app:/.test(n)&&n.split(":")[4]===r.slice(4))throw O(`own-app files cannot narrow a run: ${n}`,"scopes");if(!(/^crtr:memory:(?:read|rw):app:/.test(n)&&n.split(":")[4]===r.slice(4))&&!Ne(e,n))throw new R(403,"scope_missing",`missing scope ${n}`,void 0,void 0,n)}if(!t.includes("crtr:llm"))throw new R(403,"scope_missing","missing scope crtr:llm",void 0,void 0,"crtr:llm");return t}u(hn,"requestedScopes");export{B as IDEMPOTENCY_WINDOW_MS,ct as RUN_DELETE_STORAGE_BUDGET_MS,pe as RUN_NAME_MAX,R as RunError,gn as cancelRun,wn as deleteRun,cn as idempotencyKeyOf,fn as interruptRun,ln as listRuns,pn as messageRun,L as ownedRun,un as pruneExpiredIdempotencyKeys,En as renameRun,hn as requestedScopes,an as rewriteLocalIdempotencyGrantee,dn as runIsRunning,M as runObject,mn as startRun,yn as traceRun,_n as waitRun};
@@ -1,3 +1,3 @@
1
- var be=Object.defineProperty;var u=(e,n)=>be(e,"name",{value:n,configurable:!0});import{assertGranteeRunsHere as Se}from"./unsandboxed.js";import{spawn as ye}from"node:child_process";import{mkdirSync as ce,existsSync as $,unlinkSync as ae,openSync as j,closeSync as T,writeSync as Pe,chmodSync as Re,lstatSync as xe}from"node:fs";import{ensureAppSpace as Oe}from"../spaces/space.js";import{sandboxCacheEnv as Te}from"../spaces/cache-env.js";import{compileLaunch as De,launchStillGranted as Ne}from"../spaces/mounts.js";import{grantsGeneration as le}from"../grants/mirror.js";import{LAUNCHER_SOCKET as U}from"../../launcher/spec.js";import{launcherRequest as G}from"../../launcher/client.js";import{piAgentDir as Ae}from"./package-health.js";import{existsBeneath as ue,readFileBeneath as Ce,readTextBeneathIfExists as Le,setModeBeneath as y,withDirBeneath as W,writeFileBeneath as z}from"../spaces/open-beneath.js";import{dirname as V,join as w,resolve as Be,isAbsolute as $e}from"node:path";import{fileURLToPath as fe}from"node:url";import{connectViewSocket as je}from"./view-socket.js";import{appSpace as F}from"../layout.js";import{readConfig as Ge,readMergedLaunchConfig as Fe}from"../config.js";import{readRotationConfig as Ke}from"../subscription-state.js";import{getNode as S}from"../canvas/index.js";import{crtrHome as de,nodeRunDir as X,nodeRunJobDir as Me,jobDir as He,viewSocketPath as Je,nodeAgentDir as qe,contextDir as Ue,nodeHome as We}from"../canvas/paths.js";import{encodeFrame as ze}from"./broker-protocol.js";import{buildBrokerEnv as Ve}from"./spawn-env.js";import{hostExecPathFresh as Xe}from"./branded-host.js";import{isPidAlive as Q,killProcessTreePids as me,isAnyPidAlive as Qe,captureTeardownSnapshot as Ye}from"../canvas/pid.js";import{assertMandatoryCanvasExtensions as Ze}from"./canvas-extensions.js";import{boundFleet as Ie,brokerCapReached as er,brokerThresholdsForDaemon as rr}from"./fleet.js";import{emitEvent as E}from"../events/emit.js";import{bindBrokerEventSource as nr,eventSource as tr}from"../events/source.js";import{envBrokerEngine as or}from"../../shared/env.js";import{conversationIdFromPath as ir,conversationStore as sr,ConversationNotFoundError as he,storeAddress as cr}from"../conversation-store/index.js";import{takeNodeToken as ar}from"../canvas/node-tokens.js";function K(){const e=V(fe(import.meta.url));return w(e,"broker-cli.js")}u(K,"resolveBrokerEntry");const lr="@earendil-works/pi-coding-agent";function ur(e,n){const t=w(Ae(),"models.json"),r=w(n,"models.json"),o=Le(V(t),t);if(o===null)return;const f=JSON.parse(o),c=u(i=>Array.isArray(i)?i.map(c):i===null||typeof i!="object"?i:Object.fromEntries(Object.entries(i).filter(([s])=>!/^(apiKey|authorization|headers|token|secret)$/i.test(s)).map(([s,v])=>[s,c(v)])),"scrub"),l=`${JSON.stringify(c(f),null,2)}
2
- `;if(t===r&&o!==l){if(JSON.stringify(f)!==JSON.stringify(c(f)))throw new Error("terminal models.json contains credentials; move them into daemon secrets");y(e,r,432);return}z(e,r,l,{atomic:!0,mode:432}),y(e,r,432)}u(ur,"writeBrokerModels");function pe(e){return e.env.CRTR_BROKER_ENGINE??or()}u(pe,"resolveEngineSpec");function we(e){return e.includes("/")||e.endsWith(".jsonl")}u(we,"isPlainLaunchSessionPath");function fr(e,n){return $e(e)?e:Be(n,e)}u(fr,"resolvePlainLaunchPath");function dr(e,n,t,r){if(t==="--session"&&!we(r))throw new Error(`[broker] refusing to launch ${e}: launch recipe passes bare --session id '${r}' \u2014 the broker fatally rejects bare ids; pass the .jsonl path instead.`);if(!we(r))return;const o=fr(r,n),f=ir(o);try{if(f===null)throw new he(o);sr().header(f)}catch(c){if(!(c instanceof he))throw c;const l=t==="--session"?"session file":"fork source";throw new Error(`[broker] refusing to launch ${e}: launch recipe references missing ${l} '${r}' (${o})`)}}u(dr,"preflightLaunchSessionArg");function mr(e,n,t){const r=X(e);ce(r,{recursive:!0}),process.platform==="linux"&&$(U)&&xe(r).uid===process.getuid()&&Re(r,504);const o=w(r,"broker-launch.json"),f={user:Ge("user"),launch:Fe(t,n.env.CRTR_PROFILE_ID||null),rotation:Ke()};z(r,o,JSON.stringify({...n,configSnapshot:f}),{atomic:!0});const c=Ce(r,o).toString("utf8"),l=JSON.parse(c);if(!Array.isArray(l.argv))throw new Error(`[broker] refusing to launch ${e}: broker-launch.json is missing the argv array`);const i=pe(n);try{const d=import.meta.resolve(i);if(d.startsWith("file:")&&!$(fe(d)))throw new Error(`resolved to missing file ${d}`)}catch(d){throw new Error(`[broker] refusing to launch ${e}: cannot resolve broker engine '${i}' from ${K()} \u2014 ${d.message}`)}const s=l.argv,v=[];for(let d=0;d<s.length;d++){const P=s[d];if(P==="-e"){const D=s[++d];D!==void 0&&v.push(D);continue}if(P!=="--session"&&P!=="--fork")continue;const R=s[++d];R!==void 0&&dr(e,t,P,R)}try{Ze(v)}catch(d){throw new Error(`[broker] refusing to launch ${e}: ${d.message}`)}}u(mr,"preflightBrokerLaunch");const hr=2e3,pr=2e3,wr=150;async function M(e,n){const t=Date.now()+n;for(;Qe(e);){const r=t-Date.now();if(r<=0)return!1;await new Promise(o=>setTimeout(o,Math.min(wr,r)))}return!0}u(M,"waitForTreeExit");async function gr(e,n,t){return e.length===0?{confirmed:!0}:t&&await M(e,hr)?{confirmed:!0}:(me(e,"SIGTERM",n),await M(e,pr)?{confirmed:!0}:(me(e,"SIGKILL",n),await M(e,5e3)?{confirmed:!0}:{confirmed:!1,error:new Error(`broker process tree for ${e.join(", ")} remained alive after SIGKILL`)}))}u(gr,"terminateBrokerTree");function ge(e,n){const t=S(e),r=t?.pi_pid,o=r!=null?Ye(r,t?.pi_pid_identity??null,n.excludePid):null;return o?.reused===!0&&E({level:"warn",event:"broker.teardown.pid_reuse_refused",node_id:e,fields:{recorded_pid:r,expected_identity:t?.pi_pid_identity??null}}),{tree:o?.tree??[],identities:o?.identities??null,nodeId:e,launcherPid:$(U)&&o?.tree.includes(r)?r:null}}u(ge,"prepareBrokerTeardown");async function Ee(e){if(e.launcherPid!==null){try{await G({operation:"stop",pid:e.launcherPid})}catch(n){return Q(e.launcherPid)?{confirmed:!1,error:n instanceof Error?n:new Error(String(n))}:{confirmed:!0}}return await M([e.launcherPid],5e3)?{confirmed:!0}:{confirmed:!1,error:new Error(`launcher pid ${e.launcherPid} remained alive`)}}return new Promise(n=>{let t=!1,r=!1;const o=u(i=>{r||(r=!0,n(i))},"settle"),f=u(()=>{try{ae(Je(e.nodeId))}catch{}},"cleanupSocket"),c=u(i=>{gr(e.tree,e.identities,i).then(o,s=>o({confirmed:!1,error:s instanceof Error?s:new Error(String(s))}))},"finish"),l=je(e.nodeId);l.once("error",()=>{t||(f(),c(!1))}),l.once("connect",()=>{t=!0;try{l.write(ze({type:"shutdown"}))}catch{}l.end(),f(),c(!0)})})}u(Ee,"runPreparedBrokerTeardown");const H=new Map;function Xr(e,n={},t){const r=H.get(e);if(r!==void 0)return t!==void 0&&r.after.push(t),r.completion;let o;const f=new Promise(i=>{o=i}),c={completion:f,resolve:o,after:t===void 0?[]:[t]};H.set(e,c);let l;try{l=ge(e,n)}catch(i){const s={confirmed:!1,error:i instanceof Error?i:new Error(String(i))};return H.delete(e),E({level:"error",event:"broker.teardown.unconfirmed",node_id:e,error:s.error}),c.resolve(s),f}return setImmediate(()=>{H.delete(e),Ee(l).then(async i=>{if(i.confirmed)for(const s of c.after)try{await s()}catch(v){E({level:"error",event:"broker.teardown.after_failed",node_id:e,error:v})}else E({level:"error",event:"broker.teardown.unconfirmed",node_id:e,error:i.error});c.resolve(i)},i=>{const s={confirmed:!1,error:i instanceof Error?i:new Error(String(i))};E({level:"error",event:"broker.teardown.unconfirmed",node_id:e,error:s.error}),c.resolve(s)})}),f}u(Xr,"requestBrokerTeardown");function ke(){return process.platform==="linux"&&$(U)}u(ke,"isolatedBrokerLauncherAvailable");const Qr={async launch(e,n,t){if(Se(S(e)?.grantee),S(e)?.isolated===!0&&!ke())throw new Error("isolated broker requires the Linux root launcher");mr(e,n,t.cwd);const r=Ie();if(!r.reserveSlot(e))throw er(rr().automaticReviveCap,r.size());let o=null,f,c=!1;try{const l=He(e);ce(l,{recursive:!0}),o=j(w(l,"broker.log"),"a");const i=pe(n),s={...Ve(n),CRTR_BROKER_ENGINE:i,CRTR_STORE_ADDRESS:cr()};if(i!==lr){const a=n.env.NODE_OPTIONS??process.env.NODE_OPTIONS;a!==void 0&&(s.NODE_OPTIONS=a)}tr()===void 0&&nr(e);const v=S(e)?.run_id;if(v){const a=S(v);if(!a||a.deleting_at)throw new Error("run is deleting")}const d=n.env.CRTR_EXECUTION_ID;if(!d)throw new Error("broker launch has no execution id");const P=ar(d),R=ke();if(S(e)?.isolated===!0&&!R)throw new Error("isolated broker requires the Linux root launcher");if(process.platform==="linux"&&process.env.CRTR_LAUNCHER_SOCKET&&!R)throw new Error("root launcher socket is unavailable; refusing an unsandboxed broker");if(R){const a=S(e);if(!a)throw new Error(`missing node ${e}`);const h=a.grantee??"app:terminal",A=await Oe(h);Object.assign(s,Te(h));const p={uid:process.getuid(),gid:A.gid},g=X(e);for(const[m,k]of[[F(h).nodes,qe(e,h)],[F(h).nodes,Ue(e,h)],[g,Me(e)]])W(m,k,()=>{},!0),y(m,k,1528,p);y(g,w(g,"broker-launch.json"),416,p);const I=w(g,"system-prompt.md");ue(g,I)&&y(g,I,432,p);for(const m of["output-schema.json","situational-context.md"]){const k=w(g,m);ue(g,k)&&y(g,k,432,p)}const ee=a.isolated===!0,_=We(e,h,ee);ee&&(W(F(h).nodes,_,()=>{},!0),y(F(h).nodes,_,1528,p),s.HOME=_,s.PI_CODING_AGENT_DIR=w(_,".pi","agent"));const q=w(_,".pi","agent");W(_,q,()=>{},!0),ur(_,q);const re=w(q,"settings.json"),ve=w(V(K()),"../../builtin-pi-packages/pi-crtr-extensions");z(_,re,`${JSON.stringify({packages:[ve]})}
3
- `,{atomic:!0,mode:432}),y(_,re,432);const ne=w(X(e),`token-${d}`),C=j(ne,"wx+",384);ae(ne),Pe(C,P);const te=[j(`/proc/self/fd/${C}`,"r"),j(`/proc/self/fd/${C}`,"r")];T(C);const oe={nodeId:e,app:h,sub:a.sub??"user:local",grantId:a.grant_id??null,scopes:a.scopes??null,isolated:a.isolated===!0,...a.isolated===!0?{cwd:a.cwd}:{},command:[process.execPath,K(),"--canvas-home",de(),"--epoch",r.epoch(),e],env:Object.fromEntries(Object.entries({...s,...a.isolated===!0?{PWD:a.cwd}:{}}).filter(m=>m[1]!==void 0)),auditMissing:u(m=>E({level:"warn",event:"broker.mount.missing",node_id:e,fields:{source:m}}),"auditMissing")};try{let m;for(const O of te){const L=le(),b=De(oe),B=await G({operation:"launch",spec:b},[O]);if(!("pid"in B))throw new Error("launcher did not return a pid");if(le()===L||Ne(oe,b)){m=B.pid;break}E({level:"info",event:"broker.launch.regranted",node_id:e,fields:{pid:B.pid}}),await G({operation:"stop",pid:B.pid,signal:"SIGKILL"}).catch(()=>{})}if(m===void 0)throw new Error("grant changed during every launch attempt");const k=m,_e=Date.now(),ie=new Promise(O=>{const L=setInterval(()=>{Q(k)||(clearInterval(L),G({operation:"exit.status",pid:k}).then(b=>"oomKilled"in b&&b.oomKilled,()=>!1).then(b=>{E({level:b?"warn":"info",event:"broker.exited",node_id:e,fields:{pid:k,uptime_ms:Date.now()-_e,oom_killed:b}}),O({code:null,signal:null,...b?{oomKilled:!0}:{}})}))},250);L.unref()});T(o),o=null;const se={pid:k,exited:ie};return r.register(e,se),c=!0,ie.then(O=>r.onChildExit(e,O,d)),se}finally{for(const m of te)T(m)}}const D=await Xe(),x=ye(D,[K(),"--canvas-home",de(),"--epoch",r.epoch(),e],{cwd:t.cwd,detached:!0,stdio:["ignore",o,o,"pipe"],env:s});f=x;const N=x.stdio[3];if(!N||!("end"in N))throw new Error("broker token pipe did not open");N.on("error",a=>E({event:"broker.token_pipe.failed",level:"error",node_id:e,error:a})),N.end(P),T(o),o=null;const Y=Date.now(),Z=new Promise(a=>{let h=!1;const A=u(p=>{h||(h=!0,a(p))},"finish");x.once("exit",(p,g)=>{E({level:p===0?"info":"warn",event:"broker.exited",node_id:e,fields:{code:p,signal:g,uptime_ms:Date.now()-Y}}),A({code:p,signal:g})}),x.once("error",p=>{E({level:"error",event:"broker.launch.failed",node_id:e,error:p,fields:{uptime_ms:Date.now()-Y}}),A({code:null,signal:null})})});x.unref();const J={pid:x.pid??null,exited:Z};return J.pid!==null&&(r.register(e,J),c=!0,Z.then(a=>r.onChildExit(e,a,d))),J}catch(l){if(!c&&f?.pid!=null)try{f.kill("SIGTERM")}catch{}throw l}finally{c||r.releaseReservation(e),o!==null&&T(o)}},isAlive(e){return Q((typeof e=="string"?S(e):e)?.pi_pid)},teardown(e,n={}){try{return Ee(ge(e,n))}catch(t){return Promise.resolve({confirmed:!1,error:t instanceof Error?t:new Error(String(t))})}},signal(e,n){const t=S(e)?.pi_pid;if(t!=null)try{process.kill(t,n)}catch{}}};export{Qr as headlessBrokerHost,ke as isolatedBrokerLauncherAvailable,mr as preflightBrokerLaunch,Xr as requestBrokerTeardown};
1
+ var be=Object.defineProperty;var u=(e,n)=>be(e,"name",{value:n,configurable:!0});import{assertGranteeRunsHere as ye}from"./unsandboxed.js";import{spawn as Pe}from"node:child_process";import{mkdirSync as le,existsSync as $,unlinkSync as ue,openSync as j,closeSync as T,writeSync as Re,chmodSync as xe,lstatSync as Oe}from"node:fs";import{ensureAppSpace as Te}from"../spaces/space.js";import{sandboxCacheEnv as De}from"../spaces/cache-env.js";import{compileLaunch as Ne,launchStillGranted as Ce}from"../spaces/mounts.js";import{grantsGeneration as fe}from"../grants/mirror.js";import{LAUNCHER_SOCKET as W}from"../../launcher/spec.js";import{launcherRequest as F}from"../../launcher/client.js";import{piAgentDir as Ae}from"./package-health.js";import{existsBeneath as z,readFileBeneath as Le,readTextBeneathIfExists as Be,setModeBeneath as y,withDirBeneath as V,writeFileBeneath as G}from"../spaces/open-beneath.js";import{dirname as X,join as g,resolve as $e,isAbsolute as je}from"node:path";import{fileURLToPath as de}from"node:url";import{connectViewSocket as Fe}from"./view-socket.js";import{appSpace as K}from"../layout.js";import{readConfig as Ge,readMergedLaunchConfig as Ke}from"../config.js";import{readRotationConfig as Me}from"../subscription-state.js";import{getNode as v}from"../canvas/index.js";import{scopesFilePath as Q,crtrHome as me,nodeRunDir as Y,nodeRunJobDir as He,jobDir as Je,viewSocketPath as qe,nodeAgentDir as Ue,contextDir as We,nodeHome as ze}from"../canvas/paths.js";import{encodeFrame as Ve}from"./broker-protocol.js";import{buildBrokerEnv as Xe}from"./spawn-env.js";import{hostExecPathFresh as Qe}from"./branded-host.js";import{isPidAlive as Z,killProcessTreePids as pe,isAnyPidAlive as Ye,captureTeardownSnapshot as Ze}from"../canvas/pid.js";import{assertMandatoryCanvasExtensions as Ie}from"./canvas-extensions.js";import{boundFleet as er,brokerCapReached as rr,brokerThresholdsForDaemon as nr}from"./fleet.js";import{emitEvent as E}from"../events/emit.js";import{bindBrokerEventSource as tr,eventSource as or}from"../events/source.js";import{envBrokerEngine as ir}from"../../shared/env.js";import{conversationIdFromPath as sr,conversationStore as cr,ConversationNotFoundError as he,storeAddress as ar}from"../conversation-store/index.js";import{takeNodeToken as lr}from"../canvas/node-tokens.js";function M(){const e=X(de(import.meta.url));return g(e,"broker-cli.js")}u(M,"resolveBrokerEntry");const ur="@earendil-works/pi-coding-agent";function fr(e,n){const t=g(Ae(),"models.json"),r=g(n,"models.json"),o=Be(X(t),t);if(o===null)return;const f=JSON.parse(o),c=u(i=>Array.isArray(i)?i.map(c):i===null||typeof i!="object"?i:Object.fromEntries(Object.entries(i).filter(([s])=>!/^(apiKey|authorization|headers|token|secret)$/i.test(s)).map(([s,_])=>[s,c(_)])),"scrub"),l=`${JSON.stringify(c(f),null,2)}
2
+ `;if(t===r&&o!==l){if(JSON.stringify(f)!==JSON.stringify(c(f)))throw new Error("terminal models.json contains credentials; move them into daemon secrets");y(e,r,432);return}G(e,r,l,{atomic:!0,mode:432}),y(e,r,432)}u(fr,"writeBrokerModels");function we(e){return e.env.CRTR_BROKER_ENGINE??ir()}u(we,"resolveEngineSpec");function ge(e){return e.includes("/")||e.endsWith(".jsonl")}u(ge,"isPlainLaunchSessionPath");function dr(e,n){return je(e)?e:$e(n,e)}u(dr,"resolvePlainLaunchPath");function mr(e,n,t,r){if(t==="--session"&&!ge(r))throw new Error(`[broker] refusing to launch ${e}: launch recipe passes bare --session id '${r}' \u2014 the broker fatally rejects bare ids; pass the .jsonl path instead.`);if(!ge(r))return;const o=dr(r,n),f=sr(o);try{if(f===null)throw new he(o);cr().header(f)}catch(c){if(!(c instanceof he))throw c;const l=t==="--session"?"session file":"fork source";throw new Error(`[broker] refusing to launch ${e}: launch recipe references missing ${l} '${r}' (${o})`)}}u(mr,"preflightLaunchSessionArg");function pr(e,n,t){const r=Y(e);le(r,{recursive:!0}),process.platform==="linux"&&$(W)&&Oe(r).uid===process.getuid()&&xe(r,504);const o=g(r,"broker-launch.json"),f={user:Ge("user"),launch:Ke(t,n.env.CRTR_PROFILE_ID||null),rotation:Me()};G(r,o,JSON.stringify({...n,configSnapshot:f}),{atomic:!0}),n.env.CRTR_SCOPES_FILE!==void 0&&G(r,Q(e),JSON.stringify(v(e)?.scopes??[]),{atomic:!0});const c=Le(r,o).toString("utf8"),l=JSON.parse(c);if(!Array.isArray(l.argv))throw new Error(`[broker] refusing to launch ${e}: broker-launch.json is missing the argv array`);const i=we(n);try{const d=import.meta.resolve(i);if(d.startsWith("file:")&&!$(de(d)))throw new Error(`resolved to missing file ${d}`)}catch(d){throw new Error(`[broker] refusing to launch ${e}: cannot resolve broker engine '${i}' from ${M()} \u2014 ${d.message}`)}const s=l.argv,_=[];for(let d=0;d<s.length;d++){const P=s[d];if(P==="-e"){const D=s[++d];D!==void 0&&_.push(D);continue}if(P!=="--session"&&P!=="--fork")continue;const R=s[++d];R!==void 0&&mr(e,t,P,R)}try{Ie(_)}catch(d){throw new Error(`[broker] refusing to launch ${e}: ${d.message}`)}}u(pr,"preflightBrokerLaunch");const hr=2e3,wr=2e3,gr=150;async function H(e,n){const t=Date.now()+n;for(;Ye(e);){const r=t-Date.now();if(r<=0)return!1;await new Promise(o=>setTimeout(o,Math.min(gr,r)))}return!0}u(H,"waitForTreeExit");async function Er(e,n,t){return e.length===0?{confirmed:!0}:t&&await H(e,hr)?{confirmed:!0}:(pe(e,"SIGTERM",n),await H(e,wr)?{confirmed:!0}:(pe(e,"SIGKILL",n),await H(e,5e3)?{confirmed:!0}:{confirmed:!1,error:new Error(`broker process tree for ${e.join(", ")} remained alive after SIGKILL`)}))}u(Er,"terminateBrokerTree");function Ee(e,n){const t=v(e),r=t?.pi_pid,o=r!=null?Ze(r,t?.pi_pid_identity??null,n.excludePid):null;return o?.reused===!0&&E({level:"warn",event:"broker.teardown.pid_reuse_refused",node_id:e,fields:{recorded_pid:r,expected_identity:t?.pi_pid_identity??null}}),{tree:o?.tree??[],identities:o?.identities??null,nodeId:e,launcherPid:$(W)&&o?.tree.includes(r)?r:null}}u(Ee,"prepareBrokerTeardown");async function ke(e){if(e.launcherPid!==null){try{await F({operation:"stop",pid:e.launcherPid})}catch(n){return Z(e.launcherPid)?{confirmed:!1,error:n instanceof Error?n:new Error(String(n))}:{confirmed:!0}}return await H([e.launcherPid],5e3)?{confirmed:!0}:{confirmed:!1,error:new Error(`launcher pid ${e.launcherPid} remained alive`)}}return new Promise(n=>{let t=!1,r=!1;const o=u(i=>{r||(r=!0,n(i))},"settle"),f=u(()=>{try{ue(qe(e.nodeId))}catch{}},"cleanupSocket"),c=u(i=>{Er(e.tree,e.identities,i).then(o,s=>o({confirmed:!1,error:s instanceof Error?s:new Error(String(s))}))},"finish"),l=Fe(e.nodeId);l.once("error",()=>{t||(f(),c(!1))}),l.once("connect",()=>{t=!0;try{l.write(Ve({type:"shutdown"}))}catch{}l.end(),f(),c(!0)})})}u(ke,"runPreparedBrokerTeardown");const J=new Map;function Qr(e,n={},t){const r=J.get(e);if(r!==void 0)return t!==void 0&&r.after.push(t),r.completion;let o;const f=new Promise(i=>{o=i}),c={completion:f,resolve:o,after:t===void 0?[]:[t]};J.set(e,c);let l;try{l=Ee(e,n)}catch(i){const s={confirmed:!1,error:i instanceof Error?i:new Error(String(i))};return J.delete(e),E({level:"error",event:"broker.teardown.unconfirmed",node_id:e,error:s.error}),c.resolve(s),f}return setImmediate(()=>{J.delete(e),ke(l).then(async i=>{if(i.confirmed)for(const s of c.after)try{await s()}catch(_){E({level:"error",event:"broker.teardown.after_failed",node_id:e,error:_})}else E({level:"error",event:"broker.teardown.unconfirmed",node_id:e,error:i.error});c.resolve(i)},i=>{const s={confirmed:!1,error:i instanceof Error?i:new Error(String(i))};E({level:"error",event:"broker.teardown.unconfirmed",node_id:e,error:s.error}),c.resolve(s)})}),f}u(Qr,"requestBrokerTeardown");function ve(){return process.platform==="linux"&&$(W)}u(ve,"isolatedBrokerLauncherAvailable");const Yr={async launch(e,n,t){if(ye(v(e)?.grantee),v(e)?.isolated===!0&&!ve())throw new Error("isolated broker requires the Linux root launcher");pr(e,n,t.cwd);const r=er();if(!r.reserveSlot(e))throw rr(nr().automaticReviveCap,r.size());let o=null,f,c=!1;try{const l=Je(e);le(l,{recursive:!0}),o=j(g(l,"broker.log"),"a");const i=we(n),s={...Xe(n),CRTR_BROKER_ENGINE:i,CRTR_STORE_ADDRESS:ar()};if(i!==ur){const a=n.env.NODE_OPTIONS??process.env.NODE_OPTIONS;a!==void 0&&(s.NODE_OPTIONS=a)}or()===void 0&&tr(e);const _=v(e)?.run_id;if(_){const a=v(_);if(!a||a.deleting_at)throw new Error("run is deleting")}const d=n.env.CRTR_EXECUTION_ID;if(!d)throw new Error("broker launch has no execution id");const P=lr(d),R=ve();if(v(e)?.isolated===!0&&!R)throw new Error("isolated broker requires the Linux root launcher");if(process.platform==="linux"&&process.env.CRTR_LAUNCHER_SOCKET&&!R)throw new Error("root launcher socket is unavailable; refusing an unsandboxed broker");if(R){const a=v(e);if(!a)throw new Error(`missing node ${e}`);const w=a.grantee??"app:terminal",C=await Te(w);Object.assign(s,De(w));const m={uid:process.getuid(),gid:C.gid},p=Y(e);for(const[h,k]of[[K(w).nodes,Ue(e,w)],[K(w).nodes,We(e,w)],[p,He(e)]])V(h,k,()=>{},!0),y(h,k,1528,m);y(p,g(p,"broker-launch.json"),416,m),z(p,Q(e))&&y(p,Q(e),416,m);const re=g(p,"system-prompt.md");z(p,re)&&y(p,re,432,m);for(const h of["output-schema.json","situational-context.md"]){const k=g(p,h);z(p,k)&&y(p,k,432,m)}const ne=a.isolated===!0,S=ze(e,w,ne);ne&&(V(K(w).nodes,S,()=>{},!0),y(K(w).nodes,S,1528,m),s.HOME=S,s.PI_CODING_AGENT_DIR=g(S,".pi","agent"));const U=g(S,".pi","agent");V(S,U,()=>{},!0),fr(S,U);const te=g(U,"settings.json"),_e=g(X(M()),"../../builtin-pi-packages/pi-crtr-extensions");G(S,te,`${JSON.stringify({packages:[_e]})}
3
+ `,{atomic:!0,mode:432}),y(S,te,432);const oe=g(Y(e),`token-${d}`),A=j(oe,"wx+",384);ue(oe),Re(A,P);const ie=[j(`/proc/self/fd/${A}`,"r"),j(`/proc/self/fd/${A}`,"r")];T(A);const se={nodeId:e,app:w,sub:a.sub??"user:local",grantId:a.grant_id??null,scopes:a.scopes??null,isolated:a.isolated===!0,...a.isolated===!0?{cwd:a.cwd}:{},command:[process.execPath,M(),"--canvas-home",me(),"--epoch",r.epoch(),e],env:Object.fromEntries(Object.entries({...s,...a.isolated===!0?{PWD:a.cwd}:{}}).filter(h=>h[1]!==void 0)),auditMissing:u(h=>E({level:"warn",event:"broker.mount.missing",node_id:e,fields:{source:h}}),"auditMissing")};try{let h;for(const O of ie){const L=fe(),b=Ne(se),B=await F({operation:"launch",spec:b},[O]);if(!("pid"in B))throw new Error("launcher did not return a pid");if(fe()===L||Ce(se,b)){h=B.pid;break}E({level:"info",event:"broker.launch.regranted",node_id:e,fields:{pid:B.pid}}),await F({operation:"stop",pid:B.pid,signal:"SIGKILL"}).catch(()=>{})}if(h===void 0)throw new Error("grant changed during every launch attempt");const k=h,Se=Date.now(),ce=new Promise(O=>{const L=setInterval(()=>{Z(k)||(clearInterval(L),F({operation:"exit.status",pid:k}).then(b=>"oomKilled"in b&&b.oomKilled,()=>!1).then(b=>{E({level:b?"warn":"info",event:"broker.exited",node_id:e,fields:{pid:k,uptime_ms:Date.now()-Se,oom_killed:b}}),O({code:null,signal:null,...b?{oomKilled:!0}:{}})}))},250);L.unref()});T(o),o=null;const ae={pid:k,exited:ce};return r.register(e,ae),c=!0,ce.then(O=>r.onChildExit(e,O,d)),ae}finally{for(const h of ie)T(h)}}const D=await Qe(),x=Pe(D,[M(),"--canvas-home",me(),"--epoch",r.epoch(),e],{cwd:t.cwd,detached:!0,stdio:["ignore",o,o,"pipe"],env:s});f=x;const N=x.stdio[3];if(!N||!("end"in N))throw new Error("broker token pipe did not open");N.on("error",a=>E({event:"broker.token_pipe.failed",level:"error",node_id:e,error:a})),N.end(P),T(o),o=null;const I=Date.now(),ee=new Promise(a=>{let w=!1;const C=u(m=>{w||(w=!0,a(m))},"finish");x.once("exit",(m,p)=>{E({level:m===0?"info":"warn",event:"broker.exited",node_id:e,fields:{code:m,signal:p,uptime_ms:Date.now()-I}}),C({code:m,signal:p})}),x.once("error",m=>{E({level:"error",event:"broker.launch.failed",node_id:e,error:m,fields:{uptime_ms:Date.now()-I}}),C({code:null,signal:null})})});x.unref();const q={pid:x.pid??null,exited:ee};return q.pid!==null&&(r.register(e,q),c=!0,ee.then(a=>r.onChildExit(e,a,d))),q}catch(l){if(!c&&f?.pid!=null)try{f.kill("SIGTERM")}catch{}throw l}finally{c||r.releaseReservation(e),o!==null&&T(o)}},isAlive(e){return Z((typeof e=="string"?v(e):e)?.pi_pid)},teardown(e,n={}){try{return ke(Ee(e,n))}catch(t){return Promise.resolve({confirmed:!1,error:t instanceof Error?t:new Error(String(t))})}},signal(e,n){const t=v(e)?.pi_pid;if(t!=null)try{process.kill(t,n)}catch{}}};export{Yr as headlessBrokerHost,ve as isolatedBrokerLauncherAvailable,pr as preflightBrokerLaunch,Qr as requestBrokerTeardown};
@@ -1 +1 @@
1
- var q=Object.defineProperty;var a=(e,r)=>q(e,"name",{value:r,configurable:!0});import{envHomeOverride as L,envRuntimeHomeOverride as W,INBOX_BETWEEN_TURNS_ENV as G,scopeAllowed as R}from"../../shared/env.js";import{validateScopes as J}from"../scopes.js";import{activeGrant as A,terminalGrant as C}from"../grants/mirror.js";import{appendRunEvent as K}from"../runs/events.js";import{readConfig as I}from"../config.js";import{InputError as N}from"../io.js";import{ApiError as g}from"../../api/errors.js";import{assertGranteeRunsHere as X}from"./unsandboxed.js";import{randomBytes as z}from"node:crypto";import{existsSync as $}from"node:fs";import{resolve as Y,relative as V,isAbsolute as B,join as Q,sep as Z}from"node:path";import{appSpace as ee}from"../layout.js";import{createNode as ne,assertRunNotDeleting as re,conversationOwner as te,getNode as c,getRow as ie}from"../canvas/index.js";import{ensureAppSpace as oe}from"../spaces/space.js";import{LAUNCHER_SOCKET as F}from"../../launcher/spec.js";import{resolveInstallId as le}from"../canvas/install-id.js";import{isAppRun as ae}from"../canvas/app-run.js";import{canvasDbPath as se,contextDir as de}from"../canvas/paths.js";import{conversationIdFromPath as ue}from"../conversation-store/index.js";import{usage as M}from"../errors.js";import{assertProfileAvailableForBirth as ce}from"../profiles/deletion-reservation.js";import{loadExactProfileManifest as fe}from"../profiles/manifest.js";import{admitNodeCreate as pe}from"../command-hooks/lifecycle-create.js";import{getById as _e,nodeObject as E,ownerForGrantee as me}from"../graph/objects.js";import{recordActedOn as ge}from"../graph/edges.js";import{recordWatch as we,copyWatchesForFork as he}from"../graph/watches.js";import{publish as Re}from"../graph/events.js";let k;const P=new Map;function rn(e){k=e}a(rn,"setInstallId");function ve(){if(k!==void 0)return k;const e=se(),r=P.get(e);if(r!==void 0)return r;const o=le();return P.set(e,o),o}a(ve,"installId");function Ee(){return`${Date.now().toString(36)}-${z(4).toString("hex")}`}a(Ee,"newJobId");function ye(){return`${ve()}-${Ee()}`}a(ye,"newNodeId");const S=128,Ce=/^[a-z0-9-]+$/;class j extends Error{static{a(this,"NodeIdConflictError")}nodeId;constructor(r){super(`node already exists: ${r}`),this.nodeId=r,this.name="NodeIdConflictError"}}function T(e){if(e.length===0||e.length>S)throw M(`invalid --node-id '${e}': must be 1-${S} characters`,{next:`Pass an id-safe string (lowercase letters, digits, hyphens) of at most ${S} characters.`});if(!Ce.test(e))throw M(`invalid --node-id '${e}': must contain only lowercase letters, digits, and hyphens (e.g. 'remote42', 'my-node-1')`,{next:"Use only lowercase letters, digits, and hyphens."})}a(T,"validateNodeIdFormat");function tn(e){if(T(e),c(e)!==null)throw new j(e)}a(tn,"preflightNodeId");function Ie(e){if(T(e),c(e)!==null)throw new j(e);return e}a(Ie,"validatedExplicitNodeId");function on(e){let r=e;const o=new Set;for(;;){if(o.has(r))return r;o.add(r);const s=ie(r);if(s===null||s.parent==null)return r;r=s.parent}}a(on,"rootOfSpine");function ln(){const e=process.env;return{nodeId:e.CRTR_NODE_ID??null,kind:e.CRTR_KIND??null,mode:e.CRTR_MODE??null}}a(ln,"currentNodeContext");function Ne(e){return ae(e)}a(Ne,"inboxBetweenTurns");function an(e){const r={CRTR_NODE_ID:e.node_id,CRTR_KIND:e.kind,CRTR_MODE:e.mode,CRTR_LIFECYCLE:e.lifecycle,CRTR_NODE_CWD:e.cwd,CRTR_CONTEXT_DIR:de(e.node_id,e.grantee),CRTR_CYCLES:(e.cycles??0).toString(),CRTR_PROFILE_ID:e.profile_id??"",PI_CLAUDE_OAUTH_REINJECT_SCOPE:"never"};e.parent&&(r.CRTR_PARENT_NODE_ID=e.parent),e.run_id&&(r.CRTR_RUN_ID=e.run_id),Ne(e)&&(r[G]="1"),e.scopes!==null&&e.scopes!==void 0&&(r.CRTR_SCOPES=e.scopes.join(",")),r.CRTR_GRANTEE=e.grantee??"app:terminal";const o=L();o!==void 0&&o!==""&&(r.CRTR_HOME=o);const s=W();s!==void 0&&s!==""&&(r.CRTR_RUNTIME_HOME=s);const f=process.env.CRTR_SUBTREE;return f!==void 0&&f!==""&&(r.CRTR_SUBTREE=f),{...r,...e.launch?.env??{}}}a(an,"nodeEnv");function ke(e){const r=ue(e),o=r===null?null:te(r);return o===null?null:c(o)}a(ke,"forkSourceNode");async function sn(e){const r=e.nodeId!==void 0?(T(e.nodeId),e.nodeId):ye(),o=e.parent??null,s=o===null,f=e.spawnedBy??o,_=e.creator??null,x=e.mode??"base",y=e.lifecycle??(s?"resident":"terminal"),w=e.run===void 0?C():null,n={node_id:r,name:e.name??e.kind,description:e.description,cycles:0,created:new Date().toISOString(),cwd:e.cwd,host_kind:"broker",profile_id:e.profile_id??null,kind:e.kind,mode:x,lifecycle:y,persona_ack:{kind:e.kind,mode:x,lifecycle:y},status:"active",parent:o,spawned_by:f,creator:_,fork_from:e.forkFrom,fork_source_file:e.forkSourceFile,review_binding:e.reviewBinding,model_override:e.modelOverride??null,scopes:e.scopes??null,isolated:e.isolated===!0,grantee:e.run?.grantee??"app:terminal",sub:e.run?.sub??w?.sub??null,grant_id:e.run?.grant_id??w?.id??null,client_id:e.run?.client_id??null,metadata_json:s?e.run?.metadata_json??null:null,run_id:s?r:e.run?.run_id??null,event_cursor:s?e.run?.event_cursor??1:null,usage_json:s?e.run?.usage_json??JSON.stringify({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}):null,passive_default:e.passiveDefault??!1,...e.managedWorktree!==void 0?{managed_worktree:e.managedWorktree}:{},intent:null,deadline_at:e.deadlineAt??null,pi_session_id:null,pi_session_file:null,launch:e.launch},v=e.scopes===void 0?void 0:J(e.scopes),h=o===null?null:c(o);if(o!==null&&h===null)throw new Error(`cannot spawn under unknown parent node: ${o}`);if(h?.run_id&&c(h.run_id)?.deleting_at)throw new Error("run is deleting");const u=_===null?null:c(_);if(_!==null&&u===null)throw new Error(`cannot spawn from unknown creator node: ${_}`);if(u?.run_id&&c(u.run_id)?.deleting_at)throw new Error("run is deleting");const D=e.forkSourceFile==null?null:ke(e.forkSourceFile),O=e.replaces==null?null:c(e.replaces),m=h??D??O??u??(f===null?null:c(f)),b=[h,u,D,O,f===null?null:c(f)].some(t=>t?.isolated===!0||t?.run_id!=null&&c(t.run_id)?.isolated===!0);if(b&&e.isolated===!1)throw new g(403,"isolated_run","isolation cannot be turned off under an isolated run",{param:"isolated"});if(n.isolated=b||e.isolated===!0,m!==null){const t=u??m;if(u&&m.grantee&&u.grantee&&u.grantee!==m.grantee)throw new g(403,"other_app_run","cannot create under another app's node");const i=u&&u!==m?[m,u]:[m],d=[];for(const l of i){l.scopes!==null&&l.scopes!==void 0&&d.push(l.scopes);const p=l.grantee==="app:terminal"?C():l.grant_id?A(l.grant_id):null;p?d.push(p.resolved):l.grantee==="app:terminal"&&!I("user").runtime?.issuer||d.push([])}if(v!==void 0&&!v.every(l=>d.every(p=>R(p,l))))throw new N({error:"unknown_scope",message:"child scopes must be covered by the creating node, its source and their current grants",received:v,field:"scopes",next:"Request only scopes the creating node currently holds."});if(n.scopes=v??(i.every(l=>l.scopes==null)&&i.every(l=>l.grant_id===t.grant_id&&l.sub===t.sub)?null:i.length===1?m.scopes??null:[...new Set(d.flat().filter(l=>d.every(p=>R(p,l))))]),n.scopes?.includes("crtr:act")&&!R(n.scopes,"crtr:llm"))throw new N({error:"invalid_scope",message:"crtr:act requires crtr:llm",received:n.scopes,field:"scopes",next:"Include crtr:llm or omit crtr:act."});n.grantee=t.grantee==="user:local"?"app:terminal":t.grantee,n.sub=t.sub??(n.grantee==="app:terminal"?w?.sub:null)??null,n.grant_id=t.grant_id??(n.grantee==="app:terminal"?w?.id:null)??null,n.client_id=t.client_id,n.run_id=s?r:t.run_id??null}if(X(n.grantee),e.run===void 0&&n.grantee==="app:terminal"&&I("user").runtime?.issuer&&w===null)throw new g(403,"scope_missing","terminal grant is not active");if(n.isolated){if(process.platform!=="linux"||!$(F))throw new g(400,"isolated_unavailable","isolated nodes require the Linux root launcher");if(e.managedWorktree!==void 0)throw new g(400,"invalid_request","isolated nodes cannot use a worktree",{param:"worktree"});const t=Q(ee(n.grantee??"app:terminal").nodes,r),i=V(t,Y(n.cwd));if(!B(n.cwd)||i===".."||i.startsWith(`..${Z}`)||B(i))throw new g(400,"invalid_request","isolated node cwd must be inside its own folder",{param:"cwd"})}if(n.profile_id){const t=fe(n.profile_id).manifest.grantee??"app:terminal";if(t!==n.grantee){const i=`crtr:memory:rw:${t}:profile:${n.profile_id}`,d=n.grantee==="app:terminal"?C():n.grant_id?A(n.grant_id):null;if(!(n.grantee==="app:terminal"&&!I("user").runtime?.issuer)&&(!d||d.grantee!==n.grantee||!R(d.resolved,i))||!R(n.scopes,i))throw new N({error:"scope_missing",message:`missing scope ${i}`,received:n.profile_id,field:"profile",next:"Select a profile covered by this run and its current grant."})}}if(ce(n.profile_id??null),!n.grantee)throw new Error("node creation requires an app");const H=await pe({kind:n.kind,mode:n.mode,cwd:n.cwd,profile:n.profile_id??null,root:s,parentId:o,requestingNodeId:_,lifecycle:y,replaces:e.replaces??null},n.grantee),U=a((t,i)=>{const d=E(t,i.node_id),l=_??o??e.actor??f??null,p=l!==null&&_e(t,l)?.type==="node"?E(t,l):me(t,e.run===void 0?"user:local":i.grantee);ge(t,p,d),o!==null&&e.subscribe!==!1&&we(t,E(t,o),d,"spawn"),e.forkSourceNode&&he(t,E(t,e.forkSourceNode),d),Re(t,{event:"created",object:d,actor:p,attrs:{name:i.name,kind:i.kind,creator:_??null}})},"recordBirth");try{e.nodeId!==void 0&&Ie(e.nodeId),await e.beforeCreate?.(),(process.platform!=="linux"||$(F))&&await oe(n.grantee??"app:terminal"),ne(n,{outcomeDelivery:e.outcomeDelivery,withinCreate:a((t,i)=>{i.run_id&&i.run_id!==i.node_id&&re(t,i.run_id),s&&e.run===void 0&&K(t,r,r,"run.started",{scopes:i.scopes??null,payer:"user",usage:JSON.parse(i.usage_json)}),e.withinCreate?.(t,i),U(t,i)},"withinCreate")})}finally{H()}return n}a(sn,"spawnNode");export{j as NodeIdConflictError,ln as currentNodeContext,ke as forkSourceNode,Ne as inboxBetweenTurns,Ee as newJobId,ye as newNodeId,an as nodeEnv,tn as preflightNodeId,on as rootOfSpine,rn as setInstallId,sn as spawnNode};
1
+ var j=Object.defineProperty;var a=(e,r)=>j(e,"name",{value:r,configurable:!0});import{envHomeOverride as q,envRuntimeHomeOverride as W,INBOX_BETWEEN_TURNS_ENV as G,SCOPES_FILE_ENV as J,scopeAllowed as v}from"../../shared/env.js";import{validateScopes as K}from"../scopes.js";import{activeGrant as A,terminalGrant as I}from"../grants/mirror.js";import{appendRunEvent as X}from"../runs/events.js";import{readConfig as N}from"../config.js";import{InputError as C}from"../io.js";import{ApiError as g}from"../../api/errors.js";import{assertGranteeRunsHere as z}from"./unsandboxed.js";import{randomBytes as V}from"node:crypto";import{existsSync as $}from"node:fs";import{resolve as Y,relative as Q,isAbsolute as F,join as Z,sep as ee}from"node:path";import{appSpace as ne}from"../layout.js";import{scopesFilePath as re}from"../canvas/paths.js";import{createNode as te,assertRunNotDeleting as ie,conversationOwner as oe,getNode as c,getRow as le}from"../canvas/index.js";import{ensureAppSpace as ae}from"../spaces/space.js";import{LAUNCHER_SOCKET as B}from"../../launcher/spec.js";import{resolveInstallId as se}from"../canvas/install-id.js";import{isAppRun as de}from"../canvas/app-run.js";import{canvasDbPath as ue,contextDir as ce}from"../canvas/paths.js";import{conversationIdFromPath as fe}from"../conversation-store/index.js";import{usage as M}from"../errors.js";import{assertProfileAvailableForBirth as pe}from"../profiles/deletion-reservation.js";import{loadExactProfileManifest as _e}from"../profiles/manifest.js";import{admitNodeCreate as me}from"../command-hooks/lifecycle-create.js";import{getById as ge,nodeObject as E,ownerForGrantee as we}from"../graph/objects.js";import{recordActedOn as he}from"../graph/edges.js";import{recordWatch as ve,copyWatchesForFork as Re}from"../graph/watches.js";import{publish as Ee}from"../graph/events.js";let k;const P=new Map;function ln(e){k=e}a(ln,"setInstallId");function ye(){if(k!==void 0)return k;const e=ue(),r=P.get(e);if(r!==void 0)return r;const o=se();return P.set(e,o),o}a(ye,"installId");function Ie(){return`${Date.now().toString(36)}-${V(4).toString("hex")}`}a(Ie,"newJobId");function Ne(){return`${ye()}-${Ie()}`}a(Ne,"newNodeId");const S=128,Ce=/^[a-z0-9-]+$/;class H extends Error{static{a(this,"NodeIdConflictError")}nodeId;constructor(r){super(`node already exists: ${r}`),this.nodeId=r,this.name="NodeIdConflictError"}}function T(e){if(e.length===0||e.length>S)throw M(`invalid --node-id '${e}': must be 1-${S} characters`,{next:`Pass an id-safe string (lowercase letters, digits, hyphens) of at most ${S} characters.`});if(!Ce.test(e))throw M(`invalid --node-id '${e}': must contain only lowercase letters, digits, and hyphens (e.g. 'remote42', 'my-node-1')`,{next:"Use only lowercase letters, digits, and hyphens."})}a(T,"validateNodeIdFormat");function an(e){if(T(e),c(e)!==null)throw new H(e)}a(an,"preflightNodeId");function ke(e){if(T(e),c(e)!==null)throw new H(e);return e}a(ke,"validatedExplicitNodeId");function sn(e){let r=e;const o=new Set;for(;;){if(o.has(r))return r;o.add(r);const s=le(r);if(s===null||s.parent==null)return r;r=s.parent}}a(sn,"rootOfSpine");function dn(){const e=process.env;return{nodeId:e.CRTR_NODE_ID??null,kind:e.CRTR_KIND??null,mode:e.CRTR_MODE??null}}a(dn,"currentNodeContext");function Se(e){return de(e)}a(Se,"inboxBetweenTurns");function un(e){const r={CRTR_NODE_ID:e.node_id,CRTR_KIND:e.kind,CRTR_MODE:e.mode,CRTR_LIFECYCLE:e.lifecycle,CRTR_NODE_CWD:e.cwd,CRTR_CONTEXT_DIR:ce(e.node_id,e.grantee),CRTR_CYCLES:(e.cycles??0).toString(),CRTR_PROFILE_ID:e.profile_id??"",PI_CLAUDE_OAUTH_REINJECT_SCOPE:"never"};e.parent&&(r.CRTR_PARENT_NODE_ID=e.parent),e.run_id&&(r.CRTR_RUN_ID=e.run_id),Se(e)&&(r[G]="1"),e.scopes!==null&&e.scopes!==void 0&&(r[J]=re(e.node_id)),r.CRTR_GRANTEE=e.grantee??"app:terminal";const o=q();o!==void 0&&o!==""&&(r.CRTR_HOME=o);const s=W();s!==void 0&&s!==""&&(r.CRTR_RUNTIME_HOME=s);const f=process.env.CRTR_SUBTREE;return f!==void 0&&f!==""&&(r.CRTR_SUBTREE=f),{...r,...e.launch?.env??{}}}a(un,"nodeEnv");function Te(e){const r=fe(e),o=r===null?null:oe(r);return o===null?null:c(o)}a(Te,"forkSourceNode");async function cn(e){const r=e.nodeId!==void 0?(T(e.nodeId),e.nodeId):Ne(),o=e.parent??null,s=o===null,f=e.spawnedBy??o,_=e.creator??null,x=e.mode??"base",y=e.lifecycle??(s?"resident":"terminal"),w=e.run===void 0?I():null,n={node_id:r,name:e.name??e.kind,description:e.description,cycles:0,created:new Date().toISOString(),cwd:e.cwd,host_kind:"broker",profile_id:e.profile_id??null,kind:e.kind,mode:x,lifecycle:y,persona_ack:{kind:e.kind,mode:x,lifecycle:y},status:"active",parent:o,spawned_by:f,creator:_,fork_from:e.forkFrom,fork_source_file:e.forkSourceFile,review_binding:e.reviewBinding,model_override:e.modelOverride??null,scopes:e.scopes??null,isolated:e.isolated===!0,grantee:e.run?.grantee??"app:terminal",sub:e.run?.sub??w?.sub??null,grant_id:e.run?.grant_id??w?.id??null,client_id:e.run?.client_id??null,metadata_json:s?e.run?.metadata_json??null:null,run_id:s?r:e.run?.run_id??null,event_cursor:s?e.run?.event_cursor??1:null,usage_json:s?e.run?.usage_json??JSON.stringify({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}):null,passive_default:e.passiveDefault??!1,...e.managedWorktree!==void 0?{managed_worktree:e.managedWorktree}:{},intent:null,deadline_at:e.deadlineAt??null,pi_session_id:null,pi_session_file:null,launch:e.launch},R=e.scopes===void 0?void 0:K(e.scopes),h=o===null?null:c(o);if(o!==null&&h===null)throw new Error(`cannot spawn under unknown parent node: ${o}`);if(h?.run_id&&c(h.run_id)?.deleting_at)throw new Error("run is deleting");const u=_===null?null:c(_);if(_!==null&&u===null)throw new Error(`cannot spawn from unknown creator node: ${_}`);if(u?.run_id&&c(u.run_id)?.deleting_at)throw new Error("run is deleting");const D=e.forkSourceFile==null?null:Te(e.forkSourceFile),O=e.replaces==null?null:c(e.replaces),m=h??D??O??u??(f===null?null:c(f)),b=[h,u,D,O,f===null?null:c(f)].some(t=>t?.isolated===!0||t?.run_id!=null&&c(t.run_id)?.isolated===!0);if(b&&e.isolated===!1)throw new g(403,"isolated_run","isolation cannot be turned off under an isolated run",{param:"isolated"});if(n.isolated=b||e.isolated===!0,m!==null){const t=u??m;if(u&&m.grantee&&u.grantee&&u.grantee!==m.grantee)throw new g(403,"other_app_run","cannot create under another app's node");const i=u&&u!==m?[m,u]:[m],d=[];for(const l of i){l.scopes!==null&&l.scopes!==void 0&&d.push(l.scopes);const p=l.grantee==="app:terminal"?I():l.grant_id?A(l.grant_id):null;p?d.push(p.resolved):l.grantee==="app:terminal"&&!N("user").runtime?.issuer||d.push([])}if(R!==void 0&&!R.every(l=>d.every(p=>v(p,l))))throw new C({error:"unknown_scope",message:"child scopes must be covered by the creating node, its source and their current grants",received:R,field:"scopes",next:"Request only scopes the creating node currently holds."});if(n.scopes=R??(i.every(l=>l.scopes==null)&&i.every(l=>l.grant_id===t.grant_id&&l.sub===t.sub)?null:i.length===1?m.scopes??null:[...new Set(d.flat().filter(l=>d.every(p=>v(p,l))))]),n.scopes?.includes("crtr:act")&&!v(n.scopes,"crtr:llm"))throw new C({error:"invalid_scope",message:"crtr:act requires crtr:llm",received:n.scopes,field:"scopes",next:"Include crtr:llm or omit crtr:act."});n.grantee=t.grantee==="user:local"?"app:terminal":t.grantee,n.sub=t.sub??(n.grantee==="app:terminal"?w?.sub:null)??null,n.grant_id=t.grant_id??(n.grantee==="app:terminal"?w?.id:null)??null,n.client_id=t.client_id,n.run_id=s?r:t.run_id??null}if(z(n.grantee),e.run===void 0&&n.grantee==="app:terminal"&&N("user").runtime?.issuer&&w===null)throw new g(403,"scope_missing","terminal grant is not active");if(n.isolated){if(process.platform!=="linux"||!$(B))throw new g(400,"isolated_unavailable","isolated nodes require the Linux root launcher");if(e.managedWorktree!==void 0)throw new g(400,"invalid_request","isolated nodes cannot use a worktree",{param:"worktree"});const t=Z(ne(n.grantee??"app:terminal").nodes,r),i=Q(t,Y(n.cwd));if(!F(n.cwd)||i===".."||i.startsWith(`..${ee}`)||F(i))throw new g(400,"invalid_request","isolated node cwd must be inside its own folder",{param:"cwd"})}if(n.profile_id){const t=_e(n.profile_id).manifest.grantee??"app:terminal";if(t!==n.grantee){const i=`crtr:memory:rw:${t}:profile:${n.profile_id}`,d=n.grantee==="app:terminal"?I():n.grant_id?A(n.grant_id):null;if(!(n.grantee==="app:terminal"&&!N("user").runtime?.issuer)&&(!d||d.grantee!==n.grantee||!v(d.resolved,i))||!v(n.scopes,i))throw new C({error:"scope_missing",message:`missing scope ${i}`,received:n.profile_id,field:"profile",next:"Select a profile covered by this run and its current grant."})}}if(pe(n.profile_id??null),!n.grantee)throw new Error("node creation requires an app");const L=await me({kind:n.kind,mode:n.mode,cwd:n.cwd,profile:n.profile_id??null,root:s,parentId:o,requestingNodeId:_,lifecycle:y,replaces:e.replaces??null},n.grantee),U=a((t,i)=>{const d=E(t,i.node_id),l=_??o??e.actor??f??null,p=l!==null&&ge(t,l)?.type==="node"?E(t,l):we(t,e.run===void 0?"user:local":i.grantee);he(t,p,d),o!==null&&e.subscribe!==!1&&ve(t,E(t,o),d,"spawn"),e.forkSourceNode&&Re(t,E(t,e.forkSourceNode),d),Ee(t,{event:"created",object:d,actor:p,attrs:{name:i.name,kind:i.kind,creator:_??null}})},"recordBirth");try{e.nodeId!==void 0&&ke(e.nodeId),await e.beforeCreate?.(),(process.platform!=="linux"||$(B))&&await ae(n.grantee??"app:terminal"),te(n,{outcomeDelivery:e.outcomeDelivery,withinCreate:a((t,i)=>{i.run_id&&i.run_id!==i.node_id&&ie(t,i.run_id),s&&e.run===void 0&&X(t,r,r,"run.started",{scopes:i.scopes??null,payer:"user",usage:JSON.parse(i.usage_json)}),e.withinCreate?.(t,i),U(t,i)},"withinCreate")})}finally{L()}return n}a(cn,"spawnNode");export{H as NodeIdConflictError,dn as currentNodeContext,Te as forkSourceNode,Se as inboxBetweenTurns,Ie as newJobId,Ne as newNodeId,un as nodeEnv,an as preflightNodeId,sn as rootOfSpine,ln as setInstallId,cn as spawnNode};