From 27a2261eb6f452db3bec4fc180bf035b93c711dc Mon Sep 17 00:00:00 2001 From: Venkat Date: Fri, 10 Jul 2026 15:13:19 +0000 Subject: [PATCH 1/5] fix: make `code ` work in serve-web terminals The apt `code` package installs the desktop (Electron) binary at /usr/bin/code. In our headless serve-web/tunnel sessions there is no X display, so running `code ` from the integrated terminal crashes with "Missing X server or $DISPLAY" (plus a dbus warning). Add a small wrapper at $HOME/.local/bin/code (already first on the interactive PATH via .zshrc/.bashrc) that forwards to the VS Code serve-web/remote-cli shim, which opens files in the running window via $VSCODE_IPC_HOOK_CLI. It resolves the newest shim so it survives VS Code updates, and falls back with a clear message if no session is running. Only affects interactive shells; build-time `code --install-extension` still resolves to /usr/bin/code. Co-Authored-By: Claude Opus 4.8 (1M context) --- .devcontainer/Dockerfile | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 941a257c0e..956000f159 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -201,6 +201,31 @@ RUN curl -sS https://downloads.1password.com/linux/keys/1password.asc | \ RUN mkdir -p /home/vscode/.vscode-server/data/Machine/ && echo '{"workbench.colorTheme": "VS Code Dark"}' > /home/vscode/.vscode-server/data/Machine/settings.json +# The apt `code` package installs the desktop (Electron) binary at /usr/bin/code. +# In these headless serve-web/tunnel sessions there is no X display, so running +# `code ` in the terminal crashes ("Missing X server or $DISPLAY"). +# Drop a wrapper on $HOME/.local/bin (first on the interactive PATH, see .zshrc/.bashrc +# below) that forwards to the serve-web/remote-cli shim, which opens files in the +# already-running VS Code window via $VSCODE_IPC_HOOK_CLI. Picks the newest shim so it +# keeps working across VS Code updates. Only affects interactive shells; build-time +# `code --install-extension` above still resolves to /usr/bin/code. +RUN mkdir -p /home/vscode/.local/bin \ + && printf '%s\n' \ + '#!/usr/bin/env sh' \ + '# Route `code` to the VS Code serve-web/remote CLI shim instead of the GUI desktop' \ + '# binary (/usr/bin/code), which has no display in this container and crashes.' \ + 'shim=$(ls -t "$HOME"/.vscode/cli/serve-web/*/bin/remote-cli/code \' \ + ' "$HOME"/.vscode-server/bin/*/bin/remote-cli/code \' \ + ' "$HOME"/.vscode-server/cli/servers/*/server/bin/remote-cli/code \' \ + ' 2>/dev/null | head -n1)' \ + 'if [ -n "$shim" ]; then' \ + ' exec "$shim" "$@"' \ + 'fi' \ + 'echo "code: no VS Code remote CLI shim found; is a serve-web/remote session running?" >&2' \ + 'exit 1' \ + > /home/vscode/.local/bin/code \ + && chmod +x /home/vscode/.local/bin/code + # Add backup warning to .zshrc for vscode user RUN echo '\n# === Backup & Git Reminder ===' | tee -a /home/vscode/.zshrc && \ echo 'echo -e "\e[1;31m⚠️ WARNING: No backups are configured. You are responsible for any data loss.\e[0m"' | tee -a /home/vscode/.zshrc && \ From 4bfa2eeba9a98613807f6090e1a6fda165ccca41 Mon Sep 17 00:00:00 2001 From: Venkat Date: Fri, 10 Jul 2026 15:21:50 +0000 Subject: [PATCH 2/5] fix: recover `code` from stale IPC socket after reconnect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the `code` wrapper to handle a stale $VSCODE_IPC_HOOK_CLI. The value is captured when the shell starts, so after a VS Code reconnect (container/server restart) it can name a socket that no longer exists, and `code ` fails. When — and only when — the referenced socket is gone, fall back to the most recently created /tmp/vscode-ipc-*.sock. A still-present socket is left untouched, so routing stays correct when multiple windows (each with its own socket) are connected. Dependency-free and no double-exec. Co-Authored-By: Claude Opus 4.8 (1M context) --- .devcontainer/Dockerfile | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 956000f159..ace56460b9 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -206,23 +206,44 @@ RUN mkdir -p /home/vscode/.vscode-server/data/Machine/ && echo '{"workbench.colo # `code ` in the terminal crashes ("Missing X server or $DISPLAY"). # Drop a wrapper on $HOME/.local/bin (first on the interactive PATH, see .zshrc/.bashrc # below) that forwards to the serve-web/remote-cli shim, which opens files in the -# already-running VS Code window via $VSCODE_IPC_HOOK_CLI. Picks the newest shim so it -# keeps working across VS Code updates. Only affects interactive shells; build-time +# already-running VS Code window via the $VSCODE_IPC_HOOK_CLI socket. The wrapper picks +# the newest shim so it survives VS Code updates, and recovers from a stale socket after +# a reconnect (see the inline comments). Only affects interactive shells; build-time # `code --install-extension` above still resolves to /usr/bin/code. RUN mkdir -p /home/vscode/.local/bin \ && printf '%s\n' \ '#!/usr/bin/env sh' \ '# Route `code` to the VS Code serve-web/remote CLI shim instead of the GUI desktop' \ '# binary (/usr/bin/code), which has no display in this container and crashes.' \ + '' \ + '# 1. Locate the remote CLI shim. There is one per installed VS Code build, and the' \ + '# path embeds a build hash, so pick the newest to survive VS Code updates.' \ 'shim=$(ls -t "$HOME"/.vscode/cli/serve-web/*/bin/remote-cli/code \' \ ' "$HOME"/.vscode-server/bin/*/bin/remote-cli/code \' \ ' "$HOME"/.vscode-server/cli/servers/*/server/bin/remote-cli/code \' \ ' 2>/dev/null | head -n1)' \ - 'if [ -n "$shim" ]; then' \ - ' exec "$shim" "$@"' \ + 'if [ -z "$shim" ]; then' \ + ' echo "code: no VS Code remote CLI shim found; is a serve-web/remote session running?" >&2' \ + ' exit 1' \ 'fi' \ - 'echo "code: no VS Code remote CLI shim found; is a serve-web/remote session running?" >&2' \ - 'exit 1' \ + '' \ + '# 2. The shim talks to the running window over the IPC socket named in' \ + '# $VSCODE_IPC_HOOK_CLI. That value is captured when the shell starts, so after a' \ + '# VS Code reconnect (container or server restart) it can name a socket that no' \ + '# longer exists. Only when the referenced socket is gone, fall back to the most' \ + '# recently created /tmp/vscode-ipc-*.sock so `code` keeps working without opening' \ + '# a fresh terminal. A still-present socket is left untouched, so routing stays' \ + '# correct when multiple windows (each with its own socket) are connected.' \ + 'if [ -z "$VSCODE_IPC_HOOK_CLI" ] || [ ! -S "$VSCODE_IPC_HOOK_CLI" ]; then' \ + ' newest_sock=$(ls -t /tmp/vscode-ipc-*.sock 2>/dev/null | head -n1)' \ + ' if [ -n "$newest_sock" ]; then' \ + ' VSCODE_IPC_HOOK_CLI="$newest_sock"' \ + ' export VSCODE_IPC_HOOK_CLI' \ + ' fi' \ + 'fi' \ + '' \ + '# 3. Hand off to the shim, preserving every argument and its exit status.' \ + 'exec "$shim" "$@"' \ > /home/vscode/.local/bin/code \ && chmod +x /home/vscode/.local/bin/code From 465fe94e8a0340213235ef8d2919b3b577555906 Mon Sep 17 00:00:00 2001 From: Venkat Date: Fri, 10 Jul 2026 16:30:15 +0000 Subject: [PATCH 3/5] fix: require the resolved `code` shim to be executable Post-review hardening from a shell audit: `ls -t` can surface a shim path that lacks the execute bit, in which case `exec` fails with an opaque "exec: Permission denied". Add a `-x` check so that case takes the existing friendly "no usable shim" branch instead. Deliberately not adding a "fail if no live socket" guard: socket-less subcommands (code --version/--list-extensions/--help/tunnel) must still reach the shim, so socket presence is left for the shim to require. Co-Authored-By: Claude Opus 4.8 (1M context) --- .devcontainer/Dockerfile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index ace56460b9..5fcaab0f1a 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -217,13 +217,15 @@ RUN mkdir -p /home/vscode/.local/bin \ '# binary (/usr/bin/code), which has no display in this container and crashes.' \ '' \ '# 1. Locate the remote CLI shim. There is one per installed VS Code build, and the' \ - '# path embeds a build hash, so pick the newest to survive VS Code updates.' \ + '# path embeds a build hash, so pick the newest to survive VS Code updates. The' \ + '# -x check turns a found-but-not-executable match into the friendly error below' \ + '# instead of an opaque `exec: Permission denied`.' \ 'shim=$(ls -t "$HOME"/.vscode/cli/serve-web/*/bin/remote-cli/code \' \ ' "$HOME"/.vscode-server/bin/*/bin/remote-cli/code \' \ ' "$HOME"/.vscode-server/cli/servers/*/server/bin/remote-cli/code \' \ ' 2>/dev/null | head -n1)' \ - 'if [ -z "$shim" ]; then' \ - ' echo "code: no VS Code remote CLI shim found; is a serve-web/remote session running?" >&2' \ + 'if [ -z "$shim" ] || [ ! -x "$shim" ]; then' \ + ' echo "code: no usable VS Code remote CLI shim found; is a serve-web/remote session running?" >&2' \ ' exit 1' \ 'fi' \ '' \ From a68f14b9859aa2e1de40e01b5df6cd734f99b633 Mon Sep 17 00:00:00 2001 From: Venkat Date: Sat, 11 Jul 2026 06:11:48 +0000 Subject: [PATCH 4/5] fix: route `code` to the newest live window socket (tmux fix) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A long-lived shell captures $VSCODE_IPC_HOOK_CLI once. A tmux server that outlives a VS Code reconnect keeps handing every pane the socket of an OLD window. That socket usually still accepts connections, so `code ` "succeeds" but opens the file in a window the user can no longer see — the reported "code doesn't work in tmux" symptom. The prior fallback only triggered when the socket file was missing, so it didn't help here (the stale file still exists and connects). Since VS Code creates one socket per window/connection under /tmp, the newest is the current window. Select the newest socket that still accepts a connection instead of trusting the inherited value. A best-effort socat connect probe skips sockets whose window has gone away; if socat is missing the newest existing socket is used. When no socket is reachable the inherited value is left as-is so socket-less subcommands (code --version, --list-extensions, tunnel) keep working. Add socat to the image so the probe is deterministic. Verified end to end: with the stale tmux socket forced into the environment, the wrapper selects the newest live socket and the file opens in the active window. Co-Authored-By: Claude Opus 4.8 (1M context) --- .devcontainer/Dockerfile | 47 ++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 5fcaab0f1a..242071d54e 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -77,7 +77,7 @@ RUN curl -Lo /usr/local/bin/kubectl https://dl.k8s.io/release/v${VERSION_KUBECTL RUN apt update \ - && apt install tmux dnsutils telnet iputils-ping jq certbot nethogs nload vim -y \ + && apt install tmux dnsutils telnet iputils-ping jq certbot nethogs nload vim socat -y \ && apt clean -y RUN curl -Lo /usr/local/bin/kind https://kind.sigs.k8s.io/dl/v${VERSION_KIND}/kind-linux-amd64 \ && chmod +x /usr/local/bin/kind @@ -205,18 +205,19 @@ RUN mkdir -p /home/vscode/.vscode-server/data/Machine/ && echo '{"workbench.colo # In these headless serve-web/tunnel sessions there is no X display, so running # `code ` in the terminal crashes ("Missing X server or $DISPLAY"). # Drop a wrapper on $HOME/.local/bin (first on the interactive PATH, see .zshrc/.bashrc -# below) that forwards to the serve-web/remote-cli shim, which opens files in the -# already-running VS Code window via the $VSCODE_IPC_HOOK_CLI socket. The wrapper picks -# the newest shim so it survives VS Code updates, and recovers from a stale socket after -# a reconnect (see the inline comments). Only affects interactive shells; build-time -# `code --install-extension` above still resolves to /usr/bin/code. +# below) that forwards to the serve-web/remote-cli shim, which opens files in a running +# VS Code window over its $VSCODE_IPC_HOOK_CLI socket. The wrapper picks the newest shim +# so it survives VS Code updates, and routes to the newest live window socket so it keeps +# working in long-lived shells (e.g. tmux) whose captured socket has gone stale (see the +# inline comments). Only affects interactive shells; build-time `code --install-extension` +# above still resolves to /usr/bin/code. RUN mkdir -p /home/vscode/.local/bin \ && printf '%s\n' \ '#!/usr/bin/env sh' \ '# Route `code` to the VS Code serve-web/remote CLI shim instead of the GUI desktop' \ '# binary (/usr/bin/code), which has no display in this container and crashes.' \ '' \ - '# 1. Locate the remote CLI shim. There is one per installed VS Code build, and the' \ + '# 1. Locate the remote CLI shim. There is one per installed VS Code build and the' \ '# path embeds a build hash, so pick the newest to survive VS Code updates. The' \ '# -x check turns a found-but-not-executable match into the friendly error below' \ '# instead of an opaque `exec: Permission denied`.' \ @@ -229,20 +230,28 @@ RUN mkdir -p /home/vscode/.local/bin \ ' exit 1' \ 'fi' \ '' \ - '# 2. The shim talks to the running window over the IPC socket named in' \ - '# $VSCODE_IPC_HOOK_CLI. That value is captured when the shell starts, so after a' \ - '# VS Code reconnect (container or server restart) it can name a socket that no' \ - '# longer exists. Only when the referenced socket is gone, fall back to the most' \ - '# recently created /tmp/vscode-ipc-*.sock so `code` keeps working without opening' \ - '# a fresh terminal. A still-present socket is left untouched, so routing stays' \ - '# correct when multiple windows (each with its own socket) are connected.' \ - 'if [ -z "$VSCODE_IPC_HOOK_CLI" ] || [ ! -S "$VSCODE_IPC_HOOK_CLI" ]; then' \ - ' newest_sock=$(ls -t /tmp/vscode-ipc-*.sock 2>/dev/null | head -n1)' \ - ' if [ -n "$newest_sock" ]; then' \ - ' VSCODE_IPC_HOOK_CLI="$newest_sock"' \ + '# 2. Choose the IPC socket of the window to open files in. $VSCODE_IPC_HOOK_CLI is' \ + '# captured when the shell starts, so a long-lived shell (e.g. a tmux server that' \ + '# outlives a VS Code reconnect) keeps pointing at an OLD window. That old socket' \ + '# often still accepts connections, so files silently open in a window you can no' \ + '# longer see. VS Code creates one socket per window/connection under /tmp, so the' \ + '# newest is your current window: prefer the newest socket that still accepts a' \ + '# connection, falling back to older ones. If none is reachable, keep whatever the' \ + '# environment provided so socket-less subcommands (code --version, etc.) still work.' \ + 'ipc_live() {' \ + ' [ -S "$1" ] || return 1' \ + ' # Best-effort connect probe to skip sockets whose window has gone away. socat is' \ + ' # optional here: if it is not installed, treat an existing socket as usable.' \ + ' command -v socat >/dev/null 2>&1 || return 0' \ + ' socat -u OPEN:/dev/null UNIX-CONNECT:"$1" >/dev/null 2>&1' \ + '}' \ + 'for sock in $(ls -t /tmp/vscode-ipc-*.sock 2>/dev/null); do' \ + ' if ipc_live "$sock"; then' \ + ' VSCODE_IPC_HOOK_CLI="$sock"' \ ' export VSCODE_IPC_HOOK_CLI' \ + ' break' \ ' fi' \ - 'fi' \ + 'done' \ '' \ '# 3. Hand off to the shim, preserving every argument and its exit status.' \ 'exec "$shim" "$@"' \ From c8d27383bb0b1708411b777d76d4728b8a70564f Mon Sep 17 00:00:00 2001 From: Venkat Date: Sat, 11 Jul 2026 07:02:20 +0000 Subject: [PATCH 5/5] feat: pass code tunnel/serve-web through; harden socket probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a passthrough so `code tunnel` and `code serve-web` (full-CLI subcommands the remote-cli shim does not implement — it would treat "tunnel" as a filename) exec the real /usr/bin/code, which dispatches them to its bundled CLI and runs headless. Everything else keeps routing through the shim. Bound the socket liveness probe with `timeout 2` so a pathological connect() can never wedge `code`. Document the single-window limitation of the "newest socket" heuristic inline. Validated in a full `devcontainer build` of this image via docker exec: tunnel/serve-web show their real help; socket selection skips a dead-but-newest socket, picks the newest live one, overrides a stale inherited VSCODE_IPC_HOOK_CLI (the tmux case), and falls through without hanging when nothing is live. Co-Authored-By: Claude Opus 4.8 (1M context) --- .devcontainer/Dockerfile | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 242071d54e..de000fe978 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -217,6 +217,15 @@ RUN mkdir -p /home/vscode/.local/bin \ '# Route `code` to the VS Code serve-web/remote CLI shim instead of the GUI desktop' \ '# binary (/usr/bin/code), which has no display in this container and crashes.' \ '' \ + '# 0. tunnel/serve-web are full-CLI subcommands the remote-cli shim does not implement' \ + '# (it would treat "tunnel" as a filename to open) and they need no window socket.' \ + '# Hand them to the real /usr/bin/code, which dispatches them to its bundled CLI and' \ + '# runs fine headless. Use the absolute path -- calling `code` here would recurse' \ + '# into this wrapper.' \ + 'case "$1" in' \ + ' tunnel|serve-web) exec /usr/bin/code "$@" ;;' \ + 'esac' \ + '' \ '# 1. Locate the remote CLI shim. There is one per installed VS Code build and the' \ '# path embeds a build hash, so pick the newest to survive VS Code updates. The' \ '# -x check turns a found-but-not-executable match into the friendly error below' \ @@ -238,12 +247,17 @@ RUN mkdir -p /home/vscode/.local/bin \ '# newest is your current window: prefer the newest socket that still accepts a' \ '# connection, falling back to older ones. If none is reachable, keep whatever the' \ '# environment provided so socket-less subcommands (code --version, etc.) still work.' \ + '# LIMITATION: "newest socket" is the current window only with a single window/tab' \ + '# open. With multiple windows, or after an extension-host reload, files may open in' \ + '# another window -- serve-web exposes no API to target a specific window, so this is' \ + '# the best heuristic available.' \ 'ipc_live() {' \ ' [ -S "$1" ] || return 1' \ ' # Best-effort connect probe to skip sockets whose window has gone away. socat is' \ - ' # optional here: if it is not installed, treat an existing socket as usable.' \ + ' # optional here: if it is not installed, treat an existing socket as usable. The' \ + ' # timeout bounds the connect so a pathological socket can never wedge `code`.' \ ' command -v socat >/dev/null 2>&1 || return 0' \ - ' socat -u OPEN:/dev/null UNIX-CONNECT:"$1" >/dev/null 2>&1' \ + ' timeout 2 socat -u OPEN:/dev/null UNIX-CONNECT:"$1" >/dev/null 2>&1' \ '}' \ 'for sock in $(ls -t /tmp/vscode-ipc-*.sock 2>/dev/null); do' \ ' if ipc_live "$sock"; then' \