tokmon 0.2.0 → 0.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 +167 -70
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -97,11 +97,10 @@ async function parseFile(path, since) {
|
|
|
97
97
|
const ts = new Date(obj.timestamp ?? 0).getTime();
|
|
98
98
|
if (ts < since) continue;
|
|
99
99
|
const u = obj.message.usage;
|
|
100
|
-
const model = obj.message.model ?? "unknown";
|
|
101
100
|
entries.push({
|
|
102
101
|
ts,
|
|
103
|
-
model,
|
|
104
|
-
cost: costOf(model, u),
|
|
102
|
+
model: obj.message.model ?? "unknown",
|
|
103
|
+
cost: costOf(obj.message.model ?? "", u),
|
|
105
104
|
input: u.input_tokens ?? 0,
|
|
106
105
|
output: u.output_tokens ?? 0,
|
|
107
106
|
cacheCreate: u.cache_creation_input_tokens ?? 0,
|
|
@@ -152,19 +151,19 @@ function sum(entries) {
|
|
|
152
151
|
}
|
|
153
152
|
return { cost, tokens: tokens2 };
|
|
154
153
|
}
|
|
155
|
-
function
|
|
156
|
-
const
|
|
154
|
+
function groupBy(entries, keyFn) {
|
|
155
|
+
const groups = /* @__PURE__ */ new Map();
|
|
157
156
|
for (const e of entries) {
|
|
158
|
-
const
|
|
159
|
-
const arr =
|
|
157
|
+
const key = keyFn(e);
|
|
158
|
+
const arr = groups.get(key);
|
|
160
159
|
if (arr) arr.push(e);
|
|
161
|
-
else
|
|
160
|
+
else groups.set(key, [e]);
|
|
162
161
|
}
|
|
163
162
|
const rows = [];
|
|
164
|
-
for (const [
|
|
165
|
-
const models = [...new Set(
|
|
163
|
+
for (const [label, group] of groups) {
|
|
164
|
+
const models = [...new Set(group.map((e) => shortModel(e.model)))];
|
|
166
165
|
let input = 0, output = 0, cacheCreate = 0, cacheRead = 0, cost = 0;
|
|
167
|
-
for (const e of
|
|
166
|
+
for (const e of group) {
|
|
168
167
|
input += e.input;
|
|
169
168
|
output += e.output;
|
|
170
169
|
cacheCreate += e.cacheCreate;
|
|
@@ -172,7 +171,7 @@ function buildDaily(entries) {
|
|
|
172
171
|
cost += e.cost;
|
|
173
172
|
}
|
|
174
173
|
rows.push({
|
|
175
|
-
|
|
174
|
+
label,
|
|
176
175
|
models: models.sort(),
|
|
177
176
|
input,
|
|
178
177
|
output,
|
|
@@ -182,16 +181,28 @@ function buildDaily(entries) {
|
|
|
182
181
|
cost
|
|
183
182
|
});
|
|
184
183
|
}
|
|
185
|
-
return rows.sort((a, b) => a.
|
|
184
|
+
return rows.sort((a, b) => a.label.localeCompare(b.label));
|
|
185
|
+
}
|
|
186
|
+
function isoWeekLabel(ts) {
|
|
187
|
+
const d = new Date(ts);
|
|
188
|
+
const day = d.getDay();
|
|
189
|
+
const mondayOffset = day === 0 ? 6 : day - 1;
|
|
190
|
+
const monday = new Date(d);
|
|
191
|
+
monday.setDate(d.getDate() - mondayOffset);
|
|
192
|
+
return monday.toISOString().slice(0, 10);
|
|
193
|
+
}
|
|
194
|
+
function monthLabel(ts) {
|
|
195
|
+
return new Date(ts).toISOString().slice(0, 7);
|
|
186
196
|
}
|
|
187
197
|
async function fetchData() {
|
|
188
198
|
const now = Date.now();
|
|
189
199
|
const d = /* @__PURE__ */ new Date();
|
|
200
|
+
const lookback = new Date(d.getFullYear(), d.getMonth() - 6, 1).getTime();
|
|
190
201
|
const monthStart = new Date(d.getFullYear(), d.getMonth(), 1).getTime();
|
|
191
202
|
const todayStart = new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
|
|
192
203
|
const weekDay = d.getDay();
|
|
193
204
|
const weekStart = new Date(d.getFullYear(), d.getMonth(), d.getDate() - (weekDay === 0 ? 6 : weekDay - 1)).getTime();
|
|
194
|
-
const entries = await loadEntries(
|
|
205
|
+
const entries = await loadEntries(lookback);
|
|
195
206
|
const fiveHoursAgo = now - 5 * 36e5;
|
|
196
207
|
const blockEntries = entries.filter((e) => e.ts >= fiveHoursAgo);
|
|
197
208
|
let block = null;
|
|
@@ -204,12 +215,17 @@ async function fetchData() {
|
|
|
204
215
|
const percent = Math.min(100, (now - oldest) / (5 * 36e5) * 100);
|
|
205
216
|
block = { spent, projected: burnRate * 5, burnRate, percent, remaining: minutes(remainMs / 6e4) };
|
|
206
217
|
}
|
|
218
|
+
const daily = groupBy(entries, (e) => new Date(e.ts).toISOString().slice(0, 10));
|
|
219
|
+
const weekly = groupBy(entries, (e) => isoWeekLabel(e.ts));
|
|
220
|
+
const monthly = groupBy(entries, (e) => monthLabel(e.ts));
|
|
207
221
|
return {
|
|
208
222
|
today: sum(entries.filter((e) => e.ts >= todayStart)),
|
|
209
223
|
week: sum(entries.filter((e) => e.ts >= weekStart)),
|
|
210
|
-
month: sum(entries),
|
|
224
|
+
month: sum(entries.filter((e) => e.ts >= monthStart)),
|
|
211
225
|
block,
|
|
212
|
-
daily
|
|
226
|
+
daily,
|
|
227
|
+
weekly,
|
|
228
|
+
monthly
|
|
213
229
|
};
|
|
214
230
|
}
|
|
215
231
|
|
|
@@ -217,7 +233,7 @@ async function fetchData() {
|
|
|
217
233
|
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
218
234
|
import { join as join2 } from "path";
|
|
219
235
|
import { homedir as homedir2 } from "os";
|
|
220
|
-
var DEFAULTS = { interval: 2 };
|
|
236
|
+
var DEFAULTS = { interval: 2, clearScreen: true };
|
|
221
237
|
function configDir() {
|
|
222
238
|
if (process.platform === "win32") {
|
|
223
239
|
return join2(process.env.APPDATA ?? join2(homedir2(), "AppData", "Roaming"), "tokmon");
|
|
@@ -248,41 +264,56 @@ function configLocation() {
|
|
|
248
264
|
|
|
249
265
|
// src/app.tsx
|
|
250
266
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
251
|
-
var TABS = ["Dashboard", "
|
|
252
|
-
|
|
267
|
+
var TABS = ["Dashboard", "Table"];
|
|
268
|
+
var VIEWS = ["Daily", "Weekly", "Monthly"];
|
|
269
|
+
function App({ interval: cliInterval }) {
|
|
253
270
|
const [data, setData] = useState(null);
|
|
254
271
|
const [error, setError] = useState(null);
|
|
255
272
|
const [updated, setUpdated] = useState(/* @__PURE__ */ new Date());
|
|
256
273
|
const [tab, setTab] = useState(0);
|
|
274
|
+
const [view, setView] = useState(0);
|
|
257
275
|
const [scroll, setScroll] = useState(0);
|
|
258
276
|
const [showSettings, setShowSettings] = useState(false);
|
|
259
|
-
const [config, setConfig] = useState(
|
|
277
|
+
const [config, setConfig] = useState(null);
|
|
260
278
|
const [settingsCursor, setSettingsCursor] = useState(0);
|
|
261
279
|
const { stdout } = useStdout();
|
|
262
280
|
const rows = stdout?.rows ?? 24;
|
|
263
|
-
const
|
|
281
|
+
const cols = stdout?.columns ?? 80;
|
|
282
|
+
const interval2 = cliInterval ?? (config?.interval ?? 2) * 1e3;
|
|
264
283
|
useEffect(() => {
|
|
265
284
|
loadConfig().then((c) => {
|
|
266
|
-
if (
|
|
267
|
-
|
|
285
|
+
if (cliInterval) c = { ...c, interval: cliInterval / 1e3 };
|
|
286
|
+
setConfig(c);
|
|
287
|
+
if (c.clearScreen && stdout) stdout.write("\x1B[2J\x1B[H");
|
|
268
288
|
});
|
|
269
289
|
}, []);
|
|
270
290
|
const isTTY = process.stdin.isTTY === true;
|
|
291
|
+
const settingsItems = 2;
|
|
292
|
+
const cfg = config ?? { interval: 2, clearScreen: true };
|
|
271
293
|
useInput((input, key) => {
|
|
272
294
|
if (showSettings) {
|
|
273
295
|
if (key.escape || input === "s") setShowSettings(false);
|
|
274
296
|
if (key.upArrow) setSettingsCursor((c) => Math.max(0, c - 1));
|
|
275
|
-
if (key.downArrow) setSettingsCursor((c) => Math.min(
|
|
276
|
-
if (
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
297
|
+
if (key.downArrow) setSettingsCursor((c) => Math.min(settingsItems - 1, c + 1));
|
|
298
|
+
if (settingsCursor === 0) {
|
|
299
|
+
if (key.leftArrow) {
|
|
300
|
+
setConfig((c) => {
|
|
301
|
+
const next = { ...c, interval: Math.max(1, c.interval - 1) };
|
|
302
|
+
saveConfig(next);
|
|
303
|
+
return next;
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
if (key.rightArrow) {
|
|
307
|
+
setConfig((c) => {
|
|
308
|
+
const next = { ...c, interval: c.interval + 1 };
|
|
309
|
+
saveConfig(next);
|
|
310
|
+
return next;
|
|
311
|
+
});
|
|
312
|
+
}
|
|
282
313
|
}
|
|
283
|
-
if (key.
|
|
314
|
+
if (settingsCursor === 1 && (key.leftArrow || key.rightArrow || key.return)) {
|
|
284
315
|
setConfig((c) => {
|
|
285
|
-
const next = { ...c,
|
|
316
|
+
const next = { ...c, clearScreen: !c.clearScreen };
|
|
286
317
|
saveConfig(next);
|
|
287
318
|
return next;
|
|
288
319
|
});
|
|
@@ -293,24 +324,58 @@ function App({ interval: initialInterval }) {
|
|
|
293
324
|
setShowSettings(true);
|
|
294
325
|
return;
|
|
295
326
|
}
|
|
296
|
-
if (key.tab
|
|
327
|
+
if (key.tab) {
|
|
297
328
|
setTab((t) => (t + 1) % TABS.length);
|
|
298
329
|
setScroll(0);
|
|
330
|
+
return;
|
|
299
331
|
}
|
|
300
|
-
if (key.leftArrow) {
|
|
301
|
-
setTab((t) => (t - 1 + TABS.length) % TABS.length);
|
|
302
|
-
setScroll(0);
|
|
303
|
-
}
|
|
304
|
-
if (key.upArrow) setScroll((s) => Math.max(0, s - 1));
|
|
305
|
-
if (key.downArrow) setScroll((s) => s + 1);
|
|
306
332
|
if (input === "1") {
|
|
307
333
|
setTab(0);
|
|
308
334
|
setScroll(0);
|
|
335
|
+
return;
|
|
309
336
|
}
|
|
310
337
|
if (input === "2") {
|
|
311
338
|
setTab(1);
|
|
312
339
|
setScroll(0);
|
|
340
|
+
return;
|
|
313
341
|
}
|
|
342
|
+
if (tab === 1) {
|
|
343
|
+
if (input === "d") {
|
|
344
|
+
setView(0);
|
|
345
|
+
setScroll(0);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
if (input === "w") {
|
|
349
|
+
setView(1);
|
|
350
|
+
setScroll(0);
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
if (input === "m") {
|
|
354
|
+
setView(2);
|
|
355
|
+
setScroll(0);
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
if (key.leftArrow) {
|
|
359
|
+
setView((v) => (v - 1 + VIEWS.length) % VIEWS.length);
|
|
360
|
+
setScroll(0);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
if (key.rightArrow) {
|
|
364
|
+
setView((v) => (v + 1) % VIEWS.length);
|
|
365
|
+
setScroll(0);
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
} else {
|
|
369
|
+
if (key.leftArrow || key.rightArrow) {
|
|
370
|
+
setTab((t) => (t + 1) % TABS.length);
|
|
371
|
+
setScroll(0);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (key.upArrow) setScroll((s) => Math.max(0, s - 1));
|
|
376
|
+
if (key.downArrow) setScroll((s) => s + 1);
|
|
377
|
+
if (key.pageDown) setScroll((s) => s + Math.max(1, rows - 12));
|
|
378
|
+
if (key.pageUp) setScroll((s) => Math.max(0, s - Math.max(1, rows - 12)));
|
|
314
379
|
}, { isActive: isTTY });
|
|
315
380
|
useEffect(() => {
|
|
316
381
|
let active = true;
|
|
@@ -335,6 +400,7 @@ function App({ interval: initialInterval }) {
|
|
|
335
400
|
}, [interval2]);
|
|
336
401
|
if (error) return /* @__PURE__ */ jsx(Box, { padding: 1, children: /* @__PURE__ */ jsx(Text, { color: "red", children: error }) });
|
|
337
402
|
if (!data) return /* @__PURE__ */ jsx(Box, { padding: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Loading..." }) });
|
|
403
|
+
const tableData = [data.daily, data.weekly, data.monthly][view];
|
|
338
404
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
339
405
|
/* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", children: [
|
|
340
406
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
@@ -344,20 +410,24 @@ function App({ interval: initialInterval }) {
|
|
|
344
410
|
] }),
|
|
345
411
|
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
346
412
|
" \xB7 ",
|
|
347
|
-
|
|
413
|
+
cliInterval ? cliInterval / 1e3 : cfg.interval,
|
|
348
414
|
"s"
|
|
349
415
|
] })
|
|
350
416
|
] }),
|
|
351
417
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: time(updated) })
|
|
352
418
|
] }),
|
|
353
|
-
showSettings ? /* @__PURE__ */ jsx(SettingsView, { config, cursor: settingsCursor }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
419
|
+
showSettings ? /* @__PURE__ */ jsx(SettingsView, { config: cfg, cursor: settingsCursor }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
354
420
|
/* @__PURE__ */ jsxs(Box, { marginTop: 1, children: [
|
|
355
421
|
/* @__PURE__ */ jsx(TabBar, { tabs: TABS, active: tab }),
|
|
356
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " Tab
|
|
422
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " Tab s=settings" })
|
|
357
423
|
] }),
|
|
358
424
|
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
359
|
-
|
|
360
|
-
|
|
425
|
+
tab === 0 && /* @__PURE__ */ jsx(DashboardView, { data }),
|
|
426
|
+
tab === 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
427
|
+
/* @__PURE__ */ jsx(ViewBar, { views: VIEWS, active: view }),
|
|
428
|
+
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
429
|
+
/* @__PURE__ */ jsx(TableView, { rows: tableData, scroll, maxRows: rows - 12, wide: cols > 90 })
|
|
430
|
+
] })
|
|
361
431
|
] }),
|
|
362
432
|
/* @__PURE__ */ jsxs(Box, { marginTop: 1, children: [
|
|
363
433
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "by " }),
|
|
@@ -379,19 +449,26 @@ function TabBar({ tabs, active }) {
|
|
|
379
449
|
" "
|
|
380
450
|
] }) }, t)) });
|
|
381
451
|
}
|
|
452
|
+
function ViewBar({ views, active }) {
|
|
453
|
+
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
454
|
+
views.map((v, i) => /* @__PURE__ */ jsx(Box, { marginRight: 2, children: i === active ? /* @__PURE__ */ jsxs(Text, { bold: true, color: "cyan", children: [
|
|
455
|
+
"[",
|
|
456
|
+
v,
|
|
457
|
+
"]"
|
|
458
|
+
] }) : /* @__PURE__ */ jsx(Text, { dimColor: true, children: v }) }, v)),
|
|
459
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " d/w/m or \u2190\u2192" })
|
|
460
|
+
] });
|
|
461
|
+
}
|
|
382
462
|
function SettingsView({ config, cursor }) {
|
|
383
463
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
|
|
384
464
|
/* @__PURE__ */ jsx(Text, { bold: true, children: "Settings" }),
|
|
385
|
-
/* @__PURE__ */
|
|
386
|
-
"Saved to ",
|
|
387
|
-
configLocation()
|
|
388
|
-
] }),
|
|
465
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: configLocation() }),
|
|
389
466
|
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
390
467
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
391
|
-
|
|
392
|
-
"\u25B8",
|
|
468
|
+
/* @__PURE__ */ jsxs(Text, { color: cursor === 0 ? "green" : void 0, children: [
|
|
469
|
+
cursor === 0 ? "\u25B8" : " ",
|
|
393
470
|
" "
|
|
394
|
-
] })
|
|
471
|
+
] }),
|
|
395
472
|
/* @__PURE__ */ jsx(Text, { children: "Refresh interval " }),
|
|
396
473
|
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
397
474
|
"\u25C2",
|
|
@@ -404,11 +481,18 @@ function SettingsView({ config, cursor }) {
|
|
|
404
481
|
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
405
482
|
" ",
|
|
406
483
|
"\u25B8"
|
|
484
|
+
] })
|
|
485
|
+
] }),
|
|
486
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
487
|
+
/* @__PURE__ */ jsxs(Text, { color: cursor === 1 ? "green" : void 0, children: [
|
|
488
|
+
cursor === 1 ? "\u25B8" : " ",
|
|
489
|
+
" "
|
|
407
490
|
] }),
|
|
408
|
-
/* @__PURE__ */ jsx(Text, {
|
|
491
|
+
/* @__PURE__ */ jsx(Text, { children: "Clear screen " }),
|
|
492
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: config.clearScreen ? "green" : "red", children: config.clearScreen ? "on" : "off" })
|
|
409
493
|
] }),
|
|
410
494
|
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
411
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "
|
|
495
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191\u2193 select \u2190\u2192 adjust s/Esc close" })
|
|
412
496
|
] });
|
|
413
497
|
}
|
|
414
498
|
function DashboardView({ data }) {
|
|
@@ -438,10 +522,10 @@ function DashboardView({ data }) {
|
|
|
438
522
|
] }),
|
|
439
523
|
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
440
524
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(50) }),
|
|
441
|
-
/* @__PURE__ */
|
|
525
|
+
/* @__PURE__ */ jsxs(Box, { width: 50, children: [
|
|
442
526
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Total " }),
|
|
443
527
|
/* @__PURE__ */ jsx(Text, { bold: true, color: "yellowBright", children: currency(data.month.cost) })
|
|
444
|
-
] })
|
|
528
|
+
] })
|
|
445
529
|
] });
|
|
446
530
|
}
|
|
447
531
|
function BlockView({ block }) {
|
|
@@ -502,39 +586,42 @@ function ProgressBar({ percent, width = 36 }) {
|
|
|
502
586
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(width - filled) })
|
|
503
587
|
] });
|
|
504
588
|
}
|
|
505
|
-
function
|
|
506
|
-
const W = {
|
|
507
|
-
const lineW = W.
|
|
589
|
+
function TableView({ rows: allRows, scroll, maxRows, wide }) {
|
|
590
|
+
const W = wide ? { label: 10, models: 18, input: 8, output: 8, cc: 8, cr: 9, total: 9, cost: 10 } : { label: 8, models: 14, input: 7, output: 7, cc: 7, cr: 8, total: 0, cost: 9 };
|
|
591
|
+
const lineW = W.label + W.models + W.input + W.output + W.cc + W.cr + W.total + W.cost;
|
|
508
592
|
const totals = { input: 0, output: 0, cacheCreate: 0, cacheRead: 0, cost: 0 };
|
|
509
|
-
for (const r of
|
|
593
|
+
for (const r of allRows) {
|
|
510
594
|
totals.input += r.input;
|
|
511
595
|
totals.output += r.output;
|
|
512
596
|
totals.cacheCreate += r.cacheCreate;
|
|
513
597
|
totals.cacheRead += r.cacheRead;
|
|
514
598
|
totals.cost += r.cost;
|
|
515
599
|
}
|
|
516
|
-
const
|
|
517
|
-
const
|
|
600
|
+
const clampedScroll = Math.min(scroll, Math.max(0, allRows.length - maxRows));
|
|
601
|
+
const visible = allRows.slice(clampedScroll, clampedScroll + maxRows);
|
|
602
|
+
const more = allRows.length - clampedScroll - maxRows;
|
|
518
603
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
519
604
|
/* @__PURE__ */ jsxs(Text, { children: [
|
|
520
|
-
/* @__PURE__ */ jsx(Text, { bold: true, children: col("Date", W.
|
|
605
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: col("Date", W.label, "left") }),
|
|
521
606
|
/* @__PURE__ */ jsx(Text, { bold: true, children: col("Models", W.models, "left") }),
|
|
522
607
|
/* @__PURE__ */ jsx(Text, { bold: true, children: col("Input", W.input) }),
|
|
523
608
|
/* @__PURE__ */ jsx(Text, { bold: true, children: col("Output", W.output) }),
|
|
524
609
|
/* @__PURE__ */ jsx(Text, { bold: true, children: col("CchCrt", W.cc) }),
|
|
525
610
|
/* @__PURE__ */ jsx(Text, { bold: true, children: col("CchRd", W.cr) }),
|
|
611
|
+
W.total > 0 && /* @__PURE__ */ jsx(Text, { bold: true, children: col("Total", W.total) }),
|
|
526
612
|
/* @__PURE__ */ jsx(Text, { bold: true, children: col("Cost", W.cost) })
|
|
527
613
|
] }),
|
|
528
614
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(lineW) }),
|
|
529
615
|
visible.map((r) => /* @__PURE__ */ jsxs(Text, { children: [
|
|
530
|
-
/* @__PURE__ */ jsx(Text, { color: "cyan", children: col(
|
|
616
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", children: col(fmtLabel(r.label), W.label, "left") }),
|
|
531
617
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: col(r.models.join(", "), W.models, "left") }),
|
|
532
618
|
/* @__PURE__ */ jsx(Text, { children: col(tokens(r.input), W.input) }),
|
|
533
619
|
/* @__PURE__ */ jsx(Text, { children: col(tokens(r.output), W.output) }),
|
|
534
620
|
/* @__PURE__ */ jsx(Text, { children: col(tokens(r.cacheCreate), W.cc) }),
|
|
535
621
|
/* @__PURE__ */ jsx(Text, { children: col(tokens(r.cacheRead), W.cr) }),
|
|
622
|
+
W.total > 0 && /* @__PURE__ */ jsx(Text, { children: col(tokens(r.total), W.total) }),
|
|
536
623
|
/* @__PURE__ */ jsx(Text, { bold: true, color: "yellow", children: col(currency(r.cost), W.cost) })
|
|
537
|
-
] }, r.
|
|
624
|
+
] }, r.label)),
|
|
538
625
|
more > 0 && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
539
626
|
" \u2193 ",
|
|
540
627
|
more,
|
|
@@ -542,24 +629,34 @@ function DailyView({ daily, scroll, maxRows }) {
|
|
|
542
629
|
] }),
|
|
543
630
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(lineW) }),
|
|
544
631
|
/* @__PURE__ */ jsxs(Text, { children: [
|
|
545
|
-
/* @__PURE__ */ jsx(Text, { bold: true, color: "greenBright", children: col("Total", W.
|
|
632
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: "greenBright", children: col("Total", W.label, "left") }),
|
|
546
633
|
/* @__PURE__ */ jsx(Text, { children: col("", W.models, "left") }),
|
|
547
634
|
/* @__PURE__ */ jsx(Text, { bold: true, color: "yellow", children: col(tokens(totals.input), W.input) }),
|
|
548
635
|
/* @__PURE__ */ jsx(Text, { bold: true, color: "yellow", children: col(tokens(totals.output), W.output) }),
|
|
549
636
|
/* @__PURE__ */ jsx(Text, { bold: true, color: "yellow", children: col(tokens(totals.cacheCreate), W.cc) }),
|
|
550
637
|
/* @__PURE__ */ jsx(Text, { bold: true, color: "yellow", children: col(tokens(totals.cacheRead), W.cr) }),
|
|
638
|
+
W.total > 0 && /* @__PURE__ */ jsx(Text, { bold: true, color: "yellow", children: col(tokens(totals.input + totals.output + totals.cacheCreate + totals.cacheRead), W.total) }),
|
|
551
639
|
/* @__PURE__ */ jsx(Text, { bold: true, color: "yellowBright", children: col(currency(totals.cost), W.cost) })
|
|
552
640
|
] }),
|
|
553
641
|
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
554
|
-
"\u2191\u2193 scroll \xB7 ",
|
|
555
|
-
|
|
556
|
-
"
|
|
557
|
-
|
|
642
|
+
"\u2191\u2193 PgUp/Dn scroll \xB7 ",
|
|
643
|
+
allRows.length,
|
|
644
|
+
" rows \xB7 ",
|
|
645
|
+
clampedScroll + 1,
|
|
558
646
|
"-",
|
|
559
|
-
Math.min(
|
|
647
|
+
Math.min(clampedScroll + maxRows, allRows.length)
|
|
560
648
|
] }) })
|
|
561
649
|
] });
|
|
562
650
|
}
|
|
651
|
+
function fmtLabel(label) {
|
|
652
|
+
if (label.length === 10 && label[4] === "-") return shortDate(label);
|
|
653
|
+
if (label.length === 7 && label[4] === "-") {
|
|
654
|
+
const [, m] = label.split("-");
|
|
655
|
+
const months = ["", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
656
|
+
return `${months[Number(m)]} '${label.slice(2, 4)}`;
|
|
657
|
+
}
|
|
658
|
+
return shortDate(label);
|
|
659
|
+
}
|
|
563
660
|
|
|
564
661
|
// src/cli.tsx
|
|
565
662
|
import { jsx as jsx2 } from "react/jsx-runtime";
|