oc-codex-multi-auth 6.8.2 → 6.9.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
@@ -180,6 +180,7 @@ If browser launch is blocked, use the alternate login paths in [docs/getting-sta
180
180
  | `codex-limits` | What quota or rate-limit state is visible now? |
181
181
  | `codex-reset` | Do I have a banked rate-limit reset credit, and how do I redeem it? |
182
182
  | `codex-dashboard` | Can I manage accounts from one interactive surface? |
183
+ | `codex-pool` | Which accounts are preferred for each model, and how do I change them? |
183
184
 
184
185
  Most of these also run as a **direct CLI** with no agent/model involvement (no token cost) — e.g. `oc-codex-multi-auth warm`, `oc-codex-multi-auth status`, or `npx -y oc-codex-multi-auth@latest warm`. Use `oc-codex-multi-auth warm` to open every enabled account's usage window at the start of a session and stagger the rolling quota cooldowns. Add `--json` for scriptable output.
185
186
 
@@ -244,6 +245,58 @@ Primary config files:
244
245
  - `~/.config/opencode/tui.json`
245
246
  - `~/.opencode/openai-codex-auth-config.json`
246
247
 
248
+ ### Route models to preferred accounts
249
+
250
+ Use `modelAccountPools` to assign one or more preferred ChatGPT accounts to a
251
+ model. Account references use stable account IDs, so adding, removing, or
252
+ reordering accounts does not silently change a model's routing.
253
+
254
+ ```json
255
+ {
256
+ "modelAccountPools": {
257
+ "gpt-5.6-sol": [
258
+ "org-example-account-id",
259
+ "00000000-0000-0000-0000-000000000000"
260
+ ],
261
+ "gpt-5.6-terra": [
262
+ "org-another-account-id"
263
+ ]
264
+ }
265
+ }
266
+ ```
267
+
268
+ Save this configuration in `~/.opencode/openai-codex-auth-config.json`, then
269
+ restart OpenCode. Model matching is case-insensitive and uses the effective
270
+ model after request model normalization.
271
+
272
+ Use `codex-pool` to manage these mappings with ordinary 1-based account
273
+ numbers. The tool resolves those numbers and writes stable IDs to disk:
274
+
275
+ ```text
276
+ codex-pool
277
+ codex-pool action="set" model="gpt-5.6-sol" accounts=[7,8]
278
+ codex-pool action="add" model="gpt-5.6-sol" accounts=[9]
279
+ codex-pool action="remove" model="gpt-5.6-sol" accounts=[7]
280
+ codex-pool action="clear" model="gpt-5.6-sol"
281
+ ```
282
+
283
+ Add `dryRun=true` to preview a mutation. Use `format="json"` for structured
284
+ output; stable IDs remain redacted unless `includeSensitive=true` is also set.
285
+ Restart OpenCode after an applied mutation. The plugin configuration is global
286
+ while account storage is per-project by default, so a reference unresolved in
287
+ the current project is reported but never automatically deleted.
288
+
289
+ Routing behavior:
290
+
291
+ - A mapped model uses only healthy, selectable accounts in its preferred pool.
292
+ - Existing rotation strategy, quota, cooldown, and token-health rules still apply within the preferred pool.
293
+ - If every preferred account is unavailable, disabled, unknown, cooling down, or rate-limited, routing automatically falls back to the healthy general account pool.
294
+ - An unmapped model or an empty account list uses the general account pool directly.
295
+ - `codex-status`, `codex-dashboard`, and routing diagnostics report the account-pool mode as `preferred`, `general`, or `general-fallback`.
296
+
297
+ Account IDs are local account metadata but should still be treated as private
298
+ configuration. Do not publish a populated configuration file.
299
+
247
300
  Selected runtime/environment overrides:
248
301
 
249
302
  | Variable | Effect |
@@ -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;AA4M/D;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,iBAAiB,EAAE,MA4sH/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;AA6M/D;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,iBAAiB,EAAE,MAgvH/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, getPerProjectAccounts, getEmptyResponseMaxRetries, getEmptyResponseRetryDelayMs, getPidOffsetEnabled, getRotationStrategy, 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, getPerProjectAccounts, getEmptyResponseMaxRetries, getEmptyResponseRetryDelayMs, getPidOffsetEnabled, getRotationStrategy, getModelAccountPool, 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";
@@ -132,6 +132,8 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
132
132
  fallbackFrom: snapshot?.fallbackFrom ?? null,
133
133
  fallbackTo: snapshot?.fallbackTo ?? null,
134
134
  fallbackReason: snapshot?.fallbackReason ?? null,
135
+ accountPoolMode: snapshot?.accountPoolMode ?? null,
136
+ configuredAccountPoolSize: snapshot?.configuredAccountPoolSize ?? 0,
135
137
  selectionExplainability: serializeSelectionExplainability(options.selectionExplainability ?? snapshot?.explainability ?? []),
136
138
  };
