oc-codex-multi-auth 6.11.4 → 6.12.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 CHANGED
@@ -307,6 +307,10 @@ reordering accounts does not silently change a model's routing.
307
307
  "gpt-5.6-terra": [
308
308
  "org-another-account-id"
309
309
  ]
310
+ },
311
+ "modelAccountPoolModes": {
312
+ "gpt-5.6-sol": "strict",
313
+ "gpt-5.6-terra": "preferred"
310
314
  }
311
315
  }
312
316
  ```
@@ -323,6 +327,7 @@ codex-pool
323
327
  codex-pool action="set" model="gpt-5.6-sol" accounts=[7,8]
324
328
  codex-pool action="add" model="gpt-5.6-sol" accounts=[9]
325
329
  codex-pool action="remove" model="gpt-5.6-sol" accounts=[7]
330
+ codex-pool action="set-mode" model="gpt-5.6-sol" poolMode="strict"
326
331
  codex-pool action="clear" model="gpt-5.6-sol"
327
332
  ```
328
333
 
@@ -334,11 +339,12 @@ the current project is reported but never automatically deleted.
334
339
 
335
340
  Routing behavior:
336
341
 
337
- - A mapped model uses only healthy, selectable accounts in its preferred pool.
342
+ - A mapped model defaults to `preferred` mode and uses healthy, selectable accounts in its pool.
338
343
  - Existing rotation strategy, quota, cooldown, and token-health rules still apply within the preferred pool.
339
- - If every preferred account is unavailable, disabled, unknown, cooling down, or rate-limited, routing automatically falls back to the healthy general account pool.
344
+ - In `preferred` mode, an unavailable pool falls back to the healthy general account pool.
345
+ - In `strict` mode, routing never leaves the configured pool and immediately returns `strict_pool_unavailable` when no pooled account is selectable.
340
346
  - An unmapped model or an empty account list uses the general account pool directly.
341
- - `codex-status`, `codex-dashboard`, and routing diagnostics report the account-pool mode as `preferred`, `general`, or `general-fallback`.
347
+ - `codex-status`, `codex-dashboard`, and routing diagnostics also report `strict` and `strict-unavailable` modes.
342
348
 
343
349
  Account IDs are local account metadata but should still be treated as private
344
350
  configuration. Do not publish a populated configuration file.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAuN/D;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,iBAAiB,EAAE,MA2lH/B,CAAC;AAEF,eAAO,MAAM,gBAAgB,QAAoB,CAAC;AAElD,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAwN/D;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,iBAAiB,EAAE,MA8oH/B,CAAC;AAEF,eAAO,MAAM,gBAAgB,QAAoB,CAAC;AAElD,eAAe,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -32,7 +32,7 @@ import { queuedRefresh } from "./lib/refresh-queue.js";
32
32
  import { openBrowserUrl } from "./lib/auth/browser.js";
33
33
  import { startLocalOAuthServer } from "./lib/auth/server.js";
34
34
  import { promptAddAnotherAccount, promptLoginMode } from "./lib/cli.js";
35
- import { getCodexMode, getRequestTransformMode, getFastSession, getFastSessionStrategy, getFastSessionMaxInputItems, getRetryProfile, getRetryBudgetOverrides, getRateLimitToastDebounceMs, getRetryAllAccountsMaxRetries, getRetryAllAccountsMaxWaitMs, getRetryAllAccountsRateLimited, getFallbackToGpt52OnUnsupportedGpt53, getUnsupportedCodexPolicy, getUnsupportedCodexFallbackChain, getTokenRefreshSkewMs, getSessionRecovery, getAutoResume, getAutoUpdate, getToastDurationMs, getAccountToastsEnabled, getPerProjectAccounts, getEmptyResponseMaxRetries, getEmptyResponseRetryDelayMs, getPidOffsetEnabled, getRotationStrategy, getModelAccountPool, getFetchTimeoutMs, getStreamStallTimeoutMs, getCodexTuiV2, getCodexTuiColorProfile, getCodexTuiGlyphMode, getBeginnerSafeMode, getCodexTuiMaskEmail, loadPluginConfig, } from "./lib/config.js";
35
+ import { getCodexMode, getRequestTransformMode, getFastSession, getFastSessionStrategy, getFastSessionMaxInputItems, getRetryProfile, getRetryBudgetOverrides, getRateLimitToastDebounceMs, getRetryAllAccountsMaxRetries, getRetryAllAccountsMaxWaitMs, getRetryAllAccountsRateLimited, getFallbackToGpt52OnUnsupportedGpt53, getUnsupportedCodexPolicy, getUnsupportedCodexFallbackChain, getTokenRefreshSkewMs, getSessionRecovery, getAutoResume, getAutoUpdate, getToastDurationMs, getAccountToastsEnabled, getPerProjectAccounts, getEmptyResponseMaxRetries, getEmptyResponseRetryDelayMs, getPidOffsetEnabled, getRotationStrategy, getModelAccountPool, getModelAccountPoolMode, getFetchTimeoutMs, getStreamStallTimeoutMs, getCodexTuiV2, getCodexTuiColorProfile, getCodexTuiGlyphMode, getBeginnerSafeMode, getCodexTuiMaskEmail, loadPluginConfig, } from "./lib/config.js";
36
36
  import { AUTH_LABELS, CODEX_BASE_URL, DUMMY_API_KEY, LOG_STAGES, PLUGIN_NAME, PROVIDER_ID, ACCOUNT_LIMITS, } from "./lib/constants.js";
37
37
  import { initLogger, logRequest, logDebug, logInfo, logWarn, logError, setCorrelationId, clearCorrelationId, } from "./lib/logger.js";
