Skip to content

Commit 0577c35

Browse files
committed
Cache footer config for switch refreshes
1 parent 4735177 commit 0577c35

2 files changed

Lines changed: 66 additions & 10 deletions

File tree

index.ts

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1149,6 +1149,9 @@ export const OpenAIOAuthPlugin: Plugin = async ({ client }: PluginInput) => {
11491149
const persistedAccountIndicators = new Map<string, PersistedAccountIndicatorEntry>();
11501150
let persistedAccountIndicatorRevision = 0;
11511151
let persistedAccountCountHint = 0;
1152+
let runtimePersistAccountFooter = false;
1153+
let runtimePersistAccountFooterStyle: PersistAccountFooterStyle =
1154+
"label-masked-email";
11521155

11531156
const nextPersistedAccountIndicatorRevision = (): number => {
11541157
persistedAccountIndicatorRevision += 1;
@@ -1191,17 +1194,25 @@ export const OpenAIOAuthPlugin: Plugin = async ({ client }: PluginInput) => {
11911194
accountCount: number,
11921195
style: PersistAccountFooterStyle,
11931196
revision: number = nextPersistedAccountIndicatorRevision(),
1197+
options?: { preserveOrder?: boolean },
11941198
): boolean => {
11951199
if (!sessionID) return false;
11961200
const existing = persistedAccountIndicators.get(sessionID);
11971201
if (existing && existing.revision > revision) {
11981202
return false;
11991203
}
1200-
persistedAccountIndicators.delete(sessionID);
1201-
persistedAccountIndicators.set(sessionID, {
1204+
const nextEntry = {
12021205
label: formatPersistedAccountIndicator(account, index, accountCount, style),
12031206
revision,
1204-
});
1207+
};
1208+
if (existing && options?.preserveOrder) {
1209+
persistedAccountIndicators.set(sessionID, nextEntry);
1210+
return true;
1211+
}
1212+
if (existing) {
1213+
persistedAccountIndicators.delete(sessionID);
1214+
}
1215+
persistedAccountIndicators.set(sessionID, nextEntry);
12051216
trimPersistedAccountIndicators();
12061217
return true;
12071218
};
@@ -1223,6 +1234,7 @@ export const OpenAIOAuthPlugin: Plugin = async ({ client }: PluginInput) => {
12231234
accountCount,
12241235
style,
12251236
revision,
1237+
{ preserveOrder: true },
12261238
);
12271239
}
12281240
return true;
@@ -1393,8 +1405,17 @@ export const OpenAIOAuthPlugin: Plugin = async ({ client }: PluginInput) => {
13931405
});
13941406
};
13951407

1408+
const syncRuntimePluginConfig = (
1409+
pluginConfig: ReturnType<typeof loadPluginConfig>,
1410+
): UiRuntimeOptions => {
1411+
runtimePersistAccountFooter = getPersistAccountFooter(pluginConfig);
1412+
runtimePersistAccountFooterStyle =
1413+
getPersistAccountFooterStyle(pluginConfig);
1414+
return applyUiRuntimeFromConfig(pluginConfig);
1415+
};
1416+
13961417
const resolveUiRuntime = (): UiRuntimeOptions => {
1397-
return applyUiRuntimeFromConfig(loadPluginConfig());
1418+
return syncRuntimePluginConfig(loadPluginConfig());
13981419
};
13991420

