mantatech-sdk 0.5b0.dev65__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.
- manta/__init__.light.py +22 -0
- manta/__init__.py +83 -0
- manta/__main__.py +21 -0
- manta/apis/__init__.py +7 -0
- manta/apis/async_user_api.py +6458 -0
- manta/apis/graph.py +498 -0
- manta/apis/module.py +316 -0
- manta/apis/results.py +251 -0
- manta/apis/swarm.py +206 -0
- manta/apis/user_api.py +1016 -0
- manta/cli/__init__.py +1 -0
- manta/cli/commands/__init__.py +1 -0
- manta/cli/commands/base_handler.py +229 -0
- manta/cli/commands/doc.py +192 -0
- manta/cli/commands/install.py +346 -0
- manta/cli/commands/sdk.py +9 -0
- manta/cli/commands/sdk_cluster.py +211 -0
- manta/cli/commands/sdk_config.py +347 -0
- manta/cli/commands/sdk_globals.py +280 -0
- manta/cli/commands/sdk_logs.py +174 -0
- manta/cli/commands/sdk_main.py +167 -0
- manta/cli/commands/sdk_module.py +516 -0
- manta/cli/commands/sdk_nodes.py +168 -0
- manta/cli/commands/sdk_original.py +3873 -0
- manta/cli/commands/sdk_results.py +265 -0
- manta/cli/commands/sdk_swarm.py +454 -0
- manta/cli/commands/sdk_user.py +234 -0
- manta/cli/commands/status.py +292 -0
- manta/cli/component_detector.py +112 -0
- manta/cli/config_manager.py +445 -0
- manta/cli/main.py +265 -0
- manta/cli/utils/__init__.py +27 -0
- manta/cli/utils/converters.py +140 -0
- manta/clients/cluster_management_client.py +486 -0
- manta/clients/local_client.py +149 -0
- manta/clients/module_management_client.py +217 -0
- manta/clients/swarm_management_client.py +562 -0
- manta/clients/user_management_client.py +395 -0
- manta/clients/world_client.py +195 -0
- manta/light/__init__.py +31 -0
- manta/light/globals.py +245 -0
- manta/light/local.py +407 -0
- manta/light/logging_config.py +39 -0
- manta/light/path.py +116 -0
- manta/light/results.py +236 -0
- manta/light/task.py +100 -0
- manta/light/utils.py +217 -0
- manta/light/world.py +177 -0
- mantatech_sdk-0.5b0.dev65.dist-info/METADATA +1039 -0
- mantatech_sdk-0.5b0.dev65.dist-info/RECORD +54 -0
- mantatech_sdk-0.5b0.dev65.dist-info/WHEEL +5 -0
- mantatech_sdk-0.5b0.dev65.dist-info/entry_points.txt +2 -0
- mantatech_sdk-0.5b0.dev65.dist-info/licenses/LICENSE +683 -0
- mantatech_sdk-0.5b0.dev65.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
"""SDK configuration management commands matching node config pattern."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
|
|
6
|
+
from rich.panel import Panel
|
|
7
|
+
from rich.prompt import Confirm, Prompt, IntPrompt
|
|
8
|
+
from rich.table import Table
|
|
9
|
+
|
|
10
|
+
from .base_handler import BaseSDKHandler
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SDKConfigHandler(BaseSDKHandler):
|
|
14
|
+
"""Handle configuration-related SDK commands."""
|
|
15
|
+
|
|
16
|
+
def add_subparsers(self, parent_parser):
|
|
17
|
+
"""Add config command subparsers."""
|
|
18
|
+
self.parser = parent_parser # Store parser reference
|
|
19
|
+
subparsers = parent_parser.add_subparsers(
|
|
20
|
+
dest="config_command", help="Config commands"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# Init config command
|
|
24
|
+
init_parser = subparsers.add_parser("init", help="Initialize configuration")
|
|
25
|
+
init_parser.add_argument(
|
|
26
|
+
"--interactive", action="store_true", help="Run interactive setup"
|
|
27
|
+
)
|
|
28
|
+
init_parser.add_argument("--name", help="Configuration name", default="default")
|
|
29
|
+
init_parser.add_argument("--token", help="JWT authentication token")
|
|
30
|
+
init_parser.add_argument("--host", default="localhost", help="Server host")
|
|
31
|
+
init_parser.add_argument("--port", type=int, default=50052, help="Server port")
|
|
32
|
+
|
|
33
|
+
# List configs command
|
|
34
|
+
subparsers.add_parser("list", help="List all configurations")
|
|
35
|
+
|
|
36
|
+
# Show config command
|
|
37
|
+
show_parser = subparsers.add_parser("show", help="Show configuration")
|
|
38
|
+
show_parser.add_argument(
|
|
39
|
+
"name", nargs="?", help="Configuration name (uses active if not specified)"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Use config command (set active)
|
|
43
|
+
use_parser = subparsers.add_parser("use", help="Set active configuration")
|
|
44
|
+
use_parser.add_argument("name", help="Configuration name")
|
|
45
|
+
|
|
46
|
+
# Delete config command
|
|
47
|
+
delete_parser = subparsers.add_parser("delete", help="Delete configuration")
|
|
48
|
+
delete_parser.add_argument("name", help="Configuration name")
|
|
49
|
+
|
|
50
|
+
# Edit config command
|
|
51
|
+
edit_parser = subparsers.add_parser(
|
|
52
|
+
"edit", help="Edit configuration file in text editor"
|
|
53
|
+
)
|
|
54
|
+
edit_parser.add_argument(
|
|
55
|
+
"name", nargs="?", help="Configuration name (uses active if not specified)"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
def handle(self, args) -> int:
|
|
59
|
+
"""Handle config commands."""
|
|
60
|
+
if not args.config_command:
|
|
61
|
+
if hasattr(self, "parser"):
|
|
62
|
+
self.parser.print_help()
|
|
63
|
+
return 0 # Return 0 for help display
|
|
64
|
+
|
|
65
|
+
if args.config_command == "init":
|
|
66
|
+
return self.init_config(args)
|
|
67
|
+
elif args.config_command == "list":
|
|
68
|
+
return self.list_configs()
|
|
69
|
+
elif args.config_command == "show":
|
|
70
|
+
return self.show_config(getattr(args, "name", None))
|
|
71
|
+
elif args.config_command == "use":
|
|
72
|
+
return self.use_config(args.name)
|
|
73
|
+
elif args.config_command == "delete":
|
|
74
|
+
return self.delete_config(args.name)
|
|
75
|
+
elif args.config_command == "edit":
|
|
76
|
+
return self.edit_config(getattr(args, "name", None))
|
|
77
|
+
else:
|
|
78
|
+
self.print_error(f"Unknown config command: {args.config_command}")
|
|
79
|
+
return 1
|
|
80
|
+
|
|
81
|
+
# Profile methods removed - using simple config approach
|
|
82
|
+
|
|
83
|
+
def init_config(self, args) -> int:
|
|
84
|
+
"""Initialize configuration system."""
|
|
85
|
+
try:
|
|
86
|
+
config_name = getattr(args, "name", "default")
|
|
87
|
+
|
|
88
|
+
# Check if configuration already exists
|
|
89
|
+
config_path = self.config_manager.config_dir / f"{config_name}.toml"
|
|
90
|
+
if config_path.exists():
|
|
91
|
+
overwrite = Confirm.ask(
|
|
92
|
+
f"Configuration '{config_name}' already exists. Overwrite?",
|
|
93
|
+
console=self.console,
|
|
94
|
+
default=False,
|
|
95
|
+
)
|
|
96
|
+
if not overwrite:
|
|
97
|
+
self.print("[yellow]Configuration creation cancelled[/yellow]")
|
|
98
|
+
return 1
|
|
99
|
+
|
|
100
|
+
# Default to interactive mode if no token provided
|
|
101
|
+
token = getattr(args, "token", None)
|
|
102
|
+
interactive = getattr(args, "interactive", False) or token is None
|
|
103
|
+
|
|
104
|
+
if interactive:
|
|
105
|
+
self.console.print(
|
|
106
|
+
Panel(
|
|
107
|
+
"[bold cyan]SDK Configuration Setup[/bold cyan]\n\n"
|
|
108
|
+
"This wizard will help you create a new SDK configuration.\n"
|
|
109
|
+
"Configuration will be saved in ~/.manta/sdk/",
|
|
110
|
+
title="manta sdk Configuration",
|
|
111
|
+
border_style="blue",
|
|
112
|
+
)
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# Get configuration details
|
|
116
|
+
host = Prompt.ask(
|
|
117
|
+
"[cyan]Manager host[/cyan]",
|
|
118
|
+
console=self.console,
|
|
119
|
+
default="localhost",
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
port = IntPrompt.ask(
|
|
123
|
+
"[cyan]Manager port[/cyan]", console=self.console, default=50052
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
token = Prompt.ask(
|
|
127
|
+
"[cyan]JWT authentication token[/cyan]",
|
|
128
|
+
password=True,
|
|
129
|
+
console=self.console,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
if not token:
|
|
133
|
+
self.print_error("JWT token is required for SDK authentication")
|
|
134
|
+
return 1
|
|
135
|
+
|
|
136
|
+
# Optional: TLS configuration
|
|
137
|
+
use_tls = Confirm.ask(
|
|
138
|
+
"[cyan]Use TLS connection?[/cyan]",
|
|
139
|
+
console=self.console,
|
|
140
|
+
default=False,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
cert_folder = None
|
|
144
|
+
if use_tls:
|
|
145
|
+
cert_folder = Prompt.ask(
|
|
146
|
+
"[cyan]Certificate folder path[/cyan]",
|
|
147
|
+
console=self.console,
|
|
148
|
+
default="~/.manta/certs",
|
|
149
|
+
)
|
|
150
|
+
else:
|
|
151
|
+
# Non-interactive mode - use command line arguments
|
|
152
|
+
token = getattr(args, "token", None)
|
|
153
|
+
host = getattr(args, "host", "localhost")
|
|
154
|
+
port = getattr(args, "port", 50052)
|
|
155
|
+
cert_folder = None
|
|
156
|
+
|
|
157
|
+
# Create or update configuration
|
|
158
|
+
if config_path.exists():
|
|
159
|
+
# If we got here, user agreed to overwrite
|
|
160
|
+
success = self.config_manager.update_config(
|
|
161
|
+
name=config_name,
|
|
162
|
+
token=token,
|
|
163
|
+
host=host,
|
|
164
|
+
port=port,
|
|
165
|
+
cert_folder=cert_folder,
|
|
166
|
+
description=f"SDK configuration '{config_name}'",
|
|
167
|
+
)
|
|
168
|
+
else:
|
|
169
|
+
success = self.config_manager.create_config(
|
|
170
|
+
name=config_name,
|
|
171
|
+
token=token,
|
|
172
|
+
host=host,
|
|
173
|
+
port=port,
|
|
174
|
+
cert_folder=cert_folder,
|
|
175
|
+
description=f"SDK configuration '{config_name}'",
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
if success:
|
|
179
|
+
self.config_manager.set_active_config(config_name)
|
|
180
|
+
self.print_success(
|
|
181
|
+
f"Configuration '{config_name}' created and activated successfully"
|
|
182
|
+
)
|
|
183
|
+
if not token:
|
|
184
|
+
self.print("Run 'manta sdk config edit' to add your JWT token")
|
|
185
|
+
else:
|
|
186
|
+
self.print_error("Failed to create configuration")
|
|
187
|
+
return 1
|
|
188
|
+
|
|
189
|
+
return 0
|
|
190
|
+
|
|
191
|
+
except Exception as e:
|
|
192
|
+
self.print_error(f"Failed to initialize configuration: {e}")
|
|
193
|
+
return 1
|
|
194
|
+
|
|
195
|
+
def show_config(self, config_name: str = None) -> int:
|
|
196
|
+
"""Show current configuration."""
|
|
197
|
+
try:
|
|
198
|
+
if config_name:
|
|
199
|
+
config = self.config_manager.load_config(config_name)
|
|
200
|
+
if not config:
|
|
201
|
+
self.print_error(f"Configuration '{config_name}' not found")
|
|
202
|
+
return 1
|
|
203
|
+
title = f"Configuration: {config_name}"
|
|
204
|
+
else:
|
|
205
|
+
config = self.config_manager.get_active_config()
|
|
206
|
+
active_name = self.config_manager.get_active_config_name()
|
|
207
|
+
if config:
|
|
208
|
+
title = f"Active Configuration: {active_name}"
|
|
209
|
+
else:
|
|
210
|
+
self.print_error("No active configuration found")
|
|
211
|
+
return 1
|
|
212
|
+
|
|
213
|
+
config_text = ""
|
|
214
|
+
for key, value in config.__dict__.items():
|
|
215
|
+
if key == "token" and value:
|
|
216
|
+
# Mask token for security
|
|
217
|
+
masked_token = value[:8] + "..." if len(value) > 8 else "***"
|
|
218
|
+
config_text += f"[cyan]{key}:[/cyan] {masked_token}\n"
|
|
219
|
+
else:
|
|
220
|
+
config_text += f"[cyan]{key}:[/cyan] {value}\n"
|
|
221
|
+
|
|
222
|
+
panel = Panel.fit(config_text.strip(), title=title)
|
|
223
|
+
self.console.print(panel)
|
|
224
|
+
|
|
225
|
+
return 0
|
|
226
|
+
|
|
227
|
+
except Exception as e:
|
|
228
|
+
self.print_error(f"Failed to show configuration: {e}")
|
|
229
|
+
return 1
|
|
230
|
+
|
|
231
|
+
def edit_config(self, config_name: str = None) -> int:
|
|
232
|
+
"""Edit configuration file in default editor."""
|
|
233
|
+
try:
|
|
234
|
+
if config_name is None:
|
|
235
|
+
config_name = self.config_manager.get_active_config_name()
|
|
236
|
+
|
|
237
|
+
config_path = self.config_manager.config_dir / f"{config_name}.toml"
|
|
238
|
+
if not config_path.exists():
|
|
239
|
+
self.print_error(f"Configuration '{config_name}' not found")
|
|
240
|
+
return 1
|
|
241
|
+
|
|
242
|
+
# Get default editor
|
|
243
|
+
editor = os.environ.get("EDITOR", "nano")
|
|
244
|
+
|
|
245
|
+
# Launch editor
|
|
246
|
+
try:
|
|
247
|
+
subprocess.run([editor, str(config_path)], check=True)
|
|
248
|
+
self.print_success(f"Configuration '{config_name}' edited successfully")
|
|
249
|
+
return 0
|
|
250
|
+
except subprocess.CalledProcessError:
|
|
251
|
+
self.print_error(f"Failed to launch editor: {editor}")
|
|
252
|
+
return 1
|
|
253
|
+
except FileNotFoundError:
|
|
254
|
+
self.print_error(f"Editor not found: {editor}")
|
|
255
|
+
self.print(
|
|
256
|
+
"Set the EDITOR environment variable to your preferred editor"
|
|
257
|
+
)
|
|
258
|
+
return 1
|
|
259
|
+
|
|
260
|
+
except Exception as e:
|
|
261
|
+
self.print_error(f"Failed to edit configuration: {e}")
|
|
262
|
+
return 1
|
|
263
|
+
|
|
264
|
+
def list_configs(self) -> int:
|
|
265
|
+
"""List all configurations."""
|
|
266
|
+
try:
|
|
267
|
+
configs = self.config_manager.list_configs()
|
|
268
|
+
active_config_name = self.config_manager.get_active_config_name()
|
|
269
|
+
|
|
270
|
+
if not configs:
|
|
271
|
+
self.print("No configurations found.")
|
|
272
|
+
self.print("Run 'manta sdk config init --interactive' to create one.")
|
|
273
|
+
return 0
|
|
274
|
+
|
|
275
|
+
table = Table(title="SDK Configurations")
|
|
276
|
+
table.add_column("Configuration Name", style="cyan")
|
|
277
|
+
table.add_column("Status", style="green")
|
|
278
|
+
table.add_column("Host", style="blue")
|
|
279
|
+
table.add_column("Port", style="magenta")
|
|
280
|
+
table.add_column("Description", style="white")
|
|
281
|
+
|
|
282
|
+
for config_name in configs:
|
|
283
|
+
config = self.config_manager.load_config(config_name)
|
|
284
|
+
status = "Active" if config_name == active_config_name else ""
|
|
285
|
+
|
|
286
|
+
if config:
|
|
287
|
+
table.add_row(
|
|
288
|
+
config_name,
|
|
289
|
+
status,
|
|
290
|
+
config.host,
|
|
291
|
+
str(config.port),
|
|
292
|
+
config.description or "",
|
|
293
|
+
)
|
|
294
|
+
else:
|
|
295
|
+
table.add_row(
|
|
296
|
+
config_name, status, "Error", "Error", "Failed to load"
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
self.console.print(table)
|
|
300
|
+
|
|
301
|
+
return 0
|
|
302
|
+
|
|
303
|
+
except Exception as e:
|
|
304
|
+
self.print_error(f"Failed to list configurations: {e}")
|
|
305
|
+
return 1
|
|
306
|
+
|
|
307
|
+
def use_config(self, config_name: str) -> int:
|
|
308
|
+
"""Set the active configuration."""
|
|
309
|
+
try:
|
|
310
|
+
success = self.config_manager.set_active_config(config_name)
|
|
311
|
+
if success:
|
|
312
|
+
self.print_success(f"Active configuration set to '{config_name}'")
|
|
313
|
+
return 0
|
|
314
|
+
else:
|
|
315
|
+
return 1
|
|
316
|
+
|
|
317
|
+
except Exception as e:
|
|
318
|
+
self.print_error(f"Failed to set active configuration: {e}")
|
|
319
|
+
return 1
|
|
320
|
+
|
|
321
|
+
def delete_config(self, config_name: str) -> int:
|
|
322
|
+
"""Delete a configuration."""
|
|
323
|
+
try:
|
|
324
|
+
if config_name == "default":
|
|
325
|
+
self.print_error("Cannot delete default configuration")
|
|
326
|
+
return 1
|
|
327
|
+
|
|
328
|
+
confirm = Confirm.ask(
|
|
329
|
+
f"Are you sure you want to delete configuration '{config_name}'?",
|
|
330
|
+
console=self.console,
|
|
331
|
+
)
|
|
332
|
+
if not confirm:
|
|
333
|
+
self.print("Operation cancelled.")
|
|
334
|
+
return 0
|
|
335
|
+
|
|
336
|
+
success = self.config_manager.delete_config(config_name)
|
|
337
|
+
if success:
|
|
338
|
+
self.print_success(
|
|
339
|
+
f"Configuration '{config_name}' deleted successfully"
|
|
340
|
+
)
|
|
341
|
+
return 0
|
|
342
|
+
else:
|
|
343
|
+
return 1
|
|
344
|
+
|
|
345
|
+
except Exception as e:
|
|
346
|
+
self.print_error(f"Failed to delete configuration: {e}")
|
|
347
|
+
return 1
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
"""SDK globals retrieval and management commands."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import signal
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
from rich.table import Table
|
|
8
|
+
|
|
9
|
+
from .base_handler import BaseSDKHandler
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SDKGlobalsHandler(BaseSDKHandler):
|
|
13
|
+
"""Handle globals-related SDK commands."""
|
|
14
|
+
|
|
15
|
+
def add_subparsers(self, parent_parser):
|
|
16
|
+
"""Add globals command subparsers."""
|
|
17
|
+
self.parser = parent_parser # Store parser reference
|
|
18
|
+
subparsers = parent_parser.add_subparsers(
|
|
19
|
+
dest="globals_command", help="Globals commands"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
# List tags command
|
|
23
|
+
list_tags_parser = subparsers.add_parser(
|
|
24
|
+
"list-tags", help="List available global tags for a swarm"
|
|
25
|
+
)
|
|
26
|
+
list_tags_parser.add_argument(
|
|
27
|
+
"swarm_id", help="Swarm ID to list global tags for"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# Get globals command
|
|
31
|
+
get_parser = subparsers.add_parser("get", help="Get globals for a swarm")
|
|
32
|
+
get_parser.add_argument("swarm_id", help="Swarm ID to get globals for")
|
|
33
|
+
get_parser.add_argument(
|
|
34
|
+
"--tag", default="latest", help="Global tag (default: latest)"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Export globals command
|
|
38
|
+
export_parser = subparsers.add_parser("export", help="Export globals to file")
|
|
39
|
+
export_parser.add_argument("swarm_id", help="Swarm ID to export globals for")
|
|
40
|
+
export_parser.add_argument(
|
|
41
|
+
"--tag", default="latest", help="Global tag (default: latest)"
|
|
42
|
+
)
|
|
43
|
+
export_parser.add_argument("--output", required=True, help="Output file path")
|
|
44
|
+
|
|
45
|
+
# Stream globals command
|
|
46
|
+
stream_parser = subparsers.add_parser(
|
|
47
|
+
"stream", help="Stream globals in real-time"
|
|
48
|
+
)
|
|
49
|
+
stream_parser.add_argument("swarm_id", help="Swarm ID to stream globals for")
|
|
50
|
+
stream_parser.add_argument(
|
|
51
|
+
"--tag", default="latest", help="Global tag (default: latest)"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Add profile override to all subcommands
|
|
55
|
+
for parser in [list_tags_parser, get_parser, export_parser, stream_parser]:
|
|
56
|
+
parser.add_argument("--profile", help="Use specific profile")
|
|
57
|
+
|
|
58
|
+
def handle(self, args) -> int:
|
|
59
|
+
"""Handle globals commands."""
|
|
60
|
+
if not args.globals_command:
|
|
61
|
+
if hasattr(self, "parser"):
|
|
62
|
+
self.parser.print_help()
|
|
63
|
+
return 0 # Return 0 for help display
|
|
64
|
+
|
|
65
|
+
if args.globals_command == "list-tags":
|
|
66
|
+
return self.list_tags(args.swarm_id)
|
|
67
|
+
elif args.globals_command == "get":
|
|
68
|
+
return self.get_globals(args.swarm_id, getattr(args, "tag", "latest"))
|
|
69
|
+
elif args.globals_command == "export":
|
|
70
|
+
return self.export_globals(
|
|
71
|
+
args.swarm_id, getattr(args, "tag", "latest"), args.output
|
|
72
|
+
)
|
|
73
|
+
elif args.globals_command == "stream":
|
|
74
|
+
return self.stream_globals(args.swarm_id, getattr(args, "tag", "latest"))
|
|
75
|
+
else:
|
|
76
|
+
self.print_error(f"Unknown globals command: {args.globals_command}")
|
|
77
|
+
return 1
|
|
78
|
+
|
|
79
|
+
def list_tags(self, swarm_id: str) -> int:
|
|
80
|
+
"""List available global tags for a swarm."""
|
|
81
|
+
api = self._get_user_api()
|
|
82
|
+
if not api:
|
|
83
|
+
return 1
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
|
|
87
|
+
async def run():
|
|
88
|
+
return await api.list_global_tags(swarm_id)
|
|
89
|
+
|
|
90
|
+
tags_info = self._run_with_progress(
|
|
91
|
+
run, f"Fetching global tags for swarm {swarm_id}..."
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if tags_info is None:
|
|
95
|
+
return 1
|
|
96
|
+
|
|
97
|
+
if not tags_info or not tags_info.get("tags"):
|
|
98
|
+
self.print("No global tags found for this swarm.")
|
|
99
|
+
return 0
|
|
100
|
+
|
|
101
|
+
# Display tags in a table
|
|
102
|
+
table = Table(title=f"Global Tags for Swarm: {swarm_id}")
|
|
103
|
+
table.add_column("Tag", style="cyan")
|
|
104
|
+
table.add_column("Count", style="green")
|
|
105
|
+
table.add_column("Total Size", style="yellow")
|
|
106
|
+
table.add_column("Last Updated", style="blue")
|
|
107
|
+
|
|
108
|
+
tags = tags_info.get("tags", {})
|
|
109
|
+
for tag, tag_data in tags.items():
|
|
110
|
+
count = tag_data.get("count", 0)
|
|
111
|
+
size = tag_data.get("total_size", 0)
|
|
112
|
+
last_updated = tag_data.get("last_updated", "Unknown")
|
|
113
|
+
|
|
114
|
+
table.add_row(tag, str(count), self._format_size(size), last_updated)
|
|
115
|
+
|
|
116
|
+
self.console.print(table)
|
|
117
|
+
|
|
118
|
+
return 0
|
|
119
|
+
|
|
120
|
+
except Exception as e:
|
|
121
|
+
self.print_error(f"Failed to list global tags: {e}")
|
|
122
|
+
return 1
|
|
123
|
+
|
|
124
|
+
def get_globals(self, swarm_id: str, tag: str = "latest") -> int:
|
|
125
|
+
"""Get globals for a swarm."""
|
|
126
|
+
api = self._get_user_api()
|
|
127
|
+
if not api:
|
|
128
|
+
return 1
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
|
|
132
|
+
async def run():
|
|
133
|
+
# Use select_global to get globals data
|
|
134
|
+
globals_data = []
|
|
135
|
+
async for global_item in api.select_global(swarm_id, tag):
|
|
136
|
+
globals_data.append(global_item)
|
|
137
|
+
return globals_data
|
|
138
|
+
|
|
139
|
+
globals_data = self._run_with_progress(
|
|
140
|
+
run, f"Fetching globals for swarm {swarm_id}..."
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
if globals_data is None:
|
|
144
|
+
return 1
|
|
145
|
+
|
|
146
|
+
if not globals_data:
|
|
147
|
+
self.print("No globals found.")
|
|
148
|
+
return 0
|
|
149
|
+
|
|
150
|
+
# Display globals
|
|
151
|
+
table = Table(title=f"Globals for Swarm: {swarm_id} (Tag: {tag})")
|
|
152
|
+
table.add_column("Global ID", style="cyan")
|
|
153
|
+
table.add_column("Type", style="blue")
|
|
154
|
+
table.add_column("Size", style="green")
|
|
155
|
+
table.add_column("Created", style="yellow")
|
|
156
|
+
|
|
157
|
+
for global_item in globals_data:
|
|
158
|
+
data = global_item.get("data", {})
|
|
159
|
+
data_size = len(json.dumps(data)) if data else 0
|
|
160
|
+
|
|
161
|
+
table.add_row(
|
|
162
|
+
global_item.get("global_id", "Unknown"),
|
|
163
|
+
global_item.get("global_type", "Unknown"),
|
|
164
|
+
self._format_size(data_size),
|
|
165
|
+
global_item.get("created_at", "Unknown"),
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
self.console.print(table)
|
|
169
|
+
|
|
170
|
+
# Show first few globals as samples
|
|
171
|
+
self.print("\nSample Globals:")
|
|
172
|
+
for i, global_item in enumerate(globals_data[:3]):
|
|
173
|
+
self.print(f"Global {i + 1}:")
|
|
174
|
+
self.print(json.dumps(global_item.get("data", {}), indent=2))
|
|
175
|
+
if i < 2 and i < len(globals_data) - 1:
|
|
176
|
+
self.print("-" * 40)
|
|
177
|
+
|
|
178
|
+
return 0
|
|
179
|
+
|
|
180
|
+
except Exception as e:
|
|
181
|
+
self.print_error(f"Failed to get globals: {e}")
|
|
182
|
+
return 1
|
|
183
|
+
|
|
184
|
+
def export_globals(self, swarm_id: str, tag: str, output_file: str) -> int:
|
|
185
|
+
"""Export globals to a file."""
|
|
186
|
+
api = self._get_user_api()
|
|
187
|
+
if not api:
|
|
188
|
+
return 1
|
|
189
|
+
|
|
190
|
+
try:
|
|
191
|
+
|
|
192
|
+
async def run():
|
|
193
|
+
# Use select_global to get globals data
|
|
194
|
+
globals_data = []
|
|
195
|
+
async for global_item in api.select_global(swarm_id, tag):
|
|
196
|
+
globals_data.append(global_item)
|
|
197
|
+
return globals_data
|
|
198
|
+
|
|
199
|
+
globals_data = self._run_with_progress(
|
|
200
|
+
run, "Fetching globals for export..."
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
if globals_data is None:
|
|
204
|
+
return 1
|
|
205
|
+
|
|
206
|
+
if not globals_data:
|
|
207
|
+
self.print_error("No globals found")
|
|
208
|
+
return 1
|
|
209
|
+
|
|
210
|
+
# Write to file
|
|
211
|
+
with open(output_file, "w") as f:
|
|
212
|
+
json.dump(globals_data, f, indent=2)
|
|
213
|
+
|
|
214
|
+
self.print_success(f"Globals exported to {output_file}")
|
|
215
|
+
return 0
|
|
216
|
+
|
|
217
|
+
except Exception as e:
|
|
218
|
+
self.print_error(f"Export failed: {e}")
|
|
219
|
+
return 1
|
|
220
|
+
|
|
221
|
+
def stream_globals(self, swarm_id: str, tag: str = "latest") -> int:
|
|
222
|
+
"""Stream globals in real-time."""
|
|
223
|
+
api = self._get_user_api()
|
|
224
|
+
if not api:
|
|
225
|
+
return 1
|
|
226
|
+
|
|
227
|
+
self.print(f"Streaming globals for swarm: {swarm_id} (Tag: {tag})")
|
|
228
|
+
self.print("Press Ctrl+C to stop streaming...")
|
|
229
|
+
|
|
230
|
+
try:
|
|
231
|
+
# Set up signal handler for graceful shutdown
|
|
232
|
+
shutdown_flag = False
|
|
233
|
+
|
|
234
|
+
def signal_handler(sig, frame):
|
|
235
|
+
nonlocal shutdown_flag
|
|
236
|
+
shutdown_flag = True
|
|
237
|
+
|
|
238
|
+
signal.signal(signal.SIGINT, signal_handler)
|
|
239
|
+
|
|
240
|
+
last_global_count = 0
|
|
241
|
+
|
|
242
|
+
while not shutdown_flag:
|
|
243
|
+
try:
|
|
244
|
+
|
|
245
|
+
async def get_globals():
|
|
246
|
+
globals_data = []
|
|
247
|
+
async for global_item in api.select_global(swarm_id, tag):
|
|
248
|
+
globals_data.append(global_item)
|
|
249
|
+
return globals_data
|
|
250
|
+
|
|
251
|
+
globals_data = self._run_async(get_globals())
|
|
252
|
+
|
|
253
|
+
# Show new globals since last check
|
|
254
|
+
if globals_data and len(globals_data) > last_global_count:
|
|
255
|
+
new_globals = globals_data[last_global_count:]
|
|
256
|
+
|
|
257
|
+
for global_item in new_globals:
|
|
258
|
+
timestamp = global_item.get("created_at", "unknown")
|
|
259
|
+
data = global_item.get("data", {})
|
|
260
|
+
self.print(f"[{timestamp}] {json.dumps(data, indent=2)}")
|
|
261
|
+
|
|
262
|
+
last_global_count = len(globals_data)
|
|
263
|
+
|
|
264
|
+
time.sleep(5) # Wait 5 seconds before next poll
|
|
265
|
+
|
|
266
|
+
except KeyboardInterrupt:
|
|
267
|
+
self.print("\nStreaming stopped.")
|
|
268
|
+
return 130 # KeyboardInterrupt exit code
|
|
269
|
+
|
|
270
|
+
except Exception as e:
|
|
271
|
+
self.print_error(f"Streaming failed: {e}")
|
|
272
|
+
return 1
|
|
273
|
+
|
|
274
|
+
def _format_size(self, size: int) -> str:
|
|
275
|
+
"""Format byte size to human readable string."""
|
|
276
|
+
for unit in ["B", "KB", "MB", "GB"]:
|
|
277
|
+
if size < 1024.0:
|
|
278
|
+
return f"{size:.1f} {unit}"
|
|
279
|
+
size /= 1024.0
|
|
280
|
+
return f"{size:.1f} TB"
|