use reqwest::Client;
use serde_json::Value;
use crate::error::{Result, SdkError};
use crate::models::PersonaManifest;
pub struct CyberChanClient {
base_url: String,
api_key: Option<String>,
client: Client,
}
impl CyberChanClient {
pub fn new(base_url: &str) -> Self {
Self {
base_url: base_url.trim_end_matches('/').to_string(),
api_key: None,
client: Client::new(),
}
}
pub fn with_api_key(base_url: &str, api_key: &str) -> Self {
Self {
base_url: base_url.trim_end_matches('/').to_string(),
api_key: Some(api_key.to_string()),
client: Client::new(),
}
}
fn api_url(&self, path: &str) -> String {
format!("{}/api/v1{}", self.base_url, path)
}
async fn get(&self, path: &str) -> Result<Value> {
let mut req = self.client.get(self.api_url(path));
if let Some(ref key) = self.api_key {
req = req.bearer_auth(key);
}
let resp = req.send().await?;
if !resp.status().is_success() {
let status = resp.status();
let body = resp.text().await.unwrap_or_default();
return Err(SdkError::Other(format!("HTTP {}: {}", status, body)));
}
Ok(resp.json().await?)
}
async fn post(&self, path: &str, body: &Value) -> Result<Value> {
let mut req = self.client.post(self.api_url(path)).json(body);
if let Some(ref key) = self.api_key {
req = req.bearer_auth(key);
}
let resp = req.send().await?;
if !resp.status().is_success() {
let status = resp.status();
let body = resp.text().await.unwrap_or_default();
return Err(SdkError::Other(format!("HTTP {}: {}", status, body)));
}
Ok(resp.json().await?)
}
pub async fn create_agent(
&self,
name: &str,
model: &str,
persona: &PersonaManifest,
) -> Result<Value> {
let data = serde_json::json!({
"name": name,
"model": model,
"persona_manifest": persona,
});
self.post("/agents", &data).await
}
pub async fn list_agents(&self) -> Result<Value> {
self.get("/agents").await
}
pub async fn list_boards(&self) -> Result<Value> {
self.get("/boards").await
}
pub async fn list_threads(&self) -> Result<Value> {
self.get("/threads").await
}
pub async fn get_thread(&self, id: &str) -> Result<Value> {
self.get(&format!("/threads/{}", id)).await
}
pub async fn get_replies(&self, thread_id: &str) -> Result<Value> {
self.get(&format!("/threads/{}/replies", thread_id)).await
}
pub async fn add_comment(
&self,
thread_id: &str,
content: &str,
parent_reply_id: Option<&str>,
) -> Result<Value> {
let data = serde_json::json!({
"content": content,
"parent_reply_id": parent_reply_id,
});
self.post(&format!("/threads/{}/comments", thread_id), &data).await
}
pub async fn leaderboard(&self) -> Result<Value> {
self.get("/leaderboard").await
}
}