const fs = require("node:fs"); const path = require("node:path"); function argValue(name, fallback = "") { const prefix = `--${name}=`; const match = process.argv.find((entry) => entry.startsWith(prefix)); return match ? match.slice(prefix.length) : fallback; } function hasFlag(name) { return process.argv.includes(`--${name}`); } function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } function parseWaitSeconds(value) { if (value === "") return 0; const parsed = Number(value); if (!Number.isInteger(parsed) || parsed < 0) { throw new Error("--wait must be a non-negative integer number of seconds."); } return parsed; } function expectedSignature() { const mainPath = path.join(process.cwd(), "electron", "main.ts"); const source = fs.readFileSync(mainPath, "utf8"); const match = source.match(/APP_RUNTIME_SIGNATURE\s*=\s*"([^"]+)"/); return match?.[1] || ""; } async function fetchJson(baseUrl, endpoint) { let response; try { response = await fetch(`${baseUrl}${endpoint}`); } catch (error) { throw new Error(`Could not reach ${baseUrl}${endpoint}. Start the elevated app with npm run dev:admin and confirm UAC before running live scans. (${error instanceof Error ? error.message : String(error)})`); } const text = await response.text(); let payload; try { payload = text ? JSON.parse(text) : null; } catch { throw new Error(`${endpoint} returned non-JSON response (${response.status}).`); } if (!response.ok) throw new Error(`${endpoint} returned ${response.status}: ${JSON.stringify(payload)}`); return payload; } function validatePreflight({ health, status, expected, requireElevated = true, requireGenshin = true }) { const errors = []; const appBuild = health?.appBuild; const runtime = status?.status?.runtimeInfo; if (!appBuild?.signature) { errors.push("/health is missing appBuild.signature."); } else if (expected && appBuild.signature !== expected) { errors.push(`Runtime signature '${appBuild.signature}' does not match source '${expected}'.`); } if (!status?.status) errors.push("/scanner/status is missing status payload."); if (!runtime) { errors.push("/scanner/status is missing runtimeInfo. Open the scanner view and restart the elevated app if needed."); } else { if (requireElevated && runtime.isElevated !== true) errors.push("Runtime is not elevated."); if (requireGenshin && runtime.genshinFound !== true) errors.push("Genshin process/window was not found."); } return { ok: errors.length === 0, errors, signature: appBuild?.signature || "", expectedSignature: expected || "", isElevated: runtime?.isElevated, genshinFound: runtime?.genshinFound, targetProcess: runtime?.targetProcess || "", foregroundProcess: runtime?.foregroundProcess || "", }; } function formatSummary(result) { const lines = [ `live preflight: ${result.ok ? "PASS" : "FAIL"}`, `signature: ${result.signature || "missing"}`, `expected: ${result.expectedSignature || "unknown"}`, `elevated: ${result.isElevated === true ? "yes" : result.isElevated === false ? "no" : "unknown"}`, `genshin: ${result.genshinFound === true ? "yes" : result.genshinFound === false ? "no" : "unknown"}`, `target: ${result.targetProcess || "unknown"}`, `foreground: ${result.foregroundProcess || "unknown"}`, ]; if (!result.ok) { lines.push("errors:"); for (const error of result.errors) lines.push(`- ${error}`); } return lines.join("\n"); } async function runPreflight({ baseUrl, expected, requireElevated, requireGenshin }) { const health = await fetchJson(baseUrl, "/health"); const status = await fetchJson(baseUrl, "/scanner/status"); return validatePreflight({ health, status, expected, requireElevated, requireGenshin }); } async function waitForPreflight(options, waitSeconds) { const deadline = Date.now() + waitSeconds * 1000; let lastError = null; let lastResult = null; while (true) { try { const result = await runPreflight(options); lastResult = result; if (result.ok || Date.now() >= deadline) return result; } catch (error) { lastError = error; if (Date.now() >= deadline) throw lastError; } await sleep(1000); } } async function main() { const baseUrl = argValue("base-url", "http://127.0.0.1:17317").replace(/\/$/, ""); const expected = argValue("expected-signature", expectedSignature()); const requireElevated = !hasFlag("allow-standard"); const requireGenshin = !hasFlag("allow-missing-genshin"); const waitSeconds = parseWaitSeconds(argValue("wait", "")); const options = { baseUrl, expected, requireElevated, requireGenshin }; const result = waitSeconds > 0 ? await waitForPreflight(options, waitSeconds) : await runPreflight(options); console.log(hasFlag("json") ? JSON.stringify(result, null, 2) : formatSummary(result)); if (!result.ok) process.exit(1); } if (require.main === module) { main().catch((error) => { console.error(error instanceof Error ? error.message : String(error)); process.exit(1); }); } module.exports = { formatSummary, parseWaitSeconds, runPreflight, validatePreflight, };