Files
NetMesh/electron/bridges/ptyProcessTree.cjs

100 lines
3.1 KiB
JavaScript
Raw Permalink Normal View History

const { execFile } = require("node:child_process");
const POSIX_PROCESS_LIST_TIMEOUT_MS = 3_000;
const POSIX_PROCESS_LIST_MAX_BUFFER_BYTES = 4 * 1024 * 1024;
function createProcessTree({ platform, listPosix, listWindows } = {}) {
const sessionPidMap = new Map();
function registerPid(sessionId, pid) {
if (!sessionId || typeof pid !== "number") return;
if (sessionPidMap.has(sessionId) && sessionPidMap.get(sessionId) !== pid) {
console.warn(
`[ptyProcessTree] sessionId "${sessionId}" already registered with pid ${sessionPidMap.get(sessionId)}; overwriting with ${pid}.`,
);
}
sessionPidMap.set(sessionId, pid);
}
function unregisterPid(sessionId) {
sessionPidMap.delete(sessionId);
}
async function getChildProcesses(sessionId) {
const pid = sessionPidMap.get(sessionId);
if (!pid) return [];
if (platform === "win32") {
return listWindows ? listWindows(pid) : [];
}
return listPosix ? listPosix(pid) : [];
}
return { registerPid, unregisterPid, getChildProcesses };
}
function defaultListPosix(ppid, { execFileFn = execFile } = {}) {
return new Promise((resolve) => {
// `ps -A -o pid=,ppid=,args=` works on both BSD (macOS) and GNU (Linux).
// `args=` shows the full command line (not truncated like `comm=`).
// The trailing `=` on each column suppresses the header row.
execFileFn("ps", ["-A", "-o", "pid=,ppid=,args="], {
timeout: POSIX_PROCESS_LIST_TIMEOUT_MS,
maxBuffer: POSIX_PROCESS_LIST_MAX_BUFFER_BYTES,
windowsHide: true,
}, (err, stdout) => {
if (err || typeof stdout !== "string") return resolve([]);
const out = [];
for (const line of stdout.split("\n")) {
const trimmed = line.trim();
if (!trimmed) continue;
const m = trimmed.match(/^(\d+)\s+(\d+)\s+(.+)$/);
if (!m) continue;
if (Number(m[2]) !== ppid) continue;
out.push({ pid: Number(m[1]), command: m[3].trim() });
}
resolve(out);
});
});
}
function defaultListWindows(ppid) {
return new Promise((resolve) => {
let wpt;
try {
wpt = require("@vscode/windows-process-tree");
} catch {
return resolve([]);
}
try {
wpt.getProcessTree(ppid, (tree) => {
if (!tree || !Array.isArray(tree.children)) return resolve([]);
resolve(tree.children.map((c) => ({ pid: c.pid, command: c.name })));
});
} catch {
resolve([]);
}
});
}
function createDefaultProcessTree() {
const platform = process.platform;
return createProcessTree({
platform,
listPosix: platform === "win32" ? undefined : defaultListPosix,
listWindows: platform === "win32" ? defaultListWindows : undefined,
});
}
const defaultTree = createDefaultProcessTree();
module.exports = {
POSIX_PROCESS_LIST_TIMEOUT_MS,
POSIX_PROCESS_LIST_MAX_BUFFER_BYTES,
createProcessTree,
defaultListPosix,
processTree: defaultTree,
registerPid: (id, pid) => defaultTree.registerPid(id, pid),
unregisterPid: (id) => defaultTree.unregisterPid(id),
getChildProcesses: (id) => defaultTree.getChildProcesses(id),
};