> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Browser CLI

> Control browser automation from the command line

<Note>
  This page documents the **`praisonai-browser` Python package** and the
  unpacked developer extension that connects to its bridge server.

  It does **not** describe the **PraisonAI Browser Agent** published on the
  Chrome Web Store. That is a separate, standalone build: it runs entirely
  locally, makes no network requests, connects to no bridge or model, and
  requests six permissions with no host permissions. See
  [chrome.praison.ai](https://chrome.praison.ai).
</Note>

Control browser automation for AI agents directly from the CLI. Launch Chrome, navigate pages, take screenshots, run browser agents, and more.

<Note>
  This command group ships in the `praisonai-browser` package. Running it inside `praisonai` triggers auto-install via the `ensure_praisonai_browser()` bootstrap helper. You can also invoke the standalone entry point directly with `praisonai-browser …`.
</Note>

## Standalone Entry Point

The `praisonai-browser` console script mirrors every `praisonai browser` subcommand.

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai-browser --help
praisonai-browser run "Go to google and search praisonai"
```

Inside the full wrapper the same commands work unchanged:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai browser run "Go to google and search praisonai"
```

## Commands

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
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:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
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 theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
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:**

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.

<Note>
  `extension_connections` counts only clients whose WebSocket `Origin` starts with `chrome-extension://`. Servers older than PraisonAI #3115 don't return this field; the CLI falls back to the total `connections` count in that case (slightly noisier, but still works).
</Note>

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    Start[Run: praisonai browser doctor extension] --> Q1{Bridge connection<br/>extension_connections >= 1?}
    Q1 -->|Yes| OK[Extension is working - CDP result is optional info]
    Q1 -->|No| Q2{Bridge server running?}
    Q2 -->|No| Fix1[praisonai browser start]
    Q2 -->|Yes| Fix2[Load extension manually - see Manual Extension Load]

    classDef start fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef check fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef ok fill:#10B981,stroke:#7C90A0,color:#fff
    classDef fix fill:#8B0000,stroke:#7C90A0,color:#fff

    class Start start
    class Q1,Q2 check
    class OK ok
    class Fix1,Fix2 fix
```

<Tip>
  Run `praisonai browser doctor server` before a session to confirm the bridge is up — this avoids the bridge-unreachable message on `run`.
</Tip>

***

## Launch

Launch Chrome with the PraisonAI extension:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai browser launch
```

With a goal to execute:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
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`.

<Warning>
  With `--no-server` there is no bridge listening on port `8765`, so a `/health` check will fail until you start the server. Start it first, then load the extension into your daily "Work" Chrome.
</Warning>

***

## Run

Run a browser agent with a specific goal:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
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.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant User
    participant CLI
    participant Bridge
    participant Ext as Chrome Extension
    User->>CLI: praisonai browser run "…"
    CLI->>Bridge: GET /health (3s timeout)
    Bridge-->>CLI: extension_connections >= 1
    CLI->>Bridge: WebSocket /ws + start_session
    Bridge->>Ext: start_automation
    Bridge-->>CLI: status:running, start_automation_sent:true
    Ext-->>Bridge: step / action
    Bridge-->>CLI: session DB updates
    CLI-->>User: Step 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:

| 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      # 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 theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{"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 |

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    Step[▶️ CDP action<br/>click / type / clear_input] --> Q{Selector matched?}
    Q -->|Yes| OK[✅ success=true]
    Q -->|No| Err[⚠️ success=false<br/>selector matched no element]
    Err --> R{Retries left?}
    R -->|Yes| Alt[🔄 Try alternative selector<br/>text-match → fallback → navigate]
    Alt --> Step
    R -->|No| Fail[❌ Step failed<br/>reported to LLM]

    classDef start fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef check fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef ok fill:#10B981,stroke:#7C90A0,color:#fff
    classDef bad fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef process fill:#189AB4,stroke:#7C90A0,color:#fff

    class Step start
    class Q,R check
    class OK ok
    class Err,Fail bad
    class Alt process
```

### Common failures

<AccordionGroup>
  <Accordion title="Bridge down">
    The bridge server is not running. Start it in a separate terminal, then verify:

    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    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).
    ```

    <Note>
      The failing layer is the **local bridge server** on port 8765, not the site named in `--url`. This message replaces the raw `WinError 1225` (Windows), `errno 111` (Linux), and `errno 61` (macOS) connection-refused errors, so those still land here when searched.
    </Note>
  </Accordion>

  <Accordion title="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 theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    curl http://127.0.0.1:8765/health    # expect extension_connections >= 1
    ```

    For extension-free automation, use `--engine cdp` instead.
  </Accordion>

  <Accordion title="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 theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    curl http://127.0.0.1:8765/health   # expect extension_busy: false
    ```
  </Accordion>
</AccordionGroup>

### Options

`praisonai browser run` accepts 14 options. Most only take effect on the CDP engine — see the [Engine × Option matrix](#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](#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?

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    Q{Need to drive your<br/>already-open Chrome tab?} -->|Yes| E1[extension<br/>default]
    Q -->|No| Q2{Headless / CI /<br/>server / no browser UI?}
    Q2 -->|Yes| Q3{Need Firefox<br/>or WebKit?}
    Q3 -->|Yes| E3[playwright]
    Q3 -->|No| E2[cdp]
    Q2 -->|No| Q4{Want auto-fallback<br/>if CDP unavailable?}
    Q4 -->|Yes| E4[hybrid]
    Q4 -->|No| E2

    classDef question fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef engine fill:#189AB4,stroke:#7C90A0,color:#fff
    class Q,Q2,Q3,Q4 question
    class E1,E2,E3,E4 engine
```