38
38
  import { checkAndNotify } from "./lib/auto-update-checker.js";
@@ -191,7 +191,10 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
191
191
  lines.push(formatUiKeyValue(ui, "Fallback from", formatRoutingValue(routing.fallbackFrom), "muted"));
192
192
  lines.push(formatUiKeyValue(ui, "Fallback to", formatRoutingValue(routing.fallbackTo), "muted"));
193
193
  lines.push(formatUiKeyValue(ui, "Fallback reason", formatRoutingValue(routing.fallbackReason), routing.fallbackReason ? "warning" : "muted"));
194
- lines.push(formatUiKeyValue(ui, "Account pool", formatRoutingValue(routing.accountPoolMode), routing.accountPoolMode === "general-fallback" ? "warning" : "muted"));
194
+ lines.push(formatUiKeyValue(ui, "Account pool", formatRoutingValue(routing.accountPoolMode), routing.accountPoolMode === "general-fallback" ||
195
+ routing.accountPoolMode === "strict-unavailable"
196
+ ? "warning"
197
+ : "muted"));
195
198
  lines.push(formatUiKeyValue(ui, "Configured pool size", String(routing.configuredAccountPoolSize), "muted"));
196
199
  if (options.includeExplainability) {
197
200
  lines.push("");
@@ -1434,6 +1437,8 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
1434
1437
  let restartAccountTraversalWithFallback = false;
1435
1438
  let restartAccountTraversalAfterWorkspaceDeactivation = false;
1436
1439
  const preferredAccountIds = getModelAccountPool(pluginConfig, model);
1440
+ const accountPoolMode = getModelAccountPoolMode(pluginConfig, model);
1441
+ const strictAccountPool = preferredAccountIds.length > 0 && accountPoolMode === "strict";
1437
1442
  while (attempted.size < Math.max(1, accountCount)) {
1438
1443
  const selectionExplainability = accountManager.getSelectionExplainability(modelFamily, model, Date.now());
1439
1444
  runtimeMetrics.lastSelectionSnapshot = {
@@ -1450,8 +1455,9 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
1450
1455
  fallbackTo,
1451
1456
  fallbackReason,
1452
1457
  configuredAccountPoolSize: preferredAccountIds.length,
1458
+ accountPoolMode: strictAccountPool ? "strict" : undefined,
1453
1459
  };
1454
- const account = accountManager.getAccountForStrategy(rotationStrategy, modelFamily, model, { pidOffsetEnabled }, preferredAccountIds);
1460
+ const account = accountManager.getAccountForStrategy(rotationStrategy, modelFamily, model, { pidOffsetEnabled }, preferredAccountIds, accountPoolMode, attempted);
1455
1461
  if (!account || attempted.has(account.index)) {
1456
1462
  break;
1457
1463
  }
@@ -1469,12 +1475,14 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
1469
1475
  fallbackFrom,
1470
1476
  fallbackTo,
1471
1477
  fallbackReason,
1472
- accountPoolMode: preferredAccountIds.length === 0
1473
- ? "general"
1474
- : account.accountId !== undefined &&
1475
- preferredAccountIds.includes(account.accountId)
1476
- ? "preferred"
1477
- : "general-fallback",
1478
+ accountPoolMode: strictAccountPool
1479
+ ? "strict"
1480
+ : preferredAccountIds.length === 0
1481
+ ? "general"
1482
+ : account.accountId !== undefined &&
1483
+ preferredAccountIds.includes(account.accountId)
1484
+ ? "preferred"
1485
+ : "general-fallback",
1478
1486
  configuredAccountPoolSize: preferredAccountIds.length,
1479
1487
  };
1480
1488
  }
@@ -2132,6 +2140,36 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
2132
2140
  if (restartAccountTraversalAfterWorkspaceDeactivation) {
2133
2141
  continue;
2134
2142
  }
2143
+ if (strictAccountPool) {
2144
+ const poolWaitMs = accountManager.getMinWaitTimeForFamily(modelFamily, model, preferredAccountIds);
2145
+ const effectiveModel = model ?? requestedModel ?? "requested model";
2146
+ const waitDetail = poolWaitMs > 0
2147
+ ? ` Try again in ${formatWaitTime(poolWaitMs)}.`
2148
+ : " Check the pooled accounts with `codex-health`.";
2149
+ const message = `Strict account pool unavailable for ${effectiveModel}. ` +
2150
+ `None of its ${preferredAccountIds.length} configured account(s) is selectable.` +
2151
+ waitDetail;
2152
+ if (runtimeMetrics.lastSelectionSnapshot) {
2153
+ runtimeMetrics.lastSelectionSnapshot = {
2154
+ ...runtimeMetrics.lastSelectionSnapshot,
2155
+ accountPoolMode: "strict-unavailable",
2156
+ };
2157
+ }
2158
+ runtimeMetrics.failedRequests++;
2159
+ runtimeMetrics.lastError = message;
2160
+ runtimeMetrics.lastErrorCategory = "strict-pool-unavailable";
2161
+ return new Response(JSON.stringify({
2162
+ error: {
2163
+ code: "strict_pool_unavailable",
2164
+ message,
2165
+ },
2166
+ }), {
2167
+ status: poolWaitMs > 0 ? 429 : 503,
2168
+ headers: {
2169
+ "content-type": "application/json; charset=utf-8",
2170
+ },
2171
+ });
2172
+ }
2135
2173
  const waitMs = accountManager.getMinWaitTimeForFamily(modelFamily, model);
2136
2174
  const count = accountManager.getAccountCount();
2137
2175
  if (retryAllAccountsRateLimited &&