Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
447e6ece0b | ||
|
|
d86de0e736 |
@@ -270,6 +270,30 @@ Each session has its own:
|
||||
- Navigation history
|
||||
- Authentication state
|
||||
|
||||
## Persistent Profiles
|
||||
|
||||
By default, browser state (cookies, localStorage, login sessions) is ephemeral and lost when the browser closes. Use `--profile` to persist state across browser restarts:
|
||||
|
||||
```bash
|
||||
# Use a persistent profile directory
|
||||
agent-browser --profile ~/.myapp-profile open myapp.com
|
||||
|
||||
# Login once, then reuse the authenticated session
|
||||
agent-browser --profile ~/.myapp-profile open myapp.com/dashboard
|
||||
|
||||
# Or via environment variable
|
||||
AGENT_BROWSER_PROFILE=~/.myapp-profile agent-browser open myapp.com
|
||||
```
|
||||
|
||||
The profile directory stores:
|
||||
- Cookies and localStorage
|
||||
- IndexedDB data
|
||||
- Service workers
|
||||
- Browser cache
|
||||
- Login sessions
|
||||
|
||||
**Tip**: Use different profile paths for different projects to keep their browser state isolated.
|
||||
|
||||
## Snapshot Options
|
||||
|
||||
The `snapshot` command supports filtering to reduce output size:
|
||||
@@ -295,6 +319,7 @@ agent-browser snapshot -i -c -d 5 # Combine options
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--session <name>` | Use isolated session (or `AGENT_BROWSER_SESSION` env) |
|
||||
| `--profile <path>` | Persistent browser profile directory (or `AGENT_BROWSER_PROFILE` env) |
|
||||
| `--headers <json>` | Set HTTP headers scoped to the URL's origin |
|
||||
| `--executable-path <path>` | Custom browser executable (or `AGENT_BROWSER_EXECUTABLE_PATH` env) |
|
||||
| `--json` | JSON output (for agents) |
|
||||
|
||||
+9
-1
@@ -10,6 +10,7 @@ pub struct Flags {
|
||||
pub executable_path: Option<String>,
|
||||
pub cdp: Option<String>,
|
||||
pub extensions: Vec<String>,
|
||||
pub profile: Option<String>,
|
||||
}
|
||||
|
||||
pub fn parse_flags(args: &[String]) -> Flags {
|
||||
@@ -28,6 +29,7 @@ pub fn parse_flags(args: &[String]) -> Flags {
|
||||
executable_path: env::var("AGENT_BROWSER_EXECUTABLE_PATH").ok(),
|
||||
cdp: None,
|
||||
extensions: extensions_env,
|
||||
profile: env::var("AGENT_BROWSER_PROFILE").ok(),
|
||||
};
|
||||
|
||||
let mut i = 0;
|
||||
@@ -67,6 +69,12 @@ pub fn parse_flags(args: &[String]) -> Flags {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
"--profile" => {
|
||||
if let Some(s) = args.get(i + 1) {
|
||||
flags.profile = Some(s.clone());
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
i += 1;
|
||||
@@ -81,7 +89,7 @@ pub fn clean_args(args: &[String]) -> Vec<String> {
|
||||
// Global flags that should be stripped from command args
|
||||
const GLOBAL_FLAGS: &[&str] = &["--json", "--full", "--headed", "--debug"];
|
||||
// Global flags that take a value (need to skip the next arg too)
|
||||
const GLOBAL_FLAGS_WITH_VALUE: &[&str] = &["--session", "--headers", "--executable-path", "--cdp", "--extension"];
|
||||
const GLOBAL_FLAGS_WITH_VALUE: &[&str] = &["--session", "--headers", "--executable-path", "--cdp", "--extension", "--profile"];
|
||||
|
||||
for arg in args.iter() {
|
||||
if skip_next {
|
||||
|
||||
+15
-6
@@ -161,8 +161,8 @@ fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
// Warn if executable_path was specified but daemon was already running
|
||||
if daemon_result.already_running && (flags.executable_path.is_some() || !flags.extensions.is_empty()) {
|
||||
// Warn if executable_path, profile, or extensions were specified but daemon was already running
|
||||
if daemon_result.already_running && (flags.executable_path.is_some() || !flags.extensions.is_empty() || flags.profile.is_some()) {
|
||||
if !flags.json {
|
||||
if flags.executable_path.is_some() {
|
||||
eprintln!("\x1b[33m⚠\x1b[0m --executable-path ignored: daemon already running. Use 'agent-browser close' first to restart with new path.");
|
||||
@@ -170,6 +170,9 @@ fn main() {
|
||||
if !flags.extensions.is_empty() {
|
||||
eprintln!("\x1b[33m⚠\x1b[0m --extension ignored: daemon already running. Use 'agent-browser close' first to restart with extensions.");
|
||||
}
|
||||
if flags.profile.is_some() {
|
||||
eprintln!("\x1b[33m⚠\x1b[0m --profile ignored: daemon already running. Use 'agent-browser close' first to restart with profile.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,16 +232,22 @@ fn main() {
|
||||
}
|
||||
|
||||
// Launch headed browser if --headed flag is set (without CDP)
|
||||
if flags.headed && flags.cdp.is_none() {
|
||||
let launch_cmd = json!({
|
||||
// Also launch with profile if --profile is set
|
||||
if (flags.headed || flags.profile.is_some()) && flags.cdp.is_none() {
|
||||
let mut launch_cmd = json!({
|
||||
"id": gen_id(),
|
||||
"action": "launch",
|
||||
"headless": false
|
||||
"headless": !flags.headed
|
||||
});
|
||||
|
||||
// Add profile path if specified
|
||||
if let Some(ref profile_path) = flags.profile {
|
||||
launch_cmd["profile"] = json!(profile_path);
|
||||
}
|
||||
|
||||
if let Err(e) = send_command(launch_cmd, &flags.session) {
|
||||
if !flags.json {
|
||||
eprintln!("\x1b[33m⚠\x1b[0m Could not launch headed browser: {}", e);
|
||||
eprintln!("\x1b[33m⚠\x1b[0m Could not launch browser: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1189,6 +1189,7 @@ Snapshot Options:
|
||||
|
||||
Options:
|
||||
--session <name> Isolated session (or AGENT_BROWSER_SESSION env)
|
||||
--profile <path> Persistent browser profile (or AGENT_BROWSER_PROFILE env)
|
||||
--headers <json> HTTP headers scoped to URL's origin (for auth)
|
||||
--executable-path <path> Custom browser executable (or AGENT_BROWSER_EXECUTABLE_PATH)
|
||||
--extension <path> Load browser extensions (repeatable).
|
||||
@@ -1207,6 +1208,7 @@ Examples:
|
||||
agent-browser get text @e1
|
||||
agent-browser screenshot --full
|
||||
agent-browser --cdp 9222 snapshot # Connect via CDP port
|
||||
agent-browser --profile ~/.myapp open example.com # Persistent profile
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
@@ -609,11 +609,16 @@ export class BrowserManager {
|
||||
async launch(options: LaunchCommand): Promise<void> {
|
||||
const cdpPort = options.cdpPort;
|
||||
const hasExtensions = !!options.extensions?.length;
|
||||
const hasProfile = !!options.profile;
|
||||
|
||||
if (hasExtensions && cdpPort) {
|
||||
throw new Error('Extensions cannot be used with CDP connection');
|
||||
}
|
||||
|
||||
if (hasProfile && cdpPort) {
|
||||
throw new Error('Profile cannot be used with CDP connection');
|
||||
}
|
||||
|
||||
if (this.isLaunched()) {
|
||||
const needsRelaunch =
|
||||
(!cdpPort && this.cdpPort !== null) || (!!cdpPort && this.needsCdpReconnect(cdpPort));
|
||||
@@ -640,6 +645,7 @@ export class BrowserManager {
|
||||
|
||||
let context: BrowserContext;
|
||||
if (hasExtensions) {
|
||||
// Extensions require persistent context in a temp directory
|
||||
const extPaths = options.extensions!.join(',');
|
||||
const session = process.env.AGENT_BROWSER_SESSION || 'default';
|
||||
context = await launcher.launchPersistentContext(
|
||||
@@ -653,7 +659,19 @@ export class BrowserManager {
|
||||
}
|
||||
);
|
||||
this.isPersistentContext = true;
|
||||
} else if (hasProfile) {
|
||||
// Profile uses persistent context for durable cookies/storage
|
||||
// Expand ~ to home directory since it won't be shell-expanded
|
||||
const profilePath = options.profile!.replace(/^~\//, os.homedir() + '/');
|
||||
context = await launcher.launchPersistentContext(profilePath, {
|
||||
headless: options.headless ?? true,
|
||||
executablePath: options.executablePath,
|
||||
viewport,
|
||||
extraHTTPHeaders: options.headers,
|
||||
});
|
||||
this.isPersistentContext = true;
|
||||
} else {
|
||||
// Regular ephemeral browser
|
||||
this.browser = await launcher.launch({
|
||||
headless: options.headless ?? true,
|
||||
executablePath: options.executablePath,
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface LaunchCommand extends BaseCommand {
|
||||
executablePath?: string;
|
||||
cdpPort?: number;
|
||||
extensions?: string[];
|
||||
profile?: string; // Path to persistent browser profile directory
|
||||
}
|
||||
|
||||
export interface NavigateCommand extends BaseCommand {
|
||||
|
||||
Reference in New Issue
Block a user