@v1nvn/zai 0.36.0 → 0.36.1
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 +144 -108
- package/dist/index.js.map +1 -1
- package/dist/usage.mjs +451 -415
- 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.36.
|
|
25
|
+
npx -y @v1nvn/zai@0.36.1
|
|
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.36.
|
|
34
|
-
| `npx -y @v1nvn/zai@0.36.
|
|
35
|
-
| `npx -y @v1nvn/zai@0.36.
|
|
36
|
-
| `npx -y @v1nvn/zai@0.36.
|
|
32
|
+
| `/zai-usage` | the account's usage report, as a pane |
|
|
33
|
+
| `npx -y @v1nvn/zai@0.36.1` | the same report, printed bare |
|
|
34
|
+
| `npx -y @v1nvn/zai@0.36.1 --auth-token TOKEN` | same, key on the command line (visible in `ps`) |
|
|
35
|
+
| `npx -y @v1nvn/zai@0.36.1 --auth-token=TOKEN` | `=` form — zsh quoting-safe |
|
|
36
|
+
| `npx -y @v1nvn/zai@0.36.1 --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
|
@@ -18,69 +18,6 @@ function printUsageAndExit(program) {
|
|
|
18
18
|
}
|
|
19
19
|
Number.MAX_SAFE_INTEGER;
|
|
20
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) {
|
|
32
|
-
try {
|
|
33
|
-
const url = new URL(value);
|
|
34
|
-
return url.protocol === "https:" || url.protocol === "http:" ? url.origin : void 0;
|
|
35
|
-
} catch {
|
|
36
|
-
return;
|
|
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
|
-
};
|
|
50
|
-
}
|
|
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
|
-
}
|
|
62
|
-
function buildProgram() {
|
|
63
|
-
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"));
|
|
64
|
-
}
|
|
65
|
-
function parseArgs(args) {
|
|
66
|
-
const program = parseQuietly(buildProgram(), args);
|
|
67
|
-
if (program === void 0) return;
|
|
68
|
-
const { authToken, baseUrl } = program.opts();
|
|
69
|
-
return {
|
|
70
|
-
authToken: authToken || void 0,
|
|
71
|
-
baseUrl: baseUrl || void 0
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
function resolveConfig(env, parsed) {
|
|
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)");
|
|
78
|
-
return {
|
|
79
|
-
token,
|
|
80
|
-
url: base.origin
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
//#endregion
|
|
84
21
|
//#region ../tokens/dist/text.js
|
|
85
22
|
/**
|
|
86
23
|
* Plain-text rendering primitives for the fixed-width reports. Output targets
|
|
@@ -158,13 +95,12 @@ function meter(pct, width) {
|
|
|
158
95
|
//#endregion
|
|
159
96
|
//#region src/format.ts
|
|
160
97
|
/**
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
* passed through processQuotaLimit).
|
|
98
|
+
* Usage-report line model: `reportLines` lays the report out once as ink-tagged
|
|
99
|
+
* segments (the pane draws those), and `render` joins them into the CLI's
|
|
100
|
+
* monospace terminal / hook-block `reason` bytes — no markdown; alignment comes
|
|
101
|
+
* from fixed-width columns and unicode block glyphs. Input is the parsed `data`
|
|
102
|
+
* of the three ZAI/ZHIPU monitor endpoints (model-usage, tool-usage,
|
|
103
|
+
* quota/limit — the last already passed through processQuotaLimit).
|
|
168
104
|
*/
|
|
169
105
|
var VBLOCKS = "▁▂▃▄▅▆▇";
|
|
170
106
|
function parseSlot(s) {
|
|
@@ -337,7 +273,22 @@ function hourlyVerticalChart({ x, tok, nh, maxTok, peakIdx, slotFn }) {
|
|
|
337
273
|
if (mark.some((c) => c !== " ")) lines.push(mark.join("").trimEnd());
|
|
338
274
|
return lines;
|
|
339
275
|
}
|
|
340
|
-
function
|
|
276
|
+
function plain(text) {
|
|
277
|
+
return { text };
|
|
278
|
+
}
|
|
279
|
+
function dim(text) {
|
|
280
|
+
return {
|
|
281
|
+
text,
|
|
282
|
+
ink: "dim"
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
function bold(text) {
|
|
286
|
+
return {
|
|
287
|
+
text,
|
|
288
|
+
ink: "bold"
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
function reportLines({ platform, model, tool, quota, apiOffsetMin = 480, localOffsetMin, now = /* @__PURE__ */ new Date() }) {
|
|
341
292
|
const out = [];
|
|
342
293
|
const toOffset = localOffsetMin != null ? localOffsetMin : -(/* @__PURE__ */ new Date()).getTimezoneOffset();
|
|
343
294
|
function convertSlot(s) {
|
|
@@ -364,14 +315,16 @@ function render({ platform, model, tool, quota, apiOffsetMin = 480, localOffsetM
|
|
|
364
315
|
const firstSlot = convertSlot(x[0]);
|
|
365
316
|
const lastSlot = convertSlot(x[nh - 1]);
|
|
366
317
|
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
|
-
|
|
318
|
+
out.push([plain(rule())]);
|
|
319
|
+
out.push([bold(left), dim(padL(win, 68 - left.length))]);
|
|
320
|
+
out.push([plain(rule())]);
|
|
321
|
+
const head = plain(` ${fmtTokens(total)} tokens across ${fmtNum(totalCalls)} model calls`);
|
|
322
|
+
out.push([]);
|
|
323
|
+
if (peakIdx >= 0 && peakSlot) {
|
|
324
|
+
out.push([head, plain(" — ")]);
|
|
325
|
+
out.push([bold(`${Math.round(pctPeak)}%`), plain(" of it in a single hour")]);
|
|
326
|
+
out.push([plain(` (${peakSlot.day} ${peakSlot.time}, ${fmtTokens(peakTok)} tokens / ${fmtNum(peakCalls)} calls).`)]);
|
|
327
|
+
} else out.push([head, plain(".")]);
|
|
375
328
|
const activeHours = tok.filter((t) => t > 0).length;
|
|
376
329
|
const longest = idleRuns(tok).filter((r) => r.len >= 2).sort((a, b) => b.len - a.len).at(0);
|
|
377
330
|
const tt = tool.totalUsage ?? {};
|
|
@@ -387,13 +340,17 @@ function render({ platform, model, tool, quota, apiOffsetMin = 480, localOffsetM
|
|
|
387
340
|
}
|
|
388
341
|
const peakWinPct = total > 0 ? peakWinTokens / total * 100 : 0;
|
|
389
342
|
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
|
-
|
|
343
|
+
out.push([]);
|
|
344
|
+
out.push([plain(` Peak ${padR(peakSlot ? peakSlot.day + " " + peakSlot.time : "—", 15)}${padL(fmtTokens(peakTok), 7)} tokens · ${padL(fmtNum(peakCalls), 5)} calls`)]);
|
|
345
|
+
out.push([plain(` Active ${padR(`${activeHours} / ${nh} hours`, 15)}${longest ? idleLabel(longest, x, convertSlot) : "no idle gaps"}`)]);
|
|
346
|
+
out.push([plain(` Tools ${padR(`${toolTotal} calls`, 15)}${searchN} searches · ${readN} reads${zreadN ? ` · ${zreadN} zread` : ""}`)]);
|
|
347
|
+
if (peakWin) out.push([
|
|
348
|
+
plain(` Peak hrs Mon–Fri ${peakWin.start}–${peakWin.end} · GLM-5.2 3× · ${peakWinActive}h active · ${fmtTokens(peakWinTokens)} (`),
|
|
349
|
+
bold(`${Math.round(peakWinPct)}%`),
|
|
350
|
+
plain(")")
|
|
351
|
+
]);
|
|
352
|
+
out.push([]);
|
|
353
|
+
out.push([dim(" Hourly tokens · ↑ peak hour " + "─".repeat(Math.max(0, 39)))]);
|
|
397
354
|
const chartLines = hourlyVerticalChart({
|
|
398
355
|
x,
|
|
399
356
|
tok,
|
|
@@ -403,48 +360,126 @@ function render({ platform, model, tool, quota, apiOffsetMin = 480, localOffsetM
|
|
|
403
360
|
slotFn: convertSlot
|
|
404
361
|
});
|
|
405
362
|
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)));
|
|
363
|
+
out.push(...chartLines.map((l) => [plain(l)]));
|
|
364
|
+
if (peakIdx >= 0 && peakSlot) out.push([plain(` ◂ peak ${peakSlot.day} ${peakSlot.time} ${fmtTokens(peakTok)} tokens · ${fmtNum(peakCalls)} calls`)]);
|
|
365
|
+
} else out.push([plain(" (not enough hourly data to chart)")]);
|
|
366
|
+
out.push([]);
|
|
367
|
+
out.push([dim(" Model mix " + "─".repeat(Math.max(0, 57)))]);
|
|
411
368
|
const mixSrc = model.modelSummaryList ?? model.modelDataList ?? [];
|
|
412
369
|
const denom = mixSrc.reduce((a, m) => a + (m.totalTokens ?? 0), 0) || 1;
|
|
413
370
|
const mixSorted = [...mixSrc].sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
|
|
414
371
|
for (const m of mixSorted) {
|
|
415
372
|
const pct = (m.totalTokens ?? 0) / denom * 100;
|
|
416
|
-
out.push(
|
|
373
|
+
out.push([
|
|
374
|
+
plain(` ${padR(m.modelName || "?", 11)}${padL(fmtTokens(m.totalTokens), 8)} `),
|
|
375
|
+
bold(padL(pct.toFixed(1) + "%", 6)),
|
|
376
|
+
plain(` ${barField(pct, 100, 20)}`)
|
|
377
|
+
]);
|
|
417
378
|
}
|
|
418
|
-
out.push(
|
|
419
|
-
out.push(" Limits " + "─".repeat(Math.max(0, 60)));
|
|
379
|
+
out.push([]);
|
|
380
|
+
out.push([dim(" Limits " + "─".repeat(Math.max(0, 60)))]);
|
|
420
381
|
const bj = new Date(now.getTime() + apiOffsetMin * 6e4);
|
|
421
382
|
const bjDay = bj.getUTCDay();
|
|
422
383
|
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)}
|
|
384
|
+
if (peakWin) out.push([dim(` ${padR("Peak", 16)}`), plain(`${padL(peakWin.start, 5)} ${meter(Math.round(elapsedMin / 240 * 100), 22)} ${peakWin.end}`)]);
|
|
424
385
|
const limits = quota.limits ?? [];
|
|
425
386
|
const mcp = limits.find((l) => /mcp/i.test(l.type ?? ""));
|
|
426
387
|
const tok5 = limits.find((l) => /token/i.test(l.type ?? ""));
|
|
427
388
|
if (tok5) {
|
|
428
389
|
const reset = new Date(tok5.nextResetTime ?? "").toLocaleTimeString("en-IN");
|
|
429
|
-
out.push(
|
|
390
|
+
out.push([
|
|
391
|
+
dim(` ${padR("Tokens · 5h", 16)}`),
|
|
392
|
+
bold(padL(`${tok5.percentage || 0}%`, 5)),
|
|
393
|
+
plain(` ${meter(tok5.percentage, 22)} ${reset}`)
|
|
394
|
+
]);
|
|
430
395
|
}
|
|
431
|
-
if (tok5 && mcp) out.push(
|
|
396
|
+
if (tok5 && mcp) out.push([]);
|
|
432
397
|
if (mcp) {
|
|
433
398
|
const reset = new Date(mcp.nextResetTime ?? "").toLocaleString("en-IN");
|
|
434
|
-
out.push(
|
|
399
|
+
out.push([
|
|
400
|
+
dim(` ${padR("MCP · this month", 16)}`),
|
|
401
|
+
bold(padL(`${mcp.percentage || 0}%`, 5)),
|
|
402
|
+
plain(` ${meter(mcp.percentage, 22)} ${reset}`)
|
|
403
|
+
]);
|
|
435
404
|
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}`);
|
|
405
|
+
if (parts) out.push([plain(` ${fmtNum(mcp.currentUsage)}M / ${fmtNum(mcp.totol)}M · ${parts}`)]);
|
|
437
406
|
}
|
|
438
|
-
out.push(
|
|
439
|
-
out.push(rule());
|
|
440
|
-
return out
|
|
407
|
+
out.push([]);
|
|
408
|
+
out.push([plain(rule())]);
|
|
409
|
+
return out;
|
|
410
|
+
}
|
|
411
|
+
function render(input) {
|
|
412
|
+
return reportLines(input).map((line) => line.map((seg) => seg.text).join("")).join("\n");
|
|
413
|
+
}
|
|
414
|
+
//#endregion
|
|
415
|
+
//#region src/resolve.ts
|
|
416
|
+
var DEFAULT_BASE_URL = "https://api.z.ai";
|
|
417
|
+
var GLM_HOSTS = /* @__PURE__ */ new Set([
|
|
418
|
+
"api.z.ai",
|
|
419
|
+
"dev.bigmodel.cn",
|
|
420
|
+
"open.bigmodel.cn"
|
|
421
|
+
]);
|
|
422
|
+
function fromEnv(value) {
|
|
423
|
+
return value ? value : void 0;
|
|
424
|
+
}
|
|
425
|
+
function originOf(value) {
|
|
426
|
+
try {
|
|
427
|
+
const url = new URL(value);
|
|
428
|
+
return url.protocol === "https:" || url.protocol === "http:" ? url.origin : void 0;
|
|
429
|
+
} catch {
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
function isGlm(origin) {
|
|
434
|
+
return GLM_HOSTS.has(new URL(origin).hostname);
|
|
435
|
+
}
|
|
436
|
+
function resolveBaseUrl(flag, env) {
|
|
437
|
+
if (flag !== void 0) {
|
|
438
|
+
const origin = originOf(flag);
|
|
439
|
+
if (origin === void 0) throw new Error(`invalid base URL: ${flag}`);
|
|
440
|
+
return {
|
|
441
|
+
glm: isGlm(origin),
|
|
442
|
+
origin
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
const inherited = fromEnv(env.ANTHROPIC_BASE_URL);
|
|
446
|
+
const origin = inherited === void 0 ? void 0 : originOf(inherited);
|
|
447
|
+
if (origin !== void 0 && isGlm(origin)) return {
|
|
448
|
+
glm: true,
|
|
449
|
+
origin
|
|
450
|
+
};
|
|
451
|
+
return {
|
|
452
|
+
glm: false,
|
|
453
|
+
origin: DEFAULT_BASE_URL
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
function buildProgram() {
|
|
457
|
+
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"));
|
|
458
|
+
}
|
|
459
|
+
function parseArgs(args) {
|
|
460
|
+
const program = parseQuietly(buildProgram(), args);
|
|
461
|
+
if (program === void 0) return;
|
|
462
|
+
const { authToken, baseUrl, json } = program.opts();
|
|
463
|
+
return {
|
|
464
|
+
authToken: authToken || void 0,
|
|
465
|
+
baseUrl: baseUrl || void 0,
|
|
466
|
+
json: json === true
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
function resolveConfig(env, parsed) {
|
|
470
|
+
const base = resolveBaseUrl(parsed.baseUrl, env);
|
|
471
|
+
const token = parsed.authToken ?? (base.glm ? fromEnv(env.ANTHROPIC_AUTH_TOKEN) : void 0);
|
|
472
|
+
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)");
|
|
473
|
+
return {
|
|
474
|
+
token,
|
|
475
|
+
url: base.origin
|
|
476
|
+
};
|
|
441
477
|
}
|
|
442
478
|
//#endregion
|
|
443
479
|
//#region src/usage.ts
|
|
444
480
|
/**
|
|
445
|
-
* Usage query
|
|
446
|
-
*
|
|
447
|
-
* renders the plain-text report.
|
|
481
|
+
* Usage query: the three GLM monitor endpoints of the resolved base URL,
|
|
482
|
+
* assembled into the report input.
|
|
448
483
|
*/
|
|
449
484
|
function queryParams(now = /* @__PURE__ */ new Date()) {
|
|
450
485
|
const startDate = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1, now.getHours(), 0, 0, 0);
|
|
@@ -500,12 +535,12 @@ async function fetchReport(config) {
|
|
|
500
535
|
fetchJson(`${config.url}/api/monitor/usage/tool-usage`, "Tool usage", config.token, query),
|
|
501
536
|
fetchJson(`${config.url}/api/monitor/usage/quota/limit`, "Quota limit", config.token)
|
|
502
537
|
]);
|
|
503
|
-
return
|
|
538
|
+
return {
|
|
504
539
|
platform,
|
|
505
540
|
model,
|
|
506
541
|
tool,
|
|
507
542
|
quota: processQuotaLimit(quotaRaw)
|
|
508
|
-
}
|
|
543
|
+
};
|
|
509
544
|
} catch (e) {
|
|
510
545
|
throw new Error(`Request failed: ${e.message}`, { cause: e });
|
|
511
546
|
}
|
|
@@ -514,7 +549,8 @@ async function fetchReport(config) {
|
|
|
514
549
|
//#region src/index.ts
|
|
515
550
|
var parsed = parseArgs(process.argv.slice(2)) ?? printUsageAndExit(buildProgram());
|
|
516
551
|
try {
|
|
517
|
-
|
|
552
|
+
const input = await fetchReport(resolveConfig(process.env, parsed));
|
|
553
|
+
console.log(parsed.json ? JSON.stringify({ lines: reportLines(input) }) : render(input));
|
|
518
554
|
} catch (e) {
|
|
519
555
|
console.error(e.message);
|
|
520
556
|
process.exit(1);
|