@cortexkit/opencode-openai-auth 0.3.2 → 0.3.4

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.
Files changed (41) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/commands.d.ts +3 -1
  3. package/dist/core/accounts.d.ts +6 -0
  4. package/dist/core/cachekeep.d.ts +16 -0
  5. package/dist/core/oauth.d.ts +1 -1
  6. package/dist/core/quota-manager.d.ts +33 -1
  7. package/dist/{index-gwmhhrrc.js → index-1xmasv0x.js} +1 -1
  8. package/dist/{index-899gpxdd.js → index-5s67238f.js} +1 -1
  9. package/dist/{index-53j7bdev.js → index-e7m7qbne.js} +1 -1
  10. package/dist/{index-7w467pz8.js → index-k5zwf3j1.js} +1 -1
  11. package/dist/{index-qp2nja20.js → index-mrg2fms9.js} +2 -2
  12. package/dist/{index-jn8xd8fy.js → index-qdh7pt5a.js} +1 -1
  13. package/dist/index-y8d01tgn.js +92 -0
  14. package/dist/index.js +68 -62
  15. package/dist/{quota-normalize-dxftsgep.js → quota-normalize-dfdpwwm3.js} +1 -1
  16. package/dist/rpc/port-file.js +1 -1
  17. package/dist/rpc/rpc-client.js +1 -1
  18. package/dist/rpc/rpc-dir.js +1 -1
  19. package/dist/sidebar-state.js +1 -1
  20. package/dist/{token-28n5g707.js → token-01dcf92a.js} +1 -1
  21. package/dist/token-util-rkqd82q9.js +1 -0
  22. package/dist/tui-preferences.js +1 -1
  23. package/dist/version.d.ts +1 -1
  24. package/dist/ws-pool.d.ts +11 -0
  25. package/dist/ws.d.ts +1 -1
  26. package/package.json +6 -4
  27. package/src/tui/command-dialogs.tsx +77 -0
  28. package/src/tui/entry.mjs +37 -0
  29. package/src/tui-compiled/logger.ts +161 -0
  30. package/src/tui-compiled/rpc/port-file.ts +70 -0
  31. package/src/tui-compiled/rpc/protocol.ts +31 -0
  32. package/src/tui-compiled/rpc/rpc-client.ts +78 -0
  33. package/src/tui-compiled/rpc/rpc-dir.ts +22 -0
  34. package/src/tui-compiled/sidebar-state.ts +316 -0
  35. package/src/tui-compiled/tui/command-dialogs.tsx +640 -0
  36. package/src/tui-compiled/tui-preferences.ts +328 -0
  37. package/src/tui-compiled/tui.tsx +1041 -0
  38. package/src/tui-compiled/util/open-url.ts +30 -0
  39. package/src/tui.tsx +16 -13
  40. package/dist/index-z21wp7ac.js +0 -92
  41. package/dist/token-util-gmr2b13z.js +0 -1
@@ -0,0 +1,30 @@
1
+ import { execFileSync as defaultExecFileSync } from 'node:child_process'
2
+
3
+ type BrowserExec = (
4
+ file: string,
5
+ args: string[],
6
+ options: { stdio: 'ignore'; timeout: number },
7
+ ) => unknown
8
+
9
+ export function openUrl(
10
+ url: string,
11
+ platform: NodeJS.Platform = process.platform,
12
+ execFileSync: BrowserExec = defaultExecFileSync,
13
+ ) {
14
+ try {
15
+ if (platform === 'win32') {
16
+ execFileSync('cmd', ['/c', 'start', '', url], {
17
+ stdio: 'ignore',
18
+ timeout: 3000,
19
+ })
20
+ return
21
+ }
22
+
23
+ execFileSync(platform === 'darwin' ? 'open' : 'xdg-open', [url], {
24
+ stdio: 'ignore',
25
+ timeout: 3000,
26
+ })
27
+ } catch {
28
+ // Browser launch is best effort; the printed URL remains the fallback.
29
+ }
30
+ }
package/src/tui.tsx CHANGED
@@ -50,21 +50,24 @@ const log = createLogger('rpc-tui')
50
50
 
51
51
  const ID = 'cortexkit.openai-auth'
52
52
 
