From f413df8df7e21455af618d9cfcba3964076c75a2 Mon Sep 17 00:00:00 2001 From: jamesdanielwhitford <70632508+jamesdanielwhitford@users.noreply.github.com> Date: Fri, 6 Feb 2026 22:09:33 +0200 Subject: [PATCH] feat: add OpenAI voice transcription setup to onboarding wizard (#191) --- src/onboard.ts | 75 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/src/onboard.ts b/src/onboard.ts index 5d741b8..8bafcd8 100644 --- a/src/onboard.ts +++ b/src/onboard.ts @@ -160,6 +160,9 @@ interface OnboardConfig { // Features heartbeat: { enabled: boolean; interval?: string }; cron: boolean; + + // Transcription (voice messages) + transcription: { enabled: boolean; apiKey?: string; model?: string }; } const isPlaceholder = (val?: string) => !val || /^(your_|sk-\.\.\.|placeholder|example)/i.test(val); @@ -524,6 +527,18 @@ async function stepProviders(config: OnboardConfig, env: Record) if (response.ok) { spinner.stop(`Connected ${provider.displayName}`); config.providers.push({ id: provider.id, name: provider.name, apiKey: providerKey }); + + // If OpenAI was just connected, offer to enable voice transcription + if (provider.id === 'openai') { + const enableTranscription = await p.confirm({ + message: 'Enable voice message transcription with this OpenAI key? (uses Whisper)', + initialValue: true, + }); + if (!p.isCancel(enableTranscription) && enableTranscription) { + config.transcription.enabled = true; + config.transcription.apiKey = providerKey; + } + } } else { const error = await response.text(); spinner.stop(`Failed to connect ${provider.displayName}: ${error}`); @@ -690,6 +705,37 @@ async function stepFeatures(config: OnboardConfig): Promise { if (!p.isCancel(setupCron)) config.cron = setupCron; } +// ============================================================================ +// Voice Transcription Setup +// ============================================================================ + +async function stepTranscription(config: OnboardConfig): Promise { + // Skip if already configured from the providers step + if (config.transcription.enabled && config.transcription.apiKey) return; + + const setupTranscription = await p.confirm({ + message: 'Enable voice message transcription? (uses OpenAI Whisper)', + initialValue: config.transcription.enabled, + }); + if (p.isCancel(setupTranscription)) { p.cancel('Setup cancelled'); process.exit(0); } + config.transcription.enabled = setupTranscription; + + if (setupTranscription) { + const existingKey = process.env.OPENAI_API_KEY; + + const apiKey = await p.text({ + message: 'OpenAI API Key (for Whisper transcription)', + placeholder: 'sk-...', + initialValue: existingKey || '', + validate: (v) => { + if (!v) return 'API key is required for voice transcription'; + }, + }); + if (p.isCancel(apiKey)) { p.cancel('Setup cancelled'); process.exit(0); } + config.transcription.apiKey = apiKey; + } +} + // ============================================================================ // Google Workspace Setup (via gog CLI) // ============================================================================ @@ -959,11 +1005,14 @@ function showSummary(config: OnboardConfig): void { if (config.cron) features.push('Cron'); lines.push(`Features: ${features.length > 0 ? features.join(', ') : 'None'}`); + // Transcription + lines.push(`Voice: ${config.transcription.enabled ? 'Enabled (OpenAI Whisper)' : 'Disabled'}`); + // Google if (config.google.enabled) { lines.push(`Google: ${config.google.account} (${config.google.services?.join(', ') || 'all'})`); } - + p.note(lines.join('\n'), 'Configuration'); } @@ -981,6 +1030,7 @@ async function reviewLoop(config: OnboardConfig, env: Record): P { value: 'agent', label: 'Change agent', hint: '' }, { value: 'channels', label: 'Change channels', hint: '' }, { value: 'features', label: 'Change features', hint: '' }, + { value: 'transcription', label: 'Change voice transcription', hint: '' }, { value: 'google', label: 'Change Google Workspace', hint: '' }, ], }); @@ -999,6 +1049,7 @@ async function reviewLoop(config: OnboardConfig, env: Record): P } else if (choice === 'channels') await stepChannels(config, env); else if (choice === 'features') await stepFeatures(config); + else if (choice === 'transcription') await stepTranscription(config); else if (choice === 'google') await stepGoogle(config); } } @@ -1217,6 +1268,11 @@ export async function onboard(options?: { nonInteractive?: boolean }): Promise { if (policy === 'pairing') return 'pairing'; @@ -1367,6 +1428,7 @@ export async function onboard(options?: { nonInteractive?: boolean }): Promise