Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .changeset/browserbase-proxy-stealth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"agent-browser": patch
---

### New Features

- **Browserbase proxy support** - Enable Browserbase's residential proxy via `BROWSERBASE_PROXY=1` environment variable to route traffic through residential IPs instead of datacenter IPs
- **Browserbase advanced stealth** - Enable advanced stealth mode via `BROWSERBASE_ADVANCED_STEALTH=1` and configure OS fingerprint via `BROWSERBASE_OS` (windows, mac, linux, mobile, tablet)
- **Image blocking for providers** - Block image loading to save proxy bandwidth via `BROWSERBASE_BLOCK_IMAGES=1` environment variable, using CDP Fetch interception to abort image requests (note: not recommended on sites with anti-bot protection)

### Bug Fixes

- **Browserbase session creation** - Fixed 415 "Unsupported Media Type" error when using `-p browserbase` by sending a JSON body with the session creation request (regression from #625)
37 changes: 37 additions & 0 deletions cli/src/native/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,43 @@ async fn handle_launch(cmd: &Value, state: &mut DaemonState) -> Result<Value, St
state.browser = Some(mgr);
state.subscribe_to_browser_events();
state.update_stream_client().await;

// Block image loading when BROWSERBASE_BLOCK_IMAGES=1
if env::var("BROWSERBASE_BLOCK_IMAGES")
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
.unwrap_or(false)
{
let image_exts = [
"**/*.png", "**/*.jpg", "**/*.jpeg", "**/*.gif",
"**/*.webp", "**/*.svg", "**/*.ico", "**/*.avif",
Comment on lines +1122 to +1123
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"**/*.png", "**/*.jpg", "**/*.jpeg", "**/*.gif",
"**/*.webp", "**/*.svg", "**/*.ico", "**/*.avif",
"*.png", "*.jpg", "*.jpeg", "*.gif",
"*.webp", "*.svg", "*.ico", "*.avif",

Image blocking patterns like **/*.png never match any URL in the route matching logic, so images are intercepted by CDP but then continued instead of aborted, adding latency without blocking anything.

Fix on Vercel

];
for ext in &image_exts {
state.routes.push(RouteEntry {
url_pattern: ext.to_string(),
response: None,
abort: true,
});
}
// Sync routes to Fetch interception
if let Some(ref browser) = state.browser {
if let Ok(session_id) = browser.active_session_id() {
let patterns: Vec<Value> = state
.routes
.iter()
.map(|r| json!({ "urlPattern": r.url_pattern }))
.collect();
let _ = browser
.client
.send_command(
"Fetch.enable",
Some(json!({ "patterns": patterns })),
Some(session_id),
)
.await;
}
}
}

return Ok(json!({ "launched": true, "provider": provider }));
}
Err(e) => {
Expand Down
43 changes: 43 additions & 0 deletions cli/src/native/providers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,53 @@ async fn connect_browserbase() -> Result<(String, Option<ProviderSession>), Stri
let api_key = env::var("BROWSERBASE_API_KEY")
.map_err(|_| "BROWSERBASE_API_KEY environment variable is not set")?;

let mut session_body = json!({});
let mut browser_settings = json!({});

// Enable Browserbase proxy when BROWSERBASE_PROXY=1 or BROWSERBASE_PROXY=true
if env::var("BROWSERBASE_PROXY")
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
.unwrap_or(false)
{
session_body.as_object_mut().unwrap().insert(
"proxies".to_string(),
json!([{ "type": "browserbase" }]),
);
}

// Enable advanced stealth mode via BROWSERBASE_ADVANCED_STEALTH=1
// Basic stealth is enabled by default on Browserbase; advanced uses a
// custom Chromium build that mimics human-like environmental signals.
if env::var("BROWSERBASE_ADVANCED_STEALTH")
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
.unwrap_or(false)
{
browser_settings
.as_object_mut()
.unwrap()
.insert("advancedStealth".to_string(), json!(true));
}

// Set OS for stealth fingerprint via BROWSERBASE_OS (windows, mac, linux, mobile, tablet)
if let Ok(os) = env::var("BROWSERBASE_OS") {
browser_settings
.as_object_mut()
.unwrap()
.insert("os".to_string(), json!(os));
}

if browser_settings.as_object().map_or(false, |o| !o.is_empty()) {
session_body
.as_object_mut()
.unwrap()
.insert("browserSettings".to_string(), browser_settings);
}

let client = reqwest::Client::new();
let response = client
.post("https://api.browserbase.com/v1/sessions")
.header("X-BB-API-Key", &api_key)
.json(&session_body)
.send()
.await
.map_err(|e| format!("Browserbase request failed: {}", e))?;
Expand Down