@cortexkit/opencode-anthropic-auth 1.5.0 → 1.6.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 +54 -4
- package/dist/cli.js +1 -1
- package/dist/index-q0d64q8d.js +2 -0
- package/dist/index-zx0zb5c8.js +335 -0
- package/dist/index.js +7 -3
- package/dist/sidebar-state.d.ts +5 -0
- package/dist/sidebar-state.js +1 -1
- package/package.json +4 -4
- package/src/sidebar-state.ts +28 -0
- package/src/tui.tsx +177 -81
- package/dist/index-4wz5npbg.js +0 -325
- package/dist/index-nfgq84t3.js +0 -2
package/dist/index.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import{$ as
|
|
1
|
+
import{$ as v1,A as p1,B as l1,Ba as f0,C as u1,Ca as j1,D as c0,Da as Pj,E as M0,Ea as zj,F as i1,Fa as J1,G as $0,H as n0,I as c1,J as P0,K as z0,L as v0,M as n1,N as o1,O as s1,P as r1,Q as F0,R as q0,S as E0,T as Q0,U as t1,V as C0,W as B1,X as H1,Y as o0,Z as U1,_ as M1,a as I1,aa as x0,b as O1,ba as a1,c as _0,ca as e1,d as B0,da as jj,e as L1,ea as Jj,f as K0,fa as s0,g as k1,ga as Zj,h as N1,ha as $j,i as w1,ia as S0,j as _1,ja as R0,k as d0,ka as Gj,l as f1,la as r0,m as p0,ma as T0,n as l0,na as t0,o as F,oa as y0,p as m1,pa as Vj,q as R,qa as a0,r as Y0,ra as A0,s as b1,sa as Dj,t as H0,ta as Kj,u as U0,ua as k0,v as h1,va as Xj,w as g1,wa as Wj,x as u0,xa as e0,y as d1,z as i0,za as Yj}from"./index-zx0zb5c8.js";import{Ja as Qj}from"./index-q0d64q8d.js";import{randomUUID as pj}from"node:crypto";function Z0(j){return typeof j==="object"&&j!==null}function Fj(j){if(Array.isArray(j))return j;if(Z0(j)&&Array.isArray(j.data))return j.data;return[]}function qj(j){if(!Z0(j)||!Z0(j.info))return;return typeof j.info.role==="string"?j.info.role:void 0}function G1(j){if(!Z0(j)||!Z0(j.info))return null;let J=j.info,Z=Z0(J.model)?J.model:void 0,G=typeof J.agent==="string"?J.agent:void 0,V=typeof Z?.providerID==="string"?Z.providerID:typeof J.providerID==="string"?J.providerID:void 0,Y=typeof Z?.modelID==="string"?Z.modelID:typeof J.modelID==="string"?J.modelID:void 0,v=typeof Z?.variant==="string"?Z.variant:typeof J.variant==="string"?J.variant:void 0;if(!G&&(!V||!Y)&&!v)return null;let u={};if(G)u.agent=G;if(V&&Y)u.model={providerID:V,modelID:Y};if(v)u.variant=v;return u}function V1(j,J){return{agent:j.agent??J.agent,model:j.model??J.model,variant:j.variant??J.variant}}function D1(j){return Boolean(j.agent&&j.model&&j.variant)}async function K1(j,J){if(!j||!J)return null;let Z=j;if(typeof Z.session?.messages!=="function")return null;let G=[];try{G=Fj(await Promise.resolve(Z.session.messages({path:{id:J},query:{limit:100}})))}catch{return null}if(G.length===0)return null;let V={};for(let Y=G.length-1;Y>=0;Y--){if(qj(G[Y])!=="assistant")continue;let v=G1(G[Y]);if(!v)continue;if(V=V1(V,v),D1(V))return V}for(let Y=G.length-1;Y>=0;Y--){let v=G1(G[Y]);if(!v)continue;if(V=V1(V,v),D1(V))return V}if(!V.agent&&!V.model&&!V.variant)return null;return V}function X1(j){return`${I1}${j.charAt(0).toUpperCase()}${j.slice(1)}`}function F1(j){if(j==="StructuredOutput")return j;return`${j.charAt(0).toLowerCase()}${j.slice(1)}`}var Ej=new Set(["callgraph","conflicts","delete","import","inspect","move","navigate","outline","refactor","safety","search","transform","zoom"]);function W1(j){let J=F1(j);if(J.startsWith("aft_"))return J;if(Ej.has(J))return`aft_${J}`;return null}function q1(j,J){let Z=new Headers;if(j instanceof Request)j.headers.forEach((V,Y)=>{Z.set(Y,V)});let G=J?.headers;if(G){if(G instanceof Headers)G.forEach((V,Y)=>{Z.set(Y,V)});else if(Array.isArray(G))for(let V of G){let[Y,v]=V;if(typeof v<"u")Z.set(Y,String(v))}else for(let[V,Y]of Object.entries(G))if(typeof Y<"u")Z.set(V,String(Y))}return Z}function g0(j){return j.set("anthropic-beta",_0(j.get("anthropic-beta"),[O1])),j}function X0(j,J,Z={}){return M1(j,J,Z)}function Cj(j){if(j.tools&&Array.isArray(j.tools))j.tools=j.tools.map((J)=>({...J,name:J.name?X1(J.name):J.name}));if(j.messages&&Array.isArray(j.messages))j.messages=j.messages.map((J)=>{if(J.content&&Array.isArray(J.content))J.content=J.content.map((Z)=>{if(Z.type==="tool_use"&&Z.name)return{...Z,name:X1(Z.name)};return Z});return J});return JSON.stringify(v1(j))}function b0(j){return j.replace(/"name"\s*:\s*"(mcp_)?([^"]+)"/g,(J,Z,G)=>{if(Z)return`"name": "${W1(G)??F1(G)}"`;let V=W1(G);return V?`"name": "${V}"`:J})}function Y1(j,J=!1){if(J)return{ready:b0(j),pending:""};let Z=j.length,G='"name"',V=Math.max(0,j.length-G.length+1);for(let v=V;v<j.length;v++)if(G.startsWith(j.slice(v))){Z=Math.min(Z,v);break}let Y=j.lastIndexOf(G);if(Y!==-1){let v=j.slice(Y);if(/^"name"\s*(?::\s*(?:"[^"]*)?)?$/.test(v))Z=Math.min(Z,Y)}if(Z<j.length)return{ready:b0(j.slice(0,Z)),pending:j.slice(Z)};return{ready:b0(j),pending:""}}function E1(){if(!process.env.ANTHROPIC_BASE_URL?.trim())return!1;let j=process.env.ANTHROPIC_INSECURE?.trim();return j==="1"||j==="true"}function xj(){let j=process.env.ANTHROPIC_BASE_URL?.trim();if(!j)return null;try{let J=new URL(j);if(J.protocol!=="http:"&&J.protocol!=="https:"||J.username||J.password)return null;return J}catch{return null}}function C1(j){let J=null;try{if(typeof j==="string"||j instanceof URL)J=new URL(j.toString());else if(j instanceof Request)J=new URL(j.url)}catch{J=null}if(!J)return{input:j,url:null};let Z=J.href,G=xj();if(G)J.protocol=G.protocol,J.host=G.host;if(J.pathname==="/v1/messages"&&!J.searchParams.has("beta"))J.searchParams.set("beta","true");if(J.href===Z)return{input:j,url:J};return{input:j instanceof Request?new Request(J.toString(),j):J,url:J}}function L0(j){let G=j.split(/\n\n+/).filter((V)=>{if(V.includes(L1))return!1;for(let Y of w1)if(V.includes(Y))return!1;return!0}).join(`
|
|
2
2
|
|
|
3
|
-
`);for(let D of W1)V=V.replace(D.match,D.replacement);return V.trim()}function y(j){return j!=null&&typeof j==="object"&&!Array.isArray(j)}function zj(j){let J={type:"text",text:o};if(j==null)return[J];if(typeof j==="string"){let V=j0(j);if(V===o)return[J];return[J,{type:"text",text:V}]}if(y(j)){let V=typeof j.type==="string"?j.type:"text",D=typeof j.text==="string"?j.text:"";return[J,{...j,type:V,text:j0(D)}]}if(!Array.isArray(j))return[J];let Z=j.map((V)=>{if(typeof V==="string")return{type:"text",text:j0(V)};if(y(V)&&V.type==="text"&&typeof V.text==="string")return{...V,type:"text",text:j0(V.text)};return{type:"text",text:String(V)}});if(Z[0]?.text===o)return Z;return[J,...Z]}var L1={type:"ephemeral",ttl:"1h"},wj=20;function Oj(j){if(y(j.cache_control))return j.cache_control;if(y(j.cacheControl))return j.cacheControl;return null}function F0(j,J){if(!y(j))return!1;return delete j.cacheControl,j.cache_control=J?{...L1}:{type:"ephemeral"},!0}function e0(j){if(!y(j))return;delete j.cache_control,delete j.cacheControl}function Lj(j){if(Array.isArray(j))return j;if(typeof j==="string")return[{type:"text",text:j}];return null}function J0(j,J){if(!y(j))return;let Z=Oj(j);if(Z?.type!=="ephemeral")return;if(J)Z.ttl="1h";else delete Z.ttl}function j1(j,J){if(Array.isArray(j.system))for(let Z of j.system)J0(Z,J);else J0(j.system,J);if(!Array.isArray(j.messages))return;for(let Z of j.messages)if(J0(Z,J),y(Z)&&Array.isArray(Z.content))for(let V of Z.content)J0(V,J)}function Nj(j,J){if(Array.isArray(j.system))for(let Z of j.system)J(Z);else J(j.system);if(!Array.isArray(j.messages))return;for(let Z of j.messages)if(J(Z),y(Z)&&Array.isArray(Z.content))for(let V of Z.content)J(V)}function N1(j){e0(j),Nj(j,e0)}function _j(j){N1(j),j.cache_control={...L1}}function Z0(j){if(!y(j))return!1;let J=Lj(j.content);if(!J?.length)return!1;let Z=[...J].reverse().find((V)=>y(V)&&V.type!=="thinking"&&V.type!=="redacted_thinking");if(!Z)return!1;return j.content=J,F0(Z,!0)}function Uj(j){if(!y(j))return 0;let J=j.content;if(Array.isArray(J))return J.length;if(J==null)return 0;return 1}function Mj(j){let J=[],Z=0;return j.forEach((V,D)=>{let Y=Uj(V);if(y(V)&&V.role==="user"&&Y>0)J.push({index:D,blockPosition:Z+Y-1});Z+=Y}),J}function vj(j){let J=Mj(j),Z=[...J].reverse().find((B)=>B.index>1);if(!Z)return{latest:void 0,bridge:void 0};let V=[...J].reverse().find((B)=>B.index<Z.index),D=V?Z.blockPosition-V.blockPosition+1:0,Y=V&&V.index>1&&D>wj?V:void 0;return{latest:Z,bridge:Y}}function J1(j){if(Array.isArray(j.system)){let J=j.system.findIndex((D)=>y(D)&&D.text===o),Z=j.system.slice(J>=0?J+1:0).filter(y),V=Z[Z.length-1];F0(V,!0)}else F0(j.system,!0)}function Fj(j){if(N1(j),!Array.isArray(j.messages)){J1(j);return}let{latest:J,bridge:Z}=vj(j.messages);if(!Z)J1(j);if(Z0(j.messages[0]),Z0(j.messages[1]),Z)Z0(j.messages[Z.index]);if(J)Z0(j.messages[J.index])}function Bj(j,J){if(!J.enabled){j1(j,!1),delete j.cache_control,delete j.cacheControl;return}if(J.mode==="automatic"){_j(j);return}if(J.mode==="hybrid"){Fj(j);return}j1(j,!0),delete j.cacheControl}function Hj(j){if(!Array.isArray(j.messages))return;while(j.messages.length){let J=j.messages[j.messages.length-1];if(!y(J)||J.role!=="assistant")break;j.messages.pop()}}async function _1(j,J={}){try{let Z=JSON.parse(j);Hj(Z);let V=Array.isArray(Z.messages)&&Z.messages.some((D)=>D.role==="user")?K1(Z.messages,void 0,D1):null;if(Z.system=zj(Z.system),V&&Array.isArray(Z.system))Z.system.unshift({type:"text",text:V});if(Bj(Z,{enabled:J.cache1hEnabled??!1,mode:J.cache1hMode??"explicit"}),J.fastModeEnabled&&V0(Z.model))Z.speed="fast";else if(Z.speed==="fast")delete Z.speed;if(J.identity)Q1(Z,J.identity);return await Y1(Pj(Z))}catch{return j}}function D0(j){if(!j.body)return j;let J=j.body.getReader(),Z=new TextDecoder,V=new TextEncoder,D="",Y=new ReadableStream({async pull(B){let{done:k,value:g}=await J.read();if(k){let h=t0(`${D}${Z.decode()}`,!0);if(h.ready)B.enqueue(V.encode(h.ready));B.close();return}let b=D+Z.decode(g,{stream:!0}),m=t0(b);if(D=m.pending,m.ready)B.enqueue(V.encode(m.ready))}});return new Response(Y,{status:j.status,statusText:j.statusText,headers:j.headers})}var Ej="__OPENCODE_ANTHROPIC_AUTH_COMMAND_HANDLED__",xj=60000,Cj=60000,Sj=5000,U1=200,r1=240,Rj=r1,yj=1,M1=!1;function U0(){return process.env.OPENCODE_ANTHROPIC_AUTH_PERF==="1"}function H(){return performance.now()}function R(j){return Math.round(j*10)/10}function v1(j){return Math.floor(Math.random()*Math.max(0,j))}function fj(){if(M1||!1||!U0())return;M1=!0;let j=100,J=250,Z=H()+j;setInterval(()=>{let V=H(),D=V-Z;if(Z=V+j,D<J)return;q("[perf] opencode event_loop_lag",{lagMs:R(D),thresholdMs:J})},j).unref?.()}function Tj(j){let J=H(),Z={requestId:String(yj++),start:J,last:J,mark(V,D){let Y=H();if(U0())q("[perf] opencode request stage",{requestId:Z.requestId,stage:V,deltaMs:R(Y-Z.last),totalMs:R(Y-Z.start),...D});Z.last=Y},done(V,D){let Y=H();if(U0())q("[perf] opencode request done",{requestId:Z.requestId,stage:V,deltaMs:R(Y-Z.last),totalMs:R(Y-Z.start),...D});Z.last=Y}};if(U0())q("[perf] opencode request start",{requestId:Z.requestId,...j});return Z}async function p(j,J,Z){let V=j.client.session,D=await s0(j.client,J),Y={path:{id:J},body:{noReply:!0,parts:[{type:"text",text:Z,ignored:!0}]}};if(D?.agent)Y.body.agent=D.agent;if(D?.model)Y.body.model=D.model;if(D?.variant)Y.body.variant=D.variant;if(typeof V?.promptAsync==="function"){await V.promptAsync(Y);return}if(typeof V?.prompt==="function"){await Promise.resolve(V.prompt(Y));return}throw Error("OpenCode session prompt API is unavailable for ignored replies.")}function u(){throw Error(Ej)}function Aj(j){if(j.sessionID==null)return!1;let J=j.model;return J?.providerID==="anthropic"||J?.api?.npm==="@ai-sdk/anthropic"||J?.api?.npm==="@ai-sdk/google-vertex/anthropic"}function kj(j){if(j.some((J)=>J.includes("<use_parallel_tool_calls>")))return!1;return j.push(F1),!0}var cj=async(j)=>{fj();let{client:J}=j,Z=H1(),V=await C(Z),D=new u1({storage:V,onMainQuotaFetched:async($,W,K)=>{try{let I=await C(Z)??{version:1,accounts:[]};I.quota=I.quota??{},I.quota.mainQuota=$,I.quota.mainQuotaCheckedAt=W,I.quota.mainQuotaToken=K,I.quota.mainLastQuotaApiError=void 0,await a(I,Z)}catch(I){q("[quota] failed to persist main quota",{error:I instanceof Error?I.message:String(I)})}},onApiError:async($)=>{try{let W=await C(Z)??{version:1,accounts:[]};W.quota=W.quota??{},W.quota.mainLastQuotaApiError=$,await a(W,Z)}catch(W){q("[quota] failed to persist backoff state",{error:W instanceof Error?W.message:String(W)})}}}),Y=new T1({quotaManager:D});Y.startBackgroundRefresh();let B=null,k=new d1({loadStorage:()=>C(Z),prepareHeaders:async($,W)=>{if(!b)return $;let K=await b();if(K.type!=="oauth")return $;if(!K.access||K.expires&&K.expires<Date.now()){if(!B)return $;K.access=await B()}if(!K.access)return $;try{let I=JSON.parse(W.bodyText),_=await f0(K.access,typeof I.model==="string"?I.model:void 0);if($.delete("anthropic-beta"),s($,K.access,{body:I,identity:_}),$.set("anthropic-beta",$0($.get("anthropic-beta"),[k1])),I.speed==="fast")B0($)}catch{s($,K.access)}return $},log:q});r({enabled:X0(V),mode:W0(V)}),w0(x0(V)),L0(C0(V));function g($,W){let K=D.getMain(W.mainAccessToken),I=D.getLastApiError(),_=$?.refresh?.mainLastRefreshError,f={main:{quota:K?.quota??null,quotaBackedOff:D.isBackedOff(),quotaBackoffUntil:I?.nextRetryAt,refreshBackedOff:_?i(_,W.mainRefreshToken,Date.now()):!1,refreshBackoffUntil:_?.nextRetryAt},fallbacks:($?.accounts??[]).filter((E)=>E.enabled!==!1).map((E)=>({id:E.id,label:E.label,quota:E.access?D.getFallback(E.id,E.access)?.quota??null:null,enabled:E.enabled!==!1})),activeId:W.activeId,route:W.route,relay:(()=>{let E=m0($);return E?{enabled:!0,transport:E.transport??"http"}:null})(),fastMode:k0(),cacheKeep:{enabled:T0($),window:$?.cacheKeep?.startHour!=null&&$?.cacheKeep?.endHour!=null?`${$.cacheKeep.startHour}-${$.cacheKeep.endHour}`:void 0,trackedSessions:k.trackedCount()},lastUpdated:Date.now()};a1(f).catch((E)=>q("[sidebar] state write failed",{error:E instanceof Error?E.message:String(E)}))}let b=null,m=null,h=0;function t1($){let W=$?.refresh?.refreshBeforeExpiryMinutes??Rj;return Math.max(r1,W)*60000}function e1($){return $?.refresh?.enabled!==!1}async function d0($){if(!$)return;let W=await C(Z),K=W?.refresh?.mainLastRefreshError;if(!W?.refresh||!K?.tokenHash)return;let I=Y0($);if(K.tokenHash===I)return;if(K.nextRetryAt&&K.nextRetryAt>Date.now()){q("[refresh] opencode main oauth keeping backoff despite token rotation",{nextRetryAt:K.nextRetryAt,retryCount:K.retryCount,remainingMs:K.nextRetryAt-Date.now()});return}W.refresh.mainLastRefreshError=void 0,await a(W,Z),q("[refresh] opencode main oauth cleared stale backoff after token rotation",{previousCheckedAt:K.checkedAt,previousNextRetryAt:K.nextRetryAt,previousRetryCount:K.retryCount})}async function jj(){let $=[];if(b)try{let _=await b();if(_.type==="oauth"&&_.access){let f=await D.refreshMain(_.access);$.push({name:"OpenCode anthropic",role:"main",quota:f})}else if(_.type==="oauth")$.push({name:"OpenCode anthropic",role:"main",error:"missing access token; send a request first or reconnect auth"})}catch(_){let f=_ instanceof Error?_.message:String(_);$.push({name:"OpenCode anthropic",role:"main",error:f.includes("429")?"Usage API rate limited — try again in a moment":f})}let{storage:W,errors:K}=await Y.refreshQuotaForAllAccounts({force:!0}),I=new Map(K.map((_)=>[_.accountId,_.message]));if($.push(...i1(W,I)),!b)$.unshift({name:"OpenCode anthropic",role:"main",error:"auth loader has not run yet; send a request first"});return c1({accounts:$,refreshedAt:Date.now()})}async function Jj($){let W=A1($);if(W.type==="enable"||W.type==="disable"){let f=W.type==="enable",E=await E1(f),t=W0(E);return r({enabled:f,mode:t}),P0({argumentsText:$,enabled:f,mode:t})}if(W.type==="mode"){let f=await x1(W.mode),E=X0(f);return r({enabled:E,mode:W.mode}),P0({argumentsText:$,enabled:E,mode:W.mode})}let K=await C(Z),I=X0(K),_=W0(K);return r({enabled:I,mode:_}),P0({argumentsText:$,enabled:I,mode:_})}async function Zj($){let W=b1($),K=await C(Z);if(W.type==="window")K=await R1(W.startHour,W.endHour);else if(W.type==="disable")K=await y1(!1);let I=m1(K),_=k.stats(I);return h1({argumentsText:$,enabled:g1(K),window:I,hybridActive:T0(K),trackedSessions:_.trackedSessions,nextPrewarmAt:_.nextPrewarmAt})}async function $j($){let W=l1($);if(W.type==="enable"||W.type==="disable"){let _=W.type==="enable";return await C1(_),w0(_),A0({argumentsText:$,enabled:_})}let K=await C(Z),I=x0(K);return w0(I),A0({argumentsText:$,enabled:I})}async function Vj($){let W=p1($);if(W.type==="enable"||W.type==="disable"){let _=W.type==="enable";return await S1(_),L0(_),b0({argumentsText:$,enabled:_})}let K=await C(Z),I=C0(K);return L0(I),b0({argumentsText:$,enabled:I})}async function Dj($){let W=s1($);if(W.type==="mode")return await o1(W.mode),h0({argumentsText:$,mode:W.mode});let K=await C();return h0({argumentsText:$,mode:g0(K)})}return{config:async($)=>{$.command={...$.command??{},[K0]:{template:K0,description:"Show or toggle 1-hour Anthropic ephemeral prompt cache TTL."},[I0]:{template:I0,description:"Keep hybrid Claude cache warm for recently used sessions during a local time window."},[N0]:{template:N0,description:"Show current Claude OAuth quota usage for all accounts."},[z0]:{template:z0,description:"Show or toggle Anthropic request dump capture for debugging."},[O0]:{template:O0,description:"Show or toggle Anthropic fast mode for supported Opus models."},[_0]:{template:_0,description:"Show or change Claude account routing between main-first and fallback-first."}}},"experimental.chat.system.transform":async($,W)=>{if(!Aj($))return;kj(W.system)},"command.execute.before":async($)=>{if($.command===K0)await p(j,$.sessionID,await Jj($.arguments)),u();if($.command===I0)await p(j,$.sessionID,await Zj($.arguments)),u();if($.command===N0){await p(j,$.sessionID,await jj());let W=await C(),K=b?await b().catch(()=>{return}):void 0;g(W,{activeId:"main",route:"main",mainAccessToken:K?.access,mainRefreshToken:K?.refresh}),u()}if($.command===z0)await p(j,$.sessionID,await $j($.arguments)),u();if($.command===O0)await p(j,$.sessionID,await Vj($.arguments)),u();if($.command===_0)await p(j,$.sessionID,await Dj($.arguments)),u()},auth:{provider:"anthropic",async loader($,W){b=$;let K=await $();if(K.type==="oauth"){let f=function(){if(m)clearInterval(m),m=null;let P=async()=>{try{let w=await C(Z);if(!e1(w))return;let v=await $();if(v.type!=="oauth")return;if(await d0(v.refresh),!v.expires)return;let N=v.expires-Date.now(),x=t1(w);if(N>x)return;if(q("[refresh] opencode main oauth background due",{expiresInMs:N,refreshBeforeMs:x}),v.refresh&&i(w?.refresh?.mainLastRefreshError,v.refresh,Date.now())){q("[refresh] opencode main oauth background skipped backoff",{nextRetryAt:w?.refresh?.mainLastRefreshError?.nextRetryAt,retryCount:w?.refresh?.mainLastRefreshError?.retryCount,expiresInMs:N});return}if(v.refresh&&w?.refresh?.mainRefreshLeaseUntil&&w.refresh.mainRefreshLeaseUntil>Date.now()&&w.refresh.mainRefreshLeaseTokenHash===Y0(v.refresh))return;await _();let Q=await $();q("[refresh] opencode main oauth refreshed in background",{newExpiresInMs:Q.expires?Q.expires-Date.now():void 0})}catch(w){q("[refresh] opencode main oauth refresh failed",{message:w instanceof Error?w.message:String(w)})}};if(m=setInterval(()=>{P()},xj+v1(Cj)),"unref"in m)m.unref()},E=function(P,w){if(P instanceof Request&&P.body)return!1;return w==null||typeof w==="string"},t=function(P){return P.has("x-parent-session-id")},l0=function(P){return P.includes("rate_limit_error")||/exceed your account'?s rate limit/i.test(P)},Xj=function(P){return P?.quota?.enabled===!0};for(let P of Object.values(W.models))P.cost={input:0,output:0,cache:{read:0,write:0}};let I=null;async function _(){if(!I)I=(async()=>{let v=null,N=null,x=null;async function Q(G){let U=await C(Z)??{version:1,main:{type:"opencode",provider:"anthropic"},accounts:[]};U.refresh=U.refresh??{},G(U),await a(U,Z)}async function S(G){let U=Date.now()+Sj;while(Date.now()<U){await new Promise((M)=>setTimeout(M,U1+v1(U1)));let X=await $();if(X.type!=="oauth"||!X.access)continue;if((X.access!==G.access||X.refresh!==G.refresh||(X.expires??0)>(G.expires??0)+60000)&&(!X.expires||X.expires>Date.now()))return q("[refresh] opencode main oauth joined concurrent refresh",{expiresInMs:X.expires?X.expires-Date.now():void 0}),X.access}return null}for(let G=0;G<=2;G++){let U=null;try{if(G>0){let L=500*2**(G-1);await new Promise((A)=>setTimeout(A,L))}if(U=await $(),!U.refresh)throw Error("Token refresh failed: missing refresh token");let X=await C(Z),O=Y0(U.refresh),M=X?.refresh?.mainLastRefreshError;if(q("[refresh] opencode main oauth refresh check",{attempt:G,expiresInMs:U.expires?U.expires-Date.now():void 0,hasBackoff:Boolean(M),backoffActive:M?i(M,U.refresh,Date.now()):!1,retryCount:M?.retryCount,nextRetryAt:M?.nextRetryAt}),M&&i(M,U.refresh,Date.now()))throw q("[refresh] opencode main oauth refresh skipped backoff",{nextRetryAt:M.nextRetryAt,retryCount:M.retryCount}),Error(R0(M,Date.now()));if(X?.refresh?.mainRefreshLeaseUntil&&X.refresh.mainRefreshLeaseUntil>Date.now()&&X.refresh.mainRefreshLeaseTokenHash===O){q("[refresh] opencode main oauth refresh skipped lease",{leaseUntil:X.refresh.mainRefreshLeaseUntil});let L=await S(U);if(L)return L;throw Error("Claude OAuth refresh is already in progress")}let z=await q1({name:"opencode-main-oauth-refresh",ttlMs:120000,renew:!0});if(!z){q("[refresh] opencode main oauth refresh skipped file lock");let L=await S(U);if(L)return L;throw Error("Claude OAuth refresh is already in progress")}x=z.release,v=qj(),N=O,await Q((L)=>{L.refresh=L.refresh??{},L.refresh.mainRefreshLeaseId=v??void 0,L.refresh.mainRefreshLeaseUntil=Date.now()+120000,L.refresh.mainRefreshLeaseTokenHash=O});let T=await C(Z);if(q("[refresh] opencode main oauth refresh lease acquired",{attempt:G,leaseUntil:Date.now()+120000}),T?.refresh?.mainRefreshLeaseId!==v||T.refresh.mainRefreshLeaseTokenHash!==O)throw Error("Claude OAuth refresh is already in progress");q("[refresh] opencode main oauth refresh request start",{attempt:G});let F=await B1({refreshToken:U.refresh,maxRetries:0});return await J.auth.set({path:{id:"anthropic"},body:{type:"oauth",refresh:F.refresh,access:F.access,expires:F.expires}}),await Q((L)=>{if(!L?.refresh)return;if(L.refresh.mainLastRefreshError=void 0,L.refresh.mainRefreshLeaseId===v)L.refresh.mainRefreshLeaseId=void 0,L.refresh.mainRefreshLeaseUntil=void 0,L.refresh.mainRefreshLeaseTokenHash=void 0}),q("[refresh] opencode main oauth refresh succeeded",{attempt:G,expiresInMs:F.expires-Date.now()}),F.access}catch(X){let O=X instanceof Error&&(X.message.includes("fetch failed")||("code"in X)&&(X.code==="ECONNRESET"||X.code==="ECONNREFUSED"||X.code==="ETIMEDOUT"||X.code==="UND_ERR_CONNECT_TIMEOUT"));if(G<2&&(O||X instanceof H0&&X.status>=500))continue;q("[refresh] opencode main oauth refresh attempt failed",{attempt:G,error:X instanceof Error?X.message:String(X),transient:O});let M=U?.refresh;if(M&&X instanceof H0)await Q((z)=>{z.refresh=z.refresh??{},z.refresh.mainLastRefreshError=f1({error:X,now:Date.now(),refreshToken:M,previous:z.refresh.mainLastRefreshError})});throw X}finally{if(v)await Q((X)=>{if(!X?.refresh)return;if(X.refresh.mainRefreshLeaseId===v&&X.refresh.mainRefreshLeaseTokenHash===N)X.refresh.mainRefreshLeaseId=void 0,X.refresh.mainRefreshLeaseUntil=void 0,X.refresh.mainRefreshLeaseTokenHash=void 0}).catch(()=>{});await x?.().catch(()=>{})}}throw Error("Token refresh exhausted all retries")})().finally(()=>{I=null});return I}B=_,f(),D.seedFallbacksFromAccounts(V?.accounts??[]),g(V,{activeId:"main",route:"main",mainAccessToken:K.access,mainRefreshToken:K.refresh});async function p0(P,w){if(!P.body||P.status!==200)return w?.mark("inspect_stream_skip",{status:P.status}),{response:P,rateLimited:!1};if(P.headers.get("x-cortexkit-relay-optimistic")==="true")return w?.mark("inspect_stream_skip",{status:P.status,reason:"optimistic_relay"}),{response:P,rateLimited:!1};let v=H(),N=P.body.getReader(),x=[],Q=new TextDecoder,S="",G=0;while(!S.includes(`
|
|
3
|
+
`);for(let V of _1)G=G.replace(V.match,V.replacement);return G.trim()}function f(j){return j!=null&&typeof j==="object"&&!Array.isArray(j)}function Sj(j){let J={type:"text",text:K0};if(j==null)return[J];if(typeof j==="string"){let G=L0(j);if(G===K0)return[J];return[J,{type:"text",text:G}]}if(f(j)){let G=typeof j.type==="string"?j.type:"text",V=typeof j.text==="string"?j.text:"";return[J,{...j,type:G,text:L0(V)}]}if(!Array.isArray(j))return[J];let Z=j.map((G)=>{if(typeof G==="string")return{type:"text",text:L0(G)};if(f(G)&&G.type==="text"&&typeof G.text==="string")return{...G,type:"text",text:L0(G.text)};return{type:"text",text:String(G)}});if(Z[0]?.text===K0)return Z;return[J,...Z]}var x1={type:"ephemeral",ttl:"1h"},Rj=20;function Tj(j){if(f(j.cache_control))return j.cache_control;if(f(j.cacheControl))return j.cacheControl;return null}function h0(j,J){if(!f(j))return!1;return delete j.cacheControl,j.cache_control=J?{...x1}:{type:"ephemeral"},!0}function P1(j){if(!f(j))return;delete j.cache_control,delete j.cacheControl}function yj(j){if(Array.isArray(j))return j;if(typeof j==="string")return[{type:"text",text:j}];return null}function N0(j,J){if(!f(j))return;let Z=Tj(j);if(Z?.type!=="ephemeral")return;if(J)Z.ttl="1h";else delete Z.ttl}function z1(j,J){if(Array.isArray(j.system))for(let Z of j.system)N0(Z,J);else N0(j.system,J);if(!Array.isArray(j.messages))return;for(let Z of j.messages)if(N0(Z,J),f(Z)&&Array.isArray(Z.content))for(let G of Z.content)N0(G,J)}function Aj(j,J){if(Array.isArray(j.system))for(let Z of j.system)J(Z);else J(j.system);if(!Array.isArray(j.messages))return;for(let Z of j.messages)if(J(Z),f(Z)&&Array.isArray(Z.content))for(let G of Z.content)J(G)}function S1(j){P1(j),Aj(j,P1)}function kj(j){S1(j),j.cache_control={...x1}}function w0(j){if(!f(j))return!1;let J=yj(j.content);if(!J?.length)return!1;let Z=[...J].reverse().find((G)=>f(G)&&G.type!=="thinking"&&G.type!=="redacted_thinking");if(!Z)return!1;return j.content=J,h0(Z,!0)}function fj(j){if(!f(j))return 0;let J=j.content;if(Array.isArray(J))return J.length;if(J==null)return 0;return 1}function mj(j){let J=[],Z=0;return j.forEach((G,V)=>{let Y=fj(G);if(f(G)&&G.role==="user"&&Y>0)J.push({index:V,blockPosition:Z+Y-1});Z+=Y}),J}function bj(j){let J=mj(j),Z=[...J].reverse().find((v)=>v.index>1);if(!Z)return{latest:void 0,bridge:void 0};let G=[...J].reverse().find((v)=>v.index<Z.index),V=G?Z.blockPosition-G.blockPosition+1:0,Y=G&&G.index>1&&V>Rj?G:void 0;return{latest:Z,bridge:Y}}function Q1(j){if(Array.isArray(j.system)){let J=j.system.findIndex((V)=>f(V)&&V.text===K0),Z=j.system.slice(J>=0?J+1:0).filter(f),G=Z[Z.length-1];h0(G,!0)}else h0(j.system,!0)}function hj(j){if(S1(j),!Array.isArray(j.messages)){Q1(j);return}let{latest:J,bridge:Z}=bj(j.messages);if(!Z)Q1(j);if(w0(j.messages[0]),w0(j.messages[1]),Z)w0(j.messages[Z.index]);if(J)w0(j.messages[J.index])}function gj(j,J){if(!J.enabled){z1(j,!1),delete j.cache_control,delete j.cacheControl;return}if(J.mode==="automatic"){kj(j);return}if(J.mode==="hybrid"){hj(j);return}z1(j,!0),delete j.cacheControl}function dj(j){if(!Array.isArray(j.messages))return;while(j.messages.length){let J=j.messages[j.messages.length-1];if(!f(J)||J.role!=="assistant")break;j.messages.pop()}}async function R1(j,J={}){try{let Z=JSON.parse(j);dj(Z);let G=Array.isArray(Z.messages)&&Z.messages.some((V)=>V.role==="user")?H1(Z.messages,void 0,N1):null;if(Z.system=Sj(Z.system),G&&Array.isArray(Z.system))Z.system.unshift({type:"text",text:G});if(gj(Z,{enabled:J.cache1hEnabled??!1,mode:J.cache1hMode??"explicit"}),J.fastModeEnabled&&B0(Z.model))Z.speed="fast";else if(Z.speed==="fast")delete Z.speed;if(J.identity)U1(Z,J.identity);return await B1(Cj(Z))}catch{return j}}function W0(j){if(!j.body)return j;let J=j.body.getReader(),Z=new TextDecoder,G=new TextEncoder,V="",Y=new ReadableStream({async pull(v){let{done:u,value:G0}=await J.read();if(u){let d=Y1(`${V}${Z.decode()}`,!0);if(d.ready)v.enqueue(G.encode(d.ready));v.close();return}let o=V+Z.decode(G0,{stream:!0}),e=Y1(o);if(V=e.pending,e.ready)v.enqueue(G.encode(e.ready))}});return new Response(Y,{status:j.status,statusText:j.statusText,headers:j.headers})}var lj="__OPENCODE_ANTHROPIC_AUTH_COMMAND_HANDLED__",uj=60000,ij=60000,cj=5000,T1=200,Ij=240,nj=Ij,oj=1,y1=!1;function m0(){return process.env.OPENCODE_ANTHROPIC_AUTH_PERF==="1"}function q(){return performance.now()}function y(j){return Math.round(j*10)/10}function A1(j){return Math.floor(Math.random()*Math.max(0,j))}function sj(){if(y1||!1||!m0())return;y1=!0;let j=100,J=250,Z=q()+j;setInterval(()=>{let G=q(),V=G-Z;if(Z=G+j,V<J)return;F("[perf] opencode event_loop_lag",{lagMs:y(V),thresholdMs:J})},j).unref?.()}function rj(j){let J=q(),Z={requestId:String(oj++),start:J,last:J,mark(G,V){let Y=q();if(m0())F("[perf] opencode request stage",{requestId:Z.requestId,stage:G,deltaMs:y(Y-Z.last),totalMs:y(Y-Z.start),...V});Z.last=Y},done(G,V){let Y=q();if(m0())F("[perf] opencode request done",{requestId:Z.requestId,stage:G,deltaMs:y(Y-Z.last),totalMs:y(Y-Z.start),...V});Z.last=Y}};if(m0())F("[perf] opencode request start",{requestId:Z.requestId,...j});return Z}async function t(j,J,Z){let G=j.client.session,V=await K1(j.client,J),Y={path:{id:J},body:{noReply:!0,parts:[{type:"text",text:Z,ignored:!0}]}};if(V?.agent)Y.body.agent=V.agent;if(V?.model)Y.body.model=V.model;if(V?.variant)Y.body.variant=V.variant;if(typeof G?.promptAsync==="function"){await G.promptAsync(Y);return}if(typeof G?.prompt==="function"){await Promise.resolve(G.prompt(Y));return}throw Error("OpenCode session prompt API is unavailable for ignored replies.")}function a(){throw Error(lj)}function tj(j){if(j.sessionID==null)return!1;let J=j.model;return J?.providerID==="anthropic"||J?.api?.npm==="@ai-sdk/anthropic"||J?.api?.npm==="@ai-sdk/google-vertex/anthropic"}function aj(j){if(j.some((J)=>J.includes("<use_parallel_tool_calls>")))return!1;return j.push(k1),!0}var XJ=async(j)=>{sj();let{client:J}=j,Z=m1(),G=await R(Z),V=new Kj({storage:G,onMainQuotaFetched:async($,D,X)=>{try{let W=await R(Z)??{version:1,accounts:[]};W.quota=W.quota??{},W.quota.mainQuota=$,W.quota.mainQuotaCheckedAt=D,W.quota.mainQuotaToken=X,W.quota.mainLastQuotaApiError=void 0,await Y0(W,Z)}catch(W){F("[quota] failed to persist main quota",{error:W instanceof Error?W.message:String(W)})}},onApiError:async($)=>{try{let D=await R(Z)??{version:1,accounts:[]};D.quota=D.quota??{},D.quota.mainLastQuotaApiError=$,await Y0(D,Z)}catch(D){F("[quota] failed to persist backoff state",{error:D instanceof Error?D.message:String(D)})}}}),Y=new r1({quotaManager:V,onFallbackStorageChanged:()=>{e()}});Y.startBackgroundRefresh();let v=null,u=new $j({loadStorage:()=>R(Z),prepareHeaders:async($,D)=>{if(!d)return $;let X=await d();if(X.type!=="oauth")return $;if(!X.access||X.expires&&X.expires<Date.now()){if(!v)return $;X.access=await v()}if(!X.access)return $;try{let W=JSON.parse(D.bodyText),P=await o0(X.access,typeof W.model==="string"?W.model:void 0);if($.delete("anthropic-beta"),X0($,X.access,{body:W,identity:P}),$.set("anthropic-beta",_0($.get("anthropic-beta"),[a1])),W.speed==="fast")g0($)}catch{X0($,X.access)}return $},log:F});Q0({enabled:H0(G),mode:U0(G)}),R0(u0(G)),y0(i0(G));let G0={activeId:"main",route:"main"};function o($,D){G0={activeId:D.activeId,route:D.route};let X=V.getMain(D.mainAccessToken),W=V.getLastApiError(),P=$?.refresh?.mainLastRefreshError,x={main:{quota:X?.quota??null,quotaBackedOff:V.isBackedOff(),quotaBackoffUntil:W?.nextRetryAt,refreshBackedOff:P?$0(P,D.mainRefreshToken,Date.now()):!1,refreshBackoffUntil:P?.nextRetryAt},fallbacks:($?.accounts??[]).filter((E)=>E.enabled!==!1).map((E)=>({id:E.id,label:E.label,quota:E.access?V.getFallback(E.id,E.access)?.quota??null:null,enabled:E.enabled!==!1})),activeId:D.activeId,route:D.route,relay:(()=>{let E=e0($);return E?{enabled:!0,transport:E.transport??"http"}:null})(),fastMode:t0(),cacheKeep:{enabled:s0($),window:$?.cacheKeep?.startHour!=null&&$?.cacheKeep?.endHour!=null?`${$.cacheKeep.startHour}-${$.cacheKeep.endHour}`:void 0,trackedSessions:u.trackedCount()},lastUpdated:Date.now()};Qj(x).catch((E)=>F("[sidebar] state write failed",{error:E instanceof Error?E.message:String(E)}))}async function e(){let $=await R(Z),D,X;if(d)try{let W=await d();D=W.access,X=W.refresh}catch{}o($,{activeId:G0.activeId,route:G0.route,mainAccessToken:D,mainRefreshToken:X})}let d=null,j0=null,I0=0;function Oj($){let D=$?.refresh?.refreshBeforeExpiryMinutes??nj;return Math.max(Ij,D)*60000}function Lj($){return $?.refresh?.enabled!==!1}async function Z1($){if(!$)return;let D=await R(Z),X=D?.refresh?.mainLastRefreshError;if(!D?.refresh||!X?.tokenHash)return;let W=M0($);if(X.tokenHash===W)return;if(X.nextRetryAt&&X.nextRetryAt>Date.now()){F("[refresh] opencode main oauth keeping backoff despite token rotation",{nextRetryAt:X.nextRetryAt,retryCount:X.retryCount,remainingMs:X.nextRetryAt-Date.now()});return}D.refresh.mainLastRefreshError=void 0,await Y0(D,Z),F("[refresh] opencode main oauth cleared stale backoff after token rotation",{previousCheckedAt:X.checkedAt,previousNextRetryAt:X.nextRetryAt,previousRetryCount:X.retryCount})}async function Nj(){let $=[];if(d)try{let P=await d();if(P.type==="oauth"&&P.access){let x=await V.refreshMain(P.access);$.push({name:"OpenCode anthropic",role:"main",quota:x})}else if(P.type==="oauth")$.push({name:"OpenCode anthropic",role:"main",error:"missing access token; send a request first or reconnect auth"})}catch(P){let x=P instanceof Error?P.message:String(P);$.push({name:"OpenCode anthropic",role:"main",error:x.includes("429")?"Usage API rate limited — try again in a moment":x})}let{storage:D,errors:X}=await Y.refreshQuotaForAllAccounts({force:!0}),W=new Map(X.map((P)=>[P.accountId,P.message]));if($.push(...Xj(D,W)),!d)$.unshift({name:"OpenCode anthropic",role:"main",error:"auth loader has not run yet; send a request first"});return Wj({accounts:$,refreshedAt:Date.now()})}async function wj($){let D=t1($);if(D.type==="enable"||D.type==="disable"){let x=D.type==="enable",E=await h1(x),h=U0(E);return Q0({enabled:x,mode:h}),C0({argumentsText:$,enabled:x,mode:h})}if(D.type==="mode"){let x=await g1(D.mode),E=H0(x);return Q0({enabled:E,mode:D.mode}),C0({argumentsText:$,enabled:E,mode:D.mode})}let X=await R(Z),W=H0(X),P=U0(X);return Q0({enabled:W,mode:P}),C0({argumentsText:$,enabled:W,mode:P})}async function _j($){let D=e1($),X=await R(Z);if(D.type==="window")X=await l1(D.startHour,D.endHour);else if(D.type==="disable")X=await u1(!1);let W=jj(X),P=u.stats(W);return Zj({argumentsText:$,enabled:Jj(X),window:W,hybridActive:s0(X),trackedSessions:P.trackedSessions,nextPrewarmAt:P.nextPrewarmAt})}async function Bj($){let D=Gj($);if(D.type==="enable"||D.type==="disable"){let P=D.type==="enable";return await d1(P),R0(P),r0({argumentsText:$,enabled:P})}let X=await R(Z),W=u0(X);return R0(W),r0({argumentsText:$,enabled:W})}async function Hj($){let D=Vj($);if(D.type==="enable"||D.type==="disable"){let P=D.type==="enable";return await p1(P),y0(P),a0({argumentsText:$,enabled:P})}let X=await R(Z),W=i0(X);return y0(W),a0({argumentsText:$,enabled:W})}async function Uj($){let D=zj($);if(D.type==="mode")return await Pj(D.mode),J1({argumentsText:$,mode:D.mode});let X=await R();return J1({argumentsText:$,mode:j1(X)})}function Mj($,D=10){let X=Math.max(0,Math.min(Math.round($/100*D),D));return"█".repeat(X)+"░".repeat(D-X)}function O0($,D){return`${$} ${Mj(D)} ${String(Math.round(D)).padStart(3)}%`}function $1($){if(!$)return"";let D=new Date($).getTime();if(Number.isNaN(D))return"";let X=D-Date.now();if(X<=0)return"resets now";let W=Math.floor(X/60000);if(W<1)return"resets <1m";if(W<60)return`resets ${W}m`;let P=Math.floor(W/60),x=W%60;return x>0?`resets ${P}h${x}m`:`resets ${P}h`}function vj($,D,X){let W=[],P=0;if($){let{five_hour:h,seven_day:p}=$;if(h||p){let i=X==="main"?"active":"idle",s=$1(h?.resetsAt),n=[`main · ${i}${s?` (${s})`:""}`];if(h)n.push(O0("5h",h.usedPercent)),P=Math.max(P,h.usedPercent);if(p)n.push(O0("7d",p.usedPercent)),P=Math.max(P,p.usedPercent);W.push(n.join(`
|
|
4
|
+
`))}}if(D?.length)for(let h of D){let p=h.quota;if(!p)continue;let{five_hour:c,seven_day:i}=p;if(!c&&!i)continue;let s=h.label||"alt",V0=X===h.id?"active":"idle",r=$1(c?.resetsAt),D0=[`${s} · ${V0}${r?` (${r})`:""}`];if(c)D0.push(O0("5h",c.usedPercent)),P=Math.max(P,c.usedPercent);if(i)D0.push(O0("7d",i.usedPercent)),P=Math.max(P,i.usedPercent);W.push(D0.join(`
|
|
5
|
+
`))}if(!W.length)return;let x=W.join(`
|
|
4
6
|
|
|
5
|
-
`)
|
|
7
|
+
`),E=P>=90?"error":P>=70?"warning":"info";J.tui?.showToast?.({body:{title:"Claude Quota",message:x,variant:E,duration:E==="error"?8000:5000}})?.catch?.(()=>{})}return{config:async($)=>{$.command={...$.command??{},[F0]:{template:F0,description:"Show or toggle 1-hour Anthropic ephemeral prompt cache TTL."},[x0]:{template:x0,description:"Keep hybrid Claude cache warm for recently used sessions during a local time window."},[k0]:{template:k0,description:"Show current Claude OAuth quota usage for all accounts."},[S0]:{template:S0,description:"Show or toggle Anthropic request dump capture for debugging."},[T0]:{template:T0,description:"Show or toggle Anthropic fast mode for supported Opus models."},[f0]:{template:f0,description:"Show or change Claude account routing between main-first and fallback-first."},[A0]:{template:A0,description:"Manage killswitch — hard-block requests when quota drops below per-account thresholds."}}},"experimental.chat.system.transform":async($,D)=>{if(!tj($))return;aj(D.system)},"command.execute.before":async($)=>{if($.command===F0)await t(j,$.sessionID,await wj($.arguments)),a();if($.command===x0)await t(j,$.sessionID,await _j($.arguments)),a();if($.command===k0){await t(j,$.sessionID,await Nj());let D=await R(),X=d?await d().catch(()=>{return}):void 0;o(D,{activeId:"main",route:"main",mainAccessToken:X?.access,mainRefreshToken:X?.refresh}),a()}if($.command===S0)await t(j,$.sessionID,await Bj($.arguments)),a();if($.command===T0)await t(j,$.sessionID,await Hj($.arguments)),a();if($.command===f0)await t(j,$.sessionID,await Uj($.arguments)),a();if($.command===A0){let D=await R(),X=o1(D),W=(D?.accounts??[]).filter((x)=>x.enabled!==!1).map((x)=>x.id),P=Dj({argumentsText:$.arguments,config:X,accountIds:W});if(P.updatedConfig)await s1(P.updatedConfig);await t(j,$.sessionID,P.text),a()}},auth:{provider:"anthropic",async loader($,D){d=$;let X=await $();if(X.type==="oauth"){let x=function(){if(j0)clearInterval(j0),j0=null;let Q=async()=>{try{let L=await R(Z);if(!Lj(L))return;let U=await $();if(U.type!=="oauth")return;if(await Z1(U.refresh),!U.expires)return;let N=U.expires-Date.now(),S=Oj(L);if(N>S)return;if(F("[refresh] opencode main oauth background due",{expiresInMs:N,refreshBeforeMs:S}),U.refresh&&$0(L?.refresh?.mainLastRefreshError,U.refresh,Date.now())){F("[refresh] opencode main oauth background skipped backoff",{nextRetryAt:L?.refresh?.mainLastRefreshError?.nextRetryAt,retryCount:L?.refresh?.mainLastRefreshError?.retryCount,expiresInMs:N});return}if(U.refresh&&L?.refresh?.mainRefreshLeaseUntil&&L.refresh.mainRefreshLeaseUntil>Date.now()&&L.refresh.mainRefreshLeaseTokenHash===M0(U.refresh))return;await P();let I=await $();F("[refresh] opencode main oauth refreshed in background",{newExpiresInMs:I.expires?I.expires-Date.now():void 0})}catch(L){F("[refresh] opencode main oauth refresh failed",{message:L instanceof Error?L.message:String(L)})}};if(j0=setInterval(()=>{Q()},uj+A1(ij)),"unref"in j0)j0.unref()},E=function(Q,L){if(Q instanceof Request&&Q.body)return!1;return L==null||typeof L==="string"},h=function(Q){return Q.has("x-parent-session-id")},p=function(Q){return Q.includes("rate_limit_error")||/exceed your account'?s rate limit/i.test(Q)},c=function(Q){return Q?.quota?.enabled===!0},n=function(Q){return V.getFallback(Q.id,Q.access)?.quota??Q.quota};for(let Q of Object.values(D.models))Q.cost={input:0,output:0,cache:{read:0,write:0}};let W=null;async function P(){if(!W)W=(async()=>{let U=null,N=null,S=null;async function I(z){let H=await R(Z)??{version:1,main:{type:"opencode",provider:"anthropic"},accounts:[]};H.refresh=H.refresh??{},z(H),await Y0(H,Z)}async function T(z){let H=Date.now()+cj;while(Date.now()<H){await new Promise((w)=>setTimeout(w,T1+A1(T1)));let K=await $();if(K.type!=="oauth"||!K.access)continue;if((K.access!==z.access||K.refresh!==z.refresh||(K.expires??0)>(z.expires??0)+60000)&&(!K.expires||K.expires>Date.now()))return F("[refresh] opencode main oauth joined concurrent refresh",{expiresInMs:K.expires?K.expires-Date.now():void 0}),K.access}return null}for(let z=0;z<=2;z++){let H=null;try{if(z>0){let C=500*2**(z-1);await new Promise((B)=>setTimeout(B,C))}if(H=await $(),!H.refresh)throw Error("Token refresh failed: missing refresh token");let K=await R(Z),_=M0(H.refresh),w=K?.refresh?.mainLastRefreshError;if(F("[refresh] opencode main oauth refresh check",{attempt:z,expiresInMs:H.expires?H.expires-Date.now():void 0,hasBackoff:Boolean(w),backoffActive:w?$0(w,H.refresh,Date.now()):!1,retryCount:w?.retryCount,nextRetryAt:w?.nextRetryAt}),w&&$0(w,H.refresh,Date.now()))throw F("[refresh] opencode main oauth refresh skipped backoff",{nextRetryAt:w.nextRetryAt,retryCount:w.retryCount}),Error(n0(w,Date.now()));if(K?.refresh?.mainRefreshLeaseUntil&&K.refresh.mainRefreshLeaseUntil>Date.now()&&K.refresh.mainRefreshLeaseTokenHash===_){F("[refresh] opencode main oauth refresh skipped lease",{leaseUntil:K.refresh.mainRefreshLeaseUntil});let C=await T(H);if(C)return C;throw Error("Claude OAuth refresh is already in progress")}let O=await b1({name:"opencode-main-oauth-refresh",ttlMs:120000,renew:!0});if(!O){F("[refresh] opencode main oauth refresh skipped file lock");let C=await T(H);if(C)return C;throw Error("Claude OAuth refresh is already in progress")}S=O.release,U=pj(),N=_,await I((C)=>{C.refresh=C.refresh??{},C.refresh.mainRefreshLeaseId=U??void 0,C.refresh.mainRefreshLeaseUntil=Date.now()+120000,C.refresh.mainRefreshLeaseTokenHash=_});let g=await R(Z);if(F("[refresh] opencode main oauth refresh lease acquired",{attempt:z,leaseUntil:Date.now()+120000}),g?.refresh?.mainRefreshLeaseId!==U||g.refresh.mainRefreshLeaseTokenHash!==_)throw Error("Claude OAuth refresh is already in progress");F("[refresh] opencode main oauth refresh request start",{attempt:z});let m=await f1({refreshToken:H.refresh,maxRetries:0});return await J.auth.set({path:{id:"anthropic"},body:{type:"oauth",refresh:m.refresh,access:m.access,expires:m.expires}}),await I((C)=>{if(!C?.refresh)return;if(C.refresh.mainLastRefreshError=void 0,C.refresh.mainRefreshLeaseId===U)C.refresh.mainRefreshLeaseId=void 0,C.refresh.mainRefreshLeaseUntil=void 0,C.refresh.mainRefreshLeaseTokenHash=void 0}),F("[refresh] opencode main oauth refresh succeeded",{attempt:z,expiresInMs:m.expires-Date.now()}),m.access}catch(K){let _=K instanceof Error&&(K.message.includes("fetch failed")||("code"in K)&&(K.code==="ECONNRESET"||K.code==="ECONNREFUSED"||K.code==="ETIMEDOUT"||K.code==="UND_ERR_CONNECT_TIMEOUT"));if(z<2&&(_||K instanceof d0&&K.status>=500))continue;F("[refresh] opencode main oauth refresh attempt failed",{attempt:z,error:K instanceof Error?K.message:String(K),transient:_});let w=H?.refresh;if(w&&K instanceof d0)await I((O)=>{O.refresh=O.refresh??{},O.refresh.mainLastRefreshError=i1({error:K,now:Date.now(),refreshToken:w,previous:O.refresh.mainLastRefreshError})});throw K}finally{if(U)await I((K)=>{if(!K?.refresh)return;if(K.refresh.mainRefreshLeaseId===U&&K.refresh.mainRefreshLeaseTokenHash===N)K.refresh.mainRefreshLeaseId=void 0,K.refresh.mainRefreshLeaseUntil=void 0,K.refresh.mainRefreshLeaseTokenHash=void 0}).catch(()=>{});await S?.().catch(()=>{})}}throw Error("Token refresh exhausted all retries")})().finally(()=>{W=null});return W}v=P,x(),V.seedFallbacksFromAccounts(G?.accounts??[]),o(G,{activeId:"main",route:"main",mainAccessToken:X.access,mainRefreshToken:X.refresh});async function i(Q,L){if(!Q.body||Q.status!==200)return L?.mark("inspect_stream_skip",{status:Q.status}),{response:Q,rateLimited:!1};if(Q.headers.get("x-cortexkit-relay-optimistic")==="true")return L?.mark("inspect_stream_skip",{status:Q.status,reason:"optimistic_relay"}),{response:Q,rateLimited:!1};let U=q(),N=Q.body.getReader(),S=[],I=new TextDecoder,T="",z=0;while(!T.includes(`
|
|
8
|
+
|
|
9
|
+
`)&&T.length<65536){let{done:K,value:_}=await N.read();if(K)break;if(S.push(_),z+=_.byteLength,T+=I.decode(_,{stream:!0}),p(T))break}if(p(T))return await N.cancel().catch(()=>{}),L?.mark("inspect_stream_first_event",{ms:y(q()-U),bytes:z,rateLimited:!0}),{response:Q,rateLimited:!0};let H=new ReadableStream({start(K){for(let _ of S)K.enqueue(_)},async pull(K){let{done:_,value:w}=await N.read();if(_){K.close();return}K.enqueue(w)},cancel(K){return N.cancel(K)}});return L?.mark("inspect_stream_first_event",{ms:y(q()-U),bytes:z,rateLimited:!1}),{response:new Response(H,{status:Q.status,statusText:Q.statusText,headers:Q.headers}),rateLimited:!1}}async function s(Q,L,U,N,S="unknown",I){let T=q(),z=I,H=async()=>{return z??=await R(Z),z},K=q1(Q,L),_=K.get("x-session-affinity")||K.get("x-opencode-session"),w=h(K);K.delete("x-parent-session-id"),K.delete("x-session-affinity"),K.delete("x-opencode-session");let O=L?.body,g;if(O&&typeof O==="string")try{let b=JSON.parse(O);if(typeof b.model==="string")g=b.model}catch{}let m=await o0(U,g),C=typeof O==="string"?O.length:void 0;if(O&&typeof O==="string"){let b=q(),J0=(()=>{if(!t0())return!1;try{return B0(JSON.parse(O).model)}catch{return!1}})();O=await R1(O,{cache1hEnabled:!w&&q0(),cache1hMode:E0(),fastModeEnabled:J0,identity:m});try{X0(K,U,{body:JSON.parse(O),identity:m})}catch{X0(K,U,{identity:m})}if(J0)g0(K);N?.mark("rewrite_body",{route:S,ms:y(q()-b),originalBytes:C,rewrittenBytes:O.length,cacheEnabled:!w&&q0(),cacheMode:E0(),fastModeEnabled:J0,subagent:w})}let B=C1(Q);if(S==="main"&&typeof O==="string"&&!w&&q0()&&E0()==="hybrid"){let b=await H();if(u.track({sessionId:_,url:B.url?.toString()??B.input.toString(),headers:K,bodyText:O,storage:b,cacheMode:"hybrid"}).tracked)N?.mark("cachekeep_track",{session:_})}let M=()=>fetch(B.input,{...L,body:O,headers:K,...E1()&&{tls:{rejectUnauthorized:!1}}}),k=e0(await H()),l=q(),A=await Yj({config:k,input:B.input,init:L,headers:K,body:O,fallback:M,affinity:_,optimisticResponse:k?.transport==="websocket"});return N?.mark("send_headers_received",{route:S,ms:y(q()-l),status:A.status,relayConfigured:k!=null,totalSendWithAccessMs:y(q()-T)}),A}async function V0(Q){let L=await Y.getUsableFallbackAccounts(Q);if(!z0(Q))return L;return L.filter((U)=>v0(n(U),Q,U.id))}async function r(Q,L,U,N,S,I,T){if(!U.length)return S??null;let z=T?.returnLastOnExhausted??!0;await S?.body?.cancel().catch(()=>{});let H=S??null;for(let[K,_]of U.entries()){let w=_.access;if(!w)continue;let O=await s(Q,L,w,I,`fallback_${K}`,N);H=O;let g=c0(O.status,N);if(!g){let m=await i(O,I);O=m.response,H=O,g=m.rateLimited}if(!g){if(await Y.markUsed(_),T?.onSuccess?.(_),w&&V.shouldRefreshOnRequestCount(I0))V.refreshFallback(_.id,w).then(()=>T?.onSuccess?.(_)).catch(()=>{});return O}if(K<U.length-1||!z)await O.body?.cancel().catch(()=>{})}return z?H:null}async function D0(Q,L,U,N,S,I,T){if(!E(Q,L?.body))return U;let z=q(),H=I??await R(Z);S?.mark("fallback_load_storage",{ms:y(q()-z),cached:!!I});let K=U,_=c0(K.status,H);if(!_){let O=await i(K,S);K=O.response,_=O.rateLimited}if(!_)return K;let w=N;if(!w){let O=q();w=await Y.getUsableFallbackAccounts(),S?.mark("fallback_get_accounts",{ms:y(q()-O),accounts:w.length})}if(z0(H)){let O=w.length;if(w=w.filter((g)=>v0(n(g),H,g.id)),w.length<O)F("[killswitch] filtered fallbacks",{before:O,after:w.length})}return await r(Q,L,w,H,K,S,{onSuccess:T})??K}return{apiKey:"",async fetch(Q,L){let U=L?.body,N=rj({bodyBytes:typeof U==="string"?U.length:void 0}),S=q(),I=await $();if(N.mark("get_auth",{ms:y(q()-S),authType:I.type,hasAccess:Boolean(I.access)}),I.type!=="oauth"){let B=await fetch(Q,L);return N.done("non_oauth_passthrough",{status:B.status}),B}await Z1(I.refresh);let T=q(),z=await R();N.mark("load_storage",{ms:y(q()-T)}),V.updateStorage(z),V.seedFallbacksFromAccounts(z?.accounts??[]);let H=E(Q,L?.body);if(H)I0++;let K=(B,M)=>o(z,{activeId:B,route:M,mainAccessToken:I.access,mainRefreshToken:I.refresh}),_;if(H&&j1(z)==="fallback-first")try{let B=q();_=await V0(z),N.mark("fallback_first_get_accounts",{ms:y(q()-B),accounts:_.length});let M=await r(Q,L,_,z,void 0,N,{returnLastOnExhausted:!1,onSuccess:(k)=>K(k.id,"fallback-first")});if(M)return N.done("return_fallback_first",{status:M.status}),W0(M);_=[]}catch(B){N.mark("fallback_first_error",{error:B instanceof Error?B.message:String(B)})}if(!I.access||!I.expires||I.expires<Date.now()){let M=(await R())?.refresh?.mainLastRefreshError;if(I.refresh&&M&&$0(M,I.refresh,Date.now()))throw F("[refresh] opencode main oauth request skipped backoff",{nextRetryAt:M.nextRetryAt,retryCount:M.retryCount,expiresInMs:I.expires?I.expires-Date.now():void 0}),Error(n0(M,Date.now()));F("[refresh] opencode main oauth refresh required for request",{hasAccess:Boolean(I.access),expiresInMs:I.expires?I.expires-Date.now():void 0,expiredAgoMs:I.expires&&I.expires<Date.now()?Date.now()-I.expires:void 0});let k=q();I.access=await P(),N.mark("refresh_main_access",{ms:y(q()-k)})}if(!I.access)throw N.done("missing_access_error"),Error("OAuth access token is missing after refresh");function w(){if(z?.quota?.showToasts!==!0)return;let B=V.getMain();if(!B)return;let M=(z?.accounts??[]).filter((A)=>A.enabled!==!1).map((A)=>({...A,quota:V.getFallback(A.id,A.access)?.quota??A.quota})),k=P0(B.quota,z),l;if(k)l="main";else l=M.find((A)=>P0(A.quota,z))?.id??"main";vj(B.quota,M,l)}if(H&&c(z))try{let B=q(),M=V.getMain(I.access)?.quota;if(!M)M=await V.refreshMain(I.access),w();else if(V.needsRefresh(I0))V.refreshMain(I.access).then(()=>{e(),w()}).catch(()=>{});if(o(z,{activeId:"main",route:"main",mainAccessToken:I.access,mainRefreshToken:I.refresh}),N.mark("main_quota_for_routing",{ms:y(q()-B),passes:P0(M,z)}),!P0(M,z)){let k=q();_=await V0(z),N.mark("preselect_fallback_accounts",{ms:y(q()-k),accounts:_.length});let l=await r(Q,L,_,z,void 0,N,{onSuccess:(A)=>K(A.id,"fallback")});if(l)return N.done("return_preselected_fallback",{status:l.status}),W0(l)}}catch(B){N.mark("main_quota_for_routing_error",{error:B instanceof Error?B.message:String(B)})}let O=V.getMain(I.access)?.quota;if(z?.quota?.failClosedOnUnknownQuota&&!O&&V.isBackedOff()){let B=V.getLastApiError(),M=B?c1(B,Date.now()):"Quota API unavailable";return F("[quota] blocked: quota API backed off (failClosed)",{nextRetryAt:B?.nextRetryAt,retryCount:B?.retryCount}),new Response(JSON.stringify({type:"error",error:{type:"rate_limit_error",message:M}}),{status:429,headers:{"content-type":"application/json","retry-after":String(B?.nextRetryAt?Math.max(1,Math.ceil((B.nextRetryAt-Date.now())/1000)):60)}})}if(z0(z)){if(V.needsRefresh(I0))try{let M=(z?.accounts??[]).filter((k)=>k.enabled!==!1&&k.access);await Promise.all([V.refreshMain(I.access),V.refreshAllFallbacks(M)])}catch(M){F("[quota] killswitch refresh failed",{error:M instanceof Error?M.message:String(M),backedOff:V.isBackedOff()})}O=V.getMain(I.access)?.quota}if(z0(z)&&!v0(O,z)){let M=E(Q,L?.body)?await V0(z):[];if(M.length>0){F("[route] skipping main (killswitch), trying fallbacks");let b=await r(Q,L,M,z,void 0,N,{onSuccess:(J0)=>K(J0.id,"fallback")});if(b)return N.done("return_killswitch_fallback",{status:b.status}),W0(b)}let k=Date.now(),l=(z?.accounts??[]).filter((b)=>b.enabled!==!1).map((b)=>({...b,quota:n(b)})),A=n1(O,l,k);return new Response(JSON.stringify({type:"error",error:{type:"rate_limit_error",message:`Killswitch: no routable accounts. Retry in ${Math.floor(A/60)}m ${A%60}s.`}}),{status:429,headers:{"content-type":"application/json","retry-after":String(A)}})}let g=await s(Q,L,I.access,N,"main",z),m=!1,C=await D0(Q,L,g,_,N,z,(B)=>{m=!0,K(B.id,"fallback")});if(!m)K("main","main");return N.done("return_response",{status:C.status}),W0(C)}}}return{}},methods:[{label:"Claude Pro/Max",type:"oauth",authorize:async()=>{let $=await p0("max");return{url:$.url,instructions:"Paste the authorization code here:",method:"code",callback:async(D)=>{return l0(D,$.verifier,$.redirectUri,$.state)}}}},{label:"Create an API Key",type:"oauth",authorize:async()=>{let $=await p0("console");return{url:$.url,instructions:"Paste the authorization code here:",method:"code",callback:async(D)=>{let X=await l0(D,$.verifier,$.redirectUri,$.state);if(X.type==="failed")return X;return{type:"success",key:(await fetch("https://api.anthropic.com/api/oauth/claude_cli/create_api_key",{method:"POST",headers:{"Content-Type":"application/json",authorization:`Bearer ${X.access}`}}).then((P)=>P.json())).raw_key}}}}},{provider:"anthropic",label:"Manually enter API Key",type:"api"}]}}};export{XJ as AnthropicAuthPlugin};
|
package/dist/sidebar-state.d.ts
CHANGED
|
@@ -40,3 +40,8 @@ export declare function getSidebarStateFile(): string;
|
|
|
40
40
|
export declare const DEFAULT_SIDEBAR_STATE: SidebarState;
|
|
41
41
|
export declare function getSidebarState(): Promise<SidebarState>;
|
|
42
42
|
export declare function setSidebarState(state: SidebarState): Promise<void>;
|
|
43
|
+
export declare function resolveActiveAccount(state: SidebarState): {
|
|
44
|
+
id: string;
|
|
45
|
+
name: string;
|
|
46
|
+
quota: AccountQuota | null;
|
|
47
|
+
};
|
package/dist/sidebar-state.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{Ga as a,Ha as b,Ia as c,Ja as d,Ka as e}from"./index-q0d64q8d.js";export{d as setSidebarState,e as resolveActiveAccount,a as getSidebarStateFile,c as getSidebarState,b as DEFAULT_SIDEBAR_STATE};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/opencode-anthropic-auth",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -51,9 +51,9 @@
|
|
|
51
51
|
"@opencode-ai/plugin": "*"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@cortexkit/anthropic-auth-core": "1.
|
|
55
|
-
"@opentui/core": ">=0.1
|
|
56
|
-
"@opentui/solid": ">=0.1
|
|
54
|
+
"@cortexkit/anthropic-auth-core": "1.6.0",
|
|
55
|
+
"@opentui/core": ">=0.3.1",
|
|
56
|
+
"@opentui/solid": ">=0.3.1",
|
|
57
57
|
"solid-js": "^1.9.10"
|
|
58
58
|
}
|
|
59
59
|
}
|
package/src/sidebar-state.ts
CHANGED
|
@@ -77,3 +77,31 @@ export async function setSidebarState(state: SidebarState): Promise<void> {
|
|
|
77
77
|
// Best-effort — sidebar is non-critical
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
+
|
|
81
|
+
// Resolve the currently-active account from activeId for the collapsed sidebar
|
|
82
|
+
// view. activeId === 'main' (or undefined/unmatched/disabled) → the main
|
|
83
|
+
// account; otherwise the enabled fallback whose id matches.
|
|
84
|
+
export function resolveActiveAccount(state: SidebarState): {
|
|
85
|
+
id: string
|
|
86
|
+
name: string
|
|
87
|
+
quota: AccountQuota | null
|
|
88
|
+
} {
|
|
89
|
+
const activeId = state.activeId
|
|
90
|
+
if (activeId && activeId !== 'main') {
|
|
91
|
+
// `account.enabled` is defensive: writeSidebarState already filters disabled
|
|
92
|
+
// accounts out of state.fallbacks, so in normal operation every entry is
|
|
93
|
+
// enabled. Kept so this pure helper stays correct for any caller and is
|
|
94
|
+
// exercised directly by the unit tests.
|
|
95
|
+
const fallback = state.fallbacks.find(
|
|
96
|
+
(account) => account.enabled && account.id === activeId,
|
|
97
|
+
)
|
|
98
|
+
if (fallback) {
|
|
99
|
+
return {
|
|
100
|
+
id: fallback.id,
|
|
101
|
+
name: fallback.label ?? fallback.id,
|
|
102
|
+
quota: fallback.quota,
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return { id: 'main', name: 'main', quota: state.main.quota }
|
|
107
|
+
}
|
package/src/tui.tsx
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
/** @jsxImportSource @opentui/solid */
|
|
2
2
|
|
|
3
|
+
import { readFileSync } from 'node:fs'
|
|
4
|
+
import { dirname, join } from 'node:path'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
3
6
|
import type {
|
|
4
7
|
TuiPlugin,
|
|
5
8
|
TuiPluginApi,
|
|
6
9
|
TuiPluginModule,
|
|
7
10
|
} from '@opencode-ai/plugin/tui'
|
|
8
|
-
import { For, Show, createSignal, onCleanup } from 'solid-js'
|
|
11
|
+
import { For, type JSX, Show, createSignal, onCleanup } from 'solid-js'
|
|
9
12
|
|
|
10
13
|
import {
|
|
11
14
|
type AccountQuota,
|
|
12
15
|
DEFAULT_SIDEBAR_STATE,
|
|
13
16
|
type SidebarState,
|
|
14
17
|
getSidebarState,
|
|
18
|
+
resolveActiveAccount,
|
|
15
19
|
} from './sidebar-state.js'
|
|
16
20
|
|
|
17
21
|
const POLL_MS = 1500
|
|
@@ -22,6 +26,23 @@ const BAR_WIDTH = 10
|
|
|
22
26
|
const BAR_FILLED = '\u2588'
|
|
23
27
|
const BAR_EMPTY = '\u2591'
|
|
24
28
|
|
|
29
|
+
// Plugin version for the header (mirrors the Magic Context / AFT convention).
|
|
30
|
+
// Read at runtime from package.json relative to this module — NOT a TS JSON
|
|
31
|
+
// import, which would break the declaration build (package.json is outside
|
|
32
|
+
// rootDir). tui.tsx ships as source (package.json exports["./tui"] →
|
|
33
|
+
// "./src/tui.tsx") and is never compiled into a deeper dist tree, so `..` from
|
|
34
|
+
// src/ is always the package root. Empty string on any failure → header shows
|
|
35
|
+
// the badge with no version.
|
|
36
|
+
const PLUGIN_VERSION: string = (() => {
|
|
37
|
+
try {
|
|
38
|
+
const here = dirname(fileURLToPath(import.meta.url))
|
|
39
|
+
const raw = readFileSync(join(here, '..', 'package.json'), 'utf8')
|
|
40
|
+
return (JSON.parse(raw) as { version?: string }).version ?? ''
|
|
41
|
+
} catch {
|
|
42
|
+
return ''
|
|
43
|
+
}
|
|
44
|
+
})()
|
|
45
|
+
|
|
25
46
|
// biome-ignore lint/suspicious/noExplicitAny: opentui border prop not typed in plugin tui surface
|
|
26
47
|
const SINGLE_BORDER = { type: 'single' } as any
|
|
27
48
|
|
|
@@ -116,6 +137,21 @@ function StatRow(props: {
|
|
|
116
137
|
)
|
|
117
138
|
}
|
|
118
139
|
|
|
140
|
+
// Compact row for the collapsed view: muted label left, caller-provided value
|
|
141
|
+
// right. Mirrors StatRow's layout so columns line up.
|
|
142
|
+
function CollapsedRow(props: {
|
|
143
|
+
theme: ThemeCurrent
|
|
144
|
+
label: string
|
|
145
|
+
children: JSX.Element
|
|
146
|
+
}) {
|
|
147
|
+
return (
|
|
148
|
+
<box width='100%' flexDirection='row' justifyContent='space-between'>
|
|
149
|
+
<text fg={props.theme.textMuted}>{props.label}</text>
|
|
150
|
+
{props.children}
|
|
151
|
+
</box>
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
|
|
119
155
|
// Quota window row: muted label left, tone-colored bar + percentage right,
|
|
120
156
|
// with an optional muted reset suffix.
|
|
121
157
|
function QuotaRow(props: {
|
|
@@ -203,6 +239,7 @@ async function readStateFromFile(): Promise<SidebarState> {
|
|
|
203
239
|
|
|
204
240
|
function QuotaSidebar(props: { api: TuiPluginApi }) {
|
|
205
241
|
const [state, setState] = createSignal<SidebarState>(DEFAULT_SIDEBAR_STATE)
|
|
242
|
+
const [collapsed, setCollapsed] = createSignal(false)
|
|
206
243
|
let lastUpdated = 0
|
|
207
244
|
let debounce: ReturnType<typeof setTimeout> | null = null
|
|
208
245
|
|
|
@@ -241,6 +278,12 @@ function QuotaSidebar(props: { api: TuiPluginApi }) {
|
|
|
241
278
|
const hasData = () =>
|
|
242
279
|
state().main.quota != null || enabledFallbacks().length > 0
|
|
243
280
|
|
|
281
|
+
const headerLabel = () =>
|
|
282
|
+
!hasData() ? 'CLAUDE' : collapsed() ? '\u25b6 CLAUDE' : '\u25bc CLAUDE'
|
|
283
|
+
const activeAccount = () => resolveActiveAccount(state())
|
|
284
|
+
const activeFiveHourPct = () =>
|
|
285
|
+
activeAccount().quota?.five_hour?.usedPercent ?? null
|
|
286
|
+
|
|
244
287
|
const quotaBackedOff = () => state().main.quotaBackedOff === true
|
|
245
288
|
const refreshBackedOff = () => state().main.refreshBackedOff === true
|
|
246
289
|
const degraded = () => quotaBackedOff() || refreshBackedOff()
|
|
@@ -264,19 +307,30 @@ function QuotaSidebar(props: { api: TuiPluginApi }) {
|
|
|
264
307
|
paddingLeft={1}
|
|
265
308
|
paddingRight={1}
|
|
266
309
|
>
|
|
267
|
-
{/* Header: CLAUDE badge +
|
|
310
|
+
{/* Header: ▼/▶ CLAUDE badge (click to collapse) + version, or LIMITED badge */}
|
|
311
|
+
{/* biome-ignore lint/a11y/noStaticElementInteractions: opentui renders to a terminal, not the DOM — ARIA roles do not apply */}
|
|
268
312
|
<box
|
|
269
313
|
width='100%'
|
|
270
314
|
flexDirection='row'
|
|
271
315
|
justifyContent='space-between'
|
|
272
316
|
alignItems='center'
|
|
317
|
+
onMouseDown={() => {
|
|
318
|
+
if (hasData()) setCollapsed((value) => !value)
|
|
319
|
+
}}
|
|
273
320
|
>
|
|
274
321
|
<box paddingLeft={1} paddingRight={1} backgroundColor={theme().accent}>
|
|
275
322
|
<text fg={theme().background}>
|
|
276
|
-
<b>{
|
|
323
|
+
<b>{headerLabel()}</b>
|
|
277
324
|
</text>
|
|
278
325
|
</box>
|
|
279
|
-
<Show
|
|
326
|
+
<Show
|
|
327
|
+
when={degraded()}
|
|
328
|
+
fallback={
|
|
329
|
+
<Show when={PLUGIN_VERSION !== ''}>
|
|
330
|
+
<text fg={theme().textMuted}>{`v${PLUGIN_VERSION}`}</text>
|
|
331
|
+
</Show>
|
|
332
|
+
}
|
|
333
|
+
>
|
|
280
334
|
<box
|
|
281
335
|
paddingLeft={1}
|
|
282
336
|
paddingRight={1}
|
|
@@ -289,93 +343,135 @@ function QuotaSidebar(props: { api: TuiPluginApi }) {
|
|
|
289
343
|
</Show>
|
|
290
344
|
</box>
|
|
291
345
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
<
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
346
|
+
{/* Collapsed: active account 5h quota + dot, plus fast-mode when on */}
|
|
347
|
+
<Show when={collapsed() && hasData()}>
|
|
348
|
+
<CollapsedRow theme={theme()} label={activeAccount().name}>
|
|
349
|
+
<Show
|
|
350
|
+
when={activeFiveHourPct() != null}
|
|
351
|
+
fallback={<text fg={theme().textMuted}>{'\u2014'}</text>}
|
|
352
|
+
>
|
|
353
|
+
<box flexDirection='row'>
|
|
354
|
+
<text
|
|
355
|
+
fg={toneColor(
|
|
356
|
+
theme(),
|
|
357
|
+
usageTone(activeFiveHourPct() as number),
|
|
358
|
+
)}
|
|
359
|
+
>
|
|
360
|
+
<b>
|
|
361
|
+
{`${String(Math.round(activeFiveHourPct() as number)).padStart(3)}%`}
|
|
362
|
+
</b>
|
|
363
|
+
</text>
|
|
364
|
+
<text
|
|
365
|
+
fg={toneColor(
|
|
366
|
+
theme(),
|
|
367
|
+
usageTone(activeFiveHourPct() as number),
|
|
368
|
+
)}
|
|
369
|
+
>
|
|
370
|
+
{' \u25cf'}
|
|
371
|
+
</text>
|
|
372
|
+
</box>
|
|
373
|
+
</Show>
|
|
374
|
+
</CollapsedRow>
|
|
375
|
+
<Show when={state().fastMode}>
|
|
376
|
+
<CollapsedRow theme={theme()} label='Mode'>
|
|
377
|
+
<text fg={toneColor(theme(), 'accent')}>
|
|
378
|
+
<b>{'fast'}</b>
|
|
379
|
+
</text>
|
|
380
|
+
</CollapsedRow>
|
|
381
|
+
</Show>
|
|
319
382
|
</Show>
|
|
320
383
|
|
|
321
|
-
{/*
|
|
322
|
-
|
|
323
|
-
<
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
<StatRow
|
|
336
|
-
theme={theme()}
|
|
337
|
-
label='Relay'
|
|
338
|
-
value={relayValue()}
|
|
339
|
-
tone={state().relay?.enabled ? 'ok' : 'muted'}
|
|
340
|
-
/>
|
|
341
|
-
|
|
342
|
-
{/* Cache */}
|
|
343
|
-
<Show when={showCache()}>
|
|
344
|
-
<SectionHeader theme={theme()} title='Cache' />
|
|
345
|
-
<StatRow
|
|
346
|
-
theme={theme()}
|
|
347
|
-
label='1h cache'
|
|
348
|
-
value={`${cacheKeep()?.window} \u00b7 ${cacheKeep()?.enabled ? 'on' : 'off'}`}
|
|
349
|
-
tone={cacheKeep()?.enabled ? 'ok' : 'muted'}
|
|
350
|
-
/>
|
|
351
|
-
<Show when={(cacheKeep()?.trackedSessions ?? 0) > 0}>
|
|
352
|
-
<StatRow
|
|
384
|
+
{/* Expanded: full sections. Also render when there's no data so the
|
|
385
|
+
sidebar can never go blank if data clears while collapsed. */}
|
|
386
|
+
<Show when={!collapsed() || !hasData()}>
|
|
387
|
+
<Show
|
|
388
|
+
when={hasData()}
|
|
389
|
+
fallback={
|
|
390
|
+
<box marginTop={1} width='100%'>
|
|
391
|
+
<text fg={theme().textMuted}>{'Waiting for quota\u2026'}</text>
|
|
392
|
+
</box>
|
|
393
|
+
}
|
|
394
|
+
>
|
|
395
|
+
{/* Quota */}
|
|
396
|
+
<SectionHeader theme={theme()} title='Quota' />
|
|
397
|
+
<AccountBlock
|
|
353
398
|
theme={theme()}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
399
|
+
name='main'
|
|
400
|
+
quota={state().main.quota}
|
|
401
|
+
active={state().activeId === 'main'}
|
|
357
402
|
/>
|
|
403
|
+
<For each={enabledFallbacks()}>
|
|
404
|
+
{(fb) => (
|
|
405
|
+
<AccountBlock
|
|
406
|
+
theme={theme()}
|
|
407
|
+
name={fb.label ?? fb.id}
|
|
408
|
+
quota={fb.quota}
|
|
409
|
+
active={state().activeId === fb.id}
|
|
410
|
+
marginTop={1}
|
|
411
|
+
/>
|
|
412
|
+
)}
|
|
413
|
+
</For>
|
|
358
414
|
</Show>
|
|
359
|
-
</Show>
|
|
360
415
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
<
|
|
364
|
-
|
|
416
|
+
{/* Routing */}
|
|
417
|
+
<SectionHeader theme={theme()} title='Routing' />
|
|
418
|
+
<StatRow
|
|
419
|
+
theme={theme()}
|
|
420
|
+
label='Route'
|
|
421
|
+
value={state().route}
|
|
422
|
+
tone='accent'
|
|
423
|
+
/>
|
|
424
|
+
<StatRow
|
|
425
|
+
theme={theme()}
|
|
426
|
+
label='Mode'
|
|
427
|
+
value={state().fastMode ? 'fast' : 'std'}
|
|
428
|
+
tone={state().fastMode ? 'accent' : 'muted'}
|
|
429
|
+
/>
|
|
430
|
+
<StatRow
|
|
431
|
+
theme={theme()}
|
|
432
|
+
label='Relay'
|
|
433
|
+
value={relayValue()}
|
|
434
|
+
tone={state().relay?.enabled ? 'ok' : 'muted'}
|
|
435
|
+
/>
|
|
436
|
+
|
|
437
|
+
{/* Cache */}
|
|
438
|
+
<Show when={showCache()}>
|
|
439
|
+
<SectionHeader theme={theme()} title='Cache' />
|
|
365
440
|
<StatRow
|
|
366
441
|
theme={theme()}
|
|
367
|
-
label='
|
|
368
|
-
value={
|
|
369
|
-
tone='
|
|
442
|
+
label='1h cache'
|
|
443
|
+
value={`${cacheKeep()?.window} \u00b7 ${cacheKeep()?.enabled ? 'on' : 'off'}`}
|
|
444
|
+
tone={cacheKeep()?.enabled ? 'ok' : 'muted'}
|
|
370
445
|
/>
|
|
446
|
+
<Show when={(cacheKeep()?.trackedSessions ?? 0) > 0}>
|
|
447
|
+
<StatRow
|
|
448
|
+
theme={theme()}
|
|
449
|
+
label='Tracked'
|
|
450
|
+
value={String(cacheKeep()?.trackedSessions)}
|
|
451
|
+
tone='text'
|
|
452
|
+
/>
|
|
453
|
+
</Show>
|
|
371
454
|
</Show>
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
455
|
+
|
|
456
|
+
{/* Health — only when something is wrong */}
|
|
457
|
+
<Show when={degraded()}>
|
|
458
|
+
<SectionHeader theme={theme()} title='Health' />
|
|
459
|
+
<Show when={quotaBackedOff()}>
|
|
460
|
+
<StatRow
|
|
461
|
+
theme={theme()}
|
|
462
|
+
label='Quota API'
|
|
463
|
+
value={`backoff ${formatUntil(state().main.quotaBackoffUntil)}`}
|
|
464
|
+
tone='warn'
|
|
465
|
+
/>
|
|
466
|
+
</Show>
|
|
467
|
+
<Show when={refreshBackedOff()}>
|
|
468
|
+
<StatRow
|
|
469
|
+
theme={theme()}
|
|
470
|
+
label='Token refresh'
|
|
471
|
+
value={`backoff ${formatUntil(state().main.refreshBackoffUntil)}`}
|
|
472
|
+
tone='warn'
|
|
473
|
+
/>
|
|
474
|
+
</Show>
|
|
379
475
|
</Show>
|
|
380
476
|
</Show>
|
|
381
477
|
</box>
|