basant307/AI_Governance_Project
048
1/**2 * @license3 * Copyright 2025 Google LLC4 * SPDX-License-Identifier: Apache-2.05 */6 7import { exec, execFile } from 'node:child_process';8import { promisify } from 'node:util';9import os from 'node:os';10import path from 'node:path';11 12const execAsync = promisify(exec);13const execFileAsync = promisify(execFile);14 15const MAX_TRAVERSAL_DEPTH = 32;16 17async function getProcessInfo(pid: number): Promise<{18 parentPid: number;19 name: string;20 command: string;21}> {22 // Only used for Unix systems (macOS and Linux)23 try {24 const command = `ps -o ppid=,command= -p ${pid}`;25 const { stdout } = await execAsync(command);26 const trimmedStdout = stdout.trim();27 if (!trimmedStdout) {28 return { parentPid: 0, name: '', command: '' };29 }30 const parts = trimmedStdout.split(/\s+/);31 const ppidString = parts[0];32 const parentPid = parseInt(ppidString, 10);33 const fullCommand = trimmedStdout.substring(ppidString.length).trim();34 const processName = path.basename(fullCommand.split(' ')[0]);35 return {36 parentPid: isNaN(parentPid) ? 1 : parentPid,37 name: processName,38 command: fullCommand,39 };40 } catch (_e) {41 return { parentPid: 0, name: '', command: '' };42 }43}44/**45 * Finds the IDE process info on Unix-like systems.46 *47 * The strategy is to find the shell process that spawned the CLI, and then48 * find that shell's parent process (the IDE). To get the true IDE process,49 * we traverse one level higher to get the grandparent.50 *51 * @returns A promise that resolves to the PID and command of the IDE process.52 */53async function getIdeProcessInfoForUnix(): Promise<{54 pid: number;55 command: string;56}> {57 const shells = ['zsh', 'bash', 'sh', 'tcsh', 'csh', 'ksh', 'fish', 'dash'];58 let currentPid = process.pid;59 60 for (let i = 0; i < MAX_TRAVERSAL_DEPTH; i++) {61 try {62 const { parentPid, name } = await getProcessInfo(currentPid);63 64 const isShell = shells.some((shell) => name === shell);65 if (isShell) {66 // The direct parent of the shell is often a utility process (e.g. VS67 // Code's `ptyhost` process). To get the true IDE process, we need to68 // traverse one level higher to get the grandparent.69 let idePid = parentPid;70 try {71 const { parentPid: grandParentPid } = await getProcessInfo(parentPid);72 if (grandParentPid > 1) {73 idePid = grandParentPid;74 }75 } catch {76 // Ignore if getting grandparent fails, we'll just use the parent pid.77 }78 const { command: ideCommand } = await getProcessInfo(idePid);79 return { pid: idePid, command: ideCommand };80 }81 82 if (parentPid <= 1) {83 break; // Reached the root84 }85 currentPid = parentPid;86 } catch (_e) {87 // Process in chain died88 break;89 }90 }91 92 const { command } = await getProcessInfo(currentPid);93 return { pid: currentPid, command };94}95 96interface ProcessInfo {97 pid: number;98 parentPid: number;99 name: string;100 command: string;101}102 103interface RawProcessInfo {104 ProcessId?: number;105 ParentProcessId?: number;106 Name?: string;107 CommandLine?: string;108}109 110/**111 * Fetches the entire process table on Windows.112 */113async function getProcessTableWindows(): Promise<Map<number, ProcessInfo>> {114 const processMap = new Map<number, ProcessInfo>();115 try {116 const powershellCommand =117 'Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,Name,CommandLine | ConvertTo-Json -Compress';118 const { stdout } = await execFileAsync(119 'powershell',120 ['-NoProfile', '-NonInteractive', '-Command', powershellCommand],121 { maxBuffer: 10 * 1024 * 1024 },122 );123 124 if (!stdout.trim()) {125 return processMap;126 }127 128 let processes: RawProcessInfo | RawProcessInfo[];129 try {130 processes = JSON.parse(stdout);131 } catch (_e) {132 return processMap;133 }134 135 if (!Array.isArray(processes)) {136 processes = [processes];137 }138 139 for (const p of processes) {140 if (p && typeof p.ProcessId === 'number') {141 processMap.set(p.ProcessId, {142 pid: p.ProcessId,143 parentPid: p.ParentProcessId || 0,144 name: p.Name || '',145 command: p.CommandLine || '',146 });147 }148 }149 } catch (_e) {150 // Fallback or error handling if PowerShell fails151 }152 return processMap;153}154 155async function getIdeProcessInfoForWindows(): Promise<{156 pid: number;157 command: string;158}> {159 // Fetch the entire process table in one go.160 const processMap = await getProcessTableWindows();161 162 const myPid = process.pid;163 const myProc = processMap.get(myPid);164 165 if (!myProc) {166 // Fallback: return current process info if snapshot fails167 return { pid: myPid, command: '' };168 }169 170 // Perform tree traversal in memory171 const ancestors: ProcessInfo[] = [];172 let curr: ProcessInfo | undefined = myProc;173 174 for (let i = 0; i < MAX_TRAVERSAL_DEPTH && curr; i++) {175 ancestors.push(curr);176 177 if (curr.parentPid === 0 || !processMap.has(curr.parentPid)) {178 // Parent process not in map, stop traversal179 break;180 }181 curr = processMap.get(curr.parentPid);182 }183 184 // Use heuristic: return the great-grandparent (ancestors[length-3])185 if (ancestors.length >= 3) {186 const target = ancestors[ancestors.length - 3];187 return { pid: target.pid, command: target.command };188 } else if (ancestors.length > 0) {189 const target = ancestors[ancestors.length - 1];190 return { pid: target.pid, command: target.command };191 }192 193 return { pid: myPid, command: myProc.command };194}195 196/**197 * Traverses up the process tree to find the process ID and command of the IDE.198 *199 * This function uses different strategies depending on the operating system200 * to identify the main application process (e.g., the main VS Code window201 * process).202 *203 * If the IDE process cannot be reliably identified, it will return the204 * top-level ancestor process ID and command as a fallback.205 *206 * @returns A promise that resolves to the PID and command of the IDE process.207 */208export async function getIdeProcessInfo(): Promise<{209 pid: number;210 command: string;211}> {212 const platform = os.platform();213 214 if (platform === 'win32') {215 return getIdeProcessInfoForWindows();216 }217 218 return getIdeProcessInfoForUnix();219}220 