import { spawn } from 'node:child_process'; import fs from 'node:fs'; import http from 'node:http'; import path from 'node:path'; const vendorDir = path.resolve('vendor/funclip'); const entryFile = path.join(vendorDir, 'funclip', 'launch.py'); const projectVenvPython = path.resolve('.venv-funclip/bin/python'); const pythonCommand = process.env.TRUEGROWTH_FUNCLIP_PYTHON || (fs.existsSync(projectVenvPython) ? projectVenvPython : 'python3'); const port = process.env.TRUEGROWTH_FUNCLIP_PORT || '7860'; const lang = process.env.TRUEGROWTH_FUNCLIP_LANG || 'zh'; const serviceUrl = `http://127.0.0.1:${port}`; function probeFunClip() { return new Promise((resolve) => { const request = http.get(serviceUrl, (response) => { response.resume(); resolve(response.statusCode ? response.statusCode < 500 : true); }); request.setTimeout(1600, () => { request.destroy(); resolve(false); }); request.on('error', () => resolve(false)); }); } function waitUntilStopped() { return new Promise((resolve) => { const timer = setInterval(() => undefined, 60_000); const stop = () => { clearInterval(timer); resolve(); }; process.once('SIGINT', stop); process.once('SIGTERM', stop); }); } if (!fs.existsSync(entryFile)) { console.error(`FunClip launch.py not found: ${entryFile}`); process.exit(1); } if (await probeFunClip()) { console.log( `FunClip already running at ${serviceUrl}; reusing existing service.` ); await waitUntilStopped(); } const child = spawn( pythonCommand, ['funclip/launch.py', '--port', port, '--lang', lang], { cwd: vendorDir, stdio: 'inherit', } ); const shutdown = () => { if (!child.killed) { child.kill('SIGTERM'); } }; process.on('SIGINT', shutdown); process.on('SIGTERM', shutdown); child.on('exit', (code) => process.exit(code ?? 0));