oc-codex-multi-auth 6.23.0 → 6.25.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 (127) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +795 -729
  3. package/assets/icon.svg +7 -7
  4. package/assets/opencode-logo-ornate-dark.svg +18 -18
  5. package/assets/readme-hero.svg +31 -31
  6. package/config/README.md +197 -197
  7. package/config/minimal-opencode.json +14 -14
  8. package/config/opencode-legacy.json +1346 -1346
  9. package/config/opencode-modern.json +466 -466
  10. package/dist/index.d.ts +7 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +61 -57
  13. package/dist/index.js.map +1 -1
  14. package/dist/lib/accounts/persistence.d.ts +6 -6
  15. package/dist/lib/accounts/persistence.d.ts.map +1 -1
  16. package/dist/lib/accounts/persistence.js +60 -6
  17. package/dist/lib/accounts/persistence.js.map +1 -1
  18. package/dist/lib/accounts/recovery.d.ts +5 -1
  19. package/dist/lib/accounts/recovery.d.ts.map +1 -1
  20. package/dist/lib/accounts/recovery.js +26 -1
  21. package/dist/lib/accounts/recovery.js.map +1 -1
  22. package/dist/lib/accounts/state.d.ts +18 -0
  23. package/dist/lib/accounts/state.d.ts.map +1 -1
  24. package/dist/lib/accounts/state.js +26 -2
  25. package/dist/lib/accounts/state.js.map +1 -1
  26. package/dist/lib/accounts.d.ts +2 -0
  27. package/dist/lib/accounts.d.ts.map +1 -1
  28. package/dist/lib/accounts.js +7 -1
  29. package/dist/lib/accounts.js.map +1 -1
  30. package/dist/lib/auth/login-runner.d.ts.map +1 -1
  31. package/dist/lib/auth/login-runner.js +8 -0
  32. package/dist/lib/auth/login-runner.js.map +1 -1
  33. package/dist/lib/auth/plan-tier.d.ts.map +1 -1
  34. package/dist/lib/auth/plan-tier.js +2 -1
  35. package/dist/lib/auth/plan-tier.js.map +1 -1
  36. package/dist/lib/codex-usage.d.ts +84 -0
  37. package/dist/lib/codex-usage.d.ts.map +1 -1
  38. package/dist/lib/codex-usage.js +186 -5
  39. package/dist/lib/codex-usage.js.map +1 -1
  40. package/dist/lib/config.d.ts +16 -3
  41. package/dist/lib/config.d.ts.map +1 -1
  42. package/dist/lib/config.js +14 -0
  43. package/dist/lib/config.js.map +1 -1
  44. package/dist/lib/context-overflow.js +10 -10
  45. package/dist/lib/desktop-notifications.js +6 -6
  46. package/dist/lib/oauth-success.js +202 -202
  47. package/dist/lib/opencode-v2-provider.d.ts +2 -0
  48. package/dist/lib/opencode-v2-provider.d.ts.map +1 -0
  49. package/dist/lib/opencode-v2-provider.js +4 -0
  50. package/dist/lib/opencode-v2-provider.js.map +1 -0
  51. package/dist/lib/opencode-v2-rpc.d.ts +31 -0
  52. package/dist/lib/opencode-v2-rpc.d.ts.map +1 -0
  53. package/dist/lib/opencode-v2-rpc.js +17 -0
  54. package/dist/lib/opencode-v2-rpc.js.map +1 -0
  55. package/dist/lib/opencode-v2-status.d.ts +18 -0
  56. package/dist/lib/opencode-v2-status.d.ts.map +1 -0
  57. package/dist/lib/opencode-v2-status.js +98 -0
  58. package/dist/lib/opencode-v2-status.js.map +1 -0
  59. package/dist/lib/opencode-v2-tui.d.ts +4 -0
  60. package/dist/lib/opencode-v2-tui.d.ts.map +1 -0
  61. package/dist/lib/opencode-v2-tui.js +112 -0
  62. package/dist/lib/opencode-v2-tui.js.map +1 -0
  63. package/dist/lib/opencode-v2.d.ts +11 -0
  64. package/dist/lib/opencode-v2.d.ts.map +1 -0
  65. package/dist/lib/opencode-v2.js +235 -0
  66. package/dist/lib/opencode-v2.js.map +1 -0
  67. package/dist/lib/prompts/codex-opencode-bridge.js +67 -67
  68. package/dist/lib/prompts/codex.d.ts.map +1 -1
  69. package/dist/lib/prompts/codex.js +79 -75
  70. package/dist/lib/prompts/codex.js.map +1 -1
  71. package/dist/lib/quota-capacity.d.ts +4 -0
  72. package/dist/lib/quota-capacity.d.ts.map +1 -0
  73. package/dist/lib/quota-capacity.js +45 -0
  74. package/dist/lib/quota-capacity.js.map +1 -0
  75. package/dist/lib/quota-overview.d.ts +13 -27
  76. package/dist/lib/quota-overview.d.ts.map +1 -1
  77. package/dist/lib/quota-overview.js +59 -99
  78. package/dist/lib/quota-overview.js.map +1 -1
  79. package/dist/lib/quota-recovery.d.ts +5 -0
  80. package/dist/lib/quota-recovery.d.ts.map +1 -0
  81. package/dist/lib/quota-recovery.js +66 -0
  82. package/dist/lib/quota-recovery.js.map +1 -0
  83. package/dist/lib/request/fetch-helpers.d.ts +1 -1
  84. package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
  85. package/dist/lib/request/fetch-helpers.js +1 -1
  86. package/dist/lib/request/fetch-helpers.js.map +1 -1
  87. package/dist/lib/schemas.d.ts +14 -1
  88. package/dist/lib/schemas.d.ts.map +1 -1
  89. package/dist/lib/schemas.js +7 -2
  90. package/dist/lib/schemas.js.map +1 -1
  91. package/dist/lib/storage/load-save.d.ts.map +1 -1
  92. package/dist/lib/storage/load-save.js +22 -20
  93. package/dist/lib/storage/load-save.js.map +1 -1
  94. package/dist/lib/storage/state.d.ts +8 -0
  95. package/dist/lib/storage/state.d.ts.map +1 -1
  96. package/dist/lib/storage/state.js +47 -20
  97. package/dist/lib/storage/state.js.map +1 -1
  98. package/dist/lib/tools/codex-limits.d.ts.map +1 -1
  99. package/dist/lib/tools/codex-limits.js +49 -3
  100. package/dist/lib/tools/codex-limits.js.map +1 -1
  101. package/dist/lib/tui-quota-cache.d.ts +8 -0
  102. package/dist/lib/tui-quota-cache.d.ts.map +1 -1
  103. package/dist/lib/tui-quota-cache.js +4 -1
  104. package/dist/lib/tui-quota-cache.js.map +1 -1
  105. package/dist/lib/tui-quota-overview.d.ts.map +1 -1
  106. package/dist/lib/tui-quota-overview.js +15 -2
  107. package/dist/lib/tui-quota-overview.js.map +1 -1
  108. package/dist/lib/tui-status-slot.d.ts +3 -0
  109. package/dist/lib/tui-status-slot.d.ts.map +1 -0
  110. package/dist/lib/tui-status-slot.js +27 -0
  111. package/dist/lib/tui-status-slot.js.map +1 -0
  112. package/dist/lib/tui-status.d.ts +1 -0
  113. package/dist/lib/tui-status.d.ts.map +1 -1
  114. package/dist/lib/tui-status.js +1 -0
  115. package/dist/lib/tui-status.js.map +1 -1
  116. package/dist/tui.d.ts +10 -3
  117. package/dist/tui.d.ts.map +1 -1
  118. package/dist/tui.js +22 -1
  119. package/dist/tui.js.map +1 -1
  120. package/package.json +157 -155
  121. package/scripts/audit-dev-allowlist.js +114 -114
  122. package/scripts/clean-dist.js +27 -27
  123. package/scripts/copy-oauth-success.js +47 -47
  124. package/scripts/install-oc-codex-multi-auth-core.js +2661 -2016
  125. package/scripts/install-oc-codex-multi-auth.js +37 -37
  126. package/scripts/test-all-models.sh +260 -260
  127. package/scripts/validate-model-map.sh +97 -97
