Files
TokenWatch/language-server-process-utils.js
T
Jannik eaec6ed5c8
Build and Push Docker Image / build-and-push (push) Canceled after 0s
feat: complete TokenWatch implementation with Docker, GH actions, and Live Sync
2026-08-28 16:35:08 +02:00

95 lines
3.0 KiB
JavaScript

const { exec } = require('child_process');
const os = require('os');
const COMMAND_TIMEOUT_MS = 5000;
const MAX_BUFFER_BYTES = 10 * 1024 * 1024;
function runCommand(command, timeout = COMMAND_TIMEOUT_MS) {
return new Promise((resolve) => {
exec(command, { timeout, maxBuffer: MAX_BUFFER_BYTES }, (error, stdout, stderr) => {
resolve({
error,
stdout: stdout || '',
stderr: stderr || '',
});
});
});
}
function parseWindowsProcessJson(stdout) {
if (!stdout.trim()) return [];
const parsed = JSON.parse(stdout);
const rows = Array.isArray(parsed) ? parsed : [parsed];
return rows
.filter(row => row && row.ProcessId)
.map(row => ({
pid: Number(row.ProcessId),
ppid: Number(row.ParentProcessId || 0),
commandLine: row.CommandLine || row.ExecutablePath || '',
}));
}
function extractListeningPorts(stdout, pid) {
const ports = [];
for (const line of stdout.split(/\r?\n/)) {
if (!/(LISTEN|ABH)/i.test(line) || !belongsToProcess(line, pid)) continue;
for (const match of line.matchAll(/(?:127\.0\.0\.1|\[?::1\]?):(\d{2,5})/g)) {
const port = Number(match[1]);
if (port > 0 && port < 65536) ports.push(port);
}
}
return ports;
}
function parseUnixProcessList(stdout) {
return stdout
.split(/\r?\n/)
.map(line => line.trim().match(/^(\d+)\s+(\d+)\s+(.+)$/))
.filter(Boolean)
.map(match => ({
pid: Number(match[1]),
ppid: Number(match[2]),
commandLine: match[3],
}))
.filter(processInfo => /language_server/i.test(processInfo.commandLine));
}
async function listLanguageServerProcesses() {
if (os.platform() === 'win32') {
const command = [
'powershell -NoProfile -Command',
'"Get-CimInstance Win32_Process -Filter \\"name like \'language_server%\'\\"',
'| Select-Object ProcessId,ParentProcessId,CommandLine,ExecutablePath',
'| ConvertTo-Json -Depth 2"',
].join(' ');
const result = await runCommand(command);
if (result.error && !result.stdout.trim()) {
throw new Error(result.stderr.trim() || result.error.message);
}
return parseWindowsProcessJson(result.stdout);
}
const result = await runCommand('ps -A -ww -o pid=,ppid=,args=');
if (result.error && !result.stdout.trim()) {
throw new Error(result.stderr.trim() || result.error.message);
}
return parseUnixProcessList(result.stdout);
}
function extractArgument(commandLine, argumentName) {
const escapedName = argumentName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const pattern = new RegExp(
`--${escapedName}(?:=|\\s+)(?:"([^"]*)"|'([^']*)'|([^\\s]+))`,
'i'
);
const match = commandLine.match(pattern);
return match ? (match[1] ?? match[2] ?? match[3] ?? null) : null;
}
module.exports = {
extractArgument,
listLanguageServerProcesses,
runCommand,
};