Revert "fix(plugins): persist registry contribution metadata"

This reverts commit 1ee5654220.
This commit is contained in:
Shakker
2026-04-26 08:10:37 +01:00
parent 59d1fa65df
commit b5e4e2f257
7 changed files with 26 additions and 315 deletions

View File

@@ -48,11 +48,6 @@ const InstalledPluginIndexStartupSchema = z
const InstalledPluginIndexRecordSchema = z
.object({
pluginId: z.string(),
contributionMetadataVersion: z.number().optional(),
name: z.string().optional(),
description: z.string().optional(),
manifestVersion: z.string().optional(),
legacyPluginIds: StringArraySchema.optional(),
packageName: z.string().optional(),
packageVersion: z.string().optional(),
installRecord: z.record(z.string(), z.unknown()).optional(),
@@ -63,16 +58,6 @@ const InstalledPluginIndexRecordSchema = z
manifestHash: z.string(),
format: z.string().optional(),
bundleFormat: z.string().optional(),
bundleCapabilities: StringArraySchema.optional(),
kind: z.unknown().optional(),
channels: StringArraySchema.optional(),
providers: StringArraySchema.optional(),
cliBackends: StringArraySchema.optional(),
setupProviders: StringArraySchema.optional(),
channelConfigs: StringArraySchema.optional(),
modelCatalogProviders: StringArraySchema.optional(),
commandAliases: StringArraySchema.optional(),
contractKeys: StringArraySchema.optional(),
source: z.string().optional(),
setupSource: z.string().optional(),
packageJson: z

View File

@@ -175,18 +175,8 @@ describe("installed plugin index", () => {
plugins: [
{
pluginId: "demo",
contributionMetadataVersion: 1,
name: "Demo",
packageName: "@vendor/demo-plugin",
packageVersion: "1.2.3",
channels: ["demo-chat"],
channelConfigs: ["demo-chat"],
cliBackends: ["demo-cli", "setup-cli"],
commandAliases: ["demo-command"],
contractKeys: ["tools"],
modelCatalogProviders: ["demo"],
providers: ["demo"],
setupProviders: ["demo"],
origin: "global",
rootDir: fixture.rootDir,
source: path.join(fixture.rootDir, "index.ts"),

View File

@@ -23,7 +23,6 @@ import { hasKind } from "./slots.js";
export const INSTALLED_PLUGIN_INDEX_VERSION = 1;
export const INSTALLED_PLUGIN_INDEX_MIGRATION_VERSION = 1;
export const INSTALLED_PLUGIN_CONTRIBUTION_METADATA_VERSION = 1;
export const INSTALLED_PLUGIN_INDEX_WARNING =
"DO NOT EDIT. This file is generated by OpenClaw from plugin manifests, install records, and config policy. Use `openclaw plugins registry --refresh`, `openclaw plugins install/update/uninstall`, or `openclaw plugins enable/disable` instead.";
@@ -75,11 +74,6 @@ export type InstalledPluginPackageChannelInfo = Pick<
export type InstalledPluginIndexRecord = {
pluginId: string;
contributionMetadataVersion?: typeof INSTALLED_PLUGIN_CONTRIBUTION_METADATA_VERSION;
name?: string;
description?: string;
manifestVersion?: string;
legacyPluginIds?: readonly string[];
packageName?: string;
packageVersion?: string;
/**
@@ -99,16 +93,6 @@ export type InstalledPluginIndexRecord = {
manifestHash: string;
format?: PluginManifestRecord["format"];
bundleFormat?: PluginManifestRecord["bundleFormat"];
bundleCapabilities?: readonly string[];
kind?: PluginManifestRecord["kind"];
channels?: readonly string[];
providers?: readonly string[];
cliBackends?: readonly string[];
setupProviders?: readonly string[];
channelConfigs?: readonly string[];
modelCatalogProviders?: readonly string[];
commandAliases?: readonly string[];
contractKeys?: readonly string[];
source?: string;
setupSource?: string;
packageJson?: {
@@ -225,18 +209,6 @@ function buildStartupInfo(record: PluginManifestRecord): InstalledPluginStartupI
};
}
function collectContractKeys(record: PluginManifestRecord): readonly string[] | undefined {
const contracts = record.contracts;
if (!contracts) {
return undefined;
}
return normalizeStringList(
Object.entries(contracts).flatMap(([key, value]) =>
Array.isArray(value) && value.length > 0 ? [key] : [],
),
);
}
function collectCompatCodes(record: PluginManifestRecord): readonly PluginCompatCode[] {
const codes: PluginCompatCode[] = [];
if (record.providerAuthEnvVars && Object.keys(record.providerAuthEnvVars).length > 0) {
@@ -335,10 +307,6 @@ function normalizeStringListField(value: unknown): readonly string[] | undefined
return normalized.length > 0 ? normalized : undefined;
}
function normalizeStringList(values: readonly string[] | undefined): readonly string[] | undefined {
return normalizeStringListField(values);
}
function normalizePackageChannel(
channel: PluginPackageChannel | undefined,
): InstalledPluginPackageChannelInfo | undefined {
@@ -608,7 +576,6 @@ function buildInstalledPluginIndex(
}).enabled;
const indexRecord: InstalledPluginIndexRecord = {
pluginId: record.id,
contributionMetadataVersion: INSTALLED_PLUGIN_CONTRIBUTION_METADATA_VERSION,
manifestPath: record.manifestPath,
manifestHash,
source: record.source,
@@ -618,70 +585,12 @@ function buildInstalledPluginIndex(
startup: buildStartupInfo(record),
compat: collectCompatCodes(record),
};
if (record.name) {
indexRecord.name = record.name;
}
if (record.description) {
indexRecord.description = record.description;
}
if (record.version) {
indexRecord.manifestVersion = record.version;
}
const legacyPluginIds = normalizeStringList(record.legacyPluginIds);
if (legacyPluginIds) {
indexRecord.legacyPluginIds = legacyPluginIds;
}
if (record.format && record.format !== "openclaw") {
indexRecord.format = record.format;
}
if (record.bundleFormat) {
indexRecord.bundleFormat = record.bundleFormat;
}
if (record.bundleCapabilities?.length) {
indexRecord.bundleCapabilities = normalizeStringList(record.bundleCapabilities);
}
if (record.kind) {
indexRecord.kind = record.kind;
}
const channels = normalizeStringList(record.channels);
if (channels) {
indexRecord.channels = channels;
}
const providers = normalizeStringList(record.providers);
if (providers) {
indexRecord.providers = providers;
}
const cliBackends = normalizeStringList([
...record.cliBackends,
...(record.setup?.cliBackends ?? []),
]);
if (cliBackends) {
indexRecord.cliBackends = cliBackends;
}
const setupProviders = normalizeStringList(
record.setup?.providers?.map((provider) => provider.id),
);
if (setupProviders) {
indexRecord.setupProviders = setupProviders;
}
const channelConfigs = normalizeStringList(Object.keys(record.channelConfigs ?? {}));
if (channelConfigs) {
indexRecord.channelConfigs = channelConfigs;
}
const modelCatalogProviders = normalizeStringList(
Object.keys(record.modelCatalog?.providers ?? {}),
);
if (modelCatalogProviders) {
indexRecord.modelCatalogProviders = modelCatalogProviders;
}
const commandAliases = normalizeStringList(record.commandAliases?.map((alias) => alias.name));
if (commandAliases) {
indexRecord.commandAliases = commandAliases;
}
const contractKeys = collectContractKeys(record);
if (contractKeys) {
indexRecord.contractKeys = contractKeys;
}
if (record.enabledByDefault === true) {
indexRecord.enabledByDefault = true;
}

View File

@@ -185,24 +185,6 @@ describe("plugin registry facade", () => {
).toEqual(["demo"]);
});
it("resolves indexed contribution owners without reopening manifest roots", () => {
const rootDir = makeTempDir();
const candidate = createCandidate(rootDir);
const index = loadPluginRegistrySnapshot({
candidates: [candidate],
env: hermeticEnv(),
preferPersisted: false,
});
fs.rmSync(rootDir, { recursive: true, force: true });
expect(listPluginContributionIds({ index, contribution: "providers" })).toEqual(["demo"]);
expect(resolveProviderOwners({ index, providerId: "demo" })).toEqual(["demo"]);
expect(resolveChannelOwners({ index, channelId: "demo-chat" })).toEqual(["demo"]);
expect(resolveCliBackendOwners({ index, cliBackendId: "demo-setup-cli" })).toEqual(["demo"]);
expect(resolveSetupProviderOwners({ index, setupProviderId: "demo-setup" })).toEqual(["demo"]);
expect(createPluginRegistryIdNormalizer(index)("demo-chat")).toBe("demo");
});
it("keeps disabled records inspectable while excluding owners by default", () => {
const rootDir = makeTempDir();
const candidate = createCandidate(rootDir);

View File

@@ -12,7 +12,6 @@ import {
type InstalledPluginIndexStoreOptions,
} from "./installed-plugin-index-store.js";
import {
INSTALLED_PLUGIN_CONTRIBUTION_METADATA_VERSION,
getInstalledPluginRecord,
extractPluginInstallRecordsFromInstalledPluginIndex,
isInstalledPluginEnabled,
@@ -208,48 +207,6 @@ function listManifestContributionIds(
return [];
}
function hasIndexedContributionMetadata(plugin: InstalledPluginIndexRecord): boolean {
return plugin.contributionMetadataVersion === INSTALLED_PLUGIN_CONTRIBUTION_METADATA_VERSION;
}
function listIndexedContributionIds(
plugin: InstalledPluginIndexRecord,
contribution: PluginRegistryContributionKey,
): readonly string[] {
switch (contribution) {
case "providers":
return plugin.providers ?? [];
case "channels":
return plugin.channels ?? [];
case "channelConfigs":
return plugin.channelConfigs ?? [];
case "setupProviders":
return plugin.setupProviders ?? [];
case "cliBackends":
return plugin.cliBackends ?? [];
case "modelCatalogProviders":
return plugin.modelCatalogProviders ?? [];
case "commandAliases":
return plugin.commandAliases ?? [];
case "contracts":
return plugin.contractKeys ?? [];
}
return [];
}
function listContributionIndexRecords(params: {
index: PluginRegistrySnapshot;
includeDisabled?: boolean;
config?: OpenClawConfig;
}): readonly InstalledPluginIndexRecord[] {
if (params.includeDisabled) {
return params.index.plugins;
}
return params.index.plugins.filter((plugin) =>
isInstalledPluginEnabled(params.index, plugin.pluginId, params.config),
);
}
function resolveContributionPluginIds(params: {
index: PluginRegistrySnapshot;
includeDisabled?: boolean;
@@ -307,35 +264,6 @@ export function createPluginRegistryIdNormalizer(
aliases.set(normalizePluginRegistryAliasKey(pluginId), plugin.pluginId);
}
}
if (index.plugins.every(hasIndexedContributionMetadata)) {
for (const plugin of [...index.plugins].toSorted((left, right) =>
left.pluginId.localeCompare(right.pluginId),
)) {
const pluginId = normalizePluginRegistryAlias(plugin.pluginId);
if (!pluginId) {
continue;
}
for (const alias of [
plugin.pluginId,
...(plugin.providers ?? []),
...(plugin.channels ?? []),
...(plugin.setupProviders ?? []),
...(plugin.cliBackends ?? []),
...(plugin.modelCatalogProviders ?? []),
...(plugin.legacyPluginIds ?? []),
]) {
const normalizedAlias = normalizePluginRegistryAlias(alias);
const normalizedAliasKey = normalizePluginRegistryAliasKey(alias);
if (normalizedAlias && !aliases.has(normalizedAliasKey)) {
aliases.set(normalizedAliasKey, pluginId);
}
}
}
return (pluginId: string) => {
const trimmed = normalizePluginRegistryAlias(pluginId);
return aliases.get(normalizePluginRegistryAliasKey(trimmed)) ?? trimmed;
};
}
const registry = loadPluginManifestRegistryForInstalledIndex({
index,
includeDisabled: true,
@@ -477,16 +405,6 @@ export function listPluginContributionIds(
params: ListPluginContributionIdsParams,
): readonly string[] {
const index = resolveSnapshot(params);
const records = listContributionIndexRecords({
index,
includeDisabled: params.includeDisabled,
config: params.config,
});
if (records.every(hasIndexedContributionMetadata)) {
return sortUnique(
records.flatMap((plugin) => listIndexedContributionIds(plugin, params.contribution)),
);
}
const registry = loadContributionManifestRegistry({
...params,
index,
@@ -504,20 +422,6 @@ export function resolvePluginContributionOwners(
? (contributionId: string) => contributionId === params.matches
: params.matches;
const index = resolveSnapshot(params);
const records = listContributionIndexRecords({
index,
includeDisabled: params.includeDisabled,
config: params.config,
});
if (records.every(hasIndexedContributionMetadata)) {
return sortUnique(
records.flatMap((plugin) =>
listIndexedContributionIds(plugin, params.contribution).some(matcher)
? [plugin.pluginId]
: [],
),
);
}
const registry = loadContributionManifestRegistry({
...params,
index,

View File

@@ -3,8 +3,6 @@ import os from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { clearPluginDiscoveryCache } from "./discovery.js";
import { writePersistedInstalledPluginIndex } from "./installed-plugin-index-store.js";
import { loadInstalledPluginIndex } from "./installed-plugin-index.js";
import { clearPluginManifestRegistryCache } from "./manifest-registry.js";
import { buildPluginRegistrySnapshotReport } from "./status.js";
@@ -25,7 +23,7 @@ afterEach(() => {
});
describe("buildPluginRegistrySnapshotReport", () => {
it("reports list metadata from the installed index without importing plugin runtime", () => {
it("reconstructs list metadata from indexed manifests without importing plugin runtime", () => {
const pluginDir = makeTempDir();
const runtimeMarker = path.join(pluginDir, "runtime-loaded.txt");
fs.writeFileSync(
@@ -82,71 +80,4 @@ describe("buildPluginRegistrySnapshotReport", () => {
});
expect(fs.existsSync(runtimeMarker)).toBe(false);
});
it("reports persisted indexed metadata without reopening stale manifest roots", async () => {
const pluginDir = makeTempDir();
const stateDir = makeTempDir();
const env = {
OPENCLAW_STATE_DIR: stateDir,
OPENCLAW_VERSION: "2026.4.25",
VITEST: "true",
};
fs.writeFileSync(
path.join(pluginDir, "package.json"),
JSON.stringify({
name: "@example/openclaw-stale-indexed-demo",
version: "4.5.6",
openclaw: { extensions: ["./index.cjs"] },
}),
"utf-8",
);
fs.writeFileSync(
path.join(pluginDir, "openclaw.plugin.json"),
JSON.stringify({
id: "stale-indexed-demo",
name: "Stale Indexed Demo",
description: "Persisted list metadata",
version: "1.0.0",
providers: ["stale-provider"],
commandAliases: [{ name: "stale-command" }],
}),
"utf-8",
);
fs.writeFileSync(path.join(pluginDir, "index.cjs"), "module.exports = {};\n", "utf-8");
const index = loadInstalledPluginIndex({
config: {},
env,
candidates: [
{
idHint: "stale-indexed-demo",
source: path.join(pluginDir, "index.cjs"),
rootDir: pluginDir,
origin: "global",
packageName: "@example/openclaw-stale-indexed-demo",
packageVersion: "4.5.6",
packageDir: pluginDir,
},
],
});
await writePersistedInstalledPluginIndex(index, { stateDir });
fs.rmSync(pluginDir, { recursive: true, force: true });
const report = buildPluginRegistrySnapshotReport({
config: {},
env,
});
expect(report.registrySource).toBe("persisted");
expect(report.plugins).toEqual([
expect.objectContaining({
id: "stale-indexed-demo",
name: "Stale Indexed Demo",
description: "Persisted list metadata",
version: "4.5.6",
providerIds: ["stale-provider"],
commands: ["stale-command"],
}),
]);
});
});

View File

@@ -18,6 +18,8 @@ import {
type PluginInspectShape,
} from "./inspect-shape.js";
import { loadOpenClawPlugins } from "./loader.js";
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
import type { PluginManifestRecord } from "./manifest-registry.js";
import type { PluginDiagnostic } from "./manifest-types.js";
import {
loadPluginRegistrySnapshotWithMetadata,
@@ -155,22 +157,20 @@ type PluginReportParams = {
function buildPluginRecordFromInstalledIndex(
plugin: import("./installed-plugin-index.js").InstalledPluginIndexRecord,
manifest?: PluginManifestRecord,
): PluginRecord {
const format = plugin.format ?? "openclaw";
const bundleFormat = plugin.bundleFormat;
const format = plugin.format ?? manifest?.format ?? "openclaw";
const bundleFormat = plugin.bundleFormat ?? manifest?.bundleFormat;
return {
id: plugin.pluginId,
name: plugin.name ?? plugin.packageName ?? plugin.pluginId,
...(plugin.packageVersion || plugin.manifestVersion
? { version: plugin.packageVersion ?? plugin.manifestVersion }
name: manifest?.name ?? plugin.packageName ?? plugin.pluginId,
...(plugin.packageVersion || manifest?.version
? { version: plugin.packageVersion ?? manifest?.version }
: {}),
...(plugin.description ? { description: plugin.description } : {}),
...(manifest?.description ? { description: manifest.description } : {}),
format,
...(bundleFormat ? { bundleFormat } : {}),
...(plugin.bundleCapabilities?.length
? { bundleCapabilities: [...plugin.bundleCapabilities] }
: {}),
...(plugin.kind ? { kind: plugin.kind } : {}),
...(manifest?.kind ? { kind: manifest.kind } : {}),
source: plugin.source ?? plugin.manifestPath,
rootDir: plugin.rootDir,
origin: plugin.origin,
@@ -178,9 +178,9 @@ function buildPluginRecordFromInstalledIndex(
status: plugin.enabled ? "loaded" : "disabled",
toolNames: [],
hookNames: [],
channelIds: [...(plugin.channels ?? [])],
cliBackendIds: [...(plugin.cliBackends ?? [])],
providerIds: [...(plugin.providers ?? [])],
channelIds: [...(manifest?.channels ?? [])],
cliBackendIds: [...(manifest?.cliBackends ?? []), ...(manifest?.setup?.cliBackends ?? [])],
providerIds: [...(manifest?.providers ?? [])],
speechProviderIds: [],
realtimeTranscriptionProviderIds: [],
realtimeVoiceProviderIds: [],
@@ -196,7 +196,7 @@ function buildPluginRecordFromInstalledIndex(
cliCommands: [],
services: [],
gatewayDiscoveryServiceIds: [],
commands: [...(plugin.commandAliases ?? [])],
commands: [...(manifest?.commandAliases?.map((alias) => alias.name) ?? [])],
httpRoutes: 0,
hookCount: 0,
configSchema: false,
@@ -213,10 +213,20 @@ export function buildPluginRegistrySnapshotReport(
env: params?.env,
workspaceDir: params?.workspaceDir,
});
const manifestRegistry = loadPluginManifestRegistryForInstalledIndex({
index: result.snapshot,
config,
env: params?.env,
workspaceDir: params?.workspaceDir,
includeDisabled: true,
});
const manifestByPluginId = new Map(manifestRegistry.plugins.map((plugin) => [plugin.id, plugin]));
return {
workspaceDir: params?.workspaceDir,
...createEmptyPluginRegistry(),
plugins: result.snapshot.plugins.map((plugin) => buildPluginRecordFromInstalledIndex(plugin)),
plugins: result.snapshot.plugins.map((plugin) =>
buildPluginRecordFromInstalledIndex(plugin, manifestByPluginId.get(plugin.pluginId)),
),
diagnostics: [...result.snapshot.diagnostics],
registrySource: result.source,
registryDiagnostics: result.diagnostics,