























@@ -138,6 +138,263 @@ describe("plugins cli list", () => {
138138expect(output).not.toContain("No plugin issues detected.");
139139});
140140141+it("reports missing configured Codex runtime plugin in doctor output", async () => {
142+const sourceConfig = {
143+agents: {
144+defaults: {
145+models: {
146+"openai/gpt-5.5": {
147+agentRuntime: { id: "codex" },
148+},
149+},
150+},
151+},
152+};
153+loadConfig.mockReturnValue(sourceConfig);
154+readConfigFileSnapshot.mockResolvedValueOnce({
155+path: "/tmp/openclaw-config.json5",
156+exists: true,
157+raw: "{}",
158+parsed: sourceConfig,
159+resolved: sourceConfig,
160+ sourceConfig,
161+runtimeConfig: sourceConfig,
162+config: sourceConfig,
163+valid: true,
164+hash: "mock",
165+issues: [],
166+warnings: [],
167+legacyIssues: [],
168+});
169+buildPluginDiagnosticsReport.mockReturnValue({
170+plugins: [],
171+diagnostics: [],
172+});
173+174+await runPluginsCommand(["plugins", "doctor"]);
175+176+const output = runtimeLogs.join("\n");
177+expect(output).toContain("Plugin configuration:");
178+expect(output).toContain('Configured runtime "codex" requires the Codex plugin');
179+expect(output).toContain("openclaw doctor --fix");
180+expect(output).toContain("openclaw plugins install @openclaw/codex");
181+expect(output).toContain(
182+"No plugin install-tree issues detected; configuration warnings remain.",
183+);
184+expect(output).not.toContain("No plugin issues detected.");
185+});
186+187+it("reports missing configured ACPX runtime plugin in doctor output", async () => {
188+const sourceConfig = {
189+acp: {
190+backend: "acpx",
191+},
192+};
193+loadConfig.mockReturnValue(sourceConfig);
194+buildPluginDiagnosticsReport.mockReturnValue({
195+plugins: [],
196+diagnostics: [],
197+});
198+199+await runPluginsCommand(["plugins", "doctor"]);
200+201+const output = runtimeLogs.join("\n");
202+expect(output).toContain("Plugin configuration:");
203+expect(output).toContain('Configured runtime "acpx" requires the ACPX Runtime plugin');
204+expect(output).toContain("openclaw doctor --fix");
205+expect(output).toContain("openclaw plugins install @openclaw/acpx");
206+expect(output).not.toContain("No plugin issues detected.");
207+});
208+209+it("reports blocked configured ACPX runtime with ACP-specific guidance", async () => {
210+const sourceConfig = {
211+acp: {
212+backend: "acpx",
213+},
214+plugins: {
215+entries: {
216+acpx: { enabled: false },
217+},
218+},
219+};
220+loadConfig.mockReturnValue(sourceConfig);
221+buildPluginDiagnosticsReport.mockReturnValue({
222+plugins: [],
223+diagnostics: [],
224+});
225+226+await runPluginsCommand(["plugins", "doctor"]);
227+228+const output = runtimeLogs.join("\n");
229+expect(output).toContain('Configured runtime "acpx" requires the ACPX Runtime plugin');
230+expect(output).toContain("Set plugins.entries.acpx.enabled=true");
231+expect(output).toContain("disable ACP/acpx in acp config");
232+expect(output).not.toContain('runtime policy to "pi"');
233+expect(output).not.toContain("openclaw plugins install @openclaw/acpx");
234+expect(output).not.toContain("No plugin issues detected.");
235+});
236+237+it("reports disabled configured ACPX runtime with ACP-specific guidance", async () => {
238+const sourceConfig = {
239+acp: {
240+backend: "acpx",
241+},
242+};
243+loadConfig.mockReturnValue(sourceConfig);
244+buildPluginDiagnosticsReport.mockReturnValue({
245+plugins: [createPluginRecord({ id: "acpx", enabled: false, status: "disabled" })],
246+diagnostics: [],
247+});
248+249+await runPluginsCommand(["plugins", "doctor"]);
250+251+const output = runtimeLogs.join("\n");
252+expect(output).toContain('Configured runtime "acpx" requires the ACPX Runtime plugin');
253+expect(output).toContain('Enable the "acpx" plugin');
254+expect(output).toContain("disable ACP/acpx in acp config");
255+expect(output).not.toContain('runtime policy to "pi"');
256+expect(output).not.toContain("openclaw plugins install @openclaw/acpx");
257+expect(output).not.toContain("No plugin issues detected.");
258+});
259+260+it("does not report implicit OpenAI Codex preference as configured runtime", async () => {
261+const sourceConfig = {
262+agents: {
263+defaults: {
264+model: "openai/gpt-5.5",
265+},
266+},
267+};
268+loadConfig.mockReturnValue(sourceConfig);
269+buildPluginDiagnosticsReport.mockReturnValue({
270+plugins: [],
271+diagnostics: [],
272+});
273+274+await runPluginsCommand(["plugins", "doctor"]);
275+276+const output = runtimeLogs.join("\n");
277+expect(output).not.toContain('Configured runtime "codex"');
278+expect(output).toContain("No plugin issues detected.");
279+});
280+281+it("does not report configured Codex runtime when the plugin is enabled", async () => {
282+const sourceConfig = {
283+agents: {
284+defaults: {
285+models: {
286+"openai/gpt-5.5": {
287+agentRuntime: { id: "codex" },
288+},
289+},
290+},
291+},
292+};
293+loadConfig.mockReturnValue(sourceConfig);
294+buildPluginDiagnosticsReport.mockReturnValue({
295+plugins: [createPluginRecord({ id: "codex" })],
296+diagnostics: [],
297+});
298+299+await runPluginsCommand(["plugins", "doctor"]);
300+301+expect(runtimeLogs).toContain("No plugin issues detected.");
302+});
303+304+it("reports configured Codex runtime when the plugin record is disabled", async () => {
305+const sourceConfig = {
306+agents: {
307+defaults: {
308+models: {
309+"openai/gpt-5.5": {
310+agentRuntime: { id: "codex" },
311+},
312+},
313+},
314+},
315+};
316+loadConfig.mockReturnValue(sourceConfig);
317+buildPluginDiagnosticsReport.mockReturnValue({
318+plugins: [createPluginRecord({ id: "codex", enabled: false, status: "disabled" })],
319+diagnostics: [],
320+});
321+322+await runPluginsCommand(["plugins", "doctor"]);
323+324+const output = runtimeLogs.join("\n");
325+expect(output).toContain('Configured runtime "codex" requires the Codex plugin');
326+expect(output).toContain('but "codex" is disabled');
327+expect(output).toContain('Enable the "codex" plugin');
328+expect(output).not.toContain("openclaw plugins install @openclaw/codex");
329+expect(output).not.toContain("No plugin issues detected.");
330+});
331+332+it("reports blocked configured Codex runtime without install advice", async () => {
333+const sourceConfig = {
334+plugins: {
335+deny: ["codex"],
336+},
337+agents: {
338+defaults: {
339+models: {
340+"openai/gpt-5.5": {
341+agentRuntime: { id: "codex" },
342+},
343+},
344+},
345+},
346+};
347+loadConfig.mockReturnValue(sourceConfig);
348+buildPluginDiagnosticsReport.mockReturnValue({
349+plugins: [],
350+diagnostics: [],
351+});
352+353+await runPluginsCommand(["plugins", "doctor"]);
354+355+const output = runtimeLogs.join("\n");
356+expect(output).toContain('Configured runtime "codex" requires the Codex plugin');
357+expect(output).toContain('but "codex" is blocked by plugin configuration');
358+expect(output).toContain('Remove "codex" from plugins.deny');
359+expect(output).not.toContain('Run "openclaw doctor --fix" to install');
360+expect(output).not.toContain("openclaw plugins install @openclaw/codex");
361+expect(output).not.toContain("No plugin issues detected.");
362+});
363+364+it("reports disabled configured Codex runtime entry without install advice", async () => {
365+const sourceConfig = {
366+plugins: {
367+entries: {
368+codex: { enabled: false },
369+},
370+},
371+agents: {
372+defaults: {
373+models: {
374+"openai/gpt-5.5": {
375+agentRuntime: { id: "codex" },
376+},
377+},
378+},
379+},
380+};
381+loadConfig.mockReturnValue(sourceConfig);
382+buildPluginDiagnosticsReport.mockReturnValue({
383+plugins: [],
384+diagnostics: [],
385+});
386+387+await runPluginsCommand(["plugins", "doctor"]);
388+389+const output = runtimeLogs.join("\n");
390+expect(output).toContain('Configured runtime "codex" requires the Codex plugin');
391+expect(output).toContain('but "codex" is blocked by plugin configuration');
392+expect(output).toContain("Set plugins.entries.codex.enabled=true");
393+expect(output).not.toContain('Run "openclaw doctor --fix" to install');
394+expect(output).not.toContain("openclaw plugins install @openclaw/codex");
395+expect(output).not.toContain("No plugin issues detected.");
396+});
397+141398it("reports config-selected plugin source shadowing in doctor output", async () => {
142399buildPluginDiagnosticsReport.mockReturnValue({
143400plugins: [
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。