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
9 changes: 9 additions & 0 deletions cli/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ pub fn ensure_daemon(
provider: Option<&str>,
device: Option<&str>,
session_name: Option<&str>,
headers: Option<&str>,
) -> Result<DaemonResult, String> {
// Check if daemon is running AND responsive
if is_daemon_running(session) && daemon_ready(session) {
Expand Down Expand Up @@ -364,6 +365,10 @@ pub fn ensure_daemon(
cmd.env("AGENT_BROWSER_SESSION_NAME", sn);
}

if let Some(h) = headers {
cmd.env("AGENT_BROWSER_HEADERS", h);
}

// Create new process group and session to fully detach
unsafe {
cmd.pre_exec(|| {
Expand Down Expand Up @@ -447,6 +452,10 @@ pub fn ensure_daemon(
cmd.env("AGENT_BROWSER_SESSION_NAME", sn);
}

if let Some(h) = headers {
cmd.env("AGENT_BROWSER_HEADERS", h);
}

// CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS
const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200;
const DETACHED_PROCESS: u32 = 0x00000008;
Expand Down
2 changes: 1 addition & 1 deletion cli/src/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ pub fn parse_flags(args: &[String]) -> Flags {
headed: false,
debug: false,
session: env::var("AGENT_BROWSER_SESSION").unwrap_or_else(|_| "default".to_string()),
headers: None,
headers: env::var("AGENT_BROWSER_HEADERS").ok(),
executable_path: env::var("AGENT_BROWSER_EXECUTABLE_PATH").ok(),
cdp: None,
extensions: extensions_env,
Expand Down
1 change: 1 addition & 0 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ fn main() {
flags.provider.as_deref(),
flags.device.as_deref(),
flags.session_name.as_deref(),
flags.headers.as_deref(),
) {
Ok(result) => result,
Err(e) => {
Expand Down
12 changes: 10 additions & 2 deletions src/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1117,7 +1117,7 @@ export class BrowserManager {
}

if (cdpEndpoint) {
await this.connectViaCDP(cdpEndpoint);
await this.connectViaCDP(cdpEndpoint, options.headers);
return;
}

Expand Down Expand Up @@ -1310,7 +1310,7 @@ export class BrowserManager {
* Connect to a running browser via CDP (Chrome DevTools Protocol)
* @param cdpEndpoint Either a port number (as string) or a full WebSocket URL (ws:// or wss://)
*/
private async connectViaCDP(cdpEndpoint: string | undefined): Promise<void> {
private async connectViaCDP(cdpEndpoint: string | undefined, headers?: Record<string, string>): Promise<void> {
if (!cdpEndpoint) {
throw new Error('CDP endpoint is required for CDP connection');
}
Expand Down Expand Up @@ -1374,6 +1374,14 @@ export class BrowserManager {
}

this.activePageIndex = 0;

// Apply custom headers post-connection (Playwright's connectOverCDP doesn't support
// connection-time headers, so we set them on the context after connecting)
if (headers && Object.keys(headers).length > 0) {
for (const context of contexts) {
await context.setExtraHTTPHeaders(headers);
}
}
} catch (error) {
// Clean up browser connection if validation or setup failed
await browser.close().catch(() => {});
Expand Down
12 changes: 12 additions & 0 deletions src/daemon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,17 @@ export async function startDaemon(options?: {

const ignoreHTTPSErrors = process.env.AGENT_BROWSER_IGNORE_HTTPS_ERRORS === '1';
const allowFileAccess = process.env.AGENT_BROWSER_ALLOW_FILE_ACCESS === '1';

// Parse custom headers from env (JSON string)
let headers: Record<string, string> | undefined;
if (process.env.AGENT_BROWSER_HEADERS) {
try {
headers = JSON.parse(process.env.AGENT_BROWSER_HEADERS);
} catch {
/* ignore invalid JSON */
}
}

await manager.launch({
id: 'auto',
action: 'launch' as const,
Expand All @@ -430,6 +441,7 @@ export async function startDaemon(options?: {
proxy,
ignoreHTTPSErrors: ignoreHTTPSErrors,
allowFileAccess: allowFileAccess,
headers,
autoStateFilePath: getSessionAutoStatePath(),
});
}
Expand Down