tokmon 0.5.3 → 0.6.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 +143 -43
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -168,6 +168,17 @@ function groupBy(entries, keyFn) {
|
|
|
168
168
|
cacheRead += e.cacheRead;
|
|
169
169
|
cost += e.cost;
|
|
170
170
|
}
|
|
171
|
+
const byModel = /* @__PURE__ */ new Map();
|
|
172
|
+
for (const e of group) {
|
|
173
|
+
const name = shortModel(e.model);
|
|
174
|
+
const m = byModel.get(name) ?? { name, input: 0, output: 0, cacheCreate: 0, cacheRead: 0, cost: 0 };
|
|
175
|
+
m.input += e.input;
|
|
176
|
+
m.output += e.output;
|
|
177
|
+
m.cacheCreate += e.cacheCreate;
|
|
178
|
+
m.cacheRead += e.cacheRead;
|
|
179
|
+
m.cost += e.cost;
|
|
180
|
+
byModel.set(name, m);
|
|
181
|
+
}
|
|
171
182
|
rows.push({
|
|
172
183
|
label,
|
|
173
184
|
models: models.sort(),
|
|
@@ -176,7 +187,8 @@ function groupBy(entries, keyFn) {
|
|
|
176
187
|
cacheCreate,
|
|
177
188
|
cacheRead,
|
|
178
189
|
total: input + output + cacheCreate + cacheRead,
|
|
179
|
-
cost
|
|
190
|
+
cost,
|
|
191
|
+
breakdown: [...byModel.values()].sort((a, b) => b.cost - a.cost)
|
|
180
192
|
});
|
|
181
193
|
}
|
|
182
194
|
return rows.sort((a, b) => a.label.localeCompare(b.label));
|
|
@@ -338,7 +350,8 @@ function App({ interval: cliInterval }) {
|
|
|
338
350
|
const [updated, setUpdated] = useState(/* @__PURE__ */ new Date());
|
|
339
351
|
const [tab, setTab] = useState(0);
|
|
340
352
|
const [view, setView] = useState(0);
|
|
341
|
-
const [
|
|
353
|
+
const [cursor, setCursor] = useState(0);
|
|
354
|
+
const [expanded, setExpanded] = useState(-1);
|
|
342
355
|
const [showSettings, setShowSettings] = useState(false);
|
|
343
356
|
const [config2, setConfig] = useState(null);
|
|
344
357
|
const [settingsCursor, setSettingsCursor] = useState(0);
|
|
@@ -459,56 +472,85 @@ function App({ interval: cliInterval }) {
|
|
|
459
472
|
}
|
|
460
473
|
if (key.tab) {
|
|
461
474
|
setTab((t) => (t + 1) % TABS.length);
|
|
462
|
-
|
|
475
|
+
setCursor(0);
|
|
476
|
+
setExpanded(-1);
|
|
463
477
|
return;
|
|
464
478
|
}
|
|
465
479
|
if (input === "1") {
|
|
466
480
|
setTab(0);
|
|
467
|
-
|
|
481
|
+
setCursor(0);
|
|
482
|
+
setExpanded(-1);
|
|
468
483
|
return;
|
|
469
484
|
}
|
|
470
485
|
if (input === "2") {
|
|
471
486
|
setTab(1);
|
|
472
|
-
|
|
487
|
+
setCursor(0);
|
|
488
|
+
setExpanded(-1);
|
|
473
489
|
return;
|
|
474
490
|
}
|
|
475
491
|
if (tab === 1) {
|
|
476
492
|
if (input === "d") {
|
|
477
493
|
setView(0);
|
|
478
|
-
|
|
494
|
+
setCursor(0);
|
|
495
|
+
setExpanded(-1);
|
|
479
496
|
return;
|
|
480
497
|
}
|
|
481
498
|
if (input === "w") {
|
|
482
499
|
setView(1);
|
|
483
|
-
|
|
500
|
+
setCursor(0);
|
|
501
|
+
setExpanded(-1);
|
|
484
502
|
return;
|
|
485
503
|
}
|
|
486
504
|
if (input === "m") {
|
|
487
505
|
setView(2);
|
|
488
|
-
|
|
506
|
+
setCursor(0);
|
|
507
|
+
setExpanded(-1);
|
|
489
508
|
return;
|
|
490
509
|
}
|
|
491
510
|
if (key.leftArrow) {
|
|
492
511
|
setView((v) => (v - 1 + VIEWS.length) % VIEWS.length);
|
|
493
|
-
|
|
512
|
+
setCursor(0);
|
|
513
|
+
setExpanded(-1);
|
|
494
514
|
return;
|
|
495
515
|
}
|
|
496
516
|
if (key.rightArrow) {
|
|
497
517
|
setView((v) => (v + 1) % VIEWS.length);
|
|
498
|
-
|
|
518
|
+
setCursor(0);
|
|
519
|
+
setExpanded(-1);
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
if (key.return) {
|
|
523
|
+
setExpanded((e) => e === cursor ? -1 : cursor);
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
if (key.escape) {
|
|
527
|
+
setExpanded(-1);
|
|
499
528
|
return;
|
|
500
529
|
}
|
|
501
530
|
} else {
|
|
502
531
|
if (key.leftArrow || key.rightArrow) {
|
|
503
532
|
setTab((t) => (t + 1) % TABS.length);
|
|
504
|
-
|
|
533
|
+
setCursor(0);
|
|
534
|
+
setExpanded(-1);
|
|
505
535
|
return;
|
|
506
536
|
}
|
|
507
537
|
}
|
|
508
|
-
if (key.upArrow)
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
538
|
+
if (key.upArrow) {
|
|
539
|
+
setCursor((c) => Math.max(0, c - 1));
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
if (key.downArrow) {
|
|
543
|
+
setCursor((c) => c + 1);
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
if (key.pageDown) {
|
|
547
|
+
setCursor((c) => c + Math.max(1, rows - 12));
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
if (key.pageUp) {
|
|
551
|
+
setCursor((c) => Math.max(0, c - Math.max(1, rows - 12)));
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
512
554
|
}, { isActive: isTTY });
|
|
513
555
|
if (error) return /* @__PURE__ */ jsx(Box, { padding: 1, children: /* @__PURE__ */ jsx(Text, { color: "red", children: error }) });
|
|
514
556
|
if (!dashboard) return /* @__PURE__ */ jsx(Box, { padding: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Loading..." }) });
|
|
@@ -538,7 +580,7 @@ function App({ interval: cliInterval }) {
|
|
|
538
580
|
tab === 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
539
581
|
/* @__PURE__ */ jsx(ViewBar, { views: VIEWS, active: view }),
|
|
540
582
|
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
541
|
-
tableLoading && !table ? /* @__PURE__ */ jsx(
|
|
583
|
+
tableLoading && !table ? /* @__PURE__ */ jsx(Spinner, { label: "Loading 6 months of history" }) : /* @__PURE__ */ jsx(TableView, { rows: tableData, cursor, expanded, maxRows: rows - 12, wide: cols > 90 })
|
|
542
584
|
] })
|
|
543
585
|
] }),
|
|
544
586
|
/* @__PURE__ */ jsxs(Box, { marginTop: 1, children: [
|
|
@@ -701,7 +743,7 @@ function SummaryRow({ label, summary }) {
|
|
|
701
743
|
] }) })
|
|
702
744
|
] });
|
|
703
745
|
}
|
|
704
|
-
function TableView({ rows: allRows,
|
|
746
|
+
function TableView({ rows: allRows, cursor, expanded, maxRows, wide }) {
|
|
705
747
|
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 };
|
|
706
748
|
const lineW = W.label + W.models + W.input + W.output + W.cc + W.cr + W.total + W.cost;
|
|
707
749
|
const totals = { input: 0, output: 0, cacheCreate: 0, cacheRead: 0, cost: 0 };
|
|
@@ -712,12 +754,15 @@ function TableView({ rows: allRows, scroll, maxRows, wide }) {
|
|
|
712
754
|
totals.cacheRead += r.cacheRead;
|
|
713
755
|
totals.cost += r.cost;
|
|
714
756
|
}
|
|
715
|
-
const
|
|
716
|
-
const
|
|
717
|
-
const
|
|
757
|
+
const clampedCursor = Math.min(cursor, allRows.length - 1);
|
|
758
|
+
const scrollStart = Math.max(0, Math.min(clampedCursor - Math.floor(maxRows / 2), allRows.length - maxRows));
|
|
759
|
+
const visible = allRows.slice(scrollStart, scrollStart + maxRows);
|
|
718
760
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
719
761
|
/* @__PURE__ */ jsxs(Text, { children: [
|
|
720
|
-
/* @__PURE__ */
|
|
762
|
+
/* @__PURE__ */ jsxs(Text, { bold: true, children: [
|
|
763
|
+
" ",
|
|
764
|
+
col("Date", W.label, "left")
|
|
765
|
+
] }),
|
|
721
766
|
/* @__PURE__ */ jsx(Text, { bold: true, children: col("Models", W.models, "left") }),
|
|
722
767
|
/* @__PURE__ */ jsx(Text, { bold: true, children: col("Input", W.input) }),
|
|
723
768
|
/* @__PURE__ */ jsx(Text, { bold: true, children: col("Output", W.output) }),
|
|
@@ -726,25 +771,34 @@ function TableView({ rows: allRows, scroll, maxRows, wide }) {
|
|
|
726
771
|
W.total > 0 && /* @__PURE__ */ jsx(Text, { bold: true, children: col("Total", W.total) }),
|
|
727
772
|
/* @__PURE__ */ jsx(Text, { bold: true, children: col("Cost", W.cost) })
|
|
728
773
|
] }),
|
|
729
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(lineW) }),
|
|
730
|
-
visible.map((r) =>
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
/* @__PURE__ */
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
774
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(lineW + 2) }),
|
|
775
|
+
visible.map((r, vi) => {
|
|
776
|
+
const idx = scrollStart + vi;
|
|
777
|
+
const selected = idx === clampedCursor;
|
|
778
|
+
const isExpanded = idx === expanded;
|
|
779
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
780
|
+
/* @__PURE__ */ jsxs(Text, { inverse: selected, children: [
|
|
781
|
+
/* @__PURE__ */ jsxs(Text, { color: selected ? void 0 : "cyan", children: [
|
|
782
|
+
selected ? "\u25B8 " : " ",
|
|
783
|
+
col(fmtLabel(r.label), W.label, "left")
|
|
784
|
+
] }),
|
|
785
|
+
/* @__PURE__ */ jsx(Text, { dimColor: !selected, children: col(r.models.join(", "), W.models, "left") }),
|
|
786
|
+
/* @__PURE__ */ jsx(Text, { children: col(tokens(r.input), W.input) }),
|
|
787
|
+
/* @__PURE__ */ jsx(Text, { children: col(tokens(r.output), W.output) }),
|
|
788
|
+
/* @__PURE__ */ jsx(Text, { children: col(tokens(r.cacheCreate), W.cc) }),
|
|
789
|
+
/* @__PURE__ */ jsx(Text, { children: col(tokens(r.cacheRead), W.cr) }),
|
|
790
|
+
W.total > 0 && /* @__PURE__ */ jsx(Text, { children: col(tokens(r.total), W.total) }),
|
|
791
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: selected ? void 0 : "yellow", children: col(currency(r.cost), W.cost) })
|
|
792
|
+
] }),
|
|
793
|
+
isExpanded && /* @__PURE__ */ jsx(RowDetail, { row: r, indent: W.label + 2 })
|
|
794
|
+
] }, r.label);
|
|
795
|
+
}),
|
|
796
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(lineW + 2) }),
|
|
746
797
|
/* @__PURE__ */ jsxs(Text, { children: [
|
|
747
|
-
/* @__PURE__ */
|
|
798
|
+
/* @__PURE__ */ jsxs(Text, { bold: true, color: "greenBright", children: [
|
|
799
|
+
" ",
|
|
800
|
+
col("Total", W.label, "left")
|
|
801
|
+
] }),
|
|
748
802
|
/* @__PURE__ */ jsx(Text, { children: col("", W.models, "left") }),
|
|
749
803
|
/* @__PURE__ */ jsx(Text, { bold: true, color: "yellow", children: col(tokens(totals.input), W.input) }),
|
|
750
804
|
/* @__PURE__ */ jsx(Text, { bold: true, color: "yellow", children: col(tokens(totals.output), W.output) }),
|
|
@@ -754,15 +808,61 @@ function TableView({ rows: allRows, scroll, maxRows, wide }) {
|
|
|
754
808
|
/* @__PURE__ */ jsx(Text, { bold: true, color: "yellowBright", children: col(currency(totals.cost), W.cost) })
|
|
755
809
|
] }),
|
|
756
810
|
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
757
|
-
"\u2191\u2193
|
|
811
|
+
"\u2191\u2193 navigate Enter=detail Esc=close \xB7 ",
|
|
758
812
|
allRows.length,
|
|
759
813
|
" rows \xB7 ",
|
|
760
|
-
|
|
761
|
-
"
|
|
762
|
-
|
|
814
|
+
clampedCursor + 1,
|
|
815
|
+
"/",
|
|
816
|
+
allRows.length
|
|
763
817
|
] }) })
|
|
764
818
|
] });
|
|
765
819
|
}
|
|
820
|
+
function RowDetail({ row, indent }) {
|
|
821
|
+
const pad = " ".repeat(indent);
|
|
822
|
+
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", paddingLeft: indent, marginY: 0, children: row.breakdown.map((m, i) => {
|
|
823
|
+
const last = i === row.breakdown.length - 1;
|
|
824
|
+
const prefix = last ? "\u2514\u2500" : "\u251C\u2500";
|
|
825
|
+
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
826
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
827
|
+
prefix,
|
|
828
|
+
" "
|
|
829
|
+
] }),
|
|
830
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: col(m.name, 16, "left") }),
|
|
831
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
832
|
+
col(tokens(m.input), 8),
|
|
833
|
+
" in "
|
|
834
|
+
] }),
|
|
835
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
836
|
+
col(tokens(m.output), 8),
|
|
837
|
+
" out "
|
|
838
|
+
] }),
|
|
839
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
840
|
+
col(tokens(m.cacheCreate), 8),
|
|
841
|
+
" cc "
|
|
842
|
+
] }),
|
|
843
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
844
|
+
col(tokens(m.cacheRead), 9),
|
|
845
|
+
" cr "
|
|
846
|
+
] }),
|
|
847
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: "yellow", children: currency(m.cost) })
|
|
848
|
+
] }, m.name);
|
|
849
|
+
}) });
|
|
850
|
+
}
|
|
851
|
+
function Spinner({ label }) {
|
|
852
|
+
const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
853
|
+
const [i, setI] = useState(0);
|
|
854
|
+
useEffect(() => {
|
|
855
|
+
const id = setInterval(() => setI((n) => (n + 1) % frames.length), 80);
|
|
856
|
+
return () => clearInterval(id);
|
|
857
|
+
}, []);
|
|
858
|
+
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
859
|
+
/* @__PURE__ */ jsxs(Text, { color: "green", children: [
|
|
860
|
+
frames[i],
|
|
861
|
+
" "
|
|
862
|
+
] }),
|
|
863
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: label })
|
|
864
|
+
] });
|
|
865
|
+
}
|
|
766
866
|
function fmtLabel(label) {
|
|
767
867
|
if (label.length === 10 && label[4] === "-") return shortDate(label);
|
|
768
868
|
if (label.length === 7 && label[4] === "-") {
|