fix(cdp): 自动拉起 9333 专用浏览器
This commit is contained in:
+4
-25
@@ -623,8 +623,9 @@ fn main() {
|
||||
}
|
||||
|
||||
// Project policy: when no explicit connection mode is provided,
|
||||
// commands should attach to an existing browser.
|
||||
// Try CDP :9333 first, then fall back to auto-connect discovery.
|
||||
// commands should use the dedicated automation browser on localhost:9333.
|
||||
// If 9333 is unavailable, the native daemon auto-starts a managed Chrome
|
||||
// instance with a non-default profile and retries the CDP connection.
|
||||
if can_try_default_cdp {
|
||||
let mut launch_cmd = json!({
|
||||
"id": gen_id(),
|
||||
@@ -645,31 +646,9 @@ fn main() {
|
||||
if let Ok(resp) = send_command(launch_cmd, &flags.session) {
|
||||
attached_to_existing_browser = resp.success;
|
||||
}
|
||||
|
||||
if !attached_to_existing_browser {
|
||||
let mut auto_connect_cmd = json!({
|
||||
"id": gen_id(),
|
||||
"action": "launch",
|
||||
"autoConnect": true
|
||||
});
|
||||
|
||||
if let Some(ref cs) = flags.color_scheme {
|
||||
auto_connect_cmd["colorScheme"] = json!(cs);
|
||||
}
|
||||
if let Some(ref tg) = flags.tab_group {
|
||||
auto_connect_cmd["tabGroup"] = json!(tg);
|
||||
}
|
||||
if let Some(ref plugin_id) = flags.tab_group_plugin_id {
|
||||
auto_connect_cmd["tabGroupPluginId"] = json!(plugin_id);
|
||||
}
|
||||
|
||||
if let Ok(resp) = send_command(auto_connect_cmd, &flags.session) {
|
||||
attached_to_existing_browser = resp.success;
|
||||
}
|
||||
}
|
||||
}
|
||||
if can_try_default_cdp && !attached_to_existing_browser {
|
||||
let msg = "Project policy requires using your existing browser. Could not connect to CDP at localhost:9333 and auto-discovery also failed. Start Chrome with remote debugging (for example, --remote-debugging-port=9333), or pass --cdp <port|url>.";
|
||||
let msg = "Project policy requires using the dedicated automation browser on localhost:9333. Could not connect to or auto-start the managed Chrome profile. Start Chrome with --remote-debugging-port=9333 and a non-default --user-data-dir, or pass --cdp / --auto-connect explicitly.";
|
||||
if flags.json {
|
||||
println!(r#"{{"success":false,"error":"{}"}}"#, msg);
|
||||
} else {
|
||||
|
||||
@@ -4,7 +4,7 @@ use tokio::sync::broadcast;
|
||||
|
||||
use super::auth;
|
||||
use super::browser::{BrowserManager, WaitUntil};
|
||||
use super::cdp::chrome::LaunchOptions;
|
||||
use super::cdp::chrome::{LaunchOptions, MANAGED_CDP_PORT};
|
||||
use super::cdp::types::{
|
||||
AttachToTargetParams, AttachToTargetResult, CdpEvent, ConsoleApiCalledEvent,
|
||||
CreateTargetResult, ExceptionThrownEvent, TargetCreatedEvent, TargetDestroyedEvent,
|
||||
@@ -790,6 +790,7 @@ fn launch_options_from_env() -> LaunchOptions {
|
||||
.unwrap_or(false),
|
||||
color_scheme: env::var("AGENT_BROWSER_COLOR_SCHEME").ok(),
|
||||
download_path: env::var("AGENT_BROWSER_DOWNLOAD_PATH").ok(),
|
||||
remote_debugging_port: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -898,7 +899,22 @@ async fn handle_launch(cmd: &Value, state: &mut DaemonState) -> Result<Value, St
|
||||
}
|
||||
|
||||
if let Some(port) = cdp_port {
|
||||
state.browser = Some(BrowserManager::connect_cdp(&port.to_string()).await?);
|
||||
let headed = !headless;
|
||||
let port_u16 = u16::try_from(port).map_err(|_| format!("Invalid CDP port: {}", port))?;
|
||||
let browser = match BrowserManager::connect_cdp(&port.to_string()).await {
|
||||
Ok(browser) => browser,
|
||||
Err(err) if port_u16 == MANAGED_CDP_PORT => {
|
||||
if std::env::var("AGENT_BROWSER_DEBUG").as_deref() == Ok("1") {
|
||||
eprintln!(
|
||||
"[DEBUG] Preferred CDP port {} unavailable ({}), launching managed Chrome profile",
|
||||
MANAGED_CDP_PORT, err
|
||||
);
|
||||
}
|
||||
BrowserManager::launch_managed_cdp(executable_path.clone(), headed).await?
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
state.browser = Some(browser);
|
||||
state.subscribe_to_browser_events();
|
||||
return Ok(json!({ "launched": true }));
|
||||
}
|
||||
@@ -990,6 +1006,7 @@ async fn handle_launch(cmd: &Value, state: &mut DaemonState) -> Result<Value, St
|
||||
.get("downloadPath")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(String::from),
|
||||
remote_debugging_port: None,
|
||||
};
|
||||
|
||||
if let Some(ref domains) = cmd
|
||||
|
||||
@@ -5,7 +5,8 @@ use tokio::sync::Mutex;
|
||||
use tokio::time::{timeout, Duration};
|
||||
|
||||
use super::cdp::chrome::{
|
||||
auto_connect_cdp, discover_cdp_url, launch_chrome, ChromeProcess, LaunchOptions,
|
||||
auto_connect_cdp, discover_cdp_url, launch_chrome, launch_managed_chrome, ChromeProcess,
|
||||
LaunchOptions,
|
||||
};
|
||||
use super::cdp::client::CdpClient;
|
||||
use super::cdp::lightpanda::{launch_lightpanda, LightpandaLaunchOptions, LightpandaProcess};
|
||||
@@ -144,6 +145,7 @@ pub enum BrowserProcess {
|
||||
pub struct BrowserManager {
|
||||
pub client: CdpClient,
|
||||
browser_process: Option<BrowserProcess>,
|
||||
cdp_connection: bool,
|
||||
pages: Vec<PageInfo>,
|
||||
active_page_index: usize,
|
||||
default_timeout_ms: u64,
|
||||
@@ -202,6 +204,7 @@ impl BrowserManager {
|
||||
let mut manager = Self {
|
||||
client,
|
||||
browser_process: Some(process),
|
||||
cdp_connection: false,
|
||||
pages: Vec::new(),
|
||||
active_page_index: 0,
|
||||
default_timeout_ms: 25_000,
|
||||
@@ -264,6 +267,31 @@ impl BrowserManager {
|
||||
let mut manager = Self {
|
||||
client,
|
||||
browser_process: None,
|
||||
cdp_connection: true,
|
||||
pages: Vec::new(),
|
||||
active_page_index: 0,
|
||||
default_timeout_ms: 10_000,
|
||||
};
|
||||
|
||||
manager.discover_and_attach_targets().await?;
|
||||
Ok(manager)
|
||||
}
|
||||
|
||||
pub async fn launch_managed_cdp(
|
||||
executable_path: Option<String>,
|
||||
headed: bool,
|
||||
) -> Result<Self, String> {
|
||||
let process =
|
||||
tokio::task::spawn_blocking(move || launch_managed_chrome(executable_path, headed))
|
||||
.await
|
||||
.map_err(|e| format!("Managed Chrome launch task failed: {}", e))??;
|
||||
|
||||
let ws_url = process.ws_url.clone();
|
||||
let client = CdpClient::connect(&ws_url).await?;
|
||||
let mut manager = Self {
|
||||
client,
|
||||
browser_process: Some(BrowserProcess::Chrome(process)),
|
||||
cdp_connection: true,
|
||||
pages: Vec::new(),
|
||||
active_page_index: 0,
|
||||
default_timeout_ms: 10_000,
|
||||
@@ -643,7 +671,7 @@ impl BrowserManager {
|
||||
|
||||
/// Returns true if this manager was connected via CDP (as opposed to local launch).
|
||||
pub fn is_cdp_connection(&self) -> bool {
|
||||
self.browser_process.is_none()
|
||||
self.cdp_connection
|
||||
}
|
||||
|
||||
/// Ensures the browser has at least one page. If `pages` is empty, creates a new
|
||||
|
||||
@@ -73,6 +73,7 @@ pub struct LaunchOptions {
|
||||
pub ignore_https_errors: bool,
|
||||
pub color_scheme: Option<String>,
|
||||
pub download_path: Option<String>,
|
||||
pub remote_debugging_port: Option<u16>,
|
||||
}
|
||||
|
||||
impl Default for LaunchOptions {
|
||||
@@ -91,6 +92,7 @@ impl Default for LaunchOptions {
|
||||
ignore_https_errors: false,
|
||||
color_scheme: None,
|
||||
download_path: None,
|
||||
remote_debugging_port: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,8 +103,10 @@ struct ChromeArgs {
|
||||
}
|
||||
|
||||
fn build_chrome_args(options: &LaunchOptions) -> Result<ChromeArgs, String> {
|
||||
let remote_debugging_port = options.remote_debugging_port.unwrap_or(0);
|
||||
let mut args = vec![
|
||||
"--remote-debugging-port=0".to_string(),
|
||||
format!("--remote-debugging-port={}", remote_debugging_port),
|
||||
"--remote-debugging-address=127.0.0.1".to_string(),
|
||||
"--no-first-run".to_string(),
|
||||
"--no-default-browser-check".to_string(),
|
||||
"--disable-background-networking".to_string(),
|
||||
@@ -186,6 +190,47 @@ fn build_chrome_args(options: &LaunchOptions) -> Result<ChromeArgs, String> {
|
||||
})
|
||||
}
|
||||
|
||||
pub const MANAGED_CDP_PORT: u16 = 9333;
|
||||
|
||||
pub fn managed_cdp_profile_dir() -> PathBuf {
|
||||
dirs::home_dir()
|
||||
.unwrap_or_else(|| std::env::temp_dir())
|
||||
.join(".agent-browser")
|
||||
.join("chrome-bot-profile")
|
||||
}
|
||||
|
||||
fn cleanup_managed_profile_locks(profile_dir: &Path) {
|
||||
let _ = std::fs::remove_file(profile_dir.join("DevToolsActivePort"));
|
||||
if let Ok(entries) = std::fs::read_dir(profile_dir) {
|
||||
for entry in entries.flatten() {
|
||||
let name = entry.file_name();
|
||||
if name.to_string_lossy().starts_with("Singleton") {
|
||||
let _ = std::fs::remove_file(entry.path());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn launch_managed_chrome(
|
||||
executable_path: Option<String>,
|
||||
headed: bool,
|
||||
) -> Result<ChromeProcess, String> {
|
||||
let profile_dir = managed_cdp_profile_dir();
|
||||
std::fs::create_dir_all(&profile_dir)
|
||||
.map_err(|e| format!("Failed to create managed Chrome profile dir: {}", e))?;
|
||||
cleanup_managed_profile_locks(&profile_dir);
|
||||
|
||||
let options = LaunchOptions {
|
||||
headless: !headed,
|
||||
executable_path,
|
||||
profile: Some(profile_dir.to_string_lossy().to_string()),
|
||||
remote_debugging_port: Some(MANAGED_CDP_PORT),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
launch_chrome(&options)
|
||||
}
|
||||
|
||||
pub fn launch_chrome(options: &LaunchOptions) -> Result<ChromeProcess, String> {
|
||||
let chrome_path = match &options.executable_path {
|
||||
Some(p) => PathBuf::from(p),
|
||||
|
||||
+3
-2
@@ -2461,7 +2461,7 @@ Options:
|
||||
--headed Show browser window (not headless) (or AGENT_BROWSER_HEADED=1/true)
|
||||
--cdp <port> Connect via CDP (Chrome DevTools Protocol)
|
||||
--auto-connect Auto-discover and connect to running Chrome
|
||||
Project default: try localhost:9333 first, then auto-discovery (no managed local-launch fallback)
|
||||
Explicit existing-browser mode; may trigger Chrome permission prompts
|
||||
--color-scheme <scheme> Color scheme: dark, light, no-preference (or AGENT_BROWSER_COLOR_SCHEME)
|
||||
--download-path <path> Default download directory (or AGENT_BROWSER_DOWNLOAD_PATH)
|
||||
--tab-group <name> Base title for agent tab groups (CDP plugin mode; silent no-op if plugin unavailable)
|
||||
@@ -2490,7 +2490,8 @@ Policy:
|
||||
--profile / AGENT_BROWSER_PROFILE are forbidden
|
||||
--channel / AGENT_BROWSER_CHANNEL are forbidden
|
||||
Daemon auto-shuts down after 10 minutes of inactivity unless --resident is set
|
||||
Auto-attach existing browser (prefer CDP localhost:9333, then auto-discovery), or pass --cdp explicitly
|
||||
Default mode uses localhost:9333. If 9333 is unavailable, agent-browser auto-starts a dedicated Chrome profile at ~/.agent-browser/chrome-bot-profile
|
||||
Use --auto-connect only when you explicitly want to attach to an existing manual browser session
|
||||
|
||||
Configuration:
|
||||
agent-browser looks for agent-browser.json in these locations (lowest to highest priority):
|
||||
|
||||
Reference in New Issue
Block a user