agent_first_http/sdk/
capabilities.rs1use std::collections::BTreeMap;
4
5use serde::{Deserialize, Serialize};
6
7use crate::sdk::client::Client;
8use crate::shared::error::{Error, ErrorCode};
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct CapabilitiesResponse {
12 pub code: String,
13 pub backend: BackendFamily,
14 pub artifacts: BTreeMap<String, ArtifactSupport>,
15 pub wait_modes: Vec<String>,
16 pub takeover: TakeoverSupport,
17 pub profile: ProfileSupport,
18 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
19 pub features: BTreeMap<String, FeatureSupport>,
20 pub limits: BTreeMap<String, serde_json::Value>,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct BackendFamily {
25 pub family: String,
26 pub version: String,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct ArtifactSupport {
31 pub supported: bool,
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub source: Option<String>,
34 #[serde(default, skip_serializing_if = "Vec::is_empty")]
35 pub body_capture: Vec<String>,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct TakeoverSupport {
43 #[serde(default)]
46 pub backend_capable: bool,
47 pub supported: bool,
49 #[serde(default)]
50 pub panel_url: Option<String>,
51 #[serde(default)]
52 pub provider: Option<String>,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct ProfileSupport {
57 pub persistent: bool,
58 pub ephemeral: bool,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct FeatureSupport {
63 pub supported: bool,
64 #[serde(skip_serializing_if = "Option::is_none")]
65 pub detail: Option<String>,
66 #[serde(skip_serializing_if = "Option::is_none")]
67 pub risk: Option<String>,
68}
69
70impl Client {
71 pub async fn capabilities(&self) -> Result<CapabilitiesResponse, Error> {
72 let endpoint = self.effective_endpoint().await?;
73 let base = endpoint.http_base();
74 let url = format!("{base}/capabilities");
75 let mut req = self.http().get(&url);
76 if let Some(token) = self.token() {
77 req = req.bearer_auth(token);
78 }
79 let resp = req
80 .send()
81 .await
82 .map_err(|e| Error::new(ErrorCode::HostUnreachable, format!("GET {url}: {e}")))?;
83 let status = resp.status();
84 let bytes = resp.bytes().await.map_err(|e| {
85 Error::new(
86 ErrorCode::InternalError,
87 format!("capabilities: read response: {e}"),
88 )
89 })?;
90 if !status.is_success() {
91 if let Ok(err) = crate::shared::afdata::decode_error(&bytes) {
92 return Err(err);
93 }
94 return Err(Error::new(
95 ErrorCode::InternalError,
96 format!(
97 "capabilities: status {status}; failed to decode error envelope: {}",
98 String::from_utf8_lossy(&bytes)
99 ),
100 ));
101 }
102 crate::shared::afdata::decode_result(&bytes)
103 }
104
105 pub fn cdp(&self, method: impl Into<String>) -> crate::sdk::cdp::CdpBuilder {
107 crate::sdk::cdp::CdpBuilder::new(self.clone(), method)
108 }
109}