


















@@ -23,6 +23,25 @@ async function loadQaLabCliRuntime(): Promise<QaLabCliRuntime> {
2323return await qaLabCliRuntimePromise;
2424}
252526+const DECIMAL_INTEGER_RE = /^\d+$/;
27+28+function invalidQaCliArgument(message: string): Error & { code: string; exitCode: number } {
29+const error = new Error(message) as Error & { code: string; exitCode: number };
30+error.name = "InvalidArgumentError";
31+error.code = "commander.invalidArgument";
32+error.exitCode = 1;
33+return error;
34+}
35+36+function parseQaCliPositiveIntegerOption(value: string, flag: string): number {
37+const trimmed = value.trim();
38+const parsed = DECIMAL_INTEGER_RE.test(trimmed) ? Number(trimmed) : Number.NaN;
39+if (!Number.isSafeInteger(parsed) || parsed < 1) {
40+throw invalidQaCliArgument(`${flag} must be a positive integer.`);
41+}
42+return parsed;
43+}
44+2645async function runQaSelfCheck(opts: { repoRoot?: string; output?: string }) {
2746const runtime = await loadQaLabCliRuntime();
2847await runtime.runQaLabSelfCheckCommand(opts);
@@ -302,7 +321,7 @@ export function registerQaLabCli(program: Command) {
302321[],
303322)
304323.option("--concurrency <count>", "Scenario worker concurrency", (value: string) =>
305-Number(value),
324+parseQaCliPositiveIntegerOption(value, "--concurrency"),
306325)
307326.option("--preflight", "Run a single-scenario bootstrap preflight and stop", false)
308327.option(
@@ -316,7 +335,9 @@ export function registerQaLabCli(program: Command) {
316335"Suite thinking default: off|minimal|low|medium|high|xhigh|adaptive|max",
317336)
318337.option("--image <alias>", "Multipass image alias")
319-.option("--cpus <count>", "Multipass vCPU count", (value: string) => Number(value))
338+.option("--cpus <count>", "Multipass vCPU count", (value: string) =>
339+parseQaCliPositiveIntegerOption(value, "--cpus"),
340+)
320341.option("--memory <size>", "Multipass memory size")
321342.option("--disk <size>", "Multipass disk size")
322343.option("--runtime-pair <pair>", "Run each scenario under both runtimes, e.g. openclaw,codex")
@@ -534,17 +555,17 @@ export function registerQaLabCli(program: Command) {
534555[],
535556)
536557.option("--judge-timeout-ms <ms>", "Override judge wait timeout", (value: string) =>
537-Number(value),
558+parseQaCliPositiveIntegerOption(value, "--judge-timeout-ms"),
538559)
539560.option(
540561"--blind-judge-models",
541562"Hide candidate model refs from judge prompts; reports still map rankings back to real refs",
542563)
543564.option("--concurrency <count>", "Candidate model run concurrency", (value: string) =>
544-Number(value),
565+parseQaCliPositiveIntegerOption(value, "--concurrency"),
545566)
546567.option("--judge-concurrency <count>", "Judge model run concurrency", (value: string) =>
547-Number(value),
568+parseQaCliPositiveIntegerOption(value, "--judge-concurrency"),
548569)
549570.action(
550571async (opts: {
@@ -574,7 +595,9 @@ export function registerQaLabCli(program: Command) {
574595.option("--model <ref>", "Primary provider/model ref (defaults by provider mode)")
575596.option("--alt-model <ref>", "Alternate provider/model ref")
576597.option("--fast", "Enable provider fast mode where supported", false)
577-.option("--timeout-ms <ms>", "Override agent.wait timeout", (value: string) => Number(value))
598+.option("--timeout-ms <ms>", "Override agent.wait timeout", (value: string) =>
599+parseQaCliPositiveIntegerOption(value, "--timeout-ms"),
600+)
578601.action(
579602async (opts: {
580603message: string;
@@ -672,7 +695,9 @@ export function registerQaLabCli(program: Command) {
672695.description("List credential rows in the shared Convex pool")
673696.option("--kind <kind>", "Filter by credential kind")
674697.option("--status <status>", 'Filter by row status: "active", "disabled", or "all"', "all")
675-.option("--limit <count>", "Max rows to return", (value: string) => Number(value))
698+.option("--limit <count>", "Max rows to return", (value: string) =>
699+parseQaCliPositiveIntegerOption(value, "--limit"),
700+)
676701.option("--show-secrets", "Include credential payload JSON in output", false)
677702.option("--site-url <url>", "Override OPENCLAW_QA_CONVEX_SITE_URL")
678703.option("--endpoint-prefix <path>", "Override OPENCLAW_QA_CONVEX_ENDPOINT_PREFIX")
@@ -697,10 +722,12 @@ export function registerQaLabCli(program: Command) {
697722.description("Start the private QA debugger UI and local QA bus")
698723.option("--repo-root <path>", "Repository root to target when running from a neutral cwd")
699724.option("--host <host>", "Bind host", "127.0.0.1")
700-.option("--port <port>", "Bind port", (value: string) => Number(value))
725+.option("--port <port>", "Bind port", (value: string) =>
726+parseQaCliPositiveIntegerOption(value, "--port"),
727+)
701728.option("--advertise-host <host>", "Optional public host to advertise in bootstrap payloads")
702729.option("--advertise-port <port>", "Optional public port to advertise", (value: string) =>
703-Number(value),
730+parseQaCliPositiveIntegerOption(value, "--advertise-port"),
704731)
705732.option("--control-ui-url <url>", "Optional Control UI URL to embed beside the QA panel")
706733.option(
@@ -737,8 +764,12 @@ export function registerQaLabCli(program: Command) {
737764.description("Write a prebaked Docker scaffold for the QA dashboard + gateway lane")
738765.option("--repo-root <path>", "Repository root to target when running from a neutral cwd")
739766.requiredOption("--output-dir <path>", "Output directory for docker-compose + state files")
740-.option("--gateway-port <port>", "Gateway host port", (value: string) => Number(value))
741-.option("--qa-lab-port <port>", "QA lab host port", (value: string) => Number(value))
767+.option("--gateway-port <port>", "Gateway host port", (value: string) =>
768+parseQaCliPositiveIntegerOption(value, "--gateway-port"),
769+)
770+.option("--qa-lab-port <port>", "QA lab host port", (value: string) =>
771+parseQaCliPositiveIntegerOption(value, "--qa-lab-port"),
772+)
742773.option("--provider-base-url <url>", "Provider base URL for the QA gateway")
743774.option("--image <name>", "Prebaked image name", "openclaw:qa-local-prebaked")
744775.option("--use-prebuilt-image", "Use image: instead of build: in docker-compose", false)
@@ -774,8 +805,12 @@ export function registerQaLabCli(program: Command) {
774805.description("Build the QA site, start the Docker-backed QA stack, and print the QA Lab URL")
775806.option("--repo-root <path>", "Repository root to target when running from a neutral cwd")
776807.option("--output-dir <path>", "Output directory for docker-compose + state files")
777-.option("--gateway-port <port>", "Gateway host port", (value: string) => Number(value))
778-.option("--qa-lab-port <port>", "QA lab host port", (value: string) => Number(value))
808+.option("--gateway-port <port>", "Gateway host port", (value: string) =>
809+parseQaCliPositiveIntegerOption(value, "--gateway-port"),
810+)
811+.option("--qa-lab-port <port>", "QA lab host port", (value: string) =>
812+parseQaCliPositiveIntegerOption(value, "--qa-lab-port"),
813+)
779814.option("--provider-base-url <url>", "Provider base URL for the QA gateway")
780815.option("--image <name>", "Image tag", "openclaw:qa-local-prebaked")
781816.option("--use-prebuilt-image", "Use image: instead of build: in docker-compose", false)
@@ -805,7 +840,9 @@ export function registerQaLabCli(program: Command) {
805840qa.command(providerCommand.name)
806841.description(providerCommand.description)
807842.option("--host <host>", "Bind host", "127.0.0.1")
808-.option("--port <port>", "Bind port", (value: string) => Number(value))
843+.option("--port <port>", "Bind port", (value: string) =>
844+parseQaCliPositiveIntegerOption(value, "--port"),
845+)
809846.action(async (opts: { host?: string; port?: number }) => {
810847await runQaProviderServer(providerCommand.providerMode, opts);
811848});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。