perf(test): split secrets runtime provider coverage

This commit is contained in:
Peter Steinberger
2026-04-06 14:40:27 +01:00
parent 3da7c8610f
commit 55f18f67e2
3 changed files with 816 additions and 591 deletions

View File

@@ -0,0 +1,487 @@
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import { createEmptyPluginRegistry } from "../plugins/registry.js";
import { setActivePluginRegistry } from "../plugins/runtime.js";
import type { PluginWebSearchProviderEntry } from "../plugins/types.js";
type WebProviderUnderTest = "brave" | "gemini" | "grok" | "kimi" | "perplexity" | "firecrawl";
const { resolvePluginWebSearchProvidersMock } = vi.hoisted(() => ({
resolvePluginWebSearchProvidersMock: vi.fn(() => buildTestWebSearchProviders()),
}));
vi.mock("../plugins/web-search-providers.runtime.js", () => ({
resolvePluginWebSearchProviders: resolvePluginWebSearchProvidersMock,
}));
function asConfig(value: unknown): OpenClawConfig {
return value as OpenClawConfig;
}
function createTestProvider(params: {
id: WebProviderUnderTest;
pluginId: string;
order: number;
}): PluginWebSearchProviderEntry {
const credentialPath = `plugins.entries.${params.pluginId}.config.webSearch.apiKey`;
const readSearchConfigKey = (searchConfig?: Record<string, unknown>): unknown => {
const providerConfig =
searchConfig?.[params.id] && typeof searchConfig[params.id] === "object"
? (searchConfig[params.id] as { apiKey?: unknown })
: undefined;
return providerConfig?.apiKey ?? searchConfig?.apiKey;
};
return {
pluginId: params.pluginId,
id: params.id,
label: params.id,
hint: `${params.id} test provider`,
envVars: [`${params.id.toUpperCase()}_API_KEY`],
placeholder: `${params.id}-...`,
signupUrl: `https://example.com/${params.id}`,
autoDetectOrder: params.order,
credentialPath,
inactiveSecretPaths: [credentialPath],
getCredentialValue: readSearchConfigKey,
setCredentialValue: (searchConfigTarget, value) => {
const providerConfig =
params.id === "brave" || params.id === "firecrawl"
? searchConfigTarget
: ((searchConfigTarget[params.id] ??= {}) as { apiKey?: unknown });
providerConfig.apiKey = value;
},
getConfiguredCredentialValue: (config) =>
(config?.plugins?.entries?.[params.pluginId]?.config as { webSearch?: { apiKey?: unknown } })
?.webSearch?.apiKey,
setConfiguredCredentialValue: (configTarget, value) => {
const plugins = (configTarget.plugins ??= {}) as { entries?: Record<string, unknown> };
const entries = (plugins.entries ??= {});
const entry = (entries[params.pluginId] ??= {}) as { config?: Record<string, unknown> };
const config = (entry.config ??= {});
const webSearch = (config.webSearch ??= {}) as { apiKey?: unknown };
webSearch.apiKey = value;
},
resolveRuntimeMetadata:
params.id === "perplexity"
? () => ({
perplexityTransport: "search_api" as const,
})
: undefined,
createTool: () => null,
};
}
function buildTestWebSearchProviders(): PluginWebSearchProviderEntry[] {
return [
createTestProvider({ id: "brave", pluginId: "brave", order: 10 }),
createTestProvider({ id: "gemini", pluginId: "google", order: 20 }),
createTestProvider({ id: "grok", pluginId: "xai", order: 30 }),
createTestProvider({ id: "kimi", pluginId: "moonshot", order: 40 }),
createTestProvider({ id: "perplexity", pluginId: "perplexity", order: 50 }),
createTestProvider({ id: "firecrawl", pluginId: "firecrawl", order: 60 }),
];
}
function createOpenAiFileModelsConfig(): NonNullable<OpenClawConfig["models"]> {
return {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
apiKey: { source: "file", provider: "default", id: "/providers/openai/apiKey" },
models: [],
},
},
};
}
let clearConfigCache: typeof import("../config/config.js").clearConfigCache;
let clearRuntimeConfigSnapshot: typeof import("../config/config.js").clearRuntimeConfigSnapshot;
let clearSecretsRuntimeSnapshot: typeof import("./runtime.js").clearSecretsRuntimeSnapshot;
let prepareSecretsRuntimeSnapshot: typeof import("./runtime.js").prepareSecretsRuntimeSnapshot;
describe("secrets runtime provider and media surfaces", () => {
beforeAll(async () => {
({ clearConfigCache, clearRuntimeConfigSnapshot } = await import("../config/config.js"));
({ clearSecretsRuntimeSnapshot, prepareSecretsRuntimeSnapshot } = await import("./runtime.js"));
});
beforeEach(() => {
resolvePluginWebSearchProvidersMock.mockReset();
resolvePluginWebSearchProvidersMock.mockReturnValue(buildTestWebSearchProviders());
});
afterEach(() => {
setActivePluginRegistry(createEmptyPluginRegistry());
clearSecretsRuntimeSnapshot();
clearRuntimeConfigSnapshot();
clearConfigCache();
});
it("resolves file refs via configured file provider", async () => {
if (process.platform === "win32") {
return;
}
const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-secrets-file-provider-"));
const secretsPath = path.join(root, "secrets.json");
try {
await fs.writeFile(
secretsPath,
JSON.stringify(
{
providers: {
openai: {
apiKey: "sk-from-file-provider",
},
},
},
null,
2,
),
"utf8",
);
await fs.chmod(secretsPath, 0o600);
const config = asConfig({
secrets: {
providers: {
default: {
source: "file",
path: secretsPath,
mode: "json",
},
},
defaults: {
file: "default",
},
},
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
apiKey: { source: "file", provider: "default", id: "/providers/openai/apiKey" },
models: [],
},
},
},
});
const snapshot = await prepareSecretsRuntimeSnapshot({
config,
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }),
});
expect(snapshot.config.models?.providers?.openai?.apiKey).toBe("sk-from-file-provider");
} finally {
await fs.rm(root, { recursive: true, force: true });
}
});
it("fails when file provider payload is not a JSON object", async () => {
if (process.platform === "win32") {
return;
}
const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-secrets-file-provider-bad-"));
const secretsPath = path.join(root, "secrets.json");
try {
await fs.writeFile(secretsPath, JSON.stringify(["not-an-object"]), "utf8");
await fs.chmod(secretsPath, 0o600);
await expect(
prepareSecretsRuntimeSnapshot({
config: asConfig({
secrets: {
providers: {
default: {
source: "file",
path: secretsPath,
mode: "json",
},
},
},
models: {
...createOpenAiFileModelsConfig(),
},
}),
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }),
}),
).rejects.toThrow("payload is not a JSON object");
} finally {
await fs.rm(root, { recursive: true, force: true });
}
});
it("resolves shared media model request refs when capability blocks are omitted", async () => {
const snapshot = await prepareSecretsRuntimeSnapshot({
config: asConfig({
tools: {
media: {
models: [
{
provider: "openai",
model: "gpt-4o-mini-transcribe",
capabilities: ["audio"],
request: {
auth: {
mode: "authorization-bearer",
token: {
source: "env",
provider: "default",
id: "MEDIA_SHARED_AUDIO_TOKEN",
},
},
},
},
],
},
},
}),
env: {
MEDIA_SHARED_AUDIO_TOKEN: "shared-audio-token",
},
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }),
});
expect(snapshot.config.tools?.media?.models?.[0]?.request?.auth).toEqual({
mode: "authorization-bearer",
token: "shared-audio-token",
});
expect(snapshot.warnings.map((warning) => warning.path)).not.toContain(
"tools.media.models.0.request.auth.token",
);
});
it("treats shared media model request refs as inactive when their capabilities are disabled", async () => {
const sharedTokenRef = {
source: "env" as const,
provider: "default" as const,
id: "MEDIA_DISABLED_AUDIO_TOKEN",
};
const snapshot = await prepareSecretsRuntimeSnapshot({
config: asConfig({
tools: {
media: {
models: [
{
provider: "openai",
model: "gpt-4o-mini-transcribe",
capabilities: ["audio"],
request: {
auth: {
mode: "authorization-bearer",
token: sharedTokenRef,
},
},
},
],
audio: {
enabled: false,
},
},
},
}),
env: {},
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }),
});
expect(snapshot.config.tools?.media?.models?.[0]?.request?.auth).toEqual({
mode: "authorization-bearer",
token: sharedTokenRef,
});
expect(snapshot.warnings.map((warning) => warning.path)).toContain(
"tools.media.models.0.request.auth.token",
);
});
it("resolves shared media model request refs from inferred provider capabilities", async () => {
const pluginRegistry = createEmptyPluginRegistry();
pluginRegistry.mediaUnderstandingProviders.push({
pluginId: "deepgram",
pluginName: "Deepgram Plugin",
source: "test",
provider: {
id: "deepgram",
capabilities: ["audio"],
},
});
setActivePluginRegistry(pluginRegistry);
const snapshot = await prepareSecretsRuntimeSnapshot({
config: asConfig({
tools: {
media: {
models: [
{
provider: "deepgram",
request: {
auth: {
mode: "authorization-bearer",
token: {
source: "env",
provider: "default",
id: "MEDIA_INFERRED_AUDIO_TOKEN",
},
},
},
},
],
},
},
}),
env: {
MEDIA_INFERRED_AUDIO_TOKEN: "inferred-audio-token",
},
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }),
});
expect(snapshot.config.tools?.media?.models?.[0]?.request?.auth).toEqual({
mode: "authorization-bearer",
token: "inferred-audio-token",
});
expect(snapshot.warnings.map((warning) => warning.path)).not.toContain(
"tools.media.models.0.request.auth.token",
);
});
it("treats shared media model request refs as inactive when inferred capabilities are disabled", async () => {
const pluginRegistry = createEmptyPluginRegistry();
pluginRegistry.mediaUnderstandingProviders.push({
pluginId: "deepgram",
pluginName: "Deepgram Plugin",
source: "test",
provider: {
id: "deepgram",
capabilities: ["audio"],
},
});
setActivePluginRegistry(pluginRegistry);
const inferredTokenRef = {
source: "env" as const,
provider: "default" as const,
id: "MEDIA_INFERRED_DISABLED_AUDIO_TOKEN",
};
const snapshot = await prepareSecretsRuntimeSnapshot({
config: asConfig({
tools: {
media: {
models: [
{
provider: "deepgram",
request: {
auth: {
mode: "authorization-bearer",
token: inferredTokenRef,
},
},
},
],
audio: {
enabled: false,
},
},
},
}),
env: {},
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }),
});
expect(snapshot.config.tools?.media?.models?.[0]?.request?.auth).toEqual({
mode: "authorization-bearer",
token: inferredTokenRef,
});
expect(snapshot.warnings.map((warning) => warning.path)).toContain(
"tools.media.models.0.request.auth.token",
);
});
it("treats section media model request refs as inactive when model capabilities exclude the section", async () => {
const sectionTokenRef = {
source: "env" as const,
provider: "default" as const,
id: "MEDIA_AUDIO_SECTION_FILTERED_TOKEN",
};
const snapshot = await prepareSecretsRuntimeSnapshot({
config: asConfig({
tools: {
media: {
audio: {
enabled: true,
models: [
{
provider: "openai",
capabilities: ["video"],
request: {
auth: {
mode: "authorization-bearer",
token: sectionTokenRef,
},
},
},
],
},
},
},
}),
env: {},
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }),
});
expect(snapshot.config.tools?.media?.audio?.models?.[0]?.request?.auth).toEqual({
mode: "authorization-bearer",
token: sectionTokenRef,
});
expect(snapshot.warnings.map((warning) => warning.path)).toContain(
"tools.media.audio.models.0.request.auth.token",
);
});
it("treats defaults memorySearch ref as inactive when all enabled agents disable memorySearch", async () => {
const snapshot = await prepareSecretsRuntimeSnapshot({
config: asConfig({
agents: {
defaults: {
memorySearch: {
remote: {
apiKey: {
source: "env",
provider: "default",
id: "DEFAULT_MEMORY_REMOTE_API_KEY",
},
},
},
},
list: [
{
enabled: true,
memorySearch: {
enabled: false,
},
},
],
},
}),
env: {},
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }),
});
expect(snapshot.config.agents?.defaults?.memorySearch?.remote?.apiKey).toEqual({
source: "env",
provider: "default",
id: "DEFAULT_MEMORY_REMOTE_API_KEY",
});
expect(snapshot.warnings.map((warning) => warning.path)).toContain(
"agents.defaults.memorySearch.remote.apiKey",
);
});
});

