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.
- glaip_sdk/__init__.py +5 -5
- glaip_sdk/branding.py +146 -0
- 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 +786 -271
- glaip_sdk/cli/commands/configure.py +19 -19
- glaip_sdk/cli/commands/mcps.py +151 -141
- glaip_sdk/cli/commands/models.py +1 -1
- glaip_sdk/cli/commands/tools.py +252 -178
- glaip_sdk/cli/display.py +244 -0
- glaip_sdk/cli/io.py +106 -0
- glaip_sdk/cli/main.py +27 -20
- glaip_sdk/cli/resolution.py +59 -0
- glaip_sdk/cli/utils.py +372 -213
- glaip_sdk/cli/validators.py +235 -0
- glaip_sdk/client/__init__.py +3 -224
- glaip_sdk/client/agents.py +632 -171
- 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 +327 -104
- glaip_sdk/config/constants.py +10 -1
- glaip_sdk/models.py +43 -3
- 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.3.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 -177
- glaip_sdk-0.0.3.dist-info/RECORD +0 -40
- {glaip_sdk-0.0.3.dist-info → glaip_sdk-0.0.5.dist-info}/WHEEL +0 -0
- {glaip_sdk-0.0.3.dist-info → glaip_sdk-0.0.5.dist-info}/entry_points.txt +0 -0
glaip_sdk/cli/commands/tools.py
CHANGED
|
@@ -6,19 +6,40 @@ Authors:
|
|
|
6
6
|
|
|
7
7
|
import json
|
|
8
8
|
import re
|
|
9
|
+
from pathlib import Path
|
|
9
10
|
|
|
10
11
|
import click
|
|
11
12
|
from rich.console import Console
|
|
12
|
-
from rich.
|
|
13
|
-
|
|
14
|
-
from
|
|
13
|
+
from rich.text import Text
|
|
14
|
+
|
|
15
|
+
from glaip_sdk.cli.display import (
|
|
16
|
+
display_api_error,
|
|
17
|
+
display_confirmation_prompt,
|
|
18
|
+
display_creation_success,
|
|
19
|
+
display_deletion_success,
|
|
20
|
+
display_update_success,
|
|
21
|
+
handle_json_output,
|
|
22
|
+
handle_rich_output,
|
|
23
|
+
)
|
|
24
|
+
from glaip_sdk.cli.io import (
|
|
25
|
+
export_resource_to_file_with_validation as export_resource_to_file,
|
|
26
|
+
)
|
|
27
|
+
from glaip_sdk.cli.io import (
|
|
28
|
+
fetch_raw_resource_details,
|
|
29
|
+
)
|
|
30
|
+
from glaip_sdk.cli.io import (
|
|
31
|
+
load_resource_from_file_with_validation as load_resource_from_file,
|
|
32
|
+
)
|
|
33
|
+
from glaip_sdk.cli.resolution import resolve_resource_reference
|
|
34
|
+
from glaip_sdk.cli.utils import (
|
|
15
35
|
coerce_to_row,
|
|
16
36
|
get_client,
|
|
17
37
|
output_flags,
|
|
18
38
|
output_list,
|
|
19
39
|
output_result,
|
|
20
|
-
resolve_resource,
|
|
21
40
|
)
|
|
41
|
+
from glaip_sdk.utils import format_datetime
|
|
42
|
+
from glaip_sdk.utils.import_export import merge_import_with_cli_args
|
|
22
43
|
|
|
23
44
|
console = Console()
|
|
24
45
|
|
|
@@ -31,12 +52,14 @@ def tools_group():
|
|
|
31
52
|
|
|
32
53
|
def _resolve_tool(ctx, client, ref, select=None):
|
|
33
54
|
"""Resolve tool reference (ID or name) with ambiguity handling."""
|
|
34
|
-
return
|
|
55
|
+
return resolve_resource_reference(
|
|
35
56
|
ctx,
|
|
57
|
+
client,
|
|
36
58
|
ref,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
59
|
+
"tool",
|
|
60
|
+
client.get_tool,
|
|
61
|
+
client.find_tools,
|
|
62
|
+
"Tool",
|
|
40
63
|
select=select,
|
|
41
64
|
)
|
|
42
65
|
|
|
@@ -90,12 +113,19 @@ def _parse_tags(tags: str | None) -> list[str]:
|
|
|
90
113
|
|
|
91
114
|
@tools_group.command(name="list")
|
|
92
115
|
@output_flags()
|
|
116
|
+
@click.option(
|
|
117
|
+
"--type",
|
|
118
|
+
"tool_type",
|
|
119
|
+
help="Filter tools by type (e.g., custom, native)",
|
|
120
|
+
type=str,
|
|
121
|
+
required=False,
|
|
122
|
+
)
|
|
93
123
|
@click.pass_context
|
|
94
|
-
def list_tools(ctx):
|
|
124
|
+
def list_tools(ctx, tool_type):
|
|
95
125
|
"""List all tools."""
|
|
96
126
|
try:
|
|
97
127
|
client = get_client(ctx)
|
|
98
|
-
tools = client.list_tools()
|
|
128
|
+
tools = client.list_tools(tool_type=tool_type)
|
|
99
129
|
|
|
100
130
|
# Define table columns: (data_key, header, style, width)
|
|
101
131
|
columns = [
|
|
@@ -136,19 +166,59 @@ def list_tools(ctx):
|
|
|
136
166
|
"--tags",
|
|
137
167
|
help="Comma-separated tags for the tool",
|
|
138
168
|
)
|
|
169
|
+
@click.option(
|
|
170
|
+
"--import",
|
|
171
|
+
"import_file",
|
|
172
|
+
type=click.Path(exists=True, dir_okay=False),
|
|
173
|
+
help="Import tool configuration from JSON file",
|
|
174
|
+
)
|
|
139
175
|
@output_flags()
|
|
140
176
|
@click.pass_context
|
|
141
|
-
def create(ctx, file_arg, file, name, description, tags):
|
|
142
|
-
"""Create a new tool.
|
|
177
|
+
def create(ctx, file_arg, file, name, description, tags, import_file):
|
|
178
|
+
"""Create a new tool.
|
|
179
|
+
|
|
180
|
+
Examples:
|
|
181
|
+
aip tools create --name "My Tool" --description "A helpful tool"
|
|
182
|
+
aip tools create tool.py # Create from file
|
|
183
|
+
aip tools create --import tool.json # Create from exported configuration
|
|
184
|
+
"""
|
|
143
185
|
try:
|
|
144
186
|
client = get_client(ctx)
|
|
145
187
|
|
|
188
|
+
# Initialize merged_data for cases without import_file
|
|
189
|
+
merged_data = {}
|
|
190
|
+
|
|
191
|
+
# Handle import from file
|
|
192
|
+
if import_file:
|
|
193
|
+
import_data = load_resource_from_file(Path(import_file), "tool")
|
|
194
|
+
|
|
195
|
+
# Merge CLI args with imported data
|
|
196
|
+
cli_args = {
|
|
197
|
+
"name": name,
|
|
198
|
+
"description": description,
|
|
199
|
+
"tags": tags,
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
merged_data = merge_import_with_cli_args(import_data, cli_args)
|
|
203
|
+
else:
|
|
204
|
+
# No import file - use CLI args directly
|
|
205
|
+
merged_data = {
|
|
206
|
+
"name": name,
|
|
207
|
+
"description": description,
|
|
208
|
+
"tags": tags,
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
# Extract merged values
|
|
212
|
+
name = merged_data.get("name")
|
|
213
|
+
description = merged_data.get("description")
|
|
214
|
+
tags = merged_data.get("tags")
|
|
215
|
+
|
|
146
216
|
# Allow positional file argument for better DX (matches examples)
|
|
147
217
|
if not file and file_arg:
|
|
148
218
|
file = file_arg
|
|
149
219
|
|
|
150
220
|
# Validate required parameters based on creation method
|
|
151
|
-
if not file:
|
|
221
|
+
if not file and not import_file:
|
|
152
222
|
# Metadata-only tool creation
|
|
153
223
|
if not name:
|
|
154
224
|
raise click.ClickException(
|
|
@@ -167,14 +237,14 @@ def create(ctx, file_arg, file, name, description, tags):
|
|
|
167
237
|
|
|
168
238
|
# Upload the plugin code as-is (no rewrite)
|
|
169
239
|
tool = client.create_tool_from_code(
|
|
170
|
-
tool_name,
|
|
171
|
-
code_content,
|
|
240
|
+
name=tool_name,
|
|
241
|
+
code=code_content,
|
|
172
242
|
framework="langchain", # Always langchain
|
|
173
243
|
description=description,
|
|
174
|
-
tags=_parse_tags(tags),
|
|
244
|
+
tags=_parse_tags(tags) if tags else None,
|
|
175
245
|
)
|
|
176
246
|
else:
|
|
177
|
-
# Metadata-only tool creation
|
|
247
|
+
# Metadata-only tool creation or import from file
|
|
178
248
|
tool_kwargs = {}
|
|
179
249
|
if name:
|
|
180
250
|
tool_kwargs["name"] = name
|
|
@@ -185,60 +255,141 @@ def create(ctx, file_arg, file, name, description, tags):
|
|
|
185
255
|
if tags:
|
|
186
256
|
tool_kwargs["tags"] = _parse_tags(tags)
|
|
187
257
|
|
|
258
|
+
# If importing from file, include all other detected attributes
|
|
259
|
+
if import_file:
|
|
260
|
+
# Add all other attributes from import data (excluding already handled ones)
|
|
261
|
+
excluded_fields = {
|
|
262
|
+
"name",
|
|
263
|
+
"description",
|
|
264
|
+
"tags",
|
|
265
|
+
# System-only fields that shouldn't be passed to create_tool
|
|
266
|
+
"id",
|
|
267
|
+
"created_at",
|
|
268
|
+
"updated_at",
|
|
269
|
+
"tool_type",
|
|
270
|
+
"framework",
|
|
271
|
+
"version",
|
|
272
|
+
}
|
|
273
|
+
for key, value in merged_data.items():
|
|
274
|
+
if key not in excluded_fields and value is not None:
|
|
275
|
+
tool_kwargs[key] = value
|
|
276
|
+
|
|
188
277
|
tool = client.create_tool(**tool_kwargs)
|
|
189
278
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
)
|
|
206
|
-
console.print(panel)
|
|
279
|
+
# Handle JSON output
|
|
280
|
+
handle_json_output(ctx, tool.model_dump())
|
|
281
|
+
|
|
282
|
+
# Handle Rich output
|
|
283
|
+
creation_method = "file upload (custom)" if file else "metadata only (native)"
|
|
284
|
+
rich_panel = display_creation_success(
|
|
285
|
+
"Tool",
|
|
286
|
+
tool.name,
|
|
287
|
+
tool.id,
|
|
288
|
+
Framework=getattr(tool, "framework", "N/A"),
|
|
289
|
+
Type=getattr(tool, "tool_type", "N/A"),
|
|
290
|
+
Description=getattr(tool, "description", "No description"),
|
|
291
|
+
Method=creation_method,
|
|
292
|
+
)
|
|
293
|
+
handle_rich_output(ctx, rich_panel)
|
|
207
294
|
|
|
208
295
|
except Exception as e:
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
console.print(f"[red]Error creating tool: {e}[/red]")
|
|
296
|
+
handle_json_output(ctx, error=e)
|
|
297
|
+
if ctx.obj.get("view") != "json":
|
|
298
|
+
display_api_error(e, "tool creation")
|
|
213
299
|
raise click.ClickException(str(e))
|
|
214
300
|
|
|
215
301
|
|
|
216
302
|
@tools_group.command()
|
|
217
303
|
@click.argument("tool_ref")
|
|
218
304
|
@click.option("--select", type=int, help="Choose among ambiguous matches (1-based)")
|
|
305
|
+
@click.option(
|
|
306
|
+
"--export",
|
|
307
|
+
type=click.Path(dir_okay=False, writable=True),
|
|
308
|
+
help="Export complete tool configuration to file (format auto-detected from .json/.yaml extension)",
|
|
309
|
+
)
|
|
219
310
|
@output_flags()
|
|
220
311
|
@click.pass_context
|
|
221
|
-
def get(ctx, tool_ref, select):
|
|
222
|
-
"""Get tool details.
|
|
312
|
+
def get(ctx, tool_ref, select, export):
|
|
313
|
+
"""Get tool details.
|
|
314
|
+
|
|
315
|
+
Examples:
|
|
316
|
+
aip tools get my-tool
|
|
317
|
+
aip tools get my-tool --export tool.json # Exports complete configuration as JSON
|
|
318
|
+
aip tools get my-tool --export tool.yaml # Exports complete configuration as YAML
|
|
319
|
+
"""
|
|
223
320
|
try:
|
|
224
321
|
client = get_client(ctx)
|
|
225
322
|
|
|
226
323
|
# Resolve tool with ambiguity handling
|
|
227
324
|
tool = _resolve_tool(ctx, client, tool_ref, select)
|
|
228
325
|
|
|
229
|
-
#
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
326
|
+
# Handle export option
|
|
327
|
+
if export:
|
|
328
|
+
export_path = Path(export)
|
|
329
|
+
# Auto-detect format from file extension
|
|
330
|
+
if export_path.suffix.lower() in [".yaml", ".yml"]:
|
|
331
|
+
detected_format = "yaml"
|
|
332
|
+
else:
|
|
333
|
+
detected_format = "json"
|
|
334
|
+
|
|
335
|
+
# Always export comprehensive data - re-fetch tool with full details if needed
|
|
336
|
+
try:
|
|
337
|
+
tool = client.get_tool_by_id(tool.id)
|
|
338
|
+
except Exception as e:
|
|
339
|
+
console.print(
|
|
340
|
+
Text(f"[yellow]⚠️ Could not fetch full tool details: {e}[/yellow]")
|
|
341
|
+
)
|
|
342
|
+
console.print(
|
|
343
|
+
Text("[yellow]⚠️ Proceeding with available data[/yellow]")
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
export_resource_to_file(tool, export_path, detected_format)
|
|
347
|
+
console.print(
|
|
348
|
+
Text(
|
|
349
|
+
f"[green]✅ Complete tool configuration exported to: {export_path} (format: {detected_format})[/green]"
|
|
350
|
+
)
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
# Try to fetch raw API data first to preserve ALL fields
|
|
354
|
+
raw_tool_data = fetch_raw_resource_details(client, tool, "tools")
|
|
355
|
+
|
|
356
|
+
if raw_tool_data:
|
|
357
|
+
# Use raw API data - this preserves ALL fields
|
|
358
|
+
# Format dates for better display (minimal postprocessing)
|
|
359
|
+
formatted_data = raw_tool_data.copy()
|
|
360
|
+
if "created_at" in formatted_data:
|
|
361
|
+
formatted_data["created_at"] = format_datetime(
|
|
362
|
+
formatted_data["created_at"]
|
|
363
|
+
)
|
|
364
|
+
if "updated_at" in formatted_data:
|
|
365
|
+
formatted_data["updated_at"] = format_datetime(
|
|
366
|
+
formatted_data["updated_at"]
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
# Display using output_result with raw data
|
|
370
|
+
output_result(
|
|
371
|
+
ctx,
|
|
372
|
+
formatted_data,
|
|
373
|
+
title="Tool Details",
|
|
374
|
+
panel_title=f"🔧 {raw_tool_data.get('name', 'Unknown')}",
|
|
375
|
+
)
|
|
376
|
+
else:
|
|
377
|
+
# Fall back to original method if raw fetch fails
|
|
378
|
+
console.print("[yellow]Falling back to Pydantic model data[/yellow]")
|
|
379
|
+
|
|
380
|
+
# Create result data with all available fields from backend
|
|
381
|
+
result_data = {
|
|
382
|
+
"id": str(getattr(tool, "id", "N/A")),
|
|
383
|
+
"name": getattr(tool, "name", "N/A"),
|
|
384
|
+
"tool_type": getattr(tool, "tool_type", "N/A"),
|
|
385
|
+
"framework": getattr(tool, "framework", "N/A"),
|
|
386
|
+
"version": getattr(tool, "version", "N/A"),
|
|
387
|
+
"description": getattr(tool, "description", "N/A"),
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
output_result(
|
|
391
|
+
ctx, result_data, title="Tool Details", panel_title=f"🔧 {tool.name}"
|
|
392
|
+
)
|
|
242
393
|
|
|
243
394
|
except Exception as e:
|
|
244
395
|
raise click.ClickException(str(e))
|
|
@@ -247,7 +398,9 @@ def get(ctx, tool_ref, select):
|
|
|
247
398
|
@tools_group.command()
|
|
248
399
|
@click.argument("tool_id")
|
|
249
400
|
@click.option(
|
|
250
|
-
"--file",
|
|
401
|
+
"--file",
|
|
402
|
+
type=click.Path(exists=True),
|
|
403
|
+
help="New tool file for code update (custom tools only)",
|
|
251
404
|
)
|
|
252
405
|
@click.option("--description", help="New description")
|
|
253
406
|
@click.option("--tags", help="Comma-separated tags")
|
|
@@ -264,41 +417,44 @@ def update(ctx, tool_id, file, description, tags):
|
|
|
264
417
|
except Exception as e:
|
|
265
418
|
raise click.ClickException(f"Tool with ID '{tool_id}' not found: {e}")
|
|
266
419
|
|
|
420
|
+
# Prepare update data
|
|
267
421
|
update_data = {}
|
|
268
|
-
|
|
269
422
|
if description:
|
|
270
423
|
update_data["description"] = description
|
|
271
|
-
|
|
272
424
|
if tags:
|
|
273
425
|
update_data["tags"] = [tag.strip() for tag in tags.split(",")]
|
|
274
426
|
|
|
275
427
|
if file:
|
|
276
|
-
# Update code
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
428
|
+
# Update code via file upload (custom tools only)
|
|
429
|
+
if tool.tool_type != "custom":
|
|
430
|
+
raise click.ClickException(
|
|
431
|
+
f"File updates are only supported for custom tools. Tool '{tool.name}' is of type '{tool.tool_type}'."
|
|
432
|
+
)
|
|
433
|
+
updated_tool = client.tools.update_tool_via_file(
|
|
434
|
+
tool.id, file, framework=tool.framework
|
|
435
|
+
)
|
|
436
|
+
handle_rich_output(
|
|
437
|
+
ctx, Text(f"[green]✓[/green] Tool code updated from {file}")
|
|
438
|
+
)
|
|
280
439
|
elif update_data:
|
|
281
|
-
# Update metadata
|
|
440
|
+
# Update metadata only (native tools only)
|
|
441
|
+
if tool.tool_type != "native":
|
|
442
|
+
raise click.ClickException(
|
|
443
|
+
f"Metadata updates are only supported for native tools. Tool '{tool.name}' is of type '{tool.tool_type}'."
|
|
444
|
+
)
|
|
282
445
|
updated_tool = tool.update(**update_data)
|
|
283
|
-
|
|
284
|
-
console.print("[green]✓[/green] Tool metadata updated")
|
|
446
|
+
handle_rich_output(ctx, Text("[green]✓[/green] Tool metadata updated"))
|
|
285
447
|
else:
|
|
286
|
-
|
|
287
|
-
console.print("[yellow]No updates specified[/yellow]")
|
|
448
|
+
handle_rich_output(ctx, Text("[yellow]No updates specified[/yellow]"))
|
|
288
449
|
return
|
|
289
450
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
else:
|
|
293
|
-
console.print(
|
|
294
|
-
f"[green]✅ Tool '{updated_tool.name}' updated successfully[/green]"
|
|
295
|
-
)
|
|
451
|
+
handle_json_output(ctx, updated_tool.model_dump())
|
|
452
|
+
handle_rich_output(ctx, display_update_success("Tool", updated_tool.name))
|
|
296
453
|
|
|
297
454
|
except Exception as e:
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
console.print(f"[red]Error updating tool: {e}[/red]")
|
|
455
|
+
handle_json_output(ctx, error=e)
|
|
456
|
+
if ctx.obj.get("view") != "json":
|
|
457
|
+
display_api_error(e, "tool update")
|
|
302
458
|
raise click.ClickException(str(e))
|
|
303
459
|
|
|
304
460
|
|
|
@@ -318,35 +474,29 @@ def delete(ctx, tool_id, yes):
|
|
|
318
474
|
except Exception as e:
|
|
319
475
|
raise click.ClickException(f"Tool with ID '{tool_id}' not found: {e}")
|
|
320
476
|
|
|
321
|
-
# Confirm deletion
|
|
322
|
-
if not yes and not
|
|
323
|
-
f"Are you sure you want to delete tool '{tool.name}'?"
|
|
324
|
-
):
|
|
325
|
-
if ctx.obj.get("view") != "json":
|
|
326
|
-
console.print("Deletion cancelled.")
|
|
477
|
+
# Confirm deletion via centralized display helper
|
|
478
|
+
if not yes and not display_confirmation_prompt("Tool", tool.name):
|
|
327
479
|
return
|
|
328
480
|
|
|
329
481
|
tool.delete()
|
|
330
482
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
console.print(f"[green]✅ Tool '{tool.name}' deleted successfully[/green]")
|
|
483
|
+
handle_json_output(
|
|
484
|
+
ctx,
|
|
485
|
+
{
|
|
486
|
+
"success": True,
|
|
487
|
+
"message": f"Tool '{tool.name}' deleted",
|
|
488
|
+
},
|
|
489
|
+
)
|
|
490
|
+
handle_rich_output(ctx, display_deletion_success("Tool", tool.name))
|
|
340
491
|
|
|
341
492
|
except Exception as e:
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
console.print(f"[red]Error deleting tool: {e}[/red]")
|
|
493
|
+
handle_json_output(ctx, error=e)
|
|
494
|
+
if ctx.obj.get("view") != "json":
|
|
495
|
+
display_api_error(e, "tool deletion")
|
|
346
496
|
raise click.ClickException(str(e))
|
|
347
497
|
|
|
348
498
|
|
|
349
|
-
@tools_group.command()
|
|
499
|
+
@tools_group.command("script")
|
|
350
500
|
@click.argument("tool_id")
|
|
351
501
|
@output_flags()
|
|
352
502
|
@click.pass_context
|
|
@@ -354,92 +504,16 @@ def script(ctx, tool_id):
|
|
|
354
504
|
"""Get tool script content."""
|
|
355
505
|
try:
|
|
356
506
|
client = get_client(ctx)
|
|
357
|
-
|
|
358
|
-
# Get tool by ID (no ambiguity handling needed)
|
|
359
|
-
try:
|
|
360
|
-
tool = client.get_tool_by_id(tool_id)
|
|
361
|
-
except Exception as e:
|
|
362
|
-
raise click.ClickException(f"Tool with ID '{tool_id}' not found: {e}")
|
|
363
|
-
|
|
364
|
-
# Get tool script content
|
|
365
|
-
script_content = client.tools.get_tool_script(tool_id)
|
|
507
|
+
script_content = client.get_tool_script(tool_id)
|
|
366
508
|
|
|
367
509
|
if ctx.obj.get("view") == "json":
|
|
368
|
-
click.echo(
|
|
369
|
-
json.dumps(
|
|
370
|
-
{
|
|
371
|
-
"tool_id": tool_id,
|
|
372
|
-
"tool_name": tool.name,
|
|
373
|
-
"script": script_content,
|
|
374
|
-
},
|
|
375
|
-
indent=2,
|
|
376
|
-
)
|
|
377
|
-
)
|
|
378
|
-
elif ctx.obj.get("output"):
|
|
379
|
-
# Save to file
|
|
380
|
-
output_file = ctx.obj.get("output")
|
|
381
|
-
with open(output_file, "w", encoding="utf-8") as f:
|
|
382
|
-
f.write(script_content)
|
|
383
|
-
console.print(f"[green]✅ Tool script saved to {output_file}[/green]")
|
|
510
|
+
click.echo(json.dumps({"script": script_content}, indent=2))
|
|
384
511
|
else:
|
|
385
|
-
|
|
386
|
-
console.print(
|
|
387
|
-
Panel(
|
|
388
|
-
script_content,
|
|
389
|
-
title=f"🔧 Tool Script: {tool.name}",
|
|
390
|
-
border_style="cyan",
|
|
391
|
-
)
|
|
392
|
-
)
|
|
512
|
+
console.print(f"[green]📜 Tool Script for '{tool_id}':[/green]")
|
|
513
|
+
console.print(script_content)
|
|
393
514
|
|
|
394
515
|
except Exception as e:
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
console.print(f"[red]Error getting tool script: {e}[/red]")
|
|
399
|
-
raise click.ClickException(str(e))
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
@tools_group.command()
|
|
403
|
-
@click.argument("tool_id")
|
|
404
|
-
@click.option(
|
|
405
|
-
"--file",
|
|
406
|
-
type=click.Path(exists=True),
|
|
407
|
-
required=True,
|
|
408
|
-
help="New tool file for code update",
|
|
409
|
-
)
|
|
410
|
-
@click.option("--name", help="New tool name")
|
|
411
|
-
@click.option("--description", help="New description")
|
|
412
|
-
@click.option("--tags", help="Comma-separated tags")
|
|
413
|
-
@output_flags()
|
|
414
|
-
@click.pass_context
|
|
415
|
-
def upload_update(ctx, tool_id, file, name, description, tags):
|
|
416
|
-
"""Update a tool plugin via file upload."""
|
|
417
|
-
try:
|
|
418
|
-
client = get_client(ctx)
|
|
419
|
-
|
|
420
|
-
# Prepare update data
|
|
421
|
-
update_data = {}
|
|
422
|
-
if name:
|
|
423
|
-
update_data["name"] = name
|
|
424
|
-
if description:
|
|
425
|
-
update_data["description"] = description
|
|
426
|
-
if tags:
|
|
427
|
-
update_data["tags"] = [tag.strip() for tag in tags.split(",")]
|
|
428
|
-
|
|
429
|
-
# Update tool via file upload
|
|
430
|
-
updated_tool = client.tools.update_tool_via_file(tool_id, file, **update_data)
|
|
431
|
-
|
|
432
|
-
if ctx.obj.get("view") == "json":
|
|
433
|
-
click.echo(json.dumps(updated_tool.model_dump(), indent=2))
|
|
434
|
-
else:
|
|
435
|
-
console.print(
|
|
436
|
-
f"[green]✅ Tool '{updated_tool.name}' updated successfully via file upload[/green]"
|
|
437
|
-
)
|
|
438
|
-
console.print(f"[blue]📁 File: {file}[/blue]")
|
|
439
|
-
|
|
440
|
-
except Exception as e:
|
|
441
|
-
if ctx.obj.get("view") == "json":
|
|
442
|
-
click.echo(json.dumps({"error": str(e)}, indent=2))
|
|
443
|
-
else:
|
|
444
|
-
console.print(f"[red]Error updating tool: {e}[/red]")
|
|
516
|
+
handle_json_output(ctx, error=e)
|
|
517
|
+
if ctx.obj.get("view") != "json":
|
|
518
|
+
console.print(Text(f"[red]Error getting tool script: {e}[/red]"))
|
|
445
519
|
raise click.ClickException(str(e))
|