opencode-token-tracker 1.3.2 → 1.5.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/README.md +84 -1
- package/README.zh-CN.md +367 -0
- package/dist/bin/opencode-tokens.js +138 -93
- package/dist/index.js +210 -99
- package/dist/lib/shared.d.ts +44 -0
- package/dist/lib/shared.js +279 -0
- package/dist/test/shared.test.d.ts +1 -0
- package/dist/test/shared.test.js +285 -0
- package/package.json +2 -1
|
@@ -1,95 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { BUILTIN_PRICING, DEFAULT_CONFIG, formatCost, formatTokens, getStartOfDay, getStartOfWeek, getStartOfMonth, validateConfig } from "../lib/shared.js";
|
|
2
3
|
import { readFileSync, existsSync, writeFileSync } from "fs";
|
|
3
4
|
import { join } from "path";
|
|
4
5
|
import { homedir } from "os";
|
|
5
6
|
const CONFIG_DIR = join(homedir(), ".config", "opencode");
|
|
6
7
|
const CONFIG_FILE = join(CONFIG_DIR, "token-tracker.json");
|
|
7
8
|
const LOG_FILE = join(CONFIG_DIR, "logs", "token-tracker", "tokens.jsonl");
|
|
8
|
-
// Built-in pricing (USD per 1M tokens) - Updated 2026-02-05
|
|
9
|
-
// Keep in sync with index.ts BUILTIN_PRICING
|
|
10
|
-
const BUILTIN_PRICING = {
|
|
11
|
-
// Anthropic Claude (https://www.anthropic.com/pricing#api)
|
|
12
|
-
"claude-opus-4.5": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
|
|
13
|
-
"claude-sonnet-4.5": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
|
|
14
|
-
"claude-sonnet-4": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
|
|
15
|
-
"claude-haiku-4.5": { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 },
|
|
16
|
-
"claude-haiku-4": { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 },
|
|
17
|
-
"claude-opus-4.1": { input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75 },
|
|
18
|
-
"claude-opus-4": { input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75 },
|
|
19
|
-
"claude-haiku-3": { input: 0.25, output: 1.25, cacheRead: 0.03, cacheWrite: 0.3 },
|
|
20
|
-
// OpenAI GPT (https://openai.com/api/pricing/)
|
|
21
|
-
"gpt-5.2": { input: 1.75, output: 14, cacheRead: 0.175 },
|
|
22
|
-
"gpt-5.2-pro": { input: 21, output: 168 },
|
|
23
|
-
"gpt-5-mini": { input: 0.25, output: 2, cacheRead: 0.025 },
|
|
24
|
-
"gpt-5.1": { input: 2, output: 8 },
|
|
25
|
-
"gpt-5": { input: 5, output: 15 },
|
|
26
|
-
"gpt-4.1": { input: 3, output: 12, cacheRead: 0.75 },
|
|
27
|
-
"gpt-4.1-mini": { input: 0.8, output: 3.2, cacheRead: 0.2 },
|
|
28
|
-
"gpt-4.1-nano": { input: 0.2, output: 0.8, cacheRead: 0.05 },
|
|
29
|
-
"gpt-4o": { input: 2.5, output: 10 },
|
|
30
|
-
"gpt-4o-mini": { input: 0.15, output: 0.6 },
|
|
31
|
-
"o3": { input: 10, output: 40 },
|
|
32
|
-
"o3-mini": { input: 1.1, output: 4.4 },
|
|
33
|
-
"o4-mini": { input: 4, output: 16, cacheRead: 1 },
|
|
34
|
-
"o1": { input: 15, output: 60 },
|
|
35
|
-
"o1-mini": { input: 1.1, output: 4.4 },
|
|
36
|
-
// DeepSeek (https://api-docs.deepseek.com/quick_start/pricing)
|
|
37
|
-
"deepseek-chat": { input: 0.28, output: 0.42, cacheRead: 0.028 },
|
|
38
|
-
"deepseek-reasoner": { input: 0.28, output: 0.42, cacheRead: 0.028 },
|
|
39
|
-
// Google Gemini (https://cloud.google.com/vertex-ai/generative-ai/pricing)
|
|
40
|
-
"gemini-3-pro": { input: 2, output: 12, cacheRead: 0.2 },
|
|
41
|
-
"gemini-3-pro-preview": { input: 2, output: 12, cacheRead: 0.2 },
|
|
42
|
-
"gemini-3-flash": { input: 0.5, output: 2, cacheRead: 0.05 },
|
|
43
|
-
"gemini-3-flash-preview": { input: 0.5, output: 2, cacheRead: 0.05 },
|
|
44
|
-
"gemini-2.5-pro": { input: 1.25, output: 10, cacheRead: 0.125 },
|
|
45
|
-
"gemini-2.5-flash": { input: 0.1, output: 0.4, cacheRead: 0.01 },
|
|
46
|
-
"gemini-2.5-flash-lite": { input: 0.1, output: 0.4, cacheRead: 0.01 },
|
|
47
|
-
"gemini-2.0-flash": { input: 0.15, output: 0.6, cacheRead: 0.015 },
|
|
48
|
-
// Fallback
|
|
49
|
-
"_default": { input: 1, output: 4 },
|
|
50
|
-
};
|
|
51
9
|
// ============================================================================
|
|
52
10
|
// Helpers
|
|
53
11
|
// ============================================================================
|
|
54
|
-
function formatTokens(tokens) {
|
|
55
|
-
if (tokens >= 1_000_000)
|
|
56
|
-
return `${(tokens / 1_000_000).toFixed(2)}M`;
|
|
57
|
-
if (tokens >= 1_000)
|
|
58
|
-
return `${(tokens / 1_000).toFixed(1)}K`;
|
|
59
|
-
return tokens.toString();
|
|
60
|
-
}
|
|
61
|
-
function formatCost(cost) {
|
|
62
|
-
if (cost < 0.01)
|
|
63
|
-
return `$${cost.toFixed(4)}`;
|
|
64
|
-
if (cost < 1)
|
|
65
|
-
return `$${cost.toFixed(3)}`;
|
|
66
|
-
return `$${cost.toFixed(2)}`;
|
|
67
|
-
}
|
|
68
12
|
function padRight(str, len) {
|
|
69
13
|
return str.length >= len ? str : str + " ".repeat(len - str.length);
|
|
70
14
|
}
|
|
71
15
|
function padLeft(str, len) {
|
|
72
16
|
return str.length >= len ? str : " ".repeat(len - str.length) + str;
|
|
73
17
|
}
|
|
74
|
-
function getStartOfDay(date) {
|
|
75
|
-
const d = new Date(date);
|
|
76
|
-
d.setHours(0, 0, 0, 0);
|
|
77
|
-
return d.getTime();
|
|
78
|
-
}
|
|
79
|
-
function getStartOfWeek(date) {
|
|
80
|
-
const d = new Date(date);
|
|
81
|
-
const day = d.getDay();
|
|
82
|
-
const diff = d.getDate() - day + (day === 0 ? -6 : 1);
|
|
83
|
-
d.setDate(diff);
|
|
84
|
-
d.setHours(0, 0, 0, 0);
|
|
85
|
-
return d.getTime();
|
|
86
|
-
}
|
|
87
|
-
function getStartOfMonth(date) {
|
|
88
|
-
const d = new Date(date);
|
|
89
|
-
d.setDate(1);
|
|
90
|
-
d.setHours(0, 0, 0, 0);
|
|
91
|
-
return d.getTime();
|
|
92
|
-
}
|
|
93
18
|
// ============================================================================
|
|
94
19
|
// Data Loading
|
|
95
20
|
// ============================================================================
|
|
@@ -120,11 +45,20 @@ function loadEntries(since) {
|
|
|
120
45
|
function loadConfig() {
|
|
121
46
|
try {
|
|
122
47
|
if (existsSync(CONFIG_FILE)) {
|
|
123
|
-
|
|
48
|
+
const raw = JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
|
|
49
|
+
const result = validateConfig(raw);
|
|
50
|
+
if (result.warnings.length > 0) {
|
|
51
|
+
for (const w of result.warnings) {
|
|
52
|
+
console.error(` [token-tracker] config warning: ${w}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return result.config;
|
|
124
56
|
}
|
|
125
57
|
}
|
|
126
|
-
catch {
|
|
127
|
-
|
|
58
|
+
catch {
|
|
59
|
+
console.error(" [token-tracker] config warning: Config file is not valid JSON, using defaults");
|
|
60
|
+
}
|
|
61
|
+
return DEFAULT_CONFIG;
|
|
128
62
|
}
|
|
129
63
|
// ============================================================================
|
|
130
64
|
// Stats Aggregation
|
|
@@ -171,13 +105,13 @@ function printSummary(title, stats) {
|
|
|
171
105
|
console.log();
|
|
172
106
|
console.log(` ${title}`);
|
|
173
107
|
console.log(` ${"─".repeat(50)}`);
|
|
174
|
-
console.log(` Total Tokens: ${padLeft(formatTokens(totalTokens), 12)}`);
|
|
175
|
-
console.log(` Input: ${padLeft(formatTokens(stats.input), 12)}`);
|
|
176
|
-
console.log(` Output: ${padLeft(formatTokens(stats.output), 12)}`);
|
|
108
|
+
console.log(` Total Tokens: ${padLeft(formatTokens(totalTokens, 2), 12)}`);
|
|
109
|
+
console.log(` Input: ${padLeft(formatTokens(stats.input, 2), 12)}`);
|
|
110
|
+
console.log(` Output: ${padLeft(formatTokens(stats.output, 2), 12)}`);
|
|
177
111
|
if (stats.reasoning > 0) {
|
|
178
|
-
console.log(` Reasoning: ${padLeft(formatTokens(stats.reasoning), 12)}`);
|
|
112
|
+
console.log(` Reasoning: ${padLeft(formatTokens(stats.reasoning, 2), 12)}`);
|
|
179
113
|
}
|
|
180
|
-
console.log(` Cache Read: ${padLeft(formatTokens(stats.cacheRead), 12)}`);
|
|
114
|
+
console.log(` Cache Read: ${padLeft(formatTokens(stats.cacheRead, 2), 12)}`);
|
|
181
115
|
console.log(` Total Cost: ${padLeft(formatCost(stats.cost), 12)}`);
|
|
182
116
|
console.log(` Messages: ${padLeft(stats.count.toString(), 12)}`);
|
|
183
117
|
console.log();
|
|
@@ -199,7 +133,7 @@ function printTable(title, groups, labelHeader) {
|
|
|
199
133
|
console.log(` ${"-".repeat(labelWidth)} ${"-".repeat(tokensWidth)} ${"-".repeat(costWidth)} ${"-".repeat(countWidth)}`);
|
|
200
134
|
for (const [label, stats] of sorted) {
|
|
201
135
|
const totalTokens = stats.input + stats.output;
|
|
202
|
-
console.log(` ${padRight(label, labelWidth)} ${padLeft(formatTokens(totalTokens), tokensWidth)} ${padLeft(formatCost(stats.cost), costWidth)} ${padLeft(stats.count.toString(), countWidth)}`);
|
|
136
|
+
console.log(` ${padRight(label, labelWidth)} ${padLeft(formatTokens(totalTokens, 2), tokensWidth)} ${padLeft(formatCost(stats.cost), costWidth)} ${padLeft(stats.count.toString(), countWidth)}`);
|
|
203
137
|
}
|
|
204
138
|
console.log();
|
|
205
139
|
}
|
|
@@ -224,7 +158,7 @@ function printDailyBreakdown(entries) {
|
|
|
224
158
|
console.log(` ${"-".repeat(dateWidth)} ${"-".repeat(tokensWidth)} ${"-".repeat(costWidth)} ${"-".repeat(countWidth)}`);
|
|
225
159
|
for (const [date, stats] of sorted) {
|
|
226
160
|
const totalTokens = stats.input + stats.output;
|
|
227
|
-
console.log(` ${padRight(date, dateWidth)} ${padLeft(formatTokens(totalTokens), tokensWidth)} ${padLeft(formatCost(stats.cost), costWidth)} ${padLeft(stats.count.toString(), countWidth)}`);
|
|
161
|
+
console.log(` ${padRight(date, dateWidth)} ${padLeft(formatTokens(totalTokens, 2), tokensWidth)} ${padLeft(formatCost(stats.cost), costWidth)} ${padLeft(stats.count.toString(), countWidth)}`);
|
|
228
162
|
}
|
|
229
163
|
console.log();
|
|
230
164
|
}
|
|
@@ -285,15 +219,15 @@ function cmdStats(period, breakdown) {
|
|
|
285
219
|
function cmdPricing() {
|
|
286
220
|
const config = loadConfig();
|
|
287
221
|
console.log(`
|
|
288
|
-
Built-in Pricing Table (USD per 1M tokens) - Updated 2026-02-
|
|
222
|
+
Built-in Pricing Table (USD per 1M tokens) - Updated 2026-02-11
|
|
289
223
|
══════════════════════════════════════════════════════════════════
|
|
290
224
|
`);
|
|
291
225
|
// Group by provider
|
|
292
226
|
const groups = {
|
|
293
|
-
"Anthropic Claude": ["claude-opus-4.5", "claude-sonnet-4.5", "claude-sonnet-4", "claude-haiku-4.5", "claude-haiku-4", "claude-opus-4.1", "claude-opus-4", "claude-haiku-3"],
|
|
227
|
+
"Anthropic Claude": ["claude-opus-4.6", "claude-opus-4.5", "claude-sonnet-4.5", "claude-sonnet-4", "claude-haiku-4.5", "claude-haiku-4", "claude-opus-4.1", "claude-opus-4", "claude-haiku-3"],
|
|
294
228
|
"OpenAI": ["gpt-5.2", "gpt-5.2-pro", "gpt-5-mini", "gpt-5.1", "gpt-5", "gpt-4.1", "gpt-4.1-mini", "gpt-4.1-nano", "gpt-4o", "gpt-4o-mini", "o3", "o3-mini", "o4-mini", "o1", "o1-mini"],
|
|
295
229
|
"DeepSeek": ["deepseek-chat", "deepseek-reasoner"],
|
|
296
|
-
"Google Gemini": ["gemini-3-pro", "gemini-3-pro-preview", "gemini-3-flash", "gemini-3-flash-preview", "gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.5-flash-lite", "gemini-2.0-flash"],
|
|
230
|
+
"Google Gemini": ["gemini-3-pro", "gemini-3-pro-preview", "gemini-3-flash", "gemini-3-flash-preview", "gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.5-flash-lite", "gemini-2.0-flash", "gemini-2.0-flash-lite"],
|
|
297
231
|
};
|
|
298
232
|
const modelWidth = 20;
|
|
299
233
|
const priceWidth = 10;
|
|
@@ -404,6 +338,12 @@ function cmdConfig(action) {
|
|
|
404
338
|
duration: 3000,
|
|
405
339
|
showOnIdle: true,
|
|
406
340
|
},
|
|
341
|
+
budget: {
|
|
342
|
+
daily: 5,
|
|
343
|
+
weekly: 25,
|
|
344
|
+
monthly: 100,
|
|
345
|
+
warnAt: 0.8,
|
|
346
|
+
},
|
|
407
347
|
};
|
|
408
348
|
// Add providers as comments/examples
|
|
409
349
|
for (const provider of providers) {
|
|
@@ -418,11 +358,11 @@ function cmdConfig(action) {
|
|
|
418
358
|
}
|
|
419
359
|
// Print explanation first
|
|
420
360
|
console.log(`
|
|
421
|
-
|
|
361
|
+
Configuration Guide
|
|
422
362
|
══════════════════════════════════════════════════════════════════
|
|
423
363
|
|
|
424
|
-
|
|
425
|
-
|
|
364
|
+
PRICING (prices in USD per 1 MILLION tokens)
|
|
365
|
+
────────────────────────────────────────────────────────────────
|
|
426
366
|
Fields:
|
|
427
367
|
input Cost for input/prompt tokens sent to the model
|
|
428
368
|
output Cost for output/completion tokens from the model
|
|
@@ -434,7 +374,7 @@ function cmdConfig(action) {
|
|
|
434
374
|
{ "input": 0, "output": 0 } = Free (subscription or local model)
|
|
435
375
|
|
|
436
376
|
Common scenarios:
|
|
437
|
-
- GitHub Copilot, Cursor, etc. → Set to 0
|
|
377
|
+
- GitHub Copilot, Cursor, etc. → Set provider to { input: 0, output: 0 }
|
|
438
378
|
- Local/self-hosted models → Set to 0
|
|
439
379
|
- Direct API usage → Look up provider's pricing page
|
|
440
380
|
|
|
@@ -445,6 +385,17 @@ function cmdConfig(action) {
|
|
|
445
385
|
- Google: https://ai.google.dev/pricing
|
|
446
386
|
- Or run: opencode-tokens pricing
|
|
447
387
|
|
|
388
|
+
BUDGET CONTROL
|
|
389
|
+
────────────────────────────────────────────────────────────────
|
|
390
|
+
Set spending limits to avoid unexpected costs:
|
|
391
|
+
daily Maximum spend per day (USD)
|
|
392
|
+
weekly Maximum spend per week (USD)
|
|
393
|
+
monthly Maximum spend per month (USD)
|
|
394
|
+
warnAt Warning threshold (0-1), default 0.8 = 80%
|
|
395
|
+
|
|
396
|
+
When budget is exceeded, you'll see a warning toast.
|
|
397
|
+
Check status anytime with: opencode-tokens budget
|
|
398
|
+
|
|
448
399
|
────────────────────────────────────────────────────────────────
|
|
449
400
|
Example config based on your usage:
|
|
450
401
|
`);
|
|
@@ -485,6 +436,94 @@ function cmdConfig(action) {
|
|
|
485
436
|
console.log(` opencode-tokens config generate Create config file`);
|
|
486
437
|
console.log();
|
|
487
438
|
}
|
|
439
|
+
function cmdBudget() {
|
|
440
|
+
const config = loadConfig();
|
|
441
|
+
const budget = config.budget;
|
|
442
|
+
if (!budget?.daily && !budget?.weekly && !budget?.monthly) {
|
|
443
|
+
console.log(`
|
|
444
|
+
Budget Status
|
|
445
|
+
══════════════════════════════════════════════════════════════════
|
|
446
|
+
|
|
447
|
+
No budget configured.
|
|
448
|
+
|
|
449
|
+
To set a budget, add to your config file (${CONFIG_FILE}):
|
|
450
|
+
|
|
451
|
+
{
|
|
452
|
+
"budget": {
|
|
453
|
+
"daily": 5, // $5 per day
|
|
454
|
+
"weekly": 25, // $25 per week (optional)
|
|
455
|
+
"monthly": 100, // $100 per month (optional)
|
|
456
|
+
"warnAt": 0.8 // Warn at 80% usage
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
Run: opencode-tokens config init for more details.
|
|
461
|
+
`);
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
const now = new Date();
|
|
465
|
+
const warnAt = budget.warnAt ?? 0.8;
|
|
466
|
+
console.log(`
|
|
467
|
+
Budget Status
|
|
468
|
+
══════════════════════════════════════════════════════════════════
|
|
469
|
+
`);
|
|
470
|
+
// Helper to create progress bar
|
|
471
|
+
const progressBar = (pct, width = 20) => {
|
|
472
|
+
const filled = Math.min(Math.round(pct * width), width);
|
|
473
|
+
const empty = width - filled;
|
|
474
|
+
const bar = "█".repeat(filled) + "░".repeat(empty);
|
|
475
|
+
return bar;
|
|
476
|
+
};
|
|
477
|
+
// Helper to get color indicator
|
|
478
|
+
const statusIndicator = (pct) => {
|
|
479
|
+
if (pct >= 1)
|
|
480
|
+
return "🔴";
|
|
481
|
+
if (pct >= warnAt)
|
|
482
|
+
return "🟡";
|
|
483
|
+
return "🟢";
|
|
484
|
+
};
|
|
485
|
+
// Calculate the earliest period start to minimize data loaded
|
|
486
|
+
const dayStart = getStartOfDay(now);
|
|
487
|
+
const weekStart = getStartOfWeek(now);
|
|
488
|
+
const monthStart = getStartOfMonth(now);
|
|
489
|
+
const earliestSince = Math.min(budget.daily ? dayStart : Infinity, budget.weekly ? weekStart : Infinity, budget.monthly ? monthStart : Infinity);
|
|
490
|
+
const entries = loadEntries(earliestSince);
|
|
491
|
+
// Daily budget
|
|
492
|
+
if (budget.daily) {
|
|
493
|
+
const dayEntries = entries.filter(e => e._ts >= dayStart);
|
|
494
|
+
const spent = dayEntries.reduce((sum, e) => sum + (e.cost ?? 0), 0);
|
|
495
|
+
const pct = spent / budget.daily;
|
|
496
|
+
const pctDisplay = Math.round(pct * 100);
|
|
497
|
+
console.log(` ${statusIndicator(pct)} Daily`);
|
|
498
|
+
console.log(` ${formatCost(spent)} / ${formatCost(budget.daily)} [${progressBar(pct)}] ${pctDisplay}%`);
|
|
499
|
+
console.log(` Remaining: ${formatCost(Math.max(0, budget.daily - spent))}`);
|
|
500
|
+
console.log();
|
|
501
|
+
}
|
|
502
|
+
// Weekly budget
|
|
503
|
+
if (budget.weekly) {
|
|
504
|
+
const weekEntries = entries.filter(e => e._ts >= weekStart);
|
|
505
|
+
const spent = weekEntries.reduce((sum, e) => sum + (e.cost ?? 0), 0);
|
|
506
|
+
const pct = spent / budget.weekly;
|
|
507
|
+
const pctDisplay = Math.round(pct * 100);
|
|
508
|
+
console.log(` ${statusIndicator(pct)} Weekly`);
|
|
509
|
+
console.log(` ${formatCost(spent)} / ${formatCost(budget.weekly)} [${progressBar(pct)}] ${pctDisplay}%`);
|
|
510
|
+
console.log(` Remaining: ${formatCost(Math.max(0, budget.weekly - spent))}`);
|
|
511
|
+
console.log();
|
|
512
|
+
}
|
|
513
|
+
// Monthly budget
|
|
514
|
+
if (budget.monthly) {
|
|
515
|
+
const monthEntries = entries.filter(e => e._ts >= monthStart);
|
|
516
|
+
const spent = monthEntries.reduce((sum, e) => sum + (e.cost ?? 0), 0);
|
|
517
|
+
const pct = spent / budget.monthly;
|
|
518
|
+
const pctDisplay = Math.round(pct * 100);
|
|
519
|
+
console.log(` ${statusIndicator(pct)} Monthly`);
|
|
520
|
+
console.log(` ${formatCost(spent)} / ${formatCost(budget.monthly)} [${progressBar(pct)}] ${pctDisplay}%`);
|
|
521
|
+
console.log(` Remaining: ${formatCost(Math.max(0, budget.monthly - spent))}`);
|
|
522
|
+
console.log();
|
|
523
|
+
}
|
|
524
|
+
console.log(` Legend: 🟢 OK 🟡 Warning (>${Math.round(warnAt * 100)}%) 🔴 Exceeded`);
|
|
525
|
+
console.log();
|
|
526
|
+
}
|
|
488
527
|
function cmdHelp() {
|
|
489
528
|
console.log(`
|
|
490
529
|
opencode-tokens - Token usage statistics CLI
|
|
@@ -494,6 +533,7 @@ function cmdHelp() {
|
|
|
494
533
|
|
|
495
534
|
Commands:
|
|
496
535
|
(default) Show usage statistics
|
|
536
|
+
budget Show budget status (daily/weekly/monthly)
|
|
497
537
|
pricing Show built-in pricing table
|
|
498
538
|
models Show your used models and their pricing status
|
|
499
539
|
config Show/generate configuration
|
|
@@ -508,6 +548,7 @@ function cmdHelp() {
|
|
|
508
548
|
|
|
509
549
|
Examples:
|
|
510
550
|
opencode-tokens # All-time summary
|
|
551
|
+
opencode-tokens budget # Check budget status
|
|
511
552
|
opencode-tokens today # Today's summary
|
|
512
553
|
opencode-tokens week --by model # This week, by model
|
|
513
554
|
opencode-tokens pricing # Show pricing table
|
|
@@ -526,6 +567,10 @@ function main() {
|
|
|
526
567
|
return;
|
|
527
568
|
}
|
|
528
569
|
// Handle subcommands
|
|
570
|
+
if (command === "budget") {
|
|
571
|
+
cmdBudget();
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
529
574
|
if (command === "pricing") {
|
|
530
575
|
cmdPricing();
|
|
531
576
|
return;
|