import type { CliInstallStatus } from '../../shared/cli-install-types' import { execFileSync } from 'node:child_process' import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' const runWslProcessMock = vi.hoisted(() => vi.fn()) vi.mock('../wsl/wsl-runner', () => ({ runWslProcess: runWslProcessMock })) import { WslCliInstaller, _internals } from './wsl-cli-installer' import { reconcileManagedWslCliRegistrations } from './wsl-cli-registration-reconciliation' function makeHostStatus( launcherPath = 'C:\\Users\\me\\AppData\\Local\\Programs\\Orca\\resources\\bin\\orca.exe' ) { return { platform: 'win32', commandName: 'orca', commandPath: launcherPath, pathDirectory: 'C:\\Users\\me\\AppData\\Local\\Programs\\Orca\\resources\\bin', pathConfigured: true, launcherPath, installMethod: 'wrapper', supported: true, state: 'installed', currentTarget: launcherPath, unsupportedReason: null, detail: null } satisfies CliInstallStatus } // Frozen from v1.4.138-rc.2: the upgrade regression only reproduces when the // persisted managed script still names the pre-native Windows batch launcher. const PRE_RC4_MANAGED_WSL_LAUNCHER = `#!/usr/bin/env bash set -euo pipefail # Orca managed WSL CLI launcher # ORCA_WIN_LAUNCHER_B64=QzpcUHJvZ3JhbSBGaWxlc1xPcmNhXHJlc291cmNlc1xiaW5cb3JjYS5jbWQ= ORCA_WIN_LAUNCHER='C:\\Program Files\\Orca\\resources\\bin\\orca.cmd' ORCA_BRIDGE_PS1='/home/alice/.local/share/orca/orca-wsl-bridge.ps1' if command -v powershell.exe >/dev/null 2>&1; then ORCA_POWERSHELL=powershell.exe elif [ -x /mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe ]; then ORCA_POWERSHELL=/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe else echo "Orca WSL CLI requires Windows interop and could not find powershell.exe." >&2 exit 1 fi ORCA_BRIDGE_PS1_WIN=$(wslpath -w "$ORCA_BRIDGE_PS1") exec "$ORCA_POWERSHELL" -NoProfile -ExecutionPolicy Bypass -File "$ORCA_BRIDGE_PS1_WIN" "$ORCA_WIN_LAUNCHER" "$@" ` function createWslRunner( initialFile: string | null = null, pathIncludesLocalBin = true, options: { initialBridge?: string | null initialLegacyFile?: string | null failInstall?: boolean interopReady?: boolean } = {} ) { const commandPath = '/home/alice/.local/bin/orca-ide' const legacyCommandPath = '/home/alice/.local/bin/orca' const bridgePath = '/home/alice/.local/share/orca/orca-wsl-bridge.ps1' const files = new Map() if (initialFile !== null) { files.set(commandPath, initialFile) } if ( options.initialBridge !== null && (initialFile !== null || options.initialBridge !== undefined) ) { files.set(bridgePath, options.initialBridge ?? _internals.buildWslBridgeScript()) } if (options.initialLegacyFile) { files.set(legacyCommandPath, options.initialLegacyFile) } const calls: string[] = [] const runner = vi.fn(async (_distro: string, command: string) => { calls.push(command) if (command.includes('printf %s "$HOME"')) { return '/home/alice' } if (command.includes('case ":$PATH:"')) { return pathIncludesLocalBin ? 'yes' : 'no' } if (command.includes('cat > "$command_tmp"')) { if (options.failInstall) { throw new Error('simulated replacement failure') } if ( files.has(bridgePath) && !files.get(bridgePath)?.includes('# Orca managed WSL CLI PowerShell bridge') ) { throw new Error('__ORCA_CONFLICT__') } const launcher = command.match(/cat > "\$command_tmp" <<'ORCA_WSL_CLI'\n([\s\S]*)\nORCA_WSL_CLI/)?.[1] ?? '' const bridge = command.match( /cat > "\$bridge_tmp" <<'ORCA_WSL_BRIDGE'\n([\s\S]*)\nORCA_WSL_BRIDGE/ )?.[1] ?? '' files.set(commandPath, launcher) files.set(bridgePath, bridge) if (files.get(legacyCommandPath)?.includes('# Orca managed WSL CLI launcher')) { files.delete(legacyCommandPath) } return '' } if (command.includes('command -v powershell.exe')) { return options.interopReady === false ? 'no' : 'yes' } if (command.includes('rm -f')) { if (command.includes(`rm -f '${commandPath}'`)) { if ( files.has(bridgePath) && !files.get(bridgePath)?.includes('# Orca managed WSL CLI PowerShell bridge') ) { throw new Error('__ORCA_CONFLICT__') } files.delete(commandPath) files.delete(bridgePath) } if ( command.includes(legacyCommandPath) && files.get(legacyCommandPath)?.includes('# Orca managed WSL CLI launcher') ) { files.delete(legacyCommandPath) } return '' } if (command.includes('cat ')) { if (command.includes(commandPath)) { return files.get(commandPath) ?? '__ORCA_MISSING__' } if (command.includes(bridgePath)) { return files.get(bridgePath) ?? '__ORCA_MISSING__' } if (command.includes(legacyCommandPath)) { return files.get(legacyCommandPath) ?? '__ORCA_MISSING__' } } throw new Error(`Unexpected WSL command: ${command}`) }) return { runner, calls, getBridge: () => files.get(bridgePath) ?? null, getFile: () => files.get(commandPath) ?? null, getLegacyFile: () => files.get(legacyCommandPath) ?? null } } describe('WslCliInstaller', () => { beforeEach(() => { runWslProcessMock.mockReset() }) afterEach(() => { vi.useRealTimers() }) it('installs a WSL launcher that forwards to the Windows Orca launcher', async () => { const wsl = createWslRunner() const installer = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus() }, wslRunner: wsl.runner }) await expect(installer.getStatus()).resolves.toMatchObject({ state: 'not_installed', commandPath: '/home/alice/.local/bin/orca-ide' }) const installed = await installer.install() expect(installed).toMatchObject({ state: 'installed', pathConfigured: true, launcherPath: 'C:\\Users\\me\\AppData\\Local\\Programs\\Orca\\resources\\bin\\orca.exe' }) expect(wsl.getFile()).toBe( _internals.buildWslLauncher( 'C:\\Users\\me\\AppData\\Local\\Programs\\Orca\\resources\\bin\\orca.exe', '/home/alice/.local/share/orca/orca-wsl-bridge.ps1' ) ) expect(wsl.getBridge()).toBe(_internals.buildWslBridgeScript()) const installCommand = wsl.calls.find((command) => command.includes('cat > "$command_tmp"')) expect(installCommand).toBeDefined() expect(installCommand).toContain("legacy_command_path='/home/alice/.local/bin/orca'") expect(installCommand).toContain('rm -f "$legacy_command_path"') // Why: the new bridge accepts the old launcher's positional arguments, so // publishing it first keeps interrupted upgrades usable. const bridgePublishIndex = installCommand?.indexOf('mv -f "$bridge_tmp"') ?? -1 const launcherPublishIndex = installCommand?.indexOf('mv -f "$command_tmp"') ?? -1 expect(bridgePublishIndex).toBeGreaterThan(-1) expect(bridgePublishIndex).toBeLessThan(launcherPublishIndex) expect(installCommand).toContain('[ ! -L "$legacy_command_path" ]') }) it('continues checking WSL when the host launcher exists but host PATH is unknown', async () => { const wsl = createWslRunner() const hostStatus = { ...makeHostStatus(), pathConfigured: null, detail: 'Orca could not read the Windows user PATH registry value.' } satisfies CliInstallStatus const installer = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => hostStatus }, wslRunner: wsl.runner }) await expect(installer.getStatus()).resolves.toMatchObject({ supported: true, state: 'not_installed', commandPath: '/home/alice/.local/bin/orca-ide' }) }) it('derives the shared WSL bridge path for current and legacy command names', () => { expect(_internals.getBridgePathFromCommandPath('/home/alice/.local/bin/orca-ide')).toBe( '/home/alice/.local/share/orca/orca-wsl-bridge.ps1' ) expect(_internals.getBridgePathFromCommandPath('/home/alice/.local/bin/orca')).toBe( '/home/alice/.local/share/orca/orca-wsl-bridge.ps1' ) }) it('reports installed WSL launchers whose bin directory is missing from PATH', async () => { const launcher = _internals.buildWslLauncher( 'C:\\Orca\\orca.cmd', '/home/alice/.local/share/orca/orca-wsl-bridge.ps1' ) const wsl = createWslRunner(launcher, false) const installer = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus('C:\\Orca\\orca.cmd') }, wslRunner: wsl.runner }) await expect(installer.getStatus()).resolves.toMatchObject({ state: 'installed', pathConfigured: false, detail: expect.stringContaining('not on PATH') }) }) it('accepts current managed WSL scripts with an extra heredoc trailing newline', async () => { const launcher = `${_internals.buildWslLauncher( 'C:\\Orca\\orca.cmd', '/home/alice/.local/share/orca/orca-wsl-bridge.ps1' )}\n` const wsl = createWslRunner(launcher) const installer = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus('C:\\Orca\\orca.cmd') }, wslRunner: async (distro, command) => { if (command.includes('cat /home/alice/.local/share/orca/orca-wsl-bridge.ps1')) { return `${_internals.buildWslBridgeScript()}\n` } return wsl.runner(distro, command) } }) await expect(installer.getStatus()).resolves.toMatchObject({ state: 'installed', currentTarget: 'C:\\Orca\\orca.cmd' }) }) it('refuses to replace an unmanaged WSL command', async () => { const wsl = createWslRunner('#!/usr/bin/env bash\necho elsewhere\n') const installer = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus() }, wslRunner: wsl.runner }) await expect(installer.getStatus()).resolves.toMatchObject({ state: 'conflict' }) await expect(installer.install()).rejects.toThrow('Refusing to replace') }) it('removes a managed WSL launcher', async () => { const wsl = createWslRunner( _internals.buildWslLauncher( 'C:\\Orca\\orca.cmd', '/home/alice/.local/share/orca/orca-wsl-bridge.ps1' ) ) const installer = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus('C:\\Orca\\orca.cmd') }, wslRunner: wsl.runner }) await expect(installer.remove()).resolves.toMatchObject({ state: 'not_installed' }) expect(wsl.getFile()).toBeNull() }) it('generates a launcher that forwards arguments through a PowerShell file bridge', () => { const launcher = _internals.buildWslLauncher( 'C:\\Program Files\\Orca\\resources\\bin\\orca.exe', '/home/alice/.local/share/orca/orca-wsl-bridge.ps1' ) const bridge = _internals.buildWslBridgeScript() expect(launcher).toContain('command -v powershell.exe') expect(launcher).toContain('/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe') expect(launcher).toContain( 'Orca WSL CLI requires Windows interop and could not find powershell.exe.' ) expect(launcher).toContain('"$ORCA_POWERSHELL" -NoProfile -ExecutionPolicy Bypass -File') expect(launcher).toContain('ORCA_WSL_CWD=$(pwd -P 2>/dev/null) || {') expect(launcher).toContain('ORCA_WSL_CWD=/') expect(launcher).toContain('cd /') expect(launcher).toContain('ORCA_WSL_CWD_WIN=$(wslpath -w "$ORCA_WSL_CWD")') expect(launcher.indexOf('ORCA_WSL_CWD=$(pwd -P')).toBeLessThan( launcher.indexOf('ORCA_BRIDGE_PS1_WIN=$(wslpath') ) expect(launcher).toContain('"$ORCA_WIN_LAUNCHER" -WslCwd "$ORCA_WSL_CWD_WIN" "$@"') expect(launcher).not.toContain('-Command') expect(bridge).not.toContain('[CmdletBinding') expect(bridge).not.toMatch(/^param\(/m) expect(bridge).toContain("$args[1] -eq '-WslCwd'") expect(bridge).toContain('[string]$OrcaLauncher = $args[0]') expect(bridge).toContain('$WslCwd = $args[2]') 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('$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') expect(bridge).toContain('[System.Diagnostics.Process]::Start($StartInfo)') expect(bridge).toContain('$Process.WaitForExit()') expect(bridge).toContain('$exitCode = $Process.ExitCode') expect(bridge).not.toContain('& $OrcaLauncher @ForwardArgs') expect(bridge).toContain('Remove-Item Env:ORCA_CLI_CWD -ErrorAction SilentlyContinue') expect(bridge).toContain('catch') expect(bridge).toContain('$exitCode = 1') expect(bridge).toContain('exit $exitCode') }) it('treats absolute Windows PowerShell as interop-ready when powershell.exe is missing from PATH', async () => { const wsl = createWslRunner() const installer = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus() }, wslRunner: async (distro, command) => { if (command.includes('command -v powershell.exe') && !command.includes('cat >')) { expect(command).toContain('/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe') return 'yes' } return wsl.runner(distro, command) } }) await expect(installer.getStatus()).resolves.toMatchObject({ state: 'not_installed', commandPath: '/home/alice/.local/bin/orca-ide' }) }) it('marks stale managed launchers that point at the old app bin instead of packaged resources', async () => { const oldLauncher = _internals.buildWslLauncher( 'C:\\Users\\me\\AppData\\Local\\Programs\\Orca\\bin\\orca.cmd', '/home/alice/.local/share/orca/orca-wsl-bridge.ps1' ) const wsl = createWslRunner(oldLauncher) const installer = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus() }, wslRunner: wsl.runner }) await expect(installer.getStatus()).resolves.toMatchObject({ state: 'stale', currentTarget: 'C:\\Users\\me\\AppData\\Local\\Programs\\Orca\\bin\\orca.cmd', launcherPath: 'C:\\Users\\me\\AppData\\Local\\Programs\\Orca\\resources\\bin\\orca.exe' }) await expect(installer.install()).resolves.toMatchObject({ state: 'installed', currentTarget: 'C:\\Users\\me\\AppData\\Local\\Programs\\Orca\\resources\\bin\\orca.exe' }) }) it('repairs the frozen pre-rc4 registration so orchestration send/reply reach native rc4', async () => { const nativeLauncher = 'C:\\Program Files\\Orca\\resources\\bin\\orca.exe' const wsl = createWslRunner(PRE_RC4_MANAGED_WSL_LAUNCHER) const installer = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus(nativeLauncher) }, wslRunner: wsl.runner }) const orchestrationCalls = [ ['orchestration', 'send', '--type', 'heartbeat'], ['orchestration', 'send', '--type', 'worker_done'], ['orchestration', 'reply', '--message', 'line one\nline two'] ] const simulateRc4Launch = (args: string[]): number => { const target = _internals.parseManagedLauncherTarget(wsl.getFile() ?? '') return target?.toLowerCase().endsWith('orca.cmd') && args[0] === 'orchestration' && (args[1] === 'send' || args[1] === 'reply') ? 2 : 0 } expect(orchestrationCalls.map(simulateRc4Launch)).toEqual([2, 2, 2]) await expect( reconcileManagedWslCliRegistrations({ platform: 'win32', isPackaged: true, userDataPath: '/user-data', listDistros: async () => ['Ubuntu'], registry: { getCandidates: async () => ['Ubuntu'], recordObservations: async () => undefined }, createInstaller: () => installer }) ).resolves.toEqual([ { distro: 'Ubuntu', outcome: 'repaired', state: 'installed', managed: true } ]) await expect(installer.getStatus()).resolves.toMatchObject({ state: 'installed', currentTarget: nativeLauncher }) expect(orchestrationCalls.map(simulateRc4Launch)).toEqual([0, 0, 0]) }) it('leaves unmanaged WSL commands and conflicting bridges untouched during automatic repair', async () => { const unmanaged = '#!/usr/bin/env bash\necho user-owned\n' const wsl = createWslRunner(unmanaged) const installer = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus() }, wslRunner: wsl.runner }) await expect(installer.repairManagedRegistration()).resolves.toMatchObject({ changed: false, status: { state: 'conflict' } }) expect(wsl.getFile()).toBe(unmanaged) expect(wsl.calls.some((command) => command.includes('cat > "$command_tmp"'))).toBe(false) }) it('repairs a managed launcher whose bridge is missing, but preserves a conflicting bridge', async () => { const nativeLauncher = 'C:\\Orca\\resources\\bin\\orca.exe' const missingBridge = createWslRunner(PRE_RC4_MANAGED_WSL_LAUNCHER, true, { initialBridge: null }) const missingBridgeInstaller = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus(nativeLauncher) }, wslRunner: missingBridge.runner }) await expect(missingBridgeInstaller.repairManagedRegistration()).resolves.toMatchObject({ changed: true, status: { state: 'installed' } }) expect(missingBridge.getBridge()).toBe(_internals.buildWslBridgeScript()) const staleBridge = createWslRunner(PRE_RC4_MANAGED_WSL_LAUNCHER, true, { initialBridge: '# Orca managed WSL CLI PowerShell bridge\nWrite-Output "stale"\n' }) const staleBridgeInstaller = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus(nativeLauncher) }, wslRunner: staleBridge.runner }) await expect(staleBridgeInstaller.repairManagedRegistration()).resolves.toMatchObject({ changed: true, status: { state: 'installed' } }) expect(staleBridge.getBridge()).toBe(_internals.buildWslBridgeScript()) const conflictingBridge = createWslRunner(PRE_RC4_MANAGED_WSL_LAUNCHER, true, { initialBridge: 'Write-Output "user-owned bridge"\n' }) const conflictingBridgeInstaller = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus(nativeLauncher) }, wslRunner: conflictingBridge.runner }) // Why: a stale launcher with a user-owned bridge must surface as a // non-throwing conflict, not retry a doomed install on every startup. await expect(conflictingBridgeInstaller.repairManagedRegistration()).resolves.toMatchObject({ changed: false, managed: true, status: { state: 'conflict' } }) expect(conflictingBridge.getBridge()).toBe('Write-Output "user-owned bridge"\n') expect(conflictingBridge.getFile()).toBe(PRE_RC4_MANAGED_WSL_LAUNCHER) expect( conflictingBridge.calls.some((command) => command.includes('cat > "$command_tmp"')) ).toBe(false) }) it('retains command ownership when only the bridge conflicts', async () => { const nativeLauncher = 'C:\\Orca\\resources\\bin\\orca.exe' const currentLauncher = _internals.buildWslLauncher( nativeLauncher, '/home/alice/.local/share/orca/orca-wsl-bridge.ps1' ) const wsl = createWslRunner(currentLauncher, true, { initialBridge: 'Write-Output "user-owned bridge"\n' }) const installer = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus(nativeLauncher) }, wslRunner: wsl.runner }) await expect(installer.repairManagedRegistration()).resolves.toMatchObject({ changed: false, managed: true, status: { state: 'conflict' } }) expect(wsl.getBridge()).toBe('Write-Output "user-owned bridge"\n') expect(wsl.getFile()).toBe(currentLauncher) }) it('moves a legacy-only managed registration to orca-ide without touching unmanaged names', async () => { const nativeLauncher = 'C:\\Orca\\resources\\bin\\orca.exe' const managedLegacy = createWslRunner(null, true, { initialBridge: _internals.buildWslBridgeScript(), initialLegacyFile: PRE_RC4_MANAGED_WSL_LAUNCHER }) const installer = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus(nativeLauncher) }, wslRunner: managedLegacy.runner }) await expect(installer.repairManagedRegistration()).resolves.toMatchObject({ changed: true, status: { state: 'installed', currentTarget: nativeLauncher } }) expect(managedLegacy.getLegacyFile()).toBeNull() const unmanagedLegacy = createWslRunner(null, true, { initialLegacyFile: '#!/bin/sh\necho user-owned\n' }) const unmanagedInstaller = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus(nativeLauncher) }, wslRunner: unmanagedLegacy.runner }) await expect(unmanagedInstaller.repairManagedRegistration()).resolves.toMatchObject({ changed: false, status: { state: 'not_installed' } }) expect(unmanagedLegacy.getLegacyFile()).toBe('#!/bin/sh\necho user-owned\n') }) it('does not adopt a legacy-managed registration when the bridge is user-owned', async () => { const wsl = createWslRunner(null, true, { initialBridge: 'Write-Output "user-owned bridge"\n', initialLegacyFile: PRE_RC4_MANAGED_WSL_LAUNCHER }) const installer = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus() }, wslRunner: wsl.runner }) // Why: adoption would fail install()'s bridge guard on every startup; // repair must report blocked-but-managed instead of a doomed install. await expect(installer.repairManagedRegistration()).resolves.toMatchObject({ changed: false, managed: true, status: { state: 'not_installed' } }) expect(wsl.getLegacyFile()).toBe(PRE_RC4_MANAGED_WSL_LAUNCHER) expect(wsl.calls.some((command) => command.includes('cat > "$command_tmp"'))).toBe(false) }) it('removes the managed legacy launcher on removal so reconciliation cannot re-adopt it', async () => { const nativeLauncher = 'C:\\Orca\\resources\\bin\\orca.exe' const managedLegacy = createWslRunner(null, true, { initialBridge: _internals.buildWslBridgeScript(), initialLegacyFile: PRE_RC4_MANAGED_WSL_LAUNCHER }) const managedLegacyInstaller = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus(nativeLauncher) }, wslRunner: managedLegacy.runner }) await expect(managedLegacyInstaller.remove()).resolves.toMatchObject({ state: 'not_installed' }) expect(managedLegacy.getLegacyFile()).toBeNull() const unmanagedLegacy = createWslRunner(null, true, { initialLegacyFile: '#!/bin/sh\necho user-owned\n' }) const unmanagedInstaller2 = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus(nativeLauncher) }, wslRunner: unmanagedLegacy.runner }) await expect(unmanagedInstaller2.remove()).resolves.toMatchObject({ state: 'not_installed' }) expect(unmanagedLegacy.getLegacyFile()).toBe('#!/bin/sh\necho user-owned\n') const installedWithLegacy = createWslRunner( _internals.buildWslLauncher( nativeLauncher, '/home/alice/.local/share/orca/orca-wsl-bridge.ps1' ), true, { initialLegacyFile: PRE_RC4_MANAGED_WSL_LAUNCHER } ) const installedInstaller = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus(nativeLauncher) }, wslRunner: installedWithLegacy.runner }) await expect(installedInstaller.remove()).resolves.toMatchObject({ state: 'not_installed' }) expect(installedWithLegacy.getFile()).toBeNull() expect(installedWithLegacy.getLegacyFile()).toBeNull() }) it('keeps the pre-rc4 files on a transactional replacement failure', async () => { const bridge = _internals.buildWslBridgeScript() const wsl = createWslRunner(PRE_RC4_MANAGED_WSL_LAUNCHER, true, { initialBridge: bridge, failInstall: true }) const installer = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus('C:\\Program Files\\Orca\\resources\\bin\\orca.exe') }, wslRunner: wsl.runner }) await expect(installer.repairManagedRegistration()).rejects.toThrow( 'simulated replacement failure' ) expect(wsl.getFile()).toBe(PRE_RC4_MANAGED_WSL_LAUNCHER) expect(wsl.getBridge()).toBe(bridge) const installCommand = wsl.calls.find((command) => command.includes('cat > "$command_tmp"')) expect(installCommand).toContain('rollback() {') expect(installCommand).toContain('set +e') expect(installCommand).toContain('bridge_backup="${bridge_tmp}.backup"') expect(installCommand).toContain('cp -p') expect(installCommand).toContain('elif [ "$bridge_touched" -eq 1 ]') expect(installCommand).toContain('committed=1') expect(installCommand).toContain('flock -x -w 30 9') // Why: the command replace must stay one atomic rename; a mv-based backup // would leave a window where a concurrent shell finds no orca-ide at all. expect(installCommand).not.toContain('command_backup') expect(installCommand).not.toContain(`mv -f '/home/alice/.local/bin/orca-ide'`) }) it.skipIf(process.platform === 'win32')( 'rolls both files back when the command replacement fails after the bridge move', async () => { const root = await mkdtemp(join(tmpdir(), 'orca-wsl-cli-rollback-')) const home = join(root, 'home with spaces') const commandPath = join(home, '.local', 'bin', 'orca-ide') const bridgePath = join(home, '.local', 'share', 'orca', 'orca-wsl-bridge.ps1') const bridge = _internals.buildWslBridgeScript() await mkdir(join(home, '.local', 'bin'), { recursive: true }) await mkdir(join(home, '.local', 'share', 'orca'), { recursive: true }) await writeFile(commandPath, PRE_RC4_MANAGED_WSL_LAUNCHER, 'utf8') await writeFile(bridgePath, bridge, 'utf8') const runner = async (_distro: string, command: string): Promise => { if (command.includes('printf %s "$HOME"')) { return home } if (command.includes('cat > "$command_tmp"')) { const executableCommand = command .split('\n') .map((line) => (line.startsWith('mv -f "$command_tmp" ') ? 'exit 71' : line)) .join('\n') return execFileSync('bash', ['-c', executableCommand], { encoding: 'utf8' }) } if (command.includes('command -v powershell.exe')) { return 'yes' } if (command.includes('case ":$PATH:"')) { return 'yes' } return execFileSync('bash', ['-c', command], { encoding: 'utf8' }) } const installer = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus('C:\\Program Files\\Orca\\resources\\bin\\orca.exe') }, wslRunner: runner }) try { await expect(installer.repairManagedRegistration()).rejects.toThrow() await expect(readFile(commandPath, 'utf8')).resolves.toBe(PRE_RC4_MANAGED_WSL_LAUNCHER) await expect(readFile(bridgePath, 'utf8')).resolves.toBe(bridge) } finally { await rm(root, { recursive: true, force: true }) } } ) it('skips automatic repair when WSL interop is unavailable', async () => { const wsl = createWslRunner(PRE_RC4_MANAGED_WSL_LAUNCHER, true, { interopReady: false }) const installer = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus() }, wslRunner: wsl.runner }) await expect(installer.repairManagedRegistration()).resolves.toMatchObject({ changed: false, status: { state: 'unsupported' } }) expect(wsl.getFile()).toBe(PRE_RC4_MANAGED_WSL_LAUNCHER) }) it('is idempotent after repairing an old managed registration', async () => { const nativeLauncher = 'D:\\Custom Orca\\resources\\bin\\orca.exe' const wsl = createWslRunner(PRE_RC4_MANAGED_WSL_LAUNCHER) const installer = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus(nativeLauncher) }, wslRunner: wsl.runner }) await expect(installer.repairManagedRegistration()).resolves.toMatchObject({ changed: true }) await expect(installer.repairManagedRegistration()).resolves.toMatchObject({ changed: false }) expect(wsl.calls.filter((command) => command.includes('cat > "$command_tmp"'))).toHaveLength(1) expect(wsl.getFile()).toContain("ORCA_WIN_LAUNCHER='D:\\Custom Orca\\resources\\bin\\orca.exe'") }) it('settles when wsl.exe never reports completion', async () => { // Why no fake timers: the timeout is now runProcess's own, internal to the // mocked runWslProcess -- there is nothing left here to advance. runWslProcessMock.mockResolvedValue({ code: null, stdout: '', stderr: '', timedOut: true }) const installer = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus() } }) await expect(installer.getStatus()).rejects.toThrow('WSL command timed out') }) it('reports the distro unreachable rather than "not on PATH" without the login PATH', async () => { // The `case ":$PATH:"` probe answers from the distro default PATH when the // login PATH never resolved, and ~/.local/bin is never on that. Settings // would then state as fact that the CLI is not on PATH while the user's // own terminal finds it -- #14288 turning into a confident wrong verdict. runWslProcessMock.mockResolvedValue({ environmentResolved: false, code: 0, stdout: 'no', stderr: '', timedOut: false }) const installer = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus() } }) await expect(installer.getStatus()).rejects.toThrow('Could not reach the WSL distro') }) it('refuses to remove an old managed launcher when the bridge path is user-owned', async () => { const oldLauncher = _internals.buildWslLauncher( 'C:\\Old\\orca.cmd', '/home/alice/.local/share/orca/orca-wsl-bridge.ps1' ) const wsl = createWslRunner(oldLauncher) const installer = new WslCliInstaller({ platform: 'win32', distro: 'Ubuntu', hostInstaller: { getStatus: async () => makeHostStatus('C:\\Orca\\orca.cmd') }, wslRunner: async (distro, command) => { if (command.includes('cat /home/alice/.local/share/orca/orca-wsl-bridge.ps1')) { return 'user bridge' } if (command.includes('rm -f')) { throw new Error('__ORCA_CONFLICT__') } return wsl.runner(distro, command) } }) await expect(installer.remove()).rejects.toThrow('__ORCA_CONFLICT__') }) })