From ea64f8eb41fe5f269a82a7a5e3110d668403e9e0 Mon Sep 17 00:00:00 2001 From: lairulan Date: Wed, 11 Mar 2026 15:18:32 +0800 Subject: [PATCH 1/2] fix: improve onboarding reliability for macOS bun users - electron/main.ts: add ~/.bun/bin to getExpandedShellPath() so Claude CLI installed via bun is detectable in the Electron GUI context (macOS GUI apps do not inherit shell PATH from .zshrc/.bash_profile) - src/lib/platform.ts: add ~/.bun/bin to getExtraPathDirs() and ~/.bun/bin/claude to getClaudeCandidatePaths() for consistent PATH expansion on both renderer and main process sides - src/lib/db.ts (createProvider): set is_active=1 when inserting a new provider and deactivate all existing providers first, so the newly created provider is immediately usable without a manual toggle - src/lib/claude-client.ts: add provider-activation hint to the "exited with code 1" error message, helping users diagnose the silent failure caused by an inactive provider --- electron/main.ts | 2 +- src/lib/claude-client.ts | 2 +- src/lib/db.ts | 5 ++++- src/lib/platform.ts | 2 ++ 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/electron/main.ts b/electron/main.ts index c4f2568d..5da8698d 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -336,7 +336,7 @@ function getExpandedShellPath(): string { return [...new Set(allParts)].join(sep); } else { const basePath = `/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin`; - const raw = `${basePath}:${home}/.npm-global/bin:${home}/.local/bin:${home}/.claude/bin:${shellPath}`; + const raw = `${basePath}:${home}/.npm-global/bin:${home}/.bun/bin:${home}/.local/bin:${home}/.claude/bin:${shellPath}`; const allParts = raw.split(':').filter(Boolean); return [...new Set(allParts)].join(':'); } diff --git a/src/lib/claude-client.ts b/src/lib/claude-client.ts index a8939428..eabce2aa 100644 --- a/src/lib/claude-client.ts +++ b/src/lib/claude-client.ts @@ -930,7 +930,7 @@ export function streamClaude(options: ClaudeStreamOptions): ReadableStream isImageFile(f.type)); const imageHint = hasImages ? '\n• Provider may not support image/vision input' : ''; - errorMessage = `Claude Code process exited with an error${providerHint}. This is often caused by:\n• Invalid or missing API Key\n• Incorrect Base URL configuration\n• Network connectivity issues${imageHint}${detailHint}\n\nOriginal error: ${rawMessage}`; + errorMessage = `Claude Code process exited with an error${providerHint}. This is often caused by:\n• Invalid or missing API Key\n• Incorrect Base URL configuration\n• Network connectivity issues\n• API provider not activated (check Settings → API Providers and ensure your provider is set as active)${imageHint}${detailHint}\n\nOriginal error: ${rawMessage}`; } else if (rawMessage.includes('exited with code')) { const providerHint = resolved.provider?.name ? ` (Provider: ${resolved.provider?.name})` : ''; errorMessage = `Claude Code process crashed unexpectedly${providerHint}.\n\nOriginal error: ${rawMessage}`; diff --git a/src/lib/db.ts b/src/lib/db.ts index 35698520..55094635 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -1054,6 +1054,9 @@ export function createProvider(data: CreateProviderRequest): ApiProvider { const maxRow = db.prepare('SELECT MAX(sort_order) as max_order FROM api_providers').get() as { max_order: number | null }; const sortOrder = (maxRow.max_order ?? -1) + 1; + // Deactivate all existing providers so the new one becomes the sole active provider + db.prepare('UPDATE api_providers SET is_active = 0').run(); + db.prepare( `INSERT INTO api_providers (id, name, provider_type, protocol, base_url, api_key, is_active, sort_order, extra_env, headers_json, env_overrides_json, role_models_json, notes, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` @@ -1064,7 +1067,7 @@ export function createProvider(data: CreateProviderRequest): ApiProvider { data.protocol || '', data.base_url || '', data.api_key || '', - 0, + 1, sortOrder, data.extra_env || '{}', data.headers_json || '{}', diff --git a/src/lib/platform.ts b/src/lib/platform.ts index 0be9860b..5473e1de 100644 --- a/src/lib/platform.ts +++ b/src/lib/platform.ts @@ -40,6 +40,7 @@ export function getExtraPathDirs(): string[] { '/usr/bin', '/bin', path.join(home, '.npm-global', 'bin'), + path.join(home, '.bun', 'bin'), path.join(home, '.nvm', 'current', 'bin'), path.join(home, '.local', 'bin'), path.join(home, '.claude', 'bin'), @@ -74,6 +75,7 @@ export function getClaudeCandidatePaths(): string[] { '/usr/local/bin/claude', '/opt/homebrew/bin/claude', path.join(home, '.npm-global', 'bin', 'claude'), + path.join(home, '.bun', 'bin', 'claude'), path.join(home, '.local', 'bin', 'claude'), path.join(home, '.claude', 'bin', 'claude'), ]; From 4df64d25af86e39b2985380443bc791f9871d3e5 Mon Sep 17 00:00:00 2001 From: lairulan Date: Wed, 11 Mar 2026 15:26:39 +0800 Subject: [PATCH 2/2] fix(db): activate new provider only when no active provider exists Replace the blanket "deactivate all then activate new" logic in createProvider() with a more conservative approach: - If no active provider exists (e.g. first-time onboarding), the new provider is inserted with is_active = 1 so it works immediately - If an active provider already exists, the new provider is inserted with is_active = 0, leaving the existing selection untouched This avoids silently deactivating a provider the user is already using when they add a second/backup provider via Settings. --- src/lib/db.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/db.ts b/src/lib/db.ts index 55094635..6bba9e4c 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -1054,8 +1054,8 @@ export function createProvider(data: CreateProviderRequest): ApiProvider { const maxRow = db.prepare('SELECT MAX(sort_order) as max_order FROM api_providers').get() as { max_order: number | null }; const sortOrder = (maxRow.max_order ?? -1) + 1; - // Deactivate all existing providers so the new one becomes the sole active provider - db.prepare('UPDATE api_providers SET is_active = 0').run(); + // Activate the new provider only if there is no existing active provider (e.g. first-time onboarding) + const hasActive = db.prepare('SELECT 1 FROM api_providers WHERE is_active = 1 LIMIT 1').get(); db.prepare( `INSERT INTO api_providers (id, name, provider_type, protocol, base_url, api_key, is_active, sort_order, extra_env, headers_json, env_overrides_json, role_models_json, notes, created_at, updated_at) @@ -1067,7 +1067,7 @@ export function createProvider(data: CreateProviderRequest): ApiProvider { data.protocol || '', data.base_url || '', data.api_key || '', - 1, + hasActive ? 0 : 1, sortOrder, data.extra_env || '{}', data.headers_json || '{}',