View File

@@ -0,0 +1,329 @@
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import type { AuthProfileStore } from "../agents/auth-profiles.js";
import type { OpenClawConfig } from "../config/config.js";
import { createEmptyPluginRegistry } from "../plugins/registry.js";
import { setActivePluginRegistry } from "../plugins/runtime.js";
import type { PluginWebSearchProviderEntry } from "../plugins/types.js";
type WebProviderUnderTest = "brave" | "gemini" | "grok" | "kimi" | "perplexity" | "firecrawl";
const { resolvePluginWebSearchProvidersMock } = vi.hoisted(() => ({
resolvePluginWebSearchProvidersMock: vi.fn(() => buildTestWebSearchProviders()),
}));
vi.mock("../plugins/web-search-providers.runtime.js", () => ({
resolvePluginWebSearchProviders: resolvePluginWebSearchProvidersMock,
}));
function asConfig(value: unknown): OpenClawConfig {
return value as OpenClawConfig;
}
function createTestProvider(params: {
id: WebProviderUnderTest;
pluginId: string;
order: number;
}): PluginWebSearchProviderEntry {
const credentialPath = `plugins.entries.${params.pluginId}.config.webSearch.apiKey`;
const readSearchConfigKey = (searchConfig?: Record<string, unknown>): unknown => {
const providerConfig =
searchConfig?.[params.id] && typeof searchConfig[params.id] === "object"
? (searchConfig[params.id] as { apiKey?: unknown })
: undefined;
return providerConfig?.apiKey ?? searchConfig?.apiKey;
};
return {
pluginId: params.pluginId,
id: params.id,
label: params.id,
hint: `${params.id} test provider`,
envVars: [`${params.id.toUpperCase()}_API_KEY`],
placeholder: `${params.id}-...`,
signupUrl: `https://example.com/${params.id}`,
autoDetectOrder: params.order,
credentialPath,
inactiveSecretPaths: [credentialPath],
getCredentialValue: readSearchConfigKey,
setCredentialValue: (searchConfigTarget, value) => {
const providerConfig =
params.id === "brave" || params.id === "firecrawl"
? searchConfigTarget
: ((searchConfigTarget[params.id] ??= {}) as { apiKey?: unknown });
providerConfig.apiKey = value;
},
getConfiguredCredentialValue: (config) =>
(config?.plugins?.entries?.[params.pluginId]?.config as { webSearch?: { apiKey?: unknown } })
?.webSearch?.apiKey,
setConfiguredCredentialValue: (configTarget, value) => {
const plugins = (configTarget.plugins ??= {}) as { entries?: Record<string, unknown> };
const entries = (plugins.entries ??= {});
const entry = (entries[params.pluginId] ??= {}) as { config?: Record<string, unknown> };
const config = (entry.config ??= {});
const webSearch = (config.webSearch ??= {}) as { apiKey?: unknown };
webSearch.apiKey = value;
},
resolveRuntimeMetadata:
params.id === "perplexity"
? () => ({
perplexityTransport: "search_api" as const,
})
: undefined,
createTool: () => null,
};
}
function buildTestWebSearchProviders(): PluginWebSearchProviderEntry[] {
return [
createTestProvider({ id: "brave", pluginId: "brave", order: 10 }),
createTestProvider({ id: "gemini", pluginId: "google", order: 20 }),
createTestProvider({ id: "grok", pluginId: "xai", order: 30 }),
createTestProvider({ id: "kimi", pluginId: "moonshot", order: 40 }),
createTestProvider({ id: "perplexity", pluginId: "perplexity", order: 50 }),
createTestProvider({ id: "firecrawl", pluginId: "firecrawl", order: 60 }),
];
}
function loadAuthStoreWithProfiles(profiles: AuthProfileStore["profiles"]): AuthProfileStore {
return {
version: 1,
profiles,
};
}
let clearConfigCache: typeof import("../config/config.js").clearConfigCache;
let clearRuntimeConfigSnapshot: typeof import("../config/config.js").clearRuntimeConfigSnapshot;
let clearSecretsRuntimeSnapshot: typeof import("./runtime.js").clearSecretsRuntimeSnapshot;
let prepareSecretsRuntimeSnapshot: typeof import("./runtime.js").prepareSecretsRuntimeSnapshot;
describe("secrets runtime snapshot request secret refs", () => {
beforeAll(async () => {
({ clearConfigCache, clearRuntimeConfigSnapshot } = await import("../config/config.js"));
({ clearSecretsRuntimeSnapshot, prepareSecretsRuntimeSnapshot } = await import("./runtime.js"));
});
beforeEach(() => {
resolvePluginWebSearchProvidersMock.mockReset();
resolvePluginWebSearchProvidersMock.mockReturnValue(buildTestWebSearchProviders());
});
afterEach(() => {
setActivePluginRegistry(createEmptyPluginRegistry());
clearSecretsRuntimeSnapshot();
clearRuntimeConfigSnapshot();
clearConfigCache();
});
it("can skip auth-profile SecretRef resolution when includeAuthStoreRefs is false", async () => {
const missingEnvVar = `OPENCLAW_MISSING_AUTH_PROFILE_SECRET_${Date.now()}`;
delete process.env[missingEnvVar];
const loadAuthStore = () =>
loadAuthStoreWithProfiles({
"custom:token": {
type: "token",
provider: "custom",
tokenRef: { source: "env", provider: "default", id: missingEnvVar },
},
});
await expect(
prepareSecretsRuntimeSnapshot({
config: asConfig({}),
env: {},
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore,
}),
).rejects.toThrow(`Environment variable "${missingEnvVar}" is missing or empty.`);
const snapshot = await prepareSecretsRuntimeSnapshot({
config: asConfig({}),
env: {},
includeAuthStoreRefs: false,
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore,
});
expect(snapshot.authStores).toEqual([]);
});
it("resolves model provider request secret refs for headers, auth, and tls material", async () => {
const config = asConfig({
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
request: {
headers: {
"X-Tenant": { source: "env", provider: "default", id: "OPENAI_PROVIDER_TENANT" },
},
auth: {
mode: "authorization-bearer",
token: { source: "env", provider: "default", id: "OPENAI_PROVIDER_TOKEN" },
},
proxy: {
mode: "explicit-proxy",
url: "http://proxy.example:8080",
tls: {
ca: { source: "env", provider: "default", id: "OPENAI_PROVIDER_PROXY_CA" },
},
},
tls: {
cert: { source: "env", provider: "default", id: "OPENAI_PROVIDER_CERT" },
key: { source: "env", provider: "default", id: "OPENAI_PROVIDER_KEY" },
},
},
models: [],
},
},
},
});
const snapshot = await prepareSecretsRuntimeSnapshot({
config,
env: {
OPENAI_PROVIDER_TENANT: "tenant-acme",
OPENAI_PROVIDER_TOKEN: "sk-provider-runtime", // pragma: allowlist secret
OPENAI_PROVIDER_PROXY_CA: "proxy-ca",
OPENAI_PROVIDER_CERT: "client-cert",
OPENAI_PROVIDER_KEY: "client-key",
},
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }),
});
expect(snapshot.config.models?.providers?.openai?.request).toEqual({
headers: {
"X-Tenant": "tenant-acme",
},
auth: {
mode: "authorization-bearer",
token: "sk-provider-runtime",
},
proxy: {
mode: "explicit-proxy",
url: "http://proxy.example:8080",
tls: {
ca: "proxy-ca",
},
},
tls: {
cert: "client-cert",
key: "client-key",
},
});
});
it("resolves media request secret refs for provider headers, auth, and tls material", async () => {
const snapshot = await prepareSecretsRuntimeSnapshot({
config: asConfig({
tools: {
media: {
models: [
{
provider: "openai",
model: "gpt-4o-mini-transcribe",
capabilities: ["audio"],
request: {
headers: {
"X-Shared-Tenant": {
source: "env",
provider: "default",
id: "MEDIA_SHARED_TENANT",
},
},
auth: {
mode: "header",
headerName: "x-shared-key",
value: {
source: "env",
provider: "default",
id: "MEDIA_SHARED_MODEL_KEY",
},
},
},
},
],
audio: {
enabled: true,
request: {
headers: {
"X-Tenant": { source: "env", provider: "default", id: "MEDIA_AUDIO_TENANT" },
},
auth: {
mode: "authorization-bearer",
token: { source: "env", provider: "default", id: "MEDIA_AUDIO_TOKEN" },
},
tls: {
cert: { source: "env", provider: "default", id: "MEDIA_AUDIO_CERT" },
},
},
models: [
{
provider: "deepgram",
request: {
auth: {
mode: "header",
headerName: "x-api-key",
value: { source: "env", provider: "default", id: "MEDIA_AUDIO_MODEL_KEY" },
},
proxy: {
mode: "explicit-proxy",
url: "http://proxy.example:8080",
tls: {
ca: { source: "env", provider: "default", id: "MEDIA_AUDIO_PROXY_CA" },
},
},
},
},
],
},
},
},
}),
env: {
MEDIA_SHARED_TENANT: "tenant-shared",
MEDIA_SHARED_MODEL_KEY: "shared-model-key", // pragma: allowlist secret
MEDIA_AUDIO_TENANT: "tenant-acme",
MEDIA_AUDIO_TOKEN: "audio-token", // pragma: allowlist secret
MEDIA_AUDIO_CERT: "client-cert",
MEDIA_AUDIO_MODEL_KEY: "model-key", // pragma: allowlist secret
MEDIA_AUDIO_PROXY_CA: "proxy-ca",
},
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }),
});
expect(snapshot.config.tools?.media?.audio?.request?.headers?.["X-Tenant"]).toBe("tenant-acme");
expect(snapshot.config.tools?.media?.audio?.request?.auth).toEqual({
mode: "authorization-bearer",
token: "audio-token",
});
expect(snapshot.config.tools?.media?.audio?.request?.tls).toEqual({
cert: "client-cert",
});
expect(snapshot.config.tools?.media?.models?.[0]?.request).toEqual({
headers: {
"X-Shared-Tenant": "tenant-shared",
},
auth: {
mode: "header",
headerName: "x-shared-key",
value: "shared-model-key",
},
});
expect(snapshot.config.tools?.media?.audio?.models?.[0]?.request).toEqual({
auth: {
mode: "header",
headerName: "x-api-key",
value: "model-key",
},
proxy: {
mode: "explicit-proxy",
url: "http://proxy.example:8080",
tls: {
ca: "proxy-ca",
},
},
});
});
});

