@cortexkit/opencode-openai-auth 0.7.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +9 -5
  2. package/dist/{index-9jpc8yct.js → index-02h7g5f9.js} +1 -1
  3. package/dist/index-2xppkvkx.js +2 -0
  4. package/dist/index-3w4vmckx.js +2 -0
  5. package/dist/{index-zxqb5ydj.js → index-507jh8bv.js} +1 -1
  6. package/dist/{index-zg1x8sty.js → index-944gsj1s.js} +1 -1
  7. package/dist/index-nshbgkz1.js +198 -0
  8. package/dist/{index-87145jkw.js → index-xyg86jgt.js} +1 -1
  9. package/dist/index.d.ts +381 -35
  10. package/dist/index.js +66 -161
  11. package/dist/protocol-Cv9ZmU0p.d.ts +24 -0
  12. package/dist/quota-normalize-wap0rne8.js +1 -0
  13. package/dist/rpc/port-file.d.ts +11 -9
  14. package/dist/rpc/port-file.js +1 -1
  15. package/dist/rpc/protocol.d.ts +2 -21
  16. package/dist/rpc/rpc-client.d.ts +6 -4
  17. package/dist/rpc/rpc-client.js +1 -1
  18. package/dist/rpc/rpc-dir.d.ts +7 -5
  19. package/dist/rpc/rpc-dir.js +1 -1
  20. package/dist/sidebar-state.d.ts +84 -83
  21. package/dist/sidebar-state.js +1 -1
  22. package/dist/{token-bvp0y166.js → token-jdvpwk60.js} +1 -1
  23. package/dist/token-util-zzm7paz9.js +1 -0
  24. package/dist/tui-preferences.d.ts +27 -26
  25. package/dist/tui-preferences.js +1 -1
  26. package/dist/tui.d.ts +18 -17
  27. package/package.json +7 -14
  28. package/src/tui/entry.mjs +2 -2
  29. package/src/tui-compiled/{core/account-paths.ts → raw/core-account-paths.ts} +29 -18
  30. package/src/tui-compiled/raw/logger.ts +53 -0
  31. package/src/tui-compiled/{rpc/port-file.ts → raw/rpc-port-file.ts} +1 -1
  32. package/src/tui-compiled/raw/rpc-protocol.ts +12 -0
  33. package/src/{rpc/rpc-client.ts → tui-compiled/raw/rpc-rpc-client.ts} +2 -2
  34. package/src/{sidebar-state.ts → tui-compiled/raw/sidebar-state.ts} +2 -2
  35. package/src/{tui/command-dialogs.tsx → tui-compiled/raw/tui-command-dialogs.tsx} +5 -5
  36. package/src/{tui.tsx → tui-compiled/raw/tui.tsx} +11 -10
  37. package/src/{core/account-paths.ts → tui-compiled/runtime/core-account-paths.ts} +29 -18
  38. package/src/tui-compiled/runtime/logger.ts +53 -0
  39. package/src/{rpc/port-file.ts → tui-compiled/runtime/rpc-port-file.ts} +1 -1
  40. package/src/tui-compiled/runtime/rpc-protocol.ts +12 -0
  41. package/src/tui-compiled/{rpc/rpc-client.ts → runtime/rpc-rpc-client.ts} +2 -2
  42. package/src/tui-compiled/{sidebar-state.ts → runtime/sidebar-state.ts} +2 -2
  43. package/src/tui-compiled/{tui/command-dialogs.tsx → runtime/tui-command-dialogs.tsx} +3 -4
  44. package/src/tui-compiled/{tui.tsx → runtime/tui.tsx} +10 -10
  45. package/src/tui-compiled/shared/internal.ts +6 -0
  46. package/src/{logger.ts → tui-compiled/shared/logger.ts} +82 -21
  47. package/src/tui-compiled/shared/paths.ts +33 -0
  48. package/src/{core → tui-compiled/shared}/refresh-file-lock.ts +7 -4
  49. package/dist/cli.d.ts +0 -2
  50. package/dist/cli.js +0 -15
  51. package/dist/codex-http.d.ts +0 -3
  52. package/dist/commands.d.ts +0 -88
  53. package/dist/config.d.ts +0 -47
  54. package/dist/core/account-paths.d.ts +0 -25
  55. package/dist/core/accounts.d.ts +0 -330
  56. package/dist/core/atomic-write.d.ts +0 -1
  57. package/dist/core/background-quota-refresh.d.ts +0 -46
  58. package/dist/core/backoff.d.ts +0 -53
  59. package/dist/core/cachekeep.d.ts +0 -118
  60. package/dist/core/oauth.d.ts +0 -138
  61. package/dist/core/provider.d.ts +0 -70
  62. package/dist/core/quota-manager.d.ts +0 -192
  63. package/dist/core/refresh-all-quota.d.ts +0 -86
  64. package/dist/core/refresh-file-lock.d.ts +0 -12
  65. package/dist/core/reset-credits.d.ts +0 -116
  66. package/dist/core/sticky-routing.d.ts +0 -43
  67. package/dist/dump.d.ts +0 -20
  68. package/dist/hosted-web-search.d.ts +0 -5
  69. package/dist/index-8f1200qx.js +0 -6
  70. package/dist/index-cfjed1st.js +0 -89
  71. package/dist/index-nrvjr97q.js +0 -3
  72. package/dist/index-qaggaze1.js +0 -2
  73. package/dist/index-sdcpzbnk.js +0 -2
  74. package/dist/logger.d.ts +0 -22
  75. package/dist/model-costs.d.ts +0 -29
  76. package/dist/prompt-context.d.ts +0 -19
  77. package/dist/quota-normalize-dvwtmy7f.js +0 -1
  78. package/dist/quota-normalize.d.ts +0 -43
  79. package/dist/raw-ws-bun.d.ts +0 -40
  80. package/dist/raw-ws-node.d.ts +0 -37
  81. package/dist/raw-ws-upgrade.d.ts +0 -2
  82. package/dist/raw-ws.d.ts +0 -3
  83. package/dist/response-stream-error.d.ts +0 -12
  84. package/dist/rpc/notifications.d.ts +0 -5
  85. package/dist/rpc/rpc-server.d.ts +0 -17
  86. package/dist/token-util-jcj7k7pa.js +0 -1
  87. package/dist/tui/command-dialogs.d.ts +0 -21
  88. package/dist/util/error.d.ts +0 -1
  89. package/dist/util/open-url.d.ts +0 -6
  90. package/dist/util/proxy-env.d.ts +0 -4
  91. package/dist/util/record.d.ts +0 -1
  92. package/dist/util/stable-json.d.ts +0 -1
  93. package/dist/util/uuid-v7.d.ts +0 -1
  94. package/dist/version.d.ts +0 -1
  95. package/dist/ws-pool.d.ts +0 -57
  96. package/dist/ws.d.ts +0 -71
  97. package/src/rpc/notifications.ts +0 -54
  98. package/src/rpc/protocol.ts +0 -33
  99. package/src/rpc/rpc-server.ts +0 -172
  100. package/src/tui-compiled/core/refresh-file-lock.ts +0 -381
  101. package/src/tui-compiled/logger.ts +0 -196
  102. package/src/tui-compiled/rpc/protocol.ts +0 -33
  103. package/src/util/error.ts +0 -9
  104. package/src/util/open-url.ts +0 -30
  105. package/src/util/proxy-env.ts +0 -42
  106. package/src/util/record.ts +0 -3
  107. package/src/util/stable-json.ts +0 -15
  108. package/src/util/uuid-v7.ts +0 -18
  109. /package/src/{rpc/rpc-dir.ts → tui-compiled/raw/rpc-rpc-dir.ts} +0 -0
  110. /package/src/tui-compiled/{tui-preferences.ts → raw/tui-preferences.ts} +0 -0
  111. /package/src/tui-compiled/{rpc/rpc-dir.ts → runtime/rpc-rpc-dir.ts} +0 -0
  112. /package/src/{tui-preferences.ts → tui-compiled/runtime/tui-preferences.ts} +0 -0
  113. /package/src/tui-compiled/{util → shared/util}/error.ts +0 -0
  114. /package/src/tui-compiled/{util → shared/util}/open-url.ts +0 -0
