@cortexkit/opencode-anthropic-auth 1.15.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -26,10 +26,10 @@ This repo is a Bun workspace monorepo with two user-facing integrations and one
26
26
  ## What CortexKit adds over the original plugin
27
27
 
28
28
  - **Fallback Claude accounts**: keep each agent's normal Anthropic login as the primary account, then route to ordered fallback OAuth accounts on auth/quota/rate-limit failures.
29
- - **Routing mode toggle**: use `/claude-routing fallback-first` to prefer sidecar fallback accounts before the main account.
29
+ - **Routing modes**: use `/claude-routing sticky-balanced` to distribute cold sessions by current OAuth quota headroom while keeping each session on one account, or choose `main-first` / `fallback-first` ordering.
30
30
  - **Quota-aware routing**: skip main or fallback accounts when their 5-hour or 7-day Claude quota falls below your configured minimum.
31
31
  - **Persistent Claude cache controls**: manage Anthropic 1-hour prompt caching from `/claude-cache` with explicit, automatic, or hybrid modes.
32
- - **Cache keepalive**: use `/claude-cachekeep HH-HH` to pre-warm hybrid cache anchors for active sessions before the 1-hour TTL expires.
32
+ - **Cache keepalive**: use `/claude-cachekeep always` or `/claude-cachekeep HH-HH` to pre-warm hybrid cache anchors for active sessions before the 1-hour TTL expires.
33
33
  - **Fast mode toggle**: use `/claude-fast on|off` to request Anthropic fast mode for supported Opus models.
34
34
  - **Fable/Mythos reasoning visibility**: request summarized adaptive thinking for Claude Fable 5 and Mythos 5 so agents can display reasoning summaries instead of blank signed-thinking blocks.
35
35
  - **Fable content-filter recovery**: when Fable ends a session response with Anthropic's `refusal` content-filter reason, transparently retry with Opus 4.8 for 10 successful model responses. After each Opus response, a zero-output Fable prewarm advances the same prompt cache using the OAuth account that served the filtered Fable request. The latest Opus cache boundary is retained so a later refusal can bridge back to Opus even after more than 20 Fable content blocks; the selected OpenCode model remains Fable. The TUI sidebar shows the per-session recovery countdown and return to Fable, while OpenCode Desktop receives ignored `promptAsync` notices for both transitions.
@@ -189,6 +189,7 @@ Example:
189
189
  },
190
190
  "cacheKeep": {
191
191
  "enabled": false,
192
+ "always": false,
192
193
  "startHour": 9,
193
194
  "endHour": 23
194
195
  },
@@ -214,13 +215,17 @@ Example:
214
215
 
215
216
  The `routing` block controls `/claude-routing`, `claudeCache` controls `/claude-cache`, `cacheKeep` controls `/claude-cachekeep`, and `claudeFast` controls `/claude-fast`. OpenCode zeroes Anthropic OAuth model costs by default because OAuth usage is quota-based; set `costZeroing.enabled` to `false` only if you want OpenCode to display the provider's model pricing instead. Set `quota.showToasts` to `true` to opt into OpenCode quota toast notifications after quota refreshes. The `main` field identifies OpenCode's primary auth entry; Pi keeps primary OAuth credentials in Pi's own credential store, but uses the same sidecar shape for CortexKit settings and fallback account labels.
216
217
 
217
- Runtime data is stored separately in `anthropic-auth-state.json`: fallback OAuth tokens, API-route keys, token refresh backoff, quota snapshots, and quota API backoff. Background refresh and quota checks write only the state file, so editing `anthropic-auth.json` does not get overwritten by another running plugin instance.
218
+ Runtime data is stored separately in `anthropic-auth-state.json`: fallback OAuth tokens, API-route keys, token refresh backoff, quota snapshots, and quota API backoff. Sticky session assignments use `anthropic-auth-routing-state.json` and store only SHA-256 hashes of session IDs. Background refresh and quota checks write only runtime state, so editing `anthropic-auth.json` does not get overwritten by another running plugin instance.
218
219
 
219
220
  ## Fallback accounts
220
221
 
221
222
  Fallback accounts are separate Claude OAuth accounts or Anthropic-compatible API-key routes managed by this plugin. By default, the main account is tried first unless quota policy says it is currently unusable. Fallbacks are then tried in sidecar order when the primary request returns a configured fallback status.
222
223
 
