Browser CLI
Control browser automation from the command line
Control browser automation for AI agents directly from the CLI. Launch Chrome, navigate pages, take screenshots, run browser agents, and more.
Standalone Entry Point
The praisonai-browser console script mirrors every praisonai browser subcommand.
praisonai-browser --help
praisonai-browser run "Go to google and search praisonai"Inside the full wrapper the same commands work unchanged:
praisonai browser run "Go to google and search praisonai"Commands
praisonai browser <command> [OPTIONS]
# or standalone:
praisonai-browser <command> [OPTIONS]| Command | Description |
|---|---|
start | Start the browser automation server |
launch | Launch Chrome with extension and optionally run a goal |
run | Run browser agent with a goal |
navigate | Navigate a browser tab to a URL |
screenshot | Capture a screenshot of a browser tab |
pages | List all browser pages/tabs |
tabs | List and manage browser tabs |
dom | Get DOM tree from a browser page |
content | Read page content as text |
console | Get console logs from a page |
js | Execute JavaScript in a page |
execute | Execute JavaScript in a browser tab |
sessions | List browser automation sessions |
history | Show step-by-step history for a session |
clear | Clear session history |
doctor | Browser health diagnostics |
chrome | Chrome browser management |
extension | Chrome extension management |
benchmark | Browser automation benchmarks |
Doctor
Check browser health and configuration:
praisonai browser doctorDoctor Extension
Check whether the Chrome extension is working. This command reports two independent signals — the bridge connection is ground truth, the CDP check is optional.
praisonai browser doctor extension
praisonai browser doctor extension --server-port 8765 # custom bridge port
praisonai browser doctor extension --port 9222 # custom CDP debug port| Option | Default | Description |
|---|---|---|
--port, -p | 9222 | Chrome CDP debug port (optional check) |
--server-port | 8765 | Bridge server port (ground-truth /health check) |
Two signals:
- Bridge connection (ground truth) — queries
http://localhost:<server-port>/healthand reads theextension_connectionscount.✅ Extension connected to bridge (N connection(s))means the extension is working, regardless of which Chrome profile it runs in. - CDP :9222 (optional) — a service-worker check on the debug port. Shown as
ℹ️ Extension not in CDP Chrome on port 9222 (normal for daily 'Work' Chrome).— this is info only and is not a failure on its own.
Exit codes: doctor extension exits 0 when the bridge shows at least one extension connection (regardless of the CDP result), and exits 1 only when the bridge shows zero extension connections.
Launch
Launch Chrome with the PraisonAI extension:
praisonai browser launchWith a goal to execute:
praisonai browser launch --goal "Search for AI news"Manual Load Fallback
Auto-load can fail silently on Chrome 137+ / Windows, which now blocks automated --load-extension. When the extension doesn’t connect, launch prints a manual “Load unpacked” recovery flow. The instructions differ by flag:
- Default — the bridge is running, so it points you to
curl http://127.0.0.1:<server-port>/health(expectextension_connections >= 1). --no-server— no bridge was started, so it tells you to runpraisonai browser startfirst, then verify withpraisonai browser doctor extension.
Run
Run a browser agent with a specific goal:
praisonai browser run "Search for the latest AI news and summarize"run fails fast: it checks the bridge, honours typed exit codes, and aborts a stalled session in 30 seconds instead of hanging silently.
Pre-flight check
run hits GET http://127.0.0.1:8765/health with a 3-second timeout before opening the WebSocket, so a missing bridge or extension fails in seconds instead of hanging on the full --timeout. Three failures land here:
| Failure | Message | Exit |
|---|---|---|
| Bridge unreachable or malformed body | Cannot reach PraisonAI Browser bridge | 2 |
| No extension connected | Bridge is up but no extension is connected | 2 |
| Invalid health response | Bridge returned an unexpected health response / Bridge returned an invalid connection count | 2 |
The “no extension” message lists four recovery steps: open Chrome with the PraisonAI extension and side panel, verify with curl http://127.0.0.1:8765/health (expect extension_connections >= 1), use the side panel or the CLI (not both), and — as a tip — use --engine cdp for extension-free automation.
Exit codes
run returns a stable exit code you can script against in CI, cron, or make targets.
| Exit code | Meaning | When |
|---|---|---|
0 | Success | Session completed, or you hit Ctrl+C |
1 | Task failure | Session ended in failed or stopped state |
2 | Infra failure | Bridge unreachable, no extension, server error, or watchdog fired |
3 | Timeout | Session exceeded --timeout seconds |
These codes apply to both normal and --debug runs, so 2 always means “fix the environment” and 1 always means “the prompt or page didn’t work out.”
First-step watchdog
When the server can’t confirm the task reached an extension, run aborts after 30 seconds if no automation step has appeared:
No automation steps in 30s
Check: extension connected? side panel stopped? sessions=0?This maps to exit code 2. The watchdog applies only when delivery is unconfirmed — a confirmed run honours --timeout fully, so a slow first step from a cold browser or slow initial model call is legitimate. Steps that arrive right at the deadline are shown before the watchdog fires.
Verbose delivery indicator
Add -v to see whether the task reached an extension once the session starts:
start_automation delivered to extension # confirmed
Warning: start_automation not confirmed # unconfirmedIf the server reports delivery as False, run fails fast with Extension automation did not start. (exit 2) instead of polling an empty session until timeout.
Retry & selector fallback (CDP / hybrid engines)
When a step fails, --max-retries N re-runs it up to N times, and the CDP agent tries three alternative-selector strategies before giving up on a step:
- Text-match — replaces the failing selector with one that matches the same visible text.
- Fallback-selector — swaps in a semantically equivalent selector (
#login,[data-testid="login"],button[type="submit"], …). - Click → navigate — if the target was a link, navigate to its
hrefdirectly.
A step is considered failed when the injected JS reports the selector matched no element — for example:
{"success": false, "error": "selector matched no element: #nope"}Reachability: --engine cdp and --engine hybrid. Extension mode uses its own retry path.
| Option | Default | Description |
|---|---|---|
--max-retries | 3 | Retries per action on failure, with alternative-selector strategies |
Common failures
Bridge down
The bridge server is not running. Start it in a separate terminal, then verify:
praisonai browser start --port 8765
curl http://localhost:8765/healthCannot connect to PraisonAI Browser bridge at ws://localhost:8765/ws
The bridge server is not running. In a separate terminal, start it with:
praisonai browser start --port 8765
Then verify it is up:
curl http://localhost:8765/health
Note: this is the local bridge server, not your target site (--url).No extension connected
The bridge is up but no extension is attached. Open Chrome with the PraisonAI extension and side panel, then confirm the count:
curl http://127.0.0.1:8765/health # expect extension_connections >= 1For extension-free automation, use --engine cdp instead.
Side panel busy
The side panel agent and the CLI can’t drive the same extension at once. run surfaces server errors with a friendly message and exits 2:
<server error message>
Stop the side panel agent and retry.Stop the side panel run, then retry from the CLI. You can confirm the bridge is idle with:
curl http://127.0.0.1:8765/health # expect extension_busy: falseOptions
praisonai browser run accepts 14 options. Most only take effect on the CDP engine — see the Engine × Option matrix below for exactly which flag reaches the wire on each engine.
| Option | Default | Description | Effective on |
|---|---|---|---|
goal (positional) | — | The task the agent should complete | all engines |
--url / -u | https://www.google.com | Start URL | cdp, playwright, hybrid (context-only on extension) |
--extension / -e | — | Extension dist path | extension only |
--model / -m | gpt-4o-mini | LLM model | all engines |
--max-steps | 20 | Maximum steps per session | all engines (per-session since #4313) |
--timeout / -t | 120 | Client-side timeout in seconds | all engines |
--headless | False | Run headless (experimental) | cdp, playwright, hybrid |
--engine | extension | Automation engine: extension, cdp, playwright, hybrid | — |
--verbose / -v | False | Verbose logging | all engines |
--debug / -d | False | Debug mode — show all events | all engines |
--max-retries | 3 | Retries per action with alternative selectors | cdp |
--vision | False | Vision-based element detection (requires gpt-4o) | cdp |
--screenshots | — | Directory to save step screenshots | cdp |
--record / --no-record | --record | Record session to database | cdp for --no-record |
--record-video | False | Record recording.webm of the session | cdp |
Engine × Option matrix
This is the crux of the #4313 fix. On the default extension engine only goal, model and max-steps reach the wire — the extension drives the tab you already have open in Chrome. Engine-only flags are now warned about (see Engine-only options warning) instead of silently discarded.
| Option | extension (default) | cdp | playwright | hybrid |
|---|---|---|---|---|
goal (positional) | ✅ forwarded | ✅ | ✅ | ✅ |
--model | ✅ | ✅ | ✅ | ✅ |
--max-steps | ✅ (per-session, since #4313) | ✅ | ✅ | ✅ |
--url | ℹ️ context only (uses active tab) | ✅ start URL | ✅ start URL | ✅ start URL |
--vision | ⚠️ ignored (warning) | ✅ | ⚠️ ignored (warning) | ⚠️ ignored (warning) |
--screenshots <dir> | ⚠️ ignored (warning) | ✅ | ⚠️ ignored (warning) | ⚠️ ignored (warning) |
--record-video | ⚠️ ignored (warning) | ✅ (since #4313) — writes recording.webm; implies vision → gpt-4o | ⚠️ ignored (warning) | ⚠️ ignored (warning) |
--max-retries | ⚠️ ignored (warning) when ≠ 3 | ✅ (default 3) | ⚠️ ignored (warning) when ≠ 3 | ⚠️ ignored (warning) when ≠ 3 |
--headless | ⚠️ ignored (warning) | ✅ | ✅ | ✅ |
--extension <path> | ⚠️ ignored (warning) | n/a | n/a | n/a |
--no-record | ⚠️ ignored (warning) | ✅ | ⚠️ ignored (warning) | ⚠️ ignored (warning) |
--debug | ✅ | ✅ | ✅ | ✅ |
--verbose / -v | ✅ | ✅ | ✅ | ✅ |
--timeout | ✅ (client-side) | ✅ | ✅ | ✅ |
Which engine should I use?
Engine-only options warning
Starting with #4313, setting a CDP-only flag on the default extension engine is no longer a silent no-op. The CLI prints a yellow notice to the console listing exactly which flags were dropped, so you can catch a mistyped --engine immediately.
On the extension engine, any of --vision, --screenshots, --record-video, --max-retries (when ≠ 3), --headless, --extension, --no-record triggers:
Ignored on the extension engine: --vision, --screenshots, --record-video, --max-retries, --headless, --extension, --no-record
These options require --engine cdp (or playwright/hybrid).On the playwright or hybrid engines, any of --vision, --screenshots, --record-video, --max-retries (when ≠ 3), --no-record triggers:
Ignored on the playwright engine: --vision, --screenshots, --record-video, --max-retries, --no-record
These options require --engine cdp.Sample “Starting browser agent” output
On the extension path, run echoes the forwarded values before starting. As of #4313 this includes the per-session Max steps line and the context-only --url label:
Starting browser agent
Goal: Search for the latest AI news and summarize
URL (context only; extension uses the active tab): https://www.google.com
Model: gpt-4o-mini
Max steps: 20--max-steps is now forwarded on the wire and honoured per-session by the bridge server. Previously the value you typed on run was silently replaced by the server default set at praisonai-browser start --max-steps; the server now reads message.get("max_steps", self.max_steps) and applies it for that session.
Video recording (CDP only)
--record-video captures the run to recording.webm. Reachable only on --engine cdp — on extension, playwright, and hybrid it is ignored with the warning above.
praisonai browser run "Book a table" --engine cdp --record-video- Output location. The file lands under the auto-created
~/.praisonai/browser_screenshots/<timestamp>/directory, or under--screenshots <dir>if you pass one. The📹 Recording to: …line only appears on the CDP engine. - Implicit vision →
gpt-4o.--record-videocurrently impliesenable_vision=True, so the model is upgraded togpt-4ofor the run. Billing note: expectgpt-4opricing for a recorded run even if you passed a cheaper--model.
Examples
# Default extension engine — drives your open Chrome tab
praisonai browser run "Search for PraisonAI on Google"
# CDP engine, headless
praisonai browser run "Search for AI news" --engine cdp --headless
# Vision-based detection (hybrid honours goal/url/model/max-steps only)
praisonai browser run "Find the login button" --engine cdp --vision
# Save per-step screenshots (CDP engine)
praisonai browser run "Fill the form" --engine cdp --screenshots ./screenshots
# Record video (CDP engine) — writes recording.webm, upgrades model to gpt-4o
praisonai browser run "Book a table" --engine cdp --record-videoTwo ways to start a session
praisonai browser run isn’t the only entry point — you can also start automation from the side panel inside Chrome. Only one at a time can drive the extension.
sequenceDiagram
participant SidePanel as Chrome Side Panel
participant Bridge as Bridge Server
participant Ext as Extension (CDP)
SidePanel->>Bridge: start_session (own websocket)
Bridge->>Bridge: extension_busy? → no
Bridge->>Ext: start_automation (same connection)
Ext-->>Bridge: observation / action / step
Bridge-->>SidePanel: session updates
classDef io fill:#8B0000,stroke:#7C90A0,color:#fff
classDef bridge fill:#189AB4,stroke:#7C90A0,color:#fff
classDef success fill:#10B981,stroke:#7C90A0,color:#fffThe side panel opens its own extension-origin WebSocket to the bridge and sends start_session on it. The bridge treats that connection as the extension itself (rather than self-excluding it as “no extension available”), so a side-panel task no longer fails with NO_EXTENSION on a single-extension setup.
While a side-panel session is running, /health reports extension_busy: true and active_session_id: <session_id>. A concurrent praisonai browser run in that state is rejected at the pre-flight check with:
Extension already running a session. Stop the side panel agent or wait for it to complete before starting another.
Stop the side panel agent and retry.Exit code 2. The reverse also holds: starting a side-panel run while run is active is rejected the same way.
Navigate
Navigate a browser tab to a URL:
praisonai browser navigate https://example.comScreenshot
Capture a screenshot of the current page:
praisonai browser screenshotWith options:
praisonai browser screenshot --output screenshot.pngPages
List all open browser pages/tabs:
praisonai browser pagesDOM
Get the DOM tree from a browser page:
praisonai browser domContent
Read page content as text:
praisonai browser contentJS / Execute
Execute JavaScript in a browser page:
praisonai browser js "document.title"praisonai browser execute "document.querySelector('h1').innerText"Sessions
List browser automation sessions:
praisonai browser sessionsSessions end in one of completed, failed, stopped, or cancelled. cancelled covers mid-run disconnects (extension quit, CLI Ctrl-C, tab closed) — all four now stamp ended_at in the SQLite store, so sessions and history show accurate durations for interrupted runs.
History
Show step-by-step history for a session:
praisonai browser history <session_id>Examples
Web Scraping Workflow
# Launch browser
praisonai browser launch
# Navigate to page
praisonai browser navigate https://news.ycombinator.com
# Get page content
praisonai browser content
# Take screenshot for verification
praisonai browser screenshot --output hn.pngUsing Browser Agent
# Run agent with a goal
praisonai browser run "Go to Hacker News and find the top 3 stories about AI"Browser Health Check
# Check if browser is properly configured
praisonai browser doctorChrome Management
Start Server
praisonai browser startChrome Subcommands
praisonai browser chrome <subcommand>Extension Subcommands
praisonai browser extension <subcommand>Single-action browser-tool commands
praisonai browser-tool <action> is a lightweight command group that drives praisonai_tools.BrowserBaseTool for one-shot actions — status, open, navigate, snapshot, screenshot, click, type, and profiles. It is distinct from the agent-driven praisonai browser run above.
As of PR #4943, these commands report success only when the action actually succeeded. BrowserBaseTool.run signals failure by returning a value (for example {"error": "Unknown action: click"}) rather than raising, so six of the eight subcommands used to print that value as Result: and exit 0 for work that never happened — screenshot --output even printed “saved” without ever opening the file. Every result now flows through a _require_success check and non-zero exit on failure.
Exit codes
Screenshot
Capture the current page. Without --output the result is printed; with --output <path> the image bytes are written to disk.
- Minimal use
bash praisonai browser-tool screenshot - Guarantee bytes on disk with –output
bash praisonai browser-tool screenshot --output /tmp/shot.png # On success → exit 0, /tmp/shot.png contains PNG bytes # On failure → non-zero exit, stderr names the failureThe tool’s image data is extracted (raw bytes, base64,
data:URI, or a file path it wrote) and written to<path>. If no image data comes back, the command fails loudly instead of printing a false “saved” line. - Pick an image format with –format
bash praisonai browser-tool screenshot --output /tmp/shot.jpg --format jpeg--formatdefaults topng; a non-default value is forwarded to the tool.
Failure mode before vs. after the fix:
# Before PR #4943
praisonai browser-tool screenshot --output /tmp/shot.png
# → EXIT 0 "Screenshot saved to: /tmp/shot.png"
# → but /tmp/shot.png does not exist
# After PR #4943
praisonai browser-tool screenshot --output /tmp/shot.png
# On success → EXIT 0, /tmp/shot.png contains PNG bytes
# On failure → non-zero exit, stderr names the failureSnapshot
Capture the page as text (--format aria default, or ai). With --output the text is written to a file.
praisonai browser-tool snapshot
praisonai browser-tool snapshot --output /tmp/snap.txt
praisonai browser-tool snapshot --format aiWhen the underlying action fails, the command exits non-zero and does not write the error dict into the file as page text — the old behaviour that produced a “snapshot” file full of {'error': ...}.
Click, Type, and Navigate
| Command | Flag | Semantics |
|---|---|---|
browser-tool click <selector> | --double | Double-click instead of a single click. |
browser-tool click <selector> | --profile <name> | Run against a named browser profile (default is implicit and not forwarded). |
browser-tool type <selector> <text> | --submit | Submit (press Enter) after typing. |
browser-tool type <selector> <text> | --profile <name> | Named profile as above. |
browser-tool navigate <url> | --profile <name> | Named profile as above. |
praisonai browser-tool click "#submit-button" --double
praisonai browser-tool type "#search" "hello world" --submit
praisonai browser-tool navigate https://example.com --profile chromeAn unknown action (for example a click an older tool build cannot honour) now exits non-zero with the tool’s error message, instead of printing {'error': 'Unknown action: click'} and exiting 0.
Status and Open
praisonai browser-tool status # human-readable
praisonai browser-tool status --json # machine-readable
praisonai browser-tool open https://example.com --headlessstatus --json emits a machine-readable object:
{
"available": true,
"profile": "default",
"status": "ready"
}open --headless requests a headless browser; the flag reaches the tool (or is reported as unsupported).
Environment Variables
| Variable | Description |
|---|---|
BROWSER_HEADLESS | Run headless by default (true/false) |
BROWSER_TIMEOUT | Default timeout in seconds |