From ea37be0e309a052af5977bba37316749284e45dd Mon Sep 17 00:00:00 2001 From: qcq01083097 Date: Thu, 4 Jun 2026 17:36:47 +0800 Subject: [PATCH] feat: Add illegal flag verification --- packages/cli/src/args.ts | 17 ++++++++++++++ packages/cli/tests/args.test.ts | 40 +++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 packages/cli/tests/args.test.ts diff --git a/packages/cli/src/args.ts b/packages/cli/src/args.ts index 5f8dc35..3065a4e 100644 --- a/packages/cli/src/args.ts +++ b/packages/cli/src/args.ts @@ -25,6 +25,15 @@ interface FlagSchema { arrays: Set; } +function buildAllowedFlagKeys(options: OptionDef[]): Set { + const keys = new Set(); + for (const opt of options) { + const key = flagKey(opt); + if (key) keys.add(key); + } + return keys; +} + function buildSchema(options: OptionDef[]): FlagSchema { const booleans = new Set(); const numbers = new Set(); @@ -91,6 +100,7 @@ export function scanCommandPath(argv: string[], globalOptions: OptionDef[] = []) * - default: string */ export function parseFlags(argv: string[], options: OptionDef[]): GlobalFlags { + const allowedKeys = buildAllowedFlagKeys(options); const schema = buildSchema(options); const flags: GlobalFlags = { quiet: false, @@ -130,6 +140,13 @@ export function parseFlags(argv: string[], options: OptionDef[]): GlobalFlags { const camelKey = kebabToCamel(key); + if (!allowedKeys.has(camelKey)) { + throw new BailianError( + `Unknown flag "--${key}". Run with --help to see available options.`, + ExitCode.USAGE, + ); + } + if (schema.booleans.has(camelKey)) { (flags as Record)[camelKey] = true; i++; diff --git a/packages/cli/tests/args.test.ts b/packages/cli/tests/args.test.ts new file mode 100644 index 0000000..1a72036 --- /dev/null +++ b/packages/cli/tests/args.test.ts @@ -0,0 +1,40 @@ +import { expect, test } from "vite-plus/test"; +import { ExitCode, GLOBAL_OPTIONS } from "bailian-cli-core"; +import { parseFlags } from "../src/args.ts"; + +const IMAGE_GENERATE_OPTIONS = [ + { flag: "--prompt ", description: "Image description", required: true }, + { flag: "--model ", description: "Model ID" }, + { flag: "--watermark ", description: "Enable watermark (true/false). Default: true." }, +]; + +test("parseFlags rejects unknown long flags", () => { + expect(() => + parseFlags(["--prompt", "cat", "--xxxx", "a"], [...GLOBAL_OPTIONS, ...IMAGE_GENERATE_OPTIONS]), + ).toThrowError( + expect.objectContaining({ + name: "BailianError", + exitCode: ExitCode.USAGE, + message: expect.stringContaining('Unknown flag "--xxxx"'), + }), + ); +}); + +test("parseFlags rejects unknown flags with = syntax", () => { + expect(() => + parseFlags( + ["--prompt=cat", "--unknown-flag=yes"], + [...GLOBAL_OPTIONS, ...IMAGE_GENERATE_OPTIONS], + ), + ).toThrow(/Unknown flag "--unknown-flag"/); +}); + +test("parseFlags accepts defined command and global flags", () => { + const flags = parseFlags( + ["--quiet", "--prompt", "cat", "--watermark", "false"], + [...GLOBAL_OPTIONS, ...IMAGE_GENERATE_OPTIONS], + ); + expect(flags.quiet).toBe(true); + expect(flags.prompt).toBe("cat"); + expect(flags.watermark).toBe("false"); +});