| "use strict"; |
| var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { |
| if (k2 === undefined) k2 = k; |
| var desc = Object.getOwnPropertyDescriptor(m, k); |
| if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { |
| desc = { enumerable: true, get: function() { return m[k]; } }; |
| } |
| Object.defineProperty(o, k2, desc); |
| }) : (function(o, m, k, k2) { |
| if (k2 === undefined) k2 = k; |
| o[k2] = m[k]; |
| })); |
| var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { |
| Object.defineProperty(o, "default", { enumerable: true, value: v }); |
| }) : function(o, v) { |
| o["default"] = v; |
| }); |
| var __importStar = (this && this.__importStar) || (function () { |
| var ownKeys = function(o) { |
| ownKeys = Object.getOwnPropertyNames || function (o) { |
| var ar = []; |
| for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; |
| return ar; |
| }; |
| return ownKeys(o); |
| }; |
| return function (mod) { |
| if (mod && mod.__esModule) return mod; |
| var result = {}; |
| if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); |
| __setModuleDefault(result, mod); |
| return result; |
| }; |
| })(); |
| Object.defineProperty(exports, "__esModule", { value: true }); |
| exports.getPythonBaseUrl = getPythonBaseUrl; |
| exports.startPythonBackend = startPythonBackend; |
| exports.stopPythonBackend = stopPythonBackend; |
| exports.pyGet = pyGet; |
| exports.pyPost = pyPost; |
| |
| |
| |
| |
| const node_child_process_1 = require("node:child_process"); |
| const net = __importStar(require("node:net")); |
| const path = __importStar(require("node:path")); |
| let child = null; |
| let baseUrl = ''; |
| function getPythonBaseUrl() { |
| return baseUrl; |
| } |
| function getFreePort() { |
| return new Promise((resolve, reject) => { |
| const s = net.createServer(); |
| s.listen(0, '127.0.0.1', () => { |
| const a = s.address(); |
| const p = typeof a === 'object' && a ? a.port : 0; |
| s.close(() => resolve(p)); |
| }); |
| s.on('error', reject); |
| }); |
| } |
| async function waitForHealth(port, timeoutMs) { |
| const url = `http://127.0.0.1:${port}/health`; |
| const t0 = Date.now(); |
| while (Date.now() - t0 < timeoutMs) { |
| try { |
| const r = await fetch(url); |
| if (r.ok) |
| return; |
| } |
| catch { |
| |
| } |
| await new Promise((r) => setTimeout(r, 120)); |
| } |
| throw new Error(`Python backend did not respond at ${url}`); |
| } |
| async function startPythonBackend() { |
| const port = await getFreePort(); |
| const backendDir = path.join(__dirname, '..', 'backend'); |
| const py = process.env.PYTHON_PATH ?? (process.platform === 'win32' ? 'python' : 'python3'); |
| child = (0, node_child_process_1.spawn)(py, ['-m', 'uvicorn', 'main:app', '--host', '127.0.0.1', '--port', String(port), '--log-level', 'warning'], { |
| cwd: backendDir, |
| env: { ...process.env, AUDITOR_PY_PORT: String(port) }, |
| stdio: ['ignore', 'pipe', 'pipe'], |
| windowsHide: true, |
| }); |
| let stderrBuf = ''; |
| child.stderr?.on('data', (d) => { |
| stderrBuf += d.toString(); |
| if (stderrBuf.length > 8000) |
| stderrBuf = stderrBuf.slice(-4000); |
| }); |
| await new Promise((resolve, reject) => { |
| const fail = (err) => { |
| try { |
| child?.kill(); |
| } |
| catch { |
| |
| } |
| reject(err); |
| }; |
| const t = setTimeout(() => { |
| fail(new Error(`Python backend startup timed out. Install: cd backend && pip install -r requirements.txt\n${stderrBuf}`)); |
| }, 25_000); |
| child.once('error', (err) => { |
| clearTimeout(t); |
| fail(err instanceof Error ? err : new Error(String(err))); |
| }); |
| waitForHealth(port, 24_000) |
| .then(() => { |
| clearTimeout(t); |
| resolve(); |
| }) |
| .catch((e) => { |
| clearTimeout(t); |
| fail(e instanceof Error ? e : new Error(String(e))); |
| }); |
| }); |
| baseUrl = `http://127.0.0.1:${port}`; |
| } |
| function stopPythonBackend() { |
| if (child) { |
| try { |
| child.kill(); |
| } |
| catch { |
| |
| } |
| child = null; |
| } |
| baseUrl = ''; |
| } |
| async function pyGet(pathname) { |
| const r = await fetch(`${baseUrl}${pathname}`); |
| if (!r.ok) { |
| const t = await r.text(); |
| throw new Error(t || r.statusText); |
| } |
| return r.json(); |
| } |
| async function pyPost(pathname, body) { |
| const r = await fetch(`${baseUrl}${pathname}`, { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify(body), |
| }); |
| if (!r.ok) { |
| const t = await r.text(); |
| throw new Error(t || r.statusText); |
| } |
| return r.json(); |
| } |
|
|