import { OAuthServer } from './oauthServer.js'; import { exec } from 'child_process'; import { promisify } from 'util'; const execAsync = promisify(exec); /** * Open a URL in the user's default browser */ async function openBrowser(url: string): Promise { const platform = process.platform; try { if (platform === 'darwin') { // macOS await execAsync(`open "${url}"`); } else if (platform === 'win32') { // Windows await execAsync(`start "" "${url}"`); } else { // Linux/Unix await execAsync(`xdg-open "${url}"`); } } catch (error) { // Silently fail - browser will not open automatically // User can still authorize via the OAuth page if they have it open } } export interface OAuthFlowResult { code?: string; error?: string; port: number; } /** * Run complete OAuth flow with built-in server and browser opening * * @param authUrl Base authorization URL (without redirect_uri) * @param authUrlBuilder Function that takes port and returns complete auth URL * @returns Authorization code or error */ export async function runOAuthFlow( authUrlBuilder: (redirectUri: string) => string ): Promise { const server = new OAuthServer(); try { // Start server and get available port const port = await server.start({ startPort: 8080, endPort: 8090, timeout: 300000, // 5 minutes }); // Build redirect URI const redirectUri = `http://localhost:${port}/callback`; // Generate authorization URL const authUrl = authUrlBuilder(redirectUri); // Open browser await openBrowser(authUrl); // Wait for callback const result = await server.waitForCallback(); return { code: result.code, error: result.error, port, }; } catch (error: any) { throw new Error(`OAuth flow failed: ${error.message}`); } finally { // Ensure server is stopped await server.stop(); } }