mirror of
https://fastgit.cc/github.com/openclaw/openclaw
synced 2026-05-01 06:36:23 +08:00
perf(config): narrow channel legacy rule loading
This commit is contained in:
73
src/channels/plugins/doctor-contract-api.fast-path.test.ts
Normal file
73
src/channels/plugins/doctor-contract-api.fast-path.test.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
const { loadBundledPluginPublicArtifactModuleSyncMock } = vi.hoisted(() => ({
|
||||
loadBundledPluginPublicArtifactModuleSyncMock: vi.fn(
|
||||
({ artifactBasename, dirName }: { artifactBasename: string; dirName: string }) => {
|
||||
if (dirName === "discord" && artifactBasename === "doctor-contract-api.js") {
|
||||
return {
|
||||
legacyConfigRules: [
|
||||
{
|
||||
path: ["channels", "discord", "voice", "tts"],
|
||||
message: "legacy discord rule",
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
if (dirName === "telegram" && artifactBasename === "contract-api.js") {
|
||||
return {
|
||||
legacyConfigRules: [
|
||||
{
|
||||
path: ["channels", "telegram", "groupMentionsOnly"],
|
||||
message: "legacy telegram rule",
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
throw new Error(
|
||||
`Unable to resolve bundled plugin public surface ${dirName}/${artifactBasename}`,
|
||||
);
|
||||
},
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("../../plugins/public-surface-loader.js", () => ({
|
||||
loadBundledPluginPublicArtifactModuleSync: loadBundledPluginPublicArtifactModuleSyncMock,
|
||||
}));
|
||||
|
||||
import { loadBundledChannelDoctorContractApi } from "./doctor-contract-api.js";
|
||||
|
||||
describe("channel doctor contract api fast path", () => {
|
||||
it("prefers the explicit doctor contract artifact for bundled channels", () => {
|
||||
const api = loadBundledChannelDoctorContractApi("discord");
|
||||
|
||||
expect(api?.legacyConfigRules).toEqual([
|
||||
{
|
||||
path: ["channels", "discord", "voice", "tts"],
|
||||
message: "legacy discord rule",
|
||||
},
|
||||
]);
|
||||
expect(loadBundledPluginPublicArtifactModuleSyncMock).toHaveBeenCalledWith({
|
||||
dirName: "discord",
|
||||
artifactBasename: "doctor-contract-api.js",
|
||||
});
|
||||
});
|
||||
|
||||
it("falls back to the generic contract artifact when the doctor artifact is absent", () => {
|
||||
const api = loadBundledChannelDoctorContractApi("telegram");
|
||||
|
||||
expect(api?.legacyConfigRules).toEqual([
|
||||
{
|
||||
path: ["channels", "telegram", "groupMentionsOnly"],
|
||||
message: "legacy telegram rule",
|
||||
},
|
||||
]);
|
||||
expect(loadBundledPluginPublicArtifactModuleSyncMock).toHaveBeenCalledWith({
|
||||
dirName: "telegram",
|
||||
artifactBasename: "doctor-contract-api.js",
|
||||
});
|
||||
expect(loadBundledPluginPublicArtifactModuleSyncMock).toHaveBeenCalledWith({
|
||||
dirName: "telegram",
|
||||
artifactBasename: "contract-api.js",
|
||||
});
|
||||
});
|
||||
});
|
||||
34
src/channels/plugins/doctor-contract-api.ts
Normal file
34
src/channels/plugins/doctor-contract-api.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import type { LegacyConfigRule } from "../../config/legacy.shared.js";
|
||||
import { loadBundledPluginPublicArtifactModuleSync } from "../../plugins/public-surface-loader.js";
|
||||
|
||||
type BundledChannelDoctorContractApi = {
|
||||
legacyConfigRules?: readonly LegacyConfigRule[];
|
||||
};
|
||||
|
||||
function loadBundledChannelPublicArtifact(
|
||||
channelId: string,
|
||||
artifactBasenames: readonly string[],
|
||||
): BundledChannelDoctorContractApi | undefined {
|
||||
for (const artifactBasename of artifactBasenames) {
|
||||
try {
|
||||
return loadBundledPluginPublicArtifactModuleSync<BundledChannelDoctorContractApi>({
|
||||
dirName: channelId,
|
||||
artifactBasename,
|
||||
});
|
||||
} catch (error) {
|
||||
if (
|
||||
error instanceof Error &&
|
||||
error.message.startsWith("Unable to resolve bundled plugin public surface ")
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function loadBundledChannelDoctorContractApi(
|
||||
channelId: string,
|
||||
): BundledChannelDoctorContractApi | undefined {
|
||||
return loadBundledChannelPublicArtifact(channelId, ["doctor-contract-api.js", "contract-api.js"]);
|
||||
}
|
||||
108
src/channels/plugins/legacy-config.test.ts
Normal file
108
src/channels/plugins/legacy-config.test.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const {
|
||||
loadBundledChannelDoctorContractApiMock,
|
||||
getBootstrapChannelPluginMock,
|
||||
listPluginDoctorLegacyConfigRulesMock,
|
||||
} = vi.hoisted(() => ({
|
||||
loadBundledChannelDoctorContractApiMock: vi.fn(),
|
||||
getBootstrapChannelPluginMock: vi.fn(),
|
||||
listPluginDoctorLegacyConfigRulesMock: vi.fn(() => []),
|
||||
}));
|
||||
|
||||
vi.mock("./doctor-contract-api.js", () => ({
|
||||
loadBundledChannelDoctorContractApi: loadBundledChannelDoctorContractApiMock,
|
||||
}));
|
||||
|
||||
vi.mock("./bootstrap-registry.js", () => ({
|
||||
getBootstrapChannelPlugin: getBootstrapChannelPluginMock,
|
||||
}));
|
||||
|
||||
vi.mock("../../plugins/doctor-contract-registry.js", () => ({
|
||||
listPluginDoctorLegacyConfigRules: listPluginDoctorLegacyConfigRulesMock,
|
||||
}));
|
||||
|
||||
import { collectChannelLegacyConfigRules } from "./legacy-config.js";
|
||||
|
||||
describe("collectChannelLegacyConfigRules", () => {
|
||||
beforeEach(() => {
|
||||
loadBundledChannelDoctorContractApiMock.mockReset();
|
||||
getBootstrapChannelPluginMock.mockReset();
|
||||
listPluginDoctorLegacyConfigRulesMock.mockReset();
|
||||
listPluginDoctorLegacyConfigRulesMock.mockReturnValue([]);
|
||||
});
|
||||
|
||||
it("uses bundled doctor contract rules before falling back to registry scans", () => {
|
||||
loadBundledChannelDoctorContractApiMock.mockImplementation((channelId: string) =>
|
||||
channelId === "discord"
|
||||
? {
|
||||
legacyConfigRules: [
|
||||
{
|
||||
path: ["channels", "discord", "voice", "tts"],
|
||||
message: "legacy discord rule",
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
|
||||
const rules = collectChannelLegacyConfigRules({
|
||||
channels: {
|
||||
discord: {},
|
||||
},
|
||||
});
|
||||
|
||||
expect(rules).toEqual([
|
||||
{
|
||||
path: ["channels", "discord", "voice", "tts"],
|
||||
message: "legacy discord rule",
|
||||
},
|
||||
]);
|
||||
expect(getBootstrapChannelPluginMock).not.toHaveBeenCalled();
|
||||
expect(listPluginDoctorLegacyConfigRulesMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("falls back to bootstrap rules and only scans unresolved channels", () => {
|
||||
getBootstrapChannelPluginMock.mockImplementation((channelId: string) =>
|
||||
channelId === "slack"
|
||||
? {
|
||||
doctor: {
|
||||
legacyConfigRules: [
|
||||
{
|
||||
path: ["channels", "slack", "legacy"],
|
||||
message: "legacy slack rule",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
listPluginDoctorLegacyConfigRulesMock.mockReturnValue([
|
||||
{
|
||||
path: ["channels", "custom-chat", "legacy"],
|
||||
message: "legacy custom rule",
|
||||
},
|
||||
]);
|
||||
|
||||
const rules = collectChannelLegacyConfigRules({
|
||||
channels: {
|
||||
slack: {},
|
||||
"custom-chat": {},
|
||||
},
|
||||
});
|
||||
|
||||
expect(rules).toEqual([
|
||||
{
|
||||
path: ["channels", "slack", "legacy"],
|
||||
message: "legacy slack rule",
|
||||
},
|
||||
{
|
||||
path: ["channels", "custom-chat", "legacy"],
|
||||
message: "legacy custom rule",
|
||||
},
|
||||
]);
|
||||
expect(listPluginDoctorLegacyConfigRulesMock).toHaveBeenCalledWith({
|
||||
pluginIds: ["custom-chat"],
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { LegacyConfigRule } from "../../config/legacy.shared.js";
|
||||
import { listPluginDoctorLegacyConfigRules } from "../../plugins/doctor-contract-registry.js";
|
||||
import { getBootstrapChannelPlugin } from "./bootstrap-registry.js";
|
||||
import { loadBundledChannelDoctorContractApi } from "./doctor-contract-api.js";
|
||||
import type { ChannelId } from "./types.public.js";
|
||||
|
||||
function collectConfiguredChannelIds(raw: unknown): ChannelId[] {
|
||||
@@ -19,14 +20,25 @@ function collectConfiguredChannelIds(raw: unknown): ChannelId[] {
|
||||
export function collectChannelLegacyConfigRules(raw?: unknown): LegacyConfigRule[] {
|
||||
const channelIds = collectConfiguredChannelIds(raw);
|
||||
const rules: LegacyConfigRule[] = [];
|
||||
const unresolvedChannelIds: ChannelId[] = [];
|
||||
for (const channelId of channelIds) {
|
||||
const plugin = getBootstrapChannelPlugin(channelId);
|
||||
if (!plugin) {
|
||||
const contractRules = loadBundledChannelDoctorContractApi(channelId)?.legacyConfigRules;
|
||||
if (Array.isArray(contractRules) && contractRules.length > 0) {
|
||||
rules.push(...contractRules);
|
||||
continue;
|
||||
}
|
||||
rules.push(...(plugin.doctor?.legacyConfigRules ?? []));
|
||||
|
||||
const plugin = getBootstrapChannelPlugin(channelId);
|
||||
if (plugin?.doctor?.legacyConfigRules?.length) {
|
||||
rules.push(...plugin.doctor.legacyConfigRules);
|
||||
continue;
|
||||
}
|
||||
|
||||
unresolvedChannelIds.push(channelId);
|
||||
}
|
||||
if (unresolvedChannelIds.length > 0) {
|
||||
rules.push(...listPluginDoctorLegacyConfigRules({ pluginIds: unresolvedChannelIds }));
|
||||
}
|
||||
rules.push(...listPluginDoctorLegacyConfigRules({ pluginIds: channelIds }));
|
||||
|
||||
const seen = new Set<string>();
|
||||
return rules.filter((rule) => {
|
||||
|
||||
@@ -573,11 +573,10 @@ export function validateConfigObjectRaw(
|
||||
raw: unknown,
|
||||
): { ok: true; config: OpenClawConfig } | { ok: false; issues: ConfigValidationIssue[] } {
|
||||
const policyIssues = collectUnsupportedSecretRefPolicyIssues(raw);
|
||||
const legacyIssues = findLegacyConfigIssues(
|
||||
raw,
|
||||
raw,
|
||||
listPluginDoctorLegacyConfigRules({ pluginIds: collectRelevantDoctorPluginIds(raw) }),
|
||||
);
|
||||
const extraLegacyRules = listPluginDoctorLegacyConfigRules({
|
||||
pluginIds: collectRelevantDoctorPluginIds(raw),
|
||||
});
|
||||
const legacyIssues = findLegacyConfigIssues(raw, raw, extraLegacyRules);
|
||||
if (legacyIssues.length > 0) {
|
||||
return {
|
||||
ok: false,
|
||||
|
||||
Reference in New Issue
Block a user