use std::process::Command;
use crate::error::{AppError, Result};
const SERVICE: &str = "Claude Code-credentials";
fn account() -> String {
std::env::var("USER").unwrap_or_default()
}
pub fn read_raw() -> Result<Option<String>> {
let mut cmd = Command::new("/usr/bin/security");
cmd.args(["find-generic-password", "-s", SERVICE, "-w"]);
let acct = account();
if !acct.is_empty() {
cmd.args(["-a", &acct]);
}
let out = cmd
.output()
.map_err(|e| AppError::Other(format!("could not run `security`: {e}")))?;
if !out.status.success() {
return Ok(None);
}
let value = String::from_utf8(out.stdout)
.map_err(|e| AppError::Other(format!("Keychain value was not UTF-8: {e}")))?;
let value = value.trim_end_matches('\n').to_string();
if value.is_empty() {
Ok(None)
} else {
Ok(Some(value))
}
}
pub fn write_raw(json: &str) -> Result<()> {
let status = Command::new("/usr/bin/security")
.args([
"add-generic-password",
"-U",
"-s",
SERVICE,
"-a",
&account(),
"-w",
json,
])
.status()
.map_err(|e| AppError::Other(format!("could not run `security`: {e}")))?;
if status.success() {
Ok(())
} else {
Err(AppError::Other(
"failed to update Claude credentials in the macOS Keychain".into(),
))
}
}