mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-27 02:07:51 +00:00
fix(update): clear stale plugin refs after failed updates
This commit is contained in:
committed by
Peter Steinberger
parent
b5c3379097
commit
5214f16e29
@@ -1729,6 +1729,64 @@ describe("updateNpmInstalledPlugins", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("clears stale plugin policy and slot references when disabling failed updates", async () => {
|
||||
const warn = vi.fn();
|
||||
installPluginFromNpmSpecMock.mockResolvedValue({
|
||||
ok: false,
|
||||
error: "security scan blocked install",
|
||||
});
|
||||
const config = {
|
||||
plugins: {
|
||||
allow: ["demo", "keep"],
|
||||
deny: ["demo", "blocked"],
|
||||
slots: {
|
||||
memory: "demo",
|
||||
contextEngine: "demo",
|
||||
},
|
||||
entries: {
|
||||
demo: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
installs: {
|
||||
demo: {
|
||||
source: "npm" as const,
|
||||
spec: "@acme/demo",
|
||||
installPath: "/tmp/demo",
|
||||
},
|
||||
},
|
||||
},
|
||||
} satisfies OpenClawConfig;
|
||||
|
||||
const result = await updateNpmInstalledPlugins({
|
||||
config,
|
||||
disableOnFailure: true,
|
||||
logger: { warn },
|
||||
});
|
||||
|
||||
const message =
|
||||
'Disabled "demo" after plugin update failure; OpenClaw will continue without it. Failed to update demo: security scan blocked install';
|
||||
expect(warn).toHaveBeenCalledWith(message);
|
||||
expect(result.changed).toBe(true);
|
||||
expect(result.config.plugins?.entries?.demo).toEqual({
|
||||
enabled: false,
|
||||
});
|
||||
expect(result.config.plugins?.installs?.demo).toEqual(config.plugins.installs.demo);
|
||||
expect(result.config.plugins?.allow).toEqual(["keep"]);
|
||||
expect(result.config.plugins?.deny).toEqual(["blocked"]);
|
||||
expect(result.config.plugins?.slots).toEqual({
|
||||
memory: "memory-core",
|
||||
contextEngine: "legacy",
|
||||
});
|
||||
expect(result.outcomes).toEqual([
|
||||
{
|
||||
pluginId: "demo",
|
||||
status: "skipped",
|
||||
message,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("aborts exact pinned npm plugin updates on integrity drift by default", async () => {
|
||||
const warn = vi.fn();
|
||||
installPluginFromNpmSpecMock.mockImplementation(
|
||||
|
||||
@@ -48,6 +48,7 @@ import {
|
||||
resolveOfficialExternalPluginInstall,
|
||||
} from "./official-external-plugin-catalog.js";
|
||||
import { linkOpenClawPeerDependencies } from "./plugin-peer-link.js";
|
||||
import { defaultSlotIdForKey } from "./slots.js";
|
||||
|
||||
export type PluginUpdateLogger = {
|
||||
info?: (message: string) => void;
|
||||
@@ -760,14 +761,52 @@ function createPluginUpdateIntegrityDriftHandler(params: {
|
||||
};
|
||||
}
|
||||
|
||||
function removeDisabledPluginIdFromList(
|
||||
list: string[] | undefined,
|
||||
pluginId: string,
|
||||
): string[] | undefined {
|
||||
if (!Array.isArray(list) || !list.includes(pluginId)) {
|
||||
return list;
|
||||
}
|
||||
const next = list.filter((id) => id !== pluginId);
|
||||
return next.length > 0 ? next : undefined;
|
||||
}
|
||||
|
||||
function resetDisabledPluginSlots(
|
||||
slots: NonNullable<OpenClawConfig["plugins"]>["slots"] | undefined,
|
||||
pluginId: string,
|
||||
): NonNullable<OpenClawConfig["plugins"]>["slots"] | undefined {
|
||||
if (!slots) {
|
||||
return slots;
|
||||
}
|
||||
let next = slots;
|
||||
if (next.memory === pluginId) {
|
||||
next = {
|
||||
...next,
|
||||
memory: defaultSlotIdForKey("memory"),
|
||||
};
|
||||
}
|
||||
if (next.contextEngine === pluginId) {
|
||||
next = {
|
||||
...next,
|
||||
contextEngine: defaultSlotIdForKey("contextEngine"),
|
||||
};
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
function disablePluginConfigEntry(config: OpenClawConfig, pluginId: string): OpenClawConfig {
|
||||
const existingEntry = config.plugins?.entries?.[pluginId];
|
||||
const pluginsConfig = config.plugins ?? {};
|
||||
const existingEntry = pluginsConfig.entries?.[pluginId];
|
||||
return {
|
||||
...config,
|
||||
plugins: {
|
||||
...config.plugins,
|
||||
...pluginsConfig,
|
||||
allow: removeDisabledPluginIdFromList(pluginsConfig.allow, pluginId),
|
||||
deny: removeDisabledPluginIdFromList(pluginsConfig.deny, pluginId),
|
||||
slots: resetDisabledPluginSlots(pluginsConfig.slots, pluginId),
|
||||
entries: {
|
||||
...config.plugins?.entries,
|
||||
...pluginsConfig.entries,
|
||||
[pluginId]: {
|
||||
...existingEntry,
|
||||
enabled: false,
|
||||
|
||||
Reference in New Issue
Block a user