From 8cd4010a8c44d60cf753c43e80251787dde99d30 Mon Sep 17 00:00:00 2001 From: melvinchia3636 Date: Wed, 4 Feb 2026 22:51:49 +0800 Subject: [PATCH] fix(cli): replace platform-specific `netstat`/`ss` commands with `lsof` for unified port usage checks. --- tools/src/utils/helpers.ts | 88 ++++++++------------------------------ 1 file changed, 19 insertions(+), 69 deletions(-) diff --git a/tools/src/utils/helpers.ts b/tools/src/utils/helpers.ts index fdfaebc57..130009c79 100644 --- a/tools/src/utils/helpers.ts +++ b/tools/src/utils/helpers.ts @@ -139,80 +139,30 @@ export function checkAddressInUse(address: string, port: string): boolean { try { executeCommand('nc', { exitOnError: false }, ['-zv', address, port]) - const isMacOS = process.platform === 'darwin' + const lsofOutput = executeCommand('lsof', { exitOnError: false }, [ + '-i', + `@${address}:${port}`, + '-sTCP:LISTEN' + ]) - if (isMacOS) { - const netstatOutput = executeCommand('netstat', { exitOnError: false }, [ - '-anv', - '-p', - 'tcp' - ]) + const lines = lsofOutput.trim().split('\n') - const lines = netstatOutput.trim().split('\n') + const dataLine = lines.find(line => !line.startsWith('COMMAND')) - const matchingLine = lines.find( - line => - line.includes(`${address}.${port}`) || - line.includes(`*.${port}`) || - (address === 'localhost' && line.includes(`127.0.0.1.${port}`)) || - (address === '127.0.0.1' && line.includes(`localhost.${port}`)) + if (dataLine) { + const parts = dataLine.trim().split(/\s+/) + + const processName = parts[0] + + const pid = parts[1] + + logger.error( + `Address ${chalk.blue(address)}:${chalk.blue(port)} is in use by process: ${chalk.blue(processName)} (PID: ${chalk.blue(pid)})` ) - - if (matchingLine) { - const parts = matchingLine.trim().split(/\s+/) - - const processInfo = parts.find(part => /^.+:\d+$/.test(part)) - - if (processInfo) { - const [processName, pid] = processInfo.split(':') - - logger.error( - `Address ${chalk.blue(address)}:${chalk.blue(port)} is in use by process: ${chalk.blue(processName)} (PID: ${chalk.blue(pid)})` - ) - } else { - logger.error( - `Address ${chalk.blue(address)}:${chalk.blue(port)} is in use, but could not parse process info.` - ) - } - } else { - logger.error( - `Address ${chalk.blue(address)}:${chalk.blue(port)} is in use, but no process info found.` - ) - } } else { - const ssOutput = executeCommand('ss', { exitOnError: false }, [ - '-tlnp', - 'src', - `${address}:${port}` - ]) - - const lines = ssOutput.trim().split('\n') - - if (lines.length > 1) { - const processLine = lines[1] - - const processMatch = processLine.match( - /users:\(\("([^"]+)",pid=(\d+),fd=\d+\)\)/ - ) - - if (processMatch) { - const processName = processMatch[1] - - const pid = processMatch[2] - - logger.error( - `Address ${chalk.blue(address)}:${chalk.blue(port)} is in use by process: ${chalk.blue(processName)} (PID: ${chalk.blue(pid)})` - ) - } else { - logger.error( - `Address ${chalk.blue(address)}:${chalk.blue(port)} is in use, but could not parse process info.` - ) - } - } else { - logger.error( - `Address ${chalk.blue(address)}:${chalk.blue(port)} is in use, but no process info found.` - ) - } + logger.error( + `Address ${chalk.blue(address)}:${chalk.blue(port)} is in use, but no process info found.` + ) } return true