mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 12:39:42 +02:00
Since COD-104 a remote session lives in a durable tmux server on the host and outlives the local pane, so killing a tab only DETACHED — even for sessions we own. Propagate `kill-session` to the remote for OWNED sessions in killSession's owned path (after COD-105's non-owned detach-only early-return); non-owned detach-only is untouched. Reconciled onto upstream PR #145: #145 already upstreamed this exact owned-kill propagation as `buildRemoteKillCommand({ remote, sessionId })` on the dedicated `-L codeman-remote` socket (matching buildRemoteLaunchCommand) and wired it into killSession (Strategy 3b, owned-only, fire-and-forget). The original COD-109 commit added a second `buildRemoteKillCommand(remote, name)` overload on the old `-L codeman` socket plus a duplicate kill block — a compile error AND a wrong socket post-#145 (owned sessions no longer live on `codeman`). This commit keeps #145's socket-correct implementation and drops the duplicate; the required test/remote-kill-command.test.ts is retargeted to #145's `{ remote, sessionId }` signature and the `codeman-remote` socket. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
60 lines
3.0 KiB
TypeScript
60 lines
3.0 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { buildRemoteKillCommand } from '../src/tmux-manager.js';
|
|
import type { SessionRemote } from '../src/types.js';
|
|
|
|
// COD-109 — terminate an OWNED durable remote tmux session by propagating
|
|
// `kill-session` to the remote host. Since COD-104 an owned remote session lives
|
|
// in the dedicated `-L codeman-remote` tmux server on the host and outlives the
|
|
// local ssh pane, so ending a session we OWN must reach the remote socket.
|
|
//
|
|
// The owned-kill command builder was consolidated upstream (PR #145) into the
|
|
// `{ remote, sessionId }` form, which derives the durable session name and kills
|
|
// on the dedicated `codeman-remote` socket (matching buildRemoteLaunchCommand).
|
|
// This suite pins that builder's contract; the killSession integration
|
|
// (owned-only, after COD-105's non-owned detach-only early-return) is exercised
|
|
// against the real remote.
|
|
describe('COD-109 buildRemoteKillCommand (owned durable remote kill)', () => {
|
|
const base: SessionRemote = {
|
|
hostId: 'h',
|
|
label: 'aa',
|
|
host: '192.168.55.170',
|
|
username: 'aakht',
|
|
remotePath: '/tmp',
|
|
};
|
|
|
|
it('kills the durable session on the dedicated codeman-remote socket (no ssh -t — non-interactive)', () => {
|
|
const cmd = buildRemoteKillCommand({ remote: base, sessionId: 'abc12345def' });
|
|
expect(cmd.startsWith('ssh -o BatchMode=yes ')).toBe(true);
|
|
expect(cmd).toContain('aakht@192.168.55.170');
|
|
// Owned sessions launch on `-L codeman-remote`; the kill MUST target the same socket.
|
|
expect(cmd).toContain('tmux -L codeman-remote kill-session -t');
|
|
// Deterministic session name derived from the sessionId (codeman-ssh-<first 8>).
|
|
expect(cmd).toContain('codeman-ssh-abc12345');
|
|
// kill-session needs no PTY — must NOT request the ssh `-t` flag (attach uses
|
|
// `ssh -o BatchMode=yes -t …`; kill must not). The ` -t ` inside the quoted
|
|
// `kill-session -t <name>` is the tmux target flag, which is expected.
|
|
expect(cmd).not.toContain('-o BatchMode=yes -t');
|
|
});
|
|
|
|
it('shares the default ConnectTimeout so an unreachable host fails fast (never blocks kill)', () => {
|
|
const cmd = buildRemoteKillCommand({ remote: base, sessionId: 'abc12345def' });
|
|
expect(cmd).toContain('-o ConnectTimeout=10');
|
|
});
|
|
|
|
it('reuses the COD-107 connection options (port / identity / SOCKS proxy)', () => {
|
|
const cmd = buildRemoteKillCommand({
|
|
remote: { ...base, port: 2222, identityFile: '~/.ssh/remote_ed25519', socksProxy: '127.0.0.1:1080' },
|
|
sessionId: 'abc12345def',
|
|
});
|
|
expect(cmd).toContain('-p 2222');
|
|
expect(cmd).toMatch(/-i '.*\/\.ssh\/remote_ed25519'/);
|
|
expect(cmd).toContain("-o 'ProxyCommand=nc -X 5 -x 127.0.0.1:1080 %h %p'");
|
|
});
|
|
|
|
it('shell-escapes the derived session name so metachars stay one token', () => {
|
|
const cmd = buildRemoteKillCommand({ remote: base, sessionId: "x'; rm -rf /" });
|
|
expect(cmd).not.toMatch(/rm -rf \/\s*$/); // not a bare trailing command
|
|
expect(cmd).toContain('kill-session -t');
|
|
});
|
|
});
|