CLI: handle root option values in route parsing

This commit is contained in:
Gustavo Madeira Santana
2026-03-02 18:59:47 -05:00
parent 7ee245c272
commit b799c9e22b
6 changed files with 23 additions and 5 deletions

View File

@@ -181,6 +181,11 @@ describe("argv helpers", () => {
argv: ["node", "openclaw"],
expected: null,
},
{
name: "skips known root option values",
argv: ["node", "openclaw", "--log-level", "debug", "status"],
expected: "status",
},
])("returns primary command: $name", ({ argv, expected }) => {
expect(getPrimaryCommand(argv)).toBe(expected);
});

View File

@@ -219,7 +219,7 @@ function getCommandPathInternal(
}
export function getPrimaryCommand(argv: string[]): string | null {
const [primary] = getCommandPath(argv, 1);
const [primary] = getCommandPathWithRootOptions(argv, 1);
return primary ?? null;
}

View File

@@ -69,4 +69,16 @@ describe("tryRouteCli", () => {
commandPath: ["status"],
});
});
it("routes status when root options precede the command", async () => {
await expect(tryRouteCli(["node", "openclaw", "--log-level", "debug", "status"])).resolves.toBe(
true,
);
expect(findRoutedCommandMock).toHaveBeenCalledWith(["status"]);
expect(ensureConfigReadyMock).toHaveBeenCalledWith({
runtime: expect.any(Object),
commandPath: ["status"],
});
});
});

View File

@@ -1,7 +1,7 @@
import { isTruthyEnvValue } from "../infra/env.js";
import { defaultRuntime } from "../runtime.js";
import { VERSION } from "../version.js";
import { getCommandPath, hasFlag, hasHelpOrVersion } from "./argv.js";
import { getCommandPathWithRootOptions, hasFlag, hasHelpOrVersion } from "./argv.js";
import { emitCliBanner } from "./banner.js";
import { ensurePluginRegistryLoaded } from "./plugin-registry.js";
import { ensureConfigReady } from "./program/config-guard.js";
@@ -34,7 +34,7 @@ export async function tryRouteCli(argv: string[]): Promise<boolean> {
return false;
}
const path = getCommandPath(argv, 2);
const path = getCommandPathWithRootOptions(argv, 2);
if (!path[0]) {
return false;
}

View File

@@ -114,6 +114,7 @@ describe("shouldEnsureCliPath", () => {
it("skips path bootstrap for read-only fast paths", () => {
expect(shouldEnsureCliPath(["node", "openclaw", "status"])).toBe(false);
expect(shouldEnsureCliPath(["node", "openclaw", "--log-level", "debug", "status"])).toBe(false);
expect(shouldEnsureCliPath(["node", "openclaw", "sessions", "--json"])).toBe(false);
expect(shouldEnsureCliPath(["node", "openclaw", "config", "get", "update"])).toBe(false);
expect(shouldEnsureCliPath(["node", "openclaw", "models", "status", "--json"])).toBe(false);

View File

@@ -8,8 +8,8 @@ import { ensureOpenClawCliOnPath } from "../infra/path-env.js";
import { assertSupportedRuntime } from "../infra/runtime-guard.js";
import { installUnhandledRejectionHandler } from "../infra/unhandled-rejections.js";
import { enableConsoleCapture } from "../logging.js";
import { getCommandPath, getPrimaryCommand, hasHelpOrVersion } from "./argv.js";
import { applyCliProfileEnv, parseCliProfileArgs } from "./profile.js";
import { getCommandPathWithRootOptions, getPrimaryCommand, hasHelpOrVersion } from "./argv.js";
import { tryRouteCli } from "./route.js";
import { normalizeWindowsArgv } from "./windows-argv.js";
@@ -46,7 +46,7 @@ export function shouldEnsureCliPath(argv: string[]): boolean {
if (hasHelpOrVersion(argv)) {
return false;
}
const [primary, secondary] = getCommandPath(argv, 2);
const [primary, secondary] = getCommandPathWithRootOptions(argv, 2);
if (!primary) {
return true;
}