Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4bcfe74514 | ||
|
|
bb41c24c08 | ||
|
|
75bd1d21a7 | ||
|
|
06c75af46a | ||
|
|
312bb0d65b | ||
|
|
cff003c333 |
Generated
+1
-1
@@ -45,7 +45,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "agent-browser-stealth"
|
||||
version = "0.27.0-fork.22"
|
||||
version = "0.27.0-fork.25"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"async-trait",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "agent-browser-stealth"
|
||||
version = "0.27.0-fork.22"
|
||||
version = "0.27.0-fork.25"
|
||||
edition = "2021"
|
||||
description = "Fast browser automation CLI for AI agents"
|
||||
license = "Apache-2.0"
|
||||
|
||||
@@ -672,7 +672,10 @@ impl DaemonState {
|
||||
}
|
||||
|
||||
let tab_id = mgr.assign_tab_id();
|
||||
mgr.add_page(super::browser::PageInfo {
|
||||
// Passively discovered (event-driven) — must NOT steal the
|
||||
// active tab, or a foreign/user/other-session tab opening
|
||||
// hijacks this session's eval/screenshot target.
|
||||
mgr.add_background_page(super::browser::PageInfo {
|
||||
tab_id,
|
||||
label: None,
|
||||
target_id: te.target_info.target_id.clone(),
|
||||
|
||||
@@ -1532,6 +1532,21 @@ impl BrowserManager {
|
||||
self.active_page_index = index;
|
||||
}
|
||||
|
||||
/// Add a passively-discovered page WITHOUT changing the active tab.
|
||||
///
|
||||
/// On a shared browser (ab-connect), `Target.targetCreated` events stream in
|
||||
/// for tabs the user or OTHER agent sessions open. Those are drained on every
|
||||
/// command; routing them through `add_page` made the active tab silently jump
|
||||
/// to a foreign tab, so the session's own `eval`/`get title`/`screenshot`
|
||||
/// landed on the wrong page. Passively-tracked pages must not steal focus —
|
||||
/// only explicit opens (`tab new`, switch) set the active tab.
|
||||
pub fn add_background_page(&mut self, page: PageInfo) {
|
||||
if self.pages.iter().any(|p| p.target_id == page.target_id) {
|
||||
return;
|
||||
}
|
||||
self.pages.push(page);
|
||||
}
|
||||
|
||||
pub fn update_page_target_info(&mut self, target: &TargetInfo) -> bool {
|
||||
update_page_target_info_in_pages(&mut self.pages, target)
|
||||
}
|
||||
|
||||
@@ -106,7 +106,13 @@ pub struct TargetInfo {
|
||||
pub target_id: String,
|
||||
#[serde(rename = "type")]
|
||||
pub target_type: String,
|
||||
// Tolerate minimal targetInfo: the ab-connect relay's synthesized
|
||||
// Target.attachedToTarget (re-announce path) omits title/url, and real CDP
|
||||
// occasionally omits them too. Default to empty rather than fail the whole
|
||||
// Target.getTargets deserialize.
|
||||
#[serde(default)]
|
||||
pub title: String,
|
||||
#[serde(default)]
|
||||
pub url: String,
|
||||
pub attached: Option<bool>,
|
||||
pub browser_context_id: Option<String>,
|
||||
|
||||
@@ -107,6 +107,22 @@ impl RelayState {
|
||||
let session_id = raw.get("sessionId").and_then(|s| s.as_str());
|
||||
|
||||
match method {
|
||||
// Browser-level command the daemon uses as its liveness probe
|
||||
// (`is_connection_alive` → `Browser.getVersion`). The extension only
|
||||
// speaks per-tab `chrome.debugger`, so forwarding it errors → the
|
||||
// daemon would deem the connection dead and reconnect+re-discover on
|
||||
// EVERY command, resetting the active tab (eval/screenshot drift).
|
||||
// Answer it locally so the relay connection reads as alive.
|
||||
"Browser.getVersion" => ClientRoute::Local(json!({
|
||||
"id": id,
|
||||
"result": {
|
||||
"protocolVersion": "1.3",
|
||||
"product": "Chrome/ab-connect-relay",
|
||||
"revision": "",
|
||||
"userAgent": "",
|
||||
"jsVersion": ""
|
||||
}
|
||||
})),
|
||||
// Discovery is best-effort and event-driven in real CDP; abs only
|
||||
// reads the getTargets result, so an empty ack is enough here.
|
||||
"Target.setDiscoverTargets" | "Target.setAutoAttach" => {
|
||||
@@ -302,6 +318,21 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn browser_get_version_is_answered_locally() {
|
||||
// Liveness probe must NOT be forwarded (the extension can't do
|
||||
// browser-level commands) — else the daemon reconnects on every command.
|
||||
let mut s = RelayState::new();
|
||||
let route = s.route_client_command(1, &json!({ "id": 7, "method": "Browser.getVersion" }));
|
||||
match route {
|
||||
ClientRoute::Local(v) => {
|
||||
assert_eq!(v["id"], 7);
|
||||
assert!(v["result"]["protocolVersion"].is_string());
|
||||
}
|
||||
_ => panic!("Browser.getVersion must be answered locally"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn attach_to_target_returns_known_session() {
|
||||
let mut s = RelayState::new();
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "agent-browser-stealth",
|
||||
"version": "0.27.0-fork.22",
|
||||
"version": "0.27.0-fork.25",
|
||||
"description": "Browser automation CLI for AI agents \u2014 stealth fork with anti-detection",
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@11.1.3",
|
||||
|
||||
Reference in New Issue
Block a user