Release Healer Man 0.1.47

This commit is contained in:
phenom committed 2026-09-16 20:00:13 -04:00
1 parent 9ff574ac0b
commit 2162039b49
24 files changed
+486 -55

No files matched your search

+50 -2
View File
@@ -1,5 +1,5 @@
import { randomUUID } from "node:crypto";
import { spawn } from "node:child_process";
import { spawn, spawnSync } from "node:child_process";
import {
createReadStream,
existsSync,
@@ -101,6 +101,36 @@ export function balanceLabCommand(spec, outputDir, options = {}) {
return { executable, args, cwd: projectRoot };
}
export function inspectBalanceLabRuntime(executable, run = spawnSync) {
const command = String(executable || (process.platform === "win32" ? "python" : "python3"));
try {
const result = run(command, ["--version"], {
encoding: "utf8",
timeout: 5_000,
windowsHide: true,
});
const version = `${result?.stdout ?? ""} ${result?.stderr ?? ""}`.trim();
if (result?.status === 0 && /^Python 3\./i.test(version)) {
return { available: true, executable: command, version, error: null };
}
return {
available: false,
executable: command,
version: version || null,
error: version && /^Python 2\./i.test(version)
? "Balance Lab requires Python 3; this server resolved Python 2."
: `The server could not execute ${command} as Python 3.`,
};
} catch (error) {
return {
available: false,
executable: command,
version: null,
error: `The server could not execute ${command}: ${error?.message ?? error}`,
};
}
}
function publicJob(job) {
return {
id: job.id,
@@ -166,6 +196,13 @@ export function createBalanceLabJobService(options = {}) {
? Math.min(50, Math.floor(configuredQueueLimit))
: 8;
const launch = options.spawnProcess ?? spawn;
const pythonExecutable = String(options.pythonExecutable
?? process.env.BALANCE_LAB_PYTHON
?? (process.platform === "win32" ? "python" : "python3"));
const runtimeProbe = options.runtimeProbe
?? (options.spawnProcess
? () => ({ available: true, executable: pythonExecutable, version: "test process", error: null })
: () => inspectBalanceLabRuntime(pythonExecutable));
const jobs = new Map();
const queue = [];
const children = new Map();
@@ -288,7 +325,7 @@ export function createBalanceLabJobService(options = {}) {
persistJob(job);
const command = balanceLabCommand(job.spec, job.outputDir, {
projectRoot,
pythonExecutable: options.pythonExecutable,
pythonExecutable,
});
const child = launch(command.executable, command.args, {
cwd: command.cwd,
@@ -344,6 +381,14 @@ export function createBalanceLabJobService(options = {}) {
return {
start(input, account) {
if (closed) throw new GameDatabaseError("Balance Lab runner is shutting down.", 503, "balance_lab_unavailable");
const runtime = runtimeProbe();
if (!runtime.available) {
throw new GameDatabaseError(
`${runtime.error} Install Python 3 in the server container and set BALANCE_LAB_PYTHON=python3, then restart the app.`,
503,
"balance_lab_runtime_unavailable",
);
}
const pending = [...jobs.values()].filter((job) => job.status === "queued" || job.status === "running").length;
if (pending >= maxQueued) {
throw new GameDatabaseError(
@@ -390,6 +435,9 @@ export function createBalanceLabJobService(options = {}) {
.slice(0, 100)
.map(publicJob);
},
runtime() {
return { ...runtimeProbe() };
},
get(id) {
return publicJob(requireJob(id));
},