Skip to content
Merged
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@ This is useful for multimodal AI models that can reason about visual layout, unl
| `--headed` | Show browser window (not headless) |
| `--cdp <port\|url>` | Connect via Chrome DevTools Protocol (port or WebSocket URL) |
| `--auto-connect` | Auto-discover and connect to running Chrome (or `AGENT_BROWSER_AUTO_CONNECT` env) |
| `--color-scheme <scheme>` | Color scheme: `dark`, `light`, `no-preference` (or `AGENT_BROWSER_COLOR_SCHEME` env) |
| `--config <path>` | Use a custom config file (or `AGENT_BROWSER_CONFIG` env) |
| `--debug` | Debug output |

Expand Down
1 change: 1 addition & 0 deletions cli/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1864,6 +1864,7 @@ mod tests {
cli_proxy_bypass: false,
cli_allow_file_access: false,
annotate: false,
color_scheme: None,
}
}

Expand Down
13 changes: 13 additions & 0 deletions cli/src/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub struct Config {
pub auto_connect: Option<bool>,
pub headers: Option<String>,
pub annotate: Option<bool>,
pub color_scheme: Option<String>,
}

impl Config {
Expand Down Expand Up @@ -66,6 +67,7 @@ impl Config {
auto_connect: other.auto_connect.or(self.auto_connect),
headers: other.headers.or(self.headers),
annotate: other.annotate.or(self.annotate),
color_scheme: other.color_scheme.or(self.color_scheme),
}
}
}
Expand Down Expand Up @@ -129,6 +131,7 @@ fn extract_config_path(args: &[String]) -> Option<Option<String>> {
"--provider",
"--device",
"--session-name",
"--color-scheme",
];
let mut i = 0;
while i < args.len() {
Expand Down Expand Up @@ -199,6 +202,7 @@ pub struct Flags {
pub auto_connect: bool,
pub session_name: Option<String>,
pub annotate: bool,
pub color_scheme: Option<String>,

// Track which launch-time options were explicitly passed via CLI
// (as opposed to being set only via environment variables)
Expand Down Expand Up @@ -278,6 +282,8 @@ pub fn parse_flags(args: &[String]) -> Flags {
.or(config.session_name),
annotate: env_var_is_truthy("AGENT_BROWSER_ANNOTATE")
|| config.annotate.unwrap_or(false),
color_scheme: env::var("AGENT_BROWSER_COLOR_SCHEME").ok()
.or(config.color_scheme),
cli_executable_path: false,
cli_extensions: false,
cli_profile: false,
Expand Down Expand Up @@ -425,6 +431,12 @@ pub fn parse_flags(args: &[String]) -> Flags {
flags.annotate = val;
if consumed { i += 1; }
}
"--color-scheme" => {
if let Some(s) = args.get(i + 1) {
flags.color_scheme = Some(s.clone());
i += 1;
}
}
"--config" => {
// Already handled by load_config(); skip the value
i += 1;
Expand Down Expand Up @@ -468,6 +480,7 @@ pub fn clean_args(args: &[String]) -> Vec<String> {
"--provider",
"--device",
"--session-name",
"--color-scheme",
"--config",
];

Expand Down
21 changes: 19 additions & 2 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,10 @@ fn main() {
launch_cmd["ignoreHTTPSErrors"] = json!(true);
}

if let Some(ref cs) = flags.color_scheme {
launch_cmd["colorScheme"] = json!(cs);
}

let err = match send_command(launch_cmd, &flags.session) {
Ok(resp) if resp.success => None,
Ok(resp) => Some(
Expand Down Expand Up @@ -440,6 +444,10 @@ fn main() {
launch_cmd["ignoreHTTPSErrors"] = json!(true);
}

if let Some(ref cs) = flags.color_scheme {
launch_cmd["colorScheme"] = json!(cs);
}

let err = match send_command(launch_cmd, &flags.session) {
Ok(resp) if resp.success => None,
Ok(resp) => Some(
Expand All @@ -461,12 +469,16 @@ fn main() {

// Launch with cloud provider if -p flag is set
if let Some(ref provider) = flags.provider {
let launch_cmd = json!({
let mut launch_cmd = json!({
"id": gen_id(),
"action": "launch",
"provider": provider
});

if let Some(ref cs) = flags.color_scheme {
launch_cmd["colorScheme"] = json!(cs);
}

let err = match send_command(launch_cmd, &flags.session) {
Ok(resp) if resp.success => None,
Ok(resp) => Some(
Expand Down Expand Up @@ -494,7 +506,8 @@ fn main() {
|| flags.proxy.is_some()
|| flags.args.is_some()
|| flags.user_agent.is_some()
|| flags.allow_file_access)
|| flags.allow_file_access
|| flags.color_scheme.is_some())
&& flags.cdp.is_none()
&& flags.provider.is_none()
{
Expand Down Expand Up @@ -556,6 +569,10 @@ fn main() {
launch_cmd["allowFileAccess"] = json!(true);
}

if let Some(ref cs) = flags.color_scheme {
launch_cmd["colorScheme"] = json!(cs);
}

match send_command(launch_cmd, &flags.session) {
Ok(resp) if !resp.success => {
// Launch command failed (e.g., invalid state file, profile error)
Expand Down
3 changes: 3 additions & 0 deletions cli/src/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2062,6 +2062,7 @@ Options:
--headed Show browser window (not headless)
--cdp <port> Connect via CDP (Chrome DevTools Protocol)
--auto-connect Auto-discover and connect to running Chrome
--color-scheme <scheme> Color scheme: dark, light, no-preference (or AGENT_BROWSER_COLOR_SCHEME)
--session-name <name> Auto-save/restore session state (cookies, localStorage)
--config <path> Use a custom config file (or AGENT_BROWSER_CONFIG env)
--debug Debug output
Expand Down Expand Up @@ -2103,6 +2104,7 @@ Environment:
AGENT_BROWSER_PROVIDER Browser provider (ios, browserbase, kernel, browseruse)
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)
AGENT_BROWSER_STREAM_PORT Enable WebSocket streaming on port (e.g., 9223)
AGENT_BROWSER_IOS_DEVICE Default iOS device name
AGENT_BROWSER_IOS_UDID Default iOS device UDID
Expand All @@ -2126,6 +2128,7 @@ Examples:
agent-browser wait --load networkidle # Wait for slow pages to load
agent-browser --cdp 9222 snapshot # Connect via CDP port
agent-browser --auto-connect snapshot # Auto-discover running Chrome
agent-browser --color-scheme dark open example.com # Dark mode
agent-browser --profile ~/.myapp open example.com # Persistent profile
agent-browser --session-name myapp open example.com # Auto-save/restore state

Expand Down
16 changes: 16 additions & 0 deletions docs/src/app/cdp-mode/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,21 @@ This is useful when:
- You want a zero-configuration connection to your existing browser
- You don't want to track which port Chrome is using

## Color scheme

Playwright overrides the browser's color scheme to `light` by default when connecting via CDP. Use `--color-scheme` to set a persistent preference:

```bash
agent-browser --cdp 9222 --color-scheme dark open https://example.com
agent-browser --cdp 9222 snapshot # stays in dark mode
```

Or set it globally via config or environment variable:

```bash
AGENT_BROWSER_COLOR_SCHEME=dark agent-browser --cdp 9222 open https://example.com
```

## Use cases

This enables control of:
Expand Down Expand Up @@ -93,6 +108,7 @@ This enables control of:
<tr><td><code>--headed</code></td><td>Show browser window</td></tr>
<tr><td><code>{"--cdp <port|url>"}</code></td><td>CDP connection (port or WebSocket URL)</td></tr>
<tr><td><code>--auto-connect</code></td><td>Auto-discover and connect to running Chrome</td></tr>
<tr><td><code>--color-scheme &lt;scheme&gt;</code></td><td>Persistent color scheme (<code>dark</code>, <code>light</code>, <code>no-preference</code>)</td></tr>
<tr><td><code>--debug</code></td><td>Debug output</td></tr>
</tbody>
</table>
Expand Down
8 changes: 7 additions & 1 deletion docs/src/app/commands/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,13 @@ agent-browser set geo <lat> <lng> # Set geolocation
agent-browser set offline [on|off] # Toggle offline mode
agent-browser set headers <json> # Extra HTTP headers
agent-browser set credentials <u> <p> # HTTP basic auth
agent-browser set media [dark|light] # Emulate color scheme
agent-browser set media [dark|light] # Emulate color scheme (persists for session)
```

Use `--color-scheme` for persistent dark/light mode across all commands:

```bash
agent-browser --color-scheme dark open https://example.com
```

## Cookies & storage
Expand Down
1 change: 1 addition & 0 deletions docs/src/app/configuration/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ Every CLI flag can be set in the config file using its camelCase equivalent:
<tr><td><code>allowFileAccess</code></td><td><code>--allow-file-access</code></td><td>boolean</td></tr>
<tr><td><code>cdp</code></td><td><code>--cdp</code></td><td>string</td></tr>
<tr><td><code>autoConnect</code></td><td><code>--auto-connect</code></td><td>boolean</td></tr>
<tr><td><code>colorScheme</code></td><td><code>--color-scheme</code></td><td>string (<code>dark</code>, <code>light</code>, <code>no-preference</code>)</td></tr>
<tr><td><code>headers</code></td><td><code>--headers</code></td><td>string (JSON)</td></tr>
</tbody>
</table>
Expand Down
13 changes: 13 additions & 0 deletions skills/agent-browser/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,19 @@ agent-browser --auto-connect snapshot
agent-browser --cdp 9222 snapshot
```

### Color Scheme (Dark Mode)

```bash
# Persistent dark mode via flag (applies to all pages and new tabs)
agent-browser --color-scheme dark open https://example.com

# Or via environment variable
AGENT_BROWSER_COLOR_SCHEME=dark agent-browser open https://example.com

# Or set during session (persists for subsequent commands)
agent-browser set media dark
```

### Visual Browser (Debugging)

```bash
Expand Down
3 changes: 3 additions & 0 deletions src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2069,6 +2069,9 @@ async function handleEmulateMedia(
reducedMotion: command.reducedMotion,
forcedColors: command.forcedColors,
});
if (command.colorScheme) {
browser.setColorScheme(command.colorScheme);
}
return successResponse(command.id, { emulated: true });
}

Expand Down
26 changes: 25 additions & 1 deletion src/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,15 @@ export class BrowserManager {
private refMap: RefMap = {};
private lastSnapshot: string = '';
private scopedHeaderRoutes: Map<string, (route: Route) => Promise<void>> = new Map();
private colorScheme: 'light' | 'dark' | 'no-preference' | null = null;

/**
* Set the persistent color scheme preference.
* Applied automatically to all new pages and contexts.
*/
setColorScheme(scheme: 'light' | 'dark' | 'no-preference' | null): void {
this.colorScheme = scheme;
}

// CDP session for screencast and input injection
private cdpSession: CDPSession | null = null;
Expand Down Expand Up @@ -252,7 +261,9 @@ export class BrowserManager {
if (this.contexts.length > 0) {
context = this.contexts[this.contexts.length - 1];
} else if (this.browser) {
context = await this.browser.newContext();
context = await this.browser.newContext({
...(this.colorScheme && { colorScheme: this.colorScheme }),
});
context.setDefaultTimeout(60000);
this.contexts.push(context);
this.setupContextTracking(context);
Expand Down Expand Up @@ -1140,6 +1151,10 @@ export class BrowserManager {
}
}

if (options.colorScheme) {
this.colorScheme = options.colorScheme;
}

if (cdpEndpoint) {
await this.connectViaCDP(cdpEndpoint);
return;
Expand Down Expand Up @@ -1224,6 +1239,7 @@ export class BrowserManager {
userAgent: options.userAgent,
...(options.proxy && { proxy: options.proxy }),
ignoreHTTPSErrors: options.ignoreHTTPSErrors ?? false,
...(this.colorScheme && { colorScheme: this.colorScheme }),
}
);
this.isPersistentContext = true;
Expand All @@ -1240,6 +1256,7 @@ export class BrowserManager {
userAgent: options.userAgent,
...(options.proxy && { proxy: options.proxy }),
ignoreHTTPSErrors: options.ignoreHTTPSErrors ?? false,
...(this.colorScheme && { colorScheme: this.colorScheme }),
});
this.isPersistentContext = true;
} else {
Expand Down Expand Up @@ -1325,6 +1342,7 @@ export class BrowserManager {
storageState,
...(options.proxy && { proxy: options.proxy }),
ignoreHTTPSErrors: options.ignoreHTTPSErrors ?? false,
...(this.colorScheme && { colorScheme: this.colorScheme }),
});
}

Expand Down Expand Up @@ -1555,6 +1573,10 @@ export class BrowserManager {
* Set up console, error, and close tracking for a page
*/
private setupPageTracking(page: Page): void {
if (this.colorScheme) {
page.emulateMedia({ colorScheme: this.colorScheme }).catch(() => {});
}

page.on('console', (msg) => {
this.consoleMessages.push({
type: msg.type(),
Expand Down Expand Up @@ -1642,6 +1664,7 @@ export class BrowserManager {

const context = await this.browser.newContext({
viewport: viewport === undefined ? { width: 1280, height: 720 } : viewport,
...(this.colorScheme && { colorScheme: this.colorScheme }),
});
context.setDefaultTimeout(60000);
this.contexts.push(context);
Expand Down Expand Up @@ -2381,6 +2404,7 @@ export class BrowserManager {
this.kernelApiKey = null;
this.isPersistentContext = false;
this.activePageIndex = 0;
this.colorScheme = null;
this.refMap = {};
this.lastSnapshot = '';
this.frameCallback = null;
Expand Down
8 changes: 8 additions & 0 deletions src/daemon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,13 @@ 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';
const colorSchemeEnv = process.env.AGENT_BROWSER_COLOR_SCHEME;
const colorScheme =
colorSchemeEnv === 'dark' ||
colorSchemeEnv === 'light' ||
colorSchemeEnv === 'no-preference'
? colorSchemeEnv
: undefined;
await manager.launch({
id: 'auto',
action: 'launch' as const,
Expand All @@ -431,6 +438,7 @@ export async function startDaemon(options?: {
proxy,
ignoreHTTPSErrors: ignoreHTTPSErrors,
allowFileAccess: allowFileAccess,
colorScheme,
autoStateFilePath: getSessionAutoStatePath(),
});
}
Expand Down
1 change: 1 addition & 0 deletions src/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const launchSchema = baseCommandSchema.extend({
provider: z.string().optional(),
ignoreHTTPSErrors: z.boolean().optional(),
allowFileAccess: z.boolean().optional(),
colorScheme: z.enum(['light', 'dark', 'no-preference']).optional(),
profile: z.string().optional(),
storageState: z.string().optional(),
});
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface LaunchCommand extends BaseCommand {
provider?: string;
ignoreHTTPSErrors?: boolean;
allowFileAccess?: boolean; // Enable file:// URL access and cross-origin file requests
colorScheme?: 'light' | 'dark' | 'no-preference'; // Persistent color scheme override
// Auto-load state file for session persistence
autoStateFilePath?: string;
}
Expand Down