mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-03 15:24:03 +00:00
fix(model-auth): keep env-first profile precedence
This commit is contained in:
@@ -1660,6 +1660,40 @@ describe("resolveApiKeyForProvider — per-entry apiKey as profile ID reference"
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps env-first precedence ahead of per-entry profile references", async () => {
|
||||
await withEnvAsync({ OPENAI_API_KEY: "sk-env-first" }, async () => {
|
||||
const resolved = await resolveApiKeyForProvider({
|
||||
provider: "openai",
|
||||
credentialPrecedence: "env-first",
|
||||
cfg: {
|
||||
models: {
|
||||
providers: {
|
||||
openai: {
|
||||
api: "openai-completions" as const,
|
||||
baseUrl: "https://api.openai.com/v1",
|
||||
apiKey: "openai:key-b",
|
||||
models: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
store: {
|
||||
version: 1,
|
||||
profiles: {
|
||||
"openai:key-b": {
|
||||
type: "api_key",
|
||||
provider: "openai",
|
||||
key: "sk-profile-key",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(resolved.apiKey).toBe("sk-env-first");
|
||||
expect(resolved.source).toContain("OPENAI_API_KEY");
|
||||
});
|
||||
});
|
||||
|
||||
it("does not bleed auth.order canonical provider profiles into a per-entry provider", async () => {
|
||||
// auth.order.openrouter should not be selected when resolving openrouter-minimax
|
||||
// that has its own per-entry apiKey = "openrouter:key-b" profile reference.
|
||||
|
||||
@@ -1030,6 +1030,29 @@ export async function resolveApiKeyForProvider(params: {
|
||||
return resolveAwsSdkAuthInfo();
|
||||
}
|
||||
|
||||
if (params.credentialPrecedence === "env-first") {
|
||||
const envResolved = resolveConfigAwareEnvApiKey(cfg, provider, params.workspaceDir);
|
||||
if (envResolved) {
|
||||
const resolvedMode: ResolvedProviderAuth["mode"] = envResolved.source.includes("OAUTH_TOKEN")
|
||||
? "oauth"
|
||||
: "api-key";
|
||||
if (
|
||||
!isAuthModeAllowedForModel({
|
||||
provider,
|
||||
modelApi: params.modelApi,
|
||||
mode: resolvedMode,
|
||||
})
|
||||
) {
|
||||
return resolveApiKeyForProvider({ ...params, credentialPrecedence: "profile-first" });
|
||||
}
|
||||
return {
|
||||
apiKey: envResolved.apiKey,
|
||||
source: envResolved.source,
|
||||
mode: resolvedMode,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve stored profile-id references before literal apiKey fallbacks.
|
||||
// Matched profile references are terminal so bad bindings cannot silently
|
||||
// fall through to a different credential or to the profile id as bearer text.
|
||||
@@ -1086,29 +1109,6 @@ export async function resolveApiKeyForProvider(params: {
|
||||
};
|
||||
}
|
||||
}
|
||||
if (params.credentialPrecedence === "env-first") {
|
||||
const envResolved = resolveConfigAwareEnvApiKey(cfg, provider, params.workspaceDir);
|
||||
if (envResolved) {
|
||||
const resolvedMode: ResolvedProviderAuth["mode"] = envResolved.source.includes("OAUTH_TOKEN")
|
||||
? "oauth"
|
||||
: "api-key";
|
||||
if (
|
||||
!isAuthModeAllowedForModel({
|
||||
provider,
|
||||
modelApi: params.modelApi,
|
||||
mode: resolvedMode,
|
||||
})
|
||||
) {
|
||||
return resolveApiKeyForProvider({ ...params, credentialPrecedence: "profile-first" });
|
||||
}
|
||||
return {
|
||||
apiKey: envResolved.apiKey,
|
||||
source: envResolved.source,
|
||||
mode: resolvedMode,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const providerConfig = resolveProviderConfig(cfg, provider);
|
||||
const configuredLocalKey = resolveUsableCustomProviderApiKey({ cfg, provider });
|
||||
if (configuredLocalKey && isNonSecretApiKeyMarker(configuredLocalKey.apiKey)) {
|
||||
|
||||
Reference in New Issue
Block a user