Skip to content

Commit d2fad38

Browse files
committed
fix(auth): realign restored family routing
1 parent 2abd222 commit d2fad38

2 files changed

Lines changed: 74 additions & 1 deletion

File tree

lib/codex-manager.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4572,8 +4572,12 @@ async function persistAndSyncSelectedAccount({
45724572
throw new Error(`Account ${parsed} not found.`);
45734573
}
45744574

4575+
const shouldPreserveActiveIndexByFamily =
4576+
preserveActiveIndexByFamily &&
4577+
!!storage.activeIndexByFamily &&
4578+
targetIndex === storage.activeIndex;
45754579
storage.activeIndex = targetIndex;
4576-
if (!preserveActiveIndexByFamily || !storage.activeIndexByFamily) {
4580+
if (!shouldPreserveActiveIndexByFamily) {
45774581
storage.activeIndexByFamily = storage.activeIndexByFamily ?? {};
45784582
for (const family of MODEL_FAMILIES) {
45794583
storage.activeIndexByFamily[family] = targetIndex;

test/codex-manager-cli.test.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3759,6 +3759,75 @@ describe("codex manager cli commands", () => {
37593759
expect(promptLoginModeMock).toHaveBeenCalledTimes(1);
37603760
});
37613761

3762+
it("realigns restored family routing when a backup carries a stale active index", async () => {
3763+
const now = Date.now();
3764+
setInteractiveTTY(true);
3765+
const restoredStorage = {
3766+
version: 3 as const,
3767+
activeIndex: 99,
3768+
activeIndexByFamily: { codex: 99 },
3769+
accounts: [
3770+
{
3771+
email: "stale@example.com",
3772+
accountId: "acc_stale",
3773+
refreshToken: "refresh-stale",
3774+
accessToken: "access-stale",
3775+
expiresAt: now + 3_600_000,
3776+
addedAt: now - 2_000,
3777+
lastUsed: now - 2_000,
3778+
enabled: true,
3779+
},
3780+
{
3781+
email: "selected@example.com",
3782+
accountId: "acc_selected",
3783+
refreshToken: "refresh-selected",
3784+
accessToken: "access-selected",
3785+
expiresAt: now + 7_200_000,
3786+
addedAt: now - 1_000,
3787+
lastUsed: now - 1_000,
3788+
enabled: true,
3789+
},
3790+
],
3791+
};
3792+
let storageState: typeof restoredStorage | null = null;
3793+
loadAccountsMock.mockImplementation(async () =>
3794+
storageState == null ? null : structuredClone(storageState),
3795+
);
3796+
saveAccountsMock.mockImplementation(async (nextStorage) => {
3797+
storageState = structuredClone(nextStorage);
3798+
});
3799+
getNamedBackupsMock.mockResolvedValue([
3800+
{
3801+
path: "/mock/backups/stale-index.json",
3802+
fileName: "stale-index.json",
3803+
accountCount: 2,
3804+
mtimeMs: now,
3805+
},
3806+
]);
3807+
restoreAccountsFromBackupMock.mockResolvedValue(structuredClone(restoredStorage));
3808+
setCodexCliActiveSelectionMock.mockResolvedValueOnce(true);
3809+
selectMock
3810+
.mockResolvedValueOnce("restore-backup")
3811+
.mockResolvedValueOnce("latest");
3812+
promptLoginModeMock.mockResolvedValueOnce({ mode: "cancel" });
3813+
const { runCodexMultiAuthCli } = await import("../lib/codex-manager.js");
3814+
3815+
const exitCode = await runCodexMultiAuthCli(["auth", "login"]);
3816+
3817+
expect(exitCode).toBe(0);
3818+
expect(saveAccountsMock).toHaveBeenCalledTimes(1);
3819+
expect(storageState).toMatchObject({
3820+
activeIndex: 1,
3821+
activeIndexByFamily: { codex: 1 },
3822+
});
3823+
expect(setCodexCliActiveSelectionMock).toHaveBeenCalledWith(
3824+
expect.objectContaining({
3825+
accountId: "acc_selected",
3826+
email: "selected@example.com",
3827+
}),
3828+
);
3829+
});
3830+
37623831
it("clears the empty onboarding snapshot after adding the first account", async () => {
37633832
const now = Date.now();
37643833
let storageState: {

0 commit comments

Comments
 (0)