@leo000001/opencode-quota-sidebar 3.0.10 → 4.0.2
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/CHANGELOG.md +0 -1
- package/README.md +163 -42
- package/README.zh-CN.md +163 -42
- package/SECURITY.md +1 -1
- package/dist/cli.d.ts +18 -0
- package/dist/cli.js +354 -0
- package/dist/cli_render.d.ts +17 -0
- package/dist/cli_render.js +292 -0
- package/dist/events.d.ts +1 -1
- package/dist/events.js +2 -2
- package/dist/format.d.ts +4 -0
- package/dist/format.js +391 -49
- package/dist/history_messages.d.ts +8 -0
- package/dist/history_messages.js +157 -0
- package/dist/history_usage.d.ts +93 -0
- package/dist/history_usage.js +251 -0
- package/dist/index.js +29 -4
- package/dist/period.d.ts +29 -1
- package/dist/period.js +187 -9
- package/dist/provider_catalog.d.ts +8 -0
- package/dist/provider_catalog.js +68 -0
- package/dist/providers/core/anthropic.d.ts +1 -1
- package/dist/providers/core/anthropic.js +69 -45
- package/dist/providers/core/openai.js +38 -2
- package/dist/providers/index.d.ts +1 -2
- package/dist/providers/index.js +1 -3
- package/dist/quota.d.ts +4 -2
- package/dist/quota.js +18 -21
- package/dist/quota_render.d.ts +1 -1
- package/dist/quota_render.js +23 -24
- package/dist/quota_service.d.ts +1 -0
- package/dist/quota_service.js +151 -19
- package/dist/storage.d.ts +1 -1
- package/dist/storage.js +4 -4
- package/dist/storage_dates.d.ts +1 -1
- package/dist/storage_dates.js +8 -5
- package/dist/storage_parse.js +23 -1
- package/dist/supported_quota.d.ts +4 -0
- package/dist/supported_quota.js +36 -0
- package/dist/title.js +21 -10
- package/dist/tools.d.ts +14 -3
- package/dist/tools.js +54 -2
- package/dist/tui.tsx +17 -6
- package/dist/tui_helpers.js +11 -6
- package/dist/types.d.ts +8 -0
- package/dist/usage.d.ts +18 -0
- package/dist/usage.js +93 -9
- package/dist/usage_service.d.ts +4 -1
- package/dist/usage_service.js +193 -189
- package/package.json +4 -1
- package/quota-sidebar.config.example.json +36 -45
- package/dist/providers/third_party/xyai.d.ts +0 -2
- package/dist/providers/third_party/xyai.js +0 -348
package/dist/format.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { QuotaSidebarConfig, QuotaSnapshot } from './types.js';
|
|
2
|
+
import type { HistoryUsageResult } from './usage_service.js';
|
|
2
3
|
import { type UsageSummary } from './usage.js';
|
|
3
4
|
export type TitleView = 'multiline' | 'compact';
|
|
4
5
|
/**
|
|
@@ -31,6 +32,9 @@ export declare function renderSidebarQuotaLineGroups(quotas: QuotaSnapshot[], co
|
|
|
31
32
|
quota: QuotaSnapshot;
|
|
32
33
|
lines: string[];
|
|
33
34
|
}[];
|
|
35
|
+
export declare function renderHistoryMarkdownReport(result: HistoryUsageResult, quotas: QuotaSnapshot[], options?: {
|
|
36
|
+
showCost?: boolean;
|
|
37
|
+
}): string;
|
|
34
38
|
export declare function renderMarkdownReport(period: string, usage: UsageSummary, quotas: QuotaSnapshot[], options?: {
|
|
35
39
|
showCost?: boolean;
|
|
36
40
|
}): string;
|
package/dist/format.js
CHANGED
|
@@ -223,8 +223,6 @@ function compactProviderLabel(quota) {
|
|
|
223
223
|
return 'MiniMax';
|
|
224
224
|
if (canonical === 'rightcode')
|
|
225
225
|
return 'RC';
|
|
226
|
-
if (canonical === 'xyai')
|
|
227
|
-
return 'XYAI';
|
|
228
226
|
return sanitizeLine(quotaDisplayLabel(quota));
|
|
229
227
|
}
|
|
230
228
|
function compactWindowToken(label) {
|
|
@@ -267,6 +265,10 @@ function compactQuotaPercentToken(label, percent) {
|
|
|
267
265
|
}
|
|
268
266
|
if (/^cowork\s+7d$/i.test(safe))
|
|
269
267
|
return rounded ? `Co7d${rounded}` : 'Co7d';
|
|
268
|
+
if (/^spark\s+5h$/i.test(safe))
|
|
269
|
+
return rounded ? `Sk5h${rounded}` : 'Sk5h';
|
|
270
|
+
if (/^spark\s+weekly$/i.test(safe))
|
|
271
|
+
return rounded ? `SkW${rounded}` : 'SkW';
|
|
270
272
|
const token = compactWindowToken(safe).replace(/\s+/g, '');
|
|
271
273
|
if (!rounded)
|
|
272
274
|
return token;
|
|
@@ -328,6 +330,12 @@ function compactDesktopCurrencyValue(value, currency) {
|
|
|
328
330
|
return rendered.replace(/^\$/, '');
|
|
329
331
|
return rendered;
|
|
330
332
|
}
|
|
333
|
+
function compactQuotaStaleToken(quota) {
|
|
334
|
+
return quota.stale ? 'St' : undefined;
|
|
335
|
+
}
|
|
336
|
+
function verboseQuotaStaleText(quota) {
|
|
337
|
+
return quota.stale ? 'stale' : undefined;
|
|
338
|
+
}
|
|
331
339
|
function compactDesktopQuotaSegment(quota) {
|
|
332
340
|
const label = compactProviderLabel(quota);
|
|
333
341
|
if (quota.status !== 'ok') {
|
|
@@ -354,10 +362,15 @@ function compactDesktopQuotaSegment(quota) {
|
|
|
354
362
|
const balanceToken = `B${compactDesktopCurrencyValue(quota.balance.amount, quota.balance.currency)}`;
|
|
355
363
|
parts.push(balanceToken);
|
|
356
364
|
}
|
|
365
|
+
const staleToken = compactQuotaStaleToken(quota);
|
|
366
|
+
if (staleToken)
|
|
367
|
+
parts.push(staleToken);
|
|
357
368
|
return [label, ...parts].filter(Boolean).join(' ');
|
|
358
369
|
}
|
|
359
370
|
function renderDesktopCompactTitle(baseTitle, usage, quotas, config, _width) {
|
|
360
|
-
const visibleQuotas =
|
|
371
|
+
const visibleQuotas = config.sidebar.showQuota
|
|
372
|
+
? collapseQuotaSnapshots(quotas).filter((q) => ['ok', 'error', 'unsupported', 'unavailable'].includes(q.status))
|
|
373
|
+
: [];
|
|
361
374
|
const selectedProviderIDs = new Set(selectDesktopCompactProviderIDs(usage, config));
|
|
362
375
|
const quotaSegments = visibleQuotas
|
|
363
376
|
.filter((quota) => selectedProviderIDs.has(quota.providerID))
|
|
@@ -396,15 +409,22 @@ function usageDetailLines(usage, cacheMetrics, options) {
|
|
|
396
409
|
`I${numberToken(usage.input)}`,
|
|
397
410
|
`O${numberToken(usage.output)}`,
|
|
398
411
|
]);
|
|
399
|
-
const
|
|
412
|
+
const secondaryGroups = [];
|
|
413
|
+
const coverageTokens = [];
|
|
414
|
+
if (cacheMetrics.cachedRatio !== undefined) {
|
|
415
|
+
coverageTokens.push(`Cd ${formatPercent(cacheMetrics.cachedRatio, 0)}`);
|
|
416
|
+
}
|
|
417
|
+
if (coverageTokens.length > 0)
|
|
418
|
+
secondaryGroups.push(coverageTokens);
|
|
419
|
+
const cacheTokens = [];
|
|
400
420
|
const pushCacheRead = () => {
|
|
401
421
|
if (usage.cacheRead > 0) {
|
|
402
|
-
|
|
422
|
+
cacheTokens.push(`R${numberToken(usage.cacheRead)}`);
|
|
403
423
|
}
|
|
404
424
|
};
|
|
405
425
|
const pushCacheWrite = () => {
|
|
406
426
|
if (usage.cacheWrite > 0) {
|
|
407
|
-
|
|
427
|
+
cacheTokens.push(`W${numberToken(usage.cacheWrite)}`);
|
|
408
428
|
}
|
|
409
429
|
};
|
|
410
430
|
if (options.cacheReadFirst) {
|
|
@@ -415,11 +435,22 @@ function usageDetailLines(usage, cacheMetrics, options) {
|
|
|
415
435
|
pushCacheWrite();
|
|
416
436
|
pushCacheRead();
|
|
417
437
|
}
|
|
418
|
-
if (
|
|
419
|
-
|
|
438
|
+
if (cacheTokens.length > 0) {
|
|
439
|
+
if (coverageTokens.length > 0) {
|
|
440
|
+
const combined = [...coverageTokens, ...cacheTokens].join(' ');
|
|
441
|
+
if (fitsLine(combined, width)) {
|
|
442
|
+
secondaryGroups.length = 0;
|
|
443
|
+
secondaryGroups.push([...coverageTokens, ...cacheTokens]);
|
|
444
|
+
}
|
|
445
|
+
else {
|
|
446
|
+
secondaryGroups.push(cacheTokens);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
secondaryGroups.push(cacheTokens);
|
|
451
|
+
}
|
|
420
452
|
}
|
|
421
|
-
|
|
422
|
-
groups.push(secondary);
|
|
453
|
+
groups.push(...secondaryGroups);
|
|
423
454
|
if (options.showCost && usage.apiCost > 0) {
|
|
424
455
|
groups.push([costToken(usage.apiCost)]);
|
|
425
456
|
}
|
|
@@ -440,6 +471,65 @@ function usageDetailLines(usage, cacheMetrics, options) {
|
|
|
440
471
|
}
|
|
441
472
|
return packed;
|
|
442
473
|
}
|
|
474
|
+
function packUsageGroups(groups, width) {
|
|
475
|
+
const packed = [];
|
|
476
|
+
for (const group of groups) {
|
|
477
|
+
let current = '';
|
|
478
|
+
for (const token of group) {
|
|
479
|
+
const candidate = current ? `${current} ${token}` : token;
|
|
480
|
+
if (!current || fitsLine(candidate, width)) {
|
|
481
|
+
current = candidate;
|
|
482
|
+
continue;
|
|
483
|
+
}
|
|
484
|
+
packed.push(current);
|
|
485
|
+
current = token;
|
|
486
|
+
}
|
|
487
|
+
if (current)
|
|
488
|
+
packed.push(current);
|
|
489
|
+
}
|
|
490
|
+
return packed;
|
|
491
|
+
}
|
|
492
|
+
function readableSidebarUsageLines(usage, cacheMetrics, width, showCost) {
|
|
493
|
+
const groups = [
|
|
494
|
+
[
|
|
495
|
+
`Req ${shortNumber(usage.assistantMessages, 1)}`,
|
|
496
|
+
`In ${panelNumber(usage.input)}`,
|
|
497
|
+
`Out ${panelNumber(usage.output)}`,
|
|
498
|
+
],
|
|
499
|
+
];
|
|
500
|
+
if (cacheMetrics.cachedRatio !== undefined) {
|
|
501
|
+
const cacheTokens = [
|
|
502
|
+
`Cached ${formatPercent(cacheMetrics.cachedRatio, 0)}`,
|
|
503
|
+
];
|
|
504
|
+
if (usage.cacheRead > 0)
|
|
505
|
+
cacheTokens.push(`Read ${panelNumber(usage.cacheRead)}`);
|
|
506
|
+
if (usage.cacheWrite > 0)
|
|
507
|
+
cacheTokens.push(`Write ${panelNumber(usage.cacheWrite)}`);
|
|
508
|
+
groups.push(cacheTokens);
|
|
509
|
+
}
|
|
510
|
+
else {
|
|
511
|
+
const cacheTokens = [];
|
|
512
|
+
if (usage.cacheRead > 0)
|
|
513
|
+
cacheTokens.push(`Cache Read ${panelNumber(usage.cacheRead)}`);
|
|
514
|
+
if (usage.cacheWrite > 0)
|
|
515
|
+
cacheTokens.push(`Write ${panelNumber(usage.cacheWrite)}`);
|
|
516
|
+
if (cacheTokens.length > 0)
|
|
517
|
+
groups.push(cacheTokens);
|
|
518
|
+
}
|
|
519
|
+
const summaryTokens = [];
|
|
520
|
+
if (showCost && usage.apiCost > 0) {
|
|
521
|
+
summaryTokens.push(`Est ${formatApiCostValue(usage.apiCost)}`);
|
|
522
|
+
}
|
|
523
|
+
if (summaryTokens.length > 0)
|
|
524
|
+
groups.push(summaryTokens);
|
|
525
|
+
const allTokens = groups.flat();
|
|
526
|
+
if (allTokens.some((token) => !fitsLine(token, width)))
|
|
527
|
+
return undefined;
|
|
528
|
+
const packed = packUsageGroups(groups, width);
|
|
529
|
+
if (packed.length > 4)
|
|
530
|
+
return undefined;
|
|
531
|
+
return packed;
|
|
532
|
+
}
|
|
443
533
|
function formatQuotaPercent(value, options) {
|
|
444
534
|
const missing = options?.missing ?? '-';
|
|
445
535
|
if (value === undefined)
|
|
@@ -467,6 +557,7 @@ function alignPairs(pairs, indent = ' ') {
|
|
|
467
557
|
}
|
|
468
558
|
function compactQuotaInline(quota) {
|
|
469
559
|
const label = sanitizeLine(quotaDisplayLabel(quota));
|
|
560
|
+
const staleToken = compactQuotaStaleToken(quota);
|
|
470
561
|
if (quota.status !== 'ok') {
|
|
471
562
|
if (quota.status === 'error')
|
|
472
563
|
return `${label} Remaining ?`;
|
|
@@ -485,19 +576,19 @@ function compactQuotaInline(quota) {
|
|
|
485
576
|
: firstLabel.replace(/^Daily\s+/i, '') || firstLabel;
|
|
486
577
|
const hasMore = quota.windows.length > 1 ||
|
|
487
578
|
(quota.balance !== undefined && !summary.includes('Balance '));
|
|
488
|
-
return `${label}${summary ? ` ${summary}` : ''}${hasMore ? '+' : ''}`;
|
|
579
|
+
return `${label}${summary ? ` ${summary}` : ''}${hasMore ? '+' : ''}${staleToken ? ` ${staleToken}` : ''}`;
|
|
489
580
|
}
|
|
490
581
|
if (quota.balance) {
|
|
491
|
-
return `${label} Balance ${formatCurrency(quota.balance.amount, quota.balance.currency)}`;
|
|
582
|
+
return `${label} Balance ${formatCurrency(quota.balance.amount, quota.balance.currency)}${staleToken ? ` ${staleToken}` : ''}`;
|
|
492
583
|
}
|
|
493
584
|
const singlePercent = formatQuotaPercent(quota.remainingPercent, {
|
|
494
585
|
rounded: true,
|
|
495
586
|
missing: '',
|
|
496
587
|
});
|
|
497
588
|
if (singlePercent) {
|
|
498
|
-
return `${label} ${singlePercent}`;
|
|
589
|
+
return `${label} ${singlePercent}${staleToken ? ` ${staleToken}` : ''}`;
|
|
499
590
|
}
|
|
500
|
-
return label
|
|
591
|
+
return `${label}${staleToken ? ` ${staleToken}` : ''}`;
|
|
501
592
|
}
|
|
502
593
|
function renderSingleLineTitle(baseTitle, usage, quotas, config, width) {
|
|
503
594
|
const baseBudget = Math.min(16, Math.max(8, Math.floor(width * 0.35)));
|
|
@@ -597,9 +688,13 @@ export function renderSidebarContextLine(tokens, percent, width) {
|
|
|
597
688
|
export function renderSidebarUsageLines(usage, config, options) {
|
|
598
689
|
const width = Math.max(8, Math.floor(config.sidebar.width || 36));
|
|
599
690
|
const cacheMetrics = getCacheCoverageMetrics(usage);
|
|
691
|
+
const showCost = options?.showCost ?? config.sidebar.showCost;
|
|
692
|
+
const readable = readableSidebarUsageLines(usage, cacheMetrics, width, showCost);
|
|
693
|
+
if (readable)
|
|
694
|
+
return readable.map((line) => fitLine(line, width));
|
|
600
695
|
return usageDetailLines(usage, cacheMetrics, {
|
|
601
696
|
width,
|
|
602
|
-
showCost
|
|
697
|
+
showCost,
|
|
603
698
|
numberToken: panelNumber,
|
|
604
699
|
costToken: (value) => `Est ${formatApiCostValue(value)}`,
|
|
605
700
|
cacheReadFirst: true,
|
|
@@ -711,6 +806,9 @@ function compactQuotaWide(quota, labelWidth = 0, options) {
|
|
|
711
806
|
const tokens = [...compactTokens];
|
|
712
807
|
if (balanceText)
|
|
713
808
|
tokens.push(balanceText);
|
|
809
|
+
const staleToken = compactQuotaStaleToken(quota);
|
|
810
|
+
if (staleToken)
|
|
811
|
+
tokens.push(staleToken);
|
|
714
812
|
return packInlineTokens(label, tokens, width, ' '.repeat(stringCellWidth(label) + 1));
|
|
715
813
|
}
|
|
716
814
|
// Keep a unified wrapped layout for providers that have multiple detail
|
|
@@ -724,16 +822,22 @@ function compactQuotaWide(quota, labelWidth = 0, options) {
|
|
|
724
822
|
return maybeBreak(single, [single]);
|
|
725
823
|
}
|
|
726
824
|
if (balanceText) {
|
|
727
|
-
|
|
825
|
+
const staleText = verboseQuotaStaleText(quota);
|
|
826
|
+
const detail = staleText ? `${balanceText} ${staleText}` : balanceText;
|
|
827
|
+
return maybeBreak(detail, [detail]);
|
|
728
828
|
}
|
|
729
829
|
// Fallback: single value from top-level remainingPercent
|
|
730
830
|
const percent = formatQuotaPercent(quota.remainingPercent, { rounded: true });
|
|
731
831
|
const reset = compactReset(quota.resetAt, 'Rst');
|
|
732
832
|
const fallbackText = compactDetails
|
|
733
|
-
? [
|
|
833
|
+
? [
|
|
834
|
+
`R${percent.replace(/%$/, '')}`,
|
|
835
|
+
reset ? `R${reset}` : undefined,
|
|
836
|
+
compactQuotaStaleToken(quota),
|
|
837
|
+
]
|
|
734
838
|
.filter(Boolean)
|
|
735
839
|
.join(' ')
|
|
736
|
-
: `Remaining ${percent}${reset ? ` Rst ${reset}` : ''}`;
|
|
840
|
+
: `Remaining ${percent}${reset ? ` Rst ${reset}` : ''}${verboseQuotaStaleText(quota) ? ` ${verboseQuotaStaleText(quota)}` : ''}`;
|
|
737
841
|
return maybeBreak(fallbackText, [fallbackText]);
|
|
738
842
|
}
|
|
739
843
|
function compactCountdown(remainingMs) {
|
|
@@ -817,6 +921,238 @@ function periodLabel(period) {
|
|
|
817
921
|
return 'This Month';
|
|
818
922
|
return 'Current Session';
|
|
819
923
|
}
|
|
924
|
+
function historyPeriodLabel(period) {
|
|
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
|
+
}
|
|
933
|
+
function historyProviderLabel(providerID) {
|
|
934
|
+
return quotaDisplayLabel({
|
|
935
|
+
providerID,
|
|
936
|
+
label: providerID,
|
|
937
|
+
status: 'ok',
|
|
938
|
+
checkedAt: 0,
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
function historyMdCell(value) {
|
|
942
|
+
return sanitizeLine(value).replace(/\|/g, '\\|');
|
|
943
|
+
}
|
|
944
|
+
function formatDelta(current, previous) {
|
|
945
|
+
if (previous === undefined)
|
|
946
|
+
return 'n/a';
|
|
947
|
+
if (!Number.isFinite(previous) || previous < 0)
|
|
948
|
+
return 'n/a';
|
|
949
|
+
if (previous === 0)
|
|
950
|
+
return current === 0 ? 'flat' : 'new';
|
|
951
|
+
const delta = ((current - previous) / previous) * 100;
|
|
952
|
+
if (!Number.isFinite(delta))
|
|
953
|
+
return 'n/a';
|
|
954
|
+
const abs = Math.abs(delta);
|
|
955
|
+
const rounded = (abs >= 10 ? delta.toFixed(0) : delta.toFixed(1)).replace(/\.0$/, '');
|
|
956
|
+
return `${delta > 0 ? '+' : ''}${rounded}%`;
|
|
957
|
+
}
|
|
958
|
+
function currentHistoryRow(result) {
|
|
959
|
+
return ([...result.rows].reverse().find((row) => row.range.isCurrent) ||
|
|
960
|
+
result.rows.at(-1));
|
|
961
|
+
}
|
|
962
|
+
function previousHistoryRow(result) {
|
|
963
|
+
const current = currentHistoryRow(result);
|
|
964
|
+
if (!current)
|
|
965
|
+
return undefined;
|
|
966
|
+
const index = result.rows.indexOf(current);
|
|
967
|
+
if (index <= 0)
|
|
968
|
+
return undefined;
|
|
969
|
+
return result.rows[index - 1];
|
|
970
|
+
}
|
|
971
|
+
function historyPeakRow(result, pick) {
|
|
972
|
+
let peak;
|
|
973
|
+
let peakValue = Number.NEGATIVE_INFINITY;
|
|
974
|
+
for (const row of result.rows) {
|
|
975
|
+
const value = pick(row);
|
|
976
|
+
if (value > peakValue) {
|
|
977
|
+
peak = row;
|
|
978
|
+
peakValue = value;
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
return peak;
|
|
982
|
+
}
|
|
983
|
+
function renderHistoryTotalsTable(result, options) {
|
|
984
|
+
const rows = result.rows;
|
|
985
|
+
const cacheTotal = getCacheCoverageMetrics(result.total).cachedRatio;
|
|
986
|
+
const cacheValues = rows
|
|
987
|
+
.map((row) => getCacheCoverageMetrics(row.usage).cachedRatio)
|
|
988
|
+
.filter((value) => value !== undefined);
|
|
989
|
+
const cacheAverage = cacheValues.length > 0
|
|
990
|
+
? cacheValues.reduce((sum, value) => sum + value, 0) / cacheValues.length
|
|
991
|
+
: undefined;
|
|
992
|
+
const metricRows = [
|
|
993
|
+
{
|
|
994
|
+
label: 'Requests',
|
|
995
|
+
total: shortNumber(result.total.assistantMessages),
|
|
996
|
+
average: rows.length
|
|
997
|
+
? shortNumber(result.total.assistantMessages / rows.length)
|
|
998
|
+
: '-',
|
|
999
|
+
},
|
|
1000
|
+
{
|
|
1001
|
+
label: 'Total Tokens',
|
|
1002
|
+
total: shortNumber(result.total.total),
|
|
1003
|
+
average: rows.length
|
|
1004
|
+
? shortNumber(result.total.total / rows.length)
|
|
1005
|
+
: '-',
|
|
1006
|
+
},
|
|
1007
|
+
{
|
|
1008
|
+
label: 'Cache Hit',
|
|
1009
|
+
total: cacheTotal !== undefined ? formatPercent(cacheTotal, 1) : '-',
|
|
1010
|
+
average: cacheAverage !== undefined ? formatPercent(cacheAverage, 1) : '-',
|
|
1011
|
+
},
|
|
1012
|
+
...(options?.showCost !== false
|
|
1013
|
+
? [
|
|
1014
|
+
{
|
|
1015
|
+
label: 'API Cost',
|
|
1016
|
+
total: formatApiCostValue(result.total.apiCost),
|
|
1017
|
+
average: rows.length
|
|
1018
|
+
? formatApiCostValue(result.total.apiCost / rows.length)
|
|
1019
|
+
: '-',
|
|
1020
|
+
},
|
|
1021
|
+
]
|
|
1022
|
+
: []),
|
|
1023
|
+
];
|
|
1024
|
+
return [
|
|
1025
|
+
'| Metric | Total | Avg/Period |',
|
|
1026
|
+
'| --- | ---: | ---: |',
|
|
1027
|
+
...metricRows.map((metric) => `| ${metric.label} | ${metric.total} | ${metric.average} |`),
|
|
1028
|
+
];
|
|
1029
|
+
}
|
|
1030
|
+
function renderHistoryProviderBreakdown(result, options) {
|
|
1031
|
+
const providers = Object.values(result.total.providers);
|
|
1032
|
+
if (providers.length === 0)
|
|
1033
|
+
return ['- no provider activity in selected range'];
|
|
1034
|
+
const sorted = [...providers].sort((a, b) => {
|
|
1035
|
+
if (b.total !== a.total)
|
|
1036
|
+
return b.total - a.total;
|
|
1037
|
+
return b.assistantMessages - a.assistantMessages;
|
|
1038
|
+
});
|
|
1039
|
+
return [
|
|
1040
|
+
options?.showCost !== false
|
|
1041
|
+
? '| Provider | Req | Input | Output | Total | Share | Cache Hit | API Cost |'
|
|
1042
|
+
: '| Provider | Req | Input | Output | Total | Share | Cache Hit |',
|
|
1043
|
+
options?.showCost !== false
|
|
1044
|
+
? '| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |'
|
|
1045
|
+
: '| --- | ---: | ---: | ---: | ---: | ---: | ---: |',
|
|
1046
|
+
...sorted.map((provider) => {
|
|
1047
|
+
const cache = getProviderCacheCoverageMetrics(provider).cachedRatio;
|
|
1048
|
+
const share = result.total.total > 0
|
|
1049
|
+
? formatPercent(provider.total / result.total.total, 1)
|
|
1050
|
+
: '-';
|
|
1051
|
+
const cells = [
|
|
1052
|
+
historyMdCell(historyProviderLabel(provider.providerID)),
|
|
1053
|
+
shortNumber(provider.assistantMessages),
|
|
1054
|
+
shortNumber(provider.input),
|
|
1055
|
+
shortNumber(provider.output),
|
|
1056
|
+
shortNumber(provider.total),
|
|
1057
|
+
share,
|
|
1058
|
+
cache !== undefined ? formatPercent(cache, 1) : '-',
|
|
1059
|
+
];
|
|
1060
|
+
if (options?.showCost !== false) {
|
|
1061
|
+
cells.push(provider.apiCost > 0 ? formatApiCostValue(provider.apiCost) : '-');
|
|
1062
|
+
}
|
|
1063
|
+
return `| ${cells.join(' | ')} |`;
|
|
1064
|
+
}),
|
|
1065
|
+
];
|
|
1066
|
+
}
|
|
1067
|
+
function renderHistoryQuotaSnapshot(quotas) {
|
|
1068
|
+
const visible = toolVisibleQuotaSnapshots(quotas).slice(0, 5);
|
|
1069
|
+
if (visible.length === 0)
|
|
1070
|
+
return ['- no provider quota data available'];
|
|
1071
|
+
return visible.map((quota) => {
|
|
1072
|
+
const label = quotaDisplayLabel(quota);
|
|
1073
|
+
if (quota.status === 'error') {
|
|
1074
|
+
return `- ${label}: error${quota.note ? ` | ${quota.note}` : ''}`;
|
|
1075
|
+
}
|
|
1076
|
+
if (quota.windows && quota.windows.length > 0) {
|
|
1077
|
+
const summary = quota.windows
|
|
1078
|
+
.slice(0, 2)
|
|
1079
|
+
.map((window) => {
|
|
1080
|
+
const remaining = window.showPercent === false
|
|
1081
|
+
? undefined
|
|
1082
|
+
: formatQuotaPercent(window.remainingPercent);
|
|
1083
|
+
const reset = reportResetLine(window.resetAt, window.resetLabel, window.label);
|
|
1084
|
+
return [window.label || 'Quota', remaining, `reset ${reset}`]
|
|
1085
|
+
.filter(Boolean)
|
|
1086
|
+
.join(' | ');
|
|
1087
|
+
})
|
|
1088
|
+
.join('; ');
|
|
1089
|
+
return `- ${label}: ${summary}`;
|
|
1090
|
+
}
|
|
1091
|
+
if (quota.balance) {
|
|
1092
|
+
return `- ${label}: balance ${formatCurrency(quota.balance.amount, quota.balance.currency)}`;
|
|
1093
|
+
}
|
|
1094
|
+
return `- ${label}: ${formatQuotaPercent(quota.remainingPercent)} | reset ${reportResetLine(quota.resetAt)}`;
|
|
1095
|
+
});
|
|
1096
|
+
}
|
|
1097
|
+
function renderHistoryPeriodDetailRows(result, options) {
|
|
1098
|
+
const showCost = options?.showCost !== false;
|
|
1099
|
+
return result.rows.length
|
|
1100
|
+
? result.rows.map((row) => {
|
|
1101
|
+
const cache = getCacheCoverageMetrics(row.usage).cachedRatio;
|
|
1102
|
+
const cells = [
|
|
1103
|
+
`${row.range.label}${row.range.isCurrent ? '*' : ''}`,
|
|
1104
|
+
shortNumber(row.usage.assistantMessages),
|
|
1105
|
+
shortNumber(row.usage.input),
|
|
1106
|
+
shortNumber(row.usage.output),
|
|
1107
|
+
shortNumber(row.usage.cacheRead + row.usage.cacheWrite),
|
|
1108
|
+
cache !== undefined ? formatPercent(cache, 1) : '-',
|
|
1109
|
+
shortNumber(row.usage.total),
|
|
1110
|
+
];
|
|
1111
|
+
if (showCost)
|
|
1112
|
+
cells.push(formatApiCostValue(row.usage.apiCost));
|
|
1113
|
+
return `| ${cells.join(' | ')} |`;
|
|
1114
|
+
})
|
|
1115
|
+
: [
|
|
1116
|
+
showCost
|
|
1117
|
+
? '| - | - | - | - | - | - | - | - |'
|
|
1118
|
+
: '| - | - | - | - | - | - | - |',
|
|
1119
|
+
];
|
|
1120
|
+
}
|
|
1121
|
+
export function renderHistoryMarkdownReport(result, quotas, options) {
|
|
1122
|
+
const showCost = options?.showCost !== false;
|
|
1123
|
+
const detailRows = renderHistoryPeriodDetailRows(result, { showCost });
|
|
1124
|
+
return [
|
|
1125
|
+
`## Quota History - ${historyPeriodLabel(result.period)} since ${result.since.raw}`,
|
|
1126
|
+
...(result.warning
|
|
1127
|
+
? ['', `> Warning: ${sanitizeLine(result.warning)}`]
|
|
1128
|
+
: []),
|
|
1129
|
+
'',
|
|
1130
|
+
'### Quota Status',
|
|
1131
|
+
'',
|
|
1132
|
+
...renderHistoryQuotaSnapshot(quotas),
|
|
1133
|
+
'',
|
|
1134
|
+
'### Totals',
|
|
1135
|
+
'',
|
|
1136
|
+
...renderHistoryTotalsTable(result, { showCost }),
|
|
1137
|
+
'',
|
|
1138
|
+
'### Provider Breakdown',
|
|
1139
|
+
'',
|
|
1140
|
+
...renderHistoryProviderBreakdown(result, { showCost }),
|
|
1141
|
+
'',
|
|
1142
|
+
'### Period Detail',
|
|
1143
|
+
'',
|
|
1144
|
+
showCost
|
|
1145
|
+
? '| Period | Requests | Input | Output | Cache | Cache Hit | Total | API Cost |'
|
|
1146
|
+
: '| Period | Requests | Input | Output | Cache | Cache Hit | Total |',
|
|
1147
|
+
showCost
|
|
1148
|
+
? '| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |'
|
|
1149
|
+
: '| --- | ---: | ---: | ---: | ---: | ---: | ---: |',
|
|
1150
|
+
...detailRows,
|
|
1151
|
+
...(result.rows.some((row) => row.range.isCurrent)
|
|
1152
|
+
? ['', '* `*` marks the current partial period.']
|
|
1153
|
+
: []),
|
|
1154
|
+
].join('\n');
|
|
1155
|
+
}
|
|
820
1156
|
export function renderMarkdownReport(period, usage, quotas, options) {
|
|
821
1157
|
const showCost = options?.showCost !== false;
|
|
822
1158
|
const cacheMetrics = getCacheCoverageMetrics(usage);
|
|
@@ -926,19 +1262,21 @@ export function renderMarkdownReport(period, usage, quotas, options) {
|
|
|
926
1262
|
: '| --- | ---: | ---: | ---: | ---: | ---: |';
|
|
927
1263
|
const quotaLines = toolVisibleQuotaSnapshots(quotas).flatMap((quota) => {
|
|
928
1264
|
const displayLabel = quotaDisplayLabel(quota);
|
|
1265
|
+
const staleSuffix = quota.stale ? ' | stale' : '';
|
|
929
1266
|
// Multi-window detail
|
|
930
1267
|
if (quota.windows && quota.windows.length > 0 && quota.status === 'ok') {
|
|
931
1268
|
const windowLines = quota.windows.map((win) => {
|
|
932
1269
|
const extraNote = win.note || (win === quota.windows?.[0] && quota.note)
|
|
933
1270
|
? ` | ${win.note || quota.note}`
|
|
934
1271
|
: '';
|
|
1272
|
+
const staleNote = quota.stale && win === quota.windows?.[0] ? staleSuffix : '';
|
|
935
1273
|
if (win.showPercent === false) {
|
|
936
1274
|
const winLabel = win.label ? ` (${win.label})` : '';
|
|
937
|
-
return mdCell(`- ${displayLabel}${winLabel}: ${quota.status} | reset ${reportResetLine(win.resetAt, win.resetLabel, win.label)}${extraNote}`);
|
|
1275
|
+
return mdCell(`- ${displayLabel}${winLabel}: ${quota.status} | reset ${reportResetLine(win.resetAt, win.resetLabel, win.label)}${extraNote}${staleNote}`);
|
|
938
1276
|
}
|
|
939
1277
|
const remaining = formatQuotaPercent(win.remainingPercent);
|
|
940
1278
|
const winLabel = win.label ? ` (${win.label})` : '';
|
|
941
|
-
return mdCell(`- ${displayLabel}${winLabel}: ${quota.status} | remaining ${remaining} | reset ${reportResetLine(win.resetAt, win.resetLabel, win.label)}${extraNote}`);
|
|
1279
|
+
return mdCell(`- ${displayLabel}${winLabel}: ${quota.status} | remaining ${remaining} | reset ${reportResetLine(win.resetAt, win.resetLabel, win.label)}${extraNote}${staleNote}`);
|
|
942
1280
|
});
|
|
943
1281
|
if (quota.balance) {
|
|
944
1282
|
windowLines.push(mdCell(`- ${displayLabel}: ${quota.status} | balance ${formatCurrency(quota.balance.amount, quota.balance.currency)}`));
|
|
@@ -947,7 +1285,7 @@ export function renderMarkdownReport(period, usage, quotas, options) {
|
|
|
947
1285
|
}
|
|
948
1286
|
if (quota.status === 'ok' && quota.balance) {
|
|
949
1287
|
return [
|
|
950
|
-
mdCell(`- ${displayLabel}: ${quota.status} | balance ${formatCurrency(quota.balance.amount, quota.balance.currency)}`),
|
|
1288
|
+
mdCell(`- ${displayLabel}: ${quota.status} | balance ${formatCurrency(quota.balance.amount, quota.balance.currency)}${staleSuffix}`),
|
|
951
1289
|
];
|
|
952
1290
|
}
|
|
953
1291
|
if (quota.status === 'error') {
|
|
@@ -957,12 +1295,20 @@ export function renderMarkdownReport(period, usage, quotas, options) {
|
|
|
957
1295
|
}
|
|
958
1296
|
const remaining = formatQuotaPercent(quota.remainingPercent);
|
|
959
1297
|
return [
|
|
960
|
-
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}`),
|
|
961
1299
|
];
|
|
962
1300
|
});
|
|
963
1301
|
return [
|
|
964
1302
|
`## Quota Report - ${periodLabel(period)}`,
|
|
965
1303
|
'',
|
|
1304
|
+
'### Quota Status',
|
|
1305
|
+
'',
|
|
1306
|
+
...(quotaLines.length
|
|
1307
|
+
? quotaLines
|
|
1308
|
+
: ['- no provider quota data available']),
|
|
1309
|
+
'',
|
|
1310
|
+
'### Usage Summary',
|
|
1311
|
+
'',
|
|
966
1312
|
`- Sessions: ${usage.sessionCount}`,
|
|
967
1313
|
`- Requests: ${usage.assistantMessages}`,
|
|
968
1314
|
`- Tokens: input ${usage.input}, output ${usage.output}, cache_read ${usage.cacheRead}, cache_write ${usage.cacheWrite}, total ${usage.total}`,
|
|
@@ -975,9 +1321,6 @@ export function renderMarkdownReport(period, usage, quotas, options) {
|
|
|
975
1321
|
`- API cost: ${apiCostSummaryValue()}`,
|
|
976
1322
|
]
|
|
977
1323
|
: []),
|
|
978
|
-
...(highlightLines().length > 0
|
|
979
|
-
? ['', '### Highlights', ...highlightLines()]
|
|
980
|
-
: []),
|
|
981
1324
|
'',
|
|
982
1325
|
'### Usage by Provider',
|
|
983
1326
|
'',
|
|
@@ -990,12 +1333,9 @@ export function renderMarkdownReport(period, usage, quotas, options) {
|
|
|
990
1333
|
? '| - | - | - | - | - | - | - | - | - |'
|
|
991
1334
|
: '| - | - | - | - | - | - |',
|
|
992
1335
|
]),
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
...(quotaLines.length
|
|
997
|
-
? quotaLines
|
|
998
|
-
: ['- no provider quota data available']),
|
|
1336
|
+
...(highlightLines().length > 0
|
|
1337
|
+
? ['', '### Highlights', ...highlightLines()]
|
|
1338
|
+
: []),
|
|
999
1339
|
].join('\n');
|
|
1000
1340
|
}
|
|
1001
1341
|
export function renderToastMessage(period, usage, quotas, options) {
|
|
@@ -1030,6 +1370,22 @@ export function renderToastMessage(period, usage, quotas, options) {
|
|
|
1030
1370
|
});
|
|
1031
1371
|
}
|
|
1032
1372
|
lines.push(...alignPairs(tokenPairs).map((line) => fitLine(line, width)));
|
|
1373
|
+
const providerCachePairs = Object.values(usage.providers)
|
|
1374
|
+
.map((provider) => {
|
|
1375
|
+
const metrics = getProviderCacheCoverageMetrics(provider);
|
|
1376
|
+
if (metrics.cachedRatio === undefined)
|
|
1377
|
+
return undefined;
|
|
1378
|
+
return {
|
|
1379
|
+
label: displayShortLabel(provider.providerID),
|
|
1380
|
+
value: `Cached ${formatPercent(metrics.cachedRatio, 1)}`,
|
|
1381
|
+
};
|
|
1382
|
+
})
|
|
1383
|
+
.filter((item) => Boolean(item));
|
|
1384
|
+
if (providerCachePairs.length > 0) {
|
|
1385
|
+
lines.push('');
|
|
1386
|
+
lines.push(fitLine('Provider Cache', width));
|
|
1387
|
+
lines.push(...alignPairs(providerCachePairs).map((line) => fitLine(line, width)));
|
|
1388
|
+
}
|
|
1033
1389
|
if (showCost) {
|
|
1034
1390
|
const costPairs = Object.values(usage.providers)
|
|
1035
1391
|
.filter((provider) => canonicalProviderID(provider.providerID) !== 'github-copilot')
|
|
@@ -1055,22 +1411,6 @@ export function renderToastMessage(period, usage, quotas, options) {
|
|
|
1055
1411
|
: ' -', width));
|
|
1056
1412
|
}
|
|
1057
1413
|
}
|
|
1058
|
-
const providerCachePairs = Object.values(usage.providers)
|
|
1059
|
-
.map((provider) => {
|
|
1060
|
-
const metrics = getProviderCacheCoverageMetrics(provider);
|
|
1061
|
-
if (metrics.cachedRatio === undefined)
|
|
1062
|
-
return undefined;
|
|
1063
|
-
return {
|
|
1064
|
-
label: displayShortLabel(provider.providerID),
|
|
1065
|
-
value: `Cached ${formatPercent(metrics.cachedRatio, 1)}`,
|
|
1066
|
-
};
|
|
1067
|
-
})
|
|
1068
|
-
.filter((item) => Boolean(item));
|
|
1069
|
-
if (providerCachePairs.length > 0) {
|
|
1070
|
-
lines.push('');
|
|
1071
|
-
lines.push(fitLine('Provider Cache', width));
|
|
1072
|
-
lines.push(...alignPairs(providerCachePairs).map((line) => fitLine(line, width)));
|
|
1073
|
-
}
|
|
1074
1414
|
const quotaPairs = toolVisibleQuotaSnapshots(quotas).flatMap((item) => {
|
|
1075
1415
|
if (item.status === 'ok') {
|
|
1076
1416
|
if (item.windows && item.windows.length > 0) {
|
|
@@ -1085,6 +1425,8 @@ export function renderToastMessage(period, usage, quotas, options) {
|
|
|
1085
1425
|
parts.push(`${win.resetLabel || 'Rst'} ${reset}`);
|
|
1086
1426
|
if (win.note)
|
|
1087
1427
|
parts.push(win.note);
|
|
1428
|
+
if (item.stale && idx === 0)
|
|
1429
|
+
parts.push('stale');
|
|
1088
1430
|
return {
|
|
1089
1431
|
label: idx === 0 ? quotaDisplayLabel(item) : '',
|
|
1090
1432
|
value: parts.filter(Boolean).join(' '),
|
|
@@ -1102,7 +1444,7 @@ export function renderToastMessage(period, usage, quotas, options) {
|
|
|
1102
1444
|
return [
|
|
1103
1445
|
{
|
|
1104
1446
|
label: quotaDisplayLabel(item),
|
|
1105
|
-
value: `Balance ${formatCurrency(item.balance.amount, item.balance.currency)}`,
|
|
1447
|
+
value: `Balance ${formatCurrency(item.balance.amount, item.balance.currency)}${item.stale ? ' stale' : ''}`,
|
|
1106
1448
|
},
|
|
1107
1449
|
];
|
|
1108
1450
|
}
|
|
@@ -1111,7 +1453,7 @@ export function renderToastMessage(period, usage, quotas, options) {
|
|
|
1111
1453
|
return [
|
|
1112
1454
|
{
|
|
1113
1455
|
label: quotaDisplayLabel(item),
|
|
1114
|
-
value: `Remaining ${percent}${reset ? ` Rst ${reset}` : ''}`,
|
|
1456
|
+
value: `Remaining ${percent}${reset ? ` Rst ${reset}` : ''}${item.stale ? ' stale' : ''}`,
|
|
1115
1457
|
},
|
|
1116
1458
|
];
|
|
1117
1459
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Message } from '@opencode-ai/sdk';
|
|
2
|
+
export type MessageEntry = {
|
|
3
|
+
info: Message;
|
|
4
|
+
};
|
|
5
|
+
export declare function decodeMessageInfo(value: unknown): Message | undefined;
|
|
6
|
+
export declare function decodeMessageEntries(value: unknown): MessageEntry[] | undefined;
|
|
7
|
+
export declare function nextCursorFromResponse(value: unknown): string | undefined;
|
|
8
|
+
export declare function isMissingSessionError(error: unknown): boolean;
|