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.
60 lines
2.6 KiB
TypeScript
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),
|
|
}
|
|
}
|