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:
@@ -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)
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
agent-browser find role button click --name "Submit"
|
||||
agent-browser find text "Sign In" click
|
||||
@@ -375,6 +376,7 @@ agent-browser session
|
||||
```
|
||||
|
||||
Each session has its own:
|
||||
|
||||
- Browser instance
|
||||
- Cookies and storage
|
||||
- Navigation history
|
||||
@@ -396,6 +398,7 @@ AGENT_BROWSER_PROFILE=~/.myapp-profile agent-browser open myapp.com
|
||||
```
|
||||
|
||||
The profile directory stores:
|
||||
|
||||
- Cookies and localStorage
|
||||
- IndexedDB data
|
||||
- Service workers
|
||||
@@ -432,10 +435,10 @@ export AGENT_BROWSER_ENCRYPTION_KEY=<64-char-hex-key>
|
||||
agent-browser --session-name secure open example.com
|
||||
```
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `AGENT_BROWSER_SESSION_NAME` | Auto-save/load state persistence name |
|
||||
| `AGENT_BROWSER_ENCRYPTION_KEY` | 64-char hex key for AES-256-GCM encryption |
|
||||
| Variable | Description |
|
||||
| --------------------------------- | -------------------------------------------------- |
|
||||
| `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_STATE_EXPIRE_DAYS` | Auto-delete states older than N days (default: 30) |
|
||||
|
||||
## 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`
|
||||
- **Output Length Limits** -- Prevent context flooding: `--max-output 50000`
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `AGENT_BROWSER_CONTENT_BOUNDARIES` | Wrap page output in boundary markers |
|
||||
| `AGENT_BROWSER_MAX_OUTPUT` | Max characters for page output |
|
||||
| `AGENT_BROWSER_ALLOWED_DOMAINS` | Comma-separated allowed domain patterns |
|
||||
| `AGENT_BROWSER_ACTION_POLICY` | Path to action policy JSON file |
|
||||
| `AGENT_BROWSER_CONFIRM_ACTIONS` | Action categories requiring confirmation |
|
||||
| `AGENT_BROWSER_CONFIRM_INTERACTIVE` | Enable interactive confirmation prompts |
|
||||
| Variable | Description |
|
||||
| ----------------------------------- | ---------------------------------------- |
|
||||
| `AGENT_BROWSER_CONTENT_BOUNDARIES` | Wrap page output in boundary markers |
|
||||
| `AGENT_BROWSER_MAX_OUTPUT` | Max characters for page output |
|
||||
| `AGENT_BROWSER_ALLOWED_DOMAINS` | Comma-separated allowed domain patterns |
|
||||
| `AGENT_BROWSER_ACTION_POLICY` | Path to action policy JSON file |
|
||||
| `AGENT_BROWSER_CONFIRM_ACTIONS` | Action categories requiring confirmation |
|
||||
| `AGENT_BROWSER_CONFIRM_INTERACTIVE` | Enable interactive confirmation prompts |
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `-i, --interactive` | Only show interactive elements (buttons, links, inputs) |
|
||||
| `-C, --cursor` | Include cursor-interactive elements (cursor:pointer, onclick, tabindex) |
|
||||
| `-c, --compact` | Remove empty structural elements |
|
||||
| `-d, --depth <n>` | Limit tree depth |
|
||||
| `-s, --selector <sel>` | Scope to CSS selector |
|
||||
| Option | Description |
|
||||
| ---------------------- | ----------------------------------------------------------------------- |
|
||||
| `-i, --interactive` | Only show interactive elements (buttons, links, inputs) |
|
||||
| `-C, --cursor` | Include cursor-interactive elements (cursor:pointer, onclick, tabindex) |
|
||||
| `-c, --compact` | Remove empty structural elements |
|
||||
| `-d, --depth <n>` | Limit tree depth |
|
||||
| `-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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| Variable | Description |
|
||||
| ------------------------------- | ------------------------------------------------- |
|
||||
| `AGENT_BROWSER_DEFAULT_TIMEOUT` | Default Playwright timeout in ms (default: 25000) |
|
||||
|
||||
## Selectors
|
||||
@@ -623,6 +626,7 @@ agent-browser hover @e4 # Hover the link
|
||||
```
|
||||
|
||||
**Why use refs?**
|
||||
|
||||
- **Deterministic**: Ref points to exact element from snapshot
|
||||
- **Fast**: No DOM re-query needed
|
||||
- **AI-friendly**: Snapshot + ref workflow is optimal for LLMs
|
||||
@@ -723,6 +727,7 @@ agent-browser open other-site.com
|
||||
```
|
||||
|
||||
This is useful for:
|
||||
|
||||
- **Skipping login flows** - Authenticate via headers instead of UI
|
||||
- **Switching users** - Start new sessions with different auth tokens
|
||||
- **API testing** - Access protected endpoints directly
|
||||
@@ -744,6 +749,7 @@ agent-browser set headers '{"X-Custom-Header": "value"}'
|
||||
## Custom Browser Executable
|
||||
|
||||
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)
|
||||
- **System browsers**: Use an existing Chrome/Chromium installation
|
||||
- **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:
|
||||
|
||||
- Load and render local files
|
||||
- Access other local files via JavaScript (XHR, fetch)
|
||||
- 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:
|
||||
|
||||
- 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
|
||||
|
||||
This enables control of:
|
||||
|
||||
- Electron apps
|
||||
- Chrome/Chromium instances with remote debugging
|
||||
- WebView2 applications
|
||||
@@ -854,10 +863,12 @@ AGENT_BROWSER_AUTO_CONNECT=1 agent-browser snapshot
|
||||
```
|
||||
|
||||
Auto-connect discovers Chrome by:
|
||||
|
||||
1. Reading Chrome's `DevToolsActivePort` file from the default user data directory
|
||||
2. Falling back to probing common debugging ports (9222, 9229)
|
||||
|
||||
This is useful when:
|
||||
|
||||
- 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 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:
|
||||
|
||||
**Receive frames:**
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "frame",
|
||||
@@ -897,6 +909,7 @@ Connect to `ws://localhost:9223` to receive frames and send input:
|
||||
```
|
||||
|
||||
**Send mouse events:**
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "input_mouse",
|
||||
@@ -909,6 +922,7 @@ Connect to `ws://localhost:9223` to receive frames and send input:
|
||||
```
|
||||
|
||||
**Send keyboard events:**
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "input_keyboard",
|
||||
@@ -919,6 +933,7 @@ Connect to `ws://localhost:9223` to receive frames and send input:
|
||||
```
|
||||
|
||||
**Send touch events:**
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "input_touch",
|
||||
@@ -939,16 +954,19 @@ await browser.launch({ headless: true });
|
||||
await browser.navigate('https://example.com');
|
||||
|
||||
// Start screencast
|
||||
await browser.startScreencast((frame) => {
|
||||
// frame.data is base64-encoded image
|
||||
// frame.metadata contains viewport info
|
||||
console.log('Frame received:', frame.metadata.deviceWidth, 'x', frame.metadata.deviceHeight);
|
||||
}, {
|
||||
format: 'jpeg',
|
||||
quality: 80,
|
||||
maxWidth: 1280,
|
||||
maxHeight: 720,
|
||||
});
|
||||
await browser.startScreencast(
|
||||
(frame) => {
|
||||
// frame.data is base64-encoded image
|
||||
// frame.metadata contains viewport info
|
||||
console.log('Frame received:', frame.metadata.deviceWidth, 'x', frame.metadata.deviceHeight);
|
||||
},
|
||||
{
|
||||
format: 'jpeg',
|
||||
quality: 80,
|
||||
maxWidth: 1280,
|
||||
maxHeight: 720,
|
||||
}
|
||||
);
|
||||
|
||||
// Inject mouse events
|
||||
await browser.injectMouseEvent({
|
||||
@@ -1000,18 +1018,18 @@ agent-browser open example.com
|
||||
Or add to your config file (`agent-browser.json`):
|
||||
|
||||
```json
|
||||
{"native": true}
|
||||
{ "native": true }
|
||||
```
|
||||
|
||||
### What's Different
|
||||
|
||||
| | Default (Node.js) | Native (`--native`) |
|
||||
|---|---|---|
|
||||
| **Runtime** | Node.js + Playwright | Pure Rust binary |
|
||||
| **Protocol** | Playwright protocol | Direct CDP / WebDriver |
|
||||
| **Install size** | Larger (Node.js + npm deps) | Smaller (single binary) |
|
||||
| **Browser support** | Chromium, Firefox, WebKit | Chromium, Safari (via WebDriver) |
|
||||
| **Stability** | Stable | Experimental |
|
||||
| | Default (Node.js) | Native (`--native`) |
|
||||
| ------------------- | --------------------------- | -------------------------------- |
|
||||
| **Runtime** | Node.js + Playwright | Pure Rust binary |
|
||||
| **Protocol** | Playwright protocol | Direct CDP / WebDriver |
|
||||
| **Install size** | Larger (Node.js + npm deps) | Smaller (single binary) |
|
||||
| **Browser support** | Chromium, Firefox, WebKit | Chromium, Safari (via WebDriver) |
|
||||
| **Stability** | Stable | Experimental |
|
||||
|
||||
### Known Limitations
|
||||
|
||||
@@ -1021,13 +1039,13 @@ Or add to your config file (`agent-browser.json`):
|
||||
|
||||
## Platforms
|
||||
|
||||
| Platform | Binary | Fallback |
|
||||
|----------|--------|----------|
|
||||
| macOS ARM64 | Native Rust | Node.js |
|
||||
| macOS x64 | Native Rust | Node.js |
|
||||
| Linux ARM64 | Native Rust | Node.js |
|
||||
| Linux x64 | Native Rust | Node.js |
|
||||
| Windows x64 | Native Rust | Node.js |
|
||||
| Platform | Binary | Fallback |
|
||||
| ----------- | ----------- | -------- |
|
||||
| macOS ARM64 | Native Rust | Node.js |
|
||||
| macOS x64 | Native Rust | Node.js |
|
||||
| Linux ARM64 | Native Rust | Node.js |
|
||||
| Linux x64 | Native Rust | Node.js |
|
||||
| Windows x64 | Native Rust | Node.js |
|
||||
|
||||
## 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.
|
||||
|
||||
Core workflow:
|
||||
|
||||
1. `agent-browser open <url>` - Navigate to page
|
||||
2. `agent-browser snapshot -i` - Get interactive elements with refs (@e1, @e2)
|
||||
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
|
||||
```
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `AGENT_BROWSER_PROVIDER` | Set to `ios` to enable iOS mode |
|
||||
| Variable | Description |
|
||||
| -------------------------- | ----------------------------------------------- |
|
||||
| `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_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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
**1. Get your device UDID:**
|
||||
|
||||
```bash
|
||||
xcrun xctrace list devices
|
||||
# or
|
||||
@@ -1144,6 +1164,7 @@ system_profiler SPUSBDataType | grep -A 5 "iPhone\|iPad"
|
||||
```
|
||||
|
||||
**2. Sign WebDriverAgent (one-time):**
|
||||
|
||||
```bash
|
||||
# Open the WebDriverAgent Xcode project
|
||||
cd ~/.appium/node_modules/appium-xcuitest-driver/node_modules/appium-webdriveragent
|
||||
@@ -1151,12 +1172,14 @@ open WebDriverAgent.xcodeproj
|
||||
```
|
||||
|
||||
In Xcode:
|
||||
|
||||
- Select the `WebDriverAgentRunner` target
|
||||
- Go to Signing & Capabilities
|
||||
- Select your Team (requires Apple Developer account, free tier works)
|
||||
- Let Xcode manage signing automatically
|
||||
|
||||
**3. Use with agent-browser:**
|
||||
|
||||
```bash
|
||||
# Connect device via USB, then:
|
||||
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:**
|
||||
|
||||
- First run installs WebDriverAgent to the device (may require Trust prompt)
|
||||
- Device must be unlocked and connected via USB
|
||||
- Slightly slower initial connection than simulator
|
||||
- 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](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:
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `KERNEL_HEADLESS` | Run browser in headless mode (`true`/`false`) | `false` |
|
||||
| `KERNEL_STEALTH` | Enable stealth mode to avoid bot detection (`true`/`false`) | `true` |
|
||||
| `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) |
|
||||
| Variable | Description | Default |
|
||||
| ------------------------ | -------------------------------------------------------------------------------- | ------- |
|
||||
| `KERNEL_HEADLESS` | Run browser in headless mode (`true`/`false`) | `false` |
|
||||
| `KERNEL_STEALTH` | Enable stealth mode to avoid bot detection (`true`/`false`) | `true` |
|
||||
| `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) |
|
||||
|
||||
When enabled, agent-browser connects to a Kernel cloud session instead of launching a local browser. All commands work identically.
|
||||
|
||||
|
||||
+2
-2
@@ -2484,7 +2484,7 @@ Options:
|
||||
e.g., --proxy-bypass "localhost,*.internal.com"
|
||||
--ignore-https-errors Ignore HTTPS certificate errors
|
||||
--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")
|
||||
--json JSON output
|
||||
--full, -f Full page screenshot
|
||||
@@ -2538,7 +2538,7 @@ Environment:
|
||||
AGENT_BROWSER_ANNOTATE Annotated screenshot with numbered labels and legend
|
||||
AGENT_BROWSER_DEBUG Debug output
|
||||
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_ALLOW_FILE_ACCESS Allow file:// URLs to access local files
|
||||
AGENT_BROWSER_COLOR_SCHEME Color scheme preference (dark, light, no-preference)
|
||||
|
||||
@@ -94,7 +94,7 @@ This enables control of:
|
||||
<tbody>
|
||||
<tr><td><code>--session <name></code></td><td>Use isolated session</td></tr>
|
||||
<tr><td><code>--profile <path></code></td><td>Persistent browser profile directory</td></tr>
|
||||
<tr><td><code>-p <provider></code></td><td>Cloud browser provider (<code>browserbase</code>, <code>browseruse</code>, <code>kernel</code>)</td></tr>
|
||||
<tr><td><code>-p <provider></code></td><td>Cloud browser provider (<code>browserbase</code>, <code>browseruse</code>, <code>kernel</code>, <code>browserless</code>)</td></tr>
|
||||
<tr><td><code>--headers <json></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>--args <args></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"
|
||||
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
|
||||
export AGENT_BROWSER_PROVIDER=browserbase
|
||||
agent-browser open https://example.com
|
||||
|
||||
@@ -290,7 +290,7 @@ agent-browser reload # Reload page
|
||||
--proxy-bypass <hosts> # Hosts to bypass proxy
|
||||
--ignore-https-errors # Ignore HTTPS certificate errors
|
||||
--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")
|
||||
--json # JSON output (for scripts)
|
||||
--full, -f # Full page screenshot
|
||||
|
||||
@@ -388,8 +388,9 @@ export AGENT_BROWSER_ACTION_POLICY=./policy.json
|
||||
```
|
||||
|
||||
Example `policy.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.
|
||||
@@ -503,6 +504,7 @@ agent-browser click @e2 # Click using ref from annotated screenshot
|
||||
```
|
||||
|
||||
Use annotated screenshots when:
|
||||
|
||||
- The page has unlabeled icon buttons or visual-only elements
|
||||
- You need to verify visual layout or styling
|
||||
- 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.
|
||||
|
||||
**Rules of thumb:**
|
||||
|
||||
- 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'`
|
||||
- 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
|
||||
|
||||
| Reference | When to Use |
|
||||
|-----------|-------------|
|
||||
| [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 |
|
||||
| Reference | When to Use |
|
||||
| -------------------------------------------------------------------- | --------------------------------------------------------- |
|
||||
| [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/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/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/proxy-support.md](references/proxy-support.md) | Proxy configuration, geo-testing, rotating proxies |
|
||||
| [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/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 |
|
||||
|
||||
## Experimental: Native Mode
|
||||
|
||||
@@ -614,11 +617,11 @@ Lightpanda does not support `--extension`, `--profile`, `--state`, or `--allow-f
|
||||
|
||||
## Ready-to-Use Templates
|
||||
|
||||
| Template | Description |
|
||||
|----------|-------------|
|
||||
| [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/capture-workflow.sh](templates/capture-workflow.sh) | Content extraction with screenshots |
|
||||
| Template | Description |
|
||||
| ------------------------------------------------------------------------ | ----------------------------------- |
|
||||
| [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/capture-workflow.sh](templates/capture-workflow.sh) | Content extraction with screenshots |
|
||||
|
||||
```bash
|
||||
./templates/form-automation.sh https://example.com/form
|
||||
|
||||
+142
@@ -48,6 +48,31 @@ export function getDefaultTimeout(): number {
|
||||
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
|
||||
export interface ScreencastFrame {
|
||||
data: string; // base64 encoded image
|
||||
@@ -105,6 +130,7 @@ export class BrowserManager {
|
||||
private browserUseApiKey: string | null = null;
|
||||
private kernelSessionId: string | null = null;
|
||||
private kernelApiKey: string | null = null;
|
||||
private browserlessStopUrl: string | null = null;
|
||||
private contexts: BrowserContext[] = [];
|
||||
private pages: Page[] = [];
|
||||
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.
|
||||
* 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
|
||||
* If already launched, this is a no-op (browser stays open)
|
||||
@@ -1324,6 +1456,10 @@ export class BrowserManager {
|
||||
await this.connectToKernel();
|
||||
return;
|
||||
}
|
||||
if (provider === 'browserless') {
|
||||
await this.connectToBrowserless();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.downloadPath) {
|
||||
const resolved = path.resolve(this.downloadPath);
|
||||
@@ -2579,6 +2715,11 @@ export class BrowserManager {
|
||||
}
|
||||
);
|
||||
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) {
|
||||
// CDP: only disconnect, don't close external app's pages
|
||||
if (this.browser) {
|
||||
@@ -2609,6 +2750,7 @@ export class BrowserManager {
|
||||
this.browserUseApiKey = null;
|
||||
this.kernelSessionId = null;
|
||||
this.kernelApiKey = null;
|
||||
this.browserlessStopUrl = null;
|
||||
this.isPersistentContext = false;
|
||||
this.activePageIndex = 0;
|
||||
this.colorScheme = null;
|
||||
|
||||
Reference in New Issue
Block a user