use std::path::{Path, PathBuf};
use std::sync::Arc;
use anyhow::{Context, Result};
use oxi_sdk::fs::{
FileAuthProvider, FileConfigStore, FilePersonaProvider, FileSkillLoader, FileStateStore,
SimpleAccessGate, TomlCapabilityResolver,
};
use oxi_sdk::inmem::{
CountingResourceMonitor, InMemoryCronScheduler, InMemoryMemoryStore, InProcessEventBus,
};
use oxi_sdk::Oxi;
#[derive(Debug, Clone)]
pub struct OxiPaths {
pub home: PathBuf,
pub auth: PathBuf,
pub config: PathBuf,
pub sessions: PathBuf,
pub skills: PathBuf,
}
impl OxiPaths {
pub fn from_home(home: impl Into<PathBuf>) -> Self {
let home = home.into();
Self {
auth: home.join("auth.json"),
config: home.join("settings.toml"),
sessions: home.join("sessions"),
skills: home.join("skills"),
home,
}
}
pub fn default_paths() -> Result<Self> {
oxi_sdk::fs::home_dir()
.map(Self::from_home)
.context("could not resolve oxi home directory")
}
}
pub fn build_oxi(paths: &OxiPaths) -> Result<Oxi> {
ensure_parent(&paths.auth)?;
ensure_parent(&paths.config)?;
ensure_parent(&paths.sessions)?;
let oxi = oxi_sdk::OxiBuilder::new()
.with_builtins()
.with_state(Arc::new(FileStateStore::new(&paths.sessions)))
.with_auth(Arc::new(FileAuthProvider::new(&paths.auth)))
.with_config(Arc::new(FileConfigStore::new(&paths.config)))
.with_skills(Arc::new(FileSkillLoader::single(&paths.skills)))
.with_personas(Arc::new(FilePersonaProvider::new(
paths.home.join("personas"),
)))
.with_access(Arc::new(SimpleAccessGate::from_file(
paths.home.join("access.toml"),
)))
.with_capabilities(Arc::new(TomlCapabilityResolver::from_file(
paths.home.join("capabilities.toml"),
)))
.with_event_bus(InProcessEventBus::new(64))
.with_memory(Arc::new(InMemoryMemoryStore::new()))
.with_cron(Arc::new(InMemoryCronScheduler::new()))
.with_resources(Arc::new(CountingResourceMonitor::new()))
.build();
Ok(oxi)
}
fn ensure_parent(path: &Path) -> Result<()> {
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)
.with_context(|| format!("create_dir_all {}", parent.display()))?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn paths_are_consistent() {
let p = OxiPaths::from_home("/tmp/oxi-test");
assert!(p.auth.starts_with("/tmp/oxi-test"));
assert!(p.config.starts_with("/tmp/oxi-test"));
assert!(p.sessions.starts_with("/tmp/oxi-test"));
assert!(p.skills.starts_with("/tmp/oxi-test"));
}
#[test]
fn build_oxi_succeeds() {
let tmp = tempfile::TempDir::new().unwrap();
let paths = OxiPaths::from_home(tmp.path());
let oxi = build_oxi(&paths).unwrap();
let _ = oxi.ports().state;
}
}