mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-02 21:01:51 +00:00
CLI: share root-option parsing across argv, context, and routes
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
|
||||
import { loadConfig } from "../config/config.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { consumeRootOptionToken, FLAG_TERMINATOR } from "../infra/cli-root-options.js";
|
||||
import { resolveOpenClawAgentDir } from "./agent-paths.js";
|
||||
import { ensureOpenClawModelsJson } from "./models-config.js";
|
||||
|
||||
@@ -69,40 +70,17 @@ const MODEL_CACHE = new Map<string, number>();
|
||||
let loadPromise: Promise<void> | null = null;
|
||||
let configuredWindowsPrimed = false;
|
||||
|
||||
function isValueToken(arg: string | undefined): boolean {
|
||||
if (!arg || arg === "--") {
|
||||
return false;
|
||||
}
|
||||
if (!arg.startsWith("-")) {
|
||||
return true;
|
||||
}
|
||||
return /^-\d+(?:\.\d+)?$/.test(arg);
|
||||
}
|
||||
|
||||
function getCommandPathFromArgv(argv: string[]): string[] {
|
||||
const args = argv.slice(2);
|
||||
const tokens: string[] = [];
|
||||
let skipNextAsRootValue = false;
|
||||
for (let i = 0; i < args.length; i += 1) {
|
||||
const arg = args[i];
|
||||
if (!arg || arg === "--") {
|
||||
if (!arg || arg === FLAG_TERMINATOR) {
|
||||
break;
|
||||
}
|
||||
if (skipNextAsRootValue) {
|
||||
skipNextAsRootValue = false;
|
||||
continue;
|
||||
}
|
||||
if (arg === "--profile" || arg === "--log-level") {
|
||||
const next = args[i + 1];
|
||||
skipNextAsRootValue = isValueToken(next);
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
arg === "--dev" ||
|
||||
arg === "--no-color" ||
|
||||
arg.startsWith("--profile=") ||
|
||||
arg.startsWith("--log-level=")
|
||||
) {
|
||||
const consumed = consumeRootOptionToken(args, i);
|
||||
if (consumed > 0) {
|
||||
i += consumed - 1;
|
||||
continue;
|
||||
}
|
||||
if (arg.startsWith("-")) {
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { isBunRuntime, isNodeRuntime } from "../daemon/runtime-binary.js";
|
||||
import {
|
||||
consumeRootOptionToken,
|
||||
FLAG_TERMINATOR,
|
||||
isValueToken,
|
||||
} from "../infra/cli-root-options.js";
|
||||
|
||||
const HELP_FLAGS = new Set(["-h", "--help"]);
|
||||
const VERSION_FLAGS = new Set(["-V", "--version"]);
|
||||
const ROOT_VERSION_ALIAS_FLAG = "-v";
|
||||
const ROOT_BOOLEAN_FLAGS = new Set(["--dev", "--no-color"]);
|
||||
const ROOT_VALUE_FLAGS = new Set(["--profile", "--log-level"]);
|
||||
const FLAG_TERMINATOR = "--";
|
||||
|
||||
export function hasHelpOrVersion(argv: string[]): boolean {
|
||||
return (
|
||||
@@ -13,19 +15,6 @@ export function hasHelpOrVersion(argv: string[]): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
function isValueToken(arg: string | undefined): boolean {
|
||||
if (!arg) {
|
||||
return false;
|
||||
}
|
||||
if (arg === FLAG_TERMINATOR) {
|
||||
return false;
|
||||
}
|
||||
if (!arg.startsWith("-")) {
|
||||
return true;
|
||||
}
|
||||
return /^-\d+(?:\.\d+)?$/.test(arg);
|
||||
}
|
||||
|
||||
function parsePositiveInt(value: string): number | undefined {
|
||||
const parsed = Number.parseInt(value, 10);
|
||||
if (Number.isNaN(parsed) || parsed <= 0) {
|
||||
@@ -62,17 +51,9 @@ export function hasRootVersionAlias(argv: string[]): boolean {
|
||||
hasAlias = true;
|
||||
continue;
|
||||
}
|
||||
if (ROOT_BOOLEAN_FLAGS.has(arg)) {
|
||||
continue;
|
||||
}
|
||||
if (arg.startsWith("--profile=")) {
|
||||
continue;
|
||||
}
|
||||
if (ROOT_VALUE_FLAGS.has(arg)) {
|
||||
const next = args[i + 1];
|
||||
if (isValueToken(next)) {
|
||||
i += 1;
|
||||
}
|
||||
const consumed = consumeRootOptionToken(args, i);
|
||||
if (consumed > 0) {
|
||||
i += consumed - 1;
|
||||
continue;
|
||||
}
|
||||
if (arg.startsWith("-")) {
|
||||
@@ -109,17 +90,9 @@ function isRootInvocationForFlags(
|
||||
hasTarget = true;
|
||||
continue;
|
||||
}
|
||||
if (ROOT_BOOLEAN_FLAGS.has(arg)) {
|
||||
continue;
|
||||
}
|
||||
if (arg.startsWith("--profile=") || arg.startsWith("--log-level=")) {
|
||||
continue;
|
||||
}
|
||||
if (ROOT_VALUE_FLAGS.has(arg)) {
|
||||
const next = args[i + 1];
|
||||
if (isValueToken(next)) {
|
||||
i += 1;
|
||||
}
|
||||
const consumed = consumeRootOptionToken(args, i);
|
||||
if (consumed > 0) {
|
||||
i += consumed - 1;
|
||||
continue;
|
||||
}
|
||||
// Unknown flags and subcommand-scoped help/version should fall back to Commander.
|
||||
@@ -193,17 +166,9 @@ function getCommandPathInternal(
|
||||
break;
|
||||
}
|
||||
if (opts.skipRootOptions) {
|
||||
if (arg.startsWith("--profile=") || arg.startsWith("--log-level=")) {
|
||||
continue;
|
||||
}
|
||||
if (ROOT_BOOLEAN_FLAGS.has(arg)) {
|
||||
continue;
|
||||
}
|
||||
if (ROOT_VALUE_FLAGS.has(arg)) {
|
||||
const next = args[i + 1];
|
||||
if (isValueToken(next)) {
|
||||
i += 1;
|
||||
}
|
||||
const consumed = consumeRootOptionToken(args, i);
|
||||
if (consumed > 0) {
|
||||
i += consumed - 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { consumeRootOptionToken } from "../../infra/cli-root-options.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { getFlagValue, getPositiveIntFlagValue, getVerboseFlag, hasFlag } from "../argv.js";
|
||||
|
||||
@@ -99,16 +100,6 @@ const routeMemoryStatus: RouteSpec = {
|
||||
},
|
||||
};
|
||||
|
||||
function isValueToken(arg: string | undefined): boolean {
|
||||
if (!arg || arg === "--") {
|
||||
return false;
|
||||
}
|
||||
if (!arg.startsWith("-")) {
|
||||
return true;
|
||||
}
|
||||
return /^-\d+(?:\.\d+)?$/.test(arg);
|
||||
}
|
||||
|
||||
function getCommandPositionals(argv: string[]): string[] {
|
||||
const out: string[] = [];
|
||||
const args = argv.slice(2);
|
||||
@@ -119,17 +110,9 @@ function getCommandPositionals(argv: string[]): string[] {
|
||||
break;
|
||||
}
|
||||
if (!commandStarted) {
|
||||
if (arg.startsWith("--profile=") || arg.startsWith("--log-level=")) {
|
||||
continue;
|
||||
}
|
||||
if (arg === "--dev" || arg === "--no-color") {
|
||||
continue;
|
||||
}
|
||||
if (arg === "--profile" || arg === "--log-level") {
|
||||
const next = args[i + 1];
|
||||
if (isValueToken(next)) {
|
||||
i += 1;
|
||||
}
|
||||
const consumed = consumeRootOptionToken(args, i);
|
||||
if (consumed > 0) {
|
||||
i += consumed - 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
16
src/infra/cli-root-options.test.ts
Normal file
16
src/infra/cli-root-options.test.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { consumeRootOptionToken } from "./cli-root-options.js";
|
||||
|
||||
describe("consumeRootOptionToken", () => {
|
||||
it("consumes boolean and inline root options", () => {
|
||||
expect(consumeRootOptionToken(["--dev"], 0)).toBe(1);
|
||||
expect(consumeRootOptionToken(["--profile=work"], 0)).toBe(1);
|
||||
expect(consumeRootOptionToken(["--log-level=debug"], 0)).toBe(1);
|
||||
});
|
||||
|
||||
it("consumes split root value option only when next token is a value", () => {
|
||||
expect(consumeRootOptionToken(["--profile", "work"], 0)).toBe(2);
|
||||
expect(consumeRootOptionToken(["--profile", "--no-color"], 0)).toBe(1);
|
||||
expect(consumeRootOptionToken(["--profile", "--"], 0)).toBe(1);
|
||||
});
|
||||
});
|
||||
31
src/infra/cli-root-options.ts
Normal file
31
src/infra/cli-root-options.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
export const FLAG_TERMINATOR = "--";
|
||||
|
||||
const ROOT_BOOLEAN_FLAGS = new Set(["--dev", "--no-color"]);
|
||||
const ROOT_VALUE_FLAGS = new Set(["--profile", "--log-level"]);
|
||||
|
||||
export function isValueToken(arg: string | undefined): boolean {
|
||||
if (!arg || arg === FLAG_TERMINATOR) {
|
||||
return false;
|
||||
}
|
||||
if (!arg.startsWith("-")) {
|
||||
return true;
|
||||
}
|
||||
return /^-\d+(?:\.\d+)?$/.test(arg);
|
||||
}
|
||||
|
||||
export function consumeRootOptionToken(args: ReadonlyArray<string>, index: number): number {
|
||||
const arg = args[index];
|
||||
if (!arg) {
|
||||
return 0;
|
||||
}
|
||||
if (ROOT_BOOLEAN_FLAGS.has(arg)) {
|
||||
return 1;
|
||||
}
|
||||
if (arg.startsWith("--profile=") || arg.startsWith("--log-level=")) {
|
||||
return 1;
|
||||
}
|
||||
if (ROOT_VALUE_FLAGS.has(arg)) {
|
||||
return isValueToken(args[index + 1]) ? 2 : 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user