import { execSync } from 'node:child_process' import { writeFileSync, unlinkSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' import QRCode from 'qrcode' import { info } from './stderr' export interface QRDisplayOptions { platform: string brandColor: string scanInstruction: string } export async function createQRHtmlFile(url: string, options: QRDisplayOptions): Promise { try { const svgString = await QRCode.toString(url, { type: 'svg', margin: 2 }) const html = ` ${options.platform} QR Login

${options.platform} Login

${options.scanInstruction}

${svgString}
` const htmlPath = join(tmpdir(), `${options.platform.toLowerCase()}-qr-${Date.now()}.html`) writeFileSync(htmlPath, html) setTimeout(() => { try { unlinkSync(htmlPath) } catch {} }, 300_000).unref() return htmlPath } catch { return null } } export function openInBrowser(filePath: string): void { try { if (process.platform === 'darwin') { execSync(`open "${filePath}"`, { stdio: 'ignore' }) } else if (process.platform === 'win32') { execSync(`start "" "${filePath}"`, { stdio: 'ignore' }) } else { execSync(`xdg-open "${filePath}"`, { stdio: 'ignore' }) } } catch {} } export async function renderTerminalQR(data: string): Promise { return QRCode.toString(data, { type: 'terminal', small: true }) } export async function displayQR( data: string, options: QRDisplayOptions & { interactive: boolean openBrowser?: boolean formatOutput: (obj: Record, pretty?: boolean) => string pretty?: boolean }, ): Promise { const shouldOpenBrowser = options.openBrowser ?? options.interactive const htmlPath = await createQRHtmlFile(data, options) if (htmlPath && shouldOpenBrowser) openInBrowser(htmlPath) if (options.interactive) { try { const qrAscii = await renderTerminalQR(data) info(`\n${options.scanInstruction}:\n`) info(qrAscii) } catch { info(`\nOpen the QR code in the browser window, or scan this URL:\n${data}\n`) } } else { console.log( options.formatOutput( { next_action: 'scan_qr', qr_url: data, qr_html_path: htmlPath, message: htmlPath ? `QR code opened in browser. ${options.scanInstruction}` : `QR code generated. Open qr_url to scan.`, }, options.pretty, ), ) } }