@@ -19,6 +19,12 @@ const log = getLogger(import.meta.url);
1919const BROWSER_TIMEOUT_MS = 3000 ;
2020const SLOW_DOWN_INCREMENT = 5 ; // RFC 8628 §3.5: increase interval by 5s on slow_down
2121
22+ // Adaptive polling: first 30 seconds (while user is actively in browser)
23+ // poll every 2s for snappy UX. After that, fall back to API-provided interval.
24+ // If API returns slow_down, we respect it immediately and disable adaptive mode.
25+ const ADAPTIVE_WINDOW_MS = 30_000 ;
26+ const ADAPTIVE_INTERVAL_SEC = 2 ;
27+
2228/** Result from a completed device flow. */
2329export interface DeviceFlowResult {
2430 accessToken : string ;
@@ -133,8 +139,13 @@ export async function runDeviceFlow(
133139 }
134140
135141 // 4. Poll for authorization
136- let interval = deviceAuth . interval || DEVICE_FLOW_DEFAULT_INTERVAL ;
137- const deadline = Date . now ( ) + deviceAuth . expires_in * 1000 ;
142+ // Adaptive interval: fast polling first 30s, fall back to API interval after.
143+ // API-provided interval is the floor once adaptive window ends OR on slow_down.
144+ const apiInterval = deviceAuth . interval || DEVICE_FLOW_DEFAULT_INTERVAL ;
145+ let interval = Math . min ( apiInterval , ADAPTIVE_INTERVAL_SEC ) ;
146+ let adaptiveMode = true ;
147+ const pollStart = Date . now ( ) ;
148+ const deadline = pollStart + deviceAuth . expires_in * 1000 ;
138149
139150 // Spinner: ora for CLI, terminal.dim for REPL (stdin is raw)
140151 const isRepl = process . stdin . isRaw ;
@@ -147,6 +158,13 @@ export async function runDeviceFlow(
147158
148159 try {
149160 while ( Date . now ( ) < deadline ) {
161+ // Adaptive window expired → switch to API-provided interval
162+ if ( adaptiveMode && Date . now ( ) - pollStart >= ADAPTIVE_WINDOW_MS ) {
163+ adaptiveMode = false ;
164+ interval = apiInterval ;
165+ log . debug ( "Adaptive polling window ended" , { new_interval : String ( interval ) } ) ;
166+ }
167+
150168 // Cancellable sleep (AbortSignal from REPL Ctrl+C or CLI SIGINT)
151169 try {
152170 await sleep ( interval * 1000 , signal ) ;
@@ -175,7 +193,10 @@ export async function runDeviceFlow(
175193 break ;
176194 }
177195 case "slow_down" : {
178- interval += SLOW_DOWN_INCREMENT ;
196+ // Server throttling — disable adaptive mode and respect RFC 8628 §3.5:
197+ // "the interval MUST be increased by 5 seconds for all subsequent requests"
198+ adaptiveMode = false ;
199+ interval = Math . max ( interval , apiInterval ) + SLOW_DOWN_INCREMENT ;
179200 log . debug ( "Polling slowed down" , { new_interval : String ( interval ) } ) ;
180201 break ;
181202 }
0 commit comments