codex-multi-auth 2.22.0 → 2.24.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 (117) hide show
  1. package/.codex-plugin/plugin.json +9 -9
  2. package/LICENSE +21 -21
  3. package/README.md +159 -150
  4. package/assets/codex-multi-auth-icon.svg +39 -39
  5. package/assets/readme-hero.svg +31 -31
  6. package/config/README.md +31 -31
  7. package/config/codex-legacy.json +1021 -1021
  8. package/config/codex-modern.json +347 -347
  9. package/config/minimal-codex.json +15 -15
  10. package/config/schema/config.schema.json +8 -0
  11. package/dist/index.js +33 -10
  12. package/dist/lib/account-policy.d.ts +2 -0
  13. package/dist/lib/account-policy.js +3 -1
  14. package/dist/lib/accounts/rate-limits.d.ts +6 -0
  15. package/dist/lib/accounts/rate-limits.js +14 -1
  16. package/dist/lib/accounts.d.ts +6 -1
  17. package/dist/lib/accounts.js +33 -8
  18. package/dist/lib/api-route-store.d.ts +1 -0
  19. package/dist/lib/api-route-store.js +2 -1
  20. package/dist/lib/auth/account-access.js +2 -1
  21. package/dist/lib/auth/auth.js +3 -2
  22. package/dist/lib/auth/device-auth.js +2 -1
  23. package/dist/lib/auth/http.d.ts +1 -0
  24. package/dist/lib/auth/http.js +1 -0
  25. package/dist/lib/auth/token-utils.d.ts +5 -0
  26. package/dist/lib/auth/token-utils.js +10 -0
  27. package/dist/lib/codex-manager/account-manager-commands.js +1 -0
  28. package/dist/lib/codex-manager/commands/account.js +15 -0
  29. package/dist/lib/codex-manager/commands/locale.d.ts +12 -0
  30. package/dist/lib/codex-manager/commands/locale.js +25 -0
  31. package/dist/lib/codex-manager/commands/resets.js +18 -5
  32. package/dist/lib/codex-manager/commands/status.d.ts +4 -0
  33. package/dist/lib/codex-manager/commands/status.js +476 -5
  34. package/dist/lib/codex-manager/commands/workspace.d.ts +4 -2
  35. package/dist/lib/codex-manager/commands/workspace.js +33 -2
  36. package/dist/lib/codex-manager/help.js +5 -3
  37. package/dist/lib/codex-manager.js +112 -30
  38. package/dist/lib/config.js +3 -0
  39. package/dist/lib/constants.d.ts +2 -0
  40. package/dist/lib/constants.js +2 -0
  41. package/dist/lib/context-budget-response.js +8 -8
  42. package/dist/lib/context-overflow.js +10 -10
  43. package/dist/lib/locale.d.ts +7 -0
  44. package/dist/lib/locale.js +35 -0
  45. package/dist/lib/oauth-success.html +125 -125
  46. package/dist/lib/policy/runtime-policy-cache.js +1 -1
  47. package/dist/lib/policy/runtime-policy.d.ts +1 -0
  48. package/dist/lib/policy/runtime-policy.js +4 -0
  49. package/dist/lib/preemptive-quota-scheduler.js +12 -3
  50. package/dist/lib/prompts/codex-host-bridge.js +138 -138
  51. package/dist/lib/prompts/codex.js +79 -79
  52. package/dist/lib/quota-cache.d.ts +4 -0
  53. package/dist/lib/quota-probe.js +2 -1
  54. package/dist/lib/quota-readiness.d.ts +1 -1
  55. package/dist/lib/quota-readiness.js +9 -2
  56. package/dist/lib/request/proxy-fetch.d.ts +5 -0
  57. package/dist/lib/request/proxy-fetch.js +37 -0
  58. package/dist/lib/request/rate-limit-decision.js +1 -1
  59. package/dist/lib/request/url-rewriting.js +17 -0
  60. package/dist/lib/runtime/account-model-catalog.d.ts +2 -0
  61. package/dist/lib/runtime/account-model-catalog.js +7 -2
  62. package/dist/lib/runtime/account-reset-credits.d.ts +11 -2
  63. package/dist/lib/runtime/account-reset-credits.js +35 -12
  64. package/dist/lib/runtime/app-bind.d.ts +4 -1
  65. package/dist/lib/runtime/app-bind.js +63 -3
  66. package/dist/lib/runtime/automatic-account-checks.js +6 -5
  67. package/dist/lib/runtime/automatic-subscription-checks.js +4 -3
  68. package/dist/lib/runtime/model-discovery-status.js +2 -2
  69. package/dist/lib/runtime/reset-credit-routing.d.ts +11 -3
  70. package/dist/lib/runtime/reset-credit-routing.js +66 -21
  71. package/dist/lib/runtime/reset-credits.d.ts +31 -7
  72. package/dist/lib/runtime/reset-credits.js +135 -28
  73. package/dist/lib/runtime/responses-websocket.d.ts +30 -0
  74. package/dist/lib/runtime/responses-websocket.js +243 -29
  75. package/dist/lib/runtime/rotation-account-selection.d.ts +5 -4
  76. package/dist/lib/runtime/rotation-proxy-state.d.ts +3 -0
  77. package/dist/lib/runtime/rotation-server-types.d.ts +4 -1
  78. package/dist/lib/runtime/subscription-quota-order.d.ts +1 -0
  79. package/dist/lib/runtime/subscription-quota-order.js +7 -3
  80. package/dist/lib/runtime/workspace-model-scopes.d.ts +2 -0
  81. package/dist/lib/runtime/workspace-model-scopes.js +14 -4
  82. package/dist/lib/runtime-rotation-proxy.js +138 -38
  83. package/dist/lib/schemas.d.ts +5 -0
  84. package/dist/lib/schemas.js +1 -0
  85. package/dist/lib/storage/snapshot-merge.js +15 -4
  86. package/dist/lib/web-dashboard-html.d.ts +1 -0
  87. package/dist/lib/web-dashboard-html.js +66 -0
  88. package/dist/lib/web-dashboard.d.ts +36 -0
  89. package/dist/lib/web-dashboard.js +322 -0
  90. package/node_modules/@codex-ai/plugin/dist/index.d.ts +2 -2
  91. package/node_modules/@codex-ai/plugin/dist/index.js +2 -2
  92. package/node_modules/@codex-ai/plugin/dist/tool.d.ts +42 -42
  93. package/node_modules/@codex-ai/plugin/dist/tool.js +29 -29
  94. package/node_modules/@codex-ai/plugin/package.json +9 -9
  95. package/node_modules/@codex-ai/sdk/dist/index.d.ts +4 -4
  96. package/node_modules/@codex-ai/sdk/dist/index.js +2 -2
  97. package/node_modules/@codex-ai/sdk/package.json +8 -8
  98. package/package.json +209 -207
  99. package/scripts/codex-app-launcher.js +653 -653
  100. package/scripts/codex-app-router.js +446 -402
  101. package/scripts/codex-bin-resolver.js +332 -332
  102. package/scripts/codex-multi-auth.js +65 -65
  103. package/scripts/codex-routing.js +63 -62
  104. package/scripts/codex.js +7143 -7006
  105. package/scripts/install-codex-auth-utils.js +213 -213
  106. package/scripts/mcodex.js +367 -367
  107. package/scripts/postinstall.js +114 -114
  108. package/scripts/preuninstall.js +265 -265
  109. package/skills/codex-auth-setup/SKILL.md +73 -73
  110. package/vendor/codex-ai-plugin/dist/index.d.ts +2 -2
  111. package/vendor/codex-ai-plugin/dist/index.js +2 -2
  112. package/vendor/codex-ai-plugin/dist/tool.d.ts +42 -42
  113. package/vendor/codex-ai-plugin/dist/tool.js +29 -29
  114. package/vendor/codex-ai-plugin/package.json +9 -9
  115. package/vendor/codex-ai-sdk/dist/index.d.ts +4 -4
  116. package/vendor/codex-ai-sdk/dist/index.js +2 -2
  117. package/vendor/codex-ai-sdk/package.json +8 -8
