@north-light/cli 0.3.510 → 0.3.511

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.
@@ -1 +1 @@
1
- var x=Object.defineProperty;var o=(n,e)=>x(n,"name",{value:e,configurable:!0});import{resolve as T}from"node:path";import{envNodeId as j}from"../../shared/env.js";import{cliClient as m}from"../../commands/api-client.js";import{apiErrorToCliError as l}from"../io.js";import{ApiError as f}from"../../api/index.js";import{defineBranch as v,defineLeaf as A}from"../command.js";import{resolveCommandRegistry as _}from"../command-manifests/registry.js";import{executeExternalLeaf as L}from"./transport/exec-invoke.js";import{invokeHttpLeaf as M}from"./transport/http-invoke.js";import{readPathParam as U}from"./transport/http-plan.js";import{sha256Hex as N,signedHeaderDigest as P}from"./transport/sign.js";import{needsAppToken as S}from"../plugin-objects-manifest.js";async function R(n){const e=await m().request("GET",`/v1/providers/${encodeURIComponent(n)}/tools`);return new Set(e.groups.flatMap(r=>r.leaves.map(a=>a.tool)))}o(R,"allowedProviderTools");function h(n,e,r){return n.children.some(a=>a.kind==="leaf"?r.has([...e,a.name].join(" ")):h(a,[...e,a.name],r))}o(h,"hasAllowedLeaf");function H(n,e,r){return n.children.filter(a=>a.tier!=="hidden"&&(a.kind==="leaf"?r.has([...e,a.name].join(" ")):h(a,[...e,a.name],r))).length}o(H,"visibleChildCount");function $(n,e,r){return async()=>{if(!j())return n.help;const a=await R(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 u=[...r,i.name];if(!h(i,u,a))return[];const p=H(i,u,a);return[{...t,...t.subCount!==void 0?{subCount:p}:{}}]})}}}o($,"providerHelp");const I="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 y(n,e,r,a=!1){return{outputKind:"object",...a?{machineSelector:!0}:{},run:o(async(t,i)=>{const u={...t};for(const p of r.params){if(!("type"in p))continue;const s=p.name.replace(/-([a-z])/g,(c,C)=>C.toUpperCase()),d=u[s];p.type==="file"&&typeof d=="string"?u[s]=T(d):p.type==="path"&&d!==void 0&&(u[s]=U(`provider command \`${n} ${e}\``,p,d))}try{return await m({timeoutMs:0}).request("POST","/v1/provider/call",{provider:n,tool:e,arguments:u,...i?.machine===void 0?{}:{machine:i.machine}},{timeout:0})}catch(p){throw p instanceof f?l(p,void 0,{providerCall:!0}):p}},"run")}}o(y,"providerLeafAdapter");function E(n,e){return/^[A-Za-z0-9_-]{1,64}$/.test(n)&&n!=="crtr"&&!e.has(n)}o(E,"validRootName");function Y(n,e){const r=[],a=[];for(const t of n){const{provider:i,commands:u}=t;if(!Array.isArray(u)||t.status!=="available"&&!E(i,e))continue;if(t.status!=="available"){const s=t.error??"bundle installation pending",d=`Unavailable (${t.status}): ${s}`,c=v({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=o(s=>s.kind==="branch"?{...s,children:s.children.map(p)}:{...s,rest:{method:"GET",path:"/",params:{}}},"helpNode");for(const s of u)s.kind!=="branch"||!Array.isArray(s.children)||!E(s.name,e)||r.push({contributor:{name:i,scope:"user"},manifestName:s.name,node:p(s),adaptLeaf:o((d,c)=>y(i,c.slice(1).join(" "),d,t.transport==="machine"),"adaptLeaf")})}return[...b(_(r,e)),...a]}o(Y,"composeSandboxProviderSubtrees");async function K(n){try{const{token:e,socket:r}=await m().plugins.appToken(n);return{CRTR_APP_TOKEN:e,CRTR_DAEMON_SOCKET:r}}catch(e){if(e instanceof f&&(e.status===404||e.status===403))return;throw e instanceof f?l(e):e}}o(K,"appEnvFor");function w(n,e){return(r,a)=>{const t=n.transport,i=e(a);if(n.provider!==void 0)return y(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:o(async s=>{const d=S(n.plugin.manifest)?await K(n.plugin.name):void 0;return L({plugin:n.plugin,executable:p,commandPath:i,output:r.output,...d!==void 0?{appEnv:d}:{}},s)},"run")}}if(t.kind==="machine")throw new Error(`machine plugin "${n.plugin.name}" is not a capability provider`);const u=r;return{outputKind:u.rest.streaming===!0?"jsonl":"object",run:o((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:u.params,rest:u.rest,output:u.output,commandPath:i},p,s,{callHeaders:o(d=>m().plugins.callHeaders(n.plugin.name,{method:d.method,url:d.url.href,body_sha256:N(d.body),...P(d.headers)}).then(c=>c.headers,c=>{throw c instanceof f?l(c):c}),"callHeaders")}),"run")}}}o(w,"adaptLeaf");function ee(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:w(e,r=>[e.manifestName,...r.slice(1)]),...e.provider!==void 0?{provider:e.provider}:{}})),coreMounts:[],issues:[]}}o(ee,"adaptPluginContributions");function ne(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:w({...e,manifestName:e.node.name},r=>e.provider===void 0?[...r]:[e.provider.id,...r.slice(e.parent.length)])}))}o(ne,"adaptPluginCoreMounts");function b(n){return n.contributions.map(e=>g(e,e.node,[e.node.name]))}o(b,"composeExternalSubtrees");function re(n,e){return n.filter(r=>r.parent.join("\0")===e.join("\0")).map(r=>r.node.kind==="branch"?g(r,r.node,[...e,r.node.name],r.provider===void 0?1:e.length):k(r,r.node,[...e,r.node.name]))}o(re,"composeCoreBranchMounts");function g(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=v({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(u=>u.kind==="branch"?g(n,u,[...r,u.name],a):k(n,u,[...r,u.name]))});return n.provider!==void 0&&(i.effectiveHelp=$(i,n.provider,r.slice(a))),i}o(g,"buildBranch");function k(n,e,r){const a=n.adaptLeaf(e,r);return A({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:I}:{},params:e.params,output:e.output,outputKind:a.outputKind,effects:[...e.effects]},run:a.run})}o(k,"buildLeaf");export{ee as adaptPluginContributions,ne as adaptPluginCoreMounts,re as composeCoreBranchMounts,b as composeExternalSubtrees,Y as composeSandboxProviderSubtrees};
1
+ var j=Object.defineProperty;var o=(n,e)=>j(n,"name",{value:e,configurable:!0});import{resolve as A}from"node:path";import{envNodeId as L}from"../../shared/env.js";import{cliClient as f}from"../../commands/api-client.js";import{apiErrorToCliError as h}from"../io.js";import{ApiError as l}from"../../api/index.js";import{defineBranch as y,defineLeaf as M}from"../command.js";import{qualifiedTopLevelCommandName as _,resolveCommandRegistry as N}from"../command-manifests/registry.js";import{executeExternalLeaf as S}from"./transport/exec-invoke.js";import{invokeHttpLeaf as U}from"./transport/http-invoke.js";import{readPathParam as b}from"./transport/http-plan.js";import{sha256Hex as P,signedHeaderDigest as H}from"./transport/sign.js";import{needsAppToken as R}from"../plugin-objects-manifest.js";async function $(n){const e=await f().request("GET",`/v1/providers/${encodeURIComponent(n)}/tools`);return new Set(e.groups.flatMap(a=>a.leaves.map(t=>t.tool)))}o($,"allowedProviderTools");function g(n,e,a){return n.children.some(t=>t.kind==="leaf"?a.has([...e,t.name].join(" ")):g(t,[...e,t.name],a))}o(g,"hasAllowedLeaf");function I(n,e,a){return n.children.filter(t=>t.tier!=="hidden"&&(t.kind==="leaf"?a.has([...e,t.name].join(" ")):g(t,[...e,t.name],a))).length}o(I,"visibleChildCount");function w(n,e,a){return async()=>{if(!L())return n.help;const t=await a();return{...n.help,listing:n.help.listing?.flatMap(d=>{const i=n.children.find(c=>c.name===d.name);if(i?.kind==="leaf")return t.has([...e,i.name].join(" "))?[d]:[];if(i?.kind!=="branch")return[];const s=[...e,i.name];if(!g(i,s,t))return[];const p=I(i,s,t);return[{...d,...d.subCount!==void 0?{subCount:p}:{}}]})}}}o(w,"providerHelp");const K="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 E(n,e,a,t=!1){return{outputKind:"object",...t?{machineSelector:!0}:{},run:o(async(d,i)=>{const s={...d};for(const p of a.params){if(!("type"in p))continue;const c=p.name.replace(/-([a-z])/g,(u,m)=>m.toUpperCase()),r=s[c];p.type==="file"&&typeof r=="string"?s[c]=A(r):p.type==="path"&&r!==void 0&&(s[c]=b(`provider command \`${n} ${e}\``,p,r))}try{return await f({timeoutMs:0}).request("POST","/v1/provider/call",{provider:n,tool:e,arguments:s,...i?.machine===void 0?{}:{machine:i.machine}},{timeout:0})}catch(p){throw p instanceof l?h(p,void 0,{providerCall:!0}):p}},"run")}}o(E,"providerLeafAdapter");function k(n,e){return/^[A-Za-z0-9_-]{1,64}$/.test(n)&&n!=="crtr"&&!e.has(n)}o(k,"validRootName");function ne(n,e){const a=[],t=[];for(const i of n){const{provider:s,commands:p}=i;if(!Array.isArray(p)||i.status!=="available"&&!k(s,e))continue;if(i.status!=="available"){const r=i.error??"bundle installation pending",u=`Unavailable (${i.status}): ${r}`,m=y({name:s,rootEntry:{concept:i.display_name,desc:u,useWhen:u},help:{name:s,summary:u},children:[]});m.unavailable=u,t.push(m);continue}const c=o(r=>r.kind==="branch"?{...r,children:r.children.map(c)}:{...r,rest:{method:"GET",path:"/",params:{}}},"helpNode");for(const r of p){if(r.kind!=="branch"||!Array.isArray(r.children)||!k(r.name,e))continue;const u=c(r);a.push({contributor:{name:s,scope:"user"},manifestName:r.name,node:i.groups.length===0?{...u,rootEntry:void 0}:u,adaptLeaf:o((m,x)=>E(s,x.slice(1).join(" "),m,i.transport==="machine"),"adaptLeaf")})}}const d=q(N(a,e));for(const i of d){const s=n.find(r=>r.status==="available"&&r.commands.some(u=>u.name===i.name||_({name:r.provider},u.name)===i.name));if(s?.status!=="available")continue;const p=new Set(s.groups.flatMap(r=>r.leaves.map(u=>u.tool))),c=o((r,u)=>{r.effectiveHelp=w(r,u,async()=>p);for(const m of r.children)m.kind==="branch"&&c(m,[...u,m.name])},"attachHelp");c(i,[])}return[...d,...t]}o(ne,"composeSandboxProviderSubtrees");async function O(n){try{const{token:e,socket:a}=await f().plugins.appToken(n);return{CRTR_APP_TOKEN:e,CRTR_DAEMON_SOCKET:a}}catch(e){if(e instanceof l&&(e.status===404||e.status===403))return;throw e instanceof l?h(e):e}}o(O,"appEnvFor");function C(n,e){return(a,t)=>{const d=n.transport,i=e(t);if(n.provider!==void 0)return E(n.provider.id,i.slice(1).join(" "),a,d.kind==="machine");if(d.kind==="exec"){const p=d.executable;if(p===void 0)throw new Error(`exec plugin "${n.plugin.name}" has an executable leaf without transport.executable`);return{outputKind:"object",run:o(async c=>{const r=R(n.plugin.manifest)?await O(n.plugin.name):void 0;return S({plugin:n.plugin,executable:p,commandPath:i,output:a.output,...r!==void 0?{appEnv:r}:{}},c)},"run")}}if(d.kind==="machine")throw new Error(`machine plugin "${n.plugin.name}" is not a capability provider`);const s=a;return{outputKind:s.rest.streaming===!0?"jsonl":"object",run:o((p,c)=>U({registration:{name:n.plugin.name,endpoint:d.endpoint,...d.authEnv!==void 0?{authEnv:d.authEnv}:{}},...n.manifest?.baseUrl!==void 0?{baseUrl:n.manifest.baseUrl}:{},...n.manifest?.timeouts!==void 0?{timeouts:n.manifest.timeouts}:{},params:s.params,rest:s.rest,output:s.output,commandPath:i},p,c,{callHeaders:o(r=>f().plugins.callHeaders(n.plugin.name,{method:r.method,url:r.url.href,body_sha256:P(r.body),...H(r.headers)}).then(u=>u.headers,u=>{throw u instanceof l?h(u):u}),"callHeaders")}),"run")}}}o(C,"adaptLeaf");function ae(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:C(e,a=>[e.manifestName,...a.slice(1)]),...e.provider!==void 0?{provider:e.provider}:{}})),coreMounts:[],issues:[]}}o(ae,"adaptPluginContributions");function re(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:C({...e,manifestName:e.node.name},a=>e.provider===void 0?[...a]:[e.provider.id,...a.slice(e.parent.length)])}))}o(re,"adaptPluginCoreMounts");function q(n){return n.contributions.map(e=>v(e,e.node,[e.node.name]))}o(q,"composeExternalSubtrees");function te(n,e){return n.filter(a=>a.parent.join("\0")===e.join("\0")).map(a=>a.node.kind==="branch"?v(a,a.node,[...e,a.node.name],a.provider===void 0?1:e.length):T(a,a.node,[...e,a.node.name]))}o(te,"composeCoreBranchMounts");function v(n,e,a,t=1){const d=e.rootEntry===void 0?void 0:{concept:e.rootEntry.concept,desc:e.rootEntry.description,useWhen:e.rootEntry.whenToUse},i=y({name:e.name,description:e.description,whenToUse:e.whenToUse,...e.tier!==void 0?{tier:e.tier}:{},...n.plugin!==void 0?{plugin:n.plugin}:{},...d!==void 0?{rootEntry:d}:{},help:{name:a.join(" "),summary:e.summary,...e.model!==void 0?{model:e.model}:{},...a.length===1&&n.extensionIssue!==void 0?{extensionIssue:n.extensionIssue}:{}},...e.passthrough!==void 0?{passthrough:e.passthrough}:{},children:e.children.map(s=>s.kind==="branch"?v(n,s,[...a,s.name],t):T(n,s,[...a,s.name]))});return n.provider!==void 0&&(i.effectiveHelp=w(i,a.slice(t),()=>$(n.provider.id))),i}o(v,"buildBranch");function T(n,e,a){const t=n.adaptLeaf(e,a);return M({name:e.name,description:e.description,whenToUse:e.whenToUse,...e.tier!==void 0?{tier:e.tier}:{},...n.plugin!==void 0?{plugin:n.plugin}:{},...t.machineSelector===!0?{machineSelector:!0}:{},help:{name:a.join(" "),summary:e.summary,...t.machineSelector===!0?{guide:K}:{},params:e.params,output:e.output,outputKind:t.outputKind,effects:[...e.effects]},run:t.run})}o(T,"buildLeaf");export{ae as adaptPluginContributions,re as adaptPluginCoreMounts,te as composeCoreBranchMounts,q as composeExternalSubtrees,ne as composeSandboxProviderSubtrees};
@@ -30,7 +30,7 @@ export interface ProviderToolDescription {
30
30
  output: ManifestHttpLeaf['output'];
31
31
  }>;
