@north-light/cli 0.3.565 → 0.3.567

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 (62) hide show
  1. package/dist/api/contract.d.ts +1 -1
  2. package/dist/api/contract.js +1 -1
  3. package/dist/clients/attach/viewer.js +475 -475
  4. package/dist/core/config.js +1 -1
  5. package/dist/core/directory/issuer.d.ts +5 -0
  6. package/dist/core/directory/issuer.js +1 -0
  7. package/dist/core/environments.d.ts +11 -0
  8. package/dist/core/environments.js +1 -1
  9. package/dist/core/graph/objects.d.ts +3 -0
  10. package/dist/core/graph/objects.js +8 -8
  11. package/dist/core/graph/reader.js +3 -3
  12. package/dist/core/ledger/charges.d.ts +2 -4
  13. package/dist/core/ledger/charges.js +1 -1
  14. package/dist/core/ledger/index.d.ts +1 -1
  15. package/dist/core/ledger/index.js +1 -1
  16. package/dist/core/ledger/infra-allowance.d.ts +0 -2
  17. package/dist/core/ledger/infra-allowance.js +1 -1
  18. package/dist/core/ledger/rates.d.ts +24 -0
  19. package/dist/core/ledger/rates.js +1 -0
  20. package/dist/core/ledger/read.d.ts +5 -2
  21. package/dist/core/ledger/read.js +2 -2
  22. package/dist/core/peer/client.js +1 -1
  23. package/dist/core/peer/directory.d.ts +0 -2
  24. package/dist/core/peer/directory.js +1 -1
  25. package/dist/core/peer/home.js +2 -2
  26. package/dist/core/profiles/manifest.d.ts +9 -0
  27. package/dist/core/profiles/manifest.js +1 -1
  28. package/dist/core/providers/audit.d.ts +2 -0
  29. package/dist/core/providers/audit.js +2 -2
  30. package/dist/core/providers/bundles.js +2 -2
  31. package/dist/core/providers/call.js +1 -1
  32. package/dist/core/providers/mint.d.ts +1 -1
  33. package/dist/core/providers/mint.js +1 -1
  34. package/dist/core/runs/operations.d.ts +4 -0
  35. package/dist/core/runs/operations.js +7 -7
  36. package/dist/core/secrets/blaxel-register.d.ts +5 -1
  37. package/dist/core/secrets/blaxel-register.js +1 -1
  38. package/dist/core/storage/client.js +2 -2
  39. package/dist/core/user-settings.d.ts +0 -4
  40. package/dist/core/user-settings.js +1 -1
  41. package/dist/daemon/api/app-listener.d.ts +8 -0
  42. package/dist/daemon/api/app-listener.js +13 -13
  43. package/dist/daemon/api/handlers/charges.js +1 -1
  44. package/dist/daemon/api/handlers/plugin-app-token.js +1 -1
  45. package/dist/daemon/api/handlers/providers.js +1 -1
  46. package/dist/daemon/api/handlers/runs.js +1 -1
  47. package/dist/daemon/api/handlers/transfer.js +1 -1
  48. package/dist/daemon/api/reader.js +1 -1
  49. package/dist/daemon/api/server.js +1 -1
  50. package/dist/daemon/cron/authority.js +1 -1
  51. package/dist/daemon/crtrd.js +10 -10
  52. package/dist/daemon/peer/relay-service.js +1 -1
  53. package/dist/daemon/readiness.d.ts +2 -1
  54. package/dist/daemon/readiness.js +1 -1
  55. package/dist/daemon/reconcilers/ledger-r2.js +5 -5
  56. package/dist/daemon/reconcilers/ledger-sandbox.d.ts +0 -1
  57. package/dist/daemon/reconcilers/ledger-sandbox.js +4 -4
  58. package/dist/daemon/reconcilers/ledger-storage.js +3 -3
  59. package/dist/types.d.ts +0 -16
  60. package/package.json +2 -2
  61. package/packages/crouter-identity/package.json +1 -1
  62. package/runtime.lock.json +13 -13
@@ -1,2 +1,2 @@
1
- var d=Object.defineProperty;var c=(e,n)=>d(e,"name",{value:n,configurable:!0});import{getById as p}from"../graph/objects.js";import{isUnreachable as _,peerCall as y}from"./client.js";import{directoryIssuer as E,listMachines as x,normalizeMachineId as w,ownMachineId as b}from"./directory.js";import{stubOf as O}from"./stubs.js";const l={call:c((e,n)=>y(e,n),"call"),machines:x,self:b,now:Date.now,joined:c(()=>E()!==null,"joined")};async function T(e,n){return(await e.prepare("SELECT machine_id FROM peer_locations WHERE id = ?").get(n))?.machine_id??null}c(T,"cachedLocation");async function I(e,n,a,i=Date.now()){await e.prepare(`INSERT INTO peer_locations (id, machine_id, learned_at) VALUES (?, ?, ?)
2
- ON CONFLICT(id) DO UPDATE SET machine_id = excluded.machine_id, learned_at = excluded.learned_at`).run(n,a,i)}c(I,"rememberLocation");async function L(e,n){await e.prepare("DELETE FROM peer_locations WHERE id = ?").run(n)}c(L,"dropLocation");async function g(e,n){if(!e.joined())return[];let a;try{a=await e.machines()}catch(r){return{where:"unknown",unreachable:[`directory (${r instanceof Error?r.message:String(r)})`]}}const i=e.self();return a.filter(r=>r.machine_id!==i&&r.machine_id!==n&&r.kind!=="device")}c(g,"peersToAsk");async function f(e,n,a=l,i){const r=await g(a,i);if("where"in r)return r;const o=await Promise.all(r.map(async t=>{const h=await a.call(t.machine_id,{method:"POST",path:"/v1/peer/locate",body:{id:n}});if(_(h))return{m:t,state:"unreachable"};const s=h.body;return h.status===200&&s?.answer==="here"?{m:t,state:"here",machine:t.machine_id}:h.status===200&&s?.answer==="moved"&&typeof s.machine=="string"?{m:t,state:"here",machine:w(s.machine)}:h.status===200&&s?.answer==="no"?{m:t,state:"no"}:{m:t,state:"unreachable"}})),u=o.find(t=>t.state==="here");if(u&&"machine"in u)return await I(e,n,u.machine,a.now()),{where:"machine",machine:u.machine,source:"locate"};const m=o.filter(t=>t.state==="unreachable").map(t=>t.m.name||t.m.machine_id);return m.length>0?{where:"unknown",unreachable:m}:{where:"nowhere"}}c(f,"locate");async function C(e,n,a={}){const i=await p(e,n);if(i!==null){const o=await O(e,i);return o===null?{where:"here"}:{where:"machine",machine:o.home_machine,source:"stub"}}if(!(a.deps??l).joined())return{where:"nowhere"};if(a.hint)return{where:"machine",machine:w(a.hint),source:"hint"};const r=await T(e,n);return r!==null?{where:"machine",machine:r,source:"cache"}:f(e,n,a.deps)}c(C,"homeOf");async function N(e,n,a,i=l){return await L(e,n),f(e,n,i,a)}c(N,"relocate");export{T as cachedLocation,l as defaultHomeDeps,L as dropLocation,C as homeOf,f as locate,g as peersToAsk,N as relocate,I as rememberLocation};
1
+ var d=Object.defineProperty;var c=(e,n)=>d(e,"name",{value:n,configurable:!0});import{getById as p}from"../graph/objects.js";import{isUnreachable as _,peerCall as y}from"./client.js";import{listMachines as E,normalizeMachineId as w,ownMachineId as x}from"./directory.js";import{directoryIssuer as b}from"../directory/issuer.js";import{stubOf as O}from"./stubs.js";const l={call:c((e,n)=>y(e,n),"call"),machines:E,self:x,now:Date.now,joined:c(()=>b()!==null,"joined")};async function T(e,n){return(await e.prepare("SELECT machine_id FROM peer_locations WHERE id = ?").get(n))?.machine_id??null}c(T,"cachedLocation");async function I(e,n,r,i=Date.now()){await e.prepare(`INSERT INTO peer_locations (id, machine_id, learned_at) VALUES (?, ?, ?)
2
+ ON CONFLICT(id) DO UPDATE SET machine_id = excluded.machine_id, learned_at = excluded.learned_at`).run(n,r,i)}c(I,"rememberLocation");async function L(e,n){await e.prepare("DELETE FROM peer_locations WHERE id = ?").run(n)}c(L,"dropLocation");async function g(e,n){if(!e.joined())return[];let r;try{r=await e.machines()}catch(a){return{where:"unknown",unreachable:[`directory (${a instanceof Error?a.message:String(a)})`]}}const i=e.self();return r.filter(a=>a.machine_id!==i&&a.machine_id!==n&&a.kind!=="device")}c(g,"peersToAsk");async function f(e,n,r=l,i){const a=await g(r,i);if("where"in a)return a;const o=await Promise.all(a.map(async t=>{const h=await r.call(t.machine_id,{method:"POST",path:"/v1/peer/locate",body:{id:n}});if(_(h))return{m:t,state:"unreachable"};const s=h.body;return h.status===200&&s?.answer==="here"?{m:t,state:"here",machine:t.machine_id}:h.status===200&&s?.answer==="moved"&&typeof s.machine=="string"?{m:t,state:"here",machine:w(s.machine)}:h.status===200&&s?.answer==="no"?{m:t,state:"no"}:{m:t,state:"unreachable"}})),u=o.find(t=>t.state==="here");if(u&&"machine"in u)return await I(e,n,u.machine,r.now()),{where:"machine",machine:u.machine,source:"locate"};const m=o.filter(t=>t.state==="unreachable").map(t=>t.m.name||t.m.machine_id);return m.length>0?{where:"unknown",unreachable:m}:{where:"nowhere"}}c(f,"locate");async function N(e,n,r={}){const i=await p(e,n);if(i!==null){const o=await O(e,i);return o===null?{where:"here"}:{where:"machine",machine:o.home_machine,source:"stub"}}if(!(r.deps??l).joined())return{where:"nowhere"};if(r.hint)return{where:"machine",machine:w(r.hint),source:"hint"};const a=await T(e,n);return a!==null?{where:"machine",machine:a,source:"cache"}:f(e,n,r.deps)}c(N,"homeOf");async function j(e,n,r,i=l){return await L(e,n),f(e,n,i,r)}c(j,"relocate");export{T as cachedLocation,l as defaultHomeDeps,L as dropLocation,N as homeOf,f as locate,g as peersToAsk,j as relocate,I as rememberLocation};
@@ -55,6 +55,15 @@ export declare function resolveProfileOperand(operand: string): ProfileEntry;
55
55
  /** `resolveProfileOperand` over any candidate list — the CLI passes the
56
56
  * profiles the daemon lists for its caller. */
57
57
  export declare function matchProfileOperand<T>(all: readonly T[], operand: string, key: CandidateOf<T>): T;
58
+ /** On a cloud role (D105) a new profile gets its own Blaxel environment, created in the same canvas write as the profile row. This file
59
+ * cannot open the database (V-1), so `environments.ts` registers the writer when the daemon loads it: it runs `insert` inside one write
60
+ * after making the environment, and `insert` receives the environment's object id. */
61
+ export type CloudProfileWriter = (spec: {
62
+ profileId: string;
63
+ grantee: string;
64
+ person: string;
65
+ }, insert: (environmentId: string) => Promise<void>) => Promise<void>;
66
+ export declare function setCloudProfileWriter(writer: CloudProfileWriter): void;
58
67
  export declare function ensureRootProfile(): Promise<ProfileEntry>;
