UNPKG

@wonderwhy-er/desktop-commander

Version:

MCP server for terminal operations and file editing

88 lines (87 loc) • 3.09 kB
import { exec } from 'child_process'; import { promisify } from 'util'; import os from 'os'; import { KillProcessArgsSchema } from './schemas.js'; const execAsync = promisify(exec); /** * Parse `tasklist /FO CSV /NH` output (#662). Fields per row, all quoted: * [Image Name, PID, Session Name, Session#, Mem Usage]. CSV instead of the * default table: image names can contain spaces and Mem Usage contains * thousands separators, so positional whitespace splitting can't work. * tasklist has no live per-process CPU%, so cpu is reported as n/a. */ export function parseWindowsTasklistCsv(stdout) { return stdout.split(/\r?\n/) .filter(Boolean) .map(line => { const fields = (line.match(/"([^"]*)"/g) || []).map(f => f.slice(1, -1)); return { pid: parseInt(fields[1], 10), command: fields[0] ?? '', cpu: 'n/a', memory: fields[4] ?? '', }; }) .filter(p => !Number.isNaN(p.pid)); } /** * Parse `ps aux` output: 11 whitespace-separated columns, COMMAND from * column 10 onward (it can contain spaces, so it must be rejoined, not * taken as the last token). */ export function parsePsAux(stdout) { return stdout.split(/\r?\n/) .slice(1) .filter(Boolean) .map(line => { const parts = line.trim().split(/\s+/); return { pid: parseInt(parts[1], 10), command: parts.slice(10).join(' ') || parts[parts.length - 1], cpu: parts[2], memory: parts[3], }; }) .filter(p => !Number.isNaN(p.pid)); } export async function listProcesses() { const isWindows = os.platform() === 'win32'; const command = isWindows ? 'tasklist /FO CSV /NH' : 'ps aux'; try { const { stdout } = await execAsync(command); const processes = isWindows ? parseWindowsTasklistCsv(stdout) : parsePsAux(stdout); return { content: [{ type: "text", text: processes.map(p => `PID: ${p.pid}, Command: ${p.command}, CPU: ${p.cpu}, Memory: ${p.memory}`).join('\n') }], }; } catch (error) { return { content: [{ type: "text", text: `Error: Failed to list processes: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } } export async function killProcess(args) { const parsed = KillProcessArgsSchema.safeParse(args); if (!parsed.success) { return { content: [{ type: "text", text: `Error: Invalid arguments for kill_process: ${parsed.error}` }], isError: true, }; } try { process.kill(parsed.data.pid); return { content: [{ type: "text", text: `Successfully terminated process ${parsed.data.pid}` }], }; } catch (error) { return { content: [{ type: "text", text: `Error: Failed to kill process: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } }