mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-24 05:51:12 +00:00
fix(cli): route plugin packaging recovery hints
Route invalid-config recovery output for source-only installed plugin packages to plugin packaging guidance instead of openclaw doctor --fix.
Validated with focused config/CLI/gateway/plugin tests, autoreview, Crabbox/Testbox E2E tbx_01ksgr80tnvvc13kv6t126yv78, and green PR CI on 3b3ce73d0f.
Thanks @brokemac79.
This commit is contained in:
@@ -3,6 +3,11 @@ import { note } from "../../terminal/note.js";
|
||||
import { formatCliCommand } from "../command-format.js";
|
||||
import { ensureConfigReady, testApi } from "./config-guard.js";
|
||||
|
||||
const pluginPackagingRecoveryHint = [
|
||||
"This is a plugin packaging issue, not a local config problem.",
|
||||
"Update or reinstall the plugin after the publisher ships compiled JavaScript, or disable/uninstall the plugin until then.",
|
||||
].join("\n");
|
||||
|
||||
const loadAndMaybeMigrateDoctorConfigMock = vi.hoisted(() => vi.fn());
|
||||
const readConfigFileSnapshotMock = vi.hoisted(() => vi.fn());
|
||||
const setRuntimeConfigSnapshotMock = vi.hoisted(() => vi.fn());
|
||||
@@ -23,6 +28,7 @@ function makeSnapshot() {
|
||||
exists: false,
|
||||
valid: true,
|
||||
issues: [] as ConfigIssue[],
|
||||
warnings: [] as ConfigIssue[],
|
||||
legacyIssues: [] as ConfigIssue[],
|
||||
path: "/tmp/openclaw.json",
|
||||
};
|
||||
@@ -42,20 +48,16 @@ function plainErrorCalls(runtime: ReturnType<typeof makeRuntime>): string[] {
|
||||
|
||||
async function withCapturedStdout(run: () => Promise<void>): Promise<string> {
|
||||
const writes: string[] = [];
|
||||
const writeSpy = vi
|
||||
.spyOn(process.stdout, "write")
|
||||
.mockImplementation(
|
||||
((
|
||||
chunk: unknown,
|
||||
encodingOrCallback?: BufferEncoding | ((error?: Error | null) => void),
|
||||
callback?: (error?: Error | null) => void,
|
||||
) => {
|
||||
writes.push(String(chunk));
|
||||
const done = typeof encodingOrCallback === "function" ? encodingOrCallback : callback;
|
||||
done?.();
|
||||
return true;
|
||||
}) as typeof process.stdout.write,
|
||||
);
|
||||
const writeSpy = vi.spyOn(process.stdout, "write").mockImplementation(((
|
||||
chunk: unknown,
|
||||
encodingOrCallback?: BufferEncoding | ((error?: Error | null) => void),
|
||||
callback?: (error?: Error | null) => void,
|
||||
) => {
|
||||
writes.push(String(chunk));
|
||||
const done = typeof encodingOrCallback === "function" ? encodingOrCallback : callback;
|
||||
done?.();
|
||||
return true;
|
||||
}) as typeof process.stdout.write);
|
||||
try {
|
||||
await run();
|
||||
return writes.join("");
|
||||
@@ -185,6 +187,30 @@ describe("ensureConfigReady", () => {
|
||||
expect(runtime.exit).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
it("replaces doctor fix advice for plugin packaging-only invalid config", async () => {
|
||||
setInvalidSnapshot({
|
||||
issues: [
|
||||
{
|
||||
path: "plugins.slots.memory",
|
||||
message: "plugin not found: source-only-pack",
|
||||
},
|
||||
],
|
||||
warnings: [
|
||||
{
|
||||
path: "plugins",
|
||||
message:
|
||||
"plugin source-only-pack: installed plugin package requires compiled runtime output for TypeScript entry index.ts: expected ./dist/index.js. This is a plugin packaging issue, not a local config problem.",
|
||||
},
|
||||
],
|
||||
});
|
||||
const runtime = await runEnsureConfigReady(["message"]);
|
||||
const calls = plainErrorCalls(runtime);
|
||||
|
||||
expect(calls).toContain(`Fix: ${pluginPackagingRecoveryHint}`);
|
||||
expect(calls).not.toContain(`Fix: ${formatCliCommand("openclaw doctor --fix")}`);
|
||||
expect(runtime.exit).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
it("does not exit for invalid config on allowlisted commands", async () => {
|
||||
setInvalidSnapshot({
|
||||
issues: [{ path: "agents.defaults", message: 'Unrecognized key: "agentRuntime"' }],
|
||||
|
||||
@@ -96,12 +96,19 @@ export async function ensureConfigReady(params: {
|
||||
return;
|
||||
}
|
||||
|
||||
const [{ colorize, isRich, theme }, { shortenHomePath }, { formatCliCommand }] =
|
||||
await Promise.all([
|
||||
import("../../terminal/theme.js"),
|
||||
import("../../utils.js"),
|
||||
import("../command-format.js"),
|
||||
]);
|
||||
const [
|
||||
{ colorize, isRich, theme },
|
||||
{ shortenHomePath },
|
||||
{ formatCliCommand },
|
||||
{ isPluginPackagingRuntimeOutputInvalidConfigSnapshot },
|
||||
{ formatPluginPackagingRuntimeOutputRecoveryHint },
|
||||
] = await Promise.all([
|
||||
import("../../terminal/theme.js"),
|
||||
import("../../utils.js"),
|
||||
import("../command-format.js"),
|
||||
import("../../config/recovery-policy.js"),
|
||||
import("../config-recovery-hints.js"),
|
||||
]);
|
||||
const rich = isRich();
|
||||
const muted = (value: string) => colorize(rich, theme.muted, value);
|
||||
const error = (value: string) => colorize(rich, theme.error, value);
|
||||
@@ -119,9 +126,10 @@ export async function ensureConfigReady(params: {
|
||||
params.runtime.error(legacyIssues.map((issue) => ` ${error(issue)}`).join("\n"));
|
||||
}
|
||||
params.runtime.error("");
|
||||
params.runtime.error(
|
||||
`${muted("Fix:")} ${commandText(formatCliCommand("openclaw doctor --fix"))}`,
|
||||
);
|
||||
const fixHint = isPluginPackagingRuntimeOutputInvalidConfigSnapshot(snapshot)
|
||||
? formatPluginPackagingRuntimeOutputRecoveryHint()
|
||||
: commandText(formatCliCommand("openclaw doctor --fix"));
|
||||
params.runtime.error(`${muted("Fix:")} ${fixHint}`);
|
||||
params.runtime.error(
|
||||
`${muted("Inspect:")} ${commandText(formatCliCommand("openclaw config validate"))}`,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user