@narumitw/pi-usage 0.64.3 → 0.65.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -4
- package/dist/chunks/{chunk-ACPQJ2CM.ts → chunk-N2ZD5X4K.ts} +4 -4
- package/dist/chunks/{chunk-ACPQJ2CM.ts.map → chunk-N2ZD5X4K.ts.map} +2 -2
- package/dist/chunks/{openai-companion-auth-FGFC6DBT.ts → openai-companion-auth-QIKCD3WA.ts} +2 -2
- package/dist/chunks/{openai-companion-usage-TGHK2OQR.ts → openai-companion-usage-ZFAHCGEY.ts} +2 -2
- package/dist/index.ts +439 -149
- package/dist/index.ts.map +4 -4
- package/docs/providers.md +7 -7
- package/package.json +1 -1
- package/src/codex-fast-catalog.ts +272 -0
- package/src/codex-fast-runtime.ts +107 -26
- package/src/codex-fast.ts +28 -19
- package/src/format.ts +96 -34
- package/src/index.ts +3 -1
- package/src/query.ts +2 -1
- package/src/usage.ts +45 -22
- /package/dist/chunks/{openai-companion-auth-FGFC6DBT.ts.map → openai-companion-auth-QIKCD3WA.ts.map} +0 -0
- /package/dist/chunks/{openai-companion-usage-TGHK2OQR.ts.map → openai-companion-usage-ZFAHCGEY.ts.map} +0 -0
package/src/format.ts
CHANGED
|
@@ -60,6 +60,36 @@ export function formatUsageStatusline(
|
|
|
60
60
|
now = Date.now(),
|
|
61
61
|
showCodexResetCountdown = true,
|
|
62
62
|
codexStatusPercentage: CodexStatusPercentage = "remaining",
|
|
63
|
+
): string | undefined {
|
|
64
|
+
return formatUsageStatuslineWithCountdown(report, model, now, showCodexResetCountdown, codexStatusPercentage).value;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface StatusCountdown {
|
|
68
|
+
now: number;
|
|
69
|
+
active: boolean;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Record new-provider eligibility while formatting the selected windows so repaint cannot drift from display policy.
|
|
73
|
+
// Codex and ChatGPT companion keep their existing repaint policy in usage.ts.
|
|
74
|
+
export function formatUsageStatuslineWithCountdown(
|
|
75
|
+
report: UsageReport,
|
|
76
|
+
model?: UsageModel,
|
|
77
|
+
now = Date.now(),
|
|
78
|
+
showCodexResetCountdown = true,
|
|
79
|
+
codexStatusPercentage: CodexStatusPercentage = "remaining",
|
|
80
|
+
): { value: string | undefined; hasCountdown: boolean } {
|
|
81
|
+
const countdown: StatusCountdown = { now, active: false };
|
|
82
|
+
const value = formatStatusline(report, model, now, showCodexResetCountdown, codexStatusPercentage, countdown);
|
|
83
|
+
return { value, hasCountdown: countdown.active };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function formatStatusline(
|
|
87
|
+
report: UsageReport,
|
|
88
|
+
model: UsageModel | undefined,
|
|
89
|
+
now: number,
|
|
90
|
+
showCodexResetCountdown: boolean,
|
|
91
|
+
codexStatusPercentage: CodexStatusPercentage,
|
|
92
|
+
countdown: StatusCountdown,
|
|
63
93
|
): string | undefined {
|
|
64
94
|
if (report.providerId === "openai" && report.source === "openai-chatgpt-auth") return "chatgpt usage: web only";
|
|
65
95
|
if (report.source === "openai-chatgpt-companion") {
|
|
@@ -79,24 +109,24 @@ export function formatUsageStatusline(
|
|
|
79
109
|
if (report.providerId === "deepseek") return formatDeepSeekStatusline(report);
|
|
80
110
|
if (report.providerId === "fireworks") return formatFireworksStatusline(report);
|
|
81
111
|
if (report.providerId === "vercel-ai-gateway") return formatVercelAIGatewayStatusline(report);
|
|
82
|
-
if (report.providerId === "github-copilot") return formatGitHubCopilotStatusline(report);
|
|
112
|
+
if (report.providerId === "github-copilot") return formatGitHubCopilotStatusline(report, countdown);
|
|
83
113
|
if (report.providerId === "openrouter") {
|
|
84
114
|
const limit = report.buckets.find((bucket) => bucket.id === "key-limit");
|
|
85
115
|
if (limit?.remaining !== undefined) return `openrouter ${formatUsd(limit.remaining)} left`;
|
|
86
116
|
const total = report.metrics.find((metric) => metric.id === "usage-total");
|
|
87
117
|
if (typeof total?.value === "number") return `openrouter ${formatUsd(total.value)} used`;
|
|
88
118
|
}
|
|
89
|
-
if (report.providerId === "opencode-go") return formatOpenCodeZenStatusline(report);
|
|
90
|
-
if (report.providerId === "command-code") return formatCommandCodeStatusline(report);
|
|
91
|
-
if (report.providerId === "kimi-coding") return formatKimiCodingStatusline(report);
|
|
119
|
+
if (report.providerId === "opencode-go") return formatOpenCodeZenStatusline(report, countdown);
|
|
120
|
+
if (report.providerId === "command-code") return formatCommandCodeStatusline(report, countdown);
|
|
121
|
+
if (report.providerId === "kimi-coding") return formatKimiCodingStatusline(report, countdown);
|
|
92
122
|
if (report.providerId === "moonshotai" || report.providerId === "moonshotai-cn") {
|
|
93
123
|
return formatMoonshotStatusline(report);
|
|
94
124
|
}
|
|
95
125
|
if (report.providerId === "minimax" || report.providerId === "minimax-cn") {
|
|
96
|
-
return formatMiniMaxStatusline(report, model);
|
|
126
|
+
return formatMiniMaxStatusline(report, countdown, model);
|
|
97
127
|
}
|
|
98
128
|
if (report.providerId === "zai" || report.providerId === "zai-coding-cn") {
|
|
99
|
-
return formatZaiStatusline(report);
|
|
129
|
+
return formatZaiStatusline(report, countdown);
|
|
100
130
|
}
|
|
101
131
|
return undefined;
|
|
102
132
|
}
|
|
@@ -232,7 +262,7 @@ function formatGitHubCopilotReport(lines: string[], report: UsageReport): void {
|
|
|
232
262
|
}
|
|
233
263
|
}
|
|
234
264
|
|
|
235
|
-
function formatGitHubCopilotStatusline(report: UsageReport): string {
|
|
265
|
+
function formatGitHubCopilotStatusline(report: UsageReport, countdown: StatusCountdown): string {
|
|
236
266
|
const quota = findGitHubCopilotQuota(report);
|
|
237
267
|
const kind = compactGitHubCopilotQuotaKind(quota);
|
|
238
268
|
if (!quota || quota.limit === undefined || quota.remaining === undefined) {
|
|
@@ -240,7 +270,8 @@ function formatGitHubCopilotStatusline(report: UsageReport): string {
|
|
|
240
270
|
}
|
|
241
271
|
const overage = report.metrics.find((metric) => metric.id === "overage-used");
|
|
242
272
|
const overageSuffix = typeof overage?.value === "number" && overage.value > 0 ? ` +${overage.value} over` : "";
|
|
243
|
-
|
|
273
|
+
const reset = formatQuotaCountdown(quota, countdown);
|
|
274
|
+
return `copilot ${kind === "premium" ? "" : `${kind} `}${quota.remaining}/${quota.limit} ${percentRemaining(quota)}%${overageSuffix}${reset ? ` ↻ ${reset}` : ""}`;
|
|
244
275
|
}
|
|
245
276
|
|
|
246
277
|
function findGitHubCopilotQuota(report: UsageReport): UsageBucket | undefined {
|
|
@@ -285,14 +316,14 @@ function formatOpenCodeZenReport(lines: string[], report: UsageReport): void {
|
|
|
285
316
|
}
|
|
286
317
|
}
|
|
287
318
|
|
|
288
|
-
function formatOpenCodeZenStatusline(report: UsageReport): string | undefined {
|
|
289
|
-
const parts = [
|
|
319
|
+
function formatOpenCodeZenStatusline(report: UsageReport, countdown: StatusCountdown): string | undefined {
|
|
320
|
+
const parts: string[] = [];
|
|
290
321
|
for (const bucket of report.buckets) {
|
|
291
322
|
if (bucket.used === undefined) continue;
|
|
292
323
|
const compact = bucket.id === "rolling" ? "r" : bucket.id === "weekly" ? "w" : "m";
|
|
293
|
-
parts.push(`${clampPercent(bucket.used).toFixed(0)}
|
|
324
|
+
parts.push(formatQuotaWindow(bucket, `${clampPercent(bucket.used).toFixed(0)}%`, compact, countdown));
|
|
294
325
|
}
|
|
295
|
-
return parts.length >
|
|
326
|
+
return parts.length > 0 ? `zen ${parts.join(" · ")}` : undefined;
|
|
296
327
|
}
|
|
297
328
|
|
|
298
329
|
function formatCommandCodeReport(lines: string[], report: UsageReport): void {
|
|
@@ -311,8 +342,8 @@ function formatCommandCodeReport(lines: string[], report: UsageReport): void {
|
|
|
311
342
|
}
|
|
312
343
|
}
|
|
313
344
|
|
|
314
|
-
function formatCommandCodeStatusline(report: UsageReport): string | undefined {
|
|
315
|
-
const parts = [
|
|
345
|
+
function formatCommandCodeStatusline(report: UsageReport, countdown: StatusCountdown): string | undefined {
|
|
346
|
+
const parts: string[] = [];
|
|
316
347
|
for (const [id, label] of [
|
|
317
348
|
["five-hour", "5h"],
|
|
318
349
|
["weekly", "wk"],
|
|
@@ -320,9 +351,9 @@ function formatCommandCodeStatusline(report: UsageReport): string | undefined {
|
|
|
320
351
|
] as const) {
|
|
321
352
|
const bucket = report.buckets.find((candidate) => candidate.id === id);
|
|
322
353
|
if (bucket?.unit !== "usd" || bucket.limit === undefined || bucket.remaining === undefined) continue;
|
|
323
|
-
parts.push(`${percentRemaining(bucket)}
|
|
354
|
+
parts.push(formatQuotaWindow(bucket, `${percentRemaining(bucket)}%`, label, countdown));
|
|
324
355
|
}
|
|
325
|
-
return parts.length >
|
|
356
|
+
return parts.length > 0 ? `cmd ${parts.join(" · ")}` : undefined;
|
|
326
357
|
}
|
|
327
358
|
|
|
328
359
|
function formatKimiCodingReport(lines: string[], report: UsageReport): void {
|
|
@@ -363,7 +394,7 @@ function formatKimiCodingReport(lines: string[], report: UsageReport): void {
|
|
|
363
394
|
}
|
|
364
395
|
}
|
|
365
396
|
|
|
366
|
-
function formatKimiCodingStatusline(report: UsageReport): string | undefined {
|
|
397
|
+
function formatKimiCodingStatusline(report: UsageReport, countdown: StatusCountdown): string | undefined {
|
|
367
398
|
const fiveHour = report.buckets.find((bucket) => bucket.id === "five-hour");
|
|
368
399
|
const weekly = report.buckets.find((bucket) => bucket.id === "weekly");
|
|
369
400
|
const monthly = report.buckets.find((bucket) => bucket.id === "monthly");
|
|
@@ -371,15 +402,15 @@ function formatKimiCodingStatusline(report: UsageReport): string | undefined {
|
|
|
371
402
|
const selected = [subWindow, weekly, monthly].filter(
|
|
372
403
|
(bucket, index, buckets): bucket is UsageBucket => bucket !== undefined && buckets.indexOf(bucket) === index,
|
|
373
404
|
);
|
|
374
|
-
const parts = [
|
|
405
|
+
const parts: string[] = [];
|
|
375
406
|
for (const bucket of selected) {
|
|
376
407
|
if (bucket.remaining === undefined || (bucket.unit !== "percent" && !bucket.limit)) continue;
|
|
377
408
|
const fallback = bucket.id === "weekly" ? "weekly" : "5h";
|
|
378
409
|
const window = bucket.id === "monthly" ? "mo" : formatWindowLabel(bucket.windowMinutes, fallback, true);
|
|
379
410
|
const remaining = bucket.unit === "percent" ? Math.round(clampPercent(bucket.remaining)) : percentRemaining(bucket);
|
|
380
|
-
parts.push(`${remaining}
|
|
411
|
+
parts.push(formatQuotaWindow(bucket, `${remaining}%`, window, countdown));
|
|
381
412
|
}
|
|
382
|
-
return parts.length >
|
|
413
|
+
return parts.length > 0 ? `kimi ${parts.join(" · ")}` : undefined;
|
|
383
414
|
}
|
|
384
415
|
|
|
385
416
|
function formatMoonshotReport(lines: string[], report: UsageReport): void {
|
|
@@ -418,7 +449,11 @@ function formatMiniMaxReport(lines: string[], report: UsageReport): void {
|
|
|
418
449
|
}
|
|
419
450
|
}
|
|
420
451
|
|
|
421
|
-
function formatMiniMaxStatusline(
|
|
452
|
+
function formatMiniMaxStatusline(
|
|
453
|
+
report: UsageReport,
|
|
454
|
+
countdown: StatusCountdown,
|
|
455
|
+
model?: UsageModel,
|
|
456
|
+
): string | undefined {
|
|
422
457
|
const prefix = report.providerId === "minimax-cn" ? "minimax cn" : "minimax";
|
|
423
458
|
if (report.source === "minimax-account-balance") {
|
|
424
459
|
const available = report.metrics.find((metric) => metric.id === "available-balance");
|
|
@@ -427,22 +462,22 @@ function formatMiniMaxStatusline(report: UsageReport, model?: UsageModel): strin
|
|
|
427
462
|
const selectedGroup = selectMiniMaxGroup(report, model);
|
|
428
463
|
if (!selectedGroup) return undefined;
|
|
429
464
|
const selected = report.buckets.filter((bucket) => bucket.groupId === selectedGroup);
|
|
430
|
-
const parts = [
|
|
465
|
+
const parts: string[] = [];
|
|
431
466
|
for (const bucket of selected) {
|
|
432
467
|
const fallback = bucket.id.endsWith(":weekly") ? "weekly" : "5h";
|
|
433
468
|
const window = formatWindowLabel(bucket.windowMinutes, fallback, true);
|
|
434
469
|
if (bucket.period === "unlimited") {
|
|
435
|
-
parts.push(
|
|
470
|
+
parts.push(`${window}:unlimited`);
|
|
436
471
|
continue;
|
|
437
472
|
}
|
|
438
473
|
if (bucket.unit === "percent" && bucket.remaining !== undefined) {
|
|
439
|
-
parts.push(`${bucket.remaining}
|
|
474
|
+
parts.push(formatQuotaWindow(bucket, `${bucket.remaining}%`, window, countdown));
|
|
440
475
|
continue;
|
|
441
476
|
}
|
|
442
477
|
if (bucket.limit === undefined || bucket.remaining === undefined) continue;
|
|
443
|
-
parts.push(`${percentRemaining(bucket)}
|
|
478
|
+
parts.push(formatQuotaWindow(bucket, `${percentRemaining(bucket)}%`, window, countdown));
|
|
444
479
|
}
|
|
445
|
-
return parts.length >
|
|
480
|
+
return parts.length > 0 ? `${prefix} ${parts.join(" · ")}` : undefined;
|
|
446
481
|
}
|
|
447
482
|
|
|
448
483
|
function selectMiniMaxGroup(report: UsageReport, model?: UsageModel): string | undefined {
|
|
@@ -493,18 +528,25 @@ function wildcardKeyMatches(pattern: string, value: string): boolean {
|
|
|
493
528
|
return pattern.endsWith("*") || (last !== undefined && value.endsWith(last));
|
|
494
529
|
}
|
|
495
530
|
|
|
496
|
-
function formatZaiStatusline(report: UsageReport): string | undefined {
|
|
531
|
+
function formatZaiStatusline(report: UsageReport, countdown: StatusCountdown): string | undefined {
|
|
497
532
|
const selected = [
|
|
498
533
|
report.buckets.find((bucket) => bucket.id === "five-hour"),
|
|
499
534
|
report.buckets.find((bucket) => bucket.id === "weekly"),
|
|
500
535
|
];
|
|
501
|
-
const parts = [
|
|
536
|
+
const parts: string[] = [];
|
|
502
537
|
for (const bucket of selected) {
|
|
503
538
|
if (!bucket?.limit || bucket.remaining === undefined) continue;
|
|
504
539
|
const fallback = bucket.id === "weekly" ? "weekly" : "5h";
|
|
505
|
-
parts.push(
|
|
540
|
+
parts.push(
|
|
541
|
+
formatQuotaWindow(
|
|
542
|
+
bucket,
|
|
543
|
+
`${percentRemaining(bucket)}%`,
|
|
544
|
+
formatWindowLabel(bucket.windowMinutes, fallback, true),
|
|
545
|
+
countdown,
|
|
546
|
+
),
|
|
547
|
+
);
|
|
506
548
|
}
|
|
507
|
-
return parts.length >
|
|
549
|
+
return parts.length > 0 ? `zai ${parts.join(" · ")}` : undefined;
|
|
508
550
|
}
|
|
509
551
|
|
|
510
552
|
function formatCurrencyMetric(metric: UsageReport["metrics"][number]): string {
|
|
@@ -604,7 +646,8 @@ function formatCodexStatusline(
|
|
|
604
646
|
if (!group) return formatCodexCreditsStatus(report);
|
|
605
647
|
const buckets = report.buckets.filter((bucket) => (bucket.groupId ?? bucket.id) === group);
|
|
606
648
|
const labelBucket = buckets[0];
|
|
607
|
-
const
|
|
649
|
+
const prefix = group === "codex" ? "codex" : `codex ${compactLimitLabel(labelBucket?.groupLabel ?? group)}`;
|
|
650
|
+
const parts: string[] = [];
|
|
608
651
|
for (const bucket of buckets) {
|
|
609
652
|
if (bucket.remaining === undefined) continue;
|
|
610
653
|
const displayedPercentage = percentage === "used" ? (bucket.used ?? 100 - bucket.remaining) : bucket.remaining;
|
|
@@ -612,13 +655,13 @@ function formatCodexStatusline(
|
|
|
612
655
|
const fallback = bucket.id.endsWith(":secondary") ? "weekly" : "5h";
|
|
613
656
|
const window = formatWindowLabel(bucket.windowMinutes, fallback, true);
|
|
614
657
|
if (!showResetCountdown) {
|
|
615
|
-
parts.push(`${
|
|
658
|
+
parts.push(`${window}:${percent}`);
|
|
616
659
|
continue;
|
|
617
660
|
}
|
|
618
661
|
const reset = formatResetCountdown(bucket.resetsAt, now);
|
|
619
|
-
parts.push(`${percent}
|
|
662
|
+
parts.push(reset ? `${percent} ↻ ${reset}` : `${window}:${percent}`);
|
|
620
663
|
}
|
|
621
|
-
return parts.length >
|
|
664
|
+
return parts.length > 0 ? `${prefix} ${parts.join(" · ")}` : formatCodexCreditsStatus(report);
|
|
622
665
|
}
|
|
623
666
|
|
|
624
667
|
function formatCodexCreditsStatus(report: UsageReport): string {
|
|
@@ -711,6 +754,25 @@ function formatWindowLabel(minutes: number | undefined, fallback: "5h" | "weekly
|
|
|
711
754
|
return `${minutes}m`;
|
|
712
755
|
}
|
|
713
756
|
|
|
757
|
+
function formatQuotaWindow(bucket: UsageBucket, percent: string, window: string, countdown: StatusCountdown): string {
|
|
758
|
+
const reset = formatQuotaCountdown(bucket, countdown);
|
|
759
|
+
return reset ? `${percent} ↻ ${reset}` : `${window}:${percent}`;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
function formatQuotaCountdown(bucket: UsageBucket, countdown: StatusCountdown): string | undefined {
|
|
763
|
+
if (
|
|
764
|
+
bucket.period === "unlimited" ||
|
|
765
|
+
bucket.resetsAt === undefined ||
|
|
766
|
+
!Number.isFinite(bucket.resetsAt * 1_000) ||
|
|
767
|
+
!Number.isFinite(countdown.now) ||
|
|
768
|
+
bucket.resetsAt * 1_000 <= countdown.now
|
|
769
|
+
)
|
|
770
|
+
return undefined;
|
|
771
|
+
const reset = formatResetCountdown(bucket.resetsAt, countdown.now);
|
|
772
|
+
if (reset !== undefined) countdown.active = true;
|
|
773
|
+
return reset;
|
|
774
|
+
}
|
|
775
|
+
|
|
714
776
|
function formatResetCountdown(resetsAt: number | undefined, now: number): string | undefined {
|
|
715
777
|
if (resetsAt === undefined || !Number.isFinite(resetsAt) || !Number.isFinite(now)) return undefined;
|
|
716
778
|
const totalMinutes = Math.max(0, Math.ceil((resetsAt * 1_000 - now) / 60_000));
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
export type { CodexFastAvailability, CodexFastCapability } from "./codex-fast.js";
|
|
1
2
|
export {
|
|
2
|
-
CODEX_FAST_MODEL_IDS,
|
|
3
3
|
CODEX_FAST_SERVICE_TIER,
|
|
4
4
|
CODEX_STANDARD_SERVICE_TIER,
|
|
5
5
|
codexFastAvailability,
|
|
@@ -7,7 +7,9 @@ export {
|
|
|
7
7
|
codexFastRequestTier,
|
|
8
8
|
codexFastStatusLabel,
|
|
9
9
|
correctCodexFastMessageCost,
|
|
10
|
+
isOfficialCodexModel,
|
|
10
11
|
rewriteCodexFastPayload,
|
|
12
|
+
UNKNOWN_CODEX_FAST_CAPABILITY,
|
|
11
13
|
} from "./codex-fast.js";
|
|
12
14
|
export type {
|
|
13
15
|
CodexResetAvailability,
|
package/src/query.ts
CHANGED
|
@@ -679,6 +679,7 @@ export async function fetchProviderJson(
|
|
|
679
679
|
body?: Record<string, unknown>;
|
|
680
680
|
redirect?: RequestRedirect;
|
|
681
681
|
userAgent?: boolean;
|
|
682
|
+
maxSuccessBodyBytes?: number;
|
|
682
683
|
responseError?: (status: number, text: string) => string | undefined;
|
|
683
684
|
} = {},
|
|
684
685
|
): Promise<Record<string, unknown>> {
|
|
@@ -710,7 +711,7 @@ export async function fetchProviderJson(
|
|
|
710
711
|
if (controller.signal.aborted) throw Object.assign(new Error("Usage query aborted."), { name: "AbortError" });
|
|
711
712
|
const text = await readBoundedResponse(
|
|
712
713
|
response,
|
|
713
|
-
response.ok ? MAX_SUCCESS_BODY_BYTES : MAX_ERROR_BODY_BYTES,
|
|
714
|
+
response.ok ? (request.maxSuccessBodyBytes ?? MAX_SUCCESS_BODY_BYTES) : MAX_ERROR_BODY_BYTES,
|
|
714
715
|
!response.ok,
|
|
715
716
|
description,
|
|
716
717
|
controller.signal,
|
package/src/usage.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// account cache, cancellation, and session lifecycle share one consistency boundary.
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import type { ExtensionAPI, ExtensionCommandContext, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
5
|
+
import { isOfficialCodexModel } from "./codex-fast.js";
|
|
5
6
|
import { FAST_USAGE_WARNING, registerCodexFastMode } from "./codex-fast-runtime.js";
|
|
6
7
|
import {
|
|
7
8
|
type CodexResetAvailability,
|
|
@@ -19,7 +20,7 @@ import {
|
|
|
19
20
|
resolveCodexResetAuth,
|
|
20
21
|
} from "./codex-resets.js";
|
|
21
22
|
import { abortError, awaitWithDeadline, errorMessage, runWithConcurrency, UsageCache } from "./core.js";
|
|
22
|
-
import { formatProviderStates,
|
|
23
|
+
import { formatProviderStates, formatUsageStatuslineWithCountdown } from "./format.js";
|
|
23
24
|
import { createOAuthCredentialCandidateReader } from "./oauth-credential-source.js";
|
|
24
25
|
import { UnsupportedOpenAIUsageAuthError } from "./providers/openai-chatgpt.js";
|
|
25
26
|
import {
|
|
@@ -180,31 +181,33 @@ export default function usageExtension(pi: ExtensionAPI, dependencies: UsageExte
|
|
|
180
181
|
outcome.state.report.providerId === "openai-codex" && settings.codexStatusResetCountdown;
|
|
181
182
|
const isCompanionReport = outcome.state.report.source === "openai-chatgpt-companion";
|
|
182
183
|
const now = Date.now();
|
|
183
|
-
const
|
|
184
|
+
const status = formatUsageStatuslineWithCountdown(
|
|
184
185
|
outcome.state.report,
|
|
185
186
|
model,
|
|
186
187
|
now,
|
|
187
188
|
showCodexResetCountdown,
|
|
188
189
|
settings.codexStatusPercentage,
|
|
189
190
|
);
|
|
190
|
-
const value =
|
|
191
|
+
const value = status.value ? fastRuntime.decorateStatus(model, status.value) : undefined;
|
|
191
192
|
if (!safeSetStatus(ctx, value)) return;
|
|
192
193
|
if (shouldSchedule && sessionActive) scheduleStatusRefresh(ctx, model);
|
|
193
194
|
if (
|
|
194
195
|
sessionActive &&
|
|
195
|
-
(
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
196
|
+
(status.hasCountdown ||
|
|
197
|
+
((showCodexResetCountdown || isCompanionReport) &&
|
|
198
|
+
outcome.state.report.buckets.some(
|
|
199
|
+
(bucket) =>
|
|
200
|
+
(!isCompanionReport || bucket.groupId === "chatgpt-plan") &&
|
|
201
|
+
bucket.resetsAt !== undefined &&
|
|
202
|
+
Number.isFinite(bucket.resetsAt) &&
|
|
203
|
+
bucket.resetsAt * 1_000 > now,
|
|
204
|
+
)))
|
|
203
205
|
) {
|
|
204
206
|
const generation = statusGeneration;
|
|
205
207
|
statusCountdownTimer = setTimeout(() => {
|
|
206
|
-
|
|
208
|
+
// A queued obsolete callback must not detach its successor's owned timer.
|
|
207
209
|
if (!sessionActive || generation !== statusGeneration) return;
|
|
210
|
+
statusCountdownTimer = undefined;
|
|
208
211
|
publishStatus(ctx, outcome, model, false);
|
|
209
212
|
}, STATUS_COUNTDOWN_REFRESH_MS);
|
|
210
213
|
statusCountdownTimer.unref?.();
|
|
@@ -765,14 +768,27 @@ export default function usageExtension(pi: ExtensionAPI, dependencies: UsageExte
|
|
|
765
768
|
|
|
766
769
|
const showMenu = async (ctx: ExtensionCommandContext): Promise<void> => {
|
|
767
770
|
if (!ctx.hasUI) throw new Error("/usage requires TUI or RPC mode.");
|
|
768
|
-
statusGeneration += 1;
|
|
769
|
-
const menuGeneration = statusGeneration;
|
|
770
|
-
statusController?.abort();
|
|
771
|
-
statusController = undefined;
|
|
772
|
-
clearStatusTimers();
|
|
773
771
|
const controller = new AbortController();
|
|
774
772
|
activeControllers.add(controller);
|
|
775
773
|
try {
|
|
774
|
+
if (isOfficialCodexModel(ctx.model)) {
|
|
775
|
+
const refreshed = await runMenuOperation(
|
|
776
|
+
ctx,
|
|
777
|
+
"Checking Codex Fast capability…",
|
|
778
|
+
controller.signal,
|
|
779
|
+
async (signal) => {
|
|
780
|
+
await fastRuntime.refreshCapabilities(ctx, signal);
|
|
781
|
+
return true;
|
|
782
|
+
},
|
|
783
|
+
);
|
|
784
|
+
if (!refreshed || controller.signal.aborted) return;
|
|
785
|
+
}
|
|
786
|
+
// Capability updates can refresh status; take menu ownership after discovery.
|
|
787
|
+
statusGeneration += 1;
|
|
788
|
+
const menuGeneration = statusGeneration;
|
|
789
|
+
statusController?.abort();
|
|
790
|
+
statusController = undefined;
|
|
791
|
+
clearStatusTimers();
|
|
776
792
|
let stableCurrent = await queryStableCurrent(ctx, false, controller, "Checking current usage…");
|
|
777
793
|
if (!stableCurrent) return;
|
|
778
794
|
publishStableCurrent(ctx, stableCurrent);
|
|
@@ -919,7 +935,9 @@ export default function usageExtension(pi: ExtensionAPI, dependencies: UsageExte
|
|
|
919
935
|
? [`Fast mode: ${fastAvailability.enabled ? "On" : "Off"}`, FAST_USAGE_WARNING]
|
|
920
936
|
: fastAvailability.kind === "unavailable"
|
|
921
937
|
? [`Fast mode: Unavailable · ${fastAvailability.reason}`]
|
|
922
|
-
:
|
|
938
|
+
: fastAvailability.kind === "unknown"
|
|
939
|
+
? [`Fast mode: Unknown · ${fastAvailability.reason}`]
|
|
940
|
+
: [];
|
|
923
941
|
return {
|
|
924
942
|
kind: "actions",
|
|
925
943
|
title: "Provider usage",
|
|
@@ -936,11 +954,12 @@ export default function usageExtension(pi: ExtensionAPI, dependencies: UsageExte
|
|
|
936
954
|
},
|
|
937
955
|
]
|
|
938
956
|
: []),
|
|
939
|
-
...(fastAvailability.kind === "available"
|
|
957
|
+
...(fastAvailability.kind === "available" ||
|
|
958
|
+
(fastState.settings.codexFastMode && isOfficialCodexModel(ctx.model))
|
|
940
959
|
? [
|
|
941
960
|
{
|
|
942
961
|
id: "toggle-fast",
|
|
943
|
-
label:
|
|
962
|
+
label: fastState.settings.codexFastMode ? "Turn Fast mode off" : "Turn Fast mode on",
|
|
944
963
|
description:
|
|
945
964
|
fastState.kind === "invalid"
|
|
946
965
|
? "Repair pi-usage.json and reload before changing Fast mode."
|
|
@@ -1114,10 +1133,11 @@ export default function usageExtension(pi: ExtensionAPI, dependencies: UsageExte
|
|
|
1114
1133
|
},
|
|
1115
1134
|
"toggle-fast": async () => {
|
|
1116
1135
|
const availability = fastRuntime.availability(ctx.model);
|
|
1117
|
-
|
|
1136
|
+
const enabled = settingsRuntime.get().settings.codexFastMode;
|
|
1137
|
+
if ((availability.kind !== "available" && !enabled) || fastState.kind === "invalid") {
|
|
1118
1138
|
return { kind: "rejected" };
|
|
1119
1139
|
}
|
|
1120
|
-
const changed = await fastRuntime.toggle(ctx, !
|
|
1140
|
+
const changed = await fastRuntime.toggle(ctx, !enabled, controller.signal);
|
|
1121
1141
|
if (!changed) return { kind: "rejected" };
|
|
1122
1142
|
fastState = settingsRuntime.get();
|
|
1123
1143
|
return { kind: "stay" };
|
|
@@ -1449,5 +1469,8 @@ export default function usageExtension(pi: ExtensionAPI, dependencies: UsageExte
|
|
|
1449
1469
|
|
|
1450
1470
|
fastRuntime = registerCodexFastMode(pi, settingsRuntime, (ctx) => startStatusRefresh(ctx, ctx.model, false), {
|
|
1451
1471
|
registerSessionStart: false,
|
|
1472
|
+
credentialReader,
|
|
1473
|
+
candidateReader: credentialCandidates,
|
|
1474
|
+
timeoutMs: DEFAULT_TIMEOUT_MS,
|
|
1452
1475
|
});
|
|
1453
1476
|
}
|
/package/dist/chunks/{openai-companion-auth-FGFC6DBT.ts.map → openai-companion-auth-QIKCD3WA.ts.map}
RENAMED
|
File without changes
|
|
File without changes
|