diff --git a/src/cli.ts b/src/cli.ts index c7b2a09b..12ed15b4 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1857,10 +1857,15 @@ cli({ const daemonCmd = program.command('daemon').description('Manage the webcmd daemon'); // Snapshot before applyRootSubcommandSummaries() rewrites .description() to a child-name listing. const originalDaemonDescription = daemonCmd.description(); - daemonCmd + const daemonStatusCmd = daemonCmd .command('status') .description('Show daemon status') - .action(async () => { await daemonStatus(); }); + .option('-f, --format ', OUTPUT_FORMAT_HELP, 'table'); + daemonStatusCmd.action(async (opts) => { + const fmt = resolveOutputFormat(opts.format); + if (fmt === null) return; + await daemonStatus(fmt); + }); daemonCmd .command('stop') .description('Stop the daemon') diff --git a/src/commands/daemon.test.ts b/src/commands/daemon.test.ts index 76902221..5f6a3076 100644 --- a/src/commands/daemon.test.ts +++ b/src/commands/daemon.test.ts @@ -1,4 +1,5 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import yaml from 'js-yaml'; const { fetchDaemonStatusMock, @@ -105,6 +106,64 @@ describe('daemonStatus', () => { expect(stdoutSpy).toHaveBeenCalledWith(expect.stringContaining('fake connected')); }); + + it('renders a structured envelope for -f json when running (#175)', async () => { + fetchDaemonStatusMock.mockResolvedValue({ + ok: true, + pid: 12345, + uptime: 60, + daemonVersion: PKG_VERSION, + runtimeConnected: true, + runtimeName: 'fake', + pending: 0, + memoryMB: 64, + port: 9777, + }); + + await daemonStatus('json'); + + const printed = stdoutSpy.mock.calls.map((c: unknown[]) => c[0]).join('\n'); + const data = JSON.parse(printed); + expect(data).toMatchObject({ running: true, pid: 12345, port: 9777 }); + }); + + it('renders a structured envelope for -f json when not running (#175)', async () => { + fetchDaemonStatusMock.mockResolvedValue(null); + + await daemonStatus('json'); + + const printed = stdoutSpy.mock.calls.map((c: unknown[]) => c[0]).join('\n'); + expect(JSON.parse(printed)).toEqual({ running: false }); + }); + + it('renders a structured envelope for -f yaml when running (#175)', async () => { + fetchDaemonStatusMock.mockResolvedValue({ + ok: true, + pid: 12345, + uptime: 60, + daemonVersion: PKG_VERSION, + runtimeConnected: true, + runtimeName: 'fake', + pending: 0, + memoryMB: 64, + port: 9777, + }); + + await daemonStatus('yaml'); + + const printed = stdoutSpy.mock.calls.map((c: unknown[]) => c[0]).join('\n'); + const data = yaml.load(printed) as Record; + expect(data).toMatchObject({ running: true, pid: 12345, port: 9777 }); + }); + + it('renders a structured envelope for -f yaml when not running (#175)', async () => { + fetchDaemonStatusMock.mockResolvedValue(null); + + await daemonStatus('yaml'); + + const printed = stdoutSpy.mock.calls.map((c: unknown[]) => c[0]).join('\n'); + expect(yaml.load(printed)).toEqual({ running: false }); + }); }); // ──────────────────────────────────────────────────────────────────── diff --git a/src/commands/daemon.ts b/src/commands/daemon.ts index ab69e22c..d3a91a32 100644 --- a/src/commands/daemon.ts +++ b/src/commands/daemon.ts @@ -11,9 +11,14 @@ import { formatDuration } from '../download/progress.js'; import { log } from '../logger.js'; import { PKG_VERSION } from '../version.js'; import { formatDaemonVersion, isDaemonStale } from '../browser/daemon-version.js'; +import { render } from '../output.js'; -export async function daemonStatus(): Promise { +export async function daemonStatus(fmt: string = 'table'): Promise { const status = await fetchDaemonStatus(); + if (fmt !== 'table') { + await render(status ? { running: true, ...status } : { running: false }, { fmt }); + return; + } if (!status) { console.log('Daemon: not running'); return;