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 CHANGED
@@ -1,6 +1,6 @@
1
1
  """Hanzo - Complete AI Infrastructure Platform with CLI, Router, MCP, and Agent Runtime."""
2
2
 
3
- __version__ = "0.3.2"
4
- __all__ = ["main", "cli"]
3
+ __version__ = "0.3.25"
4
+ __all__ = ["main", "cli", "__version__"]
5
5
 
6
6
  from .cli import cli, main
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.22"
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
- repl = HanzoREPL(console=console)
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
- async def login(ctx, email: str, password: str, api_key: str, sso: bool):
66
+ def login(ctx, email: str, password: str, api_key: str, sso: bool):
25
67
  """Login to Hanzo AI."""
26
- try:
27
- from hanzoai.auth import HanzoAuth
28
- except ImportError:
29
- console.print("[red]Error:[/red] hanzoai not installed")
30
- console.print("Install with: pip install hanzo")
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
- result = await auth.login_with_api_key(api_key)
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
- result = await auth.login_with_sso()
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
- result = await auth.login(email, password)
54
-
55
- # Save credentials
56
- config_dir = Path.home() / ".hanzo"
57
- config_dir.mkdir(exist_ok=True)
58
-
59
- config_file = config_dir / "auth.json"
60
- await auth.save_credentials(config_file)
61
-
62
- # Also set environment variable if API key is available
63
- if api_key or result.get("api_key"):
64
- key = api_key or result.get("api_key")
65
- console.print(f"\n[dim]To use in environment:[/dim]")
66
- console.print(f"export HANZO_API_KEY={key}")
67
-
68
- console.print(f"[green]✓[/green] Logged in as {result.get('email', 'user')}")
69
-
70
- # Check organization
71
- if org := result.get("organization"):
72
- console.print(f" Organization: {org}")
73
-
74
- # Check permissions
75
- if permissions := result.get("permissions"):
76
- console.print(f" Permissions: {', '.join(permissions)}")
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
- async def logout(ctx):
154
+ def logout(ctx):
85
155
  """Logout from Hanzo AI."""
86
- try:
87
- from hanzoai.auth import HanzoAuth
88
- except ImportError:
89
- console.print("[red]Error:[/red] hanzoai not installed")
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
- await auth.logout()
96
-
97
- # Remove saved credentials
98
- config_file = Path.home() / ".hanzo" / "auth.json"
99
- if config_file.exists():
100
- config_file.unlink()
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
- async def status(ctx):
183
+ def status(ctx):
111
184
  """Show authentication status."""
112
- # Check environment variable
113
- if api_key := os.environ.get("HANZO_API_KEY"):
114
- console.print("[green]✓[/green] HANZO_API_KEY environment variable set")
115
- console.print(f" Key: {api_key[:8]}...{api_key[-4:]}")
116
-
117
- # Check saved credentials
118
- config_file = Path.home() / ".hanzo" / "auth.json"
119
- if config_file.exists():
120
- try:
121
- from hanzoai.auth import HanzoAuth
122
-
123
- auth = HanzoAuth()
124
- creds = await auth.load_credentials(config_file)
125
-
126
- console.print("[green]✓[/green] Saved credentials found")
127
- if email := creds.get("email"):
128
- console.print(f" Email: {email}")
129
- if org := creds.get("organization"):
130
- console.print(f" Organization: {org}")
131
-
132
- # Verify credentials are still valid
133
- with console.status("Verifying credentials..."):
134
- try:
135
- user_info = await auth.get_user_info()
136
- console.print("[green]✓[/green] Credentials are valid")
137
-
138
- # Show usage stats if available
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
- console.print("[yellow]![/yellow] Not logged in")
155
- console.print("Run 'hanzo auth login' to authenticate")
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
- @click.option("--name", "-n", required=True, help="API key name")
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
- try:
285
- from hanzoai.auth import HanzoAuth
286
- except ImportError:
287
- console.print("[red]Error:[/red] hanzoai not installed")
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 = HanzoAuth()
291
-
292
- # Check if authenticated
293
- if not await auth.is_authenticated():
294
- console.print("[yellow]Not authenticated[/yellow]")
295
-
296
- # Check if API key is set
297
- if os.environ.get("HANZO_API_KEY"):
298
- console.print("HANZO_API_KEY is set but may be invalid")
299
- else:
300
- console.print("Run 'hanzo auth login' to authenticate")
301
- return
302
-
303
- with console.status("Loading user information..."):
304
- try:
305
- user = await auth.get_user_info()
306
-
307
- console.print(f"[cyan]User Information:[/cyan]")
308
- console.print(f" ID: {user.get('id', 'unknown')}")
309
- console.print(f" Email: {user.get('email', 'unknown')}")
310
- console.print(f" Name: {user.get('name', 'unknown')}")
311
-
312
- if org := user.get("organization"):
313
- console.print(f"\n[cyan]Organization:[/cyan]")
314
- console.print(f" Name: {org.get('name', 'unknown')}")
315
- console.print(f" Role: {org.get('role', 'member')}")
316
-
317
- if teams := user.get("teams"):
318
- console.print(f"\n[cyan]Teams:[/cyan]")
319
- for team in teams:
320
- console.print(f" • {team}")
321
-
322
- if perms := user.get("permissions"):
323
- console.print(f"\n[cyan]Permissions:[/cyan]")
324
- for perm in sorted(perms):
325
- console.print(f" • {perm}")
326
-
327
- except Exception as e:
328
- console.print(f"[red]Error: {e}[/red]")
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]")