glaip-sdk 0.0.3__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.
Files changed (47) hide show
  1. glaip_sdk/__init__.py +5 -5
  2. glaip_sdk/branding.py +146 -0
  3. glaip_sdk/cli/__init__.py +1 -1
  4. glaip_sdk/cli/agent_config.py +82 -0
  5. glaip_sdk/cli/commands/__init__.py +3 -3
  6. glaip_sdk/cli/commands/agents.py +786 -271
  7. glaip_sdk/cli/commands/configure.py +19 -19
  8. glaip_sdk/cli/commands/mcps.py +151 -141
  9. glaip_sdk/cli/commands/models.py +1 -1
  10. glaip_sdk/cli/commands/tools.py +252 -178
  11. glaip_sdk/cli/display.py +244 -0
  12. glaip_sdk/cli/io.py +106 -0
  13. glaip_sdk/cli/main.py +27 -20
  14. glaip_sdk/cli/resolution.py +59 -0
  15. glaip_sdk/cli/utils.py +372 -213
  16. glaip_sdk/cli/validators.py +235 -0
  17. glaip_sdk/client/__init__.py +3 -224
  18. glaip_sdk/client/agents.py +632 -171
  19. glaip_sdk/client/base.py +66 -4
  20. glaip_sdk/client/main.py +226 -0
  21. glaip_sdk/client/mcps.py +143 -18
  22. glaip_sdk/client/tools.py +327 -104
  23. glaip_sdk/config/constants.py +10 -1
  24. glaip_sdk/models.py +43 -3
  25. glaip_sdk/rich_components.py +29 -0
  26. glaip_sdk/utils/__init__.py +18 -171
  27. glaip_sdk/utils/agent_config.py +181 -0
  28. glaip_sdk/utils/client_utils.py +159 -79
  29. glaip_sdk/utils/display.py +100 -0
  30. glaip_sdk/utils/general.py +94 -0
  31. glaip_sdk/utils/import_export.py +140 -0
  32. glaip_sdk/utils/rendering/formatting.py +6 -1
  33. glaip_sdk/utils/rendering/renderer/__init__.py +67 -8
  34. glaip_sdk/utils/rendering/renderer/base.py +340 -247
  35. glaip_sdk/utils/rendering/renderer/debug.py +3 -2
  36. glaip_sdk/utils/rendering/renderer/panels.py +11 -10
  37. glaip_sdk/utils/rendering/steps.py +1 -1
  38. glaip_sdk/utils/resource_refs.py +192 -0
  39. glaip_sdk/utils/rich_utils.py +29 -0
  40. glaip_sdk/utils/serialization.py +285 -0
  41. glaip_sdk/utils/validation.py +273 -0
  42. {glaip_sdk-0.0.3.dist-info → glaip_sdk-0.0.5.dist-info}/METADATA +6 -5
  43. glaip_sdk-0.0.5.dist-info/RECORD +55 -0
  44. glaip_sdk/cli/commands/init.py +0 -177
  45. glaip_sdk-0.0.3.dist-info/RECORD +0 -40
  46. {glaip_sdk-0.0.3.dist-info → glaip_sdk-0.0.5.dist-info}/WHEEL +0 -0
  47. {glaip_sdk-0.0.3.dist-info → glaip_sdk-0.0.5.dist-info}/entry_points.txt +0 -0
@@ -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,22 +10,21 @@ 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
16
+ from glaip_sdk.branding import AIPBranding
18
17
  from glaip_sdk.cli.commands.agents import agents_group
19
18
  from glaip_sdk.cli.commands.configure import (
20
19
  config_group,
21
20
  configure_command,
22
21
  load_config,
23
22
  )
24
- from glaip_sdk.cli.commands.init import init_command
25
23
  from glaip_sdk.cli.commands.mcps import mcps_group
26
24
  from glaip_sdk.cli.commands.models import models_group
27
25
  from glaip_sdk.cli.commands.tools import tools_group
28
26
  from glaip_sdk.config.constants import DEFAULT_AGENT_RUN_TIMEOUT
27
+ from glaip_sdk.rich_components import AIPPanel, AIPTable
29
28
 
30
29
 
31
30
  @click.group()
@@ -43,17 +42,17 @@ from glaip_sdk.config.constants import DEFAULT_AGENT_RUN_TIMEOUT
43
42
  @click.option("--no-tty", is_flag=True, help="Disable TTY renderer")
44
43
  @click.pass_context
45
44
  def main(ctx, api_url, api_key, timeout, view, no_tty):
46
- """AIP SDK Command Line Interface.
45
+ """GL AIP SDK Command Line Interface.
47
46
 
48
- A comprehensive CLI for managing AI Agent Platform resources including
47
+ A comprehensive CLI for managing GL AIP resources including
49
48
  agents, tools, MCPs, and more.
50
49
 
51
50
  Examples:
51
+ aip version # Show detailed version info
52
52
  aip configure # Configure credentials
53
53
  aip agents list # List all agents
54
54
  aip tools create my_tool.py # Create a new tool
