use super::refresh_predicate::should_refresh;
use super::types::{ AccountQuota, SubprocessModel, SubprocessEffort };
use super::subprocess::{ resolve_model, effort_pre_args };
use super::fetch::{ read_token, parse_u64_from_str };
pub( crate ) fn reason_label( aq : &AccountQuota ) -> &str
{
if !aq.is_owned
{
"not owned"
}
else if aq.cached
{
"cached-expired"
}
else if aq.is_occupied_elsewhere
{
"occupied elsewhere"
}
else
{
aq.result.as_ref().err().map_or( "ok", String::as_str )
}
}
pub( crate ) fn apply_refresh(
accounts : &mut [ AccountQuota ],
credential_store : &std::path::Path,
claude_paths : Option< &crate::ClaudePaths >,
trace : bool,
imodel : SubprocessModel,
effort : SubprocessEffort,
solo : bool,
)
{
let now_secs = std::time::SystemTime::now()
.duration_since( std::time::UNIX_EPOCH )
.unwrap_or_default()
.as_secs();
for aq in accounts
{
if solo && !aq.is_current
{
if trace { eprintln!( "[trace] refresh {} solo-skip", aq.name ); }
continue;
}
let should_retry = should_refresh( aq, now_secs );
if trace
{
eprintln!( "[trace] refresh {} should_retry={} (reason: {})", aq.name, should_retry, reason_label( aq ) );
}
if !should_retry { continue; }
if trace { eprintln!( "[trace] refresh {} attempting token refresh", aq.name ); }
let model = resolve_model( aq, imodel );
let pre_args = effort_pre_args( &model, effort );
let Some( new_creds ) = crate::account::refresh_account_token(
&aq.name, credential_store, claude_paths, trace, "refresh", model, &pre_args,
)
else
{
if trace
{
eprintln!( "[trace] refresh {} refresh returned None — skipping retry", aq.name );
}
aq.result = Err( "refresh token expired".into() );
continue;
};
if let Some( exp_ms ) = crate::output::jwt_exp_ms( &new_creds )
{
aq.expires_at_ms = exp_ms;
}
else if let Some( exp_ms ) = parse_u64_from_str( &new_creds, "expiresAt" )
{
aq.expires_at_ms = exp_ms;
}
if trace { eprintln!( "[trace] refresh {} token refreshed, retrying quota fetch", aq.name ); }
let Ok( token ) = read_token( credential_store, &aq.name ) else { continue; };
match claude_quota::fetch_oauth_usage( &token )
{
Ok( retried ) =>
{
if trace { eprintln!( "[trace] refresh {} retry OK", aq.name ); }
let h5 = retried.five_hour.as_ref().map( |p| ( p.utilization, p.resets_at.as_deref() ) );
let d7 = retried.seven_day.as_ref().map( |p| ( p.utilization, p.resets_at.as_deref() ) );
let sn = retried.seven_day_sonnet.as_ref().map( |p| ( p.utilization, p.resets_at.as_deref() ) );
claude_profile_core::account::write_quota_cache( credential_store, &aq.name, h5, d7, sn );
aq.result = Ok( retried );
aq.cached = false;
aq.cache_age_secs = None;
if let Ok( acct ) = claude_quota::fetch_oauth_account( &token )
{
aq.account = Some( acct );
}
}
Err( e ) =>
{
if trace { eprintln!( "[trace] refresh {} retry Err({})", aq.name, e ); }
aq.result = Err( e.to_string() );
}
}
}
}
#[ cfg( test ) ]
#[ path = "refresh_tests.rs" ]
mod tests;