Update Healer Man server

This commit is contained in:
phenom committed 2026-09-19 21:01:51 -04:00
1 parent f8314e7de1
commit 1fedb9368d
76 files changed
+20556 -2523

No files matched your search

+164
View File
@@ -0,0 +1,164 @@
import { createReadStream, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { createBalanceLabJobService } from "./balance-lab-jobs.mjs";
import { cloneDefaultCompanionCooldownPolicy } from "./companion-cooldown-policy.mjs";
import {
cloneDefaultCompanionSpecReadinessPolicy,
normalizeCompanionSpecReadinessPolicy,
} from "./companion-spec-readiness.mjs";
import { GameDatabaseError } from "./database.mjs";
const API_ROOT = "/api/local/balance-lab";
function json(response, status, body) {
const content = Buffer.from(JSON.stringify(body));
response.writeHead(status, {
"Content-Type": "application/json; charset=utf-8",
"Content-Length": content.length,
"Cache-Control": "no-store",
});
response.end(content);
}
async function readJson(request, limit) {
const chunks = [];
let size = 0;
for await (const chunk of request) {
size += chunk.length;
if (size > limit) throw new GameDatabaseError("Request body is too large.", 413, "body_too_large");
chunks.push(chunk);
}
if (size === 0) return {};
try {
return JSON.parse(Buffer.concat(chunks).toString("utf8"));
} catch {
throw new GameDatabaseError("Request body must be valid JSON.", 400, "invalid_json");
}
}
function isLoopback(address) {
return address === "127.0.0.1" || address === "::1" || address === "::ffff:127.0.0.1";
}
export function createLocalBalanceLabApi(options = {}) {
const projectRoot = path.resolve(options.projectRoot ?? fileURLToPath(new URL("..", import.meta.url)));
const dataDir = path.resolve(options.dataDir ?? path.join(projectRoot, "artifacts", "balance-lab-gui-runtime"));
const readinessPath = path.join(dataDir, "companion-spec-readiness.json");
mkdirSync(dataDir, { recursive: true });
let readiness = { revision: 0, updatedAt: null, updatedBy: null, config: cloneDefaultCompanionSpecReadinessPolicy() };
try {
const stored = JSON.parse(readFileSync(readinessPath, "utf8"));
readiness = {
revision: Number.isInteger(stored.revision) ? stored.revision : 0,
updatedAt: Number.isFinite(stored.updatedAt) ? stored.updatedAt : null,
updatedBy: typeof stored.updatedBy === "string" ? stored.updatedBy : null,
config: normalizeCompanionSpecReadinessPolicy(stored.config),
};
} catch { /* missing or corrupt local policy falls back to shipped defaults */ }
const persistReadiness = () => {
const temporaryPath = `${readinessPath}.tmp`;
writeFileSync(temporaryPath, `${JSON.stringify(readiness, null, 2)}\n`, "utf8");
renameSync(temporaryPath, readinessPath);
};
const service = createBalanceLabJobService({
dataDir,
projectRoot,
pythonExecutable: options.pythonExecutable,
spawnProcess: options.spawnProcess,
runtimeProbe: options.runtimeProbe,
maxConcurrent: options.maxConcurrent,
maxQueued: options.maxQueued,
cooldownPolicyProvider: () => ({ config: cloneDefaultCompanionCooldownPolicy() }),
readinessPolicyProvider: () => readiness,
});
const account = Object.freeze({ username: "Local GUI" });
return {
service,
async handle(request, response) {
const url = new URL(request.url ?? "/", "http://127.0.0.1");
if (url.pathname !== API_ROOT && !url.pathname.startsWith(`${API_ROOT}/`)) return false;
if (!isLoopback(request.socket?.remoteAddress)) {
json(response, 403, { error: "The local Balance Lab API accepts loopback requests only.", code: "loopback_required" });
return true;
}
try {
if (url.pathname === `${API_ROOT}/companion-spec-readiness` && request.method === "GET") {
json(response, 200, readiness);
return true;
}
if (url.pathname === `${API_ROOT}/companion-spec-readiness` && request.method === "PUT") {
const body = await readJson(request, 256 * 1024);
if (!Number.isInteger(body.expectedRevision) || body.expectedRevision !== readiness.revision) {
throw new GameDatabaseError("The companion readiness policy changed. Reload and try again.", 409, "revision_conflict");
}
let config;
try {
config = normalizeCompanionSpecReadinessPolicy(body.config);
} catch (error) {
throw new GameDatabaseError(
error instanceof Error ? error.message : "Invalid companion readiness policy.",
400,
"invalid_companion_spec_readiness",
);
}
readiness = {
revision: readiness.revision + 1,
updatedAt: Date.now(),
updatedBy: "Local GUI",
config,
};
persistReadiness();
json(response, 200, readiness);
return true;
}
if (url.pathname === `${API_ROOT}/jobs` && request.method === "GET") {
json(response, 200, { jobs: service.list() });
return true;
}
if (url.pathname === `${API_ROOT}/runtime` && request.method === "GET") {
json(response, 200, { runtime: service.runtime() });
return true;
}
if (url.pathname === `${API_ROOT}/jobs/batch` && request.method === "POST") {
json(response, 202, { jobs: service.startMany(await readJson(request, 4 * 1024 * 1024), account) });
return true;
}
if (url.pathname === `${API_ROOT}/jobs` && request.method === "POST") {
json(response, 202, { job: service.start(await readJson(request, 32 * 1024), account) });
return true;
}
const artifactMatch = new RegExp(`^${API_ROOT}/jobs/([0-9a-f-]{36})/artifacts/([A-Za-z0-9._-]+)$`).exec(url.pathname);
if (artifactMatch && request.method === "GET") {
const artifact = service.artifact(artifactMatch[1], artifactMatch[2]);
response.writeHead(200, {
"Content-Type": artifact.contentType,
"Content-Length": artifact.size,
"Cache-Control": "no-store",
"Content-Disposition": `inline; filename="${artifactMatch[2]}"`,
"X-Content-Type-Options": "nosniff",
});
createReadStream(artifact.filePath).pipe(response);
return true;
}
const jobMatch = new RegExp(`^${API_ROOT}/jobs/([0-9a-f-]{36})$`).exec(url.pathname);
if (jobMatch && request.method === "GET") {
json(response, 200, { job: service.get(jobMatch[1]) });
return true;
}
json(response, 404, { error: "Local Balance Lab route not found.", code: "not_found" });
} catch (error) {
const known = error instanceof GameDatabaseError;
json(response, known ? error.status : 500, {
error: known ? error.message : "The local Balance Lab request failed.",
code: known ? error.code : "internal_error",
});
}
return true;
},
close() {
service.close();
},
};
}