@north-light/cli 0.3.509 → 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.
- package/dist/api/contract.d.ts +1 -1
- package/dist/api/contract.js +1 -1
- package/dist/builtin-memory/crouter-sdk/release-notes.md +8 -0
- package/dist/builtin-memory/northlight-apps/concepts/providers.md +2 -2
- package/dist/builtin-memory/northlight-apps/providers/device.md +1 -1
- package/dist/clients/attach/viewer.js +413 -413
- package/dist/core/command-plugins/compose.js +1 -1
- package/dist/core/providers/call.d.ts +1 -1
- package/dist/core/providers/call.js +1 -1
- package/dist/core/subscription-state.js +4 -4
- package/dist/daemon/api/handlers/providers.js +1 -1
- package/docs/apps/concepts/providers.md +2 -2
- package/docs/apps/providers/device.md +1 -1
- package/docs/sdk/release-notes.md +8 -0
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +12 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var 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
|
|
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
|
|
2
|
-
`)}o(
|
|
3
|
-
`,{mode:384}),n}o(Xt,"openAIDeviceId");function b(t){return
|
|
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
|
|
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
|
|
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
|
|
|
@@ -5,7 +5,7 @@ description: "Reach the person's Mac from your app: browser, files, Messages and
|
|
|
5
5
|
|
|
6
6
|
# Device
|
|
7
7
|
|
|
8
|
-
The `device` provider reaches the person's own Mac. Its tools run on the person's machine (the provider's transport is `machine`), and each group is its own scope, so ask only for the groups your app needs. What happens when the Mac is asleep or offline is not yet checked on production.
|
|
8
|
+
The `device` provider reaches the person's own Mac. Its tools run on the person's machine (the provider's transport is `machine`), and each group is its own scope, so ask only for the groups your app needs. What happens when the Mac is asleep or offline is not yet checked on production. When the person has more than one registered Mac, a call must name the one to use with `machine` (`providers.call({provider: 'device', tool, arguments, machine})`); without it the runtime refuses with `machine_required` (except `browser libs`, which uses the most recently seen Mac).
|
|
9
9
|
|
|
10
10
|
## Scopes
|
|
11
11
|
|
|
@@ -7,8 +7,16 @@ description: Behavior changes in @north-light/sdk that an app must act on, newes
|
|
|
7
7
|
|
|
8
8
|
Changes to `@north-light/sdk` that can change what an existing app sees. Newest first.
|
|
9
9
|
|
|
10
|
+
## 0.3.510
|
|
11
|
+
|
|
12
|
+
`providers.call` takes an optional `machine`. Additive: no request or response an existing app sends or reads changes.
|
|
13
|
+
|
|
14
|
+
- `providers.call({ provider, tool, arguments, machine? })` names which of the account's devices takes the call (a `machine_id`, `runtime:` prefix optional). `POST /v1/provider/call` already accepted this field; the SDK input type did not. Only a provider served by a machine, such as `device`, accepts it. When the account has more than one such machine and the call names none, the runtime still refuses with `machine_required` (except `device`'s `browser libs`, which uses the most recently seen machine); pass `machine` and repeat the call.
|
|
15
|
+
|
|
10
16
|
## 0.3.505
|
|
11
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
|
+
|
|
12
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.
|
|
13
21
|
|
|
14
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.
|
|
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.
|
|
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",
|
package/runtime.lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.511",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@north-light/cli",
|
|
9
|
-
"version": "0.3.
|
|
9
|
+
"version": "0.3.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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
12489
|
+
"version": "0.3.511",
|
|
12490
12490
|
"license": "UNLICENSED",
|
|
12491
12491
|
"dependencies": {
|
|
12492
|
-
"@north-light/api": "0.3.
|
|
12493
|
-
"@north-light/identity": "0.3.
|
|
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.
|
|
12498
|
+
"version": "0.3.511",
|
|
12499
12499
|
"license": "UNLICENSED",
|
|
12500
12500
|
"dependencies": {
|
|
12501
|
-
"@north-light/api": "0.3.
|
|
12502
|
-
"@north-light/identity": "0.3.
|
|
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": {
|