systemprompt_cli/commands/cloud/profile/
api_keys.rs1use 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}