import React, { useCallback, useEffect, useState } from 'react'; import { useSettings } from '~/lib/hooks/useSettings'; import commit from '~/commit.json'; import { toast } from 'react-toastify'; 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; deviceType: string; colorDepth: string; pixelRatio: number; online: boolean; cookiesEnabled: boolean; doNotTrack: boolean; } interface IProviderConfig { name: string; settings: { enabled: boolean; baseUrl?: string; }; } interface CommitData { commit: string; version?: string; } const connitJson: CommitData = commit; const LOCAL_PROVIDERS = ['Ollama', 'LMStudio', 'OpenAILike']; const versionHash = connitJson.commit; const versionTag = connitJson.version; 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', commitJson: (branch: string) => `https://raw.githubusercontent.com/stackblitz-labs/bolt.diy/${branch}/app/commit.json`, }; 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]; }; const getBrowserInfo = (): string => { const ua = navigator.userAgent; let browser = 'Unknown'; if (ua.includes('Firefox/')) { browser = 'Firefox'; } else if (ua.includes('Chrome/')) { if (ua.includes('Edg/')) { browser = 'Edge'; } else if (ua.includes('OPR/')) { browser = 'Opera'; } else { browser = 'Chrome'; } } else if (ua.includes('Safari/')) { if (!ua.includes('Chrome')) { browser = 'Safari'; } } // Extract version number const match = ua.match(new RegExp(`${browser}\\/([\\d.]+)`)); const version = match ? ` ${match[1]}` : ''; return `${browser}${version}`; }; const getOperatingSystem = (): string => { const ua = navigator.userAgent; const platform = navigator.platform; if (ua.includes('Win')) { return 'Windows'; } if (ua.includes('Mac')) { if (ua.includes('iPhone') || ua.includes('iPad')) { return 'iOS'; } return 'macOS'; } if (ua.includes('Linux')) { return 'Linux'; } if (ua.includes('Android')) { return 'Android'; } return platform || 'Unknown'; }; const getDeviceType = (): string => { const ua = navigator.userAgent; if (ua.includes('Mobile')) { return 'Mobile'; } if (ua.includes('Tablet')) { return 'Tablet'; } return 'Desktop'; }; // Get more detailed memory info if available const getMemoryInfo = (): string => { if ('memory' in performance) { const memory = (performance as any).memory; return `${formatBytes(memory.jsHeapSizeLimit)} (Used: ${formatBytes(memory.usedJSHeapSize)})`; } return 'Not available'; }; return { os: getOperatingSystem(), browser: getBrowserInfo(), screen: `${window.screen.width}x${window.screen.height}`, language: navigator.language, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, memory: getMemoryInfo(), cores: navigator.hardwareConcurrency || 0, deviceType: getDeviceType(), // Add new fields colorDepth: `${window.screen.colorDepth}-bit`, pixelRatio: window.devicePixelRatio, online: navigator.onLine, cookiesEnabled: navigator.cookieEnabled, doNotTrack: navigator.doNotTrack === '1', }; } 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, isLatestBranch } = 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 = await Promise.all( 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] || provider.settings.baseUrl || null; // Ensure baseUrl is used 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, }; }), ); setActiveProviders(statuses); } 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 branchToCheck = isLatestBranch ? 'main' : 'stable'; console.log(`[Debug] Checking for updates against ${branchToCheck} branch`); const localCommitResponse = await fetch(GITHUB_URLS.commitJson(branchToCheck)); if (!localCommitResponse.ok) { throw new Error('Failed to fetch local commit info'); } const localCommitData = (await localCommitResponse.json()) as CommitData; const remoteCommitHash = localCommitData.commit; const currentCommitHash = versionHash; if (remoteCommitHash !== currentCommitHash) { setUpdateMessage( `Update available from ${branchToCheck} branch!\n` + `Current: ${currentCommitHash.slice(0, 7)}\n` + `Latest: ${remoteCommitHash.slice(0, 7)}`, ); } else { setUpdateMessage(`You are on the latest version from the ${branchToCheck} branch`); } } catch (error) { setUpdateMessage('Failed to check for updates'); console.error('[Debug] Failed to check for updates:', error); } finally { setIsCheckingUpdate(false); } }, [isCheckingUpdate, isLatestBranch]); 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: { hash: versionHash.slice(0, 7), branch: isLatestBranch ? 'main' : 'stable', }, Timestamp: new Date().toISOString(), }; navigator.clipboard.writeText(JSON.stringify(debugInfo, null, 2)).then(() => { toast.success('Debug information copied to clipboard!'); }); }, [activeProviders, systemInfo, isLatestBranch]); 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}

Device Type

{systemInfo.deviceType}

Browser

{systemInfo.browser}

Display

{systemInfo.screen} ({systemInfo.colorDepth}) @{systemInfo.pixelRatio}x

Connection

{systemInfo.online ? 'Online' : 'Offline'}

Screen Resolution

{systemInfo.screen}

Language

{systemInfo.language}

Timezone

{systemInfo.timezone}

CPU Cores

{systemInfo.cores}

Version

{versionHash.slice(0, 7)} (v{versionTag || '0.0.1'}) - {isLatestBranch ? 'nightly' : 'stable'}

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
)}
); }