Skip to content

fix(cli): route the copilot device flow through the github backend key and the real poll route (#14298) - #14300

Merged
diegosouzapw merged 1 commit into
diegosouzapw:release/v3.8.51from
xiechimon:fix/cli-copilot-device-flow
Sep 22, 2026
Merged

diegosouzapw merged 1 commit into
diegosouzapw:release/v3.8.51from
xiechimon:fix/cli-copilot-device-flow

Conversation

@xiechimon

Copy link
Copy Markdown
Contributor

What

Fixes the broken CLI device-flow onboarding for GitHub Copilot (issue #14298, findings 1-3).

Three changes in bin/cli/commands/oauth.mjs, all inside the CLI device-flow path:

  1. Add the copilot -> github entry to BACKEND_OAUTH_KEY. The server registers the GitHub device flow under the key github (src/lib/oauth/providers/index.ts), so oauth start --provider copilot used to post /api/oauth/copilot/device-code and fail with an unknown-provider error. Same class of mismap as the claude-code -> claude alias fixed in Claude Code OAuth device flow (oauth start --provider claude-code) fails with 401 — internally mapped to unrelated command-code provider instead of Anthropic #9474.

  2. Poll the route that actually exists. runDeviceFlow used to poll GET /api/providers/{key}/auth/status?state=... and then POST /api/providers/{key}/auth/apply. Neither route exists on the server, so the CLI looped until its timeout even after the user had authorized (GitHub already showed the device as connected). It now polls POST /api/oauth/{key}/poll with { deviceCode }, which is the same route the dashboard uses (src/shared/components/OAuthModal.tsx::pollDeviceCodeOnce). That route persists the connection server-side on success, so the separate apply step is not needed. codeVerifier is forwarded when the device-code response carries one, matching the server's oauthPollSchema.

  3. Stop keying the poll on start.state. GET /api/oauth/{key}/device-code never returns a state field, so the old ?state= query could never match a pending flow. The poll now uses the device code from the response (deviceCode/device_code), and exits with a clear message if the server returned none.

Also mirrors the dashboard's handling of poll results: slow_down backs the interval off by 5s per the device-flow spec, and a non-pending error reports errorDescription and exits 1 instead of looping silently until timeout.

Why

Without this, omniroute oauth start --provider copilot could not complete at all from the CLI on any platform (the reporter hit it on Windows; the mismap and the dead poll route are platform-independent). The direct API sequence the reporter found (GET /api/oauth/github/device-code -> authorize -> POST /api/oauth/github/poll) is exactly what the CLI now does.

How tested

New regression test tests/unit/14298-cli-copilot-device-flow.test.ts drives runOAuthStart against a fake HTTP server that only mounts the routes the real server exposes:

  • pending -> success poll sequence: flow completes, device-code is requested on the resolved github key, no request targets /copilot/ or /api/providers/, poll carries deviceCode + codeVerifier;
  • fatal poll error: exits 1 promptly with the upstream reason instead of burning the timeout.

Red/green verified: with the base oauth.mjs restored, both tests fail (Failed to start device flow: 404 / timeout loop); with the fix, both pass.

Adjacent regression: cli-oauth-commands, 9474-claude-code-oauth-mismap, probe-12413-antigravity-oauth-redirect-hint, grok-cli-oauth, grok-cli-device-route, oauth-device-code-endpoints all pass (42 tests, 0 failures). prettier --check clean on both files, eslint clean, typecheck:core exit 0.

Scope note

#14298 lists seven findings. This PR covers 1-3 (the CLI device-flow path). Findings 4-7 (gemini registry entry, codex/kiro flows, github/* catalog mapping, baseline binary) touch different code paths and are left to separate changes.

Fixes #14298 (findings 1-3)

Copilot AI lite review requested due to automatic review settings September 21, 2026 08:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@diegosouzapw

Copy link
Copy Markdown
Owner

Excellent repro-driven fix — the fake-server test proving the old code would 404-loop even after a successful user authorization is exactly the right way to nail this down. Since issue #14298 also covers the Gemini/codex/kiro and Windows-baseline findings that this PR doesn't touch, I'll link it with Refs #14298 rather than closing the issue outright — let us know if you'd like to tackle the remaining findings in a follow-up. Merge-ready as-is.

@xiechimon

Copy link
Copy Markdown
Contributor Author

I will take findings 4 and 5 as the follow-up against release/v3.8.51.

For finding 5, the plan is: give codex a callback-server PKCE path in the CLI (GET start-callback-server, then poll POST poll-callback, the same routes the dashboard uses) instead of the device label that 404s, and fix kiro social to GET social-authorize and then poll POST social-exchange with deviceCode + provider, replacing the POST/GET pair that gets 405 today. A fake-server test that only mounts the real routes will pin both, and it fails on the current code.

For finding 4 (gemini), the CLI entry cannot work until either the server registers a gemini provider or the entry is dropped from the list. Tell me which direction you prefer and I will fold it into the same PR, or leave it out if you want to decide later.

Findings 6 and 7 are upstream/entitlement as you triaged, so I will not touch them.

@diegosouzapw

Copy link
Copy Markdown
Owner

Thanks @xiechimon — merging via the release merge-train. Validated in local merge-train (mt-train10c) on the devbox @ train tip 4d841aa1c740bbaa03868dc0a403c62099a99a42 with the 72 sibling PRs of this batch: typecheck:core, file-size, complexity, cognitive-complexity, changelog-integrity green; changed-area node:test 831/831 (0 failing) and vitest 480/482 — the two reds are autoCombo/provider-family-combos.test.ts timing out at 20s, which reproduces on the PURE release tip under the full vitest suite (and is already tracked by the Release-Green issue #13866), so it is inherited, not this batch's. Merged --admin per merge-gates §3/§4/§7.

@diegosouzapw
diegosouzapw merged commit 844e35c into diegosouzapw:release/v3.8.51 Sep 22, 2026
7 of 16 checks passed
xiechimon added a commit to xiechimon/OmniRoute that referenced this pull request Sep 22, 2026
…kiro social routes

- codex is authorization_code_pkce on the server, so the device-code
  action rejects it (404). Drive it through start-callback-server +
  poll-callback like the dashboard does, with a manual browser-PKCE
  fallback when the callback server is unavailable.
- kiro social-authorize is GET-only and social-exchange is POST-only;
  the CLI had both methods inverted and polled by a state the response
  never carries. Use GET social-authorize (provider query) and POST
  social-exchange {deviceCode, provider} with slow_down backoff,
  mirroring KiroSocialOAuthModal.

Refs diegosouzapw#14298 findings 4-5 (gemini registry entry left for a maintainer
call, see PR discussion on diegosouzapw#14300).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants