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.

bash
praisonai-browser --help
praisonai-browser run "Go to google and search praisonai"

Inside the full wrapper the same commands work unchanged:

bash
praisonai browser run "Go to google and search praisonai"

Commands

bash
praisonai browser <command> [OPTIONS]
# or standalone:
praisonai-browser <command> [OPTIONS]
CommandDescription
startStart the browser automation server
launchLaunch Chrome with extension and optionally run a goal
runRun browser agent with a goal
navigateNavigate a browser tab to a URL
screenshotCapture a screenshot of a browser tab
pagesList all browser pages/tabs
tabsList and manage browser tabs
domGet DOM tree from a browser page
contentRead page content as text
consoleGet console logs from a page
jsExecute JavaScript in a page
executeExecute JavaScript in a browser tab
sessionsList browser automation sessions
historyShow step-by-step history for a session
clearClear session history
doctorBrowser health diagnostics
chromeChrome browser management
extensionChrome extension management
benchmarkBrowser automation benchmarks

Doctor

Check browser health and configuration:

bash
praisonai browser doctor

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

bash
praisonai browser doctor extension
praisonai browser doctor extension --server-port 8765   # custom bridge port
praisonai browser doctor extension --port 9222          # custom CDP debug port
OptionDefaultDescription
--port, -p9222Chrome CDP debug port (optional check)
--server-port8765Bridge server port (ground-truth /health check)

Two signals:

  1. Bridge connection (ground truth) — queries http://localhost:<server-port>/health and reads the extension_connections count. ✅ Extension connected to bridge (N connection(s)) means the extension is working, regardless of which Chrome profile it runs in.
  2. 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.

Diagram

Yes

No

No

Yes

Run: praisonai browser doctor extension

Bridge connection
extension_connections >= 1?

Extension is working – CDP result is optional info

Bridge server running?

praisonai browser start

Load extension manually – see Manual Extension Load


Launch

Launch Chrome with the PraisonAI extension:

bash
praisonai browser launch

With a goal to execute:

bash
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 (expect extension_connections >= 1).
  • --no-server — no bridge was started, so it tells you to run praisonai browser start first, then verify with praisonai browser doctor extension.

Run

Run a browser agent with a specific goal:

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

Diagram
Chrome ExtensionBridgeCLIUserChrome ExtensionBridgeCLIUserpraisonai browser run “…”GET /health (3s timeout)extension_connections >= 1WebSocket /ws + start_sessionstart_automationstatus:running, start_automation_sent:truestep / actionsession DB updatesStep 1… Step N… exit 0

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:

