Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
- Fixed the Python kernel bootstrap on native Windows: the venv python now resolves under `Scripts\python.exe` (uv layout). ([Discussion #1401](https://github.com/PrimeIntellect-ai/prime-agent/discussions/1401), [Discussion #1969](https://github.com/PrimeIntellect-ai/prime-agent/discussions/1969))
- Fixed `~/` path expansion producing mixed-separator paths like `C:\Users\u/rest` on Windows. ([Discussion #1442](https://github.com/PrimeIntellect-ai/prime-agent/discussions/1442), [Discussion #1469](https://github.com/PrimeIntellect-ai/prime-agent/discussions/1469))
- Fixed daemon worker handshakes timing out on slow machines: per-attempt hello/auth waits now consume the whole 30s connect budget instead of restarting a fixed 1s clock on every retry. ([Discussion #1622](https://github.com/PrimeIntellect-ai/prime-agent/discussions/1622), [Discussion #1678](https://github.com/PrimeIntellect-ai/prime-agent/discussions/1678))
- Fixed console windows flashing on Windows: all background spawns now run with hidden windows. ([Discussion #1461](https://github.com/PrimeIntellect-ai/prime-agent/discussions/1461))
- Fixed bash resolution picking WSL's System32 `bash.exe` over a per-user Git Bash on PATH. ([Discussion #1437](https://github.com/PrimeIntellect-ai/prime-agent/discussions/1437))
- Fixed the built-in Herdr reporter never connecting on Windows by dialing the socket inside the named-pipe namespace. ([Discussion #1399](https://github.com/PrimeIntellect-ai/prime-agent/discussions/1399))
4 changes: 2 additions & 2 deletions packages/coding-agent/src/cli/daemon-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { clearLine, createInterface, cursorTo, type Interface } from "node:readl
import { setTimeout as delay } from "node:timers/promises";
import type { AgentMessage } from "@earendil-works/pi-agent-core";
import chalk from "chalk";
import { spawn } from "child_process";
import { expandTildePath } from "../config.js";
import type { AgentSessionEvent } from "../core/agent-session.js";
import type { AgentSessionRuntimeConfig } from "../core/agent-session-config.js";
Expand All @@ -14,6 +13,7 @@ import type { DaemonOutbound, DaemonResponse } from "../modes/daemon/daemon-prot
import { matchesSessionIdSuffix } from "../modes/daemon/daemon-session-id.js";
import type { SessionSummary } from "../modes/daemon/daemon-session-list.js";
import { defaultDaemonSocketPath, normalizeSocketPath } from "../modes/daemon/daemon-socket.js";
import { spawnHidden } from "../utils/child-process.js";
import { isLocalPath } from "../utils/paths.js";
import { isValidThinkingLevel } from "./args.js";
import { formatSessionListTable } from "./daemon-list-format.js";
Expand Down Expand Up @@ -688,7 +688,7 @@ async function runStart(parsed: ParsedDaemonClientCommand): Promise<void> {
parsed.socketPath,
...sessionArgs.daemonArgs.filter((arg) => arg !== "--background" && arg !== "-d"),
];
const child = spawn(process.execPath, daemonArgs, {
const child = spawnHidden(process.execPath, daemonArgs, {
cwd: sessionArgs.config?.cwd ?? process.cwd(),
detached: true,
env: process.env,
Expand Down
4 changes: 2 additions & 2 deletions packages/coding-agent/src/cli/daemon-launch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
* the heavy main module graph loads. main.ts reuses the same memoized promise.
*/

import { spawn } from "node:child_process";
import { existsSync, readFileSync, statSync } from "node:fs";
import { resolve } from "node:path";
import { appendRotatingLog, expandTildePath, getClientErrorLogPath, getDaemonLogPath, VERSION } from "../config.js";
Expand All @@ -23,6 +22,7 @@ import {
DAEMON_WORKER_SUPERVISOR_SOCKET_ENV,
DAEMON_WORKER_TOKEN_ENV,
} from "../modes/daemon/daemon-worker-protocol.js";
import { spawnHidden } from "../utils/child-process.js";
import { isHelpCommandRequest, PUBLIC_COMMAND_NAMES, REMOVED_COMMAND_NAMES } from "./command-registry.js";
import { createCliSubprocessEnv, formatCurrentCliCommand } from "./subprocess-launch.js";

Expand Down Expand Up @@ -394,7 +394,7 @@ Then retry the original command.`,
delete env[SESSION_LEASE_OWNER_ID_ENV];

const logOffset = currentDaemonLogSize(socketPath);
const child = spawn(
const child = spawnHidden(
process.execPath,
[...process.execArgv, entrypoint, "--mode", "daemon", "--daemon-socket", socketPath],
{
Expand Down
15 changes: 7 additions & 8 deletions packages/coding-agent/src/cli/daemon-ps.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { spawnSync } from "node:child_process";
import { existsSync, lstatSync, readdirSync, readFileSync, rmSync, unlinkSync } from "node:fs";
import { basename, dirname, join, resolve } from "node:path";
import chalk from "chalk";
Expand All @@ -19,7 +18,7 @@ import {
import { defaultDaemonSocketDir, defaultDaemonSocketPath, normalizeSocketPath } from "../modes/daemon/daemon-socket.js";
import { acquireDaemonShutdownAdmission } from "../modes/daemon/daemon-supervisor-ownership.js";
import type { DaemonWorkerDescriptor } from "../modes/daemon/daemon-worker-protocol.js";
import { signalProcessGroupOrProcess } from "../utils/child-process.js";
import { signalProcessGroupOrProcess, spawnSyncHidden } from "../utils/child-process.js";
import { formatDaemonListTable } from "./daemon-ps-format.js";
import { promptYesNo } from "./daemon-stop-confirm.js";

Expand Down Expand Up @@ -175,18 +174,18 @@ function scanListeningDaemons(): DiscoveredDaemonProcess[] {
if (process.platform === "win32") {
return [];
}
const ss = spawnSync("ss", ["-lxp"], { encoding: "utf8" });
const ss = spawnSyncHidden("ss", ["-lxp"], { encoding: "utf8" });
if (!ss.error && ss.status === 0 && typeof ss.stdout === "string") {
return enrichUptimes(parseSsListeners(ss.stdout, APP_NAME));
}
const lsof = spawnSync("lsof", ["-nP", "-F", "pn", "-U", "-a", "-c", APP_NAME], { encoding: "utf8" });
const lsof = spawnSyncHidden("lsof", ["-nP", "-F", "pn", "-U", "-a", "-c", APP_NAME], { encoding: "utf8" });
const byName = !lsof.error && typeof lsof.stdout === "string" ? parseLsofListeners(lsof.stdout) : [];
let byPid: DiscoveredDaemonProcess[] = [];
const ps = spawnSync("ps", ["-axo", "pid=,comm=,args="], { encoding: "utf8" });
const ps = spawnSyncHidden("ps", ["-axo", "pid=,comm=,args="], { encoding: "utf8" });
if (!ps.error && ps.status === 0 && typeof ps.stdout === "string") {
const pids = parsePrimeAgentProcessIds(ps.stdout, APP_NAME);
if (pids.length > 0) {
const lsofByPid = spawnSync("lsof", ["-nP", "-F", "pn", "-U", "-a", "-p", pids.join(",")], {
const lsofByPid = spawnSyncHidden("lsof", ["-nP", "-F", "pn", "-U", "-a", "-p", pids.join(",")], {
encoding: "utf8",
});
if (!lsofByPid.error && typeof lsofByPid.stdout === "string") {
Expand All @@ -207,7 +206,7 @@ function enrichUptimes(daemons: DiscoveredDaemonProcess[]): DiscoveredDaemonProc
if (pids.length === 0) {
return daemons;
}
const ps = spawnSync("ps", ["-o", "pid=,etimes=", "-p", pids.join(",")], { encoding: "utf8" });
const ps = spawnSyncHidden("ps", ["-o", "pid=,etimes=", "-p", pids.join(",")], { encoding: "utf8" });
if (ps.error || typeof ps.stdout !== "string") {
return daemons;
}
Expand Down Expand Up @@ -803,7 +802,7 @@ function recordResidualListenerFailures(
}
}
function describeDaemonParent(pid: number): string {
const result = spawnSync("ps", ["-o", "ppid=,tty=,command=", "-p", String(pid)], { encoding: "utf8" });
const result = spawnSyncHidden("ps", ["-o", "ppid=,tty=,command=", "-p", String(pid)], { encoding: "utf8" });
if (result.error || result.status !== 0 || typeof result.stdout !== "string") {
return "";
}
Expand Down
4 changes: 2 additions & 2 deletions packages/coding-agent/src/cli/daemon-update-restart.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { spawn } from "node:child_process";
import { createHash, randomUUID } from "node:crypto";
import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
import { resolve } from "node:path";
Expand All @@ -14,6 +13,7 @@ import {
DAEMON_WORKER_SUPERVISOR_SOCKET_ENV,
DAEMON_WORKER_TOKEN_ENV,
} from "../modes/daemon/daemon-worker-protocol.js";
import { spawnHidden } from "../utils/child-process.js";
import { createCliSubprocessLaunchSpec } from "./subprocess-launch.js";

export const DAEMON_UPDATE_RESTART_COORDINATOR_FLAG = "--internal-update-restart-coordinator";
Expand Down Expand Up @@ -556,7 +556,7 @@ export async function launchDaemonUpdateRestartCoordinator(
statusPath,
...(originActiveSessionId ? [DAEMON_UPDATE_RESTART_ORIGIN_FLAG, originActiveSessionId] : []),
]);
const child = spawn(launch.command, launch.args, {
const child = spawnHidden(launch.command, launch.args, {
cwd: options.cwd ?? process.cwd(),
detached: true,
env: coordinatorEnvironment(agentDir),
Expand Down
5 changes: 3 additions & 2 deletions packages/coding-agent/src/cli/owned-session-worker.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type ChildProcess, type StdioOptions, spawn } from "node:child_process";
import type { ChildProcess, StdioOptions } from "node:child_process";
import { randomUUID } from "node:crypto";
import { chmodSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
Expand All @@ -14,6 +14,7 @@ import {
} from "../core/orphan-process-journal.js";
import { SESSION_LEASE_OWNER_ID_ENV, SESSION_LEASES_ENABLED_ENV } from "../core/session-lease.js";
import { attachJsonlLineReader, serializeJsonLine } from "../modes/rpc/jsonl.js";
import { spawnHidden } from "../utils/child-process.js";
import { isHelpCommandRequest, PUBLIC_COMMAND_NAMES, REMOVED_COMMAND_NAMES } from "./command-registry.js";
import { type CliSubprocessLaunchSpec, createCliSubprocessLaunchSpec } from "./subprocess-launch.js";

Expand Down Expand Up @@ -335,7 +336,7 @@ export async function runOwnedSessionWorkerFrontend(
const stdio: StdioOptions = interactive
? ["inherit", "inherit", "inherit", "ipc"]
: [bridgeStdin ? "pipe" : "inherit", "pipe", "pipe", "ipc"];
const child = spawn(launch.command, launch.args, {
const child = spawnHidden(launch.command, launch.args, {
Comment thread
snimu marked this conversation as resolved.
cwd: process.cwd(),
detached: process.platform !== "win32",
env: {
Expand Down
15 changes: 6 additions & 9 deletions packages/coding-agent/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { spawnSync } from "child_process";
import { createHash } from "crypto";
import {
accessSync,
Expand All @@ -13,9 +12,9 @@ import {
statSync,
} from "fs";
import { homedir } from "os";
import { basename, dirname, join, resolve, sep, win32 } from "path";
import { basename, dirname, join, posix, resolve, sep, win32 } from "path";
import { fileURLToPath } from "url";
import { shouldUseWindowsShell } from "./utils/child-process.js";
import { shouldUseWindowsShell, spawnSyncHidden } from "./utils/child-process.js";
import { normalizeSocketPath } from "./utils/daemon-socket-path.js";

// =============================================================================
Expand Down Expand Up @@ -207,7 +206,7 @@ function readCommandOutput(
args: string[],
options: { requireSuccess?: boolean } = {},
): string | undefined {
const result = spawnSync(command, args, {
const result = spawnSyncHidden(command, args, {
encoding: "utf-8",
stdio: ["ignore", "pipe", "pipe"],
shell: shouldUseWindowsShell(command),
Expand Down Expand Up @@ -366,9 +365,7 @@ export function getPackageDir(): string {
// Allow override via environment variable (useful for Nix/Guix where store paths tokenize poorly)
const envDir = process.env.PI_PACKAGE_DIR;
if (envDir) {
if (envDir === "~") return homedir();
if (envDir.startsWith("~/")) return homedir() + envDir.slice(1);
return envDir;
return expandTildePath(envDir);
}

if (isBunBinary) {
Expand Down Expand Up @@ -503,9 +500,9 @@ export const ENV_AGENT_DIR = `${envPrefix}_CODING_AGENT_DIR`;
export const ENV_SESSION_DIR = `${envPrefix}_SESSION_DIR`;
export const ENV_LEGACY_SESSION_DIR = `${envPrefix}_CODING_AGENT_SESSION_DIR`;

export function expandTildePath(path: string): string {
export function expandTildePath(path: string, platform: NodeJS.Platform = process.platform): string {
if (path === "~") return homedir();
if (path.startsWith("~/")) return homedir() + path.slice(1);
if (path.startsWith("~/")) return (platform === "win32" ? win32 : posix).join(homedir(), path.slice(2));
return path;
}

Expand Down
5 changes: 2 additions & 3 deletions packages/coding-agent/src/core/autonomous.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { spawn } from "node:child_process";
import { createHash } from "node:crypto";
import { createReadStream } from "node:fs";
import { lstat, readlink } from "node:fs/promises";
import { resolve } from "node:path";
import type { AssistantMessage, Usage, UserMessage } from "@earendil-works/pi-ai";
import { waitForChildProcess } from "../utils/child-process.js";
import { spawnHidden, waitForChildProcess } from "../utils/child-process.js";
import { killProcessTree, trackDetachedChildPid, untrackDetachedChildPid } from "../utils/shell.js";

export interface AgentAutonomousConfig {
Expand Down Expand Up @@ -491,7 +490,7 @@ function runChildProcess(
): Promise<ChildProcessResult> {
options.signal?.throwIfAborted();
return new Promise((resolve) => {
const child = spawn(command, args, {
const child = spawnHidden(command, args, {
cwd: options.cwd,
detached: process.platform !== "win32",
shell: options.shell === true,
Expand Down
5 changes: 2 additions & 3 deletions packages/coding-agent/src/core/exec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
* Shared command execution utilities for extensions and custom tools.
*/

import { spawn } from "node:child_process";
import { waitForChildProcess } from "../utils/child-process.js";
import { spawnHidden, waitForChildProcess } from "../utils/child-process.js";

/**
* Options for executing shell commands.
Expand Down Expand Up @@ -58,7 +57,7 @@ export async function execCommand(
options?: ExecOptions,
): Promise<ExecResult> {
return new Promise((resolve) => {
const proc = spawn(command, args, {
const proc = spawnHidden(command, args, {
cwd,
shell: false,
stdio: ["ignore", "pipe", "pipe"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*/

import { createConnection } from "node:net";
import { basename } from "node:path";
import { basename, win32 } from "node:path";
import type { ExtensionAPI, ExtensionFactory } from "../types.js";

type AgentState = "working" | "blocked" | "idle";
Expand All @@ -42,6 +42,16 @@ export function hasFileBasedHerdrIntegration(loadedExtensionPaths: string[]): bo
});
}

/** Windows dials local-domain sockets inside \\.\pipe\; Herdr exports a unix-style path, so map it (namespaced paths pass through). */
export function herdrSocketTarget(socketPath: string, platform: NodeJS.Platform = process.platform): string {
// The pipe namespace is case-insensitive, so only the prefix check lowercases.
const lowered = socketPath.toLowerCase();
if (platform !== "win32" || lowered.startsWith("\\\\.\\pipe\\") || lowered.startsWith("\\\\?\\pipe\\")) {
return socketPath;
}
return win32.join("\\\\.\\pipe\\", socketPath);
}

interface QueuedState {
state: AgentState;
message?: string;
Expand Down Expand Up @@ -171,7 +181,7 @@ function herdrAgentStateExtensionImpl(pi: ExtensionAPI, getLoadedExtensionPaths:
resolve();
};

const socket = createConnection(socketPath!);
const socket = createConnection(herdrSocketTarget(socketPath!));
socket.on("error", finish);
socket.on("connect", () => socket.write(`${JSON.stringify(request)}\n`));
socket.on("data", finish);
Expand Down
7 changes: 4 additions & 3 deletions packages/coding-agent/src/core/footer-data-provider.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { type ExecFileException, execFile, spawnSync } from "child_process";
import type { ExecFileException } from "child_process";
import { existsSync, type FSWatcher, readFileSync, unwatchFile, watchFile } from "fs";
import { dirname, join } from "path";
import { execFileHidden, spawnSyncHidden } from "../utils/child-process.js";
import { closeWatcher, FS_WATCH_RETRY_DELAY_MS, watchWithErrorHandler } from "../utils/fs-watch.js";
import { findGitPaths, type GitPaths } from "../utils/git.js";

/** Ask git for the current branch. Returns null on detached HEAD or if git is unavailable. */
function resolveBranchWithGitSync(repoDir: string): string | null {
const result = spawnSync("git", ["--no-optional-locks", "symbolic-ref", "--quiet", "--short", "HEAD"], {
const result = spawnSyncHidden("git", ["--no-optional-locks", "symbolic-ref", "--quiet", "--short", "HEAD"], {
cwd: repoDir,
encoding: "utf8",
stdio: ["ignore", "pipe", "ignore"],
Expand All @@ -18,7 +19,7 @@ function resolveBranchWithGitSync(repoDir: string): string | null {
/** Ask git for the current branch asynchronously. Returns null on detached HEAD or if git is unavailable. */
function resolveBranchWithGitAsync(repoDir: string): Promise<string | null> {
return new Promise((resolvePromise) => {
execFile(
execFileHidden(
"git",
["--no-optional-locks", "symbolic-ref", "--quiet", "--short", "HEAD"],
{
Expand Down
12 changes: 8 additions & 4 deletions packages/coding-agent/src/core/kernel/bootstrap.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { spawn } from "node:child_process";
import { createHash } from "node:crypto";
import { constants, existsSync, readdirSync, readFileSync } from "node:fs";
import { access, mkdir, readdir, readFile, rm, stat, writeFile } from "node:fs/promises";
Expand All @@ -9,6 +8,7 @@
import { setTimeout as sleep } from "node:timers/promises";
import { fileURLToPath } from "node:url";
import { getPackageDir } from "../../config.js";
import { spawnHidden } from "../../utils/child-process.js";
import type { PythonSkillRuntimeInfo } from "../skills.js";

const BOOTSTRAP_SCHEMA = 9;
Expand Down Expand Up @@ -371,7 +371,7 @@

function run(command: string, args: string[], options: { stdio?: "ignore" | "inherit" } = {}): Promise<void> {
return new Promise((resolve, reject) => {
const child = spawn(command, args, {
const child = spawnHidden(command, args, {
env: process.env,
stdio: options.stdio ?? "ignore",
});
Expand All @@ -389,7 +389,7 @@

async function pythonImports(python: string, moduleName: string): Promise<boolean> {
try {
await run(python, ["-c", `import ${moduleName}`], { stdio: "ignore" });

Check warning

Code scanning / CodeQL

Unsafe shell command constructed from library input Medium

This string concatenation which depends on
library input
is later used in a
shell command
.
This string concatenation which depends on
library input
is later used in a
shell command
.
This string concatenation which depends on
library input
is later used in a
shell command
.
This string concatenation which depends on
library input
is later used in a
shell command
.
This string concatenation which depends on
library input
is later used in a
shell command
.
This string concatenation which depends on
library input
is later used in a
shell command
.
return true;
} catch {
return false;
Expand Down Expand Up @@ -709,14 +709,18 @@
return `sha256:${hash.digest("hex")}`;
}

export function kernelVenvPython(venv: string, platform: NodeJS.Platform = process.platform): string {
return platform === "win32" ? path.join(venv, "Scripts", "python.exe") : path.join(venv, "bin", "python");
}

async function bootstrapVenv(
venv: string,
pythonSkills: readonly BootstrapPythonSkill[],
options: EnsureKernelPythonOptions,
): Promise<void> {
await mkdir(path.dirname(venv), { recursive: true });
const uv = await ensureUv(options);
const python = path.join(venv, "bin", "python");
const python = kernelVenvPython(venv);
const sourceDir = await resolveRuntimeSourceDir();
const runtimeRequirement = sourceDir ?? RUNTIME_REQUIREMENT;
const runtimeIdentity = await resolveRuntimeIdentity();
Expand Down Expand Up @@ -873,7 +877,7 @@
}

const venv = await resolveWritableKernelVenvDir();
const python = path.join(venv, "bin", "python");
const python = kernelVenvPython(venv);
const runtimeIdentity = await resolveRuntimeIdentity();
if (await kernelReady(python, venv, runtimeIdentity, pythonSkills)) return python;

Expand Down
5 changes: 3 additions & 2 deletions packages/coding-agent/src/core/kernel/repl-manager.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Kernel client for the REPL runtime: the kernel is a JSON-lines subprocess
// (`python -m rlm.repl`) — requests on stdin, events on stdout, stderr kept as
// a diagnostics tail. The protocol is documented in prime-agent-runtime/src/rlm/repl.md.
import { type ChildProcess, spawn } from "node:child_process";
import type { ChildProcess } from "node:child_process";
import { closeSync, existsSync, mkdirSync, openSync, renameSync, rmSync, statSync, writeSync } from "node:fs";
import { dirname } from "node:path";
import { StringDecoder } from "node:string_decoder";
import { v4 as uuid } from "uuid";
import { spawnHidden } from "../../utils/child-process.js";
import { reapKernelOrphanProcesses, recordOrphanProcessState } from "../orphan-process-journal.js";
import { ensureKernelPython } from "./bootstrap.js";
import {
Expand Down Expand Up @@ -305,7 +306,7 @@ export class ReplKernelManager {
throw new Error("Kernel was disposed during startup");
}

const child = spawn(python, ["-m", "rlm.repl"], {
const child = spawnHidden(python, ["-m", "rlm.repl"], {
cwd: this.options.cwd,
// bash.py journals its process groups under this pid so the host can
// reap them if the runtime dies without running its shutdown hook.
Expand Down
4 changes: 2 additions & 2 deletions packages/coding-agent/src/core/orphan-process-journal.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { spawnSync } from "node:child_process";
import { closeSync, fsyncSync, openSync, readFileSync, rmSync, writeSync } from "node:fs";
import { win32 } from "node:path";
import { spawnSyncHidden } from "../utils/child-process.js";
import { getProcessStartId } from "./session-lease.js";

export const ORPHAN_PROCESS_JOURNAL_ENV = "PRIME_AGENT_INTERNAL_ORPHAN_PROCESS_JOURNAL";
Expand Down Expand Up @@ -150,7 +150,7 @@ export function reapKernelOrphanProcesses(kernelPid: number): void {
export function killOrphanProcess(pid: number): boolean {
if (process.platform === "win32") {
// In-kernel bash() kill paths use taskkill /T; the reaper must kill the same tree, not just the shell pid.
const result = spawnSync(
const result = spawnSyncHidden(
win32.join(process.env.SystemRoot ?? "C:\\Windows", "System32", "taskkill.exe"),
["/F", "/T", "/PID", String(pid)],
{
Expand Down
Loading
Loading