patchright-cli 0.3.0__tar.gz → 0.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {patchright_cli-0.3.0 → patchright_cli-0.3.1}/.github/workflows/publish.yml +5 -2
- {patchright_cli-0.3.0 → patchright_cli-0.3.1}/PKG-INFO +1 -1
- {patchright_cli-0.3.0 → patchright_cli-0.3.1}/pyproject.toml +1 -1
- patchright_cli-0.3.1/skills/patchright-cli/SKILL.md +453 -0
- {patchright_cli-0.3.0 → patchright_cli-0.3.1}/src/patchright_cli/__init__.py +1 -1
- {patchright_cli-0.3.0 → patchright_cli-0.3.1}/src/patchright_cli/cli.py +9 -0
- {patchright_cli-0.3.0 → patchright_cli-0.3.1}/src/patchright_cli/daemon.py +12 -6
- {patchright_cli-0.3.0 → patchright_cli-0.3.1}/uv.lock +128 -127
- patchright_cli-0.3.0/skills/patchright-cli/SKILL.md +0 -274
- {patchright_cli-0.3.0 → patchright_cli-0.3.1}/.gitignore +0 -0
- {patchright_cli-0.3.0 → patchright_cli-0.3.1}/.pre-commit-config.yaml +0 -0
- {patchright_cli-0.3.0 → patchright_cli-0.3.1}/CLAUDE.md +0 -0
- {patchright_cli-0.3.0 → patchright_cli-0.3.1}/CONTRIBUTING.md +0 -0
- {patchright_cli-0.3.0 → patchright_cli-0.3.1}/LICENSE +0 -0
- {patchright_cli-0.3.0 → patchright_cli-0.3.1}/README.md +0 -0
- {patchright_cli-0.3.0 → patchright_cli-0.3.1}/docs/installation.md +0 -0
- {patchright_cli-0.3.0 → patchright_cli-0.3.1}/patchright-cli +0 -0
- {patchright_cli-0.3.0 → patchright_cli-0.3.1}/patchright-cli.cmd +0 -0
- {patchright_cli-0.3.0 → patchright_cli-0.3.1}/src/patchright_cli/__main__.py +0 -0
- {patchright_cli-0.3.0 → patchright_cli-0.3.1}/src/patchright_cli/snapshot.py +0 -0
|
@@ -8,6 +8,9 @@ on:
|
|
|
8
8
|
permissions:
|
|
9
9
|
contents: write
|
|
10
10
|
|
|
11
|
+
env:
|
|
12
|
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
13
|
+
|
|
11
14
|
jobs:
|
|
12
15
|
build:
|
|
13
16
|
runs-on: ubuntu-latest
|
|
@@ -15,7 +18,7 @@ jobs:
|
|
|
15
18
|
- uses: actions/checkout@v4
|
|
16
19
|
|
|
17
20
|
- name: Install uv
|
|
18
|
-
uses: astral-sh/setup-uv@
|
|
21
|
+
uses: astral-sh/setup-uv@v5
|
|
19
22
|
|
|
20
23
|
- name: Set up Python
|
|
21
24
|
run: uv python install 3.12
|
|
@@ -43,7 +46,7 @@ jobs:
|
|
|
43
46
|
path: dist/
|
|
44
47
|
|
|
45
48
|
- name: Install uv
|
|
46
|
-
uses: astral-sh/setup-uv@
|
|
49
|
+
uses: astral-sh/setup-uv@v5
|
|
47
50
|
|
|
48
51
|
- name: Publish to PyPI
|
|
49
52
|
run: uv publish
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: patchright-cli
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Anti-detect browser automation CLI using Patchright (undetected Playwright fork)
|
|
5
5
|
Project-URL: Homepage, https://github.com/AhaiMk01/patchright-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/AhaiMk01/patchright-cli
|
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: patchright-cli
|
|
3
|
+
description: Anti-detect browser automation using Patchright (undetected Playwright fork). Use when you need to interact with websites that block regular Playwright/Chrome DevTools, such as Akamai/Cloudflare-protected sites. Provides the same command interface as playwright-cli but bypasses bot detection. Use this skill whenever the user asks to automate a browser, scrape a website, fill forms, log into sites, take screenshots, or do anything involving Chrome automation — especially if the target site has bot protection like Cloudflare or Akamai.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Anti-Detect Browser Automation with patchright-cli
|
|
7
|
+
|
|
8
|
+
patchright-cli drives a real Chrome browser that passes bot detection (Cloudflare, Akamai, etc.). It works as a CLI: you issue commands, get back page state. This is the tool to reach for whenever regular Playwright or Chrome DevTools gets blocked.
|
|
9
|
+
|
|
10
|
+
## How it works: the snapshot-driven loop
|
|
11
|
+
|
|
12
|
+
Every interaction follows the same cycle:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
open browser → snapshot → read refs → interact → snapshot again → repeat
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
1. **Open** a browser session — this launches a real Chrome window
|
|
19
|
+
2. **Snapshot** the page — this scans the DOM and assigns refs (`e1`, `e2`, ...) to interactive elements
|
|
20
|
+
3. **Read the snapshot** — it's a YAML file showing the element tree with refs
|
|
21
|
+
4. **Interact** using refs — `click e5`, `fill e3 "hello"`, etc.
|
|
22
|
+
5. **Check the result** — most commands auto-snapshot, so you get updated refs
|
|
23
|
+
|
|
24
|
+
Refs are ephemeral. They change every time the page updates. If a command fails with "Could not locate element for ref", the page has changed — just run `snapshot` to get fresh refs.
|
|
25
|
+
|
|
26
|
+
## Quick start
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
patchright-cli open https://example.com # launch browser + navigate
|
|
30
|
+
patchright-cli snapshot # get element refs
|
|
31
|
+
# read the snapshot YAML to find the right ref, then:
|
|
32
|
+
patchright-cli click e5 # interact by ref
|
|
33
|
+
patchright-cli fill e3 "search query" # type into an input
|
|
34
|
+
patchright-cli press Enter # press a key
|
|
35
|
+
patchright-cli screenshot # capture the page
|
|
36
|
+
patchright-cli close # done
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
### CLI tool
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Recommended — always runs latest version
|
|
45
|
+
uvx patchright-cli <command>
|
|
46
|
+
|
|
47
|
+
# Or install globally
|
|
48
|
+
pip install patchright-cli
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Skill (for AI coding agents)
|
|
52
|
+
|
|
53
|
+
Install the skill so your agent knows how to use patchright-cli. The same command also works to update to the latest version.
|
|
54
|
+
|
|
55
|
+
**macOS / Linux (bash/zsh):**
|
|
56
|
+
|
|
57
|
+
| Agent | Install / update command |
|
|
58
|
+
|-------|--------------------------|
|
|
59
|
+
| **Claude Code** | `mkdir -p ~/.claude/skills/patchright-cli && curl -sL https://raw.githubusercontent.com/AhaiMk01/patchright-cli/main/skills/patchright-cli/SKILL.md -o ~/.claude/skills/patchright-cli/SKILL.md` |
|
|
60
|
+
| **Gemini CLI** | `mkdir -p ~/.gemini/skills/patchright-cli && curl -sL https://raw.githubusercontent.com/AhaiMk01/patchright-cli/main/skills/patchright-cli/SKILL.md -o ~/.gemini/skills/patchright-cli/SKILL.md` |
|
|
61
|
+
| **Codex CLI** | `mkdir -p ~/.codex/skills/patchright-cli && curl -sL https://raw.githubusercontent.com/AhaiMk01/patchright-cli/main/skills/patchright-cli/SKILL.md -o ~/.codex/skills/patchright-cli/SKILL.md` |
|
|
62
|
+
| **OpenCode** | `mkdir -p ~/.opencode/skills/patchright-cli && curl -sL https://raw.githubusercontent.com/AhaiMk01/patchright-cli/main/skills/patchright-cli/SKILL.md -o ~/.opencode/skills/patchright-cli/SKILL.md` |
|
|
63
|
+
|
|
64
|
+
**Windows (PowerShell):**
|
|
65
|
+
|
|
66
|
+
| Agent | Install / update command |
|
|
67
|
+
|-------|--------------------------|
|
|
68
|
+
| **Claude Code** | `New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.claude\skills\patchright-cli" | Out-Null; Invoke-WebRequest -Uri "https://raw.githubusercontent.com/AhaiMk01/patchright-cli/main/skills/patchright-cli/SKILL.md" -OutFile "$env:USERPROFILE\.claude\skills\patchright-cli\SKILL.md"` |
|
|
69
|
+
| **Gemini CLI** | `New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.gemini\skills\patchright-cli" | Out-Null; Invoke-WebRequest -Uri "https://raw.githubusercontent.com/AhaiMk01/patchright-cli/main/skills/patchright-cli/SKILL.md" -OutFile "$env:USERPROFILE\.gemini\skills\patchright-cli\SKILL.md"` |
|
|
70
|
+
| **Codex CLI** | `New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.codex\skills\patchright-cli" | Out-Null; Invoke-WebRequest -Uri "https://raw.githubusercontent.com/AhaiMk01/patchright-cli/main/skills/patchright-cli/SKILL.md" -OutFile "$env:USERPROFILE\.codex\skills\patchright-cli\SKILL.md"` |
|
|
71
|
+
| **OpenCode** | `New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.opencode\skills\patchright-cli" | Out-Null; Invoke-WebRequest -Uri "https://raw.githubusercontent.com/AhaiMk01/patchright-cli/main/skills/patchright-cli/SKILL.md" -OutFile "$env:USERPROFILE\.opencode\skills\patchright-cli\SKILL.md"` |
|
|
72
|
+
|
|
73
|
+
**IDE-based agents (Cursor / Windsurf / Aider):** Copy `SKILL.md` to `.<tool>/skills/patchright-cli/` in your project.
|
|
74
|
+
|
|
75
|
+
**Or just tell your agent:**
|
|
76
|
+
> Install patchright-cli skill from https://raw.githubusercontent.com/AhaiMk01/patchright-cli/main/skills/patchright-cli/SKILL.md
|
|
77
|
+
|
|
78
|
+
## Global options
|
|
79
|
+
|
|
80
|
+
These go before the command:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
--headless # Run headless (default: headed — headed is less detectable)
|
|
84
|
+
--persistent # Use persistent profile (keeps cookies/storage across sessions)
|
|
85
|
+
--profile=/path # Custom profile directory
|
|
86
|
+
--proxy=<url> # Proxy server (http, https, socks5)
|
|
87
|
+
-s=mysession # Named session (default: "default")
|
|
88
|
+
--port=9322 # Custom daemon port (default: 9321)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Core commands
|
|
94
|
+
|
|
95
|
+
### Browser lifecycle
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
patchright-cli open # Launch browser
|
|
99
|
+
patchright-cli open https://example.com # Launch and navigate
|
|
100
|
+
patchright-cli open --persistent # Keep cookies/storage between runs
|
|
101
|
+
patchright-cli open --headless # Headless mode
|
|
102
|
+
patchright-cli open --profile=/path/to/dir # Custom profile directory
|
|
103
|
+
patchright-cli --proxy=http://host:port open # Route traffic through HTTP proxy
|
|
104
|
+
patchright-cli --proxy=socks5://host:port open # SOCKS5 proxy
|
|
105
|
+
patchright-cli close # Close session
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Navigation
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
patchright-cli goto https://example.com
|
|
112
|
+
patchright-cli go-back
|
|
113
|
+
patchright-cli go-forward
|
|
114
|
+
patchright-cli reload
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Snapshots
|
|
118
|
+
|
|
119
|
+
Snapshots are how you discover what's on the page. They produce a YAML tree of interactive elements, each tagged with a ref like `e1`, `e5`, `e12`.
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
patchright-cli snapshot # Full page snapshot
|
|
123
|
+
patchright-cli snapshot e3 # Subtree of a specific element
|
|
124
|
+
patchright-cli snapshot --filename=snap.yml # Save to custom path
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
After each state-changing command (click, fill, goto, etc.), a snapshot is automatically taken and returned. You don't need to manually snapshot after every action.
|
|
128
|
+
|
|
129
|
+
### Clicking and interacting
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
patchright-cli click e3 # Left-click
|
|
133
|
+
patchright-cli click e3 right # Right-click
|
|
134
|
+
patchright-cli click e3 --modifiers=Alt,Shift # Click with modifier keys
|
|
135
|
+
patchright-cli dblclick e7 # Double-click
|
|
136
|
+
patchright-cli dblclick e7 --modifiers=Shift
|
|
137
|
+
patchright-cli hover e4
|
|
138
|
+
patchright-cli drag e2 e8 # Drag element to target
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Form input
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
patchright-cli fill e5 "user@example.com" # Clear field and type value
|
|
145
|
+
patchright-cli fill e5 "query" --submit # Fill and press Enter
|
|
146
|
+
patchright-cli type "search query" # Type via keyboard (no target element)
|
|
147
|
+
patchright-cli type "query" --submit # Type and press Enter
|
|
148
|
+
patchright-cli select e9 "option-value" # Select dropdown option
|
|
149
|
+
patchright-cli check e12 # Check checkbox/radio
|
|
150
|
+
patchright-cli uncheck e12
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
`fill` targets a specific input by ref and replaces its contents. `type` types via the keyboard into whatever is focused.
|
|
154
|
+
|
|
155
|
+
### Screenshots
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
patchright-cli screenshot # Page screenshot
|
|
159
|
+
patchright-cli screenshot e3 # Element screenshot
|
|
160
|
+
patchright-cli screenshot --filename=page.png # Custom filename
|
|
161
|
+
patchright-cli screenshot --full-page # Full scrollable page
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Screenshots save to `.patchright-cli/` in the current directory.
|
|
165
|
+
|
|
166
|
+
### Keyboard and mouse
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# Keyboard
|
|
170
|
+
patchright-cli press Enter # Single keypress
|
|
171
|
+
patchright-cli press ArrowDown
|
|
172
|
+
patchright-cli keydown Shift # Hold key down
|
|
173
|
+
patchright-cli keyup Shift # Release key
|
|
174
|
+
|
|
175
|
+
# Mouse
|
|
176
|
+
patchright-cli mousemove 150 300 # Move to coordinates
|
|
177
|
+
patchright-cli mousedown # Left button down
|
|
178
|
+
patchright-cli mousedown right # Right button down
|
|
179
|
+
patchright-cli mouseup
|
|
180
|
+
patchright-cli mousewheel 0 100 # Scroll (dx, dy)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Tabs
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
patchright-cli tab-list # List open tabs
|
|
187
|
+
patchright-cli tab-new https://example.com # Open new tab
|
|
188
|
+
patchright-cli tab-select 0 # Switch to tab by index
|
|
189
|
+
patchright-cli tab-close # Close current tab
|
|
190
|
+
patchright-cli tab-close 2 # Close tab by index
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## JavaScript execution
|
|
196
|
+
|
|
197
|
+
**Always use `--file` or stdin for `eval` and `run-code`.** Inline JS breaks because quotes get mangled through shell layers (bash -> uvx -> python).
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
# WRONG — nested quotes break
|
|
201
|
+
patchright-cli eval "document.querySelector('a').href"
|
|
202
|
+
|
|
203
|
+
# RIGHT — write to a temp file
|
|
204
|
+
cat > /tmp/check.js << 'JSEOF'
|
|
205
|
+
JSON.stringify({title: document.title, links: document.querySelectorAll('a').length})
|
|
206
|
+
JSEOF
|
|
207
|
+
patchright-cli eval --file=/tmp/check.js
|
|
208
|
+
|
|
209
|
+
# RIGHT — pipe via stdin (simple expressions)
|
|
210
|
+
echo 'document.title' | patchright-cli eval
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
`eval` returns the expression result. `run-code` wraps your code in `async () => { ... }` so you can use `return` and `await`.
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
# run-code example
|
|
217
|
+
cat > /tmp/scroll.js << 'JSEOF'
|
|
218
|
+
window.scrollTo(0, document.body.scrollHeight);
|
|
219
|
+
return document.body.scrollHeight;
|
|
220
|
+
JSEOF
|
|
221
|
+
patchright-cli run-code --file=/tmp/scroll.js
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Common patterns
|
|
227
|
+
|
|
228
|
+
### Using a proxy
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
# HTTP proxy
|
|
232
|
+
patchright-cli --proxy=http://host:port open https://example.com
|
|
233
|
+
|
|
234
|
+
# SOCKS5 proxy
|
|
235
|
+
patchright-cli --proxy=socks5://host:port open https://example.com
|
|
236
|
+
|
|
237
|
+
# Authenticated proxy
|
|
238
|
+
patchright-cli --proxy=http://user:pass@host:port open https://example.com
|
|
239
|
+
|
|
240
|
+
# Combine with persistent profile and named session
|
|
241
|
+
patchright-cli --proxy=http://host:port -s=proxied open https://example.com --persistent
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
The proxy is set at browser launch and applies to all traffic in that session. Note: `--proxy` is a global option and must come before the command.
|
|
245
|
+
|
|
246
|
+
### Login flow
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
patchright-cli open https://example.com/login --persistent
|
|
250
|
+
patchright-cli snapshot
|
|
251
|
+
# Find the username/password fields and login button in the snapshot
|
|
252
|
+
patchright-cli fill e3 "user@example.com"
|
|
253
|
+
patchright-cli fill e5 "password123"
|
|
254
|
+
patchright-cli click e8 # Click login button
|
|
255
|
+
# Wait a moment for redirect, then snapshot to verify
|
|
256
|
+
patchright-cli snapshot
|
|
257
|
+
# With --persistent, cookies survive across sessions
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Extracting data from a page
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
patchright-cli open https://example.com/data
|
|
264
|
+
patchright-cli snapshot # Get the page structure
|
|
265
|
+
# Use eval to extract structured data
|
|
266
|
+
cat > /tmp/extract.js << 'JSEOF'
|
|
267
|
+
JSON.stringify(
|
|
268
|
+
[...document.querySelectorAll('table tr')].map(row =>
|
|
269
|
+
[...row.cells].map(cell => cell.textContent.trim())
|
|
270
|
+
)
|
|
271
|
+
)
|
|
272
|
+
JSEOF
|
|
273
|
+
patchright-cli eval --file=/tmp/extract.js
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Waiting for dynamic content
|
|
277
|
+
|
|
278
|
+
The page may not be fully loaded when you snapshot. If elements are missing, try:
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
# Option 1: Just snapshot again after a short pause
|
|
282
|
+
# (most commands auto-wait for navigation to settle)
|
|
283
|
+
patchright-cli snapshot
|
|
284
|
+
|
|
285
|
+
# Option 2: Use run-code to wait for a specific condition
|
|
286
|
+
cat > /tmp/wait.js << 'JSEOF'
|
|
287
|
+
for (let i = 0; i < 30; i++) {
|
|
288
|
+
if (document.querySelector('.results-loaded')) return true;
|
|
289
|
+
await new Promise(r => setTimeout(r, 500));
|
|
290
|
+
}
|
|
291
|
+
return false;
|
|
292
|
+
JSEOF
|
|
293
|
+
patchright-cli run-code --file=/tmp/wait.js
|
|
294
|
+
patchright-cli snapshot
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Multi-page workflow with persistent session
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
# Use a named persistent session to maintain state
|
|
301
|
+
patchright-cli -s=myproject open https://app.example.com --persistent
|
|
302
|
+
patchright-cli -s=myproject snapshot
|
|
303
|
+
patchright-cli -s=myproject click e5
|
|
304
|
+
# ... do work ...
|
|
305
|
+
patchright-cli -s=myproject close
|
|
306
|
+
# Later, reopen — cookies and localStorage are preserved
|
|
307
|
+
patchright-cli -s=myproject open https://app.example.com --persistent
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Handling dialogs (alert/confirm/prompt)
|
|
311
|
+
|
|
312
|
+
Dialogs must be pre-armed *before* the action that triggers them:
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
patchright-cli dialog-accept # Accept next dialog
|
|
316
|
+
patchright-cli click e5 # This click triggers the dialog
|
|
317
|
+
# Or:
|
|
318
|
+
patchright-cli dialog-accept "OK" # Accept with text input
|
|
319
|
+
patchright-cli dialog-dismiss # Dismiss instead
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## Storage and state
|
|
325
|
+
|
|
326
|
+
### Cookies
|
|
327
|
+
|
|
328
|
+
```bash
|
|
329
|
+
patchright-cli cookie-list # All cookies
|
|
330
|
+
patchright-cli cookie-list --domain=example.com # Filter by domain
|
|
331
|
+
patchright-cli cookie-list --path=/api # Filter by path
|
|
332
|
+
patchright-cli cookie-get session_id # Get specific cookie
|
|
333
|
+
patchright-cli cookie-set session_id abc123 # Set cookie (current page URL)
|
|
334
|
+
patchright-cli cookie-set token xyz --domain=example.com --path=/ --httpOnly --secure --sameSite=Lax --expires=1735689600
|
|
335
|
+
patchright-cli cookie-delete session_id
|
|
336
|
+
patchright-cli cookie-clear # Clear all
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### localStorage / sessionStorage
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
# localStorage
|
|
343
|
+
patchright-cli localstorage-list
|
|
344
|
+
patchright-cli localstorage-get theme
|
|
345
|
+
patchright-cli localstorage-set theme dark
|
|
346
|
+
patchright-cli localstorage-delete theme
|
|
347
|
+
patchright-cli localstorage-clear
|
|
348
|
+
|
|
349
|
+
# sessionStorage (same pattern)
|
|
350
|
+
patchright-cli sessionstorage-list
|
|
351
|
+
patchright-cli sessionstorage-get step
|
|
352
|
+
patchright-cli sessionstorage-set step 3
|
|
353
|
+
patchright-cli sessionstorage-delete step
|
|
354
|
+
patchright-cli sessionstorage-clear
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Save/load full state (cookies + storage)
|
|
358
|
+
|
|
359
|
+
```bash
|
|
360
|
+
patchright-cli state-save auth.json # Save cookies + localStorage
|
|
361
|
+
patchright-cli state-load auth.json # Restore (navigate to matching origin first for localStorage)
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## Network tools
|
|
367
|
+
|
|
368
|
+
### Request mocking
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
patchright-cli route "**/*.jpg" --status=404 # Block images
|
|
372
|
+
patchright-cli route "https://api.example.com/**" --body='{"mock": true}' # Mock API
|
|
373
|
+
patchright-cli route "**/*" --content-type=application/json --body='{"ok":true}'
|
|
374
|
+
patchright-cli route "**/*" --header=X-Custom:value
|
|
375
|
+
patchright-cli route "**/*" --remove-header=Content-Type
|
|
376
|
+
patchright-cli route-list # List active routes
|
|
377
|
+
patchright-cli unroute "**/*.jpg" # Remove specific route
|
|
378
|
+
patchright-cli unroute # Remove all routes
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### Network state
|
|
382
|
+
|
|
383
|
+
```bash
|
|
384
|
+
patchright-cli network-state-set offline # Simulate offline
|
|
385
|
+
patchright-cli network-state-set online # Restore connectivity
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### DevTools inspection
|
|
389
|
+
|
|
390
|
+
```bash
|
|
391
|
+
patchright-cli console # Show console messages (last 50)
|
|
392
|
+
patchright-cli console warning # Filter by level
|
|
393
|
+
patchright-cli console --clear # Clear buffer after printing
|
|
394
|
+
|
|
395
|
+
patchright-cli network # Show requests (excludes static assets)
|
|
396
|
+
patchright-cli network --static # Include images, fonts, scripts, etc.
|
|
397
|
+
patchright-cli network --clear # Clear log after printing
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
## Recording and capture
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
# Tracing (Playwright trace format)
|
|
406
|
+
patchright-cli tracing-start
|
|
407
|
+
patchright-cli tracing-stop # Saves .zip to .patchright-cli/
|
|
408
|
+
|
|
409
|
+
# Video (CDP screencast, requires ffmpeg for .webm output)
|
|
410
|
+
patchright-cli video-start
|
|
411
|
+
patchright-cli video-stop # Save as .webm (or frames if no ffmpeg)
|
|
412
|
+
patchright-cli video-stop --filename=rec.webm
|
|
413
|
+
|
|
414
|
+
# PDF
|
|
415
|
+
patchright-cli pdf # Save page as PDF
|
|
416
|
+
patchright-cli pdf --filename=page.pdf
|
|
417
|
+
|
|
418
|
+
# File upload
|
|
419
|
+
patchright-cli upload ./document.pdf # Upload to first file input
|
|
420
|
+
patchright-cli upload ./photo.jpg e5 # Upload to specific input
|
|
421
|
+
|
|
422
|
+
# Viewport resize
|
|
423
|
+
patchright-cli resize 1920 1080
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
## Session management
|
|
429
|
+
|
|
430
|
+
```bash
|
|
431
|
+
# Named sessions allow multiple independent browsers
|
|
432
|
+
patchright-cli -s=session1 open https://site-a.com --persistent
|
|
433
|
+
patchright-cli -s=session2 open https://site-b.com --persistent
|
|
434
|
+
|
|
435
|
+
patchright-cli list # List all active sessions
|
|
436
|
+
patchright-cli close-all # Gracefully close all
|
|
437
|
+
patchright-cli kill-all # Force-kill all + stop daemon
|
|
438
|
+
|
|
439
|
+
# Delete persistent profile data
|
|
440
|
+
patchright-cli delete-data # Delete default session's profile
|
|
441
|
+
patchright-cli -s=mysession delete-data # Delete named session's profile
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
---
|
|
445
|
+
|
|
446
|
+
## Anti-detect notes
|
|
447
|
+
|
|
448
|
+
- Uses real Chrome (not Chromium) — this is what makes it undetectable
|
|
449
|
+
- Patchright patches `navigator.webdriver` and other detection vectors automatically
|
|
450
|
+
- Headed by default — headless mode is more detectable, use only when necessary
|
|
451
|
+
- No custom user-agent or headers — preserves Chrome's natural fingerprint
|
|
452
|
+
- Persistent profiles maintain realistic browser history and cookies
|
|
453
|
+
- The daemon architecture means Chrome stays running between commands, behaving like a real user's browser
|
|
@@ -154,6 +154,7 @@ def _print_help():
|
|
|
154
154
|
click.echo(" --headless Run headless (default: headed)")
|
|
155
155
|
click.echo(" --persistent Use persistent profile")
|
|
156
156
|
click.echo(" --profile=<path> Custom profile directory")
|
|
157
|
+
click.echo(" --proxy=<url> Proxy server (e.g. http://host:port, socks5://host:port)")
|
|
157
158
|
click.echo(" -s=<name> Named session (default: 'default')")
|
|
158
159
|
click.echo(" --port=<n> Daemon port (default: 9321)")
|
|
159
160
|
click.echo(" --version Show version")
|
|
@@ -231,6 +232,7 @@ def main():
|
|
|
231
232
|
headless = False
|
|
232
233
|
persistent = False
|
|
233
234
|
profile = None
|
|
235
|
+
proxy = None
|
|
234
236
|
session_name = "default"
|
|
235
237
|
port = DEFAULT_PORT
|
|
236
238
|
|
|
@@ -248,6 +250,11 @@ def main():
|
|
|
248
250
|
elif arg == "--profile" and i + 1 < len(argv):
|
|
249
251
|
i += 1
|
|
250
252
|
profile = argv[i]
|
|
253
|
+
elif arg.startswith("--proxy="):
|
|
254
|
+
proxy = arg.split("=", 1)[1]
|
|
255
|
+
elif arg == "--proxy" and i + 1 < len(argv):
|
|
256
|
+
i += 1
|
|
257
|
+
proxy = argv[i]
|
|
251
258
|
elif arg.startswith("-s="):
|
|
252
259
|
session_name = arg.split("=", 1)[1]
|
|
253
260
|
elif arg == "-s" and i + 1 < len(argv):
|
|
@@ -330,6 +337,8 @@ def main():
|
|
|
330
337
|
options["persistent"] = True
|
|
331
338
|
if profile:
|
|
332
339
|
options["profile"] = profile
|
|
340
|
+
if proxy:
|
|
341
|
+
options["proxy"] = proxy
|
|
333
342
|
|
|
334
343
|
# Ensure daemon is running (auto-start for 'open', require for others)
|
|
335
344
|
try:
|
|
@@ -137,6 +137,7 @@ class DaemonState:
|
|
|
137
137
|
headless: bool | None = None,
|
|
138
138
|
persistent: bool = True,
|
|
139
139
|
profile: str | None = None,
|
|
140
|
+
proxy: str | None = None,
|
|
140
141
|
url: str | None = None,
|
|
141
142
|
) -> Session:
|
|
142
143
|
if name in self.sessions:
|
|
@@ -151,14 +152,18 @@ class DaemonState:
|
|
|
151
152
|
profile_dir = profile or str(Path.home() / ".patchright-cli" / "profiles" / name)
|
|
152
153
|
Path(profile_dir).mkdir(parents=True, exist_ok=True)
|
|
153
154
|
|
|
155
|
+
launch_kwargs = {
|
|
156
|
+
"channel": "chrome",
|
|
157
|
+
"headless": use_headless,
|
|
158
|
+
"no_viewport": True,
|
|
159
|
+
"args": ["--disable-blink-features=AutomationControlled"],
|
|
160
|
+
}
|
|
161
|
+
if proxy:
|
|
162
|
+
launch_kwargs["proxy"] = {"server": proxy}
|
|
163
|
+
|
|
154
164
|
context = await self.playwright.chromium.launch_persistent_context(
|
|
155
165
|
profile_dir,
|
|
156
|
-
|
|
157
|
-
headless=use_headless,
|
|
158
|
-
no_viewport=True,
|
|
159
|
-
args=[
|
|
160
|
-
"--disable-blink-features=AutomationControlled",
|
|
161
|
-
],
|
|
166
|
+
**launch_kwargs,
|
|
162
167
|
)
|
|
163
168
|
|
|
164
169
|
pages = context.pages or []
|
|
@@ -263,6 +268,7 @@ async def handle_command(state: DaemonState, msg: dict) -> dict:
|
|
|
263
268
|
headless=headless,
|
|
264
269
|
persistent=options.get("persistent", True),
|
|
265
270
|
profile=options.get("profile"),
|
|
271
|
+
proxy=options.get("proxy"),
|
|
266
272
|
url=url,
|
|
267
273
|
)
|
|
268
274
|
return await _page_info(session, cwd)
|