import React, { useCallback, useEffect, useState } from 'react'; import { useSettings } from '~/lib/hooks/useSettings'; import commit from '~/commit.json'; interface ProviderStatus { name: string; enabled: boolean; isLocal: boolean; isRunning: boolean | null; error?: string; lastChecked: Date; responseTime?: number; url: string | null; } interface SystemInfo { os: string; browser: string; screen: string; language: string; timezone: string; memory: string; cores: number; } interface IProviderConfig { name: string; settings: { enabled: boolean; }; } const LOCAL_PROVIDERS = ['Ollama', 'LMStudio', 'OpenAILike']; const versionHash = commit.commit; const GITHUB_URLS = { original: 'https://api.github.com/repos/stackblitz-labs/bolt.diy/commits/main', fork: 'https://api.github.com/repos/Stijnus/bolt.new-any-llm/commits/main', }; function getSystemInfo(): SystemInfo { const formatBytes = (bytes: number): string => { if (bytes === 0) { return '0 Bytes'; } const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; }; return { os: navigator.platform, browser: navigator.userAgent.split(' ').slice(-1)[0], screen: `${window.screen.width}x${window.screen.height}`, language: navigator.language, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, memory: formatBytes(performance?.memory?.jsHeapSizeLimit || 0), cores: navigator.hardwareConcurrency || 0, }; } const checkProviderStatus = async (url: string | null, providerName: string): Promise => { if (!url) { console.log(`[Debug] No URL provided for ${providerName}`); return { name: providerName, enabled: false, isLocal: true, isRunning: false, error: 'No URL configured', lastChecked: new Date(), url: null, }; } console.log(`[Debug] Checking status for ${providerName} at ${url}`); const startTime = performance.now(); try { if (providerName.toLowerCase() === 'ollama') { // Special check for Ollama root endpoint try { console.log(`[Debug] Checking Ollama root endpoint: ${url}`); const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 5000); // 5 second timeout const response = await fetch(url, { signal: controller.signal, headers: { Accept: 'text/plain,application/json', }, }); clearTimeout(timeoutId); const text = await response.text(); console.log(`[Debug] Ollama root response:`, text); if (text.includes('Ollama is running')) { console.log(`[Debug] Ollama running confirmed via root endpoint`); return { name: providerName, enabled: false, isLocal: true, isRunning: true, lastChecked: new Date(), responseTime: performance.now() - startTime, url, }; } } catch (error) { console.log(`[Debug] Ollama root check failed:`, error); const errorMessage = error instanceof Error ? error.message : 'Unknown error'; if (errorMessage.includes('aborted')) { return { name: providerName, enabled: false, isLocal: true, isRunning: false, error: 'Connection timeout', lastChecked: new Date(), responseTime: performance.now() - startTime, url, }; } } } // Try different endpoints based on provider const checkUrls = [`${url}/api/health`, `${url}/v1/models`]; console.log(`[Debug] Checking additional endpoints:`, checkUrls); const results = await Promise.all( checkUrls.map(async (checkUrl) => { try { console.log(`[Debug] Trying endpoint: ${checkUrl}`); const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 5000); const response = await fetch(checkUrl, { signal: controller.signal, headers: { Accept: 'application/json', }, }); clearTimeout(timeoutId); const ok = response.ok; console.log(`[Debug] Endpoint ${checkUrl} response:`, ok); if (ok) { try { const data = await response.json(); console.log(`[Debug] Endpoint ${checkUrl} data:`, data); } catch { console.log(`[Debug] Could not parse JSON from ${checkUrl}`); } } return ok; } catch (error) { console.log(`[Debug] Endpoint ${checkUrl} failed:`, error); return false; } }), ); const isRunning = results.some((result) => result); console.log(`[Debug] Final status for ${providerName}:`, isRunning); return { name: providerName, enabled: false, isLocal: true, isRunning, lastChecked: new Date(), responseTime: performance.now() - startTime, url, }; } catch (error) { console.log(`[Debug] Provider check failed for ${providerName}:`, error); return { name: providerName, enabled: false, isLocal: true, isRunning: false, error: error instanceof Error ? error.message : 'Unknown error', lastChecked: new Date(), responseTime: performance.now() - startTime, url, }; } }; export default function DebugTab() { const { providers } = useSettings(); const [activeProviders, setActiveProviders] = useState([]); const [updateMessage, setUpdateMessage] = useState(''); const [systemInfo] = useState(getSystemInfo()); const [isCheckingUpdate, setIsCheckingUpdate] = useState(false); const updateProviderStatuses = async () => { if (!providers) { return; } try { const entries = Object.entries(providers) as [string, IProviderConfig][]; const statuses = entries .filter(([, provider]) => LOCAL_PROVIDERS.includes(provider.name)) .map(async ([, provider]) => { const envVarName = provider.name.toLowerCase() === 'ollama' ? 'OLLAMA_API_BASE_URL' : provider.name.toLowerCase() === 'lmstudio' ? 'LMSTUDIO_API_BASE_URL' : `REACT_APP_${provider.name.toUpperCase()}_URL`; // Access environment variables through import.meta.env const url = import.meta.env[envVarName] || null; console.log(`[Debug] Using URL for ${provider.name}:`, url, `(from ${envVarName})`); const status = await checkProviderStatus(url, provider.name); return { ...status, enabled: provider.settings.enabled ?? false, }; }); Promise.all(statuses).then(setActiveProviders); } catch (error) { console.error('[Debug] Failed to update provider statuses:', error); } }; useEffect(() => { updateProviderStatuses(); const interval = setInterval(updateProviderStatuses, 30000); return () => clearInterval(interval); }, [providers]); const handleCheckForUpdate = useCallback(async () => { if (isCheckingUpdate) { return; } try { setIsCheckingUpdate(true); setUpdateMessage('Checking for updates...'); const [originalResponse, forkResponse] = await Promise.all([ fetch(GITHUB_URLS.original), fetch(GITHUB_URLS.fork), ]); if (!originalResponse.ok || !forkResponse.ok) { throw new Error('Failed to fetch repository information'); } const [originalData, forkData] = await Promise.all([ originalResponse.json() as Promise<{ sha: string }>, forkResponse.json() as Promise<{ sha: string }>, ]); const originalCommitHash = originalData.sha; const forkCommitHash = forkData.sha; const isForked = versionHash === forkCommitHash && forkCommitHash !== originalCommitHash; if (originalCommitHash !== versionHash) { setUpdateMessage( `Update available from original repository!\n` + `Current: ${versionHash.slice(0, 7)}${isForked ? ' (forked)' : ''}\n` + `Latest: ${originalCommitHash.slice(0, 7)}`, ); } else { setUpdateMessage('You are on the latest version from the original repository'); } } catch (error) { setUpdateMessage('Failed to check for updates'); console.error('[Debug] Failed to check for updates:', error); } finally { setIsCheckingUpdate(false); } }, [isCheckingUpdate]); const handleCopyToClipboard = useCallback(() => { const debugInfo = { System: systemInfo, Providers: activeProviders.map((provider) => ({ name: provider.name, enabled: provider.enabled, isLocal: provider.isLocal, running: provider.isRunning, error: provider.error, lastChecked: provider.lastChecked, responseTime: provider.responseTime, url: provider.url, })), Version: versionHash, Timestamp: new Date().toISOString(), }; navigator.clipboard.writeText(JSON.stringify(debugInfo, null, 2)).then(() => { alert('Debug information copied to clipboard!'); }); }, [activeProviders, systemInfo]); return (

Debug Information

{updateMessage && (

{updateMessage}

{updateMessage.includes('Update available') && (

To update:

  1. Pull the latest changes:{' '} git pull upstream main
  2. Install any new dependencies:{' '} pnpm install
  3. Restart the application
)}
)}

System Information

Operating System

{systemInfo.os}

Browser

{systemInfo.browser}

Screen Resolution

{systemInfo.screen}

Language

{systemInfo.language}

Timezone

{systemInfo.timezone}

CPU Cores

{systemInfo.cores}

Version

{versionHash.slice(0, 7)} ({new Date().toLocaleDateString()})

Local LLM Status

{activeProviders.map((provider) => (

{provider.name}

{provider.url && (

{provider.url}

)}
{provider.enabled ? 'Enabled' : 'Disabled'} {provider.enabled && ( {provider.isRunning ? 'Running' : 'Not Running'} )}
{/* Status Details */}
Last checked: {new Date(provider.lastChecked).toLocaleTimeString()} {provider.responseTime && ( Response time: {Math.round(provider.responseTime)}ms )}
{/* Error Message */} {provider.error && (
Error: {provider.error}
)} {/* Connection Info */} {provider.url && (
Endpoints checked:
  • {provider.url} (root)
  • {provider.url}/api/health
  • {provider.url}/v1/models
)}
))} {activeProviders.length === 0 && (
No local LLMs configured
)}
); }