From 8b20f5b1f8aa004bfd484a5df89dd4a32c8b811b Mon Sep 17 00:00:00 2001 From: d fei Date: Sat, 29 Aug 2026 21:01:28 -0700 Subject: [PATCH] fix(docker): probe container CLIs by their real binary name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The adoption preflight used the mode name as the binary name. claude, codex, opencode, gemini and pi happen to match, so it never showed — but antigravity ships as `agy` and deepseek as `dsh`, so a container that has either was reported as not having it, and the mode was silently dropped from the case. Adds a MODE_BINARIES map, single-sourced with defaultDockerCommandForMode, which launches those same binaries. Probing and result filtering share one `binaryFor` so the two cannot drift apart. --- src/docker-hosts.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/docker-hosts.ts b/src/docker-hosts.ts index 7ad11346..d9d2de1b 100644 --- a/src/docker-hosts.ts +++ b/src/docker-hosts.ts @@ -67,9 +67,19 @@ export const DOCKER_ADOPT_PROBE_MODES = [ 'gemini', 'antigravity', 'pi', + 'grok', + 'deepseek', 'shell', ] as const satisfies readonly SessionMode[]; +/** + * The BINARY a mode looks for inside a container. Not always the mode name: + * `antigravity` ships as `agy` and `deepseek` as `dsh`, so probing by mode name + * would report those two as missing on a container that has them. Single source + * with `defaultDockerCommandForMode`, which launches the same binaries. + */ +const MODE_BINARIES: Partial> = { antigravity: 'agy', deepseek: 'dsh' }; + /** Per-case container name prefix. The `case` letters deliberately do NOT matter to * tmux; this is a DOCKER name (`^[a-zA-Z0-9][a-zA-Z0-9_.-]+$`), and case names are * already validated `^[a-zA-Z0-9_-]+$`, so `codeman-case-` is always valid. */ @@ -1154,7 +1164,9 @@ export async function probeAdoptableContainer( } // One exec resolves tmux plus every requested CLI, so adoption costs a single // round trip. Binaries are fixed mode names, never user input. - const probes = ['tmux', ...modes.filter((m) => m !== 'shell')]; + const wanted = modes.filter((m) => m !== 'shell'); + const binaryFor = (mode: SessionMode) => MODE_BINARIES[mode] ?? mode; + const probes = ['tmux', ...wanted.map(binaryFor)]; // `; exit 0` is load-bearing: the script's status is its LAST command's, so a // missing final CLI made the whole `sh -lc` exit 1 and the probe reported // "could not exec into the container" for a container that was perfectly fine. @@ -1206,7 +1218,7 @@ export async function probeAdoptableContainer( running: true, image, tmuxPath: 'tmux', - availableModes: modes.filter((m) => m === 'shell' || found.has(m)), + availableModes: modes.filter((m) => m === 'shell' || found.has(binaryFor(m))), workdirExists, }; } catch (err) {