codex-multi-auth 2.1.13-beta.2 → 2.2.1

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 (241) hide show
  1. package/.codex-plugin/plugin.json +9 -0
  2. package/README.md +5 -4
  3. package/dist/lib/accounts.d.ts.map +1 -1
  4. package/dist/lib/accounts.js +30 -1
  5. package/dist/lib/accounts.js.map +1 -1
  6. package/dist/lib/auth/device-auth.d.ts.map +1 -1
  7. package/dist/lib/auth/device-auth.js +15 -0
  8. package/dist/lib/auth/device-auth.js.map +1 -1
  9. package/dist/lib/auth/org-override.d.ts +19 -0
  10. package/dist/lib/auth/org-override.d.ts.map +1 -0
  11. package/dist/lib/auth/org-override.js +23 -0
  12. package/dist/lib/auth/org-override.js.map +1 -0
  13. package/dist/lib/auth/server.d.ts.map +1 -1
  14. package/dist/lib/auth/server.js +12 -7
  15. package/dist/lib/auth/server.js.map +1 -1
  16. package/dist/lib/capability-policy.d.ts.map +1 -1
  17. package/dist/lib/capability-policy.js +5 -0
  18. package/dist/lib/capability-policy.js.map +1 -1
  19. package/dist/lib/circuit-breaker.d.ts +6 -0
  20. package/dist/lib/circuit-breaker.d.ts.map +1 -1
  21. package/dist/lib/circuit-breaker.js +8 -0
  22. package/dist/lib/circuit-breaker.js.map +1 -1
  23. package/dist/lib/codex-cli/state.d.ts.map +1 -1
  24. package/dist/lib/codex-cli/state.js +28 -7
  25. package/dist/lib/codex-cli/state.js.map +1 -1
  26. package/dist/lib/codex-manager/account-manager-commands.d.ts +14 -0
  27. package/dist/lib/codex-manager/account-manager-commands.d.ts.map +1 -0
  28. package/dist/lib/codex-manager/account-manager-commands.js +43 -0
  29. package/dist/lib/codex-manager/account-manager-commands.js.map +1 -0
  30. package/dist/lib/codex-manager/commands/best.d.ts +1 -1
  31. package/dist/lib/codex-manager/commands/best.d.ts.map +1 -1
  32. package/dist/lib/codex-manager/commands/best.js +4 -3
  33. package/dist/lib/codex-manager/commands/best.js.map +1 -1
  34. package/dist/lib/codex-manager/commands/budget.d.ts.map +1 -1
  35. package/dist/lib/codex-manager/commands/budget.js +6 -0
  36. package/dist/lib/codex-manager/commands/budget.js.map +1 -1
  37. package/dist/lib/codex-manager/commands/debug-bundle.d.ts +16 -0
  38. package/dist/lib/codex-manager/commands/debug-bundle.d.ts.map +1 -1
  39. package/dist/lib/codex-manager/commands/debug-bundle.js +80 -5
  40. package/dist/lib/codex-manager/commands/debug-bundle.js.map +1 -1
  41. package/dist/lib/codex-manager/commands/forecast.d.ts +1 -1
  42. package/dist/lib/codex-manager/commands/forecast.d.ts.map +1 -1
  43. package/dist/lib/codex-manager/commands/forecast.js +11 -8
  44. package/dist/lib/codex-manager/commands/forecast.js.map +1 -1
  45. package/dist/lib/codex-manager/commands/integrations.js +2 -2
  46. package/dist/lib/codex-manager/commands/integrations.js.map +1 -1
  47. package/dist/lib/codex-manager/commands/models.js +3 -3
  48. package/dist/lib/codex-manager/commands/models.js.map +1 -1
  49. package/dist/lib/codex-manager/commands/report.d.ts.map +1 -1
  50. package/dist/lib/codex-manager/commands/report.js +11 -9
  51. package/dist/lib/codex-manager/commands/report.js.map +1 -1
  52. package/dist/lib/codex-manager/commands/status.d.ts +2 -0
  53. package/dist/lib/codex-manager/commands/status.d.ts.map +1 -1
  54. package/dist/lib/codex-manager/commands/status.js +84 -25
  55. package/dist/lib/codex-manager/commands/status.js.map +1 -1
  56. package/dist/lib/codex-manager/commands/switch.d.ts.map +1 -1
  57. package/dist/lib/codex-manager/commands/switch.js +7 -0
  58. package/dist/lib/codex-manager/commands/switch.js.map +1 -1
  59. package/dist/lib/codex-manager/commands/workspace.d.ts.map +1 -1
  60. package/dist/lib/codex-manager/commands/workspace.js +12 -1
  61. package/dist/lib/codex-manager/commands/workspace.js.map +1 -1
  62. package/dist/lib/codex-manager/experimental-settings-prompt.d.ts.map +1 -1
  63. package/dist/lib/codex-manager/experimental-settings-prompt.js +13 -3
  64. package/dist/lib/codex-manager/experimental-settings-prompt.js.map +1 -1
  65. package/dist/lib/codex-manager/forecast-report-commands.d.ts.map +1 -1
  66. package/dist/lib/codex-manager/forecast-report-commands.js +14 -13
  67. package/dist/lib/codex-manager/forecast-report-commands.js.map +1 -1
  68. package/dist/lib/codex-manager/help.d.ts.map +1 -1
  69. package/dist/lib/codex-manager/help.js +7 -5
  70. package/dist/lib/codex-manager/help.js.map +1 -1
  71. package/dist/lib/codex-manager/repair-commands.d.ts.map +1 -1
  72. package/dist/lib/codex-manager/repair-commands.js +18 -8
  73. package/dist/lib/codex-manager/repair-commands.js.map +1 -1
  74. package/dist/lib/codex-manager/settings-hub/experimental.d.ts.map +1 -1
  75. package/dist/lib/codex-manager/settings-hub/experimental.js +8 -0
  76. package/dist/lib/codex-manager/settings-hub/experimental.js.map +1 -1
  77. package/dist/lib/codex-manager.d.ts +3 -0
  78. package/dist/lib/codex-manager.d.ts.map +1 -1
  79. package/dist/lib/codex-manager.js +121 -93
  80. package/dist/lib/codex-manager.js.map +1 -1
  81. package/dist/lib/config.d.ts.map +1 -1
  82. package/dist/lib/config.js +327 -18
  83. package/dist/lib/config.js.map +1 -1
  84. package/dist/lib/context-overflow.d.ts +7 -2
  85. package/dist/lib/context-overflow.d.ts.map +1 -1
  86. package/dist/lib/context-overflow.js +55 -37
  87. package/dist/lib/context-overflow.js.map +1 -1
  88. package/dist/lib/errors.d.ts +16 -0
  89. package/dist/lib/errors.d.ts.map +1 -1
  90. package/dist/lib/errors.js +23 -0
  91. package/dist/lib/errors.js.map +1 -1
  92. package/dist/lib/forecast.d.ts +1 -0
  93. package/dist/lib/forecast.d.ts.map +1 -1
  94. package/dist/lib/forecast.js +25 -2
  95. package/dist/lib/forecast.js.map +1 -1
  96. package/dist/lib/integration-generators.d.ts.map +1 -1
  97. package/dist/lib/integration-generators.js +1 -1
  98. package/dist/lib/integration-generators.js.map +1 -1
  99. package/dist/lib/local-bridge.d.ts +8 -0
  100. package/dist/lib/local-bridge.d.ts.map +1 -1
  101. package/dist/lib/local-bridge.js +81 -7
  102. package/dist/lib/local-bridge.js.map +1 -1
  103. package/dist/lib/local-client-tokens.d.ts.map +1 -1
  104. package/dist/lib/local-client-tokens.js +117 -64
  105. package/dist/lib/local-client-tokens.js.map +1 -1
  106. package/dist/lib/logger.d.ts +9 -1
  107. package/dist/lib/logger.d.ts.map +1 -1
  108. package/dist/lib/logger.js +104 -19
  109. package/dist/lib/logger.js.map +1 -1
  110. package/dist/lib/model-capability-matrix.d.ts.map +1 -1
  111. package/dist/lib/model-capability-matrix.js +13 -10
  112. package/dist/lib/model-capability-matrix.js.map +1 -1
  113. package/dist/lib/oc-chatgpt-orchestrator.d.ts +7 -1
  114. package/dist/lib/oc-chatgpt-orchestrator.d.ts.map +1 -1
  115. package/dist/lib/oc-chatgpt-orchestrator.js +68 -9
  116. package/dist/lib/oc-chatgpt-orchestrator.js.map +1 -1
  117. package/dist/lib/policy/runtime-policy.d.ts.map +1 -1
  118. package/dist/lib/policy/runtime-policy.js +23 -1
  119. package/dist/lib/policy/runtime-policy.js.map +1 -1
  120. package/dist/lib/prompts/codex.d.ts +1 -1
  121. package/dist/lib/prompts/codex.d.ts.map +1 -1
  122. package/dist/lib/prompts/codex.js +128 -33
  123. package/dist/lib/prompts/codex.js.map +1 -1
  124. package/dist/lib/prompts/fetch-utils.d.ts +72 -0
  125. package/dist/lib/prompts/fetch-utils.d.ts.map +1 -0
  126. package/dist/lib/prompts/fetch-utils.js +165 -0
  127. package/dist/lib/prompts/fetch-utils.js.map +1 -0
  128. package/dist/lib/prompts/host-codex-prompt.d.ts.map +1 -1
  129. package/dist/lib/prompts/host-codex-prompt.js +24 -10
  130. package/dist/lib/prompts/host-codex-prompt.js.map +1 -1
  131. package/dist/lib/quota-cache.d.ts.map +1 -1
  132. package/dist/lib/quota-cache.js +26 -2
  133. package/dist/lib/quota-cache.js.map +1 -1
  134. package/dist/lib/quota-probe.d.ts +21 -0
  135. package/dist/lib/quota-probe.d.ts.map +1 -1
  136. package/dist/lib/quota-probe.js +41 -1
  137. package/dist/lib/quota-probe.js.map +1 -1
  138. package/dist/lib/quota-readiness.d.ts +3 -1
  139. package/dist/lib/quota-readiness.d.ts.map +1 -1
  140. package/dist/lib/quota-readiness.js +16 -3
  141. package/dist/lib/quota-readiness.js.map +1 -1
  142. package/dist/lib/recovery/storage.d.ts +7 -0
  143. package/dist/lib/recovery/storage.d.ts.map +1 -1
  144. package/dist/lib/recovery/storage.js +178 -10
  145. package/dist/lib/recovery/storage.js.map +1 -1
  146. package/dist/lib/refresh-lease.d.ts +1 -1
  147. package/dist/lib/refresh-lease.d.ts.map +1 -1
  148. package/dist/lib/refresh-lease.js +25 -3
  149. package/dist/lib/refresh-lease.js.map +1 -1
  150. package/dist/lib/refresh-queue.d.ts.map +1 -1
  151. package/dist/lib/refresh-queue.js +31 -17
  152. package/dist/lib/refresh-queue.js.map +1 -1
  153. package/dist/lib/request/fetch-helpers.d.ts +0 -5
  154. package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
  155. package/dist/lib/request/fetch-helpers.js +39 -7
  156. package/dist/lib/request/fetch-helpers.js.map +1 -1
  157. package/dist/lib/request/helpers/model-map.d.ts +1 -0
  158. package/dist/lib/request/helpers/model-map.d.ts.map +1 -1
  159. package/dist/lib/request/helpers/model-map.js +10 -0
  160. package/dist/lib/request/helpers/model-map.js.map +1 -1
  161. package/dist/lib/rotation.d.ts +12 -0
  162. package/dist/lib/rotation.d.ts.map +1 -1
  163. package/dist/lib/rotation.js +36 -0
  164. package/dist/lib/rotation.js.map +1 -1
  165. package/dist/lib/routing-mutex.d.ts +12 -0
  166. package/dist/lib/routing-mutex.d.ts.map +1 -1
  167. package/dist/lib/routing-mutex.js +38 -1
  168. package/dist/lib/routing-mutex.js.map +1 -1
  169. package/dist/lib/runtime/account-check-types.d.ts +1 -0
  170. package/dist/lib/runtime/account-check-types.d.ts.map +1 -1
  171. package/dist/lib/runtime/account-check-types.js +1 -0
  172. package/dist/lib/runtime/account-check-types.js.map +1 -1
  173. package/dist/lib/runtime/account-check.d.ts.map +1 -1
  174. package/dist/lib/runtime/account-check.js +15 -4
  175. package/dist/lib/runtime/account-check.js.map +1 -1
  176. package/dist/lib/runtime/quota-probe.d.ts.map +1 -1
  177. package/dist/lib/runtime/quota-probe.js +2 -5
  178. package/dist/lib/runtime/quota-probe.js.map +1 -1
  179. package/dist/lib/runtime/runtime-observability.d.ts.map +1 -1
  180. package/dist/lib/runtime/runtime-observability.js +24 -2
  181. package/dist/lib/runtime/runtime-observability.js.map +1 -1
  182. package/dist/lib/runtime-rotation-proxy.d.ts +24 -1
  183. package/dist/lib/runtime-rotation-proxy.d.ts.map +1 -1
  184. package/dist/lib/runtime-rotation-proxy.js +289 -26
  185. package/dist/lib/runtime-rotation-proxy.js.map +1 -1
  186. package/dist/lib/storage/account-clear.d.ts.map +1 -1
  187. package/dist/lib/storage/account-clear.js +4 -2
  188. package/dist/lib/storage/account-clear.js.map +1 -1
  189. package/dist/lib/storage/account-persistence.d.ts.map +1 -1
  190. package/dist/lib/storage/account-persistence.js +11 -1
  191. package/dist/lib/storage/account-persistence.js.map +1 -1
  192. package/dist/lib/storage/flagged-storage-io.d.ts.map +1 -1
  193. package/dist/lib/storage/flagged-storage-io.js +4 -1
  194. package/dist/lib/storage/flagged-storage-io.js.map +1 -1
  195. package/dist/lib/storage/import-export.d.ts.map +1 -1
  196. package/dist/lib/storage/import-export.js +29 -9
  197. package/dist/lib/storage/import-export.js.map +1 -1
  198. package/dist/lib/storage/paths.d.ts.map +1 -1
  199. package/dist/lib/storage/paths.js +93 -0
  200. package/dist/lib/storage/paths.js.map +1 -1
  201. package/dist/lib/storage/record-utils.d.ts.map +1 -1
  202. package/dist/lib/storage/record-utils.js +6 -1
  203. package/dist/lib/storage/record-utils.js.map +1 -1
  204. package/dist/lib/storage/storage-parser.d.ts.map +1 -1
  205. package/dist/lib/storage/storage-parser.js +7 -1
  206. package/dist/lib/storage/storage-parser.js.map +1 -1
  207. package/dist/lib/storage/transactions.d.ts +8 -0
  208. package/dist/lib/storage/transactions.d.ts.map +1 -1
  209. package/dist/lib/storage/transactions.js +14 -1
  210. package/dist/lib/storage/transactions.js.map +1 -1
  211. package/dist/lib/storage.d.ts.map +1 -1
  212. package/dist/lib/storage.js +59 -18
  213. package/dist/lib/storage.js.map +1 -1
  214. package/dist/lib/table-formatter.d.ts.map +1 -1
  215. package/dist/lib/table-formatter.js +20 -2
  216. package/dist/lib/table-formatter.js.map +1 -1
  217. package/dist/lib/types.d.ts +7 -0
  218. package/dist/lib/types.d.ts.map +1 -1
  219. package/dist/lib/ui/auth-menu.d.ts.map +1 -1
  220. package/dist/lib/ui/auth-menu.js +8 -2
  221. package/dist/lib/ui/auth-menu.js.map +1 -1
  222. package/dist/lib/ui/display-width.d.ts +27 -0
  223. package/dist/lib/ui/display-width.d.ts.map +1 -0
  224. package/dist/lib/ui/display-width.js +194 -0
  225. package/dist/lib/ui/display-width.js.map +1 -0
  226. package/dist/lib/ui/select.d.ts +23 -0
  227. package/dist/lib/ui/select.d.ts.map +1 -1
  228. package/dist/lib/ui/select.js +64 -16
  229. package/dist/lib/ui/select.js.map +1 -1
  230. package/dist/lib/ui/theme.d.ts +13 -0
  231. package/dist/lib/ui/theme.d.ts.map +1 -1
  232. package/dist/lib/ui/theme.js +34 -1
  233. package/dist/lib/ui/theme.js.map +1 -1
  234. package/node_modules/@codex-ai/sdk/dist/index.d.ts +4 -0
  235. package/node_modules/@codex-ai/sdk/dist/index.js +2 -0
  236. package/node_modules/@codex-ai/sdk/package.json +8 -0
  237. package/package.json +10 -6
  238. package/scripts/codex-bin-resolver.js +28 -1
  239. package/scripts/codex-routing.js +3 -0
  240. package/scripts/codex.js +508 -1
  241. package/scripts/mcodex.js +367 -0
