55 lines
1.8 KiB
Python
55 lines
1.8 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
import typer
|
|
|
|
from tht.cli.config_cmd import CONFIG_OPT
|
|
from tht.config import ConfigError, load_config
|
|
|
|
|
|
def _emit(payload: dict[str, Any], as_json: bool) -> None:
|
|
if as_json:
|
|
# A single serializer call keeps stdout valid for machine consumers.
|
|
typer.echo(json.dumps(payload, sort_keys=True))
|
|
return
|
|
for component, result in payload["components"].items():
|
|
typer.echo(f"{component}: {result['status']}")
|
|
|
|
|
|
def doctor(
|
|
config: Path = CONFIG_OPT,
|
|
as_json: bool = typer.Option(False, "--json", help="Emette diagnostica JSON."),
|
|
) -> None:
|
|
"""Validate portable storage configuration without contacting external services."""
|
|
data_root = os.environ.get("THT_DATA_ROOT")
|
|
components: dict[str, dict[str, Any]] = {
|
|
"config": {"status": "ok"},
|
|
"data_root": {"status": "ok" if data_root else "warning"},
|
|
}
|
|
try:
|
|
cfg = load_config(config)
|
|
except ConfigError as exc:
|
|
path_error = "outside workspace root" in str(exc)
|
|
target = "workspace_paths" if path_error else "config"
|
|
# Validation errors can contain Pydantic input excerpts, including credentials.
|
|
message = str(exc) if path_error else "configuration is invalid"
|
|
components[target] = {"status": "error", "message": message}
|
|
payload = {"ok": False, "components": components}
|
|
_emit(payload, as_json)
|
|
raise typer.Exit(code=1)
|
|
|
|
legacy_absolute = [
|
|
name
|
|
for name in ("sessions", "artifacts", "indexes")
|
|
if getattr(cfg.roots, name).is_absolute()
|
|
]
|
|
components["workspace_paths"] = {
|
|
"status": "warning" if legacy_absolute else "ok",
|
|
"legacy_absolute": legacy_absolute,
|
|
}
|
|
_emit({"ok": True, "components": components}, as_json)
|