@north-light/cli 0.3.457 → 0.3.459
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/contract.d.ts +1 -1
- package/dist/api/contract.js +1 -1
- package/dist/api/dto/profiles.d.ts +3 -1
- package/dist/builtin-memory/crouter-sdk/release-notes.md +6 -0
- package/dist/clients/attach/session/profile-files.js +1 -1
- package/dist/clients/attach/viewer.js +3 -3
- package/dist/commands/profile/new.js +1 -1
- package/dist/commands/profile/show.js +1 -1
- package/dist/commands/sys/panels/models-panel.js +1 -1
- package/dist/core/command-plugins/compose.js +1 -1
- package/dist/core/command-plugins/discovery.js +1 -1
- package/dist/core/config.js +1 -1
- package/dist/core/grants/on-sync.js +1 -1
- package/dist/core/layout-migrate/stored-paths.js +3 -3
- package/dist/core/layout-migrate/stores.js +1 -1
- package/dist/core/layout.d.ts +2 -2
- package/dist/core/layout.js +2 -2
- package/dist/core/profiles/manifest.d.ts +1 -1
- package/dist/core/profiles/manifest.js +1 -1
- package/dist/core/profiles/relocate.d.ts +8 -0
- package/dist/core/profiles/relocate.js +1 -0
- package/dist/core/providers/bundles.js +2 -2
- package/dist/core/providers/call.d.ts +2 -2
- package/dist/core/providers/call.js +1 -1
- package/dist/core/providers/roots.d.ts +19 -0
- package/dist/core/providers/roots.js +1 -0
- package/dist/core/providers/token-cache.d.ts +10 -2
- package/dist/core/providers/token-cache.js +1 -1
- package/dist/core/scoped-state/migrate.js +1 -1
- package/dist/core/scoped-state/profiles.js +1 -1
- package/dist/core/scoped-state/settings.js +1 -1
- package/dist/core/spaces/space.js +2 -2
- package/dist/core/storage/download.js +1 -1
- package/dist/daemon/api/handlers/providers.js +1 -1
- package/dist/daemon/api/map.js +2 -2
- package/dist/daemon/crtrd.js +8 -7
- package/dist/migrations/004-canvas-documents/index.js +2 -2
- package/dist/migrations/004-canvas-documents/legacy/types.d.ts +1 -1
- package/docs/sdk/release-notes.md +6 -0
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +12 -12
package/dist/core/layout.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var $=Object.defineProperty;var n=(r,o)=>$(r,"name",{value:o,configurable:!0});import{existsSync as i,mkdirSync as d,readFileSync as S,statSync as R}from"node:fs";import{homedir as _,userInfo as g}from"node:os";import{basename as x,dirname as f,isAbsolute as A,join as t,resolve as p}from"node:path";import{envHomeOverride as T}from"../shared/env.js";import{readConfig as O}from"./config.js";import{publishFileExclusive as z,syncDirectory as h}from"./fs-utils.js";const
|
|
2
|
-
`,384)}n(W,"initializeEmptyLayout");export{
|
|
1
|
+
var $=Object.defineProperty;var n=(r,o)=>$(r,"name",{value:o,configurable:!0});import{existsSync as i,mkdirSync as d,readFileSync as S,statSync as R}from"node:fs";import{homedir as _,userInfo as g}from"node:os";import{basename as x,dirname as f,isAbsolute as A,join as t,resolve as p}from"node:path";import{envHomeOverride as T}from"../shared/env.js";import{readConfig as O}from"./config.js";import{publishFileExclusive as z,syncDirectory as h}from"./fs-utils.js";const l="1";function s(){const r=p(T()||(process.platform==="linux"?"/":t(_(),".crouter")));if(C()&&r===L())throw new Error(`refusing to use the account's real crouter root ${r} from a node --test process; point CRTR_HOME (not only HOME) at a temporary directory`);return r}n(s,"layoutRoot");function C(){return process.env.NODE_TEST_CONTEXT!==void 0||process.execArgv.includes("--test")}n(C,"runningUnderNodeTest");let c;function L(){if(c===void 0)try{c=p(g().homedir,".crouter")}catch{c=null}return c}n(L,"accountLayoutRoot");function u(){return t(s(),"runtime")}n(u,"runtimeRoot");function v(){return t(s(),"user")}n(v,"userRoot");function w(){return t(s(),"apps")}n(w,"appsRoot");function y(){return t(u(),"secrets")}n(y,"secretsDir");function Y(r,o){if(!/^[a-z0-9]+(?:-[a-z0-9]+)*-[0-9a-f]{8}$/.test(o))throw new Error(`invalid profile id: ${o}`);const e=m(r);return{dir:t(e.profiles,o),env:t(y(),"env",x(e.root),o),home:e.home}}n(Y,"profileSpace");function E(){return process.platform==="linux"?"/run/crtr":u()}n(E,"socketDir");function a(){return t(u(),"layout")}n(a,"layoutVersionFile");function k(){const r=O("user").paths?.user_files??(process.platform==="linux"?t(v(),"files"):f(s()));if(!A(r))throw new Error("paths.user_files must be absolute");return p(r)}n(k,"userFilesRoot");function m(r){const o=r.startsWith("app:")?r.slice(4):r;if(!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(o)||o==="."||o==="..")throw new Error(`invalid app id: ${r}`);const e=t(w(),o);return{root:e,profiles:t(e,"profiles"),conversations:t(e,"conversations"),nodes:t(e,"nodes"),files:t(e,"files"),home:t(e,"home"),cache:t(e,"cache")}}n(m,"appSpace");function q(r,o){if(!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(o))throw new Error(`invalid cron id: ${o}`);return t(m(r).root,"crons",o)}n(q,"cronFolder");function P(r){const o=m(r);return process.platform==="linux"?o.home:x(o.root)==="terminal"?k():o.home}n(P,"appProcessHome");function F(r){const o=Array.from(r.subarray(0,32),e=>e===92?"\\\\":e===34?'\\"':e===10?"\\n":e===13?"\\r":e===9?"\\t":e>=32&&e<127?String.fromCharCode(e):`\\x${e.toString(16).padStart(2,"0")}`).join("");return`${r.length} bytes, preview "${o}"${r.length>32?"\u2026":""}`}n(F,"describeLayoutMarker");function M(r){const o=S(r);if(o.toString("utf8").trim()!==l)throw new Error(`unsupported runtime layout version: ${r} holds ${F(o)}; expected "${l}"`)}n(M,"assertSupportedLayoutMarker");function N(){if(process.env.CRTR_LAYOUT_REFUSE==="1")return!0;if(i(a()))return M(a()),!1;const r=s(),o=process.platform==="linux"&&r==="/"&&i("/volume")?"/volume":r;return i(t(o,"canvas"))||i(t(o,"canvas.db"))?!0:i(t(u(),"canvas.db"))}n(N,"layoutNeedsMigration");function W(){if(N())throw new Error("layout_migration_required");for(const r of[u(),v(),w(),y()]){const o=d(r,{recursive:!0,mode:448});if(o!==void 0){for(let e=r;h(e),e!==o;e=f(e));h(f(o))}}if(d(E(),{recursive:!0,mode:493}),process.platform==="linux"&&(R(E()).mode&511)!==493)throw new Error("launcher socket directory must be mode 0755");i(a())||z(a(),`${l}
|
|
2
|
+
`,384)}n(W,"initializeEmptyLayout");export{l as LAYOUT_VERSION,P as appProcessHome,m as appSpace,w as appsRoot,M as assertSupportedLayoutMarker,q as cronFolder,F as describeLayoutMarker,W as initializeEmptyLayout,N as layoutNeedsMigration,s as layoutRoot,a as layoutVersionFile,Y as profileSpace,u as runtimeRoot,y as secretsDir,E as socketDir,k as userFilesRoot,v as userRoot};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ProfileManifest } from '../../types.js';
|
|
2
2
|
import type { DeliveryLimits, ProfileProject, ProfileProjectMemory } from '../../api/dto/profiles.js';
|
|
3
3
|
export declare const ROOT_PROFILE_ID = "root-00000000";
|
|
4
|
-
export declare function
|
|
4
|
+
export declare function profileDir(profileId: string): string;
|
|
5
5
|
export declare function assertProfileProjects(entries: unknown): asserts entries is ProfileProject[];
|
|
6
6
|
export declare function assertDeliveryLimits(value: unknown): asserts value is DeliveryLimits;
|
|
7
7
|
export interface ProfileEntry {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var Y=Object.defineProperty;var o=(e,t)=>Y(e,"name",{value:t,configurable:!0});import{randomUUID as G}from"node:crypto";import{existsSync as W,realpathSync as b,rmSync as S,statSync as B}from"node:fs";import{join as A,resolve as E}from"node:path";import{SCHEMA_VERSION as R}from"../../types.js";import{profileSpace as g}from"../layout.js";import{PROFILE_PROJECT_MEMORY_VALUES as O}from"../../api/dto/profiles.js";import{userScopeRoot as c}from"../scope.js";import{ensureDir as k,nowIso as m}from"../fs-utils.js";import{deleteStoredProfile as Q,getStoredProfile as h,insertStoredProfile as M,listStoredProfiles as X,pausedProfileIds as I,profilePaused as ee,updateStoredProfile as te}from"../scoped-state/profiles.js";import{usage as f,notFound as d,ambiguous as D,general as L}from"../errors.js";import{withExclusiveDirectoryLock as re}from"../exclusive-lock.js";import{fuzzyMatchProfile as ne}from"./fuzzy-match.js";const N=/^[a-z0-9]+(?:-[a-z0-9]+)*-[0-9a-f]{8}$/,p="root-00000000",oe="root";function P(e){return e.grantee??"app:terminal"}o(P,"profileApp");function w(e,t){k(g(P(t),e).memory)}o(w,"scaffoldProfile");function y(e){if(!N.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(y,"assertSafeProfileId");function Ee(e){y(e);const t=h(c(),e);if(t===null)throw d(`profile not found: ${e}`,{received:e});return g(P(t.manifest),e).memory}o(Ee,"profileMemoryDir");function z(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(z,"assertProfileProjects");function ie(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(ie,"assertDeliveryLimits");function ae(){return{entries:X(c()),unreadableIds:[]}}o(ae,"enumerateProfiles");function x(){return ae().entries}o(x,"listProfiles");function se(e){const t=x(),r=J(t,e,$);return r?{kind:"resolved",entry:r}:{kind:"absent"}}o(se,"lookupProfile");function C(e){y(e);const t=h(c(),e);if(t!==null)return t;throw d(`profile not found: ${e}`,{received:e,next:"Run `crtr profile list` to see available profiles."})}o(C,"loadExactProfileManifest");const $=o(e=>({profileId:e.profileId,name:e.manifest.name}),"entryCandidate");function J(e,t,r){if(N.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 D(`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(J,"findExactProfile");function T(e,t,r){const n=e.map(i=>`${r(i).profileId} (${r(i).name})`);return d(`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(T,"profileNotFound");function fe(e){const t=se(e);if(t.kind==="resolved")return t.entry;throw T(x(),e,$)}o(fe,"loadProfileManifest");function Re(e){return le(x(),e,$)}o(Re,"resolveProfileOperand");function le(e,t,r){const n=J(e,t,r);if(n!==null)return n;const{match:i,tied:a}=ne(t,e.map(r));if(i!==null){const s=e.find(l=>r(l).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(l=>`${l.profileId} (${l.name})`);throw D(`"${t}" is equally close to ${a.length} profiles: ${s.join(", ")}`,{received:t,candidates:a.map(l=>l.profileId),next:`Re-run with one exact profile id or name: ${s.join(", ")}.`})}throw T(e,t,r)}o(le,"matchProfileOperand");function U(){return A(c(),"profile-locks")}o(U,"locksDir");function v(e,t){return y(e),k(U()),re(A(U(),`${e}.lock`),t,{timeoutError:o(()=>L(`timed out waiting for the profile lock: ${e}`,{next:"Retry \u2014 another `crtr profile` command may be running concurrently on this profile."}),"timeoutError")})}o(v,"withProfileManifestLock");function F(e){const t=E(e);if(!W(t)||!B(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(F,"resolveExistingProjectDir");function K(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(K,"dedupeProjects");function ce(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(ce,"slugify");function ue(e){const t=ce(e);for(let r=0;r<20;r++){const n=G().replace(/-/g,"").slice(0,8),i=`${t}-${n}`;if(h(c(),i)===null)return i}throw L(`failed to generate a unique profile id for "${e}" after 20 attempts`)}o(ue,"generateProfileId");function Oe(){return v(p,()=>{const e=h(c(),p);if(e!==null)return w(p,e.manifest),e;const t={schema_version:R,name:oe,projects:[],grantee:"app:terminal",paused_at:null,created_at:m(),last_used_at:m()};return w(p,t),M(c(),{profileId:p,manifest:t}),{profileId:p,manifest:t}})}o(Oe,"ensureRootProfile");const q=/^[A-Za-z0-9][A-Za-z0-9_-]*$/;function H(e){return`CRTR_PROFILE_META_${e.toUpperCase().replace(/[^A-Z0-9]+/g,"_")}`}o(H,"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(!q.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=H(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 Z(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"||!q.test(n)||i.includes("\0"))continue;const a=H(n);r.has(a)||(r.add(a),t[n]=i)}return t}o(Z,"sanitizeProfileMetadata");function de(e,t=[],r={}){const n=e.trim();if(n==="")throw f("profile name must not be empty");r.metadata!==void 0&&_(r.metadata),z(t);const i=K(t.map(s=>({path:F(s.path)}))),a=ue(n);return v(a,()=>{const s={schema_version:R,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 w(a,s),M(c(),{profileId:a,manifest:s}),{profileId:a,manifest:s}})}o(de,"createProfile");function ke(e){if(!e.startsWith("app:")||e.length<=4)throw f("an app grantee is required");const t=`app-${e.slice(4)}`,r=x().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]:de(t,[],{grantee:e})}o(ke,"ensureAppProfile");function V(e){if(y(e),h(c(),e)===null)throw d(`profile not found: ${e}`,{received:e,next:"Run `crtr profile list` to see available profiles."})}o(V,"requireExistingProfile");function u(e,t){return V(e),v(e,()=>{const r=te(c(),e,t);if(r===null)throw d(`profile not found: ${e}`,{received:e});return r})}o(u,"mutateManifest");function Me(e){return u(e,t=>({...t,last_used_at:m()}))}o(Me,"updateProfileLastUsed");function pe(e){if(e==null||e==="")return null;try{return fe(e)}catch{return null}}o(pe,"loadProfileManifestOrNull");function De(e){if(e==null||e==="")return!1;try{return y(e),ee(c(),e)}catch{return!1}}o(De,"isProfilePaused");function Le(){return I(c())}o(Le,"pausedCanvasProfiles");function me(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(me,"assertProfileEntryActive");function Ne(e){const t=pe(e);return t===null?null:(me(t),t)}o(Ne,"assertProfileActive");function ze(e){return u(e,t=>({...t,paused_at:t.paused_at??m()}))}o(ze,"pauseProfile");function Ce(e){let t=null;return{...u(e,n=>(t=n.paused_at,{...n,paused_at:null})),pausedAt:t}}o(Ce,"resumeProfile");function Je(e,t){return u(e,r=>({...r,default_kind:t}))}o(Je,"setProfileDefaultKind");function Te(e,t,r=[]){return _(t),u(e,n=>{const i={...Z(n.metadata),...t};for(const l of r)delete i[l];_(i);const{metadata:a,...s}=n;return{...s,...Object.keys(i).length>0?{metadata:i}:{}}})}o(Te,"updateProfileMetadata");function Ue(e){if(e===null||e==="")return{};try{return Z(C(e).manifest.metadata)}catch{return{}}}o(Ue,"readProfileMetadata");function Fe(e,t){const r=t.trim();if(r==="")throw f("profile name must not be empty");return u(e,i=>({...i,name:r}))}o(Fe,"renameProfile");function Ke(e,t){z([{path:t}]);const r=F(t);return u(e,i=>({...i,projects:K([...i.projects,{path:r}])}))}o(Ke,"addProfileProject");function qe(e,t,r){return ie({[t]:r}),u(e,n=>{const{[t]:i,...a}=n.delivery_limits??{},s=r==="content"?a:{...a,[t]:r},{delivery_limits:l,...j}=n;return Object.keys(s).length===0?j:{...j,delivery_limits:s}})}o(qe,"setProfileDeliveryLimit");function He(e,t){const r=E(t);let n=r;try{n=b(r)}catch{}return u(e,a=>{const s=a.projects.filter(l=>l.path!==n&&l.path!==r);if(s.length===a.projects.length)throw d(`project not found on profile: ${t}`,{received:t,next:`Current projects: ${a.projects.length>0?a.projects.map(l=>l.path).join(", "):"(none)"}.`});return{...a,projects:s}})}o(He,"removeProfileProject");function Ze(e){V(e),v(e,()=>{const t=g(P(C(e).manifest),e);if(!Q(c(),e))throw d(`profile not found: ${e}`,{received:e});S(t.memory,{recursive:!0,force:!0}),S(t.env,{force:!0})})}o(Ze,"deleteProfile");export{p as ROOT_PROFILE_ID,Ke as addProfileProject,ie as assertDeliveryLimits,Ne as assertProfileActive,me as assertProfileEntryActive,_ as assertProfileMetadata,z as assertProfileProjects,de as createProfile,Ze as deleteProfile,ke as ensureAppProfile,Oe as ensureRootProfile,De as isProfilePaused,x as listProfiles,C as loadExactProfileManifest,fe as loadProfileManifest,se as lookupProfile,le as matchProfileOperand,H as metadataEnvName,ze as pauseProfile,Le as pausedCanvasProfiles,Ee as profileMemoryDir,Ue as readProfileMetadata,He as removeProfileProject,Fe as renameProfile,Re as resolveProfileOperand,Ce as resumeProfile,Z as sanitizeProfileMetadata,Je as setProfileDefaultKind,qe as setProfileDeliveryLimit,Me as updateProfileLastUsed,Te as updateProfileMetadata,v as withProfileManifestLock};
|
|
1
|
+
var Y=Object.defineProperty;var o=(e,t)=>Y(e,"name",{value:t,configurable:!0});import{randomUUID as G}from"node:crypto";import{existsSync as W,realpathSync as b,rmSync as S,statSync as B}from"node:fs";import{join as A,resolve as E}from"node:path";import{SCHEMA_VERSION as R}from"../../types.js";import{profileSpace as g}from"../layout.js";import{PROFILE_PROJECT_MEMORY_VALUES as O}from"../../api/dto/profiles.js";import{userScopeRoot as c}from"../scope.js";import{ensureDir as k,nowIso as m}from"../fs-utils.js";import{deleteStoredProfile as Q,getStoredProfile as h,insertStoredProfile as M,listStoredProfiles as X,pausedProfileIds as I,profilePaused as ee,updateStoredProfile as te}from"../scoped-state/profiles.js";import{usage as f,notFound as d,ambiguous as D,general as L}from"../errors.js";import{withExclusiveDirectoryLock as re}from"../exclusive-lock.js";import{fuzzyMatchProfile as ne}from"./fuzzy-match.js";const N=/^[a-z0-9]+(?:-[a-z0-9]+)*-[0-9a-f]{8}$/,p="root-00000000",oe="root";function P(e){return e.grantee??"app:terminal"}o(P,"profileApp");function w(e,t){k(g(P(t),e).dir)}o(w,"scaffoldProfile");function y(e){if(!N.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(y,"assertSafeProfileId");function Ee(e){y(e);const t=h(c(),e);if(t===null)throw d(`profile not found: ${e}`,{received:e});return g(P(t.manifest),e).dir}o(Ee,"profileDir");function z(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(z,"assertProfileProjects");function ie(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(ie,"assertDeliveryLimits");function ae(){return{entries:X(c()),unreadableIds:[]}}o(ae,"enumerateProfiles");function x(){return ae().entries}o(x,"listProfiles");function se(e){const t=x(),r=J(t,e,$);return r?{kind:"resolved",entry:r}:{kind:"absent"}}o(se,"lookupProfile");function C(e){y(e);const t=h(c(),e);if(t!==null)return t;throw d(`profile not found: ${e}`,{received:e,next:"Run `crtr profile list` to see available profiles."})}o(C,"loadExactProfileManifest");const $=o(e=>({profileId:e.profileId,name:e.manifest.name}),"entryCandidate");function J(e,t,r){if(N.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 D(`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(J,"findExactProfile");function T(e,t,r){const n=e.map(i=>`${r(i).profileId} (${r(i).name})`);return d(`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(T,"profileNotFound");function fe(e){const t=se(e);if(t.kind==="resolved")return t.entry;throw T(x(),e,$)}o(fe,"loadProfileManifest");function Re(e){return le(x(),e,$)}o(Re,"resolveProfileOperand");function le(e,t,r){const n=J(e,t,r);if(n!==null)return n;const{match:i,tied:a}=ne(t,e.map(r));if(i!==null){const s=e.find(l=>r(l).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(l=>`${l.profileId} (${l.name})`);throw D(`"${t}" is equally close to ${a.length} profiles: ${s.join(", ")}`,{received:t,candidates:a.map(l=>l.profileId),next:`Re-run with one exact profile id or name: ${s.join(", ")}.`})}throw T(e,t,r)}o(le,"matchProfileOperand");function U(){return A(c(),"profile-locks")}o(U,"locksDir");function v(e,t){return y(e),k(U()),re(A(U(),`${e}.lock`),t,{timeoutError:o(()=>L(`timed out waiting for the profile lock: ${e}`,{next:"Retry \u2014 another `crtr profile` command may be running concurrently on this profile."}),"timeoutError")})}o(v,"withProfileManifestLock");function F(e){const t=E(e);if(!W(t)||!B(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(F,"resolveExistingProjectDir");function K(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(K,"dedupeProjects");function ce(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(ce,"slugify");function ue(e){const t=ce(e);for(let r=0;r<20;r++){const n=G().replace(/-/g,"").slice(0,8),i=`${t}-${n}`;if(h(c(),i)===null)return i}throw L(`failed to generate a unique profile id for "${e}" after 20 attempts`)}o(ue,"generateProfileId");function Oe(){return v(p,()=>{const e=h(c(),p);if(e!==null)return w(p,e.manifest),e;const t={schema_version:R,name:oe,projects:[],grantee:"app:terminal",paused_at:null,created_at:m(),last_used_at:m()};return w(p,t),M(c(),{profileId:p,manifest:t}),{profileId:p,manifest:t}})}o(Oe,"ensureRootProfile");const q=/^[A-Za-z0-9][A-Za-z0-9_-]*$/;function H(e){return`CRTR_PROFILE_META_${e.toUpperCase().replace(/[^A-Z0-9]+/g,"_")}`}o(H,"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(!q.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=H(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 Z(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"||!q.test(n)||i.includes("\0"))continue;const a=H(n);r.has(a)||(r.add(a),t[n]=i)}return t}o(Z,"sanitizeProfileMetadata");function de(e,t=[],r={}){const n=e.trim();if(n==="")throw f("profile name must not be empty");r.metadata!==void 0&&_(r.metadata),z(t);const i=K(t.map(s=>({path:F(s.path)}))),a=ue(n);return v(a,()=>{const s={schema_version:R,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 w(a,s),M(c(),{profileId:a,manifest:s}),{profileId:a,manifest:s}})}o(de,"createProfile");function ke(e){if(!e.startsWith("app:")||e.length<=4)throw f("an app grantee is required");const t=`app-${e.slice(4)}`,r=x().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]:de(t,[],{grantee:e})}o(ke,"ensureAppProfile");function V(e){if(y(e),h(c(),e)===null)throw d(`profile not found: ${e}`,{received:e,next:"Run `crtr profile list` to see available profiles."})}o(V,"requireExistingProfile");function u(e,t){return V(e),v(e,()=>{const r=te(c(),e,t);if(r===null)throw d(`profile not found: ${e}`,{received:e});return r})}o(u,"mutateManifest");function Me(e){return u(e,t=>({...t,last_used_at:m()}))}o(Me,"updateProfileLastUsed");function pe(e){if(e==null||e==="")return null;try{return fe(e)}catch{return null}}o(pe,"loadProfileManifestOrNull");function De(e){if(e==null||e==="")return!1;try{return y(e),ee(c(),e)}catch{return!1}}o(De,"isProfilePaused");function Le(){return I(c())}o(Le,"pausedCanvasProfiles");function me(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(me,"assertProfileEntryActive");function Ne(e){const t=pe(e);return t===null?null:(me(t),t)}o(Ne,"assertProfileActive");function ze(e){return u(e,t=>({...t,paused_at:t.paused_at??m()}))}o(ze,"pauseProfile");function Ce(e){let t=null;return{...u(e,n=>(t=n.paused_at,{...n,paused_at:null})),pausedAt:t}}o(Ce,"resumeProfile");function Je(e,t){return u(e,r=>({...r,default_kind:t}))}o(Je,"setProfileDefaultKind");function Te(e,t,r=[]){return _(t),u(e,n=>{const i={...Z(n.metadata),...t};for(const l of r)delete i[l];_(i);const{metadata:a,...s}=n;return{...s,...Object.keys(i).length>0?{metadata:i}:{}}})}o(Te,"updateProfileMetadata");function Ue(e){if(e===null||e==="")return{};try{return Z(C(e).manifest.metadata)}catch{return{}}}o(Ue,"readProfileMetadata");function Fe(e,t){const r=t.trim();if(r==="")throw f("profile name must not be empty");return u(e,i=>({...i,name:r}))}o(Fe,"renameProfile");function Ke(e,t){z([{path:t}]);const r=F(t);return u(e,i=>({...i,projects:K([...i.projects,{path:r}])}))}o(Ke,"addProfileProject");function qe(e,t,r){return ie({[t]:r}),u(e,n=>{const{[t]:i,...a}=n.delivery_limits??{},s=r==="content"?a:{...a,[t]:r},{delivery_limits:l,...j}=n;return Object.keys(s).length===0?j:{...j,delivery_limits:s}})}o(qe,"setProfileDeliveryLimit");function He(e,t){const r=E(t);let n=r;try{n=b(r)}catch{}return u(e,a=>{const s=a.projects.filter(l=>l.path!==n&&l.path!==r);if(s.length===a.projects.length)throw d(`project not found on profile: ${t}`,{received:t,next:`Current projects: ${a.projects.length>0?a.projects.map(l=>l.path).join(", "):"(none)"}.`});return{...a,projects:s}})}o(He,"removeProfileProject");function Ze(e){V(e),v(e,()=>{const t=g(P(C(e).manifest),e);if(!Q(c(),e))throw d(`profile not found: ${e}`,{received:e});S(t.dir,{recursive:!0,force:!0}),S(t.env,{force:!0})})}o(Ze,"deleteProfile");export{p as ROOT_PROFILE_ID,Ke as addProfileProject,ie as assertDeliveryLimits,Ne as assertProfileActive,me as assertProfileEntryActive,_ as assertProfileMetadata,z as assertProfileProjects,de as createProfile,Ze as deleteProfile,ke as ensureAppProfile,Oe as ensureRootProfile,De as isProfilePaused,x as listProfiles,C as loadExactProfileManifest,fe as loadProfileManifest,se as lookupProfile,le as matchProfileOperand,H as metadataEnvName,ze as pauseProfile,Le as pausedCanvasProfiles,Ee as profileDir,Ue as readProfileMetadata,He as removeProfileProject,Fe as renameProfile,Re as resolveProfileOperand,Ce as resumeProfile,Z as sanitizeProfileMetadata,Je as setProfileDefaultKind,qe as setProfileDeliveryLimit,Me as updateProfileLastUsed,Te as updateProfileMetadata,v as withProfileManifestLock};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Profile folders used to live at `<app space>/memory/profiles/<id>`; they are
|
|
3
|
+
* `<app space>/profiles/<id>` now, because documents live in the canvas and the
|
|
4
|
+
* folder only anchors the profile's settings row. Idempotent: it moves what is
|
|
5
|
+
* still in the old place, then points every stored profile's settings-owner root
|
|
6
|
+
* at its current folder. Returns one line per thing it did or could not do.
|
|
7
|
+
*/
|
|
8
|
+
export declare function relocateProfileFolders(): string[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var g=Object.defineProperty;var p=(e,s)=>g(e,"name",{value:s,configurable:!0});import{existsSync as f,mkdirSync as w,readdirSync as m,renameSync as d,rmdirSync as l,statSync as u}from"node:fs";import{join as c}from"node:path";import{appSpace as y,appsRoot as h}from"../layout.js";import{userScopeRoot as S}from"../scope.js";import{prepared as $}from"../sqlite-statements.js";import{withScopedWrite as R}from"../scoped-state/db.js";import{canonicalRoot as D,userStore as _}from"../scoped-state/paths.js";import{listStoredProfiles as T}from"../scoped-state/profiles.js";function E(e,s,t){for(const o of m(e)){const r=c(e,o),i=c(s,o);if(!f(i)){d(r,i);continue}if(u(r).isDirectory()&&u(i).isDirectory()){E(r,i,t);try{l(r)}catch{t.push(r)}}else t.push(r)}}p(E,"mergeInto");function I(){const e=[];if(f(h()))for(const t of m(h(),{withFileTypes:!0})){if(!t.isDirectory()||!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(t.name))continue;const o=y(t.name),r=c(o.root,"memory","profiles");if(f(r)){if(!f(o.profiles))d(r,o.profiles),e.push(`profile folders: moved ${r} to ${o.profiles}`);else{const i=[];E(r,o.profiles,i);try{l(r)}catch{}e.push(`profile folders: merged ${r} into ${o.profiles}`);for(const n of i)e.push(`profile folders: left ${n} in place; ${o.profiles} already holds that name`)}try{l(c(o.root,"memory"))}catch{}}}const s=T(S());return s.length>0&&R(_(S()),t=>{for(const{profileId:o,manifest:r}of s){const i=c(y(r.grantee??"app:terminal").profiles,o);w(i,{recursive:!0});const n=D(i),a=$(t,"SELECT root FROM scope_owners WHERE owner_key=?").get(`profile:${o}`);a!==void 0&&a.root!==n&&($(t,"UPDATE scope_owners SET root=? WHERE owner_key=?").run(n,`profile:${o}`),e.push(`profile folders: settings root of ${o} is now ${n}`))}}),e}p(I,"relocateProfileFolders");export{I as relocateProfileFolders};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
2
|
-
`)
|
|
1
|
+
var se=Object.defineProperty;var i=(e,n)=>se(e,"name",{value:n,configurable:!0});import{existsSync as w,readdirSync as z,readFileSync as S,rmSync as $,statSync as k}from"node:fs";import{join as u}from"node:path";import{validateCommandManifest as de}from"../../api/command-manifest/manifest.js";import{crtrHome as F}from"../canvas/paths.js";import{withCanvasWrite as G}from"../canvas/db.js";import{bundleVersion as ue,isInstalledGeneration as ce,recordCheckedGeneration as fe}from"../command-plugins/bundle.js";import{readConfig as q,readState as le,updateConfig as pe,updateState as me}from"../config.js";import{emitEvent as m}from"../events/emit.js";import{withExclusiveDirectoryLockAsync as V}from"../exclusive-lock.js";import{atomicWriteText as x,ensureDir as T,pathExists as ve}from"../fs-utils.js";import{reconcilePackageDocuments as Z}from"../graph/package-docs.js";import{readPluginManifest as C}from"../manifest.js";import{bundleSwapLockPath as L}from"../plugin-swap-lock.js";import{pluginsDir as he,userScopeRoot as J}from"../scope.js";import{listInstalledPlugins as ge}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}",we=new RegExp(`^(${A}):(${A})$`),K=new RegExp(`^${A}$`),$e=3e4,Q=3e4;function X(e){const n=new Set;for(const r of e)if(r.status==="active")for(const{scope:t}of r.resolved_scopes){const o=we.exec(t);o&&o[1]!=="crtr"&&n.add(o[1])}return[...n].sort()}i(X,"providerIdsFromGrants");function v(e){return u(he("user"),e)}i(v,"pluginRoot");function R(){return u(F(),"providers")}i(R,"providersRoot");function Y(e,n){return u(R(),e,`v${n}`)}i(Y,"majorDir");function D(e){if(!K.test(e))throw new c("not_found",`"${e}" is not a provider id`,{param:"provider"})}i(D,"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(/\/+$/,"")}i(E,"directoryBase");async function ee(e,n){const r=`${E()}/providers/${encodeURIComponent(e)}/plugin${n.version!==void 0?`?version=${n.version}`:""}`;let t;try{t=await fetch(r,{headers:{Accept:"application/x-tar",...n.ifNoneMatch!==void 0?{"If-None-Match":n.ifNoneMatch}:{}},signal:AbortSignal.timeout($e)})}catch(s){return{status:"failed",message:`could not reach ${r}: ${s instanceof Error?s.message:String(s)}`}}if(t.status===304)return n.ifNoneMatch===void 0?{status:"failed",message:`HTTP 304 from ${r} without a conditional request`}:{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)return await t.body?.cancel().catch(()=>{}),{status:"failed",message:`HTTP ${t.status} from ${r}`};let o;try{o=new Uint8Array(await t.arrayBuffer())}catch(s){return{status:"failed",message:`reading ${r} failed: ${s instanceof Error?s.message:String(s)}`}}const a=t.headers.get("etag");return{status:200,raw:o,...a!==null?{etag:a}:{}}}i(ee,"fetchProviderBundle");const I=new WeakSet;class M extends Error{static{i(this,"BundleInvalid")}issues;constructor(n,r){super(n),this.issues=r}}function ne(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(", ")}`}i(ne,"wrongBundle");function B(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})}i(B,"logInvalid");function re(e,n,r){m({level:"warn",event:"daemon.providers.bundle_fetch_failed",fields:{provider:e,...n!==void 0?{major:n}:{},message:r}})}i(re,"logFailure");async function te(){return import("../../commands/pkg/plugin-manage.js")}i(te,"pluginManage");const y=new Map,b=i(e=>`${F()}\0${e}`,"installKey");function ye(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"}i(ye,"publicInstallCause");function be(e){if(h(e)===void 0)return y.get(b(e))??{status:"installing"}}i(be,"providerInstallState");function _e(e){return new c("provider_unavailable",`provider "${e}" unavailable: ${be(e)?.error??"bundle installation pending"}`)}i(_e,"unavailableProviderError");function h(e){const n=C(v(e));if(!(n?.provider===void 0||n.transport?.kind!=="http"&&n.transport?.kind!=="machine")&&ve(u(v(e),"commands.json"))){try{if(k(u(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}}}}i(h,"installedManifest");async function N(e,n,r=!1){const t=J();h(e)===void 0&&y.set(b(e),{status:"installing"});try{return T(u(t,"tmp")),await V(L(t,e),async()=>{const o=v(e),a=C(o);if(a!==null&&a.provider===void 0)throw new c("provider_unavailable",`plugin "${e}" is installed and is not the directory's provider bundle`);const s=h(e)!==void 0;if(s&&r)return"unchanged";const f=s&&n?le("user").plugins[e]?.etag:void 0,d=await ee(e,f!==void 0?{ifNoneMatch:f}:{});if(d.status===304)return"unchanged";if(d.status===404||d.status===410)throw new c("not_found",`the directory serves no provider "${e}" (${d.message})`,{param:"provider"});if(d.status==="failed")throw new c("provider_unavailable",`provider "${e}" bundle fetch failed: ${d.message}`);const l=ue(d.raw);if(s&&ce(e,"user",o,l))return fe("user",e,d.etag,{swapped:!1}),"unchanged";const{installPluginArchive:g}=await te();try{await g(e,d.raw,{scope:"user",scopeRootPath:t,root:o,version:l,etag:d.etag,enable:!s,bundle:{endpoint:`${E()}/providers/${encodeURIComponent(e)}/plugin`},transport:{kind:"http",endpoint:E()},accept:i(p=>ne(e,void 0,p),"accept")})}catch(p){B(e,void 0,p);const W=new c("not_found",`provider "${e}" served an invalid bundle: ${p instanceof Error?p.message:String(p)}`,{param:"provider"});throw I.add(W),W}try{G(p=>Z(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:s}}),y.delete(b(e)),s?"updated":"installed"},{timeoutMs:Q,timeoutError:i(()=>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:ye(o)}),o instanceof c?o:(m({level:"error",event:"daemon.providers.bundle_install_failed",fields:{provider:e},error:o}),_e(e))}}i(N,"refreshInstalled");async function Je(e){D(e),h(e)===void 0&&await N(e,!1,!0)}i(Je,"ensureProviderBundle");async function Ke(e,n){return D(e),n.version!==void 0&&h(e)?.provider.major!==n.version?H(e,n.version,n.conditional):N(e,n.conditional)}i(Ke,"refreshProviderBundle");async function oe(e,n,r){const{validatePluginArchive:t}=await te(),o=await t(r);if(o.bundle===void 0)throw new M(`provider "${e}" major ${n} bundle is invalid: ${o.issues.map(s=>s.message).join("; ")}`,o.issues);const a=ne(e,n,o.bundle);if(a!==void 0)throw new M(`provider "${e}" major ${n} bundle is invalid: ${a}`,[]);if(o.bundle.transport===void 0)throw new M(`provider "${e}" major ${n} bundle declares no transport`,[]);return o.bundle}i(oe,"validateArchive");function Pe(e){T(e),x(u(e,"retired"),`${new Date().toISOString()}
|
|
2
|
+
`),$(u(e,"bundle.tar"),{force:!0}),$(u(e,"etag"),{force:!0})}i(Pe,"retire");function ie(e,n){return new c("provider_version_retired",`provider "${e}" major ${n} is retired`,{param:"version"})}i(ie,"retiredError");async function H(e,n,r){const t=Y(e,n),o=u(t,"bundle.tar"),a=w(o),s=a&&r&&w(u(t,"etag"))?S(u(t,"etag"),"utf8"):void 0,f=await ee(e,{version:n,...s!==void 0?{ifNoneMatch:s}:{}});if(f.status===304)return"unchanged";if(f.status===410)throw Pe(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}`);try{await oe(e,n,f.raw)}catch(l){B(e,n,l);const g=new c(a?"provider_unavailable":"not_found",l instanceof Error?l.message:String(l),{param:"version"});throw I.add(g),g}T(t),$(u(t,"retired"),{force:!0});const d=a&&Buffer.from(f.raw).equals(S(o));return d||x(o,f.raw),f.etag!==void 0?x(u(t,"etag"),f.etag):$(u(t,"etag"),{force:!0}),d?"unchanged":a?"updated":"installed"}i(H,"refreshPinned");function Se(){const e=[],n=R();if(!w(n))return e;for(const r of z(n)){if(!K.test(r))continue;let t;try{t=z(u(n,r))}catch{continue}for(const o of t){const a=/^v([1-9][0-9]*)$/.exec(o);a&&w(u(n,r,o,"bundle.tar"))&&e.push([r,Number(a[1])])}}return e}i(Se,"cachedMajors");function Ee(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}i(Ee,"readCommands");const _=new Map,O=new Map;function Ie(e,n){const r=k(n),t=`${r.ino}:${r.mtimeMs}:${r.size}`,o=O.get(e);if(o?.stamp===t)return o.commands;const a=Ee(JSON.parse(S(n,"utf8")));return O.set(e,{stamp:t,commands:a}),a}i(Ie,"installedCommands");async function Qe(e,n){D(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),g=Ie(e,u(l,"commands.json"));return{id:e,major:r.provider.major,provider:r.provider,commands:g,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=Y(e,n);if(w(u(t,"retired")))throw ie(e,n);const o=u(t,"bundle.tar");w(o)||await H(e,n,!1);const a=`${e}/${n}`,s=k(o),f=`${s.mtimeMs}:${s.size}`;let d=_.get(a)?.stamp===f?_.get(a).bundle:void 0;if(d===void 0){try{d=await oe(e,n,new Uint8Array(S(o)))}catch(l){throw B(e,n,l),new c("not_found",`provider "${e}" has no valid bundle for major ${n}`,{param:"version"})}_.set(a,{stamp:f,bundle:d})}return{id:e,major:n,provider:d.provider,commands:d.commands,transport:d.transport.kind==="machine"?{kind:"machine"}:{kind:"http",endpoint:d.transport.endpoint},source:"pinned"}}i(Qe,"resolveProviderBundle");let P=null,U=!1;function Me(e,n){return e?.provider?.id===n&&e.bundle?.endpoint===`${E()}/providers/${encodeURIComponent(n)}/plugin`}i(Me,"isDirectoryBundle");function ae(e){return X(j()).includes(e)}i(ae,"grantNames");async function ke(e){await V(L(J(),e),async()=>{if(!(ae(e)||!Me(C(v(e)),e))){$(v(e),{recursive:!0,force:!0}),pe("user",n=>{delete n.plugins[e]}),me("user",n=>{delete n.plugins[e]}),$(u(R(),e),{recursive:!0,force:!0}),O.delete(e),y.delete(b(e));for(const n of[..._.keys()])n.startsWith(`${e}/`)&&_.delete(n);try{G(n=>Z(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:Q,timeoutError:i(()=>new c("provider_unavailable",`timed out waiting for another process replacing plugin "${e}"`),"timeoutError")})}i(ke,"uninstallProvider");async function xe(){if(!j().every(e=>e.status!=="active")){for(const e of ge("user"))if(!(e.manifest.provider===void 0||ae(e.name)))try{await ke(e.name)}catch(n){m({level:"error",event:"daemon.providers.bundle_uninstall_failed",fields:{provider:e.name},error:n})}}}i(xe,"uninstallDisconnected");async function Te(){if(!q("user").runtime?.issuer)return;const e=X(j());for(const n of e)try{await N(n,!0)}catch(r){r instanceof Object&&I.has(r)||re(n,void 0,r instanceof Error?r.message:String(r))}await xe();for(const[n,r]of Se())try{await H(n,r,!0)}catch(t){if(t instanceof c&&t.code==="provider_version_retired"||t instanceof Object&&I.has(t))continue;re(n,r,t instanceof Error?t.message:String(t))}}i(Te,"syncOnce");function Xe(){return P!==null?(U=!0,P):(P=(async()=>{try{do U=!1,await Te();while(U)}finally{P=null}})(),P)}i(Xe,"syncProviderBundles");export{Je as ensureProviderBundle,X as providerIdsFromGrants,be as providerInstallState,Ke as refreshProviderBundle,Qe as resolveProviderBundle,Xe as syncProviderBundles,_e as unavailableProviderError};
|
|
@@ -7,7 +7,7 @@ export interface ProviderLeaf {
|
|
|
7
7
|
tool: string;
|
|
8
8
|
leaf: ManifestHttpLeaf;
|
|
9
9
|
}
|
|
10
|
-
export declare function providerLeaves(commands: ValidatedCommandManifest
|
|
10
|
+
export declare function providerLeaves(commands: ValidatedCommandManifest): ProviderLeaf[];
|
|
11
11
|
export type ProviderHelpLeaf = Pick<ManifestHttpLeaf, 'kind' | 'name' | 'description' | 'whenToUse' | 'tier' | 'summary' | 'params' | 'output' | 'effects' | 'group'>;
|
|
12
12
|
export interface ProviderHelpBranch extends Pick<ManifestBranch<ManifestHttpLeaf>, 'kind' | 'name' | 'description' | 'whenToUse' | 'tier' | 'summary' | 'model'> {
|
|
13
13
|
rootEntry?: ManifestRootEntry;
|
|
@@ -63,4 +63,4 @@ export interface ProviderResponseContext {
|
|
|
63
63
|
}
|
|
64
64
|
export declare function onProviderResponse(context: ProviderResponseContext): string | undefined;
|
|
65
65
|
export declare function mapProviderResponse(status: number, body: unknown, headers: Record<string, string | string[] | undefined>, approved?: boolean): ProviderError;
|
|
66
|
-
export declare function callProvider(caller: ProviderCaller,
|
|
66
|
+
export declare function callProvider(caller: ProviderCaller, rawInput: ProviderCallInput, request: ProviderRequest): Promise<ProviderCallResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var Z=Object.defineProperty;var g=(e,r)=>Z(e,"name",{value:r,configurable:!0});import{validateDeclaredResult as x}from"../../api/command-manifest/result.js";import{callerAllows as H,requireGroup as ee}from"../../daemon/api/handlers/providers.js";import{grantFor as re,terminalGrant as te}from"../grants/mirror.js";import{assertUnderCap as L,UNCAPPED_GRANTEE as oe}from"../usage/ledger.js";import{listInstalledPlugins as ne}from"../resolver.js";import{ProviderError as p}from"./errors.js";import{ensureProviderBundle as se,refreshProviderBundle as ie,resolveProviderBundle as S,unavailableProviderError as ae}from"./bundles.js";import{prepareFileCall as de,resignForRetry as G,resolveFileResult as z}from"./files.js";import{consumeApproval as ue,requireProviderApproval as pe}from"./approval.js";import{outboundToken as le}from"./mint.js";import{discardFile as ce}from"./token-cache.js";import{registerProviderCall as fe}from"./inflight.js";import{buildRequestPlan as me}from"../command-plugins/transport/http-plan.js";import{sendProviderRequest as ge,ProviderTransportError as ve}from"./request.js";import{acceptProviderReceipt as he}from"./receipts.js";import{writeProviderAudit as we}from"./audit.js";import{selectDevice as be,sendDeviceCall as ye,deviceFailure as _e,deviceUnreachable as Ae,DEFAULT_DEVICE_REQUEST_MS as je}from"./device.js";import{envelopeForProviderCaller as ke}from"../peer/envelope.js";function Q(e,r){const a=[],n=g((i,s)=>{const u=[...s,i.name];if(i.kind==="branch"){for(const v of i.children)n(v,u);return}const t=u[0]===r?u.slice(1).join(" "):u.join(" ");a.push({tool:t,leaf:i})},"walk");for(const i of e.roots)n(i,[]);return a}g(Q,"providerLeaves");function V(e,r,a){if(e.kind!=="branch")return null;const n=[];for(const m of e.children)if(m.kind==="branch"){const f=V(m,r,a);f&&n.push(f)}else if("group"in m&&m.group&&H(a,`${r}:${m.group}`)){const{kind:f,name:A,description:o,whenToUse:P,tier:h,summary:j,params:_,output:$,effects:E,group:k}=m;n.push({kind:f,name:A,description:o,whenToUse:P,...h===void 0?{}:{tier:h},summary:j,params:_,output:$,effects:E,group:k})}if(n.length===0)return null;const{kind:i,name:s,description:u,whenToUse:t,tier:v,summary:c,model:y,rootEntry:b}=e;return{kind:i,name:s,description:u,whenToUse:t,...v===void 0?{}:{tier:v},summary:c,...y===void 0?{}:{model:y},...b===void 0?{}:{rootEntry:b},children:n}}g(V,"providerHelpBranch");async function We(e,r){const a=await S(r),n=Q(a.commands,r),i=ne("user").find(s=>s.name===r)?.manifest.description??r;return{provider:r,status:"available",major:a.major,display_name:i,transport:a.transport.kind,groups:a.provider.groups.filter(s=>H(e,`${r}:${s.group}`)).map(s=>({group:s.group,leaves:n.filter(({leaf:u})=>u.group===s.group).map(({tool:u,leaf:t})=>({tool:u,description:t.description,summary:t.summary,params:t.params,output:t.output}))})),commands:a.commands.roots.flatMap(s=>{const u=V(s,r,e);return u?[u]:[]})}}g(We,"providerTools");function qe(e){return he(e.headers["crtr-receipt"],e)}g(qe,"onProviderResponse");function K(e){if(!(e.class==="person"||!e.sub||!e.grantee))return{grantee:e.grantee,spend_cap:re(e.grantee,e.sub)?.spend_cap??null}}g(K,"capTarget");function Pe(e){if(e.class==="person"){const r=te()?.sub;if(!r)throw new p("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 p("grant_revoked","the caller has no active grant");return{sub:e.sub,grantee:e.grantee}}g(Pe,"identity");function $e(e,r){return[`${e.id}:${r.group}`]}g($e,"callScope");function Ee(e,r){const a=Q(e.commands,e.id).find(n=>n.tool===r);if(!a)throw new p("not_found",`tool "${r}" is not in provider "${e.id}"`,{param:"tool"});if(!a.leaf.group)throw new p("not_found",`tool "${r}" has no group`,{param:"tool"});return a.leaf}g(Ee,"leafFor");function U(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const r=e.error;return!r||typeof r!="object"||Array.isArray(r)?{}:r}g(U,"errorBody");function Re(e){const r=Array.isArray(e)?e[0]:e;if(!r)return;const a=Number(r);if(Number.isFinite(a)&&a>=0)return a;const n=Date.parse(r);return Number.isFinite(n)?Math.max(0,Math.ceil((n-Date.now())/1e3)):void 0}g(Re,"retryAfter");function O(e,r,a,n=!1){const i=U(r),s=typeof i.code=="string"?i.code:void 0,u=typeof i.message=="string"?i.message:`provider answered HTTP ${e}`,t=typeof i.field=="string"?i.field:void 0,c={origin:"provider",details:{code:s??null,message:u,...t?{field:t}:{},...typeof i.next=="string"?{next:i.next}:{},...i.received!==void 0?{received:i.received}:{},status:e},...t?{param:t}:{}};return e===429?new p("provider_rate_limited",u,{...c,retry_after_s:Re(a["retry-after"])}):e===401||e>=500?new p("provider_unavailable",u,c):e===403&&s==="provider_refused"?new p("provider_refused",u,c):e===403&&s==="approval_required"?new p("provider_error",n?"provider asked for approval again":"provider approval question is invalid",{...c,status:e}):new p("provider_error",u,{...c,status:e})}g(O,"mapProviderResponse");function W(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(W,"approvalShape");async function Xe(e,r,a){const n=new AbortController,i=g(()=>n.abort(a.signal.reason),"abort");a.signal.aborted?i():a.signal.addEventListener("abort",i,{once:!0});let s=a.requestId;const u=fe({request_id:s,run_id:e.run_id,node_id:e.node_id,controller:n});let t,v,c,y,b,m,f,A="allowed";try{try{await se(r.provider)}catch{throw ae(r.provider)}let o=await S(r.provider,r.version),P=!1,h,j,_,$=!1,E=!1,k,B=0;for(;;){const w=Ee(o,r.tool);if(c=`${o.id}:${w.group}`,ee(e,o.id,w.group),L(K(e)),o.transport.kind!=="machine"&&r.machine!==void 0)throw new p("invalid_argument",`provider "${o.id}" is not served by a device, so it takes no machine`,{param:"machine"});o.transport.kind==="machine"&&k===void 0&&(k=await be(r.machine),B=Date.now()+(o.commands.timeouts?.requestMs??je));let I=null;if(r.approval&&!$){const d=ue(e,r,W(o,w));_=d.option,I=d.prepared,f=r.approval,s=d.requestId,$=!0}const R={params:w.params.filter(d=>"type"in d&&d.type==="file").map(d=>d.name),output:w.output.find(d=>d.type==="file")?.name??null},J=JSON.stringify(R);if(j!==void 0&&j!==J)throw new p("provider_bad_response","the refreshed manifest changed the tool file contract",{origin:"provider",details:{status:409,message:"manifest_stale changed the tool file contract"}});j=J,I&&(h=await G(e,R,r.arguments,I,{signal:n.signal})),h??=await de(e,R,r.arguments,{signal:n.signal});const{sub:M,grantee:T}=Pe(e);if(L(K(e)),o.transport.kind==="machine"){const d=await Te(e,o,r,w,h,{sub:M,requestId:s,deadlineAt:B,signal:n.signal,machine:k});return m=d.responder,t=d.cleanup,{status:200,body:d.result}}const F=await le({audience:o.transport.endpoint,sub:M,grantee:T,initiating_app:T,scope:$e(o,w)},{signal:n.signal});y=F.jti;const X={registration:{name:o.id,endpoint:o.transport.endpoint},...o.commands.baseUrl?{baseUrl:o.commands.baseUrl}:{},...o.commands.timeouts?{timeouts:o.commands.timeouts}:{},params:w.params,rest:w.rest,output:w.output,commandPath:[o.id,...r.tool.split(" ")]},Y=me(X,h.arguments,void 0,{authorization:`Bearer ${F.access_token}`,requestId:s,fileMode:"pass-through",extraHeaders:{...e.run_id?{"Crtr-Run-Id":e.run_id}:{},..._?{"Crtr-Approval":_}:{},...h.headers}});v??=Date.now()+(o.commands.timeouts?.requestMs??3e4);let l;try{if(l=await ge(Y,{connectMs:o.commands.timeouts?.connectMs,requestMs:o.commands.timeouts?.requestMs,deadlineAt:v,signal:n.signal}),n.signal.aborted)throw n.signal.reason??new Error("provider call aborted")}catch(d){throw n.signal.aborted?n.signal.reason??d:d instanceof ve?new p("provider_unavailable",d.message,{origin:"provider",details:{status:null,code:d.kind}}):d}if(b??=qe({provider:o.id,tool:r.tool,request_id:s,run_id:e.run_id,sub:M,grantee:T,ledger_grantee:e.class==="person"?oe:T,jti:F.jti,status:l.status,headers:l.headers}),m=l.headers["crtr-responder"],l.status===401&&ce(F.key),l.status>=200&&l.status<300){const d=l.body;if(!d||typeof d!="object"||Array.isArray(d))throw new p("provider_bad_response","provider result is not a JSON object",{origin:"provider",details:{status:l.status,message:"provider result is not a JSON object"}});let C;try{C=await z(e,h,d,{signal:n.signal})}catch(q){throw q instanceof p&&q.code==="provider_error"?new p("provider_bad_response",q.message,{origin:"provider",details:{status:l.status,message:q.message}}):q}t=C.cleanup;const D=x(w.output,C.result);if(D)throw new p("provider_bad_response",`provider result has an invalid output field "${D.field}"`,{origin:"provider",param:D.field,details:{status:l.status,...D}});if(n.signal.aborted)throw n.signal.reason??new Error("provider call aborted");return{status:200,body:C.result}}const N=U(l.body);if(l.status===403&&N.code==="approval_required"&&!_&&!E){E=!0;const d=await pe(e,r,s,N.question,n.signal,W(o,w),h);if(f=d.questionId,_=d.option,h=await G(e,R,r.arguments,h,{signal:n.signal}),v=void 0,n.signal.aborted)throw new p("user_denied","the approval wait was canceled");continue}if(l.status===403&&N.code==="approval_required"&&_)throw O(l.status,l.body,l.headers,!0);if(l.status!==401&&N.manifest_stale===!0&&!P){P=!0,await ie(o.id,{conditional:!1,...r.version===void 0?{}:{version:r.version}}),o=await S(o.id,r.version);continue}throw O(l.status,l.body,l.headers,!!_)}}catch(o){throw o instanceof p&&o.question_id&&(f=o.question_id),m===void 0&&o instanceof p&&typeof o.responder=="string"&&(m=o.responder),A=`refused:${o instanceof p?o.code:"server_error"}`,t?.(),o}finally{try{we({request_id:s,caller:e,provider:r.provider,tool:r.tool,scope_checked:c,outcome:A,jti:y,receipt_id:b,responder:m,question_id:f})}finally{u()}a.signal.removeEventListener("abort",i)}}g(Xe,"callProvider");async function Te(e,r,a,n,i,s){const{machine:u}=s,t=await ye(u,{tool:a.tool,arguments:i.arguments,version:r.major,requestId:s.requestId,runId:e.run_id,deadlineAt:s.deadlineAt,upload:i.upload===null?null:{id:i.upload.upload_id,link:i.upload.upload_link}},ke(e,s.sub),s.signal);if(s.signal.aborted)throw s.signal.reason??new Error("provider call aborted");if("unreachable"in t)throw Ae(u,t.unreachable.reason,t.unreachable.message);const v=t.headers["crtr-responder"],c=U(t.body);if(t.status<200||t.status>=300){const f=_e(u,t.status,typeof c.code=="string"?c.code:void 0,typeof c.message=="string"?c.message:void 0,t.body),A=f===null&&t.status>=500&&typeof c.code=="string"?new p("provider_error",typeof c.message=="string"?c.message:`the device answered HTTP ${t.status}`,{origin:"runtime",status:t.status,details:{code:c.code,status:t.status}}):null;throw Object.assign(f??A??O(t.status,t.body,t.headers),{responder:v})}const y=t.body&&typeof t.body=="object"&&!Array.isArray(t.body)?t.body.result:void 0;if(!y||typeof y!="object"||Array.isArray(y))throw Object.assign(new p("provider_bad_response","the device result is not a JSON object",{origin:"provider",details:{status:t.status,message:"the device result is not a JSON object"}}),{responder:v});let b;try{b=await z(e,i,y,{signal:s.signal})}catch(f){throw f instanceof p&&f.code==="provider_error"?Object.assign(new p("provider_bad_response",f.message,{origin:"provider",details:{status:t.status,message:f.message}}),{responder:v}):f}const m=x(n.output,b.result);if(m)throw b.cleanup?.(),Object.assign(new p("provider_bad_response",`the device result has an invalid output field "${m.field}"`,{origin:"provider",param:m.field,details:{status:t.status,...m}}),{responder:v});return{result:b.result,responder:v,cleanup:b.cleanup}}g(Te,"callDevice");export{Xe as callProvider,O as mapProviderResponse,qe as onProviderResponse,Q as providerLeaves,We as providerTools};
|
|
1
|
+
var re=Object.defineProperty;var f=(e,t)=>re(e,"name",{value:t,configurable:!0});import{validateDeclaredResult as L}from"../../api/command-manifest/result.js";import{callerAllows as G,requireGroup as te}from"../../daemon/api/handlers/providers.js";import{grantFor as oe,terminalGrant as ne}from"../grants/mirror.js";import{assertUnderCap as z,UNCAPPED_GRANTEE as ie}from"../usage/ledger.js";import{listInstalledPlugins as se}from"../resolver.js";import{ProviderError as u}from"./errors.js";import{resolveProviderRef as ae}from"./roots.js";import{ensureProviderBundle as de,refreshProviderBundle as ue,resolveProviderBundle as O,unavailableProviderError as pe}from"./bundles.js";import{prepareFileCall as le,resignForRetry as Q,resolveFileResult as V}from"./files.js";import{consumeApproval as ce,requireProviderApproval as fe}from"./approval.js";import{outboundToken as me}from"./mint.js";import{discardFile as ve}from"./token-cache.js";import{registerProviderCall as ge}from"./inflight.js";import{buildRequestPlan as he}from"../command-plugins/transport/http-plan.js";import{sendProviderRequest as we,ProviderTransportError as be}from"./request.js";import{acceptProviderReceipt as ye}from"./receipts.js";import{writeProviderAudit as _e}from"./audit.js";import{selectDevice as Ae,sendDeviceCall as je,deviceFailure as ke,deviceUnreachable as qe,DEFAULT_DEVICE_REQUEST_MS as Pe}from"./device.js";import{envelopeForProviderCaller as $e}from"../peer/envelope.js";function K(e){const t=[],s=e.roots.length===1?e.roots[0].name:void 0,p=f((r,n)=>{const d=[...n,r.name];if(r.kind==="branch"){for(const g of r.children)p(g,d);return}const o=d[0]===s?d.slice(1).join(" "):d.join(" ");t.push({tool:o,leaf:r})},"walk");for(const r of e.roots)p(r,[]);return t}f(K,"providerLeaves");function W(e,t,s){if(e.kind!=="branch")return null;const p=[];for(const v of e.children)if(v.kind==="branch"){const m=W(v,t,s);m&&p.push(m)}else if("group"in v&&v.group&&G(s,`${t}:${v.group}`)){const{kind:m,name:_,description:j,whenToUse:P,tier:i,summary:$,params:y,output:k,effects:A,group:E}=v;p.push({kind:m,name:_,description:j,whenToUse:P,...i===void 0?{}:{tier:i},summary:$,params:y,output:k,effects:A,group:E})}if(p.length===0)return null;const{kind:r,name:n,description:d,whenToUse:o,tier:g,summary:c,model:w,rootEntry:b}=e;return{kind:r,name:n,description:d,whenToUse:o,...g===void 0?{}:{tier:g},summary:c,...w===void 0?{}:{model:w},...b===void 0?{}:{rootEntry:b},children:p}}f(W,"providerHelpBranch");async function er(e,t){const s=await O(t),p=K(s.commands),r=se("user").find(n=>n.name===t)?.manifest.description??t;return{provider:t,status:"available",major:s.major,display_name:r,transport:s.transport.kind,groups:s.provider.groups.filter(n=>G(e,`${t}:${n.group}`)).map(n=>({group:n.group,leaves:p.filter(({leaf:d})=>d.group===n.group).map(({tool:d,leaf:o})=>({tool:d,description:o.description,summary:o.summary,params:o.params,output:o.output}))})),commands:s.commands.roots.flatMap(n=>{const d=W(n,t,e);return d?[d]:[]})}}f(er,"providerTools");function Ee(e){return ye(e.headers["crtr-receipt"],e)}f(Ee,"onProviderResponse");function X(e){if(!(e.class==="person"||!e.sub||!e.grantee))return{grantee:e.grantee,spend_cap:oe(e.grantee,e.sub)?.spend_cap??null}}f(X,"capTarget");function Re(e){if(e.class==="person"){const t=ne()?.sub;if(!t)throw new u("grant_revoked","This machine has no active Northlight grant for your terminal. Run `crtr sys join` again from /account/machines.");return{sub:t,grantee:t}}if(!e.sub||!e.grantee)throw new u("grant_revoked","the caller has no active grant");return{sub:e.sub,grantee:e.grantee}}f(Re,"identity");function Te(e,t){return[`${e.id}:${t.group}`]}f(Te,"callScope");function Fe(e,t){const s=K(e.commands).find(p=>p.tool===t);if(!s)throw new u("not_found",`tool "${t}" is not in provider "${e.id}"`,{param:"tool"});if(!s.leaf.group)throw new u("not_found",`tool "${t}" has no group`,{param:"tool"});return s.leaf}f(Fe,"leafFor");function I(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t=e.error;return!t||typeof t!="object"||Array.isArray(t)?{}:t}f(I,"errorBody");function Ne(e){const t=Array.isArray(e)?e[0]:e;if(!t)return;const s=Number(t);if(Number.isFinite(s)&&s>=0)return s;const p=Date.parse(t);return Number.isFinite(p)?Math.max(0,Math.ceil((p-Date.now())/1e3)):void 0}f(Ne,"retryAfter");function B(e,t,s,p=!1){const r=I(t),n=typeof r.code=="string"?r.code:void 0,d=typeof r.message=="string"?r.message:`provider answered HTTP ${e}`,o=typeof r.field=="string"?r.field:void 0,c={origin:"provider",details:{code:n??null,message:d,...o?{field:o}:{},...typeof r.next=="string"?{next:r.next}:{},...r.received!==void 0?{received:r.received}:{},status:e},...o?{param:o}:{}};return e===429?new u("provider_rate_limited",d,{...c,retry_after_s:Ne(s["retry-after"])}):e===401||e>=500?new u("provider_unavailable",d,c):e===403&&n==="provider_refused"?new u("provider_refused",d,c):e===403&&n==="approval_required"?new u("provider_error",p?"provider asked for approval again":"provider approval question is invalid",{...c,status:e}):new u("provider_error",d,{...c,status:e})}f(B,"mapProviderResponse");function Y(e,t){return JSON.stringify({major:e.major,endpoint:e.transport.kind==="http"?e.transport.endpoint:null,baseUrl:e.commands.baseUrl??null,rest:t.rest,params:t.params,output:t.output})}f(Y,"approvalShape");async function rr(e,t,s){const p=ae(t.provider);if(p===null)throw new u("not_found",`provider "${t.provider}" is not available`,{param:"provider"});const r={...t,provider:p},n=new AbortController,d=f(()=>n.abort(s.signal.reason),"abort");s.signal.aborted?d():s.signal.addEventListener("abort",d,{once:!0});let o=s.requestId;const g=ge({request_id:o,run_id:e.run_id,node_id:e.node_id,controller:n});let c,w,b,v,m,_,j,P="allowed";try{try{await de(r.provider)}catch{throw pe(r.provider)}let i=await O(r.provider,r.version),$=!1,y,k,A,E=!1,J=!1,M,x=0;for(;;){const h=Fe(i,r.tool);if(b=`${i.id}:${h.group}`,te(e,i.id,h.group),z(X(e)),i.transport.kind!=="machine"&&r.machine!==void 0)throw new u("invalid_argument",`provider "${i.id}" is not served by a device, so it takes no machine`,{param:"machine"});i.transport.kind==="machine"&&M===void 0&&(M=await Ae(r.machine),x=Date.now()+(i.commands.timeouts?.requestMs??Pe));let S=null;if(r.approval&&!E){const a=ce(e,r,Y(i,h));A=a.option,S=a.prepared,j=r.approval,o=a.requestId,E=!0}const R={params:h.params.filter(a=>"type"in a&&a.type==="file").map(a=>a.name),output:h.output.find(a=>a.type==="file")?.name??null},H=JSON.stringify(R);if(k!==void 0&&k!==H)throw new u("provider_bad_response","the refreshed manifest changed the tool file contract",{origin:"provider",details:{status:409,message:"manifest_stale changed the tool file contract"}});k=H,S&&(y=await Q(e,R,r.arguments,S,{signal:n.signal})),y??=await le(e,R,r.arguments,{signal:n.signal});const{sub:U,grantee:T}=Re(e);if(z(X(e)),i.transport.kind==="machine"){const a=await Ce(e,i,r,h,y,{sub:U,requestId:o,deadlineAt:x,signal:n.signal,machine:M});return _=a.responder,c=a.cleanup,{status:200,body:a.result}}const F=await me({audience:i.transport.endpoint,sub:U,grantee:T,initiating_app:T,scope:Te(i,h)},{signal:n.signal});v=F.jti;const Z={registration:{name:i.id,endpoint:i.transport.endpoint},...i.commands.baseUrl?{baseUrl:i.commands.baseUrl}:{},...i.commands.timeouts?{timeouts:i.commands.timeouts}:{},params:h.params,rest:h.rest,output:h.output,commandPath:[i.id,...r.tool.split(" ")]},ee=he(Z,y.arguments,void 0,{authorization:`Bearer ${F.access_token}`,requestId:o,fileMode:"pass-through",extraHeaders:{...e.run_id?{"Crtr-Run-Id":e.run_id}:{},...A?{"Crtr-Approval":A}:{},...y.headers}});w??=Date.now()+(i.commands.timeouts?.requestMs??3e4);let l;try{if(l=await we(ee,{connectMs:i.commands.timeouts?.connectMs,requestMs:i.commands.timeouts?.requestMs,deadlineAt:w,signal:n.signal}),n.signal.aborted)throw n.signal.reason??new Error("provider call aborted")}catch(a){throw n.signal.aborted?n.signal.reason??a:a instanceof be?new u("provider_unavailable",a.message,{origin:"provider",details:{status:null,code:a.kind}}):a}if(m??=Ee({provider:i.id,tool:r.tool,request_id:o,run_id:e.run_id,sub:U,grantee:T,ledger_grantee:e.class==="person"?ie:T,jti:F.jti,status:l.status,headers:l.headers}),_=l.headers["crtr-responder"],l.status===401&&ve(F.key),l.status>=200&&l.status<300){const a=l.body;if(!a||typeof a!="object"||Array.isArray(a))throw new u("provider_bad_response","provider result is not a JSON object",{origin:"provider",details:{status:l.status,message:"provider result is not a JSON object"}});let C;try{C=await V(e,y,a,{signal:n.signal})}catch(q){throw q instanceof u&&q.code==="provider_error"?new u("provider_bad_response",q.message,{origin:"provider",details:{status:l.status,message:q.message}}):q}c=C.cleanup;const D=L(h.output,C.result);if(D)throw new u("provider_bad_response",`provider result has an invalid output field "${D.field}"`,{origin:"provider",param:D.field,details:{status:l.status,...D}});if(n.signal.aborted)throw n.signal.reason??new Error("provider call aborted");return{status:200,body:C.result}}const N=I(l.body);if(l.status===403&&N.code==="approval_required"&&!A&&!J){J=!0;const a=await fe(e,r,o,N.question,n.signal,Y(i,h),y);if(j=a.questionId,A=a.option,y=await Q(e,R,r.arguments,y,{signal:n.signal}),w=void 0,n.signal.aborted)throw new u("user_denied","the approval wait was canceled");continue}if(l.status===403&&N.code==="approval_required"&&A)throw B(l.status,l.body,l.headers,!0);if(l.status!==401&&N.manifest_stale===!0&&!$){$=!0,await ue(i.id,{conditional:!1,...r.version===void 0?{}:{version:r.version}}),i=await O(i.id,r.version);continue}throw B(l.status,l.body,l.headers,!!A)}}catch(i){throw i instanceof u&&i.question_id&&(j=i.question_id),_===void 0&&i instanceof u&&typeof i.responder=="string"&&(_=i.responder),P=`refused:${i instanceof u?i.code:"server_error"}`,c?.(),i}finally{try{_e({request_id:o,caller:e,provider:r.provider,tool:r.tool,scope_checked:b,outcome:P,jti:v,receipt_id:m,responder:_,question_id:j})}finally{g()}s.signal.removeEventListener("abort",d)}}f(rr,"callProvider");async function Ce(e,t,s,p,r,n){const{machine:d}=n,o=await je(d,{tool:s.tool,arguments:r.arguments,version:t.major,requestId:n.requestId,runId:e.run_id,deadlineAt:n.deadlineAt,upload:r.upload===null?null:{id:r.upload.upload_id,link:r.upload.upload_link}},$e(e,n.sub),n.signal);if(n.signal.aborted)throw n.signal.reason??new Error("provider call aborted");if("unreachable"in o)throw qe(d,o.unreachable.reason,o.unreachable.message);const g=o.headers["crtr-responder"],c=I(o.body);if(o.status<200||o.status>=300){const m=ke(d,o.status,typeof c.code=="string"?c.code:void 0,typeof c.message=="string"?c.message:void 0,o.body),_=m===null&&o.status>=500&&typeof c.code=="string"?new u("provider_error",typeof c.message=="string"?c.message:`the device answered HTTP ${o.status}`,{origin:"runtime",status:o.status,details:{code:c.code,status:o.status}}):null;throw Object.assign(m??_??B(o.status,o.body,o.headers),{responder:g})}const w=o.body&&typeof o.body=="object"&&!Array.isArray(o.body)?o.body.result:void 0;if(!w||typeof w!="object"||Array.isArray(w))throw Object.assign(new u("provider_bad_response","the device result is not a JSON object",{origin:"provider",details:{status:o.status,message:"the device result is not a JSON object"}}),{responder:g});let b;try{b=await V(e,r,w,{signal:n.signal})}catch(m){throw m instanceof u&&m.code==="provider_error"?Object.assign(new u("provider_bad_response",m.message,{origin:"provider",details:{status:o.status,message:m.message}}),{responder:g}):m}const v=L(p.output,b.result);if(v)throw b.cleanup?.(),Object.assign(new u("provider_bad_response",`the device result has an invalid output field "${v.field}"`,{origin:"provider",param:v.field,details:{status:o.status,...v}}),{responder:g});return{result:b.result,responder:g,cleanup:b.cleanup}}f(Ce,"callDevice");export{rr as callProvider,B as mapProviderResponse,Ee as onProviderResponse,K as providerLeaves,er as providerTools};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { InstalledPlugin } from '../../types.js';
|
|
2
|
+
/** The single root command name of a provider bundle's `commands.json`, or
|
|
3
|
+
* undefined when it declares none or more than one. */
|
|
4
|
+
export declare function rootNameOfCommands(raw: unknown): string | undefined;
|
|
5
|
+
/** An installed provider plugin's root command name, cached per file replacement. */
|
|
6
|
+
export declare function installedRootName(plugin: Pick<InstalledPlugin, 'root'>): string | undefined;
|
|
7
|
+
export interface ProviderRootClaims {
|
|
8
|
+
/** root command name → provider id (the earliest-installed claimant). */
|
|
9
|
+
byRoot: Map<string, string>;
|
|
10
|
+
/** Provider ids skipped because an earlier-installed provider claims their root. */
|
|
11
|
+
skipped: Set<string>;
|
|
12
|
+
}
|
|
13
|
+
/** Every enabled installed provider bundle's root. When two claim one root the
|
|
14
|
+
* later-installed is skipped (and logged once), never shadowing the first. */
|
|
15
|
+
export declare function providerRootClaims(plugins?: readonly InstalledPlugin[]): ProviderRootClaims;
|
|
16
|
+
/** A provider id or a root command name → the provider id. An installed id
|
|
17
|
+
* wins; an unknown name passes through unchanged. A provider skipped for
|
|
18
|
+
* claiming a root another provider serves is unreachable by id as well (`null`). */
|
|
19
|
+
export declare function resolveProviderRef(ref: string): string | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var v=Object.defineProperty;var m=(o,n)=>v(o,"name",{value:n,configurable:!0});import{readFileSync as y,statSync as w}from"node:fs";import{join as S}from"node:path";import{emitEvent as $}from"../events/emit.js";import{readState as h}from"../config.js";import{listInstalledPlugins as p}from"../resolver.js";function g(o){const n=o?.mounts;if(!Array.isArray(n))return;const t=new Set;for(const r of n)Array.isArray(r?.parent)&&r.parent.length===0&&typeof r.node?.name=="string"&&t.add(r.node.name);return t.size===1?[...t][0]:void 0}m(g,"rootNameOfCommands");const u=new Map;function C(o){const n=S(o.root,"commands.json");try{const t=w(n),r=`${t.ino}:${t.mtimeMs}:${t.size}`,d=u.get(n);if(d?.stamp===r)return d.root;const a=g(JSON.parse(y(n,"utf8")));return u.set(n,{stamp:r,root:a}),a}catch{return}}m(C,"installedRootName");const l=new Set;function R(o=p("user")){const n=o.filter(e=>e.enabled&&e.manifest.provider!==void 0&&e.manifest.provider.id===e.name),t=new Map(n.map(e=>[e.name,C(e)])),r=new Set,d=new Set;for(const e of t.values())e!==void 0&&(r.has(e)&&d.add(e),r.add(e));if(d.size>0){const e=h("user").plugins;n.sort((s,i)=>(e[s.name]?.last_updated??"").localeCompare(e[i.name]?.last_updated??"")||s.name.localeCompare(i.name))}const a=new Map,c=new Set;for(const e of n){const s=t.get(e.name);if(s===void 0)continue;const i=a.get(s);if(i===void 0){a.set(s,e.name);continue}c.add(e.name);const f=`${e.name}\0${s}\0${i}`;l.has(f)||(l.add(f),$({level:"error",event:"daemon.providers.bundle_invalid",fields:{provider:e.name,issues:[{code:"root_command_claimed",message:`root command "${s}" is already served by provider "${i}"; "${e.name}" is skipped`}]}}))}return{byRoot:a,skipped:c}}m(R,"providerRootClaims");function z(o){const n=p("user"),t=R(n);return t.skipped.has(o)?null:n.some(r=>r.name===o&&r.manifest.provider!==void 0)?o:t.byRoot.get(o)??o}m(z,"resolveProviderRef");export{C as installedRootName,R as providerRootClaims,z as resolveProviderRef,g as rootNameOfCommands};
|
|
@@ -22,7 +22,7 @@ export declare function tokenCacheDir(): string;
|
|
|
22
22
|
export declare function cacheKey(request: OutboundTokenRequest): string;
|
|
23
23
|
/** The cached token for `key` while it is still reusable, else null (a stale or unreadable file is removed). */
|
|
24
24
|
export declare function cachedToken(key: string, nowMs?: number): CachedToken | null;
|
|
25
|
-
/** The discard epoch of the pair a key names; pass it back to `storeToken`. */
|
|
25
|
+
/** The discard epoch of the pair and providers a key names (it only ever grows); pass it back to `storeToken`. */
|
|
26
26
|
export declare function discardEpoch(key: string): number;
|
|
27
27
|
/** Store a freshly minted token, unless its pair was discarded since `epoch` was read. Returns whether it was stored. */
|
|
28
28
|
export declare function storeToken(key: string, token: CachedToken, epoch?: number): boolean;
|
|
@@ -36,9 +36,17 @@ export declare function changedGrantPairs(before: readonly GrantRecord[], after:
|
|
|
36
36
|
sub: string;
|
|
37
37
|
grantee: string;
|
|
38
38
|
}>;
|
|
39
|
+
/** The (`sub`, provider) keys a sync stopped serving: a provider whose scopes
|
|
40
|
+
* the active grants resolved before and no longer do, or whose connection
|
|
41
|
+
* grant (`sub` to `app:<provider>`) was active before and is not now, even
|
|
42
|
+
* while another grant still names its scopes. */
|
|
43
|
+
export declare function droppedProviders(before: readonly GrantRecord[], after: readonly GrantRecord[]): string[];
|
|
39
44
|
/** The grant-sync hook (D3-TOK-20, CRD-16): discard every cached token of each
|
|
40
|
-
* changed grant's (`sub`, `grantee`)
|
|
45
|
+
* changed grant's (`sub`, `grantee`) and of each (`sub`, provider) the sync
|
|
46
|
+
* stopped resolving (whatever the token's grantee — a person's own calls mint
|
|
47
|
+
* as (`sub`, `sub`)), and prune expired files. */
|
|
41
48
|
export declare function discardChangedGrantTokens(before: readonly GrantRecord[], after: readonly GrantRecord[], nowMs?: number): {
|
|
42
49
|
pairs: number;
|
|
50
|
+
providers: number;
|
|
43
51
|
tokens: number;
|
|
44
52
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var j=Object.defineProperty;var o=(e,t)=>j(e,"name",{value:t,configurable:!0});import{createHash as A}from"node:crypto";import{mkdirSync as M,readdirSync as E,readFileSync as F,rmSync as d}from"node:fs";import{join as h}from"node:path";import{crtrHome as D}from"../canvas/paths.js";import{atomicWriteJson as P}from"../fs-utils.js";const G=30;function l(){return h(D(),"secrets","outbound-tokens")}o(l,"tokenCacheDir");function U(e){return JSON.stringify([e.audience,e.sub,e.grantee,e.initiating_app,[...new Set(e.scope)].sort()])}o(U,"cacheKey");function y(e){return h(l(),`${A("sha256").update(e).digest("hex")}.json`)}o(y,"fileFor");function m(e){try{const t=JSON.parse(e);if(!Array.isArray(t)||typeof t[1]!="string"||typeof t[2]!="string")return null;const n=(Array.isArray(t[4])?t[4]:[]).flatMap(s=>typeof s=="string"?[v(s)]:[]).filter(s=>s!==null);return{sub:t[1],grantee:t[2],providers:[...new Set(n)]}}catch{return null}}o(m,"partsOf");function B(e){const t=m(e);return t===null?null:p(t.sub,t.grantee)}o(B,"pairOf");function p(e,t){return JSON.stringify([e,t])}o(p,"pairKey");function g(e,t){return JSON.stringify([e,t])}o(g,"providerKey");function v(e){const t=e.indexOf(":");return t>0?e.slice(0,t):null}o(v,"providerOf");function x(e){try{const t=JSON.parse(F(e,"utf8"));return typeof t.key!="string"||typeof t.access_token!="string"||typeof t.jti!="string"||typeof t.exp!="number"?null:t}catch{return null}}o(x,"readFile");function K(e,t){return t<(e.exp-G)*1e3}o(K,"usable");function L(e,t=Date.now()){const r=y(e),n=x(r);return!n||n.key!==e||!K(n,t)?(d(r,{force:!0}),null):{access_token:n.access_token,jti:n.jti,exp:n.exp}}o(L,"cachedToken");const f=new Map,S=new Map;function w(e){const t=m(e);return t===null?0:(f.get(p(t.sub,t.grantee))??0)+t.providers.reduce((r,n)=>r+(S.get(g(t.sub,n))??0),0)}o(w,"discardEpoch");function Q(e,t,r=w(e)){return w(e)!==r?!1:(M(l(),{recursive:!0,mode:448}),P(y(e),{key:e,...t},{mode:384}),!0)}o(Q,"storeToken");function V(e){d(y(e),{force:!0})}o(V,"discardFile");function b(e,t,r){let n;try{n=E(l())}catch{return 0}let s=0;for(const i of n){if(!i.endsWith(".json"))continue;const c=h(l(),i),a=x(c),u=a?m(a.key):null,N=u!==null&&(e.has(p(u.sub,u.grantee))||u.providers.some(_=>t.has(g(u.sub,_))));a&&N?(d(c,{force:!0}),s++):(!a||r>=a.exp*1e3)&&d(c,{force:!0})}return s}o(b,"sweep");function X(e,t,r=Date.now()){const n=p(e,t);return f.set(n,(f.get(n)??0)+1),b(new Set([n]),new Set,r)}o(X,"discardFor");function O(e){const t=new Map;for(const r of e){const n=r.resolved_scopes.map(({scope:i,mode:c})=>`${c} ${i}`).sort(),s=p(r.sub,r.grantee);t.set(s,[...t.get(s)??[],JSON.stringify([r.id,r.status,n])])}return new Map([...t].map(([r,n])=>[r,JSON.stringify(n.sort())]))}o(O,"grantStates");function T(e,t){const r=O(e),n=O(t),s=[];for(const[i,c]of r){if(n.get(i)===c)continue;const[a,u]=JSON.parse(i);s.push({sub:a,grantee:u})}return s}o(T,"changedGrantPairs");function k(e){const t=new Set;for(const r of e)if(r.status==="active")for(const{scope:n}of r.resolved_scopes){const s=v(n);s!==null&&t.add(g(r.sub,s))}return t}o(k,"resolvedProviders");function J(e){return new Set(e.filter(t=>t.status==="active"&&t.grantee.startsWith("app:")).map(t=>p(t.sub,t.grantee)))}o(J,"connectedApps");function W(e,t){const r=k(t),n=new Set([...k(e)].filter(i=>!r.has(i))),s=J(t);for(const i of J(e)){if(s.has(i))continue;const[c,a]=JSON.parse(i);n.add(g(c,a.slice(4)))}return[...n]}o(W,"droppedProviders");function Y(e,t,r=Date.now()){const n=T(e,t),s=new Set(n.map(({sub:c,grantee:a})=>p(c,a))),i=new Set(W(e,t));for(const c of s)f.set(c,(f.get(c)??0)+1);for(const c of i)S.set(c,(S.get(c)??0)+1);return{pairs:s.size,providers:i.size,tokens:b(s,i,r)}}o(Y,"discardChangedGrantTokens");export{G as REUSE_MARGIN_S,U as cacheKey,L as cachedToken,T as changedGrantPairs,Y as discardChangedGrantTokens,w as discardEpoch,V as discardFile,X as discardFor,W as droppedProviders,Q as storeToken,l as tokenCacheDir};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
var U=Object.defineProperty;var f=(e,r)=>U(e,"name",{value:r,configurable:!0});import{prepared as p}from"../sqlite-statements.js";import{randomUUID as x}from"node:crypto";import{chmodSync as I,copyFileSync as P,existsSync as m,mkdirSync as w,readFileSync as _,readdirSync as V,statSync as N}from"node:fs";import{dirname as L,join as u,relative as J}from"node:path";import W from"proper-lockfile";import{CONFIG_FILE as b,PROFILE_DIR as D,STATE_FILE as v}from"../../types.js";import{LockedJsonFile as C}from"../locked-file.js";import{assertScopedStoreReady as q,createScopedStore as j,discardIncompleteScopedStore as z,legacyScopedStorePaths as O,PROFILE_ID_SHAPE as G,scopedStoreExists as g,withProjectStateLock as H,writeOwnerRoot as K}from"./db.js";import{canonicalRoot as M,providerStore as T,projectStore as X,userStore as B}from"./paths.js";import{profileSpace as Q}from"../layout.js";import{createScopedSchema as F}from"./schema.js";function h(e){return JSON.parse(_(e,"utf8"))}f(h,"json");function R(e){return e.some(m)}f(R,"existsLegacy");function E(e,r){const o=u(e,".legacy-scoped-state");try{w(o,{mode:448})}catch(a){if(a.code!=="EEXIST"||!N(o).isDirectory())throw a}I(o,448);for(const a of r)if(m(a)&&N(a).isFile()){const n=J(e,a);if(n===""||n.startsWith(".."))throw new Error(`legacy state is outside its owning root: ${a}`);const t=u(o,n);L(t)!==o&&w(L(t),{recursive:!0,mode:448}),P(a,t),I(t,384)}}f(E,"preserve");import{initializeFreshScopedStore as ge}from"./db.js";function me(e){const r=e.kind==="user"?B(e.root):e.kind==="project"?X(e.root):T(e.root);if(e.kind!=="project"&&g(r))return;if(e.kind==="provider")throw new Error("provider conversion requires convertLegacyProviderStore() under coordinated shared-agent-dir activation");const o=O(r),a=f(()=>{e.kind==="project"&&z(r),!g(r)&&(e.kind==="project"&&R(o)&&E(r.root,o),j(r,n=>{F(n,e.kind);const t=e.kind==="user"?"user":"project";K(n,t,r.root),e.kind==="user"?re(n,r.root):k(n,t,u(r.root,v))}),e.kind==="user"&&R(o)&&E(r.root,o))},"convert");e.kind==="project"?H(r.root,()=>{a(),q(r)}):a()}f(me,"convertLegacyScopedStore");function Y(e){if(!m(e))return[];const r=JSON.parse(_(e,"utf8"));if(!Array.isArray(r))throw new Error(`legacy subscription pool is malformed: ${e}`);const o=new Set;return r.map((a,n)=>{if(a===null||typeof a!="object"||Array.isArray(a))throw new Error(`legacy subscription pool entry ${n+1} is malformed: ${e}`);const t=a,s=typeof t.label=="string"?t.label.trim():"",c=typeof t.refresh=="string"?t.refresh.trim():"",i=typeof t.access=="string"?t.access.trim():"",d=Number(t.expires);if(!s||!c||!i||!Number.isFinite(d))throw new Error(`legacy subscription pool entry ${n+1} is incomplete: ${e}`);const y=s.toLowerCase();if(o.has(y))throw new Error(`legacy subscription pool has duplicate label "${s}": ${e}`);o.add(y);const l=f(A=>Number.isFinite(Number(t[A]))?Number(t[A]):0,"timing"),S=typeof t.accountId=="string"&&t.accountId.trim()!==""?t.accountId.trim():void 0;return{label:s,refresh:c,access:i,expires:d,rateLimitedUntil:l("rateLimitedUntil"),lastAttemptAt:l("lastAttemptAt"),lastRateLimitedAt:l("lastRateLimitedAt"),...t.authFailure==="invalid_grant"?{authFailure:"invalid_grant"}:{},...S?{accountId:S}:{}}})}f(Y,"parseLegacyProviderPool");function Z(e){if(!m(e))return{};const r=JSON.parse(_(e,"utf8"));if(r===null||typeof r!="object"||Array.isArray(r))throw new Error(`legacy provider rotation settings are malformed: ${e}`);const o=r;return{...o.defaultFallbackStrength==="ultra"||o.defaultFallbackStrength==="strong"||o.defaultFallbackStrength==="medium"||o.defaultFallbackStrength==="light"?{defaultFallbackStrength:o.defaultFallbackStrength}:{},...typeof o.revertWhenAvailable=="boolean"?{revertWhenAvailable:o.revertWhenAvailable}:{}}}f(Z,"parseLegacyRotation");async function ee(e,r){const o=[];try{for(const a of e)m(a)&&o.push(await W.lock(a,{realpath:!1,retries:0,stale:3e4,update:1e4}));return await r()}finally{await Promise.allSettled(o.reverse().map(a=>a()))}}f(ee,"withLegacyProviderLocks");async function ye(e){const r=T(e);if(g(r))return;const o=O(r);await ee(o,async()=>{if(g(r))return;const a=new Map(o.slice(0,2).map((t,s)=>[s===0?"anthropic":"openai-codex",Y(t)])),n=Z(o[2]);w(r.root,{recursive:!0,mode:448}),E(r.root,o);for(const[t,s]of a){const c=Object.fromEntries(s.map(d=>[x(),{refresh:d.refresh,access:d.access,expires:d.expires,...d.accountId?{accountId:d.accountId}:{}}]));await new C(u(r.root,`${t}-credentials.json`),!0,`{"version":1,"accounts":{}}
|
|
2
2
|
`).withLockAsync(async(d,y,l)=>(l(`${JSON.stringify({version:1,accounts:c})}
|
|
3
|
-
`),{result:void 0}))}j(r,t=>{F(t,"provider");for(const[s,c]of a){const i=JSON.parse(_(u(r.root,`${s}-credentials.json`),"utf8")),d=Object.keys(i.accounts),y=p(t,"INSERT INTO managed_accounts(provider_id,id,label,label_key,position,last_attempt_at,last_rate_limited_at,rate_limited_until,auth_failure) VALUES(?,?,?,?,?,?,?,?,?)");c.forEach((l,S)=>y.run(s,d[S],l.label,l.label.toLowerCase(),S,l.lastAttemptAt,l.lastRateLimitedAt,l.rateLimitedUntil,l.authFailure??null))}(n.defaultFallbackStrength!==void 0||n.revertWhenAvailable!==void 0)&&p(t,"INSERT INTO rotation_settings(singleton,default_fallback_strength,revert_when_available) VALUES(1,?,?)").run(n.defaultFallbackStrength??null,n.revertWhenAvailable===void 0?null:n.revertWhenAvailable?1:0)})})}f(ye,"convertLegacyProviderStore");function $(e,r,o){if(!m(o))return;const a=h(o);if(a===null||typeof a!="object"||Array.isArray(a))throw new Error(`legacy config is malformed: ${o}`);p(e,"INSERT INTO scope_settings(owner_key,raw_json) VALUES(?,?)").run(r,JSON.stringify(a))}f($,"importSettings");function k(e,r,o){if(!m(o))return;const a=h(o);if(a===null||typeof a!="object"||Array.isArray(a))throw new Error(`legacy state is malformed: ${o}`);const n=a;p(e,"INSERT INTO scope_bookkeeping(owner_key,last_self_check,active_canvas) VALUES(?,?,?)").run(r,n.last_self_check??null,n.activeCanvas??null);const t=p(e,"INSERT INTO scope_marketplaces(owner_key,name,last_updated) VALUES(?,?,?)");for(const[c,i]of Object.entries(n.marketplaces??{}))t.run(r,c,i.last_updated??null);const s=p(e,"INSERT INTO scope_plugins(owner_key,name,last_updated,etag,checked_at) VALUES(?,?,?,?,?)");for(const[c,i]of Object.entries(n.plugins??{}))s.run(r,c,i.last_updated??null,i.etag??null,i.checked_at??null)}f(k,"importState");function re(e,r){$(e,"user",u(r,b)),k(e,"user",u(r,v));const o=u(r,D);if(m(o))for(const n of V(o,{withFileTypes:!0})){if(!n.isDirectory()||!G.test(n.name))continue;const t=n.name,s=u(o,t),c=u(s,"profile.json");if(!m(c))throw new Error(`legacy profile manifest is missing: ${c}`);const i=h(c);if(!i||typeof i!="object"||!Array.isArray(i.projects)||typeof i.name!="string"||typeof i.created_at!="string")throw new Error(`legacy profile manifest is malformed: ${c}`);p(e,"INSERT INTO profiles(id,name,created_at,last_used_at,paused_at,default_kind,grantee,metadata_json) VALUES(?,?,?,?,?,?,?,?)").run(t,i.name,i.created_at,i.last_used_at??null,i.paused_at??null,i.default_kind??null,i.grantee??null,JSON.stringify(i.metadata??{})),p(e,"INSERT INTO scope_owners(owner_key,kind,root,profile_id) VALUES(?,?,?,?)").run(`profile:${t}`,"profile",M(Q(i.grantee??"app:terminal",t).
|
|
3
|
+
`),{result:void 0}))}j(r,t=>{F(t,"provider");for(const[s,c]of a){const i=JSON.parse(_(u(r.root,`${s}-credentials.json`),"utf8")),d=Object.keys(i.accounts),y=p(t,"INSERT INTO managed_accounts(provider_id,id,label,label_key,position,last_attempt_at,last_rate_limited_at,rate_limited_until,auth_failure) VALUES(?,?,?,?,?,?,?,?,?)");c.forEach((l,S)=>y.run(s,d[S],l.label,l.label.toLowerCase(),S,l.lastAttemptAt,l.lastRateLimitedAt,l.rateLimitedUntil,l.authFailure??null))}(n.defaultFallbackStrength!==void 0||n.revertWhenAvailable!==void 0)&&p(t,"INSERT INTO rotation_settings(singleton,default_fallback_strength,revert_when_available) VALUES(1,?,?)").run(n.defaultFallbackStrength??null,n.revertWhenAvailable===void 0?null:n.revertWhenAvailable?1:0)})})}f(ye,"convertLegacyProviderStore");function $(e,r,o){if(!m(o))return;const a=h(o);if(a===null||typeof a!="object"||Array.isArray(a))throw new Error(`legacy config is malformed: ${o}`);p(e,"INSERT INTO scope_settings(owner_key,raw_json) VALUES(?,?)").run(r,JSON.stringify(a))}f($,"importSettings");function k(e,r,o){if(!m(o))return;const a=h(o);if(a===null||typeof a!="object"||Array.isArray(a))throw new Error(`legacy state is malformed: ${o}`);const n=a;p(e,"INSERT INTO scope_bookkeeping(owner_key,last_self_check,active_canvas) VALUES(?,?,?)").run(r,n.last_self_check??null,n.activeCanvas??null);const t=p(e,"INSERT INTO scope_marketplaces(owner_key,name,last_updated) VALUES(?,?,?)");for(const[c,i]of Object.entries(n.marketplaces??{}))t.run(r,c,i.last_updated??null);const s=p(e,"INSERT INTO scope_plugins(owner_key,name,last_updated,etag,checked_at) VALUES(?,?,?,?,?)");for(const[c,i]of Object.entries(n.plugins??{}))s.run(r,c,i.last_updated??null,i.etag??null,i.checked_at??null)}f(k,"importState");function re(e,r){$(e,"user",u(r,b)),k(e,"user",u(r,v));const o=u(r,D);if(m(o))for(const n of V(o,{withFileTypes:!0})){if(!n.isDirectory()||!G.test(n.name))continue;const t=n.name,s=u(o,t),c=u(s,"profile.json");if(!m(c))throw new Error(`legacy profile manifest is missing: ${c}`);const i=h(c);if(!i||typeof i!="object"||!Array.isArray(i.projects)||typeof i.name!="string"||typeof i.created_at!="string")throw new Error(`legacy profile manifest is malformed: ${c}`);p(e,"INSERT INTO profiles(id,name,created_at,last_used_at,paused_at,default_kind,grantee,metadata_json) VALUES(?,?,?,?,?,?,?,?)").run(t,i.name,i.created_at,i.last_used_at??null,i.paused_at??null,i.default_kind??null,i.grantee??null,JSON.stringify(i.metadata??{})),p(e,"INSERT INTO scope_owners(owner_key,kind,root,profile_id) VALUES(?,?,?,?)").run(`profile:${t}`,"profile",M(Q(i.grantee??"app:terminal",t).dir),t);const d=p(e,"INSERT INTO profile_projects(profile_id,path,position) VALUES(?,?,?)"),y=p(e,"INSERT INTO legacy_project_caps(profile_id,path,memory) VALUES(?,?,?)");i.projects.forEach((l,S)=>{d.run(t,l.path,S),l.memory!==void 0&&l.memory!=="content"&&y.run(t,l.path,l.memory)}),$(e,`profile:${t}`,u(s,b)),k(e,`profile:${t}`,u(s,v))}const a=u(r,"profile-defaults.json");if(m(a)){const n=h(a);if(n===null||typeof n!="object"||Array.isArray(n))throw new Error(`legacy profile defaults are malformed: ${a}`);const t=p(e,"INSERT INTO profile_defaults(directory,profile_id) VALUES(?,?)");for(const[s,c]of Object.entries(n)){if(typeof c!="string")throw new Error(`legacy profile default is malformed: ${a}`);t.run(s,c)}}}f(re,"importUser");export{ye as convertLegacyProviderStore,me as convertLegacyScopedStore,ge as initializeFreshScopedStore};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var c=Object.defineProperty;var o=(t,e)=>c(t,"name",{value:e,configurable:!0});import{prepared as n}from"../sqlite-statements.js";import{canonicalRoot as m,userStore as S}from"./paths.js";import{profileSpace as R}from"../layout.js";import{openScopedStore as
|
|
1
|
+
var c=Object.defineProperty;var o=(t,e)=>c(t,"name",{value:e,configurable:!0});import{prepared as n}from"../sqlite-statements.js";import{canonicalRoot as m,userStore as S}from"./paths.js";import{profileSpace as R}from"../layout.js";import{openScopedStore as d,withScopedWrite as f}from"./db.js";function s(t){return S(t)}o(s,"store");function O(t,e){return{profileId:t.id,manifest:{schema_version:1,name:t.name,projects:e,paused_at:t.paused_at,...t.default_kind===null?{}:{default_kind:t.default_kind},...t.grantee===null?{}:{grantee:t.grantee},...t.delivery_limits_json==="{}"?{}:{delivery_limits:JSON.parse(t.delivery_limits_json)},...Object.keys(JSON.parse(t.metadata_json)).length===0?{}:{metadata:JSON.parse(t.metadata_json)},created_at:t.created_at,last_used_at:t.last_used_at}}}o(O,"rowToProfile");function u(t,e){const r=n(t,"SELECT id, name, created_at, last_used_at, paused_at, default_kind, grantee, metadata_json, delivery_limits_json FROM profiles WHERE id=?").get(e);if(!r)return null;const i=n(t,"SELECT path FROM profile_projects WHERE profile_id=? ORDER BY position").all(e);return O(r,i)}o(u,"get");function I(t){const e=d(s(t));return n(e,"SELECT id FROM profiles ORDER BY rowid").all().map(({id:i})=>u(e,i)).filter(Boolean)}o(I,"listStoredProfiles");function N(t,e){return u(d(s(t)),e)}o(N,"getStoredProfile");function k(t,e){return n(d(s(t)),"SELECT paused_at FROM profiles WHERE id=?").get(e)?.paused_at!=null}o(k,"profilePaused");function h(t){const e=n(d(s(t)),"SELECT id FROM profiles WHERE paused_at IS NOT NULL").all();return new Set(e.map(r=>r.id))}o(h,"pausedProfileIds");function D(t,e){f(s(t),r=>{n(r,"INSERT INTO profiles(id,name,created_at,last_used_at,paused_at,default_kind,grantee,metadata_json,delivery_limits_json) VALUES(?,?,?,?,?,?,?,?,?)").run(e.profileId,e.manifest.name,e.manifest.created_at,e.manifest.last_used_at,e.manifest.paused_at,e.manifest.default_kind??null,e.manifest.grantee??null,JSON.stringify(e.manifest.metadata??{}),JSON.stringify(e.manifest.delivery_limits??{})),n(r,"INSERT INTO scope_owners(owner_key,kind,root,profile_id) VALUES(?,?,?,?)").run(`profile:${e.profileId}`,"profile",m(R(e.manifest.grantee??"app:terminal",e.profileId).dir),e.profileId);const i=n(r,"INSERT INTO profile_projects(profile_id,path,position) VALUES(?,?,?)");e.manifest.projects.forEach((l,a)=>i.run(e.profileId,l.path,a))})}o(D,"insertStoredProfile");function F(t,e,r){return f(s(t),i=>{const l=u(i,e);if(!l)return null;const a=r(l.manifest);n(i,"UPDATE profiles SET name=?, last_used_at=?, paused_at=?, default_kind=?, grantee=?, metadata_json=?, delivery_limits_json=? WHERE id=?").run(a.name,a.last_used_at,a.paused_at,a.default_kind??null,a.grantee??null,JSON.stringify(a.metadata??{}),JSON.stringify(a.delivery_limits??{}),e),n(i,"DELETE FROM profile_projects WHERE profile_id=?").run(e);const p=n(i,"INSERT INTO profile_projects(profile_id,path,position) VALUES(?,?,?)");return a.projects.forEach((E,_)=>p.run(e,E.path,_)),{profileId:e,manifest:a}})}o(F,"updateStoredProfile");function M(t,e){return f(s(t),r=>{if(!u(r,e))return!1;const i=`profile:${e}`;return n(r,"DELETE FROM scope_settings WHERE owner_key=?").run(i),n(r,"DELETE FROM scope_bookkeeping WHERE owner_key=?").run(i),n(r,"DELETE FROM scope_marketplaces WHERE owner_key=?").run(i),n(r,"DELETE FROM scope_plugins WHERE owner_key=?").run(i),n(r,"DELETE FROM scope_owners WHERE owner_key=?").run(i),n(r,"DELETE FROM profiles WHERE id=?").run(e),!0})}o(M,"deleteStoredProfile");function W(t){f(s(t),e=>{n(e,"UPDATE profiles SET grantee = 'app:terminal' WHERE grantee = 'user:local'").run()})}o(W,"rewriteLocalProfileGrantees");function x(t){const e=d(s(t));return Object.fromEntries(n(e,"SELECT directory,profile_id FROM profile_defaults").all().map(r=>[r.directory,r.profile_id]))}o(x,"readStoredDefaults");function H(t,e){return f(s(t),r=>{const i=Object.fromEntries(n(r,"SELECT directory,profile_id FROM profile_defaults").all().map(p=>[p.directory,p.profile_id])),l=e(i);n(r,"DELETE FROM profile_defaults").run();const a=n(r,"INSERT INTO profile_defaults(directory,profile_id) VALUES(?,?)");for(const[p,E]of Object.entries(l))a.run(p,E);return l})}o(H,"mutateStoredDefaults");function P(t){return n(d(s(t)),"SELECT profile_id, path, memory FROM legacy_project_caps ORDER BY profile_id, path").all().map(e=>({profileId:e.profile_id,path:e.path,memory:e.memory}))}o(P,"listLegacyProjectCaps");function v(t,e){f(s(t),r=>{const i=n(r,"UPDATE profiles SET delivery_limits_json=? WHERE id=?");for(const l of e)if(i.run(JSON.stringify(l.deliveryLimits),l.profileId).changes!==1)throw new Error(`no stored profile ${l.profileId}`);n(r,"DELETE FROM legacy_project_caps").run()})}o(v,"commitConvertedProjectCaps");export{v as commitConvertedProjectCaps,M as deleteStoredProfile,N as getStoredProfile,D as insertStoredProfile,P as listLegacyProjectCaps,I as listStoredProfiles,H as mutateStoredDefaults,h as pausedProfileIds,k as profilePaused,x as readStoredDefaults,W as rewriteLocalProfileGrantees,F as updateStoredProfile};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var k=Object.defineProperty;var a=(e,t)=>k(e,"name",{value:t,configurable:!0});import{prepared as r}from"../sqlite-statements.js";import{createHash as E}from"node:crypto";import{openScopedStore as p,withScopedWrite as _}from"./db.js";function l(e,t,n){const o=r(e,"SELECT kind, root, profile_id FROM scope_owners WHERE owner_key = ?").get(t.key);if(!o)throw new Error(`scoped SQL owner is missing: ${t.key}`);if(o.kind!==t.kind||(t.profileId??null)!==o.profile_id)throw new Error(`scoped SQL owner does not match its root: ${t.key}`);if(o.root===t.root)return;if(!(t.kind==="profile"&&t.profileId!==void 0)&&(t.kind!=="project"||t.store.kind!=="project"||t.store.root!==t.root))throw new Error(`scoped SQL owner does not match its root: ${t.key}`);n==="write"&&r(e,"UPDATE scope_owners SET root = ? WHERE owner_key = ?").run(t.root,t.key)}a(l,"ensureOwner");function u(e){return e===void 0?"absent":E("sha256").update(e.raw_json).digest("hex")}a(u,"revisionFor");function d(e,t){return r(e,"SELECT raw_json FROM scope_settings WHERE owner_key = ?").get(t.key)}a(d,"readRawSettingsRow");class f extends Error{static{a(this,"SettingsRevisionConflictError")}constructor(){super("settings changed since the supplied revision"),this.name="SettingsRevisionConflictError"}}function g(e){const t=p(e.store);l(t,e,"read");const n=d(t,e);return{revision:u(n),settings:n===void 0?null:JSON.parse(n.raw_json)}}a(g,"readRawSettingsWithRevision");function R(e){return g(e).settings}a(R,"readRawSettings");function h(e,t,n,o){return _(e.store,i=>{l(i,e,"write");const s=d(i,e);if(n!==u(s))throw new f;o(t);const c=JSON.stringify(t);return s?.raw_json===c?{revision:u(s),changed:!1}:(r(i,"INSERT INTO scope_settings(owner_key, raw_json) VALUES (?, ?) ON CONFLICT(owner_key) DO UPDATE SET raw_json=excluded.raw_json").run(e.key,c),{revision:u({raw_json:c}),changed:!0})})}a(h,"replaceRawSettings");function m(e,t){_(e.store,n=>{l(n,e,"write"),r(n,"INSERT INTO scope_settings(owner_key, raw_json) VALUES (?, ?) ON CONFLICT(owner_key) DO UPDATE SET raw_json=excluded.raw_json").run(e.key,JSON.stringify(t))})}a(m,"writeRawSettings");function v(e,t){return _(e.store,n=>{l(n,e,"write");const o=r(n,"SELECT raw_json FROM scope_settings WHERE owner_key = ?").get(e.key),i=t(o?JSON.parse(o.raw_json):{});return r(n,"INSERT INTO scope_settings(owner_key, raw_json) VALUES (?, ?) ON CONFLICT(owner_key) DO UPDATE SET raw_json=excluded.raw_json").run(e.key,JSON.stringify(i.next)),i.value})}a(v,"mutateRawSettings");function w(e){const t=p(e.store);l(t,e,"read");const n=r(t,"SELECT last_self_check, active_canvas FROM scope_bookkeeping WHERE owner_key = ?").get(e.key),o=Object.fromEntries(r(t,"SELECT name, last_updated FROM scope_marketplaces WHERE owner_key = ?").all(e.key).map(s=>[s.name,s.last_updated===null?{}:{last_updated:s.last_updated}])),i=Object.fromEntries(r(t,"SELECT name, last_updated, etag, checked_at FROM scope_plugins WHERE owner_key = ?").all(e.key).map(s=>[s.name,{...s.last_updated===null?{}:{last_updated:s.last_updated},...s.etag===null?{}:{etag:s.etag},...s.checked_at===null?{}:{checked_at:s.checked_at}}]));return{marketplaces:o,plugins:i,...n?.last_self_check===null||n===void 0?{}:{last_self_check:n.last_self_check},activeCanvas:n?.active_canvas??null}}a(w,"readBookkeeping");function N(e,t){_(e.store,n=>{l(n,e,"write"),r(n,"INSERT INTO scope_bookkeeping(owner_key, last_self_check, active_canvas) VALUES (?, ?, ?) ON CONFLICT(owner_key) DO UPDATE SET last_self_check=excluded.last_self_check, active_canvas=excluded.active_canvas").run(e.key,t.last_self_check??null,t.activeCanvas??null),r(n,"DELETE FROM scope_marketplaces WHERE owner_key = ?").run(e.key),r(n,"DELETE FROM scope_plugins WHERE owner_key = ?").run(e.key);const o=r(n,"INSERT INTO scope_marketplaces(owner_key, name, last_updated) VALUES (?, ?, ?)");for(const[s,c]of Object.entries(t.marketplaces))o.run(e.key,s,c.last_updated??null);const i=r(n,"INSERT INTO scope_plugins(owner_key, name, last_updated, etag, checked_at) VALUES (?, ?, ?, ?, ?)");for(const[s,c]of Object.entries(t.plugins))i.run(e.key,s,c.last_updated??null,c.etag??null,c.checked_at??null)})}a(N,"writeBookkeeping");export{f as SettingsRevisionConflictError,v as mutateRawSettings,w as readBookkeeping,R as readRawSettings,g as readRawSettingsWithRevision,h as replaceRawSettings,N as writeBookkeeping,m as writeRawSettings};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var f=Object.defineProperty;var n=(o,r)=>f(o,"name",{value:r,configurable:!0});import{chownSync as a,chmodSync as d,existsSync as
|
|
2
|
-
`).find(e=>e.startsWith("crtr-data:"));if(!o)throw new Error("crtr-data group is required on sandboxed Linux");const r=Number(o.split(":")[2]);if(!Number.isInteger(r))throw new Error("invalid crtr-data group id");return r}n(p,"dataGroup");function L(){const o=y(),r=p();c(o,{recursive:!0,mode:1528});const e=
|
|
1
|
+
var f=Object.defineProperty;var n=(o,r)=>f(o,"name",{value:r,configurable:!0});import{chownSync as a,chmodSync as d,existsSync as l,mkdirSync as c,readFileSync as h,statSync as m}from"node:fs";import{appSpace as g,appsRoot as w,userFilesRoot as y}from"../layout.js";import{launcherRequest as S}from"../../launcher/client.js";import{LAUNCHER_SOCKET as x}from"../../launcher/spec.js";import{allocateAppUser as E,uidForApp as v}from"./users.js";function p(){const o=h("/etc/group","utf8").split(`
|
|
2
|
+
`).find(e=>e.startsWith("crtr-data:"));if(!o)throw new Error("crtr-data group is required on sandboxed Linux");const r=Number(o.split(":")[2]);if(!Number.isInteger(r))throw new Error("invalid crtr-data group id");return r}n(p,"dataGroup");function L(){const o=y(),r=p();c(o,{recursive:!0,mode:1528});const e=m(o),t=e.mode&4095;if(!e.isDirectory()||e.gid!==r||t!==1528){if(!e.isDirectory()||e.uid!==process.getuid())throw new Error(`${o} has owner ${e.uid}, group ${e.gid}, mode ${t.toString(8).padStart(4,"0")}; requires an crtr-data (${r}) directory with mode 2770; only its owner can repair it`);e.gid!==r&&a(o,e.uid,r),t!==1528&&d(o,1528)}}n(L,"ensureUserFiles");async function N(o){const r=g(o);if(!(process.platform==="linux"&&l(x))){if(process.platform==="linux")throw new Error("app space requires the root launcher on Linux");for(const i of[r.root,r.profiles,r.conversations,r.nodes,r.files,r.home,r.cache])c(i,{recursive:!0});return{uid:v(o),gid:process.getgid(),root:r.root}}const t=p(),s=E(o),u=await S({operation:"user.create",app:r.root.slice(w().length+1),uid:s.uid});if(!("uid"in u)||u.uid!==s.uid)throw new Error(`launcher did not create OS user for ${o}`);for(const i of[r.root,r.profiles,r.conversations,r.nodes,r.files,r.home,r.cache])c(i,{recursive:!0,mode:1528}),a(i,process.getuid(),t),d(i,1528);return{uid:s.uid,gid:t,root:r.root}}n(N,"ensureAppSpace");export{N as ensureAppSpace,L as ensureUserFiles};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var y=Object.defineProperty;var c=(i,
|
|
1
|
+
var y=Object.defineProperty;var c=(i,e)=>y(i,"name",{value:e,configurable:!0});import{closeSync as m,constants as h,createReadStream as v,fstatSync as b,realpathSync as g}from"node:fs";import{extname as C,isAbsolute as N,join as w,relative as l,resolve as R,sep as d}from"node:path";import{pipeline as S}from"node:stream/promises";import{appsRoot as E,runtimeRoot as _,userRoot as $}from"../layout.js";import{RunError as f}from"../runs/operations.js";import{isBeneathRefusal as O,openBeneath as I}from"../spaces/open-beneath.js";import{fileRoot as T}from"../spaces/permissions.js";const j={".txt":"text/plain; charset=utf-8",".log":"text/plain; charset=utf-8",".md":"text/markdown; charset=utf-8",".csv":"text/csv; charset=utf-8",".tsv":"text/tab-separated-values; charset=utf-8",".json":"application/json",".pdf":"application/pdf",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp",".heic":"image/heic",".heif":"image/heif",".avif":"image/avif",".bmp":"image/bmp",".tif":"image/tiff",".tiff":"image/tiff",".mp3":"audio/mpeg",".m4a":"audio/mp4",".wav":"audio/wav",".ogg":"audio/ogg",".mp4":"video/mp4",".mov":"video/quicktime",".webm":"video/webm",".zip":"application/zip",".gz":"application/gzip",".tar":"application/x-tar",".doc":"application/msword",".docx":"application/vnd.openxmlformats-officedocument.wordprocessingml.document",".xls":"application/vnd.ms-excel",".xlsx":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",".ppt":"application/vnd.ms-powerpoint",".pptx":"application/vnd.openxmlformats-officedocument.presentationml.presentation"},L=new Set(["text/html","image/svg+xml","application/xhtml+xml"]);function A(i){const e=j[C(i).toLowerCase()]??"application/octet-stream";return L.has(e.split(";")[0].trim().toLowerCase())?"application/octet-stream":e}c(A,"contentTypeFor");function u(i,e){const o=l(i,e);return o===""||o!==".."&&!o.startsWith(`..${d}`)&&!o.startsWith(d)}c(u,"contains");function D(i){const e=c(n=>process.platform==="darwin"?n.toLowerCase():n,"fold"),o=e(i),r=e(E());if(u(e(_()),o)||u(e(w($(),"memory")),o))return!0;if(!u(r,o))return!1;const t=l(r,o).split(d)[1];return t==="memory"||t==="profiles"||t==="conversations"}c(D,"withheld");function z(i,e){if(typeof e!="string"||!e)throw new f(400,"invalid_request","path is required","path");if(!N(e)||e.includes("\0"))throw new f(400,"invalid_request","path must be absolute","path");const o=R(e),r=c(()=>new f(404,"not_found",`no such file: ${o}`),"notFound");if(D(o))throw r();const t=T(i,o,"read");if("hidden"in t)throw r();if(!("root"in t))throw new f(403,"scope_missing",t.missing?`missing scope ${t.missing}`:`no permission names ${o}`,void 0,void 0,t.missing??void 0);const n=c(()=>new f(400,"invalid_request",`${o} meets a symlink, leaves its permitted root, or is not a regular file`,"path"),"symlink");let p;try{p=I(t.root,o,h.O_RDONLY|h.O_NONBLOCK)}catch(a){if(O(a))throw n();const s=a.code;throw s==="ENOENT"||s==="ENOTDIR"||s==="EISDIR"||s==="ENXIO"||s==="EACCES"||s==="EPERM"?r():a}try{if(process.platform!=="linux"){let s;try{s=g(o)}catch{throw r()}if(s!==w(g(t.root),l(t.root,o)))throw n()}const a=b(p);if(!a.isFile())throw r();return{fd:p,path:o,size:a.size}}catch(a){throw m(p),a}}c(z,"openCallerFile");function M(i,e){if(i===void 0)return null;const o=c(()=>new f(416,"range_not_satisfiable","range not satisfiable","range",{size:e}),"refuse"),r=/^bytes=(.+)$/i.exec(i.trim());if(!r)throw o();if(r[1].includes(","))return null;const t=/^\s*(\d*)\s*-\s*(\d*)\s*$/.exec(r[1]);if(!t||t[1]===""&&t[2]==="")throw o();let n,p;if(t[1]===""){const a=Number(t[2]);if(!Number.isSafeInteger(a)||a===0||e===0)throw o();n=Math.max(0,e-a),p=e-1}else if(n=Number(t[1]),p=t[2]===""?e-1:Math.min(Number(t[2]),e-1),!Number.isSafeInteger(n)||!Number.isSafeInteger(p)||n>=e||t[2]!==""&&Number(t[2])<n)throw o();return{start:n,end:p}}c(M,"parseRange");async function X(i,e,o,r){const t=z(o,r);let n;try{const s=i.headers.range;try{n=M(Array.isArray(s)?s[0]:s,t.size)}catch(x){throw e.setHeader("Content-Range",`bytes */${t.size}`),x}}catch(s){throw m(t.fd),s}const p={"Content-Type":A(t.path),"X-Content-Type-Options":"nosniff","Content-Security-Policy":"sandbox; default-src 'none'","Accept-Ranges":"bytes","Cache-Control":"no-store"};if(i.destroyed||e.destroyed){m(t.fd);return}if(n?e.writeHead(206,{...p,"Content-Length":String(n.end-n.start+1),"Content-Range":`bytes ${n.start}-${n.end}/${t.size}`}):e.writeHead(200,{...p,"Content-Length":String(t.size)}),t.size===0||n&&n.end<n.start){m(t.fd),e.end();return}const a=v("",{fd:t.fd,autoClose:!0,...n?{start:n.start,end:n.end}:{start:0}});try{await S(a,e)}catch{a.destroy(),e.destroy()}}c(X,"serveDownload");export{A as contentTypeFor,z as openCallerFile,M as parseRange,X as serveDownload};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var I=Object.defineProperty;var i=(e,r)=>I(e,"name",{value:r,configurable:!0});import{randomUUID as O}from"node:crypto";import{readConfig as $}from"../../../core/config.js";import{activeGrantForApp as q,localGrants as w}from"../../../core/grants/mirror.js";import{getNode as x}from"../../../core/canvas/canvas.js";import{scopeAllowed as G}from"../../../core/scopes.js";import{ProviderError as u,providerErrorBody as N,scopeMissing as T}from"../../../core/providers/errors.js";import{callProvider as C,providerTools as F}from"../../../core/providers/call.js";import{providerIdsFromGrants as j,providerInstallState as D}from"../../../core/providers/bundles.js";import{providerRootClaims as k,resolveProviderRef as M}from"../../../core/providers/roots.js";import{listInstalledPlugins as U}from"../../../core/resolver.js";import{listAudit as z,writeProviderAudit as L}from"../../../core/providers/audit.js";import{emitEvent as _}from"../../../core/events/emit.js";import{resolvedScopes as g}from"../router.js";const be=new Set(["provider.tools","provider.call","audit.list"]);function V(e){if(e.class==="forwarded"||e.class==="machine")throw new u("permission_error","this operation is not available to the caller");if(e.class==="person")return{class:"person",sub:null,grantee:null,client_id:null,resolved:null,nodeScopes:null,run_id:null,node_id:null};if(e.class==="app"){const r=q(e.app);if(!r)throw new u("grant_revoked",`app ${e.app} has no active grant`);return{class:"app",sub:r.sub,grantee:e.app,client_id:null,resolved:r.resolved,nodeScopes:e.scopes,run_id:null,node_id:null}}return e.class==="cron"?{class:"app",sub:e.sub,grantee:e.app,client_id:null,resolved:g(e),nodeScopes:e.scopes,run_id:e.origin_run,node_id:null}:{class:"broker",sub:e.sub,grantee:e.app,client_id:e.client_id,resolved:g(e),nodeScopes:e.scopes,run_id:e.run??x(e.node)?.run_id??null,node_id:e.node}}i(V,"callerFromRequest");function we(e){return{class:"app",sub:e.sub,grantee:e.grantee,client_id:e.client_id,resolved:e.resolved,nodeScopes:null,run_id:null,node_id:null}}i(we,"callerFromApp");const Z=/^([A-Za-z0-9_-]{1,64}):([A-Za-z0-9_-]{1,64})$/;function B(e){const r=Z.exec(e);return r!==null&&r[1]!=="crtr"}i(B,"isProviderScope");const y=new WeakMap;function H(e){let r=y.get(e);return r===void 0&&(r=new Set(e),y.set(e,r)),r}i(H,"heldSet");function S(e,r){return e===null?!0:B(r)?H(e).has(r):G(e,r)}i(S,"holds");function A(e,r){return e.class==="person"?!0:S(e.resolved??[],r)&&S(e.nodeScopes,r)}i(A,"callerAllows");function _e(e,r,t){const o=`${r}:${t}`;if(!A(e,o))throw T(o)}i(_e,"requireGroup");function W(){return!!$("user").runtime?.issuer}i(W,"providerOperationsEnabled");const J=/^[A-Za-z0-9_-]{1,64}$/;function c(e,r){return new u("invalid_request",e,{param:r})}i(c,"invalid");function E(e){if(typeof e!="string"||!J.test(e)||e==="crtr")throw c("provider must be a provider id","provider");return e}i(E,"parseProvider");function X(e){if(!e||typeof e!="object"||Array.isArray(e))throw new u("invalid_request","a JSON object is required");const r=e,t=E(r.provider),o=r.tool;if(typeof o!="string"||!o.trim())throw c("tool must be a non-empty string","tool");const d=r.arguments??{};if(!d||typeof d!="object"||Array.isArray(d))throw c("arguments must be an object","arguments");const a=r.version;if(a!==void 0&&(typeof a!="number"||!Number.isInteger(a)||a<1))throw c("version must be an integer >= 1","version");const n=r.approval;if(n!==void 0&&(typeof n!="string"||!n))throw c("approval must be a question id","approval");const l=r.machine;if(l!==void 0&&(typeof l!="string"||!l.trim()))throw c("machine must be a machine id","machine");return{provider:t,tool:o.trim(),arguments:d,...a===void 0?{}:{version:a},...n===void 0?{}:{approval:n},...l===void 0?{}:{machine:l.trim()}}}i(X,"parseCallInput");function K(e,r=new URLSearchParams){const t=i((n,l,p)=>{const f=r.get(n);if(f===null)return l;if(!/^\d+$/.test(f)||Number(f)>p||Number(f)<1)throw c(`invalid ${n}`,n);return Number(f)},"numeric"),o=r.has("after")?t("after",0,Number.MAX_SAFE_INTEGER):void 0,d=t("limit",100,1e3),a=Object.fromEntries(["operation","outcome","run_id","since","until"].filter(n=>r.has(n)).map(n=>[n,r.get(n)]));return{status:200,body:z(e.class==="person"?null:e.grantee,{after:o,limit:d,...a})}}i(K,"auditList");async function Q(e,r,t){let o=r;const d=performance.now(),a=U("user"),{skipped:n}=k(a),l=new Set(a.filter(s=>s.enabled&&s.manifest.provider?.id===s.name&&!n.has(s.name)).map(s=>s.name)),p=j(w()).filter(s=>!n.has(s)),f=[...new Set([...l,...p])].sort();if(o!==void 0){const s=M(o);if(s===null)throw new u("not_found",`provider "${o}" is not available`);o=s}if(o!==void 0&&!f.includes(o))throw new u("not_found",`provider "${o}" is not available`);const P=o===void 0?f:[o],R=e.class==="person"?w().flatMap(s=>s.status==="active"?s.resolved_scopes.map(({scope:h})=>h):[]):e.resolved??[],v=await Promise.all(P.map(async s=>{if(!R.some(m=>m.startsWith(`${s}:`)&&A(e,m)))return null;if(l.has(s)){const m=await F(e,s);return m.groups.length>0?m:null}const b=D(s);return{provider:s,display_name:s,status:b.status,...b.error?{error:b.error}:{},groups:[],commands:[]}})).then(s=>s.filter(h=>h!==null));if(_({level:"debug",event:"api.provider.tools",duration_ms:performance.now()-d,fields:{caller:e.class,provider:o??null,providers:v.length}}),o!==void 0){if(v.length===0)throw new u("not_found",`provider "${o}" is not available`);return{status:200,body:v[0]}}return{status:200,body:{providers:v}}}i(Q,"providerTools");async function Y(e,r,t){return C(e,r,t)}i(Y,"providerCall");async function ee(e,r,t,o){if(!W())throw new u("not_found","provider operations need a directory-connected runtime");if(e==="audit.list")return K(r,t.query);if(e==="provider.tools")return Q(r,t.params.provider===void 0?void 0:E(t.params.provider),o);if(e==="provider.call"){let d;try{d=X(t.body)}catch(a){const n=t.body&&typeof t.body=="object"&&!Array.isArray(t.body)?t.body:{};throw L({request_id:o.requestId,caller:r,provider:typeof n.provider=="string"?n.provider:"",tool:typeof n.tool=="string"?n.tool:"",outcome:`refused:${a instanceof u?a.code:"invalid_request"}`}),a}return Y(r,d,o)}throw new u("not_found","unknown operation")}i(ee,"handleProviderOperation");function re(e){const r=new AbortController,t=i(()=>{e.writableFinished||r.abort()},"abort");return e.on("close",t),{signal:r.signal,release:i(()=>e.off("close",t),"release")}}i(re,"abortOnClose");function oe(e,r,t,o){const d=N(e,r,t,o??void 0);return d.status>=500&&!(e instanceof u)&&_({event:"api.provider.failed",level:"error",error:e instanceof Error?e:new Error(String(e)),fields:{request_id:r}}),d}i(oe,"providerFailure");const ge=[{method:"GET",pattern:"/v1/audit",name:"audit.list"},{method:"GET",pattern:"/v1/providers/tools",name:"provider.tools"},{method:"GET",pattern:"/v1/providers/:provider/tools",name:"provider.tools"},{method:"POST",pattern:"/v1/provider/call",name:"provider.call"}].map(({method:e,pattern:r,name:t})=>({method:e,pattern:r,handler:i(async o=>{const d=O(),{signal:a,release:n}=re(o.res);let l=null;try{return l=V(o.principal),{...await ee(t,l,{params:o.params,body:o.body,query:new URL(o.req.url??"/","http://runtime.local").searchParams},{requestId:d,signal:a}),headers:{"Request-Id":d}}}catch(p){return oe(p,d,!1,l?.run_id)}finally{n()}},"handler")}));export{be as PROVIDER_OPERATIONS,re as abortOnClose,A as callerAllows,we as callerFromApp,V as callerFromRequest,ee as handleProviderOperation,X as parseCallInput,E as parseProvider,oe as providerFailure,W as providerOperationsEnabled,ge as providerRoutes,_e as requireGroup};
|