oira666_pi-limits-wait 0.4.9 → 0.4.11

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 CHANGED
@@ -21,8 +21,8 @@ Restart Pi or run `/reload` after installing.
21
21
  - On `server_is_overloaded`, waits 5 minutes, then retries. If the provider is still overloaded after Pi's normal retries, it waits another 5 minutes and repeats.
22
22
  - On transient network/transport failures — including undici idle-timeout aborts (`UND_ERR_HEADERS_TIMEOUT` / `UND_ERR_BODY_TIMEOUT`), `fetch failed`, `terminated`, `ECONNRESET`, `ETIMEDOUT`, etc. — treats the error as retryable with a short backoff (default 15s, or the provider's `retry-after`) instead of giving up. This prevents a stalled streaming request from turning into a silent hang.
23
23
  - For any other (unclassified) non-retryable error, retries the same model a few times (default 3) before falling back / freezing it, so a one-off hiccup does not immediately sideline a model.
24
- - Shows a countdown in the Pi status/working line.
25
- - Press Enter during the countdown to skip the wait and retry immediately.
24
+ - Reports retry/fallback waits in chat notifications without adding persistent TUI status lines.
25
+ - Press Enter during a retry wait to skip the wait and retry immediately.
26
26
  - Optionally falls back to configured models when the current/default model is rate-limited.
27
27
 
28
28
  ## Optional fallback models
@@ -74,7 +74,7 @@ Fallback behavior:
74
74
  4. If that model later becomes rate-limited too, the extension starts again from the same ordered list.
75
75
  5. Rate-limit reset times are remembered only in memory, so known-limited models are skipped until their countdown expires. This memory is cleared when Pi restarts.
76
76
 
77
- When the settings are loaded, the extension shows the full usable fallback model list. When models become rate-limited, it shows a live countdown for each limited model.
77
+ When the settings are loaded, the extension shows the full usable fallback model list. When models become rate-limited, it reports the wait in chat notifications.
78
78
 
79
79
  ## Environment variables
80
80
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oira666_pi-limits-wait",
3
- "version": "0.4.9",
3
+ "version": "0.4.11",
4
4
  "description": "Extension for PI that allows to automatically wait for limits to reset and retry the request",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -0,0 +1,23 @@
1
+ export const CLAUDE_CODE_IDENTITY =
2
+ "You are Claude Code, Anthropic's official CLI for Claude.";
3
+
4
+ export const PI_REMOVAL_ANCHORS = [
5
+ "pi-coding-agent",
6
+ "@mariozechner/pi-coding-agent",
7
+ "badlogic/pi-mono",
8
+ ] as const;
9
+
10
+ export const PI_IDENTITY_SENTENCE_PATTERN =
11
+ /(?:^|\n)\s*You are pi\b[^.!?\n]*(?:[.!?](?=\s|$)|(?=\n|$))/gi;
12
+
13
+ export const DEFAULT_RATE_LIMIT_WAIT_MS = 30 * 60 * 1_000; // 30 minutes
14
+ export const DEFAULT_OVERLOADED_WAIT_MS = 5 * 60 * 1_000; // 5 minutes
15
+ export const DEFAULT_NETWORK_WAIT_MS = 15 * 1_000; // 15 seconds
16
+ export const DEFAULT_NON_RETRYABLE_FREEZE_MS = 10 * 60 * 1_000; // 10 minutes
17
+
18
+ export const NON_RETRYABLE_MAX_ATTEMPTS = 3;
19
+ export const NON_RETRYABLE_RETRY_DELAY_MS = 5 * 1_000; // 5 seconds
20
+
21
+ export const SETTINGS_FILE_NAME = "limits-wait.json";
22
+ export const FALLBACK_MODELS_KEY = "fallback-models";
23
+ export const FREEZING_ENV_VAR = "PI_LIMITS_WAIT_FREEZING_ENABLED";
@@ -0,0 +1,49 @@
1
+ import { DEFAULT_RATE_LIMIT_WAIT_MS } from "./constants.js";
2
+ import { retryAfterHeaderMs } from "./retry-errors.js";
3
+ import { state } from "./state.js";
4
+ import { formatErrorDetail, showAmbientRetryStatus, clearAmbientRetryStatus } from "./ui.js";
5
+
6
+ async function responseErrorMessage(response: Response): Promise<string> {
7
+ const retryAfterMs = retryAfterHeaderMs(response.headers);
8
+ const retryAfter = retryAfterMs !== undefined ? ` retry-after-ms ${retryAfterMs}` : "";
9
+ let body = "";
10
+ try {
11
+ body = await response.clone().text();
12
+ } catch {
13
+ // Ignore body read failures; status and retry headers are enough.
14
+ }
15
+ const detail = body.trim() ? ` ${formatErrorDetail(body)}` : "";
16
+ return `HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ""}${retryAfter}${detail}`;
17
+ }
18
+
19
+ export function installFetchRateLimitObserver(): void {
20
+ if (state.restoreFetch || typeof globalThis.fetch !== "function") return;
21
+
22
+ const originalFetch = globalThis.fetch.bind(globalThis);
23
+ globalThis.fetch = (async (...args: Parameters<typeof fetch>) => {
24
+ const response = await originalFetch(...args);
25
+
26
+ if (state.activeProviderRequests > 0) {
27
+ if (!response.ok) {
28
+ const message = await responseErrorMessage(response);
29
+ state.lastObservedHttpError = { at: Date.now(), message };
30
+ if (response.status === 429) {
31
+ const waitMs = retryAfterHeaderMs(response.headers) ?? DEFAULT_RATE_LIMIT_WAIT_MS;
32
+ showAmbientRetryStatus("rate-limit", waitMs);
33
+ if (state.activeFallbackProviderRequests > 0) {
34
+ throw new Error(message);
35
+ }
36
+ }
37
+ } else if (state.ambientStatusCleanup && response.ok) {
38
+ clearAmbientRetryStatus();
39
+ }
40
+ }
41
+
42
+ return response;
43
+ }) as typeof fetch;
44
+
45
+ state.restoreFetch = () => {
46
+ globalThis.fetch = originalFetch as typeof fetch;
47
+ state.restoreFetch = undefined;
48
+ };
49
+ }