WebAct Browser Control
Control Chrome directly via the Chrome DevTools Protocol. Raw CDP through a CLI helper.
If you have webact MCP tools available (e.g. navigate, click), stop here and use those instead. The MCP server handles session management and tab isolation automatically. The rest of this file is for CLI-only environments where MCP tools are not available.
How to Run Commands
All commands use the webact CLI (the webact binary). Use the binary on PATH.
Session Setup (once)
webact launchThis launches Chrome (or connects to an existing instance) and creates a session. All subsequent commands auto-discover the session — no session ID needed. Use --headless for invisible operation. Use --tab <id> to target a specific tab (creates an isolated session to avoid polluting the original).
Running Commands
Use direct CLI commands. Each is a single bash call:
webact navigate https://example.com
webact click button.submit
webact keyboard "hello world"
webact press Enter
webact domAuto-brief: State-changing commands (navigate, click, hover, press Enter/Tab, scroll, select, waitfor) auto-print a compact page summary showing URL, title, inputs, buttons, links, and total element counts. Read it first. Do not take a screenshot after every action. Use axtree -i or observe when you need actionable elements, read for content, dom only when you need HTML structure.
Command Reference
| Command | Example | |||
|---|---|---|---|---|
launch [options] | webact launch or webact launch --headless or webact launch --profile bot | |||
navigate <url> | webact navigate https://example.com | |||
kill | webact kill | |||
batch <json> | webact batch '{"actions": [{"tool": "click", "target": "..."}]}' | |||
grid [spec] | webact grid or webact grid 8x6 or webact grid off | |||
install | webact install | |||
media <features> | webact media dark or webact media reset | |||
animations <action> | webact animations pause or webact animations resume | |||
security <action> | webact security ignore-certs or webact security strict | |||
storage <action> | webact storage clear everything or webact storage get | |||
sw <action> | webact sw unregister or webact sw list | |||
back | webact back | |||
forward | webact forward | |||
reload | webact reload | |||
feedback <1-5> [text] | webact feedback 5 "Works great!" | |||
read [selector] [--tokens=N] | webact read or webact read article or webact read --tokens=2000 | |||
text [selector] [--tokens=N] | webact text or webact text --tokens=2000 | |||
dom [selector] [--tokens=N] | webact dom or webact dom.results or webact dom --tokens=1000 | |||
axtree [selector] [-i] | webact axtree or webact axtree -i | |||
observe | webact observe | |||
screenshot [options] | webact screenshot or webact screenshot --ref=3 or webact screenshot --selector=.main --scale=1 | |||
fill <sel val...> | webact fill "#email" "user@example.com" "#pass" "secret" | |||
pdf [path] | webact pdf or webact pdf /tmp/page.pdf | |||
| `click <sel\ | x,y\ | --text>` | webact click button.submit or click 550,197 or click --text Close | |
| `doubleclick <sel\ | x,y\ | --text>` | webact doubleclick td.cell or doubleclick 550,197 | |
| `rightclick <sel\ | x,y\ | --text>` | webact rightclick.context-target or rightclick 550,197 | |
| `hover <sel\ | x,y\ | --text>` | webact hover.menu-trigger or hover --text Settings | |
focus <selector> | webact focus input[name=q] | |||
clear <selector> | webact clear input[name=q] | |||
type <selector> <text> | webact type input[name=q] search query | |||
keyboard <text> | webact keyboard hello world | |||
paste <text> | webact paste Hello world | |||
select <selector> <value> | webact select select#country US | |||
upload <selector> <file> | webact upload input[type=file] /tmp/photo.png | |||
drag <from> <to> | webact drag.card.dropzone | |||
| `dialog <accept\ | dismiss> [text]` | webact dialog accept | ||
waitfor <selector> [ms] | webact waitfor.dropdown 5000 | |||
waitfornav [ms] | webact waitfornav | |||
| `press <key\ | combo>` | webact press Enter or webact press Ctrl+A | ||
scroll <target> [px] | webact scroll down 500 or webact scroll top | |||
eval <js> | webact eval document.title | |||
| `cookies [get\ | set\ | clear\ | delete]` | webact cookies or webact cookies set name val |
| `console [show\ | errors\ | listen]` | webact console or webact console errors | |
| `network [capture\ | show]` | webact network capture 10 api or webact network show cloudwatch | ||
block <pattern> | webact block images css or webact block off | |||
viewport <w> <h> | webact viewport mobile or webact viewport 1024 768 | |||
zoom <level> | webact zoom 50 or webact zoom out or webact zoom reset | |||
frames | webact frames | |||
| `frame <id\ | selector>` | webact frame main or webact frame iframe#embed | ||
| `download [path\ | list]` | webact download path /tmp/dl or webact download list | ||
tabs | webact tabs | |||
tab <id> | webact tab ABC123 | |||
newtab [url] | webact newtab https://example.com | |||
close | webact close | |||
activate | webact activate | |||
minimize | webact minimize | |||
resolve <selector> | webact resolve a.apply-btn or webact resolve 3 | |||
find <query> | webact find "submit button" | |||
update | webact update | |||
search <query> [--engine=E] | webact search "best restaurants" or webact search "query" --engine=duckduckgo | |||
readurls <url...> | webact readurls https://a.com https://b.com | |||
connect | webact connect | |||
run <sid> | webact run a1b2c3d4 | |||
| `humanclick <sel\ | x,y>` | webact humanclick button.submit | ||
humantype <sel> <text> | webact humantype input[name=q] hello | |||
lock [seconds] | webact lock 30 | |||
unlock | webact unlock | |||
uninstall | webact uninstall | |||
mcp | webact mcp (run as MCP server via stdio) | |||
| `clipboard <html\ | text>` | webact clipboard --html="<b>bold</b>" --text="bold" | ||
inserttext <text> | webact inserttext "large block of text" | |||
| `config <get\ | set> [key] [value]` | webact config get or webact config set telemetry false |
type vs keyboard vs paste vs clipboard vs inserttext: Use type to focus a specific input and fill it. Use keyboard to type at the current caret position — essential for rich text editors (Slack, Google Docs, Notion) where type's focus call resets the cursor. Use paste to insert text via a ClipboardEvent — works with apps that intercept paste and is faster than keyboard for large text. Use clipboard to paste HTML/rich text via real clipboard API + Cmd+V — works with Google Docs, Sheets, Notion. Use inserttext for fast plain text insertion at cursor via CDP Input.insertText.
click behavior: Prefer refs from axtree -i or observe. Otherwise use a CSS selector or --text. Waits up to 5s for the element, scrolls it into view, then clicks. When multiple elements match --text, interactive elements (button, a, input, [role=button]) are preferred over generic containers (div, span). Use coordinates from a screenshot only as a last resort for canvas/image/iframe-heavy pages where ref, text, and selector targeting have all failed.
fill: Fill multiple form fields in one call. Pass alternating selector/value pairs: fill "#email" "user@example.com" "#password" "secret". More efficient than multiple type calls. Supports ref numbers from axtree -i.
screenshot options: Expensive (~500+ vision tokens). Defaults to 800px wide JPEG for token efficiency. Use --ref=N to crop to a ref number from axtree -i (cheapest visual option), --selector=CSS to crop to an element, --scale=1 for full viewport resolution (or any multiplier), --format=png for lossless, --quality=N (1-100), --pad=N to control padding around ref/selector crops (default: 48).
dialog behavior: Sets a one-shot auto-handler. Run BEFORE the action that triggers the dialog.
read: Reader-mode text extraction. Strips navigation, sidebars, ads, and returns just the main content as clean text with headings, lists, and paragraphs. Best for articles, docs, search results, and information retrieval.
text: Full page in reading order, interleaving static text with interactive elements (numbered refs). Like a screen reader view. Generates ref map as side effect, so you can use ref numbers in click/type/etc afterward. Best for complex pages where you need both content and interaction targets.
axtree vs dom: The accessibility tree shows semantic roles (button, link, heading, textbox) and accessible names - better for understanding page structure. Use dom when you need HTML structure/selectors; use axtree when you need to understand what's on the page.
axtree -i (interactive mode): Shows only actionable elements (buttons, links, inputs, etc.) as a flat numbered list. Most token-efficient way to see what you can interact with on a page. After running axtree -i, use the ref numbers directly as selectors: click 1, type 3 hello. Refs are cached per URL and reused on revisits.
observe: Like axtree -i but formats each element as a ready-to-use command (e.g. click 1, type 3 <text>, select 5 <value>). Generates the ref map as a side effect.
Ref-based targeting: After axtree -i or observe, numeric refs work in all selector-accepting commands: click, type, select, hover, focus, clear, doubleclick, rightclick, upload, drag, waitfor, dom.
press combos: Supports modifier keys: Ctrl+A (select all), Ctrl+C (copy), Meta+V (paste on Mac), Shift+Enter, etc. Modifiers: Ctrl, Alt, Shift, Meta/Cmd.
Mac keyboard note: On macOS, app shortcuts documented as Ctrl+Alt+<key> (e.g., Google Docs heading shortcuts Ctrl+Alt+1 through Ctrl+Alt+6) must be sent as Meta+Alt+<key> through CDP. Mac's Ctrl key is not the Command key these apps expect. Example: press Meta+Alt+2 for Heading 2 in Google Docs.
scroll targets: up/down (default 400px, or specify pixels), top/bottom, or a CSS selector to scroll an element into view. Element-scoped: scroll <selector> <up|down|top|bottom> [px] scrolls within a container element instead of the page — essential for apps with custom scroll containers (Google Docs, Slack).
network capture: Captures XHR/fetch/API requests for a duration. network capture 10 captures for 10 seconds. network capture 15 api/query captures for 15s, filtering to URLs containing "api/query". network show re-displays the last capture. network show cloudwatch filters saved results. Shows method, URL, status, type, timing, and POST body. Essential for diagnosing API issues in SPAs.
block patterns: Block resource types (images, css, fonts, media, scripts) or URL substrings. Speeds up page loads. Use block off to disable.
viewport presets: mobile (375x667), iphone (390x844), ipad (820x1180), tablet (768x1024), desktop (1280x800). Or specify exact width and height.
frames: Lists all frames/iframes on the page. Use frame <id> to switch context, frame main to return to the top frame.
Profiles: Use profiles to launch isolated browser instances with separate data.
webact launchuses the default shared profile.webact launch --profile shopping-botcreates or reuses a named profile.webact launch --profile newauto-generates a profile ID and returns it.- Each profile runs its own browser process on its own port. Custom profiles can be killed with
webact kill.
batch: Execute multiple actions sequentially in one call. Use a JSON array of actions. Smart waits are applied after successful state-changing actions (navigate, click, fill, select, type). Batch stops on the first non-optional error.
webact batch '{"actions": [{"tool": "click", "target": "--text Submit"}, {"tool": "waitfornav"}]}'- Add
"retries": Nand"retry_delay": msto retry flaky steps. - Add
"optional": truefor dismissals or branches that can fail without aborting the batch. - Add
"wait": msto override the post-step smart wait for a specific action.
grid: Overlay a coordinate grid for targeting elements in canvas/image-heavy apps. Each cell displays its center coordinate.
webact grid(default 10x10)webact grid 8x6(cols x rows)webact grid 50(50px cell size)webact grid off(remove overlay)
install: Register webact as an MCP server with all detected clients (Claude Code, Cursor, Windsurf, Claude Desktop, etc.) without re-downloading the binary.
Troubleshooting SPAs and Stale Pages:
sw unregister: remove service workers that cache old content.storage clear everything: clear all storage, caches, cookies, and service workers for the origin.reload: force a fresh page load.
Media and Animations:
media dark: switch to dark color scheme.media reset: restore defaults.animations pause: freeze JS animations (sets playback rate to 0).animations resume: restore normal playback.security ignore-certs: accept self-signed certificates for the current origin.
Tab Isolation
Each session creates and owns its own tabs. Sessions never reuse tabs from other sessions or pre-existing tabs.
launch/connectcreates a new blank tab for the sessionnewtabopens an additional tab within the sessiontabsonly lists tabs owned by the current sessiontab <id>only switches to session-owned tabscloseremoves the tab from the session- Clicks that open a new tab via
target=_blankorwindow.openare auto-adopted into your session and become the active tab
This means two agents can work side by side in the same Chrome instance without interfering with each other.
Shared Chrome awareness: When multiple agents share Chrome, link clicks on sites like Slack can hijack your tab (e.g. Slack's link unfurling navigates to Jira). Always record your tab ID after launch/newtab and verify you're on the right tab before acting. If your tab's URL has changed unexpectedly, use tab <id> to switch back or tabs to audit your session.
The Perceive-Act Loop
- PLAN — Break the goal into steps.
- ACT — Run the appropriate command. State-changing commands auto-print a page brief.
- DECIDE — Read the brief. If you need more, use the cheapest sufficient perception tool (see escalation order below).
- REPEAT until done or blocked.
Rules
- Read the brief after acting. State-changing commands auto-print a page brief. Read it before deciding your next step.
- Text tools before screenshot. Only use
screenshotwhen the page is canvas/image-heavy, you need visual verification, or text tools are insufficient. Start with--ref=Nor--selectorcrops — not full page. - Report actual content. When the goal is information retrieval, extract and present the actual text from the page. Do not summarize — show what IS there.
- Stop when blocked. If you encounter a login wall, CAPTCHA, 2FA, or cookie consent, run
activateto bring the browser to front, then tell the user. Do not guess credentials. - Wait for dynamic content. After clicks that trigger page loads, use
waitfornavorwaitfor <selector>before reading DOM. - Prefer ref-based targeting. Use refs from
axtree -i,observe, ortext. Use CSS selectors when you need DOM structure or a ref is unavailable. Use coordinates only as a last resort for canvas/iframe surfaces. - Clean up tabs. Close tabs opened with
newtabwhen done. Runtabsbefore reporting completion. - Track tab IDs. Note tab IDs from
launch/newtaboutput. Verify you're on the expected tab before acting.
Perception Escalation
Stop at the first tool that gives you what you need:
| Priority | Tool | Use for | Cost |
|---|---|---|---|
| 1 | read | Page content (articles, docs, search results) | Low |
| 2 | axtree -i / observe | Actionable elements with refs | Low |
| 3 | text | Full visible text + refs (cap with --tokens=N) | Low-Med |
| 4 | dom | HTML structure/selectors (scope with selector or --tokens=N) | Medium |
| 5 | screenshot --ref=N or --selector | Visual of one element (800px wide) | Medium |
| 6 | screenshot | Full page visual fallback (800px wide) | High |
| 7 | zoom out then screenshot | More content per screenshot | High |
| 8 | screenshot --scale=1 | Full viewport resolution (last resort) | Highest |
Targeting Elements (priority order)
- refs: from
axtree -i,observe, ortext—click 3,type 5 hello,screenshot --ref=7 - text search:
click --text Submit— finds the smallest visible text match, then clicks the nearest actionable ancestor (button/link/tab/etc.) when needed - CSS selectors:
#id,[data-testid="..."],[aria-label="..."],.class, structural - eval:
evalwith querySelector when the element is present but hard to target - coordinates:
click 550,197— last resort for canvas/iframes only, after all above have failed
Common Patterns
All examples assume you've already run webact launch.
Navigate and read (navigate auto-prints brief - no separate dom needed):
webact navigate https://news.ycombinator.comFill a form:
# Multiple fields at once:
webact fill "input[name=q]" "search query"
# Or one at a time:
webact click input[name=q]
webact type input[name=q] search query
webact press EnterRich text editors and @mentions:
webact click .ql-editor
webact keyboard Hello @alice
webact waitfor [data-qa='tab_complete_ui_item'] 5000
webact click [data-qa='tab_complete_ui_item']
webact keyboard " check this out"Complex Web Apps
Portals, shadow DOM, and overlays:
- Modal dialogs, dropdowns, and popups often render in portal containers — CSS selectors from parent context won't find them
axtree -iandobserveinclude deep overlays, nested menus, and portal content — try refs firstclick --textfinds elements inside portals and across shadow DOM boundaries, then walks up to the nearest actionable ancestor before clickingdomtraverses open shadow roots — web component internals are visible- When all else fails, use
evalto find and.click()directly - Coordinate clicks from screenshots are a last resort for canvas/iframe-only surfaces
Configuration
Settings file: ~/.config/webact/webact.json
{
"telemetry": true,
"feedback": true
}Set telemetry to false to opt out of anonymous usage statistics (tool counts per session, no PII). Edit the file directly or use webact config set <key> <true|false>.