feat(ncp): doctor --fix applies safe, idempotent repairs
Opt-in repair pass: reinstall Python deps on import failure, npm install when node_modules missing, npm run build when dist missing, fetch Ollama binary. Plain doctor stays read-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+48
-3
@@ -458,7 +458,50 @@ def cmd_logs(target) -> None:
|
||||
print("Usage: ncp logs [frontend|backend|memory|all]")
|
||||
|
||||
|
||||
def cmd_doctor() -> None:
|
||||
def _apply_fixes() -> None:
|
||||
"""Opt-in safe repairs (ncp doctor --fix). Only idempotent, non-destructive
|
||||
actions: build the UI, install deps, fetch the Ollama binary. Anything that
|
||||
could lose data or needs a decision is left to the user with a hint."""
|
||||
print("\nApplying safe fixes...\n")
|
||||
did = False
|
||||
web, npm_path = FRONTEND_DIR, npm()
|
||||
|
||||
if not PYTHON.exists():
|
||||
print(" ✘ Promethean venv missing — run ./install.sh to build it (skipped: heavy).")
|
||||
else:
|
||||
importable = subprocess.run(
|
||||
[str(PYTHON), "-c", "from synapse.main import sio_app"],
|
||||
cwd=str(ROOT), capture_output=True).returncode == 0
|
||||
if not importable:
|
||||
print(" Backend import failed — reinstalling Python dependencies...")
|
||||
sys.path.insert(0, str(ROOT / "bin"))
|
||||
import importlib.util
|
||||
spec = importlib.util.spec_from_file_location("sync", ROOT / "bin" / "sync.py")
|
||||
sync = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(sync)
|
||||
subprocess.run([str(PYTHON), "-m", "pip", "install", "-r", sync.requirements()], cwd=str(ROOT))
|
||||
did = True
|
||||
|
||||
if npm_path and web.is_dir() and not (web / "node_modules").is_dir():
|
||||
print(" node_modules missing — running npm install...")
|
||||
subprocess.run([npm_path, "install"], cwd=str(web))
|
||||
did = True
|
||||
|
||||
if npm_path and web.is_dir() and not (web / "dist" / "index.html").exists():
|
||||
print(" Built UI missing — running npm run build...")
|
||||
subprocess.run([npm_path, "run", "build"], cwd=str(web))
|
||||
did = True
|
||||
|
||||
if not ollama_bin() and os.name != "nt" and (ROOT / "bin" / "fetch-ollama.sh").exists():
|
||||
print(" Ollama binary missing — fetching...")
|
||||
subprocess.run(["bash", str(ROOT / "bin" / "fetch-ollama.sh")])
|
||||
did = True
|
||||
|
||||
print("\n" + ("Fixes applied — re-run 'ncp doctor' to confirm." if did
|
||||
else "Nothing to fix (all safe-repairable checks already pass)."))
|
||||
|
||||
|
||||
def cmd_doctor(fix: bool = False) -> None:
|
||||
def mark(ok, good, bad):
|
||||
print(f" {'✔' if ok else '✘'} {good if ok else bad}")
|
||||
|
||||
@@ -536,6 +579,8 @@ def cmd_doctor() -> None:
|
||||
f"Ollama models directory missing at {OLLAMA_MODELS_DIR}")
|
||||
print()
|
||||
cmd_status()
|
||||
if fix:
|
||||
_apply_fixes()
|
||||
|
||||
|
||||
def cmd_update() -> None:
|
||||
@@ -707,7 +752,7 @@ Commands:
|
||||
--frontend,-f Frontend logs
|
||||
--backend, -b Backend logs
|
||||
|
||||
doctor Run Nexus diagnostics
|
||||
doctor [--fix] Run Nexus diagnostics (--fix applies safe repairs)
|
||||
update Update Nexus dependencies
|
||||
clean Remove runtime files and caches
|
||||
|
||||
@@ -753,7 +798,7 @@ def main(argv) -> int:
|
||||
elif cmd == "logs":
|
||||
cmd_logs(arg)
|
||||
elif cmd == "doctor":
|
||||
cmd_doctor()
|
||||
cmd_doctor(fix="--fix" in rest)
|
||||
elif cmd == "update":
|
||||
cmd_update()
|
||||
elif cmd == "clean":
|
||||
|
||||
Reference in New Issue
Block a user