fix: enhance child process environment handling for Windows

- Added a helper function to build the child process environment with the correct HOME directory, addressing issues with non-ASCII usernames on Windows.
- Updated CLI installer methods to utilize the new environment setup for improved compatibility and error handling.
This commit is contained in:
iliya 2026-02-27 19:38:22 +02:00
parent 517e08d8ba
commit d948cc6fb4

View file

@ -17,6 +17,7 @@
* - Human-readable error messages per phase * - Human-readable error messages per phase
*/ */
import { getHomeDir } from '@main/utils/pathDecoder';
import { getErrorMessage } from '@shared/utils/errorHandling'; import { getErrorMessage } from '@shared/utils/errorHandling';
import { createLogger } from '@shared/utils/logger'; import { createLogger } from '@shared/utils/logger';
import { execFile, spawn } from 'child_process'; import { execFile, spawn } from 'child_process';
@ -64,6 +65,20 @@ const EBUSY_MAX_RETRIES = 3;
/** Delay between EBUSY retries (multiplied by attempt number) */ /** Delay between EBUSY retries (multiplied by attempt number) */
const EBUSY_RETRY_DELAY_MS = 2000; const EBUSY_RETRY_DELAY_MS = 2000;
/**
* Build env for child processes with correct HOME.
* On Windows with non-ASCII usernames, process.env may have a broken HOME/USERPROFILE.
* getHomeDir() uses Electron's app.getPath('home') which handles Unicode correctly.
*/
function buildChildEnv(): NodeJS.ProcessEnv {
const home = getHomeDir();
return {
...process.env,
HOME: home,
USERPROFILE: home,
};
}
// ============================================================================= // =============================================================================
// Helpers // Helpers
// ============================================================================= // =============================================================================
@ -209,6 +224,7 @@ export class CliInstallerService {
try { try {
const { stdout } = await execFileAsync(binaryPath, ['--version'], { const { stdout } = await execFileAsync(binaryPath, ['--version'], {
timeout: VERSION_TIMEOUT_MS, timeout: VERSION_TIMEOUT_MS,
env: buildChildEnv(),
}); });
result.installedVersion = normalizeVersion(stdout); result.installedVersion = normalizeVersion(stdout);
logger.info( logger.info(
@ -222,6 +238,7 @@ export class CliInstallerService {
try { try {
const { stdout: authStdout } = await execFileAsync(binaryPath, ['auth', 'status'], { const { stdout: authStdout } = await execFileAsync(binaryPath, ['auth', 'status'], {
timeout: VERSION_TIMEOUT_MS, timeout: VERSION_TIMEOUT_MS,
env: buildChildEnv(),
}); });
const auth = JSON.parse(authStdout.trim()) as { loggedIn?: boolean; authMethod?: string }; const auth = JSON.parse(authStdout.trim()) as { loggedIn?: boolean; authMethod?: string };
result.authLoggedIn = auth.loggedIn === true; result.authLoggedIn = auth.loggedIn === true;
@ -450,7 +467,7 @@ export class CliInstallerService {
private async runInstallWithStreaming(binaryPath: string, attempt = 1): Promise<void> { private async runInstallWithStreaming(binaryPath: string, attempt = 1): Promise<void> {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
const child = spawn(binaryPath, ['install'], { const child = spawn(binaryPath, ['install'], {
env: { ...process.env, CLAUDE_SKIP_ANALYTICS: '1' }, env: { ...buildChildEnv(), CLAUDE_SKIP_ANALYTICS: '1' },
stdio: ['ignore', 'pipe', 'pipe'], stdio: ['ignore', 'pipe', 'pipe'],
}); });