@leo000001/opencode-quota-sidebar 4.0.9 → 4.0.11
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 +504 -446
- package/README.zh-CN.md +516 -458
- package/dist/cli.d.ts +31 -0
- package/dist/cli.js +95 -40
- package/dist/cli_render.d.ts +4 -4
- package/dist/cli_render.js +74 -74
- package/dist/cost.d.ts +21 -4
- package/dist/cost.js +493 -264
- package/dist/format.d.ts +5 -5
- package/dist/format.js +288 -287
- package/dist/history_usage.d.ts +15 -9
- package/dist/history_usage.js +28 -22
- package/dist/index.d.ts +3 -3
- package/dist/index.js +35 -34
- package/dist/models_dev_pricing.d.ts +6 -0
- package/dist/models_dev_pricing.js +226 -0
- package/dist/opencode_pricing.d.ts +14 -0
- package/dist/opencode_pricing.js +273 -0
- package/dist/storage.d.ts +3 -3
- package/dist/storage.js +27 -28
- package/dist/storage_parse.d.ts +1 -1
- package/dist/storage_parse.js +51 -45
- package/dist/storage_paths.d.ts +1 -0
- package/dist/storage_paths.js +26 -11
- package/dist/title_apply.d.ts +5 -22
- package/dist/title_apply.js +19 -61
- package/dist/tui.d.ts +1 -1
- package/dist/tui.tsx +481 -471
- package/dist/tui_helpers.d.ts +5 -3
- package/dist/tui_helpers.js +62 -34
- package/dist/types.d.ts +8 -10
- package/dist/usage.d.ts +9 -6
- package/dist/usage.js +27 -21
- package/dist/usage_service.d.ts +8 -7
- package/dist/usage_service.js +261 -150
- package/package.json +1 -1
package/dist/format.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { getCacheCoverageMetrics, getProviderCacheCoverageMetrics, } from
|
|
2
|
-
import { canonicalProviderID, collapseQuotaSnapshots, displayShortLabel, quotaDisplayLabel, } from
|
|
3
|
-
import { stripAnsi } from
|
|
1
|
+
import { getCacheCoverageMetrics, getProviderCacheCoverageMetrics, } from "./usage.js";
|
|
2
|
+
import { canonicalProviderID, collapseQuotaSnapshots, displayShortLabel, quotaDisplayLabel, } from "./quota_render.js";
|
|
3
|
+
import { stripAnsi } from "./title.js";
|
|
4
4
|
/** M6 fix: handle negative, NaN, Infinity gracefully. */
|
|
5
5
|
function shortNumber(value, decimals = 1) {
|
|
6
6
|
if (!Number.isFinite(value) || value < 0)
|
|
7
|
-
return
|
|
7
|
+
return "0";
|
|
8
8
|
if (value >= 1_000_000)
|
|
9
9
|
return `${(value / 1_000_000).toFixed(decimals)}m`;
|
|
10
10
|
if (value >= 1000) {
|
|
@@ -23,9 +23,9 @@ function sidebarNumber(value) {
|
|
|
23
23
|
function sanitizeLine(value) {
|
|
24
24
|
// Sidebars/titles must be plain text: no ANSI and no embedded newlines.
|
|
25
25
|
return (stripAnsi(value)
|
|
26
|
-
.replace(/\r?\n/g,
|
|
26
|
+
.replace(/\r?\n/g, " ")
|
|
27
27
|
// Remove control characters that can corrupt TUI rendering.
|
|
28
|
-
.replace(/[\x00-\x1F\x7F-\x9F]/g,
|
|
28
|
+
.replace(/[\x00-\x1F\x7F-\x9F]/g, " "));
|
|
29
29
|
}
|
|
30
30
|
function isCombiningCodePoint(codePoint) {
|
|
31
31
|
return ((codePoint >= 0x0300 && codePoint <= 0x036f) ||
|
|
@@ -93,13 +93,13 @@ function padEndCells(value, targetWidth) {
|
|
|
93
93
|
const current = stringCellWidth(value);
|
|
94
94
|
if (current >= targetWidth)
|
|
95
95
|
return value;
|
|
96
|
-
return `${value}${
|
|
96
|
+
return `${value}${" ".repeat(targetWidth - current)}`;
|
|
97
97
|
}
|
|
98
98
|
function truncateToCellWidth(value, width) {
|
|
99
99
|
if (width <= 0)
|
|
100
|
-
return
|
|
100
|
+
return "";
|
|
101
101
|
let used = 0;
|
|
102
|
-
let out =
|
|
102
|
+
let out = "";
|
|
103
103
|
for (const char of value) {
|
|
104
104
|
const w = cellWidthOfCodePoint(char.codePointAt(0) || 0);
|
|
105
105
|
if (used + w > width)
|
|
@@ -115,7 +115,7 @@ function truncateToCellWidth(value, width) {
|
|
|
115
115
|
*/
|
|
116
116
|
export function fitLine(value, width) {
|
|
117
117
|
if (width <= 0)
|
|
118
|
-
return
|
|
118
|
+
return "";
|
|
119
119
|
const safe = sanitizeLine(value);
|
|
120
120
|
if (stringCellWidth(safe) <= width)
|
|
121
121
|
return safe;
|
|
@@ -130,7 +130,7 @@ export function fitLine(value, width) {
|
|
|
130
130
|
}
|
|
131
131
|
function formatCurrency(value, currency) {
|
|
132
132
|
const safe = Number.isFinite(value) ? value : 0;
|
|
133
|
-
const prefix = typeof currency ===
|
|
133
|
+
const prefix = typeof currency === "string" && currency ? currency : "$";
|
|
134
134
|
if (safe === 0)
|
|
135
135
|
return `${prefix}0.00`;
|
|
136
136
|
if (safe < 0) {
|
|
@@ -138,28 +138,28 @@ function formatCurrency(value, currency) {
|
|
|
138
138
|
if (abs < 10)
|
|
139
139
|
return `-${prefix}${abs.toFixed(2)}`;
|
|
140
140
|
const one = abs.toFixed(1);
|
|
141
|
-
const trimmed = one.endsWith(
|
|
141
|
+
const trimmed = one.endsWith(".0") ? one.slice(0, -2) : one;
|
|
142
142
|
return `-${prefix}${trimmed}`;
|
|
143
143
|
}
|
|
144
144
|
if (safe < 10)
|
|
145
145
|
return `${prefix}${safe.toFixed(2)}`;
|
|
146
146
|
const one = safe.toFixed(1);
|
|
147
|
-
const trimmed = one.endsWith(
|
|
147
|
+
const trimmed = one.endsWith(".0") ? one.slice(0, -2) : one;
|
|
148
148
|
return `${prefix}${trimmed}`;
|
|
149
149
|
}
|
|
150
150
|
function formatUsd(value) {
|
|
151
|
-
return formatCurrency(value,
|
|
151
|
+
return formatCurrency(value, "$");
|
|
152
152
|
}
|
|
153
153
|
function formatApiCostValue(value) {
|
|
154
154
|
return formatUsd(value);
|
|
155
155
|
}
|
|
156
156
|
function formatApiCostLine(value) {
|
|
157
|
-
return
|
|
157
|
+
return `API ${formatApiCostValue(value)}`;
|
|
158
158
|
}
|
|
159
159
|
function trimTrailingZeroUnit(value) {
|
|
160
160
|
return value
|
|
161
|
-
.replace(/(\d+)\.0(?=[km]\b)/i,
|
|
162
|
-
.replace(/(\d+)\.0(?=$)/,
|
|
161
|
+
.replace(/(\d+)\.0(?=[km]\b)/i, "$1")
|
|
162
|
+
.replace(/(\d+)\.0(?=$)/, "$1");
|
|
163
163
|
}
|
|
164
164
|
function panelNumber(value) {
|
|
165
165
|
return trimTrailingZeroUnit(shortNumber(value, 1));
|
|
@@ -170,11 +170,11 @@ function formatRequestsLabel(value, short = false) {
|
|
|
170
170
|
}
|
|
171
171
|
export function resolveTitleView(opts) {
|
|
172
172
|
void opts;
|
|
173
|
-
if (opts.config.sidebar.titleMode ===
|
|
174
|
-
return
|
|
175
|
-
if (opts.config.sidebar.titleMode ===
|
|
176
|
-
return
|
|
177
|
-
return
|
|
173
|
+
if (opts.config.sidebar.titleMode === "compact")
|
|
174
|
+
return "compact";
|
|
175
|
+
if (opts.config.sidebar.titleMode === "multiline")
|
|
176
|
+
return "multiline";
|
|
177
|
+
return "compact";
|
|
178
178
|
}
|
|
179
179
|
function desktopCompactSettings(config) {
|
|
180
180
|
return {
|
|
@@ -209,67 +209,67 @@ export function selectDesktopCompactProviderIDs(usage, config, now = Date.now())
|
|
|
209
209
|
}
|
|
210
210
|
function compactProviderLabel(quota) {
|
|
211
211
|
const canonical = canonicalProviderID(quota.adapterID || quota.providerID);
|
|
212
|
-
if (canonical ===
|
|
213
|
-
return
|
|
214
|
-
if (canonical ===
|
|
215
|
-
return
|
|
216
|
-
if (canonical ===
|
|
217
|
-
return
|
|
218
|
-
if (canonical ===
|
|
219
|
-
return
|
|
220
|
-
if (canonical ===
|
|
221
|
-
return
|
|
222
|
-
if (canonical ===
|
|
223
|
-
return
|
|
224
|
-
if (canonical ===
|
|
225
|
-
return
|
|
212
|
+
if (canonical === "openai")
|
|
213
|
+
return "OAI";
|
|
214
|
+
if (canonical === "github-copilot")
|
|
215
|
+
return "Cop";
|
|
216
|
+
if (canonical === "anthropic")
|
|
217
|
+
return "Ant";
|
|
218
|
+
if (canonical === "kimi-for-coding")
|
|
219
|
+
return "Kimi";
|
|
220
|
+
if (canonical === "zhipuai-coding-plan")
|
|
221
|
+
return "Zhipu";
|
|
222
|
+
if (canonical === "minimax-cn-coding-plan")
|
|
223
|
+
return "MiniMax";
|
|
224
|
+
if (canonical === "rightcode")
|
|
225
|
+
return "RC";
|
|
226
226
|
return sanitizeLine(quotaDisplayLabel(quota));
|
|
227
227
|
}
|
|
228
228
|
function compactWindowToken(label) {
|
|
229
|
-
const safe = sanitizeLine(label ||
|
|
229
|
+
const safe = sanitizeLine(label || "");
|
|
230
230
|
if (!safe)
|
|
231
|
-
return
|
|
231
|
+
return "";
|
|
232
232
|
if (/^daily$/i.test(safe))
|
|
233
|
-
return
|
|
233
|
+
return "D";
|
|
234
234
|
if (/^weekly$/i.test(safe))
|
|
235
|
-
return
|
|
235
|
+
return "W";
|
|
236
236
|
if (/^monthly$/i.test(safe))
|
|
237
|
-
return
|
|
237
|
+
return "M";
|
|
238
238
|
if (/^1d$/i.test(safe))
|
|
239
|
-
return
|
|
239
|
+
return "D";
|
|
240
240
|
return safe;
|
|
241
241
|
}
|
|
242
242
|
function compactQuotaResetToken(resetLabel) {
|
|
243
|
-
const safe = sanitizeLine(resetLabel ||
|
|
243
|
+
const safe = sanitizeLine(resetLabel || "");
|
|
244
244
|
if (!safe || /^rst$/i.test(safe))
|
|
245
|
-
return
|
|
245
|
+
return "R";
|
|
246
246
|
if (/^exp\+$/i.test(safe))
|
|
247
|
-
return
|
|
247
|
+
return "E+";
|
|
248
248
|
if (/^exp$/i.test(safe))
|
|
249
|
-
return
|
|
249
|
+
return "E";
|
|
250
250
|
return safe;
|
|
251
251
|
}
|
|
252
252
|
function compactQuotaPercentToken(label, percent) {
|
|
253
253
|
const rounded = percent !== undefined && Number.isFinite(percent)
|
|
254
254
|
? `${Math.round(percent)}`
|
|
255
|
-
:
|
|
256
|
-
const safe = sanitizeLine(label ||
|
|
255
|
+
: "";
|
|
256
|
+
const safe = sanitizeLine(label || "");
|
|
257
257
|
if (!safe)
|
|
258
|
-
return rounded ? `R${rounded}` :
|
|
258
|
+
return rounded ? `R${rounded}` : "";
|
|
259
259
|
if (/^sonnet\s+7d$/i.test(safe))
|
|
260
|
-
return rounded ? `S7d${rounded}` :
|
|
260
|
+
return rounded ? `S7d${rounded}` : "S7d";
|
|
261
261
|
if (/^opus\s+7d$/i.test(safe))
|
|
262
|
-
return rounded ? `O7d${rounded}` :
|
|
262
|
+
return rounded ? `O7d${rounded}` : "O7d";
|
|
263
263
|
if (/^oauth\s+apps\s+7d$/i.test(safe)) {
|
|
264
|
-
return rounded ? `OA7d${rounded}` :
|
|
264
|
+
return rounded ? `OA7d${rounded}` : "OA7d";
|
|
265
265
|
}
|
|
266
266
|
if (/^cowork\s+7d$/i.test(safe))
|
|
267
|
-
return rounded ? `Co7d${rounded}` :
|
|
267
|
+
return rounded ? `Co7d${rounded}` : "Co7d";
|
|
268
268
|
if (/^spark\s+5h$/i.test(safe))
|
|
269
|
-
return rounded ? `Sk5h${rounded}` :
|
|
269
|
+
return rounded ? `Sk5h${rounded}` : "Sk5h";
|
|
270
270
|
if (/^spark\s+weekly$/i.test(safe))
|
|
271
|
-
return rounded ? `SkW${rounded}` :
|
|
272
|
-
const token = compactWindowToken(safe).replace(/\s+/g,
|
|
271
|
+
return rounded ? `SkW${rounded}` : "SkW";
|
|
272
|
+
const token = compactWindowToken(safe).replace(/\s+/g, "");
|
|
273
273
|
if (!rounded)
|
|
274
274
|
return token;
|
|
275
275
|
if (/^(?:D|W|M|\d+[hdw])$/i.test(token))
|
|
@@ -281,24 +281,24 @@ function compactQuotaWindowText(win) {
|
|
|
281
281
|
const resetToken = reset
|
|
282
282
|
? `${compactQuotaResetToken(win.resetLabel)}${reset}`
|
|
283
283
|
: undefined;
|
|
284
|
-
const note = sanitizeLine(win.note ||
|
|
284
|
+
const note = sanitizeLine(win.note || "");
|
|
285
285
|
if (win.showPercent === false) {
|
|
286
|
-
const safe = sanitizeLine(win.label ||
|
|
287
|
-
const daily = safe ? safe.replace(/^Daily\s+/i,
|
|
288
|
-
return [daily, resetToken, note].filter(Boolean).join(
|
|
286
|
+
const safe = sanitizeLine(win.label || "");
|
|
287
|
+
const daily = safe ? safe.replace(/^Daily\s+/i, "D") : "";
|
|
288
|
+
return [daily, resetToken, note].filter(Boolean).join(" ");
|
|
289
289
|
}
|
|
290
290
|
const percentToken = compactQuotaPercentToken(win.label, win.remainingPercent);
|
|
291
|
-
return [percentToken, resetToken, note].filter(Boolean).join(
|
|
291
|
+
return [percentToken, resetToken, note].filter(Boolean).join(" ");
|
|
292
292
|
}
|
|
293
293
|
function compactQuotaWindowTokens(win) {
|
|
294
294
|
const reset = compactReset(win.resetAt, win.resetLabel, win.label);
|
|
295
295
|
const resetToken = reset
|
|
296
296
|
? `${compactQuotaResetToken(win.resetLabel)}${reset}`
|
|
297
297
|
: undefined;
|
|
298
|
-
const note = sanitizeLine(win.note ||
|
|
298
|
+
const note = sanitizeLine(win.note || "");
|
|
299
299
|
if (win.showPercent === false) {
|
|
300
|
-
const safe = sanitizeLine(win.label ||
|
|
301
|
-
const daily = safe ? safe.replace(/^Daily\s+/i,
|
|
300
|
+
const safe = sanitizeLine(win.label || "");
|
|
301
|
+
const daily = safe ? safe.replace(/^Daily\s+/i, "D") : "";
|
|
302
302
|
return [daily, resetToken, note].filter((value) => Boolean(value));
|
|
303
303
|
}
|
|
304
304
|
const percentToken = compactQuotaPercentToken(win.label, win.remainingPercent);
|
|
@@ -307,7 +307,7 @@ function compactQuotaWindowTokens(win) {
|
|
|
307
307
|
function compactQuotaBalanceText(balance) {
|
|
308
308
|
return `B${compactDesktopCurrencyValue(balance.amount, balance.currency)}`;
|
|
309
309
|
}
|
|
310
|
-
function packInlineTokens(label, tokens, width, indent =
|
|
310
|
+
function packInlineTokens(label, tokens, width, indent = " ") {
|
|
311
311
|
if (tokens.length === 0)
|
|
312
312
|
return [label];
|
|
313
313
|
const lines = [];
|
|
@@ -326,20 +326,20 @@ function packInlineTokens(label, tokens, width, indent = ' ') {
|
|
|
326
326
|
}
|
|
327
327
|
function compactDesktopCurrencyValue(value, currency) {
|
|
328
328
|
const rendered = formatCurrency(value, currency);
|
|
329
|
-
if (currency ===
|
|
330
|
-
return rendered.replace(/^\$/,
|
|
329
|
+
if (currency === "$")
|
|
330
|
+
return rendered.replace(/^\$/, "");
|
|
331
331
|
return rendered;
|
|
332
332
|
}
|
|
333
333
|
function compactQuotaStaleToken(quota) {
|
|
334
|
-
return quota.stale ?
|
|
334
|
+
return quota.stale ? "St" : undefined;
|
|
335
335
|
}
|
|
336
336
|
function verboseQuotaStaleText(quota) {
|
|
337
|
-
return quota.stale ?
|
|
337
|
+
return quota.stale ? "stale" : undefined;
|
|
338
338
|
}
|
|
339
339
|
function compactDesktopQuotaSegment(quota) {
|
|
340
340
|
const label = compactProviderLabel(quota);
|
|
341
|
-
if (quota.status !==
|
|
342
|
-
if (quota.status ===
|
|
341
|
+
if (quota.status !== "ok") {
|
|
342
|
+
if (quota.status === "error")
|
|
343
343
|
return `${label} ?`;
|
|
344
344
|
return `${label} ${sanitizeLine(quota.status)}`;
|
|
345
345
|
}
|
|
@@ -365,11 +365,11 @@ function compactDesktopQuotaSegment(quota) {
|
|
|
365
365
|
const staleToken = compactQuotaStaleToken(quota);
|
|
366
366
|
if (staleToken)
|
|
367
367
|
parts.push(staleToken);
|
|
368
|
-
return [label, ...parts].filter(Boolean).join(
|
|
368
|
+
return [label, ...parts].filter(Boolean).join(" ");
|
|
369
369
|
}
|
|
370
370
|
function renderDesktopCompactTitle(baseTitle, usage, quotas, config, _width) {
|
|
371
371
|
const visibleQuotas = config.sidebar.showQuota
|
|
372
|
-
? collapseQuotaSnapshots(quotas).filter((q) => [
|
|
372
|
+
? collapseQuotaSnapshots(quotas).filter((q) => ["ok", "error", "unsupported", "unavailable"].includes(q.status))
|
|
373
373
|
: [];
|
|
374
374
|
const selectedProviderIDs = new Set(selectDesktopCompactProviderIDs(usage, config));
|
|
375
375
|
const quotaSegments = visibleQuotas
|
|
@@ -382,11 +382,11 @@ function renderDesktopCompactTitle(baseTitle, usage, quotas, config, _width) {
|
|
|
382
382
|
usageSegments.push(`Cd${formatPercent(cacheMetrics.cachedRatio, 0)}`);
|
|
383
383
|
}
|
|
384
384
|
if (config.sidebar.showCost && usage.apiCost > 0) {
|
|
385
|
-
usageSegments.push(`
|
|
385
|
+
usageSegments.push(`API${formatApiCostValue(usage.apiCost)}`);
|
|
386
386
|
}
|
|
387
387
|
const segments = [...quotaSegments, ...usageSegments];
|
|
388
|
-
const detail = segments.join(
|
|
389
|
-
const safeBase = sanitizeLine(baseTitle) ||
|
|
388
|
+
const detail = segments.join(" | ");
|
|
389
|
+
const safeBase = sanitizeLine(baseTitle) || "Session";
|
|
390
390
|
if (!detail)
|
|
391
391
|
return safeBase;
|
|
392
392
|
return `${safeBase} | ${detail}`;
|
|
@@ -394,7 +394,7 @@ function renderDesktopCompactTitle(baseTitle, usage, quotas, config, _width) {
|
|
|
394
394
|
function formatPercent(value, decimals = 1) {
|
|
395
395
|
const safe = Number.isFinite(value) && value >= 0 ? value : 0;
|
|
396
396
|
const pct = (safe * 100).toFixed(decimals);
|
|
397
|
-
return `${pct.replace(/\.0+$/,
|
|
397
|
+
return `${pct.replace(/\.0+$/, "").replace(/(\.\d*[1-9])0+$/, "$1")}%`;
|
|
398
398
|
}
|
|
399
399
|
function fitsLine(value, width) {
|
|
400
400
|
return stringCellWidth(sanitizeLine(value)) <= width;
|
|
@@ -402,7 +402,7 @@ function fitsLine(value, width) {
|
|
|
402
402
|
function usageDetailLines(usage, cacheMetrics, options) {
|
|
403
403
|
const width = options.width;
|
|
404
404
|
const numberToken = options.numberToken || sidebarNumber;
|
|
405
|
-
const costToken = options.costToken || ((value) => `
|
|
405
|
+
const costToken = options.costToken || ((value) => `API${formatApiCostValue(value)}`);
|
|
406
406
|
const groups = [];
|
|
407
407
|
groups.push([
|
|
408
408
|
`R${shortNumber(usage.assistantMessages, 1)}`,
|
|
@@ -437,7 +437,7 @@ function usageDetailLines(usage, cacheMetrics, options) {
|
|
|
437
437
|
}
|
|
438
438
|
if (cacheTokens.length > 0) {
|
|
439
439
|
if (coverageTokens.length > 0) {
|
|
440
|
-
const combined = [...coverageTokens, ...cacheTokens].join(
|
|
440
|
+
const combined = [...coverageTokens, ...cacheTokens].join(" ");
|
|
441
441
|
if (fitsLine(combined, width)) {
|
|
442
442
|
secondaryGroups.length = 0;
|
|
443
443
|
secondaryGroups.push([...coverageTokens, ...cacheTokens]);
|
|
@@ -456,7 +456,7 @@ function usageDetailLines(usage, cacheMetrics, options) {
|
|
|
456
456
|
}
|
|
457
457
|
const packed = [];
|
|
458
458
|
for (const group of groups) {
|
|
459
|
-
let current =
|
|
459
|
+
let current = "";
|
|
460
460
|
for (const token of group) {
|
|
461
461
|
const candidate = current ? `${current} ${token}` : token;
|
|
462
462
|
if (!current || fitsLine(candidate, width)) {
|
|
@@ -474,7 +474,7 @@ function usageDetailLines(usage, cacheMetrics, options) {
|
|
|
474
474
|
function packUsageGroups(groups, width) {
|
|
475
475
|
const packed = [];
|
|
476
476
|
for (const group of groups) {
|
|
477
|
-
let current =
|
|
477
|
+
let current = "";
|
|
478
478
|
for (const token of group) {
|
|
479
479
|
const candidate = current ? `${current} ${token}` : token;
|
|
480
480
|
if (!current || fitsLine(candidate, width)) {
|
|
@@ -518,7 +518,7 @@ function readableSidebarUsageLines(usage, cacheMetrics, width, showCost) {
|
|
|
518
518
|
}
|
|
519
519
|
const summaryTokens = [];
|
|
520
520
|
if (showCost && usage.apiCost > 0) {
|
|
521
|
-
summaryTokens.push(`
|
|
521
|
+
summaryTokens.push(`API ${formatApiCostValue(usage.apiCost)}`);
|
|
522
522
|
}
|
|
523
523
|
if (summaryTokens.length > 0)
|
|
524
524
|
groups.push(summaryTokens);
|
|
@@ -531,7 +531,7 @@ function readableSidebarUsageLines(usage, cacheMetrics, width, showCost) {
|
|
|
531
531
|
return packed;
|
|
532
532
|
}
|
|
533
533
|
function formatQuotaPercent(value, options) {
|
|
534
|
-
const missing = options?.missing ??
|
|
534
|
+
const missing = options?.missing ?? "-";
|
|
535
535
|
if (value === undefined)
|
|
536
536
|
return missing;
|
|
537
537
|
if (!Number.isFinite(value) || value < 0)
|
|
@@ -540,17 +540,17 @@ function formatQuotaPercent(value, options) {
|
|
|
540
540
|
return `${Math.round(value)}%`;
|
|
541
541
|
return `${value.toFixed(options?.decimals ?? 1)}%`;
|
|
542
542
|
}
|
|
543
|
-
function alignPairs(pairs, indent =
|
|
543
|
+
function alignPairs(pairs, indent = " ") {
|
|
544
544
|
if (pairs.length === 0)
|
|
545
545
|
return [];
|
|
546
546
|
const safePairs = pairs.map((pair) => ({
|
|
547
|
-
label: sanitizeLine(pair.label ||
|
|
548
|
-
value: sanitizeLine(pair.value ||
|
|
547
|
+
label: sanitizeLine(pair.label || ""),
|
|
548
|
+
value: sanitizeLine(pair.value || ""),
|
|
549
549
|
}));
|
|
550
550
|
const labelWidth = Math.max(...safePairs.map((pair) => stringCellWidth(pair.label)), 0);
|
|
551
551
|
return safePairs.map((pair) => {
|
|
552
552
|
if (!pair.label) {
|
|
553
|
-
return `${indent}${
|
|
553
|
+
return `${indent}${" ".repeat(labelWidth)} ${pair.value}`;
|
|
554
554
|
}
|
|
555
555
|
return `${indent}${padEndCells(pair.label, labelWidth)} ${pair.value}`;
|
|
556
556
|
});
|
|
@@ -558,37 +558,37 @@ function alignPairs(pairs, indent = ' ') {
|
|
|
558
558
|
function compactQuotaInline(quota) {
|
|
559
559
|
const label = sanitizeLine(quotaDisplayLabel(quota));
|
|
560
560
|
const staleToken = compactQuotaStaleToken(quota);
|
|
561
|
-
if (quota.status !==
|
|
562
|
-
if (quota.status ===
|
|
561
|
+
if (quota.status !== "ok") {
|
|
562
|
+
if (quota.status === "error")
|
|
563
563
|
return `${label} Remaining ?`;
|
|
564
564
|
return `${label} ${sanitizeLine(quota.status)}`;
|
|
565
565
|
}
|
|
566
566
|
if (quota.windows && quota.windows.length > 0) {
|
|
567
567
|
const first = quota.windows[0];
|
|
568
568
|
const showPercent = first.showPercent !== false;
|
|
569
|
-
const firstLabel = sanitizeLine(first.label ||
|
|
569
|
+
const firstLabel = sanitizeLine(first.label || "");
|
|
570
570
|
const pct = formatQuotaPercent(first.remainingPercent, {
|
|
571
571
|
rounded: true,
|
|
572
|
-
missing:
|
|
572
|
+
missing: "",
|
|
573
573
|
});
|
|
574
574
|
const summary = showPercent
|
|
575
|
-
? [firstLabel, pct].filter(Boolean).join(
|
|
576
|
-
: firstLabel.replace(/^Daily\s+/i,
|
|
575
|
+
? [firstLabel, pct].filter(Boolean).join(" ")
|
|
576
|
+
: firstLabel.replace(/^Daily\s+/i, "") || firstLabel;
|
|
577
577
|
const hasMore = quota.windows.length > 1 ||
|
|
578
|
-
(quota.balance !== undefined && !summary.includes(
|
|
579
|
-
return `${label}${summary ? ` ${summary}` :
|
|
578
|
+
(quota.balance !== undefined && !summary.includes("Balance "));
|
|
579
|
+
return `${label}${summary ? ` ${summary}` : ""}${hasMore ? "+" : ""}${staleToken ? ` ${staleToken}` : ""}`;
|
|
580
580
|
}
|
|
581
581
|
if (quota.balance) {
|
|
582
|
-
return `${label} Balance ${formatCurrency(quota.balance.amount, quota.balance.currency)}${staleToken ? ` ${staleToken}` :
|
|
582
|
+
return `${label} Balance ${formatCurrency(quota.balance.amount, quota.balance.currency)}${staleToken ? ` ${staleToken}` : ""}`;
|
|
583
583
|
}
|
|
584
584
|
const singlePercent = formatQuotaPercent(quota.remainingPercent, {
|
|
585
585
|
rounded: true,
|
|
586
|
-
missing:
|
|
586
|
+
missing: "",
|
|
587
587
|
});
|
|
588
588
|
if (singlePercent) {
|
|
589
|
-
return `${label} ${singlePercent}${staleToken ? ` ${staleToken}` :
|
|
589
|
+
return `${label} ${singlePercent}${staleToken ? ` ${staleToken}` : ""}`;
|
|
590
590
|
}
|
|
591
|
-
return `${label}${staleToken ? ` ${staleToken}` :
|
|
591
|
+
return `${label}${staleToken ? ` ${staleToken}` : ""}`;
|
|
592
592
|
}
|
|
593
593
|
function renderSingleLineTitle(baseTitle, usage, quotas, config, width) {
|
|
594
594
|
const baseBudget = Math.min(16, Math.max(8, Math.floor(width * 0.35)));
|
|
@@ -611,10 +611,10 @@ function renderSingleLineTitle(baseTitle, usage, quotas, config, width) {
|
|
|
611
611
|
segments.push(formatApiCostLine(usage.apiCost));
|
|
612
612
|
}
|
|
613
613
|
if (config.sidebar.showQuota) {
|
|
614
|
-
const visibleQuotas = collapseQuotaSnapshots(quotas).filter((q) => [
|
|
614
|
+
const visibleQuotas = collapseQuotaSnapshots(quotas).filter((q) => ["ok", "error", "unsupported", "unavailable"].includes(q.status));
|
|
615
615
|
segments.push(...visibleQuotas.map(compactQuotaInline));
|
|
616
616
|
}
|
|
617
|
-
const detail = segments.filter(Boolean).join(
|
|
617
|
+
const detail = segments.filter(Boolean).join(" | ");
|
|
618
618
|
if (!detail)
|
|
619
619
|
return fitLine(baseTitle, width);
|
|
620
620
|
return fitLine(`${base} | ${detail}`, width);
|
|
@@ -627,23 +627,23 @@ function renderSingleLineTitle(baseTitle, usage, quotas, config, width) {
|
|
|
627
627
|
* Input 18.9k Output 53
|
|
628
628
|
* Cache Read 1.5k (only if read > 0)
|
|
629
629
|
* Cache Write 200 (only if write > 0)
|
|
630
|
-
* $3.81
|
|
630
|
+
* API $3.81 (only if showCost=true)
|
|
631
631
|
* OpenAI Remaining 78% (only if quota available)
|
|
632
632
|
*/
|
|
633
633
|
export function renderSidebarTitle(baseTitle, usage, quotas, config, view) {
|
|
634
634
|
const width = Math.max(8, Math.floor(config.sidebar.width || 36));
|
|
635
|
-
const safeBaseTitle = stripAnsi(baseTitle ||
|
|
635
|
+
const safeBaseTitle = stripAnsi(baseTitle || "Session") || "Session";
|
|
636
636
|
const mode = view || resolveTitleView({ config });
|
|
637
|
-
if (mode ===
|
|
638
|
-
const singleLineBase = safeBaseTitle.split(/\r?\n/, 1)[0] ||
|
|
637
|
+
if (mode === "compact") {
|
|
638
|
+
const singleLineBase = safeBaseTitle.split(/\r?\n/, 1)[0] || "Session";
|
|
639
639
|
return renderDesktopCompactTitle(singleLineBase, usage, quotas, config, width);
|
|
640
640
|
}
|
|
641
641
|
const cacheMetrics = getCacheCoverageMetrics(usage);
|
|
642
642
|
const lines = [];
|
|
643
643
|
for (const line of safeBaseTitle.split(/\r?\n/)) {
|
|
644
|
-
lines.push(fitLine(line ||
|
|
644
|
+
lines.push(fitLine(line || "Session", width));
|
|
645
645
|
}
|
|
646
|
-
lines.push(
|
|
646
|
+
lines.push("");
|
|
647
647
|
for (const detailLine of usageDetailLines(usage, cacheMetrics, {
|
|
648
648
|
width,
|
|
649
649
|
showCost: config.sidebar.showCost,
|
|
@@ -652,7 +652,7 @@ export function renderSidebarTitle(baseTitle, usage, quotas, config, view) {
|
|
|
652
652
|
}
|
|
653
653
|
// Quota lines (one provider per line for stable wrapping)
|
|
654
654
|
if (config.sidebar.showQuota) {
|
|
655
|
-
const visibleQuotas = collapseQuotaSnapshots(quotas).filter((q) => [
|
|
655
|
+
const visibleQuotas = collapseQuotaSnapshots(quotas).filter((q) => ["ok", "error", "unsupported", "unavailable"].includes(q.status));
|
|
656
656
|
const compactQuotaDetails = true;
|
|
657
657
|
const forceWrappedProviders = false;
|
|
658
658
|
const labelWidth = visibleQuotas.reduce((max, item) => {
|
|
@@ -670,20 +670,20 @@ export function renderSidebarTitle(baseTitle, usage, quotas, config, view) {
|
|
|
670
670
|
}))
|
|
671
671
|
.filter((s) => Boolean(s));
|
|
672
672
|
if (quotaItems.length > 0) {
|
|
673
|
-
lines.push(
|
|
673
|
+
lines.push("");
|
|
674
674
|
}
|
|
675
675
|
for (const line of quotaItems) {
|
|
676
676
|
lines.push(fitLine(line, width));
|
|
677
677
|
}
|
|
678
678
|
}
|
|
679
|
-
return lines.join(
|
|
679
|
+
return lines.join("\n");
|
|
680
680
|
}
|
|
681
681
|
export function renderSidebarContextLine(tokens, percent, width) {
|
|
682
682
|
const parts = [`${panelNumber(tokens)} tok`];
|
|
683
683
|
if (percent !== undefined && Number.isFinite(percent) && percent >= 0) {
|
|
684
684
|
parts.push(`${Math.round(percent)}% ctx`);
|
|
685
685
|
}
|
|
686
|
-
return fitLine(parts.join(
|
|
686
|
+
return fitLine(parts.join(" "), width);
|
|
687
687
|
}
|
|
688
688
|
export function renderSidebarUsageLines(usage, config, options) {
|
|
689
689
|
const width = Math.max(8, Math.floor(config.sidebar.width || 36));
|
|
@@ -696,7 +696,7 @@ export function renderSidebarUsageLines(usage, config, options) {
|
|
|
696
696
|
width,
|
|
697
697
|
showCost,
|
|
698
698
|
numberToken: panelNumber,
|
|
699
|
-
costToken: (value) => `
|
|
699
|
+
costToken: (value) => `API ${formatApiCostValue(value)}`,
|
|
700
700
|
cacheReadFirst: true,
|
|
701
701
|
}).map((line) => fitLine(line, width));
|
|
702
702
|
}
|
|
@@ -705,7 +705,7 @@ export function renderSidebarQuotaLines(quotas, config) {
|
|
|
705
705
|
}
|
|
706
706
|
export function renderSidebarQuotaLineGroups(quotas, config) {
|
|
707
707
|
const width = Math.max(8, Math.floor(config.sidebar.width || 36));
|
|
708
|
-
const visibleQuotas = collapseQuotaSnapshots(quotas).filter((q) => [
|
|
708
|
+
const visibleQuotas = collapseQuotaSnapshots(quotas).filter((q) => ["ok", "error", "unsupported", "unavailable"].includes(q.status));
|
|
709
709
|
const labelWidth = visibleQuotas.reduce((max, item) => {
|
|
710
710
|
const label = compactProviderLabel(item);
|
|
711
711
|
return Math.max(max, stringCellWidth(label));
|
|
@@ -746,7 +746,7 @@ function compactQuotaWide(quota, labelWidth = 0, options) {
|
|
|
746
746
|
? compactProviderLabel(quota)
|
|
747
747
|
: sanitizeLine(quotaDisplayLabel(quota));
|
|
748
748
|
const labelPadded = padEndCells(label, labelWidth);
|
|
749
|
-
const detailIndent =
|
|
749
|
+
const detailIndent = " ";
|
|
750
750
|
const withLabel = (content) => `${labelPadded} ${content}`;
|
|
751
751
|
const wrap = options?.wrapLines === true && (options?.width || 0) > 0;
|
|
752
752
|
const width = options?.width || 0;
|
|
@@ -760,13 +760,13 @@ function compactQuotaWide(quota, labelWidth = 0, options) {
|
|
|
760
760
|
return [inline];
|
|
761
761
|
return [label, ...detailLines.map((d) => `${detailIndent}${d}`)];
|
|
762
762
|
};
|
|
763
|
-
if (quota.status ===
|
|
764
|
-
return maybeBreak(
|
|
765
|
-
if (quota.status ===
|
|
766
|
-
return maybeBreak(
|
|
767
|
-
if (quota.status ===
|
|
768
|
-
return maybeBreak(
|
|
769
|
-
if (quota.status !==
|
|
763
|
+
if (quota.status === "error")
|
|
764
|
+
return maybeBreak("Remaining ?", ["Remaining ?"]);
|
|
765
|
+
if (quota.status === "unsupported")
|
|
766
|
+
return maybeBreak("unsupported", ["unsupported"]);
|
|
767
|
+
if (quota.status === "unavailable")
|
|
768
|
+
return maybeBreak("unavailable", ["unavailable"]);
|
|
769
|
+
if (quota.status !== "ok")
|
|
770
770
|
return [];
|
|
771
771
|
const balanceText = quota.balance
|
|
772
772
|
? compactDetails
|
|
@@ -785,11 +785,11 @@ function compactQuotaWide(quota, labelWidth = 0, options) {
|
|
|
785
785
|
: [pct];
|
|
786
786
|
const reset = compactReset(win.resetAt, win.resetLabel, win.label);
|
|
787
787
|
if (reset) {
|
|
788
|
-
parts.push(`${sanitizeLine(win.resetLabel ||
|
|
788
|
+
parts.push(`${sanitizeLine(win.resetLabel || "Rst")} ${reset}`);
|
|
789
789
|
}
|
|
790
790
|
if (win.note)
|
|
791
791
|
parts.push(sanitizeLine(win.note));
|
|
792
|
-
return parts.join(
|
|
792
|
+
return parts.join(" ");
|
|
793
793
|
};
|
|
794
794
|
// Multi-window rendering
|
|
795
795
|
if (quota.windows && quota.windows.length > 0) {
|
|
@@ -799,7 +799,7 @@ function compactQuotaWide(quota, labelWidth = 0, options) {
|
|
|
799
799
|
: [];
|
|
800
800
|
// Build the detail lines (window texts + optional balance)
|
|
801
801
|
const details = [...parts];
|
|
802
|
-
if (balanceText && !parts.some((p) => p.includes(
|
|
802
|
+
if (balanceText && !parts.some((p) => p.includes("Balance "))) {
|
|
803
803
|
details.push(balanceText);
|
|
804
804
|
}
|
|
805
805
|
if (compactDetails) {
|
|
@@ -809,7 +809,7 @@ function compactQuotaWide(quota, labelWidth = 0, options) {
|
|
|
809
809
|
const staleToken = compactQuotaStaleToken(quota);
|
|
810
810
|
if (staleToken)
|
|
811
811
|
tokens.push(staleToken);
|
|
812
|
-
return packInlineTokens(label, tokens, width,
|
|
812
|
+
return packInlineTokens(label, tokens, width, " ".repeat(stringCellWidth(label) + 1));
|
|
813
813
|
}
|
|
814
814
|
// Keep a unified wrapped layout for providers that have multiple detail
|
|
815
815
|
// lines so OpenAI/Copilot/others match the RightCode multi-line style,
|
|
@@ -828,23 +828,23 @@ function compactQuotaWide(quota, labelWidth = 0, options) {
|
|
|
828
828
|
}
|
|
829
829
|
// Fallback: single value from top-level remainingPercent
|
|
830
830
|
const percent = formatQuotaPercent(quota.remainingPercent, { rounded: true });
|
|
831
|
-
const reset = compactReset(quota.resetAt,
|
|
831
|
+
const reset = compactReset(quota.resetAt, "Rst");
|
|
832
832
|
const fallbackText = compactDetails
|
|
833
833
|
? [
|
|
834
|
-
`R${percent.replace(/%$/,
|
|
834
|
+
`R${percent.replace(/%$/, "")}`,
|
|
835
835
|
reset ? `R${reset}` : undefined,
|
|
836
836
|
compactQuotaStaleToken(quota),
|
|
837
837
|
]
|
|
838
838
|
.filter(Boolean)
|
|
839
|
-
.join(
|
|
840
|
-
: `Remaining ${percent}${reset ? ` Rst ${reset}` :
|
|
839
|
+
.join(" ")
|
|
840
|
+
: `Remaining ${percent}${reset ? ` Rst ${reset}` : ""}${verboseQuotaStaleText(quota) ? ` ${verboseQuotaStaleText(quota)}` : ""}`;
|
|
841
841
|
return maybeBreak(fallbackText, [fallbackText]);
|
|
842
842
|
}
|
|
843
843
|
function compactCountdown(remainingMs) {
|
|
844
844
|
if (!Number.isFinite(remainingMs))
|
|
845
845
|
return undefined;
|
|
846
846
|
if (remainingMs <= 0)
|
|
847
|
-
return
|
|
847
|
+
return "0m";
|
|
848
848
|
const minuteMs = 60_000;
|
|
849
849
|
const hourMinutes = 60;
|
|
850
850
|
const dayMinutes = 24 * hourMinutes;
|
|
@@ -855,11 +855,11 @@ function compactCountdown(remainingMs) {
|
|
|
855
855
|
if (totalMinutes < dayMinutes) {
|
|
856
856
|
const hours = Math.floor(totalMinutes / hourMinutes);
|
|
857
857
|
const minutes = totalMinutes % hourMinutes;
|
|
858
|
-
return `${hours}h${`${minutes}`.padStart(2,
|
|
858
|
+
return `${hours}h${`${minutes}`.padStart(2, "0")}m`;
|
|
859
859
|
}
|
|
860
860
|
const days = Math.floor(totalMinutes / dayMinutes);
|
|
861
861
|
const hours = Math.floor((totalMinutes % dayMinutes) / hourMinutes);
|
|
862
|
-
return `${days}D${`${hours}`.padStart(2,
|
|
862
|
+
return `${days}D${`${hours}`.padStart(2, "0")}h`;
|
|
863
863
|
}
|
|
864
864
|
function compactReset(iso, resetLabel, windowLabel) {
|
|
865
865
|
void resetLabel;
|
|
@@ -873,7 +873,7 @@ function compactReset(iso, resetLabel, windowLabel) {
|
|
|
873
873
|
}
|
|
874
874
|
function dateLine(iso) {
|
|
875
875
|
if (!iso)
|
|
876
|
-
return
|
|
876
|
+
return "-";
|
|
877
877
|
const time = Date.parse(iso);
|
|
878
878
|
if (Number.isNaN(time))
|
|
879
879
|
return iso;
|
|
@@ -896,7 +896,7 @@ function expiryAlertLine(iso, nowMs = Date.now()) {
|
|
|
896
896
|
}
|
|
897
897
|
function quotaExpiryPairs(quotas, nowMs = Date.now()) {
|
|
898
898
|
return collapseQuotaSnapshots(quotas)
|
|
899
|
-
.filter((item) => item.status ===
|
|
899
|
+
.filter((item) => item.status === "ok")
|
|
900
900
|
.map((item) => ({
|
|
901
901
|
label: quotaDisplayLabel(item),
|
|
902
902
|
value: expiryAlertLine(item.expiresAt, nowMs),
|
|
@@ -904,7 +904,7 @@ function quotaExpiryPairs(quotas, nowMs = Date.now()) {
|
|
|
904
904
|
.filter((item) => Boolean(item.value));
|
|
905
905
|
}
|
|
906
906
|
function toolVisibleQuotaSnapshots(quotas) {
|
|
907
|
-
return collapseQuotaSnapshots(quotas).filter((item) => item.status ===
|
|
907
|
+
return collapseQuotaSnapshots(quotas).filter((item) => item.status === "ok" || item.status === "error");
|
|
908
908
|
}
|
|
909
909
|
function reportResetLine(iso, resetLabel, windowLabel) {
|
|
910
910
|
const compact = compactReset(iso, resetLabel, windowLabel);
|
|
@@ -913,47 +913,47 @@ function reportResetLine(iso, resetLabel, windowLabel) {
|
|
|
913
913
|
return dateLine(iso);
|
|
914
914
|
}
|
|
915
915
|
function periodLabel(period) {
|
|
916
|
-
if (period ===
|
|
917
|
-
return
|
|
918
|
-
if (period ===
|
|
919
|
-
return
|
|
920
|
-
if (period ===
|
|
921
|
-
return
|
|
922
|
-
return
|
|
916
|
+
if (period === "day")
|
|
917
|
+
return "Today";
|
|
918
|
+
if (period === "week")
|
|
919
|
+
return "This Week";
|
|
920
|
+
if (period === "month")
|
|
921
|
+
return "This Month";
|
|
922
|
+
return "Current Session";
|
|
923
923
|
}
|
|
924
924
|
function historyPeriodLabel(period) {
|
|
925
|
-
if (period ===
|
|
926
|
-
return
|
|
927
|
-
if (period ===
|
|
928
|
-
return
|
|
929
|
-
if (period ===
|
|
930
|
-
return
|
|
931
|
-
return
|
|
925
|
+
if (period === "day")
|
|
926
|
+
return "Daily";
|
|
927
|
+
if (period === "week")
|
|
928
|
+
return "Weekly";
|
|
929
|
+
if (period === "month")
|
|
930
|
+
return "Monthly";
|
|
931
|
+
return "Session";
|
|
932
932
|
}
|
|
933
933
|
function historyProviderLabel(providerID) {
|
|
934
934
|
return quotaDisplayLabel({
|
|
935
935
|
providerID,
|
|
936
936
|
label: providerID,
|
|
937
|
-
status:
|
|
937
|
+
status: "ok",
|
|
938
938
|
checkedAt: 0,
|
|
939
939
|
});
|
|
940
940
|
}
|
|
941
941
|
function historyMdCell(value) {
|
|
942
|
-
return sanitizeLine(value).replace(/\|/g,
|
|
942
|
+
return sanitizeLine(value).replace(/\|/g, "\\|");
|
|
943
943
|
}
|
|
944
944
|
function formatDelta(current, previous) {
|
|
945
945
|
if (previous === undefined)
|
|
946
|
-
return
|
|
946
|
+
return "n/a";
|
|
947
947
|
if (!Number.isFinite(previous) || previous < 0)
|
|
948
|
-
return
|
|
948
|
+
return "n/a";
|
|
949
949
|
if (previous === 0)
|
|
950
|
-
return current === 0 ?
|
|
950
|
+
return current === 0 ? "flat" : "new";
|
|
951
951
|
const delta = ((current - previous) / previous) * 100;
|
|
952
952
|
if (!Number.isFinite(delta))
|
|
953
|
-
return
|
|
953
|
+
return "n/a";
|
|
954
954
|
const abs = Math.abs(delta);
|
|
955
|
-
const rounded = (abs >= 10 ? delta.toFixed(0) : delta.toFixed(1)).replace(/\.0$/,
|
|
956
|
-
return `${delta > 0 ?
|
|
955
|
+
const rounded = (abs >= 10 ? delta.toFixed(0) : delta.toFixed(1)).replace(/\.0$/, "");
|
|
956
|
+
return `${delta > 0 ? "+" : ""}${rounded}%`;
|
|
957
957
|
}
|
|
958
958
|
function currentHistoryRow(result) {
|
|
959
959
|
return ([...result.rows].reverse().find((row) => row.range.isCurrent) ||
|
|
@@ -991,46 +991,46 @@ function renderHistoryTotalsTable(result, options) {
|
|
|
991
991
|
: undefined;
|
|
992
992
|
const metricRows = [
|
|
993
993
|
{
|
|
994
|
-
label:
|
|
994
|
+
label: "Requests",
|
|
995
995
|
total: shortNumber(result.total.assistantMessages),
|
|
996
996
|
average: rows.length
|
|
997
997
|
? shortNumber(result.total.assistantMessages / rows.length)
|
|
998
|
-
:
|
|
998
|
+
: "-",
|
|
999
999
|
},
|
|
1000
1000
|
{
|
|
1001
|
-
label:
|
|
1001
|
+
label: "Total Tokens",
|
|
1002
1002
|
total: shortNumber(result.total.total),
|
|
1003
1003
|
average: rows.length
|
|
1004
1004
|
? shortNumber(result.total.total / rows.length)
|
|
1005
|
-
:
|
|
1005
|
+
: "-",
|
|
1006
1006
|
},
|
|
1007
1007
|
{
|
|
1008
|
-
label:
|
|
1009
|
-
total: cacheTotal !== undefined ? formatPercent(cacheTotal, 1) :
|
|
1010
|
-
average: cacheAverage !== undefined ? formatPercent(cacheAverage, 1) :
|
|
1008
|
+
label: "Cache Hit",
|
|
1009
|
+
total: cacheTotal !== undefined ? formatPercent(cacheTotal, 1) : "-",
|
|
1010
|
+
average: cacheAverage !== undefined ? formatPercent(cacheAverage, 1) : "-",
|
|
1011
1011
|
},
|
|
1012
1012
|
...(options?.showCost !== false
|
|
1013
1013
|
? [
|
|
1014
1014
|
{
|
|
1015
|
-
label:
|
|
1015
|
+
label: "API Cost",
|
|
1016
1016
|
total: formatApiCostValue(result.total.apiCost),
|
|
1017
1017
|
average: rows.length
|
|
1018
1018
|
? formatApiCostValue(result.total.apiCost / rows.length)
|
|
1019
|
-
:
|
|
1019
|
+
: "-",
|
|
1020
1020
|
},
|
|
1021
1021
|
]
|
|
1022
1022
|
: []),
|
|
1023
1023
|
];
|
|
1024
1024
|
return [
|
|
1025
|
-
|
|
1026
|
-
|
|
1025
|
+
"| Metric | Total | Avg/Period |",
|
|
1026
|
+
"| --- | ---: | ---: |",
|
|
1027
1027
|
...metricRows.map((metric) => `| ${metric.label} | ${metric.total} | ${metric.average} |`),
|
|
1028
1028
|
];
|
|
1029
1029
|
}
|
|
1030
1030
|
function renderHistoryProviderBreakdown(result, options) {
|
|
1031
1031
|
const providers = Object.values(result.total.providers);
|
|
1032
1032
|
if (providers.length === 0)
|
|
1033
|
-
return [
|
|
1033
|
+
return ["- no provider activity in selected range"];
|
|
1034
1034
|
const sorted = [...providers].sort((a, b) => {
|
|
1035
1035
|
if (b.total !== a.total)
|
|
1036
1036
|
return b.total - a.total;
|
|
@@ -1038,16 +1038,16 @@ function renderHistoryProviderBreakdown(result, options) {
|
|
|
1038
1038
|
});
|
|
1039
1039
|
return [
|
|
1040
1040
|
options?.showCost !== false
|
|
1041
|
-
?
|
|
1042
|
-
:
|
|
1041
|
+
? "| Provider | Req | Input | Output | Total | Share | Cache Hit | API Cost |"
|
|
1042
|
+
: "| Provider | Req | Input | Output | Total | Share | Cache Hit |",
|
|
1043
1043
|
options?.showCost !== false
|
|
1044
|
-
?
|
|
1045
|
-
:
|
|
1044
|
+
? "| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |"
|
|
1045
|
+
: "| --- | ---: | ---: | ---: | ---: | ---: | ---: |",
|
|
1046
1046
|
...sorted.map((provider) => {
|
|
1047
1047
|
const cache = getProviderCacheCoverageMetrics(provider).cachedRatio;
|
|
1048
1048
|
const share = result.total.total > 0
|
|
1049
1049
|
? formatPercent(provider.total / result.total.total, 1)
|
|
1050
|
-
:
|
|
1050
|
+
: "-";
|
|
1051
1051
|
const cells = [
|
|
1052
1052
|
historyMdCell(historyProviderLabel(provider.providerID)),
|
|
1053
1053
|
shortNumber(provider.assistantMessages),
|
|
@@ -1055,23 +1055,23 @@ function renderHistoryProviderBreakdown(result, options) {
|
|
|
1055
1055
|
shortNumber(provider.output),
|
|
1056
1056
|
shortNumber(provider.total),
|
|
1057
1057
|
share,
|
|
1058
|
-
cache !== undefined ? formatPercent(cache, 1) :
|
|
1058
|
+
cache !== undefined ? formatPercent(cache, 1) : "-",
|
|
1059
1059
|
];
|
|
1060
1060
|
if (options?.showCost !== false) {
|
|
1061
|
-
cells.push(provider.apiCost > 0 ? formatApiCostValue(provider.apiCost) :
|
|
1061
|
+
cells.push(provider.apiCost > 0 ? formatApiCostValue(provider.apiCost) : "-");
|
|
1062
1062
|
}
|
|
1063
|
-
return `| ${cells.join(
|
|
1063
|
+
return `| ${cells.join(" | ")} |`;
|
|
1064
1064
|
}),
|
|
1065
1065
|
];
|
|
1066
1066
|
}
|
|
1067
1067
|
function renderHistoryQuotaSnapshot(quotas) {
|
|
1068
1068
|
const visible = toolVisibleQuotaSnapshots(quotas).slice(0, 5);
|
|
1069
1069
|
if (visible.length === 0)
|
|
1070
|
-
return [
|
|
1070
|
+
return ["- no provider quota data available"];
|
|
1071
1071
|
return visible.map((quota) => {
|
|
1072
1072
|
const label = quotaDisplayLabel(quota);
|
|
1073
|
-
if (quota.status ===
|
|
1074
|
-
return `- ${label}: error${quota.note ? ` | ${quota.note}` :
|
|
1073
|
+
if (quota.status === "error") {
|
|
1074
|
+
return `- ${label}: error${quota.note ? ` | ${quota.note}` : ""}`;
|
|
1075
1075
|
}
|
|
1076
1076
|
if (quota.windows && quota.windows.length > 0) {
|
|
1077
1077
|
const summary = quota.windows
|
|
@@ -1081,11 +1081,11 @@ function renderHistoryQuotaSnapshot(quotas) {
|
|
|
1081
1081
|
? undefined
|
|
1082
1082
|
: formatQuotaPercent(window.remainingPercent);
|
|
1083
1083
|
const reset = reportResetLine(window.resetAt, window.resetLabel, window.label);
|
|
1084
|
-
return [window.label ||
|
|
1084
|
+
return [window.label || "Quota", remaining, `reset ${reset}`]
|
|
1085
1085
|
.filter(Boolean)
|
|
1086
|
-
.join(
|
|
1086
|
+
.join(" | ");
|
|
1087
1087
|
})
|
|
1088
|
-
.join(
|
|
1088
|
+
.join("; ");
|
|
1089
1089
|
return `- ${label}: ${summary}`;
|
|
1090
1090
|
}
|
|
1091
1091
|
if (quota.balance) {
|
|
@@ -1100,22 +1100,22 @@ function renderHistoryPeriodDetailRows(result, options) {
|
|
|
1100
1100
|
? result.rows.map((row) => {
|
|
1101
1101
|
const cache = getCacheCoverageMetrics(row.usage).cachedRatio;
|
|
1102
1102
|
const cells = [
|
|
1103
|
-
`${row.range.label}${row.range.isCurrent ?
|
|
1103
|
+
`${row.range.label}${row.range.isCurrent ? "*" : ""}`,
|
|
1104
1104
|
shortNumber(row.usage.assistantMessages),
|
|
1105
1105
|
shortNumber(row.usage.input),
|
|
1106
1106
|
shortNumber(row.usage.output),
|
|
1107
1107
|
shortNumber(row.usage.cacheRead + row.usage.cacheWrite),
|
|
1108
|
-
cache !== undefined ? formatPercent(cache, 1) :
|
|
1108
|
+
cache !== undefined ? formatPercent(cache, 1) : "-",
|
|
1109
1109
|
shortNumber(row.usage.total),
|
|
1110
1110
|
];
|
|
1111
1111
|
if (showCost)
|
|
1112
1112
|
cells.push(formatApiCostValue(row.usage.apiCost));
|
|
1113
|
-
return `| ${cells.join(
|
|
1113
|
+
return `| ${cells.join(" | ")} |`;
|
|
1114
1114
|
})
|
|
1115
1115
|
: [
|
|
1116
1116
|
showCost
|
|
1117
|
-
?
|
|
1118
|
-
:
|
|
1117
|
+
? "| - | - | - | - | - | - | - | - |"
|
|
1118
|
+
: "| - | - | - | - | - | - | - |",
|
|
1119
1119
|
];
|
|
1120
1120
|
}
|
|
1121
1121
|
export function renderHistoryMarkdownReport(result, quotas, options) {
|
|
@@ -1124,66 +1124,66 @@ export function renderHistoryMarkdownReport(result, quotas, options) {
|
|
|
1124
1124
|
return [
|
|
1125
1125
|
`## Quota History - ${historyPeriodLabel(result.period)} since ${result.since.raw}`,
|
|
1126
1126
|
...(result.warning
|
|
1127
|
-
? [
|
|
1127
|
+
? ["", `> Warning: ${sanitizeLine(result.warning)}`]
|
|
1128
1128
|
: []),
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1129
|
+
"",
|
|
1130
|
+
"### Quota Status",
|
|
1131
|
+
"",
|
|
1132
1132
|
...renderHistoryQuotaSnapshot(quotas),
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1133
|
+
"",
|
|
1134
|
+
"### Totals",
|
|
1135
|
+
"",
|
|
1136
1136
|
...renderHistoryTotalsTable(result, { showCost }),
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1137
|
+
"",
|
|
1138
|
+
"### Provider Breakdown",
|
|
1139
|
+
"",
|
|
1140
1140
|
...renderHistoryProviderBreakdown(result, { showCost }),
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1141
|
+
"",
|
|
1142
|
+
"### Period Detail",
|
|
1143
|
+
"",
|
|
1144
1144
|
showCost
|
|
1145
|
-
?
|
|
1146
|
-
:
|
|
1145
|
+
? "| Period | Requests | Input | Output | Cache | Cache Hit | Total | API Cost |"
|
|
1146
|
+
: "| Period | Requests | Input | Output | Cache | Cache Hit | Total |",
|
|
1147
1147
|
showCost
|
|
1148
|
-
?
|
|
1149
|
-
:
|
|
1148
|
+
? "| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |"
|
|
1149
|
+
: "| --- | ---: | ---: | ---: | ---: | ---: | ---: |",
|
|
1150
1150
|
...detailRows,
|
|
1151
1151
|
...(result.rows.some((row) => row.range.isCurrent)
|
|
1152
|
-
? [
|
|
1152
|
+
? ["", "* `*` marks the current partial period."]
|
|
1153
1153
|
: []),
|
|
1154
|
-
].join(
|
|
1154
|
+
].join("\n");
|
|
1155
1155
|
}
|
|
1156
1156
|
export function renderMarkdownReport(period, usage, quotas, options) {
|
|
1157
1157
|
const showCost = options?.showCost !== false;
|
|
1158
1158
|
const cacheMetrics = getCacheCoverageMetrics(usage);
|
|
1159
|
-
const mdCell = (value) => sanitizeLine(value).replace(/\|/g,
|
|
1159
|
+
const mdCell = (value) => sanitizeLine(value).replace(/\|/g, "\\|");
|
|
1160
1160
|
const rightCodeSubscriptionProviderIDs = new Set(collapseQuotaSnapshots(quotas)
|
|
1161
|
-
.filter((quota) => quota.adapterID ===
|
|
1162
|
-
.filter((quota) => quota.status ===
|
|
1161
|
+
.filter((quota) => quota.adapterID === "rightcode")
|
|
1162
|
+
.filter((quota) => quota.status === "ok")
|
|
1163
1163
|
.filter((quota) => Array.isArray(quota.windows) && quota.windows.length)
|
|
1164
|
-
.filter((quota) => quota.windows[0].label.startsWith(
|
|
1164
|
+
.filter((quota) => quota.windows[0].label.startsWith("Daily $"))
|
|
1165
1165
|
.map((quota) => quota.providerID));
|
|
1166
1166
|
const measuredCostCell = (providerID, cost) => {
|
|
1167
1167
|
const canonical = canonicalProviderID(providerID);
|
|
1168
|
-
const isSubscription = canonical ===
|
|
1169
|
-
canonical ===
|
|
1170
|
-
canonical ===
|
|
1168
|
+
const isSubscription = canonical === "openai" ||
|
|
1169
|
+
canonical === "anthropic" ||
|
|
1170
|
+
canonical === "github-copilot" ||
|
|
1171
1171
|
rightCodeSubscriptionProviderIDs.has(providerID);
|
|
1172
1172
|
if (isSubscription)
|
|
1173
|
-
return
|
|
1173
|
+
return "-";
|
|
1174
1174
|
return formatUsd(cost);
|
|
1175
1175
|
};
|
|
1176
1176
|
const isSubscriptionMeasuredProvider = (providerID) => {
|
|
1177
1177
|
const canonical = canonicalProviderID(providerID);
|
|
1178
|
-
return (canonical ===
|
|
1179
|
-
canonical ===
|
|
1180
|
-
canonical ===
|
|
1178
|
+
return (canonical === "openai" ||
|
|
1179
|
+
canonical === "anthropic" ||
|
|
1180
|
+
canonical === "github-copilot" ||
|
|
1181
1181
|
rightCodeSubscriptionProviderIDs.has(providerID));
|
|
1182
1182
|
};
|
|
1183
1183
|
const apiCostCell = (providerID, apiCost) => {
|
|
1184
1184
|
const canonical = canonicalProviderID(providerID);
|
|
1185
|
-
if (canonical ===
|
|
1186
|
-
return
|
|
1185
|
+
if (canonical === "github-copilot")
|
|
1186
|
+
return "-";
|
|
1187
1187
|
return formatUsd(apiCost);
|
|
1188
1188
|
};
|
|
1189
1189
|
const measuredCostSummaryValue = () => {
|
|
@@ -1192,23 +1192,23 @@ export function renderMarkdownReport(period, usage, quotas, options) {
|
|
|
1192
1192
|
return formatUsd(usage.cost);
|
|
1193
1193
|
const hasNonSubscription = providers.some((provider) => !isSubscriptionMeasuredProvider(provider.providerID));
|
|
1194
1194
|
if (!hasNonSubscription)
|
|
1195
|
-
return
|
|
1195
|
+
return "-";
|
|
1196
1196
|
return formatUsd(usage.cost);
|
|
1197
1197
|
};
|
|
1198
1198
|
const apiCostSummaryValue = () => {
|
|
1199
1199
|
const providers = Object.values(usage.providers);
|
|
1200
1200
|
if (providers.length === 0)
|
|
1201
1201
|
return formatApiCostValue(usage.apiCost);
|
|
1202
|
-
const hasNonCopilot = providers.some((provider) => canonicalProviderID(provider.providerID) !==
|
|
1202
|
+
const hasNonCopilot = providers.some((provider) => canonicalProviderID(provider.providerID) !== "github-copilot");
|
|
1203
1203
|
if (!hasNonCopilot)
|
|
1204
|
-
return
|
|
1204
|
+
return "-";
|
|
1205
1205
|
return formatApiCostValue(usage.apiCost);
|
|
1206
1206
|
};
|
|
1207
1207
|
const cachedCell = (provider) => {
|
|
1208
1208
|
const metrics = getProviderCacheCoverageMetrics(provider);
|
|
1209
1209
|
return metrics.cachedRatio !== undefined
|
|
1210
1210
|
? formatPercent(metrics.cachedRatio, 1)
|
|
1211
|
-
:
|
|
1211
|
+
: "-";
|
|
1212
1212
|
};
|
|
1213
1213
|
const providerEntries = Object.values(usage.providers).sort((a, b) => b.total - a.total);
|
|
1214
1214
|
const highlightLines = () => {
|
|
@@ -1216,7 +1216,7 @@ export function renderMarkdownReport(period, usage, quotas, options) {
|
|
|
1216
1216
|
const providerLabel = (providerID) => quotaDisplayLabel({
|
|
1217
1217
|
providerID,
|
|
1218
1218
|
label: providerID,
|
|
1219
|
-
status:
|
|
1219
|
+
status: "ok",
|
|
1220
1220
|
checkedAt: 0,
|
|
1221
1221
|
});
|
|
1222
1222
|
const topApiCost = providerEntries
|
|
@@ -1226,7 +1226,7 @@ export function renderMarkdownReport(period, usage, quotas, options) {
|
|
|
1226
1226
|
lines.push(`- Top API cost: ${quotaDisplayLabel({
|
|
1227
1227
|
providerID: topApiCost.providerID,
|
|
1228
1228
|
label: topApiCost.providerID,
|
|
1229
|
-
status:
|
|
1229
|
+
status: "ok",
|
|
1230
1230
|
checkedAt: 0,
|
|
1231
1231
|
})} (${formatUsd(topApiCost.apiCost)})`);
|
|
1232
1232
|
}
|
|
@@ -1241,7 +1241,7 @@ export function renderMarkdownReport(period, usage, quotas, options) {
|
|
|
1241
1241
|
lines.push(`- Best Cached Ratio: ${providerLabel(bestCachedRatio.provider.providerID)} (${formatPercent(bestCachedRatio.value, 1)})`);
|
|
1242
1242
|
}
|
|
1243
1243
|
const highestMeasured = providerEntries
|
|
1244
|
-
.filter((provider) => measuredCostCell(provider.providerID, provider.cost) !==
|
|
1244
|
+
.filter((provider) => measuredCostCell(provider.providerID, provider.cost) !== "-")
|
|
1245
1245
|
.sort((a, b) => b.cost - a.cost)[0];
|
|
1246
1246
|
if (highestMeasured && highestMeasured.cost > 0) {
|
|
1247
1247
|
lines.push(`- Highest measured cost: ${providerLabel(highestMeasured.providerID)} (${formatUsd(highestMeasured.cost)})`);
|
|
@@ -1255,27 +1255,27 @@ export function renderMarkdownReport(period, usage, quotas, options) {
|
|
|
1255
1255
|
: `| ${providerID} | ${shortNumber(provider.assistantMessages)} | ${shortNumber(provider.input)} | ${shortNumber(provider.output)} | ${shortNumber(provider.cacheRead + provider.cacheWrite)} | ${shortNumber(provider.total)} |`;
|
|
1256
1256
|
});
|
|
1257
1257
|
const providerHeader = showCost
|
|
1258
|
-
?
|
|
1259
|
-
:
|
|
1258
|
+
? "| Provider | Requests | Input | Output | Cache | Total | Cached | Measured Cost | API Cost |"
|
|
1259
|
+
: "| Provider | Requests | Input | Output | Cache | Total |";
|
|
1260
1260
|
const providerDivider = showCost
|
|
1261
|
-
?
|
|
1262
|
-
:
|
|
1261
|
+
? "| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |"
|
|
1262
|
+
: "| --- | ---: | ---: | ---: | ---: | ---: |";
|
|
1263
1263
|
const quotaLines = toolVisibleQuotaSnapshots(quotas).flatMap((quota) => {
|
|
1264
1264
|
const displayLabel = quotaDisplayLabel(quota);
|
|
1265
|
-
const staleSuffix = quota.stale ?
|
|
1265
|
+
const staleSuffix = quota.stale ? " | stale" : "";
|
|
1266
1266
|
// Multi-window detail
|
|
1267
|
-
if (quota.windows && quota.windows.length > 0 && quota.status ===
|
|
1267
|
+
if (quota.windows && quota.windows.length > 0 && quota.status === "ok") {
|
|
1268
1268
|
const windowLines = quota.windows.map((win) => {
|
|
1269
1269
|
const extraNote = win.note || (win === quota.windows?.[0] && quota.note)
|
|
1270
1270
|
? ` | ${win.note || quota.note}`
|
|
1271
|
-
:
|
|
1272
|
-
const staleNote = quota.stale && win === quota.windows?.[0] ? staleSuffix :
|
|
1271
|
+
: "";
|
|
1272
|
+
const staleNote = quota.stale && win === quota.windows?.[0] ? staleSuffix : "";
|
|
1273
1273
|
if (win.showPercent === false) {
|
|
1274
|
-
const winLabel = win.label ? ` (${win.label})` :
|
|
1274
|
+
const winLabel = win.label ? ` (${win.label})` : "";
|
|
1275
1275
|
return mdCell(`- ${displayLabel}${winLabel}: ${quota.status} | reset ${reportResetLine(win.resetAt, win.resetLabel, win.label)}${extraNote}${staleNote}`);
|
|
1276
1276
|
}
|
|
1277
1277
|
const remaining = formatQuotaPercent(win.remainingPercent);
|
|
1278
|
-
const winLabel = win.label ? ` (${win.label})` :
|
|
1278
|
+
const winLabel = win.label ? ` (${win.label})` : "";
|
|
1279
1279
|
return mdCell(`- ${displayLabel}${winLabel}: ${quota.status} | remaining ${remaining} | reset ${reportResetLine(win.resetAt, win.resetLabel, win.label)}${extraNote}${staleNote}`);
|
|
1280
1280
|
});
|
|
1281
1281
|
if (quota.balance) {
|
|
@@ -1283,32 +1283,32 @@ export function renderMarkdownReport(period, usage, quotas, options) {
|
|
|
1283
1283
|
}
|
|
1284
1284
|
return windowLines;
|
|
1285
1285
|
}
|
|
1286
|
-
if (quota.status ===
|
|
1286
|
+
if (quota.status === "ok" && quota.balance) {
|
|
1287
1287
|
return [
|
|
1288
1288
|
mdCell(`- ${displayLabel}: ${quota.status} | balance ${formatCurrency(quota.balance.amount, quota.balance.currency)}${staleSuffix}`),
|
|
1289
1289
|
];
|
|
1290
1290
|
}
|
|
1291
|
-
if (quota.status ===
|
|
1291
|
+
if (quota.status === "error") {
|
|
1292
1292
|
return [
|
|
1293
|
-
mdCell(`- ${displayLabel}: ${quota.status}${quota.note ? ` | ${quota.note}` :
|
|
1293
|
+
mdCell(`- ${displayLabel}: ${quota.status}${quota.note ? ` | ${quota.note}` : ""}`),
|
|
1294
1294
|
];
|
|
1295
1295
|
}
|
|
1296
1296
|
const remaining = formatQuotaPercent(quota.remainingPercent);
|
|
1297
1297
|
return [
|
|
1298
|
-
mdCell(`- ${displayLabel}: ${quota.status} | remaining ${remaining} | reset ${reportResetLine(quota.resetAt)}${quota.note ? ` | ${quota.note}` :
|
|
1298
|
+
mdCell(`- ${displayLabel}: ${quota.status} | remaining ${remaining} | reset ${reportResetLine(quota.resetAt)}${quota.note ? ` | ${quota.note}` : ""}${staleSuffix}`),
|
|
1299
1299
|
];
|
|
1300
1300
|
});
|
|
1301
1301
|
return [
|
|
1302
1302
|
`## Quota Report - ${periodLabel(period)}`,
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1303
|
+
"",
|
|
1304
|
+
"### Quota Status",
|
|
1305
|
+
"",
|
|
1306
1306
|
...(quotaLines.length
|
|
1307
1307
|
? quotaLines
|
|
1308
|
-
: [
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1308
|
+
: ["- no provider quota data available"]),
|
|
1309
|
+
"",
|
|
1310
|
+
"### Usage Summary",
|
|
1311
|
+
"",
|
|
1312
1312
|
`- Sessions: ${usage.sessionCount}`,
|
|
1313
1313
|
`- Requests: ${usage.assistantMessages}`,
|
|
1314
1314
|
`- Tokens: input ${usage.input}, output ${usage.output}, cache_read ${usage.cacheRead}, cache_write ${usage.cacheWrite}, total ${usage.total}`,
|
|
@@ -1319,24 +1319,25 @@ export function renderMarkdownReport(period, usage, quotas, options) {
|
|
|
1319
1319
|
? [
|
|
1320
1320
|
`- Measured cost: ${measuredCostSummaryValue()}`,
|
|
1321
1321
|
`- API cost: ${apiCostSummaryValue()}`,
|
|
1322
|
+
"- API cost is an API-equivalent estimate based on model pricing.",
|
|
1322
1323
|
]
|
|
1323
1324
|
: []),
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1325
|
+
"",
|
|
1326
|
+
"### Usage by Provider",
|
|
1327
|
+
"",
|
|
1327
1328
|
providerHeader,
|
|
1328
1329
|
providerDivider,
|
|
1329
1330
|
...(providerRows.length
|
|
1330
1331
|
? providerRows
|
|
1331
1332
|
: [
|
|
1332
1333
|
showCost
|
|
1333
|
-
?
|
|
1334
|
-
:
|
|
1334
|
+
? "| - | - | - | - | - | - | - | - | - |"
|
|
1335
|
+
: "| - | - | - | - | - | - |",
|
|
1335
1336
|
]),
|
|
1336
1337
|
...(highlightLines().length > 0
|
|
1337
|
-
? [
|
|
1338
|
+
? ["", "### Highlights", ...highlightLines()]
|
|
1338
1339
|
: []),
|
|
1339
|
-
].join(
|
|
1340
|
+
].join("\n");
|
|
1340
1341
|
}
|
|
1341
1342
|
export function renderToastMessage(period, usage, quotas, options) {
|
|
1342
1343
|
const width = Math.max(24, Math.floor(options?.width || 56));
|
|
@@ -1344,28 +1345,28 @@ export function renderToastMessage(period, usage, quotas, options) {
|
|
|
1344
1345
|
const cacheMetrics = getCacheCoverageMetrics(usage);
|
|
1345
1346
|
const lines = [];
|
|
1346
1347
|
lines.push(fitLine(`${periodLabel(period)} - Total ${shortNumber(usage.total)}`, width));
|
|
1347
|
-
lines.push(
|
|
1348
|
-
lines.push(fitLine(
|
|
1348
|
+
lines.push("");
|
|
1349
|
+
lines.push(fitLine("Token Usage", width));
|
|
1349
1350
|
const tokenPairs = [
|
|
1350
|
-
{ label:
|
|
1351
|
-
{ label:
|
|
1352
|
-
{ label:
|
|
1351
|
+
{ label: "Requests", value: shortNumber(usage.assistantMessages) },
|
|
1352
|
+
{ label: "Input", value: shortNumber(usage.input) },
|
|
1353
|
+
{ label: "Output", value: shortNumber(usage.output) },
|
|
1353
1354
|
];
|
|
1354
1355
|
if (usage.cacheWrite > 0) {
|
|
1355
1356
|
tokenPairs.push({
|
|
1356
|
-
label:
|
|
1357
|
+
label: "Cache Write",
|
|
1357
1358
|
value: shortNumber(usage.cacheWrite),
|
|
1358
1359
|
});
|
|
1359
1360
|
}
|
|
1360
1361
|
if (usage.cacheRead > 0) {
|
|
1361
1362
|
tokenPairs.push({
|
|
1362
|
-
label:
|
|
1363
|
+
label: "Cache Read",
|
|
1363
1364
|
value: shortNumber(usage.cacheRead),
|
|
1364
1365
|
});
|
|
1365
1366
|
}
|
|
1366
1367
|
if (cacheMetrics.cachedRatio !== undefined) {
|
|
1367
1368
|
tokenPairs.push({
|
|
1368
|
-
label:
|
|
1369
|
+
label: "Cached",
|
|
1369
1370
|
value: formatPercent(cacheMetrics.cachedRatio, 1),
|
|
1370
1371
|
});
|
|
1371
1372
|
}
|
|
@@ -1382,37 +1383,37 @@ export function renderToastMessage(period, usage, quotas, options) {
|
|
|
1382
1383
|
})
|
|
1383
1384
|
.filter((item) => Boolean(item));
|
|
1384
1385
|
if (providerCachePairs.length > 0) {
|
|
1385
|
-
lines.push(
|
|
1386
|
-
lines.push(fitLine(
|
|
1386
|
+
lines.push("");
|
|
1387
|
+
lines.push(fitLine("Provider Cache", width));
|
|
1387
1388
|
lines.push(...alignPairs(providerCachePairs).map((line) => fitLine(line, width)));
|
|
1388
1389
|
}
|
|
1389
1390
|
if (showCost) {
|
|
1390
1391
|
const costPairs = Object.values(usage.providers)
|
|
1391
|
-
.filter((provider) => canonicalProviderID(provider.providerID) !==
|
|
1392
|
+
.filter((provider) => canonicalProviderID(provider.providerID) !== "github-copilot")
|
|
1392
1393
|
.filter((provider) => provider.apiCost > 0)
|
|
1393
1394
|
.sort((left, right) => right.apiCost - left.apiCost)
|
|
1394
1395
|
.map((provider) => ({
|
|
1395
1396
|
label: displayShortLabel(provider.providerID),
|
|
1396
1397
|
value: formatUsd(provider.apiCost),
|
|
1397
1398
|
}));
|
|
1398
|
-
lines.push(
|
|
1399
|
-
lines.push(fitLine(
|
|
1399
|
+
lines.push("");
|
|
1400
|
+
lines.push(fitLine("Cost as API", width));
|
|
1400
1401
|
if (costPairs.length > 0) {
|
|
1401
1402
|
lines.push(...alignPairs(costPairs).map((line) => fitLine(line, width)));
|
|
1402
1403
|
}
|
|
1403
1404
|
else {
|
|
1404
1405
|
const hasAnyUsage = Object.keys(usage.providers).length > 0;
|
|
1405
1406
|
const hasOnlyCopilotUsage = hasAnyUsage &&
|
|
1406
|
-
Object.values(usage.providers).every((provider) => canonicalProviderID(provider.providerID) ===
|
|
1407
|
+
Object.values(usage.providers).every((provider) => canonicalProviderID(provider.providerID) === "github-copilot");
|
|
1407
1408
|
lines.push(fitLine(hasOnlyCopilotUsage
|
|
1408
|
-
?
|
|
1409
|
+
? " N/A (Copilot)"
|
|
1409
1410
|
: hasAnyUsage
|
|
1410
|
-
?
|
|
1411
|
-
:
|
|
1411
|
+
? " N/A"
|
|
1412
|
+
: " -", width));
|
|
1412
1413
|
}
|
|
1413
1414
|
}
|
|
1414
1415
|
const quotaPairs = toolVisibleQuotaSnapshots(quotas).flatMap((item) => {
|
|
1415
|
-
if (item.status ===
|
|
1416
|
+
if (item.status === "ok") {
|
|
1416
1417
|
if (item.windows && item.windows.length > 0) {
|
|
1417
1418
|
const pairs = item.windows.map((win, idx) => {
|
|
1418
1419
|
const showPercent = win.showPercent !== false;
|
|
@@ -1422,19 +1423,19 @@ export function renderToastMessage(period, usage, quotas, options) {
|
|
|
1422
1423
|
if (showPercent)
|
|
1423
1424
|
parts.push(pct);
|
|
1424
1425
|
if (reset)
|
|
1425
|
-
parts.push(`${win.resetLabel ||
|
|
1426
|
+
parts.push(`${win.resetLabel || "Rst"} ${reset}`);
|
|
1426
1427
|
if (win.note)
|
|
1427
1428
|
parts.push(win.note);
|
|
1428
1429
|
if (item.stale && idx === 0)
|
|
1429
|
-
parts.push(
|
|
1430
|
+
parts.push("stale");
|
|
1430
1431
|
return {
|
|
1431
|
-
label: idx === 0 ? quotaDisplayLabel(item) :
|
|
1432
|
-
value: parts.filter(Boolean).join(
|
|
1432
|
+
label: idx === 0 ? quotaDisplayLabel(item) : "",
|
|
1433
|
+
value: parts.filter(Boolean).join(" "),
|
|
1433
1434
|
};
|
|
1434
1435
|
});
|
|
1435
1436
|
if (item.balance) {
|
|
1436
1437
|
pairs.push({
|
|
1437
|
-
label:
|
|
1438
|
+
label: "",
|
|
1438
1439
|
value: `Balance ${formatCurrency(item.balance.amount, item.balance.currency)}`,
|
|
1439
1440
|
});
|
|
1440
1441
|
}
|
|
@@ -1444,31 +1445,31 @@ export function renderToastMessage(period, usage, quotas, options) {
|
|
|
1444
1445
|
return [
|
|
1445
1446
|
{
|
|
1446
1447
|
label: quotaDisplayLabel(item),
|
|
1447
|
-
value: `Balance ${formatCurrency(item.balance.amount, item.balance.currency)}${item.stale ?
|
|
1448
|
+
value: `Balance ${formatCurrency(item.balance.amount, item.balance.currency)}${item.stale ? " stale" : ""}`,
|
|
1448
1449
|
},
|
|
1449
1450
|
];
|
|
1450
1451
|
}
|
|
1451
1452
|
const percent = formatQuotaPercent(item.remainingPercent);
|
|
1452
|
-
const reset = compactReset(item.resetAt,
|
|
1453
|
+
const reset = compactReset(item.resetAt, "Rst");
|
|
1453
1454
|
return [
|
|
1454
1455
|
{
|
|
1455
1456
|
label: quotaDisplayLabel(item),
|
|
1456
|
-
value: `Remaining ${percent}${reset ? ` Rst ${reset}` :
|
|
1457
|
+
value: `Remaining ${percent}${reset ? ` Rst ${reset}` : ""}${item.stale ? " stale" : ""}`,
|
|
1457
1458
|
},
|
|
1458
1459
|
];
|
|
1459
1460
|
}
|
|
1460
|
-
return [{ label: quotaDisplayLabel(item), value:
|
|
1461
|
+
return [{ label: quotaDisplayLabel(item), value: "Remaining ?" }];
|
|
1461
1462
|
});
|
|
1462
1463
|
if (quotaPairs.length > 0) {
|
|
1463
|
-
lines.push(
|
|
1464
|
-
lines.push(fitLine(
|
|
1464
|
+
lines.push("");
|
|
1465
|
+
lines.push(fitLine("Quota", width));
|
|
1465
1466
|
lines.push(...alignPairs(quotaPairs).map((line) => fitLine(line, width)));
|
|
1466
1467
|
}
|
|
1467
1468
|
const expiryPairs = quotaExpiryPairs(quotas);
|
|
1468
1469
|
if (expiryPairs.length > 0) {
|
|
1469
|
-
lines.push(
|
|
1470
|
-
lines.push(fitLine(
|
|
1470
|
+
lines.push("");
|
|
1471
|
+
lines.push(fitLine("Expiry Soon", width));
|
|
1471
1472
|
lines.push(...alignPairs(expiryPairs).map((line) => fitLine(line, width)));
|
|
1472
1473
|
}
|
|
1473
|
-
return lines.join(
|
|
1474
|
+
return lines.join("\n");
|
|
1474
1475
|
}
|