fix: clone setup channels from owned exports

This commit is contained in:
Gustavo Madeira Santana
2026-04-21 11:08:35 -04:00
parent 454757acd3
commit 99f5e756a4
2 changed files with 57 additions and 11 deletions

View File

@@ -269,6 +269,48 @@ describe("listReadOnlyChannelPluginsForConfig", () => {
expect(fs.existsSync(fullMarker)).toBe(false);
});
it("clones setup-only plugins when only another owned channel is configured", () => {
const { pluginDir, fullMarker, setupMarker } = writeExternalSetupChannelPlugin({
pluginId: "external-chat-plugin",
channelId: "alpha-chat",
manifestChannelIds: ["alpha-chat", "beta-chat"],
setupChannelId: "alpha-chat",
});
const plugins = listReadOnlyChannelPluginsForConfig(
{
channels: {
"beta-chat": { token: "beta-token" },
},
plugins: {
load: { paths: [pluginDir] },
allow: ["external-chat-plugin"],
},
} as never,
{
env: { ...process.env },
includePersistedAuthState: false,
},
);
expect(plugins.some((entry) => entry.id === "alpha-chat")).toBe(false);
const betaPlugin = plugins.find((entry) => entry.id === "beta-chat");
expect(betaPlugin?.meta.id).toBe("beta-chat");
expect(
betaPlugin?.secrets?.secretTargetRegistryEntries?.some(
(entry) => entry.id === "channels.beta-chat.token",
),
).toBe(true);
expect(
betaPlugin?.config.resolveAccount({
channels: {
"beta-chat": { token: "beta-token" },
},
} as never),
).toMatchObject({ token: "beta-token" });
expect(fs.existsSync(setupMarker)).toBe(true);
expect(fs.existsSync(fullMarker)).toBe(false);
});
it("keeps configured external channels visible when no setup entry exists", () => {
const { pluginDir, fullMarker, setupMarker } = writeExternalSetupChannelPlugin({
setupEntry: false,
@@ -363,7 +405,7 @@ describe("listReadOnlyChannelPluginsForConfig", () => {
const { pluginDir, fullMarker, setupMarker } = writeExternalSetupChannelPlugin({
pluginId: "external-chat-plugin",
channelId: "external-chat",
manifestChannelIds: ["external-chat", "spoofed-chat"],
manifestChannelIds: ["external-chat"],
setupChannelId: "spoofed-chat",
});
const plugins = listReadOnlyChannelPluginsForConfig(

View File

@@ -258,6 +258,7 @@ function addSetupChannelPlugins(
plugin: ChannelPlugin;
}>,
options: {
ownedChannelIdsByPluginId: ReadonlyMap<string, readonly string[]>;
ownedMissingChannelIdsByPluginId: ReadonlyMap<string, readonly string[]>;
},
): void {
@@ -283,7 +284,8 @@ function addSetupChannelPlugins(
);
continue;
}
if (setup.plugin.id !== setup.pluginId) {
const ownedChannelIds = options.ownedChannelIdsByPluginId.get(setup.pluginId) ?? [];
if (setup.plugin.id !== setup.pluginId && !ownedChannelIds.includes(setup.plugin.id)) {
continue;
}
addChannelPlugins(
@@ -422,16 +424,17 @@ export function resolveReadOnlyChannelPluginsForConfig(
});
if (externalPluginIds.length > 0) {
const missingChannelIdSet = new Set(missingConfiguredChannelIds);
const ownedMissingChannelIdsByPluginId = new Map(
const externalPluginIdSet = new Set(externalPluginIds);
const ownedChannelIdsByPluginId = new Map(
externalManifestRecords
.filter((record) => externalPluginIds.includes(record.id))
.map(
(record) =>
[
record.id,
record.channels.filter((channelId) => missingChannelIdSet.has(channelId)),
] as const,
),
.filter((record) => externalPluginIdSet.has(record.id))
.map((record) => [record.id, record.channels] as const),
);
const ownedMissingChannelIdsByPluginId = new Map(
[...ownedChannelIdsByPluginId].map(
([pluginId, channelIds]) =>
[pluginId, channelIds.filter((channelId) => missingChannelIdSet.has(channelId))] as const,
),
);
const registry = loadOpenClawPlugins({
config: cfg,
@@ -446,6 +449,7 @@ export function resolveReadOnlyChannelPluginsForConfig(
onlyPluginIds: externalPluginIds,
});
addSetupChannelPlugins(byId, registry.channelSetups, {
ownedChannelIdsByPluginId,
ownedMissingChannelIdsByPluginId,
});
}