59
68
  /** The env name a metadata entry surfaces as (spawn-env source F). Also the
60
69
  * uniqueness domain for keys: the fold is lossy (`a-b`, `a_b`, `A_B` →
@@ -1 +1 @@
1
- var F=Object.defineProperty;var o=(e,t)=>F(e,"name",{value:t,configurable:!0});import{randomUUID as K}from"node:crypto";import{existsSync as q,realpathSync as b,rmSync as A,statSync as H}from"node:fs";import{resolve as E}from"node:path";import{SCHEMA_VERSION as S}from"../../types.js";import{profileSpace as v}from"../layout.js";import{PROFILE_PROJECT_MEMORY_VALUES as O}from"../../api/dto/profiles.js";import{ensureDir as Z,nowIso as m}from"../fs-utils.js";import{cachedPausedProfileIds as V,cachedProfilePaused as Y,getCachedProfile as d,listCachedProfiles as G,writeSettings as h}from"../scoped-state/person-store.js";import{usage as f,notFound as u,ambiguous as R,general as W}from"../errors.js";import{fuzzyMatchProfile as B}from"./fuzzy-match.js";const x=/^[a-z0-9]+(?:-[a-z0-9]+)*-[0-9a-f]{8}$/,p="root-00000000",Q="root";function w(e){return e.grantee??"app:terminal"}o(w,"profileApp");function g(e,t){Z(v(w(t),e).dir)}o(g,"scaffoldProfile");function P(e){if(!x.test(e))throw f(`invalid profile id: ${e}`,{received:e,next:"Profile ids look like <slug>-<8-hex-chars> (as printed by `crtr profile new`). Pass an exact id or a profile name, not a raw path."})}o(P,"assertSafeProfileId");function we(e){P(e);const t=d(e);if(t===null)throw u(`profile not found: ${e}`,{received:e});return v(w(t.manifest),e).dir}o(we,"profileDir");function M(e){if(!Array.isArray(e))throw f("projects must be an array of {path} objects",{received:typeof e,field:"projects",next:'Pass entries like {"path": "/abs/dir"}.'});for(const t of e){if(typeof t!="object"||t===null||Array.isArray(t))throw f(`invalid project entry: ${JSON.stringify(t)}`,{field:"projects",next:'Every project is an object like {"path": "/abs/dir"}.'});const{path:r}=t;if(typeof r!="string"||r.trim()==="")throw f(`project entry has no path: ${JSON.stringify(t)}`,{field:"projects",next:"Give every project entry a non-empty directory path."})}}o(M,"assertProfileProjects");function X(e){const t=O.join(", ");if(typeof e!="object"||e===null||Array.isArray(e))throw f("delivery_limits must be an object mapping owner handles to a level",{received:typeof e,field:"delivery_limits",next:`Pass e.g. {"crouter": "preview"} \u2014 each level is one of: ${t}.`});for(const[r,n]of Object.entries(e)){if(r.trim()==="")throw f("delivery_limits has an empty owner handle",{field:"delivery_limits",next:"Key each limit by an owner handle such as a repo handle or `user`, or exactly by `<type>:<handle>` such as `repo:crouter`."});if(typeof n!="string"||!O.includes(n))throw f(`invalid delivery limit for ${r}: ${JSON.stringify(n)}`,{received:String(n),field:"delivery_limits",next:`Set ${r} to one of: ${t}.`})}}o(X,"assertDeliveryLimits");function I(){return{entries:G(),unreadableIds:[]}}o(I,"enumerateProfiles");function y(){return I().entries}o(y,"listProfiles");function ee(e){const t=y(),r=C(t,e,$);return r?{kind:"resolved",entry:r}:{kind:"absent"}}o(ee,"lookupProfile");function k(e){P(e);const t=d(e);if(t!==null)return t;throw u(`profile not found: ${e}`,{received:e,next:"Run `crtr profile list` to see available profiles."})}o(k,"loadExactProfileManifest");const $=o(e=>({profileId:e.profileId,name:e.manifest.name}),"entryCandidate");function C(e,t,r){if(x.test(t)){const i=e.find(a=>r(a).profileId===t);if(i!==void 0)return i}const n=e.filter(i=>r(i).name===t);if(n.length===1)return n[0];if(n.length>1){const i=n.map(a=>r(a).profileId);throw R(`profile name "${t}" matches ${i.length} profiles: ${i.join(", ")}`,{received:t,candidates:i,next:`Re-run with one exact profile id: ${i.join(", ")}.`})}return null}o(C,"findExactProfile");function D(e,t,r){const n=e.map(i=>`${r(i).profileId} (${r(i).name})`);return u(`profile not found: ${t}`,{received:t,available:n,next:n.length>0?`Re-run with one of the profiles on this host: ${n.join(", ")} \u2014 or create one with \`crtr profile new --name <name>\`.`:"No profiles exist on this host yet \u2014 create one with `crtr profile new --name <name>`."})}o(D,"profileNotFound");function te(e){const t=ee(e);if(t.kind==="resolved")return t.entry;throw D(y(),e,$)}o(te,"loadProfileManifest");function ge(e){return re(y(),e,$)}o(ge,"resolveProfileOperand");function re(e,t,r){const n=C(e,t,r);if(n!==null)return n;const{match:i,tied:a}=B(t,e.map(r));if(i!==null){const s=e.find(c=>r(c).profileId===i.profileId);return console.error(`crtr: no profile named "${t}" \u2014 using the closest match: ${r(s).name} (${r(s).profileId})`),s}if(a.length>1){const s=a.map(c=>`${c.profileId} (${c.name})`);throw R(`"${t}" is equally close to ${a.length} profiles: ${s.join(", ")}`,{received:t,candidates:a.map(c=>c.profileId),next:`Re-run with one exact profile id or name: ${s.join(", ")}.`})}throw D(e,t,r)}o(re,"matchProfileOperand");function L(e){const t=E(e);if(!q(t)||!H(t).isDirectory())throw f(`project directory does not exist or is not a directory: ${e}`,{received:e,next:"Pass an existing directory path."});try{return b(t)}catch{return t}}o(L,"resolveExistingProjectDir");function N(e){const t=new Set,r=[];for(const n of e)t.has(n.path)||(t.add(n.path),r.push(n));return r}o(N,"dedupeProjects");function ne(e){let t=e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"");return t.length>40&&(t=t.slice(0,40).replace(/-+$/g,"")),t.length>0?t:"profile"}o(ne,"slugify");function oe(e){const t=ne(e);for(let r=0;r<20;r++){const n=K().replace(/-/g,"").slice(0,8),i=`${t}-${n}`;if(d(i)===null)return i}throw W(`failed to generate a unique profile id for "${e}" after 20 attempts`)}o(oe,"generateProfileId");async function Pe(){const e=d(p);if(e!==null)return g(p,e.manifest),e;const t={schema_version:S,name:Q,projects:[],grantee:"app:terminal",paused_at:null,created_at:m(),last_used_at:m()};return g(p,t),await h(r=>r.profiles.some(n=>n.profileId===p)?[]:[{op:"profile.insert",profile:{profileId:p,manifest:t}}]),d(p)}o(Pe,"ensureRootProfile");const z=/^[A-Za-z0-9][A-Za-z0-9_-]*$/;function J(e){return`CRTR_PROFILE_META_${e.toUpperCase().replace(/[^A-Z0-9]+/g,"_")}`}o(J,"metadataEnvName");function _(e){if(typeof e!="object"||e===null||Array.isArray(e))throw f("metadata must be a map of string keys to string values",{field:"metadata",next:'Pass an object, e.g. {"role": "support"}.'});const t=new Map;for(const[r,n]of Object.entries(e)){if(!z.test(r))throw f(`invalid metadata key: ${JSON.stringify(r)}`,{received:r,field:"metadata",next:"Keys start with a letter or digit, followed by letters, digits, `_`, or `-`."});if(typeof n!="string")throw f(`metadata value for ${r} must be a string`,{received:typeof n,field:"metadata",next:"Every metadata value is an env-var value \u2014 pass strings only."});if(n.includes("\0"))throw f(`metadata value for ${r} contains a NUL byte, which no OS accepts in an environment variable`,{field:"metadata",next:"Remove NUL bytes from the value and retry."});const i=J(r),a=t.get(i);if(a!==void 0)throw f(`metadata keys ${JSON.stringify(a)} and ${JSON.stringify(r)} both surface as ${i}`,{field:"metadata",next:"Rename or drop one \u2014 keys must stay distinct after uppercasing and `_`-folding."});t.set(i,r)}}o(_,"assertProfileMetadata");function T(e){if(typeof e!="object"||e===null||Array.isArray(e))return{};const t={},r=new Set;for(const[n,i]of Object.entries(e)){if(typeof i!="string"||!z.test(n)||i.includes("\0"))continue;const a=J(n);r.has(a)||(r.add(a),t[n]=i)}return t}o(T,"sanitizeProfileMetadata");async function ie(e,t=[],r={}){const n=e.trim();if(n==="")throw f("profile name must not be empty");r.metadata!==void 0&&_(r.metadata),M(t);const i=N(t.map(c=>({path:L(c.path)}))),a=oe(n),s={schema_version:S,name:n,projects:i,paused_at:null,...r.grantee===void 0?{}:{grantee:r.grantee},...r.defaultKind!==void 0?{default_kind:r.defaultKind}:{},...r.metadata!==void 0&&Object.keys(r.metadata).length>0?{metadata:{...r.metadata}}:{},created_at:m(),last_used_at:null};return g(a,s),await h(()=>[{op:"profile.insert",profile:{profileId:a,manifest:s}}]),{profileId:a,manifest:s}}o(ie,"createProfile");async function $e(e){if(!e.startsWith("app:")||e.length<=4)throw f("an app grantee is required");const t=`app-${e.slice(4)}`,r=y().filter(n=>n.manifest.name===t&&n.manifest.grantee===e);if(r.length>1)throw new Error(`duplicate default profile for ${e}`);return r[0]?r[0]:await ie(t,[],{grantee:e})}o($e,"ensureAppProfile");function U(e){if(P(e),d(e)===null)throw u(`profile not found: ${e}`,{received:e,next:"Run `crtr profile list` to see available profiles."})}o(U,"requireExistingProfile");async function l(e,t){U(e);let r;return await h(n=>{const i=n.profiles.find(a=>a.profileId===e);if(i===void 0)throw u(`profile not found: ${e}`,{received:e});return r={profileId:e,manifest:t(structuredClone(i.manifest))},[{op:"profile.update",profile:r}]}),r}o(l,"mutateManifest");async function _e(e){return await l(e,t=>({...t,last_used_at:m()}))}o(_e,"updateProfileLastUsed");function ae(e){if(e==null||e==="")return null;try{return te(e)}catch{return null}}o(ae,"loadProfileManifestOrNull");function je(e){return e==null||e===""||!x.test(e)?!1:Y(e)}o(je,"isProfilePaused");function be(){return V()}o(be,"pausedCanvasProfiles");function se(e){if(e.manifest.paused_at!==null)throw f(`profile "${e.manifest.name}" is paused and cannot start nodes`,{received:e.profileId,next:`Resume it with \`crtr profile resume ${e.profileId}\`.`})}o(se,"assertProfileEntryActive");function Ae(e){const t=ae(e);return t===null?null:(se(t),t)}o(Ae,"assertProfileActive");async function Ee(e){return await l(e,t=>({...t,paused_at:t.paused_at??m()}))}o(Ee,"pauseProfile");async function Se(e){let t=null;return{...await l(e,n=>(t=n.paused_at,{...n,paused_at:null})),pausedAt:t}}o(Se,"resumeProfile");async function Oe(e,t){return await l(e,r=>({...r,default_kind:t}))}o(Oe,"setProfileDefaultKind");async function Re(e,t,r=[]){return _(t),await l(e,n=>{const i={...T(n.metadata),...t};for(const c of r)delete i[c];_(i);const{metadata:a,...s}=n;return{...s,...Object.keys(i).length>0?{metadata:i}:{}}})}o(Re,"updateProfileMetadata");function Me(e){if(e===null||e==="")return{};try{return T(k(e).manifest.metadata)}catch{return{}}}o(Me,"readProfileMetadata");async function ke(e,t){const r=t.trim();if(r==="")throw f("profile name must not be empty");return await l(e,i=>({...i,name:r}))}o(ke,"renameProfile");async function Ce(e,t){M([{path:t}]);const r=L(t);return await l(e,i=>({...i,projects:N([...i.projects,{path:r}])}))}o(Ce,"addProfileProject");async function De(e,t,r){return X({[t]:r}),await l(e,n=>{const{[t]:i,...a}=n.delivery_limits??{},s=r==="content"?a:{...a,[t]:r},{delivery_limits:c,...j}=n;return Object.keys(s).length===0?j:{...j,delivery_limits:s}})}o(De,"setProfileDeliveryLimit");async function Le(e,t){const r=E(t);let n=r;try{n=b(r)}catch{}return await l(e,a=>{const s=a.projects.filter(c=>c.path!==n&&c.path!==r);if(s.length===a.projects.length)throw u(`project not found on profile: ${t}`,{received:t,next:`Current projects: ${a.projects.length>0?a.projects.map(c=>c.path).join(", "):"(none)"}.`});return{...a,projects:s}})}o(Le,"removeProfileProject");async function Ne(e){U(e);const t=v(w(k(e).manifest),e);await h(r=>{if(!r.profiles.some(n=>n.profileId===e))throw u(`profile not found: ${e}`,{received:e});return[{op:"profile.delete",profileId:e}]}),A(t.dir,{recursive:!0,force:!0}),A(t.env,{force:!0})}o(Ne,"deleteProfile");export{p as ROOT_PROFILE_ID,Ce as addProfileProject,X as assertDeliveryLimits,Ae as assertProfileActive,se as assertProfileEntryActive,_ as assertProfileMetadata,M as assertProfileProjects,ie as createProfile,Ne as deleteProfile,$e as ensureAppProfile,Pe as ensureRootProfile,je as isProfilePaused,y as listProfiles,k as loadExactProfileManifest,te as loadProfileManifest,ee as lookupProfile,re as matchProfileOperand,J as metadataEnvName,Ee as pauseProfile,be as pausedCanvasProfiles,we as profileDir,Me as readProfileMetadata,Le as removeProfileProject,ke as renameProfile,ge as resolveProfileOperand,Se as resumeProfile,T as sanitizeProfileMetadata,Oe as setProfileDefaultKind,De as setProfileDeliveryLimit,_e as updateProfileLastUsed,Re as updateProfileMetadata};
1
+ var W=Object.defineProperty;var i=(e,t)=>W(e,"name",{value:t,configurable:!0});import{randomUUID as Z}from"node:crypto";import{existsSync as V,realpathSync as E,rmSync as S,statSync as Y}from"node:fs";import{resolve as O}from"node:path";import{SCHEMA_VERSION as R}from"../../types.js";import{profileSpace as g}from"../layout.js";import{PROFILE_PROJECT_MEMORY_VALUES as M}from"../../api/dto/profiles.js";import{ensureDir as G,nowIso as h}from"../fs-utils.js";import{cachedPausedProfileIds as B,cachedProfilePaused as Q,getCachedProfile as p,listCachedProfiles as X,writeSettings as w}from"../scoped-state/person-store.js";import{usage as l,notFound as u,ambiguous as k,general as I}from"../errors.js";import{fuzzyMatchProfile as ee}from"./fuzzy-match.js";import{isCloudRole as C,personId as te}from"../canvas/person-state.js";const P=/^[a-z0-9]+(?:-[a-z0-9]+)*-[0-9a-f]{8}$/,m="root-00000000",$="root";function y(e){return e.grantee??"app:terminal"}i(y,"profileApp");function x(e,t){G(g(y(t),e).dir)}i(x,"scaffoldProfile");function _(e){if(!P.test(e))throw l(`invalid profile id: ${e}`,{received:e,next:"Profile ids look like <slug>-<8-hex-chars> (as printed by `crtr profile new`). Pass an exact id or a profile name, not a raw path."})}i(_,"assertSafeProfileId");function _e(e){_(e);const t=p(e);if(t===null)throw u(`profile not found: ${e}`,{received:e});return g(y(t.manifest),e).dir}i(_e,"profileDir");function D(e){if(!Array.isArray(e))throw l("projects must be an array of {path} objects",{received:typeof e,field:"projects",next:'Pass entries like {"path": "/abs/dir"}.'});for(const t of e){if(typeof t!="object"||t===null||Array.isArray(t))throw l(`invalid project entry: ${JSON.stringify(t)}`,{field:"projects",next:'Every project is an object like {"path": "/abs/dir"}.'});const{path:r}=t;if(typeof r!="string"||r.trim()==="")throw l(`project entry has no path: ${JSON.stringify(t)}`,{field:"projects",next:"Give every project entry a non-empty directory path."})}}i(D,"assertProfileProjects");function re(e){const t=M.join(", ");if(typeof e!="object"||e===null||Array.isArray(e))throw l("delivery_limits must be an object mapping owner handles to a level",{received:typeof e,field:"delivery_limits",next:`Pass e.g. {"crouter": "preview"} \u2014 each level is one of: ${t}.`});for(const[r,n]of Object.entries(e)){if(r.trim()==="")throw l("delivery_limits has an empty owner handle",{field:"delivery_limits",next:"Key each limit by an owner handle such as a repo handle or `user`, or exactly by `<type>:<handle>` such as `repo:crouter`."});if(typeof n!="string"||!M.includes(n))throw l(`invalid delivery limit for ${r}: ${JSON.stringify(n)}`,{received:String(n),field:"delivery_limits",next:`Set ${r} to one of: ${t}.`})}}i(re,"assertDeliveryLimits");function ne(){return{entries:X(),unreadableIds:[]}}i(ne,"enumerateProfiles");function v(){return ne().entries}i(v,"listProfiles");function ie(e){const t=v(),r=N(t,e,j);return r?{kind:"resolved",entry:r}:{kind:"absent"}}i(ie,"lookupProfile");function L(e){_(e);const t=p(e);if(t!==null)return t;throw u(`profile not found: ${e}`,{received:e,next:"Run `crtr profile list` to see available profiles."})}i(L,"loadExactProfileManifest");const j=i(e=>({profileId:e.profileId,name:e.manifest.name}),"entryCandidate");function N(e,t,r){if(P.test(t)){const o=e.find(a=>r(a).profileId===t);if(o!==void 0)return o}const n=e.filter(o=>r(o).name===t);if(n.length===1)return n[0];if(n.length>1){const o=n.map(a=>r(a).profileId);throw k(`profile name "${t}" matches ${o.length} profiles: ${o.join(", ")}`,{received:t,candidates:o,next:`Re-run with one exact profile id: ${o.join(", ")}.`})}return null}i(N,"findExactProfile");function z(e,t,r){const n=e.map(o=>`${r(o).profileId} (${r(o).name})`);return u(`profile not found: ${t}`,{received:t,available:n,next:n.length>0?`Re-run with one of the profiles on this host: ${n.join(", ")} \u2014 or create one with \`crtr profile new --name <name>\`.`:"No profiles exist on this host yet \u2014 create one with `crtr profile new --name <name>`."})}i(z,"profileNotFound");function oe(e){const t=ie(e);if(t.kind==="resolved")return t.entry;throw z(v(),e,j)}i(oe,"loadProfileManifest");function je(e){return ae(v(),e,j)}i(je,"resolveProfileOperand");function ae(e,t,r){const n=N(e,t,r);if(n!==null)return n;const{match:o,tied:a}=ee(t,e.map(r));if(o!==null){const s=e.find(f=>r(f).profileId===o.profileId);return console.error(`crtr: no profile named "${t}" \u2014 using the closest match: ${r(s).name} (${r(s).profileId})`),s}if(a.length>1){const s=a.map(f=>`${f.profileId} (${f.name})`);throw k(`"${t}" is equally close to ${a.length} profiles: ${s.join(", ")}`,{received:t,candidates:a.map(f=>f.profileId),next:`Re-run with one exact profile id or name: ${s.join(", ")}.`})}throw z(e,t,r)}i(ae,"matchProfileOperand");function J(e){const t=O(e);if(!V(t)||!Y(t).isDirectory())throw l(`project directory does not exist or is not a directory: ${e}`,{received:e,next:"Pass an existing directory path."});try{return E(t)}catch{return t}}i(J,"resolveExistingProjectDir");function T(e){const t=new Set,r=[];for(const n of e)t.has(n.path)||(t.add(n.path),r.push(n));return r}i(T,"dedupeProjects");function se(e){let t=e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"");return t.length>40&&(t=t.slice(0,40).replace(/-+$/g,"")),t.length>0?t:"profile"}i(se,"slugify");function fe(e){const t=se(e);for(let r=0;r<20;r++){const n=Z().replace(/-/g,"").slice(0,8),o=`${t}-${n}`;if(p(o)===null)return o}throw I(`failed to generate a unique profile id for "${e}" after 20 attempts`)}i(fe,"generateProfileId");let b=null;function be(e){b=e}i(be,"setCloudProfileWriter");async function Ae(){if(C()){const r=v().find(n=>n.manifest.name===$&&y(n.manifest)==="app:terminal");return r!==void 0?(x(r.profileId,r.manifest),r):await q($,[],{grantee:"app:terminal"})}const e=p(m);if(e!==null)return x(m,e.manifest),e;const t={schema_version:R,name:$,projects:[],grantee:"app:terminal",paused_at:null,created_at:h(),last_used_at:h()};return x(m,t),await w(r=>r.profiles.some(n=>n.profileId===m)?[]:[{op:"profile.insert",profile:{profileId:m,manifest:t}}]),p(m)}i(Ae,"ensureRootProfile");const U=/^[A-Za-z0-9][A-Za-z0-9_-]*$/;function F(e){return`CRTR_PROFILE_META_${e.toUpperCase().replace(/[^A-Z0-9]+/g,"_")}`}i(F,"metadataEnvName");function A(e){if(typeof e!="object"||e===null||Array.isArray(e))throw l("metadata must be a map of string keys to string values",{field:"metadata",next:'Pass an object, e.g. {"role": "support"}.'});const t=new Map;for(const[r,n]of Object.entries(e)){if(!U.test(r))throw l(`invalid metadata key: ${JSON.stringify(r)}`,{received:r,field:"metadata",next:"Keys start with a letter or digit, followed by letters, digits, `_`, or `-`."});if(typeof n!="string")throw l(`metadata value for ${r} must be a string`,{received:typeof n,field:"metadata",next:"Every metadata value is an env-var value \u2014 pass strings only."});if(n.includes("\0"))throw l(`metadata value for ${r} contains a NUL byte, which no OS accepts in an environment variable`,{field:"metadata",next:"Remove NUL bytes from the value and retry."});const o=F(r),a=t.get(o);if(a!==void 0)throw l(`metadata keys ${JSON.stringify(a)} and ${JSON.stringify(r)} both surface as ${o}`,{field:"metadata",next:"Rename or drop one \u2014 keys must stay distinct after uppercasing and `_`-folding."});t.set(o,r)}}i(A,"assertProfileMetadata");function K(e){if(typeof e!="object"||e===null||Array.isArray(e))return{};const t={},r=new Set;for(const[n,o]of Object.entries(e)){if(typeof o!="string"||!U.test(n)||o.includes("\0"))continue;const a=F(n);r.has(a)||(r.add(a),t[n]=o)}return t}i(K,"sanitizeProfileMetadata");async function q(e,t=[],r={}){const n=e.trim();if(n==="")throw l("profile name must not be empty");r.metadata!==void 0&&A(r.metadata),D(t);const o=T(t.map(d=>({path:J(d.path)}))),a=fe(n),s={schema_version:R,name:n,projects:o,paused_at:null,...r.grantee===void 0?{}:{grantee:r.grantee},...r.defaultKind!==void 0?{default_kind:r.defaultKind}:{},...r.metadata!==void 0&&Object.keys(r.metadata).length>0?{metadata:{...r.metadata}}:{},created_at:h(),last_used_at:null};x(a,s);const f=i(async()=>{await w(()=>[{op:"profile.insert",profile:{profileId:a,manifest:s}}])},"insert");if(!C())await f();else{if(b===null)throw new Error("a cloud role has no profile environment writer registered");await b({profileId:a,grantee:y(s),person:te()},async d=>{s.environment_id=d,await f()})}return{profileId:a,manifest:s}}i(q,"createProfile");async function Ee(e){if(!e.startsWith("app:")||e.length<=4)throw l("an app grantee is required");const t=`app-${e.slice(4)}`,r=v().filter(n=>n.manifest.name===t&&n.manifest.grantee===e);if(r.length>1)throw new Error(`duplicate default profile for ${e}`);return r[0]?r[0]:await q(t,[],{grantee:e})}i(Ee,"ensureAppProfile");function H(e){if(_(e),p(e)===null)throw u(`profile not found: ${e}`,{received:e,next:"Run `crtr profile list` to see available profiles."})}i(H,"requireExistingProfile");async function c(e,t){H(e);let r;return await w(n=>{const o=n.profiles.find(a=>a.profileId===e);if(o===void 0)throw u(`profile not found: ${e}`,{received:e});return r={profileId:e,manifest:t(structuredClone(o.manifest))},[{op:"profile.update",profile:r}]}),r}i(c,"mutateManifest");async function Se(e){return await c(e,t=>({...t,last_used_at:h()}))}i(Se,"updateProfileLastUsed");function le(e){if(e==null||e==="")return null;try{return oe(e)}catch{return null}}i(le,"loadProfileManifestOrNull");function Oe(e){return e==null||e===""||!P.test(e)?!1:Q(e)}i(Oe,"isProfilePaused");function Re(){return B()}i(Re,"pausedCanvasProfiles");function ce(e){if(e.manifest.paused_at!==null)throw l(`profile "${e.manifest.name}" is paused and cannot start nodes`,{received:e.profileId,next:`Resume it with \`crtr profile resume ${e.profileId}\`.`})}i(ce,"assertProfileEntryActive");function Me(e){const t=le(e);return t===null?null:(ce(t),t)}i(Me,"assertProfileActive");async function ke(e){return await c(e,t=>({...t,paused_at:t.paused_at??h()}))}i(ke,"pauseProfile");async function Ce(e){let t=null;return{...await c(e,n=>(t=n.paused_at,{...n,paused_at:null})),pausedAt:t}}i(Ce,"resumeProfile");async function De(e,t){return await c(e,r=>({...r,default_kind:t}))}i(De,"setProfileDefaultKind");async function Le(e,t,r=[]){return A(t),await c(e,n=>{const o={...K(n.metadata),...t};for(const f of r)delete o[f];A(o);const{metadata:a,...s}=n;return{...s,...Object.keys(o).length>0?{metadata:o}:{}}})}i(Le,"updateProfileMetadata");function Ne(e){if(e===null||e==="")return{};try{return K(L(e).manifest.metadata)}catch{return{}}}i(Ne,"readProfileMetadata");async function ze(e,t){const r=t.trim();if(r==="")throw l("profile name must not be empty");return await c(e,o=>({...o,name:r}))}i(ze,"renameProfile");async function Je(e,t){D([{path:t}]);const r=J(t);return await c(e,o=>({...o,projects:T([...o.projects,{path:r}])}))}i(Je,"addProfileProject");async function Te(e,t,r){return re({[t]:r}),await c(e,n=>{const{[t]:o,...a}=n.delivery_limits??{},s=r==="content"?a:{...a,[t]:r},{delivery_limits:f,...d}=n;return Object.keys(s).length===0?d:{...d,delivery_limits:s}})}i(Te,"setProfileDeliveryLimit");async function Ue(e,t){const r=O(t);let n=r;try{n=E(r)}catch{}return await c(e,a=>{const s=a.projects.filter(f=>f.path!==n&&f.path!==r);if(s.length===a.projects.length)throw u(`project not found on profile: ${t}`,{received:t,next:`Current projects: ${a.projects.length>0?a.projects.map(f=>f.path).join(", "):"(none)"}.`});return{...a,projects:s}})}i(Ue,"removeProfileProject");async function Fe(e){H(e);const t=g(y(L(e).manifest),e);await w(r=>{if(!r.profiles.some(n=>n.profileId===e))throw u(`profile not found: ${e}`,{received:e});return[{op:"profile.delete",profileId:e}]}),S(t.dir,{recursive:!0,force:!0}),S(t.env,{force:!0})}i(Fe,"deleteProfile");export{m as ROOT_PROFILE_ID,Je as addProfileProject,re as assertDeliveryLimits,Me as assertProfileActive,ce as assertProfileEntryActive,A as assertProfileMetadata,D as assertProfileProjects,q as createProfile,Fe as deleteProfile,Ee as ensureAppProfile,Ae as ensureRootProfile,Oe as isProfilePaused,v as listProfiles,L as loadExactProfileManifest,oe as loadProfileManifest,ie as lookupProfile,ae as matchProfileOperand,F as metadataEnvName,ke as pauseProfile,Re as pausedCanvasProfiles,_e as profileDir,Ne as readProfileMetadata,Ue as removeProfileProject,ze as renameProfile,je as resolveProfileOperand,Ce as resumeProfile,K as sanitizeProfileMetadata,be as setCloudProfileWriter,De as setProfileDefaultKind,Te as setProfileDeliveryLimit,Se as updateProfileLastUsed,Le as updateProfileMetadata};
@@ -21,6 +21,8 @@ export interface AuditFilters {
21
21
  run_id?: string;
22
22
  since?: string;
23
23
  until?: string;
24
+ /** Only rows written for this person (the `sub` column). */
25
+ sub?: string;
24
26
  }
