From cbd8049b9fbb2590acf8fb44beed0c65fbf9dcd1 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sat, 30 May 2026 17:35:27 +0200 Subject: [PATCH] fix(scripts): parse forwarded package script options --- scripts/check-gateway-cpu-scenarios.mjs | 8 ++-- scripts/check-plugin-gateway-gauntlet.mjs | 8 ++-- scripts/e2e/parallels/linux-smoke.ts | 33 +++++++++------- scripts/e2e/parallels/macos-smoke.ts | 39 +++++++++++-------- scripts/e2e/parallels/npm-update-smoke.ts | 27 +++++++------ scripts/e2e/parallels/windows-smoke.ts | 11 ++++-- scripts/lib/arg-utils.mjs | 4 ++ scripts/profile-extension-memory.mjs | 18 +++++---- scripts/release-beta-smoke.ts | 19 +++++---- scripts/release-candidate-checklist.mjs | 32 ++++++++------- .../check-gateway-cpu-scenarios.test.ts | 20 ++++++++++ test/scripts/parallels-smoke-model.test.ts | 10 ++++- test/scripts/plugin-gateway-gauntlet.test.ts | 18 +++++++++ test/scripts/profile-extension-memory.test.ts | 7 ++++ test/scripts/release-beta-smoke.test.ts | 7 ++++ .../release-candidate-checklist.test.ts | 17 ++++++++ 16 files changed, 196 insertions(+), 82 deletions(-) diff --git a/scripts/check-gateway-cpu-scenarios.mjs b/scripts/check-gateway-cpu-scenarios.mjs index 23165e1c6cb..b917d973d62 100644 --- a/scripts/check-gateway-cpu-scenarios.mjs +++ b/scripts/check-gateway-cpu-scenarios.mjs @@ -10,6 +10,7 @@ import { parsePositiveInt, parsePositiveNumber, } from "./lib/numeric-options.mjs"; +import { stripLeadingPackageManagerSeparator } from "./lib/arg-utils.mjs"; import { collectGatewayCpuObservations } from "./lib/plugin-gateway-gauntlet.mjs"; import { createPnpmRunnerSpawnSpec } from "./pnpm-runner.mjs"; @@ -23,6 +24,7 @@ const DEFAULT_CPU_CORE_WARN = 0.9; const DEFAULT_HOT_WALL_WARN_MS = 30_000; function parseArgs(argv) { + const args = stripLeadingPackageManagerSeparator(argv); const options = { outputDir: path.join( process.cwd(), @@ -39,10 +41,10 @@ function parseArgs(argv) { cpuCoreWarn: DEFAULT_CPU_CORE_WARN, hotWallWarnMs: DEFAULT_HOT_WALL_WARN_MS, }; - for (let index = 0; index < argv.length; index += 1) { - const arg = argv[index]; + for (let index = 0; index < args.length; index += 1) { + const arg = args[index]; const readValue = () => { - const value = argv[index + 1]; + const value = args[index + 1]; if (!value) { throw new Error(`Missing value for ${arg}`); } diff --git a/scripts/check-plugin-gateway-gauntlet.mjs b/scripts/check-plugin-gateway-gauntlet.mjs index 3c4fb6937b2..e9c7e81f244 100644 --- a/scripts/check-plugin-gateway-gauntlet.mjs +++ b/scripts/check-plugin-gateway-gauntlet.mjs @@ -11,6 +11,7 @@ import { parsePositiveInt, parsePositiveNumber, } from "./lib/numeric-options.mjs"; +import { stripLeadingPackageManagerSeparator } from "./lib/arg-utils.mjs"; import { buildGauntletPrebuildEnv, collectGatewayCpuObservations, @@ -34,6 +35,7 @@ const COMMAND_OUTPUT_MAX_BUFFER_BYTES = 16 * 1024 * 1024; const ANSI_PATTERN = new RegExp(String.raw`\u001B\[[0-9;]*m`, "gu"); export function parseArgs(argv) { + const args = stripLeadingPackageManagerSeparator(argv); const options = { repoRoot: process.cwd(), outputDir: path.join( @@ -68,10 +70,10 @@ export function parseArgs(argv) { }; const envIds = normalizeCsv(process.env.OPENCLAW_PLUGIN_GATEWAY_GAUNTLET_IDS); options.pluginIds.push(...envIds); - parseArgv: for (let index = 0; index < argv.length; index += 1) { - const arg = argv[index]; + parseArgv: for (let index = 0; index < args.length; index += 1) { + const arg = args[index]; const readValue = () => { - const value = argv[index + 1]; + const value = args[index + 1]; if (!value) { throw new Error(`Missing value for ${arg}`); } diff --git a/scripts/e2e/parallels/linux-smoke.ts b/scripts/e2e/parallels/linux-smoke.ts index aea5d50735b..5823c69859e 100755 --- a/scripts/e2e/parallels/linux-smoke.ts +++ b/scripts/e2e/parallels/linux-smoke.ts @@ -152,61 +152,62 @@ Options: } export function parseArgs(argv: string[]): LinuxOptions { + const args = stripLeadingPackageManagerSeparator(argv); const options = defaultOptions(); - parseArgv: for (let i = 0; i < argv.length; i++) { - const arg = argv[i]; + parseArgv: for (let i = 0; i < args.length; i++) { + const arg = args[i]; switch (arg) { case "--": break parseArgv; case "--vm": - options.vmName = ensureValue(argv, i, arg); + options.vmName = ensureValue(args, i, arg); options.vmNameExplicit = true; i++; break; case "--snapshot-hint": - options.snapshotHint = ensureValue(argv, i, arg); + options.snapshotHint = ensureValue(args, i, arg); i++; break; case "--mode": - options.mode = parseMode(ensureValue(argv, i, arg)); + options.mode = parseMode(ensureValue(args, i, arg)); i++; break; case "--provider": - options.provider = parseProvider(ensureValue(argv, i, arg)); + options.provider = parseProvider(ensureValue(args, i, arg)); i++; break; case "--model": - options.modelId = ensureValue(argv, i, arg); + options.modelId = ensureValue(args, i, arg); i++; break; case "--api-key-env": case "--openai-api-key-env": - options.apiKeyEnv = ensureValue(argv, i, arg); + options.apiKeyEnv = ensureValue(args, i, arg); i++; break; case "--install-url": - options.installUrl = ensureValue(argv, i, arg); + options.installUrl = ensureValue(args, i, arg); i++; break; case "--host-port": - options.hostPort = Number(ensureValue(argv, i, arg)); + options.hostPort = Number(ensureValue(args, i, arg)); options.hostPortExplicit = true; i++; break; case "--host-ip": - options.hostIp = ensureValue(argv, i, arg); + options.hostIp = ensureValue(args, i, arg); i++; break; case "--latest-version": - options.latestVersion = ensureValue(argv, i, arg); + options.latestVersion = ensureValue(args, i, arg); i++; break; case "--install-version": - options.installVersion = ensureValue(argv, i, arg); + options.installVersion = ensureValue(args, i, arg); i++; break; case "--target-package-spec": - options.targetPackageSpec = ensureValue(argv, i, arg); + options.targetPackageSpec = ensureValue(args, i, arg); i++; break; case "--keep-server": @@ -226,6 +227,10 @@ export function parseArgs(argv: string[]): LinuxOptions { return options; } +function stripLeadingPackageManagerSeparator(argv: string[]): string[] { + return argv[0] === "--" ? argv.slice(1) : argv; +} + class LinuxSmoke extends SmokeRunController { private auth: ProviderAuth; private disableBonjour = parseBoolEnv(process.env.OPENCLAW_PARALLELS_LINUX_DISABLE_BONJOUR); diff --git a/scripts/e2e/parallels/macos-smoke.ts b/scripts/e2e/parallels/macos-smoke.ts index 76e2cb4c27c..c6ade7f78dd 100755 --- a/scripts/e2e/parallels/macos-smoke.ts +++ b/scripts/e2e/parallels/macos-smoke.ts @@ -155,60 +155,61 @@ Options: } export function parseArgs(argv: string[]): MacosOptions { + const args = stripLeadingPackageManagerSeparator(argv); const options = defaultOptions(); - parseArgv: for (let i = 0; i < argv.length; i++) { - const arg = argv[i]; + parseArgv: for (let i = 0; i < args.length; i++) { + const arg = args[i]; switch (arg) { case "--": break parseArgv; case "--vm": - options.vmName = ensureValue(argv, i, arg); + options.vmName = ensureValue(args, i, arg); i++; break; case "--snapshot-hint": - options.snapshotHint = ensureValue(argv, i, arg); + options.snapshotHint = ensureValue(args, i, arg); i++; break; case "--mode": - options.mode = parseMode(ensureValue(argv, i, arg)); + options.mode = parseMode(ensureValue(args, i, arg)); i++; break; case "--provider": - options.provider = parseProvider(ensureValue(argv, i, arg)); + options.provider = parseProvider(ensureValue(args, i, arg)); i++; break; case "--model": - options.modelId = ensureValue(argv, i, arg); + options.modelId = ensureValue(args, i, arg); i++; break; case "--api-key-env": case "--openai-api-key-env": - options.apiKeyEnv = ensureValue(argv, i, arg); + options.apiKeyEnv = ensureValue(args, i, arg); i++; break; case "--install-url": - options.installUrl = ensureValue(argv, i, arg); + options.installUrl = ensureValue(args, i, arg); i++; break; case "--host-port": - options.hostPort = parsePositiveInt(ensureValue(argv, i, arg), arg); + options.hostPort = parsePositiveInt(ensureValue(args, i, arg), arg); options.hostPortExplicit = true; i++; break; case "--host-ip": - options.hostIp = ensureValue(argv, i, arg); + options.hostIp = ensureValue(args, i, arg); i++; break; case "--latest-version": - options.latestVersion = ensureValue(argv, i, arg); + options.latestVersion = ensureValue(args, i, arg); i++; break; case "--install-version": - options.installVersion = ensureValue(argv, i, arg); + options.installVersion = ensureValue(args, i, arg); i++; break; case "--target-package-spec": - options.targetPackageSpec = ensureValue(argv, i, arg); + options.targetPackageSpec = ensureValue(args, i, arg); i++; break; case "--skip-latest-ref-check": @@ -218,15 +219,15 @@ export function parseArgs(argv: string[]): MacosOptions { options.keepServer = true; break; case "--discord-token-env": - options.discordTokenEnv = ensureValue(argv, i, arg); + options.discordTokenEnv = ensureValue(args, i, arg); i++; break; case "--discord-guild-id": - options.discordGuildId = ensureValue(argv, i, arg); + options.discordGuildId = ensureValue(args, i, arg); i++; break; case "--discord-channel-id": - options.discordChannelId = ensureValue(argv, i, arg); + options.discordChannelId = ensureValue(args, i, arg); i++; break; case "--json": @@ -243,6 +244,10 @@ export function parseArgs(argv: string[]): MacosOptions { return options; } +function stripLeadingPackageManagerSeparator(argv: string[]): string[] { + return argv[0] === "--" ? argv.slice(1) : argv; +} + class MacosSmoke { private agentTimeoutSeconds: number; private auth: ProviderAuth; diff --git a/scripts/e2e/parallels/npm-update-smoke.ts b/scripts/e2e/parallels/npm-update-smoke.ts index 753f165e7f4..c2fa116df51 100755 --- a/scripts/e2e/parallels/npm-update-smoke.ts +++ b/scripts/e2e/parallels/npm-update-smoke.ts @@ -128,6 +128,7 @@ Options: } export function parseArgs(argv: string[]): NpmUpdateOptions { + const args = stripLeadingPackageManagerSeparator(argv); const options: NpmUpdateOptions = { apiKeyEnv: undefined, betaValidation: undefined, @@ -139,25 +140,25 @@ export function parseArgs(argv: string[]): NpmUpdateOptions { provider: "openai", updateTarget: "", }; - parseArgv: for (let i = 0; i < argv.length; i++) { - const arg = argv[i]; + parseArgv: for (let i = 0; i < args.length; i++) { + const arg = args[i]; switch (arg) { case "--": break parseArgv; case "--package-spec": - options.packageSpec = ensureValue(argv, i, arg); + options.packageSpec = ensureValue(args, i, arg); i++; break; case "--update-target": - options.updateTarget = ensureValue(argv, i, arg); + options.updateTarget = ensureValue(args, i, arg); i++; break; case "--fresh-target": - options.freshTargetSpec = ensureValue(argv, i, arg); + options.freshTargetSpec = ensureValue(args, i, arg); i++; break; case "--beta-validation": { - const next = argv[i + 1]; + const next = args[i + 1]; if (next && !next.startsWith("-")) { options.betaValidation = next; i++; @@ -168,24 +169,24 @@ export function parseArgs(argv: string[]): NpmUpdateOptions { } case "--platform": case "--only": - options.platforms = parsePlatformList(ensureValue(argv, i, arg)); + options.platforms = parsePlatformList(ensureValue(args, i, arg)); i++; break; case "--provider": - options.provider = parseProvider(ensureValue(argv, i, arg)); + options.provider = parseProvider(ensureValue(args, i, arg)); i++; break; case "--model": - options.modelId = ensureValue(argv, i, arg); + options.modelId = ensureValue(args, i, arg); i++; break; case "--host-ip": - options.hostIp = ensureValue(argv, i, arg); + options.hostIp = ensureValue(args, i, arg); i++; break; case "--api-key-env": case "--openai-api-key-env": - options.apiKeyEnv = ensureValue(argv, i, arg); + options.apiKeyEnv = ensureValue(args, i, arg); i++; break; case "--json": @@ -202,6 +203,10 @@ export function parseArgs(argv: string[]): NpmUpdateOptions { return options; } +function stripLeadingPackageManagerSeparator(argv: string[]): string[] { + return argv[0] === "--" ? argv.slice(1) : argv; +} + function platformRecord(value: T): Record { return { linux: value, macos: value, windows: value }; } diff --git a/scripts/e2e/parallels/windows-smoke.ts b/scripts/e2e/parallels/windows-smoke.ts index fd0c326bf57..6c2cb676cf2 100755 --- a/scripts/e2e/parallels/windows-smoke.ts +++ b/scripts/e2e/parallels/windows-smoke.ts @@ -137,6 +137,7 @@ Options: } export function parseArgs(argv: string[]): WindowsOptions { + const args = stripLeadingPackageManagerSeparator(argv); const options = defaultOptions(); const valueHandlers: Record void> = { "--api-key-env": (value) => { @@ -194,14 +195,14 @@ export function parseArgs(argv: string[]): WindowsOptions { options.upgradeFromPackedMain = true; }, }; - for (let i = 0; i < argv.length; i++) { - const arg = argv[i]; + for (let i = 0; i < args.length; i++) { + const arg = args[i]; if (arg === "--") { break; } const valueHandler = valueHandlers[arg]; if (valueHandler) { - valueHandler(ensureValue(argv, i, arg)); + valueHandler(ensureValue(args, i, arg)); i++; continue; } @@ -219,6 +220,10 @@ export function parseArgs(argv: string[]): WindowsOptions { return options; } +function stripLeadingPackageManagerSeparator(argv: string[]): string[] { + return argv[0] === "--" ? argv.slice(1) : argv; +} + class WindowsSmoke extends SmokeRunController { private auth: ProviderAuth; private artifact: PackageArtifact | null = null; diff --git a/scripts/lib/arg-utils.mjs b/scripts/lib/arg-utils.mjs index c1cda61ed91..652c279775d 100644 --- a/scripts/lib/arg-utils.mjs +++ b/scripts/lib/arg-utils.mjs @@ -20,6 +20,10 @@ export function readFlagValue(args, name) { return undefined; } +export function stripLeadingPackageManagerSeparator(argv) { + return argv[0] === "--" ? argv.slice(1) : argv; +} + function isMissingStringFlagValue(value, options = {}) { if (!value) { return true; diff --git a/scripts/profile-extension-memory.mjs b/scripts/profile-extension-memory.mjs index 2c45129701b..5151e2acb0e 100644 --- a/scripts/profile-extension-memory.mjs +++ b/scripts/profile-extension-memory.mjs @@ -6,6 +6,7 @@ import os from "node:os"; import path from "node:path"; import { pathToFileURL } from "node:url"; import { ensureExtensionMemoryBuild } from "./ensure-extension-memory-build.mjs"; +import { stripLeadingPackageManagerSeparator } from "./lib/arg-utils.mjs"; import { formatErrorMessage } from "./lib/error-format.mjs"; const DEFAULT_CONCURRENCY = 6; @@ -53,6 +54,7 @@ function parsePositiveInt(raw, flagName) { } export function parseArgs(argv) { + const args = stripLeadingPackageManagerSeparator(argv); const options = { extensions: [], concurrency: DEFAULT_CONCURRENCY, @@ -63,14 +65,14 @@ export function parseArgs(argv) { skipCombined: false, }; - parseArgv: for (let index = 0; index < argv.length; index += 1) { - const arg = argv[index]; + parseArgv: for (let index = 0; index < args.length; index += 1) { + const arg = args[index]; switch (arg) { case "--": break parseArgv; case "--extension": case "-e": { - const next = argv[index + 1]; + const next = args[index + 1]; if (!next) { throw new Error(`${arg} requires a value`); } @@ -79,23 +81,23 @@ export function parseArgs(argv) { break; } case "--concurrency": - options.concurrency = parsePositiveInt(argv[index + 1], arg); + options.concurrency = parsePositiveInt(args[index + 1], arg); index += 1; break; case "--timeout-ms": - options.timeoutMs = parsePositiveInt(argv[index + 1], arg); + options.timeoutMs = parsePositiveInt(args[index + 1], arg); index += 1; break; case "--combined-timeout-ms": - options.combinedTimeoutMs = parsePositiveInt(argv[index + 1], arg); + options.combinedTimeoutMs = parsePositiveInt(args[index + 1], arg); index += 1; break; case "--top": - options.top = parsePositiveInt(argv[index + 1], arg); + options.top = parsePositiveInt(args[index + 1], arg); index += 1; break; case "--json": { - const next = argv[index + 1]; + const next = args[index + 1]; if (!next) { throw new Error(`${arg} requires a value`); } diff --git a/scripts/release-beta-smoke.ts b/scripts/release-beta-smoke.ts index 0d6a369af1a..cb80b5c42c3 100644 --- a/scripts/release-beta-smoke.ts +++ b/scripts/release-beta-smoke.ts @@ -50,6 +50,7 @@ Options: } export function parseArgs(argv: string[]): Options { + const args = stripLeadingPackageManagerSeparator(argv); const options: Options = { beta: "beta", model: "openai/gpt-5.4", @@ -59,25 +60,25 @@ export function parseArgs(argv: string[]): Options { skipParallels: false, skipTelegram: false, }; - parseArgv: for (let i = 0; i < argv.length; i++) { - const arg = argv[i]; + parseArgv: for (let i = 0; i < args.length; i++) { + const arg = args[i]; switch (arg) { case "--": break parseArgv; case "--beta": - options.beta = requireValue(argv, ++i, arg); + options.beta = requireValue(args, ++i, arg); break; case "--model": - options.model = requireValue(argv, ++i, arg); + options.model = requireValue(args, ++i, arg); break; case "--provider-mode": - options.providerMode = requireValue(argv, ++i, arg); + options.providerMode = requireValue(args, ++i, arg); break; case "--ref": - options.ref = requireValue(argv, ++i, arg); + options.ref = requireValue(args, ++i, arg); break; case "--repo": - options.repo = requireValue(argv, ++i, arg); + options.repo = requireValue(args, ++i, arg); break; case "--skip-parallels": options.skipParallels = true; @@ -99,6 +100,10 @@ export function parseArgs(argv: string[]): Options { return options; } +function stripLeadingPackageManagerSeparator(argv: string[]): string[] { + return argv[0] === "--" ? argv.slice(1) : argv; +} + function requireValue(argv: string[], index: number, flag: string): string { const value = argv[index]; if (!value || value.startsWith("-")) { diff --git a/scripts/release-candidate-checklist.mjs b/scripts/release-candidate-checklist.mjs index 1b06651575b..9103a93b76a 100644 --- a/scripts/release-candidate-checklist.mjs +++ b/scripts/release-candidate-checklist.mjs @@ -3,6 +3,7 @@ import { spawnSync } from "node:child_process"; import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { basename, join } from "node:path"; import { fileURLToPath } from "node:url"; +import { stripLeadingPackageManagerSeparator } from "./lib/arg-utils.mjs"; const DEFAULT_REPO = "openclaw/openclaw"; const DEFAULT_PROVIDER = "openai"; @@ -49,6 +50,7 @@ function requireValue(argv, index, flag) { } export function parseArgs(argv) { + const args = stripLeadingPackageManagerSeparator(argv); const options = { repo: DEFAULT_REPO, provider: DEFAULT_PROVIDER, @@ -68,25 +70,25 @@ export function parseArgs(argv) { npmPreflightRunId: "", outputDir: "", }; - parseArgv: for (let index = 0; index < argv.length; index += 1) { - const arg = argv[index]; + parseArgv: for (let index = 0; index < args.length; index += 1) { + const arg = args[index]; switch (arg) { case "--": break parseArgv; case "--tag": - options.tag = requireValue(argv, ++index, arg); + options.tag = requireValue(args, ++index, arg); break; case "--workflow-ref": - options.workflowRef = requireValue(argv, ++index, arg); + options.workflowRef = requireValue(args, ++index, arg); break; case "--repo": - options.repo = requireValue(argv, ++index, arg); + options.repo = requireValue(args, ++index, arg); break; case "--full-release-run": - options.fullReleaseRunId = requireValue(argv, ++index, arg); + options.fullReleaseRunId = requireValue(args, ++index, arg); break; case "--npm-preflight-run": - options.npmPreflightRunId = requireValue(argv, ++index, arg); + options.npmPreflightRunId = requireValue(args, ++index, arg); break; case "--skip-dispatch": options.skipDispatch = true; @@ -101,28 +103,28 @@ export function parseArgs(argv) { options.skipTelegram = true; break; case "--telegram-provider-mode": - options.telegramProviderMode = requireValue(argv, ++index, arg); + options.telegramProviderMode = requireValue(args, ++index, arg); break; case "--provider": - options.provider = requireValue(argv, ++index, arg); + options.provider = requireValue(args, ++index, arg); break; case "--mode": - options.mode = requireValue(argv, ++index, arg); + options.mode = requireValue(args, ++index, arg); break; case "--release-profile": - options.releaseProfile = requireValue(argv, ++index, arg); + options.releaseProfile = requireValue(args, ++index, arg); break; case "--npm-dist-tag": - options.npmDistTag = requireValue(argv, ++index, arg); + options.npmDistTag = requireValue(args, ++index, arg); break; case "--plugin-publish-scope": - options.pluginPublishScope = requireValue(argv, ++index, arg); + options.pluginPublishScope = requireValue(args, ++index, arg); break; case "--plugins": - options.plugins = requireValue(argv, ++index, arg); + options.plugins = requireValue(args, ++index, arg); break; case "--output-dir": - options.outputDir = requireValue(argv, ++index, arg); + options.outputDir = requireValue(args, ++index, arg); break; case "-h": case "--help": diff --git a/test/scripts/check-gateway-cpu-scenarios.test.ts b/test/scripts/check-gateway-cpu-scenarios.test.ts index 671fac5645c..78a919fad22 100644 --- a/test/scripts/check-gateway-cpu-scenarios.test.ts +++ b/test/scripts/check-gateway-cpu-scenarios.test.ts @@ -26,6 +26,26 @@ describe("gateway CPU scenario guard", () => { ).toThrow("--skip-startup and --skip-qa cannot be used together"); }); + it("accepts package-manager argument separators before script options", () => { + expect( + testing.parseArgs([ + "--", + "--output-dir", + makeTempRoot(), + "--startup-case", + "default", + "--qa-scenario", + "channel-chat-baseline", + "--runs", + "2", + ]), + ).toMatchObject({ + qaScenarios: ["channel-chat-baseline"], + runs: 2, + startupCases: ["default"], + }); + }); + it("rejects non-decimal numeric options", () => { expect(() => testing.parseArgs(["--output-dir", makeTempRoot(), "--runs", "1e3"]), diff --git a/test/scripts/parallels-smoke-model.test.ts b/test/scripts/parallels-smoke-model.test.ts index 21150f65335..119b49cae7c 100644 --- a/test/scripts/parallels-smoke-model.test.ts +++ b/test/scripts/parallels-smoke-model.test.ts @@ -131,13 +131,18 @@ describe("Parallels smoke model selection", () => { } }); - it("stops parsing smoke options after the argument terminator", () => { + it("accepts leading package-manager separators and still honors later terminators", () => { + expect(parseLinuxSmokeArgs(["--", "--mode", "upgrade"]).mode).toBe("upgrade"); expect(parseLinuxSmokeArgs(["--mode", "fresh", "--", "--mode", "upgrade"]).mode).toBe( "fresh", ); + expect(parseMacosSmokeArgs(["--", "--mode", "upgrade"]).mode).toBe("upgrade"); expect(parseMacosSmokeArgs(["--mode", "fresh", "--", "--mode", "upgrade"]).mode).toBe( "fresh", ); + expect( + parseNpmUpdateSmokeArgs(["--", "--package-spec", "openclaw@2026.5.1"]).packageSpec, + ).toBe("openclaw@2026.5.1"); expect( parseNpmUpdateSmokeArgs([ "--package-spec", @@ -148,6 +153,9 @@ describe("Parallels smoke model selection", () => { ]).packageSpec, ).toBe("openclaw@2026.5.1"); expect(parseWindowsSmokeArgs(["--", "--upgrade-from-packed-main"]).upgradeFromPackedMain).toBe( + true, + ); + expect(parseWindowsSmokeArgs(["--mode", "fresh", "--", "--upgrade-from-packed-main"]).upgradeFromPackedMain).toBe( false, ); }); diff --git a/test/scripts/plugin-gateway-gauntlet.test.ts b/test/scripts/plugin-gateway-gauntlet.test.ts index dae9ca5aa8c..2f45146dd29 100644 --- a/test/scripts/plugin-gateway-gauntlet.test.ts +++ b/test/scripts/plugin-gateway-gauntlet.test.ts @@ -47,6 +47,24 @@ describe("plugin gateway gauntlet helpers", () => { }); }); + it("accepts package-manager argument separators before script options", () => { + expect( + parseArgs([ + "--", + "--plugin", + "telegram", + "--limit", + "3", + "--qa-scenario", + "channel-chat-baseline", + ]), + ).toMatchObject({ + limit: 3, + pluginIds: ["telegram"], + qaScenarios: ["channel-chat-baseline"], + }); + }); + it("discovers bundled plugin manifests into lifecycle matrix rows", async () => { await writeManifest( "alpha", diff --git a/test/scripts/profile-extension-memory.test.ts b/test/scripts/profile-extension-memory.test.ts index 4b04042cda5..54686782c9c 100644 --- a/test/scripts/profile-extension-memory.test.ts +++ b/test/scripts/profile-extension-memory.test.ts @@ -30,6 +30,13 @@ describe("scripts/profile-extension-memory", () => { }); }); + it("accepts package-manager argument separators before script options", () => { + expect(parseArgs(["--", "--extension", "discord", "--skip-combined"])).toMatchObject({ + extensions: ["discord"], + skipCombined: true, + }); + }); + it("rejects loose numeric flags before scanning built plugin artifacts", () => { const cases = [ ["--concurrency", "2abc"], diff --git a/test/scripts/release-beta-smoke.test.ts b/test/scripts/release-beta-smoke.test.ts index d2da833296b..2efdf79466f 100644 --- a/test/scripts/release-beta-smoke.test.ts +++ b/test/scripts/release-beta-smoke.test.ts @@ -25,6 +25,13 @@ describe("release-beta-smoke", () => { }); }); + it("accepts package-manager argument separators before script options", () => { + expect(parseArgs(["--", "--beta", "beta-a", "--skip-parallels"])).toMatchObject({ + beta: "beta-a", + skipParallels: true, + }); + }); + it("parses workflow run urls when gh includes them in dispatch output", () => { expect( parseWorkflowRunIdFromOutput( diff --git a/test/scripts/release-candidate-checklist.test.ts b/test/scripts/release-candidate-checklist.test.ts index a985e589d4a..163e126fc85 100644 --- a/test/scripts/release-candidate-checklist.test.ts +++ b/test/scripts/release-candidate-checklist.test.ts @@ -30,6 +30,23 @@ describe("release candidate checklist", () => { expect(options.pluginPublishScope).toBe("all-publishable"); }); + it("accepts package-manager argument separators before script options", () => { + const options = parseArgs([ + "--", + "--tag", + "v2026.5.14-beta.3", + "--full-release-run", + "111", + "--npm-preflight-run", + "222", + "--skip-dispatch", + "--skip-parallels", + ]); + + expect(options.tag).toBe("v2026.5.14-beta.3"); + expect(options.skipParallels).toBe(true); + }); + it("builds the gated release publish command from green evidence inputs", () => { const options = { ...parseArgs([