feat(config): prefer dispatcher-provided API key over saved DeepSeek key when source is cli
When the CLI dispatcher launches the interactive TUI with an explicit
`--api-key` argument (e.g. for a DeepSeek-compatible subscription
endpoint), the environment variable `DEEPSEEK_API_KEY` carries the
intended key with `DEEPSEEK_API_KEY_SOURCE=cli`. Previously the
saved root `api_key` in config.toml always won over this env override
for the DeepSeek provider, blocking users from running:
codewhale --provider deepseek \
--api-key ark-... --base-url https://... --model auto
This change gives the dispatcher-supplied env key priority when the
source marker is `cli`, keeping full backward compatibility for
normal config-file or keyring paths, and also cleaning up a `***`
literal in an unrelated test.
This commit is contained in:
@@ -2518,6 +2518,20 @@ impl Config {
|
||||
// 0. DeepSeek compatibility slot. The legacy top-level `api_key`
|
||||
// belongs to DeepSeek only; provider-specific keys below must win for
|
||||
// NIM/OpenRouter/etc. so a stale DeepSeek key is not sent elsewhere.
|
||||
//
|
||||
// However, when the CLI dispatcher forwards an explicit `--api-key`
|
||||
// through `DEEPSEEK_API_KEY` with the dispatcher source marker, that
|
||||
// intentional override must win over the saved root key. This is
|
||||
// essential for DeepSeek-compatible subscription endpoints where the
|
||||
// user runs something like:
|
||||
// codewhale --provider deepseek --api-key ark-... --base-url ... --model auto
|
||||
if matches!(provider, ApiProvider::Deepseek | ApiProvider::DeepseekCN)
|
||||
&& std::env::var("DEEPSEEK_API_KEY_SOURCE").as_deref() == Ok("cli")
|
||||
&& let Some(env_key) = codewhale_secrets::env_for("deepseek")
|
||||
&& !env_key.trim().is_empty()
|
||||
{
|
||||
return Ok(env_key);
|
||||
}
|
||||
if matches!(provider, ApiProvider::Deepseek | ApiProvider::DeepseekCN)
|
||||
&& let Some(configured) = self.api_key.as_ref()
|
||||
&& !configured.trim().is_empty()
|
||||
@@ -7002,7 +7016,7 @@ action = "session.compact"
|
||||
// Env var path.
|
||||
let env_cfg = Config::default();
|
||||
unsafe {
|
||||
std::env::set_var("DEEPSEEK_API_KEY", "sk-test-from-env");
|
||||
std::env::set_var("DEEPSEEK_API_KEY", "env-key");
|
||||
}
|
||||
assert!(
|
||||
has_api_key(&env_cfg),
|
||||
@@ -7014,6 +7028,31 @@ action = "session.compact"
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deepseek_dispatcher_env_key_overrides_config_key() -> Result<()> {
|
||||
let _lock = lock_test_env();
|
||||
let prev_source = std::env::var_os("DEEPSEEK_API_KEY_SOURCE");
|
||||
unsafe {
|
||||
std::env::set_var("DEEPSEEK_API_KEY", "ark-dispatcher-key");
|
||||
std::env::set_var("DEEPSEEK_API_KEY_SOURCE", "cli");
|
||||
}
|
||||
let config = Config {
|
||||
api_key: Some("saved-deepseek-key".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
assert_eq!(config.deepseek_api_key()?, "ark-dispatcher-key");
|
||||
|
||||
unsafe {
|
||||
std::env::remove_var("DEEPSEEK_API_KEY");
|
||||
match prev_source {
|
||||
Some(value) => std::env::set_var("DEEPSEEK_API_KEY_SOURCE", value),
|
||||
None => std::env::remove_var("DEEPSEEK_API_KEY_SOURCE"),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn config_with_provider_scoped_key(provider: &str, api_key: &str) -> Config {
|
||||
let mut providers = ProvidersConfig::default();
|
||||
match provider {
|
||||
|
||||
Reference in New Issue
Block a user