glaip-sdk 0.0.15__py3-none-any.whl → 0.0.16__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/branding.py +27 -1
- glaip_sdk/cli/commands/agents.py +26 -17
- glaip_sdk/cli/commands/configure.py +39 -50
- glaip_sdk/cli/commands/mcps.py +1 -3
- glaip_sdk/cli/config.py +42 -0
- glaip_sdk/cli/display.py +92 -26
- 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/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 +110 -53
- glaip_sdk/client/_agent_payloads.py +504 -0
- glaip_sdk/client/agents.py +194 -551
- glaip_sdk/client/base.py +92 -20
- glaip_sdk/client/main.py +6 -0
- glaip_sdk/client/run_rendering.py +275 -0
- glaip_sdk/config/constants.py +3 -0
- 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.16.dist-info}/METADATA +1 -1
- glaip_sdk-0.0.16.dist-info/RECORD +72 -0
- glaip_sdk-0.0.15.dist-info/RECORD +0 -67
- {glaip_sdk-0.0.15.dist-info → glaip_sdk-0.0.16.dist-info}/WHEEL +0 -0
- {glaip_sdk-0.0.15.dist-info → glaip_sdk-0.0.16.dist-info}/entry_points.txt +0 -0
glaip_sdk/cli/utils.py
CHANGED
|
@@ -21,6 +21,8 @@ from rich.markdown import Markdown
|
|
|
21
21
|
from rich.pretty import Pretty
|
|
22
22
|
from rich.text import Text
|
|
23
23
|
|
|
24
|
+
from glaip_sdk.rich_components import AIPPanel
|
|
25
|
+
|
|
24
26
|
# Optional interactive deps (fuzzy palette)
|
|
25
27
|
try:
|
|
26
28
|
from prompt_toolkit.completion import Completion
|
|
@@ -38,9 +40,15 @@ except Exception: # pragma: no cover - optional dependency
|
|
|
38
40
|
if TYPE_CHECKING: # pragma: no cover - import-only during type checking
|
|
39
41
|
from glaip_sdk import Client
|
|
40
42
|
from glaip_sdk.cli import masking, pager
|
|
41
|
-
from glaip_sdk.cli.
|
|
42
|
-
from glaip_sdk.cli.context import
|
|
43
|
-
|
|
43
|
+
from glaip_sdk.cli.config import load_config
|
|
44
|
+
from glaip_sdk.cli.context import (
|
|
45
|
+
_get_view,
|
|
46
|
+
get_ctx_value,
|
|
47
|
+
)
|
|
48
|
+
from glaip_sdk.cli.context import (
|
|
49
|
+
detect_export_format as _detect_export_format,
|
|
50
|
+
)
|
|
51
|
+
from glaip_sdk.rich_components import AIPTable
|
|
44
52
|
from glaip_sdk.utils import is_uuid
|
|
45
53
|
from glaip_sdk.utils.rendering.renderer import (
|
|
46
54
|
CapturingConsole,
|
|
@@ -53,6 +61,59 @@ pager.console = console
|
|
|
53
61
|
logger = logging.getLogger("glaip_sdk.cli.utils")
|
|
54
62
|
|
|
55
63
|
|
|
64
|
+
# ----------------------------- Context helpers ---------------------------- #
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def detect_export_format(file_path: str | os.PathLike[str]) -> str:
|
|
68
|
+
"""Backward-compatible proxy to `glaip_sdk.cli.context.detect_export_format`."""
|
|
69
|
+
return _detect_export_format(file_path)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def in_slash_mode(ctx: click.Context | None = None) -> bool:
|
|
73
|
+
"""Return True when running inside the slash command palette."""
|
|
74
|
+
if ctx is None:
|
|
75
|
+
try:
|
|
76
|
+
ctx = click.get_current_context(silent=True)
|
|
77
|
+
except RuntimeError:
|
|
78
|
+
ctx = None
|
|
79
|
+
|
|
80
|
+
if ctx is None:
|
|
81
|
+
return False
|
|
82
|
+
|
|
83
|
+
obj = getattr(ctx, "obj", None)
|
|
84
|
+
if isinstance(obj, dict):
|
|
85
|
+
return bool(obj.get("_slash_session"))
|
|
86
|
+
|
|
87
|
+
return bool(getattr(obj, "_slash_session", False))
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def command_hint(
|
|
91
|
+
cli_command: str | None,
|
|
92
|
+
slash_command: str | None = None,
|
|
93
|
+
*,
|
|
94
|
+
ctx: click.Context | None = None,
|
|
95
|
+
) -> str | None:
|
|
96
|
+
"""Return the appropriate command string for the current mode.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
cli_command: Command string without the ``aip`` prefix (e.g., ``"status"``).
|
|
100
|
+
slash_command: Slash command counterpart (e.g., ``"status"`` or ``"/status"``).
|
|
101
|
+
ctx: Optional Click context override.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
The formatted command string for the active mode, or ``None`` when no
|
|
105
|
+
equivalent command exists in that mode.
|
|
106
|
+
"""
|
|
107
|
+
if in_slash_mode(ctx):
|
|
108
|
+
if not slash_command:
|
|
109
|
+
return None
|
|
110
|
+
return slash_command if slash_command.startswith("/") else f"/{slash_command}"
|
|
111
|
+
|
|
112
|
+
if not cli_command:
|
|
113
|
+
return None
|
|
114
|
+
return f"aip {cli_command}"
|
|
115
|
+
|
|
116
|
+
|
|
56
117
|
def spinner_context(
|
|
57
118
|
ctx: Any | None,
|
|
58
119
|
message: str,
|
|
@@ -62,7 +123,6 @@ def spinner_context(
|
|
|
62
123
|
spinner_style: str = "cyan",
|
|
63
124
|
) -> AbstractContextManager[Any]:
|
|
64
125
|
"""Return a context manager that renders a spinner when appropriate."""
|
|
65
|
-
|
|
66
126
|
active_console = console_override or console
|
|
67
127
|
if not _can_use_spinner(ctx, active_console):
|
|
68
128
|
return nullcontext()
|
|
@@ -76,7 +136,6 @@ def spinner_context(
|
|
|
76
136
|
|
|
77
137
|
def _can_use_spinner(ctx: Any | None, active_console: Console) -> bool:
|
|
78
138
|
"""Check if spinner output is allowed in the current environment."""
|
|
79
|
-
|
|
80
139
|
if ctx is not None:
|
|
81
140
|
tty_enabled = bool(get_ctx_value(ctx, "tty", True))
|
|
82
141
|
view = (_get_view(ctx) or "rich").lower()
|
|
@@ -91,7 +150,6 @@ def _can_use_spinner(ctx: Any | None, active_console: Console) -> bool:
|
|
|
91
150
|
|
|
92
151
|
def _stream_supports_tty(stream: Any) -> bool:
|
|
93
152
|
"""Return True if the provided stream can safely render a spinner."""
|
|
94
|
-
|
|
95
153
|
target = stream if hasattr(stream, "isatty") else sys.stdout
|
|
96
154
|
try:
|
|
97
155
|
return bool(target.isatty())
|
|
@@ -101,7 +159,6 @@ def _stream_supports_tty(stream: Any) -> bool:
|
|
|
101
159
|
|
|
102
160
|
def update_spinner(status_indicator: Any | None, message: str) -> None:
|
|
103
161
|
"""Update spinner text when a status indicator is active."""
|
|
104
|
-
|
|
105
162
|
if status_indicator is None:
|
|
106
163
|
return
|
|
107
164
|
|
|
@@ -113,7 +170,6 @@ def update_spinner(status_indicator: Any | None, message: str) -> None:
|
|
|
113
170
|
|
|
114
171
|
def stop_spinner(status_indicator: Any | None) -> None:
|
|
115
172
|
"""Stop an active spinner safely."""
|
|
116
|
-
|
|
117
173
|
if status_indicator is None:
|
|
118
174
|
return
|
|
119
175
|
|
|
@@ -160,9 +216,12 @@ def get_client(ctx: Any) -> Client: # pragma: no cover
|
|
|
160
216
|
}
|
|
161
217
|
|
|
162
218
|
if not config.get("api_url") or not config.get("api_key"):
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
219
|
+
configure_hint = command_hint("configure", slash_command="login", ctx=ctx)
|
|
220
|
+
actions = []
|
|
221
|
+
if configure_hint:
|
|
222
|
+
actions.append(f"Run `{configure_hint}`")
|
|
223
|
+
actions.append("set AIP_* env vars")
|
|
224
|
+
raise click.ClickException(f"Missing api_url/api_key. {' or '.join(actions)}.")
|
|
166
225
|
|
|
167
226
|
return Client(
|
|
168
227
|
api_url=config.get("api_url"),
|
|
@@ -171,6 +230,8 @@ def get_client(ctx: Any) -> Client: # pragma: no cover
|
|
|
171
230
|
)
|
|
172
231
|
|
|
173
232
|
|
|
233
|
+
# ----------------------------- Secret masking ---------------------------- #
|
|
234
|
+
|
|
174
235
|
# ----------------------------- Fuzzy palette ----------------------------- #
|
|
175
236
|
|
|
176
237
|
|
|
@@ -237,8 +298,8 @@ def _build_display_parts(
|
|
|
237
298
|
|
|
238
299
|
|
|
239
300
|
def _row_display(row: dict[str, Any], columns: list[tuple]) -> str:
|
|
240
|
-
"""
|
|
241
|
-
|
|
301
|
+
"""Build a compact text label for the palette.
|
|
302
|
+
|
|
242
303
|
Prefers: name • type • framework • [id] (when available)
|
|
243
304
|
Falls back to first 2 columns + [id].
|
|
244
305
|
"""
|
|
@@ -331,8 +392,8 @@ def _perform_fuzzy_search(
|
|
|
331
392
|
def _fuzzy_pick(
|
|
332
393
|
rows: list[dict[str, Any]], columns: list[tuple], title: str
|
|
333
394
|
) -> dict[str, Any] | None: # pragma: no cover - requires interactive prompt toolkit
|
|
334
|
-
"""
|
|
335
|
-
|
|
395
|
+
"""Open a minimal fuzzy palette using prompt_toolkit.
|
|
396
|
+
|
|
336
397
|
Returns the selected row (dict) or None if cancelled/missing deps.
|
|
337
398
|
"""
|
|
338
399
|
if not _check_fuzzy_pick_requirements():
|
|
@@ -401,8 +462,8 @@ def _calculate_length_bonus(search: str, target: str) -> int:
|
|
|
401
462
|
|
|
402
463
|
|
|
403
464
|
def _fuzzy_score(search: str, target: str) -> int:
|
|
404
|
-
"""
|
|
405
|
-
|
|
465
|
+
"""Calculate fuzzy match score.
|
|
466
|
+
|
|
406
467
|
Higher score = better match.
|
|
407
468
|
Returns -1 if no match possible.
|
|
408
469
|
"""
|
|
@@ -465,8 +526,15 @@ def output_result(
|
|
|
465
526
|
result: Any,
|
|
466
527
|
title: str = "Result",
|
|
467
528
|
panel_title: str | None = None,
|
|
468
|
-
success_message: str | None = None,
|
|
469
529
|
) -> None:
|
|
530
|
+
"""Output a result to the console with optional title.
|
|
531
|
+
|
|
532
|
+
Args:
|
|
533
|
+
ctx: Click context
|
|
534
|
+
result: Result data to output
|
|
535
|
+
title: Optional title for the output
|
|
536
|
+
panel_title: Optional Rich panel title for structured output
|
|
537
|
+
"""
|
|
470
538
|
fmt = _get_view(ctx)
|
|
471
539
|
|
|
472
540
|
data = _coerce_result_payload(result)
|
|
@@ -485,20 +553,12 @@ def output_result(
|
|
|
485
553
|
_render_markdown_output(data)
|
|
486
554
|
return
|
|
487
555
|
|
|
488
|
-
|
|
489
|
-
console.print(Text(f"[green]✅ {success_message}[/green]"))
|
|
490
|
-
|
|
556
|
+
renderable = Pretty(data)
|
|
491
557
|
if panel_title:
|
|
492
|
-
console.print(
|
|
493
|
-
AIPPanel(
|
|
494
|
-
Pretty(data),
|
|
495
|
-
title=panel_title,
|
|
496
|
-
border_style="blue",
|
|
497
|
-
)
|
|
498
|
-
)
|
|
558
|
+
console.print(AIPPanel(renderable, title=panel_title))
|
|
499
559
|
else:
|
|
500
560
|
console.print(Text(f"[cyan]{title}:[/cyan]"))
|
|
501
|
-
console.print(
|
|
561
|
+
console.print(renderable)
|
|
502
562
|
|
|
503
563
|
|
|
504
564
|
# ----------------------------- List rendering ---------------------------- #
|
|
@@ -610,7 +670,6 @@ def _handle_empty_items(title: str) -> None:
|
|
|
610
670
|
|
|
611
671
|
def _should_use_fuzzy_picker() -> bool:
|
|
612
672
|
"""Return True when the interactive fuzzy picker can be shown."""
|
|
613
|
-
|
|
614
673
|
return console.is_terminal and os.isatty(1)
|
|
615
674
|
|
|
616
675
|
|
|
@@ -618,7 +677,6 @@ def _try_fuzzy_pick(
|
|
|
618
677
|
rows: list[dict[str, Any]], columns: list[tuple], title: str
|
|
619
678
|
) -> dict[str, Any] | None:
|
|
620
679
|
"""Best-effort fuzzy selection; returns None if the picker fails."""
|
|
621
|
-
|
|
622
680
|
if not _should_use_fuzzy_picker():
|
|
623
681
|
return None
|
|
624
682
|
|
|
@@ -629,36 +687,34 @@ def _try_fuzzy_pick(
|
|
|
629
687
|
return None
|
|
630
688
|
|
|
631
689
|
|
|
632
|
-
def _resource_tip_command(title: str) -> str:
|
|
690
|
+
def _resource_tip_command(title: str) -> str | None:
|
|
633
691
|
"""Resolve the follow-up command hint for the given table title."""
|
|
634
|
-
|
|
635
692
|
title_lower = title.lower()
|
|
636
693
|
mapping = {
|
|
637
|
-
"agent": "
|
|
638
|
-
"tool": "
|
|
639
|
-
"mcp": "
|
|
640
|
-
"model": "
|
|
694
|
+
"agent": ("agents get", "agents"),
|
|
695
|
+
"tool": ("tools get", None),
|
|
696
|
+
"mcp": ("mcps get", None),
|
|
697
|
+
"model": ("models list", None), # models only ship a list command
|
|
641
698
|
}
|
|
642
|
-
for keyword,
|
|
699
|
+
for keyword, (cli_command, slash_command) in mapping.items():
|
|
643
700
|
if keyword in title_lower:
|
|
644
|
-
return
|
|
645
|
-
return "
|
|
701
|
+
return command_hint(cli_command, slash_command=slash_command)
|
|
702
|
+
return command_hint("agents get", slash_command="agents")
|
|
646
703
|
|
|
647
704
|
|
|
648
705
|
def _print_selection_tip(title: str) -> None:
|
|
649
706
|
"""Print the contextual follow-up tip after a fuzzy selection."""
|
|
650
|
-
|
|
651
707
|
tip_cmd = _resource_tip_command(title)
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
708
|
+
if tip_cmd:
|
|
709
|
+
console.print(
|
|
710
|
+
Text.from_markup(f"\n[dim]Tip: use `{tip_cmd} <ID>` for details[/dim]")
|
|
711
|
+
)
|
|
655
712
|
|
|
656
713
|
|
|
657
714
|
def _handle_fuzzy_pick_selection(
|
|
658
715
|
rows: list[dict[str, Any]], columns: list[tuple], title: str
|
|
659
716
|
) -> bool:
|
|
660
717
|
"""Handle fuzzy picker selection, returns True if selection was made."""
|
|
661
|
-
|
|
662
718
|
picked = _try_fuzzy_pick(rows, columns, title)
|
|
663
719
|
if picked is None:
|
|
664
720
|
return False
|
|
@@ -773,14 +829,16 @@ def build_renderer(
|
|
|
773
829
|
"""Build renderer and capturing console for CLI commands.
|
|
774
830
|
|
|
775
831
|
Args:
|
|
776
|
-
|
|
777
|
-
save_path: Path to save output to (enables capturing)
|
|
778
|
-
theme: Color theme ("dark" or "light")
|
|
779
|
-
verbose: Whether to enable verbose mode
|
|
780
|
-
|
|
832
|
+
_ctx: Click context object for CLI operations.
|
|
833
|
+
save_path: Path to save output to (enables capturing console).
|
|
834
|
+
theme: Color theme ("dark" or "light").
|
|
835
|
+
verbose: Whether to enable verbose mode.
|
|
836
|
+
_tty_enabled: Whether TTY is available for interactive features.
|
|
837
|
+
live: Whether to enable live rendering mode (overrides verbose default).
|
|
838
|
+
snapshots: Whether to capture and store snapshots.
|
|
781
839
|
|
|
782
840
|
Returns:
|
|
783
|
-
Tuple of (renderer, capturing_console)
|
|
841
|
+
Tuple of (renderer, capturing_console) for streaming output.
|
|
784
842
|
"""
|
|
785
843
|
# Use capturing console if saving output
|
|
786
844
|
working_console = console
|
|
@@ -859,8 +917,7 @@ def _build_resource_labels(resources: list[Any]) -> tuple[list[str], dict[str, A
|
|
|
859
917
|
def _fuzzy_pick_for_resources(
|
|
860
918
|
resources: list[Any], resource_type: str, _search_term: str
|
|
861
919
|
) -> Any | None: # pragma: no cover - interactive selection helper
|
|
862
|
-
"""
|
|
863
|
-
Fuzzy picker for resource objects, similar to _fuzzy_pick but without column dependencies.
|
|
920
|
+
"""Fuzzy picker for resource objects, similar to _fuzzy_pick but without column dependencies.
|
|
864
921
|
|
|
865
922
|
Args:
|
|
866
923
|
resources: List of resource objects to choose from
|