Auth: skip empty profile store loads

This commit is contained in:
Peter Steinberger
2026-04-07 08:32:26 +08:00
parent ac3f55504c
commit 061a9b5c58
3 changed files with 68 additions and 1 deletions

View File

@@ -21,6 +21,35 @@ async function writeAuthStore(agentDir: string) {
}
describe("resolveSessionAuthProfileOverride", () => {
it("returns early when no auth sources exist", async () => {
await withStateDirEnv("openclaw-auth-", async ({ stateDir }) => {
const agentDir = path.join(stateDir, "agent");
await fs.mkdir(agentDir, { recursive: true });
const sessionEntry: SessionEntry = {
sessionId: "s1",
updatedAt: Date.now(),
};
const sessionStore = { "agent:main:main": sessionEntry };
const resolved = await resolveSessionAuthProfileOverride({
cfg: {} as OpenClawConfig,
provider: "openrouter",
agentDir,
sessionEntry,
sessionStore,
sessionKey: "agent:main:main",
storePath: undefined,
isNewSession: false,
});
expect(resolved).toBeUndefined();
await expect(fs.access(path.join(agentDir, "auth-profiles.json"))).rejects.toMatchObject({
code: "ENOENT",
});
});
});
it("keeps user override when provider alias differs", async () => {
await withStateDirEnv("openclaw-auth-", async ({ stateDir }) => {
const agentDir = path.join(stateDir, "agent");

View File

@@ -1,7 +1,7 @@
import type { OpenClawConfig } from "../../config/config.js";
import type { SessionEntry } from "../../config/sessions/types.js";
import { resolveAuthProfileOrder } from "../auth-profiles/order.js";
import { ensureAuthProfileStore } from "../auth-profiles/store.js";
import { ensureAuthProfileStore, hasAnyAuthProfileStoreSource } from "../auth-profiles/store.js";
import { isProfileInCooldown } from "../auth-profiles/usage.js";
import { normalizeProviderId } from "../model-selection.js";
@@ -71,6 +71,17 @@ export async function resolveSessionAuthProfileOverride(params: {
return sessionEntry?.authProfileOverride;
}
const hasConfiguredAuthProfiles =
Boolean(params.cfg.auth?.profiles && Object.keys(params.cfg.auth.profiles).length > 0) ||
Boolean(params.cfg.auth?.order && Object.keys(params.cfg.auth.order).length > 0);
if (
!sessionEntry.authProfileOverride?.trim() &&
!hasConfiguredAuthProfiles &&
!hasAnyAuthProfileStoreSource(agentDir)
) {
return undefined;
}
const store = ensureAuthProfileStore(agentDir, { allowKeychainPrompt: false });
const order = resolveAuthProfileOrder({ cfg, store, provider });
let current = sessionEntry.authProfileOverride?.trim();

View File

@@ -83,6 +83,14 @@ function resolveRuntimeAuthProfileStore(agentDir?: string): AuthProfileStore | n
return null;
}
function hasStoredAuthProfileFiles(agentDir?: string): boolean {
return (
fs.existsSync(resolveAuthStorePath(agentDir)) ||
fs.existsSync(resolveAuthStatePath(agentDir)) ||
fs.existsSync(resolveLegacyAuthStorePath(agentDir))
);
}
export function replaceRuntimeAuthProfileStoreSnapshots(
entries: Array<{ agentDir?: string; store: AuthProfileStore }>,
): void {
@@ -349,6 +357,25 @@ export function ensureAuthProfileStore(
return overlayExternalAuthProfiles(merged, { agentDir });
}
export function hasAnyAuthProfileStoreSource(agentDir?: string): boolean {
const runtimeStore = resolveRuntimeAuthProfileStore(agentDir);
if (runtimeStore && Object.keys(runtimeStore.profiles).length > 0) {
return true;
}
if (hasStoredAuthProfileFiles(agentDir)) {
return true;
}
const authPath = resolveAuthStorePath(agentDir);
const mainAuthPath = resolveAuthStorePath();
if (agentDir && authPath !== mainAuthPath && hasStoredAuthProfileFiles(undefined)) {
return true;
}
return false;
}
export function saveAuthProfileStore(store: AuthProfileStore, agentDir?: string): void {
const authPath = resolveAuthStorePath(agentDir);
const statePath = resolveAuthStatePath(agentDir);