diff --git a/apps/cli/src/cli.ts b/apps/cli/src/cli.ts index f4ac2e85..ba435070 100644 --- a/apps/cli/src/cli.ts +++ b/apps/cli/src/cli.ts @@ -8,7 +8,7 @@ import * as fs from 'fs' import * as path from 'path' import { execSync } from 'child_process' import { Command } from 'commander' -import { loadConfig, getConfigPath } from '@openchatlab/config' +import { DEFAULT_API_PORT, loadConfig, getConfigPath } from '@openchatlab/config' import { NodePathProvider, DatabaseManager, @@ -276,7 +276,7 @@ program program .command('serve') .description('Start the HTTP API server') - .option('--port ', 'Server port', '3110') + .option('--port ', 'Server port', String(DEFAULT_API_PORT)) .option('--host ', 'Listen address', '127.0.0.1') .option('--token ', 'Custom Bearer Token (reads from config or auto-generates if omitted)') .option('--web [dir]', 'Serve Web frontend static assets (defaults to dist-web/)') @@ -335,7 +335,7 @@ program program .command('web') .description('Launch Web UI with one click (HTTP API + browser auto-open)') - .option('--port ', 'Server port', '3110') + .option('--port ', 'Server port', String(DEFAULT_API_PORT)) .option('--host ', 'Listen address', '127.0.0.1') .option('--no-open', 'Do not auto-open browser') .action(async (options) => { diff --git a/packages/config/src/index.ts b/packages/config/src/index.ts index 6f9ba4f5..bd13fa4b 100644 --- a/packages/config/src/index.ts +++ b/packages/config/src/index.ts @@ -5,7 +5,7 @@ */ export { loadConfig, getConfigPath, getConfigDir, writeConfigField } from './loader' -export { configSchema } from './schema' +export { configSchema, DEFAULT_API_PORT } from './schema' export type { ChatLabConfig, LlmConfig, DataConfig, ApiConfig, LocaleConfig, UiConfig } from './schema' export { loadAuthProfiles, diff --git a/packages/config/src/schema.ts b/packages/config/src/schema.ts index 722a4b0f..98dc6b8f 100644 --- a/packages/config/src/schema.ts +++ b/packages/config/src/schema.ts @@ -6,6 +6,8 @@ import { z } from 'zod' +export const DEFAULT_API_PORT = 3110 + export const llmConfigSchema = z.object({ provider: z.string().default(''), model: z.string().default(''), @@ -17,7 +19,7 @@ export const dataConfigSchema = z.object({ }) export const apiConfigSchema = z.object({ - port: z.number().int().min(1).max(65535).default(3210), + port: z.number().int().min(1).max(65535).default(DEFAULT_API_PORT), host: z.string().default('127.0.0.1'), token: z.string().default(''), }) diff --git a/scripts/dev-server-command.mjs b/scripts/dev-server-command.mjs new file mode 100644 index 00000000..2329a3c4 --- /dev/null +++ b/scripts/dev-server-command.mjs @@ -0,0 +1,11 @@ +export function createChatlabServeCommand({ serverDir, backendPort, nodeExecutable = process.execPath }) { + return { + command: nodeExecutable, + args: ['--watch', '--import', 'tsx', 'src/cli.ts', 'serve', '--port', String(backendPort)], + options: { + cwd: serverDir, + stdio: ['ignore', 'pipe', 'pipe'], + env: { ...process.env }, + }, + } +} diff --git a/scripts/dev-server-command.test.mjs b/scripts/dev-server-command.test.mjs new file mode 100644 index 00000000..4ac0a2d6 --- /dev/null +++ b/scripts/dev-server-command.test.mjs @@ -0,0 +1,18 @@ +import assert from 'node:assert/strict' +import test from 'node:test' + +import { createChatlabServeCommand } from './dev-server-command.mjs' + +test('web dev backend runs through the current Node executable with the tsx loader', () => { + const command = createChatlabServeCommand({ + rootDir: '/repo', + serverDir: '/repo/apps/cli', + coreDir: '/repo/packages/core/src', + runtimeDir: '/repo/packages/node-runtime/src', + backendPort: 3110, + nodeExecutable: '/custom/node', + }) + + assert.equal(command.command, '/custom/node') + assert.deepEqual(command.args, ['--watch', '--import', 'tsx', 'src/cli.ts', 'serve', '--port', '3110']) +}) diff --git a/vite.web.config.mts b/vite.web.config.mts index 197eff6b..e319c9d3 100644 --- a/vite.web.config.mts +++ b/vite.web.config.mts @@ -17,8 +17,10 @@ import * as net from 'net' import { defineConfig, type Plugin } from 'vite' import vue from '@vitejs/plugin-vue' import ui from '@nuxt/ui/vite' +import { DEFAULT_API_PORT } from './packages/config/src/schema' +import { createChatlabServeCommand } from './scripts/dev-server-command.mjs' -const BACKEND_PORT = 3110 +const BACKEND_PORT = DEFAULT_API_PORT function isPortInUse(port: number): Promise { return new Promise((resolve) => { @@ -51,28 +53,11 @@ function chatlabServePlugin(): Plugin { } const serverDir = resolve(__dirname, 'apps/cli') - const coreDir = resolve(__dirname, 'packages/core/src') - const runtimeDir = resolve(__dirname, 'packages/node-runtime/src') - serverProcess = spawn( - 'pnpm', - [ - 'tsx', - 'watch', - '--include', - `${coreDir}/**`, - '--include', - `${runtimeDir}/**`, - 'src/cli.ts', - 'serve', - '--port', - String(BACKEND_PORT), - ], - { - cwd: serverDir, - stdio: ['ignore', 'pipe', 'pipe'], - env: { ...process.env }, - } - ) + const serveCommand = createChatlabServeCommand({ + serverDir, + backendPort: BACKEND_PORT, + }) + serverProcess = spawn(serveCommand.command, serveCommand.args, serveCommand.options) serverProcess.stdout?.on('data', (data: Buffer) => { const line = data.toString().trim()