mosaic dispatch: codex review fixes (comms repo binding, q lookup, watch help)
ci/woodpecker/pr/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
- comms: bind comms-send.sh to the SELECTED repository via
FLEET_COMMS_REPO in the spawned env; without it, --comms-repo chose
the executable but the tool still operated on the default checkout
(codex on 9c8b6ebf). Spec asserts the stub observes the binding.
- q: Object.hasOwn subcommand lookup (reserved property names like
toString must not leak through the record); spec arm added.
- watch: .helpOption(false) — the tool owns help too; --help now passes
through to agent-watch.sh instead of commander's wrapper help
(verified against dist).
This commit is contained in:
@@ -68,7 +68,7 @@ describe('registerCommsCommand routing', () => {
|
|||||||
);
|
);
|
||||||
writeFileSync(
|
writeFileSync(
|
||||||
join(repo, 'tools', 'comms-send.sh'),
|
join(repo, 'tools', 'comms-send.sh'),
|
||||||
`#!/usr/bin/env bash\nprintf '%s\\n' "$*" >> ${JSON.stringify(commsLog)}\nexit 5\n`,
|
`#!/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(brain, 'tools', 'tmux', 'agent-send.sh'), 0o755);
|
||||||
chmodSync(join(repo, 'tools', 'comms-send.sh'), 0o755);
|
chmodSync(join(repo, 'tools', 'comms-send.sh'), 0o755);
|
||||||
@@ -112,7 +112,13 @@ describe('registerCommsCommand routing', () => {
|
|||||||
{ from: 'user' },
|
{ from: 'user' },
|
||||||
);
|
);
|
||||||
expect(process.exitCode).toBe(5);
|
expect(process.exitCode).toBe(5);
|
||||||
expect(readFileSync(f.commsLog, 'utf8').trim()).toBe('-t usc/fred -c human -m hello there');
|
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
|
expect(existsSync(f.tmuxLog)).toBe(false); // same-host tool never invoked
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -105,7 +105,11 @@ export function registerCommsCommand(program: Command): void {
|
|||||||
process.exitCode = 2; // invocation defect: fixable by the caller
|
process.exitCode = 2; // invocation defect: fixable by the caller
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const env = { ...process.env, FLEET_COMMS_SITE: opts.site };
|
// comms-send.sh locates its working repo via FLEET_COMMS_REPO
|
||||||
|
// (default $HOME/src/fleet-comms); without this, --comms-repo
|
||||||
|
// would select the executable but not the repository it operates
|
||||||
|
// on (codex review of 9c8b6ebf).
|
||||||
|
const env = { ...process.env, FLEET_COMMS_SITE: opts.site, FLEET_COMMS_REPO: repo };
|
||||||
const result = spawnSync(tool, fleetCommsSendArgs(opts.site, target, message, opts), {
|
const result = spawnSync(tool, fleetCommsSendArgs(opts.site, target, message, opts), {
|
||||||
stdio: 'inherit',
|
stdio: 'inherit',
|
||||||
env,
|
env,
|
||||||
|
|||||||
@@ -57,6 +57,10 @@ describe('registerQCommand usage + dispatch', () => {
|
|||||||
process.exitCode = undefined;
|
process.exitCode = undefined;
|
||||||
await program.parseAsync(['q', 'bogus'], { from: 'user' });
|
await program.parseAsync(['q', 'bogus'], { from: 'user' });
|
||||||
expect(process.exitCode).toBe(2);
|
expect(process.exitCode).toBe(2);
|
||||||
|
process.exitCode = undefined;
|
||||||
|
// Reserved property names must not leak through the record lookup.
|
||||||
|
await program.parseAsync(['q', 'toString'], { from: 'user' });
|
||||||
|
expect(process.exitCode).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('execs the brain tool with pass-through args and exit code', async () => {
|
it('execs the brain tool with pass-through args and exit code', async () => {
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export function registerQCommand(program: Command): void {
|
|||||||
const home = mosaicHome ?? DEFAULT_MOSAIC_HOME;
|
const home = mosaicHome ?? DEFAULT_MOSAIC_HOME;
|
||||||
|
|
||||||
const sub = args[0];
|
const sub = args[0];
|
||||||
if (!sub || !(sub in SUBCOMMANDS)) {
|
if (!sub || !Object.hasOwn(SUBCOMMANDS, sub)) {
|
||||||
console.error('mosaic q: expected a subcommand:');
|
console.error('mosaic q: expected a subcommand:');
|
||||||
for (const [name, entry] of Object.entries(SUBCOMMANDS)) {
|
for (const [name, entry] of Object.entries(SUBCOMMANDS)) {
|
||||||
console.error(` mosaic q ${name} ${entry.help}`);
|
console.error(` mosaic q ${name} ${entry.help}`);
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ export function registerWatchCommand(program: Command): void {
|
|||||||
// surface without the CLI needing passThroughOptions (which would
|
// surface without the CLI needing passThroughOptions (which would
|
||||||
// force enablePositionalOptions fleet-wide on the root program).
|
// force enablePositionalOptions fleet-wide on the root program).
|
||||||
.allowUnknownOption()
|
.allowUnknownOption()
|
||||||
|
// The tool owns help too: without this, commander would intercept
|
||||||
|
// --help and answer with wrapper help instead of agent-watch's own
|
||||||
|
// (codex review of 18f3dd49).
|
||||||
|
.helpOption(false)
|
||||||
.argument('[args...]', 'args passed through to agent-watch.sh')
|
.argument('[args...]', 'args passed through to agent-watch.sh')
|
||||||
.action(async (args: string[], _opts: unknown, command: Command) => {
|
.action(async (args: string[], _opts: unknown, command: Command) => {
|
||||||
// --mosaic-home is not global in this CLI; walk parents for it and
|
// --mosaic-home is not global in this CLI; walk parents for it and
|
||||||
|
|||||||
Reference in New Issue
Block a user