Skip to main content
Glama
qdrant-indexer-control.cjs9.22 kB
#!/usr/bin/env node /** * Control script for Qdrant background indexer */ const fs = require('fs'); const { spawn, execSync } = require('child_process'); const path = require('path'); const readline = require('readline'); const STATE_FILES = { PID: '.qdrant-indexer.pid', LOG: '.qdrant-indexer.log', STATUS: '.qdrant-indexing-status.json' }; const INDEXER_SCRIPT = path.join(__dirname, 'qdrant-background-indexer.cjs'); class QdrantIndexerControl { constructor() { this.command = process.argv[2] || 'help'; } async run() { switch (this.command) { case 'start': await this.start(); break; case 'stop': await this.stop(); break; case 'restart': await this.restart(); break; case 'status': await this.status(); break; case 'pause': await this.sendCommand('pause'); break; case 'resume': await this.sendCommand('resume'); break; case 'reindex': await this.reindex(); break; case 'clear': await this.clear(); break; case 'logs': await this.logs(); break; case 'watch': await this.watch(); break; case 'help': default: this.help(); } } async start() { if (this.isRunning()) { console.log('✓ Indexer is already running'); return; } console.log('Starting Qdrant background indexer...'); // Start indexer in background const child = spawn('node', [INDEXER_SCRIPT], { detached: true, stdio: 'ignore', cwd: process.cwd() }); child.unref(); // Wait a moment for startup await new Promise(resolve => setTimeout(resolve, 2000)); if (this.isRunning()) { console.log('✓ Indexer started successfully'); await this.status(); } else { console.error('✗ Failed to start indexer'); console.log('Check logs: tail -f .qdrant-indexer.log'); } } async stop() { const pid = this.getPid(); if (!pid) { console.log('✓ Indexer is not running'); return; } console.log('Stopping Qdrant background indexer...'); try { process.kill(pid, 'SIGTERM'); // Wait for process to stop let stopped = false; for (let i = 0; i < 10; i++) { await new Promise(resolve => setTimeout(resolve, 500)); if (!this.isRunning()) { stopped = true; break; } } if (stopped) { console.log('✓ Indexer stopped successfully'); } else { console.log('⚠ Indexer is taking longer to stop, sending SIGKILL...'); process.kill(pid, 'SIGKILL'); } } catch (error) { console.error('✗ Failed to stop indexer:', error.message); } } async restart() { await this.stop(); await new Promise(resolve => setTimeout(resolve, 1000)); await this.start(); } async status() { const isRunning = this.isRunning(); const pid = this.getPid(); console.log('\n=== Qdrant Indexer Status ==='); console.log(`Status: ${isRunning ? '🟢 Running' : '🔴 Stopped'}`); if (pid) console.log(`PID: ${pid}`); // Read status file if (fs.existsSync(STATE_FILES.STATUS)) { try { const status = JSON.parse(fs.readFileSync(STATE_FILES.STATUS, 'utf8')); console.log(`\nStatistics:`); console.log(` Total files: ${status.totalFiles || 0}`); console.log(` Indexed: ${status.indexedCount || 0}`); console.log(` Failed: ${status.failedCount || 0}`); console.log(` Queue size: ${status.queueSize || 0}`); console.log(` Paused: ${status.isPaused ? 'Yes' : 'No'}`); if (status.uptime) { const hours = Math.floor(status.uptime / 3600000); const minutes = Math.floor((status.uptime % 3600000) / 60000); console.log(` Uptime: ${hours}h ${minutes}m`); } if (status.lastActivityTime) { const lastActivity = new Date(status.lastActivityTime); console.log(` Last activity: ${lastActivity.toLocaleString()}`); } // Progress bar if (status.totalFiles > 0) { const progress = Math.round((status.indexedCount / status.totalFiles) * 100); const barLength = 30; const filled = Math.round((progress / 100) * barLength); const bar = '█'.repeat(filled) + '░'.repeat(barLength - filled); console.log(`\nProgress: [${bar}] ${progress}%`); } } catch (error) { console.error('Failed to read status:', error.message); } } } async sendCommand(command) { const pid = this.getPid(); if (!pid) { console.error('✗ Indexer is not running'); return; } console.log(`Sending ${command} command...`); // For now, we'll need to implement IPC or use signals // This is a simplified version const child = spawn('node', [INDEXER_SCRIPT, command], { stdio: 'inherit' }); child.on('close', (code) => { if (code === 0) { console.log(`✓ Command '${command}' executed successfully`); } else { console.error(`✗ Command '${command}' failed`); } }); } async reindex() { console.log('Starting full reindex...'); console.log('This will clear all indexed data and re-index all files.'); const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); const answer = await new Promise(resolve => { rl.question('Are you sure? (y/N) ', resolve); }); rl.close(); if (answer.toLowerCase() !== 'y') { console.log('Cancelled'); return; } await this.sendCommand('reindex'); } async clear() { console.log('⚠️ This will clear all indexer data!'); const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); const answer = await new Promise(resolve => { rl.question('Are you sure? (y/N) ', resolve); }); rl.close(); if (answer.toLowerCase() !== 'y') { console.log('Cancelled'); return; } await this.stop(); // Remove state files Object.values(STATE_FILES).forEach(file => { try { fs.unlinkSync(file); console.log(`✓ Removed ${file}`); } catch (e) {} }); // Also remove other state files const otherFiles = ['.qdrant-indexing-queue.json', '.qdrant-indexed-files.json']; otherFiles.forEach(file => { try { fs.unlinkSync(file); console.log(`✓ Removed ${file}`); } catch (e) {} }); console.log('✓ All indexer data cleared'); } async logs() { if (!fs.existsSync(STATE_FILES.LOG)) { console.log('No log file found'); return; } console.log('Tailing logs (Ctrl+C to stop)...\n'); const tail = spawn('tail', ['-f', STATE_FILES.LOG], { stdio: 'inherit' }); process.on('SIGINT', () => { tail.kill(); process.exit(0); }); } async watch() { console.log('Watching indexer status (Ctrl+C to stop)...\n'); // Clear screen console.clear(); const update = async () => { // Move cursor to top process.stdout.write('\u001b[H'); await this.status(); // Show recent log entries if (fs.existsSync(STATE_FILES.LOG)) { console.log('\n=== Recent Activity ==='); try { const logs = execSync(`tail -n 5 ${STATE_FILES.LOG}`).toString(); console.log(logs); } catch (e) {} } }; // Initial update await update(); // Update every 2 seconds const interval = setInterval(update, 2000); process.on('SIGINT', () => { clearInterval(interval); console.log('\n\nStopped watching'); process.exit(0); }); } isRunning() { const pid = this.getPid(); if (!pid) return false; try { process.kill(pid, 0); return true; } catch (e) { // Process not running return false; } } getPid() { try { if (fs.existsSync(STATE_FILES.PID)) { return parseInt(fs.readFileSync(STATE_FILES.PID, 'utf8')); } } catch (e) {} return null; } help() { console.log(` Qdrant Indexer Control Usage: node ${path.basename(__filename)} <command> Commands: start Start the background indexer stop Stop the background indexer restart Restart the background indexer status Show indexer status pause Pause indexing (keeps monitoring) resume Resume indexing reindex Clear and re-index all files clear Clear all indexer data logs Tail indexer logs watch Watch live status help Show this help NPM Scripts: npm run qdrant:start Start indexer npm run qdrant:stop Stop indexer npm run qdrant:status Check status npm run qdrant:watch Watch live status `); } } // Run controller const controller = new QdrantIndexerControl(); controller.run().catch(error => { console.error('Error:', error); process.exit(1); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/steiner385/qdrant-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server