//! secret: manage secrets in the OS keyring (values never shown or logged). //! Secrets are scoped to an agent (`agent/NAME`, issue #36) or to a provider //! (`providers//`, issue #89) — the provider namespace is //! shared by every agent of that provider. use super::*; use crate::cli::SecretCmd; use crate::output::print_json; use crate::secrets::{self, SecretStore}; /// Resolve the keyring key and the display scope for a secret name. /// Exactly one of (agent, provider) must be given. fn scope_key( name: &str, agent: Option<&str>, provider: Option<&str>, ) -> Result<(String, String)> { match (provider, agent) { (Some(p), _) => Ok((secrets::key_for_provider(p, name), format!("provider {p}"))), (None, Some(a)) => Ok((secrets::key_for(a, name), format!("agent {a}"))), (None, None) => bail!( "{}", crate::i18n::tr( "usage: secret set --agent --value | secret set --provider --value " ) ), } } pub fn run(app: &App, sub: &SecretCmd) -> Result { match sub { SecretCmd::Set { name, agent, provider, value, } => { let (key, scope) = scope_key(name, agent.as_deref(), provider.as_deref())?; if app.dry_run() { app.log.dry(format!("would store secret {name} for {scope} in the OS keyring")); return Ok(0); } secrets::store().set(&key, value)?; app.log.success(&crate::tr_fmt!("secret '{}' stocké pour {}", name, scope)); app.log.info("use it with --env NAME=@secret (injected at start/run)"); Ok(0) } SecretCmd::Unset { name, agent, provider, } => { let (key, scope) = scope_key(name, agent.as_deref(), provider.as_deref())?; if app.dry_run() { app.log.dry(format!("would remove secret {key}")); return Ok(0); } let removed = secrets::store().remove(&key)?; if removed { app.log.success(&crate::tr_fmt!("secret '{}' supprimé pour {}", name, scope)); } else { app.log.info(&crate::tr_fmt!("secret '{}' inexistant pour {}", name, scope)); } Ok(0) } SecretCmd::List => { let all = secrets::store().list().unwrap_or_default(); if app.json() { print_json(&all); return Ok(0); } if all.is_empty() { app.log.info("no secrets stored — see: am secret set NAME --agent AGENT --value ..."); return Ok(0); } // Both namespaces appear here: agent/NAME and providers/

/. let mut table = crate::output::Table::new(vec!["SECRET"]); for k in all { if k != "__index__" { table.row(vec![k]); } } print!("{}", table.render()); Ok(0) } } }