@1930dev/opencode-usage 0.3.3 → 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 +7 -3
- package/dist/cli.js +93 -13
- package/dist/tui.js +92 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -124,7 +124,11 @@ Normalized percentage of budget consumed per provider, from these sources (in pr
|
|
|
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
|
|
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`
|
|
128
132
|
|
|
129
133
|
2. **Documented limits** — published quotas, used when the provider reports none.
|
|
130
134
|
Each one is read against the period it resets on: a daily limit against today,
|
|
@@ -142,8 +146,8 @@ Normalized percentage of budget consumed per provider, from these sources (in pr
|
|
|
142
146
|
- `cloudflare-workers-ai`: 10k neurons/day (free tier, resets 00:00 UTC)
|
|
143
147
|
- `nvidia`: credit allowance retired — the trial is rate-limited per model
|
|
144
148
|
- `opencode`: no spend API for API keys
|
|
145
|
-
- `
|
|
146
|
-
|
|
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`
|
|
147
151
|
|
|
148
152
|
3. **budgets.json** — your monthly USD per provider, read against what the
|
|
149
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: [] };
|
|
@@ -338,11 +365,13 @@ function parseOrcaRouter(usage, subscription) {
|
|
|
338
365
|
const cap = hard > 0 && hard < NO_CAP ? hard : soft > 0 && soft < NO_CAP ? soft : 0;
|
|
339
366
|
const capped = cap > 0;
|
|
340
367
|
const pct = capped ? spent / cap * 100 : 0;
|
|
368
|
+
const hint = capped ? "" : " (no cap set)";
|
|
369
|
+
const detail = `$${spent.toFixed(2)} spent${hint}`;
|
|
341
370
|
return {
|
|
342
371
|
provider: "orcarouter",
|
|
343
372
|
ok: true,
|
|
344
|
-
detail
|
|
345
|
-
windows: [{ label: "spend", percentUsed: pct, detail: `$${spent.toFixed(2)}` }],
|
|
373
|
+
detail,
|
|
374
|
+
windows: [{ label: "spend", percentUsed: pct, detail: `$${spent.toFixed(2)}${hint}` }],
|
|
346
375
|
budget: capped ? { percentUsed: pct, label: `$${cap}` } : undefined,
|
|
347
376
|
raw: { usage, subscription }
|
|
348
377
|
};
|
|
@@ -358,6 +387,56 @@ async function fetchOrcaRouter(key) {
|
|
|
358
387
|
}
|
|
359
388
|
}
|
|
360
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
|
+
|
|
361
440
|
// packages/core/src/providers.ts
|
|
362
441
|
var TTL_MS = 15 * 60 * 1000;
|
|
363
442
|
async function readCache() {
|
|
@@ -381,7 +460,8 @@ var FETCHERS = {
|
|
|
381
460
|
"github-copilot": fetchCopilot,
|
|
382
461
|
zai: fetchZai,
|
|
383
462
|
amd: fetchAmd,
|
|
384
|
-
orcarouter: fetchOrcaRouter
|
|
463
|
+
orcarouter: fetchOrcaRouter,
|
|
464
|
+
"snowflake-cortex": fetchSnowflake
|
|
385
465
|
};
|
|
386
466
|
async function providerStatuses(opts) {
|
|
387
467
|
const auth = await readAuth();
|
|
@@ -402,7 +482,7 @@ async function providerStatuses(opts) {
|
|
|
402
482
|
const secret = authSecret(auth[p]);
|
|
403
483
|
if (!fetcher || !secret)
|
|
404
484
|
return null;
|
|
405
|
-
return fetcher(secret);
|
|
485
|
+
return fetcher(secret, auth[p].metadata);
|
|
406
486
|
}));
|
|
407
487
|
for (const q of live) {
|
|
408
488
|
if (q)
|
|
@@ -425,7 +505,7 @@ async function providerStatuses(opts) {
|
|
|
425
505
|
}));
|
|
426
506
|
}
|
|
427
507
|
// packages/core/src/report.ts
|
|
428
|
-
function
|
|
508
|
+
function fmtTokens2(n) {
|
|
429
509
|
if (n >= 1e9)
|
|
430
510
|
return `${(n / 1e9).toFixed(1)}B`;
|
|
431
511
|
if (n >= 1e6)
|
|
@@ -467,8 +547,8 @@ function usageTable(rows, totals, sinceLabel, groupBy, pct) {
|
|
|
467
547
|
const row = [
|
|
468
548
|
r.group,
|
|
469
549
|
String(r.messages),
|
|
470
|
-
|
|
471
|
-
|
|
550
|
+
fmtTokens2(r.tokensInput),
|
|
551
|
+
fmtTokens2(r.tokensOutput),
|
|
472
552
|
fmtCost(r.cost)
|
|
473
553
|
];
|
|
474
554
|
if (pct) {
|
|
@@ -485,7 +565,7 @@ function usageTable(rows, totals, sinceLabel, groupBy, pct) {
|
|
|
485
565
|
[sources: ${[...pct.values()].map((p) => `${p.source}${p.label ? `(${p.label})` : ""}`).filter((v, i, a) => a.indexOf(v) === i).join(", ")}]` : "";
|
|
486
566
|
return `${t}
|
|
487
567
|
|
|
488
|
-
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}`;
|
|
489
569
|
}
|
|
490
570
|
function providersTable(statuses, local) {
|
|
491
571
|
const headers = ["PROVIDER", "QUOTA", "USAGE 7D (LOCAL)", "LAST USED"];
|
|
@@ -735,7 +815,7 @@ var PROVIDER_LIMITS = [
|
|
|
735
815
|
limit: 100,
|
|
736
816
|
tier: "paid",
|
|
737
817
|
source: "https://docs.snowflake.com/en/sql-reference/account-usage/cortex_ai_functions_usage_history",
|
|
738
|
-
note: "
|
|
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."
|
|
739
819
|
},
|
|
740
820
|
{
|
|
741
821
|
provider: "zai",
|
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: [] };
|
|
@@ -328,11 +355,13 @@ function parseOrcaRouter(usage, subscription) {
|
|
|
328
355
|
const cap = hard > 0 && hard < NO_CAP ? hard : soft > 0 && soft < NO_CAP ? soft : 0;
|
|
329
356
|
const capped = cap > 0;
|
|
330
357
|
const pct = capped ? spent / cap * 100 : 0;
|
|
358
|
+
const hint = capped ? "" : " (no cap set)";
|
|
359
|
+
const detail = `$${spent.toFixed(2)} spent${hint}`;
|
|
331
360
|
return {
|
|
332
361
|
provider: "orcarouter",
|
|
333
362
|
ok: true,
|
|
334
|
-
detail
|
|
335
|
-
windows: [{ label: "spend", percentUsed: pct, detail: `$${spent.toFixed(2)}` }],
|
|
363
|
+
detail,
|
|
364
|
+
windows: [{ label: "spend", percentUsed: pct, detail: `$${spent.toFixed(2)}${hint}` }],
|
|
336
365
|
budget: capped ? { percentUsed: pct, label: `$${cap}` } : undefined,
|
|
337
366
|
raw: { usage, subscription }
|
|
338
367
|
};
|
|
@@ -348,6 +377,56 @@ async function fetchOrcaRouter(key) {
|
|
|
348
377
|
}
|
|
349
378
|
}
|
|
350
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
|
+
|
|
351
430
|
// packages/core/src/providers.ts
|
|
352
431
|
var TTL_MS = 15 * 60 * 1000;
|
|
353
432
|
async function readCache() {
|
|
@@ -371,7 +450,8 @@ var FETCHERS = {
|
|
|
371
450
|
"github-copilot": fetchCopilot,
|
|
372
451
|
zai: fetchZai,
|
|
373
452
|
amd: fetchAmd,
|
|
374
|
-
orcarouter: fetchOrcaRouter
|
|
453
|
+
orcarouter: fetchOrcaRouter,
|
|
454
|
+
"snowflake-cortex": fetchSnowflake
|
|
375
455
|
};
|
|
376
456
|
async function providerStatuses(opts) {
|
|
377
457
|
const auth = await readAuth();
|
|
@@ -392,7 +472,7 @@ async function providerStatuses(opts) {
|
|
|
392
472
|
const secret = authSecret(auth[p]);
|
|
393
473
|
if (!fetcher || !secret)
|
|
394
474
|
return null;
|
|
395
|
-
return fetcher(secret);
|
|
475
|
+
return fetcher(secret, auth[p].metadata);
|
|
396
476
|
}));
|
|
397
477
|
for (const q of live) {
|
|
398
478
|
if (q)
|
|
@@ -500,7 +580,7 @@ var PROVIDER_LIMITS = [
|
|
|
500
580
|
limit: 100,
|
|
501
581
|
tier: "paid",
|
|
502
582
|
source: "https://docs.snowflake.com/en/sql-reference/account-usage/cortex_ai_functions_usage_history",
|
|
503
|
-
note: "
|
|
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."
|
|
504
584
|
},
|
|
505
585
|
{
|
|
506
586
|
provider: "zai",
|
|
@@ -655,7 +735,7 @@ function columnsFor(inner) {
|
|
|
655
735
|
const budget = Math.max(BUDGET_MIN, inner - used);
|
|
656
736
|
return { cols: [...base, { title: "BUDGET", width: budget, align: "left" }], bar: wide ? WIDE_BAR : COMPACT_BAR };
|
|
657
737
|
}
|
|
658
|
-
function
|
|
738
|
+
function fmtTokens2(n) {
|
|
659
739
|
if (n >= 1e6)
|
|
660
740
|
return `${(n / 1e6).toFixed(1)}M`;
|
|
661
741
|
if (n >= 1000)
|
|
@@ -821,8 +901,8 @@ function leadCells(cols, r) {
|
|
|
821
901
|
return row(cols, [
|
|
822
902
|
r.provider,
|
|
823
903
|
String(r.messages),
|
|
824
|
-
|
|
825
|
-
|
|
904
|
+
fmtTokens2(r.tokensInput),
|
|
905
|
+
fmtTokens2(r.tokensOutput),
|
|
826
906
|
`$${r.cost.toFixed(2)}`
|
|
827
907
|
]).trimEnd().padEnd(upToBudget) + " ";
|
|
828
908
|
}
|