Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 70 additions & 8 deletions lib/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export interface LoginMenuResult {
refreshAccountIndex?: number;
toggleAccountIndex?: number;
switchAccountIndex?: number;
selectedAccountNumber?: number;
deleteAll?: boolean;
}

Expand Down Expand Up @@ -122,11 +123,40 @@ function resolveAccountSourceIndex(account: ExistingAccountInfo): number {
return -1;
}

function resolveAccountDisplayNumber(
account: ExistingAccountInfo,
): number | undefined {
if (
typeof account.quickSwitchNumber === "number" &&
Number.isFinite(account.quickSwitchNumber)
) {
return Math.max(1, Math.floor(account.quickSwitchNumber));
}
if (typeof account.index === "number" && Number.isFinite(account.index)) {
return Math.max(1, Math.floor(account.index) + 1);
}
return undefined;
}

function warnUnresolvableAccountSelection(account: ExistingAccountInfo): void {
const label = account.email?.trim() || account.accountId?.trim() || `index ${account.index + 1}`;
console.log(`Unable to resolve saved account for action: ${label}`);
}

function buildManageResult(
account: ExistingAccountInfo,
result: Omit<LoginMenuResult, "mode" | "selectedAccountNumber">,
): LoginMenuResult {
const selectedAccountNumber = resolveAccountDisplayNumber(account);
return {
mode: "manage",
...result,
...(typeof selectedAccountNumber === "number"
? { selectedAccountNumber }
: {}),
};
}

