Add CLI subcommands and doctor diagnostics
This commit is contained in:
parent
9c7d7b35b1
commit
ad1af63fac
7 changed files with 385 additions and 13 deletions
102
src/aman.py
102
src/aman.py
|
|
@ -15,9 +15,10 @@ from pathlib import Path
|
|||
from typing import Any
|
||||
|
||||
from aiprocess import LlamaProcessor
|
||||
from config import Config, load, redacted_dict
|
||||
from constants import MODEL_PATH, RECORD_TIMEOUT_SEC, STT_LANGUAGE
|
||||
from config import Config, load, redacted_dict, validate
|
||||
from constants import DEFAULT_CONFIG_PATH, MODEL_PATH, RECORD_TIMEOUT_SEC, STT_LANGUAGE
|
||||
from desktop import get_desktop_adapter
|
||||
from diagnostics import run_diagnostics
|
||||
from recorder import start_recording as start_audio_recording
|
||||
from recorder import stop_recording as stop_audio_recording
|
||||
from vocabulary import VocabularyEngine
|
||||
|
|
@ -377,20 +378,80 @@ def _lock_single_instance():
|
|||
return lock_file
|
||||
|
||||
|
||||
def main():
|
||||
global _LOCK_HANDLE
|
||||
def _build_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--config", default="", help="path to config.json")
|
||||
parser.add_argument("--dry-run", action="store_true", help="log hotkey only")
|
||||
parser.add_argument("-v", "--verbose", action="store_true", help="enable verbose logs")
|
||||
args = parser.parse_args()
|
||||
subparsers = parser.add_subparsers(dest="command")
|
||||
|
||||
run_parser = subparsers.add_parser("run", help="run the aman daemon")
|
||||
run_parser.add_argument("--config", default="", help="path to config.json")
|
||||
run_parser.add_argument("--dry-run", action="store_true", help="log hotkey only")
|
||||
run_parser.add_argument("-v", "--verbose", action="store_true", help="enable verbose logs")
|
||||
|
||||
doctor_parser = subparsers.add_parser("doctor", help="run startup diagnostics")
|
||||
doctor_parser.add_argument("--config", default="", help="path to config.json")
|
||||
doctor_parser.add_argument("--json", action="store_true", help="print JSON output")
|
||||
doctor_parser.add_argument("-v", "--verbose", action="store_true", help="enable verbose logs")
|
||||
|
||||
init_parser = subparsers.add_parser("init", help="write a default config")
|
||||
init_parser.add_argument("--config", default="", help="path to config.json")
|
||||
init_parser.add_argument("--force", action="store_true", help="overwrite existing config")
|
||||
return parser
|
||||
|
||||
|
||||
def _parse_cli_args(argv: list[str]) -> argparse.Namespace:
|
||||
parser = _build_parser()
|
||||
normalized_argv = list(argv)
|
||||
known_commands = {"run", "doctor", "init"}
|
||||
if not normalized_argv or normalized_argv[0] not in known_commands:
|
||||
normalized_argv = ["run", *normalized_argv]
|
||||
return parser.parse_args(normalized_argv)
|
||||
|
||||
|
||||
def _configure_logging(verbose: bool) -> None:
|
||||
logging.basicConfig(
|
||||
stream=sys.stderr,
|
||||
level=logging.DEBUG if args.verbose else logging.INFO,
|
||||
level=logging.DEBUG if verbose else logging.INFO,
|
||||
format="aman: %(asctime)s %(levelname)s %(message)s",
|
||||
)
|
||||
cfg = load(args.config)
|
||||
|
||||
|
||||
def _doctor_command(args: argparse.Namespace) -> int:
|
||||
report = run_diagnostics(args.config)
|
||||
if args.json:
|
||||
print(report.to_json())
|
||||
else:
|
||||
for check in report.checks:
|
||||
status = "OK" if check.ok else "FAIL"
|
||||
line = f"[{status}] {check.id}: {check.message}"
|
||||
if check.hint:
|
||||
line = f"{line} | hint: {check.hint}"
|
||||
print(line)
|
||||
print(f"overall: {'ok' if report.ok else 'failed'}")
|
||||
return 0 if report.ok else 2
|
||||
|
||||
|
||||
def _init_command(args: argparse.Namespace) -> int:
|
||||
config_path = Path(args.config) if args.config else DEFAULT_CONFIG_PATH
|
||||
if config_path.exists() and not args.force:
|
||||
logging.error("init failed: config already exists at %s (use --force to overwrite)", config_path)
|
||||
return 1
|
||||
|
||||
cfg = Config()
|
||||
validate(cfg)
|
||||
config_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
config_path.write_text(f"{json.dumps(redacted_dict(cfg), indent=2)}\n", encoding="utf-8")
|
||||
logging.info("wrote default config to %s", config_path)
|
||||
return 0
|
||||
|
||||
|
||||
def _run_command(args: argparse.Namespace) -> int:
|
||||
global _LOCK_HANDLE
|
||||
|
||||
try:
|
||||
cfg = load(args.config)
|
||||
except Exception as exc:
|
||||
logging.error("startup failed: %s", exc)
|
||||
return 1
|
||||
_LOCK_HANDLE = _lock_single_instance()
|
||||
|
||||
logging.info("hotkey: %s", cfg.daemon.hotkey)
|
||||
|
|
@ -415,7 +476,7 @@ def main():
|
|||
daemon = Daemon(cfg, desktop, verbose=args.verbose)
|
||||
except Exception as exc:
|
||||
logging.error("startup failed: %s", exc)
|
||||
raise SystemExit(1)
|
||||
return 1
|
||||
|
||||
shutdown_once = threading.Event()
|
||||
|
||||
|
|
@ -441,13 +502,28 @@ def main():
|
|||
)
|
||||
except Exception as exc:
|
||||
logging.error("hotkey setup failed: %s", exc)
|
||||
raise SystemExit(1)
|
||||
return 1
|
||||
logging.info("ready")
|
||||
try:
|
||||
desktop.run_tray(daemon.get_state, lambda: shutdown("quit requested"))
|
||||
finally:
|
||||
daemon.shutdown(timeout=1.0)
|
||||
return 0
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
args = _parse_cli_args(list(argv) if argv is not None else sys.argv[1:])
|
||||
if args.command == "run":
|
||||
_configure_logging(args.verbose)
|
||||
return _run_command(args)
|
||||
if args.command == "doctor":
|
||||
_configure_logging(args.verbose)
|
||||
return _doctor_command(args)
|
||||
if args.command == "init":
|
||||
_configure_logging(False)
|
||||
return _init_command(args)
|
||||
raise RuntimeError(f"unsupported command: {args.command}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
raise SystemExit(main())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue