mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 09:00:42 +00:00
refactor: split plugin metadata normalizer
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { resolveInstalledPluginIndexPolicyHash } from "./installed-plugin-index-policy.js";
|
||||
import type { PluginMetadataSnapshot } from "./plugin-metadata-snapshot.js";
|
||||
import type { PluginMetadataSnapshot } from "./plugin-metadata-snapshot.types.js";
|
||||
|
||||
let currentPluginMetadataSnapshot: PluginMetadataSnapshot | undefined;
|
||||
let currentPluginMetadataSnapshotConfigFingerprint: string | undefined;
|
||||
|
||||
@@ -1,62 +1,29 @@
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { resolveInstalledPluginIndexPolicyHash } from "./installed-plugin-index-policy.js";
|
||||
import type { InstalledPluginIndex } from "./installed-plugin-index.js";
|
||||
import {
|
||||
loadPluginManifestRegistryForInstalledIndex,
|
||||
resolveInstalledManifestRegistryIndexFingerprint,
|
||||
} from "./manifest-registry-installed.js";
|
||||
import type { PluginManifestRecord, PluginManifestRegistry } from "./manifest-registry.js";
|
||||
import type { PluginDiagnostic } from "./manifest-types.js";
|
||||
import { createPluginRegistryIdNormalizer } from "./plugin-registry-contributions.js";
|
||||
import {
|
||||
loadPluginRegistrySnapshotWithMetadata,
|
||||
type PluginRegistrySnapshot,
|
||||
type PluginRegistrySnapshotDiagnostic,
|
||||
} from "./plugin-registry-snapshot.js";
|
||||
|
||||
export type PluginMetadataSnapshotOwnerMaps = {
|
||||
channels: ReadonlyMap<string, readonly string[]>;
|
||||
channelConfigs: ReadonlyMap<string, readonly string[]>;
|
||||
providers: ReadonlyMap<string, readonly string[]>;
|
||||
modelCatalogProviders: ReadonlyMap<string, readonly string[]>;
|
||||
cliBackends: ReadonlyMap<string, readonly string[]>;
|
||||
setupProviders: ReadonlyMap<string, readonly string[]>;
|
||||
commandAliases: ReadonlyMap<string, readonly string[]>;
|
||||
contracts: ReadonlyMap<string, readonly string[]>;
|
||||
};
|
||||
|
||||
export type PluginMetadataSnapshotMetrics = {
|
||||
registrySnapshotMs: number;
|
||||
manifestRegistryMs: number;
|
||||
ownerMapsMs: number;
|
||||
totalMs: number;
|
||||
indexPluginCount: number;
|
||||
manifestPluginCount: number;
|
||||
};
|
||||
|
||||
export type PluginMetadataSnapshot = {
|
||||
policyHash: string;
|
||||
workspaceDir?: string;
|
||||
index: PluginRegistrySnapshot;
|
||||
registryDiagnostics: readonly PluginRegistrySnapshotDiagnostic[];
|
||||
manifestRegistry: PluginManifestRegistry;
|
||||
plugins: readonly PluginManifestRecord[];
|
||||
diagnostics: readonly PluginDiagnostic[];
|
||||
byPluginId: ReadonlyMap<string, PluginManifestRecord>;
|
||||
normalizePluginId: (pluginId: string) => string;
|
||||
owners: PluginMetadataSnapshotOwnerMaps;
|
||||
metrics: PluginMetadataSnapshotMetrics;
|
||||
};
|
||||
|
||||
export type LoadPluginMetadataSnapshotParams = {
|
||||
config: OpenClawConfig;
|
||||
workspaceDir?: string;
|
||||
env: NodeJS.ProcessEnv;
|
||||
index?: PluginRegistrySnapshot;
|
||||
};
|
||||
import type { PluginManifestRecord } from "./manifest-registry.js";
|
||||
import type {
|
||||
LoadPluginMetadataSnapshotParams,
|
||||
PluginMetadataSnapshot,
|
||||
PluginMetadataSnapshotOwnerMaps,
|
||||
} from "./plugin-metadata-snapshot.types.js";
|
||||
import { createPluginRegistryIdNormalizer } from "./plugin-registry-id-normalizer.js";
|
||||
import { loadPluginRegistrySnapshotWithMetadata } from "./plugin-registry-snapshot.js";
|
||||
export type {
|
||||
LoadPluginMetadataSnapshotParams,
|
||||
PluginMetadataSnapshot,
|
||||
PluginMetadataSnapshotMetrics,
|
||||
PluginMetadataSnapshotOwnerMaps,
|
||||
PluginMetadataSnapshotRegistryDiagnostic,
|
||||
} from "./plugin-metadata-snapshot.types.js";
|
||||
|
||||
function indexesMatch(
|
||||
left: PluginRegistrySnapshot | undefined,
|
||||
right: PluginRegistrySnapshot | undefined,
|
||||
left: InstalledPluginIndex | undefined,
|
||||
right: InstalledPluginIndex | undefined,
|
||||
): boolean {
|
||||
if (!left || !right) {
|
||||
return true;
|
||||
@@ -71,7 +38,7 @@ export function isPluginMetadataSnapshotCompatible(params: {
|
||||
snapshot: Pick<PluginMetadataSnapshot, "index" | "policyHash" | "workspaceDir">;
|
||||
config: OpenClawConfig;
|
||||
workspaceDir?: string;
|
||||
index?: PluginRegistrySnapshot;
|
||||
index?: InstalledPluginIndex;
|
||||
}): boolean {
|
||||
return (
|
||||
params.snapshot.policyHash === resolveInstalledPluginIndexPolicyHash(params.config) &&
|
||||
|
||||
55
src/plugins/plugin-metadata-snapshot.types.ts
Normal file
55
src/plugins/plugin-metadata-snapshot.types.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import type { InstalledPluginIndex } from "./installed-plugin-index.js";
|
||||
import type { PluginManifestRecord, PluginManifestRegistry } from "./manifest-registry.js";
|
||||
import type { PluginDiagnostic } from "./manifest-types.js";
|
||||
|
||||
export type PluginMetadataSnapshotOwnerMaps = {
|
||||
channels: ReadonlyMap<string, readonly string[]>;
|
||||
channelConfigs: ReadonlyMap<string, readonly string[]>;
|
||||
providers: ReadonlyMap<string, readonly string[]>;
|
||||
modelCatalogProviders: ReadonlyMap<string, readonly string[]>;
|
||||
cliBackends: ReadonlyMap<string, readonly string[]>;
|
||||
setupProviders: ReadonlyMap<string, readonly string[]>;
|
||||
commandAliases: ReadonlyMap<string, readonly string[]>;
|
||||
contracts: ReadonlyMap<string, readonly string[]>;
|
||||
};
|
||||
|
||||
export type PluginMetadataSnapshotMetrics = {
|
||||
registrySnapshotMs: number;
|
||||
manifestRegistryMs: number;
|
||||
ownerMapsMs: number;
|
||||
totalMs: number;
|
||||
indexPluginCount: number;
|
||||
manifestPluginCount: number;
|
||||
};
|
||||
|
||||
export type PluginMetadataSnapshotRegistryDiagnostic = {
|
||||
level: "info" | "warn";
|
||||
code:
|
||||
| "persisted-registry-disabled"
|
||||
| "persisted-registry-missing"
|
||||
| "persisted-registry-stale-policy"
|
||||
| "persisted-registry-stale-source";
|
||||
message: string;
|
||||
};
|
||||
|
||||
export type PluginMetadataSnapshot = {
|
||||
policyHash: string;
|
||||
workspaceDir?: string;
|
||||
index: InstalledPluginIndex;
|
||||
registryDiagnostics: readonly PluginMetadataSnapshotRegistryDiagnostic[];
|
||||
manifestRegistry: PluginManifestRegistry;
|
||||
plugins: readonly PluginManifestRecord[];
|
||||
diagnostics: readonly PluginDiagnostic[];
|
||||
byPluginId: ReadonlyMap<string, PluginManifestRecord>;
|
||||
normalizePluginId: (pluginId: string) => string;
|
||||
owners: PluginMetadataSnapshotOwnerMaps;
|
||||
metrics: PluginMetadataSnapshotMetrics;
|
||||
};
|
||||
|
||||
export type LoadPluginMetadataSnapshotParams = {
|
||||
config: OpenClawConfig;
|
||||
workspaceDir?: string;
|
||||
env: NodeJS.ProcessEnv;
|
||||
index?: InstalledPluginIndex;
|
||||
};
|
||||
@@ -13,11 +13,19 @@ import type {
|
||||
PluginManifestRegistry,
|
||||
} from "./manifest-registry.js";
|
||||
import type { PluginOrigin } from "./plugin-origin.types.js";
|
||||
import {
|
||||
createPluginRegistryIdNormalizer,
|
||||
type PluginRegistryIdNormalizerOptions,
|
||||
} from "./plugin-registry-id-normalizer.js";
|
||||
import {
|
||||
loadPluginRegistrySnapshot,
|
||||
type LoadPluginRegistryParams,
|
||||
type PluginRegistrySnapshot,
|
||||
} from "./plugin-registry-snapshot.js";
|
||||
export {
|
||||
createPluginRegistryIdNormalizer,
|
||||
type PluginRegistryIdNormalizerOptions,
|
||||
} from "./plugin-registry-id-normalizer.js";
|
||||
|
||||
export type PluginLookUpTable = {
|
||||
index: PluginRegistrySnapshot;
|
||||
@@ -101,23 +109,10 @@ export type ResolveManifestContractPluginIdsByCompatibilityRuntimePathParams =
|
||||
origin?: PluginOrigin;
|
||||
};
|
||||
|
||||
export type PluginRegistryIdNormalizerOptions = {
|
||||
manifestRegistry?: PluginManifestRegistry;
|
||||
lookUpTable?: Pick<PluginLookUpTable, "manifestRegistry">;
|
||||
};
|
||||
|
||||
function normalizeContributionId(value: string): string {
|
||||
return value.trim();
|
||||
}
|
||||
|
||||
function normalizePluginRegistryAlias(value: string): string {
|
||||
return value.trim();
|
||||
}
|
||||
|
||||
function normalizePluginRegistryAliasKey(value: string): string {
|
||||
return normalizePluginRegistryAlias(value).toLowerCase();
|
||||
}
|
||||
|
||||
function sortUnique(values: Iterable<string>): string[] {
|
||||
return [...new Set([...values].map((value) => value.trim()).filter(Boolean))].toSorted(
|
||||
(left, right) => left.localeCompare(right),
|
||||
@@ -298,54 +293,6 @@ export function loadPluginManifestRegistryForPluginRegistry(
|
||||
});
|
||||
}
|
||||
|
||||
export function createPluginRegistryIdNormalizer(
|
||||
index: PluginRegistrySnapshot,
|
||||
options: PluginRegistryIdNormalizerOptions = {},
|
||||
): (pluginId: string) => string {
|
||||
const aliases = new Map<string, string>();
|
||||
for (const plugin of index.plugins) {
|
||||
const pluginId = normalizePluginRegistryAlias(plugin.pluginId);
|
||||
if (pluginId) {
|
||||
aliases.set(normalizePluginRegistryAliasKey(pluginId), plugin.pluginId);
|
||||
}
|
||||
}
|
||||
const registry =
|
||||
options.lookUpTable?.manifestRegistry ??
|
||||
options.manifestRegistry ??
|
||||
loadPluginManifestRegistryForInstalledIndex({
|
||||
index,
|
||||
includeDisabled: true,
|
||||
});
|
||||
for (const plugin of [...registry.plugins].toSorted((left, right) =>
|
||||
left.id.localeCompare(right.id),
|
||||
)) {
|
||||
const pluginId = normalizePluginRegistryAlias(plugin.id);
|
||||
if (!pluginId) {
|
||||
continue;
|
||||
}
|
||||
aliases.set(normalizePluginRegistryAliasKey(pluginId), plugin.id);
|
||||
for (const alias of [
|
||||
plugin.id,
|
||||
...listManifestContributionIds(plugin, "providers"),
|
||||
...listManifestContributionIds(plugin, "channels"),
|
||||
...listManifestContributionIds(plugin, "setupProviders"),
|
||||
...listManifestContributionIds(plugin, "cliBackends"),
|
||||
...listManifestContributionIds(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;
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizePluginsConfigWithRegistry(
|
||||
config: OpenClawConfig["plugins"] | undefined,
|
||||
index: PluginRegistrySnapshot,
|
||||
|
||||
74
src/plugins/plugin-registry-id-normalizer.ts
Normal file
74
src/plugins/plugin-registry-id-normalizer.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import type { InstalledPluginIndex } from "./installed-plugin-index.js";
|
||||
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
|
||||
import type { PluginManifestRecord, PluginManifestRegistry } from "./manifest-registry.js";
|
||||
|
||||
export type PluginRegistryIdNormalizerOptions = {
|
||||
manifestRegistry?: PluginManifestRegistry;
|
||||
lookUpTable?: Pick<{ manifestRegistry: PluginManifestRegistry }, "manifestRegistry">;
|
||||
};
|
||||
|
||||
function normalizePluginRegistryAlias(value: string): string {
|
||||
return value.trim();
|
||||
}
|
||||
|
||||
function normalizePluginRegistryAliasKey(value: string): string {
|
||||
return normalizePluginRegistryAlias(value).toLowerCase();
|
||||
}
|
||||
|
||||
function collectObjectKeys(value: Record<string, unknown> | undefined): readonly string[] {
|
||||
return value ? Object.keys(value) : [];
|
||||
}
|
||||
|
||||
function listPluginRegistryNormalizerAliases(plugin: PluginManifestRecord): readonly string[] {
|
||||
return [
|
||||
plugin.id,
|
||||
...plugin.providers,
|
||||
...plugin.channels,
|
||||
...(plugin.setup?.providers?.map((provider) => provider.id) ?? []),
|
||||
...plugin.cliBackends,
|
||||
...(plugin.setup?.cliBackends ?? []),
|
||||
...collectObjectKeys(plugin.modelCatalog?.providers),
|
||||
...collectObjectKeys(plugin.modelCatalog?.aliases),
|
||||
...(plugin.legacyPluginIds ?? []),
|
||||
];
|
||||
}
|
||||
|
||||
export function createPluginRegistryIdNormalizer(
|
||||
index: InstalledPluginIndex,
|
||||
options: PluginRegistryIdNormalizerOptions = {},
|
||||
): (pluginId: string) => string {
|
||||
const aliases = new Map<string, string>();
|
||||
for (const plugin of index.plugins) {
|
||||
const pluginId = normalizePluginRegistryAlias(plugin.pluginId);
|
||||
if (pluginId) {
|
||||
aliases.set(normalizePluginRegistryAliasKey(pluginId), plugin.pluginId);
|
||||
}
|
||||
}
|
||||
const registry =
|
||||
options.lookUpTable?.manifestRegistry ??
|
||||
options.manifestRegistry ??
|
||||
loadPluginManifestRegistryForInstalledIndex({
|
||||
index,
|
||||
includeDisabled: true,
|
||||
});
|
||||
for (const plugin of [...registry.plugins].toSorted((left, right) =>
|
||||
left.id.localeCompare(right.id),
|
||||
)) {
|
||||
const pluginId = normalizePluginRegistryAlias(plugin.id);
|
||||
if (!pluginId) {
|
||||
continue;
|
||||
}
|
||||
aliases.set(normalizePluginRegistryAliasKey(pluginId), plugin.id);
|
||||
for (const alias of listPluginRegistryNormalizerAliases(plugin)) {
|
||||
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;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user