fix(cli): keep plugin toggles out of channel config (#76525)

This commit is contained in:
Vincent Koc
2026-05-03 00:19:37 -07:00
committed by GitHub
parent f27ecffc0c
commit 74f243a0d0
4 changed files with 34 additions and 8 deletions

View File

@@ -19,6 +19,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- CLI/plugins: keep `plugins enable` and `plugins disable` from creating unconfigured channel config sections, so channel plugins with required setup fields no longer fail validation during lifecycle probes. Thanks @vincentkoc.
- Agents/sessions: keep delayed `sessions_send` A2A replies alive after soft wait-window timeouts, while preserving terminal run timeouts and avoiding stale target replies in requester sessions. Fixes #76443. Thanks @ryswork1993 and @vincentkoc.
- CLI/sessions: keep intentional empty agent replies silent after tool-delivered channel output, instead of surfacing a misleading "No reply from agent." fallback. Thanks @vincentkoc.
- Config/doctor: cap `.clobbered.*` forensic snapshots per config path and serialize snapshot writes so repeated `doctor --fix` recovery loops cannot flood the config directory. Fixes #76454; carries forward #65649. Thanks @JUSTICEESSIELP, @rsnow, and @vincentkoc.

View File

@@ -198,11 +198,15 @@ vi.mock("../plugins/marketplace.js", () => ({
}));
vi.mock("../plugins/enable.js", () => ({
enablePluginInConfig: ((cfg: OpenClawConfig, pluginId: string) =>
invokeMock<[OpenClawConfig, string], unknown>(
enablePluginInConfig: ((
...args: Parameters<(typeof import("../plugins/enable.js"))["enablePluginInConfig"]>
) =>
invokeMock<
Parameters<(typeof import("../plugins/enable.js"))["enablePluginInConfig"]>,
unknown
>(
enablePluginInConfig,
cfg,
pluginId,
...args,
)) as (typeof import("../plugins/enable.js"))["enablePluginInConfig"],
}));

View File

@@ -32,6 +32,7 @@ describe("plugins cli policy mutations", () => {
}
it("refreshes the persisted plugin registry after enabling a plugin", async () => {
const sourceConfig = {} as OpenClawConfig;
const enabledConfig = {
plugins: {
entries: {
@@ -39,7 +40,7 @@ describe("plugins cli policy mutations", () => {
},
},
} as OpenClawConfig;
loadConfig.mockReturnValue({} as OpenClawConfig);
loadConfig.mockReturnValue(sourceConfig);
enablePluginInConfig.mockReturnValue({
config: enabledConfig,
enabled: true,
@@ -49,6 +50,9 @@ describe("plugins cli policy mutations", () => {
await runPluginsCommand(["plugins", "enable", "alpha"]);
expect(enablePluginInConfig).toHaveBeenCalledWith(sourceConfig, "alpha", {
updateChannelConfig: false,
});
expect(writeConfigFile).toHaveBeenCalledWith(enabledConfig);
expect(refreshPluginRegistry).toHaveBeenCalledWith({
config: enabledConfig,
@@ -100,7 +104,9 @@ describe("plugins cli policy mutations", () => {
await runPluginsCommand(["plugins", "enable", alias]);
expect(enablePluginInConfig).toHaveBeenCalledWith(sourceConfig, pluginId);
expect(enablePluginInConfig).toHaveBeenCalledWith(sourceConfig, pluginId, {
updateChannelConfig: false,
});
expect(writeConfigFile).toHaveBeenCalledWith(enabledConfig);
},
);
@@ -142,4 +148,15 @@ describe("plugins cli policy mutations", () => {
expect(refreshPluginRegistry).not.toHaveBeenCalled();
},
);
it("does not create a channel config when disabling a channel plugin by policy", async () => {
loadConfig.mockReturnValue({} as OpenClawConfig);
mockPluginRegistry(["twitch"]);
await runPluginsCommand(["plugins", "disable", "twitch"]);
const nextConfig = writeConfigFile.mock.calls[0]?.[0] as OpenClawConfig;
expect(nextConfig.plugins?.entries?.twitch?.enabled).toBe(false);
expect(nextConfig.channels?.twitch).toBeUndefined();
});
});

View File

@@ -126,7 +126,9 @@ export function registerPluginsCli(program: Command) {
if (!report.plugins.some((plugin) => matchesPluginId(plugin, id))) {
return reportMissingPlugin(id);
}
const enableResult = enablePluginInConfig(cfg, id);
const enableResult = enablePluginInConfig(cfg, id, {
updateChannelConfig: false,
});
let next: OpenClawConfig = enableResult.config;
const slotResult = applySlotSelectionForPlugin(next, id);
next = slotResult.config;
@@ -171,7 +173,9 @@ export function registerPluginsCli(program: Command) {
if (!report.plugins.some((plugin) => matchesPluginId(plugin, id))) {
return reportMissingPlugin(id);
}
const next = setPluginEnabledInConfig(cfg, id, false);
const next = setPluginEnabledInConfig(cfg, id, false, {
updateChannelConfig: false,
});
await replaceConfigFile({
nextConfig: next,
...(snapshot.hash !== undefined ? { baseHash: snapshot.hash } : {}),