@1930dev/opencode-usage 0.3.1 → 0.3.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 +7 -5
- package/dist/cli.js +60 -11
- package/dist/tui.js +66 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -123,7 +123,8 @@ Normalized percentage of budget consumed per provider, from these sources (in pr
|
|
|
123
123
|
- `github-copilot`: premium requests entitlement (7000/mo)
|
|
124
124
|
- `opencode-go` (Zen): rolling 5h / weekly / monthly % (binding window)
|
|
125
125
|
- `openrouter`: credits used / total credits
|
|
126
|
-
- `
|
|
126
|
+
- `orcarouter`: spend since top-up; a per-key credit cap turns into a %
|
|
127
|
+
- `zai`: coding plan quota (needs an active GLM coding plan)
|
|
127
128
|
|
|
128
129
|
2. **Documented limits** — published quotas, used when the provider reports none.
|
|
129
130
|
Each one is read against the period it resets on: a daily limit against today,
|
|
@@ -138,10 +139,11 @@ Normalized percentage of budget consumed per provider, from these sources (in pr
|
|
|
138
139
|
records tokens and requests; a credit and a neuron are the provider's own
|
|
139
140
|
unit, derived from the model and the request by a rule it does not record.
|
|
140
141
|
Give them a line in `budgets.json` to get a percentage in USD instead.
|
|
141
|
-
- `cloudflare-workers-ai`:
|
|
142
|
-
- `nvidia`:
|
|
143
|
-
- `
|
|
144
|
-
- `snowflake-cortex`: 100 credits/month
|
|
142
|
+
- `cloudflare-workers-ai`: 10k neurons/day (free tier, resets 00:00 UTC)
|
|
143
|
+
- `nvidia`: credit allowance retired — the trial is rate-limited per model
|
|
144
|
+
- `opencode`: no spend API for API keys
|
|
145
|
+
- `snowflake-cortex`: 100 credits/month, billed per token
|
|
146
|
+
- `zai`: no coding plan on this key
|
|
145
147
|
|
|
146
148
|
3. **budgets.json** — your monthly USD per provider, read against what the
|
|
147
149
|
provider cost so far this calendar month:
|
package/dist/cli.js
CHANGED
|
@@ -328,6 +328,36 @@ async function fetchAmd(key) {
|
|
|
328
328
|
}
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
+
// packages/core/src/quota/orcarouter.ts
|
|
332
|
+
var CENTS_PER_USD = 100;
|
|
333
|
+
var NO_CAP = 1e6;
|
|
334
|
+
function parseOrcaRouter(usage, subscription) {
|
|
335
|
+
const spent = (usage.total_usage ?? 0) / CENTS_PER_USD;
|
|
336
|
+
const hard = subscription.hard_limit_usd ?? 0;
|
|
337
|
+
const soft = subscription.soft_limit_usd ?? 0;
|
|
338
|
+
const cap = hard > 0 && hard < NO_CAP ? hard : soft > 0 && soft < NO_CAP ? soft : 0;
|
|
339
|
+
const capped = cap > 0;
|
|
340
|
+
const pct = capped ? spent / cap * 100 : 0;
|
|
341
|
+
return {
|
|
342
|
+
provider: "orcarouter",
|
|
343
|
+
ok: true,
|
|
344
|
+
detail: `$${spent.toFixed(2)} spent`,
|
|
345
|
+
windows: [{ label: "spend", percentUsed: pct, detail: `$${spent.toFixed(2)}` }],
|
|
346
|
+
budget: capped ? { percentUsed: pct, label: `$${cap}` } : undefined,
|
|
347
|
+
raw: { usage, subscription }
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
async function fetchOrcaRouter(key) {
|
|
351
|
+
try {
|
|
352
|
+
const headers = { Authorization: `Bearer ${key}`, Accept: "application/json" };
|
|
353
|
+
const usage = await getJson("https://api.orcarouter.ai/v1/dashboard/billing/usage", headers);
|
|
354
|
+
const subscription = await getJson("https://api.orcarouter.ai/v1/dashboard/billing/subscription", headers);
|
|
355
|
+
return parseOrcaRouter(usage, subscription);
|
|
356
|
+
} catch (err) {
|
|
357
|
+
return { provider: "orcarouter", ok: false, detail: err.message, windows: [] };
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
331
361
|
// packages/core/src/providers.ts
|
|
332
362
|
var TTL_MS = 15 * 60 * 1000;
|
|
333
363
|
async function readCache() {
|
|
@@ -350,7 +380,8 @@ var FETCHERS = {
|
|
|
350
380
|
openrouter: fetchOpenRouter,
|
|
351
381
|
"github-copilot": fetchCopilot,
|
|
352
382
|
zai: fetchZai,
|
|
353
|
-
amd: fetchAmd
|
|
383
|
+
amd: fetchAmd,
|
|
384
|
+
orcarouter: fetchOrcaRouter
|
|
354
385
|
};
|
|
355
386
|
async function providerStatuses(opts) {
|
|
356
387
|
const auth = await readAuth();
|
|
@@ -638,10 +669,10 @@ var PROVIDER_LIMITS = [
|
|
|
638
669
|
provider: "cloudflare-workers-ai",
|
|
639
670
|
unit: "neurons",
|
|
640
671
|
period: "day",
|
|
641
|
-
limit:
|
|
672
|
+
limit: 1e4,
|
|
642
673
|
tier: "free",
|
|
643
|
-
source: "https://developers.cloudflare.com/workers-ai/platform/
|
|
644
|
-
note: "A neuron is Cloudflare's own unit, per model.
|
|
674
|
+
source: "https://developers.cloudflare.com/workers-ai/platform/pricing/",
|
|
675
|
+
note: "10,000 neurons/day free, resets at 00:00 UTC. A neuron is Cloudflare's own unit, per model. Live usage needs an API token with the Analytics read scope; the Workers AI key does not carry one."
|
|
645
676
|
},
|
|
646
677
|
{
|
|
647
678
|
provider: "digitalocean",
|
|
@@ -674,10 +705,19 @@ var PROVIDER_LIMITS = [
|
|
|
674
705
|
provider: "nvidia",
|
|
675
706
|
unit: "credits",
|
|
676
707
|
period: "month",
|
|
677
|
-
limit:
|
|
708
|
+
limit: 0,
|
|
678
709
|
tier: "free",
|
|
679
|
-
source: "https://
|
|
680
|
-
note: "
|
|
710
|
+
source: "https://forums.developer.nvidia.com/t/request-more-4-000-credits-option-on-build-nvidia-com/344567",
|
|
711
|
+
note: "The credit allowance was retired; build.nvidia.com now rate-limits the trial per model (about 40 RPM, unpublished, shown in the UI header). No usage endpoint exists."
|
|
712
|
+
},
|
|
713
|
+
{
|
|
714
|
+
provider: "opencode",
|
|
715
|
+
unit: "tokens",
|
|
716
|
+
period: "month",
|
|
717
|
+
limit: 0,
|
|
718
|
+
tier: "unknown",
|
|
719
|
+
source: "https://opencode.ai/docs/go/",
|
|
720
|
+
note: "The opencode gateway exposes no spend API to its API key; the dashboard balance needs a browser session. Go usage is measured through the opencode-go provider instead."
|
|
681
721
|
},
|
|
682
722
|
{
|
|
683
723
|
provider: "orcarouter",
|
|
@@ -685,8 +725,8 @@ var PROVIDER_LIMITS = [
|
|
|
685
725
|
period: "day",
|
|
686
726
|
limit: 0,
|
|
687
727
|
tier: "unknown",
|
|
688
|
-
source: "
|
|
689
|
-
note: "
|
|
728
|
+
source: "https://docs.orcarouter.ai/operations/billing-and-usage",
|
|
729
|
+
note: "Pay-per-token at upstream rates with no markup. A per-key credit cap turns into a live budget; without one there is nothing to exhaust. Free models are rate-limited with unpublished numbers."
|
|
690
730
|
},
|
|
691
731
|
{
|
|
692
732
|
provider: "snowflake-cortex",
|
|
@@ -694,8 +734,17 @@ var PROVIDER_LIMITS = [
|
|
|
694
734
|
period: "month",
|
|
695
735
|
limit: 100,
|
|
696
736
|
tier: "paid",
|
|
697
|
-
source: "https://docs.snowflake.com/en/
|
|
698
|
-
note: "
|
|
737
|
+
source: "https://docs.snowflake.com/en/sql-reference/account-usage/cortex_ai_functions_usage_history",
|
|
738
|
+
note: "Cortex AI Functions bill per token (credits per million tokens, Service Consumption Table). Usage lives in ACCOUNT_USAGE views, which the API key alone cannot read."
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
provider: "zai",
|
|
742
|
+
unit: "tokens",
|
|
743
|
+
period: "day",
|
|
744
|
+
limit: 0,
|
|
745
|
+
tier: "unknown",
|
|
746
|
+
source: "https://openusage.sh/docs/providers/zai/",
|
|
747
|
+
note: "The quota monitor answers only keys with an active GLM coding plan; this key reports none, so the endpoint returns an error. With a plan, the monitor exposes a rolling 5h window plus weekly and monthly limits."
|
|
699
748
|
}
|
|
700
749
|
];
|
|
701
750
|
var MEASURED_UNITS = new Set(["tokens", "requests"]);
|
package/dist/tui.js
CHANGED
|
@@ -318,6 +318,36 @@ async function fetchAmd(key) {
|
|
|
318
318
|
}
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
+
// packages/core/src/quota/orcarouter.ts
|
|
322
|
+
var CENTS_PER_USD = 100;
|
|
323
|
+
var NO_CAP = 1e6;
|
|
324
|
+
function parseOrcaRouter(usage, subscription) {
|
|
325
|
+
const spent = (usage.total_usage ?? 0) / CENTS_PER_USD;
|
|
326
|
+
const hard = subscription.hard_limit_usd ?? 0;
|
|
327
|
+
const soft = subscription.soft_limit_usd ?? 0;
|
|
328
|
+
const cap = hard > 0 && hard < NO_CAP ? hard : soft > 0 && soft < NO_CAP ? soft : 0;
|
|
329
|
+
const capped = cap > 0;
|
|
330
|
+
const pct = capped ? spent / cap * 100 : 0;
|
|
331
|
+
return {
|
|
332
|
+
provider: "orcarouter",
|
|
333
|
+
ok: true,
|
|
334
|
+
detail: `$${spent.toFixed(2)} spent`,
|
|
335
|
+
windows: [{ label: "spend", percentUsed: pct, detail: `$${spent.toFixed(2)}` }],
|
|
336
|
+
budget: capped ? { percentUsed: pct, label: `$${cap}` } : undefined,
|
|
337
|
+
raw: { usage, subscription }
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
async function fetchOrcaRouter(key) {
|
|
341
|
+
try {
|
|
342
|
+
const headers = { Authorization: `Bearer ${key}`, Accept: "application/json" };
|
|
343
|
+
const usage = await getJson("https://api.orcarouter.ai/v1/dashboard/billing/usage", headers);
|
|
344
|
+
const subscription = await getJson("https://api.orcarouter.ai/v1/dashboard/billing/subscription", headers);
|
|
345
|
+
return parseOrcaRouter(usage, subscription);
|
|
346
|
+
} catch (err) {
|
|
347
|
+
return { provider: "orcarouter", ok: false, detail: err.message, windows: [] };
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
321
351
|
// packages/core/src/providers.ts
|
|
322
352
|
var TTL_MS = 15 * 60 * 1000;
|
|
323
353
|
async function readCache() {
|
|
@@ -340,7 +370,8 @@ var FETCHERS = {
|
|
|
340
370
|
openrouter: fetchOpenRouter,
|
|
341
371
|
"github-copilot": fetchCopilot,
|
|
342
372
|
zai: fetchZai,
|
|
343
|
-
amd: fetchAmd
|
|
373
|
+
amd: fetchAmd,
|
|
374
|
+
orcarouter: fetchOrcaRouter
|
|
344
375
|
};
|
|
345
376
|
async function providerStatuses(opts) {
|
|
346
377
|
const auth = await readAuth();
|
|
@@ -403,10 +434,10 @@ var PROVIDER_LIMITS = [
|
|
|
403
434
|
provider: "cloudflare-workers-ai",
|
|
404
435
|
unit: "neurons",
|
|
405
436
|
period: "day",
|
|
406
|
-
limit:
|
|
437
|
+
limit: 1e4,
|
|
407
438
|
tier: "free",
|
|
408
|
-
source: "https://developers.cloudflare.com/workers-ai/platform/
|
|
409
|
-
note: "A neuron is Cloudflare's own unit, per model.
|
|
439
|
+
source: "https://developers.cloudflare.com/workers-ai/platform/pricing/",
|
|
440
|
+
note: "10,000 neurons/day free, resets at 00:00 UTC. A neuron is Cloudflare's own unit, per model. Live usage needs an API token with the Analytics read scope; the Workers AI key does not carry one."
|
|
410
441
|
},
|
|
411
442
|
{
|
|
412
443
|
provider: "digitalocean",
|
|
@@ -439,10 +470,19 @@ var PROVIDER_LIMITS = [
|
|
|
439
470
|
provider: "nvidia",
|
|
440
471
|
unit: "credits",
|
|
441
472
|
period: "month",
|
|
442
|
-
limit:
|
|
473
|
+
limit: 0,
|
|
443
474
|
tier: "free",
|
|
444
|
-
source: "https://
|
|
445
|
-
note: "
|
|
475
|
+
source: "https://forums.developer.nvidia.com/t/request-more-4-000-credits-option-on-build-nvidia-com/344567",
|
|
476
|
+
note: "The credit allowance was retired; build.nvidia.com now rate-limits the trial per model (about 40 RPM, unpublished, shown in the UI header). No usage endpoint exists."
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
provider: "opencode",
|
|
480
|
+
unit: "tokens",
|
|
481
|
+
period: "month",
|
|
482
|
+
limit: 0,
|
|
483
|
+
tier: "unknown",
|
|
484
|
+
source: "https://opencode.ai/docs/go/",
|
|
485
|
+
note: "The opencode gateway exposes no spend API to its API key; the dashboard balance needs a browser session. Go usage is measured through the opencode-go provider instead."
|
|
446
486
|
},
|
|
447
487
|
{
|
|
448
488
|
provider: "orcarouter",
|
|
@@ -450,8 +490,8 @@ var PROVIDER_LIMITS = [
|
|
|
450
490
|
period: "day",
|
|
451
491
|
limit: 0,
|
|
452
492
|
tier: "unknown",
|
|
453
|
-
source: "
|
|
454
|
-
note: "
|
|
493
|
+
source: "https://docs.orcarouter.ai/operations/billing-and-usage",
|
|
494
|
+
note: "Pay-per-token at upstream rates with no markup. A per-key credit cap turns into a live budget; without one there is nothing to exhaust. Free models are rate-limited with unpublished numbers."
|
|
455
495
|
},
|
|
456
496
|
{
|
|
457
497
|
provider: "snowflake-cortex",
|
|
@@ -459,8 +499,17 @@ var PROVIDER_LIMITS = [
|
|
|
459
499
|
period: "month",
|
|
460
500
|
limit: 100,
|
|
461
501
|
tier: "paid",
|
|
462
|
-
source: "https://docs.snowflake.com/en/
|
|
463
|
-
note: "
|
|
502
|
+
source: "https://docs.snowflake.com/en/sql-reference/account-usage/cortex_ai_functions_usage_history",
|
|
503
|
+
note: "Cortex AI Functions bill per token (credits per million tokens, Service Consumption Table). Usage lives in ACCOUNT_USAGE views, which the API key alone cannot read."
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
provider: "zai",
|
|
507
|
+
unit: "tokens",
|
|
508
|
+
period: "day",
|
|
509
|
+
limit: 0,
|
|
510
|
+
tier: "unknown",
|
|
511
|
+
source: "https://openusage.sh/docs/providers/zai/",
|
|
512
|
+
note: "The quota monitor answers only keys with an active GLM coding plan; this key reports none, so the endpoint returns an error. With a plan, the monitor exposes a rolling 5h window plus weekly and monthly limits."
|
|
464
513
|
}
|
|
465
514
|
];
|
|
466
515
|
var MEASURED_UNITS = new Set(["tokens", "requests"]);
|
|
@@ -580,7 +629,8 @@ async function getUsageSnapshot(sinceMs, groupBy, includePct) {
|
|
|
580
629
|
}
|
|
581
630
|
// packages/plugin/layout.ts
|
|
582
631
|
var SIZE_WIDTH = { medium: 60, large: 88, xlarge: 116 };
|
|
583
|
-
var
|
|
632
|
+
var PADDING_X = 2;
|
|
633
|
+
var PADDING_Y = 1;
|
|
584
634
|
var WIDE_BAR = 12;
|
|
585
635
|
var COMPACT_BAR = 6;
|
|
586
636
|
var BUDGET_FIXED = WIDE_BAR + 6;
|
|
@@ -680,7 +730,7 @@ function barColor(pct, p) {
|
|
|
680
730
|
}
|
|
681
731
|
function chooseSize(terminal, needed) {
|
|
682
732
|
const held = (s) => terminal >= SIZE_WIDTH[s] + 2;
|
|
683
|
-
if (held("large") && SIZE_WIDTH.large -
|
|
733
|
+
if (held("large") && SIZE_WIDTH.large - PADDING_X * 2 >= needed)
|
|
684
734
|
return "large";
|
|
685
735
|
if (held("xlarge"))
|
|
686
736
|
return "xlarge";
|
|
@@ -715,7 +765,7 @@ function terminalWidth(api) {
|
|
|
715
765
|
}
|
|
716
766
|
function innerWidth(api, needed) {
|
|
717
767
|
const terminal = terminalWidth(api);
|
|
718
|
-
return Math.min(SIZE_WIDTH[chooseSize(terminal, needed)], terminal - 2) -
|
|
768
|
+
return Math.min(SIZE_WIDTH[chooseSize(terminal, needed)], terminal - 2) - PADDING_X * 2;
|
|
719
769
|
}
|
|
720
770
|
function neededWidth(snapshot) {
|
|
721
771
|
const labels = Object.values(snapshot.pct ?? {}).map((b) => compactLabel(b.label ?? b.source).length);
|
|
@@ -726,7 +776,8 @@ function Frame(props) {
|
|
|
726
776
|
return /* @__PURE__ */ jsxs("box", {
|
|
727
777
|
flexDirection: "column",
|
|
728
778
|
flexShrink: 0,
|
|
729
|
-
|
|
779
|
+
paddingX: PADDING_X,
|
|
780
|
+
paddingY: PADDING_Y,
|
|
730
781
|
children: [
|
|
731
782
|
/* @__PURE__ */ jsxs("box", {
|
|
732
783
|
flexDirection: "row",
|