mirror of
https://github.com/openclaw/clawhub.git
synced 2026-08-14 08:52:21 +00:00
fix(cli): report unknown root commands (#2433)
This commit is contained in:
@@ -89,6 +89,55 @@ function registerCommandGroup(parent: Command, path: readonly string[]) {
|
||||
return parent.command(path.at(-1) ?? "");
|
||||
}
|
||||
|
||||
function validateTopLevelCommand(args: string[]) {
|
||||
if (hasTerminalGlobalFlag(args)) return;
|
||||
const commandName = findFirstTopLevelOperand(args);
|
||||
if (!commandName) return;
|
||||
const knownCommands = new Set([
|
||||
"help",
|
||||
...program.commands.flatMap((command) => [command.name(), ...command.aliases()]),
|
||||
]);
|
||||
if (knownCommands.has(commandName)) return;
|
||||
program.error(`error: unknown command '${commandName}'`, { code: "commander.unknownCommand" });
|
||||
}
|
||||
|
||||
function hasTerminalGlobalFlag(args: string[]) {
|
||||
return args.some(
|
||||
(arg) => arg === "--help" || arg === "-h" || arg === "--cli-version" || arg === "-V",
|
||||
);
|
||||
}
|
||||
|
||||
function findFirstTopLevelOperand(args: string[]) {
|
||||
for (let index = 0; index < args.length; index += 1) {
|
||||
const arg = args[index];
|
||||
if (!arg) continue;
|
||||
if (arg === "--") return args[index + 1];
|
||||
if (arg.startsWith("--")) {
|
||||
if (arg === "--workdir" || arg === "--dir" || arg === "--site" || arg === "--registry") {
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
arg.startsWith("--workdir=") ||
|
||||
arg.startsWith("--dir=") ||
|
||||
arg.startsWith("--site=") ||
|
||||
arg.startsWith("--registry=")
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
if (arg === "--help" || arg === "--cli-version") return undefined;
|
||||
if (arg === "--no-input") continue;
|
||||
return undefined;
|
||||
}
|
||||
if (arg.startsWith("-")) {
|
||||
if (arg === "-h" || arg === "-V") return undefined;
|
||||
return undefined;
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async function resolveGlobalOpts(): Promise<GlobalOpts> {
|
||||
const raw = program.opts<{ workdir?: string; dir?: string; site?: string; registry?: string }>();
|
||||
const workdir = await resolveWorkdir(raw.workdir);
|
||||
@@ -735,6 +784,8 @@ program.action(async () => {
|
||||
process.exitCode = 0;
|
||||
});
|
||||
|
||||
validateTopLevelCommand(process.argv.slice(2));
|
||||
|
||||
void program.parseAsync(process.argv).catch((error) => {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
fail(message);
|
||||
|
||||
@@ -54,6 +54,42 @@ describe("built CLI artifact", () => {
|
||||
expect(result.stdout).toContain("ClawHub CLI");
|
||||
});
|
||||
|
||||
it("reports unknown top-level commands clearly", async () => {
|
||||
const result = runNode([binPath, "nope"]);
|
||||
|
||||
expect(result.status).toBe(1);
|
||||
expect(result.stderr).toContain("error: unknown command 'nope'");
|
||||
expect(result.stderr).not.toContain("too many arguments");
|
||||
});
|
||||
|
||||
it("reports unknown top-level commands after global options", async () => {
|
||||
const result = runNode([binPath, "--registry", "https://clawhub.ai", "nope"]);
|
||||
|
||||
expect(result.status).toBe(1);
|
||||
expect(result.stderr).toContain("error: unknown command 'nope'");
|
||||
expect(result.stderr).not.toContain("too many arguments");
|
||||
});
|
||||
|
||||
it("does not mask unknown global options", async () => {
|
||||
const result = runNode([binPath, "--bad", "nope"]);
|
||||
|
||||
expect(result.status).toBe(1);
|
||||
expect(result.stderr).toContain("error: unknown option '--bad'");
|
||||
expect(result.stderr).not.toContain("unknown command 'nope'");
|
||||
});
|
||||
|
||||
it("keeps help and version flags terminal", async () => {
|
||||
const helpResult = runNode([binPath, "nope", "--help"]);
|
||||
const versionResult = runNode([binPath, "--cli-version", "nope"]);
|
||||
|
||||
expect(helpResult.status).toBe(0);
|
||||
expect(helpResult.stderr).toBe("");
|
||||
expect(helpResult.stdout).toContain("ClawHub CLI");
|
||||
expect(versionResult.status).toBe(0);
|
||||
expect(versionResult.stderr).toBe("");
|
||||
expect(versionResult.stdout).toMatch(/^\d+\.\d+\.\d+/);
|
||||
});
|
||||
|
||||
it("publishes a local code plugin in dry-run json mode from built output", async () => {
|
||||
const root = await makeTmpDir("clawhub-artifact-");
|
||||
const pluginDir = join(root, "demo-plugin");
|
||||
|
||||
Reference in New Issue
Block a user