mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-14 08:00:28 +00:00
fix(plugins): guard config contract metadata
This commit is contained in:
@@ -265,6 +265,97 @@ describe("resolvePluginConfigContractsById", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps healthy config contract records after unreadable registry plugin metadata", () => {
|
||||
const unreadableRecord = {
|
||||
get id() {
|
||||
throw new Error("config contract plugin id getter exploded");
|
||||
},
|
||||
origin: "config",
|
||||
configContracts: {
|
||||
compatibilityMigrationPaths: ["plugins.entries.broken.config"],
|
||||
},
|
||||
} as never;
|
||||
mocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue(
|
||||
createRegistry([
|
||||
unreadableRecord,
|
||||
createPluginRecord({
|
||||
id: "healthy",
|
||||
origin: "config",
|
||||
configContracts: {
|
||||
compatibilityMigrationPaths: ["plugins.entries.healthy.config"],
|
||||
},
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
expect(
|
||||
resolvePluginConfigContractsById({
|
||||
pluginIds: ["healthy"],
|
||||
fallbackToBundledMetadata: false,
|
||||
}),
|
||||
).toEqual(
|
||||
new Map([
|
||||
[
|
||||
"healthy",
|
||||
{
|
||||
origin: "config",
|
||||
configContracts: {
|
||||
compatibilityMigrationPaths: ["plugins.entries.healthy.config"],
|
||||
},
|
||||
},
|
||||
],
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps bundled config contract fallback after unreadable bundled plugin metadata", () => {
|
||||
const unreadableRecord = {
|
||||
get id() {
|
||||
throw new Error("bundled config contract plugin id getter exploded");
|
||||
},
|
||||
origin: "bundled",
|
||||
configContracts: {
|
||||
secretInputs: {
|
||||
paths: [{ path: "broken.secret", expected: "string" }],
|
||||
},
|
||||
},
|
||||
} as never;
|
||||
mocks.loadBundledManifestRegistry.mockReturnValue(
|
||||
createRegistry([
|
||||
unreadableRecord,
|
||||
createPluginRecord({
|
||||
id: "healthy-bundled",
|
||||
origin: "bundled",
|
||||
configContracts: {
|
||||
secretInputs: {
|
||||
paths: [{ path: "healthy.secret", expected: "string" }],
|
||||
},
|
||||
},
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
expect(
|
||||
resolvePluginConfigContractsById({
|
||||
pluginIds: ["healthy-bundled"],
|
||||
}),
|
||||
).toEqual(
|
||||
new Map([
|
||||
[
|
||||
"healthy-bundled",
|
||||
{
|
||||
origin: "bundled",
|
||||
configContracts: {
|
||||
secretInputs: {
|
||||
paths: [{ path: "healthy.secret", expected: "string" }],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it("can skip bundled metadata fallback for registry-scoped callers", () => {
|
||||
expect(
|
||||
resolvePluginConfigContractsById({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { normalizeSortedUniqueStringEntries } from "@openclaw/normalization-core/string-normalization";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { discoverOpenClawPlugins, type PluginDiscoveryResult } from "./discovery.js";
|
||||
import { loadPluginManifestRegistry } from "./manifest-registry.js";
|
||||
import { loadPluginManifestRegistry, type PluginManifestRegistry } from "./manifest-registry.js";
|
||||
import type { PluginManifestConfigContracts } from "./manifest.js";
|
||||
import type { PluginOrigin } from "./plugin-origin.types.js";
|
||||
import { loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry.js";
|
||||
@@ -15,6 +15,30 @@ export type PluginConfigContractMetadata = {
|
||||
configContracts: PluginManifestConfigContracts;
|
||||
};
|
||||
|
||||
type PluginConfigContractRecordRead =
|
||||
| {
|
||||
ok: true;
|
||||
id: string;
|
||||
origin: PluginOrigin;
|
||||
configContracts?: PluginManifestConfigContracts;
|
||||
}
|
||||
| { ok: false };
|
||||
|
||||
function readPluginConfigContractRecord(
|
||||
plugin: PluginManifestRegistry["plugins"][number],
|
||||
): PluginConfigContractRecordRead {
|
||||
try {
|
||||
return {
|
||||
ok: true,
|
||||
id: plugin.id,
|
||||
origin: plugin.origin,
|
||||
configContracts: plugin.configContracts,
|
||||
};
|
||||
} catch {
|
||||
return { ok: false };
|
||||
}
|
||||
}
|
||||
|
||||
export function resolvePluginConfigContractsById(params: {
|
||||
config?: OpenClawConfig;
|
||||
workspaceDir?: string;
|
||||
@@ -54,7 +78,11 @@ export function resolvePluginConfigContractsById(params: {
|
||||
diagnostics: discovery.diagnostics,
|
||||
});
|
||||
for (const plugin of registry.plugins) {
|
||||
bundledContractFallbacks.set(plugin.id, plugin.configContracts);
|
||||
const record = readPluginConfigContractRecord(plugin);
|
||||
if (!record.ok) {
|
||||
continue;
|
||||
}
|
||||
bundledContractFallbacks.set(record.id, record.configContracts);
|
||||
}
|
||||
if (!bundledContractFallbacks.has(pluginId)) {
|
||||
bundledContractFallbacks.set(pluginId, undefined);
|
||||
@@ -70,16 +98,17 @@ export function resolvePluginConfigContractsById(params: {
|
||||
includeDisabled: true,
|
||||
});
|
||||
for (const plugin of registry.plugins) {
|
||||
if (!pluginIds.includes(plugin.id)) {
|
||||
const record = readPluginConfigContractRecord(plugin);
|
||||
if (!record.ok || !pluginIds.includes(record.id)) {
|
||||
continue;
|
||||
}
|
||||
resolvedPluginOrigins.set(plugin.id, plugin.origin);
|
||||
if (!plugin.configContracts) {
|
||||
resolvedPluginOrigins.set(record.id, record.origin);
|
||||
if (!record.configContracts) {
|
||||
continue;
|
||||
}
|
||||
matches.set(plugin.id, {
|
||||
origin: plugin.origin,
|
||||
configContracts: plugin.configContracts,
|
||||
matches.set(record.id, {
|
||||
origin: record.origin,
|
||||
configContracts: record.configContracts,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user