systemprompt_cli/commands/infrastructure/services/
mod.rs1pub 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}