@cortexkit/opencode-anthropic-auth 1.18.0 → 1.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -2
- package/dist/cli.js +1 -1
- package/dist/fable-fallback.d.ts +12 -0
- package/dist/{index-k5enth8h.js → index-075t8qec.js} +1 -1
- package/dist/{index-kckdabb4.js → index-2k47hc8b.js} +1 -1
- package/dist/index-a13w4gm9.js +2 -0
- package/dist/{index-1ets4bf8.js → index-dex8fs0m.js} +1 -1
- package/dist/{index-9aeag0wm.js → index-dzs7w297.js} +1 -1
- package/dist/index-vwgzgh5x.js +422 -0
- package/dist/index.js +22 -12
- package/dist/{logger-whpn7rjx.js → logger-n49md99d.js} +1 -1
- package/dist/rpc/port-file.js +1 -1
- package/dist/rpc/rpc-client.js +1 -1
- package/dist/rpc/rpc-dir.js +1 -1
- package/dist/server-fallback.d.ts +26 -0
- package/dist/sidebar-state.d.ts +3 -1
- package/dist/sidebar-state.js +1 -1
- package/dist/transform.d.ts +7 -1
- package/dist/tui-preferences.d.ts +4 -1
- package/dist/tui-preferences.js +2 -2
- package/package.json +2 -2
- package/src/sidebar-state.ts +15 -2
- package/src/tui-compiled/sidebar-state.ts +15 -2
- package/src/tui-compiled/tui-preferences.ts +57 -50
- package/src/tui-compiled/tui.tsx +1 -1
- package/src/tui-preferences.ts +57 -50
- package/src/tui.tsx +3 -3
- package/dist/index-h0pmqm96.js +0 -422
- package/dist/index-xjvcfyt6.js +0 -2
package/dist/sidebar-state.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r}from"./index-
|
|
1
|
+
import{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r}from"./index-a13w4gm9.js";import"./index-vwgzgh5x.js";import"./index-dex8fs0m.js";import"./index-dzs7w297.js";export{i as setSidebarState,k as resolveActiveAccount,c as normalizeSidebarState,a as getSidebarStateFile,h as getSidebarState,f as getInitialSidebarRoutingTestHooks,n as getFableRecoverySummary,o as getCollapsedQuotaSummary,l as formatScopedQuotaLabel,m as formatFallbackModelLabel,j as drainSidebarWrites,r as computeQuotaPacing,d as __setSidebarStateWriteTestHooks,e as __setInitialSidebarRoutingTestHooks,g as __acquireSidebarStateLockForTest,q as SEVEN_DAY_MS,p as FIVE_HOUR_MS,b as DEFAULT_SIDEBAR_STATE};
|
package/dist/transform.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Cache1hMode, type ClaudeCodeIdentity } from '@cortexkit/anthropic-auth-core';
|
|
2
|
+
import { type ServerSideFallbackOutcome } from './server-fallback';
|
|
2
3
|
export type FetchInput = string | URL | Request;
|
|
3
4
|
/**
|
|
4
5
|
* Merge headers from a Request object and/or a RequestInit headers value
|
|
@@ -84,14 +85,19 @@ export declare function rewriteRequestBody(body: string, options?: {
|
|
|
84
85
|
identity?: ClaudeCodeIdentity;
|
|
85
86
|
perf?: RewritePerfCallback;
|
|
86
87
|
hybridStandbyAnchor?: HybridMessageCacheAnchor;
|
|
88
|
+
serverSideFallbackEnabled?: boolean;
|
|
87
89
|
}): Promise<string>;
|
|
88
90
|
/**
|
|
89
91
|
* Create a streaming response that strips the tool prefix from tool names.
|
|
90
92
|
*/
|
|
91
93
|
export declare function createStrippedStream(response: Response, options?: {
|
|
92
94
|
perf?: RewritePerfCallback;
|
|
93
|
-
onContentFilter?: (
|
|
95
|
+
onContentFilter?: (context?: {
|
|
96
|
+
completedToolUse: boolean;
|
|
97
|
+
}) => boolean | undefined;
|
|
94
98
|
onComplete?: (finishReason: string) => void;
|
|
95
99
|
contentFilterModel?: unknown;
|
|
100
|
+
serverSideFallbackModel?: string;
|
|
101
|
+
onServerSideFallbackOutcome?: (outcome: ServerSideFallbackOutcome) => void;
|
|
96
102
|
}): Response;
|
|
97
103
|
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { watch } from 'node:fs';
|
|
1
2
|
export declare const TUI_PREFS_FILE_ENV = "OPENCODE_TUI_PREFERENCES_FILE";
|
|
2
3
|
export declare function getTuiPreferencesFile(): string;
|
|
3
4
|
export declare function readTuiPreferencesFile(): Promise<Record<string, unknown>>;
|
|
@@ -37,5 +38,7 @@ export declare function resolveAnthropicAuthPrefs(root: Record<string, unknown>)
|
|
|
37
38
|
export declare function computeEffectiveOrder(root: Record<string, unknown>, pluginKey: string, defaultOrder: number): number;
|
|
38
39
|
type JsonValue = string | number | boolean | null;
|
|
39
40
|
export declare function queueTuiPreferenceUpdate(pluginKey: string, path: string[], value: JsonValue): Promise<void>;
|
|
40
|
-
export declare function watchTuiPreferences(onChange: () => void
|
|
41
|
+
export declare function watchTuiPreferences(onChange: () => void, options?: {
|
|
42
|
+
watchDirectory?: typeof watch;
|
|
43
|
+
}): () => void;
|
|
41
44
|
export {};
|
package/dist/tui-preferences.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{tc as c,uc as C,vc as v}from"./index-dzs7w297.js";var m=C((h,k)=>{(function(Q){if(typeof k==="object"&&typeof k.exports==="object"){var J=Q(v,h);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"),G=Q("./impl/edit"),Z=Q("./impl/scanner"),$=Q("./impl/parser");J.createScanner=Z.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=$.getLocation,J.parse=$.parse,J.parseTree=$.parseTree,J.findNodeAtLocation=$.findNodeAtLocation,J.findNodeAtOffset=$.findNodeAtOffset,J.getNodePath=$.getNodePath,J.getNodeValue=$.getNodeValue,J.visit=$.visit,J.stripComments=$.stripComments;var A;(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"})(A||(J.ParseErrorCode=A={}));function L(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=L;function W(z,B,j){return X.format(z,B,j)}J.format=W;function q(z,B,j,R){return G.setProperty(z,B,j,R)}J.modify=q;function O(z,B){let j=B.slice(0).sort((M,N)=>{let T=M.offset-N.offset;if(T===0)return M.length-N.length;return T}),R=z.length;for(let M=j.length-1;M>=0;M--){let N=j[M];if(N.offset+N.length<=R)z=G.applyEdit(z,N);else throw Error("Overlapping edit");R=N.offset}return z}J.applyEdits=O})});var I=c(m(),1);import{readFileSync as y,watch as S}from"node:fs";import{mkdir as E,readFile as g,rename as p,writeFile as l}from"node:fs/promises";import{homedir as x}from"node:os";import{basename as i,dirname as P,join as w}from"node:path";import{clearTimeout as _,setTimeout as f}from"node:timers";var s="OPENCODE_TUI_PREFERENCES_FILE",d="tui-preferences.jsonc";function b(){let Q=process.env[s];if(Q)return Q;let J=process.env.OPENCODE_CONFIG_DIR||w(process.env.XDG_CONFIG_HOME||w(x(),".config"),"opencode");return w(J,d)}function U(Q){return typeof Q==="object"&&Q!==null&&!Array.isArray(Q)}async function Yz(){try{let Q=await g(b(),"utf8"),J=[],X=I.parse(Q,J,{allowTrailingComma:!0});if(J.length>0)return{};return U(X)?X:{}}catch{return{}}}var n="anthropic-auth",t=160,u={forceToTop:!1,order:t,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 H(Q,J){return typeof Q==="boolean"?Q:J}function D(Q,J,X,G){if(typeof Q!=="number"||!Number.isFinite(Q))return J;return Math.min(Math.max(Math.round(Q),X),G)}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 Bz(Q){let J=Q[n];if(!U(J))return structuredClone(u);let X=u,G=U(J.header)?J.header:{},Z=U(J.sections)?J.sections:{},$=U(J.appearance)?J.appearance:{},V=D($.warnThreshold,X.appearance.warnThreshold,0,99),Y=Math.max(D($.errorThreshold,X.appearance.errorThreshold,0,100),Math.min(V+1,100));return{forceToTop:H(J.forceToTop,X.forceToTop),order:D(J.order,X.order,-1e4,1e4),startCollapsed:H(J.startCollapsed,X.startCollapsed),rememberCollapsed:H(J.rememberCollapsed,X.rememberCollapsed),collapsed:typeof J.collapsed==="boolean"?J.collapsed:null,pollMs:D(J.pollMs,X.pollMs,500,30000),refreshDebounceMs:D(J.refreshDebounceMs,X.refreshDebounceMs,50,5000),header:{label:a(G.label,X.header.label,20),showVersion:H(G.showVersion,X.header.showVersion)},sections:{quota:H(Z.quota,X.sections.quota),fallbackAccounts:H(Z.fallbackAccounts,X.sections.fallbackAccounts),routing:H(Z.routing,X.sections.routing),cache:H(Z.cache,X.sections.cache),health:H(Z.health,X.sections.health),pacing:H(Z.pacing,X.sections.pacing)},appearance:{barWidth:D($.barWidth,X.appearance.barWidth,4,40),barFilledChar:K($.barFilledChar,X.appearance.barFilledChar),barEmptyChar:K($.barEmptyChar,X.appearance.barEmptyChar),warnThreshold:V,errorThreshold:Y}}}var o=-1e5;function Hz(Q,J,X){let G=Q[J];if(!U(G))return X;if(G.forceToTop===!0)return o+Object.keys(Q).indexOf(J);return D(G.order,X,-1e4,1e4)}var e=`// 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.
|
|
5
5
|
{}
|
|
6
|
-
`;async function
|
|
6
|
+
`;async function r(Q,J,X){let G=b();await E(P(G),{recursive:!0});let Z;try{Z=await g(G,"utf8")}catch{Z=""}if(Z.trim()==="")Z=e;let $=I.modify(Z,[Q,...J],X,{formattingOptions:{insertSpaces:!0,tabSize:2}}),V=I.applyEdits(Z,$),Y=`${G}.${process.pid}.tmp`;await l(Y,V,"utf8"),await p(Y,G)}var F=Promise.resolve();function Wz(Q,J,X){return F=F.then(()=>r(Q,J,X)).catch(()=>{}),F}var zz=150,Jz=100;function jz(Q,J={}){let X=b(),G=i(X),Z=null,$=null;try{$=y(X,"utf8")}catch{}let V=!1,Y=async()=>{let z=await g(X,"utf8").catch(()=>null);if(V||z===null)return;if(z===$)return;$=z,Q()},A=()=>{if(Z)_(Z);Z=f(()=>{Z=null,Y()},zz)},L=null;try{L=(J.watchDirectory??S)(P(X),(z,B)=>{let j=B===G||B?.startsWith(`${G}.`)&&B.endsWith(".tmp");if(B!=null&&!j)return;A()})}catch{if($===null)return()=>{}}let W=null,q=!1,O=()=>{if(V||W)return;W=f(()=>{if(W=null,V||q)return;q=!0,Y().finally(()=>{q=!1,O()})},Jz),W.unref?.()};return O(),()=>{if(V=!0,Z)_(Z);if(W)_(W);L?.close()}}export{jz as watchTuiPreferences,Bz as resolveAnthropicAuthPrefs,Yz as readTuiPreferencesFile,Wz as queueTuiPreferenceUpdate,b as getTuiPreferencesFile,Hz as computeEffectiveOrder,s as TUI_PREFS_FILE_ENV,n as PLUGIN_KEY,t as DEFAULT_SLOT_ORDER,u as DEFAULT_PREFS};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/opencode-anthropic-auth",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.19.1",
|
|
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.
|
|
64
|
+
"@cortexkit/anthropic-auth-core": "1.19.1",
|
|
65
65
|
"@opentui/core": "0.4.3",
|
|
66
66
|
"@opentui/solid": "0.4.3",
|
|
67
67
|
"jsonc-parser": "^3.3.1",
|
package/src/sidebar-state.ts
CHANGED
|
@@ -39,7 +39,7 @@ export interface SidebarAccountState {
|
|
|
39
39
|
|
|
40
40
|
export interface FableRecoverySidebarState {
|
|
41
41
|
sessionId: string
|
|
42
|
-
mode: 'opus' | 'fable'
|
|
42
|
+
mode: 'opus' | 'fable' | 'server'
|
|
43
43
|
remaining: number
|
|
44
44
|
changedAt: number
|
|
45
45
|
/**
|
|
@@ -48,6 +48,8 @@ export interface FableRecoverySidebarState {
|
|
|
48
48
|
* state files; older recoveries default to Fable 5 in the summary.
|
|
49
49
|
*/
|
|
50
50
|
requestedModelId?: string
|
|
51
|
+
/** Anthropic-selected target while server-side safety fallback is active. */
|
|
52
|
+
targetModelId?: string
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
export interface SidebarState {
|
|
@@ -280,7 +282,9 @@ export function normalizeSidebarState(raw: unknown): SidebarState {
|
|
|
280
282
|
.flatMap((recovery): FableRecoverySidebarState[] => {
|
|
281
283
|
if (
|
|
282
284
|
typeof recovery.sessionId !== 'string' ||
|
|
283
|
-
(recovery.mode !== 'opus' &&
|
|
285
|
+
(recovery.mode !== 'opus' &&
|
|
286
|
+
recovery.mode !== 'fable' &&
|
|
287
|
+
recovery.mode !== 'server') ||
|
|
284
288
|
typeof recovery.remaining !== 'number' ||
|
|
285
289
|
!Number.isFinite(recovery.remaining) ||
|
|
286
290
|
typeof recovery.changedAt !== 'number' ||
|
|
@@ -297,6 +301,10 @@ export function normalizeSidebarState(raw: unknown): SidebarState {
|
|
|
297
301
|
typeof recovery.requestedModelId === 'string'
|
|
298
302
|
? recovery.requestedModelId
|
|
299
303
|
: undefined,
|
|
304
|
+
targetModelId:
|
|
305
|
+
typeof recovery.targetModelId === 'string'
|
|
306
|
+
? recovery.targetModelId
|
|
307
|
+
: undefined,
|
|
300
308
|
},
|
|
301
309
|
]
|
|
302
310
|
})
|
|
@@ -697,6 +705,8 @@ export function formatFallbackModelLabel(modelId: string | undefined): string {
|
|
|
697
705
|
return 'Fable 5'
|
|
698
706
|
if (modelId === 'claude-opus-5' || modelId?.startsWith('claude-opus-5-'))
|
|
699
707
|
return 'Opus 5'
|
|
708
|
+
if (modelId === 'claude-opus-4-8' || modelId?.startsWith('claude-opus-4-8-'))
|
|
709
|
+
return 'Opus 4.8'
|
|
700
710
|
return modelId ?? 'Fable 5'
|
|
701
711
|
}
|
|
702
712
|
|
|
@@ -711,6 +721,9 @@ export function getFableRecoverySummary(
|
|
|
711
721
|
if (recovery.mode === 'fable') {
|
|
712
722
|
return `${formatFallbackModelLabel(recovery.requestedModelId)} · restored`
|
|
713
723
|
}
|
|
724
|
+
if (recovery.mode === 'server') {
|
|
725
|
+
return `${formatFallbackModelLabel(recovery.requestedModelId)}→${formatFallbackModelLabel(recovery.targetModelId)} · safety`
|
|
726
|
+
}
|
|
714
727
|
return `Opus 4.8 · ${recovery.remaining} left`
|
|
715
728
|
}
|
|
716
729
|
|
|
@@ -39,7 +39,7 @@ export interface SidebarAccountState {
|
|
|
39
39
|
|
|
40
40
|
export interface FableRecoverySidebarState {
|
|
41
41
|
sessionId: string
|
|
42
|
-
mode: 'opus' | 'fable'
|
|
42
|
+
mode: 'opus' | 'fable' | 'server'
|
|
43
43
|
remaining: number
|
|
44
44
|
changedAt: number
|
|
45
45
|
/**
|
|
@@ -48,6 +48,8 @@ export interface FableRecoverySidebarState {
|
|
|
48
48
|
* state files; older recoveries default to Fable 5 in the summary.
|
|
49
49
|
*/
|
|
50
50
|
requestedModelId?: string
|
|
51
|
+
/** Anthropic-selected target while server-side safety fallback is active. */
|
|
52
|
+
targetModelId?: string
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
export interface SidebarState {
|
|
@@ -280,7 +282,9 @@ export function normalizeSidebarState(raw: unknown): SidebarState {
|
|
|
280
282
|
.flatMap((recovery): FableRecoverySidebarState[] => {
|
|
281
283
|
if (
|
|
282
284
|
typeof recovery.sessionId !== 'string' ||
|
|
283
|
-
(recovery.mode !== 'opus' &&
|
|
285
|
+
(recovery.mode !== 'opus' &&
|
|
286
|
+
recovery.mode !== 'fable' &&
|
|
287
|
+
recovery.mode !== 'server') ||
|
|
284
288
|
typeof recovery.remaining !== 'number' ||
|
|
285
289
|
!Number.isFinite(recovery.remaining) ||
|
|
286
290
|
typeof recovery.changedAt !== 'number' ||
|
|
@@ -297,6 +301,10 @@ export function normalizeSidebarState(raw: unknown): SidebarState {
|
|
|
297
301
|
typeof recovery.requestedModelId === 'string'
|
|
298
302
|
? recovery.requestedModelId
|
|
299
303
|
: undefined,
|
|
304
|
+
targetModelId:
|
|
305
|
+
typeof recovery.targetModelId === 'string'
|
|
306
|
+
? recovery.targetModelId
|
|
307
|
+
: undefined,
|
|
300
308
|
},
|
|
301
309
|
]
|
|
302
310
|
})
|
|
@@ -697,6 +705,8 @@ export function formatFallbackModelLabel(modelId: string | undefined): string {
|
|
|
697
705
|
return 'Fable 5'
|
|
698
706
|
if (modelId === 'claude-opus-5' || modelId?.startsWith('claude-opus-5-'))
|
|
699
707
|
return 'Opus 5'
|
|
708
|
+
if (modelId === 'claude-opus-4-8' || modelId?.startsWith('claude-opus-4-8-'))
|
|
709
|
+
return 'Opus 4.8'
|
|
700
710
|
return modelId ?? 'Fable 5'
|
|
701
711
|
}
|
|
702
712
|
|
|
@@ -711,6 +721,9 @@ export function getFableRecoverySummary(
|
|
|
711
721
|
if (recovery.mode === 'fable') {
|
|
712
722
|
return `${formatFallbackModelLabel(recovery.requestedModelId)} · restored`
|
|
713
723
|
}
|
|
724
|
+
if (recovery.mode === 'server') {
|
|
725
|
+
return `${formatFallbackModelLabel(recovery.requestedModelId)}→${formatFallbackModelLabel(recovery.targetModelId)} · safety`
|
|
726
|
+
}
|
|
714
727
|
return `Opus 4.8 · ${recovery.remaining} left`
|
|
715
728
|
}
|
|
716
729
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { watch } from 'node:fs'
|
|
1
|
+
import { readFileSync, watch } from 'node:fs'
|
|
2
2
|
import { mkdir, readFile, rename, writeFile } from 'node:fs/promises'
|
|
3
3
|
import { homedir } from 'node:os'
|
|
4
4
|
import { basename, dirname, join } from 'node:path'
|
|
@@ -292,12 +292,22 @@ const WATCH_POLL_MS = 100
|
|
|
292
292
|
// events, and against mtime granularity.
|
|
293
293
|
//
|
|
294
294
|
// Returns a disposer; never throws.
|
|
295
|
-
export function watchTuiPreferences(
|
|
295
|
+
export function watchTuiPreferences(
|
|
296
|
+
onChange: () => void,
|
|
297
|
+
options: { watchDirectory?: typeof watch } = {},
|
|
298
|
+
): () => void {
|
|
296
299
|
const file = getTuiPreferencesFile()
|
|
297
300
|
const name = basename(file)
|
|
298
301
|
let timer: ReturnType<typeof nativeSetTimeout> | null = null
|
|
302
|
+
// Capture the baseline before returning. An asynchronous seed can resolve
|
|
303
|
+
// after the caller's first write and absorb that change as already seen.
|
|
299
304
|
let lastSeen: string | null = null
|
|
300
|
-
|
|
305
|
+
try {
|
|
306
|
+
lastSeen = readFileSync(file, 'utf8')
|
|
307
|
+
} catch {
|
|
308
|
+
// Missing/unreadable files can still be created later and detected by the
|
|
309
|
+
// directory watcher or polling fallback.
|
|
310
|
+
}
|
|
301
311
|
let disposed = false
|
|
302
312
|
|
|
303
313
|
const checkForChange = async () => {
|
|
@@ -309,7 +319,6 @@ export function watchTuiPreferences(onChange: () => void): () => void {
|
|
|
309
319
|
}
|
|
310
320
|
|
|
311
321
|
const scheduleCheck = () => {
|
|
312
|
-
observedEvent = true
|
|
313
322
|
if (timer) nativeClearTimeout(timer)
|
|
314
323
|
timer = nativeSetTimeout(() => {
|
|
315
324
|
timer = null
|
|
@@ -317,54 +326,52 @@ export function watchTuiPreferences(onChange: () => void): () => void {
|
|
|
317
326
|
}, WATCH_DEBOUNCE_MS)
|
|
318
327
|
}
|
|
319
328
|
|
|
320
|
-
|
|
321
|
-
// do not let the seed overwrite that pending change; the debounced re-read
|
|
322
|
-
// will compare against the previous value and fire.
|
|
323
|
-
void readFile(file, 'utf8')
|
|
324
|
-
.then((text) => {
|
|
325
|
-
if (!observedEvent && lastSeen === null) lastSeen = text
|
|
326
|
-
})
|
|
327
|
-
.catch(() => {})
|
|
328
|
-
|
|
329
|
+
let watcher: ReturnType<typeof watch> | null = null
|
|
329
330
|
try {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
331
|
+
watcher = (options.watchDirectory ?? watch)(
|
|
332
|
+
dirname(file),
|
|
333
|
+
(_event, filename) => {
|
|
334
|
+
// Exact match against the preferences file name, plus the temp file
|
|
335
|
+
// we use for atomic writes (carrying our pid + `.tmp`).
|
|
336
|
+
const isOurs =
|
|
337
|
+
filename === name ||
|
|
338
|
+
(filename?.startsWith(`${name}.`) && filename.endsWith('.tmp'))
|
|
339
|
+
if (filename != null && !isOurs) return
|
|
340
|
+
scheduleCheck()
|
|
341
|
+
},
|
|
342
|
+
)
|
|
343
|
+
} catch {
|
|
344
|
+
// If the file exists, polling remains a complete fallback even when Bun
|
|
345
|
+
// cannot allocate a directory watcher (for example, transient EBADF).
|
|
346
|
+
// If neither the file nor its directory can be observed, remain a no-op.
|
|
347
|
+
if (lastSeen === null) return () => {}
|
|
348
|
+
}
|
|
339
349
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
350
|
+
// Bun's fs.watch can miss atomic temp-file renames and can itself fail after
|
|
351
|
+
// repeated close/recreate cycles, so polling must be independent of watcher
|
|
352
|
+
// construction. Recursive unreferenced timers avoid fs.watchFile's lingering
|
|
353
|
+
// handles while the host's normal event loop keeps active watchers polling.
|
|
354
|
+
let pollTimer: ReturnType<typeof nativeSetTimeout> | null = null
|
|
355
|
+
let pollInFlight = false
|
|
356
|
+
const schedulePoll = () => {
|
|
357
|
+
if (disposed || pollTimer) return
|
|
358
|
+
pollTimer = nativeSetTimeout(() => {
|
|
359
|
+
pollTimer = null
|
|
360
|
+
if (disposed || pollInFlight) return
|
|
361
|
+
pollInFlight = true
|
|
362
|
+
void checkForChange().finally(() => {
|
|
363
|
+
pollInFlight = false
|
|
364
|
+
schedulePoll()
|
|
365
|
+
})
|
|
366
|
+
}, WATCH_POLL_MS)
|
|
367
|
+
pollTimer.unref?.()
|
|
368
|
+
}
|
|
369
|
+
schedulePoll()
|
|
360
370
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
}
|
|
367
|
-
} catch {
|
|
368
|
-
return () => {}
|
|
371
|
+
return () => {
|
|
372
|
+
disposed = true
|
|
373
|
+
if (timer) nativeClearTimeout(timer)
|
|
374
|
+
if (pollTimer) nativeClearTimeout(pollTimer)
|
|
375
|
+
watcher?.close()
|
|
369
376
|
}
|
|
370
377
|
}
|
package/src/tui-compiled/tui.tsx
CHANGED
|
@@ -682,7 +682,7 @@ function QuotaSidebar(props) {
|
|
|
682
682
|
const needsReauth = () => enabledFallbacks().some(f => f.needsReauth);
|
|
683
683
|
const degraded = () => quotaBackedOff() || refreshBackedOff() || needsReauth();
|
|
684
684
|
const fableRecoverySummary = () => getFableRecoverySummary(state(), props.sessionId);
|
|
685
|
-
const fableRecoveryTone = () => state().fableRecoveries?.find(recovery => recovery.sessionId === props.sessionId)?.mode === '
|
|
685
|
+
const fableRecoveryTone = () => state().fableRecoveries?.find(recovery => recovery.sessionId === props.sessionId)?.mode === 'fable' ? 'ok' : 'warn';
|
|
686
686
|
const cacheKeep = () => state().cacheKeep;
|
|
687
687
|
const showCache = () => prefs().sections.cache && cacheKeep() != null && cacheKeep()?.window != null;
|
|
688
688
|
const relayValue = () => {
|
package/src/tui-preferences.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { watch } from 'node:fs'
|
|
1
|
+
import { readFileSync, watch } from 'node:fs'
|
|
2
2
|
import { mkdir, readFile, rename, writeFile } from 'node:fs/promises'
|
|
3
3
|
import { homedir } from 'node:os'
|
|
4
4
|
import { basename, dirname, join } from 'node:path'
|
|
@@ -292,12 +292,22 @@ const WATCH_POLL_MS = 100
|
|
|
292
292
|
// events, and against mtime granularity.
|
|
293
293
|
//
|
|
294
294
|
// Returns a disposer; never throws.
|
|
295
|
-
export function watchTuiPreferences(
|
|
295
|
+
export function watchTuiPreferences(
|
|
296
|
+
onChange: () => void,
|
|
297
|
+
options: { watchDirectory?: typeof watch } = {},
|
|
298
|
+
): () => void {
|
|
296
299
|
const file = getTuiPreferencesFile()
|
|
297
300
|
const name = basename(file)
|
|
298
301
|
let timer: ReturnType<typeof nativeSetTimeout> | null = null
|
|
302
|
+
// Capture the baseline before returning. An asynchronous seed can resolve
|
|
303
|
+
// after the caller's first write and absorb that change as already seen.
|
|
299
304
|
let lastSeen: string | null = null
|
|
300
|
-
|
|
305
|
+
try {
|
|
306
|
+
lastSeen = readFileSync(file, 'utf8')
|
|
307
|
+
} catch {
|
|
308
|
+
// Missing/unreadable files can still be created later and detected by the
|
|
309
|
+
// directory watcher or polling fallback.
|
|
310
|
+
}
|
|
301
311
|
let disposed = false
|
|
302
312
|
|
|
303
313
|
const checkForChange = async () => {
|
|
@@ -309,7 +319,6 @@ export function watchTuiPreferences(onChange: () => void): () => void {
|
|
|
309
319
|
}
|
|
310
320
|
|
|
311
321
|
const scheduleCheck = () => {
|
|
312
|
-
observedEvent = true
|
|
313
322
|
if (timer) nativeClearTimeout(timer)
|
|
314
323
|
timer = nativeSetTimeout(() => {
|
|
315
324
|
timer = null
|
|
@@ -317,54 +326,52 @@ export function watchTuiPreferences(onChange: () => void): () => void {
|
|
|
317
326
|
}, WATCH_DEBOUNCE_MS)
|
|
318
327
|
}
|
|
319
328
|
|
|
320
|
-
|
|
321
|
-
// do not let the seed overwrite that pending change; the debounced re-read
|
|
322
|
-
// will compare against the previous value and fire.
|
|
323
|
-
void readFile(file, 'utf8')
|
|
324
|
-
.then((text) => {
|
|
325
|
-
if (!observedEvent && lastSeen === null) lastSeen = text
|
|
326
|
-
})
|
|
327
|
-
.catch(() => {})
|
|
328
|
-
|
|
329
|
+
let watcher: ReturnType<typeof watch> | null = null
|
|
329
330
|
try {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
331
|
+
watcher = (options.watchDirectory ?? watch)(
|
|
332
|
+
dirname(file),
|
|
333
|
+
(_event, filename) => {
|
|
334
|
+
// Exact match against the preferences file name, plus the temp file
|
|
335
|
+
// we use for atomic writes (carrying our pid + `.tmp`).
|
|
336
|
+
const isOurs =
|
|
337
|
+
filename === name ||
|
|
338
|
+
(filename?.startsWith(`${name}.`) && filename.endsWith('.tmp'))
|
|
339
|
+
if (filename != null && !isOurs) return
|
|
340
|
+
scheduleCheck()
|
|
341
|
+
},
|
|
342
|
+
)
|
|
343
|
+
} catch {
|
|
344
|
+
// If the file exists, polling remains a complete fallback even when Bun
|
|
345
|
+
// cannot allocate a directory watcher (for example, transient EBADF).
|
|
346
|
+
// If neither the file nor its directory can be observed, remain a no-op.
|
|
347
|
+
if (lastSeen === null) return () => {}
|
|
348
|
+
}
|
|
339
349
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
350
|
+
// Bun's fs.watch can miss atomic temp-file renames and can itself fail after
|
|
351
|
+
// repeated close/recreate cycles, so polling must be independent of watcher
|
|
352
|
+
// construction. Recursive unreferenced timers avoid fs.watchFile's lingering
|
|
353
|
+
// handles while the host's normal event loop keeps active watchers polling.
|
|
354
|
+
let pollTimer: ReturnType<typeof nativeSetTimeout> | null = null
|
|
355
|
+
let pollInFlight = false
|
|
356
|
+
const schedulePoll = () => {
|
|
357
|
+
if (disposed || pollTimer) return
|
|
358
|
+
pollTimer = nativeSetTimeout(() => {
|
|
359
|
+
pollTimer = null
|
|
360
|
+
if (disposed || pollInFlight) return
|
|
361
|
+
pollInFlight = true
|
|
362
|
+
void checkForChange().finally(() => {
|
|
363
|
+
pollInFlight = false
|
|
364
|
+
schedulePoll()
|
|
365
|
+
})
|
|
366
|
+
}, WATCH_POLL_MS)
|
|
367
|
+
pollTimer.unref?.()
|
|
368
|
+
}
|
|
369
|
+
schedulePoll()
|
|
360
370
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
}
|
|
367
|
-
} catch {
|
|
368
|
-
return () => {}
|
|
371
|
+
return () => {
|
|
372
|
+
disposed = true
|
|
373
|
+
if (timer) nativeClearTimeout(timer)
|
|
374
|
+
if (pollTimer) nativeClearTimeout(pollTimer)
|
|
375
|
+
watcher?.close()
|
|
369
376
|
}
|
|
370
377
|
}
|
package/src/tui.tsx
CHANGED
|
@@ -644,9 +644,9 @@ function QuotaSidebar(props: {
|
|
|
644
644
|
const fableRecoveryTone = (): Tone =>
|
|
645
645
|
state().fableRecoveries?.find(
|
|
646
646
|
(recovery) => recovery.sessionId === props.sessionId,
|
|
647
|
-
)?.mode === '
|
|
648
|
-
? '
|
|
649
|
-
: '
|
|
647
|
+
)?.mode === 'fable'
|
|
648
|
+
? 'ok'
|
|
649
|
+
: 'warn'
|
|
650
650
|
const cacheKeep = () => state().cacheKeep
|
|
651
651
|
const showCache = () =>
|
|
652
652
|
prefs().sections.cache && cacheKeep() != null && cacheKeep()?.window != null
|