Skip to content

Commit 89c789b

Browse files
committed
refactor: extract best command
1 parent 85e5f33 commit 89c789b

3 files changed

Lines changed: 472 additions & 279 deletions

File tree

lib/codex-manager.ts

Lines changed: 23 additions & 279 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ import {
3535
loadCodexCliState,
3636
} from "./codex-cli/state.js";
3737
import { setCodexCliActiveSelection } from "./codex-cli/writer.js";
38+
import {
39+
type BestCliOptions,
40+
runBestCommand,
41+
} from "./codex-manager/commands/best.js";
3842
import { runCheckCommand } from "./codex-manager/commands/check.js";
3943
import { runForecastCommand } from "./codex-manager/commands/forecast.js";
4044
import { runReportCommand } from "./codex-manager/commands/report.js";
@@ -2247,13 +2251,6 @@ async function runHealthCheck(options: HealthCheckOptions = {}): Promise<void> {
22472251
);
22482252
}
22492253

2250-
interface BestCliOptions {
2251-
live: boolean;
2252-
json: boolean;
2253-
model: string;
2254-
modelProvided: boolean;
2255-
}
2256-
22572254
interface FixCliOptions {
22582255
dryRun: boolean;
22592256
json: boolean;
@@ -4282,279 +4279,26 @@ async function persistAndSyncSelectedAccount({
42824279
}
42834280

42844281
async function runBest(args: string[]): Promise<number> {
4285-
if (args.includes("--help") || args.includes("-h")) {
4286-
printBestUsage();
4287-
return 0;
4288-
}
4289-
4290-
const parsedArgs = parseBestArgs(args);
4291-
if (!parsedArgs.ok) {
4292-
console.error(parsedArgs.message);
4293-
printBestUsage();
4294-
return 1;
4295-
}
4296-
const options = parsedArgs.options;
4297-
if (options.modelProvided && !options.live) {
4298-
console.error("--model requires --live for codex auth best");
4299-
printBestUsage();
4300-
return 1;
4301-
}
4302-
4303-
setStoragePath(null);
4304-
const storage = await loadAccounts();
4305-
if (!storage || storage.accounts.length === 0) {
4306-
if (options.json) {
4307-
console.log(
4308-
JSON.stringify({ error: "No accounts configured." }, null, 2),
4309-
);
4310-
} else {
4311-
console.log("No accounts configured.");
4312-
}
4313-
return 1;
4314-
}
4315-
4316-
const now = Date.now();
4317-
const refreshFailures = new Map<number, TokenFailure>();
4318-
const liveQuotaByIndex = new Map<
4319-
number,
4320-
Awaited<ReturnType<typeof fetchCodexQuotaSnapshot>>
4321-
>();
4322-
const probeIdTokenByIndex = new Map<number, string>();
4323-
const probeRefreshedIndices = new Set<number>();
4324-
const probeErrors: string[] = [];
4325-
let changed = false;
4326-
4327-
const printProbeNotes = (): void => {
4328-
if (probeErrors.length === 0) return;
4329-
console.log(`Live check notes (${probeErrors.length}):`);
4330-
for (const error of probeErrors) {
4331-
console.log(` - ${error}`);
4332-
}
4333-
};
4334-
4335-
const persistProbeChangesIfNeeded = async (): Promise<void> => {
4336-
if (!changed) return;
4337-
await saveAccounts(storage);
4338-
changed = false;
4339-
};
4340-
4341-
for (let i = 0; i < storage.accounts.length; i += 1) {
4342-
const account = storage.accounts[i];
4343-
if (!account || !options.live) continue;
4344-
if (account.enabled === false) continue;
4345-
4346-
let probeAccessToken = account.accessToken;
4347-
let probeAccountId =
4348-
account.accountId ?? extractAccountId(account.accessToken);
4349-
if (!hasUsableAccessToken(account, now)) {
4350-
const refreshResult = await queuedRefresh(account.refreshToken);
4351-
if (refreshResult.type !== "success") {
4352-
refreshFailures.set(i, {
4353-
...refreshResult,
4354-
message: normalizeFailureDetail(
4355-
refreshResult.message,
4356-
refreshResult.reason,
4357-
),
4358-
});
4359-
continue;
4360-
}
4361-
4362-
const refreshedEmail = sanitizeEmail(
4363-
extractAccountEmail(refreshResult.access, refreshResult.idToken),
4364-
);
4365-
const refreshedAccountId = extractAccountId(refreshResult.access);
4366-
4367-
if (account.refreshToken !== refreshResult.refresh) {
4368-
account.refreshToken = refreshResult.refresh;
4369-
changed = true;
4370-
}
4371-
if (account.accessToken !== refreshResult.access) {
4372-
account.accessToken = refreshResult.access;
4373-
changed = true;
4374-
}
4375-
if (account.expiresAt !== refreshResult.expires) {
4376-
account.expiresAt = refreshResult.expires;
4377-
changed = true;
4378-
}
4379-
if (refreshedEmail && refreshedEmail !== account.email) {
4380-
account.email = refreshedEmail;
4381-
changed = true;
4382-
}
4383-
if (refreshedAccountId && refreshedAccountId !== account.accountId) {
4384-
account.accountId = refreshedAccountId;
4385-
account.accountIdSource = "token";
4386-
changed = true;
4387-
}
4388-
if (refreshResult.idToken) {
4389-
probeIdTokenByIndex.set(i, refreshResult.idToken);
4390-
}
4391-
probeRefreshedIndices.add(i);
4392-
4393-
probeAccessToken = account.accessToken;
4394-
probeAccountId = account.accountId ?? refreshedAccountId;
4395-
}
4396-
4397-
if (!probeAccessToken || !probeAccountId) {
4398-
probeErrors.push(
4399-
`${formatAccountLabel(account, i)}: missing accountId for live probe`,
4400-
);
4401-
continue;
4402-
}
4403-
4404-
try {
4405-
const liveQuota = await fetchCodexQuotaSnapshot({
4406-
accountId: probeAccountId,
4407-
accessToken: probeAccessToken,
4408-
model: options.model,
4409-
});
4410-
liveQuotaByIndex.set(i, liveQuota);
4411-
} catch (error) {
4412-
const message = normalizeFailureDetail(
4413-
error instanceof Error ? error.message : String(error),
4414-
undefined,
4415-
);
4416-
probeErrors.push(`${formatAccountLabel(account, i)}: ${message}`);
4417-
}
4418-
}
4419-
4420-
const forecastInputs = storage.accounts.map((account, index) => ({
4421-
index,
4422-
account,
4423-
isCurrent: index === resolveActiveIndex(storage, "codex"),
4424-
now,
4425-
refreshFailure: refreshFailures.get(index),
4426-
liveQuota: liveQuotaByIndex.get(index),
4427-
}));
4428-
4429-
const forecastResults = evaluateForecastAccounts(forecastInputs);
4430-
const recommendation = recommendForecastAccount(forecastResults);
4431-
4432-
if (recommendation.recommendedIndex === null) {
4433-
await persistProbeChangesIfNeeded();
4434-
if (options.json) {
4435-
console.log(
4436-
JSON.stringify(
4437-
{
4438-
error: recommendation.reason,
4439-
...(probeErrors.length > 0 ? { probeErrors } : {}),
4440-
},
4441-
null,
4442-
2,
4443-
),
4444-
);
4445-
} else {
4446-
console.log(`No best account available: ${recommendation.reason}`);
4447-
printProbeNotes();
4448-
}
4449-
return 1;
4450-
}
4451-
4452-
const bestIndex = recommendation.recommendedIndex;
4453-
const bestAccount = storage.accounts[bestIndex];
4454-
if (!bestAccount) {
4455-
await persistProbeChangesIfNeeded();
4456-
if (options.json) {
4457-
console.log(
4458-
JSON.stringify({ error: "Best account not found." }, null, 2),
4459-
);
4460-
} else {
4461-
console.log("Best account not found.");
4462-
}
4463-
return 1;
4464-
}
4465-
4466-
// Check if already on best account
4467-
const currentIndex = resolveActiveIndex(storage, "codex");
4468-
if (currentIndex === bestIndex) {
4469-
const shouldSyncCurrentBest =
4470-
probeRefreshedIndices.has(bestIndex) ||
4471-
probeIdTokenByIndex.has(bestIndex);
4472-
let alreadyBestSynced: boolean | undefined;
4473-
if (changed) {
4474-
bestAccount.lastUsed = now;
4475-
await persistProbeChangesIfNeeded();
4476-
}
4477-
if (shouldSyncCurrentBest) {
4478-
alreadyBestSynced = await setCodexCliActiveSelection({
4479-
accountId: bestAccount.accountId,
4480-
email: bestAccount.email,
4481-
accessToken: bestAccount.accessToken,
4482-
refreshToken: bestAccount.refreshToken,
4483-
expiresAt: bestAccount.expiresAt,
4484-
...(probeIdTokenByIndex.has(bestIndex)
4485-
? { idToken: probeIdTokenByIndex.get(bestIndex) }
4486-
: {}),
4487-
});
4488-
if (!alreadyBestSynced && !options.json) {
4489-
console.warn(
4490-
"Codex auth sync did not complete. Multi-auth routing will still use this account.",
4491-
);
4492-
}
4493-
}
4494-
if (options.json) {
4495-
console.log(
4496-
JSON.stringify(
4497-
{
4498-
message: `Already on best account: ${formatAccountLabel(bestAccount, bestIndex)}`,
4499-
accountIndex: bestIndex + 1,
4500-
reason: recommendation.reason,
4501-
...(alreadyBestSynced !== undefined
4502-
? { synced: alreadyBestSynced }
4503-
: {}),
4504-
...(probeErrors.length > 0 ? { probeErrors } : {}),
4505-
},
4506-
null,
4507-
2,
4508-
),
4509-
);
4510-
} else {
4511-
console.log(
4512-
`Already on best account ${bestIndex + 1}: ${formatAccountLabel(bestAccount, bestIndex)}`,
4513-
);
4514-
console.log(`Reason: ${recommendation.reason}`);
4515-
printProbeNotes();
4516-
}
4517-
return 0;
4518-
}
4519-
4520-
const targetIndex = bestIndex;
4521-
const parsed = targetIndex + 1;
4522-
const { synced, wasDisabled } = await persistAndSyncSelectedAccount({
4523-
storage,
4524-
targetIndex,
4525-
parsed,
4526-
switchReason: "best",
4527-
initialSyncIdToken: probeIdTokenByIndex.get(bestIndex),
4282+
return runBestCommand(args, {
4283+
setStoragePath,
4284+
loadAccounts,
4285+
saveAccounts,
4286+
parseBestArgs,
4287+
printBestUsage,
4288+
resolveActiveIndex,
4289+
hasUsableAccessToken,
4290+
queuedRefresh,
4291+
normalizeFailureDetail,
4292+
extractAccountId,
4293+
extractAccountEmail,
4294+
sanitizeEmail,
4295+
formatAccountLabel,
4296+
fetchCodexQuotaSnapshot,
4297+
evaluateForecastAccounts,
4298+
recommendForecastAccount,
4299+
persistAndSyncSelectedAccount,
4300+
setCodexCliActiveSelection,
45284301
});
4529-
4530-
if (options.json) {
4531-
console.log(
4532-
JSON.stringify(
4533-
{
4534-
message: `Switched to best account: ${formatAccountLabel(bestAccount, targetIndex)}`,
4535-
accountIndex: parsed,
4536-
reason: recommendation.reason,
4537-
synced,
4538-
wasDisabled,
4539-
...(probeErrors.length > 0 ? { probeErrors } : {}),
4540-
},
4541-
null,
4542-
2,
4543-
),
4544-
);
4545-
} else {
4546-
console.log(
4547-
`Switched to best account ${parsed}: ${formatAccountLabel(bestAccount, targetIndex)}${wasDisabled ? " (re-enabled)" : ""}`,
4548-
);
4549-
console.log(`Reason: ${recommendation.reason}`);
4550-
printProbeNotes();
4551-
if (!synced) {
4552-
console.warn(
4553-
"Codex auth sync did not complete. Multi-auth routing will still use this account.",
4554-
);
4555-
}
4556-
}
4557-
return 0;
45584302
}
45594303

45604304
export async function autoSyncActiveAccountToCodex(): Promise<boolean> {

0 commit comments

Comments
 (0)