@@ -3,6 +3,7 @@ import { createInterface } from "node:readline/promises";
3
3
  import { extractAccountEmail, extractAccountId, formatAccountLabel, formatWaitTime, getAccountIdCandidates, resolveRequestAccountId, sanitizeEmail, selectBestAccountCandidate, shouldUpdateAccountIdFromToken, } from "./accounts.js";
4
4
  import { createAuthorizationFlow, exchangeAuthorizationCode, parseAuthorizationInput, redactOAuthUrlForLog, REDIRECT_URI, } from "./auth/auth.js";
5
5
  import { runDeviceAuthFlow } from "./auth/device-auth.js";
6
+ import { resolveOrgOverride } from "./auth/org-override.js";
6
7
  import { copyTextToClipboard, isBrowserLaunchSuppressed, openBrowserUrl, } from "./auth/browser.js";
7
8
  import { startLocalOAuthServer } from "./auth/server.js";
8
9
  import { promptAddAnotherAccount, promptLoginMode, } from "./cli.js";
@@ -10,6 +11,7 @@ import { loadCodexCliState } from "./codex-cli/state.js";
10
11
  import { setCodexCliActiveSelection } from "./codex-cli/writer.js";
11
12
  import { runBestCommand, } from "./codex-manager/commands/best.js";
