glaip-sdk 0.0.19__py3-none-any.whl → 0.1.0__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 +2 -2
- glaip_sdk/branding.py +27 -2
- glaip_sdk/cli/auth.py +93 -28
- glaip_sdk/cli/commands/__init__.py +2 -2
- glaip_sdk/cli/commands/agents.py +127 -21
- glaip_sdk/cli/commands/configure.py +141 -90
- glaip_sdk/cli/commands/mcps.py +82 -31
- glaip_sdk/cli/commands/models.py +4 -3
- glaip_sdk/cli/commands/tools.py +27 -14
- glaip_sdk/cli/commands/update.py +66 -0
- glaip_sdk/cli/config.py +13 -2
- glaip_sdk/cli/display.py +35 -26
- glaip_sdk/cli/io.py +14 -5
- glaip_sdk/cli/main.py +185 -73
- glaip_sdk/cli/pager.py +2 -1
- glaip_sdk/cli/resolution.py +4 -1
- glaip_sdk/cli/slash/__init__.py +3 -4
- glaip_sdk/cli/slash/agent_session.py +88 -36
- glaip_sdk/cli/slash/prompt.py +20 -48
- glaip_sdk/cli/slash/session.py +437 -189
- glaip_sdk/cli/transcript/__init__.py +71 -0
- glaip_sdk/cli/transcript/cache.py +338 -0
- glaip_sdk/cli/transcript/capture.py +278 -0
- glaip_sdk/cli/transcript/export.py +38 -0
- glaip_sdk/cli/transcript/launcher.py +79 -0
- glaip_sdk/cli/transcript/viewer.py +794 -0
- glaip_sdk/cli/update_notifier.py +29 -5
- glaip_sdk/cli/utils.py +255 -74
- glaip_sdk/client/agents.py +3 -1
- glaip_sdk/client/run_rendering.py +126 -21
- glaip_sdk/icons.py +25 -0
- glaip_sdk/models.py +6 -0
- glaip_sdk/rich_components.py +29 -1
- glaip_sdk/utils/__init__.py +1 -1
- glaip_sdk/utils/client_utils.py +6 -4
- glaip_sdk/utils/display.py +61 -32
- glaip_sdk/utils/rendering/formatting.py +55 -11
- glaip_sdk/utils/rendering/models.py +15 -2
- glaip_sdk/utils/rendering/renderer/__init__.py +0 -2
- glaip_sdk/utils/rendering/renderer/base.py +1287 -227
- glaip_sdk/utils/rendering/renderer/config.py +3 -5
- glaip_sdk/utils/rendering/renderer/debug.py +73 -16
- glaip_sdk/utils/rendering/renderer/panels.py +27 -15
- glaip_sdk/utils/rendering/renderer/progress.py +61 -38
- glaip_sdk/utils/rendering/renderer/stream.py +3 -3
- glaip_sdk/utils/rendering/renderer/toggle.py +184 -0
- glaip_sdk/utils/rendering/step_tree_state.py +102 -0
- glaip_sdk/utils/rendering/steps.py +944 -16
- glaip_sdk/utils/serialization.py +5 -2
- glaip_sdk/utils/validation.py +1 -2
- {glaip_sdk-0.0.19.dist-info → glaip_sdk-0.1.0.dist-info}/METADATA +12 -1
- glaip_sdk-0.1.0.dist-info/RECORD +82 -0
- glaip_sdk/utils/rich_utils.py +0 -29
- glaip_sdk-0.0.19.dist-info/RECORD +0 -73
- {glaip_sdk-0.0.19.dist-info → glaip_sdk-0.1.0.dist-info}/WHEEL +0 -0
- {glaip_sdk-0.0.19.dist-info → glaip_sdk-0.1.0.dist-info}/entry_points.txt +0 -0
|
@@ -12,10 +12,20 @@ from rich.text import Text
|
|
|
12
12
|
|
|
13
13
|
from glaip_sdk import Client
|
|
14
14
|
from glaip_sdk._version import __version__ as _SDK_VERSION
|
|
15
|
-
from glaip_sdk.branding import
|
|
15
|
+
from glaip_sdk.branding import (
|
|
16
|
+
ACCENT_STYLE,
|
|
17
|
+
ERROR_STYLE,
|
|
18
|
+
INFO,
|
|
19
|
+
PRIMARY,
|
|
20
|
+
SUCCESS,
|
|
21
|
+
SUCCESS_STYLE,
|
|
22
|
+
WARNING_STYLE,
|
|
23
|
+
AIPBranding,
|
|
24
|
+
)
|
|
16
25
|
from glaip_sdk.cli.config import CONFIG_FILE, load_config, save_config
|
|
17
26
|
from glaip_sdk.cli.rich_helpers import markup_text
|
|
18
|
-
from glaip_sdk.cli.utils import command_hint
|
|
27
|
+
from glaip_sdk.cli.utils import command_hint, format_command_hint
|
|
28
|
+
from glaip_sdk.icons import ICON_TOOL
|
|
19
29
|
from glaip_sdk.rich_components import AIPTable
|
|
20
30
|
|
|
21
31
|
console = Console()
|
|
@@ -33,29 +43,10 @@ def list_config() -> None:
|
|
|
33
43
|
config = load_config()
|
|
34
44
|
|
|
35
45
|
if not config:
|
|
36
|
-
|
|
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]")
|
|
46
|
+
_print_missing_config_hint()
|
|
43
47
|
return
|
|
44
48
|
|
|
45
|
-
|
|
46
|
-
table.add_column("Setting", style="cyan", width=20)
|
|
47
|
-
table.add_column("Value", style="green")
|
|
48
|
-
|
|
49
|
-
for key, value in config.items():
|
|
50
|
-
if key == "api_key" and value:
|
|
51
|
-
# Mask the API key
|
|
52
|
-
masked_value = "***" + value[-4:] if len(value) > 4 else "***"
|
|
53
|
-
table.add_row(key, masked_value)
|
|
54
|
-
else:
|
|
55
|
-
table.add_row(key, str(value))
|
|
56
|
-
|
|
57
|
-
console.print(table)
|
|
58
|
-
console.print(Text(f"\n📁 Config file: {CONFIG_FILE}"))
|
|
49
|
+
_render_config_table(config)
|
|
59
50
|
|
|
60
51
|
|
|
61
52
|
@config_group.command("set")
|
|
@@ -67,7 +58,7 @@ def set_config(key: str, value: str) -> None:
|
|
|
67
58
|
|
|
68
59
|
if key not in valid_keys:
|
|
69
60
|
console.print(
|
|
70
|
-
f"[
|
|
61
|
+
f"[{ERROR_STYLE}]Error: Invalid key '{key}'. Valid keys are: {', '.join(valid_keys)}[/]"
|
|
71
62
|
)
|
|
72
63
|
raise click.ClickException(f"Invalid configuration key: {key}")
|
|
73
64
|
|
|
@@ -76,10 +67,11 @@ def set_config(key: str, value: str) -> None:
|
|
|
76
67
|
save_config(config)
|
|
77
68
|
|
|
78
69
|
if key == "api_key":
|
|
79
|
-
|
|
80
|
-
|
|
70
|
+
console.print(
|
|
71
|
+
Text(f"✅ Set {key} = {_mask_api_key(value)}", style=SUCCESS_STYLE)
|
|
72
|
+
)
|
|
81
73
|
else:
|
|
82
|
-
console.print(Text(f"✅ Set {key} = {value}"))
|
|
74
|
+
console.print(Text(f"✅ Set {key} = {value}", style=SUCCESS_STYLE))
|
|
83
75
|
|
|
84
76
|
|
|
85
77
|
@config_group.command("get")
|
|
@@ -90,16 +82,14 @@ def get_config(key: str) -> None:
|
|
|
90
82
|
|
|
91
83
|
if key not in config:
|
|
92
84
|
console.print(
|
|
93
|
-
markup_text(f"[
|
|
85
|
+
markup_text(f"[{WARNING_STYLE}]Configuration key '{key}' not found.[/]")
|
|
94
86
|
)
|
|
95
87
|
raise click.ClickException(f"Configuration key not found: {key}")
|
|
96
88
|
|
|
97
89
|
value = config[key]
|
|
98
90
|
|
|
99
91
|
if key == "api_key":
|
|
100
|
-
|
|
101
|
-
masked_value = "***" + value[-4:] if len(value) > 4 else "***"
|
|
102
|
-
console.print(masked_value)
|
|
92
|
+
console.print(_mask_api_key(value))
|
|
103
93
|
else:
|
|
104
94
|
console.print(value)
|
|
105
95
|
|
|
@@ -112,14 +102,14 @@ def unset_config(key: str) -> None:
|
|
|
112
102
|
|
|
113
103
|
if key not in config:
|
|
114
104
|
console.print(
|
|
115
|
-
markup_text(f"[
|
|
105
|
+
markup_text(f"[{WARNING_STYLE}]Configuration key '{key}' not found.[/]")
|
|
116
106
|
)
|
|
117
107
|
return
|
|
118
108
|
|
|
119
109
|
del config[key]
|
|
120
110
|
save_config(config)
|
|
121
111
|
|
|
122
|
-
console.print(Text(f"✅ Removed {key} from configuration"))
|
|
112
|
+
console.print(Text(f"✅ Removed {key} from configuration", style=SUCCESS_STYLE))
|
|
123
113
|
|
|
124
114
|
|
|
125
115
|
@config_group.command("reset")
|
|
@@ -127,7 +117,7 @@ def unset_config(key: str) -> None:
|
|
|
127
117
|
def reset_config(force: bool) -> None:
|
|
128
118
|
"""Reset all configuration to defaults."""
|
|
129
119
|
if not force:
|
|
130
|
-
console.print("[
|
|
120
|
+
console.print(f"[{WARNING_STYLE}]This will remove all AIP configuration.[/]")
|
|
131
121
|
confirm = input("Are you sure? (y/N): ").strip().lower()
|
|
132
122
|
if confirm not in ["y", "yes"]:
|
|
133
123
|
console.print("Cancelled.")
|
|
@@ -137,8 +127,10 @@ def reset_config(force: bool) -> None:
|
|
|
137
127
|
file_exists = CONFIG_FILE.exists()
|
|
138
128
|
|
|
139
129
|
if not file_exists and not config_data:
|
|
140
|
-
console.print("[
|
|
141
|
-
console.print(
|
|
130
|
+
console.print(f"[{WARNING_STYLE}]No configuration found to reset.[/]")
|
|
131
|
+
console.print(
|
|
132
|
+
Text("✅ Configuration reset (nothing to remove).", style=SUCCESS_STYLE)
|
|
133
|
+
)
|
|
142
134
|
return
|
|
143
135
|
|
|
144
136
|
if file_exists:
|
|
@@ -151,103 +143,162 @@ def reset_config(force: bool) -> None:
|
|
|
151
143
|
save_config({})
|
|
152
144
|
|
|
153
145
|
hint = command_hint("config configure", slash_command="login")
|
|
154
|
-
message = "✅ Configuration reset."
|
|
146
|
+
message = Text("✅ Configuration reset.", style=SUCCESS_STYLE)
|
|
155
147
|
if hint:
|
|
156
|
-
message
|
|
148
|
+
message.append(f" Run '{hint}' to set up again.")
|
|
157
149
|
console.print(message)
|
|
158
150
|
|
|
159
151
|
|
|
160
152
|
def _configure_interactive() -> None:
|
|
161
153
|
"""Shared configuration logic for both configure commands."""
|
|
162
|
-
|
|
154
|
+
_render_configuration_header()
|
|
155
|
+
config = load_config()
|
|
156
|
+
_prompt_configuration_inputs(config)
|
|
157
|
+
_save_configuration(config)
|
|
158
|
+
_test_and_report_connection(config)
|
|
159
|
+
_print_post_configuration_hints()
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
@config_group.command()
|
|
163
|
+
def configure() -> None:
|
|
164
|
+
"""Configure AIP CLI credentials and settings interactively."""
|
|
165
|
+
_configure_interactive()
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
# Alias command for backward compatibility
|
|
169
|
+
@click.command()
|
|
170
|
+
def configure_command() -> None:
|
|
171
|
+
"""Configure AIP CLI credentials and settings interactively.
|
|
172
|
+
|
|
173
|
+
This is an alias for 'aip config configure' for backward compatibility.
|
|
174
|
+
"""
|
|
175
|
+
# Delegate to the shared function
|
|
176
|
+
_configure_interactive()
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
# Note: The config command group should be registered in main.py
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _mask_api_key(value: str | None) -> str:
|
|
183
|
+
if not value:
|
|
184
|
+
return ""
|
|
185
|
+
return "***" + value[-4:] if len(value) > 4 else "***"
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def _print_missing_config_hint() -> None:
|
|
189
|
+
hint = command_hint("config configure", slash_command="login")
|
|
190
|
+
if hint:
|
|
191
|
+
console.print(
|
|
192
|
+
f"[{WARNING_STYLE}]No configuration found.[/] Run {format_command_hint(hint) or hint} to set up."
|
|
193
|
+
)
|
|
194
|
+
else:
|
|
195
|
+
console.print(f"[{WARNING_STYLE}]No configuration found.[/]")
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _render_config_table(config: dict[str, str]) -> None:
|
|
199
|
+
table = AIPTable(title=f"{ICON_TOOL} AIP Configuration")
|
|
200
|
+
table.add_column("Setting", style=INFO, width=20)
|
|
201
|
+
table.add_column("Value", style=SUCCESS)
|
|
202
|
+
|
|
203
|
+
for key, value in config.items():
|
|
204
|
+
table.add_row(key, _mask_api_key(value) if key == "api_key" else str(value))
|
|
205
|
+
|
|
206
|
+
console.print(table)
|
|
207
|
+
console.print(Text(f"\n📁 Config file: {CONFIG_FILE}"))
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _render_configuration_header() -> None:
|
|
163
211
|
branding = AIPBranding.create_from_sdk(
|
|
164
212
|
sdk_version=_SDK_VERSION, package_name="glaip-sdk"
|
|
165
213
|
)
|
|
166
|
-
|
|
214
|
+
heading = "[bold]>_ GDP Labs AI Agents Package (AIP CLI)[/bold]"
|
|
215
|
+
console.print(heading)
|
|
216
|
+
console.print()
|
|
217
|
+
console.print(branding.get_welcome_banner())
|
|
218
|
+
console.rule("[bold]AIP Configuration[/bold]", style=PRIMARY)
|
|
167
219
|
|
|
168
|
-
# Load existing config
|
|
169
|
-
config = load_config()
|
|
170
220
|
|
|
221
|
+
def _prompt_configuration_inputs(config: dict[str, str]) -> None:
|
|
171
222
|
console.print("\n[bold]Enter your AIP configuration:[/bold]")
|
|
172
223
|
console.print("(Leave blank to keep current values)")
|
|
173
224
|
console.print("─" * 50)
|
|
174
225
|
|
|
175
|
-
|
|
226
|
+
_prompt_api_url(config)
|
|
227
|
+
_prompt_api_key(config)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def _prompt_api_url(config: dict[str, str]) -> None:
|
|
176
231
|
current_url = config.get("api_url", "")
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
)
|
|
232
|
+
suffix = f"(current: {current_url})" if current_url else ""
|
|
233
|
+
console.print(f"\n[{ACCENT_STYLE}]AIP API URL[/] {suffix}:")
|
|
180
234
|
new_url = input("> ").strip()
|
|
181
235
|
if new_url:
|
|
182
236
|
config["api_url"] = new_url
|
|
183
237
|
elif not current_url:
|
|
184
238
|
config["api_url"] = "https://your-aip-instance.com"
|
|
185
239
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
)
|
|
190
|
-
console.print(
|
|
191
|
-
f"\n[cyan]AIP API Key[/cyan] {f'(current: {current_key_masked})' if current_key_masked else ''}:"
|
|
192
|
-
)
|
|
240
|
+
|
|
241
|
+
def _prompt_api_key(config: dict[str, str]) -> None:
|
|
242
|
+
current_key_masked = _mask_api_key(config.get("api_key"))
|
|
243
|
+
suffix = f"(current: {current_key_masked})" if current_key_masked else ""
|
|
244
|
+
console.print(f"\n[{ACCENT_STYLE}]AIP API Key[/] {suffix}:")
|
|
193
245
|
new_key = getpass.getpass("> ")
|
|
194
246
|
if new_key:
|
|
195
247
|
config["api_key"] = new_key
|
|
196
248
|
|
|
197
|
-
|
|
249
|
+
|
|
250
|
+
def _save_configuration(config: dict[str, str]) -> None:
|
|
198
251
|
save_config(config)
|
|
252
|
+
console.print(
|
|
253
|
+
Text(f"\n✅ Configuration saved to: {CONFIG_FILE}", style=SUCCESS_STYLE)
|
|
254
|
+
)
|
|
199
255
|
|
|
200
|
-
console.print(Text(f"\n✅ Configuration saved to: {CONFIG_FILE}"))
|
|
201
256
|
|
|
202
|
-
|
|
257
|
+
def _test_and_report_connection(config: dict[str, str]) -> None:
|
|
203
258
|
console.print("\n🔌 Testing connection...")
|
|
259
|
+
client: Client | None = None
|
|
204
260
|
try:
|
|
205
|
-
# Create client with new config
|
|
206
261
|
client = Client(api_url=config["api_url"], api_key=config["api_key"])
|
|
207
|
-
|
|
208
|
-
# Try to list resources to test connection
|
|
209
262
|
try:
|
|
210
263
|
agents = client.list_agents()
|
|
211
|
-
console.print(
|
|
212
|
-
|
|
213
|
-
|
|
264
|
+
console.print(
|
|
265
|
+
Text(
|
|
266
|
+
f"✅ Connection successful! Found {len(agents)} agents",
|
|
267
|
+
style=SUCCESS_STYLE,
|
|
268
|
+
)
|
|
269
|
+
)
|
|
270
|
+
except Exception as exc: # pragma: no cover - API failures depend on network
|
|
271
|
+
console.print(
|
|
272
|
+
Text(
|
|
273
|
+
f"⚠️ Connection established but API call failed: {exc}",
|
|
274
|
+
style=WARNING_STYLE,
|
|
275
|
+
)
|
|
276
|
+
)
|
|
214
277
|
console.print(
|
|
215
278
|
" You may need to check your API permissions or network access"
|
|
216
279
|
)
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
except Exception as e:
|
|
221
|
-
console.print(Text(f"❌ Connection failed: {e}"))
|
|
280
|
+
except Exception as exc:
|
|
281
|
+
console.print(Text(f"❌ Connection failed: {exc}"))
|
|
222
282
|
console.print(" Please check your API URL and key")
|
|
223
283
|
hint_status = command_hint("status", slash_command="status")
|
|
224
284
|
if hint_status:
|
|
225
|
-
console.print(
|
|
285
|
+
console.print(
|
|
286
|
+
f" You can run {format_command_hint(hint_status) or hint_status} later to test again"
|
|
287
|
+
)
|
|
288
|
+
finally:
|
|
289
|
+
if client is not None:
|
|
290
|
+
client.close()
|
|
291
|
+
|
|
226
292
|
|
|
293
|
+
def _print_post_configuration_hints() -> None:
|
|
227
294
|
console.print("\n💡 You can now use AIP CLI commands!")
|
|
228
295
|
hint_status = command_hint("status", slash_command="status")
|
|
229
296
|
if hint_status:
|
|
230
|
-
console.print(
|
|
297
|
+
console.print(
|
|
298
|
+
f" • Run {format_command_hint(hint_status) or hint_status} to check connection"
|
|
299
|
+
)
|
|
231
300
|
hint_agents = command_hint("agents list", slash_command="agents")
|
|
232
301
|
if hint_agents:
|
|
233
|
-
console.print(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
@config_group.command()
|
|
237
|
-
def configure() -> None:
|
|
238
|
-
"""Configure AIP CLI credentials and settings interactively."""
|
|
239
|
-
_configure_interactive()
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
# Alias command for backward compatibility
|
|
243
|
-
@click.command()
|
|
244
|
-
def configure_command() -> None:
|
|
245
|
-
"""Configure AIP CLI credentials and settings interactively.
|
|
246
|
-
|
|
247
|
-
This is an alias for 'aip config configure' for backward compatibility.
|
|
248
|
-
"""
|
|
249
|
-
# Delegate to the shared function
|
|
250
|
-
_configure_interactive()
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
# Note: The config command group should be registered in main.py
|
|
302
|
+
console.print(
|
|
303
|
+
f" • Run {format_command_hint(hint_agents) or hint_agents} to see your agents"
|
|
304
|
+
)
|
glaip_sdk/cli/commands/mcps.py
CHANGED
|
@@ -13,6 +13,13 @@ from typing import Any
|
|
|
13
13
|
import click
|
|
14
14
|
from rich.console import Console
|
|
15
15
|
|
|
16
|
+
from glaip_sdk.branding import (
|
|
17
|
+
ACCENT_STYLE,
|
|
18
|
+
INFO,
|
|
19
|
+
SUCCESS,
|
|
20
|
+
SUCCESS_STYLE,
|
|
21
|
+
WARNING_STYLE,
|
|
22
|
+
)
|
|
16
23
|
from glaip_sdk.cli.context import detect_export_format, get_ctx_value, output_flags
|
|
17
24
|
from glaip_sdk.cli.display import (
|
|
18
25
|
display_api_error,
|
|
@@ -44,6 +51,7 @@ from glaip_sdk.cli.utils import (
|
|
|
44
51
|
from glaip_sdk.config.constants import (
|
|
45
52
|
DEFAULT_MCP_TYPE,
|
|
46
53
|
)
|
|
54
|
+
from glaip_sdk.icons import ICON_TOOL
|
|
47
55
|
from glaip_sdk.rich_components import AIPPanel
|
|
48
56
|
from glaip_sdk.utils import format_datetime
|
|
49
57
|
from glaip_sdk.utils.import_export import convert_export_to_import_format
|
|
@@ -73,6 +81,38 @@ def _is_sensitive_data(val: Any) -> bool:
|
|
|
73
81
|
)
|
|
74
82
|
|
|
75
83
|
|
|
84
|
+
def _redact_sensitive_dict(val: dict[str, Any]) -> dict[str, Any]:
|
|
85
|
+
"""Redact sensitive fields from a dictionary.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
val: Dictionary to redact
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Redacted dictionary
|
|
92
|
+
"""
|
|
93
|
+
redacted = val.copy()
|
|
94
|
+
sensitive_patterns = {"token", "password", "secret", "key", "credential"}
|
|
95
|
+
for k in redacted.keys():
|
|
96
|
+
if any(pattern in k.lower() for pattern in sensitive_patterns):
|
|
97
|
+
redacted[k] = "<REDACTED>"
|
|
98
|
+
return redacted
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _format_dict_value(val: dict[str, Any]) -> str:
|
|
102
|
+
"""Format a dictionary value for display.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
val: Dictionary to format
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Formatted string representation
|
|
109
|
+
"""
|
|
110
|
+
if _is_sensitive_data(val):
|
|
111
|
+
redacted = _redact_sensitive_dict(val)
|
|
112
|
+
return json.dumps(redacted, indent=2)
|
|
113
|
+
return json.dumps(val, indent=2)
|
|
114
|
+
|
|
115
|
+
|
|
76
116
|
def _format_preview_value(val: Any) -> str:
|
|
77
117
|
"""Format a value for display in update preview with sensitive data redaction.
|
|
78
118
|
|
|
@@ -85,15 +125,7 @@ def _format_preview_value(val: Any) -> str:
|
|
|
85
125
|
if val is None:
|
|
86
126
|
return "[dim]None[/dim]"
|
|
87
127
|
if isinstance(val, dict):
|
|
88
|
-
|
|
89
|
-
if _is_sensitive_data(val):
|
|
90
|
-
redacted = val.copy()
|
|
91
|
-
sensitive_patterns = {"token", "password", "secret", "key", "credential"}
|
|
92
|
-
for k in list(redacted.keys()):
|
|
93
|
-
if any(pattern in k.lower() for pattern in sensitive_patterns):
|
|
94
|
-
redacted[k] = "<REDACTED>"
|
|
95
|
-
return json.dumps(redacted, indent=2)
|
|
96
|
-
return json.dumps(val, indent=2)
|
|
128
|
+
return _format_dict_value(val)
|
|
97
129
|
if isinstance(val, str):
|
|
98
130
|
return f'"{val}"' if val else '""'
|
|
99
131
|
return str(val)
|
|
@@ -143,6 +175,26 @@ def _validate_import_payload_fields(import_payload: dict[str, Any]) -> bool:
|
|
|
143
175
|
return has_updatable
|
|
144
176
|
|
|
145
177
|
|
|
178
|
+
def _get_config_transport(
|
|
179
|
+
transport: str | None,
|
|
180
|
+
import_payload: dict[str, Any] | None,
|
|
181
|
+
mcp: Any,
|
|
182
|
+
) -> str | None:
|
|
183
|
+
"""Get the transport value for config validation.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
transport: CLI transport flag
|
|
187
|
+
import_payload: Optional import payload
|
|
188
|
+
mcp: Current MCP object
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
Transport value or None
|
|
192
|
+
"""
|
|
193
|
+
if import_payload:
|
|
194
|
+
return transport or import_payload.get("transport")
|
|
195
|
+
return transport or getattr(mcp, "transport", None)
|
|
196
|
+
|
|
197
|
+
|
|
146
198
|
def _build_update_data_from_sources(
|
|
147
199
|
import_payload: dict[str, Any] | None,
|
|
148
200
|
mcp: Any,
|
|
@@ -190,11 +242,7 @@ def _build_update_data_from_sources(
|
|
|
190
242
|
update_data["description"] = description
|
|
191
243
|
if config is not None:
|
|
192
244
|
parsed_config = parse_json_input(config)
|
|
193
|
-
config_transport = (
|
|
194
|
-
(transport or import_payload.get("transport"))
|
|
195
|
-
if import_payload
|
|
196
|
-
else (transport or getattr(mcp, "transport", None))
|
|
197
|
-
)
|
|
245
|
+
config_transport = _get_config_transport(transport, import_payload, mcp)
|
|
198
246
|
update_data["config"] = validate_mcp_config_structure(
|
|
199
247
|
parsed_config,
|
|
200
248
|
transport=config_transport,
|
|
@@ -487,8 +535,8 @@ def list_mcps(ctx: Any) -> None:
|
|
|
487
535
|
# Define table columns: (data_key, header, style, width)
|
|
488
536
|
columns = [
|
|
489
537
|
("id", "ID", "dim", 36),
|
|
490
|
-
("name", "Name",
|
|
491
|
-
("config", "Config",
|
|
538
|
+
("name", "Name", ACCENT_STYLE, None),
|
|
539
|
+
("config", "Config", INFO, None),
|
|
492
540
|
]
|
|
493
541
|
|
|
494
542
|
# Transform function for safe dictionary access
|
|
@@ -675,11 +723,11 @@ def _handle_mcp_export(
|
|
|
675
723
|
mcp = client.mcps.get_mcp_by_id(mcp.id)
|
|
676
724
|
except Exception as e:
|
|
677
725
|
print_markup(
|
|
678
|
-
f"[
|
|
726
|
+
f"[{WARNING_STYLE}]⚠️ Could not fetch full MCP details: {e}[/]",
|
|
679
727
|
console=console,
|
|
680
728
|
)
|
|
681
729
|
print_markup(
|
|
682
|
-
"[
|
|
730
|
+
f"[{WARNING_STYLE}]⚠️ Proceeding with available data[/]", console=console
|
|
683
731
|
)
|
|
684
732
|
|
|
685
733
|
# Determine if we should prompt for secrets
|
|
@@ -688,8 +736,8 @@ def _handle_mcp_export(
|
|
|
688
736
|
# Warn user if non-interactive mode forces placeholder usage
|
|
689
737
|
if not no_auth_prompt and not sys.stdin.isatty():
|
|
690
738
|
print_markup(
|
|
691
|
-
"[
|
|
692
|
-
"Using placeholder values for secrets.[/
|
|
739
|
+
f"[{WARNING_STYLE}]⚠️ Non-interactive mode detected. "
|
|
740
|
+
"Using placeholder values for secrets.[/]",
|
|
693
741
|
console=console,
|
|
694
742
|
)
|
|
695
743
|
|
|
@@ -724,8 +772,8 @@ def _handle_mcp_export(
|
|
|
724
772
|
write_resource_export(export_path, export_payload, detected_format)
|
|
725
773
|
|
|
726
774
|
print_markup(
|
|
727
|
-
f"[
|
|
728
|
-
f"{export_path} (format: {detected_format})[/
|
|
775
|
+
f"[{SUCCESS_STYLE}]✅ Complete MCP configuration exported to: "
|
|
776
|
+
f"{export_path} (format: {detected_format})[/]",
|
|
729
777
|
console=console,
|
|
730
778
|
)
|
|
731
779
|
|
|
@@ -767,7 +815,7 @@ def _display_mcp_details(ctx: Any, client: Any, mcp: Any) -> None:
|
|
|
767
815
|
)
|
|
768
816
|
else:
|
|
769
817
|
# Fall back to Pydantic model data
|
|
770
|
-
console.print("[
|
|
818
|
+
console.print(f"[{WARNING_STYLE}]Falling back to Pydantic model data[/]")
|
|
771
819
|
result_data = {
|
|
772
820
|
"id": str(getattr(mcp, "id", "N/A")),
|
|
773
821
|
"name": getattr(mcp, "name", "N/A"),
|
|
@@ -873,8 +921,8 @@ def list_tools(ctx: Any, mcp_ref: str) -> None:
|
|
|
873
921
|
|
|
874
922
|
# Define table columns: (data_key, header, style, width)
|
|
875
923
|
columns = [
|
|
876
|
-
("name", "Name",
|
|
877
|
-
("description", "Description",
|
|
924
|
+
("name", "Name", ACCENT_STYLE, None),
|
|
925
|
+
("description", "Description", INFO, 50),
|
|
878
926
|
]
|
|
879
927
|
|
|
880
928
|
# Transform function for safe dictionary access
|
|
@@ -887,7 +935,11 @@ def list_tools(ctx: Any, mcp_ref: str) -> None:
|
|
|
887
935
|
}
|
|
888
936
|
|
|
889
937
|
output_list(
|
|
890
|
-
ctx,
|
|
938
|
+
ctx,
|
|
939
|
+
tools,
|
|
940
|
+
f"{ICON_TOOL} Tools from MCP: {mcp.name}",
|
|
941
|
+
columns,
|
|
942
|
+
transform_tool,
|
|
891
943
|
)
|
|
892
944
|
|
|
893
945
|
except Exception as e:
|
|
@@ -927,7 +979,7 @@ def connect(ctx: Any, config_file: str) -> None:
|
|
|
927
979
|
view = get_ctx_value(ctx, "view", "rich")
|
|
928
980
|
if view != "json":
|
|
929
981
|
print_markup(
|
|
930
|
-
f"[
|
|
982
|
+
f"[{WARNING_STYLE}]Connecting to MCP with config from {config_file}...[/]",
|
|
931
983
|
console=console,
|
|
932
984
|
)
|
|
933
985
|
|
|
@@ -944,10 +996,10 @@ def connect(ctx: Any, config_file: str) -> None:
|
|
|
944
996
|
handle_json_output(ctx, result)
|
|
945
997
|
else:
|
|
946
998
|
success_panel = AIPPanel(
|
|
947
|
-
f"[
|
|
999
|
+
f"[{SUCCESS_STYLE}]✓[/] MCP connection successful!\n\n"
|
|
948
1000
|
f"[bold]Result:[/bold] {result}",
|
|
949
1001
|
title="🔌 Connection",
|
|
950
|
-
border_style=
|
|
1002
|
+
border_style=SUCCESS,
|
|
951
1003
|
)
|
|
952
1004
|
console.print(success_panel)
|
|
953
1005
|
|
|
@@ -1074,8 +1126,7 @@ def update(
|
|
|
1074
1126
|
)
|
|
1075
1127
|
if not import_file and not cli_flags_provided:
|
|
1076
1128
|
raise click.ClickException(
|
|
1077
|
-
"No update fields specified. Use --import
|
|
1078
|
-
"--name, --transport, --description, --config, --auth"
|
|
1129
|
+
"No update fields specified. Use --import or one of: --name, --transport, --description, --config, --auth"
|
|
1079
1130
|
)
|
|
1080
1131
|
|
|
1081
1132
|
# Resolve MCP using helper function
|
glaip_sdk/cli/commands/models.py
CHANGED
|
@@ -9,6 +9,7 @@ from typing import Any
|
|
|
9
9
|
import click
|
|
10
10
|
from rich.console import Console
|
|
11
11
|
|
|
12
|
+
from glaip_sdk.branding import ACCENT_STYLE, INFO, SUCCESS
|
|
12
13
|
from glaip_sdk.cli.context import output_flags
|
|
13
14
|
from glaip_sdk.cli.utils import (
|
|
14
15
|
get_client,
|
|
@@ -42,9 +43,9 @@ def list_models(ctx: Any) -> None:
|
|
|
42
43
|
# Define table columns: (data_key, header, style, width)
|
|
43
44
|
columns = [
|
|
44
45
|
("id", "ID", "dim", 36),
|
|
45
|
-
("provider", "Provider",
|
|
46
|
-
("name", "Model",
|
|
47
|
-
("base_url", "Base URL",
|
|
46
|
+
("provider", "Provider", ACCENT_STYLE, None),
|
|
47
|
+
("name", "Model", SUCCESS, None),
|
|
48
|
+
("base_url", "Base URL", INFO, None),
|
|
48
49
|
]
|
|
49
50
|
|
|
50
51
|
# Transform function for safe dictionary access
|