From bb46da654e048dc70a9756f2e69c0accc6498878 Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Wed, 27 May 2026 12:09:44 +0200 Subject: [PATCH 1/2] fix(cli): do not pass PLAYWRIGHT_CLI_SESSION as daemon --endpoint PLAYWRIGHT_CLI_SESSION is the env-var equivalent of -s/--session (the session name). It is already consumed as such in startDaemon via resolveSessionName. The leftover else-if branch additionally re-passed it as the daemon's --endpoint= argument when mode is 'attach', which made the daemon try to connect to a socket at the literal session-name path. Repro: PLAYWRIGHT_CLI_SESSION=myname playwright-cli attach --cdp=http://127.0.0.1:99999 -> Error: Daemon pid=...: connect ENOENT myname The fallback dates back to #39650, when PLAYWRIGHT_CLI_SESSION only meant "endpoint to attach to". After #39707 it gained its current session-name meaning, but the obsolete fallback survived through the --attach -> --endpoint rename (#39972) and the mode-guard refactor (#40176). Fixes https://github.com/microsoft/playwright-cli/issues/414 --- packages/playwright-core/src/tools/cli-client/session.ts | 2 -- tests/mcp/cli-cdp.spec.ts | 6 ++++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/playwright-core/src/tools/cli-client/session.ts b/packages/playwright-core/src/tools/cli-client/session.ts index 3547230b56b35..8e44d45b3ca7f 100644 --- a/packages/playwright-core/src/tools/cli-client/session.ts +++ b/packages/playwright-core/src/tools/cli-client/session.ts @@ -139,8 +139,6 @@ export class Session { args.push(`--cdp=${cliArgs.cdp}`); if (cliArgs.endpoint) args.push(`--endpoint=${cliArgs.endpoint}`); - else if (mode === 'attach' && process.env.PLAYWRIGHT_CLI_SESSION) - args.push(`--endpoint=${process.env.PLAYWRIGHT_CLI_SESSION}`); const child = spawn(process.execPath, args, { detached: true, diff --git a/tests/mcp/cli-cdp.spec.ts b/tests/mcp/cli-cdp.spec.ts index 880e358c6e05e..b7b951a919b5a 100644 --- a/tests/mcp/cli-cdp.spec.ts +++ b/tests/mcp/cli-cdp.spec.ts @@ -57,6 +57,12 @@ test('attach via cdp URL keeps the default session', async ({ cdpServer, cli, se expect(listOutput).toContain('(attached)'); }); +test('attach via cdp URL honors PLAYWRIGHT_CLI_SESSION as session name', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright-cli/issues/414' } }, async ({ cdpServer, cli }) => { + await cdpServer.start(); + const { exitCode } = await cli('attach', `--cdp=${cdpServer.endpoint}`, { env: { PLAYWRIGHT_CLI_SESSION: 'myname' } }); + expect(exitCode).toBe(0); +}); + test('detach tears down an attached session', async ({ cdpServer, cli }) => { await cdpServer.start(); From 25fa0e66aee74f19a0bd98e68f0831ef268e8507 Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Mon, 1 Jun 2026 16:13:15 +0200 Subject: [PATCH 2/2] fix(cli): enforce mutual exclusivity of attach target flags Reject combinations of [name], --cdp, --endpoint, and --extension at the CLI parsing layer, and chain them in the daemon arg builder so the spawned daemon never sees conflicting flags. --- packages/playwright-core/src/tools/cli-client/output.ts | 4 ++-- packages/playwright-core/src/tools/cli-client/program.ts | 3 ++- packages/playwright-core/src/tools/cli-client/session.ts | 8 ++++---- tests/mcp/cli-cdp.spec.ts | 6 ++++++ 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/playwright-core/src/tools/cli-client/output.ts b/packages/playwright-core/src/tools/cli-client/output.ts index 601384469c0c3..1972f1862adf6 100644 --- a/packages/playwright-core/src/tools/cli-client/output.ts +++ b/packages/playwright-core/src/tools/cli-client/output.ts @@ -105,7 +105,7 @@ export class TextOutput implements Output { } errorAttachConflict(): never { - console.error(`Error: cannot use target name with --cdp, --endpoint, or --extension`); + console.error(`Error: only one of [name], --cdp, --endpoint, or --extension can be specified`); return process.exit(1); } @@ -288,7 +288,7 @@ export class JsonOutput implements Output { } errorAttachConflict(): never { - this._emit({ isError: true, error: `cannot use target name with --cdp, --endpoint, or --extension` }); + this._emit({ isError: true, error: `only one of [name], --cdp, --endpoint, or --extension can be specified` }); return process.exit(1); } diff --git a/packages/playwright-core/src/tools/cli-client/program.ts b/packages/playwright-core/src/tools/cli-client/program.ts index 86a3db49e68e4..1d9dc5d657c2d 100644 --- a/packages/playwright-core/src/tools/cli-client/program.ts +++ b/packages/playwright-core/src/tools/cli-client/program.ts @@ -156,7 +156,8 @@ export async function program(options?: { embedderVersion?: string}) { } case 'attach': { const attachTarget = args._[1] as string | undefined; - if (attachTarget && (args.cdp || args.endpoint || args.extension)) + const targetCount = (attachTarget ? 1 : 0) + (args.cdp ? 1 : 0) + (args.endpoint ? 1 : 0) + (args.extension ? 1 : 0); + if (targetCount > 1) output.errorAttachConflict(); if (attachTarget) args.endpoint = attachTarget; diff --git a/packages/playwright-core/src/tools/cli-client/session.ts b/packages/playwright-core/src/tools/cli-client/session.ts index 8e44d45b3ca7f..e9d79783b0885 100644 --- a/packages/playwright-core/src/tools/cli-client/session.ts +++ b/packages/playwright-core/src/tools/cli-client/session.ts @@ -125,8 +125,6 @@ export class Session { ]; if (cliArgs.headed) args.push('--headed'); - if (cliArgs.extension) - args.push('--extension'); if (cliArgs.browser) args.push(`--browser=${cliArgs.browser}`); if (cliArgs.persistent) @@ -135,9 +133,11 @@ export class Session { args.push(`--profile=${cliArgs.profile}`); if (cliArgs.config) args.push(`--config=${cliArgs.config}`); - if (cliArgs.cdp) + if (cliArgs.extension) + args.push('--extension'); + else if (cliArgs.cdp) args.push(`--cdp=${cliArgs.cdp}`); - if (cliArgs.endpoint) + else if (cliArgs.endpoint) args.push(`--endpoint=${cliArgs.endpoint}`); const child = spawn(process.execPath, args, { diff --git a/tests/mcp/cli-cdp.spec.ts b/tests/mcp/cli-cdp.spec.ts index b7b951a919b5a..48c3a237a7683 100644 --- a/tests/mcp/cli-cdp.spec.ts +++ b/tests/mcp/cli-cdp.spec.ts @@ -63,6 +63,12 @@ test('attach via cdp URL honors PLAYWRIGHT_CLI_SESSION as session name', { annot expect(exitCode).toBe(0); }); +test('attach rejects combining --cdp, --endpoint, or --extension', async ({ cli }) => { + const { error, exitCode } = await cli('attach', '--cdp=chrome-dev', '--endpoint=/tmp/foo'); + expect(exitCode).toBe(1); + expect(error).toContain('only one of [name], --cdp, --endpoint, or --extension can be specified'); +}); + test('detach tears down an attached session', async ({ cdpServer, cli }) => { await cdpServer.start();