Files
Genarrative/apps/ai-game-creator-shell/scripts/start-dev-server.mjs
T
AIGameCreator App 72a6d635da AI 游戏创作智能体 App v1 最小落地
新增独立 Tauri 壳 apps/ai-game-creator-shell,只保留聊天入口,正式用户窗口不承载游戏预览画面,开发模式通过独立窗口展示任务、文件、记忆、预览和日志

新增游戏创作专业组与种子任务图契约,覆盖策划/美术/程序/数值/音乐/运营 6 组下 15 个组内角色

新增共享契约本地项目 manifest、内置命令权限枚举、run trace schema 和 ready-task 选择器

扩展平台 LLM 支持流式请求,增加 GENARRATIVE_GAME_CREATOR_LLM_STREAM 开关

新增 AI 游戏创作 App 的聊天命令集:/help /capabilities /audit /project /llm-status /status /files /assets /read /tasks /trace /smoke /run /preview /preview-status /preview-open /preview-stop /memory /remember /forget-memory /canvas /sync-canvas-project /import-canvas-asset /import-canvas-export

新增本地项目初始化、权限 gate(pending/confirm/cancel)、文件上传、受限命令白名单 game.static_smoke 和本地 HTTP 预览

新增 Planner / 6 组角色 agent / Generator / Evaluator / ArtifactWriter / Playtest 文件驱动 loop,最多 3 轮返工,结构化 repairRoutes

新增实时 run trace 写入 .agent/run.latest.json 与 .agent/runs/,包含 taskGraph、passPlans 和步级 toolCalls

新增短期/长期记忆 memory/session.md 和 memory/project.md,支持聊天读取/追加/覆盖/删除

新增画板对接:canvas.project_open、canvas.project_sync、canvas.asset_import、canvas.export_import

新增 npm run ai-game-creator-shell:check 开发验收入口,覆盖 typecheck、单元测试、端到端 smoke

新增 check:native-shells 静态守门:release 只登记 main 聊天窗口,CSP 禁止内嵌预览,开发窗口只在 debug 下打开,用户侧预览命令必须调用 open_local_game_preview
2026-06-25 21:03:46 +08:00

110 lines
2.6 KiB
JavaScript

import { spawn } from 'node:child_process';
import http from 'node:http';
import net from 'node:net';
import { fileURLToPath } from 'node:url';
const appRoot = fileURLToPath(new URL('..', import.meta.url));
const host = '127.0.0.1';
const port = 3080;
const devUrl = `http://${host}:${port}/`;
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>AI 游戏创作</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.log(`[ai-game-creator-shell] reuse existing Vite dev server ${devUrl}`);
process.exit(0);
}
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'],
{
cwd: appRoot,
stdio: 'inherit',
},
);
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);
});