@1930dev/opencode-usage 0.3.2 → 0.3.4
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 +11 -5
- package/dist/cli.js +149 -20
- package/dist/tui.js +148 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -123,7 +123,12 @@ 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 when the key has an active GLM plan; otherwise the
|
|
128
|
+
wallet balance (fallback)
|
|
129
|
+
- `snowflake-cortex`: tokens in the last 30d from the account usage views
|
|
130
|
+
(SQL API); the percentage comes from a `snowflake-cortex` line in
|
|
131
|
+
`budgets.json`
|
|
127
132
|
|
|
128
133
|
2. **Documented limits** — published quotas, used when the provider reports none.
|
|
129
134
|
Each one is read against the period it resets on: a daily limit against today,
|
|
@@ -138,10 +143,11 @@ Normalized percentage of budget consumed per provider, from these sources (in pr
|
|
|
138
143
|
records tokens and requests; a credit and a neuron are the provider's own
|
|
139
144
|
unit, derived from the model and the request by a rule it does not record.
|
|
140
145
|
Give them a line in `budgets.json` to get a percentage in USD instead.
|
|
141
|
-
- `cloudflare-workers-ai`:
|
|
142
|
-
- `nvidia`:
|
|
143
|
-
- `
|
|
144
|
-
- `
|
|
146
|
+
- `cloudflare-workers-ai`: 10k neurons/day (free tier, resets 00:00 UTC)
|
|
147
|
+
- `nvidia`: credit allowance retired — the trial is rate-limited per model
|
|
148
|
+
- `opencode`: no spend API for API keys
|
|
149
|
+
- `zai`: a key without a coding plan shows its wallet balance (live), and the
|
|
150
|
+
percentage comes from a `zai` line in `budgets.json`
|
|
145
151
|
|
|
146
152
|
3. **budgets.json** — your monthly USD per provider, read against what the
|
|
147
153
|
provider cost so far this calendar month:
|
package/dist/cli.js
CHANGED
|
@@ -284,12 +284,39 @@ function parseZai(data) {
|
|
|
284
284
|
raw: data
|
|
285
285
|
};
|
|
286
286
|
}
|
|
287
|
+
function parseZaiWallet(data) {
|
|
288
|
+
const failed = data.success === false || data.code !== undefined && data.code !== 0 && data.code !== 200;
|
|
289
|
+
if (failed) {
|
|
290
|
+
return { provider: "zai", ok: false, detail: data.msg ?? "wallet endpoint error", windows: [] };
|
|
291
|
+
}
|
|
292
|
+
const balance = data.data?.availableBalance ?? data.data?.rechargeAmount ?? 0;
|
|
293
|
+
const spend = data.data?.totalSpendAmount ?? 0;
|
|
294
|
+
const windows = [
|
|
295
|
+
{ label: "balance", percentUsed: 0, detail: `$${balance.toFixed(2)}` },
|
|
296
|
+
{ label: "spend", percentUsed: 0, detail: `$${spend.toFixed(2)}` }
|
|
297
|
+
];
|
|
298
|
+
const today = data.data?.todaySpendAmount;
|
|
299
|
+
if (typeof today === "number") {
|
|
300
|
+
windows.push({ label: "today", percentUsed: 0, detail: `$${today.toFixed(2)}` });
|
|
301
|
+
}
|
|
302
|
+
return {
|
|
303
|
+
provider: "zai",
|
|
304
|
+
ok: true,
|
|
305
|
+
detail: `balance $${balance.toFixed(2)}`,
|
|
306
|
+
windows,
|
|
307
|
+
raw: data
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
var QUOTA_URL = "https://api.z.ai/api/monitor/usage/quota/limit";
|
|
311
|
+
var WALLET_URL = "https://api.z.ai/api/biz/account/query-customer-account-report";
|
|
287
312
|
async function fetchZai(key) {
|
|
288
313
|
try {
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
314
|
+
const auth = { Authorization: `Bearer ${key}`, Accept: "application/json" };
|
|
315
|
+
const data = await getJson(QUOTA_URL, auth);
|
|
316
|
+
if (data.code !== undefined && data.code !== 0) {
|
|
317
|
+
const wallet = await getJson(WALLET_URL, auth);
|
|
318
|
+
return parseZaiWallet(wallet);
|
|
319
|
+
}
|
|
293
320
|
return parseZai(data);
|
|
294
321
|
} catch (err) {
|
|
295
322
|
return { provider: "zai", ok: false, detail: err.message, windows: [] };
|
|
@@ -328,6 +355,88 @@ async function fetchAmd(key) {
|
|
|
328
355
|
}
|
|
329
356
|
}
|
|
330
357
|
|
|
358
|
+
// packages/core/src/quota/orcarouter.ts
|
|
359
|
+
var CENTS_PER_USD = 100;
|
|
360
|
+
var NO_CAP = 1e6;
|
|
361
|
+
function parseOrcaRouter(usage, subscription) {
|
|
362
|
+
const spent = (usage.total_usage ?? 0) / CENTS_PER_USD;
|
|
363
|
+
const hard = subscription.hard_limit_usd ?? 0;
|
|
364
|
+
const soft = subscription.soft_limit_usd ?? 0;
|
|
365
|
+
const cap = hard > 0 && hard < NO_CAP ? hard : soft > 0 && soft < NO_CAP ? soft : 0;
|
|
366
|
+
const capped = cap > 0;
|
|
367
|
+
const pct = capped ? spent / cap * 100 : 0;
|
|
368
|
+
const hint = capped ? "" : " (no cap set)";
|
|
369
|
+
const detail = `$${spent.toFixed(2)} spent${hint}`;
|
|
370
|
+
return {
|
|
371
|
+
provider: "orcarouter",
|
|
372
|
+
ok: true,
|
|
373
|
+
detail,
|
|
374
|
+
windows: [{ label: "spend", percentUsed: pct, detail: `$${spent.toFixed(2)}${hint}` }],
|
|
375
|
+
budget: capped ? { percentUsed: pct, label: `$${cap}` } : undefined,
|
|
376
|
+
raw: { usage, subscription }
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
async function fetchOrcaRouter(key) {
|
|
380
|
+
try {
|
|
381
|
+
const headers = { Authorization: `Bearer ${key}`, Accept: "application/json" };
|
|
382
|
+
const usage = await getJson("https://api.orcarouter.ai/v1/dashboard/billing/usage", headers);
|
|
383
|
+
const subscription = await getJson("https://api.orcarouter.ai/v1/dashboard/billing/subscription", headers);
|
|
384
|
+
return parseOrcaRouter(usage, subscription);
|
|
385
|
+
} catch (err) {
|
|
386
|
+
return { provider: "orcarouter", ok: false, detail: err.message, windows: [] };
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// packages/core/src/quota/snowflake.ts
|
|
391
|
+
var SQL_API = (account) => `https://${account}.snowflakecomputing.com/api/v2/statements`;
|
|
392
|
+
var STATEMENT = `SELECT
|
|
393
|
+
SUM(CASE WHEN START_TIME >= DATE_TRUNC('day', CURRENT_TIMESTAMP())
|
|
394
|
+
AND START_TIME < DATEADD('day', 1, DATE_TRUNC('day', CURRENT_TIMESTAMP()))
|
|
395
|
+
THEN TOKENS ELSE 0 END) AS today_tokens,
|
|
396
|
+
SUM(TOKENS) AS month_tokens
|
|
397
|
+
FROM SNOWFLAKE.ACCOUNT_USAGE.CORTEX_REST_API_USAGE_HISTORY
|
|
398
|
+
WHERE START_TIME >= DATEADD('day', -30, CURRENT_TIMESTAMP())`;
|
|
399
|
+
var fmtTokens = (n) => n.toLocaleString("en-US");
|
|
400
|
+
function parseSnowflake(row) {
|
|
401
|
+
const today = Number(row?.[0] ?? 0);
|
|
402
|
+
const month = Number(row?.[1] ?? 0);
|
|
403
|
+
return {
|
|
404
|
+
provider: "snowflake-cortex",
|
|
405
|
+
ok: true,
|
|
406
|
+
detail: `${fmtTokens(month)} tokens/30d`,
|
|
407
|
+
windows: [
|
|
408
|
+
{ label: "tokens/mo", percentUsed: 0, detail: `${fmtTokens(month)}` },
|
|
409
|
+
{ label: "today", percentUsed: 0, detail: `${fmtTokens(today)}` }
|
|
410
|
+
],
|
|
411
|
+
raw: { row }
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
async function fetchSnowflake(key, metadata) {
|
|
415
|
+
const account = String(metadata?.account ?? "").toUpperCase();
|
|
416
|
+
if (!account) {
|
|
417
|
+
return { provider: "snowflake-cortex", ok: false, detail: "metadata.account is required", windows: [] };
|
|
418
|
+
}
|
|
419
|
+
try {
|
|
420
|
+
const res = await fetch(SQL_API(account), {
|
|
421
|
+
method: "POST",
|
|
422
|
+
headers: {
|
|
423
|
+
Authorization: `Bearer ${key}`,
|
|
424
|
+
"Content-Type": "application/json",
|
|
425
|
+
Accept: "application/json"
|
|
426
|
+
},
|
|
427
|
+
body: JSON.stringify({ statement: STATEMENT, timeout: 30 })
|
|
428
|
+
});
|
|
429
|
+
const body = await res.json().catch(() => null);
|
|
430
|
+
if (!res.ok) {
|
|
431
|
+
return { provider: "snowflake-cortex", ok: false, detail: body?.message ?? `HTTP ${res.status}`, windows: [] };
|
|
432
|
+
}
|
|
433
|
+
const rows = Array.isArray(body?.data) ? body.data : [];
|
|
434
|
+
return parseSnowflake(rows[0]);
|
|
435
|
+
} catch (err) {
|
|
436
|
+
return { provider: "snowflake-cortex", ok: false, detail: err.message, windows: [] };
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
331
440
|
// packages/core/src/providers.ts
|
|
332
441
|
var TTL_MS = 15 * 60 * 1000;
|
|
333
442
|
async function readCache() {
|
|
@@ -350,7 +459,9 @@ var FETCHERS = {
|
|
|
350
459
|
openrouter: fetchOpenRouter,
|
|
351
460
|
"github-copilot": fetchCopilot,
|
|
352
461
|
zai: fetchZai,
|
|
353
|
-
amd: fetchAmd
|
|
462
|
+
amd: fetchAmd,
|
|
463
|
+
orcarouter: fetchOrcaRouter,
|
|
464
|
+
"snowflake-cortex": fetchSnowflake
|
|
354
465
|
};
|
|
355
466
|
async function providerStatuses(opts) {
|
|
356
467
|
const auth = await readAuth();
|
|
@@ -371,7 +482,7 @@ async function providerStatuses(opts) {
|
|
|
371
482
|
const secret = authSecret(auth[p]);
|
|
372
483
|
if (!fetcher || !secret)
|
|
373
484
|
return null;
|
|
374
|
-
return fetcher(secret);
|
|
485
|
+
return fetcher(secret, auth[p].metadata);
|
|
375
486
|
}));
|
|
376
487
|
for (const q of live) {
|
|
377
488
|
if (q)
|
|
@@ -394,7 +505,7 @@ async function providerStatuses(opts) {
|
|
|
394
505
|
}));
|
|
395
506
|
}
|
|
396
507
|
// packages/core/src/report.ts
|
|
397
|
-
function
|
|
508
|
+
function fmtTokens2(n) {
|
|
398
509
|
if (n >= 1e9)
|
|
399
510
|
return `${(n / 1e9).toFixed(1)}B`;
|
|
400
511
|
if (n >= 1e6)
|
|
@@ -436,8 +547,8 @@ function usageTable(rows, totals, sinceLabel, groupBy, pct) {
|
|
|
436
547
|
const row = [
|
|
437
548
|
r.group,
|
|
438
549
|
String(r.messages),
|
|
439
|
-
|
|
440
|
-
|
|
550
|
+
fmtTokens2(r.tokensInput),
|
|
551
|
+
fmtTokens2(r.tokensOutput),
|
|
441
552
|
fmtCost(r.cost)
|
|
442
553
|
];
|
|
443
554
|
if (pct) {
|
|
@@ -454,7 +565,7 @@ function usageTable(rows, totals, sinceLabel, groupBy, pct) {
|
|
|
454
565
|
[sources: ${[...pct.values()].map((p) => `${p.source}${p.label ? `(${p.label})` : ""}`).filter((v, i, a) => a.indexOf(v) === i).join(", ")}]` : "";
|
|
455
566
|
return `${t}
|
|
456
567
|
|
|
457
|
-
TOTAL since ${sinceLabel}: ${totals.messages} msgs, ${
|
|
568
|
+
TOTAL since ${sinceLabel}: ${totals.messages} msgs, ${fmtTokens2(totals.tokensInput)} in / ${fmtTokens2(totals.tokensOutput)} out, ${fmtCost(totals.cost)} est.${sourceNote}`;
|
|
458
569
|
}
|
|
459
570
|
function providersTable(statuses, local) {
|
|
460
571
|
const headers = ["PROVIDER", "QUOTA", "USAGE 7D (LOCAL)", "LAST USED"];
|
|
@@ -638,10 +749,10 @@ var PROVIDER_LIMITS = [
|
|
|
638
749
|
provider: "cloudflare-workers-ai",
|
|
639
750
|
unit: "neurons",
|
|
640
751
|
period: "day",
|
|
641
|
-
limit:
|
|
752
|
+
limit: 1e4,
|
|
642
753
|
tier: "free",
|
|
643
|
-
source: "https://developers.cloudflare.com/workers-ai/platform/
|
|
644
|
-
note: "A neuron is Cloudflare's own unit, per model.
|
|
754
|
+
source: "https://developers.cloudflare.com/workers-ai/platform/pricing/",
|
|
755
|
+
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
756
|
},
|
|
646
757
|
{
|
|
647
758
|
provider: "digitalocean",
|
|
@@ -674,10 +785,19 @@ var PROVIDER_LIMITS = [
|
|
|
674
785
|
provider: "nvidia",
|
|
675
786
|
unit: "credits",
|
|
676
787
|
period: "month",
|
|
677
|
-
limit:
|
|
788
|
+
limit: 0,
|
|
678
789
|
tier: "free",
|
|
679
|
-
source: "https://
|
|
680
|
-
note: "
|
|
790
|
+
source: "https://forums.developer.nvidia.com/t/request-more-4-000-credits-option-on-build-nvidia-com/344567",
|
|
791
|
+
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."
|
|
792
|
+
},
|
|
793
|
+
{
|
|
794
|
+
provider: "opencode",
|
|
795
|
+
unit: "tokens",
|
|
796
|
+
period: "month",
|
|
797
|
+
limit: 0,
|
|
798
|
+
tier: "unknown",
|
|
799
|
+
source: "https://opencode.ai/docs/go/",
|
|
800
|
+
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
801
|
},
|
|
682
802
|
{
|
|
683
803
|
provider: "orcarouter",
|
|
@@ -685,8 +805,8 @@ var PROVIDER_LIMITS = [
|
|
|
685
805
|
period: "day",
|
|
686
806
|
limit: 0,
|
|
687
807
|
tier: "unknown",
|
|
688
|
-
source: "
|
|
689
|
-
note: "
|
|
808
|
+
source: "https://docs.orcarouter.ai/operations/billing-and-usage",
|
|
809
|
+
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
810
|
},
|
|
691
811
|
{
|
|
692
812
|
provider: "snowflake-cortex",
|
|
@@ -694,8 +814,17 @@ var PROVIDER_LIMITS = [
|
|
|
694
814
|
period: "month",
|
|
695
815
|
limit: 100,
|
|
696
816
|
tier: "paid",
|
|
697
|
-
source: "https://docs.snowflake.com/en/
|
|
698
|
-
note: "
|
|
817
|
+
source: "https://docs.snowflake.com/en/sql-reference/account-usage/cortex_ai_functions_usage_history",
|
|
818
|
+
note: "Billed per token (Service Consumption Table). Live tokens come from ACCOUNT_USAGE.CORTEX_REST_API_USAGE_HISTORY through the SQL API with the stored JWT; the percentage needs a snowflake-cortex line in budgets.json."
|
|
819
|
+
},
|
|
820
|
+
{
|
|
821
|
+
provider: "zai",
|
|
822
|
+
unit: "tokens",
|
|
823
|
+
period: "day",
|
|
824
|
+
limit: 0,
|
|
825
|
+
tier: "unknown",
|
|
826
|
+
source: "https://openusage.sh/docs/providers/zai/",
|
|
827
|
+
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
828
|
}
|
|
700
829
|
];
|
|
701
830
|
var MEASURED_UNITS = new Set(["tokens", "requests"]);
|
package/dist/tui.js
CHANGED
|
@@ -274,12 +274,39 @@ function parseZai(data) {
|
|
|
274
274
|
raw: data
|
|
275
275
|
};
|
|
276
276
|
}
|
|
277
|
+
function parseZaiWallet(data) {
|
|
278
|
+
const failed = data.success === false || data.code !== undefined && data.code !== 0 && data.code !== 200;
|
|
279
|
+
if (failed) {
|
|
280
|
+
return { provider: "zai", ok: false, detail: data.msg ?? "wallet endpoint error", windows: [] };
|
|
281
|
+
}
|
|
282
|
+
const balance = data.data?.availableBalance ?? data.data?.rechargeAmount ?? 0;
|
|
283
|
+
const spend = data.data?.totalSpendAmount ?? 0;
|
|
284
|
+
const windows = [
|
|
285
|
+
{ label: "balance", percentUsed: 0, detail: `$${balance.toFixed(2)}` },
|
|
286
|
+
{ label: "spend", percentUsed: 0, detail: `$${spend.toFixed(2)}` }
|
|
287
|
+
];
|
|
288
|
+
const today = data.data?.todaySpendAmount;
|
|
289
|
+
if (typeof today === "number") {
|
|
290
|
+
windows.push({ label: "today", percentUsed: 0, detail: `$${today.toFixed(2)}` });
|
|
291
|
+
}
|
|
292
|
+
return {
|
|
293
|
+
provider: "zai",
|
|
294
|
+
ok: true,
|
|
295
|
+
detail: `balance $${balance.toFixed(2)}`,
|
|
296
|
+
windows,
|
|
297
|
+
raw: data
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
var QUOTA_URL = "https://api.z.ai/api/monitor/usage/quota/limit";
|
|
301
|
+
var WALLET_URL = "https://api.z.ai/api/biz/account/query-customer-account-report";
|
|
277
302
|
async function fetchZai(key) {
|
|
278
303
|
try {
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
304
|
+
const auth = { Authorization: `Bearer ${key}`, Accept: "application/json" };
|
|
305
|
+
const data = await getJson(QUOTA_URL, auth);
|
|
306
|
+
if (data.code !== undefined && data.code !== 0) {
|
|
307
|
+
const wallet = await getJson(WALLET_URL, auth);
|
|
308
|
+
return parseZaiWallet(wallet);
|
|
309
|
+
}
|
|
283
310
|
return parseZai(data);
|
|
284
311
|
} catch (err) {
|
|
285
312
|
return { provider: "zai", ok: false, detail: err.message, windows: [] };
|
|
@@ -318,6 +345,88 @@ async function fetchAmd(key) {
|
|
|
318
345
|
}
|
|
319
346
|
}
|
|
320
347
|
|
|
348
|
+
// packages/core/src/quota/orcarouter.ts
|
|
349
|
+
var CENTS_PER_USD = 100;
|
|
350
|
+
var NO_CAP = 1e6;
|
|
351
|
+
function parseOrcaRouter(usage, subscription) {
|
|
352
|
+
const spent = (usage.total_usage ?? 0) / CENTS_PER_USD;
|
|
353
|
+
const hard = subscription.hard_limit_usd ?? 0;
|
|
354
|
+
const soft = subscription.soft_limit_usd ?? 0;
|
|
355
|
+
const cap = hard > 0 && hard < NO_CAP ? hard : soft > 0 && soft < NO_CAP ? soft : 0;
|
|
356
|
+
const capped = cap > 0;
|
|
357
|
+
const pct = capped ? spent / cap * 100 : 0;
|
|
358
|
+
const hint = capped ? "" : " (no cap set)";
|
|
359
|
+
const detail = `$${spent.toFixed(2)} spent${hint}`;
|
|
360
|
+
return {
|
|
361
|
+
provider: "orcarouter",
|
|
362
|
+
ok: true,
|
|
363
|
+
detail,
|
|
364
|
+
windows: [{ label: "spend", percentUsed: pct, detail: `$${spent.toFixed(2)}${hint}` }],
|
|
365
|
+
budget: capped ? { percentUsed: pct, label: `$${cap}` } : undefined,
|
|
366
|
+
raw: { usage, subscription }
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
async function fetchOrcaRouter(key) {
|
|
370
|
+
try {
|
|
371
|
+
const headers = { Authorization: `Bearer ${key}`, Accept: "application/json" };
|
|
372
|
+
const usage = await getJson("https://api.orcarouter.ai/v1/dashboard/billing/usage", headers);
|
|
373
|
+
const subscription = await getJson("https://api.orcarouter.ai/v1/dashboard/billing/subscription", headers);
|
|
374
|
+
return parseOrcaRouter(usage, subscription);
|
|
375
|
+
} catch (err) {
|
|
376
|
+
return { provider: "orcarouter", ok: false, detail: err.message, windows: [] };
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// packages/core/src/quota/snowflake.ts
|
|
381
|
+
var SQL_API = (account) => `https://${account}.snowflakecomputing.com/api/v2/statements`;
|
|
382
|
+
var STATEMENT = `SELECT
|
|
383
|
+
SUM(CASE WHEN START_TIME >= DATE_TRUNC('day', CURRENT_TIMESTAMP())
|
|
384
|
+
AND START_TIME < DATEADD('day', 1, DATE_TRUNC('day', CURRENT_TIMESTAMP()))
|
|
385
|
+
THEN TOKENS ELSE 0 END) AS today_tokens,
|
|
386
|
+
SUM(TOKENS) AS month_tokens
|
|
387
|
+
FROM SNOWFLAKE.ACCOUNT_USAGE.CORTEX_REST_API_USAGE_HISTORY
|
|
388
|
+
WHERE START_TIME >= DATEADD('day', -30, CURRENT_TIMESTAMP())`;
|
|
389
|
+
var fmtTokens = (n) => n.toLocaleString("en-US");
|
|
390
|
+
function parseSnowflake(row) {
|
|
391
|
+
const today = Number(row?.[0] ?? 0);
|
|
392
|
+
const month = Number(row?.[1] ?? 0);
|
|
393
|
+
return {
|
|
394
|
+
provider: "snowflake-cortex",
|
|
395
|
+
ok: true,
|
|
396
|
+
detail: `${fmtTokens(month)} tokens/30d`,
|
|
397
|
+
windows: [
|
|
398
|
+
{ label: "tokens/mo", percentUsed: 0, detail: `${fmtTokens(month)}` },
|
|
399
|
+
{ label: "today", percentUsed: 0, detail: `${fmtTokens(today)}` }
|
|
400
|
+
],
|
|
401
|
+
raw: { row }
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
async function fetchSnowflake(key, metadata) {
|
|
405
|
+
const account = String(metadata?.account ?? "").toUpperCase();
|
|
406
|
+
if (!account) {
|
|
407
|
+
return { provider: "snowflake-cortex", ok: false, detail: "metadata.account is required", windows: [] };
|
|
408
|
+
}
|
|
409
|
+
try {
|
|
410
|
+
const res = await fetch(SQL_API(account), {
|
|
411
|
+
method: "POST",
|
|
412
|
+
headers: {
|
|
413
|
+
Authorization: `Bearer ${key}`,
|
|
414
|
+
"Content-Type": "application/json",
|
|
415
|
+
Accept: "application/json"
|
|
416
|
+
},
|
|
417
|
+
body: JSON.stringify({ statement: STATEMENT, timeout: 30 })
|
|
418
|
+
});
|
|
419
|
+
const body = await res.json().catch(() => null);
|
|
420
|
+
if (!res.ok) {
|
|
421
|
+
return { provider: "snowflake-cortex", ok: false, detail: body?.message ?? `HTTP ${res.status}`, windows: [] };
|
|
422
|
+
}
|
|
423
|
+
const rows = Array.isArray(body?.data) ? body.data : [];
|
|
424
|
+
return parseSnowflake(rows[0]);
|
|
425
|
+
} catch (err) {
|
|
426
|
+
return { provider: "snowflake-cortex", ok: false, detail: err.message, windows: [] };
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
321
430
|
// packages/core/src/providers.ts
|
|
322
431
|
var TTL_MS = 15 * 60 * 1000;
|
|
323
432
|
async function readCache() {
|
|
@@ -340,7 +449,9 @@ var FETCHERS = {
|
|
|
340
449
|
openrouter: fetchOpenRouter,
|
|
341
450
|
"github-copilot": fetchCopilot,
|
|
342
451
|
zai: fetchZai,
|
|
343
|
-
amd: fetchAmd
|
|
452
|
+
amd: fetchAmd,
|
|
453
|
+
orcarouter: fetchOrcaRouter,
|
|
454
|
+
"snowflake-cortex": fetchSnowflake
|
|
344
455
|
};
|
|
345
456
|
async function providerStatuses(opts) {
|
|
346
457
|
const auth = await readAuth();
|
|
@@ -361,7 +472,7 @@ async function providerStatuses(opts) {
|
|
|
361
472
|
const secret = authSecret(auth[p]);
|
|
362
473
|
if (!fetcher || !secret)
|
|
363
474
|
return null;
|
|
364
|
-
return fetcher(secret);
|
|
475
|
+
return fetcher(secret, auth[p].metadata);
|
|
365
476
|
}));
|
|
366
477
|
for (const q of live) {
|
|
367
478
|
if (q)
|
|
@@ -403,10 +514,10 @@ var PROVIDER_LIMITS = [
|
|
|
403
514
|
provider: "cloudflare-workers-ai",
|
|
404
515
|
unit: "neurons",
|
|
405
516
|
period: "day",
|
|
406
|
-
limit:
|
|
517
|
+
limit: 1e4,
|
|
407
518
|
tier: "free",
|
|
408
|
-
source: "https://developers.cloudflare.com/workers-ai/platform/
|
|
409
|
-
note: "A neuron is Cloudflare's own unit, per model.
|
|
519
|
+
source: "https://developers.cloudflare.com/workers-ai/platform/pricing/",
|
|
520
|
+
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
521
|
},
|
|
411
522
|
{
|
|
412
523
|
provider: "digitalocean",
|
|
@@ -439,10 +550,19 @@ var PROVIDER_LIMITS = [
|
|
|
439
550
|
provider: "nvidia",
|
|
440
551
|
unit: "credits",
|
|
441
552
|
period: "month",
|
|
442
|
-
limit:
|
|
553
|
+
limit: 0,
|
|
443
554
|
tier: "free",
|
|
444
|
-
source: "https://
|
|
445
|
-
note: "
|
|
555
|
+
source: "https://forums.developer.nvidia.com/t/request-more-4-000-credits-option-on-build-nvidia-com/344567",
|
|
556
|
+
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."
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
provider: "opencode",
|
|
560
|
+
unit: "tokens",
|
|
561
|
+
period: "month",
|
|
562
|
+
limit: 0,
|
|
563
|
+
tier: "unknown",
|
|
564
|
+
source: "https://opencode.ai/docs/go/",
|
|
565
|
+
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
566
|
},
|
|
447
567
|
{
|
|
448
568
|
provider: "orcarouter",
|
|
@@ -450,8 +570,8 @@ var PROVIDER_LIMITS = [
|
|
|
450
570
|
period: "day",
|
|
451
571
|
limit: 0,
|
|
452
572
|
tier: "unknown",
|
|
453
|
-
source: "
|
|
454
|
-
note: "
|
|
573
|
+
source: "https://docs.orcarouter.ai/operations/billing-and-usage",
|
|
574
|
+
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
575
|
},
|
|
456
576
|
{
|
|
457
577
|
provider: "snowflake-cortex",
|
|
@@ -459,8 +579,17 @@ var PROVIDER_LIMITS = [
|
|
|
459
579
|
period: "month",
|
|
460
580
|
limit: 100,
|
|
461
581
|
tier: "paid",
|
|
462
|
-
source: "https://docs.snowflake.com/en/
|
|
463
|
-
note: "
|
|
582
|
+
source: "https://docs.snowflake.com/en/sql-reference/account-usage/cortex_ai_functions_usage_history",
|
|
583
|
+
note: "Billed per token (Service Consumption Table). Live tokens come from ACCOUNT_USAGE.CORTEX_REST_API_USAGE_HISTORY through the SQL API with the stored JWT; the percentage needs a snowflake-cortex line in budgets.json."
|
|
584
|
+
},
|
|
585
|
+
{
|
|
586
|
+
provider: "zai",
|
|
587
|
+
unit: "tokens",
|
|
588
|
+
period: "day",
|
|
589
|
+
limit: 0,
|
|
590
|
+
tier: "unknown",
|
|
591
|
+
source: "https://openusage.sh/docs/providers/zai/",
|
|
592
|
+
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
593
|
}
|
|
465
594
|
];
|
|
466
595
|
var MEASURED_UNITS = new Set(["tokens", "requests"]);
|
|
@@ -606,7 +735,7 @@ function columnsFor(inner) {
|
|
|
606
735
|
const budget = Math.max(BUDGET_MIN, inner - used);
|
|
607
736
|
return { cols: [...base, { title: "BUDGET", width: budget, align: "left" }], bar: wide ? WIDE_BAR : COMPACT_BAR };
|
|
608
737
|
}
|
|
609
|
-
function
|
|
738
|
+
function fmtTokens2(n) {
|
|
610
739
|
if (n >= 1e6)
|
|
611
740
|
return `${(n / 1e6).toFixed(1)}M`;
|
|
612
741
|
if (n >= 1000)
|
|
@@ -772,8 +901,8 @@ function leadCells(cols, r) {
|
|
|
772
901
|
return row(cols, [
|
|
773
902
|
r.provider,
|
|
774
903
|
String(r.messages),
|
|
775
|
-
|
|
776
|
-
|
|
904
|
+
fmtTokens2(r.tokensInput),
|
|
905
|
+
fmtTokens2(r.tokensOutput),
|
|
777
906
|
`$${r.cost.toFixed(2)}`
|
|
778
907
|
]).trimEnd().padEnd(upToBudget) + " ";
|
|
779
908
|
}
|