Download electron/pythonBackend.ts from algorembrant/computer-auditor: direct link, hf CLI and curl.
- Browser
- Download file 3.44 kB
-
https://huggingface.co/algorembrant/computer-auditor/resolve/main/electron/pythonBackend.ts
- Command line
-
hf download hf://algorembrant/computer-auditor/electron/pythonBackend.ts
-
curl -L -o pythonBackend.ts https://huggingface.co/algorembrant/computer-auditor/resolve/main/electron/pythonBackend.ts
3.44 kB
| /** | |
| * Spawns the FastAPI Python backend and exposes HTTP helpers for the main process. | |
| * Python handles hot paths (filesystem sizes, processes, drives, system, network, env). | |
| */ | |
| import { spawn, type ChildProcess } from 'node:child_process' | |
| import * as net from 'node:net' | |
| import * as path from 'node:path' | |
| let child: ChildProcess | null = null | |
| let baseUrl = '' | |
| export function getPythonBaseUrl(): string { | |
| return baseUrl | |
| } | |
| function getFreePort(): Promise<number> { | |
| 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: number, timeoutMs: number): Promise<void> { | |
| 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 { | |
| /* retry */ | |
| } | |
| await new Promise((r) => setTimeout(r, 120)) | |
| } | |
| throw new Error(`Python backend did not respond at ${url}`) | |
| } | |
| export async function startPythonBackend(): Promise<void> { | |
| const port = await getFreePort() | |
| const backendDir = path.join(__dirname, '..', 'backend') | |
| const py = | |
| process.env.PYTHON_PATH ?? (process.platform === 'win32' ? 'python' : 'python3') | |
| child = 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: Buffer) => { | |
| stderrBuf += d.toString() | |
| if (stderrBuf.length > 8000) stderrBuf = stderrBuf.slice(-4000) | |
| }) | |
| await new Promise<void>((resolve, reject) => { | |
| const fail = (err: Error) => { | |
| try { | |
| child?.kill() | |
| } catch { | |
| /* ignore */ | |
| } | |
| 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}` | |
| } | |
| export function stopPythonBackend(): void { | |
| if (child) { | |
| try { | |
| child.kill() | |
| } catch { | |
| /* ignore */ | |
| } | |
| child = null | |
| } | |
| baseUrl = '' | |
| } | |
| export async function pyGet<T>(pathname: string): Promise<T> { | |
| const r = await fetch(`${baseUrl}${pathname}`) | |
| if (!r.ok) { | |
| const t = await r.text() | |
| throw new Error(t || r.statusText) | |
| } | |
| return r.json() as Promise<T> | |
| } | |
| export async function pyPost<T>(pathname: string, body: unknown): Promise<T> { | |
| 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() as Promise<T> | |
| } | |