@north-light/cli 0.3.462 → 0.3.464

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.
Files changed (51) hide show
  1. package/dist/builtin-memory/crouter-concepts/scopes-and-trust.md +2 -2
  2. package/dist/builtin-memory/crouter-plugin/bundles-and-memory.md +22 -1
  3. package/dist/builtin-memory/crouter-plugin/deploying.md +12 -0
  4. package/dist/builtin-memory/internal/agent-shaping.md +2 -2
  5. package/dist/builtin-memory/internal/memory-loading.md +5 -5
  6. package/dist/clients/attach/viewer.js +548 -548
  7. package/dist/commands/doc/shared.js +2 -2
  8. package/dist/commands/profile/list.js +1 -1
  9. package/dist/commands/profile/new.js +1 -1
  10. package/dist/commands/profile/project.js +1 -1
  11. package/dist/commands/profile/show.js +1 -1
  12. package/dist/core/feed/feed.js +3 -3
  13. package/dist/core/graph/access.d.ts +4 -0
  14. package/dist/core/graph/access.js +3 -3
  15. package/dist/core/graph/documents.d.ts +5 -2
  16. package/dist/core/graph/documents.js +7 -7
  17. package/dist/core/graph/names.d.ts +1 -1
  18. package/dist/core/graph/names.js +3 -3
  19. package/dist/core/graph/reader.d.ts +8 -0
  20. package/dist/core/graph/reader.js +3 -3
  21. package/dist/core/graph/repo-sync/exchange.d.ts +8 -2
  22. package/dist/core/graph/repo-sync/exchange.js +2 -2
  23. package/dist/core/graph/repo-sync/ref-format.d.ts +2 -2
  24. package/dist/core/graph/repo-sync/ref-format.js +2 -2
  25. package/dist/core/graph/repo-sync/sync.js +2 -2
  26. package/dist/core/graph/spaces/app.js +1 -1
  27. package/dist/core/graph/spaces/own-memory.d.ts +18 -0
  28. package/dist/core/graph/spaces/own-memory.js +1 -0
  29. package/dist/core/graph/spaces/repo.d.ts +2 -1
  30. package/dist/core/graph/spaces/repo.js +1 -1
  31. package/dist/core/graph/types.d.ts +10 -1
  32. package/dist/core/runtime/nodes.js +1 -1
  33. package/dist/core/runtime/roadmap.js +3 -3
  34. package/dist/core/scopes.d.ts +3 -0
  35. package/dist/core/scopes.js +1 -1
  36. package/dist/core/substrate/delivery/corpus.d.ts +1 -1
  37. package/dist/core/substrate/delivery/corpus.js +1 -1
  38. package/dist/core/substrate/delivery/deliver.js +3 -3
  39. package/dist/core/substrate/subject-fields.d.ts +4 -2
  40. package/dist/core/substrate/subject.js +1 -1
  41. package/dist/daemon/api/handlers/docs.js +1 -1
  42. package/dist/daemon/api/handlers/profiles.d.ts +3 -0
  43. package/dist/daemon/api/handlers/profiles.js +1 -1
  44. package/dist/daemon/api/map.js +2 -2
  45. package/dist/daemon/cron/authority.js +1 -1
  46. package/docs/concepts/scopes-and-trust.md +2 -2
  47. package/docs/plugin/bundles-and-memory.md +23 -1
  48. package/docs/plugin/deploying.md +12 -0
  49. package/package.json +2 -2
  50. package/packages/crouter-identity/package.json +1 -1
  51. package/runtime.lock.json +12 -12
