1use kernex_core::config::MemoryConfig;
23use kernex_core::error::KernexError;
24use kernex_memory::Store;
25use kernex_skills::{Project, Skill};
26
27pub use kernex_core as core;
29pub use kernex_memory as memory;
30pub use kernex_pipelines as pipelines;
31pub use kernex_providers as providers;
32pub use kernex_sandbox as sandbox;
33pub use kernex_skills as skills;
34
35pub struct Runtime {
37 pub store: Store,
39 pub skills: Vec<Skill>,
41 pub projects: Vec<Project>,
43 pub data_dir: String,
45}
46
47pub struct RuntimeBuilder {
49 data_dir: String,
50 db_path: Option<String>,
51}
52
53impl RuntimeBuilder {
54 pub fn new() -> Self {
56 Self {
57 data_dir: "~/.kernex".to_string(),
58 db_path: None,
59 }
60 }
61
62 pub fn data_dir(mut self, path: &str) -> Self {
64 self.data_dir = path.to_string();
65 self
66 }
67
68 pub fn db_path(mut self, path: &str) -> Self {
70 self.db_path = Some(path.to_string());
71 self
72 }
73
74 pub async fn build(self) -> Result<Runtime, KernexError> {
76 let expanded_dir = kernex_core::shellexpand(&self.data_dir);
77
78 tokio::fs::create_dir_all(&expanded_dir)
80 .await
81 .map_err(|e| KernexError::Config(format!("failed to create data dir: {e}")))?;
82
83 let db_path = self
85 .db_path
86 .unwrap_or_else(|| format!("{expanded_dir}/memory.db"));
87 let mem_config = MemoryConfig {
88 db_path: db_path.clone(),
89 ..Default::default()
90 };
91 let store = Store::new(&mem_config).await?;
92
93 let skills = kernex_skills::load_skills(&self.data_dir);
95 let projects = kernex_skills::load_projects(&self.data_dir);
96
97 tracing::info!(
98 "runtime initialized: {} skills, {} projects",
99 skills.len(),
100 projects.len()
101 );
102
103 Ok(Runtime {
104 store,
105 skills,
106 projects,
107 data_dir: expanded_dir,
108 })
109 }
110}
111
112impl Default for RuntimeBuilder {
113 fn default() -> Self {
114 Self::new()
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[tokio::test]
123 async fn test_runtime_builder_creates_runtime() {
124 let tmp = std::env::temp_dir().join("__kernex_test_runtime__");
125 let _ = std::fs::remove_dir_all(&tmp);
126
127 let runtime = RuntimeBuilder::new()
128 .data_dir(tmp.to_str().unwrap())
129 .build()
130 .await
131 .unwrap();
132
133 assert!(runtime.skills.is_empty());
134 assert!(runtime.projects.is_empty());
135 assert!(std::path::Path::new(&runtime.data_dir).exists());
136
137 let _ = std::fs::remove_dir_all(&tmp);
138 }
139
140 #[tokio::test]
141 async fn test_runtime_builder_custom_db_path() {
142 let tmp = std::env::temp_dir().join("__kernex_test_runtime_db__");
143 let _ = std::fs::remove_dir_all(&tmp);
144 std::fs::create_dir_all(&tmp).unwrap();
145
146 let db = tmp.join("custom.db");
147 let runtime = RuntimeBuilder::new()
148 .data_dir(tmp.to_str().unwrap())
149 .db_path(db.to_str().unwrap())
150 .build()
151 .await
152 .unwrap();
153
154 assert!(db.exists());
155 drop(runtime);
156 let _ = std::fs::remove_dir_all(&tmp);
157 }
158}