codex_memory/application/
command_handlers.rs1use crate::application::DependencyContainer;
2use anyhow::Result;
3use std::sync::Arc;
4use tracing::{error, info, warn};
5
6pub struct SetupCommandHandler {
8 container: Arc<DependencyContainer>,
9}
10
11impl SetupCommandHandler {
12 pub fn new(container: Arc<DependencyContainer>) -> Self {
13 Self { container }
14 }
15
16 pub async fn run_setup(
17 &self,
18 force: bool,
19 skip_database: bool,
20 skip_models: bool,
21 ) -> Result<()> {
22 info!("๐ Starting Agentic Memory System setup...");
23
24 if !force {
25 match self.container.health_check().await {
26 Ok(true) => {
27 info!(
28 "โ
System appears to be already set up. Use --force to run setup anyway."
29 );
30 return Ok(());
31 }
32 _ => {
33 info!("๐ง System needs setup, proceeding...");
34 }
35 }
36 }
37
38 if !skip_models {
39 self.container.setup_manager.run_setup().await?;
40 }
41
42 if !skip_database {
43 self.container.database_setup.setup().await?;
44 }
45
46 info!("๐ Setup completed successfully!");
47 info!("๐ก You can now start the server with: codex-memory start");
48
49 Ok(())
50 }
51}
52
53pub struct HealthCommandHandler {
54 container: Arc<DependencyContainer>,
55}
56
57impl HealthCommandHandler {
58 pub fn new(container: Arc<DependencyContainer>) -> Self {
59 Self { container }
60 }
61
62 pub async fn run_health_check(&self, detailed: bool) -> Result<()> {
63 info!("๐ฅ Running system health check...");
64
65 if detailed {
66 let health = self.container.health_checker.check_system_health().await?;
67
68 info!("๐ System Health: {:?}", health.status);
69 info!("โฑ๏ธ Uptime: {} seconds", health.uptime_seconds);
70 info!(
71 "๐พ Memory Usage: {} MB",
72 health.memory_usage_bytes / (1024 * 1024)
73 );
74 info!("๐ฅ CPU Usage: {:.1}%", health.cpu_usage_percent);
75
76 for (component, component_health) in &health.components {
77 match component_health.status {
78 crate::monitoring::HealthStatus::Healthy => {
79 info!("โ
{}: Healthy", component);
80 }
81 crate::monitoring::HealthStatus::Degraded => {
82 warn!(
83 "โ ๏ธ {}: Degraded - {:?}",
84 component, component_health.message
85 );
86 }
87 crate::monitoring::HealthStatus::Unhealthy => {
88 error!(
89 "โ {}: Unhealthy - {:?}",
90 component, component_health.message
91 );
92 }
93 }
94 }
95 } else {
96 match self.container.health_check().await {
97 Ok(true) => info!("โ
System is healthy"),
98 _ => error!("โ System health check failed"),
99 }
100 }
101
102 Ok(())
103 }
104}
105
106pub struct ModelCommandHandler {
107 container: Arc<DependencyContainer>,
108}
109
110impl ModelCommandHandler {
111 pub fn new(container: Arc<DependencyContainer>) -> Self {
112 Self { container }
113 }
114
115 pub async fn list_models(&self) -> Result<()> {
116 self.container.setup_manager.list_available_models().await
117 }
118}
119
120pub struct DatabaseCommandHandler {
121 container: Arc<DependencyContainer>,
122}
123
124impl DatabaseCommandHandler {
125 pub fn new(container: Arc<DependencyContainer>) -> Self {
126 Self { container }
127 }
128
129 pub async fn setup(&self) -> Result<()> {
130 self.container.database_setup.setup().await
131 }
132
133 pub async fn health(&self) -> Result<()> {
134 let health = self.container.database_setup.health_check().await?;
135 info!("๐ Database Health: {}", health.status_summary());
136 info!(
137 " - Connectivity: {}",
138 if health.connectivity { "โ
" } else { "โ" }
139 );
140 info!(
141 " - pgvector: {}",
142 if health.pgvector_installed {
143 "โ
"
144 } else {
145 "โ"
146 }
147 );
148 info!(
149 " - Schema: {}",
150 if health.schema_ready { "โ
" } else { "โ" }
151 );
152 info!(" - Memory count: {}", health.memory_count);
153 Ok(())
154 }
155
156 pub async fn migrate(&self) -> Result<()> {
157 error!("โ Migration support not available in this build");
158 info!("๐ก Use direct SQL or database tools to run migrations");
159 Err(anyhow::anyhow!("Migration support not compiled in"))
160 }
161}
162
163pub struct McpCommandHandler {
164 container: Arc<DependencyContainer>,
165}
166
167impl McpCommandHandler {
168 pub fn new(container: Arc<DependencyContainer>) -> Self {
169 Self { container }
170 }
171
172 pub async fn validate(&self) -> Result<()> {
173 info!("๐ Validating MCP configuration...");
174 match self.container.config.validate_mcp_environment() {
175 Ok(_) => {
176 info!("โ
MCP configuration is valid");
177 info!(
178 " - Database: {}",
179 self.container.config.safe_database_url()
180 );
181 info!(
182 " - Embedding: {} ({})",
183 self.container.config.embedding.provider, self.container.config.embedding.model
184 );
185 info!(" - HTTP Port: {}", self.container.config.http_port);
186 if let Some(mcp_port) = self.container.config.mcp_port {
187 info!(" - MCP Port: {}", mcp_port);
188 }
189 Ok(())
190 }
191 Err(e) => {
192 error!("โ MCP configuration validation failed: {}", e);
193 std::process::exit(1);
194 }
195 }
196 }
197
198 pub async fn diagnose(&self) -> Result<()> {
199 info!("๐ Generating MCP diagnostic report...");
200 let report = self.container.config.create_diagnostic_report();
201 println!("{report}");
202 Ok(())
203 }
204
205 pub async fn test(&self) -> Result<()> {
206 info!("๐งช Testing MCP server connectivity...");
207 self.container.config.validate_mcp_environment()?;
208
209 info!("Testing database connectivity...");
211 match self.container.database_setup.health_check().await {
212 Ok(health) => {
213 info!(
214 "โ
Database: {}",
215 if health.connectivity {
216 "Connected"
217 } else {
218 "Failed"
219 }
220 );
221 }
222 Err(e) => {
223 error!("โ Database: Connection failed - {}", e);
224 }
225 }
226
227 info!("Testing embedding service...");
229 match self.container.setup_manager.quick_health_check().await {
230 Ok(_) => info!("โ
Embedding service: Available"),
231 Err(e) => error!("โ Embedding service: {}", e),
232 }
233
234 info!("๐ MCP connectivity test completed");
235 Ok(())
236 }
237
238 pub async fn template(&self, template_type: String, output: Option<String>) -> Result<()> {
239 info!(
240 "๐ Generating MCP configuration template: {}",
241 template_type
242 );
243
244 let template_content = match template_type.as_str() {
245 "basic" => self.generate_basic_template(),
246 "production" => self.generate_production_template(),
247 "development" => self.generate_development_template(),
248 _ => {
249 error!(
250 "โ Unknown template type: {}. Available: basic, production, development",
251 template_type
252 );
253 return Err(anyhow::anyhow!("Invalid template type"));
254 }
255 };
256
257 match output {
258 Some(path) => {
259 std::fs::write(&path, template_content)?;
260 info!("โ
Template written to: {}", path);
261 }
262 None => {
263 println!("{template_content}");
264 }
265 }
266 Ok(())
267 }
268
269 fn generate_basic_template(&self) -> String {
270 r#"{
271 "mcpServers": {
272 "agentic-memory": {
273 "command": "/path/to/codex-memory",
274 "args": ["mcp-stdio"],
275 "env": {
276 "DATABASE_URL": "postgresql://username:password@localhost:5432/memory_db",
277 "EMBEDDING_PROVIDER": "ollama",
278 "EMBEDDING_BASE_URL": "http://localhost:11434",
279 "EMBEDDING_MODEL": "nomic-embed-text",
280 "LOG_LEVEL": "info"
281 }
282 }
283 }
284}"#
285 .to_string()
286 }
287
288 fn generate_production_template(&self) -> String {
289 r#"{
290 "mcpServers": {
291 "agentic-memory": {
292 "command": "/usr/local/bin/codex-memory",
293 "args": ["mcp-stdio"],
294 "env": {
295 "DATABASE_URL": "${DATABASE_URL}",
296 "EMBEDDING_PROVIDER": "${EMBEDDING_PROVIDER:-ollama}",
297 "EMBEDDING_BASE_URL": "${EMBEDDING_BASE_URL:-http://localhost:11434}",
298 "EMBEDDING_MODEL": "${EMBEDDING_MODEL:-nomic-embed-text}",
299 "OPENAI_API_KEY": "${OPENAI_API_KEY}",
300 "LOG_LEVEL": "warn",
301 "MAX_DB_CONNECTIONS": "20",
302 "ENABLE_METRICS": "true"
303 }
304 }
305 }
306}"#
307 .to_string()
308 }
309
310 fn generate_development_template(&self) -> String {
311 r#"{
312 "mcpServers": {
313 "agentic-memory-dev": {
314 "command": "./target/debug/codex-memory",
315 "args": ["mcp-stdio"],
316 "env": {
317 "DATABASE_URL": "postgresql://dev_user:dev_password@localhost:5432/memory_dev_db",
318 "EMBEDDING_PROVIDER": "mock",
319 "EMBEDDING_MODEL": "mock-model",
320 "LOG_LEVEL": "debug",
321 "ENABLE_METRICS": "false"
322 }
323 }
324 }
325}"#
326 .to_string()
327 }
328}
329
330pub struct ManagerCommandHandler {
331 container: Arc<DependencyContainer>,
332}
333
334impl ManagerCommandHandler {
335 pub fn new(container: Arc<DependencyContainer>) -> Self {
336 Self { container }
337 }
338
339 pub async fn start(
340 &self,
341 daemon: bool,
342 pid_file: Option<String>,
343 log_file: Option<String>,
344 ) -> Result<()> {
345 let manager = if pid_file.is_some() || log_file.is_some() {
346 crate::manager::ServerManager::with_paths(pid_file, log_file)
347 } else {
348 crate::manager::ServerManager::new()
349 };
350 manager.start_daemon(daemon).await
351 }
352
353 pub async fn stop(&self, pid_file: Option<String>) -> Result<()> {
354 let manager = if let Some(pid_file) = pid_file {
355 crate::manager::ServerManager::with_paths(Some(pid_file), None)
356 } else {
357 crate::manager::ServerManager::new()
358 };
359 manager.stop().await
360 }
361
362 pub async fn restart(&self, pid_file: Option<String>) -> Result<()> {
363 let manager = if let Some(pid_file) = pid_file {
364 crate::manager::ServerManager::with_paths(Some(pid_file), None)
365 } else {
366 crate::manager::ServerManager::new()
367 };
368 manager.restart().await
369 }
370
371 pub async fn status(&self, detailed: bool) -> Result<()> {
372 self.container.server_manager.status(detailed).await
373 }
374
375 pub async fn logs(&self, lines: usize, follow: bool) -> Result<()> {
376 self.container.server_manager.show_logs(lines, follow).await
377 }
378
379 pub async fn install(&self, service_type: Option<String>) -> Result<()> {
380 self.container
381 .server_manager
382 .install_service(service_type)
383 .await
384 }
385
386 pub async fn uninstall(&self) -> Result<()> {
387 self.container.server_manager.uninstall_service().await
388 }
389}
390
391pub struct ServerCommandHandler {
392 container: Arc<DependencyContainer>,
393}
394
395impl ServerCommandHandler {
396 pub fn new(container: Arc<DependencyContainer>) -> Self {
397 Self { container }
398 }
399
400 pub async fn start_http(&self, skip_setup: bool) -> Result<()> {
401 info!("๐ Starting HTTP server...");
402
403 if !skip_setup {
404 self.validate_system().await?;
405 }
406
407 info!("HTTP server would start here with container dependencies");
410
411 Ok(())
412 }
413
414 pub async fn start_mcp_stdio(&self, skip_setup: bool) -> Result<()> {
415 info!("๐ Starting MCP stdio server...");
416
417 if !skip_setup {
418 self.container.config.validate()?;
419 }
420
421 let mut mcp_server = self.container.create_mcp_server().await?;
422 mcp_server.start().await?;
423
424 Ok(())
425 }
426
427 async fn validate_system(&self) -> Result<()> {
428 info!("๐ Running pre-flight checks...");
429
430 match self.container.setup_manager.quick_health_check().await {
431 Ok(_) => info!("โ
System health check passed"),
432 Err(e) => {
433 error!("โ System health check failed: {}", e);
434 info!("๐ก Try running: codex-memory setup");
435 return Err(e);
436 }
437 }
438
439 match self.container.database_setup.health_check().await {
440 Ok(health) => {
441 if health.is_healthy() {
442 info!("โ
Database health check passed");
443 } else {
444 error!(
445 "โ Database health check failed: {}",
446 health.status_summary()
447 );
448 info!("๐ก Try running: codex-memory database setup");
449 return Err(anyhow::anyhow!("Database not ready"));
450 }
451 }
452 Err(e) => {
453 error!("โ Database connectivity failed: {}", e);
454 return Err(e);
455 }
456 }
457
458 Ok(())
459 }
460}
461
462pub struct BackupCommandHandler {
463 container: Arc<DependencyContainer>,
464}
465
466impl BackupCommandHandler {
467 pub fn new(container: Arc<DependencyContainer>) -> Self {
468 Self { container }
469 }
470
471 pub async fn create_backup(&self) -> Result<()> {
472 if let Some(ref backup_manager) = self.container.backup_manager {
473 let metadata = backup_manager.create_full_backup().await?;
474 info!("โ
Backup created: {}", metadata.id);
475 Ok(())
476 } else {
477 Err(anyhow::anyhow!("Backup functionality is not enabled"))
478 }
479 }
480
481 pub async fn list_backups(&self) -> Result<()> {
482 if let Some(ref backup_manager) = self.container.backup_manager {
483 let stats = backup_manager.get_backup_statistics().await?;
484 info!("๐ Total backups: {}", stats.total_backups);
485 info!(
486 "โ
Successful (last 7 days): {}",
487 stats.successful_backups_last_7_days
488 );
489 info!(
490 "โ Failed (last 7 days): {}",
491 stats.failed_backups_last_7_days
492 );
493 Ok(())
494 } else {
495 Err(anyhow::anyhow!("Backup functionality is not enabled"))
496 }
497 }
498}