From b7866e30673e98713ef23cf4de87897951421c56 Mon Sep 17 00:00:00 2001 From: Rowboat Developer Date: Fri, 17 Apr 2026 09:56:32 -0500 Subject: [PATCH] Feat: Implement real GitHub Copilot Device Flow authentication UI - Add IPC handlers for github-copilot:authenticate, isAuthenticated, disconnect - Add schema definitions for new IPC handlers in shared/ipc.ts - Implement Device Flow authentication button with real flow - Show device code and verification URL to user when authenticating - Automatically open GitHub verification page in browser - Auto-load models after successful authentication - Update GitHub Copilot model list (gpt-4o, gpt-4-turbo, gpt-3.5-turbo, claude-3.5-sonnet) - Display loading state and error messages in UI - Properly integrate with existing auth service This fixes the broken authentication flow where the button didn't do anything. --- apps/x/apps/main/src/ipc.ts | 41 +++++++++++++ .../src/components/settings-dialog.tsx | 59 +++++++++++++++++-- .../core/src/auth/github-copilot-models.ts | 13 ++-- apps/x/packages/core/src/models/models-dev.ts | 6 +- apps/x/packages/shared/src/ipc.ts | 33 +++++++++++ 5 files changed, 135 insertions(+), 17 deletions(-) diff --git a/apps/x/apps/main/src/ipc.ts b/apps/x/apps/main/src/ipc.ts index 74388f65..fee36663 100644 --- a/apps/x/apps/main/src/ipc.ts +++ b/apps/x/apps/main/src/ipc.ts @@ -29,6 +29,7 @@ import { isSignedIn } from '@x/core/dist/account/account.js'; import { listGatewayModels } from '@x/core/dist/models/gateway.js'; import type { IModelConfigRepo } from '@x/core/dist/models/repo.js'; import type { IOAuthRepo } from '@x/core/dist/auth/repo.js'; +import { startGitHubCopilotAuthentication, getGitHubCopilotAccessToken, isGitHubCopilotAuthenticated, disconnectGitHubCopilot } from '@x/core/dist/auth/github-copilot-auth.js'; import { IGranolaConfigRepo } from '@x/core/dist/knowledge/granola/repo.js'; import { triggerSync as triggerGranolaSync } from '@x/core/dist/knowledge/granola/sync.js'; import { ISlackConfigRepo } from '@x/core/dist/slack/repo.js'; @@ -488,6 +489,46 @@ export function setupIpcHandlers() { const config = await repo.getClientFacingConfig(); return { config }; }, + 'github-copilot:authenticate': async () => { + try { + const authInfo = await startGitHubCopilotAuthentication(); + // Don't await the token promise - it will complete in the background + // Just return the device code info immediately + return { + success: true, + userCode: authInfo.userCode, + verificationUri: authInfo.verificationUri, + expiresIn: authInfo.expiresIn, + }; + } catch (error) { + console.error('[GitHub Copilot] Authentication error:', error); + return { + success: false, + error: error instanceof Error ? error.message : 'Authentication failed', + }; + } + }, + 'github-copilot:isAuthenticated': async () => { + try { + const authenticated = await isGitHubCopilotAuthenticated(); + return { authenticated }; + } catch (error) { + console.error('[GitHub Copilot] Error checking authentication:', error); + return { authenticated: false }; + } + }, + 'github-copilot:disconnect': async () => { + try { + await disconnectGitHubCopilot(); + return { success: true }; + } catch (error) { + console.error('[GitHub Copilot] Disconnect error:', error); + return { + success: false, + error: error instanceof Error ? error.message : 'Disconnect failed', + }; + } + }, 'account:getRowboat': async () => { const signedIn = await isSignedIn(); if (!signedIn) { diff --git a/apps/x/apps/renderer/src/components/settings-dialog.tsx b/apps/x/apps/renderer/src/components/settings-dialog.tsx index e0e4ba6c..6ff990b7 100644 --- a/apps/x/apps/renderer/src/components/settings-dialog.tsx +++ b/apps/x/apps/renderer/src/components/settings-dialog.tsx @@ -691,17 +691,64 @@ function ModelSettings({ dialogOpen }: { dialogOpen: boolean }) { {isGitHubCopilot && (
Authentication -

