fix: resolve posix_spawnp failure in daemon's node-pty spawn (#762)

This commit is contained in:
Jinwoo Hong 2026-04-17 13:37:56 -04:00 committed by GitHub
parent c655c13629
commit 7c6d408f16
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 35 additions and 5 deletions

View file

@ -18,7 +18,7 @@ index 1ac5758bedd8cf54f32280dea4e4aeb5afdee30d..e619813759c6f14694838bdfbd0ea5f8
+++ b/deps/winpty/src/winpty.gyp +++ b/deps/winpty/src/winpty.gyp
@@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
# make -j4 CXX=i686-w64-mingw32-g++ LDFLAGS="-static -static-libgcc -static-libstdc++" # make -j4 CXX=i686-w64-mingw32-g++ LDFLAGS="-static -static-libgcc -static-libstdc++"
'variables': { 'variables': {
- 'WINPTY_COMMIT_HASH%': '<!(cmd /c "cd shared && GetCommitHash.bat")', - 'WINPTY_COMMIT_HASH%': '<!(cmd /c "cd shared && GetCommitHash.bat")',
+ 'WINPTY_COMMIT_HASH%': '<!(cmd /c "cd shared && .\\GetCommitHash.bat")', + 'WINPTY_COMMIT_HASH%': '<!(cmd /c "cd shared && .\\GetCommitHash.bat")',
@ -54,3 +54,22 @@ index 1ac5758bedd8cf54f32280dea4e4aeb5afdee30d..e619813759c6f14694838bdfbd0ea5f8
'msvs_settings': { 'msvs_settings': {
# Specify this setting here to override a setting from somewhere # Specify this setting here to override a setting from somewhere
# else, such as node's common.gypi. # else, such as node's common.gypi.
diff --git a/lib/unixTerminal.js b/lib/unixTerminal.js
index 1ec12f796a822c78fba9ad7f6448c3987e325c23..cec8b67aef02f8199e5606a0d257088bf1865877 100644
--- a/lib/unixTerminal.js
+++ b/lib/unixTerminal.js
@@ -28,8 +28,12 @@ var native = utils_1.loadNativeModule('pty');
var pty = native.module;
var helperPath = native.dir + '/spawn-helper';
helperPath = path.resolve(__dirname, helperPath);
-helperPath = helperPath.replace('app.asar', 'app.asar.unpacked');
-helperPath = helperPath.replace('node_modules.asar', 'node_modules.asar.unpacked');
+if (!helperPath.includes('app.asar.unpacked')) {
+ helperPath = helperPath.replace('app.asar', 'app.asar.unpacked');
+}
+if (!helperPath.includes('node_modules.asar.unpacked')) {
+ helperPath = helperPath.replace('node_modules.asar', 'node_modules.asar.unpacked');
+}
var DEFAULT_FILE = 'sh';
var DEFAULT_NAME = 'xterm';
var DESTROY_SOCKET_TIMEOUT_MS = 200;

View file

@ -9,7 +9,7 @@ patchedDependencies:
hash: 12b1dc2811a859b6703ebb63be654d1682ff1bb6045713a54e7d47e28deb7776 hash: 12b1dc2811a859b6703ebb63be654d1682ff1bb6045713a54e7d47e28deb7776
path: config/patches/@xterm__addon-fit@0.11.0.patch path: config/patches/@xterm__addon-fit@0.11.0.patch
node-pty@1.1.0: node-pty@1.1.0:
hash: d35190e5f762b8d3289f13df0ff40c7ad19dfb64ca488494cce9bea79ab5c0ad hash: 02e16954edbb8e511963557e3b2c80f8a6fb230cd0c6a75ade936022f3347322
path: patches/node-pty@1.1.0.patch path: patches/node-pty@1.1.0.patch
importers: importers:
@ -138,7 +138,7 @@ importers:
version: 0.55.1 version: 0.55.1
node-pty: node-pty:
specifier: ^1.1.0 specifier: ^1.1.0
version: 1.1.0(patch_hash=d35190e5f762b8d3289f13df0ff40c7ad19dfb64ca488494cce9bea79ab5c0ad) version: 1.1.0(patch_hash=02e16954edbb8e511963557e3b2c80f8a6fb230cd0c6a75ade936022f3347322)
radix-ui: radix-ui:
specifier: ^1.4.3 specifier: ^1.4.3
version: 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) version: 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
@ -10839,7 +10839,7 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
node-pty@1.1.0(patch_hash=d35190e5f762b8d3289f13df0ff40c7ad19dfb64ca488494cce9bea79ab5c0ad): node-pty@1.1.0(patch_hash=02e16954edbb8e511963557e3b2c80f8a6fb230cd0c6a75ade936022f3347322):
dependencies: dependencies:
node-addon-api: 7.1.1 node-addon-api: 7.1.1

View file

@ -78,7 +78,12 @@ function createOutOfProcessLauncher(): DaemonLauncher {
// stdio 'ignore' prevents the child from holding the parent's stdout // stdio 'ignore' prevents the child from holding the parent's stdout
// open, which would prevent Electron from exiting cleanly. // open, which would prevent Electron from exiting cleanly.
detached: true, detached: true,
stdio: ['ignore', 'ignore', 'ignore', 'ipc'] stdio: ['ignore', 'ignore', 'ignore', 'ipc'],
// Why: ELECTRON_RUN_AS_NODE makes the forked process run as a plain
// Node.js process instead of an Electron renderer/main process. Without
// it, Electron's GPU/display initialization can interfere with native
// module operations like node-pty's posix_spawn of the spawn-helper.
env: { ...process.env, ELECTRON_RUN_AS_NODE: '1' }
}) })
// Wait for the daemon to signal readiness via IPC // Wait for the daemon to signal readiness via IPC

View file

@ -1,6 +1,7 @@
import * as pty from 'node-pty' import * as pty from 'node-pty'
import type { SubprocessHandle } from './session' import type { SubprocessHandle } from './session'
import { getShellReadyLaunchConfig, resolvePtyShellPath } from './shell-ready' import { getShellReadyLaunchConfig, resolvePtyShellPath } from './shell-ready'
import { ensureNodePtySpawnHelperExecutable } from '../providers/local-pty-utils'
export type PtySubprocessOptions = { export type PtySubprocessOptions = {
sessionId: string sessionId: string
@ -52,6 +53,11 @@ export function createPtySubprocess(opts: PtySubprocessOptions): SubprocessHandl
shellArgs = shellReadyLaunch?.args ?? ['-l'] shellArgs = shellReadyLaunch?.args ?? ['-l']
} }
// Why: asar packaging can strip the +x bit from node-pty's spawn-helper
// binary. The main process fixes this via LocalPtyProvider, but the daemon
// runs in a separate forked process with its own code path.
ensureNodePtySpawnHelperExecutable()
const proc = pty.spawn(shellPath, shellArgs, { const proc = pty.spawn(shellPath, shellArgs, {
name: 'xterm-256color', name: 'xterm-256color',
cols: opts.cols, cols: opts.cols,