32
32
  }>;
33
- /** Help-only, caller-filtered tree. Never contains REST mappings, endpoints, credentials, or executable paths. */
33
+ /** Help-only tree of every grouped leaf, including groups the caller lacks (`groups` is only the caller's). Never contains REST mappings, endpoints, credentials, or executable paths. */
34
34
  commands: ProviderHelpBranch[];
35
35
  }
36
36
  export interface UnavailableProviderToolDescription {
@@ -1 +1 @@
1
- var te=Object.defineProperty;var m=(e,t)=>te(e,"name",{value:t,configurable:!0});import{validateDeclaredResult as U}from"../../api/command-manifest/result.js";import{callerAllows as G,requireGroup as oe}from"../../daemon/api/handlers/providers.js";import{terminalGrant as ne}from"../grants/mirror.js";import{UNCAPPED_GRANTEE as ie}from"../usage/ledger.js";import{listInstalledPlugins as se}from"../resolver.js";import{ProviderError as l}from"./errors.js";import{resolveProviderRef as ae}from"./roots.js";import{ensureProviderBundle as de,refreshProviderBundle as ue,resolveProviderBundle as B,unavailableProviderError as le}from"./bundles.js";import{prepareFileCall as pe,resignForRetry as z,resolveFileResult as Q}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 je,sendDeviceCall as Ae,deviceFailure as ke,deviceUnreachable as V,DEFAULT_DEVICE_REQUEST_MS as qe}from"./device.js";import{isLibsSearch as K,wantsBundling as Pe,callLibsSearch as $e,bundleExec as Ee,mapLibOutcome as Ce,reportLibCalls as Re}from"./device-libs.js";import{directoryIssuer as Se,ownMachineId as Ne}from"../peer/directory.js";import{formatCrtrCaller as W}from"../command-plugins/caller-header.js";import{getNode as De}from"../canvas/canvas.js";import{envelopeForProviderCaller as Fe}from"../peer/envelope.js";function X(e){const t=[],a=e.roots.length===1?e.roots[0].name:void 0,c=m((r,n)=>{const d=[...n,r.name];if(r.kind==="branch"){for(const _ of r.children)c(_,d);return}const p=d[0]===a?d.slice(1).join(" "):d.join(" ");t.push({tool:p,leaf:r})},"walk");for(const r of e.roots)c(r,[]);return t}m(X,"providerLeaves");function Y(e,t,a){if(e.kind!=="branch")return null;const c=[];for(const i of e.children)if(i.kind==="branch"){const h=Y(i,t,a);h&&c.push(h)}else if("group"in i&&i.group&&G(a,`${t}:${i.group}`)){const{kind:h,name:v,description:y,whenToUse:P,tier:o,summary:A,params:s,output:w,effects:k,group:C}=i;c.push({kind:h,name:v,description:y,whenToUse:P,...o===void 0?{}:{tier:o},summary:A,params:s,output:w,effects:k,group:C})}if(c.length===0)return null;const{kind:r,name:n,description:d,whenToUse:p,tier:_,summary:g,model:j,rootEntry:q}=e;return{kind:r,name:n,description:d,whenToUse:p,..._===void 0?{}:{tier:_},summary:g,...j===void 0?{}:{model:j},...q===void 0?{}:{rootEntry:q},children:c}}m(Y,"providerHelpBranch");async function cr(e,t){const a=await B(t),c=X(a.commands),r=se("user").find(n=>n.name===t)?.manifest.description??t;return{provider:t,status:"available",major:a.major,display_name:r,transport:a.transport.kind,groups:a.provider.groups.filter(n=>G(e,`${t}:${n.group}`)).map(n=>({group:n.group,leaves:c.filter(({leaf:d})=>d.group===n.group).map(({tool:d,leaf:p})=>({tool:d,description:p.description,summary:p.summary,params:p.params,output:p.output}))})),commands:a.commands.roots.flatMap(n=>{const d=Y(n,t,e);return d?[d]:[]})}}m(cr,"providerTools");function Me(e){return ye(e.headers["crtr-receipt"],e)}m(Me,"onProviderResponse");function Oe(e){if(e.class==="person"){const t=ne()?.sub;if(!t)throw new l("grant_revoked","This machine has no active Northlight grant for your terminal. Run `crtr sys join` again from /account/machines.");return{sub:t,grantee:t}}if(!e.sub||!e.grantee)throw new l("grant_revoked","the caller has no active grant");return{sub:e.sub,grantee:e.grantee}}m(Oe,"identity");function Te(e,t){return[`${e.id}:${t.group}`]}m(Te,"callScope");function Ie(e,t){const a=X(e.commands).find(c=>c.tool===t);if(!a)throw new l("not_found",`tool "${t}" is not in provider "${e.id}"`,{param:"tool"});if(!a.leaf.group)throw new l("not_found",`tool "${t}" has no group`,{param:"tool"});return a.leaf}m(Ie,"leafFor");function L(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t=e.error;return!t||typeof t!="object"||Array.isArray(t)?{}:t}m(L,"errorBody");function Ue(e){const t=Array.isArray(e)?e[0]:e;if(!t)return;const a=Number(t);if(Number.isFinite(a)&&a>=0)return a;const c=Date.parse(t);return Number.isFinite(c)?Math.max(0,Math.ceil((c-Date.now())/1e3)):void 0}m(Ue,"retryAfter");function M(e,t,a,c=!1){const r=L(t),n=typeof r.code=="string"?r.code:void 0,d=typeof r.message=="string"?r.message:`provider answered HTTP ${e}`,p=typeof r.field=="string"?r.field:void 0,g={origin:"provider",details:{code:n??null,message:d,...p?{field:p}:{},...typeof r.next=="string"?{next:r.next}:{},...r.received!==void 0?{received:r.received}:{},status:e},...p?{param:p}:{}};return e===429?new l("provider_rate_limited",d,{...g,retry_after_s:Ue(a["retry-after"])}):e===401||e>=500?new l("provider_unavailable",d,g):e===403&&n==="provider_refused"?new l("provider_refused",d,g):e===403&&n==="approval_required"?new l("provider_error",c?"provider asked for approval again":"provider approval question is invalid",{...g,status:e}):new l("provider_error",d,{...g,status:e})}m(M,"mapProviderResponse");function Z(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})}m(Z,"approvalShape");function Be(e){const t=e.origin!==void 0?W({node:e.origin.node??void 0,run:e.origin.run??void 0,machine:e.origin.machine}):W({node:e.node_id??void 0,run:e.run_id??void 0,profile:e.node_id?De(e.node_id)?.profile_id??void 0:void 0,machine:Se()!==null?Ne():void 0});return t===""?{}:{"Crtr-Caller":t}}m(Be,"crtrCallerHeader");async function fr(e,t,a){const c=ae(t.provider);if(c===null)throw new l("not_found",`provider "${t.provider}" is not available`,{param:"provider"});const r={...t,provider:c},n=new AbortController,d=m(()=>n.abort(a.signal.reason),"abort");a.signal.aborted?d():a.signal.addEventListener("abort",d,{once:!0});let p=a.requestId;const _=ge({request_id:p,run_id:e.run_id,node_id:e.node_id,controller:n});let g,j,q,i,h,v,y,P="allowed";try{try{await de(r.provider)}catch{throw le(r.provider)}let o=await B(r.provider,r.version),A=!1,s,w,k,C=!1,J=!1,O,H=0;for(;;){const b=Ie(o,r.tool);if(q=`${o.id}:${b.group}`,oe(e,o.id,b.group),o.transport.kind!=="machine"&&r.machine!==void 0)throw new l("invalid_argument",`provider "${o.id}" is not served by a device, so it takes no machine`,{param:"machine"});o.transport.kind==="machine"&&O===void 0&&(O=await je(r.machine,void 0,{newest:K(o.major,r.tool)}),H=Date.now()+(o.commands.timeouts?.requestMs??qe));let T=null;if(r.approval&&!C){const u=ce(e,r,Z(o,b));k=u.option,T=u.prepared,y=r.approval,p=u.requestId,C=!0}const R={params:b.params.filter(u=>"type"in u&&u.type==="file").map(u=>u.name),output:b.output.find(u=>u.type==="file")?.name??null},x=JSON.stringify(R);if(w!==void 0&&w!==x)throw new l("provider_bad_response","the refreshed manifest changed the tool file contract",{origin:"provider",details:{status:409,message:"manifest_stale changed the tool file contract"}});w=x,T&&(s=await z(e,R,r.arguments,T,{signal:n.signal})),s??=await pe(e,R,r.arguments,{signal:n.signal});const{sub:I,grantee:S}=Oe(e);if(o.transport.kind==="machine"){const u=await Le(e,o,r,b,s,{sub:I,requestId:p,deadlineAt:H,signal:n.signal,machine:O});return v=u.responder,g=u.cleanup,{status:200,body:u.result}}const $=await me({audience:o.transport.endpoint,sub:I,grantee:S,initiating_app:S,scope:Te(o,b)},{signal:n.signal});i=$.jti;const ee={registration:{name:o.id,endpoint:o.transport.endpoint},...o.commands.baseUrl?{baseUrl:o.commands.baseUrl}:{},...o.commands.timeouts?{timeouts:o.commands.timeouts}:{},params:b.params,rest:b.rest,output:b.output,commandPath:[o.id,...r.tool.split(" ")]},re=he(ee,s.arguments,void 0,{authorization:`Bearer ${$.access_token}`,requestId:p,fileMode:"pass-through",extraHeaders:{...e.run_id?{"Crtr-Run-Id":e.run_id}:{},...Be(e),...k?{"Crtr-Approval":k}:{},...s.headers}});j??=Date.now()+(o.commands.timeouts?.requestMs??3e4);let f;try{if(f=await we(re,{connectMs:o.commands.timeouts?.connectMs,requestMs:o.commands.timeouts?.requestMs,deadlineAt:j,signal:n.signal}),n.signal.aborted)throw n.signal.reason??new Error("provider call aborted")}catch(u){throw n.signal.aborted?n.signal.reason??u:u instanceof be?new l("provider_unavailable",u.message,{origin:"provider",details:{status:null,code:u.kind}}):u}if(h??=Me({provider:o.id,tool:r.tool,request_id:p,run_id:e.run_id,sub:I,grantee:S,payer:$.payer,ledger_grantee:e.class==="person"?ie:S,jti:$.jti,status:f.status,headers:f.headers}),v=f.headers["crtr-responder"],f.status===401&&ve($.key),f.status>=200&&f.status<300){const u=f.body;if(!u||typeof u!="object"||Array.isArray(u))throw new l("provider_bad_response","provider result is not a JSON object",{origin:"provider",details:{status:f.status,message:"provider result is not a JSON object"}});let D;try{D=await Q(e,s,u,{signal:n.signal})}catch(E){throw E instanceof l&&E.code==="provider_error"?new l("provider_bad_response",E.message,{origin:"provider",details:{status:f.status,message:E.message}}):E}g=D.cleanup;const F=U(b.output,D.result);if(F)throw new l("provider_bad_response",`provider result has an invalid output field "${F.field}"`,{origin:"provider",param:F.field,details:{status:f.status,...F}});if(n.signal.aborted)throw n.signal.reason??new Error("provider call aborted");return{status:200,body:D.result}}const N=L(f.body);if(f.status===403&&N.code==="approval_required"&&!k&&!J){J=!0;const u=await fe(e,r,p,N.question,n.signal,Z(o,b),s);if(y=u.questionId,k=u.option,s=await z(e,R,r.arguments,s,{signal:n.signal}),j=void 0,n.signal.aborted)throw new l("user_denied","the approval wait was canceled");continue}if(f.status===403&&N.code==="approval_required"&&k)throw M(f.status,f.body,f.headers,!0);if(f.status!==401&&N.manifest_stale===!0&&!A){A=!0,await ue(o.id,{conditional:!1,...r.version===void 0?{}:{version:r.version}}),o=await B(o.id,r.version);continue}throw M(f.status,f.body,f.headers,!!k)}}catch(o){throw o instanceof l&&o.question_id&&(y=o.question_id),v===void 0&&o instanceof l&&typeof o.responder=="string"&&(v=o.responder),P=`refused:${o instanceof l?o.code:"server_error"}`,g?.(),o}finally{try{_e({request_id:p,caller:e,provider:r.provider,tool:r.tool,scope_checked:q,outcome:P,jti:i,receipt_id:h,responder:v,question_id:y})}finally{_()}a.signal.removeEventListener("abort",d)}}m(fr,"callProvider");async function Le(e,t,a,c,r,n){const{machine:d}=n,p=Fe(e,n.sub),_={machine:d,envelope:p,signal:n.signal,deadlineAt:n.deadlineAt,mapFailure:M};if(K(t.major,a.tool)){const s=await $e(_,r.arguments,t.major),w=U(c.output,s);if(w)throw new l("provider_bad_response",`the libraries result has an invalid output field "${w.field}"`,{origin:"provider",param:w.field,details:{status:200,...w}});return{result:s,responder:void 0,cleanup:void 0}}let g=r.arguments,j=null,q="";if(Pe(t.major,a.tool,r.arguments)){const s=await Ee(_,r.arguments,t.major);if(n.deadlineAt-Date.now()<=0)throw V(d,"timeout","");s!==null&&(g=s.arguments,j=s.services,q=s.release)}const i=await Ae(d,{tool:a.tool,arguments:g,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}},p,n.signal);if(n.signal.aborted)throw n.signal.reason??new Error("provider call aborted");if("unreachable"in i)throw V(d,i.unreachable.reason,i.unreachable.message);const h=i.headers["crtr-responder"],v=L(i.body);if(i.status<200||i.status>=300){const s=ke(d,i.status,typeof v.code=="string"?v.code:void 0,typeof v.message=="string"?v.message:void 0,i.body),w=s===null&&i.status>=500&&typeof v.code=="string"?new l("provider_error",typeof v.message=="string"?v.message:`the device answered HTTP ${i.status}`,{origin:"runtime",status:i.status,details:{code:v.code,status:i.status}}):null;throw Object.assign(s??w??M(i.status,i.body,i.headers),{responder:h})}const y=i.body&&typeof i.body=="object"&&!Array.isArray(i.body)?i.body.result:void 0;if(!y||typeof y!="object"||Array.isArray(y))throw Object.assign(new l("provider_bad_response","the device result is not a JSON object",{origin:"provider",details:{status:i.status,message:"the device result is not a JSON object"}}),{responder:h});let P=y;if(j!==null){const s=Ce(P,j);if(Re(d,p,q,s.calls),"error"in s)throw Object.assign(s.error,{responder:h});P=s.result}let o;try{o=await Q(e,r,P,{signal:n.signal})}catch(s){throw s instanceof l&&s.code==="provider_error"?Object.assign(new l("provider_bad_response",s.message,{origin:"provider",details:{status:i.status,message:s.message}}),{responder:h}):s}const A=U(c.output,o.result);if(A)throw o.cleanup?.(),Object.assign(new l("provider_bad_response",`the device result has an invalid output field "${A.field}"`,{origin:"provider",param:A.field,details:{status:i.status,...A}}),{responder:h});return{result:o.result,responder:h,cleanup:o.cleanup}}m(Le,"callDevice");export{fr as callProvider,Be as crtrCallerHeader,M as mapProviderResponse,Me as onProviderResponse,X as providerLeaves,cr as providerTools};
1
+ var re=Object.defineProperty;var m=(e,t)=>re(e,"name",{value:t,configurable:!0});import{validateDeclaredResult as I}from"../../api/command-manifest/result.js";import{callerAllows as te,requireGroup as oe}from"../../daemon/api/handlers/providers.js";import{terminalGrant as ne}from"../grants/mirror.js";import{UNCAPPED_GRANTEE as ie}from"../usage/ledger.js";import{listInstalledPlugins as se}from"../resolver.js";import{ProviderError as l}from"./errors.js";import{resolveProviderRef as ae}from"./roots.js";import{ensureProviderBundle as de,refreshProviderBundle as ue,resolveProviderBundle as U,unavailableProviderError as le}from"./bundles.js";import{prepareFileCall as pe,resignForRetry as G,resolveFileResult as z}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 je,sendDeviceCall as Ae,deviceFailure as ke,deviceUnreachable as Q,DEFAULT_DEVICE_REQUEST_MS as qe}from"./device.js";import{isLibsSearch as V,wantsBundling as Pe,callLibsSearch as Ee,bundleExec as $e,mapLibOutcome as Ce,reportLibCalls as Re}from"./device-libs.js";import{directoryIssuer as Se,ownMachineId as Ne}from"../peer/directory.js";import{formatCrtrCaller as K}from"../command-plugins/caller-header.js";import{getNode as De}from"../canvas/canvas.js";import{envelopeForProviderCaller as Fe}from"../peer/envelope.js";function W(e){const t=[],a=e.roots.length===1?e.roots[0].name:void 0,f=m((r,o)=>{const s=[...o,r.name];if(r.kind==="branch"){for(const j of r.children)f(j,s);return}const p=s[0]===a?s.slice(1).join(" "):s.join(" ");t.push({tool:p,leaf:r})},"walk");for(const r of e.roots)f(r,[]);return t}m(W,"providerLeaves");function X(e){if(e.kind!=="branch")return null;const t=[];for(const h of e.children)if(h.kind==="branch"){const A=X(h);A&&t.push(A)}else if("group"in h&&h.group){const{kind:A,name:d,description:_,whenToUse:v,tier:w,summary:q,params:n,output:k,effects:i,group:b}=h;t.push({kind:A,name:d,description:_,whenToUse:v,...w===void 0?{}:{tier:w},summary:q,params:n,output:k,effects:i,group:b})}if(t.length===0)return null;const{kind:a,name:f,description:r,whenToUse:o,tier:s,summary:p,model:j,rootEntry:g}=e;return{kind:a,name:f,description:r,whenToUse:o,...s===void 0?{}:{tier:s},summary:p,...j===void 0?{}:{model:j},...g===void 0?{}:{rootEntry:g},children:t}}m(X,"providerHelpBranch");async function cr(e,t){const a=await U(t),f=W(a.commands),r=se("user").find(o=>o.name===t)?.manifest.description??t;return{provider:t,status:"available",major:a.major,display_name:r,transport:a.transport.kind,groups:a.provider.groups.filter(o=>te(e,`${t}:${o.group}`)).map(o=>({group:o.group,leaves:f.filter(({leaf:s})=>s.group===o.group).map(({tool:s,leaf:p})=>({tool:s,description:p.description,summary:p.summary,params:p.params,output:p.output}))})),commands:a.commands.roots.flatMap(o=>{const s=X(o);return s?[s]:[]})}}m(cr,"providerTools");function Me(e){return ye(e.headers["crtr-receipt"],e)}m(Me,"onProviderResponse");function Oe(e){if(e.class==="person"){const t=ne()?.sub;if(!t)throw new l("grant_revoked","This machine has no active Northlight grant for your terminal. Run `crtr sys join` again from /account/machines.");return{sub:t,grantee:t}}if(!e.sub||!e.grantee)throw new l("grant_revoked","the caller has no active grant");return{sub:e.sub,grantee:e.grantee}}m(Oe,"identity");function Te(e,t){return[`${e.id}:${t.group}`]}m(Te,"callScope");function Ie(e,t){const a=W(e.commands).find(f=>f.tool===t);if(!a)throw new l("not_found",`tool "${t}" is not in provider "${e.id}"`,{param:"tool"});if(!a.leaf.group)throw new l("not_found",`tool "${t}" has no group`,{param:"tool"});return a.leaf}m(Ie,"leafFor");function B(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t=e.error;return!t||typeof t!="object"||Array.isArray(t)?{}:t}m(B,"errorBody");function Ue(e){const t=Array.isArray(e)?e[0]:e;if(!t)return;const a=Number(t);if(Number.isFinite(a)&&a>=0)return a;const f=Date.parse(t);return Number.isFinite(f)?Math.max(0,Math.ceil((f-Date.now())/1e3)):void 0}m(Ue,"retryAfter");function F(e,t,a,f=!1){const r=B(t),o=typeof r.code=="string"?r.code:void 0,s=typeof r.message=="string"?r.message:`provider answered HTTP ${e}`,p=typeof r.field=="string"?r.field:void 0,g={origin:"provider",details:{code:o??null,message:s,...p?{field:p}:{},...typeof r.next=="string"?{next:r.next}:{},...r.received!==void 0?{received:r.received}:{},status:e},...p?{param:p}:{}};return e===429?new l("provider_rate_limited",s,{...g,retry_after_s:Ue(a["retry-after"])}):e===401||e>=500?new l("provider_unavailable",s,g):e===403&&o==="provider_refused"?new l("provider_refused",s,g):e===403&&o==="approval_required"?new l("provider_error",f?"provider asked for approval again":"provider approval question is invalid",{...g,status:e}):new l("provider_error",s,{...g,status:e})}m(F,"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})}m(Y,"approvalShape");function Be(e){const t=e.origin!==void 0?K({node:e.origin.node??void 0,run:e.origin.run??void 0,machine:e.origin.machine}):K({node:e.node_id??void 0,run:e.run_id??void 0,profile:e.node_id?De(e.node_id)?.profile_id??void 0:void 0,machine:Se()!==null?Ne():void 0});return t===""?{}:{"Crtr-Caller":t}}m(Be,"crtrCallerHeader");async function fr(e,t,a){const f=ae(t.provider);if(f===null)throw new l("not_found",`provider "${t.provider}" is not available`,{param:"provider"});const r={...t,provider:f},o=new AbortController,s=m(()=>o.abort(a.signal.reason),"abort");a.signal.aborted?s():a.signal.addEventListener("abort",s,{once:!0});let p=a.requestId;const j=ge({request_id:p,run_id:e.run_id,node_id:e.node_id,controller:o});let g,h,A,d,_,v,w,q="allowed";try{try{await de(r.provider)}catch{throw le(r.provider)}let n=await U(r.provider,r.version),k=!1,i,b,P,L=!1,J=!1,M,H=0;for(;;){const y=Ie(n,r.tool);if(A=`${n.id}:${y.group}`,oe(e,n.id,y.group),n.transport.kind!=="machine"&&r.machine!==void 0)throw new l("invalid_argument",`provider "${n.id}" is not served by a device, so it takes no machine`,{param:"machine"});n.transport.kind==="machine"&&M===void 0&&(M=await je(r.machine,void 0,{newest:V(n.major,r.tool)}),H=Date.now()+(n.commands.timeouts?.requestMs??qe));let O=null;if(r.approval&&!L){const u=ce(e,r,Y(n,y));P=u.option,O=u.prepared,w=r.approval,p=u.requestId,L=!0}const C={params:y.params.filter(u=>"type"in u&&u.type==="file").map(u=>u.name),output:y.output.find(u=>u.type==="file")?.name??null},x=JSON.stringify(C);if(b!==void 0&&b!==x)throw new l("provider_bad_response","the refreshed manifest changed the tool file contract",{origin:"provider",details:{status:409,message:"manifest_stale changed the tool file contract"}});b=x,O&&(i=await G(e,C,r.arguments,O,{signal:o.signal})),i??=await pe(e,C,r.arguments,{signal:o.signal});const{sub:T,grantee:R}=Oe(e);if(n.transport.kind==="machine"){const u=await Le(e,n,r,y,i,{sub:T,requestId:p,deadlineAt:H,signal:o.signal,machine:M});return v=u.responder,g=u.cleanup,{status:200,body:u.result}}const E=await me({audience:n.transport.endpoint,sub:T,grantee:R,initiating_app:R,scope:Te(n,y)},{signal:o.signal});d=E.jti;const Z={registration:{name:n.id,endpoint:n.transport.endpoint},...n.commands.baseUrl?{baseUrl:n.commands.baseUrl}:{},...n.commands.timeouts?{timeouts:n.commands.timeouts}:{},params:y.params,rest:y.rest,output:y.output,commandPath:[n.id,...r.tool.split(" ")]},ee=he(Z,i.arguments,void 0,{authorization:`Bearer ${E.access_token}`,requestId:p,fileMode:"pass-through",extraHeaders:{...e.run_id?{"Crtr-Run-Id":e.run_id}:{},...Be(e),...P?{"Crtr-Approval":P}:{},...i.headers}});h??=Date.now()+(n.commands.timeouts?.requestMs??3e4);let c;try{if(c=await we(ee,{connectMs:n.commands.timeouts?.connectMs,requestMs:n.commands.timeouts?.requestMs,deadlineAt:h,signal:o.signal}),o.signal.aborted)throw o.signal.reason??new Error("provider call aborted")}catch(u){throw o.signal.aborted?o.signal.reason??u:u instanceof be?new l("provider_unavailable",u.message,{origin:"provider",details:{status:null,code:u.kind}}):u}if(_??=Me({provider:n.id,tool:r.tool,request_id:p,run_id:e.run_id,sub:T,grantee:R,payer:E.payer,ledger_grantee:e.class==="person"?ie:R,jti:E.jti,status:c.status,headers:c.headers}),v=c.headers["crtr-responder"],c.status===401&&ve(E.key),c.status>=200&&c.status<300){const u=c.body;if(!u||typeof u!="object"||Array.isArray(u))throw new l("provider_bad_response","provider result is not a JSON object",{origin:"provider",details:{status:c.status,message:"provider result is not a JSON object"}});let N;try{N=await z(e,i,u,{signal:o.signal})}catch($){throw $ instanceof l&&$.code==="provider_error"?new l("provider_bad_response",$.message,{origin:"provider",details:{status:c.status,message:$.message}}):$}g=N.cleanup;const D=I(y.output,N.result);if(D)throw new l("provider_bad_response",`provider result has an invalid output field "${D.field}"`,{origin:"provider",param:D.field,details:{status:c.status,...D}});if(o.signal.aborted)throw o.signal.reason??new Error("provider call aborted");return{status:200,body:N.result}}const S=B(c.body);if(c.status===403&&S.code==="approval_required"&&!P&&!J){J=!0;const u=await fe(e,r,p,S.question,o.signal,Y(n,y),i);if(w=u.questionId,P=u.option,i=await G(e,C,r.arguments,i,{signal:o.signal}),h=void 0,o.signal.aborted)throw new l("user_denied","the approval wait was canceled");continue}if(c.status===403&&S.code==="approval_required"&&P)throw F(c.status,c.body,c.headers,!0);if(c.status!==401&&S.manifest_stale===!0&&!k){k=!0,await ue(n.id,{conditional:!1,...r.version===void 0?{}:{version:r.version}}),n=await U(n.id,r.version);continue}throw F(c.status,c.body,c.headers,!!P)}}catch(n){throw n instanceof l&&n.question_id&&(w=n.question_id),v===void 0&&n instanceof l&&typeof n.responder=="string"&&(v=n.responder),q=`refused:${n instanceof l?n.code:"server_error"}`,g?.(),n}finally{try{_e({request_id:p,caller:e,provider:r.provider,tool:r.tool,scope_checked:A,outcome:q,jti:d,receipt_id:_,responder:v,question_id:w})}finally{j()}a.signal.removeEventListener("abort",s)}}m(fr,"callProvider");async function Le(e,t,a,f,r,o){const{machine:s}=o,p=Fe(e,o.sub),j={machine:s,envelope:p,signal:o.signal,deadlineAt:o.deadlineAt,mapFailure:F};if(V(t.major,a.tool)){const i=await Ee(j,r.arguments,t.major),b=I(f.output,i);if(b)throw new l("provider_bad_response",`the libraries result has an invalid output field "${b.field}"`,{origin:"provider",param:b.field,details:{status:200,...b}});return{result:i,responder:void 0,cleanup:void 0}}let g=r.arguments,h=null,A="";if(Pe(t.major,a.tool,r.arguments)){const i=await $e(j,r.arguments,t.major);if(o.deadlineAt-Date.now()<=0)throw Q(s,"timeout","");i!==null&&(g=i.arguments,h=i.services,A=i.release)}const d=await Ae(s,{tool:a.tool,arguments:g,version:t.major,requestId:o.requestId,runId:e.run_id,deadlineAt:o.deadlineAt,upload:r.upload===null?null:{id:r.upload.upload_id,link:r.upload.upload_link}},p,o.signal);if(o.signal.aborted)throw o.signal.reason??new Error("provider call aborted");if("unreachable"in d)throw Q(s,d.unreachable.reason,d.unreachable.message);const _=d.headers["crtr-responder"],v=B(d.body);if(d.status<200||d.status>=300){const i=ke(s,d.status,typeof v.code=="string"?v.code:void 0,typeof v.message=="string"?v.message:void 0,d.body),b=i===null&&d.status>=500&&typeof v.code=="string"?new l("provider_error",typeof v.message=="string"?v.message:`the device answered HTTP ${d.status}`,{origin:"runtime",status:d.status,details:{code:v.code,status:d.status}}):null;throw Object.assign(i??b??F(d.status,d.body,d.headers),{responder:_})}const w=d.body&&typeof d.body=="object"&&!Array.isArray(d.body)?d.body.result:void 0;if(!w||typeof w!="object"||Array.isArray(w))throw Object.assign(new l("provider_bad_response","the device result is not a JSON object",{origin:"provider",details:{status:d.status,message:"the device result is not a JSON object"}}),{responder:_});let q=w;if(h!==null){const i=Ce(q,h);if(Re(s,p,A,i.calls),"error"in i)throw Object.assign(i.error,{responder:_});q=i.result}let n;try{n=await z(e,r,q,{signal:o.signal})}catch(i){throw i instanceof l&&i.code==="provider_error"?Object.assign(new l("provider_bad_response",i.message,{origin:"provider",details:{status:d.status,message:i.message}}),{responder:_}):i}const k=I(f.output,n.result);if(k)throw n.cleanup?.(),Object.assign(new l("provider_bad_response",`the device result has an invalid output field "${k.field}"`,{origin:"provider",param:k.field,details:{status:d.status,...k}}),{responder:_});return{result:n.result,responder:_,cleanup:n.cleanup}}m(Le,"callDevice");export{fr as callProvider,Be as crtrCallerHeader,F as mapProviderResponse,Me as onProviderResponse,W as providerLeaves,cr as providerTools};
@@ -1,4 +1,4 @@
1
- var rt=Object.defineProperty;var o=(t,n)=>rt(t,"name",{value:n,configurable:!0});import{prepared as g}from"./sqlite-statements.js";import{createHash as ot,randomUUID as N}from"node:crypto";import{mkdirSync as st,readFileSync as ct,writeFileSync as lt}from"node:fs";import{join as q}from"node:path";import{brokerConfigSnapshot as ut}from"./config.js";import{LockedJsonFile as dt}from"./locked-file.js";import{secretsDir as mt}from"./layout.js";import{openScopedStore as ft}from"./scoped-state/db.js";import{providerStore as ht}from"./scoped-state/paths.js";import{providerModelCooldowns as T,providerModelCooldownsFor as bt,providerRowByReference as P,providerRows as pt,providerWrite as A}from"./scoped-state/providers.js";import{modelCooldownFamily as j}from"./rate-limit-scope.js";const W="anthropic",z="openai-codex",G="openai",At=600*1e3,yt=720*60*60*1e3,zt=[W,z,G],J="strong",Q={anthropic:{label:"Claude",credentials:"anthropic-credentials.json"},"openai-codex":{label:"OpenAI Codex",credentials:"openai-codex-credentials.json"},openai:{label:"ChatGPT",credentials:"openai-credentials.json"}};function I(t){return{refresh:t.refresh,access:t.access,expires:t.expires,...t.accountId?{accountId:t.accountId}:{},...t.clientId?{clientId:t.clientId}:{}}}o(I,"stored");function H(t){return typeof t=="string"&&t.trim()?t.trim():void 0}o(H,"optionalString");function h(){return mt()}o(h,"agentDir");function K(t){return q(h(),Q[t].credentials)}o(K,"credentialFile");function X(t){return t==="ultra"||t==="strong"||t==="medium"||t==="light"?t:void 0}o(X,"normalizeStrength");function f(t){return t.toLowerCase()}o(f,"key");function _t(t){const n=typeof t.label=="string"?t.label.trim():"",e=typeof t.refresh=="string"?t.refresh.trim():"",a=typeof t.access=="string"?t.access.trim():"",i=Number(t.expires);if(!n||!e||!a||!Number.isFinite(i))return;const r=H(t.accountId),s=H(t.clientId);return{label:n,refresh:e,access:a,expires:i,rateLimitedUntil:Number.isFinite(Number(t.rateLimitedUntil))?Number(t.rateLimitedUntil):0,lastAttemptAt:Number.isFinite(Number(t.lastAttemptAt))?Number(t.lastAttemptAt):0,lastRateLimitedAt:Number.isFinite(Number(t.lastRateLimitedAt))?Number(t.lastRateLimitedAt):0,...t.authFailure==="invalid_grant"?{authFailure:"invalid_grant"}:{},...r?{accountId:r}:{},...s?{clientId:s}:{},...Y(t.modelCooldowns)?{modelCooldowns:Y(t.modelCooldowns)}:{}}}o(_t,"credential");function Y(t){if(!t||typeof t!="object"||Array.isArray(t))return;const n=Object.entries(t).filter(([e,a])=>e.trim()!==""&&Number.isFinite(Number(a))).map(([e,a])=>[e,Number(a)]);return n.length?Object.fromEntries(n):void 0}o(Y,"modelCooldowns");function U(t,n){if(!n.label?.trim())throw new Error(`${b(t)} subscription label cannot be empty`);const e=_t(n);if(!e)throw new Error(`Refusing to persist incomplete ${b(t)} subscription "${n.label.trim()}"; pool rows require both OAuth tokens.`);return e}o(U,"assertCredential");function Z(t,n){if(!n||n.trim()==="")return{version:1,accounts:{}};let e;try{e=JSON.parse(n)}catch(a){throw new Error(`managed credential file ${t} is corrupt: ${a instanceof Error?a.message:String(a)}`)}if(typeof e!="object"||e===null||Array.isArray(e)||e.version!==1||typeof e.accounts!="object"||e.accounts===null||Array.isArray(e.accounts))throw new Error(`managed credential file ${t} is corrupt`);return e}o(Z,"parseCredentials");function gt(t,n,e=[]){const a=n.accounts[t.id];if(!a||typeof a.refresh!="string"||typeof a.access!="string"||!Number.isFinite(a.expires))throw new Error(`managed ${t.providerId} account ${t.id} has no credential record`);return{label:t.label,refresh:a.refresh,access:a.access,expires:a.expires,rateLimitedUntil:t.rateLimitedUntil,lastAttemptAt:t.lastAttemptAt,lastRateLimitedAt:t.lastRateLimitedAt,...t.authFailure?{authFailure:t.authFailure}:{},...a.accountId?{accountId:a.accountId}:{},...a.clientId?{clientId:a.clientId}:{},...e.length?{modelCooldowns:Object.fromEntries(e.map(i=>[i.family,i.rateLimitedUntil]))}:{}}}o(gt,"toPublic");function V(t,n){const e=bt(h(),t);return pt(h(),t).map(a=>({row:a,entry:gt(a,n,e.get(a.id))}))}o(V,"rowsWithCredentials");function v(t){return new dt(K(t),!0,`{"version":1,"accounts":{}}
2
- `)}o(v,"managedCredentialFile");function Et(t){return Z(K(t),v(t).readUnlocked())}o(Et,"readFile");function tt(t,n){const e=t.accountId?.trim(),a=n.accountId?.trim();return!!(e&&a&&e!==a)}o(tt,"differentAccount");function _(t){return t.accountId?.trim()||(t.refresh.trim()?`refresh:${ot("sha256").update(t.refresh.trim()).digest("hex")}`:void 0)}o(_,"identity");function et(t,n){const e=H(n);return e?{...t,clientId:e}:t}o(et,"withClientId");function nt(t,n,e){const a=e.refresh?.trim()??"",i=e.access?.trim()??"",r=Number(e.expires);if(!a||!i||!Number.isFinite(r))throw new Error(`${b(t)} subscription "${n}" returned an incomplete credential; the stored credential was left unchanged.`);return{refresh:a,access:i,expires:r}}o(nt,"assertTokens");function wt(t,n){const e=j(n);return Math.max(t.rateLimitedUntil||0,e===void 0?0:t.modelCooldowns?.[e]??0)}o(wt,"coolingUntilFor");function xt(t,n=Date.now()){return[...(t.rateLimitedUntil||0)>n?[{scope:"account",until:t.rateLimitedUntil}]:[],...Object.entries(t.modelCooldowns??{}).filter(([,e])=>e>n).map(([e,a])=>({scope:e,until:a}))]}o(xt,"activeCooldowns");function Gt(t,n=Date.now()){const e=xt(t,n).map(({scope:a,until:i})=>`${a==="account"?"":`${a} `}cooldown ${Lt(i-n)}`);return e.length?e.join(", "):void 0}o(Gt,"formatCooldowns");function Lt(t){const n=Math.max(1,Math.ceil(t/6e4));if(n<60)return`${n}m`;const e=Math.floor(n/60);return e<24?`${e}h${n%60?` ${n%60}m`:""}`:`${Math.floor(e/24)}d${e%24?` ${e%24}h`:""}`}o(Lt,"formatWait");function Rt(t,n){const e=j(n);return e!==void 0&&t.modelCooldowns?.[e]!==void 0}o(Rt,"hasModelCooldownRow");function Jt(t,n){return!!(t.rateLimitedUntil||t.authFailure)||Rt(t,n)}o(Jt,"subscriptionNeedsSuccessWrite");function Qt(t){return t===W||t===z||t===G}o(Qt,"isManagedProvider");function Xt(){const t=q(h(),"openai-device-id");try{const e=ct(t,"utf8").trim();if(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e))return e.toLowerCase()}catch{}const n=N();return st(h(),{recursive:!0,mode:448}),lt(t,`${n}
3
- `,{mode:384}),n}o(Xt,"openAIDeviceId");function b(t){return Q[t].label}o(b,"getProviderLabel");function Yt(t,n=Date.now(),e={}){const a=[],i=[];for(let r=0;r<t.length;r++){const s=t[r],c=s.registered?s.automaticFallback===!1?"non-managed-fallback":s.authenticated?(s.coolingUntil??0)>n?"cooling":void 0:"unauthenticated":"unregistered";if(!c)a.push(s);else if(i.push({candidate:s,reason:c}),e.pinnedToFirst&&r===0&&c!=="cooling")break}return{candidates:a,rejected:i}}o(Yt,"resolveProviderCandidates");class R extends Error{static{o(this,"DuplicateSubscriptionError")}constructor(n){super(n),this.name="DuplicateSubscriptionError"}}function D(t){return V(t,Et(t)).map(({entry:n})=>n)}o(D,"readSubscriptionPool");async function E(t,n){const e=K(t);return v(t).withLockAsync(async(a,i,r)=>{const s=Z(e,a);return{result:await n(s,V(t,s),c=>r(`${JSON.stringify(c)}
4
- `))}})}o(E,"locked");function k(t,n,e){g(t,"INSERT INTO managed_accounts(provider_id,id,label,label_key,position,last_attempt_at,last_rate_limited_at,rate_limited_until,auth_failure) VALUES(?,?,?,?,?,?,?,?,?)").run(n,e.id,e.label,e.labelKey,e.position,e.lastAttemptAt,e.lastRateLimitedAt,e.rateLimitedUntil,e.authFailure??null)}o(k,"insert");function x(t,n){g(t,"UPDATE managed_accounts SET label=?,label_key=?,position=?,last_attempt_at=?,last_rate_limited_at=?,rate_limited_until=?,auth_failure=? WHERE provider_id=? AND id=?").run(n.label,n.labelKey,n.position,n.lastAttemptAt,n.lastRateLimitedAt,n.rateLimitedUntil,n.authFailure??null,n.providerId,n.id)}o(x,"update");function at(t,n,e,a,i,r){g(t,"INSERT INTO managed_account_model_cooldowns(provider_id,account_id,family,rate_limited_until,last_rate_limited_at) VALUES(?,?,?,?,?) ON CONFLICT(provider_id,account_id,family) DO UPDATE SET rate_limited_until=excluded.rate_limited_until,last_rate_limited_at=excluded.last_rate_limited_at").run(n,e,a,i,r)}o(at,"upsertModelCooldown");function C(t,n,e){e===void 0?g(t,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=?").run(n):g(t,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=? AND account_id=?").run(n,e)}o(C,"deleteModelCooldowns");function w(t,n){return V(t,n).map(({entry:e})=>e)}o(w,"publicPool");function $(t,n){try{n()}catch(e){throw new Error(`managed ${b(t)} credential file was published, but its SQL membership update failed; SQL membership remains authoritative: ${e instanceof Error?e.message:String(e)}`,{cause:e})}}o($,"writeMembershipAfterCredentialPublish");async function Zt(t,n){return E(t,async(e,a,i)=>{const r=n.map(u=>U(t,u)),s=r.map((u,l)=>({entry:u,id:a[l]?.row.id??N()})),c={};for(const{entry:u,id:l}of s)c[l]=I(u);return i({version:1,accounts:c}),$(t,()=>A(h(),u=>{const l=T(u,t);g(u,"DELETE FROM managed_accounts WHERE provider_id=?").run(t),g(u,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=?").run(t),s.forEach(({entry:m,id:d},y)=>{k(u,t,{providerId:t,id:d,label:m.label,labelKey:f(m.label),position:y,lastAttemptAt:m.lastAttemptAt,lastRateLimitedAt:m.lastRateLimitedAt,rateLimitedUntil:m.rateLimitedUntil,...m.authFailure?{authFailure:m.authFailure}:{}});for(const[F,M]of Object.entries(m.modelCooldowns??{}))at(u,t,d,F,M,l.get(d)?.find(p=>p.family===F)?.lastRateLimitedAt??m.lastRateLimitedAt)})})),r})}o(Zt,"writeSubscriptionPool");function O(t,n){const e=n.trim().toLowerCase(),a=Number.parseInt(e,10);return Number.isFinite(a)&&String(a)===e&&a>=1&&a<=t.length?a-1:t.findIndex(({entry:i})=>f(i.label)===e)}o(O,"resolve");async function It(t,n){const e=U(t,n);return E(t,async(a,i,r)=>{if(i.some(({entry:l})=>f(l.label)===f(e.label)))throw new R(`${b(t)} subscription already exists: ${e.label}`);const s=_(e),c=i.find(({entry:l})=>_(l)===s);if(s&&c)throw new R(`This ${b(t)} account is already added as \u201C${c.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);const u=N();return a.accounts[u]=I(e),r(a),$(t,()=>A(h(),l=>k(l,t,{providerId:t,id:u,label:e.label,labelKey:f(e.label),position:i.length,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil,...e.authFailure?{authFailure:e.authFailure}:{}}))),w(t,a)})}o(It,"addSubscription");async function vt(t,n){const e=U(t,n);return D(t).find(i=>f(i.label)===f(e.label))?$t(t,e):It(t,e)}o(vt,"upsertSubscription");async function $t(t,n){const e=U(t,n);return E(t,async(a,i,r)=>{const s=i.findIndex(({entry:d})=>f(d.label)===f(e.label)),c=i[s],u=i.find(({entry:d},y)=>y!==s&&_(d)===_(e));if(_(e)&&u)throw new R(`This ${b(t)} account is already added as \u201C${u.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);if(!c){const d=N();return a.accounts[d]=I(e),r(a),$(t,()=>A(h(),y=>k(y,t,{providerId:t,id:d,label:e.label,labelKey:f(e.label),position:i.length,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil}))),w(t,a)}const l=c.row,m=e.lastRateLimitedAt>=l.lastRateLimitedAt;return a.accounts[l.id]=I(e),r(a),$(t,()=>A(h(),d=>{x(d,{...l,label:e.label,labelKey:f(e.label),lastAttemptAt:Math.max(l.lastAttemptAt,e.lastAttemptAt),lastRateLimitedAt:m?e.lastRateLimitedAt:l.lastRateLimitedAt,rateLimitedUntil:m?e.rateLimitedUntil:l.rateLimitedUntil}),tt(c.entry,e)&&C(d,t,l.id)})),w(t,a)})}o($t,"upsertSubscriptionWithUniqueAccount");async function te(t,n,e){return E(t,async(a,i)=>{const r=O(i,n);if(r<0)throw new Error(`Unknown ${b(t)} subscription: ${n}`);const s=e.trim();if(!s)throw new Error(`${b(t)} subscription label cannot be empty`);if(i.some((c,u)=>u!==r&&f(c.entry.label)===f(s)))throw new R(`${b(t)} subscription already exists: ${s}`);return A(h(),c=>x(c,{...i[r].row,label:s,labelKey:f(s)})),w(t,a)})}o(te,"renameSubscription");async function B(t,n){return E(t,async(e,a,i)=>{const r=O(a,n);if(r<0)throw new Error(`Unknown ${b(t)} subscription: ${n}`);const s=a[r].row;return A(h(),c=>{g(c,"DELETE FROM managed_accounts WHERE provider_id=? AND id=?").run(t,s.id),C(c,t,s.id)}),delete e.accounts[s.id],i(e),w(t,e)})}o(B,"remove");async function ee(t,n){return B(t,n)}o(ee,"removeManagedAccount");async function ne(t,n){return B(t,n)}o(ne,"removeSubscription");async function Ft(t){return E(t,async(n,e,a)=>{A(h(),i=>{g(i,"DELETE FROM managed_accounts WHERE provider_id=?").run(t),C(i,t)});for(const{row:i}of e)delete n.accounts[i.id];return a(n),e.length})}o(Ft,"removeAllManagedAccounts");async function ae(t,n){return n===void 0?(await Ft(t),[]):B(t,n)}o(ae,"removeManagedProvider");async function ie(t,n){return E(t,async(e,a)=>{const i=O(a,n);if(i<0)throw new Error(`Unknown ${b(t)} subscription: ${n}`);return A(h(),r=>{[a[i],...a.filter((c,u)=>u!==i)].forEach((c,u)=>x(r,{...c.row,position:u}))}),w(t,e)})}o(ie,"promoteSubscription");async function it(t,n,e){return A(h(),a=>{const i=P(a,t,n);if(!i)throw new Error(`Unknown ${b(t)} subscription: ${n}`);const r=e(i);r&&x(a,r)}),D(t)}o(it,"mutateHealth");async function re(t,n,e=Date.now()){return it(t,n,a=>({...a,lastAttemptAt:Math.max(a.lastAttemptAt,e)}))}o(re,"markSubscriptionAttempt");async function oe(t,n,e,a,i=Date.now()){return E(t,async(r,s)=>{const c=s.find(({entry:u})=>f(u.label)===f(n));if(!c)throw new Error(`Unknown ${b(t)} subscription: ${n}`);return c.entry.refresh!==e.refresh||c.entry.access!==e.access||A(h(),u=>x(u,{...c.row,authFailure:"invalid_grant",lastAttemptAt:Math.max(c.row.lastAttemptAt,i),rateLimitedUntil:Math.max(c.row.rateLimitedUntil,i+Math.max(0,a))})),w(t,r)})}o(oe,"markSubscriptionAuthenticationFailed");async function se(t,n,e,a=Date.now(),i=Date.now(),r={kind:"account"}){return e=Math.min(Math.max(0,e),At),r.kind==="account"?it(t,n,s=>i<s.lastRateLimitedAt?null:{...s,lastAttemptAt:Math.max(s.lastAttemptAt,a),lastRateLimitedAt:i,rateLimitedUntil:i+e}):(A(h(),s=>{const c=P(s,t,n);if(!c)throw new Error(`Unknown ${b(t)} subscription: ${n}`);x(s,{...c,lastAttemptAt:Math.max(c.lastAttemptAt,a)});const u=T(s,t).get(c.id)?.find(l=>l.family===r.family);u&&i<u.lastRateLimitedAt||at(s,t,c.id,r.family,i+e,i)}),D(t))}o(se,"markSubscriptionRateLimited");async function ce(t,n,e=Date.now(),a){const i=j(a);return A(h(),r=>{const s=P(r,t,n);if(!s)throw new Error(`Unknown ${b(t)} subscription: ${n}`);if(e>=s.lastRateLimitedAt&&x(r,{...s,authFailure:void 0,lastAttemptAt:Math.max(s.lastAttemptAt,e),rateLimitedUntil:0}),i===void 0)return;const c=T(r,t).get(s.id)?.find(u=>u.family===i);c&&e>=c.lastRateLimitedAt&&g(r,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=? AND account_id=? AND family=?").run(t,s.id,i)}),D(t)}o(ce,"markSubscriptionSuccess");async function le(t,n,e){return E(t,async(a,i,r)=>{const s=O(i,n);if(s<0)throw new Error(`${b(t)} subscription "${n}" no longer exists`);const c=i[s];if(c.entry.expires>Date.now())return c.entry;const u=await e(c.entry.refresh,c.entry.clientId),l=et(nt(t,c.entry.label,u),u.clientId??c.entry.clientId);return a.accounts[c.row.id]={...a.accounts[c.row.id],...l},r(a),{...c.entry,...l}})}o(le,"refreshSubscriptionCredential");function St(t){const n=t,e=[n?.errorMessage,n?.message,t instanceof Error?t.message:""].filter(i=>typeof i=="string").join(" ");return(typeof n?.status=="number"?n.status:Number(e.match(/\bstatus[=:]\s*(\d+)/i)?.[1]))===400&&/invalid_grant|refresh token .*invalid|refresh token .*not found/i.test(e)}o(St,"isInvalidRefreshTokenError");async function ue(t,n,e){return E(t,async(a,i,r)=>{const s=new Set;let c;for(;s.size<i.length;){const u=n(i.filter(({entry:m})=>!s.has(m.label)).map(({entry:m})=>m));if(!u)break;const l=i.find(({entry:m})=>m.label===u.label);s.add(l.entry.label);try{const m=await e(l.entry);if(m){const d=et(nt(t,l.entry.label,m),m.clientId??l.entry.clientId);return a.accounts[l.row.id]={...a.accounts[l.row.id],...d},r(a),{...l.entry,...d}}return l.entry}catch(m){if(!St(m))throw m;c=m;const d=Date.now();A(h(),y=>x(y,{...l.row,authFailure:"invalid_grant",lastAttemptAt:Math.max(l.row.lastAttemptAt,d),rateLimitedUntil:Math.max(l.row.rateLimitedUntil,d+yt)}))}}if(c)throw c})}o(ue,"modifyElectedSubscription");async function Nt(t,n){const e=n.accountId?.trim(),a=n.email?.trim();if(t!==W)return{...e?{accountId:e}:{},...a?{email:a}:{}};try{const i=await fetch("https://api.anthropic.com/api/oauth/profile",{headers:{Authorization:`Bearer ${n.access}`,Accept:"application/json"},signal:AbortSignal.timeout(3e3)});if(!i.ok)return e?{accountId:e}:{};const r=await i.json(),s=typeof r.account?.uuid=="string"&&r.account.uuid.trim()?r.account.uuid.trim():void 0,c=typeof r.account?.email=="string"&&r.account.email.trim()?r.account.email.trim():void 0;return{...s?{accountId:s}:{},...c?{email:c}:{}}}catch{return e?{accountId:e}:{}}}o(Nt,"enrichManagedLoginIdentity");async function de(t,n,e){const a=await Nt(t,e);return E(t,async(i,r,s)=>{const c=e.autoLabel?void 0:n?.trim(),u=_({refresh:e.refresh,accountId:a.accountId}),l=(c?r.find(({entry:p})=>f(p.label)===f(c)):void 0)??r.find(({entry:p})=>_(p)===u);let m=l?.entry.label??c;if(!m&&e.autoLabel){const p=a.email||n?.trim()||`Account ${r.length+1}`;m=p;for(let S=2;r.some(({entry:L})=>f(L.label)===f(m));S++)m=`${p} (${S})`}if(!m)throw new Error(`${b(t)} subscription label cannot be empty`);const d=U(t,{label:m,refresh:e.refresh,access:e.access,expires:e.expires,...a.accountId?{accountId:a.accountId}:{},...e.clientId?{clientId:e.clientId}:{},rateLimitedUntil:0,lastAttemptAt:Date.now(),lastRateLimitedAt:e.lastRateLimitedAt});if(!l){if(r.some(({entry:L})=>f(L.label)===f(d.label)))throw new R(`${b(t)} subscription already exists: ${d.label}`);const p=r.find(({entry:L})=>_(L)===_(d));if(_(d)&&p)throw new R(`This ${b(t)} account is already added as \u201C${p.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);const S=N();return i.accounts[S]=I(d),s(i),$(t,()=>A(h(),L=>k(L,t,{providerId:t,id:S,label:d.label,labelKey:f(d.label),position:r.length,lastAttemptAt:d.lastAttemptAt,lastRateLimitedAt:d.lastRateLimitedAt,rateLimitedUntil:d.rateLimitedUntil}))),w(t,i)}const y=l.row,F=r.find(({entry:p})=>p.label!==l.entry.label&&_(p)===_(d));if(_(d)&&F)throw new R(`This ${b(t)} account is already added as \u201C${F.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);i.accounts[y.id]=I(d),s(i);const M=d.lastRateLimitedAt>=y.lastRateLimitedAt;return $(t,()=>A(h(),p=>{x(p,{...y,authFailure:void 0,lastAttemptAt:Math.max(y.lastAttemptAt,d.lastAttemptAt),lastRateLimitedAt:M?d.lastRateLimitedAt:y.lastRateLimitedAt,rateLimitedUntil:M?0:y.rateLimitedUntil}),tt(l.entry,d)&&C(p,t,y.id)})),w(t,i)})}o(de,"commitManagedLogin");function Ut(t,n=Date.now()){if(!t?.trim())return;const e=Number(t.trim());if(Number.isFinite(e))return Math.max(0,Math.round(e*1e3));const a=Date.parse(t);return Number.isFinite(a)?Math.max(0,a-n):void 0}o(Ut,"parseRetryAfterHeader");function Dt(t){if(!t)return;const n=o(a=>t instanceof Headers?t.get(a)??void 0:Object.entries(t).find(([i])=>i.toLowerCase()===a)?.[1],"get"),e=n("retry-after-ms");return e!==void 0&&e.trim()!==""&&Number.isFinite(Number(e))?Math.max(0,Number(e)):Ut(n("retry-after"))}o(Dt,"retryAfterMsFromHeaders");function me(t){if(!t||typeof t!="object"||!("headers"in t))return;const n=t.headers;if(!(!(n instanceof Headers)&&(!n||typeof n!="object"||Array.isArray(n))))return Dt(n)}o(me,"retryAfterMsFromError");function Mt(){const t=ut();if(t)return t.rotation;const n=g(ft(ht(h())),"SELECT default_fallback_strength,revert_when_available FROM rotation_settings WHERE singleton=1").get();return{defaultFallbackStrength:X(n?.default_fallback_strength)??J,revertWhenAvailable:n?.revert_when_available===void 0||n.revert_when_available===null?!0:!!n.revert_when_available}}o(Mt,"readRotationConfig");function fe(t){const n=Mt(),e={defaultFallbackStrength:X(t.defaultFallbackStrength)??n.defaultFallbackStrength??J,revertWhenAvailable:t.revertWhenAvailable??n.revertWhenAvailable??!0};return A(h(),a=>g(a,"INSERT INTO rotation_settings(singleton,default_fallback_strength,revert_when_available) VALUES(1,?,?) ON CONFLICT(singleton) DO UPDATE SET default_fallback_strength=excluded.default_fallback_strength,revert_when_available=excluded.revert_when_available").run(e.defaultFallbackStrength,e.revertWhenAvailable?1:0)),e}o(fe,"writeRotationConfig");function he(t,n=Date.now(),e){const a=D(t),i=a.filter(s=>s.authFailure===void 0&&wt(s,e)<=n),r=b(t);return i.length===0?`${r} pool exhausted`:`${r} ${i[0]?.label??t} (${i.length}/${a.length} ready)`}o(he,"formatStatusLine");export{W as ANTHROPIC_PROVIDER_ID,R as DuplicateSubscriptionError,yt as INVALID_REFRESH_TOKEN_BACKOFF_MS,zt as MANAGED_PROVIDER_IDS,At as MAX_RATE_LIMIT_COOLDOWN_MS,G as OPENAI_CHATGPT_PROVIDER_ID,z as OPENAI_CODEX_PROVIDER_ID,xt as activeCooldowns,It as addSubscription,de as commitManagedLogin,wt as coolingUntilFor,Nt as enrichManagedLoginIdentity,Gt as formatCooldowns,he as formatStatusLine,b as getProviderLabel,St as isInvalidRefreshTokenError,Qt as isManagedProvider,re as markSubscriptionAttempt,oe as markSubscriptionAuthenticationFailed,se as markSubscriptionRateLimited,ce as markSubscriptionSuccess,ue as modifyElectedSubscription,Xt as openAIDeviceId,Ut as parseRetryAfterHeader,ie as promoteSubscription,Mt as readRotationConfig,D as readSubscriptionPool,le as refreshSubscriptionCredential,Ft as removeAllManagedAccounts,ee as removeManagedAccount,ae as removeManagedProvider,ne as removeSubscription,te as renameSubscription,Yt as resolveProviderCandidates,me as retryAfterMsFromError,Dt as retryAfterMsFromHeaders,Jt as subscriptionNeedsSuccessWrite,vt as upsertSubscription,$t as upsertSubscriptionWithUniqueAccount,fe as writeRotationConfig,Zt as writeSubscriptionPool};
1
+ var ot=Object.defineProperty;var o=(t,n)=>ot(t,"name",{value:n,configurable:!0});import{prepared as g}from"./sqlite-statements.js";import{createHash as st,randomUUID as U}from"node:crypto";import{mkdirSync as ct,readFileSync as lt,writeFileSync as ut}from"node:fs";import{join as z}from"node:path";import{brokerConfigSnapshot as dt}from"./config.js";import{LockedJsonFile as mt}from"./locked-file.js";import{secretsDir as ft}from"./layout.js";import{openScopedStore as ht}from"./scoped-state/db.js";import{providerStore as bt}from"./scoped-state/paths.js";import{providerModelCooldowns as T,providerModelCooldownsFor as pt,providerRowByReference as P,providerRows as At,providerWrite as A}from"./scoped-state/providers.js";import{modelCooldownFamily as j}from"./rate-limit-scope.js";const W="anthropic",G="openai-codex",J="openai",H=600*1e3,yt=720*60*60*1e3,zt=[W,G,J],Q="strong",X={anthropic:{label:"Claude",credentials:"anthropic-credentials.json"},"openai-codex":{label:"OpenAI Codex",credentials:"openai-codex-credentials.json"},openai:{label:"ChatGPT",credentials:"openai-credentials.json"}};function I(t){return{refresh:t.refresh,access:t.access,expires:t.expires,...t.accountId?{accountId:t.accountId}:{},...t.clientId?{clientId:t.clientId}:{}}}o(I,"stored");function K(t){return typeof t=="string"&&t.trim()?t.trim():void 0}o(K,"optionalString");function h(){return ft()}o(h,"agentDir");function V(t){return z(h(),X[t].credentials)}o(V,"credentialFile");function Y(t){return t==="ultra"||t==="strong"||t==="medium"||t==="light"?t:void 0}o(Y,"normalizeStrength");function f(t){return t.toLowerCase()}o(f,"key");function _t(t){const n=typeof t.label=="string"?t.label.trim():"",e=typeof t.refresh=="string"?t.refresh.trim():"",a=typeof t.access=="string"?t.access.trim():"",i=Number(t.expires);if(!n||!e||!a||!Number.isFinite(i))return;const r=K(t.accountId),s=K(t.clientId);return{label:n,refresh:e,access:a,expires:i,rateLimitedUntil:Number.isFinite(Number(t.rateLimitedUntil))?Number(t.rateLimitedUntil):0,lastAttemptAt:Number.isFinite(Number(t.lastAttemptAt))?Number(t.lastAttemptAt):0,lastRateLimitedAt:Number.isFinite(Number(t.lastRateLimitedAt))?Number(t.lastRateLimitedAt):0,...t.authFailure==="invalid_grant"?{authFailure:"invalid_grant"}:{},...r?{accountId:r}:{},...s?{clientId:s}:{},...Z(t.modelCooldowns)?{modelCooldowns:Z(t.modelCooldowns)}:{}}}o(_t,"credential");function Z(t){if(!t||typeof t!="object"||Array.isArray(t))return;const n=Object.entries(t).filter(([e,a])=>e.trim()!==""&&Number.isFinite(Number(a))).map(([e,a])=>[e,Number(a)]);return n.length?Object.fromEntries(n):void 0}o(Z,"modelCooldowns");function N(t,n){if(!n.label?.trim())throw new Error(`${b(t)} subscription label cannot be empty`);const e=_t(n);if(!e)throw new Error(`Refusing to persist incomplete ${b(t)} subscription "${n.label.trim()}"; pool rows require both OAuth tokens.`);return e}o(N,"assertCredential");function v(t,n){if(!n||n.trim()==="")return{version:1,accounts:{}};let e;try{e=JSON.parse(n)}catch(a){throw new Error(`managed credential file ${t} is corrupt: ${a instanceof Error?a.message:String(a)}`)}if(typeof e!="object"||e===null||Array.isArray(e)||e.version!==1||typeof e.accounts!="object"||e.accounts===null||Array.isArray(e.accounts))throw new Error(`managed credential file ${t} is corrupt`);return e}o(v,"parseCredentials");function gt(t,n,e=[]){const a=n.accounts[t.id];if(!a||typeof a.refresh!="string"||typeof a.access!="string"||!Number.isFinite(a.expires))throw new Error(`managed ${t.providerId} account ${t.id} has no credential record`);return{label:t.label,refresh:a.refresh,access:a.access,expires:a.expires,rateLimitedUntil:t.authFailure||!t.lastRateLimitedAt?t.rateLimitedUntil:Math.min(t.rateLimitedUntil,t.lastRateLimitedAt+H),lastAttemptAt:t.lastAttemptAt,lastRateLimitedAt:t.lastRateLimitedAt,...t.authFailure?{authFailure:t.authFailure}:{},...a.accountId?{accountId:a.accountId}:{},...a.clientId?{clientId:a.clientId}:{},...e.length?{modelCooldowns:Object.fromEntries(e.map(i=>[i.family,i.lastRateLimitedAt?Math.min(i.rateLimitedUntil,i.lastRateLimitedAt+H):i.rateLimitedUntil]))}:{}}}o(gt,"toPublic");function B(t,n){const e=pt(h(),t);return At(h(),t).map(a=>({row:a,entry:gt(a,n,e.get(a.id))}))}o(B,"rowsWithCredentials");function tt(t){return new mt(V(t),!0,`{"version":1,"accounts":{}}
2
+ `)}o(tt,"managedCredentialFile");function Lt(t){return v(V(t),tt(t).readUnlocked())}o(Lt,"readFile");function et(t,n){const e=t.accountId?.trim(),a=n.accountId?.trim();return!!(e&&a&&e!==a)}o(et,"differentAccount");function _(t){return t.accountId?.trim()||(t.refresh.trim()?`refresh:${st("sha256").update(t.refresh.trim()).digest("hex")}`:void 0)}o(_,"identity");function nt(t,n){const e=K(n);return e?{...t,clientId:e}:t}o(nt,"withClientId");function at(t,n,e){const a=e.refresh?.trim()??"",i=e.access?.trim()??"",r=Number(e.expires);if(!a||!i||!Number.isFinite(r))throw new Error(`${b(t)} subscription "${n}" returned an incomplete credential; the stored credential was left unchanged.`);return{refresh:a,access:i,expires:r}}o(at,"assertTokens");function Et(t,n){const e=j(n);return Math.max(t.rateLimitedUntil||0,e===void 0?0:t.modelCooldowns?.[e]??0)}o(Et,"coolingUntilFor");function xt(t,n=Date.now()){return[...(t.rateLimitedUntil||0)>n?[{scope:"account",until:t.rateLimitedUntil}]:[],...Object.entries(t.modelCooldowns??{}).filter(([,e])=>e>n).map(([e,a])=>({scope:e,until:a}))]}o(xt,"activeCooldowns");function Gt(t,n=Date.now()){const e=xt(t,n).map(({scope:a,until:i})=>`${a==="account"?"":`${a} `}cooldown ${wt(i-n)}`);return e.length?e.join(", "):void 0}o(Gt,"formatCooldowns");function wt(t){const n=Math.max(1,Math.ceil(t/6e4));if(n<60)return`${n}m`;const e=Math.floor(n/60);return e<24?`${e}h${n%60?` ${n%60}m`:""}`:`${Math.floor(e/24)}d${e%24?` ${e%24}h`:""}`}o(wt,"formatWait");function Rt(t,n){const e=j(n);return e!==void 0&&t.modelCooldowns?.[e]!==void 0}o(Rt,"hasModelCooldownRow");function Jt(t,n){return!!(t.rateLimitedUntil||t.authFailure)||Rt(t,n)}o(Jt,"subscriptionNeedsSuccessWrite");function Qt(t){return t===W||t===G||t===J}o(Qt,"isManagedProvider");function Xt(){const t=z(h(),"openai-device-id");try{const e=lt(t,"utf8").trim();if(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e))return e.toLowerCase()}catch{}const n=U();return ct(h(),{recursive:!0,mode:448}),ut(t,`${n}
3
+ `,{mode:384}),n}o(Xt,"openAIDeviceId");function b(t){return X[t].label}o(b,"getProviderLabel");function Yt(t,n=Date.now(),e={}){const a=[],i=[];for(let r=0;r<t.length;r++){const s=t[r],c=s.registered?s.automaticFallback===!1?"non-managed-fallback":s.authenticated?(s.coolingUntil??0)>n?"cooling":void 0:"unauthenticated":"unregistered";if(!c)a.push(s);else if(i.push({candidate:s,reason:c}),e.pinnedToFirst&&r===0&&c!=="cooling")break}return{candidates:a,rejected:i}}o(Yt,"resolveProviderCandidates");class R extends Error{static{o(this,"DuplicateSubscriptionError")}constructor(n){super(n),this.name="DuplicateSubscriptionError"}}function D(t){return B(t,Lt(t)).map(({entry:n})=>n)}o(D,"readSubscriptionPool");async function L(t,n){const e=V(t);return tt(t).withLockAsync(async(a,i,r)=>{const s=v(e,a);return{result:await n(s,B(t,s),c=>r(`${JSON.stringify(c)}
4
+ `))}})}o(L,"locked");function k(t,n,e){g(t,"INSERT INTO managed_accounts(provider_id,id,label,label_key,position,last_attempt_at,last_rate_limited_at,rate_limited_until,auth_failure) VALUES(?,?,?,?,?,?,?,?,?)").run(n,e.id,e.label,e.labelKey,e.position,e.lastAttemptAt,e.lastRateLimitedAt,e.rateLimitedUntil,e.authFailure??null)}o(k,"insert");function x(t,n){g(t,"UPDATE managed_accounts SET label=?,label_key=?,position=?,last_attempt_at=?,last_rate_limited_at=?,rate_limited_until=?,auth_failure=? WHERE provider_id=? AND id=?").run(n.label,n.labelKey,n.position,n.lastAttemptAt,n.lastRateLimitedAt,n.rateLimitedUntil,n.authFailure??null,n.providerId,n.id)}o(x,"update");function it(t,n,e,a,i,r){g(t,"INSERT INTO managed_account_model_cooldowns(provider_id,account_id,family,rate_limited_until,last_rate_limited_at) VALUES(?,?,?,?,?) ON CONFLICT(provider_id,account_id,family) DO UPDATE SET rate_limited_until=excluded.rate_limited_until,last_rate_limited_at=excluded.last_rate_limited_at").run(n,e,a,i,r)}o(it,"upsertModelCooldown");function C(t,n,e){e===void 0?g(t,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=?").run(n):g(t,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=? AND account_id=?").run(n,e)}o(C,"deleteModelCooldowns");function E(t,n){return B(t,n).map(({entry:e})=>e)}o(E,"publicPool");function $(t,n){try{n()}catch(e){throw new Error(`managed ${b(t)} credential file was published, but its SQL membership update failed; SQL membership remains authoritative: ${e instanceof Error?e.message:String(e)}`,{cause:e})}}o($,"writeMembershipAfterCredentialPublish");async function Zt(t,n){return L(t,async(e,a,i)=>{const r=n.map(u=>N(t,u)),s=r.map((u,l)=>({entry:u,id:a[l]?.row.id??U()})),c={};for(const{entry:u,id:l}of s)c[l]=I(u);return i({version:1,accounts:c}),$(t,()=>A(h(),u=>{const l=T(u,t);g(u,"DELETE FROM managed_accounts WHERE provider_id=?").run(t),g(u,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=?").run(t),s.forEach(({entry:m,id:d},y)=>{k(u,t,{providerId:t,id:d,label:m.label,labelKey:f(m.label),position:y,lastAttemptAt:m.lastAttemptAt,lastRateLimitedAt:m.lastRateLimitedAt,rateLimitedUntil:m.rateLimitedUntil,...m.authFailure?{authFailure:m.authFailure}:{}});for(const[F,M]of Object.entries(m.modelCooldowns??{}))it(u,t,d,F,M,l.get(d)?.find(p=>p.family===F)?.lastRateLimitedAt??m.lastRateLimitedAt)})})),r})}o(Zt,"writeSubscriptionPool");function O(t,n){const e=n.trim().toLowerCase(),a=Number.parseInt(e,10);return Number.isFinite(a)&&String(a)===e&&a>=1&&a<=t.length?a-1:t.findIndex(({entry:i})=>f(i.label)===e)}o(O,"resolve");async function It(t,n){const e=N(t,n);return L(t,async(a,i,r)=>{if(i.some(({entry:l})=>f(l.label)===f(e.label)))throw new R(`${b(t)} subscription already exists: ${e.label}`);const s=_(e),c=i.find(({entry:l})=>_(l)===s);if(s&&c)throw new R(`This ${b(t)} account is already added as \u201C${c.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);const u=U();return a.accounts[u]=I(e),r(a),$(t,()=>A(h(),l=>k(l,t,{providerId:t,id:u,label:e.label,labelKey:f(e.label),position:i.length,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil,...e.authFailure?{authFailure:e.authFailure}:{}}))),E(t,a)})}o(It,"addSubscription");async function vt(t,n){const e=N(t,n);return D(t).find(i=>f(i.label)===f(e.label))?$t(t,e):It(t,e)}o(vt,"upsertSubscription");async function $t(t,n){const e=N(t,n);return L(t,async(a,i,r)=>{const s=i.findIndex(({entry:d})=>f(d.label)===f(e.label)),c=i[s],u=i.find(({entry:d},y)=>y!==s&&_(d)===_(e));if(_(e)&&u)throw new R(`This ${b(t)} account is already added as \u201C${u.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);if(!c){const d=U();return a.accounts[d]=I(e),r(a),$(t,()=>A(h(),y=>k(y,t,{providerId:t,id:d,label:e.label,labelKey:f(e.label),position:i.length,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil}))),E(t,a)}const l=c.row,m=e.lastRateLimitedAt>=l.lastRateLimitedAt;return a.accounts[l.id]=I(e),r(a),$(t,()=>A(h(),d=>{x(d,{...l,label:e.label,labelKey:f(e.label),lastAttemptAt:Math.max(l.lastAttemptAt,e.lastAttemptAt),lastRateLimitedAt:m?e.lastRateLimitedAt:l.lastRateLimitedAt,rateLimitedUntil:m?e.rateLimitedUntil:l.rateLimitedUntil}),et(c.entry,e)&&C(d,t,l.id)})),E(t,a)})}o($t,"upsertSubscriptionWithUniqueAccount");async function te(t,n,e){return L(t,async(a,i)=>{const r=O(i,n);if(r<0)throw new Error(`Unknown ${b(t)} subscription: ${n}`);const s=e.trim();if(!s)throw new Error(`${b(t)} subscription label cannot be empty`);if(i.some((c,u)=>u!==r&&f(c.entry.label)===f(s)))throw new R(`${b(t)} subscription already exists: ${s}`);return A(h(),c=>x(c,{...i[r].row,label:s,labelKey:f(s)})),E(t,a)})}o(te,"renameSubscription");async function q(t,n){return L(t,async(e,a,i)=>{const r=O(a,n);if(r<0)throw new Error(`Unknown ${b(t)} subscription: ${n}`);const s=a[r].row;return A(h(),c=>{g(c,"DELETE FROM managed_accounts WHERE provider_id=? AND id=?").run(t,s.id),C(c,t,s.id)}),delete e.accounts[s.id],i(e),E(t,e)})}o(q,"remove");async function ee(t,n){return q(t,n)}o(ee,"removeManagedAccount");async function ne(t,n){return q(t,n)}o(ne,"removeSubscription");async function Ft(t){return L(t,async(n,e,a)=>{A(h(),i=>{g(i,"DELETE FROM managed_accounts WHERE provider_id=?").run(t),C(i,t)});for(const{row:i}of e)delete n.accounts[i.id];return a(n),e.length})}o(Ft,"removeAllManagedAccounts");async function ae(t,n){return n===void 0?(await Ft(t),[]):q(t,n)}o(ae,"removeManagedProvider");async function ie(t,n){return L(t,async(e,a)=>{const i=O(a,n);if(i<0)throw new Error(`Unknown ${b(t)} subscription: ${n}`);return A(h(),r=>{[a[i],...a.filter((c,u)=>u!==i)].forEach((c,u)=>x(r,{...c.row,position:u}))}),E(t,e)})}o(ie,"promoteSubscription");async function rt(t,n,e){return A(h(),a=>{const i=P(a,t,n);if(!i)throw new Error(`Unknown ${b(t)} subscription: ${n}`);const r=e(i);r&&x(a,r)}),D(t)}o(rt,"mutateHealth");async function re(t,n,e=Date.now()){return rt(t,n,a=>({...a,lastAttemptAt:Math.max(a.lastAttemptAt,e)}))}o(re,"markSubscriptionAttempt");async function oe(t,n,e,a,i=Date.now()){return L(t,async(r,s)=>{const c=s.find(({entry:u})=>f(u.label)===f(n));if(!c)throw new Error(`Unknown ${b(t)} subscription: ${n}`);return c.entry.refresh!==e.refresh||c.entry.access!==e.access||A(h(),u=>x(u,{...c.row,authFailure:"invalid_grant",lastAttemptAt:Math.max(c.row.lastAttemptAt,i),rateLimitedUntil:Math.max(c.row.rateLimitedUntil,i+Math.max(0,a))})),E(t,r)})}o(oe,"markSubscriptionAuthenticationFailed");async function se(t,n,e,a=Date.now(),i=Date.now(),r={kind:"account"}){return e=Math.min(Math.max(0,e),H),r.kind==="account"?rt(t,n,s=>i<s.lastRateLimitedAt?null:{...s,lastAttemptAt:Math.max(s.lastAttemptAt,a),lastRateLimitedAt:i,rateLimitedUntil:i+e}):(A(h(),s=>{const c=P(s,t,n);if(!c)throw new Error(`Unknown ${b(t)} subscription: ${n}`);x(s,{...c,lastAttemptAt:Math.max(c.lastAttemptAt,a)});const u=T(s,t).get(c.id)?.find(l=>l.family===r.family);u&&i<u.lastRateLimitedAt||it(s,t,c.id,r.family,i+e,i)}),D(t))}o(se,"markSubscriptionRateLimited");async function ce(t,n,e=Date.now(),a){const i=j(a);return A(h(),r=>{const s=P(r,t,n);if(!s)throw new Error(`Unknown ${b(t)} subscription: ${n}`);if(e>=s.lastRateLimitedAt&&x(r,{...s,authFailure:void 0,lastAttemptAt:Math.max(s.lastAttemptAt,e),rateLimitedUntil:0}),i===void 0)return;const c=T(r,t).get(s.id)?.find(u=>u.family===i);c&&e>=c.lastRateLimitedAt&&g(r,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=? AND account_id=? AND family=?").run(t,s.id,i)}),D(t)}o(ce,"markSubscriptionSuccess");async function le(t,n,e){return L(t,async(a,i,r)=>{const s=O(i,n);if(s<0)throw new Error(`${b(t)} subscription "${n}" no longer exists`);const c=i[s];if(c.entry.expires>Date.now())return c.entry;const u=await e(c.entry.refresh,c.entry.clientId),l=nt(at(t,c.entry.label,u),u.clientId??c.entry.clientId);return a.accounts[c.row.id]={...a.accounts[c.row.id],...l},r(a),{...c.entry,...l}})}o(le,"refreshSubscriptionCredential");function St(t){const n=t,e=[n?.errorMessage,n?.message,t instanceof Error?t.message:""].filter(i=>typeof i=="string").join(" ");return(typeof n?.status=="number"?n.status:Number(e.match(/\bstatus[=:]\s*(\d+)/i)?.[1]))===400&&/invalid_grant|refresh token .*invalid|refresh token .*not found/i.test(e)}o(St,"isInvalidRefreshTokenError");async function ue(t,n,e){return L(t,async(a,i,r)=>{const s=new Set;let c;for(;s.size<i.length;){const u=n(i.filter(({entry:m})=>!s.has(m.label)).map(({entry:m})=>m));if(!u)break;const l=i.find(({entry:m})=>m.label===u.label);s.add(l.entry.label);try{const m=await e(l.entry);if(m){const d=nt(at(t,l.entry.label,m),m.clientId??l.entry.clientId);return a.accounts[l.row.id]={...a.accounts[l.row.id],...d},r(a),{...l.entry,...d}}return l.entry}catch(m){if(!St(m))throw m;c=m;const d=Date.now();A(h(),y=>x(y,{...l.row,authFailure:"invalid_grant",lastAttemptAt:Math.max(l.row.lastAttemptAt,d),rateLimitedUntil:Math.max(l.row.rateLimitedUntil,d+yt)}))}}if(c)throw c})}o(ue,"modifyElectedSubscription");async function Ut(t,n){const e=n.accountId?.trim(),a=n.email?.trim();if(t!==W)return{...e?{accountId:e}:{},...a?{email:a}:{}};try{const i=await fetch("https://api.anthropic.com/api/oauth/profile",{headers:{Authorization:`Bearer ${n.access}`,Accept:"application/json"},signal:AbortSignal.timeout(3e3)});if(!i.ok)return e?{accountId:e}:{};const r=await i.json(),s=typeof r.account?.uuid=="string"&&r.account.uuid.trim()?r.account.uuid.trim():void 0,c=typeof r.account?.email=="string"&&r.account.email.trim()?r.account.email.trim():void 0;return{...s?{accountId:s}:{},...c?{email:c}:{}}}catch{return e?{accountId:e}:{}}}o(Ut,"enrichManagedLoginIdentity");async function de(t,n,e){const a=await Ut(t,e);return L(t,async(i,r,s)=>{const c=e.autoLabel?void 0:n?.trim(),u=_({refresh:e.refresh,accountId:a.accountId}),l=(c?r.find(({entry:p})=>f(p.label)===f(c)):void 0)??r.find(({entry:p})=>_(p)===u);let m=l?.entry.label??c;if(!m&&e.autoLabel){const p=a.email||n?.trim()||`Account ${r.length+1}`;m=p;for(let S=2;r.some(({entry:w})=>f(w.label)===f(m));S++)m=`${p} (${S})`}if(!m)throw new Error(`${b(t)} subscription label cannot be empty`);const d=N(t,{label:m,refresh:e.refresh,access:e.access,expires:e.expires,...a.accountId?{accountId:a.accountId}:{},...e.clientId?{clientId:e.clientId}:{},rateLimitedUntil:0,lastAttemptAt:Date.now(),lastRateLimitedAt:e.lastRateLimitedAt});if(!l){if(r.some(({entry:w})=>f(w.label)===f(d.label)))throw new R(`${b(t)} subscription already exists: ${d.label}`);const p=r.find(({entry:w})=>_(w)===_(d));if(_(d)&&p)throw new R(`This ${b(t)} account is already added as \u201C${p.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);const S=U();return i.accounts[S]=I(d),s(i),$(t,()=>A(h(),w=>k(w,t,{providerId:t,id:S,label:d.label,labelKey:f(d.label),position:r.length,lastAttemptAt:d.lastAttemptAt,lastRateLimitedAt:d.lastRateLimitedAt,rateLimitedUntil:d.rateLimitedUntil}))),E(t,i)}const y=l.row,F=r.find(({entry:p})=>p.label!==l.entry.label&&_(p)===_(d));if(_(d)&&F)throw new R(`This ${b(t)} account is already added as \u201C${F.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);i.accounts[y.id]=I(d),s(i);const M=d.lastRateLimitedAt>=y.lastRateLimitedAt;return $(t,()=>A(h(),p=>{x(p,{...y,authFailure:void 0,lastAttemptAt:Math.max(y.lastAttemptAt,d.lastAttemptAt),lastRateLimitedAt:M?d.lastRateLimitedAt:y.lastRateLimitedAt,rateLimitedUntil:M?0:y.rateLimitedUntil}),et(l.entry,d)&&C(p,t,y.id)})),E(t,i)})}o(de,"commitManagedLogin");function Nt(t,n=Date.now()){if(!t?.trim())return;const e=Number(t.trim());if(Number.isFinite(e))return Math.max(0,Math.round(e*1e3));const a=Date.parse(t);return Number.isFinite(a)?Math.max(0,a-n):void 0}o(Nt,"parseRetryAfterHeader");function Dt(t){if(!t)return;const n=o(a=>t instanceof Headers?t.get(a)??void 0:Object.entries(t).find(([i])=>i.toLowerCase()===a)?.[1],"get"),e=n("retry-after-ms");return e!==void 0&&e.trim()!==""&&Number.isFinite(Number(e))?Math.max(0,Number(e)):Nt(n("retry-after"))}o(Dt,"retryAfterMsFromHeaders");function me(t){if(!t||typeof t!="object"||!("headers"in t))return;const n=t.headers;if(!(!(n instanceof Headers)&&(!n||typeof n!="object"||Array.isArray(n))))return Dt(n)}o(me,"retryAfterMsFromError");function Mt(){const t=dt();if(t)return t.rotation;const n=g(ht(bt(h())),"SELECT default_fallback_strength,revert_when_available FROM rotation_settings WHERE singleton=1").get();return{defaultFallbackStrength:Y(n?.default_fallback_strength)??Q,revertWhenAvailable:n?.revert_when_available===void 0||n.revert_when_available===null?!0:!!n.revert_when_available}}o(Mt,"readRotationConfig");function fe(t){const n=Mt(),e={defaultFallbackStrength:Y(t.defaultFallbackStrength)??n.defaultFallbackStrength??Q,revertWhenAvailable:t.revertWhenAvailable??n.revertWhenAvailable??!0};return A(h(),a=>g(a,"INSERT INTO rotation_settings(singleton,default_fallback_strength,revert_when_available) VALUES(1,?,?) ON CONFLICT(singleton) DO UPDATE SET default_fallback_strength=excluded.default_fallback_strength,revert_when_available=excluded.revert_when_available").run(e.defaultFallbackStrength,e.revertWhenAvailable?1:0)),e}o(fe,"writeRotationConfig");function he(t,n=Date.now(),e){const a=D(t),i=a.filter(s=>s.authFailure===void 0&&Et(s,e)<=n),r=b(t);return i.length===0?`${r} pool exhausted`:`${r} ${i[0]?.label??t} (${i.length}/${a.length} ready)`}o(he,"formatStatusLine");export{W as ANTHROPIC_PROVIDER_ID,R as DuplicateSubscriptionError,yt as INVALID_REFRESH_TOKEN_BACKOFF_MS,zt as MANAGED_PROVIDER_IDS,H as MAX_RATE_LIMIT_COOLDOWN_MS,J as OPENAI_CHATGPT_PROVIDER_ID,G as OPENAI_CODEX_PROVIDER_ID,xt as activeCooldowns,It as addSubscription,de as commitManagedLogin,Et as coolingUntilFor,Ut as enrichManagedLoginIdentity,Gt as formatCooldowns,he as formatStatusLine,b as getProviderLabel,St as isInvalidRefreshTokenError,Qt as isManagedProvider,re as markSubscriptionAttempt,oe as markSubscriptionAuthenticationFailed,se as markSubscriptionRateLimited,ce as markSubscriptionSuccess,ue as modifyElectedSubscription,Xt as openAIDeviceId,Nt as parseRetryAfterHeader,ie as promoteSubscription,Mt as readRotationConfig,D as readSubscriptionPool,le as refreshSubscriptionCredential,Ft as removeAllManagedAccounts,ee as removeManagedAccount,ae as removeManagedProvider,ne as removeSubscription,te as renameSubscription,Yt as resolveProviderCandidates,me as retryAfterMsFromError,Dt as retryAfterMsFromHeaders,Jt as subscriptionNeedsSuccessWrite,vt as upsertSubscription,$t as upsertSubscriptionWithUniqueAccount,fe as writeRotationConfig,Zt as writeSubscriptionPool};
@@ -1 +1 @@
1
- var I=Object.defineProperty;var i=(e,r)=>I(e,"name",{value:r,configurable:!0});import{randomUUID as $}from"node:crypto";import{readConfig as O}from"../../../core/config.js";import{activeGrantForApp as q,grantFor as x,localGrants as _}from"../../../core/grants/mirror.js";import{getNode as G}from"../../../core/canvas/canvas.js";import{scopeAllowed as N}from"../../../core/scopes.js";import{ProviderError as u,providerErrorBody as T,scopeMissing as F}from"../../../core/providers/errors.js";import{callProvider as C,providerTools as j}from"../../../core/providers/call.js";import{providerIdsFromGrants as k,providerInstallState as D}from"../../../core/providers/bundles.js";import{providerRootClaims as M,resolveProviderRef as U}from"../../../core/providers/roots.js";import{listInstalledPlugins as z}from"../../../core/resolver.js";import{listAudit as L,writeProviderAudit as V}from"../../../core/providers/audit.js";import{emitEvent as g}from"../../../core/events/emit.js";import{resolvedScopes as w}from"../router.js";const _e=new Set(["provider.tools","provider.call","audit.list"]);function Z(e){if(e.class==="forwarded"){if(e.actor.kind==="person")return{class:"person",sub:null,grantee:null,client_id:null,resolved:null,nodeScopes:null,run_id:null,node_id:null,origin:{node:null,run:null,machine:e.via}};const r=x(e.actor.app,e.sub);if(!r)throw new u("grant_revoked",`app ${e.actor.app} has no active grant`);return{class:"app",sub:e.sub,grantee:e.actor.app,client_id:null,resolved:r.resolved,nodeScopes:e.actor.node_scopes,run_id:null,node_id:null,origin:{node:e.actor.node,run:e.actor.place?.run??null,machine:e.via}}}if(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:w(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:w(e),nodeScopes:e.scopes,run_id:e.run??G(e.node)?.run_id??null,node_id:e.node}}i(Z,"callerFromRequest");function ge(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(ge,"callerFromApp");const B=/^([A-Za-z0-9_-]{1,64}):([A-Za-z0-9_-]{1,64})$/;function H(e){const r=B.exec(e);return r!==null&&r[1]!=="crtr"}i(H,"isProviderScope");const y=new WeakMap;function W(e){let r=y.get(e);return r===void 0&&(r=new Set(e),y.set(e,r)),r}i(W,"heldSet");function S(e,r){return e===null?!0:H(r)?W(e).has(r):N(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 we(e,r,t){const o=`${r}:${t}`;if(!A(e,o))throw F(o)}i(we,"requireGroup");function J(){return!!O("user").runtime?.issuer}i(J,"providerOperationsEnabled");const X=/^[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"||!X.test(e)||e==="crtr")throw c("provider must be a provider id","provider");return e}i(E,"parseProvider");function K(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(K,"parseCallInput");function Q(e,r=new URLSearchParams){const t=i((n,l,m)=>{const f=r.get(n);if(f===null)return l;if(!/^\d+$/.test(f)||Number(f)>m||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:L(e.class==="person"?null:e.grantee,{after:o,limit:d,...a})}}i(Q,"auditList");async function Y(e,r,t){let o=r;const d=performance.now(),a=z("user"),{skipped:n}=M(a),l=new Set(a.filter(s=>s.enabled&&s.manifest.provider?.id===s.name&&!n.has(s.name)).map(s=>s.name)),m=k(_()).filter(s=>!n.has(s)),f=[...new Set([...l,...m])].sort();if(o!==void 0){const s=U(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"?_().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(p=>p.startsWith(`${s}:`)&&A(e,p)))return null;if(l.has(s)){const p=await j(e,s);return p.groups.length>0?p: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(g({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(Y,"providerTools");async function ee(e,r,t){return C(e,r,t)}i(ee,"providerCall");async function re(e,r,t,o){if(!J())throw new u("not_found","provider operations need a directory-connected runtime");if(e==="audit.list")return Q(r,t.query);if(e==="provider.tools")return Y(r,t.params.provider===void 0?void 0:E(t.params.provider),o);if(e==="provider.call"){let d;try{d=K(t.body)}catch(a){const n=t.body&&typeof t.body=="object"&&!Array.isArray(t.body)?t.body:{};throw V({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 ee(r,d,o)}throw new u("not_found","unknown operation")}i(re,"handleProviderOperation");function oe(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(oe,"abortOnClose");function te(e,r,t,o){const d=T(e,r,t,o??void 0);return d.status>=500&&!(e instanceof u)&&g({event:"api.provider.failed",level:"error",error:e instanceof Error?e:new Error(String(e)),fields:{request_id:r}}),d}i(te,"providerFailure");const ye=[{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=$(),{signal:a,release:n}=oe(o.res);let l=null;try{return l=Z(o.principal),{...await re(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(m){return te(m,d,!1,l?.run_id)}finally{n()}},"handler")}));export{_e as PROVIDER_OPERATIONS,oe as abortOnClose,A as callerAllows,ge as callerFromApp,Z as callerFromRequest,re as handleProviderOperation,K as parseCallInput,E as parseProvider,te as providerFailure,J as providerOperationsEnabled,ye as providerRoutes,we as requireGroup};
1
+ var R=Object.defineProperty;var i=(e,r)=>R(e,"name",{value:r,configurable:!0});import{randomUUID as I}from"node:crypto";import{readConfig as $}from"../../../core/config.js";import{activeGrantForApp as O,grantFor as q,localGrants as b}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 F,providerTools as C}from"../../../core/providers/call.js";import{providerIdsFromGrants as j,providerInstallState as k}from"../../../core/providers/bundles.js";import{providerRootClaims as D,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"){if(e.actor.kind==="person")return{class:"person",sub:null,grantee:null,client_id:null,resolved:null,nodeScopes:null,run_id:null,node_id:null,origin:{node:null,run:null,machine:e.via}};const r=q(e.actor.app,e.sub);if(!r)throw new u("grant_revoked",`app ${e.actor.app} has no active grant`);return{class:"app",sub:e.sub,grantee:e.actor.app,client_id:null,resolved:r.resolved,nodeScopes:e.actor.node_scopes,run_id:null,node_id:null,origin:{node:e.actor.node,run:e.actor.place?.run??null,machine:e.via}}}if(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=O(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 _e(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(_e,"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 w=new WeakMap;function W(e){let r=w.get(e);return r===void 0&&(r=new Set(e),w.set(e,r)),r}i(W,"heldSet");function y(e,r){return e===null?!0:B(r)?W(e).has(r):G(e,r)}i(y,"holds");function S(e,r){return e.class==="person"?!0:y(e.resolved??[],r)&&y(e.nodeScopes,r)}i(S,"callerAllows");function ge(e,r,t){const o=`${r}:${t}`;if(!S(e,o))throw T(o)}i(ge,"requireGroup");function J(){return!!$("user").runtime?.issuer}i(J,"providerOperationsEnabled");const X=/^[A-Za-z0-9_-]{1,64}$/;function m(e,r){return new u("invalid_request",e,{param:r})}i(m,"invalid");function A(e){if(typeof e!="string"||!X.test(e)||e==="crtr")throw m("provider must be a provider id","provider");return e}i(A,"parseProvider");function H(e){if(!e||typeof e!="object"||Array.isArray(e))throw new u("invalid_request","a JSON object is required");const r=e,t=A(r.provider),o=r.tool;if(typeof o!="string"||!o.trim())throw m("tool must be a non-empty string","tool");const d=r.arguments??{};if(!d||typeof d!="object"||Array.isArray(d))throw m("arguments must be an object","arguments");const a=r.version;if(a!==void 0&&(typeof a!="number"||!Number.isInteger(a)||a<1))throw m("version must be an integer >= 1","version");const n=r.approval;if(n!==void 0&&(typeof n!="string"||!n))throw m("approval must be a question id","approval");const l=r.machine;if(l!==void 0&&(typeof l!="string"||!l.trim()))throw m("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(H,"parseCallInput");function K(e,r=new URLSearchParams){const t=i((n,l,v)=>{const f=r.get(n);if(f===null)return l;if(!/^\d+$/.test(f)||Number(f)>v||Number(f)<1)throw m(`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}=D(a),l=new Set(a.filter(s=>s.enabled&&s.manifest.provider?.id===s.name&&!n.has(s.name)).map(s=>s.name)),v=j(b()).filter(s=>!n.has(s)),f=[...new Set([...l,...v])].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 E=o===void 0?f:[o],P=e.class==="person"?b().flatMap(s=>s.status==="active"?s.resolved_scopes.map(({scope:c})=>c):[]):e.resolved??[],p=await Promise.all(E.map(async s=>{if(l.has(s))return C(e,s);if(!P.some(h=>h.startsWith(`${s}:`)&&S(e,h)))return null;const c=k(s);return{provider:s,display_name:s,status:c.status,...c.error?{error:c.error}:{},groups:[],commands:[]}})).then(s=>s.filter(c=>c!==null));if(_({level:"debug",event:"api.provider.tools",duration_ms:performance.now()-d,fields:{caller:e.class,provider:o??null,providers:p.length}}),o!==void 0){if(p.length===0)throw new u("not_found",`provider "${o}" is not available`);return{status:200,body:p[0]}}return{status:200,body:{providers:p}}}i(Q,"providerTools");async function Y(e,r,t){return F(e,r,t)}i(Y,"providerCall");async function ee(e,r,t,o){if(!J())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:A(t.params.provider),o);if(e==="provider.call"){let d;try{d=H(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 we=[{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=I(),{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(v){return oe(v,d,!1,l?.run_id)}finally{n()}},"handler")}));export{be as PROVIDER_OPERATIONS,re as abortOnClose,S as callerAllows,_e as callerFromApp,V as callerFromRequest,ee as handleProviderOperation,H as parseCallInput,A as parseProvider,oe as providerFailure,J as providerOperationsEnabled,we as providerRoutes,ge as requireGroup};
@@ -19,7 +19,7 @@ Take scope strings from the **Add permissions** list. Do not guess them. [Provid
19
19
 
20
20
  ## Find the tools, then call one
21
21
 
22
- `providers.tools('<provider>')` lists the tools the person has granted your app, with their descriptions and parameters. Tool names and arguments come from there, never from memory:
22
+ `providers.tools('<provider>')` lists all grouped tools of an installed provider in `commands`; `groups` lists only the tools the person has granted your app, with their descriptions and parameters. Tool names and arguments come from there, never from memory:
23
23
 
24
24
  <include lang="ts">../../../examples/apps/provider-tools.ts</include>
25
25
 
@@ -27,7 +27,7 @@ Take scope strings from the **Add permissions** list. Do not guess them. [Provid
27
27
 
28
28
  <include lang="ts">../../../examples/apps/quickstart-server.ts#provider-call</include>
29
29
 
30
- `send` takes a required `body` and an optional `file`. It has no recipient argument: it texts the verified phone of the person who signed in to your app, from Northlight's number, at most 50 texts per person per UTC day. Calling `providers.tools()` with no provider lists every provider the person's runtime can reach.
30
+ `send` takes a required `body` and an optional `file`. It has no recipient argument: it texts the verified phone of the person who signed in to your app, from Northlight's number, at most 50 texts per person per UTC day. Calling `providers.tools()` with no provider lists every installed provider, including those whose groups the app cannot call.
31
31
 
32
32
  A call is refused when the scope is missing from the grant. The refusal is an `APIError` whose code says why (`scope_missing` for a scope the run or grant lacks).
33
33
 
@@ -15,6 +15,8 @@ Changes to `@north-light/sdk` that can change what an existing app sees. Newest
15
15
 
16
16
  ## 0.3.505
17
17
 
18
+ `providers.tools()` now returns every installed provider, including providers with no granted group. Each entry’s `commands` contains every grouped leaf; `groups` still contains only granted groups. Filter tools shown to users by `groups` and expect `scope_missing` when directly calling an ungranted leaf.
19
+
18
20
  The dashboard and `/resume` content search add two routes and two optional fields. Additive: no request or response an existing app sends or reads changes.
19
21
 
20
22
  - `GET /v1/canvas/live-roots` returns the profile's live root conversations with their attention state (`LiveRootsDTO`).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/cli",
3
- "version": "0.3.510",
3
+ "version": "0.3.511",
4
4
  "description": "crtr — agent runtime with memory, plugins, and marketplaces",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -139,7 +139,7 @@
139
139
  },
140
140
  "license": "UNLICENSED",
141
141
  "dependencies": {
142
- "@north-light/identity": "0.3.510",
142
+ "@north-light/identity": "0.3.511",
143
143
  "@earendil-works/pi-agent-core": "1.1.0",
144
144
  "@earendil-works/pi-ai": "1.1.0",
145
145
  "@earendil-works/pi-coding-agent": "1.1.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/identity",
3
- "version": "0.3.510",
3
+ "version": "0.3.511",
4
4
  "description": "Northlight access-token verification and scope rules",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/runtime.lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@north-light/cli",
3
- "version": "0.3.510",
3
+ "version": "0.3.511",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@north-light/cli",
9
- "version": "0.3.510",
9
+ "version": "0.3.511",
10
10
  "hasInstallScript": true,
11
11
  "license": "UNLICENSED",
12
12
  "workspaces": [
@@ -24,7 +24,7 @@
24
24
  "@milkdown/preset-gfm": "^7.22.3",
25
25
  "@milkdown/prose": "^7.22.3",
26
26
  "@milkdown/utils": "^7.22.3",
27
- "@north-light/identity": "0.3.510",
27
+ "@north-light/identity": "0.3.511",
28
28
  "cron-parser": "^5.6.0",
29
29
  "esbuild": "^0.27.7",
30
30
  "jose": "^6.2.12",
@@ -12468,17 +12468,17 @@
12468
12468
  },
12469
12469
  "packages/crouter-api": {
12470
12470
  "name": "@north-light/api",
12471
- "version": "0.3.510",
12471
+ "version": "0.3.511",
12472
12472
  "license": "UNLICENSED"
12473
12473
  },
12474
12474
  "packages/crouter-env-docker": {
12475
12475
  "name": "@north-light/env-docker",
12476
- "version": "0.3.510",
12476
+ "version": "0.3.511",
12477
12477
  "license": "UNLICENSED"
12478
12478
  },
12479
12479
  "packages/crouter-identity": {
12480
12480
  "name": "@north-light/identity",
12481
- "version": "0.3.510",
12481
+ "version": "0.3.511",
12482
12482
  "license": "UNLICENSED",
12483
12483
  "dependencies": {
12484
12484
  "jose": "^6.2.1"
@@ -12486,20 +12486,20 @@
12486
12486
  },
12487
12487
  "packages/crouter-plugin": {
12488
12488
  "name": "@north-light/plugin",
12489
- "version": "0.3.510",
12489
+ "version": "0.3.511",
12490
12490
  "license": "UNLICENSED",
12491
12491
  "dependencies": {
12492
- "@north-light/api": "0.3.510",
12493
- "@north-light/identity": "0.3.510"
12492
+ "@north-light/api": "0.3.511",
12493
+ "@north-light/identity": "0.3.511"
12494
12494
  }
12495
12495
  },
12496
12496
  "packages/crouter-sdk": {
12497
12497
  "name": "@north-light/sdk",
12498
- "version": "0.3.510",
12498
+ "version": "0.3.511",
12499
12499
  "license": "UNLICENSED",
12500
12500
  "dependencies": {
12501
- "@north-light/api": "0.3.510",
12502
- "@north-light/identity": "0.3.510",
12501
+ "@north-light/api": "0.3.511",
12502
+ "@north-light/identity": "0.3.511",
12503
12503
  "jose": "^6.2.1"
12504
12504
  },
12505
12505
  "bin": {