codex-multi-auth 0.1.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/LICENSE +1 -0
- package/README.md +162 -0
- package/assets/opencode-logo-ornate-dark.svg +18 -0
- package/assets/readme-hero.svg +31 -0
- package/config/README.md +87 -0
- package/config/minimal-opencode.json +13 -0
- package/config/opencode-legacy.json +571 -0
- package/config/opencode-modern.json +239 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3160 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/accounts/rate-limits.d.ts +22 -0
- package/dist/lib/accounts/rate-limits.d.ts.map +1 -0
- package/dist/lib/accounts/rate-limits.js +63 -0
- package/dist/lib/accounts/rate-limits.js.map +1 -0
- package/dist/lib/accounts.d.ts +95 -0
- package/dist/lib/accounts.d.ts.map +1 -0
- package/dist/lib/accounts.js +668 -0
- package/dist/lib/accounts.js.map +1 -0
- package/dist/lib/audit.d.ts +45 -0
- package/dist/lib/audit.d.ts.map +1 -0
- package/dist/lib/audit.js +131 -0
- package/dist/lib/audit.js.map +1 -0
- package/dist/lib/auth/auth.d.ts +56 -0
- package/dist/lib/auth/auth.d.ts.map +1 -0
- package/dist/lib/auth/auth.js +214 -0
- package/dist/lib/auth/auth.js.map +1 -0
- package/dist/lib/auth/browser.d.ts +34 -0
- package/dist/lib/auth/browser.d.ts.map +1 -0
- package/dist/lib/auth/browser.js +185 -0
- package/dist/lib/auth/browser.js.map +1 -0
- package/dist/lib/auth/server.d.ts +24 -0
- package/dist/lib/auth/server.d.ts.map +1 -0
- package/dist/lib/auth/server.js +116 -0
- package/dist/lib/auth/server.js.map +1 -0
- package/dist/lib/auth/token-utils.d.ts +59 -0
- package/dist/lib/auth/token-utils.d.ts.map +1 -0
- package/dist/lib/auth/token-utils.js +331 -0
- package/dist/lib/auth/token-utils.js.map +1 -0
- package/dist/lib/auth-rate-limit.d.ts +20 -0
- package/dist/lib/auth-rate-limit.d.ts.map +1 -0
- package/dist/lib/auth-rate-limit.js +91 -0
- package/dist/lib/auth-rate-limit.js.map +1 -0
- package/dist/lib/auto-update-checker.d.ts +10 -0
- package/dist/lib/auto-update-checker.d.ts.map +1 -0
- package/dist/lib/auto-update-checker.js +216 -0
- package/dist/lib/auto-update-checker.js.map +1 -0
- package/dist/lib/capability-policy.d.ts +18 -0
- package/dist/lib/capability-policy.d.ts.map +1 -0
- package/dist/lib/capability-policy.js +150 -0
- package/dist/lib/capability-policy.js.map +1 -0
- package/dist/lib/circuit-breaker.d.ts +34 -0
- package/dist/lib/circuit-breaker.d.ts.map +1 -0
- package/dist/lib/circuit-breaker.js +124 -0
- package/dist/lib/circuit-breaker.js.map +1 -0
- package/dist/lib/cli.d.ts +64 -0
- package/dist/lib/cli.d.ts.map +1 -0
- package/dist/lib/cli.js +274 -0
- package/dist/lib/cli.js.map +1 -0
- package/dist/lib/codex-cli/observability.d.ts +22 -0
- package/dist/lib/codex-cli/observability.d.ts.map +1 -0
- package/dist/lib/codex-cli/observability.js +36 -0
- package/dist/lib/codex-cli/observability.js.map +1 -0
- package/dist/lib/codex-cli/state.d.ts +86 -0
- package/dist/lib/codex-cli/state.d.ts.map +1 -0
- package/dist/lib/codex-cli/state.js +470 -0
- package/dist/lib/codex-cli/state.js.map +1 -0
- package/dist/lib/codex-cli/sync.d.ts +27 -0
- package/dist/lib/codex-cli/sync.d.ts.map +1 -0
- package/dist/lib/codex-cli/sync.js +325 -0
- package/dist/lib/codex-cli/sync.js.map +1 -0
- package/dist/lib/codex-cli/writer.d.ts +12 -0
- package/dist/lib/codex-cli/writer.d.ts.map +1 -0
- package/dist/lib/codex-cli/writer.js +388 -0
- package/dist/lib/codex-cli/writer.js.map +1 -0
- package/dist/lib/codex-manager.d.ts +2 -0
- package/dist/lib/codex-manager.d.ts.map +1 -0
- package/dist/lib/codex-manager.js +4841 -0
- package/dist/lib/codex-manager.js.map +1 -0
- package/dist/lib/config.d.ts +269 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +789 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/constants.d.ts +78 -0
- package/dist/lib/constants.d.ts.map +1 -0
- package/dist/lib/constants.js +78 -0
- package/dist/lib/constants.js.map +1 -0
- package/dist/lib/context-overflow.d.ts +27 -0
- package/dist/lib/context-overflow.d.ts.map +1 -0
- package/dist/lib/context-overflow.js +124 -0
- package/dist/lib/context-overflow.js.map +1 -0
- package/dist/lib/dashboard-settings.d.ts +90 -0
- package/dist/lib/dashboard-settings.d.ts.map +1 -0
- package/dist/lib/dashboard-settings.js +327 -0
- package/dist/lib/dashboard-settings.js.map +1 -0
- package/dist/lib/entitlement-cache.d.ts +41 -0
- package/dist/lib/entitlement-cache.d.ts.map +1 -0
- package/dist/lib/entitlement-cache.js +137 -0
- package/dist/lib/entitlement-cache.js.map +1 -0
- package/dist/lib/errors.d.ts +113 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +103 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/forecast.d.ts +42 -0
- package/dist/lib/forecast.d.ts.map +1 -0
- package/dist/lib/forecast.js +256 -0
- package/dist/lib/forecast.js.map +1 -0
- package/dist/lib/health.d.ts +33 -0
- package/dist/lib/health.d.ts.map +1 -0
- package/dist/lib/health.js +70 -0
- package/dist/lib/health.js.map +1 -0
- package/dist/lib/index.d.ts +32 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +32 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/live-account-sync.d.ts +39 -0
- package/dist/lib/live-account-sync.d.ts.map +1 -0
- package/dist/lib/live-account-sync.js +196 -0
- package/dist/lib/live-account-sync.js.map +1 -0
- package/dist/lib/logger.d.ts +40 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +364 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/oauth-success.html +338 -0
- package/dist/lib/parallel-probe.d.ts +28 -0
- package/dist/lib/parallel-probe.d.ts.map +1 -0
- package/dist/lib/parallel-probe.js +97 -0
- package/dist/lib/parallel-probe.js.map +1 -0
- package/dist/lib/preemptive-quota-scheduler.d.ts +53 -0
- package/dist/lib/preemptive-quota-scheduler.d.ts.map +1 -0
- package/dist/lib/preemptive-quota-scheduler.js +220 -0
- package/dist/lib/preemptive-quota-scheduler.js.map +1 -0
- package/dist/lib/proactive-refresh.d.ts +66 -0
- package/dist/lib/proactive-refresh.d.ts.map +1 -0
- package/dist/lib/proactive-refresh.js +143 -0
- package/dist/lib/proactive-refresh.js.map +1 -0
- package/dist/lib/prompts/codex-opencode-bridge.d.ts +19 -0
- package/dist/lib/prompts/codex-opencode-bridge.d.ts.map +1 -0
- package/dist/lib/prompts/codex-opencode-bridge.js +169 -0
- package/dist/lib/prompts/codex-opencode-bridge.js.map +1 -0
- package/dist/lib/prompts/codex.d.ts +41 -0
- package/dist/lib/prompts/codex.d.ts.map +1 -0
- package/dist/lib/prompts/codex.js +383 -0
- package/dist/lib/prompts/codex.js.map +1 -0
- package/dist/lib/prompts/opencode-codex.d.ts +25 -0
- package/dist/lib/prompts/opencode-codex.d.ts.map +1 -0
- package/dist/lib/prompts/opencode-codex.js +270 -0
- package/dist/lib/prompts/opencode-codex.js.map +1 -0
- package/dist/lib/quota-cache.d.ts +68 -0
- package/dist/lib/quota-cache.d.ts.map +1 -0
- package/dist/lib/quota-cache.js +224 -0
- package/dist/lib/quota-cache.js.map +1 -0
- package/dist/lib/quota-probe.d.ts +49 -0
- package/dist/lib/quota-probe.d.ts.map +1 -0
- package/dist/lib/quota-probe.js +368 -0
- package/dist/lib/quota-probe.js.map +1 -0
- package/dist/lib/recovery/constants.d.ts +12 -0
- package/dist/lib/recovery/constants.d.ts.map +1 -0
- package/dist/lib/recovery/constants.js +31 -0
- package/dist/lib/recovery/constants.js.map +1 -0
- package/dist/lib/recovery/index.d.ts +12 -0
- package/dist/lib/recovery/index.d.ts.map +1 -0
- package/dist/lib/recovery/index.js +12 -0
- package/dist/lib/recovery/index.js.map +1 -0
- package/dist/lib/recovery/storage.d.ts +24 -0
- package/dist/lib/recovery/storage.d.ts.map +1 -0
- package/dist/lib/recovery/storage.js +362 -0
- package/dist/lib/recovery/storage.js.map +1 -0
- package/dist/lib/recovery/types.d.ts +116 -0
- package/dist/lib/recovery/types.d.ts.map +1 -0
- package/dist/lib/recovery/types.js +7 -0
- package/dist/lib/recovery/types.js.map +1 -0
- package/dist/lib/recovery.d.ts +31 -0
- package/dist/lib/recovery.d.ts.map +1 -0
- package/dist/lib/recovery.js +313 -0
- package/dist/lib/recovery.js.map +1 -0
- package/dist/lib/refresh-guardian.d.ts +31 -0
- package/dist/lib/refresh-guardian.d.ts.map +1 -0
- package/dist/lib/refresh-guardian.js +151 -0
- package/dist/lib/refresh-guardian.js.map +1 -0
- package/dist/lib/refresh-lease.d.ts +37 -0
- package/dist/lib/refresh-lease.d.ts.map +1 -0
- package/dist/lib/refresh-lease.js +335 -0
- package/dist/lib/refresh-lease.js.map +1 -0
- package/dist/lib/refresh-queue.d.ts +117 -0
- package/dist/lib/refresh-queue.d.ts.map +1 -0
- package/dist/lib/refresh-queue.js +297 -0
- package/dist/lib/refresh-queue.js.map +1 -0
- package/dist/lib/request/failure-policy.d.ts +42 -0
- package/dist/lib/request/failure-policy.d.ts.map +1 -0
- package/dist/lib/request/failure-policy.js +133 -0
- package/dist/lib/request/failure-policy.js.map +1 -0
- package/dist/lib/request/fetch-helpers.d.ts +152 -0
- package/dist/lib/request/fetch-helpers.d.ts.map +1 -0
- package/dist/lib/request/fetch-helpers.js +704 -0
- package/dist/lib/request/fetch-helpers.js.map +1 -0
- package/dist/lib/request/helpers/input-utils.d.ts +7 -0
- package/dist/lib/request/helpers/input-utils.d.ts.map +1 -0
- package/dist/lib/request/helpers/input-utils.js +214 -0
- package/dist/lib/request/helpers/input-utils.js.map +1 -0
- package/dist/lib/request/helpers/model-map.d.ts +28 -0
- package/dist/lib/request/helpers/model-map.d.ts.map +1 -0
- package/dist/lib/request/helpers/model-map.js +133 -0
- package/dist/lib/request/helpers/model-map.js.map +1 -0
- package/dist/lib/request/helpers/tool-utils.d.ts +29 -0
- package/dist/lib/request/helpers/tool-utils.d.ts.map +1 -0
- package/dist/lib/request/helpers/tool-utils.js +117 -0
- package/dist/lib/request/helpers/tool-utils.js.map +1 -0
- package/dist/lib/request/rate-limit-backoff.d.ts +17 -0
- package/dist/lib/request/rate-limit-backoff.d.ts.map +1 -0
- package/dist/lib/request/rate-limit-backoff.js +83 -0
- package/dist/lib/request/rate-limit-backoff.js.map +1 -0
- package/dist/lib/request/request-transformer.d.ts +107 -0
- package/dist/lib/request/request-transformer.d.ts.map +1 -0
- package/dist/lib/request/request-transformer.js +814 -0
- package/dist/lib/request/request-transformer.js.map +1 -0
- package/dist/lib/request/response-handler.d.ts +23 -0
- package/dist/lib/request/response-handler.d.ts.map +1 -0
- package/dist/lib/request/response-handler.js +155 -0
- package/dist/lib/request/response-handler.js.map +1 -0
- package/dist/lib/request/stream-failover.d.ts +21 -0
- package/dist/lib/request/stream-failover.d.ts.map +1 -0
- package/dist/lib/request/stream-failover.js +204 -0
- package/dist/lib/request/stream-failover.js.map +1 -0
- package/dist/lib/rotation.d.ts +146 -0
- package/dist/lib/rotation.d.ts.map +1 -0
- package/dist/lib/rotation.js +321 -0
- package/dist/lib/rotation.js.map +1 -0
- package/dist/lib/runtime-paths.d.ts +58 -0
- package/dist/lib/runtime-paths.d.ts.map +1 -0
- package/dist/lib/runtime-paths.js +164 -0
- package/dist/lib/runtime-paths.js.map +1 -0
- package/dist/lib/schemas.d.ts +435 -0
- package/dist/lib/schemas.d.ts.map +1 -0
- package/dist/lib/schemas.js +268 -0
- package/dist/lib/schemas.js.map +1 -0
- package/dist/lib/session-affinity.d.ts +23 -0
- package/dist/lib/session-affinity.d.ts.map +1 -0
- package/dist/lib/session-affinity.js +127 -0
- package/dist/lib/session-affinity.js.map +1 -0
- package/dist/lib/shutdown.d.ts +7 -0
- package/dist/lib/shutdown.d.ts.map +1 -0
- package/dist/lib/shutdown.js +43 -0
- package/dist/lib/shutdown.js.map +1 -0
- package/dist/lib/storage/migrations.d.ts +59 -0
- package/dist/lib/storage/migrations.d.ts.map +1 -0
- package/dist/lib/storage/migrations.js +41 -0
- package/dist/lib/storage/migrations.js.map +1 -0
- package/dist/lib/storage/paths.d.ts +51 -0
- package/dist/lib/storage/paths.d.ts.map +1 -0
- package/dist/lib/storage/paths.js +152 -0
- package/dist/lib/storage/paths.js.map +1 -0
- package/dist/lib/storage.d.ts +106 -0
- package/dist/lib/storage.d.ts.map +1 -0
- package/dist/lib/storage.js +896 -0
- package/dist/lib/storage.js.map +1 -0
- package/dist/lib/table-formatter.d.ts +32 -0
- package/dist/lib/table-formatter.d.ts.map +1 -0
- package/dist/lib/table-formatter.js +44 -0
- package/dist/lib/table-formatter.js.map +1 -0
- package/dist/lib/tools/hashline-tools.d.ts +51 -0
- package/dist/lib/tools/hashline-tools.d.ts.map +1 -0
- package/dist/lib/tools/hashline-tools.js +456 -0
- package/dist/lib/tools/hashline-tools.js.map +1 -0
- package/dist/lib/types.d.ts +130 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/lib/ui/ansi.d.ts +40 -0
- package/dist/lib/ui/ansi.d.ts.map +1 -0
- package/dist/lib/ui/ansi.js +68 -0
- package/dist/lib/ui/ansi.js.map +1 -0
- package/dist/lib/ui/auth-menu.d.ts +76 -0
- package/dist/lib/ui/auth-menu.d.ts.map +1 -0
- package/dist/lib/ui/auth-menu.js +590 -0
- package/dist/lib/ui/auth-menu.js.map +1 -0
- package/dist/lib/ui/confirm.d.ts +11 -0
- package/dist/lib/ui/confirm.d.ts.map +1 -0
- package/dist/lib/ui/confirm.js +29 -0
- package/dist/lib/ui/confirm.js.map +1 -0
- package/dist/lib/ui/copy.d.ts +123 -0
- package/dist/lib/ui/copy.d.ts.map +1 -0
- package/dist/lib/ui/copy.js +127 -0
- package/dist/lib/ui/copy.js.map +1 -0
- package/dist/lib/ui/format.d.ts +62 -0
- package/dist/lib/ui/format.d.ts.map +1 -0
- package/dist/lib/ui/format.js +205 -0
- package/dist/lib/ui/format.js.map +1 -0
- package/dist/lib/ui/runtime.d.ts +43 -0
- package/dist/lib/ui/runtime.d.ts.map +1 -0
- package/dist/lib/ui/runtime.js +69 -0
- package/dist/lib/ui/runtime.js.map +1 -0
- package/dist/lib/ui/select.d.ts +60 -0
- package/dist/lib/ui/select.d.ts.map +1 -0
- package/dist/lib/ui/select.js +467 -0
- package/dist/lib/ui/select.js.map +1 -0
- package/dist/lib/ui/theme.d.ts +56 -0
- package/dist/lib/ui/theme.d.ts.map +1 -0
- package/dist/lib/ui/theme.js +186 -0
- package/dist/lib/ui/theme.js.map +1 -0
- package/dist/lib/unified-settings.d.ts +71 -0
- package/dist/lib/unified-settings.d.ts.map +1 -0
- package/dist/lib/unified-settings.js +299 -0
- package/dist/lib/unified-settings.js.map +1 -0
- package/dist/lib/utils.d.ts +29 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +54 -0
- package/dist/lib/utils.js.map +1 -0
- package/package.json +115 -0
- package/scripts/audit-dev-allowlist.js +128 -0
- package/scripts/bench-format/hashline-v2.mjs +642 -0
- package/scripts/bench-format/models.mjs +105 -0
- package/scripts/bench-format/opencode.mjs +205 -0
- package/scripts/bench-format/render.mjs +496 -0
- package/scripts/bench-format/stats.mjs +54 -0
- package/scripts/bench-format/tasks.mjs +151 -0
- package/scripts/benchmark-edit-formats.mjs +1161 -0
- package/scripts/benchmark-render-dashboard.mjs +49 -0
- package/scripts/codex-multi-auth.js +6 -0
- package/scripts/codex-routing.js +34 -0
- package/scripts/codex.js +122 -0
- package/scripts/copy-oauth-success.js +37 -0
- package/scripts/install-opencode-codex-auth.js +193 -0
- package/scripts/test-all-models.sh +7 -0
- package/scripts/test-model-matrix.js +424 -0
- package/scripts/validate-model-map.sh +7 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Refresh Queue Module
|
|
3
|
+
*
|
|
4
|
+
* Prevents race conditions when multiple concurrent requests try to refresh
|
|
5
|
+
* the same account's token simultaneously. Instead of firing parallel refresh
|
|
6
|
+
* requests, subsequent callers await the existing in-flight refresh.
|
|
7
|
+
*
|
|
8
|
+
* Ported from antigravity-auth refresh-queue.ts pattern.
|
|
9
|
+
*/
|
|
10
|
+
import { refreshAccessToken } from "./auth/auth.js";
|
|
11
|
+
import { createLogger } from "./logger.js";
|
|
12
|
+
import { RefreshLeaseCoordinator } from "./refresh-lease.js";
|
|
13
|
+
const log = createLogger("refresh-queue");
|
|
14
|
+
/**
|
|
15
|
+
* Manages queued token refresh operations to prevent race conditions.
|
|
16
|
+
*
|
|
17
|
+
* When multiple concurrent requests need to refresh the same account's token,
|
|
18
|
+
* only the first request triggers the actual refresh. Subsequent requests
|
|
19
|
+
* await the same promise, ensuring:
|
|
20
|
+
* - No duplicate refresh API calls for the same refresh token
|
|
21
|
+
* - Consistent token state across all waiting callers
|
|
22
|
+
* - Reduced load on OpenAI's token endpoint
|
|
23
|
+
*
|
|
24
|
+
* Token Rotation Handling:
|
|
25
|
+
* When OpenAI rotates the refresh token during a refresh operation, we maintain
|
|
26
|
+
* a mapping from old token → new token. This ensures that requests arriving with
|
|
27
|
+
* either the old or new token will find the in-flight refresh and not trigger
|
|
28
|
+
* duplicate refreshes.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const queue = new RefreshQueue();
|
|
33
|
+
*
|
|
34
|
+
* // These three concurrent calls will only trigger ONE actual refresh
|
|
35
|
+
* const [result1, result2, result3] = await Promise.all([
|
|
36
|
+
* queue.refresh(refreshToken),
|
|
37
|
+
* queue.refresh(refreshToken),
|
|
38
|
+
* queue.refresh(refreshToken),
|
|
39
|
+
* ]);
|
|
40
|
+
*
|
|
41
|
+
* // All three get the same result
|
|
42
|
+
* console.log(result1 === result2); // true (same object reference)
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export class RefreshQueue {
|
|
46
|
+
pending = new Map();
|
|
47
|
+
leaseCoordinator;
|
|
48
|
+
/**
|
|
49
|
+
* Maps old refresh tokens to new tokens after rotation.
|
|
50
|
+
* This allows lookups with either old or new token to find the same entry.
|
|
51
|
+
* Format: oldToken → newToken
|
|
52
|
+
*/
|
|
53
|
+
tokenRotationMap = new Map();
|
|
54
|
+
/**
|
|
55
|
+
* Maximum time to keep a refresh entry in the queue (prevents memory leaks
|
|
56
|
+
* from stuck requests). After this timeout, the entry is removed and new
|
|
57
|
+
* callers will trigger a fresh refresh.
|
|
58
|
+
*/
|
|
59
|
+
maxEntryAgeMs;
|
|
60
|
+
/**
|
|
61
|
+
* Create a new RefreshQueue instance.
|
|
62
|
+
* @param maxEntryAgeMs - Maximum age for pending entries before cleanup (default: 30s)
|
|
63
|
+
*/
|
|
64
|
+
constructor(maxEntryAgeMs = 30_000, leaseCoordinator = RefreshLeaseCoordinator.fromEnvironment()) {
|
|
65
|
+
this.maxEntryAgeMs = maxEntryAgeMs;
|
|
66
|
+
this.leaseCoordinator = leaseCoordinator;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Refresh a token, deduplicating concurrent requests for the same refresh token.
|
|
70
|
+
*
|
|
71
|
+
* If a refresh is already in-flight for this token, returns the existing promise.
|
|
72
|
+
* Otherwise, initiates a new refresh and caches the promise for other callers.
|
|
73
|
+
*
|
|
74
|
+
* @param refreshToken - The refresh token to use
|
|
75
|
+
* @returns Token result (success with new tokens, or failure)
|
|
76
|
+
*/
|
|
77
|
+
async refresh(refreshToken) {
|
|
78
|
+
this.cleanup();
|
|
79
|
+
// Check for existing in-flight refresh (direct match)
|
|
80
|
+
const existing = this.pending.get(refreshToken);
|
|
81
|
+
if (existing) {
|
|
82
|
+
log.info("Reusing in-flight refresh for token", {
|
|
83
|
+
tokenSuffix: refreshToken.slice(-6),
|
|
84
|
+
waitingMs: Date.now() - existing.startedAt,
|
|
85
|
+
});
|
|
86
|
+
return existing.promise;
|
|
87
|
+
}
|
|
88
|
+
// Check if this token was rotated FROM another token that's still refreshing
|
|
89
|
+
// This handles: Request A starts with oldToken, gets newToken, Request B arrives with newToken
|
|
90
|
+
const rotatedFrom = this.findOriginalToken(refreshToken);
|
|
91
|
+
if (rotatedFrom) {
|
|
92
|
+
const originalEntry = this.pending.get(rotatedFrom);
|
|
93
|
+
if (originalEntry) {
|
|
94
|
+
log.info("Reusing in-flight refresh via rotation mapping", {
|
|
95
|
+
newTokenSuffix: refreshToken.slice(-6),
|
|
96
|
+
originalTokenSuffix: rotatedFrom.slice(-6),
|
|
97
|
+
waitingMs: Date.now() - originalEntry.startedAt,
|
|
98
|
+
});
|
|
99
|
+
return originalEntry.promise;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Start a new refresh immediately so local state reflects "in-flight"
|
|
103
|
+
// without waiting on cross-process lease checks.
|
|
104
|
+
const startedAt = Date.now();
|
|
105
|
+
const promise = (async () => {
|
|
106
|
+
let lease;
|
|
107
|
+
try {
|
|
108
|
+
lease = await this.leaseCoordinator.acquire(refreshToken);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
log.warn("Refresh lease acquire failed; falling back to local refresh", {
|
|
112
|
+
tokenSuffix: refreshToken.slice(-6),
|
|
113
|
+
error: error?.message ?? String(error),
|
|
114
|
+
});
|
|
115
|
+
return this.executeRefreshWithRotationTracking(refreshToken);
|
|
116
|
+
}
|
|
117
|
+
if (lease.role === "follower" && lease.result) {
|
|
118
|
+
log.info("Using refresh result from cross-process lease", {
|
|
119
|
+
tokenSuffix: refreshToken.slice(-6),
|
|
120
|
+
});
|
|
121
|
+
return lease.result;
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
const result = await this.executeRefreshWithRotationTracking(refreshToken);
|
|
125
|
+
try {
|
|
126
|
+
await lease.release(result);
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
log.warn("Failed to publish lease refresh result", {
|
|
130
|
+
tokenSuffix: refreshToken.slice(-6),
|
|
131
|
+
error: error?.message ?? String(error),
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
try {
|
|
138
|
+
await lease.release();
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
log.warn("Failed to release refresh lease", {
|
|
142
|
+
tokenSuffix: refreshToken.slice(-6),
|
|
143
|
+
error: error?.message ?? String(error),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
})();
|
|
148
|
+
this.pending.set(refreshToken, { promise, startedAt });
|
|
149
|
+
try {
|
|
150
|
+
return await promise;
|
|
151
|
+
}
|
|
152
|
+
finally {
|
|
153
|
+
this.pending.delete(refreshToken);
|
|
154
|
+
this.cleanupRotationMapping(refreshToken);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
findOriginalToken(newToken) {
|
|
158
|
+
for (const [oldToken, mappedNewToken] of this.tokenRotationMap.entries()) {
|
|
159
|
+
if (mappedNewToken === newToken) {
|
|
160
|
+
return oldToken;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
cleanupRotationMapping(token) {
|
|
166
|
+
this.tokenRotationMap.delete(token);
|
|
167
|
+
for (const [oldToken, newToken] of this.tokenRotationMap.entries()) {
|
|
168
|
+
if (newToken === token) {
|
|
169
|
+
this.tokenRotationMap.delete(oldToken);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
async executeRefreshWithRotationTracking(refreshToken) {
|
|
174
|
+
const result = await this.executeRefresh(refreshToken);
|
|
175
|
+
if (result.type === "success" && result.refresh !== refreshToken) {
|
|
176
|
+
this.tokenRotationMap.set(refreshToken, result.refresh);
|
|
177
|
+
log.info("Token rotated during refresh", {
|
|
178
|
+
oldTokenSuffix: refreshToken.slice(-6),
|
|
179
|
+
newTokenSuffix: result.refresh.slice(-6),
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Execute the actual refresh and log results.
|
|
186
|
+
*/
|
|
187
|
+
async executeRefresh(refreshToken) {
|
|
188
|
+
const startTime = Date.now();
|
|
189
|
+
log.info("Starting token refresh", { tokenSuffix: refreshToken.slice(-6) });
|
|
190
|
+
try {
|
|
191
|
+
const result = await refreshAccessToken(refreshToken);
|
|
192
|
+
const duration = Date.now() - startTime;
|
|
193
|
+
if (result.type === "success") {
|
|
194
|
+
log.info("Token refresh succeeded", {
|
|
195
|
+
tokenSuffix: refreshToken.slice(-6),
|
|
196
|
+
durationMs: duration,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
log.warn("Token refresh failed", {
|
|
201
|
+
tokenSuffix: refreshToken.slice(-6),
|
|
202
|
+
reason: result.reason,
|
|
203
|
+
durationMs: duration,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
const duration = Date.now() - startTime;
|
|
210
|
+
log.error("Token refresh threw exception", {
|
|
211
|
+
tokenSuffix: refreshToken.slice(-6),
|
|
212
|
+
error: error?.message ?? String(error),
|
|
213
|
+
durationMs: duration,
|
|
214
|
+
});
|
|
215
|
+
return {
|
|
216
|
+
type: "failed",
|
|
217
|
+
reason: "network_error",
|
|
218
|
+
message: error?.message ?? "Unknown error during refresh",
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Remove stale entries that have been pending too long.
|
|
224
|
+
* This prevents memory leaks from stuck or abandoned refresh operations.
|
|
225
|
+
*/
|
|
226
|
+
cleanup() {
|
|
227
|
+
const now = Date.now();
|
|
228
|
+
const staleTokens = [];
|
|
229
|
+
for (const [token, entry] of this.pending.entries()) {
|
|
230
|
+
if (now - entry.startedAt > this.maxEntryAgeMs) {
|
|
231
|
+
staleTokens.push(token);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
for (const token of staleTokens) {
|
|
235
|
+
// istanbul ignore next -- defensive: token always exists in pending at this point (not yet deleted)
|
|
236
|
+
const ageMs = now - (this.pending.get(token)?.startedAt ?? now);
|
|
237
|
+
log.warn("Removing stale refresh entry", {
|
|
238
|
+
tokenSuffix: token.slice(-6),
|
|
239
|
+
ageMs,
|
|
240
|
+
});
|
|
241
|
+
this.pending.delete(token);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Check if there's an in-flight refresh for a given token.
|
|
246
|
+
* @param refreshToken - The refresh token to check
|
|
247
|
+
* @returns True if refresh is in progress
|
|
248
|
+
*/
|
|
249
|
+
isRefreshing(refreshToken) {
|
|
250
|
+
return this.pending.has(refreshToken);
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Get the number of pending refresh operations.
|
|
254
|
+
* Useful for debugging and monitoring.
|
|
255
|
+
*/
|
|
256
|
+
get pendingCount() {
|
|
257
|
+
return this.pending.size;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Clear all pending entries (primarily for testing).
|
|
261
|
+
*/
|
|
262
|
+
clear() {
|
|
263
|
+
this.pending.clear();
|
|
264
|
+
this.tokenRotationMap.clear();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
// ============================================================================
|
|
268
|
+
// Singleton Instance
|
|
269
|
+
// ============================================================================
|
|
270
|
+
let refreshQueueInstance = null;
|
|
271
|
+
/**
|
|
272
|
+
* Get the singleton RefreshQueue instance.
|
|
273
|
+
* @param maxEntryAgeMs - Maximum age for pending entries (only used on first call)
|
|
274
|
+
* @returns The global RefreshQueue instance
|
|
275
|
+
*/
|
|
276
|
+
export function getRefreshQueue(maxEntryAgeMs) {
|
|
277
|
+
if (!refreshQueueInstance) {
|
|
278
|
+
refreshQueueInstance = new RefreshQueue(maxEntryAgeMs);
|
|
279
|
+
}
|
|
280
|
+
return refreshQueueInstance;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Reset the singleton instance (primarily for testing).
|
|
284
|
+
*/
|
|
285
|
+
export function resetRefreshQueue() {
|
|
286
|
+
refreshQueueInstance?.clear();
|
|
287
|
+
refreshQueueInstance = null;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Convenience function to refresh a token using the singleton queue.
|
|
291
|
+
* @param refreshToken - The refresh token to use
|
|
292
|
+
* @returns Token result
|
|
293
|
+
*/
|
|
294
|
+
export async function queuedRefresh(refreshToken) {
|
|
295
|
+
return getRefreshQueue().refresh(refreshToken);
|
|
296
|
+
}
|
|
297
|
+
//# sourceMappingURL=refresh-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh-queue.js","sourceRoot":"","sources":["../../lib/refresh-queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { CooldownReason } from "../storage.js";
|
|
2
|
+
export type FailureKind = "auth-refresh" | "network" | "server" | "rate-limit" | "empty-response";
|
|
3
|
+
export type FailoverMode = "aggressive" | "balanced" | "conservative";
|
|
4
|
+
export interface FailurePolicyInput {
|
|
5
|
+
kind: FailureKind;
|
|
6
|
+
consecutiveAuthFailures?: number;
|
|
7
|
+
maxAuthFailuresBeforeRemoval?: number;
|
|
8
|
+
serverRetryAfterMs?: number;
|
|
9
|
+
failoverMode?: FailoverMode;
|
|
10
|
+
}
|
|
11
|
+
export interface FailurePolicyDecision {
|
|
12
|
+
rotateAccount: boolean;
|
|
13
|
+
refundToken: boolean;
|
|
14
|
+
recordFailure: boolean;
|
|
15
|
+
markRateLimited: boolean;
|
|
16
|
+
removeAccount: boolean;
|
|
17
|
+
cooldownMs?: number;
|
|
18
|
+
cooldownReason?: CooldownReason;
|
|
19
|
+
retrySameAccount: boolean;
|
|
20
|
+
retryDelayMs?: number;
|
|
21
|
+
handoffStrategy: "soft" | "hard";
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Compute a FailurePolicyDecision that specifies how to handle a failure described by `input`.
|
|
25
|
+
*
|
|
26
|
+
* Evaluates the provided failure kind and related hints to decide whether to rotate or remove an account,
|
|
27
|
+
* refund a token, record or mark the failure, apply a cooldown, retry on the same account (and with what delay),
|
|
28
|
+
* and choose a handoff strategy.
|
|
29
|
+
*
|
|
30
|
+
* Concurrency assumptions: this function is pure and safe to call concurrently from multiple threads/processes.
|
|
31
|
+
* Filesystem notes: no filesystem access is performed (no Windows-specific behavior).
|
|
32
|
+
* Token redaction: decisions may set `refundToken` to true/false but this function does not log or expose token values.
|
|
33
|
+
*
|
|
34
|
+
* @param input - Configuration and hints for the failure policy (must include `kind`; may include `consecutiveAuthFailures`, `maxAuthFailuresBeforeRemoval`, `serverRetryAfterMs`, and `failoverMode`).
|
|
35
|
+
* @param overrides - Optional environment overrides: `networkCooldownMs` and `serverCooldownMs` adjust fallback cooldown values.
|
|
36
|
+
* @returns A FailurePolicyDecision object describing rotation, refund, recording, rate-limit marking, removal, cooldown and retry behavior, and the chosen handoff strategy.
|
|
37
|
+
*/
|
|
38
|
+
export declare function evaluateFailurePolicy(input: FailurePolicyInput, overrides?: {
|
|
39
|
+
networkCooldownMs?: number;
|
|
40
|
+
serverCooldownMs?: number;
|
|
41
|
+
}): FailurePolicyDecision;
|
|
42
|
+
//# sourceMappingURL=failure-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failure-policy.d.ts","sourceRoot":"","sources":["../../../lib/request/failure-policy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,MAAM,MAAM,WAAW,GACpB,cAAc,GACd,SAAS,GACT,QAAQ,GACR,YAAY,GACZ,gBAAgB,CAAC;AAEpB,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,UAAU,GAAG,cAAc,CAAC;AAEtE,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,WAAW,CAAC;IAClB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,4BAA4B,CAAC,EAAE,MAAM,CAAC;IACtC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,YAAY,CAAC,EAAE,YAAY,CAAC;CAC5B;AAED,MAAM,WAAW,qBAAqB;IACrC,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,GAAG,MAAM,CAAC;CACjC;AA4BD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CACpC,KAAK,EAAE,kBAAkB,EACzB,SAAS,CAAC,EAAE;IACX,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B,GACC,qBAAqB,CAoGvB"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { ACCOUNT_LIMITS } from "../constants.js";
|
|
2
|
+
const DEFAULT_NETWORK_COOLDOWN_MS = 6_000;
|
|
3
|
+
const DEFAULT_SERVER_COOLDOWN_MS = 4_000;
|
|
4
|
+
const NETWORK_RETRY_DELAY_MS = {
|
|
5
|
+
aggressive: 0,
|
|
6
|
+
balanced: 250,
|
|
7
|
+
conservative: 900,
|
|
8
|
+
};
|
|
9
|
+
const EMPTY_RESPONSE_RETRY_DELAY_MS = {
|
|
10
|
+
aggressive: 0,
|
|
11
|
+
balanced: 200,
|
|
12
|
+
conservative: 600,
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Selects the failover mode provided on the input or uses `aggressive` when not set.
|
|
16
|
+
*
|
|
17
|
+
* This is a pure, concurrency-safe helper with no filesystem side effects (including on Windows)
|
|
18
|
+
* and does not log or expose tokens from its input.
|
|
19
|
+
*
|
|
20
|
+
* @param input - Failure policy input that may contain an optional `failoverMode`
|
|
21
|
+
* @returns The chosen failover mode: `aggressive`, `balanced`, or `conservative` (defaults to `aggressive`)
|
|
22
|
+
*/
|
|
23
|
+
function getFailoverMode(input) {
|
|
24
|
+
return input.failoverMode ?? "aggressive";
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Compute a FailurePolicyDecision that specifies how to handle a failure described by `input`.
|
|
28
|
+
*
|
|
29
|
+
* Evaluates the provided failure kind and related hints to decide whether to rotate or remove an account,
|
|
30
|
+
* refund a token, record or mark the failure, apply a cooldown, retry on the same account (and with what delay),
|
|
31
|
+
* and choose a handoff strategy.
|
|
32
|
+
*
|
|
33
|
+
* Concurrency assumptions: this function is pure and safe to call concurrently from multiple threads/processes.
|
|
34
|
+
* Filesystem notes: no filesystem access is performed (no Windows-specific behavior).
|
|
35
|
+
* Token redaction: decisions may set `refundToken` to true/false but this function does not log or expose token values.
|
|
36
|
+
*
|
|
37
|
+
* @param input - Configuration and hints for the failure policy (must include `kind`; may include `consecutiveAuthFailures`, `maxAuthFailuresBeforeRemoval`, `serverRetryAfterMs`, and `failoverMode`).
|
|
38
|
+
* @param overrides - Optional environment overrides: `networkCooldownMs` and `serverCooldownMs` adjust fallback cooldown values.
|
|
39
|
+
* @returns A FailurePolicyDecision object describing rotation, refund, recording, rate-limit marking, removal, cooldown and retry behavior, and the chosen handoff strategy.
|
|
40
|
+
*/
|
|
41
|
+
export function evaluateFailurePolicy(input, overrides) {
|
|
42
|
+
switch (input.kind) {
|
|
43
|
+
case "auth-refresh": {
|
|
44
|
+
const failures = Math.max(0, Math.floor(input.consecutiveAuthFailures ?? 0));
|
|
45
|
+
const maxFailures = Math.max(1, Math.floor(input.maxAuthFailuresBeforeRemoval ?? ACCOUNT_LIMITS.MAX_AUTH_FAILURES_BEFORE_REMOVAL));
|
|
46
|
+
return {
|
|
47
|
+
rotateAccount: true,
|
|
48
|
+
refundToken: false,
|
|
49
|
+
recordFailure: false,
|
|
50
|
+
markRateLimited: false,
|
|
51
|
+
removeAccount: failures >= maxFailures,
|
|
52
|
+
cooldownMs: ACCOUNT_LIMITS.AUTH_FAILURE_COOLDOWN_MS,
|
|
53
|
+
cooldownReason: "auth-failure",
|
|
54
|
+
retrySameAccount: false,
|
|
55
|
+
handoffStrategy: "hard",
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
case "network": {
|
|
59
|
+
const mode = getFailoverMode(input);
|
|
60
|
+
const cooldownMs = Math.max(0, Math.floor(overrides?.networkCooldownMs ?? DEFAULT_NETWORK_COOLDOWN_MS));
|
|
61
|
+
const retryDelayMs = NETWORK_RETRY_DELAY_MS[mode];
|
|
62
|
+
const retrySameAccount = retryDelayMs > 0;
|
|
63
|
+
return {
|
|
64
|
+
rotateAccount: !retrySameAccount,
|
|
65
|
+
refundToken: true,
|
|
66
|
+
recordFailure: true,
|
|
67
|
+
markRateLimited: false,
|
|
68
|
+
removeAccount: false,
|
|
69
|
+
cooldownMs,
|
|
70
|
+
cooldownReason: cooldownMs > 0 ? "network-error" : undefined,
|
|
71
|
+
retrySameAccount,
|
|
72
|
+
retryDelayMs: retrySameAccount ? retryDelayMs : undefined,
|
|
73
|
+
handoffStrategy: "soft",
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
case "server": {
|
|
77
|
+
const mode = getFailoverMode(input);
|
|
78
|
+
const retryAfterMs = Math.max(0, Math.floor(input.serverRetryAfterMs ?? 0));
|
|
79
|
+
const fallbackCooldown = Math.max(0, Math.floor(overrides?.serverCooldownMs ?? DEFAULT_SERVER_COOLDOWN_MS));
|
|
80
|
+
const cooldownMs = retryAfterMs > 0 ? retryAfterMs : fallbackCooldown;
|
|
81
|
+
const retrySameAccount = mode === "conservative" && retryAfterMs <= 0;
|
|
82
|
+
return {
|
|
83
|
+
rotateAccount: !retrySameAccount,
|
|
84
|
+
refundToken: true,
|
|
85
|
+
recordFailure: true,
|
|
86
|
+
markRateLimited: false,
|
|
87
|
+
removeAccount: false,
|
|
88
|
+
cooldownMs,
|
|
89
|
+
cooldownReason: cooldownMs > 0 ? "network-error" : undefined,
|
|
90
|
+
retrySameAccount,
|
|
91
|
+
retryDelayMs: retrySameAccount ? 500 : undefined,
|
|
92
|
+
handoffStrategy: "hard",
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
case "rate-limit": {
|
|
96
|
+
return {
|
|
97
|
+
rotateAccount: true,
|
|
98
|
+
refundToken: false,
|
|
99
|
+
recordFailure: false,
|
|
100
|
+
markRateLimited: true,
|
|
101
|
+
removeAccount: false,
|
|
102
|
+
retrySameAccount: false,
|
|
103
|
+
handoffStrategy: "hard",
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
case "empty-response": {
|
|
107
|
+
const mode = getFailoverMode(input);
|
|
108
|
+
const retryDelayMs = EMPTY_RESPONSE_RETRY_DELAY_MS[mode];
|
|
109
|
+
const retrySameAccount = retryDelayMs > 0;
|
|
110
|
+
return {
|
|
111
|
+
rotateAccount: !retrySameAccount,
|
|
112
|
+
refundToken: true,
|
|
113
|
+
recordFailure: true,
|
|
114
|
+
markRateLimited: false,
|
|
115
|
+
removeAccount: false,
|
|
116
|
+
retrySameAccount,
|
|
117
|
+
retryDelayMs: retrySameAccount ? retryDelayMs : undefined,
|
|
118
|
+
handoffStrategy: "soft",
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
default:
|
|
122
|
+
return {
|
|
123
|
+
rotateAccount: true,
|
|
124
|
+
refundToken: true,
|
|
125
|
+
recordFailure: true,
|
|
126
|
+
markRateLimited: false,
|
|
127
|
+
removeAccount: false,
|
|
128
|
+
retrySameAccount: false,
|
|
129
|
+
handoffStrategy: "hard",
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=failure-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failure-policy.js","sourceRoot":"","sources":["../../../lib/request/failure-policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAiCjD,MAAM,2BAA2B,GAAG,KAAK,CAAC;AAC1C,MAAM,0BAA0B,GAAG,KAAK,CAAC;AACzC,MAAM,sBAAsB,GAAiC;IAC5D,UAAU,EAAE,CAAC;IACb,QAAQ,EAAE,GAAG;IACb,YAAY,EAAE,GAAG;CACjB,CAAC;AACF,MAAM,6BAA6B,GAAiC;IACnE,UAAU,EAAE,CAAC;IACb,QAAQ,EAAE,GAAG;IACb,YAAY,EAAE,GAAG;CACjB,CAAC;AAEF;;;;;;;;GAQG;AACH,SAAS,eAAe,CAAC,KAAyB;IACjD,OAAO,KAAK,CAAC,YAAY,IAAI,YAAY,CAAC;AAC3C,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,qBAAqB,CACpC,KAAyB,EACzB,SAGC;IAED,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,cAAc,CAAC,CAAC,CAAC;YACrB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,uBAAuB,IAAI,CAAC,CAAC,CAAC,CAAC;YAC7E,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAC3B,CAAC,EACD,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,4BAA4B,IAAI,cAAc,CAAC,gCAAgC,CAAC,CACjG,CAAC;YACF,OAAO;gBACN,aAAa,EAAE,IAAI;gBACnB,WAAW,EAAE,KAAK;gBAClB,aAAa,EAAE,KAAK;gBACpB,eAAe,EAAE,KAAK;gBACtB,aAAa,EAAE,QAAQ,IAAI,WAAW;gBACtC,UAAU,EAAE,cAAc,CAAC,wBAAwB;gBACnD,cAAc,EAAE,cAAc;gBAC9B,gBAAgB,EAAE,KAAK;gBACvB,eAAe,EAAE,MAAM;aACvB,CAAC;QACH,CAAC;QACD,KAAK,SAAS,CAAC,CAAC,CAAC;YAChB,MAAM,IAAI,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;YACpC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAC1B,CAAC,EACD,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,iBAAiB,IAAI,2BAA2B,CAAC,CACvE,CAAC;YACF,MAAM,YAAY,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC;YAClD,MAAM,gBAAgB,GAAG,YAAY,GAAG,CAAC,CAAC;YAC1C,OAAO;gBACN,aAAa,EAAE,CAAC,gBAAgB;gBAChC,WAAW,EAAE,IAAI;gBACjB,aAAa,EAAE,IAAI;gBACnB,eAAe,EAAE,KAAK;gBACtB,aAAa,EAAE,KAAK;gBACpB,UAAU;gBACV,cAAc,EAAE,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS;gBAC5D,gBAAgB;gBAChB,YAAY,EAAE,gBAAgB,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS;gBACzD,eAAe,EAAE,MAAM;aACvB,CAAC;QACH,CAAC;QACD,KAAK,QAAQ,CAAC,CAAC,CAAC;YACf,MAAM,IAAI,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;YACpC,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,kBAAkB,IAAI,CAAC,CAAC,CAAC,CAAC;YAC5E,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAChC,CAAC,EACD,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,gBAAgB,IAAI,0BAA0B,CAAC,CACrE,CAAC;YACF,MAAM,UAAU,GAAG,YAAY,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,gBAAgB,CAAC;YACtE,MAAM,gBAAgB,GAAG,IAAI,KAAK,cAAc,IAAI,YAAY,IAAI,CAAC,CAAC;YACtE,OAAO;gBACN,aAAa,EAAE,CAAC,gBAAgB;gBAChC,WAAW,EAAE,IAAI;gBACjB,aAAa,EAAE,IAAI;gBACnB,eAAe,EAAE,KAAK;gBACtB,aAAa,EAAE,KAAK;gBACpB,UAAU;gBACV,cAAc,EAAE,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS;gBAC5D,gBAAgB;gBAChB,YAAY,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS;gBAChD,eAAe,EAAE,MAAM;aACvB,CAAC;QACH,CAAC;QACD,KAAK,YAAY,CAAC,CAAC,CAAC;YACnB,OAAO;gBACN,aAAa,EAAE,IAAI;gBACnB,WAAW,EAAE,KAAK;gBAClB,aAAa,EAAE,KAAK;gBACpB,eAAe,EAAE,IAAI;gBACrB,aAAa,EAAE,KAAK;gBACpB,gBAAgB,EAAE,KAAK;gBACvB,eAAe,EAAE,MAAM;aACvB,CAAC;QACH,CAAC;QACD,KAAK,gBAAgB,CAAC,CAAC,CAAC;YACvB,MAAM,IAAI,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;YACpC,MAAM,YAAY,GAAG,6BAA6B,CAAC,IAAI,CAAC,CAAC;YACzD,MAAM,gBAAgB,GAAG,YAAY,GAAG,CAAC,CAAC;YAC1C,OAAO;gBACN,aAAa,EAAE,CAAC,gBAAgB;gBAChC,WAAW,EAAE,IAAI;gBACjB,aAAa,EAAE,IAAI;gBACnB,eAAe,EAAE,KAAK;gBACtB,aAAa,EAAE,KAAK;gBACpB,gBAAgB;gBAChB,YAAY,EAAE,gBAAgB,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS;gBACzD,eAAe,EAAE,MAAM;aACvB,CAAC;QACH,CAAC;QACD;YACC,OAAO;gBACN,aAAa,EAAE,IAAI;gBACnB,WAAW,EAAE,IAAI;gBACjB,aAAa,EAAE,IAAI;gBACnB,eAAe,EAAE,KAAK;gBACtB,aAAa,EAAE,KAAK;gBACpB,gBAAgB,EAAE,KAAK;gBACvB,eAAe,EAAE,MAAM;aACvB,CAAC;IACJ,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper functions for the custom fetch implementation
|
|
3
|
+
* These functions break down the complex fetch logic into manageable, testable units
|
|
4
|
+
*/
|
|
5
|
+
import type { Auth, OpencodeClient } from "@opencode-ai/sdk";
|
|
6
|
+
import type { UserConfig, RequestBody } from "../types.js";
|
|
7
|
+
export interface RateLimitInfo {
|
|
8
|
+
retryAfterMs: number;
|
|
9
|
+
code?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface EntitlementError {
|
|
12
|
+
isEntitlement: true;
|
|
13
|
+
code: string;
|
|
14
|
+
message: string;
|
|
15
|
+
}
|
|
16
|
+
export declare const DEFAULT_UNSUPPORTED_CODEX_FALLBACK_CHAIN: Record<string, string[]>;
|
|
17
|
+
export interface UnsupportedCodexModelInfo {
|
|
18
|
+
isUnsupported: boolean;
|
|
19
|
+
code?: string;
|
|
20
|
+
message?: string;
|
|
21
|
+
unsupportedModel?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface ResolveUnsupportedCodexFallbackOptions {
|
|
24
|
+
requestedModel: string | undefined;
|
|
25
|
+
errorBody: unknown;
|
|
26
|
+
attemptedModels?: Iterable<string>;
|
|
27
|
+
fallbackOnUnsupportedCodexModel: boolean;
|
|
28
|
+
fallbackToGpt52OnUnsupportedGpt53: boolean;
|
|
29
|
+
customChain?: Record<string, string[]>;
|
|
30
|
+
}
|
|
31
|
+
export declare function extractUnsupportedCodexModelFromText(bodyText: string): string | undefined;
|
|
32
|
+
export declare function getUnsupportedCodexModelInfo(errorBody: unknown): UnsupportedCodexModelInfo;
|
|
33
|
+
export declare function resolveUnsupportedCodexFallbackModel(options: ResolveUnsupportedCodexFallbackOptions): string | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Returns true when the legacy `gpt-5.3-codex -> gpt-5.2-codex` edge is available.
|
|
36
|
+
*/
|
|
37
|
+
export declare function shouldFallbackToGpt52OnUnsupportedGpt53(requestedModel: string | undefined, errorBody: unknown): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Detects whether an error code or response body indicates an entitlement/subscription issue for Codex models.
|
|
40
|
+
*
|
|
41
|
+
* Entitlement errors signal that the requested feature is not included in the user's plan and should not be treated as rate limits.
|
|
42
|
+
* This function is pure and safe to call concurrently; it performs no filesystem access (including on Windows) and does not read or redact tokens — callers must avoid passing sensitive credentials in `code` or `bodyText`.
|
|
43
|
+
*
|
|
44
|
+
* @param code - The error code string returned by the service
|
|
45
|
+
* @param bodyText - The response body text to inspect for entitlement-related phrases
|
|
46
|
+
* @returns `true` if the combined `code` or `bodyText` indicates an entitlement/subscription issue, `false` otherwise
|
|
47
|
+
*/
|
|
48
|
+
export declare function isEntitlementError(code: string, bodyText: string): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Constructs a standardized 403 entitlement error Response indicating the user lacks access to Codex models.
|
|
51
|
+
*
|
|
52
|
+
* This function returns a JSON Response with an `error` payload containing a user-facing message, a
|
|
53
|
+
* `type` of `"entitlement_error"`, and a `code` of `"usage_not_included"`. The message suggests checking
|
|
54
|
+
* account/workspace access and re-authenticating with `codex login`.
|
|
55
|
+
*
|
|
56
|
+
* Concurrency: stateless and safe to call concurrently from multiple threads or requests.
|
|
57
|
+
* Windows filesystem behavior: none (function does not access the filesystem).
|
|
58
|
+
* Token redaction: any tokens are not included in the generated payload; do not pass sensitive tokens in `_bodyText`.
|
|
59
|
+
*
|
|
60
|
+
* @param _bodyText - Original response body text (accepted for compatibility; ignored when building the response)
|
|
61
|
+
* @returns A 403 Response with a JSON body describing the entitlement error and guidance for resolving it
|
|
62
|
+
*/
|
|
63
|
+
export declare function createEntitlementErrorResponse(_bodyText: string): Response;
|
|
64
|
+
export interface ErrorHandlingResult {
|
|
65
|
+
response: Response;
|
|
66
|
+
rateLimit?: RateLimitInfo;
|
|
67
|
+
errorBody?: unknown;
|
|
68
|
+
}
|
|
69
|
+
export interface ErrorHandlingOptions {
|
|
70
|
+
requestCorrelationId?: string;
|
|
71
|
+
threadId?: string;
|
|
72
|
+
}
|
|
73
|
+
export interface ErrorDiagnostics {
|
|
74
|
+
requestId?: string;
|
|
75
|
+
cfRay?: string;
|
|
76
|
+
correlationId?: string;
|
|
77
|
+
threadId?: string;
|
|
78
|
+
httpStatus?: number;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Determines if the current auth token needs to be refreshed
|
|
82
|
+
* @param auth - Current authentication state
|
|
83
|
+
* @returns True if token is expired or invalid
|
|
84
|
+
*/
|
|
85
|
+
export declare function shouldRefreshToken(auth: Auth, skewMs?: number): boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Refreshes the OAuth token and updates stored credentials
|
|
88
|
+
* @param currentAuth - Current auth state
|
|
89
|
+
* @param client - Opencode client for updating stored credentials
|
|
90
|
+
* @returns Updated auth (throws on failure)
|
|
91
|
+
*/
|
|
92
|
+
export declare function refreshAndUpdateToken(currentAuth: Auth, client: OpencodeClient): Promise<Auth>;
|
|
93
|
+
/**
|
|
94
|
+
* Extracts URL string from various request input types
|
|
95
|
+
* @param input - Request input (string, URL, or Request object)
|
|
96
|
+
* @returns URL string
|
|
97
|
+
*/
|
|
98
|
+
export declare function extractRequestUrl(input: Request | string | URL): string;
|
|
99
|
+
/**
|
|
100
|
+
* Rewrites OpenAI API URLs to Codex backend URLs
|
|
101
|
+
* @param url - Original URL
|
|
102
|
+
* @returns Rewritten URL for Codex backend
|
|
103
|
+
*/
|
|
104
|
+
export declare function rewriteUrlForCodex(url: string): string;
|
|
105
|
+
/**
|
|
106
|
+
* Transforms request body and logs the transformation
|
|
107
|
+
* Fetches model-specific Codex instructions based on the request model
|
|
108
|
+
*
|
|
109
|
+
* @param init - Request init options
|
|
110
|
+
* @param url - Request URL
|
|
111
|
+
* @param userConfig - User configuration
|
|
112
|
+
* @param codexMode - Enable CODEX_MODE (bridge prompt instead of tool remap)
|
|
113
|
+
* @param parsedBody - Pre-parsed body to avoid double JSON.parse (optional)
|
|
114
|
+
* @returns Transformed body and updated init, or undefined if no body
|
|
115
|
+
*/
|
|
116
|
+
export declare function transformRequestForCodex(init: RequestInit | undefined, url: string, userConfig: UserConfig, codexMode?: boolean, parsedBody?: Record<string, unknown>, options?: {
|
|
117
|
+
fastSession?: boolean;
|
|
118
|
+
fastSessionStrategy?: "hybrid" | "always";
|
|
119
|
+
fastSessionMaxInputItems?: number;
|
|
120
|
+
}): Promise<{
|
|
121
|
+
body: RequestBody;
|
|
122
|
+
updatedInit: RequestInit;
|
|
123
|
+
} | undefined>;
|
|
124
|
+
/**
|
|
125
|
+
* Creates headers for Codex API requests
|
|
126
|
+
* @param init - Request init options
|
|
127
|
+
* @param accountId - ChatGPT account ID
|
|
128
|
+
* @param accessToken - OAuth access token
|
|
129
|
+
* @returns Headers object with all required Codex headers
|
|
130
|
+
*/
|
|
131
|
+
export declare function createCodexHeaders(init: RequestInit | undefined, accountId: string, accessToken: string, opts?: {
|
|
132
|
+
model?: string;
|
|
133
|
+
promptCacheKey?: string;
|
|
134
|
+
}): Headers;
|
|
135
|
+
/**
|
|
136
|
+
* Handles error responses from the Codex API
|
|
137
|
+
* @param response - Error response from API
|
|
138
|
+
* @returns Original response or mapped retryable response
|
|
139
|
+
*/
|
|
140
|
+
export declare function handleErrorResponse(response: Response, options?: ErrorHandlingOptions): Promise<ErrorHandlingResult>;
|
|
141
|
+
/**
|
|
142
|
+
* Handles successful responses from the Codex API
|
|
143
|
+
* Converts SSE to JSON for non-streaming requests (generateText)
|
|
144
|
+
* Passes through SSE for streaming requests (streamText)
|
|
145
|
+
* @param response - Success response from API
|
|
146
|
+
* @param isStreaming - Whether this is a streaming request (stream=true in body)
|
|
147
|
+
* @returns Processed response (SSE→JSON for non-streaming, stream for streaming)
|
|
148
|
+
*/
|
|
149
|
+
export declare function handleSuccessResponse(response: Response, isStreaming: boolean, options?: {
|
|
150
|
+
streamStallTimeoutMs?: number;
|
|
151
|
+
}): Promise<Response>;
|
|
152
|
+
//# sourceMappingURL=fetch-helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-helpers.d.ts","sourceRoot":"","sources":["../../../lib/request/fetch-helpers.ts"],"names":[],"mappings":"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"}
|