tokmon 0.28.5 → 0.28.6
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/{bootstrap-ink-RXSGXK7H.js → bootstrap-ink-PYYVQAYP.js} +25 -9
- package/dist/{chunk-NNQGZNX4.js → chunk-23NB5Z6V.js} +64 -9
- package/dist/{chunk-KLRDULRC.js → chunk-6E2OJCIQ.js} +4 -4
- package/dist/{chunk-TN6V7XSL.js → chunk-BZFDTIFV.js} +32 -10
- package/dist/{chunk-CFKUZCMB.js → chunk-DESEB73E.js} +7 -6
- package/dist/{chunk-5B3XCEMC.js → chunk-MMAKKTCU.js} +2 -1
- package/dist/{chunk-HNWEJ6MS.js → chunk-O7ORALKA.js} +2 -0
- package/dist/{chunk-6YJ5H3JT.js → chunk-RY24UHMK.js} +2 -2
- package/dist/{chunk-EEMD2PXA.js → chunk-UON47FYF.js} +11 -3
- package/dist/{cli-command-4XXKT32M.js → cli-command-MW6EBLNA.js} +17 -8
- package/dist/cli.js +6 -6
- package/dist/{config-3DGZ47F7.js → config-JEDXAKPZ.js} +1 -1
- package/dist/{daemon-D42FZQC5.js → daemon-XB6ZORTE.js} +6 -6
- package/dist/daemon-handle-4WDYZBVL.js +11 -0
- package/dist/server-LNFK5MRX.js +12 -0
- package/dist/web/assets/{Area-DSOnmPHW.js → Area-BqyvRn2x.js} +1 -1
- package/dist/web/assets/{analytics-CQcXEq3U.js → analytics-B6QwqU9g.js} +2 -2
- package/dist/web/assets/{breakdown-DLwDPWvA.js → breakdown-DYnVKScr.js} +1 -1
- package/dist/web/assets/{button-D767RAip.js → button-CnT5g-Jl.js} +1 -1
- package/dist/web/assets/{chart-CylR-9UK.js → chart-Duz9D-nH.js} +1 -1
- package/dist/web/assets/{explore-jBA0FMUo.js → explore-P2EolJrt.js} +1 -1
- package/dist/web/assets/{index-DMNfKjk7.js → index-9hkGpa7j.js} +12 -12
- package/dist/web/assets/{models-B4X_VrrZ.js → models-8B4FBxr7.js} +2 -2
- package/dist/web/assets/{overview-E1xtkiJT.js → overview-DXA_HAsw.js} +2 -2
- package/dist/web/assets/{panel-Cna_MX7C.js → panel-gxLFBb8y.js} +1 -1
- package/dist/web/assets/settings-sheet-CJJl-3Gw.js +1 -0
- package/dist/web/assets/{share-sheet-BBs3U9QV.js → share-sheet-CXdyA1ga.js} +1 -1
- package/dist/web/assets/{timeline-DAOfy1yc.js → timeline-CzC85trW.js} +1 -1
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
- package/dist/daemon-handle-RHIX5OYY.js +0 -11
- package/dist/server-BNZCKS2A.js +0 -12
- package/dist/web/assets/settings-sheet-Uba9f8CS.js +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
createDaemonRpcClient
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-RY24UHMK.js";
|
|
5
5
|
import {
|
|
6
6
|
accountIdentityText,
|
|
7
7
|
accountsByProvider,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
resolveQuotaViews,
|
|
12
12
|
severity,
|
|
13
13
|
usageFromHeadroom
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-BZFDTIFV.js";
|
|
15
15
|
import {
|
|
16
16
|
PROVIDERS,
|
|
17
17
|
PROVIDER_ORDER,
|
|
@@ -28,8 +28,8 @@ import {
|
|
|
28
28
|
time,
|
|
29
29
|
tokens,
|
|
30
30
|
withTimeout
|
|
31
|
-
} from "./chunk-
|
|
32
|
-
import "./chunk-
|
|
31
|
+
} from "./chunk-DESEB73E.js";
|
|
32
|
+
import "./chunk-UON47FYF.js";
|
|
33
33
|
import {
|
|
34
34
|
COLOR_PALETTE,
|
|
35
35
|
DEFAULTS,
|
|
@@ -54,7 +54,7 @@ import {
|
|
|
54
54
|
snapshotCacheFile,
|
|
55
55
|
themePresetOption,
|
|
56
56
|
toggleProviderSelection
|
|
57
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-O7ORALKA.js";
|
|
58
58
|
import {
|
|
59
59
|
glyphs
|
|
60
60
|
} from "./chunk-RF4GGQGM.js";
|
|
@@ -655,6 +655,12 @@ function quotaViews(stats) {
|
|
|
655
655
|
}
|
|
656
656
|
function QuotaRow({ quota, color, barW, labelW, resetW, resetDisplay, tz }) {
|
|
657
657
|
const theme = useTuiTheme();
|
|
658
|
+
if (quota.value?.kind === "money") {
|
|
659
|
+
return /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
660
|
+
/* @__PURE__ */ jsx3(Box2, { width: labelW, flexShrink: 0, children: /* @__PURE__ */ jsx3(Text2, { dimColor: true, wrap: "truncate", children: quota.label }) }),
|
|
661
|
+
/* @__PURE__ */ jsx3(Text2, { bold: true, color: theme.cost, wrap: "truncate-end", children: quota.valueText })
|
|
662
|
+
] });
|
|
663
|
+
}
|
|
658
664
|
if (quota.bounded && quota.usedPct !== null) {
|
|
659
665
|
const used = quota.usedPct;
|
|
660
666
|
const remaining = quota.remainingPct ?? 0;
|
|
@@ -1293,6 +1299,14 @@ var DESKTOP_FIXED_SETTINGS = [
|
|
|
1293
1299
|
if (isAdjustKey(input, key)) ctx.global.updateConfig((c) => ({ ...c, tray: { ...c.tray, showMenuBarText: !c.tray.showMenuBarText } }));
|
|
1294
1300
|
}
|
|
1295
1301
|
},
|
|
1302
|
+
{
|
|
1303
|
+
key: "menuBarContent",
|
|
1304
|
+
label: "Menu bar content",
|
|
1305
|
+
render: (rc) => caret(/* @__PURE__ */ jsx7(Text6, { bold: true, color: rc.theme.accent, children: rc.config.tray.menuBarValue === "todayTokens" ? "tokens today" : "usage" })),
|
|
1306
|
+
onAdjust: (input, key, ctx) => {
|
|
1307
|
+
if (isAdjustKey(input, key)) ctx.global.updateConfig((c) => ({ ...c, tray: { ...c.tray, menuBarValue: c.tray.menuBarValue === "usage" ? "todayTokens" : "usage" } }));
|
|
1308
|
+
}
|
|
1309
|
+
},
|
|
1296
1310
|
{
|
|
1297
1311
|
key: "summary",
|
|
1298
1312
|
label: "Summary",
|
|
@@ -3622,7 +3636,7 @@ function useAccountForm({ cfg, detected, updateConfig, trackedAccountRows, setSe
|
|
|
3622
3636
|
|
|
3623
3637
|
// src/app.tsx
|
|
3624
3638
|
import { Fragment as Fragment6, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3625
|
-
function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "degraded" }) {
|
|
3639
|
+
function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "degraded", degradedMessage }) {
|
|
3626
3640
|
const connected = mode === "connected" && baseUrl !== null;
|
|
3627
3641
|
const degraded = !connected;
|
|
3628
3642
|
const daemon = useDaemon(connected ? baseUrl : null);
|
|
@@ -3996,7 +4010,7 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3996
4010
|
if (webStartingRef.current) return;
|
|
3997
4011
|
webStartingRef.current = true;
|
|
3998
4012
|
try {
|
|
3999
|
-
const { startWebServer } = await import("./server-
|
|
4013
|
+
const { startWebServer } = await import("./server-LNFK5MRX.js");
|
|
4000
4014
|
const ctrl = await startWebServer({ config: cfg, log: false });
|
|
4001
4015
|
webRef.current = ctrl;
|
|
4002
4016
|
openUrl(ctrl.url);
|
|
@@ -4185,7 +4199,8 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
4185
4199
|
glyphs().warn,
|
|
4186
4200
|
" degraded ",
|
|
4187
4201
|
glyphs().middot,
|
|
4188
|
-
"
|
|
4202
|
+
" ",
|
|
4203
|
+
degradedMessage ?? "background service unavailable, running in-process"
|
|
4189
4204
|
] }),
|
|
4190
4205
|
connected && daemon.conn !== "live" && /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
|
|
4191
4206
|
glyphs().warn,
|
|
@@ -4333,7 +4348,8 @@ async function bootstrapInk({ interval, config, daemon, mode }) {
|
|
|
4333
4348
|
interval,
|
|
4334
4349
|
initialConfig: config,
|
|
4335
4350
|
baseUrl: daemon.baseUrl,
|
|
4336
|
-
mode
|
|
4351
|
+
mode,
|
|
4352
|
+
degradedMessage: daemon.issue?.message
|
|
4337
4353
|
}
|
|
4338
4354
|
) })
|
|
4339
4355
|
);
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
isAlive,
|
|
4
|
+
probeHealth,
|
|
5
|
+
readForeignLock,
|
|
3
6
|
readLock,
|
|
4
7
|
retireIncompatibleCliOwner,
|
|
5
8
|
verifyLock
|
|
6
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-MMAKKTCU.js";
|
|
7
10
|
import {
|
|
8
11
|
daemonChannelFromWire,
|
|
9
12
|
resolveDaemonChannel
|
|
10
13
|
} from "./chunk-UYPDMVW5.js";
|
|
11
14
|
import {
|
|
12
15
|
TOKMON_PROTOCOL_VERSION
|
|
13
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-UON47FYF.js";
|
|
14
17
|
|
|
15
18
|
// src/client/daemon-handle.ts
|
|
16
19
|
import { spawn } from "child_process";
|
|
@@ -51,6 +54,28 @@ async function attach(opts, protocolVersion) {
|
|
|
51
54
|
const lock = await verifyLock(readLock(opts), protocolVersion);
|
|
52
55
|
return lock ? connected(lock.url) : null;
|
|
53
56
|
}
|
|
57
|
+
async function incompatibleDesktopIssue(opts, protocolVersion, timeoutMs) {
|
|
58
|
+
const owner = readForeignLock(opts);
|
|
59
|
+
if (!owner || owner.ownerKind !== "desktop" || owner.protocolVersion === void 0 || owner.protocolVersion === protocolVersion || !isAlive(owner.pid)) return null;
|
|
60
|
+
const verified = await probeHealth(owner.url, owner.wsToken, {
|
|
61
|
+
ownerKind: "desktop",
|
|
62
|
+
channel: owner.channel,
|
|
63
|
+
protocolVersion: owner.protocolVersion,
|
|
64
|
+
...owner.version ? { version: owner.version } : {}
|
|
65
|
+
}, Math.min(500, timeoutMs));
|
|
66
|
+
if (!verified) return null;
|
|
67
|
+
const ownerIsOlder = owner.protocolVersion < protocolVersion;
|
|
68
|
+
const direction = ownerIsOlder ? "an older" : "a newer";
|
|
69
|
+
const ownerName = owner.version ? `Tokmon Desktop ${owner.version}` : "Tokmon Desktop";
|
|
70
|
+
const recovery = ownerIsOlder ? "Update and restart the desktop app, or quit it before running the CLI." : "Update the CLI, or quit the desktop app before running this version. With pnpm release-age filtering, use `pnpm --config.minimum-release-age=0 dlx tokmon@latest` once.";
|
|
71
|
+
return {
|
|
72
|
+
kind: "incompatible-desktop",
|
|
73
|
+
message: `${ownerName} is using ${direction} background service (protocol ${owner.protocolVersion}; this CLI needs ${protocolVersion}). ${recovery}`,
|
|
74
|
+
ownerVersion: owner.version ?? null,
|
|
75
|
+
ownerProtocolVersion: owner.protocolVersion,
|
|
76
|
+
clientProtocolVersion: protocolVersion
|
|
77
|
+
};
|
|
78
|
+
}
|
|
54
79
|
async function attachOrSpawn(opts = {}) {
|
|
55
80
|
const protocolVersion = TOKMON_PROTOCOL_VERSION;
|
|
56
81
|
const timeoutMs = opts.timeoutMs ?? HANDSHAKE_TIMEOUT_MS;
|
|
@@ -59,6 +84,8 @@ async function attachOrSpawn(opts = {}) {
|
|
|
59
84
|
const lockOpts = { cachePath: opts.cachePath, channel };
|
|
60
85
|
const existing = await attach(lockOpts, protocolVersion);
|
|
61
86
|
if (existing) return existing;
|
|
87
|
+
const desktopIssue = await incompatibleDesktopIssue(lockOpts, protocolVersion, timeoutMs);
|
|
88
|
+
if (desktopIssue) return degraded(desktopIssue);
|
|
62
89
|
await retireIncompatibleCliOwner(lockOpts, protocolVersion, timeoutMs);
|
|
63
90
|
const upgraded = await attach(lockOpts, protocolVersion);
|
|
64
91
|
if (upgraded) return upgraded;
|
|
@@ -79,13 +106,19 @@ async function attachOrSpawn(opts = {}) {
|
|
|
79
106
|
detached: process.platform !== "win32",
|
|
80
107
|
env
|
|
81
108
|
});
|
|
82
|
-
} catch {
|
|
83
|
-
|
|
109
|
+
} catch (error) {
|
|
110
|
+
const detail = error instanceof Error && error.message ? `: ${error.message}` : "";
|
|
111
|
+
resolve(degraded({
|
|
112
|
+
kind: "spawn-failed",
|
|
113
|
+
message: `Could not start the Tokmon background service${detail}. Check Node permissions and retry; local-only mode is active.`
|
|
114
|
+
}));
|
|
84
115
|
return;
|
|
85
116
|
}
|
|
86
117
|
let settled = false;
|
|
87
118
|
let stdout = "";
|
|
88
119
|
let pollTimer = null;
|
|
120
|
+
let startupError = null;
|
|
121
|
+
let startupExit = null;
|
|
89
122
|
const finish = (handle) => {
|
|
90
123
|
if (settled) return;
|
|
91
124
|
settled = true;
|
|
@@ -102,7 +135,23 @@ async function attachOrSpawn(opts = {}) {
|
|
|
102
135
|
return;
|
|
103
136
|
}
|
|
104
137
|
if (final) {
|
|
105
|
-
|
|
138
|
+
if (startupError) {
|
|
139
|
+
finish(degraded({
|
|
140
|
+
kind: "spawn-failed",
|
|
141
|
+
message: `Could not start the Tokmon background service: ${startupError}. Check Node permissions and retry; local-only mode is active.`
|
|
142
|
+
}));
|
|
143
|
+
} else if (startupExit) {
|
|
144
|
+
const result = startupExit.signal ? `signal ${startupExit.signal}` : `exit code ${startupExit.code ?? "unknown"}`;
|
|
145
|
+
finish(degraded({
|
|
146
|
+
kind: "startup-exit",
|
|
147
|
+
message: `The Tokmon background service exited before it was ready (${result}). Update or reinstall Tokmon, then retry; local-only mode is active.`
|
|
148
|
+
}));
|
|
149
|
+
} else {
|
|
150
|
+
finish(degraded({
|
|
151
|
+
kind: "startup-timeout",
|
|
152
|
+
message: `The Tokmon background service did not become ready within ${Math.ceil(timeoutMs / 1e3)}s. Quit any stuck Tokmon process and retry; local-only mode is active.`
|
|
153
|
+
}));
|
|
154
|
+
}
|
|
106
155
|
return;
|
|
107
156
|
}
|
|
108
157
|
if (!settled && !pollTimer) {
|
|
@@ -127,12 +176,18 @@ async function attachOrSpawn(opts = {}) {
|
|
|
127
176
|
return;
|
|
128
177
|
}
|
|
129
178
|
});
|
|
130
|
-
child.once("error", () =>
|
|
131
|
-
|
|
179
|
+
child.once("error", (error) => {
|
|
180
|
+
startupError = error.message || error.name;
|
|
181
|
+
tryAttach();
|
|
182
|
+
});
|
|
183
|
+
child.once("exit", (code, signal) => {
|
|
184
|
+
startupExit = { code, signal };
|
|
185
|
+
tryAttach();
|
|
186
|
+
});
|
|
132
187
|
});
|
|
133
188
|
}
|
|
134
|
-
function degraded() {
|
|
135
|
-
return { kind: "degraded", baseUrl: null, stop: () => {
|
|
189
|
+
function degraded(issue) {
|
|
190
|
+
return { kind: "degraded", baseUrl: null, ...issue ? { issue } : {}, stop: () => {
|
|
136
191
|
} };
|
|
137
192
|
}
|
|
138
193
|
|
|
@@ -10,13 +10,13 @@ import {
|
|
|
10
10
|
deriveQuotaViews,
|
|
11
11
|
fetchPeak,
|
|
12
12
|
namedHex
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-BZFDTIFV.js";
|
|
14
14
|
import {
|
|
15
15
|
PROVIDERS,
|
|
16
16
|
detectProviders,
|
|
17
17
|
resolveTimezone,
|
|
18
18
|
withTimeout
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-DESEB73E.js";
|
|
20
20
|
import {
|
|
21
21
|
ConfigPersistenceFailure,
|
|
22
22
|
ConfigUpdateConflictFailure,
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
TOKMON_WS_METHODS,
|
|
27
27
|
TOKMON_WS_PATH,
|
|
28
28
|
TokmonRpcGroup
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-UON47FYF.js";
|
|
30
30
|
import {
|
|
31
31
|
cacheDir,
|
|
32
32
|
expandHome,
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
normalizeConfig,
|
|
35
35
|
saveConfig,
|
|
36
36
|
snapshotCacheFile
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-O7ORALKA.js";
|
|
38
38
|
|
|
39
39
|
// src/web/server.ts
|
|
40
40
|
import { createServer } from "http";
|
|
@@ -5,13 +5,13 @@ import {
|
|
|
5
5
|
identityFromIdToken,
|
|
6
6
|
readClaudeIdentity,
|
|
7
7
|
readJson
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-DESEB73E.js";
|
|
9
9
|
import {
|
|
10
10
|
containsEmail,
|
|
11
11
|
expandHome,
|
|
12
12
|
redactEmail,
|
|
13
13
|
slugify
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-O7ORALKA.js";
|
|
15
15
|
|
|
16
16
|
// src/accounts.ts
|
|
17
17
|
import { existsSync, readdirSync, readFileSync as readFileSync2, statSync } from "fs";
|
|
@@ -281,15 +281,34 @@ function inferRole(metric) {
|
|
|
281
281
|
if (metric.modelId || !metric.primary && metric.format.kind === "percent" && !["usage", "api", "auto"].includes(label)) return "model";
|
|
282
282
|
return "other";
|
|
283
283
|
}
|
|
284
|
-
function
|
|
284
|
+
function formatMoney(value, currency) {
|
|
285
|
+
try {
|
|
286
|
+
return new Intl.NumberFormat("en-US", { style: "currency", currency }).format(value);
|
|
287
|
+
} catch {
|
|
288
|
+
return `$${value.toFixed(2)}`;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
function moneyValue(metric) {
|
|
292
|
+
if (metric.format.kind !== "dollars" || !Number.isFinite(metric.used)) return void 0;
|
|
293
|
+
const limit = metric.limit != null && Number.isFinite(metric.limit) ? metric.limit : null;
|
|
294
|
+
return {
|
|
295
|
+
kind: "money",
|
|
296
|
+
used: metric.used,
|
|
297
|
+
limit,
|
|
298
|
+
remaining: limit === null ? null : limit - metric.used,
|
|
299
|
+
currency: metric.format.currency?.trim().toUpperCase() || "USD"
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
function valueText(metric, usedPct, value) {
|
|
303
|
+
if (value) {
|
|
304
|
+
if (value.remaining === null) return formatMoney(value.used, value.currency);
|
|
305
|
+
const used = `${formatMoney(value.used, value.currency)} used`;
|
|
306
|
+
return value.remaining >= 0 ? `${used} \xB7 ${formatMoney(value.remaining, value.currency)} left` : `${used} \xB7 ${formatMoney(-value.remaining, value.currency)} over`;
|
|
307
|
+
}
|
|
285
308
|
if (usedPct !== null) return `${Math.round(usedPct)}% used`;
|
|
286
309
|
if (!Number.isFinite(metric.used)) return "\u2014";
|
|
287
310
|
if (metric.format.kind === "dollars") {
|
|
288
|
-
|
|
289
|
-
return new Intl.NumberFormat("en-US", { style: "currency", currency: metric.format.currency ?? "USD", maximumFractionDigits: 2 }).format(metric.used);
|
|
290
|
-
} catch {
|
|
291
|
-
return `$${metric.used.toFixed(2)}`;
|
|
292
|
-
}
|
|
311
|
+
return formatMoney(metric.used, metric.format.currency?.trim().toUpperCase() || "USD");
|
|
293
312
|
}
|
|
294
313
|
if (metric.format.kind === "count") {
|
|
295
314
|
const suffix = metric.format.suffix?.trim();
|
|
@@ -303,6 +322,7 @@ function deriveQuotaView(metric, sourceIndex = 0) {
|
|
|
303
322
|
const usedPct = metric.format.kind === "percent" ? metric.used : metric.limit != null && metric.limit > 0 ? metric.used / metric.limit * 100 : null;
|
|
304
323
|
const finiteUsedPct = usedPct == null || !Number.isFinite(usedPct) ? null : clampPct(usedPct);
|
|
305
324
|
const resets = metric.resetsAt ? Date.parse(metric.resetsAt) : Number.NaN;
|
|
325
|
+
const value = moneyValue(metric);
|
|
306
326
|
return {
|
|
307
327
|
key: metric.key ?? `${metric.label}:${sourceIndex}`,
|
|
308
328
|
label: metric.label.replace(/\s*(limit|usage)$/i, "").trim() || metric.label,
|
|
@@ -315,7 +335,8 @@ function deriveQuotaView(metric, sourceIndex = 0) {
|
|
|
315
335
|
primary: metric.primary === true,
|
|
316
336
|
active: metric.active === true,
|
|
317
337
|
displayOrder: sourceIndex,
|
|
318
|
-
valueText: valueText(metric, finiteUsedPct)
|
|
338
|
+
valueText: valueText(metric, finiteUsedPct, value),
|
|
339
|
+
...value ? { value } : {}
|
|
319
340
|
};
|
|
320
341
|
}
|
|
321
342
|
function deriveQuotaViews(metrics) {
|
|
@@ -425,8 +446,9 @@ function cumulativeQuotaViews(accounts) {
|
|
|
425
446
|
const remainingPct = rows.reduce((sum, quota) => sum + quota.remainingPct, 0) / rows.length;
|
|
426
447
|
const usedPct = 100 - remainingPct;
|
|
427
448
|
const resets = rows.map((quota) => quota.resetsAt).filter((value) => value !== null);
|
|
449
|
+
const { value: _value, ...firstWithoutValue } = first;
|
|
428
450
|
return {
|
|
429
|
-
...
|
|
451
|
+
...firstWithoutValue,
|
|
430
452
|
usedPct,
|
|
431
453
|
remainingPct,
|
|
432
454
|
resetsAt: resets.length > 0 ? Math.min(...resets) : null,
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
envDir,
|
|
6
6
|
expandHome,
|
|
7
7
|
isValidTimezone
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-O7ORALKA.js";
|
|
9
9
|
|
|
10
10
|
// src/async.ts
|
|
11
11
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
@@ -1706,7 +1706,6 @@ function identityFromIdToken(idToken) {
|
|
|
1706
1706
|
// src/providers/codex/billing.ts
|
|
1707
1707
|
var USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
|
|
1708
1708
|
var RESET_CREDITS_URL = "https://chatgpt.com/backend-api/wham/rate-limit-reset-credits";
|
|
1709
|
-
var CREDIT_USD_RATE = 0.04;
|
|
1710
1709
|
function chatGptPlanLabel(planType) {
|
|
1711
1710
|
if (typeof planType !== "string" || !planType.trim()) return null;
|
|
1712
1711
|
const p = planType.trim().toLowerCase();
|
|
@@ -1830,11 +1829,13 @@ function appendWindowMetrics(metrics, rl, headerPct) {
|
|
|
1830
1829
|
metrics.push(percentMetric(label, used, resetFrom2(item)));
|
|
1831
1830
|
}
|
|
1832
1831
|
}
|
|
1833
|
-
function
|
|
1832
|
+
function creditBalanceMetric(source) {
|
|
1834
1833
|
const balance = numberValue(source?.credits?.balance ?? source?.credit_balance);
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1834
|
+
return balance !== void 0 && balance >= 0 ? { key: "credits", role: "unbounded", label: "Credits", used: balance, limit: null, format: { kind: "count", suffix: "available" } } : null;
|
|
1835
|
+
}
|
|
1836
|
+
function appendCredits(metrics, source) {
|
|
1837
|
+
const metric = creditBalanceMetric(source);
|
|
1838
|
+
if (metric) metrics.push(metric);
|
|
1838
1839
|
}
|
|
1839
1840
|
async function fetchResetCredits(headers) {
|
|
1840
1841
|
try {
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from "./chunk-UYPDMVW5.js";
|
|
6
6
|
import {
|
|
7
7
|
cacheDir
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-O7ORALKA.js";
|
|
9
9
|
|
|
10
10
|
// src/web/lockfile.ts
|
|
11
11
|
import { closeSync, fchmodSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from "fs";
|
|
@@ -289,6 +289,7 @@ export {
|
|
|
289
289
|
reclaimUnhealthyLock,
|
|
290
290
|
reclaimAbandonedLock,
|
|
291
291
|
isAlive,
|
|
292
|
+
probeHealth,
|
|
292
293
|
verifyLock,
|
|
293
294
|
retireIncompatibleCliOwner
|
|
294
295
|
};
|
|
@@ -503,6 +503,7 @@ function moveProviderSelection(values, providerId, direction) {
|
|
|
503
503
|
var DEFAULT_TRAY_CONFIG = {
|
|
504
504
|
enabled: true,
|
|
505
505
|
showMenuBarText: true,
|
|
506
|
+
menuBarValue: "usage",
|
|
506
507
|
displayMetric: "smartHeadroom",
|
|
507
508
|
pollIntervalSec: 30,
|
|
508
509
|
activeTimeoutMin: 10,
|
|
@@ -821,6 +822,7 @@ function repairConfig(input) {
|
|
|
821
822
|
const tray = {
|
|
822
823
|
enabled: typeof rawTray.enabled === "boolean" ? rawTray.enabled : DEFAULT_TRAY_CONFIG.enabled,
|
|
823
824
|
showMenuBarText: typeof rawTray.showMenuBarText === "boolean" ? rawTray.showMenuBarText : DEFAULT_TRAY_CONFIG.showMenuBarText,
|
|
825
|
+
menuBarValue: rawTray.menuBarValue === "todayTokens" || rawTray.menuBarValue === "usage" ? rawTray.menuBarValue : DEFAULT_TRAY_CONFIG.menuBarValue,
|
|
824
826
|
displayMetric: rawTray.displayMetric === "tightestRemaining" || rawTray.displayMetric === "smartHeadroom" ? rawTray.displayMetric : DEFAULT_TRAY_CONFIG.displayMetric,
|
|
825
827
|
pollIntervalSec: finiteInRange(rawTray.pollIntervalSec, DEFAULT_TRAY_CONFIG.pollIntervalSec, 1, 86400),
|
|
826
828
|
activeTimeoutMin: finiteInRange(rawTray.activeTimeoutMin, DEFAULT_TRAY_CONFIG.activeTimeoutMin, 1, 1440),
|
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
TOKMON_WS_METHODS,
|
|
5
5
|
TOKMON_WS_PATH,
|
|
6
6
|
TokmonRpcGroup
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-UON47FYF.js";
|
|
8
8
|
import {
|
|
9
9
|
normalizeConfig
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-O7ORALKA.js";
|
|
11
11
|
|
|
12
12
|
// src/client/daemon-rpc-client.ts
|
|
13
13
|
import { Cause, Context, Duration, Effect, Exit, Fiber, Layer, ManagedRuntime, Schedule, Schema, Stream } from "effect";
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
DESKTOP_GRAPH_RANGES,
|
|
5
5
|
PROVIDER_IDS,
|
|
6
6
|
THEME_PRESET_IDS
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-O7ORALKA.js";
|
|
8
8
|
|
|
9
9
|
// src/rpc/contract.ts
|
|
10
10
|
import { Schema } from "effect";
|
|
@@ -12,7 +12,7 @@ import * as Rpc from "effect/unstable/rpc/Rpc";
|
|
|
12
12
|
import * as RpcGroup from "effect/unstable/rpc/RpcGroup";
|
|
13
13
|
var TOKMON_WS_PATH = "/ws";
|
|
14
14
|
var TOKMON_PROTOCOL_VERSION = 4;
|
|
15
|
-
var TOKMON_CAPABILITIES = ["config-cas", "config-revision", "allowed-hosts", "tray-config", "usage-activity", "tray-pins", "provider-pins", "desktop-disclosure", "desktop-graph-range", "provider-headroom", "canonical-identity", "appearance-v1", "theme-engine", "account-detection-v1"];
|
|
15
|
+
var TOKMON_CAPABILITIES = ["config-cas", "config-revision", "allowed-hosts", "tray-config", "usage-activity", "tray-pins", "provider-pins", "desktop-disclosure", "desktop-graph-range", "provider-headroom", "canonical-identity", "appearance-v1", "theme-engine", "account-detection-v1", "menu-bar-today-tokens"];
|
|
16
16
|
var TOKMON_WS_METHODS = {
|
|
17
17
|
getConfig: "tokmon.getConfig",
|
|
18
18
|
setConfig: "tokmon.setConfig",
|
|
@@ -41,6 +41,7 @@ var AccountSchema = Schema.Struct({
|
|
|
41
41
|
var TrayConfigSchema = Schema.Struct({
|
|
42
42
|
enabled: Schema.Boolean,
|
|
43
43
|
showMenuBarText: Schema.Boolean,
|
|
44
|
+
menuBarValue: Schema.optionalKey(Schema.Literals(["usage", "todayTokens"])),
|
|
44
45
|
displayMetric: Schema.Literals(["smartHeadroom", "tightestRemaining"]),
|
|
45
46
|
pollIntervalSec: Schema.Finite.check(Schema.isBetween({ minimum: 1, maximum: 86400 })),
|
|
46
47
|
activeTimeoutMin: Schema.Finite.check(Schema.isBetween({ minimum: 1, maximum: 1440 })),
|
|
@@ -271,7 +272,14 @@ var QuotaViewSchema = Schema.Struct({
|
|
|
271
272
|
primary: Schema.Boolean,
|
|
272
273
|
active: Schema.Boolean,
|
|
273
274
|
displayOrder: NonNegativeIntegerSchema,
|
|
274
|
-
valueText: Schema.String
|
|
275
|
+
valueText: Schema.String,
|
|
276
|
+
value: Schema.optionalKey(Schema.Struct({
|
|
277
|
+
kind: Schema.Literal("money"),
|
|
278
|
+
used: Schema.Finite,
|
|
279
|
+
limit: Schema.NullOr(Schema.Finite),
|
|
280
|
+
remaining: Schema.NullOr(Schema.Finite),
|
|
281
|
+
currency: Schema.String
|
|
282
|
+
}))
|
|
275
283
|
});
|
|
276
284
|
var HeadroomFactorSchema = Schema.Struct({
|
|
277
285
|
key: Schema.String,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
attachOrSpawn
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-23NB5Z6V.js";
|
|
5
|
+
import "./chunk-MMAKKTCU.js";
|
|
6
6
|
import "./chunk-UYPDMVW5.js";
|
|
7
7
|
import {
|
|
8
8
|
createDaemonRpcClient
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-RY24UHMK.js";
|
|
10
10
|
import {
|
|
11
11
|
PROVIDERS,
|
|
12
12
|
antigravityStateDb,
|
|
@@ -26,13 +26,13 @@ import {
|
|
|
26
26
|
startOfMonth,
|
|
27
27
|
startOfWeek,
|
|
28
28
|
withTimeout
|
|
29
|
-
} from "./chunk-
|
|
30
|
-
import "./chunk-
|
|
29
|
+
} from "./chunk-DESEB73E.js";
|
|
30
|
+
import "./chunk-UON47FYF.js";
|
|
31
31
|
import {
|
|
32
32
|
DESKTOP_GRAPH_RANGES,
|
|
33
33
|
PROVIDER_IDS,
|
|
34
34
|
configLocation
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-O7ORALKA.js";
|
|
36
36
|
|
|
37
37
|
// src/provider-locations.ts
|
|
38
38
|
import { access } from "fs/promises";
|
|
@@ -356,6 +356,7 @@ Settings:
|
|
|
356
356
|
privacy-key <char>
|
|
357
357
|
menu-bar-pins <ids|none> Comma-separated provider ids; at most 2
|
|
358
358
|
menu-bar-text <on|off>
|
|
359
|
+
menu-bar-value <usage|tokens-today>
|
|
359
360
|
summary-mode <smart|tightest>
|
|
360
361
|
expanded-providers <ids|none> Comma-separated provider ids
|
|
361
362
|
active-window <minutes> 1..1440
|
|
@@ -457,7 +458,7 @@ var delay = (ms) => new Promise((resolve) => {
|
|
|
457
458
|
});
|
|
458
459
|
async function connectDaemonConfig(timeoutMs) {
|
|
459
460
|
const handle = await attachOrSpawn({ timeoutMs });
|
|
460
|
-
if (handle.kind !== "spawned" || !handle.baseUrl) throw new Error("tokmon daemon is unavailable");
|
|
461
|
+
if (handle.kind !== "spawned" || !handle.baseUrl) throw new Error(handle.issue?.message ?? "tokmon daemon is unavailable");
|
|
461
462
|
return createDaemonRpcClient(handle.baseUrl, {
|
|
462
463
|
transport: "node",
|
|
463
464
|
reconnectAttempts: 2,
|
|
@@ -466,7 +467,7 @@ async function connectDaemonConfig(timeoutMs) {
|
|
|
466
467
|
}
|
|
467
468
|
async function fetchDaemonSnapshot(timeoutMs, refresh) {
|
|
468
469
|
const handle = await attachOrSpawn({ timeoutMs });
|
|
469
|
-
if (handle.kind !== "spawned" || !handle.baseUrl) throw new Error("tokmon daemon is unavailable");
|
|
470
|
+
if (handle.kind !== "spawned" || !handle.baseUrl) throw new Error(handle.issue?.message ?? "tokmon daemon is unavailable");
|
|
470
471
|
const deadline = Date.now() + timeoutMs;
|
|
471
472
|
if (refresh) {
|
|
472
473
|
const client = createDaemonRpcClient(handle.baseUrl, {
|
|
@@ -514,6 +515,7 @@ var CONFIG_SETTINGS = [
|
|
|
514
515
|
"privacy-key",
|
|
515
516
|
"menu-bar-pins",
|
|
516
517
|
"menu-bar-text",
|
|
518
|
+
"menu-bar-value",
|
|
517
519
|
"summary-mode",
|
|
518
520
|
"expanded-providers",
|
|
519
521
|
"active-window",
|
|
@@ -529,6 +531,7 @@ function configReport(config) {
|
|
|
529
531
|
privacyKey: config.privacyToggleKey,
|
|
530
532
|
menuBarPins: [...config.tray.pinnedProviders],
|
|
531
533
|
menuBarText: config.tray.showMenuBarText ? "on" : "off",
|
|
534
|
+
menuBarValue: config.tray.menuBarValue === "todayTokens" ? "tokens-today" : "usage",
|
|
532
535
|
summaryMode: config.tray.displayMetric === "smartHeadroom" ? "smart" : "tightest",
|
|
533
536
|
expandedProviders: [...config.desktop.expandedProviders],
|
|
534
537
|
activeWindowMinutes: config.tray.activeTimeoutMin,
|
|
@@ -545,6 +548,7 @@ function formatConfigReport(report) {
|
|
|
545
548
|
`privacy-key ${report.privacyKey}`,
|
|
546
549
|
`menu-bar-pins ${list(report.menuBarPins)}`,
|
|
547
550
|
`menu-bar-text ${report.menuBarText}`,
|
|
551
|
+
`menu-bar-value ${report.menuBarValue}`,
|
|
548
552
|
`summary-mode ${report.summaryMode}`,
|
|
549
553
|
`expanded-providers ${list(report.expandedProviders)}`,
|
|
550
554
|
`active-window ${report.activeWindowMinutes}m`,
|
|
@@ -595,6 +599,11 @@ function settingMutation(setting, value) {
|
|
|
595
599
|
const enabled2 = onOff(value, setting);
|
|
596
600
|
return { mutate: (config) => ({ ...config, tray: { ...config.tray, showMenuBarText: enabled2 } }), display: value };
|
|
597
601
|
}
|
|
602
|
+
if (setting === "menu-bar-value") {
|
|
603
|
+
if (value !== "usage" && value !== "tokens-today") throw new Error("menu-bar-value must be usage or tokens-today");
|
|
604
|
+
const menuBarValue = value === "tokens-today" ? "todayTokens" : "usage";
|
|
605
|
+
return { mutate: (config) => ({ ...config, tray: { ...config.tray, menuBarValue } }), display: value };
|
|
606
|
+
}
|
|
598
607
|
if (setting === "summary-mode") {
|
|
599
608
|
if (value !== "smart" && value !== "tightest") throw new Error("summary-mode must be smart or tightest");
|
|
600
609
|
const displayMetric = value === "smart" ? "smartHeadroom" : "tightestRemaining";
|
package/dist/cli.js
CHANGED
|
@@ -41,7 +41,7 @@ function validateServeArgs(serveArgs) {
|
|
|
41
41
|
}
|
|
42
42
|
async function main() {
|
|
43
43
|
if (subcommand && ["usage", "models", "query", "providers", "snapshot", "config"].includes(subcommand)) {
|
|
44
|
-
const { runQueryCommand } = await import("./cli-command-
|
|
44
|
+
const { runQueryCommand } = await import("./cli-command-MW6EBLNA.js");
|
|
45
45
|
try {
|
|
46
46
|
const output = await runQueryCommand(
|
|
47
47
|
subcommand,
|
|
@@ -60,14 +60,14 @@ Run tokmon ${subcommand} --help for usage.
|
|
|
60
60
|
return;
|
|
61
61
|
}
|
|
62
62
|
if (subcommand === "__daemon") {
|
|
63
|
-
const { runDaemon } = await import("./daemon-
|
|
63
|
+
const { runDaemon } = await import("./daemon-XB6ZORTE.js");
|
|
64
64
|
await runDaemon(args.slice(1), { foreground: false });
|
|
65
65
|
process.exitCode ??= 0;
|
|
66
66
|
return;
|
|
67
67
|
}
|
|
68
68
|
if (subcommand === "serve" || subcommand === "web") {
|
|
69
69
|
validateServeArgs(args.slice(1));
|
|
70
|
-
const { runDaemon } = await import("./daemon-
|
|
70
|
+
const { runDaemon } = await import("./daemon-XB6ZORTE.js");
|
|
71
71
|
await runDaemon(args.slice(1), { foreground: true });
|
|
72
72
|
process.exitCode ??= 0;
|
|
73
73
|
return;
|
|
@@ -115,9 +115,9 @@ Run tokmon ${subcommand} --help for usage.
|
|
|
115
115
|
return;
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
|
-
const { loadConfig } = await import("./config-
|
|
118
|
+
const { loadConfig } = await import("./config-JEDXAKPZ.js");
|
|
119
119
|
const { resolveGlyphs, setGlyphs } = await import("./glyphs-NKCSZLGO.js");
|
|
120
|
-
const { attachOrSpawn } = await import("./daemon-handle-
|
|
120
|
+
const { attachOrSpawn } = await import("./daemon-handle-4WDYZBVL.js");
|
|
121
121
|
const config = await loadConfig();
|
|
122
122
|
setGlyphs(resolveGlyphs({
|
|
123
123
|
flag: asciiFlag,
|
|
@@ -128,7 +128,7 @@ Run tokmon ${subcommand} --help for usage.
|
|
|
128
128
|
}));
|
|
129
129
|
const daemon = await attachOrSpawn();
|
|
130
130
|
const mode = daemon.kind === "spawned" ? "connected" : "degraded";
|
|
131
|
-
const { bootstrapInk } = await import("./bootstrap-ink-
|
|
131
|
+
const { bootstrapInk } = await import("./bootstrap-ink-PYYVQAYP.js");
|
|
132
132
|
await bootstrapInk({ interval, config, daemon, mode });
|
|
133
133
|
}
|
|
134
134
|
void main().catch((error) => {
|
|
@@ -12,25 +12,25 @@ import {
|
|
|
12
12
|
unlinkLock,
|
|
13
13
|
verifyLock,
|
|
14
14
|
writeLock
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-MMAKKTCU.js";
|
|
16
16
|
import {
|
|
17
17
|
appVersion,
|
|
18
18
|
startWebServer
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-6E2OJCIQ.js";
|
|
20
20
|
import {
|
|
21
21
|
resolveDaemonChannel
|
|
22
22
|
} from "./chunk-UYPDMVW5.js";
|
|
23
|
-
import "./chunk-
|
|
23
|
+
import "./chunk-BZFDTIFV.js";
|
|
24
24
|
import {
|
|
25
25
|
flushDisk
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-DESEB73E.js";
|
|
27
27
|
import {
|
|
28
28
|
TOKMON_CAPABILITIES,
|
|
29
29
|
TOKMON_PROTOCOL_VERSION
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-UON47FYF.js";
|
|
31
31
|
import {
|
|
32
32
|
loadConfig
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-O7ORALKA.js";
|
|
34
34
|
|
|
35
35
|
// src/web/daemon-controller.ts
|
|
36
36
|
import { randomBytes } from "crypto";
|