12
13
  import { runAccountCommand } from "./codex-manager/commands/account.js";
14
+ import { ACCOUNT_MANAGER_COMMANDS } from "./codex-manager/account-manager-commands.js";
13
15
  import { runBudgetCommand } from "./codex-manager/commands/budget.js";
14
16
  import { runBridgeCommand } from "./codex-manager/commands/bridge.js";
15
17
  import { runCheckCommand } from "./codex-manager/commands/check.js";
@@ -44,12 +46,13 @@ import { DEFAULT_DASHBOARD_DISPLAY_SETTINGS, loadDashboardDisplaySettings, } fro
44
46
  import { evaluateForecastAccounts, recommendForecastAccount, summarizeForecast, } from "./forecast.js";
45
47
  import { createLogger } from "./logger.js";
46
48
  import { MODEL_FAMILIES } from "./prompts/codex.js";
47
- import { getModelCapabilities, getModelProfile, resolveNormalizedModel, } from "./request/helpers/model-map.js";
49
+ import { DEFAULT_MODEL, getModelCapabilities, getModelProfile, resolveNormalizedModel, } from "./request/helpers/model-map.js";
48
50
  import { formatRateLimitEntry as formatAccountRateLimitEntry, getRateLimitResetTimeForFamily, resolveActiveIndex, } from "./runtime/account-status.js";
