perf(secrets): trim runtime import walls

This commit is contained in:
Peter Steinberger
2026-04-06 07:30:04 +01:00
parent c9fc6f5a56
commit 73485c2300
7 changed files with 183 additions and 95 deletions

View File

@@ -59,7 +59,10 @@ import { createPluginRuntime } from "../plugins/runtime/index.js";
import type { PluginServicesHandle } from "../plugins/services.js";
import { getTotalQueueSize } from "../process/command-queue.js";
import type { RuntimeEnv } from "../runtime.js";
import type { CommandSecretAssignment } from "../secrets/command-config.js";
import {
resolveCommandSecretsFromActiveRuntimeSnapshot,
type CommandSecretAssignment,
} from "../secrets/runtime-command-secrets.js";
import {
GATEWAY_AUTH_SURFACE_PATHS,
evaluateGatewayAuthSurfaceStates,
@@ -69,7 +72,6 @@ import {
clearSecretsRuntimeSnapshot,
getActiveSecretsRuntimeSnapshot,
prepareSecretsRuntimeSnapshot,
resolveCommandSecretsFromActiveRuntimeSnapshot,
} from "../secrets/runtime.js";
import { onSessionLifecycleEvent } from "../sessions/session-lifecycle-events.js";
import { onSessionTranscriptUpdate } from "../sessions/transcript-events.js";

View File

@@ -0,0 +1,130 @@
import fs from "node:fs/promises";
import path from "node:path";
import { expect, vi } from "vitest";
import { ensureAuthProfileStore, type AuthProfileStore } from "../agents/auth-profiles.js";
import type { OpenClawConfig } from "../config/config.js";
import { clearConfigCache, clearRuntimeConfigSnapshot, loadConfig } from "../config/config.js";
import { captureEnv } from "../test-utils/env.js";
import { clearSecretsRuntimeSnapshot } from "./runtime.js";
export const OPENAI_ENV_KEY_REF = {
source: "env",
provider: "default",
id: "OPENAI_API_KEY",
} as const;
export const OPENAI_FILE_KEY_REF = {
source: "file",
provider: "default",
id: "/providers/openai/apiKey",
} as const;
export const EMPTY_LOADABLE_PLUGIN_ORIGINS = new Map();
export type SecretsRuntimeEnvSnapshot = ReturnType<typeof captureEnv>;
const allowInsecureTempSecretFile = process.platform === "win32";
export function asConfig(value: unknown): OpenClawConfig {
return value as OpenClawConfig;
}
export function loadAuthStoreWithProfiles(
profiles: AuthProfileStore["profiles"],
): AuthProfileStore {
return {
version: 1,
profiles,
};
}
export async function createOpenAIFileRuntimeFixture(home: string) {
const configDir = path.join(home, ".openclaw");
const secretFile = path.join(configDir, "secrets.json");
const agentDir = path.join(configDir, "agents", "main", "agent");
const authStorePath = path.join(agentDir, "auth-profiles.json");
await fs.mkdir(agentDir, { recursive: true });
await fs.chmod(configDir, 0o700).catch(() => {});
await fs.writeFile(
secretFile,
`${JSON.stringify({ providers: { openai: { apiKey: "sk-file-runtime" } } }, null, 2)}\n`,
{ encoding: "utf8", mode: 0o600 },
);
await fs.writeFile(
authStorePath,
`${JSON.stringify(
{
version: 1,
profiles: {
"openai:default": {
type: "api_key",
provider: "openai",
keyRef: OPENAI_FILE_KEY_REF,
},
},
},
null,
2,
)}\n`,
{ encoding: "utf8", mode: 0o600 },
);
return {
configDir,
secretFile,
agentDir,
};
}
export function createOpenAIFileRuntimeConfig(secretFile: string): OpenClawConfig {
return asConfig({
secrets: {
providers: {
default: {
source: "file",
path: secretFile,
mode: "json",
...(allowInsecureTempSecretFile ? { allowInsecurePath: true } : {}),
},
},
},
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
apiKey: OPENAI_FILE_KEY_REF,
models: [],
},
},
},
});
}
export function expectResolvedOpenAIRuntime(agentDir: string) {
expect(loadConfig().models?.providers?.openai?.apiKey).toBe("sk-file-runtime");
expect(ensureAuthProfileStore(agentDir).profiles["openai:default"]).toMatchObject({
type: "api_key",
key: "sk-file-runtime",
});
}
export function beginSecretsRuntimeIsolationForTest(): SecretsRuntimeEnvSnapshot {
const envSnapshot = captureEnv([
"OPENCLAW_BUNDLED_PLUGINS_DIR",
"OPENCLAW_DISABLE_BUNDLED_PLUGINS",
"OPENCLAW_DISABLE_PLUGIN_DISCOVERY_CACHE",
"OPENCLAW_VERSION",
]);
delete process.env.OPENCLAW_BUNDLED_PLUGINS_DIR;
process.env.OPENCLAW_DISABLE_PLUGIN_DISCOVERY_CACHE = "1";
delete process.env.OPENCLAW_VERSION;
return envSnapshot;
}
export function endSecretsRuntimeIsolationForTest(envSnapshot: SecretsRuntimeEnvSnapshot) {
vi.restoreAllMocks();
envSnapshot.restore();
clearSecretsRuntimeSnapshot();
clearRuntimeConfigSnapshot();
clearConfigCache();
}

