@cortexkit/opencode-openai-auth 0.4.2 → 0.5.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 +8 -4
- package/dist/cli.js +1 -1
- package/dist/codex-http.d.ts +1 -1
- package/dist/commands.d.ts +6 -0
- package/dist/core/accounts.d.ts +6 -1
- package/dist/core/background-quota-refresh.d.ts +46 -0
- package/dist/core/cachekeep.d.ts +4 -0
- package/dist/core/oauth.d.ts +1 -1
- package/dist/core/quota-manager.d.ts +18 -15
- package/dist/core/refresh-all-quota.d.ts +12 -1
- package/dist/core/sticky-routing.d.ts +43 -0
- package/dist/dump.d.ts +1 -0
- package/dist/{index-x1c39tzz.js → index-6rnw2cgs.js} +1 -1
- package/dist/index-89mh4ahn.js +89 -0
- package/dist/index-rfgyxxxq.js +1 -0
- package/dist/index-s5fbqqwx.js +3 -0
- package/dist/index-vrdc90yd.js +6 -0
- package/dist/index-wnxfzjfr.js +2 -0
- package/dist/{index-jpx7h5gc.js → index-wpd7mg3p.js} +1 -1
- package/dist/index-zkzb0twr.js +2 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +101 -83
- package/dist/{quota-normalize-02mn21p4.js → quota-normalize-n02dfdx0.js} +1 -1
- package/dist/raw-ws-bun.d.ts +2 -0
- package/dist/raw-ws-node.d.ts +2 -0
- package/dist/raw-ws-upgrade.d.ts +2 -0
- package/dist/rpc/port-file.d.ts +4 -0
- package/dist/rpc/port-file.js +1 -1
- package/dist/rpc/protocol.d.ts +1 -0
- package/dist/rpc/rpc-client.js +1 -1
- package/dist/rpc/rpc-dir.d.ts +6 -0
- package/dist/rpc/rpc-dir.js +1 -1
- package/dist/rpc/rpc-server.d.ts +2 -0
- package/dist/sidebar-state.d.ts +46 -5
- package/dist/sidebar-state.js +1 -1
- package/dist/{token-dab1ama0.js → token-5hdnazxp.js} +1 -1
- package/dist/token-util-3d2nj45d.js +1 -0
- package/dist/tui-preferences.js +1 -1
- package/dist/tui.d.ts +9 -0
- package/dist/version.d.ts +1 -1
- package/dist/ws-pool.d.ts +2 -11
- package/dist/ws.d.ts +7 -2
- package/package.json +2 -2
- package/src/rpc/port-file.ts +115 -8
- package/src/rpc/protocol.ts +1 -0
- package/src/rpc/rpc-dir.ts +31 -7
- package/src/rpc/rpc-server.ts +18 -2
- package/src/sidebar-state.ts +771 -23
- package/src/tui/command-dialogs.tsx +25 -3
- package/src/tui-compiled/rpc/port-file.ts +115 -8
- package/src/tui-compiled/rpc/protocol.ts +1 -0
- package/src/tui-compiled/rpc/rpc-dir.ts +31 -7
- package/src/tui-compiled/sidebar-state.ts +771 -23
- package/src/tui-compiled/tui/command-dialogs.tsx +17 -4
- package/src/tui-compiled/tui.tsx +50 -15
- package/src/tui.tsx +57 -19
- package/dist/index-72cas5ht.js +0 -7
- package/dist/index-dc7za61x.js +0 -89
- package/dist/index-hjgzqz1c.js +0 -1
- package/dist/index-qnd9x5jh.js +0 -2
- package/dist/index-wpk440dz.js +0 -2
- package/dist/token-util-41n40m0y.js +0 -1
package/README.md
CHANGED
|
@@ -26,9 +26,9 @@ Restart OpenCode after changing plugin config, then authenticate:
|
|
|
26
26
|
- Codex request rewriting for OAuth requests, with Codex identity parity.
|
|
27
27
|
- OAuth model filtering and zero-cost display.
|
|
28
28
|
- Prompt-cache stabilizer (`web_search`) that keeps tool-continuation requests on the backend's cached path (on by default).
|
|
29
|
-
- Multiple ChatGPT accounts with automatic reactive fallback on rate limits,
|
|
29
|
+
- Multiple ChatGPT accounts with automatic reactive fallback on rate limits, `main-first`, `fallback-first`, or sticky-balanced routing, and a per-account quota killswitch.
|
|
30
30
|
- Per-turn quota tracking (5-hour + weekly windows) on both transports, with a sidebar readout and an explicit all-accounts refresh.
|
|
31
|
-
- Idle prompt-cache keep-warm, with an optional subagent mode.
|
|
31
|
+
- Idle prompt-cache keep-warm, with an optional subagent mode and main-only sustain mode.
|
|
32
32
|
- Leveled, secret-redacting, rotating log file.
|
|
33
33
|
- Interactive in-TUI control surfaces for every command, plus an `openai-auth` CLI for managing fallback accounts headlessly.
|
|
34
34
|
- Optional OpenAI Responses WebSocket transport (HTTP is the default).
|
|
@@ -41,9 +41,9 @@ Each opens an interactive dialog in the TUI and also accepts explicit arguments:
|
|
|
41
41
|
| --- | --- | --- |
|
|
42
42
|
| `/openai-quota` | — | Show 5h + weekly quota for all accounts. |
|
|
43
43
|
| `/openai-account` | `add [label]` · `remove <id>` · `order <a> <b>` | Manage main + fallback accounts. |
|
|
44
|
-
| `/openai-routing` | `main-first` · `fallback-first` | Routing
|
|
44
|
+
| `/openai-routing` | `main-first` · `fallback-first` · `sticky-balanced` · `reset` | Routing order, sticky balanced session pins, or clear the current pin. |
|
|
45
45
|
| `/openai-killswitch` | `on` · `off` · `set <acct>:<5h>,<1w> ...` | Hard-block accounts below quota thresholds. |
|
|
46
|
-
| `/openai-cachekeep` | `on` · `off` · `subagents on` · `subagents off` | Idle prompt-cache keep-warm. |
|
|
46
|
+
| `/openai-cachekeep` | `on` · `off` · `subagents on` · `subagents off` · `sustain on` · `sustain off` | Idle prompt-cache keep-warm; sustain bypasses only main idle pruning. |
|
|
47
47
|
| `/openai-logging` | `<level>` | Set log level live. |
|
|
48
48
|
| `/openai-dump` | `on` · `off` | Toggle transport request dumps. |
|
|
49
49
|
|
|
@@ -71,6 +71,10 @@ Settings resolve as environment variable → config file (`~/.config/opencode/op
|
|
|
71
71
|
|
|
72
72
|
See the [repository README](https://github.com/cortexkit/openai-auth#readme) for transport differences and why `web_search` is needed.
|
|
73
73
|
|
|
74
|
+
`sticky-balanced` places a cold session by least projected quota pressure, then keeps its SHA-256-keyed sidebar-state pin for up to seven days. It does not rebalance mid-session or use a Retry-After hold; it migrates only after confirmed exhaustion or permanent auth failure. Stale or unknown quota is excluded from weighted placement; when the killswitch is enabled, accounts below their per-account threshold are also excluded from both weighted placement AND the mode-fallback fail-open branch — that branch otherwise orders by `resetCreditsApplicable` first, then configured order, then account id. Subagents have separate pins and reuse them when resumed.
|
|
75
|
+
|
|
76
|
+
`/openai-cachekeep sustain on|off` defaults off, applies only to main sessions, remains subject to the clock window and memory/LRU caps, and never warms non-active accounts. It costs about two GPT-5.6 warms per hour per session (about 1K output tokens/hour at about 99.4% cache hit); non-5.6 sessions warm about twelve times per hour. Before enabling it for a main-session model, preserve existing entries in `~/.config/cortexkit/magic-context.jsonc` and set that model's `cache_ttl` to `"never"`; Magic Context does not run in subagents. `sustain` means bypass main idle pruning, unlike the sibling anthropic plugin's `always`, which means ignore the clock schedule.
|
|
77
|
+
|
|
74
78
|
## License
|
|
75
79
|
|
|
76
80
|
MIT
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{F as q,J as B,K as V,r as Y,y as Z,z as $}from"./index-
|
|
2
|
+
import{F as q,J as B,K as V,r as Y,y as Z,z as $}from"./index-89mh4ahn.js";import"./index-vrdc90yd.js";import"./index-s5fbqqwx.js";import{Ga as X}from"./index-wpd7mg3p.js";import{execFileSync as E}from"node:child_process";function Q(C,H=process.platform,D=E){try{if(H==="win32"){D("cmd",["/c","start","",C],{stdio:"ignore",timeout:3000});return}D(H==="darwin"?"open":"xdg-open",[C],{stdio:"ignore",timeout:3000})}catch{}}function T(){console.log(`Usage:
|
|
3
3
|
npx @cortexkit/opencode-openai-auth login [--label <name>] [--headless]
|
|
4
4
|
npx @cortexkit/opencode-openai-auth list
|
|
5
5
|
npx @cortexkit/opencode-openai-auth remove <id>
|
package/dist/codex-http.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export declare function hasWebSocketResponsesLiteMetadata(body: BodyInit | null | undefined): boolean;
|
|
2
|
-
export declare function sanitizeHttpFallbackBody(body: BodyInit | null | undefined): string | ArrayBuffer | ArrayBufferView<ArrayBuffer> |
|
|
2
|
+
export declare function sanitizeHttpFallbackBody(body: BodyInit | null | undefined): string | ArrayBuffer | ArrayBufferView<ArrayBuffer> | Blob | FormData | ReadableStream<any> | URLSearchParams | null | undefined;
|
|
3
3
|
export declare function sanitizeHttpFallbackInit(init: RequestInit | undefined): RequestInit | undefined;
|
package/dist/commands.d.ts
CHANGED
|
@@ -44,8 +44,14 @@ export interface CommandContext {
|
|
|
44
44
|
setCacheKeepEnabled?: (enabled: boolean) => void;
|
|
45
45
|
/** Updates the live loader's persisted-subagent cachekeep gate. */
|
|
46
46
|
setCacheKeepSubagents?: (enabled: boolean) => void;
|
|
47
|
+
/** Updates the live loader's main-agent idle-cap bypass gate. */
|
|
48
|
+
setCacheKeepSustain?: (enabled: boolean) => void;
|
|
47
49
|
/** Updates the live loader's clock-hour warm window. undefined = no window. */
|
|
48
50
|
setCacheKeepWindow?: (window: CacheKeepWindow | undefined) => void;
|
|
51
|
+
/** Clears only the sticky account assignment for one OpenCode session. */
|
|
52
|
+
clearStickyRouting?: (sessionId: string) => Promise<boolean>;
|
|
53
|
+
/** Resolves the current session's usable sticky account, if one exists. */
|
|
54
|
+
getStickyRouting?: (sessionId: string) => Promise<string | undefined>;
|
|
49
55
|
}
|
|
50
56
|
export declare function buildDialogPayload(command: CommandModalName, args: string, ctx: CommandContext): Promise<OpenDialogPayload>;
|
|
51
57
|
export declare function applyCommand(request: ApplyRequest, ctx: CommandContext): Promise<ApplyResult>;
|
package/dist/core/accounts.d.ts
CHANGED
|
@@ -54,7 +54,7 @@ export type FallbackAccount = OAuthAccount | ApiKeyAccount;
|
|
|
54
54
|
export declare function isOAuthAccount(account: FallbackAccount): account is OAuthAccount;
|
|
55
55
|
export declare function isApiKeyAccount(account: FallbackAccount): account is ApiKeyAccount;
|
|
56
56
|
export declare function isValidApiBaseURL(value: string | undefined): boolean;
|
|
57
|
-
export type RoutingMode = 'main-first' | 'fallback-first';
|
|
57
|
+
export type RoutingMode = 'main-first' | 'fallback-first' | 'sticky-balanced';
|
|
58
58
|
export type KillswitchThresholds = Partial<Record<QuotaWindowName | '5h' | '1w', number>>;
|
|
59
59
|
export type KillswitchConfig = {
|
|
60
60
|
enabled?: boolean;
|
|
@@ -105,6 +105,7 @@ export type AccountStorage = {
|
|
|
105
105
|
cachekeep?: {
|
|
106
106
|
enabled?: boolean;
|
|
107
107
|
subagents?: boolean;
|
|
108
|
+
sustain?: boolean;
|
|
108
109
|
/** Clock-hour window start (0-23, inclusive) — keeps cachekeep idle warming
|
|
109
110
|
* inside `[startHour, endHour)` local hours. Omit to warm unconditionally. */
|
|
110
111
|
startHour?: number;
|
|
@@ -184,6 +185,10 @@ export declare function saveAccountState(storage: AccountStorage, path?: string,
|
|
|
184
185
|
export declare function shouldFallbackStatus(status: number, storage: AccountStorage | null): boolean;
|
|
185
186
|
export declare function quotaSnapshotPassesPolicy(quota: OAuthQuotaSnapshot | undefined, storage: AccountStorage | null, now?: number): boolean;
|
|
186
187
|
export declare function isKillswitchEnabled(storage: AccountStorage | null): boolean;
|
|
188
|
+
export declare function getKillswitchThresholdsForAccount(storage: AccountStorage | null, accountId?: string): {
|
|
189
|
+
primary: number;
|
|
190
|
+
secondary: number;
|
|
191
|
+
};
|
|
187
192
|
export declare function killswitchPassesPolicy(quota: OAuthQuotaSnapshot | undefined, storage: AccountStorage | null, accountId?: string, now?: number): boolean;
|
|
188
193
|
export declare function killswitchRetryAfterSeconds(mainQuota: OAuthQuotaSnapshot | undefined, fallbackAccounts: Array<{
|
|
189
194
|
accountId: string;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type RefreshAllQuotaDeps, type RefreshAllQuotaResult } from './refresh-all-quota';
|
|
2
|
+
export declare const BACKGROUND_QUOTA_REFRESH_INTERVAL_MS: number;
|
|
3
|
+
export declare const BACKGROUND_QUOTA_REFRESH_JITTER_MS = 30000;
|
|
4
|
+
export declare const BACKGROUND_QUOTA_FRESHNESS_MS: number;
|
|
5
|
+
export declare const BACKGROUND_QUOTA_REFRESH_LOCK_NAME = "bg-quota-refresh";
|
|
6
|
+
export declare const BACKGROUND_QUOTA_REFRESH_LOCK_TTL_MS = 120000;
|
|
7
|
+
type TimerHandle = ReturnType<typeof setInterval>;
|
|
8
|
+
interface BackgroundQuotaRefreshOptions {
|
|
9
|
+
setIntervalFn?: (callback: () => void, intervalMs: number) => TimerHandle;
|
|
10
|
+
clearIntervalFn?: (timer: TimerHandle) => void;
|
|
11
|
+
random?: () => number;
|
|
12
|
+
onError?: (error: unknown) => void;
|
|
13
|
+
}
|
|
14
|
+
type RefreshAllQuotaFn = (deps: RefreshAllQuotaDeps) => Promise<RefreshAllQuotaResult[]>;
|
|
15
|
+
type BackgroundLockHandle = {
|
|
16
|
+
release: () => Promise<void>;
|
|
17
|
+
};
|
|
18
|
+
export type BackgroundLockAcquirer = () => Promise<BackgroundLockHandle | null>;
|
|
19
|
+
/**
|
|
20
|
+
* Acquire the cross-process lock that serializes the background refresh.
|
|
21
|
+
* `renew` re-arms the TTL on an interval until release(), so a serial pass that
|
|
22
|
+
* outlasts the TTL (a fleet of accounts each costing a token refresh plus a
|
|
23
|
+
* wham fetch) never lets the lock expire mid-run — an expiry would let a second
|
|
24
|
+
* process start a concurrent refresh. Overrides exist so tests can drive the
|
|
25
|
+
* renewal with a mock clock and a fast interval.
|
|
26
|
+
*/
|
|
27
|
+
export declare function acquireBackgroundRefreshLock(configPath: string, overrides?: {
|
|
28
|
+
now?: () => number;
|
|
29
|
+
renewIntervalMs?: number;
|
|
30
|
+
}): Promise<BackgroundLockHandle | null>;
|
|
31
|
+
export declare function refreshQuotaInBackground(deps: RefreshAllQuotaDeps, refreshFn?: RefreshAllQuotaFn, acquireLock?: BackgroundLockAcquirer): Promise<RefreshAllQuotaResult[]>;
|
|
32
|
+
export declare class BackgroundQuotaRefresh {
|
|
33
|
+
private readonly setIntervalFn;
|
|
34
|
+
private readonly clearIntervalFn;
|
|
35
|
+
private readonly random;
|
|
36
|
+
private onError;
|
|
37
|
+
private run;
|
|
38
|
+
private timer;
|
|
39
|
+
private tickPromise;
|
|
40
|
+
private stopped;
|
|
41
|
+
constructor(options?: BackgroundQuotaRefreshOptions);
|
|
42
|
+
start(run: () => Promise<void>, onError?: ((error: unknown) => void) | undefined): void;
|
|
43
|
+
stop(): void;
|
|
44
|
+
isStopped(): boolean;
|
|
45
|
+
}
|
|
46
|
+
export {};
|
package/dist/core/cachekeep.d.ts
CHANGED
|
@@ -40,6 +40,8 @@ export interface CacheKeepManagerOptions {
|
|
|
40
40
|
maxBytes?: number;
|
|
41
41
|
/** Returns the configured clock-hour window; undefined means always warm. */
|
|
42
42
|
getWindow?: () => CacheKeepWindow | undefined;
|
|
43
|
+
/** Returns whether main-agent targets bypass only the idle warm cap. */
|
|
44
|
+
getSustain?: () => boolean;
|
|
43
45
|
}
|
|
44
46
|
export interface CacheKeepStatus {
|
|
45
47
|
running: boolean;
|
|
@@ -50,6 +52,7 @@ export interface CacheKeepStatus {
|
|
|
50
52
|
maxSubagentIdleMs: number;
|
|
51
53
|
ttlMs: number;
|
|
52
54
|
leadMs: number;
|
|
55
|
+
sustain: boolean;
|
|
53
56
|
window?: CacheKeepWindow;
|
|
54
57
|
targets: Array<{
|
|
55
58
|
sessionKey: string;
|
|
@@ -94,6 +97,7 @@ export declare class CacheKeepManager {
|
|
|
94
97
|
private readonly maxTargets;
|
|
95
98
|
private readonly maxBytes;
|
|
96
99
|
private readonly getWindow?;
|
|
100
|
+
private readonly getSustain?;
|
|
97
101
|
private timer;
|
|
98
102
|
private startedAt;
|
|
99
103
|
private totalBytes;
|
package/dist/core/oauth.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export declare const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
|
2
2
|
export declare const ISSUER = "https://auth.openai.com";
|
|
3
3
|
export declare const OAUTH_PORT = 1455;
|
|
4
4
|
export declare const OAUTH_POLLING_SAFETY_MARGIN_MS = 3000;
|
|
5
|
-
export declare const USER_AGENT = "cortexkit-opencode-openai-auth/0.
|
|
5
|
+
export declare const USER_AGENT = "cortexkit-opencode-openai-auth/0.5.0";
|
|
6
6
|
export declare const RESERVED_ACCOUNT_ID = "main";
|
|
7
7
|
export declare const RESERVED_ACCOUNT_ID_ERROR = "\"main\" is a reserved account id; choose a different label.";
|
|
8
8
|
export interface PkceCodes {
|
|
@@ -13,18 +13,14 @@ import { type ProviderQuotaFn } from './provider.ts';
|
|
|
13
13
|
export type { ProviderQuotaFn };
|
|
14
14
|
export declare function quotaWindowResetIsPast(window: AccountQuotaWindow | undefined, now: number): boolean;
|
|
15
15
|
/**
|
|
16
|
-
* Resolve when a
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* blackhole the account long past its actual rate-limit. The default is
|
|
23
|
-
* self-correcting: if the account is still exhausted, the next response.failed
|
|
24
|
-
* frame re-marks it. Pure — single source of truth shared by the fetch-override
|
|
25
|
-
* reroute and its unit tests.
|
|
16
|
+
* Resolve when a WS-exhausted account becomes usable. Admission errors can
|
|
17
|
+
* carry the provider's authoritative reset; response.failed frames do not, so
|
|
18
|
+
* those fall back to the named window in the last-known quota snapshot. Never
|
|
19
|
+
* borrow the other window's reset — an unrelated window can blackhole the
|
|
20
|
+
* account long past its actual limit. The bounded default self-corrects when a
|
|
21
|
+
* later request reports exhaustion again.
|
|
26
22
|
*/
|
|
27
|
-
export declare function resolveMidStreamRateLimitResetAt(quota: OAuthQuotaSnapshot | undefined, window: string, now: number, defaultMs: number): number;
|
|
23
|
+
export declare function resolveMidStreamRateLimitResetAt(quota: OAuthQuotaSnapshot | undefined, window: string, now: number, defaultMs: number, explicitResetAt?: number): number;
|
|
28
24
|
/**
|
|
29
25
|
* Stable, non-reversible fingerprint of an access token. Used to detect a
|
|
30
26
|
* main-account switch so a different account's persisted/cached quota is never
|
|
@@ -35,6 +31,7 @@ export type QuotaEntry = {
|
|
|
35
31
|
quota: OAuthQuotaSnapshot;
|
|
36
32
|
refreshAfter: number;
|
|
37
33
|
checkedAt: number;
|
|
34
|
+
accountId?: string;
|
|
38
35
|
};
|
|
39
36
|
export type QuotaManagerOptions = {
|
|
40
37
|
storage: AccountStorage | null;
|
|
@@ -98,11 +95,17 @@ export declare class QuotaManager {
|
|
|
98
95
|
peekMainForPolicy(accountId?: string): QuotaEntry | null;
|
|
99
96
|
/**
|
|
100
97
|
* Non-invalidating read of a cached fallback quota for POLICY decisions
|
|
101
|
-
* (killswitch). Keyed by the stable internal account id, so a
|
|
102
|
-
* for the same account does not drop it (getFallback(id, token)
|
|
98
|
+
* (killswitch, admission). Keyed by the stable internal account id, so a
|
|
99
|
+
* token refresh for the same account does not drop it (getFallback(id, token)
|
|
100
|
+
* would).
|
|
101
|
+
*
|
|
102
|
+
* Pass the live ChatGPT identity to still drop on a genuine re-login: if the
|
|
103
|
+
* cached entry's identity is known and differs, return null so the previous
|
|
104
|
+
* identity's (possibly exhausted) quota is never judged against its
|
|
105
|
+
* replacement. Unknown on either side fails open, matching peekMainForPolicy.
|
|
103
106
|
*/
|
|
104
|
-
peekFallbackForPolicy(accountId: string): QuotaEntry | null;
|
|
105
|
-
setFallback(accountId: string, entry: QuotaEntry, accessToken?: string, completeSnapshot?: boolean): void;
|
|
107
|
+
peekFallbackForPolicy(accountId: string, identity?: string): QuotaEntry | null;
|
|
108
|
+
setFallback(accountId: string, entry: QuotaEntry, accessToken?: string, completeSnapshot?: boolean, identity?: string): void;
|
|
106
109
|
/**
|
|
107
110
|
* Mark accountId ('main' or a fallback id) rate-limited until resetAtMs.
|
|
108
111
|
* Keeps the LATER reset: skips only when an existing mark is strictly
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type SidebarState } from '../sidebar-state';
|
|
1
2
|
import type { FallbackAccountManager, isOAuthAccount, loadAccounts } from './accounts';
|
|
2
3
|
import type { whamUsageFn } from './provider';
|
|
3
4
|
import type { QuotaManager } from './quota-manager';
|
|
@@ -25,6 +26,11 @@ export interface RefreshAllQuotaDeps {
|
|
|
25
26
|
refresh: string;
|
|
26
27
|
expires: number;
|
|
27
28
|
}>;
|
|
29
|
+
refreshMainWithLease: () => Promise<{
|
|
30
|
+
access: string;
|
|
31
|
+
refresh: string;
|
|
32
|
+
expires: number;
|
|
33
|
+
}>;
|
|
28
34
|
fallbackManager: FallbackAccountManager;
|
|
29
35
|
quotaManager: QuotaManager;
|
|
30
36
|
loadAccounts: typeof loadAccounts;
|
|
@@ -52,11 +58,16 @@ export interface RefreshAllQuotaDeps {
|
|
|
52
58
|
whamFn?: typeof whamUsageFn;
|
|
53
59
|
respectBackoff?: boolean;
|
|
54
60
|
logger?: QuotaLogger;
|
|
61
|
+
skipFresherThanMs?: number;
|
|
62
|
+
readSidebarState?: () => Promise<SidebarState>;
|
|
63
|
+
}
|
|
64
|
+
export interface RefreshAllQuotaOptions {
|
|
65
|
+
accountKey?: string;
|
|
55
66
|
}
|
|
56
67
|
export interface RefreshAllQuotaResult {
|
|
57
68
|
account: string;
|
|
58
69
|
ok: boolean;
|
|
59
70
|
error?: string;
|
|
60
71
|
}
|
|
61
|
-
export declare function refreshAllQuota(deps: RefreshAllQuotaDeps): Promise<RefreshAllQuotaResult[]>;
|
|
72
|
+
export declare function refreshAllQuota(deps: RefreshAllQuotaDeps, options?: RefreshAllQuotaOptions): Promise<RefreshAllQuotaResult[]>;
|
|
62
73
|
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type AccountQuota, type QuotaWindow, type QuotaWindowKey } from '../sidebar-state';
|
|
2
|
+
export declare const QUOTA_STALENESS_MS: number;
|
|
3
|
+
export declare const MIN_RESET_HOURS: number;
|
|
4
|
+
export declare const MIN_WEIGHT = 0.000001;
|
|
5
|
+
export declare function snapshotCheckedAt(quota: AccountQuota | null | undefined, entryCheckedAt?: number): number | undefined;
|
|
6
|
+
export type StickyBreakDecision = {
|
|
7
|
+
action: 'retain';
|
|
8
|
+
reason: 'unknown' | 'stale' | 'healthy' | 'transient';
|
|
9
|
+
} | {
|
|
10
|
+
action: 'migrate';
|
|
11
|
+
reason: 'exhausted' | 'permanent' | 'killswitch';
|
|
12
|
+
windowKey?: QuotaWindowKey;
|
|
13
|
+
resetsAt?: string;
|
|
14
|
+
};
|
|
15
|
+
export declare function decideStickyBreak(input: {
|
|
16
|
+
quota: AccountQuota | null | undefined;
|
|
17
|
+
quotaCheckedAt?: number;
|
|
18
|
+
status?: number;
|
|
19
|
+
now: number;
|
|
20
|
+
killswitchPasses?: boolean;
|
|
21
|
+
}): StickyBreakDecision;
|
|
22
|
+
export declare function sustainableWindowWeight(window: Pick<QuotaWindow, 'remainingPercent' | 'resetsAt'>, reservePercent: number, now: number): number;
|
|
23
|
+
export interface StickySelectionCandidate {
|
|
24
|
+
accountId: string;
|
|
25
|
+
quota: AccountQuota | null | undefined;
|
|
26
|
+
quotaCheckedAt?: number;
|
|
27
|
+
reservePercent: Record<QuotaWindowKey, number>;
|
|
28
|
+
configuredOrder: number;
|
|
29
|
+
resetCreditsApplicable?: number;
|
|
30
|
+
killswitchPasses?: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface StickySelectionInput {
|
|
33
|
+
candidates: readonly StickySelectionCandidate[];
|
|
34
|
+
pendingBytes: ReadonlyMap<string, number>;
|
|
35
|
+
requestBytes: number;
|
|
36
|
+
now: number;
|
|
37
|
+
onEmptyWeightedSet?: () => void;
|
|
38
|
+
}
|
|
39
|
+
export declare function selectStickyCandidate(input: StickySelectionInput): {
|
|
40
|
+
accountId: string;
|
|
41
|
+
quotaCheckedAt?: number;
|
|
42
|
+
source: 'weighted' | 'mode-fallback';
|
|
43
|
+
} | undefined;
|
package/dist/dump.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{Ea as Z,Ga as W}from"./index-wpd7mg3p.js";var R=Z(($G,E)=>{var{defineProperty:N,getOwnPropertyDescriptor:Kz,getOwnPropertyNames:Qz}=Object,Wz=Object.prototype.hasOwnProperty,Xz=(z,G)=>{for(var H in G)N(z,H,{get:G[H],enumerable:!0})},Yz=(z,G,H,Q)=>{if(G&&typeof G==="object"||typeof G==="function"){for(let K of Qz(G))if(!Wz.call(z,K)&&K!==H)N(z,K,{get:()=>G[K],enumerable:!(Q=Kz(G,K))||Q.enumerable})}return z},Zz=(z)=>Yz(N({},"__esModule",{value:!0}),z),O={};Xz(O,{VercelOidcTokenError:()=>S});E.exports=Zz(O);class S extends Error{constructor(z,G){super(z);this.name="VercelOidcTokenError",this.cause=G}toString(){if(this.cause)return`${this.name}: ${this.message}: ${this.cause}`;return`${this.name}: ${this.message}`}}});var P=Z((qG,y)=>{var{create:$z,defineProperty:L,getOwnPropertyDescriptor:qz,getOwnPropertyNames:vz,getPrototypeOf:Bz}=Object,Fz=Object.prototype.hasOwnProperty,Uz=(z,G)=>{for(var H in G)L(z,H,{get:G[H],enumerable:!0})},C=(z,G,H,Q)=>{if(G&&typeof G==="object"||typeof G==="function"){for(let K of vz(G))if(!Fz.call(z,K)&&K!==H)L(z,K,{get:()=>G[K],enumerable:!(Q=qz(G,K))||Q.enumerable})}return z},I=(z,G,H)=>(H=z!=null?$z(Bz(z)):{},C(G||!z||!z.__esModule?L(H,"default",{value:z,enumerable:!0}):H,z)),Lz=(z)=>C(L({},"__esModule",{value:!0}),z),x={};Uz(x,{findRootDir:()=>Jz,getUserDataDir:()=>Nz});y.exports=Lz(x);var B=I(W("path")),bz=I(W("fs")),A=I(W("os")),Vz=R();function Jz(){try{let z=process.cwd();while(z!==B.default.dirname(z)){let G=B.default.join(z,".vercel");if(bz.default.existsSync(G))return z;z=B.default.dirname(z)}}catch(z){throw new Vz.VercelOidcTokenError("Token refresh only supported in node server environments")}return null}function Nz(){if(process.env.XDG_DATA_HOME)return process.env.XDG_DATA_HOME;switch(A.default.platform()){case"darwin":return B.default.join(A.default.homedir(),"Library/Application Support");case"linux":return B.default.join(A.default.homedir(),".local/share");case"win32":if(process.env.LOCALAPPDATA)return process.env.LOCALAPPDATA;return null;default:return null}}});var i=Z((vG,c)=>{var{create:Rz,defineProperty:b,getOwnPropertyDescriptor:Az,getOwnPropertyNames:Iz,getPrototypeOf:Dz}=Object,wz=Object.prototype.hasOwnProperty,Tz=(z,G)=>{for(var H in G)b(z,H,{get:G[H],enumerable:!0})},u=(z,G,H,Q)=>{if(G&&typeof G==="object"||typeof G==="function"){for(let K of Iz(G))if(!wz.call(z,K)&&K!==H)b(z,K,{get:()=>G[K],enumerable:!(Q=Az(G,K))||Q.enumerable})}return z},l=(z,G,H)=>(H=z!=null?Rz(Dz(z)):{},u(G||!z||!z.__esModule?b(H,"default",{value:z,enumerable:!0}):H,z)),jz=(z)=>u(b({},"__esModule",{value:!0}),z),g={};Tz(g,{isValidAccessToken:()=>Ez,readAuthConfig:()=>Oz,writeAuthConfig:()=>Sz});c.exports=jz(g);var F=l(W("fs")),h=l(W("path")),Mz=f();function k(){let z=(0,Mz.getVercelDataDir)();if(!z)throw Error(`Unable to find Vercel CLI data directory. Your platform: ${process.platform}. Supported: darwin, linux, win32.`);return h.join(z,"auth.json")}function Oz(){try{let z=k();if(!F.existsSync(z))return null;let G=F.readFileSync(z,"utf8");if(!G)return null;return JSON.parse(G)}catch(z){return null}}function Sz(z){let G=k(),H=h.dirname(G);if(!F.existsSync(H))F.mkdirSync(H,{mode:504,recursive:!0});F.writeFileSync(G,JSON.stringify(z,null,2),{mode:384})}function Ez(z,G=0){if(!z.token)return!1;if(typeof z.expiresAt!=="number")return!0;let H=Math.floor(Date.now()/1000),Q=G/1000;return z.expiresAt>=H+Q}});var a=Z((BG,s)=>{var{defineProperty:T,getOwnPropertyDescriptor:Cz,getOwnPropertyNames:xz}=Object,yz=Object.prototype.hasOwnProperty,Pz=(z,G)=>{for(var H in G)T(z,H,{get:G[H],enumerable:!0})},uz=(z,G,H,Q)=>{if(G&&typeof G==="object"||typeof G==="function"){for(let K of xz(G))if(!yz.call(z,K)&&K!==H)T(z,K,{get:()=>G[K],enumerable:!(Q=Cz(G,K))||Q.enumerable})}return z},lz=(z)=>uz(T({},"__esModule",{value:!0}),z),d={};Pz(d,{processTokenResponse:()=>iz,refreshTokenRequest:()=>cz});s.exports=lz(d);var D=W("os"),gz="https://vercel.com",hz="cl_HYyOPBNtFMfHhaUn9L4QPfTZz6TP47bp",p=`@vercel/oidc node-${process.version} ${(0,D.platform)()} (${(0,D.arch)()}) ${(0,D.hostname)()}`,w=null;async function kz(){if(w)return w;let z=`${gz}/.well-known/openid-configuration`,G=await fetch(z,{headers:{"user-agent":p}});if(!G.ok)throw Error("Failed to discover OAuth endpoints");let H=await G.json();if(!H||typeof H.token_endpoint!=="string")throw Error("Invalid OAuth discovery response");let Q=H.token_endpoint;return w=Q,Q}async function cz(z){let G=await kz();return await fetch(G,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded","user-agent":p},body:new URLSearchParams({client_id:hz,grant_type:"refresh_token",...z})})}async function iz(z){let G=await z.json();if(!z.ok){let H=typeof G==="object"&&G&&"error"in G?String(G.error):"Token refresh failed";return[Error(H)]}if(typeof G!=="object"||G===null)return[Error("Invalid token response")];if(typeof G.access_token!=="string")return[Error("Missing access_token in response")];if(G.token_type!=="Bearer")return[Error("Invalid token_type in response")];if(typeof G.expires_in!=="number")return[Error("Missing expires_in in response")];return[null,G]}});var t=Z((FG,r)=>{var{defineProperty:j,getOwnPropertyDescriptor:fz,getOwnPropertyNames:dz}=Object,pz=Object.prototype.hasOwnProperty,sz=(z,G)=>{for(var H in G)j(z,H,{get:G[H],enumerable:!0})},az=(z,G,H,Q)=>{if(G&&typeof G==="object"||typeof G==="function"){for(let K of dz(G))if(!pz.call(z,K)&&K!==H)j(z,K,{get:()=>G[K],enumerable:!(Q=fz(G,K))||Q.enumerable})}return z},_z=(z)=>az(j({},"__esModule",{value:!0}),z),_={};sz(_,{AccessTokenMissingError:()=>m,RefreshAccessTokenFailedError:()=>n});r.exports=_z(_);class m extends Error{constructor(){super("No authentication found. Please log in with the Vercel CLI (vercel login).");this.name="AccessTokenMissingError"}}class n extends Error{constructor(z){super("Failed to refresh authentication token.",{cause:z});this.name="RefreshAccessTokenFailedError"}}});var f=Z((UG,Hz)=>{var{create:mz,defineProperty:V,getOwnPropertyDescriptor:nz,getOwnPropertyNames:rz,getPrototypeOf:tz}=Object,ez=Object.prototype.hasOwnProperty,oz=(z,G)=>{for(var H in G)V(z,H,{get:G[H],enumerable:!0})},o=(z,G,H,Q)=>{if(G&&typeof G==="object"||typeof G==="function"){for(let K of rz(G))if(!ez.call(z,K)&&K!==H)V(z,K,{get:()=>G[K],enumerable:!(Q=nz(G,K))||Q.enumerable})}return z},zz=(z,G,H)=>(H=z!=null?mz(tz(z)):{},o(G||!z||!z.__esModule?V(H,"default",{value:z,enumerable:!0}):H,z)),zG=(z)=>o(V({},"__esModule",{value:!0}),z),Gz={};oz(Gz,{assertVercelOidcTokenResponse:()=>M,findProjectInfo:()=>QG,getTokenPayload:()=>YG,getVercelDataDir:()=>GG,getVercelOidcToken:()=>KG,getVercelToken:()=>HG,isExpired:()=>ZG,loadToken:()=>XG,saveToken:()=>WG});Hz.exports=zG(Gz);var U=zz(W("path")),X=zz(W("fs")),v=R(),J=P(),$=i(),e=a(),q=t();function GG(){let G=(0,J.getUserDataDir)();if(!G)return null;return U.join(G,"com.vercel.cli")}async function HG(z){let G=(0,$.readAuthConfig)();if(!G?.token)throw new q.AccessTokenMissingError;if((0,$.isValidAccessToken)(G,z?.expirationBufferMs))return G.token;if(!G.refreshToken)throw(0,$.writeAuthConfig)({}),new q.RefreshAccessTokenFailedError("No refresh token available");try{let H=await(0,e.refreshTokenRequest)({refresh_token:G.refreshToken}),[Q,K]=await(0,e.processTokenResponse)(H);if(Q||!K)throw(0,$.writeAuthConfig)({}),new q.RefreshAccessTokenFailedError(Q);let Y={token:K.access_token,expiresAt:Math.floor(Date.now()/1000)+K.expires_in};if(K.refresh_token)Y.refreshToken=K.refresh_token;return(0,$.writeAuthConfig)(Y),Y.token}catch(H){if((0,$.writeAuthConfig)({}),H instanceof q.AccessTokenMissingError||H instanceof q.RefreshAccessTokenFailedError)throw H;throw new q.RefreshAccessTokenFailedError(H)}}async function KG(z,G,H){let Q=`https://api.vercel.com/v1/projects/${G}/token?source=vercel-oidc-refresh${H?`&teamId=${H}`:""}`,K=await fetch(Q,{method:"POST",headers:{Authorization:`Bearer ${z}`}});if(!K.ok)throw new v.VercelOidcTokenError(`Failed to refresh OIDC token: ${K.statusText}`);let Y=await K.json();return M(Y),Y}function M(z){if(!z||typeof z!=="object")throw TypeError("Vercel OIDC token is malformed. Expected an object. Please run `vc env pull` and try again");if(!("token"in z)||typeof z.token!=="string")throw TypeError("Vercel OIDC token is malformed. Expected a string-valued token property. Please run `vc env pull` and try again")}function QG(){let z=(0,J.findRootDir)();if(!z)throw new v.VercelOidcTokenError("Unable to find project root directory. Have you linked your project with `vc link?`");let G=U.join(z,".vercel","project.json");if(!X.existsSync(G))throw new v.VercelOidcTokenError("project.json not found, have you linked your project with `vc link?`");let H=JSON.parse(X.readFileSync(G,"utf8"));if(typeof H.projectId!=="string"&&typeof H.orgId!=="string")throw TypeError("Expected a string-valued projectId property. Try running `vc link` to re-link your project.");return{projectId:H.projectId,teamId:H.orgId}}function WG(z,G){let H=(0,J.getUserDataDir)();if(!H)throw new v.VercelOidcTokenError("Unable to find user data directory. Please reach out to Vercel support.");let Q=U.join(H,"com.vercel.token",`${G}.json`),K=JSON.stringify(z);X.mkdirSync(U.dirname(Q),{mode:504,recursive:!0}),X.writeFileSync(Q,K),X.chmodSync(Q,432);return}function XG(z){let G=(0,J.getUserDataDir)();if(!G)throw new v.VercelOidcTokenError("Unable to find user data directory. Please reach out to Vercel support.");let H=U.join(G,"com.vercel.token",`${z}.json`);if(!X.existsSync(H))return null;let Q=JSON.parse(X.readFileSync(H,"utf8"));return M(Q),Q}function YG(z){let G=z.split(".");if(G.length!==3)throw new v.VercelOidcTokenError("Invalid token. Please run `vc env pull` and try again");let H=G[1].replace(/-/g,"+").replace(/_/g,"/"),Q=H.padEnd(H.length+(4-H.length%4)%4,"=");return JSON.parse(Buffer.from(Q,"base64").toString("utf8"))}function ZG(z,G=0){return z.exp*1000<Date.now()+G}});
|
|
2
2
|
export{R as a,f as b};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import{ta as T}from"./index-vrdc90yd.js";import{za as s}from"./index-s5fbqqwx.js";import{Ga as YB}from"./index-wpd7mg3p.js";import{createHash as p0}from"node:crypto";var l0=86400000,d0=300000,g0=3600000,i0=60000,u0=900000,s0=300000;function t(B){return B instanceof Error?B.message:String(B)}function r0(B){let Z=B?.status;if(typeof Z==="number")return Z===429||Z>=500;if(!(B instanceof Error))return!1;return B.name==="AbortError"||B.name==="TimeoutError"||B.message.includes("fetch failed")||"code"in B&&(B.code==="ECONNRESET"||B.code==="ECONNREFUSED"||B.code==="ETIMEDOUT"||B.code==="UND_ERR_CONNECT_TIMEOUT")}function e(B){let Z=t(B),$=B?.status;if(typeof $==="number")return $===429||$>=500;if(!(B instanceof Error))return!1;let W=B.code;return B.name==="AbortError"||B.name==="TimeoutError"||Z.includes("fetch failed")||W==="ECONNRESET"||W==="ECONNREFUSED"||W==="ETIMEDOUT"||W==="UND_ERR_CONNECT_TIMEOUT"}function Q0(B){return p0("sha256").update(B).digest("hex")}function N0(B){let Z=Q0(B.refreshToken),W=(B.previous?.tokenHash===Z?B.previous.retryCount??0:0)+1,z,X=B.error?.retryAfter;if(typeof X==="number"&&X>0)z=X*1000;else if(r0(B.error))z=Math.min(g0,d0*2**Math.min(W-1,6));else z=l0;return{message:t(B.error),checkedAt:B.now,nextRetryAt:B.now+z,retryCount:W,tokenHash:Z}}function U(B,Z,$){if(!B?.nextRetryAt||B.nextRetryAt<=$)return!1;if(!Z)return!0;return B.tokenHash===Q0(Z)}function d(B,Z){return`Codex OAuth refresh is backed off for ${Math.max(1,Math.ceil(((B.nextRetryAt??Z)-Z)/1000))}s after: ${B.message}`}function A(B){let $=(B.previous?.retryCount??0)+1,W,z=B.error?.retryAfter;if(typeof z==="number"&&z>0)W=z*1000;else if(e(B.error))W=Math.min(u0,i0*2**Math.min($-1,6));else W=s0;return{message:t(B.error),checkedAt:B.now,nextRetryAt:B.now+W,retryCount:$}}function x(B,Z){if(!B?.nextRetryAt||B.nextRetryAt<=Z)return!1;return!0}function B0(B){if(!B)return;let Z=Number(B);if(Number.isFinite(Z)&&Z>0)return Math.ceil(Z);let $=Date.parse(B);if(Number.isFinite($)){let W=Math.ceil(($-Date.now())/1000);return W>0?W:void 0}return}import{createServer as n0}from"node:http";import{setTimeout as c0}from"node:timers/promises";var P0="0.5.0";var u="app_EMoamEEZ73f0CkXaXp7hrann",q="https://auth.openai.com",m=1455,a0=3000,F0=`cortexkit-opencode-openai-auth/${P0}`,O0="main",o0=`"${O0}" is a reserved account id; choose a different label.`;function b0(B){let Z=new Uint8Array(B),$=String.fromCharCode(...Z);return btoa($).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}async function t0(){let Z=Array.from(crypto.getRandomValues(new Uint8Array(43))).map((W)=>"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"[W%66]).join(""),$=b0(await crypto.subtle.digest("SHA-256",new TextEncoder().encode(Z)));return{verifier:Z,challenge:$}}function V0(B){if(B.length>16384)return;let Z=B.split(".");if(Z.length!==3)return;let $=Z[1];if(!$||$.length>8192)return;try{return JSON.parse(Buffer.from($,"base64url").toString())}catch{return}}function L0(B){let Z=B.chatgpt_account_id;if(typeof Z==="string"&&Z.length>0)return Z;let $=B["https://api.openai.com/auth"];if($&&typeof $==="object"&&!Array.isArray($)&&typeof $.chatgpt_account_id==="string"&&$.chatgpt_account_id.length>0)return $.chatgpt_account_id;let W=B.organizations;if(Array.isArray(W)){let z=W[0];if(z&&typeof z==="object"&&!Array.isArray(z)&&typeof z.id==="string"&&z.id.length>0)return z.id}return}function i(B){if(B.id_token){let Z=V0(B.id_token),$=Z&&L0(Z);if($)return $}if(B.access_token){let Z=V0(B.access_token);return Z?L0(Z):void 0}return}function e0(B){return typeof B==="string"&&B.toLowerCase()===O0}function Z0(B){if(e0(B))throw Error(o0);return B}function BB(B,Z,$){let W=new URLSearchParams({response_type:"code",client_id:u,redirect_uri:B,scope:"openid profile email offline_access",code_challenge:Z.challenge,code_challenge_method:"S256",id_token_add_organizations:"true",codex_cli_simplified_flow:"true",state:$,originator:"opencode"});return`${q}/oauth/authorize?${W.toString()}`}async function ZB(B,Z,$){let W=await fetch(`${q}/oauth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",code:B,redirect_uri:Z,client_id:u,code_verifier:$.verifier}).toString()});if(!W.ok)throw Error(`Token exchange failed: ${W.status}`);return await W.json()}function $B(B){return B.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}var WB=`<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>CortexKit OpenAI Auth - Authorization Successful</title>
|
|
5
|
+
<style>
|
|
6
|
+
body {
|
|
7
|
+
font-family:
|
|
8
|
+
system-ui,
|
|
9
|
+
-apple-system,
|
|
10
|
+
sans-serif;
|
|
11
|
+
display: flex;
|
|
12
|
+
justify-content: center;
|
|
13
|
+
align-items: center;
|
|
14
|
+
height: 100vh;
|
|
15
|
+
margin: 0;
|
|
16
|
+
background: #131010;
|
|
17
|
+
color: #f1ecec;
|
|
18
|
+
}
|
|
19
|
+
.container {
|
|
20
|
+
text-align: center;
|
|
21
|
+
padding: 2rem;
|
|
22
|
+
}
|
|
23
|
+
h1 {
|
|
24
|
+
color: #f1ecec;
|
|
25
|
+
margin-bottom: 1rem;
|
|
26
|
+
}
|
|
27
|
+
p {
|
|
28
|
+
color: #b7b1b1;
|
|
29
|
+
}
|
|
30
|
+
</style>
|
|
31
|
+
</head>
|
|
32
|
+
<body>
|
|
33
|
+
<div class="container">
|
|
34
|
+
<h1>Authorization Successful</h1>
|
|
35
|
+
<p>You can close this window and return to OpenCode.</p>
|
|
36
|
+
</div>
|
|
37
|
+
<script>
|
|
38
|
+
setTimeout(() => window.close(), 2000)
|
|
39
|
+
</script>
|
|
40
|
+
</body>
|
|
41
|
+
</html>`,g=(B)=>`<!doctype html>
|
|
42
|
+
<html>
|
|
43
|
+
<head>
|
|
44
|
+
<title>CortexKit OpenAI Auth - Authorization Failed</title>
|
|
45
|
+
<style>
|
|
46
|
+
body {
|
|
47
|
+
font-family:
|
|
48
|
+
system-ui,
|
|
49
|
+
-apple-system,
|
|
50
|
+
sans-serif;
|
|
51
|
+
display: flex;
|
|
52
|
+
justify-content: center;
|
|
53
|
+
align-items: center;
|
|
54
|
+
height: 100vh;
|
|
55
|
+
margin: 0;
|
|
56
|
+
background: #131010;
|
|
57
|
+
color: #f1ecec;
|
|
58
|
+
}
|
|
59
|
+
.container {
|
|
60
|
+
text-align: center;
|
|
61
|
+
padding: 2rem;
|
|
62
|
+
}
|
|
63
|
+
h1 {
|
|
64
|
+
color: #fc533a;
|
|
65
|
+
margin-bottom: 1rem;
|
|
66
|
+
}
|
|
67
|
+
p {
|
|
68
|
+
color: #b7b1b1;
|
|
69
|
+
}
|
|
70
|
+
.error {
|
|
71
|
+
color: #ff917b;
|
|
72
|
+
font-family: monospace;
|
|
73
|
+
margin-top: 1rem;
|
|
74
|
+
padding: 1rem;
|
|
75
|
+
background: #3c140d;
|
|
76
|
+
border-radius: 0.5rem;
|
|
77
|
+
}
|
|
78
|
+
</style>
|
|
79
|
+
</head>
|
|
80
|
+
<body>
|
|
81
|
+
<div class="container">
|
|
82
|
+
<h1>Authorization Failed</h1>
|
|
83
|
+
<p>An error occurred during authorization.</p>
|
|
84
|
+
<div class="error">${$B(B)}</div>
|
|
85
|
+
</div>
|
|
86
|
+
</body>
|
|
87
|
+
</html>`,k,w,L=new Map;function M(){return L.size}function C(B){if(L.delete(B),M()===0)_()}async function zB(){let B=`http://localhost:${m}/auth/callback`;if(k)return{port:m,redirectUri:B};if(w)return w;w=(async()=>{let Z=n0(($,W)=>{let z=new URL($.url||"/",`http://127.0.0.1:${m}`);if(z.pathname==="/auth/callback"){let X=z.searchParams.get("code"),G=z.searchParams.get("state"),K=z.searchParams.get("error"),Y=z.searchParams.get("error_description");if(K){let j=Y||K;if(G){let Q=L.get(G);if(Q)Q.reject(Error(j)),C(G);else if(M()===0)_()}else if(M()===0)_();W.writeHead(200,{"Content-Type":"text/html"}),W.end(g(j));return}if(!G){if(M()===0)_();W.writeHead(400,{"Content-Type":"text/html"}),W.end(g("Missing state parameter"));return}if(!X){if(M()===0)_();W.writeHead(400,{"Content-Type":"text/html"}),W.end(g("Missing authorization code"));return}let J=L.get(G);if(!J){if(M()===0)_();W.writeHead(400,{"Content-Type":"text/html"}),W.end(g("Unknown state — authorization may have expired"));return}C(G),ZB(X,B,J.pkce).then((j)=>J.resolve(j)).catch((j)=>J.reject(j)),W.writeHead(200,{"Content-Type":"text/html"}),W.end(WB);return}if(z.pathname==="/cancel"){let X=z.searchParams.get("state"),G=X?[X]:[...L.keys()];W.writeHead(200),W.end("Login cancelled");for(let K of G){let Y=L.get(K);if(!Y)continue;Y.reject(Error("Login cancelled")),C(K)}if(M()===0)_();return}W.writeHead(404),W.end("Not found")});return await new Promise(($,W)=>{Z.once("error",W),Z.listen(m,"127.0.0.1",()=>{Z.off("error",W),$()})}),k=Z,{port:m,redirectUri:B}})();try{return await w}finally{w=void 0}}function _(){if(k){let B=k;k=void 0,B.close(()=>{}),B.closeIdleConnections?.(),setTimeout(()=>{B.closeAllConnections?.()},25).unref?.()}}function XB(B,Z,$=300000,W){return new Promise((z,X)=>{if(W?.aborted){C(Z),X(Error("Login cancelled"));return}let G=setTimeout(()=>{if(L.get(Z))C(Z),X(Error("OAuth callback timeout — authorization took too long"))},$),K=()=>{if(clearTimeout(G),L.get(Z))C(Z),X(Error("Login cancelled"))};if(W)W.addEventListener("abort",K);L.set(Z,{pkce:B,state:Z,resolve:(Y)=>{if(clearTimeout(G),W)W.removeEventListener("abort",K);z(Y)},reject:(Y)=>{if(clearTimeout(G),W)W.removeEventListener("abort",K);X(Y)}})})}async function GB(){let B=await fetch(`${q}/api/accounts/deviceauth/usercode`,{method:"POST",headers:{"Content-Type":"application/json","User-Agent":F0},body:JSON.stringify({client_id:u})});if(!B.ok)throw Error("Failed to initiate device authorization");let Z=await B.json();return{deviceData:Z,url:`${q}/codex/device`,instructions:`Enter code: ${Z.user_code}`}}async function KB(B,Z){let $=Math.max(parseInt(B.interval,10)||5,1)*1000,W=typeof B.expires_in==="number"?B.expires_in:parseInt(B.expires_in||"",10),z=!Number.isNaN(W)&&W>0?W*1000:900000,X=Date.now();while(!0){if(Z?.aborted)throw Error("Device authorization cancelled");if(Date.now()-X>z)throw Error("Device authorization expired");let G=await fetch(`${q}/api/accounts/deviceauth/token`,{method:"POST",headers:{"Content-Type":"application/json","User-Agent":F0},body:JSON.stringify({device_auth_id:B.device_auth_id,user_code:B.user_code})});if(G.ok){let J=await G.json(),j=await fetch(`${q}/oauth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",code:J.authorization_code,redirect_uri:`${q}/deviceauth/callback`,client_id:u,code_verifier:J.code_verifier}).toString()});if(!j.ok)throw Error(`Token exchange failed: ${j.status}`);return await j.json()}if(G.status!==403&&G.status!==404)throw Error(`Device authorization failed: ${G.status}`);let K=z-(Date.now()-X);if(K<=0)throw Error("Device authorization expired");let Y=Math.min($+a0,K);try{await c0(Y,void 0,{signal:Z})}catch(J){if(J instanceof Error&&J.name==="AbortError")throw Error("Device authorization cancelled");throw J}}}function qZ(B,Z){let $=-1;if(Z.accountId)$=B.findIndex((W)=>W.accountId&&W.accountId===Z.accountId);if($<0)$=B.findIndex((W)=>W.id===Z.id);if($<0&&Z.label&&!Z.accountId)$=B.findIndex((W)=>W.label===Z.label);if($>=0)return B[$]={...B[$],...Z,addedAt:B[$]?.addedAt??Z.addedAt},$;return B.push(Z),B.length-1}async function CZ(B={}){let{label:Z,headless:$=!1,signal:W}=B;if(Z0(Z),$){let{deviceData:J,url:j,instructions:Q}=await GB(),y=(async()=>{let V=await KB(J,W),F=Date.now(),H0=i(V);return{id:Z0(Z||H0||crypto.randomUUID()),label:Z||void 0,type:"oauth",access:V.access_token,refresh:V.refresh_token,expires:F+(V.expires_in??3600)*1000,enabled:!0,addedAt:F,lastUsed:F,lastRefreshedAt:F,accountId:H0}})();return{url:j,instructions:Q,completion:y}}let{redirectUri:z}=await zB(),X=await t0(),G=b0(crypto.getRandomValues(new Uint8Array(32)).buffer),K=BB(z,X,G),Y=(async()=>{try{if(W?.aborted)throw Error("Login cancelled");let J=await XB(X,G,void 0,W),j=Date.now(),Q=i(J);return{id:Z0(Z||Q||crypto.randomUUID()),label:Z||void 0,type:"oauth",access:J.access_token,refresh:J.refresh_token,expires:j+(J.expires_in??3600)*1000,enabled:!0,addedAt:j,lastUsed:j,lastRefreshedAt:j,accountId:Q}}finally{C(G)}})();return{url:K,instructions:"Complete authorization in your browser. This window will close automatically.",completion:Y}}function D0(B){if(B instanceof Error)return B.message;if(typeof B==="string")return B;try{return JSON.stringify(B)}catch{return String(B)}}var JB=s("quota"),$0="primary",W0="secondary",jB="app_EMoamEEZ73f0CkXaXp7hrann",HB="https://auth.openai.com";async function TZ(B){let Z=await B.fetchImpl(`${HB}/oauth/token`,{method:"POST",signal:AbortSignal.timeout(15000),headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"refresh_token",refresh_token:B.refreshToken,client_id:jB}).toString()});if(!Z.ok){let W=B0(Z.headers.get("retry-after"));throw Object.assign(Error(`Token refresh failed: ${Z.status}`),{status:Z.status,retryAfter:W,isRefreshError:!0})}let $=await Z.json();if(!$||typeof $.access_token!=="string"||!$.access_token||typeof $.refresh_token!=="string"||!$.refresh_token||typeof $.expires_in!=="number")throw Object.assign(Error("Token refresh failed: malformed response"),{status:Z.status,isRefreshError:!0});return{access:$.access_token,refresh:$.refresh_token,expires:B.now()+$.expires_in*1000,expiresIn:$.expires_in}}var QB="https://chatgpt.com/backend-api/wham/usage";async function fZ(B){let Z=B.logger??JB,$=B.accountKey??"unknown",W=Date.now();Z.debug("wham usage fetch started",{pid:process.pid,accountId:$});try{let z=await B.fetchImpl(QB,{signal:AbortSignal.timeout(15000),headers:{authorization:`Bearer ${B.accessToken}`,"chatgpt-account-id":B.accountId??"","oai-client-platform":"web","oai-client-version":"0","x-openai-target-path":"/backend-api/wham/usage"}});if(!z.ok){let K=B0(z.headers.get("retry-after"));throw Object.assign(Error(`wham usage check failed: ${z.status}`),{status:z.status,retryAfter:K})}let{normalizeWham:X}=await import("./quota-normalize-n02dfdx0.js"),G=X(await z.json());return Z.debug("wham usage fetch succeeded",{pid:process.pid,accountId:$,status:z.status,elapsedMs:Date.now()-W}),G}catch(z){throw Z.warn("wham usage fetch failed",{pid:process.pid,accountId:$,status:typeof z?.status==="number"?z.status:"error",elapsedMs:Date.now()-W,error:D0(z)}),z}}import{createHash as NB}from"node:crypto";var PB=globalThis.setTimeout,VB=5;function h(B){let Z=B?.quota?.checkIntervalMinutes??VB;return Math.max(1,Z)*60000}function LB(B){return B?.quota?.enabled!==!1}function r(B,Z){if(!B?.resetsAt)return!1;let $=Date.parse(B.resetsAt);return Number.isFinite($)&&$<=Z}function hZ(B,Z,$,W,z){if(z!==void 0&&Number.isFinite(z)&&z>$)return z;let X=Z===$0?B?.primary:Z===W0?B?.secondary:void 0,G=X?.resetsAt?Date.parse(X.resetsAt):NaN;if(Number.isFinite(G)&&G>$)return G;return $+W}function FB(B){let Z=B?.quota?.minimumRemaining||{};return{primary:Z.primary??Z["5h"]??15,secondary:Z.secondary??Z["1w"]??10}}function U0(B,Z,$){if(!LB(Z))return $+h(Z);let W=FB(Z),z=[];for(let X of["primary","secondary"]){let G=B?.[X];if(!G)continue;if(G.remainingPercent>=W[X])continue;let K=G.resetsAt?Date.parse(G.resetsAt):Number.NaN;if(!Number.isFinite(K)||K<=$)return $+h(Z);z.push(K)}if(!z.length)return $+h(Z);return Math.min(...z)+60000}function OB(B){if(!B?.quota?.mainQuota||!B.quota.mainQuotaCheckedAt)return null;return{quota:B.quota.mainQuota,checkedAt:B.quota.mainQuotaCheckedAt,tokenFingerprint:B.quota.mainQuotaToken}}function bB(B){let Z=B?.quota?.refreshEveryNRequests;return typeof Z==="number"&&Number.isFinite(Z)&&Z>0?Math.floor(Z):0}function P(B){return NB("sha256").update(B).digest("hex").slice(0,16)}class O{main=null;mainTokenFp=null;mainQuotaAccountId=null;fallbacks=new Map;fallbackTokenFps=new Map;inflightMain=null;inflightMainFp=null;inflightFallbacks=new Map;mainLastApiError=void 0;fallbackApiErrors=new Map;fallbackErrorTokenFps=new Map;rateLimitedUntilMap=new Map;apiGate=Promise.resolve();lastApiCallAt=0;storage;fetchImpl;now;fetchQuotaFn;onMainQuotaFetched;onApiError;constructor(B){this.storage=B.storage,this.fetchImpl=B.fetchImpl??fetch,this.now=B.now??Date.now,this.fetchQuotaFn=B.fetchQuotaFn,this.onMainQuotaFetched=B.onMainQuotaFetched,this.onApiError=B.onApiError,this.seedMainFromStorage(B.storage),this.seedMainBackoffFromStorage(B.storage)}getMain(B){if(B&&this.main&&this.mainTokenFp&&this.mainTokenFp!==P(B))this.main=null,this.mainTokenFp=null;return this.main}getFallback(B,Z){let $=this.fallbacks.get(B)??null;if(!Z||!$)return $;if(this.fallbackTokenFps.get(B)!==P(Z))return this.fallbacks.delete(B),this.fallbackTokenFps.delete(B),null;return $}getAllFallbacks(){return this.fallbacks}setMain(B,Z,$,W=!1){if(!W&&!M0(Z.quota))return;if(this.mainTokenFp=P(B),$&&this.mainQuotaAccountId&&this.mainQuotaAccountId!==$)this.clearRateLimited("main");if($)this.mainQuotaAccountId=$;if(this.main=Z,z0(Z.quota))this.clearRateLimited("main")}peekMainForPolicy(B){if(B&&this.mainQuotaAccountId&&this.mainQuotaAccountId!==B)return null;return this.main}peekFallbackForPolicy(B,Z){let $=this.fallbacks.get(B)??null;if(Z&&$?.accountId&&$.accountId!==Z)return null;return $}setFallback(B,Z,$,W=!1,z){if(!W&&!M0(Z.quota))return;let X=this.fallbacks.get(B)?.accountId;if(z&&X&&z!==X)this.clearRateLimited(B);let G=z??X;if(this.fallbacks.set(B,{...Z,accountId:G}),$)this.fallbackTokenFps.set(B,P($));else this.fallbackTokenFps.delete(B);if(z0(Z.quota))this.clearRateLimited(B)}markRateLimited(B,Z){let $=this.rateLimitedUntilMap.get(B);if($!==void 0&&$>Z)return;this.rateLimitedUntilMap.set(B,Z)}isRateLimited(B){let Z=this.rateLimitedUntilMap.get(B);return Z!==void 0&&this.now()<Z}rateLimitedUntil(B){return this.rateLimitedUntilMap.get(B)}clearRateLimited(B){this.rateLimitedUntilMap.delete(B)}async refreshMain(B){let Z=P(B);if(this.mainTokenFp&&this.mainTokenFp!==Z)this.main=null,this.mainTokenFp=null;if(this.inflightMain&&this.inflightMainFp===Z)return this.inflightMain;if(this.isBackedOff()){if(this.main)return this.main.quota;throw Error("Quota API rate-limited — try again later")}return this.inflightMainFp=Z,this.inflightMain=this._fetchMain(B),this.inflightMain}async refreshFallback(B,Z){let $=O.fallbackInflightKey(B,Z),W=this.inflightFallbacks.get($);if(W)return W;if(this.isFallbackBackedOff(B,Z)){let X=this.getFallback(B,Z);if(X)return X.quota;throw Error("Quota API rate-limited — try again later")}let z=this._fetchFallback(B,Z);return this.inflightFallbacks.set($,z),z}async refreshAllFallbacks(B){let Z=this.now();for(let $ of B){if($.enabled===!1)continue;if(!$.access)continue;let W=this.getFallback($.id,$.access);if(W&&Z<W.refreshAfter)continue;try{await this.refreshFallback($.id,$.access)}catch{}}}refreshMainInBackground(B){let Z=P(B);if(this.inflightMain&&this.inflightMainFp===Z)return;if(this.isBackedOff())return;this.refreshMain(B).catch(()=>{})}isMainStale(){if(!this.main)return!0;return this.now()>=this.main.refreshAfter}isFallbackStale(B,Z){let $=this.getFallback(B,Z);if(!$)return!0;return this.now()>=$.refreshAfter}shouldRefreshOnRequestCount(B){let Z=bB(this.storage);if(Z<=0)return!1;return B>0&&B%Z===0}needsRefresh(B){return this.isMainStale()||this.shouldRefreshOnRequestCount(B)}updateStorage(B){this.storage=B,this.seedMainFromStorage(B),this.seedMainBackoffFromStorage(B)}seedMainFromStorage(B,Z){let $=OB(B);if(!$)return;let W=Z?P(Z):null;if(W&&$.tokenFingerprint&&$.tokenFingerprint!==W)return;let z={quota:$.quota,refreshAfter:U0($.quota,B,$.checkedAt),checkedAt:$.checkedAt};if(this.main&&this.main.checkedAt>=z.checkedAt&&(!W||!this.mainTokenFp||this.mainTokenFp===W))return;if(this.main=z,this.mainTokenFp=$.tokenFingerprint??null,B?.mainAccountId){if(this.mainQuotaAccountId&&this.mainQuotaAccountId!==B.mainAccountId)this.clearRateLimited("main");this.mainQuotaAccountId=B.mainAccountId}}seedMainBackoffFromStorage(B){let Z=B?.quota?.mainLastQuotaApiError;this.mainLastApiError=Z&&x(Z,this.now())?Z:void 0}seedFallbacksFromAccounts(B){let Z=h(this.storage);for(let $ of B){if($.enabled===!1)continue;if(!$.quota)continue;let W=Math.max($.quota.primary?.checkedAt??0,$.quota.secondary?.checkedAt??0);if(W<=0)continue;let z=this.getFallback($.id,$.access);if(z&&z.checkedAt>=W)continue;this.setFallback($.id,{quota:$.quota,refreshAfter:W+Z,checkedAt:W},$.access,!1,$.accountId)}}isBackedOff(){return x(this.mainLastApiError,this.now())}isFallbackBackedOff(B,Z){if(Z){if(this.fallbackErrorTokenFps.get(B)!==P(Z))return!1}return x(this.fallbackApiErrors.get(B),this.now())}getLastApiError(){return this.mainLastApiError}static API_CALL_GAP_MS=1000;static fallbackInflightKey(B,Z){return JSON.stringify([B,P(Z)])}static quotaLockName(B){return`opencode-fallback-quota-refresh-${B.replace(/[^a-zA-Z0-9._-]+/g,"-")||"account"}`}_enqueueApiFetch(B){let Z=async()=>{let W=this.now()-this.lastApiCallAt;if(W<O.API_CALL_GAP_MS)await new Promise((z)=>{let X=PB(z,O.API_CALL_GAP_MS-W);if(typeof X==="object"&&"unref"in X)X.unref()});return this.lastApiCallAt=this.now(),B()},$=this.apiGate.then(Z,Z);return this.apiGate=$.catch(()=>{}),$}async _fetchMain(B){let Z=P(B);return this._enqueueApiFetch(async()=>{try{if(this.isBackedOff()){if(this.main)return this.main.quota;throw Error("Quota API rate-limited — try again later")}let $=await T({name:"opencode-main-quota-refresh",ttlMs:30000});if(!$){let W=this.main;if(W&&this.now()<W.refreshAfter)return W.quota;throw Error("Quota refresh is already in progress")}try{let W=this.now(),z=await this._fetchQuota(B),X=this.now();if(this.mainTokenFp=P(B),this.main={quota:z,refreshAfter:U0(z,this.storage,X),checkedAt:X},z0(z))this.clearRateLimited("main");return this.mainLastApiError=void 0,this.onMainQuotaFetched?.(z,X,this.mainTokenFp,W),z}catch(W){throw this._handleMainFetchError(W),W}finally{await $.release()}}finally{if(this.inflightMainFp===Z)this.inflightMain=null,this.inflightMainFp=null}})}async _fetchFallback(B,Z){return this._enqueueApiFetch(async()=>{try{if(this.isFallbackBackedOff(B,Z)){let W=this.getFallback(B,Z);if(W)return W.quota;throw Error("Quota API rate-limited — try again later")}let $=await T({name:O.quotaLockName(B),ttlMs:30000});if(!$){let W=this.getFallback(B,Z);if(W&&this.now()<W.refreshAfter)return W.quota;throw Error("Quota refresh is already in progress")}try{let W=await this._fetchQuota(Z),z=this.now();return this.setFallback(B,{quota:W,refreshAfter:z+h(this.storage),checkedAt:z},Z,!0),this.fallbackApiErrors.delete(B),this.fallbackErrorTokenFps.delete(B),W}finally{await $.release()}}catch($){throw this._handleFallbackFetchError(B,Z,$),$}finally{this.inflightFallbacks.delete(O.fallbackInflightKey(B,Z))}})}async _fetchQuota(B){if(!this.fetchQuotaFn)throw Error("Quota fetch not available — no fetchQuotaFn injected (push-only mode)");return this.fetchQuotaFn({accessToken:B,fetchImpl:this.fetchImpl,now:this.now})}static isAuthError(B){return B?.status===401}_handleMainFetchError(B){if(O.isAuthError(B))return;this.mainLastApiError=A({error:B,now:this.now(),previous:this.mainLastApiError}),this.onApiError?.(this.mainLastApiError)}_handleFallbackFetchError(B,Z,$){if(O.isAuthError($))return;let W=P(Z),z=this.fallbackErrorTokenFps.get(B)===W?this.fallbackApiErrors.get(B):void 0;this.fallbackApiErrors.set(B,A({error:$,now:this.now(),previous:z})),this.fallbackErrorTokenFps.set(B,W)}}function M0(B){return Boolean(B.primary||B.secondary)}function z0(B){let{primary:Z,secondary:$}=B;if(!Z&&!$)return!1;if(Z&&Z.usedPercent>=100)return!1;if($&&$.usedPercent>=100)return!1;return!0}import{createHash as EB,randomUUID as SB}from"node:crypto";import{homedir as RB}from"node:os";import{dirname as yB,join as n}from"node:path";import{setTimeout as xB}from"node:timers/promises";import{randomUUID as DB}from"node:crypto";import{mkdir as UB,rename as MB,rm as _B,writeFile as qB}from"node:fs/promises";import{dirname as CB}from"node:path";async function f(B,Z){await UB(CB(B),{recursive:!0});let $=`${B}.${DB()}.tmp`;await qB($,`${JSON.stringify(Z,null,2)}
|
|
88
|
+
`,{encoding:"utf8",mode:384});try{await MB($,B)}catch(W){throw await _B($,{force:!0}).catch(()=>{}),W}}import{readFile as lB}from"node:fs/promises";var E=s("refresh"),_0=1e4,TB=50,x0="openai-auth.json",fB="openai-auth-state.json";function IB(){if(process.env.OPENCODE_CONFIG_DIR?.trim())return process.env.OPENCODE_CONFIG_DIR.trim();return n(process.env.XDG_CONFIG_HOME||n(RB(),".config"),"opencode")}function I(){return process.env.OPENCODE_OPENAI_AUTH_FILE?.trim()||n(IB(),x0)}function o(B=I()){let Z=process.env.OPENCODE_OPENAI_AUTH_STATE_FILE?.trim();if(Z)return Z;return AB(B)}function AB(B){return B.endsWith(x0)?n(yB(B),fB):`${B}.state.json`}function b(B){return B.type==="oauth"}function mB(B){let Z=B?.trim();if(!Z)return!1;try{let $=new URL(Z);return($.protocol==="http:"||$.protocol==="https:")&&!$.username&&!$.password}catch{return!1}}function Z1(B){return B.costZeroing?.enabled!==!1}class c extends Error{code="ACCOUNT_REMOVED_DURING_REFRESH";constructor(B){super(`Fallback account ${B} was removed before refresh`);this.name="AccountRemovedDuringRefreshError"}}var wB=[401,403,429],q0={primary:0,secondary:0},kB=!1,K0={primary:5,secondary:10};function H(B){return B!=null&&typeof B==="object"&&!Array.isArray(B)}function hB(B){return B instanceof c||H(B)&&B.name==="AccountRemovedDuringRefreshError"&&B.code==="ACCOUNT_REMOVED_DURING_REFRESH"}function C0(B){return{id:typeof B.id==="string"&&B.id.trim()?B.id.trim():SB(),label:typeof B.label==="string"?B.label:void 0,enabled:typeof B.enabled==="boolean"?B.enabled:void 0,addedAt:typeof B.addedAt==="number"?B.addedAt:void 0,lastUsed:typeof B.lastUsed==="number"?B.lastUsed:void 0,accountId:typeof B.accountId==="string"?B.accountId:void 0}}function E0(B){if(!H(B))return;if(typeof B.message!=="string")return;let Z=Number(B.checkedAt);if(!Number.isFinite(Z))return;let $=Number(B.nextRetryAt),W=Number(B.retryCount);return{message:B.message,checkedAt:Z,nextRetryAt:Number.isFinite($)?$:void 0,retryCount:Number.isFinite(W)?W:void 0,tokenHash:typeof B.tokenHash==="string"?B.tokenHash:void 0}}function vB(B){if(!H(B))return;let Z={};for(let W of["primary","secondary"]){let z=B[W];if(!H(z))continue;let X=Number(z.usedPercent),G=Number(z.remainingPercent),K=Number(z.checkedAt);if(!Number.isFinite(X)||!Number.isFinite(G)||!Number.isFinite(K))continue;let Y=typeof z.windowMinutes==="number"?z.windowMinutes:Number.NaN;Z[W]={usedPercent:X,remainingPercent:G,checkedAt:K,resetsAt:typeof z.resetsAt==="string"?z.resetsAt:void 0,...Number.isFinite(Y)&&Y>0?{windowMinutes:Y}:{}}}let $=typeof B.resetCreditsAvailable==="number"?B.resetCreditsAvailable:Number.NaN;if(Number.isFinite($)&&$>=0)Z.resetCreditsAvailable=$;return Object.keys(Z).length?Z:void 0}function pB(B){if(!H(B))return null;if(B.type==="api"){let Z=typeof B.baseURL==="string"?B.baseURL.trim():"",$=typeof B.apiKey==="string"?B.apiKey.trim():"";if(!mB(Z))return null;let W=B.authHeader==="x-api-key"?"x-api-key":"authorization-bearer";return{...C0(B),type:"api",apiKey:$||void 0,baseURL:Z,authHeader:W}}if(B.type!=="oauth")return null;if(typeof B.refresh!=="string"||!B.refresh.trim())return null;return{...C0(B),type:"oauth",access:typeof B.access==="string"?B.access:void 0,refresh:B.refresh,expires:typeof B.expires==="number"?B.expires:void 0,lastRefreshedAt:typeof B.lastRefreshedAt==="number"?B.lastRefreshedAt:void 0,lastRefreshError:E0(B.lastRefreshError),lastQuotaRefreshError:E0(B.lastQuotaRefreshError),quota:vB(B.quota)}}function T0(B){if(!H(B)||!Array.isArray(B.accounts))return null;return{version:1,main:{type:"opencode",provider:"openai"},routing:H(B.routing)?B.routing:void 0,fallbackOn:Array.isArray(B.fallbackOn)?B.fallbackOn.filter((Z)=>Number.isInteger(Z)):void 0,refresh:H(B.refresh)?B.refresh:void 0,quota:H(B.quota)?B.quota:void 0,dump:H(B.dump)?B.dump:void 0,costZeroing:H(B.costZeroing)?B.costZeroing:void 0,killswitch:H(B.killswitch)?B.killswitch:void 0,logging:H(B.logging)?B.logging:void 0,cachekeep:H(B.cachekeep)?B.cachekeep:void 0,mainAccountId:typeof B.mainAccountId==="string"?B.mainAccountId:void 0,accounts:B.accounts.map(pB).filter((Z)=>Z!=null)}}async function R(B){try{return{exists:!0,value:JSON.parse(await lB(B,"utf8"))}}catch(Z){if(Z.code==="ENOENT")return{exists:!1,value:null};throw Z}}function D(B){return Object.fromEntries(Object.entries(B).filter(([,Z])=>Z!==void 0))}async function dB(B){let Z;try{Z=(await R(B)).value}catch{return null}if(!H(Z)||!Array.isArray(Z.accounts))return null;let $=new Set;for(let W of Z.accounts)if(H(W)&&typeof W.id==="string")$.add(W.id);return $}function f0(B,Z){if(!H(B))return B;let $=H(Z)?Z:{},W=H($.main)?$.main:void 0,z=H($.accounts)?$.accounts:{},X=H(B.quota)?B.quota:{},G=H(B.refresh)?B.refresh:{},K=W??X,Y=W??G,j=Array.isArray(B.accounts)?B.accounts.map((Q)=>{if(!H(Q))return Q;let y=typeof Q.id==="string"&&H(z[Q.id])?z[Q.id]:{};return{...Q,...y}}):void 0;return I0({...B,refresh:D({...G,mainLastRefreshError:Y.lastRefreshError,mainRefreshLeaseId:Y.refreshLeaseId,mainRefreshLeaseUntil:Y.refreshLeaseUntil,mainRefreshLeaseTokenHash:Y.refreshLeaseTokenHash}),quota:D({...X,mainQuota:K.quota,mainQuotaCheckedAt:K.quotaCheckedAt,mainQuotaToken:K.quotaToken,mainLastQuotaApiError:K.lastQuotaApiError}),accounts:j})}async function gB(B=I()){let Z=await R(B);if(!Z.exists)return null;let $=await R(o(B));return T0(f0(Z.value,$.value))}function I0(B){return Object.fromEntries(Object.entries(B).filter(([,Z])=>Z!==void 0))}function iB(B){return D({id:B.id,label:B.label,type:B.type,enabled:B.enabled,addedAt:B.addedAt,accountId:B.accountId,baseURL:B.type==="api"?B.baseURL:void 0,authHeader:B.type==="api"?B.authHeader:void 0})}function A0(B){if(B.type==="api")return D({apiKey:B.apiKey,lastUsed:B.lastUsed});return D({access:B.access,refresh:B.refresh,expires:B.expires,lastUsed:B.lastUsed,lastRefreshedAt:B.lastRefreshedAt,lastRefreshError:B.lastRefreshError,lastQuotaRefreshError:B.lastQuotaRefreshError,quota:B.quota})}function a(B){return Math.max(B?.primary?.checkedAt??0,B?.secondary?.checkedAt??0)}function v(B,Z,$){if($ in Z)B[$]=Z[$];else delete B[$]}function uB(B,Z){return B.access===Z.access&&B.refresh===Z.refresh&&B.expires===Z.expires&&B.lastRefreshedAt===Z.lastRefreshedAt}function sB(B,Z){if(!uB(B,Z))return Z;if(!("lastRefreshError"in Z))return B;if(!("lastRefreshError"in B))return Z;return(Z.lastRefreshError?.checkedAt??0)>(B.lastRefreshError?.checkedAt??0)?Z:B}function rB(B,Z,$){let W=Z.lastRefreshedAt??0,z=$.lastRefreshedAt??0,X=Z.expires??0,G=$.expires??0,K=z>W?$:W>z?Z:G>X?$:X>G?Z:sB(Z,$);v(B,K,"access"),v(B,K,"refresh"),v(B,K,"expires"),v(B,K,"lastRefreshedAt"),v(B,K,"lastRefreshError")}function nB(B,Z){if(!H(B))return Z;let $=B,W=a($.quota),z=a(Z.quota),X=W>z,G=X?{...$,...Z,quota:$.quota,lastQuotaRefreshError:$.lastQuotaRefreshError}:{...$,...Z};if(!X&&!("lastQuotaRefreshError"in Z))delete G.lastQuotaRefreshError;if(!("lastRefreshError"in Z))delete G.lastRefreshError;if(typeof $.lastUsed==="number"&&(typeof Z.lastUsed!=="number"||$.lastUsed>Z.lastUsed))G.lastUsed=$.lastUsed;return rB(G,$,Z),G}function m0(B){let Z=B.refresh?D({enabled:B.refresh.enabled,intervalMinutes:B.refresh.intervalMinutes,refreshBeforeExpiryMinutes:B.refresh.refreshBeforeExpiryMinutes}):void 0,$=B.quota?D({enabled:B.quota.enabled,checkIntervalMinutes:B.quota.checkIntervalMinutes,refreshEveryNRequests:B.quota.refreshEveryNRequests,minimumRemaining:B.quota.minimumRemaining,failClosedOnUnknownQuota:B.quota.failClosedOnUnknownQuota,showToasts:B.quota.showToasts}):void 0;return I0({version:1,main:B.main,routing:B.routing,fallbackOn:B.fallbackOn,refresh:Z,quota:$,dump:B.dump,costZeroing:B.costZeroing,killswitch:B.killswitch,logging:B.logging,cachekeep:B.cachekeep,mainAccountId:B.mainAccountId,accounts:B.accounts.map(iB)})}async function p(B){let Z=Date.now()+_0;while(Date.now()<=Z){let $=await T({name:"save",ttlMs:_0,path:B});if($)return $;let W=Z-Date.now();if(W<=0)break;await xB(Math.min(TB,W))}throw Error(`Timed out acquiring account store lock for ${B}; refusing to overwrite newer account data`)}function w0(B){let Z=Object.fromEntries(B.accounts.map(($)=>[$.id,A0($)]));return{version:1,main:D({quota:B.quota?.mainQuota,quotaCheckedAt:B.quota?.mainQuotaCheckedAt,quotaToken:B.quota?.mainQuotaToken,lastQuotaApiError:B.quota?.mainLastQuotaApiError,lastRefreshError:B.refresh?.mainLastRefreshError,refreshLeaseId:B.refresh?.mainRefreshLeaseId,refreshLeaseUntil:B.refresh?.mainRefreshLeaseUntil,refreshLeaseTokenHash:B.refresh?.mainRefreshLeaseTokenHash}),accounts:Z}}async function W1(B,Z=I()){let $=o(Z),W=await p(Z);try{let z=await p($);try{let X=await R(Z),G=await R($),K=(X.exists?T0(f0(X.value,G.value)):null)??cB(),Y=B(K)??K,j={...H(X.value)?X.value:{},...m0(Y)};return await f(Z,j),await f($,w0(Y)),Y}finally{await z.release()}}finally{await W.release()}}function cB(){return{version:1,main:{type:"opencode",provider:"openai"},accounts:[]}}function aB(B,Z){B.main=B.main??{};let $=typeof B.main.quotaCheckedAt==="number"?B.main.quotaCheckedAt:a(B.main.quota),W=typeof Z.quota?.mainQuotaCheckedAt==="number"?Z.quota.mainQuotaCheckedAt:a(Z.quota?.mainQuota);if($>W)return;B.main.quota=Z.quota?.mainQuota,B.main.quotaCheckedAt=Z.quota?.mainQuotaCheckedAt,B.main.quotaToken=Z.quota?.mainQuotaToken,B.main.lastQuotaApiError=Z.quota?.mainLastQuotaApiError}function oB(B,Z){B.main=B.main??{},B.main.lastRefreshError=Z.refresh?.mainLastRefreshError,B.main.refreshLeaseId=Z.refresh?.mainRefreshLeaseId,B.main.refreshLeaseUntil=Z.refresh?.mainRefreshLeaseUntil,B.main.refreshLeaseTokenHash=Z.refresh?.mainRefreshLeaseTokenHash}function Y0(B){if(Array.isArray(B))return B.map(Y0);if(!H(B))return B;return Object.fromEntries(Object.entries(B).filter(([,Z])=>Z!==void 0).map(([Z,$])=>[Z,Y0($)]))}async function tB(B,Z=I(),$={mainQuota:!0,mainRefresh:!0,accounts:!0}){let W=o(Z),z=await p(W);try{let X=(await R(W)).value,G=H(X)?{...X,version:1}:{version:1};if($.mainQuota)aB(G,B);if($.mainRefresh)oB(G,B);if($.accounts){let K=$.accounts===!0?null:new Set($.accounts),Y=await dB(Z);G.accounts={...H(G.accounts)?G.accounts:{}};for(let J of B.accounts){if(K&&!K.has(J.id))continue;if(Y&&!Y.has(J.id))continue;G.accounts[J.id]=nB(G.accounts[J.id],A0(J))}if(K){for(let J of K)if(!B.accounts.some((j)=>j.id===J))delete G.accounts[J]}if(Y){for(let J of Object.keys(G.accounts))if(!Y.has(J))delete G.accounts[J]}}await f(W,Y0(G))}finally{await z.release()}}function eB(B){return B?.fallbackOn?.length?B.fallbackOn:wB}function z1(B,Z){return eB(Z).includes(B)}function BZ(B){let Z=B?.quota?.minimumRemaining||{};return{primary:Z.primary??Z["5h"]??q0.primary,secondary:Z.secondary??Z["1w"]??q0.secondary}}function J0(B){return B?.quota?.enabled!==!1}function l(B){return B?.quota?.failClosedOnUnknownQuota??kB}function k0(B,Z,$=Date.now()){if(!J0(Z))return!0;if(!B)return!l(Z);let W=BZ(Z),z=l(Z);for(let X of["primary","secondary"]){let G=B[X];if(!G)continue;if(r(G,$)){if(z)return!1;continue}if(G.remainingPercent<W[X])return!1}return!0}function h0(B){return B?.killswitch?.enabled===!0}function S0(B){return{primary:B?.primary??B?.["5h"]??K0.primary,secondary:B?.secondary??B?.["1w"]??K0.secondary}}function v0(B,Z){if(!B?.killswitch)return K0;if(Z&&B.killswitch.accounts?.[Z])return S0(B.killswitch.accounts[Z]);return S0(B.killswitch.main)}function X1(B,Z,$,W=Date.now()){if(!h0(Z))return!0;if(!B)return!l(Z);let z=v0(Z,$),X=l(Z);for(let G of["primary","secondary"]){let K=B[G];if(!K)continue;if(r(K,W)){if(X)return!1;continue}if(K.remainingPercent<z[G])return!1}return!0}function G1(B,Z,$,W){if(!h0(W))return 300;let z=[],X=[{accountId:void 0,quota:B},...Z];for(let{accountId:K,quota:Y}of X){if(!Y)continue;let J=v0(W,K),j=[],Q=!1;for(let y of["primary","secondary"]){let V=Y[y];if(!V||r(V,$))continue;if(V.remainingPercent>=J[y])continue;let F=V.resetsAt?Date.parse(V.resetsAt):NaN;if(!Number.isFinite(F)||F<=$){Q=!0;break}j.push(F)}if(Q||j.length===0)continue;z.push(Math.max(...j))}if(z.length===0)return 300;let G=Math.min(...z);return Math.max(1,Math.ceil((G-$)/1000))}function ZZ(B){return typeof B.version==="number"&&Array.isArray(B.accounts)}async function K1(B,Z=I()){let $=o(Z),W=await p(Z);try{let z=await p($);try{let X=await R(Z);if(X.exists&&H(X.value)){if(ZZ(X.value))return}if(!B)return;let G={version:1,main:{type:"opencode",provider:"openai"},accounts:[]};if(B.access){let J=i({id_token:"",access_token:B.access,refresh_token:B.refresh});if(J)G.mainAccountId=J}let Y={...X.exists&&H(X.value)?X.value:{},...m0(G)};await f(Z,Y),await f($,w0(G))}finally{await z.release()}}finally{await W.release()}}function $Z(B){return B?.refresh?.enabled!==!1}function WZ(B){return(B?.refresh?.refreshBeforeExpiryMinutes??240)*60000}function zZ(B){return Math.floor(Math.random()*(B+1))}function S(B,Z,$){return!B.access||!B.expires||B.expires-$<=WZ(Z)}function XZ(B,Z){return Boolean(B.access&&typeof B.expires==="number"&&B.expires>Z)}function GZ(B,Z){return Boolean(B.mainAccountId&&Z.accountId&&Z.accountId===B.mainAccountId)}function N(B,Z){let $=B.accounts.findIndex((W)=>W.id===Z.id);if($!==-1)B.accounts[$]=Z}function X0(B){return B instanceof Error?B.message:String(B)}function j0(B,Z,$){B.lastRefreshError=N0({error:Z,now:$,refreshToken:B.refresh,previous:B.lastRefreshError})}function G0(B,Z,$){if(B.lastQuotaRefreshError=A({error:Z,now:$,previous:B.lastQuotaRefreshError}),Z?.isRefreshError===!0)j0(B,Z,$)}function KZ(B){return`fallback-oauth-refresh-${EB("sha256").update(B).digest("base64url").slice(0,16)}`}function R0(B,Z){if(!B)return!1;if(!B.resetsAt)return!0;let $=Date.parse(B.resetsAt);return Number.isFinite($)?$>Z:!0}function YZ(B,Z){if(!B)return!1;return R0(B.primary,Z)||R0(B.secondary,Z)}function JZ(B,Z,$){if(!B)return!1;let W=Z?.quota?.checkIntervalMinutes??5,z=Math.max(1,W)*60000,X=Math.max(B.primary?.checkedAt??0,B.secondary?.checkedAt??0);return $-X<z}function y0(B,Z,$){return!JZ(B.quota,Z,$)}function jZ(B,Z,$,W){return e($)&&k0(B.quota,Z,W)&&YZ(B.quota,W)}function HZ(B){let Z=B?.quota?.checkIntervalMinutes??5;return Math.max(1,Z)*60000}var QZ=60000,NZ=60000,PZ=600000,VZ=1e4,LZ=100;var Y1=globalThis.setTimeout.bind(globalThis),J1=globalThis.clearTimeout.bind(globalThis);class FZ{now;fetchImpl;configPath;refreshPromises=new Map;refreshTimer=null;quotaTimer=null;quotaManager;onFallbackStorageChanged;options;constructor(B={}){this.options=B,this.now=B.now??Date.now,this.fetchImpl=B.fetchImpl??fetch,this.configPath=B.configPath??I(),this.quotaManager=B.quotaManager??null,this.onFallbackStorageChanged=B.onFallbackStorageChanged}seedFallbackQuota(B,Z){if(!this.quotaManager)return;if(!B.quota)return;let $=Math.max(B.quota.primary?.checkedAt??0,B.quota.secondary?.checkedAt??0);if($<=0)return;let W=this.quotaManager.getFallback(B.id,B.access);if(W&&W.checkedAt>=$)return;this.quotaManager.setFallback(B.id,{quota:B.quota,refreshAfter:$+HZ(Z),checkedAt:$},B.access,!1,B.accountId)}async load(){return gB(this.configPath)}async save(B,Z){await tB(B,this.configPath,{accounts:Z??!0})}startBackgroundRefresh(){let B=async()=>{if(await this.refreshDueAccounts(),this.options.fetchQuotaFn)await this.refreshQuotaForDueAccounts()};if(B().catch(()=>{}),!this.refreshTimer){if(this.refreshTimer=setInterval(()=>{B().catch(()=>{})},QZ+zZ(NZ)),"unref"in this.refreshTimer)this.refreshTimer.unref()}}stopBackgroundRefresh(){if(this.refreshTimer)clearInterval(this.refreshTimer);if(this.quotaTimer)clearInterval(this.quotaTimer);this.refreshTimer=null,this.quotaTimer=null}async getUsableFallbackAccounts(B){let Z=B!==void 0?B:await this.load();if(!Z)return[];let $=[],W=!1;for(let z of Z.accounts){if(z.enabled===!1||!b(z))continue;if(GZ(Z,z))continue;let X=!1,G=z;try{if(S(G,Z,this.now())){let K=G.lastRefreshError;if(K&&U(K,G.refresh,this.now()))throw X=!0,Error(d(K,this.now()));try{G=await this.refreshAccount(G,Z),W=!0}catch(Y){if(hB(Y))continue;X=!0;let J=Z.accounts.find((j)=>j.id===z.id&&b(j));if(J&&!U(J.lastRefreshError,J.refresh,this.now()))j0(J,Y,this.now()),N(Z,J),W=!0;throw Y}}if(this.seedFallbackQuota(G,Z),this.accountPassesQuotaPolicy(this.quotaPolicyAccount(G),Z))$.push(G)}catch(K){let Y=XZ(G,this.now());if(X){if(!Y)continue}else if(!Y)continue;if(jZ(G,Z,K,this.now()))E.debug("fallback quota using cached quota after refresh error",{pid:process.pid,accountId:G.id,error:X0(K)}),$.push(G);else if(!l(Z))$.push(G)}}if(W)await this.save(Z);return $}async markUsed(B){let Z=await this.load();if(!Z)return;let $=Z.accounts.find((W)=>W.id===B.id);if(!$)return;$.lastUsed=this.now(),await this.save(Z)}accountPassesQuotaPolicy(B,Z){return k0(B.quota,Z,this.now())}quotaPolicyAccount(B){if(!this.quotaManager)return B;let Z=this.quotaManager.getFallback(B.id,B.access)?.quota;return Z?{...B,quota:Z}:B}async refreshDueAccounts(){let B=await this.load();if(!B||!$Z(B))return;let Z=!1;for(let $ of B.accounts){if($.enabled===!1||!b($))continue;if(!S($,B,this.now()))continue;if(U($.lastRefreshError,$.refresh,this.now()))continue;try{E.debug("fallback oauth background due",{pid:process.pid,accountId:$.id,expiresInMs:$.expires?$.expires-this.now():void 0}),await this.refreshAccount($,B),Z=!0}catch(W){E.warn("fallback oauth background failed",{pid:process.pid,accountId:$.id,error:X0(W)}),j0($,W,this.now()),N(B,$),Z=!0}}if(Z)await this.save(B)}async refreshQuotaForDueAccounts(){let B=await this.load();if(!B||!J0(B))return;if(!this.options.fetchQuotaFn)return;let Z=!1;for(let $ of B.accounts){if($.enabled===!1||!b($))continue;let W=$;try{if(S(W,B,this.now())){if(U(W.lastRefreshError,W.refresh,this.now()))continue;W=await this.refreshAccount(W,B),Z=!0}if(x(W.lastQuotaRefreshError,this.now()))continue;if(this.seedFallbackQuota(W,B),!(this.quotaManager?this.quotaManager.isFallbackStale(W.id,W.access):y0(W,B,this.now())))continue;await this.refreshAccountQuota(W,B),Z=!0}catch(z){G0($,z,this.now()),N(B,$),Z=!0}}if(Z)await this.save(B),this.onFallbackStorageChanged?.()}async refreshQuotaForAllAccounts(B={}){let Z=await this.load(),$=[];if(!Z||!J0(Z))return{storage:Z,errors:$};if(!this.options.fetchQuotaFn)return{storage:Z,errors:$};let W=B.force??!1,z=!1;for(let X of Z.accounts){if(X.enabled===!1||!b(X))continue;let G=X;try{if(S(G,Z,this.now())){let K=G.lastRefreshError;if(K&&U(K,G.refresh,this.now()))throw Error(d(K,this.now()));G=await this.refreshAccount(G,Z),z=!0}if(!W&&!y0(G,Z,this.now())){if(G.lastQuotaRefreshError)G.lastQuotaRefreshError=void 0,N(Z,G),z=!0;continue}await this.refreshAccountQuota(G,Z),z=!0}catch(K){G0(X,K,this.now()),N(Z,X),z=!0,$.push({accountId:X.id,message:X0(K)})}}if(z)await this.save(Z);return{storage:Z,errors:$}}async refreshAccount(B,Z,$={}){let W=this.refreshPromises.get(B.id);if(W){let G=await W;return N(Z,G),G}let z=this.refreshAccountNow(B,Z,$).finally(()=>{this.refreshPromises.delete(B.id)});this.refreshPromises.set(B.id,z);let X=await z;return N(Z,X),X}async waitForConcurrentFallbackRefresh(B,Z,$,W){let z=Date.now()+VZ;while(Date.now()<z){await new Promise((J)=>setTimeout(J,LZ));let X=await this.load(),G=X?.accounts.find((J)=>J.id===B.id&&b(J));if(!G)continue;if((G.access!==$.access||G.refresh!==$.refresh||(G.expires??0)>($.expires??0)+60000)&&(W.force||!S(G,X,this.now())))return N(Z,G),E.debug("fallback oauth joined concurrent refresh",{pid:process.pid,accountId:G.id,expiresInMs:G.expires?G.expires-this.now():void 0}),G;let Y=G.lastRefreshError;if(Y&&U(Y,G.refresh,this.now()))throw N(Z,G),Error(d(Y,this.now()))}return null}async refreshAccountNow(B,Z,$){let W=await this.load(),z=W?.accounts.find((K)=>K.id===B.id&&b(K));if(z&&!$.force&&!S(z,W,this.now()))return N(Z,z),z;let X=z??B,G=await T({name:KZ(X.id),ttlMs:PZ,path:this.configPath,now:this.now,renew:!0});if(!G){E.debug("fallback oauth refresh skipped file lock",{pid:process.pid,accountId:X.id});let K=await this.waitForConcurrentFallbackRefresh(B,Z,X,$);if(K)return K;throw Error("Fallback OAuth refresh is already in progress")}try{if(W=await this.load(),z=W?.accounts.find((j)=>j.id===B.id&&b(j)),z&&!$.force&&!S(z,W,this.now()))return N(Z,z),z;if(!z)throw new c(B.id);X=z;let K=this.options.refreshFn??(async()=>{throw Error("No refreshFn injected into FallbackAccountManager")});E.debug("fallback oauth refresh request start",{pid:process.pid,accountId:X.id,force:$.force===!0,expiresInMs:X.expires?X.expires-this.now():void 0});let Y=await K({refreshToken:X.refresh,fetchImpl:this.fetchImpl,now:this.now});if(X.access=Y.access,X.refresh=Y.refresh,X.expires=Y.expires,X.lastRefreshedAt=Y.expires-Y.expiresIn*1000,X.lastRefreshError=void 0,N(Z,X),await this.save(Z),!(await this.load())?.accounts.some((j)=>j.id===B.id))throw new c(B.id);return E.debug("fallback oauth refresh succeeded",{pid:process.pid,accountId:X.id,expiresInMs:X.expires?X.expires-this.now():void 0}),X}finally{await G.release()}}async refreshAccountQuota(B,Z){let $=B;if(!$.access)throw Error(`Fallback account ${B.id} has no access token`);if(!this.options.fetchQuotaFn)throw Error("No fetchQuotaFn injected — wham/usage supplement is disabled");let W=this.options.fetchQuotaFn??(async()=>{throw Error("No fetchQuotaFn injected — wham/usage supplement is disabled")}),z=(X)=>this.quotaManager?this.quotaManager.refreshFallback($.id,X):W({accessToken:X,fetchImpl:this.fetchImpl,now:this.now});try{if(!$.access)throw Error(`Fallback account ${B.id} has no access token`);let X=await z($.access);$.quota=X,$.lastQuotaRefreshError=void 0,N(Z,$),await this.save(Z)}catch(X){throw G0(B,X,this.now()),N(Z,B),X}}}
|
|
89
|
+
export{Q0 as h,N0 as i,U as j,d as k,F0 as l,b0 as m,t0 as n,V0 as o,L0 as p,i as q,Z0 as r,BB as s,C as t,zB as u,XB as v,GB as w,KB as x,qZ as y,CZ as z,D0 as A,TZ as B,fZ as C,hZ as D,O as E,I as F,b as G,Z1 as H,K0 as I,gB as J,W1 as K,z1 as L,h0 as M,v0 as N,X1 as O,G1 as P,K1 as Q,FZ as R};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createHash as B}from"node:crypto";import{homedir as F,tmpdir as Q}from"node:os";import{dirname as G,join as w,resolve as z}from"node:path";var A="OPENCODE_OPENAI_AUTH_RPC_DIR";function J(k){return B("sha256").update(k).digest("hex").slice(0,16)}function K(){let k=process.env.XDG_STATE_HOME||w(F(),".local","state");return w(k,"cortexkit","openai-auth","rpc")}function L(k){let q=process.env[A]?.trim();if(q)return z(k,q);return w(K(),`openai-auth-${J(k)}`)}async function T(k){let q=process.env[A]?.trim();if(q)return{dir:z(k,q),secureDir:!1};let x=L(k);return{dir:x,secureDir:!0,sweepRoot:G(x)}}export{Q as Aa,L as Ba,T as Ca};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{appendFileSync as Y,chmodSync as w,existsSync as B,renameSync as N,statSync as C}from"node:fs";import{tmpdir as F}from"node:os";import{join as x}from"node:path";var $={error:0,warn:1,info:2,debug:3,trace:4},A=!1,D=5242880;function X(){return process.env.OPENCODE_OPENAI_AUTH_LOG_FILE??x(F(),"opencode-openai-auth.log")}var G;function k(q){if(q===void 0||q in $)G=q}function I(){if(G)return G;let q=process.env.OPENCODE_OPENAI_AUTH_LOG_LEVEL;if(q&&q in $)return q;return"info"}var K=/^(authorization|x-api-key|cookie|set-cookie|refresh|access|token)$/i;function b(q){if(K.test(q))return!0;let H=q.toLowerCase().replace(/[-_]/g,"");if(H==="chatgptaccountid")return!0;if(H.includes("apikey"))return!0;if(H.endsWith("secret")||H.endsWith("password"))return!0;if(H.endsWith("token")&&!H.endsWith("tokens"))return!0;return!1}var L=/\b(Bearer\s+[\w.-]+|sk-[\w-]+|eyJ[\w.-]+)\b/g;function P(q){return M(q,new WeakSet)}function M(q,H){if(typeof q==="string")return q.replace(L,"***REDACTED***");if(Array.isArray(q)){if(H.has(q))return"[Circular]";H.add(q);let J=q.map((Q)=>M(Q,H));return H.delete(q),J}if(q&&typeof q==="object"){if(H.has(q))return"[Circular]";H.add(q);let J={};for(let[Q,j]of Object.entries(q))J[Q]=b(Q)?"***REDACTED***":M(j,H);return H.delete(q),J}return q}var Z=[],z,_=3;function U(q){try{w(q,384)}catch{}}function g(){try{let q=X();if(!(B(q)&&C(q).size>D))return;for(let J=_-1;J>=1;J--)if(B(`${q}.${J}`)){let Q=`${q}.${J+1}`;N(`${q}.${J}`,Q),U(Q)}let H=`${q}.1`;N(q,H),U(H)}catch{}}function V(){if(!Z.length)return;let q=Z.join("");Z=[];try{g();let H=X();if(B(H))U(H);Y(H,q,{encoding:"utf8",mode:384})}catch{}}function O(){if(!z)z=setTimeout(()=>{z=void 0,V()},500)}if(!A)process.on("exit",V);function p(q){try{return` ${JSON.stringify(P(q))}`}catch{return" [unserializable]"}}function W(q,H,J,Q){if($[H]>$[I()])return;let j=`[${new Date().toISOString()}] ${H.toUpperCase()} [${q}] ${J}`+(Q===void 0?"":p(Q))+`
|
|
2
|
+
`;if(Z.push(j),Z.length>=50)V();else O()}function y(q){return{error:(H,J)=>W(q,"error",H,J),warn:(H,J)=>W(q,"warn",H,J),info:(H,J)=>W(q,"info",H,J),debug:(H,J)=>W(q,"debug",H,J),trace:(H,J)=>W(q,"trace",H,J)}}
|
|
3
|
+
export{k as xa,P as ya,y as za};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import{randomUUID as L}from"node:crypto";import{mkdir as M,readFile as C,rename as u,rm as $,stat as O,writeFile as S}from"node:fs/promises";import{homedir as E}from"node:os";import{dirname as v,join as x}from"node:path";var m=globalThis.setTimeout.bind(globalThis),P=globalThis.clearTimeout.bind(globalThis),k="openai-auth.json";function p(){if(process.env.OPENCODE_CONFIG_DIR?.trim())return process.env.OPENCODE_CONFIG_DIR.trim();return x(process.env.XDG_CONFIG_HOME||x(E(),".config"),"opencode")}function h(z){let b=z?.code;return b==="ENOENT"||b==="EINVAL"||b==="ENOTDIR"}function l(){return process.env.OPENCODE_OPENAI_AUTH_FILE?.trim()||x(p(),k)}async function i(z){let G=`${z.path??l()}.${z.name}.lock`,T=x(G,"owner.json"),X=L(),K=z.now??Date.now,W=null,j=!1;async function N(){try{return JSON.parse(await C(G,"utf8"))}catch(B){if(B.code!=="EISDIR")throw B;return JSON.parse(await C(T,"utf8"))}}async function I(){await S(G,`${JSON.stringify({ownerId:X,expiresAt:K()+z.ttlMs})}
|
|
2
|
+
`,{encoding:"utf8",mode:384})}async function R(){try{return await S(G,`${JSON.stringify({ownerId:X,expiresAt:K()+z.ttlMs})}
|
|
3
|
+
`,{encoding:"utf8",mode:384,flag:"wx"}),!0}catch(B){let J=B.code;if(J==="EEXIST"||J==="EISDIR")return!1;if(J==="ENOENT"){await M(v(G),{recursive:!0});try{return await S(G,`${JSON.stringify({ownerId:X,expiresAt:K()+z.ttlMs})}
|
|
4
|
+
`,{encoding:"utf8",mode:384,flag:"wx"}),!0}catch(V){let _=V.code;if(_==="EEXIST"||_==="EISDIR")return!1;throw V}}throw B}}function U(){if(!z.renew||j)return;let B=z.renewIntervalMs??Math.max(1000,Math.floor(z.ttlMs/3));if(W=m(()=>{(async()=>{try{let J=await N(),V=K();if(j||J?.ownerId!==X||Number(J?.expiresAt)<=V)return;await I(),U()}catch{}})()},B),"unref"in W)W.unref()}let Q=await R();if(!Q){let B=`${G}.evicting`,J=x(B,"owner.json"),V=L(),_=5000,w=8;async function D(){await new Promise((H)=>setTimeout(H,Math.floor(Math.random()*4)))}async function y(){try{let H=await N();return Number(H?.expiresAt)>K()}catch{try{return(await O(G)).mtimeMs+z.ttlMs>K()}catch{return!1}}}async function Y(){try{return JSON.parse(await C(J,"utf8"))?.ownerId===V}catch{return!1}}async function f(){await M(B);try{await S(J,`${JSON.stringify({ownerId:V,createdAt:K()})}
|
|
5
|
+
`,{encoding:"utf8",mode:384,flag:"wx"})}catch(H){if(h(H))return!1;throw await F(),H}return await z.onStep?.("eviction-marker-acquired"),!0}async function F(){if(await Y())await $(B,{recursive:!0,force:!0}).catch(()=>{})}for(let H=0;H<8;H++){if(Q=await R(),Q)break;if(await y())return null;try{if(!await f()){await D();continue}}catch(q){if(q.code!=="EEXIST")throw q;let g;try{g=await O(B)}catch(Z){if(Z.code==="ENOENT"){await D();continue}throw Z}if(g.mtimeMs+5000>K())return null;await z.onStep?.("stale-marker-stat");let A=`${B}.${L()}`;try{await u(B,A)}catch(Z){if(Z.code==="ENOENT"){await D();continue}throw Z}await z.onStep?.("stale-marker-claimed"),await $(A,{recursive:!0,force:!0}).catch(()=>{}),await D();continue}try{if(await y())return null;if(!await Y())return null;if(await z.onStep?.("stale-lock-confirmed"),!await Y())return null;if(await $(G,{recursive:!0,force:!0}).catch(()=>{}),!await Y())return null;if(Q=await R(),!Q)return null;if(!await Y())return await $(G,{recursive:!0,force:!0}).catch(()=>{}),Q=!1,null;break}finally{await F()}}}if(!Q)return null;return U(),{release:async()=>{if(j=!0,W)P(W),W=null;try{if((await N())?.ownerId!==X)return}catch{return}await $(G,{recursive:!0,force:!0}).catch(()=>{})}}}
|
|
6
|
+
export{i as ta};
|