Skip to main content

agent_first_http/cli/cmd/
tabs.rs

1//! `afhttp tabs` — list and close CDP targets.
2
3use serde_json::Value;
4
5use crate::cli::output;
6use crate::sdk::Client;
7use crate::shared::error::{Error, ErrorCode};
8
9#[derive(Debug)]
10pub struct Args {
11    pub sub: TabsSub,
12}
13
14#[derive(Debug)]
15pub enum TabsSub {
16    List(EndpointArgs),
17    Close(CloseArgs),
18}
19
20#[derive(Debug)]
21pub struct EndpointArgs {
22    pub endpoint: String,
23    pub token: Option<String>,
24}
25
26#[derive(Debug)]
27pub struct CloseArgs {
28    pub tab: String,
29    pub endpoint: String,
30    pub token: Option<String>,
31}
32
33pub async fn run(args: Args) -> Result<(), Error> {
34    match args.sub {
35        TabsSub::List(a) => list(a).await,
36        TabsSub::Close(a) => close(a).await,
37    }
38}
39
40fn build_client(endpoint: &str, token: Option<String>) -> Result<Client, Error> {
41    let mut client = Client::connect(endpoint)?;
42    if let Some(t) = token {
43        client = client.with_token(t);
44    }
45    Ok(client)
46}
47
48async fn list(args: EndpointArgs) -> Result<(), Error> {
49    let client = build_client(&args.endpoint, args.token)?;
50    let response = client.cdp("Target.getTargets").send().await?;
51    // `Client.cdp(...).send()` unwraps the JSON-RPC `result` envelope, so
52    // `response` is the inner method result and we read `targetInfos`
53    // off it directly.
54    let targets = response
55        .get("targetInfos")
56        .cloned()
57        .unwrap_or(Value::Array(Vec::new()));
58    let payload = serde_json::json!({
59        "code": "tabs",
60        "targets": targets,
61    });
62    output::emit("tabs", &payload)
63}
64
65async fn close(args: CloseArgs) -> Result<(), Error> {
66    if args.tab.trim().is_empty() {
67        return Err(Error::new(
68            ErrorCode::InvalidArgument,
69            "tabs close: target id must not be empty",
70        ));
71    }
72    let client = build_client(&args.endpoint, args.token)?;
73    let response = client
74        .cdp("Target.closeTarget")
75        .params(serde_json::json!({ "targetId": args.tab }))
76        .send()
77        .await?;
78    let success = response
79        .get("success")
80        .and_then(Value::as_bool)
81        .unwrap_or(false);
82    let payload = serde_json::json!({
83        "code": "tab_closed",
84        "target_id": args.tab,
85        "success": success,
86    });
87    output::emit("tab_closed", &payload)
88}