codex-multi-auth 1.3.2 → 2.0.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.
- package/README.md +40 -26
- package/config/README.md +4 -4
- package/config/codex-legacy.json +571 -646
- package/config/codex-modern.json +258 -274
- package/config/minimal-codex.json +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +85 -14
- package/dist/index.js.map +1 -1
- package/dist/lib/auto-update-checker.d.ts +23 -1
- package/dist/lib/auto-update-checker.d.ts.map +1 -1
- package/dist/lib/auto-update-checker.js +481 -8
- package/dist/lib/auto-update-checker.js.map +1 -1
- package/dist/lib/cli.d.ts +5 -0
- package/dist/lib/cli.d.ts.map +1 -1
- package/dist/lib/cli.js.map +1 -1
- package/dist/lib/codex-manager/commands/best.js +1 -1
- package/dist/lib/codex-manager/commands/best.js.map +1 -1
- package/dist/lib/codex-manager/commands/forecast.js +3 -3
- package/dist/lib/codex-manager/commands/forecast.js.map +1 -1
- package/dist/lib/codex-manager/commands/report.js +3 -3
- package/dist/lib/codex-manager/commands/report.js.map +1 -1
- package/dist/lib/codex-manager/commands/rotation.d.ts +26 -0
- package/dist/lib/codex-manager/commands/rotation.d.ts.map +1 -0
- package/dist/lib/codex-manager/commands/rotation.js +313 -0
- package/dist/lib/codex-manager/commands/rotation.js.map +1 -0
- package/dist/lib/codex-manager/commands/status.d.ts +6 -0
- package/dist/lib/codex-manager/commands/status.d.ts.map +1 -1
- package/dist/lib/codex-manager/commands/status.js +64 -10
- package/dist/lib/codex-manager/commands/status.js.map +1 -1
- package/dist/lib/codex-manager/forecast-report-commands.js +4 -4
- package/dist/lib/codex-manager/forecast-report-commands.js.map +1 -1
- package/dist/lib/codex-manager/help.d.ts.map +1 -1
- package/dist/lib/codex-manager/help.js +3 -2
- package/dist/lib/codex-manager/help.js.map +1 -1
- package/dist/lib/codex-manager/rate-limit-markers.d.ts +2 -0
- package/dist/lib/codex-manager/rate-limit-markers.d.ts.map +1 -0
- package/dist/lib/codex-manager/rate-limit-markers.js +4 -0
- package/dist/lib/codex-manager/rate-limit-markers.js.map +1 -0
- package/dist/lib/codex-manager/repair-commands.js +3 -3
- package/dist/lib/codex-manager/repair-commands.js.map +1 -1
- package/dist/lib/codex-manager.d.ts.map +1 -1
- package/dist/lib/codex-manager.js +115 -87
- package/dist/lib/codex-manager.js.map +1 -1
- package/dist/lib/config.d.ts +1 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +9 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/constants.d.ts +2 -0
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +2 -0
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/fs-retry.d.ts +7 -0
- package/dist/lib/fs-retry.d.ts.map +1 -0
- package/dist/lib/fs-retry.js +35 -0
- package/dist/lib/fs-retry.js.map +1 -0
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +1 -0
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/logger.js +1 -0
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/prompts/codex.d.ts +1 -1
- package/dist/lib/prompts/codex.d.ts.map +1 -1
- package/dist/lib/prompts/codex.js +3 -3
- package/dist/lib/prompts/codex.js.map +1 -1
- package/dist/lib/quota-probe.js +1 -1
- package/dist/lib/quota-probe.js.map +1 -1
- package/dist/lib/quota-readiness.d.ts +16 -0
- package/dist/lib/quota-readiness.d.ts.map +1 -0
- package/dist/lib/quota-readiness.js +82 -0
- package/dist/lib/quota-readiness.js.map +1 -0
- package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
- package/dist/lib/request/fetch-helpers.js +19 -8
- package/dist/lib/request/fetch-helpers.js.map +1 -1
- package/dist/lib/request/helpers/model-map.d.ts +2 -1
- package/dist/lib/request/helpers/model-map.d.ts.map +1 -1
- package/dist/lib/request/helpers/model-map.js +52 -60
- package/dist/lib/request/helpers/model-map.js.map +1 -1
- package/dist/lib/request/request-transformer.d.ts +2 -2
- package/dist/lib/request/request-transformer.js +4 -3
- package/dist/lib/request/request-transformer.js.map +1 -1
- package/dist/lib/runtime/app-bind.d.ts +74 -0
- package/dist/lib/runtime/app-bind.d.ts.map +1 -0
- package/dist/lib/runtime/app-bind.js +620 -0
- package/dist/lib/runtime/app-bind.js.map +1 -0
- package/dist/lib/runtime/config-toml.d.ts +10 -0
- package/dist/lib/runtime/config-toml.d.ts.map +1 -0
- package/dist/lib/runtime/config-toml.js +128 -0
- package/dist/lib/runtime/config-toml.js.map +1 -0
- package/dist/lib/runtime/login-menu-accounts.d.ts +5 -0
- package/dist/lib/runtime/login-menu-accounts.d.ts.map +1 -1
- package/dist/lib/runtime/login-menu-accounts.js +7 -2
- package/dist/lib/runtime/login-menu-accounts.js.map +1 -1
- package/dist/lib/runtime/quota-probe.js +1 -1
- package/dist/lib/runtime/quota-probe.js.map +1 -1
- package/dist/lib/runtime/runtime-current-account.d.ts +52 -0
- package/dist/lib/runtime/runtime-current-account.d.ts.map +1 -0
- package/dist/lib/runtime/runtime-current-account.js +252 -0
- package/dist/lib/runtime/runtime-current-account.js.map +1 -0
- package/dist/lib/runtime/runtime-observability.d.ts +5 -0
- package/dist/lib/runtime/runtime-observability.d.ts.map +1 -1
- package/dist/lib/runtime/runtime-observability.js +5 -0
- package/dist/lib/runtime/runtime-observability.js.map +1 -1
- package/dist/lib/runtime-constants.d.ts +3 -0
- package/dist/lib/runtime-constants.d.ts.map +1 -0
- package/dist/lib/runtime-constants.js +3 -0
- package/dist/lib/runtime-constants.js.map +1 -0
- package/dist/lib/runtime-rotation-proxy.d.ts +36 -0
- package/dist/lib/runtime-rotation-proxy.d.ts.map +1 -0
- package/dist/lib/runtime-rotation-proxy.js +828 -0
- package/dist/lib/runtime-rotation-proxy.js.map +1 -0
- package/dist/lib/schemas.d.ts +1 -0
- package/dist/lib/schemas.d.ts.map +1 -1
- package/dist/lib/schemas.js +1 -0
- package/dist/lib/schemas.js.map +1 -1
- package/dist/lib/ui/auth-menu.d.ts +6 -1
- package/dist/lib/ui/auth-menu.d.ts.map +1 -1
- package/dist/lib/ui/auth-menu.js +22 -2
- package/dist/lib/ui/auth-menu.js.map +1 -1
- package/package.json +13 -11
- package/scripts/bench-format/codex-host.mjs +142 -73
- package/scripts/bench-format/models.mjs +23 -24
- package/scripts/benchmark-edit-formats.mjs +9 -7
- package/scripts/check-pack-budget-lib.js +49 -6
- package/scripts/codex-app-launcher.js +653 -0
- package/scripts/codex-app-router.js +322 -0
- package/scripts/codex-multi-auth.js +3 -1
- package/scripts/codex-routing.js +6 -0
- package/scripts/codex.js +2428 -445
- package/scripts/postinstall.js +343 -0
|
@@ -19,12 +19,14 @@ import { runDoctor as runRepairDoctor, runFix as runRepairFix, runVerifyFlagged
|
|
|
19
19
|
import { runForecastCommand } from "./codex-manager/commands/forecast.js";
|
|
20
20
|
import { runInitConfigCommand } from "./codex-manager/commands/init-config.js";
|
|
21
21
|
import { runReportCommand } from "./codex-manager/commands/report.js";
|
|
22
|
+
import { runRotationCommand } from "./codex-manager/commands/rotation.js";
|
|
22
23
|
import { runFeaturesCommand, runStatusCommand, } from "./codex-manager/commands/status.js";
|
|
23
24
|
import { loadPersistedRuntimeObservabilitySnapshot } from "./runtime/runtime-observability.js";
|
|
24
25
|
import { runSwitchCommand } from "./codex-manager/commands/switch.js";
|
|
25
26
|
import { parseAuthLoginArgs, printUsage } from "./codex-manager/help.js";
|
|
26
27
|
import { applyUiThemeFromDashboardSettings, configureUnifiedSettings, resolveMenuLayoutMode, } from "./codex-manager/settings-hub.js";
|
|
27
|
-
import { getPluginConfigExplainReport } from "./config.js";
|
|
28
|
+
import { getCodexRuntimeRotationProxy, getPluginConfigExplainReport, loadPluginConfig, savePluginConfig, } from "./config.js";
|
|
29
|
+
import { bindCodexAppRuntimeRotation, getAppBindStatus, unbindCodexAppRuntimeRotation, } from "./runtime/app-bind.js";
|
|
28
30
|
import { ACCOUNT_LIMITS } from "./constants.js";
|
|
29
31
|
import { DEFAULT_DASHBOARD_DISPLAY_SETTINGS, loadDashboardDisplaySettings, } from "./dashboard-settings.js";
|
|
30
32
|
import { evaluateForecastAccounts, recommendForecastAccount, summarizeForecast, } from "./forecast.js";
|
|
@@ -32,7 +34,9 @@ import { createLogger } from "./logger.js";
|
|
|
32
34
|
import { MODEL_FAMILIES } from "./prompts/codex.js";
|
|
33
35
|
import { getModelCapabilities, getModelProfile, resolveNormalizedModel, } from "./request/helpers/model-map.js";
|
|
34
36
|
import { formatRateLimitEntry as formatAccountRateLimitEntry, getRateLimitResetTimeForFamily, resolveActiveIndex, } from "./runtime/account-status.js";
|
|
37
|
+
import { buildQuotaEmailFallbackState, hasSafeQuotaEmailFallback, hasUniqueQuotaAccountId, isQuotaCacheEntryExhausted, normalizeQuotaAccountId, normalizeQuotaEmail, quotaLeftPercentFromUsed, } from "./quota-readiness.js";
|
|
35
38
|
import { loadQuotaCache, saveQuotaCache, } from "./quota-cache.js";
|
|
39
|
+
import { isDisplayCurrentAccount, readAppRuntimeHelperAccountSignal, resolveAccountCurrentMarkers, resolveRuntimeCurrentAccount, } from "./runtime/runtime-current-account.js";
|
|
36
40
|
import { fetchCodexQuotaSnapshot, formatQuotaSnapshotLine, } from "./quota-probe.js";
|
|
37
41
|
import { queuedRefresh } from "./refresh-queue.js";
|
|
38
42
|
import { clearAccounts, findMatchingAccountIndex, formatStorageErrorHint, inspectStorageHealth, getLastAccountsSaveTimestamp, getNamedBackups, getStoragePath, loadAccounts, loadFlaggedAccounts, restoreAccountsFromBackup, StorageError, saveAccounts, setStoragePath, withAccountStorageTransaction, } from "./storage.js";
|
|
@@ -316,70 +320,6 @@ const IMPLEMENTED_FEATURES = [
|
|
|
316
320
|
function formatRateLimitEntry(account, now, family = "codex") {
|
|
317
321
|
return formatAccountRateLimitEntry(account, now, formatWaitTime, family);
|
|
318
322
|
}
|
|
319
|
-
function normalizeQuotaEmail(email) {
|
|
320
|
-
const normalized = sanitizeEmail(email);
|
|
321
|
-
return normalized && normalized.length > 0 ? normalized : null;
|
|
322
|
-
}
|
|
323
|
-
function normalizeQuotaAccountId(accountId) {
|
|
324
|
-
if (typeof accountId !== "string")
|
|
325
|
-
return null;
|
|
326
|
-
const trimmed = accountId.trim();
|
|
327
|
-
return trimmed.length > 0 ? trimmed : null;
|
|
328
|
-
}
|
|
329
|
-
function hasUniqueQuotaAccountId(accounts, account) {
|
|
330
|
-
const accountId = normalizeQuotaAccountId(account.accountId);
|
|
331
|
-
if (!accountId)
|
|
332
|
-
return false;
|
|
333
|
-
let matchCount = 0;
|
|
334
|
-
for (const candidate of accounts) {
|
|
335
|
-
if (normalizeQuotaAccountId(candidate.accountId) !== accountId)
|
|
336
|
-
continue;
|
|
337
|
-
matchCount += 1;
|
|
338
|
-
if (matchCount > 1)
|
|
339
|
-
return false;
|
|
340
|
-
}
|
|
341
|
-
return matchCount === 1;
|
|
342
|
-
}
|
|
343
|
-
function buildQuotaEmailFallbackState(accounts) {
|
|
344
|
-
const stateByEmail = new Map();
|
|
345
|
-
for (const account of accounts) {
|
|
346
|
-
const email = normalizeQuotaEmail(account.email);
|
|
347
|
-
if (!email)
|
|
348
|
-
continue;
|
|
349
|
-
const existing = stateByEmail.get(email);
|
|
350
|
-
if (existing) {
|
|
351
|
-
existing.matchingCount += 1;
|
|
352
|
-
const accountId = normalizeQuotaAccountId(account.accountId);
|
|
353
|
-
if (accountId) {
|
|
354
|
-
existing.distinctAccountIds.add(accountId);
|
|
355
|
-
}
|
|
356
|
-
continue;
|
|
357
|
-
}
|
|
358
|
-
const distinctAccountIds = new Set();
|
|
359
|
-
const accountId = normalizeQuotaAccountId(account.accountId);
|
|
360
|
-
if (accountId) {
|
|
361
|
-
distinctAccountIds.add(accountId);
|
|
362
|
-
}
|
|
363
|
-
stateByEmail.set(email, {
|
|
364
|
-
matchingCount: 1,
|
|
365
|
-
distinctAccountIds,
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
return stateByEmail;
|
|
369
|
-
}
|
|
370
|
-
function hasSafeQuotaEmailFallback(emailFallbackState, account) {
|
|
371
|
-
const email = normalizeQuotaEmail(account.email);
|
|
372
|
-
if (!email)
|
|
373
|
-
return false;
|
|
374
|
-
const state = emailFallbackState.get(email);
|
|
375
|
-
if (!state)
|
|
376
|
-
return false;
|
|
377
|
-
// size > 1 only matters when multiple accounts share the same email but
|
|
378
|
-
// disagree on accountId; a single matching account already implies size <= 1.
|
|
379
|
-
if (state.distinctAccountIds.size > 1)
|
|
380
|
-
return false;
|
|
381
|
-
return state.matchingCount === 1;
|
|
382
|
-
}
|
|
383
323
|
function quotaCacheEntryToSnapshot(entry) {
|
|
384
324
|
return {
|
|
385
325
|
status: entry.status,
|
|
@@ -415,13 +355,7 @@ function formatCompactQuotaPart(windowMinutes, usedPercent) {
|
|
|
415
355
|
const left = quotaLeftPercentFromUsed(usedPercent);
|
|
416
356
|
return `${label} ${left}%`;
|
|
417
357
|
}
|
|
418
|
-
function
|
|
419
|
-
if (typeof usedPercent !== "number" || !Number.isFinite(usedPercent)) {
|
|
420
|
-
return undefined;
|
|
421
|
-
}
|
|
422
|
-
return Math.max(0, Math.min(100, Math.round(100 - usedPercent)));
|
|
423
|
-
}
|
|
424
|
-
function formatCompactQuotaSnapshot(snapshot) {
|
|
358
|
+
function formatCompactQuotaSnapshot(snapshot, now = Date.now()) {
|
|
425
359
|
const parts = [
|
|
426
360
|
formatCompactQuotaPart(snapshot.primary.windowMinutes, snapshot.primary.usedPercent),
|
|
427
361
|
formatCompactQuotaPart(snapshot.secondary.windowMinutes, snapshot.secondary.usedPercent),
|
|
@@ -429,12 +363,15 @@ function formatCompactQuotaSnapshot(snapshot) {
|
|
|
429
363
|
if (snapshot.status === 429) {
|
|
430
364
|
parts.push("rate-limited");
|
|
431
365
|
}
|
|
366
|
+
if (isQuotaCacheEntryExhausted(snapshot, now)) {
|
|
367
|
+
parts.push("quota-exhausted");
|
|
368
|
+
}
|
|
432
369
|
if (parts.length > 0) {
|
|
433
370
|
return parts.join(" | ");
|
|
434
371
|
}
|
|
435
372
|
return formatQuotaSnapshotLine(snapshot);
|
|
436
373
|
}
|
|
437
|
-
function formatAccountQuotaSummary(entry) {
|
|
374
|
+
function formatAccountQuotaSummary(entry, now = Date.now()) {
|
|
438
375
|
const parts = [
|
|
439
376
|
formatCompactQuotaPart(entry.primary.windowMinutes, entry.primary.usedPercent),
|
|
440
377
|
formatCompactQuotaPart(entry.secondary.windowMinutes, entry.secondary.usedPercent),
|
|
@@ -442,6 +379,9 @@ function formatAccountQuotaSummary(entry) {
|
|
|
442
379
|
if (entry.status === 429) {
|
|
443
380
|
parts.push("rate-limited");
|
|
444
381
|
}
|
|
382
|
+
if (isQuotaCacheEntryExhausted(entry, now)) {
|
|
383
|
+
parts.push("quota-exhausted");
|
|
384
|
+
}
|
|
445
385
|
if (parts.length > 0) {
|
|
446
386
|
return parts.join(" | ");
|
|
447
387
|
}
|
|
@@ -479,7 +419,7 @@ function getPersistedQuotaViewForAccount(cache, account, accounts, now, emailFal
|
|
|
479
419
|
return {
|
|
480
420
|
updatedAt: cachedEntry?.updatedAt ?? now,
|
|
481
421
|
status: 429,
|
|
482
|
-
model: cachedEntry?.model ?? "gpt-5-codex",
|
|
422
|
+
model: cachedEntry?.model ?? "gpt-5.3-codex",
|
|
483
423
|
planType: cachedEntry?.planType,
|
|
484
424
|
primary: {
|
|
485
425
|
...cachedEntry?.primary,
|
|
@@ -547,7 +487,7 @@ function pruneUnsafeQuotaEmailCacheEntry(cache, email, accounts, emailFallbackSt
|
|
|
547
487
|
return true;
|
|
548
488
|
}
|
|
549
489
|
const DEFAULT_MENU_QUOTA_REFRESH_TTL_MS = 5 * 60_000;
|
|
550
|
-
const MENU_QUOTA_REFRESH_MODEL = "gpt-5-codex";
|
|
490
|
+
const MENU_QUOTA_REFRESH_MODEL = "gpt-5.3-codex";
|
|
551
491
|
function resolveMenuQuotaProbeInput(account, cache, maxAgeMs, now, accounts, emailFallbackState = buildQuotaEmailFallbackState(accounts)) {
|
|
552
492
|
if (account.enabled === false)
|
|
553
493
|
return null;
|
|
@@ -649,19 +589,22 @@ function hasLikelyInvalidRefreshToken(refreshToken) {
|
|
|
649
589
|
return true;
|
|
650
590
|
return trimmed.startsWith("token-");
|
|
651
591
|
}
|
|
652
|
-
function mapAccountStatus(account,
|
|
592
|
+
function mapAccountStatus(account, isCurrentAccount, now, persistedQuotaEntry) {
|
|
653
593
|
if (account.enabled === false)
|
|
654
594
|
return "disabled";
|
|
655
595
|
if (typeof account.coolingDownUntil === "number" &&
|
|
656
596
|
account.coolingDownUntil > now) {
|
|
657
597
|
return "cooldown";
|
|
658
598
|
}
|
|
659
|
-
if (
|
|
599
|
+
if (persistedQuotaEntry && isQuotaCacheEntryExhausted(persistedQuotaEntry, now)) {
|
|
600
|
+
return "quota-exhausted";
|
|
601
|
+
}
|
|
602
|
+
if (persistedQuotaEntry?.status === 429)
|
|
660
603
|
return "rate-limited";
|
|
661
604
|
const rateLimit = formatRateLimitEntry(account, now, "codex");
|
|
662
605
|
if (rateLimit)
|
|
663
606
|
return "rate-limited";
|
|
664
|
-
if (
|
|
607
|
+
if (isCurrentAccount)
|
|
665
608
|
return "active";
|
|
666
609
|
return "ok";
|
|
667
610
|
}
|
|
@@ -693,6 +636,7 @@ function accountStatusSortBucket(status) {
|
|
|
693
636
|
return 0;
|
|
694
637
|
case "unknown":
|
|
695
638
|
return 1;
|
|
639
|
+
case "quota-exhausted":
|
|
696
640
|
case "cooldown":
|
|
697
641
|
case "rate-limited":
|
|
698
642
|
return 2;
|
|
@@ -708,7 +652,7 @@ function accountReadinessSortBucket(account) {
|
|
|
708
652
|
const statusBucket = accountStatusSortBucket(account.status);
|
|
709
653
|
if (statusBucket >= 2)
|
|
710
654
|
return statusBucket;
|
|
711
|
-
return account.quotaRateLimited ? 2 : statusBucket;
|
|
655
|
+
return account.quotaRateLimited || account.quotaExhausted ? 2 : statusBucket;
|
|
712
656
|
}
|
|
713
657
|
function compareReadyFirstAccounts(left, right) {
|
|
714
658
|
const bucketDelta = accountReadinessSortBucket(left) - accountReadinessSortBucket(right);
|
|
@@ -763,13 +707,16 @@ function applyAccountMenuOrdering(accounts, displaySettings) {
|
|
|
763
707
|
}
|
|
764
708
|
return sorted;
|
|
765
709
|
}
|
|
766
|
-
function toExistingAccountInfo(storage, quotaCache, displaySettings) {
|
|
710
|
+
function toExistingAccountInfo(storage, quotaCache, displaySettings, runtimeCurrent = null) {
|
|
767
711
|
const now = Date.now();
|
|
768
712
|
const activeIndex = resolveActiveIndex(storage, "codex");
|
|
769
713
|
const layoutMode = resolveMenuLayoutMode(displaySettings);
|
|
770
714
|
const emailFallbackState = buildQuotaEmailFallbackState(storage.accounts);
|
|
771
715
|
const baseAccounts = storage.accounts.map((account, index) => {
|
|
772
716
|
const entry = getPersistedQuotaViewForAccount(quotaCache, account, storage.accounts, now, emailFallbackState);
|
|
717
|
+
const currentMarkers = resolveAccountCurrentMarkers(index, activeIndex, runtimeCurrent);
|
|
718
|
+
const isCurrentAccount = isDisplayCurrentAccount(index, activeIndex, runtimeCurrent);
|
|
719
|
+
const quotaExhausted = entry ? isQuotaCacheEntryExhausted(entry, now) : false;
|
|
773
720
|
return {
|
|
774
721
|
index,
|
|
775
722
|
sourceIndex: index,
|
|
@@ -778,16 +725,20 @@ function toExistingAccountInfo(storage, quotaCache, displaySettings) {
|
|
|
778
725
|
email: account.email,
|
|
779
726
|
addedAt: account.addedAt,
|
|
780
727
|
lastUsed: account.lastUsed,
|
|
781
|
-
status: mapAccountStatus(account,
|
|
728
|
+
status: mapAccountStatus(account, isCurrentAccount, now, entry),
|
|
782
729
|
quotaSummary: (displaySettings.menuShowQuotaSummary ?? true) && entry
|
|
783
|
-
? formatAccountQuotaSummary(entry)
|
|
730
|
+
? formatAccountQuotaSummary(entry, now)
|
|
784
731
|
: undefined,
|
|
785
732
|
quota5hLeftPercent: quotaLeftPercentFromUsed(entry?.primary.usedPercent),
|
|
786
733
|
quota5hResetAtMs: entry?.primary.resetAtMs,
|
|
787
734
|
quota7dLeftPercent: quotaLeftPercentFromUsed(entry?.secondary.usedPercent),
|
|
788
735
|
quota7dResetAtMs: entry?.secondary.resetAtMs,
|
|
789
736
|
quotaRateLimited: entry?.status === 429,
|
|
790
|
-
|
|
737
|
+
quotaExhausted,
|
|
738
|
+
isCurrentAccount,
|
|
739
|
+
isDefaultAccount: index === activeIndex,
|
|
740
|
+
isRuntimeCurrentAccount: runtimeCurrent?.index === index,
|
|
741
|
+
currentMarkers,
|
|
791
742
|
enabled: account.enabled !== false,
|
|
792
743
|
showStatusBadge: displaySettings.menuShowStatusBadge ?? true,
|
|
793
744
|
showCurrentBadge: displaySettings.menuShowCurrentBadge ?? true,
|
|
@@ -813,6 +764,60 @@ function toExistingAccountInfo(storage, quotaCache, displaySettings) {
|
|
|
813
764
|
: (account.sourceIndex ?? displayIndex) + 1,
|
|
814
765
|
}));
|
|
815
766
|
}
|
|
767
|
+
async function loadRuntimeCurrentSelectionForStorage(storage, now = Date.now()) {
|
|
768
|
+
const [runtimeSnapshot, appBindStatus] = await Promise.all([
|
|
769
|
+
loadPersistedRuntimeObservabilitySnapshot().catch(() => null),
|
|
770
|
+
getAppBindStatus()
|
|
771
|
+
.then((status) => (status.running ? status.router : null))
|
|
772
|
+
.catch(() => null),
|
|
773
|
+
]);
|
|
774
|
+
return resolveRuntimeCurrentAccount(storage, {
|
|
775
|
+
runtimeSnapshot,
|
|
776
|
+
appBindStatus,
|
|
777
|
+
appHelperStatus: readAppRuntimeHelperAccountSignal(),
|
|
778
|
+
}, { now });
|
|
779
|
+
}
|
|
780
|
+
function activeAccountMatchesCodexCliState(account, state) {
|
|
781
|
+
if (!state)
|
|
782
|
+
return true;
|
|
783
|
+
const accountId = account.accountId?.trim();
|
|
784
|
+
const activeAccountId = state.activeAccountId?.trim();
|
|
785
|
+
if (accountId && activeAccountId) {
|
|
786
|
+
return accountId === activeAccountId;
|
|
787
|
+
}
|
|
788
|
+
const email = sanitizeEmail(account.email);
|
|
789
|
+
const activeEmail = sanitizeEmail(state.activeEmail);
|
|
790
|
+
if (email && activeEmail) {
|
|
791
|
+
return email === activeEmail;
|
|
792
|
+
}
|
|
793
|
+
return false;
|
|
794
|
+
}
|
|
795
|
+
async function syncCodexCliActiveSelectionIfDrifted(storage) {
|
|
796
|
+
const activeIndex = resolveActiveIndex(storage, "codex");
|
|
797
|
+
if (activeIndex < 0 || activeIndex >= storage.accounts.length) {
|
|
798
|
+
return false;
|
|
799
|
+
}
|
|
800
|
+
const account = storage.accounts[activeIndex];
|
|
801
|
+
if (!account) {
|
|
802
|
+
return false;
|
|
803
|
+
}
|
|
804
|
+
try {
|
|
805
|
+
const cliState = await loadCodexCliState({ forceRefresh: true });
|
|
806
|
+
if (!cliState || activeAccountMatchesCodexCliState(account, cliState)) {
|
|
807
|
+
return false;
|
|
808
|
+
}
|
|
809
|
+
return setCodexCliActiveSelection({
|
|
810
|
+
accountId: account.accountId,
|
|
811
|
+
email: account.email,
|
|
812
|
+
accessToken: account.accessToken,
|
|
813
|
+
refreshToken: account.refreshToken,
|
|
814
|
+
expiresAt: account.expiresAt,
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
catch {
|
|
818
|
+
return false;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
816
821
|
function resolveAccountSelection(tokens) {
|
|
817
822
|
const override = (process.env.CODEX_AUTH_ACCOUNT_ID ?? "").trim();
|
|
818
823
|
if (override) {
|
|
@@ -1529,7 +1534,7 @@ async function syncSelectionToCodex(tokens) {
|
|
|
1529
1534
|
async function runHealthCheck(options = {}) {
|
|
1530
1535
|
const forceRefresh = options.forceRefresh === true;
|
|
1531
1536
|
const liveProbe = options.liveProbe === true;
|
|
1532
|
-
const probeModel = options.model?.trim() || "gpt-5-codex";
|
|
1537
|
+
const probeModel = options.model?.trim() || "gpt-5.3-codex";
|
|
1533
1538
|
const modelInspection = inspectRequestedModel(probeModel);
|
|
1534
1539
|
const display = options.display ?? DEFAULT_DASHBOARD_DISPLAY_SETTINGS;
|
|
1535
1540
|
const quotaCache = liveProbe ? await loadQuotaCache() : null;
|
|
@@ -1745,7 +1750,7 @@ function printBestUsage() {
|
|
|
1745
1750
|
"Options:",
|
|
1746
1751
|
" --live, -l Probe live quota headers via Codex backend before switching",
|
|
1747
1752
|
" --json, -j Print machine-readable JSON output",
|
|
1748
|
-
" --model, -m Probe model for live mode (default: gpt-5-codex)",
|
|
1753
|
+
" --model, -m Probe model for live mode (default: gpt-5.3-codex)",
|
|
1749
1754
|
"",
|
|
1750
1755
|
"Behavior:",
|
|
1751
1756
|
" - Chooses the healthiest account using forecast scoring",
|
|
@@ -1756,7 +1761,7 @@ function parseBestArgs(args) {
|
|
|
1756
1761
|
const options = {
|
|
1757
1762
|
live: false,
|
|
1758
1763
|
json: false,
|
|
1759
|
-
model: "gpt-5-codex",
|
|
1764
|
+
model: "gpt-5.3-codex",
|
|
1760
1765
|
modelProvided: false,
|
|
1761
1766
|
};
|
|
1762
1767
|
for (let i = 0; i < args.length; i += 1) {
|
|
@@ -2057,7 +2062,9 @@ async function runAuthLogin(args) {
|
|
|
2057
2062
|
}
|
|
2058
2063
|
}
|
|
2059
2064
|
const flaggedStorage = await loadFlaggedAccounts();
|
|
2060
|
-
|
|
2065
|
+
await syncCodexCliActiveSelectionIfDrifted(currentStorage);
|
|
2066
|
+
const runtimeCurrent = await loadRuntimeCurrentSelectionForStorage(currentStorage);
|
|
2067
|
+
const menuResult = await promptLoginMode(toExistingAccountInfo(currentStorage, quotaCache, displaySettings, runtimeCurrent), {
|
|
2061
2068
|
flaggedCount: flaggedStorage.accounts.length,
|
|
2062
2069
|
statusMessage: showFetchStatus
|
|
2063
2070
|
? () => menuQuotaRefreshStatus
|
|
@@ -2532,6 +2539,11 @@ export async function runCodexMultiAuthCli(rawArgs) {
|
|
|
2532
2539
|
resolveActiveIndex,
|
|
2533
2540
|
formatRateLimitEntry,
|
|
2534
2541
|
loadRuntimeObservabilitySnapshot: loadPersistedRuntimeObservabilitySnapshot,
|
|
2542
|
+
loadAppBindStatus: async () => getAppBindStatus()
|
|
2543
|
+
.then((status) => (status.running ? status.router : null))
|
|
2544
|
+
.catch(() => null),
|
|
2545
|
+
loadAppHelperStatus: readAppRuntimeHelperAccountSignal,
|
|
2546
|
+
loadQuotaCache,
|
|
2535
2547
|
});
|
|
2536
2548
|
}
|
|
2537
2549
|
if (command === "switch") {
|
|
@@ -2572,6 +2584,22 @@ export async function runCodexMultiAuthCli(rawArgs) {
|
|
|
2572
2584
|
loadRuntimeObservabilitySnapshot: loadPersistedRuntimeObservabilitySnapshot,
|
|
2573
2585
|
});
|
|
2574
2586
|
}
|
|
2587
|
+
if (command === "rotation") {
|
|
2588
|
+
return runRotationCommand(rest, {
|
|
2589
|
+
loadPluginConfig,
|
|
2590
|
+
savePluginConfig,
|
|
2591
|
+
getCodexRuntimeRotationProxy,
|
|
2592
|
+
setStoragePath,
|
|
2593
|
+
getStoragePath,
|
|
2594
|
+
loadAccounts,
|
|
2595
|
+
resolveActiveIndex,
|
|
2596
|
+
bindCodexApp: bindCodexAppRuntimeRotation,
|
|
2597
|
+
unbindCodexApp: unbindCodexAppRuntimeRotation,
|
|
2598
|
+
getCodexAppBindStatus: getAppBindStatus,
|
|
2599
|
+
loadRuntimeObservabilitySnapshot: loadPersistedRuntimeObservabilitySnapshot,
|
|
2600
|
+
loadQuotaCache,
|
|
2601
|
+
});
|
|
2602
|
+
}
|
|
2575
2603
|
if (command === "why-selected") {
|
|
2576
2604
|
return runWhySelectedCommand(rest, {
|
|
2577
2605
|
parseWhySelectedArgs,
|