Skip to main content

agent_first_http/sdk/cdp/
mod.rs

1//! Raw CDP client: a WebSocket transport with flattened-session
2//! multiplexing for multi-attach.
3
4pub mod session;
5pub mod ws_client;
6
7use serde_json::Value;
8
9use crate::sdk::client::Client;
10use crate::shared::error::Error;
11use crate::shared::ids::TabId;
12
13/// Builder for `Client::cdp(method)`.
14pub struct CdpBuilder {
15    pub(crate) client: Client,
16    pub(crate) method: String,
17    pub(crate) tab: Option<TabId>,
18    pub(crate) params: Value,
19    pub(crate) wait: Option<(String, std::time::Duration)>,
20}
21
22impl CdpBuilder {
23    pub fn new(client: Client, method: impl Into<String>) -> Self {
24        Self {
25            client,
26            method: method.into(),
27            tab: None,
28            params: Value::Object(Default::default()),
29            wait: None,
30        }
31    }
32
33    #[must_use]
34    pub fn tab(mut self, tab: TabId) -> Self {
35        self.tab = Some(tab);
36        self
37    }
38
39    #[must_use]
40    pub fn params(mut self, p: Value) -> Self {
41        self.params = p;
42        self
43    }
44
45    #[must_use]
46    pub fn wait_for(mut self, event: impl Into<String>, timeout: std::time::Duration) -> Self {
47        self.wait = Some((event.into(), timeout));
48        self
49    }
50
51    /// Execute the CDP method on the Client's cached `/cdp` connection,
52    /// optionally waiting for a follow-up event.
53    pub async fn send(self) -> Result<Value, Error> {
54        let conn = self.client.cdp_connection().await?;
55
56        // If --tab was provided, attach to it via Target.attachToTarget so
57        // the call lands on the right session. Without --tab we issue
58        // browser-scoped methods (Browser.getVersion, Target.*, etc.).
59        let session_id = if let Some(tab) = self.tab.as_ref() {
60            Some(session::attach_to_target(&conn, tab.as_str()).await?)
61        } else {
62            None
63        };
64
65        let outcome = async {
66            let result = conn
67                .send(&self.method, &self.params, session_id.as_deref())
68                .await?;
69
70            if let Some((event, timeout)) = self.wait {
71                let event_name = event.clone();
72                let _ev = conn
73                    .wait_event(timeout, move |ev| ev.method == event_name)
74                    .await?;
75            }
76            Ok(result)
77        }
78        .await;
79
80        if let Some(sid) = session_id.as_deref() {
81            let _ = session::detach_from_target(&conn, sid).await;
82        }
83        outcome
84    }
85}