223
- Use `/claude-routing fallback-first` to prefer usable fallback accounts before the main account. Use `/claude-routing main-first` to restore the default. The command persists `routing.mode` and takes effect on the next request without restarting.
224
+ Use `/claude-routing fallback-first` to prefer usable fallback accounts before the main account, `/claude-routing main-first` to restore the default, or `/claude-routing sticky-balanced` to allocate each new session according to spendable 5-hour, 7-day, and matching model-scoped OAuth quota headroom. The command persists `routing.mode` and takes effect on the next request without restarting. `/claude-routing reset` clears only the current session's assignment so its next request is allocated again.
225
+
226
+ `sticky-balanced` normalizes quota headroom by time until reset and tracks weighted initial-prompt deficit until the next quota refresh. Assignments are persisted by hashed session ID in `anthropic-auth-routing-state.json`, shared safely across OpenCode plugin instances and restarts. Active hybrid CacheKeep sessions seed their current route when the mode is first enabled.
227
+
228
+ Affinity survives network/relay failures, 5xx responses, unconfirmed 429s, quota-probe failures, and relative quota changes. Confirmed 7-day or matching model-scoped exhaustion migrates a session. Confirmed 5-hour exhaustion migrates only when more than 15 minutes remain; otherwise the plugin retains the route and returns `Retry-After`. Fable content-filter recovery keeps Fable, Opus 4.8, and Fable prewarms on the same sticky account. Direct Opus sessions first prefer an otherwise usable account whose Fable quota is exhausted.
224
229
 
225
230
  Default fallback statuses:
226
231
 
@@ -359,13 +364,16 @@ In OpenCode, subagent requests do not receive 1-hour TTL caching. The plugin det
359
364
 
360
365
  ```text
361
366
  /claude-cachekeep
367
+ /claude-cachekeep always
362
368
  /claude-cachekeep 09-23
363
369
  /claude-cachekeep off
364
370
  ```
365
371
 
366
- The hour range uses local 24-hour time and is start-inclusive/end-exclusive. `09-23` means cache keepalive may run from 09:00 until 22:59. Overnight windows such as `23-09` are accepted.
372
+ `always` keeps tracked sessions warm continuously for as long as the OpenCode process remains open. Hour ranges use local 24-hour time and are start-inclusive/end-exclusive: `09-23` runs from 09:00 until 22:59, and overnight windows such as `23-09` are accepted.
373
+
374
+ Cache keepalive only tracks requests when `/claude-cache` is enabled in `hybrid` mode. For each active session seen in the configured schedule, the package keeps an in-memory clone of the latest rewritten Anthropic request and sends a non-streaming `max_tokens: 0` pre-warm request about five minutes before the 1-hour cache entry would expire. Running `/claude-cachekeep` without arguments lists every live tracked session across OpenCode projects and plugin processes.
367
375
 
368
- Cache keepalive only tracks requests when `/claude-cache` is enabled in `hybrid` mode. For each active session seen that day, the package keeps an in-memory clone of the latest rewritten Anthropic request and sends a non-streaming `max_tokens: 0` pre-warm request about five minutes before the 1-hour cache entry would expire. Nothing is written to disk except the schedule configuration.
376
+ Request bodies, headers, and tokens remain in memory. A lease-backed file under the system temporary directory shares only session IDs and cache timing for cross-instance status; records from stopped processes disappear from status after about three minutes.
369
377
 
370
378
  Pre-warm requests preserve explicit cache anchors but remove response-only fields that Anthropic rejects with `max_tokens: 0`, such as streaming, enabled thinking, structured output format, and forced/any tool choice. The feature works only while OpenCode or Pi is running and the machine is awake, and cache writes are still billed when the cache entry is no longer warm.
371
379
 
