| 20 | * @throws Will throw an error if the underlying shell commands fail unexpectedly. |
| 21 | */ |
| 22 | export async function getIdeProcessId(): Promise<number> { |
| 23 | const platform = os.platform(); |
| 24 | let currentPid = process.pid; |
| 25 | |
| 26 | // Loop upwards through the process tree, with a depth limit to prevent infinite loops. |
| 27 | const MAX_TRAVERSAL_DEPTH = 32; // Maximum penetration depth for process hierarchy |
| 28 | for (let i = 0; i < MAX_TRAVERSAL_DEPTH; i++) { |
| 29 | let parentPid: number; |
| 30 | |
| 31 | try { |
| 32 | // Use wmic for Windows |
| 33 | if (platform === 'win32') { |
| 34 | const command = `wmic process where "ProcessId=${currentPid}" get ParentProcessId /value`; |
| 35 | const { stdout } = await execAsync(command); |
| 36 | const match = stdout.match(/ParentProcessId=(\d+)/); |
| 37 | parentPid = match ? parseInt(match[1], 10) : 0; // Top of the tree is 0 |
| 38 | } |
| 39 | // Use ps for macOS, Linux, and other Unix-like systems |
| 40 | else { |
| 41 | const command = `ps -o ppid= -p ${currentPid}`; |
| 42 | const { stdout } = await execAsync(command); |
| 43 | const ppid = parseInt(stdout.trim(), 10); |
| 44 | parentPid = isNaN(ppid) ? 1 : ppid; // Top of the tree is 1 |
| 45 | } |
| 46 | } catch (_) { |
| 47 | // This can happen if a process in the chain dies during execution. |
| 48 | // We'll break the loop and return the last valid PID we found. |
| 49 | break; |
| 50 | } |
| 51 | |
| 52 | // Define the root PID for the current OS |
| 53 | const rootPid = platform === 'win32' ? 0 : 1; |
| 54 | |
| 55 | // If the parent is the root process or invalid, we've found our target. |
| 56 | if (parentPid === rootPid || parentPid <= 0) { |
| 57 | break; |
| 58 | } |
| 59 | // Move one level up the tree for the next iteration. |
| 60 | currentPid = parentPid; |
| 61 | } |
| 62 | return currentPid; |
| 63 | } |