### 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.
```

<Note>
  Only the flags you actually set appear in the list, so the exact string depends on your invocation. Grep your terminal for `Ignored on the` to confirm whether a flag took effect.
</Note>

### 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 theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
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 theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# 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.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
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 theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai browser navigate https://example.com
```

***

## Screenshot

Capture a screenshot of the current page:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai browser screenshot
```

With options:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai browser screenshot --output screenshot.png
```

<Note>
  The single-action `praisonai browser-tool screenshot` (a separate command group, below) is the surface hardened by PR #4943 — it now guarantees bytes on disk when you pass `--output` and fails loudly if the tool returned no image data. See [Single-action browser-tool commands](#single-action-browser-tool-commands).
</Note>

***

## Pages

List all open browser pages/tabs:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai browser pages
```

***

## DOM

Get the DOM tree from a browser page:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai browser dom
```

***

## Content

Read page content as text:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai browser content
```

***

## JS / Execute

Execute JavaScript in a browser page:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai browser js "document.title"
```

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai browser execute "document.querySelector('h1').innerText"
```

***

## Sessions

List browser automation sessions:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
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 theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai browser history <session_id>
```

***

## Examples

### Web Scraping Workflow

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# 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 theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Run agent with a goal
praisonai browser run "Go to Hacker News and find the top 3 stories about AI"
```

### Browser Health Check

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Check if browser is properly configured
praisonai browser doctor
```

***

## Chrome Management

### Start Server

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai browser start
```

### Chrome Subcommands

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai browser chrome <subcommand>
```

### Extension Subcommands

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
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](https://github.com/MervinPraison/PraisonAI/pull/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.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    Run[▶️ browser-tool action] --> Check{tool returned<br/>an error value?}
    Check -->|No| OK[✅ exit 0<br/>print result]
    Check -->|Yes| Err[❌ exit non-zero<br/>stderr names the failure]

    classDef start fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef check fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef ok fill:#10B981,stroke:#7C90A0,color:#fff
    classDef bad fill:#8B0000,stroke:#7C90A0,color:#fff

    class Run start
    class Check check
    class OK ok
    class Err bad
```

### Exit codes

<Note>
  As of PR #4943, **every `browser-tool` subcommand exits non-zero on tool-level failure.** Previously most exited `0` with an error string printed as the result. A `0` exit now means the action reported success.
</Note>

### Screenshot

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

<Steps>
  <Step title="Minimal use">
    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    praisonai browser-tool screenshot
    ```
  </Step>

  <Step title="Guarantee bytes on disk with --output">
    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    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.
  </Step>

  <Step title="Pick an image format with --format">
    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    praisonai browser-tool screenshot --output /tmp/shot.jpg --format jpeg
    ```

    `--format` defaults to `png`; a non-default value is forwarded to the tool.
  </Step>
</Steps>

Failure mode before vs. after the fix:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# 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 theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
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

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

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
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 theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
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 theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "available": true,
  "profile": "default",
  "status": "ready"
}
```

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

<Note>
  **Compatibility guarantee.** Flags left at their defaults are **not** forwarded to `BrowserBaseTool.run`, so a `praisonai-tools` build with a narrower `run()` signature keeps working unchanged. Only a flag you actually set is forwarded — and if the installed tool cannot accept it, the command reports it as unsupported rather than silently dropping it.
</Note>

***

## Environment Variables

| Variable           | Description                              |
| ------------------ | ---------------------------------------- |
| `BROWSER_HEADLESS` | Run headless by default (`true`/`false`) |
| `BROWSER_TIMEOUT`  | Default timeout in seconds               |

***

## Related

<CardGroup cols={2}>
  <Card title="Bot CLI" icon="robot" href="/docs/cli/bot">
    Deploy messaging bots
  </Card>

  <Card title="Sandbox CLI" icon="box" href="/docs/cli/sandbox">
    Sandbox container management
  </Card>
</CardGroup>
