systemprompt_cli/commands/admin/config/
services.rs1use anyhow::{Context, Result, bail};
12use clap::{Args, Subcommand};
13use std::fs;
14use systemprompt_config::ProfileBootstrap;
15use systemprompt_logging::CliService;
16use systemprompt_models::Profile;
17
18use super::runtime::save_profile;
19use super::types::{ServicesConfigOutput, ServicesSetOutput};
20use crate::CliConfig;
21use crate::cli_settings::OutputFormat;
22use crate::shared::{CommandOutput, render_result};
23
24#[derive(Debug, Clone, Copy, Subcommand)]
25pub enum ServicesCommands {
26 #[command(about = "Show services configuration", alias = "list")]
27 Show,
28
29 #[command(about = "Set services configuration value")]
30 Set(SetArgs),
31}
32
33#[derive(Debug, Clone, Copy, Args)]
34pub struct SetArgs {
35 #[arg(
36 long,
37 help = "Shift every locally-bound MCP and agent port by this amount"
38 )]
39 pub port_offset: Option<u16>,
40}
41
42pub fn execute(command: &ServicesCommands, config: &CliConfig) -> Result<()> {
43 match command {
44 ServicesCommands::Show => execute_show(config),
45 ServicesCommands::Set(args) => execute_set(args, config),
46 }
47}
48
49pub(super) fn execute_show(config: &CliConfig) -> Result<()> {
50 let profile = ProfileBootstrap::get()?;
51
52 let output = ServicesConfigOutput {
53 port_offset: profile.services.port_offset,
54 };
55
56 render_result(
57 &CommandOutput::card_value("Services Configuration", &output),
58 config,
59 );
60
61 Ok(())
62}
63
64pub(super) fn execute_set(args: &SetArgs, config: &CliConfig) -> Result<()> {
65 let Some(port_offset) = args.port_offset else {
66 bail!("Must specify at least one option: --port-offset");
67 };
68
69 let profile_path = ProfileBootstrap::get_path()?;
70 let mut profile = load_profile(profile_path)?;
71
72 let old = profile.services.port_offset;
73 profile.services.port_offset = port_offset;
74
75 save_profile(&profile, profile_path)?;
76
77 let change = ServicesSetOutput {
78 field: "port_offset".to_owned(),
79 old_value: old.to_string(),
80 new_value: port_offset.to_string(),
81 message: format!("Updated port_offset to {port_offset}"),
82 };
83
84 render_result(
85 &CommandOutput::card_value("Services Updated", &change),
86 config,
87 );
88
89 if config.output_format() == OutputFormat::Table {
90 CliService::warning("Restart services for changes to take effect");
91 }
92
93 Ok(())
94}
95
96fn load_profile(path: &str) -> Result<Profile> {
97 let content =
98 fs::read_to_string(path).with_context(|| format!("Failed to read profile: {path}"))?;
99 let profile: Profile = serde_yaml::from_str(&content)
100 .with_context(|| format!("Failed to parse profile: {path}"))?;
101 Ok(profile)
102}