tokmon 0.5.2 → 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 -49
- 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: [
|
|
@@ -669,12 +711,6 @@ function DashboardView({ data, billing }) {
|
|
|
669
711
|
]
|
|
670
712
|
}
|
|
671
713
|
)
|
|
672
|
-
] }),
|
|
673
|
-
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
674
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(50) }),
|
|
675
|
-
/* @__PURE__ */ jsxs(Box, { width: 50, children: [
|
|
676
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Total " }),
|
|
677
|
-
/* @__PURE__ */ jsx(Text, { bold: true, color: "yellowBright", children: currency(data.month.cost) })
|
|
678
714
|
] })
|
|
679
715
|
] });
|
|
680
716
|
}
|
|
@@ -707,7 +743,7 @@ function SummaryRow({ label, summary }) {
|
|
|
707
743
|
] }) })
|
|
708
744
|
] });
|
|
709
745
|
}
|
|
710
|
-
function TableView({ rows: allRows,
|
|
746
|
+
function TableView({ rows: allRows, cursor, expanded, maxRows, wide }) {
|
|
711
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 };
|
|
712
748
|
const lineW = W.label + W.models + W.input + W.output + W.cc + W.cr + W.total + W.cost;
|
|
713
749
|
const totals = { input: 0, output: 0, cacheCreate: 0, cacheRead: 0, cost: 0 };
|
|
@@ -718,12 +754,15 @@ function TableView({ rows: allRows, scroll, maxRows, wide }) {
|
|
|
718
754
|
totals.cacheRead += r.cacheRead;
|
|
719
755
|
totals.cost += r.cost;
|
|
720
756
|
}
|
|
721
|
-
const
|
|
722
|
-
const
|
|
723
|
-
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);
|
|
724
760
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
725
761
|
/* @__PURE__ */ jsxs(Text, { children: [
|
|
726
|
-
/* @__PURE__ */
|
|
762
|
+
/* @__PURE__ */ jsxs(Text, { bold: true, children: [
|
|
763
|
+
" ",
|
|
764
|
+
col("Date", W.label, "left")
|
|
765
|
+
] }),
|
|
727
766
|
/* @__PURE__ */ jsx(Text, { bold: true, children: col("Models", W.models, "left") }),
|
|
728
767
|
/* @__PURE__ */ jsx(Text, { bold: true, children: col("Input", W.input) }),
|
|
729
768
|
/* @__PURE__ */ jsx(Text, { bold: true, children: col("Output", W.output) }),
|
|
@@ -732,25 +771,34 @@ function TableView({ rows: allRows, scroll, maxRows, wide }) {
|
|
|
732
771
|
W.total > 0 && /* @__PURE__ */ jsx(Text, { bold: true, children: col("Total", W.total) }),
|
|
733
772
|
/* @__PURE__ */ jsx(Text, { bold: true, children: col("Cost", W.cost) })
|
|
734
773
|
] }),
|
|
735
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(lineW) }),
|
|
736
|
-
visible.map((r) =>
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
/* @__PURE__ */
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
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) }),
|
|
752
797
|
/* @__PURE__ */ jsxs(Text, { children: [
|
|
753
|
-
/* @__PURE__ */
|
|
798
|
+
/* @__PURE__ */ jsxs(Text, { bold: true, color: "greenBright", children: [
|
|
799
|
+
" ",
|
|
800
|
+
col("Total", W.label, "left")
|
|
801
|
+
] }),
|
|
754
802
|
/* @__PURE__ */ jsx(Text, { children: col("", W.models, "left") }),
|
|
755
803
|
/* @__PURE__ */ jsx(Text, { bold: true, color: "yellow", children: col(tokens(totals.input), W.input) }),
|
|
756
804
|
/* @__PURE__ */ jsx(Text, { bold: true, color: "yellow", children: col(tokens(totals.output), W.output) }),
|
|
@@ -760,15 +808,61 @@ function TableView({ rows: allRows, scroll, maxRows, wide }) {
|
|
|
760
808
|
/* @__PURE__ */ jsx(Text, { bold: true, color: "yellowBright", children: col(currency(totals.cost), W.cost) })
|
|
761
809
|
] }),
|
|
762
810
|
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
763
|
-
"\u2191\u2193
|
|
811
|
+
"\u2191\u2193 navigate Enter=detail Esc=close \xB7 ",
|
|
764
812
|
allRows.length,
|
|
765
813
|
" rows \xB7 ",
|
|
766
|
-
|
|
767
|
-
"
|
|
768
|
-
|
|
814
|
+
clampedCursor + 1,
|
|
815
|
+
"/",
|
|
816
|
+
allRows.length
|
|
769
817
|
] }) })
|
|
770
818
|
] });
|
|
771
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
|
+
}
|
|
772
866
|
function fmtLabel(label) {
|
|
773
867
|
if (label.length === 10 && label[4] === "-") return shortDate(label);
|
|
774
868
|
if (label.length === 7 && label[4] === "-") {
|