diff --git a/src/commands/device.ts b/src/commands/device.ts index fab1bc2..3f63694 100644 --- a/src/commands/device.ts +++ b/src/commands/device.ts @@ -1094,7 +1094,8 @@ const requestHumanSub = defineCommand({ ...commonArgs, reason: { type: 'positional', - required: true, + // Checked in run, so a missing reason still fails with the --json envelope. + required: false, description: 'What the person should do, e.g. "Enter the 2FA code sent to the test phone"', }, id: idArg, diff --git a/test/unit/device-command.test.ts b/test/unit/device-command.test.ts index 81927b7..af7d334 100644 --- a/test/unit/device-command.test.ts +++ b/test/unit/device-command.test.ts @@ -3,6 +3,8 @@ import * as fs from 'node:fs'; import * as os from 'node:os'; import * as path from 'node:path'; +import { runCommand } from 'citty'; + import { collectFlagValues, decodeDataUrl, @@ -610,6 +612,38 @@ describe('dcd device', () => { expect(calls).to.have.length(0); }); + it('request-human with no reason fails with the --json envelope', async () => { + // Through citty's parser, not run(): a required positional would be + // refused before run() and print a different shape. + const calls = stubFetch(() => ({ body: {} })); + let out = ''; + const write = process.stdout.write.bind(process.stdout); + const exit = process.exit; + process.stdout.write = ((chunk: string) => { + out += chunk; + return true; + }) as typeof process.stdout.write; + process.exit = ((code?: number) => { + throw new ExitError(code ?? 0); + }) as typeof process.exit; + let code = 0; + try { + await runCommand(deviceCommand, { rawArgs: ['request-human', '--json', '--api-url', API, '--api-key', 'k'] }); + } catch (error) { + if (!(error instanceof ExitError)) throw error; + code = error.code; + } finally { + process.stdout.write = write; + process.exit = exit; + } + + const envelope = JSON.parse(out) as { error: { message: string }; ok: boolean }; + expect(code).to.equal(DEVICE_EXIT.usage); + expect(envelope.ok).to.equal(false); + expect(envelope.error.message).to.match(/what the person should do/i); + expect(calls).to.have.length(0); + }); + it('request-human --wait polls until the session is handed back', async () => { let polls = 0; const calls = stubFetch((call) => {