fix(session): probe the Claude CLI version over ssh for remote sessions

Remote Claude sessions were the one backend left relying on the
startup-banner scrape for cliVersion (the unreliable path #154 was filed
for: newer Claude Code builds print no banner and resumed sessions never
do), so wheel/touch forwarding silently stayed off for them. Mirror the
docker approach: a deferred best-effort probe at session start, running
claude --version on the remote host through the same
buildSshConnectionArgs + login-shell wrapper as the real launch, parsing
the first semver in stdout (an interactive login shell may echo rc-file
noise around it).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer
2026-08-07 05:05:41 +02:00
parent a1d7ec02e9
commit a7a1cef3d6
3 changed files with 126 additions and 3 deletions
+32 -1
View File
@@ -20,7 +20,12 @@
import { homedir } from 'node:os';
import { describe, it, expect } from 'vitest';
import { buildSshConnectionArgs, buildRemoteTmuxCheckCommand, remoteSshTarget } from '../src/remote-hosts.js';
import {
buildSshConnectionArgs,
buildRemoteTmuxCheckCommand,
buildRemoteCliVersionProbeCommand,
remoteSshTarget,
} from '../src/remote-hosts.js';
import { buildRemoteLaunchCommand } from '../src/tmux-manager.js';
import type { SessionRemote } from '../src/types.js';
@@ -198,3 +203,29 @@ describe('COD-107 buildRemoteTmuxCheckCommand — same connection options as the
expect(buildRemoteTmuxCheckCommand({ username: 'ubuntu', host: '10.0.0.42', port: 2222 })).toContain('-p 2222');
});
});
describe('buildRemoteCliVersionProbeCommand: remote CLI version over the same connection (#205)', () => {
it('routes the version query through the interactive-login shell wrapper, like the launch', () => {
const cmd = buildRemoteCliVersionProbeCommand(baseRemote, 'claude');
// Same PATH-resolution wrapper as defaultRemoteCommandForMode: a bare
// `claude --version` over ssh sees only sshd's minimal PATH (exit 127).
expect(cmd).toBe(
'ssh -o BatchMode=yes -o ConnectTimeout=10 ubuntu@10.0.0.42 ' +
`'exec "\${SHELL:-/bin/sh}" -i -l -c '\\''claude --version'\\'''`
);
});
it('uses the shared connection args (proxy/identity/port), so it reaches what the launch reaches', () => {
const cmd = buildRemoteCliVersionProbeCommand(aaDesktop, 'claude');
expect(cmd).toContain('-o BatchMode=yes');
expect(cmd).toContain('-p 2222');
expect(cmd).toContain(`-i '${HOME}/.ssh/remote_ed25519'`);
expect(cmd).toContain("-o 'ProxyCommand=nc -X 5 -x 127.0.0.1:1080 %h %p'");
expect(cmd).toContain('aakht@192.168.55.170');
});
it('maps antigravity to its real binary name and shell to no probe at all', () => {
expect(buildRemoteCliVersionProbeCommand(baseRemote, 'antigravity')).toContain('agy --version');
expect(buildRemoteCliVersionProbeCommand(baseRemote, 'shell')).toBeNull();
});
});