@@ -6,45 +6,68 @@ import { ExitCode } from "../errors/codes";
66// OAuth config — endpoints TBD pending MiniMax OAuth documentation
77const TOKEN_URL = "https://api.minimax.io/v1/oauth/token" ;
88
9+ const MAX_REFRESH_RETRIES = 2 ;
10+ const RETRY_DELAY_MS = 500 ;
11+
912export async function refreshAccessToken (
1013 refreshToken : string ,
1114) : Promise < OAuthTokens > {
12- let res : Response ;
13- try {
14- res = await fetch ( TOKEN_URL , {
15- method : "POST" ,
16- headers : { "Content-Type" : "application/x-www-form-urlencoded" } ,
17- body : new URLSearchParams ( {
18- grant_type : "refresh_token" ,
19- refresh_token : refreshToken ,
20- } ) ,
21- signal : AbortSignal . timeout ( 10_000 ) ,
22- } ) ;
23- } catch ( err ) {
24- const isTimeout =
25- err instanceof Error &&
26- ( err . name === "AbortError" ||
27- err . name === "TimeoutError" ||
28- err . message . includes ( "timed out" ) ) ;
29- throw new CLIError (
30- isTimeout
31- ? "Token refresh timed out — auth server did not respond within 10 s."
32- : `Token refresh failed: ${ err instanceof Error ? err . message : String ( err ) } ` ,
33- ExitCode . AUTH ,
34- "Check your network connection.\nRe-authenticate: mmx auth login" ,
35- ) ;
36- }
15+ let lastErr : Error | null = null ;
16+
17+ for ( let attempt = 0 ; attempt <= MAX_REFRESH_RETRIES ; attempt ++ ) {
18+ if ( attempt > 0 ) {
19+ // Exponential backoff before retry
20+ await new Promise ( r => setTimeout ( r , RETRY_DELAY_MS * attempt ) ) ;
21+ }
22+
23+ let res : Response ;
24+ try {
25+ res = await fetch ( TOKEN_URL , {
26+ method : "POST" ,
27+ headers : { "Content-Type" : "application/x-www-form-urlencoded" } ,
28+ body : new URLSearchParams ( {
29+ grant_type : "refresh_token" ,
30+ refresh_token : refreshToken ,
31+ } ) ,
32+ signal : AbortSignal . timeout ( 10_000 ) ,
33+ } ) ;
34+ } catch ( err ) {
35+ const isTimeout =
36+ err instanceof Error &&
37+ ( err . name === "AbortError" ||
38+ err . name === "TimeoutError" ||
39+ err . message . includes ( "timed out" ) ) ;
40+ lastErr = new Error (
41+ isTimeout
42+ ? "Token refresh timed out — auth server did not respond within 10 s."
43+ : `Token refresh failed: ${ err instanceof Error ? err . message : String ( err ) } ` ,
44+ ) ;
45+ continue ; // retry
46+ }
47+
48+ if ( ! res . ok ) {
49+ // 4xx errors won't recover with retry
50+ if ( res . status >= 400 && res . status < 500 ) {
51+ throw new CLIError (
52+ "OAuth session expired and could not be refreshed." ,
53+ ExitCode . AUTH ,
54+ "Re-authenticate: mmx auth login" ,
55+ ) ;
56+ }
57+ lastErr = new Error ( `Token refresh failed: HTTP ${ res . status } ` ) ;
58+ continue ; // retry 5xx errors
59+ }
3760
38- if ( ! res . ok ) {
39- throw new CLIError (
40- "OAuth session expired and could not be refreshed." ,
41- ExitCode . AUTH ,
42- "Re-authenticate: mmx auth login" ,
43- ) ;
61+ const data = ( await res . json ( ) ) as OAuthTokens ;
62+ return data ;
4463 }
4564
46- const data = ( await res . json ( ) ) as OAuthTokens ;
47- return data ;
65+ // All retries exhausted
66+ throw new CLIError (
67+ `Token refresh failed after ${ MAX_REFRESH_RETRIES + 1 } attempts: ${ lastErr ?. message } ` ,
68+ ExitCode . AUTH ,
69+ "Check your network connection.\nRe-authenticate: mmx auth login" ,
70+ ) ;
4871}
4972
5073export async function ensureFreshToken ( creds : CredentialFile ) : Promise < string > {
0 commit comments