use std::collections::HashMap;
use std::sync::RwLock;
use axum::extract::{Path, State};
use axum::http::StatusCode;
use axum::Json;
use serde::Deserialize;
use serde_json::json;
use uuid::Uuid;
use super::error::{ErrorType, UhpFailure};
use super::harnesses::{Harness, McpServer, Skill};
use super::state::UhpState;
#[derive(Debug, Clone, Deserialize)]
pub struct HarnessConfig {
pub name: String,
pub base: String,
#[serde(default, alias = "defaultModel")]
pub default_model: Option<String>,
#[serde(default)]
pub skills: Vec<Skill>,
#[serde(default, alias = "mcpServers")]
pub mcp_servers: Vec<McpServer>,
#[serde(default, alias = "disabledTools")]
pub disabled_tools: Vec<String>,
}
fn validated_skills(skills: &[Skill]) -> Result<(), UhpFailure> {
for skill in skills {
if !skill.has_manifest() {
return Err(UhpFailure::new(
ErrorType::InvalidRequestError,
"invalid_input",
format!(
"skill '{}' carries no {} — it would be stored and then silently ignored at \
run time",
skill.name,
Skill::MANIFEST
),
));
}
}
Ok(())
}
#[derive(Debug, Default)]
pub struct ConfiguredHarnesses(RwLock<HashMap<String, Harness>>);
impl ConfiguredHarnesses {
pub fn all(&self) -> Vec<Harness> {
self.0
.read()
.map(|h| h.values().cloned().collect())
.unwrap_or_default()
}
pub fn get(&self, id: &str) -> Option<Harness> {
self.0.read().ok()?.get(id).cloned()
}
pub fn put(&self, harness: Harness) {
if let Ok(mut all) = self.0.write() {
all.insert(harness.id.clone(), harness);
}
}
pub fn remove(&self, id: &str) -> bool {
self.0.write().is_ok_and(|mut all| all.remove(id).is_some())
}
}
async fn validated_base(state: &UhpState, base: &str) -> Result<(), UhpFailure> {
let supported: Vec<String> = super::harnesses::discover(&state.shared)
.await
.into_iter()
.map(|d| d.harness.base)
.collect();
if supported.iter().any(|b| b == base) {
return Ok(());
}
Err(UhpFailure::new(
ErrorType::InvalidRequestError,
"unsupported_base",
format!("this server has no installed harness for base '{base}'"),
)
.with_detail(json!({ "supported": supported })))
}
pub async fn create(
State(state): State<UhpState>,
Json(config): Json<HarnessConfig>,
) -> Result<Json<Harness>, UhpFailure> {
validated_base(&state, &config.base).await?;
validated_skills(&config.skills)?;
let harness = Harness {
id: format!("chrn_{}", Uuid::new_v4().simple()),
object: "harness",
name: config.name,
base: config.base,
default_model: config.default_model,
skills: config.skills,
mcp_servers: config.mcp_servers,
disabled_tools: config.disabled_tools,
};
state.configured.put(harness.clone());
Ok(Json(harness))
}
pub async fn update(
State(state): State<UhpState>,
Path(id): Path<String>,
Json(config): Json<HarnessConfig>,
) -> Result<Json<Harness>, UhpFailure> {
let existing = state.configured.get(&id).ok_or_else(|| {
UhpFailure::not_found("harness_not_found", "no harness with that id is configured")
})?;
validated_base(&state, &config.base).await?;
validated_skills(&config.skills)?;
let updated = Harness {
id: existing.id,
object: "harness",
name: config.name,
base: existing.base,
default_model: config.default_model.or(existing.default_model),
skills: if config.skills.is_empty() {
existing.skills
} else {
config.skills
},
mcp_servers: if config.mcp_servers.is_empty() {
existing.mcp_servers
} else {
config.mcp_servers
},
disabled_tools: if config.disabled_tools.is_empty() {
existing.disabled_tools
} else {
config.disabled_tools
},
};
state.configured.put(updated.clone());
Ok(Json(updated))
}
pub async fn delete(
State(state): State<UhpState>,
Path(id): Path<String>,
) -> Result<StatusCode, UhpFailure> {
if state.configured.remove(&id) {
Ok(StatusCode::NO_CONTENT)
} else {
Err(UhpFailure::not_found(
"harness_not_found",
"no harness with that id is configured",
))
}
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct SkillFiles {
pub files: Vec<super::harnesses::SkillFile>,
}
pub async fn skill_files(
State(state): State<UhpState>,
Path((harness_id, skill_name)): Path<(String, String)>,
) -> Result<Json<SkillFiles>, UhpFailure> {
let harness = state.configured.get(&harness_id).ok_or_else(|| {
UhpFailure::not_found("harness_not_found", "no harness with that id is configured")
})?;
let skill = harness
.skills
.into_iter()
.find(|s| s.name == skill_name)
.ok_or_else(|| {
UhpFailure::not_found(
"skill_not_found",
"this harness carries no skill by that name",
)
})?;
Ok(Json(SkillFiles { files: skill.files }))
}
#[cfg(test)]
mod tests {
use super::*;
fn harness(id: &str, base: &str) -> Harness {
Harness {
id: id.to_owned(),
object: "harness",
name: "n".to_owned(),
base: base.to_owned(),
default_model: None,
skills: Vec::new(),
mcp_servers: Vec::new(),
disabled_tools: Vec::new(),
}
}
#[test]
fn a_binary_member_round_trips_by_its_base64() {
use super::super::harnesses::SkillFile;
let file = SkillFile {
path: "assets/blob.bin".to_owned(),
content: None,
content_b64: Some("AAECAwQF".to_owned()),
rest: serde_json::Map::new(),
};
let json = serde_json::to_string(&file).expect("serialises"); assert!(
json.contains("AAECAwQF"),
"the binary member's bytes must survive the round trip: {json}"
);
assert!(
!json.contains("\"content\":"),
"a member with no text content must not gain an empty one"
);
}
#[test]
fn a_skill_bundle_without_a_manifest_is_refused() {
use super::super::harnesses::SkillFile;
let ok = Skill {
name: "good".to_owned(),
enabled: true,
files: vec![SkillFile {
path: "SKILL.md".to_owned(),
content: Some("# skill".to_owned()),
content_b64: None,
rest: serde_json::Map::new(),
}],
};
assert!(validated_skills(&[ok]).is_ok());
let bad = Skill {
name: "no-manifest".to_owned(),
enabled: true,
files: vec![SkillFile {
path: "notes.md".to_owned(),
content: Some("no manifest here".to_owned()),
content_b64: None,
rest: serde_json::Map::new(),
}],
};
assert!(
validated_skills(&[bad]).is_err(),
"a bundle with no SKILL.md must be refused at config time, not ignored at run time"
);
}
#[test]
fn a_configured_harness_round_trips_and_then_is_gone() {
let store = ConfiguredHarnesses::default();
store.put(harness("chrn_a", "claude-code"));
assert_eq!(
store.get("chrn_a").map(|h| h.base),
Some("claude-code".to_owned())
);
assert_eq!(store.all().len(), 1);
assert!(
store.remove("chrn_a"),
"removing a present harness reports true"
);
assert!(
store.get("chrn_a").is_none(),
"a deleted harness must not still resolve"
);
assert!(
!store.remove("chrn_a"),
"removing it twice reports false, so the caller can answer 404"
);
}
#[test]
fn a_created_id_satisfies_the_schema_pattern() {
let id = format!("chrn_{}", Uuid::new_v4().simple());
assert!(id.starts_with("chrn_"));
assert!(id.len() > "chrn_".len());
}
}