14001421
const getStatusMarker = (
@@ -1861,13 +1882,12 @@ export const OpenAIOAuthPlugin: Plugin = async ({ client }: PluginInput) => {
18611882
accountManagerPromise = Promise.resolve(reloadedManager);
18621883
}
18631884

1864-
const pluginConfig = loadPluginConfig();
1865-
if (getPersistAccountFooter(pluginConfig)) {
1885+
if (runtimePersistAccountFooter) {
18661886
refreshVisiblePersistedAccountIndicators(
18671887
account,
18681888
index,
18691889
storage.accounts.length,
1870-
getPersistAccountFooterStyle(pluginConfig),
1890+
runtimePersistAccountFooterStyle,
18711891
);
18721892
} else {
18731893
await showToast(`Switched to account ${index + 1}`, "info");
@@ -1957,7 +1977,7 @@ export const OpenAIOAuthPlugin: Plugin = async ({ client }: PluginInput) => {
19571977
async loader(getAuth: () => Promise<Auth>, provider: unknown) {
19581978
const auth = await getAuth();
19591979
const pluginConfig = loadPluginConfig();
1960-
applyUiRuntimeFromConfig(pluginConfig);
1980+
syncRuntimePluginConfig(pluginConfig);
19611981
const perProjectAccounts = getPerProjectAccounts(pluginConfig);
19621982
setStoragePath(perProjectAccounts ? process.cwd() : null);
19631983
const authFallback = auth.type === "oauth" ? (auth as OAuthAuthDetails) : undefined;
@@ -2949,7 +2969,7 @@ while (attempted.size < Math.max(1, accountCount)) {
29492969
type: "oauth" as const,
29502970
authorize: async (inputs?: Record<string, string>) => {
29512971
const authPluginConfig = loadPluginConfig();
2952-
applyUiRuntimeFromConfig(authPluginConfig);
2972+
syncRuntimePluginConfig(authPluginConfig);
29532973
const authPerProjectAccounts = getPerProjectAccounts(authPluginConfig);
29542974
setStoragePath(authPerProjectAccounts ? process.cwd() : null);
29552975

@@ -3924,7 +3944,7 @@ while (attempted.size < Math.max(1, accountCount)) {
39243944
// Initialize storage path for manual OAuth flow
39253945
// Must happen BEFORE persistAccountPool to ensure correct storage location
39263946
const manualPluginConfig = loadPluginConfig();
3927-
applyUiRuntimeFromConfig(manualPluginConfig);
3947+
syncRuntimePluginConfig(manualPluginConfig);
39283948
const manualPerProjectAccounts = getPerProjectAccounts(manualPluginConfig);
39293949
setStoragePath(manualPerProjectAccounts ? process.cwd() : null);
39303950

test/index.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2375,9 +2375,12 @@ describe("OpenAIOAuthPlugin fetch handler", () => {
23752375
{ index: 1, accountId: "acc-2", email: "user2@example.com", refreshToken: "refresh-2" },
23762376
];
23772377
vi.spyOn(accountsModule.AccountManager, "loadFromDisk").mockResolvedValue(manager as never);
2378+
const configModule = await import("../lib/config.js");
23782379

23792380
const { plugin, sdk, mockClient } = await setupPlugin();
23802381
await sendPersistedAccountRequest(sdk, "session-switch");
2382+
const configReadCountBeforeSwitch =
2383+
vi.mocked(configModule.loadPluginConfig).mock.calls.length;
23812384
mockClient.tui.showToast.mockClear();
23822385

23832386
expect((await readPersistedAccountIndicator(plugin, "session-switch")).variant).toBe(
@@ -2394,6 +2397,9 @@ describe("OpenAIOAuthPlugin fetch handler", () => {
23942397
variant: "info",
23952398
},
23962399
});
2400+
expect(vi.mocked(configModule.loadPluginConfig)).toHaveBeenCalledTimes(
2401+
configReadCountBeforeSwitch,
2402+
);
23972403
expect((await readPersistedAccountIndicator(plugin, "session-switch")).variant).toBe(
23982404
"user2@example.com [2/2]",
23992405
);
@@ -2459,6 +2465,36 @@ describe("OpenAIOAuthPlugin fetch handler", () => {
24592465
);
24602466
});
24612467

2468+
it("evicts the oldest persisted indicator after a full refresh when a new session overflows the cap", async () => {
2469+
await enablePersistedFooter("full-email");
2470+
mockStorage.accounts = [
2471+
{ accountId: "acc-1", email: "user@example.com", refreshToken: "refresh-token" },
2472+
{ accountId: "acc-2", email: "user2@example.com", refreshToken: "refresh-2" },
2473+
];
2474+
const maxPersistedIndicators = 200;
2475+
const sessionIDs = Array.from(
2476+
{ length: maxPersistedIndicators },
2477+
(_, index) => `session-overflow-${index}`,
2478+
);
2479+
2480+
const { plugin, sdk } = await setupPlugin();
2481+
for (const sessionID of sessionIDs) {
2482+
await sendPersistedAccountRequest(sdk, sessionID);
2483+
}
2484+
2485+
await plugin.event({
2486+
event: { type: "account.select", properties: { index: 1 } },
2487+
});
2488+
2489+
await sendPersistedAccountRequest(sdk, "session-overflow-new");
2490+
2491+
expect((await readPersistedAccountIndicator(plugin, sessionIDs[0]!)).variant).toBeUndefined();
2492+
expect((await readPersistedAccountIndicator(plugin, sessionIDs[1]!)).variant).toBe(
2493+
"user2@example.com [2/2]",
2494+
);
2495+
expect((await readPersistedAccountIndicator(plugin, "session-overflow-new")).variant).toBeDefined();
2496+
});
2497+
24622498
it("suppresses the account-use info toast when the footer is enabled", async () => {
24632499
await enablePersistedFooter("full-email");
24642500
mockStorage.accounts = [

0 commit comments

Comments
 (0)