feat(browser): add optional computer-use sidecar backend (#335)

This commit is contained in:
Chummy 2026-02-17 00:31:45 +08:00 committed by GitHub
parent db0904c8a4
commit 04bf94443f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 625 additions and 24 deletions

View file

@ -419,6 +419,53 @@ impl Default for SecretsConfig {
// ── Browser (friendly-service browsing only) ───────────────────
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BrowserComputerUseConfig {
/// Sidecar endpoint for computer-use actions (OS-level mouse/keyboard/screenshot)
#[serde(default = "default_browser_computer_use_endpoint")]
pub endpoint: String,
/// Optional bearer token for computer-use sidecar
#[serde(default)]
pub api_key: Option<String>,
/// Per-action request timeout in milliseconds
#[serde(default = "default_browser_computer_use_timeout_ms")]
pub timeout_ms: u64,
/// Allow remote/public endpoint for computer-use sidecar (default: false)
#[serde(default)]
pub allow_remote_endpoint: bool,
/// Optional window title/process allowlist forwarded to sidecar policy
#[serde(default)]
pub window_allowlist: Vec<String>,
/// Optional X-axis boundary for coordinate-based actions
#[serde(default)]
pub max_coordinate_x: Option<i64>,
/// Optional Y-axis boundary for coordinate-based actions
#[serde(default)]
pub max_coordinate_y: Option<i64>,
}
fn default_browser_computer_use_endpoint() -> String {
"http://127.0.0.1:8787/v1/actions".into()
}
fn default_browser_computer_use_timeout_ms() -> u64 {
15_000
}
impl Default for BrowserComputerUseConfig {
fn default() -> Self {
Self {
endpoint: default_browser_computer_use_endpoint(),
api_key: None,
timeout_ms: default_browser_computer_use_timeout_ms(),
allow_remote_endpoint: false,
window_allowlist: Vec::new(),
max_coordinate_x: None,
max_coordinate_y: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BrowserConfig {
/// Enable `browser_open` tool (opens URLs in Brave without scraping)
@ -430,7 +477,7 @@ pub struct BrowserConfig {
/// Browser session name (for agent-browser automation)
#[serde(default)]
pub session_name: Option<String>,
/// Browser automation backend: "agent_browser" | "rust_native" | "auto"
/// Browser automation backend: "agent_browser" | "rust_native" | "computer_use" | "auto"
#[serde(default = "default_browser_backend")]
pub backend: String,
/// Headless mode for rust-native backend
@ -442,6 +489,9 @@ pub struct BrowserConfig {
/// Optional Chrome/Chromium executable path for rust-native backend
#[serde(default)]
pub native_chrome_path: Option<String>,
/// Computer-use sidecar configuration
#[serde(default)]
pub computer_use: BrowserComputerUseConfig,
}
fn default_browser_backend() -> String {
@ -462,6 +512,7 @@ impl Default for BrowserConfig {
native_headless: default_true(),
native_webdriver_url: default_browser_webdriver_url(),
native_chrome_path: None,
computer_use: BrowserComputerUseConfig::default(),
}
}
}
@ -2334,6 +2385,12 @@ default_temperature = 0.7
assert!(b.native_headless);
assert_eq!(b.native_webdriver_url, "http://127.0.0.1:9515");
assert!(b.native_chrome_path.is_none());
assert_eq!(b.computer_use.endpoint, "http://127.0.0.1:8787/v1/actions");
assert_eq!(b.computer_use.timeout_ms, 15_000);
assert!(!b.computer_use.allow_remote_endpoint);
assert!(b.computer_use.window_allowlist.is_empty());
assert!(b.computer_use.max_coordinate_x.is_none());
assert!(b.computer_use.max_coordinate_y.is_none());
}
#[test]
@ -2346,6 +2403,15 @@ default_temperature = 0.7
native_headless: false,
native_webdriver_url: "http://localhost:4444".into(),
native_chrome_path: Some("/usr/bin/chromium".into()),
computer_use: BrowserComputerUseConfig {
endpoint: "https://computer-use.example.com/v1/actions".into(),
api_key: Some("test-token".into()),
timeout_ms: 8_000,
allow_remote_endpoint: true,
window_allowlist: vec!["Chrome".into(), "Visual Studio Code".into()],
max_coordinate_x: Some(3840),
max_coordinate_y: Some(2160),
},
};
let toml_str = toml::to_string(&b).unwrap();
let parsed: BrowserConfig = toml::from_str(&toml_str).unwrap();
@ -2359,6 +2425,16 @@ default_temperature = 0.7
parsed.native_chrome_path.as_deref(),
Some("/usr/bin/chromium")
);
assert_eq!(
parsed.computer_use.endpoint,
"https://computer-use.example.com/v1/actions"
);
assert_eq!(parsed.computer_use.api_key.as_deref(), Some("test-token"));
assert_eq!(parsed.computer_use.timeout_ms, 8_000);
assert!(parsed.computer_use.allow_remote_endpoint);
assert_eq!(parsed.computer_use.window_allowlist.len(), 2);
assert_eq!(parsed.computer_use.max_coordinate_x, Some(3840));
assert_eq!(parsed.computer_use.max_coordinate_y, Some(2160));
}
#[test]