glaip-sdk 0.1.0__py3-none-any.whl → 0.1.2__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/_version.py +1 -3
- glaip_sdk/branding.py +2 -6
- glaip_sdk/cli/agent_config.py +2 -6
- glaip_sdk/cli/auth.py +11 -30
- glaip_sdk/cli/commands/agents.py +45 -107
- glaip_sdk/cli/commands/configure.py +12 -36
- glaip_sdk/cli/commands/mcps.py +26 -63
- glaip_sdk/cli/commands/models.py +2 -4
- glaip_sdk/cli/commands/tools.py +22 -35
- glaip_sdk/cli/commands/update.py +3 -8
- glaip_sdk/cli/config.py +1 -3
- glaip_sdk/cli/display.py +4 -12
- glaip_sdk/cli/io.py +8 -14
- glaip_sdk/cli/main.py +10 -30
- glaip_sdk/cli/mcp_validators.py +5 -15
- glaip_sdk/cli/pager.py +3 -9
- glaip_sdk/cli/parsers/json_input.py +11 -22
- glaip_sdk/cli/resolution.py +3 -9
- glaip_sdk/cli/rich_helpers.py +1 -3
- glaip_sdk/cli/slash/agent_session.py +5 -10
- glaip_sdk/cli/slash/prompt.py +3 -10
- glaip_sdk/cli/slash/session.py +46 -95
- glaip_sdk/cli/transcript/cache.py +6 -19
- glaip_sdk/cli/transcript/capture.py +6 -20
- glaip_sdk/cli/transcript/launcher.py +1 -3
- glaip_sdk/cli/transcript/viewer.py +11 -40
- glaip_sdk/cli/update_notifier.py +165 -21
- glaip_sdk/cli/utils.py +33 -84
- glaip_sdk/cli/validators.py +11 -12
- glaip_sdk/client/_agent_payloads.py +10 -30
- glaip_sdk/client/agents.py +33 -63
- glaip_sdk/client/base.py +77 -35
- glaip_sdk/client/mcps.py +1 -3
- glaip_sdk/client/run_rendering.py +6 -14
- glaip_sdk/client/tools.py +8 -24
- glaip_sdk/client/validators.py +20 -48
- glaip_sdk/exceptions.py +1 -3
- glaip_sdk/models.py +14 -33
- glaip_sdk/payload_schemas/agent.py +1 -3
- glaip_sdk/utils/agent_config.py +4 -14
- glaip_sdk/utils/client_utils.py +7 -21
- glaip_sdk/utils/display.py +2 -6
- glaip_sdk/utils/general.py +1 -3
- glaip_sdk/utils/import_export.py +3 -9
- glaip_sdk/utils/rendering/formatting.py +2 -5
- glaip_sdk/utils/rendering/models.py +2 -6
- glaip_sdk/utils/rendering/renderer/__init__.py +1 -3
- glaip_sdk/utils/rendering/renderer/base.py +63 -189
- glaip_sdk/utils/rendering/renderer/debug.py +4 -14
- glaip_sdk/utils/rendering/renderer/panels.py +1 -3
- glaip_sdk/utils/rendering/renderer/progress.py +3 -11
- glaip_sdk/utils/rendering/renderer/stream.py +7 -19
- glaip_sdk/utils/rendering/renderer/toggle.py +1 -3
- glaip_sdk/utils/rendering/step_tree_state.py +1 -3
- glaip_sdk/utils/rendering/steps.py +29 -83
- glaip_sdk/utils/resource_refs.py +4 -13
- glaip_sdk/utils/serialization.py +14 -46
- glaip_sdk/utils/validation.py +4 -4
- {glaip_sdk-0.1.0.dist-info → glaip_sdk-0.1.2.dist-info}/METADATA +1 -1
- glaip_sdk-0.1.2.dist-info/RECORD +82 -0
- glaip_sdk-0.1.0.dist-info/RECORD +0 -82
- {glaip_sdk-0.1.0.dist-info → glaip_sdk-0.1.2.dist-info}/WHEEL +0 -0
- {glaip_sdk-0.1.0.dist-info → glaip_sdk-0.1.2.dist-info}/entry_points.txt +0 -0
glaip_sdk/cli/commands/mcps.py
CHANGED
|
@@ -76,9 +76,7 @@ def _is_sensitive_data(val: Any) -> bool:
|
|
|
76
76
|
return False
|
|
77
77
|
|
|
78
78
|
sensitive_patterns = {"token", "password", "secret", "key", "credential"}
|
|
79
|
-
return any(
|
|
80
|
-
pattern in str(k).lower() for k in val.keys() for pattern in sensitive_patterns
|
|
81
|
-
)
|
|
79
|
+
return any(pattern in str(k).lower() for k in val.keys() for pattern in sensitive_patterns)
|
|
82
80
|
|
|
83
81
|
|
|
84
82
|
def _redact_sensitive_dict(val: dict[str, Any]) -> dict[str, Any]:
|
|
@@ -143,13 +141,8 @@ def _build_empty_override_warnings(empty_fields: list[str]) -> list[str]:
|
|
|
143
141
|
if not empty_fields:
|
|
144
142
|
return []
|
|
145
143
|
|
|
146
|
-
warnings = [
|
|
147
|
-
|
|
148
|
-
]
|
|
149
|
-
warnings.extend(
|
|
150
|
-
f"- [yellow]{field}: will be set to empty string[/yellow]"
|
|
151
|
-
for field in empty_fields
|
|
152
|
-
)
|
|
144
|
+
warnings = ["\n[yellow]⚠️ Warning: Empty values provided via CLI will override import values[/yellow]"]
|
|
145
|
+
warnings.extend(f"- [yellow]{field}: will be set to empty string[/yellow]" for field in empty_fields)
|
|
153
146
|
return warnings
|
|
154
147
|
|
|
155
148
|
|
|
@@ -250,9 +243,7 @@ def _build_update_data_from_sources(
|
|
|
250
243
|
)
|
|
251
244
|
if auth is not None:
|
|
252
245
|
parsed_auth = parse_json_input(auth)
|
|
253
|
-
update_data["authentication"] = validate_mcp_auth_structure(
|
|
254
|
-
parsed_auth, source="--auth"
|
|
255
|
-
)
|
|
246
|
+
update_data["authentication"] = validate_mcp_auth_structure(parsed_auth, source="--auth")
|
|
256
247
|
|
|
257
248
|
return update_data
|
|
258
249
|
|
|
@@ -308,9 +299,7 @@ def mcps_group() -> None:
|
|
|
308
299
|
pass
|
|
309
300
|
|
|
310
301
|
|
|
311
|
-
def _resolve_mcp(
|
|
312
|
-
ctx: Any, client: Any, ref: str, select: int | None = None
|
|
313
|
-
) -> Any | None:
|
|
302
|
+
def _resolve_mcp(ctx: Any, client: Any, ref: str, select: int | None = None) -> Any | None:
|
|
314
303
|
"""Resolve MCP reference (ID or name) with ambiguity handling.
|
|
315
304
|
|
|
316
305
|
Args:
|
|
@@ -546,17 +535,13 @@ def list_mcps(ctx: Any) -> None:
|
|
|
546
535
|
row["id"] = str(row["id"])
|
|
547
536
|
# Truncate config field for display
|
|
548
537
|
if row["config"] != "N/A":
|
|
549
|
-
row["config"] = (
|
|
550
|
-
str(row["config"])[:50] + "..."
|
|
551
|
-
if len(str(row["config"])) > 50
|
|
552
|
-
else str(row["config"])
|
|
553
|
-
)
|
|
538
|
+
row["config"] = str(row["config"])[:50] + "..." if len(str(row["config"])) > 50 else str(row["config"])
|
|
554
539
|
return row
|
|
555
540
|
|
|
556
541
|
output_list(ctx, mcps, "🔌 Available MCPs", columns, transform_mcp)
|
|
557
542
|
|
|
558
543
|
except Exception as e:
|
|
559
|
-
raise click.ClickException(str(e))
|
|
544
|
+
raise click.ClickException(str(e)) from e
|
|
560
545
|
|
|
561
546
|
|
|
562
547
|
@mcps_group.command()
|
|
@@ -621,9 +606,7 @@ def create(
|
|
|
621
606
|
try:
|
|
622
607
|
client = get_client(ctx)
|
|
623
608
|
|
|
624
|
-
import_payload = (
|
|
625
|
-
_load_import_ready_payload(import_file) if import_file is not None else None
|
|
626
|
-
)
|
|
609
|
+
import_payload = _load_import_ready_payload(import_file) if import_file is not None else None
|
|
627
610
|
|
|
628
611
|
merged_payload, missing_fields = _merge_import_payload(
|
|
629
612
|
import_payload,
|
|
@@ -636,8 +619,7 @@ def create(
|
|
|
636
619
|
|
|
637
620
|
if missing_fields:
|
|
638
621
|
raise click.ClickException(
|
|
639
|
-
"Missing required fields after combining import and CLI values: "
|
|
640
|
-
+ ", ".join(missing_fields)
|
|
622
|
+
"Missing required fields after combining import and CLI values: " + ", ".join(missing_fields)
|
|
641
623
|
)
|
|
642
624
|
|
|
643
625
|
effective_name = merged_payload["name"]
|
|
@@ -726,9 +708,7 @@ def _handle_mcp_export(
|
|
|
726
708
|
f"[{WARNING_STYLE}]⚠️ Could not fetch full MCP details: {e}[/]",
|
|
727
709
|
console=console,
|
|
728
710
|
)
|
|
729
|
-
print_markup(
|
|
730
|
-
f"[{WARNING_STYLE}]⚠️ Proceeding with available data[/]", console=console
|
|
731
|
-
)
|
|
711
|
+
print_markup(f"[{WARNING_STYLE}]⚠️ Proceeding with available data[/]", console=console)
|
|
732
712
|
|
|
733
713
|
# Determine if we should prompt for secrets
|
|
734
714
|
prompt_for_secrets = not no_auth_prompt and sys.stdin.isatty()
|
|
@@ -736,8 +716,7 @@ def _handle_mcp_export(
|
|
|
736
716
|
# Warn user if non-interactive mode forces placeholder usage
|
|
737
717
|
if not no_auth_prompt and not sys.stdin.isatty():
|
|
738
718
|
print_markup(
|
|
739
|
-
f"[{WARNING_STYLE}]⚠️ Non-interactive mode detected. "
|
|
740
|
-
"Using placeholder values for secrets.[/]",
|
|
719
|
+
f"[{WARNING_STYLE}]⚠️ Non-interactive mode detected. Using placeholder values for secrets.[/]",
|
|
741
720
|
console=console,
|
|
742
721
|
)
|
|
743
722
|
|
|
@@ -772,8 +751,7 @@ def _handle_mcp_export(
|
|
|
772
751
|
write_resource_export(export_path, export_payload, detected_format)
|
|
773
752
|
|
|
774
753
|
print_markup(
|
|
775
|
-
f"[{SUCCESS_STYLE}]✅ Complete MCP configuration exported to: "
|
|
776
|
-
f"{export_path} (format: {detected_format})[/]",
|
|
754
|
+
f"[{SUCCESS_STYLE}]✅ Complete MCP configuration exported to: {export_path} (format: {detected_format})[/]",
|
|
777
755
|
console=console,
|
|
778
756
|
)
|
|
779
757
|
|
|
@@ -832,8 +810,7 @@ def _display_mcp_details(ctx: Any, client: Any, mcp: Any) -> None:
|
|
|
832
810
|
@click.option(
|
|
833
811
|
"--export",
|
|
834
812
|
type=click.Path(dir_okay=False, writable=True),
|
|
835
|
-
help="Export complete MCP configuration to file "
|
|
836
|
-
"(format auto-detected from .json/.yaml extension)",
|
|
813
|
+
help="Export complete MCP configuration to file (format auto-detected from .json/.yaml extension)",
|
|
837
814
|
)
|
|
838
815
|
@click.option(
|
|
839
816
|
"--no-auth-prompt",
|
|
@@ -880,15 +857,13 @@ def get(
|
|
|
880
857
|
|
|
881
858
|
# Handle export option
|
|
882
859
|
if export:
|
|
883
|
-
_handle_mcp_export(
|
|
884
|
-
ctx, client, mcp, Path(export), no_auth_prompt, auth_placeholder
|
|
885
|
-
)
|
|
860
|
+
_handle_mcp_export(ctx, client, mcp, Path(export), no_auth_prompt, auth_placeholder)
|
|
886
861
|
|
|
887
862
|
# Display MCP details
|
|
888
863
|
_display_mcp_details(ctx, client, mcp)
|
|
889
864
|
|
|
890
865
|
except Exception as e:
|
|
891
|
-
raise click.ClickException(str(e))
|
|
866
|
+
raise click.ClickException(str(e)) from e
|
|
892
867
|
|
|
893
868
|
|
|
894
869
|
@mcps_group.command("tools")
|
|
@@ -943,7 +918,7 @@ def list_tools(ctx: Any, mcp_ref: str) -> None:
|
|
|
943
918
|
)
|
|
944
919
|
|
|
945
920
|
except Exception as e:
|
|
946
|
-
raise click.ClickException(str(e))
|
|
921
|
+
raise click.ClickException(str(e)) from e
|
|
947
922
|
|
|
948
923
|
|
|
949
924
|
@mcps_group.command("connect")
|
|
@@ -996,20 +971,17 @@ def connect(ctx: Any, config_file: str) -> None:
|
|
|
996
971
|
handle_json_output(ctx, result)
|
|
997
972
|
else:
|
|
998
973
|
success_panel = AIPPanel(
|
|
999
|
-
f"[{SUCCESS_STYLE}]✓[/] MCP connection successful!\n\n"
|
|
1000
|
-
f"[bold]Result:[/bold] {result}",
|
|
974
|
+
f"[{SUCCESS_STYLE}]✓[/] MCP connection successful!\n\n[bold]Result:[/bold] {result}",
|
|
1001
975
|
title="🔌 Connection",
|
|
1002
976
|
border_style=SUCCESS,
|
|
1003
977
|
)
|
|
1004
978
|
console.print(success_panel)
|
|
1005
979
|
|
|
1006
980
|
except Exception as e:
|
|
1007
|
-
raise click.ClickException(str(e))
|
|
981
|
+
raise click.ClickException(str(e)) from e
|
|
1008
982
|
|
|
1009
983
|
|
|
1010
|
-
def _generate_update_preview(
|
|
1011
|
-
mcp: Any, update_data: dict[str, Any], cli_overrides: dict[str, Any]
|
|
1012
|
-
) -> str:
|
|
984
|
+
def _generate_update_preview(mcp: Any, update_data: dict[str, Any], cli_overrides: dict[str, Any]) -> str:
|
|
1013
985
|
"""Generate formatted preview of changes for user confirmation.
|
|
1014
986
|
|
|
1015
987
|
Args:
|
|
@@ -1020,9 +992,7 @@ def _generate_update_preview(
|
|
|
1020
992
|
Returns:
|
|
1021
993
|
Formatted preview string showing old→new values
|
|
1022
994
|
"""
|
|
1023
|
-
lines = [
|
|
1024
|
-
f"\n[bold]The following fields will be updated for MCP '{mcp.name}':[/bold]\n"
|
|
1025
|
-
]
|
|
995
|
+
lines = [f"\n[bold]The following fields will be updated for MCP '{mcp.name}':[/bold]\n"]
|
|
1026
996
|
|
|
1027
997
|
empty_overrides = []
|
|
1028
998
|
|
|
@@ -1048,9 +1018,7 @@ def _generate_update_preview(
|
|
|
1048
1018
|
@mcps_group.command()
|
|
1049
1019
|
@click.argument("mcp_ref")
|
|
1050
1020
|
@click.option("--name", help="New MCP name")
|
|
1051
|
-
@click.option(
|
|
1052
|
-
"--transport", type=click.Choice(["http", "sse"]), help="New transport protocol"
|
|
1053
|
-
)
|
|
1021
|
+
@click.option("--transport", type=click.Choice(["http", "sse"]), help="New transport protocol")
|
|
1054
1022
|
@click.option("--description", help="New description")
|
|
1055
1023
|
@click.option(
|
|
1056
1024
|
"--config",
|
|
@@ -1121,12 +1089,11 @@ def update(
|
|
|
1121
1089
|
client = get_client(ctx)
|
|
1122
1090
|
|
|
1123
1091
|
# Validate that at least one update method is provided
|
|
1124
|
-
cli_flags_provided = any(
|
|
1125
|
-
v is not None for v in [name, transport, description, config, auth]
|
|
1126
|
-
)
|
|
1092
|
+
cli_flags_provided = any(v is not None for v in [name, transport, description, config, auth])
|
|
1127
1093
|
if not import_file and not cli_flags_provided:
|
|
1128
1094
|
raise click.ClickException(
|
|
1129
|
-
"No update fields specified. Use --import or one of:
|
|
1095
|
+
"No update fields specified. Use --import or one of: "
|
|
1096
|
+
"--name, --transport, --description, --config, --auth"
|
|
1130
1097
|
)
|
|
1131
1098
|
|
|
1132
1099
|
# Resolve MCP using helper function
|
|
@@ -1140,18 +1107,14 @@ def update(
|
|
|
1140
1107
|
return
|
|
1141
1108
|
|
|
1142
1109
|
# Build update data from import and CLI flags
|
|
1143
|
-
update_data = _build_update_data_from_sources(
|
|
1144
|
-
import_payload, mcp, name, transport, description, config, auth
|
|
1145
|
-
)
|
|
1110
|
+
update_data = _build_update_data_from_sources(import_payload, mcp, name, transport, description, config, auth)
|
|
1146
1111
|
|
|
1147
1112
|
if not update_data:
|
|
1148
1113
|
raise click.ClickException("No update fields specified")
|
|
1149
1114
|
|
|
1150
1115
|
# Show confirmation preview for import-based updates (unless -y flag)
|
|
1151
1116
|
if import_payload and not y:
|
|
1152
|
-
cli_overrides = _collect_cli_overrides(
|
|
1153
|
-
name, transport, description, config, auth
|
|
1154
|
-
)
|
|
1117
|
+
cli_overrides = _collect_cli_overrides(name, transport, description, config, auth)
|
|
1155
1118
|
preview = _generate_update_preview(mcp, update_data, cli_overrides)
|
|
1156
1119
|
print_markup(preview)
|
|
1157
1120
|
|
glaip_sdk/cli/commands/models.py
CHANGED
|
@@ -57,9 +57,7 @@ def list_models(ctx: Any) -> None:
|
|
|
57
57
|
"base_url": model.get("base_url", "Default") or "Default",
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
output_list(
|
|
61
|
-
ctx, models, "🧠 Available Language Models", columns, transform_model
|
|
62
|
-
)
|
|
60
|
+
output_list(ctx, models, "🧠 Available Language Models", columns, transform_model)
|
|
63
61
|
|
|
64
62
|
except Exception as e:
|
|
65
|
-
raise click.ClickException(str(e))
|
|
63
|
+
raise click.ClickException(str(e)) from e
|
glaip_sdk/cli/commands/tools.py
CHANGED
|
@@ -60,9 +60,7 @@ def tools_group() -> None:
|
|
|
60
60
|
pass
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
def _resolve_tool(
|
|
64
|
-
ctx: Any, client: Any, ref: str, select: int | None = None
|
|
65
|
-
) -> Any | None:
|
|
63
|
+
def _resolve_tool(ctx: Any, client: Any, ref: str, select: int | None = None) -> Any | None:
|
|
66
64
|
"""Resolve tool reference (ID or name) with ambiguity handling."""
|
|
67
65
|
return resolve_resource_reference(
|
|
68
66
|
ctx,
|
|
@@ -181,9 +179,7 @@ def _validate_creation_parameters(
|
|
|
181
179
|
) -> None:
|
|
182
180
|
"""Validate required parameters for tool creation."""
|
|
183
181
|
if not file and not import_file:
|
|
184
|
-
raise click.ClickException(
|
|
185
|
-
"A tool file must be provided. Use --file to specify the tool file to upload."
|
|
186
|
-
)
|
|
182
|
+
raise click.ClickException("A tool file must be provided. Use --file to specify the tool file to upload.")
|
|
187
183
|
|
|
188
184
|
|
|
189
185
|
@tools_group.command(name="list")
|
|
@@ -224,7 +220,7 @@ def list_tools(ctx: Any, tool_type: str | None) -> None:
|
|
|
224
220
|
output_list(ctx, tools, f"{ICON_TOOL} Available Tools", columns, transform_tool)
|
|
225
221
|
|
|
226
222
|
except Exception as e:
|
|
227
|
-
raise click.ClickException(str(e))
|
|
223
|
+
raise click.ClickException(str(e)) from e
|
|
228
224
|
|
|
229
225
|
|
|
230
226
|
@tools_group.command()
|
|
@@ -315,7 +311,7 @@ def create(
|
|
|
315
311
|
handle_json_output(ctx, error=e)
|
|
316
312
|
if get_ctx_value(ctx, "view") != "json":
|
|
317
313
|
display_api_error(e, "tool creation")
|
|
318
|
-
raise click.ClickException(str(e))
|
|
314
|
+
raise click.ClickException(str(e)) from e
|
|
319
315
|
|
|
320
316
|
|
|
321
317
|
@tools_group.command()
|
|
@@ -373,7 +369,8 @@ def get(ctx: Any, tool_ref: str, select: int | None, export: str | None) -> None
|
|
|
373
369
|
):
|
|
374
370
|
export_resource_to_file(tool, export_path, detected_format)
|
|
375
371
|
print_markup(
|
|
376
|
-
f"[{SUCCESS_STYLE}]✅ Complete tool configuration exported to: {export_path}
|
|
372
|
+
f"[{SUCCESS_STYLE}]✅ Complete tool configuration exported to: {export_path} "
|
|
373
|
+
f"(format: {detected_format})[/]",
|
|
377
374
|
console=console,
|
|
378
375
|
)
|
|
379
376
|
|
|
@@ -390,13 +387,9 @@ def get(ctx: Any, tool_ref: str, select: int | None, export: str | None) -> None
|
|
|
390
387
|
# Format dates for better display (minimal postprocessing)
|
|
391
388
|
formatted_data = raw_tool_data.copy()
|
|
392
389
|
if "created_at" in formatted_data:
|
|
393
|
-
formatted_data["created_at"] = format_datetime(
|
|
394
|
-
formatted_data["created_at"]
|
|
395
|
-
)
|
|
390
|
+
formatted_data["created_at"] = format_datetime(formatted_data["created_at"])
|
|
396
391
|
if "updated_at" in formatted_data:
|
|
397
|
-
formatted_data["updated_at"] = format_datetime(
|
|
398
|
-
formatted_data["updated_at"]
|
|
399
|
-
)
|
|
392
|
+
formatted_data["updated_at"] = format_datetime(formatted_data["updated_at"])
|
|
400
393
|
|
|
401
394
|
# Display using output_result with raw data
|
|
402
395
|
output_result(
|
|
@@ -427,7 +420,7 @@ def get(ctx: Any, tool_ref: str, select: int | None, export: str | None) -> None
|
|
|
427
420
|
)
|
|
428
421
|
|
|
429
422
|
except Exception as e:
|
|
430
|
-
raise click.ClickException(str(e))
|
|
423
|
+
raise click.ClickException(str(e)) from e
|
|
431
424
|
|
|
432
425
|
|
|
433
426
|
@tools_group.command()
|
|
@@ -461,7 +454,7 @@ def update(
|
|
|
461
454
|
):
|
|
462
455
|
tool = client.get_tool_by_id(tool_id)
|
|
463
456
|
except Exception as e:
|
|
464
|
-
raise click.ClickException(f"Tool with ID '{tool_id}' not found: {e}")
|
|
457
|
+
raise click.ClickException(f"Tool with ID '{tool_id}' not found: {e}") from e
|
|
465
458
|
|
|
466
459
|
# Prepare update data
|
|
467
460
|
update_data = {}
|
|
@@ -474,16 +467,15 @@ def update(
|
|
|
474
467
|
# Update code via file upload (custom tools only)
|
|
475
468
|
if tool.tool_type != "custom":
|
|
476
469
|
raise click.ClickException(
|
|
477
|
-
|
|
470
|
+
"File updates are only supported for custom tools. "
|
|
471
|
+
f"Tool '{tool.name}' is of type '{tool.tool_type}'."
|
|
478
472
|
)
|
|
479
473
|
with spinner_context(
|
|
480
474
|
ctx,
|
|
481
475
|
"[bold blue]Uploading new tool code…[/bold blue]",
|
|
482
476
|
console_override=console,
|
|
483
477
|
):
|
|
484
|
-
updated_tool = client.tools.update_tool_via_file(
|
|
485
|
-
tool.id, file, framework=tool.framework
|
|
486
|
-
)
|
|
478
|
+
updated_tool = client.tools.update_tool_via_file(tool.id, file, framework=tool.framework)
|
|
487
479
|
handle_rich_output(
|
|
488
480
|
ctx,
|
|
489
481
|
markup_text(f"[{SUCCESS_STYLE}]✓[/] Tool code updated from {file}"),
|
|
@@ -492,7 +484,8 @@ def update(
|
|
|
492
484
|
# Update metadata only (native tools only)
|
|
493
485
|
if tool.tool_type != "native":
|
|
494
486
|
raise click.ClickException(
|
|
495
|
-
|
|
487
|
+
"Metadata updates are only supported for native tools. "
|
|
488
|
+
f"Tool '{tool.name}' is of type '{tool.tool_type}'."
|
|
496
489
|
)
|
|
497
490
|
with spinner_context(
|
|
498
491
|
ctx,
|
|
@@ -500,13 +493,9 @@ def update(
|
|
|
500
493
|
console_override=console,
|
|
501
494
|
):
|
|
502
495
|
updated_tool = tool.update(**update_data)
|
|
503
|
-
handle_rich_output(
|
|
504
|
-
ctx, markup_text(f"[{SUCCESS_STYLE}]✓[/] Tool metadata updated")
|
|
505
|
-
)
|
|
496
|
+
handle_rich_output(ctx, markup_text(f"[{SUCCESS_STYLE}]✓[/] Tool metadata updated"))
|
|
506
497
|
else:
|
|
507
|
-
handle_rich_output(
|
|
508
|
-
ctx, markup_text(f"[{WARNING_STYLE}]No updates specified[/]")
|
|
509
|
-
)
|
|
498
|
+
handle_rich_output(ctx, markup_text(f"[{WARNING_STYLE}]No updates specified[/]"))
|
|
510
499
|
return
|
|
511
500
|
|
|
512
501
|
handle_json_output(ctx, updated_tool.model_dump())
|
|
@@ -516,7 +505,7 @@ def update(
|
|
|
516
505
|
handle_json_output(ctx, error=e)
|
|
517
506
|
if get_ctx_value(ctx, "view") != "json":
|
|
518
507
|
display_api_error(e, "tool update")
|
|
519
|
-
raise click.ClickException(str(e))
|
|
508
|
+
raise click.ClickException(str(e)) from e
|
|
520
509
|
|
|
521
510
|
|
|
522
511
|
@tools_group.command()
|
|
@@ -538,7 +527,7 @@ def delete(ctx: Any, tool_id: str, yes: bool) -> None:
|
|
|
538
527
|
):
|
|
539
528
|
tool = client.get_tool_by_id(tool_id)
|
|
540
529
|
except Exception as e:
|
|
541
|
-
raise click.ClickException(f"Tool with ID '{tool_id}' not found: {e}")
|
|
530
|
+
raise click.ClickException(f"Tool with ID '{tool_id}' not found: {e}") from e
|
|
542
531
|
|
|
543
532
|
# Confirm deletion via centralized display helper
|
|
544
533
|
if not yes and not display_confirmation_prompt("Tool", tool.name):
|
|
@@ -564,7 +553,7 @@ def delete(ctx: Any, tool_id: str, yes: bool) -> None:
|
|
|
564
553
|
handle_json_output(ctx, error=e)
|
|
565
554
|
if get_ctx_value(ctx, "view") != "json":
|
|
566
555
|
display_api_error(e, "tool deletion")
|
|
567
|
-
raise click.ClickException(str(e))
|
|
556
|
+
raise click.ClickException(str(e)) from e
|
|
568
557
|
|
|
569
558
|
|
|
570
559
|
@tools_group.command("script")
|
|
@@ -591,7 +580,5 @@ def script(ctx: Any, tool_id: str) -> None:
|
|
|
591
580
|
except Exception as e:
|
|
592
581
|
handle_json_output(ctx, error=e)
|
|
593
582
|
if get_ctx_value(ctx, "view") != "json":
|
|
594
|
-
print_markup(
|
|
595
|
-
|
|
596
|
-
)
|
|
597
|
-
raise click.ClickException(str(e))
|
|
583
|
+
print_markup(f"[{ERROR_STYLE}]Error getting tool script: {e}[/]", console=console)
|
|
584
|
+
raise click.ClickException(str(e)) from e
|
glaip_sdk/cli/commands/update.py
CHANGED
|
@@ -44,22 +44,17 @@ def update_command(include_prerelease: bool) -> None:
|
|
|
44
44
|
"""Upgrade the glaip-sdk package using pip."""
|
|
45
45
|
console = Console()
|
|
46
46
|
upgrade_cmd = _build_upgrade_command(include_prerelease)
|
|
47
|
-
console.print(
|
|
48
|
-
f"[{ACCENT_STYLE}]Upgrading {PACKAGE_NAME} using[/] "
|
|
49
|
-
f"[{INFO_STYLE}]{' '.join(upgrade_cmd)}[/]"
|
|
50
|
-
)
|
|
47
|
+
console.print(f"[{ACCENT_STYLE}]Upgrading {PACKAGE_NAME} using[/] [{INFO_STYLE}]{' '.join(upgrade_cmd)}[/]")
|
|
51
48
|
|
|
52
49
|
try:
|
|
53
50
|
subprocess.run(upgrade_cmd, check=True)
|
|
54
51
|
except FileNotFoundError as exc:
|
|
55
52
|
raise click.ClickException(
|
|
56
|
-
"Unable to locate Python executable to run pip. "
|
|
57
|
-
"Please ensure Python is installed and try again."
|
|
53
|
+
"Unable to locate Python executable to run pip. Please ensure Python is installed and try again."
|
|
58
54
|
) from exc
|
|
59
55
|
except subprocess.CalledProcessError as exc:
|
|
60
56
|
console.print(
|
|
61
|
-
f"[{ERROR_STYLE}]Automatic upgrade failed.[/] "
|
|
62
|
-
f"Please run `pip install -U {PACKAGE_NAME}` manually."
|
|
57
|
+
f"[{ERROR_STYLE}]Automatic upgrade failed.[/] Please run `pip install -U {PACKAGE_NAME}` manually."
|
|
63
58
|
)
|
|
64
59
|
raise click.ClickException("Automatic upgrade failed.") from exc
|
|
65
60
|
|
glaip_sdk/cli/config.py
CHANGED
|
@@ -47,7 +47,5 @@ def save_config(config: dict[str, Any]) -> None:
|
|
|
47
47
|
# Set secure file permissions
|
|
48
48
|
try:
|
|
49
49
|
os.chmod(CONFIG_FILE, 0o600)
|
|
50
|
-
except
|
|
51
|
-
OSError
|
|
52
|
-
): # pragma: no cover - permission errors are expected in some environments
|
|
50
|
+
except OSError: # pragma: no cover - permission errors are expected in some environments
|
|
53
51
|
pass
|
glaip_sdk/cli/display.py
CHANGED
|
@@ -41,9 +41,7 @@ def display_creation_success(
|
|
|
41
41
|
# Build additional fields display
|
|
42
42
|
fields_display = ""
|
|
43
43
|
if additional_fields:
|
|
44
|
-
fields_display = "\n" + "\n".join(
|
|
45
|
-
f"{key}: {value}" for key, value in additional_fields.items()
|
|
46
|
-
)
|
|
44
|
+
fields_display = "\n" + "\n".join(f"{key}: {value}" for key, value in additional_fields.items())
|
|
47
45
|
|
|
48
46
|
return AIPPanel(
|
|
49
47
|
f"[{SUCCESS_STYLE}]✅ {resource_type} '{resource_name}' created successfully![/]\n\n"
|
|
@@ -64,9 +62,7 @@ def display_update_success(resource_type: str, resource_name: str) -> Text:
|
|
|
64
62
|
Returns:
|
|
65
63
|
Rich Text object for display
|
|
66
64
|
"""
|
|
67
|
-
return markup_text(
|
|
68
|
-
f"[{SUCCESS_STYLE}]✅ {resource_type} '{resource_name}' updated successfully[/]"
|
|
69
|
-
)
|
|
65
|
+
return markup_text(f"[{SUCCESS_STYLE}]✅ {resource_type} '{resource_name}' updated successfully[/]")
|
|
70
66
|
|
|
71
67
|
|
|
72
68
|
def display_deletion_success(resource_type: str, resource_name: str) -> Text:
|
|
@@ -79,9 +75,7 @@ def display_deletion_success(resource_type: str, resource_name: str) -> Text:
|
|
|
79
75
|
Returns:
|
|
80
76
|
Rich Text object for display
|
|
81
77
|
"""
|
|
82
|
-
return markup_text(
|
|
83
|
-
f"[{SUCCESS_STYLE}]✅ {resource_type} '{resource_name}' deleted successfully[/]"
|
|
84
|
-
)
|
|
78
|
+
return markup_text(f"[{SUCCESS_STYLE}]✅ {resource_type} '{resource_name}' deleted successfully[/]")
|
|
85
79
|
|
|
86
80
|
|
|
87
81
|
def display_api_error(error: Exception, operation: str = "operation") -> None:
|
|
@@ -293,9 +287,7 @@ def display_confirmation_prompt(resource_type: str, resource_name: str) -> bool:
|
|
|
293
287
|
Returns:
|
|
294
288
|
True if user confirms, False otherwise
|
|
295
289
|
"""
|
|
296
|
-
if not click.confirm(
|
|
297
|
-
f"Are you sure you want to delete {resource_type.lower()} '{resource_name}'?"
|
|
298
|
-
):
|
|
290
|
+
if not click.confirm(f"Are you sure you want to delete {resource_type.lower()} '{resource_name}'?"):
|
|
299
291
|
if console.is_terminal:
|
|
300
292
|
console.print(Text("Deletion cancelled."))
|
|
301
293
|
return False
|
glaip_sdk/cli/io.py
CHANGED
|
@@ -30,9 +30,7 @@ def _create_console() -> "Console":
|
|
|
30
30
|
return Console()
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
def load_resource_from_file_with_validation(
|
|
34
|
-
file_path: Path, resource_type: str
|
|
35
|
-
) -> dict[str, Any]:
|
|
33
|
+
def load_resource_from_file_with_validation(file_path: Path, resource_type: str) -> dict[str, Any]:
|
|
36
34
|
"""Load resource data from JSON or YAML file with CLI-friendly error handling.
|
|
37
35
|
|
|
38
36
|
Args:
|
|
@@ -47,17 +45,15 @@ def load_resource_from_file_with_validation(
|
|
|
47
45
|
"""
|
|
48
46
|
try:
|
|
49
47
|
return load_resource_from_file(file_path)
|
|
50
|
-
except FileNotFoundError:
|
|
51
|
-
raise click.ClickException(f"File not found: {file_path}")
|
|
48
|
+
except FileNotFoundError as err:
|
|
49
|
+
raise click.ClickException(f"File not found: {file_path}") from err
|
|
52
50
|
except ValueError as e:
|
|
53
|
-
raise click.ClickException(f"Invalid {resource_type.lower()} file format: {e}")
|
|
51
|
+
raise click.ClickException(f"Invalid {resource_type.lower()} file format: {e}") from e
|
|
54
52
|
except Exception as e:
|
|
55
|
-
raise click.ClickException(f"Failed to load {resource_type.lower()} file: {e}")
|
|
53
|
+
raise click.ClickException(f"Failed to load {resource_type.lower()} file: {e}") from e
|
|
56
54
|
|
|
57
55
|
|
|
58
|
-
def export_resource_to_file_with_validation(
|
|
59
|
-
resource: Any, file_path: Path, format: str = "json"
|
|
60
|
-
) -> None:
|
|
56
|
+
def export_resource_to_file_with_validation(resource: Any, file_path: Path, format: str = "json") -> None:
|
|
61
57
|
"""Export resource to file with CLI-friendly error handling.
|
|
62
58
|
|
|
63
59
|
Args:
|
|
@@ -73,7 +69,7 @@ def export_resource_to_file_with_validation(
|
|
|
73
69
|
export_data = collect_attributes_for_export(resource)
|
|
74
70
|
write_resource_export(file_path, export_data, format)
|
|
75
71
|
except Exception as e:
|
|
76
|
-
raise click.ClickException(f"Failed to export resource: {e}")
|
|
72
|
+
raise click.ClickException(f"Failed to export resource: {e}") from e
|
|
77
73
|
|
|
78
74
|
|
|
79
75
|
def fetch_raw_resource_details(client: Any, resource: Any, resource_type: str) -> Any:
|
|
@@ -107,9 +103,7 @@ def fetch_raw_resource_details(client: Any, resource: Any, resource_type: str) -
|
|
|
107
103
|
# Direct response
|
|
108
104
|
return raw_response
|
|
109
105
|
except Exception as e:
|
|
110
|
-
console.print(
|
|
111
|
-
f"[{WARNING_STYLE}]Failed to fetch raw {resource_type} details: {e}[/]"
|
|
112
|
-
)
|
|
106
|
+
console.print(f"[{WARNING_STYLE}]Failed to fetch raw {resource_type} details: {e}[/]")
|
|
113
107
|
# Fall back to regular method
|
|
114
108
|
return None
|
|
115
109
|
return None
|
glaip_sdk/cli/main.py
CHANGED
|
@@ -215,9 +215,7 @@ def _validate_config_and_show_error(config: dict, console: Console) -> None:
|
|
|
215
215
|
border_style=ERROR,
|
|
216
216
|
)
|
|
217
217
|
)
|
|
218
|
-
console.print(
|
|
219
|
-
f"\n[{SUCCESS_STYLE}]✅ AIP - Ready[/] (SDK v{_SDK_VERSION}) - Configure to connect"
|
|
220
|
-
)
|
|
218
|
+
console.print(f"\n[{SUCCESS_STYLE}]✅ AIP - Ready[/] (SDK v{_SDK_VERSION}) - Configure to connect")
|
|
221
219
|
sys.exit(1)
|
|
222
220
|
|
|
223
221
|
|
|
@@ -251,16 +249,11 @@ def _collect_cache_summary() -> tuple[str | None, str | None]:
|
|
|
251
249
|
else:
|
|
252
250
|
size_part = ""
|
|
253
251
|
|
|
254
|
-
cache_line =
|
|
255
|
-
f"[dim]Saved run history[/dim]: {runs_text}{size_part}"
|
|
256
|
-
f" · {cache_stats.cache_dir}"
|
|
257
|
-
)
|
|
252
|
+
cache_line = f"[dim]Saved run history[/dim]: {runs_text}{size_part} · {cache_stats.cache_dir}"
|
|
258
253
|
return cache_line, None
|
|
259
254
|
|
|
260
255
|
|
|
261
|
-
def _display_cache_summary(
|
|
262
|
-
console: Console, slash_mode: bool, cache_line: str | None, cache_note: str | None
|
|
263
|
-
) -> None:
|
|
256
|
+
def _display_cache_summary(console: Console, slash_mode: bool, cache_line: str | None, cache_note: str | None) -> None:
|
|
264
257
|
"""Render the cache summary details."""
|
|
265
258
|
if cache_line:
|
|
266
259
|
console.print(cache_line)
|
|
@@ -268,9 +261,7 @@ def _display_cache_summary(
|
|
|
268
261
|
console.print(cache_note)
|
|
269
262
|
|
|
270
263
|
|
|
271
|
-
def _create_and_test_client(
|
|
272
|
-
config: dict, console: Console, *, compact: bool = False
|
|
273
|
-
) -> Client:
|
|
264
|
+
def _create_and_test_client(config: dict, console: Console, *, compact: bool = False) -> Client:
|
|
274
265
|
"""Create client and test connection by fetching resources."""
|
|
275
266
|
# Try to create client
|
|
276
267
|
client = Client(
|
|
@@ -308,13 +299,9 @@ def _create_and_test_client(
|
|
|
308
299
|
|
|
309
300
|
if compact:
|
|
310
301
|
connection_summary = "GL AIP reachable"
|
|
311
|
-
console.print(
|
|
312
|
-
f"[dim]• Base URL[/dim]: {client.api_url} ({connection_summary})"
|
|
313
|
-
)
|
|
302
|
+
console.print(f"[dim]• Base URL[/dim]: {client.api_url} ({connection_summary})")
|
|
314
303
|
console.print(f"[dim]• Agent timeout[/dim]: {DEFAULT_AGENT_RUN_TIMEOUT}s")
|
|
315
|
-
console.print(
|
|
316
|
-
f"[dim]• Resources[/dim]: agents {len(agents)}, tools {len(tools)}, mcps {len(mcps)}"
|
|
317
|
-
)
|
|
304
|
+
console.print(f"[dim]• Resources[/dim]: agents {len(agents)}, tools {len(tools)}, mcps {len(mcps)}")
|
|
318
305
|
else:
|
|
319
306
|
console.print( # pragma: no cover - UI display formatting
|
|
320
307
|
AIPPanel(
|
|
@@ -334,9 +321,7 @@ def _create_and_test_client(
|
|
|
334
321
|
status_text = "API call failed"
|
|
335
322
|
console.print(f"[dim]• Base URL[/dim]: {client.api_url} ({status_text})")
|
|
336
323
|
console.print(f"[{ERROR_STYLE}]• Error[/]: {e}")
|
|
337
|
-
console.print(
|
|
338
|
-
"[dim]• Tip[/dim]: Check network connectivity or API permissions and try again."
|
|
339
|
-
)
|
|
324
|
+
console.print("[dim]• Tip[/dim]: Check network connectivity or API permissions and try again.")
|
|
340
325
|
console.print("[dim]• Resources[/dim]: unavailable")
|
|
341
326
|
else:
|
|
342
327
|
console.print(
|
|
@@ -406,16 +391,12 @@ def status(ctx: Any) -> None:
|
|
|
406
391
|
@main.command()
|
|
407
392
|
def version() -> None:
|
|
408
393
|
"""Show version information."""
|
|
409
|
-
branding = AIPBranding.create_from_sdk(
|
|
410
|
-
sdk_version=_SDK_VERSION, package_name="glaip-sdk"
|
|
411
|
-
)
|
|
394
|
+
branding = AIPBranding.create_from_sdk(sdk_version=_SDK_VERSION, package_name="glaip-sdk")
|
|
412
395
|
branding.display_version_panel()
|
|
413
396
|
|
|
414
397
|
|
|
415
398
|
@main.command()
|
|
416
|
-
@click.option(
|
|
417
|
-
"--check-only", is_flag=True, help="Only check for updates without installing"
|
|
418
|
-
)
|
|
399
|
+
@click.option("--check-only", is_flag=True, help="Only check for updates without installing")
|
|
419
400
|
@click.option(
|
|
420
401
|
"--force",
|
|
421
402
|
is_flag=True,
|
|
@@ -429,8 +410,7 @@ def update(check_only: bool, force: bool) -> None:
|
|
|
429
410
|
if check_only:
|
|
430
411
|
console.print(
|
|
431
412
|
AIPPanel(
|
|
432
|
-
"[bold blue]🔍 Checking for updates...[/bold blue]\n\n"
|
|
433
|
-
"💡 To install updates, run: aip update",
|
|
413
|
+
"[bold blue]🔍 Checking for updates...[/bold blue]\n\n💡 To install updates, run: aip update",
|
|
434
414
|
title="📋 Update Check",
|
|
435
415
|
border_style="blue",
|
|
436
416
|
)
|