@cortexkit/opencode-openai-auth 0.6.0 → 0.6.1

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.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import{F as q,K as B,L as V,r as Y,y as Z,z as $}from"./index-zddpw2sa.js";import"./index-n00ybbbq.js";import"./index-hxpv25e8.js";import{Ha as X}from"./index-s2vhsb2v.js";import{execFileSync as E}from"node:child_process";function Q(C,H=process.platform,D=E){try{if(H==="win32"){D("cmd",["/c","start","",C],{stdio:"ignore",timeout:3000});return}D(H==="darwin"?"open":"xdg-open",[C],{stdio:"ignore",timeout:3000})}catch{}}function T(){console.log(`Usage:
2
+ import{F as q,K as B,L as V,r as Y,y as Z,z as $}from"./index-cxze8h26.js";import"./index-n00ybbbq.js";import"./index-hxpv25e8.js";import{Ha as X}from"./index-s2vhsb2v.js";import{execFileSync as E}from"node:child_process";function Q(C,H=process.platform,D=E){try{if(H==="win32"){D("cmd",["/c","start","",C],{stdio:"ignore",timeout:3000});return}D(H==="darwin"?"open":"xdg-open",[C],{stdio:"ignore",timeout:3000})}catch{}}function T(){console.log(`Usage:
3
3
  npx @cortexkit/opencode-openai-auth login [--label <name>] [--headless]
4
4
  npx @cortexkit/opencode-openai-auth list
5
5
  npx @cortexkit/opencode-openai-auth remove <id>
@@ -68,6 +68,21 @@ export interface ResetTargetIdentity {
68
68
  }
69
69
  type ResetCommandContext = CommandContext & Required<Pick<CommandContext, 'resolveResetTarget' | 'fetchImpl' | 'now' | 'randomUUID' | 'refreshResetTargetQuota'>>;
70
70
  export declare function renderResetCoordinatorResult(result: RunResetCreditResult, ctx: ResetCommandContext, boundChatgptAccountId?: string): Promise<OpenDialogPayload>;
71
+ /**
72
+ * Strip credential-shaped fields from a dialog payload's knobs.
73
+ *
74
+ * Knobs are returned across the loopback RPC and JSON-serialized to the TUI, so
75
+ * a knob is a published surface. Individual commands project their own knobs
76
+ * deliberately (see accountKnob), but this is the boundary backstop: a future
77
+ * command that returns a stored object directly cannot leak credentials even if
78
+ * the projection is forgotten, because nothing credential-shaped survives here.
79
+ *
80
+ * Scrub rather than throw. A rejected dialog is a visible outage for a live
81
+ * command, while a scrubbed one keeps working with the leak removed; the warning
82
+ * is what gets the projection fixed. Recurses into nested objects and arrays,
83
+ * since the account list arrives as an array of records.
84
+ */
85
+ export declare function scrubKnobs(value: unknown, path: string, found: string[]): unknown;
71
86
  export declare function buildDialogPayload(command: CommandModalName, args: string, ctx: CommandContext): Promise<OpenDialogPayload>;
72
87
  export declare function applyCommand(request: ApplyRequest, ctx: CommandContext): Promise<ApplyResult>;
73
88
  export {};
@@ -261,6 +261,23 @@ export declare class FallbackAccountManager {
261
261
  startBackgroundRefresh(): void;
262
262
  stopBackgroundRefresh(): void;
263
263
  getUsableFallbackAccounts(existingStorage?: AccountStorage | null): Promise<OAuthAccount[]>;
264
+ /**
265
+ * Stamp `lastUsed` on a served account.
266
+ *
267
+ * This runs on the request path after a response is already in hand, and
268
+ * `lastUsed` is telemetry: nothing reads it to make a routing, quota, or
269
+ * killswitch decision. The WHOLE operation is therefore best-effort, read
270
+ * included — loadAccounts deliberately rethrows anything that is not ENOENT so
271
+ * corruption surfaces to callers that must act on it, and a store lock shared
272
+ * by every session in the host process can legitimately time out under a burst
273
+ * of concurrent turns. Neither may reach this caller: it would discard a
274
+ * successful, already-billed provider response to record a timestamp. Losing
275
+ * the stamp costs nothing a later turn cannot redo.
276
+ *
277
+ * Contrast refreshAccount, whose save persists rotated tokens and MUST
278
+ * propagate: dropping it silently would strand a refresh and invalidate the
279
+ * account's credentials.
280
+ */
264
281
  markUsed(account: FallbackAccount): Promise<void>;
265
282
  accountPassesQuotaPolicy(account: OAuthAccount, storage: AccountStorage | null): boolean;
266
283
  /**
@@ -2,7 +2,7 @@ export declare const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
2
2
  export declare const ISSUER = "https://auth.openai.com";
3
3
  export declare const OAUTH_PORT = 1455;
4
4
  export declare const OAUTH_POLLING_SAFETY_MARGIN_MS = 3000;
5
- export declare const USER_AGENT = "cortexkit-opencode-openai-auth/0.6.0";
5
+ export declare const USER_AGENT = "cortexkit-opencode-openai-auth/0.6.1";
6
6
  export declare const RESERVED_ACCOUNT_ID = "main";
7
7
  export declare const RESERVED_ACCOUNT_ID_ERROR = "\"main\" is a reserved account id; choose a different label.";
8
8
  export interface PkceCodes {
@@ -0,0 +1,89 @@
1
+ import{ua as T}from"./index-n00ybbbq.js";import{Aa as h}from"./index-hxpv25e8.js";import{Ha as H1}from"./index-s2vhsb2v.js";import{createHash as g0}from"node:crypto";var i0=86400000,u0=300000,s0=3600000,r0=60000,n0=900000,c0=300000;function e(B){return B instanceof Error?B.message:String(B)}function o0(B){let Z=B?.status;if(typeof Z==="number")return Z===429||Z>=500;if(!(B instanceof Error))return!1;return B.name==="AbortError"||B.name==="TimeoutError"||B.message.includes("fetch failed")||"code"in B&&(B.code==="ECONNRESET"||B.code==="ECONNREFUSED"||B.code==="ETIMEDOUT"||B.code==="UND_ERR_CONNECT_TIMEOUT")}function B0(B){let Z=e(B),$=B?.status;if(typeof $==="number")return $===429||$>=500;if(!(B instanceof Error))return!1;let W=B.code;return B.name==="AbortError"||B.name==="TimeoutError"||Z.includes("fetch failed")||W==="ECONNRESET"||W==="ECONNREFUSED"||W==="ETIMEDOUT"||W==="UND_ERR_CONNECT_TIMEOUT"}function Q0(B){return g0("sha256").update(B).digest("hex")}function N0(B){let Z=Q0(B.refreshToken),W=(B.previous?.tokenHash===Z?B.previous.retryCount??0:0)+1,X,G=B.error?.retryAfter;if(typeof G==="number"&&G>0)X=G*1000;else if(o0(B.error))X=Math.min(s0,u0*2**Math.min(W-1,6));else X=i0;return{message:e(B.error),checkedAt:B.now,nextRetryAt:B.now+X,retryCount:W,tokenHash:Z}}function U(B,Z,$){if(!B?.nextRetryAt||B.nextRetryAt<=$)return!1;if(!Z)return!0;return B.tokenHash===Q0(Z)}function i(B,Z){return`Codex OAuth refresh is backed off for ${Math.max(1,Math.ceil(((B.nextRetryAt??Z)-Z)/1000))}s after: ${B.message}`}function I(B){let $=(B.previous?.retryCount??0)+1,W,X=B.error?.retryAfter;if(typeof X==="number"&&X>0)W=X*1000;else if(B0(B.error))W=Math.min(n0,r0*2**Math.min($-1,6));else W=c0;return{message:e(B.error),checkedAt:B.now,nextRetryAt:B.now+W,retryCount:$}}function x(B,Z){if(!B?.nextRetryAt||B.nextRetryAt<=Z)return!1;return!0}function Z0(B){if(!B)return;let Z=Number(B);if(Number.isFinite(Z)&&Z>0)return Math.ceil(Z);let $=Date.parse(B);if(Number.isFinite($)){let W=Math.ceil(($-Date.now())/1000);return W>0?W:void 0}return}import{createServer as a0}from"node:http";import{setTimeout as t0}from"node:timers/promises";var P0="0.6.1";var r="app_EMoamEEZ73f0CkXaXp7hrann",q="https://auth.openai.com",w=1455,e0=3000,F0=`cortexkit-opencode-openai-auth/${P0}`,O0="main",B1=`"${O0}" is a reserved account id; choose a different label.`;function b0(B){let Z=new Uint8Array(B),$=String.fromCharCode(...Z);return btoa($).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}async function Z1(){let Z=Array.from(crypto.getRandomValues(new Uint8Array(43))).map((W)=>"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"[W%66]).join(""),$=b0(await crypto.subtle.digest("SHA-256",new TextEncoder().encode(Z)));return{verifier:Z,challenge:$}}function V0(B){if(B.length>16384)return;let Z=B.split(".");if(Z.length!==3)return;let $=Z[1];if(!$||$.length>8192)return;try{return JSON.parse(Buffer.from($,"base64url").toString())}catch{return}}function L0(B){let Z=B.chatgpt_account_id;if(typeof Z==="string"&&Z.length>0)return Z;let $=B["https://api.openai.com/auth"];if($&&typeof $==="object"&&!Array.isArray($)&&typeof $.chatgpt_account_id==="string"&&$.chatgpt_account_id.length>0)return $.chatgpt_account_id;let W=B.organizations;if(Array.isArray(W)){let X=W[0];if(X&&typeof X==="object"&&!Array.isArray(X)&&typeof X.id==="string"&&X.id.length>0)return X.id}return}function s(B){if(B.id_token){let Z=V0(B.id_token),$=Z&&L0(Z);if($)return $}if(B.access_token){let Z=V0(B.access_token);return Z?L0(Z):void 0}return}function $1(B){return typeof B==="string"&&B.toLowerCase()===O0}function $0(B){if($1(B))throw Error(B1);return B}function W1(B,Z,$){let W=new URLSearchParams({response_type:"code",client_id:r,redirect_uri:B,scope:"openid profile email offline_access",code_challenge:Z.challenge,code_challenge_method:"S256",id_token_add_organizations:"true",codex_cli_simplified_flow:"true",state:$,originator:"opencode"});return`${q}/oauth/authorize?${W.toString()}`}async function X1(B,Z,$){let W=await fetch(`${q}/oauth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",code:B,redirect_uri:Z,client_id:r,code_verifier:$.verifier}).toString()});if(!W.ok)throw Error(`Token exchange failed: ${W.status}`);return await W.json()}function G1(B){return B.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;")}var z1=`<!doctype html>
2
+ <html>
3
+ <head>
4
+ <title>CortexKit OpenAI Auth - Authorization Successful</title>
5
+ <style>
6
+ body {
7
+ font-family:
8
+ system-ui,
9
+ -apple-system,
10
+ sans-serif;
11
+ display: flex;
12
+ justify-content: center;
13
+ align-items: center;
14
+ height: 100vh;
15
+ margin: 0;
16
+ background: #131010;
17
+ color: #f1ecec;
18
+ }
19
+ .container {
20
+ text-align: center;
21
+ padding: 2rem;
22
+ }
23
+ h1 {
24
+ color: #f1ecec;
25
+ margin-bottom: 1rem;
26
+ }
27
+ p {
28
+ color: #b7b1b1;
29
+ }
30
+ </style>
31
+ </head>
32
+ <body>
33
+ <div class="container">
34
+ <h1>Authorization Successful</h1>
35
+ <p>You can close this window and return to OpenCode.</p>
36
+ </div>
37
+ <script>
38
+ setTimeout(() => window.close(), 2000)
39
+ </script>
40
+ </body>
41
+ </html>`,u=(B)=>`<!doctype html>
42
+ <html>
43
+ <head>
44
+ <title>CortexKit OpenAI Auth - Authorization Failed</title>
45
+ <style>
46
+ body {
47
+ font-family:
48
+ system-ui,
49
+ -apple-system,
50
+ sans-serif;
51
+ display: flex;
52
+ justify-content: center;
53
+ align-items: center;
54
+ height: 100vh;
55
+ margin: 0;
56
+ background: #131010;
57
+ color: #f1ecec;
58
+ }
59
+ .container {
60
+ text-align: center;
61
+ padding: 2rem;
62
+ }
63
+ h1 {
64
+ color: #fc533a;
65
+ margin-bottom: 1rem;
66
+ }
67
+ p {
68
+ color: #b7b1b1;
69
+ }
70
+ .error {
71
+ color: #ff917b;
72
+ font-family: monospace;
73
+ margin-top: 1rem;
74
+ padding: 1rem;
75
+ background: #3c140d;
76
+ border-radius: 0.5rem;
77
+ }
78
+ </style>
79
+ </head>
80
+ <body>
81
+ <div class="container">
82
+ <h1>Authorization Failed</h1>
83
+ <p>An error occurred during authorization.</p>
84
+ <div class="error">${G1(B)}</div>
85
+ </div>
86
+ </body>
87
+ </html>`,k,m,L=new Map;function M(){return L.size}function C(B){if(L.delete(B),M()===0)_()}async function K1(){let B=`http://localhost:${w}/auth/callback`;if(k)return{port:w,redirectUri:B};if(m)return m;m=(async()=>{let Z=a0(($,W)=>{let X=new URL($.url||"/",`http://127.0.0.1:${w}`);if(X.pathname==="/auth/callback"){let G=X.searchParams.get("code"),z=X.searchParams.get("state"),K=X.searchParams.get("error"),Y=X.searchParams.get("error_description");if(K){let j=Y||K;if(z){let Q=L.get(z);if(Q)Q.reject(Error(j)),C(z);else if(M()===0)_()}else if(M()===0)_();W.writeHead(200,{"Content-Type":"text/html"}),W.end(u(j));return}if(!z){if(M()===0)_();W.writeHead(400,{"Content-Type":"text/html"}),W.end(u("Missing state parameter"));return}if(!G){if(M()===0)_();W.writeHead(400,{"Content-Type":"text/html"}),W.end(u("Missing authorization code"));return}let J=L.get(z);if(!J){if(M()===0)_();W.writeHead(400,{"Content-Type":"text/html"}),W.end(u("Unknown state — authorization may have expired"));return}C(z),X1(G,B,J.pkce).then((j)=>J.resolve(j)).catch((j)=>J.reject(j)),W.writeHead(200,{"Content-Type":"text/html"}),W.end(z1);return}if(X.pathname==="/cancel"){let G=X.searchParams.get("state"),z=G?[G]:[...L.keys()];W.writeHead(200),W.end("Login cancelled");for(let K of z){let Y=L.get(K);if(!Y)continue;Y.reject(Error("Login cancelled")),C(K)}if(M()===0)_();return}W.writeHead(404),W.end("Not found")});return await new Promise(($,W)=>{Z.once("error",W),Z.listen(w,"127.0.0.1",()=>{Z.off("error",W),$()})}),k=Z,{port:w,redirectUri:B}})();try{return await m}finally{m=void 0}}function _(){if(k){let B=k;k=void 0,B.close(()=>{}),B.closeIdleConnections?.(),setTimeout(()=>{B.closeAllConnections?.()},25).unref?.()}}function Y1(B,Z,$=300000,W){return new Promise((X,G)=>{if(W?.aborted){C(Z),G(Error("Login cancelled"));return}let z=setTimeout(()=>{if(L.get(Z))C(Z),G(Error("OAuth callback timeout — authorization took too long"))},$),K=()=>{if(clearTimeout(z),L.get(Z))C(Z),G(Error("Login cancelled"))};if(W)W.addEventListener("abort",K);L.set(Z,{pkce:B,state:Z,resolve:(Y)=>{if(clearTimeout(z),W)W.removeEventListener("abort",K);X(Y)},reject:(Y)=>{if(clearTimeout(z),W)W.removeEventListener("abort",K);G(Y)}})})}async function J1(){let B=await fetch(`${q}/api/accounts/deviceauth/usercode`,{method:"POST",headers:{"Content-Type":"application/json","User-Agent":F0},body:JSON.stringify({client_id:r})});if(!B.ok)throw Error("Failed to initiate device authorization");let Z=await B.json();return{deviceData:Z,url:`${q}/codex/device`,instructions:`Enter code: ${Z.user_code}`}}async function j1(B,Z){let $=Math.max(parseInt(B.interval,10)||5,1)*1000,W=typeof B.expires_in==="number"?B.expires_in:parseInt(B.expires_in||"",10),X=!Number.isNaN(W)&&W>0?W*1000:900000,G=Date.now();while(!0){if(Z?.aborted)throw Error("Device authorization cancelled");if(Date.now()-G>X)throw Error("Device authorization expired");let z=await fetch(`${q}/api/accounts/deviceauth/token`,{method:"POST",headers:{"Content-Type":"application/json","User-Agent":F0},body:JSON.stringify({device_auth_id:B.device_auth_id,user_code:B.user_code})});if(z.ok){let J=await z.json(),j=await fetch(`${q}/oauth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",code:J.authorization_code,redirect_uri:`${q}/deviceauth/callback`,client_id:r,code_verifier:J.code_verifier}).toString()});if(!j.ok)throw Error(`Token exchange failed: ${j.status}`);return await j.json()}if(z.status!==403&&z.status!==404)throw Error(`Device authorization failed: ${z.status}`);let K=X-(Date.now()-G);if(K<=0)throw Error("Device authorization expired");let Y=Math.min($+e0,K);try{await t0(Y,void 0,{signal:Z})}catch(J){if(J instanceof Error&&J.name==="AbortError")throw Error("Device authorization cancelled");throw J}}}function y6(B,Z){let $=-1;if(Z.accountId)$=B.findIndex((W)=>W.accountId&&W.accountId===Z.accountId);if($<0)$=B.findIndex((W)=>W.id===Z.id);if($<0&&Z.label&&!Z.accountId)$=B.findIndex((W)=>W.label===Z.label);if($>=0)return B[$]={...B[$],...Z,addedAt:B[$]?.addedAt??Z.addedAt},$;return B.push(Z),B.length-1}async function x6(B={}){let{label:Z,headless:$=!1,signal:W}=B;if($0(Z),$){let{deviceData:J,url:j,instructions:Q}=await J1(),y=(async()=>{let V=await j1(J,W),F=Date.now(),H0=s(V);return{id:$0(Z||H0||crypto.randomUUID()),label:Z||void 0,type:"oauth",access:V.access_token,refresh:V.refresh_token,expires:F+(V.expires_in??3600)*1000,enabled:!0,addedAt:F,lastUsed:F,lastRefreshedAt:F,accountId:H0}})();return{url:j,instructions:Q,completion:y}}let{redirectUri:X}=await K1(),G=await Z1(),z=b0(crypto.getRandomValues(new Uint8Array(32)).buffer),K=W1(X,G,z),Y=(async()=>{try{if(W?.aborted)throw Error("Login cancelled");let J=await Y1(G,z,void 0,W),j=Date.now(),Q=s(J);return{id:$0(Z||Q||crypto.randomUUID()),label:Z||void 0,type:"oauth",access:J.access_token,refresh:J.refresh_token,expires:j+(J.expires_in??3600)*1000,enabled:!0,addedAt:j,lastUsed:j,lastRefreshedAt:j,accountId:Q}}finally{C(z)}})();return{url:K,instructions:"Complete authorization in your browser. This window will close automatically.",completion:Y}}function D0(B){if(B instanceof Error)return B.message;if(typeof B==="string")return B;try{return JSON.stringify(B)}catch{return String(B)}}var Q1=h("quota"),W0="primary",X0="secondary",N1="app_EMoamEEZ73f0CkXaXp7hrann",P1="https://auth.openai.com";async function m6(B){let Z=await B.fetchImpl(`${P1}/oauth/token`,{method:"POST",signal:AbortSignal.timeout(15000),headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"refresh_token",refresh_token:B.refreshToken,client_id:N1}).toString()});if(!Z.ok){let W=Z0(Z.headers.get("retry-after"));throw Object.assign(Error(`Token refresh failed: ${Z.status}`),{status:Z.status,retryAfter:W,isRefreshError:!0})}let $=await Z.json();if(!$||typeof $.access_token!=="string"||!$.access_token||typeof $.refresh_token!=="string"||!$.refresh_token||typeof $.expires_in!=="number")throw Object.assign(Error("Token refresh failed: malformed response"),{status:Z.status,isRefreshError:!0});return{access:$.access_token,refresh:$.refresh_token,expires:B.now()+$.expires_in*1000,expiresIn:$.expires_in}}var V1="https://chatgpt.com/backend-api/wham/usage";async function k6(B){let Z=B.logger??Q1,$=B.accountKey??"unknown",W=Date.now();Z.debug("wham usage fetch started",{pid:process.pid,accountId:$});try{let X=await B.fetchImpl(V1,{signal:AbortSignal.timeout(15000),headers:{authorization:`Bearer ${B.accessToken}`,"chatgpt-account-id":B.accountId??"","oai-client-platform":"web","oai-client-version":"0","x-openai-target-path":"/backend-api/wham/usage"}});if(!X.ok){let K=Z0(X.headers.get("retry-after"));throw Object.assign(Error(`wham usage check failed: ${X.status}`),{status:X.status,retryAfter:K})}let{normalizeWham:G}=await import("./quota-normalize-vf2pj6n7.js"),z=G(await X.json());return Z.debug("wham usage fetch succeeded",{pid:process.pid,accountId:$,status:X.status,elapsedMs:Date.now()-W}),z}catch(X){throw Z.warn("wham usage fetch failed",{pid:process.pid,accountId:$,status:typeof X?.status==="number"?X.status:"error",elapsedMs:Date.now()-W,error:D0(X)}),X}}import{createHash as L1}from"node:crypto";var F1=globalThis.setTimeout,O1=5;function v(B){let Z=B?.quota?.checkIntervalMinutes??O1;return Math.max(1,Z)*60000}function b1(B){return B?.quota?.enabled!==!1}function n(B,Z){if(!B?.resetsAt)return!1;let $=Date.parse(B.resetsAt);return Number.isFinite($)&&$<=Z}function g6(B,Z,$,W,X){if(X!==void 0&&Number.isFinite(X)&&X>$)return X;let G=Z===W0?B?.primary:Z===X0?B?.secondary:void 0,z=G?.resetsAt?Date.parse(G.resetsAt):NaN;if(Number.isFinite(z)&&z>$)return z;return $+W}function D1(B){let Z=B?.quota?.minimumRemaining||{};return{primary:Z.primary??Z["5h"]??15,secondary:Z.secondary??Z["1w"]??10}}function U0(B,Z,$){if(!b1(Z))return $+v(Z);let W=D1(Z),X=[];for(let G of["primary","secondary"]){let z=B?.[G];if(!z)continue;if(z.remainingPercent>=W[G])continue;let K=z.resetsAt?Date.parse(z.resetsAt):Number.NaN;if(!Number.isFinite(K)||K<=$)return $+v(Z);X.push(K)}if(!X.length)return $+v(Z);return Math.min(...X)+60000}function U1(B){if(!B?.quota?.mainQuota||!B.quota.mainQuotaCheckedAt)return null;return{quota:B.quota.mainQuota,checkedAt:B.quota.mainQuotaCheckedAt,tokenFingerprint:B.quota.mainQuotaToken}}function M1(B){let Z=B?.quota?.refreshEveryNRequests;return typeof Z==="number"&&Number.isFinite(Z)&&Z>0?Math.floor(Z):0}function P(B){return L1("sha256").update(B).digest("hex").slice(0,16)}class O{main=null;mainTokenFp=null;mainQuotaAccountId=null;fallbacks=new Map;fallbackTokenFps=new Map;inflightMain=null;inflightMainFp=null;inflightFallbacks=new Map;mainLastApiError=void 0;fallbackApiErrors=new Map;fallbackErrorTokenFps=new Map;rateLimitedUntilMap=new Map;apiGate=Promise.resolve();lastApiCallAt=0;storage;fetchImpl;now;fetchQuotaFn;onMainQuotaFetched;onApiError;constructor(B){this.storage=B.storage,this.fetchImpl=B.fetchImpl??fetch,this.now=B.now??Date.now,this.fetchQuotaFn=B.fetchQuotaFn,this.onMainQuotaFetched=B.onMainQuotaFetched,this.onApiError=B.onApiError,this.seedMainFromStorage(B.storage),this.seedMainBackoffFromStorage(B.storage)}getMain(B){if(B&&this.main&&this.mainTokenFp&&this.mainTokenFp!==P(B))this.main=null,this.mainTokenFp=null;return this.main}getFallback(B,Z){let $=this.fallbacks.get(B)??null;if(!Z||!$)return $;if(this.fallbackTokenFps.get(B)!==P(Z))return this.fallbacks.delete(B),this.fallbackTokenFps.delete(B),null;return $}getAllFallbacks(){return this.fallbacks}setMain(B,Z,$,W=!1){if(!W&&!M0(Z.quota))return;if(this.mainTokenFp=P(B),$&&this.mainQuotaAccountId&&this.mainQuotaAccountId!==$)this.clearRateLimited("main");if($)this.mainQuotaAccountId=$;if(this.main=Z,G0(Z.quota))this.clearRateLimited("main")}peekMainForPolicy(B){if(B&&this.mainQuotaAccountId&&this.mainQuotaAccountId!==B)return null;return this.main}peekFallbackForPolicy(B,Z){let $=this.fallbacks.get(B)??null;if(Z&&$?.accountId&&$.accountId!==Z)return null;return $}setFallback(B,Z,$,W=!1,X){if(!W&&!M0(Z.quota))return;let G=this.fallbacks.get(B)?.accountId;if(X&&G&&X!==G)this.clearRateLimited(B);let z=X??G;if(this.fallbacks.set(B,{...Z,accountId:z}),$)this.fallbackTokenFps.set(B,P($));else this.fallbackTokenFps.delete(B);if(G0(Z.quota))this.clearRateLimited(B)}markRateLimited(B,Z){let $=this.rateLimitedUntilMap.get(B);if($!==void 0&&$>Z)return;this.rateLimitedUntilMap.set(B,Z)}isRateLimited(B){let Z=this.rateLimitedUntilMap.get(B);return Z!==void 0&&this.now()<Z}rateLimitedUntil(B){return this.rateLimitedUntilMap.get(B)}clearRateLimited(B){this.rateLimitedUntilMap.delete(B)}async refreshMain(B){let Z=P(B);if(this.mainTokenFp&&this.mainTokenFp!==Z)this.main=null,this.mainTokenFp=null;if(this.inflightMain&&this.inflightMainFp===Z)return this.inflightMain;if(this.isBackedOff()){if(this.main)return this.main.quota;throw Error("Quota API rate-limited — try again later")}return this.inflightMainFp=Z,this.inflightMain=this._fetchMain(B),this.inflightMain}async refreshFallback(B,Z){let $=O.fallbackInflightKey(B,Z),W=this.inflightFallbacks.get($);if(W)return W;if(this.isFallbackBackedOff(B,Z)){let G=this.getFallback(B,Z);if(G)return G.quota;throw Error("Quota API rate-limited — try again later")}let X=this._fetchFallback(B,Z);return this.inflightFallbacks.set($,X),X}async refreshAllFallbacks(B){let Z=this.now();for(let $ of B){if($.enabled===!1)continue;if(!$.access)continue;let W=this.getFallback($.id,$.access);if(W&&Z<W.refreshAfter)continue;try{await this.refreshFallback($.id,$.access)}catch{}}}refreshMainInBackground(B){let Z=P(B);if(this.inflightMain&&this.inflightMainFp===Z)return;if(this.isBackedOff())return;this.refreshMain(B).catch(()=>{})}isMainStale(){if(!this.main)return!0;return this.now()>=this.main.refreshAfter}isFallbackStale(B,Z){let $=this.getFallback(B,Z);if(!$)return!0;return this.now()>=$.refreshAfter}shouldRefreshOnRequestCount(B){let Z=M1(this.storage);if(Z<=0)return!1;return B>0&&B%Z===0}needsRefresh(B){return this.isMainStale()||this.shouldRefreshOnRequestCount(B)}updateStorage(B){this.storage=B,this.seedMainFromStorage(B),this.seedMainBackoffFromStorage(B)}seedMainFromStorage(B,Z){let $=U1(B);if(!$)return;let W=Z?P(Z):null;if(W&&$.tokenFingerprint&&$.tokenFingerprint!==W)return;let X={quota:$.quota,refreshAfter:U0($.quota,B,$.checkedAt),checkedAt:$.checkedAt};if(this.main&&this.main.checkedAt>=X.checkedAt&&(!W||!this.mainTokenFp||this.mainTokenFp===W))return;if(this.main=X,this.mainTokenFp=$.tokenFingerprint??null,B?.mainAccountId){if(this.mainQuotaAccountId&&this.mainQuotaAccountId!==B.mainAccountId)this.clearRateLimited("main");this.mainQuotaAccountId=B.mainAccountId}}seedMainBackoffFromStorage(B){let Z=B?.quota?.mainLastQuotaApiError;this.mainLastApiError=Z&&x(Z,this.now())?Z:void 0}seedFallbacksFromAccounts(B){let Z=v(this.storage);for(let $ of B){if($.enabled===!1)continue;if(!$.quota)continue;let W=Math.max($.quota.primary?.checkedAt??0,$.quota.secondary?.checkedAt??0);if(W<=0)continue;let X=this.getFallback($.id,$.access);if(X&&X.checkedAt>=W)continue;this.setFallback($.id,{quota:$.quota,refreshAfter:W+Z,checkedAt:W},$.access,!1,$.accountId)}}isBackedOff(){return x(this.mainLastApiError,this.now())}isFallbackBackedOff(B,Z){if(Z){if(this.fallbackErrorTokenFps.get(B)!==P(Z))return!1}return x(this.fallbackApiErrors.get(B),this.now())}getLastApiError(){return this.mainLastApiError}static API_CALL_GAP_MS=1000;static fallbackInflightKey(B,Z){return JSON.stringify([B,P(Z)])}static quotaLockName(B){return`opencode-fallback-quota-refresh-${B.replace(/[^a-zA-Z0-9._-]+/g,"-")||"account"}`}_enqueueApiFetch(B){let Z=async()=>{let W=this.now()-this.lastApiCallAt;if(W<O.API_CALL_GAP_MS)await new Promise((X)=>{let G=F1(X,O.API_CALL_GAP_MS-W);if(typeof G==="object"&&"unref"in G)G.unref()});return this.lastApiCallAt=this.now(),B()},$=this.apiGate.then(Z,Z);return this.apiGate=$.catch(()=>{}),$}async _fetchMain(B){let Z=P(B);return this._enqueueApiFetch(async()=>{try{if(this.isBackedOff()){if(this.main)return this.main.quota;throw Error("Quota API rate-limited — try again later")}let $=await T({name:"opencode-main-quota-refresh",ttlMs:30000});if(!$){let W=this.main;if(W&&this.now()<W.refreshAfter)return W.quota;throw Error("Quota refresh is already in progress")}try{let W=this.now(),X=await this._fetchQuota(B),G=this.now();if(this.mainTokenFp=P(B),this.main={quota:X,refreshAfter:U0(X,this.storage,G),checkedAt:G},G0(X))this.clearRateLimited("main");return this.mainLastApiError=void 0,this.onMainQuotaFetched?.(X,G,this.mainTokenFp,W),X}catch(W){throw this._handleMainFetchError(W),W}finally{await $.release()}}finally{if(this.inflightMainFp===Z)this.inflightMain=null,this.inflightMainFp=null}})}async _fetchFallback(B,Z){return this._enqueueApiFetch(async()=>{try{if(this.isFallbackBackedOff(B,Z)){let W=this.getFallback(B,Z);if(W)return W.quota;throw Error("Quota API rate-limited — try again later")}let $=await T({name:O.quotaLockName(B),ttlMs:30000});if(!$){let W=this.getFallback(B,Z);if(W&&this.now()<W.refreshAfter)return W.quota;throw Error("Quota refresh is already in progress")}try{let W=await this._fetchQuota(Z),X=this.now();return this.setFallback(B,{quota:W,refreshAfter:X+v(this.storage),checkedAt:X},Z,!0),this.fallbackApiErrors.delete(B),this.fallbackErrorTokenFps.delete(B),W}finally{await $.release()}}catch($){throw this._handleFallbackFetchError(B,Z,$),$}finally{this.inflightFallbacks.delete(O.fallbackInflightKey(B,Z))}})}async _fetchQuota(B){if(!this.fetchQuotaFn)throw Error("Quota fetch not available — no fetchQuotaFn injected (push-only mode)");return this.fetchQuotaFn({accessToken:B,fetchImpl:this.fetchImpl,now:this.now})}static isAuthError(B){return B?.status===401}_handleMainFetchError(B){if(O.isAuthError(B))return;this.mainLastApiError=I({error:B,now:this.now(),previous:this.mainLastApiError}),this.onApiError?.(this.mainLastApiError)}_handleFallbackFetchError(B,Z,$){if(O.isAuthError($))return;let W=P(Z),X=this.fallbackErrorTokenFps.get(B)===W?this.fallbackApiErrors.get(B):void 0;this.fallbackApiErrors.set(B,I({error:$,now:this.now(),previous:X})),this.fallbackErrorTokenFps.set(B,W)}}function M0(B){return Boolean(B.primary||B.secondary)}function G0(B){let{primary:Z,secondary:$}=B;if(!Z&&!$)return!1;if(Z&&Z.usedPercent>=100)return!1;if($&&$.usedPercent>=100)return!1;return!0}import{createHash as y1,randomUUID as x1}from"node:crypto";import{homedir as T1}from"node:os";import{dirname as f1,join as c}from"node:path";import{setTimeout as A1}from"node:timers/promises";import{randomUUID as _1}from"node:crypto";import{mkdir as q1,rename as C1,rm as E1,writeFile as S1}from"node:fs/promises";import{dirname as R1}from"node:path";async function f(B,Z){await q1(R1(B),{recursive:!0});let $=`${B}.${_1()}.tmp`;await S1($,`${JSON.stringify(Z,null,2)}
88
+ `,{encoding:"utf8",mode:384});try{await C1($,B)}catch(W){throw await E1($,{force:!0}).catch(()=>{}),W}}import{readFile as r1}from"node:fs/promises";var E=h("refresh"),_0=h("accounts"),I1=1e4,q0=15000,C0=50,f0="openai-auth.json",w1="openai-auth-state.json";function m1(){if(process.env.OPENCODE_CONFIG_DIR?.trim())return process.env.OPENCODE_CONFIG_DIR.trim();return c(process.env.XDG_CONFIG_HOME||c(T1(),".config"),"opencode")}function A(){return process.env.OPENCODE_OPENAI_AUTH_FILE?.trim()||c(m1(),f0)}function t(B=A()){let Z=process.env.OPENCODE_OPENAI_AUTH_STATE_FILE?.trim();if(Z)return Z;return k1(B)}function k1(B){return B.endsWith(f0)?c(f1(B),w1):`${B}.state.json`}function b(B){return B.type==="oauth"}function h1(B){let Z=B?.trim();if(!Z)return!1;try{let $=new URL(Z);return($.protocol==="http:"||$.protocol==="https:")&&!$.username&&!$.password}catch{return!1}}function zB(B){return B.costZeroing?.enabled!==!1}class o extends Error{code="ACCOUNT_REMOVED_DURING_REFRESH";constructor(B){super(`Fallback account ${B} was removed before refresh`);this.name="AccountRemovedDuringRefreshError"}}var v1=[401,403,429],E0={primary:0,secondary:0},p1=!1,K0={primary:5,secondary:10};function H(B){return B!=null&&typeof B==="object"&&!Array.isArray(B)}var l1=new Set(["__proto__","constructor","prototype"]);function d1(B){return B.length>0&&!l1.has(B)}function g1(B){return B instanceof o||H(B)&&B.name==="AccountRemovedDuringRefreshError"&&B.code==="ACCOUNT_REMOVED_DURING_REFRESH"}function S0(B){return{id:typeof B.id==="string"&&B.id.trim()?B.id.trim():x1(),label:typeof B.label==="string"?B.label:void 0,enabled:typeof B.enabled==="boolean"?B.enabled:void 0,addedAt:typeof B.addedAt==="number"?B.addedAt:void 0,lastUsed:typeof B.lastUsed==="number"?B.lastUsed:void 0,accountId:typeof B.accountId==="string"?B.accountId:void 0}}function R0(B){if(!H(B))return;if(typeof B.message!=="string")return;let Z=Number(B.checkedAt);if(!Number.isFinite(Z))return;let $=Number(B.nextRetryAt),W=Number(B.retryCount);return{message:B.message,checkedAt:Z,nextRetryAt:Number.isFinite($)?$:void 0,retryCount:Number.isFinite(W)?W:void 0,tokenHash:typeof B.tokenHash==="string"?B.tokenHash:void 0}}function i1(B){if(!H(B))return;let Z={};for(let $ of["primary","secondary"]){let W=B[$];if(!H(W))continue;let X=Number(W.usedPercent),G=Number(W.remainingPercent),z=Number(W.checkedAt);if(!Number.isFinite(X)||!Number.isFinite(G)||!Number.isFinite(z))continue;let K=typeof W.windowMinutes==="number"?W.windowMinutes:Number.NaN;Z[$]={usedPercent:X,remainingPercent:G,checkedAt:z,resetsAt:typeof W.resetsAt==="string"?W.resetsAt:void 0,...Number.isFinite(K)&&K>0?{windowMinutes:K}:{}}}for(let $ of["resetCreditsAvailable","resetCreditsApplicable"]){let W=typeof B[$]==="number"?B[$]:Number.NaN;if(Number.isFinite(W)&&W>=0)Z[$]=W}return Object.keys(Z).length?Z:void 0}function u1(B){if(!H(B))return null;if(B.type==="api"){let Z=typeof B.baseURL==="string"?B.baseURL.trim():"",$=typeof B.apiKey==="string"?B.apiKey.trim():"";if(!h1(Z))return null;let W=B.authHeader==="x-api-key"?"x-api-key":"authorization-bearer";return{...S0(B),type:"api",apiKey:$||void 0,baseURL:Z,authHeader:W}}if(B.type!=="oauth")return null;if(typeof B.refresh!=="string"||!B.refresh.trim())return null;return{...S0(B),type:"oauth",access:typeof B.access==="string"?B.access:void 0,refresh:B.refresh,expires:typeof B.expires==="number"?B.expires:void 0,lastRefreshedAt:typeof B.lastRefreshedAt==="number"?B.lastRefreshedAt:void 0,lastRefreshError:R0(B.lastRefreshError),lastQuotaRefreshError:R0(B.lastQuotaRefreshError),quota:i1(B.quota)}}function s1(B){if(!H(B))return;let Z=Object.create(null);for(let[$,W]of Object.entries(B)){if(!d1($)||!H(W))continue;let X={};if(H(W.inFlight))X.inFlight={...W.inFlight};if(H(W.lastOutcome)&&typeof W.lastOutcome.code==="string"&&W.lastOutcome.code.length>0&&typeof W.lastOutcome.at==="number"&&Number.isFinite(W.lastOutcome.at))X.lastOutcome={code:W.lastOutcome.code,at:W.lastOutcome.at,...H(W.lastOutcome.previousOutcome)&&typeof W.lastOutcome.previousOutcome.code==="string"&&W.lastOutcome.previousOutcome.code.length>0&&typeof W.lastOutcome.previousOutcome.at==="number"&&Number.isFinite(W.lastOutcome.previousOutcome.at)?{previousOutcome:{code:W.lastOutcome.previousOutcome.code,at:W.lastOutcome.previousOutcome.at}}:{}};if(typeof W.cooldownUntil==="number"&&Number.isFinite(W.cooldownUntil))X.cooldownUntil=W.cooldownUntil;if(Object.keys(X).length>0)Z[$]=X}return Object.keys(Z).length>0?Z:void 0}function A0(B){if(!H(B)||!Array.isArray(B.accounts))return null;return{version:1,main:{type:"opencode",provider:"openai"},routing:H(B.routing)?B.routing:void 0,fallbackOn:Array.isArray(B.fallbackOn)?B.fallbackOn.filter((Z)=>Number.isInteger(Z)):void 0,refresh:H(B.refresh)?B.refresh:void 0,quota:H(B.quota)?B.quota:void 0,reset:s1(B.reset),dump:H(B.dump)?B.dump:void 0,costZeroing:H(B.costZeroing)?B.costZeroing:void 0,killswitch:H(B.killswitch)?B.killswitch:void 0,logging:H(B.logging)?B.logging:void 0,cachekeep:H(B.cachekeep)?B.cachekeep:void 0,mainAccountId:typeof B.mainAccountId==="string"?B.mainAccountId:void 0,accounts:B.accounts.map(u1).filter((Z)=>Z!=null)}}async function R(B){try{return{exists:!0,value:JSON.parse(await r1(B,"utf8"))}}catch(Z){if(Z.code==="ENOENT")return{exists:!1,value:null};throw Z}}function D(B){return Object.fromEntries(Object.entries(B).filter(([,Z])=>Z!==void 0))}async function n1(B){let Z;try{Z=(await R(B)).value}catch{return null}if(!H(Z)||!Array.isArray(Z.accounts))return null;let $=new Set;for(let W of Z.accounts)if(H(W)&&typeof W.id==="string")$.add(W.id);return $}function I0(B,Z){if(!H(B))return B;let $=H(Z)?Z:{},W=H($.main)?$.main:void 0,X=H($.accounts)?$.accounts:{},G=H(B.quota)?B.quota:{},z=H(B.refresh)?B.refresh:{},K=W??G,Y=W??z,j=Array.isArray(B.accounts)?B.accounts.map((Q)=>{if(!H(Q))return Q;let y=typeof Q.id==="string"&&H(X[Q.id])?X[Q.id]:{};return{...Q,...y}}):void 0;return w0({...B,refresh:D({...z,mainLastRefreshError:Y.lastRefreshError,mainRefreshLeaseId:Y.refreshLeaseId,mainRefreshLeaseUntil:Y.refreshLeaseUntil,mainRefreshLeaseTokenHash:Y.refreshLeaseTokenHash}),quota:D({...G,mainQuota:K.quota,mainQuotaCheckedAt:K.quotaCheckedAt,mainQuotaToken:K.quotaToken,mainLastQuotaApiError:K.lastQuotaApiError}),accounts:j})}async function c1(B=A()){let Z=await R(B);if(!Z.exists)return null;let $=await R(t(B));return A0(I0(Z.value,$.value))}function w0(B){return Object.fromEntries(Object.entries(B).filter(([,Z])=>Z!==void 0))}function o1(B){return D({id:B.id,label:B.label,type:B.type,enabled:B.enabled,addedAt:B.addedAt,accountId:B.accountId,baseURL:B.type==="api"?B.baseURL:void 0,authHeader:B.type==="api"?B.authHeader:void 0})}function m0(B){if(B.type==="api")return D({apiKey:B.apiKey,lastUsed:B.lastUsed});return D({access:B.access,refresh:B.refresh,expires:B.expires,lastUsed:B.lastUsed,lastRefreshedAt:B.lastRefreshedAt,lastRefreshError:B.lastRefreshError,lastQuotaRefreshError:B.lastQuotaRefreshError,quota:B.quota})}function a(B){return Math.max(B?.primary?.checkedAt??0,B?.secondary?.checkedAt??0)}function p(B,Z,$){if($ in Z)B[$]=Z[$];else delete B[$]}function a1(B,Z){return B.access===Z.access&&B.refresh===Z.refresh&&B.expires===Z.expires&&B.lastRefreshedAt===Z.lastRefreshedAt}function t1(B,Z){if(!a1(B,Z))return Z;if(!("lastRefreshError"in Z))return B;if(!("lastRefreshError"in B))return Z;return(Z.lastRefreshError?.checkedAt??0)>(B.lastRefreshError?.checkedAt??0)?Z:B}function e1(B,Z,$){let W=Z.lastRefreshedAt??0,X=$.lastRefreshedAt??0,G=Z.expires??0,z=$.expires??0,K=X>W?$:W>X?Z:z>G?$:G>z?Z:t1(Z,$);p(B,K,"access"),p(B,K,"refresh"),p(B,K,"expires"),p(B,K,"lastRefreshedAt"),p(B,K,"lastRefreshError")}function B6(B,Z){if(!H(B))return Z;let $=B,W=a($.quota),X=a(Z.quota),G=W>X,z=G?{...$,...Z,quota:$.quota,lastQuotaRefreshError:$.lastQuotaRefreshError}:{...$,...Z};if(!G&&!("lastQuotaRefreshError"in Z))delete z.lastQuotaRefreshError;if(!("lastRefreshError"in Z))delete z.lastRefreshError;if(typeof $.lastUsed==="number"&&(typeof Z.lastUsed!=="number"||$.lastUsed>Z.lastUsed))z.lastUsed=$.lastUsed;return e1(z,$,Z),z}function k0(B){let Z=B.refresh?D({enabled:B.refresh.enabled,intervalMinutes:B.refresh.intervalMinutes,refreshBeforeExpiryMinutes:B.refresh.refreshBeforeExpiryMinutes}):void 0,$=B.quota?D({enabled:B.quota.enabled,checkIntervalMinutes:B.quota.checkIntervalMinutes,refreshEveryNRequests:B.quota.refreshEveryNRequests,minimumRemaining:B.quota.minimumRemaining,failClosedOnUnknownQuota:B.quota.failClosedOnUnknownQuota,showToasts:B.quota.showToasts}):void 0;return w0({version:1,main:B.main,routing:B.routing,fallbackOn:B.fallbackOn,refresh:Z,quota:$,reset:B.reset,dump:B.dump,costZeroing:B.costZeroing,killswitch:B.killswitch,logging:B.logging,cachekeep:B.cachekeep,mainAccountId:B.mainAccountId,accounts:B.accounts.map(o1)})}async function d(B){let Z=Date.now()+q0,$=0;while(Date.now()<=Z){$++;let W=await T({name:"save",ttlMs:I1,path:B});if(W)return W;let X=Z-Date.now();if(X<=0)break;await A1(Math.min(C0+d0(C0),X))}throw Error(`Timed out after ${q0}ms waiting for the account store lock on ${B} (${$} attempts); another writer held it for the whole window`)}function h0(B){let Z=Object.fromEntries(B.accounts.map(($)=>[$.id,m0($)]));return{version:1,main:D({quota:B.quota?.mainQuota,quotaCheckedAt:B.quota?.mainQuotaCheckedAt,quotaToken:B.quota?.mainQuotaToken,lastQuotaApiError:B.quota?.mainLastQuotaApiError,lastRefreshError:B.refresh?.mainLastRefreshError,refreshLeaseId:B.refresh?.mainRefreshLeaseId,refreshLeaseUntil:B.refresh?.mainRefreshLeaseUntil,refreshLeaseTokenHash:B.refresh?.mainRefreshLeaseTokenHash}),accounts:Z}}async function YB(B,Z=A()){let $=t(Z),W=await d(Z);try{let X=await d($);try{let G=await R(Z),z=await R($),K=(G.exists?A0(I0(G.value,z.value)):null)??Z6(),Y=B(K)??K,j={...H(G.value)?G.value:{},...k0(Y)};return await f(Z,j),await f($,h0(Y)),Y}finally{await X.release()}}finally{await W.release()}}function Z6(){return{version:1,main:{type:"opencode",provider:"openai"},accounts:[]}}function $6(B,Z){B.main=B.main??{};let $=typeof B.main.quotaCheckedAt==="number"?B.main.quotaCheckedAt:a(B.main.quota),W=typeof Z.quota?.mainQuotaCheckedAt==="number"?Z.quota.mainQuotaCheckedAt:a(Z.quota?.mainQuota);if($>W)return;B.main.quota=Z.quota?.mainQuota,B.main.quotaCheckedAt=Z.quota?.mainQuotaCheckedAt,B.main.quotaToken=Z.quota?.mainQuotaToken,B.main.lastQuotaApiError=Z.quota?.mainLastQuotaApiError}function W6(B,Z){B.main=B.main??{},B.main.lastRefreshError=Z.refresh?.mainLastRefreshError,B.main.refreshLeaseId=Z.refresh?.mainRefreshLeaseId,B.main.refreshLeaseUntil=Z.refresh?.mainRefreshLeaseUntil,B.main.refreshLeaseTokenHash=Z.refresh?.mainRefreshLeaseTokenHash}function Y0(B){if(Array.isArray(B))return B.map(Y0);if(!H(B))return B;return Object.fromEntries(Object.entries(B).filter(([,Z])=>Z!==void 0).map(([Z,$])=>[Z,Y0($)]))}async function X6(B,Z=A(),$={mainQuota:!0,mainRefresh:!0,accounts:!0}){let W=t(Z),X=await d(W);try{let G=(await R(W)).value,z=H(G)?{...G,version:1}:{version:1};if($.mainQuota)$6(z,B);if($.mainRefresh)W6(z,B);if($.accounts){let K=$.accounts===!0?null:new Set($.accounts),Y=await n1(Z);z.accounts={...H(z.accounts)?z.accounts:{}};for(let J of B.accounts){if(K&&!K.has(J.id))continue;if(Y&&!Y.has(J.id))continue;z.accounts[J.id]=B6(z.accounts[J.id],m0(J))}if(K){for(let J of K)if(!B.accounts.some((j)=>j.id===J))delete z.accounts[J]}if(Y){for(let J of Object.keys(z.accounts))if(!Y.has(J))delete z.accounts[J]}}await f(W,Y0(z))}finally{await X.release()}}function G6(B){return B?.fallbackOn?.length?B.fallbackOn:v1}function JB(B,Z){return G6(Z).includes(B)}function z6(B){let Z=B?.quota?.minimumRemaining||{};return{primary:Z.primary??Z["5h"]??E0.primary,secondary:Z.secondary??Z["1w"]??E0.secondary}}function J0(B){return B?.quota?.enabled!==!1}function g(B){return B?.quota?.failClosedOnUnknownQuota??p1}function v0(B,Z,$=Date.now()){if(!J0(Z))return!0;if(!B)return!g(Z);let W=z6(Z),X=g(Z);for(let G of["primary","secondary"]){let z=B[G];if(!z)continue;if(n(z,$)){if(X)return!1;continue}if(z.remainingPercent<W[G])return!1}return!0}function p0(B){return B?.killswitch?.enabled===!0}function y0(B){return{primary:B?.primary??B?.["5h"]??K0.primary,secondary:B?.secondary??B?.["1w"]??K0.secondary}}function l0(B,Z){if(!B?.killswitch)return K0;if(Z&&B.killswitch.accounts?.[Z])return y0(B.killswitch.accounts[Z]);return y0(B.killswitch.main)}function jB(B,Z,$,W=Date.now()){if(!p0(Z))return!0;if(!B)return!g(Z);let X=l0(Z,$),G=g(Z);for(let z of["primary","secondary"]){let K=B[z];if(!K)continue;if(n(K,W)){if(G)return!1;continue}if(K.remainingPercent<X[z])return!1}return!0}function HB(B,Z,$,W){if(!p0(W))return 300;let X=[],G=[{accountId:void 0,quota:B},...Z];for(let{accountId:K,quota:Y}of G){if(!Y)continue;let J=l0(W,K),j=[],Q=!1;for(let y of["primary","secondary"]){let V=Y[y];if(!V||n(V,$))continue;if(V.remainingPercent>=J[y])continue;let F=V.resetsAt?Date.parse(V.resetsAt):NaN;if(!Number.isFinite(F)||F<=$){Q=!0;break}j.push(F)}if(Q||j.length===0)continue;X.push(Math.max(...j))}if(X.length===0)return 300;let z=Math.min(...X);return Math.max(1,Math.ceil((z-$)/1000))}function K6(B){return typeof B.version==="number"&&Array.isArray(B.accounts)}async function QB(B,Z=A()){let $=t(Z),W=await d(Z);try{let X=await d($);try{let G=await R(Z);if(G.exists&&H(G.value)){if(K6(G.value))return}if(!B)return;let z={version:1,main:{type:"opencode",provider:"openai"},accounts:[]};if(B.access){let J=s({id_token:"",access_token:B.access,refresh_token:B.refresh});if(J)z.mainAccountId=J}let Y={...G.exists&&H(G.value)?G.value:{},...k0(z)};await f(Z,Y),await f($,h0(z))}finally{await X.release()}}finally{await W.release()}}function Y6(B){return B?.refresh?.enabled!==!1}function J6(B){return(B?.refresh?.refreshBeforeExpiryMinutes??240)*60000}function d0(B){return Math.floor(Math.random()*(B+1))}function S(B,Z,$){return!B.access||!B.expires||B.expires-$<=J6(Z)}function j6(B,Z){return Boolean(B.access&&typeof B.expires==="number"&&B.expires>Z)}function H6(B,Z){return Boolean(B.mainAccountId&&Z.accountId&&Z.accountId===B.mainAccountId)}function N(B,Z){let $=B.accounts.findIndex((W)=>W.id===Z.id);if($!==-1)B.accounts[$]=Z}function l(B){return B instanceof Error?B.message:String(B)}function j0(B,Z,$){B.lastRefreshError=N0({error:Z,now:$,refreshToken:B.refresh,previous:B.lastRefreshError})}function z0(B,Z,$){if(B.lastQuotaRefreshError=I({error:Z,now:$,previous:B.lastQuotaRefreshError}),Z?.isRefreshError===!0)j0(B,Z,$)}function Q6(B){return`fallback-oauth-refresh-${y1("sha256").update(B).digest("base64url").slice(0,16)}`}function x0(B,Z){if(!B)return!1;if(!B.resetsAt)return!0;let $=Date.parse(B.resetsAt);return Number.isFinite($)?$>Z:!0}function N6(B,Z){if(!B)return!1;return x0(B.primary,Z)||x0(B.secondary,Z)}function P6(B,Z,$){if(!B)return!1;let W=Z?.quota?.checkIntervalMinutes??5,X=Math.max(1,W)*60000,G=Math.max(B.primary?.checkedAt??0,B.secondary?.checkedAt??0);return $-G<X}function T0(B,Z,$){return!P6(B.quota,Z,$)}function V6(B,Z,$,W){return B0($)&&v0(B.quota,Z,W)&&N6(B.quota,W)}function L6(B){let Z=B?.quota?.checkIntervalMinutes??5;return Math.max(1,Z)*60000}var F6=60000,O6=60000,b6=600000,D6=1e4,U6=100;var NB=globalThis.setTimeout.bind(globalThis),PB=globalThis.clearTimeout.bind(globalThis);class M6{now;fetchImpl;configPath;refreshPromises=new Map;refreshTimer=null;quotaTimer=null;quotaManager;onFallbackStorageChanged;options;constructor(B={}){this.options=B,this.now=B.now??Date.now,this.fetchImpl=B.fetchImpl??fetch,this.configPath=B.configPath??A(),this.quotaManager=B.quotaManager??null,this.onFallbackStorageChanged=B.onFallbackStorageChanged}seedFallbackQuota(B,Z){if(!this.quotaManager)return;if(!B.quota)return;let $=Math.max(B.quota.primary?.checkedAt??0,B.quota.secondary?.checkedAt??0);if($<=0)return;let W=this.quotaManager.getFallback(B.id,B.access);if(W&&W.checkedAt>=$)return;this.quotaManager.setFallback(B.id,{quota:B.quota,refreshAfter:$+L6(Z),checkedAt:$},B.access,!1,B.accountId)}async load(){return c1(this.configPath)}async save(B,Z){await X6(B,this.configPath,{accounts:Z??!0})}startBackgroundRefresh(){let B=async()=>{if(await this.refreshDueAccounts(),this.options.fetchQuotaFn)await this.refreshQuotaForDueAccounts()};if(B().catch(()=>{}),!this.refreshTimer){if(this.refreshTimer=setInterval(()=>{B().catch(()=>{})},F6+d0(O6)),"unref"in this.refreshTimer)this.refreshTimer.unref()}}stopBackgroundRefresh(){if(this.refreshTimer)clearInterval(this.refreshTimer);if(this.quotaTimer)clearInterval(this.quotaTimer);this.refreshTimer=null,this.quotaTimer=null}async getUsableFallbackAccounts(B){let Z=B!==void 0?B:await this.load();if(!Z)return[];let $=[],W=!1;for(let X of Z.accounts){if(X.enabled===!1||!b(X))continue;if(H6(Z,X))continue;let G=!1,z=X;try{if(S(z,Z,this.now())){let K=z.lastRefreshError;if(K&&U(K,z.refresh,this.now()))throw G=!0,Error(i(K,this.now()));try{z=await this.refreshAccount(z,Z),W=!0}catch(Y){if(g1(Y))continue;G=!0;let J=Z.accounts.find((j)=>j.id===X.id&&b(j));if(J&&!U(J.lastRefreshError,J.refresh,this.now()))j0(J,Y,this.now()),N(Z,J),W=!0;throw Y}}if(this.seedFallbackQuota(z,Z),this.accountPassesQuotaPolicy(this.quotaPolicyAccount(z),Z))$.push(z)}catch(K){let Y=j6(z,this.now());if(G){if(!Y)continue}else if(!Y)continue;if(V6(z,Z,K,this.now()))E.debug("fallback quota using cached quota after refresh error",{pid:process.pid,accountId:z.id,error:l(K)}),$.push(z);else if(!g(Z))$.push(z)}}if(W)try{await this.save(Z)}catch(X){_0.warn("fallback selection bookkeeping not persisted",{pid:process.pid,error:l(X)})}return $}async markUsed(B){try{let Z=await this.load();if(!Z)return;let $=Z.accounts.find((W)=>W.id===B.id);if(!$)return;$.lastUsed=this.now(),await this.save(Z,[$.id])}catch(Z){_0.warn("lastUsed stamp not persisted",{pid:process.pid,accountId:B.id,error:l(Z)})}}accountPassesQuotaPolicy(B,Z){return v0(B.quota,Z,this.now())}quotaPolicyAccount(B){if(!this.quotaManager)return B;let Z=this.quotaManager.getFallback(B.id,B.access)?.quota;return Z?{...B,quota:Z}:B}async refreshDueAccounts(){let B=await this.load();if(!B||!Y6(B))return;let Z=!1;for(let $ of B.accounts){if($.enabled===!1||!b($))continue;if(!S($,B,this.now()))continue;if(U($.lastRefreshError,$.refresh,this.now()))continue;try{E.debug("fallback oauth background due",{pid:process.pid,accountId:$.id,expiresInMs:$.expires?$.expires-this.now():void 0}),await this.refreshAccount($,B),Z=!0}catch(W){E.warn("fallback oauth background failed",{pid:process.pid,accountId:$.id,error:l(W)}),j0($,W,this.now()),N(B,$),Z=!0}}if(Z)await this.save(B)}async refreshQuotaForDueAccounts(){let B=await this.load();if(!B||!J0(B))return;if(!this.options.fetchQuotaFn)return;let Z=!1;for(let $ of B.accounts){if($.enabled===!1||!b($))continue;let W=$;try{if(S(W,B,this.now())){if(U(W.lastRefreshError,W.refresh,this.now()))continue;W=await this.refreshAccount(W,B),Z=!0}if(x(W.lastQuotaRefreshError,this.now()))continue;if(this.seedFallbackQuota(W,B),!(this.quotaManager?this.quotaManager.isFallbackStale(W.id,W.access):T0(W,B,this.now())))continue;await this.refreshAccountQuota(W,B),Z=!0}catch(X){z0($,X,this.now()),N(B,$),Z=!0}}if(Z)await this.save(B),this.onFallbackStorageChanged?.()}async refreshQuotaForAllAccounts(B={}){let Z=await this.load(),$=[];if(!Z||!J0(Z))return{storage:Z,errors:$};if(!this.options.fetchQuotaFn)return{storage:Z,errors:$};let W=B.force??!1,X=!1;for(let G of Z.accounts){if(G.enabled===!1||!b(G))continue;let z=G;try{if(S(z,Z,this.now())){let K=z.lastRefreshError;if(K&&U(K,z.refresh,this.now()))throw Error(i(K,this.now()));z=await this.refreshAccount(z,Z),X=!0}if(!W&&!T0(z,Z,this.now())){if(z.lastQuotaRefreshError)z.lastQuotaRefreshError=void 0,N(Z,z),X=!0;continue}await this.refreshAccountQuota(z,Z),X=!0}catch(K){z0(G,K,this.now()),N(Z,G),X=!0,$.push({accountId:G.id,message:l(K)})}}if(X)await this.save(Z);return{storage:Z,errors:$}}async refreshAccount(B,Z,$={}){let W=this.refreshPromises.get(B.id);if(W){let z=await W;return N(Z,z),z}let X=this.refreshAccountNow(B,Z,$).finally(()=>{this.refreshPromises.delete(B.id)});this.refreshPromises.set(B.id,X);let G=await X;return N(Z,G),G}async waitForConcurrentFallbackRefresh(B,Z,$,W){let X=Date.now()+D6;while(Date.now()<X){await new Promise((J)=>setTimeout(J,U6));let G=await this.load(),z=G?.accounts.find((J)=>J.id===B.id&&b(J));if(!z)continue;if((z.access!==$.access||z.refresh!==$.refresh||(z.expires??0)>($.expires??0)+60000)&&(W.force||!S(z,G,this.now())))return N(Z,z),E.debug("fallback oauth joined concurrent refresh",{pid:process.pid,accountId:z.id,expiresInMs:z.expires?z.expires-this.now():void 0}),z;let Y=z.lastRefreshError;if(Y&&U(Y,z.refresh,this.now()))throw N(Z,z),Error(i(Y,this.now()))}return null}async refreshAccountNow(B,Z,$){let W=await this.load(),X=W?.accounts.find((K)=>K.id===B.id&&b(K));if(X&&!$.force&&!S(X,W,this.now()))return N(Z,X),X;let G=X??B,z=await T({name:Q6(G.id),ttlMs:b6,path:this.configPath,now:this.now,renew:!0});if(!z){E.debug("fallback oauth refresh skipped file lock",{pid:process.pid,accountId:G.id});let K=await this.waitForConcurrentFallbackRefresh(B,Z,G,$);if(K)return K;throw Error("Fallback OAuth refresh is already in progress")}try{if(W=await this.load(),X=W?.accounts.find((j)=>j.id===B.id&&b(j)),X&&!$.force&&!S(X,W,this.now()))return N(Z,X),X;if(!X)throw new o(B.id);G=X;let K=this.options.refreshFn??(async()=>{throw Error("No refreshFn injected into FallbackAccountManager")});E.debug("fallback oauth refresh request start",{pid:process.pid,accountId:G.id,force:$.force===!0,expiresInMs:G.expires?G.expires-this.now():void 0});let Y=await K({refreshToken:G.refresh,fetchImpl:this.fetchImpl,now:this.now});if(G.access=Y.access,G.refresh=Y.refresh,G.expires=Y.expires,G.lastRefreshedAt=Y.expires-Y.expiresIn*1000,G.lastRefreshError=void 0,N(Z,G),await this.save(Z),!(await this.load())?.accounts.some((j)=>j.id===B.id))throw new o(B.id);return E.debug("fallback oauth refresh succeeded",{pid:process.pid,accountId:G.id,expiresInMs:G.expires?G.expires-this.now():void 0}),G}finally{await z.release()}}async refreshAccountQuota(B,Z){let $=B;if(!$.access)throw Error(`Fallback account ${B.id} has no access token`);if(!this.options.fetchQuotaFn)throw Error("No fetchQuotaFn injected — wham/usage supplement is disabled");let W=this.options.fetchQuotaFn??(async()=>{throw Error("No fetchQuotaFn injected — wham/usage supplement is disabled")}),X=(G)=>this.quotaManager?this.quotaManager.refreshFallback($.id,G):W({accessToken:G,fetchImpl:this.fetchImpl,now:this.now});try{if(!$.access)throw Error(`Fallback account ${B.id} has no access token`);let G=await X($.access);$.quota=G,$.lastQuotaRefreshError=void 0,N(Z,$),await this.save(Z)}catch(G){throw z0(B,G,this.now()),N(Z,B),G}}}
89
+ export{Q0 as h,N0 as i,U as j,i as k,F0 as l,b0 as m,Z1 as n,V0 as o,L0 as p,s as q,$0 as r,W1 as s,C as t,K1 as u,Y1 as v,J1 as w,j1 as x,y6 as y,x6 as z,D0 as A,m6 as B,k6 as C,g6 as D,O as E,A as F,b as G,zB as H,K0 as I,d1 as J,c1 as K,YB as L,JB as M,p0 as N,l0 as O,jB as P,HB as Q,QB as R,M6 as S};