systemprompt-cli 0.2.2

Unified CLI for systemprompt.io AI governance: agent orchestration, MCP governance, analytics, profiles, cloud deploy, and self-hosted operations.
Documentation
use anyhow::{Result, bail};
use dialoguer::theme::ColorfulTheme;
use dialoguer::{Password, Select};
use systemprompt_logging::CliService;

#[derive(Debug)]
pub struct ApiKeys {
    pub gemini: Option<String>,
    pub anthropic: Option<String>,
    pub openai: Option<String>,
}

impl ApiKeys {
    pub fn from_options(
        gemini: Option<String>,
        anthropic: Option<String>,
        openai: Option<String>,
    ) -> Result<Self> {
        if gemini.is_none() && anthropic.is_none() && openai.is_none() {
            bail!(
                "At least one AI provider API key is required.\nProvide: --anthropic-key, \
                 --openai-key, or --gemini-key"
            );
        }
        Ok(Self {
            gemini,
            anthropic,
            openai,
        })
    }

    pub const fn selected_provider(&self) -> &'static str {
        if self.anthropic.is_some() {
            "anthropic"
        } else if self.openai.is_some() {
            "openai"
        } else if self.gemini.is_some() {
            "gemini"
        } else {
            "anthropic"
        }
    }
}

pub fn collect_api_keys() -> Result<ApiKeys> {
    CliService::info("At least one AI provider API key is required.");

    let providers = vec![
        "Google AI (Gemini) - https://aistudio.google.com/app/apikey",
        "Anthropic (Claude) - https://console.anthropic.com/api-keys",
        "OpenAI (GPT) - https://platform.openai.com/api-keys",
    ];

    let selection = Select::with_theme(&ColorfulTheme::default())
        .with_prompt("Select your AI provider")
        .items(&providers)
        .default(0)
        .interact()?;

    match selection {
        0 => {
            let key = Password::with_theme(&ColorfulTheme::default())
                .with_prompt("Gemini API Key")
                .interact()?;
            if key.is_empty() {
                bail!("API key is required");
            }
            Ok(ApiKeys {
                gemini: Some(key),
                anthropic: None,
                openai: None,
            })
        },
        1 => {
            let key = Password::with_theme(&ColorfulTheme::default())
                .with_prompt("Anthropic API Key")
                .interact()?;
            if key.is_empty() {
                bail!("API key is required");
            }
            Ok(ApiKeys {
                gemini: None,
                anthropic: Some(key),
                openai: None,
            })
        },
        2 => {
            let key = Password::with_theme(&ColorfulTheme::default())
                .with_prompt("OpenAI API Key")
                .interact()?;
            if key.is_empty() {
                bail!("API key is required");
            }
            Ok(ApiKeys {
                gemini: None,
                anthropic: None,
                openai: Some(key),
            })
        },
        _ => bail!("Invalid selection"),
    }
}