mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-13 18:29:32 +01:00
A remote host whose login shell is tcsh came up on 1.48.0, when the local
sh rewrote ${SHELL:-/bin/sh} to the Codeman host's shell. Since #770 the
launch reaches the far end verbatim and runs tcsh itself, and tcsh refuses
-i -l -c, -l -c and -i -l, so every stock launch left a dead pane.
The login flags are now picked on the far end, in the shape of
loginShellArgs(): set -- -i -l, cleared for csh and tcsh, then
exec "${SHELL:-/bin/sh}" "$@". The text is one brace group, so
cd <path> && { ...; } still starts nothing when the cd fails. bsd-csh takes
every flag combination, csh is listed because it is tcsh on macOS and the BSDs.
The version probe keeps its one statement: the login shell parses it, not
/bin/sh, so fish and tcsh refuse it either way, as they did on 1.48.0.
Measured with the real builder on a real sshd in a throwaway Debian container
(tcsh 6.24.07, bsd-csh, fish 3.6, bash) and through a fake sshd with
tcsh 6.24.10, fish, zsh, dash and bash, a real Codeman server on tcsh included.
The tcsh disclosure comes out of the docs and the changeset. They keep the two
limits 1.48.0 had too: tcsh has no export, so the PATH bootstrap only prints an
error, and the tmux probe needs a command executable, which Debian lacks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
64 lines
2.8 KiB
TypeScript
64 lines
2.8 KiB
TypeScript
/**
|
|
* @fileoverview Golden pins for the remote/docker LOCATION OVERLAY commands, now that both
|
|
* are read from `overlays.<location>` on the registry entry rather than from a hardcoded
|
|
* `Record<…CommandMode, string>` in each file.
|
|
*
|
|
* The literals below are transcribed from those two tables as they stood BEFORE the wiring,
|
|
* which is the whole point: the tables were dead-simple duplicates of registry data with
|
|
* nothing keeping the two in step, and the way to delete a duplicate safely is to pin what it
|
|
* produced first. A diff here means an entry's `overlays` (or its first declared binary)
|
|
* changed what a remote or in-container pane actually runs.
|
|
*
|
|
* Note the two arms deliberately NOT read from an entry, each for its own reason: remote
|
|
* `shell` resolves the REMOTE user's login shell (unknowable from here, hence `$SHELL`), and
|
|
* docker `shell` is the entry that declares `docker: { disabled: true }` — a container has no
|
|
* per-user login shell to resolve, so it gets a plain `bash -l`.
|
|
*
|
|
* Port: none (pure, over registry data).
|
|
*/
|
|
|
|
import { it, expect } from 'vitest';
|
|
import { defaultRemoteCommandForMode, remoteLoginShellCommand } from '../src/remote-hosts.js';
|
|
import { defaultDockerCommandForMode } from '../src/docker-hosts.js';
|
|
import type { SessionMode } from '../src/types/session.js';
|
|
|
|
it('pins every remote pane command', () => {
|
|
const expected: Record<string, string> = {
|
|
shell: '{ set -- -i -l; case ${SHELL##*/} in csh|tcsh) set --;; esac; exec "${SHELL:-/bin/sh}" "$@"; }',
|
|
claude: remoteLoginShellCommand('claude --dangerously-skip-permissions'),
|
|
opencode: remoteLoginShellCommand('opencode'),
|
|
codex: remoteLoginShellCommand('codex'),
|
|
gemini: remoteLoginShellCommand('gemini'),
|
|
antigravity: remoteLoginShellCommand('agy'),
|
|
pi: remoteLoginShellCommand('pi'),
|
|
grok: remoteLoginShellCommand('grok'),
|
|
deepseek: remoteLoginShellCommand('dsh'),
|
|
omp: remoteLoginShellCommand('omp'),
|
|
copilot: remoteLoginShellCommand('copilot'),
|
|
};
|
|
for (const [mode, want] of Object.entries(expected)) {
|
|
expect(defaultRemoteCommandForMode(mode as SessionMode), mode).toBe(want);
|
|
}
|
|
expect(defaultRemoteCommandForMode('nope' as SessionMode)).toBe(expected.shell);
|
|
});
|
|
|
|
it('pins every in-container pane command', () => {
|
|
const expected: Record<string, string> = {
|
|
shell: 'exec bash -l',
|
|
claude: 'exec claude --dangerously-skip-permissions',
|
|
opencode: 'exec opencode',
|
|
codex: 'exec codex',
|
|
gemini: 'exec gemini',
|
|
antigravity: 'exec agy',
|
|
pi: 'exec pi',
|
|
grok: 'exec grok',
|
|
deepseek: 'exec dsh',
|
|
omp: 'exec omp',
|
|
copilot: 'exec copilot',
|
|
};
|
|
for (const [mode, want] of Object.entries(expected)) {
|
|
expect(defaultDockerCommandForMode(mode as SessionMode), mode).toBe(want);
|
|
}
|
|
expect(defaultDockerCommandForMode('nope' as SessionMode)).toBe('exec bash -l');
|
|
});
|