Files
python-sdk/pkg/hanzo-cli/hanzo_cli/cli.py
T
Zoo QueenandHanzo Dev a84ec95815 fix(lint): resolve all ruff + black errors across all packages
Fix 35 ruff errors (E722, F841, B904, B905, B007, E741, S202, S310,
S306, S110, TID251, UP042, F401) and run black on 526 files.

Co-authored-by: Hanzo Dev <[email protected]>
2026-02-21 23:32:32 -08:00

140 lines
4.1 KiB
Python

"""Hanzo CLI — unified command-line interface for the Hanzo platform.
Usage:
hanzo --version
hanzo login [--no-browser] [--port N]
hanzo logout
hanzo whoami
hanzo iam <subcommand>
hanzo kms <subcommand>
hanzo paas <subcommand>
hanzo k8s <subcommand | kubectl args>
"""
from __future__ import annotations
import click
from rich.console import Console
from rich.table import Table
from hanzo_cli import __version__
from hanzo_cli.auth import (
browser_login,
get_token_info,
password_login,
)
from hanzo_cli.auth import (
logout as do_logout,
)
console = Console()
@click.group()
@click.version_option(version=__version__, prog_name="hanzo")
@click.pass_context
def main(ctx: click.Context) -> None:
"""Hanzo CLI — manage IAM, secrets, and deployments."""
ctx.ensure_object(dict)
# =========================================================================
# Auth commands
# =========================================================================
@main.command()
@click.option(
"--no-browser", is_flag=True, help="Use password login instead of browser OAuth."
)
@click.option("--port", default=8399, help="Local callback port for browser login.")
@click.pass_context
def login(ctx: click.Context, no_browser: bool, port: int) -> None:
"""Authenticate with Hanzo IAM."""
try:
if no_browser:
token_data = password_login()
else:
token_data = browser_login(port=port)
console.print("[green]Logged in successfully.[/green]")
if token_data.get("access_token"):
# Decode the token subject for display
_at = token_data["access_token"]
console.print("Token stored at ~/.hanzo/auth/token.json")
except Exception as e:
console.print(f"[red]Login failed:[/red] {e}")
raise SystemExit(1) from e
@main.command()
def logout() -> None:
"""Clear stored credentials."""
do_logout()
console.print("Logged out. Token removed.")
@main.command()
def whoami() -> None:
"""Show current authentication status."""
token_data = get_token_info()
if not token_data:
console.print("[yellow]Not logged in.[/yellow] Run 'hanzo login'.")
raise SystemExit(1)
table = Table(title="Current Session")
table.add_column("Field", style="cyan")
table.add_column("Value", style="white")
table.add_row("Server", token_data.get("server_url", "—"))
table.add_row("Organization", token_data.get("organization", "—"))
table.add_row("Application", token_data.get("application", "—"))
table.add_row("Client ID", token_data.get("client_id", "—"))
# Try to decode the token for user info
access_token = token_data.get("access_token", "")
if access_token:
try:
import jwt
# Decode without verification just to show user info
claims = jwt.decode(access_token, options={"verify_signature": False})
table.add_row("User", claims.get("name", claims.get("sub", "—")))
table.add_row("Email", claims.get("email", "—"))
table.add_row("Owner", claims.get("owner", "—"))
except Exception:
table.add_row("Token", f"{access_token[:20]}...")
login_time = token_data.get("login_time")
if login_time:
from datetime import datetime, timezone
dt = datetime.fromtimestamp(login_time, tz=timezone.utc)
table.add_row("Login Time", dt.isoformat())
console.print(table)
# =========================================================================
# Register subgroups
# =========================================================================
from hanzo_cli.bot.commands import bot # noqa: E402
from hanzo_cli.iam.commands import iam # noqa: E402
from hanzo_cli.k8s.commands import k8s # noqa: E402
from hanzo_cli.kms.commands import kms # noqa: E402
from hanzo_cli.paas.commands import deploy, paas # noqa: E402
main.add_command(bot)
main.add_command(iam)
main.add_command(k8s)
main.add_command(kms)
main.add_command(paas)
# Top-level aliases — `hanzo deploy` = `hanzo paas deploy`
main.add_command(deploy)
if __name__ == "__main__":
main()