glaip-sdk 0.0.1b5__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.
@@ -0,0 +1,592 @@
1
+ """CLI commands for managing SDK configuration.
2
+
3
+ Authors:
4
+ Raymond Christopher (raymond.christopher@gdplabs.id)
5
+ """
6
+
7
+ import json
8
+ import os
9
+ from pathlib import Path
10
+
11
+ import click
12
+
13
+ from glaip_sdk import Client
14
+
15
+
16
+ def get_client(ctx) -> Client:
17
+ """Get configured client from context."""
18
+ config = ctx.obj or {}
19
+ return Client(
20
+ api_url=config.get("api_url"),
21
+ api_key=config.get("api_key"),
22
+ timeout=config.get("timeout", 30.0),
23
+ )
24
+
25
+
26
+ @click.group()
27
+ def config_group():
28
+ """Manage SDK configuration."""
29
+ pass
30
+
31
+
32
+ @config_group.command()
33
+ @click.pass_context
34
+ def show(ctx):
35
+ """Show current configuration."""
36
+ try:
37
+ # Get config from context
38
+ context_config = ctx.obj or {}
39
+
40
+ # Get config from environment
41
+ env_config = {
42
+ "api_url": os.getenv("AIP_API_URL"),
43
+ "api_key": os.getenv("AIP_API_KEY"),
44
+ "timeout": os.getenv("AIP_TIMEOUT", "30.0"),
45
+ }
46
+
47
+ # Get config from file
48
+ config_path = Path.home() / ".aip" / "config.yaml"
49
+ file_config = {}
50
+ if config_path.exists():
51
+ try:
52
+ import yaml
53
+
54
+ with open(config_path) as f:
55
+ file_config = yaml.safe_load(f) or {}
56
+ except Exception:
57
+ file_config = {}
58
+
59
+ click.echo("šŸ”§ AIP SDK Configuration")
60
+ click.echo("=" * 50)
61
+
62
+ # Show configuration sources
63
+ click.echo("Configuration Sources (in order of precedence):")
64
+ click.echo(" 1. Command line arguments")
65
+ click.echo(" 2. Environment variables")
66
+ click.echo(" 3. Config file (~/.aip/config.yaml)")
67
+ click.echo(" 4. Default values")
68
+ click.echo()
69
+
70
+ # Show current values
71
+ click.echo("Current Configuration:")
72
+ click.echo(
73
+ f" API URL: {context_config.get('api_url') or env_config['api_url'] or file_config.get('api_url') or 'Not set'}"
74
+ )
75
+
76
+ api_key = (
77
+ context_config.get("api_key")
78
+ or env_config["api_key"]
79
+ or file_config.get("api_key")
80
+ )
81
+ if api_key:
82
+ # Mask API key for security
83
+ masked_key = (
84
+ api_key[:8] + "..." + api_key[-4:] if len(api_key) > 12 else "***"
85
+ )
86
+ click.echo(f" API Key: {masked_key}")
87
+ else:
88
+ click.echo(" API Key: Not set")
89
+
90
+ timeout = (
91
+ context_config.get("timeout")
92
+ or env_config["timeout"]
93
+ or file_config.get("timeout")
94
+ or "30.0"
95
+ )
96
+ click.echo(f" Timeout: {timeout} seconds")
97
+
98
+ # Show config file location
99
+ click.echo(f"\nConfig file: {config_path}")
100
+ if config_path.exists():
101
+ click.echo("āœ… Config file exists")
102
+ else:
103
+ click.echo("āŒ Config file does not exist")
104
+
105
+ except Exception as e:
106
+ click.echo(f"āŒ Error showing configuration: {e}")
107
+
108
+
109
+ @config_group.command()
110
+ @click.option("--api-url", help="Set API URL")
111
+ @click.option("--api-key", help="Set API key")
112
+ @click.option("--timeout", type=float, help="Set timeout in seconds")
113
+ @click.option("--output", "-o", type=click.Path(), help="Output file for configuration")
114
+ @click.pass_context
115
+ def set(ctx, api_url, api_key, timeout, output):
116
+ """Set configuration values."""
117
+ try:
118
+ # Get current config
119
+ config_path = Path.home() / ".aip" / "config.yaml"
120
+ config_path.parent.mkdir(parents=True, exist_ok=True)
121
+
122
+ # Load existing config
123
+ current_config = {}
124
+ if config_path.exists():
125
+ try:
126
+ import yaml
127
+
128
+ with open(config_path) as f:
129
+ current_config = yaml.safe_load(f) or {}
130
+ except Exception:
131
+ current_config = {}
132
+
133
+ # Update config with new values
134
+ updated = False
135
+ if api_url:
136
+ current_config["api_url"] = api_url
137
+ updated = True
138
+ click.echo(f"āœ… API URL set to: {api_url}")
139
+
140
+ if api_key:
141
+ current_config["api_key"] = api_key
142
+ updated = True
143
+ # Mask API key for display
144
+ masked_key = (
145
+ api_key[:8] + "..." + api_key[-4:] if len(api_key) > 12 else "***"
146
+ )
147
+ click.echo(f"āœ… API Key set to: {masked_key}")
148
+
149
+ if timeout:
150
+ current_config["timeout"] = timeout
151
+ updated = True
152
+ click.echo(f"āœ… Timeout set to: {timeout} seconds")
153
+
154
+ if not updated:
155
+ click.echo(
156
+ "āŒ No configuration values provided. Use --help for available options."
157
+ )
158
+ return
159
+
160
+ # Save updated config
161
+ try:
162
+ import yaml
163
+
164
+ with open(config_path, "w") as f:
165
+ yaml.dump(current_config, f, default_flow_style=False)
166
+ click.echo(f"šŸ“„ Configuration saved to: {config_path}")
167
+ except Exception as e:
168
+ click.echo(f"āŒ Error saving configuration: {e}")
169
+ return
170
+
171
+ # Save to output file if specified
172
+ if output:
173
+ output_path = Path(output)
174
+ with open(output_path, "w") as f:
175
+ json.dump(current_config, f, indent=2, default=str)
176
+ click.echo(f"šŸ“„ Configuration also saved to: {output_path}")
177
+
178
+ except Exception as e:
179
+ click.echo(f"āŒ Error setting configuration: {e}")
180
+
181
+
182
+ @config_group.command()
183
+ @click.option("--api-url", is_flag=True, help="Remove API URL setting")
184
+ @click.option("--api-key", is_flag=True, help="Remove API key setting")
185
+ @click.option("--timeout", is_flag=True, help="Remove timeout setting")
186
+ @click.option("--all", is_flag=True, help="Remove all configuration")
187
+ @click.option(
188
+ "--output", "-o", type=click.Path(), help="Output file for remaining configuration"
189
+ )
190
+ @click.pass_context
191
+ def unset(ctx, api_url, api_key, timeout, all, output):
192
+ """Remove configuration values."""
193
+ try:
194
+ # Get current config
195
+ config_path = Path.home() / ".aip" / "config.yaml"
196
+
197
+ if not config_path.exists():
198
+ click.echo("āŒ No configuration file found.")
199
+ return
200
+
201
+ # Load current config
202
+ try:
203
+ import yaml
204
+
205
+ with open(config_path) as f:
206
+ current_config = yaml.safe_load(f) or {}
207
+ except Exception as e:
208
+ click.echo(f"āŒ Error loading configuration: {e}")
209
+ return
210
+
211
+ # Remove specified values
212
+ removed = False
213
+ if all:
214
+ # Remove all configuration
215
+ current_config = {}
216
+ removed = True
217
+ click.echo("āœ… All configuration removed")
218
+ else:
219
+ if api_url and "api_url" in current_config:
220
+ del current_config["api_url"]
221
+ removed = True
222
+ click.echo("āœ… API URL removed")
223
+
224
+ if api_key and "api_key" in current_config:
225
+ del current_config["api_key"]
226
+ removed = True
227
+ click.echo("āœ… API Key removed")
228
+
229
+ if timeout and "timeout" in current_config:
230
+ del current_config["timeout"]
231
+ removed = True
232
+ click.echo("āœ… Timeout removed")
233
+
234
+ if not removed:
235
+ click.echo(
236
+ "āŒ No configuration values specified for removal. Use --help for available options."
237
+ )
238
+ return
239
+
240
+ # Save updated config
241
+ if current_config:
242
+ try:
243
+ with open(config_path, "w") as f:
244
+ yaml.dump(current_config, f, default_flow_style=False)
245
+ click.echo(f"šŸ“„ Updated configuration saved to: {config_path}")
246
+ except Exception as e:
247
+ click.echo(f"āŒ Error saving configuration: {e}")
248
+ return
249
+ else:
250
+ # Remove config file if empty
251
+ try:
252
+ config_path.unlink()
253
+ click.echo(f"šŸ“„ Configuration file removed: {config_path}")
254
+ except Exception as e:
255
+ click.echo(f"āŒ Error removing configuration file: {e}")
256
+ return
257
+
258
+ # Save to output file if specified
259
+ if output:
260
+ output_path = Path(output)
261
+ with open(output_path, "w") as f:
262
+ json.dump(current_config, f, indent=2, default=str)
263
+ click.echo(f"šŸ“„ Remaining configuration saved to: {output_path}")
264
+
265
+ except Exception as e:
266
+ click.echo(f"āŒ Error removing configuration: {e}")
267
+
268
+
269
+ @config_group.command()
270
+ @click.option("--output", "-o", type=click.Path(), help="Output file for configuration")
271
+ @click.pass_context
272
+ def export(ctx, output):
273
+ """Export current configuration."""
274
+ try:
275
+ # Get config from all sources
276
+ config_path = Path.home() / ".aip" / "config.yaml"
277
+
278
+ # Load file config
279
+ file_config = {}
280
+ if config_path.exists():
281
+ try:
282
+ import yaml
283
+
284
+ with open(config_path) as f:
285
+ file_config = yaml.safe_load(f) or {}
286
+ except Exception:
287
+ file_config = {}
288
+
289
+ # Get environment config
290
+ env_config = {
291
+ "api_url": os.getenv("AIP_API_URL"),
292
+ "api_key": os.getenv("AIP_API_KEY"),
293
+ "timeout": os.getenv("AIP_TIMEOUT"),
294
+ }
295
+
296
+ # Combine configs (environment overrides file)
297
+ combined_config = {**file_config, **env_config}
298
+
299
+ # Remove None values
300
+ combined_config = {k: v for k, v in combined_config.items() if v is not None}
301
+
302
+ if not combined_config:
303
+ click.echo("āŒ No configuration found to export.")
304
+ return
305
+
306
+ # Display configuration
307
+ click.echo("šŸ“¤ Exported Configuration:")
308
+ click.echo("=" * 30)
309
+
310
+ for key, value in combined_config.items():
311
+ if key == "api_key" and value:
312
+ # Mask API key for display
313
+ masked_value = (
314
+ value[:8] + "..." + value[-4:] if len(value) > 12 else "***"
315
+ )
316
+ click.echo(f"{key}: {masked_value}")
317
+ else:
318
+ click.echo(f"{key}: {value}")
319
+
320
+ # Save to output file if specified
321
+ if output:
322
+ output_path = Path(output)
323
+ with open(output_path, "w") as f:
324
+ json.dump(combined_config, f, indent=2, default=str)
325
+ click.echo(f"\nšŸ“„ Configuration exported to: {output_path}")
326
+ else:
327
+ # Save to default location
328
+ export_path = Path.home() / ".aip" / "config_export.json"
329
+ export_path.parent.mkdir(parents=True, exist_ok=True)
330
+ with open(export_path, "w") as f:
331
+ json.dump(combined_config, f, indent=2, default=str)
332
+ click.echo(f"\nšŸ“„ Configuration exported to: {export_path}")
333
+
334
+ except Exception as e:
335
+ click.echo(f"āŒ Error exporting configuration: {e}")
336
+
337
+
338
+ @config_group.command()
339
+ @click.argument("config_file", type=click.Path(exists=True))
340
+ @click.option("--overwrite", is_flag=True, help="Overwrite existing configuration")
341
+ @click.option(
342
+ "--output", "-o", type=click.Path(), help="Output file for imported configuration"
343
+ )
344
+ @click.pass_context
345
+ def import_config(ctx, config_file, overwrite, output):
346
+ """Import configuration from a file."""
347
+ try:
348
+ config_path = Path(config_file)
349
+
350
+ # Load configuration from file
351
+ try:
352
+ if config_path.suffix.lower() in [".json", ".js"]:
353
+ with open(config_path) as f:
354
+ imported_config = json.load(f)
355
+ elif config_path.suffix.lower() in [".yaml", ".yml"]:
356
+ import yaml
357
+
358
+ with open(config_path) as f:
359
+ imported_config = yaml.safe_load(f)
360
+ else:
361
+ click.echo(f"āŒ Unsupported file format: {config_path.suffix}")
362
+ click.echo("Supported formats: .json, .yaml, .yml")
363
+ return
364
+ except Exception as e:
365
+ click.echo(f"āŒ Error loading configuration file: {e}")
366
+ return
367
+
368
+ if not imported_config:
369
+ click.echo("āŒ Configuration file is empty or invalid.")
370
+ return
371
+
372
+ # Get current config
373
+ current_config_path = Path.home() / ".aip" / "config.yaml"
374
+ current_config = {}
375
+
376
+ if current_config_path.exists() and not overwrite:
377
+ try:
378
+ import yaml
379
+
380
+ with open(current_config_path) as f:
381
+ current_config = yaml.safe_load(f) or {}
382
+ except Exception:
383
+ current_config = {}
384
+
385
+ # Merge configurations
386
+ merged_config = {**current_config, **imported_config}
387
+
388
+ # Save merged configuration
389
+ current_config_path.parent.mkdir(parents=True, exist_ok=True)
390
+ try:
391
+ import yaml
392
+
393
+ with open(current_config_path, "w") as f:
394
+ yaml.dump(merged_config, f, default_flow_style=False)
395
+ click.echo(f"āœ… Configuration imported and saved to: {current_config_path}")
396
+ except Exception as e:
397
+ click.echo(f"āŒ Error saving merged configuration: {e}")
398
+ return
399
+
400
+ # Display imported configuration
401
+ click.echo("\nšŸ“„ Imported Configuration:")
402
+ click.echo("=" * 30)
403
+
404
+ for key, value in imported_config.items():
405
+ if key == "api_key" and value:
406
+ # Mask API key for display
407
+ masked_value = (
408
+ value[:8] + "..." + value[-4:] if len(value) > 12 else "***"
409
+ )
410
+ click.echo(f"{key}: {masked_value}")
411
+ else:
412
+ click.echo(f"{key}: {value}")
413
+
414
+ # Save to output file if specified
415
+ if output:
416
+ output_path = Path(output)
417
+ with open(output_path, "w") as f:
418
+ json.dump(merged_config, f, indent=2, default=str)
419
+ click.echo(f"\nšŸ“„ Merged configuration saved to: {output_path}")
420
+
421
+ except Exception as e:
422
+ click.echo(f"āŒ Error importing configuration: {e}")
423
+
424
+
425
+ @config_group.command()
426
+ @click.pass_context
427
+ def validate(ctx):
428
+ """Validate current configuration."""
429
+ try:
430
+ # Get config from all sources
431
+ config_path = Path.home() / ".aip" / "config.yaml"
432
+
433
+ # Load file config
434
+ file_config = {}
435
+ if config_path.exists():
436
+ try:
437
+ import yaml
438
+
439
+ with open(config_path) as f:
440
+ file_config = yaml.safe_load(f) or {}
441
+ except Exception as e:
442
+ click.echo(f"āŒ Error loading config file: {e}")
443
+ file_config = {}
444
+
445
+ # Get environment config
446
+ env_config = {
447
+ "api_url": os.getenv("AIP_API_URL"),
448
+ "api_key": os.getenv("AIP_API_KEY"),
449
+ "timeout": os.getenv("AIP_TIMEOUT"),
450
+ }
451
+
452
+ # Combine configs (environment overrides file)
453
+ combined_config = {**file_config, **env_config}
454
+
455
+ # Remove None values
456
+ combined_config = {k: v for k, v in combined_config.items() if v is not None}
457
+
458
+ click.echo("šŸ” Validating Configuration")
459
+ click.echo("=" * 40)
460
+
461
+ # Check required fields
462
+ required_fields = ["api_url", "api_key"]
463
+ missing_fields = []
464
+
465
+ for field in required_fields:
466
+ if field not in combined_config:
467
+ missing_fields.append(field)
468
+ else:
469
+ click.echo(f"āœ… {field}: Set")
470
+
471
+ if missing_fields:
472
+ click.echo(f"āŒ Missing required fields: {', '.join(missing_fields)}")
473
+ click.echo("\nTo set these values, use:")
474
+ for field in missing_fields:
475
+ if field == "api_url":
476
+ click.echo(f" aip config set --{field} <your-api-url>")
477
+ elif field == "api_key":
478
+ click.echo(f" aip config set --{field} <your-api-key>")
479
+ return
480
+
481
+ # Validate API URL format
482
+ api_url = combined_config["api_url"]
483
+ if not api_url.startswith(("http://", "https://")):
484
+ click.echo("āš ļø Warning: API URL should start with http:// or https://")
485
+
486
+ # Validate timeout if present
487
+ if "timeout" in combined_config:
488
+ try:
489
+ timeout = float(combined_config["timeout"])
490
+ if timeout <= 0:
491
+ click.echo(f"āŒ Timeout must be positive, got: {timeout}")
492
+ return
493
+ click.echo(f"āœ… timeout: {timeout} seconds")
494
+ except ValueError:
495
+ click.echo(f"āŒ Invalid timeout value: {combined_config['timeout']}")
496
+ return
497
+
498
+ # Test connection if possible
499
+ click.echo("\nšŸ”Œ Testing Connection...")
500
+ try:
501
+ client = Client(
502
+ api_url=combined_config["api_url"],
503
+ api_key=combined_config["api_key"],
504
+ timeout=float(combined_config.get("timeout", 30.0)),
505
+ )
506
+
507
+ # Try to list resources to test connection
508
+ try:
509
+ agents = client.list_agents()
510
+ click.echo(f"āœ… Connection successful! Found {len(agents)} agents")
511
+ except Exception as e:
512
+ click.echo(f"āš ļø Connection established but API call failed: {e}")
513
+
514
+ client.close()
515
+
516
+ except Exception as e:
517
+ click.echo(f"āŒ Connection failed: {e}")
518
+ return
519
+
520
+ click.echo("\nšŸŽ‰ Configuration is valid!")
521
+
522
+ except Exception as e:
523
+ click.echo(f"āŒ Error validating configuration: {e}")
524
+
525
+
526
+ @config_group.command()
527
+ @click.pass_context
528
+ def init(ctx):
529
+ """Initialize configuration interactively."""
530
+ try:
531
+ click.echo("šŸš€ AIP SDK Configuration Initialization")
532
+ click.echo("=" * 50)
533
+ click.echo("This will help you set up your AIP SDK configuration.")
534
+ click.echo()
535
+
536
+ # Get configuration values interactively
537
+ api_url = click.prompt(
538
+ "Enter your AIP API URL", default="https://api.aiagentplatform.com"
539
+ )
540
+
541
+ api_key = click.prompt("Enter your AIP API Key", hide_input=True)
542
+
543
+ timeout = click.prompt(
544
+ "Enter request timeout in seconds", default=30.0, type=float
545
+ )
546
+
547
+ # Confirm configuration
548
+ click.echo("\nšŸ“‹ Configuration Summary:")
549
+ click.echo(f" API URL: {api_url}")
550
+ click.echo(
551
+ f" API Key: {api_key[:8]}...{api_key[-4:] if len(api_key) > 12 else '***'}"
552
+ )
553
+ click.echo(f" Timeout: {timeout} seconds")
554
+
555
+ if not click.confirm("\nIs this configuration correct?"):
556
+ click.echo("Configuration cancelled.")
557
+ return
558
+
559
+ # Save configuration
560
+ config_path = Path.home() / ".aip" / "config.yaml"
561
+ config_path.parent.mkdir(parents=True, exist_ok=True)
562
+
563
+ config_data = {"api_url": api_url, "api_key": api_key, "timeout": timeout}
564
+
565
+ try:
566
+ import yaml
567
+
568
+ with open(config_path, "w") as f:
569
+ yaml.dump(config_data, f, default_flow_style=False)
570
+ click.echo(f"\nāœ… Configuration saved to: {config_path}")
571
+ except Exception as e:
572
+ click.echo(f"āŒ Error saving configuration: {e}")
573
+ return
574
+
575
+ # Test connection
576
+ click.echo("\nšŸ”Œ Testing connection...")
577
+ try:
578
+ client = Client(api_url=api_url, api_key=api_key, timeout=timeout)
579
+ agents = client.list_agents()
580
+ click.echo(f"āœ… Connection successful! Found {len(agents)} agents")
581
+ client.close()
582
+ except Exception as e:
583
+ click.echo(f"āš ļø Configuration saved but connection test failed: {e}")
584
+ click.echo(
585
+ "You may need to check your API credentials or network connection."
586
+ )
587
+
588
+ click.echo("\nšŸŽ‰ Configuration initialization complete!")
589
+ click.echo("You can now use the AIP SDK CLI commands.")
590
+
591
+ except Exception as e:
592
+ click.echo(f"āŒ Error during configuration initialization: {e}")