oc-codex-multi-auth 6.1.2 → 6.1.6
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 +27 -23
- package/config/README.md +35 -35
- package/config/opencode-legacy.json +80 -30
- package/config/opencode-modern.json +14 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +157 -11
- package/dist/index.js.map +1 -1
- package/dist/lib/accounts/persistence.d.ts.map +1 -1
- package/dist/lib/accounts/persistence.js +1 -0
- package/dist/lib/accounts/persistence.js.map +1 -1
- package/dist/lib/accounts/state.d.ts +2 -2
- package/dist/lib/accounts/state.d.ts.map +1 -1
- package/dist/lib/accounts/state.js +87 -19
- package/dist/lib/accounts/state.js.map +1 -1
- package/dist/lib/auth/auth.d.ts +2 -2
- package/dist/lib/auth/auth.d.ts.map +1 -1
- package/dist/lib/auth/auth.js +9 -6
- package/dist/lib/auth/auth.js.map +1 -1
- package/dist/lib/auth/login-runner.d.ts +1 -0
- package/dist/lib/auth/login-runner.d.ts.map +1 -1
- package/dist/lib/auth/login-runner.js +3 -0
- package/dist/lib/auth/login-runner.js.map +1 -1
- package/dist/lib/auth/scopes.d.ts +5 -0
- package/dist/lib/auth/scopes.d.ts.map +1 -0
- package/dist/lib/auth/scopes.js +23 -0
- package/dist/lib/auth/scopes.js.map +1 -0
- package/dist/lib/auth/server.d.ts +6 -1
- package/dist/lib/auth/server.d.ts.map +1 -1
- package/dist/lib/auth/server.js +95 -51
- package/dist/lib/auth/server.js.map +1 -1
- package/dist/lib/codex-usage.d.ts +91 -0
- package/dist/lib/codex-usage.d.ts.map +1 -0
- package/dist/lib/codex-usage.js +368 -0
- package/dist/lib/codex-usage.js.map +1 -0
- package/dist/lib/oauth-constants.d.ts +5 -5
- package/dist/lib/oauth-constants.d.ts.map +1 -1
- package/dist/lib/oauth-constants.js +5 -5
- package/dist/lib/oauth-constants.js.map +1 -1
- package/dist/lib/prompts/codex.d.ts +2 -0
- package/dist/lib/prompts/codex.d.ts.map +1 -1
- package/dist/lib/prompts/codex.js +43 -11
- package/dist/lib/prompts/codex.js.map +1 -1
- package/dist/lib/request/fetch-helpers.d.ts +2 -2
- package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
- package/dist/lib/request/fetch-helpers.js +37 -25
- package/dist/lib/request/fetch-helpers.js.map +1 -1
- package/dist/lib/request/helpers/model-map.d.ts +1 -0
- package/dist/lib/request/helpers/model-map.d.ts.map +1 -1
- package/dist/lib/request/helpers/model-map.js +28 -25
- package/dist/lib/request/helpers/model-map.js.map +1 -1
- package/dist/lib/request/request-transformer.d.ts +1 -0
- package/dist/lib/request/request-transformer.d.ts.map +1 -1
- package/dist/lib/request/request-transformer.js +46 -22
- package/dist/lib/request/request-transformer.js.map +1 -1
- package/dist/lib/schemas.d.ts +8 -0
- package/dist/lib/schemas.d.ts.map +1 -1
- package/dist/lib/schemas.js +3 -0
- package/dist/lib/schemas.js.map +1 -1
- package/dist/lib/storage/migrations.d.ts +4 -0
- package/dist/lib/storage/migrations.d.ts.map +1 -1
- package/dist/lib/storage/migrations.js +1 -0
- package/dist/lib/storage/migrations.js.map +1 -1
- package/dist/lib/tools/codex-limits.d.ts.map +1 -1
- package/dist/lib/tools/codex-limits.js +39 -340
- package/dist/lib/tools/codex-limits.js.map +1 -1
- package/dist/lib/tools/codex-switch.d.ts.map +1 -1
- package/dist/lib/tools/codex-switch.js +9 -0
- package/dist/lib/tools/codex-switch.js.map +1 -1
- package/dist/lib/tui-quota-cache.d.ts +33 -0
- package/dist/lib/tui-quota-cache.d.ts.map +1 -0
- package/dist/lib/tui-quota-cache.js +247 -0
- package/dist/lib/tui-quota-cache.js.map +1 -0
- package/dist/lib/tui-status.d.ts +55 -0
- package/dist/lib/tui-status.d.ts.map +1 -0
- package/dist/lib/tui-status.js +343 -0
- package/dist/lib/tui-status.js.map +1 -0
- package/dist/lib/types.d.ts +3 -1
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/tui.d.ts +6 -0
- package/dist/tui.d.ts.map +1 -0
- package/dist/tui.js +363 -0
- package/dist/tui.js.map +1 -0
- package/package.json +16 -2
- package/scripts/install-oc-codex-multi-auth-core.js +119 -18
package/dist/index.js
CHANGED
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
* @repository https://github.com/ndycode/oc-codex-multi-auth
|
|
23
23
|
|
|
24
24
|
*/
|
|
25
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
26
|
+
import { homedir } from "node:os";
|
|
27
|
+
import { join } from "node:path";
|
|
25
28
|
import { createAuthorizationFlow, exchangeAuthorizationCode, parseAuthorizationInput, REDIRECT_URI, } from "./lib/auth/auth.js";
|
|
26
29
|
import { buildDeviceCodeInstructions, completeDeviceCodeSession, createDeviceCodeSession, } from "./lib/auth/device-code.js";
|
|
27
30
|
import { applyAccountSelectionFallbacks, persistResolvedAccountSelection, persistAccountPool, resolveAndPersistAccountSelection, resolveAccountSelection, } from "./lib/auth/login-runner.js";
|
|
@@ -38,7 +41,7 @@ import { AccountManager, extractAccountEmail, extractAccountId, formatAccountLab
|
|
|
38
41
|
import { getStoragePath, loadAccounts, saveAccounts, clearAccounts, setStoragePath, loadFlaggedAccounts, saveFlaggedAccounts, withFlaggedAccountStorageTransaction, clearFlaggedAccounts, getWorkspaceIdentityKey, StorageError, formatStorageErrorHint, } from "./lib/storage.js";
|
|
39
42
|
import { createCodexHeaders, extractRequestUrl, handleErrorResponse, handleSuccessResponse, isDeactivatedWorkspaceError, getUnsupportedCodexModelInfo, resolveUnsupportedCodexFallbackModel, refreshAndUpdateToken, rewriteUrlForCodex, shouldRefreshToken, transformRequestForCodex, } from "./lib/request/fetch-helpers.js";
|
|
40
43
|
import { createDeactivatedWorkspaceError, DEACTIVATED_WORKSPACE_ERROR_CODE, isDeactivatedWorkspaceErrorMessage, } from "./lib/error-sentinels.js";
|
|
41
|
-
import { applyFastSessionDefaults } from "./lib/request/request-transformer.js";
|
|
44
|
+
import { applyFastSessionDefaults, upsertBackendModelIdentityMessage, } from "./lib/request/request-transformer.js";
|
|
42
45
|
import { getRateLimitBackoff, RATE_LIMIT_SHORT_RETRY_THRESHOLD_MS, resetRateLimitBackoff, } from "./lib/request/rate-limit-backoff.js";
|
|
43
46
|
import { isEmptyResponse } from "./lib/request/response-handler.js";
|
|
44
47
|
import { getCircuitBreaker } from "./lib/circuit-breaker.js";
|
|
@@ -52,6 +55,8 @@ import { prewarmOpenCodeCodexPrompt } from "./lib/prompts/opencode-codex.js";
|
|
|
52
55
|
import { createSessionRecoveryHook, isRecoverableError, detectErrorType, getRecoveryToastContent, } from "./lib/recovery.js";
|
|
53
56
|
import { matchesWorkspaceIdentity, upsertFlaggedAccountRecord, createRetryBudgetUsage, serializeSelectionExplainability, formatRoutingValue, formatExplainabilitySummary, } from "./lib/runtime.js";
|
|
54
57
|
import { createToolRegistry, } from "./lib/tools/index.js";
|
|
58
|
+
import { createUsageAccountFingerprint } from "./lib/codex-usage.js";
|
|
59
|
+
import { clearTuiQuotaSnapshot, parseTuiQuotaSnapshotFromHeaders, writeTuiQuotaSnapshot, } from "./lib/tui-quota-cache.js";
|
|
55
60
|
/**
|
|
56
61
|
* OpenAI Codex OAuth authentication plugin for opencode
|
|
57
62
|
*
|
|
@@ -68,7 +73,6 @@ import { createToolRegistry, } from "./lib/tools/index.js";
|
|
|
68
73
|
* }
|
|
69
74
|
* ```
|
|
70
75
|
*/
|
|
71
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
72
76
|
export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
73
77
|
initLogger(client);
|
|
74
78
|
let cachedAccountManager = null;
|
|
@@ -252,7 +256,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
252
256
|
openBrowserUrl(url);
|
|
253
257
|
if (!serverInfo || !serverInfo.ready) {
|
|
254
258
|
serverInfo?.close();
|
|
255
|
-
const message = `OAuth callback server failed to start on
|
|
259
|
+
const message = `OAuth callback server failed to start on localhost loopback port 1455. ` +
|
|
256
260
|
`Retry with "${AUTH_LABELS.OAUTH_DEVICE_CODE}" or "${AUTH_LABELS.OAUTH_MANUAL}".`;
|
|
257
261
|
logWarn(`\n[${PLUGIN_NAME}] ${message}\n`);
|
|
258
262
|
return {
|
|
@@ -288,6 +292,31 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
288
292
|
// Ignore when TUI is not available.
|
|
289
293
|
}
|
|
290
294
|
};
|
|
295
|
+
const clearPromptQuotaCache = async () => {
|
|
296
|
+
try {
|
|
297
|
+
await clearTuiQuotaSnapshot();
|
|
298
|
+
}
|
|
299
|
+
catch (error) {
|
|
300
|
+
logDebug(`[${PLUGIN_NAME}] Failed to clear TUI quota cache: ${error instanceof Error ? error.message : String(error)}`);
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
const recordPromptQuotaHeaders = async (response, account, accountCount) => {
|
|
304
|
+
try {
|
|
305
|
+
const snapshot = parseTuiQuotaSnapshotFromHeaders(response.headers, {
|
|
306
|
+
fingerprint: createUsageAccountFingerprint(account),
|
|
307
|
+
accountIndex: account.index + 1,
|
|
308
|
+
accountCount,
|
|
309
|
+
accountEmail: account.email?.trim() || undefined,
|
|
310
|
+
accountLabel: formatAccountLabel(account, account.index),
|
|
311
|
+
});
|
|
312
|
+
if (!snapshot)
|
|
313
|
+
return;
|
|
314
|
+
await writeTuiQuotaSnapshot(snapshot);
|
|
315
|
+
}
|
|
316
|
+
catch (error) {
|
|
317
|
+
logDebug(`[${PLUGIN_NAME}] Failed to record TUI quota headers: ${error instanceof Error ? error.message : String(error)}`);
|
|
318
|
+
}
|
|
319
|
+
};
|
|
291
320
|
const resolveActiveIndex = (storage, family = "codex") => {
|
|
292
321
|
const total = storage.accounts.length;
|
|
293
322
|
if (total === 0)
|
|
@@ -296,6 +325,67 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
296
325
|
const raw = Number.isFinite(rawCandidate) ? rawCandidate : 0;
|
|
297
326
|
return Math.max(0, Math.min(raw, total - 1));
|
|
298
327
|
};
|
|
328
|
+
const backfillHostOpenAIAuthFromPool = async () => {
|
|
329
|
+
const authPath = join(homedir(), ".local", "share", "opencode", "auth.json");
|
|
330
|
+
let authStore = {};
|
|
331
|
+
try {
|
|
332
|
+
const authRaw = await readFile(authPath, "utf8");
|
|
333
|
+
const parsed = JSON.parse(authRaw);
|
|
334
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
335
|
+
authStore = parsed;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
const errorCode = error?.code;
|
|
340
|
+
if (errorCode !== "ENOENT") {
|
|
341
|
+
logWarn(`[${PLUGIN_NAME}] Failed to read host auth store for OpenAI backfill: ${error?.message ?? String(error)}`);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
const existing = authStore[PROVIDER_ID];
|
|
346
|
+
const hasExistingOAuth = existing?.type === "oauth" &&
|
|
347
|
+
typeof existing.access === "string" &&
|
|
348
|
+
existing.access.trim().length > 0 &&
|
|
349
|
+
typeof existing.refresh === "string" &&
|
|
350
|
+
existing.refresh.trim().length > 0 &&
|
|
351
|
+
typeof existing.expires === "number" &&
|
|
352
|
+
Number.isFinite(existing.expires);
|
|
353
|
+
if (hasExistingOAuth) {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
const storage = await loadAccounts();
|
|
357
|
+
if (!storage || storage.accounts.length === 0) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
const hasUsableTokens = (account) => typeof account?.accessToken === "string" &&
|
|
361
|
+
account.accessToken.trim().length > 0 &&
|
|
362
|
+
typeof account?.refreshToken === "string" &&
|
|
363
|
+
account.refreshToken.trim().length > 0 &&
|
|
364
|
+
typeof account?.expiresAt === "number" &&
|
|
365
|
+
Number.isFinite(account.expiresAt);
|
|
366
|
+
const activeIndex = resolveActiveIndex(storage, "codex");
|
|
367
|
+
const activeAccount = storage.accounts[activeIndex];
|
|
368
|
+
const candidate = hasUsableTokens(activeAccount)
|
|
369
|
+
? activeAccount
|
|
370
|
+
: storage.accounts.find(hasUsableTokens);
|
|
371
|
+
if (!candidate || !hasUsableTokens(candidate)) {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
authStore[PROVIDER_ID] = {
|
|
375
|
+
type: "oauth",
|
|
376
|
+
access: candidate.accessToken,
|
|
377
|
+
refresh: candidate.refreshToken,
|
|
378
|
+
expires: candidate.expiresAt,
|
|
379
|
+
};
|
|
380
|
+
try {
|
|
381
|
+
await mkdir(join(homedir(), ".local", "share", "opencode"), { recursive: true });
|
|
382
|
+
await writeFile(authPath, `${JSON.stringify(authStore, null, 2)}\n`, "utf8");
|
|
383
|
+
logInfo(`[${PLUGIN_NAME}] Restored missing host OpenAI auth entry from stored account pool`);
|
|
384
|
+
}
|
|
385
|
+
catch (error) {
|
|
386
|
+
logWarn(`[${PLUGIN_NAME}] Failed to backfill host OpenAI auth entry: ${error?.message ?? String(error)}`);
|
|
387
|
+
}
|
|
388
|
+
};
|
|
299
389
|
const hydrateEmails = async (storage) => {
|
|
300
390
|
if (!storage)
|
|
301
391
|
return storage;
|
|
@@ -339,6 +429,10 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
339
429
|
account.expiresAt = refreshed.expires;
|
|
340
430
|
changed = true;
|
|
341
431
|
}
|
|
432
|
+
if (refreshed.scope && refreshed.scope !== account.oauthScope) {
|
|
433
|
+
account.oauthScope = refreshed.scope;
|
|
434
|
+
changed = true;
|
|
435
|
+
}
|
|
342
436
|
if (refreshed.refresh && refreshed.refresh !== account.refreshToken) {
|
|
343
437
|
account.refreshToken = refreshed.refresh;
|
|
344
438
|
changed = true;
|
|
@@ -777,6 +871,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
777
871
|
storage.activeIndexByFamily[family] = index;
|
|
778
872
|
}
|
|
779
873
|
await saveAccounts(storage);
|
|
874
|
+
await clearPromptQuotaCache();
|
|
780
875
|
// Reload manager from disk so we don't overwrite newer rotated
|
|
781
876
|
// refresh tokens with stale in-memory state.
|
|
782
877
|
if (cachedAccountManager) {
|
|
@@ -844,6 +939,10 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
844
939
|
invalidateAccountManagerCache,
|
|
845
940
|
upsertFlaggedAccountRecord,
|
|
846
941
|
};
|
|
942
|
+
const startupPluginConfig = loadPluginConfig();
|
|
943
|
+
const startupPerProjectAccounts = getPerProjectAccounts(startupPluginConfig);
|
|
944
|
+
setStoragePath(startupPerProjectAccounts ? process.cwd() : null);
|
|
945
|
+
await backfillHostOpenAIAuthFromPool();
|
|
847
946
|
return {
|
|
848
947
|
event: eventHandler,
|
|
849
948
|
auth: {
|
|
@@ -900,9 +999,17 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
900
999
|
if (needsPersist) {
|
|
901
1000
|
await accountManager.saveToDisk();
|
|
902
1001
|
}
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
1002
|
+
const accountCount = accountManager.getAccountCount();
|
|
1003
|
+
const storagePath = getStoragePath();
|
|
1004
|
+
logDebug(`[${PLUGIN_NAME}] Loader auth bootstrap`, {
|
|
1005
|
+
authType: auth.type,
|
|
1006
|
+
authHasRefresh: !!authFallback?.refresh,
|
|
1007
|
+
authHasMultiAccount: !!authWithMulti?.multiAccount,
|
|
1008
|
+
accountCount,
|
|
1009
|
+
storagePath,
|
|
1010
|
+
});
|
|
1011
|
+
if (accountCount === 0) {
|
|
1012
|
+
logWarn(`[${PLUGIN_NAME}] No Codex accounts available (run opencode auth login)`);
|
|
906
1013
|
}
|
|
907
1014
|
// Extract user configuration (global + per-model options)
|
|
908
1015
|
const providerConfig = provider;
|
|
@@ -1278,6 +1385,10 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1278
1385
|
}
|
|
1279
1386
|
account.email =
|
|
1280
1387
|
extractAccountEmail(accountAuth.access) ?? account.email;
|
|
1388
|
+
// Keep the persisted active identity in step with the account used
|
|
1389
|
+
// for this request so the TUI quota cache accepts the next real
|
|
1390
|
+
// response-header snapshot instead of filtering it as stale.
|
|
1391
|
+
accountManager.saveToDiskDebounced();
|
|
1281
1392
|
if (accountCount > 1 &&
|
|
1282
1393
|
accountManager.shouldShowAccountToast(account.index, rateLimitToastDebounceMs)) {
|
|
1283
1394
|
const accountLabel = formatAccountLabel(account, account.index);
|
|
@@ -1401,6 +1512,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1401
1512
|
latencyMs: fetchLatencyMs,
|
|
1402
1513
|
headers: Object.fromEntries(response.headers.entries()),
|
|
1403
1514
|
});
|
|
1515
|
+
void recordPromptQuotaHeaders(response, account, accountCount);
|
|
1404
1516
|
if (!response.ok) {
|
|
1405
1517
|
const contextOverflowResult = await handleContextOverflow(response, model);
|
|
1406
1518
|
if (contextOverflowResult.handled) {
|
|
@@ -1500,15 +1612,31 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1500
1612
|
fallbackFrom = previousModel;
|
|
1501
1613
|
fallbackTo = model;
|
|
1502
1614
|
fallbackReason = "fallback-unsupported-model-entitlement";
|
|
1615
|
+
const fallbackInstructions = await getCodexInstructions(model);
|
|
1503
1616
|
if (transformedBody && typeof transformedBody === "object") {
|
|
1504
|
-
transformedBody = {
|
|
1617
|
+
transformedBody = {
|
|
1618
|
+
...transformedBody,
|
|
1619
|
+
model,
|
|
1620
|
+
instructions: fallbackInstructions,
|
|
1621
|
+
input: upsertBackendModelIdentityMessage(transformedBody.input, model),
|
|
1622
|
+
};
|
|
1505
1623
|
}
|
|
1506
1624
|
else {
|
|
1507
|
-
let fallbackBody = {
|
|
1625
|
+
let fallbackBody = {
|
|
1626
|
+
model,
|
|
1627
|
+
instructions: fallbackInstructions,
|
|
1628
|
+
};
|
|
1508
1629
|
if (requestInit?.body && typeof requestInit.body === "string") {
|
|
1509
1630
|
try {
|
|
1510
1631
|
const parsed = JSON.parse(requestInit.body);
|
|
1511
|
-
fallbackBody = {
|
|
1632
|
+
fallbackBody = {
|
|
1633
|
+
...parsed,
|
|
1634
|
+
model,
|
|
1635
|
+
instructions: fallbackInstructions,
|
|
1636
|
+
};
|
|
1637
|
+
if (Array.isArray(fallbackBody.input)) {
|
|
1638
|
+
fallbackBody.input = upsertBackendModelIdentityMessage(fallbackBody.input, model);
|
|
1639
|
+
}
|
|
1512
1640
|
}
|
|
1513
1641
|
catch {
|
|
1514
1642
|
// Keep minimal fallback body if parsing fails.
|
|
@@ -1715,14 +1843,28 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1715
1843
|
continue;
|
|
1716
1844
|
}
|
|
1717
1845
|
const waitLabel = waitMs > 0 ? formatWaitTime(waitMs) : "a bit";
|
|
1846
|
+
const wasEntitlementExhaustion = runtimeMetrics.lastErrorCategory === "unsupported-model";
|
|
1847
|
+
const entitlementModel = typeof runtimeMetrics.lastError === "string"
|
|
1848
|
+
? runtimeMetrics.lastError.replace(/^Unsupported model.*?:\s*/i, "").trim()
|
|
1849
|
+
: "";
|
|
1850
|
+
const entitlementDetail = entitlementModel.length > 0
|
|
1851
|
+
? ` The backend rejected '${entitlementModel}' as not entitled for Codex OAuth on every pooled account.`
|
|
1852
|
+
: "";
|
|
1718
1853
|
const message = count === 0
|
|
1719
1854
|
? "No Codex accounts configured. Run `opencode auth login`."
|
|
1720
1855
|
: waitMs > 0
|
|
1721
1856
|
? `All ${count} account(s) are rate-limited. Try again in ${waitLabel} or add another account with \`opencode auth login\`.`
|
|
1722
|
-
:
|
|
1857
|
+
: wasEntitlementExhaustion
|
|
1858
|
+
? `All ${count} account(s) returned 'model not supported' for the requested model.${entitlementDetail} If this is a GPT-5.5 request during the rollout period, set \`unsupportedCodexPolicy: "fallback"\` (or \`CODEX_AUTH_UNSUPPORTED_MODEL_POLICY=fallback\`) to auto-fallback to gpt-5.4. See \`codex-health\` for per-account details.`
|
|
1859
|
+
: `All ${count} account(s) failed (server errors or auth issues). Check account health with \`codex-health\`.`;
|
|
1723
1860
|
runtimeMetrics.failedRequests++;
|
|
1724
1861
|
runtimeMetrics.lastError = message;
|
|
1725
|
-
runtimeMetrics.lastErrorCategory =
|
|
1862
|
+
runtimeMetrics.lastErrorCategory =
|
|
1863
|
+
waitMs > 0
|
|
1864
|
+
? "rate-limit"
|
|
1865
|
+
: wasEntitlementExhaustion
|
|
1866
|
+
? "unsupported-model"
|
|
1867
|
+
: "account-failure";
|
|
1726
1868
|
return new Response(JSON.stringify({ error: { message } }), {
|
|
1727
1869
|
status: waitMs > 0 ? 429 : 503,
|
|
1728
1870
|
headers: {
|
|
@@ -2127,6 +2269,10 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2127
2269
|
account.expiresAt = refreshResult.expires;
|
|
2128
2270
|
storageChanged = true;
|
|
2129
2271
|
}
|
|
2272
|
+
if (refreshResult.scope && refreshResult.scope !== account.oauthScope) {
|
|
2273
|
+
account.oauthScope = refreshResult.scope;
|
|
2274
|
+
storageChanged = true;
|
|
2275
|
+
}
|
|
2130
2276
|
const hydratedEmail = sanitizeEmail(extractAccountEmail(refreshResult.access, refreshResult.idToken));
|
|
2131
2277
|
if (hydratedEmail && hydratedEmail !== account.email) {
|
|
2132
2278
|
account.email = hydratedEmail;
|