oc-chatgpt-multi-auth 5.2.1 → 5.2.3
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 +21 -16
- package/config/README.md +10 -8
- package/config/opencode-legacy.json +47 -73
- package/config/opencode-modern.json +32 -38
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +163 -125
- package/dist/index.js.map +1 -1
- package/dist/lib/accounts.d.ts.map +1 -1
- package/dist/lib/accounts.js +5 -18
- package/dist/lib/accounts.js.map +1 -1
- package/dist/lib/config.d.ts +1 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +5 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/logger.d.ts +1 -0
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/logger.js +25 -2
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/prompts/codex-opencode-bridge.d.ts +4 -3
- package/dist/lib/prompts/codex-opencode-bridge.d.ts.map +1 -1
- package/dist/lib/prompts/codex-opencode-bridge.js +73 -106
- package/dist/lib/prompts/codex-opencode-bridge.js.map +1 -1
- package/dist/lib/prompts/codex.d.ts +4 -4
- package/dist/lib/prompts/codex.d.ts.map +1 -1
- package/dist/lib/prompts/codex.js +27 -30
- package/dist/lib/prompts/codex.js.map +1 -1
- package/dist/lib/recovery.d.ts.map +1 -1
- package/dist/lib/recovery.js +10 -5
- package/dist/lib/recovery.js.map +1 -1
- package/dist/lib/request/fetch-helpers.d.ts +2 -1
- package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
- package/dist/lib/request/fetch-helpers.js +57 -6
- package/dist/lib/request/fetch-helpers.js.map +1 -1
- package/dist/lib/request/helpers/model-map.d.ts.map +1 -1
- package/dist/lib/request/helpers/model-map.js +35 -25
- package/dist/lib/request/helpers/model-map.js.map +1 -1
- package/dist/lib/request/request-transformer.d.ts +3 -3
- package/dist/lib/request/request-transformer.d.ts.map +1 -1
- package/dist/lib/request/request-transformer.js +73 -35
- package/dist/lib/request/request-transformer.js.map +1 -1
- package/dist/lib/request/response-handler.d.ts.map +1 -1
- package/dist/lib/request/response-handler.js +101 -10
- package/dist/lib/request/response-handler.js.map +1 -1
- package/dist/lib/schemas.d.ts +7 -9
- package/dist/lib/schemas.d.ts.map +1 -1
- package/dist/lib/schemas.js +1 -0
- package/dist/lib/schemas.js.map +1 -1
- package/dist/lib/storage/migrations.d.ts.map +1 -1
- package/dist/lib/storage/migrations.js +1 -9
- package/dist/lib/storage/migrations.js.map +1 -1
- package/dist/lib/storage/paths.d.ts.map +1 -1
- package/dist/lib/storage/paths.js +14 -2
- package/dist/lib/storage/paths.js.map +1 -1
- package/dist/lib/storage.d.ts +1 -0
- package/dist/lib/storage.d.ts.map +1 -1
- package/dist/lib/storage.js +124 -84
- package/dist/lib/storage.js.map +1 -1
- package/package.json +26 -12
- package/scripts/audit-dev-allowlist.js +114 -0
- package/dist/lib/request/local-fast-path.d.ts +0 -15
- package/dist/lib/request/local-fast-path.d.ts.map +0 -1
- package/dist/lib/request/local-fast-path.js +0 -164
- package/dist/lib/request/local-fast-path.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -28,13 +28,13 @@ import { queuedRefresh } from "./lib/refresh-queue.js";
|
|
|
28
28
|
import { openBrowserUrl } from "./lib/auth/browser.js";
|
|
29
29
|
import { startLocalOAuthServer } from "./lib/auth/server.js";
|
|
30
30
|
import { promptAddAnotherAccount, promptLoginMode } from "./lib/cli.js";
|
|
31
|
-
import { getCodexMode, getFastSession, getFastSessionStrategy, getFastSessionMaxInputItems, getRateLimitToastDebounceMs, getRetryAllAccountsMaxRetries, getRetryAllAccountsMaxWaitMs, getRetryAllAccountsRateLimited, getFallbackToGpt52OnUnsupportedGpt53, getUnsupportedCodexPolicy, getUnsupportedCodexFallbackChain, getTokenRefreshSkewMs, getSessionRecovery, getAutoResume, getToastDurationMs, getPerProjectAccounts, getEmptyResponseMaxRetries, getEmptyResponseRetryDelayMs, getPidOffsetEnabled, getFetchTimeoutMs, getStreamStallTimeoutMs, getCodexTuiV2, getCodexTuiColorProfile, getCodexTuiGlyphMode, loadPluginConfig, } from "./lib/config.js";
|
|
31
|
+
import { getCodexMode, getRequestTransformMode, getFastSession, getFastSessionStrategy, getFastSessionMaxInputItems, getRateLimitToastDebounceMs, getRetryAllAccountsMaxRetries, getRetryAllAccountsMaxWaitMs, getRetryAllAccountsRateLimited, getFallbackToGpt52OnUnsupportedGpt53, getUnsupportedCodexPolicy, getUnsupportedCodexFallbackChain, getTokenRefreshSkewMs, getSessionRecovery, getAutoResume, getToastDurationMs, getPerProjectAccounts, getEmptyResponseMaxRetries, getEmptyResponseRetryDelayMs, getPidOffsetEnabled, getFetchTimeoutMs, getStreamStallTimeoutMs, getCodexTuiV2, getCodexTuiColorProfile, getCodexTuiGlyphMode, loadPluginConfig, } from "./lib/config.js";
|
|
32
32
|
import { AUTH_LABELS, CODEX_BASE_URL, DUMMY_API_KEY, LOG_STAGES, PLUGIN_NAME, PROVIDER_ID, ACCOUNT_LIMITS, } from "./lib/constants.js";
|
|
33
33
|
import { initLogger, logRequest, logDebug, logInfo, logWarn, logError, setCorrelationId, clearCorrelationId, } from "./lib/logger.js";
|
|
34
34
|
import { checkAndNotify } from "./lib/auto-update-checker.js";
|
|
35
35
|
import { handleContextOverflow } from "./lib/context-overflow.js";
|
|
36
36
|
import { AccountManager, getAccountIdCandidates, extractAccountEmail, extractAccountId, formatAccountLabel, formatCooldown, formatWaitTime, sanitizeEmail, selectBestAccountCandidate, shouldUpdateAccountIdFromToken, resolveRequestAccountId, parseRateLimitReason, lookupCodexCliTokensByEmail, } from "./lib/accounts.js";
|
|
37
|
-
import { getStoragePath, loadAccounts, saveAccounts, clearAccounts, setStoragePath, exportAccounts, importAccounts, loadFlaggedAccounts, saveFlaggedAccounts, clearFlaggedAccounts, StorageError, formatStorageErrorHint, } from "./lib/storage.js";
|
|
37
|
+
import { getStoragePath, loadAccounts, saveAccounts, withAccountStorageTransaction, clearAccounts, setStoragePath, exportAccounts, importAccounts, loadFlaggedAccounts, saveFlaggedAccounts, clearFlaggedAccounts, StorageError, formatStorageErrorHint, } from "./lib/storage.js";
|
|
38
38
|
import { createCodexHeaders, extractRequestUrl, handleErrorResponse, handleSuccessResponse, getUnsupportedCodexModelInfo, resolveUnsupportedCodexFallbackModel, refreshAndUpdateToken, rewriteUrlForCodex, shouldRefreshToken, transformRequestForCodex, } from "./lib/request/fetch-helpers.js";
|
|
39
39
|
import { applyFastSessionDefaults } from "./lib/request/request-transformer.js";
|
|
40
40
|
import { getRateLimitBackoff, RATE_LIMIT_SHORT_RETRY_THRESHOLD_MS, resetRateLimitBackoff, } from "./lib/request/rate-limit-backoff.js";
|
|
@@ -124,7 +124,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
124
124
|
accountLabel: choice.label,
|
|
125
125
|
};
|
|
126
126
|
};
|
|
127
|
-
const buildManualOAuthFlow = (pkce, url, onSuccess) => ({
|
|
127
|
+
const buildManualOAuthFlow = (pkce, url, expectedState, onSuccess) => ({
|
|
128
128
|
url,
|
|
129
129
|
method: "code",
|
|
130
130
|
instructions: AUTH_LABELS.INSTRUCTIONS_MANUAL,
|
|
@@ -133,12 +133,29 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
133
133
|
if (!parsed.code) {
|
|
134
134
|
return "No authorization code found. Paste the full callback URL (e.g., http://localhost:1455/auth/callback?code=...)";
|
|
135
135
|
}
|
|
136
|
+
if (!parsed.state) {
|
|
137
|
+
return "Missing OAuth state. Paste the full callback URL including both code and state parameters.";
|
|
138
|
+
}
|
|
139
|
+
if (parsed.state !== expectedState) {
|
|
140
|
+
return "OAuth state mismatch. Restart login and paste the callback URL generated for this login attempt.";
|
|
141
|
+
}
|
|
136
142
|
return undefined;
|
|
137
143
|
},
|
|
138
144
|
callback: async (input) => {
|
|
139
145
|
const parsed = parseAuthorizationInput(input);
|
|
140
|
-
if (!parsed.code) {
|
|
141
|
-
return {
|
|
146
|
+
if (!parsed.code || !parsed.state) {
|
|
147
|
+
return {
|
|
148
|
+
type: "failed",
|
|
149
|
+
reason: "invalid_response",
|
|
150
|
+
message: "Missing authorization code or OAuth state",
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (parsed.state !== expectedState) {
|
|
154
|
+
return {
|
|
155
|
+
type: "failed",
|
|
156
|
+
reason: "invalid_response",
|
|
157
|
+
message: "OAuth state mismatch. Restart login and try again.",
|
|
158
|
+
};
|
|
142
159
|
}
|
|
143
160
|
const tokens = await exchangeAuthorizationCode(parsed.code, pkce.verifier, REDIRECT_URI);
|
|
144
161
|
if (tokens?.type === "success") {
|
|
@@ -182,121 +199,123 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
182
199
|
const persistAccountPool = async (results, replaceAll = false) => {
|
|
183
200
|
if (results.length === 0)
|
|
184
201
|
return;
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
202
|
+
await withAccountStorageTransaction(async (loadedStorage, persist) => {
|
|
203
|
+
const now = Date.now();
|
|
204
|
+
const stored = replaceAll ? null : loadedStorage;
|
|
205
|
+
const accounts = stored?.accounts ? [...stored.accounts] : [];
|
|
206
|
+
const indexByRefreshToken = new Map();
|
|
207
|
+
const indexByAccountId = new Map();
|
|
208
|
+
const indexByEmail = new Map();
|
|
209
|
+
for (let i = 0; i < accounts.length; i += 1) {
|
|
210
|
+
const account = accounts[i];
|
|
211
|
+
if (!account)
|
|
212
|
+
continue;
|
|
213
|
+
if (account.refreshToken) {
|
|
214
|
+
indexByRefreshToken.set(account.refreshToken, i);
|
|
215
|
+
}
|
|
216
|
+
if (account.accountId) {
|
|
217
|
+
indexByAccountId.set(account.accountId, i);
|
|
218
|
+
}
|
|
219
|
+
if (account.email) {
|
|
220
|
+
indexByEmail.set(account.email, i);
|
|
221
|
+
}
|
|
203
222
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
223
|
+
for (const result of results) {
|
|
224
|
+
const accountId = result.accountIdOverride ?? extractAccountId(result.access);
|
|
225
|
+
const accountIdSource = accountId
|
|
226
|
+
? result.accountIdSource ??
|
|
227
|
+
(result.accountIdOverride ? "manual" : "token")
|
|
228
|
+
: undefined;
|
|
229
|
+
const accountLabel = result.accountLabel;
|
|
230
|
+
const accountEmail = sanitizeEmail(extractAccountEmail(result.access, result.idToken));
|
|
231
|
+
const existingByEmail = accountEmail && indexByEmail.has(accountEmail)
|
|
232
|
+
? indexByEmail.get(accountEmail)
|
|
233
|
+
: undefined;
|
|
234
|
+
const existingById = accountId && indexByAccountId.has(accountId)
|
|
235
|
+
? indexByAccountId.get(accountId)
|
|
236
|
+
: undefined;
|
|
237
|
+
const existingByToken = indexByRefreshToken.get(result.refresh);
|
|
238
|
+
const existingIndex = existingById ?? existingByEmail ?? existingByToken;
|
|
239
|
+
if (existingIndex === undefined) {
|
|
240
|
+
const newIndex = accounts.length;
|
|
241
|
+
accounts.push({
|
|
242
|
+
accountId,
|
|
243
|
+
accountIdSource,
|
|
244
|
+
accountLabel,
|
|
245
|
+
email: accountEmail,
|
|
246
|
+
refreshToken: result.refresh,
|
|
247
|
+
accessToken: result.access,
|
|
248
|
+
expiresAt: result.expires,
|
|
249
|
+
addedAt: now,
|
|
250
|
+
lastUsed: now,
|
|
251
|
+
});
|
|
252
|
+
indexByRefreshToken.set(result.refresh, newIndex);
|
|
253
|
+
if (accountId) {
|
|
254
|
+
indexByAccountId.set(accountId, newIndex);
|
|
255
|
+
}
|
|
256
|
+
if (accountEmail) {
|
|
257
|
+
indexByEmail.set(accountEmail, newIndex);
|
|
258
|
+
}
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
const existing = accounts[existingIndex];
|
|
262
|
+
if (!existing)
|
|
263
|
+
continue;
|
|
264
|
+
const oldToken = existing.refreshToken;
|
|
265
|
+
const oldEmail = existing.email;
|
|
266
|
+
const nextEmail = accountEmail ?? existing.email;
|
|
267
|
+
const nextAccountId = accountId ?? existing.accountId;
|
|
268
|
+
const nextAccountIdSource = accountId ? accountIdSource ?? existing.accountIdSource : existing.accountIdSource;
|
|
269
|
+
const nextAccountLabel = accountLabel ?? existing.accountLabel;
|
|
270
|
+
accounts[existingIndex] = {
|
|
271
|
+
...existing,
|
|
272
|
+
accountId: nextAccountId,
|
|
273
|
+
accountIdSource: nextAccountIdSource,
|
|
274
|
+
accountLabel: nextAccountLabel,
|
|
275
|
+
email: nextEmail,
|
|
228
276
|
refreshToken: result.refresh,
|
|
229
277
|
accessToken: result.access,
|
|
230
278
|
expiresAt: result.expires,
|
|
231
|
-
addedAt: now,
|
|
232
279
|
lastUsed: now,
|
|
233
|
-
}
|
|
234
|
-
|
|
280
|
+
};
|
|
281
|
+
if (oldToken !== result.refresh) {
|
|
282
|
+
indexByRefreshToken.delete(oldToken);
|
|
283
|
+
indexByRefreshToken.set(result.refresh, existingIndex);
|
|
284
|
+
}
|
|
235
285
|
if (accountId) {
|
|
236
|
-
indexByAccountId.set(accountId,
|
|
286
|
+
indexByAccountId.set(accountId, existingIndex);
|
|
237
287
|
}
|
|
238
|
-
if (
|
|
239
|
-
indexByEmail.
|
|
288
|
+
if (oldEmail && oldEmail !== nextEmail) {
|
|
289
|
+
indexByEmail.delete(oldEmail);
|
|
290
|
+
}
|
|
291
|
+
if (nextEmail) {
|
|
292
|
+
indexByEmail.set(nextEmail, existingIndex);
|
|
240
293
|
}
|
|
241
|
-
continue;
|
|
242
|
-
}
|
|
243
|
-
const existing = accounts[existingIndex];
|
|
244
|
-
if (!existing)
|
|
245
|
-
continue;
|
|
246
|
-
const oldToken = existing.refreshToken;
|
|
247
|
-
const oldEmail = existing.email;
|
|
248
|
-
const nextEmail = accountEmail ?? existing.email;
|
|
249
|
-
const nextAccountId = accountId ?? existing.accountId;
|
|
250
|
-
const nextAccountIdSource = accountId ? accountIdSource ?? existing.accountIdSource : existing.accountIdSource;
|
|
251
|
-
const nextAccountLabel = accountLabel ?? existing.accountLabel;
|
|
252
|
-
accounts[existingIndex] = {
|
|
253
|
-
...existing,
|
|
254
|
-
accountId: nextAccountId,
|
|
255
|
-
accountIdSource: nextAccountIdSource,
|
|
256
|
-
accountLabel: nextAccountLabel,
|
|
257
|
-
email: nextEmail,
|
|
258
|
-
refreshToken: result.refresh,
|
|
259
|
-
accessToken: result.access,
|
|
260
|
-
expiresAt: result.expires,
|
|
261
|
-
lastUsed: now,
|
|
262
|
-
};
|
|
263
|
-
if (oldToken !== result.refresh) {
|
|
264
|
-
indexByRefreshToken.delete(oldToken);
|
|
265
|
-
indexByRefreshToken.set(result.refresh, existingIndex);
|
|
266
|
-
}
|
|
267
|
-
if (accountId) {
|
|
268
|
-
indexByAccountId.set(accountId, existingIndex);
|
|
269
|
-
}
|
|
270
|
-
if (oldEmail && oldEmail !== nextEmail) {
|
|
271
|
-
indexByEmail.delete(oldEmail);
|
|
272
|
-
}
|
|
273
|
-
if (nextEmail) {
|
|
274
|
-
indexByEmail.set(nextEmail, existingIndex);
|
|
275
294
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
const activeIndex = replaceAll
|
|
280
|
-
? 0
|
|
281
|
-
: typeof stored?.activeIndex === "number" && Number.isFinite(stored.activeIndex)
|
|
282
|
-
? stored.activeIndex
|
|
283
|
-
: 0;
|
|
284
|
-
const clampedActiveIndex = Math.max(0, Math.min(activeIndex, accounts.length - 1));
|
|
285
|
-
const activeIndexByFamily = {};
|
|
286
|
-
for (const family of MODEL_FAMILIES) {
|
|
287
|
-
const storedFamilyIndex = stored?.activeIndexByFamily?.[family];
|
|
288
|
-
const rawFamilyIndex = replaceAll
|
|
295
|
+
if (accounts.length === 0)
|
|
296
|
+
return;
|
|
297
|
+
const activeIndex = replaceAll
|
|
289
298
|
? 0
|
|
290
|
-
: typeof
|
|
291
|
-
?
|
|
292
|
-
:
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
299
|
+
: typeof stored?.activeIndex === "number" && Number.isFinite(stored.activeIndex)
|
|
300
|
+
? stored.activeIndex
|
|
301
|
+
: 0;
|
|
302
|
+
const clampedActiveIndex = Math.max(0, Math.min(activeIndex, accounts.length - 1));
|
|
303
|
+
const activeIndexByFamily = {};
|
|
304
|
+
for (const family of MODEL_FAMILIES) {
|
|
305
|
+
const storedFamilyIndex = stored?.activeIndexByFamily?.[family];
|
|
306
|
+
const rawFamilyIndex = replaceAll
|
|
307
|
+
? 0
|
|
308
|
+
: typeof storedFamilyIndex === "number" && Number.isFinite(storedFamilyIndex)
|
|
309
|
+
? storedFamilyIndex
|
|
310
|
+
: clampedActiveIndex;
|
|
311
|
+
activeIndexByFamily[family] = Math.max(0, Math.min(Math.floor(rawFamilyIndex), accounts.length - 1));
|
|
312
|
+
}
|
|
313
|
+
await persist({
|
|
314
|
+
version: 3,
|
|
315
|
+
accounts,
|
|
316
|
+
activeIndex: clampedActiveIndex,
|
|
317
|
+
activeIndexByFamily,
|
|
318
|
+
});
|
|
300
319
|
});
|
|
301
320
|
};
|
|
302
321
|
const showToast = async (message, variant = "success", options) => {
|
|
@@ -549,6 +568,8 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
549
568
|
// Load plugin configuration and determine CODEX_MODE
|
|
550
569
|
// Priority: CODEX_MODE env var > config file > default (true)
|
|
551
570
|
const codexMode = getCodexMode(pluginConfig);
|
|
571
|
+
const requestTransformMode = getRequestTransformMode(pluginConfig);
|
|
572
|
+
const useLegacyRequestTransform = requestTransformMode === "legacy";
|
|
552
573
|
const fastSessionEnabled = getFastSession(pluginConfig);
|
|
553
574
|
const fastSessionStrategy = getFastSessionStrategy(pluginConfig);
|
|
554
575
|
const fastSessionMaxInputItems = getFastSessionMaxInputItems(pluginConfig);
|
|
@@ -584,7 +605,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
584
605
|
const prewarmEnabled = process.env.CODEX_AUTH_PREWARM !== "0" &&
|
|
585
606
|
process.env.VITEST !== "true" &&
|
|
586
607
|
process.env.NODE_ENV !== "test";
|
|
587
|
-
if (!startupPrewarmTriggered && prewarmEnabled) {
|
|
608
|
+
if (!startupPrewarmTriggered && prewarmEnabled && useLegacyRequestTransform) {
|
|
588
609
|
startupPrewarmTriggered = true;
|
|
589
610
|
const configuredModels = Object.keys(userConfig.models ?? {});
|
|
590
611
|
prewarmCodexInstructions(configuredModels);
|
|
@@ -688,6 +709,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
688
709
|
fastSession: fastSessionEnabled,
|
|
689
710
|
fastSessionStrategy,
|
|
690
711
|
fastSessionMaxInputItems,
|
|
712
|
+
requestTransformMode,
|
|
691
713
|
});
|
|
692
714
|
let requestInit = transformation?.updatedInit ?? baseInit;
|
|
693
715
|
let transformedBody = transformation?.body;
|
|
@@ -748,6 +770,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
748
770
|
while (true) {
|
|
749
771
|
const accountCount = accountManager.getAccountCount();
|
|
750
772
|
const attempted = new Set();
|
|
773
|
+
let restartAccountTraversalWithFallback = false;
|
|
751
774
|
while (attempted.size < Math.max(1, accountCount)) {
|
|
752
775
|
const account = accountManager.getCurrentOrNextForFamilyHybrid(modelFamily, model, { pidOffsetEnabled });
|
|
753
776
|
if (!account || attempted.has(account.index)) {
|
|
@@ -803,12 +826,20 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
803
826
|
await showToast(`Using ${accountLabel} (${account.index + 1}/${accountCount})`, "info");
|
|
804
827
|
accountManager.markToastShown(account.index);
|
|
805
828
|
}
|
|
806
|
-
|
|
829
|
+
const headers = createCodexHeaders(requestInit, accountId, accountAuth.access, {
|
|
807
830
|
model,
|
|
808
831
|
promptCacheKey,
|
|
809
832
|
});
|
|
810
833
|
// Consume a token before making the request for proactive rate limiting
|
|
811
|
-
accountManager.consumeToken(account, modelFamily, model);
|
|
834
|
+
const tokenConsumed = accountManager.consumeToken(account, modelFamily, model);
|
|
835
|
+
if (!tokenConsumed) {
|
|
836
|
+
accountManager.recordRateLimit(account, modelFamily, model);
|
|
837
|
+
runtimeMetrics.accountRotations++;
|
|
838
|
+
runtimeMetrics.lastError =
|
|
839
|
+
`Local token bucket depleted for account ${account.index + 1} (${modelFamily}${model ? `:${model}` : ""})`;
|
|
840
|
+
logWarn(`Skipping account ${account.index + 1}: local token bucket depleted for ${modelFamily}${model ? `:${model}` : ""}`);
|
|
841
|
+
break;
|
|
842
|
+
}
|
|
812
843
|
while (true) {
|
|
813
844
|
let response;
|
|
814
845
|
const fetchStart = performance.now();
|
|
@@ -896,7 +927,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
896
927
|
customChain: unsupportedCodexFallbackChain,
|
|
897
928
|
});
|
|
898
929
|
if (fallbackModel) {
|
|
899
|
-
const previousModel = model ?? "gpt-5
|
|
930
|
+
const previousModel = model ?? "gpt-5-codex";
|
|
900
931
|
const previousModelFamily = modelFamily;
|
|
901
932
|
attemptedUnsupportedFallbackModels.add(previousModel);
|
|
902
933
|
attemptedUnsupportedFallbackModels.add(fallbackModel);
|
|
@@ -924,11 +955,6 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
924
955
|
...(requestInit ?? {}),
|
|
925
956
|
body: JSON.stringify(transformedBody),
|
|
926
957
|
};
|
|
927
|
-
headers = createCodexHeaders(requestInit, accountId, accountAuth.access, {
|
|
928
|
-
model,
|
|
929
|
-
promptCacheKey,
|
|
930
|
-
});
|
|
931
|
-
accountManager.consumeToken(account, modelFamily, model);
|
|
932
958
|
runtimeMetrics.lastError = `Model fallback: ${previousModel} -> ${model}`;
|
|
933
959
|
logWarn(`Model ${previousModel} is unsupported for this ChatGPT account. Falling back to ${model}.`, {
|
|
934
960
|
unsupportedCodexPolicy,
|
|
@@ -938,7 +964,8 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
938
964
|
fallbackReason: "unsupported-model-entitlement",
|
|
939
965
|
});
|
|
940
966
|
await showToast(`Model ${previousModel} is not available for this account. Retrying with ${model}.`, "warning", { duration: toastDurationMs });
|
|
941
|
-
|
|
967
|
+
restartAccountTraversalWithFallback = true;
|
|
968
|
+
break;
|
|
942
969
|
}
|
|
943
970
|
if (unsupportedModelInfo.isUnsupported && !fallbackOnUnsupportedCodexModel) {
|
|
944
971
|
const blockedModel = unsupportedModelInfo.unsupportedModel ?? model ?? "requested model";
|
|
@@ -1003,6 +1030,11 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1003
1030
|
const successResponse = await handleSuccessResponse(response, isStreaming, {
|
|
1004
1031
|
streamStallTimeoutMs,
|
|
1005
1032
|
});
|
|
1033
|
+
if (!successResponse.ok) {
|
|
1034
|
+
runtimeMetrics.failedRequests++;
|
|
1035
|
+
runtimeMetrics.lastError = `HTTP ${successResponse.status}`;
|
|
1036
|
+
return successResponse;
|
|
1037
|
+
}
|
|
1006
1038
|
if (!isStreaming && emptyResponseMaxRetries > 0) {
|
|
1007
1039
|
const clonedResponse = successResponse.clone();
|
|
1008
1040
|
try {
|
|
@@ -1031,6 +1063,12 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1031
1063
|
runtimeMetrics.lastError = null;
|
|
1032
1064
|
return successResponse;
|
|
1033
1065
|
}
|
|
1066
|
+
if (restartAccountTraversalWithFallback) {
|
|
1067
|
+
break;
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
if (restartAccountTraversalWithFallback) {
|
|
1071
|
+
continue;
|
|
1034
1072
|
}
|
|
1035
1073
|
const waitMs = accountManager.getMinWaitTimeForFamily(modelFamily, model);
|
|
1036
1074
|
const count = accountManager.getAccountCount();
|
|
@@ -1245,7 +1283,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1245
1283
|
return parts.join(", ");
|
|
1246
1284
|
};
|
|
1247
1285
|
const fetchCodexQuotaSnapshot = async (params) => {
|
|
1248
|
-
const QUOTA_PROBE_MODELS = ["gpt-5
|
|
1286
|
+
const QUOTA_PROBE_MODELS = ["gpt-5-codex", "gpt-5.3-codex", "gpt-5.2-codex"];
|
|
1249
1287
|
let lastError = null;
|
|
1250
1288
|
for (const model of QUOTA_PROBE_MODELS) {
|
|
1251
1289
|
try {
|
|
@@ -1753,8 +1791,8 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1753
1791
|
targetCount = 1;
|
|
1754
1792
|
}
|
|
1755
1793
|
if (useManualMode) {
|
|
1756
|
-
const { pkce, url } = await createAuthorizationFlow();
|
|
1757
|
-
return buildManualOAuthFlow(pkce, url, async (tokens) => {
|
|
1794
|
+
const { pkce, state, url } = await createAuthorizationFlow();
|
|
1795
|
+
return buildManualOAuthFlow(pkce, url, state, async (tokens) => {
|
|
1758
1796
|
try {
|
|
1759
1797
|
await persistAccountPool([tokens], startFresh);
|
|
1760
1798
|
invalidateAccountManagerCache();
|
|
@@ -1880,8 +1918,8 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1880
1918
|
applyUiRuntimeFromConfig(manualPluginConfig);
|
|
1881
1919
|
const manualPerProjectAccounts = getPerProjectAccounts(manualPluginConfig);
|
|
1882
1920
|
setStoragePath(manualPerProjectAccounts ? process.cwd() : null);
|
|
1883
|
-
const { pkce, url } = await createAuthorizationFlow();
|
|
1884
|
-
return buildManualOAuthFlow(pkce, url, async (tokens) => {
|
|
1921
|
+
const { pkce, state, url } = await createAuthorizationFlow();
|
|
1922
|
+
return buildManualOAuthFlow(pkce, url, state, async (tokens) => {
|
|
1885
1923
|
try {
|
|
1886
1924
|
await persistAccountPool([tokens], false);
|
|
1887
1925
|
}
|