(
flowId: string,
opts?: { timeoutMs?: number }
)
| 265 | } |
| 266 | |
| 267 | async waitForDeviceFlow( |
| 268 | flowId: string, |
| 269 | opts?: { timeoutMs?: number } |
| 270 | ): Promise<Result<void, string>> { |
| 271 | const flow = this.deviceFlows.get(flowId); |
| 272 | if (!flow) { |
| 273 | return Err("OAuth flow not found"); |
| 274 | } |
| 275 | |
| 276 | if (!flow.pollingStarted) { |
| 277 | flow.pollingStarted = true; |
| 278 | this.pollDeviceFlow(flowId).catch((error) => { |
| 279 | // The polling loop is responsible for resolving the flow; if we reach |
| 280 | // here something unexpected happened. |
| 281 | const message = getErrorMessage(error); |
| 282 | log.warn(`[Codex OAuth] Device polling crashed (flowId=${flowId}): ${message}`); |
| 283 | void this.finishDeviceFlow(flowId, Err(`Device polling crashed: ${message}`)); |
| 284 | }); |
| 285 | } |
| 286 | |
| 287 | const timeoutMs = opts?.timeoutMs ?? DEFAULT_DEVICE_TIMEOUT_MS; |
| 288 | |
| 289 | let timeoutHandle: ReturnType<typeof setTimeout> | null = null; |
| 290 | const timeoutPromise = new Promise<Result<void, string>>((resolve) => { |
| 291 | timeoutHandle = setTimeout(() => { |
| 292 | resolve(Err("Timed out waiting for device authorization")); |
| 293 | }, timeoutMs); |
| 294 | }); |
| 295 | |
| 296 | const result = await Promise.race([flow.resultPromise, timeoutPromise]); |
| 297 | |
| 298 | if (timeoutHandle !== null) { |
| 299 | clearTimeout(timeoutHandle); |
| 300 | } |
| 301 | |
| 302 | if (!result.success) { |
| 303 | // Ensure polling is cancelled on timeout/errors. |
| 304 | void this.finishDeviceFlow(flowId, result); |
| 305 | } |
| 306 | |
| 307 | return result; |
| 308 | } |
| 309 | |
| 310 | async cancelDeviceFlow(flowId: string): Promise<void> { |
| 311 | const flow = this.deviceFlows.get(flowId); |
no test coverage detected