53e37ea361
新增App Run独立开发profile并隔离端口、数据库、Tauri身份与AppData 常驻资源排列切换并避免布局状态挤压操作入口 显式扩展资源画布横向滚动范围并收敛依赖图可见连线 补充启动器、资源布局、界面回归测试与项目文档
151 lines
3.8 KiB
JavaScript
151 lines
3.8 KiB
JavaScript
import { resolve } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
import {
|
|
preflightExistingVite,
|
|
resolveDevStackProfile,
|
|
spawnChild,
|
|
stopChild,
|
|
terminateChildTree,
|
|
waitForChildTermination,
|
|
} from './start-dev-stack.mjs';
|
|
|
|
const appRoot = fileURLToPath(new URL('..', import.meta.url));
|
|
const repoRoot = resolve(appRoot, '../..');
|
|
const tauriCliPath = resolve(repoRoot, 'node_modules/@tauri-apps/cli/tauri.js');
|
|
|
|
function parseLauncherArguments(argv) {
|
|
const args = [...argv];
|
|
const appRun = args[0] === '--app-run';
|
|
if (appRun) {
|
|
args.shift();
|
|
}
|
|
const gameChat = args[0] === '--game-chat';
|
|
if (gameChat) {
|
|
args.shift();
|
|
}
|
|
if (appRun && gameChat) {
|
|
throw new Error('app-run profile 不能与 game-chat 入口同时使用');
|
|
}
|
|
return { appRun, gameChat, args };
|
|
}
|
|
|
|
function buildTauriArguments(argv) {
|
|
const { appRun, gameChat, args } = parseLauncherArguments(argv);
|
|
if (appRun) {
|
|
return [
|
|
'dev',
|
|
'--config',
|
|
'src-tauri/tauri.app-run-dev.conf.json',
|
|
...args,
|
|
];
|
|
}
|
|
if (gameChat) {
|
|
return ['dev', '--', '--', '--game-chat', ...args];
|
|
}
|
|
return ['dev', ...args];
|
|
}
|
|
|
|
function spawnTauriCli(argv, { profileName = 'default' } = {}) {
|
|
return spawnChild(process.execPath, [tauriCliPath, ...argv], {
|
|
cwd: appRoot,
|
|
env: {
|
|
...process.env,
|
|
GENARRATIVE_AGC_DEV_PROFILE: profileName,
|
|
},
|
|
shell: false,
|
|
});
|
|
}
|
|
|
|
async function runTauriDev(
|
|
argv = process.argv.slice(2),
|
|
{
|
|
preflight = preflightExistingVite,
|
|
spawnCli = spawnTauriCli,
|
|
waitForCli = waitForChildTermination,
|
|
terminateTree = terminateChildTree,
|
|
} = {},
|
|
) {
|
|
const { appRun } = parseLauncherArguments(argv);
|
|
const profile = resolveDevStackProfile(appRun ? 'app-run' : 'default');
|
|
await preflight({ profile });
|
|
|
|
const tauriArguments = buildTauriArguments(argv);
|
|
const child = spawnCli(tauriArguments, { profileName: profile.name });
|
|
let resolveShutdown;
|
|
let shutdownSignal = '';
|
|
let repeatedSignal = false;
|
|
const shutdownRequested = new Promise((resolveRequest) => {
|
|
resolveShutdown = resolveRequest;
|
|
});
|
|
const signalHandlers = new Map();
|
|
|
|
for (const signal of ['SIGINT', 'SIGTERM']) {
|
|
const handler = () => {
|
|
if (!shutdownSignal) {
|
|
shutdownSignal = signal;
|
|
stopChild(child, 'SIGTERM');
|
|
resolveShutdown(signal);
|
|
return;
|
|
}
|
|
repeatedSignal = true;
|
|
stopChild(child, 'SIGKILL');
|
|
};
|
|
signalHandlers.set(signal, handler);
|
|
process.on(signal, handler);
|
|
}
|
|
|
|
try {
|
|
const childResult = waitForCli(child);
|
|
const outcome = await Promise.race([
|
|
childResult.then((failure) => ({ type: 'exit', failure })),
|
|
shutdownRequested.then((signal) => ({ type: 'signal', signal })),
|
|
]);
|
|
const cleanup = await terminateTree(child, {
|
|
gracefulTimeoutMs: repeatedSignal ? 0 : 2500,
|
|
});
|
|
if (!cleanup.stopped) {
|
|
console.error(
|
|
'[ai-game-creator-shell] Tauri dev exited, but its process tree could not be fully stopped.',
|
|
);
|
|
return 1;
|
|
}
|
|
|
|
if (outcome.type === 'signal') {
|
|
return 1;
|
|
}
|
|
const { failure } = outcome;
|
|
return failure.type === 'error' || failure.signal ? 1 : (failure.code ?? 0);
|
|
} finally {
|
|
for (const [signal, handler] of signalHandlers) {
|
|
process.off(signal, handler);
|
|
}
|
|
}
|
|
}
|
|
|
|
function isDirectModuleExecution() {
|
|
return Boolean(
|
|
process.argv[1] &&
|
|
resolve(process.argv[1]) === fileURLToPath(import.meta.url),
|
|
);
|
|
}
|
|
|
|
export {
|
|
buildTauriArguments,
|
|
isDirectModuleExecution,
|
|
parseLauncherArguments,
|
|
runTauriDev,
|
|
spawnTauriCli,
|
|
};
|
|
|
|
if (isDirectModuleExecution()) {
|
|
try {
|
|
process.exitCode = await runTauriDev();
|
|
} catch (error) {
|
|
console.error(
|
|
`[ai-game-creator-shell] ${error instanceof Error ? error.message : String(error)}`,
|
|
);
|
|
process.exitCode = 1;
|
|
}
|
|
}
|