Consistent Tab IDs & Global Tag Targeting (#892)
Introduces stable per-tab IDs and a global `--tab <id>` flag for scoping individual commands to a specific tab.
Breaking change: response payloads for `tab_list`, `tab_new`, `tab_switch`, `tab_close`, and `window_new` now use `tabId` instead of `index`. `tab_close` returns `{tabId, closed: true}` instead of `{closed, activeIndex}`. `agent-browser tab <unknown>` now errors instead of silently listing tabs.
Follow-up PR to land immediately after this fixes a compile error on the provider direct-page path, clears per-tab daemon state around scoped switches, and implements active-tab restoration so `--tab N` is non-intrusive as intended.
This commit is contained in:
@@ -57,6 +57,7 @@ pub struct Config {
|
||||
pub json: Option<bool>,
|
||||
pub debug: Option<bool>,
|
||||
pub session: Option<String>,
|
||||
pub tab: Option<u32>,
|
||||
pub session_name: Option<String>,
|
||||
pub executable_path: Option<String>,
|
||||
pub extensions: Option<Vec<String>>,
|
||||
@@ -98,6 +99,7 @@ impl Config {
|
||||
json: other.json.or(self.json),
|
||||
debug: other.debug.or(self.debug),
|
||||
session: other.session.or(self.session),
|
||||
tab: other.tab.or(self.tab),
|
||||
session_name: other.session_name.or(self.session_name),
|
||||
executable_path: other.executable_path.or(self.executable_path),
|
||||
extensions: match (self.extensions, other.extensions) {
|
||||
@@ -196,6 +198,7 @@ fn parse_bool_arg(args: &[String], i: usize) -> (bool, bool) {
|
||||
fn extract_config_path(args: &[String]) -> Option<Option<String>> {
|
||||
const FLAGS_WITH_VALUE: &[&str] = &[
|
||||
"--session",
|
||||
"--tab",
|
||||
"--headers",
|
||||
"--executable-path",
|
||||
"--cdp",
|
||||
@@ -273,6 +276,7 @@ pub struct Flags {
|
||||
pub headed: bool,
|
||||
pub debug: bool,
|
||||
pub session: String,
|
||||
pub tab: Option<u32>,
|
||||
pub headers: Option<String>,
|
||||
pub executable_path: Option<String>,
|
||||
pub cdp: Option<String>,
|
||||
@@ -355,6 +359,7 @@ pub fn parse_flags(args: &[String]) -> Flags {
|
||||
.ok()
|
||||
.or(config.session)
|
||||
.unwrap_or_else(|| "default".to_string()),
|
||||
tab: config.tab,
|
||||
headers: config.headers,
|
||||
executable_path: env::var("AGENT_BROWSER_EXECUTABLE_PATH")
|
||||
.ok()
|
||||
@@ -492,6 +497,12 @@ pub fn parse_flags(args: &[String]) -> Flags {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
"--tab" => {
|
||||
if let Some(s) = args.get(i + 1) {
|
||||
flags.tab = s.parse::<u32>().ok();
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
"--idle-timeout" => {
|
||||
if let Some(s) = args.get(i + 1) {
|
||||
match parse_idle_timeout(s) {
|
||||
@@ -775,6 +786,7 @@ pub fn clean_args(args: &[String]) -> Vec<String> {
|
||||
// Global flags that always take a value (need to skip the next arg too)
|
||||
const GLOBAL_FLAGS_WITH_VALUE: &[&str] = &[
|
||||
"--session",
|
||||
"--tab",
|
||||
"--headers",
|
||||
"--executable-path",
|
||||
"--cdp",
|
||||
@@ -1441,4 +1453,25 @@ mod tests {
|
||||
let clean = clean_args(&input);
|
||||
assert_eq!(clean, vec!["open", "example.com"]);
|
||||
}
|
||||
|
||||
// === Tab flag tests ===
|
||||
|
||||
#[test]
|
||||
fn test_parse_tab_flag() {
|
||||
let flags = parse_flags(&args("--tab 4 snapshot"));
|
||||
assert_eq!(flags.tab, Some(4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clean_args_removes_tab_flag() {
|
||||
let cleaned = clean_args(&args("--tab 4 snapshot"));
|
||||
assert_eq!(cleaned, vec!["snapshot"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_tab_config() {
|
||||
let json = r#"{"tab": 4}"#;
|
||||
let config: Config = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(config.tab, Some(4));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user