package/dist/index.d.ts CHANGED
@@ -41,5 +41,11 @@ import type { Plugin } from "@opencode-ai/plugin";
41
41
  */
42
42
  export declare const OpenAIOAuthPlugin: Plugin;
43
43
  export declare const OpenAIAuthPlugin: Plugin;
44
- export default OpenAIOAuthPlugin;
44
+ declare const _default: {
45
+ id: string;
46
+ server: Plugin;
47
+ /** V2 loads the same package through setup instead of the V1 server hook. */
48
+ setup(context: import("@opencode/plugin").Plugin.Context): Promise<() => Promise<void>>;
49
+ };
50
+ export default _default;
45
51
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAYH,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAsV/D;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,iBAAiB,EAAE,MA+pJ/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;AAYH,OAAO,KAAK,EAAE,MAAM,EAAsB,MAAM,qBAAqB,CAAC;AAsVtE;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,iBAAiB,EAAE,MAA2E,CAAC;AAupJ5G,eAAO,MAAM,gBAAgB,QAAoB,CAAC;;;;IAKjD,6EAA6E;mBACxD,OAAO,kBAAkB,EAAE,MAAM,CAAC,OAAO;;AAJ/D,wBAQE"}
package/dist/index.js CHANGED
@@ -179,8 +179,16 @@ function resolveOpenAIBaseURL() {
179
179
  * }
