chromectl 0.1.0__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.
- chromectl-0.1.0/LICENSE +21 -0
- chromectl-0.1.0/MANIFEST.in +3 -0
- chromectl-0.1.0/PKG-INFO +248 -0
- chromectl-0.1.0/README.md +210 -0
- chromectl-0.1.0/chromectl/__init__.py +2 -0
- chromectl-0.1.0/chromectl/cli.py +2301 -0
- chromectl-0.1.0/chromectl/protocol.json +34907 -0
- chromectl-0.1.0/chromectl.egg-info/PKG-INFO +248 -0
- chromectl-0.1.0/chromectl.egg-info/SOURCES.txt +13 -0
- chromectl-0.1.0/chromectl.egg-info/dependency_links.txt +1 -0
- chromectl-0.1.0/chromectl.egg-info/entry_points.txt +2 -0
- chromectl-0.1.0/chromectl.egg-info/requires.txt +11 -0
- chromectl-0.1.0/chromectl.egg-info/top_level.txt +1 -0
- chromectl-0.1.0/pyproject.toml +55 -0
- chromectl-0.1.0/setup.cfg +4 -0
chromectl-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Enes Bayram
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
chromectl-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: chromectl
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A friendly CLI for driving Chrome over the DevTools Protocol — for agents and humans.
|
|
5
|
+
Author: Enes Bayram
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/0xenesbayram/chromectl
|
|
8
|
+
Project-URL: Repository, https://github.com/0xenesbayram/chromectl
|
|
9
|
+
Project-URL: Issues, https://github.com/0xenesbayram/chromectl/issues
|
|
10
|
+
Keywords: chrome,chromium,cdp,devtools,automation,agent,cli,playwright,lighthouse,headless
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
|
|
21
|
+
Classifier: Topic :: Software Development :: Testing
|
|
22
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
23
|
+
Classifier: Topic :: Utilities
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: websocket-client>=1.7
|
|
28
|
+
Requires-Dist: rich>=13
|
|
29
|
+
Requires-Dist: playwright>=1.40
|
|
30
|
+
Requires-Dist: readability-lxml>=0.8
|
|
31
|
+
Requires-Dist: markdownify>=0.11
|
|
32
|
+
Requires-Dist: lxml_html_clean>=0.1
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: build; extra == "dev"
|
|
35
|
+
Requires-Dist: twine; extra == "dev"
|
|
36
|
+
Requires-Dist: pipx; extra == "dev"
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
|
|
39
|
+
# chromectl — a friendly CLI for the Chrome DevTools Protocol
|
|
40
|
+
|
|
41
|
+
Drive a Chrome/Chromium instance from the shell over its remote debugging port.
|
|
42
|
+
Everything is the Chrome DevTools Protocol (CDP) under the hood; this wraps it in
|
|
43
|
+
ergonomic subcommands with nice output. Not just network — JS execution, console
|
|
44
|
+
logs/errors, screenshots, PDFs, cookies, SEO audits, page-reading, form flows,
|
|
45
|
+
Core Web Vitals / Lighthouse, and a raw escape hatch.
|
|
46
|
+
|
|
47
|
+
## Install
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pipx install chromectl # from PyPI
|
|
51
|
+
pipx upgrade chromectl # pull later releases
|
|
52
|
+
npm i -g lighthouse # optional — only for `lighthouse`
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Bleeding edge, straight from the repo:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pipx install git+https://github.com/0xenesbayram/chromectl.git
|
|
59
|
+
# or, from a local clone: pipx install .
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
pipx puts `chromectl` on your PATH in an isolated env and pulls all Python deps
|
|
63
|
+
(`websocket-client`, `rich`, `playwright`, `readability-lxml`, `markdownify`).
|
|
64
|
+
**No `playwright install` needed** — the interaction commands *attach* to your Chrome
|
|
65
|
+
over CDP rather than launching their own browser. Playwright and the read/markdown
|
|
66
|
+
libs are imported lazily, so the core commands stay fast.
|
|
67
|
+
|
|
68
|
+
## 1. Launch Chrome
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
chromectl start # headless, port 9222, PERSISTENT profile
|
|
72
|
+
chromectl start --headful --port 9223 # visible window, custom port
|
|
73
|
+
chromectl start --profile ~/.cache/chromectl # custom profile dir
|
|
74
|
+
chromectl start --ephemeral # throwaway profile in /tmp (no persistence)
|
|
75
|
+
chromectl start --copy-profile # copy your REAL Chrome profile (logins!) then launch
|
|
76
|
+
chromectl start --from-profile /path/to/profile # copy from a specific profile dir
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`--copy-profile` auto-detects your default Chrome user-data-dir (per-OS), copies it into
|
|
80
|
+
`--profile` (skipping caches), and launches from the copy — so you debug **with your real
|
|
81
|
+
logins** without touching/using the original profile. Close Chrome using that profile first
|
|
82
|
+
so its files aren't mid-write. Security: the copy holds your live cookies/sessions — anyone
|
|
83
|
+
who reaches the debug port can act as you, so keep it local and delete it when done.
|
|
84
|
+
|
|
85
|
+
### Multiple instances
|
|
86
|
+
Run and manage several browsers at once, each on its own port, addressed by name:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
chromectl start --name work # instance on 9222
|
|
90
|
+
chromectl start --name scratch --auto-port # a second, on the next free port
|
|
91
|
+
chromectl instances # list them + up/down status (--json, --prune)
|
|
92
|
+
|
|
93
|
+
chromectl -i scratch open https://example.com # target by name (or --port 9223)
|
|
94
|
+
chromectl -i work read --json
|
|
95
|
+
|
|
96
|
+
chromectl stop scratch # stop one by name/port
|
|
97
|
+
chromectl stop --all # stop every managed instance
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`-i NAME` (or `--port N`) selects which browser every command talks to. The registry lives
|
|
101
|
+
at `~/.chromectl/instances.json`.
|
|
102
|
+
|
|
103
|
+
Notes:
|
|
104
|
+
- **Profiles persist by default.** Each instance gets a stable dir at
|
|
105
|
+
`~/.chromectl/profiles/<name>` that's **reused every time you start that name**, so
|
|
106
|
+
cookies/logins survive restarts. Use `--profile PATH` for a custom location,
|
|
107
|
+
`--ephemeral` for a throwaway `/tmp` profile, or `stop <name> --purge` to delete it.
|
|
108
|
+
- A **non-default profile is mandatory** since Chrome 136 — the real default profile
|
|
109
|
+
refuses the debug port (anti-cookie-theft). `start`'s profile is non-default; to reuse
|
|
110
|
+
your logins, copy your real one first: `cp -r ~/.config/google-chrome /tmp/prof` then
|
|
111
|
+
`chromectl start --profile /tmp/prof`.
|
|
112
|
+
- `start` sets `--remote-allow-origins=*` so clients (incl. Playwright) can connect.
|
|
113
|
+
- Anyone who can reach the port has **full, unauthenticated control** of that browser
|
|
114
|
+
(read cookies/sessions, run JS, read traffic). Keep it on localhost; never forward it.
|
|
115
|
+
|
|
116
|
+
## 2. Use it
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
chromectl list # open tabs/targets
|
|
120
|
+
chromectl version # browser + protocol info
|
|
121
|
+
chromectl open https://example.com # open a tab
|
|
122
|
+
chromectl close example # close a tab (by url/title substring or id-prefix)
|
|
123
|
+
|
|
124
|
+
chromectl goto example https://news.ycombinator.com # navigate
|
|
125
|
+
chromectl eval example "document.title" # run JS, get the value back
|
|
126
|
+
chromectl eval example "({t: document.title, links: document.links.length})"
|
|
127
|
+
chromectl html example --out page.html # dump HTML
|
|
128
|
+
chromectl text example # visible text
|
|
129
|
+
chromectl cookies example # cookie table (--json for raw)
|
|
130
|
+
chromectl screenshot example --full --out shot.png # full-page screenshot
|
|
131
|
+
chromectl pdf example --out page.pdf # print to PDF
|
|
132
|
+
|
|
133
|
+
chromectl console example # tail console.* + JS errors (Ctrl-C)
|
|
134
|
+
chromectl watch example # live one-line-per-request network tail
|
|
135
|
+
|
|
136
|
+
chromectl capture https://github.com --print 3 # Burp-style full req/resp capture
|
|
137
|
+
chromectl capture https://api.github.com/ --type xhr --har out.har # filter + HAR export
|
|
138
|
+
chromectl capture --attach example --reload # capture an existing tab from reload
|
|
139
|
+
|
|
140
|
+
chromectl seo https://example.com # on-page SEO audit (open+audit+close)
|
|
141
|
+
chromectl seo example # audit an already-loaded tab
|
|
142
|
+
|
|
143
|
+
# --- run a whole flow in ONE process (script/batch) ---
|
|
144
|
+
chromectl run steps.txt # one command per line (# comments ok)
|
|
145
|
+
chromectl run --step "open https://site/login" \
|
|
146
|
+
--step "wait --selector #user" \
|
|
147
|
+
--step "fill-form --set #user=ada --set #pass=pw --submit #go" \
|
|
148
|
+
--step "wait --url /dashboard" \
|
|
149
|
+
--step "read --json"
|
|
150
|
+
# opened tab auto-becomes the target for later steps; one connection; state persists; --keep-going to continue on error
|
|
151
|
+
|
|
152
|
+
# --- read & extract (turn pages into data) ---
|
|
153
|
+
chromectl read example # main content as clean Markdown
|
|
154
|
+
chromectl read example --json # {url,title,markdown,chars}
|
|
155
|
+
chromectl extract example --field "title=h1" --field "prices=.price[]" --field "img=img@src"
|
|
156
|
+
chromectl links example --external --json # list links (filter internal/external)
|
|
157
|
+
|
|
158
|
+
# --- reliable flows ---
|
|
159
|
+
chromectl wait example --selector "#results" --timeout 8000 # wait until it's visible
|
|
160
|
+
chromectl wait example --network-idle # or: --text "Done" / --url "/checkout" / --gone
|
|
161
|
+
chromectl fill-form login --set "#user=ada" --set "#pass=secret" --submit "#go"
|
|
162
|
+
|
|
163
|
+
# Most read-only commands accept --json for scripting/agents:
|
|
164
|
+
chromectl list --json ; chromectl cookies example --json ; chromectl seo example --json
|
|
165
|
+
|
|
166
|
+
# --- performance (Tier 3) ---
|
|
167
|
+
chromectl perf https://example.com # Core Web Vitals (LCP/CLS/INP/FCP/TTFB)
|
|
168
|
+
chromectl perf https://example.com --out trace.json # + raw trace (DevTools ▸ Performance ▸ Load)
|
|
169
|
+
chromectl perf --attach example --reload # measure an existing tab
|
|
170
|
+
chromectl lighthouse https://example.com --preset desktop # full Lighthouse audit (needs the CLI)
|
|
171
|
+
chromectl lighthouse https://example.com --categories performance,seo --out report.json
|
|
172
|
+
|
|
173
|
+
# --- robust interaction via Playwright-over-CDP (Tier 2) ---
|
|
174
|
+
chromectl snapshot example # list interactive elements + save refs
|
|
175
|
+
chromectl click example --role button --name "Sign in" # click by ARIA role + name
|
|
176
|
+
chromectl click example --ref 3 # click element #3 from last snapshot
|
|
177
|
+
chromectl click example --text "Add to cart" # click by visible text
|
|
178
|
+
chromectl fill example "ada@x.com" --selector "#email" --enter # fill + submit (auto-waits)
|
|
179
|
+
chromectl hover example --selector ".menu" # hover (reveals submenus)
|
|
180
|
+
|
|
181
|
+
# --- interaction / emulation (Tier 1) ---
|
|
182
|
+
chromectl resize example 390 844 --mobile --shot m.png # viewport + full-page screenshot
|
|
183
|
+
chromectl emulate example --color dark --shot dark.png # dark mode
|
|
184
|
+
chromectl emulate example --geo 48.85,2.35 --throttle slow-3g --hold # geo + network (held)
|
|
185
|
+
chromectl type example "hello" --selector "#search" --enter # type into a field
|
|
186
|
+
chromectl press example Enter # press key(s): Enter/Tab/ArrowDown/a…
|
|
187
|
+
chromectl upload example ./photo.png --selector "#file" # set a file <input>
|
|
188
|
+
chromectl dialog example --accept --text "Ada" # auto-answer alert/confirm/prompt
|
|
189
|
+
chromectl heap example --out heap.heapsnapshot # V8 heap snapshot (DevTools ▸ Memory)
|
|
190
|
+
|
|
191
|
+
chromectl raw browser Browser.getVersion # raw CDP command (browser target)
|
|
192
|
+
chromectl raw example Runtime.evaluate '{"expression":"1+1","returnByValue":true}'
|
|
193
|
+
chromectl proto Network # protocol lookup: a domain…
|
|
194
|
+
chromectl proto Network.getResponseBody # …a command's params/returns
|
|
195
|
+
chromectl repl example # interactive CDP prompt
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Target selection
|
|
199
|
+
Anywhere a command takes a target you can pass:
|
|
200
|
+
- an **id-prefix** (`B1B0`), a **url/title substring** (`example`, `github`),
|
|
201
|
+
- `browser` for the browser-level target (for `raw`/`repl`),
|
|
202
|
+
- or **nothing** to use the first open page.
|
|
203
|
+
|
|
204
|
+
### Emulation caveat
|
|
205
|
+
CDP overrides (`emulate`/`resize` viewport, geo, throttle, color-scheme, UA) live on
|
|
206
|
+
the **CDP connection** and revert when it closes. Since each `chromectl` command connects,
|
|
207
|
+
acts, and disconnects, use `--shot` to capture in the same session, or `--hold` to keep
|
|
208
|
+
the connection open (Ctrl-C to release) so the override persists while you do other work.
|
|
209
|
+
|
|
210
|
+
### Interaction (Tier 2) — how it works
|
|
211
|
+
`snapshot`/`click`/`fill`/`hover` use **Playwright attached to your Chrome over CDP**
|
|
212
|
+
(`connect_over_cdp`) — no separate browser is launched. You get Playwright's
|
|
213
|
+
auto-waiting and actionability checks (waits for the element to exist, be visible, and
|
|
214
|
+
be stable before acting) instead of hand-rolled timing. `snapshot` writes `.cdp-snap.json`
|
|
215
|
+
so `--ref N` works in a later, separate command (the stateless equivalent of the MCP's
|
|
216
|
+
element uids). Locate an element by any of: `--ref`, `--selector`, `--text`, or
|
|
217
|
+
`--role`+`--name`.
|
|
218
|
+
|
|
219
|
+
### Handy options
|
|
220
|
+
- `--host` / `--port` (or env `CDP_HOST` / `CDP_PORT`) — default `localhost:9222`.
|
|
221
|
+
- `capture`: `--print N`, `--out FILE`, `--har FILE`, `--type TYPE`,
|
|
222
|
+
`--no-bodies`, `--bodycap N`, `--max SECONDS`, `--quiet SECONDS`,
|
|
223
|
+
`--attach TARGET`, `--reload`.
|
|
224
|
+
|
|
225
|
+
## How it works (the workflow)
|
|
226
|
+
|
|
227
|
+
`HTTP /json/version` → open ONE WebSocket to the browser (or a per-tab socket) →
|
|
228
|
+
send CDP commands (`{id, method, params}`) and receive replies + unsolicited
|
|
229
|
+
events → for capture: `Network.enable` **before** navigating, correlate events by
|
|
230
|
+
`requestId`, fetch each body on `loadingFinished` (before Chrome evicts it), merge
|
|
231
|
+
the `*ExtraInfo` events for the real cookies/headers, and reconstruct raw HTTP.
|
|
232
|
+
|
|
233
|
+
A background thread reads frames into a queue so `call()` (request/response) and
|
|
234
|
+
the capture/console/watch loops (event streams) share one connection cleanly.
|
|
235
|
+
|
|
236
|
+
**Gotcha baked in:** `websocket-client` sends an `Origin` header by default, which
|
|
237
|
+
modern Chrome rejects on the debug port. `chromectl` sets `suppress_origin=True` so
|
|
238
|
+
connections are accepted without relaunching Chrome.
|
|
239
|
+
|
|
240
|
+
## Layout
|
|
241
|
+
- `chromectl/cli.py` — the CLI.
|
|
242
|
+
- `chromectl/protocol.json` — bundled CDP schema used by `chromectl proto`/`cheat` (falls back to live).
|
|
243
|
+
- `pyproject.toml` — packaging; `chromectl` console entry point.
|
|
244
|
+
- `AGENTS.md` — the agent-facing interface reference (auto-read by coding agents).
|
|
245
|
+
|
|
246
|
+
## For agents
|
|
247
|
+
Read `AGENTS.md` (or run `chromectl cheat --json` once) for the full command surface —
|
|
248
|
+
no need to call `--help` per command.
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# chromectl — a friendly CLI for the Chrome DevTools Protocol
|
|
2
|
+
|
|
3
|
+
Drive a Chrome/Chromium instance from the shell over its remote debugging port.
|
|
4
|
+
Everything is the Chrome DevTools Protocol (CDP) under the hood; this wraps it in
|
|
5
|
+
ergonomic subcommands with nice output. Not just network — JS execution, console
|
|
6
|
+
logs/errors, screenshots, PDFs, cookies, SEO audits, page-reading, form flows,
|
|
7
|
+
Core Web Vitals / Lighthouse, and a raw escape hatch.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pipx install chromectl # from PyPI
|
|
13
|
+
pipx upgrade chromectl # pull later releases
|
|
14
|
+
npm i -g lighthouse # optional — only for `lighthouse`
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Bleeding edge, straight from the repo:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pipx install git+https://github.com/0xenesbayram/chromectl.git
|
|
21
|
+
# or, from a local clone: pipx install .
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
pipx puts `chromectl` on your PATH in an isolated env and pulls all Python deps
|
|
25
|
+
(`websocket-client`, `rich`, `playwright`, `readability-lxml`, `markdownify`).
|
|
26
|
+
**No `playwright install` needed** — the interaction commands *attach* to your Chrome
|
|
27
|
+
over CDP rather than launching their own browser. Playwright and the read/markdown
|
|
28
|
+
libs are imported lazily, so the core commands stay fast.
|
|
29
|
+
|
|
30
|
+
## 1. Launch Chrome
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
chromectl start # headless, port 9222, PERSISTENT profile
|
|
34
|
+
chromectl start --headful --port 9223 # visible window, custom port
|
|
35
|
+
chromectl start --profile ~/.cache/chromectl # custom profile dir
|
|
36
|
+
chromectl start --ephemeral # throwaway profile in /tmp (no persistence)
|
|
37
|
+
chromectl start --copy-profile # copy your REAL Chrome profile (logins!) then launch
|
|
38
|
+
chromectl start --from-profile /path/to/profile # copy from a specific profile dir
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`--copy-profile` auto-detects your default Chrome user-data-dir (per-OS), copies it into
|
|
42
|
+
`--profile` (skipping caches), and launches from the copy — so you debug **with your real
|
|
43
|
+
logins** without touching/using the original profile. Close Chrome using that profile first
|
|
44
|
+
so its files aren't mid-write. Security: the copy holds your live cookies/sessions — anyone
|
|
45
|
+
who reaches the debug port can act as you, so keep it local and delete it when done.
|
|
46
|
+
|
|
47
|
+
### Multiple instances
|
|
48
|
+
Run and manage several browsers at once, each on its own port, addressed by name:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
chromectl start --name work # instance on 9222
|
|
52
|
+
chromectl start --name scratch --auto-port # a second, on the next free port
|
|
53
|
+
chromectl instances # list them + up/down status (--json, --prune)
|
|
54
|
+
|
|
55
|
+
chromectl -i scratch open https://example.com # target by name (or --port 9223)
|
|
56
|
+
chromectl -i work read --json
|
|
57
|
+
|
|
58
|
+
chromectl stop scratch # stop one by name/port
|
|
59
|
+
chromectl stop --all # stop every managed instance
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`-i NAME` (or `--port N`) selects which browser every command talks to. The registry lives
|
|
63
|
+
at `~/.chromectl/instances.json`.
|
|
64
|
+
|
|
65
|
+
Notes:
|
|
66
|
+
- **Profiles persist by default.** Each instance gets a stable dir at
|
|
67
|
+
`~/.chromectl/profiles/<name>` that's **reused every time you start that name**, so
|
|
68
|
+
cookies/logins survive restarts. Use `--profile PATH` for a custom location,
|
|
69
|
+
`--ephemeral` for a throwaway `/tmp` profile, or `stop <name> --purge` to delete it.
|
|
70
|
+
- A **non-default profile is mandatory** since Chrome 136 — the real default profile
|
|
71
|
+
refuses the debug port (anti-cookie-theft). `start`'s profile is non-default; to reuse
|
|
72
|
+
your logins, copy your real one first: `cp -r ~/.config/google-chrome /tmp/prof` then
|
|
73
|
+
`chromectl start --profile /tmp/prof`.
|
|
74
|
+
- `start` sets `--remote-allow-origins=*` so clients (incl. Playwright) can connect.
|
|
75
|
+
- Anyone who can reach the port has **full, unauthenticated control** of that browser
|
|
76
|
+
(read cookies/sessions, run JS, read traffic). Keep it on localhost; never forward it.
|
|
77
|
+
|
|
78
|
+
## 2. Use it
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
chromectl list # open tabs/targets
|
|
82
|
+
chromectl version # browser + protocol info
|
|
83
|
+
chromectl open https://example.com # open a tab
|
|
84
|
+
chromectl close example # close a tab (by url/title substring or id-prefix)
|
|
85
|
+
|
|
86
|
+
chromectl goto example https://news.ycombinator.com # navigate
|
|
87
|
+
chromectl eval example "document.title" # run JS, get the value back
|
|
88
|
+
chromectl eval example "({t: document.title, links: document.links.length})"
|
|
89
|
+
chromectl html example --out page.html # dump HTML
|
|
90
|
+
chromectl text example # visible text
|
|
91
|
+
chromectl cookies example # cookie table (--json for raw)
|
|
92
|
+
chromectl screenshot example --full --out shot.png # full-page screenshot
|
|
93
|
+
chromectl pdf example --out page.pdf # print to PDF
|
|
94
|
+
|
|
95
|
+
chromectl console example # tail console.* + JS errors (Ctrl-C)
|
|
96
|
+
chromectl watch example # live one-line-per-request network tail
|
|
97
|
+
|
|
98
|
+
chromectl capture https://github.com --print 3 # Burp-style full req/resp capture
|
|
99
|
+
chromectl capture https://api.github.com/ --type xhr --har out.har # filter + HAR export
|
|
100
|
+
chromectl capture --attach example --reload # capture an existing tab from reload
|
|
101
|
+
|
|
102
|
+
chromectl seo https://example.com # on-page SEO audit (open+audit+close)
|
|
103
|
+
chromectl seo example # audit an already-loaded tab
|
|
104
|
+
|
|
105
|
+
# --- run a whole flow in ONE process (script/batch) ---
|
|
106
|
+
chromectl run steps.txt # one command per line (# comments ok)
|
|
107
|
+
chromectl run --step "open https://site/login" \
|
|
108
|
+
--step "wait --selector #user" \
|
|
109
|
+
--step "fill-form --set #user=ada --set #pass=pw --submit #go" \
|
|
110
|
+
--step "wait --url /dashboard" \
|
|
111
|
+
--step "read --json"
|
|
112
|
+
# opened tab auto-becomes the target for later steps; one connection; state persists; --keep-going to continue on error
|
|
113
|
+
|
|
114
|
+
# --- read & extract (turn pages into data) ---
|
|
115
|
+
chromectl read example # main content as clean Markdown
|
|
116
|
+
chromectl read example --json # {url,title,markdown,chars}
|
|
117
|
+
chromectl extract example --field "title=h1" --field "prices=.price[]" --field "img=img@src"
|
|
118
|
+
chromectl links example --external --json # list links (filter internal/external)
|
|
119
|
+
|
|
120
|
+
# --- reliable flows ---
|
|
121
|
+
chromectl wait example --selector "#results" --timeout 8000 # wait until it's visible
|
|
122
|
+
chromectl wait example --network-idle # or: --text "Done" / --url "/checkout" / --gone
|
|
123
|
+
chromectl fill-form login --set "#user=ada" --set "#pass=secret" --submit "#go"
|
|
124
|
+
|
|
125
|
+
# Most read-only commands accept --json for scripting/agents:
|
|
126
|
+
chromectl list --json ; chromectl cookies example --json ; chromectl seo example --json
|
|
127
|
+
|
|
128
|
+
# --- performance (Tier 3) ---
|
|
129
|
+
chromectl perf https://example.com # Core Web Vitals (LCP/CLS/INP/FCP/TTFB)
|
|
130
|
+
chromectl perf https://example.com --out trace.json # + raw trace (DevTools ▸ Performance ▸ Load)
|
|
131
|
+
chromectl perf --attach example --reload # measure an existing tab
|
|
132
|
+
chromectl lighthouse https://example.com --preset desktop # full Lighthouse audit (needs the CLI)
|
|
133
|
+
chromectl lighthouse https://example.com --categories performance,seo --out report.json
|
|
134
|
+
|
|
135
|
+
# --- robust interaction via Playwright-over-CDP (Tier 2) ---
|
|
136
|
+
chromectl snapshot example # list interactive elements + save refs
|
|
137
|
+
chromectl click example --role button --name "Sign in" # click by ARIA role + name
|
|
138
|
+
chromectl click example --ref 3 # click element #3 from last snapshot
|
|
139
|
+
chromectl click example --text "Add to cart" # click by visible text
|
|
140
|
+
chromectl fill example "ada@x.com" --selector "#email" --enter # fill + submit (auto-waits)
|
|
141
|
+
chromectl hover example --selector ".menu" # hover (reveals submenus)
|
|
142
|
+
|
|
143
|
+
# --- interaction / emulation (Tier 1) ---
|
|
144
|
+
chromectl resize example 390 844 --mobile --shot m.png # viewport + full-page screenshot
|
|
145
|
+
chromectl emulate example --color dark --shot dark.png # dark mode
|
|
146
|
+
chromectl emulate example --geo 48.85,2.35 --throttle slow-3g --hold # geo + network (held)
|
|
147
|
+
chromectl type example "hello" --selector "#search" --enter # type into a field
|
|
148
|
+
chromectl press example Enter # press key(s): Enter/Tab/ArrowDown/a…
|
|
149
|
+
chromectl upload example ./photo.png --selector "#file" # set a file <input>
|
|
150
|
+
chromectl dialog example --accept --text "Ada" # auto-answer alert/confirm/prompt
|
|
151
|
+
chromectl heap example --out heap.heapsnapshot # V8 heap snapshot (DevTools ▸ Memory)
|
|
152
|
+
|
|
153
|
+
chromectl raw browser Browser.getVersion # raw CDP command (browser target)
|
|
154
|
+
chromectl raw example Runtime.evaluate '{"expression":"1+1","returnByValue":true}'
|
|
155
|
+
chromectl proto Network # protocol lookup: a domain…
|
|
156
|
+
chromectl proto Network.getResponseBody # …a command's params/returns
|
|
157
|
+
chromectl repl example # interactive CDP prompt
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Target selection
|
|
161
|
+
Anywhere a command takes a target you can pass:
|
|
162
|
+
- an **id-prefix** (`B1B0`), a **url/title substring** (`example`, `github`),
|
|
163
|
+
- `browser` for the browser-level target (for `raw`/`repl`),
|
|
164
|
+
- or **nothing** to use the first open page.
|
|
165
|
+
|
|
166
|
+
### Emulation caveat
|
|
167
|
+
CDP overrides (`emulate`/`resize` viewport, geo, throttle, color-scheme, UA) live on
|
|
168
|
+
the **CDP connection** and revert when it closes. Since each `chromectl` command connects,
|
|
169
|
+
acts, and disconnects, use `--shot` to capture in the same session, or `--hold` to keep
|
|
170
|
+
the connection open (Ctrl-C to release) so the override persists while you do other work.
|
|
171
|
+
|
|
172
|
+
### Interaction (Tier 2) — how it works
|
|
173
|
+
`snapshot`/`click`/`fill`/`hover` use **Playwright attached to your Chrome over CDP**
|
|
174
|
+
(`connect_over_cdp`) — no separate browser is launched. You get Playwright's
|
|
175
|
+
auto-waiting and actionability checks (waits for the element to exist, be visible, and
|
|
176
|
+
be stable before acting) instead of hand-rolled timing. `snapshot` writes `.cdp-snap.json`
|
|
177
|
+
so `--ref N` works in a later, separate command (the stateless equivalent of the MCP's
|
|
178
|
+
element uids). Locate an element by any of: `--ref`, `--selector`, `--text`, or
|
|
179
|
+
`--role`+`--name`.
|
|
180
|
+
|
|
181
|
+
### Handy options
|
|
182
|
+
- `--host` / `--port` (or env `CDP_HOST` / `CDP_PORT`) — default `localhost:9222`.
|
|
183
|
+
- `capture`: `--print N`, `--out FILE`, `--har FILE`, `--type TYPE`,
|
|
184
|
+
`--no-bodies`, `--bodycap N`, `--max SECONDS`, `--quiet SECONDS`,
|
|
185
|
+
`--attach TARGET`, `--reload`.
|
|
186
|
+
|
|
187
|
+
## How it works (the workflow)
|
|
188
|
+
|
|
189
|
+
`HTTP /json/version` → open ONE WebSocket to the browser (or a per-tab socket) →
|
|
190
|
+
send CDP commands (`{id, method, params}`) and receive replies + unsolicited
|
|
191
|
+
events → for capture: `Network.enable` **before** navigating, correlate events by
|
|
192
|
+
`requestId`, fetch each body on `loadingFinished` (before Chrome evicts it), merge
|
|
193
|
+
the `*ExtraInfo` events for the real cookies/headers, and reconstruct raw HTTP.
|
|
194
|
+
|
|
195
|
+
A background thread reads frames into a queue so `call()` (request/response) and
|
|
196
|
+
the capture/console/watch loops (event streams) share one connection cleanly.
|
|
197
|
+
|
|
198
|
+
**Gotcha baked in:** `websocket-client` sends an `Origin` header by default, which
|
|
199
|
+
modern Chrome rejects on the debug port. `chromectl` sets `suppress_origin=True` so
|
|
200
|
+
connections are accepted without relaunching Chrome.
|
|
201
|
+
|
|
202
|
+
## Layout
|
|
203
|
+
- `chromectl/cli.py` — the CLI.
|
|
204
|
+
- `chromectl/protocol.json` — bundled CDP schema used by `chromectl proto`/`cheat` (falls back to live).
|
|
205
|
+
- `pyproject.toml` — packaging; `chromectl` console entry point.
|
|
206
|
+
- `AGENTS.md` — the agent-facing interface reference (auto-read by coding agents).
|
|
207
|
+
|
|
208
|
+
## For agents
|
|
209
|
+
Read `AGENTS.md` (or run `chromectl cheat --json` once) for the full command surface —
|
|
210
|
+
no need to call `--help` per command.
|