@cortexkit/opencode-anthropic-auth 1.10.2 → 1.11.0

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/cli.d.ts CHANGED
@@ -1,2 +1,43 @@
1
1
  #!/usr/bin/env node
2
+ import { authorize, exchange } from '@cortexkit/anthropic-auth-core';
3
+ /**
4
+ * Minimal fetch shape relaySetup needs. Narrower than `typeof fetch` (no
5
+ * `preconnect`) so test stubs and the global `fetch` are both assignable
6
+ * without a cast. The global `fetch` satisfies this structurally.
7
+ */
8
+ type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
9
+ /**
10
+ * Dependencies relaySetup talks to the outside world through. Both default to
11
+ * the real implementations (global fetch, the readline-backed prompt) so the
12
+ * production `relay setup` path is unchanged; tests inject deterministic stubs
13
+ * to exercise the full setup logic in-process without a subprocess.
14
+ */
15
+ export interface RelaySetupDeps {
16
+ fetchImpl?: FetchLike;
17
+ prompt?: (message: string) => Promise<string>;
18
+ }
19
+ export declare function relaySetup(deps?: RelaySetupDeps): Promise<void>;
20
+ /**
21
+ * Dependencies the `login` command talks to the outside world through. All
22
+ * default to the real implementations (the readline-backed prompt, and the
23
+ * core authorize/exchange helpers) so the production `login` path is
24
+ * unchanged; tests inject deterministic stubs to exercise the full login flow
25
+ * in-process without a subprocess, real network, or stdin.
26
+ */
27
+ export interface LoginDeps {
28
+ prompt?: (message: string) => Promise<string>;
29
+ authorize?: typeof authorize;
30
+ exchange?: typeof exchange;
31
+ }
32
+ export declare function login(labelArg?: string, deps?: LoginDeps): Promise<void>;
33
+ /**
34
+ * Dependencies the `api add` command talks to the outside world through. The
35
+ * prompt defaults to the real readline-backed prompt so the production
36
+ * `api add` path is unchanged; tests inject canned answers to exercise the
37
+ * full route-add flow in-process without a subprocess or stdin.
38
+ */
39
+ export interface ApiAddDeps {
40
+ prompt?: (message: string) => Promise<string>;
41
+ }
42
+ export declare function addApiRoute(labelArg?: string, deps?: ApiAddDeps): Promise<void>;
2
43
  export {};
