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.
Files changed (40) hide show
  1. glaip_sdk/branding.py +27 -1
  2. glaip_sdk/cli/commands/agents.py +26 -17
  3. glaip_sdk/cli/commands/configure.py +39 -50
  4. glaip_sdk/cli/commands/mcps.py +1 -3
  5. glaip_sdk/cli/config.py +42 -0
  6. glaip_sdk/cli/display.py +92 -26
  7. glaip_sdk/cli/main.py +141 -124
  8. glaip_sdk/cli/mcp_validators.py +2 -2
  9. glaip_sdk/cli/pager.py +3 -2
  10. glaip_sdk/cli/parsers/json_input.py +2 -2
  11. glaip_sdk/cli/resolution.py +12 -10
  12. glaip_sdk/cli/slash/agent_session.py +7 -0
  13. glaip_sdk/cli/slash/prompt.py +21 -2
  14. glaip_sdk/cli/slash/session.py +15 -21
  15. glaip_sdk/cli/update_notifier.py +8 -2
  16. glaip_sdk/cli/utils.py +110 -53
  17. glaip_sdk/client/_agent_payloads.py +504 -0
  18. glaip_sdk/client/agents.py +194 -551
  19. glaip_sdk/client/base.py +92 -20
  20. glaip_sdk/client/main.py +6 -0
  21. glaip_sdk/client/run_rendering.py +275 -0
  22. glaip_sdk/config/constants.py +3 -0
  23. glaip_sdk/exceptions.py +15 -0
  24. glaip_sdk/models.py +5 -0
  25. glaip_sdk/payload_schemas/__init__.py +19 -0
  26. glaip_sdk/payload_schemas/agent.py +87 -0
  27. glaip_sdk/rich_components.py +12 -0
  28. glaip_sdk/utils/client_utils.py +12 -0
  29. glaip_sdk/utils/import_export.py +2 -2
  30. glaip_sdk/utils/rendering/formatting.py +5 -0
  31. glaip_sdk/utils/rendering/models.py +22 -0
  32. glaip_sdk/utils/rendering/renderer/base.py +9 -1
  33. glaip_sdk/utils/rendering/renderer/panels.py +0 -1
  34. glaip_sdk/utils/rendering/steps.py +59 -0
  35. glaip_sdk/utils/serialization.py +24 -3
  36. {glaip_sdk-0.0.15.dist-info → glaip_sdk-0.0.16.dist-info}/METADATA +1 -1
  37. glaip_sdk-0.0.16.dist-info/RECORD +72 -0
  38. glaip_sdk-0.0.15.dist-info/RECORD +0 -67
  39. {glaip_sdk-0.0.15.dist-info → glaip_sdk-0.0.16.dist-info}/WHEEL +0 -0
  40. {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.commands.configure import load_config
42
- from glaip_sdk.cli.context import _get_view, get_ctx_value
43
- from glaip_sdk.rich_components import AIPPanel, AIPTable
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
- raise click.ClickException(
164
- "Missing api_url/api_key. Run `aip configure` or set AIP_* env vars."
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
- Build a compact text label for the palette.
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
- Open a minimal fuzzy palette using prompt_toolkit.
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
- Calculate fuzzy match score.
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
- if success_message:
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(Pretty(data))
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": "aip agents get",
638
- "tool": "aip tools get",
639
- "mcp": "aip mcps get",
640
- "model": "aip models list", # models only ship a list command
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, command in mapping.items():
699
+ for keyword, (cli_command, slash_command) in mapping.items():
643
700
  if keyword in title_lower:
644
- return command
645
- return "aip agents get"
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
- console.print(
653
- Text.from_markup(f"\n[dim]Tip: use `{tip_cmd} <ID>` for details[/dim]")
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
- ctx: Click context
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
- tty_enabled: Whether TTY is available
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