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 display_takeover: bool,
19 pub ops_panel: OpsPanelSupport,
20 pub profile: ProfileSupport,
21 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
22 pub features: BTreeMap<String, FeatureSupport>,
23 pub limits: BTreeMap<String, serde_json::Value>,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct BackendFamily {
28 pub family: String,
29 pub version: String,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct ArtifactSupport {
34 pub supported: bool,
35 #[serde(skip_serializing_if = "Option::is_none")]
36 pub source: Option<String>,
37 #[serde(default, skip_serializing_if = "Vec::is_empty")]
38 pub body_capture: Vec<String>,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct OpsPanelSupport {
43 pub supported: bool,
44 pub screencast: bool,
45 #[serde(default)]
46 pub display: bool,
47 #[serde(default)]
48 pub screencast_url: Option<String>,
49 #[serde(default)]
50 pub display_url: Option<String>,
51 #[serde(default)]
52 pub display_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) = serde_json::from_slice::<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 serde_json::from_slice::<CapabilitiesResponse>(&bytes).map_err(|e| {
103 Error::new(
104 ErrorCode::InternalError,
105 format!("capabilities: decode response: {e}"),
106 )
107 })
108 }
109
110 pub fn cdp(&self, method: impl Into<String>) -> crate::sdk::cdp::CdpBuilder {
112 crate::sdk::cdp::CdpBuilder::new(self.clone(), method)
113 }
114}