180
180
  * ```
181
181
  */
182
- export const OpenAIOAuthPlugin = async ({ client }) => {
183
- initLogger(client);
182
+ export const OpenAIOAuthPlugin = async ({ client }) => createPluginRuntime({ client });
183
+ /**
184
+ * Shared request/account runtime; V2 has no V1 client or host auth.json.
185
+ *
186
+ * Not exported: V1 hosts call every function this module exports as a plugin,
187
+ * so an exported factory would boot a second runtime beside the real one. The
188
+ * V2 entry receives it through `setup` instead.
189
+ */
190
+ async function createPluginRuntime({ client, directory = process.cwd() }) {
191
+ initLogger(client ?? {});
184
192
  let customBaseURLWarningShown = false;
185
193
  let customBaseURLErrorShown = false;
186
194
  let cachedAccountManager = null;
@@ -456,7 +464,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
456
464
  };
457
465
  const showToast = async (message, variant = "success", options) => {
458
466
  try {
459
- await client.tui.showToast({
467
+ await client?.tui.showToast({
460
468
  body: {
461
469
  message,
462
470
  variant,
@@ -1478,6 +1486,8 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
1478
1486
  if (event.type === "server.instance.disposed") {
1479
1487
  quotaMonitor.dispose();
1480
1488
  disposeAccountsWatcher();
1489
+ await cachedAccountManager?.flushPendingSave();
1490
+ cachedAccountManager?.disposeShutdownHandler();
1481
1491
  return;
1482
1492
  }
1483
1493
  // Handle TUI account selection events
@@ -1585,8 +1595,9 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
1585
1595
  };
1586
1596
  const startupPluginConfig = loadPluginConfig();
1587
1597
  const startupPerProjectAccounts = getPerProjectAccounts(startupPluginConfig);
1588
- setStoragePath(startupPerProjectAccounts ? process.cwd() : null);
1589
- await backfillHostOpenAIAuthFromPool();
1598
+ setStoragePath(startupPerProjectAccounts ? directory : null);
1599
+ if (client)
1600
+ await backfillHostOpenAIAuthFromPool();
1590
1601
  quotaMonitor.start();
1591
1602
  return {
1592
1603
  event: eventHandler,
@@ -1635,7 +1646,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
1635
1646
  let perProjectAccounts = getPerProjectAccounts(pluginConfig);
1636
1647
  let storageTransition;
1637
1648
  const activeFetches = new Set();
1638
- setStoragePath(perProjectAccounts ? process.cwd() : null);
1649
+ setStoragePath(perProjectAccounts ? directory : null);
1639
1650
  const authFallback = auth.type === "oauth" ? auth : undefined;
1640
1651
  // Prefer multi-account auth metadata when available, but still handle
1641
1652
  // plain OAuth credentials (for OpenCode versions that inject internal
@@ -1744,8 +1755,8 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
1744
1755
  prewarmOpenCodeCodexPrompt();
1745
1756
  }
1746
1757
  }
1747
- const recoveryHook = sessionRecoveryEnabled
1748
- ? createSessionRecoveryHook({ client, directory: process.cwd() }, { sessionRecovery: true, autoResume: autoResumeEnabled })
1758
+ const recoveryHook = sessionRecoveryEnabled && client
1759
+ ? createSessionRecoveryHook({ client, directory }, { sessionRecovery: true, autoResume: autoResumeEnabled })
1749
1760
  : null;
1750
1761
  const pluginOrigin = getPluginOrigin();
1751
1762
  if (pluginOrigin && !startupOriginRecorded) {
@@ -1800,7 +1811,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
1800
1811
  await Promise.all(activeFetches);
1801
1812
  // Drain the old pool's pending write before changing the global path.
1802
1813
  await cachedAccountManager?.flushPendingSave();
1803
- setStoragePath(currentPerProjectAccounts ? process.cwd() : null);
1814
+ setStoragePath(currentPerProjectAccounts ? directory : null);
1804
1815
  invalidateAccountManagerCache();
1805
1816
  perProjectAccounts = currentPerProjectAccounts;
1806
1817
  })().finally(() => { storageTransition = undefined; });
@@ -2203,7 +2214,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
2203
2214
  accountManager = reloaded;
2204
2215
  }
2205
2216
  }
2206
- let accountCount = accountManager.getAccountCount();
2217
+ const accountCount = accountManager.getAccountCount();
2207
2218
  const attempted = new Set();
2208
2219
  // Diagnostics for the terminal error message below. The composite key keeps
2209
2220
  // legacy Business seats distinct and can also be compared with snapshots
@@ -2303,9 +2314,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
2303
2314
  runtimeMetrics.lastError = err?.message ?? String(err);
2304
2315
  runtimeMetrics.lastErrorCategory = "auth-refresh";
2305
2316
  // Transient refresh failures (network blip / upstream 5xx) must NOT
2306
- // count toward permanent account removal — the credentials are still
2307
- // valid and a flaky network or outage would otherwise silently delete
2308
- // them. Cool the account down briefly and rotate instead.
2317
+ // count toward disabling an account. Cool it down and rotate instead.
2309
2318
  const isTransientRefreshFailure = err instanceof CodexAuthError && err.retryable === true;
2310
2319
  if (isTransientRefreshFailure) {
2311
2320
  const cooledCount = accountManager.markAccountsWithRefreshTokenCoolingDown(account.refreshToken, ACCOUNT_LIMITS.AUTH_FAILURE_COOLDOWN_MS, "auth-failure");
@@ -2313,7 +2322,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
2313
2322
  accountManager.markAccountCoolingDown(account, ACCOUNT_LIMITS.AUTH_FAILURE_COOLDOWN_MS, "auth-failure");
2314
2323
  }
2315
2324
  accountManager.saveToDiskDebounced();
2316
- logWarn(`[${PLUGIN_NAME}] Transient auth refresh failure for account ${account.index + 1} (${err.refreshFailureReason ?? "unknown"}${err.statusCode ? ` ${err.statusCode}` : ""}); cooling down without counting toward removal.`);
2325
+ logWarn(`[${PLUGIN_NAME}] Transient auth refresh failure for account ${account.index + 1} (${err.refreshFailureReason ?? "unknown"}${err.statusCode ? ` ${err.statusCode}` : ""}); cooling down without disabling.`);
2317
2326
  continue;
2318
2327
  }
2319
2328
  const failures = await accountManager.incrementAuthFailures(account);
@@ -2322,27 +2331,19 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
2322
2331
  peerAccounts: accountManager.getAccountsSnapshot(),
2323
2332
  });
2324
2333
  if (failures >= ACCOUNT_LIMITS.MAX_AUTH_FAILURES_BEFORE_REMOVAL) {
2325
- const removedCount = accountManager.removeAccountsWithSameRefreshToken(account);
2326
- if (removedCount <= 0) {
2327
- logWarn(`[${PLUGIN_NAME}] Expected grouped account removal after auth failures, but removed ${removedCount}.`);
2328
- const cooledCount = accountManager.markAccountsWithRefreshTokenCoolingDown(account.refreshToken, ACCOUNT_LIMITS.AUTH_FAILURE_COOLDOWN_MS, "auth-failure");
2329
- if (cooledCount <= 0) {
2330
- logWarn(`[${PLUGIN_NAME}] Unable to apply auth-failure cooldown; no live account found for refresh token.`);
2331
- }
2334
+ const disabledCount = accountManager.disableAccountsWithSameRefreshToken(account);
2335
+ if (disabledCount > 0) {
2332
2336
  accountManager.saveToDiskDebounced();
2337
+ await showToast(disabledCount > 1
2338
+ ? `Disabled ${disabledCount} accounts after ${failures} auth failures; credentials retained. Run 'opencode auth login' to repair.`
2339
+ : `Disabled ${accountLabel} after ${failures} auth failures; credentials retained. Run 'opencode auth login' to repair.`, "error", { duration: toastDurationMs * 2 });
2333
2340
  continue;
2334
2341
  }
2335
- accountManager.saveToDiskDebounced();
2336
- const removalMessage = removedCount > 1
2337
- ? `Removed ${removedCount} accounts (same refresh token) after ${failures} consecutive auth failures. Run 'opencode auth login' to re-add.`
2338
- : `Removed ${accountLabel} after ${failures} consecutive auth failures. Run 'opencode auth login' to re-add.`;
2339
- await showToast(removalMessage, "error", { duration: toastDurationMs * 2 });
2340
- // Restart traversal: clear attempted and refresh accountCount to avoid skipping healthy accounts
2341
- attempted.clear();
2342
- accountCount = accountManager.getAccountCount();
2343
- continue;
2344
2342
  }
2345
- accountManager.markAccountCoolingDown(account, ACCOUNT_LIMITS.AUTH_FAILURE_COOLDOWN_MS, "auth-failure");
2343
+ const cooledCount = accountManager.markAccountsWithRefreshTokenCoolingDown(account.refreshToken, ACCOUNT_LIMITS.AUTH_FAILURE_COOLDOWN_MS, "auth-failure");
2344
+ if (cooledCount <= 0) {
2345
+ accountManager.markAccountCoolingDown(account, ACCOUNT_LIMITS.AUTH_FAILURE_COOLDOWN_MS, "auth-failure");
2346
+ }
2346
2347
  accountManager.saveToDiskDebounced();
2347
2348
  continue;
2348
2349
  }
@@ -2605,23 +2606,22 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
2605
2606
  catch (flagError) {
2606
2607
  logWarn(`Failed to persist deactivated workspace flag for ${accountLabel}: ${flagError instanceof Error ? flagError.message : String(flagError)}`);
2607
2608
  }
2608
- // Remove ONLY the deactivated workspace, scoped by workspace
2609
+ // Disable ONLY the deactivated workspace, scoped by workspace
2609
2610
  // identity (org/account id). A single multi-org OAuth login
2610
2611
  // produces sibling accounts that share one refresh token but are
2611
- // independently valid; removing all refresh-token siblings here
2612
+ // independently valid; disabling all refresh-token siblings here
2612
2613
  // would silently drop still-valid workspaces from rotation. The
2613
2614
  // refresh token itself is still good, so siblings must survive.
2614
- const removedCount = accountManager.removeAccountsByWorkspaceIdentity(account);
2615
- if (removedCount > 0) {
2615
+ const disabledCount = accountManager.disableAccountsByWorkspaceIdentity(account);
2616
+ if (disabledCount > 0) {
2616
2617
  accountManager.saveToDiskDebounced();
2617
2618
  restartAccountTraversalAfterWorkspaceDeactivation = true;
2618
- const removalMessage = removedCount > 1
2619
- ? `Workspace deactivated. Removed ${removedCount} related entries from rotation and switching accounts.`
2620
- : `Workspace deactivated. Removed ${accountLabel} from rotation and switching accounts.`;
2619
+ const removalMessage = disabledCount > 1
2620
+ ? `Workspace deactivated. Disabled ${disabledCount} related entries; credentials retained.`
2621
+ : `Workspace deactivated. Disabled ${accountLabel}; credentials retained.`;
2621
2622
  await showToast(removalMessage, "warning", { duration: toastDurationMs });
2622
2623
  break;
2623
2624
  }
2624
- logWarn(`[${PLUGIN_NAME}] Expected grouped account removal after workspace deactivation, but removed ${removedCount}.`);
2625
2625
  accountManager.markAccountCoolingDown(account, ACCOUNT_LIMITS.AUTH_FAILURE_COOLDOWN_MS, "auth-failure");
2626
2626
  accountManager.saveToDiskDebounced();
2627
2627
  break;
@@ -2782,7 +2782,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
2782
2782
  // explicit handler the 401 fell straight through to `return errorResponse`
2783
2783
  // below, so persisted family routing kept pinning every request to the dead
2784
2784
  // account slot instead of failing over (issue #171). Treat it as an
2785
- // account-health failure: cool the refresh-token group down (or remove it
2785
+ // account-health failure: cool the refresh-token group down (or disable it
2786
2786
  // past the failure threshold) and rotate to the next healthy account.
2787
2787
  //
2788
2788
  // Note: 401s intentionally do NOT feed the circuit breaker — the breaker
@@ -2802,21 +2802,16 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
2802
2802
  runtimeMetrics.lastErrorCategory = "auth-invalidated";
2803
2803
  const failures = await accountManager.incrementAuthFailures(account);
2804
2804
  if (failures >= ACCOUNT_LIMITS.MAX_AUTH_FAILURES_BEFORE_REMOVAL) {
2805
- const removedCount = accountManager.removeAccountsWithSameRefreshToken(account);
2806
- if (removedCount > 0) {
2805
+ const disabledCount = accountManager.disableAccountsWithSameRefreshToken(account);
2806
+ if (disabledCount > 0) {
2807
2807
  accountManager.saveToDiskDebounced();
2808
- await showToast(removedCount > 1
2809
- ? `Removed ${removedCount} accounts (same refresh token) after ${failures} auth-token failures. Run 'opencode auth login' to re-add.`
2810
- : `Removed ${accountLabel} after ${failures} auth-token failures. Run 'opencode auth login' to re-add.`, "error", { duration: toastDurationMs * 2 });
2811
- // Indices shift after removal; restart traversal with a fresh
2812
- // attempted set so no healthy account is skipped.
2813
- attempted.clear();
2814
- accountCount = accountManager.getAccountCount();
2808
+ await showToast(disabledCount > 1
2809
+ ? `Disabled ${disabledCount} accounts after ${failures} auth-token failures; credentials retained. Run 'opencode auth login' to repair.`
2810
+ : `Disabled ${accountLabel} after ${failures} auth-token failures; credentials retained. Run 'opencode auth login' to repair.`, "error", { duration: toastDurationMs * 2 });
2815
2811
  break;
2816
2812
  }
2817
- logWarn(`[${PLUGIN_NAME}] Expected grouped account removal after auth-token invalidation, but removed ${removedCount}.`);
2818
2813
  }
2819
- // Below the removal threshold (or grouped removal was a no-op): cool the
2814
+ // Below the disable threshold (or the group was already disabled): cool the
2820
2815
  // account's whole refresh-token group down so selection skips it, then
2821
2816
  // rotate to the next healthy account instead of returning the 401.
2822
2817
  const cooledCount = accountManager.markAccountsWithRefreshTokenCoolingDown(account.refreshToken, ACCOUNT_LIMITS.AUTH_FAILURE_COOLDOWN_MS, "auth-failure");
@@ -3127,7 +3122,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
3127
3122
  const authPluginConfig = loadPluginConfig();
3128
3123
  applyUiRuntimeFromConfig(authPluginConfig);
3129
3124
  const authPerProjectAccounts = getPerProjectAccounts(authPluginConfig);
3130
- setStoragePath(authPerProjectAccounts ? process.cwd() : null);
3125
+ setStoragePath(authPerProjectAccounts ? directory : null);
3131
3126
  const accounts = [];
3132
3127
  // Programmatic input, not a menu choice: a headless caller or
3133
3128
  // script passes it to say "never launch a browser". Ignoring it
@@ -3886,7 +3881,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
3886
3881
  const manualBrowserPluginConfig = loadPluginConfig();
3887
3882
  applyUiRuntimeFromConfig(manualBrowserPluginConfig);
3888
3883
  const manualBrowserPerProjectAccounts = getPerProjectAccounts(manualBrowserPluginConfig);
3889
- setStoragePath(manualBrowserPerProjectAccounts ? process.cwd() : null);
3884
+ setStoragePath(manualBrowserPerProjectAccounts ? directory : null);
3890
3885
  const session = await startLoopbackFlow({ openBrowser: false });
3891
3886
  if (session.type === "unavailable") {
3892
3887
  const message = unavailableMessage(session.lifecycle);
@@ -3934,7 +3929,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
3934
3929
  const devicePluginConfig = loadPluginConfig();
3935
3930
  applyUiRuntimeFromConfig(devicePluginConfig);
3936
3931
  const devicePerProjectAccounts = getPerProjectAccounts(devicePluginConfig);
3937
- setStoragePath(devicePerProjectAccounts ? process.cwd() : null);
3932
+ setStoragePath(devicePerProjectAccounts ? directory : null);
3938
3933
  const started = await createDeviceCodeSession();
3939
3934
  if (started.type === "failed") {
3940
3935
  return {
@@ -3972,7 +3967,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
3972
3967
  const manualPluginConfig = loadPluginConfig();
3973
3968
  applyUiRuntimeFromConfig(manualPluginConfig);
3974
3969
  const manualPerProjectAccounts = getPerProjectAccounts(manualPluginConfig);
3975
- setStoragePath(manualPerProjectAccounts ? process.cwd() : null);
3970
+ setStoragePath(manualPerProjectAccounts ? directory : null);
3976
3971
  const { pkce, state, url } = await createAuthorizationFlow();
3977
3972
  return buildManualOAuthFlow(pkce, url, state, false);
3978
3973
  },
@@ -3981,7 +3976,16 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
3981
3976
  },
3982
3977
  tool: createToolRegistry(ctx),
3983
3978
  };
3984
- };
3979
+ }
3980
+ ;
3985
3981
  export const OpenAIAuthPlugin = OpenAIOAuthPlugin;
3986
- export default OpenAIOAuthPlugin;
3982
+ export default {
3983
+ id: "oc-codex-multi-auth",
3984
+ server: OpenAIOAuthPlugin,
3985
+ /** V2 loads the same package through setup instead of the V1 server hook. */
3986
+ async setup(context) {
3987
+ const { setupV2 } = await import("./lib/opencode-v2.js");
3988
+ return setupV2(context, createPluginRuntime);
3989
+ },
3990
+ };
3987
3991
  //# sourceMappingURL=index.js.map