mod bundled;
mod migrations;
mod paths;
mod settings;
mod trust;
mod version;
use elph_agent::{DatabaseSpec, InitProgress, ensure_databases, ensure_dirs};
pub use paths::Paths;
pub type InitError = elph_agent::InitError;
pub type Result<T> = std::result::Result<T, InitError>;
const INIT_STEPS: u64 = 4;
pub async fn ensure(app_version: &str) -> Result<Paths> {
let progress = InitProgress::new(INIT_STEPS).with_quiet_env("ECLAW_QUIET");
progress.advance("Resolving home directories");
let paths = Paths::resolve()?;
run_init_steps(&paths, app_version, &progress).await?;
progress.finish();
Ok(paths)
}
#[allow(dead_code)]
pub async fn ensure_with_paths(paths: &Paths, app_version: &str) -> Result<()> {
let progress = InitProgress::new(INIT_STEPS).with_quiet_env("ECLAW_QUIET");
run_init_steps(paths, app_version, &progress).await?;
progress.finish();
Ok(())
}
pub fn ensure_blocking(app_version: &str) -> Result<Paths> {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.map_err(|err| InitError::Io(std::io::Error::other(err)))?
.block_on(ensure(app_version))
}
async fn run_init_steps(paths: &Paths, app_version: &str, progress: &InitProgress) -> Result<()> {
progress.advance("Creating directories");
let mut dirs = vec![paths.config_dir.clone(), paths.data_dir.clone()];
dirs.extend(paths.required_dirs());
ensure_dirs(&dirs)?;
progress.advance("Writing configuration");
ensure_files(paths, app_version)?;
progress.advance("Initializing databases");
ensure_databases(&[
DatabaseSpec {
path: &paths.metadata_db_path(),
migrations: migrations::metadata_migrations(),
},
DatabaseSpec {
path: &paths.memory_db_path(),
migrations: migrations::memory_migrations(),
},
])
.await?;
Ok(())
}
fn ensure_files(paths: &Paths, app_version: &str) -> Result<()> {
settings::Settings::ensure(paths)?;
trust::TrustStore::ensure(paths)?;
version::VersionFile::ensure(paths, app_version)?;
bundled::BundledManifest::ensure(paths, app_version)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn ensure_creates_eclaw_layout() {
let tmp = tempfile::tempdir().expect("tempdir");
let paths = Paths::from_dirs(tmp.path().join("config"), tmp.path().join("data"));
ensure_with_paths(&paths, "0.0.10-test").await.expect("ensure layout");
assert!(paths.settings_path().exists());
assert!(paths.trust_path().exists());
assert!(paths.version_path().exists());
assert!(paths.bundled_manifest_path().exists());
assert!(paths.metadata_db_path().exists());
assert!(paths.memory_db_path().exists());
assert!(paths.bundled_dir().join("agents").is_dir());
assert!(paths.bundled_dir().join("personas").is_dir());
assert!(paths.bundled_dir().join("skills").is_dir());
assert!(paths.bundled_dir().join("user-guide").is_dir());
assert!(paths.prompts_dir().is_dir());
assert!(paths.providers_dir().is_dir());
assert!(paths.sessions_dir().is_dir());
assert!(paths.skills_dir().is_dir());
assert!(paths.worktrees_dir().is_dir());
assert!(paths.attachments_dir().is_dir());
assert!(paths.downloads_dir().is_dir());
assert!(paths.logs_dir().is_dir());
assert!(paths.vendor_dir().is_dir());
}
}