use trusty_mpm::core::oauth_token::{self, TokenStatus};
fn read_token_from_reader(mut r: impl std::io::Read) -> anyhow::Result<String> {
let mut buf = String::new();
r.read_to_string(&mut buf)?;
let trimmed = buf.trim();
if trimmed.is_empty() {
anyhow::bail!(
"no token provided on stdin — pipe the token (e.g. `claude setup-token | tm auth \
set-token`) or pass --token <val>"
);
}
Ok(trimmed.to_string())
}
pub(crate) fn set_token(token: Option<String>, _stdin: bool) -> anyhow::Result<()> {
let raw = match token {
Some(t) => t,
None => read_token_from_reader(std::io::stdin())?,
};
let path = oauth_token::set_token(&raw)?;
println!("tm auth: token stored at {} (mode 0600)", path.display());
Ok(())
}
pub(crate) fn clear_token() -> anyhow::Result<()> {
let path = oauth_token::clear_token()?;
println!("tm auth: token cleared ({})", path.display());
Ok(())
}
fn format_status_lines(status: &TokenStatus) -> Vec<String> {
let mut lines = Vec::new();
lines.push(format!(
"stored token: {}",
if status.stored_token_present {
"present"
} else {
"absent"
}
));
if let Some(p) = &status.token_path {
lines.push(format!(" path: {}", p.display()));
}
lines.push(format!(
"{}: {}",
oauth_token::OAUTH_TOKEN_ENV_VAR,
if status.env_var_set { "set" } else { "not set" }
));
lines.push(format!(
"ANTHROPIC_API_KEY: {}",
if status.anthropic_api_key_set {
"set"
} else {
"not set"
}
));
lines.push(format!(
"credentials file: {}",
if status.credentials_file_present {
"present"
} else {
"absent (this is normal on macOS, which uses the Keychain instead)"
}
));
if let Some(p) = &status.credentials_path {
lines.push(format!(" path: {}", p.display()));
}
lines
}
pub(crate) fn status() -> anyhow::Result<()> {
println!("tm auth status");
for line in format_status_lines(&oauth_token::compute_status()) {
println!(" {line}");
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
#[test]
fn read_token_from_reader_trims_and_rejects_blank() {
let token = read_token_from_reader(" sk-ant-oat01-fake-token\n".as_bytes()).unwrap();
assert_eq!(token, "sk-ant-oat01-fake-token");
let err = read_token_from_reader(" \n".as_bytes());
assert!(err.is_err(), "blank stdin must be rejected");
}
#[test]
fn status_lines_never_contain_a_token_value() {
let status = TokenStatus {
stored_token_present: true,
token_path: Some(PathBuf::from(
"/home/bob/.trusty-tools/trusty-mpm/claude-code-oauth.token",
)),
env_var_set: true,
anthropic_api_key_set: false,
credentials_file_present: true,
credentials_path: Some(PathBuf::from("/home/bob/.claude/.credentials.json")),
};
let lines = format_status_lines(&status);
let rendered = lines.join("\n");
assert!(
!rendered.contains("sk-ant-oat01-fake-token"),
"status output must never contain a token value: {rendered}"
);
assert!(rendered.contains("present"));
assert!(rendered.contains("claude-code-oauth.token"));
}
#[test]
fn status_lines_report_every_signal() {
let status = TokenStatus {
stored_token_present: false,
token_path: None,
env_var_set: false,
anthropic_api_key_set: false,
credentials_file_present: false,
credentials_path: None,
};
let rendered = format_status_lines(&status).join("\n");
assert!(rendered.contains("stored token: absent"));
assert!(rendered.contains("CLAUDE_CODE_OAUTH_TOKEN: not set"));
assert!(rendered.contains("ANTHROPIC_API_KEY: not set"));
assert!(rendered.contains("credentials file: absent"));
}
}