summaryrefslogtreecommitdiff
path: root/packages/core/src/ide/process-utils.ts
blob: 40e16a734fbe6c28798eefbeec14911f1f0f26af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

import { exec } from 'child_process';
import { promisify } from 'util';
import os from 'os';

const execAsync = promisify(exec);

/**
 * Traverses up the process tree from the current process to find the top-level ancestor process ID.
 * This is useful for identifying the main application process that spawned the current script,
 * such as the main VS Code window process.
 *
 * @returns A promise that resolves to the numeric PID of the top-level process.
 * @throws Will throw an error if the underlying shell commands fail unexpectedly.
 */
export async function getIdeProcessId(): Promise<number> {
  const platform = os.platform();
  let currentPid = process.pid;

  // Loop upwards through the process tree, with a depth limit to prevent infinite loops.
  const MAX_TRAVERSAL_DEPTH = 32;
  for (let i = 0; i < MAX_TRAVERSAL_DEPTH; i++) {
    let parentPid: number;

    try {
      // Use wmic for Windows
      if (platform === 'win32') {
        const command = `wmic process where "ProcessId=${currentPid}" get ParentProcessId /value`;
        const { stdout } = await execAsync(command);
        const match = stdout.match(/ParentProcessId=(\d+)/);
        parentPid = match ? parseInt(match[1], 10) : 0; // Top of the tree is 0
      }
      // Use ps for macOS, Linux, and other Unix-like systems
      else {
        const command = `ps -o ppid= -p ${currentPid}`;
        const { stdout } = await execAsync(command);
        const ppid = parseInt(stdout.trim(), 10);
        parentPid = isNaN(ppid) ? 1 : ppid; // Top of the tree is 1
      }
    } catch (_) {
      // This can happen if a process in the chain dies during execution.
      // We'll break the loop and return the last valid PID we found.
      break;
    }

    // Define the root PID for the current OS
    const rootPid = platform === 'win32' ? 0 : 1;

    // If the parent is the root process or invalid, we've found our target.
    if (parentPid === rootPid || parentPid <= 0) {
      break;
    }
    // Move one level up the tree for the next iteration.
    currentPid = parentPid;
  }
  return currentPid;
}