mirror of
https://github.com/stablyai/orca.git
synced 2026-09-30 16:02:56 +00:00
fix(wsl): name an explicit Windows cwd for wsl.exe spawns
Removing the worktree Orca was launched from broke every wsl.exe spawn for the rest of the session. The WSL command builders passed `cwd: undefined` meaning "the directory is inside the command" -- but CreateProcessW reads NULL as "inherit the parent's", and the parent's was a \\wsl.localhost path Linux had just deleted. Fixes #16463
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
"../src/main/wsl-distro-retry.ts",
|
||||
"../src/main/wsl-running-distro-cache.ts",
|
||||
"../src/main/wsl.ts",
|
||||
"../src/main/wsl-interop-spawn-directory.ts",
|
||||
"../src/main/persistence/applying-settings/ui-state-read.ts",
|
||||
"../src/main/persistence/applying-settings/ui-state-update.ts",
|
||||
"../src/main/persistence/applying-settings/ui-selection-normalization.ts",
|
||||
|
||||
@@ -340,7 +340,13 @@ describe('WslCliInstaller', () => {
|
||||
expect(bridge).toContain('$ForwardArgs = @($args[$ForwardArgStart..($args.Count - 1)])')
|
||||
expect(bridge).toContain('if ([string]::IsNullOrEmpty($WslCwd))')
|
||||
expect(bridge).toContain('$env:ORCA_CLI_CWD = $WslCwd')
|
||||
expect(bridge).toContain('Push-Location -LiteralPath (Split-Path -Parent $OrcaLauncher)')
|
||||
expect(bridge).toContain('$LauncherDirectory = Split-Path -Parent $OrcaLauncher')
|
||||
expect(bridge).toContain('Push-Location -LiteralPath $LauncherDirectory')
|
||||
// Why (#16463): Push-Location moves only the PowerShell provider location.
|
||||
// Without an explicit WorkingDirectory the started app inherits the caller's
|
||||
// Win32 cwd — the user's worktree on \\wsl.localhost — and every wsl.exe
|
||||
// spawn it makes dies with ENOENT once that worktree is removed.
|
||||
expect(bridge).toContain('$StartInfo.WorkingDirectory = $LauncherDirectory')
|
||||
expect(bridge).toContain('function ConvertTo-NativeCommandLineArgument')
|
||||
expect(bridge).toContain("[void]$Quoted.Append([char]'\\', $BackslashCount * 2 + 1)")
|
||||
expect(bridge).toContain('$StartInfo.UseShellExecute = $false')
|
||||
|
||||
@@ -88,7 +88,8 @@ try {
|
||||
} else {
|
||||
$env:ORCA_CLI_CWD = $WslCwd
|
||||
}
|
||||
Push-Location -LiteralPath (Split-Path -Parent $OrcaLauncher)
|
||||
$LauncherDirectory = Split-Path -Parent $OrcaLauncher
|
||||
Push-Location -LiteralPath $LauncherDirectory
|
||||
# Why: Windows PowerShell 5.1 cannot losslessly splat strings to native argv.
|
||||
$StartInfo = [System.Diagnostics.ProcessStartInfo]::new()
|
||||
$StartInfo.FileName = $OrcaLauncher
|
||||
@@ -96,6 +97,13 @@ try {
|
||||
ConvertTo-NativeCommandLineArgument $_
|
||||
}) -join ' ')
|
||||
$StartInfo.UseShellExecute = $false
|
||||
# Why (#16463): Push-Location moves the PowerShell provider location, not the
|
||||
# Win32 current directory, and an empty WorkingDirectory with UseShellExecute
|
||||
# disabled means "inherit the caller's". Launched from a WSL shell that is the
|
||||
# user's worktree on the 9P share, so without this the app stands in a
|
||||
# directory Linux can delete -- after which every CreateProcessW it makes
|
||||
# fails ERROR_PATH_NOT_FOUND, reported as: spawn wsl.exe ENOENT.
|
||||
$StartInfo.WorkingDirectory = $LauncherDirectory
|
||||
$Process = [System.Diagnostics.Process]::Start($StartInfo)
|
||||
if ($null -eq $Process) {
|
||||
throw 'Unable to start the Orca Windows CLI launcher.'
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
type WslProcessGroupTermination
|
||||
} from '../wsl-process-group-termination'
|
||||
import { translateArgForWsl, translateArgsForWsl } from './wsl-path-translation'
|
||||
import { resolveWslInteropSpawnCwd } from '../../wsl-interop-spawn-directory'
|
||||
|
||||
// Env-assignment prefix for WSL-routed git, where spawn env can't cross the wsl.exe boundary; values are shell-safe unquoted.
|
||||
const GIT_OUTPUT_LOCALE_SHELL_PREFIX = Object.entries(UNTRANSLATED_GIT_OUTPUT_ENV)
|
||||
@@ -111,7 +112,7 @@ export function resolveCommand(
|
||||
...(linuxCwd ? ['-C', linuxCwd] : []),
|
||||
...translatedArgs
|
||||
],
|
||||
cwd: undefined,
|
||||
cwd: resolveWslInteropSpawnCwd(),
|
||||
wsl,
|
||||
wslMode: 'direct-git'
|
||||
},
|
||||
@@ -130,7 +131,7 @@ export function resolveCommand(
|
||||
{
|
||||
binary: 'wsl.exe',
|
||||
args: buildWslExecArgs(wsl.distro, ['sh', '-lc', captured.command]),
|
||||
cwd: undefined,
|
||||
cwd: resolveWslInteropSpawnCwd(),
|
||||
wsl,
|
||||
wslMode: 'login-shell',
|
||||
captured
|
||||
@@ -142,7 +143,7 @@ export function resolveCommand(
|
||||
{
|
||||
binary: 'wsl.exe',
|
||||
args: buildWslExecArgs(wsl.distro, ['sh', '-lc', buildWslLoginShellCommand(shellCmd)]),
|
||||
cwd: undefined,
|
||||
cwd: resolveWslInteropSpawnCwd(),
|
||||
wsl,
|
||||
wslMode: 'login-shell'
|
||||
},
|
||||
@@ -154,8 +155,11 @@ export function resolveCommand(
|
||||
{
|
||||
binary: 'wsl.exe',
|
||||
args: buildWslExecArgs(wsl.distro, ['bash', '-c', shellCmd]),
|
||||
// Why: the `cd` inside bash -c handles the directory; a UNC cwd on the Node process is redundant and can break Node internals.
|
||||
cwd: undefined,
|
||||
// Why: the `cd` inside bash -c handles the Linux directory. This names an
|
||||
// explicit Windows directory anyway, because `undefined` makes
|
||||
// CreateProcessW inherit the parent's — which is a deletable WSL UNC path
|
||||
// when Orca was launched from a worktree (#16463).
|
||||
cwd: resolveWslInteropSpawnCwd(),
|
||||
wsl,
|
||||
wslMode: 'non-login-shell'
|
||||
},
|
||||
|
||||
@@ -626,7 +626,11 @@ describe('runner execFile timeout handling', () => {
|
||||
expect(execFileMock).toHaveBeenCalledWith(
|
||||
'wsl.exe',
|
||||
['-d', 'Ubuntu', '--exec', 'sh', '-lc', expect.any(String)],
|
||||
expect.objectContaining({ cwd: undefined }),
|
||||
// Why a concrete directory (#16463): `undefined` makes CreateProcessW inherit
|
||||
// Orca's own cwd, a deletable WSL UNC path when it was launched from a
|
||||
// worktree. The Linux directory still rides inside the command (/mnt/c/repo,
|
||||
// asserted below).
|
||||
expect.objectContaining({ cwd: expect.any(String) }),
|
||||
expect.any(Function)
|
||||
)
|
||||
// A read also warms the direct-git environment probe in the background, so
|
||||
@@ -659,7 +663,11 @@ describe('runner execFile timeout handling', () => {
|
||||
expect(execFileMock).toHaveBeenCalledWith(
|
||||
'wsl.exe',
|
||||
['-d', 'Ubuntu', '--exec', 'bash', '-c', expect.any(String)],
|
||||
expect.objectContaining({ cwd: undefined }),
|
||||
// Why a concrete directory (#16463): `undefined` makes CreateProcessW inherit
|
||||
// Orca's own cwd, a deletable WSL UNC path when it was launched from a
|
||||
// worktree. The Linux directory still rides inside the command (/mnt/c/repo,
|
||||
// asserted below).
|
||||
expect.objectContaining({ cwd: expect.any(String) }),
|
||||
expect.any(Function)
|
||||
)
|
||||
const shellCommand = execFileMock.mock.calls[0]?.[1]?.[5] as string
|
||||
|
||||
@@ -99,7 +99,10 @@ describe('ghExecFileAsync WSL fallback', () => {
|
||||
'-c',
|
||||
"cd '/home/jinwoo/stably/noqa' && 'gh' 'issue' 'list' '--repo' 'stablyhq/noqa' '--json' 'number,title'"
|
||||
],
|
||||
expect.objectContaining({ cwd: undefined }),
|
||||
// Why a concrete directory (#16463): `undefined` makes CreateProcessW inherit
|
||||
// Orca's own cwd, a deletable WSL UNC path when it was launched from a
|
||||
// worktree. The Linux directory still rides inside the command.
|
||||
expect.objectContaining({ cwd: expect.any(String) }),
|
||||
expect.any(Function)
|
||||
)
|
||||
expect(execFileMock).toHaveBeenNthCalledWith(
|
||||
@@ -382,7 +385,11 @@ describe('ghExecFileAsync WSL fallback', () => {
|
||||
2,
|
||||
'wsl.exe',
|
||||
['-d', 'Ubuntu', '--exec', 'bash', '-c', "'gh' 'api' 'rate_limit'"],
|
||||
expect.objectContaining({ cwd: undefined }),
|
||||
// Why a concrete directory (#16463): `undefined` makes CreateProcessW inherit
|
||||
// Orca's own cwd, a deletable WSL UNC path when it was launched from a
|
||||
// worktree. This global call has no repo directory at all, so nothing about
|
||||
// where it runs changes.
|
||||
expect.objectContaining({ cwd: expect.any(String) }),
|
||||
expect.any(Function)
|
||||
)
|
||||
})
|
||||
@@ -501,7 +508,11 @@ describe('ghExecFileAsync WSL fallback', () => {
|
||||
2,
|
||||
'wsl.exe',
|
||||
['-d', 'Ubuntu', '--exec', 'bash', '-c', "'glab' 'api' 'projects'"],
|
||||
expect.objectContaining({ cwd: undefined }),
|
||||
// Why a concrete directory (#16463): `undefined` makes CreateProcessW inherit
|
||||
// Orca's own cwd, a deletable WSL UNC path when it was launched from a
|
||||
// worktree. This global call has no repo directory at all, so nothing about
|
||||
// where it runs changes.
|
||||
expect.objectContaining({ cwd: expect.any(String) }),
|
||||
expect.any(Function)
|
||||
)
|
||||
})
|
||||
|
||||
@@ -76,7 +76,11 @@ describe('glab known-hosts probe on Windows', () => {
|
||||
expect(execFileMock).toHaveBeenCalledWith(
|
||||
'wsl.exe',
|
||||
['-d', 'Ubuntu', '--exec', 'bash', '-c', "'glab' 'auth' 'status'"],
|
||||
expect.objectContaining({ cwd: undefined }),
|
||||
// Why a concrete directory (#16463): `undefined` makes CreateProcessW inherit
|
||||
// Orca's own cwd, a deletable WSL UNC path when it was launched from a
|
||||
// worktree. This probe has no repo directory at all, so nothing about where
|
||||
// it runs changes. The native `glab` assertion above keeps `undefined`.
|
||||
expect.objectContaining({ cwd: expect.any(String) }),
|
||||
expect.any(Function)
|
||||
)
|
||||
})
|
||||
|
||||
@@ -164,7 +164,11 @@ describe('generateCommitMessageFromContext', () => {
|
||||
'wsl.exe',
|
||||
['-d', 'Ubuntu 24.04', '--exec', 'sh', '-lc', expect.any(String)],
|
||||
expect.objectContaining({
|
||||
cwd: undefined,
|
||||
// Why a concrete directory (#16463): `undefined` makes CreateProcessW inherit
|
||||
// Orca's own cwd, a deletable WSL UNC path when it was launched from a
|
||||
// worktree. The Linux directory still rides inside the command (/mnt/c/repo,
|
||||
// asserted below), so the Windows-side cwd never decides where the agent runs.
|
||||
cwd: expect.any(String),
|
||||
windowsHide: true,
|
||||
env: expect.objectContaining({ CODEX_HOME: '/home/tester/.codex' })
|
||||
})
|
||||
|
||||
@@ -235,7 +235,11 @@ describe('discoverCommitMessageModelsLocal', () => {
|
||||
'wsl.exe',
|
||||
['-d', 'Ubuntu', '--exec', 'sh', '-lc', expect.any(String)],
|
||||
expect.objectContaining({
|
||||
cwd: undefined,
|
||||
// Why a concrete directory (#16463): `undefined` makes CreateProcessW inherit
|
||||
// Orca's own cwd, a deletable WSL UNC path when it was launched from a
|
||||
// worktree. The Linux directory still rides inside the command (/mnt/c/repo,
|
||||
// asserted below), so the Windows-side cwd never decides where discovery runs.
|
||||
cwd: expect.any(String),
|
||||
windowsHide: true
|
||||
})
|
||||
)
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import { mkdtempSync, rmSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
||||
|
||||
import {
|
||||
resetWslInteropSpawnDirectoryCache,
|
||||
resolveWslInteropSpawnCwd
|
||||
} from './wsl-interop-spawn-directory'
|
||||
|
||||
// Regression coverage for #16463 ("Removing the worktree Orca was launched from
|
||||
// breaks every wsl.exe spawn for the rest of the session"). The WSL command
|
||||
// builders passed `cwd: undefined` meaning "the directory is inside the
|
||||
// command", but CreateProcessW reads NULL as "inherit the parent's" — and the
|
||||
// parent's was a `\\wsl.localhost\...` worktree Linux had just deleted. 1805 of
|
||||
// 1806 git calls then failed `spawn wsl.exe ENOENT` until the app restarted.
|
||||
|
||||
const createdRoots: string[] = []
|
||||
|
||||
function makeExistingDirectory(): string {
|
||||
const dir = mkdtempSync(join(tmpdir(), 'orca-wsl-spawn-cwd-'))
|
||||
createdRoots.push(dir)
|
||||
return dir
|
||||
}
|
||||
|
||||
const ENV_KEYS = ['ORCA_USER_DATA_PATH', 'USERPROFILE', 'HOMEDRIVE', 'HOMEPATH'] as const
|
||||
const savedEnv = new Map<string, string | undefined>()
|
||||
|
||||
beforeEach(() => {
|
||||
for (const key of ENV_KEYS) {
|
||||
savedEnv.set(key, process.env[key])
|
||||
delete process.env[key]
|
||||
}
|
||||
resetWslInteropSpawnDirectoryCache()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
for (const key of ENV_KEYS) {
|
||||
const saved = savedEnv.get(key)
|
||||
if (saved === undefined) {
|
||||
delete process.env[key]
|
||||
} else {
|
||||
process.env[key] = saved
|
||||
}
|
||||
}
|
||||
resetWslInteropSpawnDirectoryCache()
|
||||
while (createdRoots.length > 0) {
|
||||
rmSync(createdRoots.pop()!, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
describe('resolveWslInteropSpawnCwd', () => {
|
||||
it('names the app-owned directory first, so no worktree can be the answer', () => {
|
||||
const userData = makeExistingDirectory()
|
||||
process.env.ORCA_USER_DATA_PATH = userData
|
||||
process.env.USERPROFILE = makeExistingDirectory()
|
||||
|
||||
expect(resolveWslInteropSpawnCwd()).toBe(userData)
|
||||
})
|
||||
|
||||
it('skips a candidate that does not resolve instead of naming it', () => {
|
||||
process.env.ORCA_USER_DATA_PATH = join(tmpdir(), 'orca-wsl-spawn-cwd-never-created')
|
||||
const profile = makeExistingDirectory()
|
||||
process.env.USERPROFILE = profile
|
||||
|
||||
expect(resolveWslInteropSpawnCwd()).toBe(profile)
|
||||
})
|
||||
|
||||
it('always names some directory rather than letting the spawn inherit one', () => {
|
||||
// Why: inheriting is the failure mode. With no configured candidate at all
|
||||
// the home directory and system root still stand between a spawn and the
|
||||
// parent's cwd.
|
||||
expect(resolveWslInteropSpawnCwd()).toEqual(expect.any(String))
|
||||
})
|
||||
|
||||
it('re-answers after the directory it memoized goes away mid-session', () => {
|
||||
// This is the incident: the chosen directory was valid when the process
|
||||
// started and was deleted underneath it hours later. A memo that is never
|
||||
// re-validated reproduces the original bug one layer up.
|
||||
const doomed = makeExistingDirectory()
|
||||
process.env.ORCA_USER_DATA_PATH = doomed
|
||||
const survivor = makeExistingDirectory()
|
||||
expect(resolveWslInteropSpawnCwd()).toBe(doomed)
|
||||
|
||||
rmSync(doomed, { recursive: true, force: true })
|
||||
process.env.ORCA_USER_DATA_PATH = survivor
|
||||
|
||||
expect(resolveWslInteropSpawnCwd()).toBe(survivor)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,70 @@
|
||||
import { statSync } from 'node:fs'
|
||||
import { homedir } from 'node:os'
|
||||
|
||||
/**
|
||||
* A Windows directory that is safe to hand `wsl.exe` as its working directory.
|
||||
*
|
||||
* Why this exists (#16463): the WSL command builders set `cwd: undefined`,
|
||||
* meaning "the directory is already expressed inside the command" — but that is
|
||||
* not what `undefined` means to `CreateProcessW`. libuv passes NULL for
|
||||
* `lpCurrentDirectory`, and NULL means *inherit the parent's*. Orca launched by
|
||||
* `orca-ide` from a WSL shell inherits `\\wsl.localhost\<distro>\...\<worktree>`
|
||||
* as its Win32 cwd; Linux can delete that directory out from under a Windows
|
||||
* process across the 9P share, and from then on `CreateProcessW` fails
|
||||
* `ERROR_PATH_NOT_FOUND` — surfaced by libuv as `spawn wsl.exe ENOENT`, for the
|
||||
* rest of the process's life, for every repository.
|
||||
*
|
||||
* Naming an explicit directory removes the dependency on process-global state
|
||||
* entirely, so a repaired or unrepaired `process.cwd()` cannot decide whether
|
||||
* git works. It is never the cwd the command runs in: WSL invocations carry
|
||||
* their Linux directory in `git -C`, a `cd` inside `bash -c`, or the `sh -c`
|
||||
* wrapper `withGuestCwd` builds.
|
||||
*/
|
||||
|
||||
let cachedSpawnCwd: string | null = null
|
||||
|
||||
function isExistingDirectory(path: string | undefined | null): path is string {
|
||||
if (!path) {
|
||||
return false
|
||||
}
|
||||
try {
|
||||
return statSync(path).isDirectory()
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/** Test seam: forget the memoized directory so a later probe re-validates. */
|
||||
export function resetWslInteropSpawnDirectoryCache(): void {
|
||||
cachedSpawnCwd = null
|
||||
}
|
||||
|
||||
export function resolveWslInteropSpawnCwd(): string | undefined {
|
||||
// Why re-validate: the answer is only useful while it still resolves, and the
|
||||
// user's profile directory can go away on a roaming/mapped-drive host.
|
||||
if (isExistingDirectory(cachedSpawnCwd)) {
|
||||
return cachedSpawnCwd
|
||||
}
|
||||
const env = process.env
|
||||
// Why this order: an app-owned directory first (it outlives every worktree),
|
||||
// then the user's profile, then the system root as a floor that always exists.
|
||||
// A root is fine here — nothing scans this directory, it is only the value
|
||||
// `CreateProcessW` receives for `lpCurrentDirectory`.
|
||||
const candidates: (string | undefined)[] = [
|
||||
env.ORCA_USER_DATA_PATH,
|
||||
env.USERPROFILE,
|
||||
env.HOMEDRIVE && env.HOMEPATH ? `${env.HOMEDRIVE}${env.HOMEPATH}` : undefined,
|
||||
homedir(),
|
||||
env.SystemDrive ? `${env.SystemDrive}\\` : 'C:\\'
|
||||
]
|
||||
for (const candidate of candidates) {
|
||||
if (isExistingDirectory(candidate)) {
|
||||
cachedSpawnCwd = candidate
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
cachedSpawnCwd = null
|
||||
// Why undefined rather than a guess: inheriting is still better than naming a
|
||||
// directory we just proved does not exist.
|
||||
return undefined
|
||||
}
|
||||
+16
-5
@@ -7,6 +7,7 @@ import {
|
||||
_setWslAvailabilityCacheForTests,
|
||||
dropStaleWslAvailabilityFailure
|
||||
} from './wsl-availability'
|
||||
import { resolveWslInteropSpawnCwd } from './wsl-interop-spawn-directory'
|
||||
import {
|
||||
_resetRunningWslDistroCacheForTests,
|
||||
resolveRunningWslDistros
|
||||
@@ -76,7 +77,8 @@ export function wslUncDirectoryExists(uncPath: string): boolean | null {
|
||||
const stdout = execFileSync('wsl.exe', getWslDirectoryProbeArgs(info), {
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
timeout: 5000,
|
||||
encoding: 'utf8'
|
||||
encoding: 'utf8',
|
||||
cwd: resolveWslInteropSpawnCwd()
|
||||
})
|
||||
return parseWslDirectoryProbeOutput(stdout)
|
||||
} catch {
|
||||
@@ -93,7 +95,8 @@ export function wslUncDirectoryExistsAsync(uncPath: string): Promise<boolean | n
|
||||
return Promise.resolve(null)
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
execFile('wsl.exe', getWslDirectoryProbeArgs(info), { timeout: 5000 }, (_error, stdout) => {
|
||||
const probeOpts = { timeout: 5000, cwd: resolveWslInteropSpawnCwd() }
|
||||
execFile('wsl.exe', getWslDirectoryProbeArgs(info), probeOpts, (_error, stdout) => {
|
||||
// Why: wsl.exe uses numeric exits for both guest results and host failures; only the guest marker is authoritative.
|
||||
resolve(parseWslDirectoryProbeOutput(stdout))
|
||||
})
|
||||
@@ -181,7 +184,8 @@ export function listWslDistros(): string[] {
|
||||
const output = execFileSync('wsl.exe', ['--list', '--quiet'], {
|
||||
encoding: 'utf-8',
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
timeout: 5000
|
||||
timeout: 5000,
|
||||
cwd: resolveWslInteropSpawnCwd()
|
||||
})
|
||||
return cacheWslDistroList(parseWslDistros(output), probeSequence)
|
||||
} catch {
|
||||
@@ -283,7 +287,8 @@ export function getWslHome(distro: string): string | null {
|
||||
const home = execFileSync('wsl.exe', ['-d', distro, '--exec', 'bash', '-c', 'echo $HOME'], {
|
||||
encoding: 'utf-8',
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
timeout: 5000
|
||||
timeout: 5000,
|
||||
cwd: resolveWslInteropSpawnCwd()
|
||||
}).trim()
|
||||
|
||||
if (!home || !home.startsWith('/')) {
|
||||
@@ -382,7 +387,13 @@ function execFileUtf8(command: string, args: string[], env?: NodeJS.ProcessEnv):
|
||||
execFile(
|
||||
command,
|
||||
args,
|
||||
{ encoding: 'utf-8', env, timeout: 5000, windowsHide: true },
|
||||
{
|
||||
encoding: 'utf-8',
|
||||
env,
|
||||
timeout: 5000,
|
||||
windowsHide: true,
|
||||
cwd: resolveWslInteropSpawnCwd()
|
||||
},
|
||||
(error, stdout) => {
|
||||
if (error) {
|
||||
reject(error)
|
||||
|
||||
@@ -23,3 +23,9 @@ main/ipc/preflight-command-exec.ts
|
||||
main/ipc/preflight-test-harness.ts
|
||||
main/ipc/preflight-wsl-agent-detection.ts
|
||||
main/wsl.ts
|
||||
# Scanned only because the filename starts with `wsl`; it answers nothing about a
|
||||
# distro. The swallow is a `statSync` on a LOCAL WINDOWS directory, and only the
|
||||
# positive answer is memoized -- and re-validated on every call, which is the
|
||||
# point of the module (#16463). A failed stat drops to the next candidate for
|
||||
# that one call and is re-asked on the next, so there is no value to pin.
|
||||
main/wsl-interop-spawn-directory.ts
|
||||
|
||||
Reference in New Issue
Block a user