"""`catlas` — Company Atlas operations CLI. Command groups live in `companyatlas.commands.`; each module exposes `register(app: typer.Typer) -> None` and is auto-discovered here, so crawl, intelligence, API and seed commands can evolve independently. """ from __future__ import annotations import asyncio import importlib import logging import pkgutil from pathlib import Path from typing import Annotated import typer from rich.console import Console from companyatlas.config import settings from companyatlas.logging import setup_logging app = typer.Typer(name="catlas", help="Company Atlas — the live atlas of global companies.", no_args_is_help=True, add_completion=False) console = Console(stderr=True) out = Console() @app.callback() def _main(verbose: Annotated[bool, typer.Option("--verbose", "-v")] = False) -> None: setup_logging(level=logging.DEBUG if verbose else logging.INFO, service="catlas") settings.ensure_dirs() def run_async(coro): # type: ignore[no-untyped-def] """Run a coroutine and dispose the shared DB engine afterwards (for CLI commands).""" from companyatlas.db import dispose async def wrapper(): # type: ignore[no-untyped-def] try: return await coro finally: await dispose() return asyncio.run(wrapper()) @app.command() def migrate(revision: str = "head") -> None: """Apply database migrations (forward-only).""" from alembic import command from alembic.config import Config root = Path(__file__).resolve().parents[2] cfg = Config(str(root / "alembic.ini")) cfg.set_main_option("script_location", str(root / "migrations")) command.upgrade(cfg, revision) out.print("[green]migrations applied[/]") @app.command() def api(host: str | None = None, port: int | None = None, workers: int = 1, reload: bool = False) -> None: """Serve the FastAPI application (development).""" import uvicorn uvicorn.run("companyatlas.api.main:app", host=host or settings.api_host, port=port or settings.api_port, workers=workers, reload=reload, proxy_headers=True, access_log=False) @app.command() def version() -> None: import companyatlas out.print(companyatlas.__version__) def _discover_commands() -> None: try: import companyatlas.commands as pkg except ImportError: return for mod in pkgutil.iter_modules(pkg.__path__): if mod.name.startswith("_"): continue module = importlib.import_module(f"companyatlas.commands.{mod.name}") register = getattr(module, "register", None) if callable(register): register(app) _discover_commands() __all__ = ["app", "console", "out", "run_async"]