@ravshansbox/pi-quota 0.9.0 → 0.10.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/README.md +2 -2
- package/index.ts +26 -26
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,12 +12,12 @@ Add `-l` to install it in project settings.
|
|
|
12
12
|
|
|
13
13
|
## Usage
|
|
14
14
|
|
|
15
|
-
Pi loads the extension from `./index.ts` and shows the
|
|
15
|
+
Pi loads the extension from `./index.ts` and shows the used quota for the provider backing the active model in the footer.
|
|
16
16
|
|
|
17
17
|
- Calls the Anthropic usage endpoint once on session start, then reads the Anthropic quota from the rate limit headers of each Anthropic response
|
|
18
18
|
- Polls the OpenAI Codex usage endpoint on session start, then at every 10-minute wall-clock mark (`HH:00`, `HH:10`, `HH:20`, `HH:30`, `HH:40`, `HH:50`)
|
|
19
19
|
- Shows a footer status for the provider backing the **active model**, and only when quota data for that provider has been polled; switching models re-renders it immediately
|
|
20
|
-
- The text stays compact, e.g. `
|
|
20
|
+
- The text stays compact, e.g. `7% 1h29m · 33% 4d`, where the 5-hour window comes first and the 7-day window second, and each window shows its used percentage followed by time until reset
|
|
21
21
|
- Available resets, when present, are appended as `· 2x 5h` (count, then time until the soonest expires)
|
|
22
22
|
- Both providers are checked on session start whenever their credentials exist, so quota is already known when the active model changes
|
|
23
23
|
- Refreshes Anthropic and OpenAI Codex OAuth access tokens from `~/.pi/agent/auth.json` when needed, writes updated credentials back (re-reading the file first to avoid clobbering concurrent updates), and notifies on the first successful refresh per provider each session
|
package/index.ts
CHANGED
|
@@ -13,9 +13,9 @@ interface QuotaConfig {
|
|
|
13
13
|
|
|
14
14
|
interface QuotaState {
|
|
15
15
|
provider: 'anthropic' | 'openai-codex';
|
|
16
|
-
|
|
16
|
+
fiveHourUsed: number | null;
|
|
17
17
|
fiveHourReset: Date | null;
|
|
18
|
-
|
|
18
|
+
sevenDayUsed: number | null;
|
|
19
19
|
sevenDayReset: Date | null;
|
|
20
20
|
resetsAvailable: number;
|
|
21
21
|
resetSoonestExpiry: Date | null;
|
|
@@ -118,7 +118,7 @@ function parseAnthropicHeaderWindow(headers: Record<string, string>, name: strin
|
|
|
118
118
|
const utilization = headers[`anthropic-ratelimit-unified-${name}-utilization`];
|
|
119
119
|
const reset = headers[`anthropic-ratelimit-unified-${name}-reset`];
|
|
120
120
|
return {
|
|
121
|
-
|
|
121
|
+
used: utilization === undefined ? null : clampPercent(Number(utilization) * 100),
|
|
122
122
|
reset: reset === undefined ? null : new Date(Number(reset) * 1000),
|
|
123
123
|
};
|
|
124
124
|
}
|
|
@@ -126,12 +126,12 @@ function parseAnthropicHeaderWindow(headers: Record<string, string>, name: strin
|
|
|
126
126
|
function parseAnthropicHeaders(headers: Record<string, string>): QuotaState | null {
|
|
127
127
|
const fiveHour = parseAnthropicHeaderWindow(headers, '5h');
|
|
128
128
|
const sevenDay = parseAnthropicHeaderWindow(headers, '7d');
|
|
129
|
-
if (fiveHour.
|
|
129
|
+
if (fiveHour.used === null && sevenDay.used === null) return null;
|
|
130
130
|
return {
|
|
131
131
|
provider: 'anthropic',
|
|
132
|
-
|
|
132
|
+
fiveHourUsed: fiveHour.used,
|
|
133
133
|
fiveHourReset: fiveHour.reset,
|
|
134
|
-
|
|
134
|
+
sevenDayUsed: sevenDay.used,
|
|
135
135
|
sevenDayReset: sevenDay.reset,
|
|
136
136
|
resetsAvailable: 0,
|
|
137
137
|
resetSoonestExpiry: null,
|
|
@@ -213,13 +213,13 @@ export default function (pi: ExtensionAPI) {
|
|
|
213
213
|
if (!state) return undefined;
|
|
214
214
|
|
|
215
215
|
const parts: string[] = [];
|
|
216
|
-
if (state.
|
|
216
|
+
if (state.fiveHourUsed !== null) {
|
|
217
217
|
const resetStr = state.fiveHourReset ? formatResetTime(state.fiveHourReset) : '?';
|
|
218
|
-
parts.push(`${state.
|
|
218
|
+
parts.push(`${state.fiveHourUsed}% ${resetStr}`);
|
|
219
219
|
}
|
|
220
|
-
if (state.
|
|
220
|
+
if (state.sevenDayUsed !== null) {
|
|
221
221
|
const resetStr = state.sevenDayReset ? formatResetTime(state.sevenDayReset) : '?';
|
|
222
|
-
parts.push(`${state.
|
|
222
|
+
parts.push(`${state.sevenDayUsed}% ${resetStr}`);
|
|
223
223
|
}
|
|
224
224
|
if (state.resetsAvailable > 0) {
|
|
225
225
|
const expiryStr = state.resetSoonestExpiry
|
|
@@ -242,13 +242,13 @@ export default function (pi: ExtensionAPI) {
|
|
|
242
242
|
function windowResetDetected(
|
|
243
243
|
previousReset: Date | null,
|
|
244
244
|
nextReset: Date | null,
|
|
245
|
-
|
|
246
|
-
|
|
245
|
+
previousUsed: number | null,
|
|
246
|
+
nextUsed: number | null,
|
|
247
247
|
): boolean {
|
|
248
248
|
if (!previousReset || !nextReset) return false;
|
|
249
249
|
if (nextReset.getTime() <= previousReset.getTime()) return false;
|
|
250
|
-
if (
|
|
251
|
-
return
|
|
250
|
+
if (previousUsed === null || nextUsed === null) return false;
|
|
251
|
+
return nextUsed < previousUsed;
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
function shouldPingOnReset(previous: QuotaState, next: QuotaState): boolean {
|
|
@@ -256,14 +256,14 @@ export default function (pi: ExtensionAPI) {
|
|
|
256
256
|
windowResetDetected(
|
|
257
257
|
previous.fiveHourReset,
|
|
258
258
|
next.fiveHourReset,
|
|
259
|
-
previous.
|
|
260
|
-
next.
|
|
259
|
+
previous.fiveHourUsed,
|
|
260
|
+
next.fiveHourUsed,
|
|
261
261
|
) ||
|
|
262
262
|
windowResetDetected(
|
|
263
263
|
previous.sevenDayReset,
|
|
264
264
|
next.sevenDayReset,
|
|
265
|
-
previous.
|
|
266
|
-
next.
|
|
265
|
+
previous.sevenDayUsed,
|
|
266
|
+
next.sevenDayUsed,
|
|
267
267
|
)
|
|
268
268
|
);
|
|
269
269
|
}
|
|
@@ -321,9 +321,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
321
321
|
|
|
322
322
|
const shouldPing = parsed.provider !== 'anthropic' && shouldPingOnReset(existing, parsed);
|
|
323
323
|
|
|
324
|
-
existing.
|
|
324
|
+
existing.fiveHourUsed = parsed.fiveHourUsed;
|
|
325
325
|
existing.fiveHourReset = parsed.fiveHourReset;
|
|
326
|
-
existing.
|
|
326
|
+
existing.sevenDayUsed = parsed.sevenDayUsed;
|
|
327
327
|
existing.sevenDayReset = parsed.sevenDayReset;
|
|
328
328
|
existing.resetsAvailable = parsed.resetsAvailable;
|
|
329
329
|
existing.resetSoonestExpiry = parsed.resetSoonestExpiry;
|
|
@@ -500,9 +500,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
500
500
|
const sevenDay = data.seven_day;
|
|
501
501
|
updateState({
|
|
502
502
|
provider: 'anthropic',
|
|
503
|
-
|
|
503
|
+
fiveHourUsed: fiveHour ? clampPercent(fiveHour.utilization ?? 0) : null,
|
|
504
504
|
fiveHourReset: fiveHour?.resets_at ? new Date(fiveHour.resets_at) : null,
|
|
505
|
-
|
|
505
|
+
sevenDayUsed: sevenDay ? clampPercent(sevenDay.utilization ?? 0) : null,
|
|
506
506
|
sevenDayReset: sevenDay?.resets_at ? new Date(sevenDay.resets_at) : null,
|
|
507
507
|
resetsAvailable: 0,
|
|
508
508
|
resetSoonestExpiry: null,
|
|
@@ -536,9 +536,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
536
536
|
resetsAvailable !== undefined ? Math.max(0, Math.trunc(resetsAvailable)) : 0;
|
|
537
537
|
updateState({
|
|
538
538
|
provider: 'openai-codex',
|
|
539
|
-
|
|
539
|
+
fiveHourUsed: fiveHour ? clampPercent(fiveHour.used_percent ?? 0) : null,
|
|
540
540
|
fiveHourReset: fiveHour?.reset_at ? new Date(fiveHour.reset_at * 1000) : null,
|
|
541
|
-
|
|
541
|
+
sevenDayUsed: sevenDay ? clampPercent(sevenDay.used_percent ?? 0) : null,
|
|
542
542
|
sevenDayReset: sevenDay?.reset_at ? new Date(sevenDay.reset_at * 1000) : null,
|
|
543
543
|
resetsAvailable: resetCount,
|
|
544
544
|
resetSoonestExpiry:
|
|
@@ -562,11 +562,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
562
562
|
const codexState = states.find((s) => s.provider === 'openai-codex');
|
|
563
563
|
if (!codexState) return;
|
|
564
564
|
|
|
565
|
-
if (codexState.
|
|
565
|
+
if (codexState.sevenDayUsed !== null && codexState.sevenDayUsed < 100) {
|
|
566
566
|
codexRedeemAttempted = false;
|
|
567
567
|
}
|
|
568
568
|
|
|
569
|
-
if (codexState.
|
|
569
|
+
if (codexState.sevenDayUsed !== 100) return;
|
|
570
570
|
if (codexState.resetsAvailable === 0) return;
|
|
571
571
|
if (codexRedeemAttempted) return;
|
|
572
572
|
|