async function promptDeleteAllTypedConfirm(): Promise<boolean> {
const rl = createInterface({ input, output });
try {
Expand Down Expand Up @@ -225,51 +255,83 @@ export async function promptLoginMode(
const accountAction = await showAccountDetails(action.account);
if (accountAction === "delete") {
const index = resolveAccountSourceIndex(action.account);
if (index >= 0) return { mode: "manage", deleteAccountIndex: index };
if (index >= 0) {
return buildManageResult(action.account, {
deleteAccountIndex: index,
});
}
warnUnresolvableAccountSelection(action.account);
continue;
}
if (accountAction === "set-current") {
const index = resolveAccountSourceIndex(action.account);
if (index >= 0) return { mode: "manage", switchAccountIndex: index };
if (index >= 0) {
return buildManageResult(action.account, {
switchAccountIndex: index,
});
}
warnUnresolvableAccountSelection(action.account);
continue;
}
if (accountAction === "refresh") {
const index = resolveAccountSourceIndex(action.account);
if (index >= 0) return { mode: "manage", refreshAccountIndex: index };
if (index >= 0) {
return buildManageResult(action.account, {
refreshAccountIndex: index,
});
}
warnUnresolvableAccountSelection(action.account);
continue;
}
if (accountAction === "toggle") {
const index = resolveAccountSourceIndex(action.account);
if (index >= 0) return { mode: "manage", toggleAccountIndex: index };
if (index >= 0) {
return buildManageResult(action.account, {
toggleAccountIndex: index,
});
}
warnUnresolvableAccountSelection(action.account);
continue;
}
continue;
}
case "set-current-account": {
const index = resolveAccountSourceIndex(action.account);
if (index >= 0) return { mode: "manage", switchAccountIndex: index };
if (index >= 0) {
return buildManageResult(action.account, {
switchAccountIndex: index,
});
}
warnUnresolvableAccountSelection(action.account);
continue;
}
case "refresh-account": {
const index = resolveAccountSourceIndex(action.account);
if (index >= 0) return { mode: "manage", refreshAccountIndex: index };
if (index >= 0) {
return buildManageResult(action.account, {
refreshAccountIndex: index,
});
}
warnUnresolvableAccountSelection(action.account);
continue;
}
case "toggle-account": {
const index = resolveAccountSourceIndex(action.account);
if (index >= 0) return { mode: "manage", toggleAccountIndex: index };
if (index >= 0) {
return buildManageResult(action.account, {
toggleAccountIndex: index,
});
}
warnUnresolvableAccountSelection(action.account);
continue;
}
case "delete-account": {
const index = resolveAccountSourceIndex(action.account);
if (index >= 0) return { mode: "manage", deleteAccountIndex: index };
if (index >= 0) {
return buildManageResult(action.account, {
deleteAccountIndex: index,
});
}
warnUnresolvableAccountSelection(action.account);
continue;
}
Expand Down
64 changes: 54 additions & 10 deletions lib/codex-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3736,18 +3736,44 @@ async function clearAccountsAndReset(): Promise<void> {
await clearAccounts();
}

function resolveManageActionAccountNumber(
menuResult: Awaited<ReturnType<typeof promptLoginMode>>,
fallbackIndex: number,
): number {
if (
typeof menuResult.selectedAccountNumber === "number" &&
Number.isFinite(menuResult.selectedAccountNumber)
) {
return Math.max(1, Math.floor(menuResult.selectedAccountNumber));
}
return fallbackIndex + 1;
}

interface RunSwitchOptions {
displayAccountNumber?: number;
}

async function handleManageAction(
storage: AccountStorageV3,
menuResult: Awaited<ReturnType<typeof promptLoginMode>>,
): Promise<void> {
if (typeof menuResult.switchAccountIndex === "number") {
const index = menuResult.switchAccountIndex;
await runSwitch([String(index + 1)]);
await runSwitch([String(index + 1)], {
displayAccountNumber: resolveManageActionAccountNumber(
menuResult,
index,
),
});
return;
}

if (typeof menuResult.deleteAccountIndex === "number") {
const idx = menuResult.deleteAccountIndex;
const displayAccountNumber = resolveManageActionAccountNumber(
menuResult,
idx,
);
if (idx >= 0 && idx < storage.accounts.length) {
storage.accounts.splice(idx, 1);
storage.activeIndex = 0;
Expand All @@ -3756,26 +3782,34 @@ async function handleManageAction(
storage.activeIndexByFamily[family] = 0;
}
await saveAccounts(storage);
console.log(`Deleted account ${idx + 1}.`);
console.log(`Deleted account ${displayAccountNumber}.`);
}
return;
}

if (typeof menuResult.toggleAccountIndex === "number") {
const idx = menuResult.toggleAccountIndex;
const displayAccountNumber = resolveManageActionAccountNumber(
menuResult,
idx,
);
const account = storage.accounts[idx];
if (account) {
account.enabled = account.enabled === false;
await saveAccounts(storage);
console.log(
`${account.enabled === false ? "Disabled" : "Enabled"} account ${idx + 1}.`,
`${account.enabled === false ? "Disabled" : "Enabled"} account ${displayAccountNumber}.`,
);
}
return;
}

if (typeof menuResult.refreshAccountIndex === "number") {
const idx = menuResult.refreshAccountIndex;
const displayAccountNumber = resolveManageActionAccountNumber(
menuResult,
idx,
);
const existing = storage.accounts[idx];
if (!existing) return;

Expand All @@ -3788,7 +3822,7 @@ async function handleManageAction(
const resolved = resolveAccountSelection(tokenResult);
await persistAccountPool([resolved], false);
await syncSelectionToCodex(resolved);
console.log(`Refreshed account ${idx + 1}.`);
console.log(`Refreshed account ${displayAccountNumber}.`);
}
}

Expand Down Expand Up @@ -3945,7 +3979,10 @@ async function runAuthLogin(): Promise<number> {
}
}

async function runSwitch(args: string[]): Promise<number> {
async function runSwitch(
args: string[],
options: RunSwitchOptions = {},
): Promise<number> {
setStoragePath(null);
const indexArg = args[0];
if (!indexArg) {
Expand All @@ -3958,20 +3995,27 @@ async function runSwitch(args: string[]): Promise<number> {
return 1;
}
const targetIndex = parsed - 1;
const displayAccountNumber =
typeof options.displayAccountNumber === "number" &&
Number.isFinite(options.displayAccountNumber)
? Math.max(1, Math.floor(options.displayAccountNumber))
: parsed;

const storage = await loadAccounts();
if (!storage || storage.accounts.length === 0) {
console.error("No accounts configured.");
return 1;
}
if (targetIndex < 0 || targetIndex >= storage.accounts.length) {
console.error(`Index out of range. Valid range: 1-${storage.accounts.length}`);
console.error(
`Selected account ${displayAccountNumber} is out of range. Valid range: 1-${storage.accounts.length}.`,
);
return 1;
}

const account = storage.accounts[targetIndex];
if (!account) {
console.error(`Account ${parsed} not found.`);
console.error(`Selected account ${displayAccountNumber} is no longer available.`);
return 1;
}

Expand Down Expand Up @@ -4017,7 +4061,7 @@ async function runSwitch(args: string[]): Promise<number> {
syncIdToken = refreshResult.idToken;
} else {
console.warn(
`Switch validation refresh failed for account ${parsed}: ${normalizeFailureDetail(refreshResult.message, refreshResult.reason)}.`,
`Switch validation refresh failed for account ${displayAccountNumber}: ${normalizeFailureDetail(refreshResult.message, refreshResult.reason)}.`,
);
}
}
Expand All @@ -4036,12 +4080,12 @@ async function runSwitch(args: string[]): Promise<number> {
});
if (!synced) {
console.warn(
`Switched account ${parsed} locally, but Codex auth sync did not complete. Multi-auth routing will still use this account.`,
`Switched account ${displayAccountNumber} locally, but Codex auth sync did not complete. Multi-auth routing will still use this account.`,
);
}

console.log(
`Switched to account ${parsed}: ${formatAccountLabel(account, targetIndex)}${wasDisabled ? " (re-enabled)" : ""}`,
`Switched to account ${displayAccountNumber}: ${formatAccountLabel(account, displayAccountNumber - 1)}${wasDisabled ? " (re-enabled)" : ""}`,
);
return 0;
}
Expand Down
Loading