fix: resolve acpx MCP secret inputs

This commit is contained in:
Peter Steinberger
2026-03-30 05:29:12 +09:00
parent 35233bae96
commit 694bc082a8
6 changed files with 861 additions and 1 deletions

View File

@@ -288,6 +288,39 @@ Optional per-id errors:
}
```
## MCP server environment variables
MCP server env vars configured via `plugins.entries.acpx.config.mcpServers` support SecretInput. This keeps API keys and tokens out of plaintext config:
```json5
{
plugins: {
entries: {
acpx: {
enabled: true,
config: {
mcpServers: {
github: {
command: "npx",
args: ["-y", "@modelcontextprotocol/server-github"],
env: {
GITHUB_PERSONAL_ACCESS_TOKEN: {
source: "env",
provider: "default",
id: "MCP_GITHUB_PAT",
},
},
},
},
},
},
},
},
}
```
Plaintext string values still work. Env-template refs like `${MCP_SERVER_API_KEY}` and SecretRef objects are resolved during gateway activation before the MCP server process is spawned. As with other SecretRef surfaces, unresolved refs only block activation when the `acpx` plugin is effectively active.
## Sandbox SSH auth material
The core `ssh` sandbox backend also supports SecretRefs for SSH auth material:

View File

@@ -0,0 +1,502 @@
import { describe, expect, it } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import type { PluginOrigin } from "../plugins/types.js";
import { collectPluginConfigAssignments } from "./runtime-config-collectors-plugins.js";
import {
createResolverContext,
type ResolverContext,
type SecretDefaults,
} from "./runtime-shared.js";
function asConfig(value: unknown): OpenClawConfig {
return value as OpenClawConfig;
}
function makeContext(sourceConfig: OpenClawConfig): ResolverContext {
return createResolverContext({
sourceConfig,
env: {},
});
}
function envRef(id: string) {
return { source: "env" as const, provider: "default", id };
}
function loadablePluginOrigins(entries: Array<[string, PluginOrigin]>) {
return new Map(entries);
}
describe("collectPluginConfigAssignments", () => {
it("collects SecretRef assignments from active acpx MCP server env vars", () => {
const config = asConfig({
plugins: {
entries: {
acpx: {
enabled: true,
config: {
mcpServers: {
github: {
command: "npx",
args: ["-y", "@modelcontextprotocol/server-github"],
env: {
GITHUB_TOKEN: envRef("GITHUB_TOKEN"),
PLAIN_VAR: "plain-value",
},
},
},
},
},
},
},
});
const context = makeContext(config);
const defaults: SecretDefaults = undefined;
collectPluginConfigAssignments({
config,
defaults,
context,
loadablePluginOrigins: loadablePluginOrigins([["acpx", "bundled"]]),
});
expect(context.assignments).toHaveLength(1);
expect(context.assignments[0]?.path).toBe(
"plugins.entries.acpx.config.mcpServers.github.env.GITHUB_TOKEN",
);
expect(context.assignments[0]?.expected).toBe("string");
});
it("resolves assignments via apply callback", () => {
const config = asConfig({
plugins: {
entries: {
acpx: {
enabled: true,
config: {
mcpServers: {
mcp1: {
command: "node",
env: {
API_KEY: envRef("MY_API_KEY"),
},
},
},
},
},
},
},
});
const context = makeContext(config);
collectPluginConfigAssignments({
config,
defaults: undefined,
context,
loadablePluginOrigins: loadablePluginOrigins([["acpx", "bundled"]]),
});
expect(context.assignments).toHaveLength(1);
context.assignments[0]?.apply("resolved-key-value");
const entries = config.plugins?.entries as Record<string, Record<string, unknown>>;
const mcpServers = (entries?.acpx?.config as Record<string, unknown>)?.mcpServers as Record<
string,
Record<string, unknown>
>;
const env = mcpServers?.mcp1?.env as Record<string, unknown>;
expect(env?.API_KEY).toBe("resolved-key-value");
});
it("collects across multiple acpx servers only", () => {
const config = asConfig({
plugins: {
entries: {
acpx: {
enabled: true,
config: {
mcpServers: {
s1: { command: "a", env: { K1: envRef("K1") } },
s2: { command: "b", env: { K2: envRef("K2"), K3: envRef("K3") } },
},
},
},
other: {
enabled: true,
config: {
mcpServers: {
s3: { command: "c", env: { K4: envRef("K4") } },
},
},
},
},
},
});
const context = makeContext(config);
collectPluginConfigAssignments({
config,
defaults: undefined,
context,
loadablePluginOrigins: loadablePluginOrigins([
["acpx", "bundled"],
["other", "config"],
]),
});
expect(context.assignments).toHaveLength(3);
const paths = context.assignments.map((a) => a.path).toSorted();
expect(paths).toEqual([
"plugins.entries.acpx.config.mcpServers.s1.env.K1",
"plugins.entries.acpx.config.mcpServers.s2.env.K2",
"plugins.entries.acpx.config.mcpServers.s2.env.K3",
]);
});
it("skips entries without config or mcpServers", () => {
const config = asConfig({
plugins: {
entries: {
noConfig: {},
noMcpServers: { config: { otherKey: "value" } },
noEnv: { config: { mcpServers: { s1: { command: "x" } } } },
},
},
});
const context = makeContext(config);
collectPluginConfigAssignments({
config,
defaults: undefined,
context,
loadablePluginOrigins: loadablePluginOrigins([]),
});
expect(context.assignments).toHaveLength(0);
});
it("skips when no plugins.entries at all", () => {
const config = asConfig({});
const context = makeContext(config);
collectPluginConfigAssignments({
config,
defaults: undefined,
context,
loadablePluginOrigins: loadablePluginOrigins([]),
});
expect(context.assignments).toHaveLength(0);
});
it("skips assignments when plugins.enabled is false", () => {
const config = asConfig({
plugins: {
enabled: false,
entries: {
acpx: {
enabled: true,
config: {
mcpServers: {
s1: { command: "node", env: { K: envRef("K") } },
},
},
},
},
},
});
const context = makeContext(config);
collectPluginConfigAssignments({
config,
defaults: undefined,
context,
loadablePluginOrigins: loadablePluginOrigins([["acpx", "bundled"]]),
});
expect(context.assignments).toHaveLength(0);
expect(context.warnings.some((w) => w.code === "SECRETS_REF_IGNORED_INACTIVE_SURFACE")).toBe(
true,
);
});
it("skips assignments when entry.enabled is false", () => {
const config = asConfig({
plugins: {
entries: {
acpx: {
enabled: false,
config: {
mcpServers: {
s1: { command: "node", env: { K: envRef("K") } },
},
},
},
},
},
});
const context = makeContext(config);
collectPluginConfigAssignments({
config,
defaults: undefined,
context,
loadablePluginOrigins: loadablePluginOrigins([["acpx", "bundled"]]),
});
expect(context.assignments).toHaveLength(0);
expect(context.warnings.some((w) => w.code === "SECRETS_REF_IGNORED_INACTIVE_SURFACE")).toBe(
true,
);
});
it("keeps bundled acpx inactive unless explicitly enabled", () => {
const config = asConfig({
plugins: {
enabled: true,
entries: {
acpx: {
config: {
mcpServers: {
s1: { command: "node", env: { K: envRef("K") } },
},
},
},
},
},
});
const context = makeContext(config);
collectPluginConfigAssignments({
config,
defaults: undefined,
context,
loadablePluginOrigins: loadablePluginOrigins([["acpx", "bundled"]]),
});
expect(context.assignments).toHaveLength(0);
expect(context.warnings.some((w) => w.code === "SECRETS_REF_IGNORED_INACTIVE_SURFACE")).toBe(
true,
);
});
it("skips assignments when plugin is in denylist", () => {
const config = asConfig({
plugins: {
deny: ["acpx"],
entries: {
acpx: {
enabled: true,
config: {
mcpServers: {
s1: { command: "node", env: { K: envRef("K") } },
},
},
},
},
},
});
const context = makeContext(config);
collectPluginConfigAssignments({
config,
defaults: undefined,
context,
loadablePluginOrigins: loadablePluginOrigins([["acpx", "bundled"]]),
});
expect(context.assignments).toHaveLength(0);
expect(context.warnings.some((w) => w.code === "SECRETS_REF_IGNORED_INACTIVE_SURFACE")).toBe(
true,
);
});
it("skips assignments when allowlist is set and plugin is not in it", () => {
const config = asConfig({
plugins: {
allow: ["other-plugin"],
entries: {
acpx: {
enabled: true,
config: {
mcpServers: {
s1: { command: "node", env: { K: envRef("K") } },
},
},
},
},
},
});
const context = makeContext(config);
collectPluginConfigAssignments({
config,
defaults: undefined,
context,
loadablePluginOrigins: loadablePluginOrigins([["acpx", "bundled"]]),
});
expect(context.assignments).toHaveLength(0);
expect(context.warnings.some((w) => w.code === "SECRETS_REF_IGNORED_INACTIVE_SURFACE")).toBe(
true,
);
});
it("collects assignments when plugin is in allowlist", () => {
const config = asConfig({
plugins: {
allow: ["acpx"],
entries: {
acpx: {
config: {
mcpServers: {
s1: { command: "node", env: { K: envRef("K") } },
},
},
},
},
},
});
const context = makeContext(config);
collectPluginConfigAssignments({
config,
defaults: undefined,
context,
loadablePluginOrigins: loadablePluginOrigins([["acpx", "config"]]),
});
expect(context.assignments).toHaveLength(1);
});
it("ignores plain string env values", () => {
const config = asConfig({
plugins: {
entries: {
acpx: {
enabled: true,
config: {
mcpServers: {
s1: {
command: "node",
env: { PLAIN: "hello", ALSO_PLAIN: "world" },
},
},
},
},
},
},
});
const context = makeContext(config);
collectPluginConfigAssignments({
config,
defaults: undefined,
context,
loadablePluginOrigins: loadablePluginOrigins([["acpx", "bundled"]]),
});
expect(context.assignments).toHaveLength(0);
});
it("collects inline env-template refs while leaving normal strings literal", () => {
const config = asConfig({
plugins: {
entries: {
acpx: {
enabled: true,
config: {
mcpServers: {
s1: {
command: "node",
env: {
INLINE: "${INLINE_KEY}",
SECOND: "${SECOND_KEY}",
LITERAL: "hello",
},
},
},
},
},
},
},
});
const context = makeContext(config);
collectPluginConfigAssignments({
config,
defaults: undefined,
context,
loadablePluginOrigins: loadablePluginOrigins([["acpx", "bundled"]]),
});
expect(context.assignments).toHaveLength(2);
expect(context.assignments[0]?.path).toBe(
"plugins.entries.acpx.config.mcpServers.s1.env.INLINE",
);
expect(context.assignments[1]?.path).toBe(
"plugins.entries.acpx.config.mcpServers.s1.env.SECOND",
);
});
it("skips stale acpx entries not in loadablePluginOrigins", () => {
const config = asConfig({
plugins: {
entries: {
acpx: {
enabled: true,
config: {
mcpServers: {
s1: { command: "node", env: { K1: envRef("K1") } },
},
},
},
},
},
});
const context = makeContext(config);
collectPluginConfigAssignments({
config,
defaults: undefined,
context,
loadablePluginOrigins: loadablePluginOrigins([]),
});
expect(context.assignments).toHaveLength(0);
expect(
context.warnings.some(
(w) =>
w.code === "SECRETS_REF_IGNORED_INACTIVE_SURFACE" &&
w.path === "plugins.entries.acpx.config.mcpServers.s1.env.K1",
),
).toBe(true);
});
it("ignores non-acpx plugin mcpServers surfaces", () => {
const config = asConfig({
plugins: {
entries: {
other: {
enabled: true,
config: {
mcpServers: {
s1: { command: "node", env: { K1: envRef("K1") } },
},
},
},
},
},
});
const context = makeContext(config);
collectPluginConfigAssignments({
config,
defaults: undefined,
context,
loadablePluginOrigins: loadablePluginOrigins([["other", "config"]]),
});
expect(context.assignments).toHaveLength(0);
});
});

View File

@@ -0,0 +1,118 @@
import type { OpenClawConfig } from "../config/config.js";
import { normalizePluginsConfig, resolveEnableState } from "../plugins/config-state.js";
import type { PluginOrigin } from "../plugins/types.js";
import {
collectSecretInputAssignment,
type ResolverContext,
type SecretDefaults,
} from "./runtime-shared.js";
import { isRecord } from "./shared.js";
const ACPX_PLUGIN_ID = "acpx";
/**
* Walk plugin config entries and collect SecretRef assignments for MCP server
* env vars. Without this, SecretRefs in paths like
* `plugins.entries.acpx.config.mcpServers.*.env.*` are never resolved and
* remain as raw objects at runtime.
*
* This surface is intentionally scoped to ACPX. Third-party plugins may define
* their own `mcpServers`-shaped config, but that is not a documented SecretRef
* surface and should not be rewritten here.
*
* When `loadablePluginOrigins` is provided, entries whose ID is not in the map
* are treated as inactive (stale config entries for plugins that are no longer
* installed). This prevents resolution failures for SecretRefs belonging to
* non-loadable plugins from blocking startup or preflight validation.
*/
export function collectPluginConfigAssignments(params: {
config: OpenClawConfig;
defaults: SecretDefaults | undefined;
context: ResolverContext;
loadablePluginOrigins?: ReadonlyMap<string, PluginOrigin>;
}): void {
const entries = params.config.plugins?.entries;
if (!isRecord(entries)) {
return;
}
const normalizedConfig = normalizePluginsConfig(params.config.plugins);
for (const [pluginId, entry] of Object.entries(entries)) {
if (pluginId !== ACPX_PLUGIN_ID) {
continue;
}
if (!isRecord(entry)) {
continue;
}
const pluginConfig = entry.config;
if (!isRecord(pluginConfig)) {
continue;
}
const pluginOrigin = params.loadablePluginOrigins?.get(pluginId);
if (params.loadablePluginOrigins && !pluginOrigin) {
collectMcpServerEnvAssignments({
pluginId,
pluginConfig,
active: false,
inactiveReason: "plugin is not loadable (stale config entry).",
defaults: params.defaults,
context: params.context,
});
continue;
}
const enableState = resolveEnableState(pluginId, pluginOrigin ?? "config", normalizedConfig);
collectMcpServerEnvAssignments({
pluginId,
pluginConfig,
active: enableState.enabled,
inactiveReason: enableState.reason ?? "plugin is disabled.",
defaults: params.defaults,
context: params.context,
});
}
}
function collectMcpServerEnvAssignments(params: {
pluginId: string;
pluginConfig: Record<string, unknown>;
active: boolean;
inactiveReason: string;
defaults: SecretDefaults | undefined;
context: ResolverContext;
}): void {
const mcpServers = params.pluginConfig.mcpServers;
if (!isRecord(mcpServers)) {
return;
}
for (const [serverName, serverConfig] of Object.entries(mcpServers)) {
if (!isRecord(serverConfig)) {
continue;
}
const env = serverConfig.env;
if (!isRecord(env)) {
continue;
}
for (const [envKey, envValue] of Object.entries(env)) {
// SecretInput allows both explicit objects and inline env-template refs
// like `${MCP_API_KEY}`. Non-ref strings remain untouched because
// collectSecretInputAssignment ignores them.
collectSecretInputAssignment({
value: envValue,
path: `plugins.entries.${params.pluginId}.config.mcpServers.${serverName}.env.${envKey}`,
expected: "string",
defaults: params.defaults,
context: params.context,
active: params.active,
inactiveReason: `plugin "${params.pluginId}": ${params.inactiveReason}`,
apply: (value) => {
env[envKey] = value;
},
});
}
}
}

View File

@@ -1,11 +1,14 @@
import type { OpenClawConfig } from "../config/config.js";
import type { PluginOrigin } from "../plugins/types.js";
import { collectChannelConfigAssignments } from "./runtime-config-collectors-channels.js";
import { collectCoreConfigAssignments } from "./runtime-config-collectors-core.js";
import { collectPluginConfigAssignments } from "./runtime-config-collectors-plugins.js";
import type { ResolverContext } from "./runtime-shared.js";
export function collectConfigAssignments(params: {
config: OpenClawConfig;
context: ResolverContext;
loadablePluginOrigins?: ReadonlyMap<string, PluginOrigin>;
}): void {
const defaults = params.context.sourceConfig.secrets?.defaults;
@@ -20,4 +23,11 @@ export function collectConfigAssignments(params: {
defaults,
context: params.context,
});
collectPluginConfigAssignments({
config: params.config,
defaults,
context: params.context,
loadablePluginOrigins: params.loadablePluginOrigins,
});
}

View File

@@ -2423,6 +2423,165 @@ describe("secrets runtime snapshot", () => {
);
});
it("resolves SecretRef objects for active acpx MCP env vars", async () => {
const config = asConfig({
plugins: {
entries: {
acpx: {
enabled: true,
config: {
mcpServers: {
github: {
command: "npx",
env: {
GITHUB_TOKEN: {
source: "env",
provider: "default",
id: "GH_TOKEN_SECRET",
},
},
},
},
},
},
},
},
});
const snapshot = await prepareSecretsRuntimeSnapshot({
config,
env: {
GH_TOKEN_SECRET: "ghp-object-token",
},
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }),
});
const sourceEntries = snapshot.sourceConfig.plugins?.entries as Record<
string,
{ config?: Record<string, unknown> }
>;
const sourceMcpServers = sourceEntries?.acpx?.config?.mcpServers as Record<
string,
{ env?: Record<string, unknown> }
>;
const entries = snapshot.config.plugins?.entries as Record<
string,
{ config?: Record<string, unknown> }
>;
const mcpServers = entries?.acpx?.config?.mcpServers as Record<
string,
{ env?: Record<string, unknown> }
>;
expect(mcpServers?.github?.env?.GITHUB_TOKEN).toBe("ghp-object-token");
expect(sourceMcpServers?.github?.env?.GITHUB_TOKEN).toEqual({
source: "env",
provider: "default",
id: "GH_TOKEN_SECRET",
});
});
it("resolves inline env-template refs for active acpx MCP env vars", async () => {
const config = asConfig({
plugins: {
entries: {
acpx: {
enabled: true,
config: {
mcpServers: {
github: {
command: "npx",
env: {
GITHUB_TOKEN: "${GH_TOKEN_SECRET}",
SECOND_TOKEN: "${SECOND_SECRET}",
LITERAL: "literal-value",
},
},
},
},
},
},
},
});
const snapshot = await prepareSecretsRuntimeSnapshot({
config,
env: {
GH_TOKEN_SECRET: "ghp-inline-token",
SECOND_SECRET: "ghp-second-token",
},
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }),
});
const entries = snapshot.config.plugins?.entries as Record<
string,
{ config?: Record<string, unknown> }
>;
const mcpServers = entries?.acpx?.config?.mcpServers as Record<
string,
{ env?: Record<string, unknown> }
>;
expect(mcpServers?.github?.env?.GITHUB_TOKEN).toBe("ghp-inline-token");
expect(mcpServers?.github?.env?.SECOND_TOKEN).toBe("ghp-second-token");
expect(mcpServers?.github?.env?.LITERAL).toBe("literal-value");
});
it("treats bundled acpx MCP env refs as inactive until the plugin is enabled", async () => {
const config = asConfig({
plugins: {
entries: {
acpx: {
config: {
mcpServers: {
github: {
command: "npx",
env: {
GITHUB_TOKEN: {
source: "env",
provider: "default",
id: "GH_TOKEN_SECRET",
},
},
},
},
},
},
},
},
});
const snapshot = await prepareSecretsRuntimeSnapshot({
config,
env: {},
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }),
});
expect(
snapshot.warnings.some(
(warning) =>
warning.code === "SECRETS_REF_IGNORED_INACTIVE_SURFACE" &&
warning.path === "plugins.entries.acpx.config.mcpServers.github.env.GITHUB_TOKEN",
),
).toBe(true);
const entries = snapshot.config.plugins?.entries as Record<
string,
{ config?: Record<string, unknown> }
>;
const mcpServers = entries?.acpx?.config?.mcpServers as Record<
string,
{ env?: Record<string, unknown> }
>;
expect(mcpServers?.github?.env?.GITHUB_TOKEN).toEqual({
source: "env",
provider: "default",
id: "GH_TOKEN_SECRET",
});
});
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");

View File

@@ -1,5 +1,10 @@
import { resolveOpenClawAgentDir } from "../agents/agent-paths.js";
import { listAgentIds, resolveAgentDir } from "../agents/agent-scope.js";
import {
listAgentIds,
resolveAgentDir,
resolveAgentWorkspaceDir,
resolveDefaultAgentId,
} from "../agents/agent-scope.js";
import type { AuthProfileStore } from "../agents/auth-profiles.js";
import {
clearRuntimeAuthProfileStoreSnapshots,
@@ -13,6 +18,8 @@ import {
type OpenClawConfig,
} from "../config/config.js";
import { migrateLegacyConfig } from "../config/legacy-migrate.js";
import { loadPluginManifestRegistry } from "../plugins/manifest-registry.js";
import type { PluginOrigin } from "../plugins/types.js";
import { resolveUserPath } from "../utils.js";
import {
collectCommandSecretAssignmentsFromSnapshot,
@@ -42,6 +49,7 @@ type SecretsRuntimeRefreshContext = {
env: Record<string, string | undefined>;
explicitAgentDirs: string[] | null;
loadAuthStore: (agentDir?: string) => AuthProfileStore;
loadablePluginOrigins: ReadonlyMap<string, PluginOrigin>;
};
const RUNTIME_PATH_ENV_KEYS = [
@@ -82,6 +90,7 @@ function cloneRefreshContext(context: SecretsRuntimeRefreshContext): SecretsRunt
env: { ...context.env },
explicitAgentDirs: context.explicitAgentDirs ? [...context.explicitAgentDirs] : null,
loadAuthStore: context.loadAuthStore,
loadablePluginOrigins: new Map(context.loadablePluginOrigins),
};
}
@@ -116,6 +125,23 @@ function resolveRefreshAgentDirs(
return [...new Set([...context.explicitAgentDirs, ...configDerived])];
}
function resolveLoadablePluginOrigins(params: {
config: OpenClawConfig;
env: NodeJS.ProcessEnv;
}): ReadonlyMap<string, PluginOrigin> {
const workspaceDir = resolveAgentWorkspaceDir(
params.config,
resolveDefaultAgentId(params.config),
);
const manifestRegistry = loadPluginManifestRegistry({
config: params.config,
workspaceDir,
cache: true,
env: params.env,
});
return new Map(manifestRegistry.plugins.map((record) => [record.id, record.origin]));
}
function mergeSecretsRuntimeEnv(
env: NodeJS.ProcessEnv | Record<string, string | undefined> | undefined,
): Record<string, string | undefined> {
@@ -137,12 +163,17 @@ export async function prepareSecretsRuntimeSnapshot(params: {
env?: NodeJS.ProcessEnv;
agentDirs?: string[];
loadAuthStore?: (agentDir?: string) => AuthProfileStore;
/** Test override for discovered loadable plugins and their origins. */
loadablePluginOrigins?: ReadonlyMap<string, PluginOrigin>;
}): Promise<PreparedSecretsRuntimeSnapshot> {
const runtimeEnv = mergeSecretsRuntimeEnv(params.env);
const sourceConfig = structuredClone(params.config);
const resolvedConfig = structuredClone(
migrateLegacyConfig(params.config).config ?? params.config,
);
const loadablePluginOrigins =
params.loadablePluginOrigins ??
resolveLoadablePluginOrigins({ config: sourceConfig, env: runtimeEnv });
const context = createResolverContext({
sourceConfig,
env: runtimeEnv,
@@ -151,6 +182,7 @@ export async function prepareSecretsRuntimeSnapshot(params: {
collectConfigAssignments({
config: resolvedConfig,
context,
loadablePluginOrigins,
});
const loadAuthStore = params.loadAuthStore ?? loadAuthProfileStoreForSecretsRuntime;
@@ -197,6 +229,7 @@ export async function prepareSecretsRuntimeSnapshot(params: {
env: runtimeEnv,
explicitAgentDirs: params.agentDirs?.length ? [...candidateDirs] : null,
loadAuthStore,
loadablePluginOrigins,
});
return snapshot;
}
@@ -210,6 +243,10 @@ export function activateSecretsRuntimeSnapshot(snapshot: PreparedSecretsRuntimeS
env: { ...process.env } as Record<string, string | undefined>,
explicitAgentDirs: null,
loadAuthStore: loadAuthProfileStoreForSecretsRuntime,
loadablePluginOrigins: resolveLoadablePluginOrigins({
config: next.sourceConfig,
env: process.env,
}),
} satisfies SecretsRuntimeRefreshContext);
setRuntimeConfigSnapshot(next.config, next.sourceConfig);
replaceRuntimeAuthProfileStoreSnapshots(next.authStores);
@@ -225,6 +262,7 @@ export function activateSecretsRuntimeSnapshot(snapshot: PreparedSecretsRuntimeS
env: activeRefreshContext.env,
agentDirs: resolveRefreshAgentDirs(sourceConfig, activeRefreshContext),
loadAuthStore: activeRefreshContext.loadAuthStore,
loadablePluginOrigins: activeRefreshContext.loadablePluginOrigins,
});
activateSecretsRuntimeSnapshot(refreshed);
return true;