diff --git a/packages/cli/src/commands/daemon/local-daemon.ts b/packages/cli/src/commands/daemon/local-daemon.ts index 7895e2902..e8721ba85 100644 --- a/packages/cli/src/commands/daemon/local-daemon.ts +++ b/packages/cli/src/commands/daemon/local-daemon.ts @@ -169,12 +169,20 @@ function sleep(ms: number): Promise { }) } +function readNodeErrnoCode(error: unknown): string | undefined { + if (typeof error !== 'object' || error === null || !('code' in error)) { + return undefined + } + + return typeof error.code === 'string' ? error.code : undefined +} + function isProcessRunning(pid: number): boolean { try { process.kill(pid, 0) return true } catch (err) { - const code = typeof err === 'object' && err && 'code' in err ? (err as { code?: string }).code : undefined + const code = readNodeErrnoCode(err) if (code === 'EPERM') { return true } @@ -187,7 +195,7 @@ function signalProcess(pid: number, signal: NodeJS.Signals): boolean { process.kill(pid, signal) return true } catch (err) { - const code = typeof err === 'object' && err && 'code' in err ? (err as { code?: string }).code : undefined + const code = readNodeErrnoCode(err) if (code === 'ESRCH') { return false } diff --git a/packages/cli/tests/18-local-daemon-utils.test.ts b/packages/cli/tests/18-local-daemon-utils.test.ts new file mode 100644 index 000000000..43119077a --- /dev/null +++ b/packages/cli/tests/18-local-daemon-utils.test.ts @@ -0,0 +1,43 @@ +#!/usr/bin/env npx tsx + +/** + * Phase 18: Local daemon utility tests. + * + * Tests pure helpers that do not require a running daemon. + */ + +import assert from 'node:assert' +import { resolveTcpHostFromListen } from '../src/commands/daemon/local-daemon.js' + +console.log('=== Local Daemon Utility Helpers ===\n') + +{ + console.log('Test 1: resolves numeric listen values to localhost host:port') + assert.strictEqual(resolveTcpHostFromListen('6767'), '127.0.0.1:6767') + assert.strictEqual(resolveTcpHostFromListen(' 7777 '), '127.0.0.1:7777') + console.log('✓ resolves numeric listen values\n') +} + +{ + console.log('Test 2: preserves explicit host:port listen values') + assert.strictEqual(resolveTcpHostFromListen('localhost:6767'), 'localhost:6767') + assert.strictEqual(resolveTcpHostFromListen('0.0.0.0:8080'), '0.0.0.0:8080') + console.log('✓ preserves explicit host:port values\n') +} + +{ + console.log('Test 3: rejects unix socket listen values') + assert.strictEqual(resolveTcpHostFromListen('/tmp/paseo.sock'), null) + assert.strictEqual(resolveTcpHostFromListen('unix:///tmp/paseo.sock'), null) + console.log('✓ rejects unix socket listen values\n') +} + +{ + console.log('Test 4: rejects empty and non-host listen values') + assert.strictEqual(resolveTcpHostFromListen(''), null) + assert.strictEqual(resolveTcpHostFromListen(' '), null) + assert.strictEqual(resolveTcpHostFromListen('localhost'), null) + console.log('✓ rejects empty and non-host listen values\n') +} + +console.log('=== All local daemon utility tests passed ===')