Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions cortexapps_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

from cortexapps_cli.cortex_client import CortexClient

import cortexapps_cli.commands.ai_skills as ai_skills
import cortexapps_cli.commands.api_keys as api_keys
import cortexapps_cli.commands.audit_logs as audit_logs
import cortexapps_cli.commands.backup as backup
Expand Down Expand Up @@ -47,6 +46,7 @@
import cortexapps_cli.commands.teams as teams
import cortexapps_cli.commands.users as users
import cortexapps_cli.commands.verifications as verifications
import cortexapps_cli.commands.whoami as whoami
import cortexapps_cli.commands.workflows as workflows

class _SortedTyper(typer.Typer):
Expand All @@ -68,7 +68,7 @@ def __call__(self, *args, **kwargs):
# interleaved with add_typer() calls so they appear alphabetically in --help.

# global options
@app.callback()
@app.callback(invoke_without_command=True)
def global_callback(
ctx: typer.Context,
api_key: str = typer.Option(None, "--api-key", "-k", help="API key", envvar="CORTEX_API_KEY"),
Expand Down Expand Up @@ -143,6 +143,10 @@ def global_callback(
api_key = api_key.strip('"\' ')
url = url.strip('"\' /')

if ctx.invoked_subcommand is None:
typer.echo(ctx.get_help())
raise typer.Exit()

ctx.obj["client"] = CortexClient(api_key, tenant, numeric_level, url, rate_limit)

DEFAULT_API_URL = "https://api.getcortexapp.com"
Expand Down Expand Up @@ -253,7 +257,6 @@ def version():
print(version)

# Register all commands alphabetically so they appear in order in --help
app.add_typer(ai_skills.app, name="ai-skills")
app.add_typer(api_keys.app, name="api-keys")
app.add_typer(audit_logs.app, name="audit-logs")
app.add_typer(backup.app, name="backup")
Expand Down Expand Up @@ -288,6 +291,7 @@ def version():
app.add_typer(users.app, name="users")
app.add_typer(verifications.app, name="verifications")
app.command()(version)
app.add_typer(whoami.app, name="whoami")
app.add_typer(workflows.app, name="workflows")

if __name__ == "__main__":
Expand Down
200 changes: 0 additions & 200 deletions cortexapps_cli/commands/ai_skills.py

This file was deleted.

8 changes: 8 additions & 0 deletions cortexapps_cli/commands/api_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,14 @@ def get(
r = client.get("api/v1/auth/key/"+ cid)
print_output_with_context(ctx, r)

@app.command()
def current(ctx: typer.Context):
"""
Print the API key currently in use. Useful for passing to scripts, e.g. $(cortex api-keys current).
"""
client = ctx.obj["client"]
typer.echo(client.api_key)

@app.command()
def delete(
ctx: typer.Context,
Expand Down
59 changes: 59 additions & 0 deletions cortexapps_cli/commands/whoami.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import base64
import gzip
import json
import typer
from datetime import datetime, timezone
from rich.console import Console
from rich.table import Table
import rich.box

app = typer.Typer(
help="Show identity and metadata for the currently configured API key",
no_args_is_help=False,
invoke_without_command=True,
)

@app.callback(invoke_without_command=True)
def whoami(ctx: typer.Context):
"""Show identity and metadata for the currently configured API key."""
if ctx.invoked_subcommand is not None:
return

client = ctx.obj["client"]
token = client.api_key

parts = token.split(".")
if len(parts) != 3:
typer.echo("Error: API key is not a valid JWT", err=True)
raise typer.Exit(1)

header_b64, payload_b64, _ = parts
header = json.loads(base64.urlsafe_b64decode(header_b64 + "=="))
payload_bytes = base64.urlsafe_b64decode(payload_b64 + "==")
if header.get("zip") == "GZIP":
payload_bytes = gzip.decompress(payload_bytes)
claims = json.loads(payload_bytes)

def fmt_ts(ts):
if ts is None:
return "—"
return datetime.fromtimestamp(ts, tz=timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")

rows = [
("Base URL", client.base_url),
("Tenant", client.tenant),
("Tenant ID", claims.get("sub") or "—"),
("Token Type", claims.get("type") or "—"),
("Auth Scope", claims.get("key-authenticated-request-type") or "—"),
("Issued", fmt_ts(claims.get("iat"))),
("Expires", fmt_ts(claims.get("exp"))),
("Last 4", token[-4:]),
]

console = Console()
table = Table(show_header=False, box=rich.box.SIMPLE, padding=(0, 1))
table.add_column(style="dim", no_wrap=True)
table.add_column()
for label, value in rows:
table.add_row(label, value)
console.print(table)