Finish the wonderland: System told truthfully, in eight tabs instead of ten
Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
@@ -200,6 +200,10 @@ CHECK_TITLES = {
|
||||
"integration.bluebubbles": "BlueBubbles",
|
||||
"integration.home-assistant": "Home Assistant",
|
||||
"integration.calendar": "Calendar",
|
||||
# Every id in CHECK_ORDER needs an entry here: unavailable_check() looks the
|
||||
# title up by id, so a missing one turned a probe that merely timed out into
|
||||
# a KeyError that took the whole scan down with it.
|
||||
"panama.updates": "Software updates",
|
||||
"panama.runtime-links": "Panama runtime links",
|
||||
"panama.vicinae-commands": "Panama commands",
|
||||
"panama.selected-terminal": "Selected terminal",
|
||||
@@ -294,6 +298,14 @@ def check_json(check: Check) -> dict[str, object]:
|
||||
result: dict[str, object] = {"id": check.id, "group": check.group, "title": check.title, "status": check.status, "detail": check.detail}
|
||||
if check.action is not None:
|
||||
result["action"] = action_json(check.action)
|
||||
# The exact command a repair would run, so the page can show it before
|
||||
# anybody presses the button. Taken from REPAIR_COMMANDS rather than
|
||||
# written out again in the UI -- a second copy is a copy that can be wrong,
|
||||
# and the whole point of showing it is that it is what actually happens.
|
||||
# Only the authored-command repairs have one; the three in-process repairs
|
||||
# are Python, not a command line, and claiming otherwise would be a lie.
|
||||
if check.id in REPAIR_COMMANDS:
|
||||
result["repairCommand"] = " ".join(REPAIR_COMMANDS[check.id])
|
||||
return result
|
||||
|
||||
|
||||
@@ -659,14 +671,14 @@ def check_updates(config: DoctorConfig) -> Check:
|
||||
if newest != running:
|
||||
return Check("panama.updates", "panama-tools", "Software updates", "warning",
|
||||
f"A newer kernel is installed than the one running ({running} → {newest}). Restart to use it.",
|
||||
action=Action("open", "Open Software Update"))
|
||||
action=Action("open", "Open Software Update", target="updates"))
|
||||
|
||||
try:
|
||||
payload = json.loads(cache.read_text(encoding="utf-8"))
|
||||
except (OSError, json.JSONDecodeError):
|
||||
return Check("panama.updates", "panama-tools", "Software updates", "unconfigured",
|
||||
"Updates have not been checked yet.",
|
||||
action=Action("open", "Open Software Update"))
|
||||
action=Action("open", "Open Software Update", target="updates"))
|
||||
|
||||
checked_at = int(payload.get("checkedAt", 0))
|
||||
age_days = (time.time() - checked_at) / 86400 if checked_at else 999
|
||||
@@ -677,11 +689,11 @@ def check_updates(config: DoctorConfig) -> Check:
|
||||
if security > 0:
|
||||
return Check("panama.updates", "panama-tools", "Software updates", "warning",
|
||||
f"{security} pending update{'' if security == 1 else 's'} carry a security advisory.",
|
||||
action=Action("open", "Open Software Update"))
|
||||
action=Action("open", "Open Software Update", target="updates"))
|
||||
if age_days > 7:
|
||||
return Check("panama.updates", "panama-tools", "Software updates", "unconfigured",
|
||||
"Updates have not been checked in over a week.",
|
||||
action=Action("open", "Open Software Update"))
|
||||
action=Action("open", "Open Software Update", target="updates"))
|
||||
if total > 0:
|
||||
return Check("panama.updates", "panama-tools", "Software updates", "ok",
|
||||
f"{total} update{'' if total == 1 else 's'} available, none carrying a security advisory.")
|
||||
@@ -799,14 +811,18 @@ def unavailable_versions() -> list[dict[str, str]]:
|
||||
return [{"id": name, "version": "unavailable"} for name in ("hyprland", "quickshell", "fedora", "panama")]
|
||||
|
||||
|
||||
def collect_checks(config: DoctorConfig) -> list[Check]:
|
||||
probes: dict[str, Callable[[], Check]] = {
|
||||
def probe_table(config: DoctorConfig) -> dict[str, Callable[[], Check]]:
|
||||
return {
|
||||
"desktop.hyprland": lambda: check_hyprland(config), "desktop.quickshell": lambda: check_quickshell(config), "desktop.notifications": lambda: check_notifications(config), "desktop.portals": lambda: check_portals(config), "desktop.portal-stability": lambda: check_portal_stability(config), "desktop.document-portal": lambda: check_document_portal(config),
|
||||
"desktop.hyprpaper": lambda: service_check("desktop.hyprpaper", "Hyprpaper", "hyprpaper", config, Action("repair", "Restart Hyprpaper")), "desktop.hypridle": lambda: service_check("desktop.hypridle", "Hypridle", "hypridle", config, Action("repair", "Restart Hypridle")), "desktop.hyprlock": lambda: check_hyprlock(config), "desktop.vicinae": lambda: service_check("desktop.vicinae", "Vicinae", "vicinae", config, Action("repair", "Restart Vicinae")), "input.pipewire": lambda: service_check("input.pipewire", "PipeWire", "pipewire", config),
|
||||
"input.clipboard": lambda: simple_ipc_check("input.clipboard", "Clipboard", "clipboard", config), "input.wallpaper": lambda: simple_ipc_check("input.wallpaper", "Wallpaper", "wallpaper", config), "input.video-wallpaper": lambda: check_video_wallpaper(config), "input.capture": lambda: simple_ipc_check("input.capture", "Capture", "capture", config), "input.ocr": lambda: executable_check("input.ocr", "OCR", "tesseract", config), "input.brightness": lambda: check_brightness(config),
|
||||
"integration.nextcloud": lambda: check_nextcloud(config), "integration.rustdesk": lambda: check_rustdesk(config), "integration.kdeconnect": lambda: check_kdeconnect(config), "integration.bluebubbles": lambda: check_bluebubbles(config), "integration.home-assistant": lambda: check_home_assistant(config), "integration.calendar": lambda: check_calendar(config),
|
||||
"panama.updates": lambda: check_updates(config), "panama.runtime-links": lambda: check_runtime_links(config), "panama.vicinae-commands": lambda: check_vicinae_commands(config), "panama.selected-terminal": lambda: executable_check("panama.selected-terminal", "Selected terminal", "kitty", config), "panama.selected-launcher": lambda: executable_check("panama.selected-launcher", "Selected launcher", "vicinae", config), "panama.processes": lambda: check_processes(config), "panama.caffeine": lambda: check_caffeine(config),
|
||||
}
|
||||
|
||||
|
||||
def collect_checks(config: DoctorConfig) -> list[Check]:
|
||||
probes = probe_table(config)
|
||||
with ThreadPoolExecutor(max_workers=8) as executor:
|
||||
futures = {check_id: executor.submit(probes[check_id]) for check_id in CHECK_ORDER}
|
||||
checks: list[Check] = []
|
||||
@@ -818,11 +834,7 @@ def collect_checks(config: DoctorConfig) -> list[Check]:
|
||||
return checks
|
||||
|
||||
|
||||
def snapshot(config: DoctorConfig) -> dict[str, object]:
|
||||
try:
|
||||
checks = collect_checks(config)
|
||||
except Exception:
|
||||
checks = [unavailable_check(check_id) for check_id in CHECK_ORDER]
|
||||
def snapshot_of(config: DoctorConfig, checks: list[Check]) -> dict[str, object]:
|
||||
counts = {status: sum(check.status == status for check in checks) for status in ("ok", "warning", "error", "unconfigured")}
|
||||
overall: Literal["healthy", "warning", "error"] = "error" if counts["error"] else "warning" if counts["warning"] else "healthy"
|
||||
session = "hyprland" if "hyprland" in os.environ.get("XDG_CURRENT_DESKTOP", "").casefold() else "other"
|
||||
@@ -833,6 +845,30 @@ def snapshot(config: DoctorConfig) -> dict[str, object]:
|
||||
return {"schemaVersion": 1, "generatedAt": datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z"), "summary": {"status": overall, "healthy": counts["ok"], "warnings": counts["warning"], "errors": counts["error"], "unconfigured": counts["unconfigured"]}, "context": {"session": session, "versions": versions}, "checks": [check_json(check) for check in checks]}
|
||||
|
||||
|
||||
def snapshot(config: DoctorConfig) -> dict[str, object]:
|
||||
try:
|
||||
checks = collect_checks(config)
|
||||
except Exception:
|
||||
checks = [unavailable_check(check_id) for check_id in CHECK_ORDER]
|
||||
return snapshot_of(config, checks)
|
||||
|
||||
|
||||
def single_check(check_id: str, config: DoctorConfig) -> dict[str, object]:
|
||||
"""One probe, in the shape of a whole snapshot.
|
||||
|
||||
Re-checking a single row after a repair should not cost the other
|
||||
twenty-nine probes. The reply is the same envelope a full scan produces --
|
||||
same schema, same summary arithmetic, same check object -- so the caller
|
||||
validates it with the code it already has, rather than growing a second
|
||||
reader for a second shape that could drift from the first.
|
||||
"""
|
||||
try:
|
||||
checks = [probe_table(config)[check_id]()]
|
||||
except Exception:
|
||||
checks = [unavailable_check(check_id)]
|
||||
return snapshot_of(config, checks)
|
||||
|
||||
|
||||
def repair_authored_command(check_id: str, config: DoctorConfig) -> RepairResult:
|
||||
command = REPAIR_COMMANDS[check_id]
|
||||
if check_id == "desktop.quickshell":
|
||||
@@ -1139,8 +1175,22 @@ def main(argv: list[str]) -> int:
|
||||
output.add_argument("--json", action="store_true")
|
||||
output.add_argument("--summary", action="store_true")
|
||||
parser.add_argument("--repair", metavar="CHECK_ID")
|
||||
parser.add_argument("verb", nargs="*", metavar="check CHECK_ID",
|
||||
help="check CHECK_ID -- re-run one probe and print it as a snapshot")
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
if args.verb:
|
||||
# An unknown id is refused rather than answered with an empty snapshot:
|
||||
# a caller that asked for a check that does not exist has a bug, and a
|
||||
# valid-looking reply with no rows in it would hide it.
|
||||
if len(args.verb) != 2 or args.verb[0] != "check" or args.verb[1] not in CHECK_ORDER:
|
||||
parser.error("usage: panama-doctor check CHECK_ID")
|
||||
if args.repair is not None or args.summary:
|
||||
parser.error("check takes no other output mode")
|
||||
print(json.dumps(single_check(args.verb[1], config_from_environment()),
|
||||
separators=(",", ":"), sort_keys=False))
|
||||
return 0
|
||||
|
||||
if args.repair is not None:
|
||||
if args.repair not in REPAIR_IDS or args.summary:
|
||||
result = RepairResult(args.repair, False, 2, "This health check has no authored repair.")
|
||||
|
||||
Reference in New Issue
Block a user