View File

@@ -0,0 +1,39 @@
import {
collectCommandSecretAssignmentsFromSnapshot,
type CommandSecretAssignment,
} from "./command-config.js";
import { getActiveSecretsRuntimeSnapshot } from "./runtime.js";
export type { CommandSecretAssignment } from "./command-config.js";
export function resolveCommandSecretsFromActiveRuntimeSnapshot(params: {
commandName: string;
targetIds: ReadonlySet<string>;
}): { assignments: CommandSecretAssignment[]; diagnostics: string[]; inactiveRefPaths: string[] } {
const activeSnapshot = getActiveSecretsRuntimeSnapshot();
if (!activeSnapshot) {
throw new Error("Secrets runtime snapshot is not active.");
}
if (params.targetIds.size === 0) {
return { assignments: [], diagnostics: [], inactiveRefPaths: [] };
}
const inactiveRefPaths = [
...new Set(
activeSnapshot.warnings
.filter((warning) => warning.code === "SECRETS_REF_IGNORED_INACTIVE_SURFACE")
.map((warning) => warning.path),
),
];
const resolved = collectCommandSecretAssignmentsFromSnapshot({
sourceConfig: activeSnapshot.sourceConfig,
resolvedConfig: activeSnapshot.config,
commandName: params.commandName,
targetIds: params.targetIds,
inactiveRefPaths: new Set(inactiveRefPaths),
});
return {
assignments: resolved.assignments,
diagnostics: resolved.diagnostics,
inactiveRefPaths,
};
}

View File