55
- aip agents run my-agent "Hello" # Run an agent
56
- aip init # Initialize configuration
55
+ aip agents run my-agent "Hello world" # Run an agent
57
56
  """
58
57
 
59
58
  # Store configuration in context
@@ -72,7 +71,6 @@ main.add_command(config_group)
72
71
  main.add_command(tools_group)
73
72
  main.add_command(mcps_group)
74
73
  main.add_command(models_group)
75
- main.add_command(init_command)
76
74
 
77
75
  # Add top-level commands
78
76
  main.add_command(configure_command)
@@ -89,6 +87,12 @@ def status(ctx):
89
87
  try:
90
88
  console = Console()
91
89
 
90
+ # Display AIP status banner
91
+ branding = AIPBranding.create_from_sdk(
92
+ sdk_version=_SDK_VERSION, package_name="glaip-sdk"
93
+ )
94
+ branding.display_status_banner("ready")
95
+
92
96
  # Load config from file and merge with context
93
97
  file_config = load_config()
94
98
  context_config = ctx.obj or {}
@@ -109,7 +113,7 @@ def status(ctx):
109
113
 
110
114
  if not config.get("api_url") or not config.get("api_key"):
111
115
  console.print(
112
- Panel(
116
+ AIPPanel(
113
117
  "[bold red]❌ Configuration incomplete[/bold red]\n\n"
114
118
  f"🔍 Current config:\n"
115
119
  f" • API URL: {config.get('api_url', 'Not set')}\n"
@@ -137,7 +141,7 @@ def status(ctx):
137
141
  mcps = client.list_mcps()
138
142
 
139
143
  # Create status table
140
- table = Table(title="🔗 AIP Platform Status")
144
+ table = AIPTable(title="🔗 GL AIP Status")
141
145
  table.add_column("Resource", style="cyan", width=15)
142
146
  table.add_column("Count", style="green", width=10)
143
147
  table.add_column("Status", style="green", width=15)
@@ -147,8 +151,8 @@ def status(ctx):
147
151
  table.add_row("MCPs", str(len(mcps)), "✅ Available")
148
152
 
149
153
  console.print(
150
- Panel(
151
- f"[bold green]✅ Connected to AIP Platform[/bold green]\n"
154
+ AIPPanel(
155
+ f"[bold green]✅ Connected to GL AIP[/bold green]\n"
152
156
  f"🔗 API URL: {client.api_url}\n"
153
157
  f"🤖 Agent Run Timeout: {DEFAULT_AGENT_RUN_TIMEOUT}s",
154
158
  title="🚀 Connection Status",
@@ -160,7 +164,7 @@ def status(ctx):
160
164
 
161
165
  except Exception as e:
162
166
  console.print(
163
- Panel(
167
+ AIPPanel(
164
168
  f"[bold yellow]⚠️ Connection established but API call failed[/bold yellow]\n"
165
169
  f"🔗 API URL: {client.api_url}\n"
166
170
  f"❌ Error: {e}\n\n"
@@ -177,7 +181,7 @@ def status(ctx):
177
181
 
178
182
  except Exception as e:
179
183
  console.print(
180
- Panel(
184
+ AIPPanel(
181
185
  f"[bold red]❌ Connection failed[/bold red]\n\n"
182
186
  f"🔍 Error: {e}\n\n"
183
187
  f"💡 Troubleshooting steps:\n"
@@ -195,7 +199,10 @@ def status(ctx):
195
199
  @main.command()
196
200
  def version():
197
201
  """Show version information."""
198
- click.echo(f"aip version {_SDK_VERSION}")
202
+ branding = AIPBranding.create_from_sdk(
203
+ sdk_version=_SDK_VERSION, package_name="glaip-sdk"
204
+ )
205
+ branding.display_version_panel()
199
206
 
200
207
 
201
208
  @main.command()
@@ -214,7 +221,7 @@ def update(check_only: bool, force: bool):
214
221
 
215
222
  if check_only:
216
223
  console.print(
217
- Panel(
224
+ AIPPanel(
218
225
  "[bold blue]🔍 Checking for updates...[/bold blue]\n\n"
219
226
  "💡 To install updates, run: aip update",
220
227
  title="📋 Update Check",
@@ -224,7 +231,7 @@ def update(check_only: bool, force: bool):
224
231
  return
225
232
 
226
233
  console.print(
227
- Panel(
234
+ AIPPanel(
228
235
  "[bold blue]🔄 Updating AIP SDK...[/bold blue]\n\n"
229
236
  "📦 This will update the package from PyPI\n"
230
237
  "💡 Use --check-only to just check for updates",
@@ -249,7 +256,7 @@ def update(check_only: bool, force: bool):
249
256
  subprocess.run(cmd, capture_output=True, text=True, check=True)
250
257
 
251
258
  console.print(
252
- Panel(
259
+ AIPPanel(
253
260
  "[bold green]✅ Update successful![/bold green]\n\n"
254
261
  "🔄 AIP SDK has been updated to the latest version\n"
255
262
  "💡 Restart your terminal or run 'aip --version' to verify",
@@ -270,7 +277,7 @@ def update(check_only: bool, force: bool):
270
277
 
271
278
  except subprocess.CalledProcessError as e:
272
279
  console.print(
273
- Panel(
280
+ AIPPanel(
274
281
  f"[bold red]❌ Update failed[/bold red]\n\n"
275
282
  f"🔍 Error: {e.stderr}\n\n"
276
283
  "💡 Troubleshooting:\n"
@@ -286,7 +293,7 @@ def update(check_only: bool, force: bool):
286
293
 
287
294
  except ImportError:
288
295
  console.print(
289
- Panel(
296
+ AIPPanel(
290
297
  "[bold red]❌ Rich library not available[/bold red]\n\n"
291
298
  "💡 Install rich: pip install rich\n"
292
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}")