mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:50:43 +00:00
fix(agents): resolve plugin skill metadata cold
This commit is contained in:
@@ -7,10 +7,12 @@ import { openBoundaryFileSync } from "../infra/boundary-file-read.js";
|
||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
import type { BundleMcpServerConfig } from "../plugins/bundle-mcp.js";
|
||||
import {
|
||||
normalizePluginsConfig,
|
||||
normalizePluginsConfigWithResolver,
|
||||
resolveEffectivePluginActivationState,
|
||||
} from "../plugins/config-state.js";
|
||||
import { loadPluginManifestRegistry } from "../plugins/manifest-registry.js";
|
||||
} from "../plugins/config-policy.js";
|
||||
import { loadPluginManifestRegistryForInstalledIndex } from "../plugins/manifest-registry-installed.js";
|
||||
import type { PluginManifestRegistry } from "../plugins/manifest-registry.js";
|
||||
import { loadPluginRegistrySnapshot } from "../plugins/plugin-registry.js";
|
||||
import { isRecord } from "../utils.js";
|
||||
import { loadEmbeddedPiMcpConfig } from "./embedded-pi-mcp.js";
|
||||
|
||||
@@ -68,6 +70,33 @@ function loadBundleSettingsFile(params: {
|
||||
}
|
||||
}
|
||||
|
||||
function buildRegistryPluginIdAliases(
|
||||
registry: PluginManifestRegistry,
|
||||
): Readonly<Record<string, string>> {
|
||||
return Object.fromEntries(
|
||||
registry.plugins
|
||||
.flatMap((record) => [
|
||||
...(record.providers ?? [])
|
||||
.filter((providerId) => providerId !== record.id)
|
||||
.map((providerId) => [providerId, record.id] as const),
|
||||
...(record.legacyPluginIds ?? []).map(
|
||||
(legacyPluginId) => [legacyPluginId, record.id] as const,
|
||||
),
|
||||
])
|
||||
.toSorted(([left], [right]) => left.localeCompare(right)),
|
||||
);
|
||||
}
|
||||
|
||||
function createRegistryPluginIdNormalizer(
|
||||
registry: PluginManifestRegistry,
|
||||
): (id: string) => string {
|
||||
const aliases = buildRegistryPluginIdAliases(registry);
|
||||
return (id: string) => {
|
||||
const trimmed = id.trim();
|
||||
return aliases[trimmed] ?? trimmed;
|
||||
};
|
||||
}
|
||||
|
||||
export function loadEnabledBundlePiSettingsSnapshot(params: {
|
||||
cwd: string;
|
||||
cfg?: OpenClawConfig;
|
||||
@@ -76,15 +105,24 @@ export function loadEnabledBundlePiSettingsSnapshot(params: {
|
||||
if (!workspaceDir) {
|
||||
return {};
|
||||
}
|
||||
const registry = loadPluginManifestRegistry({
|
||||
const index = loadPluginRegistrySnapshot({
|
||||
workspaceDir,
|
||||
config: params.cfg,
|
||||
});
|
||||
const registry = loadPluginManifestRegistryForInstalledIndex({
|
||||
index,
|
||||
workspaceDir,
|
||||
config: params.cfg,
|
||||
includeDisabled: true,
|
||||
});
|
||||
if (registry.plugins.length === 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const normalizedPlugins = normalizePluginsConfig(params.cfg?.plugins);
|
||||
const normalizedPlugins = normalizePluginsConfigWithResolver(
|
||||
params.cfg?.plugins,
|
||||
createRegistryPluginIdNormalizer(registry),
|
||||
);
|
||||
let snapshot: PiSettingsSnapshot = {};
|
||||
|
||||
for (const record of registry.plugins) {
|
||||
|
||||
@@ -13,11 +13,11 @@ vi.mock("../infra/boundary-file-read.js", async () => {
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../plugins/manifest-registry.js", async () => {
|
||||
vi.mock("../plugins/manifest-registry-installed.js", async () => {
|
||||
const fs = await import("node:fs");
|
||||
const path = await import("node:path");
|
||||
return {
|
||||
loadPluginManifestRegistry: (params: { workspaceDir?: string }) => {
|
||||
loadPluginManifestRegistryForInstalledIndex: (params: { workspaceDir?: string }) => {
|
||||
const rootDir = path.join(
|
||||
params.workspaceDir ?? "",
|
||||
".openclaw",
|
||||
@@ -45,6 +45,10 @@ vi.mock("../plugins/manifest-registry.js", async () => {
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../plugins/plugin-registry.js", () => ({
|
||||
loadPluginRegistrySnapshot: () => ({ plugins: [] }),
|
||||
}));
|
||||
|
||||
vi.mock("./embedded-pi-mcp.js", async () => {
|
||||
const fs = await import("node:fs");
|
||||
const path = await import("node:path");
|
||||
|
||||
@@ -6,11 +6,17 @@ import type { PluginManifestRegistry } from "../../plugins/manifest-registry.js"
|
||||
import { createTrackedTempDirs } from "../../test-utils/tracked-temp-dirs.js";
|
||||
|
||||
const hoisted = vi.hoisted(() => ({
|
||||
loadPluginManifestRegistry: vi.fn(),
|
||||
loadPluginManifestRegistryForInstalledIndex: vi.fn(),
|
||||
loadPluginRegistrySnapshot: vi.fn(() => ({ plugins: [] })),
|
||||
}));
|
||||
|
||||
vi.mock("../../plugins/manifest-registry.js", () => ({
|
||||
loadPluginManifestRegistry: (...args: unknown[]) => hoisted.loadPluginManifestRegistry(...args),
|
||||
vi.mock("../../plugins/manifest-registry-installed.js", () => ({
|
||||
loadPluginManifestRegistryForInstalledIndex: (...args: unknown[]) =>
|
||||
hoisted.loadPluginManifestRegistryForInstalledIndex(...args),
|
||||
}));
|
||||
|
||||
vi.mock("../../plugins/plugin-registry.js", () => ({
|
||||
loadPluginRegistrySnapshot: (...args: unknown[]) => hoisted.loadPluginRegistrySnapshot(...args),
|
||||
}));
|
||||
|
||||
let resolvePluginSkillDirs: typeof import("./plugin-skills.js").resolvePluginSkillDirs;
|
||||
@@ -85,7 +91,9 @@ async function setupAcpxAndHelperRegistry() {
|
||||
const helperRoot = await tempDirs.make("openclaw-helper-plugin-");
|
||||
await fs.mkdir(path.join(acpxRoot, "skills"), { recursive: true });
|
||||
await fs.mkdir(path.join(helperRoot, "skills"), { recursive: true });
|
||||
hoisted.loadPluginManifestRegistry.mockReturnValue(buildRegistry({ acpxRoot, helperRoot }));
|
||||
hoisted.loadPluginManifestRegistryForInstalledIndex.mockReturnValue(
|
||||
buildRegistry({ acpxRoot, helperRoot }),
|
||||
);
|
||||
return { workspaceDir, acpxRoot, helperRoot };
|
||||
}
|
||||
|
||||
@@ -98,7 +106,8 @@ async function setupPluginOutsideSkills() {
|
||||
}
|
||||
|
||||
afterEach(async () => {
|
||||
hoisted.loadPluginManifestRegistry.mockReset();
|
||||
hoisted.loadPluginManifestRegistryForInstalledIndex.mockReset();
|
||||
hoisted.loadPluginRegistrySnapshot.mockReset();
|
||||
await tempDirs.cleanup();
|
||||
});
|
||||
|
||||
@@ -108,7 +117,13 @@ describe("resolvePluginSkillDirs", () => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
hoisted.loadPluginManifestRegistry.mockReset();
|
||||
hoisted.loadPluginManifestRegistryForInstalledIndex.mockReset();
|
||||
hoisted.loadPluginManifestRegistryForInstalledIndex.mockReturnValue({
|
||||
diagnostics: [],
|
||||
plugins: [],
|
||||
});
|
||||
hoisted.loadPluginRegistrySnapshot.mockReset();
|
||||
hoisted.loadPluginRegistrySnapshot.mockReturnValue({ plugins: [] });
|
||||
});
|
||||
|
||||
it.each([
|
||||
@@ -152,7 +167,7 @@ describe("resolvePluginSkillDirs", () => {
|
||||
await fs.mkdir(outsideSkills, { recursive: true });
|
||||
const escapePath = path.relative(pluginRoot, outsideSkills);
|
||||
|
||||
hoisted.loadPluginManifestRegistry.mockReturnValue(
|
||||
hoisted.loadPluginManifestRegistryForInstalledIndex.mockReturnValue(
|
||||
createSinglePluginRegistry({
|
||||
pluginRoot,
|
||||
skills: ["./skills", escapePath],
|
||||
@@ -183,7 +198,7 @@ describe("resolvePluginSkillDirs", () => {
|
||||
process.platform === "win32" ? ("junction" as const) : ("dir" as const),
|
||||
);
|
||||
|
||||
hoisted.loadPluginManifestRegistry.mockReturnValue(
|
||||
hoisted.loadPluginManifestRegistryForInstalledIndex.mockReturnValue(
|
||||
createSinglePluginRegistry({
|
||||
pluginRoot,
|
||||
skills: ["./skills-link"],
|
||||
@@ -210,7 +225,7 @@ describe("resolvePluginSkillDirs", () => {
|
||||
await fs.mkdir(path.join(pluginRoot, "commands"), { recursive: true });
|
||||
await fs.mkdir(path.join(pluginRoot, "skills"), { recursive: true });
|
||||
|
||||
hoisted.loadPluginManifestRegistry.mockReturnValue(
|
||||
hoisted.loadPluginManifestRegistryForInstalledIndex.mockReturnValue(
|
||||
createSinglePluginRegistry({
|
||||
pluginRoot,
|
||||
format: "bundle",
|
||||
@@ -240,7 +255,7 @@ describe("resolvePluginSkillDirs", () => {
|
||||
const pluginRoot = await tempDirs.make("openclaw-legacy-plugin-");
|
||||
await fs.mkdir(path.join(pluginRoot, "skills"), { recursive: true });
|
||||
|
||||
hoisted.loadPluginManifestRegistry.mockReturnValue(
|
||||
hoisted.loadPluginManifestRegistryForInstalledIndex.mockReturnValue(
|
||||
createSinglePluginRegistry({
|
||||
pluginRoot,
|
||||
skills: ["./skills"],
|
||||
|
||||
@@ -7,10 +7,9 @@ import {
|
||||
resolveEffectivePluginActivationState,
|
||||
resolveMemorySlotDecision,
|
||||
} from "../../plugins/config-policy.js";
|
||||
import {
|
||||
loadPluginManifestRegistry,
|
||||
type PluginManifestRegistry,
|
||||
} from "../../plugins/manifest-registry.js";
|
||||
import { loadPluginManifestRegistryForInstalledIndex } from "../../plugins/manifest-registry-installed.js";
|
||||
import type { PluginManifestRegistry } from "../../plugins/manifest-registry.js";
|
||||
import { loadPluginRegistrySnapshot } from "../../plugins/plugin-registry.js";
|
||||
import { hasKind } from "../../plugins/slots.js";
|
||||
import { isPathInsideWithRealpath } from "../../security/scan-paths.js";
|
||||
|
||||
@@ -51,10 +50,16 @@ export function resolvePluginSkillDirs(params: {
|
||||
if (!workspaceDir) {
|
||||
return [];
|
||||
}
|
||||
const registry = loadPluginManifestRegistry({
|
||||
const index = loadPluginRegistrySnapshot({
|
||||
workspaceDir,
|
||||
config: params.config,
|
||||
});
|
||||
const registry = loadPluginManifestRegistryForInstalledIndex({
|
||||
index,
|
||||
workspaceDir,
|
||||
config: params.config,
|
||||
includeDisabled: true,
|
||||
});
|
||||
if (registry.plugins.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user