/** * Utility CLI Commands - TypeScript Implementation * * Database maintenance, statistics, and benchmarking */ import chalk from 'chalk' import ora from 'ora' import Table from 'cli-table3' import { Brainy } from '../../brainyData.js' interface UtilityOptions { verbose?: boolean json?: boolean pretty?: boolean } interface StatsOptions extends UtilityOptions { byService?: boolean detailed?: boolean } interface CleanOptions extends UtilityOptions { removeOrphans?: boolean rebuildIndex?: boolean } interface BenchmarkOptions extends UtilityOptions { operations?: string iterations?: string } let brainyInstance: Brainy | null = null const getBrainy = async (): Promise => { if (!brainyInstance) { brainyInstance = new Brainy() await brainyInstance.init() } return brainyInstance } const formatBytes = (bytes: number): string => { if (bytes === 0) return '0 B' const k = 1024 const sizes = ['B', '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 formatOutput = (data: any, options: UtilityOptions): void => { if (options.json) { console.log(options.pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data)) } } export const utilityCommands = { /** * Show database statistics */ async stats(options: StatsOptions) { const spinner = ora('Gathering statistics...').start() try { const brain = await getBrainy() const stats = await brain.getStatistics() const memUsage = process.memoryUsage() spinner.succeed('Statistics gathered') if (options.json) { formatOutput(stats, options) return } console.log(chalk.cyan('\nšŸ“Š Database Statistics\n')) // Core stats table const coreTable = new Table({ head: [chalk.cyan('Metric'), chalk.cyan('Value')], style: { head: [], border: [] } }) coreTable.push( ['Total Items', chalk.green(stats.nounCount + stats.verbCount + stats.metadataCount || 0)], ['Nouns', chalk.green(stats.nounCount || 0)], ['Verbs (Relationships)', chalk.green(stats.verbCount || 0)], ['Metadata Records', chalk.green(stats.metadataCount || 0)] ) console.log(coreTable.toString()) // Service breakdown if available if (options.byService && stats.serviceBreakdown) { console.log(chalk.cyan('\nšŸ”§ Service Breakdown\n')) const serviceTable = new Table({ head: [chalk.cyan('Service'), chalk.cyan('Nouns'), chalk.cyan('Verbs'), chalk.cyan('Metadata')], style: { head: [], border: [] } }) Object.entries(stats.serviceBreakdown).forEach(([service, serviceStats]: [string, any]) => { serviceTable.push([ service, serviceStats.nounCount || 0, serviceStats.verbCount || 0, serviceStats.metadataCount || 0 ]) }) console.log(serviceTable.toString()) } // Storage info if (stats.storage) { console.log(chalk.cyan('\nšŸ’¾ Storage\n')) const storageTable = new Table({ head: [chalk.cyan('Property'), chalk.cyan('Value')], style: { head: [], border: [] } }) storageTable.push( ['Type', stats.storage.type || 'Unknown'], ['Size', stats.storage.size ? formatBytes(stats.storage.size) : 'N/A'], ['Location', stats.storage.location || 'N/A'] ) console.log(storageTable.toString()) } // Performance metrics if (stats.performance && options.detailed) { console.log(chalk.cyan('\n⚔ Performance\n')) const perfTable = new Table({ head: [chalk.cyan('Metric'), chalk.cyan('Value')], style: { head: [], border: [] } }) if (stats.performance.avgQueryTime) { perfTable.push(['Avg Query Time', `${stats.performance.avgQueryTime.toFixed(2)} ms`]) } if (stats.performance.totalQueries) { perfTable.push(['Total Queries', stats.performance.totalQueries]) } if (stats.performance.cacheHitRate) { perfTable.push(['Cache Hit Rate', `${(stats.performance.cacheHitRate * 100).toFixed(1)}%`]) } console.log(perfTable.toString()) } // Memory usage console.log(chalk.cyan('\n🧠 Memory Usage\n')) const memTable = new Table({ head: [chalk.cyan('Type'), chalk.cyan('Size')], style: { head: [], border: [] } }) memTable.push( ['Heap Used', formatBytes(memUsage.heapUsed)], ['Heap Total', formatBytes(memUsage.heapTotal)], ['RSS', formatBytes(memUsage.rss)], ['External', formatBytes(memUsage.external)] ) console.log(memTable.toString()) // Index info if (stats.index && options.detailed) { console.log(chalk.cyan('\nšŸŽÆ Vector Index\n')) const indexTable = new Table({ head: [chalk.cyan('Property'), chalk.cyan('Value')], style: { head: [], border: [] } }) indexTable.push( ['Dimensions', stats.index.dimensions || 'N/A'], ['Indexed Vectors', stats.index.vectorCount || 0], ['Index Size', stats.index.indexSize ? formatBytes(stats.index.indexSize) : 'N/A'] ) console.log(indexTable.toString()) } } catch (error: any) { spinner.fail('Failed to gather statistics') console.error(chalk.red(error.message)) process.exit(1) } }, /** * Clean and optimize database */ async clean(options: CleanOptions) { const spinner = ora('Cleaning database...').start() try { const brain = await getBrainy() const tasks: string[] = [] if (options.removeOrphans) { spinner.text = 'Removing orphaned items...' tasks.push('Removed orphaned items') // Implementation would go here await new Promise(resolve => setTimeout(resolve, 500)) // Simulate work } if (options.rebuildIndex) { spinner.text = 'Rebuilding search index...' tasks.push('Rebuilt search index') // Implementation would go here await new Promise(resolve => setTimeout(resolve, 1000)) // Simulate work } if (tasks.length === 0) { spinner.text = 'Running general cleanup...' tasks.push('General cleanup completed') // Run general cleanup tasks await new Promise(resolve => setTimeout(resolve, 500)) // Simulate work } spinner.succeed('Database cleaned') if (!options.json) { console.log(chalk.green('\nāœ“ Cleanup completed:')) tasks.forEach(task => { console.log(chalk.dim(` • ${task}`)) }) // Get new stats const stats = await brain.getStatistics() console.log(chalk.cyan('\nDatabase Status:')) console.log(` Total items: ${stats.nounCount + stats.verbCount}`) console.log(` Index status: ${chalk.green('Healthy')}`) } else { formatOutput({ tasks, success: true }, options) } } catch (error: any) { spinner.fail('Cleanup failed') console.error(chalk.red(error.message)) process.exit(1) } }, /** * Run performance benchmarks */ async benchmark(options: BenchmarkOptions) { const operations = options.operations || 'all' const iterations = parseInt(options.iterations || '100') console.log(chalk.cyan(`\nšŸš€ Running Benchmarks (${iterations} iterations)\n`)) const results: any = { operations: {}, summary: {} } try { const brain = await getBrainy() // Benchmark different operations const benchmarks = [ { name: 'add', enabled: operations === 'all' || operations.includes('add') }, { name: 'search', enabled: operations === 'all' || operations.includes('search') }, { name: 'similarity', enabled: operations === 'all' || operations.includes('similarity') }, { name: 'cluster', enabled: operations === 'all' || operations.includes('cluster') } ] for (const bench of benchmarks) { if (!bench.enabled) continue const spinner = ora(`Benchmarking ${bench.name}...`).start() const times: number[] = [] for (let i = 0; i < iterations; i++) { const start = Date.now() switch (bench.name) { case 'add': await brain.add(`Test item ${i}`, { benchmark: true }) break case 'search': await brain.search('test', 10) break case 'similarity': const neural = brain.neural await neural.similar('test1', 'test2') break case 'cluster': const neuralApi = brain.neural await neuralApi.clusters() break } times.push(Date.now() - start) } // Calculate statistics const avg = times.reduce((a, b) => a + b, 0) / times.length const min = Math.min(...times) const max = Math.max(...times) const median = times.sort((a, b) => a - b)[Math.floor(times.length / 2)] results.operations[bench.name] = { avg: avg.toFixed(2), min, max, median, ops: (1000 / avg).toFixed(2) } spinner.succeed(`${bench.name}: ${avg.toFixed(2)}ms avg (${(1000 / avg).toFixed(2)} ops/sec)`) } // Calculate summary const totalOps = Object.values(results.operations).reduce((sum: number, op: any) => sum + parseFloat(op.ops), 0) results.summary = { totalOperations: Object.keys(results.operations).length, averageOpsPerSec: (totalOps / Object.keys(results.operations).length).toFixed(2) } if (!options.json) { // Display results table console.log(chalk.cyan('\nšŸ“Š Benchmark Results\n')) const table = new Table({ head: [ chalk.cyan('Operation'), chalk.cyan('Avg (ms)'), chalk.cyan('Min (ms)'), chalk.cyan('Max (ms)'), chalk.cyan('Median (ms)'), chalk.cyan('Ops/sec') ], style: { head: [], border: [] } }) Object.entries(results.operations).forEach(([op, stats]: [string, any]) => { table.push([ op, stats.avg, stats.min, stats.max, stats.median, chalk.green(stats.ops) ]) }) console.log(table.toString()) console.log(chalk.cyan('\nšŸ“ˆ Summary')) console.log(` Operations tested: ${results.summary.totalOperations}`) console.log(` Average throughput: ${chalk.green(results.summary.averageOpsPerSec)} ops/sec`) } else { formatOutput(results, options) } } catch (error: any) { console.error(chalk.red('Benchmark failed:'), error.message) process.exit(1) } } }