@@ -1,2 +1,2 @@
1
- var $=Object.defineProperty;var a=(n,r)=>$(n,"name",{value:r,configurable:!0});import{createHash as O}from"node:crypto";const D=a(n=>`docs/${n}.json`,"docPath"),N=["name","kind","privacy","preview","summary","why_it_exists","unlisted","lint_ignore","extensions","deleted_at","rules"];function R(n){return O("sha256").update(n,"utf8").digest("hex")}a(R,"sha256");function h(n){if(Array.isArray(n))return n.map(h);if(n&&typeof n=="object"){const r={};for(const o of Object.keys(n).sort())r[o]=h(n[o]);return r}return n}a(h,"sortKeys");const v=a(n=>JSON.stringify(h(n??null)),"stable");function S(n,r){return n.number-r.number||(n.rev_id<r.rev_id?-1:n.rev_id>r.rev_id?1:0)}a(S,"byRevision");function j(n){const r={...n,rules:[...n.rules].sort((o,t)=>o.position-t.position),revisions:[...n.revisions].map(o=>({...o,parents:[...o.parents].sort()})).sort(S)};return`${JSON.stringify(h(r),null,2)}
2
- `}a(j,"serializeDoc");const E=new Set(["boot","workspace-open","file-read","document-read","command","pre-command","slash-command"]),I=new Set(["name","preview","content"]),p=a(n=>typeof n=="string","isStr"),g=a(n=>n===null||typeof n=="string","isStrOrNull");function x(n,r){let o;try{o=JSON.parse(r)}catch(e){return{ok:!1,reason:`not JSON: ${e.message}`}}if(!o||typeof o!="object"||Array.isArray(o))return{ok:!1,reason:"not an object"};const t=o;if(t.privacy!=="public")return{ok:!1,reason:`privacy is ${String(t.privacy)}; a repo holds only public documents`};if(!p(t.id)||D(t.id)!==n)return{ok:!1,reason:`id ${String(t.id)} does not match file ${n}`};for(const e of["name","kind","preview","created_at"])if(!p(t[e]))return{ok:!1,reason:`${e} is not a string`};for(const e of["summary","why_it_exists","deleted_at"])if(!g(t[e]))return{ok:!1,reason:`${e} is not a string or null`};if(typeof t.unlisted!="boolean")return{ok:!1,reason:"unlisted is not a boolean"};if(!Array.isArray(t.rules))return{ok:!1,reason:"rules is not an array"};for(const e of t.rules)if(!e||typeof e.position!="number"||!E.has(e.on)||!I.has(e.deliver))return{ok:!1,reason:"malformed rule"};if(!Array.isArray(t.revisions)||t.revisions.length===0)return{ok:!1,reason:"no revisions"};const d=new Set;for(const e of t.revisions){if(!e||!p(e.rev_id)||typeof e.number!="number"||!p(e.body)||!p(e.body_hash)||!p(e.created_at)||!g(e.author)||!g(e.rationale)||!Array.isArray(e.parents)||!e.parents.every(p)||!Array.isArray(e.links))return{ok:!1,reason:"malformed revision"};if(d.has(e.rev_id))return{ok:!1,reason:`revision ${e.rev_id} appears twice`};if(d.add(e.rev_id),R(e.body)!==e.body_hash)return{ok:!1,reason:`revision ${e.rev_id} body does not match body_hash`}}return{ok:!0,doc:t}}a(x,"parseDoc");function A(...n){const r=new Map;for(const o of n)for(const t of o?.revisions??[])r.has(t.rev_id)||r.set(t.rev_id,t);return[...r.values()].sort(S)}a(A,"unionRevisions");function M(n,r,o,t=new Date().toISOString()){if(!r&&!o)return null;if(!r||!o){const e=r??o;return n?{...e,deleted_at:e.deleted_at??t,revisions:A(o,r,n)}:e}const d={...o,revisions:A(o,r,n)};for(const e of N){const u=v(r[e]),m=v(o[e]);if(u===m)continue;(n?v(n[e]):void 0)===m&&(d[e]=r[e])}return d}a(M,"mergeDoc");function L(n,r,o,t=new Date().toISOString()){const d=new Map,e=[],u=new Map,m=[...new Set([...n.keys(),...r.keys(),...o.keys()])].sort();for(const i of m){const s=[];let l=null;for(const c of[n,r,o]){const y=c.get(i);if(y===void 0){s.push(null);continue}const k=x(i,y);k.ok?s.push(k.doc):(l??=k.reason,s.push(null))}if(l!==null){e.push({path:i,reason:l});const c=r.get(i);c!==void 0&&d.set(i,c);continue}const f=M(s[0],s[1],s[2],t);f&&u.set(i,f)}const b=a(i=>{const s=o.get(i);if(s===void 0)return null;const l=x(i,s);return l.ok&&l.doc.deleted_at===null?l.doc.name:null},"remoteNameOf"),_=new Map;for(const[i,s]of u)s.deleted_at===null&&_.set(s.name,[..._.get(s.name)??[],i]);const w=[];for(const[i,s]of _){if(s.length<2)continue;const l=s.find(f=>b(f)===i)??[...s].sort((f,c)=>u.get(f).id.localeCompare(u.get(c).id))[0];for(const f of s){if(f===l)continue;const c=u.get(f),y=`${i}-${c.id.slice(0,8)}`;u.set(f,{...c,name:y}),w.push({id:c.id,from:i,to:y})}}for(const[i,s]of u)d.set(i,j(s));return{tree:new Map([...d].sort(([i],[s])=>i.localeCompare(s))),renamed:w,refused:e}}a(L,"mergeTrees");export{N as RECORD_FIELDS,D as docPath,M as mergeDoc,L as mergeTrees,x as parseDoc,j as serializeDoc,R as sha256};
1
+ var $=Object.defineProperty;var a=(n,t)=>$(n,"name",{value:t,configurable:!0});import{createHash as O}from"node:crypto";const D=a(n=>`docs/${n}.json`,"docPath"),N=["name","kind","privacy","preview","summary","why_it_exists","unlisted","lint_ignore","extensions","deleted_at","rules"];function R(n){return O("sha256").update(n,"utf8").digest("hex")}a(R,"sha256");function _(n){if(Array.isArray(n))return n.map(_);if(n&&typeof n=="object"){const t={};for(const r of Object.keys(n).sort())t[r]=_(n[r]);return t}return n}a(_,"sortKeys");const v=a(n=>JSON.stringify(_(n??null)),"stable");function b(n,t){return n.number-t.number||(n.rev_id<t.rev_id?-1:n.rev_id>t.rev_id?1:0)}a(b,"byRevision");function j(n){const t={...n,rules:[...n.rules].sort((r,o)=>r.position-o.position),revisions:[...n.revisions].map(r=>({...r,parents:[...r.parents].sort()})).sort(b)};return`${JSON.stringify(_(t),null,2)}
2
+ `}a(j,"serializeDoc");const E=new Set(["boot","workspace-open","file-read","document-read","command","pre-command","slash-command"]),I=new Set(["name","preview","content"]),p=a(n=>typeof n=="string","isStr"),w=a(n=>n===null||typeof n=="string","isStrOrNull");function x(n,t){let r;try{r=JSON.parse(t)}catch(e){return{ok:!1,reason:`not JSON: ${e.message}`}}if(!r||typeof r!="object"||Array.isArray(r))return{ok:!1,reason:"not an object"};const o=r;if(o.privacy!=="public")return{ok:!1,reason:`privacy is ${String(o.privacy)}; a repo holds only public documents`};if(!p(o.id)||D(o.id)!==n)return{ok:!1,reason:`id ${String(o.id)} does not match file ${n}`};for(const e of["name","kind","preview","created_at"])if(!p(o[e]))return{ok:!1,reason:`${e} is not a string`};for(const e of["summary","why_it_exists","deleted_at"])if(!w(o[e]))return{ok:!1,reason:`${e} is not a string or null`};if(typeof o.unlisted!="boolean")return{ok:!1,reason:"unlisted is not a boolean"};if(!Array.isArray(o.rules))return{ok:!1,reason:"rules is not an array"};for(const e of o.rules)if(!e||typeof e.position!="number"||!E.has(e.on)||!I.has(e.deliver))return{ok:!1,reason:"malformed rule"};if(!Array.isArray(o.revisions)||o.revisions.length===0)return{ok:!1,reason:"no revisions"};const d=new Set;for(const e of o.revisions){if(!e||!p(e.rev_id)||typeof e.number!="number"||!p(e.body)||!p(e.body_hash)||!p(e.created_at)||!w(e.author)||!w(e.rationale)||!Array.isArray(e.parents)||!e.parents.every(p)||!Array.isArray(e.links))return{ok:!1,reason:"malformed revision"};if(d.has(e.rev_id))return{ok:!1,reason:`revision ${e.rev_id} appears twice`};if(d.add(e.rev_id),R(e.body)!==e.body_hash)return{ok:!1,reason:`revision ${e.rev_id} body does not match body_hash`}}return{ok:!0,doc:o}}a(x,"parseDoc");function A(...n){const t=new Map;for(const r of n)for(const o of r?.revisions??[])t.has(o.rev_id)||t.set(o.rev_id,o);return[...t.values()].sort(b)}a(A,"unionRevisions");function M(n,t,r,o=new Date().toISOString()){if(!t&&!r)return null;if(!t||!r){const e=t??r;return n?{...e,deleted_at:e.deleted_at??o,revisions:A(r,t,n)}:e}const d={...r,revisions:A(r,t,n)};for(const e of N){const c=v(t[e]),m=v(r[e]);if(c===m)continue;const h=n?v(n[e]):void 0;if(e==="deleted_at"&&n?.deleted_at!=null&&t.deleted_at===null!=(r.deleted_at===null)){d.deleted_at=null,d.rules=(t.deleted_at===null?t:r).rules;continue}h===m&&(d[e]=t[e])}return d}a(M,"mergeDoc");function L(n,t,r,o=new Date().toISOString()){const d=new Map,e=[],c=new Map,m=[...new Set([...n.keys(),...t.keys(),...r.keys()])].sort();for(const i of m){const s=[];let l=null;for(const u of[n,t,r]){const y=u.get(i);if(y===void 0){s.push(null);continue}const g=x(i,y);g.ok?s.push(g.doc):(l??=g.reason,s.push(null))}if(l!==null){e.push({path:i,reason:l});const u=t.get(i)??r.get(i);u!==void 0&&d.set(i,u);continue}const f=M(s[0],s[1],s[2],o);f&&c.set(i,f)}const h=a(i=>{const s=r.get(i);if(s===void 0)return null;const l=x(i,s);return l.ok&&l.doc.deleted_at===null?l.doc.name:null},"remoteNameOf"),k=new Map;for(const[i,s]of c)s.deleted_at===null&&k.set(s.name,[...k.get(s.name)??[],i]);const S=[];for(const[i,s]of k){if(s.length<2)continue;const l=s.find(f=>h(f)===i)??[...s].sort((f,u)=>c.get(f).id.localeCompare(c.get(u).id))[0];for(const f of s){if(f===l)continue;const u=c.get(f),y=`${i}-${u.id.slice(0,8)}`;c.set(f,{...u,name:y}),S.push({id:u.id,from:i,to:y})}}for(const[i,s]of c)d.set(i,j(s));return{tree:new Map([...d].sort(([i],[s])=>i.localeCompare(s))),renamed:S,refused:e}}a(L,"mergeTrees");export{N as RECORD_FIELDS,D as docPath,M as mergeDoc,L as mergeTrees,x as parseDoc,j as serializeDoc,R as sha256};
@@ -1,2 +1,2 @@
1
- var N=Object.defineProperty;var s=(t,r)=>N(t,"name",{value:r,configurable:!0});import{prepared as R}from"../../sqlite-statements.js";import{existsSync as D}from"node:fs";import{readFile as z,writeFile as B}from"node:fs/promises";import{join as M}from"node:path";import{emitEvent as K}from"../../events/emit.js";import{exportRepo as q,importTree as G}from"./exchange.js";import{checkoutChain as J}from"./identity.js";import{checkoutsForCwd as Q,reposForCwd as V}from"./repos.js";import{commitTree as I,fetchDocs as X,FIRST_FETCH_MARKER as Y,isAncestor as L,mergeBase as Z,openMirror as k,pushDocs as ee,readTree as p,remoteDocsTip as te,resolveRef as w,setRef as h}from"./mirror.js";import{mergeTrees as re}from"./ref-format.js";const ne=2e4,A=3,O="remote-url",l="refs/crtr/docs",oe="refs/crtr/remote-docs",g="refs/crtr/imported";function y(t){try{K(t)}catch{}}s(y,"emitEvent");let T=null;const S=new Map,H=new Set;function Re(t,r){T={lifecycle:t,db:r}}s(Re,"startRepoSync");function U(t,r){return r.type!=="repo"?!1:R(t,"SELECT 1 FROM identities WHERE object = ? AND scheme = 'git'").get(r.rowid)!==void 0}s(U,"gitScheme");async function W(t,r){const e=new Map;for(const f of J(r))f.ok&&f.scheme==="git"&&e.set(f.externalId,f.remoteUrl);if(e.size===0)return;const i=T.db(),o=R(i,"SELECT external_id FROM identities WHERE object = ? AND scheme = 'git'");for(const f of t){const a=o.get(f.rowid),c=a&&e.get(a.external_id);if(!c)continue;const n=await k(f.id);await B(M(n.dir,O),`${c}
2
- `)}}s(W,"recordRemoteUrls");async function se(t,r){const i=E().db(),o=t.filter(n=>U(i,n));if(o.length===0)return;await W(o,r);const f=[];for(const n of o){const d=!H.has(n.id)&&!D(M((await k(n.id)).dir,Y)),u=v(n);d&&u&&f.push(u)}if(f.length===0)return;let a;const c=new Promise(n=>{a=setTimeout(n,ne)});await Promise.race([Promise.all(f),c]),clearTimeout(a)}s(se,"noteNodeWorking");async function Me(t){try{const r=E().db(),e=R(r,"SELECT cwd FROM nodes WHERE node_id = ?").get(t);if(!e)return;const i=ie(r,()=>V(r,e.cwd));i.length>0&&await se(i,e.cwd)}catch(r){y({level:"warn",event:"repo_sync.failed",outcome:"failed",error:r,node_id:t,fields:{step:"fetch",reason:`the boot trigger failed: ${r instanceof Error?r.message:String(r)}`}})}}s(Me,"noteNodeBooting");function ie(t,r){if(t.isTransaction)return r();t.exec("BEGIN IMMEDIATE");try{const e=r();return t.exec("COMMIT"),e}catch(e){throw t.exec("ROLLBACK"),e}}s(ie,"inWrite");function ke(t,r){const e=E();if(!U(e.db(),t))return;if(r===void 0){v(t);return}const i=W([t],r).then(()=>{v(t)},o=>{y({level:"error",event:"repo_sync.failed",error:o,fields:{repo:t.name,step:"fetch",reason:"recording the remote URL failed"}})});e.lifecycle.registerDetached(i)}s(ke,"noteRepoDocumentsChanged");function E(){if(!T)throw new Error("repo sync is not started: crtrd must call startRepoSync first");return T}s(E,"requireEngine");function v(t){const r=E(),e=S.get(t.id)??{running:null,again:!1};if(S.set(t.id,e),e.running)return e.again=!0,e.running.then(()=>S.get(t.id)?.running??null).then(o=>o??{ok:!0,pushed:!1,imported:$()});if(!r.lifecycle.acceptsDetachedWork())return null;H.add(t.id);const i=ae(t).then(o=>(P(t,o),o),o=>{const f={ok:!1,step:"export",reason:o instanceof Error?o.message:String(o)};return P(t,f),f}).finally(()=>{e.running=null,e.again&&(e.again=!1,v(t))});return e.running=i,r.lifecycle.registerDetached(i.then(()=>{})),i}s(v,"schedule");function P(t,r){if(r.ok){const e=r.imported;y({level:"info",event:"repo_sync.completed",outcome:"succeeded",fields:{repo:t.name,pushed:r.pushed,created:e.created,revisions:e.revisions,deleted:e.deleted,needs_merge:e.needsMerge,refused:e.refused}})}else y({level:"warn",event:"repo_sync.failed",outcome:"failed",fields:{repo:t.name,step:r.step,reason:r.reason}})}s(P,"report");function $(){return{created:0,revisions:0,deleted:0,needsMerge:[],refused:[]}}s($,"emptyImport");class x extends Error{static{s(this,"StepError")}step;constructor(r,e){super(e),this.step=r}}async function _(t,r){try{return await r()}catch(e){throw e instanceof x?e:new x(t,e instanceof Error?e.message:String(e))}}s(_,"step");async function ae(t){const r=E();try{const e=await k(t.id),i=await ce(e);if(i===null)return{ok:!1,step:"fetch",reason:`no checkout of ${t.name} with an origin remote has been seen by this runtime`};let o=$();const f=s(()=>_("import",async()=>{const a=await w(e,l),c=await w(e,g);if(a===null||c===a)return;const n=c?await p(e,g):new Map,d=r.db(),u=G(d,t,n,await p(e,l),m=>Q(d,m));o={created:o.created+u.created,revisions:o.revisions+u.revisions,deleted:o.deleted+u.deleted,needsMerge:[...new Set([...o.needsMerge,...u.needsMerge])],refused:[...o.refused,...u.refused]},await h(e,g,a,c)}),"importLocal");await f(),await _("export",async()=>{const a=await w(e,l),c=a?await p(e,l):null,n=q(r.db(),t,c);if(a?ue(c,n):n.size===0)return;const d=await I(e,n,a?[a]:[],`crtr: export ${t.name}`);await h(e,l,d,a),await h(e,g,d,await w(e,g))});for(let a=1;;a++){const c=await te(e,i);if(!c.ok)return{ok:!1,step:"fetch",reason:c.reason};let n=await w(e,l);if(c.value!==null&&!(n&&await fe(e,c.value)&&await L(e,c.value,n))){const u=await X(e,i);if(!u.ok)return{ok:!1,step:"fetch",reason:u.reason};const m=u.value;n=await _("merge",async()=>{if(!n||await L(e,n,m))return await h(e,l,m,n),m;const b=await Z(e,n,m),C=re(b?await p(e,b):new Map,await p(e,l),await p(e,oe));for(const j of C.refused)o.refused.push(j);const F=await I(e,C.tree,[n,m],`crtr: merge ${t.name}`);return await h(e,l,F,n),F})}if(await f(),n===null||n===c.value)return{ok:!0,pushed:!1,imported:o};const d=await ee(e,i,n);if(d.ok)return{ok:!0,pushed:!0,imported:o};if(d.reason!=="rejected")return{ok:!1,step:"push",reason:d.reason};if(a>=A)return{ok:!1,step:"push",reason:`push rejected ${A} times`}}}catch(e){if(e instanceof x)return{ok:!1,step:e.step,reason:e.message};throw e}}s(ae,"syncRepo");async function ce(t){const r=M(t.dir,O);return D(r)&&(await z(r,"utf8")).trim()||null}s(ce,"readUrl");async function fe(t,r){return await w(t,r)!==null}s(fe,"hasCommit");function ue(t,r){if(t.size!==r.size)return!1;for(const[e,i]of t)if(r.get(e)!==i)return!1;return!0}s(ue,"sameTree");export{ne as FIRST_FETCH_WAIT_MS,Me as noteNodeBooting,se as noteNodeWorking,ke as noteRepoDocumentsChanged,Re as startRepoSync,ae as syncRepo};
1
+ var B=Object.defineProperty;var f=(t,r)=>B(t,"name",{value:r,configurable:!0});import{prepared as v}from"../../sqlite-statements.js";import{existsSync as D}from"node:fs";import{readFile as K,writeFile as q}from"node:fs/promises";import{join as S}from"node:path";import{emitEvent as G}from"../../events/emit.js";import{exportRepo as J,importTree as Q,pendingPaths as V}from"./exchange.js";import{checkoutChain as X}from"./identity.js";import{checkoutsForCwd as Y,reposForCwd as Z}from"./repos.js";import{commitTree as k,fetchDocs as ee,FIRST_FETCH_MARKER as te,isAncestor as L,mergeBase as re,openMirror as _,pushDocs as ne,readTree as p,remoteDocsTip as se,resolveRef as h,setRef as y}from"./mirror.js";import{mergeTrees as oe}from"./ref-format.js";const ie=2e4,A=3,H="remote-url",m="refs/crtr/docs",ae="refs/crtr/remote-docs",g="refs/crtr/imported";function T(t){try{G(t)}catch{}}f(T,"emitEvent");let M=null;const b=new Map,O=new Set;function Se(t,r){M={lifecycle:t,db:r}}f(Se,"startRepoSync");function P(t,r){return r.type!=="repo"?!1:v(t,"SELECT 1 FROM identities WHERE object = ? AND scheme = 'git'").get(r.rowid)!==void 0}f(P,"gitScheme");async function U(t,r){const e=new Map;for(const c of X(r))c.ok&&c.scheme==="git"&&e.set(c.externalId,c.remoteUrl);if(e.size===0)return;const i=M.db(),n=v(i,"SELECT external_id FROM identities WHERE object = ? AND scheme = 'git'");for(const c of t){const a=n.get(c.rowid),o=a&&e.get(a.external_id);if(!o)continue;const s=await _(c.id);await q(S(s.dir,H),`${o}
2
+ `)}}f(U,"recordRemoteUrls");async function ce(t,r){const i=E().db(),n=t.filter(s=>P(i,s));if(n.length===0)return;await U(n,r);const c=[];for(const s of n){const l=!O.has(s.id)&&!D(S((await _(s.id)).dir,te)),u=R(s);l&&u&&c.push(u)}if(c.length===0)return;let a;const o=new Promise(s=>{a=setTimeout(s,ie)});await Promise.race([Promise.all(c),o]),clearTimeout(a)}f(ce,"noteNodeWorking");async function ke(t){try{const r=E().db(),e=v(r,"SELECT cwd FROM nodes WHERE node_id = ?").get(t);if(!e)return;const i=fe(r,()=>Z(r,e.cwd));i.length>0&&await ce(i,e.cwd)}catch(r){T({level:"warn",event:"repo_sync.failed",outcome:"failed",error:r,node_id:t,fields:{step:"fetch",reason:`the boot trigger failed: ${r instanceof Error?r.message:String(r)}`}})}}f(ke,"noteNodeBooting");function fe(t,r){if(t.isTransaction)return r();t.exec("BEGIN IMMEDIATE");try{const e=r();return t.exec("COMMIT"),e}catch(e){throw t.exec("ROLLBACK"),e}}f(fe,"inWrite");function _e(t,r){const e=E();if(!P(e.db(),t))return;if(r===void 0){R(t);return}const i=U([t],r).then(()=>{R(t)},n=>{T({level:"error",event:"repo_sync.failed",error:n,fields:{repo:t.name,step:"fetch",reason:"recording the remote URL failed"}})});e.lifecycle.registerDetached(i)}f(_e,"noteRepoDocumentsChanged");function E(){if(!M)throw new Error("repo sync is not started: crtrd must call startRepoSync first");return M}f(E,"requireEngine");function R(t){const r=E(),e=b.get(t.id)??{running:null,again:!1};if(b.set(t.id,e),e.running)return e.again=!0,e.running.then(()=>b.get(t.id)?.running??null).then(n=>n??{ok:!0,pushed:!1,imported:$()});if(!r.lifecycle.acceptsDetachedWork())return null;O.add(t.id);const i=de(t).then(n=>(W(t,n),n),n=>{const c={ok:!1,step:"export",reason:n instanceof Error?n.message:String(n)};return W(t,c),c}).finally(()=>{e.running=null,e.again&&(e.again=!1,R(t))});return e.running=i,r.lifecycle.registerDetached(i.then(()=>{})),i}f(R,"schedule");function W(t,r){if(r.ok){const e=r.imported;T({level:"info",event:"repo_sync.completed",outcome:"succeeded",fields:{repo:t.name,pushed:r.pushed,created:e.created,revisions:e.revisions,deleted:e.deleted,needs_merge:e.needsMerge,refused:e.refused}})}else T({level:"warn",event:"repo_sync.failed",outcome:"failed",fields:{repo:t.name,step:r.step,reason:r.reason}})}f(W,"report");function $(){return{created:0,revisions:0,deleted:0,needsMerge:[],refused:[]}}f($,"emptyImport");class x extends Error{static{f(this,"StepError")}step;constructor(r,e){super(e),this.step=r}}async function C(t,r){try{return await r()}catch(e){throw e instanceof x?e:new x(t,e instanceof Error?e.message:String(e))}}f(C,"step");async function de(t){const r=E();try{const e=await _(t.id),i=await ue(e);if(i===null)return{ok:!1,step:"fetch",reason:`no checkout of ${t.name} with an origin remote has been seen by this runtime`};let n=$();const c=f(()=>C("import",async()=>{const a=await h(e,m),o=await h(e,g);if(a===null||o===a)return;const s=o?await p(e,g):new Map,l=r.db(),u=await p(e,m),d=Q(l,t,s,u,w=>Y(l,w));n={created:n.created+d.created,revisions:n.revisions+d.revisions,deleted:n.deleted+d.deleted,needsMerge:[...new Set([...n.needsMerge,...d.needsMerge])],refused:[...n.refused,...d.refused]},await j(e,a,u,s,new Set(d.refused.map(w=>w.path)),o)}),"importLocal");await c(),await C("export",async()=>{const a=await h(e,m),o=a?await p(e,m):null,s=await h(e,g),l=s?await p(e,g):null,u=J(r.db(),t,o,l);if(a?z(o,u):u.size===0)return;const d=await k(e,u,a?[a]:[],`crtr: export ${t.name}`);await y(e,m,d,a),await j(e,d,u,l??new Map,new Set(V(o,l)),s)});for(let a=1;;a++){const o=await se(e,i);if(!o.ok)return{ok:!1,step:"fetch",reason:o.reason};let s=await h(e,m);if(o.value!==null&&!(s&&await le(e,o.value)&&await L(e,o.value,s))){const u=await ee(e,i);if(!u.ok)return{ok:!1,step:"fetch",reason:u.reason};const d=u.value;s=await C("merge",async()=>{if(!s||await L(e,s,d))return await y(e,m,d,s),d;const w=await re(e,s,d),F=oe(w?await p(e,w):new Map,await p(e,m),await p(e,ae));for(const N of F.refused)n.refused.push(N);const I=await k(e,F.tree,[s,d],`crtr: merge ${t.name}`);return await y(e,m,I,s),I})}if(await c(),s===null||s===o.value)return{ok:!0,pushed:!1,imported:n};const l=await ne(e,i,s);if(l.ok)return{ok:!0,pushed:!0,imported:n};if(l.reason!=="rejected")return{ok:!1,step:"push",reason:l.reason};if(a>=A)return{ok:!1,step:"push",reason:`push rejected ${A} times`}}}catch(e){if(e instanceof x)return{ok:!1,step:e.step,reason:e.message};throw e}}f(de,"syncRepo");async function j(t,r,e,i,n,c){if(n.size===0){await y(t,g,r,c);return}const a=new Map(e);for(const o of n)i.has(o)?a.set(o,i.get(o)):a.delete(o);c!==null&&z(i,a)||await y(t,g,await k(t,a,[],"crtr: imported baseline (some files pending)"),c)}f(j,"advanceImported");async function ue(t){const r=S(t.dir,H);return D(r)&&(await K(r,"utf8")).trim()||null}f(ue,"readUrl");async function le(t,r){return await h(t,r)!==null}f(le,"hasCommit");function z(t,r){if(t.size!==r.size)return!1;for(const[e,i]of t)if(r.get(e)!==i)return!1;return!0}f(z,"sameTree");export{ie as FIRST_FETCH_WAIT_MS,ke as noteNodeBooting,ce as noteNodeWorking,_e as noteRepoDocumentsChanged,Se as startRepoSync,de as syncRepo};
@@ -1 +1 @@
1
- var g=Object.defineProperty;var s=(t,n)=>g(t,"name",{value:n,configurable:!0});import{covers as u}from"@north-light/identity";import{identityOf as c}from"./index.js";import{objectPlace as $,runPlace as p,runsSeeEachOther as a}from"../../runs/confinement.js";const j={identity:c,sync:"runtime",canRead(t,n,o,e,r){if(n.class==="person"||e.length===0)return!0;if(n.app.rowid===o.rowid)return m(t,n,e);const i=c(t,o).external_id,l=s(w=>n.scopes!==null&&n.scopes.some(d=>u(d,w)),"held");return e[0].type==="node"?l(`crtr:objects:read:${i}`)||r.type==="document"&&r.kind==="report"&&l(`crtr:conversations:read:${i}`):l(y(t,i,"read",e))},canChange(t,n,o,e){if(n.class==="person")return!0;if(n.app.rowid===o.rowid)return e.length===0||m(t,n,e);if(e.length===0||e[0].type==="node")return!1;const r=y(t,c(t,o).external_id,"rw",e);return n.scopes!==null&&n.scopes.some(i=>u(i,r))}},f=new WeakMap;function m(t,n,o){if(n.class==="person"||o.length===0||o[0].type!=="node"||n.class==="app"&&n.place===void 0)return!0;let e=f.get(n);if(e===void 0&&(e=n.class==="app"?n.place:n.class==="node"?p(n.node.id,t):n.place??$(n.object.rowid,t),f.set(n,e)),e===null)return!0;const r=p(o[0].id,t);return r===null?!e.isolated:a(e,r)}s(m,"runConfined");function y(t,n,o,e){const r=e[0];return r.type!=="profile"?`crtr:memory:${o}:${n}`:`crtr:memory:${o}:${n}:profile:${c(t,r).external_id}`}s(y,"memoryScope");export{j as appSpace};
1
+ var d=Object.defineProperty;var c=(t,n)=>d(t,"name",{value:n,configurable:!0});import{covers as u}from"@north-light/identity";import{identityOf as r}from"./index.js";import{memoryScope as p,missingOwnMemoryScope as f}from"./own-memory.js";import{objectPlace as x,runPlace as m,runsSeeEachOther as O}from"../../runs/confinement.js";const M={identity:r,sync:"runtime",canRead(t,n,e,o,s){if(n.class==="person"||o.length===0)return!0;if(n.app.rowid===e.rowid)return y(t,n,o)&&f(t,n,e,o,s,"read")===null;const i=r(t,e).external_id,l=c(g=>n.scopes!==null&&n.scopes.some(S=>u(S,g)),"held");return o[0].type==="node"?l(`crtr:objects:read:${i}`)||s.type==="document"&&s.kind==="report"&&l(`crtr:conversations:read:${i}`):l(p(t,i,"read",o))},canChange(t,n,e,o,s){if(n.class==="person")return!0;if(n.app.rowid===e.rowid)return o.length===0||y(t,n,o)&&f(t,n,e,o,s,"rw")===null;if(o.length===0||o[0].type==="node")return!1;const i=p(t,r(t,e).external_id,"rw",o);return n.scopes!==null&&n.scopes.some(l=>u(l,i))}},w=new WeakMap;function y(t,n,e){if(n.class==="person"||e.length===0||e[0].type!=="node"||n.class==="app"&&n.place===void 0)return!0;let o=w.get(n);if(o===void 0&&(o=n.class==="app"?n.place:n.class==="node"?m(n.node.id,t):n.place??x(n.object.rowid,t),w.set(n,o)),o===null)return!0;const s=m(e[0].id,t);return s===null?!o.isolated:O(o,s)}c(y,"runConfined");export{M as appSpace};
@@ -0,0 +1,18 @@
1
+ import type { DatabaseSync } from 'node:sqlite';
2
+ import type { ObjectRow, Reader } from '../types.js';
3
+ type Path = ObjectRow[];
4
+ /** The scope a run launched with a `scopes` list lacks to read (`read`) or change (`rw`) one of its own app's documents, or null.
5
+ * Own-app memory needs no grant scope, but a launched list narrows it (identity MEM-2): reading needs `crtr:memory:read:app:<A>`,
6
+ * writing `crtr:memory:rw:app:<A>` (profile forms for a profile's documents). Only documents are gated — never a node, job or
7
+ * lifecycle object — and a run with no list, an app's server and the person keep full access. A report is readable inside its
8
+ * own run (a run hears its children through them); writing one is the runtime's own act (see `asRuntimeWriter`). */
9
+ export declare function missingOwnMemoryScope(db: DatabaseSync, reader: Reader, space: ObjectRow, path: Path, object: {
10
+ type: string;
11
+ kind: string | null;
12
+ } | null, access: 'read' | 'rw'): string | null;
13
+ /** The same reader with its launched list lifted, for the runtime's own writes inside a narrowed run: a `crtr push` report and the
14
+ * scaffolded roadmap of a promoted node. Wrap only the one call that creates the document the runtime names. */
15
+ export declare function asRuntimeWriter<R extends Reader>(reader: R): R;
16
+ /** `crtr:memory:<access>:app:<B>` for an app document, or its profile form for a profile and what it owns. */
17
+ export declare function memoryScope(db: DatabaseSync, app: string, access: 'read' | 'rw', path: Path): string;
18
+ export {};
@@ -0,0 +1 @@
1
+ var f=Object.defineProperty;var l=(n,e)=>f(n,"name",{value:e,configurable:!0});import{prepared as d}from"../../sqlite-statements.js";import{covers as a}from"@north-light/identity";import{runPlace as s}from"../../runs/confinement.js";const m=l((n,e)=>d(n,"SELECT external_id FROM identities WHERE object = ?").get(e.rowid).external_id,"externalId");function S(n,e,o,t,r,i){if(e.class==="person"||t.length===0||r!==null&&r.type!=="document")return null;const u=e.launched;if(u==null||i==="read"&&r?.kind==="report"&&t[0].type==="node"&&y(n,e,t[0].id))return null;const c=x(n,m(n,o),i,t);return u.some(p=>a(p,c))?null:c}l(S,"missingOwnMemoryScope");function y(n,e,o){const t=e.class==="node"?s(e.node.id,n):e.class==="person"?null:e.place??null,r=s(o,n);return t!==null&&r!==null&&t.run===r.run}l(y,"inReadersRun");function w(n){return n.class==="person"?n:{...n,launched:null}}l(w,"asRuntimeWriter");function x(n,e,o,t){const r=t[0];return r.type!=="profile"?`crtr:memory:${o}:${e}`:`crtr:memory:${o}:${e}:profile:${m(n,r)}`}l(x,"memoryScope");export{w as asRuntimeWriter,x as memoryScope,S as missingOwnMemoryScope};
@@ -2,7 +2,8 @@ import type { Reader } from '../types.js';
2
2
  import type { Space } from './index.js';
3
3
  /** Whether a node is working in a checkout of `repo` that its file permissions reach:
4
4
  * read to read the repo's documents, rw to change them. The cwd alone never lets a run
5
- * in, since a node may be started in any directory. A repo-owned custom object is inside
5
+ * in, since a node may be started in any directory. A project repo of the node's profile
6
+ * is readable the same way but never changed through the node's view. A repo-owned custom object is inside
6
7
  * its own repo. A Mac terminal node reaches every path. */
