opencode-usage 0.5.0 → 0.5.1
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 +20 -0
- package/dist/index.js +58 -4
- package/dist/index.js.map +5 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ CLI tool for tracking [OpenCode](https://github.com/sst/opencode) AI coding assi
|
|
|
11
11
|
- JSON output for scripting and automation
|
|
12
12
|
- Model pricing for accurate cost estimation
|
|
13
13
|
- Terminal table output
|
|
14
|
+
- **Commander web dashboard** with quota status, account management, and ping
|
|
14
15
|
|
|
15
16
|
## Installation
|
|
16
17
|
|
|
@@ -63,6 +64,25 @@ opencode-usage -w -d 1
|
|
|
63
64
|
opencode-usage --provider anthropic --since 7d --json
|
|
64
65
|
```
|
|
65
66
|
|
|
67
|
+
### Commander Web Dashboard
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Launch the web dashboard
|
|
71
|
+
opencode-usage --commander
|
|
72
|
+
|
|
73
|
+
# Custom port
|
|
74
|
+
opencode-usage --commander --commander-port 5000
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The Commander provides a single-page web UI with:
|
|
78
|
+
|
|
79
|
+
- **Quota Status** - Per-provider account usage with progress bars, thresholds, and stale detection (Anthropic, Codex, Antigravity)
|
|
80
|
+
- **Usage Breakdown** - Daily token usage table with cost estimates and provider drill-down
|
|
81
|
+
- **Account Management** - Add, switch, remove, and re-authenticate accounts
|
|
82
|
+
- **Ping** - Verify account connectivity with live PONG/FAIL indicators
|
|
83
|
+
- **Dark mode** toggle
|
|
84
|
+
- Auto-refresh every 5 minutes
|
|
85
|
+
|
|
66
86
|
## Output
|
|
67
87
|
|
|
68
88
|
```
|
package/dist/index.js
CHANGED
|
@@ -30146,6 +30146,56 @@ import { join as join10, dirname as dirname2 } from "path";
|
|
|
30146
30146
|
import { readFileSync as readFileSync2 } from "fs";
|
|
30147
30147
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
30148
30148
|
|
|
30149
|
+
// src/commander/services/usage-service.ts
|
|
30150
|
+
function serializeProviderStats(ps) {
|
|
30151
|
+
return {
|
|
30152
|
+
input: ps.input,
|
|
30153
|
+
output: ps.output,
|
|
30154
|
+
cacheWrite: ps.cacheWrite,
|
|
30155
|
+
cacheRead: ps.cacheRead,
|
|
30156
|
+
reasoning: ps.reasoning,
|
|
30157
|
+
cost: ps.cost,
|
|
30158
|
+
models: [...ps.models]
|
|
30159
|
+
};
|
|
30160
|
+
}
|
|
30161
|
+
function serializeDailyStats(stats) {
|
|
30162
|
+
const providerStats = {};
|
|
30163
|
+
for (const [id, ps] of stats.providerStats) {
|
|
30164
|
+
providerStats[id] = serializeProviderStats(ps);
|
|
30165
|
+
}
|
|
30166
|
+
return {
|
|
30167
|
+
date: stats.date,
|
|
30168
|
+
input: stats.input,
|
|
30169
|
+
output: stats.output,
|
|
30170
|
+
cacheWrite: stats.cacheWrite,
|
|
30171
|
+
cacheRead: stats.cacheRead,
|
|
30172
|
+
reasoning: stats.reasoning,
|
|
30173
|
+
cost: stats.cost,
|
|
30174
|
+
models: [...stats.models],
|
|
30175
|
+
providers: [...stats.providers],
|
|
30176
|
+
providerStats
|
|
30177
|
+
};
|
|
30178
|
+
}
|
|
30179
|
+
async function getUsageData(opts = {}) {
|
|
30180
|
+
const storagePath = getOpenCodeStoragePath();
|
|
30181
|
+
const messages = await loadMessages(storagePath, opts.provider);
|
|
30182
|
+
let stats = aggregateByDate(messages);
|
|
30183
|
+
if (opts.days !== undefined) {
|
|
30184
|
+
stats = filterByDays(stats, opts.days);
|
|
30185
|
+
}
|
|
30186
|
+
if (opts.since !== undefined || opts.until !== undefined) {
|
|
30187
|
+
stats = filterByDateRange(stats, opts.since, opts.until);
|
|
30188
|
+
}
|
|
30189
|
+
if (opts.monthly) {
|
|
30190
|
+
stats = aggregateByMonth(stats);
|
|
30191
|
+
}
|
|
30192
|
+
const result = [];
|
|
30193
|
+
for (const [, entry] of stats) {
|
|
30194
|
+
result.push(serializeDailyStats(entry));
|
|
30195
|
+
}
|
|
30196
|
+
return result;
|
|
30197
|
+
}
|
|
30198
|
+
|
|
30149
30199
|
// src/commander/services/quota-service.ts
|
|
30150
30200
|
async function getQuotaData() {
|
|
30151
30201
|
const [anthropic, antigravity, codex] = await Promise.all([
|
|
@@ -30466,7 +30516,9 @@ var registered2 = false;
|
|
|
30466
30516
|
|
|
30467
30517
|
// src/commander/server.ts
|
|
30468
30518
|
init_config_service();
|
|
30469
|
-
function
|
|
30519
|
+
async function queryUsage(opts) {
|
|
30520
|
+
if (!canUseWorker)
|
|
30521
|
+
return getUsageData(opts);
|
|
30470
30522
|
return new Promise((resolve3, reject) => {
|
|
30471
30523
|
const worker = new Worker(usageWorkerPath);
|
|
30472
30524
|
worker.onmessage = (event) => {
|
|
@@ -30514,7 +30566,7 @@ async function runCommanderServer(args) {
|
|
|
30514
30566
|
const since = url.searchParams.get("since") ?? undefined;
|
|
30515
30567
|
const until = url.searchParams.get("until") ?? undefined;
|
|
30516
30568
|
const monthly = url.searchParams.get("monthly") === "true";
|
|
30517
|
-
const data = await
|
|
30569
|
+
const data = await queryUsage({
|
|
30518
30570
|
provider,
|
|
30519
30571
|
days,
|
|
30520
30572
|
since,
|
|
@@ -30679,7 +30731,8 @@ async function runCommanderServer(args) {
|
|
|
30679
30731
|
}
|
|
30680
30732
|
}
|
|
30681
30733
|
if (!url.pathname.startsWith("/api/")) {
|
|
30682
|
-
const
|
|
30734
|
+
const base = new URL(".", import.meta.url).pathname;
|
|
30735
|
+
const UI_DIST = await Bun.file(join10(base, "commander-ui", "index.html")).exists() ? join10(base, "commander-ui") : join10(base, "..", "commander-ui", "dist");
|
|
30683
30736
|
const filePath = url.pathname === "/" ? join10(UI_DIST, "index.html") : join10(UI_DIST, url.pathname);
|
|
30684
30737
|
const file = Bun.file(filePath);
|
|
30685
30738
|
if (await file.exists())
|
|
@@ -30708,6 +30761,7 @@ var PKG_VERSION = (() => {
|
|
|
30708
30761
|
}
|
|
30709
30762
|
})();
|
|
30710
30763
|
var usageWorkerPath = join10(__dirname2, "services", "usage-worker.ts");
|
|
30764
|
+
var canUseWorker = await Bun.file(usageWorkerPath).exists();
|
|
30711
30765
|
// src/index.ts
|
|
30712
30766
|
function clearScreen() {
|
|
30713
30767
|
process.stdout.write("\x1B[2J\x1B[H");
|
|
@@ -30818,4 +30872,4 @@ async function main2() {
|
|
|
30818
30872
|
var WATCH_INTERVAL_MS = 5 * 60 * 1000;
|
|
30819
30873
|
main2().catch(console.error);
|
|
30820
30874
|
|
|
30821
|
-
//# debugId=
|
|
30875
|
+
//# debugId=AAD27BF82DBA8BEB64756E2164756E21
|