codex-multi-auth 0.1.9 → 1.1.10
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 +6 -9
- package/dist/lib/accounts.d.ts +1 -1
- package/dist/lib/accounts.d.ts.map +1 -1
- package/dist/lib/codex-manager.d.ts.map +1 -1
- package/dist/lib/codex-manager.js +347 -28
- package/dist/lib/codex-manager.js.map +1 -1
- package/dist/lib/schemas.d.ts +7 -0
- package/dist/lib/schemas.d.ts.map +1 -1
- package/dist/lib/schemas.js +1 -1
- package/dist/lib/schemas.js.map +1 -1
- package/dist/lib/storage/migrations.d.ts +2 -2
- package/dist/lib/storage/migrations.d.ts.map +1 -1
- package/dist/lib/storage.d.ts.map +1 -1
- package/dist/lib/storage.js +5 -2
- package/dist/lib/storage.js.map +1 -1
- package/package.json +1 -1
- package/scripts/codex-routing.js +1 -0
package/README.md
CHANGED
|
@@ -50,7 +50,6 @@ Codex CLI-first multi-account OAuth manager for the official `@openai/codex` CLI
|
|
|
50
50
|
### Option A: Standard install
|
|
51
51
|
|
|
52
52
|
```bash
|
|
53
|
-
npm i -g @openai/codex
|
|
54
53
|
npm i -g codex-multi-auth
|
|
55
54
|
```
|
|
56
55
|
|
|
@@ -75,18 +74,16 @@ codex auth status
|
|
|
75
74
|
|
|
76
75
|
### Step-by-step
|
|
77
76
|
|
|
78
|
-
1. Install global
|
|
79
|
-
- `npm i -g @openai/codex`
|
|
77
|
+
1. Install global package:
|
|
80
78
|
- `npm i -g codex-multi-auth`
|
|
81
79
|
2. Run first login flow with `codex auth login`
|
|
82
|
-
3. Validate state with `codex auth
|
|
80
|
+
3. Validate state with `codex auth status` and `codex auth check`
|
|
83
81
|
4. Confirm routing with `codex auth forecast --live`
|
|
84
82
|
|
|
85
83
|
### Verification
|
|
86
84
|
|
|
87
85
|
```bash
|
|
88
86
|
codex auth status
|
|
89
|
-
codex auth list
|
|
90
87
|
codex auth check
|
|
91
88
|
```
|
|
92
89
|
|
|
@@ -98,7 +95,7 @@ codex auth check
|
|
|
98
95
|
|
|
99
96
|
```bash
|
|
100
97
|
codex auth login
|
|
101
|
-
codex auth
|
|
98
|
+
codex auth status
|
|
102
99
|
codex auth check
|
|
103
100
|
codex auth forecast --live
|
|
104
101
|
```
|
|
@@ -278,9 +275,9 @@ codex auth doctor --json
|
|
|
278
275
|
|
|
279
276
|
## Release Notes
|
|
280
277
|
|
|
281
|
-
- Current stable: [docs/releases/
|
|
282
|
-
- Previous stable: [docs/releases/v0.1.
|
|
283
|
-
- Earlier stable: [docs/releases/v0.1.
|
|
278
|
+
- Current stable: [docs/releases/v1.1.10.md](docs/releases/v1.1.10.md)
|
|
279
|
+
- Previous stable: [docs/releases/v0.1.9.md](docs/releases/v0.1.9.md)
|
|
280
|
+
- Earlier stable: [docs/releases/v0.1.8.md](docs/releases/v0.1.8.md)
|
|
284
281
|
- Archived prerelease: [docs/releases/v0.1.0-beta.0.md](docs/releases/v0.1.0-beta.0.md)
|
|
285
282
|
|
|
286
283
|
## License
|
package/dist/lib/accounts.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ export interface ManagedAccount {
|
|
|
19
19
|
expires?: number;
|
|
20
20
|
addedAt: number;
|
|
21
21
|
lastUsed: number;
|
|
22
|
-
lastSwitchReason?: "rate-limit" | "initial" | "rotation";
|
|
22
|
+
lastSwitchReason?: "rate-limit" | "initial" | "rotation" | "best";
|
|
23
23
|
lastRateLimitReason?: RateLimitReason;
|
|
24
24
|
rateLimitResetTimes: RateLimitStateV3;
|
|
25
25
|
coolingDownUntil?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accounts.d.ts","sourceRoot":"","sources":["../../lib/accounts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAE1C,OAAO,EAGN,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EAErB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAKN,KAAK,sBAAsB,EAC3B,MAAM,eAAe,CAAC;AASvB,OAAO,EACN,gBAAgB,EAChB,mBAAmB,EACnB,sBAAsB,EACtB,0BAA0B,EAC1B,8BAA8B,EAC9B,uBAAuB,EACvB,aAAa,EACb,KAAK,kBAAkB,GACvB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACN,oBAAoB,EACpB,WAAW,EACX,mBAAmB,EACnB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,EACtB,cAAc,EACd,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,iBAAiB,GACtB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACN,2BAA2B,EAC3B,qBAAqB,EACrB,KAAK,uBAAuB,GAC5B,MAAM,sBAAsB,CAAC;AAQ9B,OAAO,EAMN,KAAK,eAAe,EACpB,MAAM,2BAA2B,CAAC;AAUnC,MAAM,WAAW,cAAc;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,YAAY,GAAG,SAAS,GAAG,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"accounts.d.ts","sourceRoot":"","sources":["../../lib/accounts.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex-manager.d.ts","sourceRoot":"","sources":["../../lib/codex-manager.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"codex-manager.d.ts","sourceRoot":"","sources":["../../lib/codex-manager.ts"],"names":[],"mappings":"AA01IA,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,OAAO,CAAC,CAmErE;AAED,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAgE7E"}
|
|
@@ -6,7 +6,7 @@ import { createAuthorizationFlow, exchangeAuthorizationCode, parseAuthorizationI
|
|
|
6
6
|
import { startLocalOAuthServer } from "./auth/server.js";
|
|
7
7
|
import { copyTextToClipboard, openBrowserUrl } from "./auth/browser.js";
|
|
8
8
|
import { promptAddAnotherAccount, promptLoginMode } from "./cli.js";
|
|
9
|
-
import { extractAccountEmail, extractAccountId, formatAccountLabel, formatCooldown, formatWaitTime, getAccountIdCandidates, resolveRequestAccountId, sanitizeEmail, selectBestAccountCandidate, } from "./accounts.js";
|
|
9
|
+
import { extractAccountEmail, extractAccountId, formatAccountLabel, formatCooldown, formatWaitTime, getAccountIdCandidates, resolveRequestAccountId, sanitizeEmail, selectBestAccountCandidate, shouldUpdateAccountIdFromToken, } from "./accounts.js";
|
|
10
10
|
import { ACCOUNT_LIMITS } from "./constants.js";
|
|
11
11
|
import { loadDashboardDisplaySettings, DEFAULT_DASHBOARD_DISPLAY_SETTINGS, } from "./dashboard-settings.js";
|
|
12
12
|
import { evaluateForecastAccounts, isHardRefreshFailure, recommendForecastAccount, summarizeForecast, } from "./forecast.js";
|
|
@@ -219,6 +219,7 @@ function printUsage() {
|
|
|
219
219
|
" codex auth list",
|
|
220
220
|
" codex auth status",
|
|
221
221
|
" codex auth switch <index>",
|
|
222
|
+
" codex auth best [--live] [--json] [--model <model>]",
|
|
222
223
|
" codex auth check",
|
|
223
224
|
" codex auth features",
|
|
224
225
|
" codex auth verify-flagged [--dry-run] [--json] [--no-restore]",
|
|
@@ -273,6 +274,7 @@ const IMPLEMENTED_FEATURES = [
|
|
|
273
274
|
{ id: 38, name: "Dashboard display customization" },
|
|
274
275
|
{ id: 39, name: "Unified color/theme runtime (v2 UI)" },
|
|
275
276
|
{ id: 40, name: "OAuth browser-first flow with manual callback fallback" },
|
|
277
|
+
{ id: 41, name: "Auto-switch to best account command" },
|
|
276
278
|
];
|
|
277
279
|
function runFeaturesReport() {
|
|
278
280
|
console.log(`Implemented features (${IMPLEMENTED_FEATURES.length})`);
|
|
@@ -719,6 +721,35 @@ function resolveAccountSelection(tokens) {
|
|
|
719
721
|
accountLabel: best.label,
|
|
720
722
|
};
|
|
721
723
|
}
|
|
724
|
+
function resolveStoredAccountIdentity(storedAccountId, storedAccountIdSource, tokenAccountId) {
|
|
725
|
+
const accountId = resolveRequestAccountId(storedAccountId, storedAccountIdSource, tokenAccountId);
|
|
726
|
+
if (!accountId) {
|
|
727
|
+
return {};
|
|
728
|
+
}
|
|
729
|
+
if (!shouldUpdateAccountIdFromToken(storedAccountIdSource, storedAccountId)) {
|
|
730
|
+
return {
|
|
731
|
+
accountId,
|
|
732
|
+
accountIdSource: storedAccountIdSource,
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
return {
|
|
736
|
+
accountId,
|
|
737
|
+
accountIdSource: accountId === tokenAccountId ? "token" : storedAccountIdSource,
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
function applyTokenAccountIdentity(account, tokenAccountId) {
|
|
741
|
+
const nextIdentity = resolveStoredAccountIdentity(account.accountId, account.accountIdSource, tokenAccountId);
|
|
742
|
+
if (!nextIdentity.accountId) {
|
|
743
|
+
return false;
|
|
744
|
+
}
|
|
745
|
+
if (nextIdentity.accountId === account.accountId
|
|
746
|
+
&& nextIdentity.accountIdSource === account.accountIdSource) {
|
|
747
|
+
return false;
|
|
748
|
+
}
|
|
749
|
+
account.accountId = nextIdentity.accountId;
|
|
750
|
+
account.accountIdSource = nextIdentity.accountIdSource;
|
|
751
|
+
return true;
|
|
752
|
+
}
|
|
722
753
|
async function promptManualCallback(state) {
|
|
723
754
|
if (!input.isTTY || !output.isTTY) {
|
|
724
755
|
return null;
|
|
@@ -1313,9 +1344,7 @@ async function runHealthCheck(options = {}) {
|
|
|
1313
1344
|
account.email = nextEmail;
|
|
1314
1345
|
changed = true;
|
|
1315
1346
|
}
|
|
1316
|
-
if (
|
|
1317
|
-
account.accountId = tokenAccountId;
|
|
1318
|
-
account.accountIdSource = "token";
|
|
1347
|
+
if (applyTokenAccountIdentity(account, tokenAccountId)) {
|
|
1319
1348
|
changed = true;
|
|
1320
1349
|
}
|
|
1321
1350
|
if (account.enabled === false) {
|
|
@@ -1413,6 +1442,21 @@ function printForecastUsage() {
|
|
|
1413
1442
|
" --model, -m Probe model for live mode (default: gpt-5-codex)",
|
|
1414
1443
|
].join("\n"));
|
|
1415
1444
|
}
|
|
1445
|
+
function printBestUsage() {
|
|
1446
|
+
console.log([
|
|
1447
|
+
"Usage:",
|
|
1448
|
+
" codex auth best [--live] [--json] [--model <model>]",
|
|
1449
|
+
"",
|
|
1450
|
+
"Options:",
|
|
1451
|
+
" --live, -l Probe live quota headers via Codex backend before switching",
|
|
1452
|
+
" --json, -j Print machine-readable JSON output",
|
|
1453
|
+
" --model, -m Probe model for live mode (default: gpt-5-codex)",
|
|
1454
|
+
"",
|
|
1455
|
+
"Behavior:",
|
|
1456
|
+
" - Chooses the healthiest account using forecast scoring",
|
|
1457
|
+
" - Switches to the recommended account when it is not already active",
|
|
1458
|
+
].join("\n"));
|
|
1459
|
+
}
|
|
1416
1460
|
function printFixUsage() {
|
|
1417
1461
|
console.log([
|
|
1418
1462
|
"Usage:",
|
|
@@ -1486,6 +1530,48 @@ function parseForecastArgs(args) {
|
|
|
1486
1530
|
}
|
|
1487
1531
|
return { ok: true, options };
|
|
1488
1532
|
}
|
|
1533
|
+
function parseBestArgs(args) {
|
|
1534
|
+
const options = {
|
|
1535
|
+
live: false,
|
|
1536
|
+
json: false,
|
|
1537
|
+
model: "gpt-5-codex",
|
|
1538
|
+
modelProvided: false,
|
|
1539
|
+
};
|
|
1540
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
1541
|
+
const arg = args[i];
|
|
1542
|
+
if (!arg)
|
|
1543
|
+
continue;
|
|
1544
|
+
if (arg === "--live" || arg === "-l") {
|
|
1545
|
+
options.live = true;
|
|
1546
|
+
continue;
|
|
1547
|
+
}
|
|
1548
|
+
if (arg === "--json" || arg === "-j") {
|
|
1549
|
+
options.json = true;
|
|
1550
|
+
continue;
|
|
1551
|
+
}
|
|
1552
|
+
if (arg === "--model" || arg === "-m") {
|
|
1553
|
+
const value = args[i + 1];
|
|
1554
|
+
if (!value) {
|
|
1555
|
+
return { ok: false, message: "Missing value for --model" };
|
|
1556
|
+
}
|
|
1557
|
+
options.model = value;
|
|
1558
|
+
options.modelProvided = true;
|
|
1559
|
+
i += 1;
|
|
1560
|
+
continue;
|
|
1561
|
+
}
|
|
1562
|
+
if (arg.startsWith("--model=")) {
|
|
1563
|
+
const value = arg.slice("--model=".length).trim();
|
|
1564
|
+
if (!value) {
|
|
1565
|
+
return { ok: false, message: "Missing value for --model" };
|
|
1566
|
+
}
|
|
1567
|
+
options.model = value;
|
|
1568
|
+
options.modelProvided = true;
|
|
1569
|
+
continue;
|
|
1570
|
+
}
|
|
1571
|
+
return { ok: false, message: `Unknown option: ${arg}` };
|
|
1572
|
+
}
|
|
1573
|
+
return { ok: true, options };
|
|
1574
|
+
}
|
|
1489
1575
|
function parseFixArgs(args) {
|
|
1490
1576
|
const options = {
|
|
1491
1577
|
dryRun: false,
|
|
@@ -2022,7 +2108,8 @@ function findExistingAccountIndexForFlagged(storage, flagged, nextRefreshToken,
|
|
|
2022
2108
|
}
|
|
2023
2109
|
function upsertRecoveredFlaggedAccount(storage, flagged, refreshResult, now) {
|
|
2024
2110
|
const nextEmail = sanitizeEmail(extractAccountEmail(refreshResult.access, refreshResult.idToken)) ?? flagged.email;
|
|
2025
|
-
const
|
|
2111
|
+
const tokenAccountId = extractAccountId(refreshResult.access);
|
|
2112
|
+
const { accountId: nextAccountId, accountIdSource: nextAccountIdSource } = resolveStoredAccountIdentity(flagged.accountId, flagged.accountIdSource, tokenAccountId);
|
|
2026
2113
|
const existingIndex = findExistingAccountIndexForFlagged(storage, flagged, refreshResult.refresh, nextAccountId, nextEmail);
|
|
2027
2114
|
if (existingIndex >= 0) {
|
|
2028
2115
|
const existing = storage.accounts[existingIndex];
|
|
@@ -2046,9 +2133,11 @@ function upsertRecoveredFlaggedAccount(storage, flagged, refreshResult, now) {
|
|
|
2046
2133
|
existing.email = nextEmail;
|
|
2047
2134
|
changed = true;
|
|
2048
2135
|
}
|
|
2049
|
-
if (nextAccountId
|
|
2136
|
+
if (nextAccountId !== undefined &&
|
|
2137
|
+
((nextAccountId !== existing.accountId)
|
|
2138
|
+
|| (nextAccountIdSource !== existing.accountIdSource))) {
|
|
2050
2139
|
existing.accountId = nextAccountId;
|
|
2051
|
-
existing.accountIdSource =
|
|
2140
|
+
existing.accountIdSource = nextAccountIdSource;
|
|
2052
2141
|
changed = true;
|
|
2053
2142
|
}
|
|
2054
2143
|
if (existing.enabled === false) {
|
|
@@ -2078,7 +2167,7 @@ function upsertRecoveredFlaggedAccount(storage, flagged, refreshResult, now) {
|
|
|
2078
2167
|
accessToken: refreshResult.access,
|
|
2079
2168
|
expiresAt: refreshResult.expires,
|
|
2080
2169
|
accountId: nextAccountId,
|
|
2081
|
-
accountIdSource:
|
|
2170
|
+
accountIdSource: nextAccountIdSource,
|
|
2082
2171
|
accountLabel: flagged.accountLabel,
|
|
2083
2172
|
email: nextEmail,
|
|
2084
2173
|
addedAt: flagged.addedAt ?? now,
|
|
@@ -2146,13 +2235,15 @@ async function runVerifyFlagged(args) {
|
|
|
2146
2235
|
const { index: i, flagged, label, result } = check;
|
|
2147
2236
|
if (result.type === "success") {
|
|
2148
2237
|
if (!options.restore) {
|
|
2238
|
+
const tokenAccountId = extractAccountId(result.access);
|
|
2239
|
+
const nextIdentity = resolveStoredAccountIdentity(flagged.accountId, flagged.accountIdSource, tokenAccountId);
|
|
2149
2240
|
const nextFlagged = {
|
|
2150
2241
|
...flagged,
|
|
2151
2242
|
refreshToken: result.refresh,
|
|
2152
2243
|
accessToken: result.access,
|
|
2153
2244
|
expiresAt: result.expires,
|
|
2154
|
-
accountId:
|
|
2155
|
-
accountIdSource:
|
|
2245
|
+
accountId: nextIdentity.accountId,
|
|
2246
|
+
accountIdSource: nextIdentity.accountIdSource,
|
|
2156
2247
|
email: sanitizeEmail(extractAccountEmail(result.access, result.idToken)) ?? flagged.email,
|
|
2157
2248
|
lastUsed: now,
|
|
2158
2249
|
lastError: undefined,
|
|
@@ -2181,13 +2272,15 @@ async function runVerifyFlagged(args) {
|
|
|
2181
2272
|
});
|
|
2182
2273
|
continue;
|
|
2183
2274
|
}
|
|
2275
|
+
const tokenAccountId = extractAccountId(result.access);
|
|
2276
|
+
const nextIdentity = resolveStoredAccountIdentity(flagged.accountId, flagged.accountIdSource, tokenAccountId);
|
|
2184
2277
|
const updatedFlagged = {
|
|
2185
2278
|
...flagged,
|
|
2186
2279
|
refreshToken: result.refresh,
|
|
2187
2280
|
accessToken: result.access,
|
|
2188
2281
|
expiresAt: result.expires,
|
|
2189
|
-
accountId:
|
|
2190
|
-
accountIdSource:
|
|
2282
|
+
accountId: nextIdentity.accountId,
|
|
2283
|
+
accountIdSource: nextIdentity.accountIdSource,
|
|
2191
2284
|
email: sanitizeEmail(extractAccountEmail(result.access, result.idToken)) ?? flagged.email,
|
|
2192
2285
|
lastUsed: now,
|
|
2193
2286
|
lastError: upsertResult.message,
|
|
@@ -3027,10 +3120,7 @@ async function runDoctor(args) {
|
|
|
3027
3120
|
activeAccount.expiresAt = refreshResult.expires;
|
|
3028
3121
|
if (refreshedEmail)
|
|
3029
3122
|
activeAccount.email = refreshedEmail;
|
|
3030
|
-
|
|
3031
|
-
activeAccount.accountId = refreshedAccountId;
|
|
3032
|
-
activeAccount.accountIdSource = "token";
|
|
3033
|
-
}
|
|
3123
|
+
applyTokenAccountIdentity(activeAccount, refreshedAccountId);
|
|
3034
3124
|
syncAccessToken = refreshResult.access;
|
|
3035
3125
|
syncRefreshToken = refreshResult.refresh;
|
|
3036
3126
|
syncExpiresAt = refreshResult.expires;
|
|
@@ -3349,6 +3439,23 @@ async function runSwitch(args) {
|
|
|
3349
3439
|
console.error(`Account ${parsed} not found.`);
|
|
3350
3440
|
return 1;
|
|
3351
3441
|
}
|
|
3442
|
+
const { synced, wasDisabled } = await persistAndSyncSelectedAccount({
|
|
3443
|
+
storage,
|
|
3444
|
+
targetIndex,
|
|
3445
|
+
parsed,
|
|
3446
|
+
switchReason: "rotation",
|
|
3447
|
+
});
|
|
3448
|
+
if (!synced) {
|
|
3449
|
+
console.warn(`Switched account ${parsed} locally, but Codex auth sync did not complete. Multi-auth routing will still use this account.`);
|
|
3450
|
+
}
|
|
3451
|
+
console.log(`Switched to account ${parsed}: ${formatAccountLabel(account, targetIndex)}${wasDisabled ? " (re-enabled)" : ""}`);
|
|
3452
|
+
return 0;
|
|
3453
|
+
}
|
|
3454
|
+
async function persistAndSyncSelectedAccount({ storage, targetIndex, parsed, switchReason, initialSyncIdToken, }) {
|
|
3455
|
+
const account = storage.accounts[targetIndex];
|
|
3456
|
+
if (!account) {
|
|
3457
|
+
throw new Error(`Account ${parsed} not found.`);
|
|
3458
|
+
}
|
|
3352
3459
|
storage.activeIndex = targetIndex;
|
|
3353
3460
|
storage.activeIndexByFamily = storage.activeIndexByFamily ?? {};
|
|
3354
3461
|
for (const family of MODEL_FAMILIES) {
|
|
@@ -3362,7 +3469,7 @@ async function runSwitch(args) {
|
|
|
3362
3469
|
let syncAccessToken = account.accessToken;
|
|
3363
3470
|
let syncRefreshToken = account.refreshToken;
|
|
3364
3471
|
let syncExpiresAt = account.expiresAt;
|
|
3365
|
-
let syncIdToken;
|
|
3472
|
+
let syncIdToken = initialSyncIdToken;
|
|
3366
3473
|
if (!hasUsableAccessToken(account, switchNow)) {
|
|
3367
3474
|
const refreshResult = await queuedRefresh(account.refreshToken);
|
|
3368
3475
|
if (refreshResult.type === "success") {
|
|
@@ -3380,10 +3487,7 @@ async function runSwitch(args) {
|
|
|
3380
3487
|
if (nextEmail && nextEmail !== account.email) {
|
|
3381
3488
|
account.email = nextEmail;
|
|
3382
3489
|
}
|
|
3383
|
-
|
|
3384
|
-
account.accountId = tokenAccountId;
|
|
3385
|
-
account.accountIdSource = "token";
|
|
3386
|
-
}
|
|
3490
|
+
applyTokenAccountIdentity(account, tokenAccountId);
|
|
3387
3491
|
syncAccessToken = refreshResult.access;
|
|
3388
3492
|
syncRefreshToken = refreshResult.refresh;
|
|
3389
3493
|
syncExpiresAt = refreshResult.expires;
|
|
@@ -3394,7 +3498,7 @@ async function runSwitch(args) {
|
|
|
3394
3498
|
}
|
|
3395
3499
|
}
|
|
3396
3500
|
account.lastUsed = switchNow;
|
|
3397
|
-
account.lastSwitchReason =
|
|
3501
|
+
account.lastSwitchReason = switchReason;
|
|
3398
3502
|
await saveAccounts(storage);
|
|
3399
3503
|
const synced = await setCodexCliActiveSelection({
|
|
3400
3504
|
accountId: account.accountId,
|
|
@@ -3404,10 +3508,224 @@ async function runSwitch(args) {
|
|
|
3404
3508
|
expiresAt: syncExpiresAt,
|
|
3405
3509
|
...(syncIdToken ? { idToken: syncIdToken } : {}),
|
|
3406
3510
|
});
|
|
3407
|
-
|
|
3408
|
-
|
|
3511
|
+
return { synced, wasDisabled };
|
|
3512
|
+
}
|
|
3513
|
+
async function runBest(args) {
|
|
3514
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
3515
|
+
printBestUsage();
|
|
3516
|
+
return 0;
|
|
3517
|
+
}
|
|
3518
|
+
const parsedArgs = parseBestArgs(args);
|
|
3519
|
+
if (!parsedArgs.ok) {
|
|
3520
|
+
console.error(parsedArgs.message);
|
|
3521
|
+
printBestUsage();
|
|
3522
|
+
return 1;
|
|
3523
|
+
}
|
|
3524
|
+
const options = parsedArgs.options;
|
|
3525
|
+
if (options.modelProvided && !options.live) {
|
|
3526
|
+
console.error("--model requires --live for codex auth best");
|
|
3527
|
+
printBestUsage();
|
|
3528
|
+
return 1;
|
|
3529
|
+
}
|
|
3530
|
+
setStoragePath(null);
|
|
3531
|
+
const storage = await loadAccounts();
|
|
3532
|
+
if (!storage || storage.accounts.length === 0) {
|
|
3533
|
+
if (options.json) {
|
|
3534
|
+
console.log(JSON.stringify({ error: "No accounts configured." }, null, 2));
|
|
3535
|
+
}
|
|
3536
|
+
else {
|
|
3537
|
+
console.log("No accounts configured.");
|
|
3538
|
+
}
|
|
3539
|
+
return 1;
|
|
3540
|
+
}
|
|
3541
|
+
const now = Date.now();
|
|
3542
|
+
const refreshFailures = new Map();
|
|
3543
|
+
const liveQuotaByIndex = new Map();
|
|
3544
|
+
const probeIdTokenByIndex = new Map();
|
|
3545
|
+
const probeRefreshedIndices = new Set();
|
|
3546
|
+
const probeErrors = [];
|
|
3547
|
+
let changed = false;
|
|
3548
|
+
const printProbeNotes = () => {
|
|
3549
|
+
if (probeErrors.length === 0)
|
|
3550
|
+
return;
|
|
3551
|
+
console.log(`Live check notes (${probeErrors.length}):`);
|
|
3552
|
+
for (const error of probeErrors) {
|
|
3553
|
+
console.log(` - ${error}`);
|
|
3554
|
+
}
|
|
3555
|
+
};
|
|
3556
|
+
const persistProbeChangesIfNeeded = async () => {
|
|
3557
|
+
if (!changed)
|
|
3558
|
+
return;
|
|
3559
|
+
await saveAccounts(storage);
|
|
3560
|
+
changed = false;
|
|
3561
|
+
};
|
|
3562
|
+
for (let i = 0; i < storage.accounts.length; i += 1) {
|
|
3563
|
+
const account = storage.accounts[i];
|
|
3564
|
+
if (!account || !options.live)
|
|
3565
|
+
continue;
|
|
3566
|
+
if (account.enabled === false)
|
|
3567
|
+
continue;
|
|
3568
|
+
let probeAccessToken = account.accessToken;
|
|
3569
|
+
let probeAccountId = account.accountId ?? extractAccountId(account.accessToken);
|
|
3570
|
+
if (!hasUsableAccessToken(account, now)) {
|
|
3571
|
+
const refreshResult = await queuedRefresh(account.refreshToken);
|
|
3572
|
+
if (refreshResult.type !== "success") {
|
|
3573
|
+
refreshFailures.set(i, {
|
|
3574
|
+
...refreshResult,
|
|
3575
|
+
message: normalizeFailureDetail(refreshResult.message, refreshResult.reason),
|
|
3576
|
+
});
|
|
3577
|
+
continue;
|
|
3578
|
+
}
|
|
3579
|
+
const refreshedEmail = sanitizeEmail(extractAccountEmail(refreshResult.access, refreshResult.idToken));
|
|
3580
|
+
const refreshedAccountId = extractAccountId(refreshResult.access);
|
|
3581
|
+
if (account.refreshToken !== refreshResult.refresh) {
|
|
3582
|
+
account.refreshToken = refreshResult.refresh;
|
|
3583
|
+
changed = true;
|
|
3584
|
+
}
|
|
3585
|
+
if (account.accessToken !== refreshResult.access) {
|
|
3586
|
+
account.accessToken = refreshResult.access;
|
|
3587
|
+
changed = true;
|
|
3588
|
+
}
|
|
3589
|
+
if (account.expiresAt !== refreshResult.expires) {
|
|
3590
|
+
account.expiresAt = refreshResult.expires;
|
|
3591
|
+
changed = true;
|
|
3592
|
+
}
|
|
3593
|
+
if (refreshedEmail && refreshedEmail !== account.email) {
|
|
3594
|
+
account.email = refreshedEmail;
|
|
3595
|
+
changed = true;
|
|
3596
|
+
}
|
|
3597
|
+
if (refreshedAccountId && refreshedAccountId !== account.accountId) {
|
|
3598
|
+
account.accountId = refreshedAccountId;
|
|
3599
|
+
account.accountIdSource = "token";
|
|
3600
|
+
changed = true;
|
|
3601
|
+
}
|
|
3602
|
+
if (refreshResult.idToken) {
|
|
3603
|
+
probeIdTokenByIndex.set(i, refreshResult.idToken);
|
|
3604
|
+
}
|
|
3605
|
+
probeRefreshedIndices.add(i);
|
|
3606
|
+
probeAccessToken = account.accessToken;
|
|
3607
|
+
probeAccountId = account.accountId ?? refreshedAccountId;
|
|
3608
|
+
}
|
|
3609
|
+
if (!probeAccessToken || !probeAccountId) {
|
|
3610
|
+
probeErrors.push(`${formatAccountLabel(account, i)}: missing accountId for live probe`);
|
|
3611
|
+
continue;
|
|
3612
|
+
}
|
|
3613
|
+
try {
|
|
3614
|
+
const liveQuota = await fetchCodexQuotaSnapshot({
|
|
3615
|
+
accountId: probeAccountId,
|
|
3616
|
+
accessToken: probeAccessToken,
|
|
3617
|
+
model: options.model,
|
|
3618
|
+
});
|
|
3619
|
+
liveQuotaByIndex.set(i, liveQuota);
|
|
3620
|
+
}
|
|
3621
|
+
catch (error) {
|
|
3622
|
+
const message = normalizeFailureDetail(error instanceof Error ? error.message : String(error), undefined);
|
|
3623
|
+
probeErrors.push(`${formatAccountLabel(account, i)}: ${message}`);
|
|
3624
|
+
}
|
|
3625
|
+
}
|
|
3626
|
+
const forecastInputs = storage.accounts.map((account, index) => ({
|
|
3627
|
+
index,
|
|
3628
|
+
account,
|
|
3629
|
+
isCurrent: index === resolveActiveIndex(storage, "codex"),
|
|
3630
|
+
now,
|
|
3631
|
+
refreshFailure: refreshFailures.get(index),
|
|
3632
|
+
liveQuota: liveQuotaByIndex.get(index),
|
|
3633
|
+
}));
|
|
3634
|
+
const forecastResults = evaluateForecastAccounts(forecastInputs);
|
|
3635
|
+
const recommendation = recommendForecastAccount(forecastResults);
|
|
3636
|
+
if (recommendation.recommendedIndex === null) {
|
|
3637
|
+
await persistProbeChangesIfNeeded();
|
|
3638
|
+
if (options.json) {
|
|
3639
|
+
console.log(JSON.stringify({
|
|
3640
|
+
error: recommendation.reason,
|
|
3641
|
+
...(probeErrors.length > 0 ? { probeErrors } : {}),
|
|
3642
|
+
}, null, 2));
|
|
3643
|
+
}
|
|
3644
|
+
else {
|
|
3645
|
+
console.log(`No best account available: ${recommendation.reason}`);
|
|
3646
|
+
printProbeNotes();
|
|
3647
|
+
}
|
|
3648
|
+
return 1;
|
|
3649
|
+
}
|
|
3650
|
+
const bestIndex = recommendation.recommendedIndex;
|
|
3651
|
+
const bestAccount = storage.accounts[bestIndex];
|
|
3652
|
+
if (!bestAccount) {
|
|
3653
|
+
await persistProbeChangesIfNeeded();
|
|
3654
|
+
if (options.json) {
|
|
3655
|
+
console.log(JSON.stringify({ error: "Best account not found." }, null, 2));
|
|
3656
|
+
}
|
|
3657
|
+
else {
|
|
3658
|
+
console.log("Best account not found.");
|
|
3659
|
+
}
|
|
3660
|
+
return 1;
|
|
3661
|
+
}
|
|
3662
|
+
// Check if already on best account
|
|
3663
|
+
const currentIndex = resolveActiveIndex(storage, "codex");
|
|
3664
|
+
if (currentIndex === bestIndex) {
|
|
3665
|
+
const shouldSyncCurrentBest = probeRefreshedIndices.has(bestIndex) || probeIdTokenByIndex.has(bestIndex);
|
|
3666
|
+
let alreadyBestSynced;
|
|
3667
|
+
if (changed) {
|
|
3668
|
+
bestAccount.lastUsed = now;
|
|
3669
|
+
await persistProbeChangesIfNeeded();
|
|
3670
|
+
}
|
|
3671
|
+
if (shouldSyncCurrentBest) {
|
|
3672
|
+
alreadyBestSynced = await setCodexCliActiveSelection({
|
|
3673
|
+
accountId: bestAccount.accountId,
|
|
3674
|
+
email: bestAccount.email,
|
|
3675
|
+
accessToken: bestAccount.accessToken,
|
|
3676
|
+
refreshToken: bestAccount.refreshToken,
|
|
3677
|
+
expiresAt: bestAccount.expiresAt,
|
|
3678
|
+
...(probeIdTokenByIndex.has(bestIndex)
|
|
3679
|
+
? { idToken: probeIdTokenByIndex.get(bestIndex) }
|
|
3680
|
+
: {}),
|
|
3681
|
+
});
|
|
3682
|
+
if (!alreadyBestSynced && !options.json) {
|
|
3683
|
+
console.warn("Codex auth sync did not complete. Multi-auth routing will still use this account.");
|
|
3684
|
+
}
|
|
3685
|
+
}
|
|
3686
|
+
if (options.json) {
|
|
3687
|
+
console.log(JSON.stringify({
|
|
3688
|
+
message: `Already on best account: ${formatAccountLabel(bestAccount, bestIndex)}`,
|
|
3689
|
+
accountIndex: bestIndex + 1,
|
|
3690
|
+
reason: recommendation.reason,
|
|
3691
|
+
...(alreadyBestSynced !== undefined ? { synced: alreadyBestSynced } : {}),
|
|
3692
|
+
...(probeErrors.length > 0 ? { probeErrors } : {}),
|
|
3693
|
+
}, null, 2));
|
|
3694
|
+
}
|
|
3695
|
+
else {
|
|
3696
|
+
console.log(`Already on best account ${bestIndex + 1}: ${formatAccountLabel(bestAccount, bestIndex)}`);
|
|
3697
|
+
console.log(`Reason: ${recommendation.reason}`);
|
|
3698
|
+
printProbeNotes();
|
|
3699
|
+
}
|
|
3700
|
+
return 0;
|
|
3701
|
+
}
|
|
3702
|
+
const targetIndex = bestIndex;
|
|
3703
|
+
const parsed = targetIndex + 1;
|
|
3704
|
+
const { synced, wasDisabled } = await persistAndSyncSelectedAccount({
|
|
3705
|
+
storage,
|
|
3706
|
+
targetIndex,
|
|
3707
|
+
parsed,
|
|
3708
|
+
switchReason: "best",
|
|
3709
|
+
initialSyncIdToken: probeIdTokenByIndex.get(bestIndex),
|
|
3710
|
+
});
|
|
3711
|
+
if (options.json) {
|
|
3712
|
+
console.log(JSON.stringify({
|
|
3713
|
+
message: `Switched to best account: ${formatAccountLabel(bestAccount, targetIndex)}`,
|
|
3714
|
+
accountIndex: parsed,
|
|
3715
|
+
reason: recommendation.reason,
|
|
3716
|
+
synced,
|
|
3717
|
+
wasDisabled,
|
|
3718
|
+
...(probeErrors.length > 0 ? { probeErrors } : {}),
|
|
3719
|
+
}, null, 2));
|
|
3720
|
+
}
|
|
3721
|
+
else {
|
|
3722
|
+
console.log(`Switched to best account ${parsed}: ${formatAccountLabel(bestAccount, targetIndex)}${wasDisabled ? " (re-enabled)" : ""}`);
|
|
3723
|
+
console.log(`Reason: ${recommendation.reason}`);
|
|
3724
|
+
printProbeNotes();
|
|
3725
|
+
if (!synced) {
|
|
3726
|
+
console.warn("Codex auth sync did not complete. Multi-auth routing will still use this account.");
|
|
3727
|
+
}
|
|
3409
3728
|
}
|
|
3410
|
-
console.log(`Switched to account ${parsed}: ${formatAccountLabel(account, targetIndex)}${wasDisabled ? " (re-enabled)" : ""}`);
|
|
3411
3729
|
return 0;
|
|
3412
3730
|
}
|
|
3413
3731
|
export async function autoSyncActiveAccountToCodex() {
|
|
@@ -3451,9 +3769,7 @@ export async function autoSyncActiveAccountToCodex() {
|
|
|
3451
3769
|
account.email = nextEmail;
|
|
3452
3770
|
changed = true;
|
|
3453
3771
|
}
|
|
3454
|
-
if (
|
|
3455
|
-
account.accountId = tokenAccountId;
|
|
3456
|
-
account.accountIdSource = "token";
|
|
3772
|
+
if (applyTokenAccountIdentity(account, tokenAccountId)) {
|
|
3457
3773
|
changed = true;
|
|
3458
3774
|
}
|
|
3459
3775
|
syncAccessToken = refreshResult.access;
|
|
@@ -3519,6 +3835,9 @@ export async function runCodexMultiAuthCli(rawArgs) {
|
|
|
3519
3835
|
if (command === "forecast") {
|
|
3520
3836
|
return runForecast(rest);
|
|
3521
3837
|
}
|
|
3838
|
+
if (command === "best") {
|
|
3839
|
+
return runBest(rest);
|
|
3840
|
+
}
|
|
3522
3841
|
if (command === "report") {
|
|
3523
3842
|
return runReport(rest);
|
|
3524
3843
|
}
|