Skip to content

Commit 402ee9b

Browse files
sanketsudakeclaude
andauthored
docs: adoption-focused README + docs/ (CLI reference & scenario guides) (#3)
* docs: adoption-focused README + docs/ (CLI reference & scenario guides) Rework the README for open-source adoption and move the exhaustive detail into a docs/ set, applying Tufte information-design principles (front-load the claim, integrate examples at the point of use, tables for lookup, cut the internal-spec and wall-of-text status notes). README: - Add CI / Go Reference (godoc) / Go Report Card badges. - Lead with the value prop + a runnable 3-line example; a 4-step quickstart. - "Why chrome-cdp", a curated command taste, an AI-agent pitch, security, and a documentation index. Drops the .scratch spec note and the long Status section (superseded by the CLI reference). docs/: - cli-reference.md — every command, flag, exit code, and the output contract as lookup tables (verified against the binary's --help). - scenarios/automating-a-logged-in-web-app.md — the list→use→snap→act→verify loop end to end. - scenarios/forms-and-grids.md — label/cell addressing and batched fills. - scenarios/driving-widgets-with-select.md — menus, cascades, native selects. - using-with-ai-agents.md — the envelope/exit-code/snap design for agent tools. - README.md — docs index, with suggested future scenarios. Examples use generic placeholders; no real project/tenant/account data. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: add MIT LICENSE and license badge Release under MIT so the repo can be adopted once public (an unlicensed repo is "all rights reserved"). Adds the LICENSE file, a License badge, and flips the README's License section from "not yet licensed" to MIT. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 2f75a71 commit 402ee9b

8 files changed

Lines changed: 672 additions & 124 deletions

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Sanket Sudake
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.

README.md

Lines changed: 78 additions & 124 deletions
Large diffs are not rendered by default.

docs/README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Documentation
2+
3+
Start with the [project README](../README.md) for what `chrome-cdp` is and a five-minute quickstart.
4+
From there:
5+
6+
## Reference
7+
8+
- **[CLI reference](cli-reference.md)** — every command, flag, exit code, and the output contract, in lookup tables.
9+
10+
## Scenario guides
11+
12+
- **[Automating a logged-in web app](scenarios/automating-a-logged-in-web-app.md)** — the `list → use → snap → act → verify` loop against an app you're already signed into.
13+
- **[Forms and grids](scenarios/forms-and-grids.md)** — fill fields by visible label, grid cells by column header, and a whole grid over one batched connection.
14+
- **[Driving widgets with `select`](scenarios/driving-widgets-with-select.md)** — portal menus, cascade prompts, and native `<select>`s that a plain click can't open.
15+
16+
## For tool authors
17+
18+
- **[Using chrome-cdp from an AI agent](using-with-ai-agents.md)** — why the JSON envelope, exit codes, and accessibility reads make this a good agent tool, and the guardrails to keep.
19+
20+
---
21+
22+
Missing a scenario you'd find useful?
23+
Candidates not yet written: reading virtualized lists/calendars (scroll + `--dedupe`), piercing shadow DOM and iframes, debugging a page with `raw` CDP methods, and emulating viewport/geolocation for responsive checks.
24+
Open an issue and say what you're automating.

docs/cli-reference.md

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
# CLI reference
2+
3+
Every command speaks the same JSON envelope and the same exit-code contract, so a script or an agent parses one shape and branches on one number.
4+
This page is the lookup table for all of it; for the ideas behind the commands, start with the [README](../README.md) and the [scenario guides](README.md).
5+
6+
## Output contract
7+
8+
Add `--json` to any command and it emits exactly one envelope on stdout:
9+
10+
```json
11+
{ "ok": true, "command": "eval", "target": {"id":"","title":"","url":""},
12+
"result": { "value": "" }, "elapsed_ms": 12 }
13+
```
14+
15+
A failure uses the same shape with `"ok": false` and an `error{code,message,details}`, plus a nonzero exit code.
16+
Branch on the exit code, not on message text.
17+
18+
| Exit | Code | Meaning |
19+
|-----:|------|---------|
20+
| 0 || success |
21+
| 1 | generic | unclassified failure |
22+
| 2 | usage | bad flags or arguments |
23+
| 3 | connection | attach / launch failed |
24+
| 4 | target/timeout | selector not found, timed out, or ambiguous/unknown target |
25+
| 5 | cdp | CDP protocol error |
26+
| 6 | daemon | daemon error |
27+
28+
Without `--json` the same information renders as a short human line (result to stdout, errors to stderr).
29+
30+
## Global flags
31+
32+
These apply to every command.
33+
34+
| Flag | Default | Purpose |
35+
|------|---------|---------|
36+
| `--json` | off | one JSON value to stdout |
37+
| `--target <spec>` | sticky tab | tab to act on (see [Targeting](#targeting-a-tab)) |
38+
| `--timeout <dur>` | `30s` | max time to wait for the command |
39+
| `--by <mode>` | `css` | selector syntax (see [Addressing](#addressing-elements)) |
40+
| `--wait <cond>` | `visible` | element wait: `visible` \| `ready` \| `enabled` |
41+
| `--no-wait` | off | act immediately; fail fast instead of waiting |
42+
| `--role <role>` || with `--by name`: constrain to an ARIA role |
43+
| `--nth <n>` || with `--by name`: pick the Nth (1-based) match |
44+
| `--match <mode>` | `exact` | with `--by name`: `exact` \| `contains` \| `regex` |
45+
| `--in-row <text>` || with `--by name`: scope to the table row whose text contains this |
46+
| `--on-dialog <policy>` || on click/type/fill: `accept` \| `dismiss` a native dialog raised during the action |
47+
| `--pierce` | off | reach into shadow DOM / iframes (via DevTools search) |
48+
| `--no-daemon` | off | connect directly instead of via the shared daemon |
49+
| `--no-launch` | off | don't auto-launch a fallback Chrome |
50+
| `--port <n>` | auto | explicit Chrome debug port |
51+
| `--profile-dir <dir>` | default | managed-launch Chrome profile dir |
52+
| `--no-color` | off | plain output (also honors `$NO_COLOR`) |
53+
| `-q, --quiet` | off | suppress non-essential output |
54+
| `-v, --verbose` | off | verbose diagnostics on stderr |
55+
56+
Precedence, highest first: **command-line flag > `CHROME_CDP_*` env var > config file > built-in default** (see [Configuration](#configuration)).
57+
58+
## Targeting a tab
59+
60+
A command acts on one tab, chosen (highest precedence first) by `--target`, then the sticky tab set with `use`, then a `target` default in config.
61+
62+
| Spec | Matches |
63+
|------|---------|
64+
| `<idprefix>` | a tab whose id starts with this |
65+
| `url:<substr>` | first tab whose URL contains the substring |
66+
| `title:<substr>` | first tab whose title contains the substring |
67+
| `@N` | the Nth tab (1-based) in `list` order |
68+
69+
```sh
70+
chrome-cdp use url:github # set the sticky tab once…
71+
chrome-cdp snap # …then omit --target on later commands
72+
```
73+
74+
## Addressing elements
75+
76+
`--by` chooses how a selector argument is interpreted.
77+
On real apps, prefer `name` — it reads the accessibility tree, skips hidden/utility nodes, and crosses shadow DOM and same-origin iframes.
78+
79+
| `--by` | Selector is | Notes |
80+
|--------|-------------|-------|
81+
| `css` | a CSS selector | default; dynamic-id apps make it brittle |
82+
| `id` | an element id | |
83+
| `name` | an ARIA accessible name | prefer on real apps; pair with `--role` / `--nth` / `--match` |
84+
| `ref` | a `snap`-issued `e<id>` ref | act on the exact node `snap` reported, no re-resolve |
85+
| `cell` | a `[row\|]column` grid header | resolves the editable input in that grid cell |
86+
| `label` | a form control's visible label | for controls whose label isn't wired (no `aria-label` / `<label for>`) |
87+
| `search` | DevTools text/XPath/CSS search | broad; first match wins |
88+
| `jspath` | a JS path | |
89+
| `css-all` | a CSS selector (all matches) | |
90+
91+
Modifiers that refine a `--by name` match:
92+
93+
| Modifier | Effect |
94+
|----------|--------|
95+
| `--role button` | keep only nodes with that ARIA role |
96+
| `--match contains` | match a case-insensitive substring (real apps have verbose names) |
97+
| `--nth 2` | pick the 2nd match among visible candidates |
98+
| `--in-row "<text>"` | keep only the match inside the table row containing `<text>` |
99+
100+
**Backgrounded tabs:** `--by css` / `id` / `search` resolve via `querySelector` and work regardless.
101+
`--by name` / `ref` / `cell` read the accessibility tree, which Chrome throttles on a tab it can't foreground — a timeout there returns `tab_hidden: true` so you know to foreground Chrome (`--by name` also falls back to a DOM name match, and `--in-row` / `label` resolve via the DOM, so those keep working).
102+
103+
## Commands
104+
105+
### Tabs and navigation
106+
107+
| Command | Does |
108+
|---------|------|
109+
| `list [--url <s>] [--title <s>]` | list open tabs (`id`, title, URL); filters narrow by substring |
110+
| `open <url>` | new tab → navigate → make it current; returns its id |
111+
| `use <target>` | set the sticky current tab |
112+
| `nav <url>` | navigate the target tab, wait for load |
113+
114+
```sh
115+
chrome-cdp list --url outlook # just the Outlook tabs
116+
chrome-cdp open https://example.com # returns the new tab's id
117+
```
118+
119+
### Reading the page
120+
121+
| Command | Returns |
122+
|---------|---------|
123+
| `snap [--role <r>] [--grep <re>] [--region <name>] [--dedupe]` | accessibility snapshot: roles + names of actionable nodes, plus `alerts`, `focused`, per-node `states`/`value`/`ref` |
124+
| `grid [selector]` | a table/grid as `{headers, rows, count}` |
125+
| `value <selector> [--all]` | a form field's value (`--all`: every match, as a list) |
126+
| `text <selector>` | visible text of a selector (or the page) |
127+
| `html <selector> [--inner]` | outer (or inner) HTML |
128+
| `eval <js>` | evaluate JS in the top frame |
129+
130+
The `snap` filters run server-side, so a read returns just the relevant nodes instead of a whole page.
131+
132+
```sh
133+
chrome-cdp snap --role button --grep "[AP]M" # calendar-event buttons only
134+
chrome-cdp grid # read a table without parsing snap
135+
chrome-cdp value --all "input.hours" # every hour cell in one call
136+
```
137+
138+
### Acting
139+
140+
| Command | Does |
141+
|---------|------|
142+
| `click <selector>` | click at the element's occlusion-verified centre |
143+
| `type <selector> <text>` | type via real keystrokes (**appends**; end with `\n` to press Enter) |
144+
| `fill <selector> <value>` | set a field, **replacing** its content (clears, then types) |
145+
| `select <field> <option>` | choose an option in a prompt / combobox / cascade / native `<select>` |
146+
| `scroll [selector] [--dx <p>] [--dy <p>] [--to] [--wheel]` | scroll by a delta, `--to` a selector into view, or a real `--wheel` |
147+
| `attr get\|list\|set\|rm <selector> [name] [value]` | read/write element attributes |
148+
149+
`click` / `type` / `fill` / `select` also take **`--wait-text "<substr>"`** — after the action, block until the page contains the text (a `Saved` toast), folding act-and-confirm into one call.
150+
151+
`select` addresses the field by accessible name by default; a cascade path is `>`-separated:
152+
153+
| `select` flag | Purpose |
154+
|---------------|---------|
155+
| `--option-match <mode>` | how each option segment matches: `contains` (default) \| `exact` \| `regex` |
156+
| `--filter <text>` | type this into the prompt to narrow options before selecting |
157+
| `--sep <char>` | cascade path separator between levels (default `>`) |
158+
159+
```sh
160+
chrome-cdp click --by name "Sign in" --role button
161+
chrome-cdp fill "#hours" "8"
162+
chrome-cdp fill --by cell "Mon, 7/13" "8" # grid input by column header
163+
chrome-cdp select --by label "Category" "Direct Revenue" # native <select> by label
164+
chrome-cdp select "Time Type" "Projects > Acme: Platform > Project > Time Entry" --role textbox
165+
chrome-cdp click --by name "Delete" --in-row "row two" --role button
166+
chrome-cdp click "#delete" --on-dialog accept # auto-accept a native confirm()
167+
```
168+
169+
### Waiting
170+
171+
`wait` blocks until one condition holds (or `--timeout`).
172+
Prefer a condition over a fixed `--for` sleep.
173+
174+
| Condition | Waits until |
175+
|-----------|-------------|
176+
| `--url <substr>` | the tab's URL contains the substring |
177+
| `--visible <selector>` | the selector is visible |
178+
| `--gone <selector>` | the selector is gone |
179+
| `--text <substr>` | the accessibility tree (incl. alerts) contains the text |
180+
| `--stable` | the accessibility tree stops changing (page settled) |
181+
| `--idle` | network activity settles (no in-flight requests) — for SPA loads |
182+
| `--for <dur>` | a fixed duration (fallback) |
183+
184+
```sh
185+
chrome-cdp wait --idle # after nav/open on an SPA
186+
chrome-cdp wait --text "Success" # confirm a write landed
187+
```
188+
189+
### Batch mode
190+
191+
`session` reads NDJSON argv lines on stdin and runs each over **one** held connection, emitting one NDJSON envelope per line — no per-command process spawn, and `snap` refs stay valid across the batch.
192+
193+
```sh
194+
printf '%s\n' \
195+
'["fill","--by","cell","Mon, 7/13","8"]' \
196+
'["fill","--by","cell","Tue, 7/14","8"]' \
197+
'["value","--all","input[data-automation-id=numericInput]"]' \
198+
'["click","--by","name","Save and Close","--role","button","--wait-text","saved"]' \
199+
| chrome-cdp session
200+
```
201+
202+
### Capture
203+
204+
| Command | Writes |
205+
|---------|--------|
206+
| `screenshot [-o <path>]` | a PNG (to cwd, or `-o -` for stdout) |
207+
| `pdf [-o <path>]` | a PDF of the page |
208+
209+
### Browser state
210+
211+
| Command | Does |
212+
|---------|------|
213+
| `cookie list\|set\|rm\|clear` | read and write cookies for the tab |
214+
| `headers set <k=v> …` | set extra request headers |
215+
| `emulate viewport\|geo\|reset` | override viewport size / geolocation, or clear overrides |
216+
| `frame list` | list the tab's frame tree |
217+
218+
### Escape hatch
219+
220+
`raw` calls any CDP method by name — full protocol coverage, no per-method wrapper.
221+
222+
```sh
223+
chrome-cdp raw --list # list CDP domains
224+
chrome-cdp raw Network.setCacheDisabled '{"cacheDisabled":true}'
225+
chrome-cdp raw Browser.getVersion --browser # browser-level method
226+
```
227+
228+
### Meta
229+
230+
| Command | Does |
231+
|---------|------|
232+
| `doctor` | check the connection and print the exact fix if it's not ready |
233+
| `daemon start\|stop\|status` | manage the background connection |
234+
| `exit-codes` | print the exit-code table |
235+
| `version` | print the version |
236+
| `completion bash\|zsh\|fish\|powershell` | shell completion script |
237+
238+
## Connection model
239+
240+
`chrome-cdp` attaches to your **real** Chrome via the one-time `chrome://inspect/#remote-debugging` toggle; it reads Chrome's `DevToolsActivePort` file and connects the WebSocket directly (the classic `--remote-debugging-port` flag no longer works on the default profile since Chrome M136).
241+
If no debug-enabled Chrome is found, it launches a managed Chrome on a dedicated profile alongside your real one.
242+
243+
A background **daemon** holds the connection, so Chrome's "Allow debugging?" prompt appears once per session rather than once per command.
244+
It starts lazily on first use and idles out after 30 minutes; manage it with `daemon start|stop|status`, or bypass it with `--no-daemon`.
245+
246+
Run `chrome-cdp doctor` to check the connection and get the exact fix when it isn't ready.
247+
248+
## Configuration
249+
250+
Persist flags you'd otherwise retype in `$XDG_CONFIG_HOME/chrome-cdp/config.toml` (usually `~/.config/chrome-cdp/config.toml`); see [`config.example.toml`](../config.example.toml) for the full key set.
251+
252+
```toml
253+
json = true # default to machine-readable output
254+
timeout = "10s"
255+
by = "search" # default selector syntax
256+
target = "url:github" # default tab when neither --target nor `use` is set
257+
```
258+
259+
A malformed config is a warning on stderr, not a fatal error — the CLI still runs on the built-ins.

0 commit comments

Comments
 (0)