mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 19:10:42 +00:00
fix(cli): exit 0 when invoking parent commands without a subcommand (#73077)
Several `openclaw <parent>` commands (channels, plugins, approvals, devices, cron, mcp) were exiting with code 1 when invoked bare, while printing the same help-style content that `<parent> --help` produces (which exits 0). This broke `&&` chains and surfaced a misleading `ELIFECYCLE Command failed with exit code 1.` line under pnpm. Add a small `applyParentDefaultHelpAction(cmd)` helper in `src/cli/program/parent-default-help.ts` that attaches a default action which prints the parent's own help and sets `process.exitCode = 0`. The helper is a no-op when the parent already has its own action (e.g. `agents` defaulting to `agents list`), so existing intentional defaults are preserved. Apply it to the six core parents listed in #73077.
This commit is contained in:
committed by
Peter Steinberger
parent
482c74b724
commit
ba80695bba
44
src/cli/program/parent-default-help.test.ts
Normal file
44
src/cli/program/parent-default-help.test.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { Command } from "commander";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { applyParentDefaultHelpAction } from "./parent-default-help.js";
|
||||
|
||||
describe("applyParentDefaultHelpAction (#73077)", () => {
|
||||
let originalExitCode: NodeJS.Process["exitCode"];
|
||||
beforeEach(() => {
|
||||
originalExitCode = process.exitCode;
|
||||
process.exitCode = undefined;
|
||||
});
|
||||
afterEach(() => {
|
||||
process.exitCode = originalExitCode;
|
||||
});
|
||||
|
||||
function buildParent(): Command {
|
||||
const program = new Command();
|
||||
program.exitOverride();
|
||||
const parent = program.command("parent").description("test parent");
|
||||
parent.exitOverride();
|
||||
parent.command("list").action(() => {});
|
||||
parent.command("status").action(() => {});
|
||||
return parent;
|
||||
}
|
||||
|
||||
it("invokes parent help and exits 0 when invoked without subcommand", async () => {
|
||||
const parent = buildParent();
|
||||
const helpSpy = vi.spyOn(parent, "outputHelp").mockImplementation(() => {});
|
||||
applyParentDefaultHelpAction(parent);
|
||||
await parent.parent!.parseAsync(["node", "test", "parent"]);
|
||||
expect(helpSpy).toHaveBeenCalledTimes(1);
|
||||
expect(process.exitCode).toBe(0);
|
||||
});
|
||||
|
||||
it("still routes through subcommand actions when one is invoked", async () => {
|
||||
const parent = buildParent();
|
||||
const listAction = vi.fn();
|
||||
parent.commands.find((c) => c.name() === "list")!.action(listAction);
|
||||
const helpSpy = vi.spyOn(parent, "outputHelp").mockImplementation(() => {});
|
||||
applyParentDefaultHelpAction(parent);
|
||||
await parent.parent!.parseAsync(["node", "test", "parent", "list"]);
|
||||
expect(listAction).toHaveBeenCalledTimes(1);
|
||||
expect(helpSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
22
src/cli/program/parent-default-help.ts
Normal file
22
src/cli/program/parent-default-help.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import type { Command } from "commander";
|
||||
|
||||
/**
|
||||
* Wire a parent command so that invoking it without a subcommand prints the
|
||||
* parent's own help and exits with status `0`.
|
||||
*
|
||||
* Commander's default behavior for a parent with subcommands is to print help
|
||||
* and set `process.exitCode = 1`, which differs from `<parent> --help` (which
|
||||
* exits 0). That asymmetry breaks shell `&&` chains and surfaces a misleading
|
||||
* `ELIFECYCLE Command failed with exit code 1.` line for users running through
|
||||
* pnpm. See #73077.
|
||||
*
|
||||
* Apply this helper only to parent commands that do not have their own default
|
||||
* action. Commander does not expose a public "has action handler" API, so
|
||||
* callers keep that ownership explicit instead of probing private internals.
|
||||
*/
|
||||
export function applyParentDefaultHelpAction(parent: Command): void {
|
||||
parent.action(() => {
|
||||
parent.outputHelp();
|
||||
process.exitCode = 0;
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user