From 266b6103585ba9c980217338aacb4ba424f2569e Mon Sep 17 00:00:00 2001 From: leeguooooo Date: Fri, 12 Jun 2026 12:07:46 +0900 Subject: [PATCH] feat(launch): label the throwaway --launch profile + document escape hatches (issue #9) A bare --launch opens an isolated empty profile (no cookies/login/ extensions). A human watching the desktop sees a mystery Chrome window under an unfamiliar profile and reads it as broken/suspicious. - Seed the temp profile's Local State (profile.info_cache.Default.name, the field Chrome's profile chip reads) + Default/Preferences with 'agent-browser ()', so the window self-identifies which agent session owns it. - Rewrite the --launch warning to explain it's an isolated test profile and point at the escape hatches: --profile auto / AGENT_BROWSER_PROFILE=auto to reuse real Chrome, and --args "--load-extension=" for extensions. - SKILL.md documents the same. Adds a unit test for the profile-label writer. --- cli/src/main.rs | 10 ++++-- cli/src/native/cdp/chrome.rs | 60 ++++++++++++++++++++++++++++++++++++ skill-data/core/SKILL.md | 11 +++++++ 3 files changed, 78 insertions(+), 3 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index 8f6fb9f..a9d5cda 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -547,9 +547,13 @@ fn main() { // Skipped under CI (force_launch is implicit there and login isn't expected). if flags.force_launch && flags.profile.is_none() && env::var("CI").is_err() { eprintln!( - "⚠ --launch uses a temporary EMPTY browser profile (no cookies, no login). \ - For logged-in sites, add `--profile auto` (or `--profile Default`) to reuse \ - your real Chrome session." + "⚠ --launch opens a fresh, isolated test profile (no cookies, no login, no \ + extensions). The window is labelled `agent-browser ()` in Chrome's \ + profile menu so you can tell it apart from your real browser.\n \ + • reuse your real Chrome (cookies/login/extensions): `--profile auto` \ + (or set AGENT_BROWSER_PROFILE=auto once)\n \ + • load an unpacked extension into the test profile: \ + `--args \"--load-extension=\"`" ); } diff --git a/cli/src/native/cdp/chrome.rs b/cli/src/native/cdp/chrome.rs index f9f0482..7203b9d 100644 --- a/cli/src/native/cdp/chrome.rs +++ b/cli/src/native/cdp/chrome.rs @@ -180,6 +180,30 @@ fn webrtc_ip_handling_policy(has_proxy: bool) -> Option<&'static str> { } } +/// Seed a throwaway `--launch` profile with a human-readable name +/// (`agent-browser ()`) so Chrome's toolbar profile chip identifies the +/// window as an agent's test profile rather than an anonymous empty profile +/// (issue #9). The name lives in `Local State`'s `profile.info_cache..name` +/// — the same field `resolve_chrome_profile("auto")` reads. Best-effort: any +/// write error is ignored (the profile still works, just unlabeled). +fn write_temp_profile_label(dir: &std::path::Path) { + let session = std::env::var("AGENT_BROWSER_SESSION").unwrap_or_else(|_| "default".to_string()); + let label = format!("agent-browser ({session})"); + let local_state = serde_json::json!({ + "profile": { + "info_cache": { + "Default": { "name": label, "is_using_default_name": false } + } + } + }); + let _ = std::fs::write(dir.join("Local State"), local_state.to_string()); + let default_dir = dir.join("Default"); + if std::fs::create_dir_all(&default_dir).is_ok() { + let prefs = serde_json::json!({ "profile": { "name": label } }); + let _ = std::fs::write(default_dir.join("Preferences"), prefs.to_string()); + } +} + fn build_chrome_args(options: &LaunchOptions) -> Result { let mut args = vec![ "--remote-debugging-port=0".to_string(), @@ -266,6 +290,10 @@ fn build_chrome_args(options: &LaunchOptions) -> Result { std::env::temp_dir().join(format!("agent-browser-chrome-{}", uuid::Uuid::new_v4())); std::fs::create_dir_all(&dir) .map_err(|e| format!("Failed to create temp profile dir: {}", e))?; + // Label the throwaway profile so a human watching the desktop can tell + // which agent session owns this otherwise-anonymous empty-profile window, + // instead of "which profile is this? where did it come from?" (issue #9). + write_temp_profile_label(&dir); args.push(format!("--user-data-dir={}", dir.display())); (dir.clone(), Some(dir)) }; @@ -1890,6 +1918,38 @@ mod tests { let _ = std::fs::remove_dir_all(&tmp); } + #[test] + fn test_write_temp_profile_label_names_the_profile() { + // issue #9: a throwaway --launch profile must carry a human-readable name + // in Local State (the field Chrome's profile chip reads) + Preferences. + let tmp = std::env::temp_dir().join("ab-label-test"); + let _ = std::fs::remove_dir_all(&tmp); + std::fs::create_dir_all(&tmp).unwrap(); + write_temp_profile_label(&tmp); + + let ls: serde_json::Value = + serde_json::from_str(&std::fs::read_to_string(tmp.join("Local State")).unwrap()) + .unwrap(); + let name = ls["profile"]["info_cache"]["Default"]["name"] + .as_str() + .unwrap(); + assert!(name.starts_with("agent-browser ("), "got: {name}"); + assert_eq!( + ls["profile"]["info_cache"]["Default"]["is_using_default_name"], + serde_json::json!(false) + ); + + let prefs: serde_json::Value = serde_json::from_str( + &std::fs::read_to_string(tmp.join("Default/Preferences")).unwrap(), + ) + .unwrap(); + assert!(prefs["profile"]["name"] + .as_str() + .unwrap() + .starts_with("agent-browser (")); + let _ = std::fs::remove_dir_all(&tmp); + } + #[test] fn test_resolve_chrome_profile_auto_falls_back_to_default() { let tmp = std::env::temp_dir().join("ab-auto-default-test"); diff --git a/skill-data/core/SKILL.md b/skill-data/core/SKILL.md index 6844cea..bdeb402 100644 --- a/skill-data/core/SKILL.md +++ b/skill-data/core/SKILL.md @@ -73,6 +73,17 @@ popup never fires. `agent-browser extension connect` is the explicit form of the same path. Zero-confirmation, zero-token. Use `--launch` instead when a fresh, isolated browser is fine. +`--launch` opens an **isolated, empty test profile** — no cookies, no login, no +extensions (so the extension-relay path is off). Its window is labelled +`agent-browser ()` in Chrome's profile menu so a human watching the +desktop knows which session owns it. If a launched session needs more: + +- **Real cookies / login / extensions** → drop `--launch`, use `--profile auto` + (reuses the user's real Chrome profile), or set `AGENT_BROWSER_PROFILE=auto` + once so every call does it by default. +- **A specific unpacked extension in the test profile** → + `--launch --args "--load-extension="`. + **If you DO hit the "Allow remote debugging?" dialog**, don't keep retrying (every attempt re-pops it). One of two things is true: