mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-29 10:50:58 +00:00
fix: keep provider normalization on local sync paths
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import fs from "node:fs";
|
||||
import type { OAuthCredentials } from "@mariozechner/pi-ai";
|
||||
import { resolveOAuthPath } from "../../config/paths.js";
|
||||
import { withFileLock } from "../../infra/file-lock.js";
|
||||
import { loadJsonFile, saveJsonFile } from "../../infra/json-file.js";
|
||||
@@ -11,7 +10,12 @@ import {
|
||||
} from "./constants.js";
|
||||
import { syncExternalCliCredentials } from "./external-cli-sync.js";
|
||||
import { ensureAuthStoreFile, resolveAuthStorePath, resolveLegacyAuthStorePath } from "./paths.js";
|
||||
import type { AuthProfileCredential, AuthProfileStore, ProfileUsageStats } from "./types.js";
|
||||
import type {
|
||||
AuthProfileCredential,
|
||||
AuthProfileStore,
|
||||
OAuthCredentials,
|
||||
ProfileUsageStats,
|
||||
} from "./types.js";
|
||||
|
||||
type LegacyAuthStore = Record<string, AuthProfileCredential>;
|
||||
type CredentialRejectReason = "non_object" | "invalid_type" | "missing_provider";
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
import type { OAuthCredentials } from "@mariozechner/pi-ai";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import type { SecretRef } from "../../config/types.secrets.js";
|
||||
|
||||
export type OAuthProvider = string;
|
||||
|
||||
export type OAuthCredentials = {
|
||||
access: string;
|
||||
refresh: string;
|
||||
expires: number;
|
||||
provider?: OAuthProvider;
|
||||
email?: string;
|
||||
enterpriseUrl?: string;
|
||||
projectId?: string;
|
||||
accountId?: string;
|
||||
};
|
||||
|
||||
export type ApiKeyCredential = {
|
||||
type: "api_key";
|
||||
provider: string;
|
||||
|
||||
@@ -2,10 +2,10 @@ import { execFileSync, execSync } from "node:child_process";
|
||||
import { createHash } from "node:crypto";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import type { OAuthCredentials, OAuthProvider } from "@mariozechner/pi-ai";
|
||||
import { loadJsonFile, saveJsonFile } from "../infra/json-file.js";
|
||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
import { resolveUserPath } from "../utils.js";
|
||||
import type { OAuthCredentials, OAuthProvider } from "./auth-profiles/types.js";
|
||||
|
||||
const log = createSubsystemLogger("agents/auth-profiles");
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { ensureAuthProfileStore } from "./auth-profiles.js";
|
||||
import { ensureAuthProfileStore } from "./auth-profiles/store.js";
|
||||
import {
|
||||
normalizeProviderSpecificConfig,
|
||||
resolveProviderConfigApiKeyResolver,
|
||||
@@ -30,9 +30,17 @@ export function normalizeProviders(params: {
|
||||
return providers;
|
||||
}
|
||||
const env = params.env ?? process.env;
|
||||
const authStore = ensureAuthProfileStore(params.agentDir, {
|
||||
allowKeychainPrompt: false,
|
||||
});
|
||||
let authStore: ReturnType<typeof ensureAuthProfileStore> | undefined;
|
||||
const resolveProfileApiKey = (providerKey: string) => {
|
||||
authStore ??= ensureAuthProfileStore(params.agentDir, {
|
||||
allowKeychainPrompt: false,
|
||||
});
|
||||
return resolveApiKeyFromProfiles({
|
||||
provider: providerKey,
|
||||
store: authStore,
|
||||
env,
|
||||
});
|
||||
};
|
||||
let mutated = false;
|
||||
const next: Record<string, ProviderConfig> = {};
|
||||
|
||||
@@ -54,16 +62,11 @@ export function normalizeProviders(params: {
|
||||
mutated = true;
|
||||
normalizedProvider = { ...normalizedProvider, headers: normalizedHeaders.headers };
|
||||
}
|
||||
const profileApiKey = resolveApiKeyFromProfiles({
|
||||
provider: normalizedKey,
|
||||
store: authStore,
|
||||
env,
|
||||
});
|
||||
const providerWithConfiguredApiKey = normalizeConfiguredProviderApiKey({
|
||||
providerKey: normalizedKey,
|
||||
provider: normalizedProvider,
|
||||
secretDefaults: params.secretDefaults,
|
||||
profileApiKey,
|
||||
profileApiKey: undefined,
|
||||
secretRefManagedProviders: params.secretRefManagedProviders,
|
||||
});
|
||||
if (providerWithConfiguredApiKey !== normalizedProvider) {
|
||||
@@ -86,13 +89,24 @@ export function normalizeProviders(params: {
|
||||
normalizedProvider = providerWithResolvedEnvApiKey;
|
||||
}
|
||||
|
||||
const needsProfileApiKey =
|
||||
Array.isArray(normalizedProvider.models) &&
|
||||
normalizedProvider.models.length > 0 &&
|
||||
!(
|
||||
(typeof normalizedProvider.apiKey === "string" && normalizedProvider.apiKey.trim()) ||
|
||||
normalizedProvider.apiKey
|
||||
);
|
||||
const profileApiKey = needsProfileApiKey ? resolveProfileApiKey(normalizedKey) : undefined;
|
||||
const providerApiKeyResolver = needsProfileApiKey
|
||||
? resolveProviderConfigApiKeyResolver(normalizedKey)
|
||||
: undefined;
|
||||
const providerWithApiKey = resolveMissingProviderApiKey({
|
||||
providerKey: normalizedKey,
|
||||
provider: normalizedProvider,
|
||||
env,
|
||||
profileApiKey,
|
||||
secretRefManagedProviders: params.secretRefManagedProviders,
|
||||
providerApiKeyResolver: resolveProviderConfigApiKeyResolver(normalizedKey),
|
||||
providerApiKeyResolver,
|
||||
});
|
||||
if (providerWithApiKey !== normalizedProvider) {
|
||||
mutated = true;
|
||||
|
||||
@@ -1,23 +1,7 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("models-config.providers.policy", () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("resolves config apiKey markers through provider runtime hooks", async () => {
|
||||
const providerRuntime = await import("../plugins/provider-runtime.js");
|
||||
vi.spyOn(providerRuntime, "resolveProviderRuntimePlugin").mockReturnValue({
|
||||
id: "amazon-bedrock",
|
||||
label: "Amazon Bedrock",
|
||||
auth: [],
|
||||
resolveConfigApiKey: () => "AWS_PROFILE",
|
||||
});
|
||||
const resolveProviderConfigApiKeyWithPluginSpy = vi
|
||||
.spyOn(providerRuntime, "resolveProviderConfigApiKeyWithPlugin")
|
||||
.mockReturnValue("AWS_PROFILE");
|
||||
|
||||
it("resolves config apiKey markers through the local bedrock helper", async () => {
|
||||
const { resolveProviderConfigApiKeyResolver } =
|
||||
await import("./models-config.providers.policy.js");
|
||||
const env = {
|
||||
@@ -27,13 +11,5 @@ describe("models-config.providers.policy", () => {
|
||||
|
||||
expect(resolver).toBeTypeOf("function");
|
||||
expect(resolver?.(env)).toBe("AWS_PROFILE");
|
||||
expect(resolveProviderConfigApiKeyWithPluginSpy).toHaveBeenCalledWith({
|
||||
provider: "amazon-bedrock",
|
||||
env,
|
||||
context: {
|
||||
provider: "amazon-bedrock",
|
||||
env,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
import { resolveBedrockConfigApiKey } from "../plugin-sdk/amazon-bedrock.js";
|
||||
import { resolveAnthropicVertexConfigApiKey } from "../plugin-sdk/anthropic-vertex.js";
|
||||
import {
|
||||
applyProviderNativeStreamingUsageCompatWithPlugin,
|
||||
normalizeProviderConfigWithPlugin,
|
||||
resolveProviderConfigApiKeyWithPlugin,
|
||||
resolveProviderRuntimePlugin,
|
||||
} from "../plugins/provider-runtime.js";
|
||||
import { normalizeGoogleProviderConfig } from "../plugin-sdk/google.js";
|
||||
import { applyModelStudioNativeStreamingUsageCompat } from "../plugin-sdk/modelstudio.js";
|
||||
import { applyMoonshotNativeStreamingUsageCompat } from "../plugin-sdk/moonshot.js";
|
||||
import type { ProviderConfig } from "./models-config.providers.secrets.js";
|
||||
|
||||
const PROVIDER_CONFIG_API_KEY_RESOLVERS: Partial<
|
||||
Record<string, (env: NodeJS.ProcessEnv) => string | undefined>
|
||||
> = {
|
||||
"amazon-bedrock": resolveBedrockConfigApiKey,
|
||||
"anthropic-vertex": resolveAnthropicVertexConfigApiKey,
|
||||
};
|
||||
|
||||
function shouldNormalizeGoogleProviderConfigLocally(providerKey: string): boolean {
|
||||
return (
|
||||
providerKey === "google" ||
|
||||
providerKey === "google-antigravity" ||
|
||||
providerKey === "google-vertex"
|
||||
);
|
||||
}
|
||||
|
||||
export function applyNativeStreamingUsageCompat(
|
||||
providers: Record<string, ProviderConfig>,
|
||||
): Record<string, ProviderConfig> {
|
||||
@@ -21,13 +28,11 @@ export function applyNativeStreamingUsageCompat(
|
||||
|
||||
for (const [providerKey, provider] of Object.entries(providers)) {
|
||||
const nextProvider =
|
||||
applyProviderNativeStreamingUsageCompatWithPlugin({
|
||||
provider: providerKey,
|
||||
context: {
|
||||
provider: providerKey,
|
||||
providerConfig: provider,
|
||||
},
|
||||
}) ?? provider;
|
||||
providerKey === "modelstudio"
|
||||
? applyModelStudioNativeStreamingUsageCompat(provider)
|
||||
: providerKey === "moonshot"
|
||||
? applyMoonshotNativeStreamingUsageCompat(provider)
|
||||
: provider;
|
||||
nextProviders[providerKey] = nextProvider;
|
||||
changed ||= nextProvider !== provider;
|
||||
}
|
||||
@@ -39,32 +44,15 @@ export function normalizeProviderSpecificConfig(
|
||||
providerKey: string,
|
||||
provider: ProviderConfig,
|
||||
): ProviderConfig {
|
||||
return (
|
||||
normalizeProviderConfigWithPlugin({
|
||||
provider: providerKey,
|
||||
context: {
|
||||
provider: providerKey,
|
||||
providerConfig: provider,
|
||||
},
|
||||
}) ?? provider
|
||||
);
|
||||
if (shouldNormalizeGoogleProviderConfigLocally(providerKey)) {
|
||||
return normalizeGoogleProviderConfig(providerKey, provider);
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
export function resolveProviderConfigApiKeyResolver(
|
||||
providerKey: string,
|
||||
): ((env: NodeJS.ProcessEnv) => string | undefined) | undefined {
|
||||
const fallback = PROVIDER_CONFIG_API_KEY_RESOLVERS[providerKey];
|
||||
const plugin = resolveProviderRuntimePlugin({ provider: providerKey });
|
||||
if (!plugin?.resolveConfigApiKey && !fallback) {
|
||||
return undefined;
|
||||
}
|
||||
return (env: NodeJS.ProcessEnv) =>
|
||||
resolveProviderConfigApiKeyWithPlugin({
|
||||
provider: providerKey,
|
||||
env,
|
||||
context: {
|
||||
provider: providerKey,
|
||||
env,
|
||||
},
|
||||
}) ?? fallback?.(env);
|
||||
return fallback;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { coerceSecretRef, resolveSecretInputRef } from "../config/types.secrets.js";
|
||||
import { normalizeOptionalSecretInput } from "../utils/normalize-secret-input.js";
|
||||
import { ensureAuthProfileStore, listProfilesForProvider } from "./auth-profiles.js";
|
||||
import { listProfilesForProvider } from "./auth-profiles/profiles.js";
|
||||
import { ensureAuthProfileStore } from "./auth-profiles/store.js";
|
||||
import { resolveEnvApiKey } from "./model-auth-env.js";
|
||||
import {
|
||||
isNonSecretApiKeyMarker,
|
||||
resolveEnvSecretRefHeaderValueMarker,
|
||||
resolveNonEnvSecretRefApiKeyMarker,
|
||||
resolveNonEnvSecretRefHeaderValueMarker,
|
||||
} from "./model-auth-markers.js";
|
||||
import { resolveAwsSdkEnvVarName, resolveEnvApiKey } from "./model-auth.js";
|
||||
import { resolveAwsSdkEnvVarName } from "./model-auth-runtime-shared.js";
|
||||
|
||||
type ModelsConfig = NonNullable<OpenClawConfig["models"]>;
|
||||
export type ProviderConfig = NonNullable<ModelsConfig["providers"]>[string];
|
||||
|
||||
Reference in New Issue
Block a user