@@ -7,7 +7,8 @@ export function printUsage() {
7
7
  "Start here:",
8
8
  " codex-multi-auth login [--device-auth|--manual|--no-browser] [--org <org_id>] [--preserve-selection] [--account <index|email|account_id>]",
9
9
  " codex-multi-auth login --api (API credentials and visible models)",
10
- " codex-multi-auth status [--json] (list is the same command)",
10
+ " codex-multi-auth status [--json|--details] (list is the same command)",
11
+ " codex-multi-auth web [--port 4317] [--no-open] (local real-time pool dashboard)",
11
12
  " codex-multi-auth check [accounts|resets|capabilities] [--prime] (default: all checks)",
12
13
  " codex-multi-auth limits --json [--refresh] (structured quota windows; refresh is age-gated)",
13
14
  "",
@@ -16,10 +17,10 @@ export function printUsage() {
16
17
  " codex-multi-auth switch <index> (pins the account for runtime routing)",
17
18
  " codex-multi-auth resets list [--refresh] | redeem <account> | auto manual|last-resort",
18
19
  " codex-multi-auth unpin (clears the manual pin set by switch)",
19
- " codex-multi-auth workspace <account> [workspace] (list or switch an account's workspaces)",
20
+ " codex-multi-auth workspace <account> [workspace] (list or select; add enable/disable to change availability)",
20
21
  " codex-multi-auth best [--live] [--json] [--model <model>] (clears any manual pin set by switch)",
21
22
  " codex-multi-auth forecast [--live] [--json] [--explain] [--model <model>] [--no-runtime-overlay]",
22
- " codex-multi-auth account tag|untag|weight|priority|auto-prime|pause|unpause|drain|undrain|note|policy list ...",
23
+ " codex-multi-auth account tag|untag|weight|priority|auto-prime|emergency-only|pause|unpause|drain|undrain|note|policy list ...",
23
24
  "",
24
25
  "Repair:",
25
26
  " codex-multi-auth uninstall [--dry-run] [--json] [--clear-accounts]",
@@ -41,6 +42,7 @@ export function printUsage() {
41
42
  " codex-multi-auth rotation reset-rate-limits [--all | --account <idx>] [--dry-run] [--json]",
42
43
  " codex-multi-auth why-selected [--now|-n | --last|-l] [--json]",
43
44
  " codex-multi-auth history [list|show <id>] [--json] (all local sessions, ignores /resume provider filtering)",
45
+ " codex-multi-auth config locale [auto|en|zh] (language; auto follows the system locale)",
44
46
  "",
45
47
  "Advanced:",
46
48
  " codex-multi-auth report [--live] [--json] [--explain] [--model <model>] [--max-accounts <n>] [--max-probes <n>] [--cached-only] [--out <path>]",
@@ -39,10 +39,13 @@ import { runInitConfigCommand } from "./codex-manager/commands/init-config.js";
39
39
  import { runReportCommand } from "./codex-manager/commands/report.js";
40
40
  import { runRotationCommand } from "./codex-manager/commands/rotation.js";
41
41
  import { runFeaturesCommand, runStatusCommand, } from "./codex-manager/commands/status.js";
42
+ import { runWebCommand } from "./web-dashboard.js";
42
43
  import { loadPersistedRuntimeObservabilitySnapshot } from "./runtime/runtime-observability.js";
43
44
  import { runSwitchCommand } from "./codex-manager/commands/switch.js";
44
45
  import { runHistoryCommand } from "./codex-manager/commands/history.js";
46
+ import { runLocaleCommand } from "./codex-manager/commands/locale.js";
45
47
  import { runUnpinCommand } from "./codex-manager/commands/unpin.js";
48
+ import { resolveOutputLocale } from "./locale.js";
46
49
  import { runWorkspaceCommand } from "./codex-manager/commands/workspace.js";
47
50
  import { runUsageCommand } from "./codex-manager/commands/usage.js";
48
51
  import { refreshQuotaCacheForMenu } from "./codex-manager/login-menu-data.js";
@@ -60,7 +63,7 @@ import { loadQuotaCache, saveQuotaCache } from "./quota-cache.js";
60
63
  import { readAppRuntimeHelperAccountSignal } from "./runtime/runtime-current-account.js";
61
64
  import { fetchCodexQuotaSnapshot, formatQuotaSnapshotLine, } from "./quota-probe.js";
62
65
  import { queuedRefresh } from "./refresh-queue.js";
63
- import { clearAccounts, inspectStorageHealth, getLastAccountsSaveTimestamp, getStoragePath, loadAccounts, loadFlaggedAccounts, saveAccounts, setStoragePath, } from "./storage.js";
66
+ import { clearAccounts, inspectStorageHealth, getLastAccountsSaveTimestamp, getStoragePath, loadAccounts, loadFlaggedAccounts, saveAccounts, setStoragePath, withAccountStorageTransaction, } from "./storage.js";
64
67
  // Formatter implementations moved to lib/codex-manager/formatters/ (audit
65
68
  // roadmap §4.1.1 phase 1). Re-exported here so existing consumers importing
66
69
  // from lib/codex-manager.js keep working unchanged.
@@ -140,6 +143,7 @@ const IMPLEMENTED_FEATURES = [
140
143
  { id: 52, name: "Why-selected and monitor diagnostics" },
141
144
  { id: 53, name: "Config explain, init-config templates, and debug bundle" },
142
145
  { id: 54, name: "mcodex convenience launcher (monitor/tmux/forward)" },
146
+ { id: 55, name: "Local real-time web pool dashboard" },
143
147
  ];
144
148
  async function runForecast(args) {
145
149
  return runForecastCommand(args, {
@@ -252,6 +256,18 @@ export function buildSelectAccountTraced() {
252
256
  blockedReasonByAccount[index] = AUTH_INVALIDATION_MARKER;
253
257
  }
254
258
  }
259
+ // This offline diagnostic cannot establish ordinary-pool exhaustion or
260
+ // perform permitted reset recovery. Leave emergency capacity reserved;
261
+ // keep any more specific policy, authentication, or runtime exclusion.
262
+ for (const account of accountsWithMetrics) {
263
+ if (policy.emergencyAccountIndexes?.has(account.index) &&
264
+ account.isAvailable &&
265
+ !blockedAccountIndexes.has(account.index)) {
266
+ blockedAccountIndexes.add(account.index);
267
+ blockedReasonByAccount[account.index] =
268
+ "emergency-only: reserved until runtime confirms ordinary capacity and permitted resets are exhausted";
269
+ }
270
+ }
255
271
  return selectHybridAccountTraced({
256
272
  accounts: accountsWithMetrics,
257
273
  healthTracker,
@@ -267,31 +283,50 @@ export function buildSelectAccountTraced() {
267
283
  /**
268
284
  * Shared handler for `list` and `status` (aliases of the same view).
269
285
  */
270
- const runListOrStatusCommand = (rest) => runStatusCommand({
271
- loadApiRoutes,
272
- loadResetCreditState,
273
- loadInferenceRequestTimes,
274
- loadModelInventory,
275
- setStoragePath,
276
- getStoragePath,
277
- loadAccounts,
278
- inspectStorageHealth,
279
- resolveActiveIndex,
280
- formatRateLimitEntry,
281
- loadRuntimeObservabilitySnapshot: loadPersistedRuntimeObservabilitySnapshot,
282
- loadAppBindStatus: async () => getAppBindStatus()
283
- .then((status) => status.router ? {
284
- ...status.router,
285
- // Keep historical diagnostics, but never revive a crashed router's
286
- // persisted "running" marker or its account-selection signal.
287
- state: status.running ? status.router.state : "stopped",
288
- nativeOpenai: status.running && status.state?.nativeOpenai === true,
289
- } : null)
290
- .catch(() => null),
291
- loadAppHelperStatus: readAppRuntimeHelperAccountSignal,
292
- loadQuotaCache,
293
- json: rest.includes("--json") || rest.includes("-j"),
294
- });
286
+ function buildStatusCommandDeps(rest, logInfo) {
287
+ return {
288
+ loadApiRoutes,
289
+ loadResetCreditState,
290
+ loadInferenceRequestTimes,
291
+ loadModelInventory,
292
+ setStoragePath,
293
+ getStoragePath,
294
+ loadAccounts,
295
+ inspectStorageHealth,
296
+ resolveActiveIndex,
297
+ formatRateLimitEntry,
298
+ loadRuntimeObservabilitySnapshot: loadPersistedRuntimeObservabilitySnapshot,
299
+ loadAppBindStatus: async () => getAppBindStatus()
300
+ .then((status) => status.router ? {
301
+ ...status.router,
302
+ // Keep historical diagnostics, but never revive a crashed router's
303
+ // persisted "running" marker or its account-selection signal.
304
+ state: status.running ? status.router.state : "stopped",
305
+ nativeOpenai: status.running && status.state?.nativeOpenai === true,
306
+ } : null)
307
+ .catch(() => null),
308
+ loadAppHelperStatus: readAppRuntimeHelperAccountSignal,
309
+ loadQuotaCache,
310
+ json: rest.includes("--json") || rest.includes("-j"),
311
+ compact: !rest.includes("--details") && !rest.includes("--verbose") && !rest.includes("--json") && !rest.includes("-j"),
312
+ locale: resolveOutputLocale(loadPluginConfig().locale),
313
+ ...(logInfo ? { logInfo } : {}),
314
+ };
315
+ }
316
+ const runListOrStatusCommand = (rest) => runStatusCommand(buildStatusCommandDeps(rest));
317
+ async function loadStatusJsonSnapshot() {
318
+ let serialized = "";
319
+ const exitCode = await runStatusCommand(buildStatusCommandDeps(["--json"], (message) => {
320
+ serialized = message;
321
+ }));
322
+ if (exitCode !== 0)
323
+ throw new Error("Unable to load status snapshot.");
324
+ const parsed = JSON.parse(serialized);
325
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
326
+ throw new Error("Status snapshot is not an object.");
327
+ }
328
+ return parsed;
329
+ }
295
330
  /**
296
331
  * Command registry for the `auth` dispatcher (audit roadmap §4.1.1 phase 2).
297
332
  *
@@ -307,6 +342,10 @@ const CLI_COMMAND_HANDLERS = new Map([
307
342
  ["login", (rest) => runAuthLogin(rest, { runForecast, createRepairCommandDeps })],
308
343
  ["list", runListOrStatusCommand],
309
344
  ["status", runListOrStatusCommand],
345
+ ["web", (rest) => runWebCommand(rest, {
346
+ getStatus: loadStatusJsonSnapshot,
347
+ locale: resolveOutputLocale(loadPluginConfig().locale),
348
+ })],
310
349
  [
311
350
  "limits",
312
351
  (rest) => runLimitsCommand(rest, {
@@ -341,6 +380,7 @@ const CLI_COMMAND_HANDLERS = new Map([
341
380
  setStoragePath,
342
381
  loadAccounts,
343
382
  saveAccounts,
383
+ withAccountStorageTransaction,
344
384
  }),
345
385
  ],
346
386
  ["check", (rest) => runCheckCommand({
@@ -481,6 +521,12 @@ const CLI_COMMAND_HANDLERS = new Map([
481
521
  if (subcommand === "template") {
482
522
  return runInitConfigCommand(configArgs);
483
523
  }
524
+ if (subcommand === "locale") {
525
+ return runLocaleCommand(configArgs, {
526
+ loadConfig: () => loadPluginConfig(),
527
+ saveConfig: savePluginConfig,
528
+ });
529
+ }
484
530
  console.error(`Unknown config command: ${subcommand ?? "(missing)"}`);
485
531
  return 1;
486
532
  },
@@ -506,6 +552,32 @@ const CLI_COMMAND_HANDLERS = new Map([
506
552
  ],
507
553
  ]);
508
554
  export async function runCodexMultiAuthCli(rawArgs) {
555
+ const localeArgs = [...rawArgs];
556
+ const previousLocaleOverride = process.env.CODEX_MULTI_AUTH_LOCALE;
557
+ let localeOverrideChanged = false;
558
+ const restoreLocaleOverride = () => {
559
+ if (!localeOverrideChanged)
560
+ return;
561
+ if (previousLocaleOverride === undefined)
562
+ delete process.env.CODEX_MULTI_AUTH_LOCALE;
563
+ else
564
+ process.env.CODEX_MULTI_AUTH_LOCALE = previousLocaleOverride;
565
+ };
566
+ for (let i = 0; i < localeArgs.length; i += 1) {
567
+ const arg = localeArgs[i];
568
+ if (arg === "--locale" && localeArgs[i + 1]) {
569
+ process.env.CODEX_MULTI_AUTH_LOCALE = localeArgs[i + 1];
570
+ localeOverrideChanged = true;
571
+ localeArgs.splice(i, 2);
572
+ i -= 1;
573
+ }
574
+ else if (typeof arg === "string" && arg.startsWith("--locale=")) {
575
+ process.env.CODEX_MULTI_AUTH_LOCALE = arg.slice("--locale=".length);
576
+ localeOverrideChanged = true;
577
+ localeArgs.splice(i, 1);
578
+ i -= 1;
579
+ }
580
+ }
509
581
  // Lazy install setup (audit roadmap §4.5.4): app detection, Codex app bind,
510
582
  // and launcher routing moved out of npm postinstall to the first CLI run.
511
583
  // ensureFirstRunSetup never throws; the catch is belt-and-braces so no
@@ -515,31 +587,41 @@ export async function runCodexMultiAuthCli(rawArgs) {
515
587
  }).catch(() => undefined);
516
588
  const startupDisplaySettings = await loadDashboardDisplaySettings();
517
589
  applyUiThemeFromDashboardSettings(startupDisplaySettings);
518
- const args = rawArgs[0] && rawArgs[0] !== "auth" && ACCOUNT_MANAGER_COMMANDS.has(rawArgs[0])
519
- ? ["auth", ...rawArgs]
520
- : [...rawArgs];
590
+ const args = localeArgs[0] && localeArgs[0] !== "auth" && ACCOUNT_MANAGER_COMMANDS.has(localeArgs[0])
591
+ ? ["auth", ...localeArgs]
592
+ : [...localeArgs];
521
593
  if (args.length === 0) {
594
+ restoreLocaleOverride();
522
595
  printUsage();
523
596
  return 0;
524
597
  }
525
598
  if (args[0] === "--help" || args[0] === "-h") {
599
+ restoreLocaleOverride();
526
600
  printUsage();
527
601
  return 0;
528
602
  }
529
603
  const [root, sub, ...rest] = args;
530
604
  if (root !== "auth") {
605
+ restoreLocaleOverride();
531
606
  printUsage();
532
607
  return 1;
533
608
  }
534
609
  const command = sub ?? "login";
535
610
  if (command === "--help" || command === "-h") {
611
+ restoreLocaleOverride();
536
612
  printUsage();
537
613
  return 0;
538
614
  }
539
615
  const handler = CLI_COMMAND_HANDLERS.get(command);
540
616
  if (handler) {
541
- return handler(rest);
617
+ try {
618
+ return await handler(rest);
619
+ }
620
+ finally {
621
+ restoreLocaleOverride();
622
+ }
542
623
  }
624
+ restoreLocaleOverride();
543
625
  console.error(`Unknown command: ${command}`);
544
626
  printUsage();
545
627
  return 1;
@@ -1,6 +1,7 @@
1
1
  import { existsSync, promises as fs, readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { parseBooleanEnv } from "./env-parsing.js";
4
+ import { normalizeOutputLocaleSetting } from "./locale.js";
4
5
  import { stripModelEffortSuffix } from "./constants.js";
5
6
  import { withRetry, withRetrySync } from "./fs-retry.js";
6
7
  import { logWarn } from "./logger.js";
@@ -113,6 +114,7 @@ function resolvePluginConfigPath() {
113
114
  * CODEX_MODE is enabled by default for better Codex CLI parity
114
115
  */
115
116
  export const DEFAULT_PLUGIN_CONFIG = {
117
+ locale: "auto",
116
118
  codexMode: true,
117
119
  codexRuntimeRotationProxy: true,
118
120
  codexTuiV2: true,
@@ -1275,6 +1277,7 @@ function normalizeConfigExplainValue(value) {
1275
1277
  return value;
1276
1278
  }
1277
1279
  const CONFIG_EXPLAIN_ENTRIES = [
1280
+ { key: "locale", envNames: ["CODEX_MULTI_AUTH_LOCALE"], getValue: config => normalizeOutputLocaleSetting(process.env.CODEX_MULTI_AUTH_LOCALE?.trim() || config.locale || "auto") ?? "auto" },
1278
1281
  { key: "codexMode", envNames: ["CODEX_MODE"], getValue: getCodexMode },
1279
1282
  {
1280
1283
  key: "codexRuntimeRotationProxy",
@@ -116,3 +116,5 @@ export type WireReasoningEffort = Exclude<ModelReasoningEffort, "ultra">;
116
116
  * untouched.
117
117
  */
118
118
  export declare function stripModelEffortSuffix(modelId: string): string;
119
+ /** Maximum lifetime of a local quota rejection before upstream revalidation. */
120
+ export declare const RATE_LIMIT_CACHE_TTL_MS: number;
@@ -136,3 +136,5 @@ export function stripModelEffortSuffix(modelId) {
136
136
  return modelId;
137
137
  return modelId.replace(EFFORT_SUFFIX_PATTERN, "");
138
138
  }
139
+ /** Maximum lifetime of a local quota rejection before upstream revalidation. */
140
+ export const RATE_LIMIT_CACHE_TTL_MS = 60 * 60_000;
@@ -17,14 +17,14 @@ function buildPauseMessage(advisory) {
17
17
  const windowNote = advisory.windowSource === "override"
18
18
  ? "your configured window size"
19
19
  : "an estimated window size (unverified — set `contextBudgetGuardModelWindowOverrides` if you know the real limit)";
20
- return `[Plugin Notice] Context budget guard paused this session at ${formatPercent(advisory.percent)}% of ${windowNote} for \`${advisory.model}\`, before hitting the model's actual limit.
21
-
22
- Please use one of these commands to reduce context size:
23
-
24
- • **/compact** - Compress conversation history (recommended)
25
- • **/clear** - Start fresh with empty context
26
- • **/undo** - Remove recent messages
27
-
20
+ return `[Plugin Notice] Context budget guard paused this session at ${formatPercent(advisory.percent)}% of ${windowNote} for \`${advisory.model}\`, before hitting the model's actual limit.
21
+
22
+ Please use one of these commands to reduce context size:
23
+
24
+ • **/compact** - Compress conversation history (recommended)
25
+ • **/clear** - Start fresh with empty context
26
+ • **/undo** - Remove recent messages
27
+
28
28
  Then retry your request. This pause clears itself automatically once your context usage drops back below the threshold.`;
29
29
  }
30
30
  /**
@@ -33,16 +33,16 @@ export function isContextOverflowError(status, bodyText) {
33
33
  /**
34
34
  * The message shown to users when context overflow occurs
35
35
  */
36
- const CONTEXT_OVERFLOW_MESSAGE = `[Plugin Notice] Context is too long for this model.
37
-
38
- Please use one of these commands to reduce context size:
39
-
40
- • **/compact** - Compress conversation history (recommended)
41
- • **/clear** - Start fresh with empty context
42
- • **/undo** - Remove recent messages
43
-
44
- Then retry your request.
45
-
36
+ const CONTEXT_OVERFLOW_MESSAGE = `[Plugin Notice] Context is too long for this model.
37
+
38
+ Please use one of these commands to reduce context size:
39
+
40
+ • **/compact** - Compress conversation history (recommended)
41
+ • **/clear** - Start fresh with empty context
42
+ • **/undo** - Remove recent messages
43
+
44
+ Then retry your request.
45
+
46
46
  Alternatively, you can switch to a model with a larger context window.`;
47
47
  /**
48
48
  * Creates a synthetic SSE response for context overflow errors.
@@ -0,0 +1,7 @@
1
+ export type OutputLocale = "en" | "zh";
2
+ export type OutputLocaleSetting = OutputLocale | "auto";
3
+ /** Resolve an explicit locale, environment override, or system language. */
4
+ export declare function resolveOutputLocale(requested?: string | null, env?: NodeJS.ProcessEnv): OutputLocale;
5
+ export declare function normalizeOutputLocaleSetting(value: string): OutputLocaleSetting | null;
6
+ export declare function localized(locale: OutputLocale, en: string, zh: string): string;
7
+ export declare function localeName(locale: OutputLocale): string;
@@ -0,0 +1,35 @@
1
+ const SUPPORTED_LOCALES = new Set(["auto", "en", "zh"]);
2
+ /** Resolve an explicit locale, environment override, or system language. */
3
+ export function resolveOutputLocale(requested, env = process.env) {
4
+ const configured = requested?.trim().toLowerCase();
5
+ const value = (env.CODEX_MULTI_AUTH_LOCALE?.trim() || configured || "auto")
6
+ .trim()
7
+ .toLowerCase();
8
+ if (value === "en" || value.startsWith("en-"))
9
+ return "en";
10
+ if (value === "zh" || value.startsWith("zh-"))
11
+ return "zh";
12
+ if (value !== "" && value !== "auto" && SUPPORTED_LOCALES.has(value)) {
13
+ return value === "zh" ? "zh" : "en";
14
+ }
15
+ const system = [env.LC_ALL, env.LC_MESSAGES, env.LANG]
16
+ .find((candidate) => typeof candidate === "string" && candidate.trim())
17
+ ?.toLowerCase() ?? "";
18
+ return system.startsWith("zh") ? "zh" : "en";
19
+ }
20
+ export function normalizeOutputLocaleSetting(value) {
21
+ const normalized = value.trim().toLowerCase();
22
+ if (normalized === "auto")
23
+ return "auto";
24
+ if (normalized === "en" || normalized.startsWith("en-"))
25
+ return "en";
26
+ if (normalized === "zh" || normalized.startsWith("zh-"))
27
+ return "zh";
28
+ return null;
29
+ }
30
+ export function localized(locale, en, zh) {
31
+ return locale === "zh" ? zh : en;
32
+ }
33
+ export function localeName(locale) {
34
+ return locale === "zh" ? "中文" : "English";
35
+ }
@@ -1,126 +1,126 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <title>Codex Sign-In Complete</title>
7
- <style>
8
- :root {
9
- color-scheme: dark;
10
- --bg: #0b0b0b;
11
- --panel: #111111;
12
- --text: #f2f2f2;
13
- --muted: #a3a3a3;
14
- --line: #232323;
15
- --ok: #7cf0a6;
16
- }
17
-
18
- * {
19
- box-sizing: border-box;
20
- }
21
-
22
- body {
23
- margin: 0;
24
- min-height: 100vh;
25
- display: grid;
26
- place-items: center;
27
- background: var(--bg);
28
- color: var(--text);
29
- font-family: "Geist Mono", "GeistMono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
30
- padding: 24px;
31
- }
32
-
33
- main {
34
- width: min(520px, 100%);
35
- background: var(--panel);
36
- border: 1px solid var(--line);
37
- border-radius: 12px;
38
- padding: 20px;
39
- display: grid;
40
- gap: 14px;
41
- }
42
-
43
- h1 {
44
- margin: 0;
45
- font-size: 20px;
46
- font-weight: 600;
47
- letter-spacing: -0.02em;
48
- }
49
-
50
- p {
51
- margin: 0;
52
- color: var(--muted);
53
- font-size: 14px;
54
- line-height: 1.5;
55
- }
56
-
57
- .status {
58
- display: inline-flex;
59
- align-items: center;
60
- gap: 8px;
61
- color: var(--ok);
62
- font-size: 13px;
63
- }
64
-
65
- .dot {
66
- width: 8px;
67
- height: 8px;
68
- border-radius: 50%;
69
- background: currentColor;
70
- }
71
-
72
- dl {
73
- margin: 0;
74
- display: grid;
75
- grid-template-columns: 120px 1fr;
76
- gap: 8px 12px;
77
- font-size: 13px;
78
- }
79
-
80
- dt {
81
- color: var(--muted);
82
- }
83
-
84
- dd {
85
- margin: 0;
86
- color: var(--text);
87
- word-break: break-word;
88
- }
89
-
90
- code {
91
- background: #171717;
92
- border: 1px solid var(--line);
93
- border-radius: 6px;
94
- padding: 2px 6px;
95
- font-family: inherit;
96
- font-size: 12px;
97
- }
98
-
99
- @media (max-width: 520px) {
100
- dl {
101
- grid-template-columns: 1fr;
102
- }
103
- }
104
- </style>
105
- </head>
106
- <body>
107
- <main role="main" aria-label="OAuth callback received">
108
- <h1>Sign-in complete</h1>
109
- <p>Return to your terminal to finish Codex login.</p>
110
-
111
- <div class="status" aria-live="polite">
112
- <span class="dot" aria-hidden="true"></span>
113
- Callback received
114
- </div>
115
-
116
- <dl>
117
- <dt>Endpoint</dt>
118
- <dd><code>localhost:1455</code></dd>
119
- <dt>Flow</dt>
120
- <dd>OAuth callback captured</dd>
121
- <dt>Next</dt>
122
- <dd>Close this tab and continue in terminal</dd>
123
- </dl>
124
- </main>
125
- </body>
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Codex Sign-In Complete</title>
7
+ <style>
8
+ :root {
9
+ color-scheme: dark;
10
+ --bg: #0b0b0b;
11
+ --panel: #111111;
12
+ --text: #f2f2f2;
13
+ --muted: #a3a3a3;
14
+ --line: #232323;
15
+ --ok: #7cf0a6;
16
+ }
17
+
18
+ * {
19
+ box-sizing: border-box;
20
+ }
21
+
22
+ body {
23
+ margin: 0;
24
+ min-height: 100vh;
25
+ display: grid;
26
+ place-items: center;
27
+ background: var(--bg);
28
+ color: var(--text);
29
+ font-family: "Geist Mono", "GeistMono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
30
+ padding: 24px;
31
+ }
32
+
33
+ main {
34
+ width: min(520px, 100%);
35
+ background: var(--panel);
36
+ border: 1px solid var(--line);
37
+ border-radius: 12px;
38
+ padding: 20px;
39
+ display: grid;
40
+ gap: 14px;
41
+ }
42
+
43
+ h1 {
44
+ margin: 0;
45
+ font-size: 20px;
46
+ font-weight: 600;
47
+ letter-spacing: -0.02em;
48
+ }
49
+
50
+ p {
51
+ margin: 0;
52
+ color: var(--muted);
53
+ font-size: 14px;
54
+ line-height: 1.5;
55
+ }
56
+
57
+ .status {
58
+ display: inline-flex;
59
+ align-items: center;
60
+ gap: 8px;
61
+ color: var(--ok);
62
+ font-size: 13px;
63
+ }
64
+
65
+ .dot {
66
+ width: 8px;
67
+ height: 8px;
68
+ border-radius: 50%;
69
+ background: currentColor;
70
+ }
71
+
72
+ dl {
73
+ margin: 0;
74
+ display: grid;
75
+ grid-template-columns: 120px 1fr;
76
+ gap: 8px 12px;
77
+ font-size: 13px;
78
+ }
79
+
80
+ dt {
81
+ color: var(--muted);
82
+ }
83
+
84
+ dd {
85
+ margin: 0;
86
+ color: var(--text);
87
+ word-break: break-word;
88
+ }
89
+
90
+ code {
91
+ background: #171717;
92
+ border: 1px solid var(--line);
93
+ border-radius: 6px;
94
+ padding: 2px 6px;
95
+ font-family: inherit;
96
+ font-size: 12px;
97
+ }
98
+
99
+ @media (max-width: 520px) {
100
+ dl {
101
+ grid-template-columns: 1fr;
102
+ }
103
+ }
104
+ </style>
105
+ </head>
106
+ <body>
107
+ <main role="main" aria-label="OAuth callback received">
108
+ <h1>Sign-in complete</h1>
109
+ <p>Return to your terminal to finish Codex login.</p>
110
+
111
+ <div class="status" aria-live="polite">
112
+ <span class="dot" aria-hidden="true"></span>
113
+ Callback received
114
+ </div>
115
+
116
+ <dl>
117
+ <dt>Endpoint</dt>
118
+ <dd><code>localhost:1455</code></dd>
119
+ <dt>Flow</dt>
120
+ <dd>OAuth callback captured</dd>
121
+ <dt>Next</dt>
122
+ <dd>Close this tab and continue in terminal</dd>
123
+ </dl>
124
+ </main>
125
+ </body>
126
126
  </html>
@@ -168,7 +168,7 @@ export function loadAccountPolicyStoreCached() {
168
168
  return loadStoreCached({
169
169
  path: getAccountPolicyPath(),
170
170
  cache: ACCOUNT_POLICY_CACHE,
171
- load: loadAccountPolicyStore,
171
+ load: () => loadAccountPolicyStore({ strict: true }),
172
172
  isEmptyStore: (store) => Object.keys(store.accounts).length === 0,
173
173
  });
174
174
  }