@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.
Files changed (3) hide show
  1. package/README.md +2 -2
  2. package/index.ts +26 -26
  3. 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 remaining quota for the provider backing the active model in the footer.
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. `93% 1h29m · 67% 4d`, where the 5-hour window comes first and the 7-day window second, and each window shows its remaining percentage followed by time until reset
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
- fiveHourRemaining: number | null;
16
+ fiveHourUsed: number | null;
17
17
  fiveHourReset: Date | null;
18
- sevenDayRemaining: number | null;
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
- remaining: utilization === undefined ? null : clampPercent(100 - Number(utilization) * 100),
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.remaining === null && sevenDay.remaining === null) return null;
129
+ if (fiveHour.used === null && sevenDay.used === null) return null;
130
130
  return {
131
131
  provider: 'anthropic',
132
- fiveHourRemaining: fiveHour.remaining,
132
+ fiveHourUsed: fiveHour.used,
133
133
  fiveHourReset: fiveHour.reset,
134
- sevenDayRemaining: sevenDay.remaining,
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.fiveHourRemaining !== null) {
216
+ if (state.fiveHourUsed !== null) {
217
217
  const resetStr = state.fiveHourReset ? formatResetTime(state.fiveHourReset) : '?';
218
- parts.push(`${state.fiveHourRemaining}% ${resetStr}`);
218
+ parts.push(`${state.fiveHourUsed}% ${resetStr}`);
219
219
  }
220
- if (state.sevenDayRemaining !== null) {
220
+ if (state.sevenDayUsed !== null) {
221
221
  const resetStr = state.sevenDayReset ? formatResetTime(state.sevenDayReset) : '?';
222
- parts.push(`${state.sevenDayRemaining}% ${resetStr}`);
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
- previousRemaining: number | null,
246
- nextRemaining: number | null,
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 (previousRemaining === null || nextRemaining === null) return false;
251
- return nextRemaining > previousRemaining;
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.fiveHourRemaining,
260
- next.fiveHourRemaining,
259
+ previous.fiveHourUsed,
260
+ next.fiveHourUsed,
261
261
  ) ||
262
262
  windowResetDetected(
263
263
  previous.sevenDayReset,
264
264
  next.sevenDayReset,
265
- previous.sevenDayRemaining,
266
- next.sevenDayRemaining,
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.fiveHourRemaining = parsed.fiveHourRemaining;
324
+ existing.fiveHourUsed = parsed.fiveHourUsed;
325
325
  existing.fiveHourReset = parsed.fiveHourReset;
326
- existing.sevenDayRemaining = parsed.sevenDayRemaining;
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
- fiveHourRemaining: fiveHour ? clampPercent(100 - (fiveHour.utilization ?? 0)) : null,
503
+ fiveHourUsed: fiveHour ? clampPercent(fiveHour.utilization ?? 0) : null,
504
504
  fiveHourReset: fiveHour?.resets_at ? new Date(fiveHour.resets_at) : null,
505
- sevenDayRemaining: sevenDay ? clampPercent(100 - (sevenDay.utilization ?? 0)) : null,
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
- fiveHourRemaining: fiveHour ? clampPercent(100 - (fiveHour.used_percent ?? 0)) : null,
539
+ fiveHourUsed: fiveHour ? clampPercent(fiveHour.used_percent ?? 0) : null,
540
540
  fiveHourReset: fiveHour?.reset_at ? new Date(fiveHour.reset_at * 1000) : null,
541
- sevenDayRemaining: sevenDay ? clampPercent(100 - (sevenDay.used_percent ?? 0)) : null,
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.sevenDayRemaining !== null && codexState.sevenDayRemaining > 0) {
565
+ if (codexState.sevenDayUsed !== null && codexState.sevenDayUsed < 100) {
566
566
  codexRedeemAttempted = false;
567
567
  }
568
568
 
569
- if (codexState.sevenDayRemaining !== 0) return;
569
+ if (codexState.sevenDayUsed !== 100) return;
570
570
  if (codexState.resetsAvailable === 0) return;
571
571
  if (codexRedeemAttempted) return;
572
572
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ravshansbox/pi-quota",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Anthropic and OpenAI Codex quota status extension for pi",
5
5
  "license": "MIT",
6
6
  "type": "module",