acp_http_adapter/
registry.rs1use std::collections::HashMap;
2use std::path::PathBuf;
3
4use serde::Deserialize;
5use serde_json::Value;
6use thiserror::Error;
7
8#[derive(Debug, Clone)]
9pub struct LaunchSpec {
10 pub program: PathBuf,
11 pub args: Vec<String>,
12 pub env: HashMap<String, String>,
13}
14
15#[derive(Debug, Error)]
16pub enum RegistryError {
17 #[error("invalid registry json: {0}")]
18 InvalidJson(#[from] serde_json::Error),
19 #[error("unable to resolve registry entry from blob")]
20 UnsupportedBlob,
21 #[error("registry blob has agents[] but no --registry-agent-id was provided")]
22 MissingAgentId,
23 #[error("agent '{0}' was not found in registry blob")]
24 AgentNotFound(String),
25 #[error("registry entry has no supported launch target")]
26 MissingLaunchTarget,
27 #[error("platform '{0}' is not present in distribution.binary")]
28 UnsupportedPlatform(String),
29}
30
31impl LaunchSpec {
32 pub fn from_registry_blob(blob: &str, agent_id: Option<&str>) -> Result<Self, RegistryError> {
33 let value: Value = serde_json::from_str(blob)?;
34 Self::from_registry_value(value, agent_id)
35 }
36
37 fn from_registry_value(value: Value, agent_id: Option<&str>) -> Result<Self, RegistryError> {
38 if value.get("agents").is_some() {
39 let doc: RegistryDocument = serde_json::from_value(value)?;
40 let wanted = agent_id.ok_or(RegistryError::MissingAgentId)?;
41 let agent = doc
42 .agents
43 .into_iter()
44 .find(|a| a.id == wanted)
45 .ok_or_else(|| RegistryError::AgentNotFound(wanted.to_string()))?;
46 return Self::from_distribution(agent.distribution);
47 }
48
49 if value.get("distribution").is_some() {
50 let entry: RegistryAgent = serde_json::from_value(value)?;
51 return Self::from_distribution(entry.distribution);
52 }
53
54 if value.get("npx").is_some() || value.get("binary").is_some() {
55 let distribution: RegistryDistribution = serde_json::from_value(value)?;
56 return Self::from_distribution(distribution);
57 }
58
59 Err(RegistryError::UnsupportedBlob)
60 }
61
62 fn from_distribution(distribution: RegistryDistribution) -> Result<Self, RegistryError> {
63 if let Some(npx) = distribution.npx {
64 let mut args = vec!["-y".to_string(), npx.package];
65 args.extend(npx.args);
66 return Ok(Self {
67 program: PathBuf::from("npx"),
68 args,
69 env: npx.env,
70 });
71 }
72
73 if let Some(binary) = distribution.binary {
74 let platform = platform_key().ok_or(RegistryError::UnsupportedPlatform(format!(
75 "{}/{}",
76 std::env::consts::OS,
77 std::env::consts::ARCH
78 )))?;
79 let target = binary
80 .get(platform)
81 .ok_or_else(|| RegistryError::UnsupportedPlatform(platform.to_string()))?;
82 return Ok(Self {
83 program: PathBuf::from(&target.cmd),
84 args: target.args.clone(),
85 env: target.env.clone(),
86 });
87 }
88
89 Err(RegistryError::MissingLaunchTarget)
90 }
91}
92
93fn platform_key() -> Option<&'static str> {
94 match (std::env::consts::OS, std::env::consts::ARCH) {
95 ("linux", "x86_64") => Some("linux-x86_64"),
96 ("linux", "aarch64") => Some("linux-aarch64"),
97 ("macos", "x86_64") => Some("darwin-x86_64"),
98 ("macos", "aarch64") => Some("darwin-aarch64"),
99 ("windows", "x86_64") => Some("windows-x86_64"),
100 ("windows", "aarch64") => Some("windows-aarch64"),
101 _ => None,
102 }
103}
104
105#[derive(Debug, Deserialize)]
106struct RegistryDocument {
107 agents: Vec<RegistryAgent>,
108}
109
110#[derive(Debug, Deserialize)]
111struct RegistryAgent {
112 #[allow(dead_code)]
113 id: String,
114 distribution: RegistryDistribution,
115}
116
117#[derive(Debug, Deserialize)]
118struct RegistryDistribution {
119 #[serde(default)]
120 npx: Option<RegistryNpx>,
121 #[serde(default)]
122 binary: Option<HashMap<String, RegistryBinaryTarget>>,
123}
124
125#[derive(Debug, Deserialize)]
126struct RegistryNpx {
127 package: String,
128 #[serde(default)]
129 args: Vec<String>,
130 #[serde(default)]
131 env: HashMap<String, String>,
132}
133
134#[derive(Debug, Deserialize)]
135struct RegistryBinaryTarget {
136 #[allow(dead_code)]
137 archive: Option<String>,
138 cmd: String,
139 #[serde(default)]
140 args: Vec<String>,
141 #[serde(default)]
142 env: HashMap<String, String>,
143}