fix(cdp): 自动拉起 9333 专用浏览器

This commit is contained in:
leeguooooo
2026-03-09 17:45:13 +09:00
parent b3ed4b63be
commit 8eee9310a4
11 changed files with 249 additions and 100 deletions
+19 -2
View File
@@ -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
+30 -2
View File
@@ -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
+46 -1
View File
@@ -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),