feat: Add browserless.io as a browser provider (#502)

* feat: Add browserless as a hosted option + boolean env-parsing utility

* Add ensureDomainFilter, sanitizeExistingPage and move parseBooleanParam

* Add docs in relevant places, fix utils, rename of API env var

* Update readme

* Fix env variable name in readme

* Cleanup session stop urls when errors happen

* Fix browserlessStopUrl not being assigned in happy path
This commit is contained in:
Joel Griffith
2026-03-13 00:45:02 -05:00
committed by GitHub
parent fb7185d860
commit b9a24df40f
6 changed files with 279 additions and 74 deletions
+112 -56
View File
@@ -165,6 +165,7 @@ agent-browser find nth <n> <sel> <action> [value] # Nth match
**Options:** `--name <name>` (filter role by accessible name), `--exact` (require exact text match) **Options:** `--name <name>` (filter role by accessible name), `--exact` (require exact text match)
**Examples:** **Examples:**
```bash ```bash
agent-browser find role button click --name "Submit" agent-browser find role button click --name "Submit"
agent-browser find text "Sign In" click agent-browser find text "Sign In" click
@@ -375,6 +376,7 @@ agent-browser session
``` ```
Each session has its own: Each session has its own:
- Browser instance - Browser instance
- Cookies and storage - Cookies and storage
- Navigation history - Navigation history
@@ -396,6 +398,7 @@ AGENT_BROWSER_PROFILE=~/.myapp-profile agent-browser open myapp.com
``` ```
The profile directory stores: The profile directory stores:
- Cookies and localStorage - Cookies and localStorage
- IndexedDB data - IndexedDB data
- Service workers - Service workers
@@ -432,10 +435,10 @@ export AGENT_BROWSER_ENCRYPTION_KEY=<64-char-hex-key>
agent-browser --session-name secure open example.com agent-browser --session-name secure open example.com
``` ```
| Variable | Description | | Variable | Description |
|----------|-------------| | --------------------------------- | -------------------------------------------------- |
| `AGENT_BROWSER_SESSION_NAME` | Auto-save/load state persistence name | | `AGENT_BROWSER_SESSION_NAME` | Auto-save/load state persistence name |
| `AGENT_BROWSER_ENCRYPTION_KEY` | 64-char hex key for AES-256-GCM encryption | | `AGENT_BROWSER_ENCRYPTION_KEY` | 64-char hex key for AES-256-GCM encryption |
| `AGENT_BROWSER_STATE_EXPIRE_DAYS` | Auto-delete states older than N days (default: 30) | | `AGENT_BROWSER_STATE_EXPIRE_DAYS` | Auto-delete states older than N days (default: 30) |
## Security ## Security
@@ -449,14 +452,14 @@ agent-browser includes security features for safe AI agent deployments. All feat
- **Action Confirmation** -- Require explicit approval for sensitive action categories: `--confirm-actions eval,download` - **Action Confirmation** -- Require explicit approval for sensitive action categories: `--confirm-actions eval,download`
- **Output Length Limits** -- Prevent context flooding: `--max-output 50000` - **Output Length Limits** -- Prevent context flooding: `--max-output 50000`
| Variable | Description | | Variable | Description |
|----------|-------------| | ----------------------------------- | ---------------------------------------- |
| `AGENT_BROWSER_CONTENT_BOUNDARIES` | Wrap page output in boundary markers | | `AGENT_BROWSER_CONTENT_BOUNDARIES` | Wrap page output in boundary markers |
| `AGENT_BROWSER_MAX_OUTPUT` | Max characters for page output | | `AGENT_BROWSER_MAX_OUTPUT` | Max characters for page output |
| `AGENT_BROWSER_ALLOWED_DOMAINS` | Comma-separated allowed domain patterns | | `AGENT_BROWSER_ALLOWED_DOMAINS` | Comma-separated allowed domain patterns |
| `AGENT_BROWSER_ACTION_POLICY` | Path to action policy JSON file | | `AGENT_BROWSER_ACTION_POLICY` | Path to action policy JSON file |
| `AGENT_BROWSER_CONFIRM_ACTIONS` | Action categories requiring confirmation | | `AGENT_BROWSER_CONFIRM_ACTIONS` | Action categories requiring confirmation |
| `AGENT_BROWSER_CONFIRM_INTERACTIVE` | Enable interactive confirmation prompts | | `AGENT_BROWSER_CONFIRM_INTERACTIVE` | Enable interactive confirmation prompts |
See [Security documentation](https://agent-browser.dev/security) for details. See [Security documentation](https://agent-browser.dev/security) for details.
@@ -474,13 +477,13 @@ agent-browser snapshot -s "#main" # Scope to CSS selector
agent-browser snapshot -i -c -d 5 # Combine options agent-browser snapshot -i -c -d 5 # Combine options
``` ```
| Option | Description | | Option | Description |
|--------|-------------| | ---------------------- | ----------------------------------------------------------------------- |
| `-i, --interactive` | Only show interactive elements (buttons, links, inputs) | | `-i, --interactive` | Only show interactive elements (buttons, links, inputs) |
| `-C, --cursor` | Include cursor-interactive elements (cursor:pointer, onclick, tabindex) | | `-C, --cursor` | Include cursor-interactive elements (cursor:pointer, onclick, tabindex) |
| `-c, --compact` | Remove empty structural elements | | `-c, --compact` | Remove empty structural elements |
| `-d, --depth <n>` | Limit tree depth | | `-d, --depth <n>` | Limit tree depth |
| `-s, --selector <sel>` | Scope to CSS selector | | `-s, --selector <sel>` | Scope to CSS selector |
The `-C` flag is useful for modern web apps that use custom clickable elements (divs, spans) instead of standard buttons/links. The `-C` flag is useful for modern web apps that use custom clickable elements (divs, spans) instead of standard buttons/links.
@@ -596,8 +599,8 @@ export AGENT_BROWSER_DEFAULT_TIMEOUT=45000
> **Note:** Setting this above 30000 (30s) may cause EAGAIN errors on slow operations because the CLI's read timeout will expire before Playwright responds. The CLI retries transient errors automatically, but response times will increase. > **Note:** Setting this above 30000 (30s) may cause EAGAIN errors on slow operations because the CLI's read timeout will expire before Playwright responds. The CLI retries transient errors automatically, but response times will increase.
| Variable | Description | | Variable | Description |
|----------|-------------| | ------------------------------- | ------------------------------------------------- |
| `AGENT_BROWSER_DEFAULT_TIMEOUT` | Default Playwright timeout in ms (default: 25000) | | `AGENT_BROWSER_DEFAULT_TIMEOUT` | Default Playwright timeout in ms (default: 25000) |
## Selectors ## Selectors
@@ -623,6 +626,7 @@ agent-browser hover @e4 # Hover the link
``` ```
**Why use refs?** **Why use refs?**
- **Deterministic**: Ref points to exact element from snapshot - **Deterministic**: Ref points to exact element from snapshot
- **Fast**: No DOM re-query needed - **Fast**: No DOM re-query needed
- **AI-friendly**: Snapshot + ref workflow is optimal for LLMs - **AI-friendly**: Snapshot + ref workflow is optimal for LLMs
@@ -723,6 +727,7 @@ agent-browser open other-site.com
``` ```
This is useful for: This is useful for:
- **Skipping login flows** - Authenticate via headers instead of UI - **Skipping login flows** - Authenticate via headers instead of UI
- **Switching users** - Start new sessions with different auth tokens - **Switching users** - Start new sessions with different auth tokens
- **API testing** - Access protected endpoints directly - **API testing** - Access protected endpoints directly
@@ -744,6 +749,7 @@ agent-browser set headers '{"X-Custom-Header": "value"}'
## Custom Browser Executable ## Custom Browser Executable
Use a custom browser executable instead of the bundled Chromium. This is useful for: Use a custom browser executable instead of the bundled Chromium. This is useful for:
- **Serverless deployment**: Use lightweight Chromium builds like `@sparticuz/chromium` (~50MB vs ~684MB) - **Serverless deployment**: Use lightweight Chromium builds like `@sparticuz/chromium` (~50MB vs ~684MB)
- **System browsers**: Use an existing Chrome/Chromium installation - **System browsers**: Use an existing Chrome/Chromium installation
- **Custom builds**: Use modified browser builds - **Custom builds**: Use modified browser builds
@@ -804,6 +810,7 @@ agent-browser screenshot report.png
``` ```
The `--allow-file-access` flag adds Chromium flags (`--allow-file-access-from-files`, `--allow-file-access`) that allow `file://` URLs to: The `--allow-file-access` flag adds Chromium flags (`--allow-file-access-from-files`, `--allow-file-access`) that allow `file://` URLs to:
- Load and render local files - Load and render local files
- Access other local files via JavaScript (XHR, fetch) - Access other local files via JavaScript (XHR, fetch)
- Load local resources (images, scripts, stylesheets) - Load local resources (images, scripts, stylesheets)
@@ -831,10 +838,12 @@ agent-browser --cdp "wss://your-browser-service.com/cdp?token=..." snapshot
``` ```
The `--cdp` flag accepts either: The `--cdp` flag accepts either:
- A port number (e.g., `9222`) for local connections via `http://localhost:{port}` - A port number (e.g., `9222`) for local connections via `http://localhost:{port}`
- A full WebSocket URL (e.g., `wss://...` or `ws://...`) for remote browser services - A full WebSocket URL (e.g., `wss://...` or `ws://...`) for remote browser services
This enables control of: This enables control of:
- Electron apps - Electron apps
- Chrome/Chromium instances with remote debugging - Chrome/Chromium instances with remote debugging
- WebView2 applications - WebView2 applications
@@ -854,10 +863,12 @@ AGENT_BROWSER_AUTO_CONNECT=1 agent-browser snapshot
``` ```
Auto-connect discovers Chrome by: Auto-connect discovers Chrome by:
1. Reading Chrome's `DevToolsActivePort` file from the default user data directory 1. Reading Chrome's `DevToolsActivePort` file from the default user data directory
2. Falling back to probing common debugging ports (9222, 9229) 2. Falling back to probing common debugging ports (9222, 9229)
This is useful when: This is useful when:
- Chrome 144+ has remote debugging enabled via `chrome://inspect/#remote-debugging` (which uses a dynamic port) - Chrome 144+ has remote debugging enabled via `chrome://inspect/#remote-debugging` (which uses a dynamic port)
- You want a zero-configuration connection to your existing browser - You want a zero-configuration connection to your existing browser
- You don't want to track which port Chrome is using - You don't want to track which port Chrome is using
@@ -881,6 +892,7 @@ This starts a WebSocket server on the specified port that streams the browser vi
Connect to `ws://localhost:9223` to receive frames and send input: Connect to `ws://localhost:9223` to receive frames and send input:
**Receive frames:** **Receive frames:**
```json ```json
{ {
"type": "frame", "type": "frame",
@@ -897,6 +909,7 @@ Connect to `ws://localhost:9223` to receive frames and send input:
``` ```
**Send mouse events:** **Send mouse events:**
```json ```json
{ {
"type": "input_mouse", "type": "input_mouse",
@@ -909,6 +922,7 @@ Connect to `ws://localhost:9223` to receive frames and send input:
``` ```
**Send keyboard events:** **Send keyboard events:**
```json ```json
{ {
"type": "input_keyboard", "type": "input_keyboard",
@@ -919,6 +933,7 @@ Connect to `ws://localhost:9223` to receive frames and send input:
``` ```
**Send touch events:** **Send touch events:**
```json ```json
{ {
"type": "input_touch", "type": "input_touch",
@@ -939,16 +954,19 @@ await browser.launch({ headless: true });
await browser.navigate('https://example.com'); await browser.navigate('https://example.com');
// Start screencast // Start screencast
await browser.startScreencast((frame) => { await browser.startScreencast(
// frame.data is base64-encoded image (frame) => {
// frame.metadata contains viewport info // frame.data is base64-encoded image
console.log('Frame received:', frame.metadata.deviceWidth, 'x', frame.metadata.deviceHeight); // frame.metadata contains viewport info
}, { console.log('Frame received:', frame.metadata.deviceWidth, 'x', frame.metadata.deviceHeight);
format: 'jpeg', },
quality: 80, {
maxWidth: 1280, format: 'jpeg',
maxHeight: 720, quality: 80,
}); maxWidth: 1280,
maxHeight: 720,
}
);
// Inject mouse events // Inject mouse events
await browser.injectMouseEvent({ await browser.injectMouseEvent({
@@ -1000,18 +1018,18 @@ agent-browser open example.com
Or add to your config file (`agent-browser.json`): Or add to your config file (`agent-browser.json`):
```json ```json
{"native": true} { "native": true }
``` ```
### What's Different ### What's Different
| | Default (Node.js) | Native (`--native`) | | | Default (Node.js) | Native (`--native`) |
|---|---|---| | ------------------- | --------------------------- | -------------------------------- |
| **Runtime** | Node.js + Playwright | Pure Rust binary | | **Runtime** | Node.js + Playwright | Pure Rust binary |
| **Protocol** | Playwright protocol | Direct CDP / WebDriver | | **Protocol** | Playwright protocol | Direct CDP / WebDriver |
| **Install size** | Larger (Node.js + npm deps) | Smaller (single binary) | | **Install size** | Larger (Node.js + npm deps) | Smaller (single binary) |
| **Browser support** | Chromium, Firefox, WebKit | Chromium, Safari (via WebDriver) | | **Browser support** | Chromium, Firefox, WebKit | Chromium, Safari (via WebDriver) |
| **Stability** | Stable | Experimental | | **Stability** | Stable | Experimental |
### Known Limitations ### Known Limitations
@@ -1021,13 +1039,13 @@ Or add to your config file (`agent-browser.json`):
## Platforms ## Platforms
| Platform | Binary | Fallback | | Platform | Binary | Fallback |
|----------|--------|----------| | ----------- | ----------- | -------- |
| macOS ARM64 | Native Rust | Node.js | | macOS ARM64 | Native Rust | Node.js |
| macOS x64 | Native Rust | Node.js | | macOS x64 | Native Rust | Node.js |
| Linux ARM64 | Native Rust | Node.js | | Linux ARM64 | Native Rust | Node.js |
| Linux x64 | Native Rust | Node.js | | Linux x64 | Native Rust | Node.js |
| Windows x64 | Native Rust | Node.js | | Windows x64 | Native Rust | Node.js |
## Usage with AI Agents ## Usage with AI Agents
@@ -1071,6 +1089,7 @@ For more consistent results, add to your project or global instructions file:
Use `agent-browser` for web automation. Run `agent-browser --help` for all commands. Use `agent-browser` for web automation. Run `agent-browser --help` for all commands.
Core workflow: Core workflow:
1. `agent-browser open <url>` - Navigate to page 1. `agent-browser open <url>` - Navigate to page
2. `agent-browser snapshot -i` - Get interactive elements with refs (@e1, @e2) 2. `agent-browser snapshot -i` - Get interactive elements with refs (@e1, @e2)
3. `agent-browser click @e1` / `fill @e2 "text"` - Interact using refs 3. `agent-browser click @e1` / `fill @e2 "text"` - Interact using refs
@@ -1122,11 +1141,11 @@ export AGENT_BROWSER_IOS_DEVICE="iPhone 16 Pro"
agent-browser open https://example.com agent-browser open https://example.com
``` ```
| Variable | Description | | Variable | Description |
|----------|-------------| | -------------------------- | ----------------------------------------------- |
| `AGENT_BROWSER_PROVIDER` | Set to `ios` to enable iOS mode | | `AGENT_BROWSER_PROVIDER` | Set to `ios` to enable iOS mode |
| `AGENT_BROWSER_IOS_DEVICE` | Device name (e.g., "iPhone 16 Pro", "iPad Pro") | | `AGENT_BROWSER_IOS_DEVICE` | Device name (e.g., "iPhone 16 Pro", "iPad Pro") |
| `AGENT_BROWSER_IOS_UDID` | Device UDID (alternative to device name) | | `AGENT_BROWSER_IOS_UDID` | Device UDID (alternative to device name) |
**Supported devices:** All iOS Simulators available in Xcode (iPhones, iPads), plus real iOS devices. **Supported devices:** All iOS Simulators available in Xcode (iPhones, iPads), plus real iOS devices.
@@ -1137,6 +1156,7 @@ agent-browser open https://example.com
Appium also supports real iOS devices connected via USB. This requires additional one-time setup: Appium also supports real iOS devices connected via USB. This requires additional one-time setup:
**1. Get your device UDID:** **1. Get your device UDID:**
```bash ```bash
xcrun xctrace list devices xcrun xctrace list devices
# or # or
@@ -1144,6 +1164,7 @@ system_profiler SPUSBDataType | grep -A 5 "iPhone\|iPad"
``` ```
**2. Sign WebDriverAgent (one-time):** **2. Sign WebDriverAgent (one-time):**
```bash ```bash
# Open the WebDriverAgent Xcode project # Open the WebDriverAgent Xcode project
cd ~/.appium/node_modules/appium-xcuitest-driver/node_modules/appium-webdriveragent cd ~/.appium/node_modules/appium-xcuitest-driver/node_modules/appium-webdriveragent
@@ -1151,12 +1172,14 @@ open WebDriverAgent.xcodeproj
``` ```
In Xcode: In Xcode:
- Select the `WebDriverAgentRunner` target - Select the `WebDriverAgentRunner` target
- Go to Signing & Capabilities - Go to Signing & Capabilities
- Select your Team (requires Apple Developer account, free tier works) - Select your Team (requires Apple Developer account, free tier works)
- Let Xcode manage signing automatically - Let Xcode manage signing automatically
**3. Use with agent-browser:** **3. Use with agent-browser:**
```bash ```bash
# Connect device via USB, then: # Connect device via USB, then:
agent-browser -p ios --device "<DEVICE_UDID>" open https://example.com agent-browser -p ios --device "<DEVICE_UDID>" open https://example.com
@@ -1166,11 +1189,44 @@ agent-browser -p ios --device "John's iPhone" open https://example.com
``` ```
**Real device notes:** **Real device notes:**
- First run installs WebDriverAgent to the device (may require Trust prompt) - First run installs WebDriverAgent to the device (may require Trust prompt)
- Device must be unlocked and connected via USB - Device must be unlocked and connected via USB
- Slightly slower initial connection than simulator - Slightly slower initial connection than simulator
- Tests against real Safari performance and behavior - Tests against real Safari performance and behavior
### Browserless
[Browserless](https://browserless.io) provides cloud browser infrastructure with a Sessions API. Use it when running agent-browser in environments where a local browser isn't available.
To enable Browserless, use the `-p` flag:
```bash
export BROWSERLESS_API_KEY="your-api-token"
agent-browser -p browserless open https://example.com
```
Or use environment variables for CI/scripts:
```bash
export AGENT_BROWSER_PROVIDER=browserless
export BROWSERLESS_API_KEY="your-api-token"
agent-browser open https://example.com
```
Optional configuration via environment variables:
| Variable | Description | Default |
| -------------------------- | ------------------------------------------------ | --------------------------------------- |
| `BROWSERLESS_API_URL` | Base API URL (for custom regions or self-hosted) | `https://production-sfo.browserless.io` |
| `BROWSERLESS_BROWSER_TYPE` | Type of browser to use (chromium or chrome) | chromium |
| `BROWSERLESS_TTL` | Session TTL in milliseconds | `300000` |
| `BROWSERLESS_STEALTH` | Enable stealth mode (`true`/`false`) | `true` |
When enabled, agent-browser connects to a Browserless cloud session instead of launching a local browser. All commands work identically.
Get your API token from the [Browserless Dashboard](https://browserless.io).
### Browserbase ### Browserbase
[Browserbase](https://browserbase.com) provides remote browser infrastructure to make deployment of agentic browsing agents easy. Use it when running the agent-browser CLI in an environment where a local browser isn't feasible. [Browserbase](https://browserbase.com) provides remote browser infrastructure to make deployment of agentic browsing agents easy. Use it when running the agent-browser CLI in an environment where a local browser isn't feasible.
@@ -1238,12 +1294,12 @@ agent-browser open https://example.com
Optional configuration via environment variables: Optional configuration via environment variables:
| Variable | Description | Default | | Variable | Description | Default |
|----------|-------------|---------| | ------------------------ | -------------------------------------------------------------------------------- | ------- |
| `KERNEL_HEADLESS` | Run browser in headless mode (`true`/`false`) | `false` | | `KERNEL_HEADLESS` | Run browser in headless mode (`true`/`false`) | `false` |
| `KERNEL_STEALTH` | Enable stealth mode to avoid bot detection (`true`/`false`) | `true` | | `KERNEL_STEALTH` | Enable stealth mode to avoid bot detection (`true`/`false`) | `true` |
| `KERNEL_TIMEOUT_SECONDS` | Session timeout in seconds | `300` | | `KERNEL_TIMEOUT_SECONDS` | Session timeout in seconds | `300` |
| `KERNEL_PROFILE_NAME` | Browser profile name for persistent cookies/logins (created if it doesn't exist) | (none) | | `KERNEL_PROFILE_NAME` | Browser profile name for persistent cookies/logins (created if it doesn't exist) | (none) |
When enabled, agent-browser connects to a Kernel cloud session instead of launching a local browser. All commands work identically. When enabled, agent-browser connects to a Kernel cloud session instead of launching a local browser. All commands work identically.
+2 -2
View File
@@ -2484,7 +2484,7 @@ Options:
e.g., --proxy-bypass "localhost,*.internal.com" e.g., --proxy-bypass "localhost,*.internal.com"
--ignore-https-errors Ignore HTTPS certificate errors --ignore-https-errors Ignore HTTPS certificate errors
--allow-file-access Allow file:// URLs to access local files (Chromium only) --allow-file-access Allow file:// URLs to access local files (Chromium only)
-p, --provider <name> Browser provider: ios, browserbase, kernel, browseruse -p, --provider <name> Browser provider: ios, browserbase, kernel, browseruse, browserless
--device <name> iOS device name (e.g., "iPhone 15 Pro") --device <name> iOS device name (e.g., "iPhone 15 Pro")
--json JSON output --json JSON output
--full, -f Full page screenshot --full, -f Full page screenshot
@@ -2538,7 +2538,7 @@ Environment:
AGENT_BROWSER_ANNOTATE Annotated screenshot with numbered labels and legend AGENT_BROWSER_ANNOTATE Annotated screenshot with numbered labels and legend
AGENT_BROWSER_DEBUG Debug output AGENT_BROWSER_DEBUG Debug output
AGENT_BROWSER_IGNORE_HTTPS_ERRORS Ignore HTTPS certificate errors AGENT_BROWSER_IGNORE_HTTPS_ERRORS Ignore HTTPS certificate errors
AGENT_BROWSER_PROVIDER Browser provider (ios, browserbase, kernel, browseruse) AGENT_BROWSER_PROVIDER Browser provider (ios, browserbase, kernel, browseruse, browserless)
AGENT_BROWSER_AUTO_CONNECT Auto-discover and connect to running Chrome AGENT_BROWSER_AUTO_CONNECT Auto-discover and connect to running Chrome
AGENT_BROWSER_ALLOW_FILE_ACCESS Allow file:// URLs to access local files AGENT_BROWSER_ALLOW_FILE_ACCESS Allow file:// URLs to access local files
AGENT_BROWSER_COLOR_SCHEME Color scheme preference (dark, light, no-preference) AGENT_BROWSER_COLOR_SCHEME Color scheme preference (dark, light, no-preference)
+5 -1
View File
@@ -94,7 +94,7 @@ This enables control of:
<tbody> <tbody>
<tr><td><code>--session &lt;name&gt;</code></td><td>Use isolated session</td></tr> <tr><td><code>--session &lt;name&gt;</code></td><td>Use isolated session</td></tr>
<tr><td><code>--profile &lt;path&gt;</code></td><td>Persistent browser profile directory</td></tr> <tr><td><code>--profile &lt;path&gt;</code></td><td>Persistent browser profile directory</td></tr>
<tr><td><code>-p &lt;provider&gt;</code></td><td>Cloud browser provider (<code>browserbase</code>, <code>browseruse</code>, <code>kernel</code>)</td></tr> <tr><td><code>-p &lt;provider&gt;</code></td><td>Cloud browser provider (<code>browserbase</code>, <code>browseruse</code>, <code>kernel</code>, <code>browserless</code>)</td></tr>
<tr><td><code>--headers &lt;json&gt;</code></td><td>HTTP headers scoped to origin</td></tr> <tr><td><code>--headers &lt;json&gt;</code></td><td>HTTP headers scoped to origin</td></tr>
<tr><td><code>--executable-path</code></td><td>Custom browser executable</td></tr> <tr><td><code>--executable-path</code></td><td>Custom browser executable</td></tr>
<tr><td><code>--args &lt;args&gt;</code></td><td>Browser launch args (comma-separated)</td></tr> <tr><td><code>--args &lt;args&gt;</code></td><td>Browser launch args (comma-separated)</td></tr>
@@ -130,6 +130,10 @@ agent-browser -p browseruse open https://example.com
export KERNEL_API_KEY="your-api-key" export KERNEL_API_KEY="your-api-key"
agent-browser -p kernel open https://example.com agent-browser -p kernel open https://example.com
# Browserless
export BROWSERLESS_API_KEY="your-api-key"
agent-browser -p browserless open https://example.com
# Or via environment variable # Or via environment variable
export AGENT_BROWSER_PROVIDER=browserbase export AGENT_BROWSER_PROVIDER=browserbase
agent-browser open https://example.com agent-browser open https://example.com
+1 -1
View File
@@ -290,7 +290,7 @@ agent-browser reload # Reload page
--proxy-bypass <hosts> # Hosts to bypass proxy --proxy-bypass <hosts> # Hosts to bypass proxy
--ignore-https-errors # Ignore HTTPS certificate errors --ignore-https-errors # Ignore HTTPS certificate errors
--allow-file-access # Allow file:// URLs to access local files (Chromium only) --allow-file-access # Allow file:// URLs to access local files (Chromium only)
-p, --provider <name> # Browser provider (ios, browserbase, kernel, browseruse) -p, --provider <name> # Browser provider (ios, browserbase, kernel, browseruse, browserless)
--device <name> # iOS device name (e.g., "iPhone 15 Pro") --device <name> # iOS device name (e.g., "iPhone 15 Pro")
--json # JSON output (for scripts) --json # JSON output (for scripts)
--full, -f # Full page screenshot --full, -f # Full page screenshot
+17 -14
View File
@@ -388,8 +388,9 @@ export AGENT_BROWSER_ACTION_POLICY=./policy.json
``` ```
Example `policy.json`: Example `policy.json`:
```json ```json
{"default": "deny", "allow": ["navigate", "snapshot", "click", "scroll", "wait", "get"]} { "default": "deny", "allow": ["navigate", "snapshot", "click", "scroll", "wait", "get"] }
``` ```
Auth vault operations (`auth login`, etc.) bypass action policy but domain allowlist still applies. Auth vault operations (`auth login`, etc.) bypass action policy but domain allowlist still applies.
@@ -503,6 +504,7 @@ agent-browser click @e2 # Click using ref from annotated screenshot
``` ```
Use annotated screenshots when: Use annotated screenshots when:
- The page has unlabeled icon buttons or visual-only elements - The page has unlabeled icon buttons or visual-only elements
- You need to verify visual layout or styling - You need to verify visual layout or styling
- Canvas or chart elements are present (invisible to text snapshots) - Canvas or chart elements are present (invisible to text snapshots)
@@ -545,6 +547,7 @@ agent-browser eval -b "$(echo -n 'Array.from(document.querySelectorAll("a")).map
**Why this matters:** When the shell processes your command, inner double quotes, `!` characters (history expansion), backticks, and `$()` can all corrupt the JavaScript before it reaches agent-browser. The `--stdin` and `-b` flags bypass shell interpretation entirely. **Why this matters:** When the shell processes your command, inner double quotes, `!` characters (history expansion), backticks, and `$()` can all corrupt the JavaScript before it reaches agent-browser. The `--stdin` and `-b` flags bypass shell interpretation entirely.
**Rules of thumb:** **Rules of thumb:**
- Single-line, no nested quotes -> regular `eval 'expression'` with single quotes is fine - Single-line, no nested quotes -> regular `eval 'expression'` with single quotes is fine
- Nested quotes, arrow functions, template literals, or multiline -> use `eval --stdin <<'EVALEOF'` - Nested quotes, arrow functions, template literals, or multiline -> use `eval --stdin <<'EVALEOF'`
- Programmatic/generated scripts -> use `eval -b` with base64 - Programmatic/generated scripts -> use `eval -b` with base64
@@ -565,15 +568,15 @@ Priority (lowest to highest): `~/.agent-browser/config.json` < `./agent-browser.
## Deep-Dive Documentation ## Deep-Dive Documentation
| Reference | When to Use | | Reference | When to Use |
|-----------|-------------| | -------------------------------------------------------------------- | --------------------------------------------------------- |
| [references/commands.md](references/commands.md) | Full command reference with all options | | [references/commands.md](references/commands.md) | Full command reference with all options |
| [references/snapshot-refs.md](references/snapshot-refs.md) | Ref lifecycle, invalidation rules, troubleshooting | | [references/snapshot-refs.md](references/snapshot-refs.md) | Ref lifecycle, invalidation rules, troubleshooting |
| [references/session-management.md](references/session-management.md) | Parallel sessions, state persistence, concurrent scraping | | [references/session-management.md](references/session-management.md) | Parallel sessions, state persistence, concurrent scraping |
| [references/authentication.md](references/authentication.md) | Login flows, OAuth, 2FA handling, state reuse | | [references/authentication.md](references/authentication.md) | Login flows, OAuth, 2FA handling, state reuse |
| [references/video-recording.md](references/video-recording.md) | Recording workflows for debugging and documentation | | [references/video-recording.md](references/video-recording.md) | Recording workflows for debugging and documentation |
| [references/profiling.md](references/profiling.md) | Chrome DevTools profiling for performance analysis | | [references/profiling.md](references/profiling.md) | Chrome DevTools profiling for performance analysis |
| [references/proxy-support.md](references/proxy-support.md) | Proxy configuration, geo-testing, rotating proxies | | [references/proxy-support.md](references/proxy-support.md) | Proxy configuration, geo-testing, rotating proxies |
## Experimental: Native Mode ## Experimental: Native Mode
@@ -614,11 +617,11 @@ Lightpanda does not support `--extension`, `--profile`, `--state`, or `--allow-f
## Ready-to-Use Templates ## Ready-to-Use Templates
| Template | Description | | Template | Description |
|----------|-------------| | ------------------------------------------------------------------------ | ----------------------------------- |
| [templates/form-automation.sh](templates/form-automation.sh) | Form filling with validation | | [templates/form-automation.sh](templates/form-automation.sh) | Form filling with validation |
| [templates/authenticated-session.sh](templates/authenticated-session.sh) | Login once, reuse state | | [templates/authenticated-session.sh](templates/authenticated-session.sh) | Login once, reuse state |
| [templates/capture-workflow.sh](templates/capture-workflow.sh) | Content extraction with screenshots | | [templates/capture-workflow.sh](templates/capture-workflow.sh) | Content extraction with screenshots |
```bash ```bash
./templates/form-automation.sh https://example.com/form ./templates/form-automation.sh https://example.com/form
+142
View File
@@ -48,6 +48,31 @@ export function getDefaultTimeout(): number {
return 25000; return 25000;
} }
/**
* Handles boolean env vars and parsing (e.g., "true", "1", "false", "0"),
* with a default value if not set or invalid
*/
export function parseBooleanEnvVar(name: string, defaultValue: boolean): boolean {
const truthyVals = ['1', 'true'];
const falsyVals = ['0', 'false'];
if (!Object.hasOwn(process.env, name)) {
return defaultValue;
}
const param = process.env[name]!.toLowerCase();
if (truthyVals.includes(param)) {
return true;
}
if (falsyVals.includes(param)) {
return false;
}
return defaultValue;
}
// Screencast frame data from CDP // Screencast frame data from CDP
export interface ScreencastFrame { export interface ScreencastFrame {
data: string; // base64 encoded image data: string; // base64 encoded image
@@ -105,6 +130,7 @@ export class BrowserManager {
private browserUseApiKey: string | null = null; private browserUseApiKey: string | null = null;
private kernelSessionId: string | null = null; private kernelSessionId: string | null = null;
private kernelApiKey: string | null = null; private kernelApiKey: string | null = null;
private browserlessStopUrl: string | null = null;
private contexts: BrowserContext[] = []; private contexts: BrowserContext[] = [];
private pages: Page[] = []; private pages: Page[] = [];
private activePageIndex: number = 0; private activePageIndex: number = 0;
@@ -939,6 +965,19 @@ export class BrowserManager {
} }
} }
/**
* Close a Browserless session via its stop URL
*/
private async closeBrowserlessSession(stopUrl: string): Promise<void> {
const response = await fetch(stopUrl, {
method: 'DELETE',
});
if (!response.ok) {
throw new Error(`Failed to close Browserless session: ${response.statusText}`);
}
}
/** /**
* Connect to Browserbase remote browser via CDP. * Connect to Browserbase remote browser via CDP.
* Requires BROWSERBASE_API_KEY environment variable. * Requires BROWSERBASE_API_KEY environment variable.
@@ -1221,6 +1260,99 @@ export class BrowserManager {
} }
} }
/**
* Connect to Browserless remote browser via CDP.
* Requires BROWSERLESS_API_KEY environment variable.
*/
private async connectToBrowserless(): Promise<void> {
const browserlessToken = process.env.BROWSERLESS_API_KEY;
if (!browserlessToken) {
throw new Error('BROWSERLESS_API_KEY is required when using browserless as a provider');
}
const supportedBrowsers = ['chromium', 'chrome'];
const apiUrl = process.env.BROWSERLESS_API_URL || 'https://production-sfo.browserless.io';
const browserType = process.env.BROWSERLESS_BROWSER_TYPE || 'chromium';
const ttl = parseInt(process.env.BROWSERLESS_TTL || '300000', 10);
const stealth = parseBooleanEnvVar('BROWSERLESS_STEALTH', true);
if (!supportedBrowsers.includes(browserType)) {
throw new Error(
`BROWSERLESS_BROWSER_TYPE "${browserType}" is not supported. Only ${supportedBrowsers.join(', ')} are allowed.`
);
}
const response = await fetch(
`${apiUrl}/session?token=${encodeURIComponent(browserlessToken)}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
ttl,
stealth,
browser: browserType,
}),
}
);
if (!response.ok) {
throw new Error(`Failed to create Browserless session: ${response.statusText}`);
}
let session: { connect: string; stop: string };
try {
session = (await response.json()) as { connect: string; stop: string };
} catch (error) {
throw new Error(
`Failed to parse Browserless session response: ${error instanceof Error ? error.message : String(error)}`
);
}
if (!session.connect || !session.stop) {
throw new Error(
`Invalid Browserless session response: missing ${!session.connect ? 'connect' : 'stop'}`
);
}
const browser = await chromium.connectOverCDP(session.connect).catch(() => {
throw new Error('Failed to connect to Browserless session via CDP');
});
try {
const contexts = browser.contexts();
let context: BrowserContext;
let page: Page;
if (contexts.length === 0) {
context = await browser.newContext();
page = await context.newPage();
} else {
context = contexts[0];
const pages = context.pages();
page = pages[0] ?? (await context.newPage());
}
this.browser = browser;
this.browserlessStopUrl = session.stop;
context.setDefaultTimeout(getDefaultTimeout());
this.contexts.push(context);
this.setupContextTracking(context);
await this.ensureDomainFilter(context);
await this.sanitizeExistingPages([page]);
this.pages.push(page);
this.activePageIndex = 0;
this.setupPageTracking(page);
} catch (error) {
await this.closeBrowserlessSession(session.stop).catch((sessionError) => {
console.error('Failed to close Browserless session during cleanup:', sessionError);
});
this.browserlessStopUrl = null;
throw error;
}
}
/** /**
* Launch the browser with the specified options * Launch the browser with the specified options
* If already launched, this is a no-op (browser stays open) * If already launched, this is a no-op (browser stays open)
@@ -1324,6 +1456,10 @@ export class BrowserManager {
await this.connectToKernel(); await this.connectToKernel();
return; return;
} }
if (provider === 'browserless') {
await this.connectToBrowserless();
return;
}
if (this.downloadPath) { if (this.downloadPath) {
const resolved = path.resolve(this.downloadPath); const resolved = path.resolve(this.downloadPath);
@@ -2579,6 +2715,11 @@ export class BrowserManager {
} }
); );
this.browser = null; this.browser = null;
} else if (this.browserlessStopUrl) {
await this.closeBrowserlessSession(this.browserlessStopUrl).catch((error) => {
console.error('Failed to close Browserless session:', error);
});
this.browser = null;
} else if (this.cdpEndpoint !== null) { } else if (this.cdpEndpoint !== null) {
// CDP: only disconnect, don't close external app's pages // CDP: only disconnect, don't close external app's pages
if (this.browser) { if (this.browser) {
@@ -2609,6 +2750,7 @@ export class BrowserManager {
this.browserUseApiKey = null; this.browserUseApiKey = null;
this.kernelSessionId = null; this.kernelSessionId = null;
this.kernelApiKey = null; this.kernelApiKey = null;
this.browserlessStopUrl = null;
this.isPersistentContext = false; this.isPersistentContext = false;
this.activePageIndex = 0; this.activePageIndex = 0;
this.colorScheme = null; this.colorScheme = null;