

























@@ -1,3 +1,4 @@
1+import { isDeepStrictEqual } from "node:util";
12import { formatInvalidConfigRecoveryHint } from "../cli/config-recovery-hints.js";
23import {
34type ReadConfigFileSnapshotWithPluginMetadataResult,
@@ -31,17 +32,26 @@ type GatewayStartupLog = {
31323233type GatewaySecretsStateEventCode = "SECRETS_RELOADER_DEGRADED" | "SECRETS_RELOADER_RECOVERED";
333434-export type ActivateRuntimeSecrets = (
35-config: OpenClawConfig,
36-params: { reason: "startup" | "reload" | "restart-check"; activate: boolean },
37-) => Promise<
38-Awaited<ReturnType<typeof import("../secrets/runtime.js").prepareSecretsRuntimeSnapshot>>
39->;
40-4135type PrepareRuntimeSecretsSnapshot =
4236typeof import("../secrets/runtime.js").prepareSecretsRuntimeSnapshot;
4337type ActivateRuntimeSecretsSnapshot =
4438typeof import("../secrets/runtime.js").activateSecretsRuntimeSnapshot;
39+type PreparedRuntimeSecretsSnapshot = Awaited<ReturnType<PrepareRuntimeSecretsSnapshot>>;
40+41+type RuntimeSecretsActivationParams = {
42+reason: "startup" | "reload" | "restart-check";
43+activate: boolean;
44+};
45+46+export type ActivateRuntimeSecrets = ((
47+config: OpenClawConfig,
48+params: RuntimeSecretsActivationParams,
49+) => Promise<PreparedRuntimeSecretsSnapshot>) & {
50+activatePreparedSnapshot?: (
51+snapshot: PreparedRuntimeSecretsSnapshot,
52+params: RuntimeSecretsActivationParams,
53+) => Promise<PreparedRuntimeSecretsSnapshot>;
54+};
45554656type GatewayStartupConfigOverrides = {
4757auth?: GatewayAuthConfig;
@@ -152,7 +162,64 @@ export function createRuntimeSecretsActivator(params: {
152162return await run;
153163};
154164155-return async (config, activationParams) =>
165+const loadActivateRuntimeSecretsSnapshot = async () => {
166+if (params.activateRuntimeSecretsSnapshot) {
167+return params.activateRuntimeSecretsSnapshot;
168+}
169+return (await loadSecretsRuntime()).activateSecretsRuntimeSnapshot;
170+};
171+172+const finishPreparedSnapshot = async (
173+prepared: PreparedRuntimeSecretsSnapshot,
174+activationParams: RuntimeSecretsActivationParams,
175+) => {
176+assertRuntimeGatewayAuthNotKnownWeak(prepared.config);
177+if (activationParams.activate) {
178+const activateRuntimeSecretsSnapshot = await loadActivateRuntimeSecretsSnapshot();
179+activateRuntimeSecretsSnapshot(prepared);
180+logGatewayAuthSurfaceDiagnostics(prepared, params.logSecrets);
181+}
182+for (const warning of prepared.warnings) {
183+params.logSecrets.warn(`[${warning.code}] ${warning.message}`);
184+}
185+if (secretsDegraded) {
186+const recoveredMessage =
187+"Secret resolution recovered; runtime remained on last-known-good during the outage.";
188+params.logSecrets.info(`[SECRETS_RELOADER_RECOVERED] ${recoveredMessage}`);
189+params.emitStateEvent("SECRETS_RELOADER_RECOVERED", recoveredMessage, prepared.config);
190+}
191+secretsDegraded = false;
192+return prepared;
193+};
194+195+const handleSecretsActivationError = (
196+err: unknown,
197+activationParams: RuntimeSecretsActivationParams,
198+eventConfig: OpenClawConfig,
199+): never => {
200+const details = String(err);
201+if (!secretsDegraded) {
202+params.logSecrets.error?.(`[SECRETS_RELOADER_DEGRADED] ${details}`);
203+if (activationParams.reason !== "startup") {
204+params.emitStateEvent(
205+"SECRETS_RELOADER_DEGRADED",
206+`Secret resolution failed; runtime remains on last-known-good snapshot. ${details}`,
207+eventConfig,
208+);
209+}
210+} else {
211+params.logSecrets.warn(`[SECRETS_RELOADER_DEGRADED] ${details}`);
212+}
213+secretsDegraded = true;
214+if (activationParams.reason === "startup") {
215+throw new Error(`Startup failed: required secrets are unavailable. ${details}`, {
216+cause: err,
217+});
218+}
219+throw err;
220+};
221+222+const activateRuntimeSecrets = (async (config, activationParams) =>
156223await runWithSecretsActivationLock(async () => {
157224try {
158225const secretsRuntime =
@@ -161,8 +228,6 @@ export function createRuntimeSecretsActivator(params: {
161228 : await loadSecretsRuntime();
162229const prepareRuntimeSecretsSnapshot =
163230params.prepareRuntimeSecretsSnapshot ?? secretsRuntime!.prepareSecretsRuntimeSnapshot;
164-const activateRuntimeSecretsSnapshot =
165-params.activateRuntimeSecretsSnapshot ?? secretsRuntime!.activateSecretsRuntimeSnapshot;
166231const startupPreflight =
167232activationParams.reason === "startup" || activationParams.reason === "restart-check";
168233const loadAuthStore = startupPreflight
@@ -172,45 +237,22 @@ export function createRuntimeSecretsActivator(params: {
172237config: pruneSkippedStartupSecretSurfaces(config),
173238 ...(loadAuthStore ? { loadAuthStore } : {}),
174239});
175-assertRuntimeGatewayAuthNotKnownWeak(prepared.config);
176-if (activationParams.activate) {
177-activateRuntimeSecretsSnapshot(prepared);
178-logGatewayAuthSurfaceDiagnostics(prepared, params.logSecrets);
179-}
180-for (const warning of prepared.warnings) {
181-params.logSecrets.warn(`[${warning.code}] ${warning.message}`);
182-}
183-if (secretsDegraded) {
184-const recoveredMessage =
185-"Secret resolution recovered; runtime remained on last-known-good during the outage.";
186-params.logSecrets.info(`[SECRETS_RELOADER_RECOVERED] ${recoveredMessage}`);
187-params.emitStateEvent("SECRETS_RELOADER_RECOVERED", recoveredMessage, prepared.config);
188-}
189-secretsDegraded = false;
190-return prepared;
240+return await finishPreparedSnapshot(prepared, activationParams);
191241} catch (err) {
192-const details = String(err);
193-if (!secretsDegraded) {
194-params.logSecrets.error?.(`[SECRETS_RELOADER_DEGRADED] ${details}`);
195-if (activationParams.reason !== "startup") {
196-params.emitStateEvent(
197-"SECRETS_RELOADER_DEGRADED",
198-`Secret resolution failed; runtime remains on last-known-good snapshot. ${details}`,
199-config,
200-);
201-}
202-} else {
203-params.logSecrets.warn(`[SECRETS_RELOADER_DEGRADED] ${details}`);
204-}
205-secretsDegraded = true;
206-if (activationParams.reason === "startup") {
207-throw new Error(`Startup failed: required secrets are unavailable. ${details}`, {
208-cause: err,
209-});
210-}
211-throw err;
242+return handleSecretsActivationError(err, activationParams, config);
243+}
244+})) as ActivateRuntimeSecrets;
245+246+activateRuntimeSecrets.activatePreparedSnapshot = async (snapshot, activationParams) =>
247+await runWithSecretsActivationLock(async () => {
248+try {
249+return await finishPreparedSnapshot(snapshot, activationParams);
250+} catch (err) {
251+return handleSecretsActivationError(err, activationParams, snapshot.sourceConfig);
212252}
213253});
254+255+return activateRuntimeSecrets;
214256}
215257216258export function assertValidGatewayStartupConfigSnapshot(
@@ -253,17 +295,38 @@ export async function prepareGatewayStartupConfig(params: {
253295const needsAuthSecretPreflight = await measure("config.auth.secret-surface", () =>
254296hasActiveGatewayAuthSecretRef(startupPreflightConfig),
255297);
298+let preflightPrepared: PreparedRuntimeSecretsSnapshot | undefined;
256299const preflightConfig = await measure("config.auth.secret-preflight", async () => {
257300if (!needsAuthSecretPreflight) {
258301return startupPreflightConfig;
259302}
260-return (
261-await params.activateRuntimeSecrets(startupPreflightConfig, {
262-reason: "startup",
263-activate: false,
264-})
265-).config;
303+preflightPrepared = await params.activateRuntimeSecrets(startupPreflightConfig, {
304+reason: "startup",
305+activate: false,
306+});
307+return preflightPrepared.config;
266308});
309+const canReusePreflightPreparedSnapshot = (config: OpenClawConfig): boolean =>
310+Boolean(
311+preflightPrepared &&
312+params.activateRuntimeSecrets.activatePreparedSnapshot &&
313+isDeepStrictEqual(
314+pruneSkippedStartupSecretSurfaces(config),
315+preflightPrepared.sourceConfig,
316+),
317+);
318+const activateStartupSecrets = async (config: OpenClawConfig) => {
319+if (preflightPrepared && canReusePreflightPreparedSnapshot(config)) {
320+return await params.activateRuntimeSecrets.activatePreparedSnapshot!(preflightPrepared, {
321+reason: "startup",
322+activate: true,
323+});
324+}
325+return await params.activateRuntimeSecrets(config, {
326+reason: "startup",
327+activate: true,
328+});
329+};
267330const preflightAuthOverride = await measure("config.auth.preflight-override", () =>
268331typeof preflightConfig.gateway?.auth?.token === "string" ||
269332typeof preflightConfig.gateway?.auth?.password === "string"
@@ -296,12 +359,7 @@ export async function prepareGatewayStartupConfig(params: {
296359}),
297360);
298361const activatedConfig = (
299-await measure("config.auth.secrets-activate", () =>
300-params.activateRuntimeSecrets(runtimeStartupConfig, {
301-reason: "startup",
302-activate: true,
303-}),
304-)
362+await measure("config.auth.secrets-activate", () => activateStartupSecrets(runtimeStartupConfig))
305363).config;
306364return {
307365 ...authBootstrap,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。