hanzo 0.3.24__py3-none-any.whl → 0.3.25__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of hanzo might be problematic. Click here for more details.
- hanzo/__init__.py +2 -2
- hanzo/cli.py +13 -5
- hanzo/commands/auth.py +206 -266
- hanzo/commands/auth_broken.py +377 -0
- hanzo/commands/chat.py +3 -0
- hanzo/interactive/enhanced_repl.py +513 -0
- hanzo/interactive/repl.py +2 -2
- hanzo/ui/__init__.py +13 -0
- hanzo/ui/inline_startup.py +136 -0
- hanzo/ui/startup.py +350 -0
- {hanzo-0.3.24.dist-info → hanzo-0.3.25.dist-info}/METADATA +1 -1
- {hanzo-0.3.24.dist-info → hanzo-0.3.25.dist-info}/RECORD +14 -9
- {hanzo-0.3.24.dist-info → hanzo-0.3.25.dist-info}/WHEEL +0 -0
- {hanzo-0.3.24.dist-info → hanzo-0.3.25.dist-info}/entry_points.txt +0 -0
hanzo/__init__.py
CHANGED
hanzo/cli.py
CHANGED
|
@@ -25,9 +25,11 @@ from .commands import (
|
|
|
25
25
|
)
|
|
26
26
|
from .utils.output import console
|
|
27
27
|
from .interactive.repl import HanzoREPL
|
|
28
|
+
from .interactive.enhanced_repl import EnhancedHanzoREPL
|
|
29
|
+
from .ui.startup import show_startup
|
|
28
30
|
|
|
29
31
|
# Version
|
|
30
|
-
__version__ = "0.3.
|
|
32
|
+
__version__ = "0.3.23"
|
|
31
33
|
|
|
32
34
|
|
|
33
35
|
@click.group(invoke_without_command=True)
|
|
@@ -55,14 +57,20 @@ def cli(ctx, verbose: bool, json: bool, config: Optional[str]):
|
|
|
55
57
|
|
|
56
58
|
if os.environ.get("HANZO_COMPUTE_NODE") == "1":
|
|
57
59
|
# Start as a compute node
|
|
58
|
-
|
|
59
60
|
asyncio.run(start_compute_node(ctx))
|
|
60
61
|
else:
|
|
62
|
+
# Show startup UI (unless in quiet mode)
|
|
63
|
+
if not ctx.obj.get("quiet") and not os.environ.get("HANZO_NO_STARTUP"):
|
|
64
|
+
show_startup(minimal=os.environ.get("HANZO_MINIMAL_UI") == "1")
|
|
65
|
+
|
|
61
66
|
# Enter interactive REPL mode
|
|
62
|
-
console.print("[bold cyan]Hanzo AI - Interactive Mode[/bold cyan]")
|
|
63
|
-
console.print("Type 'help' for commands, 'exit' to quit\n")
|
|
64
67
|
try:
|
|
65
|
-
|
|
68
|
+
# Use enhanced REPL if available, otherwise fallback
|
|
69
|
+
use_enhanced = os.environ.get("HANZO_ENHANCED_REPL", "1") == "1"
|
|
70
|
+
if use_enhanced:
|
|
71
|
+
repl = EnhancedHanzoREPL(console=console)
|
|
72
|
+
else:
|
|
73
|
+
repl = HanzoREPL(console=console)
|
|
66
74
|
asyncio.run(repl.run())
|
|
67
75
|
except KeyboardInterrupt:
|
|
68
76
|
console.print("\n[yellow]Interrupted[/yellow]")
|
hanzo/commands/auth.py
CHANGED
|
@@ -1,14 +1,56 @@
|
|
|
1
|
-
"""Authentication commands."""
|
|
1
|
+
"""Authentication commands for Hanzo CLI."""
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
|
+
import json
|
|
4
5
|
from pathlib import Path
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from typing import Optional
|
|
5
8
|
|
|
6
9
|
import click
|
|
7
10
|
from rich.prompt import Prompt
|
|
11
|
+
from rich.panel import Panel
|
|
12
|
+
from rich.table import Table
|
|
13
|
+
from rich import box
|
|
8
14
|
|
|
9
15
|
from ..utils.output import console
|
|
10
16
|
|
|
11
17
|
|
|
18
|
+
class AuthManager:
|
|
19
|
+
"""Manage Hanzo authentication."""
|
|
20
|
+
|
|
21
|
+
def __init__(self):
|
|
22
|
+
self.config_dir = Path.home() / ".hanzo"
|
|
23
|
+
self.auth_file = self.config_dir / "auth.json"
|
|
24
|
+
|
|
25
|
+
def load_auth(self) -> dict:
|
|
26
|
+
"""Load authentication data."""
|
|
27
|
+
if self.auth_file.exists():
|
|
28
|
+
try:
|
|
29
|
+
return json.loads(self.auth_file.read_text())
|
|
30
|
+
except:
|
|
31
|
+
pass
|
|
32
|
+
return {}
|
|
33
|
+
|
|
34
|
+
def save_auth(self, auth: dict):
|
|
35
|
+
"""Save authentication data."""
|
|
36
|
+
self.config_dir.mkdir(exist_ok=True)
|
|
37
|
+
self.auth_file.write_text(json.dumps(auth, indent=2))
|
|
38
|
+
|
|
39
|
+
def is_authenticated(self) -> bool:
|
|
40
|
+
"""Check if authenticated."""
|
|
41
|
+
if os.getenv("HANZO_API_KEY"):
|
|
42
|
+
return True
|
|
43
|
+
auth = self.load_auth()
|
|
44
|
+
return bool(auth.get("api_key") or auth.get("logged_in"))
|
|
45
|
+
|
|
46
|
+
def get_api_key(self) -> Optional[str]:
|
|
47
|
+
"""Get API key."""
|
|
48
|
+
if os.getenv("HANZO_API_KEY"):
|
|
49
|
+
return os.getenv("HANZO_API_KEY")
|
|
50
|
+
auth = self.load_auth()
|
|
51
|
+
return auth.get("api_key")
|
|
52
|
+
|
|
53
|
+
|
|
12
54
|
@click.group(name="auth")
|
|
13
55
|
def auth_group():
|
|
14
56
|
"""Manage Hanzo authentication."""
|
|
@@ -21,308 +63,206 @@ def auth_group():
|
|
|
21
63
|
@click.option("--api-key", "-k", help="API key for direct authentication")
|
|
22
64
|
@click.option("--sso", is_flag=True, help="Use SSO authentication")
|
|
23
65
|
@click.pass_context
|
|
24
|
-
|
|
66
|
+
def login(ctx, email: str, password: str, api_key: str, sso: bool):
|
|
25
67
|
"""Login to Hanzo AI."""
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
console.print("
|
|
68
|
+
auth_mgr = AuthManager()
|
|
69
|
+
|
|
70
|
+
# Check if already authenticated
|
|
71
|
+
if auth_mgr.is_authenticated():
|
|
72
|
+
console.print("[yellow]Already authenticated[/yellow]")
|
|
73
|
+
auth = auth_mgr.load_auth()
|
|
74
|
+
if auth.get("email"):
|
|
75
|
+
console.print(f"Logged in as: {auth['email']}")
|
|
31
76
|
return
|
|
32
|
-
|
|
33
|
-
auth = HanzoAuth()
|
|
34
|
-
|
|
77
|
+
|
|
35
78
|
try:
|
|
36
79
|
if api_key:
|
|
37
80
|
# Direct API key authentication
|
|
38
81
|
console.print("Authenticating with API key...")
|
|
39
|
-
|
|
82
|
+
auth = {
|
|
83
|
+
"api_key": api_key,
|
|
84
|
+
"logged_in": True,
|
|
85
|
+
"last_login": datetime.now().isoformat()
|
|
86
|
+
}
|
|
87
|
+
auth_mgr.save_auth(auth)
|
|
88
|
+
console.print("[green]✓[/green] Successfully authenticated with API key")
|
|
89
|
+
|
|
40
90
|
elif sso:
|
|
41
91
|
# SSO authentication via browser
|
|
42
92
|
console.print("Opening browser for SSO login...")
|
|
43
93
|
console.print("If browser doesn't open, visit: https://iam.hanzo.ai/login")
|
|
44
|
-
|
|
94
|
+
|
|
95
|
+
# Try using hanzoai if available
|
|
96
|
+
try:
|
|
97
|
+
from hanzoai.auth import HanzoAuth
|
|
98
|
+
hanzo_auth = HanzoAuth()
|
|
99
|
+
# SSO not implemented yet
|
|
100
|
+
console.print("[yellow]SSO authentication not yet implemented[/yellow]")
|
|
101
|
+
return
|
|
102
|
+
|
|
103
|
+
auth = {
|
|
104
|
+
"email": result.get("email"),
|
|
105
|
+
"logged_in": True,
|
|
106
|
+
"last_login": datetime.now().isoformat()
|
|
107
|
+
}
|
|
108
|
+
auth_mgr.save_auth(auth)
|
|
109
|
+
console.print(f"[green]✓[/green] Logged in as {result.get('email')}")
|
|
110
|
+
except ImportError:
|
|
111
|
+
console.print("[yellow]SSO requires hanzoai package[/yellow]")
|
|
112
|
+
console.print("Install with: pip install hanzoai")
|
|
113
|
+
|
|
45
114
|
else:
|
|
46
115
|
# Email/password authentication
|
|
47
116
|
if not email:
|
|
48
117
|
email = Prompt.ask("Email")
|
|
49
118
|
if not password:
|
|
50
119
|
password = Prompt.ask("Password", password=True)
|
|
51
|
-
|
|
120
|
+
|
|
52
121
|
console.print("Authenticating...")
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
122
|
+
|
|
123
|
+
# Try using hanzoai if available
|
|
124
|
+
try:
|
|
125
|
+
from hanzoai.auth import HanzoAuth
|
|
126
|
+
hanzo_auth = HanzoAuth()
|
|
127
|
+
# Email auth not implemented yet
|
|
128
|
+
console.print("[yellow]Email authentication not yet implemented[/yellow]")
|
|
129
|
+
console.print("[dim]Saving credentials locally for development[/dim]")
|
|
130
|
+
|
|
131
|
+
auth = {
|
|
132
|
+
"email": email,
|
|
133
|
+
"logged_in": True,
|
|
134
|
+
"last_login": datetime.now().isoformat()
|
|
135
|
+
}
|
|
136
|
+
auth_mgr.save_auth(auth)
|
|
137
|
+
console.print(f"[green]✓[/green] Logged in as {email}")
|
|
138
|
+
except ImportError:
|
|
139
|
+
# Fallback to saving credentials locally
|
|
140
|
+
auth = {
|
|
141
|
+
"email": email,
|
|
142
|
+
"logged_in": True,
|
|
143
|
+
"last_login": datetime.now().isoformat()
|
|
144
|
+
}
|
|
145
|
+
auth_mgr.save_auth(auth)
|
|
146
|
+
console.print(f"[green]✓[/green] Credentials saved for {email}")
|
|
147
|
+
|
|
78
148
|
except Exception as e:
|
|
79
149
|
console.print(f"[red]Login failed: {e}[/red]")
|
|
80
150
|
|
|
81
151
|
|
|
82
152
|
@auth_group.command()
|
|
83
153
|
@click.pass_context
|
|
84
|
-
|
|
154
|
+
def logout(ctx):
|
|
85
155
|
"""Logout from Hanzo AI."""
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
console.print("[
|
|
156
|
+
auth_mgr = AuthManager()
|
|
157
|
+
|
|
158
|
+
if not auth_mgr.is_authenticated():
|
|
159
|
+
console.print("[yellow]Not logged in[/yellow]")
|
|
90
160
|
return
|
|
91
|
-
|
|
92
|
-
auth = HanzoAuth()
|
|
93
|
-
|
|
161
|
+
|
|
94
162
|
try:
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
163
|
+
# Try using hanzoai if available
|
|
164
|
+
try:
|
|
165
|
+
from hanzoai.auth import HanzoAuth
|
|
166
|
+
hanzo_auth = HanzoAuth()
|
|
167
|
+
# Logout not implemented yet
|
|
168
|
+
pass
|
|
169
|
+
except ImportError:
|
|
170
|
+
pass # hanzoai not installed, just clear local auth
|
|
171
|
+
|
|
172
|
+
# Clear local auth
|
|
173
|
+
auth_mgr.save_auth({})
|
|
174
|
+
|
|
102
175
|
console.print("[green]✓[/green] Logged out successfully")
|
|
103
|
-
|
|
176
|
+
|
|
104
177
|
except Exception as e:
|
|
105
178
|
console.print(f"[red]Logout failed: {e}[/red]")
|
|
106
179
|
|
|
107
180
|
|
|
108
181
|
@auth_group.command()
|
|
109
182
|
@click.pass_context
|
|
110
|
-
|
|
183
|
+
def status(ctx):
|
|
111
184
|
"""Show authentication status."""
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if usage := user_info.get("usage"):
|
|
140
|
-
console.print("\n[cyan]Usage:[/cyan]")
|
|
141
|
-
console.print(f" API calls: {usage.get('api_calls', 0)}")
|
|
142
|
-
console.print(f" Tokens: {usage.get('tokens', 0)}")
|
|
143
|
-
if quota := usage.get("quota"):
|
|
144
|
-
console.print(
|
|
145
|
-
f" Quota: {usage.get('tokens', 0)} / {quota}"
|
|
146
|
-
)
|
|
147
|
-
except Exception:
|
|
148
|
-
console.print("[yellow]![/yellow] Credentials may be expired")
|
|
149
|
-
console.print("Run 'hanzo auth login' to refresh")
|
|
150
|
-
|
|
151
|
-
except Exception as e:
|
|
152
|
-
console.print(f"[red]Error reading credentials: {e}[/red]")
|
|
185
|
+
auth_mgr = AuthManager()
|
|
186
|
+
|
|
187
|
+
# Create status table
|
|
188
|
+
table = Table(title="Authentication Status", box=box.ROUNDED)
|
|
189
|
+
table.add_column("Property", style="cyan")
|
|
190
|
+
table.add_column("Value", style="white")
|
|
191
|
+
|
|
192
|
+
if auth_mgr.is_authenticated():
|
|
193
|
+
auth = auth_mgr.load_auth()
|
|
194
|
+
|
|
195
|
+
table.add_row("Status", "✅ Authenticated")
|
|
196
|
+
|
|
197
|
+
# Show auth method
|
|
198
|
+
if os.getenv("HANZO_API_KEY"):
|
|
199
|
+
table.add_row("Method", "Environment Variable")
|
|
200
|
+
api_key = os.getenv("HANZO_API_KEY")
|
|
201
|
+
table.add_row("API Key", f"{api_key[:8]}...{api_key[-4:]}")
|
|
202
|
+
elif auth.get("api_key"):
|
|
203
|
+
table.add_row("Method", "API Key")
|
|
204
|
+
table.add_row("API Key", f"{auth['api_key'][:8]}...")
|
|
205
|
+
elif auth.get("email"):
|
|
206
|
+
table.add_row("Method", "Email/Password")
|
|
207
|
+
table.add_row("Email", auth['email'])
|
|
208
|
+
|
|
209
|
+
if auth.get("last_login"):
|
|
210
|
+
table.add_row("Last Login", auth['last_login'])
|
|
211
|
+
|
|
153
212
|
else:
|
|
154
|
-
|
|
155
|
-
|
|
213
|
+
table.add_row("Status", "❌ Not authenticated")
|
|
214
|
+
table.add_row("Action", "Run 'hanzo auth login' to authenticate")
|
|
215
|
+
|
|
216
|
+
console.print(table)
|
|
156
217
|
|
|
157
218
|
|
|
158
219
|
@auth_group.command()
|
|
159
|
-
|
|
160
|
-
@click.option(
|
|
161
|
-
"--permissions", "-p", multiple=True, help="Permissions (e.g., read, write, admin)"
|
|
162
|
-
)
|
|
163
|
-
@click.option("--expires", "-e", help="Expiration (e.g., 30d, 1y, never)")
|
|
164
|
-
@click.pass_context
|
|
165
|
-
async def create_key(ctx, name: str, permissions: tuple, expires: str):
|
|
166
|
-
"""Create a new API key."""
|
|
167
|
-
try:
|
|
168
|
-
from hanzoai.auth import HanzoAuth
|
|
169
|
-
except ImportError:
|
|
170
|
-
console.print("[red]Error:[/red] hanzoai not installed")
|
|
171
|
-
return
|
|
172
|
-
|
|
173
|
-
auth = HanzoAuth()
|
|
174
|
-
|
|
175
|
-
# Ensure authenticated
|
|
176
|
-
if not await auth.is_authenticated():
|
|
177
|
-
console.print("[red]Error:[/red] Not authenticated")
|
|
178
|
-
console.print("Run 'hanzo auth login' first")
|
|
179
|
-
return
|
|
180
|
-
|
|
181
|
-
with console.status(f"Creating API key '{name}'..."):
|
|
182
|
-
try:
|
|
183
|
-
result = await auth.create_api_key(
|
|
184
|
-
name=name,
|
|
185
|
-
permissions=list(permissions) if permissions else None,
|
|
186
|
-
expires=expires,
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
key = result.get("key")
|
|
190
|
-
console.print(f"[green]✓[/green] Created API key: {name}")
|
|
191
|
-
console.print(
|
|
192
|
-
f"\n[yellow]Save this key - it won't be shown again:[/yellow]"
|
|
193
|
-
)
|
|
194
|
-
console.print(f"{key}")
|
|
195
|
-
console.print(f"\nTo use:")
|
|
196
|
-
console.print(f"export HANZO_API_KEY={key}")
|
|
197
|
-
|
|
198
|
-
except Exception as e:
|
|
199
|
-
console.print(f"[red]Failed to create key: {e}[/red]")
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
@auth_group.command()
|
|
203
|
-
@click.pass_context
|
|
204
|
-
async def list_keys(ctx):
|
|
205
|
-
"""List your API keys."""
|
|
206
|
-
try:
|
|
207
|
-
from hanzoai.auth import HanzoAuth
|
|
208
|
-
except ImportError:
|
|
209
|
-
console.print("[red]Error:[/red] hanzoai not installed")
|
|
210
|
-
return
|
|
211
|
-
|
|
212
|
-
auth = HanzoAuth()
|
|
213
|
-
|
|
214
|
-
# Ensure authenticated
|
|
215
|
-
if not await auth.is_authenticated():
|
|
216
|
-
console.print("[red]Error:[/red] Not authenticated")
|
|
217
|
-
console.print("Run 'hanzo auth login' first")
|
|
218
|
-
return
|
|
219
|
-
|
|
220
|
-
with console.status("Loading API keys..."):
|
|
221
|
-
try:
|
|
222
|
-
keys = await auth.list_api_keys()
|
|
223
|
-
|
|
224
|
-
if keys:
|
|
225
|
-
from rich.table import Table
|
|
226
|
-
|
|
227
|
-
table = Table(title="API Keys")
|
|
228
|
-
table.add_column("Name", style="cyan")
|
|
229
|
-
table.add_column("Created", style="green")
|
|
230
|
-
table.add_column("Last Used", style="yellow")
|
|
231
|
-
table.add_column("Permissions", style="blue")
|
|
232
|
-
table.add_column("Status", style="magenta")
|
|
233
|
-
|
|
234
|
-
for key in keys:
|
|
235
|
-
table.add_row(
|
|
236
|
-
key.get("name", "unknown"),
|
|
237
|
-
key.get("created_at", "unknown"),
|
|
238
|
-
key.get("last_used", "never"),
|
|
239
|
-
", ".join(key.get("permissions", [])),
|
|
240
|
-
key.get("status", "active"),
|
|
241
|
-
)
|
|
242
|
-
|
|
243
|
-
console.print(table)
|
|
244
|
-
else:
|
|
245
|
-
console.print("[yellow]No API keys found[/yellow]")
|
|
246
|
-
console.print("Create one with: hanzo auth create-key")
|
|
247
|
-
|
|
248
|
-
except Exception as e:
|
|
249
|
-
console.print(f"[red]Failed to list keys: {e}[/red]")
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
@auth_group.command()
|
|
253
|
-
@click.argument("name")
|
|
254
|
-
@click.pass_context
|
|
255
|
-
async def revoke_key(ctx, name: str):
|
|
256
|
-
"""Revoke an API key."""
|
|
257
|
-
try:
|
|
258
|
-
from hanzoai.auth import HanzoAuth
|
|
259
|
-
except ImportError:
|
|
260
|
-
console.print("[red]Error:[/red] hanzoai not installed")
|
|
261
|
-
return
|
|
262
|
-
|
|
263
|
-
auth = HanzoAuth()
|
|
264
|
-
|
|
265
|
-
# Ensure authenticated
|
|
266
|
-
if not await auth.is_authenticated():
|
|
267
|
-
console.print("[red]Error:[/red] Not authenticated")
|
|
268
|
-
console.print("Run 'hanzo auth login' first")
|
|
269
|
-
return
|
|
270
|
-
|
|
271
|
-
if click.confirm(f"Revoke API key '{name}'?"):
|
|
272
|
-
with console.status(f"Revoking key '{name}'..."):
|
|
273
|
-
try:
|
|
274
|
-
await auth.revoke_api_key(name)
|
|
275
|
-
console.print(f"[green]✓[/green] Revoked API key: {name}")
|
|
276
|
-
except Exception as e:
|
|
277
|
-
console.print(f"[red]Failed to revoke key: {e}[/red]")
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
@auth_group.command()
|
|
281
|
-
@click.pass_context
|
|
282
|
-
async def whoami(ctx):
|
|
220
|
+
def whoami():
|
|
283
221
|
"""Show current user information."""
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
console.print("[
|
|
222
|
+
auth_mgr = AuthManager()
|
|
223
|
+
|
|
224
|
+
if not auth_mgr.is_authenticated():
|
|
225
|
+
console.print("[yellow]Not logged in[/yellow]")
|
|
226
|
+
console.print("[dim]Run 'hanzo auth login' to authenticate[/dim]")
|
|
288
227
|
return
|
|
289
|
-
|
|
290
|
-
auth =
|
|
291
|
-
|
|
292
|
-
#
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
228
|
+
|
|
229
|
+
auth = auth_mgr.load_auth()
|
|
230
|
+
|
|
231
|
+
# Create user info panel
|
|
232
|
+
lines = []
|
|
233
|
+
|
|
234
|
+
if auth.get("email"):
|
|
235
|
+
lines.append(f"[cyan]Email:[/cyan] {auth['email']}")
|
|
236
|
+
|
|
237
|
+
if os.getenv("HANZO_API_KEY"):
|
|
238
|
+
lines.append("[cyan]API Key:[/cyan] Set via environment")
|
|
239
|
+
elif auth.get("api_key"):
|
|
240
|
+
lines.append(f"[cyan]API Key:[/cyan] {auth['api_key'][:8]}...")
|
|
241
|
+
|
|
242
|
+
if auth.get("last_login"):
|
|
243
|
+
lines.append(f"[cyan]Last Login:[/cyan] {auth['last_login']}")
|
|
244
|
+
|
|
245
|
+
content = "\n".join(lines) if lines else "[dim]No user information available[/dim]"
|
|
246
|
+
|
|
247
|
+
console.print(Panel(
|
|
248
|
+
content,
|
|
249
|
+
title="[bold cyan]User Information[/bold cyan]",
|
|
250
|
+
box=box.ROUNDED
|
|
251
|
+
))
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
@auth_group.command(name="set-key")
|
|
255
|
+
@click.argument("api_key")
|
|
256
|
+
def set_key(api_key: str):
|
|
257
|
+
"""Set API key for authentication."""
|
|
258
|
+
auth_mgr = AuthManager()
|
|
259
|
+
|
|
260
|
+
auth = auth_mgr.load_auth()
|
|
261
|
+
auth["api_key"] = api_key
|
|
262
|
+
auth["logged_in"] = True
|
|
263
|
+
auth["last_login"] = datetime.now().isoformat()
|
|
264
|
+
|
|
265
|
+
auth_mgr.save_auth(auth)
|
|
266
|
+
|
|
267
|
+
console.print("[green]✓[/green] API key saved successfully")
|
|
268
|
+
console.print("[dim]You can now use Hanzo Cloud services[/dim]")
|