Skip to main content

systemprompt_cli/commands/infrastructure/services/
mod.rs

1//! `infra services` command group: start, stop, restart, status, cleanup, and
2//! serve.
3//!
4//! Defines the [`ServicesCommands`] clap subcommand tree and the individual
5//! target enums ([`StartTarget`], [`StopTarget`], [`RestartTarget`]); dispatch
6//! is delegated to the sibling implementation modules via [`execute`].
7//!
8//! Copyright (c) systemprompt.io — Business Source License 1.1.
9//! See <https://systemprompt.io> for licensing details.
10
11pub mod cleanup;
12mod dispatch;
13mod lifecycle;
14pub mod restart;
15pub mod serve;
16pub mod start;
17pub mod status;
18mod stop;
19mod types;
20
21use clap::Subcommand;
22use systemprompt_config::ProfileBootstrap;
23use systemprompt_identifiers::McpServerId;
24
25pub use dispatch::{execute, load_service_configs};
26
27const DEFAULT_API_PORT: u16 = 8080;
28
29pub(super) fn get_api_port() -> u16 {
30    ProfileBootstrap::get().map_or(DEFAULT_API_PORT, |p| p.server.port)
31}
32
33pub(super) fn get_api_addr() -> anyhow::Result<String> {
34    let profile = ProfileBootstrap::get()?;
35    Ok(format!("{}:{}", profile.server.host, profile.server.port))
36}
37
38#[derive(Debug, Clone, Subcommand)]
39pub enum StartTarget {
40    #[command(about = "Start a single agent by name")]
41    Agent { agent: String },
42    #[command(about = "Start a single MCP server by name")]
43    Mcp {
44        #[arg(value_parser = crate::shared::parse_mcp_server_id)]
45        server_name: McpServerId,
46    },
47}
48
49#[derive(Debug, Clone, Subcommand)]
50pub enum StopTarget {
51    #[command(about = "Stop a single agent by name")]
52    Agent {
53        agent: String,
54        #[arg(long, help = "Force stop (SIGKILL)")]
55        force: bool,
56    },
57    #[command(about = "Stop a single MCP server by name")]
58    Mcp {
59        #[arg(value_parser = crate::shared::parse_mcp_server_id)]
60        server_name: McpServerId,
61        #[arg(long, help = "Force stop (SIGKILL)")]
62        force: bool,
63    },
64}
65
66const KILL_PORT_PROCESS_HELP: &str = "Signal the process holding the API port even when it is \
67                                      not a verified systemprompt API server (asks for \
68                                      confirmation naming the PID; refused when non-interactive)";
69
70#[derive(Debug, Subcommand)]
71pub enum ServicesCommands {
72    #[command(
73        about = "Start API, agents, and MCP servers",
74        after_help = "EXAMPLES:\n  systemprompt infra services start\n  systemprompt infra \
75                      services start --api\n  systemprompt infra services start --agents --mcp\n  \
76                      systemprompt infra services start agent <name>"
77    )]
78    Start {
79        #[command(subcommand)]
80        target: Option<StartTarget>,
81
82        #[arg(long, help = "Start all services")]
83        all: bool,
84
85        #[arg(long, help = "Start API server only")]
86        api: bool,
87
88        #[arg(long, help = "Start agents only")]
89        agents: bool,
90
91        #[arg(long, help = "Start MCP servers only")]
92        mcp: bool,
93
94        #[arg(long, help = "Run in foreground (default)")]
95        foreground: bool,
96
97        #[arg(long, help = "Skip database migrations")]
98        skip_migrate: bool,
99
100        #[arg(long, help = KILL_PORT_PROCESS_HELP)]
101        kill_port_process: bool,
102    },
103
104    #[command(
105        about = "Stop running services gracefully",
106        after_help = "EXAMPLES:\n  systemprompt infra services stop\n  systemprompt infra \
107                      services stop --api\n  systemprompt infra services stop agent <name> \
108                      [--force]"
109    )]
110    Stop {
111        #[command(subcommand)]
112        target: Option<StopTarget>,
113
114        #[arg(long, help = "Stop all services")]
115        all: bool,
116
117        #[arg(long, help = "Stop API server only")]
118        api: bool,
119
120        #[arg(long, help = "Stop agents only")]
121        agents: bool,
122
123        #[arg(long, help = "Stop MCP servers only")]
124        mcp: bool,
125
126        #[arg(long, help = "Force stop (SIGKILL)")]
127        force: bool,
128    },
129
130    #[command(about = "Restart services")]
131    Restart {
132        #[command(subcommand)]
133        target: Option<RestartTarget>,
134
135        #[arg(long, help = "Restart only failed services")]
136        failed: bool,
137
138        #[arg(long, help = "Restart all agents")]
139        agents: bool,
140
141        #[arg(long, help = "Restart all MCP servers")]
142        mcp: bool,
143    },
144
145    #[command(about = "Show detailed service status")]
146    Status {
147        #[arg(long, help = "Show detailed information")]
148        detailed: bool,
149
150        #[arg(long, help = "Output as JSON")]
151        json: bool,
152
153        #[arg(long, help = "Include health check results")]
154        health: bool,
155    },
156
157    #[command(about = "Clean up orphaned processes and stale entries")]
158    Cleanup {
159        #[arg(short = 'y', long, help = "Skip confirmation prompt")]
160        yes: bool,
161
162        #[arg(long, help = "Preview cleanup without executing")]
163        dry_run: bool,
164    },
165
166    #[command(about = "Start API server (automatically starts agents and MCP servers)")]
167    Serve {
168        #[arg(long, help = "Run in foreground mode")]
169        foreground: bool,
170
171        #[arg(long, help = KILL_PORT_PROCESS_HELP)]
172        kill_port_process: bool,
173
174        #[arg(
175            long,
176            help = "Skip database migrations (the schema is still verified to be current)"
177        )]
178        skip_migrate: bool,
179    },
180}
181
182impl ServicesCommands {
183    #[must_use]
184    pub const fn serves_api(&self) -> bool {
185        match self {
186            Self::Serve { .. }
187            | Self::Restart {
188                target: Some(RestartTarget::Api),
189                ..
190            } => true,
191            Self::Start {
192                target: None,
193                all,
194                api,
195                agents,
196                mcp,
197                ..
198            } => *all || *api || !(*agents || *mcp),
199            _ => false,
200        }
201    }
202}
203
204#[derive(Debug, Clone, Subcommand)]
205pub enum RestartTarget {
206    #[command(about = "Restart the API server")]
207    Api,
208    #[command(about = "Restart a single agent by name")]
209    Agent { agent: String },
210    #[command(about = "Restart a single MCP server by name")]
211    Mcp {
212        #[arg(value_parser = crate::shared::parse_mcp_server_id)]
213        server_name: McpServerId,
214        #[arg(long, help = "Rebuild the binary before restarting")]
215        build: bool,
216    },
217}