@llblab/pi-codex-usage 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BACKLOG.md +1 -2
- package/CHANGELOG.md +6 -0
- package/README.md +9 -1
- package/index.ts +57 -9
- package/package.json +1 -1
package/BACKLOG.md
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
# Backlog
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
- [ ] `Statusline polish` Tune the successful-refresh blink duration if the current 150ms redraw feels too visible or too subtle in the Pi footer.
|
|
3
|
+
No open work.
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.7.0: Exhausted Quota Visibility
|
|
6
|
+
|
|
7
|
+
- Added exhausted-quota warning background for the statusline quota bar. Impact: when either the 5-hour or weekly Codex window has 0% remaining, the bar keeps its shape but switches from the selected background to the error background for faster visual detection.
|
|
8
|
+
- Added exhausted-primary countdown display for cases where the 5-hour Codex window has 0% remaining and exposes a reset time. Impact: the statusline keeps the normal ten-cell dual bar and appends `<5-hour-reset>/<weekly-reset>` countdowns, so operators can see both how long to wait for short-window recovery and how much weekly quota remains.
|
|
9
|
+
- Fixed quota-bar rounding so a truly empty segment state is shown only when a quota window has 0% remaining, while any positive sub-5% remainder still renders as one visible step. Impact: the statusline now distinguishes exhausted Codex limits from tiny remaining quota without changing normal round-to-nearest behavior elsewhere.
|
|
10
|
+
|
|
5
11
|
## 0.6.0: Telegram Status Integration
|
|
6
12
|
|
|
7
13
|
- Added optional `pi-telegram` status-menu integration through the public Telegram status-line provider API. When `pi-telegram` is available and the active model is an OpenAI Codex subscription model, the `/start` menu status text now includes `codex: <value>` using the same compact quota bar and reset countdown value as the terminal statusline. Impact: Telegram operators can see Codex quota/reset state in the main control menu without any extra configuration, while non-Codex models and missing `pi-telegram` installs stay unchanged.
|
package/README.md
CHANGED
|
@@ -47,10 +47,18 @@ Normal usage:
|
|
|
47
47
|
codex ██████▀▀▀▀ 6d
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
The ten-character bar encodes two twenty-step limits at once: 40 total bits of quota state in 10 terminal cells. Each step is 5%: the top quadrants are the 5-hour limit, and the bottom quadrants are the weekly limit.
|
|
50
|
+
The ten-character bar encodes two twenty-step limits at once: 40 total bits of quota state in 10 terminal cells. Each step is 5%: the top quadrants are the 5-hour limit, and the bottom quadrants are the weekly limit. If either quota window is exhausted, the bar keeps the same shape but switches to the error background color.
|
|
51
51
|
|
|
52
52
|
When the weekly reset time is available, the bar is followed by a countdown. More than a day remains is shown in 144-minute day-tenth steps such as `7d`, `6.9d`, `6.6d`, `5.1d`, `5d`, `3.7d`, `3d`, `2d`, `1.9d`, `1.5d`, and `1.1d`, rounded upward to the next tenth. At 24 hours and below it switches to upward-rounded 6-minute hour-tenth steps such as `24h`, `23.7h`, `20.1h`, `20h`, `19.9h`, `1.4h`, `1.3h`, `1.2h`, `1.1h`, and `1h`. Under an hour it switches to floored minutes, and under a minute to seconds. After the reset timestamp passes, `0s` is held until the next successful quota refresh reports the new weekly window.
|
|
53
53
|
|
|
54
|
+
When the 5-hour window is exhausted and exposes its own reset time, the statusline adds the 5-hour reset before the weekly reset:
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
codex ▄▄▄▄▄⠀⠀⠀⠀⠀ 5h/7d
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The ten-character dual bar stays unchanged. The first countdown is the 5-hour reset, and the second countdown after `/` is the weekly reset.
|
|
61
|
+
|
|
54
62
|
Unavailable because Codex auth or subscription quota is not available:
|
|
55
63
|
|
|
56
64
|
```text
|
package/index.ts
CHANGED
|
@@ -864,7 +864,11 @@ export function formatCodexUsageStatusline(
|
|
|
864
864
|
const value = formatCodexUsageStatusValue(report);
|
|
865
865
|
if (!value) return formatStatuslineText(ctx, "n/a");
|
|
866
866
|
const [bar, countdown] = value.split(" ", 2);
|
|
867
|
-
const barText = formatStatuslineBarText(
|
|
867
|
+
const barText = formatStatuslineBarText(
|
|
868
|
+
ctx,
|
|
869
|
+
bar ?? "",
|
|
870
|
+
hasExhaustedQuotaWindow(report) ? "toolErrorBg" : "selectedBg",
|
|
871
|
+
);
|
|
868
872
|
return countdown
|
|
869
873
|
? `${barText} ${ctx.ui.theme.fg("dim", countdown)}`
|
|
870
874
|
: barText;
|
|
@@ -880,8 +884,18 @@ export function formatCodexUsageStatusValue(
|
|
|
880
884
|
report: CodexUsageReport,
|
|
881
885
|
now = Date.now(),
|
|
882
886
|
): string | undefined {
|
|
883
|
-
const
|
|
884
|
-
if (!
|
|
887
|
+
const snapshot = selectPrimaryCodexSnapshot(report);
|
|
888
|
+
if (!snapshot || (!snapshot.primary && !snapshot.secondary)) return undefined;
|
|
889
|
+
const bar = formatDualLimitBar(snapshot.primary, snapshot.secondary);
|
|
890
|
+
if (isQuotaWindowExhausted(snapshot.primary) && snapshot.primary?.resetAt) {
|
|
891
|
+
const primaryCountdown = formatResetCountdown(snapshot.primary.resetAt, now);
|
|
892
|
+
const weeklyCountdown = snapshot.secondary?.resetAt
|
|
893
|
+
? formatResetCountdown(snapshot.secondary.resetAt, now)
|
|
894
|
+
: undefined;
|
|
895
|
+
return weeklyCountdown
|
|
896
|
+
? `${bar} ${primaryCountdown}/${weeklyCountdown}`
|
|
897
|
+
: `${bar} ${primaryCountdown}`;
|
|
898
|
+
}
|
|
885
899
|
const countdown = formatWeeklyResetCountdown(report, now);
|
|
886
900
|
return countdown ? `${bar} ${countdown}` : bar;
|
|
887
901
|
}
|
|
@@ -917,9 +931,16 @@ export function nextResetCountdownDelayMs(
|
|
|
917
931
|
report: CodexUsageReport,
|
|
918
932
|
now = Date.now(),
|
|
919
933
|
): number | undefined {
|
|
920
|
-
const
|
|
921
|
-
|
|
922
|
-
|
|
934
|
+
const snapshot = selectPrimaryCodexSnapshot(report);
|
|
935
|
+
const resetTimes = [snapshot?.secondary?.resetAt];
|
|
936
|
+
if (isQuotaWindowExhausted(snapshot?.primary)) {
|
|
937
|
+
resetTimes.push(snapshot?.primary?.resetAt);
|
|
938
|
+
}
|
|
939
|
+
const delays = resetTimes
|
|
940
|
+
.filter((resetAt): resetAt is number => resetAt !== undefined)
|
|
941
|
+
.map((resetAt) => nextResetCountdownDelayForRemainingMs(resetAt - now))
|
|
942
|
+
.filter((delay): delay is number => delay !== undefined);
|
|
943
|
+
return delays.length > 0 ? Math.min(...delays) : undefined;
|
|
923
944
|
}
|
|
924
945
|
|
|
925
946
|
export function nextResetCountdownDelayForRemainingMs(
|
|
@@ -962,9 +983,13 @@ function formatStatuslineText(ctx: ExtensionContext, value: string): string {
|
|
|
962
983
|
return `${label} ${ctx.ui.theme.fg("dim", value)}`;
|
|
963
984
|
}
|
|
964
985
|
|
|
965
|
-
function formatStatuslineBarText(
|
|
986
|
+
function formatStatuslineBarText(
|
|
987
|
+
ctx: ExtensionContext,
|
|
988
|
+
bar: string,
|
|
989
|
+
background: "selectedBg" | "toolErrorBg" = "selectedBg",
|
|
990
|
+
): string {
|
|
966
991
|
const label = ctx.ui.theme.fg("accent", STATUS_LABEL_TEXT);
|
|
967
|
-
const value = ctx.ui.theme.bg(
|
|
992
|
+
const value = ctx.ui.theme.bg(background, ctx.ui.theme.fg("dim", bar));
|
|
968
993
|
return `${label} ${value}`;
|
|
969
994
|
}
|
|
970
995
|
|
|
@@ -1041,15 +1066,38 @@ function formatDualLimitBar(
|
|
|
1041
1066
|
|
|
1042
1067
|
function filledTwentieths(
|
|
1043
1068
|
window: NormalizedRateLimitWindow | undefined,
|
|
1069
|
+
): number {
|
|
1070
|
+
return filledParts(window, 20);
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
function filledParts(
|
|
1074
|
+
window: NormalizedRateLimitWindow | undefined,
|
|
1075
|
+
totalParts: number,
|
|
1044
1076
|
): number {
|
|
1045
1077
|
if (!window) return 0;
|
|
1046
|
-
|
|
1078
|
+
const remaining = remainingPercent(window);
|
|
1079
|
+
if (remaining <= 0) return 0;
|
|
1080
|
+
return Math.max(1, Math.round(remaining / (100 / totalParts)));
|
|
1047
1081
|
}
|
|
1048
1082
|
|
|
1049
1083
|
function remainingPercent(window: NormalizedRateLimitWindow): number {
|
|
1050
1084
|
return 100 - clampPercent(window.usedPercent);
|
|
1051
1085
|
}
|
|
1052
1086
|
|
|
1087
|
+
function isQuotaWindowExhausted(
|
|
1088
|
+
window: NormalizedRateLimitWindow | undefined,
|
|
1089
|
+
): boolean {
|
|
1090
|
+
return window !== undefined && remainingPercent(window) <= 0;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
function hasExhaustedQuotaWindow(report: CodexUsageReport): boolean {
|
|
1094
|
+
const snapshot = selectPrimaryCodexSnapshot(report);
|
|
1095
|
+
return (
|
|
1096
|
+
isQuotaWindowExhausted(snapshot?.primary) ||
|
|
1097
|
+
isQuotaWindowExhausted(snapshot?.secondary)
|
|
1098
|
+
);
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1053
1101
|
function isPrimaryCodexSnapshot(
|
|
1054
1102
|
snapshot: NormalizedRateLimitSnapshot,
|
|
1055
1103
|
): boolean {
|