49
51
  import { buildQuotaEmailFallbackState, hasSafeQuotaEmailFallback, hasUniqueQuotaAccountId, isQuotaCacheEntryExhausted, normalizeQuotaAccountId, normalizeQuotaEmail, quotaLeftPercentFromUsed, } from "./quota-readiness.js";
50
52
  import { loadQuotaCache, saveQuotaCache, } from "./quota-cache.js";
51
53
  import { isDisplayCurrentAccount, readAppRuntimeHelperAccountSignal, resolveAccountCurrentMarkers, resolveRuntimeCurrentAccount, } from "./runtime/runtime-current-account.js";
52
- import { fetchCodexQuotaSnapshot, formatQuotaSnapshotLine, } from "./quota-probe.js";
54
+ import { fetchCodexQuotaSnapshot, formatQuotaSnapshotLine, CODEX_UNAVAILABLE_PROBE_NOTE, } from "./quota-probe.js";
55
+ import { isCodexUnavailableError } from "./errors.js";
53
56
  import { queuedRefresh } from "./refresh-queue.js";
54
57
  import { clearAccounts, findMatchingAccountIndex, formatStorageErrorHint, inspectStorageHealth, getLastAccountsSaveTimestamp, getNamedBackups, getStoragePath, loadAccounts, loadFlaggedAccounts, readAffinityGenerationFromDisk, restoreAccountsFromBackup, StorageError, saveAccounts, setStoragePath, withAccountStorageTransaction, } from "./storage.js";
