diff --git a/package-lock.json b/package-lock.json index 0bb0b815..27ad924c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.2.0", "license": "Apache-2.0", "dependencies": { - "patchright-core": "1.64.1" + "patchright-core": "1.64.2" }, "bin": { "patchright-cli": "bin/patchright-cli.js" @@ -17,7 +17,7 @@ "devDependencies": { "@types/node": "^25.2.1", "esbuild": "^0.25.12", - "patchright": "1.64.1", + "patchright": "1.64.2", "tsx": "^4.23.13", "typescript": "^5.9.3" }, @@ -520,13 +520,13 @@ } }, "node_modules/patchright": { - "version": "1.64.1", - "resolved": "https://registry.npmjs.org/patchright/-/patchright-1.64.1.tgz", - "integrity": "sha512-uZYBmH3iofTHhUj9DvNQkdFYAY2weGJtoGIEjGJ9K0kNA/ycC4p9sx3QhioEutIzJjsAzBGKbNAaNNvf7Wyw4Q==", + "version": "1.64.2", + "resolved": "https://registry.npmjs.org/patchright/-/patchright-1.64.2.tgz", + "integrity": "sha512-7bUwQelki5xWotiQUm0J6SfYlUboxtHdBgUfn7rt0+8Pwk/6A7Z5nRoewPRDg9nmiak9ECBF9rmiaBIVzljEag==", "dev": true, "license": "Apache-2.0", "dependencies": { - "patchright-core": "1.64.1" + "patchright-core": "1.64.2" }, "bin": { "patchright": "cli.js" @@ -536,9 +536,9 @@ } }, "node_modules/patchright-core": { - "version": "1.64.1", - "resolved": "https://registry.npmjs.org/patchright-core/-/patchright-core-1.64.1.tgz", - "integrity": "sha512-nUWuqaaUVd60qepdDPv+i5g257F8jywbzn7aKV8fPRur4dmkFLxYV/xus8f69gKAB7+S/zfdYe0zA3mJkO6+0w==", + "version": "1.64.2", + "resolved": "https://registry.npmjs.org/patchright-core/-/patchright-core-1.64.2.tgz", + "integrity": "sha512-j7Hsw8XThZfiGlNPGJZtX4ERLLi4cval4ihCdOScwBgWqElCT4MWQSDmV/Tuuartgy9/a8DColg4gTAtJ27nPQ==", "license": "Apache-2.0", "bin": { "patchright-core": "cli.js" diff --git a/package.json b/package.json index 666e26be..177e6845 100644 --- a/package.json +++ b/package.json @@ -46,12 +46,12 @@ "session-check": "node scripts/session-check.mjs --verbose" }, "dependencies": { - "patchright-core": "1.64.1" + "patchright-core": "1.64.2" }, "devDependencies": { "@types/node": "^25.2.1", "esbuild": "^0.25.12", - "patchright": "1.64.1", + "patchright": "1.64.2", "tsx": "^4.23.13", "typescript": "^5.9.3" } diff --git a/scripts/upstream-snapshot/client.patch b/scripts/upstream-snapshot/client.patch index d48f2ba6..348a2524 100644 --- a/scripts/upstream-snapshot/client.patch +++ b/scripts/upstream-snapshot/client.patch @@ -1,9 +1,9 @@ # Our client layer relative to the pristine upstream client (Playwright v1.64.1, -# compiled as patchright-core 1.64.1). Generated by scripts/upstream-patch.ts; +# compiled as patchright-core 1.64.2). Generated by scripts/upstream-patch.ts; # not applied anywhere: the layer is carried forward by three-way merge in scripts/roll.ts. diff --git a/vendor/cli-client/program.ts b/src/program.ts -index 246a21a..e704c10 100644 +index 246a21a..0a1806f 100644 --- a/vendor/cli-client/program.ts +++ b/src/program.ts @@ -1,5 +1,6 @@ @@ -13,7 +13,7 @@ index 246a21a..e704c10 100644 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. -@@ -14,24 +15,36 @@ +@@ -14,24 +15,37 @@ * limitations under the License. */ @@ -43,6 +43,7 @@ index 246a21a..e704c10 100644 +import { coreBundle, corePackageJSON, dashboardAppPath, serverRegistry, utilsBundle } from './core'; +import { loadUserConfig } from './config/load'; +import { applyCoreEnvInProcess, coreProcessEnv } from './daemonEnv'; ++import { assertProfileFree } from './commands/profiles'; +import { loadHelp } from './help'; +import { killAllDaemons } from './killAll'; +import { bundledSkillDir, parseSkillTarget, skillInstallDir, workspaceMarker } from './paths'; @@ -59,7 +60,7 @@ index 246a21a..e704c10 100644 type GlobalOptions = { help?: boolean; -@@ -64,18 +77,20 @@ const globalOptions: (keyof (GlobalOptions & OpenOptions & AttachOptions))[] = [ +@@ -64,18 +78,20 @@ const globalOptions: (keyof (GlobalOptions & OpenOptions & AttachOptions))[] = [ 'session', ]; @@ -84,7 +85,7 @@ index 246a21a..e704c10 100644 const argv = process.argv.slice(2); const boolean = [...help.booleanOptions, ...booleanOptions]; -@@ -95,9 +110,17 @@ export async function program(options?: { embedderVersion?: string}) { +@@ -95,9 +111,17 @@ export async function program(options?: { embedderVersion?: string}) { const commandName = args._?.[0]; if (args.version || args.v) { @@ -105,7 +106,7 @@ index 246a21a..e704c10 100644 } const command = commandName && help.commands[commandName]; -@@ -105,9 +128,9 @@ export async function program(options?: { embedderVersion?: string}) { +@@ -105,9 +129,9 @@ export async function program(options?: { embedderVersion?: string}) { if (command) { output.help(command.help); } else { @@ -117,7 +118,7 @@ index 246a21a..e704c10 100644 lines.push(help.global); output.help(lines.join('\n\n')); } -@@ -135,6 +158,7 @@ export async function program(options?: { embedderVersion?: string}) { +@@ -135,6 +159,7 @@ export async function program(options?: { embedderVersion?: string}) { const closed: string[] = []; for (const entry of entries) { await new Session(entry).stop(); @@ -125,7 +126,7 @@ index 246a21a..e704c10 100644 closed.push(entry.config.name); } output.closeAll(closed); -@@ -147,6 +171,8 @@ export async function program(options?: { embedderVersion?: string}) { +@@ -147,6 +172,8 @@ export async function program(options?: { embedderVersion?: string}) { return; } const result = await new Session(entry).deleteData(); @@ -134,7 +135,7 @@ index 246a21a..e704c10 100644 output.deleteData(sessionName, result); return; } -@@ -156,11 +182,11 @@ export async function program(options?: { embedderVersion?: string}) { +@@ -156,11 +183,11 @@ export async function program(options?: { embedderVersion?: string}) { return; } case 'open': { @@ -148,7 +149,7 @@ index 246a21a..e704c10 100644 return; } case 'attach': { -@@ -182,7 +208,7 @@ export async function program(options?: { embedderVersion?: string}) { +@@ -182,7 +209,7 @@ export async function program(options?: { embedderVersion?: string}) { output.errorAttachNoTarget(); const attachSessionName = explicitSessionName(args.session as string) ?? attachTarget ?? cdpChannel ?? extensionChannel ?? sessionName; args.session = attachSessionName; @@ -157,7 +158,7 @@ index 246a21a..e704c10 100644 const newEntry = await registry.loadEntry(clientInfo, attachSessionName); const toolText = await runInSessionOrStop(newEntry, clientInfo, { _: ['snapshot'], filename: '' }, output); output.attach(attachSessionName, pid, targetName, toolText); -@@ -191,6 +217,7 @@ export async function program(options?: { embedderVersion?: string}) { +@@ -191,6 +218,7 @@ export async function program(options?: { embedderVersion?: string}) { case 'close': { const closeEntry = registry.entry(clientInfo, sessionName); const { wasOpen } = closeEntry ? await new Session(closeEntry).stop() : { wasOpen: false }; @@ -165,7 +166,7 @@ index 246a21a..e704c10 100644 output.close(sessionName, wasOpen); return; } -@@ -199,30 +226,30 @@ export async function program(options?: { embedderVersion?: string}) { +@@ -199,30 +227,30 @@ export async function program(options?: { embedderVersion?: string}) { if (detachEntry && !detachEntry.config.attached) output.errorDetachNotAttached(sessionName); const { wasOpen } = detachEntry ? await new Session(detachEntry).stop() : { wasOpen: false }; @@ -205,7 +206,7 @@ index 246a21a..e704c10 100644 const explicit = explicitSessionName(args.session as string); if (explicit) daemonArgs.push(`--sessionName=${explicit}`); -@@ -232,7 +259,7 @@ export async function program(options?: { embedderVersion?: string}) { +@@ -232,7 +260,7 @@ export async function program(options?: { embedderVersion?: string}) { daemonArgs.push(`--host=${args.host as string}`); if (args.kill) { daemonArgs.push(`--kill`); @@ -214,7 +215,7 @@ index 246a21a..e704c10 100644 await new Promise(resolve => child.on('exit', () => resolve())); return; } -@@ -249,6 +276,7 @@ export async function program(options?: { embedderVersion?: string}) { +@@ -249,6 +277,7 @@ export async function program(options?: { embedderVersion?: string}) { const child = spawn(process.execPath, daemonArgs, { detached: !foreground, stdio: foreground ? 'inherit' : ['pipe', 'pipe', 'ignore'], @@ -222,7 +223,7 @@ index 246a21a..e704c10 100644 windowsHide: true, }); if (foreground) { -@@ -281,30 +309,109 @@ export async function program(options?: { embedderVersion?: string}) { +@@ -281,30 +310,112 @@ export async function program(options?: { embedderVersion?: string}) { return; } default: { @@ -281,6 +282,9 @@ index 246a21a..e704c10 100644 + output.warn({ key: 'config', severity: 'LOW', kind: 'notice', message }); + // Resolved before the launch so a bad --idle-timeout/--owner-pid never leaves a daemon behind. + const lifetime = resolveLifetime(args, loaded.config, process.env); ++ // Chrome runs one browser per profile; say who holds it instead of failing in the browser. ++ if (mode === 'open' && typeof args.profile === 'string' && args.profile && args.isolated !== true) ++ await assertProfileFree(registry, args.profile, cwd, { session: sessionName, daemonDir: clientInfo.daemonProfilesDir }); + + const profile = await stealth.resolveLaunchProfile({ + mode, @@ -340,7 +344,7 @@ index 246a21a..e704c10 100644 if (result.isError) process.exitCode = 1; return result.text; -@@ -323,95 +430,47 @@ async function runInSessionOrStop(entry: SessionFile, clientInfo: ClientInfo, ar +@@ -323,95 +434,47 @@ async function runInSessionOrStop(entry: SessionFile, clientInfo: ClientInfo, ar } } @@ -462,7 +466,7 @@ index 246a21a..e704c10 100644 const key = clientKey(clientInfo); for (const [workspaceKey, list] of entries) { if (!all && workspaceKey !== key) -@@ -436,6 +495,7 @@ async function collectList(registry: Registry, clientInfo: ClientInfo, all: bool +@@ -436,6 +499,7 @@ async function collectList(registry: Registry, clientInfo: ClientInfo, all: bool attached: !!config.attached, compatible: session.isCompatible(clientInfo), version: config.version, @@ -470,7 +474,7 @@ index 246a21a..e704c10 100644 }); } } -@@ -454,6 +514,8 @@ function validateFlags(args: MinimistArgs, command: { flags: Record [args] [options]\nUsage: patchright-cli -s= [args] [options]\n\nCore:\n open [url] open the browser\n attach [name] attach to a running playwright browser\n close close the browser\n detach detach from an attached browser\n goto navigate to a url\n type type text into editable element\n click [button] perform click on a web page\n dblclick [button] perform double click on a web page\n fill fill text into editable element\n drag perform drag and drop between two elements\n drop drop files or data onto an element\n hover hover over element on page\n select select an option in a dropdown\n upload upload one or multiple files\n check check a checkbox or radio button\n uncheck uncheck a checkbox or radio button\n snapshot [target] capture page snapshot to obtain element ref\n find [text] search the page snapshot for text or a regexp, returning matching nodes with surrounding context (like search snippets)\n eval [target] evaluate javascript expression on page or element\n dialog-accept [prompt] accept a dialog\n dialog-dismiss dismiss a dialog\n resize resize the browser window\n delete-data delete session data\n\nNavigation:\n go-back go back to the previous page\n go-forward go forward to the next page\n reload reload the current page\n\nKeyboard:\n press press a key on the keyboard, `a`, `arrowleft`\n keydown press a key down on the keyboard\n keyup press a key up on the keyboard\n\nMouse:\n mousemove move mouse to a given position\n mousedown [button] press mouse down\n mouseup [button] press mouse up\n mousewheel scroll mouse wheel\n\nSave as:\n screenshot [target] screenshot of the current page or element\n pdf save page as pdf\n\nTabs:\n tab-list list all tabs\n tab-new [url] create a new tab\n tab-close [index] close a browser tab\n tab-select select a browser tab\n\nStorage:\n state-load loads browser storage (authentication) state from a file\n state-save [filename] saves the current storage (authentication) state to a file\n cookie-list list all cookies (optionally filtered by domain/path)\n cookie-get get a specific cookie by name\n cookie-set set a cookie with optional flags\n cookie-delete delete a specific cookie\n cookie-clear clear all cookies\n localstorage-list list all localstorage key-value pairs\n localstorage-get get a localstorage item by key\n localstorage-set set a localstorage item\n localstorage-delete delete a localstorage item\n localstorage-clear clear all localstorage\n sessionstorage-list list all sessionstorage key-value pairs\n sessionstorage-get get a sessionstorage item by key\n sessionstorage-set set a sessionstorage item\n sessionstorage-delete delete a sessionstorage item\n sessionstorage-clear clear all sessionstorage\n\nEmulation:\n set-color-scheme emulate the light or dark color scheme\n set-reduced-motion emulate the reduced motion preference\n set-forced-colors emulate forced colors mode\n set-contrast emulate the preferred contrast\n set-media emulate the css media type\n clear-color-scheme clear color scheme emulation\n clear-reduced-motion clear reduced motion emulation\n clear-forced-colors clear forced colors emulation\n clear-contrast clear preferred contrast emulation\n clear-media clear css media type emulation\n\nNetwork:\n requests list all network requests since loading the page. each request is numbered for use with the `request` command.\n request show full details (headers, body, response) of a single network request by its number from the `requests` command.\n request-headers print only the request headers for a single network request by its number from the `requests` command.\n request-body print only the request body for a single network request by its number from the `requests` command.\n response-headers print only the response headers for a single network request by its number from the `requests` command.\n response-body print the response body for a single network request by its number from the `requests` command. textual bodies are inlined; binary bodies are saved to a file and the path is printed.\n route mock network requests matching a url pattern\n route-list list all active network routes\n unroute [pattern] remove routes matching a pattern (or all routes)\n network-state-set set the browser network state to online or offline\n\nDevTools:\n console [min-level] list console messages\n run-code [code] run playwright code snippet\n recording-start start recording user actions\n recording-stop stop recording user actions and print them as playwright code\n tracing-start start trace recording\n tracing-stop stop trace recording\n video-start [filename] start video recording\n video-stop stop video recording\n video-chapter add a chapter marker to the video recording\n video-show-actions annotate subsequent cli/mcp actions on the page with a callout that names the action and, when styled, marks the action point and highlights the target element\n video-hide-actions stop annotating actions performed on the page\n show show playwright dashboard\n pause-at <location> run the test up to a specific location and pause there\n resume resume the test execution\n step-over step over the next call in the test\n generate-locator <target> generate a playwright locator for the given element\n highlight [target] show (or with --hide, remove) a highlight overlay for an element; `--hide` without a target hides all page highlights.\n\nWebMCP:\n webmcp-list list the webmcp tools registered by the page\n webmcp-call <name> call a webmcp tool registered by the page\n\nInstall:\n install initialize workspace\n install-browser [browser] install browser\n\nShortcuts:\n url print the current page url\n title print the current page title\n text [target] print the visible text of the page, or of one element (innertext)\n wait [ms] pause for a number of milliseconds, or with --url wait until the page url matches a glob (`**/dashboard`) or an exact url\n wait-for <target> wait until an element is visible (or hidden, attached, detached)\n scroll-to <target> scroll an element into view, the way an action does before clicking it, and print where it ended up\n cookie-export [file] write the session cookies to a file (json, or netscape cookies.txt); defaults to a timestamped file in the output dir\n cookie-import <file> add cookies from a file\n\nStealth:\n env show how to run commands on this system\n batch [file] run a whole flow from a script in one invocation (one command per line, # comments; \"-\" or no file reads stdin)\n wait-for-user [seconds] hand the visible browser window to the user (password, one-time code, consent dialog) and wait until the page navigates or the time runs out (default 300s), then take a fresh snapshot\n identity show the identity of the session profile\n profile-list list the named profiles (open --profile=<name>)\n profile-delete <name> delete a named profile and the identity it carries (logins, cookies, history); refused while a session holds it\n doctor check the environment\n selftest run the detection self-test in a throwaway session\n\nBrowser sessions:\n list list browser sessions\n close-all close all browser sessions\n kill-all forcefully kill all browser sessions (for stale/zombie processes)\n\nGlobal options:\n --help [command] print help\n --json output response as JSON\n --raw output only the result value, without status and code\n --version print version", "commands": { "open": {