Skip to main content

systemprompt_cli/commands/cloud/profile/
api_keys.rs

1use anyhow::{bail, Result};
2use dialoguer::theme::ColorfulTheme;
3use dialoguer::{Password, Select};
4use systemprompt_logging::CliService;
5
6#[derive(Debug)]
7pub struct ApiKeys {
8    pub gemini: Option<String>,
9    pub anthropic: Option<String>,
10    pub openai: Option<String>,
11}
12
13impl ApiKeys {
14    pub fn from_options(
15        gemini: Option<String>,
16        anthropic: Option<String>,
17        openai: Option<String>,
18    ) -> Result<Self> {
19        if gemini.is_none() && anthropic.is_none() && openai.is_none() {
20            bail!(
21                "At least one AI provider API key is required.\nProvide: --anthropic-key, \
22                 --openai-key, or --gemini-key"
23            );
24        }
25        Ok(Self {
26            gemini,
27            anthropic,
28            openai,
29        })
30    }
31
32    pub const fn selected_provider(&self) -> &'static str {
33        if self.anthropic.is_some() {
34            "anthropic"
35        } else if self.openai.is_some() {
36            "openai"
37        } else if self.gemini.is_some() {
38            "gemini"
39        } else {
40            "anthropic"
41        }
42    }
43}
44
45pub fn collect_api_keys() -> Result<ApiKeys> {
46    CliService::info("At least one AI provider API key is required.");
47
48    let providers = vec![
49        "Google AI (Gemini) - https://aistudio.google.com/app/apikey",
50        "Anthropic (Claude) - https://console.anthropic.com/api-keys",
51        "OpenAI (GPT) - https://platform.openai.com/api-keys",
52    ];
53
54    let selection = Select::with_theme(&ColorfulTheme::default())
55        .with_prompt("Select your AI provider")
56        .items(&providers)
57        .default(0)
58        .interact()?;
59
60    match selection {
61        0 => {
62            let key = Password::with_theme(&ColorfulTheme::default())
63                .with_prompt("Gemini API Key")
64                .interact()?;
65            if key.is_empty() {
66                bail!("API key is required");
67            }
68            Ok(ApiKeys {
69                gemini: Some(key),
70                anthropic: None,
71                openai: None,
72            })
73        },
74        1 => {
75            let key = Password::with_theme(&ColorfulTheme::default())
76                .with_prompt("Anthropic API Key")
77                .interact()?;
78            if key.is_empty() {
79                bail!("API key is required");
80            }
81            Ok(ApiKeys {
82                gemini: None,
83                anthropic: Some(key),
84                openai: None,
85            })
86        },
87        2 => {
88            let key = Password::with_theme(&ColorfulTheme::default())
89                .with_prompt("OpenAI API Key")
90                .interact()?;
91            if key.is_empty() {
92                bail!("API key is required");
93            }
94            Ok(ApiKeys {
95                gemini: None,
96                anthropic: None,
97                openai: Some(key),
98            })
99        },
100        _ => bail!("Invalid selection"),
101    }
102}