@v1nvn/zai 0.36.0 → 0.37.0
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 +17 -17
- package/dist/index.js +150 -111
- package/dist/index.js.map +1 -1
- package/dist/usage.mjs +457 -418
- package/dist/usage.mjs.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @v1nvn/zai
|
|
2
2
|
|
|
3
3
|
The home of the zai plugin — and its plugin root: the `zai-usage` CLI in `src/`,
|
|
4
|
-
the mod's hooks module at `hooks/register.
|
|
4
|
+
the mod's hooks module at `hooks/register.tsx` (`.ts`/`.tsx` the engine loads
|
|
5
5
|
directly, no build), engine tests at `tests/`. It reports GLM Coding Plan quota
|
|
6
6
|
and usage for the current account — models, tools, limits — straight to the
|
|
7
7
|
terminal; no browser, nothing written.
|
|
@@ -11,8 +11,8 @@ User-facing docs: [root README](../../README.md).
|
|
|
11
11
|
## Quickstart
|
|
12
12
|
|
|
13
13
|
In Claude Code, the plugin is the way in — a mod serves the one surface the
|
|
14
|
-
model never reads: `/zai-usage` execs the shipped CLI and
|
|
15
|
-
|
|
14
|
+
model never reads: `/zai-usage` execs the shipped CLI and opens its report as
|
|
15
|
+
a pane (nothing execs until the command runs).
|
|
16
16
|
|
|
17
17
|
```sh
|
|
18
18
|
claude plugin marketplace add v1nvn/agentic
|
|
@@ -22,18 +22,18 @@ claude plugin install zai@agentic
|
|
|
22
22
|
In a terminal, bare:
|
|
23
23
|
|
|
24
24
|
```sh
|
|
25
|
-
npx -y @v1nvn/zai@0.
|
|
25
|
+
npx -y @v1nvn/zai@0.37.0
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
## Usage
|
|
29
29
|
|
|
30
30
|
| Invocation | Does |
|
|
31
31
|
|---|---|
|
|
32
|
-
| `/zai-usage` | the account's usage report, as
|
|
33
|
-
| `npx -y @v1nvn/zai@0.
|
|
34
|
-
| `npx -y @v1nvn/zai@0.
|
|
35
|
-
| `npx -y @v1nvn/zai@0.
|
|
36
|
-
| `npx -y @v1nvn/zai@0.
|
|
32
|
+
| `/zai-usage` | the account's usage report, as a pane |
|
|
33
|
+
| `npx -y @v1nvn/zai@0.37.0` | the same report, printed bare |
|
|
34
|
+
| `npx -y @v1nvn/zai@0.37.0 --auth-token TOKEN` | same, key on the command line (visible in `ps`) |
|
|
35
|
+
| `npx -y @v1nvn/zai@0.37.0 --auth-token=TOKEN` | `=` form — zsh quoting-safe |
|
|
36
|
+
| `npx -y @v1nvn/zai@0.37.0 --base-url URL` | another GLM endpoint (default `api.z.ai`) |
|
|
37
37
|
|
|
38
38
|
Each setting takes the first source that provides it:
|
|
39
39
|
|
|
@@ -73,17 +73,17 @@ and re-vendor `types/claude-code.d.ts` (repo root) from the engine-laid
|
|
|
73
73
|
| File | Role |
|
|
74
74
|
|---|---|
|
|
75
75
|
| `src/index.ts` | bin entry (`zai-usage`) — dispatch, exit codes |
|
|
76
|
-
| `src/usage.ts` | the GLM API call and the report it
|
|
76
|
+
| `src/usage.ts` | the GLM API call and the report input it assembles |
|
|
77
77
|
| `src/resolve.ts` | auth-token resolution chain |
|
|
78
|
-
| `src/format.ts` | the printed
|
|
79
|
-
| `hooks/register.
|
|
78
|
+
| `src/format.ts` | the report's lines — printed by the CLI, drawn by the pane |
|
|
79
|
+
| `hooks/register.tsx` | the mod: registers `/zai-usage`, execs the CLI, opens its report as a pane |
|
|
80
80
|
| `bin/usage.mjs` | the standalone build the mod execs — committed, synced by `yarn build` |
|
|
81
81
|
|
|
82
82
|
## Contracts
|
|
83
83
|
|
|
84
84
|
- One job: print the report, exit 0. Nothing is written anywhere.
|
|
85
|
-
- `/zai-usage` is one `node <plugin root>/bin/usage.mjs` exec through
|
|
86
|
-
session's Bash tool — the same query `npx zai-usage` runs, awaited
|
|
87
|
-
one allow rule it needs is
|
|
88
|
-
first run asks for it
|
|
89
|
-
|
|
85
|
+
- `/zai-usage` is one `node <plugin root>/bin/usage.mjs --json` exec through
|
|
86
|
+
the session's Bash tool — the same query `npx zai-usage` runs, awaited
|
|
87
|
+
as-is. The one allow rule it needs is
|
|
88
|
+
`Bash(node <plugin root>/bin/usage.mjs --json)`; the first run asks for it
|
|
89
|
+
once, then it is remembered. Its report is a pane the model never reads.
|
package/dist/index.js
CHANGED
|
@@ -16,77 +16,32 @@ function printUsageAndExit(program) {
|
|
|
16
16
|
console.error(program.helpInformation());
|
|
17
17
|
process.exit(1);
|
|
18
18
|
}
|
|
19
|
-
|
|
20
|
-
//#endregion
|
|
21
|
-
//#region src/resolve.ts
|
|
22
|
-
var DEFAULT_BASE_URL = "https://api.z.ai";
|
|
23
|
-
var GLM_HOSTS = /* @__PURE__ */ new Set([
|
|
24
|
-
"api.z.ai",
|
|
25
|
-
"dev.bigmodel.cn",
|
|
26
|
-
"open.bigmodel.cn"
|
|
27
|
-
]);
|
|
28
|
-
function fromEnv(value) {
|
|
29
|
-
return value ? value : void 0;
|
|
30
|
-
}
|
|
31
|
-
function originOf(value) {
|
|
19
|
+
async function runMain(main) {
|
|
32
20
|
try {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
function isGlm(origin) {
|
|
40
|
-
return GLM_HOSTS.has(new URL(origin).hostname);
|
|
41
|
-
}
|
|
42
|
-
function resolveBaseUrl(flag, env) {
|
|
43
|
-
if (flag !== void 0) {
|
|
44
|
-
const origin = originOf(flag);
|
|
45
|
-
if (origin === void 0) throw new Error(`invalid base URL: ${flag}`);
|
|
46
|
-
return {
|
|
47
|
-
glm: isGlm(origin),
|
|
48
|
-
origin
|
|
49
|
-
};
|
|
21
|
+
await main();
|
|
22
|
+
} catch (e) {
|
|
23
|
+
console.error(e.message);
|
|
24
|
+
process.exit(1);
|
|
50
25
|
}
|
|
51
|
-
const inherited = fromEnv(env.ANTHROPIC_BASE_URL);
|
|
52
|
-
const origin = inherited === void 0 ? void 0 : originOf(inherited);
|
|
53
|
-
if (origin !== void 0 && isGlm(origin)) return {
|
|
54
|
-
glm: true,
|
|
55
|
-
origin
|
|
56
|
-
};
|
|
57
|
-
return {
|
|
58
|
-
glm: false,
|
|
59
|
-
origin: DEFAULT_BASE_URL
|
|
60
|
-
};
|
|
61
26
|
}
|
|
62
|
-
|
|
63
|
-
|
|
27
|
+
Number.MAX_SAFE_INTEGER;
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region ../tokens/dist/assets/text-D_R3UWAk.js
|
|
30
|
+
function plain(text) {
|
|
31
|
+
return { text };
|
|
64
32
|
}
|
|
65
|
-
function
|
|
66
|
-
const program = parseQuietly(buildProgram(), args);
|
|
67
|
-
if (program === void 0) return;
|
|
68
|
-
const { authToken, baseUrl } = program.opts();
|
|
33
|
+
function dim(text) {
|
|
69
34
|
return {
|
|
70
|
-
|
|
71
|
-
|
|
35
|
+
text,
|
|
36
|
+
ink: "dim"
|
|
72
37
|
};
|
|
73
38
|
}
|
|
74
|
-
function
|
|
75
|
-
const base = resolveBaseUrl(parsed.baseUrl, env);
|
|
76
|
-
const token = parsed.authToken ?? (base.glm ? fromEnv(env.ANTHROPIC_AUTH_TOKEN) : void 0);
|
|
77
|
-
if (token === void 0) throw new Error(env.ANTHROPIC_AUTH_TOKEN ? "ANTHROPIC_AUTH_TOKEN is set, but the resolved base URL does not name a GLM host, so it is not a GLM Coding Plan token — set ZAI_AUTH_TOKEN (or --auth-token)" : "no API key: set ZAI_AUTH_TOKEN (or --auth-token)");
|
|
39
|
+
function bold(text) {
|
|
78
40
|
return {
|
|
79
|
-
|
|
80
|
-
|
|
41
|
+
text,
|
|
42
|
+
ink: "bold"
|
|
81
43
|
};
|
|
82
44
|
}
|
|
83
|
-
//#endregion
|
|
84
|
-
//#region ../tokens/dist/text.js
|
|
85
|
-
/**
|
|
86
|
-
* Plain-text rendering primitives for the fixed-width reports. Output targets
|
|
87
|
-
* a monospace terminal — the pane — so everything here is fixed-width:
|
|
88
|
-
* padding, block-glyph bars, and compact number formatting.
|
|
89
|
-
*/
|
|
90
45
|
var MONTHS = [
|
|
91
46
|
"Jan",
|
|
92
47
|
"Feb",
|
|
@@ -155,16 +110,18 @@ function meter(pct, width) {
|
|
|
155
110
|
filled = Math.max(0, Math.min(width, filled));
|
|
156
111
|
return "█".repeat(filled) + "░".repeat(width - filled);
|
|
157
112
|
}
|
|
113
|
+
function renderLines(lines) {
|
|
114
|
+
return lines.map((line) => line.map((seg) => seg.text).join("")).join("\n");
|
|
115
|
+
}
|
|
158
116
|
//#endregion
|
|
159
117
|
//#region src/format.ts
|
|
160
118
|
/**
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
* passed through processQuotaLimit).
|
|
119
|
+
* Usage-report line model: `reportLines` lays the report out once as ink-tagged
|
|
120
|
+
* segments (the pane draws those), and `render` joins them into the CLI's
|
|
121
|
+
* monospace terminal / hook-block `reason` bytes — no markdown; alignment comes
|
|
122
|
+
* from fixed-width columns and unicode block glyphs. Input is the parsed `data`
|
|
123
|
+
* of the three ZAI/ZHIPU monitor endpoints (model-usage, tool-usage,
|
|
124
|
+
* quota/limit — the last already passed through processQuotaLimit).
|
|
168
125
|
*/
|
|
169
126
|
var VBLOCKS = "▁▂▃▄▅▆▇";
|
|
170
127
|
function parseSlot(s) {
|
|
@@ -337,7 +294,7 @@ function hourlyVerticalChart({ x, tok, nh, maxTok, peakIdx, slotFn }) {
|
|
|
337
294
|
if (mark.some((c) => c !== " ")) lines.push(mark.join("").trimEnd());
|
|
338
295
|
return lines;
|
|
339
296
|
}
|
|
340
|
-
function
|
|
297
|
+
function reportLines({ platform, model, tool, quota, apiOffsetMin = 480, localOffsetMin, now = /* @__PURE__ */ new Date() }) {
|
|
341
298
|
const out = [];
|
|
342
299
|
const toOffset = localOffsetMin != null ? localOffsetMin : -(/* @__PURE__ */ new Date()).getTimezoneOffset();
|
|
343
300
|
function convertSlot(s) {
|
|
@@ -364,14 +321,16 @@ function render({ platform, model, tool, quota, apiOffsetMin = 480, localOffsetM
|
|
|
364
321
|
const firstSlot = convertSlot(x[0]);
|
|
365
322
|
const lastSlot = convertSlot(x[nh - 1]);
|
|
366
323
|
const win = firstSlot && lastSlot ? `${firstSlot.day} ${firstSlot.time} → ${lastSlot.day} ${lastSlot.time} · ${nh}h` : platform;
|
|
367
|
-
out.push(rule());
|
|
368
|
-
out.push(left
|
|
369
|
-
out.push(rule());
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
324
|
+
out.push([plain(rule())]);
|
|
325
|
+
out.push([bold(left), dim(padL(win, 68 - left.length))]);
|
|
326
|
+
out.push([plain(rule())]);
|
|
327
|
+
const head = plain(` ${fmtTokens(total)} tokens across ${fmtNum(totalCalls)} model calls`);
|
|
328
|
+
out.push([]);
|
|
329
|
+
if (peakIdx >= 0 && peakSlot) {
|
|
330
|
+
out.push([head, plain(" — ")]);
|
|
331
|
+
out.push([bold(`${Math.round(pctPeak)}%`), plain(" of it in a single hour")]);
|
|
332
|
+
out.push([plain(` (${peakSlot.day} ${peakSlot.time}, ${fmtTokens(peakTok)} tokens / ${fmtNum(peakCalls)} calls).`)]);
|
|
333
|
+
} else out.push([head, plain(".")]);
|
|
375
334
|
const activeHours = tok.filter((t) => t > 0).length;
|
|
376
335
|
const longest = idleRuns(tok).filter((r) => r.len >= 2).sort((a, b) => b.len - a.len).at(0);
|
|
377
336
|
const tt = tool.totalUsage ?? {};
|
|
@@ -387,13 +346,17 @@ function render({ platform, model, tool, quota, apiOffsetMin = 480, localOffsetM
|
|
|
387
346
|
}
|
|
388
347
|
const peakWinPct = total > 0 ? peakWinTokens / total * 100 : 0;
|
|
389
348
|
const peakWin = localPeakWindow(apiOffsetMin, toOffset);
|
|
390
|
-
out.push(
|
|
391
|
-
out.push(` Peak ${padR(peakSlot ? peakSlot.day + " " + peakSlot.time : "—", 15)}${padL(fmtTokens(peakTok), 7)} tokens · ${padL(fmtNum(peakCalls), 5)} calls`);
|
|
392
|
-
out.push(` Active ${padR(`${activeHours} / ${nh} hours`, 15)}${longest ? idleLabel(longest, x, convertSlot) : "no idle gaps"}`);
|
|
393
|
-
out.push(` Tools ${padR(`${toolTotal} calls`, 15)}${searchN} searches · ${readN} reads${zreadN ? ` · ${zreadN} zread` : ""}`);
|
|
394
|
-
if (peakWin) out.push(
|
|
395
|
-
|
|
396
|
-
|
|
349
|
+
out.push([]);
|
|
350
|
+
out.push([plain(` Peak ${padR(peakSlot ? peakSlot.day + " " + peakSlot.time : "—", 15)}${padL(fmtTokens(peakTok), 7)} tokens · ${padL(fmtNum(peakCalls), 5)} calls`)]);
|
|
351
|
+
out.push([plain(` Active ${padR(`${activeHours} / ${nh} hours`, 15)}${longest ? idleLabel(longest, x, convertSlot) : "no idle gaps"}`)]);
|
|
352
|
+
out.push([plain(` Tools ${padR(`${toolTotal} calls`, 15)}${searchN} searches · ${readN} reads${zreadN ? ` · ${zreadN} zread` : ""}`)]);
|
|
353
|
+
if (peakWin) out.push([
|
|
354
|
+
plain(` Peak hrs Mon–Fri ${peakWin.start}–${peakWin.end} · GLM-5.2 3× · ${peakWinActive}h active · ${fmtTokens(peakWinTokens)} (`),
|
|
355
|
+
bold(`${Math.round(peakWinPct)}%`),
|
|
356
|
+
plain(")")
|
|
357
|
+
]);
|
|
358
|
+
out.push([]);
|
|
359
|
+
out.push([dim(" Hourly tokens · ↑ peak hour " + "─".repeat(Math.max(0, 39)))]);
|
|
397
360
|
const chartLines = hourlyVerticalChart({
|
|
398
361
|
x,
|
|
399
362
|
tok,
|
|
@@ -403,48 +366,126 @@ function render({ platform, model, tool, quota, apiOffsetMin = 480, localOffsetM
|
|
|
403
366
|
slotFn: convertSlot
|
|
404
367
|
});
|
|
405
368
|
if (chartLines) {
|
|
406
|
-
out.push(...chartLines);
|
|
407
|
-
if (peakIdx >= 0 && peakSlot) out.push(` ◂ peak ${peakSlot.day} ${peakSlot.time} ${fmtTokens(peakTok)} tokens · ${fmtNum(peakCalls)} calls`);
|
|
408
|
-
} else out.push(" (not enough hourly data to chart)");
|
|
409
|
-
out.push(
|
|
410
|
-
out.push(" Model mix " + "─".repeat(Math.max(0, 57)));
|
|
369
|
+
out.push(...chartLines.map((l) => [plain(l)]));
|
|
370
|
+
if (peakIdx >= 0 && peakSlot) out.push([plain(` ◂ peak ${peakSlot.day} ${peakSlot.time} ${fmtTokens(peakTok)} tokens · ${fmtNum(peakCalls)} calls`)]);
|
|
371
|
+
} else out.push([plain(" (not enough hourly data to chart)")]);
|
|
372
|
+
out.push([]);
|
|
373
|
+
out.push([dim(" Model mix " + "─".repeat(Math.max(0, 57)))]);
|
|
411
374
|
const mixSrc = model.modelSummaryList ?? model.modelDataList ?? [];
|
|
412
375
|
const denom = mixSrc.reduce((a, m) => a + (m.totalTokens ?? 0), 0) || 1;
|
|
413
376
|
const mixSorted = [...mixSrc].sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
|
|
414
377
|
for (const m of mixSorted) {
|
|
415
378
|
const pct = (m.totalTokens ?? 0) / denom * 100;
|
|
416
|
-
out.push(
|
|
379
|
+
out.push([
|
|
380
|
+
plain(` ${padR(m.modelName || "?", 11)}${padL(fmtTokens(m.totalTokens), 8)} `),
|
|
381
|
+
bold(padL(pct.toFixed(1) + "%", 6)),
|
|
382
|
+
plain(` ${barField(pct, 100, 20)}`)
|
|
383
|
+
]);
|
|
417
384
|
}
|
|
418
|
-
out.push(
|
|
419
|
-
out.push(" Limits " + "─".repeat(Math.max(0, 60)));
|
|
385
|
+
out.push([]);
|
|
386
|
+
out.push([dim(" Limits " + "─".repeat(Math.max(0, 60)))]);
|
|
420
387
|
const bj = new Date(now.getTime() + apiOffsetMin * 6e4);
|
|
421
388
|
const bjDay = bj.getUTCDay();
|
|
422
389
|
const elapsedMin = bjDay >= 1 && bjDay <= 5 ? Math.max(0, Math.min(240, (bj.getUTCHours() - 14) * 60 + bj.getUTCMinutes())) : 0;
|
|
423
|
-
if (peakWin) out.push(` ${padR("Peak", 16)}
|
|
390
|
+
if (peakWin) out.push([dim(` ${padR("Peak", 16)}`), plain(`${padL(peakWin.start, 5)} ${meter(Math.round(elapsedMin / 240 * 100), 22)} ${peakWin.end}`)]);
|
|
424
391
|
const limits = quota.limits ?? [];
|
|
425
392
|
const mcp = limits.find((l) => /mcp/i.test(l.type ?? ""));
|
|
426
393
|
const tok5 = limits.find((l) => /token/i.test(l.type ?? ""));
|
|
427
394
|
if (tok5) {
|
|
428
395
|
const reset = new Date(tok5.nextResetTime ?? "").toLocaleTimeString("en-IN");
|
|
429
|
-
out.push(
|
|
396
|
+
out.push([
|
|
397
|
+
dim(` ${padR("Tokens · 5h", 16)}`),
|
|
398
|
+
bold(padL(`${tok5.percentage || 0}%`, 5)),
|
|
399
|
+
plain(` ${meter(tok5.percentage, 22)} ${reset}`)
|
|
400
|
+
]);
|
|
430
401
|
}
|
|
431
|
-
if (tok5 && mcp) out.push(
|
|
402
|
+
if (tok5 && mcp) out.push([]);
|
|
432
403
|
if (mcp) {
|
|
433
404
|
const reset = new Date(mcp.nextResetTime ?? "").toLocaleString("en-IN");
|
|
434
|
-
out.push(
|
|
405
|
+
out.push([
|
|
406
|
+
dim(` ${padR("MCP · this month", 16)}`),
|
|
407
|
+
bold(padL(`${mcp.percentage || 0}%`, 5)),
|
|
408
|
+
plain(` ${meter(mcp.percentage, 22)} ${reset}`)
|
|
409
|
+
]);
|
|
435
410
|
const parts = (mcp.usageDetails ?? []).map((d) => `${friendlyTool(d.modelCode)} ${fmtNum(d.usage)}`).join(" ");
|
|
436
|
-
if (parts) out.push(` ${fmtNum(mcp.currentUsage)}M / ${fmtNum(mcp.totol)}M · ${parts}`);
|
|
411
|
+
if (parts) out.push([plain(` ${fmtNum(mcp.currentUsage)}M / ${fmtNum(mcp.totol)}M · ${parts}`)]);
|
|
412
|
+
}
|
|
413
|
+
out.push([]);
|
|
414
|
+
out.push([plain(rule())]);
|
|
415
|
+
return out;
|
|
416
|
+
}
|
|
417
|
+
function render(input) {
|
|
418
|
+
return renderLines(reportLines(input));
|
|
419
|
+
}
|
|
420
|
+
//#endregion
|
|
421
|
+
//#region src/resolve.ts
|
|
422
|
+
var DEFAULT_BASE_URL = "https://api.z.ai";
|
|
423
|
+
var GLM_HOSTS = /* @__PURE__ */ new Set([
|
|
424
|
+
"api.z.ai",
|
|
425
|
+
"dev.bigmodel.cn",
|
|
426
|
+
"open.bigmodel.cn"
|
|
427
|
+
]);
|
|
428
|
+
function fromEnv(value) {
|
|
429
|
+
return value ? value : void 0;
|
|
430
|
+
}
|
|
431
|
+
function originOf(value) {
|
|
432
|
+
try {
|
|
433
|
+
const url = new URL(value);
|
|
434
|
+
return url.protocol === "https:" || url.protocol === "http:" ? url.origin : void 0;
|
|
435
|
+
} catch {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
function isGlm(origin) {
|
|
440
|
+
return GLM_HOSTS.has(new URL(origin).hostname);
|
|
441
|
+
}
|
|
442
|
+
function resolveBaseUrl(flag, env) {
|
|
443
|
+
if (flag !== void 0) {
|
|
444
|
+
const origin = originOf(flag);
|
|
445
|
+
if (origin === void 0) throw new Error(`invalid base URL: ${flag}`);
|
|
446
|
+
return {
|
|
447
|
+
glm: isGlm(origin),
|
|
448
|
+
origin
|
|
449
|
+
};
|
|
437
450
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
451
|
+
const inherited = fromEnv(env.ANTHROPIC_BASE_URL);
|
|
452
|
+
const origin = inherited === void 0 ? void 0 : originOf(inherited);
|
|
453
|
+
if (origin !== void 0 && isGlm(origin)) return {
|
|
454
|
+
glm: true,
|
|
455
|
+
origin
|
|
456
|
+
};
|
|
457
|
+
return {
|
|
458
|
+
glm: false,
|
|
459
|
+
origin: DEFAULT_BASE_URL
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
function buildProgram() {
|
|
463
|
+
return new Command().name("zai-usage").addOption(new Option("--auth-token <token>", "API key").env("ZAI_AUTH_TOKEN")).addOption(new Option("--base-url <url>", "base URL").env("ZAI_BASE_URL")).addOption(new Option("--json", "print the report lines as JSON for the zai mod"));
|
|
464
|
+
}
|
|
465
|
+
function parseArgs(args) {
|
|
466
|
+
const program = parseQuietly(buildProgram(), args);
|
|
467
|
+
if (program === void 0) return;
|
|
468
|
+
const { authToken, baseUrl, json } = program.opts();
|
|
469
|
+
return {
|
|
470
|
+
authToken: authToken || void 0,
|
|
471
|
+
baseUrl: baseUrl || void 0,
|
|
472
|
+
json: json === true
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
function resolveConfig(env, parsed) {
|
|
476
|
+
const base = resolveBaseUrl(parsed.baseUrl, env);
|
|
477
|
+
const token = parsed.authToken ?? (base.glm ? fromEnv(env.ANTHROPIC_AUTH_TOKEN) : void 0);
|
|
478
|
+
if (token === void 0) throw new Error(env.ANTHROPIC_AUTH_TOKEN ? "ANTHROPIC_AUTH_TOKEN is set, but the resolved base URL does not name a GLM host, so it is not a GLM Coding Plan token — set ZAI_AUTH_TOKEN (or --auth-token)" : "no API key: set ZAI_AUTH_TOKEN (or --auth-token)");
|
|
479
|
+
return {
|
|
480
|
+
token,
|
|
481
|
+
url: base.origin
|
|
482
|
+
};
|
|
441
483
|
}
|
|
442
484
|
//#endregion
|
|
443
485
|
//#region src/usage.ts
|
|
444
486
|
/**
|
|
445
|
-
* Usage query
|
|
446
|
-
*
|
|
447
|
-
* renders the plain-text report.
|
|
487
|
+
* Usage query: the three GLM monitor endpoints of the resolved base URL,
|
|
488
|
+
* assembled into the report input.
|
|
448
489
|
*/
|
|
449
490
|
function queryParams(now = /* @__PURE__ */ new Date()) {
|
|
450
491
|
const startDate = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1, now.getHours(), 0, 0, 0);
|
|
@@ -500,12 +541,12 @@ async function fetchReport(config) {
|
|
|
500
541
|
fetchJson(`${config.url}/api/monitor/usage/tool-usage`, "Tool usage", config.token, query),
|
|
501
542
|
fetchJson(`${config.url}/api/monitor/usage/quota/limit`, "Quota limit", config.token)
|
|
502
543
|
]);
|
|
503
|
-
return
|
|
544
|
+
return {
|
|
504
545
|
platform,
|
|
505
546
|
model,
|
|
506
547
|
tool,
|
|
507
548
|
quota: processQuotaLimit(quotaRaw)
|
|
508
|
-
}
|
|
549
|
+
};
|
|
509
550
|
} catch (e) {
|
|
510
551
|
throw new Error(`Request failed: ${e.message}`, { cause: e });
|
|
511
552
|
}
|
|
@@ -513,12 +554,10 @@ async function fetchReport(config) {
|
|
|
513
554
|
//#endregion
|
|
514
555
|
//#region src/index.ts
|
|
515
556
|
var parsed = parseArgs(process.argv.slice(2)) ?? printUsageAndExit(buildProgram());
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
process.exit(1);
|
|
521
|
-
}
|
|
557
|
+
await runMain(async () => {
|
|
558
|
+
const input = await fetchReport(resolveConfig(process.env, parsed));
|
|
559
|
+
console.log(parsed.json ? JSON.stringify({ lines: reportLines(input) }) : render(input));
|
|
560
|
+
});
|
|
522
561
|
//#endregion
|
|
523
562
|
export {};
|
|
524
563
|
|