Files
web-terminal/desktop/src/server-config.ts
Yaojia Wang 5833529c0c fix(desktop): default USE_TMUX to auto so quitting the app stops killing sessions
The desktop shell pinned USE_TMUX=0 on every platform, not just Windows. Without
tmux the shell is a direct child of the embedded server, so SessionManager.shutdown
takes the kill branch and quitting the app — the tray Quit, a logout, an update —
ends every session with it.

That defeats the app's premise. The point is to hand Claude Code a task, walk away,
and reconnect from another device; a session that cannot survive the window closing
does not do that. With tmux the node-pty process is only a tmux CLIENT, so shutdown
detaches and the shell keeps running, which is exactly what the standalone server
has always done (USE_TMUX unset → 'auto' → is the binary present?).

Non-Windows now defaults to 'auto'. Windows stays forced off — tmux is a *nix
keepalive. An explicit ambient USE_TMUX still wins either way, so USE_TMUX=0 remains
the opt-out.

Tests assert the end-to-end result, not just the emitted string: buildServerEnv fed
through loadConfig yields useTmux === true with tmux installed, and false for an
explicit opt-out. The original bug was these two layers disagreeing about what a
value meant, so checking the string alone would not have caught it.

Sessions started in the app before this change are NOT retroactively protected —
they have no tmux session behind them and will still die with the app. Only sessions
created after the new build is installed survive.
2026-07-30 12:47:05 +02:00

60 lines
2.6 KiB
TypeScript

/**
* desktop/src/server-config.ts — build the env overrides fed to the backend's
* loadConfig() for the embedded server.
*
* Starts from a COPY of the ambient env (never mutates it) and pins the four
* values the desktop shell controls: PORT (the free port we already resolved),
* BIND_HOST (private 127.0.0.1 by default; 0.0.0.0 only when LAN sharing is on),
* SHELL_PATH (platform-aware default so Windows gets PowerShell), and USE_TMUX
* (forced off on Windows — tmux is a *nix keepalive; elsewhere respect the
* ambient env, defaulting to auto-detect). Every other ambient var passes through
* untouched.
*/
import type { DesktopPrefs } from './types.js'
import { defaultShellForPlatform } from './shell.js'
/** Loopback bind host — private mode; the window connects here and always passes Origin checks. */
const PRIVATE_BIND_HOST = '127.0.0.1'
/** Wildcard bind host — LAN sharing (phones/tablets reconnect); opt-in, no auth. */
const LAN_BIND_HOST = '0.0.0.0'
/** tmux is a *nix keepalive, so Windows forces it off outright. */
const TMUX_OFF = '0'
/**
* Everywhere else the desktop shell auto-detects (loadConfig's 'auto' → is the tmux
* binary present?), which is what the standalone server already did.
*
* It used to default OFF, and that quietly broke the app's whole premise. Without
* tmux the shell is a direct child of the embedded server, so quitting the app —
* including the tray "Quit", including a logout — kills every session with it. The
* point of this app is to hand Claude Code a task, walk away, and reconnect from
* another device; a session that cannot survive the window closing does not do that.
* With tmux the node-pty process is only a tmux CLIENT, so shutdown detaches and the
* shell keeps running (see SessionManager.shutdown).
*
* An explicit ambient USE_TMUX still wins, so USE_TMUX=0 remains the way to opt out.
*/
const TMUX_AUTO = 'auto'
export interface ServerEnvInput {
readonly prefs: DesktopPrefs
readonly port: number
readonly platform: NodeJS.Platform
readonly env: NodeJS.ProcessEnv
}
/**
* Produce the env-override object for loadConfig(). Immutable: input.env is
* spread into a NEW object and is never mutated.
*/
export function buildServerEnv(input: ServerEnvInput): Record<string, string | undefined> {
const { prefs, port, platform, env } = input
return {
...env,
PORT: String(port),
BIND_HOST: prefs.lanSharing ? LAN_BIND_HOST : PRIVATE_BIND_HOST,
SHELL_PATH: prefs.shellPath ?? defaultShellForPlatform(platform, env),
USE_TMUX: platform === 'win32' ? TMUX_OFF : (env.USE_TMUX ?? TMUX_AUTO),
}
}