glaip-sdk 0.7.12__py3-none-any.whl → 0.7.13__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/agents/base.py +39 -4
- glaip_sdk/cli/slash/tui/accounts.tcss +12 -0
- glaip_sdk/cli/slash/tui/accounts_app.py +56 -15
- glaip_sdk/cli/slash/tui/context.py +7 -2
- glaip_sdk/cli/slash/tui/layouts/harlequin.py +35 -11
- glaip_sdk/cli/slash/tui/remote_runs_app.py +47 -10
- glaip_sdk/cli/slash/tui/toast.py +14 -0
- {glaip_sdk-0.7.12.dist-info → glaip_sdk-0.7.13.dist-info}/METADATA +1 -1
- {glaip_sdk-0.7.12.dist-info → glaip_sdk-0.7.13.dist-info}/RECORD +12 -12
- {glaip_sdk-0.7.12.dist-info → glaip_sdk-0.7.13.dist-info}/WHEEL +0 -0
- {glaip_sdk-0.7.12.dist-info → glaip_sdk-0.7.13.dist-info}/entry_points.txt +0 -0
- {glaip_sdk-0.7.12.dist-info → glaip_sdk-0.7.13.dist-info}/top_level.txt +0 -0
glaip_sdk/agents/base.py
CHANGED
|
@@ -185,6 +185,18 @@ class Agent:
|
|
|
185
185
|
self._framework = kwargs.pop("framework", Agent._UNSET) # type: ignore[assignment]
|
|
186
186
|
self._version = kwargs.pop("version", Agent._UNSET) # type: ignore[assignment]
|
|
187
187
|
self._agent_type = kwargs.pop("agent_type", Agent._UNSET) # type: ignore[assignment]
|
|
188
|
+
|
|
189
|
+
# Handle 'type' as a legacy alias for 'agent_type'
|
|
190
|
+
legacy_type = kwargs.pop("type", Agent._UNSET)
|
|
191
|
+
if legacy_type is not Agent._UNSET:
|
|
192
|
+
warnings.warn(
|
|
193
|
+
"The 'type' parameter is deprecated and will be removed in a future version. Use 'agent_type' instead.",
|
|
194
|
+
DeprecationWarning,
|
|
195
|
+
stacklevel=2,
|
|
196
|
+
)
|
|
197
|
+
if self._agent_type is Agent._UNSET:
|
|
198
|
+
self._agent_type = legacy_type
|
|
199
|
+
|
|
188
200
|
self._agent_config = kwargs.pop("agent_config", Agent._UNSET) # type: ignore[assignment]
|
|
189
201
|
self._tool_configs = kwargs.pop("tool_configs", Agent._UNSET) # type: ignore[assignment]
|
|
190
202
|
self._mcp_configs = kwargs.pop("mcp_configs", Agent._UNSET) # type: ignore[assignment]
|
|
@@ -848,21 +860,42 @@ class Agent:
|
|
|
848
860
|
"""Return a dict representation of the Agent.
|
|
849
861
|
|
|
850
862
|
Provides Pydantic-style serialization for backward compatibility.
|
|
863
|
+
This implementation avoids triggering external tool resolution to ensure
|
|
864
|
+
it remains robust even when the environment is not fully configured.
|
|
851
865
|
|
|
852
866
|
Args:
|
|
853
867
|
exclude_none: If True, exclude None values from the output.
|
|
854
868
|
|
|
855
869
|
Returns:
|
|
856
|
-
Dictionary containing Agent attributes.
|
|
870
|
+
Dictionary containing Agent attributes. Note: Mutable fields (dicts, lists)
|
|
871
|
+
are returned as references. Modify with caution or make a deep copy if needed.
|
|
857
872
|
"""
|
|
858
|
-
|
|
873
|
+
# Map convenience timeout to agent_config if not already present
|
|
874
|
+
agent_config = self.agent_config if self.agent_config is not self._UNSET else {}
|
|
875
|
+
agent_config = dict(agent_config) if agent_config else {}
|
|
876
|
+
|
|
877
|
+
if self.timeout and "execution_timeout" not in agent_config:
|
|
878
|
+
agent_config["execution_timeout"] = self.timeout
|
|
879
|
+
|
|
880
|
+
# Handle guardrail serialization without full config build
|
|
881
|
+
if self.guardrail:
|
|
882
|
+
try:
|
|
883
|
+
from glaip_sdk.guardrails.serializer import ( # noqa: PLC0415
|
|
884
|
+
serialize_guardrail_manager,
|
|
885
|
+
)
|
|
886
|
+
|
|
887
|
+
agent_config["guardrails"] = serialize_guardrail_manager(self.guardrail)
|
|
888
|
+
except ImportError: # pragma: no cover
|
|
889
|
+
# Serializer not available (optional dependency); skip guardrail data
|
|
890
|
+
pass
|
|
859
891
|
|
|
860
892
|
data = {
|
|
861
893
|
"id": self._id,
|
|
862
894
|
"name": self.name,
|
|
863
895
|
"instruction": self.instruction,
|
|
864
896
|
"description": self.description,
|
|
865
|
-
"
|
|
897
|
+
"agent_type": self.agent_type,
|
|
898
|
+
"type": self.agent_type, # Legacy key for backward compatibility
|
|
866
899
|
"framework": self.framework,
|
|
867
900
|
"version": self.version,
|
|
868
901
|
"tools": self.tools,
|
|
@@ -870,7 +903,8 @@ class Agent:
|
|
|
870
903
|
"mcps": self.mcps,
|
|
871
904
|
"timeout": self.timeout,
|
|
872
905
|
"metadata": self.metadata,
|
|
873
|
-
"
|
|
906
|
+
"model": self.model,
|
|
907
|
+
"agent_config": agent_config,
|
|
874
908
|
"tool_configs": self.tool_configs,
|
|
875
909
|
"mcp_configs": self.mcp_configs,
|
|
876
910
|
"a2a_profile": self.a2a_profile,
|
|
@@ -878,6 +912,7 @@ class Agent:
|
|
|
878
912
|
"created_at": self._created_at,
|
|
879
913
|
"updated_at": self._updated_at,
|
|
880
914
|
}
|
|
915
|
+
|
|
881
916
|
if exclude_none:
|
|
882
917
|
return {k: v for k, v in data.items() if v is not None}
|
|
883
918
|
return data
|
|
@@ -53,6 +53,10 @@ Screen {
|
|
|
53
53
|
margin-left: 1;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
Button:hover {
|
|
57
|
+
background: $surface-lighten-1;
|
|
58
|
+
}
|
|
59
|
+
|
|
56
60
|
#accounts-table {
|
|
57
61
|
padding: 0 1 0 1;
|
|
58
62
|
margin: 0 0 0 0;
|
|
@@ -60,6 +64,14 @@ Screen {
|
|
|
60
64
|
border: tall $primary;
|
|
61
65
|
}
|
|
62
66
|
|
|
67
|
+
#accounts-table > .datatable--row:hover {
|
|
68
|
+
background: $surface-lighten-1;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.sidebar-block:hover {
|
|
72
|
+
background: $surface-lighten-1;
|
|
73
|
+
}
|
|
74
|
+
|
|
63
75
|
#status-bar {
|
|
64
76
|
height: 3;
|
|
65
77
|
padding: 0 1;
|
|
@@ -37,11 +37,19 @@ from glaip_sdk.cli.slash.tui.terminal import TerminalCapabilities
|
|
|
37
37
|
from glaip_sdk.cli.slash.tui.theme.catalog import _BUILTIN_THEMES
|
|
38
38
|
|
|
39
39
|
try: # pragma: no cover - optional dependency
|
|
40
|
-
from glaip_sdk.cli.slash.tui.toast import
|
|
40
|
+
from glaip_sdk.cli.slash.tui.toast import (
|
|
41
|
+
ClipboardToastMixin,
|
|
42
|
+
Toast,
|
|
43
|
+
ToastBus,
|
|
44
|
+
ToastContainer,
|
|
45
|
+
ToastHandlerMixin,
|
|
46
|
+
ToastVariant,
|
|
47
|
+
)
|
|
41
48
|
except Exception: # pragma: no cover - optional dependency
|
|
42
49
|
ClipboardToastMixin = object # type: ignore[assignment, misc]
|
|
43
50
|
Toast = None # type: ignore[assignment]
|
|
44
51
|
ToastBus = None # type: ignore[assignment]
|
|
52
|
+
ToastContainer = None # type: ignore[assignment]
|
|
45
53
|
ToastHandlerMixin = object # type: ignore[assignment, misc]
|
|
46
54
|
ToastVariant = None # type: ignore[assignment]
|
|
47
55
|
from glaip_sdk.cli.validators import validate_api_key
|
|
@@ -51,7 +59,8 @@ try: # pragma: no cover - optional dependency
|
|
|
51
59
|
from textual import events
|
|
52
60
|
from textual.app import App, ComposeResult
|
|
53
61
|
from textual.binding import Binding
|
|
54
|
-
from textual.containers import
|
|
62
|
+
from textual.containers import Horizontal, Vertical
|
|
63
|
+
from textual.coordinate import Coordinate
|
|
55
64
|
from textual.screen import ModalScreen
|
|
56
65
|
from textual.suggester import SuggestFromList
|
|
57
66
|
from textual.widgets import Button, Checkbox, DataTable, Footer, Header, Input, LoadingIndicator, Static
|
|
@@ -60,9 +69,9 @@ except Exception: # pragma: no cover - optional dependency
|
|
|
60
69
|
App = None # type: ignore[assignment]
|
|
61
70
|
ComposeResult = None # type: ignore[assignment]
|
|
62
71
|
Binding = None # type: ignore[assignment]
|
|
63
|
-
Container = None # type: ignore[assignment]
|
|
64
72
|
Horizontal = None # type: ignore[assignment]
|
|
65
73
|
Vertical = None # type: ignore[assignment]
|
|
74
|
+
Coordinate = None # type: ignore[assignment]
|
|
66
75
|
Button = None # type: ignore[assignment]
|
|
67
76
|
Checkbox = None # type: ignore[assignment]
|
|
68
77
|
DataTable = None # type: ignore[assignment]
|
|
@@ -527,7 +536,7 @@ class AccountsHarlequinScreen( # pragma: no cover - interactive
|
|
|
527
536
|
|
|
528
537
|
def compose(self) -> ComposeResult: # type: ignore[return]
|
|
529
538
|
"""Compose the Harlequin layout with account list and detail panes."""
|
|
530
|
-
if not TEXTUAL_SUPPORTED or Horizontal is None or Vertical is None or
|
|
539
|
+
if not TEXTUAL_SUPPORTED or Horizontal is None or Vertical is None or Static is None:
|
|
531
540
|
return # type: ignore[return-value]
|
|
532
541
|
|
|
533
542
|
# Main container with horizontal split (25/75)
|
|
@@ -561,8 +570,8 @@ class AccountsHarlequinScreen( # pragma: no cover - interactive
|
|
|
561
570
|
)
|
|
562
571
|
|
|
563
572
|
# Toast container for notifications
|
|
564
|
-
if Toast is not None:
|
|
565
|
-
yield
|
|
573
|
+
if Toast is not None and ToastContainer is not None:
|
|
574
|
+
yield ToastContainer(Toast(), id="toast-container")
|
|
566
575
|
|
|
567
576
|
def on_mount(self) -> None:
|
|
568
577
|
"""Configure the screen after mount."""
|
|
@@ -833,6 +842,21 @@ class AccountsHarlequinScreen( # pragma: no cover - interactive
|
|
|
833
842
|
if not self._is_switching:
|
|
834
843
|
self.action_switch_account()
|
|
835
844
|
|
|
845
|
+
def on_data_table_cell_selected(self, event: DataTable.CellSelected) -> None: # type: ignore[override]
|
|
846
|
+
"""Handle mouse click selection by triggering switch."""
|
|
847
|
+
if not TEXTUAL_SUPPORTED:
|
|
848
|
+
return
|
|
849
|
+
table = self.query_one(HARLEQUIN_ACCOUNTS_LIST_ID, DataTable)
|
|
850
|
+
try:
|
|
851
|
+
table.cursor_coordinate = (event.coordinate.row, 0)
|
|
852
|
+
except Exception:
|
|
853
|
+
return
|
|
854
|
+
filtered = self._filtered_rows()
|
|
855
|
+
if event.coordinate.row < len(filtered):
|
|
856
|
+
self._update_selected_account(filtered[event.coordinate.row])
|
|
857
|
+
if not self._is_switching:
|
|
858
|
+
self.action_switch_account()
|
|
859
|
+
|
|
836
860
|
def on_data_table_cursor_row_changed(self, event: DataTable.CursorRowChanged) -> None: # type: ignore[override]
|
|
837
861
|
"""Handle cursor movement in the accounts list."""
|
|
838
862
|
if not TEXTUAL_SUPPORTED:
|
|
@@ -875,6 +899,8 @@ class AccountsHarlequinScreen( # pragma: no cover - interactive
|
|
|
875
899
|
"""Open edit account modal."""
|
|
876
900
|
if self._check_env_lock():
|
|
877
901
|
return
|
|
902
|
+
# Get account from cursor position if not explicitly selected
|
|
903
|
+
self._ensure_account_selected_from_cursor()
|
|
878
904
|
name = self._get_selected_name()
|
|
879
905
|
if not name:
|
|
880
906
|
self._set_status("Select an account to edit.", "yellow")
|
|
@@ -897,6 +923,8 @@ class AccountsHarlequinScreen( # pragma: no cover - interactive
|
|
|
897
923
|
"""Open delete confirmation modal."""
|
|
898
924
|
if self._check_env_lock():
|
|
899
925
|
return
|
|
926
|
+
# Get account from cursor position if not explicitly selected
|
|
927
|
+
self._ensure_account_selected_from_cursor()
|
|
900
928
|
name = self._get_selected_name()
|
|
901
929
|
if not name:
|
|
902
930
|
self._set_status("Select an account to delete.", "yellow")
|
|
@@ -987,7 +1015,7 @@ class AccountsHarlequinScreen( # pragma: no cover - interactive
|
|
|
987
1015
|
if output is None:
|
|
988
1016
|
return None
|
|
989
1017
|
|
|
990
|
-
def _write(sequence: str, _output=output) -> None:
|
|
1018
|
+
def _write(sequence: str, _output: Any = output) -> None:
|
|
991
1019
|
_output.write(sequence)
|
|
992
1020
|
_output.flush()
|
|
993
1021
|
|
|
@@ -1310,20 +1338,33 @@ class AccountsTextualApp( # pragma: no cover - interactive
|
|
|
1310
1338
|
self._queue_switch(name)
|
|
1311
1339
|
|
|
1312
1340
|
def on_data_table_row_selected(self, event: DataTable.RowSelected) -> None: # type: ignore[override]
|
|
1313
|
-
"""Handle
|
|
1341
|
+
"""Handle row selection by triggering switch."""
|
|
1342
|
+
self._handle_table_click(self._event_row(event))
|
|
1314
1343
|
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1344
|
+
def on_data_table_cell_selected(self, event: DataTable.CellSelected) -> None: # type: ignore[override]
|
|
1345
|
+
"""Handle mouse click selection by triggering switch."""
|
|
1346
|
+
self._handle_table_click(self._event_row(event))
|
|
1347
|
+
|
|
1348
|
+
def _event_row(self, event: object) -> int | None:
|
|
1349
|
+
"""Extract the row index from a DataTable event."""
|
|
1350
|
+
row = getattr(event, "cursor_row", None)
|
|
1351
|
+
if row is not None:
|
|
1352
|
+
return int(row)
|
|
1353
|
+
coordinate = getattr(event, "coordinate", None)
|
|
1354
|
+
return getattr(coordinate, "row", None) if coordinate is not None else None
|
|
1355
|
+
|
|
1356
|
+
def _handle_table_click(self, row: int | None) -> None:
|
|
1357
|
+
"""Move the cursor to a clicked row and trigger the switch action."""
|
|
1358
|
+
if row is None:
|
|
1359
|
+
return
|
|
1319
1360
|
try:
|
|
1320
1361
|
table = self.query_one(ACCOUNTS_TABLE_ID, DataTable)
|
|
1321
1362
|
except Exception:
|
|
1322
|
-
# Harlequin screen is active, let it handle the
|
|
1363
|
+
# Harlequin screen is active, let it handle the action
|
|
1323
1364
|
return
|
|
1324
1365
|
try:
|
|
1325
1366
|
# Move cursor to clicked row then switch
|
|
1326
|
-
table.cursor_coordinate = (
|
|
1367
|
+
table.cursor_coordinate = Coordinate(row, 0)
|
|
1327
1368
|
except Exception:
|
|
1328
1369
|
return
|
|
1329
1370
|
self.action_switch_row()
|
|
@@ -1654,7 +1695,7 @@ class AccountsTextualApp( # pragma: no cover - interactive
|
|
|
1654
1695
|
if output is None:
|
|
1655
1696
|
return None
|
|
1656
1697
|
|
|
1657
|
-
def _write(sequence: str, _output=output) -> None:
|
|
1698
|
+
def _write(sequence: str, _output: Any = output) -> None:
|
|
1658
1699
|
_output.write(sequence)
|
|
1659
1700
|
_output.flush()
|
|
1660
1701
|
|
|
@@ -61,13 +61,18 @@ class TUIContext:
|
|
|
61
61
|
store = get_account_store()
|
|
62
62
|
settings = load_tui_settings(store=store)
|
|
63
63
|
|
|
64
|
-
# Handle env var override: normalize empty strings and "default" to None
|
|
65
|
-
# Empty string from os.getenv() is falsy, so strip() result becomes None in the or expression
|
|
66
64
|
env_theme = os.getenv("AIP_TUI_THEME")
|
|
67
65
|
env_theme = env_theme.strip() if env_theme else None
|
|
68
66
|
if env_theme and env_theme.lower() == "default":
|
|
69
67
|
env_theme = None
|
|
70
68
|
|
|
69
|
+
env_mouse = os.getenv("AIP_TUI_MOUSE_CAPTURE")
|
|
70
|
+
mouse_capture = settings.mouse_capture
|
|
71
|
+
if env_mouse is not None:
|
|
72
|
+
mouse_capture = env_mouse.lower() == "true"
|
|
73
|
+
|
|
74
|
+
terminal.mouse = mouse_capture
|
|
75
|
+
|
|
71
76
|
theme_name = env_theme or settings.theme_name
|
|
72
77
|
theme = ThemeManager(
|
|
73
78
|
terminal,
|
|
@@ -19,8 +19,8 @@ from __future__ import annotations
|
|
|
19
19
|
from typing import TYPE_CHECKING, Any
|
|
20
20
|
|
|
21
21
|
try: # pragma: no cover - optional dependency
|
|
22
|
-
from textual.containers import Container, Horizontal, Vertical
|
|
23
22
|
from textual.screen import Screen
|
|
23
|
+
from textual.widget import Widget
|
|
24
24
|
except Exception: # pragma: no cover - optional dependency
|
|
25
25
|
|
|
26
26
|
class Screen: # type: ignore[no-redef]
|
|
@@ -30,23 +30,47 @@ except Exception: # pragma: no cover - optional dependency
|
|
|
30
30
|
"""Return the class for typing subscripts."""
|
|
31
31
|
return cls
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
Vertical = None # type: ignore[assignment]
|
|
35
|
-
Container = None # type: ignore[assignment]
|
|
33
|
+
Widget = None # type: ignore[assignment]
|
|
36
34
|
|
|
37
35
|
if TYPE_CHECKING:
|
|
38
36
|
from glaip_sdk.cli.slash.tui.context import TUIContext
|
|
39
37
|
|
|
40
38
|
try: # pragma: no cover - optional dependency
|
|
41
|
-
from glaip_sdk.cli.slash.tui.toast import Toast
|
|
39
|
+
from glaip_sdk.cli.slash.tui.toast import Toast, ToastContainer
|
|
42
40
|
except Exception: # pragma: no cover - optional dependency
|
|
43
41
|
Toast = None # type: ignore[assignment, misc]
|
|
42
|
+
ToastContainer = None # type: ignore[assignment, misc]
|
|
44
43
|
|
|
45
44
|
# GDP Labs Brand Palette
|
|
46
45
|
PRIMARY_BLUE = "#005CB8"
|
|
47
46
|
BLACK_BACKGROUND = "#000000"
|
|
48
47
|
|
|
49
48
|
|
|
49
|
+
if Widget is not None:
|
|
50
|
+
|
|
51
|
+
class HarlequinContainer(Widget):
|
|
52
|
+
"""Base container for the Harlequin layout."""
|
|
53
|
+
|
|
54
|
+
DEFAULT_CSS = """
|
|
55
|
+
HarlequinContainer {
|
|
56
|
+
layout: horizontal;
|
|
57
|
+
}
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
class HarlequinPane(Widget):
|
|
61
|
+
"""Pane container for Harlequin layout sections."""
|
|
62
|
+
|
|
63
|
+
DEFAULT_CSS = """
|
|
64
|
+
HarlequinPane {
|
|
65
|
+
layout: vertical;
|
|
66
|
+
}
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
else:
|
|
70
|
+
HarlequinContainer = None # type: ignore[assignment, misc]
|
|
71
|
+
HarlequinPane = None # type: ignore[assignment, misc]
|
|
72
|
+
|
|
73
|
+
|
|
50
74
|
class HarlequinScreen(Screen[None]): # type: ignore[misc]
|
|
51
75
|
"""Base class for Harlequin-style multi-pane screens.
|
|
52
76
|
|
|
@@ -136,19 +160,19 @@ class HarlequinScreen(Screen[None]): # type: ignore[misc]
|
|
|
136
160
|
Returns:
|
|
137
161
|
ComposeResult yielding the base layout containers.
|
|
138
162
|
"""
|
|
139
|
-
if
|
|
163
|
+
if HarlequinContainer is None or HarlequinPane is None:
|
|
140
164
|
return
|
|
141
165
|
|
|
142
166
|
# Main container with horizontal split (25/75)
|
|
143
|
-
yield
|
|
144
|
-
|
|
145
|
-
|
|
167
|
+
yield HarlequinContainer(
|
|
168
|
+
HarlequinPane(id="left-pane"),
|
|
169
|
+
HarlequinPane(id="right-pane"),
|
|
146
170
|
id="harlequin-container",
|
|
147
171
|
)
|
|
148
172
|
|
|
149
173
|
# Toast container for notifications
|
|
150
|
-
if Toast is not None and
|
|
151
|
-
yield
|
|
174
|
+
if Toast is not None and ToastContainer is not None:
|
|
175
|
+
yield ToastContainer(Toast(), id="toast-container")
|
|
152
176
|
|
|
153
177
|
@property
|
|
154
178
|
def ctx(self) -> TUIContext | None:
|
|
@@ -17,18 +17,35 @@ from dataclasses import dataclass
|
|
|
17
17
|
from typing import Any
|
|
18
18
|
|
|
19
19
|
from rich.text import Text
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
from textual.
|
|
23
|
-
from textual.
|
|
24
|
-
from textual.
|
|
25
|
-
from textual.
|
|
26
|
-
from textual.
|
|
20
|
+
|
|
21
|
+
try: # pragma: no cover - optional dependency
|
|
22
|
+
from textual.app import App, ComposeResult
|
|
23
|
+
from textual.binding import Binding
|
|
24
|
+
from textual.containers import Horizontal, Vertical
|
|
25
|
+
from textual.coordinate import Coordinate
|
|
26
|
+
from textual.reactive import ReactiveError
|
|
27
|
+
from textual.screen import ModalScreen
|
|
28
|
+
from textual.widgets import DataTable, Footer, Header, LoadingIndicator, RichLog, Static
|
|
29
|
+
except Exception: # pragma: no cover - optional dependency
|
|
30
|
+
App = None # type: ignore[assignment]
|
|
31
|
+
ComposeResult = None # type: ignore[assignment]
|
|
32
|
+
Binding = None # type: ignore[assignment]
|
|
33
|
+
Horizontal = None # type: ignore[assignment]
|
|
34
|
+
Vertical = None # type: ignore[assignment]
|
|
35
|
+
Coordinate = None # type: ignore[assignment]
|
|
36
|
+
ReactiveError = Exception # type: ignore[assignment, misc]
|
|
37
|
+
ModalScreen = object # type: ignore[assignment, misc]
|
|
38
|
+
DataTable = None # type: ignore[assignment]
|
|
39
|
+
Footer = None # type: ignore[assignment]
|
|
40
|
+
Header = None # type: ignore[assignment]
|
|
41
|
+
LoadingIndicator = None # type: ignore[assignment]
|
|
42
|
+
RichLog = None # type: ignore[assignment]
|
|
43
|
+
Static = None # type: ignore[assignment]
|
|
27
44
|
|
|
28
45
|
from glaip_sdk.cli.slash.tui.clipboard import ClipboardAdapter
|
|
29
46
|
from glaip_sdk.cli.slash.tui.context import TUIContext
|
|
30
47
|
from glaip_sdk.cli.slash.tui.loading import hide_loading_indicator, show_loading_indicator
|
|
31
|
-
from glaip_sdk.cli.slash.tui.toast import ClipboardToastMixin, Toast, ToastBus, ToastHandlerMixin
|
|
48
|
+
from glaip_sdk.cli.slash.tui.toast import ClipboardToastMixin, Toast, ToastBus, ToastContainer, ToastHandlerMixin
|
|
32
49
|
|
|
33
50
|
logger = logging.getLogger(__name__)
|
|
34
51
|
|
|
@@ -149,7 +166,7 @@ class RunDetailScreen(ToastHandlerMixin, ClipboardToastMixin, ModalScreen[None])
|
|
|
149
166
|
RichLog(id="detail-events", wrap=False),
|
|
150
167
|
)
|
|
151
168
|
yield main_content
|
|
152
|
-
yield
|
|
169
|
+
yield ToastContainer(Toast(), id="toast-container")
|
|
153
170
|
yield Footer()
|
|
154
171
|
|
|
155
172
|
def on_mount(self) -> None:
|
|
@@ -369,7 +386,7 @@ class RemoteRunsTextualApp(ToastHandlerMixin, App[None]):
|
|
|
369
386
|
def compose(self) -> ComposeResult:
|
|
370
387
|
"""Build layout."""
|
|
371
388
|
yield Header()
|
|
372
|
-
yield
|
|
389
|
+
yield ToastContainer(Toast(), id="toast-container")
|
|
373
390
|
table = DataTable(id=RUNS_TABLE_ID) # pragma: no cover - mocked in tests
|
|
374
391
|
table.cursor_type = "row" # pragma: no cover - mocked in tests
|
|
375
392
|
table.add_columns( # pragma: no cover - mocked in tests
|
|
@@ -478,6 +495,26 @@ class RemoteRunsTextualApp(ToastHandlerMixin, App[None]):
|
|
|
478
495
|
"""Track cursor position when DataTable selection changes."""
|
|
479
496
|
self.cursor_index = getattr(event, "cursor_row", self.cursor_index)
|
|
480
497
|
|
|
498
|
+
def _handle_table_click(self, row: int | None) -> None:
|
|
499
|
+
if row is None:
|
|
500
|
+
return
|
|
501
|
+
table = self.query_one(RUNS_TABLE_SELECTOR, DataTable)
|
|
502
|
+
self.cursor_index = row
|
|
503
|
+
try:
|
|
504
|
+
table.cursor_coordinate = Coordinate(row, 0)
|
|
505
|
+
except Exception:
|
|
506
|
+
return
|
|
507
|
+
self.action_open_detail()
|
|
508
|
+
|
|
509
|
+
def on_data_table_row_selected(self, event: DataTable.RowSelected) -> None: # pragma: no cover - UI hook
|
|
510
|
+
"""Handle row selection event from DataTable."""
|
|
511
|
+
self._handle_table_click(getattr(event, "cursor_row", None))
|
|
512
|
+
|
|
513
|
+
def on_data_table_cell_selected(self, event: DataTable.CellSelected) -> None: # pragma: no cover - UI hook
|
|
514
|
+
"""Handle cell selection event from DataTable."""
|
|
515
|
+
row = getattr(event.coordinate, "row", None) if event.coordinate else None
|
|
516
|
+
self._handle_table_click(row)
|
|
517
|
+
|
|
481
518
|
def action_page_left(self) -> None:
|
|
482
519
|
"""Navigate to the previous page."""
|
|
483
520
|
if not self.current_page.has_prev:
|
glaip_sdk/cli/slash/tui/toast.py
CHANGED
|
@@ -10,6 +10,7 @@ from typing import Any, cast
|
|
|
10
10
|
|
|
11
11
|
from rich.text import Text
|
|
12
12
|
from textual.message import Message
|
|
13
|
+
from textual.widget import Widget
|
|
13
14
|
from textual.widgets import Static
|
|
14
15
|
|
|
15
16
|
|
|
@@ -290,6 +291,19 @@ class ClipboardToastMixin:
|
|
|
290
291
|
self._append_copy_fallback(text)
|
|
291
292
|
|
|
292
293
|
|
|
294
|
+
class ToastContainer(Widget):
|
|
295
|
+
"""Simple wrapper for docking toast widgets without relying on containers.
|
|
296
|
+
|
|
297
|
+
This class exists to provide a lightweight widget wrapper for toast containers
|
|
298
|
+
that avoids direct dependency on Textual's Container class. It allows the toast
|
|
299
|
+
system to work consistently across different Textual versions and provides a
|
|
300
|
+
stable API for toast container composition.
|
|
301
|
+
|
|
302
|
+
Usage:
|
|
303
|
+
yield ToastContainer(Toast(), id="toast-container")
|
|
304
|
+
"""
|
|
305
|
+
|
|
306
|
+
|
|
293
307
|
class Toast(Static):
|
|
294
308
|
"""A Textual widget that displays toast notifications at the top-right of the screen.
|
|
295
309
|
|
|
@@ -5,7 +5,7 @@ glaip_sdk/exceptions.py,sha256=iAChFClkytXRBLP0vZq1_YjoZxA9i4m4bW1gDLiGR1g,2321
|
|
|
5
5
|
glaip_sdk/icons.py,sha256=J5THz0ReAmDwIiIooh1_G3Le-mwTJyEjhJDdJ13KRxM,524
|
|
6
6
|
glaip_sdk/rich_components.py,sha256=44Z0V1ZQleVh9gUDGwRR5mriiYFnVGOhm7fFxZYbP8c,4052
|
|
7
7
|
glaip_sdk/agents/__init__.py,sha256=VfYov56edbWuySXFEbWJ_jLXgwnFzPk1KB-9-mfsUCc,776
|
|
8
|
-
glaip_sdk/agents/base.py,sha256=
|
|
8
|
+
glaip_sdk/agents/base.py,sha256=TKULRlc3wSPLd901_e2h5dXorYVbGt_Nc9YHkDVqp6A,47979
|
|
9
9
|
glaip_sdk/cli/__init__.py,sha256=xCCfuF1Yc7mpCDcfhHZTX0vizvtrDSLeT8MJ3V7m5A0,156
|
|
10
10
|
glaip_sdk/cli/account_store.py,sha256=u_memecwEQssustZs2wYBrHbEmKUlDfmmL-zO1F3n3A,19034
|
|
11
11
|
glaip_sdk/cli/agent_config.py,sha256=YAbFKrTNTRqNA6b0i0Q3pH-01rhHDRi5v8dxSFwGSwM,2401
|
|
@@ -80,18 +80,18 @@ glaip_sdk/cli/slash/prompt.py,sha256=q4f1c2zr7ZMUeO6AgOBF2Nz4qgMOXrVPt6WzPRQMbAM
|
|
|
80
80
|
glaip_sdk/cli/slash/remote_runs_controller.py,sha256=iLl4a-mu9QU7dcedgEILewPtDIVtFUJkbKGtcx1F66U,21445
|
|
81
81
|
glaip_sdk/cli/slash/session.py,sha256=YJ7UIeWyged1znmBVnGweOzH2l4NKeF5lT9VGdDvQWo,75998
|
|
82
82
|
glaip_sdk/cli/slash/tui/__init__.py,sha256=oBUzeoslYwPKVlhqhgg4I7480b77vQNc9ec0NgdTC1s,977
|
|
83
|
-
glaip_sdk/cli/slash/tui/accounts.tcss,sha256=
|
|
84
|
-
glaip_sdk/cli/slash/tui/accounts_app.py,sha256=
|
|
83
|
+
glaip_sdk/cli/slash/tui/accounts.tcss,sha256=GA5NFu_VSPRQdpfTaO0iUHjGDh8U0QDYqiI-WY59ZTc,2356
|
|
84
|
+
glaip_sdk/cli/slash/tui/accounts_app.py,sha256=mv4SzBZrAvS-q-_kMEQQbQStiJPaJ71AmvcaoXiQm0A,73518
|
|
85
85
|
glaip_sdk/cli/slash/tui/background_tasks.py,sha256=SAe1mV2vXB3mJcSGhelU950vf8Lifjhws9iomyIVFKw,2422
|
|
86
86
|
glaip_sdk/cli/slash/tui/clipboard.py,sha256=7fEshhTwHYaj-n7n0W0AsWTs8W0RLZw_9luXxrFTrtw,6227
|
|
87
|
-
glaip_sdk/cli/slash/tui/context.py,sha256=
|
|
87
|
+
glaip_sdk/cli/slash/tui/context.py,sha256=mzI4TDXnfZd42osACp5uo10d10y1_A0z6IxRK1KVoVk,3320
|
|
88
88
|
glaip_sdk/cli/slash/tui/keybind_registry.py,sha256=_rK05BxTxNudYc4iJ9gDxpgeUkjDAq8rarIT-9A-jyM,6739
|
|
89
89
|
glaip_sdk/cli/slash/tui/loading.py,sha256=nW5pv_Tnl9FUOPR3Qf2O5gt1AGHSo3b5-Uofg34F6AE,1909
|
|
90
|
-
glaip_sdk/cli/slash/tui/remote_runs_app.py,sha256=
|
|
90
|
+
glaip_sdk/cli/slash/tui/remote_runs_app.py,sha256=F50pn2VmCN6RQImhQjNZvpE4gU3eUj6dV32_w1Gg5JU,30282
|
|
91
91
|
glaip_sdk/cli/slash/tui/terminal.py,sha256=ZAC3sB17TGpl-GFeRVm_nI8DQTN3pyti3ynlZ41wT_A,12323
|
|
92
|
-
glaip_sdk/cli/slash/tui/toast.py,sha256=
|
|
92
|
+
glaip_sdk/cli/slash/tui/toast.py,sha256=XGITLHhO40xIGmtg9hanPmDsPCQY2hQXzoM_9mJXQyg,12442
|
|
93
93
|
glaip_sdk/cli/slash/tui/layouts/__init__.py,sha256=KT77pZHa7Wz84QlHYT2mfhQ_AXUA-T0eHv_HtAvc1ac,473
|
|
94
|
-
glaip_sdk/cli/slash/tui/layouts/harlequin.py,sha256=
|
|
94
|
+
glaip_sdk/cli/slash/tui/layouts/harlequin.py,sha256=JOsaK18jTojzZ-Py-87foxfijuRDWwi8LIWmqM6qS0k,5644
|
|
95
95
|
glaip_sdk/cli/slash/tui/theme/__init__.py,sha256=rtM2ik83YNCRcI1qh_Sf3rnxco2OvCNNT3NbHY6cLvw,432
|
|
96
96
|
glaip_sdk/cli/slash/tui/theme/catalog.py,sha256=G52eU3h8YI9D8XUALVg1KVZ4Lq65VnZdgPS3F_P7XLE,2544
|
|
97
97
|
glaip_sdk/cli/slash/tui/theme/manager.py,sha256=LBnxEMIwz-8cAlZGYk5tIoAJbOJyGYsmDlyuGJ-LlX4,3945
|
|
@@ -212,8 +212,8 @@ glaip_sdk/utils/rendering/steps/format.py,sha256=Chnq7OBaj8XMeBntSBxrX5zSmrYeGcO
|
|
|
212
212
|
glaip_sdk/utils/rendering/steps/manager.py,sha256=BiBmTeQMQhjRMykgICXsXNYh1hGsss-fH9BIGVMWFi0,13194
|
|
213
213
|
glaip_sdk/utils/rendering/viewer/__init__.py,sha256=XrxmE2cMAozqrzo1jtDFm8HqNtvDcYi2mAhXLXn5CjI,457
|
|
214
214
|
glaip_sdk/utils/rendering/viewer/presenter.py,sha256=mlLMTjnyeyPVtsyrAbz1BJu9lFGQSlS-voZ-_Cuugv0,5725
|
|
215
|
-
glaip_sdk-0.7.
|
|
216
|
-
glaip_sdk-0.7.
|
|
217
|
-
glaip_sdk-0.7.
|
|
218
|
-
glaip_sdk-0.7.
|
|
219
|
-
glaip_sdk-0.7.
|
|
215
|
+
glaip_sdk-0.7.13.dist-info/METADATA,sha256=tcKELknQg-32vMVt0GdHxK7cyIWpE-QpCKUFlTtcJeY,8528
|
|
216
|
+
glaip_sdk-0.7.13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
217
|
+
glaip_sdk-0.7.13.dist-info/entry_points.txt,sha256=NkhO6FfgX9Zrjn63GuKphf-dLw7KNJvucAcXc7P3aMk,54
|
|
218
|
+
glaip_sdk-0.7.13.dist-info/top_level.txt,sha256=td7yXttiYX2s94-4wFhv-5KdT0rSZ-pnJRSire341hw,10
|
|
219
|
+
glaip_sdk-0.7.13.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|