@@ -7,6 +7,9 @@ export function collectChannelConfigAssignments(params: {
defaults: SecretDefaults | undefined;
context: ResolverContext;
}): void {
if (!params.config.channels || Object.keys(params.config.channels).length === 0) {
return;
}
for (const plugin of iterateBootstrapChannelPlugins()) {
plugin.secrets?.collectRuntimeConfigAssignments?.(params);
}

View File

@@ -18,7 +18,7 @@ import {
OPENAI_ENV_KEY_REF,
OPENAI_FILE_KEY_REF,
type SecretsRuntimeEnvSnapshot,
} from "./runtime.integration.test-helpers.js";
} from "./runtime-auth.integration.test-helpers.js";
import {
activateSecretsRuntimeSnapshot,
getActiveSecretsRuntimeSnapshot,

View File

@@ -135,7 +135,7 @@ describe("secrets runtime snapshot", () => {
clearConfigCache();
});
it("resolves env refs for config and auth profiles", async () => {
it("resolves core env refs for config and auth profiles", async () => {
const config = asConfig({
agents: {
defaults: {
@@ -185,39 +185,6 @@ describe("secrets runtime snapshot", () => {
password: { source: "env", provider: "default", id: "REMOTE_GATEWAY_PASSWORD" },
},
},
channels: {
telegram: {
botToken: { source: "env", provider: "default", id: "TELEGRAM_BOT_TOKEN_REF" },
webhookUrl: "https://example.test/telegram-webhook",
webhookSecret: { source: "env", provider: "default", id: "TELEGRAM_WEBHOOK_SECRET_REF" },
accounts: {
work: {
botToken: {
source: "env",
provider: "default",
id: "TELEGRAM_WORK_BOT_TOKEN_REF",
},
},
},
},
slack: {
mode: "http",
signingSecret: { source: "env", provider: "default", id: "SLACK_SIGNING_SECRET_REF" },
accounts: {
work: {
botToken: { source: "env", provider: "default", id: "SLACK_WORK_BOT_TOKEN_REF" },
appToken: { source: "env", provider: "default", id: "SLACK_WORK_APP_TOKEN_REF" },
},
},
},
},
tools: {
web: {
search: {
apiKey: { source: "env", provider: "default", id: "WEB_SEARCH_API_KEY" },
},
},
},
});
const snapshot = await prepareSecretsRuntimeSnapshot({
@@ -231,15 +198,9 @@ describe("secrets runtime snapshot", () => {
TALK_PROVIDER_API_KEY: "talk-provider-ref-key", // pragma: allowlist secret
REMOTE_GATEWAY_TOKEN: "remote-token-ref",
REMOTE_GATEWAY_PASSWORD: "remote-password-ref", // pragma: allowlist secret
TELEGRAM_BOT_TOKEN_REF: "telegram-bot-ref",
TELEGRAM_WEBHOOK_SECRET_REF: "telegram-webhook-ref", // pragma: allowlist secret
TELEGRAM_WORK_BOT_TOKEN_REF: "telegram-work-ref",
SLACK_SIGNING_SECRET_REF: "slack-signing-ref", // pragma: allowlist secret
SLACK_WORK_BOT_TOKEN_REF: "slack-work-bot-ref",
SLACK_WORK_APP_TOKEN_REF: "slack-work-app-ref",
WEB_SEARCH_API_KEY: "web-search-ref", // pragma: allowlist secret
},
agentDirs: ["/tmp/openclaw-agent-main"],
loadablePluginOrigins: new Map(),
loadAuthStore: () =>
loadAuthStoreWithProfiles({
"openai:default": {
@@ -272,23 +233,11 @@ describe("secrets runtime snapshot", () => {
expect(snapshot.config.talk?.providers?.["acme-speech"]?.apiKey).toBe("talk-provider-ref-key");
expect(snapshot.config.gateway?.remote?.token).toBe("remote-token-ref");
expect(snapshot.config.gateway?.remote?.password).toBe("remote-password-ref");
expect(snapshot.config.channels?.telegram?.botToken).toEqual({
source: "env",
provider: "default",
id: "TELEGRAM_BOT_TOKEN_REF",
});
expect(snapshot.config.channels?.telegram?.webhookSecret).toBe("telegram-webhook-ref");
expect(snapshot.config.channels?.telegram?.accounts?.work?.botToken).toBe("telegram-work-ref");
expect(snapshot.config.channels?.slack?.signingSecret).toBe("slack-signing-ref");
expect(snapshot.config.channels?.slack?.accounts?.work?.botToken).toBe("slack-work-bot-ref");
expect(snapshot.config.channels?.slack?.accounts?.work?.appToken).toEqual({
source: "env",
provider: "default",
id: "SLACK_WORK_APP_TOKEN_REF",
});
expect(snapshot.config.tools?.web?.search?.apiKey).toBe("web-search-ref");
expect(snapshot.warnings.map((warning) => warning.path)).toEqual(
expect.arrayContaining(["channels.slack.accounts.work.appToken"]),
expect.arrayContaining([
"/tmp/openclaw-agent-main.auth-profiles.openai:default.key",
"/tmp/openclaw-agent-main.auth-profiles.github-copilot:default.token",
]),
);
expect(snapshot.authStores[0]?.store.profiles["openai:default"]).toMatchObject({
type: "api_key",

View File

@@ -19,10 +19,6 @@ import {
} from "../config/config.js";
import type { PluginOrigin } from "../plugins/types.js";
import { resolveUserPath } from "../utils.js";
import {
collectCommandSecretAssignmentsFromSnapshot,
type CommandSecretAssignment,
} from "./command-config.js";
import { type SecretResolverWarning } from "./runtime-shared.js";
import {
clearActiveRuntimeWebToolsMetadata,
@@ -302,37 +298,6 @@ export function getActiveRuntimeWebToolsMetadata(): RuntimeWebToolsMetadata | nu
return getActiveRuntimeWebToolsMetadataFromState();
}
export function resolveCommandSecretsFromActiveRuntimeSnapshot(params: {
commandName: string;
targetIds: ReadonlySet<string>;
}): { assignments: CommandSecretAssignment[]; diagnostics: string[]; inactiveRefPaths: string[] } {
if (!activeSnapshot) {
throw new Error("Secrets runtime snapshot is not active.");
}
if (params.targetIds.size === 0) {
return { assignments: [], diagnostics: [], inactiveRefPaths: [] };
}
const inactiveRefPaths = [
...new Set(
activeSnapshot.warnings
.filter((warning) => warning.code === "SECRETS_REF_IGNORED_INACTIVE_SURFACE")
.map((warning) => warning.path),
),
];
const resolved = collectCommandSecretAssignmentsFromSnapshot({
sourceConfig: activeSnapshot.sourceConfig,
resolvedConfig: activeSnapshot.config,
commandName: params.commandName,
targetIds: params.targetIds,
inactiveRefPaths: new Set(inactiveRefPaths),
});
return {
assignments: resolved.assignments,
diagnostics: resolved.diagnostics,
inactiveRefPaths,
};
}
export function clearSecretsRuntimeSnapshot(): void {
clearActiveSecretsRuntimeState();
}