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::connect::Connection;
6use crate::cli::output;
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 connection: Connection,
23}
24
25#[derive(Debug)]
26pub struct CloseArgs {
27    pub tab: String,
28    pub connection: Connection,
29}
30
31pub async fn run(args: Args) -> Result<(), Error> {
32    match args.sub {
33        TabsSub::List(a) => list(a).await,
34        TabsSub::Close(a) => close(a).await,
35    }
36}
37
38async fn list(args: EndpointArgs) -> Result<(), Error> {
39    let client = args.connection.client().await?;
40    let response = client.cdp("Target.getTargets").send().await?;
41    // `Client.cdp(...).send()` unwraps the JSON-RPC `result` envelope, so
42    // `response` is the inner method result and we read `targetInfos`
43    // off it directly.
44    let targets = response
45        .get("targetInfos")
46        .cloned()
47        .unwrap_or(Value::Array(Vec::new()));
48    let payload = serde_json::json!({
49        "code": "tabs",
50        "targets": targets,
51    });
52    output::emit("tabs", &payload)
53}
54
55async fn close(args: CloseArgs) -> Result<(), Error> {
56    if args.tab.trim().is_empty() {
57        return Err(Error::new(
58            ErrorCode::InvalidArgument,
59            "tabs close: target id must not be empty",
60        ));
61    }
62    let client = args.connection.client().await?;
63    let response = client
64        .cdp("Target.closeTarget")
65        .params(serde_json::json!({ "targetId": args.tab }))
66        .send()
67        .await?;
68    let success = response
69        .get("success")
70        .and_then(Value::as_bool)
71        .unwrap_or(false);
72    let payload = serde_json::json!({
73        "code": "tab_closed",
74        "target_id": args.tab,
75        "success": success,
76    });
77    output::emit("tab_closed", &payload)
78}