import { chmodSync, existsSync, mkdirSync, mkdtempSync, writeFileSync, readFileSync, } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { Command } from 'commander'; import { afterEach, describe, expect, it } from 'vitest'; import { fleetCommsSendArgs, registerCommsCommand, tmuxSendArgs } from './comms.js'; describe('arg translation', () => { // B2 (2026-08-29): when --socket is omitted, the CLI forwards NO -L and // agent-send.sh's own resolution governs (explicit -L > MOSAIC_TMUX_SOCKET // > unique hit > ambiguity refusal). Forwarding a guessed default here // would defeat that resolution and reintroduce the stale-twin defect. it('omits -L entirely when --socket is not given (tool-owned resolution)', () => { const args = tmuxSendArgs('orch-01', 'hello', {}); expect(args).toEqual(['-s', 'orch-01', '-m', 'hello']); expect(args).not.toContain('-L'); expect(args.join(' ')).not.toContain('-L'); }); it('tmux path: -s/-C/-L/-f/-m per agent-send.sh getopts', () => { expect(tmuxSendArgs('orch-01', 'hello', {})).toEqual(['-s', 'orch-01', '-m', 'hello']); expect( tmuxSendArgs('orch-01', 'unused', { class: 'actionable', socket: 'mosaic-fleet', file: '/tmp/body.txt', }), ).toEqual(['-s', 'orch-01', '-C', 'actionable', '-L', 'mosaic-fleet', '-f', '/tmp/body.txt']); }); it('fleet-comms path: -t site/agent and -c class', () => { expect(fleetCommsSendArgs('usc', 'fred', 'hi', {})).toEqual(['-t', 'usc/fred', '-m', 'hi']); expect(fleetCommsSendArgs('usc', 'fred', 'hi', { class: 'human' })).toEqual([ '-t', 'usc/fred', '-c', 'human', '-m', 'hi', ]); }); }); describe('registerCommsCommand routing', () => { const savedBrain = process.env['MOSAIC_BRAIN_HOME']; const savedRepo = process.env['MOSAIC_FLEET_COMMS_REPO']; const savedAgent = process.env['MOSAIC_AGENT_NAME']; afterEach(() => { for (const [k, v] of [ ['MOSAIC_BRAIN_HOME', savedBrain], ['MOSAIC_FLEET_COMMS_REPO', savedRepo], ['MOSAIC_AGENT_NAME', savedAgent], ] as const) { if (v === undefined) delete process.env[k]; else process.env[k] = v; } process.exitCode = undefined; }); function fixture(): { brain: string; repo: string; tmuxLog: string; commsLog: string } { const brain = mkdtempSync(join(tmpdir(), 'comms-brain-')); const repo = mkdtempSync(join(tmpdir(), 'comms-repo-')); mkdirSync(join(brain, 'tools', 'tmux'), { recursive: true }); mkdirSync(join(repo, 'tools'), { recursive: true }); const tmuxLog = join(brain, 'tmux.log'); const commsLog = join(repo, 'comms.log'); writeFileSync( join(brain, 'tools', 'tmux', 'agent-send.sh'), `#!/usr/bin/env bash\nprintf '%s\\n' "$*" >> ${JSON.stringify(tmuxLog)}\nexit 7\n`, ); writeFileSync( join(repo, 'tools', 'comms-send.sh'), `#!/usr/bin/env bash\nprintf '%s\\n' "$*" >> ${JSON.stringify(commsLog)}\nprintf 'FLEET_COMMS_REPO=%s FLEET_COMMS_SITE=%s\\n' "$FLEET_COMMS_REPO" "$FLEET_COMMS_SITE" >> ${JSON.stringify(commsLog)}\nexit 5\n`, ); chmodSync(join(brain, 'tools', 'tmux', 'agent-send.sh'), 0o755); chmodSync(join(repo, 'tools', 'comms-send.sh'), 0o755); process.env['MOSAIC_BRAIN_HOME'] = brain; process.env['MOSAIC_FLEET_COMMS_REPO'] = repo; process.env['MOSAIC_AGENT_NAME'] = 'tester'; return { brain, repo, tmuxLog, commsLog }; } it('default routes same-host via agent-send with translated flags and passes rc through', async () => { const f = fixture(); const program = new Command(); registerCommsCommand(program); await program.parseAsync( [ 'comms', 'send', 'orch-01', '--class', 'actionable', '--socket', 'mosaic-fleet', 'verdict', 'landed', ], { from: 'user' }, ); expect(process.exitCode).toBe(7); expect(readFileSync(f.tmuxLog, 'utf8').trim()).toBe( '-s orch-01 -C actionable -L mosaic-fleet -m verdict landed', ); expect(existsSync(f.commsLog)).toBe(false); // inter-site tool never invoked }); it('--site routes inter-site via comms-send with site-prefixed target and passes rc through', async () => { const f = fixture(); const program = new Command(); registerCommsCommand(program); await program.parseAsync( ['comms', 'send', 'fred', '--site', 'usc', '--class', 'human', 'hello', 'there'], { from: 'user' }, ); expect(process.exitCode).toBe(5); expect(readFileSync(f.commsLog, 'utf8').split('\n')[0]?.trim()).toBe( '-t usc/fred -c human -m hello there', ); // The sender must bind comms-send.sh to the SELECTED repo (codex 9c8b6ebf). expect(readFileSync(f.commsLog, 'utf8')).toContain( `FLEET_COMMS_REPO=${f.repo} FLEET_COMMS_SITE=usc`, ); expect(existsSync(f.tmuxLog)).toBe(false); // same-host tool never invoked }); it('inter-site without MOSAIC_AGENT_NAME is an invocation defect (exit 2)', async () => { const f = fixture(); delete process.env['MOSAIC_AGENT_NAME']; const program = new Command(); registerCommsCommand(program); await program.parseAsync(['comms', 'send', 'fred', '--site', 'usc', 'hi'], { from: 'user' }); expect(process.exitCode).toBe(2); expect(existsSync(f.commsLog)).toBe(false); // inter-site tool never invoked }); it('missing fleet-comms repo fails 127 naming the expected path', async () => { fixture(); process.env['MOSAIC_FLEET_COMMS_REPO'] = '/nonexistent-comms-repo'; const program = new Command(); registerCommsCommand(program); await program.parseAsync(['comms', 'send', 'fred', '--site', 'usc', 'hi'], { from: 'user' }); expect(process.exitCode).toBe(127); }); });