diff --git a/Releases/v4.0.3/.claude/PAI-Install/install.sh b/Releases/v4.0.3/.claude/PAI-Install/install.sh index b618184f7..b3251c855 100755 --- a/Releases/v4.0.3/.claude/PAI-Install/install.sh +++ b/Releases/v4.0.3/.claude/PAI-Install/install.sh @@ -154,8 +154,11 @@ fi info "Launching installer..." echo "" -# Auto-detect headless/SSH environments and fall back to CLI mode -if [ -z "$DISPLAY" ] && [ -z "$WAYLAND_DISPLAY" ] && [ "$(uname)" != "Darwin" ]; then +# Auto-detect headless/SSH environments and fall back to CLI mode. +# Use ${VAR:-} to tolerate `set -u` (nounset, enabled on line 8) — on SSH/headless +# Linux hosts DISPLAY and WAYLAND_DISPLAY are typically unset, which would abort +# the script before the detection runs. +if [ -z "${DISPLAY:-}" ] && [ -z "${WAYLAND_DISPLAY:-}" ] && [ "$(uname)" != "Darwin" ]; then INSTALL_MODE="cli" info "Headless environment detected — using CLI installer." else diff --git a/Releases/v4.0.3/.claude/PAI-Install/main.ts b/Releases/v4.0.3/.claude/PAI-Install/main.ts index fb01d85a3..61e9d8a3c 100644 --- a/Releases/v4.0.3/.claude/PAI-Install/main.ts +++ b/Releases/v4.0.3/.claude/PAI-Install/main.ts @@ -15,7 +15,18 @@ import { existsSync } from "fs"; const args = process.argv.slice(2); const modeIdx = args.indexOf("--mode"); -const mode = modeIdx >= 0 ? args[modeIdx + 1] : "gui"; + +// Auto-detect headless (no DISPLAY/WAYLAND_DISPLAY on non-Darwin) when --mode +// is omitted. install.sh already does this when invoked via bash, but running +// main.ts directly (e.g. `bun PAI-Install/main.ts`) bypassed that check and +// unconditionally launched the Electron GUI, which fails on headless Linux. +function defaultMode(): "gui" | "cli" { + if (process.platform === "darwin") return "gui"; + if (!process.env.DISPLAY && !process.env.WAYLAND_DISPLAY) return "cli"; + return "gui"; +} + +const mode = modeIdx >= 0 ? args[modeIdx + 1] : defaultMode(); const ROOT = import.meta.dir;