mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 12:39:42 +02:00
Remote shell-mode sessions hardcoded 'exec bash -l', ignoring the remote user's actual login shell. sshd sets $SHELL from the remote user's /etc/passwd entry, so 'exec $SHELL -i -l' launches their real shell (zsh, fish, etc.) with rc files sourced, same fix as the local shell-mode launch. Also set remain-on-exit on the remote tmux session. It was only ever set on the local socket, so if the remote command exited for any reason -- even something transient -- tmux destroyed the pane, window, and (being the only session) the whole remote server, tearing down the local ssh attach along with it and leaving no trace to diagnose. The local pane saw this as an instant clean exit, and reconnect's -A then created a fresh session, which could repeat as a flap loop with no evidence surviving between attempts. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
68 lines
2.2 KiB
TypeScript
68 lines
2.2 KiB
TypeScript
import { mkdtempSync, rmSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { afterEach, describe, expect, it } from 'vitest';
|
|
import {
|
|
defaultRemoteCommandForMode,
|
|
readRemoteCases,
|
|
readRemoteHosts,
|
|
remoteDisplayPath,
|
|
remoteSshTarget,
|
|
writeRemoteCases,
|
|
writeRemoteHosts,
|
|
} from '../src/remote-hosts.js';
|
|
|
|
describe('remote-hosts domain', () => {
|
|
let dir: string | null = null;
|
|
|
|
afterEach(() => {
|
|
if (dir) rmSync(dir, { recursive: true, force: true });
|
|
dir = null;
|
|
});
|
|
|
|
function configDir(): string {
|
|
dir = mkdtempSync(join(tmpdir(), 'codeman-remote-hosts-'));
|
|
return dir;
|
|
}
|
|
|
|
it('round-trips remote hosts and remote cases from a config directory', async () => {
|
|
const root = configDir();
|
|
await writeRemoteHosts(root, [
|
|
{
|
|
id: 'gpu-box',
|
|
label: 'GPU Box',
|
|
host: '10.0.0.42',
|
|
username: 'ubuntu',
|
|
commands: { codex: 'exec codx personal' },
|
|
},
|
|
]);
|
|
await writeRemoteCases(root, [
|
|
{ name: 'gpu-work', type: 'remote', hostId: 'gpu-box', remotePath: '/home/ubuntu/work' },
|
|
]);
|
|
|
|
await expect(readRemoteHosts(root)).resolves.toEqual([
|
|
{
|
|
id: 'gpu-box',
|
|
label: 'GPU Box',
|
|
host: '10.0.0.42',
|
|
username: 'ubuntu',
|
|
commands: { codex: 'exec codx personal' },
|
|
},
|
|
]);
|
|
await expect(readRemoteCases(root)).resolves.toEqual([
|
|
{ name: 'gpu-work', type: 'remote', hostId: 'gpu-box', remotePath: '/home/ubuntu/work' },
|
|
]);
|
|
});
|
|
|
|
it('returns safe mode defaults and remote display values', () => {
|
|
expect(defaultRemoteCommandForMode('shell')).toBe('exec $SHELL -i -l');
|
|
expect(defaultRemoteCommandForMode('codex')).toBe('exec codex');
|
|
// Mirrors the local claude default so the remote agent runs non-interactively.
|
|
expect(defaultRemoteCommandForMode('claude')).toBe('exec claude --dangerously-skip-permissions');
|
|
expect(remoteSshTarget({ id: 'h1', label: 'H1', host: 'box.local', username: 'aamer' })).toBe('aamer@box.local');
|
|
expect(remoteDisplayPath({ username: 'aamer', host: 'box.local', path: '/opt/work' })).toBe(
|
|
'aamer@box.local:/opt/work'
|
|
);
|
|
});
|
|
});
|