From 051b69867856f276f1094ddb4913b07e683e72ad Mon Sep 17 00:00:00 2001 From: lukashow Date: Tue, 3 Feb 2026 19:48:16 +0800 Subject: [PATCH 1/5] fix(cli): include cwd logging in single service execution --- tools/src/commands/dev/functions/startServices.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/src/commands/dev/functions/startServices.ts b/tools/src/commands/dev/functions/startServices.ts index a2cf0ca82..77208b14b 100644 --- a/tools/src/commands/dev/functions/startServices.ts +++ b/tools/src/commands/dev/functions/startServices.ts @@ -1,3 +1,4 @@ +import chalk from 'chalk' import concurrently from 'concurrently' import { PROJECTS } from '@/commands/project/constants/projects' @@ -30,7 +31,9 @@ export async function startSingleService( const cwd = config.cwd instanceof Function ? config.cwd() : config.cwd - executeCommand(command, { cwd }, extraArgs) + logger.debug(`Current Working Directory: ${chalk.blue(cwd)}`) + + executeCommand(command, { cwd, stdio: 'inherit' }, extraArgs) return } From c784a00433d756bde979d3db653e13a6ce7285b7 Mon Sep 17 00:00:00 2001 From: lukashow Date: Tue, 3 Feb 2026 19:48:53 +0800 Subject: [PATCH 2/5] fix(cli): Remove extra logger conditional statement --- tools/src/utils/commands.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/tools/src/utils/commands.ts b/tools/src/utils/commands.ts index af119ea77..d248796c6 100644 --- a/tools/src/utils/commands.ts +++ b/tools/src/utils/commands.ts @@ -50,14 +50,12 @@ export default function executeCommand( stdio: options.stdio ?? 'pipe' }) - if (logger.level === 'debug') { - if (result.stdout) { - logger.debug(result.stdout.toString()) - } + if (result.stdout) { + logger.debug(result.stdout.toString()) + } - if (result.stderr) { - logger.debug(result.stderr.toString()) - } + if (result.stderr) { + logger.debug(result.stderr.toString()) } if (result.error) { From b2c58447a1ed4fe0f79aadcff2e60f09a80dfdd6 Mon Sep 17 00:00:00 2001 From: lukashow Date: Tue, 3 Feb 2026 20:11:54 +0800 Subject: [PATCH 3/5] fix(db): Implement Pocketbsse http kwargs for accurate database startup --- tools/src/constants/db.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/src/constants/db.ts b/tools/src/constants/db.ts index b5c22dcb4..d17937280 100644 --- a/tools/src/constants/db.ts +++ b/tools/src/constants/db.ts @@ -26,10 +26,14 @@ export const PB_BINARY_PATH = path.resolve( process.env.PB_BINARY_PATH || `${PB_DIR}/pocketbase` ) +// Remove http:// prefix if present +export const PB_HOST = getEnvVar('PB_HOST').replace(/^http:\/\//, '') + export const PB_KWARGS = [ `--dir=${PB_DATA_DIR}`, `--migrationsDir=${PB_MIGRATIONS_DIR}`, - '--automigrate=0' + '--automigrate=0', + `--http ${PB_HOST || 'localhost:8090'}` ] // Straightaway exit if PB_DIR is not accessible (skip in Docker mode) From f178141dc8d7a5872a50c5611cd9983f5dfc9d76 Mon Sep 17 00:00:00 2001 From: lukashow Date: Tue, 3 Feb 2026 20:47:27 +0800 Subject: [PATCH 4/5] fix(cli): enhance database boot with address check and logging --- tools/src/commands/dev/config/commands.ts | 22 ++++++++-------------- tools/src/constants/db.ts | 7 +++++-- tools/src/utils/helpers.ts | 22 ++++++++++++++++++++++ 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/tools/src/commands/dev/config/commands.ts b/tools/src/commands/dev/config/commands.ts index fa98d475d..5da04102f 100644 --- a/tools/src/commands/dev/config/commands.ts +++ b/tools/src/commands/dev/config/commands.ts @@ -1,8 +1,8 @@ import fs from 'fs' -import { PB_BINARY_PATH, PB_DIR, PB_KWARGS } from '@/constants/db' +import { PB_BINARY_PATH, PB_DIR, PB_KWARGS, PB_HOST, PB_PORT } from '@/constants/db' import executeCommand from '@/utils/commands' -import { checkPortInUse, delay, killExistingProcess } from '@/utils/helpers' +import { checkAddressInUse, checkPortInUse, delay, killExistingProcess } from '@/utils/helpers' import logger from '@/utils/logger' /** @@ -17,18 +17,12 @@ interface ServiceConfig { export const SERVICE_COMMANDS: Record = { db: { command: async () => { - const killedProcess = killExistingProcess('./pocketbase serve') - - if (killedProcess) { - await delay(2000) - } - - if (checkPortInUse(8090)) { - logger.error( - 'No Pocketbase instance found running, but port 8090 is already in use.' - ) - process.exit(1) - } + if (checkAddressInUse(PB_HOST, PB_PORT)) { + logger.error( + `Database address ${PB_HOST}:${PB_PORT} is already in use.` + ) + process.exit(1) + } if (!fs.existsSync(PB_BINARY_PATH)) { logger.error( diff --git a/tools/src/constants/db.ts b/tools/src/constants/db.ts index d17937280..6eaf190d1 100644 --- a/tools/src/constants/db.ts +++ b/tools/src/constants/db.ts @@ -27,13 +27,16 @@ export const PB_BINARY_PATH = path.resolve( ) // Remove http:// prefix if present -export const PB_HOST = getEnvVar('PB_HOST').replace(/^http:\/\//, '') +export const PB_URL = getEnvVar('PB_HOST').replace(/^http:\/\//, '') + +// Extract host from PB_HOST +export const [PB_HOST, PB_PORT] = PB_URL.split(':') export const PB_KWARGS = [ `--dir=${PB_DATA_DIR}`, `--migrationsDir=${PB_MIGRATIONS_DIR}`, '--automigrate=0', - `--http ${PB_HOST || 'localhost:8090'}` + `--http ${PB_URL || 'localhost:8090'}` ] // Straightaway exit if PB_DIR is not accessible (skip in Docker mode) diff --git a/tools/src/utils/helpers.ts b/tools/src/utils/helpers.ts index a2e852aa9..a7d52bebe 100644 --- a/tools/src/utils/helpers.ts +++ b/tools/src/utils/helpers.ts @@ -122,6 +122,28 @@ export function checkPortInUse(port: number): boolean { } } +/** + * Checks if a specific port is currently in use. + * + * @param port - The port number to check + * @returns True if the port is in use, false otherwise + */ +export function checkAddressInUse(address: string, port: string): boolean { + logger.debug(`Checking if address ${address}:${port} is in use...`); + + try { + executeCommand('nc', { exitOnError: false }, [ + '-zv', + address, + port, + ]); + + return true; + } catch { + return false; + } +} + /** * Creates a promise that resolves after the specified delay. * From b7fe04dd613f2e54afa563bad376c2acccef034e Mon Sep 17 00:00:00 2001 From: lukashow Date: Wed, 4 Feb 2026 17:54:00 +0800 Subject: [PATCH 5/5] fix(cli): improve logging with chalk for better visibility --- tools/src/commands/dev/config/commands.ts | 5 +- tools/src/utils/commands.ts | 22 ++++---- tools/src/utils/helpers.ts | 63 ++++++++++++++++++----- 3 files changed, 63 insertions(+), 27 deletions(-) diff --git a/tools/src/commands/dev/config/commands.ts b/tools/src/commands/dev/config/commands.ts index 5da04102f..5154bf729 100644 --- a/tools/src/commands/dev/config/commands.ts +++ b/tools/src/commands/dev/config/commands.ts @@ -4,6 +4,7 @@ import { PB_BINARY_PATH, PB_DIR, PB_KWARGS, PB_HOST, PB_PORT } from '@/constants import executeCommand from '@/utils/commands' import { checkAddressInUse, checkPortInUse, delay, killExistingProcess } from '@/utils/helpers' import logger from '@/utils/logger' +import chalk from 'chalk' /** * Service command configurations @@ -19,14 +20,14 @@ export const SERVICE_COMMANDS: Record = { command: async () => { if (checkAddressInUse(PB_HOST, PB_PORT)) { logger.error( - `Database address ${PB_HOST}:${PB_PORT} is already in use.` + `Database address ${chalk.blue(`${PB_HOST}:${PB_PORT}`)} is already in use.` ) process.exit(1) } if (!fs.existsSync(PB_BINARY_PATH)) { logger.error( - `PocketBase binary does not exist: ${PB_BINARY_PATH}. Please run "bun forge db init" to initialize the database.` + `PocketBase binary does not exist: ${chalk.blue(PB_BINARY_PATH)}. Please run "bun forge db init" to initialize the database.` ) process.exit(1) } diff --git a/tools/src/utils/commands.ts b/tools/src/utils/commands.ts index d248796c6..b9874d19f 100644 --- a/tools/src/utils/commands.ts +++ b/tools/src/utils/commands.ts @@ -6,6 +6,7 @@ import { ROOT_DIR } from '@/constants/constants' import logger from './logger' import { addDependency, removeDependency } from './packageJson' +import chalk from 'chalk' interface CommandExecutionOptions { stdio?: IOType | [IOType, IOType, IOType] @@ -34,12 +35,12 @@ export default function executeCommand( cmd = typeof command === 'function' ? command() : command } catch (error) { logger.error(`Failed to generate command.`) - logger.debug(`Error details: ${error}`) + logger.debug(`Error details: ${chalk.grey(String(error))}`) process.exit(1) } try { - logger.debug(`Executing: ${cmd}`) + logger.debug(`Executing command ${chalk.blue(cmd)} with arguments: ${chalk.blue(_arguments.length ? _arguments.join(' ') : `${chalk.red('none')}`)}`) const [toBeExecuted, ...args] = cmd.split(' ') @@ -51,11 +52,11 @@ export default function executeCommand( }) if (result.stdout) { - logger.debug(result.stdout.toString()) + logger.debug(chalk.grey(result.stdout.toString())) } if (result.stderr) { - logger.debug(result.stderr.toString()) + logger.debug(chalk.grey(result.stderr.toString())) } if (result.error) { @@ -67,7 +68,7 @@ export default function executeCommand( } if (!options.stdio || options.stdio === 'pipe') { - logger.debug(`Completed: ${cmd}`) + logger.debug(`Command Completed: ${chalk.blue(cmd)}, exit code: ${chalk.blue(String(result.status))}`) } return result.stdout?.toString().trim() || '' @@ -76,8 +77,8 @@ export default function executeCommand( throw error } - logger.error(`Command execution failed: ${cmd}`) - logger.debug(`Error details: ${error}`) + logger.error(`Command execution failed: ${chalk.blue(cmd)}`) + logger.debug(`Error details: ${chalk.grey(String(error))}`) process.exit(1) } } @@ -111,7 +112,7 @@ export function installPackage( fs.rmSync(targetDir, { recursive: true, force: true }) } - logger.debug(`Installing ${fullName} from registry...`) + logger.debug(`Installing ${chalk.blue(fullName)} from registry...`) executeCommand(`bun add ${fullName}@latest --ignore-scripts`, { cwd: ROOT_DIR @@ -120,12 +121,11 @@ export function installPackage( const installedPath = path.join(ROOT_DIR, 'node_modules', fullName) if (!fs.existsSync(installedPath)) { - logger.error(`Failed to find installed package at ${installedPath}`) + logger.error(`Failed to find installed package at ${chalk.blue(installedPath)}`) process.exit(1) } - logger.debug(`Copying ${fullName} to ${targetDir}...`) - + logger.debug(`Copying ${chalk.blue(fullName)} to ${chalk.blue(targetDir)}...`) fs.cpSync(installedPath, targetDir, { recursive: true, dereference: true }) // Add to target package.json (apps or locales) diff --git a/tools/src/utils/helpers.ts b/tools/src/utils/helpers.ts index a7d52bebe..1404ada1f 100644 --- a/tools/src/utils/helpers.ts +++ b/tools/src/utils/helpers.ts @@ -1,3 +1,4 @@ +import chalk from 'chalk' import prompts from 'prompts' import executeCommand from './commands' @@ -60,7 +61,7 @@ export function getEnvVar(varName: string, fallback?: string): string { return fallback } - logger.error(`Missing required environment variable: ${varName}`) + logger.error(`Missing required environment variable: ${chalk.red(varName)}`) process.exit(1) } @@ -77,7 +78,9 @@ export function killExistingProcess( if (typeof processKeywordOrPID === 'number') { process.kill(processKeywordOrPID) - logger.debug(`Killed process with PID: ${String(processKeywordOrPID)}`) + logger.debug( + `Killed process with PID: ${chalk.blue(String(processKeywordOrPID))}` + ) return } @@ -92,7 +95,7 @@ export function killExistingProcess( }) logger.debug( - `Killed process matching keyword: ${processKeywordOrPID} (PID: ${serverInstance})` + `Killed process matching keyword: ${chalk.blue(processKeywordOrPID)} (PID: ${chalk.blue(serverInstance)})` ) return parseInt(serverInstance, 10) @@ -129,19 +132,51 @@ export function checkPortInUse(port: number): boolean { * @returns True if the port is in use, false otherwise */ export function checkAddressInUse(address: string, port: string): boolean { - logger.debug(`Checking if address ${address}:${port} is in use...`); + logger.debug( + `Checking if address ${chalk.blue(address)}:${chalk.blue(port)} is in use...` + ) - try { - executeCommand('nc', { exitOnError: false }, [ - '-zv', - address, - port, - ]); + try { + executeCommand('nc', { exitOnError: false }, ['-zv', address, port]) - return true; - } catch { - return false; - } + 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.` + ) + } + + return true + } catch { + return false + } } /**