Files
Genarrative/apps/ai-game-creator-shell/scripts/start-dev-server.mjs
T
kdletters 5ea453d5d1 收口AGC单窗口启动
取消开发态自动打开 Agent 聊天窗口

统一客户端可见标题为陶泥儿

同步启动守卫、测试与技术文档
2026-08-17 14:32:13 +08:00

126 lines
3.0 KiB
JavaScript

import { spawn } from 'node:child_process';
import http from 'node:http';
import net from 'node:net';
import { fileURLToPath } from 'node:url';
import { resolveAgcDevEndpoint, withAgcDevEndpointEnv } from './dev-port.mjs';
const appRoot = fileURLToPath(new URL('..', import.meta.url));
const endpoint = await resolveAgcDevEndpoint();
const { host, port, url: devUrl } = endpoint;
function readExistingServer() {
return new Promise((resolve) => {
const request = http.get(
{
host,
port,
path: '/',
timeout: 1000,
},
(response) => {
let body = '';
response.setEncoding('utf8');
response.on('data', (chunk) => {
if (body.length < 4096) {
body += chunk;
}
});
response.on('end', () => {
resolve({
statusCode: response.statusCode ?? 0,
body,
});
});
},
);
request.on('timeout', () => {
request.destroy();
resolve(null);
});
request.on('error', () => resolve(null));
});
}
function isAiGameCreatorServer(response) {
return (
response &&
response.statusCode >= 200 &&
response.statusCode < 500 &&
response.body.includes('<title>陶泥儿</title>') &&
response.body.includes('/src/main.tsx')
);
}
function isPortListening() {
return new Promise((resolve) => {
const socket = net.connect({ host, port });
socket.once('connect', () => {
socket.destroy();
resolve(true);
});
socket.once('error', () => resolve(false));
socket.setTimeout(1000, () => {
socket.destroy();
resolve(true);
});
});
}
const existing = await readExistingServer();
if (existing) {
if (isAiGameCreatorServer(existing)) {
console.error(
`[ai-game-creator-shell] ${devUrl} is already running but cannot be safely reused. Stop that process before starting the dev server.`,
);
process.exit(1);
}
console.error(
`[ai-game-creator-shell] ${devUrl} is already in use by another server. Stop that process before starting Tauri dev.`,
);
process.exit(1);
}
if (await isPortListening()) {
console.error(
`[ai-game-creator-shell] ${devUrl} is already in use by a non-HTTP or unrecognized server. Stop that process before starting Tauri dev.`,
);
process.exit(1);
}
const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
const child = spawn(
npm,
[
'--prefix',
'../..',
'exec',
'vite',
'--',
'--config',
'vite.config.ts',
'--port',
String(endpoint.port),
],
{
cwd: appRoot,
env: withAgcDevEndpointEnv(endpoint),
stdio: 'inherit',
// Node 18.20+/20+/24 on Windows rejects spawning .cmd (npm.cmd) without a shell (EINVAL).
shell: true,
},
);
for (const signal of ['SIGINT', 'SIGTERM']) {
process.on(signal, () => {
child.kill(signal);
});
}
child.on('exit', (code, signal) => {
if (signal) {
process.exit(1);
}
process.exit(code ?? 0);
});