@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 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
- - `zai`: coding plan quota
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`: 100k neurons/day (free tier)
142
- - `nvidia`: 1000 credits/month (free tier)
143
- - `orcarouter`: undocumented
144
- - `snowflake-cortex`: 100 credits/month (paid)
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: 1e5,
672
+ limit: 1e4,
642
673
  tier: "free",
643
- source: "https://developers.cloudflare.com/workers-ai/platform/limits/",
644
- note: "A neuron is Cloudflare's own unit, per model. Not measurable from tokens."
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: 1000,
708
+ limit: 0,
678
709
  tier: "free",
679
- source: "https://build.nvidia.com/",
680
- note: "NIM free tier ~1000 credits/month. A credit varies by model."
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: "unknown",
689
- note: "Proxy service. No public limits documented."
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/user-guide/snowflake-cortex",
698
- note: "Credits follow warehouse time, not tokens. Not measurable from usage."
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: 1e5,
437
+ limit: 1e4,
407
438
  tier: "free",
408
- source: "https://developers.cloudflare.com/workers-ai/platform/limits/",
409
- note: "A neuron is Cloudflare's own unit, per model. Not measurable from tokens."
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: 1000,
473
+ limit: 0,
443
474
  tier: "free",
444
- source: "https://build.nvidia.com/",
445
- note: "NIM free tier ~1000 credits/month. A credit varies by model."
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: "unknown",
454
- note: "Proxy service. No public limits documented."
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/user-guide/snowflake-cortex",
463
- note: "Credits follow warehouse time, not tokens. Not measurable from usage."
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 PADDING = 1;
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 - PADDING * 2 >= needed)
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) - PADDING * 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
- padding: PADDING,
779
+ paddingX: PADDING_X,
780
+ paddingY: PADDING_Y,
730
781
  children: [
731
782
  /* @__PURE__ */ jsxs("box", {
732
783
  flexDirection: "row",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1930dev/opencode-usage",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Usage tracking, budget percentages and model ranking for opencode — CLI plus a /usage TUI plugin",
5
5
  "type": "module",
6
6
  "bin": {