fix: improve WSL user home path resolution and enhance process renaming on Windows
- Updated fallback home path logic to correctly handle 'root' user case in WSL. - Refactored process renaming in TeamAgentToolsInstaller to include retry logic on Windows for better error handling during concurrent operations. - Enhanced user information retrieval in TeamProvisioningService to handle potential errors gracefully. - Clarified documentation in processKill utility regarding platform-specific behavior.
This commit is contained in:
parent
91933c2ee3
commit
0099be4f23
5 changed files with 37 additions and 7 deletions
|
|
@ -981,7 +981,11 @@ async function handleFindWslClaudeRoots(
|
|||
// Fallback: query the default WSL user, then try Windows USERNAME
|
||||
const wslUser = await resolveWslDefaultUser(distro);
|
||||
const fallbackUser = wslUser || process.env.USERNAME;
|
||||
const fallbackHomePath = fallbackUser ? `/home/${fallbackUser}` : null;
|
||||
const fallbackHomePath = fallbackUser
|
||||
? fallbackUser === 'root'
|
||||
? '/root'
|
||||
: `/home/${fallbackUser}`
|
||||
: null;
|
||||
const normalizedHome =
|
||||
normalizeWslHomePath(resolvedHomePath ?? '') ??
|
||||
(fallbackHomePath ? normalizeWslHomePath(fallbackHomePath) : null);
|
||||
|
|
|
|||
|
|
@ -97,9 +97,8 @@ async function collectNvmWindowsCandidates(): Promise<string[]> {
|
|||
|
||||
const exts = getWindowsExecutableExtensions();
|
||||
return versions
|
||||
.flatMap((version) => exts.map((ext) => path.join(nvmRoot, version, `claude${ext}`)))
|
||||
.sort((a, b) => a.localeCompare(b))
|
||||
.reverse();
|
||||
.sort((a, b) => b.localeCompare(a, undefined, { numeric: true, sensitivity: 'base' }))
|
||||
.flatMap((version) => exts.map((ext) => path.join(nvmRoot, version, `claude${ext}`)));
|
||||
}
|
||||
|
||||
async function resolveFromPathEnv(binaryName: string): Promise<string | null> {
|
||||
|
|
|
|||
|
|
@ -153,7 +153,26 @@ function atomicWrite(filePath, data) {
|
|||
'.' +
|
||||
String(Math.random().toString(16).slice(2));
|
||||
fs.writeFileSync(tmp, data, 'utf8');
|
||||
fs.renameSync(tmp, filePath);
|
||||
// On Windows, fs.renameSync can throw EPERM/EACCES when another process
|
||||
// is concurrently renaming to the same target. Retry with backoff.
|
||||
const maxRetries = process.platform === 'win32' ? 5 : 1;
|
||||
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
||||
try {
|
||||
fs.renameSync(tmp, filePath);
|
||||
return;
|
||||
} catch (e) {
|
||||
if (attempt < maxRetries - 1 && e && (e.code === 'EPERM' || e.code === 'EACCES')) {
|
||||
// Busy wait — small random delay to reduce contention
|
||||
const ms = Math.floor(Math.random() * 50) + 10;
|
||||
const end = Date.now() + ms;
|
||||
while (Date.now() < end) { /* spin */ }
|
||||
continue;
|
||||
}
|
||||
// Clean up temp file on final failure
|
||||
try { fs.unlinkSync(tmp); } catch { /* ignore */ }
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeStatus(value) {
|
||||
|
|
|
|||
|
|
@ -2702,11 +2702,18 @@ export class TeamProvisioningService {
|
|||
const electronHome = getHomeDir();
|
||||
const isWindows = process.platform === 'win32';
|
||||
const home = shellEnv.HOME?.trim() || electronHome;
|
||||
let osUsername = '';
|
||||
try {
|
||||
osUsername = os.userInfo().username;
|
||||
} catch {
|
||||
// os.userInfo() can throw SystemError in restricted environments (no passwd entry, Docker, etc.)
|
||||
}
|
||||
const user =
|
||||
shellEnv.USER?.trim() ||
|
||||
process.env.USER?.trim() ||
|
||||
process.env.USERNAME?.trim() ||
|
||||
os.userInfo().username;
|
||||
osUsername ||
|
||||
'unknown';
|
||||
|
||||
// Shell: on Windows there is no SHELL env var; use COMSPEC (cmd.exe / powershell).
|
||||
// On Unix, prefer the user's login shell from env or fall back to /bin/zsh.
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ import path from 'path';
|
|||
* it calls TerminateProcess() which is equivalent to SIGKILL (immediate, ungraceful).
|
||||
* - `taskkill /T` also kills child processes, preventing orphaned process trees.
|
||||
*
|
||||
* Throws if the process cannot be killed (except ESRCH — process already dead).
|
||||
* On Unix, throws if the process cannot be killed (except ESRCH — process already dead).
|
||||
* On Windows, taskkill is best-effort (async fire-and-forget) to match killProcessTree() semantics.
|
||||
*/
|
||||
export function killProcessByPid(pid: number): void {
|
||||
if (process.platform === 'win32') {
|
||||
|
|
|
|||
Loading…
Reference in a new issue