fix(cdp): 自动拉起 9333 专用浏览器
This commit is contained in:
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user