opencode-token-tracker 1.6.1 → 1.6.3
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/bin/opencode-tokens.js +131 -100
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { join } from "path";
|
|
5
|
-
import {
|
|
2
|
+
import { closeSync, copyFileSync, existsSync, mkdirSync, openSync, readFileSync, readSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { BUILTIN_PRICING, DEFAULT_CONFIG, findModelConfigPricing, formatCost, formatTokens, getStartOfDay, getStartOfMonth, getStartOfWeek, validateConfig } from "../lib/shared.js";
|
|
6
6
|
const CONFIG_DIR = join(homedir(), ".config", "opencode");
|
|
7
7
|
const CONFIG_FILE = join(CONFIG_DIR, "token-tracker.json");
|
|
8
8
|
const LOG_FILE = join(CONFIG_DIR, "logs", "token-tracker", "tokens.jsonl");
|
|
@@ -10,15 +10,15 @@ const LOG_FILE = join(CONFIG_DIR, "logs", "token-tracker", "tokens.jsonl");
|
|
|
10
10
|
// Helpers
|
|
11
11
|
// ============================================================================
|
|
12
12
|
function padRight(str, len) {
|
|
13
|
-
return str.length >= len ? str : str
|
|
13
|
+
return str.length >= len ? str : `${str}${" ".repeat(len - str.length)}`;
|
|
14
14
|
}
|
|
15
15
|
function padLeft(str, len) {
|
|
16
|
-
return str.length >= len ? str : " ".repeat(len - str.length)
|
|
16
|
+
return str.length >= len ? str : `${" ".repeat(len - str.length)}${str}`;
|
|
17
17
|
}
|
|
18
18
|
function truncateSessionId(sessionId) {
|
|
19
19
|
if (!sessionId)
|
|
20
20
|
return "unknown";
|
|
21
|
-
return sessionId.length > 16 ? sessionId.slice(0, 14)
|
|
21
|
+
return sessionId.length > 16 ? `${sessionId.slice(0, 14)}…` : sessionId;
|
|
22
22
|
}
|
|
23
23
|
function parseArgs(args) {
|
|
24
24
|
const positional = [];
|
|
@@ -70,9 +70,6 @@ function flagValue(flags, name) {
|
|
|
70
70
|
const v = flags.get(name);
|
|
71
71
|
return typeof v === "string" ? v : undefined;
|
|
72
72
|
}
|
|
73
|
-
function flagBool(flags, name) {
|
|
74
|
-
return flags.has(name);
|
|
75
|
-
}
|
|
76
73
|
// ============================================================================
|
|
77
74
|
// Data Loading
|
|
78
75
|
// ============================================================================
|
|
@@ -178,10 +175,11 @@ function groupBy(entries, keyFn) {
|
|
|
178
175
|
const groups = new Map();
|
|
179
176
|
for (const e of entries) {
|
|
180
177
|
const key = keyFn(e);
|
|
181
|
-
|
|
182
|
-
|
|
178
|
+
let stats = groups.get(key);
|
|
179
|
+
if (!stats) {
|
|
180
|
+
stats = createEmptyStats();
|
|
181
|
+
groups.set(key, stats);
|
|
183
182
|
}
|
|
184
|
-
const stats = groups.get(key);
|
|
185
183
|
stats.input += e.input ?? 0;
|
|
186
184
|
stats.output += e.output ?? 0;
|
|
187
185
|
stats.reasoning += e.reasoning ?? 0;
|
|
@@ -277,7 +275,6 @@ function cmdStats(period, breakdown) {
|
|
|
277
275
|
since = getStartOfMonth(now);
|
|
278
276
|
title = "This Month's Usage";
|
|
279
277
|
break;
|
|
280
|
-
case "all":
|
|
281
278
|
default:
|
|
282
279
|
since = undefined;
|
|
283
280
|
title = "All-Time Usage";
|
|
@@ -337,15 +334,15 @@ function cmdPricing() {
|
|
|
337
334
|
const p = BUILTIN_PRICING[model];
|
|
338
335
|
if (!p)
|
|
339
336
|
continue;
|
|
340
|
-
const overridden = config.models
|
|
341
|
-
console.log(` ${padRight(model
|
|
337
|
+
const overridden = config.models[model] ? " *" : "";
|
|
338
|
+
console.log(` ${padRight(`${model}${overridden}`, modelWidth)} ${padLeft(`$${p.input.toString()}`, priceWidth)} ${padLeft(`$${p.output.toString()}`, priceWidth)} ${padLeft(p.cacheRead ? `$${p.cacheRead.toString()}` : "-", priceWidth)} ${padLeft(p.cacheWrite ? `$${p.cacheWrite.toString()}` : "-", priceWidth)}`);
|
|
342
339
|
}
|
|
343
340
|
console.log();
|
|
344
341
|
}
|
|
345
342
|
console.log(` Default (unknown models)`);
|
|
346
343
|
console.log(` ${"-".repeat(modelWidth + priceWidth * 4 + 12)}`);
|
|
347
|
-
const def = BUILTIN_PRICING
|
|
348
|
-
console.log(` ${padRight("_default", modelWidth)} ${padLeft(
|
|
344
|
+
const def = BUILTIN_PRICING._default;
|
|
345
|
+
console.log(` ${padRight("_default", modelWidth)} ${padLeft(`$${def.input.toString()}`, priceWidth)} ${padLeft(`$${def.output.toString()}`, priceWidth)} ${padLeft("-", priceWidth)} ${padLeft("-", priceWidth)}`);
|
|
349
346
|
console.log();
|
|
350
347
|
if (Object.keys(config.models || {}).length > 0) {
|
|
351
348
|
console.log(` * = overridden in config`);
|
|
@@ -363,10 +360,11 @@ function cmdModels() {
|
|
|
363
360
|
const model = e.model ?? "unknown";
|
|
364
361
|
const provider = e.provider ?? "unknown";
|
|
365
362
|
const key = `${model}|${provider}`;
|
|
366
|
-
|
|
367
|
-
|
|
363
|
+
let info = modelProviders.get(key);
|
|
364
|
+
if (!info) {
|
|
365
|
+
info = { provider, count: 0, lastUsed: 0 };
|
|
366
|
+
modelProviders.set(key, info);
|
|
368
367
|
}
|
|
369
|
-
const info = modelProviders.get(key);
|
|
370
368
|
info.count++;
|
|
371
369
|
info.lastUsed = Math.max(info.lastUsed, e._ts);
|
|
372
370
|
}
|
|
@@ -569,7 +567,7 @@ function cmdConfig(positional) {
|
|
|
569
567
|
return;
|
|
570
568
|
}
|
|
571
569
|
}
|
|
572
|
-
applyConfigSet(key, value
|
|
570
|
+
applyConfigSet(key, value);
|
|
573
571
|
console.log(`\n Set ${key} = ${JSON.stringify(value)}\n`);
|
|
574
572
|
return;
|
|
575
573
|
}
|
|
@@ -584,7 +582,7 @@ function cmdConfig(positional) {
|
|
|
584
582
|
console.log(`\n Unknown key: ${key}\n Available: ${Object.keys(SETTABLE_KEYS).join(", ")}\n`);
|
|
585
583
|
return;
|
|
586
584
|
}
|
|
587
|
-
applyConfigUnset(key
|
|
585
|
+
applyConfigUnset(key);
|
|
588
586
|
console.log(`\n Unset ${key} (reverted to default)\n`);
|
|
589
587
|
return;
|
|
590
588
|
}
|
|
@@ -604,7 +602,7 @@ function cmdConfig(positional) {
|
|
|
604
602
|
if (existsSync(CONFIG_FILE)) {
|
|
605
603
|
console.log(` Contents:`);
|
|
606
604
|
console.log(` ${"-".repeat(60)}`);
|
|
607
|
-
console.log(JSON.stringify(config, null, 2).split("\n").map(l =>
|
|
605
|
+
console.log(JSON.stringify(config, null, 2).split("\n").map(l => ` ${l}`).join("\n"));
|
|
608
606
|
console.log();
|
|
609
607
|
}
|
|
610
608
|
console.log(` Commands:`);
|
|
@@ -648,7 +646,7 @@ function resolveConfigKey(config, key) {
|
|
|
648
646
|
}
|
|
649
647
|
return obj[spec.path[spec.path.length - 1]] ?? spec.default;
|
|
650
648
|
}
|
|
651
|
-
function applyConfigSet(key, value
|
|
649
|
+
function applyConfigSet(key, value) {
|
|
652
650
|
const spec = SETTABLE_KEYS[key];
|
|
653
651
|
const fullConfig = loadOrInitConfig();
|
|
654
652
|
let obj = fullConfig;
|
|
@@ -660,7 +658,7 @@ function applyConfigSet(key, value, config) {
|
|
|
660
658
|
obj[spec.path[spec.path.length - 1]] = value;
|
|
661
659
|
saveConfig(fullConfig);
|
|
662
660
|
}
|
|
663
|
-
function applyConfigUnset(key
|
|
661
|
+
function applyConfigUnset(key) {
|
|
664
662
|
const spec = SETTABLE_KEYS[key];
|
|
665
663
|
const fullConfig = loadOrInitConfig();
|
|
666
664
|
let obj = fullConfig;
|
|
@@ -687,9 +685,9 @@ function saveConfig(raw) {
|
|
|
687
685
|
mkdirSync(dir, { recursive: true });
|
|
688
686
|
}
|
|
689
687
|
if (existsSync(CONFIG_FILE)) {
|
|
690
|
-
copyFileSync(CONFIG_FILE, CONFIG_FILE
|
|
688
|
+
copyFileSync(CONFIG_FILE, `${CONFIG_FILE}.bak`);
|
|
691
689
|
}
|
|
692
|
-
writeFileSync(CONFIG_FILE, JSON.stringify(raw, null, 2)
|
|
690
|
+
writeFileSync(CONFIG_FILE, `${JSON.stringify(raw, null, 2)}\n`);
|
|
693
691
|
}
|
|
694
692
|
// ============================================================================
|
|
695
693
|
// Export
|
|
@@ -738,7 +736,7 @@ function cmdExport(flags) {
|
|
|
738
736
|
e.cacheWrite ?? 0,
|
|
739
737
|
e.cost ?? 0,
|
|
740
738
|
].map(csvEscape).join(","));
|
|
741
|
-
output = [headers.join(","), ...rows].join("\n")
|
|
739
|
+
output = `${[headers.join(","), ...rows].join("\n")}\n`;
|
|
742
740
|
}
|
|
743
741
|
if (outputFile) {
|
|
744
742
|
writeFileSync(outputFile, output);
|
|
@@ -901,9 +899,74 @@ function cmdHelp() {
|
|
|
901
899
|
opencode-tokens config get toast.enabled # Check if toast is enabled
|
|
902
900
|
`);
|
|
903
901
|
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
902
|
+
function getTrendValue(point, metric) {
|
|
903
|
+
return metric === "tokens" ? point.tokens : metric === "messages" ? point.messages : point.cost;
|
|
904
|
+
}
|
|
905
|
+
function formatTrendValue(value, metric) {
|
|
906
|
+
return metric === "tokens" ? formatTokens(value) : metric === "messages" ? String(Math.round(value)) : formatCost(value);
|
|
907
|
+
}
|
|
908
|
+
function metricLabel(metric) {
|
|
909
|
+
return metric === "tokens" ? "Token Trend" : metric === "messages" ? "Message Trend" : "Cost Trend";
|
|
910
|
+
}
|
|
911
|
+
function createTrendRows(height, width, gridRows) {
|
|
912
|
+
const rows = [];
|
|
913
|
+
for (let row = 0; row < height; row++) {
|
|
914
|
+
const grid = gridRows.has(row);
|
|
915
|
+
rows.push(Array.from({ length: width }, (_, x) => grid && x % 2 === 0 ? "·" : " "));
|
|
916
|
+
}
|
|
917
|
+
return rows;
|
|
918
|
+
}
|
|
919
|
+
function drawTrendSegment(rows, from, to) {
|
|
920
|
+
const startX = Math.min(from.x, to.x);
|
|
921
|
+
const endX = Math.max(from.x, to.x);
|
|
922
|
+
const slope = to.y - from.y;
|
|
923
|
+
for (let x = startX; x <= endX; x++) {
|
|
924
|
+
const t = x === from.x ? 0 : (x - from.x) / (to.x - from.x);
|
|
925
|
+
const y = Math.round(from.y + slope * t);
|
|
926
|
+
const char = slope > 0 ? "╱" : slope < 0 ? "╲" : "─";
|
|
927
|
+
rows[y][x] = char;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
function drawTrendArea(rows, points) {
|
|
931
|
+
for (let i = 1; i < points.length; i++) {
|
|
932
|
+
const from = points[i - 1];
|
|
933
|
+
const to = points[i];
|
|
934
|
+
const startX = Math.min(from.x, to.x);
|
|
935
|
+
const endX = Math.max(from.x, to.x);
|
|
936
|
+
for (let x = startX; x <= endX; x++) {
|
|
937
|
+
const t = x === from.x ? 0 : (x - from.x) / (to.x - from.x);
|
|
938
|
+
const y = Math.round(from.y + (to.y - from.y) * t);
|
|
939
|
+
for (let fillY = 0; fillY < y; fillY++) {
|
|
940
|
+
if (rows[fillY][x] === " " || rows[fillY][x] === "·") {
|
|
941
|
+
rows[fillY][x] = "░";
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
function drawTrendPoints(rows, points) {
|
|
948
|
+
for (const point of points) {
|
|
949
|
+
rows[point.y][point.x] = "●";
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
function buildTrendXAxis(points, chartPoints, chartWidth) {
|
|
953
|
+
const labelChars = Array.from({ length: chartWidth }, () => " ");
|
|
954
|
+
const labelStep = Math.max(1, Math.ceil(points.length / 6));
|
|
955
|
+
for (let i = 0; i < chartPoints.length; i++) {
|
|
956
|
+
if (i % labelStep !== 0 && i !== chartPoints.length - 1)
|
|
957
|
+
continue;
|
|
958
|
+
const date = new Date(points[i][0]);
|
|
959
|
+
const label = `${date.getMonth() + 1}/${date.getDate()}`;
|
|
960
|
+
const start = Math.min(Math.max(0, chartPoints[i].x - Math.floor(label.length / 2)), Math.max(0, chartWidth - label.length));
|
|
961
|
+
const hasSpace = labelChars.slice(Math.max(0, start - 1), Math.min(chartWidth, start + label.length + 1)).every((c) => c === " ");
|
|
962
|
+
if (!hasSpace)
|
|
963
|
+
continue;
|
|
964
|
+
for (let j = 0; j < label.length; j++) {
|
|
965
|
+
labelChars[start + j] = label[j];
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
return labelChars.join("");
|
|
969
|
+
}
|
|
907
970
|
function cmdTrend(flags) {
|
|
908
971
|
const days = parseInt(String(flagValue(flags, "days") ?? "30"), 10);
|
|
909
972
|
const metric = flagValue(flags, "metric") ?? "cost";
|
|
@@ -914,14 +977,14 @@ function cmdTrend(flags) {
|
|
|
914
977
|
console.log(`\n (no data in period)\n`);
|
|
915
978
|
return;
|
|
916
979
|
}
|
|
917
|
-
// Aggregate by day
|
|
918
980
|
const dayMap = new Map();
|
|
919
981
|
for (const e of entries) {
|
|
920
982
|
const dayStart = getStartOfDay(new Date(e._ts));
|
|
921
|
-
|
|
922
|
-
|
|
983
|
+
let d = dayMap.get(dayStart);
|
|
984
|
+
if (!d) {
|
|
985
|
+
d = { cost: 0, tokens: 0, messages: 0 };
|
|
986
|
+
dayMap.set(dayStart, d);
|
|
923
987
|
}
|
|
924
|
-
const d = dayMap.get(dayStart);
|
|
925
988
|
d.cost += e.cost ?? 0;
|
|
926
989
|
d.tokens += (e.input ?? 0) + (e.output ?? 0) + (e.reasoning ?? 0);
|
|
927
990
|
d.messages += 1;
|
|
@@ -930,91 +993,60 @@ function cmdTrend(flags) {
|
|
|
930
993
|
if (sorted.length < 2) {
|
|
931
994
|
const only = sorted[0];
|
|
932
995
|
if (only) {
|
|
933
|
-
const v =
|
|
996
|
+
const v = formatTrendValue(getTrendValue(only[1], metric), metric);
|
|
934
997
|
console.log(`\n ${new Date(only[0]).toISOString().slice(0, 10)}: ${v}\n`);
|
|
935
998
|
}
|
|
936
999
|
return;
|
|
937
1000
|
}
|
|
938
|
-
const values = sorted.map(([, d]) =>
|
|
1001
|
+
const values = sorted.map(([, d]) => getTrendValue(d, metric));
|
|
939
1002
|
const maxVal = Math.max(...values, 1);
|
|
1003
|
+
const minVal = Math.min(...values);
|
|
1004
|
+
const totalVal = values.reduce((sum, value) => sum + value, 0);
|
|
1005
|
+
const avgVal = totalVal / values.length;
|
|
1006
|
+
const deltaVal = values[values.length - 1] - values[0];
|
|
940
1007
|
const H = Math.max(5, Math.min(Math.floor(width / 3), 20));
|
|
941
1008
|
if (width < 35) {
|
|
942
1009
|
// Fallback: simple sparkline
|
|
943
1010
|
const chars = ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"];
|
|
944
1011
|
const spark = values.map(v => chars[Math.min(Math.floor((v / maxVal) * 7), 7)]).join("");
|
|
945
|
-
console.log(`\n ${spark}\n`);
|
|
1012
|
+
console.log(`\n ${metricLabel(metric)} ${spark}\n`);
|
|
946
1013
|
return;
|
|
947
1014
|
}
|
|
948
|
-
// Build chart
|
|
949
|
-
const cols = values.map((v) => ({ value: v, y: Math.round((v / maxVal) * (H - 1)) }));
|
|
950
1015
|
const chartWidth = Math.max(width - 12, 20);
|
|
951
|
-
const xStep = Math.max(2, Math.floor(chartWidth / sorted.length));
|
|
952
1016
|
const yLabelStep = Math.max(1, Math.floor(H / 5));
|
|
1017
|
+
const gridRows = new Set();
|
|
1018
|
+
for (let row = 0; row < H; row++) {
|
|
1019
|
+
if (row === 0 || row === H - 1 || (H - 1 - row) % yLabelStep === 0) {
|
|
1020
|
+
gridRows.add(row);
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
const chartPoints = values.map((value, i) => ({
|
|
1024
|
+
x: values.length === 1 ? Math.floor(chartWidth / 2) : Math.round((i / (values.length - 1)) * (chartWidth - 1)),
|
|
1025
|
+
y: Math.round((value / maxVal) * (H - 1)),
|
|
1026
|
+
}));
|
|
1027
|
+
const rows = createTrendRows(H, chartWidth, gridRows);
|
|
1028
|
+
drawTrendArea(rows, chartPoints);
|
|
1029
|
+
for (let i = 1; i < chartPoints.length; i++) {
|
|
1030
|
+
drawTrendSegment(rows, chartPoints[i - 1], chartPoints[i]);
|
|
1031
|
+
}
|
|
1032
|
+
drawTrendPoints(rows, chartPoints);
|
|
953
1033
|
const lines = [];
|
|
1034
|
+
lines.push(`${metricLabel(metric)} · ${sorted.length} days · peak ${formatTrendValue(maxVal, metric)} · avg ${formatTrendValue(avgVal, metric)} · Δ ${deltaVal >= 0 ? "+" : ""}${formatTrendValue(deltaVal, metric)}`);
|
|
1035
|
+
lines.push(`range ${formatTrendValue(minVal, metric)} → ${formatTrendValue(maxVal, metric)}`);
|
|
954
1036
|
for (let row = H - 1; row >= 0; row--) {
|
|
955
|
-
let line = "";
|
|
956
1037
|
const valAtRow = (row / (H - 1)) * maxVal;
|
|
957
|
-
const label =
|
|
958
|
-
?
|
|
1038
|
+
const label = gridRows.has(row)
|
|
1039
|
+
? formatTrendValue(valAtRow, metric)
|
|
959
1040
|
: "";
|
|
960
|
-
line
|
|
961
|
-
line += row === 0 ? " ┼" : " ┤";
|
|
962
|
-
for (let ci = 0; ci < cols.length; ci++) {
|
|
963
|
-
const col = cols[ci];
|
|
964
|
-
const nextY = ci < cols.length - 1 ? cols[ci + 1].y : col.y;
|
|
965
|
-
if (col.y === row) {
|
|
966
|
-
if (ci > 0) {
|
|
967
|
-
const prevY = cols[ci - 1].y;
|
|
968
|
-
if (prevY < col.y && nextY <= col.y)
|
|
969
|
-
line += "╭";
|
|
970
|
-
else if (prevY > col.y && nextY >= col.y)
|
|
971
|
-
line += "╰";
|
|
972
|
-
else if (prevY < col.y || nextY < col.y)
|
|
973
|
-
line += "╭";
|
|
974
|
-
else if (prevY > col.y || nextY > col.y)
|
|
975
|
-
line += "╰";
|
|
976
|
-
else
|
|
977
|
-
line += "─";
|
|
978
|
-
}
|
|
979
|
-
else {
|
|
980
|
-
line += nextY > col.y ? "╭" : nextY < col.y ? "╰" : "─";
|
|
981
|
-
}
|
|
982
|
-
if (xStep > 1 && ci < cols.length - 1 && nextY === row) {
|
|
983
|
-
line += "─".repeat(xStep - 1);
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
else if (ci > 0 && ci < cols.length) {
|
|
987
|
-
const prevY = cols[ci - 1].y;
|
|
988
|
-
if ((prevY < row && col.y > row) || (prevY > row && col.y < row)) {
|
|
989
|
-
line += prevY < col.y ? "╱" : "╲";
|
|
990
|
-
}
|
|
991
|
-
else {
|
|
992
|
-
line += " ".repeat(xStep > 1 && nextY !== row ? 1 : Math.min(xStep, 1));
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
}
|
|
1041
|
+
const line = `${padLeft(label, 9)}${row === 0 ? " ┼" : " ┤"}${rows[row].join("")}`;
|
|
996
1042
|
lines.push(line);
|
|
997
1043
|
}
|
|
998
|
-
|
|
999
|
-
let axis = " ".repeat(9) + " └";
|
|
1000
|
-
axis += "─".repeat(xStep * cols.length);
|
|
1044
|
+
const axis = `${" ".repeat(9)} └${"─".repeat(chartWidth)}`;
|
|
1001
1045
|
lines.push(axis);
|
|
1002
|
-
|
|
1003
|
-
const labelStep = Math.max(1, Math.ceil(sorted.length / 6));
|
|
1004
|
-
let xLabels = " ".repeat(11);
|
|
1005
|
-
for (let i = 0; i < cols.length; i++) {
|
|
1006
|
-
if (i % labelStep === 0 || i === cols.length - 1) {
|
|
1007
|
-
const d = new Date(sorted[i][0]);
|
|
1008
|
-
const ds = `${d.getMonth() + 1}/${d.getDate()}`;
|
|
1009
|
-
xLabels += ds;
|
|
1010
|
-
if (i < cols.length - 1)
|
|
1011
|
-
xLabels += " ".repeat(Math.max(1, xStep - ds.length + 1));
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
lines.push(xLabels);
|
|
1046
|
+
lines.push(`${" ".repeat(11)}${buildTrendXAxis(sorted, chartPoints, chartWidth)}`);
|
|
1015
1047
|
console.log();
|
|
1016
1048
|
for (const l of lines)
|
|
1017
|
-
console.log(
|
|
1049
|
+
console.log(` ${l}`);
|
|
1018
1050
|
console.log();
|
|
1019
1051
|
}
|
|
1020
1052
|
// ============================================================================
|
|
@@ -1050,8 +1082,7 @@ function main() {
|
|
|
1050
1082
|
}
|
|
1051
1083
|
// Default: stats
|
|
1052
1084
|
let period = "all";
|
|
1053
|
-
|
|
1054
|
-
breakdown = flagValue(parsed.flags, "by") || (parsed.flags.has("b") ? String(parsed.flags.get("b")) : undefined);
|
|
1085
|
+
const breakdown = flagValue(parsed.flags, "by") || (parsed.flags.has("b") ? String(parsed.flags.get("b")) : undefined);
|
|
1055
1086
|
for (const p of ["today", "week", "month", "all"]) {
|
|
1056
1087
|
if (parsed.positional.includes(p)) {
|
|
1057
1088
|
period = p;
|
package/package.json
CHANGED