tokely 0.5.1 → 0.5.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/README.md +8 -3
- package/dist/cli.js +247 -12
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,22 +24,25 @@ tokely
|
|
|
24
24
|
## Usage
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
tokely [--all] [--claude] [--codex] [--cursor] [--gemini] [--opencode] [--pi] [--dark] [--format png|svg|json] [--output ./heatmap-last-year.png]
|
|
27
|
+
tokely [--all] [--today] [--claude] [--codex] [--cursor] [--gemini] [--opencode] [--openclaw] [--pi] [--dark] [--format png|svg|json] [--output ./heatmap-last-year.png]
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
By default, the CLI:
|
|
31
31
|
|
|
32
32
|
- scans all supported providers
|
|
33
|
+
- renders the top 5 providers with available data (priority: Claude Code → Codex → Cursor → Open Code → OpenClaw)
|
|
33
34
|
- writes `./heatmap-last-year.png`
|
|
34
35
|
- infers the date window as the rolling last year ending today
|
|
35
36
|
|
|
36
37
|
## Options
|
|
37
38
|
|
|
39
|
+
- `--today`: show today's usage stats in terminal
|
|
38
40
|
- `--claude`: include only Claude Code data
|
|
39
41
|
- `--codex`: include only Codex data
|
|
40
42
|
- `--cursor`: include only Cursor data
|
|
41
43
|
- `--gemini`: include only Gemini CLI data
|
|
42
44
|
- `--opencode`: include only Open Code data
|
|
45
|
+
- `--openclaw`: include only OpenClaw data
|
|
43
46
|
- `--pi`: include only Pi Coding Agent data
|
|
44
47
|
- `--all`: merge all providers into one combined graph
|
|
45
48
|
- `--dark`: render the image with the dark theme
|
|
@@ -121,6 +124,7 @@ npx tokely --dark --format svg --output ./out/heatmap-dark.svg
|
|
|
121
124
|
- Cursor: reads `cursorAuth/accessToken` and `cursorAuth/refreshToken` from `$CURSOR_STATE_DB_PATH`, `$CURSOR_CONFIG_DIR/User/globalStorage/state.vscdb`, `~/Library/Application Support/Cursor/User/globalStorage/state.vscdb` (macOS), `%APPDATA%/Cursor/User/globalStorage/state.vscdb` (Windows), or `~/.config/Cursor/User/globalStorage/state.vscdb` (Linux), then loads usage from Cursor's CSV export endpoint
|
|
122
125
|
- Gemini CLI: `$GEMINI_CONFIG_DIR/tmp/**/chats/session-*.json` or `~/.gemini/tmp/**/chats/session-*.json`
|
|
123
126
|
- Open Code: prefers `$OPENCODE_DATA_DIR/opencode.db` or `~/.local/share/opencode/opencode.db`, and falls back to `$OPENCODE_DATA_DIR/storage/message` or `~/.local/share/opencode/storage/message`
|
|
127
|
+
- OpenClaw: `$HOME/.openclaw/agents`, `$HOME/.clawdbot/agents`, `$HOME/.moltbot/agents`, or `$HOME/.moldbot/agents` (reads `*.jsonl` session transcripts)
|
|
124
128
|
- Pi Coding Agent: `$PI_CODING_AGENT_DIR/sessions` or `~/.pi/agent/sessions`
|
|
125
129
|
|
|
126
130
|
When Claude Code falls back to `stats-cache.json`, the daily input/output/cache split is reconstructed from Claude's cached model totals because the older layout does not keep per-request usage logs.
|
|
@@ -128,8 +132,9 @@ When Claude Code falls back to `history.jsonl`, those days are rendered as activ
|
|
|
128
132
|
|
|
129
133
|
## Exit behavior
|
|
130
134
|
|
|
131
|
-
-
|
|
132
|
-
-
|
|
135
|
+
- By default, renders the top 4 providers with available data.
|
|
136
|
+
- Priority order: Claude Code → Codex → Cursor → Open Code → Amp → Gemini CLI → Pi Coding Agent.
|
|
137
|
+
- Use `--all` to merge all providers, or specify providers explicitly.
|
|
133
138
|
- Pi Coding Agent usage is derived from assistant messages in Pi session logs, grouped by the model that handled each turn.
|
|
134
139
|
- If provider flags are passed and a requested provider has no data, the command exits with an error.
|
|
135
140
|
- If no provider has data, the command exits with an error.
|
package/dist/cli.js
CHANGED
|
@@ -5833,6 +5833,35 @@ var heatmapThemes = {
|
|
|
5833
5833
|
]
|
|
5834
5834
|
}
|
|
5835
5835
|
},
|
|
5836
|
+
openclaw: {
|
|
5837
|
+
title: "OpenClaw",
|
|
5838
|
+
colors: {
|
|
5839
|
+
light: [
|
|
5840
|
+
"#fef2f2",
|
|
5841
|
+
// red-50
|
|
5842
|
+
"#fecaca",
|
|
5843
|
+
// red-200
|
|
5844
|
+
"#fca5a5",
|
|
5845
|
+
// red-300
|
|
5846
|
+
"#ef4444",
|
|
5847
|
+
// red-500
|
|
5848
|
+
"#991b1b"
|
|
5849
|
+
// red-800
|
|
5850
|
+
],
|
|
5851
|
+
dark: [
|
|
5852
|
+
"#450a0a",
|
|
5853
|
+
// red-950
|
|
5854
|
+
"#991b1b",
|
|
5855
|
+
// red-800
|
|
5856
|
+
"#dc2626",
|
|
5857
|
+
// red-600
|
|
5858
|
+
"#fca5a5",
|
|
5859
|
+
// red-300
|
|
5860
|
+
"#fecaca"
|
|
5861
|
+
// red-200
|
|
5862
|
+
]
|
|
5863
|
+
}
|
|
5864
|
+
},
|
|
5836
5865
|
pi: {
|
|
5837
5866
|
title: "Pi Coding Agent",
|
|
5838
5867
|
colors: {
|
|
@@ -5863,7 +5892,7 @@ var heatmapThemes = {
|
|
|
5863
5892
|
}
|
|
5864
5893
|
},
|
|
5865
5894
|
all: {
|
|
5866
|
-
title: "Amp / Claude Code / Codex / Cursor / Gemini CLI / Open Code / Pi Coding Agent",
|
|
5895
|
+
title: "Amp / Claude Code / Codex / Cursor / Gemini CLI / Open Code / OpenClaw / Pi Coding Agent",
|
|
5867
5896
|
titleCaption: "Total usage from",
|
|
5868
5897
|
colors: {
|
|
5869
5898
|
light: [
|
|
@@ -7874,6 +7903,7 @@ var providerIds = [
|
|
|
7874
7903
|
"codex",
|
|
7875
7904
|
"cursor",
|
|
7876
7905
|
"opencode",
|
|
7906
|
+
"openclaw",
|
|
7877
7907
|
"amp",
|
|
7878
7908
|
"gemini",
|
|
7879
7909
|
"pi"
|
|
@@ -7882,7 +7912,8 @@ var defaultProviderIds = [
|
|
|
7882
7912
|
"claude",
|
|
7883
7913
|
"codex",
|
|
7884
7914
|
"cursor",
|
|
7885
|
-
"opencode"
|
|
7915
|
+
"opencode",
|
|
7916
|
+
"openclaw"
|
|
7886
7917
|
];
|
|
7887
7918
|
var providerStatusLabel = {
|
|
7888
7919
|
amp: "Amp",
|
|
@@ -7891,6 +7922,7 @@ var providerStatusLabel = {
|
|
|
7891
7922
|
cursor: "Cursor",
|
|
7892
7923
|
gemini: "Gemini CLI",
|
|
7893
7924
|
opencode: "Open Code",
|
|
7925
|
+
openclaw: "OpenClaw",
|
|
7894
7926
|
pi: "Pi Coding Agent"
|
|
7895
7927
|
};
|
|
7896
7928
|
|
|
@@ -8088,21 +8120,149 @@ async function loadOpenCodeRows(start, end) {
|
|
|
8088
8120
|
);
|
|
8089
8121
|
}
|
|
8090
8122
|
|
|
8091
|
-
// src/lib/
|
|
8092
|
-
import { existsSync as existsSync7 } from "fs";
|
|
8123
|
+
// src/lib/openclaw.ts
|
|
8124
|
+
import { existsSync as existsSync7, readFileSync, statSync } from "fs";
|
|
8125
|
+
import { readdirSync as readdirSync2 } from "fs";
|
|
8126
|
+
import { join as join8 } from "path";
|
|
8093
8127
|
import { homedir as homedir7 } from "os";
|
|
8094
|
-
|
|
8128
|
+
function scanOpenClawJsonlFiles() {
|
|
8129
|
+
const dirs = [
|
|
8130
|
+
join8(homedir7(), ".openclaw", "agents"),
|
|
8131
|
+
join8(homedir7(), ".clawdbot", "agents"),
|
|
8132
|
+
join8(homedir7(), ".moltbot", "agents"),
|
|
8133
|
+
join8(homedir7(), ".moldbot", "agents")
|
|
8134
|
+
];
|
|
8135
|
+
const files = [];
|
|
8136
|
+
for (const dir of dirs) {
|
|
8137
|
+
if (!existsSync7(dir)) continue;
|
|
8138
|
+
scanDirRecursive(dir, files);
|
|
8139
|
+
}
|
|
8140
|
+
return files;
|
|
8141
|
+
}
|
|
8142
|
+
function scanDirRecursive(dir, files) {
|
|
8143
|
+
try {
|
|
8144
|
+
const entries = readdirSync2(dir, { withFileTypes: true });
|
|
8145
|
+
for (const entry of entries) {
|
|
8146
|
+
const fullPath = join8(dir, entry.name);
|
|
8147
|
+
if (entry.isDirectory()) {
|
|
8148
|
+
scanDirRecursive(fullPath, files);
|
|
8149
|
+
} else if (entry.name.endsWith(".jsonl")) {
|
|
8150
|
+
files.push(fullPath);
|
|
8151
|
+
}
|
|
8152
|
+
}
|
|
8153
|
+
} catch {
|
|
8154
|
+
}
|
|
8155
|
+
}
|
|
8156
|
+
function parseTranscript(filePath, start, end, totals, modelTotals, recentModelTotals) {
|
|
8157
|
+
const content = readFileSync(filePath, "utf-8");
|
|
8158
|
+
const lines = content.split("\n").filter((l) => l.trim());
|
|
8159
|
+
let currentModel = null;
|
|
8160
|
+
let currentProvider = null;
|
|
8161
|
+
const fileMtime = statSync(filePath).mtimeMs;
|
|
8162
|
+
const recentStart = getRecentWindowStart(end);
|
|
8163
|
+
for (const line of lines) {
|
|
8164
|
+
let entry;
|
|
8165
|
+
try {
|
|
8166
|
+
entry = JSON.parse(line);
|
|
8167
|
+
} catch {
|
|
8168
|
+
continue;
|
|
8169
|
+
}
|
|
8170
|
+
const type = entry.type;
|
|
8171
|
+
if (type === "model_change") {
|
|
8172
|
+
currentModel = entry.modelId ?? currentModel;
|
|
8173
|
+
currentProvider = entry.provider ?? currentProvider;
|
|
8174
|
+
continue;
|
|
8175
|
+
}
|
|
8176
|
+
if (type === "custom" && entry.customType === "model-snapshot") {
|
|
8177
|
+
const data = entry.data;
|
|
8178
|
+
if (data) {
|
|
8179
|
+
currentModel = data.modelId ?? currentModel;
|
|
8180
|
+
currentProvider = data.provider ?? currentProvider;
|
|
8181
|
+
}
|
|
8182
|
+
continue;
|
|
8183
|
+
}
|
|
8184
|
+
if (type === "message") {
|
|
8185
|
+
const msg = entry.message;
|
|
8186
|
+
if (!msg || msg.role !== "assistant") continue;
|
|
8187
|
+
const model = msg.model ?? currentModel;
|
|
8188
|
+
if (!model) continue;
|
|
8189
|
+
const usage = msg.usage;
|
|
8190
|
+
if (!usage) continue;
|
|
8191
|
+
const input = Math.max(usage.input ?? 0, 0);
|
|
8192
|
+
const output = Math.max(usage.output ?? 0, 0);
|
|
8193
|
+
const cacheRead = Math.max(usage.cacheRead ?? 0, 0);
|
|
8194
|
+
const cacheWrite = Math.max(usage.cacheWrite ?? 0, 0);
|
|
8195
|
+
const total = Math.max(usage.totalTokens ?? 0, 0);
|
|
8196
|
+
if (total <= 0 && input <= 0 && output <= 0) continue;
|
|
8197
|
+
const ts = msg.timestamp ?? fileMtime;
|
|
8198
|
+
const timestamp = new Date(ts);
|
|
8199
|
+
if (timestamp < start || timestamp > end) continue;
|
|
8200
|
+
const normalizedModel = normalizeModelName(model);
|
|
8201
|
+
addDailyTokenTotals(
|
|
8202
|
+
totals,
|
|
8203
|
+
timestamp,
|
|
8204
|
+
{
|
|
8205
|
+
input,
|
|
8206
|
+
output,
|
|
8207
|
+
cache: { input: cacheRead, output: cacheWrite },
|
|
8208
|
+
total
|
|
8209
|
+
},
|
|
8210
|
+
normalizedModel
|
|
8211
|
+
);
|
|
8212
|
+
addModelTokenTotals(modelTotals, normalizedModel, {
|
|
8213
|
+
input,
|
|
8214
|
+
output,
|
|
8215
|
+
cache: { input: cacheRead, output: cacheWrite },
|
|
8216
|
+
total
|
|
8217
|
+
});
|
|
8218
|
+
if (timestamp >= recentStart) {
|
|
8219
|
+
addModelTokenTotals(recentModelTotals, normalizedModel, {
|
|
8220
|
+
input,
|
|
8221
|
+
output,
|
|
8222
|
+
cache: { input: cacheRead, output: cacheWrite },
|
|
8223
|
+
total
|
|
8224
|
+
});
|
|
8225
|
+
}
|
|
8226
|
+
}
|
|
8227
|
+
}
|
|
8228
|
+
}
|
|
8229
|
+
function isOpenClawAvailable() {
|
|
8230
|
+
return scanOpenClawJsonlFiles().length > 0;
|
|
8231
|
+
}
|
|
8232
|
+
async function loadOpenClawRows(start, end) {
|
|
8233
|
+
const files = scanOpenClawJsonlFiles();
|
|
8234
|
+
const totals = /* @__PURE__ */ new Map();
|
|
8235
|
+
const modelTotals = /* @__PURE__ */ new Map();
|
|
8236
|
+
const recentModelTotals = /* @__PURE__ */ new Map();
|
|
8237
|
+
for (const file of files) {
|
|
8238
|
+
try {
|
|
8239
|
+
parseTranscript(file, start, end, totals, modelTotals, recentModelTotals);
|
|
8240
|
+
} catch {
|
|
8241
|
+
}
|
|
8242
|
+
}
|
|
8243
|
+
const daily = totalsToRows(totals);
|
|
8244
|
+
return {
|
|
8245
|
+
provider: "openclaw",
|
|
8246
|
+
daily,
|
|
8247
|
+
insights: getProviderInsights(modelTotals, recentModelTotals, daily, end)
|
|
8248
|
+
};
|
|
8249
|
+
}
|
|
8250
|
+
|
|
8251
|
+
// src/lib/pi.ts
|
|
8252
|
+
import { existsSync as existsSync8 } from "fs";
|
|
8253
|
+
import { homedir as homedir8 } from "os";
|
|
8254
|
+
import { join as join9, resolve as resolve7 } from "path";
|
|
8095
8255
|
var PI_AGENT_DIR_ENV = "PI_CODING_AGENT_DIR";
|
|
8096
8256
|
var CLASSIFICATION_PREFIX_BYTES2 = 16 * 1024;
|
|
8097
8257
|
function getPiAgentDir() {
|
|
8098
8258
|
const configuredAgentDir = process.env[PI_AGENT_DIR_ENV]?.trim();
|
|
8099
|
-
return configuredAgentDir ? resolve7(configuredAgentDir) :
|
|
8259
|
+
return configuredAgentDir ? resolve7(configuredAgentDir) : join9(homedir8(), ".pi", "agent");
|
|
8100
8260
|
}
|
|
8101
8261
|
async function getPiSessionFiles() {
|
|
8102
|
-
return listFilesRecursive(
|
|
8262
|
+
return listFilesRecursive(join9(getPiAgentDir(), "sessions"), ".jsonl");
|
|
8103
8263
|
}
|
|
8104
8264
|
function isPiAvailable() {
|
|
8105
|
-
return
|
|
8265
|
+
return existsSync8(join9(getPiAgentDir(), "sessions"));
|
|
8106
8266
|
}
|
|
8107
8267
|
function classifyPiRecord(prefix) {
|
|
8108
8268
|
if (prefix.includes('"type":"message"') && prefix.includes('"role":"assistant"')) {
|
|
@@ -8208,6 +8368,7 @@ function createEmptyProviderAvailability() {
|
|
|
8208
8368
|
cursor: false,
|
|
8209
8369
|
gemini: false,
|
|
8210
8370
|
opencode: false,
|
|
8371
|
+
openclaw: false,
|
|
8211
8372
|
pi: false
|
|
8212
8373
|
};
|
|
8213
8374
|
}
|
|
@@ -8225,6 +8386,8 @@ async function isProviderAvailable(provider) {
|
|
|
8225
8386
|
return isGeminiAvailable();
|
|
8226
8387
|
case "opencode":
|
|
8227
8388
|
return isOpenCodeAvailable();
|
|
8389
|
+
case "openclaw":
|
|
8390
|
+
return isOpenClawAvailable();
|
|
8228
8391
|
case "pi":
|
|
8229
8392
|
return isPiAvailable();
|
|
8230
8393
|
default: {
|
|
@@ -8260,6 +8423,7 @@ async function aggregateUsage({
|
|
|
8260
8423
|
cursor: null,
|
|
8261
8424
|
gemini: null,
|
|
8262
8425
|
opencode: null,
|
|
8426
|
+
openclaw: null,
|
|
8263
8427
|
pi: null
|
|
8264
8428
|
};
|
|
8265
8429
|
const warnings = [];
|
|
@@ -8284,6 +8448,9 @@ async function aggregateUsage({
|
|
|
8284
8448
|
case "opencode":
|
|
8285
8449
|
summary = await loadOpenCodeRows(start, end);
|
|
8286
8450
|
break;
|
|
8451
|
+
case "openclaw":
|
|
8452
|
+
summary = await loadOpenClawRows(start, end);
|
|
8453
|
+
break;
|
|
8287
8454
|
case "pi":
|
|
8288
8455
|
summary = await loadPiRows(start, end);
|
|
8289
8456
|
break;
|
|
@@ -8307,16 +8474,18 @@ var HELP_TEXT = `tokely
|
|
|
8307
8474
|
Generate rolling 1-year usage heatmap image(s) (today is the latest day).
|
|
8308
8475
|
|
|
8309
8476
|
Usage:
|
|
8310
|
-
tokely [--all] [--amp] [--claude] [--codex] [--cursor] [--gemini] [--opencode] [--pi] [--dark] [--format png|svg|json] [--output ./heatmap-last-year.png]
|
|
8477
|
+
tokely [--all] [--today] [--amp] [--claude] [--codex] [--cursor] [--gemini] [--opencode] [--openclaw] [--pi] [--dark] [--format png|svg|json] [--output ./heatmap-last-year.png]
|
|
8311
8478
|
|
|
8312
8479
|
Options:
|
|
8313
8480
|
--all Render one merged graph for all providers
|
|
8481
|
+
--today Show today's usage stats in terminal
|
|
8314
8482
|
--amp Render Amp graph
|
|
8315
8483
|
--claude Render Claude Code graph
|
|
8316
8484
|
--codex Render Codex graph
|
|
8317
8485
|
--cursor Render Cursor graph
|
|
8318
8486
|
--gemini Render Gemini CLI graph
|
|
8319
8487
|
--opencode Render Open Code graph
|
|
8488
|
+
--openclaw Render OpenClaw graph
|
|
8320
8489
|
--pi Render Pi Coding Agent graph
|
|
8321
8490
|
--dark Render with the dark theme
|
|
8322
8491
|
-f, --format Output format: png, svg, or json (default: png)
|
|
@@ -8335,12 +8504,14 @@ function validateArgs(values) {
|
|
|
8335
8504
|
help: ow.boolean,
|
|
8336
8505
|
dark: ow.boolean,
|
|
8337
8506
|
all: ow.boolean,
|
|
8507
|
+
today: ow.boolean,
|
|
8338
8508
|
amp: ow.boolean,
|
|
8339
8509
|
claude: ow.boolean,
|
|
8340
8510
|
codex: ow.boolean,
|
|
8341
8511
|
cursor: ow.boolean,
|
|
8342
8512
|
gemini: ow.boolean,
|
|
8343
8513
|
opencode: ow.boolean,
|
|
8514
|
+
openclaw: ow.boolean,
|
|
8344
8515
|
pi: ow.boolean
|
|
8345
8516
|
})
|
|
8346
8517
|
);
|
|
@@ -8407,7 +8578,7 @@ function getRequestedProviders(values) {
|
|
|
8407
8578
|
return providerIds.filter((id) => values[id]);
|
|
8408
8579
|
}
|
|
8409
8580
|
function getMergedNoDataMessage() {
|
|
8410
|
-
return "No usage data found for
|
|
8581
|
+
return "No usage data found for Claude Code, Codex, Cursor, Open Code, OpenClaw, Amp, Gemini CLI, or Pi Coding Agent.";
|
|
8411
8582
|
}
|
|
8412
8583
|
function getRequestedMissingProvidersMessage(missing) {
|
|
8413
8584
|
return `Requested provider data not found: ${missing.map((provider) => providerStatusLabel[provider]).join(", ")}`;
|
|
@@ -8439,7 +8610,7 @@ function getDefaultOutputProviderIds(rowsByProvider) {
|
|
|
8439
8610
|
continue;
|
|
8440
8611
|
}
|
|
8441
8612
|
selected.push(provider);
|
|
8442
|
-
if (selected.length ===
|
|
8613
|
+
if (selected.length === 5) {
|
|
8443
8614
|
return selected;
|
|
8444
8615
|
}
|
|
8445
8616
|
}
|
|
@@ -8487,6 +8658,62 @@ function printRunSummary(outputPath, format, colorMode, startDate, endDate, rend
|
|
|
8487
8658
|
`
|
|
8488
8659
|
);
|
|
8489
8660
|
}
|
|
8661
|
+
function formatTokens(value) {
|
|
8662
|
+
const units = [
|
|
8663
|
+
{ size: 1e12, suffix: "T" },
|
|
8664
|
+
{ size: 1e9, suffix: "B" },
|
|
8665
|
+
{ size: 1e6, suffix: "M" },
|
|
8666
|
+
{ size: 1e3, suffix: "K" }
|
|
8667
|
+
];
|
|
8668
|
+
for (const unit of units) {
|
|
8669
|
+
if (value >= unit.size) {
|
|
8670
|
+
const scaled = value / unit.size;
|
|
8671
|
+
const precision = scaled >= 100 ? 0 : scaled >= 10 ? 1 : 2;
|
|
8672
|
+
const compact = scaled.toFixed(precision).replace(/\.0+$/, "").replace(/(\.\d*[1-9])0+$/, "$1");
|
|
8673
|
+
return `${compact}${unit.suffix}`;
|
|
8674
|
+
}
|
|
8675
|
+
}
|
|
8676
|
+
return new Intl.NumberFormat("en-US").format(value);
|
|
8677
|
+
}
|
|
8678
|
+
function printTodayStats(rowsByProvider, providers) {
|
|
8679
|
+
const today = formatLocalDate(/* @__PURE__ */ new Date());
|
|
8680
|
+
const rows = [];
|
|
8681
|
+
for (const provider of providers) {
|
|
8682
|
+
const summary = rowsByProvider[provider];
|
|
8683
|
+
if (!summary) {
|
|
8684
|
+
continue;
|
|
8685
|
+
}
|
|
8686
|
+
const todayRow = summary.daily.find(
|
|
8687
|
+
(row) => formatLocalDate(row.date) === today
|
|
8688
|
+
);
|
|
8689
|
+
if (!todayRow || todayRow.total <= 0 && (todayRow.displayValue ?? 0) <= 0) {
|
|
8690
|
+
continue;
|
|
8691
|
+
}
|
|
8692
|
+
const topModel = todayRow.breakdown.length > 0 ? `${todayRow.breakdown[0].name} (${formatTokens(todayRow.breakdown[0].tokens.total)})` : "-";
|
|
8693
|
+
rows.push({
|
|
8694
|
+
provider: providerStatusLabel[provider],
|
|
8695
|
+
input: formatTokens(todayRow.input),
|
|
8696
|
+
output: formatTokens(todayRow.output),
|
|
8697
|
+
total: formatTokens(todayRow.total),
|
|
8698
|
+
topModel
|
|
8699
|
+
});
|
|
8700
|
+
}
|
|
8701
|
+
if (rows.length === 0) {
|
|
8702
|
+
process.stdout.write(`
|
|
8703
|
+
No usage data found for today (${today}).
|
|
8704
|
+
`);
|
|
8705
|
+
return;
|
|
8706
|
+
}
|
|
8707
|
+
const header = "| Provider | Input | Output | Total | Top Model |\n| --- | --- | --- | --- | --- |";
|
|
8708
|
+
const body = rows.map(
|
|
8709
|
+
(row) => `| ${row.provider} | ${row.input} | ${row.output} | ${row.total} | ${row.topModel} |`
|
|
8710
|
+
).join("\n");
|
|
8711
|
+
process.stdout.write(`
|
|
8712
|
+
${header}
|
|
8713
|
+
${body}
|
|
8714
|
+
|
|
8715
|
+
`);
|
|
8716
|
+
}
|
|
8490
8717
|
async function main() {
|
|
8491
8718
|
let spinner;
|
|
8492
8719
|
const parsed = parseArgs({
|
|
@@ -8496,12 +8723,14 @@ async function main() {
|
|
|
8496
8723
|
help: { type: "boolean", short: "h", default: false },
|
|
8497
8724
|
dark: { type: "boolean", default: false },
|
|
8498
8725
|
all: { type: "boolean", default: false },
|
|
8726
|
+
today: { type: "boolean", default: false },
|
|
8499
8727
|
amp: { type: "boolean", default: false },
|
|
8500
8728
|
claude: { type: "boolean", default: false },
|
|
8501
8729
|
codex: { type: "boolean", default: false },
|
|
8502
8730
|
cursor: { type: "boolean", default: false },
|
|
8503
8731
|
gemini: { type: "boolean", default: false },
|
|
8504
8732
|
opencode: { type: "boolean", default: false },
|
|
8733
|
+
openclaw: { type: "boolean", default: false },
|
|
8505
8734
|
pi: { type: "boolean", default: false }
|
|
8506
8735
|
},
|
|
8507
8736
|
allowPositionals: false
|
|
@@ -8533,7 +8762,13 @@ async function main() {
|
|
|
8533
8762
|
process.stderr.write(`${warning}
|
|
8534
8763
|
`);
|
|
8535
8764
|
}
|
|
8536
|
-
|
|
8765
|
+
if (!values.today) {
|
|
8766
|
+
printProviderAvailability(availabilityByProvider, inspectedProviders);
|
|
8767
|
+
}
|
|
8768
|
+
if (values.today) {
|
|
8769
|
+
printTodayStats(rowsByProvider, inspectedProviders);
|
|
8770
|
+
return;
|
|
8771
|
+
}
|
|
8537
8772
|
const exportProviders = getOutputProviders(
|
|
8538
8773
|
values,
|
|
8539
8774
|
availabilityByProvider,
|