feat(adopt): read a pre-existing tab without opening a new one
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
New `chrome-use adopt <url-substring|targetId>`: drive a tab the user (or another session) already has open, with ZERO new tabs. After group-scoped isolation (#40) a session can't see foreign tabs, so adopt adds an explicit, opt-in path: - Relay (relay.rs): `ABRelay.getAllTargets` returns every attached target UNSCOPED (ignores group scoping), so the agent can find a specific tab by URL or targetId. +1 unit test. - Daemon (browser.rs): `collect_all_targets` (unscoped, falls back to scoped on older relays) + `adopt_existing_target` — matches by exact targetId or case-insensitive URL substring, attaches it (the relay re-tags it into the adopter's group, so isolation holds), pins it; never creates a tab. On no match it errors AND lists the open tabs it can see, rather than launching. discover_and_attach_targets honors AGENT_BROWSER_ADOPT at first connect, so no about:blank is ever created. - CLI (main.rs): `adopt` sets the env, forces a fresh daemon, and rewrites into `connect <relay-url>` (like `extension connect`) so the daemon attaches to the user's real Chrome before parse_command. Extension (ab-connect 0.4.11): `reannounceAttachedTabs` now re-sends each tab's url/title (it previously sent neither) so the relay's target list stays matchable by URL after the MV3 service worker reconnects — otherwise reannounced tabs show a blank url and `adopt <url>` can't find them. Repacked upload zip + crx. Mechanism verified live (enumerated all 11 of the user's open tabs incl. the target). 862 tests pass.
This commit is contained in:
@@ -155,6 +155,20 @@ impl RelayState {
|
||||
"Target.setDiscoverTargets" | "Target.setAutoAttach" => {
|
||||
ClientRoute::Local(json!({ "id": id, "result": {} }))
|
||||
}
|
||||
// Unscoped discovery for EXPLICIT cross-group adoption (`chrome-use
|
||||
// adopt`): returns every target the extension has attached, ignoring
|
||||
// group scoping, so an agent can find a specific pre-existing tab (the
|
||||
// user's, another session's) by URL/targetId and adopt it. Isolation
|
||||
// is preserved because the daemon only acts on the one tab it then
|
||||
// attaches (which the relay re-tags into the adopter's group).
|
||||
"ABRelay.getAllTargets" => {
|
||||
let infos: Vec<Value> = self
|
||||
.targets
|
||||
.values()
|
||||
.map(|t| t.target_info.clone())
|
||||
.collect();
|
||||
ClientRoute::Local(json!({ "id": id, "result": { "targetInfos": infos } }))
|
||||
}
|
||||
"Target.getTargets" => {
|
||||
// Scope to the client's own group when it announced one; an
|
||||
// un-announced (legacy) client gets the full list (back-compat).
|
||||
@@ -772,6 +786,33 @@ mod tests {
|
||||
assert!(get_target_ids(&mut s, 2).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_all_targets_is_unscoped() {
|
||||
let mut s = RelayState::new();
|
||||
create_in_group(&mut s, 1, "agent-a", "ta", "sa");
|
||||
create_in_group(&mut s, 2, "agent-b", "tb", "sb");
|
||||
// Client 1's scoped getTargets sees only its own group...
|
||||
assert_eq!(get_target_ids(&mut s, 1), vec!["ta"]);
|
||||
// ...but ABRelay.getAllTargets returns EVERY target regardless of group
|
||||
// (for explicit cross-group adoption).
|
||||
let all = match s
|
||||
.route_client_command(1, &json!({ "id": 1, "method": "ABRelay.getAllTargets" }))
|
||||
{
|
||||
ClientRoute::Local(v) => {
|
||||
let mut ids: Vec<String> = v["result"]["targetInfos"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|t| t["targetId"].as_str().unwrap().to_string())
|
||||
.collect();
|
||||
ids.sort();
|
||||
ids
|
||||
}
|
||||
_ => panic!("getAllTargets must be local"),
|
||||
};
|
||||
assert_eq!(all, vec!["ta", "tb"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detach_clears_target_group() {
|
||||
let mut s = RelayState::new();
|
||||
|
||||
Reference in New Issue
Block a user