@juspay/neurolink
Version:
Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio
153 lines • 4.81 kB
JavaScript
/**
* Server Utilities for NeuroLink CLI
* Shared utility functions for server management commands (serve.ts and server.ts)
*/
import fs from "fs";
import os from "os";
import path from "path";
// ============================================
// State Directory Management
// ============================================
/**
* Get the base directory for NeuroLink state files
* @returns Path to ~/.neurolink directory
*/
export function getNeuroLinkDir() {
return path.join(os.homedir(), ".neurolink");
}
/**
* Ensure the NeuroLink state directory exists
* Creates ~/.neurolink if it doesn't exist
*/
export function ensureStateDir() {
const dir = getNeuroLinkDir();
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
}
// ============================================
// Process Management
// ============================================
/**
* Check if a process with the given PID is currently running
*
* Uses `process.kill(pid, 0)` which tests if a process exists without sending a signal.
*
* **Platform Behavior:**
* - **Unix/Linux/macOS**: Returns `true` if process exists, `false` if not.
* If the process exists but belongs to another user, returns `true` (via EPERM check).
* - **Windows**: Behavior differs - `process.kill(pid, 0)` may throw even for existing
* processes if they are system processes or have restricted access. This function
* handles EPERM by returning `true`, but other Windows-specific errors may occur.
* For more reliable Windows process detection, consider using `tasklist` command.
*
* @param pid - Process ID to check
* @returns true if the process is running, false otherwise
*/
export function isProcessRunning(pid) {
try {
// Sending signal 0 tests if process exists without actually sending a signal
process.kill(pid, 0);
return true;
}
catch (err) {
const code = err.code;
// EPERM means process exists but we lack permission to send signals to it
return code === "EPERM";
}
}
// ============================================
// Time Formatting
// ============================================
/**
* Format a duration in milliseconds to a human-readable uptime string
* @param ms - Duration in milliseconds
* @returns Formatted string like "2d 5h 30m" or "45m 30s"
*/
export function formatUptime(ms) {
const seconds = Math.floor(ms / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
if (days > 0) {
return `${days}d ${hours % 24}h ${minutes % 60}m`;
}
if (hours > 0) {
return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
}
if (minutes > 0) {
return `${minutes}m ${seconds % 60}s`;
}
return `${seconds}s`;
}
// ============================================
// Generic State File Management
// ============================================
/**
* Generic state file manager for server state persistence
* @template T - Type of the state object
*/
export class StateFileManager {
filePath;
/**
* Create a new state file manager
* @param filename - Name of the state file (e.g., "serve-state.json")
* @param baseDir - Optional base directory (defaults to ~/.neurolink)
*/
constructor(filename, baseDir) {
this.filePath = path.join(baseDir ?? getNeuroLinkDir(), filename);
}
/**
* Get the full path to the state file
*/
getFilePath() {
return this.filePath;
}
/**
* Save state to the state file
* @param state - State object to save
*/
save(state) {
const dir = path.dirname(this.filePath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
}
fs.writeFileSync(this.filePath, JSON.stringify(state, null, 2));
}
/**
* Load state from the state file
* @returns The state object, or null if the file doesn't exist or is invalid
*/
load() {
try {
if (fs.existsSync(this.filePath)) {
const content = fs.readFileSync(this.filePath, "utf8");
return JSON.parse(content);
}
}
catch {
// Ignore errors - return null for missing or invalid files
}
return null;
}
/**
* Clear (delete) the state file
*/
clear() {
try {
if (fs.existsSync(this.filePath)) {
fs.unlinkSync(this.filePath);
}
}
catch {
// Ignore errors when clearing
}
}
/**
* Check if state file exists
*/
exists() {
return fs.existsSync(this.filePath);
}
}
//# sourceMappingURL=serverUtils.js.map