| #!/usr/bin/env node |
| |
| |
| |
|
|
| import { spawn } from 'node:child_process'; |
| import net from 'node:net'; |
|
|
| const DEFAULT_API_PORT = 5174; |
| const DEFAULT_WEB_PORT = 5173; |
| const MAX_PROBE = 50; |
|
|
| async function isFree(port) { |
| return new Promise((resolve) => { |
| const srv = net.createServer(); |
| srv.unref(); |
| srv.once('error', () => { |
| resolve(false); |
| }); |
| srv.once('listening', () => { |
| srv.close(() => { |
| resolve(true); |
| }); |
| }); |
| srv.listen({ port, host: '127.0.0.1', exclusive: true }); |
| }); |
| } |
|
|
| async function pickPort(startPort, exclude = new Set()) { |
| for (let port = startPort; port < startPort + MAX_PROBE; port += 1) { |
| if (exclude.has(port)) continue; |
| |
| if (await isFree(port)) return port; |
| } |
| throw new Error( |
| `no free port in [${startPort}, ${startPort + MAX_PROBE}); something is hogging the range`, |
| ); |
| } |
|
|
| const requestedApi = Number(process.env.PORT) || DEFAULT_API_PORT; |
| const apiPort = await pickPort(requestedApi); |
| if (apiPort !== requestedApi) { |
| process.stdout.write(`[vis] api port ${requestedApi} busy, using ${apiPort} instead\n`); |
| } |
|
|
| const requestedWeb = Number(process.env.WEB_PORT) || DEFAULT_WEB_PORT; |
| const webPort = await pickPort(requestedWeb, new Set([apiPort])); |
| if (webPort !== requestedWeb) { |
| process.stdout.write(`[vis] web port ${requestedWeb} busy, using ${webPort} instead\n`); |
| } |
|
|
| process.stdout.write(`[vis] web → http://localhost:${webPort} (api on ${apiPort})\n`); |
|
|
| const env = { ...process.env, PORT: String(apiPort), WEB_PORT: String(webPort) }; |
| const child = spawn( |
| 'concurrently', |
| [ |
| '-k', |
| '-n', 'server,web', |
| '-c', 'cyan,magenta', |
| 'pnpm --filter @moonshot-ai/vis-server dev', |
| 'pnpm --filter @moonshot-ai/vis-web dev', |
| ], |
| { stdio: 'inherit', env, shell: false }, |
| ); |
|
|
| child.on('exit', (code, signal) => { |
| if (signal !== null) process.kill(process.pid, signal); |
| else process.exit(code ?? 0); |
| }); |
|
|