@north-light/cli 0.3.457 → 0.3.458
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/core/command-plugins/compose.js +1 -1
- package/dist/core/command-plugins/discovery.js +1 -1
- package/dist/core/grants/on-sync.js +1 -1
- 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/daemon/api/handlers/providers.js +1 -1
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +12 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var w=Object.defineProperty;var u=(n,e)=>w(n,"name",{value:e,configurable:!0});import{resolve as x}from"node:path";import{envNodeId as C}from"../../shared/env.js";import{cliClient as f}from"../../commands/api-client.js";import{defineBranch as h,defineLeaf as j}from"../command.js";import{resolveCommandRegistry as L}from"../command-manifests/registry.js";import{executeExternalLeaf as U}from"./transport/exec-invoke.js";import{invokeHttpLeaf as M}from"./transport/http-invoke.js";async function T(n){const e=await f().request("GET",`/v1/providers/${encodeURIComponent(n)}/tools`);return new Set(e.groups.flatMap(r=>r.leaves.map(a=>a.tool)))}u(T,"allowedProviderTools");function m(n,e,r){return n.children.some(a=>a.kind==="leaf"?r.has([...e,a.name].join(" ")):m(a,[...e,a.name],r))}u(m,"hasAllowedLeaf");function S(n,e,r){return n.children.filter(a=>a.tier!=="hidden"&&(a.kind==="leaf"?r.has([...e,a.name].join(" ")):m(a,[...e,a.name],r))).length}u(S,"visibleChildCount");function A(n,e,r){return async()=>{if(!C())return n.help;const a=await T(e.id);return{...n.help,listing:n.help.listing?.flatMap(t=>{const i=n.children.find(s=>s.name===t.name);if(i?.kind==="leaf")return a.has([...r,i.name].join(" "))?[t]:[];if(i?.kind!=="branch")return[];const o=[...r,i.name];if(!m(i,o,a))return[];const p=S(i,o,a);return[{...t,...t.subCount!==void 0?{subCount:p}:{}}]})}}}u(A,"providerHelp");const N="Runs on one of your devices. Add `--machine <machine_id>` to pick which; without it the call goes to your only device, and with several the answer is `machine_required` listing each one's machine_id. The `runtime:` prefix on the id is optional.";function g(n,e,r,a=!1){return{outputKind:"object",...a?{machineSelector:!0}:{},run:u(async(t,i)=>{const o={...t};for(const p of r.params){if(!("type"in p)||p.type!=="file")continue;const s=p.name.replace(/-([a-z])/g,(c,k)=>k.toUpperCase()),d=o[s];typeof d=="string"&&(o[s]=x(d))}return f({timeoutMs:0}).request("POST","/v1/provider/call",{provider:n,tool:e,arguments:o,...i?.machine===void 0?{}:{machine:i.machine}},{timeout:0})},"run")}}u(g,"providerLeafAdapter");function v(n,e){return/^[A-Za-z0-9_-]{1,64}$/.test(n)&&n!=="crtr"&&!e.has(n)}u(v,"validRootName");function B(n,e){const r=[],a=[];for(const t of n){const{provider:i,commands:o}=t;if(!Array.isArray(o)||t.status!=="available"&&!v(i,e))continue;if(t.status!=="available"){const s=t.error??"bundle installation pending",d=`Unavailable (${t.status}): ${s}`,c=h({name:i,rootEntry:{concept:t.display_name,desc:d,useWhen:d},help:{name:i,summary:d},children:[]});c.unavailable=d,a.push(c);continue}const p=u(s=>s.kind==="branch"?{...s,children:s.children.map(p)}:{...s,rest:{method:"GET",path:"/",params:{}}},"helpNode");for(const s of o)s.kind!=="branch"||!Array.isArray(s.children)||!v(s.name,e)||r.push({contributor:{name:i,scope:"user"},manifestName:s.name,node:p(s),adaptLeaf:u((d,c)=>g(i,c.slice(1).join(" "),d,t.transport==="machine"),"adaptLeaf")})}return[..._(L(r,e)),...a]}u(B,"composeSandboxProviderSubtrees");function y(n,e){return(r,a)=>{const t=n.transport,i=e(a);if(n.provider!==void 0)return g(n.provider.id,i.slice(1).join(" "),r,t.kind==="machine");if(t.kind==="exec"){const p=t.executable;if(p===void 0)throw new Error(`exec plugin "${n.plugin.name}" has an executable leaf without transport.executable`);return{outputKind:"object",run:u(async s=>U({plugin:n.plugin,executable:p,commandPath:i,output:r.output},s),"run")}}if(t.kind==="machine")throw new Error(`machine plugin "${n.plugin.name}" is not a capability provider`);const o=r;return{outputKind:o.rest.streaming===!0?"jsonl":"object",run:u((p,s)=>M({registration:{name:n.plugin.name,endpoint:t.endpoint,...t.authEnv!==void 0?{authEnv:t.authEnv}:{}},...n.manifest?.baseUrl!==void 0?{baseUrl:n.manifest.baseUrl}:{},...n.manifest?.timeouts!==void 0?{timeouts:n.manifest.timeouts}:{},params:o.params,rest:o.rest,output:o.output,commandPath:i},p,s),"run")}}}u(y,"adaptLeaf");function G(n){return{contributions:n.map(e=>({contributor:{name:e.plugin.name,scope:e.plugin.scope,sourceMarketplace:e.plugin.sourceMarketplace},node:e.node,manifestName:e.manifestName,adaptLeaf:y(e,r=>[e.manifestName,...r.slice(1)]),...e.provider!==void 0?{provider:e.provider}:{}})),coreMounts:[],issues:[]}}u(G,"adaptPluginContributions");function z(n){return n.map(e=>({parent:e.parent,plugin:e.plugin.name,contributor:{name:e.plugin.name,scope:e.plugin.scope,sourceMarketplace:e.plugin.sourceMarketplace},node:e.node,...e.provider!==void 0?{provider:e.provider}:{},adaptLeaf:y({...e,manifestName:e.node.name},r=>e.provider===void 0?[...r]:[e.provider.id,...r.slice(e.parent.length)])}))}u(z,"adaptPluginCoreMounts");function _(n){return n.contributions.map(e=>l(e,e.node,[e.node.name]))}u(_,"composeExternalSubtrees");function D(n,e){return n.filter(r=>r.parent.join("\0")===e.join("\0")).map(r=>r.node.kind==="branch"?l(r,r.node,[...e,r.node.name],r.provider===void 0?1:e.length):E(r,r.node,[...e,r.node.name]))}u(D,"composeCoreBranchMounts");function l(n,e,r,a=1){const t=e.rootEntry===void 0?void 0:{concept:e.rootEntry.concept,desc:e.rootEntry.description,useWhen:e.rootEntry.whenToUse},i=h({name:e.name,description:e.description,whenToUse:e.whenToUse,...e.tier!==void 0?{tier:e.tier}:{},...n.plugin!==void 0?{plugin:n.plugin}:{},...t!==void 0?{rootEntry:t}:{},help:{name:r.join(" "),summary:e.summary,...e.model!==void 0?{model:e.model}:{},...r.length===1&&n.extensionIssue!==void 0?{extensionIssue:n.extensionIssue}:{}},...e.passthrough!==void 0?{passthrough:e.passthrough}:{},children:e.children.map(o=>o.kind==="branch"?l(n,o,[...r,o.name],a):E(n,o,[...r,o.name]))});return n.provider!==void 0&&(i.effectiveHelp=A(i,n.provider,r.slice(a))),i}u(l,"buildBranch");function E(n,e,r){const a=n.adaptLeaf(e,r);return j({name:e.name,description:e.description,whenToUse:e.whenToUse,...e.tier!==void 0?{tier:e.tier}:{},...n.plugin!==void 0?{plugin:n.plugin}:{},...a.machineSelector===!0?{machineSelector:!0}:{},help:{name:r.join(" "),summary:e.summary,...a.machineSelector===!0?{guide:N}:{},params:e.params,output:e.output,outputKind:a.outputKind,effects:[...e.effects]},run:a.run})}u(E,"buildLeaf");export{G as adaptPluginContributions,z as adaptPluginCoreMounts,D as composeCoreBranchMounts,_ as composeExternalSubtrees,B as composeSandboxProviderSubtrees};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var C=Object.defineProperty;var u=(e,t)=>C(e,"name",{value:t,configurable:!0});import{realpathSync as j,readFileSync as N,statSync as g}from"node:fs";import{isSandboxedNode as F}from"../../shared/env.js";import{isAbsolute as O,resolve as k,sep as q}from"node:path";import{listInstalledPlugins as A,listInstalledPluginsInRoot as E}from"../resolver.js";import{projectScopeRoots as R}from"../scope.js";import{validateCommandManifest as I}from"../../api/command-manifest/manifest.js";import{qualifiedTopLevelCommandName as J,resolveCommandRegistry as $}from"../command-manifests/registry.js";import{adaptPluginContributions as T,adaptPluginCoreMounts as B}from"./compose.js";import{validateHttpPluginTransport as H}from"./endpoint.js";import{applyCommandExtensions as L}from"./extensions.js";function _(e=process.cwd(),t){const i=new Set,a=[],s=u(n=>{i.has(n.name)||(i.add(n.name),a.push(n))},"consider");for(const n of R(e,t))for(const r of E("project",n))s(r);if(!F())for(const n of A("user"))s(n);return a.filter(n=>n.enabled&&(n.manifest.commands!==void 0||n.manifest.transport!==void 0))}u(_,"effectiveCommandPlugins");function W(e,t){return i=>t.push({...i,plugin:e.name})}u(W,"issueFor");function v(e,t){if(typeof t!="string"||t.length===0)return null;try{const i=j(e),a=j(k(e,t));return(a===i||a.startsWith(i+q))&&g(a).isFile()?a:null}catch{return null}}u(v,"safePath");function X(e,t){const i=e.manifest.transport;if(i===void 0){t.push({code:"command_manifest_invalid",plugin:e.name,message:"commands requires transport in plugin.json",received:"(missing)",expected:"a transport object",next:"Add plugin.json.transport.",path:"transport"});return}if(typeof i!="object"||i===null||Array.isArray(i)){t.push({code:"command_manifest_invalid",plugin:e.name,message:"transport must be an object",received:Array.isArray(i)?"array":typeof i,expected:"an exec or http transport object",next:"Fix plugin.json.transport.",path:"transport"});return}const a=i;if(a.kind==="http"){const n=H(a);if(n.transport!==void 0)return n.transport;t.push({code:"command_manifest_invalid",plugin:e.name,message:n.errors.join("; "),received:JSON.stringify(i),expected:"a valid HTTP transport",next:"Fix plugin.json.transport.",path:"transport"});return}if(a.kind==="machine"){if(e.manifest.provider===void 0||Object.keys(a).length!==1){t.push({code:"command_manifest_invalid",plugin:e.name,message:e.manifest.provider===void 0?"a machine transport belongs to a capability provider bundle":"transport has unknown keys",received:JSON.stringify(i),expected:'{ kind: "machine" } on a provider bundle',next:"Fix plugin.json.transport.",path:"transport"});return}return{kind:"machine"}}const s=Object.keys(a).filter(n=>!["kind","executable"].includes(n));if(a.kind!=="exec"||s.length||a.executable!==void 0&&(typeof a.executable!="string"||a.executable.length===0)){t.push({code:"command_manifest_invalid",plugin:e.name,message:s.length?`transport has unknown keys: ${s.join(", ")}`:"exec transport requires a non-empty executable when declared",received:JSON.stringify(i),expected:'{ kind: "exec", executable?: "plugin-relative path" }',next:"Fix plugin.json.transport.",path:"transport"});return}return{kind:"exec",...a.executable!==void 0?{executable:a.executable}:{}}}u(X,"transportFor");function w(e){return e.kind==="leaf"||e.children.some(w)}u(w,"hasExecutableLeaf");function M(e){return[...e.roots,...e.coreMounts.map(t=>t.node)]}u(M,"manifestNodes");function z(e,t,i,a){if(t.executable===void 0){if(!M(i).some(w))return t;a.push({code:"command_manifest_invalid",plugin:e.name,message:"exec transport requires executable when commands.json declares executable leaves",received:"(missing)",expected:'{ kind: "exec", executable: "plugin-relative path" }',next:"Add plugin.json.transport.executable.",path:"transport.executable"});return}const s=v(e.root,t.executable);if(s===null){a.push({code:"command_path_unsafe",plugin:e.name,message:"executable escapes the plugin root or is not a regular file",received:t.executable,expected:"a relative path to a regular file inside the plugin root",next:"Fix plugin.json.transport.executable.",path:"transport.executable"});return}if((g(s).mode&73)===0){a.push({code:"command_not_executable",plugin:e.name,message:"executable lacks the POSIX exec bit",received:t.executable,expected:"a file with an executable permission bit",next:"chmod +x the plugin executable, then update the plugin.",path:"transport.executable"});return}return{kind:"exec",executable:s}}u(z,"validateExecExecutable");function G(e){return O(e)||e.startsWith(".")||e.includes("/")||e.includes("\\")}u(G,"namesPath");function K(e,t,i){let a=!0;const s=u(n=>{if(n.kind!=="branch")return;const r=n.passthrough;if(r!==void 0&&G(r.bin)){const l=v(e.root,r.bin);l===null?(i.push({code:"command_path_unsafe",plugin:e.name,message:"passthrough binary escapes the plugin root or is not a regular file",received:r.bin,expected:"a relative path to a regular file inside the plugin root",next:"Fix passthrough.bin in commands.json.",path:"passthrough.bin"}),a=!1):(g(l).mode&73)===0?(i.push({code:"command_not_executable",plugin:e.name,message:"passthrough binary lacks the POSIX exec bit",received:r.bin,expected:"a file with an executable permission bit",next:"chmod +x the plugin executable, then update commands.json.",path:"passthrough.bin"}),a=!1):n.passthrough={...r,bin:l}}for(const l of n.children)s(l)},"visit");for(const n of M(t))s(n);return a}u(K,"resolvePassthroughBinaries");function S(e,t=new Set,i,a=new Set){const s=[],n=e.manifest.commands,r=e.manifest.transport;let l=k(e.root,typeof n=="string"?n:"commands.json");const c=u(x=>({plugin:e,manifestPath:l,...x===void 0?{}:{transport:x},contributions:[],coreMounts:[],issues:s}),"empty");if(n===void 0&&r===void 0)return c();if(typeof n!="string"||n.length===0)return s.push({code:"command_manifest_invalid",plugin:e.name,message:"transport requires commands in plugin.json",received:"(missing)",expected:"a plugin-root-relative commands path",next:"Add plugin.json.commands or remove transport.",path:"commands"}),c();const o=X(e,s);if(o===void 0)return c();const d=v(e.root,n);if(d===null)return s.push({code:"command_path_unsafe",plugin:e.name,message:"commands pointer escapes the plugin root or is not a regular file",received:n,expected:"a relative path to a regular file inside the plugin root",next:"Fix plugin.json.commands.",path:"commands"}),c(o);l=d;let p;try{p=N(d)}catch{p=null}if(p===null)return s.push({code:"command_manifest_unreadable",plugin:e.name,message:"commands.json is missing or unreadable",received:d,expected:"a readable JSON file",next:"Reinstall or update the plugin."}),c(o);let h;try{h=JSON.parse(Buffer.from(p).toString("utf8"))}catch{return s.push({code:"command_manifest_invalid",plugin:e.name,message:"commands.json is not valid JSON",received:d,expected:"a JSON object",next:"Regenerate commands.json."}),c(o)}const f=I(h,{transport:o.kind==="machine"?"http":o.kind,reservedCoreNames:t,extensibleCoreBranches:a,...i!==void 0?{coreCommandPaths:i}:{}});if(f.manifest===void 0)return f.issues.forEach(W(e,s)),c(o);const m=o.kind==="exec"?z(e,o,f.manifest,s):o;if(m===void 0)return c(o);if(m.kind==="exec"&&!K(e,f.manifest,s))return c(m);const b=e.manifest.provider,y=f.manifest.roots;if(!y.every(x=>x.kind==="branch"))throw new Error("plugin manifest validation returned a top-level leaf");return{plugin:e,manifestPath:l,transport:m,manifest:f.manifest,contributions:y.map(x=>({plugin:e,transport:m,node:x,manifest:f.manifest,manifestName:x.name,...b!==void 0?{provider:b}:{}})),coreMounts:f.manifest.coreMounts.map(x=>({plugin:e,transport:m,parent:x.parent,node:x.node,manifest:f.manifest,...b!==void 0?{provider:b}:{}})),issues:s}}u(S,"validatePluginCommands");function se(e=process.cwd(),t){return _(e,t).map(i=>S(i))}u(se,"discoverPluginCommandCandidates");function P(e,t=process.cwd(),i,a,s=new Set){const n=_(t,i).map(c=>S(c,e,a,s)),r=new Map;for(const c of n)for(const o of c.contributions){const d=r.get(o.node.name);d===void 0?r.set(o.node.name,[o]):d.push(o)}for(const[c,o]of r)if(o.length>1)for(const d of o){const{plugin:p}=d,h=J({name:p.name,sourceMarketplace:p.sourceMarketplace},c),f=n.find(m=>m.plugin.root===p.root);f.contributions=f.contributions.map(m=>m===d?{...m,node:{...m.node,name:h}}:m),f.issues.push({code:"command_collision",plugin:p.name,path:c,message:`top-level command "${c}" is claimed by ${o.map(m=>m.plugin.name).join(", ")} \u2014 mounted as "${h}"`,received:o.map(m=>m.plugin.name).join(", "),expected:"an origin-qualified top-level command for each claimant",next:`Invoke "${h}" to use this plugin command.`})}const l=new Map;for(const c of n)for(const o of c.coreMounts){const d=[...o.parent,o.node.name].join("/"),p=l.get(d);p===void 0?l.set(d,[o]):p.push(o)}for(const[c,o]of l)if(o.length>1)for(const d of o){const p=n.find(h=>h.plugin.root===d.plugin.root);p.coreMounts=p.coreMounts.filter(h=>h!==d),p.issues.push({code:"command_collision",plugin:d.plugin.name,path:c.replace("/","."),message:`contributed command "${d.node.name}" conflicts with another plugin child below the extensible core branch`,received:o.map(h=>h.plugin.name).join(", "),expected:`a unique child name below ${d.parent.join(".")}`,next:"Rename the contributed command or remove this mount."})}return n}u(P,"validateEffectiveCommandPlugins");function Q(e,t){return{...t,contributor:{name:e.name,scope:e.scope,sourceMarketplace:e.sourceMarketplace}}}u(Q,"contributorIssue");function re(e,t=process.cwd(),i,a,s=new Set){const n=P(e,t,i,a,s);return{contributions:n.flatMap(r=>r.contributions),coreMounts:n.flatMap(r=>r.coreMounts),issues:n.flatMap(r=>r.issues)}}u(re,"discoverCommandContributions");function ce(e,t=process.cwd(),i,a,s=new Set){const n=P(e,t,i,a,s),r=T(n.flatMap(o=>o.contributions)).contributions,l=$(r,e);return{contributions:L(l.contributions,t).contributions,coreMounts:B(n.flatMap(o=>o.coreMounts)),issues:[...n.flatMap(o=>o.issues.map(d=>Q(o.plugin,d))),...l.issues]}}u(ce,"buildExternalCommandSnapshot");export{ce as buildExternalCommandSnapshot,re as discoverCommandContributions,se as discoverPluginCommandCandidates,_ as effectiveCommandPlugins,P as validateEffectiveCommandPlugins,S as validatePluginCommands};
|
|
1
|
+
var P=Object.defineProperty;var u=(e,n)=>P(e,"name",{value:n,configurable:!0});import{realpathSync as j,readFileSync as N,statSync as g}from"node:fs";import{isSandboxedNode as F}from"../../shared/env.js";import{isAbsolute as O,resolve as k,sep as R}from"node:path";import{listInstalledPlugins as q,listInstalledPluginsInRoot as A}from"../resolver.js";import{projectScopeRoots as E}from"../scope.js";import{validateCommandManifest as I}from"../../api/command-manifest/manifest.js";import{qualifiedTopLevelCommandName as J,resolveCommandRegistry as $}from"../command-manifests/registry.js";import{adaptPluginContributions as T,adaptPluginCoreMounts as B}from"./compose.js";import{validateHttpPluginTransport as H}from"./endpoint.js";import{providerRootClaims as L}from"../providers/roots.js";import{applyCommandExtensions as W}from"./extensions.js";function _(e=process.cwd(),n){const i=new Set,o=[],r=u(s=>{i.has(s.name)||(i.add(s.name),o.push(s))},"consider");for(const s of E(e,n))for(const m of A("project",s))r(m);if(!F())for(const s of q("user"))r(s);const{skipped:a}=L(o);return o.filter(s=>!a.has(s.name)).filter(s=>s.enabled&&(s.manifest.commands!==void 0||s.manifest.transport!==void 0))}u(_,"effectiveCommandPlugins");function X(e,n){return i=>n.push({...i,plugin:e.name})}u(X,"issueFor");function v(e,n){if(typeof n!="string"||n.length===0)return null;try{const i=j(e),o=j(k(e,n));return(o===i||o.startsWith(i+R))&&g(o).isFile()?o:null}catch{return null}}u(v,"safePath");function z(e,n){const i=e.manifest.transport;if(i===void 0){n.push({code:"command_manifest_invalid",plugin:e.name,message:"commands requires transport in plugin.json",received:"(missing)",expected:"a transport object",next:"Add plugin.json.transport.",path:"transport"});return}if(typeof i!="object"||i===null||Array.isArray(i)){n.push({code:"command_manifest_invalid",plugin:e.name,message:"transport must be an object",received:Array.isArray(i)?"array":typeof i,expected:"an exec or http transport object",next:"Fix plugin.json.transport.",path:"transport"});return}const o=i;if(o.kind==="http"){const a=H(o);if(a.transport!==void 0)return a.transport;n.push({code:"command_manifest_invalid",plugin:e.name,message:a.errors.join("; "),received:JSON.stringify(i),expected:"a valid HTTP transport",next:"Fix plugin.json.transport.",path:"transport"});return}if(o.kind==="machine"){if(e.manifest.provider===void 0||Object.keys(o).length!==1){n.push({code:"command_manifest_invalid",plugin:e.name,message:e.manifest.provider===void 0?"a machine transport belongs to a capability provider bundle":"transport has unknown keys",received:JSON.stringify(i),expected:'{ kind: "machine" } on a provider bundle',next:"Fix plugin.json.transport.",path:"transport"});return}return{kind:"machine"}}const r=Object.keys(o).filter(a=>!["kind","executable"].includes(a));if(o.kind!=="exec"||r.length||o.executable!==void 0&&(typeof o.executable!="string"||o.executable.length===0)){n.push({code:"command_manifest_invalid",plugin:e.name,message:r.length?`transport has unknown keys: ${r.join(", ")}`:"exec transport requires a non-empty executable when declared",received:JSON.stringify(i),expected:'{ kind: "exec", executable?: "plugin-relative path" }',next:"Fix plugin.json.transport.",path:"transport"});return}return{kind:"exec",...o.executable!==void 0?{executable:o.executable}:{}}}u(z,"transportFor");function w(e){return e.kind==="leaf"||e.children.some(w)}u(w,"hasExecutableLeaf");function M(e){return[...e.roots,...e.coreMounts.map(n=>n.node)]}u(M,"manifestNodes");function G(e,n,i,o){if(n.executable===void 0){if(!M(i).some(w))return n;o.push({code:"command_manifest_invalid",plugin:e.name,message:"exec transport requires executable when commands.json declares executable leaves",received:"(missing)",expected:'{ kind: "exec", executable: "plugin-relative path" }',next:"Add plugin.json.transport.executable.",path:"transport.executable"});return}const r=v(e.root,n.executable);if(r===null){o.push({code:"command_path_unsafe",plugin:e.name,message:"executable escapes the plugin root or is not a regular file",received:n.executable,expected:"a relative path to a regular file inside the plugin root",next:"Fix plugin.json.transport.executable.",path:"transport.executable"});return}if((g(r).mode&73)===0){o.push({code:"command_not_executable",plugin:e.name,message:"executable lacks the POSIX exec bit",received:n.executable,expected:"a file with an executable permission bit",next:"chmod +x the plugin executable, then update the plugin.",path:"transport.executable"});return}return{kind:"exec",executable:r}}u(G,"validateExecExecutable");function K(e){return O(e)||e.startsWith(".")||e.includes("/")||e.includes("\\")}u(K,"namesPath");function Q(e,n,i){let o=!0;const r=u(a=>{if(a.kind!=="branch")return;const s=a.passthrough;if(s!==void 0&&K(s.bin)){const m=v(e.root,s.bin);m===null?(i.push({code:"command_path_unsafe",plugin:e.name,message:"passthrough binary escapes the plugin root or is not a regular file",received:s.bin,expected:"a relative path to a regular file inside the plugin root",next:"Fix passthrough.bin in commands.json.",path:"passthrough.bin"}),o=!1):(g(m).mode&73)===0?(i.push({code:"command_not_executable",plugin:e.name,message:"passthrough binary lacks the POSIX exec bit",received:s.bin,expected:"a file with an executable permission bit",next:"chmod +x the plugin executable, then update commands.json.",path:"passthrough.bin"}),o=!1):a.passthrough={...s,bin:m}}for(const m of a.children)r(m)},"visit");for(const a of M(n))r(a);return o}u(Q,"resolvePassthroughBinaries");function S(e,n=new Set,i,o=new Set){const r=[],a=e.manifest.commands,s=e.manifest.transport;let m=k(e.root,typeof a=="string"?a:"commands.json");const c=u(x=>({plugin:e,manifestPath:m,...x===void 0?{}:{transport:x},contributions:[],coreMounts:[],issues:r}),"empty");if(a===void 0&&s===void 0)return c();if(typeof a!="string"||a.length===0)return r.push({code:"command_manifest_invalid",plugin:e.name,message:"transport requires commands in plugin.json",received:"(missing)",expected:"a plugin-root-relative commands path",next:"Add plugin.json.commands or remove transport.",path:"commands"}),c();const t=z(e,r);if(t===void 0)return c();const d=v(e.root,a);if(d===null)return r.push({code:"command_path_unsafe",plugin:e.name,message:"commands pointer escapes the plugin root or is not a regular file",received:a,expected:"a relative path to a regular file inside the plugin root",next:"Fix plugin.json.commands.",path:"commands"}),c(t);m=d;let p;try{p=N(d)}catch{p=null}if(p===null)return r.push({code:"command_manifest_unreadable",plugin:e.name,message:"commands.json is missing or unreadable",received:d,expected:"a readable JSON file",next:"Reinstall or update the plugin."}),c(t);let h;try{h=JSON.parse(Buffer.from(p).toString("utf8"))}catch{return r.push({code:"command_manifest_invalid",plugin:e.name,message:"commands.json is not valid JSON",received:d,expected:"a JSON object",next:"Regenerate commands.json."}),c(t)}const f=I(h,{transport:t.kind==="machine"?"http":t.kind,reservedCoreNames:n,extensibleCoreBranches:o,...i!==void 0?{coreCommandPaths:i}:{}});if(f.manifest===void 0)return f.issues.forEach(X(e,r)),c(t);const l=t.kind==="exec"?G(e,t,f.manifest,r):t;if(l===void 0)return c(t);if(l.kind==="exec"&&!Q(e,f.manifest,r))return c(l);const b=e.manifest.provider,y=f.manifest.roots;if(!y.every(x=>x.kind==="branch"))throw new Error("plugin manifest validation returned a top-level leaf");return{plugin:e,manifestPath:m,transport:l,manifest:f.manifest,contributions:y.map(x=>({plugin:e,transport:l,node:x,manifest:f.manifest,manifestName:x.name,...b!==void 0?{provider:b}:{}})),coreMounts:f.manifest.coreMounts.map(x=>({plugin:e,transport:l,parent:x.parent,node:x.node,manifest:f.manifest,...b!==void 0?{provider:b}:{}})),issues:r}}u(S,"validatePluginCommands");function ce(e=process.cwd(),n){return _(e,n).map(i=>S(i))}u(ce,"discoverPluginCommandCandidates");function C(e,n=process.cwd(),i,o,r=new Set){const a=_(n,i).map(c=>S(c,e,o,r)),s=new Map;for(const c of a)for(const t of c.contributions){const d=s.get(t.node.name);d===void 0?s.set(t.node.name,[t]):d.push(t)}for(const[c,t]of s)if(t.length>1)for(const d of t){const{plugin:p}=d,h=J({name:p.name,sourceMarketplace:p.sourceMarketplace},c),f=a.find(l=>l.plugin.root===p.root);f.contributions=f.contributions.map(l=>l===d?{...l,node:{...l.node,name:h}}:l),f.issues.push({code:"command_collision",plugin:p.name,path:c,message:`top-level command "${c}" is claimed by ${t.map(l=>l.plugin.name).join(", ")} \u2014 mounted as "${h}"`,received:t.map(l=>l.plugin.name).join(", "),expected:"an origin-qualified top-level command for each claimant",next:`Invoke "${h}" to use this plugin command.`})}const m=new Map;for(const c of a)for(const t of c.coreMounts){const d=[...t.parent,t.node.name].join("/"),p=m.get(d);p===void 0?m.set(d,[t]):p.push(t)}for(const[c,t]of m)if(t.length>1)for(const d of t){const p=a.find(h=>h.plugin.root===d.plugin.root);p.coreMounts=p.coreMounts.filter(h=>h!==d),p.issues.push({code:"command_collision",plugin:d.plugin.name,path:c.replace("/","."),message:`contributed command "${d.node.name}" conflicts with another plugin child below the extensible core branch`,received:t.map(h=>h.plugin.name).join(", "),expected:`a unique child name below ${d.parent.join(".")}`,next:"Rename the contributed command or remove this mount."})}return a}u(C,"validateEffectiveCommandPlugins");function U(e,n){return{...n,contributor:{name:e.name,scope:e.scope,sourceMarketplace:e.sourceMarketplace}}}u(U,"contributorIssue");function de(e,n=process.cwd(),i,o,r=new Set){const a=C(e,n,i,o,r);return{contributions:a.flatMap(s=>s.contributions),coreMounts:a.flatMap(s=>s.coreMounts),issues:a.flatMap(s=>s.issues)}}u(de,"discoverCommandContributions");function ue(e,n=process.cwd(),i,o,r=new Set){const a=C(e,n,i,o,r),s=T(a.flatMap(t=>t.contributions)).contributions,m=$(s,e);return{contributions:W(m.contributions,n).contributions,coreMounts:B(a.flatMap(t=>t.coreMounts)),issues:[...a.flatMap(t=>t.issues.map(d=>U(t.plugin,d))),...m.issues]}}u(ue,"buildExternalCommandSnapshot");export{ue as buildExternalCommandSnapshot,de as discoverCommandContributions,ce as discoverPluginCommandCandidates,_ as effectiveCommandPlugins,C as validateEffectiveCommandPlugins,S as validatePluginCommands};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var w=Object.defineProperty;var c=(n,r)=>w(n,"name",{value:r,configurable:!0});import{prepared as v}from"../sqlite-statements.js";import{relative as b,sep as g}from"node:path";import{activeBackgroundBashJobs as x,readJobPgid as y}from"../bash-jobs.js";import{appSpace as S}from"../layout.js";import{getNode as l,recordSettledTurn as k}from"../canvas/index.js";import{transition as E}from"../runtime/lifecycle.js";import{openDb as _}from"../canvas/db.js";import{contextDir as R}from"../canvas/node-agent-paths.js";import{emitEvent as p}from"../events/emit.js";import{closeGrantStreams as C}from"../runs/events.js";import{grantedMounts as G,isGrantedSource as I,mountAllowed as P}from"../spaces/mounts.js";import{permissionsForRun as m}from"../spaces/permissions.js";import{stopNodeSandboxes as O}from"../spaces/stop.js";import{discardChangedGrantTokens as T}from"../providers/token-cache.js";import{activeGrantForApp as F,allGrants as M,mirrorOnly as A}from"./mirror.js";import{runRemovalCascades as D}from"./remove.js";import{notifyGrantChanges as N}from"./watch.js";import{launcherPresent as W,listSandboxes as j,stopSandboxPid as B}from"./sandboxes.js";import{checkCronFire as J}from"../../daemon/cron/authority.js";import{cronExecutionFor as L}from"../../daemon/cron/live-runs.js";import{cronExecutionsReady as U}from"../../daemon/cron/run-socket.js";function h(){return new Map(v(_(),"SELECT uid, app, status FROM app_users").all().map(n=>[n.uid,{app:n.app,status:n.status}]))}c(h,"appUserOwners");function $(n,r=h()){const o=r.get(n.uid),i=n.nodeId.startsWith("cron-"),e=i?L(n.nodeId.slice(5)):void 0;if(i&&(e===void 0||!o))return"permission_revoked";if(e!==void 0&&o.status==="active"){const s=J({grantee:e.grantee,sub:e.sub,grant_id:e.grantId,scopes:e.scopes});if(!s.allowed)return e.refusal??={reason:s.reason,message:s.message},"permission_revoked"}if(!o||o.app==="app:terminal")return null;if(o.status!=="active")return"app_removed";const t=l(n.nodeId);try{let s;if(e!==void 0)s=m(e.grantee,e.sub,e.grantId,e.scopes);else if(t&&t.grantee===o.app)s=m(o.app,t.sub??"user:local",t.grant_id??null,t.scopes??null);else{const d=F(o.app);if(!d)return"permission_revoked";s=m(o.app,d.sub,d.id,null)}const u=G(s),f=S(o.app).root;return n.mounts.every(d=>{const a=b(f,d.source);return a===""||a!==".."&&!a.startsWith(`..${g}`)&&!a.startsWith(g)||!I(o.app,d.source)||P(d,u)})?null:"permission_revoked"}catch{return"permission_revoked"}}c($,"exceeds");async function H(n){const r=l(n);if(!r)return;(r.status==="active"||r.status==="idle")&&E(n,"release");const o=new Date().toISOString();k(n,o),r.run_id&&r.run_id!==n&&v(_(),"UPDATE nodes SET last_settled_at = COALESCE(last_settled_at, ?) WHERE node_id = ?").run(o,r.run_id)}c(H,"afterRevocationStop");function q(n){const r=new Set,o=l(n);o?.pi_pid!=null&&r.add(o.pi_pid);try{const i=R(n,o?.grantee);for(const e of x(i,!0)){const t=y(i,e.jobId);t!==void 0&&r.add(t)}}catch{}return r}c(q,"nodePids");async function z(){if(!W())return{completion:Promise.resolve()};await U();const n=await j(),r=h(),o=n.flatMap(t=>{const s=$(t,r);return s?[{sandbox:t,reason:s}]:[]}),i=[],e=new Map;for(const t of o){p({level:"info",event:"daemon.grants.sandbox_stop",fields:{sandbox_node:t.sandbox.nodeId,pid:t.sandbox.pid,grantee:r.get(t.sandbox.uid).app,reason:t.reason}});const s=e.get(t.sandbox.nodeId)??[];s.push(t),e.set(t.sandbox.nodeId,s)}for(const[t,s]of e){const u=l(t),f=u?q(t):new Set,d=u!==null&&s.some(({sandbox:a})=>f.has(a.pid));i.push((async()=>{if(d){const a=await O(t,"permission_revoked",{holdRecovery:!0,after:c(()=>H(t),"after")});if(!a.confirmed)throw a.error??new Error(`stop of ${t} was not confirmed`)}for(const{sandbox:a}of s)(!d||!f.has(a.pid))&&await B(a.pid)})().catch(a=>{p({level:"error",event:"daemon.grants.sandbox_stop_failed",...u?{node_id:t}:{fields:{sandbox_node:t}},error:a})}))}return{completion:Promise.all(i).then(()=>{})}}c(z,"sweepSandboxes");function K(n,r){try{const{pairs:o,tokens:
|
|
1
|
+
var w=Object.defineProperty;var c=(n,r)=>w(n,"name",{value:r,configurable:!0});import{prepared as v}from"../sqlite-statements.js";import{relative as b,sep as g}from"node:path";import{activeBackgroundBashJobs as x,readJobPgid as y}from"../bash-jobs.js";import{appSpace as S}from"../layout.js";import{getNode as l,recordSettledTurn as k}from"../canvas/index.js";import{transition as E}from"../runtime/lifecycle.js";import{openDb as _}from"../canvas/db.js";import{contextDir as R}from"../canvas/node-agent-paths.js";import{emitEvent as p}from"../events/emit.js";import{closeGrantStreams as C}from"../runs/events.js";import{grantedMounts as G,isGrantedSource as I,mountAllowed as P}from"../spaces/mounts.js";import{permissionsForRun as m}from"../spaces/permissions.js";import{stopNodeSandboxes as O}from"../spaces/stop.js";import{discardChangedGrantTokens as T}from"../providers/token-cache.js";import{activeGrantForApp as F,allGrants as M,mirrorOnly as A}from"./mirror.js";import{runRemovalCascades as D}from"./remove.js";import{notifyGrantChanges as N}from"./watch.js";import{launcherPresent as W,listSandboxes as j,stopSandboxPid as B}from"./sandboxes.js";import{checkCronFire as J}from"../../daemon/cron/authority.js";import{cronExecutionFor as L}from"../../daemon/cron/live-runs.js";import{cronExecutionsReady as U}from"../../daemon/cron/run-socket.js";function h(){return new Map(v(_(),"SELECT uid, app, status FROM app_users").all().map(n=>[n.uid,{app:n.app,status:n.status}]))}c(h,"appUserOwners");function $(n,r=h()){const o=r.get(n.uid),i=n.nodeId.startsWith("cron-"),e=i?L(n.nodeId.slice(5)):void 0;if(i&&(e===void 0||!o))return"permission_revoked";if(e!==void 0&&o.status==="active"){const s=J({grantee:e.grantee,sub:e.sub,grant_id:e.grantId,scopes:e.scopes});if(!s.allowed)return e.refusal??={reason:s.reason,message:s.message},"permission_revoked"}if(!o||o.app==="app:terminal")return null;if(o.status!=="active")return"app_removed";const t=l(n.nodeId);try{let s;if(e!==void 0)s=m(e.grantee,e.sub,e.grantId,e.scopes);else if(t&&t.grantee===o.app)s=m(o.app,t.sub??"user:local",t.grant_id??null,t.scopes??null);else{const d=F(o.app);if(!d)return"permission_revoked";s=m(o.app,d.sub,d.id,null)}const u=G(s),f=S(o.app).root;return n.mounts.every(d=>{const a=b(f,d.source);return a===""||a!==".."&&!a.startsWith(`..${g}`)&&!a.startsWith(g)||!I(o.app,d.source)||P(d,u)})?null:"permission_revoked"}catch{return"permission_revoked"}}c($,"exceeds");async function H(n){const r=l(n);if(!r)return;(r.status==="active"||r.status==="idle")&&E(n,"release");const o=new Date().toISOString();k(n,o),r.run_id&&r.run_id!==n&&v(_(),"UPDATE nodes SET last_settled_at = COALESCE(last_settled_at, ?) WHERE node_id = ?").run(o,r.run_id)}c(H,"afterRevocationStop");function q(n){const r=new Set,o=l(n);o?.pi_pid!=null&&r.add(o.pi_pid);try{const i=R(n,o?.grantee);for(const e of x(i,!0)){const t=y(i,e.jobId);t!==void 0&&r.add(t)}}catch{}return r}c(q,"nodePids");async function z(){if(!W())return{completion:Promise.resolve()};await U();const n=await j(),r=h(),o=n.flatMap(t=>{const s=$(t,r);return s?[{sandbox:t,reason:s}]:[]}),i=[],e=new Map;for(const t of o){p({level:"info",event:"daemon.grants.sandbox_stop",fields:{sandbox_node:t.sandbox.nodeId,pid:t.sandbox.pid,grantee:r.get(t.sandbox.uid).app,reason:t.reason}});const s=e.get(t.sandbox.nodeId)??[];s.push(t),e.set(t.sandbox.nodeId,s)}for(const[t,s]of e){const u=l(t),f=u?q(t):new Set,d=u!==null&&s.some(({sandbox:a})=>f.has(a.pid));i.push((async()=>{if(d){const a=await O(t,"permission_revoked",{holdRecovery:!0,after:c(()=>H(t),"after")});if(!a.confirmed)throw a.error??new Error(`stop of ${t} was not confirmed`)}for(const{sandbox:a}of s)(!d||!f.has(a.pid))&&await B(a.pid)})().catch(a=>{p({level:"error",event:"daemon.grants.sandbox_stop_failed",...u?{node_id:t}:{fields:{sandbox_node:t}},error:a})}))}return{completion:Promise.all(i).then(()=>{})}}c(z,"sweepSandboxes");function K(n,r){try{const{pairs:o,providers:i,tokens:e}=T(n,r);(o||i)&&p({level:"info",event:"daemon.grants.outbound_tokens_discarded",fields:{grants:o,providers:i,tokens:e}})}catch(o){p({level:"error",event:"daemon.grants.outbound_tokens_discard_failed",error:o})}}c(K,"discardOutboundTokens");async function he(n,r=o=>{}){const o=M();try{N(n,o)}catch(e){p({level:"error",event:"daemon.grants.notify_failed",error:e})}K(n,o);for(const e of n){if(e.status!=="active"||o.find(u=>u.id===e.id)?.status==="active")continue;const s=C(e.id);s&&p({level:"info",event:"daemon.grants.streams_closed",fields:{grant:e.id,grantee:e.grantee,streams:s}})}try{const{completion:e}=await z();r(e)}catch(e){p({level:"error",event:"daemon.grants.sweep_failed",error:e})}const i=new Set(o.filter(e=>!A(e)&&e.status==="removed"&&n.find(t=>t.id===e.id)?.status!=="removed").map(e=>e.grantee));r(D(i).catch(()=>{})),r(import("../providers/bundles.js").then(({syncProviderBundles:e})=>e()).catch(e=>{p({level:"error",event:"daemon.providers.bundle_sync_failed",error:e})}))}c(he,"onGrantSync");export{$ as exceeds,he as onGrantSync,z as sweepSandboxes};
|
|
@@ -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 +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};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.458",
|
|
4
4
|
"description": "crtr — agent runtime with memory, plugins, and marketplaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
},
|
|
138
138
|
"license": "UNLICENSED",
|
|
139
139
|
"dependencies": {
|
|
140
|
-
"@north-light/identity": "0.3.
|
|
140
|
+
"@north-light/identity": "0.3.458",
|
|
141
141
|
"@earendil-works/pi-agent-core": "1.1.0",
|
|
142
142
|
"@earendil-works/pi-ai": "1.1.0",
|
|
143
143
|
"@earendil-works/pi-coding-agent": "1.1.0",
|
package/runtime.lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.458",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@north-light/cli",
|
|
9
|
-
"version": "0.3.
|
|
9
|
+
"version": "0.3.458",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "UNLICENSED",
|
|
12
12
|
"workspaces": [
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"@earendil-works/pi-ai": "1.1.0",
|
|
19
19
|
"@earendil-works/pi-coding-agent": "1.1.0",
|
|
20
20
|
"@earendil-works/pi-tui": "1.1.0",
|
|
21
|
-
"@north-light/identity": "0.3.
|
|
21
|
+
"@north-light/identity": "0.3.458",
|
|
22
22
|
"cron-parser": "^5.6.0",
|
|
23
23
|
"esbuild": "^0.27.7",
|
|
24
24
|
"jose": "^6.2.12",
|
|
@@ -12095,17 +12095,17 @@
|
|
|
12095
12095
|
},
|
|
12096
12096
|
"packages/crouter-api": {
|
|
12097
12097
|
"name": "@north-light/api",
|
|
12098
|
-
"version": "0.3.
|
|
12098
|
+
"version": "0.3.458",
|
|
12099
12099
|
"license": "UNLICENSED"
|
|
12100
12100
|
},
|
|
12101
12101
|
"packages/crouter-env-docker": {
|
|
12102
12102
|
"name": "@north-light/env-docker",
|
|
12103
|
-
"version": "0.3.
|
|
12103
|
+
"version": "0.3.458",
|
|
12104
12104
|
"license": "UNLICENSED"
|
|
12105
12105
|
},
|
|
12106
12106
|
"packages/crouter-identity": {
|
|
12107
12107
|
"name": "@north-light/identity",
|
|
12108
|
-
"version": "0.3.
|
|
12108
|
+
"version": "0.3.458",
|
|
12109
12109
|
"license": "UNLICENSED",
|
|
12110
12110
|
"dependencies": {
|
|
12111
12111
|
"jose": "^6.2.1"
|
|
@@ -12113,20 +12113,20 @@
|
|
|
12113
12113
|
},
|
|
12114
12114
|
"packages/crouter-plugin": {
|
|
12115
12115
|
"name": "@north-light/plugin",
|
|
12116
|
-
"version": "0.3.
|
|
12116
|
+
"version": "0.3.458",
|
|
12117
12117
|
"license": "UNLICENSED",
|
|
12118
12118
|
"dependencies": {
|
|
12119
|
-
"@north-light/api": "0.3.
|
|
12120
|
-
"@north-light/identity": "0.3.
|
|
12119
|
+
"@north-light/api": "0.3.458",
|
|
12120
|
+
"@north-light/identity": "0.3.458"
|
|
12121
12121
|
}
|
|
12122
12122
|
},
|
|
12123
12123
|
"packages/crouter-sdk": {
|
|
12124
12124
|
"name": "@north-light/sdk",
|
|
12125
|
-
"version": "0.3.
|
|
12125
|
+
"version": "0.3.458",
|
|
12126
12126
|
"license": "UNLICENSED",
|
|
12127
12127
|
"dependencies": {
|
|
12128
|
-
"@north-light/api": "0.3.
|
|
12129
|
-
"@north-light/identity": "0.3.
|
|
12128
|
+
"@north-light/api": "0.3.458",
|
|
12129
|
+
"@north-light/identity": "0.3.458",
|
|
12130
12130
|
"jose": "^6.2.1"
|
|
12131
12131
|
},
|
|
12132
12132
|
"bin": {
|