use super::KeyStore;
use super::dotenv;
use super::file_store::FileKeyStore;
#[cfg(feature = "keyring-store")]
use super::keyring_store::KeyringStore;
use super::memory_store::MemoryKeyStore;
pub fn env_var_for(provider: &str) -> Option<&'static str> {
match provider.to_ascii_lowercase().as_str() {
"fireworks" => Some("FIREWORKS_API_KEY"),
"openrouter" => Some("OPENROUTER_API_KEY"),
"anthropic" => Some("ANTHROPIC_API_KEY"),
"openai" => Some("OPENAI_API_KEY"),
"together" => Some("TOGETHER_API_KEY"),
"atlascloud" => Some("ATLASCLOUD_API_KEY"),
"slack" => Some("SLACK_BOT_TOKEN"),
"slack-user" => Some("SLACK_USER_TOKEN"),
"telegram" => Some("TELEGRAM_BOT_TOKEN"),
"claude-code" => Some("CLAUDE_CODE_OAUTH_TOKEN"),
_ => None,
}
}
pub fn resolve_key(provider: &str) -> Option<String> {
dotenv::load_env_local_once();
resolve_key_with(provider, default_store().as_ref())
}
pub fn resolve_key_with(provider: &str, store: &dyn KeyStore) -> Option<String> {
if let Some(value) = env_tier(provider) {
return Some(value);
}
store.get(provider)
}
fn env_tier(provider: &str) -> Option<String> {
let var = env_var_for(provider)?;
std::env::var(var).ok().filter(|v| !v.is_empty())
}
pub fn default_store() -> Box<dyn KeyStore> {
#[cfg(feature = "keyring-store")]
{
let keyring = KeyringStore::new();
if keyring.probe_available() {
return Box::new(keyring);
}
}
match FileKeyStore::new() {
Ok(store) => Box::new(store),
Err(_) => Box::new(MemoryKeyStore::new()),
}
}
#[cfg(test)]
mod tests {
use super::*;
use serial_test::serial;
#[test]
fn env_var_for_known_providers() {
assert_eq!(env_var_for("fireworks"), Some("FIREWORKS_API_KEY"));
assert_eq!(env_var_for("OpenRouter"), Some("OPENROUTER_API_KEY"));
assert_eq!(env_var_for("anthropic"), Some("ANTHROPIC_API_KEY"));
assert_eq!(env_var_for("OPENAI"), Some("OPENAI_API_KEY"));
assert_eq!(env_var_for("together"), Some("TOGETHER_API_KEY"));
assert_eq!(env_var_for("atlascloud"), Some("ATLASCLOUD_API_KEY"));
assert_eq!(env_var_for("slack"), Some("SLACK_BOT_TOKEN"));
assert_eq!(env_var_for("Slack"), Some("SLACK_BOT_TOKEN"));
assert_eq!(env_var_for("slack-user"), Some("SLACK_USER_TOKEN"));
assert_eq!(env_var_for("Slack-User"), Some("SLACK_USER_TOKEN"));
assert_eq!(env_var_for("telegram"), Some("TELEGRAM_BOT_TOKEN"));
assert_eq!(env_var_for("claude-code"), Some("CLAUDE_CODE_OAUTH_TOKEN"));
assert_eq!(env_var_for("Claude-Code"), Some("CLAUDE_CODE_OAUTH_TOKEN"));
}
#[test]
fn env_var_for_unknown_provider_is_none() {
assert_eq!(env_var_for("some-future-provider"), None);
}
#[test]
#[serial(dotenv_credential_env)]
fn env_beats_store() {
unsafe {
std::env::set_var("FIREWORKS_API_KEY", "from-env");
}
let store = MemoryKeyStore::new();
store.set("fireworks", "from-store").unwrap();
assert_eq!(
resolve_key_with("fireworks", &store),
Some("from-env".to_string())
);
unsafe {
std::env::remove_var("FIREWORKS_API_KEY");
}
}
#[test]
#[serial(dotenv_credential_env)]
fn dotenv_loaded_value_beats_store() {
unsafe {
std::env::remove_var("OPENROUTER_API_KEY");
}
let tmp = tempfile::TempDir::new().unwrap();
let env_path = tmp.path().join(".env.local");
std::fs::write(&env_path, "OPENROUTER_API_KEY=from-dotenv\n").unwrap();
assert!(dotenv::load_env_from_path(&env_path));
let store = MemoryKeyStore::new();
store.set("openrouter", "from-store").unwrap();
assert_eq!(
resolve_key_with("openrouter", &store),
Some("from-dotenv".to_string())
);
unsafe {
std::env::remove_var("OPENROUTER_API_KEY");
}
}
#[test]
#[serial(dotenv_credential_env)]
fn falls_through_to_store() {
unsafe {
std::env::remove_var("ANTHROPIC_API_KEY");
}
let store = MemoryKeyStore::new();
store.set("anthropic", "from-store").unwrap();
assert_eq!(
resolve_key_with("anthropic", &store),
Some("from-store".to_string())
);
}
#[test]
#[serial(dotenv_credential_env)]
fn absent_everywhere_is_none() {
unsafe {
std::env::remove_var("OPENAI_API_KEY");
}
let store = MemoryKeyStore::new();
assert_eq!(resolve_key_with("openai", &store), None);
}
#[test]
#[serial(dotenv_credential_env)]
fn empty_env_var_falls_through_to_store() {
unsafe {
std::env::set_var("FIREWORKS_API_KEY", "");
}
let store = MemoryKeyStore::new();
store.set("fireworks", "from-store").unwrap();
assert_eq!(
resolve_key_with("fireworks", &store),
Some("from-store".to_string())
);
unsafe {
std::env::remove_var("FIREWORKS_API_KEY");
}
}
}