diff --git a/src/cli/browser-cli-state.cookies-storage.ts b/src/cli/browser-cli-state.cookies-storage.ts index d71cb9a04..c3b03404f 100644 --- a/src/cli/browser-cli-state.cookies-storage.ts +++ b/src/cli/browser-cli-state.cookies-storage.ts @@ -4,6 +4,17 @@ import { defaultRuntime } from "../runtime.js"; import { callBrowserRequest, type BrowserParentOpts } from "./browser-cli-shared.js"; import { inheritOptionFromParent } from "./command-options.js"; +function resolveUrl(opts: { url?: string }, command: Command): string | undefined { + if (typeof opts.url === "string" && opts.url.trim()) { + return opts.url.trim(); + } + const inherited = inheritOptionFromParent(command, "url"); + if (typeof inherited === "string" && inherited.trim()) { + return inherited.trim(); + } + return undefined; +} + function resolveTargetId(rawTargetId: unknown, command: Command): string | undefined { const local = typeof rawTargetId === "string" ? rawTargetId.trim() : ""; if (local) { @@ -58,12 +69,18 @@ export function registerBrowserCookiesAndStorageCommands( .description("Set a cookie (requires --url or domain+path)") .argument("", "Cookie name") .argument("", "Cookie value") - .requiredOption("--url ", "Cookie URL scope (recommended)") + .option("--url ", "Cookie URL scope (recommended)") .option("--target-id ", "CDP target id (or unique prefix)") .action(async (name: string, value: string, opts, cmd) => { const parent = parentOpts(cmd); const profile = parent?.browserProfile; const targetId = resolveTargetId(opts.targetId, cmd); + const url = resolveUrl(opts, cmd); + if (!url) { + defaultRuntime.error(danger("Missing required --url option for cookies set")); + defaultRuntime.exit(1); + return; + } try { const result = await callBrowserRequest( parent, @@ -73,7 +90,7 @@ export function registerBrowserCookiesAndStorageCommands( query: profile ? { profile } : undefined, body: { targetId, - cookie: { name, value, url: opts.url }, + cookie: { name, value, url }, }, }, { timeoutMs: 20000 }, diff --git a/src/cli/browser-cli-state.option-collisions.test.ts b/src/cli/browser-cli-state.option-collisions.test.ts index 7284a2de0..45ec5c6a5 100644 --- a/src/cli/browser-cli-state.option-collisions.test.ts +++ b/src/cli/browser-cli-state.option-collisions.test.ts @@ -26,12 +26,15 @@ vi.mock("../runtime.js", () => ({ })); describe("browser state option collisions", () => { - const createBrowserProgram = () => { + const createBrowserProgram = ({ withGatewayUrl = false } = {}) => { const program = new Command(); const browser = program .command("browser") .option("--browser-profile ", "Browser profile") .option("--json", "Output JSON", false); + if (withGatewayUrl) { + browser.option("--url ", "Gateway WebSocket URL"); + } const parentOpts = (cmd: Command) => cmd.parent?.opts?.() as BrowserParentOpts; registerBrowserStateCommands(browser, parentOpts); return program; @@ -79,6 +82,30 @@ describe("browser state option collisions", () => { expect((request as { body?: { targetId?: string } }).body?.targetId).toBe("tab-1"); }); + it("resolves --url via parent when addGatewayClientOptions captures it", async () => { + const program = createBrowserProgram({ withGatewayUrl: true }); + await program.parseAsync( + ["browser", "--url", "ws://gw", "cookies", "set", "session", "abc", "--url", "https://example.com"], + { from: "user" }, + ); + const call = mocks.callBrowserRequest.mock.calls.at(-1); + expect(call).toBeDefined(); + const request = call![1] as { body?: { cookie?: { url?: string } } }; + expect(request.body?.cookie?.url).toBe("https://example.com"); + }); + + it("inherits --url from parent when subcommand does not provide it", async () => { + const program = createBrowserProgram({ withGatewayUrl: true }); + await program.parseAsync( + ["browser", "--url", "https://inherited.example.com", "cookies", "set", "session", "abc"], + { from: "user" }, + ); + const call = mocks.callBrowserRequest.mock.calls.at(-1); + expect(call).toBeDefined(); + const request = call![1] as { body?: { cookie?: { url?: string } } }; + expect(request.body?.cookie?.url).toBe("https://inherited.example.com"); + }); + it("accepts legacy parent `--json` by parsing payload via positional headers fallback", async () => { const request = (await runBrowserCommandAndGetRequest([ "set",