use suno_core::{ClerkAuth, TOKEN_EXPIRY_WARN_DAYS, TokenExpiry};
use crate::cli::run;
const DAY_SECS: i64 = 86_400;
pub fn token_expiry_message(label: &str, expiry: TokenExpiry) -> Option<String> {
let remedy = format!(
"Get a fresh __client cookie from your browser and update it: \
set SUNO_TOKEN (or --token), or update the token for [accounts.{label}] in your config."
);
match expiry {
TokenExpiry::Fresh | TokenExpiry::Unknown => None,
TokenExpiry::Expiring { days } => Some(format!(
"warning: the token for account '{label}' expires in {days} day(s). {remedy}"
)),
TokenExpiry::Expired => Some(format!(
"warning: the token for account '{label}' has expired. {remedy}"
)),
}
}
pub fn warn_token_expiry(label: &str, auth: &ClerkAuth, verbosity: i8) {
if verbosity < -1 {
return;
}
let now = run::now_secs() as i64;
let window = TOKEN_EXPIRY_WARN_DAYS * DAY_SECS;
if let Some(message) = token_expiry_message(label, auth.token_expiry(now, window)) {
eprintln!("{message}");
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fresh_and_unknown_stay_silent() {
assert!(token_expiry_message("alice", TokenExpiry::Fresh).is_none());
assert!(token_expiry_message("alice", TokenExpiry::Unknown).is_none());
}
#[test]
fn expiring_carries_label_and_day_count() {
let message = token_expiry_message("alice", TokenExpiry::Expiring { days: 3 }).unwrap();
assert!(message.contains("alice"));
assert!(message.contains('3'));
assert!(!message.contains("auth refresh"));
}
#[test]
fn expired_carries_label() {
let message = token_expiry_message("bob", TokenExpiry::Expired).unwrap();
assert!(message.contains("bob"));
assert!(message.contains("expired"));
}
#[test]
fn message_never_leaks_a_token() {
for expiry in [TokenExpiry::Expiring { days: 1 }, TokenExpiry::Expired] {
let message = token_expiry_message("acct", expiry).unwrap();
assert!(
!message.contains("eyJ"),
"message leaked a JWT/cookie: {message}"
);
}
}
}