Skip to main content
Glama
cli.js7.64 kB
#!/usr/bin/env node import { promises as fs } from 'fs'; import path from 'path'; import { spawn } from 'child_process'; import { fileURLToPath } from 'url'; // Get __dirname equivalent in ES modules const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const PID_FILE = path.join(process.cwd(), '.shrimp-viewer.pid'); const DEFAULT_PORT = 9998; const DEFAULT_HOST = '127.0.0.1'; async function getPid() { try { const pid = await fs.readFile(PID_FILE, 'utf8'); return parseInt(pid.trim()); } catch { return null; } } async function setPid(pid) { await fs.writeFile(PID_FILE, pid.toString()); } async function removePid() { try { await fs.unlink(PID_FILE); } catch {} } function isProcessRunning(pid) { try { process.kill(pid, 0); return true; } catch { return false; } } async function startServer(options = {}) { const existingPid = await getPid(); if (existingPid && isProcessRunning(existingPid)) { console.log(`🦐 Shrimp Task Viewer is already running (PID: ${existingPid})`); console.log(` View at: http://${options.host || DEFAULT_HOST}:${options.port || DEFAULT_PORT}`); return; } // Clean up stale PID file if (existingPid) { await removePid(); } console.log('🦐 Starting Shrimp Task Viewer...'); const env = { ...process.env }; if (options.port) env.SHRIMP_VIEWER_PORT = options.port; if (options.host) env.SHRIMP_VIEWER_HOST = options.host; if (options.dataDir) env.SHRIMP_DATA_DIR = options.dataDir; if (options.configFile) env.SHRIMP_CONFIG_FILE = options.configFile; const serverPath = path.join(__dirname, 'server.js'); const child = spawn('node', [serverPath], { env, detached: true, stdio: 'ignore' }); child.unref(); await setPid(child.pid); // Give server time to start await new Promise(resolve => setTimeout(resolve, 1000)); if (isProcessRunning(child.pid)) { console.log(`✅ Shrimp Task Viewer started successfully!`); console.log(` PID: ${child.pid}`); console.log(` URL: http://${options.host || DEFAULT_HOST}:${options.port || DEFAULT_PORT}`); console.log(` Data: ${options.dataDir || process.cwd()}`); console.log('\n Use --stop to stop the server'); } else { console.error('❌ Failed to start server'); await removePid(); process.exit(1); } } async function stopServer() { const pid = await getPid(); if (!pid) { console.log('🦐 Shrimp Task Viewer is not running'); return; } if (!isProcessRunning(pid)) { console.log('🦐 Shrimp Task Viewer process not found, cleaning up...'); await removePid(); return; } console.log(`🛑 Stopping Shrimp Task Viewer (PID: ${pid})...`); try { process.kill(pid, 'SIGTERM'); // Wait for process to stop let attempts = 0; while (isProcessRunning(pid) && attempts < 50) { await new Promise(resolve => setTimeout(resolve, 100)); attempts++; } if (isProcessRunning(pid)) { console.log('🔨 Force killing process...'); process.kill(pid, 'SIGKILL'); } await removePid(); console.log('✅ Shrimp Task Viewer stopped'); } catch (err) { console.error('❌ Error stopping server:', err.message); await removePid(); } } async function restartServer(options = {}) { console.log('🔄 Restarting Shrimp Task Viewer...'); await stopServer(); await new Promise(resolve => setTimeout(resolve, 1000)); await startServer(options); } async function statusServer() { const pid = await getPid(); if (!pid) { console.log('🦐 Shrimp Task Viewer: Not running'); return; } if (isProcessRunning(pid)) { console.log(`🦐 Shrimp Task Viewer: Running (PID: ${pid})`); console.log(` URL: http://${DEFAULT_HOST}:${DEFAULT_PORT}`); } else { console.log('🦐 Shrimp Task Viewer: Not running (stale PID file)'); await removePid(); } } function showHelp() { console.log(` 🦐 Shrimp Task Manager Viewer CLI Usage: shrimp-viewer [command] [options] Commands: start Start the task viewer server (default) stop Stop the task viewer server restart Restart the task viewer server status Show server status help Show this help message Options: --port <number> Server port (default: ${DEFAULT_PORT}) --host <host> Server host (default: ${DEFAULT_HOST}) --data-dir <path> Base directory for task discovery (default: current directory) --config <file> Configuration file path (optional) Environment Variables: SHRIMP_VIEWER_PORT Server port SHRIMP_VIEWER_HOST Server host SHRIMP_DATA_DIR Base directory for task discovery SHRIMP_CONFIG_FILE Configuration file path Examples: shrimp-viewer # Start with defaults shrimp-viewer start --port 8080 # Start on port 8080 shrimp-viewer --data-dir ~/projects # Start with custom data directory shrimp-viewer stop # Stop the server shrimp-viewer restart # Restart the server shrimp-viewer status # Check status Configuration File Format: { "agents": [ { "id": "team1", "name": "Team 1", "path": "/path/to/team1/tasks.json" } ] } `); } // Parse command line arguments function parseArgs(args) { const options = {}; let command = 'start'; for (let i = 0; i < args.length; i++) { const arg = args[i]; if (['start', 'stop', 'restart', 'status', 'help'].includes(arg)) { command = arg; } else if (arg === '--port' && i + 1 < args.length) { options.port = args[++i]; } else if (arg === '--host' && i + 1 < args.length) { options.host = args[++i]; } else if (arg === '--data-dir' && i + 1 < args.length) { options.dataDir = args[++i]; } else if (arg === '--config' && i + 1 < args.length) { options.configFile = args[++i]; } } return { command, options }; } // Main function async function main() { const { command, options } = parseArgs(process.argv.slice(2)); try { switch (command) { case 'start': await startServer(options); break; case 'stop': await stopServer(); break; case 'restart': await restartServer(options); break; case 'status': await statusServer(); break; case 'help': showHelp(); break; default: console.error(`Unknown command: ${command}`); showHelp(); process.exit(1); } } catch (err) { console.error('Error:', err.message); process.exit(1); } } // Handle SIGINT for graceful shutdown when running directly process.on('SIGINT', async () => { console.log('\nReceived SIGINT, shutting down gracefully...'); await stopServer(); process.exit(0); }); // Run if called directly main(); export { startServer, stopServer, restartServer, statusServer, showHelp };

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/cjo4m06/mcp-shrimp-task-manager'

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