UNPKG

lanonasis-memory

Version:

Memory as a Service integration - AI-powered memory management with semantic search (Compatible with CLI v3.0.6+)

460 lines (393 loc) 16.5 kB
import * as vscode from 'vscode'; import { SecureApiKeyService } from '../services/SecureApiKeyService'; import type { IMemoryService } from '../services/IMemoryService'; import { EnhancedMemoryService } from '../services/EnhancedMemoryService'; /** * System diagnostics and health check utilities */ export interface DiagnosticResult { category: string; status: 'success' | 'warning' | 'error' | 'info'; message: string; details?: string; action?: string; } export interface SystemHealth { overall: 'healthy' | 'degraded' | 'critical'; results: DiagnosticResult[]; timestamp: Date; } /** * Runs comprehensive system diagnostics */ export async function runDiagnostics( context: vscode.ExtensionContext, secureApiKeyService: SecureApiKeyService, memoryService: IMemoryService, outputChannel: vscode.OutputChannel ): Promise<SystemHealth> { const results: DiagnosticResult[] = []; outputChannel.appendLine('=================================================='); outputChannel.appendLine('Starting Lanonasis Extension Diagnostics'); outputChannel.appendLine(`Timestamp: ${new Date().toISOString()}`); outputChannel.appendLine('==================================================\n'); // Check 1: Extension Context results.push(await checkExtensionContext(context, outputChannel)); // Check 2: VSCode Version results.push(await checkVSCodeVersion(outputChannel)); // Check 3: Configuration results.push(await checkConfiguration(outputChannel)); // Check 4: Authentication results.push(await checkAuthentication(secureApiKeyService, outputChannel)); // Check 5: Network Connectivity results.push(await checkNetworkConnectivity(memoryService, outputChannel)); // Check 6: CLI Integration results.push(await checkCLIIntegration(memoryService, outputChannel)); // Check 7: Storage results.push(await checkStorage(context, outputChannel)); // Determine overall health const overall = determineOverallHealth(results); outputChannel.appendLine('\n=================================================='); outputChannel.appendLine(`Overall Health: ${overall.toUpperCase()}`); outputChannel.appendLine('=================================================='); return { overall, results, timestamp: new Date() }; } async function checkExtensionContext( context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel ): Promise<DiagnosticResult> { outputChannel.appendLine('[1/7] Checking Extension Context...'); try { if (!context) { return { category: 'Extension Context', status: 'error', message: 'Extension context is not available', action: 'Reload VSCode' }; } const globalStoragePath = context.globalStorageUri?.fsPath; const workspaceStoragePath = context.storageUri?.fsPath; outputChannel.appendLine(` ✓ Extension ID: ${context.extension.id}`); outputChannel.appendLine(` ✓ Extension Path: ${context.extensionPath}`); outputChannel.appendLine(` ✓ Global Storage: ${globalStoragePath || 'N/A'}`); outputChannel.appendLine(` ✓ Workspace Storage: ${workspaceStoragePath || 'N/A'}`); return { category: 'Extension Context', status: 'success', message: 'Extension context is properly initialized' }; } catch (error) { outputChannel.appendLine(` ✗ Error: ${error instanceof Error ? error.message : String(error)}`); return { category: 'Extension Context', status: 'error', message: 'Failed to check extension context', details: error instanceof Error ? error.message : String(error) }; } } async function checkVSCodeVersion(outputChannel: vscode.OutputChannel): Promise<DiagnosticResult> { outputChannel.appendLine('\n[2/7] Checking VSCode Version...'); try { const version = vscode.version; const requiredVersion = '1.74.0'; outputChannel.appendLine(` ✓ Current Version: ${version}`); outputChannel.appendLine(` ✓ Required Version: ${requiredVersion}+`); // Parse version numbers for comparison const [major, minor] = version.split('.').map(Number); const [reqMajor, reqMinor] = requiredVersion.split('.').map(Number); if (major > reqMajor || (major === reqMajor && minor >= reqMinor)) { return { category: 'VSCode Version', status: 'success', message: `VSCode ${version} meets minimum requirements` }; } else { return { category: 'VSCode Version', status: 'warning', message: `VSCode ${version} is below recommended version ${requiredVersion}`, action: 'Update VSCode' }; } } catch (error) { outputChannel.appendLine(` ✗ Error: ${error instanceof Error ? error.message : String(error)}`); return { category: 'VSCode Version', status: 'error', message: 'Failed to check VSCode version', details: error instanceof Error ? error.message : String(error) }; } } async function checkConfiguration(outputChannel: vscode.OutputChannel): Promise<DiagnosticResult> { outputChannel.appendLine('\n[3/7] Checking Configuration...'); try { const config = vscode.workspace.getConfiguration('lanonasis'); const apiUrl = config.get<string>('apiUrl'); const gatewayUrl = config.get<string>('gatewayUrl'); const useGateway = config.get<boolean>('useGateway'); const enableMCP = config.get<boolean>('enableMCP'); const preferCLI = config.get<boolean>('preferCLI'); outputChannel.appendLine(` ✓ API URL: ${apiUrl}`); outputChannel.appendLine(` ✓ Gateway URL: ${gatewayUrl}`); outputChannel.appendLine(` ✓ Use Gateway: ${useGateway}`); outputChannel.appendLine(` ✓ Enable MCP: ${enableMCP}`); outputChannel.appendLine(` ✓ Prefer CLI: ${preferCLI}`); const issues: string[] = []; if (!apiUrl) { issues.push('API URL not configured'); } if (useGateway && !gatewayUrl) { issues.push('Gateway mode enabled but Gateway URL not configured'); } if (issues.length > 0) { return { category: 'Configuration', status: 'warning', message: 'Configuration issues detected', details: issues.join('; '), action: 'Check Settings' }; } return { category: 'Configuration', status: 'success', message: 'Configuration is valid' }; } catch (error) { outputChannel.appendLine(` ✗ Error: ${error instanceof Error ? error.message : String(error)}`); return { category: 'Configuration', status: 'error', message: 'Failed to check configuration', details: error instanceof Error ? error.message : String(error) }; } } async function checkAuthentication( secureApiKeyService: SecureApiKeyService, outputChannel: vscode.OutputChannel ): Promise<DiagnosticResult> { outputChannel.appendLine('\n[4/7] Checking Authentication...'); try { const hasApiKey = await secureApiKeyService.hasApiKey(); if (hasApiKey) { outputChannel.appendLine(' ✓ API key is stored securely'); // Try to retrieve it to verify it's accessible try { const apiKey = await secureApiKeyService.getApiKey(); if (apiKey && apiKey.length > 0) { outputChannel.appendLine(` ✓ API key length: ${apiKey.length} characters`); outputChannel.appendLine(` ✓ API key prefix: ${apiKey.substring(0, 8)}...`); return { category: 'Authentication', status: 'success', message: 'Authenticated with valid API key' }; } else { return { category: 'Authentication', status: 'warning', message: 'API key exists but appears empty', action: 'Re-authenticate' }; } } catch (error) { return { category: 'Authentication', status: 'warning', message: 'API key exists but could not be retrieved', details: error instanceof Error ? error.message : String(error), action: 'Re-authenticate' }; } } else { outputChannel.appendLine(' ℹ No API key configured'); return { category: 'Authentication', status: 'info', message: 'Not authenticated', action: 'Authenticate' }; } } catch (error) { outputChannel.appendLine(` ✗ Error: ${error instanceof Error ? error.message : String(error)}`); return { category: 'Authentication', status: 'error', message: 'Failed to check authentication status', details: error instanceof Error ? error.message : String(error) }; } } async function checkNetworkConnectivity( memoryService: IMemoryService, outputChannel: vscode.OutputChannel ): Promise<DiagnosticResult> { outputChannel.appendLine('\n[5/7] Checking Network Connectivity...'); try { if (!memoryService.isAuthenticated()) { outputChannel.appendLine(' ℹ Skipping (not authenticated)'); return { category: 'Network Connectivity', status: 'info', message: 'Skipped - not authenticated' }; } outputChannel.appendLine(' ⏳ Testing connection...'); const startTime = Date.now(); await memoryService.testConnection(); const duration = Date.now() - startTime; outputChannel.appendLine(` ✓ Connection successful (${duration}ms)`); return { category: 'Network Connectivity', status: 'success', message: `Connected successfully in ${duration}ms` }; } catch (error) { outputChannel.appendLine(` ✗ Connection failed: ${error instanceof Error ? error.message : String(error)}`); return { category: 'Network Connectivity', status: 'error', message: 'Unable to connect to Lanonasis servers', details: error instanceof Error ? error.message : String(error), action: 'Check internet connection' }; } } async function checkCLIIntegration( memoryService: IMemoryService, outputChannel: vscode.OutputChannel ): Promise<DiagnosticResult> { outputChannel.appendLine('\n[6/7] Checking CLI Integration...'); try { if (memoryService instanceof EnhancedMemoryService) { const capabilities = memoryService.getCapabilities(); if (capabilities) { outputChannel.appendLine(` ✓ Enhanced Memory Service detected`); outputChannel.appendLine(` ✓ CLI Available: ${capabilities.cliAvailable}`); outputChannel.appendLine(` ✓ Golden Contract: ${capabilities.goldenContract}`); outputChannel.appendLine(` ✓ CLI Version: ${capabilities.version || 'Unknown'}`); if (capabilities.cliAvailable && capabilities.goldenContract) { return { category: 'CLI Integration', status: 'success', message: `CLI v${capabilities.version} integrated and operational` }; } else if (capabilities.cliAvailable) { return { category: 'CLI Integration', status: 'warning', message: 'CLI detected but not fully compatible', details: 'Golden contract not met', action: 'Update CLI to v3.0.6+' }; } } } outputChannel.appendLine(' ℹ Using basic memory service (CLI not available)'); return { category: 'CLI Integration', status: 'info', message: 'CLI not available - using direct API', details: 'Install @lanonasis/cli for enhanced performance' }; } catch (error) { outputChannel.appendLine(` ✗ Error: ${error instanceof Error ? error.message : String(error)}`); return { category: 'CLI Integration', status: 'warning', message: 'Unable to check CLI status', details: error instanceof Error ? error.message : String(error) }; } } async function checkStorage( context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel ): Promise<DiagnosticResult> { outputChannel.appendLine('\n[7/7] Checking Storage...'); try { // Test global state await context.globalState.update('lanonasis.diagnosticTest', Date.now()); const testValue = context.globalState.get('lanonasis.diagnosticTest'); if (!testValue) { outputChannel.appendLine(' ✗ Global state write/read failed'); return { category: 'Storage', status: 'error', message: 'Storage system is not working properly', action: 'Reload VSCode' }; } outputChannel.appendLine(' ✓ Global state is accessible'); // List stored keys (for diagnostics) const keys = context.globalState.keys(); outputChannel.appendLine(` ✓ Stored keys: ${keys.length}`); const firstTime = context.globalState.get('lanonasis.firstTime'); outputChannel.appendLine(` ✓ First time flag: ${firstTime}`); return { category: 'Storage', status: 'success', message: 'Storage system is working properly' }; } catch (error) { outputChannel.appendLine(` ✗ Error: ${error instanceof Error ? error.message : String(error)}`); return { category: 'Storage', status: 'error', message: 'Storage system check failed', details: error instanceof Error ? error.message : String(error) }; } } function determineOverallHealth(results: DiagnosticResult[]): 'healthy' | 'degraded' | 'critical' { const hasError = results.some(r => r.status === 'error'); const hasWarning = results.some(r => r.status === 'warning'); if (hasError) { return 'critical'; } else if (hasWarning) { return 'degraded'; } else { return 'healthy'; } } /** * Formats diagnostic results for display */ export function formatDiagnosticResults(health: SystemHealth): string { const statusEmoji = { healthy: '✅', degraded: '⚠️', critical: '❌' }; const resultEmoji = { success: '✅', warning: '⚠️', error: '❌', info: 'ℹ️' }; let output = `# Lanonasis Extension Diagnostics\n\n`; output += `**Overall Health:** ${statusEmoji[health.overall]} ${health.overall.toUpperCase()}\n`; output += `**Timestamp:** ${health.timestamp.toLocaleString()}\n\n`; output += `---\n\n`; for (const result of health.results) { output += `## ${resultEmoji[result.status]} ${result.category}\n\n`; output += `**Status:** ${result.status.toUpperCase()}\n\n`; output += `**Message:** ${result.message}\n\n`; if (result.details) { output += `**Details:** ${result.details}\n\n`; } if (result.action) { output += `**Recommended Action:** ${result.action}\n\n`; } output += `---\n\n`; } return output; }