mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 00:00:23 +00:00
refactor(auth): drop provider auth storage switchboard
This commit is contained in:
@@ -1,12 +1,5 @@
|
|||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
import {
|
import { upsertApiKeyProfile } from "../plugins/provider-auth-helpers.js";
|
||||||
setByteplusApiKey,
|
|
||||||
setCloudflareAiGatewayConfig,
|
|
||||||
setMoonshotApiKey,
|
|
||||||
setOpencodeZenApiKey,
|
|
||||||
setOpenaiApiKey,
|
|
||||||
setVolcengineApiKey,
|
|
||||||
} from "../plugins/provider-auth-storage.js";
|
|
||||||
import {
|
import {
|
||||||
createAuthTestLifecycle,
|
createAuthTestLifecycle,
|
||||||
readAuthProfilesForAgent,
|
readAuthProfilesForAgent,
|
||||||
@@ -80,7 +73,7 @@ describe("onboard auth credentials secret refs", () => {
|
|||||||
envValue: "sk-moonshot-env",
|
envValue: "sk-moonshot-env",
|
||||||
profileId: "moonshot:default",
|
profileId: "moonshot:default",
|
||||||
apply: async () => {
|
apply: async () => {
|
||||||
await setMoonshotApiKey("sk-moonshot-env");
|
upsertApiKeyProfile({ provider: "moonshot", input: "sk-moonshot-env" });
|
||||||
},
|
},
|
||||||
expected: {
|
expected: {
|
||||||
key: "sk-moonshot-env",
|
key: "sk-moonshot-env",
|
||||||
@@ -96,7 +89,12 @@ describe("onboard auth credentials secret refs", () => {
|
|||||||
envValue: "sk-moonshot-env",
|
envValue: "sk-moonshot-env",
|
||||||
profileId: "moonshot:default",
|
profileId: "moonshot:default",
|
||||||
apply: async (agentDir) => {
|
apply: async (agentDir) => {
|
||||||
await setMoonshotApiKey("sk-moonshot-env", agentDir, { secretInputMode: "ref" }); // pragma: allowlist secret
|
upsertApiKeyProfile({
|
||||||
|
provider: "moonshot",
|
||||||
|
input: "sk-moonshot-env",
|
||||||
|
agentDir,
|
||||||
|
options: { secretInputMode: "ref" }, // pragma: allowlist secret
|
||||||
|
});
|
||||||
},
|
},
|
||||||
expected: {
|
expected: {
|
||||||
keyRef: { source: "env", provider: "default", id: "MOONSHOT_API_KEY" },
|
keyRef: { source: "env", provider: "default", id: "MOONSHOT_API_KEY" },
|
||||||
@@ -110,7 +108,7 @@ describe("onboard auth credentials secret refs", () => {
|
|||||||
prefix: "openclaw-onboard-auth-credentials-inline-ref-",
|
prefix: "openclaw-onboard-auth-credentials-inline-ref-",
|
||||||
profileId: "moonshot:default",
|
profileId: "moonshot:default",
|
||||||
apply: async () => {
|
apply: async () => {
|
||||||
await setMoonshotApiKey("${MOONSHOT_API_KEY}");
|
upsertApiKeyProfile({ provider: "moonshot", input: "${MOONSHOT_API_KEY}" });
|
||||||
},
|
},
|
||||||
expected: {
|
expected: {
|
||||||
keyRef: { source: "env", provider: "default", id: "MOONSHOT_API_KEY" },
|
keyRef: { source: "env", provider: "default", id: "MOONSHOT_API_KEY" },
|
||||||
@@ -126,7 +124,7 @@ describe("onboard auth credentials secret refs", () => {
|
|||||||
envValue: "sk-moonshot-other",
|
envValue: "sk-moonshot-other",
|
||||||
profileId: "moonshot:default",
|
profileId: "moonshot:default",
|
||||||
apply: async () => {
|
apply: async () => {
|
||||||
await setMoonshotApiKey("sk-moonshot-plaintext");
|
upsertApiKeyProfile({ provider: "moonshot", input: "sk-moonshot-plaintext" });
|
||||||
},
|
},
|
||||||
expected: {
|
expected: {
|
||||||
key: "sk-moonshot-plaintext",
|
key: "sk-moonshot-plaintext",
|
||||||
@@ -140,8 +138,15 @@ describe("onboard auth credentials secret refs", () => {
|
|||||||
lifecycle.setStateDir(env.stateDir);
|
lifecycle.setStateDir(env.stateDir);
|
||||||
process.env.CLOUDFLARE_AI_GATEWAY_API_KEY = "cf-secret"; // pragma: allowlist secret
|
process.env.CLOUDFLARE_AI_GATEWAY_API_KEY = "cf-secret"; // pragma: allowlist secret
|
||||||
|
|
||||||
await setCloudflareAiGatewayConfig("account-1", "gateway-1", "cf-secret", env.agentDir, {
|
upsertApiKeyProfile({
|
||||||
secretInputMode: "ref", // pragma: allowlist secret
|
provider: "cloudflare-ai-gateway",
|
||||||
|
input: "cf-secret",
|
||||||
|
agentDir: env.agentDir,
|
||||||
|
options: { secretInputMode: "ref" }, // pragma: allowlist secret
|
||||||
|
metadata: {
|
||||||
|
accountId: "account-1",
|
||||||
|
gatewayId: "gateway-1",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const parsed = await readAuthProfilesForAgent<{
|
const parsed = await readAuthProfilesForAgent<{
|
||||||
@@ -161,7 +166,7 @@ describe("onboard auth credentials secret refs", () => {
|
|||||||
envValue: "sk-openai-env",
|
envValue: "sk-openai-env",
|
||||||
profileId: "openai:default",
|
profileId: "openai:default",
|
||||||
apply: async () => {
|
apply: async () => {
|
||||||
await setOpenaiApiKey("sk-openai-env");
|
upsertApiKeyProfile({ provider: "openai", input: "sk-openai-env" });
|
||||||
},
|
},
|
||||||
expected: {
|
expected: {
|
||||||
key: "sk-openai-env",
|
key: "sk-openai-env",
|
||||||
@@ -177,7 +182,12 @@ describe("onboard auth credentials secret refs", () => {
|
|||||||
envValue: "sk-openai-env",
|
envValue: "sk-openai-env",
|
||||||
profileId: "openai:default",
|
profileId: "openai:default",
|
||||||
apply: async (agentDir) => {
|
apply: async (agentDir) => {
|
||||||
await setOpenaiApiKey("sk-openai-env", agentDir, { secretInputMode: "ref" }); // pragma: allowlist secret
|
upsertApiKeyProfile({
|
||||||
|
provider: "openai",
|
||||||
|
input: "sk-openai-env",
|
||||||
|
agentDir,
|
||||||
|
options: { secretInputMode: "ref" }, // pragma: allowlist secret
|
||||||
|
});
|
||||||
},
|
},
|
||||||
expected: {
|
expected: {
|
||||||
keyRef: { source: "env", provider: "default", id: "OPENAI_API_KEY" },
|
keyRef: { source: "env", provider: "default", id: "OPENAI_API_KEY" },
|
||||||
@@ -192,8 +202,18 @@ describe("onboard auth credentials secret refs", () => {
|
|||||||
process.env.VOLCANO_ENGINE_API_KEY = "volcengine-secret"; // pragma: allowlist secret
|
process.env.VOLCANO_ENGINE_API_KEY = "volcengine-secret"; // pragma: allowlist secret
|
||||||
process.env.BYTEPLUS_API_KEY = "byteplus-secret"; // pragma: allowlist secret
|
process.env.BYTEPLUS_API_KEY = "byteplus-secret"; // pragma: allowlist secret
|
||||||
|
|
||||||
await setVolcengineApiKey("volcengine-secret", env.agentDir, { secretInputMode: "ref" }); // pragma: allowlist secret
|
upsertApiKeyProfile({
|
||||||
await setByteplusApiKey("byteplus-secret", env.agentDir, { secretInputMode: "ref" }); // pragma: allowlist secret
|
provider: "volcengine",
|
||||||
|
input: "volcengine-secret",
|
||||||
|
agentDir: env.agentDir,
|
||||||
|
options: { secretInputMode: "ref" }, // pragma: allowlist secret
|
||||||
|
});
|
||||||
|
upsertApiKeyProfile({
|
||||||
|
provider: "byteplus",
|
||||||
|
input: "byteplus-secret",
|
||||||
|
agentDir: env.agentDir,
|
||||||
|
options: { secretInputMode: "ref" }, // pragma: allowlist secret
|
||||||
|
});
|
||||||
|
|
||||||
const parsed = await readAuthProfilesForAgent<{
|
const parsed = await readAuthProfilesForAgent<{
|
||||||
profiles?: Record<string, { key?: string; keyRef?: unknown }>;
|
profiles?: Record<string, { key?: string; keyRef?: unknown }>;
|
||||||
@@ -215,9 +235,14 @@ describe("onboard auth credentials secret refs", () => {
|
|||||||
lifecycle.setStateDir(env.stateDir);
|
lifecycle.setStateDir(env.stateDir);
|
||||||
process.env.OPENCODE_API_KEY = "sk-opencode-env"; // pragma: allowlist secret
|
process.env.OPENCODE_API_KEY = "sk-opencode-env"; // pragma: allowlist secret
|
||||||
|
|
||||||
await setOpencodeZenApiKey("sk-opencode-env", env.agentDir, {
|
for (const provider of ["opencode", "opencode-go"] as const) {
|
||||||
secretInputMode: "ref", // pragma: allowlist secret
|
upsertApiKeyProfile({
|
||||||
});
|
provider,
|
||||||
|
input: "sk-opencode-env",
|
||||||
|
agentDir: env.agentDir,
|
||||||
|
options: { secretInputMode: "ref" }, // pragma: allowlist secret
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const parsed = await readAuthProfilesForAgent<{
|
const parsed = await readAuthProfilesForAgent<{
|
||||||
profiles?: Record<string, { key?: string; keyRef?: unknown }>;
|
profiles?: Record<string, { key?: string; keyRef?: unknown }>;
|
||||||
|
|||||||
@@ -3,8 +3,11 @@ import os from "node:os";
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import type { OAuthCredentials } from "@mariozechner/pi-ai";
|
import type { OAuthCredentials } from "@mariozechner/pi-ai";
|
||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
import { applyAuthProfileConfig } from "../plugins/provider-auth-helpers.js";
|
import {
|
||||||
import { setMinimaxApiKey, writeOAuthCredentials } from "../plugins/provider-auth-storage.js";
|
applyAuthProfileConfig,
|
||||||
|
upsertApiKeyProfile,
|
||||||
|
writeOAuthCredentials,
|
||||||
|
} from "../plugins/provider-auth-helpers.js";
|
||||||
import {
|
import {
|
||||||
createAuthTestLifecycle,
|
createAuthTestLifecycle,
|
||||||
readAuthProfilesForAgent,
|
readAuthProfilesForAgent,
|
||||||
@@ -163,7 +166,7 @@ describe("writeOAuthCredentials", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("setMinimaxApiKey", () => {
|
describe("upsertApiKeyProfile", () => {
|
||||||
const lifecycle = createAuthTestLifecycle([
|
const lifecycle = createAuthTestLifecycle([
|
||||||
"OPENCLAW_STATE_DIR",
|
"OPENCLAW_STATE_DIR",
|
||||||
"OPENCLAW_AGENT_DIR",
|
"OPENCLAW_AGENT_DIR",
|
||||||
@@ -178,7 +181,7 @@ describe("setMinimaxApiKey", () => {
|
|||||||
const env = await setupAuthTestEnv("openclaw-minimax-", { agentSubdir: "custom-agent" });
|
const env = await setupAuthTestEnv("openclaw-minimax-", { agentSubdir: "custom-agent" });
|
||||||
lifecycle.setStateDir(env.stateDir);
|
lifecycle.setStateDir(env.stateDir);
|
||||||
|
|
||||||
await setMinimaxApiKey("sk-minimax-test");
|
upsertApiKeyProfile({ provider: "minimax", input: "sk-minimax-test" });
|
||||||
|
|
||||||
const parsed = await readAuthProfilesForAgent<{
|
const parsed = await readAuthProfilesForAgent<{
|
||||||
profiles?: Record<string, { type?: string; provider?: string; key?: string }>;
|
profiles?: Record<string, { type?: string; provider?: string; key?: string }>;
|
||||||
|
|||||||
@@ -103,6 +103,28 @@ export function buildApiKeyCredential(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function upsertApiKeyProfile(params: {
|
||||||
|
provider: string;
|
||||||
|
input: SecretInput;
|
||||||
|
agentDir?: string;
|
||||||
|
options?: ApiKeyStorageOptions;
|
||||||
|
profileId?: string;
|
||||||
|
metadata?: Record<string, string>;
|
||||||
|
}): string {
|
||||||
|
const profileId = params.profileId ?? buildAuthProfileId({ providerId: params.provider });
|
||||||
|
upsertAuthProfile({
|
||||||
|
profileId,
|
||||||
|
credential: buildApiKeyCredential(
|
||||||
|
params.provider,
|
||||||
|
params.input,
|
||||||
|
params.metadata,
|
||||||
|
params.options,
|
||||||
|
),
|
||||||
|
agentDir: resolveAuthAgentDir(params.agentDir),
|
||||||
|
});
|
||||||
|
return profileId;
|
||||||
|
}
|
||||||
|
|
||||||
export function applyAuthProfileConfig(
|
export function applyAuthProfileConfig(
|
||||||
cfg: OpenClawConfig,
|
cfg: OpenClawConfig,
|
||||||
params: {
|
params: {
|
||||||
|
|||||||
@@ -1,201 +0,0 @@
|
|||||||
import { resolveOpenClawAgentDir } from "../agents/agent-paths.js";
|
|
||||||
import { upsertAuthProfile } from "../agents/auth-profiles.js";
|
|
||||||
import type { SecretInput } from "../config/types.secrets.js";
|
|
||||||
import {
|
|
||||||
buildApiKeyCredential,
|
|
||||||
type ApiKeyStorageOptions,
|
|
||||||
writeOAuthCredentials,
|
|
||||||
type WriteOAuthCredentialsOptions,
|
|
||||||
} from "./provider-auth-helpers.js";
|
|
||||||
|
|
||||||
const resolveAuthAgentDir = (agentDir?: string) => agentDir ?? resolveOpenClawAgentDir();
|
|
||||||
|
|
||||||
type ProviderApiKeySetter = (
|
|
||||||
key: SecretInput,
|
|
||||||
agentDir?: string,
|
|
||||||
options?: ApiKeyStorageOptions,
|
|
||||||
) => Promise<void> | void;
|
|
||||||
|
|
||||||
function upsertProviderApiKeyProfile(params: {
|
|
||||||
provider: string;
|
|
||||||
key: SecretInput;
|
|
||||||
agentDir?: string;
|
|
||||||
options?: ApiKeyStorageOptions;
|
|
||||||
profileId?: string;
|
|
||||||
metadata?: Record<string, string>;
|
|
||||||
}) {
|
|
||||||
upsertAuthProfile({
|
|
||||||
profileId: params.profileId ?? `${params.provider}:default`,
|
|
||||||
credential: buildApiKeyCredential(params.provider, params.key, params.metadata, params.options),
|
|
||||||
agentDir: resolveAuthAgentDir(params.agentDir),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function createProviderApiKeySetter(
|
|
||||||
provider: string,
|
|
||||||
resolveKey: (key: SecretInput) => SecretInput = (key) => key,
|
|
||||||
): ProviderApiKeySetter {
|
|
||||||
return async (key, agentDir, options) => {
|
|
||||||
upsertProviderApiKeyProfile({
|
|
||||||
provider,
|
|
||||||
key: resolveKey(key),
|
|
||||||
agentDir,
|
|
||||||
options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProviderApiKeySetterSpec = {
|
|
||||||
provider: string;
|
|
||||||
resolveKey?: (key: SecretInput) => SecretInput;
|
|
||||||
};
|
|
||||||
|
|
||||||
function createProviderApiKeySetters<const T extends Record<string, ProviderApiKeySetterSpec>>(
|
|
||||||
specs: T,
|
|
||||||
): { [K in keyof T]: ProviderApiKeySetter } {
|
|
||||||
const entries = Object.entries(specs).map(([name, spec]) => [
|
|
||||||
name,
|
|
||||||
createProviderApiKeySetter(spec.provider, spec.resolveKey),
|
|
||||||
]);
|
|
||||||
return Object.fromEntries(entries) as { [K in keyof T]: ProviderApiKeySetter };
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
buildApiKeyCredential,
|
|
||||||
type ApiKeyStorageOptions,
|
|
||||||
writeOAuthCredentials,
|
|
||||||
type WriteOAuthCredentialsOptions,
|
|
||||||
};
|
|
||||||
|
|
||||||
const {
|
|
||||||
setAnthropicApiKey,
|
|
||||||
setOpenaiApiKey,
|
|
||||||
setGeminiApiKey,
|
|
||||||
setMoonshotApiKey,
|
|
||||||
setKimiCodingApiKey,
|
|
||||||
setVolcengineApiKey,
|
|
||||||
setByteplusApiKey,
|
|
||||||
setSyntheticApiKey,
|
|
||||||
setVeniceApiKey,
|
|
||||||
setZaiApiKey,
|
|
||||||
setXiaomiApiKey,
|
|
||||||
setOpenrouterApiKey,
|
|
||||||
setLitellmApiKey,
|
|
||||||
setVercelAiGatewayApiKey,
|
|
||||||
setTogetherApiKey,
|
|
||||||
setHuggingfaceApiKey,
|
|
||||||
setQianfanApiKey,
|
|
||||||
setQwenApiKey,
|
|
||||||
setModelStudioApiKey,
|
|
||||||
setXaiApiKey,
|
|
||||||
setMistralApiKey,
|
|
||||||
setKilocodeApiKey,
|
|
||||||
} = createProviderApiKeySetters({
|
|
||||||
setAnthropicApiKey: { provider: "anthropic" },
|
|
||||||
setOpenaiApiKey: { provider: "openai" },
|
|
||||||
setGeminiApiKey: { provider: "google" },
|
|
||||||
setMoonshotApiKey: { provider: "moonshot" },
|
|
||||||
setKimiCodingApiKey: { provider: "kimi" },
|
|
||||||
setVolcengineApiKey: { provider: "volcengine" },
|
|
||||||
setByteplusApiKey: { provider: "byteplus" },
|
|
||||||
setSyntheticApiKey: { provider: "synthetic" },
|
|
||||||
setVeniceApiKey: { provider: "venice" },
|
|
||||||
setZaiApiKey: { provider: "zai" },
|
|
||||||
setXiaomiApiKey: { provider: "xiaomi" },
|
|
||||||
setOpenrouterApiKey: {
|
|
||||||
provider: "openrouter",
|
|
||||||
resolveKey: (key) => (typeof key === "string" && key === "undefined" ? "" : key),
|
|
||||||
},
|
|
||||||
setLitellmApiKey: { provider: "litellm" },
|
|
||||||
setVercelAiGatewayApiKey: { provider: "vercel-ai-gateway" },
|
|
||||||
setTogetherApiKey: { provider: "together" },
|
|
||||||
setHuggingfaceApiKey: { provider: "huggingface" },
|
|
||||||
setQianfanApiKey: { provider: "qianfan" },
|
|
||||||
setQwenApiKey: { provider: "qwen" },
|
|
||||||
setModelStudioApiKey: { provider: "qwen" },
|
|
||||||
setXaiApiKey: { provider: "xai" },
|
|
||||||
setMistralApiKey: { provider: "mistral" },
|
|
||||||
setKilocodeApiKey: { provider: "kilocode" },
|
|
||||||
});
|
|
||||||
|
|
||||||
export {
|
|
||||||
setAnthropicApiKey,
|
|
||||||
setOpenaiApiKey,
|
|
||||||
setGeminiApiKey,
|
|
||||||
setMoonshotApiKey,
|
|
||||||
setKimiCodingApiKey,
|
|
||||||
setVolcengineApiKey,
|
|
||||||
setByteplusApiKey,
|
|
||||||
setSyntheticApiKey,
|
|
||||||
setVeniceApiKey,
|
|
||||||
setZaiApiKey,
|
|
||||||
setXiaomiApiKey,
|
|
||||||
setOpenrouterApiKey,
|
|
||||||
setLitellmApiKey,
|
|
||||||
setVercelAiGatewayApiKey,
|
|
||||||
setTogetherApiKey,
|
|
||||||
setHuggingfaceApiKey,
|
|
||||||
setQianfanApiKey,
|
|
||||||
setQwenApiKey,
|
|
||||||
setModelStudioApiKey,
|
|
||||||
setXaiApiKey,
|
|
||||||
setMistralApiKey,
|
|
||||||
setKilocodeApiKey,
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function setMinimaxApiKey(
|
|
||||||
key: SecretInput,
|
|
||||||
agentDir?: string,
|
|
||||||
profileId: string = "minimax:default",
|
|
||||||
options?: ApiKeyStorageOptions,
|
|
||||||
) {
|
|
||||||
const provider = profileId.split(":")[0] ?? "minimax";
|
|
||||||
upsertProviderApiKeyProfile({ provider, key, agentDir, options, profileId });
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function setCloudflareAiGatewayConfig(
|
|
||||||
accountId: string,
|
|
||||||
gatewayId: string,
|
|
||||||
apiKey: SecretInput,
|
|
||||||
agentDir?: string,
|
|
||||||
options?: ApiKeyStorageOptions,
|
|
||||||
) {
|
|
||||||
const normalizedAccountId = accountId.trim();
|
|
||||||
const normalizedGatewayId = gatewayId.trim();
|
|
||||||
upsertProviderApiKeyProfile({
|
|
||||||
provider: "cloudflare-ai-gateway",
|
|
||||||
key: apiKey,
|
|
||||||
agentDir,
|
|
||||||
options,
|
|
||||||
metadata: {
|
|
||||||
accountId: normalizedAccountId,
|
|
||||||
gatewayId: normalizedGatewayId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function setOpencodeZenApiKey(
|
|
||||||
key: SecretInput,
|
|
||||||
agentDir?: string,
|
|
||||||
options?: ApiKeyStorageOptions,
|
|
||||||
) {
|
|
||||||
await setSharedOpencodeApiKey(key, agentDir, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function setOpencodeGoApiKey(
|
|
||||||
key: SecretInput,
|
|
||||||
agentDir?: string,
|
|
||||||
options?: ApiKeyStorageOptions,
|
|
||||||
) {
|
|
||||||
await setSharedOpencodeApiKey(key, agentDir, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function setSharedOpencodeApiKey(
|
|
||||||
key: SecretInput,
|
|
||||||
agentDir?: string,
|
|
||||||
options?: ApiKeyStorageOptions,
|
|
||||||
) {
|
|
||||||
for (const provider of ["opencode", "opencode-go"] as const) {
|
|
||||||
upsertProviderApiKeyProfile({ provider, key, agentDir, options });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user