tokmon 0.24.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/dist/{bootstrap-ink-HL2BYNW3.js → bootstrap-ink-5UFPNDV4.js} +53 -44
- package/dist/{chunk-45ZP755H.js → chunk-4CY725NY.js} +1 -1
- package/dist/{chunk-W2WGBXZG.js → chunk-AMD4PXDG.js} +1 -1
- package/dist/{chunk-5CWOJMAH.js → chunk-E2YXYU73.js} +2 -0
- package/dist/{chunk-REO4O5VE.js → chunk-OWPJUG4C.js} +33 -13
- package/dist/{chunk-3TJVFKXV.js → chunk-QM5E5RJZ.js} +1 -1
- package/dist/{chunk-O27I2XFN.js → chunk-RTGMDE7U.js} +39 -18
- package/dist/{chunk-42H7R376.js → chunk-VGYBQH5I.js} +4 -3
- package/dist/{cli-command-TLGIGF63.js → cli-command-U7RZ2GTD.js} +5 -5
- package/dist/cli.js +6 -6
- package/dist/{config-QC5QSP3G.js → config-2PXUENQL.js} +1 -1
- package/dist/{daemon-F4EGY77J.js → daemon-PQWKWRC6.js} +5 -5
- package/dist/{daemon-handle-NLCLYWFU.js → daemon-handle-2GVZT55B.js} +3 -3
- package/dist/server-5WIWS27N.js +11 -0
- package/dist/web/assets/{Area-DCES17aZ.js → Area-Tx1mUuqM.js} +1 -1
- package/dist/web/assets/{analytics-DDW6zcnj.js → analytics-CAzklhXc.js} +2 -2
- package/dist/web/assets/{breakdown-D5LPPtqh.js → breakdown-BoPnOv7Y.js} +1 -1
- package/dist/web/assets/{chart-bHY7MQV0.js → chart-jK7A9m7R.js} +1 -1
- package/dist/web/assets/{explore-DdWB-Pu7.js → explore-e8wtwk7s.js} +1 -1
- package/dist/web/assets/{index-C9jxb2o3.js → index-C8oe6EfM.js} +24 -24
- package/dist/web/assets/index-NnKaHxPO.css +1 -0
- package/dist/web/assets/{models-o1wJnG6w.js → models-_4IZNDxW.js} +2 -2
- package/dist/web/assets/overview-BBOfd_Fd.js +2 -0
- package/dist/web/assets/{panel-DBnK1oy0.js → panel-CstB4hPm.js} +1 -1
- package/dist/web/assets/{primitives-CBmteetD.js → primitives-G9ALG7ih.js} +1 -1
- package/dist/web/assets/settings-sheet-rq6vbYXM.js +1 -0
- package/dist/web/assets/{share-sheet-DUee3FlK.js → share-sheet-BwmnebRB.js} +2 -2
- package/dist/web/assets/{timeline-BxHwOJA_.js → timeline-DkYWH6XW.js} +1 -1
- package/dist/web/assets/{use-dialog-trap-30EqxYF-.js → use-dialog-trap-Jci2xaMX.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/server-X63FGHIZ.js +0 -11
- package/dist/web/assets/index-m0KfWnN2.css +0 -1
- package/dist/web/assets/overview-wq2uAEk4.js +0 -2
- package/dist/web/assets/settings-sheet-CZ6GEPl3.js +0 -1
package/README.md
CHANGED
|
@@ -76,7 +76,7 @@ A responsive grid of provider cards (or one card at a time — see **Dashboard l
|
|
|
76
76
|
- **Today / This Week / This Month** — cost and token summaries
|
|
77
77
|
- **Burn rate** — current $/hr
|
|
78
78
|
- **Cache saved** — what caching has saved you
|
|
79
|
-
- **Rate limits** — live utilization bars with reset countdowns
|
|
79
|
+
- **Rate limits** — live utilization bars with reset countdowns or exact reset dates
|
|
80
80
|
- **Sparkline** — recent daily activity
|
|
81
81
|
|
|
82
82
|
The grid reflows to fit your terminal — more columns when it's wide, compacting cards when it's short. With more providers than fit on screen, it splits into **pages**; **scroll** (mouse wheel) to move between them (or `↑`/`↓` / `[` `]`). When you track more than one account, a focus strip lets you view **All** together or zoom into a single account.
|
|
@@ -105,7 +105,7 @@ tokmon serve --port 8080
|
|
|
105
105
|
tokmon serve --no-open # don't auto-open the browser
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
It binds to `127.0.0.1` by default and reads the same local daemon state — nothing leaves your machine. The browser connects directly over a same-origin WebSocket; dashboard URLs have no token or login step. Optional LAN access can be enabled in settings, with an explicit unsafe-access warning, and takes effect after the daemon restarts. The dashboard renders instantly from a cached snapshot, then streams live updates and goes idle when no tab is open. Filter by provider, model, account, and period, flip between dark and light, and export any panel — or a summary card — as a PNG with the **Share** button.
|
|
108
|
+
It binds to `127.0.0.1` by default and reads the same local daemon state — nothing leaves your machine. The browser connects directly over a same-origin WebSocket; dashboard URLs have no token or login step. Optional LAN access can be enabled in settings, with an explicit unsafe-access warning, and takes effect after the daemon restarts. The dashboard renders instantly from a cached snapshot, then streams live updates and goes idle when no tab is open. Press `R` or use the visible **Refresh** control to update it. Filter by provider, model, account, and period, flip between dark and light, and export any panel — or a summary card — as a PNG with the **Share** button.
|
|
109
109
|
|
|
110
110
|
### Overview
|
|
111
111
|
|
|
@@ -192,6 +192,8 @@ Press `s` to open.
|
|
|
192
192
|
- **Dashboard layout** — `grid` (all providers at once) or `single` (one at a time)
|
|
193
193
|
- **Default focus** — start on `all`, or remember your `last` focused account
|
|
194
194
|
- **ASCII glyphs** — `auto` (detect), `on` (force ASCII), or `off` (force Unicode)
|
|
195
|
+
- **Network access** — loopback-only by default; optional LAN access is explicitly marked unsafe
|
|
196
|
+
- **Reset times** — show time remaining or the exact reset date/time in the configured timezone
|
|
195
197
|
|
|
196
198
|
**Providers** — toggle each provider on or off.
|
|
197
199
|
|
|
@@ -253,7 +255,7 @@ tokmon runs a small local **daemon** that does all the data collection. The term
|
|
|
253
255
|
- Each enabled provider is detected automatically, and its real account identity — email and plan — is read from local auth (e.g. Claude `~/.claude.json`, the Codex `id_token`, Cursor's state DB). Extra accounts, like additional Claude homes, are auto-discovered too.
|
|
254
256
|
|
|
255
257
|
**Limits & billing**
|
|
256
|
-
- Rate limits and remaining spend/quota come from each provider's own official API
|
|
258
|
+
- Rate limits and remaining spend/quota come from each provider's own official API. Tokmon refreshes them on the configured billing interval; terminal focus and extra dashboard viewers reuse fresh data instead of issuing extra provider requests.
|
|
257
259
|
|
|
258
260
|
**Responsiveness**
|
|
259
261
|
- Dashboard summaries and table history load independently and refresh on separate intervals, so the UI stays responsive even on large histories.
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
createDaemonRpcClient
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-4CY725NY.js";
|
|
5
5
|
import {
|
|
6
6
|
accountsByProvider,
|
|
7
7
|
buildAccounts,
|
|
8
8
|
fetchPeak,
|
|
9
9
|
modelColor
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-VGYBQH5I.js";
|
|
11
11
|
import {
|
|
12
12
|
PROVIDERS,
|
|
13
13
|
PROVIDER_ORDER,
|
|
@@ -15,13 +15,15 @@ import {
|
|
|
15
15
|
col,
|
|
16
16
|
currency,
|
|
17
17
|
detectProviders,
|
|
18
|
+
formatResetAt,
|
|
19
|
+
resetAt,
|
|
18
20
|
resolveTimezone,
|
|
19
21
|
shortDate,
|
|
20
22
|
systemTimezone,
|
|
21
23
|
time,
|
|
22
24
|
tokens,
|
|
23
25
|
withTimeout
|
|
24
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-RTGMDE7U.js";
|
|
25
27
|
import {
|
|
26
28
|
COLOR_PALETTE,
|
|
27
29
|
DEFAULTS,
|
|
@@ -36,7 +38,7 @@ import {
|
|
|
36
38
|
sanitizeTyped,
|
|
37
39
|
saveConfigSync,
|
|
38
40
|
snapshotCacheFile
|
|
39
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-E2YXYU73.js";
|
|
40
42
|
import {
|
|
41
43
|
glyphs
|
|
42
44
|
} from "./chunk-RF4GGQGM.js";
|
|
@@ -161,27 +163,22 @@ var TabBar = memo(function TabBar2({ tabs, active, onSelect }) {
|
|
|
161
163
|
" "
|
|
162
164
|
] }) }, t)) });
|
|
163
165
|
});
|
|
164
|
-
var PeakBadge = memo(function PeakBadge2({ peak }) {
|
|
166
|
+
var PeakBadge = memo(function PeakBadge2({ peak, resetDisplay = "relative", tz }) {
|
|
165
167
|
const color = peak.state === "peak" ? "red" : "green";
|
|
168
|
+
const changesAt = peak.changesAt ?? (peak.minutesUntilChange !== null ? new Date(Date.now() + peak.minutesUntilChange * 6e4).toISOString() : null);
|
|
166
169
|
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
167
170
|
/* @__PURE__ */ jsxs(Text, { color, children: [
|
|
168
171
|
glyphs().dot,
|
|
169
172
|
" "
|
|
170
173
|
] }),
|
|
171
174
|
/* @__PURE__ */ jsx(Text, { bold: true, color, children: peak.label }),
|
|
172
|
-
|
|
175
|
+
changesAt && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
173
176
|
" (",
|
|
174
|
-
|
|
177
|
+
formatResetAt(changesAt, resetDisplay, Date.now(), tz),
|
|
175
178
|
")"
|
|
176
179
|
] })
|
|
177
180
|
] });
|
|
178
181
|
});
|
|
179
|
-
function fmtMinutes(mins) {
|
|
180
|
-
if (mins < 60) return `${mins}m`;
|
|
181
|
-
const h = Math.floor(mins / 60);
|
|
182
|
-
const m = mins % 60;
|
|
183
|
-
return m === 0 ? `${h}h` : `${h}h ${m}m`;
|
|
184
|
-
}
|
|
185
182
|
function currencySymbol(cur) {
|
|
186
183
|
return cur === "EUR" ? glyphs().eur : cur === "GBP" ? glyphs().gbp : "$";
|
|
187
184
|
}
|
|
@@ -292,7 +289,7 @@ function computeDashLayout(groups, stats, cols, budget, focusId, layoutPref) {
|
|
|
292
289
|
}
|
|
293
290
|
return chooseLayout(content, budget, groups.length, single, cols, heights);
|
|
294
291
|
}
|
|
295
|
-
var DashboardView = memo2(function DashboardView2({ groups, stats, cols, budget, focusId, layout, page = 0, privacyMode = false }) {
|
|
292
|
+
var DashboardView = memo2(function DashboardView2({ groups, stats, cols, budget, focusId, layout, page = 0, privacyMode = false, resetDisplay = "relative", tz }) {
|
|
296
293
|
if (groups.length === 0) {
|
|
297
294
|
return /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
298
295
|
"No providers enabled ",
|
|
@@ -307,7 +304,7 @@ var DashboardView = memo2(function DashboardView2({ groups, stats, cols, budget,
|
|
|
307
304
|
const pg = pageCount > 1 ? (page % pageCount + pageCount) % pageCount : 0;
|
|
308
305
|
const visible = pageCount > 1 ? groups.slice(pg * cardsPerPage, pg * cardsPerPage + cardsPerPage) : groups;
|
|
309
306
|
return /* @__PURE__ */ jsxs2(Box2, { height: budget, flexDirection: "column", overflow: "hidden", children: [
|
|
310
|
-
/* @__PURE__ */ jsx2(Box2, { width: content, flexWrap: "wrap", columnGap: GAP, rowGap: 1, alignItems: "flex-start", children: visible.map((g) => /* @__PURE__ */ jsx2(Box2, { flexShrink: 0, children: /* @__PURE__ */ jsx2(ProviderCard, { provider: g.provider, accounts: g.accounts, stats, width: cardW, variant, privacyMode }) }, g.provider)) }),
|
|
307
|
+
/* @__PURE__ */ jsx2(Box2, { width: content, flexWrap: "wrap", columnGap: GAP, rowGap: 1, alignItems: "flex-start", children: visible.map((g) => /* @__PURE__ */ jsx2(Box2, { flexShrink: 0, children: /* @__PURE__ */ jsx2(ProviderCard, { provider: g.provider, accounts: g.accounts, stats, width: cardW, variant, privacyMode, resetDisplay, tz }) }, g.provider)) }),
|
|
311
308
|
pageCount > 1 && /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
312
309
|
" ",
|
|
313
310
|
glyphs().middot,
|
|
@@ -323,7 +320,7 @@ var DashboardView = memo2(function DashboardView2({ groups, stats, cols, budget,
|
|
|
323
320
|
] })
|
|
324
321
|
] });
|
|
325
322
|
});
|
|
326
|
-
function ProviderCard({ provider, accounts, stats, width, variant, privacyMode = false }) {
|
|
323
|
+
function ProviderCard({ provider, accounts, stats, width, variant, privacyMode = false, resetDisplay, tz }) {
|
|
327
324
|
const meta = PROVIDERS[provider];
|
|
328
325
|
const items = accounts.map((a) => ({ account: a, s: stats.get(a.id) }));
|
|
329
326
|
const dashboards = items.map((i) => i.s?.dashboard).filter((d) => !!d);
|
|
@@ -359,7 +356,7 @@ function ProviderCard({ provider, accounts, stats, width, variant, privacyMode =
|
|
|
359
356
|
] })),
|
|
360
357
|
meta.hasBilling && showBars && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
361
358
|
meta.hasUsage && /* @__PURE__ */ jsx2(Rule, { inner }),
|
|
362
|
-
/* @__PURE__ */ jsx2(LimitsBlock, { items, inner, privacyMode })
|
|
359
|
+
/* @__PURE__ */ jsx2(LimitsBlock, { items, inner, privacyMode, resetDisplay, tz })
|
|
363
360
|
] }),
|
|
364
361
|
meta.hasBilling && !showBars && !meta.hasUsage && /* @__PURE__ */ jsx2(CompactBilling, { items, privacyMode }),
|
|
365
362
|
hasSpark && showSpark && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
@@ -432,11 +429,12 @@ function accountTitle(account, billing, privacyMode = false) {
|
|
|
432
429
|
const title = email && !account.name.includes("@") ? `${account.name} ${email}` : account.name;
|
|
433
430
|
return privacyMode ? redactEmail(title) : title;
|
|
434
431
|
}
|
|
435
|
-
function LimitsBlock({ items, inner, privacyMode }) {
|
|
432
|
+
function LimitsBlock({ items, inner, privacyMode, resetDisplay, tz }) {
|
|
436
433
|
const showName = items.length > 1;
|
|
437
434
|
const labels = items.flatMap((i) => i.s?.billing?.metrics ?? []).map((m) => m.label.length);
|
|
438
435
|
const labelW = Math.min(Math.max(7, ...labels) + 1, 14);
|
|
439
|
-
const
|
|
436
|
+
const resetW = resetDisplay === "absolute" ? 17 : 8;
|
|
437
|
+
const barW = Math.max(10, Math.min(46, inner - labelW - resetW - 5));
|
|
440
438
|
return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: items.map(({ account, s }, idx) => {
|
|
441
439
|
const billing = s?.billing;
|
|
442
440
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginTop: showName && idx > 0 ? 1 : 0, children: [
|
|
@@ -450,11 +448,11 @@ function LimitsBlock({ items, inner, privacyMode }) {
|
|
|
450
448
|
!billing ? /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
451
449
|
"Fetching",
|
|
452
450
|
glyphs().ellipsis
|
|
453
|
-
] }) : billing.error ? /* @__PURE__ */ jsx2(Text2, { color: "red", wrap: "truncate-end", children: privacyMode ? redactEmail(billing.error) : billing.error }) : billing.metrics.length === 0 ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "No data" }) : billing.metrics.map((m, i) => /* @__PURE__ */ jsx2(MetricRow, { m, color: account.color, barW, labelW }, `${m.label}${i}`))
|
|
451
|
+
] }) : billing.error ? /* @__PURE__ */ jsx2(Text2, { color: "red", wrap: "truncate-end", children: privacyMode ? redactEmail(billing.error) : billing.error }) : billing.metrics.length === 0 ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "No data" }) : billing.metrics.map((m, i) => /* @__PURE__ */ jsx2(MetricRow, { m, color: account.color, barW, labelW, resetW, resetDisplay, tz }, `${m.label}${i}`))
|
|
454
452
|
] }, account.id);
|
|
455
453
|
}) });
|
|
456
454
|
}
|
|
457
|
-
function MetricRow({ m, color, barW, labelW }) {
|
|
455
|
+
function MetricRow({ m, color, barW, labelW, resetW, resetDisplay, tz }) {
|
|
458
456
|
if (m.format.kind === "percent") {
|
|
459
457
|
const barColor = m.used >= 90 ? "red" : m.used >= 75 ? "yellow" : color;
|
|
460
458
|
return /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
@@ -464,7 +462,7 @@ function MetricRow({ m, color, barW, labelW }) {
|
|
|
464
462
|
Math.round(m.used),
|
|
465
463
|
"%"
|
|
466
464
|
] }) }),
|
|
467
|
-
/* @__PURE__ */ jsx2(Box2, { width:
|
|
465
|
+
/* @__PURE__ */ jsx2(Box2, { width: resetW, justifyContent: "flex-end", children: m.resetsAt ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: resetAt(m.resetsAt, resetDisplay, Date.now(), tz) }) : /* @__PURE__ */ jsx2(Text2, { children: " " }) })
|
|
468
466
|
] });
|
|
469
467
|
}
|
|
470
468
|
return /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
@@ -483,12 +481,13 @@ function SparkFooter({ series, month, color }) {
|
|
|
483
481
|
] });
|
|
484
482
|
}
|
|
485
483
|
function aggregate(list) {
|
|
486
|
-
const zero = () => ({ cost: 0, tokens: 0, cacheRead: 0, cacheSavings: 0 });
|
|
484
|
+
const zero = () => ({ cost: 0, tokens: 0, input: 0, cacheRead: 0, cacheSavings: 0 });
|
|
487
485
|
const z = { today: zero(), week: zero(), month: zero(), burnRate: 0, series: [] };
|
|
488
486
|
for (const d of list) {
|
|
489
487
|
for (const k of ["today", "week", "month"]) {
|
|
490
488
|
z[k].cost += d[k].cost;
|
|
491
489
|
z[k].tokens += d[k].tokens;
|
|
490
|
+
z[k].input += d[k].input ?? 0;
|
|
492
491
|
z[k].cacheRead += d[k].cacheRead;
|
|
493
492
|
z[k].cacheSavings += d[k].cacheSavings;
|
|
494
493
|
}
|
|
@@ -500,7 +499,7 @@ function aggregate(list) {
|
|
|
500
499
|
return z;
|
|
501
500
|
}
|
|
502
501
|
var TotalsRow = memo2(function TotalsRow2({ groups, stats, cols }) {
|
|
503
|
-
const zero = () => ({ cost: 0, tokens: 0, cacheRead: 0, cacheSavings: 0 });
|
|
502
|
+
const zero = () => ({ cost: 0, tokens: 0, input: 0, cacheRead: 0, cacheSavings: 0 });
|
|
504
503
|
const t = zero(), w = zero(), m = zero();
|
|
505
504
|
for (const g of groups) {
|
|
506
505
|
if (!PROVIDERS[g.provider].hasUsage) continue;
|
|
@@ -509,18 +508,23 @@ var TotalsRow = memo2(function TotalsRow2({ groups, stats, cols }) {
|
|
|
509
508
|
if (!d) continue;
|
|
510
509
|
t.cost += d.today.cost;
|
|
511
510
|
t.tokens += d.today.tokens;
|
|
511
|
+
t.input += d.today.input ?? 0;
|
|
512
512
|
w.cost += d.week.cost;
|
|
513
513
|
w.tokens += d.week.tokens;
|
|
514
|
+
w.input += d.week.input ?? 0;
|
|
514
515
|
m.cost += d.month.cost;
|
|
515
516
|
m.tokens += d.month.tokens;
|
|
517
|
+
m.input += d.month.input ?? 0;
|
|
516
518
|
}
|
|
517
519
|
}
|
|
518
520
|
const inner = cols - 4;
|
|
519
521
|
const dot = glyphs().middot;
|
|
520
|
-
const
|
|
521
|
-
const
|
|
522
|
+
const monthNonCached = m.input > 0 ? `, ${tokens(m.input)} non-cached` : "";
|
|
523
|
+
const full = `${glyphs().dotAll} Today ${currency(t.cost)} (${tokens(t.tokens)} tok) ${dot} Week ${currency(w.cost)} (${tokens(w.tokens)} tok) ${dot} Month ${currency(m.cost)} (${tokens(m.tokens)} tok${monthNonCached})`;
|
|
524
|
+
const monthOnly = `${glyphs().dotAll} This Month ${currency(m.cost)} (${tokens(m.tokens)} tok${monthNonCached})`;
|
|
525
|
+
const noTok = `${glyphs().dotAll} Today ${currency(t.cost)} ${dot} Week ${currency(w.cost)} ${dot} Month ${currency(m.cost)}${m.input > 0 ? ` (${tokens(m.input)} non-cached)` : ""}`;
|
|
522
526
|
const tight = `${glyphs().dotAll} ${currency(t.cost)} ${dot} ${currency(w.cost)} ${dot} ${currency(m.cost)}`;
|
|
523
|
-
const text = full.length <= inner ? full : noTok.length <= inner ? noTok : tight;
|
|
527
|
+
const text = full.length <= inner ? full : monthOnly.length <= inner ? monthOnly : noTok.length <= inner ? noTok : tight;
|
|
524
528
|
return /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: text }) });
|
|
525
529
|
});
|
|
526
530
|
|
|
@@ -532,7 +536,7 @@ import { Box as Box4, Text as Text4 } from "ink";
|
|
|
532
536
|
import { memo as memo3 } from "react";
|
|
533
537
|
import { Box as Box3, Text as Text3 } from "ink";
|
|
534
538
|
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
535
|
-
var GENERAL_ROWS =
|
|
539
|
+
var GENERAL_ROWS = 10;
|
|
536
540
|
var SETTINGS_TABS = ["general", "providers", "accounts"];
|
|
537
541
|
var SETTINGS_TAB_LABELS = {
|
|
538
542
|
general: "General",
|
|
@@ -628,7 +632,18 @@ var SettingsView = memo3(function SettingsView2({
|
|
|
628
632
|
] })
|
|
629
633
|
] }),
|
|
630
634
|
/* @__PURE__ */ jsx3(Row, { cursor, idx: 8, label: "Network access", children: /* @__PURE__ */ jsx3(Text3, { bold: true, color: config.allowNetworkAccess ? "red" : "green", children: config.allowNetworkAccess ? "LAN (unsafe)" : "local only" }) }),
|
|
631
|
-
config.allowNetworkAccess && /* @__PURE__ */ jsx3(Text3, { color: "red", children: " Warning: dashboard data and settings will be exposed to your local network after daemon restart." })
|
|
635
|
+
config.allowNetworkAccess && /* @__PURE__ */ jsx3(Text3, { color: "red", children: " Warning: dashboard data and settings will be exposed to your local network after daemon restart." }),
|
|
636
|
+
/* @__PURE__ */ jsxs3(Row, { cursor, idx: 9, label: "Reset times", children: [
|
|
637
|
+
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
638
|
+
glyphs().caretL,
|
|
639
|
+
" "
|
|
640
|
+
] }),
|
|
641
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, color: "yellow", children: config.resetDisplay === "relative" ? "time remaining" : "exact date/time" }),
|
|
642
|
+
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
643
|
+
" ",
|
|
644
|
+
glyphs().caretR
|
|
645
|
+
] })
|
|
646
|
+
] })
|
|
632
647
|
] }),
|
|
633
648
|
activeTab === "providers" && /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
634
649
|
/* @__PURE__ */ jsx3(Text3, { bold: true, dimColor: true, children: "Providers" }),
|
|
@@ -2123,6 +2138,10 @@ function handleSettings(input, key, ctx) {
|
|
|
2123
2138
|
}
|
|
2124
2139
|
if (index === 8 && (inputKey.leftArrow || inputKey.rightArrow || inputKey.return)) {
|
|
2125
2140
|
updateConfig((current) => ({ ...current, allowNetworkAccess: !current.allowNetworkAccess }));
|
|
2141
|
+
return;
|
|
2142
|
+
}
|
|
2143
|
+
if (index === 9 && (inputKey.leftArrow || inputKey.rightArrow || inputKey.return)) {
|
|
2144
|
+
updateConfig((current) => ({ ...current, resetDisplay: current.resetDisplay === "relative" ? "absolute" : "relative" }));
|
|
2126
2145
|
}
|
|
2127
2146
|
}
|
|
2128
2147
|
}
|
|
@@ -2278,10 +2297,9 @@ function terminalFocusEvent(input) {
|
|
|
2278
2297
|
if (input === "[O") return "out";
|
|
2279
2298
|
return null;
|
|
2280
2299
|
}
|
|
2281
|
-
function handleTerminalFocusInput(input
|
|
2300
|
+
function handleTerminalFocusInput(input) {
|
|
2282
2301
|
const event = terminalFocusEvent(input);
|
|
2283
2302
|
if (!event) return false;
|
|
2284
|
-
if (event === "in") onFocusIn();
|
|
2285
2303
|
return true;
|
|
2286
2304
|
}
|
|
2287
2305
|
function isRefreshAllShortcut(input, mode) {
|
|
@@ -3389,7 +3407,7 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3389
3407
|
if (webStartingRef.current) return;
|
|
3390
3408
|
webStartingRef.current = true;
|
|
3391
3409
|
try {
|
|
3392
|
-
const { startWebServer } = await import("./server-
|
|
3410
|
+
const { startWebServer } = await import("./server-5WIWS27N.js");
|
|
3393
3411
|
const ctrl = await startWebServer({ config: cfg, log: false });
|
|
3394
3412
|
webRef.current = ctrl;
|
|
3395
3413
|
openUrl(ctrl.url);
|
|
@@ -3436,17 +3454,8 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3436
3454
|
() => tab === 1 ? sortRows(filterTokenRows(filterRowsByModel(rawTokenRows, activeTableModel), search), sort) : [],
|
|
3437
3455
|
[tab, rawTokenRows, activeTableModel, search, sort]
|
|
3438
3456
|
);
|
|
3439
|
-
const lastFocusRefreshRef = useRef8(Date.now());
|
|
3440
|
-
const refreshOnFocus = useCallback7(() => {
|
|
3441
|
-
const now = Date.now();
|
|
3442
|
-
if (now - lastFocusRefreshRef.current < 3e4) return;
|
|
3443
|
-
lastFocusRefreshRef.current = now;
|
|
3444
|
-
const task = connected ? daemonRefreshRef.current("billing") : degradedRefreshRef.current();
|
|
3445
|
-
void task.catch(() => {
|
|
3446
|
-
});
|
|
3447
|
-
}, [connected]);
|
|
3448
3457
|
useInput((input, key) => {
|
|
3449
|
-
if (handleTerminalFocusInput(input
|
|
3458
|
+
if (handleTerminalFocusInput(input)) return;
|
|
3450
3459
|
handleKey(input, key, {
|
|
3451
3460
|
onboarding: {
|
|
3452
3461
|
show: showPicker,
|
|
@@ -3568,7 +3577,7 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3568
3577
|
] }),
|
|
3569
3578
|
/* @__PURE__ */ jsxs12(Box11, { children: [
|
|
3570
3579
|
peak && /* @__PURE__ */ jsxs12(Fragment4, { children: [
|
|
3571
|
-
/* @__PURE__ */ jsx12(PeakBadge, { peak }),
|
|
3580
|
+
/* @__PURE__ */ jsx12(PeakBadge, { peak, resetDisplay: cfg.resetDisplay, tz }),
|
|
3572
3581
|
/* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
|
|
3573
3582
|
" ",
|
|
3574
3583
|
glyphs().middot,
|
|
@@ -3627,7 +3636,7 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3627
3636
|
] })
|
|
3628
3637
|
] }),
|
|
3629
3638
|
tab === 0 && /* @__PURE__ */ jsxs12(Fragment4, { children: [
|
|
3630
|
-
/* @__PURE__ */ jsx12(DashboardView, { groups, stats, cols, budget: gridBudget, focusId, layout: cfg.dashboardLayout, page: dashPage, privacyMode: cfg.privacyMode }),
|
|
3639
|
+
/* @__PURE__ */ jsx12(DashboardView, { groups, stats, cols, budget: gridBudget, focusId, layout: cfg.dashboardLayout, page: dashPage, privacyMode: cfg.privacyMode, resetDisplay: cfg.resetDisplay, tz }),
|
|
3631
3640
|
slots.length > 1 && /* @__PURE__ */ jsxs12(Box11, { marginTop: 1, children: [
|
|
3632
3641
|
/* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "focus " }),
|
|
3633
3642
|
/* @__PURE__ */ jsx12(
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
TOKMON_WS_METHODS,
|
|
4
4
|
TOKMON_WS_PATH,
|
|
5
5
|
TokmonRpcGroup
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-RTGMDE7U.js";
|
|
7
7
|
|
|
8
8
|
// src/client/daemon-rpc-client.ts
|
|
9
9
|
import { Cause, Context, Duration, Effect, Exit, Fiber, Layer, ManagedRuntime, Schedule, Stream } from "effect";
|
|
@@ -26,6 +26,7 @@ var DEFAULTS = {
|
|
|
26
26
|
defaultFocus: "all",
|
|
27
27
|
ascii: "auto",
|
|
28
28
|
allowNetworkAccess: false,
|
|
29
|
+
resetDisplay: "relative",
|
|
29
30
|
knownProviders: []
|
|
30
31
|
};
|
|
31
32
|
var LEGACY_KNOWN = ["claude", "codex", "cursor"];
|
|
@@ -216,6 +217,7 @@ function repairConfig(input) {
|
|
|
216
217
|
defaultFocus: parsed.defaultFocus === "last" ? "last" : "all",
|
|
217
218
|
ascii: parsed.ascii === "on" ? "on" : parsed.ascii === "off" ? "off" : "auto",
|
|
218
219
|
allowNetworkAccess: parsed.allowNetworkAccess === true,
|
|
220
|
+
resetDisplay: parsed.resetDisplay === "absolute" ? "absolute" : "relative",
|
|
219
221
|
knownProviders
|
|
220
222
|
};
|
|
221
223
|
for (const key of Object.keys(DEFAULTS)) {
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
colorHex,
|
|
12
12
|
fetchPeak,
|
|
13
13
|
namedHex
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-VGYBQH5I.js";
|
|
15
15
|
import {
|
|
16
16
|
PROVIDERS,
|
|
17
17
|
TOKMON_CAPABILITIES,
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
detectProviders,
|
|
23
23
|
resolveTimezone,
|
|
24
24
|
withTimeout
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-RTGMDE7U.js";
|
|
26
26
|
import {
|
|
27
27
|
cacheDir,
|
|
28
28
|
expandHome,
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
normalizeConfig,
|
|
31
31
|
saveConfig,
|
|
32
32
|
snapshotCacheFile
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-E2YXYU73.js";
|
|
34
34
|
|
|
35
35
|
// src/web/server.ts
|
|
36
36
|
import { createServer } from "http";
|
|
@@ -189,7 +189,7 @@ function deferred() {
|
|
|
189
189
|
});
|
|
190
190
|
return { promise, resolve, reject };
|
|
191
191
|
}
|
|
192
|
-
function createRefreshQueue(perform, skipAutomatic = () => false) {
|
|
192
|
+
function createRefreshQueue(perform, skipAutomatic = () => false, options = {}) {
|
|
193
193
|
let activePass = null;
|
|
194
194
|
let activeResult = null;
|
|
195
195
|
let queued = null;
|
|
@@ -226,6 +226,7 @@ function createRefreshQueue(perform, skipAutomatic = () => false) {
|
|
|
226
226
|
if (stopped || !force && skipAutomatic()) return Promise.resolve();
|
|
227
227
|
if (!activePass) return launch();
|
|
228
228
|
if (!force) return activeResult.promise;
|
|
229
|
+
if (options.forceWhileActive === "join") return activeResult.promise;
|
|
229
230
|
if (!queued) queued = deferred();
|
|
230
231
|
return queued.promise;
|
|
231
232
|
};
|
|
@@ -252,10 +253,21 @@ var IDLE_PAUSE_MS = 6e4;
|
|
|
252
253
|
var SNAPSHOT_CACHE_THROTTLE_MS = 2e4;
|
|
253
254
|
var REVEAL_THROTTLE_MS = 500;
|
|
254
255
|
var FETCH_TIMEOUT_MS = 3e4;
|
|
255
|
-
var
|
|
256
|
-
function billingNeedsCatchUp(accounts, updatedAt, now = Date.now(), maxAgeMs =
|
|
256
|
+
var DEFAULT_BILLING_STALE_MS = 5 * 6e4;
|
|
257
|
+
function billingNeedsCatchUp(accounts, updatedAt, now = Date.now(), maxAgeMs = DEFAULT_BILLING_STALE_MS) {
|
|
257
258
|
return accounts.some(({ account }) => now - (updatedAt.get(account.id) ?? 0) >= maxAgeMs);
|
|
258
259
|
}
|
|
260
|
+
async function forEachProviderSequentially(values, task) {
|
|
261
|
+
const groups = /* @__PURE__ */ new Map();
|
|
262
|
+
for (const value of values) {
|
|
263
|
+
const group = groups.get(value.account.providerId) ?? [];
|
|
264
|
+
group.push(value);
|
|
265
|
+
groups.set(value.account.providerId, group);
|
|
266
|
+
}
|
|
267
|
+
await Promise.all([...groups.values()].map(async (group) => {
|
|
268
|
+
for (const value of group) await task(value);
|
|
269
|
+
}));
|
|
270
|
+
}
|
|
259
271
|
function runInBackground(task) {
|
|
260
272
|
void task.catch(() => {
|
|
261
273
|
});
|
|
@@ -405,7 +417,14 @@ function createDataEngine(opts) {
|
|
|
405
417
|
}
|
|
406
418
|
reveal();
|
|
407
419
|
};
|
|
408
|
-
if (opts2.concurrent) {
|
|
420
|
+
if (opts2.concurrent === "provider") {
|
|
421
|
+
await forEachProviderSequentially(opts2.accounts(), async (r) => {
|
|
422
|
+
const result = await fetchOne(r);
|
|
423
|
+
if (stopped || epoch !== configEpoch) return;
|
|
424
|
+
applyResult(result);
|
|
425
|
+
});
|
|
426
|
+
if (stopped || epoch !== configEpoch) return;
|
|
427
|
+
} else if (opts2.concurrent) {
|
|
409
428
|
await Promise.all(opts2.accounts().map(async (r) => {
|
|
410
429
|
const result = await fetchOne(r);
|
|
411
430
|
if (stopped || epoch !== configEpoch) return;
|
|
@@ -423,7 +442,8 @@ function createDataEngine(opts) {
|
|
|
423
442
|
rebuild();
|
|
424
443
|
throwIfRefreshFailures(opts2.scope, failures);
|
|
425
444
|
},
|
|
426
|
-
idle
|
|
445
|
+
idle,
|
|
446
|
+
{ forceWhileActive: opts2.forceWhileActive }
|
|
427
447
|
);
|
|
428
448
|
const refreshSummary = makeRefreshLoop({
|
|
429
449
|
scope: "summary",
|
|
@@ -454,8 +474,10 @@ function createDataEngine(opts) {
|
|
|
454
474
|
},
|
|
455
475
|
state: billingState,
|
|
456
476
|
updatedAt: billingUpdatedAt,
|
|
457
|
-
concurrent:
|
|
477
|
+
concurrent: "provider",
|
|
478
|
+
forceWhileActive: "join"
|
|
458
479
|
});
|
|
480
|
+
const refreshBillingIfStale = () => billingNeedsCatchUp(billingAccounts, billingUpdatedAt, Date.now(), billingIntervalMs) ? refreshBilling.run(true) : Promise.resolve();
|
|
459
481
|
const refreshPeak = createRefreshQueue(
|
|
460
482
|
async () => {
|
|
461
483
|
if (stopped || !hasClaude) return;
|
|
@@ -549,7 +571,7 @@ function createDataEngine(opts) {
|
|
|
549
571
|
rebuild();
|
|
550
572
|
runInBackground(refreshSummary.run(true));
|
|
551
573
|
runInBackground(refreshTable.run(true));
|
|
552
|
-
runInBackground(
|
|
574
|
+
runInBackground(refreshBillingIfStale());
|
|
553
575
|
if (hasClaude) runInBackground(refreshPeak.run(true));
|
|
554
576
|
startTimers();
|
|
555
577
|
},
|
|
@@ -576,9 +598,7 @@ function createDataEngine(opts) {
|
|
|
576
598
|
runInBackground(refreshSummary.run(true));
|
|
577
599
|
runInBackground(refreshTable.run(true));
|
|
578
600
|
}
|
|
579
|
-
|
|
580
|
-
runInBackground(refreshBilling.run());
|
|
581
|
-
}
|
|
601
|
+
runInBackground(refreshBillingIfStale());
|
|
582
602
|
return () => {
|
|
583
603
|
snapshotSubscribers.delete(onSnapshot);
|
|
584
604
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
cacheDir
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-E2YXYU73.js";
|
|
5
5
|
|
|
6
6
|
// src/web/lockfile.ts
|
|
7
7
|
import { closeSync, fchmodSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from "fs";
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
envDir,
|
|
6
6
|
expandHome,
|
|
7
7
|
isValidTimezone
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-E2YXYU73.js";
|
|
9
9
|
|
|
10
10
|
// src/async.ts
|
|
11
11
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
@@ -590,15 +590,16 @@ function summarize(entries, tz) {
|
|
|
590
590
|
const todayStart = startOfDay(now, tz);
|
|
591
591
|
const weekStart = startOfWeek(now, tz);
|
|
592
592
|
const monthStart = startOfMonth(now, tz);
|
|
593
|
-
const today = { cost: 0, tokens: 0, cacheRead: 0, cacheSavings: 0 };
|
|
594
|
-
const week = { cost: 0, tokens: 0, cacheRead: 0, cacheSavings: 0 };
|
|
595
|
-
const month = { cost: 0, tokens: 0, cacheRead: 0, cacheSavings: 0 };
|
|
593
|
+
const today = { cost: 0, tokens: 0, input: 0, cacheRead: 0, cacheSavings: 0 };
|
|
594
|
+
const week = { cost: 0, tokens: 0, input: 0, cacheRead: 0, cacheSavings: 0 };
|
|
595
|
+
const month = { cost: 0, tokens: 0, input: 0, cacheRead: 0, cacheSavings: 0 };
|
|
596
596
|
const byDay = /* @__PURE__ */ new Map();
|
|
597
597
|
let oldestToday = now;
|
|
598
598
|
let hadToday = false;
|
|
599
599
|
const add = (s, e) => {
|
|
600
600
|
s.cost += e.cost;
|
|
601
601
|
s.tokens += e.input + e.output + e.cacheCreate + e.cacheRead;
|
|
602
|
+
s.input += e.input;
|
|
602
603
|
s.cacheRead += e.cacheRead;
|
|
603
604
|
s.cacheSavings += e.cacheSavings;
|
|
604
605
|
};
|
|
@@ -786,6 +787,7 @@ var ConfigSchema = Schema.Struct({
|
|
|
786
787
|
defaultFocus: Schema.Literals(["all", "last"]),
|
|
787
788
|
ascii: Schema.Literals(["auto", "on", "off"]),
|
|
788
789
|
allowNetworkAccess: Schema.Boolean,
|
|
790
|
+
resetDisplay: Schema.Literals(["relative", "absolute"]),
|
|
789
791
|
knownProviders: Schema.Array(ProviderIdSchema)
|
|
790
792
|
});
|
|
791
793
|
var ProtocolInfoSchema = Schema.Struct({
|
|
@@ -824,6 +826,7 @@ var FsListingSchema = Schema.Struct({
|
|
|
824
826
|
var UsageSummarySchema = Schema.Struct({
|
|
825
827
|
cost: Schema.Finite,
|
|
826
828
|
tokens: Schema.Finite,
|
|
829
|
+
input: Schema.Finite,
|
|
827
830
|
cacheRead: Schema.Finite,
|
|
828
831
|
cacheSavings: Schema.Finite
|
|
829
832
|
});
|
|
@@ -930,7 +933,8 @@ var WebSnapshotSchema = Schema.Struct({
|
|
|
930
933
|
label: Schema.String,
|
|
931
934
|
// The upstream clock is only normalized to a finite number; do not make
|
|
932
935
|
// the wire contract narrower than the producer's declared type.
|
|
933
|
-
minutesUntilChange: Schema.NullOr(Schema.Finite)
|
|
936
|
+
minutesUntilChange: Schema.NullOr(Schema.Finite),
|
|
937
|
+
changesAt: Schema.optionalKey(Schema.NullOr(Schema.String))
|
|
934
938
|
}))
|
|
935
939
|
});
|
|
936
940
|
var EmptyPayloadSchema = Schema.Struct({});
|
|
@@ -1017,6 +1021,18 @@ function formatResetIn(iso, now = Date.now()) {
|
|
|
1017
1021
|
const hrs = hours % 24;
|
|
1018
1022
|
return `${days}d ${hrs}h`;
|
|
1019
1023
|
}
|
|
1024
|
+
function formatResetAt(iso, display, now = Date.now(), tz) {
|
|
1025
|
+
const timestamp = Date.parse(iso);
|
|
1026
|
+
if (!Number.isFinite(timestamp)) return iso;
|
|
1027
|
+
if (display === "relative") return formatResetIn(iso, now);
|
|
1028
|
+
return new Intl.DateTimeFormat(void 0, {
|
|
1029
|
+
month: "short",
|
|
1030
|
+
day: "numeric",
|
|
1031
|
+
hour: "numeric",
|
|
1032
|
+
minute: "2-digit",
|
|
1033
|
+
timeZone: tz
|
|
1034
|
+
}).format(new Date(timestamp));
|
|
1035
|
+
}
|
|
1020
1036
|
|
|
1021
1037
|
// src/format.ts
|
|
1022
1038
|
var currency = formatCurrency;
|
|
@@ -1030,7 +1046,7 @@ function col(s, w, align = "right") {
|
|
|
1030
1046
|
const spaces = " ".repeat(w - s.length);
|
|
1031
1047
|
return align === "right" ? spaces + s : s + spaces;
|
|
1032
1048
|
}
|
|
1033
|
-
var
|
|
1049
|
+
var resetAt = formatResetAt;
|
|
1034
1050
|
|
|
1035
1051
|
// src/providers/claude/usage.ts
|
|
1036
1052
|
import { stat as fsStat, access } from "fs/promises";
|
|
@@ -1432,11 +1448,13 @@ function boolValue(value) {
|
|
|
1432
1448
|
return false;
|
|
1433
1449
|
}
|
|
1434
1450
|
function resetFrom(value) {
|
|
1435
|
-
if (typeof value === "string" && value.trim())
|
|
1451
|
+
if (typeof value === "string" && value.trim()) {
|
|
1452
|
+
const timestamp = Date.parse(value);
|
|
1453
|
+
return Number.isFinite(timestamp) ? new Date(timestamp).toISOString() : null;
|
|
1454
|
+
}
|
|
1436
1455
|
const n = numberValue(value);
|
|
1437
1456
|
if (n === void 0) return null;
|
|
1438
|
-
|
|
1439
|
-
return iso ? resetIn(iso) : null;
|
|
1457
|
+
return msToIso(Math.abs(n) < 1e10 ? n * 1e3 : n);
|
|
1440
1458
|
}
|
|
1441
1459
|
function usageMetric(label, window, primary) {
|
|
1442
1460
|
const used = numberValue(window?.utilization);
|
|
@@ -1989,8 +2007,7 @@ function resetDateMs(window) {
|
|
|
1989
2007
|
}
|
|
1990
2008
|
function resetFrom2(window) {
|
|
1991
2009
|
const ms = resetDateMs(window);
|
|
1992
|
-
|
|
1993
|
-
return iso ? resetIn(iso) : null;
|
|
2010
|
+
return ms === null ? null : msToIso(ms);
|
|
1994
2011
|
}
|
|
1995
2012
|
function windowSeconds(window) {
|
|
1996
2013
|
const seconds = numberValue(window?.limit_window_seconds ?? window?.period_seconds ?? window?.window_seconds);
|
|
@@ -2569,7 +2586,7 @@ async function cursorBillingCore(account) {
|
|
|
2569
2586
|
const rawEnd = usage.billingCycleEnd;
|
|
2570
2587
|
const endMs = timestampMs(rawEnd);
|
|
2571
2588
|
const iso = endMs === null ? null : msToIso(endMs);
|
|
2572
|
-
const resets = iso
|
|
2589
|
+
const resets = iso;
|
|
2573
2590
|
const limit = numberValue(pu.limit);
|
|
2574
2591
|
const planUsedCents = numberValue(pu.totalSpend) ?? (limit !== void 0 && numberValue(pu.remaining) !== void 0 ? limit - numberValue(pu.remaining) : void 0);
|
|
2575
2592
|
const computedPercent = limit !== void 0 && limit > 0 && planUsedCents !== void 0 ? planUsedCents / limit * 100 : void 0;
|
|
@@ -3118,7 +3135,9 @@ function redactToken(token) {
|
|
|
3118
3135
|
return token.length <= 4 ? "****" : `****${token.slice(-4)}`;
|
|
3119
3136
|
}
|
|
3120
3137
|
function resetDate(value) {
|
|
3121
|
-
|
|
3138
|
+
if (typeof value !== "string" || !value.trim()) return null;
|
|
3139
|
+
const timestamp = Date.parse(value);
|
|
3140
|
+
return Number.isFinite(timestamp) ? new Date(timestamp).toISOString() : null;
|
|
3122
3141
|
}
|
|
3123
3142
|
function boolValue2(value) {
|
|
3124
3143
|
if (typeof value === "boolean") return value;
|
|
@@ -3625,7 +3644,7 @@ function cloudCodeBucketsToMetrics(buckets, options = {}) {
|
|
|
3625
3644
|
used: Math.round((1 - clamped) * 100),
|
|
3626
3645
|
limit: 100,
|
|
3627
3646
|
format: { kind: "percent" },
|
|
3628
|
-
resetsAt: bucket.resetTime && Number.isFinite(Date.parse(bucket.resetTime)) ?
|
|
3647
|
+
resetsAt: bucket.resetTime && Number.isFinite(Date.parse(bucket.resetTime)) ? new Date(bucket.resetTime).toISOString() : null,
|
|
3629
3648
|
primary: i === 0
|
|
3630
3649
|
};
|
|
3631
3650
|
});
|
|
@@ -4556,15 +4575,17 @@ export {
|
|
|
4556
4575
|
flushDisk,
|
|
4557
4576
|
coalesceTables,
|
|
4558
4577
|
claudeConfigDirs,
|
|
4578
|
+
readJson,
|
|
4579
|
+
readClaudeIdentity,
|
|
4580
|
+
codexHomes,
|
|
4581
|
+
identityFromIdToken,
|
|
4582
|
+
formatResetAt,
|
|
4559
4583
|
currency,
|
|
4560
4584
|
tokens,
|
|
4561
4585
|
time,
|
|
4562
4586
|
shortDate,
|
|
4563
4587
|
col,
|
|
4564
|
-
|
|
4565
|
-
readClaudeIdentity,
|
|
4566
|
-
codexHomes,
|
|
4567
|
-
identityFromIdToken,
|
|
4588
|
+
resetAt,
|
|
4568
4589
|
cursorStateDb,
|
|
4569
4590
|
piSessionsDir,
|
|
4570
4591
|
opencodeDbPaths,
|