package/dist/cli.js CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
- import{Oa as I,Qa as $,m as w,n as k,p as A,r as R,t as d,u as l,v as p}from"./index-fvzta6xw.js";import"./index-p8j7mmfc.js";import{stdin as E,stdout as U}from"node:process";import{createInterface as _}from"node:readline/promises";function g(){return{version:1,main:{type:"opencode",provider:"anthropic"},fallbackOn:[401,403,429],refresh:{enabled:!0,intervalMinutes:10,refreshBeforeExpiryMinutes:240},quota:{enabled:!0,checkIntervalMinutes:5,minimumRemaining:{five_hour:10,seven_day:20},failClosedOnUnknownQuota:!0},accounts:[]}}function y(){console.log(`Usage:
2
+ import{ca as w,cb as S,eb as x,l as N,m as L,n as T,p as q,r as D,t as C,u as H}from"./index-c1phnbjn.js";import"./index-zfddfgk2.js";import{Ob as z}from"./index-7hq52c3b.js";import{stdin as y,stdout as v}from"node:process";import{createInterface as R}from"node:readline/promises";function O(){return{version:1,main:{type:"opencode",provider:"anthropic"},fallbackOn:[401,403,429],refresh:{enabled:!0,intervalMinutes:10,refreshBeforeExpiryMinutes:240},quota:{enabled:!0,checkIntervalMinutes:5,minimumRemaining:{five_hour:10,seven_day:20},failClosedOnUnknownQuota:!0},accounts:[]}}function _(){console.log(`Usage:
3
3
  opencode-anthropic-auth login [label]
4
4
  opencode-anthropic-auth api add [label]
5
5
  opencode-anthropic-auth list
6
6
  opencode-anthropic-auth relay setup
7
7
 
8
8
  Fallback accounts are stored in:
9
- ${d()}`)}function h(e,t){let a=e?.trim();if(!a)throw Error(`${t} is required`);return a}async function f(e){let t=await fetch(`https://api.cloudflare.com/client/v4${e.path}`,{method:e.method,headers:{authorization:`Bearer ${e.token}`,...e.body instanceof FormData?{}:{"content-type":"application/json"},...e.headers},body:e.body}),a=await t.text(),n;try{n=JSON.parse(a)}catch{throw Error(`Cloudflare API returned ${t.status}: ${a}`)}if(!t.ok||n.success===!1){let r=n.errors?.map((o)=>o.message).filter(Boolean).join("; ");throw Error(r||`Cloudflare API returned ${t.status}`)}return n.result}async function P(e,t,a){return f({token:e,method:"POST",path:`/accounts/${t}/storage/kv/namespaces`,body:JSON.stringify({title:a})})}async function T(e){let t={main_module:"worker.js",compatibility_date:"2026-04-28",bindings:[{type:"kv_namespace",name:"RELAY_STATE",namespace_id:e.kvNamespaceId},{type:"secret_text",name:"RELAY_TOKEN",text:e.relayToken}]},a=new FormData;return a.set("metadata",JSON.stringify(t)),a.set("worker.js",new Blob([$],{type:"application/javascript+module"}),"worker.js"),f({token:e.token,method:"PUT",path:`/accounts/${e.accountId}/workers/scripts/${e.scriptName}`,body:a})}async function C(e,t,a){await f({token:e,method:"POST",path:`/accounts/${t}/workers/scripts/${a}/subdomain`,body:JSON.stringify({enabled:!0,previews_enabled:!1})})}async function O(e,t){return f({token:e,method:"GET",path:`/accounts/${t}/workers/subdomain`}).catch(()=>null)}async function N(){let e=await l()??g(),t=h(process.env.CLOUDFLARE_API_TOKEN?.trim()||await s("Cloudflare API token: "),"Cloudflare API token"),a=h(process.env.CLOUDFLARE_ACCOUNT_ID||await s("Cloudflare account ID: "),"Cloudflare account ID"),n=await s("Worker name [opencode-anthropic-relay]: ")||"opencode-anthropic-relay",r=`${n}-state`,o=I();console.log("Creating Cloudflare KV namespace...");let i=await P(t,a,r);console.log("Uploading relay Worker..."),await T({token:t,accountId:a,scriptName:n,kvNamespaceId:i.id,relayToken:o}),await C(t,a,n).catch((m)=>{console.warn(`Could not enable workers.dev automatically: ${m instanceof Error?m.message:String(m)}`)});let c=await O(t,a),b=(c?.subdomain?`https://${n}.${c.subdomain}.workers.dev`:"")||h(await s("Relay Worker URL: "),"Relay Worker URL");e.relay={enabled:!0,url:b,token:o,fallbackToDirect:!0,transport:"http"},await p(e),console.log(`Relay enabled at ${b}`),console.log(`Config saved to ${d()}.`)}var u=null;async function s(e){return u??=_({input:E,output:U}),(await u.question(e)).trim()}function L(){u?.close(),u=null}function v(e,t){let a=e.accounts.findIndex((n)=>n.id===t.id||t.label&&n.label===t.label);if(a>=0){e.accounts[a]={...e.accounts[a],...t,addedAt:e.accounts[a]?.addedAt??t.addedAt,...t.type==="oauth"&&{quota:t.quota,lastRefreshedAt:t.lastRefreshedAt,lastRefreshError:t.lastRefreshError,lastQuotaRefreshError:t.lastQuotaRefreshError}};return}e.accounts.push(t)}async function S(e){let t=await l()??g(),a=e?.trim()||await s("Fallback account label (optional): "),n=await w("max");console.log(`
9
+ ${D()}`)}function W(j,G){let F=j?.trim();if(!F)throw Error(`${G} is required`);return F}async function K(j){let F=await(j.fetchImpl??fetch)(`https://api.cloudflare.com/client/v4${j.path}`,{method:j.method,headers:{authorization:`Bearer ${j.token}`,...j.body instanceof FormData?{}:{"content-type":"application/json"},...j.headers},body:j.body}),M=await F.text(),J;try{J=JSON.parse(M)}catch{throw Error(`Cloudflare API returned ${F.status}: ${M}`)}if(!F.ok||J.success===!1){let X=J.errors?.map((Q)=>Q.message).filter(Boolean).join("; ");throw Error(X||`Cloudflare API returned ${F.status}`)}return J.result}async function A(j,G,F,M){return K({token:j,method:"POST",path:`/accounts/${G}/storage/kv/namespaces`,body:JSON.stringify({title:F}),fetchImpl:M})}async function h(j){let G={main_module:"worker.js",compatibility_date:"2026-04-28",bindings:[{type:"kv_namespace",name:"RELAY_STATE",namespace_id:j.kvNamespaceId},{type:"secret_text",name:"RELAY_TOKEN",text:j.relayToken}]},F=new FormData;return F.set("metadata",JSON.stringify(G)),F.set("worker.js",new Blob([x],{type:"application/javascript+module"}),"worker.js"),K({token:j.token,method:"PUT",path:`/accounts/${j.accountId}/workers/scripts/${j.scriptName}`,body:F,fetchImpl:j.fetchImpl})}async function k(j,G,F,M){await K({token:j,method:"POST",path:`/accounts/${G}/workers/scripts/${F}/subdomain`,body:JSON.stringify({enabled:!0,previews_enabled:!1}),fetchImpl:M})}async function I(j,G,F){return K({token:j,method:"GET",path:`/accounts/${G}/workers/subdomain`,fetchImpl:F}).catch(()=>null)}async function f(j={}){let G=j.fetchImpl??fetch,F=j.prompt??V,M=await C()??O(),J=W(process.env.CLOUDFLARE_API_TOKEN?.trim()||await F("Cloudflare API token: "),"Cloudflare API token"),X=W(process.env.CLOUDFLARE_ACCOUNT_ID||await F("Cloudflare account ID: "),"Cloudflare account ID"),Q=await F("Worker name [opencode-anthropic-relay]: ")||"opencode-anthropic-relay",Y=`${Q}-state`,$=S();console.log("Creating Cloudflare KV namespace...");let Z=await A(J,X,Y,G);console.log("Uploading relay Worker..."),await h({token:J,accountId:X,scriptName:Q,kvNamespaceId:Z.id,relayToken:$,fetchImpl:G}),await k(J,X,Q,G).catch((U)=>{console.warn(`Could not enable workers.dev automatically: ${U instanceof Error?U.message:String(U)}`)});let B=await I(J,X,G),P=(B?.subdomain?`https://${Q}.${B.subdomain}.workers.dev`:"")||W(await V("Relay Worker URL: "),"Relay Worker URL");M.relay={enabled:!0,url:P,token:$,fallbackToDirect:!0,transport:"http"},await H(M),console.log(`Relay enabled at ${P}`),console.log(`Config saved to ${D()}.`)}var E=null;async function V(j){return E??=R({input:y,output:v}),(await E.question(j)).trim()}function b(){E?.close(),E=null}async function g(j,G={}){let F=G.prompt??V,M=G.authorize??N,J=G.exchange??L,X=await C()??O(),Q=j?.trim()||await F("Fallback account label (optional): "),Y=await M("max");console.log(`
10
10
  Open this URL in your browser and complete Claude sign-in:
11
- `),console.log(`${n.url}
12
- `);let r=await s("Paste the full callback URL or authorization code here: "),o=await k(r,n.verifier,n.redirectUri,n.state);if(o.type==="failed")throw Error("Authentication failed");let i=Date.now();v(t,{id:a||crypto.randomUUID(),label:a||void 0,type:"oauth",access:o.access,refresh:o.refresh,expires:o.expires,enabled:!0,addedAt:i,lastUsed:i}),await p(t),console.log(`
13
- Saved fallback account${a?` "${a}"`:""}.`)}async function x(e){let t=await l()??g(),a=e?.trim()||await s("API fallback label (optional): "),n=process.env.OPENCODE_ANTHROPIC_AUTH_API_BASE_URL?.trim()||(await s("Anthropic-compatible base URL [https://api.kie.ai/claude]: ")).trim()||"https://api.kie.ai/claude";if(!R(n))throw Error("API fallback base URL must be an http(s) URL without embedded credentials");let r=process.env.OPENCODE_ANTHROPIC_AUTH_API_KEY?.trim()||await s("API key: ");if(!r.trim())throw Error("API key is required");let i=(process.env.OPENCODE_ANTHROPIC_AUTH_API_AUTH_HEADER?.trim()||await s("Auth header [authorization-bearer|x-api-key] (default authorization-bearer): ")).trim().toLowerCase()==="x-api-key"?"x-api-key":"authorization-bearer",c=Date.now();v(t,{id:a||crypto.randomUUID(),label:a||void 0,type:"api",apiKey:r.trim(),baseURL:n,authHeader:i,enabled:!0,addedAt:c,lastUsed:c}),await p(t),console.log(`
14
- Saved API fallback route${a?` "${a}"`:""} (${n}).`)}async function D(){let e=await l();if(!e?.accounts.length){console.log(`No fallback accounts found at ${d()}.`);return}for(let[t,a]of e.accounts.entries()){let n=a.label||a.id,r=a.enabled===!1?"disabled":"enabled";if(!A(a)){console.log(`${t+1}. ${n} (${r}) — API route ${a.baseURL}`);continue}let o=a.quota?.five_hour?.remainingPercent,i=a.quota?.seven_day?.remainingPercent,c=o===void 0&&i===void 0?"quota unknown":`5h ${o??"?"}%, 1w ${i??"?"}% remaining`;console.log(`${t+1}. ${n} (${r}) — ${c}`)}}async function q(){let[e,t,a]=process.argv.slice(2);if(!e||e==="help"||e==="--help"||e==="-h"){y();return}if(e==="login"){await S(t);return}if(e==="api"&&t==="add"){await x(a);return}if(e==="list"){await D();return}if(e==="relay"&&t==="setup"){await N();return}y(),process.exitCode=1}try{await q()}catch(e){console.error(e instanceof Error?e.message:String(e)),process.exitCode=1}finally{L()}
11
+ `),console.log(`${Y.url}
12
+ `);let $=await F("Paste the full callback URL or authorization code here: "),Z=await J($,Y.verifier,Y.redirectUri,Y.state);if(Z.type==="failed")throw Error("Authentication failed");let B=Date.now();w(X,{id:Q||crypto.randomUUID(),label:Q||void 0,type:"oauth",access:Z.access,refresh:Z.refresh,expires:Z.expires,enabled:!0,addedAt:B,lastUsed:B}),await H(X),console.log(`
13
+ Saved fallback account${Q?` "${Q}"`:""}.`)}async function d(j,G={}){let F=G.prompt??V,M=await C()??O(),J=j?.trim()||await F("API fallback label (optional): "),X=process.env.OPENCODE_ANTHROPIC_AUTH_API_BASE_URL?.trim()||(await F("Anthropic-compatible base URL [https://api.kie.ai/claude]: ")).trim()||"https://api.kie.ai/claude";if(!q(X))throw Error("API fallback base URL must be an http(s) URL without embedded credentials");let Q=process.env.OPENCODE_ANTHROPIC_AUTH_API_KEY?.trim()||await F("API key: ");if(!Q.trim())throw Error("API key is required");let $=(process.env.OPENCODE_ANTHROPIC_AUTH_API_AUTH_HEADER?.trim()||await F("Auth header [authorization-bearer|x-api-key] (default authorization-bearer): ")).trim().toLowerCase()==="x-api-key"?"x-api-key":"authorization-bearer",Z=Date.now();w(M,{id:J||crypto.randomUUID(),label:J||void 0,type:"api",apiKey:Q.trim(),baseURL:X,authHeader:$,enabled:!0,addedAt:Z,lastUsed:Z}),await H(M),console.log(`
14
+ Saved API fallback route${J?` "${J}"`:""} (${X}).`)}async function m(){let j=await C();if(!j?.accounts.length){console.log(`No fallback accounts found at ${D()}.`);return}for(let[G,F]of j.accounts.entries()){let M=F.label||F.id,J=F.enabled===!1?"disabled":"enabled";if(!T(F)){console.log(`${G+1}. ${M} (${J}) — API route ${F.baseURL}`);continue}let X=F.quota?.five_hour?.remainingPercent,Q=F.quota?.seven_day?.remainingPercent,Y=X===void 0&&Q===void 0?"quota unknown":`5h ${X??"?"}%, 1w ${Q??"?"}% remaining`;console.log(`${G+1}. ${M} (${J}) — ${Y}`)}}async function i(){let[j,G,F]=process.argv.slice(2);if(!j||j==="help"||j==="--help"||j==="-h"){_();return}if(j==="login"){await g(G);return}if(j==="api"&&G==="add"){await d(F);return}if(j==="list"){await m();return}if(j==="relay"&&G==="setup"){await f();return}_(),process.exitCode=1}if(z.main==z.module)try{await i()}catch(j){console.error(j instanceof Error?j.message:String(j)),process.exitCode=1}finally{b()}export{f as relaySetup,g as login,d as addApiRoute};
@@ -0,0 +1,2 @@
1
+ import{mkdir as N,readFile as h,writeFile as C}from"node:fs/promises";import{tmpdir as b}from"node:os";import{dirname as L,join as q}from"node:path";var M="OPENCODE_ANTHROPIC_AUTH_SIDEBAR_STATE_FILE",f=q(b(),"opencode-anthropic-auth"),k=q(f,"sidebar-state.json");function B(){return process.env[M]||k}var $={main:{quota:null},fallbacks:[],activeId:void 0,route:"main",relay:null,fastMode:!1,lastUpdated:0};function Y(J){return J!==null&&typeof J==="object"&&!Array.isArray(J)}function R(J){if(!Y(J))return{...$};let Q={quota:null};if(Y(J.main)){let x=J.main;if(Q.quota=Y(x.quota)?x.quota:null,typeof x.quotaBackedOff==="boolean")Q.quotaBackedOff=x.quotaBackedOff;if(typeof x.quotaBackoffUntil==="number")Q.quotaBackoffUntil=x.quotaBackoffUntil;if(typeof x.refreshBackedOff==="boolean")Q.refreshBackedOff=x.refreshBackedOff;if(typeof x.refreshBackoffUntil==="number")Q.refreshBackoffUntil=x.refreshBackoffUntil}let W=Array.isArray(J.fallbacks)?J.fallbacks.filter(Y).filter((x)=>typeof x.id==="string").map((x)=>({id:x.id,label:typeof x.label==="string"?x.label:void 0,quota:Y(x.quota)?x.quota:null,enabled:typeof x.enabled==="boolean"?x.enabled:!1,needsReauth:typeof x.needsReauth==="boolean"?x.needsReauth:!1})):[],X=null;if(Y(J.relay)){let x=J.relay;if(typeof x.enabled==="boolean"&&typeof x.transport==="string")X={enabled:x.enabled,transport:x.transport}}let Z;if(Y(J.cacheKeep)){let x=J.cacheKeep;if(typeof x.enabled==="boolean")Z={enabled:x.enabled,window:typeof x.window==="string"?x.window:void 0,trackedSessions:typeof x.trackedSessions==="number"?x.trackedSessions:void 0}}return{main:Q,fallbacks:W,activeId:typeof J.activeId==="string"?J.activeId:void 0,route:typeof J.route==="string"?J.route:$.route,relay:X,fastMode:typeof J.fastMode==="boolean"?J.fastMode:$.fastMode,cacheKeep:Z,lastUpdated:typeof J.lastUpdated==="number"?J.lastUpdated:0}}var z=Promise.resolve();async function P(){try{let J=await h(B(),"utf8");return R(JSON.parse(J))}catch{return $}}async function S(J,Q=B()){return z=z.then(async()=>{await N(L(Q),{recursive:!0}),await C(Q,JSON.stringify(J),"utf8")}).catch(()=>{}),z}async function _(){return z}function T(J){let Q=J.activeId;if(Q&&Q!=="main"){let W=J.fallbacks?.find((X)=>X.enabled&&X.id===Q);if(W)return{id:W.id,name:W.label??W.id,quota:W.quota}}return{id:"main",name:"main",quota:J.main?.quota??null}}function u(J){let Q=J?.five_hour?.usedPercent??null,W=J?.seven_day?.usedPercent??null;if(Q==null&&W==null)return{fiveHourUsedPercent:Q,sevenDayUsedPercent:W,text:null};return{fiveHourUsedPercent:Q,sevenDayUsedPercent:W,text:`5h: ${Q==null?"—":`${Math.round(Q)}%`} 7d: ${W==null?"—":`${Math.round(W)}%`}`}}var E=18000000,m=604800000,p=300000,I=0.01,U=1;function y(J,Q,W){if(!J.resetsAt)return null;let X=new Date(J.resetsAt).getTime();if(!Number.isFinite(X))return null;let Z=X-Q,x=W-Z;if(x<p)return null;if(x<Q*I)return null;if(x>=Q)return null;let V=J.usedPercent,G=Math.min(Math.max(x/Q*100,0),100),j=V-G,H=Math.abs(j)<U?"on-pace":j>0?"deficit":"reserve",K=null;if(V>0){let O=x*100/V,g=Z+O;if(g<X)K=new Date(g).toISOString()}return{pacePercent:G,deltaPercent:j,state:H,runsOutAt:K}}
2
+ export{B as xb,$ as yb,R as zb,P as Ab,S as Bb,_ as Cb,T as Db,u as Eb,E as Fb,m as Gb,y as Hb};
@@ -1,2 +1,2 @@
1
1
  import{createRequire as q}from"node:module";var j=Object.create;var{getPrototypeOf:k,defineProperty:i,getOwnPropertyNames:l}=Object;var m=Object.prototype.hasOwnProperty;function n(a){return this[a]}var o,p,r=(a,b,d)=>{var f=a!=null&&typeof a==="object";if(f){var g=b?o??=new WeakMap:p??=new WeakMap,h=g.get(a);if(h)return h}d=a!=null?j(k(a)):{};let c=b||!a||!a.__esModule?i(d,"default",{value:a,enumerable:!0}):d;for(let e of l(a))if(!m.call(c,e))i(c,e,{get:n.bind(a,e),enumerable:!0});if(f)g.set(a,c);return c};var s=(a,b)=>()=>(b||a((b={exports:{}}).exports,b),b.exports);var u=q(import.meta.url);
2
- export{r as hb,s as ib,u as jb};
2
+ export{r as Mb,s as Nb,u as Ob};