@cortexkit/opencode-anthropic-auth 1.15.1 → 1.17.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.
@@ -13,6 +13,23 @@ export interface AccountQuota {
13
13
  five_hour?: QuotaWindow;
14
14
  seven_day?: QuotaWindow;
15
15
  scoped?: ScopedQuotaWindow[];
16
+ extraUsage?: {
17
+ used: {
18
+ amountMinor: number;
19
+ currency: string;
20
+ exponent: number;
21
+ };
22
+ limit: {
23
+ amountMinor: number;
24
+ currency: string;
25
+ exponent: number;
26
+ };
27
+ utilizationPercent?: number;
28
+ severity?: string;
29
+ exhausted: boolean;
30
+ };
31
+ bindingWindow?: string;
32
+ fallbackAdvised?: boolean;
16
33
  }
17
34
  export interface SidebarAccountState {
18
35
  id: string;
@@ -20,6 +37,7 @@ export interface SidebarAccountState {
20
37
  quota: AccountQuota | null;
21
38
  enabled: boolean;
22
39
  needsReauth: boolean;
40
+ tierLabel?: string;
23
41
  }
24
42
  export interface FableRecoverySidebarState {
25
43
  sessionId: string;
@@ -30,6 +48,7 @@ export interface FableRecoverySidebarState {
30
48
  export interface SidebarState {
31
49
  main: {
32
50
  quota: AccountQuota | null;
51
+ tierLabel?: string;
33
52
  quotaBackedOff?: boolean;
34
53
  quotaBackoffUntil?: number;
35
54
  refreshBackedOff?: boolean;
@@ -54,8 +73,38 @@ export interface SidebarState {
54
73
  export declare function getSidebarStateFile(): string;
55
74
  export declare const DEFAULT_SIDEBAR_STATE: SidebarState;
56
75
  export declare function normalizeSidebarState(raw: unknown): SidebarState;
57
- export declare function getSidebarState(): Promise<SidebarState>;
58
- export declare function setSidebarState(state: SidebarState, stateFile?: string): Promise<void>;
76
+ interface SidebarStateWriteTestHooks {
77
+ afterMergeRead?: (stateFile: string) => void | Promise<void>;
78
+ beforeRename?: (stateFile: string, tempFile: string) => void | Promise<void>;
79
+ afterRename?: (stateFile: string) => void | Promise<void>;
80
+ afterStaleLockStat?: (lockDir: string) => void | Promise<void>;
81
+ onStaleLockClaimed?: (lockDir: string) => void | Promise<void>;
82
+ onLockAcquired?: (lockDir: string) => void | Promise<void>;
83
+ beforeReleaseDirectoryRemoval?: (lockDir: string) => void | Promise<void>;
84
+ lockBudgetMs?: number;
85
+ lockRetryMinMs?: number;
86
+ lockRetryMaxMs?: number;
87
+ }
88
+ export declare function __setSidebarStateWriteTestHooks(hooks: SidebarStateWriteTestHooks | null): void;
89
+ export interface InitialSidebarRoutingTestHooks {
90
+ beforeSidebarRead?: () => void | Promise<void>;
91
+ beforeStorageLoad?: () => void | Promise<void>;
92
+ afterStorageLoad?: () => void | Promise<void>;
93
+ }
94
+ export declare function __setInitialSidebarRoutingTestHooks(hooks: InitialSidebarRoutingTestHooks | null): void;
95
+ export declare function getInitialSidebarRoutingTestHooks(): InitialSidebarRoutingTestHooks | null;
96
+ export declare function __acquireSidebarStateLockForTest(stateFile: string): Promise<(() => Promise<void>) | null>;
97
+ export declare function getSidebarState(stateFile?: string): Promise<SidebarState>;
98
+ export interface SidebarStateWriteOptions {
99
+ routingAuthoritative?: boolean;
100
+ resolvePreservedRouting?: (current: SidebarState) => (Pick<SidebarState, 'activeId' | 'route'> & {
101
+ state?: SidebarState;
102
+ }) | undefined | Promise<(Pick<SidebarState, 'activeId' | 'route'> & {
103
+ state?: SidebarState;
104
+ }) | undefined>;
105
+ onRoutingResolved?: (routing: Pick<SidebarState, 'activeId' | 'route'>) => void;
106
+ }
107
+ export declare function setSidebarState(state: SidebarState, stateFile?: string, options?: SidebarStateWriteOptions): Promise<void>;
59
108
  export declare function drainSidebarWrites(): Promise<void>;
60
109
  export declare function resolveActiveAccount(state: SidebarState): {
61
110
  id: string;
@@ -79,3 +128,4 @@ export interface QuotaPacing {
79
128
  runsOutAt: string | null;
80
129
  }
81
130
  export declare function computeQuotaPacing(window: QuotaWindow, windowMs: number, now: number): QuotaPacing | null;
131
+ export {};
@@ -1 +1 @@
1
- import{Eb as a,Fb as b,Gb as c,Hb as d,Ib as e,Jb as f,Kb as g,Lb as h,Mb as i,Nb as j,Ob as k,Pb as l,Qb as m}from"./index-bxs26mw4.js";import"./index-qsaph2g4.js";export{e as setSidebarState,g as resolveActiveAccount,c as normalizeSidebarState,a as getSidebarStateFile,d as getSidebarState,i as getFableRecoverySummary,j as getCollapsedQuotaSummary,h as formatScopedQuotaLabel,f as drainSidebarWrites,m as computeQuotaPacing,l as SEVEN_DAY_MS,k as FIVE_HOUR_MS,b as DEFAULT_SIDEBAR_STATE};
1
+ import{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q}from"./index-gj7emmj5.js";import"./index-xvfesm3w.js";import"./index-y2n9h7p6.js";import"./index-f5r96vyv.js";export{i as setSidebarState,k as resolveActiveAccount,c as normalizeSidebarState,a as getSidebarStateFile,h as getSidebarState,f as getInitialSidebarRoutingTestHooks,m as getFableRecoverySummary,n as getCollapsedQuotaSummary,l as formatScopedQuotaLabel,j as drainSidebarWrites,q as computeQuotaPacing,d as __setSidebarStateWriteTestHooks,e as __setInitialSidebarRoutingTestHooks,g as __acquireSidebarStateLockForTest,p as SEVEN_DAY_MS,o as FIVE_HOUR_MS,b as DEFAULT_SIDEBAR_STATE};
@@ -11,5 +11,17 @@ type KillswitchDialogConfig = {
11
11
  accounts?: Record<string, Record<string, number>>;
12
12
  };
13
13
  export declare function buildKillswitchThresholdSeed(config: KillswitchDialogConfig, accountIds: string[]): string;
14
+ export declare function buildAccountDialogOption(account: {
15
+ id: string;
16
+ label: string;
17
+ role: string;
18
+ enabled: boolean;
19
+ quotaPercent: number | null;
20
+ tierLabel?: string;
21
+ }): {
22
+ description?: string | undefined;
23
+ title: string;
24
+ value: string;
25
+ };
14
26
  export declare function openCommandDialog(api: TuiPluginApi, payload: OpenDialogPayload, apply: ApplyFn): void;
15
27
  export {};
@@ -1,4 +1,4 @@
1
- import{Vb as c,Wb as C,Xb as h}from"./index-qsaph2g4.js";var m=C((v,k)=>{(function(Q){if(typeof k==="object"&&typeof k.exports==="object"){var J=Q(h,v);if(J!==void 0)k.exports=J}else if(typeof define==="function"&&define.amd)define(["require","exports","./impl/format","./impl/edit","./impl/scanner","./impl/parser"],Q)})(function(Q,J){Object.defineProperty(J,"__esModule",{value:!0}),J.applyEdits=J.modify=J.format=J.printParseErrorCode=J.ParseErrorCode=J.stripComments=J.visit=J.getNodeValue=J.getNodePath=J.findNodeAtOffset=J.findNodeAtLocation=J.parseTree=J.parse=J.getLocation=J.SyntaxKind=J.ScanError=J.createScanner=void 0;let X=Q("./impl/format"),Z=Q("./impl/edit"),$=Q("./impl/scanner"),G=Q("./impl/parser");J.createScanner=$.createScanner;var V;(function(z){z[z.None=0]="None",z[z.UnexpectedEndOfComment=1]="UnexpectedEndOfComment",z[z.UnexpectedEndOfString=2]="UnexpectedEndOfString",z[z.UnexpectedEndOfNumber=3]="UnexpectedEndOfNumber",z[z.InvalidUnicode=4]="InvalidUnicode",z[z.InvalidEscapeCharacter=5]="InvalidEscapeCharacter",z[z.InvalidCharacter=6]="InvalidCharacter"})(V||(J.ScanError=V={}));var Y;(function(z){z[z.OpenBraceToken=1]="OpenBraceToken",z[z.CloseBraceToken=2]="CloseBraceToken",z[z.OpenBracketToken=3]="OpenBracketToken",z[z.CloseBracketToken=4]="CloseBracketToken",z[z.CommaToken=5]="CommaToken",z[z.ColonToken=6]="ColonToken",z[z.NullKeyword=7]="NullKeyword",z[z.TrueKeyword=8]="TrueKeyword",z[z.FalseKeyword=9]="FalseKeyword",z[z.StringLiteral=10]="StringLiteral",z[z.NumericLiteral=11]="NumericLiteral",z[z.LineCommentTrivia=12]="LineCommentTrivia",z[z.BlockCommentTrivia=13]="BlockCommentTrivia",z[z.LineBreakTrivia=14]="LineBreakTrivia",z[z.Trivia=15]="Trivia",z[z.Unknown=16]="Unknown",z[z.EOF=17]="EOF"})(Y||(J.SyntaxKind=Y={})),J.getLocation=G.getLocation,J.parse=G.parse,J.parseTree=G.parseTree,J.findNodeAtLocation=G.findNodeAtLocation,J.findNodeAtOffset=G.findNodeAtOffset,J.getNodePath=G.getNodePath,J.getNodeValue=G.getNodeValue,J.visit=G.visit,J.stripComments=G.stripComments;var L;(function(z){z[z.InvalidSymbol=1]="InvalidSymbol",z[z.InvalidNumberFormat=2]="InvalidNumberFormat",z[z.PropertyNameExpected=3]="PropertyNameExpected",z[z.ValueExpected=4]="ValueExpected",z[z.ColonExpected=5]="ColonExpected",z[z.CommaExpected=6]="CommaExpected",z[z.CloseBraceExpected=7]="CloseBraceExpected",z[z.CloseBracketExpected=8]="CloseBracketExpected",z[z.EndOfFileExpected=9]="EndOfFileExpected",z[z.InvalidCommentToken=10]="InvalidCommentToken",z[z.UnexpectedEndOfComment=11]="UnexpectedEndOfComment",z[z.UnexpectedEndOfString=12]="UnexpectedEndOfString",z[z.UnexpectedEndOfNumber=13]="UnexpectedEndOfNumber",z[z.InvalidUnicode=14]="InvalidUnicode",z[z.InvalidEscapeCharacter=15]="InvalidEscapeCharacter",z[z.InvalidCharacter=16]="InvalidCharacter"})(L||(J.ParseErrorCode=L={}));function B(z){switch(z){case 1:return"InvalidSymbol";case 2:return"InvalidNumberFormat";case 3:return"PropertyNameExpected";case 4:return"ValueExpected";case 5:return"ColonExpected";case 6:return"CommaExpected";case 7:return"CloseBraceExpected";case 8:return"CloseBracketExpected";case 9:return"EndOfFileExpected";case 10:return"InvalidCommentToken";case 11:return"UnexpectedEndOfComment";case 12:return"UnexpectedEndOfString";case 13:return"UnexpectedEndOfNumber";case 14:return"InvalidUnicode";case 15:return"InvalidEscapeCharacter";case 16:return"InvalidCharacter"}return"<unknown ParseErrorCode>"}J.printParseErrorCode=B;function j(z,H,N){return X.format(z,H,N)}J.format=j;function A(z,H,N,R){return Z.setProperty(z,H,N,R)}J.modify=A;function O(z,H){let N=H.slice(0).sort((U,D)=>{let T=U.offset-D.offset;if(T===0)return U.length-D.length;return T}),R=z.length;for(let U=N.length-1;U>=0;U--){let D=N[U];if(D.offset+D.length<=R)z=Z.applyEdit(z,D);else throw Error("Overlapping edit");R=D.offset}return z}J.applyEdits=O})});var q=c(m(),1);import{watch as y}from"node:fs";import{mkdir as S,readFile as w,rename as E,writeFile as p}from"node:fs/promises";import{homedir as l}from"node:os";import{basename as x,dirname as P,join as _}from"node:path";import{clearTimeout as F,setTimeout as f}from"node:timers";var i="OPENCODE_TUI_PREFERENCES_FILE",s="tui-preferences.jsonc";function b(){let Q=process.env[i];if(Q)return Q;let J=process.env.OPENCODE_CONFIG_DIR||_(process.env.XDG_CONFIG_HOME||_(l(),".config"),"opencode");return _(J,s)}function I(Q){return typeof Q==="object"&&Q!==null&&!Array.isArray(Q)}async function Vz(){try{let Q=await w(b(),"utf8"),J=[],X=q.parse(Q,J,{allowTrailingComma:!0});if(J.length>0)return{};return I(X)?X:{}}catch{return{}}}var d="anthropic-auth",n=160,u={forceToTop:!1,order:n,startCollapsed:!1,rememberCollapsed:!0,collapsed:null,pollMs:1500,refreshDebounceMs:200,header:{label:"CLAUDE",showVersion:!0},sections:{quota:!0,fallbackAccounts:!0,routing:!0,cache:!0,health:!0,pacing:!0},appearance:{barWidth:10,barFilledChar:"█",barEmptyChar:"░",warnThreshold:50,errorThreshold:80}};function W(Q,J){return typeof Q==="boolean"?Q:J}function M(Q,J,X,Z){if(typeof Q!=="number"||!Number.isFinite(Q))return J;return Math.min(Math.max(Math.round(Q),X),Z)}function a(Q,J,X){if(typeof Q!=="string"||Q.length===0)return J;return Q.slice(0,X)}function K(Q,J){if(typeof Q!=="string")return J;return[...Q][0]??J}function Yz(Q){let J=Q[d];if(!I(J))return structuredClone(u);let X=u,Z=I(J.header)?J.header:{},$=I(J.sections)?J.sections:{},G=I(J.appearance)?J.appearance:{},V=M(G.warnThreshold,X.appearance.warnThreshold,0,99),Y=Math.max(M(G.errorThreshold,X.appearance.errorThreshold,0,100),Math.min(V+1,100));return{forceToTop:W(J.forceToTop,X.forceToTop),order:M(J.order,X.order,-1e4,1e4),startCollapsed:W(J.startCollapsed,X.startCollapsed),rememberCollapsed:W(J.rememberCollapsed,X.rememberCollapsed),collapsed:typeof J.collapsed==="boolean"?J.collapsed:null,pollMs:M(J.pollMs,X.pollMs,500,30000),refreshDebounceMs:M(J.refreshDebounceMs,X.refreshDebounceMs,50,5000),header:{label:a(Z.label,X.header.label,20),showVersion:W(Z.showVersion,X.header.showVersion)},sections:{quota:W($.quota,X.sections.quota),fallbackAccounts:W($.fallbackAccounts,X.sections.fallbackAccounts),routing:W($.routing,X.sections.routing),cache:W($.cache,X.sections.cache),health:W($.health,X.sections.health),pacing:W($.pacing,X.sections.pacing)},appearance:{barWidth:M(G.barWidth,X.appearance.barWidth,4,40),barFilledChar:K(G.barFilledChar,X.appearance.barFilledChar),barEmptyChar:K(G.barEmptyChar,X.appearance.barEmptyChar),warnThreshold:V,errorThreshold:Y}}}var t=-1e5;function Hz(Q,J,X){let Z=Q[J];if(!I(Z))return X;if(Z.forceToTop===!0)return t+Object.keys(Q).indexOf(J);return M(Z.order,X,-1e4,1e4)}var o=`// Shared preferences for opencode TUI plugins.
1
+ import{pc as c,qc as C,rc as h}from"./index-f5r96vyv.js";var m=C((v,k)=>{(function(Q){if(typeof k==="object"&&typeof k.exports==="object"){var J=Q(h,v);if(J!==void 0)k.exports=J}else if(typeof define==="function"&&define.amd)define(["require","exports","./impl/format","./impl/edit","./impl/scanner","./impl/parser"],Q)})(function(Q,J){Object.defineProperty(J,"__esModule",{value:!0}),J.applyEdits=J.modify=J.format=J.printParseErrorCode=J.ParseErrorCode=J.stripComments=J.visit=J.getNodeValue=J.getNodePath=J.findNodeAtOffset=J.findNodeAtLocation=J.parseTree=J.parse=J.getLocation=J.SyntaxKind=J.ScanError=J.createScanner=void 0;let X=Q("./impl/format"),Z=Q("./impl/edit"),$=Q("./impl/scanner"),G=Q("./impl/parser");J.createScanner=$.createScanner;var V;(function(z){z[z.None=0]="None",z[z.UnexpectedEndOfComment=1]="UnexpectedEndOfComment",z[z.UnexpectedEndOfString=2]="UnexpectedEndOfString",z[z.UnexpectedEndOfNumber=3]="UnexpectedEndOfNumber",z[z.InvalidUnicode=4]="InvalidUnicode",z[z.InvalidEscapeCharacter=5]="InvalidEscapeCharacter",z[z.InvalidCharacter=6]="InvalidCharacter"})(V||(J.ScanError=V={}));var Y;(function(z){z[z.OpenBraceToken=1]="OpenBraceToken",z[z.CloseBraceToken=2]="CloseBraceToken",z[z.OpenBracketToken=3]="OpenBracketToken",z[z.CloseBracketToken=4]="CloseBracketToken",z[z.CommaToken=5]="CommaToken",z[z.ColonToken=6]="ColonToken",z[z.NullKeyword=7]="NullKeyword",z[z.TrueKeyword=8]="TrueKeyword",z[z.FalseKeyword=9]="FalseKeyword",z[z.StringLiteral=10]="StringLiteral",z[z.NumericLiteral=11]="NumericLiteral",z[z.LineCommentTrivia=12]="LineCommentTrivia",z[z.BlockCommentTrivia=13]="BlockCommentTrivia",z[z.LineBreakTrivia=14]="LineBreakTrivia",z[z.Trivia=15]="Trivia",z[z.Unknown=16]="Unknown",z[z.EOF=17]="EOF"})(Y||(J.SyntaxKind=Y={})),J.getLocation=G.getLocation,J.parse=G.parse,J.parseTree=G.parseTree,J.findNodeAtLocation=G.findNodeAtLocation,J.findNodeAtOffset=G.findNodeAtOffset,J.getNodePath=G.getNodePath,J.getNodeValue=G.getNodeValue,J.visit=G.visit,J.stripComments=G.stripComments;var L;(function(z){z[z.InvalidSymbol=1]="InvalidSymbol",z[z.InvalidNumberFormat=2]="InvalidNumberFormat",z[z.PropertyNameExpected=3]="PropertyNameExpected",z[z.ValueExpected=4]="ValueExpected",z[z.ColonExpected=5]="ColonExpected",z[z.CommaExpected=6]="CommaExpected",z[z.CloseBraceExpected=7]="CloseBraceExpected",z[z.CloseBracketExpected=8]="CloseBracketExpected",z[z.EndOfFileExpected=9]="EndOfFileExpected",z[z.InvalidCommentToken=10]="InvalidCommentToken",z[z.UnexpectedEndOfComment=11]="UnexpectedEndOfComment",z[z.UnexpectedEndOfString=12]="UnexpectedEndOfString",z[z.UnexpectedEndOfNumber=13]="UnexpectedEndOfNumber",z[z.InvalidUnicode=14]="InvalidUnicode",z[z.InvalidEscapeCharacter=15]="InvalidEscapeCharacter",z[z.InvalidCharacter=16]="InvalidCharacter"})(L||(J.ParseErrorCode=L={}));function B(z){switch(z){case 1:return"InvalidSymbol";case 2:return"InvalidNumberFormat";case 3:return"PropertyNameExpected";case 4:return"ValueExpected";case 5:return"ColonExpected";case 6:return"CommaExpected";case 7:return"CloseBraceExpected";case 8:return"CloseBracketExpected";case 9:return"EndOfFileExpected";case 10:return"InvalidCommentToken";case 11:return"UnexpectedEndOfComment";case 12:return"UnexpectedEndOfString";case 13:return"UnexpectedEndOfNumber";case 14:return"InvalidUnicode";case 15:return"InvalidEscapeCharacter";case 16:return"InvalidCharacter"}return"<unknown ParseErrorCode>"}J.printParseErrorCode=B;function j(z,H,N){return X.format(z,H,N)}J.format=j;function A(z,H,N,R){return Z.setProperty(z,H,N,R)}J.modify=A;function O(z,H){let N=H.slice(0).sort((U,D)=>{let T=U.offset-D.offset;if(T===0)return U.length-D.length;return T}),R=z.length;for(let U=N.length-1;U>=0;U--){let D=N[U];if(D.offset+D.length<=R)z=Z.applyEdit(z,D);else throw Error("Overlapping edit");R=D.offset}return z}J.applyEdits=O})});var q=c(m(),1);import{watch as y}from"node:fs";import{mkdir as S,readFile as w,rename as E,writeFile as p}from"node:fs/promises";import{homedir as l}from"node:os";import{basename as x,dirname as P,join as _}from"node:path";import{clearTimeout as F,setTimeout as f}from"node:timers";var i="OPENCODE_TUI_PREFERENCES_FILE",s="tui-preferences.jsonc";function b(){let Q=process.env[i];if(Q)return Q;let J=process.env.OPENCODE_CONFIG_DIR||_(process.env.XDG_CONFIG_HOME||_(l(),".config"),"opencode");return _(J,s)}function I(Q){return typeof Q==="object"&&Q!==null&&!Array.isArray(Q)}async function Vz(){try{let Q=await w(b(),"utf8"),J=[],X=q.parse(Q,J,{allowTrailingComma:!0});if(J.length>0)return{};return I(X)?X:{}}catch{return{}}}var d="anthropic-auth",n=160,u={forceToTop:!1,order:n,startCollapsed:!1,rememberCollapsed:!0,collapsed:null,pollMs:1500,refreshDebounceMs:200,header:{label:"CLAUDE",showVersion:!0},sections:{quota:!0,fallbackAccounts:!0,routing:!0,cache:!0,health:!0,pacing:!0},appearance:{barWidth:10,barFilledChar:"█",barEmptyChar:"░",warnThreshold:50,errorThreshold:80}};function W(Q,J){return typeof Q==="boolean"?Q:J}function M(Q,J,X,Z){if(typeof Q!=="number"||!Number.isFinite(Q))return J;return Math.min(Math.max(Math.round(Q),X),Z)}function a(Q,J,X){if(typeof Q!=="string"||Q.length===0)return J;return Q.slice(0,X)}function K(Q,J){if(typeof Q!=="string")return J;return[...Q][0]??J}function Yz(Q){let J=Q[d];if(!I(J))return structuredClone(u);let X=u,Z=I(J.header)?J.header:{},$=I(J.sections)?J.sections:{},G=I(J.appearance)?J.appearance:{},V=M(G.warnThreshold,X.appearance.warnThreshold,0,99),Y=Math.max(M(G.errorThreshold,X.appearance.errorThreshold,0,100),Math.min(V+1,100));return{forceToTop:W(J.forceToTop,X.forceToTop),order:M(J.order,X.order,-1e4,1e4),startCollapsed:W(J.startCollapsed,X.startCollapsed),rememberCollapsed:W(J.rememberCollapsed,X.rememberCollapsed),collapsed:typeof J.collapsed==="boolean"?J.collapsed:null,pollMs:M(J.pollMs,X.pollMs,500,30000),refreshDebounceMs:M(J.refreshDebounceMs,X.refreshDebounceMs,50,5000),header:{label:a(Z.label,X.header.label,20),showVersion:W(Z.showVersion,X.header.showVersion)},sections:{quota:W($.quota,X.sections.quota),fallbackAccounts:W($.fallbackAccounts,X.sections.fallbackAccounts),routing:W($.routing,X.sections.routing),cache:W($.cache,X.sections.cache),health:W($.health,X.sections.health),pacing:W($.pacing,X.sections.pacing)},appearance:{barWidth:M(G.barWidth,X.appearance.barWidth,4,40),barFilledChar:K(G.barFilledChar,X.appearance.barFilledChar),barEmptyChar:K(G.barEmptyChar,X.appearance.barEmptyChar),warnThreshold:V,errorThreshold:Y}}}var t=-1e5;function Hz(Q,J,X){let Z=Q[J];if(!I(Z))return X;if(Z.forceToTop===!0)return t+Object.keys(Q).indexOf(J);return M(Z.order,X,-1e4,1e4)}var o=`// Shared preferences for opencode TUI plugins.
2
2
  // One top-level key per plugin (short name). See each plugin's README for
3
3
  // its supported settings. This file is safe to hand-edit; plugins update
4
4
  // individual keys in place and preserve comments.
package/dist/tui.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  /** @jsxImportSource @opentui/solid */
2
+ import { formatQuotaMoney } from '@cortexkit/anthropic-auth-core';
2
3
  import type { TuiPluginModule } from '@opencode-ai/plugin/tui';
4
+ export { formatQuotaMoney };
3
5
  declare const plugin: TuiPluginModule & {
4
6
  id: string;
5
7
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/opencode-anthropic-auth",
3
- "version": "1.15.1",
3
+ "version": "1.17.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -61,7 +61,7 @@
61
61
  "@opencode-ai/plugin": "*"
62
62
  },
63
63
  "dependencies": {
64
- "@cortexkit/anthropic-auth-core": "1.15.1",
64
+ "@cortexkit/anthropic-auth-core": "1.17.0",
65
65
  "@opentui/core": "0.4.3",
66
66
  "@opentui/solid": "0.4.3",
67
67
  "jsonc-parser": "^3.3.1",
@@ -15,6 +15,15 @@ export interface AccountQuota {
15
15
  five_hour?: QuotaWindow
16
16
  seven_day?: QuotaWindow
17
17
  scoped?: ScopedQuotaWindow[]
18
+ extraUsage?: {
19
+ used: { amountMinor: number; currency: string; exponent: number }
20
+ limit: { amountMinor: number; currency: string; exponent: number }
21
+ utilizationPercent?: number
22
+ severity?: string
23
+ exhausted: boolean
24
+ }
25
+ bindingWindow?: string
26
+ fallbackAdvised?: boolean
18
27
  }
19
28
 
20
29
  export interface SidebarAccountState {
@@ -25,6 +34,7 @@ export interface SidebarAccountState {
25
34
  // True when the account's refresh token is permanently dead (400
26
35
  // invalid_grant) and it needs a re-login — distinct from a transient backoff.
27
36
  needsReauth: boolean
37
+ tierLabel?: string
28
38
  }
29
39
 
30
40
  export interface FableRecoverySidebarState {
@@ -37,6 +47,7 @@ export interface FableRecoverySidebarState {
37
47
  export interface SidebarState {
38
48
  main: {
39
49
  quota: AccountQuota | null
50
+ tierLabel?: string
40
51
  quotaBackedOff?: boolean
41
52
  quotaBackoffUntil?: number
42
53
  refreshBackedOff?: boolean
@@ -56,9 +67,20 @@ export interface SidebarState {
56
67
  lastUpdated: number
57
68
  }
58
69
 
59
- import { mkdir, readFile, writeFile } from 'node:fs/promises'
70
+ import { randomUUID } from 'node:crypto'
71
+ import {
72
+ mkdir,
73
+ readFile,
74
+ rename,
75
+ rm,
76
+ rmdir,
77
+ stat,
78
+ unlink,
79
+ writeFile,
80
+ } from 'node:fs/promises'
60
81
  import { tmpdir } from 'node:os'
61
82
  import { dirname, join } from 'node:path'
83
+ import { logger } from '@cortexkit/anthropic-auth-core'
62
84
 
63
85
  const STATE_FILE_ENV = 'OPENCODE_ANTHROPIC_AUTH_SIDEBAR_STATE_FILE'
64
86
  const DEFAULT_STATE_DIR = join(tmpdir(), 'opencode-anthropic-auth')
@@ -136,9 +158,53 @@ function normalizeAccountQuota(value: unknown): AccountQuota | null {
136
158
  quota.scoped = scoped
137
159
  }
138
160
 
161
+ if (isRecord(value.extraUsage)) {
162
+ const used = normalizeQuotaMoney(value.extraUsage.used)
163
+ const limit = normalizeQuotaMoney(value.extraUsage.limit)
164
+ if (used && limit && typeof value.extraUsage.exhausted === 'boolean') {
165
+ quota.extraUsage = {
166
+ used,
167
+ limit,
168
+ ...(typeof value.extraUsage.utilizationPercent === 'number' &&
169
+ Number.isFinite(value.extraUsage.utilizationPercent) && {
170
+ utilizationPercent: value.extraUsage.utilizationPercent,
171
+ }),
172
+ ...(typeof value.extraUsage.severity === 'string' && {
173
+ severity: value.extraUsage.severity,
174
+ }),
175
+ exhausted: value.extraUsage.exhausted,
176
+ }
177
+ }
178
+ }
179
+ if (typeof value.bindingWindow === 'string' && value.bindingWindow.trim()) {
180
+ quota.bindingWindow = value.bindingWindow.trim()
181
+ }
182
+ if (typeof value.fallbackAdvised === 'boolean') {
183
+ quota.fallbackAdvised = value.fallbackAdvised
184
+ }
185
+
139
186
  return Object.keys(quota).length ? quota : null
140
187
  }
141
188
 
189
+ function normalizeQuotaMoney(value: unknown) {
190
+ if (!isRecord(value)) return undefined
191
+ if (
192
+ !Number.isInteger(value.amountMinor) ||
193
+ typeof value.currency !== 'string' ||
194
+ !/^[A-Za-z]{3}$/.test(value.currency.trim()) ||
195
+ !Number.isInteger(value.exponent) ||
196
+ (value.exponent as number) < 0 ||
197
+ (value.exponent as number) > 20
198
+ ) {
199
+ return undefined
200
+ }
201
+ return {
202
+ amountMinor: value.amountMinor as number,
203
+ currency: value.currency.trim(),
204
+ exponent: value.exponent as number,
205
+ }
206
+ }
207
+
142
208
  export function normalizeSidebarState(raw: unknown): SidebarState {
143
209
  if (!isRecord(raw)) return { ...DEFAULT_SIDEBAR_STATE }
144
210
 
@@ -146,6 +212,9 @@ export function normalizeSidebarState(raw: unknown): SidebarState {
146
212
  if (isRecord(raw.main)) {
147
213
  const m = raw.main
148
214
  main.quota = normalizeAccountQuota(m.quota)
215
+ if (typeof m.tierLabel === 'string' && m.tierLabel.trim()) {
216
+ main.tierLabel = m.tierLabel.trim()
217
+ }
149
218
  if (typeof m.quotaBackedOff === 'boolean')
150
219
  main.quotaBackedOff = m.quotaBackedOff
151
220
  if (typeof m.quotaBackoffUntil === 'number')
@@ -167,6 +236,10 @@ export function normalizeSidebarState(raw: unknown): SidebarState {
167
236
  enabled: typeof entry.enabled === 'boolean' ? entry.enabled : false,
168
237
  needsReauth:
169
238
  typeof entry.needsReauth === 'boolean' ? entry.needsReauth : false,
239
+ tierLabel:
240
+ typeof entry.tierLabel === 'string' && entry.tierLabel.trim()
241
+ ? entry.tierLabel.trim()
242
+ : undefined,
170
243
  }))
171
244
  : []
172
245
 
@@ -238,23 +311,327 @@ export function normalizeSidebarState(raw: unknown): SidebarState {
238
311
 
239
312
  let writeChain: Promise<void> = Promise.resolve()
240
313
 
241
- export async function getSidebarState(): Promise<SidebarState> {
314
+ // Wait through ordinary contention while staying below the 2s stale-eviction window.
315
+ const SIDEBAR_LOCK_BUDGET_MS = 1_000
316
+ const SIDEBAR_LOCK_STALE_MS = 2_000
317
+ const SIDEBAR_LOCK_RETRY_MIN_MS = 5
318
+ const SIDEBAR_LOCK_RETRY_MAX_MS = 15
319
+
320
+ interface SidebarStateWriteTestHooks {
321
+ afterMergeRead?: (stateFile: string) => void | Promise<void>
322
+ beforeRename?: (stateFile: string, tempFile: string) => void | Promise<void>
323
+ afterRename?: (stateFile: string) => void | Promise<void>
324
+ afterStaleLockStat?: (lockDir: string) => void | Promise<void>
325
+ onStaleLockClaimed?: (lockDir: string) => void | Promise<void>
326
+ onLockAcquired?: (lockDir: string) => void | Promise<void>
327
+ beforeReleaseDirectoryRemoval?: (lockDir: string) => void | Promise<void>
328
+ lockBudgetMs?: number
329
+ lockRetryMinMs?: number
330
+ lockRetryMaxMs?: number
331
+ }
332
+
333
+ let sidebarStateWriteTestHooks: SidebarStateWriteTestHooks | null = null
334
+
335
+ export function __setSidebarStateWriteTestHooks(
336
+ hooks: SidebarStateWriteTestHooks | null,
337
+ ): void {
338
+ sidebarStateWriteTestHooks = hooks
339
+ }
340
+
341
+ // Boot-routing test hooks live here rather than in index.ts: the plugin entry
342
+ // module must export nothing beyond the plugin factory — opencode's plugin
343
+ // loader treats extra entry-point exports as plugin candidates and fails to
344
+ // load the plugin (same incident class as the removed
345
+ // __setBootProfileHydrationForTest export).
346
+ export interface InitialSidebarRoutingTestHooks {
347
+ beforeSidebarRead?: () => void | Promise<void>
348
+ beforeStorageLoad?: () => void | Promise<void>
349
+ afterStorageLoad?: () => void | Promise<void>
350
+ }
351
+
352
+ let initialSidebarRoutingTestHooks: InitialSidebarRoutingTestHooks | null = null
353
+
354
+ export function __setInitialSidebarRoutingTestHooks(
355
+ hooks: InitialSidebarRoutingTestHooks | null,
356
+ ): void {
357
+ initialSidebarRoutingTestHooks = hooks
358
+ }
359
+
360
+ export function getInitialSidebarRoutingTestHooks(): InitialSidebarRoutingTestHooks | null {
361
+ return initialSidebarRoutingTestHooks
362
+ }
363
+
364
+ function sleep(ms: number): Promise<void> {
365
+ return new Promise((resolve) => setTimeout(resolve, ms))
366
+ }
367
+
368
+ async function acquireSidebarStateLock(stateFile: string): Promise<{
369
+ release: () => Promise<void>
370
+ ownsLock: () => Promise<boolean>
371
+ } | null> {
372
+ const lockDir = `${stateFile}.lock`
373
+ const budgetMs =
374
+ sidebarStateWriteTestHooks?.lockBudgetMs ?? SIDEBAR_LOCK_BUDGET_MS
375
+ const retryMinMs =
376
+ sidebarStateWriteTestHooks?.lockRetryMinMs ?? SIDEBAR_LOCK_RETRY_MIN_MS
377
+ const retryMaxMs =
378
+ sidebarStateWriteTestHooks?.lockRetryMaxMs ?? SIDEBAR_LOCK_RETRY_MAX_MS
379
+ const deadline = Date.now() + budgetMs
380
+
381
+ while (true) {
382
+ try {
383
+ await mkdir(lockDir)
384
+ const ownerId = randomUUID()
385
+ const ownerFile = join(lockDir, ownerId)
386
+ await writeFile(ownerFile, '', {
387
+ encoding: 'utf8',
388
+ mode: 0o600,
389
+ flag: 'wx',
390
+ })
391
+ await sidebarStateWriteTestHooks?.onLockAcquired?.(lockDir)
392
+ return {
393
+ ownsLock: async () => {
394
+ try {
395
+ await stat(ownerFile)
396
+ return true
397
+ } catch {
398
+ return false
399
+ }
400
+ },
401
+ release: async () => {
402
+ // Unlinking this acquisition's unique file makes the filesystem reject
403
+ // a stale release after eviction handed the path to a successor.
404
+ try {
405
+ await unlink(ownerFile)
406
+ } catch {
407
+ return
408
+ }
409
+ await sidebarStateWriteTestHooks?.beforeReleaseDirectoryRemoval?.(
410
+ lockDir,
411
+ )
412
+ await rmdir(lockDir).catch(() => {})
413
+ },
414
+ }
415
+ } catch (error) {
416
+ if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {
417
+ logger.trace('sidebar', 'state lock unavailable; write skipped', {
418
+ stateFile,
419
+ error: error instanceof Error ? error.message : String(error),
420
+ })
421
+ return null
422
+ }
423
+ }
424
+
425
+ try {
426
+ const lockStat = await stat(lockDir)
427
+ if (Date.now() - lockStat.mtimeMs > SIDEBAR_LOCK_STALE_MS) {
428
+ await sidebarStateWriteTestHooks?.afterStaleLockStat?.(lockDir)
429
+ const evictedLockDir = `${lockDir}.evict-${process.pid}-${randomUUID()}`
430
+ try {
431
+ await rename(lockDir, evictedLockDir)
432
+ } catch (error) {
433
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') continue
434
+ throw error
435
+ }
436
+ await sidebarStateWriteTestHooks?.onStaleLockClaimed?.(lockDir)
437
+ await rm(evictedLockDir, { recursive: true, force: true }).catch(
438
+ () => {},
439
+ )
440
+ continue
441
+ }
442
+ } catch (error) {
443
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') continue
444
+ logger.trace('sidebar', 'state lock inspection failed; write skipped', {
445
+ stateFile,
446
+ error: error instanceof Error ? error.message : String(error),
447
+ })
448
+ return null
449
+ }
450
+
451
+ const remainingMs = deadline - Date.now()
452
+ if (remainingMs <= 0) {
453
+ logger.warn('sidebar', 'lock budget exhausted, write skipped', {
454
+ stateFile,
455
+ budgetMs,
456
+ })
457
+ return null
458
+ }
459
+ const jitterMs =
460
+ retryMinMs + Math.floor(Math.random() * (retryMaxMs - retryMinMs + 1))
461
+ await sleep(Math.min(jitterMs, remainingMs))
462
+ }
463
+ }
464
+
465
+ export async function __acquireSidebarStateLockForTest(
466
+ stateFile: string,
467
+ ): Promise<(() => Promise<void>) | null> {
468
+ return (await acquireSidebarStateLock(stateFile))?.release ?? null
469
+ }
470
+
471
+ async function writeSidebarStateAtomic(
472
+ stateFile: string,
473
+ state: SidebarState,
474
+ ownsLock: () => Promise<boolean>,
475
+ ): Promise<'written' | 'lock-lost-before-rename' | 'lock-lost-after-rename'> {
476
+ const tempFile = `${stateFile}.${process.pid}.${randomUUID()}.tmp`
477
+ await writeFile(tempFile, JSON.stringify(state), {
478
+ encoding: 'utf8',
479
+ mode: 0o600,
480
+ })
242
481
  try {
243
- const raw = await readFile(getSidebarStateFile(), 'utf8')
482
+ if (!(await ownsLock())) {
483
+ logger.trace('sidebar', 'state lock lost before rename; write aborted', {
484
+ stateFile,
485
+ })
486
+ await rm(tempFile, { force: true }).catch(() => {})
487
+ return 'lock-lost-before-rename'
488
+ }
489
+ await sidebarStateWriteTestHooks?.beforeRename?.(stateFile, tempFile)
490
+ // No production await separates this ownership fence from rename. A process
491
+ // freeze between the adjacent syscalls remains possible, so rename is also
492
+ // fenced from the other side below.
493
+ if (!(await ownsLock())) {
494
+ logger.trace('sidebar', 'state lock lost before rename; write aborted', {
495
+ stateFile,
496
+ })
497
+ await rm(tempFile, { force: true }).catch(() => {})
498
+ return 'lock-lost-before-rename'
499
+ }
500
+ await rename(tempFile, stateFile)
501
+ await sidebarStateWriteTestHooks?.afterRename?.(stateFile)
502
+ if (!(await ownsLock())) return 'lock-lost-after-rename'
503
+ return 'written'
504
+ } catch (error) {
505
+ await rm(tempFile, { force: true }).catch(() => {})
506
+ throw error
507
+ }
508
+ }
509
+
510
+ export async function getSidebarState(
511
+ stateFile = getSidebarStateFile(),
512
+ ): Promise<SidebarState> {
513
+ try {
514
+ const raw = await readFile(stateFile, 'utf8')
244
515
  return normalizeSidebarState(JSON.parse(raw))
245
516
  } catch {
246
517
  return DEFAULT_SIDEBAR_STATE
247
518
  }
248
519
  }
249
520
 
521
+ export interface SidebarStateWriteOptions {
522
+ routingAuthoritative?: boolean
523
+ resolvePreservedRouting?: (current: SidebarState) =>
524
+ | (Pick<SidebarState, 'activeId' | 'route'> & {
525
+ state?: SidebarState
526
+ })
527
+ | undefined
528
+ | Promise<
529
+ | (Pick<SidebarState, 'activeId' | 'route'> & {
530
+ state?: SidebarState
531
+ })
532
+ | undefined
533
+ >
534
+ onRoutingResolved?: (
535
+ routing: Pick<SidebarState, 'activeId' | 'route'>,
536
+ ) => void
537
+ }
538
+
250
539
  export async function setSidebarState(
251
540
  state: SidebarState,
252
541
  stateFile = getSidebarStateFile(),
542
+ options: SidebarStateWriteOptions = {},
253
543
  ): Promise<void> {
254
544
  writeChain = writeChain
255
545
  .then(async () => {
256
546
  await mkdir(dirname(stateFile), { recursive: true })
257
- await writeFile(stateFile, JSON.stringify(state), 'utf8')
547
+ for (let attempt = 0; attempt < 2; attempt++) {
548
+ const repairingPostRenameLoss = attempt === 1
549
+ const lock = await acquireSidebarStateLock(stateFile)
550
+ // Sidebar frames are display-only and refresh within seconds; dropping one
551
+ // is safer than clobbering routing state that may stay authoritative until
552
+ // another process handles its next request.
553
+ if (!lock) {
554
+ if (repairingPostRenameLoss) {
555
+ logger.warn(
556
+ 'sidebar',
557
+ 'post-rename repair lock unavailable; write skipped',
558
+ { stateFile },
559
+ )
560
+ }
561
+ return
562
+ }
563
+
564
+ let stateToWrite = state
565
+ let result:
566
+ | 'written'
567
+ | 'lock-lost-before-rename'
568
+ | 'lock-lost-after-rename'
569
+ try {
570
+ if (options.routingAuthoritative === false) {
571
+ const current = await getSidebarState(stateFile)
572
+ const preservedRouting =
573
+ await options.resolvePreservedRouting?.(current)
574
+ if (preservedRouting) {
575
+ const preservedState = preservedRouting.state ?? state
576
+ stateToWrite = {
577
+ ...preservedState,
578
+ activeId: preservedRouting.activeId,
579
+ route: preservedRouting.route,
580
+ lastUpdated: Math.max(
581
+ preservedState.lastUpdated,
582
+ current.lastUpdated,
583
+ ),
584
+ }
585
+ }
586
+ await sidebarStateWriteTestHooks?.afterMergeRead?.(stateFile)
587
+ } else if (repairingPostRenameLoss) {
588
+ const current = await getSidebarState(stateFile)
589
+ // A successor owns its fresh account/quota and recovery snapshots;
590
+ // this routing frame may only republish its decision and shared UI state.
591
+ stateToWrite = {
592
+ ...current,
593
+ activeId: state.activeId,
594
+ route: state.route,
595
+ relay: state.relay,
596
+ fastMode: state.fastMode,
597
+ cacheKeep: state.cacheKeep,
598
+ lastUpdated: Math.max(current.lastUpdated, state.lastUpdated),
599
+ }
600
+ }
601
+ result = await writeSidebarStateAtomic(
602
+ stateFile,
603
+ stateToWrite,
604
+ lock.ownsLock,
605
+ )
606
+ } finally {
607
+ await lock.release()
608
+ }
609
+
610
+ if (result === 'lock-lost-after-rename') {
611
+ if (repairingPostRenameLoss) {
612
+ logger.warn(
613
+ 'sidebar',
614
+ 'post-rename repair lost lock; write skipped',
615
+ { stateFile },
616
+ )
617
+ return
618
+ }
619
+ logger.warn(
620
+ 'sidebar',
621
+ 'state lock lost after rename; repairing write',
622
+ {
623
+ stateFile,
624
+ },
625
+ )
626
+ continue
627
+ }
628
+ if (result === 'lock-lost-before-rename') return
629
+ options.onRoutingResolved?.({
630
+ activeId: stateToWrite.activeId,
631
+ route: stateToWrite.route,
632
+ })
633
+ return
634
+ }
258
635
  })
259
636
  .catch(() => {
260
637
  // Best-effort — sidebar is non-critical