
























@@ -266,6 +266,104 @@ describe("config plugin validation", () => {
266266}
267267});
268268269+it("warns instead of failing for stale channel config backed by missing plugin refs", async () => {
270+const res = validateInSuite({
271+agents: { list: [{ id: "pi" }] },
272+channels: {
273+"missing-chat": { token: "stale" },
274+},
275+plugins: {
276+allow: ["missing-chat"],
277+entries: { "missing-chat": { enabled: true } },
278+},
279+});
280+281+expect(res.ok).toBe(true);
282+if (!res.ok) {
283+return;
284+}
285+expect(res.warnings).toContainEqual({
286+path: "channels.missing-chat",
287+message:
288+"unknown channel id: missing-chat (stale channel plugin config ignored; run openclaw doctor --fix to remove stale config, or install the plugin)",
289+});
290+expect(res.warnings).toContainEqual({
291+path: "plugins.allow",
292+message:
293+"plugin not found: missing-chat (stale config entry ignored; remove it from plugins config)",
294+});
295+expect(res.warnings).toContainEqual({
296+path: "plugins.entries.missing-chat",
297+message:
298+"plugin not found: missing-chat (stale config entry ignored; remove it from plugins config)",
299+});
300+});
301+302+it("keeps unknown channel typos fatal when there is no stale plugin evidence", async () => {
303+const res = validateInSuite({
304+agents: { list: [{ id: "pi" }] },
305+channels: {
306+telegarm: { botToken: "typo" },
307+},
308+plugins: {
309+allow: ["telegram"],
310+},
311+});
312+313+expect(res.ok).toBe(false);
314+if (res.ok) {
315+return;
316+}
317+expect(res.issues).toContainEqual({
318+path: "channels.telegarm",
319+message: "unknown channel id: telegarm",
320+});
321+expect(res.warnings).not.toContainEqual(expect.objectContaining({ path: "channels.telegarm" }));
322+});
323+324+it("uses persisted installed-plugin records as stale channel evidence", async () => {
325+const installedPluginIndexPath = path.join(suiteHome, ".openclaw", "plugins", "installs.json");
326+await mkdirSafe(path.dirname(installedPluginIndexPath));
327+await fs.writeFile(
328+installedPluginIndexPath,
329+JSON.stringify(
330+{
331+installRecords: {
332+"missing-sms": {
333+source: "npm",
334+spec: "missing-sms@1.0.0",
335+installedAt: "2026-04-12T00:00:00.000Z",
336+},
337+},
338+plugins: [],
339+},
340+null,
341+2,
342+),
343+"utf-8",
344+);
345+try {
346+const res = validateInSuite({
347+agents: { list: [{ id: "pi" }] },
348+channels: {
349+"missing-sms": { token: "stale" },
350+},
351+});
352+353+expect(res.ok).toBe(true);
354+if (!res.ok) {
355+return;
356+}
357+expect(res.warnings).toContainEqual({
358+path: "channels.missing-sms",
359+message:
360+"unknown channel id: missing-sms (stale channel plugin config ignored; run openclaw doctor --fix to remove stale config, or install the plugin)",
361+});
362+} finally {
363+await fs.rm(installedPluginIndexPath, { force: true });
364+}
365+});
366+269367it("warns with actionable guidance when a runtime command name is used in plugins.allow", async () => {
270368const res = validateInSuite({
271369agents: { list: [{ id: "pi" }] },
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。