@1930dev/opencode-usage 0.1.0 → 0.1.2
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 +58 -34
- package/dist/cli.js +7 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# opencode-usage
|
|
2
2
|
|
|
3
|
-
Usage tracking,
|
|
3
|
+
Usage tracking, budget percentages and model ranking for [opencode](https://opencode.ai):
|
|
4
|
+
a CLI, and a `/usage` slash command for the TUI.
|
|
4
5
|
|
|
5
6
|
## Install
|
|
6
7
|
|
|
@@ -15,14 +16,22 @@ Requires [Bun](https://bun.sh): the CLI reads opencode's SQLite through `bun:sql
|
|
|
15
16
|
```bash
|
|
16
17
|
# Local usage from opencode's SQLite (all providers, all projects)
|
|
17
18
|
opencode-usage usage # last 7 days, grouped by provider
|
|
19
|
+
|
|
20
|
+
# Window
|
|
18
21
|
opencode-usage usage --today # today only
|
|
19
22
|
opencode-usage usage --since 30d # last 30 days
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
opencode-usage usage --by agent #
|
|
23
|
-
opencode-usage usage --
|
|
24
|
-
|
|
25
|
-
opencode-usage usage --
|
|
23
|
+
|
|
24
|
+
# Grouping
|
|
25
|
+
opencode-usage usage --by agent # by agent (build/plan/etc)
|
|
26
|
+
opencode-usage usage --by day # by day
|
|
27
|
+
opencode-usage usage --by model # by model
|
|
28
|
+
opencode-usage usage --by project # by project directory
|
|
29
|
+
opencode-usage usage --by provider # the default; lists every connected
|
|
30
|
+
# provider, idle ones included
|
|
31
|
+
|
|
32
|
+
# Output
|
|
33
|
+
opencode-usage usage --json # machine-readable
|
|
34
|
+
opencode-usage usage --pct # add the % BUDGET column (--by provider only)
|
|
26
35
|
|
|
27
36
|
# Connected providers + live quota
|
|
28
37
|
opencode-usage providers
|
|
@@ -74,19 +83,20 @@ Restart opencode after a rebuild: the bundle is read once at start.
|
|
|
74
83
|
Normalized percentage of budget consumed per provider, from these sources (in priority):
|
|
75
84
|
|
|
76
85
|
1. **Live quota** — provider-reported usage:
|
|
77
|
-
- `opencode-go` (Zen): rolling 5h / weekly / monthly % (binding window)
|
|
78
86
|
- `github-copilot`: premium requests entitlement (7000/mo)
|
|
87
|
+
- `opencode-go` (Zen): rolling 5h / weekly / monthly % (binding window)
|
|
79
88
|
- `openrouter`: credits used / total credits
|
|
80
89
|
- `zai`: coding plan quota
|
|
81
90
|
|
|
82
|
-
2. **Documented limits** —
|
|
83
|
-
- `
|
|
84
|
-
- `
|
|
85
|
-
- `digitalocean`: 5M tokens/day
|
|
86
|
-
- `
|
|
87
|
-
- `
|
|
88
|
-
- `
|
|
89
|
-
- `
|
|
91
|
+
2. **Documented limits** — published quotas, used when the provider reports none:
|
|
92
|
+
- `cerebras`: 1M tokens/day (free tier)
|
|
93
|
+
- `cloudflare-workers-ai`: 100k neurons/day (free tier)
|
|
94
|
+
- `digitalocean`: 5M tokens/day (paid)
|
|
95
|
+
- `google`: 1500 requests/day (free tier)
|
|
96
|
+
- `groq`: 200k tokens/day (free tier)
|
|
97
|
+
- `nvidia`: 1000 credits/month (free tier)
|
|
98
|
+
- `orcarouter`: undocumented
|
|
99
|
+
- `snowflake-cortex`: 100 credits/month (paid)
|
|
90
100
|
|
|
91
101
|
3. **budgets.json** — your monthly USD per provider:
|
|
92
102
|
```json
|
|
@@ -108,36 +118,50 @@ glm-5.3-flash GLM 5.3-Flash 58 72 $0.11 534.9
|
|
|
108
118
|
...
|
|
109
119
|
```
|
|
110
120
|
|
|
111
|
-
|
|
121
|
+
Intelligence indices from [Artificial Analysis](https://artificialanalysis.ai), pricing
|
|
122
|
+
from [models.dev](https://models.dev).
|
|
123
|
+
|
|
124
|
+
`top` needs an `AA_API_KEY` for the indices. This package ships neither a key nor a copy
|
|
125
|
+
of the data: the free Data API tier is "internal use only; no redistribution", and
|
|
126
|
+
attribution is required on every tier — which is why the credit is printed under the
|
|
127
|
+
table and carried in `--json`.
|
|
112
128
|
|
|
113
129
|
## Requirements
|
|
114
130
|
|
|
115
|
-
- [Bun](https://bun.sh)
|
|
116
|
-
- [opencode](https://opencode.ai) with existing sessions
|
|
117
|
-
|
|
131
|
+
- [Bun](https://bun.sh) >= 1.0 — the CLI reads opencode's SQLite through `bun:sqlite`
|
|
132
|
+
- [opencode](https://opencode.ai) with existing sessions, at
|
|
133
|
+
`~/.local/share/opencode/opencode.db`
|
|
134
|
+
- For live quota: provider credentials already configured in opencode, at
|
|
135
|
+
`~/.local/share/opencode/auth.json`
|
|
118
136
|
|
|
119
137
|
## Configuration
|
|
120
138
|
|
|
121
|
-
- `
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
- `
|
|
125
|
-
- `
|
|
126
|
-
- `
|
|
127
|
-
|
|
139
|
+
- `AA_API_KEY` — Artificial Analysis key, used by `top` for the intelligence indices.
|
|
140
|
+
Optional: without it `top` still prints prices, only without `IQ` and `IQ/$`.
|
|
141
|
+
A free key is at [artificialanalysis.ai/data-api](https://artificialanalysis.ai/data-api)
|
|
142
|
+
- `OPENCODE_AUTH_PATH` — override the path to opencode's `auth.json`
|
|
143
|
+
- `OPENCODE_DB_PATH` — override the path to opencode's database
|
|
144
|
+
- `OPENCODE_USAGE_BUDGETS` — override the path to `budgets.json`
|
|
145
|
+
- `OPENCODE_USAGE_CACHE` — cache directory (default `~/.cache/opencode-usage`)
|
|
128
146
|
|
|
129
147
|
## Development
|
|
130
148
|
|
|
131
149
|
```bash
|
|
132
|
-
bun install
|
|
133
|
-
bun
|
|
134
|
-
|
|
150
|
+
bun install # install workspace deps
|
|
151
|
+
bun run build # write dist/tui.js and dist/cli.js
|
|
152
|
+
bun run preview # render the /usage dialog headless, at several widths
|
|
153
|
+
bun test # run tests
|
|
154
|
+
bunx tsc --noEmit # typecheck
|
|
135
155
|
```
|
|
136
156
|
|
|
137
|
-
The workspace is a Bun monorepo
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
- `@opencode-usage/
|
|
157
|
+
The workspace is a Bun monorepo. The packages are listed by dependency order,
|
|
158
|
+
since `cli` and `plugin` both build on `core`:
|
|
159
|
+
|
|
160
|
+
- `@opencode-usage/core` — shared data layer (SQLite, quotas, matching, ranking)
|
|
161
|
+
- `@opencode-usage/cli` — the `opencode-usage` binary
|
|
162
|
+
- `@opencode-usage/plugin` — the opencode TUI plugin
|
|
163
|
+
|
|
164
|
+
Only the root package is published; `core` is inlined into both bundles.
|
|
141
165
|
|
|
142
166
|
## License
|
|
143
167
|
|
package/dist/cli.js
CHANGED
|
@@ -448,7 +448,6 @@ function providersTable(statuses, local) {
|
|
|
448
448
|
// packages/core/src/ranking/fetch.ts
|
|
449
449
|
import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
450
450
|
import path3 from "path";
|
|
451
|
-
var {$ } = globalThis.Bun;
|
|
452
451
|
var TTL_MS2 = 24 * 60 * 60 * 1000;
|
|
453
452
|
async function readCache2(file) {
|
|
454
453
|
try {
|
|
@@ -481,24 +480,14 @@ async function fetchModelsDev() {
|
|
|
481
480
|
await writeCache2("models-dev.json", flat);
|
|
482
481
|
return flat;
|
|
483
482
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
return process.env.AA_API_KEY;
|
|
487
|
-
const projectId = process.env.INFISICAL_PROJECT_ID;
|
|
488
|
-
if (!projectId)
|
|
489
|
-
return;
|
|
490
|
-
try {
|
|
491
|
-
const out = await $`infisical-secret get AA_API_KEY --projectId ${projectId} --env prod --plain --silent`.text();
|
|
492
|
-
return out.trim() || undefined;
|
|
493
|
-
} catch {
|
|
494
|
-
return;
|
|
495
|
-
}
|
|
483
|
+
function aaApiKey() {
|
|
484
|
+
return process.env.AA_API_KEY || undefined;
|
|
496
485
|
}
|
|
497
486
|
async function fetchAA() {
|
|
498
487
|
const cached = await readCache2("aa.json");
|
|
499
488
|
if (cached)
|
|
500
489
|
return cached;
|
|
501
|
-
const key =
|
|
490
|
+
const key = aaApiKey();
|
|
502
491
|
if (!key)
|
|
503
492
|
return [];
|
|
504
493
|
const res = await fetch("https://artificialanalysis.ai/api/v2/data/llms/models", {
|
|
@@ -585,6 +574,7 @@ function valueScore(intelligenceIndex, cost) {
|
|
|
585
574
|
}
|
|
586
575
|
|
|
587
576
|
// packages/core/src/top.ts
|
|
577
|
+
var RANKING_ATTRIBUTION = "Intelligence: Artificial Analysis (artificialanalysis.ai) \xB7 Pricing: models.dev";
|
|
588
578
|
async function buildTop(limit) {
|
|
589
579
|
const [modelsDev, aaModels] = await Promise.all([fetchModelsDev(), fetchAA()]);
|
|
590
580
|
const match = buildMatcher(aaModels, modelsDev);
|
|
@@ -883,7 +873,7 @@ async function cmdTop(flags, json) {
|
|
|
883
873
|
const limit = typeof flags.get("--limit") === "string" ? Number(flags.get("--limit")) : 20;
|
|
884
874
|
const rows = await buildTop(limit);
|
|
885
875
|
if (json) {
|
|
886
|
-
console.log(JSON.stringify(rows, null, 2));
|
|
876
|
+
console.log(JSON.stringify({ attribution: RANKING_ATTRIBUTION, models: rows }, null, 2));
|
|
887
877
|
return;
|
|
888
878
|
}
|
|
889
879
|
const head = ["MODEL", "NAME", "IQ", "CODING", "$/M", "IQ/$"];
|
|
@@ -901,6 +891,8 @@ async function cmdTop(flags, json) {
|
|
|
901
891
|
r.value !== undefined ? r.value.toFixed(1) : "\u2014"
|
|
902
892
|
]));
|
|
903
893
|
}
|
|
894
|
+
console.log(`
|
|
895
|
+
${RANKING_ATTRIBUTION}`);
|
|
904
896
|
}
|
|
905
897
|
if (import.meta.main) {
|
|
906
898
|
const { cmd, flags } = parseArgs(process.argv.slice(2));
|