fix: address review feedback (#5871)

This commit is contained in:
Catalin Lupuleti
2026-02-26 22:10:12 +00:00
committed by Vincent Koc
parent 83d609926e
commit c5e6209310
5 changed files with 76 additions and 5 deletions

View File

@@ -8,6 +8,7 @@ import {
getVerboseFlag,
hasHelpOrVersion,
hasFlag,
isRootVersionRequest,
shouldMigrateState,
shouldMigrateStateFromPath,
} from "./argv.js";
@@ -284,4 +285,51 @@ describe("argv helpers", () => {
])("reuses command path for migrate state decisions: $path", ({ path, expected }) => {
expect(shouldMigrateStateFromPath(path)).toBe(expected);
});
// isRootVersionRequest: guards the entry.ts fast-path exit.
// Each case documents a semantic boundary that the fast path must respect.
it.each([
{
name: "--version flag",
argv: ["node", "openclaw", "--version"],
expected: true,
},
{
name: "-V flag",
argv: ["node", "openclaw", "-V"],
expected: true,
},
{
name: "-v root alias (no subcommand)",
argv: ["node", "openclaw", "-v"],
expected: true,
},
{
name: "-v root alias with a root-level flag before it",
argv: ["node", "openclaw", "--dev", "-v"],
expected: true,
},
{
name: "--version after -- terminator must NOT trigger (forwarded arg)",
argv: ["node", "openclaw", "nodes", "run", "--", "git", "--version"],
expected: false,
},
{
name: "-V after -- terminator must NOT trigger",
argv: ["node", "openclaw", "--", "-V"],
expected: false,
},
{
name: "-v with subcommand must NOT trigger (subcommand-scoped flag)",
argv: ["node", "openclaw", "acp", "-v"],
expected: false,
},
{
name: "normal command without version flag",
argv: ["node", "openclaw", "status"],
expected: false,
},
])("isRootVersionRequest: $name", ({ argv, expected }) => {
expect(isRootVersionRequest(argv)).toBe(expected);
});
});

View File

@@ -13,6 +13,17 @@ export function hasHelpOrVersion(argv: string[]): boolean {
);
}
/**
* Returns true only when the process is a root-level version request:
* - `--version` or `-V` appear before any `--` terminator (so forwarded args like
* `nodes run -- git --version` are excluded), and
* - `-v` is only matched at root scope (no subcommand before it).
* Used by the entry.ts fast path to exit before loading heavy CLI modules.
*/
export function isRootVersionRequest(argv: string[]): boolean {
return hasFlag(argv, "--version") || hasFlag(argv, "-V") || hasRootVersionAlias(argv);
}
function isValueToken(arg: string | undefined): boolean {
if (!arg) {
return false;

View File

@@ -23,9 +23,14 @@ export function resolveCliChannelOptions(): string[] {
const catalog = listChannelPluginCatalogEntries().map((entry) => entry.id);
const base = dedupe([...CHAT_CHANNEL_ORDER, ...catalog]);
if (isTruthyEnvValue(process.env.OPENCLAW_EAGER_CHANNEL_OPTIONS)) {
// Reads plugin channel IDs from the registry if already populated.
// (ensurePluginRegistryLoaded is intentionally not called here to avoid
// loading jiti; plugins are loaded by the preaction hook for real commands.)
// CHANGED SEMANTIC: ensurePluginRegistryLoaded() is intentionally NOT called
// here to avoid pulling in plugins/loader.ts → jiti at startup (slow on
// low-powered devices). As a result, OPENCLAW_EAGER_CHANNEL_OPTIONS is
// effectively a no-op for its original purpose of force-loading all plugins
// into the option list before Commander parses args. Plugin IDs are only
// included here if the registry was already populated by some other means
// (e.g. the preaction hook for a real command). If this env var behaviour is
// needed, restore the ensurePluginRegistryLoaded() call explicitly.
const pluginIds = listChannelPlugins().map((plugin) => plugin.id);
return dedupe([...base, ...pluginIds]);
}

View File

@@ -106,6 +106,9 @@ export function configureProgramHelp(program: Command, ctx: ProgramContext) {
outputError: (str, write) => write(theme.error(str)),
});
// Defense-in-depth: entry.ts already exits early for version flags before
// loading this module. This block handles the case where help is configured
// outside the normal entry path (e.g. tests, programmatic use).
if (
hasFlag(process.argv, "-V") ||
hasFlag(process.argv, "--version") ||

View File

@@ -2,6 +2,7 @@
import { spawn } from "node:child_process";
import process from "node:process";
import { fileURLToPath } from "node:url";
import { isRootVersionRequest } from "./cli/argv.js";
import { applyCliProfileEnv, parseCliProfileArgs } from "./cli/profile.js";
import { shouldSkipRespawnForArgv } from "./cli/respawn-policy.js";
import { normalizeWindowsArgv } from "./cli/windows-argv.js";
@@ -132,9 +133,12 @@ if (
}
// Fast path: print version and exit before loading heavy CLI modules.
// This avoids the full plugin/config startup on low-powered devices (e.g. Pi4b).
// isRootVersionRequest handles --version/-V (stops at -- terminator so forwarded
// args like `nodes run -- git --version` are excluded) and -v (only at root scope,
// not when a subcommand is present). Avoids the full plugin/config startup on
// low-powered devices (e.g. Pi4b).
const argv = process.argv;
if (argv.includes("--version") || argv.includes("-V")) {
if (isRootVersionRequest(argv)) {
console.log(VERSION);
process.exit(0);
}