@cortexkit/opencode-anthropic-auth 2.2.7 → 2.3.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/dist/cli.js +43 -42
- package/dist/index-2jsr8wvj.js +43 -0
- package/dist/index-46e2gk3c.js +2 -0
- package/dist/{index-62x1p34p.js → index-71temqvq.js} +1 -1
- package/dist/{index-ppdrzs41.js → index-bcp23wwb.js} +1 -1
- package/dist/{index-hazrk5c3.js → index-c8ehrd0p.js} +1 -1
- package/dist/index-dpnhamzw.js +2 -0
- package/dist/{index-0kxy8k2c.js → index-m4dazzyf.js} +1 -1
- package/dist/{index-b1dvkp15.js → index-naff9h33.js} +1 -1
- package/dist/{index-6chd6jnr.js → index-sg0knfec.js} +1 -1
- package/dist/index.js +1 -1
- package/dist/oauth-destination.d.ts +35 -0
- package/dist/plugin-test-handles.d.ts +26 -0
- package/dist/rpc/port-file.js +1 -1
- package/dist/rpc/protocol.js +1 -1
- package/dist/rpc/rpc-client.js +1 -1
- package/dist/rpc/rpc-dir.js +1 -1
- package/dist/sidebar-state.d.ts +6 -3
- package/dist/sidebar-state.js +1 -1
- package/dist/tui/claude-sidebar.d.ts +58 -0
- package/dist/tui/menu-status.d.ts +70 -0
- package/dist/tui-preferences.js +2 -2
- package/dist/v2/adapter.d.ts +13 -8
- package/dist/v2/server.js +10 -10
- package/dist/v2/tui.d.ts +12 -0
- package/dist/v2/tui.js +4 -5
- package/package.json +2 -2
- package/src/sidebar-state.ts +55 -46
- package/src/tui/claude-sidebar.tsx +155 -15
- package/src/tui/entry.mjs +13 -0
- package/src/tui/menu-status.tsx +300 -0
- package/src/tui-compiled/sidebar-state.ts +55 -46
- package/src/tui-compiled/tui/claude-sidebar.tsx +112 -12
- package/src/tui-compiled/tui/menu-status.tsx +388 -0
- package/src/tui-compiled/tui-preferences.ts +32 -3
- package/src/tui-preferences.ts +32 -3
- package/dist/index-20d6rmjj.js +0 -2
- package/dist/index-cjjjgewq.js +0 -43
- package/dist/index-jvhs8sxv.js +0 -2
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import { memo as _$memo } from "opentui:runtime-module:%40opentui%2Fsolid";
|
|
2
|
+
import { createComponent as _$createComponent } from "opentui:runtime-module:%40opentui%2Fsolid";
|
|
3
|
+
import { createTextNode as _$createTextNode } from "opentui:runtime-module:%40opentui%2Fsolid";
|
|
4
|
+
import { insertNode as _$insertNode } from "opentui:runtime-module:%40opentui%2Fsolid";
|
|
5
|
+
import { setProp as _$setProp } from "opentui:runtime-module:%40opentui%2Fsolid";
|
|
6
|
+
import { effect as _$effect } from "opentui:runtime-module:%40opentui%2Fsolid";
|
|
7
|
+
import { insert as _$insert } from "opentui:runtime-module:%40opentui%2Fsolid";
|
|
8
|
+
import { createElement as _$createElement } from "opentui:runtime-module:%40opentui%2Fsolid";
|
|
9
|
+
/** @jsxImportSource @opentui/solid */
|
|
10
|
+
|
|
11
|
+
// The status view of the OpenCode 2 `/claude` menu. Core writes a section's
|
|
12
|
+
// status as plain lines, some of them Markdown: the quota summary has `##`
|
|
13
|
+
// headings and indented `-` bullets. This view draws headings and bullets as
|
|
14
|
+
// styled text instead of showing their markers. For the Quota section it draws
|
|
15
|
+
// the account blocks of OpenCode 1's quota view, read from the same server
|
|
16
|
+
// state as the sidebar, and falls back to the text when that read fails.
|
|
17
|
+
|
|
18
|
+
import { TextAttributes } from "opentui:runtime-module:%40opentui%2Fcore";
|
|
19
|
+
import { createSignal, For, Show } from "opentui:runtime-module:solid-js";
|
|
20
|
+
import { readTuiPreferencesFile, resolveAnthropicAuthPrefs } from "../tui-preferences.js";
|
|
21
|
+
import { AccountBlock, sidebarThemeFromHost } from "./claude-sidebar.js";
|
|
22
|
+
|
|
23
|
+
/** One status line, classified for drawing. */
|
|
24
|
+
|
|
25
|
+
// The widest indent kept for a bullet; deeper nesting is drawn at this indent.
|
|
26
|
+
const MAX_BULLET_INDENT = 8;
|
|
27
|
+
|
|
28
|
+
// The width of OpenCode 1's quota view (`QuotaDialogContent` in `tui.tsx`).
|
|
29
|
+
const QUOTA_VIEW_WIDTH = 58;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Splits a section's status lines (each may hold several lines) and classifies
|
|
33
|
+
* each one. Only line-leading Markdown is read: `#` headings and `-`, `*` or
|
|
34
|
+
* `+` bullets. Everything else, including any `*` inside a line, stays as
|
|
35
|
+
* written. Runs of blank lines collapse to one, and leading or trailing blank
|
|
36
|
+
* lines are dropped.
|
|
37
|
+
*/
|
|
38
|
+
export function parseStatusLines(lines) {
|
|
39
|
+
const parsed = [];
|
|
40
|
+
for (const raw of lines.join('\n').split(/\r?\n/)) {
|
|
41
|
+
const line = raw.trimEnd();
|
|
42
|
+
if (line.trim() === '') {
|
|
43
|
+
if (parsed.length && parsed.at(-1)?.kind !== 'blank') parsed.push({
|
|
44
|
+
kind: 'blank'
|
|
45
|
+
});
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const heading = /^\s{0,3}(#{1,6})\s+(.+?)(?:\s+#+)?$/.exec(line);
|
|
49
|
+
if (heading?.[1] && heading[2]) {
|
|
50
|
+
parsed.push({
|
|
51
|
+
kind: 'heading',
|
|
52
|
+
level: heading[1].length,
|
|
53
|
+
text: heading[2]
|
|
54
|
+
});
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const bullet = /^(\s*)[-*+]\s+(.+)$/.exec(line);
|
|
58
|
+
if (bullet?.[2]) {
|
|
59
|
+
parsed.push({
|
|
60
|
+
kind: 'bullet',
|
|
61
|
+
indent: Math.min(bullet[1]?.length ?? 0, MAX_BULLET_INDENT),
|
|
62
|
+
text: bullet[2]
|
|
63
|
+
});
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
parsed.push({
|
|
67
|
+
kind: 'text',
|
|
68
|
+
text: line
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
if (parsed.at(-1)?.kind === 'blank') parsed.pop();
|
|
72
|
+
return parsed;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Draws one status line; headings and bullets lose their Markdown markers. */
|
|
76
|
+
function StatusRow(props) {
|
|
77
|
+
const line = props.line;
|
|
78
|
+
switch (line.kind) {
|
|
79
|
+
case 'heading':
|
|
80
|
+
return (() => {
|
|
81
|
+
var _el$ = _$createElement("text");
|
|
82
|
+
_$insert(_el$, () => line.text);
|
|
83
|
+
_$effect(_p$ => {
|
|
84
|
+
var _v$ = line.level <= 2 ? props.theme.accent : props.theme.text,
|
|
85
|
+
_v$2 = TextAttributes.BOLD;
|
|
86
|
+
_v$ !== _p$.e && (_p$.e = _$setProp(_el$, "fg", _v$, _p$.e));
|
|
87
|
+
_v$2 !== _p$.t && (_p$.t = _$setProp(_el$, "attributes", _v$2, _p$.t));
|
|
88
|
+
return _p$;
|
|
89
|
+
}, {
|
|
90
|
+
e: undefined,
|
|
91
|
+
t: undefined
|
|
92
|
+
});
|
|
93
|
+
return _el$;
|
|
94
|
+
})();
|
|
95
|
+
case 'bullet':
|
|
96
|
+
return (// The text sits in its own column, so a long bullet wraps under its
|
|
97
|
+
// first word instead of under the bullet mark.
|
|
98
|
+
(() => {
|
|
99
|
+
var _el$2 = _$createElement("box"),
|
|
100
|
+
_el$3 = _$createElement("text"),
|
|
101
|
+
_el$5 = _$createElement("text");
|
|
102
|
+
_$insertNode(_el$2, _el$3);
|
|
103
|
+
_$insertNode(_el$2, _el$5);
|
|
104
|
+
_$setProp(_el$2, "flexDirection", 'row');
|
|
105
|
+
_$insertNode(_el$3, _$createTextNode(`• `));
|
|
106
|
+
_$setProp(_el$3, "flexShrink", 0);
|
|
107
|
+
_$setProp(_el$5, "flexGrow", 1);
|
|
108
|
+
_$setProp(_el$5, "flexShrink", 1);
|
|
109
|
+
_$insert(_el$5, () => line.text);
|
|
110
|
+
_$effect(_p$ => {
|
|
111
|
+
var _v$3 = line.indent,
|
|
112
|
+
_v$4 = props.theme.textMuted,
|
|
113
|
+
_v$5 = props.theme.text;
|
|
114
|
+
_v$3 !== _p$.e && (_p$.e = _$setProp(_el$2, "paddingLeft", _v$3, _p$.e));
|
|
115
|
+
_v$4 !== _p$.t && (_p$.t = _$setProp(_el$3, "fg", _v$4, _p$.t));
|
|
116
|
+
_v$5 !== _p$.a && (_p$.a = _$setProp(_el$5, "fg", _v$5, _p$.a));
|
|
117
|
+
return _p$;
|
|
118
|
+
}, {
|
|
119
|
+
e: undefined,
|
|
120
|
+
t: undefined,
|
|
121
|
+
a: undefined
|
|
122
|
+
});
|
|
123
|
+
return _el$2;
|
|
124
|
+
})()
|
|
125
|
+
);
|
|
126
|
+
case 'text':
|
|
127
|
+
return (() => {
|
|
128
|
+
var _el$6 = _$createElement("text");
|
|
129
|
+
_$insert(_el$6, () => line.text);
|
|
130
|
+
_$effect(_$p => _$setProp(_el$6, "fg", props.theme.text, _$p));
|
|
131
|
+
return _el$6;
|
|
132
|
+
})();
|
|
133
|
+
default:
|
|
134
|
+
return (() => {
|
|
135
|
+
var _el$7 = _$createElement("box");
|
|
136
|
+
_$setProp(_el$7, "height", 1);
|
|
137
|
+
return _el$7;
|
|
138
|
+
})();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Draws status lines with headings and bullets styled, not as Markdown. */
|
|
143
|
+
export function StatusText(props) {
|
|
144
|
+
return (() => {
|
|
145
|
+
var _el$8 = _$createElement("box");
|
|
146
|
+
_$setProp(_el$8, "flexDirection", 'column');
|
|
147
|
+
_$setProp(_el$8, "width", '100%');
|
|
148
|
+
_$insert(_el$8, _$createComponent(For, {
|
|
149
|
+
get each() {
|
|
150
|
+
return parseStatusLines(props.lines);
|
|
151
|
+
},
|
|
152
|
+
children: line => _$createComponent(StatusRow, {
|
|
153
|
+
line: line,
|
|
154
|
+
get theme() {
|
|
155
|
+
return props.theme;
|
|
156
|
+
}
|
|
157
|
+
})
|
|
158
|
+
}));
|
|
159
|
+
return _el$8;
|
|
160
|
+
})();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* OpenCode 1's quota view: one account block for main and for each enabled or
|
|
165
|
+
* vault-held fallback, as the sidebar draws them.
|
|
166
|
+
*/
|
|
167
|
+
export function QuotaStatus(props) {
|
|
168
|
+
const fallbacks = () => (props.state.fallbacks ?? []).filter(fallback => fallback.enabled || fallback.custodyState && fallback.custodyState !== 'off');
|
|
169
|
+
return (// As wide as OpenCode 1's quota view: wider, the reset times drift far
|
|
170
|
+
// from the bars they belong to.
|
|
171
|
+
(() => {
|
|
172
|
+
var _el$9 = _$createElement("box");
|
|
173
|
+
_$setProp(_el$9, "flexDirection", 'column');
|
|
174
|
+
_$setProp(_el$9, "width", '100%');
|
|
175
|
+
_$setProp(_el$9, "maxWidth", 58);
|
|
176
|
+
_$insert(_el$9, _$createComponent(AccountBlock, {
|
|
177
|
+
get theme() {
|
|
178
|
+
return props.theme;
|
|
179
|
+
},
|
|
180
|
+
get appearance() {
|
|
181
|
+
return props.prefs.appearance;
|
|
182
|
+
},
|
|
183
|
+
name: "main",
|
|
184
|
+
get quota() {
|
|
185
|
+
return props.state.main?.quota;
|
|
186
|
+
},
|
|
187
|
+
get active() {
|
|
188
|
+
return props.state.activeId === 'main';
|
|
189
|
+
},
|
|
190
|
+
get pacingEnabled() {
|
|
191
|
+
return props.prefs.sections.pacing;
|
|
192
|
+
},
|
|
193
|
+
get tierLabel() {
|
|
194
|
+
return props.state.main?.tierLabel;
|
|
195
|
+
},
|
|
196
|
+
get stateLine() {
|
|
197
|
+
return props.state.main?.stateLine;
|
|
198
|
+
},
|
|
199
|
+
get quotaDated() {
|
|
200
|
+
return props.state.main?.quotaDated;
|
|
201
|
+
},
|
|
202
|
+
get now() {
|
|
203
|
+
return props.now;
|
|
204
|
+
}
|
|
205
|
+
}), null);
|
|
206
|
+
_$insert(_el$9, _$createComponent(Show, {
|
|
207
|
+
get when() {
|
|
208
|
+
return props.prefs.sections.fallbackAccounts;
|
|
209
|
+
},
|
|
210
|
+
get children() {
|
|
211
|
+
return _$createComponent(For, {
|
|
212
|
+
get each() {
|
|
213
|
+
return fallbacks();
|
|
214
|
+
},
|
|
215
|
+
children: fallback => _$createComponent(AccountBlock, {
|
|
216
|
+
get theme() {
|
|
217
|
+
return props.theme;
|
|
218
|
+
},
|
|
219
|
+
get appearance() {
|
|
220
|
+
return props.prefs.appearance;
|
|
221
|
+
},
|
|
222
|
+
get name() {
|
|
223
|
+
return fallback.label ?? fallback.id;
|
|
224
|
+
},
|
|
225
|
+
get quota() {
|
|
226
|
+
return fallback.quota;
|
|
227
|
+
},
|
|
228
|
+
get active() {
|
|
229
|
+
return props.state.activeId === fallback.id;
|
|
230
|
+
},
|
|
231
|
+
get pacingEnabled() {
|
|
232
|
+
return props.prefs.sections.pacing;
|
|
233
|
+
},
|
|
234
|
+
get tierLabel() {
|
|
235
|
+
return fallback.tierLabel;
|
|
236
|
+
},
|
|
237
|
+
get needsReauth() {
|
|
238
|
+
return fallback.needsReauth;
|
|
239
|
+
},
|
|
240
|
+
get vaultReauth() {
|
|
241
|
+
return fallback.vaultReauth;
|
|
242
|
+
},
|
|
243
|
+
get stateLine() {
|
|
244
|
+
return fallback.stateLine;
|
|
245
|
+
},
|
|
246
|
+
get quotaDated() {
|
|
247
|
+
return fallback.quotaDated;
|
|
248
|
+
},
|
|
249
|
+
marginTop: 1,
|
|
250
|
+
get now() {
|
|
251
|
+
return props.now;
|
|
252
|
+
}
|
|
253
|
+
})
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}), null);
|
|
257
|
+
return _el$9;
|
|
258
|
+
})()
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// --- OpenCode 2 -------------------------------------------------------------
|
|
263
|
+
|
|
264
|
+
/** One section's status, as the `/claude` menu hands it over. */
|
|
265
|
+
|
|
266
|
+
/** The OpenCode 2 TUI context members the status dialog uses. */
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Shows a section's status in a wide OpenCode 2 dialog. The returned promise
|
|
270
|
+
* settles when the person closes the dialog, or when `signal` aborts.
|
|
271
|
+
*/
|
|
272
|
+
export function showMenuStatusDialog(host, view, signal) {
|
|
273
|
+
return new Promise(resolve => {
|
|
274
|
+
if (signal?.aborted) return resolve();
|
|
275
|
+
// Stops the quota read once the dialog closes or the menu goes away.
|
|
276
|
+
const reading = new AbortController();
|
|
277
|
+
const close = () => {
|
|
278
|
+
reading.abort();
|
|
279
|
+
resolve();
|
|
280
|
+
};
|
|
281
|
+
signal?.addEventListener('abort', close, {
|
|
282
|
+
once: true
|
|
283
|
+
});
|
|
284
|
+
const [quota, setQuota] = createSignal({
|
|
285
|
+
kind: 'loading'
|
|
286
|
+
});
|
|
287
|
+
const readQuota = view.readQuota;
|
|
288
|
+
if (readQuota) void Promise.all([readQuota(reading.signal), readTuiPreferencesFile().then(resolveAnthropicAuthPrefs)]).then(([state, prefs]) => {
|
|
289
|
+
if (!reading.signal.aborted) setQuota({
|
|
290
|
+
kind: 'ready',
|
|
291
|
+
state,
|
|
292
|
+
prefs
|
|
293
|
+
});
|
|
294
|
+
}, () => {
|
|
295
|
+
// An older server without the sidebar method, or a failed read:
|
|
296
|
+
// the status text still says everything the blocks would.
|
|
297
|
+
if (!reading.signal.aborted) setQuota({
|
|
298
|
+
kind: 'failed'
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
const ready = () => {
|
|
302
|
+
const current = quota();
|
|
303
|
+
return current.kind === 'ready' ? current : undefined;
|
|
304
|
+
};
|
|
305
|
+
const theme = () => sidebarThemeFromHost(host.theme);
|
|
306
|
+
host.ui.dialog.show(() => (() => {
|
|
307
|
+
var _el$0 = _$createElement("box"),
|
|
308
|
+
_el$1 = _$createElement("box"),
|
|
309
|
+
_el$10 = _$createElement("text"),
|
|
310
|
+
_el$11 = _$createElement("box"),
|
|
311
|
+
_el$12 = _$createElement("text");
|
|
312
|
+
_$insertNode(_el$0, _el$1);
|
|
313
|
+
_$insertNode(_el$0, _el$11);
|
|
314
|
+
_$setProp(_el$0, "flexDirection", 'column');
|
|
315
|
+
_$setProp(_el$0, "paddingLeft", 2);
|
|
316
|
+
_$setProp(_el$0, "paddingRight", 2);
|
|
317
|
+
_$insertNode(_el$1, _el$10);
|
|
318
|
+
_$setProp(_el$1, "paddingBottom", 1);
|
|
319
|
+
_$insert(_el$10, () => view.title);
|
|
320
|
+
_$insert(_el$0, _$createComponent(Show, {
|
|
321
|
+
get when() {
|
|
322
|
+
return readQuota && quota().kind !== 'failed';
|
|
323
|
+
},
|
|
324
|
+
get fallback() {
|
|
325
|
+
return _$createComponent(StatusText, {
|
|
326
|
+
get lines() {
|
|
327
|
+
return view.lines;
|
|
328
|
+
},
|
|
329
|
+
get theme() {
|
|
330
|
+
return theme();
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
},
|
|
334
|
+
get children() {
|
|
335
|
+
return _$createComponent(Show, {
|
|
336
|
+
get when() {
|
|
337
|
+
return ready();
|
|
338
|
+
},
|
|
339
|
+
get fallback() {
|
|
340
|
+
return (() => {
|
|
341
|
+
var _el$14 = _$createElement("text");
|
|
342
|
+
_$insertNode(_el$14, _$createTextNode(`Reading quota…`));
|
|
343
|
+
_$effect(_$p => _$setProp(_el$14, "fg", theme().textMuted, _$p));
|
|
344
|
+
return _el$14;
|
|
345
|
+
})();
|
|
346
|
+
},
|
|
347
|
+
children: ready => _$createComponent(QuotaStatus, {
|
|
348
|
+
get state() {
|
|
349
|
+
return ready().state;
|
|
350
|
+
},
|
|
351
|
+
get prefs() {
|
|
352
|
+
return ready().prefs;
|
|
353
|
+
},
|
|
354
|
+
get theme() {
|
|
355
|
+
return theme();
|
|
356
|
+
},
|
|
357
|
+
get now() {
|
|
358
|
+
return Date.now();
|
|
359
|
+
}
|
|
360
|
+
})
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
}), _el$11);
|
|
364
|
+
_$insertNode(_el$11, _el$12);
|
|
365
|
+
_$setProp(_el$11, "paddingTop", 1);
|
|
366
|
+
_$insertNode(_el$12, _$createTextNode(`Press Esc to close.`));
|
|
367
|
+
_$effect(_p$ => {
|
|
368
|
+
var _v$6 = theme().text,
|
|
369
|
+
_v$7 = TextAttributes.BOLD,
|
|
370
|
+
_v$8 = theme().textMuted;
|
|
371
|
+
_v$6 !== _p$.e && (_p$.e = _$setProp(_el$10, "fg", _v$6, _p$.e));
|
|
372
|
+
_v$7 !== _p$.t && (_p$.t = _$setProp(_el$10, "attributes", _v$7, _p$.t));
|
|
373
|
+
_v$8 !== _p$.a && (_p$.a = _$setProp(_el$12, "fg", _v$8, _p$.a));
|
|
374
|
+
return _p$;
|
|
375
|
+
}, {
|
|
376
|
+
e: undefined,
|
|
377
|
+
t: undefined,
|
|
378
|
+
a: undefined
|
|
379
|
+
});
|
|
380
|
+
return _el$0;
|
|
381
|
+
})(), close);
|
|
382
|
+
// The widest dialog gives the account blocks their full width and wraps
|
|
383
|
+
// status text as little as it can.
|
|
384
|
+
host.ui.dialog.set({
|
|
385
|
+
size: 'xlarge'
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFileSync, watch } from 'node:fs'
|
|
1
|
+
import { readFileSync, statSync, 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'
|
|
@@ -274,6 +274,20 @@ export function queueTuiPreferenceUpdate(
|
|
|
274
274
|
const WATCH_DEBOUNCE_MS = 150
|
|
275
275
|
const WATCH_POLL_MS = 100
|
|
276
276
|
|
|
277
|
+
// Identity of the preferences file's current version: its modification time,
|
|
278
|
+
// size and inode. A replacement via rename (how editors and our own writes
|
|
279
|
+
// save it) always changes the inode, so one `stat` per poll is enough to know
|
|
280
|
+
// that nothing was rewritten. The nanosecond timestamps, rather than Date's
|
|
281
|
+
// millisecond rounding, keep two saves within one millisecond distinguishable.
|
|
282
|
+
function statSignature(file: string): string | null {
|
|
283
|
+
try {
|
|
284
|
+
const info = statSync(file, { bigint: true })
|
|
285
|
+
return `${info.ino}:${info.size}:${info.mtimeNs}:${info.ctimeNs}`
|
|
286
|
+
} catch {
|
|
287
|
+
return null
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
277
291
|
// Watches the directory rather than the file: editors and our own atomic
|
|
278
292
|
// writes replace the file via rename, which kills file-level watchers.
|
|
279
293
|
//
|
|
@@ -308,11 +322,23 @@ export function watchTuiPreferences(
|
|
|
308
322
|
// Missing/unreadable files can still be created later and detected by the
|
|
309
323
|
// directory watcher or polling fallback.
|
|
310
324
|
}
|
|
325
|
+
// The stat of the file that `lastSeen` was read from. A file whose mtime,
|
|
326
|
+
// size and inode all match it cannot have changed, so a poll skips the read.
|
|
327
|
+
let lastStat = statSignature(file)
|
|
311
328
|
let disposed = false
|
|
312
329
|
|
|
313
|
-
const checkForChange = async () => {
|
|
330
|
+
const checkForChange = async (readAnyway = false) => {
|
|
331
|
+
const current = await statSignature(file)
|
|
332
|
+
// An absent file cannot have new content; re-reading it would only fail.
|
|
333
|
+
if (current === null) return
|
|
334
|
+
const unchanged = current === lastStat && lastSeen !== null
|
|
335
|
+
// The watcher path always reads, even when the stat looks familiar, so a
|
|
336
|
+
// hand edit inside one timestamp tick is still noticed there. Only the
|
|
337
|
+
// poll relies on the stat to skip a read.
|
|
338
|
+
if (unchanged && !readAnyway) return
|
|
314
339
|
const text = await readFile(file, 'utf8').catch(() => null)
|
|
315
340
|
if (disposed || text === null) return
|
|
341
|
+
lastStat = current
|
|
316
342
|
if (text === lastSeen) return
|
|
317
343
|
lastSeen = text
|
|
318
344
|
onChange()
|
|
@@ -322,7 +348,7 @@ export function watchTuiPreferences(
|
|
|
322
348
|
if (timer) nativeClearTimeout(timer)
|
|
323
349
|
timer = nativeSetTimeout(() => {
|
|
324
350
|
timer = null
|
|
325
|
-
void checkForChange()
|
|
351
|
+
void checkForChange(true)
|
|
326
352
|
}, WATCH_DEBOUNCE_MS)
|
|
327
353
|
}
|
|
328
354
|
|
|
@@ -351,6 +377,9 @@ export function watchTuiPreferences(
|
|
|
351
377
|
// repeated close/recreate cycles, so polling must be independent of watcher
|
|
352
378
|
// construction. Recursive unreferenced timers avoid fs.watchFile's lingering
|
|
353
379
|
// handles while the host's normal event loop keeps active watchers polling.
|
|
380
|
+
// A poll only reads the file when its stat changed; one controller per
|
|
381
|
+
// terminal polled at 10 Hz, so an unconditional read ran ten times a second
|
|
382
|
+
// per terminal.
|
|
354
383
|
let pollTimer: ReturnType<typeof nativeSetTimeout> | null = null
|
|
355
384
|
let pollInFlight = false
|
|
356
385
|
const schedulePoll = () => {
|
package/src/tui-preferences.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFileSync, watch } from 'node:fs'
|
|
1
|
+
import { readFileSync, statSync, 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'
|
|
@@ -274,6 +274,20 @@ export function queueTuiPreferenceUpdate(
|
|
|
274
274
|
const WATCH_DEBOUNCE_MS = 150
|
|
275
275
|
const WATCH_POLL_MS = 100
|
|
276
276
|
|
|
277
|
+
// Identity of the preferences file's current version: its modification time,
|
|
278
|
+
// size and inode. A replacement via rename (how editors and our own writes
|
|
279
|
+
// save it) always changes the inode, so one `stat` per poll is enough to know
|
|
280
|
+
// that nothing was rewritten. The nanosecond timestamps, rather than Date's
|
|
281
|
+
// millisecond rounding, keep two saves within one millisecond distinguishable.
|
|
282
|
+
function statSignature(file: string): string | null {
|
|
283
|
+
try {
|
|
284
|
+
const info = statSync(file, { bigint: true })
|
|
285
|
+
return `${info.ino}:${info.size}:${info.mtimeNs}:${info.ctimeNs}`
|
|
286
|
+
} catch {
|
|
287
|
+
return null
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
277
291
|
// Watches the directory rather than the file: editors and our own atomic
|
|
278
292
|
// writes replace the file via rename, which kills file-level watchers.
|
|
279
293
|
//
|
|
@@ -308,11 +322,23 @@ export function watchTuiPreferences(
|
|
|
308
322
|
// Missing/unreadable files can still be created later and detected by the
|
|
309
323
|
// directory watcher or polling fallback.
|
|
310
324
|
}
|
|
325
|
+
// The stat of the file that `lastSeen` was read from. A file whose mtime,
|
|
326
|
+
// size and inode all match it cannot have changed, so a poll skips the read.
|
|
327
|
+
let lastStat = statSignature(file)
|
|
311
328
|
let disposed = false
|
|
312
329
|
|
|
313
|
-
const checkForChange = async () => {
|
|
330
|
+
const checkForChange = async (readAnyway = false) => {
|
|
331
|
+
const current = await statSignature(file)
|
|
332
|
+
// An absent file cannot have new content; re-reading it would only fail.
|
|
333
|
+
if (current === null) return
|
|
334
|
+
const unchanged = current === lastStat && lastSeen !== null
|
|
335
|
+
// The watcher path always reads, even when the stat looks familiar, so a
|
|
336
|
+
// hand edit inside one timestamp tick is still noticed there. Only the
|
|
337
|
+
// poll relies on the stat to skip a read.
|
|
338
|
+
if (unchanged && !readAnyway) return
|
|
314
339
|
const text = await readFile(file, 'utf8').catch(() => null)
|
|
315
340
|
if (disposed || text === null) return
|
|
341
|
+
lastStat = current
|
|
316
342
|
if (text === lastSeen) return
|
|
317
343
|
lastSeen = text
|
|
318
344
|
onChange()
|
|
@@ -322,7 +348,7 @@ export function watchTuiPreferences(
|
|
|
322
348
|
if (timer) nativeClearTimeout(timer)
|
|
323
349
|
timer = nativeSetTimeout(() => {
|
|
324
350
|
timer = null
|
|
325
|
-
void checkForChange()
|
|
351
|
+
void checkForChange(true)
|
|
326
352
|
}, WATCH_DEBOUNCE_MS)
|
|
327
353
|
}
|
|
328
354
|
|
|
@@ -351,6 +377,9 @@ export function watchTuiPreferences(
|
|
|
351
377
|
// repeated close/recreate cycles, so polling must be independent of watcher
|
|
352
378
|
// construction. Recursive unreferenced timers avoid fs.watchFile's lingering
|
|
353
379
|
// handles while the host's normal event loop keeps active watchers polling.
|
|
380
|
+
// A poll only reads the file when its stat changed; one controller per
|
|
381
|
+
// terminal polled at 10 Hz, so an unconditional read ran ten times a second
|
|
382
|
+
// per terminal.
|
|
354
383
|
let pollTimer: ReturnType<typeof nativeSetTimeout> | null = null
|
|
355
384
|
let pollInFlight = false
|
|
356
385
|
const schedulePoll = () => {
|
package/dist/index-20d6rmjj.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{randomUUID as P}from"node:crypto";import{mkdir as g,readFile as y,rename as b,rm as C,rmdir as m,stat as R,unlink as w,writeFile as S}from"node:fs/promises";import{tmpdir as d}from"node:os";import{dirname as v,join as M}from"node:path";import{formatQuota as c,isQuotaStale as u,logger as z,quotaDisplayProjection as I,quotaWindowParts as s}from"@cortexkit/anthropic-auth-core";var n="OPENCODE_ANTHROPIC_AUTH_SIDEBAR_STATE_FILE",i=M(d(),"opencode-anthropic-auth"),l=M(i,"sidebar-state.json");function f(){return process.env[n]||l}var H={main:{quota:null},fallbacks:[],activeId:void 0,route:"main",relay:null,fastMode:!1,lastUpdated:0};function Q(J){return J!==null&&typeof J==="object"&&!Array.isArray(J)}function N(J){return typeof J==="number"&&Number.isFinite(J)}function o(J){if(!Q(J))return;let Z=Number(J.count),V=Number(J.inputTokens),X=Number(J.outputTokens),Y=Number(J.since);if(![Z,V,X,Y].every(Number.isFinite)||Z<0||V<0||X<0||Y<0)return;return{count:Math.floor(Z),inputTokens:Math.floor(V),outputTokens:Math.floor(X),since:Math.floor(Y)}}function r(J){if(!Q(J))return;if(typeof J.id!=="string"||!J.id.trim())return;if(typeof J.label!=="string"||!J.label.trim())return;let Z={id:J.id.trim(),label:J.label.trim()};if(J.nextDueAt===null)Z.nextDueAt=null;else if(N(J.nextDueAt))Z.nextDueAt=J.nextDueAt;if(J.lastPrimedAt===null)Z.lastPrimedAt=null;else if(N(J.lastPrimedAt))Z.lastPrimedAt=J.lastPrimedAt;if(J.lastResult==="ok"||J.lastResult==="error"||J.lastResult==="skipped")Z.lastResult=J.lastResult;let V=o(J.usage);if(V)Z.usage=V;if(N(J.estimatedCostUsd))Z.estimatedCostUsd=J.estimatedCostUsd;return Z}function a(J){if(!Q(J))return;if(typeof J.enabled!=="boolean")return;let Z=Array.isArray(J.accounts)?J.accounts.map(r).filter((V)=>V!=null):[];return{enabled:J.enabled,accounts:Z}}function q(J){if(!Q(J))return;let Z=Number(J.usedPercent),V=Number(J.remainingPercent);if(!Number.isFinite(Z)||!Number.isFinite(V))return;return{usedPercent:Z,remainingPercent:V,resetsAt:typeof J.resetsAt==="string"?J.resetsAt:void 0,...N(J.checkedAt)&&{checkedAt:J.checkedAt}}}function D(J){if(!Q(J))return null;let Z={};if(N(J.checkedAt))Z.checkedAt=J.checkedAt;let V=q(J.five_hour),X=q(J.seven_day);if(V)Z.five_hour=V;if(X)Z.seven_day=X;if(Array.isArray(J.scoped)){let Y=J.scoped.map((x)=>{if(!Q(x))return;let $=q(x);if(!$)return;if(typeof x.id!=="string"||!x.id.trim())return;if(typeof x.title!=="string"||!x.title.trim())return;if(typeof x.modelName!=="string"||!x.modelName.trim())return;let j=typeof x.modelId==="string"&&x.modelId.trim()?x.modelId.trim():void 0;return{...$,id:x.id.trim(),title:x.title.trim(),...j&&{modelId:j},modelName:x.modelName.trim()}}).filter((x)=>x!=null);Z.scoped=Y}if(Q(J.extraUsage)){let Y=A(J.extraUsage.used),x=A(J.extraUsage.limit);if(Y&&x&&typeof J.extraUsage.exhausted==="boolean")Z.extraUsage={used:Y,limit:x,...typeof J.extraUsage.utilizationPercent==="number"&&Number.isFinite(J.extraUsage.utilizationPercent)&&{utilizationPercent:J.extraUsage.utilizationPercent},...typeof J.extraUsage.severity==="string"&&{severity:J.extraUsage.severity},exhausted:J.extraUsage.exhausted}}if(typeof J.bindingWindow==="string"&&J.bindingWindow.trim())Z.bindingWindow=J.bindingWindow.trim();if(typeof J.fallbackAdvised==="boolean")Z.fallbackAdvised=J.fallbackAdvised;return Object.keys(Z).length?Z:null}function A(J){if(!Q(J))return;if(!Number.isInteger(J.amountMinor)||typeof J.currency!=="string"||!/^[A-Za-z]{3}$/.test(J.currency.trim())||!Number.isInteger(J.exponent)||J.exponent<0||J.exponent>20)return;return{amountMinor:J.amountMinor,currency:J.currency.trim(),exponent:J.exponent}}function p(J){if(!Q(J))return{...H};let Z={quota:null};if(Q(J.main)){let $=J.main;if(Z.quota=D($.quota),typeof $.enabled==="boolean")Z.enabled=$.enabled;if(typeof $.stateLine==="string")Z.stateLine=$.stateLine;if($.quotaDated===!0)Z.quotaDated=!0;if(typeof $.tierLabel==="string"&&$.tierLabel.trim())Z.tierLabel=$.tierLabel.trim();if(typeof $.quotaBackedOff==="boolean")Z.quotaBackedOff=$.quotaBackedOff;if(typeof $.quotaBackoffUntil==="number")Z.quotaBackoffUntil=$.quotaBackoffUntil;if(typeof $.refreshBackedOff==="boolean")Z.refreshBackedOff=$.refreshBackedOff;if(typeof $.refreshBackoffUntil==="number")Z.refreshBackoffUntil=$.refreshBackoffUntil}let V=Array.isArray(J.fallbacks)?J.fallbacks.filter(Q).filter(($)=>typeof $.id==="string").map(($)=>({id:$.id,label:typeof $.label==="string"?$.label:void 0,quota:D($.quota),enabled:typeof $.enabled==="boolean"?$.enabled:!1,...typeof $.stateLine==="string"&&{stateLine:$.stateLine},...$.quotaDated===!0&&{quotaDated:!0},needsReauth:typeof $.needsReauth==="boolean"?$.needsReauth:!1,...$.vaultReauth===!0&&{vaultReauth:!0},...$.vaultServed===!0&&{vaultServed:!0},...typeof $.custodyState==="string"&&{custodyState:$.custodyState},tierLabel:typeof $.tierLabel==="string"&&$.tierLabel.trim()?$.tierLabel.trim():void 0})):[],X=null;if(Q(J.relay)){let $=J.relay;if(typeof $.enabled==="boolean"&&typeof $.transport==="string")X={enabled:$.enabled,transport:$.transport}}let Y;if(Q(J.cacheKeep)){let $=J.cacheKeep;if(typeof $.enabled==="boolean")Y={enabled:$.enabled,window:typeof $.window==="string"?$.window:void 0,trackedSessions:typeof $.trackedSessions==="number"?$.trackedSessions:void 0}}let x=Array.isArray(J.fableRecoveries)?J.fableRecoveries.filter(Q).flatMap(($)=>{if(typeof $.sessionId!=="string"||$.mode!=="opus"&&$.mode!=="fable"&&$.mode!=="server"||typeof $.remaining!=="number"||!Number.isFinite($.remaining)||typeof $.changedAt!=="number"||!Number.isFinite($.changedAt))return[];return[{sessionId:$.sessionId,mode:$.mode,remaining:Math.max(0,Math.floor($.remaining)),changedAt:$.changedAt,requestedModelId:typeof $.requestedModelId==="string"?$.requestedModelId:void 0,targetModelId:typeof $.targetModelId==="string"?$.targetModelId:void 0}]}):[];return{main:Z,fallbacks:V,activeId:typeof J.activeId==="string"?J.activeId:void 0,route:typeof J.route==="string"?J.route:H.route,relay:X,fastMode:typeof J.fastMode==="boolean"?J.fastMode:H.fastMode,cacheKeep:Y,prime:a(J.prime),fableRecoveries:x.length>0?x:void 0,lastUpdated:typeof J.lastUpdated==="number"?J.lastUpdated:0}}var W=Promise.resolve(),t=1000,e=2000,JJ=5,ZJ=15,E=new Map;function HJ(J,Z=f()){if(J)E.set(Z,J);else E.delete(Z)}function U(J){return E.get(J)}var k=null;function WJ(J){k=J}function UJ(){return k}function $J(J){return new Promise((Z)=>setTimeout(Z,J))}async function F(J){let Z=`${J}.lock`,V=U(J),X=V?.lockBudgetMs??t,Y=V?.lockRetryMinMs??JJ,x=V?.lockRetryMaxMs??ZJ,$=Date.now()+X;while(!0){try{await g(Z);let G=P(),K=M(Z,G);return await S(K,"",{encoding:"utf8",mode:384,flag:"wx"}),await V?.onLockAcquired?.(Z),{ownsLock:async()=>{try{return await R(K),!0}catch{return!1}},release:async()=>{try{await w(K)}catch{return}await V?.beforeReleaseDirectoryRemoval?.(Z),await m(Z).catch(()=>{})}}}catch(G){if(G.code!=="EEXIST")return z.trace("sidebar","state lock unavailable; write skipped",{stateFile:J,error:G instanceof Error?G.message:String(G)}),null}try{let G=await R(Z);if(Date.now()-G.mtimeMs>e){await V?.afterStaleLockStat?.(Z);let K=`${Z}.evict-${process.pid}-${P()}`;try{await b(Z,K)}catch(O){if(O.code==="ENOENT")continue;throw O}await V?.onStaleLockClaimed?.(Z),await C(K,{recursive:!0,force:!0}).catch(()=>{});continue}}catch(G){if(G.code==="ENOENT")continue;return z.trace("sidebar","state lock inspection failed; write skipped",{stateFile:J,error:G instanceof Error?G.message:String(G)}),null}let j=$-Date.now();if(j<=0)return z.warn("sidebar","lock budget exhausted, write skipped",{stateFile:J,budgetMs:X}),null;let B=Y+Math.floor(Math.random()*(x-Y+1));await $J(Math.min(B,j))}}async function qJ(J){return(await F(J))?.release??null}async function VJ(J,Z,V){let X=U(J),Y=`${J}.${process.pid}.${P()}.tmp`;await S(Y,JSON.stringify(Z),{encoding:"utf8",mode:384});try{if(!await V())return z.trace("sidebar","state lock lost before rename; write aborted",{stateFile:J}),await C(Y,{force:!0}).catch(()=>{}),"lock-lost-before-rename";if(await X?.beforeRename?.(J,Y),!await V())return z.trace("sidebar","state lock lost before rename; write aborted",{stateFile:J}),await C(Y,{force:!0}).catch(()=>{}),"lock-lost-before-rename";if(await b(Y,J),await U(J)?.afterRename?.(J),!await V())return"lock-lost-after-rename";return"written"}catch(x){throw await C(Y,{force:!0}).catch(()=>{}),x}}async function T(J=f()){try{let Z=await y(J,"utf8");return p(JSON.parse(Z))}catch{return H}}async function LJ(J,Z=f(),V={}){return W=W.then(async()=>{await g(v(Z),{recursive:!0});for(let X=0;X<2;X++){let Y=X===1,x=await F(Z);if(!x){if(Y)z.warn("sidebar","post-rename repair lock unavailable; write skipped",{stateFile:Z});return}let $=J,j;try{if(V.routingAuthoritative===!1){let B=await T(Z),G=await V.resolvePreservedRouting?.(B);if(G){let K=G.state??J;$={...K,activeId:G.activeId,route:G.route,lastUpdated:Math.max(K.lastUpdated,B.lastUpdated)}}await U(Z)?.afterMergeRead?.(Z)}else if(Y){let B=await T(Z);$={...B,activeId:J.activeId,route:J.route,relay:J.relay,fastMode:J.fastMode,cacheKeep:J.cacheKeep,lastUpdated:Math.max(B.lastUpdated,J.lastUpdated)}}$=p($),j=await VJ(Z,$,x.ownsLock)}finally{await x.release()}if(j==="lock-lost-after-rename"){if(Y){z.warn("sidebar","post-rename repair lost lock; write skipped",{stateFile:Z});return}z.warn("sidebar","state lock lost after rename; repairing write",{stateFile:Z});continue}if(j==="lock-lost-before-rename")return;V.onRoutingResolved?.({activeId:$.activeId,route:$.route});return}}).catch(()=>{}),W}async function PJ(){return W}function XJ(J){let Z=J.activeId;if(Z&&Z!=="main"){let V=J.fallbacks?.find((X)=>X.enabled&&X.id===Z);if(V)return{id:V.id,name:V.label??V.id,quota:V.quota,...V.stateLine&&{stateLine:V.stateLine,quotaDated:V.quotaDated}}}return{id:"main",name:"main",quota:J.main?.quota??null,...J.main?.stateLine&&{stateLine:J.main.stateLine,quotaDated:J.main.quotaDated}}}function L(J){if(J==="claude-mythos-5-1"||J?.startsWith("claude-mythos-5-1-"))return"Mythos 5.1";if(J==="claude-fable-5-1"||J?.startsWith("claude-fable-5-1-"))return"Fable 5.1";if(J==="claude-fable-5"||J?.startsWith("claude-fable-5-"))return"Fable 5";if(J==="claude-opus-5-5"||J?.startsWith("claude-opus-5-5-"))return"Opus 5.5";if(J==="claude-opus-5"||J?.startsWith("claude-opus-5-"))return"Opus 5";if(J==="claude-opus-4-8"||J?.startsWith("claude-opus-4-8-"))return"Opus 4.8";return J??"Fable 5"}function h(J){return new Date(J).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}function xJ(J){if(J===0)return"0";if(J<0.0001)return J.toExponential(2);return J.toFixed(Math.min(6,Math.max(0,4)))}function EJ(J,Z=Date.now()){if(J.lastResult==="error")return{text:"err",hasError:!0};if(J.lastResult==="skipped")return{text:"skip",hasError:!1};if(J.nextDueAt&&J.nextDueAt>Z)return{text:h(J.nextDueAt),hasError:!1};if(J.lastPrimedAt)return{text:`primed ${h(J.lastPrimedAt)} ✓`,hasError:!1};if(J.usage?.count)return{text:`✓ ${J.usage.count} ≈ $${xJ(J.estimatedCostUsd??0)}`,hasError:!1};return{text:"—",hasError:!1}}function MJ(J,Z){let V=J.fableRecoveries?.find((X)=>X.sessionId===Z);if(!V)return;if(V.mode==="fable")return`${L(V.requestedModelId)} · restored`;if(V.mode==="server")return`${L(V.requestedModelId)}→${L(V.targetModelId)} · safety`;return`Opus 4.8 · ${V.remaining} left`}function fJ(J,Z=10){if(Z<=0)return"";let V=J;if(Bun.stringWidth(V)>Z){V="";for(let X of J){if(Bun.stringWidth(V+X)>Z-1)break;V+=X}V+="…"}return V+" ".repeat(Math.max(0,Z-Bun.stringWidth(V)))}function YJ(J,Z=Date.now()){let V=J?.scoped??[];if(!V.length)return[];let{windows:X}=s(I({scoped:V},Z),{now:Z});return V.map((Y)=>X.find((x)=>x.kind==="window"&&x.name===Y.title)?.short||Y.title)}function _J(J,Z=Date.now()){return Math.max(2,...J.flatMap((V)=>YJ(V,Z).map((X)=>Bun.stringWidth(X))))}var GJ=3;function RJ(J){let{width:Z,labelWidth:V,barWidth:X,figure:Y,reset:x}=J,$={barCells:X,reset:x||void 0};if(Z===void 0)return $;let j=(K)=>V+1+(K.barCells>0?K.barCells+1:0)+Bun.stringWidth(Y)+(K.reset?1+Bun.stringWidth(K.reset):0),B=Math.min(GJ,X),G=[$];for(let K=X;K>=B;K--)G.push({barCells:K});return G.find((K)=>j(K)<=Z)??{barCells:B}}function DJ(J,Z,V){if(!J||J.state==="on-pace")return null;let X=`${J.state} ${Math.round(Math.abs(J.deltaPercent))}%`,Y=V?`${X} · out in ${V}`:`${X} · lasts to reset`;if(Z===void 0||Bun.stringWidth(Y)<=Z)return Y;if(Z<=0)return"";let x="";for(let $ of Y){if(Bun.stringWidth(x+$)>Z-1)break;x+=$}return`${x.trimEnd()}…`}function AJ(J,Z=Date.now()){let V=J?.five_hour?.usedPercent??null,X=J?.seven_day?.usedPercent??null,Y=J?.scoped??[],x=Y.map((j)=>j.usedPercent);if(!J||V==null&&X==null&&Y.length===0&&!J.extraUsage)return{fiveHourUsedPercent:V,sevenDayUsedPercent:X,scopedUsedPercents:x,text:null,stale:!1};let $=I(J,Z);return{fiveHourUsedPercent:V,sevenDayUsedPercent:X,scopedUsedPercents:x,text:c($,{now:Z,form:"narrow"}),stale:u($,{now:Z})}}function TJ(J,Z=!0){let V=J.activeId===void 0?void 0:XJ(J).id;return[{id:"main",name:"main",quota:J.main?.quota??null,quotaDated:J.main?.quotaDated,active:V==="main"},...Z?(J.fallbacks??[]).filter((X)=>X.enabled||X.custodyState&&X.custodyState!=="off").map((X)=>({id:X.id,name:X.label??X.id,quota:X.quota,quotaDated:X.quotaDated,active:V===X.id})):[]]}var hJ=18000000,gJ=604800000,KJ=300000,jJ=0.01,BJ=1;function bJ(J,Z,V){if(!J.resetsAt)return null;let X=new Date(J.resetsAt).getTime();if(!Number.isFinite(X))return null;let Y=X-Z,x=V-Y;if(x<KJ)return null;if(x<Z*jJ)return null;if(x>=Z)return null;let $=J.usedPercent,j=Math.min(Math.max(x/Z*100,0),100),B=$-j,G=Math.abs(B)<BJ?"on-pace":B>0?"deficit":"reserve",K=null;if($>0){let O=x*100/$,_=Y+O;if(_<X)K=new Date(_).toISOString()}return{pacePercent:j,deltaPercent:B,state:G,runsOutAt:K}}
|
|
2
|
-
export{f as G,H,p as I,HJ as J,WJ as K,UJ as L,qJ as M,T as N,LJ as O,PJ as P,XJ as Q,L as R,h as S,xJ as T,EJ as U,MJ as V,fJ as W,YJ as X,_J as Y,GJ as Z,RJ as _,DJ as $,AJ as aa,TJ as ba,hJ as ca,gJ as da,bJ as ea};
|