GitHub Copilot uses Device Flow OAuth for authentication. Click the button below to authenticate with your GitHub account.

+

GitHub Copilot uses Device Flow OAuth for authentication. Click the button to authenticate with your GitHub account.

)} diff --git a/apps/x/packages/core/src/auth/github-copilot-models.ts b/apps/x/packages/core/src/auth/github-copilot-models.ts index 0da39ed0..756584bb 100644 --- a/apps/x/packages/core/src/auth/github-copilot-models.ts +++ b/apps/x/packages/core/src/auth/github-copilot-models.ts @@ -14,14 +14,13 @@ import { LlmProvider } from '@x/shared/dist/models.js'; const GITHUB_COPILOT_API_BASE = 'https://models.github.com/api/openai/'; // List of models available through GitHub Copilot -// Based on GitHub Copilot documentation +// Based on GitHub Copilot API documentation +// https://docs.github.com/en/copilot/using-github-copilot/asking-github-copilot-questions export const GITHUB_COPILOT_MODELS = [ - 'gpt-4o', - 'gpt-4-turbo', - 'gpt-4', - 'gpt-3.5-turbo', - 'claude-3.5-sonnet', // If available in student plan - 'claude-3-opus', // If available in student plan + 'gpt-4o', // GPT-4 Optimized (recommended) + 'gpt-4-turbo', // GPT-4 Turbo + 'gpt-3.5-turbo', // GPT-3.5 Turbo (fastest) + 'claude-3.5-sonnet', // Claude 3.5 Sonnet (if available in plan) ] as const; export type GitHubCopilotModel = typeof GITHUB_COPILOT_MODELS[number]; diff --git a/apps/x/packages/core/src/models/models-dev.ts b/apps/x/packages/core/src/models/models-dev.ts index ca341edf..5036aeb0 100644 --- a/apps/x/packages/core/src/models/models-dev.ts +++ b/apps/x/packages/core/src/models/models-dev.ts @@ -227,12 +227,10 @@ export async function listOnboardingModels(): Promise<{ providers: ProviderSumma id: "github-copilot", name: "GitHub Copilot Student", models: [ - { id: "gpt-4o", name: "GPT-4o" }, + { id: "gpt-4o", name: "GPT-4o (Recommended)" }, { id: "gpt-4-turbo", name: "GPT-4 Turbo" }, - { id: "gpt-4", name: "GPT-4" }, - { id: "gpt-3.5-turbo", name: "GPT-3.5 Turbo" }, + { id: "gpt-3.5-turbo", name: "GPT-3.5 Turbo (Fastest)" }, { id: "claude-3.5-sonnet", name: "Claude 3.5 Sonnet" }, - { id: "claude-3-opus", name: "Claude 3 Opus" }, ], }); diff --git a/apps/x/packages/shared/src/ipc.ts b/apps/x/packages/shared/src/ipc.ts index 645e41ff..c509023d 100644 --- a/apps/x/packages/shared/src/ipc.ts +++ b/apps/x/packages/shared/src/ipc.ts @@ -257,6 +257,39 @@ const ipcSchemas = { })), }), }, + 'github-copilot:authenticate': { + req: z.null(), + res: z.union([ + z.object({ + success: z.literal(true), + userCode: z.string(), + verificationUri: z.string(), + expiresIn: z.number(), + }), + z.object({ + success: z.literal(false), + error: z.string(), + }), + ]), + }, + 'github-copilot:isAuthenticated': { + req: z.null(), + res: z.object({ + authenticated: z.boolean(), + }), + }, + 'github-copilot:disconnect': { + req: z.null(), + res: z.union([ + z.object({ + success: z.literal(true), + }), + z.object({ + success: z.literal(false), + error: z.string(), + }), + ]), + }, 'account:getRowboat': { req: z.null(), res: z.object({