|
| 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