25
27
  export declare function listAudit(grantee: string | null, filters: AuditFilters): Promise<{
26
28
  rows: Record<string, unknown>[];
@@ -1,4 +1,4 @@
1
- var p=Object.defineProperty;var r=(i,n)=>p(i,"name",{value:n,configurable:!0});import{openDb as a}from"../canvas/db.js";import{runtimeId as m}from"../grants/sync.js";import{terminalGrant as h}from"../grants/mirror.js";async function E(i){const{caller:n}=i,e=n.sub??(n.class==="person"?(await h())?.sub??null:null),t=n.grantee??e,o=e&&t?[e,t,`runtime:${m()}`]:null;await(await a()).prepare(`INSERT INTO audit_rows
1
+ var l=Object.defineProperty;var s=(i,n)=>l(i,"name",{value:n,configurable:!0});import{openDb as a}from"../canvas/db.js";import{runtimeId as h}from"../grants/sync.js";import{terminalGrant as m}from"../grants/mirror.js";async function b(i){const{caller:n}=i,e=n.sub??(n.class==="person"?(await m())?.sub??null:null),t=n.grantee??e,u=e&&t?[e,t,`runtime:${h()}`]:null;await(await a()).prepare(`INSERT INTO audit_rows
2
2
  (request_id, time, grantee, sub, client_id, class, operation, scope_checked,
3
3
  outcome, node_id, run_id, resource, initiator_chain, provider, tool, jti, receipt_id, responder, question_id)
4
- VALUES (?, ?, ?, ?, ?, ?, 'provider.call', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(i.request_id,new Date().toISOString(),t,e,n.client_id,n.class,i.scope_checked??null,i.outcome,n.node_id,n.run_id,`${i.provider} ${i.tool}`,o?JSON.stringify(o):null,i.provider,i.tool,i.jti??null,i.receipt_id??null,i.responder??null,i.question_id??null)}r(E,"writeProviderAudit");async function I(i,n){const e=[],t=[];i!==null&&(e.push("grantee = ?"),t.push(i)),n.after!==void 0&&(e.push("id < ?"),t.push(n.after));for(const u of["operation","outcome","run_id"])n[u]!==void 0&&(e.push(`${u} = ?`),t.push(n[u]));n.since!==void 0&&(e.push("time >= ?"),t.push(n.since)),n.until!==void 0&&(e.push("time <= ?"),t.push(n.until));const o=await(await a()).prepareUncached(`SELECT * FROM audit_rows ${e.length?`WHERE ${e.join(" AND ")}`:""} ORDER BY id DESC LIMIT ?`).all(...t,n.limit+1),s=o.length>n.limit;s&&o.pop();const c=s?String(o.at(-1)?.id):null;return{rows:o.map(({id:u,initiator_chain:d,...l})=>({...l,initiator_chain:typeof d=="string"?JSON.parse(d):null})),next:c}}r(I,"listAudit");export{I as listAudit,E as writeProviderAudit};
4
+ VALUES (?, ?, ?, ?, ?, ?, 'provider.call', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(i.request_id,new Date().toISOString(),t,e,n.client_id,n.class,i.scope_checked??null,i.outcome,n.node_id,n.run_id,`${i.provider} ${i.tool}`,u?JSON.stringify(u):null,i.provider,i.tool,i.jti??null,i.receipt_id??null,i.responder??null,i.question_id??null)}s(b,"writeProviderAudit");async function E(i,n){const e=[],t=[];i!==null&&(e.push("grantee = ?"),t.push(i)),n.sub!==void 0&&(e.push("sub = ?"),t.push(n.sub)),n.after!==void 0&&(e.push("id < ?"),t.push(n.after));for(const o of["operation","outcome","run_id"])n[o]!==void 0&&(e.push(`${o} = ?`),t.push(n[o]));n.since!==void 0&&(e.push("time >= ?"),t.push(n.since)),n.until!==void 0&&(e.push("time <= ?"),t.push(n.until));const u=await(await a()).prepareUncached(`SELECT * FROM audit_rows ${e.length?`WHERE ${e.join(" AND ")}`:""} ORDER BY id DESC LIMIT ?`).all(...t,n.limit+1),d=u.length>n.limit;d&&u.pop();const p=d?String(u.at(-1)?.id):null;return{rows:u.map(({id:o,initiator_chain:r,...c})=>({...c,initiator_chain:typeof r=="string"?JSON.parse(r):null})),next:p}}s(E,"listAudit");export{E as listAudit,b as writeProviderAudit};
@@ -1,2 +1,2 @@
1
- var ue=Object.defineProperty;var a=(e,n)=>ue(e,"name",{value:n,configurable:!0});import{existsSync as g,readdirSync as W,readFileSync as S,rmSync as $,statSync as k}from"node:fs";import{join as d}from"node:path";import{validateCommandManifest as de}from"../../api/command-manifest/manifest.js";import{crtrHome as z}from"../canvas/paths.js";import{withCanvasWrite as G}from"../canvas/db.js";import{bundleVersion as ce,isInstalledGeneration as fe,recordCheckedGeneration as le}from"../command-plugins/bundle.js";import{readConfig as q,readState as pe,updateConfig as me,updateState as ve}from"../config.js";import{emitEvent as m}from"../events/emit.js";import{describeOutboundFailure as he,outboundFetch as we,outboundRefusal as V,OutboundError as ge}from"../events/outbound.js";import{withExclusiveDirectoryLockAsync as Z}from"../exclusive-lock.js";import{atomicWriteText as x,ensureDir as T,pathExists as $e}from"../fs-utils.js";import{reconcilePackages as L}from"../graph/package-reconcile.js";import{readPluginManifest as C}from"../manifest.js";import{bundleSwapLockPath as J}from"../plugin-swap-lock.js";import{pluginsDir as ye,userScopeRoot as K}from"../scope.js";import{listInstalledPlugins as be}from"../resolver.js";import{localGrants as j}from"../grants/mirror.js";import{ProviderError as c}from"./errors.js";const A="[A-Za-z0-9_-]{1,64}",_e=new RegExp(`^(${A}):(${A})$`),Q=new RegExp(`^${A}$`),Pe=3e4,X=3e4;function Y(e){const n=new Set;for(const r of e)if(r.status==="active")for(const{scope:t}of r.resolved_scopes){const o=_e.exec(t);o&&o[1]!=="crtr"&&n.add(o[1])}return[...n].sort()}a(Y,"providerIdsFromGrants");function v(e){return d(ye("user"),e)}a(v,"pluginRoot");function R(){return d(z(),"providers")}a(R,"providersRoot");function ee(e,n){return d(R(),e,`v${n}`)}a(ee,"majorDir");function B(e){if(!Q.test(e))throw new c("not_found",`"${e}" is not a provider id`,{param:"provider"})}a(B,"assertId");function E(){const e=q("user").runtime?.issuer;if(!e)throw new c("not_found","provider bundles need a directory-connected runtime");return e.replace(/\/+$/,"")}a(E,"directoryBase");async function ne(e,n){const r=`${E()}/providers/${encodeURIComponent(e)}/plugin${n.version!==void 0?`?version=${n.version}`:""}`;let t;try{t=await we("bundle_fetch",r,{headers:{Accept:"application/x-tar",...n.ifNoneMatch!==void 0?{"If-None-Match":n.ifNoneMatch}:{}},signal:AbortSignal.timeout(Pe)})}catch(i){return{status:"failed",message:`could not reach ${r}: ${i instanceof Error?i.message:String(i)}`,cause:i}}if(t.status===304){if(n.ifNoneMatch===void 0){const i=`HTTP 304 from ${r} without a conditional request`;return{status:"failed",message:i,cause:V("bundle_fetch",r,i)}}return{status:304}}if(t.status===404||t.status===410)return await t.body?.cancel().catch(()=>{}),{status:t.status===404?404:410,message:`HTTP ${t.status} from ${r}`};if(t.status<200||t.status>=300){await t.body?.cancel().catch(()=>{});{const i=`HTTP ${t.status} from ${r}`;return{status:"failed",message:i,cause:V("bundle_fetch",r,i)}}}let o;try{o=new Uint8Array(await t.arrayBuffer())}catch(i){return{status:"failed",message:`reading ${r} failed: ${i instanceof Error?i.message:String(i)}`,cause:new ge("bundle_fetch",r,i)}}const s=t.headers.get("etag");return{status:200,raw:o,...s!==null?{etag:s}:{}}}a(ne,"fetchProviderBundle");const I=new WeakSet;class M extends Error{static{a(this,"BundleInvalid")}issues;constructor(n,r){super(n),this.issues=r}}function re(e,n,r){if(r.provider===void 0)return"the archive carries no provider block";if(r.provider.id!==e)return`the archive is provider "${r.provider.id}", not "${e}"`;if(n!==void 0&&r.provider.major!==n)return`the archive is major ${r.provider.major}, not ${n}`;const t=["hooks","kinds","page_components","memory_extensions"].filter(o=>r[o]!==void 0);if(t.length>0)return`a provider bundle may not carry ${t.join(", ")}`}a(re,"wrongBundle");function D(e,n,r){m({level:"error",event:"daemon.providers.bundle_invalid",fields:{provider:e,...n!==void 0?{major:n}:{},...r instanceof M?{issues:r.issues}:{}},error:r})}a(D,"logInvalid");function te(e,n,r){m({level:"warn",event:"daemon.providers.bundle_fetch_failed",fields:{provider:e,...n!==void 0?{major:n}:{},message:r instanceof Error?r.message:String(r),...he(r instanceof Error?r.cause:void 0)}})}a(te,"logFailure");async function oe(){return import("../../commands/pkg/plugin-manage.js")}a(oe,"pluginManage");const y=new Map,b=a(e=>`${z()}\0${e}`,"installKey");function Se(e){const n=e,r=n?.code??n?.cause?.code;return typeof r=="string"&&/^[A-Z][A-Z0-9_]{1,30}$/.test(r)?`${r} on provider install directory`:e instanceof c&&e.message.includes("HTTP 404")?"directory returned HTTP 404":e instanceof c&&e.message.includes("HTTP 410")?"directory returned HTTP 410":"provider bundle could not be installed"}a(Se,"publicInstallCause");function Ee(e){if(h(e)===void 0)return y.get(b(e))??{status:"installing"}}a(Ee,"providerInstallState");function Ie(e){return new c("provider_unavailable",`provider "${e}" unavailable: ${Ee(e)?.error??"bundle installation pending"}`)}a(Ie,"unavailableProviderError");function h(e){const n=C(v(e));if(!(n?.provider===void 0||n.transport?.kind!=="http"&&n.transport?.kind!=="machine")&&$e(d(v(e),"commands.json"))){try{if(k(d(v(e),"commands.json")).size===0)return}catch{return}return{provider:n.provider,transport:n.transport.kind==="machine"?{kind:"machine"}:{kind:"http",endpoint:n.transport.endpoint}}}}a(h,"installedManifest");async function N(e,n,r=!1){const t=K();h(e)===void 0&&y.set(b(e),{status:"installing"});try{return T(d(t,"tmp")),await Z(J(t,e),async()=>{const o=v(e),s=C(o);if(s!==null&&s.provider===void 0)throw new c("provider_unavailable",`plugin "${e}" is installed and is not the directory's provider bundle`);const i=h(e)!==void 0;if(i&&r)return"unchanged";const f=i&&n?pe("user").plugins[e]?.etag:void 0,u=await ne(e,f!==void 0?{ifNoneMatch:f}:{});if(u.status===304)return"unchanged";if(u.status===404||u.status===410)throw new c("not_found",`the directory serves no provider "${e}" (${u.message})`,{param:"provider"});if(u.status==="failed")throw new c("provider_unavailable",`provider "${e}" bundle fetch failed: ${u.message}`,{cause:u.cause});const l=ce(u.raw);if(i&&fe(e,"user",o,l))return await le("user",e,u.etag,{swapped:!1}),"unchanged";const{installPluginArchive:w}=await oe();try{await w(e,u.raw,{scope:"user",scopeRootPath:t,root:o,version:l,etag:u.etag,enable:!i,bundle:{endpoint:`${E()}/providers/${encodeURIComponent(e)}/plugin`},transport:{kind:"http",endpoint:E()},accept:a(p=>re(e,void 0,p),"accept")})}catch(p){D(e,void 0,p);const U=new c("not_found",`provider "${e}" served an invalid bundle: ${p instanceof Error?p.message:String(p)}`,{param:"provider"});throw I.add(U),U}try{await G(async p=>await L(p))}catch(p){m({level:"error",event:"daemon.providers.docs_reconcile_failed",fields:{provider:e},error:p})}return m({level:"info",event:"daemon.providers.bundle_installed",fields:{provider:e,version:l,replaced:i}}),y.delete(b(e)),i?"updated":"installed"},{timeoutMs:X,timeoutError:a(()=>new c("provider_unavailable",`timed out waiting for another process replacing plugin "${e}"`),"timeoutError")})}catch(o){throw h(e)===void 0&&y.set(b(e),{status:"install_failed",error:Se(o)}),o instanceof c?o:(m({level:"error",event:"daemon.providers.bundle_install_failed",fields:{provider:e},error:o}),Ie(e))}}a(N,"refreshInstalled");async function en(e){B(e),h(e)===void 0&&await N(e,!1,!0)}a(en,"ensureProviderBundle");async function nn(e,n){return B(e),n.version!==void 0&&h(e)?.provider.major!==n.version?O(e,n.version,n.conditional):N(e,n.conditional)}a(nn,"refreshProviderBundle");async function ae(e,n,r){const{validatePluginArchive:t}=await oe(),o=await t(r);if(o.bundle===void 0)throw new M(`provider "${e}" major ${n} bundle is invalid: ${o.issues.map(i=>i.message).join("; ")}`,o.issues);const s=re(e,n,o.bundle);if(s!==void 0)throw new M(`provider "${e}" major ${n} bundle is invalid: ${s}`,[]);if(o.bundle.transport===void 0)throw new M(`provider "${e}" major ${n} bundle declares no transport`,[]);return o.bundle}a(ae,"validateArchive");function Me(e){T(e),x(d(e,"retired"),`${new Date().toISOString()}
2
- `),$(d(e,"bundle.tar"),{force:!0}),$(d(e,"etag"),{force:!0})}a(Me,"retire");function ie(e,n){return new c("provider_version_retired",`provider "${e}" major ${n} is retired`,{param:"version"})}a(ie,"retiredError");async function O(e,n,r){const t=ee(e,n),o=d(t,"bundle.tar"),s=g(o),i=s&&r&&g(d(t,"etag"))?S(d(t,"etag"),"utf8"):void 0,f=await ne(e,{version:n,...i!==void 0?{ifNoneMatch:i}:{}});if(f.status===304)return"unchanged";if(f.status===410)throw Me(t),m({level:"info",event:"daemon.providers.major_retired",fields:{provider:e,major:n}}),ie(e,n);if(f.status===404)throw new c("not_found",`provider "${e}" never served major ${n}`,{param:"version"});if(f.status==="failed")throw new c("provider_unavailable",`provider "${e}" major ${n} bundle fetch failed: ${f.message}`,{cause:f.cause});try{await ae(e,n,f.raw)}catch(l){D(e,n,l);const w=new c(s?"provider_unavailable":"not_found",l instanceof Error?l.message:String(l),{param:"version"});throw I.add(w),w}T(t),$(d(t,"retired"),{force:!0});const u=s&&Buffer.from(f.raw).equals(S(o));return u||x(o,f.raw),f.etag!==void 0?x(d(t,"etag"),f.etag):$(d(t,"etag"),{force:!0}),u?"unchanged":s?"updated":"installed"}a(O,"refreshPinned");function ke(){const e=[],n=R();if(!g(n))return e;for(const r of W(n)){if(!Q.test(r))continue;let t;try{t=W(d(n,r))}catch{continue}for(const o of t){const s=/^v([1-9][0-9]*)$/.exec(o);s&&g(d(n,r,o,"bundle.tar"))&&e.push([r,Number(s[1])])}}return e}a(ke,"cachedMajors");function xe(e){const n=de(e,{transport:"http",reservedCoreNames:new Set});if(n.manifest===void 0)throw new c("not_found","the provider bundle's commands.json no longer validates");return n.manifest}a(xe,"readCommands");const _=new Map,H=new Map;function Te(e,n){const r=k(n),t=`${r.ino}:${r.mtimeMs}:${r.size}`,o=H.get(e);if(o?.stamp===t)return o.commands;const s=xe(JSON.parse(S(n,"utf8")));return H.set(e,{stamp:t,commands:s}),s}a(Te,"installedCommands");async function rn(e,n){B(e);const r=h(e);if(n===void 0||r?.provider.major===n){if(r===void 0)throw new c("not_found",`provider "${e}" has no installed bundle`,{param:"provider"});const l=v(e),w=Te(e,d(l,"commands.json"));return{id:e,major:r.provider.major,provider:r.provider,commands:w,transport:r.transport,source:"installed",root:l}}if(!Number.isInteger(n)||n<1)throw new c("invalid_request","version must be an integer of at least 1",{param:"version"});const t=ee(e,n);if(g(d(t,"retired")))throw ie(e,n);const o=d(t,"bundle.tar");g(o)||await O(e,n,!1);const s=`${e}/${n}`,i=k(o),f=`${i.mtimeMs}:${i.size}`;let u=_.get(s)?.stamp===f?_.get(s).bundle:void 0;if(u===void 0){try{u=await ae(e,n,new Uint8Array(S(o)))}catch(l){throw D(e,n,l),new c("not_found",`provider "${e}" has no valid bundle for major ${n}`,{param:"version"})}_.set(s,{stamp:f,bundle:u})}return{id:e,major:n,provider:u.provider,commands:u.commands,transport:u.transport.kind==="machine"?{kind:"machine"}:{kind:"http",endpoint:u.transport.endpoint},source:"pinned"}}a(rn,"resolveProviderBundle");let P=null,F=!1;function Ce(e,n){return e?.provider?.id===n&&e.bundle?.endpoint===`${E()}/providers/${encodeURIComponent(n)}/plugin`}a(Ce,"isDirectoryBundle");async function se(e){return Y(await j()).includes(e)}a(se,"grantNames");async function je(e){await Z(J(K(),e),async()=>{if(!(await se(e)||!Ce(C(v(e)),e))){$(v(e),{recursive:!0,force:!0}),await me("user",n=>{delete n.plugins[e]}),await ve("user",n=>{delete n.plugins[e]}),$(d(R(),e),{recursive:!0,force:!0}),H.delete(e),y.delete(b(e));for(const n of[..._.keys()])n.startsWith(`${e}/`)&&_.delete(n);try{await G(async n=>await L(n))}catch(n){m({level:"error",event:"daemon.providers.docs_reconcile_failed",fields:{provider:e},error:n})}m({level:"info",event:"daemon.providers.bundle_uninstalled",fields:{provider:e}})}},{timeoutMs:X,timeoutError:a(()=>new c("provider_unavailable",`timed out waiting for another process replacing plugin "${e}"`),"timeoutError")})}a(je,"uninstallProvider");async function Ae(){if(!(await j()).every(e=>e.status!=="active")){for(const e of be("user"))if(!(e.manifest.provider===void 0||await se(e.name)))try{await je(e.name)}catch(n){m({level:"error",event:"daemon.providers.bundle_uninstall_failed",fields:{provider:e.name},error:n})}}}a(Ae,"uninstallDisconnected");async function Re(){if(!q("user").runtime?.issuer)return;const e=Y(await j());for(const n of e)try{await N(n,!0)}catch(r){r instanceof Object&&I.has(r)||te(n,void 0,r)}await Ae();for(const[n,r]of ke())try{await O(n,r,!0)}catch(t){if(t instanceof c&&t.code==="provider_version_retired"||t instanceof Object&&I.has(t))continue;te(n,r,t)}}a(Re,"syncOnce");function tn(){return P!==null?(F=!0,P):(P=(async()=>{try{do F=!1,await Re();while(F)}finally{P=null}})(),P)}a(tn,"syncProviderBundles");export{en as ensureProviderBundle,Y as providerIdsFromGrants,Ee as providerInstallState,nn as refreshProviderBundle,rn as resolveProviderBundle,tn as syncProviderBundles,Ie as unavailableProviderError};
1
+ var ue=Object.defineProperty;var a=(e,n)=>ue(e,"name",{value:n,configurable:!0});import{existsSync as g,readdirSync as W,readFileSync as S,rmSync as $,statSync as k}from"node:fs";import{join as d}from"node:path";import{validateCommandManifest as de}from"../../api/command-manifest/manifest.js";import{crtrHome as z}from"../canvas/paths.js";import{withCanvasWrite as G}from"../canvas/db.js";import{bundleVersion as ce,isInstalledGeneration as fe,recordCheckedGeneration as le}from"../command-plugins/bundle.js";import{readState as pe,updateConfig as me,updateState as ve}from"../config.js";import{directoryIssuer as q}from"../directory/issuer.js";import{emitEvent as m}from"../events/emit.js";import{describeOutboundFailure as he,outboundFetch as we,outboundRefusal as V,OutboundError as ge}from"../events/outbound.js";import{withExclusiveDirectoryLockAsync as Z}from"../exclusive-lock.js";import{atomicWriteText as x,ensureDir as T,pathExists as $e}from"../fs-utils.js";import{reconcilePackages as L}from"../graph/package-reconcile.js";import{readPluginManifest as C}from"../manifest.js";import{bundleSwapLockPath as J}from"../plugin-swap-lock.js";import{pluginsDir as ye,userScopeRoot as K}from"../scope.js";import{listInstalledPlugins as be}from"../resolver.js";import{localGrants as j}from"../grants/mirror.js";import{ProviderError as c}from"./errors.js";const A="[A-Za-z0-9_-]{1,64}",_e=new RegExp(`^(${A}):(${A})$`),Q=new RegExp(`^${A}$`),Pe=3e4,X=3e4;function Y(e){const n=new Set;for(const r of e)if(r.status==="active")for(const{scope:t}of r.resolved_scopes){const o=_e.exec(t);o&&o[1]!=="crtr"&&n.add(o[1])}return[...n].sort()}a(Y,"providerIdsFromGrants");function v(e){return d(ye("user"),e)}a(v,"pluginRoot");function R(){return d(z(),"providers")}a(R,"providersRoot");function ee(e,n){return d(R(),e,`v${n}`)}a(ee,"majorDir");function B(e){if(!Q.test(e))throw new c("not_found",`"${e}" is not a provider id`,{param:"provider"})}a(B,"assertId");function E(){const e=q();if(!e)throw new c("not_found","provider bundles need a directory-connected runtime");return e}a(E,"directoryBase");async function ne(e,n){const r=`${E()}/providers/${encodeURIComponent(e)}/plugin${n.version!==void 0?`?version=${n.version}`:""}`;let t;try{t=await we("bundle_fetch",r,{headers:{Accept:"application/x-tar",...n.ifNoneMatch!==void 0?{"If-None-Match":n.ifNoneMatch}:{}},signal:AbortSignal.timeout(Pe)})}catch(i){return{status:"failed",message:`could not reach ${r}: ${i instanceof Error?i.message:String(i)}`,cause:i}}if(t.status===304){if(n.ifNoneMatch===void 0){const i=`HTTP 304 from ${r} without a conditional request`;return{status:"failed",message:i,cause:V("bundle_fetch",r,i)}}return{status:304}}if(t.status===404||t.status===410)return await t.body?.cancel().catch(()=>{}),{status:t.status===404?404:410,message:`HTTP ${t.status} from ${r}`};if(t.status<200||t.status>=300){await t.body?.cancel().catch(()=>{});{const i=`HTTP ${t.status} from ${r}`;return{status:"failed",message:i,cause:V("bundle_fetch",r,i)}}}let o;try{o=new Uint8Array(await t.arrayBuffer())}catch(i){return{status:"failed",message:`reading ${r} failed: ${i instanceof Error?i.message:String(i)}`,cause:new ge("bundle_fetch",r,i)}}const s=t.headers.get("etag");return{status:200,raw:o,...s!==null?{etag:s}:{}}}a(ne,"fetchProviderBundle");const I=new WeakSet;class M extends Error{static{a(this,"BundleInvalid")}issues;constructor(n,r){super(n),this.issues=r}}function re(e,n,r){if(r.provider===void 0)return"the archive carries no provider block";if(r.provider.id!==e)return`the archive is provider "${r.provider.id}", not "${e}"`;if(n!==void 0&&r.provider.major!==n)return`the archive is major ${r.provider.major}, not ${n}`;const t=["hooks","kinds","page_components","memory_extensions"].filter(o=>r[o]!==void 0);if(t.length>0)return`a provider bundle may not carry ${t.join(", ")}`}a(re,"wrongBundle");function D(e,n,r){m({level:"error",event:"daemon.providers.bundle_invalid",fields:{provider:e,...n!==void 0?{major:n}:{},...r instanceof M?{issues:r.issues}:{}},error:r})}a(D,"logInvalid");function te(e,n,r){m({level:"warn",event:"daemon.providers.bundle_fetch_failed",fields:{provider:e,...n!==void 0?{major:n}:{},message:r instanceof Error?r.message:String(r),...he(r instanceof Error?r.cause:void 0)}})}a(te,"logFailure");async function oe(){return import("../../commands/pkg/plugin-manage.js")}a(oe,"pluginManage");const y=new Map,b=a(e=>`${z()}\0${e}`,"installKey");function Se(e){const n=e,r=n?.code??n?.cause?.code;return typeof r=="string"&&/^[A-Z][A-Z0-9_]{1,30}$/.test(r)?`${r} on provider install directory`:e instanceof c&&e.message.includes("HTTP 404")?"directory returned HTTP 404":e instanceof c&&e.message.includes("HTTP 410")?"directory returned HTTP 410":"provider bundle could not be installed"}a(Se,"publicInstallCause");function Ee(e){if(h(e)===void 0)return y.get(b(e))??{status:"installing"}}a(Ee,"providerInstallState");function Ie(e){return new c("provider_unavailable",`provider "${e}" unavailable: ${Ee(e)?.error??"bundle installation pending"}`)}a(Ie,"unavailableProviderError");function h(e){const n=C(v(e));if(!(n?.provider===void 0||n.transport?.kind!=="http"&&n.transport?.kind!=="machine")&&$e(d(v(e),"commands.json"))){try{if(k(d(v(e),"commands.json")).size===0)return}catch{return}return{provider:n.provider,transport:n.transport.kind==="machine"?{kind:"machine"}:{kind:"http",endpoint:n.transport.endpoint}}}}a(h,"installedManifest");async function N(e,n,r=!1){const t=K();h(e)===void 0&&y.set(b(e),{status:"installing"});try{return T(d(t,"tmp")),await Z(J(t,e),async()=>{const o=v(e),s=C(o);if(s!==null&&s.provider===void 0)throw new c("provider_unavailable",`plugin "${e}" is installed and is not the directory's provider bundle`);const i=h(e)!==void 0;if(i&&r)return"unchanged";const f=i&&n?pe("user").plugins[e]?.etag:void 0,u=await ne(e,f!==void 0?{ifNoneMatch:f}:{});if(u.status===304)return"unchanged";if(u.status===404||u.status===410)throw new c("not_found",`the directory serves no provider "${e}" (${u.message})`,{param:"provider"});if(u.status==="failed")throw new c("provider_unavailable",`provider "${e}" bundle fetch failed: ${u.message}`,{cause:u.cause});const l=ce(u.raw);if(i&&fe(e,"user",o,l))return await le("user",e,u.etag,{swapped:!1}),"unchanged";const{installPluginArchive:w}=await oe();try{await w(e,u.raw,{scope:"user",scopeRootPath:t,root:o,version:l,etag:u.etag,enable:!i,bundle:{endpoint:`${E()}/providers/${encodeURIComponent(e)}/plugin`},transport:{kind:"http",endpoint:E()},accept:a(p=>re(e,void 0,p),"accept")})}catch(p){D(e,void 0,p);const U=new c("not_found",`provider "${e}" served an invalid bundle: ${p instanceof Error?p.message:String(p)}`,{param:"provider"});throw I.add(U),U}try{await G(async p=>await L(p))}catch(p){m({level:"error",event:"daemon.providers.docs_reconcile_failed",fields:{provider:e},error:p})}return m({level:"info",event:"daemon.providers.bundle_installed",fields:{provider:e,version:l,replaced:i}}),y.delete(b(e)),i?"updated":"installed"},{timeoutMs:X,timeoutError:a(()=>new c("provider_unavailable",`timed out waiting for another process replacing plugin "${e}"`),"timeoutError")})}catch(o){throw h(e)===void 0&&y.set(b(e),{status:"install_failed",error:Se(o)}),o instanceof c?o:(m({level:"error",event:"daemon.providers.bundle_install_failed",fields:{provider:e},error:o}),Ie(e))}}a(N,"refreshInstalled");async function nn(e){B(e),h(e)===void 0&&await N(e,!1,!0)}a(nn,"ensureProviderBundle");async function rn(e,n){return B(e),n.version!==void 0&&h(e)?.provider.major!==n.version?O(e,n.version,n.conditional):N(e,n.conditional)}a(rn,"refreshProviderBundle");async function ae(e,n,r){const{validatePluginArchive:t}=await oe(),o=await t(r);if(o.bundle===void 0)throw new M(`provider "${e}" major ${n} bundle is invalid: ${o.issues.map(i=>i.message).join("; ")}`,o.issues);const s=re(e,n,o.bundle);if(s!==void 0)throw new M(`provider "${e}" major ${n} bundle is invalid: ${s}`,[]);if(o.bundle.transport===void 0)throw new M(`provider "${e}" major ${n} bundle declares no transport`,[]);return o.bundle}a(ae,"validateArchive");function Me(e){T(e),x(d(e,"retired"),`${new Date().toISOString()}
2
+ `),$(d(e,"bundle.tar"),{force:!0}),$(d(e,"etag"),{force:!0})}a(Me,"retire");function ie(e,n){return new c("provider_version_retired",`provider "${e}" major ${n} is retired`,{param:"version"})}a(ie,"retiredError");async function O(e,n,r){const t=ee(e,n),o=d(t,"bundle.tar"),s=g(o),i=s&&r&&g(d(t,"etag"))?S(d(t,"etag"),"utf8"):void 0,f=await ne(e,{version:n,...i!==void 0?{ifNoneMatch:i}:{}});if(f.status===304)return"unchanged";if(f.status===410)throw Me(t),m({level:"info",event:"daemon.providers.major_retired",fields:{provider:e,major:n}}),ie(e,n);if(f.status===404)throw new c("not_found",`provider "${e}" never served major ${n}`,{param:"version"});if(f.status==="failed")throw new c("provider_unavailable",`provider "${e}" major ${n} bundle fetch failed: ${f.message}`,{cause:f.cause});try{await ae(e,n,f.raw)}catch(l){D(e,n,l);const w=new c(s?"provider_unavailable":"not_found",l instanceof Error?l.message:String(l),{param:"version"});throw I.add(w),w}T(t),$(d(t,"retired"),{force:!0});const u=s&&Buffer.from(f.raw).equals(S(o));return u||x(o,f.raw),f.etag!==void 0?x(d(t,"etag"),f.etag):$(d(t,"etag"),{force:!0}),u?"unchanged":s?"updated":"installed"}a(O,"refreshPinned");function ke(){const e=[],n=R();if(!g(n))return e;for(const r of W(n)){if(!Q.test(r))continue;let t;try{t=W(d(n,r))}catch{continue}for(const o of t){const s=/^v([1-9][0-9]*)$/.exec(o);s&&g(d(n,r,o,"bundle.tar"))&&e.push([r,Number(s[1])])}}return e}a(ke,"cachedMajors");function xe(e){const n=de(e,{transport:"http",reservedCoreNames:new Set});if(n.manifest===void 0)throw new c("not_found","the provider bundle's commands.json no longer validates");return n.manifest}a(xe,"readCommands");const _=new Map,H=new Map;function Te(e,n){const r=k(n),t=`${r.ino}:${r.mtimeMs}:${r.size}`,o=H.get(e);if(o?.stamp===t)return o.commands;const s=xe(JSON.parse(S(n,"utf8")));return H.set(e,{stamp:t,commands:s}),s}a(Te,"installedCommands");async function tn(e,n){B(e);const r=h(e);if(n===void 0||r?.provider.major===n){if(r===void 0)throw new c("not_found",`provider "${e}" has no installed bundle`,{param:"provider"});const l=v(e),w=Te(e,d(l,"commands.json"));return{id:e,major:r.provider.major,provider:r.provider,commands:w,transport:r.transport,source:"installed",root:l}}if(!Number.isInteger(n)||n<1)throw new c("invalid_request","version must be an integer of at least 1",{param:"version"});const t=ee(e,n);if(g(d(t,"retired")))throw ie(e,n);const o=d(t,"bundle.tar");g(o)||await O(e,n,!1);const s=`${e}/${n}`,i=k(o),f=`${i.mtimeMs}:${i.size}`;let u=_.get(s)?.stamp===f?_.get(s).bundle:void 0;if(u===void 0){try{u=await ae(e,n,new Uint8Array(S(o)))}catch(l){throw D(e,n,l),new c("not_found",`provider "${e}" has no valid bundle for major ${n}`,{param:"version"})}_.set(s,{stamp:f,bundle:u})}return{id:e,major:n,provider:u.provider,commands:u.commands,transport:u.transport.kind==="machine"?{kind:"machine"}:{kind:"http",endpoint:u.transport.endpoint},source:"pinned"}}a(tn,"resolveProviderBundle");let P=null,F=!1;function Ce(e,n){return e?.provider?.id===n&&e.bundle?.endpoint===`${E()}/providers/${encodeURIComponent(n)}/plugin`}a(Ce,"isDirectoryBundle");async function se(e){return Y(await j()).includes(e)}a(se,"grantNames");async function je(e){await Z(J(K(),e),async()=>{if(!(await se(e)||!Ce(C(v(e)),e))){$(v(e),{recursive:!0,force:!0}),await me("user",n=>{delete n.plugins[e]}),await ve("user",n=>{delete n.plugins[e]}),$(d(R(),e),{recursive:!0,force:!0}),H.delete(e),y.delete(b(e));for(const n of[..._.keys()])n.startsWith(`${e}/`)&&_.delete(n);try{await G(async n=>await L(n))}catch(n){m({level:"error",event:"daemon.providers.docs_reconcile_failed",fields:{provider:e},error:n})}m({level:"info",event:"daemon.providers.bundle_uninstalled",fields:{provider:e}})}},{timeoutMs:X,timeoutError:a(()=>new c("provider_unavailable",`timed out waiting for another process replacing plugin "${e}"`),"timeoutError")})}a(je,"uninstallProvider");async function Ae(){if(!(await j()).every(e=>e.status!=="active")){for(const e of be("user"))if(!(e.manifest.provider===void 0||await se(e.name)))try{await je(e.name)}catch(n){m({level:"error",event:"daemon.providers.bundle_uninstall_failed",fields:{provider:e.name},error:n})}}}a(Ae,"uninstallDisconnected");async function Re(){if(!q())return;const e=Y(await j());for(const n of e)try{await N(n,!0)}catch(r){r instanceof Object&&I.has(r)||te(n,void 0,r)}await Ae();for(const[n,r]of ke())try{await O(n,r,!0)}catch(t){if(t instanceof c&&t.code==="provider_version_retired"||t instanceof Object&&I.has(t))continue;te(n,r,t)}}a(Re,"syncOnce");function on(){return P!==null?(F=!0,P):(P=(async()=>{try{do F=!1,await Re();while(F)}finally{P=null}})(),P)}a(on,"syncProviderBundles");export{nn as ensureProviderBundle,Y as providerIdsFromGrants,Ee as providerInstallState,rn as refreshProviderBundle,tn as resolveProviderBundle,on as syncProviderBundles,Ie as unavailableProviderError};
@@ -1 +1 @@
1
- var ie=Object.defineProperty;var g=(e,r)=>ie(e,"name",{value:r,configurable:!0});import{validateDeclaredResult as J}from"../../api/command-manifest/result.js";import{callerAllows as ae,requireGroup as se}from"../../daemon/api/handlers/providers.js";import{terminalGrant as de}from"../grants/mirror.js";import{findRefusal as ue,payerOf as le,refusalError as pe,refusedMark as ce,writeCharge as fe}from"../ledger/index.js";import{listInstalledPlugins as me}from"../resolver.js";import{ProviderError as l}from"./errors.js";import{resolveProviderRef as ge}from"./roots.js";import{providerToolPath as ve}from"../plugin-objects-manifest.js";import{ensureProviderBundle as he,refreshProviderBundle as we,resolveProviderBundle as H,unavailableProviderError as ye}from"./bundles.js";import{prepareFileCall as be,resignForRetry as K,resolveFileResult as W}from"./files.js";import{consumeApproval as _e,requireProviderApproval as Ae}from"./approval.js";import{outboundToken as je}from"./mint.js";import{discardFile as ke}from"./token-cache.js";import{registerProviderCall as Pe}from"./inflight.js";import{buildRequestPlan as $e}from"../command-plugins/transport/http-plan.js";import{newTraceId as qe}from"../../shared/traceparent.js";import{sendProviderRequest as Ce,ProviderTransportError as Ee}from"./request.js";import{acceptProviderReceipt as Re}from"./receipts.js";import{writeProviderAudit as Ie}from"./audit.js";import{selectDevice as Te,sendDeviceCall as Me,deviceFailure as Se,deviceUnreachable as X,DEFAULT_DEVICE_REQUEST_MS as Be}from"./device.js";import{isLibsSearch as Y,wantsBundling as Ne,callLibsSearch as Oe,bundleExec as Fe,mapLibOutcome as De,reportLibCalls as Ue}from"./device-libs.js";import{directoryIssuer as Le,ownMachineId as Je}from"../peer/directory.js";import{formatCrtrCaller as Z}from"../command-plugins/caller-header.js";import{getNode as He}from"../canvas/canvas.js";import{envelopeForProviderCaller as Ge}from"../peer/envelope.js";const xe="app:terminal";function ee(e){const r=[],i=g((c,t,n)=>{const a=[...t,c.name],s=n||c.tier==="hidden";if(c.kind==="branch"){for(const v of c.children)i(v,a,s);return}const j=ve(e.roots.length,a);r.push({tool:j,leaf:c,hidden:s})},"walk");for(const c of e.roots)i(c,[],!1);return r}g(ee,"providerLeaves");function re(e){if(e.kind!=="branch")return null;const r=[];for(const h of e.children)if(h.kind==="branch"){const _=re(h);_&&r.push(_)}else if("group"in h&&h.group){const{kind:_,name:d,description:A,whenToUse:w,tier:y,summary:k,params:P,output:o,effects:p,group:m}=h;r.push({kind:_,name:d,description:A,whenToUse:w,...y===void 0?{}:{tier:y},summary:k,params:P,output:o,effects:p,group:m})}if(r.length===0)return null;const{kind:i,name:c,description:t,whenToUse:n,tier:a,summary:s,model:j,rootEntry:v}=e;return{kind:i,name:c,description:t,whenToUse:n,...a===void 0?{}:{tier:a},summary:s,...j===void 0?{}:{model:j},...v===void 0?{}:{rootEntry:v},children:r}}g(re,"providerHelpBranch");async function Pr(e,r){const i=await H(r),c=ee(i.commands),t=me("user").find(n=>n.name===r)?.manifest.description??r;return{provider:r,status:"available",major:i.major,display_name:t,transport:i.transport.kind,groups:i.provider.groups.filter(n=>ae(e,`${r}:${n.group}`)).map(n=>({group:n.group,leaves:c.filter(({leaf:a,hidden:s})=>!s&&a.group===n.group).map(({tool:a,leaf:s})=>({tool:a,description:s.description,summary:s.summary,params:s.params,output:s.output}))})),commands:i.commands.roots.flatMap(n=>{const a=re(n);return a?[a]:[]})}}g(Pr,"providerTools");async function ze(e){return await Re(e.headers["crtr-receipt"],e)}g(ze,"onProviderResponse");async function Qe(e){if(e.class==="person"){const r=(await de())?.sub;if(!r)throw new l("grant_revoked","This machine has no active Northlight grant for your terminal. Run `crtr sys join` again from /account/machines.");return{sub:r,grantee:r}}if(!e.sub||!e.grantee)throw new l("grant_revoked","the caller has no active grant");return{sub:e.sub,grantee:e.grantee}}g(Qe,"identity");function Ve(e,r){return[`${e.id}:${r.group}`]}g(Ve,"callScope");function Ke(e,r){const i=ee(e.commands).find(c=>c.tool===r);if(!i)throw new l("not_found",`tool "${r}" is not in provider "${e.id}"`,{param:"tool"});if(!i.leaf.group)throw new l("not_found",`tool "${r}" has no group`,{param:"tool"});return i.leaf}g(Ke,"leafFor");function G(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const r=e.error;return!r||typeof r!="object"||Array.isArray(r)?{}:r}g(G,"errorBody");function We(e){const r=Array.isArray(e)?e[0]:e;if(!r)return;const i=Number(r);if(Number.isFinite(i)&&i>=0)return i;const c=Date.parse(r);return Number.isFinite(c)?Math.max(0,Math.ceil((c-Date.now())/1e3)):void 0}g(We,"retryAfter");function N(e,r,i,c=!1){const t=G(r),n=typeof t.code=="string"?t.code:void 0,a=typeof t.message=="string"?t.message:`provider answered HTTP ${e}`,s=typeof t.field=="string"?t.field:void 0,v={origin:"provider",details:{code:n??null,message:a,...s?{field:s}:{},...typeof t.next=="string"?{next:t.next}:{},...t.received!==void 0?{received:t.received}:{},status:e},...s?{param:s}:{}};return e===429?new l("provider_rate_limited",a,{...v,retry_after_s:We(i["retry-after"])}):e===401||e>=500?new l("provider_unavailable",a,v):e===403&&n==="provider_refused"?new l("provider_refused",a,v):e===403&&n==="approval_required"?new l("provider_error",c?"provider asked for approval again":"provider approval question is invalid",{...v,status:e}):new l("provider_error",a,{...v,status:e})}g(N,"mapProviderResponse");function te(e,r){return JSON.stringify({major:e.major,endpoint:e.transport.kind==="http"?e.transport.endpoint:null,baseUrl:e.commands.baseUrl??null,rest:r.rest,params:r.params,output:r.output})}g(te,"approvalShape");async function Xe(e){const r=e.origin!==void 0?Z({node:e.origin.node??void 0,run:e.origin.run??void 0,machine:e.origin.machine}):Z({node:e.node_id??void 0,run:e.run_id??void 0,profile:e.node_id?(await He(e.node_id))?.profile_id??void 0:void 0,machine:Le()!==null?Je():void 0});return r===""?{}:{"Crtr-Caller":r}}g(Xe,"crtrCallerHeader");async function $r(e,r,i){const c=ge(r.provider);if(c===null)throw new l("not_found",`provider "${r.provider}" is not available`,{param:"provider"});const t={...r,provider:c},n=new AbortController,a=g(()=>n.abort(i.signal.reason),"abort");i.signal.aborted?a():i.signal.addEventListener("abort",a,{once:!0});let s=i.requestId;const j=i.traceId??qe(),v=Pe({request_id:s,run_id:e.run_id,node_id:e.node_id,controller:n});let h,_,d,A,w,y,k,P="allowed";try{try{await he(t.provider)}catch{throw ye(t.provider)}let o=await H(t.provider,t.version),p=!1,m,O,$,x=!1,z=!1,F,Q=0;for(;;){const b=Ke(o,t.tool);if(d=`${o.id}:${b.group}`,se(e,o.id,b.group),o.transport.kind!=="machine"&&t.machine!==void 0)throw new l("invalid_argument",`provider "${o.id}" is not served by a device, so it takes no machine`,{param:"machine"});o.transport.kind==="machine"&&F===void 0&&(F=await Te(t.machine,void 0,{newest:Y(o.major,t.tool)}),Q=Date.now()+(o.commands.timeouts?.requestMs??Be));let D=null;if(t.approval&&!x){const u=await _e(e,t,te(o,b));$=u.option,D=u.prepared,k=t.approval,s=u.requestId,x=!0}const I={params:b.params.filter(u=>"type"in u&&u.type==="file").map(u=>u.name),output:b.output.find(u=>u.type==="file")?.name??null},V=JSON.stringify(I);if(O!==void 0&&O!==V)throw new l("provider_bad_response","the refreshed manifest changed the tool file contract",{origin:"provider",details:{status:409,message:"manifest_stale changed the tool file contract"}});O=V,D&&(m=await K(e,I,t.arguments,D,{signal:n.signal})),m??=await be(e,I,t.arguments,{signal:n.signal});const{sub:q,grantee:T}=await Qe(e);if(o.transport.kind==="machine"){const u=await Ye(e,o,t,b,m,{sub:q,requestId:s,deadlineAt:Q,signal:n.signal,machine:F});return y=u.responder,h=u.cleanup,{status:200,body:u.result}}const C=await je({audience:o.transport.endpoint,sub:q,grantee:T,initiating_app:T,scope:Ve(o,b)},{signal:n.signal});A=C.jti;const U=e.class==="person"?xe:T,E=le({call:"tool",tokenPayer:C.payer,sub:q}),L=await ue({scope:"call",payer:E.payer,funding:E.funding,sub:q,grantee:U,provider:`app:${o.id}`});if(L!==null)throw await fe({request:s,kind:"tool",what:`${o.id}:${t.tool}`,usd:0,payer:E.payer,funding:E.funding,sub:q,grantee:U,node:e.node_id,run:e.run_id,provider:`app:${o.id}`,causedBy:i.causedBy??null,refused:ce(L)}),pe(L);const oe={registration:{name:o.id,endpoint:o.transport.endpoint},...o.commands.baseUrl?{baseUrl:o.commands.baseUrl}:{},...o.commands.timeouts?{timeouts:o.commands.timeouts}:{},params:b.params,rest:b.rest,output:b.output,commandPath:[o.id,...t.tool.split(" ")]},ne=$e(oe,m.arguments,void 0,{authorization:`Bearer ${C.access_token}`,requestId:s,traceId:j,fileMode:"pass-through",extraHeaders:{...i.causedBy?{"Crtr-Caused-By":i.causedBy}:{},...e.run_id?{"Crtr-Run-Id":e.run_id}:{},...await Xe(e),...$?{"Crtr-Approval":$}:{},...m.headers}});_??=Date.now()+(o.commands.timeouts?.requestMs??3e4);let f;try{if(f=await Ce(ne,{connectMs:o.commands.timeouts?.connectMs,requestMs:o.commands.timeouts?.requestMs,deadlineAt:_,signal:n.signal}),n.signal.aborted)throw n.signal.reason??new Error("provider call aborted")}catch(u){throw n.signal.aborted?n.signal.reason??u:u instanceof Ee?new l("provider_unavailable",u.message,{origin:"provider",details:{status:null,code:u.kind}}):u}if(w??=await ze({provider:o.id,tool:t.tool,request_id:s,run_id:e.run_id,sub:q,grantee:T,payer:C.payer,ledger_grantee:U,funding:E.funding,node_id:e.node_id,caused_by:i.causedBy??null,jti:C.jti,status:f.status,headers:f.headers}),y=f.headers["crtr-responder"],f.status===401&&ke(C.key),f.status>=200&&f.status<300){const u=f.body;if(!u||typeof u!="object"||Array.isArray(u))throw new l("provider_bad_response","provider result is not a JSON object",{origin:"provider",details:{status:f.status,message:"provider result is not a JSON object"}});let S;try{S=await W(e,m,u,{signal:n.signal})}catch(R){throw R instanceof l&&R.code==="provider_error"?new l("provider_bad_response",R.message,{origin:"provider",details:{status:f.status,message:R.message}}):R}h=S.cleanup;const B=J(b.output,S.result);if(B)throw new l("provider_bad_response",`provider result has an invalid output field "${B.field}"`,{origin:"provider",param:B.field,details:{status:f.status,...B}});if(n.signal.aborted)throw n.signal.reason??new Error("provider call aborted");return{status:200,body:S.result}}const M=G(f.body);if(f.status===403&&M.code==="approval_required"&&!$&&!z){z=!0;const u=await Ae(e,t,s,M.question,n.signal,te(o,b),m);if(k=u.questionId,$=u.option,m=await K(e,I,t.arguments,m,{signal:n.signal}),_=void 0,n.signal.aborted)throw new l("user_denied","the approval wait was canceled");continue}if(f.status===403&&M.code==="approval_required"&&$)throw N(f.status,f.body,f.headers,!0);if(f.status!==401&&M.manifest_stale===!0&&!p){p=!0,await we(o.id,{conditional:!1,...t.version===void 0?{}:{version:t.version}}),o=await H(o.id,t.version);continue}throw N(f.status,f.body,f.headers,!!$)}}catch(o){throw o instanceof l&&o.question_id&&(k=o.question_id),y===void 0&&o instanceof l&&typeof o.responder=="string"&&(y=o.responder),P=`refused:${o instanceof l?o.code:"server_error"}`,await h?.(),o}finally{try{await Ie({request_id:s,caller:e,provider:t.provider,tool:t.tool,scope_checked:d,outcome:P,jti:A,receipt_id:w,responder:y,question_id:k})}finally{v()}i.signal.removeEventListener("abort",a)}}g($r,"callProvider");async function Ye(e,r,i,c,t,n){const{machine:a}=n,s=await Ge(e,n.sub),j={machine:a,envelope:s,signal:n.signal,deadlineAt:n.deadlineAt,mapFailure:N};if(Y(r.major,i.tool)){const p=await Oe(j,t.arguments,r.major),m=J(c.output,p);if(m)throw new l("provider_bad_response",`the libraries result has an invalid output field "${m.field}"`,{origin:"provider",param:m.field,details:{status:200,...m}});return{result:p,responder:void 0,cleanup:void 0}}let v=t.arguments,h=null,_="";if(Ne(r.major,i.tool,t.arguments)){const p=await Fe(j,t.arguments,r.major);if(n.deadlineAt-Date.now()<=0)throw X(a,"timeout","");p!==null&&(v=p.arguments,h=p.services,_=p.release)}const d=await Me(a,{tool:i.tool,arguments:v,version:r.major,requestId:n.requestId,runId:e.run_id,deadlineAt:n.deadlineAt,upload:t.upload===null?null:{id:t.upload.upload_id,link:t.upload.upload_link}},s,n.signal);if(n.signal.aborted)throw n.signal.reason??new Error("provider call aborted");if("unreachable"in d)throw X(a,d.unreachable.reason,d.unreachable.message);const A=d.headers["crtr-responder"],w=G(d.body);if(d.status<200||d.status>=300){const p=Se(a,d.status,typeof w.code=="string"?w.code:void 0,typeof w.message=="string"?w.message:void 0,d.body),m=p===null&&d.status>=500&&typeof w.code=="string"?new l("provider_error",typeof w.message=="string"?w.message:`the device answered HTTP ${d.status}`,{origin:"runtime",status:d.status,details:{code:w.code,status:d.status}}):null;throw Object.assign(p??m??N(d.status,d.body,d.headers),{responder:A})}const y=d.body&&typeof d.body=="object"&&!Array.isArray(d.body)?d.body.result:void 0;if(!y||typeof y!="object"||Array.isArray(y))throw Object.assign(new l("provider_bad_response","the device result is not a JSON object",{origin:"provider",details:{status:d.status,message:"the device result is not a JSON object"}}),{responder:A});let k=y;if(h!==null){const p=De(k,h);if(Ue(a,s,_,p.calls),"error"in p)throw Object.assign(p.error,{responder:A});k=p.result}let P;try{P=await W(e,t,k,{signal:n.signal})}catch(p){throw p instanceof l&&p.code==="provider_error"?Object.assign(new l("provider_bad_response",p.message,{origin:"provider",details:{status:d.status,message:p.message}}),{responder:A}):p}const o=J(c.output,P.result);if(o)throw await P.cleanup?.(),Object.assign(new l("provider_bad_response",`the device result has an invalid output field "${o.field}"`,{origin:"provider",param:o.field,details:{status:d.status,...o}}),{responder:A});return{result:P.result,responder:A,cleanup:P.cleanup}}g(Ye,"callDevice");export{$r as callProvider,Xe as crtrCallerHeader,N as mapProviderResponse,ze as onProviderResponse,ee as providerLeaves,Pr as providerTools};
1
+ var ie=Object.defineProperty;var g=(e,r)=>ie(e,"name",{value:r,configurable:!0});import{validateDeclaredResult as J}from"../../api/command-manifest/result.js";import{callerAllows as ae,requireGroup as se}from"../../daemon/api/handlers/providers.js";import{terminalGrant as de}from"../grants/mirror.js";import{findRefusal as ue,payerOf as le,refusalError as pe,refusedMark as ce,writeCharge as fe}from"../ledger/index.js";import{listInstalledPlugins as me}from"../resolver.js";import{ProviderError as l}from"./errors.js";import{resolveProviderRef as ge}from"./roots.js";import{providerToolPath as ve}from"../plugin-objects-manifest.js";import{ensureProviderBundle as he,refreshProviderBundle as we,resolveProviderBundle as H,unavailableProviderError as ye}from"./bundles.js";import{prepareFileCall as be,resignForRetry as K,resolveFileResult as W}from"./files.js";import{consumeApproval as _e,requireProviderApproval as Ae}from"./approval.js";import{outboundToken as je}from"./mint.js";import{discardFile as ke}from"./token-cache.js";import{registerProviderCall as Pe}from"./inflight.js";import{buildRequestPlan as $e}from"../command-plugins/transport/http-plan.js";import{newTraceId as qe}from"../../shared/traceparent.js";import{sendProviderRequest as Ce,ProviderTransportError as Ee}from"./request.js";import{acceptProviderReceipt as Re}from"./receipts.js";import{writeProviderAudit as Ie}from"./audit.js";import{selectDevice as Te,sendDeviceCall as Me,deviceFailure as Se,deviceUnreachable as X,DEFAULT_DEVICE_REQUEST_MS as Be}from"./device.js";import{isLibsSearch as Y,wantsBundling as Ne,callLibsSearch as Oe,bundleExec as Fe,mapLibOutcome as De,reportLibCalls as Ue}from"./device-libs.js";import{ownMachineId as Le}from"../peer/directory.js";import{directoryIssuer as Je}from"../directory/issuer.js";import{formatCrtrCaller as Z}from"../command-plugins/caller-header.js";import{getNode as He}from"../canvas/canvas.js";import{envelopeForProviderCaller as Ge}from"../peer/envelope.js";const xe="app:terminal";function ee(e){const r=[],i=g((c,t,n)=>{const a=[...t,c.name],s=n||c.tier==="hidden";if(c.kind==="branch"){for(const v of c.children)i(v,a,s);return}const j=ve(e.roots.length,a);r.push({tool:j,leaf:c,hidden:s})},"walk");for(const c of e.roots)i(c,[],!1);return r}g(ee,"providerLeaves");function re(e){if(e.kind!=="branch")return null;const r=[];for(const h of e.children)if(h.kind==="branch"){const _=re(h);_&&r.push(_)}else if("group"in h&&h.group){const{kind:_,name:d,description:A,whenToUse:w,tier:y,summary:k,params:P,output:o,effects:p,group:m}=h;r.push({kind:_,name:d,description:A,whenToUse:w,...y===void 0?{}:{tier:y},summary:k,params:P,output:o,effects:p,group:m})}if(r.length===0)return null;const{kind:i,name:c,description:t,whenToUse:n,tier:a,summary:s,model:j,rootEntry:v}=e;return{kind:i,name:c,description:t,whenToUse:n,...a===void 0?{}:{tier:a},summary:s,...j===void 0?{}:{model:j},...v===void 0?{}:{rootEntry:v},children:r}}g(re,"providerHelpBranch");async function $r(e,r){const i=await H(r),c=ee(i.commands),t=me("user").find(n=>n.name===r)?.manifest.description??r;return{provider:r,status:"available",major:i.major,display_name:t,transport:i.transport.kind,groups:i.provider.groups.filter(n=>ae(e,`${r}:${n.group}`)).map(n=>({group:n.group,leaves:c.filter(({leaf:a,hidden:s})=>!s&&a.group===n.group).map(({tool:a,leaf:s})=>({tool:a,description:s.description,summary:s.summary,params:s.params,output:s.output}))})),commands:i.commands.roots.flatMap(n=>{const a=re(n);return a?[a]:[]})}}g($r,"providerTools");async function ze(e){return await Re(e.headers["crtr-receipt"],e)}g(ze,"onProviderResponse");async function Qe(e){if(e.class==="person"){const r=(await de())?.sub;if(!r)throw new l("grant_revoked","This machine has no active Northlight grant for your terminal. Run `crtr sys join` again from /account/machines.");return{sub:r,grantee:r}}if(!e.sub||!e.grantee)throw new l("grant_revoked","the caller has no active grant");return{sub:e.sub,grantee:e.grantee}}g(Qe,"identity");function Ve(e,r){return[`${e.id}:${r.group}`]}g(Ve,"callScope");function Ke(e,r){const i=ee(e.commands).find(c=>c.tool===r);if(!i)throw new l("not_found",`tool "${r}" is not in provider "${e.id}"`,{param:"tool"});if(!i.leaf.group)throw new l("not_found",`tool "${r}" has no group`,{param:"tool"});return i.leaf}g(Ke,"leafFor");function G(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const r=e.error;return!r||typeof r!="object"||Array.isArray(r)?{}:r}g(G,"errorBody");function We(e){const r=Array.isArray(e)?e[0]:e;if(!r)return;const i=Number(r);if(Number.isFinite(i)&&i>=0)return i;const c=Date.parse(r);return Number.isFinite(c)?Math.max(0,Math.ceil((c-Date.now())/1e3)):void 0}g(We,"retryAfter");function N(e,r,i,c=!1){const t=G(r),n=typeof t.code=="string"?t.code:void 0,a=typeof t.message=="string"?t.message:`provider answered HTTP ${e}`,s=typeof t.field=="string"?t.field:void 0,v={origin:"provider",details:{code:n??null,message:a,...s?{field:s}:{},...typeof t.next=="string"?{next:t.next}:{},...t.received!==void 0?{received:t.received}:{},status:e},...s?{param:s}:{}};return e===429?new l("provider_rate_limited",a,{...v,retry_after_s:We(i["retry-after"])}):e===401||e>=500?new l("provider_unavailable",a,v):e===403&&n==="provider_refused"?new l("provider_refused",a,v):e===403&&n==="approval_required"?new l("provider_error",c?"provider asked for approval again":"provider approval question is invalid",{...v,status:e}):new l("provider_error",a,{...v,status:e})}g(N,"mapProviderResponse");function te(e,r){return JSON.stringify({major:e.major,endpoint:e.transport.kind==="http"?e.transport.endpoint:null,baseUrl:e.commands.baseUrl??null,rest:r.rest,params:r.params,output:r.output})}g(te,"approvalShape");async function Xe(e){const r=e.origin!==void 0?Z({node:e.origin.node??void 0,run:e.origin.run??void 0,machine:e.origin.machine}):Z({node:e.node_id??void 0,run:e.run_id??void 0,profile:e.node_id?(await He(e.node_id))?.profile_id??void 0:void 0,machine:Je()!==null?Le():void 0});return r===""?{}:{"Crtr-Caller":r}}g(Xe,"crtrCallerHeader");async function qr(e,r,i){const c=ge(r.provider);if(c===null)throw new l("not_found",`provider "${r.provider}" is not available`,{param:"provider"});const t={...r,provider:c},n=new AbortController,a=g(()=>n.abort(i.signal.reason),"abort");i.signal.aborted?a():i.signal.addEventListener("abort",a,{once:!0});let s=i.requestId;const j=i.traceId??qe(),v=Pe({request_id:s,run_id:e.run_id,node_id:e.node_id,controller:n});let h,_,d,A,w,y,k,P="allowed";try{try{await he(t.provider)}catch{throw ye(t.provider)}let o=await H(t.provider,t.version),p=!1,m,O,$,x=!1,z=!1,F,Q=0;for(;;){const b=Ke(o,t.tool);if(d=`${o.id}:${b.group}`,se(e,o.id,b.group),o.transport.kind!=="machine"&&t.machine!==void 0)throw new l("invalid_argument",`provider "${o.id}" is not served by a device, so it takes no machine`,{param:"machine"});o.transport.kind==="machine"&&F===void 0&&(F=await Te(t.machine,void 0,{newest:Y(o.major,t.tool)}),Q=Date.now()+(o.commands.timeouts?.requestMs??Be));let D=null;if(t.approval&&!x){const u=await _e(e,t,te(o,b));$=u.option,D=u.prepared,k=t.approval,s=u.requestId,x=!0}const I={params:b.params.filter(u=>"type"in u&&u.type==="file").map(u=>u.name),output:b.output.find(u=>u.type==="file")?.name??null},V=JSON.stringify(I);if(O!==void 0&&O!==V)throw new l("provider_bad_response","the refreshed manifest changed the tool file contract",{origin:"provider",details:{status:409,message:"manifest_stale changed the tool file contract"}});O=V,D&&(m=await K(e,I,t.arguments,D,{signal:n.signal})),m??=await be(e,I,t.arguments,{signal:n.signal});const{sub:q,grantee:T}=await Qe(e);if(o.transport.kind==="machine"){const u=await Ye(e,o,t,b,m,{sub:q,requestId:s,deadlineAt:Q,signal:n.signal,machine:F});return y=u.responder,h=u.cleanup,{status:200,body:u.result}}const C=await je({audience:o.transport.endpoint,sub:q,grantee:T,initiating_app:T,scope:Ve(o,b)},{signal:n.signal});A=C.jti;const U=e.class==="person"?xe:T,E=le({call:"tool",tokenPayer:C.payer,sub:q}),L=await ue({scope:"call",payer:E.payer,funding:E.funding,sub:q,grantee:U,provider:`app:${o.id}`});if(L!==null)throw await fe({request:s,kind:"tool",what:`${o.id}:${t.tool}`,usd:0,payer:E.payer,funding:E.funding,sub:q,grantee:U,node:e.node_id,run:e.run_id,provider:`app:${o.id}`,causedBy:i.causedBy??null,refused:ce(L)}),pe(L);const oe={registration:{name:o.id,endpoint:o.transport.endpoint},...o.commands.baseUrl?{baseUrl:o.commands.baseUrl}:{},...o.commands.timeouts?{timeouts:o.commands.timeouts}:{},params:b.params,rest:b.rest,output:b.output,commandPath:[o.id,...t.tool.split(" ")]},ne=$e(oe,m.arguments,void 0,{authorization:`Bearer ${C.access_token}`,requestId:s,traceId:j,fileMode:"pass-through",extraHeaders:{...i.causedBy?{"Crtr-Caused-By":i.causedBy}:{},...e.run_id?{"Crtr-Run-Id":e.run_id}:{},...await Xe(e),...$?{"Crtr-Approval":$}:{},...m.headers}});_??=Date.now()+(o.commands.timeouts?.requestMs??3e4);let f;try{if(f=await Ce(ne,{connectMs:o.commands.timeouts?.connectMs,requestMs:o.commands.timeouts?.requestMs,deadlineAt:_,signal:n.signal}),n.signal.aborted)throw n.signal.reason??new Error("provider call aborted")}catch(u){throw n.signal.aborted?n.signal.reason??u:u instanceof Ee?new l("provider_unavailable",u.message,{origin:"provider",details:{status:null,code:u.kind}}):u}if(w??=await ze({provider:o.id,tool:t.tool,request_id:s,run_id:e.run_id,sub:q,grantee:T,payer:C.payer,ledger_grantee:U,funding:E.funding,node_id:e.node_id,caused_by:i.causedBy??null,jti:C.jti,status:f.status,headers:f.headers}),y=f.headers["crtr-responder"],f.status===401&&ke(C.key),f.status>=200&&f.status<300){const u=f.body;if(!u||typeof u!="object"||Array.isArray(u))throw new l("provider_bad_response","provider result is not a JSON object",{origin:"provider",details:{status:f.status,message:"provider result is not a JSON object"}});let S;try{S=await W(e,m,u,{signal:n.signal})}catch(R){throw R instanceof l&&R.code==="provider_error"?new l("provider_bad_response",R.message,{origin:"provider",details:{status:f.status,message:R.message}}):R}h=S.cleanup;const B=J(b.output,S.result);if(B)throw new l("provider_bad_response",`provider result has an invalid output field "${B.field}"`,{origin:"provider",param:B.field,details:{status:f.status,...B}});if(n.signal.aborted)throw n.signal.reason??new Error("provider call aborted");return{status:200,body:S.result}}const M=G(f.body);if(f.status===403&&M.code==="approval_required"&&!$&&!z){z=!0;const u=await Ae(e,t,s,M.question,n.signal,te(o,b),m);if(k=u.questionId,$=u.option,m=await K(e,I,t.arguments,m,{signal:n.signal}),_=void 0,n.signal.aborted)throw new l("user_denied","the approval wait was canceled");continue}if(f.status===403&&M.code==="approval_required"&&$)throw N(f.status,f.body,f.headers,!0);if(f.status!==401&&M.manifest_stale===!0&&!p){p=!0,await we(o.id,{conditional:!1,...t.version===void 0?{}:{version:t.version}}),o=await H(o.id,t.version);continue}throw N(f.status,f.body,f.headers,!!$)}}catch(o){throw o instanceof l&&o.question_id&&(k=o.question_id),y===void 0&&o instanceof l&&typeof o.responder=="string"&&(y=o.responder),P=`refused:${o instanceof l?o.code:"server_error"}`,await h?.(),o}finally{try{await Ie({request_id:s,caller:e,provider:t.provider,tool:t.tool,scope_checked:d,outcome:P,jti:A,receipt_id:w,responder:y,question_id:k})}finally{v()}i.signal.removeEventListener("abort",a)}}g(qr,"callProvider");async function Ye(e,r,i,c,t,n){const{machine:a}=n,s=await Ge(e,n.sub),j={machine:a,envelope:s,signal:n.signal,deadlineAt:n.deadlineAt,mapFailure:N};if(Y(r.major,i.tool)){const p=await Oe(j,t.arguments,r.major),m=J(c.output,p);if(m)throw new l("provider_bad_response",`the libraries result has an invalid output field "${m.field}"`,{origin:"provider",param:m.field,details:{status:200,...m}});return{result:p,responder:void 0,cleanup:void 0}}let v=t.arguments,h=null,_="";if(Ne(r.major,i.tool,t.arguments)){const p=await Fe(j,t.arguments,r.major);if(n.deadlineAt-Date.now()<=0)throw X(a,"timeout","");p!==null&&(v=p.arguments,h=p.services,_=p.release)}const d=await Me(a,{tool:i.tool,arguments:v,version:r.major,requestId:n.requestId,runId:e.run_id,deadlineAt:n.deadlineAt,upload:t.upload===null?null:{id:t.upload.upload_id,link:t.upload.upload_link}},s,n.signal);if(n.signal.aborted)throw n.signal.reason??new Error("provider call aborted");if("unreachable"in d)throw X(a,d.unreachable.reason,d.unreachable.message);const A=d.headers["crtr-responder"],w=G(d.body);if(d.status<200||d.status>=300){const p=Se(a,d.status,typeof w.code=="string"?w.code:void 0,typeof w.message=="string"?w.message:void 0,d.body),m=p===null&&d.status>=500&&typeof w.code=="string"?new l("provider_error",typeof w.message=="string"?w.message:`the device answered HTTP ${d.status}`,{origin:"runtime",status:d.status,details:{code:w.code,status:d.status}}):null;throw Object.assign(p??m??N(d.status,d.body,d.headers),{responder:A})}const y=d.body&&typeof d.body=="object"&&!Array.isArray(d.body)?d.body.result:void 0;if(!y||typeof y!="object"||Array.isArray(y))throw Object.assign(new l("provider_bad_response","the device result is not a JSON object",{origin:"provider",details:{status:d.status,message:"the device result is not a JSON object"}}),{responder:A});let k=y;if(h!==null){const p=De(k,h);if(Ue(a,s,_,p.calls),"error"in p)throw Object.assign(p.error,{responder:A});k=p.result}let P;try{P=await W(e,t,k,{signal:n.signal})}catch(p){throw p instanceof l&&p.code==="provider_error"?Object.assign(new l("provider_bad_response",p.message,{origin:"provider",details:{status:d.status,message:p.message}}),{responder:A}):p}const o=J(c.output,P.result);if(o)throw await P.cleanup?.(),Object.assign(new l("provider_bad_response",`the device result has an invalid output field "${o.field}"`,{origin:"provider",param:o.field,details:{status:d.status,...o}}),{responder:A});return{result:P.result,responder:A,cleanup:P.cleanup}}g(Ye,"callDevice");export{qr as callProvider,Xe as crtrCallerHeader,N as mapProviderResponse,ze as onProviderResponse,ee as providerLeaves,$r as providerTools};
@@ -7,7 +7,7 @@ export declare const MINT_TIMEOUT_MS = 10000;
7
7
  /** Waits before the second and third attempt at a token endpoint that answered 502, 503 or 504. */
8
8
  export declare const MINT_RETRY_DELAYS_MS: number[];
9
9
  export interface MintOptions {
10
- /** The directory issuer; default the configured `runtime.issuer`. */
10
+ /** The directory issuer; default `directoryIssuer()`. */
11
11
  issuer?: string;
12
12
  /** The caller's signal: an abort rethrows its reason, not a mapped error. */
13
13
  signal?: AbortSignal;
@@ -1 +1 @@
1
- var E=Object.defineProperty;var c=(r,e)=>E(r,"name",{value:e,configurable:!0});import{createHash as M}from"node:crypto";import{readConfig as j}from"../config.js";import{emitEvent as S}from"../events/emit.js";import{OutboundError as A}from"../events/outbound.js";import{grantSyncStateFor as N}from"../grants/mirror.js";import{assertion as O,machineRevokedMessage as P,requestGrantSync as I,runtimeClient as L}from"../grants/sync.js";import{isUnsandboxedMachine as D}from"../runtime/unsandboxed.js";import{ProviderError as h}from"./errors.js";import{cacheKey as F,cachedToken as G,discardEpoch as U,storeToken as C}from"./token-cache.js";import{discardFile as le}from"./token-cache.js";const J=1e4,Y=[300,900];function B(r){const e=r.issuer??j("user").runtime?.issuer;if(!e)throw new h("not_found","provider calls need a directory-connected runtime");return e.replace(/\/$/,"")}c(B,"issuerOf");function g(r,e,t){return new h(r,e,{origin:"directory",details:t})}c(g,"directoryError");function H(r,e,t,i){const n=typeof e.error=="string"?e.error:null,s=typeof e.error_description=="string"?e.error_description:void 0,o={directory_status:r,...n?{directory_error:n}:{},...s?{directory_description:s}:{}},d=s?`: ${s}`:"";switch(n){case"invalid_client":return D()?{error:g("machine_revoked",P(i),o),grantRefusal:!1}:{error:g("server_error",`the directory refused the outbound token request (${n})${d}`,o),grantRefusal:!1};case"invalid_request":return{error:g("server_error",`the directory refused the outbound token request (${n})${d}`,o),grantRefusal:!1};case"invalid_target":return{error:g("not_found",`the directory knows no provider at ${t.audience}${d}`,o),grantRefusal:!1};case"invalid_grant":return{error:g("grant_revoked",`the directory has no active grant for ${t.grantee}${d}`,o),grantRefusal:!0};case"provider_not_connected":return{error:g("provider_not_connected",s??"The user has not connected this provider.",o),grantRefusal:!1};case"invalid_scope":{const a=typeof e.scope=="string"&&e.scope?e.scope:t.scope.length===1?t.scope[0]:void 0,l=a?`missing scope ${a}`:`the grant does not cover ${t.scope.join(" ")}`;return{error:new h("scope_missing",l,{origin:"directory",details:o,...a?{scope:a}:{}}),grantRefusal:!0}}default:return{error:g("server_error",`the directory token endpoint answered ${r}${n?` ${n}`:""}${d}`,o),grantRefusal:!1}}}c(H,"refusal");function K(r){try{return JSON.parse(Buffer.from(r.split(".")[1]??"","base64url").toString("utf8"))}catch{return{}}}c(K,"claims");function z(r){const e=r.grant_rev,t=r.grant_status;return typeof e!="number"||!Number.isInteger(e)||e<0||typeof t!="string"?null:{rev:e,status:t}}c(z,"refusedGrantState");function Q(r,e){const t=c(i=>i.status==="active"?0:1,"phase");return r.rev>e.rev||r.rev===e.rev&&t(r)>t(e)}c(Q,"newer");async function V(r,e,t){if(!e)return;const i=await N(r.grantee,r.sub)??{rev:0,status:"removed"};if(!Q(e,i))return;const n=t.syncGrants??I;Promise.resolve().then(()=>n({fresh:!0})).catch(()=>{})}c(V,"syncIfNewer");async function W(r,e={}){const t=B(e),i=`${t}/token`,n=L(),s=c(async()=>{const u=new URLSearchParams({grant_type:"client_credentials",client_id:n.client_id,client_assertion_type:"urn:ietf:params:oauth:client-assertion-type:jwt-bearer",client_assertion:O(n,i),audience:r.audience,sub:r.sub,grantee:r.grantee,scope:[...new Set(r.scope)].sort().join(" "),initiating_app:r.initiating_app}),p=AbortSignal.timeout(J),w=e.signal?AbortSignal.any([e.signal,p]):p,$=await(e.fetch??fetch)(i,{method:"POST",body:u,signal:w,headers:{Accept:"application/json"}});return{response:$,text:await $.text()}},"attempt");let o,d;try{for(let u=0;;u++){({response:o,text:d}=await s());const p=Y[u];if(p===void 0||![502,503,504].includes(o.status))break;await new Promise(w=>setTimeout(w,p))}}catch(u){throw e.signal?.aborted?e.signal.reason:new h("server_error",`the directory token endpoint could not be reached: ${u instanceof Error?u.message:String(u)}`,{origin:"directory",cause:new A("directory_token",i,u)})}let a=null;try{a=d?JSON.parse(d):null}catch{a=null}const l=a&&typeof a=="object"&&!Array.isArray(a)?a:{};if(!o.ok){const{error:u,grantRefusal:p}=H(o.status,l,r,t);throw u.code==="machine_revoked"&&S({level:"error",event:"daemon.grants.machine_revoked",fields:{audience:r.audience}}),S({level:"info",event:"daemon.providers.mint_refused",fields:{audience:r.audience,grantee:r.grantee,code:u.code,directory_error:typeof l.error=="string"?l.error:null,status:o.status}}),p&&await V(r,z(l),e),u}const m=l.access_token;if(typeof m!="string"||!m)throw new h("server_error","the directory token response has no access_token",{origin:"directory"});const f=K(m),x=Math.floor((e.now?.()??Date.now())/1e3),y=l.expires_in,_=typeof y=="number"&&Number.isFinite(y)&&y>0?x+Math.floor(y):null,v=typeof f.exp=="number"?f.exp:null,b=_!==null&&v!==null?Math.min(_,v):_??v;if(b===null)throw new h("server_error","the directory token response names no expiry",{origin:"directory"});const R=typeof f.jti=="string"&&f.jti?f.jti:M("sha256").update(m).digest("hex"),T=typeof f.payer=="string"&&f.payer?f.payer:r.sub;return{access_token:m,jti:R,exp:b,payer:T}}c(W,"mintOutboundToken");const k=new Map;async function ce(r,e={}){const t=F(r),i=e.now??Date.now,n=G(t,i());if(n)return{...n,key:t};let s=k.get(t);if(!s){const d=U(t);s=W(r,{...e,signal:void 0}).then(a=>(C(t,a,d),a)).finally(()=>{k.delete(t)}),s.catch(()=>{}),k.set(t,s)}return{...e.signal?await X(s,e.signal):await s,key:t}}c(ce,"outboundToken");function X(r,e){return e.aborted?Promise.reject(e.reason):new Promise((t,i)=>{const n=c(()=>i(e.reason),"onAbort");e.addEventListener("abort",n,{once:!0}),r.then(t,i).finally(()=>e.removeEventListener("abort",n))})}c(X,"abortable");export{Y as MINT_RETRY_DELAYS_MS,J as MINT_TIMEOUT_MS,le as discardFile,W as mintOutboundToken,ce as outboundToken};
1
+ var E=Object.defineProperty;var c=(r,e)=>E(r,"name",{value:e,configurable:!0});import{createHash as M}from"node:crypto";import{directoryIssuer as j}from"../directory/issuer.js";import{emitEvent as $}from"../events/emit.js";import{OutboundError as A}from"../events/outbound.js";import{grantSyncStateFor as I}from"../grants/mirror.js";import{assertion as N,machineRevokedMessage as O,requestGrantSync as P,runtimeClient as L}from"../grants/sync.js";import{isUnsandboxedMachine as D}from"../runtime/unsandboxed.js";import{ProviderError as g}from"./errors.js";import{cacheKey as F,cachedToken as G,discardEpoch as U,storeToken as J}from"./token-cache.js";import{discardFile as le}from"./token-cache.js";const Y=1e4,B=[300,900];function C(r){const e=r.issuer??j();if(!e)throw new g("not_found","provider calls need a directory-connected runtime");return e}c(C,"issuerOf");function h(r,e,t){return new g(r,e,{origin:"directory",details:t})}c(h,"directoryError");function H(r,e,t,i){const n=typeof e.error=="string"?e.error:null,s=typeof e.error_description=="string"?e.error_description:void 0,o={directory_status:r,...n?{directory_error:n}:{},...s?{directory_description:s}:{}},d=s?`: ${s}`:"";switch(n){case"invalid_client":return D()?{error:h("machine_revoked",O(i),o),grantRefusal:!1}:{error:h("server_error",`the directory refused the outbound token request (${n})${d}`,o),grantRefusal:!1};case"invalid_request":return{error:h("server_error",`the directory refused the outbound token request (${n})${d}`,o),grantRefusal:!1};case"invalid_target":return{error:h("not_found",`the directory knows no provider at ${t.audience}${d}`,o),grantRefusal:!1};case"invalid_grant":return{error:h("grant_revoked",`the directory has no active grant for ${t.grantee}${d}`,o),grantRefusal:!0};case"provider_not_connected":return{error:h("provider_not_connected",s??"The user has not connected this provider.",o),grantRefusal:!1};case"invalid_scope":{const a=typeof e.scope=="string"&&e.scope?e.scope:t.scope.length===1?t.scope[0]:void 0,l=a?`missing scope ${a}`:`the grant does not cover ${t.scope.join(" ")}`;return{error:new g("scope_missing",l,{origin:"directory",details:o,...a?{scope:a}:{}}),grantRefusal:!0}}default:return{error:h("server_error",`the directory token endpoint answered ${r}${n?` ${n}`:""}${d}`,o),grantRefusal:!1}}}c(H,"refusal");function K(r){try{return JSON.parse(Buffer.from(r.split(".")[1]??"","base64url").toString("utf8"))}catch{return{}}}c(K,"claims");function z(r){const e=r.grant_rev,t=r.grant_status;return typeof e!="number"||!Number.isInteger(e)||e<0||typeof t!="string"?null:{rev:e,status:t}}c(z,"refusedGrantState");function Q(r,e){const t=c(i=>i.status==="active"?0:1,"phase");return r.rev>e.rev||r.rev===e.rev&&t(r)>t(e)}c(Q,"newer");async function V(r,e,t){if(!e)return;const i=await I(r.grantee,r.sub)??{rev:0,status:"removed"};if(!Q(e,i))return;const n=t.syncGrants??P;Promise.resolve().then(()=>n({fresh:!0})).catch(()=>{})}c(V,"syncIfNewer");async function W(r,e={}){const t=C(e),i=`${t}/token`,n=L(),s=c(async()=>{const u=new URLSearchParams({grant_type:"client_credentials",client_id:n.client_id,client_assertion_type:"urn:ietf:params:oauth:client-assertion-type:jwt-bearer",client_assertion:N(n,i),audience:r.audience,sub:r.sub,grantee:r.grantee,scope:[...new Set(r.scope)].sort().join(" "),initiating_app:r.initiating_app}),p=AbortSignal.timeout(Y),w=e.signal?AbortSignal.any([e.signal,p]):p,S=await(e.fetch??fetch)(i,{method:"POST",body:u,signal:w,headers:{Accept:"application/json"}});return{response:S,text:await S.text()}},"attempt");let o,d;try{for(let u=0;;u++){({response:o,text:d}=await s());const p=B[u];if(p===void 0||![502,503,504].includes(o.status))break;await new Promise(w=>setTimeout(w,p))}}catch(u){throw e.signal?.aborted?e.signal.reason:new g("server_error",`the directory token endpoint could not be reached: ${u instanceof Error?u.message:String(u)}`,{origin:"directory",cause:new A("directory_token",i,u)})}let a=null;try{a=d?JSON.parse(d):null}catch{a=null}const l=a&&typeof a=="object"&&!Array.isArray(a)?a:{};if(!o.ok){const{error:u,grantRefusal:p}=H(o.status,l,r,t);throw u.code==="machine_revoked"&&$({level:"error",event:"daemon.grants.machine_revoked",fields:{audience:r.audience}}),$({level:"info",event:"daemon.providers.mint_refused",fields:{audience:r.audience,grantee:r.grantee,code:u.code,directory_error:typeof l.error=="string"?l.error:null,status:o.status}}),p&&await V(r,z(l),e),u}const m=l.access_token;if(typeof m!="string"||!m)throw new g("server_error","the directory token response has no access_token",{origin:"directory"});const f=K(m),x=Math.floor((e.now?.()??Date.now())/1e3),y=l.expires_in,_=typeof y=="number"&&Number.isFinite(y)&&y>0?x+Math.floor(y):null,v=typeof f.exp=="number"?f.exp:null,b=_!==null&&v!==null?Math.min(_,v):_??v;if(b===null)throw new g("server_error","the directory token response names no expiry",{origin:"directory"});const R=typeof f.jti=="string"&&f.jti?f.jti:M("sha256").update(m).digest("hex"),T=typeof f.payer=="string"&&f.payer?f.payer:r.sub;return{access_token:m,jti:R,exp:b,payer:T}}c(W,"mintOutboundToken");const k=new Map;async function ce(r,e={}){const t=F(r),i=e.now??Date.now,n=G(t,i());if(n)return{...n,key:t};let s=k.get(t);if(!s){const d=U(t);s=W(r,{...e,signal:void 0}).then(a=>(J(t,a,d),a)).finally(()=>{k.delete(t)}),s.catch(()=>{}),k.set(t,s)}return{...e.signal?await X(s,e.signal):await s,key:t}}c(ce,"outboundToken");function X(r,e){return e.aborted?Promise.reject(e.reason):new Promise((t,i)=>{const n=c(()=>i(e.reason),"onAbort");e.addEventListener("abort",n,{once:!0}),r.then(t,i).finally(()=>e.removeEventListener("abort",n))})}c(X,"abortable");export{B as MINT_RETRY_DELAYS_MS,Y as MINT_TIMEOUT_MS,le as discardFile,W as mintOutboundToken,ce as outboundToken};
@@ -29,6 +29,9 @@ export declare function runObject(root: NodeMeta): Promise<Record<string, unknow
29
29
  export declare const IDEMPOTENCY_WINDOW_MS = 86400000;
30
30
  /** An `Idempotency-Key` header value: absent, or 1–255 characters. */
31
31
  export declare function idempotencyKeyOf(header: unknown): string | undefined;
32
+ /** The person a cloud role's per-person rows and keys are cut to: undefined on a Mac (one person, nothing to cut); on a cloud role the caller's `sub`,
33
+ * and a caller without one is refused, so a missing `sub` can never read as "every person". */
34
+ export declare function cloudPersonScope(sub: string | null | undefined): string | undefined;
32
35
  /** Delete keys past their window; returns how many were removed. */
33
36
  export declare function pruneExpiredIdempotencyKeys(now?: number): Promise<number>;
34
37
  /** Longest `run.start` deadline an app may set: 24 hours. The grammar is `crtr node new --deadline`'s (parts of `<int>[smhd]`, summed). */
@@ -53,6 +56,7 @@ export declare function listRuns(grantee: string | null, options: {
53
56
  after?: string;
54
57
  limit: number;
55
58
  visible?: (runId: string) => Promise<boolean>;
59
+ sub?: string;
56
60
  }): Promise<{
57
61
  runs: Record<string, unknown>[];
58
62
  next: string | null;
@@ -1,14 +1,14 @@
1
- var Oe=Object.defineProperty;var u=(e,t)=>Oe(e,"name",{value:t,configurable:!0});import{coveredByAny as be}from"../../shared/env.js";import{createHash as z}from"node:crypto";import{ApiError as De}from"../../api/errors.js";import{CrtrError as Me}from"../errors.js";import{isAbsolute as Q,relative as Ae,resolve as Ie,sep as Te,join as Le}from"node:path";import{SessionManager as Ce}from"@earendil-works/pi-coding-agent";import{covers as P}from"@north-light/identity";import{openDb as B,withCanvasWrite as W}from"../canvas/db.js";import{deleteNodeWithOwned as ke}from"../graph/deletion.js";import{getById as xe}from"../graph/objects.js";import{repairRunConversations as V}from"../canvas/sessions.js";import{getNode as b,nodeFiles as We,removeNodeFiles as Fe,updateNode as qe}from"../canvas/canvas.js";import{fullName as He}from"../canvas/labels.js";import{renameWindow as Ue}from"../runtime/placement.js";import{liveExecutionForNode as Z}from"../canvas/executions.js";import{conversationStore as K,isConversationNotFound as Pe}from"../conversation-store/index.js";import{PARKED_RESIDENT_SQL as Be}from"../canvas/canvas.js";import{WAKE_MAIL_SQL as $e}from"../canvas/deliveries.js";import{readNodeRecap as je}from"../canvas/node-recap.js";import{enqueueNodeMail as Je}from"../feed/messages.js";import{newestReportBody as Ye}from"../feed/reports.js";import{ensureAppProfile as Ge,loadExactProfileManifest as Xe}from"../profiles/manifest.js";import{stopNodeSandboxes as ee}from"../spaces/stop.js";import{BrokerUnreachableError as ze}from"../runtime/broker-request.js";import{interruptLive as Qe}from"../runtime/interactive-deliver.js";import{isBrokerLive as te}from"../runtime/model-swap.js";import{newNodeId as Ve}from"../runtime/nodes.js";import{reviveNode as $}from"../runtime/revive.js";import{spawnChildPrepared as Ze}from"../runtime/spawn.js";import{isPortableModelToken as Ke}from"../runtime/launch.js";import{parseDurationMs as et}from"../wake.js";import{expandModelCandidates as tt,parseModelSpec as nt}from"../model-routes.js";import{payerOfGrant as j}from"../grants/mirror.js";import{isolatedBrokerLauncherAvailable as at}from"../runtime/host.js";import{resolveRunKind as st,resolveRunModel as it}from"../runtime/launch-target.js";import{cycleAwareMessages as rt}from"../runtime/session-cycles.js";import{originFor as ne}from"../runtime/stamp/protocol.js";import{readOutputRequest as ot,readOutputResult as ae}from"../runtime/output-result.js";import{admitLegacyStructuredOutput as se}from"../../daemon/structured-requests/sidecar.js";import{runSchemaRepromptPending as dt}from"../runtime/stop-guard.js";import{toRunOutcomeDTO as ct}from"../../daemon/api/map.js";import{openAssistantMessageStartSequence as ut}from"../../daemon/runs/observer.js";import{closeNode as ie}from"../runtime/close.js";import{parseScope as mt}from"@north-light/identity";import{appendRunEvent as re,lockRunRoot as lt,subscribeRunEvents as pt}from"./events.js";import{runActivity as _t}from"./activity.js";import{cancelOpenQuestions as oe,openNoticeIds as ft,openQuestionIds as de}from"./questions.js";import{appSpace as J}from"../layout.js";import{ensureAppSpace as wt}from"../spaces/space.js";import{assertUploadsUsable as ce,resolveStorageUrls as gt}from"../storage/attachments.js";import{storageClient as Et,storageConfig as yt}from"../storage/client.js";import{deleteUploadRow as ht,insertUploadUses as ue,releaseRunUploads as Rt,runUsesPath as St}from"../storage/tables.js";import{abortProviderCalls as me}from"../providers/inflight.js";import{filterAsync as le,mapAsync as k}from"../async-array.js";const pe=u((e,t,s,n)=>gt(t,e,s,n?{taken:n}:{}),"defaultResolver"),Nt=3e3;class S extends Error{static{u(this,"RunError")}status;code;param;details;scope;retryable;constructor(t,s,n,o,m,i,r=t===503){super(n),this.status=t,this.code=s,this.param=o,this.details=m,this.scope=i,this.retryable=r}}const N=u((e,t)=>new S(400,"invalid_request",e,t),"invalid"),Y=u(()=>new S(404,"not_found","run not found"),"missing"),G=u(()=>({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}),"usage"),A=u(async()=>await B(),"db");async function F(e,t,s=!1){const n=await b(e);if(!n||n.parent!==null||n.run_id!==e||n.grantee!==t||!s&&n.deleting_at)throw Y();return n}u(F,"ownedRun");const _e=["waiting_on_user","idle","settled"];async function C(e){if(e.status==="canceled"||e.status==="dead"||e.status==="done"&&e.intent!=="parked")return"settled";const t=await(await A()).prepare(`SELECT 1 FROM node_rows n WHERE n.run_id = ? AND n.status = 'active' AND
1
+ var Oe=Object.defineProperty;var c=(e,t)=>Oe(e,"name",{value:t,configurable:!0});import{coveredByAny as De}from"../../shared/env.js";import{createHash as Q}from"node:crypto";import{ApiError as Me}from"../../api/errors.js";import{CrtrError as Ae}from"../errors.js";import{isCloudRole as Ie}from"../canvas/person.js";import{isAbsolute as V,relative as Te,resolve as Le,sep as Ce,join as ke}from"node:path";import{SessionManager as xe}from"@earendil-works/pi-coding-agent";import{covers as P}from"@north-light/identity";import{openDb as B,withCanvasWrite as F}from"../canvas/db.js";import{deleteNodeWithOwned as We}from"../graph/deletion.js";import{getById as Fe}from"../graph/objects.js";import{repairRunConversations as Z}from"../canvas/sessions.js";import{getNode as O,nodeFiles as qe,removeNodeFiles as He,updateNode as Ue}from"../canvas/canvas.js";import{fullName as $e}from"../canvas/labels.js";import{renameWindow as Pe}from"../runtime/placement.js";import{liveExecutionForNode as K}from"../canvas/executions.js";import{conversationStore as ee,isConversationNotFound as Be}from"../conversation-store/index.js";import{PARKED_RESIDENT_SQL as je}from"../canvas/canvas.js";import{WAKE_MAIL_SQL as Je}from"../canvas/deliveries.js";import{readNodeRecap as Ye}from"../canvas/node-recap.js";import{enqueueNodeMail as Ge}from"../feed/messages.js";import{newestReportBody as Xe}from"../feed/reports.js";import{ensureAppProfile as ze,loadExactProfileManifest as Qe}from"../profiles/manifest.js";import{stopNodeSandboxes as te}from"../spaces/stop.js";import{BrokerUnreachableError as Ve}from"../runtime/broker-request.js";import{interruptLive as Ze}from"../runtime/interactive-deliver.js";import{isBrokerLive as ne}from"../runtime/model-swap.js";import{newNodeId as Ke}from"../runtime/nodes.js";import{reviveNode as j}from"../runtime/revive.js";import{spawnChildPrepared as et}from"../runtime/spawn.js";import{isPortableModelToken as tt}from"../runtime/launch.js";import{parseDurationMs as nt}from"../wake.js";import{expandModelCandidates as st,parseModelSpec as at}from"../model-routes.js";import{payerOfGrant as J}from"../grants/mirror.js";import{isolatedBrokerLauncherAvailable as it}from"../runtime/host.js";import{resolveRunKind as rt,resolveRunModel as ot}from"../runtime/launch-target.js";import{cycleAwareMessages as dt}from"../runtime/session-cycles.js";import{originFor as se}from"../runtime/stamp/protocol.js";import{readOutputRequest as ct,readOutputResult as ae}from"../runtime/output-result.js";import{admitLegacyStructuredOutput as ie}from"../../daemon/structured-requests/sidecar.js";import{runSchemaRepromptPending as ut}from"../runtime/stop-guard.js";import{toRunOutcomeDTO as mt}from"../../daemon/api/map.js";import{openAssistantMessageStartSequence as lt}from"../../daemon/runs/observer.js";import{closeNode as re}from"../runtime/close.js";import{parseScope as pt}from"@north-light/identity";import{appendRunEvent as oe,lockRunRoot as ft,subscribeRunEvents as _t}from"./events.js";import{runActivity as wt}from"./activity.js";import{cancelOpenQuestions as de,openNoticeIds as gt,openQuestionIds as ce}from"./questions.js";import{appSpace as Y}from"../layout.js";import{ensureAppSpace as Et}from"../spaces/space.js";import{assertUploadsUsable as ue,resolveStorageUrls as yt}from"../storage/attachments.js";import{storageClient as ht,storageConfig as Rt}from"../storage/client.js";import{deleteUploadRow as St,insertUploadUses as me,releaseRunUploads as Nt,runUsesPath as vt}from"../storage/tables.js";import{abortProviderCalls as le}from"../providers/inflight.js";import{filterAsync as pe,mapAsync as x}from"../async-array.js";const fe=c((e,t,s,n)=>yt(t,e,s,n?{taken:n}:{}),"defaultResolver"),bt=3e3;class R extends Error{static{c(this,"RunError")}status;code;param;details;scope;retryable;constructor(t,s,n,o,m,i,r=t===503){super(n),this.status=t,this.code=s,this.param=o,this.details=m,this.scope=i,this.retryable=r}}const N=c((e,t)=>new R(400,"invalid_request",e,t),"invalid"),G=c(()=>new R(404,"not_found","run not found"),"missing"),X=c(()=>({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}),"usage"),A=c(async()=>await B(),"db");async function q(e,t,s=!1){const n=await O(e);if(!n||n.parent!==null||n.run_id!==e||n.grantee!==t||!s&&n.deleting_at)throw G();return n}c(q,"ownedRun");const _e=["waiting_on_user","idle","settled"];async function C(e){if(e.status==="canceled"||e.status==="dead"||e.status==="done"&&e.intent!=="parked")return"settled";const t=await(await A()).prepare(`SELECT 1 FROM node_rows n WHERE n.run_id = ? AND n.status = 'active' AND
2
2
  (n.last_settled_at IS NULL OR EXISTS (
3
3
  SELECT 1 FROM run_events e WHERE e.run_id = ? AND e.node_id = n.node_id AND e.type = 'node.turn.started'
4
4
  AND e.sequence_number > COALESCE((SELECT MAX(c.sequence_number) FROM run_events c
5
5
  WHERE c.run_id = e.run_id AND c.node_id = e.node_id AND c.type = 'node.turn.completed'), 0)
6
- )) LIMIT 1`).get(e.node_id,e.node_id),s=!e.last_settled_at&&e.status!=="done";return t||s||await dt(e)?"running":(await de(e.node_id)).length?"waiting_on_user":await(await A()).prepare(`SELECT 1 FROM deliveries d JOIN nodes n ON n.node_id = d.destination_id
7
- WHERE n.run_id = ? AND ${$e("d","'armed', 'pending'")}
8
- AND (n.status IN ('active', 'idle') OR ${Be("n")}) LIMIT 1`).get(e.node_id)?"running":"idle"}u(C,"runStatus");async function In(e){const t=await b(e);return t!==null&&await C(t)==="running"}u(In,"runIsRunning");async function x(e){await V(e.node_id),e=await b(e.node_id)??e;const t=await C(e),s=t==="settled"?[]:await de(e.node_id),n=await Ye(e.node_id)||void 0,o=t==="settled"?await ct(e):null,m=o?.kind==="result"?await ae(e.node_id):void 0,i=je(e.node_id);return{run_id:e.node_id,name:e.name,status:t,...o?{outcome:o}:{},...m===void 0?{}:{result:m},...n!==void 0?{last_report:n}:{},...i?{recap:i}:{},pending_question_ids:s,open_notice_ids:t==="settled"?[]:await ft(e.node_id),...e.last_turn_error_json?{last_turn_error:JSON.parse(e.last_turn_error_json)}:{},usage:JSON.parse(e.usage_json??JSON.stringify(G())),payer:await j(e.grant_id),scopes:e.scopes,metadata:e.metadata_json?JSON.parse(e.metadata_json):{},created_at:e.created,...t==="settled"&&e.outcome_at?{settled_at:e.outcome_at}:{},latest_sequence:(e.event_cursor??1)-1,last_activity_at:e.last_activity_at,activity:await _t(e.node_id,t)}}u(x,"runObject");const X=864e5,fe=255;function Tn(e){if(e!==void 0){if(typeof e!="string"||!e.length||e.length>fe)throw N(`Idempotency-Key must be 1-${fe} characters`);return e}}u(Tn,"idempotencyKeyOf");async function q(e,t,s,n){n&&await n.prepare("SELECT pg_advisory_xact_lock(hashtext(?))").get(`idempotency:${e}:${t}`),n??=await B();const o=await n.prepare("SELECT body_hash, run_id, response_json, created_at FROM idempotency_keys WHERE grantee = ? AND key = ?").get(e,t);if(!(!o||Date.now()-Date.parse(o.created_at)>=X)){if(o.body_hash!==s)throw new S(409,"idempotency_conflict","key used with a different request");return{run_id:o.run_id,response:JSON.parse(o.response_json)}}}u(q,"findIdempotent");async function we(e,t,s,n,o,m,i){await e.prepare("DELETE FROM idempotency_keys WHERE grantee = ? AND key = ? AND created_at <= ?").run(t,s,new Date(Date.now()-X).toISOString()),await e.prepare("INSERT INTO idempotency_keys (grantee, key, body_hash, run_id, response_json, created_at) VALUES (?, ?, ?, ?, ?, ?)").run(t,s,n,o,JSON.stringify(m),i)}u(we,"recordIdempotent");async function Ln(e=Date.now()){return Number((await(await B()).prepare("DELETE FROM idempotency_keys WHERE created_at <= ?").run(new Date(e-X).toISOString())).changes)}u(Ln,"pruneExpiredIdempotencyKeys");async function ge(e,t,s,n,o){const m=n.text;let i=0;return{messageId:(await Je(t,{kind:"message",label:m.slice(0,120),senderNodeId:"human",senderName:s,bodyJson:o?{body:m}:{body:m,start_turn:!1},urgency:o?"normal":"deferred",wake:o?1:0,withinEnqueue:u(async(c,l)=>{n.uses.length&&(await ce(s,n.uses,c),await ue(n.uses.map(({upload_id:g,path:E,blob_key:y})=>({upload_id:g,path:E,blob_key:y,run_id:t})),c)),i=(await re(c,t,t,"node.message.accepted",{message_id:l,start_turn:o})).sequence_number},"withinEnqueue")})).messageId,sequence:i}}u(ge,"acceptRunMessage");class Ee extends Error{static{u(this,"IdempotentReplay")}winner;constructor(t){super("idempotent replay"),this.winner=t}}const vt=1440*60*1e3;function ye(e){const t=typeof e=="string"?et(e):null;if(t===null||!Number.isSafeInteger(t))throw N(`deadline '${String(e)}' is not a duration; use one or more <int>[smhd] parts such as 10m or 1h30m`,"deadline");if(t<=0)throw N("deadline must be greater than zero","deadline");if(t>vt)throw N("deadline must be at most 24h","deadline");return t}u(ye,"parseRunDeadlineMs");async function Cn(e,t,s,n=pe){if(t.isolated&&!at())throw new S(503,"isolated_unavailable","isolated runs require the Linux root launcher","isolated");t.deadline!==void 0&&ye(t.deadline);const{causedBy:o,...m}=t,i=z("sha256").update(JSON.stringify(m)).digest("hex");if(s){const a=await q(e.grantee,s,i);if(a){const p=await F(a.run_id,e.grantee);return!p.pi_pid&&await C(p)==="running"&&(t.output_schema!==void 0&&!p.outcome_at&&await ot(p.node_id)===null&&await ae(p.node_id)===void 0&&await se(p.node_id,"terminal",t.output_schema),await $(p.node_id,{resume:!0,capacity:"freeze"})),a.response}}await wt(e.grantee);const r=t.profile===void 0?await Ge(e.grantee):Xe(t.profile),c=r.manifest.grantee??"app:terminal";if(c!==e.grantee){const a=`crtr:memory:rw:${c}:profile:${r.profileId}`;if(!e.resolved.some(p=>P(p,a))||t.scopes!==null&&!t.scopes.some(p=>P(p,a)))throw new S(403,"scope_missing",`missing scope ${a}`,"profile",void 0,a)}if(t.model!==void 0){const a=t.model;if(Ke(a)){if(a.includes("/")&&await j(e.grant)!=="app"){const p=a.lastIndexOf("/"),D=a.slice(0,p),v=a.slice(p+1);if(["ultra","strong","medium","light"].includes(v)&&(await tt({kind:"logical",family:D,strength:v,spec:a},J(e.grantee).files,r.profileId)).every(R=>R.family!==D))throw N(`model '${a}' names a model family that is not configured in this daemon`,"model")}}else if(nt(a)===void 0)throw N(`model '${a}' is not a strength (ultra, strong, medium, light), a family/strength, or a provider/model id`,"model")}const l=await Ve(),g=Le(J(e.grantee).nodes,l);if(t.isolated&&t.cwd!==void 0){const a=Ae(g,Ie(t.cwd));if(!Q(t.cwd)||a===".."||a.startsWith(`..${Te}`)||Q(a))throw N("cwd must be inside the isolated root node folder","cwd")}const E=await n(l,e.grantee,t.prompt),y=E.text,d=t.message===void 0?void 0:await n(l,e.grantee,t.message,E.uses.map(a=>a.path)),f=await j(e.grant);let h;try{const a=it(r,t.model);await Ze({kind:st(r),mode:"base",root:!0,rootLifecycle:"resident",readiness:"launch",cwd:t.isolated?t.cwd??g:J(e.grantee).files,profile:r.profileId,prompt:y,nodeId:l,isolated:t.isolated??!1,...t.causedBy===void 0?{}:{causedBy:t.causedBy},...a===void 0?{}:{model:a},...t.deadline===void 0?{}:{deadlineAt:new Date(Date.now()+ye(t.deadline)).toISOString()},...t.scopes===null?{}:{scopes:t.scopes},run:{grantee:e.grantee,sub:e.sub,grant_id:e.grant,client_id:e.client_id,metadata_json:JSON.stringify(t.metadata),run_id:l,event_cursor:1,usage_json:JSON.stringify(G())},withinCreate:u(async(p,D)=>{if(s){const R=await q(e.grantee,s,i,p);if(R)throw new Ee(R)}const v=E.uses;if(v.length&&(await ce(e.grantee,v,p),await ue(v.map(({upload_id:R,path:I,blob_key:H})=>({upload_id:R,path:I,blob_key:H,run_id:l})),p)),h={run_id:l,node_id:l,grantee:e.grantee,grant:e.grant,rev:e.rev,scopes:t.scopes,payer:f,status:"running",created_at:D.created,metadata:t.metadata,sequence_number:0},await re(p,l,l,"run.started",{scopes:t.scopes,payer:f,usage:G()}),d){const R=await ge(p,l,e.grantee,d,!1);h.message={run_id:l,message_id:R.messageId,sequence_number:R.sequence}}s&&await we(p,e.grantee,s,i,l,h,D.created)},"withinCreate")},t.output_schema===void 0?void 0:async p=>{await se(p,"terminal",t.output_schema)})}catch(a){if(a instanceof Ee)return a.winner.response;if(t.model!==void 0&&a instanceof Me&&a.code==="usage")throw N(a.message,"model");if(a instanceof De&&(a.code==="isolated_run"||a.code==="invalid_request")){const p=a.details&&typeof a.details=="object"&&"param"in a.details&&typeof a.details.param=="string"?a.details.param:void 0;throw new S(a.status,a.code,a.message,p)}throw a}if(!h)throw new Error("run creation did not commit");return h}u(Cn,"startRun");async function kn(e,t){let s;if(t.after)try{const r=JSON.parse(Buffer.from(t.after,"base64url").toString());if(!r||typeof r!="object"||!("activity"in r)||typeof r.activity!="string"||!("id"in r)||typeof r.id!="string")throw new Error("invalid cursor");s={activity:r.activity,id:r.id}}catch{throw N("invalid list cursor","after")}const n=await(await A()).prepareUncached(`SELECT node_id, COALESCE(last_activity_at, created) AS activity FROM node_rows
9
- WHERE run_id = node_id AND deleting_at IS NULL ${e===null?"":"AND grantee = ?"}
10
- ORDER BY activity DESC, node_id DESC`).all(...e===null?[]:[e]),o=await le(await k((await le(n,async({node_id:r})=>!t.visible||await t.visible(r))).filter(({activity:r,node_id:c})=>!s||r<s.activity||r===s.activity&&c<s.id),async r=>({root:await b(r.node_id),activity:r.activity})),async({root:r})=>!t.status||await C(r)===t.status),m=o.slice(0,t.limit),i=m.at(-1);return{runs:await k(m,async({root:r})=>await x(r)),next:o.length>t.limit&&i?Buffer.from(JSON.stringify({activity:i.activity,id:i.root.node_id})).toString("base64url"):null}}u(kn,"listRuns");async function xn(e,t,s){const n=await F(e,t),o=u(async()=>await x(await F(e,t)),"read");return!s||_e.includes(await C(n))?await o():new Promise((m,i)=>{let r=u(()=>{},"unsubscribe");const c=u(()=>{clearTimeout(d),clearInterval(y),r()},"settle"),l=u(async()=>{c();try{m(await o())}catch(f){i(f)}},"finish"),g=u(async()=>{_e.includes(String((await o()).status))&&await l()},"check"),E=u(()=>{g().catch(f=>{c(),i(f)})},"run"),y=setInterval(E,250),d=setTimeout(()=>{l()},s*1e3);pt(e,E).then(f=>{r=f},f=>{c(),i(f)})})}u(xn,"waitRun");const Ot=5;async function Wn(e,t,s=pe,n=!0,o){const m=e.status==="canceled"||e.outcome_reason==="closed",i=e.grantee??"app:terminal",r=z("sha256").update(JSON.stringify(["run.message",e.node_id,t,n])).digest("hex"),c=u(async g=>{const E=await b(e.node_id),y=await(await A()).prepare("SELECT 1 FROM deliveries WHERE message_id = ? AND state = 'pending' AND canceled_at IS NULL").get(String(g.message_id));return E&&!m&&n&&!E.pi_pid&&y&&await $(e.node_id,{resume:!0,capacity:"freeze"}),g},"replay");if(o){const g=await q(i,o,r);if(g)return c(g.response)}if(m)throw new S(409,"run_settled","canceled run cannot receive a message");const l=n&&e.final_report&&!e.pi_pid?(await import("../runtime/reopen.js")).commitReopenResident:void 0;for(let g=1;;g++){const E=await s(e.node_id,i,t),y=await W(async d=>{if(o){const a=await q(i,o,r,d);if(a)return{replayed:a.response}}if(E.uses.length){await lt(d,e.node_id);for(const a of E.uses)if(await St(e.node_id,a.path,d))return"path_taken"}await l?.(e.node_id);const f=await ge(d,e.node_id,i,E,n),h={run_id:e.node_id,message_id:f.messageId,sequence_number:f.sequence};return o&&await we(d,i,o,r,e.node_id,h,new Date().toISOString()),{result:h}});if(y==="path_taken"){if(g>=Ot)throw new S(503,"service_unavailable","attachments of concurrent messages kept taking each other's file names; retry");continue}return"replayed"in y?c(y.replayed):(n&&!e.pi_pid&&await $(e.node_id,{resume:!0,capacity:"freeze"}),y.result)}}u(Wn,"messageRun");async function bt(e){const t=u(()=>new S(503,"service_unavailable",`node ${e} broker was unavailable before the interrupt reached it`),"unavailable");for(let s=0;s<2;s++){const n=await b(e);if(!n)throw Y();if(!te(n)){if(n.streaming)throw t();return}const o=(await Z(e))?.executionId;try{await Qe(e)==="turn"&&me({nodes:[e]});return}catch(m){if(!(m instanceof ze))throw m;const i=await b(e);if(!i)throw Y();if(!i.streaming)return;if(s===0&&te(i)&&(await Z(e))?.executionId!==o)continue;throw t()}}throw t()}u(bt,"interruptNode");async function Fn(e,t="stop"){const s=t==="stop"?(await(await A()).prepare("SELECT node_id FROM nodes WHERE run_id = ?").all(e.node_id)).map(({node_id:n})=>n):[e.node_id];return await Promise.all(s.map(n=>bt(n))),await x(await b(e.node_id)??e)}u(Fn,"interruptRun");async function qn(e,t={}){const s=(await(await A()).prepare("SELECT node_id FROM nodes WHERE run_id = ? ORDER BY created DESC").all(e.node_id)).map(i=>i.node_id);me({run:e.node_id});const n=[];if(await C(e)!=="settled")n.push(...(await ie(e.node_id,{preserveEmpty:!0,stopReason:"run_canceled"})).closed);else for(const i of s){if(i===e.node_id)continue;const r=await b(i);r&&r.status!=="done"&&r.status!=="canceled"&&r.status!=="dead"&&n.push(...(await ie(i,{preserveEmpty:!0,stopReason:"run_canceled"})).closed)}if(await oe({run_id:e.node_id},"the run was canceled"),(await Promise.all([...new Set([e.node_id,...n])].map(i=>ee(i,"run_canceled")))).some(i=>!i.confirmed))throw new S(500,"server_error","run process teardown could not be confirmed",void 0,void 0,void 0,!0);const m=await b(e.node_id);return await x(m??e)}u(qn,"cancelRun");const he=200;async function Hn(e,t){if(typeof t!="string"||!t.trim())throw N("name is required","name");const s=t.trim();if(s.length>he)throw N(`name exceeds ${he} characters`,"name");const n=await qe(e.node_id,{name:s});return n.window&&Ue(n.window,He(n)),await x(n)}u(Hn,"renameRun");async function Un(e,t={config:yt,client:Et}){await oe({run_id:e.node_id},"the run was deleted");const s=await W(async c=>(await c.prepare("UPDATE nodes SET deleting_at = COALESCE(deleting_at, ?) WHERE node_id = ?").run(new Date().toISOString(),e.node_id),(await c.prepare("SELECT node_id FROM nodes WHERE run_id = ? ORDER BY created DESC").all(e.node_id)).map(l=>l.node_id)));if((await Promise.all(s.map(c=>ee(c,"run_deleted",{deleting:!0})))).some(c=>!c.confirmed))throw new S(500,"server_error","run process teardown could not be confirmed",void 0,void 0,void 0,!0);const o=await k(s,async c=>{const l=await We(c);if(l.app===null)throw new Error(`run node missing during deletion: ${c}`);return l}),m=await(await A()).prepare("SELECT DISTINCT session_id FROM sessions WHERE session_id IS NOT NULL AND node_id IN (SELECT node_id FROM nodes WHERE run_id = ?)").all(e.node_id);for(const{session_id:c}of m)try{await K().delete(c)}catch(l){if(!Pe(l))throw l}const i=await W(async c=>{for(const y of s){const d=await xe(c,y);d!==null&&d.deleted_at===null&&await ke(c,d)}const l=await Rt([e.node_id],c);await c.prepare("DELETE FROM run_events WHERE run_id = ?").run(e.node_id),await c.prepare("DELETE FROM idempotency_keys WHERE run_id = ?").run(e.node_id);const g=s.map(()=>"?").join(","),E=await c.prepareUncached(`SELECT message_id FROM messages WHERE sender_node_id IN (${g}) UNION SELECT message_id FROM deliveries WHERE destination_id IN (${g})`).all(...s,...s);await c.prepareUncached(`DELETE FROM deliveries WHERE destination_id IN (${g})`).run(...s);for(const{message_id:y}of E)await c.prepare("DELETE FROM messages WHERE message_id = ? AND NOT EXISTS (SELECT 1 FROM deliveries WHERE message_id = ?)").run(y,y);return l});for(const c of o)await Fe(c);const{notifyRunDeleted:r}=await import("./events.js");return r(e.node_id),{uploads_pending:await Dt(i,t)}}u(Un,"deleteRun");async function Dt(e,t){if(!e.length||!t.config())return e.length;let s;try{s=t.client()}catch{return e.length}const n=new Set,o=e.map(i=>s.deleteUpload(i).then(async()=>{await ht(i),n.add(i)},()=>{}));let m;return await Promise.race([Promise.all(o),new Promise(i=>{m=setTimeout(i,t.budgetMs??Nt),m.unref?.()})]),clearTimeout(m),e.length-n.size}u(Dt,"deleteReleasedUploads");async function Pn(e,t,s){await F(e,t),await V(e);const n=await W(async d=>{const f=await d.prepare("SELECT node_id, parent FROM nodes WHERE run_id = ? ORDER BY CASE WHEN node_id = ? THEN 0 ELSE 1 END, created, node_id").all(e,e),h=new Map;for(const R of f)h.set(R.node_id,(await d.prepare("SELECT session_id FROM sessions WHERE node_id = ? GROUP BY session_id ORDER BY min(created_at)").all(R.node_id)).map(I=>I.session_id));const p=(await d.prepare("SELECT event_cursor FROM nodes WHERE node_id = ?").get(e)).event_cursor-1,D=await d.prepare(`SELECT node_id, sequence_number FROM run_events
6
+ )) LIMIT 1`).get(e.node_id,e.node_id),s=!e.last_settled_at&&e.status!=="done";return t||s||await ut(e)?"running":(await ce(e.node_id)).length?"waiting_on_user":await(await A()).prepare(`SELECT 1 FROM deliveries d JOIN nodes n ON n.node_id = d.destination_id
7
+ WHERE n.run_id = ? AND ${Je("d","'armed', 'pending'")}
8
+ AND (n.status IN ('active', 'idle') OR ${je("n")}) LIMIT 1`).get(e.node_id)?"running":"idle"}c(C,"runStatus");async function kn(e){const t=await O(e);return t!==null&&await C(t)==="running"}c(kn,"runIsRunning");async function W(e){await Z(e.node_id),e=await O(e.node_id)??e;const t=await C(e),s=t==="settled"?[]:await ce(e.node_id),n=await Xe(e.node_id)||void 0,o=t==="settled"?await mt(e):null,m=o?.kind==="result"?await ae(e.node_id):void 0,i=Ye(e.node_id);return{run_id:e.node_id,name:e.name,status:t,...o?{outcome:o}:{},...m===void 0?{}:{result:m},...n!==void 0?{last_report:n}:{},...i?{recap:i}:{},pending_question_ids:s,open_notice_ids:t==="settled"?[]:await gt(e.node_id),...e.last_turn_error_json?{last_turn_error:JSON.parse(e.last_turn_error_json)}:{},usage:JSON.parse(e.usage_json??JSON.stringify(X())),payer:await J(e.grant_id),scopes:e.scopes,metadata:e.metadata_json?JSON.parse(e.metadata_json):{},created_at:e.created,...t==="settled"&&e.outcome_at?{settled_at:e.outcome_at}:{},latest_sequence:(e.event_cursor??1)-1,last_activity_at:e.last_activity_at,activity:await wt(e.node_id,t)}}c(W,"runObject");const z=864e5,we=255;function xn(e){if(e!==void 0){if(typeof e!="string"||!e.length||e.length>we)throw N(`Idempotency-Key must be 1-${we} characters`);return e}}c(xn,"idempotencyKeyOf");function Ot(e){if(Ie()){if(!e)throw new R(403,"permission_error","a cloud role serves this call only for a token that names a person");return e}}c(Ot,"cloudPersonScope");function k(e,t){const s=Ot(e);return s===void 0?t:`${s}:${t}`}c(k,"personKey");async function H(e,t,s,n){n&&await n.prepare("SELECT pg_advisory_xact_lock(hashtext(?))").get(`idempotency:${e}:${t}`),n??=await B();const o=await n.prepare("SELECT body_hash, run_id, response_json, created_at FROM idempotency_keys WHERE grantee = ? AND key = ?").get(e,t);if(!(!o||Date.now()-Date.parse(o.created_at)>=z)){if(o.body_hash!==s)throw new R(409,"idempotency_conflict","key used with a different request");return{run_id:o.run_id,response:JSON.parse(o.response_json)}}}c(H,"findIdempotent");async function ge(e,t,s,n,o,m,i){await e.prepare("DELETE FROM idempotency_keys WHERE grantee = ? AND key = ? AND created_at <= ?").run(t,s,new Date(Date.now()-z).toISOString()),await e.prepare("INSERT INTO idempotency_keys (grantee, key, body_hash, run_id, response_json, created_at) VALUES (?, ?, ?, ?, ?, ?)").run(t,s,n,o,JSON.stringify(m),i)}c(ge,"recordIdempotent");async function Wn(e=Date.now()){return Number((await(await B()).prepare("DELETE FROM idempotency_keys WHERE created_at <= ?").run(new Date(e-z).toISOString())).changes)}c(Wn,"pruneExpiredIdempotencyKeys");async function Ee(e,t,s,n,o){const m=n.text;let i=0;return{messageId:(await Ge(t,{kind:"message",label:m.slice(0,120),senderNodeId:"human",senderName:s,bodyJson:o?{body:m}:{body:m,start_turn:!1},urgency:o?"normal":"deferred",wake:o?1:0,withinEnqueue:c(async(u,l)=>{n.uses.length&&(await ue(s,n.uses,u),await me(n.uses.map(({upload_id:g,path:E,blob_key:y})=>({upload_id:g,path:E,blob_key:y,run_id:t})),u)),i=(await oe(u,t,t,"node.message.accepted",{message_id:l,start_turn:o})).sequence_number},"withinEnqueue")})).messageId,sequence:i}}c(Ee,"acceptRunMessage");class ye extends Error{static{c(this,"IdempotentReplay")}winner;constructor(t){super("idempotent replay"),this.winner=t}}const Dt=1440*60*1e3;function he(e){const t=typeof e=="string"?nt(e):null;if(t===null||!Number.isSafeInteger(t))throw N(`deadline '${String(e)}' is not a duration; use one or more <int>[smhd] parts such as 10m or 1h30m`,"deadline");if(t<=0)throw N("deadline must be greater than zero","deadline");if(t>Dt)throw N("deadline must be at most 24h","deadline");return t}c(he,"parseRunDeadlineMs");async function Fn(e,t,s,n=fe){if(t.isolated&&!it())throw new R(503,"isolated_unavailable","isolated runs require the Linux root launcher","isolated");t.deadline!==void 0&&he(t.deadline);const{causedBy:o,...m}=t,i=Q("sha256").update(JSON.stringify(m)).digest("hex");if(s){const a=await H(e.grantee,k(e.sub,s),i);if(a){const p=await q(a.run_id,e.grantee);return!p.pi_pid&&await C(p)==="running"&&(t.output_schema!==void 0&&!p.outcome_at&&await ct(p.node_id)===null&&await ae(p.node_id)===void 0&&await ie(p.node_id,"terminal",t.output_schema),await j(p.node_id,{resume:!0,capacity:"freeze"})),a.response}}await Et(e.grantee);const r=t.profile===void 0?await ze(e.grantee):Qe(t.profile),u=r.manifest.grantee??"app:terminal";if(u!==e.grantee){const a=`crtr:memory:rw:${u}:profile:${r.profileId}`;if(!e.resolved.some(p=>P(p,a))||t.scopes!==null&&!t.scopes.some(p=>P(p,a)))throw new R(403,"scope_missing",`missing scope ${a}`,"profile",void 0,a)}if(t.model!==void 0){const a=t.model;if(tt(a)){if(a.includes("/")&&await J(e.grant)!=="app"){const p=a.lastIndexOf("/"),D=a.slice(0,p),v=a.slice(p+1);if(["ultra","strong","medium","light"].includes(v)&&(await st({kind:"logical",family:D,strength:v,spec:a},Y(e.grantee).files,r.profileId)).every(S=>S.family!==D))throw N(`model '${a}' names a model family that is not configured in this daemon`,"model")}}else if(at(a)===void 0)throw N(`model '${a}' is not a strength (ultra, strong, medium, light), a family/strength, or a provider/model id`,"model")}const l=await Ke(),g=ke(Y(e.grantee).nodes,l);if(t.isolated&&t.cwd!==void 0){const a=Te(g,Le(t.cwd));if(!V(t.cwd)||a===".."||a.startsWith(`..${Ce}`)||V(a))throw N("cwd must be inside the isolated root node folder","cwd")}const E=await n(l,e.grantee,t.prompt),y=E.text,d=t.message===void 0?void 0:await n(l,e.grantee,t.message,E.uses.map(a=>a.path)),_=await J(e.grant);let h;try{const a=ot(r,t.model);await et({kind:rt(r),mode:"base",root:!0,rootLifecycle:"resident",readiness:"launch",cwd:t.isolated?t.cwd??g:Y(e.grantee).files,profile:r.profileId,prompt:y,nodeId:l,isolated:t.isolated??!1,...t.causedBy===void 0?{}:{causedBy:t.causedBy},...a===void 0?{}:{model:a},...t.deadline===void 0?{}:{deadlineAt:new Date(Date.now()+he(t.deadline)).toISOString()},...t.scopes===null?{}:{scopes:t.scopes},run:{grantee:e.grantee,sub:e.sub,grant_id:e.grant,client_id:e.client_id,metadata_json:JSON.stringify(t.metadata),run_id:l,event_cursor:1,usage_json:JSON.stringify(X())},withinCreate:c(async(p,D)=>{if(s){const S=await H(e.grantee,k(e.sub,s),i,p);if(S)throw new ye(S)}const v=E.uses;if(v.length&&(await ue(e.grantee,v,p),await me(v.map(({upload_id:S,path:I,blob_key:U})=>({upload_id:S,path:I,blob_key:U,run_id:l})),p)),h={run_id:l,node_id:l,grantee:e.grantee,grant:e.grant,rev:e.rev,scopes:t.scopes,payer:_,status:"running",created_at:D.created,metadata:t.metadata,sequence_number:0},await oe(p,l,l,"run.started",{scopes:t.scopes,payer:_,usage:X()}),d){const S=await Ee(p,l,e.grantee,d,!1);h.message={run_id:l,message_id:S.messageId,sequence_number:S.sequence}}s&&await ge(p,e.grantee,k(e.sub,s),i,l,h,D.created)},"withinCreate")},t.output_schema===void 0?void 0:async p=>{await ie(p,"terminal",t.output_schema)})}catch(a){if(a instanceof ye)return a.winner.response;if(t.model!==void 0&&a instanceof Ae&&a.code==="usage")throw N(a.message,"model");if(a instanceof Me&&(a.code==="isolated_run"||a.code==="invalid_request")){const p=a.details&&typeof a.details=="object"&&"param"in a.details&&typeof a.details.param=="string"?a.details.param:void 0;throw new R(a.status,a.code,a.message,p)}throw a}if(!h)throw new Error("run creation did not commit");return h}c(Fn,"startRun");async function qn(e,t){let s;if(t.after)try{const r=JSON.parse(Buffer.from(t.after,"base64url").toString());if(!r||typeof r!="object"||!("activity"in r)||typeof r.activity!="string"||!("id"in r)||typeof r.id!="string")throw new Error("invalid cursor");s={activity:r.activity,id:r.id}}catch{throw N("invalid list cursor","after")}const n=await(await A()).prepareUncached(`SELECT node_id, COALESCE(last_activity_at, created) AS activity FROM node_rows
9
+ WHERE run_id = node_id AND deleting_at IS NULL ${e===null?"":"AND grantee = ?"}${t.sub===void 0?"":" AND sub = ?"}
10
+ ORDER BY activity DESC, node_id DESC`).all(...e===null?[]:[e],...t.sub===void 0?[]:[t.sub]),o=await pe(await x((await pe(n,async({node_id:r})=>!t.visible||await t.visible(r))).filter(({activity:r,node_id:u})=>!s||r<s.activity||r===s.activity&&u<s.id),async r=>({root:await O(r.node_id),activity:r.activity})),async({root:r})=>!t.status||await C(r)===t.status),m=o.slice(0,t.limit),i=m.at(-1);return{runs:await x(m,async({root:r})=>await W(r)),next:o.length>t.limit&&i?Buffer.from(JSON.stringify({activity:i.activity,id:i.root.node_id})).toString("base64url"):null}}c(qn,"listRuns");async function Hn(e,t,s){const n=await q(e,t),o=c(async()=>await W(await q(e,t)),"read");return!s||_e.includes(await C(n))?await o():new Promise((m,i)=>{let r=c(()=>{},"unsubscribe");const u=c(()=>{clearTimeout(d),clearInterval(y),r()},"settle"),l=c(async()=>{u();try{m(await o())}catch(_){i(_)}},"finish"),g=c(async()=>{_e.includes(String((await o()).status))&&await l()},"check"),E=c(()=>{g().catch(_=>{u(),i(_)})},"run"),y=setInterval(E,250),d=setTimeout(()=>{l()},s*1e3);_t(e,E).then(_=>{r=_},_=>{u(),i(_)})})}c(Hn,"waitRun");const Mt=5;async function Un(e,t,s=fe,n=!0,o){const m=e.status==="canceled"||e.outcome_reason==="closed",i=e.grantee??"app:terminal",r=Q("sha256").update(JSON.stringify(["run.message",e.node_id,t,n])).digest("hex"),u=c(async g=>{const E=await O(e.node_id),y=await(await A()).prepare("SELECT 1 FROM deliveries WHERE message_id = ? AND state = 'pending' AND canceled_at IS NULL").get(String(g.message_id));return E&&!m&&n&&!E.pi_pid&&y&&await j(e.node_id,{resume:!0,capacity:"freeze"}),g},"replay");if(o){const g=await H(i,k(e.sub,o),r);if(g)return u(g.response)}if(m)throw new R(409,"run_settled","canceled run cannot receive a message");const l=n&&e.final_report&&!e.pi_pid?(await import("../runtime/reopen.js")).commitReopenResident:void 0;for(let g=1;;g++){const E=await s(e.node_id,i,t),y=await F(async d=>{if(o){const a=await H(i,k(e.sub,o),r,d);if(a)return{replayed:a.response}}if(E.uses.length){await ft(d,e.node_id);for(const a of E.uses)if(await vt(e.node_id,a.path,d))return"path_taken"}await l?.(e.node_id);const _=await Ee(d,e.node_id,i,E,n),h={run_id:e.node_id,message_id:_.messageId,sequence_number:_.sequence};return o&&await ge(d,i,k(e.sub,o),r,e.node_id,h,new Date().toISOString()),{result:h}});if(y==="path_taken"){if(g>=Mt)throw new R(503,"service_unavailable","attachments of concurrent messages kept taking each other's file names; retry");continue}return"replayed"in y?u(y.replayed):(n&&!e.pi_pid&&await j(e.node_id,{resume:!0,capacity:"freeze"}),y.result)}}c(Un,"messageRun");async function At(e){const t=c(()=>new R(503,"service_unavailable",`node ${e} broker was unavailable before the interrupt reached it`),"unavailable");for(let s=0;s<2;s++){const n=await O(e);if(!n)throw G();if(!ne(n)){if(n.streaming)throw t();return}const o=(await K(e))?.executionId;try{await Ze(e)==="turn"&&le({nodes:[e]});return}catch(m){if(!(m instanceof Ve))throw m;const i=await O(e);if(!i)throw G();if(!i.streaming)return;if(s===0&&ne(i)&&(await K(e))?.executionId!==o)continue;throw t()}}throw t()}c(At,"interruptNode");async function $n(e,t="stop"){const s=t==="stop"?(await(await A()).prepare("SELECT node_id FROM nodes WHERE run_id = ?").all(e.node_id)).map(({node_id:n})=>n):[e.node_id];return await Promise.all(s.map(n=>At(n))),await W(await O(e.node_id)??e)}c($n,"interruptRun");async function Pn(e,t={}){const s=(await(await A()).prepare("SELECT node_id FROM nodes WHERE run_id = ? ORDER BY created DESC").all(e.node_id)).map(i=>i.node_id);le({run:e.node_id});const n=[];if(await C(e)!=="settled")n.push(...(await re(e.node_id,{preserveEmpty:!0,stopReason:"run_canceled"})).closed);else for(const i of s){if(i===e.node_id)continue;const r=await O(i);r&&r.status!=="done"&&r.status!=="canceled"&&r.status!=="dead"&&n.push(...(await re(i,{preserveEmpty:!0,stopReason:"run_canceled"})).closed)}if(await de({run_id:e.node_id},"the run was canceled"),(await Promise.all([...new Set([e.node_id,...n])].map(i=>te(i,"run_canceled")))).some(i=>!i.confirmed))throw new R(500,"server_error","run process teardown could not be confirmed",void 0,void 0,void 0,!0);const m=await O(e.node_id);return await W(m??e)}c(Pn,"cancelRun");const Re=200;async function Bn(e,t){if(typeof t!="string"||!t.trim())throw N("name is required","name");const s=t.trim();if(s.length>Re)throw N(`name exceeds ${Re} characters`,"name");const n=await Ue(e.node_id,{name:s});return n.window&&Pe(n.window,$e(n)),await W(n)}c(Bn,"renameRun");async function jn(e,t={config:Rt,client:ht}){await de({run_id:e.node_id},"the run was deleted");const s=await F(async u=>(await u.prepare("UPDATE nodes SET deleting_at = COALESCE(deleting_at, ?) WHERE node_id = ?").run(new Date().toISOString(),e.node_id),(await u.prepare("SELECT node_id FROM nodes WHERE run_id = ? ORDER BY created DESC").all(e.node_id)).map(l=>l.node_id)));if((await Promise.all(s.map(u=>te(u,"run_deleted",{deleting:!0})))).some(u=>!u.confirmed))throw new R(500,"server_error","run process teardown could not be confirmed",void 0,void 0,void 0,!0);const o=await x(s,async u=>{const l=await qe(u);if(l.app===null)throw new Error(`run node missing during deletion: ${u}`);return l}),m=await(await A()).prepare("SELECT DISTINCT session_id FROM sessions WHERE session_id IS NOT NULL AND node_id IN (SELECT node_id FROM nodes WHERE run_id = ?)").all(e.node_id);for(const{session_id:u}of m)try{await ee().delete(u)}catch(l){if(!Be(l))throw l}const i=await F(async u=>{for(const y of s){const d=await Fe(u,y);d!==null&&d.deleted_at===null&&await We(u,d)}const l=await Nt([e.node_id],u);await u.prepare("DELETE FROM run_events WHERE run_id = ?").run(e.node_id),await u.prepare("DELETE FROM idempotency_keys WHERE run_id = ?").run(e.node_id);const g=s.map(()=>"?").join(","),E=await u.prepareUncached(`SELECT message_id FROM messages WHERE sender_node_id IN (${g}) UNION SELECT message_id FROM deliveries WHERE destination_id IN (${g})`).all(...s,...s);await u.prepareUncached(`DELETE FROM deliveries WHERE destination_id IN (${g})`).run(...s);for(const{message_id:y}of E)await u.prepare("DELETE FROM messages WHERE message_id = ? AND NOT EXISTS (SELECT 1 FROM deliveries WHERE message_id = ?)").run(y,y);return l});for(const u of o)await He(u);const{notifyRunDeleted:r}=await import("./events.js");return r(e.node_id),{uploads_pending:await It(i,t)}}c(jn,"deleteRun");async function It(e,t){if(!e.length||!t.config())return e.length;let s;try{s=t.client()}catch{return e.length}const n=new Set,o=e.map(i=>s.deleteUpload(i).then(async()=>{await St(i),n.add(i)},()=>{}));let m;return await Promise.race([Promise.all(o),new Promise(i=>{m=setTimeout(i,t.budgetMs??bt),m.unref?.()})]),clearTimeout(m),e.length-n.size}c(It,"deleteReleasedUploads");async function Jn(e,t,s){await q(e,t),await Z(e);const n=await F(async d=>{const _=await d.prepare("SELECT node_id, parent FROM nodes WHERE run_id = ? ORDER BY CASE WHEN node_id = ? THEN 0 ELSE 1 END, created, node_id").all(e,e),h=new Map;for(const S of _)h.set(S.node_id,(await d.prepare("SELECT session_id FROM sessions WHERE node_id = ? GROUP BY session_id ORDER BY min(created_at)").all(S.node_id)).map(I=>I.session_id));const p=(await d.prepare("SELECT event_cursor FROM nodes WHERE node_id = ?").get(e)).event_cursor-1,D=await d.prepare(`SELECT node_id, sequence_number FROM run_events
11
11
  WHERE run_id = ? AND sequence_number <= ? AND type IN ('node.thinking.done', 'node.output_text.done', 'usage')
12
12
  ORDER BY sequence_number`).all(e,p),v=await d.prepare(`SELECT node_id, sequence_number, type, body_json FROM run_events
13
13
  WHERE run_id = ? AND sequence_number <= ? AND type IN ('node.thinking.delta', 'node.output_text.delta')
14
- ORDER BY sequence_number`).all(e,p);return{nodes:f,sessionIds:h,latestSequence:p,completed:D,deltas:v}}),{nodes:o,sessionIds:m,latestSequence:i,completed:r,deltas:c}=n,l=await k(o,async d=>({node_id:d.node_id,parent_node_id:d.parent,conversations:await k(m.get(d.node_id)??[],async f=>{const{header:h,entries:a}=await K().read(f);if(s==="entries")return{conversation_id:f,header:h,entries:a};const p=Ce.inMemory(h.cwd,void 0,[h,...a]),D=rt(p)??[],v=new Map(a.map((w,O)=>[w.id,O])),R=a.filter(w=>w.type==="custom"&&w.customType==="crtr-system-prompt").map(w=>({id:w.id,message:{role:"custom",customType:"crtr-system-prompt",data:"data"in w?w.data:void 0,timestamp:w.timestamp}})),I=a.filter(w=>w.type==="custom_message"&&w.customType==="app_message").map(w=>({id:w.id,message:{role:"custom",customType:"app_message",content:"content"in w?w.content:"",timestamp:w.timestamp}})),H=d.node_id===e?a.find(w=>{if(w.type!=="message"||w.message.role!=="user")return!1;const O=ne(a,w.message.timestamp);return O?.type==="runtime"&&O.kind==="kickoff"})?.id:void 0,Re=[...D.filter(({id:w})=>!I.some(O=>O.id===w)),...R,...I].sort((w,O)=>(v.get(w.id)??Number.MAX_SAFE_INTEGER)-(v.get(O.id)??Number.MAX_SAFE_INTEGER)).map(({id:w,message:O})=>{const _=O,T=_.usage,U=_.role==="user"?ne(a,Number(_.timestamp)):null,Se=U?.type==="human"||w===H,L=_.role==="toolResult"?"tool_result":_.role==="bashExecution"?"bash_execution":_.role==="compactionSummary"?"compaction_summary":_.role==="branchSummary"?"branch_summary":_.role==="custom"&&_.customType==="crtr-system-prompt"?"system":_.role==="user"?Se?"user":"custom":_.role,Ne=L==="custom"?_.role==="custom"?_.customType:U?.type==="runtime"?U.kind:"unknown":void 0,ve=L==="system"&&_.customType==="crtr-system-prompt"?[{type:"text",text:_.data?.text??""}]:typeof _.content=="string"?[{type:"text",text:_.content}]:Array.isArray(_.content)?_.content.map(M=>M.type==="toolCall"?{type:"tool_call",tool_call_id:M.id,name:M.name,arguments:M.arguments}:M.type==="image"?{type:"image",mime_type:M.mimeType}:M.type==="thinking"?{type:"thinking",text:M.thinking}:M):[];return{entry_id:w,role:L,...L==="custom"?{custom_type:Ne}:{},time:typeof _.timestamp=="string"?_.timestamp:new Date(Number(_.timestamp??Date.now())).toISOString(),content:ve,...L==="assistant"?{model:_.model,provider:_.provider,stop_reason:_.stopReason,..._.errorMessage?{error_message:_.errorMessage}:{},...T?{usage:{input_tokens:T.input??0,output_tokens:T.output??0,cache_read_tokens:T.cacheRead??0,cache_write_tokens:T.cacheWrite??0,cost_usd:T.cost?.total??0}}:{}}:{},...L==="tool_result"?{tool_call_id:_.toolCallId,tool_name:_.toolName,is_error:_.isError}:{}}})??[];return{conversation_id:f,messages:Re}})})),g=new Map(await Promise.all(l.map(async d=>[d.node_id,await ut(d.node_id)])));for(const d of r){const f=g.get(d.node_id);f!=null&&d.sequence_number>=f&&g.set(d.node_id,null)}const E=new Map;for(const d of c){const f=g.get(d.node_id);if(f==null||d.sequence_number<f)continue;const h=JSON.parse(d.body_json),a=E.get(d.node_id)??{thinking:"",text:""};d.type==="node.thinking.delta"?a.thinking+=h.delta:a.text+=h.delta,E.set(d.node_id,a)}const y=l.map(d=>({...d,in_progress_message:g.get(d.node_id)==null?null:E.get(d.node_id)??{thinking:"",text:""}}));return{run_id:e,latest_sequence:i,nodes:y}}u(Pn,"traceRun");function Bn(e,t,s){if(t===void 0){if(!e.some(n=>P(n,"crtr:llm")))throw new S(403,"scope_missing","missing scope crtr:llm",void 0,void 0,"crtr:llm");return null}if(!Array.isArray(t)||t.some(n=>typeof n!="string"))throw N("scopes must be a list of scope strings","scopes");for(const n of t){try{mt(n)}catch{throw N(`invalid scope ${n}`,"scopes")}if(/^crtr:files:(?:read|rw):app:/.test(n)&&n.split(":")[4]===s.slice(4))throw N(`own-app files cannot narrow a run: ${n}`,"scopes");if(!(/^crtr:memory:(?:read|rw):app:/.test(n)&&n.split(":")[4]===s.slice(4))&&!be(e,n))throw new S(403,"scope_missing",`missing scope ${n}`,void 0,void 0,n)}if(!t.includes("crtr:llm"))throw new S(403,"scope_missing","missing scope crtr:llm",void 0,void 0,"crtr:llm");return t}u(Bn,"requestedScopes");export{X as IDEMPOTENCY_WINDOW_MS,vt as RUN_DEADLINE_MAX_MS,Nt as RUN_DELETE_STORAGE_BUDGET_MS,he as RUN_NAME_MAX,S as RunError,qn as cancelRun,Un as deleteRun,Tn as idempotencyKeyOf,Fn as interruptRun,kn as listRuns,Wn as messageRun,F as ownedRun,ye as parseRunDeadlineMs,Ln as pruneExpiredIdempotencyKeys,Hn as renameRun,Bn as requestedScopes,In as runIsRunning,x as runObject,Cn as startRun,Pn as traceRun,xn as waitRun};
14
+ ORDER BY sequence_number`).all(e,p);return{nodes:_,sessionIds:h,latestSequence:p,completed:D,deltas:v}}),{nodes:o,sessionIds:m,latestSequence:i,completed:r,deltas:u}=n,l=await x(o,async d=>({node_id:d.node_id,parent_node_id:d.parent,conversations:await x(m.get(d.node_id)??[],async _=>{const{header:h,entries:a}=await ee().read(_);if(s==="entries")return{conversation_id:_,header:h,entries:a};const p=xe.inMemory(h.cwd,void 0,[h,...a]),D=dt(p)??[],v=new Map(a.map((w,b)=>[w.id,b])),S=a.filter(w=>w.type==="custom"&&w.customType==="crtr-system-prompt").map(w=>({id:w.id,message:{role:"custom",customType:"crtr-system-prompt",data:"data"in w?w.data:void 0,timestamp:w.timestamp}})),I=a.filter(w=>w.type==="custom_message"&&w.customType==="app_message").map(w=>({id:w.id,message:{role:"custom",customType:"app_message",content:"content"in w?w.content:"",timestamp:w.timestamp}})),U=d.node_id===e?a.find(w=>{if(w.type!=="message"||w.message.role!=="user")return!1;const b=se(a,w.message.timestamp);return b?.type==="runtime"&&b.kind==="kickoff"})?.id:void 0,Se=[...D.filter(({id:w})=>!I.some(b=>b.id===w)),...S,...I].sort((w,b)=>(v.get(w.id)??Number.MAX_SAFE_INTEGER)-(v.get(b.id)??Number.MAX_SAFE_INTEGER)).map(({id:w,message:b})=>{const f=b,T=f.usage,$=f.role==="user"?se(a,Number(f.timestamp)):null,Ne=$?.type==="human"||w===U,L=f.role==="toolResult"?"tool_result":f.role==="bashExecution"?"bash_execution":f.role==="compactionSummary"?"compaction_summary":f.role==="branchSummary"?"branch_summary":f.role==="custom"&&f.customType==="crtr-system-prompt"?"system":f.role==="user"?Ne?"user":"custom":f.role,ve=L==="custom"?f.role==="custom"?f.customType:$?.type==="runtime"?$.kind:"unknown":void 0,be=L==="system"&&f.customType==="crtr-system-prompt"?[{type:"text",text:f.data?.text??""}]:typeof f.content=="string"?[{type:"text",text:f.content}]:Array.isArray(f.content)?f.content.map(M=>M.type==="toolCall"?{type:"tool_call",tool_call_id:M.id,name:M.name,arguments:M.arguments}:M.type==="image"?{type:"image",mime_type:M.mimeType}:M.type==="thinking"?{type:"thinking",text:M.thinking}:M):[];return{entry_id:w,role:L,...L==="custom"?{custom_type:ve}:{},time:typeof f.timestamp=="string"?f.timestamp:new Date(Number(f.timestamp??Date.now())).toISOString(),content:be,...L==="assistant"?{model:f.model,provider:f.provider,stop_reason:f.stopReason,...f.errorMessage?{error_message:f.errorMessage}:{},...T?{usage:{input_tokens:T.input??0,output_tokens:T.output??0,cache_read_tokens:T.cacheRead??0,cache_write_tokens:T.cacheWrite??0,cost_usd:T.cost?.total??0}}:{}}:{},...L==="tool_result"?{tool_call_id:f.toolCallId,tool_name:f.toolName,is_error:f.isError}:{}}})??[];return{conversation_id:_,messages:Se}})})),g=new Map(await Promise.all(l.map(async d=>[d.node_id,await lt(d.node_id)])));for(const d of r){const _=g.get(d.node_id);_!=null&&d.sequence_number>=_&&g.set(d.node_id,null)}const E=new Map;for(const d of u){const _=g.get(d.node_id);if(_==null||d.sequence_number<_)continue;const h=JSON.parse(d.body_json),a=E.get(d.node_id)??{thinking:"",text:""};d.type==="node.thinking.delta"?a.thinking+=h.delta:a.text+=h.delta,E.set(d.node_id,a)}const y=l.map(d=>({...d,in_progress_message:g.get(d.node_id)==null?null:E.get(d.node_id)??{thinking:"",text:""}}));return{run_id:e,latest_sequence:i,nodes:y}}c(Jn,"traceRun");function Yn(e,t,s){if(t===void 0){if(!e.some(n=>P(n,"crtr:llm")))throw new R(403,"scope_missing","missing scope crtr:llm",void 0,void 0,"crtr:llm");return null}if(!Array.isArray(t)||t.some(n=>typeof n!="string"))throw N("scopes must be a list of scope strings","scopes");for(const n of t){try{pt(n)}catch{throw N(`invalid scope ${n}`,"scopes")}if(/^crtr:files:(?:read|rw):app:/.test(n)&&n.split(":")[4]===s.slice(4))throw N(`own-app files cannot narrow a run: ${n}`,"scopes");if(!(/^crtr:memory:(?:read|rw):app:/.test(n)&&n.split(":")[4]===s.slice(4))&&!De(e,n))throw new R(403,"scope_missing",`missing scope ${n}`,void 0,void 0,n)}if(!t.includes("crtr:llm"))throw new R(403,"scope_missing","missing scope crtr:llm",void 0,void 0,"crtr:llm");return t}c(Yn,"requestedScopes");export{z as IDEMPOTENCY_WINDOW_MS,Dt as RUN_DEADLINE_MAX_MS,bt as RUN_DELETE_STORAGE_BUDGET_MS,Re as RUN_NAME_MAX,R as RunError,Pn as cancelRun,Ot as cloudPersonScope,jn as deleteRun,xn as idempotencyKeyOf,$n as interruptRun,qn as listRuns,Un as messageRun,q as ownedRun,he as parseRunDeadlineMs,Wn as pruneExpiredIdempotencyKeys,Bn as renameRun,Yn as requestedScopes,kn as runIsRunning,W as runObject,Fn as startRun,Jn as traceRun,Hn as waitRun};
@@ -1,2 +1,6 @@
1
- /** Registers the Blaxel runner when `BLAXEL_API_KEY` is set; returns whether it did. Any other missing setting throws, so a half-configured control plane fails at boot. */
1
+ /** A fleet role (`CRTR_RUNTIME` set) booted without `BLAXEL_API_KEY`: it would run agent nodes as processes beside the control plane's database URLs and `CRTR_SECRETS_KEY`. */
2
+ export declare class FleetRoleWithoutSandboxRunnerError extends Error {
3
+ constructor();
4
+ }
5
+ /** Registers the Blaxel runner when `BLAXEL_API_KEY` is set; returns whether it did (false only for a role with neither `BLAXEL_API_KEY` nor `CRTR_RUNTIME`). A fleet role without the key, or any other missing setting, throws, so a misconfigured control plane fails at boot. */
2
6
  export declare function registerBlaxelRunnerFromEnv(env?: NodeJS.ProcessEnv): boolean;
@@ -1 +1 @@
1
- var t=Object.defineProperty;var o=(r,e)=>t(r,"name",{value:e,configurable:!0});import{readFileSync as n}from"node:fs";import{join as l}from"node:path";import{emitEvent as a}from"../events/emit.js";import{assetRoot as i}from"../asset-root.js";import{createBlaxelClient as m,blaxelConfigFromEnv as s}from"../runtime/runner/blaxel/client.js";import{registerBlaxelRunner as p}from"../runtime/runner/index.js";import{createBlaxelRunner as f}from"../runtime/runner/blaxel/runner.js";import{blaxelLaunchDepsFromEnv as u,createBlaxelLaunchContext as c}from"./blaxel-launch.js";function C(r=process.env){if(!r.BLAXEL_API_KEY)return!1;const e=n(l(i(),"..","scripts","sandbox-bootstrap.sh"),"utf8");return p(f({client:m(s(r)),launchContext:c(u(r)),bootstrap:o(()=>e,"bootstrap")})),a({level:"info",event:"daemon.blaxel_runner.registered",fields:{workspace:r.BLAXEL_WORKSPACE??null,region:r.BLAXEL_REGION??null}}),!0}o(C,"registerBlaxelRunnerFromEnv");export{C as registerBlaxelRunnerFromEnv};
1
+ var t=Object.defineProperty;var r=(e,o)=>t(e,"name",{value:o,configurable:!0});import{readFileSync as n}from"node:fs";import{join as l}from"node:path";import{emitEvent as s}from"../events/emit.js";import{assetRoot as i}from"../asset-root.js";import{createBlaxelClient as a,blaxelConfigFromEnv as u}from"../runtime/runner/blaxel/client.js";import{registerBlaxelRunner as m}from"../runtime/runner/index.js";import{createBlaxelRunner as p}from"../runtime/runner/blaxel/runner.js";import{blaxelLaunchDepsFromEnv as E,createBlaxelLaunchContext as R}from"./blaxel-launch.js";class c extends Error{static{r(this,"FleetRoleWithoutSandboxRunnerError")}constructor(){super("CRTR_RUNTIME is set but BLAXEL_API_KEY is not: a fleet role needs a sandbox runner, and without one it would run agent nodes inside the control-plane process environment. Set BLAXEL_API_KEY or unset CRTR_RUNTIME."),this.name="FleetRoleWithoutSandboxRunnerError"}}function C(e=process.env){if(!e.BLAXEL_API_KEY){if(e.CRTR_RUNTIME)throw new c;return!1}const o=n(l(i(),"..","scripts","sandbox-bootstrap.sh"),"utf8");return m(p({client:a(u(e)),launchContext:R(E(e)),bootstrap:r(()=>o,"bootstrap")})),s({level:"info",event:"daemon.blaxel_runner.registered",fields:{workspace:e.BLAXEL_WORKSPACE??null,region:e.BLAXEL_REGION??null}}),!0}r(C,"registerBlaxelRunnerFromEnv");export{c as FleetRoleWithoutSandboxRunnerError,C as registerBlaxelRunnerFromEnv};