fix(plugins): persist registry enabled snapshot

This commit is contained in:
Vincent Koc
2026-04-25 01:58:55 -07:00
parent 00f47f01fe
commit c959c18fc7
5 changed files with 29 additions and 2 deletions

View File

@@ -36,6 +36,7 @@ function createIndex(overrides: Partial<InstalledPluginIndex> = {}): InstalledPl
manifestHash: "manifest-hash",
rootDir: "/plugins/demo",
origin: "global",
enabled: true,
contributions: {
providers: ["demo"],
channels: ["demo-chat"],
@@ -150,7 +151,7 @@ describe("installed plugin index persistence", () => {
refreshReasons: [],
persisted: current,
current: {
plugins: [expect.objectContaining({ pluginId: "demo" })],
plugins: [expect.objectContaining({ pluginId: "demo", enabled: true })],
},
});
@@ -174,7 +175,7 @@ describe("installed plugin index persistence", () => {
refreshReasons: ["policy-changed"],
persisted: current,
current: {
plugins: [expect.objectContaining({ pluginId: "demo" })],
plugins: [expect.objectContaining({ pluginId: "demo", enabled: false })],
},
});

View File

@@ -60,6 +60,7 @@ const InstalledPluginIndexRecordSchema = z
packageJsonHash: z.string().optional(),
rootDir: z.string(),
origin: z.string(),
enabled: z.boolean(),
enabledByDefault: z.boolean().optional(),
contributions: InstalledPluginIndexContributionsSchema,
compat: z.array(z.string()),

View File

@@ -162,6 +162,7 @@ describe("installed plugin index", () => {
packageVersion: "1.2.3",
origin: "global",
rootDir: fixture.rootDir,
enabled: true,
packageInstall: {
defaultChoice: "npm",
npm: {
@@ -220,6 +221,7 @@ describe("installed plugin index", () => {
const record = getInstalledPluginRecord(index, "demo");
expect(record).toMatchObject({
pluginId: "demo",
enabled: true,
});
expect(record?.installRecord).toBeUndefined();
expect(isInstalledPluginEnabled(index, "demo")).toBe(true);
@@ -259,6 +261,7 @@ describe("installed plugin index", () => {
expect(listEnabledInstalledPluginRecords(index, config)).toEqual([]);
expect(getInstalledPluginRecord(index, "demo")).toMatchObject({
pluginId: "demo",
enabled: false,
});
expect(isInstalledPluginEnabled(index, "demo", config)).toBe(false);
expect(listInstalledPluginContributionIds(index, "providers", { config })).toEqual([]);
@@ -536,6 +539,7 @@ describe("installed plugin index", () => {
},
}),
).toBe(false);
expect(index.plugins[0]?.enabled).toBe(false);
expect(index.plugins[0]?.contributions.providers).toEqual(["demo"]);
});

View File

@@ -91,6 +91,7 @@ export type InstalledPluginIndexRecord = {
packageJsonHash?: string;
rootDir: string;
origin: PluginManifestRecord["origin"];
enabled: boolean;
enabledByDefault?: boolean;
contributions: InstalledPluginIndexContributions;
compat: readonly PluginCompatCode[];
@@ -400,6 +401,7 @@ function buildInstalledPluginIndex(
const env = params.env ?? process.env;
const { candidates, registry } = resolveRegistry(params);
const candidateByRootDir = buildCandidateLookup(candidates);
const normalizedConfig = normalizePluginsConfigWithResolver(params.config?.plugins);
const diagnostics: PluginDiagnostic[] = [...registry.diagnostics];
const generatedAtMs = (params.now?.() ?? new Date()).getTime();
const plugins = registry.plugins.map((record): InstalledPluginIndexRecord => {
@@ -422,12 +424,20 @@ function buildInstalledPluginIndex(
required: false,
})
: undefined;
const enabled = resolveEffectiveEnableState({
id: record.id,
origin: record.origin,
config: normalizedConfig,
rootConfig: params.config,
enabledByDefault: record.enabledByDefault,
}).enabled;
const indexRecord: InstalledPluginIndexRecord = {
pluginId: record.id,
manifestPath: record.manifestPath,
manifestHash,
rootDir: record.rootDir,
origin: record.origin,
enabled,
contributions: buildContributions(record),
compat: collectCompatCodes(record),
};
@@ -490,6 +500,9 @@ export function listEnabledInstalledPluginRecords(
index: InstalledPluginIndex,
config?: OpenClawConfig,
): readonly InstalledPluginIndexRecord[] {
if (!config) {
return index.plugins.filter((plugin) => plugin.enabled);
}
const normalizedConfig = normalizePluginsConfigWithResolver(config?.plugins);
return index.plugins.filter(
(plugin) =>
@@ -519,6 +532,9 @@ export function isInstalledPluginEnabled(
if (!record) {
return false;
}
if (!config) {
return record.enabled;
}
const normalizedConfig = normalizePluginsConfigWithResolver(config?.plugins);
return resolveEffectiveEnableState({
id: record.pluginId,
@@ -674,6 +690,9 @@ export function diffInstalledPluginIndexInvalidationReasons(
) {
reasons.add("source-changed");
}
if (previousPlugin.enabled !== currentPlugin.enabled) {
reasons.add("policy-changed");
}
if (previousPlugin.manifestHash !== currentPlugin.manifestHash) {
reasons.add("stale-manifest");
}

View File

@@ -97,6 +97,7 @@ describe("plugin registry facade", () => {
expect(listPluginRecords({ index }).map((plugin) => plugin.pluginId)).toEqual(["demo"]);
expect(getPluginRecord({ index, pluginId: "demo" })).toMatchObject({
pluginId: "demo",
enabled: true,
});
expect(isPluginEnabled({ index, pluginId: "demo" })).toBe(true);
expect(listPluginContributionIds({ index, contribution: "providers" })).toEqual(["demo"]);
@@ -132,6 +133,7 @@ describe("plugin registry facade", () => {
expect(getPluginRecord({ index, pluginId: "demo" })).toMatchObject({
pluginId: "demo",
enabled: false,
});
const config = {
plugins: {