FailureMessageExit
Bridge unreachable or malformed bodyCannot reach PraisonAI Browser bridge2
No extension connectedBridge is up but no extension is connected2
Invalid health responseBridge returned an unexpected health response / Bridge returned an invalid connection count2

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 codeMeaningWhen
0SuccessSession completed, or you hit Ctrl+C
1Task failureSession ended in failed or stopped state
2Infra failureBridge unreachable, no extension, server error, or watchdog fired
3TimeoutSession 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      # unconfirmed

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

  1. Text-match — replaces the failing selector with one that matches the same visible text.
  2. Fallback-selector — swaps in a semantically equivalent selector (#login, [data-testid="login"], button[type="submit"], …).
  3. Click → navigate — if the target was a link, navigate to its href directly.

A step is considered failed when the injected JS reports the selector matched no element — for example:

json
{"success": false, "error": "selector matched no element: #nope"}

Reachability: --engine cdp and --engine hybrid. Extension mode uses its own retry path.

OptionDefaultDescription
--max-retries3Retries per action on failure, with alternative-selector strategies
Diagram

Yes

No

Yes

No

▶️ CDP action
click / type / clear_input

Selector matched?

✅ success=true

⚠️ success=false
selector matched no element

Retries left?

🔄 Try alternative selector
text-match → fallback → navigate

❌ Step failed
reported to LLM

Common failures

Bridge down

The bridge server is not running. Start it in a separate terminal, then verify:

bash
praisonai browser start --port 8765
curl http://localhost:8765/health
Cannot 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:

bash
curl http://127.0.0.1:8765/health    # expect extension_connections >= 1

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

bash
curl http://127.0.0.1:8765/health   # expect extension_busy: false

Options

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.

OptionDefaultDescriptionEffective on
goal (positional)—The task the agent should completeall engines
--url / -uhttps://www.google.comStart URLcdp, playwright, hybrid (context-only on extension)
--extension / -e—Extension dist pathextension only
--model / -mgpt-4o-miniLLM modelall engines
--max-steps20Maximum steps per sessionall engines (per-session since #4313)
--timeout / -t120Client-side timeout in secondsall engines
--headlessFalseRun headless (experimental)cdp, playwright, hybrid
--engineextensionAutomation engine: extension, cdp, playwright, hybrid—
--verbose / -vFalseVerbose loggingall engines
--debug / -dFalseDebug mode — show all eventsall engines
--max-retries3Retries per action with alternative selectorscdp
--visionFalseVision-based element detection (requires gpt-4o)cdp
--screenshots—Directory to save step screenshotscdp
--record / --no-record--recordRecord session to databasecdp for --no-record
--record-videoFalseRecord recording.webm of the sessioncdp

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.

Optionextension (default)cdpplaywrighthybrid
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/an/an/a
--no-record⚠️ ignored (warning)✅⚠️ ignored (warning)⚠️ ignored (warning)
--debug✅✅✅✅
--verbose / -v✅✅✅✅
--timeout✅ (client-side)✅✅✅

Which engine should I use?

Diagram

Yes

No

Yes

Yes

No

No

Yes

No

Need to drive your
already-open Chrome tab?

extension
default

Headless / CI /
server / no browser UI?

Need Firefox
or WebKit?

playwright

cdp

Want auto-fallback
if CDP unavailable?

hybrid

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.

bash
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-video currently implies enable_vision=True, so the model is upgraded to gpt-4o for the run. Billing note: expect gpt-4o pricing for a recorded run even if you passed a cheaper --model.

Examples

bash
# 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-video

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

Diagram
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:#fff

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

bash
praisonai browser navigate https://example.com

Screenshot

Capture a screenshot of the current page:

bash
praisonai browser screenshot

With options:

bash
praisonai browser screenshot --output screenshot.png

Pages

List all open browser pages/tabs:

bash
praisonai browser pages

DOM

Get the DOM tree from a browser page:

bash
praisonai browser dom

Content

Read page content as text:

bash
praisonai browser content

JS / Execute

Execute JavaScript in a browser page:

bash
praisonai browser js "document.title"
bash
praisonai browser execute "document.querySelector('h1').innerText"

Sessions

List browser automation sessions:

bash
praisonai browser sessions

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

bash
praisonai browser history <session_id>

Examples

Web Scraping Workflow

bash
# 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.png

Using Browser Agent

bash
# Run agent with a goal
praisonai browser run "Go to Hacker News and find the top 3 stories about AI"

Browser Health Check

bash
# Check if browser is properly configured
praisonai browser doctor

Chrome Management

Start Server

bash
praisonai browser start

Chrome Subcommands

bash
praisonai browser chrome <subcommand>

Extension Subcommands

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

Diagram

No

Yes

▶️ browser-tool action

tool returned
an error value?

✅ exit 0
print result

❌ exit non-zero
stderr names the failure

Exit codes

Screenshot

Capture the current page. Without --output the result is printed; with --output <path> the image bytes are written to disk.

  1. Minimal use
    bash
    praisonai browser-tool screenshot
  2. 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 failure

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

  3. Pick an image format with –format
    bash
    praisonai browser-tool screenshot --output /tmp/shot.jpg --format jpeg

    --format defaults to png; a non-default value is forwarded to the tool.

Failure mode before vs. after the fix:

bash
# 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 failure

Snapshot

Capture the page as text (--format aria default, or ai). With --output the text is written to a file.

bash
praisonai browser-tool snapshot
praisonai browser-tool snapshot --output /tmp/snap.txt
praisonai browser-tool snapshot --format ai

When 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

CommandFlagSemantics
browser-tool click <selector>--doubleDouble-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>--submitSubmit (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.
bash
praisonai browser-tool click "#submit-button" --double
praisonai browser-tool type "#search" "hello world" --submit
praisonai browser-tool navigate https://example.com --profile chrome

An 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

bash
praisonai browser-tool status            # human-readable
praisonai browser-tool status --json     # machine-readable
praisonai browser-tool open https://example.com --headless

status --json emits a machine-readable object:

json
{
  "available": true,
  "profile": "default",
  "status": "ready"
}

open --headless requests a headless browser; the flag reaches the tool (or is reported as unsupported).


Environment Variables

VariableDescription
BROWSER_HEADLESSRun headless by default (true/false)
BROWSER_TIMEOUTDefault timeout in seconds

Related