@v1nvn/zai 0.14.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/dist/index.js +500 -0
- package/dist/index.js.map +1 -0
- package/package.json +43 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
//#region ../core/dist/index.js
|
|
3
|
+
/**
|
|
4
|
+
* Print the UserPromptExpansion decision for a zero-token command: `block`
|
|
5
|
+
* keeps the command from reaching the model, with the output as the `reason`.
|
|
6
|
+
*/
|
|
7
|
+
function emitHookBlock(reason) {
|
|
8
|
+
process.stdout.write(`${JSON.stringify({
|
|
9
|
+
decision: "block",
|
|
10
|
+
reason
|
|
11
|
+
})}\n`);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Plain-text rendering primitives shared by the zai and tokens report
|
|
15
|
+
* renderers. Output targets a monospace terminal / hook-block `reason`, so
|
|
16
|
+
* everything here is fixed-width: padding, block-glyph bars, and compact
|
|
17
|
+
* number formatting.
|
|
18
|
+
*/
|
|
19
|
+
var MONTHS = [
|
|
20
|
+
"Jan",
|
|
21
|
+
"Feb",
|
|
22
|
+
"Mar",
|
|
23
|
+
"Apr",
|
|
24
|
+
"May",
|
|
25
|
+
"Jun",
|
|
26
|
+
"Jul",
|
|
27
|
+
"Aug",
|
|
28
|
+
"Sep",
|
|
29
|
+
"Oct",
|
|
30
|
+
"Nov",
|
|
31
|
+
"Dec"
|
|
32
|
+
];
|
|
33
|
+
var EIGHTHS = [
|
|
34
|
+
"",
|
|
35
|
+
"▏",
|
|
36
|
+
"▎",
|
|
37
|
+
"▍",
|
|
38
|
+
"▌",
|
|
39
|
+
"▋",
|
|
40
|
+
"▊",
|
|
41
|
+
"▉"
|
|
42
|
+
];
|
|
43
|
+
function fmtTokens(n) {
|
|
44
|
+
if (n == null || Number.isNaN(n)) return "—";
|
|
45
|
+
if (n >= 1e9) return (n / 1e9).toFixed(1) + "B";
|
|
46
|
+
if (n >= 1e6) return (n / 1e6).toFixed(1) + "M";
|
|
47
|
+
if (n >= 1e3) return (n / 1e3).toFixed(1) + "K";
|
|
48
|
+
return String(n);
|
|
49
|
+
}
|
|
50
|
+
function fmtNum(n) {
|
|
51
|
+
return (n || 0).toLocaleString("en-US");
|
|
52
|
+
}
|
|
53
|
+
function padR(s, n) {
|
|
54
|
+
return s.length >= n ? s : s + " ".repeat(n - s.length);
|
|
55
|
+
}
|
|
56
|
+
function padL(s, n) {
|
|
57
|
+
return s.length >= n ? s : " ".repeat(n - s.length) + s;
|
|
58
|
+
}
|
|
59
|
+
/** Fixed-width bar field (width cols): █ blocks + an eighth-fraction + trailing spaces. */
|
|
60
|
+
function barField(v, max, width) {
|
|
61
|
+
if (!v || v <= 0 || max <= 0) return " ".repeat(width);
|
|
62
|
+
const scaled = v / max * width;
|
|
63
|
+
let full = Math.floor(scaled);
|
|
64
|
+
let fi = Math.round((scaled - full) * 8);
|
|
65
|
+
if (fi === 8) {
|
|
66
|
+
full += 1;
|
|
67
|
+
fi = 0;
|
|
68
|
+
}
|
|
69
|
+
if (full === 0 && fi === 0) fi = 1;
|
|
70
|
+
let s = "█".repeat(Math.min(full, width));
|
|
71
|
+
if (full < width && fi > 0) s += EIGHTHS[fi];
|
|
72
|
+
if (s.length < width) s += " ".repeat(width - s.length);
|
|
73
|
+
return s.slice(0, width);
|
|
74
|
+
}
|
|
75
|
+
/** Filled/empty meter: █ for used, ░ for remaining. */
|
|
76
|
+
function meter(pct, width) {
|
|
77
|
+
let filled = Math.round((pct || 0) / 100 * width);
|
|
78
|
+
filled = Math.max(0, Math.min(width, filled));
|
|
79
|
+
return "█".repeat(filled) + "░".repeat(width - filled);
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/format.ts
|
|
83
|
+
/**
|
|
84
|
+
* Plain-text usage report renderer.
|
|
85
|
+
*
|
|
86
|
+
* Rendered for a monospace terminal / hook-block `reason`, so it must NOT rely on
|
|
87
|
+
* markdown (bold, tables, fenced code). Alignment comes from fixed-width columns
|
|
88
|
+
* and unicode block glyphs. Input is the parsed `data` of the three ZAI/ZHIPU
|
|
89
|
+
* monitor endpoints (model-usage, tool-usage, quota/limit — the last already
|
|
90
|
+
* passed through processQuotaLimit).
|
|
91
|
+
*/
|
|
92
|
+
var VBLOCKS = "▁▂▃▄▅▆▇";
|
|
93
|
+
var W = 68;
|
|
94
|
+
function parseSlot(s) {
|
|
95
|
+
const m = /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2})/.exec(s ?? "");
|
|
96
|
+
if (!m) return null;
|
|
97
|
+
return {
|
|
98
|
+
h: +m[4],
|
|
99
|
+
day: `${MONTHS[+m[2] - 1]} ${m[3]}`,
|
|
100
|
+
time: `${m[4]}:${m[5]}`
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Re-express a naive "YYYY-MM-DD HH:MM" string from one UTC offset to another.
|
|
105
|
+
* Offsets are minutes east of UTC (Beijing = 480, IST = 330). Pure — does not
|
|
106
|
+
* depend on the runtime timezone, so it is deterministic under test.
|
|
107
|
+
*/
|
|
108
|
+
function shiftSlot(s, fromOffsetMin, toOffsetMin) {
|
|
109
|
+
const m = /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2})/.exec(s);
|
|
110
|
+
if (!m) return null;
|
|
111
|
+
const utcMs = Date.UTC(+m[1], +m[2] - 1, +m[3], +m[4], +m[5]) - fromOffsetMin * 6e4;
|
|
112
|
+
const dt = new Date(utcMs + toOffsetMin * 6e4);
|
|
113
|
+
return {
|
|
114
|
+
h: dt.getUTCHours(),
|
|
115
|
+
day: `${MONTHS[dt.getUTCMonth()]} ${String(dt.getUTCDate()).padStart(2, "0")}`,
|
|
116
|
+
time: `${String(dt.getUTCHours()).padStart(2, "0")}:${String(dt.getUTCMinutes()).padStart(2, "0")}`
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function friendlyTool(code) {
|
|
120
|
+
switch ((code ?? "").toLowerCase()) {
|
|
121
|
+
case "search-prime": return "web search";
|
|
122
|
+
case "web-reader": return "web read";
|
|
123
|
+
default: return code || "other";
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function idleRuns(tok) {
|
|
127
|
+
const runs = [];
|
|
128
|
+
let i = 0;
|
|
129
|
+
while (i < tok.length) if ((tok[i] || 0) === 0) {
|
|
130
|
+
let j = i;
|
|
131
|
+
while (j < tok.length && (tok[j] || 0) === 0) j++;
|
|
132
|
+
runs.push({
|
|
133
|
+
i,
|
|
134
|
+
j: j - 1,
|
|
135
|
+
len: j - i
|
|
136
|
+
});
|
|
137
|
+
i = j;
|
|
138
|
+
} else i++;
|
|
139
|
+
return runs;
|
|
140
|
+
}
|
|
141
|
+
function idleLabel(run, x, slotFn) {
|
|
142
|
+
const a = slotFn(x[run.i]);
|
|
143
|
+
const b = slotFn(x[run.j]);
|
|
144
|
+
if (!a || !b) return `idle (${run.len}h)`;
|
|
145
|
+
return `idle ${`${a.day} ${a.time}`} → ${a.day === b.day ? b.time : `${b.day} ${b.time}`} (${run.len}h)`;
|
|
146
|
+
}
|
|
147
|
+
function isPeakBucket(s) {
|
|
148
|
+
const m = /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):/.exec(s ?? "");
|
|
149
|
+
if (!m) return false;
|
|
150
|
+
const wd = new Date(Date.UTC(+m[1], +m[2] - 1, +m[3])).getUTCDay();
|
|
151
|
+
return wd >= 1 && wd <= 5 && +m[4] >= 14 && +m[4] <= 17;
|
|
152
|
+
}
|
|
153
|
+
/** Local-time start/end of the 14:00–18:00 Beijing peak window (e.g. 11:30 / 15:30). */
|
|
154
|
+
function localPeakWindow(apiOffsetMin, toOffsetMin) {
|
|
155
|
+
const a = shiftSlot("2000-01-03 14:00", apiOffsetMin, toOffsetMin);
|
|
156
|
+
const b = shiftSlot("2000-01-03 18:00", apiOffsetMin, toOffsetMin);
|
|
157
|
+
if (!a || !b) return null;
|
|
158
|
+
return {
|
|
159
|
+
start: a.time,
|
|
160
|
+
end: b.time
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Vertical bar chart of hourly tokens. Returns an array of plain-text lines
|
|
165
|
+
* (without the section header), or null if there are fewer than 2 buckets.
|
|
166
|
+
*
|
|
167
|
+
* Bars are scaled to `maxTok` across `ROWS` rows; the topmost filled cell of a
|
|
168
|
+
* bar uses a fractional lower-block glyph (▁▂▃▄▅▆▇) for sub-row height. The
|
|
169
|
+
* y-axis labels only the peak (top) and 0 (baseline); the header window and the
|
|
170
|
+
* caller's peak annotation carry the exact values.
|
|
171
|
+
*/
|
|
172
|
+
function hourlyVerticalChart({ x, tok, nh, maxTok, peakIdx, slotFn }) {
|
|
173
|
+
const ROWS = 6;
|
|
174
|
+
if (nh < 2 || maxTok <= 0) return null;
|
|
175
|
+
const lines = [];
|
|
176
|
+
const maxLab = fmtTokens(maxTok);
|
|
177
|
+
const L = maxLab.length;
|
|
178
|
+
const AXIS = 1 + L + 1;
|
|
179
|
+
const P0 = AXIS + 2;
|
|
180
|
+
const PW = W - P0;
|
|
181
|
+
const slot = Math.max(1, Math.floor(PW / nh));
|
|
182
|
+
const barW = slot >= 2 ? slot - 1 : 1;
|
|
183
|
+
function barLeft(i) {
|
|
184
|
+
return P0 + i * slot;
|
|
185
|
+
}
|
|
186
|
+
function barCenter(i) {
|
|
187
|
+
return barLeft(i) + Math.floor(barW / 2);
|
|
188
|
+
}
|
|
189
|
+
const usedCols = nh * slot;
|
|
190
|
+
function blank() {
|
|
191
|
+
return Array(W).fill(" ");
|
|
192
|
+
}
|
|
193
|
+
function glyph(i, r) {
|
|
194
|
+
const v = tok[i] || 0;
|
|
195
|
+
if (v <= 0) return " ";
|
|
196
|
+
const fill = v / maxTok * ROWS - r;
|
|
197
|
+
if (fill >= 1) return "█";
|
|
198
|
+
if (fill > 0) {
|
|
199
|
+
let e = Math.round(fill * 8);
|
|
200
|
+
if (e < 1) e = 1;
|
|
201
|
+
if (e > 7) e = 7;
|
|
202
|
+
return VBLOCKS[e - 1];
|
|
203
|
+
}
|
|
204
|
+
return " ";
|
|
205
|
+
}
|
|
206
|
+
for (let r = 5; r >= 0; r--) {
|
|
207
|
+
const line = blank();
|
|
208
|
+
line[AXIS] = r === 5 ? "┤" : "│";
|
|
209
|
+
if (r === 5) for (let k = 0; k < L; k++) line[AXIS - 1 - L + k] = maxLab[k];
|
|
210
|
+
for (let i = 0; i < nh; i++) {
|
|
211
|
+
const g = glyph(i, r);
|
|
212
|
+
if (g !== " ") for (let c = 0; c < barW; c++) line[barLeft(i) + c] = g;
|
|
213
|
+
}
|
|
214
|
+
lines.push(line.join("").trimEnd());
|
|
215
|
+
}
|
|
216
|
+
function slotOf(i) {
|
|
217
|
+
return slotFn(x[i]);
|
|
218
|
+
}
|
|
219
|
+
function isTick(i) {
|
|
220
|
+
if (i === 0 || i === nh - 1) return true;
|
|
221
|
+
const s = slotOf(i);
|
|
222
|
+
return !!s && s.h % 6 === 0;
|
|
223
|
+
}
|
|
224
|
+
const base = blank();
|
|
225
|
+
base[AXIS - 2] = "0";
|
|
226
|
+
base[AXIS] = "└";
|
|
227
|
+
for (let c = AXIS + 1; c < P0 + usedCols; c++) base[c] = "─";
|
|
228
|
+
for (let i = 0; i < nh; i++) if (isTick(i)) base[barCenter(i)] = "┬";
|
|
229
|
+
lines.push(base.join("").trimEnd());
|
|
230
|
+
const hr = blank();
|
|
231
|
+
let last = -10;
|
|
232
|
+
for (let i = 0; i < nh; i++) {
|
|
233
|
+
if (!isTick(i)) continue;
|
|
234
|
+
const col = barCenter(i);
|
|
235
|
+
if (col - last < 3) continue;
|
|
236
|
+
const s = slotOf(i);
|
|
237
|
+
const txt = s ? String(s.h).padStart(2, "0") : " ";
|
|
238
|
+
hr[col] = txt[0];
|
|
239
|
+
hr[col + 1] = txt[1];
|
|
240
|
+
last = col;
|
|
241
|
+
}
|
|
242
|
+
lines.push(hr.join("").trimEnd());
|
|
243
|
+
const day = blank();
|
|
244
|
+
let lastDay = null;
|
|
245
|
+
last = -10;
|
|
246
|
+
for (let i = 0; i < nh; i++) {
|
|
247
|
+
if (!isTick(i)) continue;
|
|
248
|
+
const s = slotOf(i);
|
|
249
|
+
if (!s || s.day === lastDay) continue;
|
|
250
|
+
const col = barCenter(i);
|
|
251
|
+
if (col - last >= s.day.length && col + s.day.length <= W) {
|
|
252
|
+
for (let k = 0; k < s.day.length; k++) day[col + k] = s.day[k];
|
|
253
|
+
lastDay = s.day;
|
|
254
|
+
last = col + s.day.length;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (day.some((c) => c !== " ")) lines.push(day.join("").trimEnd());
|
|
258
|
+
const mark = blank();
|
|
259
|
+
for (let i = 0; i < nh; i++) if (isPeakBucket(x[i])) mark[barCenter(i)] = "↑";
|
|
260
|
+
if (peakIdx >= 0) mark[barCenter(peakIdx)] = "◂";
|
|
261
|
+
if (mark.some((c) => c !== " ")) lines.push(mark.join("").trimEnd());
|
|
262
|
+
return lines;
|
|
263
|
+
}
|
|
264
|
+
function render({ platform, model, tool, quota, apiOffsetMin = 480, localOffsetMin, now = /* @__PURE__ */ new Date() }) {
|
|
265
|
+
const out = [];
|
|
266
|
+
function rule() {
|
|
267
|
+
return "─".repeat(W);
|
|
268
|
+
}
|
|
269
|
+
const toOffset = localOffsetMin != null ? localOffsetMin : -(/* @__PURE__ */ new Date()).getTimezoneOffset();
|
|
270
|
+
function convertSlot(s) {
|
|
271
|
+
return shiftSlot(s, apiOffsetMin, toOffset) ?? parseSlot(s);
|
|
272
|
+
}
|
|
273
|
+
const x = model.x_time ?? [];
|
|
274
|
+
const tok = model.tokensUsage ?? [];
|
|
275
|
+
const calls = model.modelCallCount ?? [];
|
|
276
|
+
const nh = x.length;
|
|
277
|
+
const tu = model.totalUsage ?? {};
|
|
278
|
+
const total = tu.totalTokensUsage != null ? tu.totalTokensUsage : tok.reduce((a, b) => a + (b || 0), 0);
|
|
279
|
+
const totalCalls = tu.totalModelCallCount != null ? tu.totalModelCallCount : calls.reduce((a, b) => a + (b || 0), 0);
|
|
280
|
+
let peakIdx = -1;
|
|
281
|
+
let peakTok = 0;
|
|
282
|
+
for (let i = 0; i < tok.length; i++) if (tok[i] > peakTok) {
|
|
283
|
+
peakTok = tok[i];
|
|
284
|
+
peakIdx = i;
|
|
285
|
+
}
|
|
286
|
+
const peakCalls = peakIdx >= 0 ? calls[peakIdx] || 0 : 0;
|
|
287
|
+
const pctPeak = total > 0 ? peakTok / total * 100 : 0;
|
|
288
|
+
const peakSlot = peakIdx >= 0 ? convertSlot(x[peakIdx]) : null;
|
|
289
|
+
const level = quota.level ? quota.level.charAt(0).toUpperCase() + quota.level.slice(1) : "";
|
|
290
|
+
const left = ` GLM Coding Plan${level ? " · " + level : ""}`;
|
|
291
|
+
const firstSlot = convertSlot(x[0]);
|
|
292
|
+
const lastSlot = convertSlot(x[nh - 1]);
|
|
293
|
+
const win = firstSlot && lastSlot ? `${firstSlot.day} ${firstSlot.time} → ${lastSlot.day} ${lastSlot.time} · ${nh}h` : platform;
|
|
294
|
+
out.push(rule());
|
|
295
|
+
out.push(left + padL(win, W - left.length));
|
|
296
|
+
out.push(rule());
|
|
297
|
+
let lead = ` ${fmtTokens(total)} tokens across ${fmtNum(totalCalls)} model calls`;
|
|
298
|
+
if (peakIdx >= 0 && peakSlot) lead += ` — ${Math.round(pctPeak)}% of it in a single hour (${peakSlot.day} ${peakSlot.time}, ${fmtTokens(peakTok)} tokens / ${fmtNum(peakCalls)} calls)`;
|
|
299
|
+
lead += ".";
|
|
300
|
+
out.push("");
|
|
301
|
+
out.push(lead);
|
|
302
|
+
const activeHours = tok.filter((t) => t > 0).length;
|
|
303
|
+
const longest = idleRuns(tok).filter((r) => r.len >= 2).sort((a, b) => b.len - a.len).at(0);
|
|
304
|
+
const tt = tool.totalUsage ?? {};
|
|
305
|
+
const searchN = tt.totalNetworkSearchCount || 0;
|
|
306
|
+
const readN = tt.totalWebReadMcpCount || 0;
|
|
307
|
+
const zreadN = tt.totalZreadMcpCount || 0;
|
|
308
|
+
const toolTotal = tt.totalSearchMcpCount != null ? tt.totalSearchMcpCount : searchN + readN + zreadN;
|
|
309
|
+
let peakWinActive = 0;
|
|
310
|
+
let peakWinTokens = 0;
|
|
311
|
+
for (let k = 0; k < nh; k++) if (isPeakBucket(x[k]) && tok[k] > 0) {
|
|
312
|
+
peakWinActive++;
|
|
313
|
+
peakWinTokens += tok[k];
|
|
314
|
+
}
|
|
315
|
+
const peakWinPct = total > 0 ? peakWinTokens / total * 100 : 0;
|
|
316
|
+
const peakWin = localPeakWindow(apiOffsetMin, toOffset);
|
|
317
|
+
out.push("");
|
|
318
|
+
out.push(` Peak ${padR(peakSlot ? peakSlot.day + " " + peakSlot.time : "—", 15)}${padL(fmtTokens(peakTok), 7)} tokens · ${padL(fmtNum(peakCalls), 5)} calls`);
|
|
319
|
+
out.push(` Active ${padR(`${activeHours} / ${nh} hours`, 15)}${longest ? idleLabel(longest, x, convertSlot) : "no idle gaps"}`);
|
|
320
|
+
out.push(` Tools ${padR(`${toolTotal} calls`, 15)}${searchN} searches · ${readN} reads${zreadN ? ` · ${zreadN} zread` : ""}`);
|
|
321
|
+
if (peakWin) out.push(` Peak hrs Mon–Fri ${peakWin.start}–${peakWin.end} · GLM-5.2 3× · ${peakWinActive}h active · ${fmtTokens(peakWinTokens)} (${Math.round(peakWinPct)}%)`);
|
|
322
|
+
out.push("");
|
|
323
|
+
out.push(" Hourly tokens · ↑ peak hour " + "─".repeat(Math.max(0, 39)));
|
|
324
|
+
const chartLines = hourlyVerticalChart({
|
|
325
|
+
x,
|
|
326
|
+
tok,
|
|
327
|
+
nh,
|
|
328
|
+
maxTok: peakTok || 1,
|
|
329
|
+
peakIdx,
|
|
330
|
+
slotFn: convertSlot
|
|
331
|
+
});
|
|
332
|
+
if (chartLines) {
|
|
333
|
+
out.push(...chartLines);
|
|
334
|
+
if (peakIdx >= 0 && peakSlot) out.push(` ◂ peak ${peakSlot.day} ${peakSlot.time} ${fmtTokens(peakTok)} tokens · ${fmtNum(peakCalls)} calls`);
|
|
335
|
+
} else out.push(" (not enough hourly data to chart)");
|
|
336
|
+
out.push("");
|
|
337
|
+
out.push(" Model mix " + "─".repeat(Math.max(0, 57)));
|
|
338
|
+
const mixSrc = model.modelSummaryList ?? model.modelDataList ?? [];
|
|
339
|
+
const denom = mixSrc.reduce((a, m) => a + (m.totalTokens ?? 0), 0) || 1;
|
|
340
|
+
const mixSorted = [...mixSrc].sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
|
|
341
|
+
for (const m of mixSorted) {
|
|
342
|
+
const pct = (m.totalTokens ?? 0) / denom * 100;
|
|
343
|
+
out.push(` ${padR(m.modelName || "?", 11)}${padL(fmtTokens(m.totalTokens), 8)} ${padL(pct.toFixed(1) + "%", 6)} ${barField(pct, 100, 20)}`);
|
|
344
|
+
}
|
|
345
|
+
out.push("");
|
|
346
|
+
out.push(" Limits " + "─".repeat(Math.max(0, 60)));
|
|
347
|
+
const bj = new Date(now.getTime() + apiOffsetMin * 6e4);
|
|
348
|
+
const bjDay = bj.getUTCDay();
|
|
349
|
+
const elapsedMin = bjDay >= 1 && bjDay <= 5 ? Math.max(0, Math.min(240, (bj.getUTCHours() - 14) * 60 + bj.getUTCMinutes())) : 0;
|
|
350
|
+
if (peakWin) out.push(` ${padR("Peak", 16)}${padL(peakWin.start, 5)} ${meter(Math.round(elapsedMin / 240 * 100), 22)} ${peakWin.end}`);
|
|
351
|
+
const limits = quota.limits ?? [];
|
|
352
|
+
const mcp = limits.find((l) => /mcp/i.test(l.type ?? ""));
|
|
353
|
+
const tok5 = limits.find((l) => /token/i.test(l.type ?? ""));
|
|
354
|
+
if (tok5) {
|
|
355
|
+
const reset = new Date(tok5.nextResetTime ?? "").toLocaleTimeString("en-IN");
|
|
356
|
+
out.push(` ${padR("Tokens · 5h", 16)}${padL(`${tok5.percentage || 0}%`, 5)} ${meter(tok5.percentage, 22)} ${reset}`);
|
|
357
|
+
}
|
|
358
|
+
if (tok5 && mcp) out.push("");
|
|
359
|
+
if (mcp) {
|
|
360
|
+
const reset = new Date(mcp.nextResetTime ?? "").toLocaleString("en-IN");
|
|
361
|
+
out.push(` ${padR("MCP · this month", 16)}${padL(`${mcp.percentage || 0}%`, 5)} ${meter(mcp.percentage, 22)} ${reset}`);
|
|
362
|
+
const parts = (mcp.usageDetails ?? []).map((d) => `${friendlyTool(d.modelCode)} ${fmtNum(d.usage)}`).join(" ");
|
|
363
|
+
if (parts) out.push(` ${fmtNum(mcp.currentUsage)}M / ${fmtNum(mcp.totol)}M · ${parts}`);
|
|
364
|
+
}
|
|
365
|
+
out.push("");
|
|
366
|
+
out.push(rule());
|
|
367
|
+
return out.join("\n");
|
|
368
|
+
}
|
|
369
|
+
//#endregion
|
|
370
|
+
//#region src/usage.ts
|
|
371
|
+
function requireEnv() {
|
|
372
|
+
const baseUrl = process.env.ANTHROPIC_BASE_URL ?? "";
|
|
373
|
+
const authToken = process.env.ANTHROPIC_AUTH_TOKEN ?? "";
|
|
374
|
+
if (!authToken) throw new Error([
|
|
375
|
+
"Error: ANTHROPIC_AUTH_TOKEN is not set",
|
|
376
|
+
"",
|
|
377
|
+
"Set the environment variable and retry:",
|
|
378
|
+
" export ANTHROPIC_AUTH_TOKEN=\"your-token-here\""
|
|
379
|
+
].join("\n"));
|
|
380
|
+
if (!baseUrl) throw new Error([
|
|
381
|
+
"Error: ANTHROPIC_BASE_URL is not set",
|
|
382
|
+
"",
|
|
383
|
+
"Set the environment variable and retry:",
|
|
384
|
+
" export ANTHROPIC_BASE_URL=\"https://api.z.ai/api/anthropic\"",
|
|
385
|
+
" or",
|
|
386
|
+
" export ANTHROPIC_BASE_URL=\"https://open.bigmodel.cn/api/anthropic\""
|
|
387
|
+
].join("\n"));
|
|
388
|
+
return {
|
|
389
|
+
baseUrl,
|
|
390
|
+
authToken
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
function queryParams(now = /* @__PURE__ */ new Date()) {
|
|
394
|
+
const startDate = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1, now.getHours(), 0, 0, 0);
|
|
395
|
+
const endDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), 59, 59, 999);
|
|
396
|
+
function formatDateTime(date) {
|
|
397
|
+
function p(n) {
|
|
398
|
+
return String(n).padStart(2, "0");
|
|
399
|
+
}
|
|
400
|
+
return `${date.getFullYear()}-${p(date.getMonth() + 1)}-${p(date.getDate())} ${p(date.getHours())}:${p(date.getMinutes())}:${p(date.getSeconds())}`;
|
|
401
|
+
}
|
|
402
|
+
return `?startTime=${encodeURIComponent(formatDateTime(startDate))}&endTime=${encodeURIComponent(formatDateTime(endDate))}`;
|
|
403
|
+
}
|
|
404
|
+
function processQuotaLimit(data) {
|
|
405
|
+
if (!data.limits) return data;
|
|
406
|
+
data.limits = data.limits.map((item) => {
|
|
407
|
+
if (item.type === "TOKENS_LIMIT") return {
|
|
408
|
+
type: "Token usage(5 Hour)",
|
|
409
|
+
percentage: item.percentage,
|
|
410
|
+
nextResetTime: item.nextResetTime
|
|
411
|
+
};
|
|
412
|
+
if (item.type === "TIME_LIMIT") return {
|
|
413
|
+
type: "MCP usage(1 Month)",
|
|
414
|
+
percentage: item.percentage,
|
|
415
|
+
currentUsage: item.currentValue,
|
|
416
|
+
totol: item.usage,
|
|
417
|
+
usageDetails: item.usageDetails,
|
|
418
|
+
nextResetTime: item.nextResetTime
|
|
419
|
+
};
|
|
420
|
+
return item;
|
|
421
|
+
});
|
|
422
|
+
return data;
|
|
423
|
+
}
|
|
424
|
+
async function fetchJson(apiUrl, label, authToken, query = "") {
|
|
425
|
+
const res = await fetch(apiUrl + query, { headers: {
|
|
426
|
+
Authorization: authToken,
|
|
427
|
+
"Accept-Language": "en-US,en",
|
|
428
|
+
"Content-Type": "application/json"
|
|
429
|
+
} });
|
|
430
|
+
const body = await res.text();
|
|
431
|
+
if (res.status !== 200) throw new Error(`[${label}] HTTP ${res.status}\n${body}`);
|
|
432
|
+
try {
|
|
433
|
+
const json = JSON.parse(body);
|
|
434
|
+
return json.data ?? json;
|
|
435
|
+
} catch (e) {
|
|
436
|
+
throw new Error(`[${label}] could not parse response: ${e.message}`, { cause: e });
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
async function fetchReport() {
|
|
440
|
+
const { baseUrl, authToken } = requireEnv();
|
|
441
|
+
const parsedBaseUrl = new URL(baseUrl);
|
|
442
|
+
const baseDomain = `${parsedBaseUrl.protocol}//${parsedBaseUrl.host}`;
|
|
443
|
+
let platform;
|
|
444
|
+
let modelUsageUrl;
|
|
445
|
+
let toolUsageUrl;
|
|
446
|
+
let quotaLimitUrl;
|
|
447
|
+
if (baseUrl.includes("api.z.ai")) {
|
|
448
|
+
platform = "ZAI";
|
|
449
|
+
modelUsageUrl = `${baseDomain}/api/monitor/usage/model-usage`;
|
|
450
|
+
toolUsageUrl = `${baseDomain}/api/monitor/usage/tool-usage`;
|
|
451
|
+
quotaLimitUrl = `${baseDomain}/api/monitor/usage/quota/limit`;
|
|
452
|
+
} else if (baseUrl.includes("open.bigmodel.cn") || baseUrl.includes("dev.bigmodel.cn")) {
|
|
453
|
+
platform = "ZHIPU";
|
|
454
|
+
modelUsageUrl = `${baseDomain}/api/monitor/usage/model-usage`;
|
|
455
|
+
toolUsageUrl = `${baseDomain}/api/monitor/usage/tool-usage`;
|
|
456
|
+
quotaLimitUrl = `${baseDomain}/api/monitor/usage/quota/limit`;
|
|
457
|
+
} else throw new Error([
|
|
458
|
+
`Error: Unrecognized ANTHROPIC_BASE_URL: ${baseUrl}`,
|
|
459
|
+
"",
|
|
460
|
+
"Supported values:",
|
|
461
|
+
" - https://api.z.ai/api/anthropic",
|
|
462
|
+
" - https://open.bigmodel.cn/api/anthropic"
|
|
463
|
+
].join("\n"));
|
|
464
|
+
const query = queryParams();
|
|
465
|
+
try {
|
|
466
|
+
const [model, tool, quotaRaw] = await Promise.all([
|
|
467
|
+
fetchJson(modelUsageUrl, "Model usage", authToken, query),
|
|
468
|
+
fetchJson(toolUsageUrl, "Tool usage", authToken, query),
|
|
469
|
+
fetchJson(quotaLimitUrl, "Quota limit", authToken)
|
|
470
|
+
]);
|
|
471
|
+
return render({
|
|
472
|
+
platform,
|
|
473
|
+
model,
|
|
474
|
+
tool,
|
|
475
|
+
quota: processQuotaLimit(quotaRaw)
|
|
476
|
+
});
|
|
477
|
+
} catch (e) {
|
|
478
|
+
throw new Error(`Request failed: ${e.message}`, { cause: e });
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
//#endregion
|
|
482
|
+
//#region src/index.ts
|
|
483
|
+
if (process.argv.includes("--hook")) {
|
|
484
|
+
let reason;
|
|
485
|
+
try {
|
|
486
|
+
reason = await fetchReport();
|
|
487
|
+
} catch (e) {
|
|
488
|
+
reason = `query failed: ${e.message}`;
|
|
489
|
+
}
|
|
490
|
+
emitHookBlock(reason);
|
|
491
|
+
} else try {
|
|
492
|
+
console.log(await fetchReport());
|
|
493
|
+
} catch (e) {
|
|
494
|
+
console.error(e.message);
|
|
495
|
+
process.exit(1);
|
|
496
|
+
}
|
|
497
|
+
//#endregion
|
|
498
|
+
export {};
|
|
499
|
+
|
|
500
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../core/dist/index.js","../src/format.ts","../src/usage.ts","../src/index.ts"],"sourcesContent":["import { existsSync, readFileSync, readdirSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n//#region src/hook.ts\n/**\n* Read the Claude Code hook event JSON from stdin; empty or unparseable input\n* yields an empty event, never a thrown error — a hook must always answer.\n*/\nfunction readHookEvent(stream = process.stdin) {\n\treturn new Promise((resolve) => {\n\t\tlet data = \"\";\n\t\tstream.setEncoding(\"utf8\");\n\t\tstream.on(\"data\", (chunk) => {\n\t\t\tdata += chunk;\n\t\t});\n\t\tstream.on(\"end\", () => {\n\t\t\ttry {\n\t\t\t\tresolve(JSON.parse(data));\n\t\t\t} catch {\n\t\t\t\tresolve({});\n\t\t\t}\n\t\t});\n\t\tstream.on(\"error\", () => {\n\t\t\tresolve({});\n\t\t});\n\t});\n}\n/**\n* Which transcript lastReply should read, per the hook event: transcript_path\n* when it names a real file, else session_id, else nothing (lastReply then\n* falls back to the newest session for the current project).\n*/\nfunction replyTarget(event) {\n\tif (event.transcript_path !== void 0 && isFile$1(event.transcript_path)) return event.transcript_path;\n\treturn event.session_id || void 0;\n}\nfunction isFile$1(path) {\n\ttry {\n\t\treturn statSync(path).isFile();\n\t} catch {\n\t\treturn false;\n\t}\n}\n/**\n* Print the UserPromptExpansion decision for a zero-token command: `block`\n* keeps the command from reaching the model, with the output as the `reason`.\n*/\nfunction emitHookBlock(reason) {\n\tprocess.stdout.write(`${JSON.stringify({\n\t\tdecision: \"block\",\n\t\treason\n\t})}\\n`);\n}\n//#endregion\n//#region src/last-reply.ts\nfunction isFile(path) {\n\ttry {\n\t\treturn statSync(path).isFile();\n\t} catch {\n\t\treturn false;\n\t}\n}\nfunction textBlocks(entry) {\n\tconst content = entry.message?.content;\n\tif (entry.type !== \"assistant\" || !Array.isArray(content)) return;\n\tconst blocks = content.filter((b) => typeof b === \"object\" && b !== null && b.type === \"text\");\n\treturn blocks.length > 0 ? blocks : void 0;\n}\n/**\n* The last assistant text reply from a Claude Code session, reproducing Claude\n* Code's `/copy` byte-for-byte: the last assistant entry that contains a\n* `text` block, only its `text` block(s) (tool_use / thinking dropped), blocks\n* joined with a blank line, and no trailing newline.\n*\n* @param arg a transcript file, a session UUID under the project dir, or\n* nothing to use the newest session for the current project.\n*/\nfunction lastReply(arg) {\n\tconst claudeDir = process.env.CLAUDE_DIR ?? join(homedir(), \".claude\");\n\tconst proj = (process.env.CLAUDE_PROJECT_DIR ?? process.cwd()).replaceAll(\"/\", \"-\");\n\tconst projDir = join(claudeDir, \"projects\", proj);\n\tlet file;\n\tif (arg !== void 0) file = isFile(arg) ? arg : join(projDir, `${arg}.jsonl`);\n\telse if (existsSync(projDir)) {\n\t\tconst newest = readdirSync(projDir).filter((f) => f.endsWith(\".jsonl\")).map((f) => ({\n\t\t\tf,\n\t\t\tmtimeMs: statSync(join(projDir, f)).mtimeMs\n\t\t})).sort((a, b) => b.mtimeMs - a.mtimeMs).at(0);\n\t\tif (newest) file = join(projDir, newest.f);\n\t}\n\tif (file === void 0 || !isFile(file)) throw new Error(`no session transcript found in ${projDir}`);\n\tlet last;\n\tfor (const line of readFileSync(file, \"utf8\").split(\"\\n\")) {\n\t\tif (!line.includes(\"\\\"assistant\\\"\")) continue;\n\t\tlet entry;\n\t\ttry {\n\t\t\tentry = JSON.parse(line);\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t\tconst blocks = textBlocks(entry);\n\t\tif (blocks) last = blocks;\n\t}\n\treturn (last ?? []).map((b) => b.text ?? \"\").join(\"\\n\\n\");\n}\n//#endregion\n//#region src/text-format.ts\n/**\n* Plain-text rendering primitives shared by the zai and tokens report\n* renderers. Output targets a monospace terminal / hook-block `reason`, so\n* everything here is fixed-width: padding, block-glyph bars, and compact\n* number formatting.\n*/\nvar MONTHS = [\n\t\"Jan\",\n\t\"Feb\",\n\t\"Mar\",\n\t\"Apr\",\n\t\"May\",\n\t\"Jun\",\n\t\"Jul\",\n\t\"Aug\",\n\t\"Sep\",\n\t\"Oct\",\n\t\"Nov\",\n\t\"Dec\"\n];\nvar EIGHTHS = [\n\t\"\",\n\t\"▏\",\n\t\"▎\",\n\t\"▍\",\n\t\"▌\",\n\t\"▋\",\n\t\"▊\",\n\t\"▉\"\n];\nfunction fmtTokens(n) {\n\tif (n == null || Number.isNaN(n)) return \"—\";\n\tif (n >= 1e9) return (n / 1e9).toFixed(1) + \"B\";\n\tif (n >= 1e6) return (n / 1e6).toFixed(1) + \"M\";\n\tif (n >= 1e3) return (n / 1e3).toFixed(1) + \"K\";\n\treturn String(n);\n}\nfunction fmtNum(n) {\n\treturn (n || 0).toLocaleString(\"en-US\");\n}\nfunction padR(s, n) {\n\treturn s.length >= n ? s : s + \" \".repeat(n - s.length);\n}\nfunction padL(s, n) {\n\treturn s.length >= n ? s : \" \".repeat(n - s.length) + s;\n}\n/** Fixed-width bar field (width cols): █ blocks + an eighth-fraction + trailing spaces. */\nfunction barField(v, max, width) {\n\tif (!v || v <= 0 || max <= 0) return \" \".repeat(width);\n\tconst scaled = v / max * width;\n\tlet full = Math.floor(scaled);\n\tlet fi = Math.round((scaled - full) * 8);\n\tif (fi === 8) {\n\t\tfull += 1;\n\t\tfi = 0;\n\t}\n\tif (full === 0 && fi === 0) fi = 1;\n\tlet s = \"█\".repeat(Math.min(full, width));\n\tif (full < width && fi > 0) s += EIGHTHS[fi];\n\tif (s.length < width) s += \" \".repeat(width - s.length);\n\treturn s.slice(0, width);\n}\n/** Filled/empty meter: █ for used, ░ for remaining. */\nfunction meter(pct, width) {\n\tlet filled = Math.round((pct || 0) / 100 * width);\n\tfilled = Math.max(0, Math.min(width, filled));\n\treturn \"█\".repeat(filled) + \"░\".repeat(width - filled);\n}\n//#endregion\nexport { MONTHS, barField, emitHookBlock, fmtNum, fmtTokens, lastReply, meter, padL, padR, readHookEvent, replyTarget };\n\n//# sourceMappingURL=index.js.map","/**\n * Plain-text usage report renderer.\n *\n * Rendered for a monospace terminal / hook-block `reason`, so it must NOT rely on\n * markdown (bold, tables, fenced code). Alignment comes from fixed-width columns\n * and unicode block glyphs. Input is the parsed `data` of the three ZAI/ZHIPU\n * monitor endpoints (model-usage, tool-usage, quota/limit — the last already\n * passed through processQuotaLimit).\n */\n\nimport {\n barField,\n fmtNum,\n fmtTokens,\n meter,\n MONTHS,\n padL,\n padR,\n} from '@v1nvn/agentic-core';\n\n// Lower-half block eighths for vertical bars (1/8 .. 7/8); a full cell uses '█'.\nconst VBLOCKS = '▁▂▃▄▅▆▇';\n\nconst W = 68; // overall rule width\n\nexport interface ModelMixEntry {\n modelName?: string;\n sortOrder?: number;\n totalTokens?: number;\n}\n\nexport interface ZaiModelUsage {\n modelCallCount?: number[];\n modelDataList?: ModelMixEntry[];\n modelSummaryList?: ModelMixEntry[];\n tokensUsage?: number[];\n totalUsage?: { totalModelCallCount?: number; totalTokensUsage?: number };\n x_time?: string[];\n}\n\nexport interface ZaiToolUsage {\n totalUsage?: {\n totalNetworkSearchCount?: number;\n totalSearchMcpCount?: number;\n totalWebReadMcpCount?: number;\n totalZreadMcpCount?: number;\n };\n}\n\nexport interface ZaiQuotaLimit {\n currentUsage?: number;\n nextResetTime?: string;\n percentage?: number;\n totol?: number;\n type?: string;\n usageDetails?: { modelCode?: string; usage?: number }[];\n}\n\nexport interface ZaiQuota {\n level?: string;\n limits?: ZaiQuotaLimit[];\n}\n\nexport interface RenderInput {\n apiOffsetMin?: number;\n localOffsetMin?: number;\n model: ZaiModelUsage;\n now?: Date;\n platform: string;\n quota: ZaiQuota;\n tool: ZaiToolUsage;\n}\n\ninterface Slot {\n day: string;\n h: number;\n time: string;\n}\n\nfunction parseSlot(s: string | undefined): null | Slot {\n const m = /^(\\d{4})-(\\d{2})-(\\d{2}) (\\d{2}):(\\d{2})/.exec(s ?? '');\n if (!m) {\n return null;\n }\n return {\n h: +m[4],\n day: `${MONTHS[+m[2] - 1]} ${m[3]}`,\n time: `${m[4]}:${m[5]}`,\n };\n}\n\n/**\n * Re-express a naive \"YYYY-MM-DD HH:MM\" string from one UTC offset to another.\n * Offsets are minutes east of UTC (Beijing = 480, IST = 330). Pure — does not\n * depend on the runtime timezone, so it is deterministic under test.\n */\nfunction shiftSlot(\n s: string,\n fromOffsetMin: number,\n toOffsetMin: number,\n): null | Slot {\n const m = /^(\\d{4})-(\\d{2})-(\\d{2}) (\\d{2}):(\\d{2})/.exec(s);\n if (!m) {\n return null;\n }\n const utcMs =\n Date.UTC(+m[1], +m[2] - 1, +m[3], +m[4], +m[5]) - fromOffsetMin * 60000;\n const dt = new Date(utcMs + toOffsetMin * 60000);\n return {\n h: dt.getUTCHours(),\n day: `${MONTHS[dt.getUTCMonth()]} ${String(dt.getUTCDate()).padStart(2, '0')}`,\n time: `${String(dt.getUTCHours()).padStart(2, '0')}:${String(dt.getUTCMinutes()).padStart(2, '0')}`,\n };\n}\n\nfunction friendlyTool(code: string | undefined): string {\n switch ((code ?? '').toLowerCase()) {\n case 'search-prime':\n return 'web search';\n case 'web-reader':\n return 'web read';\n default:\n return code || 'other';\n }\n}\n\nfunction idleRuns(tok: number[]): { i: number; j: number; len: number }[] {\n const runs: { i: number; j: number; len: number }[] = [];\n let i = 0;\n while (i < tok.length) {\n if ((tok[i] || 0) === 0) {\n let j = i;\n while (j < tok.length && (tok[j] || 0) === 0) {\n j++;\n }\n runs.push({ i, j: j - 1, len: j - i });\n i = j;\n } else {\n i++;\n }\n }\n return runs;\n}\n\nfunction idleLabel(\n run: { i: number; j: number; len: number },\n x: string[],\n slotFn: (s: string) => null | Slot,\n): string {\n const a = slotFn(x[run.i]);\n const b = slotFn(x[run.j]);\n if (!a || !b) {\n return `idle (${run.len}h)`;\n }\n const start = `${a.day} ${a.time}`;\n const end = a.day === b.day ? b.time : `${b.day} ${b.time}`;\n return `idle ${start} → ${end} (${run.len}h)`;\n}\n\n// Per the usage-revision notice: Mon–Fri 14:00–18:00 Beijing (UTC+8) are peak\n// hours, where GLM-5.2 / GLM-5-Turbo tokens are billed at a 3× coefficient\n// (GLM-4.7 stays 1× all day). A bucket labelled \"HH:00\" spans HH:00–HH+1, so\n// the peak buckets are HH ∈ {14,15,16,17} on a weekday.\nfunction isPeakBucket(s: string | undefined): boolean {\n const m = /^(\\d{4})-(\\d{2})-(\\d{2}) (\\d{2}):/.exec(s ?? '');\n if (!m) {\n return false;\n }\n const wd = new Date(Date.UTC(+m[1], +m[2] - 1, +m[3])).getUTCDay(); // 0=Sun … 6=Sat\n return wd >= 1 && wd <= 5 && +m[4] >= 14 && +m[4] <= 17;\n}\n\n/** Local-time start/end of the 14:00–18:00 Beijing peak window (e.g. 11:30 / 15:30). */\nfunction localPeakWindow(\n apiOffsetMin: number,\n toOffsetMin: number,\n): null | { end: string; start: string } {\n const a = shiftSlot('2000-01-03 14:00', apiOffsetMin, toOffsetMin);\n const b = shiftSlot('2000-01-03 18:00', apiOffsetMin, toOffsetMin);\n if (!a || !b) {\n return null;\n }\n return { start: a.time, end: b.time };\n}\n\n/**\n * Vertical bar chart of hourly tokens. Returns an array of plain-text lines\n * (without the section header), or null if there are fewer than 2 buckets.\n *\n * Bars are scaled to `maxTok` across `ROWS` rows; the topmost filled cell of a\n * bar uses a fractional lower-block glyph (▁▂▃▄▅▆▇) for sub-row height. The\n * y-axis labels only the peak (top) and 0 (baseline); the header window and the\n * caller's peak annotation carry the exact values.\n */\nfunction hourlyVerticalChart({\n x,\n tok,\n nh,\n maxTok,\n peakIdx,\n slotFn,\n}: {\n maxTok: number;\n nh: number;\n peakIdx: number;\n slotFn: (s: string) => null | Slot;\n tok: number[];\n x: string[];\n}): null | string[] {\n const ROWS = 6; // bar rows above the baseline\n if (nh < 2 || maxTok <= 0) {\n return null;\n }\n\n const lines: string[] = [];\n const maxLab = fmtTokens(maxTok);\n const L = maxLab.length;\n const IND = 1; // leading indent\n const AXIS = IND + L + 1; // axis spine col (1-space gap after the label)\n const P0 = AXIS + 2; // first bar column (axis + 1-char gap)\n const PW = W - P0; // columns available for bars + gaps\n const slot = Math.max(1, Math.floor(PW / nh));\n const barW = slot >= 2 ? slot - 1 : 1;\n function barLeft(i: number): number {\n return P0 + i * slot;\n }\n function barCenter(i: number): number {\n return barLeft(i) + Math.floor(barW / 2);\n }\n const usedCols = nh * slot;\n function blank(): string[] {\n return Array<string>(W).fill(' ');\n }\n\n // bar glyph for hour i at row r (0 = bottom bar-row, ROWS-1 = top)\n function glyph(i: number, r: number): string {\n const v = tok[i] || 0;\n if (v <= 0) {\n return ' ';\n }\n const fill = (v / maxTok) * ROWS - r; // how much of row r the bar covers\n if (fill >= 1) {\n return '█';\n }\n if (fill > 0) {\n let e = Math.round(fill * 8); // 1..7 eighths\n if (e < 1) {\n e = 1;\n }\n if (e > 7) {\n e = 7;\n }\n return VBLOCKS[e - 1];\n }\n return ' ';\n }\n\n // bar rows, top → bottom; top row carries the max label + '┤', rest are '│'\n for (let r = ROWS - 1; r >= 0; r--) {\n const line = blank();\n line[AXIS] = r === ROWS - 1 ? '┤' : '│';\n if (r === ROWS - 1) {\n for (let k = 0; k < L; k++) {\n line[AXIS - 1 - L + k] = maxLab[k];\n }\n }\n for (let i = 0; i < nh; i++) {\n const g = glyph(i, r);\n if (g !== ' ') {\n for (let c = 0; c < barW; c++) {\n line[barLeft(i) + c] = g;\n }\n }\n }\n lines.push(line.join('').trimEnd());\n }\n\n // x-axis ticks: first, last, and every bucket landing on a 6h boundary (local)\n function slotOf(i: number): null | Slot {\n return slotFn(x[i]);\n }\n function isTick(i: number): boolean {\n if (i === 0 || i === nh - 1) {\n return true;\n }\n const s = slotOf(i);\n return !!s && s.h % 6 === 0;\n }\n\n // baseline: '0' label + corner '└' + '─' run, with '┬' under each tick\n const base = blank();\n base[AXIS - 2] = '0';\n base[AXIS] = '└';\n for (let c = AXIS + 1; c < P0 + usedCols; c++) {\n base[c] = '─';\n }\n for (let i = 0; i < nh; i++) {\n if (isTick(i)) {\n base[barCenter(i)] = '┬';\n }\n }\n lines.push(base.join('').trimEnd());\n\n // hour labels under ticks (skip if too close to the previous label)\n const hr = blank();\n let last = -10;\n for (let i = 0; i < nh; i++) {\n if (!isTick(i)) {\n continue;\n }\n const col = barCenter(i);\n if (col - last < 3) {\n continue;\n }\n const s = slotOf(i);\n const txt = s ? String(s.h).padStart(2, '0') : ' ';\n hr[col] = txt[0];\n hr[col + 1] = txt[1];\n last = col;\n }\n lines.push(hr.join('').trimEnd());\n\n // day labels under the first tick of each new local day\n const day = blank();\n let lastDay: null | string = null;\n last = -10;\n for (let i = 0; i < nh; i++) {\n if (!isTick(i)) {\n continue;\n }\n const s = slotOf(i);\n if (!s || s.day === lastDay) {\n continue;\n }\n const col = barCenter(i);\n if (col - last >= s.day.length && col + s.day.length <= W) {\n for (let k = 0; k < s.day.length; k++) {\n day[col + k] = s.day[k];\n }\n lastDay = s.day;\n last = col + s.day.length;\n }\n }\n if (day.some(c => c !== ' ')) {\n lines.push(day.join('').trimEnd());\n }\n\n // peak-window (↑) and peak (◂) markers under bar centers\n const mark = blank();\n for (let i = 0; i < nh; i++) {\n if (isPeakBucket(x[i])) {\n mark[barCenter(i)] = '↑';\n }\n }\n if (peakIdx >= 0) {\n mark[barCenter(peakIdx)] = '◂';\n }\n if (mark.some(c => c !== ' ')) {\n lines.push(mark.join('').trimEnd());\n }\n\n return lines;\n}\n\nexport function render({\n platform,\n model,\n tool,\n quota,\n apiOffsetMin = 480,\n localOffsetMin,\n now = new Date(),\n}: RenderInput): string {\n const out: string[] = [];\n function rule(): string {\n return '─'.repeat(W);\n }\n\n // The monitor API labels every bucket in Beijing time (UTC+8). Shift each\n // timestamp to the caller's offset (minutes east of UTC) for display.\n // Defaults to the JS runtime's local zone.\n const toOffset =\n localOffsetMin != null ? localOffsetMin : -new Date().getTimezoneOffset();\n function convertSlot(s: string): null | Slot {\n return shiftSlot(s, apiOffsetMin, toOffset) ?? parseSlot(s);\n }\n\n const x = model.x_time ?? [];\n const tok = model.tokensUsage ?? [];\n const calls = model.modelCallCount ?? [];\n const nh = x.length;\n\n const tu = model.totalUsage ?? {};\n const total =\n tu.totalTokensUsage != null\n ? tu.totalTokensUsage\n : tok.reduce((a, b) => a + (b || 0), 0);\n const totalCalls =\n tu.totalModelCallCount != null\n ? tu.totalModelCallCount\n : calls.reduce((a, b) => a + (b || 0), 0);\n\n // peak hour\n let peakIdx = -1;\n let peakTok = 0;\n for (let i = 0; i < tok.length; i++) {\n if (tok[i] > peakTok) {\n peakTok = tok[i];\n peakIdx = i;\n }\n }\n const peakCalls = peakIdx >= 0 ? calls[peakIdx] || 0 : 0;\n const pctPeak = total > 0 ? (peakTok / total) * 100 : 0;\n const peakSlot = peakIdx >= 0 ? convertSlot(x[peakIdx]) : null;\n\n const level = quota.level\n ? quota.level.charAt(0).toUpperCase() + quota.level.slice(1)\n : '';\n\n // ---- header ----\n const left = ` GLM Coding Plan${level ? ' · ' + level : ''}`;\n const firstSlot = convertSlot(x[0]);\n const lastSlot = convertSlot(x[nh - 1]);\n const win =\n firstSlot && lastSlot\n ? `${firstSlot.day} ${firstSlot.time} → ${lastSlot.day} ${lastSlot.time} · ${nh}h`\n : platform;\n out.push(rule());\n out.push(left + padL(win, W - left.length));\n out.push(rule());\n\n // ---- lead ----\n let lead = ` ${fmtTokens(total)} tokens across ${fmtNum(totalCalls)} model calls`;\n if (peakIdx >= 0 && peakSlot) {\n lead += ` — ${Math.round(pctPeak)}% of it in a single hour (${peakSlot.day} ${peakSlot.time}, ${fmtTokens(peakTok)} tokens / ${fmtNum(peakCalls)} calls)`;\n }\n lead += '.';\n out.push('');\n out.push(lead);\n\n // ---- stat block ----\n const activeHours = tok.filter(t => t > 0).length;\n const longest = idleRuns(tok)\n .filter(r => r.len >= 2)\n .sort((a, b) => b.len - a.len)\n .at(0);\n const tt = tool.totalUsage ?? {};\n const searchN = tt.totalNetworkSearchCount || 0;\n const readN = tt.totalWebReadMcpCount || 0;\n const zreadN = tt.totalZreadMcpCount || 0;\n const toolTotal =\n tt.totalSearchMcpCount != null\n ? tt.totalSearchMcpCount\n : searchN + readN + zreadN;\n\n // how much of the window's usage fell inside the 3× billing peak hours\n let peakWinActive = 0;\n let peakWinTokens = 0;\n for (let k = 0; k < nh; k++) {\n if (isPeakBucket(x[k]) && tok[k] > 0) {\n peakWinActive++;\n peakWinTokens += tok[k];\n }\n }\n const peakWinPct = total > 0 ? (peakWinTokens / total) * 100 : 0;\n const peakWin = localPeakWindow(apiOffsetMin, toOffset);\n\n out.push('');\n out.push(\n ` Peak ${padR(peakSlot ? peakSlot.day + ' ' + peakSlot.time : '—', 15)}${padL(fmtTokens(peakTok), 7)} tokens · ${padL(fmtNum(peakCalls), 5)} calls`,\n );\n out.push(\n ` Active ${padR(`${activeHours} / ${nh} hours`, 15)}${longest ? idleLabel(longest, x, convertSlot) : 'no idle gaps'}`,\n );\n out.push(\n ` Tools ${padR(`${toolTotal} calls`, 15)}${searchN} searches · ${readN} reads${zreadN ? ` · ${zreadN} zread` : ''}`,\n );\n if (peakWin) {\n out.push(\n ` Peak hrs Mon–Fri ${peakWin.start}–${peakWin.end} · GLM-5.2 3× · ${peakWinActive}h active · ${fmtTokens(peakWinTokens)} (${Math.round(peakWinPct)}%)`,\n );\n }\n\n // ---- hourly chart (vertical bars) ----\n out.push('');\n const chartHdr = ' Hourly tokens · ↑ peak hour ';\n out.push(chartHdr + '─'.repeat(Math.max(0, W - chartHdr.length)));\n const maxTok = peakTok || 1;\n const chartLines = hourlyVerticalChart({\n x,\n tok,\n nh,\n maxTok,\n peakIdx,\n slotFn: convertSlot,\n });\n if (chartLines) {\n out.push(...chartLines);\n if (peakIdx >= 0 && peakSlot) {\n out.push(\n ` ◂ peak ${peakSlot.day} ${peakSlot.time} ${fmtTokens(peakTok)} tokens · ${fmtNum(peakCalls)} calls`,\n );\n }\n } else {\n out.push(' (not enough hourly data to chart)');\n }\n\n // ---- model mix ----\n out.push('');\n out.push(' Model mix ' + '─'.repeat(Math.max(0, W - 11)));\n const mixSrc = model.modelSummaryList ?? model.modelDataList ?? [];\n const denom = mixSrc.reduce((a, m) => a + (m.totalTokens ?? 0), 0) || 1;\n const mixSorted = [...mixSrc].sort(\n (a, b) => (a.sortOrder || 0) - (b.sortOrder || 0),\n );\n for (const m of mixSorted) {\n const pct = ((m.totalTokens ?? 0) / denom) * 100;\n out.push(\n ` ${padR(m.modelName || '?', 11)}${padL(fmtTokens(m.totalTokens), 8)} ${padL(pct.toFixed(1) + '%', 6)} ${barField(pct, 100, 20)}`,\n );\n }\n\n // ---- limits ----\n out.push('');\n out.push(' Limits ' + '─'.repeat(Math.max(0, W - 8)));\n\n // Peak-window row, always present under Limits. The bar is now's progress\n // through the 14:00–18:00 Beijing window: empty before it opens, full after\n // it closes, and no fill on weekends, when the window does not bill. The\n // start/end labels come from peakWin, already shifted to the caller's offset.\n const bj = new Date(now.getTime() + apiOffsetMin * 60000);\n const bjDay = bj.getUTCDay();\n const inWindow = bjDay >= 1 && bjDay <= 5;\n const elapsedMin = inWindow\n ? Math.max(\n 0,\n Math.min(240, (bj.getUTCHours() - 14) * 60 + bj.getUTCMinutes()),\n )\n : 0;\n if (peakWin) {\n out.push(\n ` ${padR('Peak', 16)}${padL(peakWin.start, 5)} ${meter(Math.round((elapsedMin / 240) * 100), 22)} ${peakWin.end}`,\n );\n }\n\n const limits = quota.limits ?? [];\n const mcp = limits.find(l => /mcp/i.test(l.type ?? ''));\n const tok5 = limits.find(l => /token/i.test(l.type ?? ''));\n if (tok5) {\n const reset = new Date(tok5.nextResetTime ?? '').toLocaleTimeString(\n 'en-IN',\n );\n out.push(\n ` ${padR('Tokens · 5h', 16)}${padL(`${tok5.percentage || 0}%`, 5)} ${meter(tok5.percentage, 22)} ${reset}`,\n );\n }\n\n if (tok5 && mcp) {\n out.push('');\n }\n\n if (mcp) {\n const reset = new Date(mcp.nextResetTime ?? '').toLocaleString('en-IN');\n out.push(\n ` ${padR('MCP · this month', 16)}${padL(`${mcp.percentage || 0}%`, 5)} ${meter(mcp.percentage, 22)} ${reset}`,\n );\n const det = mcp.usageDetails ?? [];\n const parts = det\n .map(d => `${friendlyTool(d.modelCode)} ${fmtNum(d.usage)}`)\n .join(' ');\n if (parts) {\n out.push(\n ` ${fmtNum(mcp.currentUsage)}M / ${fmtNum(mcp.totol)}M · ${parts}`,\n );\n }\n }\n\n out.push('');\n out.push(rule());\n return out.join('\\n');\n}\n","/**\n * Usage query. Determines whether to call the Z.ai or ZHIPU endpoint based on\n * ANTHROPIC_BASE_URL and authenticates with ANTHROPIC_AUTH_TOKEN. Fetches model\n * usage, tool usage, and quota limits, then renders the plain-text report.\n */\n\nimport type { ZaiModelUsage, ZaiQuota, ZaiToolUsage } from './format.js';\n\nimport { render } from './format.js';\n\ninterface RawLimit {\n currentValue?: number;\n nextResetTime?: string;\n percentage?: number;\n type?: string;\n usage?: number;\n usageDetails?: { modelCode?: string; usage?: number }[];\n}\n\ninterface RawQuota {\n level?: string;\n limits?: RawLimit[];\n}\n\nfunction requireEnv(): { authToken: string; baseUrl: string } {\n const baseUrl = process.env.ANTHROPIC_BASE_URL ?? '';\n const authToken = process.env.ANTHROPIC_AUTH_TOKEN ?? '';\n\n if (!authToken) {\n throw new Error(\n [\n 'Error: ANTHROPIC_AUTH_TOKEN is not set',\n '',\n 'Set the environment variable and retry:',\n ' export ANTHROPIC_AUTH_TOKEN=\"your-token-here\"',\n ].join('\\n'),\n );\n }\n if (!baseUrl) {\n throw new Error(\n [\n 'Error: ANTHROPIC_BASE_URL is not set',\n '',\n 'Set the environment variable and retry:',\n ' export ANTHROPIC_BASE_URL=\"https://api.z.ai/api/anthropic\"',\n ' or',\n ' export ANTHROPIC_BASE_URL=\"https://open.bigmodel.cn/api/anthropic\"',\n ].join('\\n'),\n );\n }\n return { baseUrl, authToken };\n}\n\n// The monitor API labels every bucket in Beijing time; the query window runs\n// from yesterday at the current hour to the end of today's current hour.\nfunction queryParams(now = new Date()): string {\n const startDate = new Date(\n now.getFullYear(),\n now.getMonth(),\n now.getDate() - 1,\n now.getHours(),\n 0,\n 0,\n 0,\n );\n const endDate = new Date(\n now.getFullYear(),\n now.getMonth(),\n now.getDate(),\n now.getHours(),\n 59,\n 59,\n 999,\n );\n function formatDateTime(date: Date): string {\n function p(n: number): string {\n return String(n).padStart(2, '0');\n }\n return `${date.getFullYear()}-${p(date.getMonth() + 1)}-${p(date.getDate())} ${p(date.getHours())}:${p(date.getMinutes())}:${p(date.getSeconds())}`;\n }\n return `?startTime=${encodeURIComponent(formatDateTime(startDate))}&endTime=${encodeURIComponent(formatDateTime(endDate))}`;\n}\n\n// Rename the API's limit kinds to their display names; `totol` is the field the\n// renderer reads (sic — the upstream spelling).\nfunction processQuotaLimit(data: RawQuota): ZaiQuota {\n if (!data.limits) {\n return data;\n }\n data.limits = data.limits.map(item => {\n if (item.type === 'TOKENS_LIMIT') {\n return {\n type: 'Token usage(5 Hour)',\n percentage: item.percentage,\n nextResetTime: item.nextResetTime,\n };\n }\n if (item.type === 'TIME_LIMIT') {\n return {\n type: 'MCP usage(1 Month)',\n percentage: item.percentage,\n currentUsage: item.currentValue,\n totol: item.usage,\n usageDetails: item.usageDetails,\n nextResetTime: item.nextResetTime,\n };\n }\n return item;\n });\n return data;\n}\n\nasync function fetchJson(\n apiUrl: string,\n label: string,\n authToken: string,\n query = '',\n): Promise<unknown> {\n const res = await fetch(apiUrl + query, {\n headers: {\n Authorization: authToken,\n 'Accept-Language': 'en-US,en',\n 'Content-Type': 'application/json',\n },\n });\n const body = await res.text();\n if (res.status !== 200) {\n throw new Error(`[${label}] HTTP ${res.status}\\n${body}`);\n }\n try {\n const json = JSON.parse(body) as { data?: unknown };\n return json.data ?? json;\n } catch (e) {\n throw new Error(\n `[${label}] could not parse response: ${(e as Error).message}`,\n { cause: e },\n );\n }\n}\n\nexport async function fetchReport(): Promise<string> {\n const { baseUrl, authToken } = requireEnv();\n\n const parsedBaseUrl = new URL(baseUrl);\n const baseDomain = `${parsedBaseUrl.protocol}//${parsedBaseUrl.host}`;\n let platform: string;\n let modelUsageUrl: string;\n let toolUsageUrl: string;\n let quotaLimitUrl: string;\n\n if (baseUrl.includes('api.z.ai')) {\n platform = 'ZAI';\n modelUsageUrl = `${baseDomain}/api/monitor/usage/model-usage`;\n toolUsageUrl = `${baseDomain}/api/monitor/usage/tool-usage`;\n quotaLimitUrl = `${baseDomain}/api/monitor/usage/quota/limit`;\n } else if (\n baseUrl.includes('open.bigmodel.cn') ||\n baseUrl.includes('dev.bigmodel.cn')\n ) {\n platform = 'ZHIPU';\n modelUsageUrl = `${baseDomain}/api/monitor/usage/model-usage`;\n toolUsageUrl = `${baseDomain}/api/monitor/usage/tool-usage`;\n quotaLimitUrl = `${baseDomain}/api/monitor/usage/quota/limit`;\n } else {\n throw new Error(\n [\n `Error: Unrecognized ANTHROPIC_BASE_URL: ${baseUrl}`,\n '',\n 'Supported values:',\n ' - https://api.z.ai/api/anthropic',\n ' - https://open.bigmodel.cn/api/anthropic',\n ].join('\\n'),\n );\n }\n\n const query = queryParams();\n try {\n const [model, tool, quotaRaw] = await Promise.all([\n fetchJson(modelUsageUrl, 'Model usage', authToken, query),\n fetchJson(toolUsageUrl, 'Tool usage', authToken, query),\n fetchJson(quotaLimitUrl, 'Quota limit', authToken),\n ]);\n return render({\n platform,\n model: model as ZaiModelUsage,\n tool: tool as ZaiToolUsage,\n quota: processQuotaLimit(quotaRaw as RawQuota),\n });\n } catch (e) {\n throw new Error(`Request failed: ${(e as Error).message}`, { cause: e });\n }\n}\n","import { emitHookBlock } from '@v1nvn/agentic-core';\n\nimport { fetchReport } from './usage.js';\n\nif (process.argv.includes('--hook')) {\n let reason: string;\n try {\n reason = await fetchReport();\n } catch (e) {\n reason = `query failed: ${(e as Error).message}`;\n }\n emitHookBlock(reason);\n} else {\n try {\n console.log(await fetchReport());\n } catch (e) {\n console.error((e as Error).message);\n // CLIs report failure through the exit code; the rule targets libraries.\n // eslint-disable-next-line n/no-process-exit\n process.exit(1);\n }\n}\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@v1nvn/zai",
|
|
3
|
+
"version": "0.14.0",
|
|
4
|
+
"description": "GLM Coding Plan usage and quota report — the zai-usage CLI behind the zai plugin.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"zai-usage": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "vite build",
|
|
11
|
+
"test": "vitest run",
|
|
12
|
+
"test:watch": "vitest"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"glm",
|
|
16
|
+
"zai",
|
|
17
|
+
"claude-code",
|
|
18
|
+
"usage"
|
|
19
|
+
],
|
|
20
|
+
"author": "v1nvn",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+https://github.com/v1nvn/agentic.git",
|
|
25
|
+
"directory": "packages/zai"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist"
|
|
29
|
+
],
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=22"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@v1nvn/agentic-core": "^0.14.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"vite": "^8.1.4",
|
|
41
|
+
"vitest": "^4.1.10"
|
|
42
|
+
}
|
|
43
|
+
}
|