55
58
  import { ANSI } from "./ui/ansi.js";
@@ -59,36 +62,6 @@ import { paintUiText, quotaToneFromLeftPercent } from "./ui/format.js";
59
62
  import { getUiRuntimeOptions } from "./ui/runtime.js";
60
63
  import { select } from "./ui/select.js";
61
64
  const log = createLogger("codex-manager");
62
- const ACCOUNT_MANAGER_COMMANDS = new Set([
63
- "login",
64
- "list",
65
- "status",
66
- "switch",
67
- "unpin",
68
- "workspace",
69
- "best",
70
- "check",
71
- "features",
72
- "usage",
73
- "verify-flagged",
74
- "verify",
75
- "forecast",
76
- "report",
77
- "fix",
78
- "doctor",
79
- "uninstall",
80
- "account",
81
- "budget",
82
- "bridge",
83
- "integrations",
84
- "models",
85
- "monitor",
86
- "rotation",
87
- "why-selected",
88
- "config",
89
- "init-config",
90
- "debug",
91
- ]);
92
65
  function stylePromptText(text, tone) {
93
66
  if (!output.isTTY)
94
67
  return text;
@@ -245,7 +218,7 @@ function styleQuotaSummary(summary) {
245
218
  return stylePromptText(segment, "muted");
246
219
  }
247
220
  const windowLabel = match[1] ?? "";
248
- const leftPercent = Number.parseInt(match[2] ?? "", 10);
221
+ const leftPercent = Math.max(0, Math.min(100, Number.parseInt(match[2] ?? "", 10)));
249
222
  if (!Number.isFinite(leftPercent)) {
250
223
  return stylePromptText(segment, "muted");
251
224
  }
@@ -254,7 +227,10 @@ function styleQuotaSummary(summary) {
254
227
  });
255
228
  return joinStyledSegments(rendered);
256
229
  }
