glaip-sdk 0.0.4__py3-none-any.whl → 0.0.5__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.
- glaip_sdk/__init__.py +5 -5
- glaip_sdk/branding.py +18 -17
- glaip_sdk/cli/__init__.py +1 -1
- glaip_sdk/cli/agent_config.py +82 -0
- glaip_sdk/cli/commands/__init__.py +3 -3
- glaip_sdk/cli/commands/agents.py +570 -673
- glaip_sdk/cli/commands/configure.py +2 -2
- glaip_sdk/cli/commands/mcps.py +148 -143
- glaip_sdk/cli/commands/models.py +1 -1
- glaip_sdk/cli/commands/tools.py +250 -179
- glaip_sdk/cli/display.py +244 -0
- glaip_sdk/cli/io.py +106 -0
- glaip_sdk/cli/main.py +14 -18
- glaip_sdk/cli/resolution.py +59 -0
- glaip_sdk/cli/utils.py +305 -264
- glaip_sdk/cli/validators.py +235 -0
- glaip_sdk/client/__init__.py +3 -224
- glaip_sdk/client/agents.py +631 -191
- glaip_sdk/client/base.py +66 -4
- glaip_sdk/client/main.py +226 -0
- glaip_sdk/client/mcps.py +143 -18
- glaip_sdk/client/tools.py +146 -11
- glaip_sdk/config/constants.py +10 -1
- glaip_sdk/models.py +42 -2
- glaip_sdk/rich_components.py +29 -0
- glaip_sdk/utils/__init__.py +18 -171
- glaip_sdk/utils/agent_config.py +181 -0
- glaip_sdk/utils/client_utils.py +159 -79
- glaip_sdk/utils/display.py +100 -0
- glaip_sdk/utils/general.py +94 -0
- glaip_sdk/utils/import_export.py +140 -0
- glaip_sdk/utils/rendering/formatting.py +6 -1
- glaip_sdk/utils/rendering/renderer/__init__.py +67 -8
- glaip_sdk/utils/rendering/renderer/base.py +340 -247
- glaip_sdk/utils/rendering/renderer/debug.py +3 -2
- glaip_sdk/utils/rendering/renderer/panels.py +11 -10
- glaip_sdk/utils/rendering/steps.py +1 -1
- glaip_sdk/utils/resource_refs.py +192 -0
- glaip_sdk/utils/rich_utils.py +29 -0
- glaip_sdk/utils/serialization.py +285 -0
- glaip_sdk/utils/validation.py +273 -0
- {glaip_sdk-0.0.4.dist-info → glaip_sdk-0.0.5.dist-info}/METADATA +6 -5
- glaip_sdk-0.0.5.dist-info/RECORD +55 -0
- glaip_sdk/cli/commands/init.py +0 -93
- glaip_sdk-0.0.4.dist-info/RECORD +0 -41
- {glaip_sdk-0.0.4.dist-info → glaip_sdk-0.0.5.dist-info}/WHEEL +0 -0
- {glaip_sdk-0.0.4.dist-info → glaip_sdk-0.0.5.dist-info}/entry_points.txt +0 -0
glaip_sdk/cli/display.py
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"""CLI display utilities for success/failure panels and Rich renderers.
|
|
2
|
+
|
|
3
|
+
This module handles all display-related functionality for CLI commands,
|
|
4
|
+
including success messages, error handling, and output formatting.
|
|
5
|
+
|
|
6
|
+
Authors:
|
|
7
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
import click
|
|
14
|
+
from rich.console import Console
|
|
15
|
+
from rich.panel import Panel
|
|
16
|
+
from rich.text import Text
|
|
17
|
+
|
|
18
|
+
from glaip_sdk.rich_components import AIPPanel
|
|
19
|
+
|
|
20
|
+
console = Console()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def display_creation_success(
|
|
24
|
+
resource_type: str, resource_name: str, resource_id: str, **additional_fields
|
|
25
|
+
) -> Panel:
|
|
26
|
+
"""Create standardized success message for resource creation.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
resource_type: Type of resource (e.g., "Agent", "Tool", "MCP")
|
|
30
|
+
resource_name: Name of the created resource
|
|
31
|
+
resource_id: ID of the created resource
|
|
32
|
+
**additional_fields: Additional fields to display
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Rich Panel object for display
|
|
36
|
+
"""
|
|
37
|
+
# Build additional fields display
|
|
38
|
+
fields_display = ""
|
|
39
|
+
if additional_fields:
|
|
40
|
+
fields_display = "\n" + "\n".join(
|
|
41
|
+
f"{key}: {value}" for key, value in additional_fields.items()
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
return AIPPanel(
|
|
45
|
+
f"[green]✅ {resource_type} '{resource_name}' created successfully![/green]\n\n"
|
|
46
|
+
f"ID: {resource_id}{fields_display}",
|
|
47
|
+
title=f"🤖 {resource_type} Created",
|
|
48
|
+
border_style="green",
|
|
49
|
+
padding=(0, 1),
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def display_update_success(resource_type: str, resource_name: str) -> Text:
|
|
54
|
+
"""Create standardized success message for resource update.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
resource_type: Type of resource (e.g., "Agent", "Tool", "MCP")
|
|
58
|
+
resource_name: Name of the updated resource
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Rich Text object for display
|
|
62
|
+
"""
|
|
63
|
+
return Text(
|
|
64
|
+
f"[green]✅ {resource_type} '{resource_name}' updated successfully[/green]"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def display_deletion_success(resource_type: str, resource_name: str) -> Text:
|
|
69
|
+
"""Create standardized success message for resource deletion.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
resource_type: Type of resource (e.g., "Agent", "Tool", "MCP")
|
|
73
|
+
resource_name: Name of the deleted resource
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Rich Text object for display
|
|
77
|
+
"""
|
|
78
|
+
return Text(
|
|
79
|
+
f"[green]✅ {resource_type} '{resource_name}' deleted successfully[/green]"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def display_api_error(error: Exception, operation: str = "operation") -> None:
|
|
84
|
+
"""Display standardized API error message.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
error: The exception that occurred
|
|
88
|
+
operation: Description of the operation that failed
|
|
89
|
+
"""
|
|
90
|
+
error_type = type(error).__name__
|
|
91
|
+
console.print(Text(f"[red]Error during {operation}: {error}[/red]"))
|
|
92
|
+
console.print(Text(f"[dim]Error type: {error_type}[/dim]"))
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def print_api_error(e: Exception) -> None:
|
|
96
|
+
"""Print API error with consistent formatting for both JSON and Rich views.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
e: The exception to format and display
|
|
100
|
+
|
|
101
|
+
Notes:
|
|
102
|
+
- Extracts status_code, error_type, and payload from APIError exceptions
|
|
103
|
+
- Provides consistent error reporting across CLI commands
|
|
104
|
+
- Handles both JSON and Rich output formats
|
|
105
|
+
"""
|
|
106
|
+
if hasattr(e, "__dict__"): # Check if it's an APIError-like object
|
|
107
|
+
error_info = {
|
|
108
|
+
"error": str(e),
|
|
109
|
+
"status_code": getattr(e, "status_code", None),
|
|
110
|
+
"error_type": getattr(e, "error_type", None),
|
|
111
|
+
"details": getattr(e, "payload", None),
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
# Filter out None values
|
|
115
|
+
error_info = {k: v for k, v in error_info.items() if v is not None}
|
|
116
|
+
|
|
117
|
+
# For JSON view, just return the structured error
|
|
118
|
+
# (CLI commands handle the JSON formatting)
|
|
119
|
+
if hasattr(e, "status_code"):
|
|
120
|
+
console.print(f"[red]API Error: {e}[/red]")
|
|
121
|
+
if hasattr(e, "status_code"):
|
|
122
|
+
console.print(f"[yellow]Status: {e.status_code}[/yellow]")
|
|
123
|
+
if hasattr(e, "payload"):
|
|
124
|
+
console.print(f"[yellow]Details: {e.payload}[/yellow]")
|
|
125
|
+
else:
|
|
126
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
127
|
+
else:
|
|
128
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
_MISSING = object()
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def build_resource_result_data(resource: Any, fields: list[str]) -> dict[str, Any]:
|
|
135
|
+
"""Return a normalized mapping of ``fields`` extracted from ``resource``."""
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
field: _normalise_field_value(field, _safe_get_attr(resource, field))
|
|
139
|
+
for field in fields
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _safe_get_attr(resource: Any, field: str) -> Any:
|
|
144
|
+
try:
|
|
145
|
+
return getattr(resource, field, _MISSING)
|
|
146
|
+
except Exception:
|
|
147
|
+
return _MISSING
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _normalise_field_value(field: str, value: Any) -> Any:
|
|
151
|
+
if value is _MISSING:
|
|
152
|
+
return "N/A"
|
|
153
|
+
if hasattr(value, "_mock_name"):
|
|
154
|
+
return "N/A"
|
|
155
|
+
if field == "id":
|
|
156
|
+
return str(value)
|
|
157
|
+
return value
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def handle_json_output(
|
|
161
|
+
ctx, data: Any = None, error: Exception = None
|
|
162
|
+
) -> None: # pragma: no cover - formatting covered via integration tests
|
|
163
|
+
"""Handle JSON output format for CLI commands.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
ctx: Click context
|
|
167
|
+
data: Data to output (for successful operations)
|
|
168
|
+
error: Error to output (for failed operations)
|
|
169
|
+
"""
|
|
170
|
+
ctx_obj = getattr(ctx, "obj", {}) if ctx is not None else {}
|
|
171
|
+
if not isinstance(ctx_obj, dict):
|
|
172
|
+
ctx_obj = {}
|
|
173
|
+
|
|
174
|
+
if ctx_obj.get("view") == "json":
|
|
175
|
+
if error:
|
|
176
|
+
output_data = {"error": str(error)}
|
|
177
|
+
# Add additional error details if available
|
|
178
|
+
if hasattr(error, "status_code"):
|
|
179
|
+
output_data["status_code"] = error.status_code
|
|
180
|
+
if hasattr(error, "error_type"):
|
|
181
|
+
output_data["error_type"] = error.error_type
|
|
182
|
+
if hasattr(error, "payload"):
|
|
183
|
+
output_data["details"] = error.payload
|
|
184
|
+
else:
|
|
185
|
+
output_data = data if data is not None else {"success": True}
|
|
186
|
+
|
|
187
|
+
click.echo(json.dumps(output_data, indent=2, default=str))
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def handle_rich_output(ctx, rich_content: Any = None) -> None:
|
|
191
|
+
"""Handle Rich output format for CLI commands.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
ctx: Click context
|
|
195
|
+
rich_content: Rich content to display
|
|
196
|
+
"""
|
|
197
|
+
ctx_obj = getattr(ctx, "obj", {}) if ctx is not None else {}
|
|
198
|
+
if not isinstance(ctx_obj, dict):
|
|
199
|
+
ctx_obj = {}
|
|
200
|
+
|
|
201
|
+
if ctx_obj.get("view") != "json" and rich_content:
|
|
202
|
+
console.print(rich_content)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def display_confirmation_prompt(resource_type: str, resource_name: str) -> bool:
|
|
206
|
+
"""Display standardized confirmation prompt for destructive operations.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
resource_type: Type of resource (e.g., "Agent", "Tool", "MCP")
|
|
210
|
+
resource_name: Name of the resource
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
True if user confirms, False otherwise
|
|
214
|
+
"""
|
|
215
|
+
if not click.confirm(
|
|
216
|
+
f"Are you sure you want to delete {resource_type.lower()} '{resource_name}'?"
|
|
217
|
+
):
|
|
218
|
+
if console.is_terminal:
|
|
219
|
+
console.print(Text("Deletion cancelled."))
|
|
220
|
+
return False
|
|
221
|
+
return True
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def display_agent_run_suggestions(agent: Any) -> Panel:
|
|
225
|
+
"""Return a panel with post-creation suggestions for an agent."""
|
|
226
|
+
|
|
227
|
+
return AIPPanel(
|
|
228
|
+
f"[bold blue]💡 Next Steps:[/bold blue]\n\n"
|
|
229
|
+
f"🚀 Run this agent:\n"
|
|
230
|
+
f' [green]aip agents run {agent.id} "Your message here"[/green]\n\n'
|
|
231
|
+
f"📋 Or use the agent name:\n"
|
|
232
|
+
f' [green]aip agents run "{agent.name}" "Your message here"[/green]\n\n'
|
|
233
|
+
f"🔧 Available options:\n"
|
|
234
|
+
f" [dim]--chat-history[/dim] Include previous conversation\n"
|
|
235
|
+
f" [dim]--file[/dim] Attach files\n"
|
|
236
|
+
f" [dim]--input[/dim] Alternative input method\n"
|
|
237
|
+
f" [dim]--timeout[/dim] Set execution timeout\n"
|
|
238
|
+
f" [dim]--save[/dim] Save transcript to file\n"
|
|
239
|
+
f" [dim]--verbose[/dim] Show detailed execution\n\n"
|
|
240
|
+
f"💡 [dim]Input text can be positional OR use --input flag (both work!)[/dim]",
|
|
241
|
+
title="🤖 Ready to Run Agent",
|
|
242
|
+
border_style="blue",
|
|
243
|
+
padding=(0, 1),
|
|
244
|
+
)
|
glaip_sdk/cli/io.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""CLI I/O utilities for file import/export orchestration.
|
|
2
|
+
|
|
3
|
+
This module handles file operations and network requests for CLI commands,
|
|
4
|
+
wrapping core serialization utilities with Click-friendly error handling.
|
|
5
|
+
|
|
6
|
+
Authors:
|
|
7
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
import click
|
|
14
|
+
|
|
15
|
+
from glaip_sdk.utils.serialization import (
|
|
16
|
+
collect_attributes_for_export,
|
|
17
|
+
load_resource_from_file,
|
|
18
|
+
write_resource_export,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def load_resource_from_file_with_validation(
|
|
23
|
+
file_path: Path, resource_type: str
|
|
24
|
+
) -> dict[str, Any]:
|
|
25
|
+
"""Load resource data from JSON or YAML file with CLI-friendly error handling.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
file_path: Path to the file
|
|
29
|
+
resource_type: Type of resource (for error messages)
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Dictionary with resource data
|
|
33
|
+
|
|
34
|
+
Raises:
|
|
35
|
+
click.ClickException: If file operations fail
|
|
36
|
+
"""
|
|
37
|
+
try:
|
|
38
|
+
return load_resource_from_file(file_path)
|
|
39
|
+
except FileNotFoundError:
|
|
40
|
+
raise click.ClickException(f"File not found: {file_path}")
|
|
41
|
+
except ValueError as e:
|
|
42
|
+
raise click.ClickException(f"Invalid {resource_type.lower()} file format: {e}")
|
|
43
|
+
except Exception as e:
|
|
44
|
+
raise click.ClickException(f"Failed to load {resource_type.lower()} file: {e}")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def export_resource_to_file_with_validation(
|
|
48
|
+
resource: Any, file_path: Path, format: str = "json"
|
|
49
|
+
) -> None:
|
|
50
|
+
"""Export resource to file with CLI-friendly error handling.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
resource: Resource object to export
|
|
54
|
+
file_path: Path to export file
|
|
55
|
+
format: Export format ("json" or "yaml")
|
|
56
|
+
|
|
57
|
+
Raises:
|
|
58
|
+
click.ClickException: If export operations fail
|
|
59
|
+
"""
|
|
60
|
+
try:
|
|
61
|
+
# Get all available resource attributes dynamically
|
|
62
|
+
export_data = collect_attributes_for_export(resource)
|
|
63
|
+
write_resource_export(file_path, export_data, format)
|
|
64
|
+
except Exception as e:
|
|
65
|
+
raise click.ClickException(f"Failed to export resource: {e}")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def fetch_raw_resource_details(client, resource, resource_type: str):
|
|
69
|
+
"""Fetch raw resource details directly from API to preserve ALL fields.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
client: API client
|
|
73
|
+
resource: Resource object
|
|
74
|
+
resource_type: Type of resource ("agents", "tools", "mcps")
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Raw API response data or None if failed
|
|
78
|
+
|
|
79
|
+
Notes:
|
|
80
|
+
This is CLI-specific functionality for displaying comprehensive resource details.
|
|
81
|
+
"""
|
|
82
|
+
from rich.console import Console
|
|
83
|
+
|
|
84
|
+
console = Console()
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
resource_id = str(getattr(resource, "id", "")).strip()
|
|
88
|
+
if resource_id:
|
|
89
|
+
# Make direct API call to get raw response
|
|
90
|
+
response = client.http_client.get(f"/{resource_type}/{resource_id}")
|
|
91
|
+
response.raise_for_status()
|
|
92
|
+
raw_response = response.json()
|
|
93
|
+
|
|
94
|
+
# If it's a wrapped response (success/data/message), extract the data
|
|
95
|
+
if isinstance(raw_response, dict) and "data" in raw_response:
|
|
96
|
+
return raw_response["data"]
|
|
97
|
+
else:
|
|
98
|
+
# Direct response
|
|
99
|
+
return raw_response
|
|
100
|
+
except Exception as e:
|
|
101
|
+
console.print(
|
|
102
|
+
f"[yellow]Failed to fetch raw {resource_type} details: {e}[/yellow]"
|
|
103
|
+
)
|
|
104
|
+
# Fall back to regular method
|
|
105
|
+
return None
|
|
106
|
+
return None
|
glaip_sdk/cli/main.py
CHANGED
|
@@ -10,8 +10,6 @@ import sys
|
|
|
10
10
|
|
|
11
11
|
import click
|
|
12
12
|
from rich.console import Console
|
|
13
|
-
from rich.panel import Panel
|
|
14
|
-
from rich.table import Table
|
|
15
13
|
|
|
16
14
|
from glaip_sdk import Client
|
|
17
15
|
from glaip_sdk._version import __version__ as _SDK_VERSION
|
|
@@ -22,11 +20,11 @@ from glaip_sdk.cli.commands.configure import (
|
|
|
22
20
|
configure_command,
|
|
23
21
|
load_config,
|
|
24
22
|
)
|
|
25
|
-
from glaip_sdk.cli.commands.init import init_command
|
|
26
23
|
from glaip_sdk.cli.commands.mcps import mcps_group
|
|
27
24
|
from glaip_sdk.cli.commands.models import models_group
|
|
28
25
|
from glaip_sdk.cli.commands.tools import tools_group
|
|
29
26
|
from glaip_sdk.config.constants import DEFAULT_AGENT_RUN_TIMEOUT
|
|
27
|
+
from glaip_sdk.rich_components import AIPPanel, AIPTable
|
|
30
28
|
|
|
31
29
|
|
|
32
30
|
@click.group()
|
|
@@ -44,15 +42,14 @@ from glaip_sdk.config.constants import DEFAULT_AGENT_RUN_TIMEOUT
|
|
|
44
42
|
@click.option("--no-tty", is_flag=True, help="Disable TTY renderer")
|
|
45
43
|
@click.pass_context
|
|
46
44
|
def main(ctx, api_url, api_key, timeout, view, no_tty):
|
|
47
|
-
"""AIP SDK Command Line Interface.
|
|
45
|
+
"""GL AIP SDK Command Line Interface.
|
|
48
46
|
|
|
49
|
-
A comprehensive CLI for managing
|
|
47
|
+
A comprehensive CLI for managing GL AIP resources including
|
|
50
48
|
agents, tools, MCPs, and more.
|
|
51
49
|
|
|
52
50
|
Examples:
|
|
53
51
|
aip version # Show detailed version info
|
|
54
52
|
aip configure # Configure credentials
|
|
55
|
-
aip init # Initialize configuration
|
|
56
53
|
aip agents list # List all agents
|
|
57
54
|
aip tools create my_tool.py # Create a new tool
|
|
58
55
|
aip agents run my-agent "Hello world" # Run an agent
|
|
@@ -74,7 +71,6 @@ main.add_command(config_group)
|
|
|
74
71
|
main.add_command(tools_group)
|
|
75
72
|
main.add_command(mcps_group)
|
|
76
73
|
main.add_command(models_group)
|
|
77
|
-
main.add_command(init_command)
|
|
78
74
|
|
|
79
75
|
# Add top-level commands
|
|
80
76
|
main.add_command(configure_command)
|
|
@@ -117,7 +113,7 @@ def status(ctx):
|
|
|
117
113
|
|
|
118
114
|
if not config.get("api_url") or not config.get("api_key"):
|
|
119
115
|
console.print(
|
|
120
|
-
|
|
116
|
+
AIPPanel(
|
|
121
117
|
"[bold red]❌ Configuration incomplete[/bold red]\n\n"
|
|
122
118
|
f"🔍 Current config:\n"
|
|
123
119
|
f" • API URL: {config.get('api_url', 'Not set')}\n"
|
|
@@ -145,7 +141,7 @@ def status(ctx):
|
|
|
145
141
|
mcps = client.list_mcps()
|
|
146
142
|
|
|
147
143
|
# Create status table
|
|
148
|
-
table =
|
|
144
|
+
table = AIPTable(title="🔗 GL AIP Status")
|
|
149
145
|
table.add_column("Resource", style="cyan", width=15)
|
|
150
146
|
table.add_column("Count", style="green", width=10)
|
|
151
147
|
table.add_column("Status", style="green", width=15)
|
|
@@ -155,8 +151,8 @@ def status(ctx):
|
|
|
155
151
|
table.add_row("MCPs", str(len(mcps)), "✅ Available")
|
|
156
152
|
|
|
157
153
|
console.print(
|
|
158
|
-
|
|
159
|
-
f"[bold green]✅ Connected to AIP
|
|
154
|
+
AIPPanel(
|
|
155
|
+
f"[bold green]✅ Connected to GL AIP[/bold green]\n"
|
|
160
156
|
f"🔗 API URL: {client.api_url}\n"
|
|
161
157
|
f"🤖 Agent Run Timeout: {DEFAULT_AGENT_RUN_TIMEOUT}s",
|
|
162
158
|
title="🚀 Connection Status",
|
|
@@ -168,7 +164,7 @@ def status(ctx):
|
|
|
168
164
|
|
|
169
165
|
except Exception as e:
|
|
170
166
|
console.print(
|
|
171
|
-
|
|
167
|
+
AIPPanel(
|
|
172
168
|
f"[bold yellow]⚠️ Connection established but API call failed[/bold yellow]\n"
|
|
173
169
|
f"🔗 API URL: {client.api_url}\n"
|
|
174
170
|
f"❌ Error: {e}\n\n"
|
|
@@ -185,7 +181,7 @@ def status(ctx):
|
|
|
185
181
|
|
|
186
182
|
except Exception as e:
|
|
187
183
|
console.print(
|
|
188
|
-
|
|
184
|
+
AIPPanel(
|
|
189
185
|
f"[bold red]❌ Connection failed[/bold red]\n\n"
|
|
190
186
|
f"🔍 Error: {e}\n\n"
|
|
191
187
|
f"💡 Troubleshooting steps:\n"
|
|
@@ -225,7 +221,7 @@ def update(check_only: bool, force: bool):
|
|
|
225
221
|
|
|
226
222
|
if check_only:
|
|
227
223
|
console.print(
|
|
228
|
-
|
|
224
|
+
AIPPanel(
|
|
229
225
|
"[bold blue]🔍 Checking for updates...[/bold blue]\n\n"
|
|
230
226
|
"💡 To install updates, run: aip update",
|
|
231
227
|
title="📋 Update Check",
|
|
@@ -235,7 +231,7 @@ def update(check_only: bool, force: bool):
|
|
|
235
231
|
return
|
|
236
232
|
|
|
237
233
|
console.print(
|
|
238
|
-
|
|
234
|
+
AIPPanel(
|
|
239
235
|
"[bold blue]🔄 Updating AIP SDK...[/bold blue]\n\n"
|
|
240
236
|
"📦 This will update the package from PyPI\n"
|
|
241
237
|
"💡 Use --check-only to just check for updates",
|
|
@@ -260,7 +256,7 @@ def update(check_only: bool, force: bool):
|
|
|
260
256
|
subprocess.run(cmd, capture_output=True, text=True, check=True)
|
|
261
257
|
|
|
262
258
|
console.print(
|
|
263
|
-
|
|
259
|
+
AIPPanel(
|
|
264
260
|
"[bold green]✅ Update successful![/bold green]\n\n"
|
|
265
261
|
"🔄 AIP SDK has been updated to the latest version\n"
|
|
266
262
|
"💡 Restart your terminal or run 'aip --version' to verify",
|
|
@@ -281,7 +277,7 @@ def update(check_only: bool, force: bool):
|
|
|
281
277
|
|
|
282
278
|
except subprocess.CalledProcessError as e:
|
|
283
279
|
console.print(
|
|
284
|
-
|
|
280
|
+
AIPPanel(
|
|
285
281
|
f"[bold red]❌ Update failed[/bold red]\n\n"
|
|
286
282
|
f"🔍 Error: {e.stderr}\n\n"
|
|
287
283
|
"💡 Troubleshooting:\n"
|
|
@@ -297,7 +293,7 @@ def update(check_only: bool, force: bool):
|
|
|
297
293
|
|
|
298
294
|
except ImportError:
|
|
299
295
|
console.print(
|
|
300
|
-
|
|
296
|
+
AIPPanel(
|
|
301
297
|
"[bold red]❌ Rich library not available[/bold red]\n\n"
|
|
302
298
|
"💡 Install rich: pip install rich\n"
|
|
303
299
|
" Then try: aip update",
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""CLI resource resolution utilities for handling ID/name references.
|
|
2
|
+
|
|
3
|
+
This module provides CLI-specific resource resolution functionality,
|
|
4
|
+
including interactive pickers and ambiguity handling.
|
|
5
|
+
|
|
6
|
+
Authors:
|
|
7
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from collections.abc import Callable
|
|
11
|
+
|
|
12
|
+
import click
|
|
13
|
+
|
|
14
|
+
from glaip_sdk.cli.utils import resolve_resource
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def resolve_resource_reference(
|
|
18
|
+
ctx,
|
|
19
|
+
_client,
|
|
20
|
+
reference: str,
|
|
21
|
+
resource_type: str,
|
|
22
|
+
get_by_id_func: Callable,
|
|
23
|
+
find_by_name_func: Callable,
|
|
24
|
+
label: str,
|
|
25
|
+
select: int = None,
|
|
26
|
+
interface_preference: str | None = None,
|
|
27
|
+
):
|
|
28
|
+
"""Resolve resource reference (ID or name) with ambiguity handling.
|
|
29
|
+
|
|
30
|
+
This is a common pattern used across all resource types.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
ctx: Click context
|
|
34
|
+
client: API client
|
|
35
|
+
reference: Resource ID or name
|
|
36
|
+
resource_type: Type of resource
|
|
37
|
+
get_by_id_func: Function to get resource by ID
|
|
38
|
+
find_by_name_func: Function to find resources by name
|
|
39
|
+
label: Label for error messages
|
|
40
|
+
select: Selection index for ambiguous matches
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
Resolved resource object
|
|
44
|
+
|
|
45
|
+
Raises:
|
|
46
|
+
click.ClickException: If resolution fails
|
|
47
|
+
"""
|
|
48
|
+
try:
|
|
49
|
+
return resolve_resource(
|
|
50
|
+
ctx,
|
|
51
|
+
reference,
|
|
52
|
+
get_by_id=get_by_id_func,
|
|
53
|
+
find_by_name=find_by_name_func,
|
|
54
|
+
label=label,
|
|
55
|
+
select=select,
|
|
56
|
+
interface_preference=interface_preference,
|
|
57
|
+
)
|
|
58
|
+
except Exception as e:
|
|
59
|
+
raise click.ClickException(f"Failed to resolve {resource_type.lower()}: {e}")
|