mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-25 22:41:45 +00:00
fix(plugins): surface configured runtime plugin doctor warnings (#81674)
Fixes #81326. Summary: - Warn from `openclaw plugins doctor` when configured runtime owner plugins are missing, disabled, or blocked. - Share configured-runtime plugin install mapping with `openclaw doctor --fix`, including ACP/acpx. - Keep implicit OpenAI Codex preferences quiet to avoid false-positive plugin doctor warnings. Verification: - `pnpm test src/agents/harness-runtimes.test.ts src/cli/plugins-cli.list.test.ts src/commands/doctor/shared/missing-configured-plugin-install.test.ts -- --reporter=verbose` - `pnpm exec oxfmt --check CHANGELOG.md src/agents/harness-runtimes.ts src/agents/harness-runtimes.test.ts src/cli/plugins-cli.runtime.ts src/cli/plugins-cli.list.test.ts src/commands/doctor/shared/configured-runtime-plugin-installs.ts src/commands/doctor/shared/missing-configured-plugin-install.ts` - `pnpm build:plugin-sdk:dts` - `codex-review --mode branch` - Testbox-through-Crabbox `pnpm check:changed`: provider `blacksmith-testbox`, id `tbx_01krt8vte22m7ht6wfss4jkeaa`, Actions run https://github.com/openclaw/openclaw/actions/runs/25983150787, exit 0 Co-authored-by: Zavian Wang <36817799+Zavianx@users.noreply.github.com>
This commit is contained in:
@@ -138,6 +138,263 @@ describe("plugins cli list", () => {
|
||||
expect(output).not.toContain("No plugin issues detected.");
|
||||
});
|
||||
|
||||
it("reports missing configured Codex runtime plugin in doctor output", async () => {
|
||||
const sourceConfig = {
|
||||
agents: {
|
||||
defaults: {
|
||||
models: {
|
||||
"openai/gpt-5.5": {
|
||||
agentRuntime: { id: "codex" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
loadConfig.mockReturnValue(sourceConfig);
|
||||
readConfigFileSnapshot.mockResolvedValueOnce({
|
||||
path: "/tmp/openclaw-config.json5",
|
||||
exists: true,
|
||||
raw: "{}",
|
||||
parsed: sourceConfig,
|
||||
resolved: sourceConfig,
|
||||
sourceConfig,
|
||||
runtimeConfig: sourceConfig,
|
||||
config: sourceConfig,
|
||||
valid: true,
|
||||
hash: "mock",
|
||||
issues: [],
|
||||
warnings: [],
|
||||
legacyIssues: [],
|
||||
});
|
||||
buildPluginDiagnosticsReport.mockReturnValue({
|
||||
plugins: [],
|
||||
diagnostics: [],
|
||||
});
|
||||
|
||||
await runPluginsCommand(["plugins", "doctor"]);
|
||||
|
||||
const output = runtimeLogs.join("\n");
|
||||
expect(output).toContain("Plugin configuration:");
|
||||
expect(output).toContain('Configured runtime "codex" requires the Codex plugin');
|
||||
expect(output).toContain("openclaw doctor --fix");
|
||||
expect(output).toContain("openclaw plugins install @openclaw/codex");
|
||||
expect(output).toContain(
|
||||
"No plugin install-tree issues detected; configuration warnings remain.",
|
||||
);
|
||||
expect(output).not.toContain("No plugin issues detected.");
|
||||
});
|
||||
|
||||
it("reports missing configured ACPX runtime plugin in doctor output", async () => {
|
||||
const sourceConfig = {
|
||||
acp: {
|
||||
backend: "acpx",
|
||||
},
|
||||
};
|
||||
loadConfig.mockReturnValue(sourceConfig);
|
||||
buildPluginDiagnosticsReport.mockReturnValue({
|
||||
plugins: [],
|
||||
diagnostics: [],
|
||||
});
|
||||
|
||||
await runPluginsCommand(["plugins", "doctor"]);
|
||||
|
||||
const output = runtimeLogs.join("\n");
|
||||
expect(output).toContain("Plugin configuration:");
|
||||
expect(output).toContain('Configured runtime "acpx" requires the ACPX Runtime plugin');
|
||||
expect(output).toContain("openclaw doctor --fix");
|
||||
expect(output).toContain("openclaw plugins install @openclaw/acpx");
|
||||
expect(output).not.toContain("No plugin issues detected.");
|
||||
});
|
||||
|
||||
it("reports blocked configured ACPX runtime with ACP-specific guidance", async () => {
|
||||
const sourceConfig = {
|
||||
acp: {
|
||||
backend: "acpx",
|
||||
},
|
||||
plugins: {
|
||||
entries: {
|
||||
acpx: { enabled: false },
|
||||
},
|
||||
},
|
||||
};
|
||||
loadConfig.mockReturnValue(sourceConfig);
|
||||
buildPluginDiagnosticsReport.mockReturnValue({
|
||||
plugins: [],
|
||||
diagnostics: [],
|
||||
});
|
||||
|
||||
await runPluginsCommand(["plugins", "doctor"]);
|
||||
|
||||
const output = runtimeLogs.join("\n");
|
||||
expect(output).toContain('Configured runtime "acpx" requires the ACPX Runtime plugin');
|
||||
expect(output).toContain("Set plugins.entries.acpx.enabled=true");
|
||||
expect(output).toContain("disable ACP/acpx in acp config");
|
||||
expect(output).not.toContain('runtime policy to "pi"');
|
||||
expect(output).not.toContain("openclaw plugins install @openclaw/acpx");
|
||||
expect(output).not.toContain("No plugin issues detected.");
|
||||
});
|
||||
|
||||
it("reports disabled configured ACPX runtime with ACP-specific guidance", async () => {
|
||||
const sourceConfig = {
|
||||
acp: {
|
||||
backend: "acpx",
|
||||
},
|
||||
};
|
||||
loadConfig.mockReturnValue(sourceConfig);
|
||||
buildPluginDiagnosticsReport.mockReturnValue({
|
||||
plugins: [createPluginRecord({ id: "acpx", enabled: false, status: "disabled" })],
|
||||
diagnostics: [],
|
||||
});
|
||||
|
||||
await runPluginsCommand(["plugins", "doctor"]);
|
||||
|
||||
const output = runtimeLogs.join("\n");
|
||||
expect(output).toContain('Configured runtime "acpx" requires the ACPX Runtime plugin');
|
||||
expect(output).toContain('Enable the "acpx" plugin');
|
||||
expect(output).toContain("disable ACP/acpx in acp config");
|
||||
expect(output).not.toContain('runtime policy to "pi"');
|
||||
expect(output).not.toContain("openclaw plugins install @openclaw/acpx");
|
||||
expect(output).not.toContain("No plugin issues detected.");
|
||||
});
|
||||
|
||||
it("does not report implicit OpenAI Codex preference as configured runtime", async () => {
|
||||
const sourceConfig = {
|
||||
agents: {
|
||||
defaults: {
|
||||
model: "openai/gpt-5.5",
|
||||
},
|
||||
},
|
||||
};
|
||||
loadConfig.mockReturnValue(sourceConfig);
|
||||
buildPluginDiagnosticsReport.mockReturnValue({
|
||||
plugins: [],
|
||||
diagnostics: [],
|
||||
});
|
||||
|
||||
await runPluginsCommand(["plugins", "doctor"]);
|
||||
|
||||
const output = runtimeLogs.join("\n");
|
||||
expect(output).not.toContain('Configured runtime "codex"');
|
||||
expect(output).toContain("No plugin issues detected.");
|
||||
});
|
||||
|
||||
it("does not report configured Codex runtime when the plugin is enabled", async () => {
|
||||
const sourceConfig = {
|
||||
agents: {
|
||||
defaults: {
|
||||
models: {
|
||||
"openai/gpt-5.5": {
|
||||
agentRuntime: { id: "codex" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
loadConfig.mockReturnValue(sourceConfig);
|
||||
buildPluginDiagnosticsReport.mockReturnValue({
|
||||
plugins: [createPluginRecord({ id: "codex" })],
|
||||
diagnostics: [],
|
||||
});
|
||||
|
||||
await runPluginsCommand(["plugins", "doctor"]);
|
||||
|
||||
expect(runtimeLogs).toContain("No plugin issues detected.");
|
||||
});
|
||||
|
||||
it("reports configured Codex runtime when the plugin record is disabled", async () => {
|
||||
const sourceConfig = {
|
||||
agents: {
|
||||
defaults: {
|
||||
models: {
|
||||
"openai/gpt-5.5": {
|
||||
agentRuntime: { id: "codex" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
loadConfig.mockReturnValue(sourceConfig);
|
||||
buildPluginDiagnosticsReport.mockReturnValue({
|
||||
plugins: [createPluginRecord({ id: "codex", enabled: false, status: "disabled" })],
|
||||
diagnostics: [],
|
||||
});
|
||||
|
||||
await runPluginsCommand(["plugins", "doctor"]);
|
||||
|
||||
const output = runtimeLogs.join("\n");
|
||||
expect(output).toContain('Configured runtime "codex" requires the Codex plugin');
|
||||
expect(output).toContain('but "codex" is disabled');
|
||||
expect(output).toContain('Enable the "codex" plugin');
|
||||
expect(output).not.toContain("openclaw plugins install @openclaw/codex");
|
||||
expect(output).not.toContain("No plugin issues detected.");
|
||||
});
|
||||
|
||||
it("reports blocked configured Codex runtime without install advice", async () => {
|
||||
const sourceConfig = {
|
||||
plugins: {
|
||||
deny: ["codex"],
|
||||
},
|
||||
agents: {
|
||||
defaults: {
|
||||
models: {
|
||||
"openai/gpt-5.5": {
|
||||
agentRuntime: { id: "codex" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
loadConfig.mockReturnValue(sourceConfig);
|
||||
buildPluginDiagnosticsReport.mockReturnValue({
|
||||
plugins: [],
|
||||
diagnostics: [],
|
||||
});
|
||||
|
||||
await runPluginsCommand(["plugins", "doctor"]);
|
||||
|
||||
const output = runtimeLogs.join("\n");
|
||||
expect(output).toContain('Configured runtime "codex" requires the Codex plugin');
|
||||
expect(output).toContain('but "codex" is blocked by plugin configuration');
|
||||
expect(output).toContain('Remove "codex" from plugins.deny');
|
||||
expect(output).not.toContain('Run "openclaw doctor --fix" to install');
|
||||
expect(output).not.toContain("openclaw plugins install @openclaw/codex");
|
||||
expect(output).not.toContain("No plugin issues detected.");
|
||||
});
|
||||
|
||||
it("reports disabled configured Codex runtime entry without install advice", async () => {
|
||||
const sourceConfig = {
|
||||
plugins: {
|
||||
entries: {
|
||||
codex: { enabled: false },
|
||||
},
|
||||
},
|
||||
agents: {
|
||||
defaults: {
|
||||
models: {
|
||||
"openai/gpt-5.5": {
|
||||
agentRuntime: { id: "codex" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
loadConfig.mockReturnValue(sourceConfig);
|
||||
buildPluginDiagnosticsReport.mockReturnValue({
|
||||
plugins: [],
|
||||
diagnostics: [],
|
||||
});
|
||||
|
||||
await runPluginsCommand(["plugins", "doctor"]);
|
||||
|
||||
const output = runtimeLogs.join("\n");
|
||||
expect(output).toContain('Configured runtime "codex" requires the Codex plugin');
|
||||
expect(output).toContain('but "codex" is blocked by plugin configuration');
|
||||
expect(output).toContain("Set plugins.entries.codex.enabled=true");
|
||||
expect(output).not.toContain('Run "openclaw doctor --fix" to install');
|
||||
expect(output).not.toContain("openclaw plugins install @openclaw/codex");
|
||||
expect(output).not.toContain("No plugin issues detected.");
|
||||
});
|
||||
|
||||
it("reports config-selected plugin source shadowing in doctor output", async () => {
|
||||
buildPluginDiagnosticsReport.mockReturnValue({
|
||||
plugins: [
|
||||
|
||||
Reference in New Issue
Block a user