7
8
  export declare function insideRepo(db: Parameters<Space['canRead']>[0], reader: Reader, repo: {
8
9
  rowid: number;
@@ -1 +1 @@
1
- var u=Object.defineProperty;var i=(t,o)=>u(t,"name",{value:o,configurable:!0});import{fileRoot as f}from"../../spaces/file-root.js";import{identityOf as c}from"./index.js";function l(t,o,e,n){if(o.class==="object")return o.repos.some(r=>r.rowid===e.rowid);if(o.class!=="node")return!1;const s=o.checkouts.filter(r=>r.repo.rowid===e.rowid);if(s.length===0)return!1;const p={app:c(t,o.app).external_id,resolved:o.scopes??[],launched:null};return s.some(r=>"root"in f(p,r.root,n))}i(l,"insideRepo");const m={identity:c,sync:"repo-ref",canRead(t,o,e,n){return o.class==="person"||n.length===0?!0:l(t,o,e,"read")},canChange(t,o,e,n,s){return s.privacy!=="public"?!1:o.class==="person"?!0:l(t,o,e,"rw")}};export{l as insideRepo,m as repoSpace};
1
+ var u=Object.defineProperty;var s=(t,o)=>u(t,"name",{value:o,configurable:!0});import{fileRoot as f}from"../../spaces/file-root.js";import{identityOf as c}from"./index.js";function p(t,o,e,r){if(o.class==="object")return o.repos.some(n=>n.rowid===e.rowid);if(o.class!=="node")return!1;const i=[...o.checkouts,...r==="read"?o.projectCheckouts:[]].filter(n=>n.repo.rowid===e.rowid);if(i.length===0)return!1;const l={app:c(t,o.app).external_id,resolved:o.scopes??[],launched:null};return i.some(n=>"root"in f(l,n.root,r))}s(p,"insideRepo");const m={identity:c,sync:"repo-ref",canRead(t,o,e,r){return o.class==="person"||r.length===0?!0:p(t,o,e,"read")},canChange(t,o,e,r,i){return i.privacy!=="public"?!1:o.class==="person"?!0:p(t,o,e,"rw")}};export{p as insideRepo,m as repoSpace};
@@ -56,6 +56,8 @@ export type Reader = {
56
56
  class: 'app';
57
57
  app: ObjectRow;
58
58
  scopes: readonly string[];
59
+ /** The launched `scopes` list exactly as given (null/absent = none was given). Unlike `scopes` it is not filtered through the grant, so it keeps the own-app memory strings a grant never holds; it alone narrows access to the app's own documents. */
60
+ launched?: readonly string[] | null;
59
61
  /** A cron command's run place (its origin run, and whether that run is isolated): it reads its own app's nodes under run confinement, as a node of that run does. Absent for the app's server. */
60
62
  place?: {
61
63
  run: string;
@@ -67,9 +69,15 @@ export type Reader = {
67
69
  app: ObjectRow;
68
70
  scopes: readonly string[] | null;
69
71
  cwd: string;
70
- /** The repos enclosing `cwd`, nearest first; `checkouts` pairs each with its checkout root. */
72
+ /** The launched `scopes` list as given (see the app reader); null/absent = the run was launched with no list. */
73
+ launched?: readonly string[] | null;
74
+ /** The repos enclosing `cwd`, nearest first; `checkouts` pairs each with its checkout root. Only these are repos the node
75
+ * "works in": `--owner repo` and write access follow them; rule `if: {repo}` follows these plus `projectRepos`. */
71
76
  repos: ObjectRow[];
72
77
  checkouts: Checkout[];
78
+ /** The repos of the profile's project paths that do not enclose `cwd`, in manifest order: in the node's view and readable, not worked in. */
79
+ projectRepos: ObjectRow[];
80
+ projectCheckouts: Checkout[];
73
81
  profile: ObjectRow | null;
74
82
  } | {
75
83
  class: 'object';
@@ -81,6 +89,7 @@ export type Reader = {
81
89
  node: ObjectRow;
82
90
  app: ObjectRow;
83
91
  scopes: readonly string[] | null;
92
+ launched?: readonly string[] | null;
84
93
  repos: ObjectRow[];
85
94
  owner: ObjectRow;
86
95
  };
@@ -1 +1 @@
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};
1
+ var J=Object.defineProperty;var a=(e,t)=>J(e,"name",{value:t,configurable:!0});import{envHomeOverride as K,envRuntimeHomeOverride as X,INBOX_BETWEEN_TURNS_ENV as z,SCOPES_FILE_ENV as V,scopeAllowed as C}from"../../shared/env.js";import{isOwnMemoryScope as Y,validateScopes as Q}from"../scopes.js";import{activeGrant as B,terminalGrant as k}from"../grants/mirror.js";import{appendRunEvent as Z}from"../runs/events.js";import{readConfig as S}from"../config.js";import{InputError as T}from"../io.js";import{ApiError as w}from"../../api/errors.js";import{assertGranteeRunsHere as ee}from"./unsandboxed.js";import{randomBytes as ne}from"node:crypto";import{existsSync as P}from"node:fs";import{resolve as re,relative as te,isAbsolute as H,join as ie,sep as oe}from"node:path";import{appSpace as le}from"../layout.js";import{scopesFilePath as ae}from"../canvas/paths.js";import{createNode as se,assertRunNotDeleting as de,conversationOwner as ue,getNode as c,getRow as ce}from"../canvas/index.js";import{ensureAppSpace as fe}from"../spaces/space.js";import{LAUNCHER_SOCKET as L}from"../../launcher/spec.js";import{resolveInstallId as pe}from"../canvas/install-id.js";import{isAppRun as me}from"../canvas/app-run.js";import{canvasDbPath as _e,contextDir as ge}from"../canvas/paths.js";import{conversationIdFromPath as we}from"../conversation-store/index.js";import{usage as U}from"../errors.js";import{assertProfileAvailableForBirth as he}from"../profiles/deletion-reservation.js";import{loadExactProfileManifest as ve}from"../profiles/manifest.js";import{admitNodeCreate as Re}from"../command-hooks/lifecycle-create.js";import{getById as Ee,nodeObject as I,ownerForGrantee as ye}from"../graph/objects.js";import{recordActedOn as Ce}from"../graph/edges.js";import{recordWatch as Ie,copyWatchesForFork as Ne}from"../graph/watches.js";import{publish as ke}from"../graph/events.js";let O;const j=new Map;function cn(e){O=e}a(cn,"setInstallId");function Se(){if(O!==void 0)return O;const e=_e(),t=j.get(e);if(t!==void 0)return t;const o=pe();return j.set(e,o),o}a(Se,"installId");function Te(){return`${Date.now().toString(36)}-${ne(4).toString("hex")}`}a(Te,"newJobId");function Oe(){return`${Se()}-${Te()}`}a(Oe,"newNodeId");const x=128,xe=/^[a-z0-9-]+$/;class q extends Error{static{a(this,"NodeIdConflictError")}nodeId;constructor(t){super(`node already exists: ${t}`),this.nodeId=t,this.name="NodeIdConflictError"}}function D(e){if(e.length===0||e.length>x)throw U(`invalid --node-id '${e}': must be 1-${x} characters`,{next:`Pass an id-safe string (lowercase letters, digits, hyphens) of at most ${x} characters.`});if(!xe.test(e))throw U(`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(D,"validateNodeIdFormat");function fn(e){if(D(e),c(e)!==null)throw new q(e)}a(fn,"preflightNodeId");function De(e){if(D(e),c(e)!==null)throw new q(e);return e}a(De,"validatedExplicitNodeId");function pn(e){let t=e;const o=new Set;for(;;){if(o.has(t))return t;o.add(t);const s=ce(t);if(s===null||s.parent==null)return t;t=s.parent}}a(pn,"rootOfSpine");function mn(){const e=process.env;return{nodeId:e.CRTR_NODE_ID??null,kind:e.CRTR_KIND??null,mode:e.CRTR_MODE??null}}a(mn,"currentNodeContext");function be(e){return me(e)}a(be,"inboxBetweenTurns");function _n(e){const t={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:ge(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&&(t.CRTR_PARENT_NODE_ID=e.parent),e.run_id&&(t.CRTR_RUN_ID=e.run_id),be(e)&&(t[z]="1"),e.scopes!==null&&e.scopes!==void 0&&(t[V]=ae(e.node_id)),t.CRTR_GRANTEE=e.grantee??"app:terminal";const o=K();o!==void 0&&o!==""&&(t.CRTR_HOME=o);const s=X();s!==void 0&&s!==""&&(t.CRTR_RUNTIME_HOME=s);const f=process.env.CRTR_SUBTREE;return f!==void 0&&f!==""&&(t.CRTR_SUBTREE=f),{...t,...e.launch?.env??{}}}a(_n,"nodeEnv");function Ae(e){const t=we(e),o=t===null?null:ue(t);return o===null?null:c(o)}a(Ae,"forkSourceNode");async function gn(e){const t=e.nodeId!==void 0?(D(e.nodeId),e.nodeId):Oe(),o=e.parent??null,s=o===null,f=e.spawnedBy??o,m=e.creator??null,b=e.mode??"base",N=e.lifecycle??(s?"resident":"terminal"),h=e.run===void 0?k():null,n={node_id:t,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:b,lifecycle:N,persona_ack:{kind:e.kind,mode:b,lifecycle:N},status:"active",parent:o,spawned_by:f,creator:m,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??h?.sub??null,grant_id:e.run?.grant_id??h?.id??null,client_id:e.run?.client_id??null,metadata_json:s?e.run?.metadata_json??null:null,run_id:s?t: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},E=e.scopes===void 0?void 0:Q(e.scopes),v=o===null?null:c(o);if(o!==null&&v===null)throw new Error(`cannot spawn under unknown parent node: ${o}`);if(v?.run_id&&c(v.run_id)?.deleting_at)throw new Error("run is deleting");const u=m===null?null:c(m);if(m!==null&&u===null)throw new Error(`cannot spawn from unknown creator node: ${m}`);if(u?.run_id&&c(u.run_id)?.deleting_at)throw new Error("run is deleting");const A=e.forkSourceFile==null?null:Ae(e.forkSourceFile),$=e.replaces==null?null:c(e.replaces),_=v??A??$??u??(f===null?null:c(f)),F=[v,u,A,$,f===null?null:c(f)].some(r=>r?.isolated===!0||r?.run_id!=null&&c(r.run_id)?.isolated===!0);if(F&&e.isolated===!1)throw new w(403,"isolated_run","isolation cannot be turned off under an isolated run",{param:"isolated"});if(n.isolated=F||e.isolated===!0,_!==null){const r=u??_;if(u&&_.grantee&&u.grantee&&u.grantee!==_.grantee)throw new w(403,"other_app_run","cannot create under another app's node");const i=u&&u!==_?[_,u]:[_],d=[],g=new Set;for(const l of i){l.scopes!==null&&l.scopes!==void 0&&d.push(l.scopes);const p=l.grantee==="app:terminal"?k():l.grant_id?B(l.grant_id):null;if(p){const R=[...p.resolved];g.add(R),d.push(R)}else if(!(l.grantee==="app:terminal"&&!S("user").runtime?.issuer)){const R=[];g.add(R),d.push(R)}}const y=a(l=>Y(l,r.grantee==="user:local"?"app:terminal":r.grantee??"app:terminal"),"ownMemory"),M=a((l,p)=>y(p)&&g.has(l)||C(l,p),"allowed");if(E!==void 0&&!E.every(l=>d.every(p=>M(p,l))))throw new T({error:"unknown_scope",message:"child scopes must be covered by the creating node, its source and their current grants",received:E,field:"scopes",next:"Request only scopes the creating node currently holds."});if(n.scopes=E??(i.every(l=>l.scopes==null)&&i.every(l=>l.grant_id===r.grant_id&&l.sub===r.sub)?null:i.length===1?_.scopes??null:[...new Set(d.flat().filter(l=>d.every(p=>M(p,l))))]),n.scopes?.includes("crtr:act")&&!C(n.scopes,"crtr:llm"))throw new T({error:"invalid_scope",message:"crtr:act requires crtr:llm",received:n.scopes,field:"scopes",next:"Include crtr:llm or omit crtr:act."});n.grantee=r.grantee==="user:local"?"app:terminal":r.grantee,n.sub=r.sub??(n.grantee==="app:terminal"?h?.sub:null)??null,n.grant_id=r.grant_id??(n.grantee==="app:terminal"?h?.id:null)??null,n.client_id=r.client_id,n.run_id=s?t:r.run_id??null}if(ee(n.grantee),e.run===void 0&&n.grantee==="app:terminal"&&S("user").runtime?.issuer&&h===null)throw new w(403,"scope_missing","terminal grant is not active");if(n.isolated){if(process.platform!=="linux"||!P(L))throw new w(400,"isolated_unavailable","isolated nodes require the Linux root launcher");if(e.managedWorktree!==void 0)throw new w(400,"invalid_request","isolated nodes cannot use a worktree",{param:"worktree"});const r=ie(le(n.grantee??"app:terminal").nodes,t),i=te(r,re(n.cwd));if(!H(n.cwd)||i===".."||i.startsWith(`..${oe}`)||H(i))throw new w(400,"invalid_request","isolated node cwd must be inside its own folder",{param:"cwd"})}if(n.profile_id){const r=ve(n.profile_id).manifest.grantee??"app:terminal";if(r!==n.grantee){const i=`crtr:memory:rw:${r}:profile:${n.profile_id}`,d=n.grantee==="app:terminal"?k():n.grant_id?B(n.grant_id):null;if(!(n.grantee==="app:terminal"&&!S("user").runtime?.issuer)&&(!d||d.grantee!==n.grantee||!C(d.resolved,i))||!C(n.scopes,i))throw new T({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(he(n.profile_id??null),!n.grantee)throw new Error("node creation requires an app");const W=await Re({kind:n.kind,mode:n.mode,cwd:n.cwd,profile:n.profile_id??null,root:s,parentId:o,requestingNodeId:m,lifecycle:N,replaces:e.replaces??null},n.grantee),G=a((r,i)=>{const d=I(r,i.node_id),g=m??o??e.actor??f??null,y=g!==null&&Ee(r,g)?.type==="node"?I(r,g):ye(r,e.run===void 0?"user:local":i.grantee);Ce(r,y,d),o!==null&&e.subscribe!==!1&&Ie(r,I(r,o),d,"spawn"),e.forkSourceNode&&Ne(r,I(r,e.forkSourceNode),d),ke(r,{event:"created",object:d,actor:y,attrs:{name:i.name,kind:i.kind,creator:m??null}})},"recordBirth");try{e.nodeId!==void 0&&De(e.nodeId),await e.beforeCreate?.(),(process.platform!=="linux"||P(L))&&await fe(n.grantee??"app:terminal"),se(n,{outcomeDelivery:e.outcomeDelivery,withinCreate:a((r,i)=>{i.run_id&&i.run_id!==i.node_id&&de(r,i.run_id),s&&e.run===void 0&&Z(r,t,t,"run.started",{scopes:i.scopes??null,payer:"user",usage:JSON.parse(i.usage_json)}),e.withinCreate?.(r,i),G(r,i)},"withinCreate")})}finally{W()}return n}a(gn,"spawnNode");export{q as NodeIdConflictError,mn as currentNodeContext,Ae as forkSourceNode,be as inboxBetweenTurns,Te as newJobId,Oe as newNodeId,_n as nodeEnv,fn as preflightNodeId,pn as rootOfSpine,cn as setInstallId,gn as spawnNode};
@@ -1,5 +1,5 @@
1
- var l=Object.defineProperty;var r=(e,t)=>l(e,"name",{value:t,configurable:!0});import{openDb as u,withCanvasWrite as d}from"../canvas/db.js";import{get as s}from"../graph/bodies.js";import{heads as p,writeDocument as c}from"../graph/documents.js";import{getById as m,liveDocumentNamed as f,nodeObject as h}from"../graph/objects.js";import{readerForNode as g}from"../graph/reader.js";const i="roadmap",w="To understand this node's long-term task and current plan, read this.";function x(e){return e?.split(`
2
- `).map(n=>n.trim()).find(n=>n!=="")??w}r(x,"roadmapPreview");function O(e){return`${e}/${i}`}r(O,"roadmapRef");function a(e,t){const n=m(e,t);return n===null||n.type!=="node"?null:f(e,n.rowid,i)}r(a,"roadmapObject");function P(e){return a(u(),e)!==null}r(P,"hasRoadmap");function W(e){const t=u(),n=a(t,e);if(n===null)return null;const o=p(t,n)[0];if(o===void 0)return null;try{return s(o.body_hash)}catch{return null}}r(W,"readRoadmap");function y(e){return`# Roadmap
1
+ var l=Object.defineProperty;var r=(e,n)=>l(e,"name",{value:n,configurable:!0});import{openDb as u,withCanvasWrite as d}from"../canvas/db.js";import{get as s}from"../graph/bodies.js";import{heads as m,writeDocument as p}from"../graph/documents.js";import{asRuntimeWriter as c}from"../graph/spaces/own-memory.js";import{getById as f,liveDocumentNamed as h,nodeObject as g}from"../graph/objects.js";import{readerForNode as w}from"../graph/reader.js";const i="roadmap",x="To understand this node's long-term task and current plan, read this.";function R(e){return e?.split(`
2
+ `).map(t=>t.trim()).find(t=>t!=="")??x}r(R,"roadmapPreview");function P(e){return`${e}/${i}`}r(P,"roadmapRef");function a(e,n){const t=f(e,n);return t===null||t.type!=="node"?null:h(e,t.rowid,i)}r(a,"roadmapObject");function $(e){return a(u(),e)!==null}r($,"hasRoadmap");function M(e){const n=u(),t=a(n,e);if(t===null)return null;const o=m(n,t)[0];if(o===void 0)return null;try{return s(o.body_hash)}catch{return null}}r(M,"readRoadmap");function y(e){return`# Roadmap
3
3
 
4
4
  ## Goal
5
5
  ${e.goal?.trim()||"(the goal you now own)"}
@@ -12,4 +12,4 @@ ${e.exitCriteria?.trim()||'(what "done" looks like)'}
12
12
 
13
13
  ## Working set
14
14
  (links to the documents the work depends on now, one per line)
15
- `}r(y,"scaffold");function R(e,t,n={}){if(a(e,t)!==null)return null;const o=y(n);return c(e,g(e,t,()=>[]),{owner:h(e,t),name:i,kind:"knowledge",preview:x(n.goal),privacy:"public",rules:[{on:"boot",deliver:"content"}]},o),o}r(R,"seedRoadmap");function $(e,t={}){return d(n=>R(n,e,t)!==null)}r($,"ensureRoadmap");export{i as ROADMAP_NAME,w as ROADMAP_PREVIEW,$ as ensureRoadmap,P as hasRoadmap,W as readRoadmap,x as roadmapPreview,O as roadmapRef,R as seedRoadmap};
15
+ `}r(y,"scaffold");function k(e,n,t={}){if(a(e,n)!==null)return null;const o=y(t);return p(e,c(w(e,n,()=>[])),{owner:g(e,n),name:i,kind:"knowledge",preview:R(t.goal),privacy:"public",rules:[{on:"boot",deliver:"content"}]},o),o}r(k,"seedRoadmap");function N(e,n={}){return d(t=>k(t,e,n)!==null)}r(N,"ensureRoadmap");export{i as ROADMAP_NAME,x as ROADMAP_PREVIEW,N as ensureRoadmap,$ as hasRoadmap,M as readRoadmap,R as roadmapPreview,P as roadmapRef,k as seedRoadmap};
@@ -2,6 +2,9 @@ import { scopeAllowed } from '../shared/env.js';
2
2
  export { scopeAllowed };
3
3
  /** The scopes an unrestricted local terminal run presents to the document substrate. */
4
4
  export declare const SCOPE_VOCABULARY: readonly ["crtr:llm", "crtr:act", "crtr:schedule", "crtr:memory:rw:user", "crtr:memory:manage:user", "crtr:files:rw:user:."];
5
+ /** A memory string naming the app itself (`crtr:memory:read|rw:app:<A>`, or its profile form). No grant holds one: it exists only to
6
+ * narrow a run within its own space. */
7
+ export declare function isOwnMemoryScope(scope: string, grantee: string): boolean;
5
8
  /** Reject unknown scope strings before they can silently change an allow-list. */
6
9
  export declare function validateScopes(value: unknown): string[];
7
10
  export declare function assertScope(meta: {
@@ -1 +1 @@
1
- var c=Object.defineProperty;var o=(e,r)=>c(e,"name",{value:r,configurable:!0});import{parseScope as i}from"@north-light/identity";import{scopeAllowed as t}from"../shared/env.js";import{InputError as n}from"./io.js";const w=["crtr:llm","crtr:act","crtr:schedule","crtr:memory:rw:user","crtr:memory:manage:user","crtr:files:rw:user:."];function h(e){if(!Array.isArray(e))throw new n({error:"unknown_scope",message:"scopes must be a list",received:e,field:"scopes",next:"Pass identity scope strings."});for(const r of e)try{if(typeof r!="string")throw new TypeError("scope is not a string");i(r)}catch{throw new n({error:"unknown_scope",message:`unknown scope: ${String(r)}`,received:r,field:"scopes",next:"Pass identity scope strings."})}return[...e]}o(h,"validateScopes");function u(e,r){if(!t(e.scopes,r))throw new p(`node ${e.node_id}`,r)}o(u,"assertScope");class p extends Error{static{o(this,"ScopeDeniedError")}holder;scope;constructor(r,s){super(`${r} does not hold the ${s} scope`),this.holder=r,this.scope=s,this.name="ScopeDeniedError"}}export{w as SCOPE_VOCABULARY,p as ScopeDeniedError,u as assertScope,t as scopeAllowed,h as validateScopes};
1
+ var c=Object.defineProperty;var s=(e,r)=>c(e,"name",{value:r,configurable:!0});import{parseScope as i}from"@north-light/identity";import{scopeAllowed as t}from"../shared/env.js";import{InputError as n}from"./io.js";const f=["crtr:llm","crtr:act","crtr:schedule","crtr:memory:rw:user","crtr:memory:manage:user","crtr:files:rw:user:."];function u(e,r){const o=e.split(":");return o[0]==="crtr"&&o[1]==="memory"&&(o[2]==="read"||o[2]==="rw")&&`${o[3]}:${o[4]}`===r}s(u,"isOwnMemoryScope");function h(e){if(!Array.isArray(e))throw new n({error:"unknown_scope",message:"scopes must be a list",received:e,field:"scopes",next:"Pass identity scope strings."});for(const r of e)try{if(typeof r!="string")throw new TypeError("scope is not a string");i(r)}catch{throw new n({error:"unknown_scope",message:`unknown scope: ${String(r)}`,received:r,field:"scopes",next:"Pass identity scope strings."})}return[...e]}s(h,"validateScopes");function l(e,r){if(!t(e.scopes,r))throw new p(`node ${e.node_id}`,r)}s(l,"assertScope");class p extends Error{static{s(this,"ScopeDeniedError")}holder;scope;constructor(r,o){super(`${r} does not hold the ${o} scope`),this.holder=r,this.scope=o,this.name="ScopeDeniedError"}}export{f as SCOPE_VOCABULARY,p as ScopeDeniedError,l as assertScope,u as isOwnMemoryScope,t as scopeAllowed,h as validateScopes};
@@ -23,5 +23,5 @@ export declare function loadCorpus(db: DatabaseSync, reader: Reader, on: RuleOn[
23
23
  export declare function deliveryDocOf(db: DatabaseSync, reader: Reader, doc: ObjectRow): DeliveryDoc;
24
24
  /** Every listed document of one owner the reader can read, for prefix listings. */
25
25
  export declare function listingCorpus(db: DatabaseSync, reader: Reader, owner: ObjectRow): DeliveryDoc[];
26
- /** The node reader's gate subject, with `repo` from the reader's enclosing repos. */
26
+ /** The node reader's gate subject, with `repo` from the reader's eligible repos (cwd's, then its profile projects'). */
27
27
  export declare function nodeSubject(_db: DatabaseSync, reader: Reader): NodeConfigSubject;
@@ -1,3 +1,3 @@
1
1
  var O=Object.defineProperty;var s=(o,e)=>O(o,"name",{value:e,configurable:!0});import{prepared as R}from"../../sqlite-statements.js";import{canRead as $}from"../../graph/access.js";import{bodyOf as D}from"../../graph/bodies.js";import{resolveName as k,viewOf as m}from"../../graph/names.js";import{rulesForOwners as S,rulesOf as N}from"../../graph/rules.js";import{loadExactProfileManifest as M}from"../../profiles/manifest.js";import{assembleNodeSubject as L}from"../subject.js";function h(o){return o.type==="node"?o.id:o.name}s(h,"ownerHandle");function C(o){return`${o.type}:${h(o)}`}s(C,"typedLimitKey");function H(o,e,n){if(n?.class==="node"&&n.profile!==null&&n.profile.rowid===e.rowid)return"content";const t=o?.delivery_limits;return t?t[C(e)]??t[h(e)]??"content":"content"}s(H,"deliveryLimitFor");function E(o,e){if(e.class!=="node"||e.profile===null)return null;const n=R(o,"SELECT external_id FROM identities WHERE object = ? AND scheme = 'profile'").get(e.profile.rowid);if(!n)throw new Error(`profile object ${e.profile.id} has no profile identity`);return M(n.external_id).manifest}s(E,"readerManifest");function f(o,e,n){return o.prepare(`SELECT o.*, d.heads_key, d.unlisted, d.source_path FROM objects o JOIN documents d ON d.object = o.rowid
2
2
  WHERE o.type = 'document' AND o.deleted_at IS NULL AND ${e} ORDER BY o.rowid`).all(...n)}s(f,"documentRows");function b(o,e){const n=e.heads_key.split(","),t=o.prepare(`SELECT body_hash FROM revisions WHERE document = ? AND rev_id IN (${n.map(()=>"?").join(",")})
3
- ORDER BY number DESC LIMIT 1`).get(e.rowid,...n);if(!t)throw new Error(`document ${e.id} has no head revision ${e.heads_key}`);return t.body_hash}s(b,"headBodyHash");function y(o,e,n){const t=new Map;return{view:n,readable:s(i=>{let l=t.get(i.rowid);return l===void 0&&t.set(i.rowid,l=$(o,e,i)),l},"readable")}}s(y,"resolveContext");function v(o,e,n,t,i,l){const{view:c}=n,u=c.findIndex(x=>x.rowid===i.owner),d=u>=0?c[u]:R(o,"SELECT * FROM objects WHERE rowid = ?").get(i.owner),p=h(d),a=k(o,e,i.name,n),r=b(o,i),{heads_key:w,unlisted:_,source_path:j,...g}=i;return{object:g,owner:d,ownerHandle:p,viewRank:u>=0?u:c.length,ownNode:e.class==="node"&&i.owner===e.node.rowid,shipped:j!==null,kind:i.kind==="preference"?"preference":"knowledge",name:i.name,displayName:"object"in a&&a.object.rowid===i.rowid?i.name:`${p}/${i.name}`,preview:i.preview,unlisted:_!==0,headsKey:w,bodyHash:r,body:s(()=>D(r,j),"body"),rules:l,limit:H(t,d,e)}}s(v,"toDeliveryDoc");function J(o,e,n){const t=m(o,e),i=y(o,e,t),l=E(o,e),c=new Map;for(const r of S(o,n,t.map(w=>w.rowid)))c.set(r.object,[...c.get(r.object)??[],r]);const u=e.class==="node"&&n.includes("boot")?f(o,"o.owner = ?",[e.node.rowid]):[],d=[...new Set([...c.keys(),...u.map(r=>r.rowid)])],p=d.length===0?[]:f(o,`o.rowid IN (${d.map(()=>"?").join(",")})`,d),a=new Map(t.map((r,w)=>[r.rowid,w]));return p.filter(i.readable).sort((r,w)=>a.get(r.owner)-a.get(w.owner)||r.rowid-w.rowid).map(r=>v(o,e,i,l,r,c.get(r.rowid)??[]))}s(J,"loadCorpus");function P(o,e,n){const t=f(o,"o.rowid = ?",[n.rowid])[0];if(!t)throw new Error(`document ${n.id} has no documents row`);return v(o,e,y(o,e,m(o,e)),E(o,e),t,N(o,t))}s(P,"deliveryDocOf");function U(o,e,n){const t=y(o,e,m(o,e)),i=E(o,e);return f(o,"o.owner = ? AND d.unlisted = 0",[n.rowid]).filter(t.readable).map(l=>v(o,e,t,i,l,N(o,l)))}s(U,"listingCorpus");function q(o,e){if(e.class!=="node")throw new Error(`nodeSubject needs a node reader, got ${e.class}`);const n=L(e.node.id,e.repos);if(n===null)throw new Error(`no node ${e.node.id}`);return n}s(q,"nodeSubject");export{P as deliveryDocOf,H as deliveryLimitFor,U as listingCorpus,J as loadCorpus,q as nodeSubject,h as ownerHandle,C as typedLimitKey};
3
+ ORDER BY number DESC LIMIT 1`).get(e.rowid,...n);if(!t)throw new Error(`document ${e.id} has no head revision ${e.heads_key}`);return t.body_hash}s(b,"headBodyHash");function y(o,e,n){const t=new Map;return{view:n,readable:s(i=>{let c=t.get(i.rowid);return c===void 0&&t.set(i.rowid,c=$(o,e,i)),c},"readable")}}s(y,"resolveContext");function j(o,e,n,t,i,c){const{view:l}=n,d=l.findIndex(x=>x.rowid===i.owner),u=d>=0?l[d]:R(o,"SELECT * FROM objects WHERE rowid = ?").get(i.owner),a=h(u),p=k(o,e,i.name,n),r=b(o,i),{heads_key:w,unlisted:_,source_path:v,...g}=i;return{object:g,owner:u,ownerHandle:a,viewRank:d>=0?d:l.length,ownNode:e.class==="node"&&i.owner===e.node.rowid,shipped:v!==null,kind:i.kind==="preference"?"preference":"knowledge",name:i.name,displayName:"object"in p&&p.object.rowid===i.rowid?i.name:`${a}/${i.name}`,preview:i.preview,unlisted:_!==0,headsKey:w,bodyHash:r,body:s(()=>D(r,v),"body"),rules:c,limit:H(t,u,e)}}s(j,"toDeliveryDoc");function J(o,e,n){const t=m(o,e),i=y(o,e,t),c=E(o,e),l=new Map;for(const r of S(o,n,t.map(w=>w.rowid)))l.set(r.object,[...l.get(r.object)??[],r]);const d=e.class==="node"&&n.includes("boot")?f(o,"o.owner = ?",[e.node.rowid]):[],u=[...new Set([...l.keys(),...d.map(r=>r.rowid)])],a=u.length===0?[]:f(o,`o.rowid IN (${u.map(()=>"?").join(",")})`,u),p=new Map(t.map((r,w)=>[r.rowid,w]));return a.filter(i.readable).sort((r,w)=>p.get(r.owner)-p.get(w.owner)||r.rowid-w.rowid).map(r=>j(o,e,i,c,r,l.get(r.rowid)??[]))}s(J,"loadCorpus");function P(o,e,n){const t=f(o,"o.rowid = ?",[n.rowid])[0];if(!t)throw new Error(`document ${n.id} has no documents row`);return j(o,e,y(o,e,m(o,e)),E(o,e),t,N(o,t))}s(P,"deliveryDocOf");function U(o,e,n){const t=y(o,e,m(o,e)),i=E(o,e);return f(o,"o.owner = ? AND d.unlisted = 0",[n.rowid]).filter(t.readable).map(c=>j(o,e,t,i,c,N(o,c)))}s(U,"listingCorpus");function q(o,e){if(e.class!=="node")throw new Error(`nodeSubject needs a node reader, got ${e.class}`);const n=L(e.node.id,[...e.repos,...e.projectRepos]);if(n===null)throw new Error(`no node ${e.node.id}`);return n}s(q,"nodeSubject");export{P as deliveryDocOf,H as deliveryLimitFor,U as listingCorpus,J as loadCorpus,q as nodeSubject,h as ownerHandle,C as typedLimitKey};
@@ -1,6 +1,6 @@
1
- var F=Object.defineProperty;var a=(t,e)=>F(t,"name",{value:e,configurable:!0});import{prepared as y}from"../../sqlite-statements.js";import{dirname as L}from"node:path";import{clearLedger as $,demoteTranscript as M,freezePreferences as g,loadLedger as P,saveLedger as T}from"../../graph/exposures.js";import{recordWatch as H}from"../../graph/watches.js";import{publish as x}from"../../graph/events.js";import{viewOf as W}from"../../graph/names.js";import{checkoutsForCwd as v}from"../../graph/repo-sync/repos.js";import{profileObject as Y}from"../../graph/reader.js";import{ownerForGrantee as U}from"../../graph/objects.js";import{resolveName as B}from"../../graph/names.js";import{SCOPE_VOCABULARY as G}from"../../scopes.js";import{getNode as J}from"../../canvas/index.js";import{recordMemoryReadsIn as K}from"../../canvas/memory-reads.js";import{lookupProfile as V}from"../../profiles/manifest.js";import{getDefaultProfileId as X}from"../../profiles/default-binding.js";import{effectiveScopes as z,profileNameFor as Q,scopeForCwd as Z}from"../subject-fields.js";import{spineDepth as q}from"../subject.js";import{appForNode as ee}from"../../canvas/node-agent-paths.js";import{interpolateNodePaths as te}from"../../canvas/paths.js";import{readableProjectForScopes as oe}from"../../spaces/permissions.js";import{ApiError as u}from"../../../api/errors.js";import{buildContextBearings as ne}from"../../runtime/bearings.js";import{deliveryDocOf as j,listingCorpus as re,loadCorpus as w,nodeSubject as N}from"./corpus.js";import{listingPrefixes as ie}from"./listings.js";import{planDelivery as ce}from"./plan.js";import{renderEvent as O,renderExplicitRead as k}from"./render-event.js";import{renderConfigDelta as se,renderKnowledge as de,renderPreferences as _,renderWorkspaceOpen as S}from"./render-boot.js";function f(t){if(t.class!=="node")throw new Error(`delivery needs a node reader, got ${t.class}`);return t}a(f,"nodeOf");const R=-1;function h(t){return t.node.rowid===R}a(h,"isHypothetical");function E(t,e){if(!h(e))return ee(e.node.id);const o=y(t,"SELECT external_id FROM identities WHERE object = ?").get(e.app.rowid);if(!o)throw new Error(`app object ${e.app.id} has no identity`);return o.external_id}a(E,"granteeOf");function ae(t,e,o,r=i=>v(t,i)){const i=f(e).node,d=new Set,n=new Date().toISOString(),c=[];for(const{doc:s,level:l}of o)l!=="content"||d.has(s.object.rowid)||(d.add(s.object.rowid),H(t,i,s.object,"deliver",{readHeadsKey:s.headsKey}),x(t,{event:"read",object:s.object,actor:i,attrs:{}},r),c.push({name:s.name,scope:s.ownerHandle,at:n,type:"loaded"}));h(f(e))||K(t,i.id,c)}a(ae,"recordContentDelivery");function C(t,e,o){const r=v(t,L(o)).filter(i=>!e.repos.some(d=>d.rowid===i.repo.rowid));return r.length===0?e:{...e,repos:[...e.repos,...r.map(i=>i.repo)],checkouts:[...e.checkouts,...r]}}a(C,"fileReadReader");function le(t,e,o){const r=E(t,e);if(!(o.startsWith("/")&&(process.platform!=="linux"&&r==="app:terminal"||oe(o,r,e.scopes??[],null))))throw new u(403,"scope_missing","read file is outside the run mounts")}a(le,"assertReadableFile");function pe(t){const e=f(t.reader),o=e.node,r=E(t.db,e),i=w(t.db,t.reader,["slash-command"]),d=i.map(n=>{const c=n.body();return{name:n.displayName,command_name:n.displayName,description:n.preview,canonical_body:c,execution_body:te(c,o.id,r)}});return{docs:i,commands:d}}a(pe,"slashCommands");function ue(t){if(t.shipped)return"builtin";switch(t.owner.type){case"node":return"node";case"repo":return"project";case"profile":return"profile";default:return"user"}}a(ue,"scopeOf");function me(t,e){const o=[],r=new Set;for(const i of W(t,e)){const d=re(t,e,i);for(const n of d){o.push({name:n.displayName,kind:n.kind,scope:ue(n),shortForm:n.preview,gatewayVisible:!n.shipped}),r.add(n.displayName);const c=n.displayName===n.name?"":`${n.ownerHandle}/`;for(const s of ie(d,n.name))r.add(`${c}${s}`)}}return{refs:o,names:[...r]}}a(me,"composerNames");function D(t,e){const o=f(t.reader),{db:r,ledger:i}=t,d=a(()=>{if(typeof e.subject!="string"||e.subject==="")throw new u(400,"usage",`delivery activity ${e.activity} requires a subject`);return e.subject},"requireSubject");switch(e.activity){case"boot":{if(e.inherited===!0){for(const[s,l]of i.objects)if(l.transcript===void 0)i.objects.delete(s);else{const{system:p,...m}=l;i.objects.set(s,m)}for(const[s,l]of i.listed)l.transcript===void 0?i.listed.delete(s):i.listed.set(s,{transcript:l.transcript});return i.preferenceSnapshot=null,{result:{content:"",inherited:!0},delivered:[]}}const n=h(o)?fe(t):ne(o.node.id,t),c=_({...t,channel:"system"});return g(i,c.content),{result:{content:n.content},delivered:[...n.delivered,...c.delivered]}}case"system-prompt":{if(i.preferenceSnapshot!==null)return{result:{content:i.preferenceSnapshot},delivered:[]};const n=_({...t,channel:"system"});return{result:{content:g(i,n.content)},delivered:n.delivered}}case"compact":{for(const n of w(r,o,["pre-command"]))M(i,n.object.rowid);return{result:{content:""},delivered:[]}}case"persona":{const n=se(t);return{result:{content:n.content},delivered:n.delivered}}case"file-read":{const n=d();le(r,o,n);const c=C(r,o,n),s={...t,reader:c},l=O(s,"file-read",{absoluteFile:n},w(r,c,["file-read"]));return{result:{content:l.content},delivered:l.delivered}}case"command":case"pre-command":{const n=O(t,e.activity,{command:d()});return{result:{content:n.content},delivered:n.delivered}}case"slash-command":{const{docs:n,commands:c}=pe(t);if(e.subject===void 0||e.subject==="")return{result:{content:"",commands:c},delivered:[]};const s=n.findIndex(m=>m.displayName===e.subject||m.name===e.subject);if(s<0)throw new u(404,"not_found",`no slash command named ${e.subject}`);const l=n[s],p=k(t,l,!0);return{result:{content:"",execution_body:c[s].execution_body,blocks:p.attachments===""?[]:[p.attachments]},delivered:[{doc:l,level:"content"},...p.delivered]}}case"workspace-open":{const n=S(t);return{result:{content:n.content},delivered:n.delivered}}case"document-read":{const n=I(r,o,d()),c=k(t,j(r,o,n),!0);return{result:{content:c.attachments},delivered:c.delivered}}case"names":return{result:{content:"",...me(r,o)},delivered:[]};default:throw new u(400,"usage",`unknown delivery activity ${String(e.activity)}`)}}a(D,"render");function fe(t){const e=de(t),o=S(t);return{content:[e.content,o.content].filter(r=>r!=="").join(`
2
- `),delivered:[...e.delivered,...o.delivered]}}a(fe,"hypotheticalBearings");function I(t,e,o){const r=B(t,e,o);if("object"in r&&r.object.type==="document")return r.object;throw"ambiguous"in r?new u(409,"ambiguous",`${o} names ${r.ambiguous.length} documents; name one as <type>:<owner>/<name>`):new u(404,"not_found",`no document ${o} the reader can read`)}a(I,"readDocument");const we=["workspace-open","document-read"];function Je(t,e,o){if(we.includes(o.activity))throw new u(400,"usage",`delivery activity ${o.activity} is available to a dry run only`);const r=f(e).node;o.activity==="boot"&&o.inherited!==!0&&$(t,r.rowid);const i=P(t,r.rowid),d={db:t,reader:e,subject:N(t,e),ledger:i,channel:"transcript"},{result:n,delivered:c}=D(d,o);return ae(t,e,c),T(t,i),n}a(Je,"deliver");const ye={boot:"boot","workspace-open":"workspace-open","file-read":"file-read","document-read":"document-read",command:"command","pre-command":"pre-command","slash-command":"slash-command"};function Ke(t,e){const o=J(t);if(o===null)throw new u(404,"not_found",`no node ${t}`);return{cwd:e.cwd??o.cwd,profile:e.profile!==void 0?e.profile:o.profile_id,grantee:e.grantee??o.grantee,kind:e.kind??o.kind,mode:e.mode??o.mode,lifecycle:e.lifecycle??o.lifecycle,hasManager:e.hasManager??(o.parent??null)!==null,depth:e.depth??q(t)}}a(Ke,"hypotheticalFrom");function ve(t,e){if(t===null)return null;if(t===void 0||t==="")return X(e);const o=V(t);if(o.kind!=="resolved"){const r=`no profile ${t}`;throw new u(404,"not_found",r,{error:"not_found",message:r,next:"List profile ids and names with `crtr profile list`."})}return o.entry.profileId}a(ve,"resolveProfileId");function Ve(t,e){const o=U(t,e.grantee??"app:terminal"),r=ve(e.profile,e.cwd),i=v(t,e.cwd),d=i.map(p=>p.repo),n=e.mode??"base",s={class:"node",node:{rowid:R,id:"hypothetical",type:"node",kind:e.kind??"general",owner:o.rowid,privacy:"private",name:"hypothetical",preview:"",created_at:new Date(0).toISOString(),deleted_at:null},app:o,scopes:[...G],cwd:e.cwd,repos:d,checkouts:i,profile:r===null?null:Y(t,r,o)},l={kind:e.kind??"general",mode:n,lifecycle:e.lifecycle??(n==="orchestrator"?"resident":"terminal"),hasManager:e.hasManager??!0,cwd:e.cwd,scope:Z(e.cwd),orchestration:{depth:e.depth??0},profile:Q(r),scopes:z(null),repo:d.map(p=>p.name)};return{reader:s,subject:l}}a(Ve,"hypotheticalReader");function Xe(t,e,o,r,i){const n={node:f(e).node.rowid,preferenceSnapshot:null,objects:new Map,listed:new Map},c={...i??N(t,e),...r},s={db:t,reader:e,subject:c,ledger:n,channel:"transcript"},{result:l}=D(s,o),p=ye[o.activity];let m=null;if(p!==void 0){const A=o.activity==="file-read"?{absoluteFile:o.subject}:o.activity==="command"||o.activity==="pre-command"?{command:o.subject}:o.activity==="document-read"&&o.subject!==void 0?{doc:j(t,e,I(t,e,o.subject))}:{},b=o.activity==="file-read"&&o.subject!==void 0?C(t,f(e),o.subject):e;m=ce(t,c,b,w(t,b,[p]),p,A)}return{result:l,plan:m}}a(Xe,"inspect");function ze(t,e,o){return y(t,`SELECT o.*, MAX(r.number) AS to_seq, GROUP_CONCAT(DISTINCT a.id) AS authors
1
+ var L=Object.defineProperty;var a=(t,e)=>L(t,"name",{value:e,configurable:!0});import{prepared as v}from"../../sqlite-statements.js";import{dirname as F}from"node:path";import{clearLedger as $,demoteTranscript as M,freezePreferences as g,loadLedger as P,saveLedger as T}from"../../graph/exposures.js";import{recordWatch as H}from"../../graph/watches.js";import{publish as x}from"../../graph/events.js";import{viewOf as W}from"../../graph/names.js";import{checkoutsForCwd as y}from"../../graph/repo-sync/repos.js";import{liveLookups as Y,profileObject as U,viewRepos as B}from"../../graph/reader.js";import{ownerForGrantee as G}from"../../graph/objects.js";import{resolveName as J}from"../../graph/names.js";import{SCOPE_VOCABULARY as K}from"../../scopes.js";import{getNode as V}from"../../canvas/index.js";import{recordMemoryReadsIn as X}from"../../canvas/memory-reads.js";import{lookupProfile as z}from"../../profiles/manifest.js";import{getDefaultProfileId as Q}from"../../profiles/default-binding.js";import{effectiveScopes as Z,profileNameFor as q,scopeForCwd as ee}from"../subject-fields.js";import{spineDepth as te}from"../subject.js";import{appForNode as oe}from"../../canvas/node-agent-paths.js";import{interpolateNodePaths as ne}from"../../canvas/paths.js";import{readableProjectForScopes as re}from"../../spaces/permissions.js";import{ApiError as u}from"../../../api/errors.js";import{buildContextBearings as ie}from"../../runtime/bearings.js";import{deliveryDocOf as j,listingCorpus as se,loadCorpus as w,nodeSubject as N}from"./corpus.js";import{listingPrefixes as ce}from"./listings.js";import{planDelivery as de}from"./plan.js";import{renderEvent as O,renderExplicitRead as k}from"./render-event.js";import{renderConfigDelta as ae,renderKnowledge as le,renderPreferences as _,renderWorkspaceOpen as S}from"./render-boot.js";function f(t){if(t.class!=="node")throw new Error(`delivery needs a node reader, got ${t.class}`);return t}a(f,"nodeOf");const R=-1;function h(t){return t.node.rowid===R}a(h,"isHypothetical");function E(t,e){if(!h(e))return oe(e.node.id);const o=v(t,"SELECT external_id FROM identities WHERE object = ?").get(e.app.rowid);if(!o)throw new Error(`app object ${e.app.id} has no identity`);return o.external_id}a(E,"granteeOf");function pe(t,e,o,r=i=>y(t,i)){const i=f(e).node,d=new Set,n=new Date().toISOString(),s=[];for(const{doc:c,level:l}of o)l!=="content"||d.has(c.object.rowid)||(d.add(c.object.rowid),H(t,i,c.object,"deliver",{readHeadsKey:c.headsKey}),x(t,{event:"read",object:c.object,actor:i,attrs:{}},r),s.push({name:c.name,scope:c.ownerHandle,at:n,type:"loaded"}));h(f(e))||X(t,i.id,s)}a(pe,"recordContentDelivery");function C(t,e,o){const r=y(t,F(o)).filter(i=>!e.repos.some(d=>d.rowid===i.repo.rowid));return r.length===0?e:{...e,repos:[...e.repos,...r.map(i=>i.repo)],checkouts:[...e.checkouts,...r]}}a(C,"fileReadReader");function ue(t,e,o){const r=E(t,e);if(!(o.startsWith("/")&&(process.platform!=="linux"&&r==="app:terminal"||re(o,r,e.scopes??[],null))))throw new u(403,"scope_missing","read file is outside the run mounts")}a(ue,"assertReadableFile");function me(t){const e=f(t.reader),o=e.node,r=E(t.db,e),i=w(t.db,t.reader,["slash-command"]),d=i.map(n=>{const s=n.body();return{name:n.displayName,command_name:n.displayName,description:n.preview,canonical_body:s,execution_body:ne(s,o.id,r)}});return{docs:i,commands:d}}a(me,"slashCommands");function fe(t){if(t.shipped)return"builtin";switch(t.owner.type){case"node":return"node";case"repo":return"project";case"profile":return"profile";default:return"user"}}a(fe,"scopeOf");function we(t,e){const o=[],r=new Set;for(const i of W(t,e)){const d=se(t,e,i);for(const n of d){o.push({name:n.displayName,kind:n.kind,scope:fe(n),shortForm:n.preview,gatewayVisible:!n.shipped}),r.add(n.displayName);const s=n.displayName===n.name?"":`${n.ownerHandle}/`;for(const c of ce(d,n.name))r.add(`${s}${c}`)}}return{refs:o,names:[...r]}}a(we,"composerNames");function D(t,e){const o=f(t.reader),{db:r,ledger:i}=t,d=a(()=>{if(typeof e.subject!="string"||e.subject==="")throw new u(400,"usage",`delivery activity ${e.activity} requires a subject`);return e.subject},"requireSubject");switch(e.activity){case"boot":{if(e.inherited===!0){for(const[c,l]of i.objects)if(l.transcript===void 0)i.objects.delete(c);else{const{system:p,...m}=l;i.objects.set(c,m)}for(const[c,l]of i.listed)l.transcript===void 0?i.listed.delete(c):i.listed.set(c,{transcript:l.transcript});return i.preferenceSnapshot=null,{result:{content:"",inherited:!0},delivered:[]}}const n=h(o)?ve(t):ie(o.node.id,t),s=_({...t,channel:"system"});return g(i,s.content),{result:{content:n.content},delivered:[...n.delivered,...s.delivered]}}case"system-prompt":{if(i.preferenceSnapshot!==null)return{result:{content:i.preferenceSnapshot},delivered:[]};const n=_({...t,channel:"system"});return{result:{content:g(i,n.content)},delivered:n.delivered}}case"compact":{for(const n of w(r,o,["pre-command"]))M(i,n.object.rowid);return{result:{content:""},delivered:[]}}case"persona":{const n=ae(t);return{result:{content:n.content},delivered:n.delivered}}case"file-read":{const n=d();ue(r,o,n);const s=C(r,o,n),c={...t,reader:s},l=O(c,"file-read",{absoluteFile:n},w(r,s,["file-read"]));return{result:{content:l.content},delivered:l.delivered}}case"command":case"pre-command":{const n=O(t,e.activity,{command:d()});return{result:{content:n.content},delivered:n.delivered}}case"slash-command":{const{docs:n,commands:s}=me(t);if(e.subject===void 0||e.subject==="")return{result:{content:"",commands:s},delivered:[]};const c=n.findIndex(m=>m.displayName===e.subject||m.name===e.subject);if(c<0)throw new u(404,"not_found",`no slash command named ${e.subject}`);const l=n[c],p=k(t,l,!0);return{result:{content:"",execution_body:s[c].execution_body,blocks:p.attachments===""?[]:[p.attachments]},delivered:[{doc:l,level:"content"},...p.delivered]}}case"workspace-open":{const n=S(t);return{result:{content:n.content},delivered:n.delivered}}case"document-read":{const n=I(r,o,d()),s=k(t,j(r,o,n),!0);return{result:{content:s.attachments},delivered:s.delivered}}case"names":return{result:{content:"",...we(r,o)},delivered:[]};default:throw new u(400,"usage",`unknown delivery activity ${String(e.activity)}`)}}a(D,"render");function ve(t){const e=le(t),o=S(t);return{content:[e.content,o.content].filter(r=>r!=="").join(`
2
+ `),delivered:[...e.delivered,...o.delivered]}}a(ve,"hypotheticalBearings");function I(t,e,o){const r=J(t,e,o);if("object"in r&&r.object.type==="document")return r.object;throw"ambiguous"in r?new u(409,"ambiguous",`${o} names ${r.ambiguous.length} documents; name one as <type>:<owner>/<name>`):new u(404,"not_found",`no document ${o} the reader can read`)}a(I,"readDocument");const ye=["workspace-open","document-read"];function Ve(t,e,o){if(ye.includes(o.activity))throw new u(400,"usage",`delivery activity ${o.activity} is available to a dry run only`);const r=f(e).node;o.activity==="boot"&&o.inherited!==!0&&$(t,r.rowid);const i=P(t,r.rowid),d={db:t,reader:e,subject:N(t,e),ledger:i,channel:"transcript"},{result:n,delivered:s}=D(d,o);return pe(t,e,s),T(t,i),n}a(Ve,"deliver");const he={boot:"boot","workspace-open":"workspace-open","file-read":"file-read","document-read":"document-read",command:"command","pre-command":"pre-command","slash-command":"slash-command"};function Xe(t,e){const o=V(t);if(o===null)throw new u(404,"not_found",`no node ${t}`);return{cwd:e.cwd??o.cwd,profile:e.profile!==void 0?e.profile:o.profile_id,grantee:e.grantee??o.grantee,kind:e.kind??o.kind,mode:e.mode??o.mode,lifecycle:e.lifecycle??o.lifecycle,hasManager:e.hasManager??(o.parent??null)!==null,depth:e.depth??te(t)}}a(Xe,"hypotheticalFrom");function be(t,e){if(t===null)return null;if(t===void 0||t==="")return Q(e);const o=z(t);if(o.kind!=="resolved"){const r=`no profile ${t}`;throw new u(404,"not_found",r,{error:"not_found",message:r,next:"List profile ids and names with `crtr profile list`."})}return o.entry.profileId}a(be,"resolveProfileId");function ze(t,e){const o=G(t,e.grantee??"app:terminal"),r=be(e.profile,e.cwd),i=B(e.cwd,r,p=>y(t,p),Y(t)),{repos:d}=i,n=e.mode??"base",c={class:"node",node:{rowid:R,id:"hypothetical",type:"node",kind:e.kind??"general",owner:o.rowid,privacy:"private",name:"hypothetical",preview:"",created_at:new Date(0).toISOString(),deleted_at:null},app:o,scopes:[...K],cwd:e.cwd,...i,profile:r===null?null:U(t,r,o)},l={kind:e.kind??"general",mode:n,lifecycle:e.lifecycle??(n==="orchestrator"?"resident":"terminal"),hasManager:e.hasManager??!0,cwd:e.cwd,scope:ee(e.cwd),orchestration:{depth:e.depth??0},profile:q(r),scopes:Z(null),repo:[...d,...i.projectRepos].map(p=>p.name)};return{reader:c,subject:l}}a(ze,"hypotheticalReader");function Qe(t,e,o,r,i){const n={node:f(e).node.rowid,preferenceSnapshot:null,objects:new Map,listed:new Map},s={...i??N(t,e),...r},c={db:t,reader:e,subject:s,ledger:n,channel:"transcript"},{result:l}=D(c,o),p=he[o.activity];let m=null;if(p!==void 0){const A=o.activity==="file-read"?{absoluteFile:o.subject}:o.activity==="command"||o.activity==="pre-command"?{command:o.subject}:o.activity==="document-read"&&o.subject!==void 0?{doc:j(t,e,I(t,e,o.subject))}:{},b=o.activity==="file-read"&&o.subject!==void 0?C(t,f(e),o.subject):e;m=de(t,s,b,w(t,b,[p]),p,A)}return{result:l,plan:m}}a(Qe,"inspect");function Ze(t,e,o){return v(t,`SELECT o.*, MAX(r.number) AS to_seq, GROUP_CONCAT(DISTINCT a.id) AS authors
3
3
  FROM watches w JOIN objects o ON o.rowid = w.object AND o.type = 'document' AND o.deleted_at IS NULL
4
4
  JOIN revisions r ON r.document = o.rowid LEFT JOIN objects a ON a.rowid = r.author
5
5
  WHERE w.watcher = ? AND r.created_at > ? AND (r.author IS NULL OR r.author != ?)
6
- GROUP BY o.rowid ORDER BY o.rowid`).all(e.rowid,o,e.rowid).map(({to_seq:i,authors:d,...n})=>{const c=y(t,"SELECT MAX(number) AS seq FROM revisions WHERE document = ? AND created_at <= ?").get(n.rowid,o);return{doc:n,from:c.seq,to:i,authors:d===null?[]:d.split(",")}})}a(ze,"changedWhileDone");export{we as DRY_RUN_ONLY,ze as changedWhileDone,Je as deliver,Ke as hypotheticalFrom,Ve as hypotheticalReader,Xe as inspect,ae as recordContentDelivery};
6
+ GROUP BY o.rowid ORDER BY o.rowid`).all(e.rowid,o,e.rowid).map(({to_seq:i,authors:d,...n})=>{const s=v(t,"SELECT MAX(number) AS seq FROM revisions WHERE document = ? AND created_at <= ?").get(n.rowid,o);return{doc:n,from:s.seq,to:i,authors:d===null?[]:d.split(",")}})}a(Ze,"changedWhileDone");export{ye as DRY_RUN_ONLY,Ze as changedWhileDone,Ve as deliver,Xe as hypotheticalFrom,ze as hypotheticalReader,Qe as inspect,pe as recordContentDelivery};
@@ -30,8 +30,10 @@ export interface NodeConfigSubject {
30
30
  profile: string | null;
31
31
  /** Effective per-run scopes. */
32
32
  scopes: readonly string[];
33
- /** DERIVED: handles of the repos enclosing `cwd`, nearest first. A rule's
34
- * `if: {repo: crouter}` holds when any element is `crouter`. */
33
+ /** DERIVED: handles of the repos eligible for the node's rules: those enclosing `cwd`
34
+ * (nearest first), then the repos of its profile's project paths. A rule's
35
+ * `if: {repo: crouter}` holds when any element is `crouter`; a condition about the
36
+ * directory itself uses `cwd`. */
35
37
  repo: readonly string[];
36
38
  }
37
39
  export declare function effectiveScopes(scopes: readonly string[] | null | undefined): string[];
@@ -1 +1 @@
1
- var c=Object.defineProperty;var n=(r,o)=>c(r,"name",{value:o,configurable:!0});import{getNode as l,getRow as i}from"../canvas/index.js";import{openDb as f}from"../canvas/db.js";import{reposForCwd as s}from"../graph/repo-sync/repos.js";import{effectiveScopes as a,scopeForCwd as m,profileNameFor as d}from"./subject-fields.js";import{effectiveScopes as x,scopeForCwd as C,profileNameFor as v}from"./subject-fields.js";function u(r){let o=r;const e=new Set;let t=0;for(;!e.has(o);){e.add(o);const p=i(o);if(p===null||p.parent==null)break;o=p.parent,t++}return t}n(u,"spineDepth");function N(r,o){const e=l(r);return e===null?null:{kind:e.kind,mode:e.mode,lifecycle:e.lifecycle,hasManager:(e.parent??null)!==null,cwd:e.cwd,scope:m(e.cwd),orchestration:{depth:u(r)},profile:d(e.profile_id),scopes:a(e.scopes),repo:(o??s(f(),e.cwd)).map(t=>t.name)}}n(N,"assembleNodeSubject");export{N as assembleNodeSubject,x as effectiveScopes,v as profileNameFor,C as scopeForCwd,u as spineDepth};
1
+ var c=Object.defineProperty;var n=(r,o)=>c(r,"name",{value:o,configurable:!0});import{getNode as i,getRow as l}from"../canvas/index.js";import{openDb as s}from"../canvas/db.js";import{checkoutsForCwd as f}from"../graph/repo-sync/repos.js";import{liveLookups as m,viewRepos as u}from"../graph/reader.js";import{effectiveScopes as a,scopeForCwd as d,profileNameFor as w}from"./subject-fields.js";import{effectiveScopes as j,scopeForCwd as y,profileNameFor as D}from"./subject-fields.js";function b(r){let o=r;const e=new Set;let t=0;for(;!e.has(o);){e.add(o);const p=l(o);if(p===null||p.parent==null)break;o=p.parent,t++}return t}n(b,"spineDepth");function h(r,o){const e=s(),t=u(r,o,p=>f(e,p),m(e));return[...t.repos,...t.projectRepos]}n(h,"eligibleRepos");function S(r,o){const e=i(r);return e===null?null:{kind:e.kind,mode:e.mode,lifecycle:e.lifecycle,hasManager:(e.parent??null)!==null,cwd:e.cwd,scope:d(e.cwd),orchestration:{depth:b(r)},profile:w(e.profile_id),scopes:a(e.scopes),repo:(o??h(e.cwd,e.profile_id??null)).map(t=>t.name)}}n(S,"assembleNodeSubject");export{S as assembleNodeSubject,j as effectiveScopes,D as profileNameFor,y as scopeForCwd,b as spineDepth};
@@ -1 +1 @@
1
- var re=Object.defineProperty;var u=(t,e)=>re(t,"name",{value:e,configurable:!0});import{prepared as P}from"../../../core/sqlite-statements.js";import{ApiError as g}from"../../../api/errors.js";import{DOC_KINDS as q,DOC_UNSET_FIELDS as H}from"../../../api/dto/docs.js";import{PRIVACY_LEVELS_DTO as V}from"../../../api/dto/objects.js";import{withCanvasWrite as k}from"../../../core/canvas/db.js";import{CrtrError as ie}from"../../../core/errors.js";import{emitEvent as se}from"../../../core/events/emit.js";import{canChange as K,spaceOf as ae}from"../../../core/graph/access.js";import{get as A}from"../../../core/graph/bodies.js";import{deleteDocument as ce,documentRecord as F,editDocument as de,heads as I,history as ue,linksIn as U,moveDocument as fe,readBase as le,setDocumentRecord as me,withCurrentNames as pe,writeDocument as he}from"../../../core/graph/documents.js";import{resolveName as X}from"../../../core/graph/names.js";import{getByRowid as M}from"../../../core/graph/objects.js";import{noteRepoDocumentsChanged as we}from"../../../core/graph/repo-sync/index.js";import{checkoutsForCwd as ye}from"../../../core/graph/repo-sync/repos.js";import{lintRules as ve,rulesOf as ge}from"../../../core/graph/rules.js";import{unifiedDiff as _e}from"../../../core/graph/diff.js";import{ownerFromFlag as W,readerFor as E,resolveRef as z}from"../reader.js";function _(t,e,n,o,i={}){return new g(t,e,n,{...i,error:e,message:n,next:o})}u(_,"refuse");const b=u((t,e)=>new g(400,"invalid_request",t,e),"invalid");function D(t){const e=t.body;if(e===void 0)return{};if(typeof e!="object"||e===null||Array.isArray(e))throw b("the request body must be a JSON object");return e}u(D,"bodyOf");const x=u(t=>e=>ye(t,e),"reposFnFor");function C(t,e){if(t instanceof ie){const n=t.details;if(t.code==="read_first")throw _(409,"read_first",t.message,`Read it first with \`crtr canvas read ${e?.id??"<ref>"}\`, then rerun the edit.`,n??{});if(t.code==="conflict")throw new g(409,"name_taken",t.message,n);if(t.code==="forbidden")throw new g(403,"forbidden",t.message,n);if(n?.rule==="repo-public-only")throw _(422,"repo_documents_are_public",t.message,'Own it as `user` and add a rule with `if: {"repo": "<handle>"}` so it loads only in that repo.')}throw t}u(C,"mapGraphError");function B(t,e,n){const o=z(t,e,decodeURIComponent(n));if("remote"in o)throw _(404,"not_found",G(o.remote),"Check the name with `crtr canvas search`.");if("deleted"in o)throw new g(404,"not_found",`${o.deleted.name} is deleted`,{id:o.deleted.id,deleted:!0});if(o.type!=="document")throw b(`${n} is a ${o.type}, not a document`);return o}u(B,"refDocument");function G(t){return`no object named ${t} on this runtime; it may be on another runtime, or the name may be misspelled`}u(G,"remoteMessage");function be(t,e){const n=e.owner===null?null:M(t,e.owner);return n?n.type==="node"?n.id:n.name:""}u(be,"ownerHandle");function j(t,e){const n=be(t,e);return n?`${n}/${e.name}`:e.name}u(j,"canonicalName");function ke(t,e){const n=e.owner===null?null:M(t,e.owner),o=n?{id:n.id,type:n.type,handle:n.type==="node"?n.id:n.name}:null;return{id:e.id,type:e.type,kind:e.kind,owner:o,privacy:e.privacy,name:e.name,preview:e.preview??"",created_at:e.created_at,deleted_at:e.deleted_at}}u(ke,"objectDTO");function Ee(t,e){const n=e.owner===null?null:M(t,e.owner);return n?.type==="repo"?n:null}u(Ee,"repoOwner");function S(t,e,n,o){const i=[...new Map(n.flatMap(r=>{const f=Ee(t,r);return f?[[f.id,f]]:[]})).values()],a=e.class==="node"?e.cwd:o;return{repos:i,...a!==void 0?{cwd:a}:{}}}u(S,"repoChange");function T(t){for(const e of t.repos)try{we(e,t.cwd)}catch(n){se({level:"warn",event:"repo_sync.failed",outcome:"failed",error:n,fields:{repo:e.name,step:"export",reason:n instanceof Error?n.message:String(n)}})}}u(T,"syncChanged");const Y=/^[A-Za-z0-9_-]+(\/[A-Za-z0-9_-]+)*$/;function Oe(t,e){if(t===void 0)return;if(!Array.isArray(t)){e.push("`rules` must be a list");return}const n=t.map(i=>{if(typeof i!="object"||i===null||Array.isArray(i))return i;const{match_frontmatter:a,...r}=i;return"matchFrontmatter"in r?{...r,match_frontmatter:r.matchFrontmatter}:a===void 0?r:{...r,matchFrontmatter:a}}),o=ve(n);if(o){e.push(o.replaceAll("matchFrontmatter","match_frontmatter"));return}return n}u(Oe,"rulesFromDTO");function Z(t,e){const n={},o=u(s=>{const c=t[s];if(c!==void 0){if(typeof c!="string"){e.push(`\`${s}\` must be a string`);return}return c}},"str"),i=o("preview");i!==void 0&&(i.trim()===""||/[\r\n]/.test(i)?e.push("`preview` must be one non-empty line"):n.preview=i);const a=o("summary");a!==void 0&&(n.summary=a);const r=o("why_it_exists");if(r!==void 0&&(n.whyItExists=r),t.privacy!==void 0&&(V.includes(t.privacy)?n.privacy=t.privacy:e.push(`\`privacy\` must be one of ${V.join(", ")}`)),t.unlisted!==void 0&&(typeof t.unlisted!="boolean"?e.push("`unlisted` must be a boolean"):n.unlisted=t.unlisted),t.lint_ignore!==void 0){const s=t.lint_ignore;!Array.isArray(s)||s.some(c=>typeof c!="string")?e.push("`lint_ignore` must be a list of strings"):n.lintIgnore=s}if(t.extensions!==void 0){const s=t.extensions;typeof s!="object"||s===null||Array.isArray(s)||Object.values(s).some(c=>typeof c!="object"||c===null||Array.isArray(c))?e.push("`extensions` must map each plugin to an object of fields"):n.extensions=s}const f=Oe(t.rules,e);return f!==void 0&&(n.rules=f),n}u(Z,"fieldsFrom");function J(t){if(t.length>0)throw b(t.join("; "),{errors:t})}u(J,"failOn");function Q(t,e,n,o){const i=U(n).filter(a=>"remote"in X(t,e,a));return{...o.length>0?{unresolved_links:o}:{},...i.length>0?{remote_links:i}:{}}}u(Q,"linkReport");function $e(t){const e=D(t),n=[],o=e.name;(typeof o!="string"||!Y.test(o))&&n.push("`name` must be segments of letters, digits, `_` or `-` joined by `/`"),typeof e.body!="string"&&n.push("`body` must be a string"),typeof e.preview!="string"&&n.push("`preview` is required");const i=e.kind??"knowledge";q.includes(i)||n.push(i==="report"?"reports are written only by `crtr push`":`\`kind\` must be one of ${q.join(", ")}`);const a=Z(e,n);e.cwd!==void 0&&typeof e.cwd!="string"&&n.push("`cwd` must be a string"),J(n);const r=e,{dto:f,change:s}=k(c=>{const h=E(c,t),d=W(c,h,r.owner,r.cwd);if(!K(c,h,d))throw new g(404,"not_found",`no owner ${d.name} you can write to`);try{const{object:p,unresolved:l}=he(c,h,{...a,owner:d,name:r.name,kind:i,preview:a.preview,privacy:a.privacy??"public"},r.body,x(c));return{dto:{id:p.id,name:j(c,p),...Q(c,h,r.body,l)},change:S(c,h,[p],r.cwd)}}catch(p){C(p)}});return T(s),f}u($e,"write");function Ae(t,e){let n=t;e.forEach(({old:o,new:i},a)=>{const r=o===""?-1:n.indexOf(o);if(r<0)throw _(422,"replace_no_match",`replace[${a}]: old text not found in the revision you read`,"Copy the old text exactly from your last read and rerun.",{index:a});if(n.indexOf(o,r+1)>=0)throw _(422,"replace_ambiguous",`replace[${a}]: old text occurs more than once`,"Include more surrounding text so the old text matches once, and rerun.",{index:a});n=n.slice(0,r)+i+n.slice(r+o.length)})}u(Ae,"checkReplacements");function je(t){const e=D(t),n=[],o=e.body!==void 0,i=e.replace!==void 0;o&&i&&n.push("give `body` or `replace`, not both"),o&&typeof e.body!="string"&&n.push("`body` must be a string");const a=e.replace;i&&(!Array.isArray(a)||a.length===0||a.some(d=>typeof d!="object"||d===null||typeof d.old!="string"||typeof d.new!="string"))&&n.push("`replace` must be a non-empty list of {old, new} strings"),(typeof e.rationale!="string"||e.rationale.trim()==="")&&n.push("`rationale` is required"),e.verbatim!==void 0&&typeof e.verbatim!="boolean"&&n.push("`verbatim` must be a boolean"),e.base!==void 0&&(typeof e.base!="string"||e.base==="")&&n.push("`base` must be a non-empty heads key"),e.base!==void 0&&!o&&!i&&n.push("`base` applies only to a `body` or `replace` edit");const r=Z(e,n),f=e.unset??[];if(!Array.isArray(f)||f.some(d=>!H.includes(d)))n.push(`\`unset\` takes only ${H.join(", ")}`);else for(const d of f)d==="summary"&&(r.summary=null),d==="why_it_exists"&&(r.whyItExists=null),d==="unlisted"&&(r.unlisted=!1),d==="lint_ignore"&&(r.lintIgnore=null),d==="extensions"&&(r.extensions=null),d==="rules"&&(r.rules=[]);n.length===0&&!o&&!i&&Object.keys(r).length===0&&n.push("nothing to change: give `body`, `replace`, or a field"),J(n);const s=e,{dto:c,synced:h}=k(d=>{const p=E(d,t),l=B(d,p,t.params.ref),v=j(d,l),$=S(d,p,[l]);try{if(!o&&!i)return me(d,p,l,r),{dto:{id:l.id,name:v,revision:F(d,l).revision},synced:$};const w=s.base??le(d,p,l);if(i&&w!==null){const oe=F(d,l),L=I(d,l);oe.heads_key===w&&L.length===1&&Ae(pe(d,l,A(L[0].body_hash)),s.replace)}const R=s.verbatim===!0,m=o?{body:s.body,verbatim:R}:{replace:s.replace,verbatim:R},y=de(d,p,l,m,s.rationale,w,r,x(d));if("stale"in y)throw _(409,"stale_revision",`${v} changed after your read; it is now at revision ${y.stale.revision}`,`Read it again with \`crtr canvas read ${v}\`, then redo your change on the current text.`,{stale:y.stale});const N=A(I(d,l)[0].body_hash);return{dto:{id:l.id,name:v,revision:y.revision,...Q(d,p,N,y.unresolved)},synced:$}}catch(w){C(w,l)}});return T(h),c}u(je,"edit");function Re(t){const e=D(t);if(e.new_name===void 0&&e.owner===void 0)throw b("give a new name, an owner, or both");if(e.new_name!==void 0&&(typeof e.new_name!="string"||!Y.test(e.new_name)))throw b("`new_name` must be segments of letters, digits, `_` or `-` joined by `/`");const{dto:n,change:o}=k(i=>{const a=E(i,t),r=B(i,a,t.params.ref),f=e.owner===void 0?void 0:W(i,a,e.owner,e.cwd);try{const s=fe(i,a,r,{...e.new_name!==void 0?{name:e.new_name}:{},...f?{owner:f}:{}});return{dto:{id:s.id,name:j(i,s)},change:S(i,a,[r,s],e.cwd)}}catch(s){C(s,r)}});return T(o),n}u(Re,"move");function De(t){const{dto:e,change:n}=k(o=>{const i=E(o,t),a=B(o,i,t.params.ref),r=j(o,a),f=S(o,i,[a]);try{ce(o,i,a,x(o))}catch(s){C(s,a)}return{dto:{id:a.id,name:r},change:f}});return T(n),e}u(De,"remove");function Ce(t){const e=t.query.get("diff"),n=e===null?void 0:Number(e);if(n!==void 0&&!(Number.isInteger(n)&&n>0))throw b("`diff` must be a revision number");return k(o=>{const i=E(o,t),a=z(o,i,decodeURIComponent(t.params.ref));if("remote"in a)throw _(404,"not_found",G(a.remote),"Check the name with `crtr canvas search`.");const r="deleted"in a?a.deleted:a;if(r.type!=="document")throw b(`${r.name} is a ${r.type}, not a document`);const f=ue(o,i,r),s={object:ke(o,r),revisions:f.map(c=>({rev_id:c.rev_id,number:c.number,author_id:c.author,rationale:c.rationale,verbatim:c.verbatim,created_at:c.created_at,parents:c.parents}))};if(n!==void 0){const c=f.find(d=>d.number===n);if(!c)throw new g(404,"not_found",`${r.name} has no revision ${n}`);const h=c.parents[0]===void 0?void 0:f.find(d=>d.rev_id===c.parents[0]);s.diff=_e(h?A(h.body_hash):"",A(c.body_hash))}return s})}u(Ce,"revisionHistory");const ee=1e3,ne=1e3,te=3e3,Se=/^When\b.+\bbecause\b.+/,Te=u(t=>{const e=t.trim();return e===""?0:e.split(/\s+/).length},"words");function Ne(t,e){if(t.some(o=>o.on==="boot"&&o.deliver==="content")&&e>ee)return`body is ${e} words, over the ${ee}-word cap for a boot rule at content (the whole body enters every node's context). Keep the core here and move separate subjects into [[linked]] documents.`;const n=[...new Set(t.filter(o=>o.on!=="boot"&&o.deliver==="content").map(o=>o.on))];return n.length>0&&e>ne?`body is ${e} words, over the ${ne}-word cap for a content rule on ${n.join("/")}. Keep the core here and move separate subjects into [[linked]] documents.`:t.some(o=>o.on==="boot"&&o.deliver==="preview")&&e>te?`body is ${e} words, over the ${te}-word cap for a boot rule at preview. Keep the core here and move separate subjects into [[linked]] documents.`:null}u(Ne,"lengthFinding");function Fe(t){const e=D(t);return k(n=>{const o=E(n,t),i=e.owner===void 0?null:W(n,o,e.owner,e.cwd),a=P(n,"SELECT * FROM objects WHERE type = 'document' AND deleted_at IS NULL AND kind != 'report' ORDER BY name").all(),r=[],f=new Map;for(const s of a){if(i&&s.owner!==i.rowid||!K(n,o,s))continue;const c=F(n,s),h=new Set(c.lint_ignore??[]),d=j(n,s),p=u((m,y,N)=>{h.has(m)||r.push({name:d,severity:y,rule:m,message:N})},"add"),l=ge(n,s),v=I(n,s)[0],$=v?A(v.body_hash):"",w=Ne(l,Te($));w&&p("length","error",w),l.some(m=>m.deliver==="preview")&&!Se.test(s.preview??"")&&p("preview-shape","error",`a rule delivers this document at preview, so the preview must read "When <situation in the reader's work>, read this because <what the reader gains>"`);const R=new Set(P(n,"SELECT written_as FROM edges WHERE type = 'links-to' AND src = ?").all(s.rowid).map(m=>m.written_as));for(const m of U($))R.has(m)||"remote"in X(n,o,m)||p("links","warning",`[[${m}]] names no object in your view`);if(l.some(m=>m.on==="workspace-open"&&m.deliver==="content")){const m=ae(n,s);m.type==="repo"&&!h.has("workspace-open")&&f.set(m.name,[...f.get(m.name)??[],d])}}for(const[s,c]of f)if(!(c.length<2))for(const h of c)r.push({name:h,severity:"warning",rule:"workspace-open",message:`repo ${s} has ${c.length} documents delivered at content on workspace-open (${c.join(", ")}); keep one and link the rest from it`});return{findings:r}})}u(Fe,"lint");const O=u((t,e=200)=>n=>({status:e,body:t(n)}),"ok"),Qe=[{method:"POST",pattern:"/v1/docs",handler:O($e,201)},{method:"PATCH",pattern:"/v1/docs/:ref",handler:O(je)},{method:"POST",pattern:"/v1/docs/:ref/move",handler:O(Re)},{method:"DELETE",pattern:"/v1/docs/:ref",handler:O(De)},{method:"GET",pattern:"/v1/docs/:ref/history",handler:O(Ce)},{method:"POST",pattern:"/v1/docs/lint",handler:O(Fe)}];export{Qe as docRoutes};
1
+ var re=Object.defineProperty;var u=(t,e)=>re(t,"name",{value:e,configurable:!0});import{prepared as P}from"../../../core/sqlite-statements.js";import{ApiError as _}from"../../../api/errors.js";import{DOC_KINDS as q,DOC_UNSET_FIELDS as H}from"../../../api/dto/docs.js";import{PRIVACY_LEVELS_DTO as V}from"../../../api/dto/objects.js";import{withCanvasWrite as k}from"../../../core/canvas/db.js";import{CrtrError as ie}from"../../../core/errors.js";import{emitEvent as se}from"../../../core/events/emit.js";import{canChange as K,spaceOf as ae}from"../../../core/graph/access.js";import{get as A}from"../../../core/graph/bodies.js";import{deleteDocument as ce,documentRecord as F,editDocument as de,heads as I,history as ue,linksIn as U,moveDocument as fe,readBase as le,setDocumentRecord as me,withCurrentNames as pe,writeDocument as he}from"../../../core/graph/documents.js";import{resolveName as X}from"../../../core/graph/names.js";import{getByRowid as M}from"../../../core/graph/objects.js";import{noteRepoDocumentsChanged as we}from"../../../core/graph/repo-sync/index.js";import{checkoutsForCwd as ye}from"../../../core/graph/repo-sync/repos.js";import{lintRules as ve,rulesOf as ge}from"../../../core/graph/rules.js";import{unifiedDiff as _e}from"../../../core/graph/diff.js";import{ownerFromFlag as W,readerFor as E,resolveRef as z}from"../reader.js";function v(t,e,n,o,i={}){return new _(t,e,n,{...i,error:e,message:n,next:o})}u(v,"refuse");const b=u((t,e)=>new _(400,"invalid_request",t,e),"invalid");function D(t){const e=t.body;if(e===void 0)return{};if(typeof e!="object"||e===null||Array.isArray(e))throw b("the request body must be a JSON object");return e}u(D,"bodyOf");const L=u(t=>e=>ye(t,e),"reposFnFor");function C(t,e){if(t instanceof ie){const n=t.details;if(t.code==="read_first")throw v(409,"read_first",t.message,`Read it first with \`crtr canvas read ${e?.id??"<ref>"}\`, then rerun the edit.`,n??{});if(t.code==="conflict")throw new _(409,"name_taken",t.message,n);if(t.code==="forbidden")throw new _(403,"forbidden",t.message,n);if(t.code==="scope_missing")throw v(403,"scope_missing",t.message,`Launch the run with \`${String(n?.scope)}\` in its scopes list.`,n??{});if(n?.rule==="repo-public-only")throw v(422,"repo_documents_are_public",t.message,'Own it as `user` and add a rule with `if: {"repo": "<handle>"}` so it loads only in that repo.')}throw t}u(C,"mapGraphError");function x(t,e,n){const o=z(t,e,decodeURIComponent(n));if("remote"in o)throw v(404,"not_found",G(o.remote),"Check the name with `crtr canvas search`.");if("deleted"in o)throw new _(404,"not_found",`${o.deleted.name} is deleted`,{id:o.deleted.id,deleted:!0});if(o.type!=="document")throw b(`${n} is a ${o.type}, not a document`);return o}u(x,"refDocument");function G(t){return`no object named ${t} on this runtime; it may be on another runtime, or the name may be misspelled`}u(G,"remoteMessage");function be(t,e){const n=e.owner===null?null:M(t,e.owner);return n?n.type==="node"?n.id:n.name:""}u(be,"ownerHandle");function j(t,e){const n=be(t,e);return n?`${n}/${e.name}`:e.name}u(j,"canonicalName");function ke(t,e){const n=e.owner===null?null:M(t,e.owner),o=n?{id:n.id,type:n.type,handle:n.type==="node"?n.id:n.name}:null;return{id:e.id,type:e.type,kind:e.kind,owner:o,privacy:e.privacy,name:e.name,preview:e.preview??"",created_at:e.created_at,deleted_at:e.deleted_at}}u(ke,"objectDTO");function Ee(t,e){const n=e.owner===null?null:M(t,e.owner);return n?.type==="repo"?n:null}u(Ee,"repoOwner");function S(t,e,n,o){const i=[...new Map(n.flatMap(r=>{const f=Ee(t,r);return f?[[f.id,f]]:[]})).values()],a=e.class==="node"?e.cwd:o;return{repos:i,...a!==void 0?{cwd:a}:{}}}u(S,"repoChange");function T(t){for(const e of t.repos)try{we(e,t.cwd)}catch(n){se({level:"warn",event:"repo_sync.failed",outcome:"failed",error:n,fields:{repo:e.name,step:"export",reason:n instanceof Error?n.message:String(n)}})}}u(T,"syncChanged");const Y=/^[A-Za-z0-9_-]+(\/[A-Za-z0-9_-]+)*$/;function $e(t,e){if(t===void 0)return;if(!Array.isArray(t)){e.push("`rules` must be a list");return}const n=t.map(i=>{if(typeof i!="object"||i===null||Array.isArray(i))return i;const{match_frontmatter:a,...r}=i;return"matchFrontmatter"in r?{...r,match_frontmatter:r.matchFrontmatter}:a===void 0?r:{...r,matchFrontmatter:a}}),o=ve(n);if(o){e.push(o.replaceAll("matchFrontmatter","match_frontmatter"));return}return n}u($e,"rulesFromDTO");function Z(t,e){const n={},o=u(s=>{const c=t[s];if(c!==void 0){if(typeof c!="string"){e.push(`\`${s}\` must be a string`);return}return c}},"str"),i=o("preview");i!==void 0&&(i.trim()===""||/[\r\n]/.test(i)?e.push("`preview` must be one non-empty line"):n.preview=i);const a=o("summary");a!==void 0&&(n.summary=a);const r=o("why_it_exists");if(r!==void 0&&(n.whyItExists=r),t.privacy!==void 0&&(V.includes(t.privacy)?n.privacy=t.privacy:e.push(`\`privacy\` must be one of ${V.join(", ")}`)),t.unlisted!==void 0&&(typeof t.unlisted!="boolean"?e.push("`unlisted` must be a boolean"):n.unlisted=t.unlisted),t.lint_ignore!==void 0){const s=t.lint_ignore;!Array.isArray(s)||s.some(c=>typeof c!="string")?e.push("`lint_ignore` must be a list of strings"):n.lintIgnore=s}if(t.extensions!==void 0){const s=t.extensions;typeof s!="object"||s===null||Array.isArray(s)||Object.values(s).some(c=>typeof c!="object"||c===null||Array.isArray(c))?e.push("`extensions` must map each plugin to an object of fields"):n.extensions=s}const f=$e(t.rules,e);return f!==void 0&&(n.rules=f),n}u(Z,"fieldsFrom");function J(t){if(t.length>0)throw b(t.join("; "),{errors:t})}u(J,"failOn");function Q(t,e,n,o){const i=U(n).filter(a=>"remote"in X(t,e,a));return{...o.length>0?{unresolved_links:o}:{},...i.length>0?{remote_links:i}:{}}}u(Q,"linkReport");function Oe(t){const e=D(t),n=[],o=e.name;(typeof o!="string"||!Y.test(o))&&n.push("`name` must be segments of letters, digits, `_` or `-` joined by `/`"),typeof e.body!="string"&&n.push("`body` must be a string"),typeof e.preview!="string"&&n.push("`preview` is required");const i=e.kind??"knowledge";q.includes(i)||n.push(i==="report"?"reports are written only by `crtr push`":`\`kind\` must be one of ${q.join(", ")}`);const a=Z(e,n);e.cwd!==void 0&&typeof e.cwd!="string"&&n.push("`cwd` must be a string"),J(n);const r=e,{dto:f,change:s}=k(c=>{const h=E(c,t),d=W(c,h,r.owner,r.cwd);if(!K(c,h,d))throw new _(404,"not_found",`no owner ${d.name} you can write to`);try{const{object:p,unresolved:l}=he(c,h,{...a,owner:d,name:r.name,kind:i,preview:a.preview,privacy:a.privacy??"public"},r.body,L(c));return{dto:{id:p.id,name:j(c,p),...Q(c,h,r.body,l)},change:S(c,h,[p],r.cwd)}}catch(p){C(p)}});return T(s),f}u(Oe,"write");function Ae(t,e){let n=t;e.forEach(({old:o,new:i},a)=>{const r=o===""?-1:n.indexOf(o);if(r<0)throw v(422,"replace_no_match",`replace[${a}]: old text not found in the revision you read`,"Copy the old text exactly from your last read and rerun.",{index:a});if(n.indexOf(o,r+1)>=0)throw v(422,"replace_ambiguous",`replace[${a}]: old text occurs more than once`,"Include more surrounding text so the old text matches once, and rerun.",{index:a});n=n.slice(0,r)+i+n.slice(r+o.length)})}u(Ae,"checkReplacements");function je(t){const e=D(t),n=[],o=e.body!==void 0,i=e.replace!==void 0;o&&i&&n.push("give `body` or `replace`, not both"),o&&typeof e.body!="string"&&n.push("`body` must be a string");const a=e.replace;i&&(!Array.isArray(a)||a.length===0||a.some(d=>typeof d!="object"||d===null||typeof d.old!="string"||typeof d.new!="string"))&&n.push("`replace` must be a non-empty list of {old, new} strings"),(typeof e.rationale!="string"||e.rationale.trim()==="")&&n.push("`rationale` is required"),e.verbatim!==void 0&&typeof e.verbatim!="boolean"&&n.push("`verbatim` must be a boolean"),e.base!==void 0&&(typeof e.base!="string"||e.base==="")&&n.push("`base` must be a non-empty heads key"),e.base!==void 0&&!o&&!i&&n.push("`base` applies only to a `body` or `replace` edit");const r=Z(e,n),f=e.unset??[];if(!Array.isArray(f)||f.some(d=>!H.includes(d)))n.push(`\`unset\` takes only ${H.join(", ")}`);else for(const d of f)d==="summary"&&(r.summary=null),d==="why_it_exists"&&(r.whyItExists=null),d==="unlisted"&&(r.unlisted=!1),d==="lint_ignore"&&(r.lintIgnore=null),d==="extensions"&&(r.extensions=null),d==="rules"&&(r.rules=[]);n.length===0&&!o&&!i&&Object.keys(r).length===0&&n.push("nothing to change: give `body`, `replace`, or a field"),J(n);const s=e,{dto:c,synced:h}=k(d=>{const p=E(d,t),l=x(d,p,t.params.ref),g=j(d,l),O=S(d,p,[l]);try{if(!o&&!i)return me(d,p,l,r),{dto:{id:l.id,name:g,revision:F(d,l).revision},synced:O};const w=s.base??le(d,p,l);if(i&&w!==null){const oe=F(d,l),B=I(d,l);oe.heads_key===w&&B.length===1&&Ae(pe(d,l,A(B[0].body_hash)),s.replace)}const R=s.verbatim===!0,m=o?{body:s.body,verbatim:R}:{replace:s.replace,verbatim:R},y=de(d,p,l,m,s.rationale,w,r,L(d));if("stale"in y)throw v(409,"stale_revision",`${g} changed after your read; it is now at revision ${y.stale.revision}`,`Read it again with \`crtr canvas read ${g}\`, then redo your change on the current text.`,{stale:y.stale});const N=A(I(d,l)[0].body_hash);return{dto:{id:l.id,name:g,revision:y.revision,...Q(d,p,N,y.unresolved)},synced:O}}catch(w){C(w,l)}});return T(h),c}u(je,"edit");function Re(t){const e=D(t);if(e.new_name===void 0&&e.owner===void 0)throw b("give a new name, an owner, or both");if(e.new_name!==void 0&&(typeof e.new_name!="string"||!Y.test(e.new_name)))throw b("`new_name` must be segments of letters, digits, `_` or `-` joined by `/`");const{dto:n,change:o}=k(i=>{const a=E(i,t),r=x(i,a,t.params.ref),f=e.owner===void 0?void 0:W(i,a,e.owner,e.cwd);try{const s=fe(i,a,r,{...e.new_name!==void 0?{name:e.new_name}:{},...f?{owner:f}:{}});return{dto:{id:s.id,name:j(i,s)},change:S(i,a,[r,s],e.cwd)}}catch(s){C(s,r)}});return T(o),n}u(Re,"move");function De(t){const{dto:e,change:n}=k(o=>{const i=E(o,t),a=x(o,i,t.params.ref),r=j(o,a),f=S(o,i,[a]);try{ce(o,i,a,L(o))}catch(s){C(s,a)}return{dto:{id:a.id,name:r},change:f}});return T(n),e}u(De,"remove");function Ce(t){const e=t.query.get("diff"),n=e===null?void 0:Number(e);if(n!==void 0&&!(Number.isInteger(n)&&n>0))throw b("`diff` must be a revision number");return k(o=>{const i=E(o,t),a=z(o,i,decodeURIComponent(t.params.ref));if("remote"in a)throw v(404,"not_found",G(a.remote),"Check the name with `crtr canvas search`.");const r="deleted"in a?a.deleted:a;if(r.type!=="document")throw b(`${r.name} is a ${r.type}, not a document`);const f=ue(o,i,r),s={object:ke(o,r),revisions:f.map(c=>({rev_id:c.rev_id,number:c.number,author_id:c.author,rationale:c.rationale,verbatim:c.verbatim,created_at:c.created_at,parents:c.parents}))};if(n!==void 0){const c=f.find(d=>d.number===n);if(!c)throw new _(404,"not_found",`${r.name} has no revision ${n}`);const h=c.parents[0]===void 0?void 0:f.find(d=>d.rev_id===c.parents[0]);s.diff=_e(h?A(h.body_hash):"",A(c.body_hash))}return s})}u(Ce,"revisionHistory");const ee=1e3,ne=1e3,te=3e3,Se=/^When\b.+\bbecause\b.+/,Te=u(t=>{const e=t.trim();return e===""?0:e.split(/\s+/).length},"words");function Ne(t,e){if(t.some(o=>o.on==="boot"&&o.deliver==="content")&&e>ee)return`body is ${e} words, over the ${ee}-word cap for a boot rule at content (the whole body enters every node's context). Keep the core here and move separate subjects into [[linked]] documents.`;const n=[...new Set(t.filter(o=>o.on!=="boot"&&o.deliver==="content").map(o=>o.on))];return n.length>0&&e>ne?`body is ${e} words, over the ${ne}-word cap for a content rule on ${n.join("/")}. Keep the core here and move separate subjects into [[linked]] documents.`:t.some(o=>o.on==="boot"&&o.deliver==="preview")&&e>te?`body is ${e} words, over the ${te}-word cap for a boot rule at preview. Keep the core here and move separate subjects into [[linked]] documents.`:null}u(Ne,"lengthFinding");function Fe(t){const e=D(t);return k(n=>{const o=E(n,t),i=e.owner===void 0?null:W(n,o,e.owner,e.cwd),a=P(n,"SELECT * FROM objects WHERE type = 'document' AND deleted_at IS NULL AND kind != 'report' ORDER BY name").all(),r=[],f=new Map;for(const s of a){if(i&&s.owner!==i.rowid||!K(n,o,s))continue;const c=F(n,s),h=new Set(c.lint_ignore??[]),d=j(n,s),p=u((m,y,N)=>{h.has(m)||r.push({name:d,severity:y,rule:m,message:N})},"add"),l=ge(n,s),g=I(n,s)[0],O=g?A(g.body_hash):"",w=Ne(l,Te(O));w&&p("length","error",w),l.some(m=>m.deliver==="preview")&&!Se.test(s.preview??"")&&p("preview-shape","error",`a rule delivers this document at preview, so the preview must read "When <situation in the reader's work>, read this because <what the reader gains>"`);const R=new Set(P(n,"SELECT written_as FROM edges WHERE type = 'links-to' AND src = ?").all(s.rowid).map(m=>m.written_as));for(const m of U(O))R.has(m)||"remote"in X(n,o,m)||p("links","warning",`[[${m}]] names no object in your view`);if(l.some(m=>m.on==="workspace-open"&&m.deliver==="content")){const m=ae(n,s);m.type==="repo"&&!h.has("workspace-open")&&f.set(m.name,[...f.get(m.name)??[],d])}}for(const[s,c]of f)if(!(c.length<2))for(const h of c)r.push({name:h,severity:"warning",rule:"workspace-open",message:`repo ${s} has ${c.length} documents delivered at content on workspace-open (${c.join(", ")}); keep one and link the rest from it`});return{findings:r}})}u(Fe,"lint");const $=u((t,e=200)=>n=>({status:e,body:t(n)}),"ok"),Qe=[{method:"POST",pattern:"/v1/docs",handler:$(Oe,201)},{method:"PATCH",pattern:"/v1/docs/:ref",handler:$(je)},{method:"POST",pattern:"/v1/docs/:ref/move",handler:$(Re)},{method:"DELETE",pattern:"/v1/docs/:ref",handler:$(De)},{method:"GET",pattern:"/v1/docs/:ref/history",handler:$(Ce)},{method:"POST",pattern:"/v1/docs/lint",handler:$(Fe)}];export{Qe as docRoutes};
@@ -6,6 +6,9 @@ import { type RequestContext, type RouteTable } from '../router.js';
6
6
  * anything is written when another live profile object holds the handle; a manifest failure then
7
7
  * rolls the object rename back. */
8
8
  export declare function renameProfileAndHandle(profileId: string, name: string): ProfileEntry;
9
+ /** Add a project path; each repo enclosing it starts at `preview` unless the profile already limits that
10
+ * handle (bare or `repo:`-typed). Repos are identified before the manifest changes, so a failed lookup adds nothing. */
11
+ export declare function addProjectWithDefaultLimit(profileId: string, limits: Readonly<Record<string, string>>, path: string): void;
9
12
  /** One visible profile by exact id or unique name; a miss or an invisible profile is 404. */
10
13
  export declare function requireUsableProfile(ctx: RequestContext, name: string): ReturnType<typeof listProfiles>[number];
11
14
  export declare const profileRoutes: RouteTable;
@@ -1 +1 @@
1
- var T=Object.defineProperty;var i=(t,e)=>T(t,"name",{value:e,configurable:!0});import{assertLaunchKind as v}from"../../../core/runtime/launch-target.js";import{listNodesByProfile as h,shiftProfileNodeDeadlines as k}from"../../../core/canvas/canvas.js";import{liveExecutionForNode as D}from"../../../core/canvas/executions.js";import{shiftAutoBackgroundBashDeadlines as q}from"../../../core/bash-jobs.js";import{contextDir as N}from"../../../core/canvas/node-agent-paths.js";import{boundFleet as O}from"../../../core/runtime/fleet.js";import{recordedPidLiveness as L}from"../../../core/canvas/pid.js";import{withCanvasWrite as M}from"../../../core/canvas/db.js";import{renameProfileObject as S}from"../../../core/graph/objects.js";import{usage as l}from"../../../core/errors.js";import{ApiError as p}from"../../../api/errors.js";import{scopeAllowed as P}from"../../../core/scopes.js";import{validateEnvVarName as V}from"../../../core/env-name.js";import{listProfileEnvNames as $,removeProfileEnvVar as F,setProfileEnvVar as R}from"../../../core/profiles/env-store.js";import{listAppEnvNames as G,removeAppEnvVar as U,setAppEnvVar as B}from"../../../core/profiles/app-env-store.js";import{addProfileProject as C,assertDeliveryLimits as K,setProfileDeliveryLimit as H,assertProfileProjects as y,createProfile as w,ensureRootProfile as W,listProfiles as u,loadProfileManifest as _,pauseProfile as z,removeProfileProject as J,renameProfile as Q,resumeProfile as X,setProfileDefaultKind as Y,updateProfileMetadata as Z}from"../../../core/profiles/manifest.js";import{requestProfileDeletion as x}from"../../profile-delete.js";import{brokerSignalBus as ee}from"../../broker-signals/bus.js";import{toProfileDTO as d}from"../map.js";import{appOf as b,resolvedScopes as te}from"../router.js";const E="root";function m(t){return b(t.principal)??"app:terminal"}i(m,"callerApp");function g(t,e,n=null){if(u().some(r=>r.manifest.name===e&&r.profileId!==n&&(r.manifest.grantee??"app:terminal")===t))throw new p(400,"invalid_request",`${t} already has a profile named ${e}`)}i(g,"assertNameFree");function f(t,e){const n=A(t,e),r=n.manifest.grantee??"app:terminal";if(t.principal.class!=="person"&&r!==b(t.principal))throw new p(403,"permission_error",`profile ${n.profileId} belongs to ${r}; only that app changes it`);return n}i(f,"requireOwnProfile");async function re(t){const e=t.body??{};if(typeof e.name!="string"||e.name.trim()==="")throw l("a profile name is required",{field:"name"});e.projects!==void 0&&y(e.projects);const n=m(t),r=e.name.trim();g(n,r),e.default_kind!==void 0&&await v(e.default_kind,process.cwd(),null);const o=w(r,e.projects??[],{defaultKind:e.default_kind,metadata:e.metadata,grantee:n});return{status:201,body:d(o)}}i(re,"handleCreate");function ne(t,e){const n=e.trim();if(n==="")throw l("profile name must not be empty");return M(r=>(S(r,t,n),Q(t,n)))}i(ne,"renameProfileAndHandle");async function ae(t){const{profileId:e,manifest:n}=f(t,t.params.name),r=t.body??{};return r.name!==void 0&&g(n.grantee??"app:terminal",r.name.trim(),e),r.default_kind!==void 0&&await v(r.default_kind,process.cwd(),e),r.add_project!==void 0&&y([r.add_project]),r.set_delivery_limit!==void 0&&K({[r.set_delivery_limit.owner]:r.set_delivery_limit.level}),r.name!==void 0&&ne(e,r.name),r.default_kind!==void 0&&Y(e,r.default_kind),r.add_project!==void 0&&C(e,r.add_project.path),r.set_delivery_limit!==void 0&&H(e,r.set_delivery_limit.owner,r.set_delivery_limit.level),r.remove_project!==void 0&&J(e,r.remove_project),{status:200,body:d(_(e))}}i(ae,"handleUpdate");function ie(t){const{profileId:e}=f(t,t.params.name);return{status:200,body:{profile_id:e,names:$(e)}}}i(ie,"handleEnvList");function c(t){const e=t.params.var,n=V(e);if(!n.isValid)throw l(`invalid env var name: ${n.errors.join("; ")}`,{field:"name",received:e});return e}i(c,"requireEnvVarName");function oe(t){const{profileId:e}=f(t,t.params.name),n=c(t),{value:r}=t.body??{};if(typeof r!="string"||r==="")throw l("a non-empty env value is required",{field:"value"});return{status:200,body:{profile_id:e,changed:R(e,n,r)}}}i(oe,"handleEnvSet");function se(t){const{profileId:e}=f(t,t.params.name),n=c(t);return{status:200,body:{profile_id:e,changed:F(e,n)}}}i(se,"handleEnvRemove");async function de(t){const e=t.params.name,n=t.body??{};n.projects!==void 0&&y(n.projects);const r=m(t);if(e===E&&r==="app:terminal")return{status:200,body:d(W())};if(e===E)throw new p(403,"permission_error","root belongs to the terminal app");const o=u().find(s=>s.manifest.name===e&&(s.manifest.grantee??"app:terminal")===r);if(o!==void 0)return{status:200,body:d(o)};n.default_kind!==void 0&&await v(n.default_kind,process.cwd(),null);const a=w(e,n.projects??[],{defaultKind:n.default_kind,metadata:n.metadata,grantee:r});return{status:200,body:d(a)}}i(de,"handleEnsure");function le(t){const e=f(t,t.params.name),n=t.body??{};if(typeof n!="object"||n===null||Array.isArray(n))throw l("profile metadata body must be an object",{field:"body"});const r=n,o=r.unset===void 0?[]:r.unset;if(!Array.isArray(o)||o.some(I=>typeof I!="string"))throw l("profile metadata `unset` must be an array of strings",{field:"unset"});const a=r.set===void 0?{}:r.set,s=Z(e.profileId,a,o);return{status:200,body:d(s)}}i(le,"handleSetMetadata");function fe(t){const e=_(t.params.name),n=z(e.profileId),r=h(e.profileId).filter(a=>a.status==="active"||a.status==="idle");if(r.length===0)return{status:200,body:{profile_id:e.profileId,paused_at:n.manifest.paused_at}};const o=O();for(const a of r){const s=o.get(a.node_id);if(s!==void 0){o.interruptForProfilePause(a.node_id,s.pid);continue}a.pi_pid!=null&&L(a.pi_pid,a.pi_pid_identity)==="alive"&&o.interruptForProfilePause(a.node_id,a.pi_pid)}return{status:200,body:{profile_id:e.profileId,paused_at:n.manifest.paused_at}}}i(fe,"handlePause");function pe(t){const e=_(t.params.name),n=X(e.profileId);for(const r of h(e.profileId))(r.status==="active"||r.status==="idle"||D(r.node_id)!==null)&&ee.markMail(r.node_id);if(n.pausedAt!==null){const r=Date.parse(n.pausedAt);if(!Number.isNaN(r)){const o=Math.max(0,Date.now()-r);k(e.profileId,o);for(const a of h(e.profileId))a.status!=="dead"&&q(N(a.node_id),o)}}return{status:200,body:{profile_id:e.profileId,paused_at:n.manifest.paused_at}}}i(pe,"handleResume");function j(t){const e=t.principal;if(e.class==="person")return u();const n=te(e);return u().filter(({profileId:r,manifest:o})=>{const a=o.grantee??"app:terminal";if(a===b(e))return!0;const s=`crtr:memory:rw:${a}:profile:${r}`;return P(n,s)&&(e.class!=="broker"||P(e.scopes,s))})}i(j,"visibleProfiles");function A(t,e){const n=j(t),r=n.find(({profileId:a})=>a===e),o=r?[r]:n.filter(({manifest:a})=>a.name===e);if(o.length===0)throw new p(404,"not_found","profile not found");if(o.length>1)throw new p(400,"ambiguous","profile name matches multiple visible profiles; use an exact id");return o[0]}i(A,"requireUsableProfile");function me(t){return{status:200,body:j(t).map(d)}}i(me,"handleList");function ue(t){return{status:200,body:d(A(t,t.params.name))}}i(ue,"handleGet");async function ce(t){return x(f(t,t.params.name).profileId,t.body)}i(ce,"handleDelete");const Ve=[{method:"GET",pattern:"/v1/apps/self/env",handler:i(t=>({status:200,body:{names:G(m(t))}}),"handler")},{method:"PUT",pattern:"/v1/apps/self/env/:var",handler:i(t=>{const e=c(t),{value:n}=t.body??{};if(typeof n!="string"||!n)throw l("a non-empty env value is required",{field:"value"});return{status:200,body:{changed:B(m(t),e,n)}}},"handler")},{method:"DELETE",pattern:"/v1/apps/self/env/:var",handler:i(t=>({status:200,body:{changed:U(m(t),c(t))}}),"handler")},{method:"PUT",pattern:"/v1/profiles/:name",handler:de},{method:"POST",pattern:"/v1/profiles/:name/pause",ownerOnly:!0,handler:fe},{method:"POST",pattern:"/v1/profiles/:name/resume",ownerOnly:!0,handler:pe},{method:"PATCH",pattern:"/v1/profiles/:name/metadata",handler:le},{method:"POST",pattern:"/v1/profiles",handler:re},{method:"PATCH",pattern:"/v1/profiles/:name",handler:ae},{method:"GET",pattern:"/v1/profiles/:name/env",handler:ie},{method:"PUT",pattern:"/v1/profiles/:name/env/:var",handler:oe},{method:"DELETE",pattern:"/v1/profiles/:name/env/:var",handler:se},{method:"DELETE",pattern:"/v1/profiles/:name",handler:ce},{method:"GET",pattern:"/v1/profiles",handler:me},{method:"GET",pattern:"/v1/profiles/:name",handler:ue}];export{Ve as profileRoutes,ne as renameProfileAndHandle,A as requireUsableProfile};
1
+ var D=Object.defineProperty;var a=(t,e)=>D(t,"name",{value:e,configurable:!0});import{assertLaunchKind as v}from"../../../core/runtime/launch-target.js";import{listNodesByProfile as h,shiftProfileNodeDeadlines as q}from"../../../core/canvas/canvas.js";import{liveExecutionForNode as N}from"../../../core/canvas/executions.js";import{shiftAutoBackgroundBashDeadlines as O}from"../../../core/bash-jobs.js";import{contextDir as L}from"../../../core/canvas/node-agent-paths.js";import{boundFleet as $}from"../../../core/runtime/fleet.js";import{recordedPidLiveness as F}from"../../../core/canvas/pid.js";import{withCanvasWrite as P}from"../../../core/canvas/db.js";import{reposForCwd as M}from"../../../core/graph/repo-sync/repos.js";import{renameProfileObject as S}from"../../../core/graph/objects.js";import{usage as l}from"../../../core/errors.js";import{ApiError as p}from"../../../api/errors.js";import{scopeAllowed as w}from"../../../core/scopes.js";import{validateEnvVarName as V}from"../../../core/env-name.js";import{listProfileEnvNames as R,removeProfileEnvVar as C,setProfileEnvVar as G}from"../../../core/profiles/env-store.js";import{listAppEnvNames as U,removeAppEnvVar as B,setAppEnvVar as K}from"../../../core/profiles/app-env-store.js";import{addProfileProject as H,assertDeliveryLimits as W,setProfileDeliveryLimit as E,assertProfileProjects as y,createProfile as g,ensureRootProfile as z,listProfiles as u,loadProfileManifest as _,pauseProfile as J,removeProfileProject as Q,renameProfile as X,resumeProfile as Y,setProfileDefaultKind as Z,updateProfileMetadata as x}from"../../../core/profiles/manifest.js";import{requestProfileDeletion as ee}from"../../profile-delete.js";import{brokerSignalBus as te}from"../../broker-signals/bus.js";import{toProfileDTO as d}from"../map.js";import{appOf as b,resolvedScopes as re}from"../router.js";const j="root";function m(t){return b(t.principal)??"app:terminal"}a(m,"callerApp");function A(t,e,n=null){if(u().some(r=>r.manifest.name===e&&r.profileId!==n&&(r.manifest.grantee??"app:terminal")===t))throw new p(400,"invalid_request",`${t} already has a profile named ${e}`)}a(A,"assertNameFree");function f(t,e){const n=I(t,e),r=n.manifest.grantee??"app:terminal";if(t.principal.class!=="person"&&r!==b(t.principal))throw new p(403,"permission_error",`profile ${n.profileId} belongs to ${r}; only that app changes it`);return n}a(f,"requireOwnProfile");async function ne(t){const e=t.body??{};if(typeof e.name!="string"||e.name.trim()==="")throw l("a profile name is required",{field:"name"});e.projects!==void 0&&y(e.projects);const n=m(t),r=e.name.trim();A(n,r),e.default_kind!==void 0&&await v(e.default_kind,process.cwd(),null);const o=g(r,e.projects??[],{defaultKind:e.default_kind,metadata:e.metadata,grantee:n});return{status:201,body:d(o)}}a(ne,"handleCreate");function oe(t,e){const n=e.trim();if(n==="")throw l("profile name must not be empty");return P(r=>(S(r,t,n),X(t,n)))}a(oe,"renameProfileAndHandle");function ie(t,e,n){const r=P(o=>M(o,n).map(i=>i.name));H(t,n);for(const o of r)e[o]===void 0&&e[`repo:${o}`]===void 0&&E(t,o,"preview")}a(ie,"addProjectWithDefaultLimit");async function ae(t){const{profileId:e,manifest:n}=f(t,t.params.name),r=t.body??{};return r.name!==void 0&&A(n.grantee??"app:terminal",r.name.trim(),e),r.default_kind!==void 0&&await v(r.default_kind,process.cwd(),e),r.add_project!==void 0&&y([r.add_project]),r.set_delivery_limit!==void 0&&W({[r.set_delivery_limit.owner]:r.set_delivery_limit.level}),r.name!==void 0&&oe(e,r.name),r.default_kind!==void 0&&Z(e,r.default_kind),r.add_project!==void 0&&ie(e,n.delivery_limits??{},r.add_project.path),r.set_delivery_limit!==void 0&&E(e,r.set_delivery_limit.owner,r.set_delivery_limit.level),r.remove_project!==void 0&&Q(e,r.remove_project),{status:200,body:d(_(e))}}a(ae,"handleUpdate");function se(t){const{profileId:e}=f(t,t.params.name);return{status:200,body:{profile_id:e,names:R(e)}}}a(se,"handleEnvList");function c(t){const e=t.params.var,n=V(e);if(!n.isValid)throw l(`invalid env var name: ${n.errors.join("; ")}`,{field:"name",received:e});return e}a(c,"requireEnvVarName");function de(t){const{profileId:e}=f(t,t.params.name),n=c(t),{value:r}=t.body??{};if(typeof r!="string"||r==="")throw l("a non-empty env value is required",{field:"value"});return{status:200,body:{profile_id:e,changed:G(e,n,r)}}}a(de,"handleEnvSet");function le(t){const{profileId:e}=f(t,t.params.name),n=c(t);return{status:200,body:{profile_id:e,changed:C(e,n)}}}a(le,"handleEnvRemove");async function fe(t){const e=t.params.name,n=t.body??{};n.projects!==void 0&&y(n.projects);const r=m(t);if(e===j&&r==="app:terminal")return{status:200,body:d(z())};if(e===j)throw new p(403,"permission_error","root belongs to the terminal app");const o=u().find(s=>s.manifest.name===e&&(s.manifest.grantee??"app:terminal")===r);if(o!==void 0)return{status:200,body:d(o)};n.default_kind!==void 0&&await v(n.default_kind,process.cwd(),null);const i=g(e,n.projects??[],{defaultKind:n.default_kind,metadata:n.metadata,grantee:r});return{status:200,body:d(i)}}a(fe,"handleEnsure");function pe(t){const e=f(t,t.params.name),n=t.body??{};if(typeof n!="object"||n===null||Array.isArray(n))throw l("profile metadata body must be an object",{field:"body"});const r=n,o=r.unset===void 0?[]:r.unset;if(!Array.isArray(o)||o.some(k=>typeof k!="string"))throw l("profile metadata `unset` must be an array of strings",{field:"unset"});const i=r.set===void 0?{}:r.set,s=x(e.profileId,i,o);return{status:200,body:d(s)}}a(pe,"handleSetMetadata");function me(t){const e=_(t.params.name),n=J(e.profileId),r=h(e.profileId).filter(i=>i.status==="active"||i.status==="idle");if(r.length===0)return{status:200,body:{profile_id:e.profileId,paused_at:n.manifest.paused_at}};const o=$();for(const i of r){const s=o.get(i.node_id);if(s!==void 0){o.interruptForProfilePause(i.node_id,s.pid);continue}i.pi_pid!=null&&F(i.pi_pid,i.pi_pid_identity)==="alive"&&o.interruptForProfilePause(i.node_id,i.pi_pid)}return{status:200,body:{profile_id:e.profileId,paused_at:n.manifest.paused_at}}}a(me,"handlePause");function ue(t){const e=_(t.params.name),n=Y(e.profileId);for(const r of h(e.profileId))(r.status==="active"||r.status==="idle"||N(r.node_id)!==null)&&te.markMail(r.node_id);if(n.pausedAt!==null){const r=Date.parse(n.pausedAt);if(!Number.isNaN(r)){const o=Math.max(0,Date.now()-r);q(e.profileId,o);for(const i of h(e.profileId))i.status!=="dead"&&O(L(i.node_id),o)}}return{status:200,body:{profile_id:e.profileId,paused_at:n.manifest.paused_at}}}a(ue,"handleResume");function T(t){const e=t.principal;if(e.class==="person")return u();const n=re(e);return u().filter(({profileId:r,manifest:o})=>{const i=o.grantee??"app:terminal";if(i===b(e))return!0;const s=`crtr:memory:rw:${i}:profile:${r}`;return w(n,s)&&(e.class!=="broker"||w(e.scopes,s))})}a(T,"visibleProfiles");function I(t,e){const n=T(t),r=n.find(({profileId:i})=>i===e),o=r?[r]:n.filter(({manifest:i})=>i.name===e);if(o.length===0)throw new p(404,"not_found","profile not found");if(o.length>1)throw new p(400,"ambiguous","profile name matches multiple visible profiles; use an exact id");return o[0]}a(I,"requireUsableProfile");function ce(t){return{status:200,body:T(t).map(d)}}a(ce,"handleList");function ve(t){return{status:200,body:d(I(t,t.params.name))}}a(ve,"handleGet");async function he(t){return ee(f(t,t.params.name).profileId,t.body)}a(he,"handleDelete");const Re=[{method:"GET",pattern:"/v1/apps/self/env",handler:a(t=>({status:200,body:{names:U(m(t))}}),"handler")},{method:"PUT",pattern:"/v1/apps/self/env/:var",handler:a(t=>{const e=c(t),{value:n}=t.body??{};if(typeof n!="string"||!n)throw l("a non-empty env value is required",{field:"value"});return{status:200,body:{changed:K(m(t),e,n)}}},"handler")},{method:"DELETE",pattern:"/v1/apps/self/env/:var",handler:a(t=>({status:200,body:{changed:B(m(t),c(t))}}),"handler")},{method:"PUT",pattern:"/v1/profiles/:name",handler:fe},{method:"POST",pattern:"/v1/profiles/:name/pause",ownerOnly:!0,handler:me},{method:"POST",pattern:"/v1/profiles/:name/resume",ownerOnly:!0,handler:ue},{method:"PATCH",pattern:"/v1/profiles/:name/metadata",handler:pe},{method:"POST",pattern:"/v1/profiles",handler:ne},{method:"PATCH",pattern:"/v1/profiles/:name",handler:ae},{method:"GET",pattern:"/v1/profiles/:name/env",handler:se},{method:"PUT",pattern:"/v1/profiles/:name/env/:var",handler:de},{method:"DELETE",pattern:"/v1/profiles/:name/env/:var",handler:le},{method:"DELETE",pattern:"/v1/profiles/:name",handler:he},{method:"GET",pattern:"/v1/profiles",handler:ce},{method:"GET",pattern:"/v1/profiles/:name",handler:ve}];export{ie as addProjectWithDefaultLimit,Re as profileRoutes,oe as renameProfileAndHandle,I as requireUsableProfile};