Files
codeg/src-tauri/src/acp/remote_registry.rs
2026-03-07 16:40:59 +08:00

126 lines
3.4 KiB
Rust

use std::collections::BTreeMap;
use serde::Deserialize;
use crate::acp::registry;
use crate::app_error::AppCommandError;
use crate::models::agent::AgentType;
pub const REGISTRY_URL: &str =
"https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json";
#[derive(Debug, Clone)]
pub struct RegistryAgent {
pub agent_type: AgentType,
pub registry_id: String,
pub name: String,
pub description: String,
pub version: Option<String>,
}
#[derive(Debug, Clone)]
pub struct RegistryBinaryRelease {
pub version: String,
pub archive_url: String,
}
#[derive(Debug, Deserialize)]
struct RegistryPayload {
agents: Vec<RegistryAgentItem>,
}
#[derive(Debug, Deserialize)]
struct RegistryAgentItem {
id: String,
name: String,
description: String,
#[serde(default)]
version: Option<String>,
#[serde(default)]
distribution: RegistryDistribution,
}
#[derive(Debug, Default, Deserialize)]
struct RegistryDistribution {
#[serde(default)]
binary: BTreeMap<String, RegistryBinaryPlatformItem>,
}
#[derive(Debug, Default, Deserialize)]
struct RegistryBinaryPlatformItem {
#[serde(default, alias = "url")]
archive: String,
}
async fn fetch_registry_payload() -> Result<RegistryPayload, AppCommandError> {
let response = reqwest::Client::new()
.get(REGISTRY_URL)
.send()
.await
.map_err(|e| AppCommandError::network(format!("failed to fetch ACP registry: {e}")))?;
if !response.status().is_success() {
return Err(AppCommandError::network(format!(
"failed to fetch ACP registry: HTTP {}",
response.status()
)));
}
let text = response
.text()
.await
.map_err(|e| AppCommandError::network(format!("failed to read ACP registry response: {e}")))?;
serde_json::from_str::<RegistryPayload>(&text).map_err(|e| {
AppCommandError::configuration_invalid(format!("failed to parse ACP registry JSON: {e}"))
})
}
pub async fn fetch_supported_agents() -> Result<Vec<RegistryAgent>, AppCommandError> {
let payload = fetch_registry_payload().await?;
let mut supported = Vec::new();
for item in payload.agents {
if let Some(agent_type) = registry::from_registry_id(&item.id) {
supported.push(RegistryAgent {
agent_type,
registry_id: item.id,
name: item.name,
description: item.description,
version: item.version,
});
}
}
Ok(supported)
}
pub async fn fetch_binary_release(
agent_type: AgentType,
platform: &str,
) -> Result<Option<RegistryBinaryRelease>, AppCommandError> {
let payload = fetch_registry_payload().await?;
let item = payload.agents.into_iter().find(|item| {
registry::from_registry_id(&item.id)
.map(|candidate| candidate == agent_type)
.unwrap_or(false)
});
let Some(item) = item else {
return Ok(None);
};
if item.version.as_deref().unwrap_or_default().is_empty() {
return Ok(None);
}
let platform_item = item.distribution.binary.get(platform);
let Some(platform_item) = platform_item else {
return Ok(None);
};
if platform_item.archive.is_empty() {
return Ok(None);
}
Ok(Some(RegistryBinaryRelease {
version: item.version.unwrap_or_default(),
archive_url: platform_item.archive.clone(),
}))
}