hanzo 0.3.6__py3-none-any.whl → 0.3.7__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 +1 -1
- hanzo/__main__.py +1 -1
- hanzo/cli.py +86 -52
- hanzo/commands/__init__.py +12 -1
- hanzo/commands/agent.py +18 -21
- hanzo/commands/auth.py +63 -59
- hanzo/commands/chat.py +54 -35
- hanzo/commands/cluster.py +109 -78
- hanzo/commands/config.py +39 -38
- hanzo/commands/mcp.py +63 -42
- hanzo/commands/miner.py +71 -58
- hanzo/commands/network.py +64 -55
- hanzo/commands/repl.py +37 -30
- hanzo/commands/tools.py +52 -67
- hanzo/interactive/__init__.py +1 -1
- hanzo/interactive/dashboard.py +34 -44
- hanzo/interactive/repl.py +35 -32
- hanzo/mcp_server.py +7 -2
- hanzo/repl.py +13 -3
- hanzo/router/__init__.py +21 -9
- hanzo/utils/__init__.py +1 -1
- hanzo/utils/config.py +37 -35
- hanzo/utils/net_check.py +33 -29
- hanzo/utils/output.py +23 -18
- hanzo-0.3.7.dist-info/METADATA +138 -0
- hanzo-0.3.7.dist-info/RECORD +28 -0
- {hanzo-0.3.6.dist-info → hanzo-0.3.7.dist-info}/WHEEL +1 -2
- hanzo-0.3.7.dist-info/entry_points.txt +6 -0
- hanzo-0.3.6.dist-info/METADATA +0 -76
- hanzo-0.3.6.dist-info/RECORD +0 -29
- hanzo-0.3.6.dist-info/entry_points.txt +0 -2
- hanzo-0.3.6.dist-info/top_level.txt +0 -1
hanzo/commands/cluster.py
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
"""Cluster management commands."""
|
|
2
2
|
|
|
3
|
-
import asyncio
|
|
4
3
|
from typing import List, Optional
|
|
5
4
|
|
|
6
5
|
import click
|
|
7
|
-
import httpx
|
|
8
|
-
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
9
6
|
from rich.table import Table
|
|
7
|
+
from rich.progress import Progress, TextColumn, SpinnerColumn
|
|
10
8
|
|
|
11
|
-
from ..utils.output import console
|
|
9
|
+
from ..utils.output import console
|
|
12
10
|
|
|
13
11
|
|
|
14
12
|
@click.group(name="cluster")
|
|
@@ -21,14 +19,21 @@ def cluster_group():
|
|
|
21
19
|
@click.option("--name", "-n", default="hanzo-local", help="Cluster name")
|
|
22
20
|
@click.option("--port", "-p", default=8000, type=int, help="API port")
|
|
23
21
|
@click.option("--models", "-m", multiple=True, help="Models to load")
|
|
24
|
-
@click.option(
|
|
22
|
+
@click.option(
|
|
23
|
+
"--device",
|
|
24
|
+
type=click.Choice(["cpu", "gpu", "auto"]),
|
|
25
|
+
default="auto",
|
|
26
|
+
help="Device to use",
|
|
27
|
+
)
|
|
25
28
|
@click.pass_context
|
|
26
29
|
async def start(ctx, name: str, port: int, models: tuple, device: str):
|
|
27
30
|
"""Start local AI cluster."""
|
|
28
31
|
await start_cluster(ctx, name, port, list(models) if models else None, device)
|
|
29
32
|
|
|
30
33
|
|
|
31
|
-
async def start_cluster(
|
|
34
|
+
async def start_cluster(
|
|
35
|
+
ctx, name: str, port: int, models: Optional[List[str]] = None, device: str = "auto"
|
|
36
|
+
):
|
|
32
37
|
"""Start a local cluster via hanzo-cluster."""
|
|
33
38
|
try:
|
|
34
39
|
from hanzo_cluster import HanzoCluster
|
|
@@ -36,16 +41,16 @@ async def start_cluster(ctx, name: str, port: int, models: Optional[List[str]] =
|
|
|
36
41
|
console.print("[red]Error:[/red] hanzo-cluster not installed")
|
|
37
42
|
console.print("Install with: pip install hanzo[cluster]")
|
|
38
43
|
return
|
|
39
|
-
|
|
44
|
+
|
|
40
45
|
cluster = HanzoCluster(name=name, port=port, device=device)
|
|
41
|
-
|
|
46
|
+
|
|
42
47
|
with Progress(
|
|
43
48
|
SpinnerColumn(),
|
|
44
49
|
TextColumn("[progress.description]{task.description}"),
|
|
45
50
|
console=console,
|
|
46
51
|
) as progress:
|
|
47
52
|
task = progress.add_task("Starting cluster...", total=None)
|
|
48
|
-
|
|
53
|
+
|
|
49
54
|
try:
|
|
50
55
|
await cluster.start(models=models)
|
|
51
56
|
progress.update(task, completed=True)
|
|
@@ -53,10 +58,10 @@ async def start_cluster(ctx, name: str, port: int, models: Optional[List[str]] =
|
|
|
53
58
|
progress.stop()
|
|
54
59
|
console.print(f"[red]Failed to start cluster: {e}[/red]")
|
|
55
60
|
return
|
|
56
|
-
|
|
61
|
+
|
|
57
62
|
console.print(f"[green]✓[/green] Cluster started at http://localhost:{port}")
|
|
58
63
|
console.print("Press Ctrl+C to stop\n")
|
|
59
|
-
|
|
64
|
+
|
|
60
65
|
# Show cluster info
|
|
61
66
|
info = await cluster.info()
|
|
62
67
|
console.print("[cyan]Cluster Information:[/cyan]")
|
|
@@ -64,11 +69,11 @@ async def start_cluster(ctx, name: str, port: int, models: Optional[List[str]] =
|
|
|
64
69
|
console.print(f" Port: {info.get('port', port)}")
|
|
65
70
|
console.print(f" Device: {info.get('device', device)}")
|
|
66
71
|
console.print(f" Nodes: {info.get('nodes', 1)}")
|
|
67
|
-
if models := info.get(
|
|
72
|
+
if models := info.get("models", models):
|
|
68
73
|
console.print(f" Models: {', '.join(models)}")
|
|
69
|
-
|
|
74
|
+
|
|
70
75
|
console.print("\n[dim]Logs:[/dim]")
|
|
71
|
-
|
|
76
|
+
|
|
72
77
|
try:
|
|
73
78
|
# Stream logs
|
|
74
79
|
async for log in cluster.stream_logs():
|
|
@@ -89,9 +94,9 @@ async def stop(ctx, name: str):
|
|
|
89
94
|
except ImportError:
|
|
90
95
|
console.print("[red]Error:[/red] hanzo-cluster not installed")
|
|
91
96
|
return
|
|
92
|
-
|
|
97
|
+
|
|
93
98
|
cluster = HanzoCluster(name=name)
|
|
94
|
-
|
|
99
|
+
|
|
95
100
|
console.print("[yellow]Stopping cluster...[/yellow]")
|
|
96
101
|
try:
|
|
97
102
|
await cluster.stop()
|
|
@@ -110,38 +115,40 @@ async def status(ctx, name: str):
|
|
|
110
115
|
except ImportError:
|
|
111
116
|
console.print("[red]Error:[/red] hanzo-cluster not installed")
|
|
112
117
|
return
|
|
113
|
-
|
|
118
|
+
|
|
114
119
|
cluster = HanzoCluster(name=name)
|
|
115
|
-
|
|
120
|
+
|
|
116
121
|
try:
|
|
117
122
|
status = await cluster.status()
|
|
118
|
-
|
|
119
|
-
if status.get(
|
|
123
|
+
|
|
124
|
+
if status.get("running"):
|
|
120
125
|
console.print("[green]✓[/green] Cluster is running")
|
|
121
|
-
|
|
126
|
+
|
|
122
127
|
# Show cluster info
|
|
123
128
|
console.print("\n[cyan]Cluster Information:[/cyan]")
|
|
124
129
|
console.print(f" Name: {status.get('name', name)}")
|
|
125
130
|
console.print(f" Nodes: {status.get('nodes', 0)}")
|
|
126
131
|
console.print(f" Status: {status.get('state', 'unknown')}")
|
|
127
|
-
|
|
132
|
+
|
|
128
133
|
# Show models
|
|
129
|
-
if models := status.get(
|
|
134
|
+
if models := status.get("models", []):
|
|
130
135
|
console.print("\n[cyan]Available Models:[/cyan]")
|
|
131
136
|
for model in models:
|
|
132
137
|
console.print(f" • {model}")
|
|
133
|
-
|
|
138
|
+
|
|
134
139
|
# Show nodes
|
|
135
|
-
if nodes := status.get(
|
|
140
|
+
if nodes := status.get("node_details", []):
|
|
136
141
|
console.print("\n[cyan]Nodes:[/cyan]")
|
|
137
142
|
for node in nodes:
|
|
138
|
-
console.print(
|
|
139
|
-
|
|
143
|
+
console.print(
|
|
144
|
+
f" • {node.get('name', 'unknown')} ({node.get('state', 'unknown')})"
|
|
145
|
+
)
|
|
146
|
+
if device := node.get("device"):
|
|
140
147
|
console.print(f" Device: {device}")
|
|
141
148
|
else:
|
|
142
149
|
console.print("[yellow]![/yellow] Cluster is not running")
|
|
143
150
|
console.print("Start with: hanzo cluster start")
|
|
144
|
-
|
|
151
|
+
|
|
145
152
|
except Exception as e:
|
|
146
153
|
console.print(f"[red]Error checking status: {e}[/red]")
|
|
147
154
|
|
|
@@ -156,32 +163,32 @@ async def models(ctx, name: str):
|
|
|
156
163
|
except ImportError:
|
|
157
164
|
console.print("[red]Error:[/red] hanzo-cluster not installed")
|
|
158
165
|
return
|
|
159
|
-
|
|
166
|
+
|
|
160
167
|
cluster = HanzoCluster(name=name)
|
|
161
|
-
|
|
168
|
+
|
|
162
169
|
try:
|
|
163
170
|
models = await cluster.list_models()
|
|
164
|
-
|
|
171
|
+
|
|
165
172
|
if models:
|
|
166
173
|
table = Table(title="Available Models")
|
|
167
174
|
table.add_column("Model ID", style="cyan")
|
|
168
175
|
table.add_column("Type", style="green")
|
|
169
176
|
table.add_column("Status", style="yellow")
|
|
170
177
|
table.add_column("Node", style="blue")
|
|
171
|
-
|
|
178
|
+
|
|
172
179
|
for model in models:
|
|
173
180
|
table.add_row(
|
|
174
181
|
model.get("id", "unknown"),
|
|
175
182
|
model.get("type", "model"),
|
|
176
183
|
model.get("status", "unknown"),
|
|
177
|
-
model.get("node", "local")
|
|
184
|
+
model.get("node", "local"),
|
|
178
185
|
)
|
|
179
|
-
|
|
186
|
+
|
|
180
187
|
console.print(table)
|
|
181
188
|
else:
|
|
182
189
|
console.print("[yellow]No models loaded[/yellow]")
|
|
183
190
|
console.print("Load models with: hanzo cluster load <model>")
|
|
184
|
-
|
|
191
|
+
|
|
185
192
|
except Exception as e:
|
|
186
193
|
console.print(f"[red]Error: {e}[/red]")
|
|
187
194
|
|
|
@@ -198,21 +205,21 @@ async def load(ctx, model: str, name: str, node: str = None):
|
|
|
198
205
|
except ImportError:
|
|
199
206
|
console.print("[red]Error:[/red] hanzo-cluster not installed")
|
|
200
207
|
return
|
|
201
|
-
|
|
208
|
+
|
|
202
209
|
cluster = HanzoCluster(name=name)
|
|
203
|
-
|
|
210
|
+
|
|
204
211
|
with console.status(f"Loading model '{model}'..."):
|
|
205
212
|
try:
|
|
206
213
|
result = await cluster.load_model(model, node=node)
|
|
207
214
|
console.print(f"[green]✓[/green] Loaded model: {model}")
|
|
208
|
-
if node_name := result.get(
|
|
215
|
+
if node_name := result.get("node"):
|
|
209
216
|
console.print(f" Node: {node_name}")
|
|
210
217
|
except Exception as e:
|
|
211
218
|
console.print(f"[red]Failed to load model: {e}[/red]")
|
|
212
219
|
|
|
213
220
|
|
|
214
221
|
@cluster_group.command()
|
|
215
|
-
@click.argument("model")
|
|
222
|
+
@click.argument("model")
|
|
216
223
|
@click.option("--name", "-n", default="hanzo-local", help="Cluster name")
|
|
217
224
|
@click.pass_context
|
|
218
225
|
async def unload(ctx, model: str, name: str):
|
|
@@ -222,9 +229,9 @@ async def unload(ctx, model: str, name: str):
|
|
|
222
229
|
except ImportError:
|
|
223
230
|
console.print("[red]Error:[/red] hanzo-cluster not installed")
|
|
224
231
|
return
|
|
225
|
-
|
|
232
|
+
|
|
226
233
|
cluster = HanzoCluster(name=name)
|
|
227
|
-
|
|
234
|
+
|
|
228
235
|
if click.confirm(f"Unload model '{model}'?"):
|
|
229
236
|
with console.status(f"Unloading model '{model}'..."):
|
|
230
237
|
try:
|
|
@@ -243,35 +250,51 @@ def node_group():
|
|
|
243
250
|
@node_group.command(name="start")
|
|
244
251
|
@click.option("--name", "-n", default="node-1", help="Node name")
|
|
245
252
|
@click.option("--cluster", "-c", default="hanzo-local", help="Cluster to join")
|
|
246
|
-
@click.option(
|
|
247
|
-
|
|
253
|
+
@click.option(
|
|
254
|
+
"--device",
|
|
255
|
+
type=click.Choice(["cpu", "gpu", "auto"]),
|
|
256
|
+
default="auto",
|
|
257
|
+
help="Device to use",
|
|
258
|
+
)
|
|
259
|
+
@click.option(
|
|
260
|
+
"--port", "-p", type=int, help="Node port (auto-assigned if not specified)"
|
|
261
|
+
)
|
|
248
262
|
@click.option("--blockchain", is_flag=True, help="Enable blockchain features")
|
|
249
263
|
@click.option("--network", is_flag=True, help="Enable network discovery")
|
|
250
264
|
@click.pass_context
|
|
251
|
-
async def node_start(
|
|
265
|
+
async def node_start(
|
|
266
|
+
ctx,
|
|
267
|
+
name: str,
|
|
268
|
+
cluster: str,
|
|
269
|
+
device: str,
|
|
270
|
+
port: int,
|
|
271
|
+
blockchain: bool,
|
|
272
|
+
network: bool,
|
|
273
|
+
):
|
|
252
274
|
"""Start this machine as a node in the cluster."""
|
|
253
275
|
try:
|
|
254
276
|
from hanzo_cluster import HanzoNode
|
|
277
|
+
|
|
255
278
|
if blockchain or network:
|
|
256
279
|
from hanzo_network import HanzoNetwork
|
|
257
280
|
except ImportError:
|
|
258
281
|
console.print("[red]Error:[/red] Required packages not installed")
|
|
259
282
|
console.print("Install with: pip install hanzo[cluster,network]")
|
|
260
283
|
return
|
|
261
|
-
|
|
284
|
+
|
|
262
285
|
node = HanzoNode(name=name, device=device, port=port)
|
|
263
|
-
|
|
286
|
+
|
|
264
287
|
with Progress(
|
|
265
288
|
SpinnerColumn(),
|
|
266
289
|
TextColumn("[progress.description]{task.description}"),
|
|
267
290
|
console=console,
|
|
268
291
|
) as progress:
|
|
269
292
|
task = progress.add_task(f"Starting node '{name}'...", total=None)
|
|
270
|
-
|
|
293
|
+
|
|
271
294
|
try:
|
|
272
295
|
# Start the node
|
|
273
296
|
await node.start(cluster=cluster)
|
|
274
|
-
|
|
297
|
+
|
|
275
298
|
# Enable blockchain/network features if requested
|
|
276
299
|
if blockchain or network:
|
|
277
300
|
network_mgr = HanzoNetwork(node=node)
|
|
@@ -279,13 +302,13 @@ async def node_start(ctx, name: str, cluster: str, device: str, port: int, block
|
|
|
279
302
|
await network_mgr.enable_blockchain()
|
|
280
303
|
if network:
|
|
281
304
|
await network_mgr.enable_discovery()
|
|
282
|
-
|
|
305
|
+
|
|
283
306
|
progress.update(task, completed=True)
|
|
284
307
|
except Exception as e:
|
|
285
308
|
progress.stop()
|
|
286
309
|
console.print(f"[red]Failed to start node: {e}[/red]")
|
|
287
310
|
return
|
|
288
|
-
|
|
311
|
+
|
|
289
312
|
console.print(f"[green]✓[/green] Node '{name}' started")
|
|
290
313
|
console.print(f" Cluster: {cluster}")
|
|
291
314
|
console.print(f" Device: {device}")
|
|
@@ -295,10 +318,10 @@ async def node_start(ctx, name: str, cluster: str, device: str, port: int, block
|
|
|
295
318
|
console.print(" [cyan]Blockchain enabled[/cyan]")
|
|
296
319
|
if network:
|
|
297
320
|
console.print(" [cyan]Network discovery enabled[/cyan]")
|
|
298
|
-
|
|
321
|
+
|
|
299
322
|
console.print("\nPress Ctrl+C to stop\n")
|
|
300
323
|
console.print("[dim]Logs:[/dim]")
|
|
301
|
-
|
|
324
|
+
|
|
302
325
|
try:
|
|
303
326
|
# Stream logs
|
|
304
327
|
async for log in node.stream_logs():
|
|
@@ -320,7 +343,7 @@ async def node_stop(ctx, name: str, all: bool):
|
|
|
320
343
|
except ImportError:
|
|
321
344
|
console.print("[red]Error:[/red] hanzo-cluster not installed")
|
|
322
345
|
return
|
|
323
|
-
|
|
346
|
+
|
|
324
347
|
if all:
|
|
325
348
|
if click.confirm("Stop all nodes?"):
|
|
326
349
|
console.print("[yellow]Stopping all nodes...[/yellow]")
|
|
@@ -351,10 +374,10 @@ async def node_list(ctx, cluster: str):
|
|
|
351
374
|
except ImportError:
|
|
352
375
|
console.print("[red]Error:[/red] hanzo-cluster not installed")
|
|
353
376
|
return
|
|
354
|
-
|
|
377
|
+
|
|
355
378
|
try:
|
|
356
379
|
nodes = await HanzoNode.list_nodes(cluster=cluster)
|
|
357
|
-
|
|
380
|
+
|
|
358
381
|
if nodes:
|
|
359
382
|
table = Table(title="Cluster Nodes")
|
|
360
383
|
table.add_column("Name", style="cyan")
|
|
@@ -362,21 +385,21 @@ async def node_list(ctx, cluster: str):
|
|
|
362
385
|
table.add_column("Device", style="yellow")
|
|
363
386
|
table.add_column("Status", style="blue")
|
|
364
387
|
table.add_column("Models", style="magenta")
|
|
365
|
-
|
|
388
|
+
|
|
366
389
|
for node in nodes:
|
|
367
390
|
table.add_row(
|
|
368
391
|
node.get("name", "unknown"),
|
|
369
392
|
node.get("cluster", "unknown"),
|
|
370
393
|
node.get("device", "unknown"),
|
|
371
394
|
node.get("status", "unknown"),
|
|
372
|
-
str(len(node.get("models", [])))
|
|
395
|
+
str(len(node.get("models", []))),
|
|
373
396
|
)
|
|
374
|
-
|
|
397
|
+
|
|
375
398
|
console.print(table)
|
|
376
399
|
else:
|
|
377
400
|
console.print("[yellow]No nodes found[/yellow]")
|
|
378
401
|
console.print("Start a node with: hanzo cluster node start")
|
|
379
|
-
|
|
402
|
+
|
|
380
403
|
except Exception as e:
|
|
381
404
|
console.print(f"[red]Error: {e}[/red]")
|
|
382
405
|
|
|
@@ -391,38 +414,46 @@ async def node_info(ctx, name: str):
|
|
|
391
414
|
except ImportError:
|
|
392
415
|
console.print("[red]Error:[/red] hanzo-cluster not installed")
|
|
393
416
|
return
|
|
394
|
-
|
|
417
|
+
|
|
395
418
|
node = HanzoNode(name=name)
|
|
396
|
-
|
|
419
|
+
|
|
397
420
|
try:
|
|
398
421
|
info = await node.info()
|
|
399
|
-
|
|
422
|
+
|
|
400
423
|
console.print(f"[cyan]Node: {name}[/cyan]")
|
|
401
424
|
console.print(f" Cluster: {info.get('cluster', 'unknown')}")
|
|
402
425
|
console.print(f" Status: {info.get('status', 'unknown')}")
|
|
403
426
|
console.print(f" Device: {info.get('device', 'unknown')}")
|
|
404
|
-
|
|
405
|
-
if uptime := info.get(
|
|
427
|
+
|
|
428
|
+
if uptime := info.get("uptime"):
|
|
406
429
|
console.print(f" Uptime: {uptime}")
|
|
407
|
-
|
|
408
|
-
if resources := info.get(
|
|
430
|
+
|
|
431
|
+
if resources := info.get("resources"):
|
|
409
432
|
console.print("\n[cyan]Resources:[/cyan]")
|
|
410
433
|
console.print(f" CPU: {resources.get('cpu_percent', 'N/A')}%")
|
|
411
|
-
console.print(
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
434
|
+
console.print(
|
|
435
|
+
f" Memory: {resources.get('memory_used', 'N/A')} / {resources.get('memory_total', 'N/A')}"
|
|
436
|
+
)
|
|
437
|
+
if gpu := resources.get("gpu"):
|
|
438
|
+
console.print(
|
|
439
|
+
f" GPU: {gpu.get('name', 'N/A')} ({gpu.get('memory_used', 'N/A')} / {gpu.get('memory_total', 'N/A')})"
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
if models := info.get("models"):
|
|
416
443
|
console.print("\n[cyan]Loaded Models:[/cyan]")
|
|
417
444
|
for model in models:
|
|
418
445
|
console.print(f" • {model}")
|
|
419
|
-
|
|
420
|
-
if network := info.get(
|
|
446
|
+
|
|
447
|
+
if network := info.get("network"):
|
|
421
448
|
console.print("\n[cyan]Network:[/cyan]")
|
|
422
|
-
console.print(
|
|
423
|
-
|
|
424
|
-
|
|
449
|
+
console.print(
|
|
450
|
+
f" Blockchain: {'enabled' if network.get('blockchain') else 'disabled'}"
|
|
451
|
+
)
|
|
452
|
+
console.print(
|
|
453
|
+
f" Discovery: {'enabled' if network.get('discovery') else 'disabled'}"
|
|
454
|
+
)
|
|
455
|
+
if peers := network.get("peers"):
|
|
425
456
|
console.print(f" Peers: {len(peers)}")
|
|
426
|
-
|
|
457
|
+
|
|
427
458
|
except Exception as e:
|
|
428
|
-
console.print(f"[red]Error: {e}[/red]")
|
|
459
|
+
console.print(f"[red]Error: {e}[/red]")
|
hanzo/commands/config.py
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
"""Configuration management commands."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
from typing import Any, Optional
|
|
6
4
|
|
|
7
|
-
import click
|
|
8
5
|
import yaml
|
|
6
|
+
import click
|
|
9
7
|
from rich.syntax import Syntax
|
|
10
8
|
|
|
11
|
-
from ..utils.output import console
|
|
9
|
+
from ..utils.output import console
|
|
12
10
|
|
|
13
11
|
|
|
14
12
|
@click.group(name="config")
|
|
@@ -25,37 +23,37 @@ def config_group():
|
|
|
25
23
|
def show(ctx, is_global: bool, is_local: bool, system: bool):
|
|
26
24
|
"""Show configuration."""
|
|
27
25
|
from ..utils.config import load_config, get_config_paths
|
|
28
|
-
|
|
26
|
+
|
|
29
27
|
# Determine which configs to show
|
|
30
28
|
show_all = not (is_global or is_local or system)
|
|
31
|
-
|
|
29
|
+
|
|
32
30
|
configs = {}
|
|
33
31
|
paths = get_config_paths()
|
|
34
|
-
|
|
32
|
+
|
|
35
33
|
if show_all or system:
|
|
36
34
|
if paths["system"].exists():
|
|
37
35
|
configs["System"] = (paths["system"], load_config(paths["system"]))
|
|
38
|
-
|
|
36
|
+
|
|
39
37
|
if show_all or is_global:
|
|
40
38
|
if paths["global"].exists():
|
|
41
39
|
configs["Global"] = (paths["global"], load_config(paths["global"]))
|
|
42
|
-
|
|
40
|
+
|
|
43
41
|
if show_all or is_local:
|
|
44
42
|
if paths["local"].exists():
|
|
45
43
|
configs["Local"] = (paths["local"], load_config(paths["local"]))
|
|
46
|
-
|
|
44
|
+
|
|
47
45
|
# Merge and show
|
|
48
46
|
if configs:
|
|
49
47
|
for name, (path, config) in configs.items():
|
|
50
48
|
console.print(f"[cyan]{name} Config:[/cyan] {path}")
|
|
51
|
-
|
|
49
|
+
|
|
52
50
|
# Pretty print config
|
|
53
51
|
if config:
|
|
54
52
|
syntax = Syntax(
|
|
55
53
|
yaml.dump(config, default_flow_style=False),
|
|
56
54
|
"yaml",
|
|
57
55
|
theme="monokai",
|
|
58
|
-
line_numbers=False
|
|
56
|
+
line_numbers=False,
|
|
59
57
|
)
|
|
60
58
|
console.print(syntax)
|
|
61
59
|
else:
|
|
@@ -75,20 +73,20 @@ def show(ctx, is_global: bool, is_local: bool, system: bool):
|
|
|
75
73
|
def set(ctx, key: str, value: str, is_global: bool, is_local: bool):
|
|
76
74
|
"""Set configuration value."""
|
|
77
75
|
from ..utils.config import load_config, save_config, get_config_paths
|
|
78
|
-
|
|
76
|
+
|
|
79
77
|
# Determine target config
|
|
80
78
|
paths = get_config_paths()
|
|
81
|
-
|
|
79
|
+
|
|
82
80
|
if is_local:
|
|
83
81
|
config_path = paths["local"]
|
|
84
82
|
config_name = "local"
|
|
85
83
|
else: # Default to global
|
|
86
84
|
config_path = paths["global"]
|
|
87
85
|
config_name = "global"
|
|
88
|
-
|
|
86
|
+
|
|
89
87
|
# Load existing config
|
|
90
88
|
config = load_config(config_path) if config_path.exists() else {}
|
|
91
|
-
|
|
89
|
+
|
|
92
90
|
# Parse value
|
|
93
91
|
try:
|
|
94
92
|
# Try to parse as JSON first
|
|
@@ -102,22 +100,24 @@ def set(ctx, key: str, value: str, is_global: bool, is_local: bool):
|
|
|
102
100
|
else:
|
|
103
101
|
# Keep as string
|
|
104
102
|
parsed_value = value
|
|
105
|
-
|
|
103
|
+
|
|
106
104
|
# Set value (support nested keys with dot notation)
|
|
107
105
|
keys = key.split(".")
|
|
108
106
|
current = config
|
|
109
|
-
|
|
107
|
+
|
|
110
108
|
for k in keys[:-1]:
|
|
111
109
|
if k not in current:
|
|
112
110
|
current[k] = {}
|
|
113
111
|
current = current[k]
|
|
114
|
-
|
|
112
|
+
|
|
115
113
|
current[keys[-1]] = parsed_value
|
|
116
|
-
|
|
114
|
+
|
|
117
115
|
# Save config
|
|
118
116
|
save_config(config_path, config)
|
|
119
|
-
|
|
120
|
-
console.print(
|
|
117
|
+
|
|
118
|
+
console.print(
|
|
119
|
+
f"[green]✓[/green] Set {key} = {parsed_value} in {config_name} config"
|
|
120
|
+
)
|
|
121
121
|
|
|
122
122
|
|
|
123
123
|
@config_group.command()
|
|
@@ -128,10 +128,10 @@ def set(ctx, key: str, value: str, is_global: bool, is_local: bool):
|
|
|
128
128
|
def get(ctx, key: str, is_global: bool, is_local: bool):
|
|
129
129
|
"""Get configuration value."""
|
|
130
130
|
from ..utils.config import get_config_value
|
|
131
|
-
|
|
131
|
+
|
|
132
132
|
scope = "local" if is_local else ("global" if is_global else None)
|
|
133
133
|
value = get_config_value(key, scope=scope)
|
|
134
|
-
|
|
134
|
+
|
|
135
135
|
if value is not None:
|
|
136
136
|
if isinstance(value, (dict, list)):
|
|
137
137
|
console.print_json(data=value)
|
|
@@ -149,32 +149,32 @@ def get(ctx, key: str, is_global: bool, is_local: bool):
|
|
|
149
149
|
def unset(ctx, key: str, is_global: bool, is_local: bool):
|
|
150
150
|
"""Unset configuration value."""
|
|
151
151
|
from ..utils.config import load_config, save_config, get_config_paths
|
|
152
|
-
|
|
152
|
+
|
|
153
153
|
# Determine target config
|
|
154
154
|
paths = get_config_paths()
|
|
155
|
-
|
|
155
|
+
|
|
156
156
|
if is_local:
|
|
157
157
|
config_path = paths["local"]
|
|
158
158
|
config_name = "local"
|
|
159
159
|
else: # Default to global
|
|
160
160
|
config_path = paths["global"]
|
|
161
161
|
config_name = "global"
|
|
162
|
-
|
|
162
|
+
|
|
163
163
|
if not config_path.exists():
|
|
164
164
|
console.print(f"[yellow]No {config_name} config found[/yellow]")
|
|
165
165
|
return
|
|
166
|
-
|
|
166
|
+
|
|
167
167
|
# Load config
|
|
168
168
|
config = load_config(config_path)
|
|
169
|
-
|
|
169
|
+
|
|
170
170
|
# Remove value (support nested keys)
|
|
171
171
|
keys = key.split(".")
|
|
172
172
|
current = config
|
|
173
|
-
|
|
173
|
+
|
|
174
174
|
try:
|
|
175
175
|
for k in keys[:-1]:
|
|
176
176
|
current = current[k]
|
|
177
|
-
|
|
177
|
+
|
|
178
178
|
if keys[-1] in current:
|
|
179
179
|
del current[keys[-1]]
|
|
180
180
|
save_config(config_path, config)
|
|
@@ -194,26 +194,27 @@ def edit(ctx, system: bool, is_global: bool, is_local: bool):
|
|
|
194
194
|
"""Edit configuration file in editor."""
|
|
195
195
|
import os
|
|
196
196
|
import subprocess
|
|
197
|
+
|
|
197
198
|
from ..utils.config import get_config_paths
|
|
198
|
-
|
|
199
|
+
|
|
199
200
|
# Determine which config to edit
|
|
200
201
|
paths = get_config_paths()
|
|
201
|
-
|
|
202
|
+
|
|
202
203
|
if system:
|
|
203
204
|
config_path = paths["system"]
|
|
204
205
|
elif is_local:
|
|
205
206
|
config_path = paths["local"]
|
|
206
207
|
else: # Default to global
|
|
207
208
|
config_path = paths["global"]
|
|
208
|
-
|
|
209
|
+
|
|
209
210
|
# Ensure file exists
|
|
210
211
|
if not config_path.exists():
|
|
211
212
|
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
212
213
|
config_path.write_text("# Hanzo configuration\n")
|
|
213
|
-
|
|
214
|
+
|
|
214
215
|
# Get editor
|
|
215
216
|
editor = os.environ.get("EDITOR", "vi")
|
|
216
|
-
|
|
217
|
+
|
|
217
218
|
# Open in editor
|
|
218
219
|
try:
|
|
219
220
|
subprocess.run([editor, str(config_path)], check=True)
|
|
@@ -230,11 +231,11 @@ def edit(ctx, system: bool, is_global: bool, is_local: bool):
|
|
|
230
231
|
def init(ctx):
|
|
231
232
|
"""Initialize configuration."""
|
|
232
233
|
from ..utils.config import init_config
|
|
233
|
-
|
|
234
|
+
|
|
234
235
|
try:
|
|
235
236
|
paths = init_config()
|
|
236
237
|
console.print("[green]✓[/green] Initialized configuration")
|
|
237
238
|
console.print(f" Global: {paths['global']}")
|
|
238
239
|
console.print(f" Local: {paths.get('local', 'Not in project')}")
|
|
239
240
|
except Exception as e:
|
|
240
|
-
console.print(f"[red]Failed to initialize config: {e}[/red]")
|
|
241
|
+
console.print(f"[red]Failed to initialize config: {e}[/red]")
|