glaip-sdk 0.0.15__py3-none-any.whl → 0.0.17__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 +1 -1
- glaip_sdk/branding.py +28 -2
- glaip_sdk/cli/commands/agents.py +36 -27
- glaip_sdk/cli/commands/configure.py +46 -52
- glaip_sdk/cli/commands/mcps.py +19 -22
- glaip_sdk/cli/commands/tools.py +19 -13
- glaip_sdk/cli/config.py +42 -0
- glaip_sdk/cli/display.py +97 -30
- glaip_sdk/cli/main.py +141 -124
- glaip_sdk/cli/mcp_validators.py +2 -2
- glaip_sdk/cli/pager.py +3 -2
- glaip_sdk/cli/parsers/json_input.py +2 -2
- glaip_sdk/cli/resolution.py +12 -10
- glaip_sdk/cli/rich_helpers.py +29 -0
- glaip_sdk/cli/slash/agent_session.py +7 -0
- glaip_sdk/cli/slash/prompt.py +21 -2
- glaip_sdk/cli/slash/session.py +15 -21
- glaip_sdk/cli/update_notifier.py +8 -2
- glaip_sdk/cli/utils.py +115 -58
- glaip_sdk/client/_agent_payloads.py +504 -0
- glaip_sdk/client/agents.py +633 -559
- glaip_sdk/client/base.py +92 -20
- glaip_sdk/client/main.py +14 -0
- glaip_sdk/client/run_rendering.py +275 -0
- glaip_sdk/config/constants.py +4 -1
- glaip_sdk/exceptions.py +15 -0
- glaip_sdk/models.py +5 -0
- glaip_sdk/payload_schemas/__init__.py +19 -0
- glaip_sdk/payload_schemas/agent.py +87 -0
- glaip_sdk/rich_components.py +12 -0
- glaip_sdk/utils/client_utils.py +12 -0
- glaip_sdk/utils/import_export.py +2 -2
- glaip_sdk/utils/rendering/formatting.py +5 -0
- glaip_sdk/utils/rendering/models.py +22 -0
- glaip_sdk/utils/rendering/renderer/base.py +9 -1
- glaip_sdk/utils/rendering/renderer/panels.py +0 -1
- glaip_sdk/utils/rendering/steps.py +59 -0
- glaip_sdk/utils/serialization.py +24 -3
- {glaip_sdk-0.0.15.dist-info → glaip_sdk-0.0.17.dist-info}/METADATA +2 -2
- glaip_sdk-0.0.17.dist-info/RECORD +73 -0
- glaip_sdk-0.0.15.dist-info/RECORD +0 -67
- {glaip_sdk-0.0.15.dist-info → glaip_sdk-0.0.17.dist-info}/WHEEL +0 -0
- {glaip_sdk-0.0.15.dist-info → glaip_sdk-0.0.17.dist-info}/entry_points.txt +0 -0
glaip_sdk/__init__.py
CHANGED
glaip_sdk/branding.py
CHANGED
|
@@ -42,7 +42,7 @@ LABEL = "bold"
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
class AIPBranding:
|
|
45
|
-
"""GL AIP
|
|
45
|
+
"""GL AIP branding utilities with ASCII banner and version display."""
|
|
46
46
|
|
|
47
47
|
# GL AIP ASCII art - Modern block style with enhanced visibility
|
|
48
48
|
AIP_LOGO = r"""
|
|
@@ -60,7 +60,8 @@ GDP Labs AI Agents Package
|
|
|
60
60
|
version: str | None = None,
|
|
61
61
|
package_name: str | None = None,
|
|
62
62
|
) -> None:
|
|
63
|
-
"""
|
|
63
|
+
"""Initialize AIPBranding instance.
|
|
64
|
+
|
|
64
65
|
Args:
|
|
65
66
|
version: Explicit SDK version (overrides auto-detection).
|
|
66
67
|
package_name: If set, attempt to read version from installed package.
|
|
@@ -105,6 +106,11 @@ GDP Labs AI Agents Package
|
|
|
105
106
|
return banner
|
|
106
107
|
|
|
107
108
|
def get_version_info(self) -> dict:
|
|
109
|
+
"""Get comprehensive version information for the SDK.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
Dictionary containing version, Python version, platform, and architecture info
|
|
113
|
+
"""
|
|
108
114
|
return {
|
|
109
115
|
"version": self.version,
|
|
110
116
|
"python_version": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
|
|
@@ -113,6 +119,11 @@ GDP Labs AI Agents Package
|
|
|
113
119
|
}
|
|
114
120
|
|
|
115
121
|
def display_welcome_panel(self, title: str = "Welcome to AIP") -> None:
|
|
122
|
+
"""Display a welcome panel with branding.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
title: Custom title for the welcome panel
|
|
126
|
+
"""
|
|
116
127
|
banner = self.get_welcome_banner()
|
|
117
128
|
panel = AIPPanel(
|
|
118
129
|
banner,
|
|
@@ -123,6 +134,7 @@ GDP Labs AI Agents Package
|
|
|
123
134
|
self.console.print(panel)
|
|
124
135
|
|
|
125
136
|
def display_version_panel(self) -> None:
|
|
137
|
+
"""Display a panel with comprehensive version information."""
|
|
126
138
|
v = self.get_version_info()
|
|
127
139
|
version_text = (
|
|
128
140
|
f"[{TITLE_STYLE}]AIP SDK Version Information[/{TITLE_STYLE}]\n\n"
|
|
@@ -140,6 +152,11 @@ GDP Labs AI Agents Package
|
|
|
140
152
|
self.console.print(panel)
|
|
141
153
|
|
|
142
154
|
def display_status_banner(self, status: str = "ready") -> None:
|
|
155
|
+
"""Display a status banner for the current state.
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
status: Current status to display
|
|
159
|
+
"""
|
|
143
160
|
# Keep it simple (no emoji); easy to parse in logs/CI
|
|
144
161
|
banner = f"[{LABEL}]AIP[/{LABEL}] - {status.title()}"
|
|
145
162
|
self.console.print(banner)
|
|
@@ -148,4 +165,13 @@ GDP Labs AI Agents Package
|
|
|
148
165
|
def create_from_sdk(
|
|
149
166
|
cls, sdk_version: str | None = None, package_name: str | None = None
|
|
150
167
|
) -> AIPBranding:
|
|
168
|
+
"""Create AIPBranding instance from SDK package information.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
sdk_version: Explicit SDK version override
|
|
172
|
+
package_name: Package name to read version from
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
AIPBranding instance
|
|
176
|
+
"""
|
|
151
177
|
return cls(version=sdk_version, package_name=package_name)
|
glaip_sdk/cli/commands/agents.py
CHANGED
|
@@ -12,7 +12,6 @@ from typing import Any
|
|
|
12
12
|
|
|
13
13
|
import click
|
|
14
14
|
from rich.console import Console
|
|
15
|
-
from rich.text import Text
|
|
16
15
|
|
|
17
16
|
from glaip_sdk.cli.agent_config import (
|
|
18
17
|
merge_agent_config_with_cli_args as merge_import_with_cli_args,
|
|
@@ -45,6 +44,7 @@ from glaip_sdk.cli.io import (
|
|
|
45
44
|
load_resource_from_file_with_validation as load_resource_from_file,
|
|
46
45
|
)
|
|
47
46
|
from glaip_sdk.cli.resolution import resolve_resource_reference
|
|
47
|
+
from glaip_sdk.cli.rich_helpers import markup_text, print_markup
|
|
48
48
|
from glaip_sdk.cli.utils import (
|
|
49
49
|
_fuzzy_pick_for_resources,
|
|
50
50
|
build_renderer,
|
|
@@ -78,7 +78,6 @@ AGENT_NOT_FOUND_ERROR = "Agent not found"
|
|
|
78
78
|
|
|
79
79
|
def _safe_agent_attribute(agent: Any, name: str) -> Any:
|
|
80
80
|
"""Return attribute value for ``name`` while filtering Mock sentinels."""
|
|
81
|
-
|
|
82
81
|
try:
|
|
83
82
|
value = getattr(agent, name)
|
|
84
83
|
except Exception:
|
|
@@ -91,7 +90,6 @@ def _safe_agent_attribute(agent: Any, name: str) -> Any:
|
|
|
91
90
|
|
|
92
91
|
def _coerce_mapping_candidate(candidate: Any) -> dict[str, Any] | None:
|
|
93
92
|
"""Convert a mapping-like candidate to a plain dict when possible."""
|
|
94
|
-
|
|
95
93
|
if candidate is None:
|
|
96
94
|
return None
|
|
97
95
|
if isinstance(candidate, Mapping):
|
|
@@ -101,7 +99,6 @@ def _coerce_mapping_candidate(candidate: Any) -> dict[str, Any] | None:
|
|
|
101
99
|
|
|
102
100
|
def _call_agent_method(agent: Any, method_name: str) -> dict[str, Any] | None:
|
|
103
101
|
"""Attempt to call the named method and coerce its output to a dict."""
|
|
104
|
-
|
|
105
102
|
method = getattr(agent, method_name, None)
|
|
106
103
|
if not callable(method):
|
|
107
104
|
return None
|
|
@@ -114,7 +111,6 @@ def _call_agent_method(agent: Any, method_name: str) -> dict[str, Any] | None:
|
|
|
114
111
|
|
|
115
112
|
def _coerce_agent_via_methods(agent: Any) -> dict[str, Any] | None:
|
|
116
113
|
"""Try standard serialisation helpers to produce a mapping."""
|
|
117
|
-
|
|
118
114
|
for attr in ("model_dump", "dict", "to_dict"):
|
|
119
115
|
mapping = _call_agent_method(agent, attr)
|
|
120
116
|
if mapping is not None:
|
|
@@ -124,7 +120,6 @@ def _coerce_agent_via_methods(agent: Any) -> dict[str, Any] | None:
|
|
|
124
120
|
|
|
125
121
|
def _build_fallback_agent_mapping(agent: Any) -> dict[str, Any]:
|
|
126
122
|
"""Construct a minimal mapping from well-known agent attributes."""
|
|
127
|
-
|
|
128
123
|
fallback_fields = (
|
|
129
124
|
"id",
|
|
130
125
|
"name",
|
|
@@ -136,6 +131,7 @@ def _build_fallback_agent_mapping(agent: Any) -> dict[str, Any]:
|
|
|
136
131
|
"agents",
|
|
137
132
|
"mcps",
|
|
138
133
|
"timeout",
|
|
134
|
+
"tool_configs",
|
|
139
135
|
)
|
|
140
136
|
|
|
141
137
|
fallback: dict[str, Any] = {}
|
|
@@ -149,7 +145,6 @@ def _build_fallback_agent_mapping(agent: Any) -> dict[str, Any]:
|
|
|
149
145
|
|
|
150
146
|
def _prepare_agent_output(agent: Any) -> dict[str, Any]:
|
|
151
147
|
"""Build a JSON-serialisable mapping for CLI output."""
|
|
152
|
-
|
|
153
148
|
method_mapping = _coerce_agent_via_methods(agent)
|
|
154
149
|
if method_mapping is not None:
|
|
155
150
|
return method_mapping
|
|
@@ -256,11 +251,11 @@ def _format_fallback_agent_data(client: Any, agent: Any) -> dict:
|
|
|
256
251
|
"metadata",
|
|
257
252
|
"language_model_id",
|
|
258
253
|
"agent_config",
|
|
259
|
-
"tool_configs",
|
|
260
254
|
"tools",
|
|
261
255
|
"agents",
|
|
262
256
|
"mcps",
|
|
263
257
|
"a2a_profile",
|
|
258
|
+
"tool_configs",
|
|
264
259
|
]
|
|
265
260
|
|
|
266
261
|
result_data = build_resource_result_data(full_agent, fields)
|
|
@@ -280,7 +275,7 @@ def _format_fallback_agent_data(client: Any, agent: Any) -> dict:
|
|
|
280
275
|
def _display_agent_details(ctx: Any, client: Any, agent: Any) -> None:
|
|
281
276
|
"""Display full agent details using raw API data to preserve ALL fields."""
|
|
282
277
|
if agent is None:
|
|
283
|
-
handle_rich_output(ctx,
|
|
278
|
+
handle_rich_output(ctx, markup_text("[red]❌ No agent provided[/red]"))
|
|
284
279
|
return
|
|
285
280
|
|
|
286
281
|
# Try to fetch and format raw agent data first
|
|
@@ -297,13 +292,13 @@ def _display_agent_details(ctx: Any, client: Any, agent: Any) -> None:
|
|
|
297
292
|
output_result(
|
|
298
293
|
ctx,
|
|
299
294
|
formatted_data,
|
|
300
|
-
title=
|
|
301
|
-
panel_title=panel_title,
|
|
295
|
+
title=panel_title,
|
|
302
296
|
)
|
|
303
297
|
else:
|
|
304
298
|
# Fall back to Pydantic model data if raw fetch fails
|
|
305
299
|
handle_rich_output(
|
|
306
|
-
ctx,
|
|
300
|
+
ctx,
|
|
301
|
+
markup_text("[yellow]Falling back to Pydantic model data[/yellow]"),
|
|
307
302
|
)
|
|
308
303
|
|
|
309
304
|
with spinner_context(
|
|
@@ -318,7 +313,6 @@ def _display_agent_details(ctx: Any, client: Any, agent: Any) -> None:
|
|
|
318
313
|
ctx,
|
|
319
314
|
result_data,
|
|
320
315
|
title="Agent Details",
|
|
321
|
-
panel_title=f"🤖 {result_data.get('name', 'Unknown')}",
|
|
322
316
|
)
|
|
323
317
|
|
|
324
318
|
|
|
@@ -338,7 +332,14 @@ def _resolve_agent(
|
|
|
338
332
|
"""Resolve agent reference (ID or name) with ambiguity handling.
|
|
339
333
|
|
|
340
334
|
Args:
|
|
341
|
-
|
|
335
|
+
ctx: Click context object for CLI operations.
|
|
336
|
+
client: AIP client instance for API operations.
|
|
337
|
+
ref: Agent reference (ID or name) to resolve.
|
|
338
|
+
select: Pre-selected agent index for non-interactive mode.
|
|
339
|
+
interface_preference: "fuzzy" for fuzzy picker, "questionary" for up/down list.
|
|
340
|
+
|
|
341
|
+
Returns:
|
|
342
|
+
Resolved agent object or None if not found.
|
|
342
343
|
"""
|
|
343
344
|
return resolve_resource_reference(
|
|
344
345
|
ctx,
|
|
@@ -495,18 +496,19 @@ def get(ctx: Any, agent_ref: str, select: int | None, export: str | None) -> Non
|
|
|
495
496
|
except Exception as e:
|
|
496
497
|
handle_rich_output(
|
|
497
498
|
ctx,
|
|
498
|
-
|
|
499
|
+
markup_text(
|
|
499
500
|
f"[yellow]⚠️ Could not fetch full agent details: {e}[/yellow]"
|
|
500
501
|
),
|
|
501
502
|
)
|
|
502
503
|
handle_rich_output(
|
|
503
|
-
ctx,
|
|
504
|
+
ctx,
|
|
505
|
+
markup_text("[yellow]⚠️ Proceeding with available data[/yellow]"),
|
|
504
506
|
)
|
|
505
507
|
|
|
506
508
|
export_resource_to_file(agent, export_path, detected_format)
|
|
507
509
|
handle_rich_output(
|
|
508
510
|
ctx,
|
|
509
|
-
|
|
511
|
+
markup_text(
|
|
510
512
|
f"[green]✅ Complete agent configuration exported to: {export_path} (format: {detected_format})[/green]"
|
|
511
513
|
),
|
|
512
514
|
)
|
|
@@ -619,7 +621,7 @@ def _save_run_transcript(save: str | None, result: Any, working_console: Any) ->
|
|
|
619
621
|
|
|
620
622
|
with open(save, "w", encoding="utf-8") as f:
|
|
621
623
|
f.write(content)
|
|
622
|
-
|
|
624
|
+
print_markup(f"[green]Full debug output saved to: {save}[/green]", console=console)
|
|
623
625
|
|
|
624
626
|
|
|
625
627
|
@agents_group.command()
|
|
@@ -878,7 +880,6 @@ def _add_import_file_attributes(
|
|
|
878
880
|
"type",
|
|
879
881
|
"framework",
|
|
880
882
|
"version",
|
|
881
|
-
"tool_configs",
|
|
882
883
|
"mcps",
|
|
883
884
|
"a2a_profile",
|
|
884
885
|
}
|
|
@@ -1050,6 +1051,7 @@ def _handle_update_import_file(
|
|
|
1050
1051
|
instruction: str | None,
|
|
1051
1052
|
tools: tuple[str, ...] | None,
|
|
1052
1053
|
agents: tuple[str, ...] | None,
|
|
1054
|
+
mcps: tuple[str, ...] | None,
|
|
1053
1055
|
timeout: float | None,
|
|
1054
1056
|
) -> tuple[
|
|
1055
1057
|
Any | None,
|
|
@@ -1057,11 +1059,12 @@ def _handle_update_import_file(
|
|
|
1057
1059
|
str | None,
|
|
1058
1060
|
tuple[str, ...] | None,
|
|
1059
1061
|
tuple[str, ...] | None,
|
|
1062
|
+
tuple[str, ...] | None,
|
|
1060
1063
|
float | None,
|
|
1061
1064
|
]:
|
|
1062
1065
|
"""Handle import file processing for agent update."""
|
|
1063
1066
|
if not import_file:
|
|
1064
|
-
return None, name, instruction, tools, agents, timeout
|
|
1067
|
+
return None, name, instruction, tools, agents, mcps, timeout
|
|
1065
1068
|
|
|
1066
1069
|
import_data = load_resource_from_file(Path(import_file), "agent")
|
|
1067
1070
|
import_data = convert_export_to_import_format(import_data)
|
|
@@ -1072,6 +1075,7 @@ def _handle_update_import_file(
|
|
|
1072
1075
|
"instruction": instruction,
|
|
1073
1076
|
"tools": tools or (),
|
|
1074
1077
|
"agents": agents or (),
|
|
1078
|
+
"mcps": mcps or (),
|
|
1075
1079
|
"timeout": timeout,
|
|
1076
1080
|
}
|
|
1077
1081
|
|
|
@@ -1083,6 +1087,7 @@ def _handle_update_import_file(
|
|
|
1083
1087
|
merged_data.get("instruction"),
|
|
1084
1088
|
tuple(merged_data.get("tools", ())),
|
|
1085
1089
|
tuple(merged_data.get("agents", ())),
|
|
1090
|
+
tuple(merged_data.get("mcps", ())),
|
|
1086
1091
|
coerce_timeout(merged_data.get("timeout")),
|
|
1087
1092
|
)
|
|
1088
1093
|
|
|
@@ -1092,6 +1097,7 @@ def _build_update_data(
|
|
|
1092
1097
|
instruction: str | None,
|
|
1093
1098
|
tools: tuple[str, ...] | None,
|
|
1094
1099
|
agents: tuple[str, ...] | None,
|
|
1100
|
+
mcps: tuple[str, ...] | None,
|
|
1095
1101
|
timeout: float | None,
|
|
1096
1102
|
) -> dict[str, Any]:
|
|
1097
1103
|
"""Build the update data dictionary from provided parameters."""
|
|
@@ -1104,6 +1110,8 @@ def _build_update_data(
|
|
|
1104
1110
|
update_data["tools"] = list(tools)
|
|
1105
1111
|
if agents:
|
|
1106
1112
|
update_data["agents"] = list(agents)
|
|
1113
|
+
if mcps:
|
|
1114
|
+
update_data["mcps"] = list(mcps)
|
|
1107
1115
|
if timeout is not None:
|
|
1108
1116
|
update_data["timeout"] = timeout
|
|
1109
1117
|
return update_data
|
|
@@ -1141,8 +1149,6 @@ def _handle_update_import_config(
|
|
|
1141
1149
|
"type",
|
|
1142
1150
|
"framework",
|
|
1143
1151
|
"version",
|
|
1144
|
-
"tool_configs",
|
|
1145
|
-
"mcps",
|
|
1146
1152
|
"a2a_profile",
|
|
1147
1153
|
}
|
|
1148
1154
|
for key, value in merged_data.items():
|
|
@@ -1156,6 +1162,7 @@ def _handle_update_import_config(
|
|
|
1156
1162
|
@click.option("--instruction", help="New instruction")
|
|
1157
1163
|
@click.option("--tools", multiple=True, help="New tool names or IDs")
|
|
1158
1164
|
@click.option("--agents", multiple=True, help="New sub-agent names")
|
|
1165
|
+
@click.option("--mcps", multiple=True, help="New MCP names or IDs")
|
|
1159
1166
|
@click.option("--timeout", type=int, help="New timeout value")
|
|
1160
1167
|
@click.option(
|
|
1161
1168
|
"--import",
|
|
@@ -1172,6 +1179,7 @@ def update(
|
|
|
1172
1179
|
instruction: str | None,
|
|
1173
1180
|
tools: tuple[str, ...] | None,
|
|
1174
1181
|
agents: tuple[str, ...] | None,
|
|
1182
|
+
mcps: tuple[str, ...] | None,
|
|
1175
1183
|
timeout: float | None,
|
|
1176
1184
|
import_file: str | None,
|
|
1177
1185
|
) -> None:
|
|
@@ -1192,12 +1200,15 @@ def update(
|
|
|
1192
1200
|
instruction,
|
|
1193
1201
|
tools,
|
|
1194
1202
|
agents,
|
|
1203
|
+
mcps,
|
|
1195
1204
|
timeout,
|
|
1196
1205
|
) = _handle_update_import_file(
|
|
1197
|
-
import_file, name, instruction, tools, agents, timeout
|
|
1206
|
+
import_file, name, instruction, tools, agents, mcps, timeout
|
|
1198
1207
|
)
|
|
1199
1208
|
|
|
1200
|
-
update_data = _build_update_data(
|
|
1209
|
+
update_data = _build_update_data(
|
|
1210
|
+
name, instruction, tools, agents, mcps, timeout
|
|
1211
|
+
)
|
|
1201
1212
|
|
|
1202
1213
|
if merged_data:
|
|
1203
1214
|
_handle_update_import_config(import_file, merged_data, update_data)
|
|
@@ -1297,8 +1308,6 @@ def sync_langflow(ctx: Any, base_url: str | None, api_key: str | None) -> None:
|
|
|
1297
1308
|
|
|
1298
1309
|
# Show success message for non-JSON output
|
|
1299
1310
|
if get_ctx_value(ctx, "view") != "json":
|
|
1300
|
-
from rich.text import Text
|
|
1301
|
-
|
|
1302
1311
|
# Extract some useful info from the result
|
|
1303
1312
|
success_count = result.get("data", {}).get("created_count", 0) + result.get(
|
|
1304
1313
|
"data", {}
|
|
@@ -1307,7 +1316,7 @@ def sync_langflow(ctx: Any, base_url: str | None, api_key: str | None) -> None:
|
|
|
1307
1316
|
|
|
1308
1317
|
handle_rich_output(
|
|
1309
1318
|
ctx,
|
|
1310
|
-
|
|
1319
|
+
markup_text(
|
|
1311
1320
|
f"[green]✅ Successfully synced {success_count} LangFlow agents ({total_count} total processed)[/green]"
|
|
1312
1321
|
),
|
|
1313
1322
|
)
|
|
@@ -5,51 +5,21 @@ Authors:
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import getpass
|
|
8
|
-
import os
|
|
9
|
-
from pathlib import Path
|
|
10
|
-
from typing import Any
|
|
11
8
|
|
|
12
9
|
import click
|
|
13
|
-
import yaml
|
|
14
10
|
from rich.console import Console
|
|
15
11
|
from rich.text import Text
|
|
16
12
|
|
|
17
13
|
from glaip_sdk import Client
|
|
18
14
|
from glaip_sdk._version import __version__ as _SDK_VERSION
|
|
19
15
|
from glaip_sdk.branding import AIPBranding
|
|
16
|
+
from glaip_sdk.cli.config import CONFIG_FILE, load_config, save_config
|
|
17
|
+
from glaip_sdk.cli.rich_helpers import markup_text
|
|
18
|
+
from glaip_sdk.cli.utils import command_hint
|
|
20
19
|
from glaip_sdk.rich_components import AIPTable
|
|
21
20
|
|
|
22
21
|
console = Console()
|
|
23
22
|
|
|
24
|
-
CONFIG_DIR = Path.home() / ".aip"
|
|
25
|
-
CONFIG_FILE = CONFIG_DIR / "config.yaml"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def load_config() -> dict[str, Any]:
|
|
29
|
-
"""Load configuration from file."""
|
|
30
|
-
if not CONFIG_FILE.exists():
|
|
31
|
-
return {}
|
|
32
|
-
|
|
33
|
-
try:
|
|
34
|
-
with open(CONFIG_FILE) as f:
|
|
35
|
-
return yaml.safe_load(f) or {}
|
|
36
|
-
except yaml.YAMLError:
|
|
37
|
-
return {}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def save_config(config: dict[str, Any]) -> None:
|
|
41
|
-
"""Save configuration to file."""
|
|
42
|
-
CONFIG_DIR.mkdir(exist_ok=True)
|
|
43
|
-
|
|
44
|
-
with open(CONFIG_FILE, "w") as f:
|
|
45
|
-
yaml.dump(config, f, default_flow_style=False)
|
|
46
|
-
|
|
47
|
-
# Set secure file permissions
|
|
48
|
-
try:
|
|
49
|
-
os.chmod(CONFIG_FILE, 0o600)
|
|
50
|
-
except Exception: # pragma: no cover - platform dependent best effort
|
|
51
|
-
pass
|
|
52
|
-
|
|
53
23
|
|
|
54
24
|
@click.group()
|
|
55
25
|
def config_group() -> None:
|
|
@@ -60,13 +30,16 @@ def config_group() -> None:
|
|
|
60
30
|
@config_group.command("list")
|
|
61
31
|
def list_config() -> None:
|
|
62
32
|
"""List current configuration."""
|
|
63
|
-
|
|
64
33
|
config = load_config()
|
|
65
34
|
|
|
66
35
|
if not config:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
36
|
+
hint = command_hint("config configure", slash_command="login")
|
|
37
|
+
if hint:
|
|
38
|
+
console.print(
|
|
39
|
+
f"[yellow]No configuration found. Run '{hint}' to set up.[/yellow]"
|
|
40
|
+
)
|
|
41
|
+
else:
|
|
42
|
+
console.print("[yellow]No configuration found.[/yellow]")
|
|
70
43
|
return
|
|
71
44
|
|
|
72
45
|
table = AIPTable(title="🔧 AIP Configuration")
|
|
@@ -90,7 +63,6 @@ def list_config() -> None:
|
|
|
90
63
|
@click.argument("value")
|
|
91
64
|
def set_config(key: str, value: str) -> None:
|
|
92
65
|
"""Set a configuration value."""
|
|
93
|
-
|
|
94
66
|
valid_keys = ["api_url", "api_key"]
|
|
95
67
|
|
|
96
68
|
if key not in valid_keys:
|
|
@@ -114,11 +86,12 @@ def set_config(key: str, value: str) -> None:
|
|
|
114
86
|
@click.argument("key")
|
|
115
87
|
def get_config(key: str) -> None:
|
|
116
88
|
"""Get a configuration value."""
|
|
117
|
-
|
|
118
89
|
config = load_config()
|
|
119
90
|
|
|
120
91
|
if key not in config:
|
|
121
|
-
console.print(
|
|
92
|
+
console.print(
|
|
93
|
+
markup_text(f"[yellow]Configuration key '{key}' not found.[/yellow]")
|
|
94
|
+
)
|
|
122
95
|
raise click.ClickException(f"Configuration key not found: {key}")
|
|
123
96
|
|
|
124
97
|
value = config[key]
|
|
@@ -135,11 +108,12 @@ def get_config(key: str) -> None:
|
|
|
135
108
|
@click.argument("key")
|
|
136
109
|
def unset_config(key: str) -> None:
|
|
137
110
|
"""Remove a configuration value."""
|
|
138
|
-
|
|
139
111
|
config = load_config()
|
|
140
112
|
|
|
141
113
|
if key not in config:
|
|
142
|
-
console.print(
|
|
114
|
+
console.print(
|
|
115
|
+
markup_text(f"[yellow]Configuration key '{key}' not found.[/yellow]")
|
|
116
|
+
)
|
|
143
117
|
return
|
|
144
118
|
|
|
145
119
|
del config[key]
|
|
@@ -152,7 +126,6 @@ def unset_config(key: str) -> None:
|
|
|
152
126
|
@click.option("--force", is_flag=True, help="Skip confirmation prompt")
|
|
153
127
|
def reset_config(force: bool) -> None:
|
|
154
128
|
"""Reset all configuration to defaults."""
|
|
155
|
-
|
|
156
129
|
if not force:
|
|
157
130
|
console.print("[yellow]This will remove all AIP configuration.[/yellow]")
|
|
158
131
|
confirm = input("Are you sure? (y/N): ").strip().lower()
|
|
@@ -160,13 +133,28 @@ def reset_config(force: bool) -> None:
|
|
|
160
133
|
console.print("Cancelled.")
|
|
161
134
|
return
|
|
162
135
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
)
|
|
168
|
-
else:
|
|
136
|
+
config_data = load_config()
|
|
137
|
+
file_exists = CONFIG_FILE.exists()
|
|
138
|
+
|
|
139
|
+
if not file_exists and not config_data:
|
|
169
140
|
console.print("[yellow]No configuration found to reset.[/yellow]")
|
|
141
|
+
console.print("✅ Configuration reset (nothing to remove).")
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
if file_exists:
|
|
145
|
+
try:
|
|
146
|
+
CONFIG_FILE.unlink()
|
|
147
|
+
except FileNotFoundError: # pragma: no cover - defensive cleanup
|
|
148
|
+
pass
|
|
149
|
+
else:
|
|
150
|
+
# In-memory configuration (e.g., tests) needs explicit clearing
|
|
151
|
+
save_config({})
|
|
152
|
+
|
|
153
|
+
hint = command_hint("config configure", slash_command="login")
|
|
154
|
+
message = "✅ Configuration reset."
|
|
155
|
+
if hint:
|
|
156
|
+
message += f" Run '{hint}' to set up again."
|
|
157
|
+
console.print(message)
|
|
170
158
|
|
|
171
159
|
|
|
172
160
|
def _configure_interactive() -> None:
|
|
@@ -232,11 +220,17 @@ def _configure_interactive() -> None:
|
|
|
232
220
|
except Exception as e:
|
|
233
221
|
console.print(Text(f"❌ Connection failed: {e}"))
|
|
234
222
|
console.print(" Please check your API URL and key")
|
|
235
|
-
|
|
223
|
+
hint_status = command_hint("status", slash_command="status")
|
|
224
|
+
if hint_status:
|
|
225
|
+
console.print(f" You can run '{hint_status}' later to test again")
|
|
236
226
|
|
|
237
227
|
console.print("\n💡 You can now use AIP CLI commands!")
|
|
238
|
-
|
|
239
|
-
|
|
228
|
+
hint_status = command_hint("status", slash_command="status")
|
|
229
|
+
if hint_status:
|
|
230
|
+
console.print(f" • Run '{hint_status}' to check connection")
|
|
231
|
+
hint_agents = command_hint("agents list", slash_command="agents")
|
|
232
|
+
if hint_agents:
|
|
233
|
+
console.print(f" • Run '{hint_agents}' to see your agents")
|
|
240
234
|
|
|
241
235
|
|
|
242
236
|
@config_group.command()
|
glaip_sdk/cli/commands/mcps.py
CHANGED
|
@@ -11,7 +11,6 @@ from typing import Any
|
|
|
11
11
|
|
|
12
12
|
import click
|
|
13
13
|
from rich.console import Console
|
|
14
|
-
from rich.text import Text
|
|
15
14
|
|
|
16
15
|
from glaip_sdk.cli.context import detect_export_format, get_ctx_value, output_flags
|
|
17
16
|
from glaip_sdk.cli.display import (
|
|
@@ -33,6 +32,7 @@ from glaip_sdk.cli.mcp_validators import (
|
|
|
33
32
|
)
|
|
34
33
|
from glaip_sdk.cli.parsers.json_input import parse_json_input
|
|
35
34
|
from glaip_sdk.cli.resolution import resolve_resource_reference
|
|
35
|
+
from glaip_sdk.cli.rich_helpers import print_markup
|
|
36
36
|
from glaip_sdk.cli.utils import (
|
|
37
37
|
coerce_to_row,
|
|
38
38
|
get_client,
|
|
@@ -481,21 +481,23 @@ def _handle_mcp_export(
|
|
|
481
481
|
):
|
|
482
482
|
mcp = client.mcps.get_mcp_by_id(mcp.id)
|
|
483
483
|
except Exception as e:
|
|
484
|
-
|
|
485
|
-
|
|
484
|
+
print_markup(
|
|
485
|
+
f"[yellow]⚠️ Could not fetch full MCP details: {e}[/yellow]",
|
|
486
|
+
console=console,
|
|
487
|
+
)
|
|
488
|
+
print_markup(
|
|
489
|
+
"[yellow]⚠️ Proceeding with available data[/yellow]", console=console
|
|
486
490
|
)
|
|
487
|
-
console.print(Text("[yellow]⚠️ Proceeding with available data[/yellow]"))
|
|
488
491
|
|
|
489
492
|
# Determine if we should prompt for secrets
|
|
490
493
|
prompt_for_secrets = not no_auth_prompt and sys.stdin.isatty()
|
|
491
494
|
|
|
492
495
|
# Warn user if non-interactive mode forces placeholder usage
|
|
493
496
|
if not no_auth_prompt and not sys.stdin.isatty():
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
)
|
|
497
|
+
print_markup(
|
|
498
|
+
"[yellow]⚠️ Non-interactive mode detected. "
|
|
499
|
+
"Using placeholder values for secrets.[/yellow]",
|
|
500
|
+
console=console,
|
|
499
501
|
)
|
|
500
502
|
|
|
501
503
|
# Build and write export payload
|
|
@@ -528,11 +530,10 @@ def _handle_mcp_export(
|
|
|
528
530
|
)
|
|
529
531
|
write_resource_export(export_path, export_payload, detected_format)
|
|
530
532
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
)
|
|
533
|
+
print_markup(
|
|
534
|
+
f"[green]✅ Complete MCP configuration exported to: "
|
|
535
|
+
f"{export_path} (format: {detected_format})[/green]",
|
|
536
|
+
console=console,
|
|
536
537
|
)
|
|
537
538
|
|
|
538
539
|
|
|
@@ -582,9 +583,7 @@ def _display_mcp_details(ctx: Any, client: Any, mcp: Any) -> None:
|
|
|
582
583
|
"status": getattr(mcp, "status", "N/A"),
|
|
583
584
|
"connection_status": getattr(mcp, "connection_status", "N/A"),
|
|
584
585
|
}
|
|
585
|
-
output_result(
|
|
586
|
-
ctx, result_data, title="MCP Details", panel_title=f"🔌 {mcp.name}"
|
|
587
|
-
)
|
|
586
|
+
output_result(ctx, result_data, title=f"🔌 {mcp.name}")
|
|
588
587
|
|
|
589
588
|
|
|
590
589
|
@mcps_group.command()
|
|
@@ -734,11 +733,9 @@ def connect(ctx: Any, config_file: str) -> None:
|
|
|
734
733
|
|
|
735
734
|
view = get_ctx_value(ctx, "view", "rich")
|
|
736
735
|
if view != "json":
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
f"{config_file}...[/yellow]"
|
|
741
|
-
)
|
|
736
|
+
print_markup(
|
|
737
|
+
f"[yellow]Connecting to MCP with config from {config_file}...[/yellow]",
|
|
738
|
+
console=console,
|
|
742
739
|
)
|
|
743
740
|
|
|
744
741
|
# Test connection using config
|