mod store;
mod types;
#[cfg(feature = "cure-network")]
mod openrouter;
pub use types::{CureCache, CuredModel, CureError, CureSource};
pub fn load_cure_cache() -> Option<CureCache> {
store::read_cache()
}
pub fn cure() -> Result<CureSource, CureError> {
if store::read_cache().is_none() {
let cache = CureCache {
updated_at: chrono::Utc::now().timestamp(),
source: CureSource::Hardcoded,
tools: std::collections::HashMap::new(),
};
store::write_cache(&cache)?;
}
Ok(CureSource::Hardcoded)
}
#[cfg(feature = "cure-network")]
pub async fn cure_async() -> Result<CureSource, CureError> {
match openrouter::fetch_openrouter_models().await {
Ok(tool_models) => {
let cache = CureCache {
updated_at: chrono::Utc::now().timestamp(),
source: CureSource::OpenRouter,
tools: tool_models,
};
store::write_cache(&cache)?;
return Ok(CureSource::OpenRouter);
}
Err(_) => {
}
}
if store::read_cache().is_none() {
let cache = CureCache {
updated_at: chrono::Utc::now().timestamp(),
source: CureSource::Hardcoded,
tools: std::collections::HashMap::new(),
};
store::write_cache(&cache)?;
}
Ok(CureSource::Hardcoded)
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use std::sync::Mutex;
static ENV_LOCK: Mutex<()> = Mutex::new(());
fn temp_home(suffix: &str) -> std::path::PathBuf {
let dir = std::env::temp_dir().join(format!("gate4agent_cure_mod_{}", suffix));
fs::create_dir_all(&dir).unwrap();
dir
}
#[test]
fn cure_falls_through_to_hardcoded_when_no_sources() {
let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let home = temp_home("hardcoded");
std::env::set_var("HOME", home.to_str().unwrap());
let _ = fs::remove_file(home.join(".gate4agent").join("models.json"));
let result = cure();
assert!(result.is_ok(), "cure() must not fail: {:?}", result);
assert_eq!(result.unwrap(), CureSource::Hardcoded);
let loaded = load_cure_cache();
assert!(loaded.is_some(), "cache must exist after cure()");
assert_eq!(loaded.unwrap().source, CureSource::Hardcoded);
}
}