补充 dev:all 启动摘要表
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled

在 AGC Vite marker 就绪后打印全部服务的实际地址表。

补充 AGC 就绪等待、摘要构造测试和开发运维文档。
This commit is contained in:
2026-10-01 18:22:24 +08:00
parent 888ab83f39
commit e9b3e2b531
3 changed files with 125 additions and 3 deletions
+95 -1
View File
@@ -19,6 +19,7 @@ const rootReadyTimeoutMs = 660_000;
const rootReadyPollMs = 500;
const childGracefulTimeoutMs = 2_500;
const childForceTimeoutMs = 2_000;
const agcReadyPollMs = 500;
const rootOnlyOptions = new Set([
'--database',
@@ -139,6 +140,57 @@ async function areRootFrontendsReady(state) {
return results.every(Boolean);
}
async function readAgcFrontendMarker(endpoint, timeoutMs = 1_000) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetch(endpoint.markerUrl, {
signal: controller.signal,
});
if (!response.ok) {
return null;
}
const marker = await response.json();
return marker?.schemaVersion === 1 &&
marker?.app === 'ai-game-creator-shell'
? marker
: null;
} catch {
return null;
} finally {
clearTimeout(timeout);
}
}
async function waitForAgcFrontend(child, endpoint, options = {}) {
const timeoutMs = options.timeoutMs ?? rootReadyTimeoutMs;
const pollMs = options.pollMs ?? agcReadyPollMs;
const readMarker = options.readMarker ?? readAgcFrontendMarker;
const childExit = waitForChildExit(child);
const startedAt = Date.now();
while (Date.now() - startedAt < timeoutMs) {
if (await readMarker(endpoint)) {
return true;
}
const result = await Promise.race([
childExit,
wait(pollMs).then(() => null),
]);
if (result) {
const detail = result.error
? result.error.message
: result.signal
? `signal=${result.signal}`
: `code=${result.code ?? 1}`;
throw new Error(`AGC Vite 未就绪即退出: ${detail}`);
}
}
throw new Error(`等待 AGC Vite 就绪超时:${endpoint.url}`);
}
function isActiveService(service, expectedRepoRoot, expectedInstanceId) {
return Boolean(
service &&
@@ -201,6 +253,36 @@ function isSharedRootStackReady(
);
}
function formatServiceStatus(service) {
return service?.status === 'reused' ? '已复用' : '已启动';
}
function buildStartupSummary(state, agcEndpoint) {
const services = state?.services ?? {};
const rows = [
['SpacetimeDB', services.spacetime],
['BgFilter worker', services['bgfilter-worker']],
['API server', services['api-server']],
['Web 主站', services.web],
['Admin 后台', services['admin-web']],
].map(([name, service]) => ({
项目: name,
状态: formatServiceStatus(service),
地址: service?.url ?? '—',
}));
return [
...rows,
{ 项目: 'AGC Vite', 状态: '已启动', 地址: agcEndpoint.url },
{ 项目: 'Tauri AGC', 状态: '已启动', 地址: '本地窗口' },
];
}
function printStartupSummary(state, agcEndpoint, table = console.table) {
console.log('\n[dev:all] 全部开发服务已启动');
table(buildStartupSummary(state, agcEndpoint));
}
function waitForChildExit(child) {
if (!child) {
return Promise.resolve({ code: null, signal: null, error: null });
@@ -366,6 +448,7 @@ async function runDevAll(argv = process.argv.slice(2), dependencies = {}) {
const stopProcessTree = dependencies.stopProcessTree ?? terminateChildTree;
const readState = dependencies.readState ?? (() => readDevStackState());
const waitForRoot = dependencies.waitForRoot ?? waitForRootStack;
const waitForAgc = dependencies.waitForAgc ?? waitForAgcFrontend;
let rootChild = null;
let agcChild = null;
@@ -410,7 +493,7 @@ async function runDevAll(argv = process.argv.slice(2), dependencies = {}) {
);
rootChild = spawnProcess(npmCommand, rootArgs, env);
const rootExit = waitForChildExit(rootChild);
await waitForRoot(rootChild, {
const rootState = await waitForRoot(rootChild, {
readState,
expected: {
expectedRepoRoot: repoRoot,
@@ -430,6 +513,16 @@ async function runDevAll(argv = process.argv.slice(2), dependencies = {}) {
console.log('[dev:all] 根开发栈已就绪,启动 AGC Vite 与 Tauri');
agcChild = spawnProcess(npmCommand, ['run', 'agc'], env);
const agcExit = waitForChildExit(agcChild);
await waitForAgc(agcChild, endpoint);
if (rootChild.exitCode !== null || rootChild.signalCode !== null) {
throw new Error('根开发栈在 AGC 就绪前退出');
}
if (shuttingDown) {
await stopAll();
return signalExitCode || 1;
}
printStartupSummary(readState() ?? rootState, endpoint);
const result = await Promise.race([
rootExit.then((exit) => ({ owner: 'root', exit })),
agcExit.then((exit) => ({ owner: 'agc', exit })),
@@ -469,6 +562,7 @@ export {
buildDevAllEnvironment,
buildManagedSpawnOptions,
buildRootDevArgs,
buildStartupSummary,
isSharedRootStackReady,
readDevStackState,
runDevAll,