from __future__ import annotations from pathlib import Path from typing import Any from .fsutil import sha256_file from .state import bootstrap_state_path, load_bootstrap_state BSIPA_PLUGIN_ID = "bsipa" def latest_log_path(instance_path: Path) -> Path: return instance_path / "Logs" / "_latest.log" def check_bsipa_health(instance_path: Path, state_root: Path, instance: str) -> dict[str, Any]: state = load_bootstrap_state(state_root, instance) messages: list[str] = [] required = ["IPA.exe", "winhttp.dll"] for rel in required: if not (instance_path / rel).is_file(): messages.append(f"missing {rel}") for rel in ("IPA", "Libs"): if not (instance_path / rel).is_dir(): messages.append(f"missing {rel}/") log_path = latest_log_path(instance_path) if not log_path.is_file(): messages.append("missing Logs/_latest.log") else: text = log_path.read_text(encoding="utf-8", errors="replace") if "Beat Saber IPA (BSIPA):" not in text and "Beat Saber IPA" not in text: messages.append("Logs/_latest.log does not show BSIPA startup") if not state: messages.append(f"missing bootstrap state: {bootstrap_state_path(state_root, instance)}") return { "ok": not messages, "messages": messages, "statePath": str(bootstrap_state_path(state_root, instance)), "logPath": str(log_path), "logSha256": sha256_file(log_path) if log_path.is_file() else None, "bootstrapRecordedAt": state.get("updatedAt"), }