1use crate::llm::{AnthropicSecret, OpenAISecret, Provider, load_presets, verify_client};
2use crate::system::{LLMConfig, SecretManager};
3use anyhow::{Context, Result};
4use cliclack::{input, password, select, spinner};
5
6pub async fn handle_secret_command() -> Result<()> {
7 cliclack::intro("🔐 Configure LLM Provider Secret")?;
8
9 let providers = Provider::all();
11 let provider_items: Vec<_> = providers
12 .iter()
13 .map(|p| (p.as_str(), p.as_display(), ""))
14 .collect();
15
16 let provider_str = select("Select provider to configure")
17 .items(&provider_items)
18 .interact()
19 .context("Selection failed")?;
20
21 let provider: Provider = provider_str.parse()?;
22
23 match provider {
25 Provider::OpenAI => {
26 let api_key = password("OpenAI API Key (required)")
27 .interact()
28 .context("Password input failed")?;
29
30 let organization: String = input("Organization ID (optional, press Enter to skip)")
31 .placeholder("Press Enter to skip")
32 .required(false)
33 .interact()
34 .context("Input failed")?;
35
36 let project: String = input("Project ID (optional, press Enter to skip)")
37 .placeholder("Press Enter to skip")
38 .required(false)
39 .interact()
40 .context("Input failed")?;
41
42 let secret = OpenAISecret {
43 api_key: api_key.trim().to_string(),
44 organization: if organization.trim().is_empty() {
45 None
46 } else {
47 Some(organization.trim().to_string())
48 },
49 project: if project.trim().is_empty() {
50 None
51 } else {
52 Some(project.trim().to_string())
53 },
54 };
55 SecretManager::save(&provider, &secret)?;
56 }
57 Provider::Anthropic => {
58 let api_key = password("Anthropic API Key (required)")
59 .interact()
60 .context("Password input failed")?;
61
62 let secret = AnthropicSecret {
63 api_key: api_key.trim().to_string(),
64 };
65 SecretManager::save(&provider, &secret)?;
66 }
67 }
68
69 let first_model = load_presets(&provider)
71 .first()
72 .map(|m| m.id.clone())
73 .context("No preset models available for this provider")?;
74
75 let llm_config = LLMConfig {
76 provider,
77 model: first_model.clone(),
78 timeout: 30,
79 use_thinking: false,
80 use_web_search: false,
81 };
82
83 let sp = spinner();
84 sp.start("Verifying API key...");
85
86 match verify_client(&llm_config).await {
87 Ok(true) => {
88 sp.stop(format!(
89 "{} secrets saved and verified successfully",
90 provider_str.to_uppercase()
91 ));
92 cliclack::outro("✅ Configuration complete!")?;
93 Ok(())
94 }
95 Ok(false) => {
96 sp.stop("API key verification failed");
97 cliclack::outro(
98 "⚠️ Secrets saved but verification failed. Please check your API key.",
99 )?;
100 Ok(())
101 }
102 Err(e) => {
103 sp.stop("Verification error");
104 cliclack::outro(format!("❌ Error: {}", e))?;
105 Err(e)
106 }
107 }
108}