//! Common helpers shared by all commands. use std::io::{self, Write}; use std::path::PathBuf; use std::time::{SystemTime, UNIX_EPOCH}; use anyhow::{anyhow, Context, Result}; use levcs_core::Repository; use levcs_identity::keychain::Keychain; use levcs_identity::keys::SecretKey; pub fn now_micros() -> i64 { SystemTime::now() .duration_since(UNIX_EPOCH) .map(|d| d.as_micros() as i64) .unwrap_or(0) } pub fn open_repo() -> Result { let cwd = std::env::current_dir()?; Ok(Repository::discover(&cwd)?) } pub fn keychain_path() -> PathBuf { Keychain::default_path() } pub fn load_keychain() -> Result { Ok(Keychain::load_or_default(&keychain_path())?) } pub fn save_keychain(kc: &Keychain) -> Result<()> { Ok(kc.save(&keychain_path())?) } pub fn read_passphrase(prompt: &str) -> Result { eprint!("{prompt}"); io::stderr().flush().ok(); // Best-effort no-echo: rely on terminal raw mode if available; otherwise // fall back to plain readline. This is intentionally simple to avoid // pulling another crate; users on headless CI should pass keys via // unencrypted slots or a future agent. let mut buf = String::new(); io::stdin().read_line(&mut buf)?; if buf.ends_with('\n') { buf.pop(); if buf.ends_with('\r') { buf.pop(); } } Ok(buf) } /// Resolve a key label and load the secret. If `label` is None, default to /// `"personal"`, then to the only key in the chain if there is exactly one. pub fn load_secret(label: Option<&str>) -> Result<(String, SecretKey)> { let kc = load_keychain()?; let chosen = match label { Some(l) => l.to_string(), None => { if kc.entry("personal").is_some() { "personal".into() } else if kc.keys.len() == 1 { kc.keys[0].label.clone() } else if kc.keys.is_empty() { return Err(anyhow!( "no keys in keychain at {:?}; run `levcs key generate