@@ -1,53 +0,0 @@
1
- export declare const NON_TRANSIENT_REFRESH_RETRY_DELAY_MS: number;
2
- export declare function isTransientRefreshError(error: unknown): boolean;
3
- export declare function isTransientQuotaError(error: unknown): boolean;
4
- export declare function hashRefreshToken(refreshToken: string): string;
5
- export declare function buildRefreshOperationError(input: {
6
- error: unknown;
7
- now: number;
8
- refreshToken: string;
9
- previous?: {
10
- message: string;
11
- checkedAt: number;
12
- nextRetryAt?: number;
13
- retryCount?: number;
14
- tokenHash?: string;
15
- };
16
- }): {
17
- message: string;
18
- checkedAt: number;
19
- nextRetryAt: number;
20
- retryCount: number;
21
- tokenHash: string;
22
- };
23
- export declare function refreshBackoffActive(error: {
24
- nextRetryAt?: number;
25
- tokenHash?: string;
26
- } | undefined, refreshToken: string | undefined, now: number): boolean;
27
- export declare function formatRefreshBackoffMessage(error: {
28
- message: string;
29
- nextRetryAt?: number;
30
- }, now: number): string;
31
- export declare function buildQuotaOperationError(input: {
32
- error: unknown;
33
- now: number;
34
- previous?: {
35
- message: string;
36
- checkedAt: number;
37
- nextRetryAt?: number;
38
- retryCount?: number;
39
- };
40
- }): {
41
- message: string;
42
- checkedAt: number;
43
- nextRetryAt: number;
44
- retryCount: number;
45
- };
46
- export declare function quotaBackoffActive(error: {
47
- nextRetryAt?: number;
48
- } | undefined, now: number): boolean;
49
- export declare function formatQuotaBackoffMessage(error: {
50
- message: string;
51
- nextRetryAt?: number;
52
- }, now: number): string;
53
- export declare function parseRetryAfter(value: string | undefined | null): number | undefined;
@@ -1,118 +0,0 @@
1
- import type { AccountStorage } from './accounts.ts';
2
- export type CacheKeepWindow = {
3
- startHour: number;
4
- endHour: number;
5
- };
6
- export interface Target {
7
- bodyText: string;
8
- accountId: string | undefined;
9
- chatgptAccountId?: string;
10
- route: 'main';
11
- cacheExpiresAt: number;
12
- ttlMs: number;
13
- warmCount: number;
14
- lastRealRequestAt: number;
15
- lastWarmedAt?: number;
16
- backoffUntil?: number;
17
- replayHeaders: Record<string, string>;
18
- isSubagent?: boolean;
19
- is56: boolean;
20
- }
21
- export interface CacheKeepManagerOptions {
22
- fetchImpl: typeof fetch;
23
- getMainToken: () => Promise<string>;
24
- refreshFallback: (accountId: string) => Promise<string>;
25
- codexResponsesUrl: string;
26
- logger: {
27
- info: (msg: string, data?: unknown) => void;
28
- warn: (msg: string, data?: unknown) => void;
29
- debug: (msg: string, data?: unknown) => void;
30
- error: (msg: string, data?: unknown) => void;
31
- };
32
- now: () => number;
33
- ttlMs?: number;
34
- leadMs?: number;
35
- maxDurationMs?: number;
36
- maxIdleWarmMs?: number;
37
- maxSubagentIdleMs?: number;
38
- tickIntervalMs?: number;
39
- maxTargets?: number;
40
- maxBytes?: number;
41
- /** Returns the configured clock-hour window; undefined means always warm. */
42
- getWindow?: () => CacheKeepWindow | undefined;
43
- /** Returns whether main-agent targets bypass only the idle warm cap. */
44
- getSustain?: () => boolean;
45
- }
46
- export interface CacheKeepStatus {
47
- running: boolean;
48
- tracked: number;
49
- generatedAt: number;
50
- startedAt: number | null;
51
- maxIdleWarmMs: number;
52
- maxSubagentIdleMs: number;
53
- ttlMs: number;
54
- leadMs: number;
55
- sustain: boolean;
56
- window?: CacheKeepWindow;
57
- targets: Array<{
58
- sessionKey: string;
59
- accountId: string | undefined;
60
- route: 'main';
61
- cacheExpiresAt: number;
62
- lastRealRequestAt: number;
63
- lastWarmedAt?: number;
64
- backoffUntil?: number;
65
- bodyBytes: number;
66
- }>;
67
- }
68
- export interface KeepwarmCapture {
69
- sessionKey: string;
70
- bodyText: string;
71
- replayHeaders: Record<string, string>;
72
- isSubagent: boolean;
73
- }
74
- export declare function buildKeepwarmCapture(input: {
75
- enabled: boolean;
76
- includeSubagents: boolean;
77
- headers: Headers;
78
- body: unknown;
79
- }): KeepwarmCapture | undefined;
80
- export declare function getCacheKeepWindow(storage: AccountStorage | null): CacheKeepWindow | undefined;
81
- export declare function isWithinCacheKeepWindow(window: CacheKeepWindow | undefined, now?: Date): boolean;
82
- export declare function buildKeepwarmBody(bodyText: string): string;
83
- export declare function ttlForModel(bodyText: string, defaultTtlMs: number): number;
84
- export declare class CacheKeepManager {
85
- private readonly targets;
86
- private readonly fetchImpl;
87
- private readonly getMainToken;
88
- private readonly refreshFallback;
89
- private readonly codexResponsesUrl;
90
- private readonly log;
91
- private readonly now;
92
- private readonly ttlMs;
93
- private readonly leadMs;
94
- private readonly maxIdleWarmMs;
95
- private readonly maxSubagentIdleMs;
96
- private readonly tickIntervalMs;
97
- private readonly maxTargets;
98
- private readonly maxBytes;
99
- private readonly getWindow?;
100
- private readonly getSustain?;
101
- private timer;
102
- private startedAt;
103
- private totalBytes;
104
- private tickInFlight;
105
- private disposed;
106
- private abortController;
107
- private logPayload;
108
- constructor(options: CacheKeepManagerOptions);
109
- track(sessionKey: string, bodyText: string, accountId: string | undefined, chatgptAccountId?: string, replayHeaders?: Record<string, string>, isSubagent?: boolean): void;
110
- private isGpt56Subagent;
111
- private pruneStale;
112
- start(): void;
113
- stop(): void;
114
- remove(sessionKey: string): void;
115
- status(): CacheKeepStatus;
116
- tick(): Promise<void>;
117
- private prewarm;
118
- }
@@ -1,138 +0,0 @@
1
- export declare const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
2
- export declare const ISSUER = "https://auth.openai.com";
3
- export declare const OAUTH_PORT = 1455;
4
- export declare const OAUTH_POLLING_SAFETY_MARGIN_MS = 3000;
5
- export declare const USER_AGENT = "cortexkit-opencode-openai-auth/0.7.2";
6
- export declare const RESERVED_ACCOUNT_ID = "main";
7
- export declare const RESERVED_ACCOUNT_ID_ERROR = "\"main\" is a reserved account id; choose a different label.";
8
- export interface PkceCodes {
9
- verifier: string;
10
- challenge: string;
11
- }
12
- export declare function base64UrlEncode(buffer: ArrayBuffer): string;
13
- export declare function generatePKCE(): Promise<PkceCodes>;
14
- export interface IdTokenClaims {
15
- chatgpt_account_id?: string;
16
- email?: string;
17
- organizations?: Array<{
18
- id: string;
19
- }>;
20
- 'https://api.openai.com/auth'?: {
21
- chatgpt_account_id?: string;
22
- };
23
- }
24
- export declare function parseJwtClaims(token: string): IdTokenClaims | undefined;
25
- export declare function extractAccountIdFromClaims(claims: IdTokenClaims): string | undefined;
26
- export interface TokenResponse {
27
- id_token: string;
28
- access_token: string;
29
- refresh_token: string;
30
- expires_in?: number;
31
- }
32
- export declare function extractAccountId(tokens: TokenResponse): string | undefined;
33
- export declare function isReservedAccountId(value: string | undefined): boolean;
34
- export declare function assertFallbackAccountIdAllowed<T extends string | undefined>(value: T): T;
35
- export declare function buildAuthorizeUrl(redirectUri: string, pkce: PkceCodes, state: string): string;
36
- export declare function exchangeCodeForTokens(code: string, redirectUri: string, pkce: PkceCodes): Promise<TokenResponse>;
37
- export declare function escapeHtml(value: string): string;
38
- export declare const HTML_SUCCESS = "<!doctype html>\n<html>\n <head>\n <title>CortexKit OpenAI Auth - Authorization Successful</title>\n <style>\n body {\n font-family:\n system-ui,\n -apple-system,\n sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100vh;\n margin: 0;\n background: #131010;\n color: #f1ecec;\n }\n .container {\n text-align: center;\n padding: 2rem;\n }\n h1 {\n color: #f1ecec;\n margin-bottom: 1rem;\n }\n p {\n color: #b7b1b1;\n }\n </style>\n </head>\n <body>\n <div class=\"container\">\n <h1>Authorization Successful</h1>\n <p>You can close this window and return to OpenCode.</p>\n </div>\n <script>\n setTimeout(() => window.close(), 2000)\n </script>\n </body>\n</html>";
39
- export declare const HTML_ERROR: (error: string) => string;
40
- export interface PendingOAuth {
41
- pkce: PkceCodes;
42
- state: string;
43
- resolve: (tokens: TokenResponse) => void;
44
- reject: (error: Error) => void;
45
- }
46
- export declare function flowCleanup(state: string): void;
47
- export declare function startOAuthServer(): Promise<{
48
- port: number;
49
- redirectUri: string;
50
- }>;
51
- export declare function stopOAuthServer(): void;
52
- export declare function resetOAuthStateForTest(): void;
53
- export declare function waitForOAuthCallback(pkce: PkceCodes, state: string, timeoutMs?: number, signal?: AbortSignal): Promise<TokenResponse>;
54
- export interface DeviceAuthInit {
55
- device_auth_id: string;
56
- user_code: string;
57
- interval: string;
58
- expires_in?: number | string;
59
- }
60
- export declare function beginDeviceAuth(): Promise<{
61
- deviceData: DeviceAuthInit;
62
- url: string;
63
- instructions: string;
64
- }>;
65
- export declare function completeDeviceAuth(deviceData: DeviceAuthInit, signal?: AbortSignal): Promise<TokenResponse>;
66
- /**
67
- * Minimal shape of a fallback OAuth account for ingestion. Matches the
68
- * fields used by upsertAccount and beginAccountLogin, compatible with
69
- * the full OAuthAccount type in accounts.ts.
70
- */
71
- export interface IngestAccount {
72
- id: string;
73
- label?: string;
74
- type: 'oauth';
75
- access?: string;
76
- refresh: string;
77
- expires?: number;
78
- enabled: boolean;
79
- addedAt: number;
80
- lastUsed: number;
81
- /**
82
- * When the token was obtained. Stamped at login so a freshly added account
83
- * carries a refresh marker — without it, the runtime-state merge cannot tell a
84
- * rotated token from a stale one (both default to 0) and a concurrent stale
85
- * save could roll the token back.
86
- */
87
- lastRefreshedAt?: number;
88
- /** Stable ChatGPT account identifier from the OAuth token claims. */
89
- accountId?: string;
90
- }
91
- export interface AccountStorageLike {
92
- version: 1;
93
- mainAccountId?: string;
94
- accounts: IngestAccount[];
95
- }
96
- /**
97
- * Dedup by stable accountId first (strongest signal — same ChatGPT account
98
- * added twice with different labels must merge), then by id, then by label.
99
- * If found, merge-update preserving addedAt. Otherwise push.
100
- * Re-running `add --label work` is idempotent.
101
- *
102
- * Accepts the accounts array directly (not the whole storage object) to
103
- * avoid coupling to any particular storage shape.
104
- */
105
- export declare function upsertAccount<T extends {
106
- id: string;
107
- label?: string;
108
- accountId?: string;
109
- addedAt?: number;
110
- }>(accounts: T[], account: T): number;
111
- export interface BeginAccountLoginOptions {
112
- label?: string;
113
- headless?: boolean;
114
- signal?: AbortSignal;
115
- }
116
- export interface BeginAccountLoginResult {
117
- url: string;
118
- instructions: string;
119
- /** Resolves after the user completes the OAuth flow with a ready-to-ingest account. */
120
- completion: Promise<IngestAccount>;
121
- }
122
- /**
123
- * Split-return OAuth flow entry point.
124
- *
125
- * Browser flow (default):
126
- * 1. Start OAuth server, generate PKCE, build authorize URL
127
- * 2. Return { url, instructions, completion } — url is ready immediately
128
- * 3. completion resolves after browser callback + token exchange
129
- *
130
- * Headless flow:
131
- * 1. Begin device auth
132
- * 2. Return { url, instructions, completion }
133
- * 3. completion polls device endpoint + exchanges for tokens
134
- *
135
- * The split return allows the TUI command to show the URL before the
136
- * (potentially 30-60s) wait, avoiding a deadlock.
137
- */
138
- export declare function beginAccountLogin(opts?: BeginAccountLoginOptions): Promise<BeginAccountLoginResult>;
@@ -1,70 +0,0 @@
1
- /**
2
- * Provider injection seam — shared types and the 2 Codex provider fns.
3
- *
4
- * Both fns are constructor-injected into FallbackAccountManager and
5
- * QuotaManager so the generic core never imports provider specifics
6
- * directly.
7
- */
8
- import type { OAuthQuotaSnapshot } from './accounts.ts';
9
- declare const log: {
10
- error: (m: string, d?: unknown) => void;
11
- warn: (m: string, d?: unknown) => void;
12
- info: (m: string, d?: unknown) => void;
13
- debug: (m: string, d?: unknown) => void;
14
- trace: (m: string, d?: unknown) => void;
15
- };
16
- type QuotaLogger = Pick<typeof log, 'debug' | 'warn'>;
17
- export type QuotaWindowName = string;
18
- export declare const PRIMARY: QuotaWindowName;
19
- export declare const SECONDARY: QuotaWindowName;
20
- export interface ProviderHttpError extends Error {
21
- status?: number;
22
- retryAfter?: number;
23
- /** True when this error originated from a token-refresh call (not a quota fetch). */
24
- isRefreshError?: boolean;
25
- }
26
- export type ProviderRefreshFn = (input: {
27
- refreshToken: string;
28
- fetchImpl: typeof fetch;
29
- now: () => number;
30
- }) => Promise<{
31
- access: string;
32
- refresh: string;
33
- expires: number;
34
- expiresIn: number;
35
- }>;
36
- export type ProviderQuotaFn = (input: {
37
- accessToken: string;
38
- fetchImpl: typeof fetch;
39
- now: () => number;
40
- }) => Promise<OAuthQuotaSnapshot>;
41
- export declare const CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
42
- export declare const CODEX_ISSUER = "https://auth.openai.com";
43
- /**
44
- * Core Codex OAuth refresh — the single entry point for refreshing an
45
- * OpenAI OAuth token. Used by both the legacy refreshAccessToken in
46
- * index.ts AND the FallbackAccountManager (via constructor injection).
47
- *
48
- * Throws a ProviderHttpError carrying `.status` (from the HTTP response)
49
- * and `.retryAfter` (parsed from Retry-After) so backoff machinery can
50
- * duck-type them without instanceof checks.
51
- */
52
- export declare function codexRefreshFn(input: {
53
- refreshToken: string;
54
- fetchImpl: typeof fetch;
55
- now: () => number;
56
- }): Promise<{
57
- access: string;
58
- refresh: string;
59
- expires: number;
60
- expiresIn: number;
61
- }>;
62
- export declare function whamUsageFn(input: {
63
- accessToken: string;
64
- fetchImpl: typeof fetch;
65
- now: () => number;
66
- accountId?: string;
67
- accountKey?: string;
68
- logger?: QuotaLogger;
69
- }): Promise<OAuthQuotaSnapshot>;
70
- export {};
@@ -1,192 +0,0 @@
1
- /**
2
- * Unified quota cache and API gateway.
3
- *
4
- * Single source of truth for main + fallback quota state. All consumers
5
- * share one QuotaManager instance so they see the same in-memory cache.
6
- * Handles deduplication, rate-limiting (429 backoff), and staleness.
7
- *
8
- * Adapted from anthropic-auth — provider-specific quota fetch injected via
9
- * fetchQuotaFn instead of hard-importing fetchOAuthQuotaSnapshot.
10
- */
11
- import type { AccountOperationError, AccountQuotaWindow, AccountStorage, OAuthAccount, OAuthQuotaSnapshot } from './accounts.ts';
12
- import { type ProviderQuotaFn } from './provider.ts';
13
- export type { ProviderQuotaFn };
14
- export declare function quotaWindowResetIsPast(window: AccountQuotaWindow | undefined, now: number): boolean;
15
- /**
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.
22
- */
23
- export declare function resolveMidStreamRateLimitResetAt(quota: OAuthQuotaSnapshot | undefined, window: string, now: number, defaultMs: number, explicitResetAt?: number): number;
24
- /**
25
- * Stable, non-reversible fingerprint of an access token. Used to detect a
26
- * main-account switch so a different account's persisted/cached quota is never
27
- * reused. Not a secret — a truncated SHA-256, safe to persist alongside quota.
28
- */
29
- export declare function tokenFingerprint(token: string): string;
30
- export type QuotaEntry = {
31
- quota: OAuthQuotaSnapshot;
32
- refreshAfter: number;
33
- checkedAt: number;
34
- accountId?: string;
35
- };
36
- export type QuotaManagerOptions = {
37
- storage: AccountStorage | null;
38
- fetchImpl?: typeof fetch;
39
- now?: () => number;
40
- /** Injected quota-fetch function — replaces the hard-imported Anthropic fetchOAuthQuotaSnapshot */
41
- fetchQuotaFn?: ProviderQuotaFn;
42
- onMainQuotaFetched?: (quota: OAuthQuotaSnapshot, checkedAt: number, fingerprint: string, fetchStartedAt: number) => void;
43
- onApiError?: (error: AccountOperationError) => void;
44
- };
45
- export declare class QuotaManager {
46
- private main;
47
- private mainTokenFp;
48
- private mainQuotaAccountId;
49
- private fallbacks;
50
- private fallbackTokenFps;
51
- private inflightMain;
52
- private inflightMainFp;
53
- private inflightFallbacks;
54
- private mainLastApiError;
55
- private fallbackApiErrors;
56
- private fallbackErrorTokenFps;
57
- private rateLimitedUntilMap;
58
- private apiGate;
59
- private lastApiCallAt;
60
- private storage;
61
- private readonly fetchImpl;
62
- private readonly now;
63
- private readonly fetchQuotaFn;
64
- private readonly onMainQuotaFetched;
65
- private readonly onApiError;
66
- constructor(opts: QuotaManagerOptions);
67
- /**
68
- * Cached main quota entry. Pass the live access token to enforce token
69
- * binding: if the cached entry was produced by a different token (main
70
- * account switched), it is dropped and null is returned so the caller
71
- * refetches for the current account. Called without a token (e.g. for
72
- * display) it returns whatever is cached.
73
- */
74
- getMain(accessToken?: string): QuotaEntry | null;
75
- /**
76
- * Cached fallback quota entry. Pass the live access token to enforce token
77
- * binding: if the entry was produced by a different token (account re-login),
78
- * it is dropped and null is returned so the caller refetches.
79
- */
80
- getFallback(accountId: string, accessToken?: string): QuotaEntry | null;
81
- getAllFallbacks(): Map<string, QuotaEntry>;
82
- setMain(accessToken: string, entry: QuotaEntry, accountId?: string, completeSnapshot?: boolean): void;
83
- /**
84
- * Non-invalidating read of the cached main quota for POLICY decisions
85
- * (killswitch). Unlike getMain(token), a token mismatch does NOT drop the
86
- * cache — an access-token refresh is a same-account event and must not turn a
87
- * known-exhausted account into "unknown" (which would fail open and spend).
88
- *
89
- * Pass the live ChatGPT accountId to still drop on a genuine account SWITCH:
90
- * if the cached entry's account identity is known and differs, return null so
91
- * the killswitch treats it as unknown rather than judging account A by account
92
- * B's quota. When identity is unknown on either side, the cached snapshot is
93
- * returned (best-effort) — the same fail-open-on-unknown stance as elsewhere.
94
- */
95
- peekMainForPolicy(accountId?: string): QuotaEntry | null;
96
- /**
97
- * Non-invalidating read of a cached fallback quota for POLICY decisions
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.
106
- */
107
- peekFallbackForPolicy(accountId: string, identity?: string): QuotaEntry | null;
108
- setFallback(accountId: string, entry: QuotaEntry, accessToken?: string, completeSnapshot?: boolean, identity?: string): void;
109
- /**
110
- * Mark accountId ('main' or a fallback id) rate-limited until resetAtMs.
111
- * Keeps the LATER reset: skips only when an existing mark is strictly
112
- * later than resetAtMs, so an equal-reset re-mark still applies (idempotent)
113
- * instead of being silently dropped by a stale `>=` comparison.
114
- */
115
- markRateLimited(accountId: string, resetAtMs: number): void;
116
- /** True iff accountId has a mark whose reset has not yet passed. */
117
- isRateLimited(accountId: string): boolean;
118
- /**
119
- * Raw stored reset for accountId's mark, or undefined if none. Unlike
120
- * isRateLimited, this does NOT apply read-time expiry — callers on the
121
- * block path already know the mark is live (isRateLimited just returned
122
- * true) and want its exact reset estimate for a Retry-After computation.
123
- */
124
- rateLimitedUntil(accountId: string): number | undefined;
125
- /** Explicit early clear — read-time expiry in isRateLimited is primary. */
126
- clearRateLimited(accountId: string): void;
127
- refreshMain(accessToken: string): Promise<OAuthQuotaSnapshot>;
128
- refreshFallback(accountId: string, accessToken: string): Promise<OAuthQuotaSnapshot>;
129
- refreshAllFallbacks(accounts: OAuthAccount[]): Promise<void>;
130
- /**
131
- * Fire-and-forget refresh. Does not await, swallows errors.
132
- */
133
- refreshMainInBackground(accessToken: string): void;
134
- isMainStale(): boolean;
135
- isFallbackStale(accountId: string, accessToken?: string): boolean;
136
- shouldRefreshOnRequestCount(requestCount: number): boolean;
137
- /**
138
- * Combined check: should a refresh happen right now?
139
- * True if main is stale by time OR triggered by request count.
140
- */
141
- needsRefresh(requestCount: number): boolean;
142
- updateStorage(storage: AccountStorage | null): void;
143
- /**
144
- * Seed/update the main quota cache from persisted state. This is deliberately
145
- * callable after every disk load so another plugin process's fresh quota write
146
- * can stop this process from showing "checking…" or making a redundant quota
147
- * API call.
148
- */
149
- seedMainFromStorage(storage: AccountStorage | null, accessToken?: string): void;
150
- private seedMainBackoffFromStorage;
151
- /**
152
- * Seed fallback cache entries from persisted account.quota data.
153
- * Updates older in-memory entries so a fresh quota write from another plugin
154
- * process prevents redundant checks and stale sidebar writes.
155
- */
156
- seedFallbacksFromAccounts(accounts: OAuthAccount[]): void;
157
- /**
158
- * Whether the MAIN quota API is currently in backoff. Scoped to the main
159
- * account — a fallback account's 429 never reports here.
160
- */
161
- isBackedOff(): boolean;
162
- /**
163
- * Whether a specific fallback account's quota API is in backoff.
164
- */
165
- isFallbackBackedOff(accountId: string, accessToken?: string): boolean;
166
- getLastApiError(): AccountOperationError | undefined;
167
- /** Minimum gap between consecutive quota API calls (ms). */
168
- private static readonly API_CALL_GAP_MS;
169
- private static fallbackInflightKey;
170
- private static quotaLockName;
171
- /**
172
- * Serialize API calls through a shared gate so only one
173
- * quota API request runs at a time, with a minimum gap
174
- * between calls. Prevents concurrent and rapid-fire calls
175
- * from triggering rate limits.
176
- */
177
- private _enqueueApiFetch;
178
- private _fetchMain;
179
- private _fetchFallback;
180
- /** Route through injected fetchQuotaFn, or throw if unset. */
181
- private _fetchQuota;
182
- private static isAuthError;
183
- /** Main quota failure: arms main-only backoff and persists via onApiError. */
184
- private _handleMainFetchError;
185
- /**
186
- * Fallback quota failure: arms backoff for THIS account only. Never touches
187
- * main backoff state and never calls onApiError (which persists the main
188
- * quota error) — the per-account error is recorded by the caller via the
189
- * account's lastQuotaRefreshError.
190
- */
191
- private _handleFallbackFetchError;
192
- }
@@ -1,86 +0,0 @@
1
- import { type SidebarState } from '../sidebar-state';
2
- import type { FallbackAccountManager, isOAuthAccount, loadAccounts } from './accounts';
3
- import type { whamUsageFn } from './provider';
4
- import type { QuotaManager } from './quota-manager';
5
- declare const log: {
6
- error: (m: string, d?: unknown) => void;
7
- warn: (m: string, d?: unknown) => void;
8
- info: (m: string, d?: unknown) => void;
9
- debug: (m: string, d?: unknown) => void;
10
- trace: (m: string, d?: unknown) => void;
11
- };
12
- type QuotaLogger = Pick<typeof log, 'debug' | 'warn'>;
13
- export interface RefreshAllQuotaDeps {
14
- getAuth: () => Promise<{
15
- type: string;
16
- access?: string;
17
- refresh?: string;
18
- expires?: number;
19
- }>;
20
- codexRefreshFn: (input: {
21
- refreshToken: string;
22
- fetchImpl: typeof fetch;
23
- now: () => number;
24
- }) => Promise<{
25
- access: string;
26
- refresh: string;
27
- expires: number;
28
- }>;
29
- refreshMainWithLease: () => Promise<{
30
- access: string;
31
- refresh: string;
32
- expires: number;
33
- }>;
34
- fallbackManager: FallbackAccountManager;
35
- quotaManager: QuotaManager;
36
- loadAccounts: typeof loadAccounts;
37
- writeSidebarState: (qm: QuotaManager, store: Awaited<ReturnType<typeof loadAccounts>>) => Promise<void>;
38
- client: {
39
- auth: {
40
- set: (input: {
41
- path: {
42
- id: string;
43
- };
44
- body: {
45
- type: string;
46
- access?: string;
47
- refresh: string;
48
- expires?: number;
49
- };
50
- }) => Promise<unknown>;
51
- };
52
- };
53
- fetchImpl: typeof fetch;
54
- now: () => number;
55
- configPath: string;
56
- storageMainAccountId: string | undefined;
57
- isOAuthAccountFn: typeof isOAuthAccount;
58
- whamFn?: typeof whamUsageFn;
59
- respectBackoff?: boolean;
60
- logger?: QuotaLogger;
61
- skipFresherThanMs?: number;
62
- readSidebarState?: () => Promise<SidebarState>;
63
- }
64
- export interface RefreshAllQuotaOptions {
65
- accountKey?: string;
66
- }
67
- export interface RefreshAllQuotaOptions {
68
- accountKey?: string;
69
- }
70
- export interface RefreshAllQuotaResult {
71
- account: string;
72
- ok: boolean;
73
- error?: string;
74
- /**
75
- * The account cannot recover on its own: the provider has rejected its
76
- * credentials, so no amount of retrying will help until the operator re-adds
77
- * it.
78
- *
79
- * Carried as a flag rather than left for the caller to infer from `error`,
80
- * because sniffing an error string for a user-facing decision breaks the
81
- * moment the wording changes.
82
- */
83
- permanent?: boolean;
84
- }
85
- export declare function refreshAllQuota(deps: RefreshAllQuotaDeps, options?: RefreshAllQuotaOptions): Promise<RefreshAllQuotaResult[]>;
86
- export {};
@@ -1,12 +0,0 @@
1
- export declare function isLostMarkerRaceError(error: unknown): boolean;
2
- export declare function acquireRefreshFileLock(options: {
3
- name: string;
4
- ttlMs: number;
5
- path?: string;
6
- now?: () => number;
7
- renew?: boolean;
8
- renewIntervalMs?: number;
9
- onStep?: (step: 'stale-marker-stat' | 'stale-marker-claimed' | 'stale-lock-confirmed' | 'eviction-marker-acquired' | 'renewal-owner-confirmed' | 'renewal-marker-unavailable' | 'renewal-write-fenced' | 'renewal-write-ready' | 'relinquish-read' | 'renewal-finished' | 'release-owner-confirmed') => void | Promise<void>;
10
- }): Promise<{
11
- release: () => Promise<void>;
12
- } | null>;