package/dist/cli.js CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
- import{ga as O,jb as S,l as N,lb as x,m as L,n as T,p as q,r as D,t as C,u as H}from"./index-z03wayzs.js";import"./index-6gdd6e4s.js";import{Xb as z}from"./index-qsaph2g4.js";import{stdin as y,stdout as v}from"node:process";import{createInterface as R}from"node:readline/promises";function P(){return{version:1,main:{type:"opencode",provider:"anthropic"},fallbackOn:[401,403,429],refresh:{enabled:!0,intervalMinutes:10,refreshBeforeExpiryMinutes:240},quota:{enabled:!0,checkIntervalMinutes:5,minimumRemaining:{five_hour:10,seven_day:20},failClosedOnUnknownQuota:!0},accounts:[]}}function w(){console.log(`Usage:
2
+ import{fa as U,l as z,m as N,mb as T,n as P,ob as q,p as L,r as E,t as K,u as S}from"./index-sgwz35qx.js";import"./index-epg96jpt.js";import{fc as w}from"./index-dbczmr1v.js";import{stdin as x,stdout as v}from"node:process";import{createInterface as y}from"node:readline/promises";function R(){return{version:1,main:{type:"opencode",provider:"anthropic"},fallbackOn:[401,403,429],refresh:{enabled:!0,intervalMinutes:10,refreshBeforeExpiryMinutes:240},quota:{enabled:!0,checkIntervalMinutes:5,minimumRemaining:{five_hour:10,seven_day:20},failClosedOnUnknownQuota:!0},accounts:[]}}function _(){console.log(`Usage:
3
3
  opencode-anthropic-auth login [label]
4
4
  opencode-anthropic-auth api add [label]
5
5
  opencode-anthropic-auth list
6
6
  opencode-anthropic-auth relay setup
7
7
 
8
8
  Fallback accounts are stored in:
9
- ${D()}`)}function W(j,G){let F=j?.trim();if(!F)throw Error(`${G} is required`);return F}async function K(j){let F=await(j.fetchImpl??fetch)(`https://api.cloudflare.com/client/v4${j.path}`,{method:j.method,headers:{authorization:`Bearer ${j.token}`,...j.body instanceof FormData?{}:{"content-type":"application/json"},...j.headers},body:j.body}),M=await F.text(),J;try{J=JSON.parse(M)}catch{throw Error(`Cloudflare API returned ${F.status}: ${M}`)}if(!F.ok||J.success===!1){let X=J.errors?.map((Q)=>Q.message).filter(Boolean).join("; ");throw Error(X||`Cloudflare API returned ${F.status}`)}return J.result}async function A(j,G,F,M){return K({token:j,method:"POST",path:`/accounts/${G}/storage/kv/namespaces`,body:JSON.stringify({title:F}),fetchImpl:M})}async function h(j){let G={main_module:"worker.js",compatibility_date:"2026-04-28",bindings:[{type:"kv_namespace",name:"RELAY_STATE",namespace_id:j.kvNamespaceId},{type:"secret_text",name:"RELAY_TOKEN",text:j.relayToken}]},F=new FormData;return F.set("metadata",JSON.stringify(G)),F.set("worker.js",new Blob([x],{type:"application/javascript+module"}),"worker.js"),K({token:j.token,method:"PUT",path:`/accounts/${j.accountId}/workers/scripts/${j.scriptName}`,body:F,fetchImpl:j.fetchImpl})}async function k(j,G,F,M){await K({token:j,method:"POST",path:`/accounts/${G}/workers/scripts/${F}/subdomain`,body:JSON.stringify({enabled:!0,previews_enabled:!1}),fetchImpl:M})}async function I(j,G,F){return K({token:j,method:"GET",path:`/accounts/${G}/workers/subdomain`,fetchImpl:F}).catch(()=>null)}async function f(j={}){let G=j.fetchImpl??fetch,F=j.prompt??V,M=await C()??P(),J=W(process.env.CLOUDFLARE_API_TOKEN?.trim()||await F("Cloudflare API token: "),"Cloudflare API token"),X=W(process.env.CLOUDFLARE_ACCOUNT_ID||await F("Cloudflare account ID: "),"Cloudflare account ID"),Q=await F("Worker name [opencode-anthropic-relay]: ")||"opencode-anthropic-relay",Y=`${Q}-state`,$=S();console.log("Creating Cloudflare KV namespace...");let Z=await A(J,X,Y,G);console.log("Uploading relay Worker..."),await h({token:J,accountId:X,scriptName:Q,kvNamespaceId:Z.id,relayToken:$,fetchImpl:G}),await k(J,X,Q,G).catch((U)=>{console.warn(`Could not enable workers.dev automatically: ${U instanceof Error?U.message:String(U)}`)});let B=await I(J,X,G),_=(B?.subdomain?`https://${Q}.${B.subdomain}.workers.dev`:"")||W(await V("Relay Worker URL: "),"Relay Worker URL");M.relay={enabled:!0,url:_,token:$,fallbackToDirect:!0,transport:"http"},await H(M),console.log(`Relay enabled at ${_}`),console.log(`Config saved to ${D()}.`)}var E=null;async function V(j){return E??=R({input:y,output:v}),(await E.question(j)).trim()}function b(){E?.close(),E=null}async function g(j,G={}){let F=G.prompt??V,M=G.authorize??N,J=G.exchange??L,X=await C()??P(),Q=j?.trim()||await F("Fallback account label (optional): "),Y=await M("max");console.log(`
9
+ ${E()}`)}function H(j,G){let F=j?.trim();if(!F)throw Error(`${G} is required`);return F}async function V(j){let F=await(j.fetchImpl??fetch)(`https://api.cloudflare.com/client/v4${j.path}`,{method:j.method,headers:{authorization:`Bearer ${j.token}`,...j.body instanceof FormData?{}:{"content-type":"application/json"},...j.headers},body:j.body}),J=await F.text(),M;try{M=JSON.parse(J)}catch{throw Error(`Cloudflare API returned ${F.status}: ${J}`)}if(!F.ok||M.success===!1){let Q=M.errors?.map((X)=>X.message).filter(Boolean).join("; ");throw Error(Q||`Cloudflare API returned ${F.status}`)}return M.result}async function A(j,G,F,J){return V({token:j,method:"POST",path:`/accounts/${G}/storage/kv/namespaces`,body:JSON.stringify({title:F}),fetchImpl:J})}async function g(j){let G={main_module:"worker.js",compatibility_date:"2026-04-28",bindings:[{type:"kv_namespace",name:"RELAY_STATE",namespace_id:j.kvNamespaceId},{type:"secret_text",name:"RELAY_TOKEN",text:j.relayToken}]},F=new FormData;return F.set("metadata",JSON.stringify(G)),F.set("worker.js",new Blob([q],{type:"application/javascript+module"}),"worker.js"),V({token:j.token,method:"PUT",path:`/accounts/${j.accountId}/workers/scripts/${j.scriptName}`,body:F,fetchImpl:j.fetchImpl})}async function h(j,G,F,J){await V({token:j,method:"POST",path:`/accounts/${G}/workers/scripts/${F}/subdomain`,body:JSON.stringify({enabled:!0,previews_enabled:!1}),fetchImpl:J})}async function k(j,G,F){return V({token:j,method:"GET",path:`/accounts/${G}/workers/subdomain`,fetchImpl:F}).catch(()=>null)}async function f(j={}){let G=j.fetchImpl??fetch,F=j.prompt??C,J=H(process.env.CLOUDFLARE_API_TOKEN?.trim()||await F("Cloudflare API token: "),"Cloudflare API token"),M=H(process.env.CLOUDFLARE_ACCOUNT_ID||await F("Cloudflare account ID: "),"Cloudflare account ID"),Q=await F("Worker name [opencode-anthropic-relay]: ")||"opencode-anthropic-relay",X=`${Q}-state`,Z=T();console.log("Creating Cloudflare KV namespace...");let Y=await A(J,M,X,G);console.log("Uploading relay Worker..."),await g({token:J,accountId:M,scriptName:Q,kvNamespaceId:Y.id,relayToken:Z,fetchImpl:G}),await h(J,M,Q,G).catch((D)=>{console.warn(`Could not enable workers.dev automatically: ${D instanceof Error?D.message:String(D)}`)});let $=await k(J,M,G),W=($?.subdomain?`https://${Q}.${$.subdomain}.workers.dev`:"")||H(await C("Relay Worker URL: "),"Relay Worker URL"),O=await K()??R();O.relay={enabled:!0,url:W,token:Z,fallbackToDirect:!0,transport:"http"},await S(O),console.log(`Relay enabled at ${W}`),console.log(`Config saved to ${E()}.`)}var B=null;async function C(j){return B??=y({input:x,output:v}),(await B.question(j)).trim()}function I(){B?.close(),B=null}async function b(j,G={}){let F=G.prompt??C,J=G.authorize??z,M=G.exchange??N,Q=j?.trim()||await F("Fallback account label (optional): "),X=await J("max");console.log(`
10
10
  Open this URL in your browser and complete Claude sign-in:
11
- `),console.log(`${Y.url}
12
- `);let $=await F("Paste the full callback URL or authorization code here: "),Z=await J($,Y.verifier,Y.redirectUri,Y.state);if(Z.type==="failed")throw Error("Authentication failed");let B=Date.now();O(X,{id:Q||crypto.randomUUID(),label:Q||void 0,type:"oauth",access:Z.access,refresh:Z.refresh,expires:Z.expires,enabled:!0,addedAt:B,lastUsed:B,lastRefreshedAt:B}),await H(X),console.log(`
13
- Saved fallback account${Q?` "${Q}"`:""}.`)}async function d(j,G={}){let F=G.prompt??V,M=await C()??P(),J=j?.trim()||await F("API fallback label (optional): "),X=process.env.OPENCODE_ANTHROPIC_AUTH_API_BASE_URL?.trim()||(await F("Anthropic-compatible base URL [https://api.kie.ai/claude]: ")).trim()||"https://api.kie.ai/claude";if(!q(X))throw Error("API fallback base URL must be an http(s) URL without embedded credentials");let Q=process.env.OPENCODE_ANTHROPIC_AUTH_API_KEY?.trim()||await F("API key: ");if(!Q.trim())throw Error("API key is required");let $=(process.env.OPENCODE_ANTHROPIC_AUTH_API_AUTH_HEADER?.trim()||await F("Auth header [authorization-bearer|x-api-key] (default authorization-bearer): ")).trim().toLowerCase()==="x-api-key"?"x-api-key":"authorization-bearer",Z=Date.now();O(M,{id:J||crypto.randomUUID(),label:J||void 0,type:"api",apiKey:Q.trim(),baseURL:X,authHeader:$,enabled:!0,addedAt:Z,lastUsed:Z}),await H(M),console.log(`
14
- Saved API fallback route${J?` "${J}"`:""} (${X}).`)}async function m(){let j=await C();if(!j?.accounts.length){console.log(`No fallback accounts found at ${D()}.`);return}for(let[G,F]of j.accounts.entries()){let M=F.label||F.id,J=F.enabled===!1?"disabled":"enabled";if(!T(F)){console.log(`${G+1}. ${M} (${J}) — API route ${F.baseURL}`);continue}let X=F.quota?.five_hour?.remainingPercent,Q=F.quota?.seven_day?.remainingPercent,Y=X===void 0&&Q===void 0?"quota unknown":`5h ${X??"?"}%, 1w ${Q??"?"}% remaining`;console.log(`${G+1}. ${M} (${J}) — ${Y}`)}}async function i(){let[j,G,F]=process.argv.slice(2);if(!j||j==="help"||j==="--help"||j==="-h"){w();return}if(j==="login"){await g(G);return}if(j==="api"&&G==="add"){await d(F);return}if(j==="list"){await m();return}if(j==="relay"&&G==="setup"){await f();return}w(),process.exitCode=1}if(z.main==z.module)try{await i()}catch(j){console.error(j instanceof Error?j.message:String(j)),process.exitCode=1}finally{b()}export{f as relaySetup,g as login,d as addApiRoute};
11
+ `),console.log(`${X.url}
12
+ `);let Z=await F("Paste the full callback URL or authorization code here: "),Y=await M(Z,X.verifier,X.redirectUri,X.state);if(Y.type==="failed")throw Error("Authentication failed");let $=Date.now();await U({id:Q||crypto.randomUUID(),label:Q||void 0,type:"oauth",access:Y.access,refresh:Y.refresh,expires:Y.expires,enabled:!0,addedAt:$,lastUsed:$,lastRefreshedAt:$}),console.log(`
13
+ Saved fallback account${Q?` "${Q}"`:""}.`)}async function d(j,G={}){let F=G.prompt??C,J=j?.trim()||await F("API fallback label (optional): "),M=process.env.OPENCODE_ANTHROPIC_AUTH_API_BASE_URL?.trim()||(await F("Anthropic-compatible base URL [https://api.kie.ai/claude]: ")).trim()||"https://api.kie.ai/claude";if(!L(M))throw Error("API fallback base URL must be an http(s) URL without embedded credentials");let Q=process.env.OPENCODE_ANTHROPIC_AUTH_API_KEY?.trim()||await F("API key: ");if(!Q.trim())throw Error("API key is required");let Z=(process.env.OPENCODE_ANTHROPIC_AUTH_API_AUTH_HEADER?.trim()||await F("Auth header [authorization-bearer|x-api-key] (default authorization-bearer): ")).trim().toLowerCase()==="x-api-key"?"x-api-key":"authorization-bearer",Y=Date.now();await U({id:J||crypto.randomUUID(),label:J||void 0,type:"api",apiKey:Q.trim(),baseURL:M,authHeader:Z,enabled:!0,addedAt:Y,lastUsed:Y}),console.log(`
14
+ Saved API fallback route${J?` "${J}"`:""} (${M}).`)}async function m(){let j=await K();if(!j?.accounts.length){console.log(`No fallback accounts found at ${E()}.`);return}for(let[G,F]of j.accounts.entries()){let J=F.label||F.id,M=F.enabled===!1?"disabled":"enabled";if(!P(F)){console.log(`${G+1}. ${J} (${M}) — API route ${F.baseURL}`);continue}let Q=F.quota?.five_hour?.remainingPercent,X=F.quota?.seven_day?.remainingPercent,Z=Q===void 0&&X===void 0?"quota unknown":`5h ${Q??"?"}%, 1w ${X??"?"}% remaining`;console.log(`${G+1}. ${J} (${M}) — ${Z}`)}}async function u(){let[j,G,F]=process.argv.slice(2);if(!j||j==="help"||j==="--help"||j==="-h"){_();return}if(j==="login"){await b(G);return}if(j==="api"&&G==="add"){await d(F);return}if(j==="list"){await m();return}if(j==="relay"&&G==="setup"){await f();return}_(),process.exitCode=1}if(w.main==w.module)try{await u()}catch(j){console.error(j instanceof Error?j.message:String(j)),process.exitCode=1}finally{I()}export{f as relaySetup,b as login,d as addApiRoute};
@@ -25,6 +25,8 @@ export declare class FableFallbackManager {
25
25
  constructor(now?: () => number);
26
26
  private prune;
27
27
  plan(sessionId: string | null | undefined, bodyText: unknown): FableFallbackPlan | null;
28
+ recoveryAccount(plan: FableFallbackPlan): string | undefined;
29
+ bindRecoveryAccount(plan: FableFallbackPlan, oauthAccountId: string): boolean;
28
30
  activate(plan: FableFallbackPlan, cacheAccountId?: string): number;
29
31
  complete(plan: FableFallbackPlan, standbyCacheAnchor?: FableStandbyCacheAnchor): {
30
32
  counted: boolean;
@@ -1,2 +1,2 @@
1
1
  import{mkdir as q,readFile as M,writeFile as L}from"node:fs/promises";import{tmpdir as k}from"node:os";import{dirname as b,join as g}from"node:path";var R="OPENCODE_ANTHROPIC_AUTH_SIDEBAR_STATE_FILE",F=g(k(),"opencode-anthropic-auth"),f=g(F,"sidebar-state.json");function h(){return process.env[R]||f}var K={main:{quota:null},fallbacks:[],activeId:void 0,route:"main",relay:null,fastMode:!1,lastUpdated:0};function G(x){return x!==null&&typeof x==="object"&&!Array.isArray(x)}function z(x){if(!G(x))return;let X=Number(x.usedPercent),Z=Number(x.remainingPercent);if(!Number.isFinite(X)||!Number.isFinite(Z))return;return{usedPercent:X,remainingPercent:Z,resetsAt:typeof x.resetsAt==="string"?x.resetsAt:void 0}}function N(x){if(!G(x))return null;let X={},Z=z(x.five_hour),$=z(x.seven_day);if(Z)X.five_hour=Z;if($)X.seven_day=$;if(Array.isArray(x.scoped)){let V=x.scoped.map((Y)=>{if(!G(Y))return;let J=z(Y);if(!J)return;if(typeof Y.id!=="string"||!Y.id.trim())return;if(typeof Y.title!=="string"||!Y.title.trim())return;if(typeof Y.modelName!=="string"||!Y.modelName.trim())return;let j=typeof Y.modelId==="string"&&Y.modelId.trim()?Y.modelId.trim():void 0;return{...J,id:Y.id.trim(),title:Y.title.trim(),...j&&{modelId:j},modelName:Y.modelName.trim()}}).filter((Y)=>Y!=null);X.scoped=V}return Object.keys(X).length?X:null}function I(x){if(!G(x))return{...K};let X={quota:null};if(G(x.main)){let J=x.main;if(X.quota=N(J.quota),typeof J.quotaBackedOff==="boolean")X.quotaBackedOff=J.quotaBackedOff;if(typeof J.quotaBackoffUntil==="number")X.quotaBackoffUntil=J.quotaBackoffUntil;if(typeof J.refreshBackedOff==="boolean")X.refreshBackedOff=J.refreshBackedOff;if(typeof J.refreshBackoffUntil==="number")X.refreshBackoffUntil=J.refreshBackoffUntil}let Z=Array.isArray(x.fallbacks)?x.fallbacks.filter(G).filter((J)=>typeof J.id==="string").map((J)=>({id:J.id,label:typeof J.label==="string"?J.label:void 0,quota:N(J.quota),enabled:typeof J.enabled==="boolean"?J.enabled:!1,needsReauth:typeof J.needsReauth==="boolean"?J.needsReauth:!1})):[],$=null;if(G(x.relay)){let J=x.relay;if(typeof J.enabled==="boolean"&&typeof J.transport==="string")$={enabled:J.enabled,transport:J.transport}}let V;if(G(x.cacheKeep)){let J=x.cacheKeep;if(typeof J.enabled==="boolean")V={enabled:J.enabled,window:typeof J.window==="string"?J.window:void 0,trackedSessions:typeof J.trackedSessions==="number"?J.trackedSessions:void 0}}let Y=Array.isArray(x.fableRecoveries)?x.fableRecoveries.filter(G).flatMap((J)=>{if(typeof J.sessionId!=="string"||J.mode!=="opus"&&J.mode!=="fable"||typeof J.remaining!=="number"||!Number.isFinite(J.remaining)||typeof J.changedAt!=="number"||!Number.isFinite(J.changedAt))return[];return[{sessionId:J.sessionId,mode:J.mode,remaining:Math.max(0,Math.floor(J.remaining)),changedAt:J.changedAt}]}):[];return{main:X,fallbacks:Z,activeId:typeof x.activeId==="string"?x.activeId:void 0,route:typeof x.route==="string"?x.route:K.route,relay:$,fastMode:typeof x.fastMode==="boolean"?x.fastMode:K.fastMode,cacheKeep:V,fableRecoveries:Y.length>0?Y:void 0,lastUpdated:typeof x.lastUpdated==="number"?x.lastUpdated:0}}var W=Promise.resolve();async function P(){try{let x=await M(h(),"utf8");return I(JSON.parse(x))}catch{return K}}async function A(x,X=h()){return W=W.then(async()=>{await q(b(X),{recursive:!0}),await L(X,JSON.stringify(x),"utf8")}).catch(()=>{}),W}async function m(){return W}function u(x){let X=x.activeId;if(X&&X!=="main"){let Z=x.fallbacks?.find(($)=>$.enabled&&$.id===X);if(Z)return{id:Z.id,name:Z.label??Z.id,quota:Z.quota}}return{id:"main",name:"main",quota:x.main?.quota??null}}function U(x){let X=x.replace(/\s+only$/i,"").trim();return/^fable$/i.test(X)?"Fa":X}function l(x,X){let Z=x.fableRecoveries?.find(($)=>$.sessionId===X);if(!Z)return;if(Z.mode==="fable")return"Fable 5 · restored";return`Opus 4.8 · ${Z.remaining} left`}function v(x){let X=x?.five_hour?.usedPercent??null,Z=x?.seven_day?.usedPercent??null,$=x?.scoped??[],V=$.map((j)=>j.usedPercent);if(X==null&&Z==null&&$.length===0)return{fiveHourUsedPercent:X,sevenDayUsedPercent:Z,scopedUsedPercents:V,text:null};let Y=$.map((j)=>`${U(j.title)}: ${Math.round(j.usedPercent)}%`),J=X==null&&Z==null?[]:[`5h: ${X==null?"—":`${Math.round(X)}%`}`,`7d: ${Z==null?"—":`${Math.round(Z)}%`}`];return{fiveHourUsedPercent:X,sevenDayUsedPercent:Z,scopedUsedPercents:V,text:[...J,...Y].join(" ")}}var y=18000000,d=604800000,D=300000,_=0.01,p=1;function w(x,X,Z){if(!x.resetsAt)return null;let $=new Date(x.resetsAt).getTime();if(!Number.isFinite($))return null;let V=$-X,Y=Z-V;if(Y<D)return null;if(Y<X*_)return null;if(Y>=X)return null;let J=x.usedPercent,j=Math.min(Math.max(Y/X*100,0),100),B=J-j,C=Math.abs(B)<p?"on-pace":B>0?"deficit":"reserve",O=null;if(J>0){let H=Y*100/J,Q=V+H;if(Q<$)O=new Date(Q).toISOString()}return{pacePercent:j,deltaPercent:B,state:C,runsOutAt:O}}
2
- export{h as Eb,K as Fb,I as Gb,P as Hb,A as Ib,m as Jb,u as Kb,U as Lb,l as Mb,v as Nb,y as Ob,d as Pb,w as Qb};
2
+ export{h as Ob,K as Pb,I as Qb,P as Rb,A as Sb,m as Tb,u as Ub,U as Vb,l as Wb,v as Xb,y as Yb,d as Zb,w as _b};
@@ -1 +1 @@
1
- import{createHash as z}from"node:crypto";import{homedir as F,tmpdir as O}from"node:os";import{join as q,resolve as G}from"node:path";var J="OPENCODE_ANTHROPIC_AUTH_RPC_DIR";function T(g){let k=process.env[J]?.trim();if(k)return G(g,k);let w=z("sha256").update(g).digest("hex").slice(0,16),x=process.env.XDG_STATE_HOME||q(F(),".local","state");return q(x,"cortexkit","anthropic-auth","rpc",w)}export{O as Tb,T as Ub};
1
+ import{createHash as z}from"node:crypto";import{homedir as F,tmpdir as O}from"node:os";import{join as q,resolve as G}from"node:path";var J="OPENCODE_ANTHROPIC_AUTH_RPC_DIR";function T(g){let k=process.env[J]?.trim();if(k)return G(g,k);let w=z("sha256").update(g).digest("hex").slice(0,16),x=process.env.XDG_STATE_HOME||q(F(),".local","state");return q(x,"cortexkit","anthropic-auth","rpc",w)}export{O as bc,T as cc};
@@ -1,2 +1,2 @@
1
1
  import{createRequire as q}from"node:module";var j=Object.create;var{getPrototypeOf:k,defineProperty:i,getOwnPropertyNames:l}=Object;var m=Object.prototype.hasOwnProperty;function n(a){return this[a]}var o,p,r=(a,b,d)=>{var f=a!=null&&typeof a==="object";if(f){var g=b?o??=new WeakMap:p??=new WeakMap,h=g.get(a);if(h)return h}d=a!=null?j(k(a)):{};let c=b||!a||!a.__esModule?i(d,"default",{value:a,enumerable:!0}):d;for(let e of l(a))if(!m.call(c,e))i(c,e,{get:n.bind(a,e),enumerable:!0});if(f)g.set(a,c);return c};var s=(a,b)=>()=>(b||a((b={exports:{}}).exports,b),b.exports);var u=q(import.meta.url);
2
- export{r as Vb,s as Wb,u as Xb};
2
+ export{r as dc,s as ec,u as fc};
@@ -1,4 +1,4 @@
1
1
  import*as j from"node:fs";import*as P from"node:os";import*as V from"node:path";var U={error:0,warn:1,info:2,debug:3,trace:4},O=new Set(Object.keys(U));function F(q){if(!q)return null;let Q=q.trim().toLowerCase();return O.has(Q)?Q:null}var S=F(process.env.OPENCODE_ANTHROPIC_AUTH_LOG_LEVEL),M=S??"info";function L(q){M=q}function i(){return M}function X(q){return U[q]<=U[M]}var J=process.env.OPENCODE_ANTHROPIC_AUTH_LOG_FILE??V.join(P.tmpdir(),"opencode-anthropic-auth.log");function h(){return J}var R=5242880,N=3;function I(){try{if(!j.existsSync(J))return;if(j.statSync(J).size<R)return}catch{return}try{for(let q=N;q>=1;q--){let Q=q===1?J:`${J}.${q-1}`,W=`${J}.${q}`;try{if(q===N)try{j.unlinkSync(W)}catch{}if(j.existsSync(Q))j.renameSync(Q,W)}catch{}}}catch{}}var T=/^(authorization|x-api-key|cookie|set-cookie|refresh|access|token)$/i;function k(q){if(T.test(q))return!0;let Q=q.toLowerCase().replace(/[-_]/g,"");if(Q.includes("apikey"))return!0;if(Q.endsWith("secret")||Q.endsWith("password"))return!0;if(Q.endsWith("token")&&!Q.endsWith("tokens"))return!0;return!1}var f=[/Bearer\s+\S+/,/sk-[A-Za-z0-9-]+/,/eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/];function _(q){if(typeof q!=="string")return!1;return f.some((Q)=>Q.test(q))}function G(q){if(q==null||Object.keys(q).length===0)return;let Q=new WeakSet;function W($){if($==null||typeof $!=="object"){if(_($))return"***REDACTED***";return $}if(Q.has($))return"[Circular]";Q.add($);try{if(Array.isArray($))return $.map(W);let x=$,B={};for(let[H,C]of Object.entries(x)){if(k(H)){B[H]="***REDACTED***";continue}B[H]=W(C)}return B}finally{Q.delete($)}}return W(q)}function g(q,Q,W,$){let x=new Date().toISOString(),B=q.toUpperCase().padEnd(5),H=Q?` [${Q}]`:"",C=(()=>{try{let w=G($),A=w?{...w,pid:process.pid}:{pid:process.pid};return` ${JSON.stringify(A)}`}catch{return` {"pid":${process.pid},"payload":"[unserializable]"}`}})();return`[${x}] ${B}${H} ${W}${C}`}var D=null;function E(q){D=q}function Y(q,Q,W,$){if(!X(q))return;if(D)try{D({level:q,channel:Q,message:W,payload:G($)})}catch{}if(Z)return;try{let x=`${g(q,Q,W,$)}
2
2
  `;if(K.push(x),K.length>=p)b();else r()}catch{}}var m={error(q,Q,W){Y("error",q,Q,W)},warn(q,Q,W){Y("warn",q,Q,W)},info(q,Q,W){Y("info",q,Q,W)},debug(q,Q,W){Y("debug",q,Q,W)},trace(q,Q,W){Y("trace",q,Q,W)}},Z=!1,c=500,p=50,K=[],z=null;function b(){if(z)clearTimeout(z),z=null;if(K.length===0)return;let q=K.join("");K=[];try{I(),j.appendFileSync(J,q)}catch{}}function r(){if(z)return;z=setTimeout(()=>{z=null,b()},c)}var y=/^\[([\w-]+)\]\s*/;function o(q,Q){if(!X("debug"))return;if(Z)return;try{let W="",$=q,x=q.match(y);if(x)W=x[1]??"",$=q.slice(x[0].length);let B;if(Q!=null){if(Q instanceof Error)B={error:`${Q.message}${Q.stack?`
3
3
  ${Q.stack}`:""}`};else if(typeof Q==="object")B=Q}Y("debug",W,$,B)}catch{}}function v(q,Q){if(!X("debug"))return;if(Z)return;try{let W;if(Q!=null){if(typeof Q==="object"&&!(Q instanceof Error))W=Q}Y("debug","relay",q,W)}catch{}}if(!Z)process.on("exit",b);
4
- export{F as rb,L as sb,i as tb,X as ub,h as vb,T as wb,k as xb,G as yb,g as zb,E as Ab,m as Bb,o as Cb,v as Db};
4
+ export{F as Bb,L as Cb,i as Db,X as Eb,h as Fb,T as Gb,k as Hb,G as Ib,g as Jb,E as Kb,m as Lb,o as Mb,v as Nb};