hanzo 0.3.24__py3-none-any.whl → 0.3.26__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.

@@ -0,0 +1,377 @@
1
+ """Authentication commands."""
2
+
3
+ import os
4
+ import json
5
+ from pathlib import Path
6
+ from datetime import datetime
7
+ from typing import Optional
8
+
9
+ import click
10
+ from rich.prompt import Prompt
11
+ from rich.panel import Panel
12
+ from rich.table import Table
13
+ from rich import box
14
+
15
+ from ..utils.output import console
16
+
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
+
54
+ @click.group(name="auth")
55
+ def auth_group():
56
+ """Manage Hanzo authentication."""
57
+ pass
58
+
59
+
60
+ @auth_group.command()
61
+ @click.option("--email", "-e", help="Email address")
62
+ @click.option("--password", "-p", help="Password (not recommended, use prompt)")
63
+ @click.option("--api-key", "-k", help="API key for direct authentication")
64
+ @click.option("--sso", is_flag=True, help="Use SSO authentication")
65
+ @click.pass_context
66
+ async def login(ctx, email: str, password: str, api_key: str, sso: bool):
67
+ """Login to Hanzo AI."""
68
+ try:
69
+ from hanzoai.auth import HanzoAuth
70
+ except ImportError:
71
+ console.print("[red]Error:[/red] hanzoai not installed")
72
+ console.print("Install with: pip install hanzo")
73
+ return
74
+
75
+ auth = HanzoAuth()
76
+
77
+ try:
78
+ if api_key:
79
+ # Direct API key authentication
80
+ console.print("Authenticating with API key...")
81
+ result = await auth.login_with_api_key(api_key)
82
+ elif sso:
83
+ # SSO authentication via browser
84
+ console.print("Opening browser for SSO login...")
85
+ console.print("If browser doesn't open, visit: https://iam.hanzo.ai/login")
86
+ result = await auth.login_with_sso()
87
+ else:
88
+ # Email/password authentication
89
+ if not email:
90
+ email = Prompt.ask("Email")
91
+ if not password:
92
+ password = Prompt.ask("Password", password=True)
93
+
94
+ console.print("Authenticating...")
95
+ result = await auth.login(email, password)
96
+
97
+ # Save credentials
98
+ config_dir = Path.home() / ".hanzo"
99
+ config_dir.mkdir(exist_ok=True)
100
+
101
+ config_file = config_dir / "auth.json"
102
+ await auth.save_credentials(config_file)
103
+
104
+ # Also set environment variable if API key is available
105
+ if api_key or result.get("api_key"):
106
+ key = api_key or result.get("api_key")
107
+ console.print(f"\n[dim]To use in environment:[/dim]")
108
+ console.print(f"export HANZO_API_KEY={key}")
109
+
110
+ console.print(f"[green]✓[/green] Logged in as {result.get('email', 'user')}")
111
+
112
+ # Check organization
113
+ if org := result.get("organization"):
114
+ console.print(f" Organization: {org}")
115
+
116
+ # Check permissions
117
+ if permissions := result.get("permissions"):
118
+ console.print(f" Permissions: {', '.join(permissions)}")
119
+
120
+ except Exception as e:
121
+ console.print(f"[red]Login failed: {e}[/red]")
122
+
123
+
124
+ @auth_group.command()
125
+ @click.pass_context
126
+ async def logout(ctx):
127
+ """Logout from Hanzo AI."""
128
+ auth_mgr = AuthManager()
129
+
130
+ if not auth_mgr.is_authenticated():
131
+ console.print("[yellow]Not logged in[/yellow]")
132
+ return
133
+
134
+ try:
135
+ # Try using hanzoai if available
136
+ try:
137
+ from hanzoai.auth import HanzoAuth
138
+ hanzo_auth = HanzoAuth()
139
+ await hanzo_auth.logout()
140
+ except ImportError:
141
+ pass # hanzoai not installed, just clear local auth
142
+
143
+ # Clear local auth
144
+ auth_mgr.save_auth({})
145
+
146
+ console.print("[green]✓[/green] Logged out successfully")
147
+
148
+ except Exception as e:
149
+ console.print(f"[red]Logout failed: {e}[/red]")
150
+
151
+
152
+ @auth_group.command()
153
+ @click.pass_context
154
+ async def status(ctx):
155
+ """Show authentication status."""
156
+ auth_mgr = AuthManager()
157
+
158
+ # Create status table
159
+ table = Table(title="Authentication Status", box=box.ROUNDED)
160
+ table.add_column("Property", style="cyan")
161
+ table.add_column("Value", style="white")
162
+
163
+ if auth_mgr.is_authenticated():
164
+ auth = auth_mgr.load_auth()
165
+
166
+ table.add_row("Status", "✅ Authenticated")
167
+
168
+ # Show auth method
169
+ if os.getenv("HANZO_API_KEY"):
170
+ table.add_row("Method", "Environment Variable")
171
+ api_key = os.getenv("HANZO_API_KEY")
172
+ table.add_row("API Key", f"{api_key[:8]}...{api_key[-4:]}")
173
+ elif auth.get("api_key"):
174
+ table.add_row("Method", "API Key")
175
+ table.add_row("API Key", f"{auth['api_key'][:8]}...")
176
+ elif auth.get("email"):
177
+ console.print(f" Email: {email}")
178
+ if org := creds.get("organization"):
179
+ console.print(f" Organization: {org}")
180
+
181
+ # Verify credentials are still valid
182
+ with console.status("Verifying credentials..."):
183
+ try:
184
+ user_info = await auth.get_user_info()
185
+ console.print("[green]✓[/green] Credentials are valid")
186
+
187
+ # Show usage stats if available
188
+ if usage := user_info.get("usage"):
189
+ console.print("\n[cyan]Usage:[/cyan]")
190
+ console.print(f" API calls: {usage.get('api_calls', 0)}")
191
+ console.print(f" Tokens: {usage.get('tokens', 0)}")
192
+ if quota := usage.get("quota"):
193
+ console.print(
194
+ f" Quota: {usage.get('tokens', 0)} / {quota}"
195
+ )
196
+ except Exception:
197
+ console.print("[yellow]![/yellow] Credentials may be expired")
198
+ console.print("Run 'hanzo auth login' to refresh")
199
+
200
+ except Exception as e:
201
+ console.print(f"[red]Error reading credentials: {e}[/red]")
202
+ else:
203
+ console.print("[yellow]![/yellow] Not logged in")
204
+ console.print("Run 'hanzo auth login' to authenticate")
205
+
206
+
207
+ @auth_group.command()
208
+ @click.option("--name", "-n", required=True, help="API key name")
209
+ @click.option(
210
+ "--permissions", "-p", multiple=True, help="Permissions (e.g., read, write, admin)"
211
+ )
212
+ @click.option("--expires", "-e", help="Expiration (e.g., 30d, 1y, never)")
213
+ @click.pass_context
214
+ async def create_key(ctx, name: str, permissions: tuple, expires: str):
215
+ """Create a new API key."""
216
+ try:
217
+ from hanzoai.auth import HanzoAuth
218
+ except ImportError:
219
+ console.print("[red]Error:[/red] hanzoai not installed")
220
+ return
221
+
222
+ auth = HanzoAuth()
223
+
224
+ # Ensure authenticated
225
+ if not await auth.is_authenticated():
226
+ console.print("[red]Error:[/red] Not authenticated")
227
+ console.print("Run 'hanzo auth login' first")
228
+ return
229
+
230
+ with console.status(f"Creating API key '{name}'..."):
231
+ try:
232
+ result = await auth.create_api_key(
233
+ name=name,
234
+ permissions=list(permissions) if permissions else None,
235
+ expires=expires,
236
+ )
237
+
238
+ key = result.get("key")
239
+ console.print(f"[green]✓[/green] Created API key: {name}")
240
+ console.print(
241
+ f"\n[yellow]Save this key - it won't be shown again:[/yellow]"
242
+ )
243
+ console.print(f"{key}")
244
+ console.print(f"\nTo use:")
245
+ console.print(f"export HANZO_API_KEY={key}")
246
+
247
+ except Exception as e:
248
+ console.print(f"[red]Failed to create key: {e}[/red]")
249
+
250
+
251
+ @auth_group.command()
252
+ @click.pass_context
253
+ async def list_keys(ctx):
254
+ """List your API keys."""
255
+ try:
256
+ from hanzoai.auth import HanzoAuth
257
+ except ImportError:
258
+ console.print("[red]Error:[/red] hanzoai not installed")
259
+ return
260
+
261
+ auth = HanzoAuth()
262
+
263
+ # Ensure authenticated
264
+ if not await auth.is_authenticated():
265
+ console.print("[red]Error:[/red] Not authenticated")
266
+ console.print("Run 'hanzo auth login' first")
267
+ return
268
+
269
+ with console.status("Loading API keys..."):
270
+ try:
271
+ keys = await auth.list_api_keys()
272
+
273
+ if keys:
274
+ from rich.table import Table
275
+
276
+ table = Table(title="API Keys")
277
+ table.add_column("Name", style="cyan")
278
+ table.add_column("Created", style="green")
279
+ table.add_column("Last Used", style="yellow")
280
+ table.add_column("Permissions", style="blue")
281
+ table.add_column("Status", style="magenta")
282
+
283
+ for key in keys:
284
+ table.add_row(
285
+ key.get("name", "unknown"),
286
+ key.get("created_at", "unknown"),
287
+ key.get("last_used", "never"),
288
+ ", ".join(key.get("permissions", [])),
289
+ key.get("status", "active"),
290
+ )
291
+
292
+ console.print(table)
293
+ else:
294
+ console.print("[yellow]No API keys found[/yellow]")
295
+ console.print("Create one with: hanzo auth create-key")
296
+
297
+ except Exception as e:
298
+ console.print(f"[red]Failed to list keys: {e}[/red]")
299
+
300
+
301
+ @auth_group.command()
302
+ @click.argument("name")
303
+ @click.pass_context
304
+ async def revoke_key(ctx, name: str):
305
+ """Revoke an API key."""
306
+ try:
307
+ from hanzoai.auth import HanzoAuth
308
+ except ImportError:
309
+ console.print("[red]Error:[/red] hanzoai not installed")
310
+ return
311
+
312
+ auth = HanzoAuth()
313
+
314
+ # Ensure authenticated
315
+ if not await auth.is_authenticated():
316
+ console.print("[red]Error:[/red] Not authenticated")
317
+ console.print("Run 'hanzo auth login' first")
318
+ return
319
+
320
+ if click.confirm(f"Revoke API key '{name}'?"):
321
+ with console.status(f"Revoking key '{name}'..."):
322
+ try:
323
+ await auth.revoke_api_key(name)
324
+ console.print(f"[green]✓[/green] Revoked API key: {name}")
325
+ except Exception as e:
326
+ console.print(f"[red]Failed to revoke key: {e}[/red]")
327
+
328
+
329
+ @auth_group.command()
330
+ @click.pass_context
331
+ async def whoami(ctx):
332
+ """Show current user information."""
333
+ try:
334
+ from hanzoai.auth import HanzoAuth
335
+ except ImportError:
336
+ console.print("[red]Error:[/red] hanzoai not installed")
337
+ return
338
+
339
+ auth = HanzoAuth()
340
+
341
+ # Check if authenticated
342
+ if not await auth.is_authenticated():
343
+ console.print("[yellow]Not authenticated[/yellow]")
344
+
345
+ # Check if API key is set
346
+ if os.environ.get("HANZO_API_KEY"):
347
+ console.print("HANZO_API_KEY is set but may be invalid")
348
+ else:
349
+ console.print("Run 'hanzo auth login' to authenticate")
350
+ return
351
+
352
+ with console.status("Loading user information..."):
353
+ try:
354
+ user = await auth.get_user_info()
355
+
356
+ console.print(f"[cyan]User Information:[/cyan]")
357
+ console.print(f" ID: {user.get('id', 'unknown')}")
358
+ console.print(f" Email: {user.get('email', 'unknown')}")
359
+ console.print(f" Name: {user.get('name', 'unknown')}")
360
+
361
+ if org := user.get("organization"):
362
+ console.print(f"\n[cyan]Organization:[/cyan]")
363
+ console.print(f" Name: {org.get('name', 'unknown')}")
364
+ console.print(f" Role: {org.get('role', 'member')}")
365
+
366
+ if teams := user.get("teams"):
367
+ console.print(f"\n[cyan]Teams:[/cyan]")
368
+ for team in teams:
369
+ console.print(f" • {team}")
370
+
371
+ if perms := user.get("permissions"):
372
+ console.print(f"\n[cyan]Permissions:[/cyan]")
373
+ for perm in sorted(perms):
374
+ console.print(f" • {perm}")
375
+
376
+ except Exception as e:
377
+ console.print(f"[red]Error: {e}[/red]")
hanzo/commands/chat.py CHANGED
@@ -1,11 +1,14 @@
1
1
  """Chat command for interactive AI conversations."""
2
2
 
3
3
  import asyncio
4
+ import os
4
5
  from typing import Optional
5
6
 
6
7
  import click
7
8
  import httpx
8
9
  from rich.markdown import Markdown
10
+ from rich.panel import Panel
11
+ from rich import box
9
12
 
10
13
  from ..utils.output import console
11
14