Skip to main content

systemprompt_cli/commands/infrastructure/services/
restart.rs

1use crate::cli_settings::CliConfig;
2use anyhow::{Context, Result};
3use std::sync::Arc;
4use systemprompt_agent::services::agent_orchestration::AgentOrchestrator;
5use systemprompt_agent::services::registry::AgentRegistry;
6use systemprompt_agent::AgentState;
7use systemprompt_logging::CliService;
8use systemprompt_mcp::services::McpManager;
9use systemprompt_models::ProfileBootstrap;
10use systemprompt_oauth::JwtValidationProviderImpl;
11use systemprompt_runtime::AppContext;
12use systemprompt_scheduler::ProcessCleanup;
13
14const DEFAULT_API_PORT: u16 = 8080;
15
16fn create_agent_state(ctx: &AppContext) -> Result<Arc<AgentState>> {
17    let jwt_provider = Arc::new(
18        JwtValidationProviderImpl::from_config().context("Failed to create JWT provider")?,
19    );
20    Ok(Arc::new(AgentState::new(
21        Arc::clone(ctx.db_pool()),
22        Arc::new(ctx.config().clone()),
23        jwt_provider,
24    )))
25}
26
27fn get_api_port() -> u16 {
28    ProfileBootstrap::get()
29        .map(|p| p.server.port)
30        .unwrap_or(DEFAULT_API_PORT)
31}
32
33async fn resolve_name(agent_identifier: &str) -> Result<String> {
34    let registry = AgentRegistry::new().await?;
35    let agent = registry.get_agent(agent_identifier).await?;
36    Ok(agent.name)
37}
38
39pub async fn execute_api(config: &CliConfig) -> Result<()> {
40    CliService::section("Restarting API Server");
41
42    let port = get_api_port();
43    let Some(pid) = ProcessCleanup::check_port(port) else {
44        CliService::warning("API server is not running");
45        CliService::info("Starting API server...");
46        return super::serve::execute(true, false, config).await;
47    };
48    CliService::info(&format!("Stopping API server (PID: {})...", pid));
49
50    ProcessCleanup::terminate_gracefully(pid, 100).await;
51    ProcessCleanup::kill_port(port);
52
53    ProcessCleanup::wait_for_port_free(port, 5, 500).await?;
54
55    CliService::success("API server stopped");
56    CliService::info("Starting API server...");
57
58    super::serve::execute(true, false, config).await?;
59
60    CliService::success("API server restarted successfully");
61    Ok(())
62}
63
64pub async fn execute_agent(
65    ctx: &Arc<AppContext>,
66    agent_id: &str,
67    _config: &CliConfig,
68) -> Result<()> {
69    CliService::section(&format!("Restarting Agent: {}", agent_id));
70
71    let agent_state = create_agent_state(ctx)?;
72    let orchestrator = AgentOrchestrator::new(agent_state, None)
73        .await
74        .context("Failed to initialize agent orchestrator")?;
75
76    let name = resolve_name(agent_id).await?;
77    let service_id = orchestrator.restart_agent(&name, None).await?;
78
79    CliService::success(&format!(
80        "Agent {} restarted successfully (service ID: {})",
81        agent_id, service_id
82    ));
83
84    Ok(())
85}
86
87pub async fn execute_mcp(
88    ctx: &Arc<AppContext>,
89    server_name: &str,
90    build: bool,
91    _config: &CliConfig,
92) -> Result<()> {
93    let action = if build {
94        "Building and restarting"
95    } else {
96        "Restarting"
97    };
98    CliService::section(&format!("{} MCP Server: {}", action, server_name));
99
100    let manager =
101        McpManager::new(Arc::clone(ctx.db_pool())).context("Failed to initialize MCP manager")?;
102
103    if build {
104        manager
105            .build_and_restart_services(Some(server_name.to_string()))
106            .await?;
107    } else {
108        manager
109            .restart_services_sync(Some(server_name.to_string()))
110            .await?;
111    }
112
113    CliService::success(&format!(
114        "MCP server {} restarted successfully",
115        server_name
116    ));
117
118    Ok(())
119}
120
121pub async fn execute_all_agents(ctx: &Arc<AppContext>, _config: &CliConfig) -> Result<()> {
122    CliService::section("Restarting All Agents");
123
124    let agent_state = create_agent_state(ctx)?;
125    let orchestrator = AgentOrchestrator::new(agent_state, None)
126        .await
127        .context("Failed to initialize agent orchestrator")?;
128
129    let agent_registry = AgentRegistry::new().await?;
130    let all_agents = orchestrator.list_all().await?;
131
132    let mut restarted = 0i32;
133    let mut failed = 0i32;
134
135    for (agent_id, _status) in &all_agents {
136        let Ok(agent_config) = agent_registry.get_agent(agent_id).await else {
137            continue;
138        };
139
140        if !agent_config.enabled {
141            continue;
142        }
143
144        CliService::info(&format!("Restarting agent: {}", agent_config.name));
145        match orchestrator.restart_agent(agent_id, None).await {
146            Ok(_) => {
147                restarted += 1;
148                CliService::success(&format!("  {} restarted", agent_config.name));
149            },
150            Err(e) => {
151                failed += 1;
152                CliService::error(&format!("  Failed to restart {}: {}", agent_config.name, e));
153            },
154        }
155    }
156
157    match (restarted, failed) {
158        (0, 0) => CliService::info("No enabled agents found"),
159        (r, 0) => CliService::success(&format!("Restarted {} agents", r)),
160        (0, f) => CliService::warning(&format!("Failed to restart {} agents", f)),
161        (r, f) => {
162            CliService::success(&format!("Restarted {} agents", r));
163            CliService::warning(&format!("Failed to restart {} agents", f));
164        },
165    }
166
167    Ok(())
168}
169
170pub async fn execute_all_mcp(ctx: &Arc<AppContext>, _config: &CliConfig) -> Result<()> {
171    CliService::section("Restarting All MCP Servers");
172
173    let mcp_manager =
174        McpManager::new(Arc::clone(ctx.db_pool())).context("Failed to initialize MCP manager")?;
175
176    systemprompt_mcp::services::RegistryManager::validate()?;
177    let servers = systemprompt_mcp::services::RegistryManager::get_enabled_servers()?;
178
179    let mut restarted = 0i32;
180    let mut failed = 0i32;
181
182    for server in servers {
183        if !server.enabled {
184            continue;
185        }
186
187        CliService::info(&format!("Restarting MCP server: {}", server.name));
188        match mcp_manager
189            .restart_services(Some(server.name.clone()))
190            .await
191        {
192            Ok(()) => {
193                restarted += 1;
194                CliService::success(&format!("  {} restarted", server.name));
195            },
196            Err(e) => {
197                failed += 1;
198                CliService::error(&format!("  Failed to restart {}: {}", server.name, e));
199            },
200        }
201    }
202
203    match (restarted, failed) {
204        (0, 0) => CliService::info("No enabled MCP servers found"),
205        (r, 0) => CliService::success(&format!("Restarted {} MCP servers", r)),
206        (0, f) => CliService::warning(&format!("Failed to restart {} MCP servers", f)),
207        (r, f) => {
208            CliService::success(&format!("Restarted {} MCP servers", r));
209            CliService::warning(&format!("Failed to restart {} MCP servers", f));
210        },
211    }
212
213    Ok(())
214}
215
216pub async fn execute_failed(ctx: &Arc<AppContext>, _config: &CliConfig) -> Result<()> {
217    CliService::section("Restarting Failed Services");
218
219    let mut restarted_count = 0;
220    let mut failed_count = 0;
221
222    restart_failed_agents(ctx, &mut restarted_count, &mut failed_count).await?;
223    restart_failed_mcp(ctx, &mut restarted_count, &mut failed_count).await?;
224
225    if restarted_count > 0 {
226        CliService::success(&format!("Restarted {} failed services", restarted_count));
227    } else {
228        CliService::info("No failed services found");
229    }
230
231    if failed_count > 0 {
232        CliService::warning(&format!("Failed to restart {} services", failed_count));
233    }
234
235    Ok(())
236}
237
238async fn restart_failed_agents(
239    ctx: &Arc<AppContext>,
240    restarted_count: &mut i32,
241    failed_count: &mut i32,
242) -> Result<()> {
243    let agent_state = create_agent_state(ctx)?;
244    let orchestrator = AgentOrchestrator::new(agent_state, None)
245        .await
246        .context("Failed to initialize agent orchestrator")?;
247
248    let agent_registry = AgentRegistry::new().await?;
249
250    let all_agents = orchestrator.list_all().await?;
251    for (agent_id, status) in &all_agents {
252        let Ok(agent_config) = agent_registry.get_agent(agent_id).await else {
253            continue;
254        };
255
256        if !agent_config.enabled {
257            continue;
258        }
259
260        if let systemprompt_agent::services::agent_orchestration::AgentStatus::Failed { .. } =
261            status
262        {
263            CliService::info(&format!("Restarting failed agent: {}", agent_config.name));
264            match orchestrator.restart_agent(agent_id, None).await {
265                Ok(_) => {
266                    *restarted_count += 1;
267                    CliService::success(&format!("  {} restarted", agent_config.name));
268                },
269                Err(e) => {
270                    *failed_count += 1;
271                    CliService::error(&format!("  Failed to restart {}: {}", agent_config.name, e));
272                },
273            }
274        }
275    }
276
277    Ok(())
278}
279
280async fn restart_failed_mcp(
281    ctx: &Arc<AppContext>,
282    restarted_count: &mut i32,
283    failed_count: &mut i32,
284) -> Result<()> {
285    let mcp_manager =
286        McpManager::new(Arc::clone(ctx.db_pool())).context("Failed to initialize MCP manager")?;
287
288    systemprompt_mcp::services::RegistryManager::validate()?;
289    let servers = systemprompt_mcp::services::RegistryManager::get_enabled_servers()?;
290
291    for server in servers {
292        if !server.enabled {
293            continue;
294        }
295
296        let database = systemprompt_mcp::services::DatabaseManager::new(Arc::clone(ctx.db_pool()));
297        let service_info = database.get_service_by_name(&server.name).await?;
298
299        let needs_restart = match service_info {
300            Some(info) => info.status != "running",
301            None => true,
302        };
303
304        if needs_restart {
305            CliService::info(&format!("Restarting MCP server: {}", server.name));
306            match mcp_manager
307                .restart_services(Some(server.name.clone()))
308                .await
309            {
310                Ok(()) => {
311                    *restarted_count += 1;
312                    CliService::success(&format!("  {} restarted", server.name));
313                },
314                Err(e) => {
315                    *failed_count += 1;
316                    CliService::error(&format!("  Failed to restart {}: {}", server.name, e));
317                },
318            }
319        }
320    }
321
322    Ok(())
323}