fix(plugins): migrate only enabled registry entries

This commit is contained in:
Vincent Koc
2026-04-25 03:59:48 -07:00
parent d7cf803705
commit f22a2f7e8b
2 changed files with 58 additions and 11 deletions

View File

@@ -34,7 +34,7 @@ function hermeticEnv(overrides: NodeJS.ProcessEnv = {}): NodeJS.ProcessEnv {
};
}
function createCandidate(rootDir: string): PluginCandidate {
function createCandidate(rootDir: string, id = "demo"): PluginCandidate {
fs.writeFileSync(
path.join(rootDir, "index.ts"),
"throw new Error('runtime entry should not load while migrating plugin registry');\n",
@@ -43,15 +43,15 @@ function createCandidate(rootDir: string): PluginCandidate {
fs.writeFileSync(
path.join(rootDir, "openclaw.plugin.json"),
JSON.stringify({
id: "demo",
name: "Demo",
id,
name: id,
configSchema: { type: "object" },
providers: ["demo"],
providers: [id],
}),
"utf8",
);
return {
idHint: "demo",
idHint: id,
source: path.join(rootDir, "index.ts"),
rootDir,
origin: "global",
@@ -83,6 +83,45 @@ describe("plugin registry install migration", () => {
expect(readConfig).not.toHaveBeenCalled();
});
it("persists only plugins enabled by the central config policy", async () => {
const stateDir = makeTempDir();
const enabledDir = path.join(stateDir, "plugins", "enabled-demo");
const disabledDir = path.join(stateDir, "plugins", "disabled-demo");
fs.mkdirSync(enabledDir, { recursive: true });
fs.mkdirSync(disabledDir, { recursive: true });
await expect(
migratePluginRegistryForInstall({
stateDir,
candidates: [
createCandidate(enabledDir, "enabled-demo"),
createCandidate(disabledDir, "disabled-demo"),
],
readConfig: async () => ({
plugins: {
entries: {
"disabled-demo": {
enabled: false,
},
},
},
}),
env: hermeticEnv(),
}),
).resolves.toMatchObject({
status: "migrated",
current: {
plugins: [expect.objectContaining({ pluginId: "enabled-demo" })],
},
});
await expect(readPersistedInstalledPluginIndex({ stateDir })).resolves.toMatchObject({
plugins: [expect.objectContaining({ pluginId: "enabled-demo" })],
});
const persisted = await readPersistedInstalledPluginIndex({ stateDir });
expect(persisted?.plugins.map((plugin) => plugin.pluginId)).toEqual(["enabled-demo"]);
});
it("supports dry-run preflight without reading config or writing the registry", async () => {
const stateDir = makeTempDir();
const readConfig = vi.fn(async () => ({}));

View File

@@ -2,14 +2,16 @@ import fs from "node:fs";
import type { OpenClawConfig } from "../../../config/types.openclaw.js";
import {
inspectPersistedInstalledPluginIndex,
refreshPersistedInstalledPluginIndex,
resolveInstalledPluginIndexStorePath,
writePersistedInstalledPluginIndex,
type InstalledPluginIndexStoreInspection,
type InstalledPluginIndexStoreOptions,
} from "../../../plugins/installed-plugin-index-store.js";
import type {
InstalledPluginIndex,
LoadInstalledPluginIndexParams,
import {
listEnabledInstalledPluginRecords,
loadInstalledPluginIndex,
type InstalledPluginIndex,
type LoadInstalledPluginIndexParams,
} from "../../../plugins/installed-plugin-index.js";
export const DISABLE_PLUGIN_REGISTRY_MIGRATION_ENV = "OPENCLAW_DISABLE_PLUGIN_REGISTRY_MIGRATION";
@@ -121,10 +123,16 @@ export async function migratePluginRegistryForInstall(
config,
};
const inspection = await inspectPersistedInstalledPluginIndex(migrationParams);
const current = await refreshPersistedInstalledPluginIndex({
const candidateIndex = loadInstalledPluginIndex({
...migrationParams,
reason: "migration",
cache: false,
});
const current: InstalledPluginIndex = {
...candidateIndex,
refreshReason: "migration",
plugins: listEnabledInstalledPluginRecords(candidateIndex, config),
};
await writePersistedInstalledPluginIndex(current, params);
return {
status: "migrated",
migrated: true,