()
| 386 | |
| 387 | // 6. Auto-install Python deps + spawn proxy |
| 388 | const bootstrap = async () => { |
| 389 | const pythonPath = cfg.pythonPath || process.env.UNCOMMON_ROUTE_PYTHON || null; |
| 390 | let python = pythonPath; |
| 391 | |
| 392 | if (!python || !isPythonPackageInstalled(python)) { |
| 393 | api.logger.info("Checking Python dependencies..."); |
| 394 | python = ensurePythonDeps(api.logger); |
| 395 | if (!python) { |
| 396 | api.logger.error("Cannot start — Python setup failed. See errors above."); |
| 397 | return; |
| 398 | } |
| 399 | } |
| 400 | |
| 401 | const args = ["-m", "uncommon_route.cli", "serve", "--port", String(port), "--upstream", upstream]; |
| 402 | pyProc = spawn(python, args, { |
| 403 | stdio: ["ignore", "pipe", "pipe"], |
| 404 | env: { ...process.env, PYTHONUNBUFFERED: "1" }, |
| 405 | }); |
| 406 | |
| 407 | pyProc.stdout?.on("data", (chunk) => { |
| 408 | const line = chunk.toString().trim(); |
| 409 | if (line) api.logger.info(`[proxy] ${line}`); |
| 410 | }); |
| 411 | pyProc.stderr?.on("data", (chunk) => { |
| 412 | const line = chunk.toString().trim(); |
| 413 | if (line) api.logger.warn(`[proxy] ${line}`); |
| 414 | }); |
| 415 | pyProc.on("exit", (code) => { |
| 416 | if (code !== null && code !== 0) api.logger.error(`Proxy exited with code ${code}`); |
| 417 | pyProc = null; |
| 418 | }); |
| 419 | |
| 420 | api.logger.info(`Starting proxy on port ${port}...`); |
| 421 | const healthy = await waitForHealth(port); |
| 422 | if (healthy) { |
| 423 | api.logger.info(`UncommonRoute ready at http://127.0.0.1:${port}`); |
| 424 | api.logger.info(`Default model: uncommon-route/auto`); |
| 425 | } else { |
| 426 | api.logger.warn("Proxy health check timed out — may need more time to start"); |
| 427 | } |
| 428 | }; |
| 429 | |
| 430 | bootstrap().catch((err) => { |
| 431 | api.logger.error(`Bootstrap failed: ${err.message}`); |
no test coverage detected