@@ -4,16 +4,22 @@ import path from "node:path";
44import process from "node:process" ;
55import { fileURLToPath } from "node:url" ;
66import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce" ;
7+ import type { Command as CommanderCommand , Option as CommanderOption } from "commander" ;
78import { resolveStateDir } from "../config/paths.js" ;
89import type { ConfigFileSnapshot , OpenClawConfig } from "../config/types.openclaw.js" ;
10+ import { FLAG_TERMINATOR , isValueToken } from "../infra/cli-root-options.js" ;
911import { isTruthyEnvValue , normalizeEnv } from "../infra/env.js" ;
1012import { isMainModule } from "../infra/is-main.js" ;
1113import type { ProxyHandle } from "../infra/net/proxy/proxy-lifecycle.js" ;
1214import { ensureOpenClawCliOnPath } from "../infra/path-env.js" ;
1315import { assertSupportedRuntime } from "../infra/runtime-guard.js" ;
1416import type { PluginManifestCommandAliasRegistry } from "../plugins/manifest-command-aliases.js" ;
1517import { resolveCliArgvInvocation } from "./argv-invocation.js" ;
16- import { normalizeGeneratedHelpCommandArgv , normalizeRootHelpTargetArgv } from "./argv.js" ;
18+ import {
19+ normalizeGeneratedHelpCommandArgv ,
20+ normalizeRootHelpTargetArgv ,
21+ normalizeRootNoColorArgv ,
22+ } from "./argv.js" ;
1723import {
1824 isReservedNonPluginCommandRoot ,
1925 shouldRegisterPrimaryCommandOnly ,
@@ -315,6 +321,69 @@ function isCommanderParseExit(error: unknown): error is { exitCode: number } {
315321 ) ;
316322}
317323
324+ function findCommandOption ( command : CommanderCommand , token : string ) : CommanderOption | undefined {
325+ const equalsIndex = token . indexOf ( "=" ) ;
326+ const flag = equalsIndex === - 1 ? token : token . slice ( 0 , equalsIndex ) ;
327+ return command . options . find ( ( option ) => option . long === flag || option . short === flag ) ;
328+ }
329+
330+ function findSubcommand ( command : CommanderCommand , name : string ) : CommanderCommand | undefined {
331+ return command . commands . find (
332+ ( subcommand ) => subcommand . name ( ) === name || subcommand . aliases ( ) . includes ( name ) ,
333+ ) ;
334+ }
335+
336+ function shouldOptionConsumeFollowingToken (
337+ option : CommanderOption | undefined ,
338+ token : string ,
339+ next : string | undefined ,
340+ ) : boolean {
341+ if ( ! option || token . includes ( "=" ) ) {
342+ return false ;
343+ }
344+ if ( option . required ) {
345+ return true ;
346+ }
347+ return option . optional && isValueToken ( next ) ;
348+ }
349+
350+ function isNoColorConsumedAsCommandOptionValue (
351+ program : CommanderCommand ,
352+ remainingArgs : readonly string [ ] ,
353+ noColorIndex : number ,
354+ ) : boolean {
355+ let command = program ;
356+ let pendingValue = false ;
357+ for ( let index = 0 ; index < noColorIndex ; index += 1 ) {
358+ const arg = remainingArgs [ index ] ;
359+ if ( ! arg || arg === FLAG_TERMINATOR ) {
360+ return false ;
361+ }
362+ if ( pendingValue ) {
363+ pendingValue = false ;
364+ continue ;
365+ }
366+ if ( arg . startsWith ( "-" ) ) {
367+ const option = findCommandOption ( command , arg ) ;
368+ if ( ! option && index === noColorIndex - 1 && ! arg . includes ( "=" ) ) {
369+ // Unknown option surfaces may allow arbitrary flags; keep the value-safe behavior there.
370+ return true ;
371+ }
372+ pendingValue = shouldOptionConsumeFollowingToken ( option , arg , remainingArgs [ index + 1 ] ) ;
373+ continue ;
374+ }
375+ command = findSubcommand ( command , arg ) ?? command ;
376+ }
377+ return pendingValue ;
378+ }
379+
380+ function normalizeRootNoColorArgvForProgram ( argv : string [ ] , program : CommanderCommand ) : string [ ] {
381+ return normalizeRootNoColorArgv ( argv , {
382+ shouldPreserveNoColor : ( { remainingArgs, noColorIndex } ) =>
383+ isNoColorConsumedAsCommandOptionValue ( program , remainingArgs , noColorIndex ) ,
384+ } ) ;
385+ }
386+
318387async function ensureCliEnvProxyDispatcher ( ) : Promise < void > {
319388 try {
320389 const { hasEnvHttpProxyAgentConfigured } = await import ( "../infra/net/proxy-env.js" ) ;
@@ -499,7 +568,7 @@ export async function runCli(argv: string[] = process.argv) {
499568 }
500569 return ;
501570 }
502- const normalizedArgv = normalizeRootHelpTargetArgv ( parsedProfile . argv ) ;
571+ const normalizedArgv = normalizeRootHelpTargetArgv ( normalizeRootNoColorArgv ( parsedProfile . argv ) ) ;
503572 const normalizedInvocation = resolveCliArgvInvocation ( normalizedArgv ) ;
504573 const isHelpOrVersionInvocation = normalizedInvocation . hasHelpOrVersion ;
505574 startupTrace . mark ( "argv" ) ;
@@ -753,7 +822,7 @@ export async function runCli(argv: string[] = process.argv) {
753822 return ;
754823 }
755824
756- const parseArgv = normalizeGeneratedHelpCommandArgv ( rewriteUpdateFlagArgv ( normalizedArgv ) ) ;
825+ let parseArgv = normalizeGeneratedHelpCommandArgv ( rewriteUpdateFlagArgv ( normalizedArgv ) ) ;
757826 const suppressStartupProgress = hasJsonOutputFlag ( parseArgv ) ;
758827 const { createCliProgress } = await loadProgressModule ( ) ;
759828 const startupProgress = createCliProgress ( {
@@ -895,6 +964,7 @@ export async function runCli(argv: string[] = process.argv) {
895964 }
896965 }
897966
967+ parseArgv = normalizeRootNoColorArgvForProgram ( parseArgv , program ) ;
898968 stopStartupProgress ( ) ;
899969
900970 try {
0 commit comments