257
- function styleAccountDetailText(detail, fallbackTone = "muted") {
230
+ // Exported for unit tests: tone precedence (danger before warning) is
231
+ // security/UX-relevant — a failure whose text contains "unavailable" must
232
+ // render red, not be downgraded to yellow. See test/codex-manager-detail-tone.test.ts.
233
+ export function styleAccountDetailText(detail, fallbackTone = "muted") {
258
234
  const compact = collapseWhitespace(detail);
259
235
  if (!compact)
260
236
  return stylePromptText("", fallbackTone);
@@ -263,15 +239,24 @@ function styleAccountDetailText(detail, fallbackTone = "muted") {
263
239
  const prefix = (quotaMatch[1] ?? "").trim();
264
240
  const quota = (quotaMatch[2] ?? "").trim();
265
241
  const suffix = (quotaMatch[3] ?? "").trim();
266
- const prefixTone = /failed|error/i.test(prefix)
242
+ // danger wins across the WHOLE detail: a failure keyword anywhere — even
243
+ // trapped inside the (…%) quota segment, e.g.
244
+ // "signed in and working (live check failed: … 0%)" — must keep the prefix
245
+ // red, never let a "working"/"ok" prefix render green over a real failure.
246
+ const detailHasFailure = /failed|error|rate-limited/i.test(compact);
247
+ const prefixTone = detailHasFailure
267
248
  ? "danger"
268
- : /ok|working|succeeded|valid/i.test(prefix)
249
+ : /\b(ok|working|succeeded|valid)\b/i.test(prefix)
269
250
  ? "success"
270
251
  : fallbackTone;
271
- const suffixTone = /re-login|stale|warning|retry|fallback/i.test(suffix)
272
- ? "warning"
273
- : /failed|error/i.test(suffix)
274
- ? "danger"
252
+ const suffixTone =
253
+ // danger wins first: a real failure whose text happens to contain
254
+ // "unavailable"/"not available" (e.g. a 5xx "service not available")
255
+ // must render red, not be downgraded to a yellow warning.
256
+ /failed|error/i.test(suffix)
257
+ ? "danger"
258
+ : /re-login|stale|warning|retry|fallback|unavailable|not available/i.test(suffix)
259
+ ? "warning"
275
260
  : "muted";
276
261
  const chunks = [];
277
262
  if (prefix)
@@ -283,11 +268,11 @@ function styleAccountDetailText(detail, fallbackTone = "muted") {
283
268
  }
284
269
  if (/rate-limited/i.test(compact))
285
270
  return stylePromptText(compact, "danger");
286
- if (/re-login|stale|warning|fallback/i.test(compact))
287
- return stylePromptText(compact, "warning");
288
271
  if (/failed|error/i.test(compact))
289
272
  return stylePromptText(compact, "danger");
290
- if (/ok|working|succeeded|valid/i.test(compact))
273
+ if (/re-login|stale|warning|fallback|unavailable|not available/i.test(compact))
274
+ return stylePromptText(compact, "warning");
275
+ if (/\b(ok|working|succeeded|valid)\b/i.test(compact))
291
276
  return stylePromptText(compact, "success");
292
277
  return stylePromptText(compact, fallbackTone);
293
278
  }
@@ -461,7 +446,7 @@ function getPersistedQuotaViewForAccount(cache, account, accounts, now, emailFal
461
446
  return {
462
447
  updatedAt: cachedEntry?.updatedAt ?? now,
463
448
  status: 429,
464
- model: cachedEntry?.model ?? "gpt-5.3-codex",
449
+ model: cachedEntry?.model ?? DEFAULT_MODEL,
465
450
  planType: cachedEntry?.planType,
466
451
  primary: {
467
452
  ...cachedEntry?.primary,
@@ -529,7 +514,8 @@ function pruneUnsafeQuotaEmailCacheEntry(cache, email, accounts, emailFallbackSt
529
514
  return true;
530
515
  }
531
516
  const DEFAULT_MENU_QUOTA_REFRESH_TTL_MS = 5 * 60_000;
532
- const MENU_QUOTA_REFRESH_MODEL = "gpt-5.3-codex";
517
+ const DEFAULT_LIVE_PROBE_MODEL = DEFAULT_MODEL;
518
+ const MENU_QUOTA_REFRESH_MODEL = DEFAULT_LIVE_PROBE_MODEL;
533
519
  function resolveMenuQuotaProbeInput(account, cache, maxAgeMs, now, accounts, emailFallbackState = buildQuotaEmailFallbackState(accounts)) {
534
520
  if (account.enabled === false)
535
521
  return null;
@@ -860,8 +846,17 @@ async function syncCodexCliActiveSelectionIfDrifted(storage) {
860
846
  return false;
861
847
  }
862
848
  }
863
- function resolveAccountSelection(tokens) {
864
- const override = (process.env.CODEX_AUTH_ACCOUNT_ID ?? "").trim();
849
+ /**
850
+ * Resolve the account-id selection for freshly-minted tokens.
851
+ *
852
+ * The org-override precedence (explicit `login --org` wins over the ambient
853
+ * CODEX_AUTH_ACCOUNT_ID env, for this call only) lives in the internal
854
+ * lib/auth/org-override.ts module so it can be unit-tested without exporting this
855
+ * CLI-internal function. Threading the org as a parameter avoids mutating
856
+ * process.env for the duration of a login, which raced on concurrent re-entry.
857
+ */
858
+ function resolveAccountSelection(tokens, orgOverride) {
859
+ const override = resolveOrgOverride(orgOverride);
865
860
  if (override) {
866
861
  return {
867
862
  ...tokens,
@@ -1592,7 +1587,7 @@ async function syncSelectionToCodex(tokens) {
1592
1587
  async function runHealthCheck(options = {}) {
1593
1588
  const forceRefresh = options.forceRefresh === true;
1594
1589
  const liveProbe = options.liveProbe === true;
1595
- const probeModel = options.model?.trim() || "gpt-5.3-codex";
1590
+ const probeModel = options.model?.trim() || DEFAULT_LIVE_PROBE_MODEL;
1596
1591
  const modelInspection = inspectRequestedModel(probeModel);
1597
1592
  const display = options.display ?? DEFAULT_DASHBOARD_DISPLAY_SETTINGS;
1598
1593
  const quotaCache = liveProbe ? await loadQuotaCache() : null;
@@ -1611,6 +1606,8 @@ async function runHealthCheck(options = {}) {
1611
1606
  let ok = 0;
1612
1607
  let failed = 0;
1613
1608
  let warnings = 0;
1609
+ let codexAvailable = 0;
1610
+ let signedInOnly = 0;
1614
1611
  const activeIndex = resolveActiveIndex(storage, "codex");
1615
1612
  let activeAccountRefreshed = false;
1616
1613
  const now = Date.now();
@@ -1636,6 +1633,7 @@ async function runHealthCheck(options = {}) {
1636
1633
  activeAccountRefreshed = true;
1637
1634
  }
1638
1635
  let healthDetail = "signed in and working";
1636
+ let healthTone = "success";
1639
1637
  if (liveProbe) {
1640
1638
  const currentAccessToken = account.accessToken;
1641
1639
  const probeAccountId = currentAccessToken
@@ -1643,8 +1641,10 @@ async function runHealthCheck(options = {}) {
1643
1641
  : undefined;
1644
1642
  if (!probeAccountId || !currentAccessToken) {
1645
1643
  warnings += 1;
1644
+ signedInOnly += 1;
1645
+ healthTone = "warning";
1646
1646
  healthDetail =
1647
- "signed in and working (live check skipped: missing account ID)";
1647
+ "signed in (live check skipped: missing account ID)";
1648
1648
  }
1649
1649
  else {
1650
1650
  try {
@@ -1658,11 +1658,20 @@ async function runHealthCheck(options = {}) {
1658
1658
  updateQuotaCacheForAccount(workingQuotaCache, account, snapshot, storage.accounts, quotaEmailFallbackState ?? undefined) || quotaCacheChanged;
1659
1659
  }
1660
1660
  healthDetail = formatQuotaSnapshotForDashboard(snapshot, display);
1661
+ codexAvailable += 1;
1661
1662
  }
1662
1663
  catch (error) {
1663
- const message = normalizeFailureDetail(error instanceof Error ? error.message : String(error), undefined);
1664
1664
  warnings += 1;
1665
- healthDetail = `signed in and working (live check failed: ${message})`;
1665
+ signedInOnly += 1;
1666
+ healthTone = "warning";
1667
+ if (isCodexUnavailableError(error)) {
1668
+ healthDetail =
1669
+ `signed in; ${CODEX_UNAVAILABLE_PROBE_NOTE}`;
1670
+ }
1671
+ else {
1672
+ const message = normalizeFailureDetail(error instanceof Error ? error.message : String(error), undefined);
1673
+ healthDetail = `signed in (live check failed: ${message})`;
1674
+ }
1666
1675
  }
1667
1676
  }
1668
1677
  }
@@ -1671,7 +1680,8 @@ async function runHealthCheck(options = {}) {
1671
1680
  }
1672
1681
  ok += 1;
1673
1682
  if (display.showPerAccountRows) {
1674
- console.log(` ${stylePromptText("✓", "success")} ${labelText} ${stylePromptText("|", "muted")} ${styleAccountDetailText(healthDetail)}`);
1683
+ const healthMarker = healthTone === "success" ? "✓" : "!";
1684
+ console.log(` ${stylePromptText(healthMarker, healthTone)} ${labelText} ${stylePromptText("|", "muted")} ${styleAccountDetailText(healthDetail, healthTone)}`);
1675
1685
  }
1676
1686
  continue;
1677
1687
  }
@@ -1717,12 +1727,15 @@ async function runHealthCheck(options = {}) {
1717
1727
  }
1718
1728
  ok += 1;
1719
1729
  let healthyMessage = "working now";
1730
+ let healthyTone = "success";
1720
1731
  if (liveProbe) {
1721
1732
  const probeAccountId = account.accountId ?? tokenAccountId;
1722
1733
  if (!probeAccountId) {
1723
1734
  warnings += 1;
1735
+ signedInOnly += 1;
1736
+ healthyTone = "warning";
1724
1737
  healthyMessage =
1725
- "working now (live check skipped: missing account ID)";
1738
+ "signed in (live check skipped: missing account ID)";
1726
1739
  }
1727
1740
  else {
1728
1741
  try {
@@ -1736,22 +1749,35 @@ async function runHealthCheck(options = {}) {
1736
1749
  updateQuotaCacheForAccount(workingQuotaCache, account, snapshot, storage.accounts, quotaEmailFallbackState ?? undefined) || quotaCacheChanged;
1737
1750
  }
1738
1751
  healthyMessage = formatQuotaSnapshotForDashboard(snapshot, display);
1752
+ codexAvailable += 1;
1739
1753
  }
1740
1754
  catch (error) {
1741
- const message = normalizeFailureDetail(error instanceof Error ? error.message : String(error), undefined);
1742
1755
  warnings += 1;
1743
- healthyMessage = `working now (live check failed: ${message})`;
1756
+ signedInOnly += 1;
1757
+ healthyTone = "warning";
1758
+ if (isCodexUnavailableError(error)) {
1759
+ healthyMessage =
1760
+ `signed in; ${CODEX_UNAVAILABLE_PROBE_NOTE}`;
1761
+ }
1762
+ else {
1763
+ const message = normalizeFailureDetail(error instanceof Error ? error.message : String(error), undefined);
1764
+ healthyMessage = `signed in (live check failed: ${message})`;
1765
+ }
1744
1766
  }
1745
1767
  }
1746
1768
  }
1747
1769
  if (display.showPerAccountRows) {
1748
- console.log(` ${stylePromptText("✓", "success")} ${labelText} ${stylePromptText("|", "muted")} ${styleAccountDetailText(healthyMessage)}`);
1770
+ const healthyMarker = healthyTone === "success" ? "✓" : "!";
1771
+ console.log(` ${stylePromptText(healthyMarker, healthyTone)} ${labelText} ${stylePromptText("|", "muted")} ${styleAccountDetailText(healthyMessage, healthyTone)}`);
1749
1772
  }
1750
1773
  }
1751
1774
  else {
1752
1775
  const detail = normalizeFailureDetail(result.message, result.reason);
1753
1776
  if (sessionLikelyValid) {
1754
1777
  warnings += 1;
1778
+ if (liveProbe) {
1779
+ signedInOnly += 1;
1780
+ }
1755
1781
  if (display.showPerAccountRows) {
1756
1782
  console.log(` ${stylePromptText("!", "warning")} ${labelText} ${stylePromptText("|", "muted")} ${stylePromptText(`refresh failed (${detail}) but this account still works right now`, "warning")}`);
1757
1783
  }
@@ -1795,17 +1821,32 @@ async function runHealthCheck(options = {}) {
1795
1821
  }
1796
1822
  }
1797
1823
  console.log("");
1798
- console.log(formatResultSummary([
1799
- { text: `${ok} working`, tone: "success" },
1800
- {
1801
- text: `${failed} need re-login`,
1802
- tone: failed > 0 ? "danger" : "muted",
1803
- },
1804
- {
1805
- text: `${warnings} warning${warnings === 1 ? "" : "s"}`,
1806
- tone: warnings > 0 ? "warning" : "muted",
1807
- },
1808
- ]));
1824
+ console.log(formatResultSummary(liveProbe
1825
+ ? [
1826
+ {
1827
+ text: `${codexAvailable} Codex available`,
1828
+ tone: codexAvailable > 0 ? "success" : "muted",
1829
+ },
1830
+ {
1831
+ text: `${signedInOnly} signed in only`,
1832
+ tone: signedInOnly > 0 ? "warning" : "muted",
1833
+ },
1834
+ {
1835
+ text: `${failed} need re-login`,
1836
+ tone: failed > 0 ? "danger" : "muted",
1837
+ },
1838
+ ]
1839
+ : [
1840
+ { text: `${ok} working`, tone: "success" },
1841
+ {
1842
+ text: `${failed} need re-login`,
1843
+ tone: failed > 0 ? "danger" : "muted",
1844
+ },
1845
+ {
1846
+ text: `${warnings} warning${warnings === 1 ? "" : "s"}`,
1847
+ tone: warnings > 0 ? "warning" : "muted",
1848
+ },
1849
+ ]));
1809
1850
  }
1810
1851
  function printBestUsage() {
1811
1852
  console.log([
@@ -1815,7 +1856,7 @@ function printBestUsage() {
1815
1856
  "Options:",
1816
1857
  " --live, -l Probe live quota headers via Codex backend before switching",
1817
1858
  " --json, -j Print machine-readable JSON output",
1818
- " --model, -m Probe model for live mode (default: gpt-5.3-codex)",
1859
+ ` --model, -m Probe model for live mode (default: ${DEFAULT_LIVE_PROBE_MODEL})`,
1819
1860
  "",
1820
1861
  "Behavior:",
1821
1862
  " - Chooses the healthiest account using forecast scoring",
@@ -1826,7 +1867,7 @@ function parseBestArgs(args) {
1826
1867
  const options = {
1827
1868
  live: false,
1828
1869
  json: false,
1829
- model: "gpt-5.3-codex",
1870
+ model: DEFAULT_LIVE_PROBE_MODEL,
1830
1871
  modelProvided: false,
1831
1872
  };
1832
1873
  for (let i = 0; i < args.length; i += 1) {
@@ -1842,8 +1883,8 @@ function parseBestArgs(args) {
1842
1883
  continue;
1843
1884
  }
1844
1885
  if (arg === "--model" || arg === "-m") {
1845
- const value = args[i + 1];
1846
- if (!value) {
1886
+ const value = args[i + 1]?.trim();
1887
+ if (!value || value.startsWith("-")) {
1847
1888
  return { ok: false, message: "Missing value for --model" };
1848
1889
  }
1849
1890
  options.model = value;
@@ -1853,7 +1894,7 @@ function parseBestArgs(args) {
1853
1894
  }
1854
1895
  if (arg.startsWith("--model=")) {
1855
1896
  const value = arg.slice("--model=".length).trim();
1856
- if (!value) {
1897
+ if (!value || value.startsWith("-")) {
1857
1898
  return { ok: false, message: "Missing value for --model" };
1858
1899
  }
1859
1900
  options.model = value;
@@ -2074,27 +2115,13 @@ async function runAuthLogin(args) {
2074
2115
  const loginOptions = parsedArgs.options;
2075
2116
  // `--org <id>` binds this login to a specific workspace/org so the same
2076
2117
  // email's personal vs business/team workspace can be registered on demand
2077
- // (issue #491). It reuses the CODEX_AUTH_ACCOUNT_ID override that every login
2078
- // resolver already honors. Scope it to this invocation and restore the prior
2079
- // value in a finally so a later login in the same process (menu re-entry, a
2080
- // reused test worker) is never silently bound to a stale org.
2081
- if (!loginOptions.org) {
2082
- return runAuthLoginFlow(loginOptions);
2083
- }
2084
- const previousAccountIdOverride = process.env.CODEX_AUTH_ACCOUNT_ID;
2085
- process.env.CODEX_AUTH_ACCOUNT_ID = loginOptions.org;
2086
- console.log(`Binding this login to workspace org id: ${loginOptions.org}`);
2087
- try {
2088
- return await runAuthLoginFlow(loginOptions);
2089
- }
2090
- finally {
2091
- if (previousAccountIdOverride === undefined) {
2092
- delete process.env.CODEX_AUTH_ACCOUNT_ID;
2093
- }
2094
- else {
2095
- process.env.CODEX_AUTH_ACCOUNT_ID = previousAccountIdOverride;
2096
- }
2118
+ // (issue #491). The org is threaded explicitly into resolveAccountSelection
2119
+ // (no process.env mutation), so concurrent re-entry (menu re-entry, a reused
2120
+ // test worker) can never bind a login to a stale org via a shared global.
2121
+ if (loginOptions.org) {
2122
+ console.log(`Binding this login to workspace org id: ${loginOptions.org}`);
2097
2123
  }
2124
+ return runAuthLoginFlow(loginOptions);
2098
2125
  }
2099
2126
  async function runAuthLoginFlow(loginOptions) {
2100
2127
  setStoragePath(null);
@@ -2368,7 +2395,7 @@ async function runAuthLoginFlow(loginOptions) {
2368
2395
  console.error(`Login failed: ${tokenResult.message ?? tokenResult.reason ?? "unknown error"}`);
2369
2396
  return 1;
2370
2397
  }
2371
- const resolved = resolveAccountSelection(tokenResult);
2398
+ const resolved = resolveAccountSelection(tokenResult, loginOptions.org);
2372
2399
  await persistAccountPool([resolved], false);
2373
2400
  await syncSelectionToCodex(resolved);
2374
2401
  const latestStorage = await loadAccounts();
@@ -2688,6 +2715,7 @@ export async function runCodexMultiAuthCli(rawArgs) {
2688
2715
  .catch(() => null),
2689
2716
  loadAppHelperStatus: readAppRuntimeHelperAccountSignal,
2690
2717
  loadQuotaCache,
2718
+ json: rest.includes("--json") || rest.includes("-j"),
2691
2719
  });
2692
2720
  }
2693
2721
  if (command === "switch") {