137
139
  };
@@ -160,6 +162,8 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
160
162
  lines.push(` Fallback from: ${formatRoutingValue(routing.fallbackFrom)}`);
161
163
  lines.push(` Fallback to: ${formatRoutingValue(routing.fallbackTo)}`);
162
164
  lines.push(` Fallback reason: ${formatRoutingValue(routing.fallbackReason)}`);
165
+ lines.push(` Account pool: ${formatRoutingValue(routing.accountPoolMode)}`);
166
+ lines.push(` Configured pool size: ${routing.configuredAccountPoolSize}`);
163
167
  if (options.includeExplainability) {
164
168
  lines.push(" Selection explainability:");
165
169
  if (routing.selectionExplainability.length === 0) {
@@ -186,6 +190,8 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
186
190
  lines.push(formatUiKeyValue(ui, "Fallback from", formatRoutingValue(routing.fallbackFrom), "muted"));
187
191
  lines.push(formatUiKeyValue(ui, "Fallback to", formatRoutingValue(routing.fallbackTo), "muted"));
188
192
  lines.push(formatUiKeyValue(ui, "Fallback reason", formatRoutingValue(routing.fallbackReason), routing.fallbackReason ? "warning" : "muted"));
193
+ lines.push(formatUiKeyValue(ui, "Account pool", formatRoutingValue(routing.accountPoolMode), routing.accountPoolMode === "general-fallback" ? "warning" : "muted"));
194
+ lines.push(formatUiKeyValue(ui, "Configured pool size", String(routing.configuredAccountPoolSize), "muted"));
189
195
  if (options.includeExplainability) {
190
196
  lines.push("");
191
197
  lines.push(...formatUiSection(ui, "Selection explainability"));
@@ -1355,6 +1361,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
1355
1361
  const attempted = new Set();
1356
1362
  let restartAccountTraversalWithFallback = false;
1357
1363
  let restartAccountTraversalAfterWorkspaceDeactivation = false;
1364
+ const preferredAccountIds = getModelAccountPool(pluginConfig, model);
1358
1365
  while (attempted.size < Math.max(1, accountCount)) {
1359
1366
  const selectionExplainability = accountManager.getSelectionExplainability(modelFamily, model, Date.now());
1360
1367
  runtimeMetrics.lastSelectionSnapshot = {
@@ -1370,8 +1377,9 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
1370
1377
  fallbackFrom,
1371
1378
  fallbackTo,
1372
1379
  fallbackReason,
1380
+ configuredAccountPoolSize: preferredAccountIds.length,
1373
1381
  };
1374
- const account = accountManager.getAccountForStrategy(rotationStrategy, modelFamily, model, { pidOffsetEnabled });
1382
+ const account = accountManager.getAccountForStrategy(rotationStrategy, modelFamily, model, { pidOffsetEnabled }, preferredAccountIds);
1375
1383
  if (!account || attempted.has(account.index)) {
1376
1384
  break;
1377
1385
  }
@@ -1389,6 +1397,13 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
1389
1397
  fallbackFrom,
1390
1398
  fallbackTo,
1391
1399
  fallbackReason,
1400
+ accountPoolMode: preferredAccountIds.length === 0
1401
+ ? "general"
1402
+ : account.accountId !== undefined &&
1403
+ preferredAccountIds.includes(account.accountId)
1404
+ ? "preferred"
1405
+ : "general-fallback",
1406
+ configuredAccountPoolSize: preferredAccountIds.length,
1392
1407
  };
1393
1408
  }
1394
1409
  // Log account selection for debugging rotation