()
| 548 | } |
| 549 | |
| 550 | private async requestDeviceUserCode(): Promise< |
| 551 | Result< |
| 552 | { |
| 553 | deviceAuthId: string; |
| 554 | userCode: string; |
| 555 | intervalSeconds: number; |
| 556 | expiresAtMs: number; |
| 557 | }, |
| 558 | string |
| 559 | > |
| 560 | > { |
| 561 | try { |
| 562 | const response = await fetch(CODEX_OAUTH_DEVICE_USERCODE_URL, { |
| 563 | method: "POST", |
| 564 | headers: { "Content-Type": "application/json" }, |
| 565 | body: JSON.stringify({ client_id: CODEX_OAUTH_CLIENT_ID }), |
| 566 | }); |
| 567 | |
| 568 | if (!response.ok) { |
| 569 | const errorText = await response.text().catch(() => ""); |
| 570 | const prefix = `Codex OAuth device auth request failed (${response.status})`; |
| 571 | return Err(errorText ? `${prefix}: ${errorText}` : prefix); |
| 572 | } |
| 573 | |
| 574 | const json = (await response.json()) as unknown; |
| 575 | if (!isPlainObject(json)) { |
| 576 | return Err("Codex OAuth device auth response returned an invalid JSON payload"); |
| 577 | } |
| 578 | |
| 579 | const deviceAuthId = typeof json.device_auth_id === "string" ? json.device_auth_id : null; |
| 580 | const userCode = typeof json.user_code === "string" ? json.user_code : null; |
| 581 | const interval = parseOptionalNumber(json.interval); |
| 582 | const expiresIn = parseOptionalNumber(json.expires_in); |
| 583 | |
| 584 | if (!deviceAuthId || !userCode) { |
| 585 | return Err("Codex OAuth device auth response missing required fields"); |
| 586 | } |
| 587 | |
| 588 | const intervalSeconds = interval !== null ? Math.max(1, Math.floor(interval)) : 5; |
| 589 | const expiresAtMs = |
| 590 | expiresIn !== null |
| 591 | ? Date.now() + Math.max(0, Math.floor(expiresIn * 1000)) |
| 592 | : Date.now() + DEFAULT_DEVICE_TIMEOUT_MS; |
| 593 | |
| 594 | return Ok({ deviceAuthId, userCode, intervalSeconds, expiresAtMs }); |
| 595 | } catch (error) { |
| 596 | const message = getErrorMessage(error); |
| 597 | return Err(`Codex OAuth device auth request failed: ${message}`); |
| 598 | } |
| 599 | } |
| 600 | |
| 601 | private async pollDeviceFlow(flowId: string): Promise<void> { |
| 602 | const flow = this.deviceFlows.get(flowId); |
no test coverage detected