@@ -44,32 +44,43 @@ struct UsageBucket {
4444}
4545
4646pub fn poll ( ) -> Result < UsageData , PollError > {
47- let mut creds = match read_credentials ( ) {
47+ let creds = match read_first_credentials ( ) {
4848 Some ( c) => c,
4949 None => {
5050 diagnose:: log ( "poll failed: no Claude credentials found" ) ;
5151 return Err ( PollError :: NoCredentials ) ;
5252 }
5353 } ;
5454
55- if is_token_expired ( creds. expires_at ) {
56- cli_refresh_token ( & creds. source ) ;
55+ let creds = refresh_or_fallback ( creds) ?;
5756
58- match read_credentials_from_source ( & creds. source ) {
59- Some ( refreshed) => creds = refreshed,
60- None => {
61- diagnose:: log ( "poll failed: credentials still unavailable after refresh attempt" ) ;
62- return Err ( PollError :: NoCredentials ) ;
63- }
57+ fetch_usage_with_fallback ( & creds. access_token )
58+ }
59+
60+ fn refresh_or_fallback ( mut creds : Credentials ) -> Result < Credentials , PollError > {
61+ loop {
62+ if !is_token_expired ( creds. expires_at ) {
63+ return Ok ( creds) ;
6464 }
6565
66- if is_token_expired ( creds. expires_at ) {
67- diagnose:: log ( "poll failed: token is still expired after refresh attempt" ) ;
68- return Err ( PollError :: TokenExpired ) ;
66+ let source = creds. source . clone ( ) ;
67+ cli_refresh_token ( & source) ;
68+
69+ match read_credentials_from_source ( & source) {
70+ Some ( refreshed) if !is_token_expired ( refreshed. expires_at ) => return Ok ( refreshed) ,
71+ Some ( _) => diagnose:: log ( format ! (
72+ "credentials from {source:?} still expired after refresh attempt"
73+ ) ) ,
74+ None => diagnose:: log ( format ! (
75+ "credentials from {source:?} unavailable after refresh attempt"
76+ ) ) ,
6977 }
70- }
7178
72- fetch_usage_with_fallback ( & creds. access_token )
79+ match read_next_credentials_after ( & source) {
80+ Some ( next) => creds = next,
81+ None => return Err ( PollError :: TokenExpired ) ,
82+ }
83+ }
7384}
7485
7586/// Invoke the Claude CLI with a minimal prompt to force its internal
@@ -245,7 +256,7 @@ fn build_agent() -> Result<ureq::Agent, PollError> {
245256
246257pub fn credential_watch_snapshot ( mode : CredentialWatchMode ) -> CredentialWatchSnapshot {
247258 let sources = match mode {
248- CredentialWatchMode :: ActiveSource => read_credentials ( )
259+ CredentialWatchMode :: ActiveSource => read_first_credentials ( )
249260 . map ( |creds| vec ! [ creds. source] )
250261 . unwrap_or_else ( all_known_credential_sources) ,
251262 CredentialWatchMode :: AllSources => all_known_credential_sources ( ) ,
@@ -506,20 +517,18 @@ enum CredentialSource {
506517 Wsl { distro : String } ,
507518}
508519
509- fn read_credentials ( ) -> Option < Credentials > {
510- let mut candidates = Vec :: new ( ) ;
511-
520+ fn read_first_credentials ( ) -> Option < Credentials > {
512521 if let Some ( creds) = read_windows_credentials ( ) {
513- candidates . push ( creds) ;
522+ return Some ( creds) ;
514523 }
515524
516525 for distro in list_wsl_distros ( ) {
517526 if let Some ( creds) = read_wsl_credentials ( & distro) {
518- candidates . push ( creds) ;
527+ return Some ( creds) ;
519528 }
520529 }
521530
522- choose_best_credentials ( candidates )
531+ None
523532}
524533
525534fn read_windows_credentials ( ) -> Option < Credentials > {
@@ -600,13 +609,30 @@ fn parse_credentials(content: &str, source: CredentialSource) -> Option<Credenti
600609 } )
601610}
602611
603- fn choose_best_credentials ( mut candidates : Vec < Credentials > ) -> Option < Credentials > {
604- if candidates. is_empty ( ) {
605- return None ;
612+ fn read_next_credentials_after ( source : & CredentialSource ) -> Option < Credentials > {
613+ match source {
614+ CredentialSource :: Windows ( _) => {
615+ for distro in list_wsl_distros ( ) {
616+ if let Some ( creds) = read_wsl_credentials ( & distro) {
617+ return Some ( creds) ;
618+ }
619+ }
620+ }
621+ CredentialSource :: Wsl { distro } => {
622+ let mut past_current = false ;
623+ for candidate_distro in list_wsl_distros ( ) {
624+ if !past_current {
625+ past_current = candidate_distro == * distro;
626+ continue ;
627+ }
628+ if let Some ( creds) = read_wsl_credentials ( & candidate_distro) {
629+ return Some ( creds) ;
630+ }
631+ }
632+ }
606633 }
607634
608- candidates. sort_by_key ( |creds| is_token_expired ( creds. expires_at ) ) ;
609- candidates. into_iter ( ) . next ( )
635+ None
610636}
611637
612638fn list_wsl_distros ( ) -> Vec < String > {
0 commit comments