(flowId: string)
| 599 | } |
| 600 | |
| 601 | private async pollDeviceFlow(flowId: string): Promise<void> { |
| 602 | const flow = this.deviceFlows.get(flowId); |
| 603 | if (!flow || flow.settled) { |
| 604 | return; |
| 605 | } |
| 606 | |
| 607 | const intervalSeconds = flow.intervalSeconds; |
| 608 | |
| 609 | while (Date.now() < flow.expiresAtMs) { |
| 610 | if (flow.abortController.signal.aborted) { |
| 611 | await this.finishDeviceFlow(flowId, Err("OAuth flow cancelled")); |
| 612 | return; |
| 613 | } |
| 614 | |
| 615 | const attempt = await this.pollDeviceTokenOnce(flow); |
| 616 | if (attempt.kind === "success") { |
| 617 | const persistResult = await this.persistAuth(attempt.auth); |
| 618 | if (!persistResult.success) { |
| 619 | await this.finishDeviceFlow(flowId, Err(persistResult.error)); |
| 620 | return; |
| 621 | } |
| 622 | |
| 623 | log.debug(`[Codex OAuth] Device authorization completed (flowId=${flowId})`); |
| 624 | this.windowService?.focusMainWindow(); |
| 625 | await this.finishDeviceFlow(flowId, Ok(undefined)); |
| 626 | return; |
| 627 | } |
| 628 | |
| 629 | if (attempt.kind === "fatal") { |
| 630 | await this.finishDeviceFlow(flowId, Err(attempt.message)); |
| 631 | return; |
| 632 | } |
| 633 | |
| 634 | try { |
| 635 | // OpenCode guide: intervalSeconds * 1000 + 3000 |
| 636 | await sleepWithAbort(intervalSeconds * 1000 + 3000, flow.abortController.signal); |
| 637 | } catch { |
| 638 | // Abort is handled via cancelDeviceFlow()/finishDeviceFlow(). |
| 639 | return; |
| 640 | } |
| 641 | } |
| 642 | |
| 643 | await this.finishDeviceFlow(flowId, Err("Device code expired")); |
| 644 | } |
| 645 | |
| 646 | private async pollDeviceTokenOnce( |
| 647 | flow: DeviceFlow |
no test coverage detected