Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions setup-claude-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,19 @@ function updateSetupStep(index, status, error = null) {
}
}

// Resolve a system utility to an absolute path so it can't be shadowed by a
// foreign entry prepended to PATH (shell rc files, direnv, tool shims, etc.).
// Returns the first candidate that exists on disk, falling back to the bare
// name if none are found (e.g. a distro that installs the tool elsewhere).
function resolveSystemBinary(candidates, fallback) {
for (const candidate of candidates) {
if (existsSync(candidate)) {
return candidate;
}
}
return fallback;
}

async function execAsync(command) {
const execStep = addSetupStep(`exec_${command.substring(0, 20)}...`);
return new Promise((resolve, reject) => {
Expand Down Expand Up @@ -569,16 +582,20 @@ async function restartClaude() {
`taskkill /F /IM "Claude.exe"`,
);
break;
case "darwin":
case "darwin": {
const killall = resolveSystemBinary(['/usr/bin/killall'], 'killall');
await execAsync(
`killall "Claude"`,
`"${killall}" "Claude"`,
);
break;
case "linux":
}
case "linux": {
const pkill = resolveSystemBinary(['/usr/bin/pkill', '/bin/pkill'], 'pkill');
await execAsync(
`pkill -f "claude"`,
`"${pkill}" -f "claude"`,
);
break;
}
}
updateSetupStep(killStep, 'completed');
await trackEvent('npx_setup_kill_claude_success', { platform });
Expand All @@ -600,7 +617,8 @@ async function restartClaude() {
updateSetupStep(startStep, 'skipped');
await trackEvent('npx_setup_start_claude_skipped', { platform });
} else if (platform === "darwin") {
await execAsync(`open -a "Claude"`);
const open = resolveSystemBinary(['/usr/bin/open'], 'open');
await execAsync(`"${open}" -a "Claude"`);
updateSetupStep(startStep, 'completed');
logToFile("\n✅ Claude has been restarted automatically!");
await trackEvent('npx_setup_start_claude_success', { platform });
Expand Down
6 changes: 5 additions & 1 deletion src/remote-device/remote-channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,11 @@ export class RemoteChannel {
console.debug('[DEBUG] Spawning blocking update script:', scriptPath);
console.debug('[DEBUG] Using node executable:', process.execPath);

const result = spawnSync('node', [
// process.execPath is the exact Node binary currently running the
// server. Using it (instead of the bare name 'node') skips PATH
// resolution entirely, so the update script always runs on the same
// runtime and can't be shadowed by a different 'node' on PATH.
const result = spawnSync(process.execPath, [
scriptPath,
deviceId,
supabaseUrl,
Expand Down
24 changes: 19 additions & 5 deletions uninstall-claude-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,15 @@ function updateUninstallStep(index, status, error = null) {
}
}

function resolveSystemBinary(candidates, fallback) {
for (const candidate of candidates) {
if (existsSync(candidate)) {
return candidate;
}
}
return fallback;
}

async function execAsync(command) {
const execStep = addUninstallStep(`exec_${command.substring(0, 20)}...`);
return new Promise((resolve, reject) => {
Expand Down Expand Up @@ -489,12 +498,16 @@ async function restartClaude() {
case "win32":
await execAsync(`taskkill /F /IM "Claude.exe"`);
break;
case "darwin":
await execAsync(`killall "Claude"`);
case "darwin": {
const killall = resolveSystemBinary(['/usr/bin/killall'], 'killall');
await execAsync(`"${killall}" "Claude"`);
break;
case "linux":
await execAsync(`pkill -f "claude"`);
}
case "linux": {
const pkill = resolveSystemBinary(['/usr/bin/pkill', '/bin/pkill'], 'pkill');
await execAsync(`"${pkill}" -f "claude"`);
break;
}
}
updateUninstallStep(killStep, 'completed');
logToFile("Claude process terminated successfully");
Expand All @@ -516,7 +529,8 @@ async function restartClaude() {
updateUninstallStep(startStep, 'skipped');
await trackEvent('uninstall_start_claude_skipped');
} else if (platform === "darwin") {
await execAsync(`open -a "Claude"`);
const open = resolveSystemBinary(['/usr/bin/open'], 'open');
await execAsync(`"${open}" -a "Claude"`);
updateUninstallStep(startStep, 'completed');
logToFile("✅ Claude has been restarted automatically!");
await trackEvent('uninstall_start_claude_success');
Expand Down