fix: preserve setup-entry secrets

This commit is contained in:
Mason Huang
2026-04-14 21:33:10 +08:00
parent 2958b03d0a
commit 5816227f29
2 changed files with 107 additions and 9 deletions

View File

@@ -523,6 +523,7 @@ function createSetupEntryChannelPluginFixture(params: {
configured: boolean;
startupDeferConfiguredChannelFullLoadUntilAfterListen?: boolean;
useBundledSetupEntryContract?: boolean;
splitBundledSetupSecrets?: boolean;
}) {
useNoBundledPlugins();
const pluginDir = makeTempDir();
@@ -618,6 +619,18 @@ module.exports = {
},
outbound: { deliveryMode: "direct" },
}),
${
params.splitBundledSetupSecrets
? `loadSetupSecrets: () => ({
secretTargetRegistryEntries: [
{
id: ${JSON.stringify(`channels.${params.id}.setup-token`)},
targetType: "channel",
},
],
}),`
: ""
}
};`
: `require("node:fs").writeFileSync(${JSON.stringify(setupMarker)}, "loaded", "utf-8");
module.exports = {
@@ -3215,6 +3228,33 @@ module.exports = {
expectSetupLoaded: true,
expectedChannels: 1,
},
{
name: "preserves bundled setupEntry split secrets for setup-runtime channel loads",
fixture: {
id: "setup-runtime-bundled-contract-secrets-test",
label: "Setup Runtime Bundled Contract Secrets Test",
packageName: "@openclaw/setup-runtime-bundled-contract-secrets-test",
fullBlurb: "full entry should not run while unconfigured",
setupBlurb: "setup runtime bundled contract secrets",
configured: false,
useBundledSetupEntryContract: true,
splitBundledSetupSecrets: true,
},
load: ({ pluginDir }: { pluginDir: string }) =>
loadOpenClawPlugins({
cache: false,
config: {
plugins: {
load: { paths: [pluginDir] },
allow: ["setup-runtime-bundled-contract-secrets-test"],
},
},
}),
expectFullLoaded: false,
expectSetupLoaded: true,
expectedChannels: 1,
expectedSetupSecretId: "channels.setup-runtime-bundled-contract-secrets-test.setup-token",
},
{
name: "does not prefer setupEntry for configured channel loads without startup opt-in",
fixture: {
@@ -3246,15 +3286,41 @@ module.exports = {
expectSetupLoaded: false,
expectedChannels: 1,
},
])("$name", ({ fixture, load, expectFullLoaded, expectSetupLoaded, expectedChannels }) => {
const built = createSetupEntryChannelPluginFixture(fixture);
const registry = load({ pluginDir: built.pluginDir });
])(
"$name",
({
fixture,
load,
expectFullLoaded,
expectSetupLoaded,
expectedChannels,
expectedSetupSecretId,
}) => {
const built = createSetupEntryChannelPluginFixture(fixture);
const registry = load({ pluginDir: built.pluginDir });
expect(fs.existsSync(built.fullMarker)).toBe(expectFullLoaded);
expect(fs.existsSync(built.setupMarker)).toBe(expectSetupLoaded);
expect(registry.channelSetups).toHaveLength(1);
expect(registry.channels).toHaveLength(expectedChannels);
});
expect(fs.existsSync(built.fullMarker)).toBe(expectFullLoaded);
expect(fs.existsSync(built.setupMarker)).toBe(expectSetupLoaded);
expect(registry.channelSetups).toHaveLength(1);
expect(registry.channels).toHaveLength(expectedChannels);
if (expectedSetupSecretId) {
expect(registry.channelSetups[0]?.plugin.secrets?.secretTargetRegistryEntries).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: expectedSetupSecretId,
}),
]),
);
expect(registry.channels[0]?.plugin.secrets?.secretTargetRegistryEntries).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: expectedSetupSecretId,
}),
]),
);
}
},
);
it("isolates loadSetupPlugin errors as per-plugin diagnostics instead of crashing registry load", () => {
useNoBundledPlugins();

View File

@@ -644,6 +644,26 @@ function resolvePluginModuleExport(moduleExport: unknown): {
return {};
}
function mergeSetupPluginSection<T>(
baseValue: T | undefined,
setupValue: T | undefined,
): T | undefined {
if (baseValue && setupValue && typeof baseValue === "object" && typeof setupValue === "object") {
const merged = {
...(baseValue as Record<string, unknown>),
};
for (const [key, value] of Object.entries(setupValue as Record<string, unknown>)) {
if (value !== undefined) {
merged[key] = value;
}
}
return {
...merged,
} as T;
}
return setupValue ?? baseValue;
}
function resolveSetupChannelRegistration(moduleExport: unknown): {
plugin?: ChannelPlugin;
loadError?: unknown;
@@ -655,6 +675,7 @@ function resolveSetupChannelRegistration(moduleExport: unknown): {
const setupEntryRecord = resolved as {
kind?: unknown;
loadSetupPlugin?: unknown;
loadSetupSecrets?: unknown;
};
if (
setupEntryRecord.kind === "bundled-channel-setup-entry" &&
@@ -662,9 +683,20 @@ function resolveSetupChannelRegistration(moduleExport: unknown): {
) {
try {
const loadedPlugin = setupEntryRecord.loadSetupPlugin();
const loadedSecrets =
typeof setupEntryRecord.loadSetupSecrets === "function"
? (setupEntryRecord.loadSetupSecrets() as ChannelPlugin["secrets"] | undefined)
: undefined;
if (loadedPlugin && typeof loadedPlugin === "object") {
const mergedSecrets = mergeSetupPluginSection(
(loadedPlugin as ChannelPlugin).secrets,
loadedSecrets,
);
return {
plugin: loadedPlugin as ChannelPlugin,
plugin: {
...(loadedPlugin as ChannelPlugin),
...(mergedSecrets !== undefined ? { secrets: mergedSecrets } : {}),
},
};
}
} catch (err) {