返回 CodeWhale
credential_handoff.rs
根目录 / crates / cli / src / credential_handoff.rs
1 use super::{runtime_overrides_for_provider, xai_auth_diagnostics};
2 use anyhow::{Context, Result, bail, ensure};
3 use codewhale_config::{
4 CliRuntimeOverrides, ConfigStore, ProviderKind, RuntimeApiKeySource,
5 auth_mode_uses_kimi_imported_token,
6 };
7 use codewhale_secrets::Secrets;
8 use std::io::{ErrorKind, Write};
9 use zeroize::Zeroizing;
10
11 const TERMINAL_REFUSAL: &str =
12 "refusing terminal output; pipe credential handoff to the intended local client";
13
14 pub(crate) fn prepare_stdout(stdout_is_terminal: bool) -> Result<()> {
15 ensure!(!stdout_is_terminal, TERMINAL_REFUSAL);
16 #[cfg(unix)]
17 // SAFETY: this one-shot CLI exits before another command can inherit it.
18 unsafe {
19 let _ = libc::signal(libc::SIGPIPE, libc::SIG_IGN);
20 }
21 Ok(())
22 }
23
24 pub(crate) fn resolve_api_key(
25 store: &ConfigStore,
26 secrets: &Secrets,
27 provider: ProviderKind,
28 runtime_overrides: &CliRuntimeOverrides,
29 ) -> Result<String> {
30 let resolved = store.config.resolve_runtime_options_with_secrets(
31 &runtime_overrides_for_provider(runtime_overrides, provider),
32 secrets,
33 );
34 if resolved.provider != provider {
35 bail!("resolved a different provider");
36 }
37 let source = resolved.api_key_source;
38 if source != Some(RuntimeApiKeySource::Cli) {
39 if provider == ProviderKind::OpenaiCodex {
40 bail!("bearer credentials are not an API key");
41 }
42 let uses_api_key = provider != ProviderKind::Xai
43 || xai_auth_diagnostics(store, runtime_overrides).evaluates_runtime_api_key();
44 ensure!(uses_api_key, "OAuth bearer credentials are not an API key");
45 let kimi_bearer = provider == ProviderKind::Moonshot
46 && resolved
47 .auth_mode
48 .as_deref()
49 .is_some_and(auth_mode_uses_kimi_imported_token);
50 ensure!(!kimi_bearer, "bearer credentials are not an API key");
51 }
52 ensure!(source.is_some(), "no runtime-effective API key");
53 resolved
54 .api_key
55 .filter(|value| !value.trim().is_empty())
56 .context("no usable runtime-effective API key")
57 }
58
59 pub(crate) fn handoff_secret_line(
60 writer: &mut impl Write,
61 stdout_is_terminal: bool,
62 resolve: impl FnOnce() -> Result<String>,
63 ) -> Result<()> {
64 prepare_stdout(stdout_is_terminal)?;
65 let secret = Zeroizing::new(resolve().map_err(|_| anyhow::anyhow!("unavailable credential"))?);
66 ensure!(!secret.trim().is_empty(), "credential handoff was empty");
67 let written = writeln!(writer, "{}", secret.as_str());
68 if written.is_ok() || written.is_err_and(|error| error.kind() == ErrorKind::BrokenPipe) {
69 return Ok(());
70 }
71 bail!("credential handoff could not write to stdout")
72 }
73 #[cfg(test)]
74 mod tests;
75
75 lines RUST