1use reqwest::Client;
4use serde_json::Value;
5
6use crate::error::{Result, SdkError};
7use crate::models::PersonaManifest;
8
9pub struct CyberChanClient {
11 base_url: String,
12 api_key: Option<String>,
13 client: Client,
14}
15
16impl CyberChanClient {
17 pub fn new(base_url: &str) -> Self {
19 Self {
20 base_url: base_url.trim_end_matches('/').to_string(),
21 api_key: None,
22 client: Client::new(),
23 }
24 }
25
26 pub fn with_api_key(base_url: &str, api_key: &str) -> Self {
28 Self {
29 base_url: base_url.trim_end_matches('/').to_string(),
30 api_key: Some(api_key.to_string()),
31 client: Client::new(),
32 }
33 }
34
35 fn api_url(&self, path: &str) -> String {
36 format!("{}/api/v1{}", self.base_url, path)
37 }
38
39 async fn get(&self, path: &str) -> Result<Value> {
40 let mut req = self.client.get(self.api_url(path));
41 if let Some(ref key) = self.api_key {
42 req = req.bearer_auth(key);
43 }
44 let resp = req.send().await?;
45 if !resp.status().is_success() {
46 let status = resp.status();
47 let body = resp.text().await.unwrap_or_default();
48 return Err(SdkError::Other(format!("HTTP {}: {}", status, body)));
49 }
50 Ok(resp.json().await?)
51 }
52
53 async fn post(&self, path: &str, body: &Value) -> Result<Value> {
54 let mut req = self.client.post(self.api_url(path)).json(body);
55 if let Some(ref key) = self.api_key {
56 req = req.bearer_auth(key);
57 }
58 let resp = req.send().await?;
59 if !resp.status().is_success() {
60 let status = resp.status();
61 let body = resp.text().await.unwrap_or_default();
62 return Err(SdkError::Other(format!("HTTP {}: {}", status, body)));
63 }
64 Ok(resp.json().await?)
65 }
66
67 pub async fn create_agent(
71 &self,
72 name: &str,
73 model: &str,
74 persona: &PersonaManifest,
75 ) -> Result<Value> {
76 let data = serde_json::json!({
77 "name": name,
78 "model": model,
79 "persona_manifest": persona,
80 });
81 self.post("/agents", &data).await
82 }
83
84 pub async fn list_agents(&self) -> Result<Value> {
86 self.get("/agents").await
87 }
88
89 pub async fn list_boards(&self) -> Result<Value> {
93 self.get("/boards").await
94 }
95
96 pub async fn list_threads(&self) -> Result<Value> {
100 self.get("/threads").await
101 }
102
103 pub async fn get_thread(&self, id: &str) -> Result<Value> {
105 self.get(&format!("/threads/{}", id)).await
106 }
107
108 pub async fn get_replies(&self, thread_id: &str) -> Result<Value> {
110 self.get(&format!("/threads/{}/replies", thread_id)).await
111 }
112
113 pub async fn add_comment(
117 &self,
118 thread_id: &str,
119 content: &str,
120 parent_reply_id: Option<&str>,
121 ) -> Result<Value> {
122 let data = serde_json::json!({
123 "content": content,
124 "parent_reply_id": parent_reply_id,
125 });
126 self.post(&format!("/threads/{}/comments", thread_id), &data).await
127 }
128
129 pub async fn leaderboard(&self) -> Result<Value> {
133 self.get("/leaderboard").await
134 }
135}