53
- // Plugin version for the header (mirrors the Magic Context / AFT convention).
54
- // Read at runtime from package.json relative to this module — NOT a TS JSON
55
- // import, which would break the declaration build (package.json is outside
56
- // rootDir). tui.tsx ships as source (package.json exports["./tui"] →
57
- // "./src/tui.tsx") and is never compiled into a deeper dist tree, so `..` from
58
- // src/ is always the package root. Empty string on any failure → header shows
59
- // the badge with no version.
53
+ // Read package metadata from either the raw src/ entry or its generated
54
+ // src/tui-compiled/ counterpart. Avoid a JSON import because package.json sits
55
+ // outside the declaration build's rootDir.
60
56
  const PLUGIN_VERSION: string = (() => {
61
- try {
62
- const here = dirname(fileURLToPath(import.meta.url))
63
- const raw = readFileSync(join(here, '..', 'package.json'), 'utf8')
64
- return (JSON.parse(raw) as { version?: string }).version ?? ''
65
- } catch {
66
- return ''
57
+ const here = dirname(fileURLToPath(import.meta.url))
58
+ for (const packageFile of [
59
+ join(here, '..', 'package.json'),
60
+ join(here, '..', '..', 'package.json'),
61
+ ]) {
62
+ try {
63
+ const raw = readFileSync(packageFile, 'utf8')
64
+ const version = (JSON.parse(raw) as { version?: string }).version
65
+ if (version) return version
66
+ } catch {
67
+ // Try the path for the other TUI entry layout.
68
+ }
67
69
  }
70
+ return ''
68
71
  })()
69
72
 
70
73
  // biome-ignore lint/suspicious/noExplicitAny: opentui border prop not typed in plugin tui surface
@@ -1,92 +0,0 @@
1
- import{ba as d0}from"./index-jn8xd8fy.js";import{ja as M2}from"./index-53j7bdev.js";import{createHash as t0}from"node:crypto";var e0=86400000,B2=300000,Z2=3600000,$2=60000,z2=900000,W2=300000;function W0(B){return B instanceof Error?B.message:String(B)}function X2(B){let Z=B?.status;if(typeof Z==="number")return Z===429||Z>=500;if(!(B instanceof Error))return!1;return B.message.includes("fetch failed")||"code"in B&&(B.code==="ECONNRESET"||B.code==="ECONNREFUSED"||B.code==="ETIMEDOUT"||B.code==="UND_ERR_CONNECT_TIMEOUT")}function X0(B){let Z=W0(B),$=B?.status;if(typeof $==="number")return $===429||$>=500;if(!(B instanceof Error))return!1;let z=B.code;return Z.includes("fetch failed")||z==="ECONNRESET"||z==="ECONNREFUSED"||z==="ETIMEDOUT"||z==="UND_ERR_CONNECT_TIMEOUT"}function q0(B){return t0("sha256").update(B).digest("hex")}function C0(B){let Z=q0(B.refreshToken),z=(B.previous?.tokenHash===Z?B.previous.retryCount??0:0)+1,X,W=B.error?.retryAfter;if(typeof W==="number"&&W>0)X=W*1000;else if(X2(B.error))X=Math.min(Z2,B2*2**Math.min(z-1,6));else X=e0;return{message:W0(B.error),checkedAt:B.now,nextRetryAt:B.now+X,retryCount:z,tokenHash:Z}}function q(B,Z,$){if(!B?.nextRetryAt||B.nextRetryAt<=$)return!1;if(!Z)return!0;return B.tokenHash===q0(Z)}function n(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 v(B){let $=(B.previous?.retryCount??0)+1,z,X=B.error?.retryAfter;if(typeof X==="number"&&X>0)z=X*1000;else if(X0(B.error))z=Math.min(z2,$2*2**Math.min($-1,6));else z=W2;return{message:W0(B.error),checkedAt:B.now,nextRetryAt:B.now+z,retryCount:$}}function A(B,Z){if(!B?.nextRetryAt||B.nextRetryAt<=Z)return!1;return!0}function G0(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 z=Math.ceil(($-Date.now())/1000);return z>0?z:void 0}return}import{createServer as G2}from"node:http";import{setTimeout as K2}from"node:timers/promises";var R0="0.3.2";var o="app_EMoamEEZ73f0CkXaXp7hrann",S="https://auth.openai.com",p=1455,Y2=3000,E0=`cortexkit-opencode-openai-auth/${R0}`,I0="main",J2=`"${I0}" is a reserved account id; choose a different label.`;function x0(B){let Z=new Uint8Array(B),$=String.fromCharCode(...Z);return btoa($).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}async function H2(){let Z=Array.from(crypto.getRandomValues(new Uint8Array(43))).map((z)=>"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"[z%66]).join(""),$=x0(await crypto.subtle.digest("SHA-256",new TextEncoder().encode(Z)));return{verifier:Z,challenge:$}}function S0(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 y0(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 z=B.organizations;if(Array.isArray(z)){let X=z[0];if(X&&typeof X==="object"&&!Array.isArray(X)&&typeof X.id==="string"&&X.id.length>0)return X.id}return}function a(B){if(B.id_token){let Z=S0(B.id_token),$=Z&&y0(Z);if($)return $}if(B.access_token){let Z=S0(B.access_token);return Z?y0(Z):void 0}return}function j2(B){return typeof B==="string"&&B.toLowerCase()===I0}function K0(B){if(j2(B))throw Error(J2);return B}function Q2(B,Z,$){let z=new URLSearchParams({response_type:"code",client_id:o,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`${S}/oauth/authorize?${z.toString()}`}async function V2(B,Z,$){let z=await fetch(`${S}/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:o,code_verifier:$.verifier}).toString()});if(!z.ok)throw Error(`Token exchange failed: ${z.status}`);return await z.json()}function N2(B){return B.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;")}var L2=`<!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>`,c=(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">${N2(B)}</div>
85
- </div>
86
- </body>
87
- </html>`,d,l,O=new Map;function C(){return O.size}function y(B){if(O.delete(B),C()===0)R()}async function b2(){let B=`http://localhost:${p}/auth/callback`;if(d)return{port:p,redirectUri:B};if(l)return l;l=(async()=>{let Z=G2(($,z)=>{let X=new URL($.url||"/",`http://127.0.0.1:${p}`);if(X.pathname==="/auth/callback"){let W=X.searchParams.get("code"),G=X.searchParams.get("state"),K=X.searchParams.get("error"),Y=X.searchParams.get("error_description");if(K){let H=Y||K;if(G){let V=O.get(G);if(V)V.reject(Error(H)),y(G);else if(C()===0)R()}else if(C()===0)R();z.writeHead(200,{"Content-Type":"text/html"}),z.end(c(H));return}if(!G){if(C()===0)R();z.writeHead(400,{"Content-Type":"text/html"}),z.end(c("Missing state parameter"));return}if(!W){if(C()===0)R();z.writeHead(400,{"Content-Type":"text/html"}),z.end(c("Missing authorization code"));return}let J=O.get(G);if(!J){if(C()===0)R();z.writeHead(400,{"Content-Type":"text/html"}),z.end(c("Unknown state — authorization may have expired"));return}y(G),V2(W,B,J.pkce).then((H)=>J.resolve(H)).catch((H)=>J.reject(H)),z.writeHead(200,{"Content-Type":"text/html"}),z.end(L2);return}if(X.pathname==="/cancel"){let W=X.searchParams.get("state"),G=W?[W]:[...O.keys()];z.writeHead(200),z.end("Login cancelled");for(let K of G){let Y=O.get(K);if(!Y)continue;Y.reject(Error("Login cancelled")),y(K)}if(C()===0)R();return}z.writeHead(404),z.end("Not found")});return await new Promise(($,z)=>{Z.once("error",z),Z.listen(p,"127.0.0.1",()=>{Z.off("error",z),$()})}),d=Z,{port:p,redirectUri:B}})();try{return await l}finally{l=void 0}}function R(){if(d){let B=d;d=void 0,B.close(()=>{}),B.closeIdleConnections?.(),setTimeout(()=>{B.closeAllConnections?.()},25).unref?.()}}function D2(B,Z,$=300000,z){return new Promise((X,W)=>{if(z?.aborted){y(Z),W(Error("Login cancelled"));return}let G=setTimeout(()=>{if(O.get(Z))y(Z),W(Error("OAuth callback timeout — authorization took too long"))},$),K=()=>{if(clearTimeout(G),O.get(Z))y(Z),W(Error("Login cancelled"))};if(z)z.addEventListener("abort",K);O.set(Z,{pkce:B,state:Z,resolve:(Y)=>{if(clearTimeout(G),z)z.removeEventListener("abort",K);X(Y)},reject:(Y)=>{if(clearTimeout(G),z)z.removeEventListener("abort",K);W(Y)}})})}async function F2(){let B=await fetch(`${S}/api/accounts/deviceauth/usercode`,{method:"POST",headers:{"Content-Type":"application/json","User-Agent":E0},body:JSON.stringify({client_id:o})});if(!B.ok)throw Error("Failed to initiate device authorization");let Z=await B.json();return{deviceData:Z,url:`${S}/codex/device`,instructions:`Enter code: ${Z.user_code}`}}async function O2(B,Z){let $=Math.max(parseInt(B.interval,10)||5,1)*1000,z=typeof B.expires_in==="number"?B.expires_in:parseInt(B.expires_in||"",10),X=!Number.isNaN(z)&&z>0?z*1000:900000,W=Date.now();while(!0){if(Z?.aborted)throw Error("Device authorization cancelled");if(Date.now()-W>X)throw Error("Device authorization expired");let G=await fetch(`${S}/api/accounts/deviceauth/token`,{method:"POST",headers:{"Content-Type":"application/json","User-Agent":E0},body:JSON.stringify({device_auth_id:B.device_auth_id,user_code:B.user_code})});if(G.ok){let J=await G.json(),H=await fetch(`${S}/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:`${S}/deviceauth/callback`,client_id:o,code_verifier:J.code_verifier}).toString()});if(!H.ok)throw Error(`Token exchange failed: ${H.status}`);return await H.json()}if(G.status!==403&&G.status!==404)throw Error(`Device authorization failed: ${G.status}`);let K=X-(Date.now()-W);if(K<=0)throw Error("Device authorization expired");let Y=Math.min($+Y2,K);try{await K2(Y,void 0,{signal:Z})}catch(J){if(J instanceof Error&&J.name==="AbortError")throw Error("Device authorization cancelled");throw J}}}function c3(B,Z){let $=-1;if(Z.accountId)$=B.findIndex((z)=>z.accountId&&z.accountId===Z.accountId);if($<0)$=B.findIndex((z)=>z.id===Z.id);if($<0&&Z.label&&!Z.accountId)$=B.findIndex((z)=>z.label===Z.label);if($>=0)return B[$]={...B[$],...Z,addedAt:B[$]?.addedAt??Z.addedAt},$;return B.push(Z),B.length-1}async function a3(B={}){let{label:Z,headless:$=!1,signal:z}=B;if(K0(Z),$){let{deviceData:J,url:H,instructions:V}=await F2(),D=(async()=>{let Q=await O2(J,z),N=Date.now(),_=a(Q);return{id:K0(Z||_||crypto.randomUUID()),label:Z||void 0,type:"oauth",access:Q.access_token,refresh:Q.refresh_token,expires:N+(Q.expires_in??3600)*1000,enabled:!0,addedAt:N,lastUsed:N,lastRefreshedAt:N,accountId:_}})();return{url:H,instructions:V,completion:D}}let{redirectUri:X}=await b2(),W=await H2(),G=x0(crypto.getRandomValues(new Uint8Array(32)).buffer),K=Q2(X,W,G),Y=(async()=>{try{if(z?.aborted)throw Error("Login cancelled");let J=await D2(W,G,void 0,z),H=Date.now(),V=a(J);return{id:K0(Z||V||crypto.randomUUID()),label:Z||void 0,type:"oauth",access:J.access_token,refresh:J.refresh_token,expires:H+(J.expires_in??3600)*1000,enabled:!0,addedAt:H,lastUsed:H,lastRefreshedAt:H,accountId:V}}finally{y(G)}})();return{url:K,instructions:"Complete authorization in your browser. This window will close automatically.",completion:Y}}var t="primary",e="secondary",P2="app_EMoamEEZ73f0CkXaXp7hrann",U2="https://auth.openai.com";async function e3(B){let Z=await B.fetchImpl(`${U2}/oauth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"refresh_token",refresh_token:B.refreshToken,client_id:P2}).toString()});if(!Z.ok){let z=G0(Z.headers.get("retry-after"));throw Object.assign(Error(`Token refresh failed: ${Z.status}`),{status:Z.status,retryAfter:z,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 _2="https://chatgpt.com/backend-api/wham/usage";async function BB(B){let Z=await B.fetchImpl(_2,{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(!Z.ok){let z=G0(Z.headers.get("retry-after"));throw Object.assign(Error(`wham usage check failed: ${Z.status}`),{status:Z.status,retryAfter:z})}let{normalizeWham:$}=await import("./quota-normalize-dxftsgep.js");return $(await Z.json())}import{randomUUID as Y0}from"node:crypto";import{mkdir as q2,readFile as J0,rename as C2,rm as g,stat as A0,writeFile as H0}from"node:fs/promises";import{homedir as R2}from"node:os";import{dirname as GB,join as i}from"node:path";var S2=globalThis.setTimeout.bind(globalThis),y2=globalThis.clearTimeout.bind(globalThis),E2="openai-auth.json";function I2(){if(process.env.OPENCODE_CONFIG_DIR?.trim())return process.env.OPENCODE_CONFIG_DIR.trim();return i(process.env.XDG_CONFIG_HOME||i(R2(),".config"),"opencode")}function x2(B){let Z=B?.code;return Z==="ENOENT"||Z==="EINVAL"||Z==="ENOTDIR"}function A2(){return process.env.OPENCODE_OPENAI_AUTH_FILE?.trim()||i(I2(),E2)}async function T(B){let $=`${B.path??A2()}.${B.name}.lock`,z=i($,"owner.json"),X=Y0(),W=B.now??Date.now,G=null,K=!1;async function Y(){try{return JSON.parse(await J0($,"utf8"))}catch(Q){if(Q.code!=="EISDIR")throw Q;return JSON.parse(await J0(z,"utf8"))}}async function J(){await H0($,`${JSON.stringify({ownerId:X,expiresAt:W()+B.ttlMs})}
88
- `,{encoding:"utf8",mode:384})}async function H(){try{return await H0($,`${JSON.stringify({ownerId:X,expiresAt:W()+B.ttlMs})}
89
- `,{encoding:"utf8",mode:384,flag:"wx"}),!0}catch(Q){let N=Q.code;if(N==="EEXIST"||N==="EISDIR")return!1;throw Q}}function V(){if(!B.renew||K)return;let Q=B.renewIntervalMs??Math.max(1000,Math.floor(B.ttlMs/3));if(G=S2(()=>{(async()=>{try{let N=await Y(),_=W();if(K||N?.ownerId!==X||Number(N?.expiresAt)<=_)return;await J(),V()}catch{}})()},Q),"unref"in G)G.unref()}let D=await H();if(!D){let Q=`${$}.evicting`,N=i(Q,"owner.json"),_=Y0(),p3=5000,l3=8;async function r(){await new Promise((F)=>setTimeout(F,Math.floor(Math.random()*4)))}async function O0(){try{let F=await Y();return Number(F?.expiresAt)>W()}catch{try{return(await A0($)).mtimeMs+B.ttlMs>W()}catch{return!1}}}async function k(){try{return JSON.parse(await J0(N,"utf8"))?.ownerId===_}catch{return!1}}async function o0(){await q2(Q);try{await H0(N,`${JSON.stringify({ownerId:_,createdAt:W()})}
90
- `,{encoding:"utf8",mode:384,flag:"wx"})}catch(F){if(x2(F))return!1;throw await M0(),F}return await B.onStep?.("eviction-marker-acquired"),!0}async function M0(){if(await k())await g(Q,{recursive:!0,force:!0}).catch(()=>{})}for(let F=0;F<8;F++){if(D=await H(),D)break;if(await O0())return null;try{if(!await o0()){await r();continue}}catch(P0){if(P0.code!=="EEXIST")throw P0;let U0;try{U0=await A0(Q)}catch(h){if(h.code==="ENOENT"){await r();continue}throw h}if(U0.mtimeMs+5000>W())return null;await B.onStep?.("stale-marker-stat");let _0=`${Q}.${Y0()}`;try{await C2(Q,_0)}catch(h){if(h.code==="ENOENT"){await r();continue}throw h}await B.onStep?.("stale-marker-claimed"),await g(_0,{recursive:!0,force:!0}).catch(()=>{}),await r();continue}try{if(await O0())return null;if(!await k())return null;if(await B.onStep?.("stale-lock-confirmed"),!await k())return null;if(await g($,{recursive:!0,force:!0}).catch(()=>{}),!await k())return null;if(D=await H(),!D)return null;if(!await k())return await g($,{recursive:!0,force:!0}).catch(()=>{}),D=!1,null;break}finally{await M0()}}}if(!D)return null;return V(),{release:async()=>{if(K=!0,G)y2(G),G=null;try{if((await Y())?.ownerId!==X)return}catch{return}await g($,{recursive:!0,force:!0}).catch(()=>{})}}}import{createHash as T2}from"node:crypto";var f2=globalThis.setTimeout,m2=5;function f(B){let Z=B?.quota?.checkIntervalMinutes??m2;return Math.max(1,Z)*60000}function w2(B){return B?.quota?.enabled!==!1}function j0(B,Z){if(!B?.resetsAt)return!1;let $=Date.parse(B.resetsAt);return Number.isFinite($)&&$<=Z}function k2(B){let Z=B?.quota?.minimumRemaining||{};return{primary:Z.primary??Z["5h"]??15,secondary:Z.secondary??Z["1w"]??10}}function T0(B,Z,$){if(!w2(Z))return $+f(Z);let z=k2(Z),X=[];for(let W of["primary","secondary"]){let G=B?.[W];if(!G)return $+f(Z);if(G.remainingPercent>=z[W])continue;let K=G.resetsAt?Date.parse(G.resetsAt):Number.NaN;if(!Number.isFinite(K)||K<=$)return $+f(Z);X.push(K)}if(!X.length)return $+f(Z);return Math.min(...X)+60000}function h2(B){if(!B?.quota?.mainQuota||!B.quota.mainQuotaCheckedAt)return null;return{quota:B.quota.mainQuota,checkedAt:B.quota.mainQuotaCheckedAt,tokenFingerprint:B.quota.mainQuotaToken}}function v2(B){let Z=B?.quota?.refreshEveryNRequests;return typeof Z==="number"&&Number.isFinite(Z)&&Z>0?Math.floor(Z):0}function b(B){return T2("sha256").update(B).digest("hex").slice(0,16)}class M{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;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!==b(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)!==b(Z))return this.fallbacks.delete(B),this.fallbackTokenFps.delete(B),null;return $}getAllFallbacks(){return this.fallbacks}setMain(B,Z,$){if(!f0(Z.quota))return;if(this.mainTokenFp=b(B),$)this.mainQuotaAccountId=$;this.main=Z}peekMainForPolicy(B){if(B&&this.mainQuotaAccountId&&this.mainQuotaAccountId!==B)return null;return this.main}peekFallbackForPolicy(B){return this.fallbacks.get(B)??null}setFallback(B,Z,$){if(!f0(Z.quota))return;if(this.fallbacks.set(B,Z),$)this.fallbackTokenFps.set(B,b($));else this.fallbackTokenFps.delete(B)}async refreshMain(B){let Z=b(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 $=M.fallbackInflightKey(B,Z),z=this.inflightFallbacks.get($);if(z)return z;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 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 z=this.getFallback($.id,$.access);if(z&&Z<z.refreshAfter)continue;try{await this.refreshFallback($.id,$.access)}catch{}}}refreshMainInBackground(B){let Z=b(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=v2(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 $=h2(B);if(!$)return;let z=Z?b(Z):null;if(z&&$.tokenFingerprint&&$.tokenFingerprint!==z)return;let X={quota:$.quota,refreshAfter:T0($.quota,B,$.checkedAt),checkedAt:$.checkedAt};if(this.main&&this.main.checkedAt>=X.checkedAt&&(!z||!this.mainTokenFp||this.mainTokenFp===z))return;if(this.main=X,this.mainTokenFp=$.tokenFingerprint??null,B?.mainAccountId)this.mainQuotaAccountId=B.mainAccountId}seedMainBackoffFromStorage(B){let Z=B?.quota?.mainLastQuotaApiError;this.mainLastApiError=Z&&A(Z,this.now())?Z:void 0}seedFallbacksFromAccounts(B){let Z=f(this.storage);for(let $ of B){if($.enabled===!1)continue;if(!$.quota)continue;let z=Math.max($.quota.primary?.checkedAt??0,$.quota.secondary?.checkedAt??0);if(z<=0)continue;let X=this.getFallback($.id,$.access);if(X&&X.checkedAt>=z)continue;this.setFallback($.id,{quota:$.quota,refreshAfter:z+Z,checkedAt:z},$.access)}}isBackedOff(){return A(this.mainLastApiError,this.now())}isFallbackBackedOff(B,Z){if(Z){if(this.fallbackErrorTokenFps.get(B)!==b(Z))return!1}return A(this.fallbackApiErrors.get(B),this.now())}getLastApiError(){return this.mainLastApiError}static API_CALL_GAP_MS=1000;static fallbackInflightKey(B,Z){return JSON.stringify([B,b(Z)])}static quotaLockName(B){return`opencode-fallback-quota-refresh-${B.replace(/[^a-zA-Z0-9._-]+/g,"-")||"account"}`}_enqueueApiFetch(B){let Z=async()=>{let z=this.now()-this.lastApiCallAt;if(z<M.API_CALL_GAP_MS)await new Promise((X)=>{let W=f2(X,M.API_CALL_GAP_MS-z);if(typeof W==="object"&&"unref"in W)W.unref()});return this.lastApiCallAt=this.now(),B()},$=this.apiGate.then(Z,Z);return this.apiGate=$.catch(()=>{}),$}async _fetchMain(B){let Z=b(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 z=this.main;if(z&&this.now()<z.refreshAfter)return z.quota;throw Error("Quota refresh is already in progress")}try{let z=this.now(),X=await this._fetchQuota(B),W=this.now();return this.mainTokenFp=b(B),this.main={quota:X,refreshAfter:T0(X,this.storage,W),checkedAt:W},this.mainLastApiError=void 0,this.onMainQuotaFetched?.(X,W,this.mainTokenFp,z),X}catch(z){throw this._handleMainFetchError(z),z}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 z=this.getFallback(B,Z);if(z)return z.quota;throw Error("Quota API rate-limited — try again later")}let $=await T({name:M.quotaLockName(B),ttlMs:30000});if(!$){let z=this.getFallback(B,Z);if(z&&this.now()<z.refreshAfter)return z.quota;throw Error("Quota refresh is already in progress")}try{let z=await this._fetchQuota(Z),X=this.now();return this.setFallback(B,{quota:z,refreshAfter:X+f(this.storage),checkedAt:X},Z),this.fallbackApiErrors.delete(B),this.fallbackErrorTokenFps.delete(B),z}finally{await $.release()}}catch($){throw this._handleFallbackFetchError(B,Z,$),$}finally{this.inflightFallbacks.delete(M.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(M.isAuthError(B))return;this.mainLastApiError=v({error:B,now:this.now(),previous:this.mainLastApiError}),this.onApiError?.(this.mainLastApiError)}_handleFallbackFetchError(B,Z,$){if(M.isAuthError($))return;let z=b(Z),X=this.fallbackErrorTokenFps.get(B)===z?this.fallbackApiErrors.get(B):void 0;this.fallbackApiErrors.set(B,v({error:$,now:this.now(),previous:X})),this.fallbackErrorTokenFps.set(B,z)}}function f0(B){for(let Z of Object.keys(B))return!0;return!1}import{createHash as s2,randomUUID as r2}from"node:crypto";import{homedir as n2}from"node:os";import{dirname as c2,join as B0}from"node:path";import{setTimeout as a2}from"node:timers/promises";import{randomUUID as p2}from"node:crypto";import{mkdir as l2,rename as d2,rm as g2,writeFile as i2}from"node:fs/promises";import{dirname as u2}from"node:path";async function m(B,Z){await l2(u2(B),{recursive:!0});let $=`${B}.${p2()}.tmp`;await i2($,`${JSON.stringify(Z,null,2)}
91
- `,{encoding:"utf8",mode:384});try{await d2($,B)}catch(z){throw await g2($,{force:!0}).catch(()=>{}),z}}import{readFile as K3}from"node:fs/promises";var E=d0("refresh"),m0=1e4,o2=50,g0="openai-auth.json",t2="openai-auth-state.json";function e2(){if(process.env.OPENCODE_CONFIG_DIR?.trim())return process.env.OPENCODE_CONFIG_DIR.trim();return B0(process.env.XDG_CONFIG_HOME||B0(n2(),".config"),"opencode")}function w(){return process.env.OPENCODE_OPENAI_AUTH_FILE?.trim()||B0(e2(),g0)}function z0(B=w()){let Z=process.env.OPENCODE_OPENAI_AUTH_STATE_FILE?.trim();if(Z)return Z;return B3(B)}function B3(B){return B.endsWith(g0)?B0(c2(B),t2):`${B}.state.json`}function P(B){return B.type==="oauth"}function Z3(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 SB(B){return B.costZeroing?.enabled!==!1}class Z0 extends Error{code="ACCOUNT_REMOVED_DURING_REFRESH";constructor(B){super(`Fallback account ${B} was removed before refresh`);this.name="AccountRemovedDuringRefreshError"}}var $3=[401,403,429],w0={primary:0,secondary:0},z3=!1,N0={primary:5,secondary:10};function j(B){return B!=null&&typeof B==="object"&&!Array.isArray(B)}function W3(B){return B instanceof Z0||j(B)&&B.name==="AccountRemovedDuringRefreshError"&&B.code==="ACCOUNT_REMOVED_DURING_REFRESH"}function k0(B){return{id:typeof B.id==="string"&&B.id.trim()?B.id.trim():r2(),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 h0(B){if(!j(B))return;if(typeof B.message!=="string")return;let Z=Number(B.checkedAt);if(!Number.isFinite(Z))return;let $=Number(B.nextRetryAt),z=Number(B.retryCount);return{message:B.message,checkedAt:Z,nextRetryAt:Number.isFinite($)?$:void 0,retryCount:Number.isFinite(z)?z:void 0,tokenHash:typeof B.tokenHash==="string"?B.tokenHash:void 0}}function X3(B){if(!j(B))return;let Z={};for(let $ of["primary","secondary"]){let z=B[$];if(!j(z))continue;let X=Number(z.usedPercent),W=Number(z.remainingPercent),G=Number(z.checkedAt);if(!Number.isFinite(X)||!Number.isFinite(W)||!Number.isFinite(G))continue;Z[$]={usedPercent:X,remainingPercent:W,checkedAt:G,resetsAt:typeof z.resetsAt==="string"?z.resetsAt:void 0}}return Object.keys(Z).length?Z:void 0}function G3(B){if(!j(B))return null;if(B.type==="api"){let Z=typeof B.baseURL==="string"?B.baseURL.trim():"",$=typeof B.apiKey==="string"?B.apiKey.trim():"";if(!Z3(Z))return null;let z=B.authHeader==="x-api-key"?"x-api-key":"authorization-bearer";return{...k0(B),type:"api",apiKey:$||void 0,baseURL:Z,authHeader:z}}if(B.type!=="oauth")return null;if(typeof B.refresh!=="string"||!B.refresh.trim())return null;return{...k0(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:h0(B.lastRefreshError),lastQuotaRefreshError:h0(B.lastQuotaRefreshError),quota:X3(B.quota)}}function i0(B){if(!j(B)||!Array.isArray(B.accounts))return null;return{version:1,main:{type:"opencode",provider:"openai"},routing:j(B.routing)?B.routing:void 0,fallbackOn:Array.isArray(B.fallbackOn)?B.fallbackOn.filter((Z)=>Number.isInteger(Z)):void 0,refresh:j(B.refresh)?B.refresh:void 0,quota:j(B.quota)?B.quota:void 0,dump:j(B.dump)?B.dump:void 0,costZeroing:j(B.costZeroing)?B.costZeroing:void 0,killswitch:j(B.killswitch)?B.killswitch:void 0,logging:j(B.logging)?B.logging:void 0,cachekeep:j(B.cachekeep)?B.cachekeep:void 0,mainAccountId:typeof B.mainAccountId==="string"?B.mainAccountId:void 0,accounts:B.accounts.map(G3).filter((Z)=>Z!=null)}}async function x(B){try{return{exists:!0,value:JSON.parse(await K3(B,"utf8"))}}catch(Z){if(Z.code==="ENOENT")return{exists:!1,value:null};throw Z}}function U(B){return Object.fromEntries(Object.entries(B).filter(([,Z])=>Z!==void 0))}async function Y3(B){let Z;try{Z=(await x(B)).value}catch{return null}if(!j(Z)||!Array.isArray(Z.accounts))return null;let $=new Set;for(let z of Z.accounts)if(j(z)&&typeof z.id==="string")$.add(z.id);return $}function u0(B,Z){if(!j(B))return B;let $=j(Z)?Z:{},z=j($.main)?$.main:void 0,X=j($.accounts)?$.accounts:{},W=j(B.quota)?B.quota:{},G=j(B.refresh)?B.refresh:{},K=z??W,Y=z??G,H=Array.isArray(B.accounts)?B.accounts.map((V)=>{if(!j(V))return V;let D=typeof V.id==="string"&&j(X[V.id])?X[V.id]:{};return{...V,...D}}):void 0;return s0({...B,refresh:U({...G,mainLastRefreshError:Y.lastRefreshError,mainRefreshLeaseId:Y.refreshLeaseId,mainRefreshLeaseUntil:Y.refreshLeaseUntil,mainRefreshLeaseTokenHash:Y.refreshLeaseTokenHash}),quota:U({...W,mainQuota:K.quota,mainQuotaCheckedAt:K.quotaCheckedAt,mainQuotaToken:K.quotaToken,mainLastQuotaApiError:K.lastQuotaApiError}),accounts:H})}async function J3(B=w()){let Z=await x(B);if(!Z.exists)return null;let $=await x(z0(B));return i0(u0(Z.value,$.value))}function s0(B){return Object.fromEntries(Object.entries(B).filter(([,Z])=>Z!==void 0))}function H3(B){return U({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 r0(B){if(B.type==="api")return U({apiKey:B.apiKey,lastUsed:B.lastUsed});return U({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 $0(B){return Math.max(B?.primary?.checkedAt??0,B?.secondary?.checkedAt??0)}function u(B,Z,$){if($ in Z)B[$]=Z[$];else delete B[$]}function j3(B,Z){return B.access===Z.access&&B.refresh===Z.refresh&&B.expires===Z.expires&&B.lastRefreshedAt===Z.lastRefreshedAt}function Q3(B,Z){if(!j3(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 V3(B,Z,$){let z=Z.lastRefreshedAt??0,X=$.lastRefreshedAt??0,W=Z.expires??0,G=$.expires??0,K=X>z?$:z>X?Z:G>W?$:W>G?Z:Q3(Z,$);u(B,K,"access"),u(B,K,"refresh"),u(B,K,"expires"),u(B,K,"lastRefreshedAt"),u(B,K,"lastRefreshError")}function N3(B,Z){if(!j(B))return Z;let $=B,z=$0($.quota),X=$0(Z.quota),W=z>X,G=W?{...$,...Z,quota:$.quota,lastQuotaRefreshError:$.lastQuotaRefreshError}:{...$,...Z};if(!W&&!("lastQuotaRefreshError"in Z))delete G.lastQuotaRefreshError;if(!("lastRefreshError"in Z))delete G.lastRefreshError;if(typeof $.lastUsed==="number"&&(typeof Z.lastUsed!=="number"||$.lastUsed>Z.lastUsed))G.lastUsed=$.lastUsed;return V3(G,$,Z),G}function n0(B){let Z=B.refresh?U({enabled:B.refresh.enabled,intervalMinutes:B.refresh.intervalMinutes,refreshBeforeExpiryMinutes:B.refresh.refreshBeforeExpiryMinutes}):void 0,$=B.quota?U({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 s0({version:1,main:B.main,routing:B.routing,fallbackOn:B.fallbackOn,refresh:Z,quota:$,dump:B.dump,costZeroing:B.costZeroing,killswitch:B.killswitch,logging:B.logging,cachekeep:B.cachekeep,mainAccountId:B.mainAccountId,accounts:B.accounts.map(H3)})}async function s(B){let Z=Date.now()+m0;while(Date.now()<=Z){let $=await T({name:"save",ttlMs:m0,path:B});if($)return $;let z=Z-Date.now();if(z<=0)break;await a2(Math.min(o2,z))}throw Error(`Timed out acquiring account store lock for ${B}; refusing to overwrite newer account data`)}function c0(B){let Z=Object.fromEntries(B.accounts.map(($)=>[$.id,r0($)]));return{version:1,main:U({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 EB(B,Z=w()){let $=z0(Z),z=await s(Z);try{let X=await s($);try{let W=await x(Z),G=await x($),K=(W.exists?i0(u0(W.value,G.value)):null)??L3(),Y=B(K)??K,H={...j(W.value)?W.value:{},...n0(Y)};return await m(Z,H),await m($,c0(Y)),Y}finally{await X.release()}}finally{await z.release()}}function L3(){return{version:1,main:{type:"opencode",provider:"openai"},accounts:[]}}function b3(B,Z){B.main=B.main??{};let $=typeof B.main.quotaCheckedAt==="number"?B.main.quotaCheckedAt:$0(B.main.quota),z=typeof Z.quota?.mainQuotaCheckedAt==="number"?Z.quota.mainQuotaCheckedAt:$0(Z.quota?.mainQuota);if($>z)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 D3(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 L0(B){if(Array.isArray(B))return B.map(L0);if(!j(B))return B;return Object.fromEntries(Object.entries(B).filter(([,Z])=>Z!==void 0).map(([Z,$])=>[Z,L0($)]))}async function F3(B,Z=w(),$={mainQuota:!0,mainRefresh:!0,accounts:!0}){let z=z0(Z),X=await s(z);try{let W=(await x(z)).value,G=j(W)?{...W,version:1}:{version:1};if($.mainQuota)b3(G,B);if($.mainRefresh)D3(G,B);if($.accounts){let K=$.accounts===!0?null:new Set($.accounts),Y=await Y3(Z);G.accounts={...j(G.accounts)?G.accounts:{}};for(let J of B.accounts){if(K&&!K.has(J.id))continue;if(Y&&!Y.has(J.id))continue;G.accounts[J.id]=N3(G.accounts[J.id],r0(J))}if(K){for(let J of K)if(!B.accounts.some((H)=>H.id===J))delete G.accounts[J]}if(Y){for(let J of Object.keys(G.accounts))if(!Y.has(J))delete G.accounts[J]}}await m(z,L0(G))}finally{await X.release()}}function O3(B){return B?.fallbackOn?.length?B.fallbackOn:$3}function IB(B,Z){return O3(Z).includes(B)}function M3(B){let Z=B?.quota?.minimumRemaining||{};return{primary:Z.primary??Z["5h"]??w0.primary,secondary:Z.secondary??Z["1w"]??w0.secondary}}function b0(B){return B?.quota?.enabled!==!1}function F0(B){return B?.quota?.failClosedOnUnknownQuota??z3}function a0(B,Z,$=Date.now()){if(!b0(Z))return!0;let z=M3(Z);for(let X of["primary","secondary"]){let W=B?.[X];if(!W||j0(W,$))return!F0(Z);if(W.remainingPercent<z[X])return!1}return!0}function P3(B){return B?.killswitch?.enabled===!0}function v0(B){return{primary:B?.primary??B?.["5h"]??N0.primary,secondary:B?.secondary??B?.["1w"]??N0.secondary}}function U3(B,Z){if(!B?.killswitch)return N0;if(Z&&B.killswitch.accounts?.[Z])return v0(B.killswitch.accounts[Z]);return v0(B.killswitch.main)}function xB(B,Z,$,z=Date.now()){if(!P3(Z))return!0;let X=U3(Z,$),W=!1;for(let G of["primary","secondary"]){let K=B?.[G];if(!K||j0(K,z)){W=!0;continue}if(K.remainingPercent<X[G])return!1}if(W)return!F0(Z);return!0}function AB(B,Z,$){let z=[],X=[B,...Z.map((G)=>G.quota)];for(let G of X)for(let K of["primary","secondary"]){let Y=G?.[K]?.resetsAt;if(!Y)continue;let J=Date.parse(Y);if(Number.isFinite(J)&&J>$)z.push(J)}if(z.length===0)return 300;let W=Math.min(...z);return Math.max(1,Math.ceil((W-$)/1000))}function _3(B){return typeof B.version==="number"&&Array.isArray(B.accounts)}async function TB(B,Z=w()){let $=z0(Z),z=await s(Z);try{let X=await s($);try{let W=await x(Z);if(W.exists&&j(W.value)){if(_3(W.value))return}if(!B)return;let G={version:1,main:{type:"opencode",provider:"openai"},accounts:[]};if(B.access){let J=a({id_token:"",access_token:B.access,refresh_token:B.refresh});if(J)G.mainAccountId=J}let Y={...W.exists&&j(W.value)?W.value:{},...n0(G)};await m(Z,Y),await m($,c0(G))}finally{await X.release()}}finally{await z.release()}}function q3(B){return B?.refresh?.enabled!==!1}function C3(B){return(B?.refresh?.refreshBeforeExpiryMinutes??240)*60000}function R3(B){return Math.floor(Math.random()*(B+1))}function I(B,Z,$){return!B.access||!B.expires||B.expires-$<=C3(Z)}function S3(B,Z){return Boolean(B.access&&typeof B.expires==="number"&&B.expires>Z)}function y3(B,Z){return Boolean(B.mainAccountId&&Z.accountId&&Z.accountId===B.mainAccountId)}function L(B,Z){let $=B.accounts.findIndex((z)=>z.id===Z.id);if($!==-1)B.accounts[$]=Z}function Q0(B){return B instanceof Error?B.message:String(B)}function D0(B,Z,$){B.lastRefreshError=C0({error:Z,now:$,refreshToken:B.refresh,previous:B.lastRefreshError})}function V0(B,Z,$){if(B.lastQuotaRefreshError=v({error:Z,now:$,previous:B.lastQuotaRefreshError}),Z?.isRefreshError===!0)D0(B,Z,$)}function E3(B){return`fallback-oauth-refresh-${s2("sha256").update(B).digest("base64url").slice(0,16)}`}function p0(B,Z){if(!B)return!1;if(!B.resetsAt)return!0;let $=Date.parse(B.resetsAt);return Number.isFinite($)?$>Z:!0}function I3(B,Z){if(!B)return!1;return p0(B[t],Z)||p0(B[e],Z)}function x3(B,Z,$){if(!B)return!1;let z=Z?.quota?.checkIntervalMinutes??5,X=Math.max(1,z)*60000,W=Math.max(B[t]?.checkedAt??0,B[e]?.checkedAt??0);return $-W<X}function l0(B,Z,$){return!x3(B.quota,Z,$)}function A3(B,Z,$,z){return X0($)&&a0(B.quota,Z,z)&&I3(B.quota,z)}function T3(B){let Z=B?.quota?.checkIntervalMinutes??5;return Math.max(1,Z)*60000}var f3=60000,m3=60000,w3=600000,k3=1e4,h3=100;var fB=globalThis.setTimeout.bind(globalThis),mB=globalThis.clearTimeout.bind(globalThis);class v3{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??w(),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[t]?.checkedAt??0,B.quota[e]?.checkedAt??0);if($<=0)return;let z=this.quotaManager.getFallback(B.id,B.access);if(z&&z.checkedAt>=$)return;this.quotaManager.setFallback(B.id,{quota:B.quota,refreshAfter:$+T3(Z),checkedAt:$},B.access)}async load(){return J3(this.configPath)}async save(B,Z){await F3(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(()=>{})},f3+R3(m3)),"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 $=[],z=!1;for(let X of Z.accounts){if(X.enabled===!1||!P(X))continue;if(y3(Z,X))continue;let W=!1,G=X;try{if(I(G,Z,this.now())){let K=G.lastRefreshError;if(K&&q(K,G.refresh,this.now()))throw W=!0,Error(n(K,this.now()));try{G=await this.refreshAccount(G,Z),z=!0}catch(Y){if(W3(Y))continue;W=!0;let J=Z.accounts.find((H)=>H.id===X.id&&P(H));if(J&&!q(J.lastRefreshError,J.refresh,this.now()))D0(J,Y,this.now()),L(Z,J),z=!0;throw Y}}if(this.seedFallbackQuota(G,Z),this.accountPassesQuotaPolicy(this.quotaPolicyAccount(G),Z))$.push(G)}catch(K){let Y=S3(G,this.now());if(W){if(!Y)continue}else if(!Y)continue;if(A3(G,Z,K,this.now()))E.debug("fallback quota using cached quota after refresh error",{pid:process.pid,accountId:G.id,error:Q0(K)}),$.push(G);else if(!F0(Z))$.push(G)}}if(z)await this.save(Z);return $}async markUsed(B){let Z=await this.load();if(!Z)return;let $=Z.accounts.find((z)=>z.id===B.id);if(!$)return;$.lastUsed=this.now(),await this.save(Z)}accountPassesQuotaPolicy(B,Z){return a0(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||!q3(B))return;let Z=!1;for(let $ of B.accounts){if($.enabled===!1||!P($))continue;if(!I($,B,this.now()))continue;if(q($.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(z){E.warn("fallback oauth background failed",{pid:process.pid,accountId:$.id,error:Q0(z)}),D0($,z,this.now()),L(B,$),Z=!0}}if(Z)await this.save(B)}async refreshQuotaForDueAccounts(){let B=await this.load();if(!B||!b0(B))return;if(!this.options.fetchQuotaFn)return;let Z=!1;for(let $ of B.accounts){if($.enabled===!1||!P($))continue;let z=$;try{if(I(z,B,this.now())){if(q(z.lastRefreshError,z.refresh,this.now()))continue;z=await this.refreshAccount(z,B),Z=!0}if(A(z.lastQuotaRefreshError,this.now()))continue;if(this.seedFallbackQuota(z,B),!(this.quotaManager?this.quotaManager.isFallbackStale(z.id,z.access):l0(z,B,this.now())))continue;await this.refreshAccountQuota(z,B),Z=!0}catch(X){V0($,X,this.now()),L(B,$),Z=!0}}if(Z)await this.save(B),this.onFallbackStorageChanged?.()}async refreshQuotaForAllAccounts(B={}){let Z=await this.load(),$=[];if(!Z||!b0(Z))return{storage:Z,errors:$};if(!this.options.fetchQuotaFn)return{storage:Z,errors:$};let z=B.force??!1,X=!1;for(let W of Z.accounts){if(W.enabled===!1||!P(W))continue;let G=W;try{if(I(G,Z,this.now())){let K=G.lastRefreshError;if(K&&q(K,G.refresh,this.now()))throw Error(n(K,this.now()));G=await this.refreshAccount(G,Z),X=!0}if(!z&&!l0(G,Z,this.now())){if(G.lastQuotaRefreshError)G.lastQuotaRefreshError=void 0,L(Z,G),X=!0;continue}await this.refreshAccountQuota(G,Z),X=!0}catch(K){V0(W,K,this.now()),L(Z,W),X=!0,$.push({accountId:W.id,message:Q0(K)})}}if(X)await this.save(Z);return{storage:Z,errors:$}}async refreshAccount(B,Z,$={}){let z=this.refreshPromises.get(B.id);if(z){let G=await z;return L(Z,G),G}let X=this.refreshAccountNow(B,Z,$).finally(()=>{this.refreshPromises.delete(B.id)});this.refreshPromises.set(B.id,X);let W=await X;return L(Z,W),W}async waitForConcurrentFallbackRefresh(B,Z,$,z){let X=Date.now()+k3;while(Date.now()<X){await new Promise((J)=>setTimeout(J,h3));let W=await this.load(),G=W?.accounts.find((J)=>J.id===B.id&&P(J));if(!G)continue;if((G.access!==$.access||G.refresh!==$.refresh||(G.expires??0)>($.expires??0)+60000)&&(z.force||!I(G,W,this.now())))return L(Z,G),E.debug("fallback oauth joined concurrent refresh",{pid:process.pid,accountId:G.id,expiresInMs:G.expires?G.expires-this.now():void 0}),G;let Y=G.lastRefreshError;if(Y&&q(Y,G.refresh,this.now()))throw L(Z,G),Error(n(Y,this.now()))}return null}async refreshAccountNow(B,Z,$){let z=await this.load(),X=z?.accounts.find((K)=>K.id===B.id&&P(K));if(X&&!$.force&&!I(X,z,this.now()))return L(Z,X),X;let W=X??B,G=await T({name:E3(W.id),ttlMs:w3,path:this.configPath,now:this.now,renew:!0});if(!G){E.debug("fallback oauth refresh skipped file lock",{pid:process.pid,accountId:W.id});let K=await this.waitForConcurrentFallbackRefresh(B,Z,W,$);if(K)return K;throw Error("Fallback OAuth refresh is already in progress")}try{if(z=await this.load(),X=z?.accounts.find((H)=>H.id===B.id&&P(H)),X&&!$.force&&!I(X,z,this.now()))return L(Z,X),X;if(!X)throw new Z0(B.id);W=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:W.id,force:$.force===!0,expiresInMs:W.expires?W.expires-this.now():void 0});let Y=await K({refreshToken:W.refresh,fetchImpl:this.fetchImpl,now:this.now});if(W.access=Y.access,W.refresh=Y.refresh,W.expires=Y.expires,W.lastRefreshedAt=Y.expires-Y.expiresIn*1000,W.lastRefreshError=void 0,L(Z,W),await this.save(Z),!(await this.load())?.accounts.some((H)=>H.id===B.id))throw new Z0(B.id);return E.debug("fallback oauth refresh succeeded",{pid:process.pid,accountId:W.id,expiresInMs:W.expires?W.expires-this.now():void 0}),W}finally{await G.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 z=this.options.fetchQuotaFn??(async()=>{throw Error("No fetchQuotaFn injected — wham/usage supplement is disabled")}),X=(W)=>this.quotaManager?this.quotaManager.refreshFallback($.id,W):z({accessToken:W,fetchImpl:this.fetchImpl,now:this.now});try{if(!$.access)throw Error(`Fallback account ${B.id} has no access token`);let W=await X($.access);$.quota=W,$.lastQuotaRefreshError=void 0,L(Z,$),await this.save(Z)}catch(W){throw V0(B,W,this.now()),L(Z,B),W}}}
92
- export{q0 as g,C0 as h,q as i,n as j,E0 as k,x0 as l,H2 as m,S0 as n,y0 as o,a as p,K0 as q,Q2 as r,y as s,b2 as t,D2 as u,F2 as v,O2 as w,c3 as x,a3 as y,e3 as z,BB as A,T as B,j0 as C,M as D,w as E,P as F,SB as G,N0 as H,J3 as I,EB as J,IB as K,P3 as L,xB as M,AB as N,TB as O,v3 as P};
@@ -1 +0,0 @@
1
- import{b as a}from"./index-899gpxdd.js";import"./index-53j7bdev.js";export default a();