Skip to content

Commit be54af3

Browse files
committed
Harden desktop startup and installer wrappers against broken cwd launches
1 parent 3b87b7b commit be54af3

2 files changed

Lines changed: 63 additions & 0 deletions

File tree

apps/desktop/src/main/index.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,50 @@ import { setMainWindow as setStreamingMainWindow } from './streaming';
1212
// Must match the .desktop file name (pairux.desktop) and electron-builder executableName.
1313
app.setName('pairux');
1414

15+
function normalizeWorkingDirectory(): void {
16+
try {
17+
// This throws ENOENT if the shell launched us from a deleted directory.
18+
process.cwd();
19+
return;
20+
} catch (error) {
21+
console.warn('[Main] Invalid startup working directory, switching to a safe path:', error);
22+
}
23+
24+
const candidates = [
25+
process.env.HOME,
26+
process.env.USERPROFILE,
27+
(() => {
28+
try {
29+
return app.getPath('home');
30+
} catch {
31+
return undefined;
32+
}
33+
})(),
34+
(() => {
35+
try {
36+
return app.getPath('temp');
37+
} catch {
38+
return undefined;
39+
}
40+
})(),
41+
process.platform === 'win32' ? 'C:\\' : '/',
42+
].filter((value): value is string => Boolean(value));
43+
44+
for (const dir of candidates) {
45+
try {
46+
process.chdir(dir);
47+
console.log('[Main] Working directory reset to:', dir);
48+
return;
49+
} catch {
50+
// Try next candidate.
51+
}
52+
}
53+
54+
console.error('[Main] Failed to reset working directory; continuing with invalid cwd');
55+
}
56+
57+
normalizeWorkingDirectory();
58+
1559
// Load environment variables from .env file in development only.
1660
// In production, env vars are injected at build time by electron-vite.
1761
if (!app.isPackaged) {

apps/installer/scripts/install.sh

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ warn() {
4747
echo -e "${YELLOW}[WARN]${NC} $1"
4848
}
4949

50+
warn_existing_launcher() {
51+
local launcher="$BIN_DIR/pairux"
52+
if [ -L "$launcher" ]; then
53+
local target
54+
target=$(readlink "$launcher" 2>/dev/null || echo "unknown")
55+
warn "Found legacy symlink launcher at $launcher -> $target"
56+
warn "Replacing it with the managed PairUX wrapper script"
57+
fi
58+
}
59+
5060
error() {
5161
echo -e "${RED}[ERROR]${NC} $1"
5262
exit 1
@@ -260,6 +270,7 @@ install_macos() {
260270
# Create CLI wrapper script
261271
info "Creating launcher script..."
262272
mkdir -p "$BIN_DIR"
273+
warn_existing_launcher
263274
# Remove any existing symlink so we don't follow it into the app bundle
264275
# and overwrite the real Electron binary
265276
rm -f "$BIN_DIR/pairux"
@@ -360,6 +371,9 @@ case "\${1-}" in
360371
esac
361372
362373
if [ -x "\$APP_BIN" ]; then
374+
# Launch from a stable working directory. A deleted caller cwd can break
375+
# Electron startup with confusing "open dir" errors.
376+
cd "\$HOME" 2>/dev/null || cd / 2>/dev/null || true
363377
# Check for macOS quarantine attribute which causes the app to hang silently
364378
if xattr -p com.apple.quarantine "\$APP_PATH" &>/dev/null; then
365379
echo ""
@@ -414,6 +428,7 @@ install_linux() {
414428
# Create wrapper script that handles sandbox issues
415429
info "Creating launcher script..."
416430
mkdir -p "$BIN_DIR"
431+
warn_existing_launcher
417432
# Remove any existing symlink so we don't follow it and overwrite the AppImage
418433
rm -f "$BIN_DIR/pairux"
419434
cat > "$BIN_DIR/pairux" << WRAPPER
@@ -521,6 +536,10 @@ if [ -x "\$APPIMAGE" ]; then
521536
# Unset ELECTRON_RUN_AS_NODE — VSCode's integrated terminal sets this,
522537
# which prevents Electron from exposing its API (app, BrowserWindow, etc.)
523538
unset ELECTRON_RUN_AS_NODE
539+
# Launch from a stable working directory. If the caller's cwd was deleted
540+
# (common with terminals in temp/build dirs), Electron/AppImage startup can
541+
# emit "open dir error: No such file or directory" on Linux.
542+
cd "\$HOME" 2>/dev/null || cd / 2>/dev/null || true
524543
525544
# If FUSE is unavailable or inaccessible (common in restricted/containerized
526545
# environments), fall back to extract-and-run mode so the AppImage can still

0 commit comments

Comments
 (0)