72 lines
2.9 KiB
Python
72 lines
2.9 KiB
Python
#!/usr/bin/env python3
|
|
"""echo_doctor.py — M3: one-call readiness check. [v1.0 SCAFFOLD — run() implemented]
|
|
|
|
There is no single "is everything OK" command. doctor checks, in order, the things
|
|
that make ECHO usable this session and prints a green/red report. Intended to back an
|
|
`echo.py doctor` subcommand and the start of /echo-load when something looks wrong.
|
|
|
|
Also tracked here (TODO): complete the `load` fallback — echo.cmd_load reads the
|
|
heartbeat pointer but never lists _agent/sessions/ when it's missing/stale, though
|
|
SKILL.md:122 documents that fallback. See patch_load_fallback() below.
|
|
|
|
Exit: 0 all green · 1 a check is red.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
import echo # noqa: E402
|
|
|
|
MIN_PY = (3, 9)
|
|
|
|
|
|
def run() -> int:
|
|
reds = 0
|
|
|
|
def line(ok: bool, label: str, detail: str = "") -> None:
|
|
nonlocal reds
|
|
reds += 0 if ok else 1
|
|
print(f" [{'OK ' if ok else 'RED'}] {label}" + (f" — {detail}" if detail else ""))
|
|
|
|
print(f"echo doctor — endpoint {echo.BASE}")
|
|
|
|
# 1. interpreter
|
|
line(sys.version_info >= MIN_PY, f"python >= {MIN_PY[0]}.{MIN_PY[1]}",
|
|
f"running {sys.version_info.major}.{sys.version_info.minor}")
|
|
|
|
# 2. reachability + auth + marker, in one GET of the bootstrap marker
|
|
status, body = echo.request("GET", echo.vault_url("_agent/echo-vault.md"))
|
|
if status == 0:
|
|
line(False, "vault reachable", body.decode(errors="replace")[:120])
|
|
print("\ndoctor: endpoint unreachable — is Obsidian + the Local REST API running?")
|
|
return 1
|
|
line(status not in (401, 403), "auth accepted", f"HTTP {status}" if status in (401, 403) else "")
|
|
if status == 404:
|
|
line(False, "vault bootstrapped", "marker absent — run bootstrap.py")
|
|
elif status == 200:
|
|
text = body.decode(errors="replace")
|
|
ver = next((ln.split(":", 1)[1].strip() for ln in text.splitlines()
|
|
if ln.startswith("schema_version:")), "?")
|
|
line(True, "vault bootstrapped", f"schema_version={ver}")
|
|
else:
|
|
line(status < 400, "marker fetch", f"HTTP {status}")
|
|
|
|
# 3. key source (M1) + invariants pointer.
|
|
import echo_secrets
|
|
key_src = ("ECHO_KEY env" if os.environ.get("ECHO_KEY")
|
|
else "credentials file" if (echo_secrets._state_dir() / "credentials").exists()
|
|
else "baked-in fallback (deprecated — see API-KEY-SETUP.md)")
|
|
line(os.environ.get("ECHO_KEY") is not None or (echo_secrets._state_dir() / "credentials").exists(),
|
|
"API key source", key_src)
|
|
print(" [i] invariants — run `/echo-health` (vault_lint.py) for the full check")
|
|
|
|
print(f"\ndoctor: {'all green' if reds == 0 else f'{reds} issue(s) — see RED above'}")
|
|
return 1 if reds else 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(run())
|