View File

@@ -94,18 +94,6 @@ let activateSecretsRuntimeSnapshot: typeof import("./runtime.js").activateSecret
let clearSecretsRuntimeSnapshot: typeof import("./runtime.js").clearSecretsRuntimeSnapshot;
let prepareSecretsRuntimeSnapshot: typeof import("./runtime.js").prepareSecretsRuntimeSnapshot;
function createOpenAiFileModelsConfig(): NonNullable<OpenClawConfig["models"]> {
return {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
apiKey: { source: "file", provider: "default", id: "/providers/openai/apiKey" },
models: [],
},
},
};
}
function loadAuthStoreWithProfiles(profiles: AuthProfileStore["profiles"]): AuthProfileStore {
return {
version: 1,
@@ -135,39 +123,6 @@ describe("secrets runtime snapshot", () => {
clearConfigCache();
});
it("can skip auth-profile SecretRef resolution when includeAuthStoreRefs is false", async () => {
const missingEnvVar = `OPENCLAW_MISSING_AUTH_PROFILE_SECRET_${Date.now()}`;
delete process.env[missingEnvVar];
const loadAuthStore = () =>
loadAuthStoreWithProfiles({
"custom:token": {
type: "token",
provider: "custom",
tokenRef: { source: "env", provider: "default", id: missingEnvVar },
},
});
await expect(
prepareSecretsRuntimeSnapshot({
config: asConfig({}),
env: {},
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore,
}),
).rejects.toThrow(`Environment variable "${missingEnvVar}" is missing or empty.`);
const snapshot = await prepareSecretsRuntimeSnapshot({
config: asConfig({}),
env: {},
includeAuthStoreRefs: false,
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore,
});
expect(snapshot.authStores).toEqual([]);
});
it("resolves sandbox ssh secret refs for active ssh backends", async () => {
const snapshot = await prepareSecretsRuntimeSnapshot({
config: asConfig({
@@ -324,512 +279,6 @@ describe("secrets runtime snapshot", () => {
expect(profile.key).toBe("primary-key-value");
});
it("resolves model provider request secret refs for headers, auth, and tls material", async () => {
const config = asConfig({
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
request: {
headers: {
"X-Tenant": { source: "env", provider: "default", id: "OPENAI_PROVIDER_TENANT" },
},
auth: {
mode: "authorization-bearer",
token: { source: "env", provider: "default", id: "OPENAI_PROVIDER_TOKEN" },
},
proxy: {
mode: "explicit-proxy",
url: "http://proxy.example:8080",
tls: {
ca: { source: "env", provider: "default", id: "OPENAI_PROVIDER_PROXY_CA" },
},
},
tls: {
cert: { source: "env", provider: "default", id: "OPENAI_PROVIDER_CERT" },
key: { source: "env", provider: "default", id: "OPENAI_PROVIDER_KEY" },
},
},
models: [],
},
},
},
});
const snapshot = await prepareSecretsRuntimeSnapshot({
config,
env: {
OPENAI_PROVIDER_TENANT: "tenant-acme",
OPENAI_PROVIDER_TOKEN: "sk-provider-runtime", // pragma: allowlist secret
OPENAI_PROVIDER_PROXY_CA: "proxy-ca",
OPENAI_PROVIDER_CERT: "client-cert",
OPENAI_PROVIDER_KEY: "client-key",
},
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }),
});
expect(snapshot.config.models?.providers?.openai?.request).toEqual({
headers: {
"X-Tenant": "tenant-acme",
},
auth: {
mode: "authorization-bearer",
token: "sk-provider-runtime",
},
proxy: {
mode: "explicit-proxy",
url: "http://proxy.example:8080",
tls: {
ca: "proxy-ca",
},
},
tls: {
cert: "client-cert",
key: "client-key",
},
});
});
it("resolves file refs via configured file provider", async () => {
if (process.platform === "win32") {
return;
}
const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-secrets-file-provider-"));
const secretsPath = path.join(root, "secrets.json");
try {
await fs.writeFile(
secretsPath,
JSON.stringify(
{
providers: {
openai: {
apiKey: "sk-from-file-provider", // pragma: allowlist secret
},
},
},
null,
2,
),
"utf8",
);
await fs.chmod(secretsPath, 0o600);
const config = asConfig({
secrets: {
providers: {
default: {
source: "file",
path: secretsPath,
mode: "json",
},
},
defaults: {
file: "default",
},
},
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
apiKey: { source: "file", provider: "default", id: "/providers/openai/apiKey" },
models: [],
},
},
},
});
const snapshot = await prepareSecretsRuntimeSnapshot({
config,
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }),
});
expect(snapshot.config.models?.providers?.openai?.apiKey).toBe("sk-from-file-provider");
} finally {
await fs.rm(root, { recursive: true, force: true });
}
});
it("fails when file provider payload is not a JSON object", async () => {
if (process.platform === "win32") {
return;
}
const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-secrets-file-provider-bad-"));
const secretsPath = path.join(root, "secrets.json");
try {
await fs.writeFile(secretsPath, JSON.stringify(["not-an-object"]), "utf8");
await fs.chmod(secretsPath, 0o600);
await expect(
prepareSecretsRuntimeSnapshot({
config: asConfig({
secrets: {
providers: {
default: {
source: "file",
path: secretsPath,
mode: "json",
},
},
},
models: {
...createOpenAiFileModelsConfig(),
},
}),
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }),
}),
).rejects.toThrow("payload is not a JSON object");
} finally {
await fs.rm(root, { recursive: true, force: true });
}
});
it("resolves media request secret refs for provider headers, auth, and tls material", async () => {
const snapshot = await prepareSecretsRuntimeSnapshot({
config: asConfig({
tools: {
media: {
models: [
{
provider: "openai",
model: "gpt-4o-mini-transcribe",
capabilities: ["audio"],
request: {
headers: {
"X-Shared-Tenant": {
source: "env",
provider: "default",
id: "MEDIA_SHARED_TENANT",
},
},
auth: {
mode: "header",
headerName: "x-shared-key",
value: {
source: "env",
provider: "default",
id: "MEDIA_SHARED_MODEL_KEY",
},
},
},
},
],
audio: {
enabled: true,
request: {
headers: {
"X-Tenant": { source: "env", provider: "default", id: "MEDIA_AUDIO_TENANT" },
},
auth: {
mode: "authorization-bearer",
token: { source: "env", provider: "default", id: "MEDIA_AUDIO_TOKEN" },
},
tls: {
cert: { source: "env", provider: "default", id: "MEDIA_AUDIO_CERT" },
},
},
models: [
{
provider: "deepgram",
request: {
auth: {
mode: "header",
headerName: "x-api-key",
value: { source: "env", provider: "default", id: "MEDIA_AUDIO_MODEL_KEY" },
},
proxy: {
mode: "explicit-proxy",
url: "http://proxy.example:8080",
tls: {
ca: { source: "env", provider: "default", id: "MEDIA_AUDIO_PROXY_CA" },
},
},
},
},
],
},
},
},
}),
env: {
MEDIA_SHARED_TENANT: "tenant-shared",
MEDIA_SHARED_MODEL_KEY: "shared-model-key", // pragma: allowlist secret
MEDIA_AUDIO_TENANT: "tenant-acme",
MEDIA_AUDIO_TOKEN: "audio-token", // pragma: allowlist secret
MEDIA_AUDIO_CERT: "client-cert",
MEDIA_AUDIO_MODEL_KEY: "model-key", // pragma: allowlist secret
MEDIA_AUDIO_PROXY_CA: "proxy-ca",
},
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }),
});
expect(snapshot.config.tools?.media?.audio?.request?.headers?.["X-Tenant"]).toBe("tenant-acme");
expect(snapshot.config.tools?.media?.audio?.request?.auth).toEqual({
mode: "authorization-bearer",
token: "audio-token",
});
expect(snapshot.config.tools?.media?.audio?.request?.tls).toEqual({
cert: "client-cert",
});
expect(snapshot.config.tools?.media?.models?.[0]?.request).toEqual({
headers: {
"X-Shared-Tenant": "tenant-shared",
},
auth: {
mode: "header",
headerName: "x-shared-key",
value: "shared-model-key",
},
});
expect(snapshot.config.tools?.media?.audio?.models?.[0]?.request).toEqual({
auth: {
mode: "header",
headerName: "x-api-key",
value: "model-key",
},
proxy: {
mode: "explicit-proxy",
url: "http://proxy.example:8080",
tls: {
ca: "proxy-ca",
},
},
});
});
it("resolves shared media model request refs when capability blocks are omitted", async () => {
const snapshot = await prepareSecretsRuntimeSnapshot({
config: asConfig({
tools: {
media: {
models: [
{
provider: "openai",
model: "gpt-4o-mini-transcribe",
capabilities: ["audio"],
request: {
auth: {
mode: "authorization-bearer",
token: {
source: "env",
provider: "default",
id: "MEDIA_SHARED_AUDIO_TOKEN",
},
},
},
},
],
},
},
}),
env: {
MEDIA_SHARED_AUDIO_TOKEN: "shared-audio-token", // pragma: allowlist secret
},
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }),
});
expect(snapshot.config.tools?.media?.models?.[0]?.request?.auth).toEqual({
mode: "authorization-bearer",
token: "shared-audio-token",
});
expect(snapshot.warnings.map((warning) => warning.path)).not.toContain(
"tools.media.models.0.request.auth.token",
);
});
it("treats shared media model request refs as inactive when their capabilities are disabled", async () => {
const sharedTokenRef = {
source: "env" as const,
provider: "default" as const,
id: "MEDIA_DISABLED_AUDIO_TOKEN",
};
const snapshot = await prepareSecretsRuntimeSnapshot({
config: asConfig({
tools: {
media: {
models: [
{
provider: "openai",
model: "gpt-4o-mini-transcribe",
capabilities: ["audio"],
request: {
auth: {
mode: "authorization-bearer",
token: sharedTokenRef,
},
},
},
],
audio: {
enabled: false,
},
},
},
}),
env: {},
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }),
});
expect(snapshot.config.tools?.media?.models?.[0]?.request?.auth).toEqual({
mode: "authorization-bearer",
token: sharedTokenRef,
});
expect(snapshot.warnings.map((warning) => warning.path)).toContain(
"tools.media.models.0.request.auth.token",
);
});
it("resolves shared media model request refs from inferred provider capabilities", async () => {
const pluginRegistry = createEmptyPluginRegistry();
pluginRegistry.mediaUnderstandingProviders.push({
pluginId: "deepgram",
pluginName: "Deepgram Plugin",
source: "test",
provider: {
id: "deepgram",
capabilities: ["audio"],
},
});
setActivePluginRegistry(pluginRegistry);
const snapshot = await prepareSecretsRuntimeSnapshot({
config: asConfig({
tools: {
media: {
models: [
{
provider: "deepgram",
request: {
auth: {
mode: "authorization-bearer",
token: {
source: "env",
provider: "default",
id: "MEDIA_INFERRED_AUDIO_TOKEN",
},
},
},
},
],
},
},
}),
env: {
MEDIA_INFERRED_AUDIO_TOKEN: "inferred-audio-token", // pragma: allowlist secret
},
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }),
});
expect(snapshot.config.tools?.media?.models?.[0]?.request?.auth).toEqual({
mode: "authorization-bearer",
token: "inferred-audio-token",
});
expect(snapshot.warnings.map((warning) => warning.path)).not.toContain(
"tools.media.models.0.request.auth.token",
);
});
it("treats shared media model request refs as inactive when inferred capabilities are disabled", async () => {
const pluginRegistry = createEmptyPluginRegistry();
pluginRegistry.mediaUnderstandingProviders.push({
pluginId: "deepgram",
pluginName: "Deepgram Plugin",
source: "test",
provider: {
id: "deepgram",
capabilities: ["audio"],
},
});
setActivePluginRegistry(pluginRegistry);
const inferredTokenRef = {
source: "env" as const,
provider: "default" as const,
id: "MEDIA_INFERRED_DISABLED_AUDIO_TOKEN",
};
const snapshot = await prepareSecretsRuntimeSnapshot({
config: asConfig({
tools: {
media: {
models: [
{
provider: "deepgram",
request: {
auth: {
mode: "authorization-bearer",
token: inferredTokenRef,
},
},
},
],
audio: {
enabled: false,
},
},
},
}),
env: {},
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }),
});
expect(snapshot.config.tools?.media?.models?.[0]?.request?.auth).toEqual({
mode: "authorization-bearer",
token: inferredTokenRef,
});
expect(snapshot.warnings.map((warning) => warning.path)).toContain(
"tools.media.models.0.request.auth.token",
);
});
it("treats section media model request refs as inactive when model capabilities exclude the section", async () => {
const sectionTokenRef = {
source: "env" as const,
provider: "default" as const,
id: "MEDIA_AUDIO_SECTION_FILTERED_TOKEN",
};
const snapshot = await prepareSecretsRuntimeSnapshot({
config: asConfig({
tools: {
media: {
audio: {
enabled: true,
models: [
{
provider: "openai",
capabilities: ["video"],
request: {
auth: {
mode: "authorization-bearer",
token: sectionTokenRef,
},
},
},
],
},
},
},
}),
env: {},
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }),
});
expect(snapshot.config.tools?.media?.audio?.models?.[0]?.request?.auth).toEqual({
mode: "authorization-bearer",
token: sectionTokenRef,
});
expect(snapshot.warnings.map((warning) => warning.path)).toContain(
"tools.media.audio.models.0.request.auth.token",
);
});
it("fails when an active exec ref id contains traversal segments", async () => {
await expect(
prepareSecretsRuntimeSnapshot({
@@ -853,46 +302,6 @@ describe("secrets runtime snapshot", () => {
).rejects.toThrow(/must not include "\." or "\.\." path segments/i);
});
it("treats defaults memorySearch ref as inactive when all enabled agents disable memorySearch", async () => {
const snapshot = await prepareSecretsRuntimeSnapshot({
config: asConfig({
agents: {
defaults: {
memorySearch: {
remote: {
apiKey: {
source: "env",
provider: "default",
id: "DEFAULT_MEMORY_REMOTE_API_KEY",
},
},
},
},
list: [
{
enabled: true,
memorySearch: {
enabled: false,
},
},
],
},
}),
env: {},
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }),
});
expect(snapshot.config.agents?.defaults?.memorySearch?.remote?.apiKey).toEqual({
source: "env",
provider: "default",
id: "DEFAULT_MEMORY_REMOTE_API_KEY",
});
expect(snapshot.warnings.map((warning) => warning.path)).toContain(
"agents.defaults.memorySearch.remote.apiKey",
);
});
it("does not write inherited auth stores during runtime secret activation", async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-secrets-runtime-"));
const stateDir = path.join(root, ".openclaw");