mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-25 20:05:46 -06:00
fix(cli): preserve gateway startup policy with parent options (#117675)
* fix(cli): use parsed command path for startup Fixes #117674 * style(cli): format startup regression
This commit is contained in:
committed by
GitHub
parent
4153451a9a
commit
91c9d8f8d5
@@ -123,11 +123,21 @@ describe("command-execution-startup", () => {
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("uses the resolved action command path for protocol startup policy", () => {
|
||||
it("uses the resolved action command path for every execution startup decision", () => {
|
||||
const context = mod.resolveCliExecutionStartupContext({
|
||||
argv: ["node", "openclaw", "gateway", "--token", "secret", "call", "health"],
|
||||
commandPath: ["gateway", "call"],
|
||||
jsonOutputMode: false,
|
||||
env: {},
|
||||
});
|
||||
|
||||
expect(context.invocation.commandPath).toEqual(["gateway", "secret"]);
|
||||
expect(context.commandPath).toEqual(["gateway", "call"]);
|
||||
|
||||
expect(
|
||||
mod.resolveCliExecutionStartupContext({
|
||||
argv: ["node", "openclaw", "acp", "--token", "-secret"],
|
||||
protocolCommandPath: ["acp"],
|
||||
commandPath: ["acp"],
|
||||
jsonOutputMode: false,
|
||||
env: {},
|
||||
}).startupPolicy.suppressDoctorStdout,
|
||||
@@ -135,7 +145,7 @@ describe("command-execution-startup", () => {
|
||||
expect(
|
||||
mod.resolveCliExecutionStartupContext({
|
||||
argv: ["node", "openclaw", "acp", "--verbose", "client"],
|
||||
protocolCommandPath: ["acp", "client"],
|
||||
commandPath: ["acp", "client"],
|
||||
jsonOutputMode: false,
|
||||
env: {},
|
||||
}).startupPolicy.suppressDoctorStdout,
|
||||
|
||||
@@ -16,21 +16,21 @@ const hasVersionFlag = (argv: readonly string[]) =>
|
||||
|
||||
export function resolveCliExecutionStartupContext(params: {
|
||||
argv: string[];
|
||||
protocolCommandPath?: string[];
|
||||
commandPath?: string[];
|
||||
jsonOutputMode: boolean;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
routeMode?: boolean;
|
||||
}) {
|
||||
// Resolve argv once so startup policy, routing, and bootstrap share the same command path.
|
||||
const invocation = resolveCliArgvInvocation(params.argv);
|
||||
const { commandPath } = invocation;
|
||||
// Commander owns the action path after parsing option values. Route-first
|
||||
// callers omit it and keep using raw argv discovery.
|
||||
const commandPath = params.commandPath ?? invocation.commandPath;
|
||||
return {
|
||||
invocation,
|
||||
commandPath,
|
||||
startupPolicy: resolveCliStartupPolicy({
|
||||
argv: params.argv,
|
||||
commandPath,
|
||||
protocolCommandPath: params.protocolCommandPath,
|
||||
jsonOutputMode: params.jsonOutputMode,
|
||||
env: params.env,
|
||||
routeMode: params.routeMode,
|
||||
|
||||
@@ -28,19 +28,13 @@ function shouldLoadPlugins(params: {
|
||||
export function resolveCliStartupPolicy(params: {
|
||||
argv?: string[];
|
||||
commandPath: string[];
|
||||
protocolCommandPath?: string[];
|
||||
jsonOutputMode: boolean;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
routeMode?: boolean;
|
||||
}) {
|
||||
const commandPolicy = resolveCliCommandPathPolicy(params.commandPath);
|
||||
// Commander resolves required option values before selecting the action command, so this path
|
||||
// remains authoritative when a protocol option value itself begins with "-".
|
||||
const ownsProtocolStdout = params.protocolCommandPath
|
||||
? resolveCliCommandPathPolicy(params.protocolCommandPath).ownsProtocolStdout
|
||||
: commandPolicy.ownsProtocolStdout;
|
||||
// Protocol commands own stdout from process startup, before their action installs later routing.
|
||||
const suppressDoctorStdout = params.jsonOutputMode || ownsProtocolStdout;
|
||||
const suppressDoctorStdout = params.jsonOutputMode || commandPolicy.ownsProtocolStdout;
|
||||
const env = params.env ?? process.env;
|
||||
return {
|
||||
suppressDoctorStdout,
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Command } from "commander";
|
||||
import { repoInstallSpec } from "openclaw/plugin-sdk/test-fixtures";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { loggingState } from "../../logging/state.js";
|
||||
import { shouldMigrateStateFromPath } from "../argv.js";
|
||||
import { isConfigSetJsonParseOnly } from "../config-output-mode.js";
|
||||
import { setCommandJsonMode } from "./json-mode.js";
|
||||
import { applyParentDefaultHelpAction } from "./parent-default-help.js";
|
||||
@@ -149,7 +150,7 @@ describe("registerPreActionHooks", () => {
|
||||
| null = null;
|
||||
|
||||
function buildProgram() {
|
||||
const programLocal = new Command().name("openclaw");
|
||||
const programLocal = new Command().name("openclaw").enablePositionalOptions();
|
||||
const agent = programLocal
|
||||
.command("agent")
|
||||
.argument("[note]")
|
||||
@@ -181,6 +182,8 @@ describe("registerPreActionHooks", () => {
|
||||
.action(() => {});
|
||||
const gateway = programLocal
|
||||
.command("gateway")
|
||||
.option("--port <port>")
|
||||
.option("--token <token>")
|
||||
.option("--allow-unconfigured")
|
||||
.option("--force")
|
||||
.option("--reset")
|
||||
@@ -191,6 +194,15 @@ describe("registerPreActionHooks", () => {
|
||||
.option("--force")
|
||||
.option("--reset")
|
||||
.action(() => {});
|
||||
gateway
|
||||
.command("call")
|
||||
.argument("<method>")
|
||||
.option("--json")
|
||||
.action(() => {});
|
||||
gateway
|
||||
.command("health")
|
||||
.option("--json")
|
||||
.action(() => {});
|
||||
programLocal
|
||||
.command("backup")
|
||||
.command("create")
|
||||
@@ -890,6 +902,36 @@ describe("registerPreActionHooks", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
name: "keeps a remote call migration-free",
|
||||
argv: ["node", "openclaw", "gateway", "--token", "secret", "call", "health", "--json"],
|
||||
expectedPath: ["gateway", "call"],
|
||||
expectedMigration: false,
|
||||
},
|
||||
{
|
||||
name: "keeps health on the invalid-config allowlist",
|
||||
argv: ["node", "openclaw", "gateway", "--port", "19083", "health", "--json"],
|
||||
expectedPath: ["gateway", "health"],
|
||||
expectedMigration: true,
|
||||
},
|
||||
])("uses the Commander path past parent option values and $name", async (testCase) => {
|
||||
const parseProgram = buildProgram();
|
||||
process.argv = testCase.argv;
|
||||
|
||||
await parseProgram.parseAsync(process.argv);
|
||||
|
||||
const bootstrap = ensureConfigReadyMock.mock.calls.at(-1)?.[0];
|
||||
expect(bootstrap).toEqual({
|
||||
runtime: runtimeMock,
|
||||
commandPath: testCase.expectedPath,
|
||||
suppressDoctorStdout: true,
|
||||
});
|
||||
expect(shouldMigrateStateFromPath(bootstrap?.commandPath ?? [])).toBe(
|
||||
testCase.expectedMigration,
|
||||
);
|
||||
});
|
||||
|
||||
it("does not preload plugins for agents list JSON output", async () => {
|
||||
await runPreAction({
|
||||
parseArgv: ["agents", "list"],
|
||||
|
||||
@@ -121,7 +121,7 @@ export function registerPreActionHooks(program: Command, programVersion: string)
|
||||
applyResolvedCommandOutputMode(jsonOutputMode);
|
||||
const { commandPath, startupPolicy } = resolveCliExecutionStartupContext({
|
||||
argv,
|
||||
protocolCommandPath: getCommanderCommandPath(actionCommand),
|
||||
commandPath: getCommanderCommandPath(actionCommand),
|
||||
jsonOutputMode,
|
||||
env: process.env,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user