glaip-sdk 0.2.2__py3-none-any.whl → 0.3.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/cli/commands/agents.py +50 -35
- glaip_sdk/cli/commands/mcps.py +28 -18
- glaip_sdk/cli/commands/models.py +3 -5
- glaip_sdk/cli/commands/tools.py +27 -16
- glaip_sdk/cli/constants.py +3 -0
- glaip_sdk/cli/main.py +1 -3
- glaip_sdk/cli/slash/agent_session.py +3 -13
- glaip_sdk/cli/slash/prompt.py +3 -0
- glaip_sdk/cli/slash/remote_runs_controller.py +566 -0
- glaip_sdk/cli/slash/session.py +138 -47
- glaip_sdk/cli/slash/tui/__init__.py +9 -0
- glaip_sdk/cli/slash/tui/remote_runs_app.py +632 -0
- glaip_sdk/cli/transcript/viewer.py +6 -32
- glaip_sdk/cli/utils.py +183 -9
- glaip_sdk/cli/validators.py +5 -6
- glaip_sdk/client/__init__.py +2 -1
- glaip_sdk/client/agent_runs.py +147 -0
- glaip_sdk/client/agents.py +42 -22
- glaip_sdk/client/main.py +18 -6
- glaip_sdk/client/mcps.py +2 -4
- glaip_sdk/client/tools.py +2 -3
- glaip_sdk/config/constants.py +11 -0
- glaip_sdk/models/__init__.py +56 -0
- glaip_sdk/models/agent_runs.py +117 -0
- glaip_sdk/rich_components.py +58 -2
- glaip_sdk/utils/client_utils.py +13 -0
- glaip_sdk/utils/export.py +143 -0
- glaip_sdk/utils/import_export.py +6 -9
- glaip_sdk/utils/rendering/__init__.py +122 -1
- glaip_sdk/utils/rendering/renderer/base.py +3 -7
- glaip_sdk/utils/rendering/renderer/debug.py +0 -1
- glaip_sdk/utils/rendering/renderer/stream.py +4 -12
- glaip_sdk/utils/rendering/steps.py +1 -0
- glaip_sdk/utils/resource_refs.py +26 -15
- {glaip_sdk-0.2.2.dist-info → glaip_sdk-0.3.0.dist-info}/METADATA +24 -2
- {glaip_sdk-0.2.2.dist-info → glaip_sdk-0.3.0.dist-info}/RECORD +38 -31
- {glaip_sdk-0.2.2.dist-info → glaip_sdk-0.3.0.dist-info}/WHEEL +0 -0
- {glaip_sdk-0.2.2.dist-info → glaip_sdk-0.3.0.dist-info}/entry_points.txt +0 -0
glaip_sdk/cli/slash/session.py
CHANGED
|
@@ -37,6 +37,7 @@ from glaip_sdk.cli.commands import transcripts as transcripts_cmd
|
|
|
37
37
|
from glaip_sdk.cli.commands.configure import configure_command, load_config
|
|
38
38
|
from glaip_sdk.cli.commands.update import update_command
|
|
39
39
|
from glaip_sdk.cli.slash.agent_session import AgentRunSession
|
|
40
|
+
from glaip_sdk.cli.slash.remote_runs_controller import RemoteRunsController
|
|
40
41
|
from glaip_sdk.cli.slash.prompt import (
|
|
41
42
|
FormattedText,
|
|
42
43
|
PromptSession,
|
|
@@ -57,6 +58,7 @@ from glaip_sdk.cli.utils import (
|
|
|
57
58
|
format_command_hint,
|
|
58
59
|
format_size,
|
|
59
60
|
get_client,
|
|
61
|
+
restore_slash_session_context,
|
|
60
62
|
)
|
|
61
63
|
from glaip_sdk.rich_components import AIPGrid, AIPPanel, AIPTable
|
|
62
64
|
|
|
@@ -71,6 +73,7 @@ class SlashCommand:
|
|
|
71
73
|
help: str
|
|
72
74
|
handler: SlashHandler
|
|
73
75
|
aliases: tuple[str, ...] = ()
|
|
76
|
+
agent_only: bool = False
|
|
74
77
|
|
|
75
78
|
|
|
76
79
|
NEW_QUICK_ACTIONS: tuple[dict[str, Any], ...] = (
|
|
@@ -80,6 +83,15 @@ NEW_QUICK_ACTIONS: tuple[dict[str, Any], ...] = (
|
|
|
80
83
|
"description": "Review transcript cache",
|
|
81
84
|
"tag": "NEW",
|
|
82
85
|
"priority": 10,
|
|
86
|
+
"scope": "global",
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"cli": None,
|
|
90
|
+
"slash": "runs",
|
|
91
|
+
"description": "View remote run history for the active agent",
|
|
92
|
+
"tag": "NEW",
|
|
93
|
+
"priority": 8,
|
|
94
|
+
"scope": "agent",
|
|
83
95
|
},
|
|
84
96
|
)
|
|
85
97
|
|
|
@@ -103,9 +115,26 @@ DEFAULT_QUICK_ACTIONS: tuple[dict[str, Any], ...] = (
|
|
|
103
115
|
"description": "Show all commands",
|
|
104
116
|
"priority": 0,
|
|
105
117
|
},
|
|
118
|
+
{
|
|
119
|
+
"cli": "configure",
|
|
120
|
+
"slash": "login",
|
|
121
|
+
"description": f"Configure credentials (alias [{HINT_COMMAND_STYLE}]/configure[/])",
|
|
122
|
+
"priority": -1,
|
|
123
|
+
},
|
|
106
124
|
)
|
|
107
125
|
|
|
108
126
|
|
|
127
|
+
HELP_COMMAND = "/help"
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _quick_action_scope(action: dict[str, Any]) -> str:
|
|
131
|
+
"""Return the scope for a quick action definition."""
|
|
132
|
+
scope = action.get("scope") or "global"
|
|
133
|
+
if isinstance(scope, str):
|
|
134
|
+
return scope.lower()
|
|
135
|
+
return "global"
|
|
136
|
+
|
|
137
|
+
|
|
109
138
|
class SlashSession:
|
|
110
139
|
"""Interactive command palette controller."""
|
|
111
140
|
|
|
@@ -131,6 +160,7 @@ class SlashSession:
|
|
|
131
160
|
self._welcome_rendered = False
|
|
132
161
|
self._active_renderer: Any | None = None
|
|
133
162
|
self._current_agent: Any | None = None
|
|
163
|
+
self._runs_pagination_state: dict[str, dict[str, Any]] = {} # agent_id -> {page, limit, cursor}
|
|
134
164
|
|
|
135
165
|
self._home_placeholder = "Hint: type / to explore commands · Ctrl+D exits"
|
|
136
166
|
|
|
@@ -199,10 +229,7 @@ class SlashSession:
|
|
|
199
229
|
self._run_interactive_loop()
|
|
200
230
|
finally:
|
|
201
231
|
if ctx_obj is not None:
|
|
202
|
-
|
|
203
|
-
ctx_obj.pop("_slash_session", None)
|
|
204
|
-
else:
|
|
205
|
-
ctx_obj["_slash_session"] = previous_session
|
|
232
|
+
restore_slash_session_context(ctx_obj, previous_session)
|
|
206
233
|
|
|
207
234
|
def _run_interactive_loop(self) -> None:
|
|
208
235
|
"""Run the main interactive command loop."""
|
|
@@ -290,7 +317,7 @@ class SlashSession:
|
|
|
290
317
|
if suggestion:
|
|
291
318
|
self.console.print(f"[{WARNING_STYLE}]Unknown command '{verb}'. Did you mean '/{suggestion}'?[/]")
|
|
292
319
|
else:
|
|
293
|
-
help_command =
|
|
320
|
+
help_command = HELP_COMMAND
|
|
294
321
|
help_hint = format_command_hint(help_command) or help_command
|
|
295
322
|
self.console.print(
|
|
296
323
|
f"[{WARNING_STYLE}]Unknown command '{verb}'. Type {help_hint} for a list of options.[/]"
|
|
@@ -324,7 +351,7 @@ class SlashSession:
|
|
|
324
351
|
if invoked_from_agent:
|
|
325
352
|
self._render_agent_help()
|
|
326
353
|
else:
|
|
327
|
-
self._render_global_help()
|
|
354
|
+
self._render_global_help(include_agent_hint=True)
|
|
328
355
|
except Exception as exc: # pragma: no cover - UI/display errors
|
|
329
356
|
self.console.print(f"[{ERROR_STYLE}]Error displaying help: {exc}[/]")
|
|
330
357
|
return False
|
|
@@ -339,9 +366,10 @@ class SlashSession:
|
|
|
339
366
|
table.add_row("<message>", "Run the active agent once with that prompt.")
|
|
340
367
|
table.add_row("/details", "Show the agent export (prompts to expand instructions).")
|
|
341
368
|
table.add_row(self.STATUS_COMMAND, "Display connection status without leaving.")
|
|
369
|
+
table.add_row("/runs", "✨ NEW · Open the remote run browser for this agent.")
|
|
342
370
|
table.add_row("/export [path]", "Export the latest agent transcript as JSONL.")
|
|
343
371
|
table.add_row("/exit (/back)", "Return to the slash home screen.")
|
|
344
|
-
table.add_row("
|
|
372
|
+
table.add_row(f"{HELP_COMMAND} (/?)", "Display this context-aware menu.")
|
|
345
373
|
|
|
346
374
|
panel_items = [table]
|
|
347
375
|
if self.last_run_input:
|
|
@@ -359,14 +387,28 @@ class SlashSession:
|
|
|
359
387
|
border_style=PRIMARY,
|
|
360
388
|
)
|
|
361
389
|
)
|
|
390
|
+
new_commands_table = AIPTable()
|
|
391
|
+
new_commands_table.add_column("Command", style=HINT_COMMAND_STYLE, no_wrap=True)
|
|
392
|
+
new_commands_table.add_column("Description", style=HINT_DESCRIPTION_COLOR)
|
|
393
|
+
new_commands_table.add_row(
|
|
394
|
+
"/runs",
|
|
395
|
+
"✨ NEW · View remote run history with keyboard navigation and export options.",
|
|
396
|
+
)
|
|
397
|
+
self.console.print(
|
|
398
|
+
AIPPanel(
|
|
399
|
+
new_commands_table,
|
|
400
|
+
title="New commands",
|
|
401
|
+
border_style=SECONDARY_LIGHT,
|
|
402
|
+
)
|
|
403
|
+
)
|
|
362
404
|
|
|
363
|
-
def _render_global_help(self) -> None:
|
|
405
|
+
def _render_global_help(self, *, include_agent_hint: bool = False) -> None:
|
|
364
406
|
"""Render help text for global slash commands."""
|
|
365
407
|
table = AIPTable()
|
|
366
408
|
table.add_column("Command", style=HINT_COMMAND_STYLE, no_wrap=True)
|
|
367
409
|
table.add_column("Description", style=HINT_DESCRIPTION_COLOR)
|
|
368
410
|
|
|
369
|
-
for cmd in
|
|
411
|
+
for cmd in self._visible_commands(include_agent_only=False):
|
|
370
412
|
aliases = ", ".join(f"/{alias}" for alias in cmd.aliases if alias)
|
|
371
413
|
verb = f"/{cmd.name}"
|
|
372
414
|
if aliases:
|
|
@@ -386,6 +428,11 @@ class SlashSession:
|
|
|
386
428
|
border_style=PRIMARY,
|
|
387
429
|
)
|
|
388
430
|
)
|
|
431
|
+
if include_agent_hint:
|
|
432
|
+
self.console.print(
|
|
433
|
+
"[dim]Additional commands (e.g. `/runs`) become available after you pick an agent with `/agents`. "
|
|
434
|
+
"Those agent-only commands stay hidden here to avoid confusion.[/]"
|
|
435
|
+
)
|
|
389
436
|
|
|
390
437
|
def _cmd_login(self, _args: list[str], _invoked_from_agent: bool) -> bool:
|
|
391
438
|
"""Handle the /login command.
|
|
@@ -589,6 +636,19 @@ class SlashSession:
|
|
|
589
636
|
view = transcripts_cmd._render_transcript_display(entry, snapshot.manifest_path, transcript_path, meta, events)
|
|
590
637
|
self.console.print(view, markup=False, highlight=False, soft_wrap=True, end="")
|
|
591
638
|
|
|
639
|
+
def _cmd_runs(self, args: list[str], _invoked_from_agent: bool) -> bool:
|
|
640
|
+
"""Handle the /runs command for browsing remote agent run history.
|
|
641
|
+
|
|
642
|
+
Args:
|
|
643
|
+
args: Command arguments (optional run_id for detail view).
|
|
644
|
+
_invoked_from_agent: Whether invoked from agent context.
|
|
645
|
+
|
|
646
|
+
Returns:
|
|
647
|
+
True to continue session.
|
|
648
|
+
"""
|
|
649
|
+
controller = RemoteRunsController(self)
|
|
650
|
+
return controller.handle_runs_command(args)
|
|
651
|
+
|
|
592
652
|
def _cmd_agents(self, args: list[str], _invoked_from_agent: bool) -> bool:
|
|
593
653
|
"""Handle the /agents command.
|
|
594
654
|
|
|
@@ -718,7 +778,7 @@ class SlashSession:
|
|
|
718
778
|
self._register(
|
|
719
779
|
SlashCommand(
|
|
720
780
|
name="login",
|
|
721
|
-
help="
|
|
781
|
+
help="Configure API credentials (alias `/configure`).",
|
|
722
782
|
handler=SlashSession._cmd_login,
|
|
723
783
|
aliases=("configure",),
|
|
724
784
|
)
|
|
@@ -733,7 +793,10 @@ class SlashSession:
|
|
|
733
793
|
self._register(
|
|
734
794
|
SlashCommand(
|
|
735
795
|
name="transcripts",
|
|
736
|
-
help=
|
|
796
|
+
help=(
|
|
797
|
+
"✨ NEW · Review cached transcript history. "
|
|
798
|
+
"Add a number (e.g. `/transcripts 5`) to change the row limit."
|
|
799
|
+
),
|
|
737
800
|
handler=SlashSession._cmd_transcripts,
|
|
738
801
|
)
|
|
739
802
|
)
|
|
@@ -766,6 +829,14 @@ class SlashSession:
|
|
|
766
829
|
handler=SlashSession._cmd_update,
|
|
767
830
|
)
|
|
768
831
|
)
|
|
832
|
+
self._register(
|
|
833
|
+
SlashCommand(
|
|
834
|
+
name="runs",
|
|
835
|
+
help="✨ NEW · Browse remote agent run history (requires active agent session).",
|
|
836
|
+
handler=SlashSession._cmd_runs,
|
|
837
|
+
agent_only=True,
|
|
838
|
+
)
|
|
839
|
+
)
|
|
769
840
|
|
|
770
841
|
def _register(self, command: SlashCommand) -> None:
|
|
771
842
|
"""Register a slash command.
|
|
@@ -777,6 +848,13 @@ class SlashSession:
|
|
|
777
848
|
for key in (command.name, *command.aliases):
|
|
778
849
|
self._commands[key] = command
|
|
779
850
|
|
|
851
|
+
def _visible_commands(self, *, include_agent_only: bool) -> list[SlashCommand]:
|
|
852
|
+
"""Return the list of commands that should be shown in global listings."""
|
|
853
|
+
commands = sorted(self._unique_commands.values(), key=lambda c: c.name)
|
|
854
|
+
if include_agent_only:
|
|
855
|
+
return commands
|
|
856
|
+
return [cmd for cmd in commands if not cmd.agent_only]
|
|
857
|
+
|
|
780
858
|
def open_transcript_viewer(self, *, announce: bool = True) -> None:
|
|
781
859
|
"""Launch the transcript viewer for the most recent run."""
|
|
782
860
|
payload, manifest = self._get_last_transcript()
|
|
@@ -1104,8 +1182,9 @@ class SlashSession:
|
|
|
1104
1182
|
|
|
1105
1183
|
header_grid = self._build_header_grid(agent_info, transcript_status)
|
|
1106
1184
|
keybar = self._build_keybar()
|
|
1107
|
-
|
|
1108
1185
|
header_grid.add_row(keybar, "")
|
|
1186
|
+
|
|
1187
|
+
# Agent-scoped commands like /runs will appear in /help, no need to duplicate here
|
|
1109
1188
|
self.console.print(AIPPanel(header_grid, title="Agent Session", border_style=PRIMARY))
|
|
1110
1189
|
|
|
1111
1190
|
def _get_agent_info(self, active_agent: Any) -> dict[str, str]:
|
|
@@ -1163,9 +1242,10 @@ class SlashSession:
|
|
|
1163
1242
|
keybar = AIPGrid(expand=True)
|
|
1164
1243
|
keybar.add_column(justify="left", ratio=1)
|
|
1165
1244
|
keybar.add_column(justify="left", ratio=1)
|
|
1245
|
+
keybar.add_column(justify="left", ratio=1)
|
|
1166
1246
|
|
|
1167
1247
|
keybar.add_row(
|
|
1168
|
-
format_command_hint(
|
|
1248
|
+
format_command_hint(HELP_COMMAND, "Show commands") or "",
|
|
1169
1249
|
format_command_hint("/details", "Agent config (expand prompt)") or "",
|
|
1170
1250
|
format_command_hint("/exit", "Back") or "",
|
|
1171
1251
|
)
|
|
@@ -1225,33 +1305,39 @@ class SlashSession:
|
|
|
1225
1305
|
return None
|
|
1226
1306
|
|
|
1227
1307
|
def _show_default_quick_actions(self) -> None:
|
|
1228
|
-
"""Show
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
self.console.print(f"[dim]{'─' * 40}[/]")
|
|
1233
|
-
self._render_quick_action_group(new_hints, "New commands")
|
|
1234
|
-
self._render_quick_action_group(evergreen_hints, "Quick actions")
|
|
1308
|
+
"""Show simplified help hint to discover commands."""
|
|
1309
|
+
self.console.print(f"[dim]{'─' * 40}[/]")
|
|
1310
|
+
help_hint = format_command_hint(HELP_COMMAND, "Show all commands") or HELP_COMMAND
|
|
1311
|
+
self.console.print(f"• {help_hint}")
|
|
1235
1312
|
self._default_actions_shown = True
|
|
1236
1313
|
|
|
1314
|
+
def _collect_scoped_new_action_hints(self, scope: str) -> list[tuple[str, str]]:
|
|
1315
|
+
"""Return new quick action hints filtered by scope."""
|
|
1316
|
+
scoped_actions = [action for action in NEW_QUICK_ACTIONS if _quick_action_scope(action) == scope]
|
|
1317
|
+
# Don't highlight with sparkle emoji in quick actions display - it will show in command palette instead
|
|
1318
|
+
return self._collect_quick_action_hints(scoped_actions)
|
|
1319
|
+
|
|
1237
1320
|
def _collect_quick_action_hints(
|
|
1238
1321
|
self,
|
|
1239
1322
|
actions: Iterable[dict[str, Any]],
|
|
1240
|
-
*,
|
|
1241
|
-
highlight_new: bool = False,
|
|
1242
1323
|
) -> list[tuple[str, str]]:
|
|
1243
1324
|
"""Collect quick action hints from action definitions.
|
|
1244
1325
|
|
|
1245
1326
|
Args:
|
|
1246
1327
|
actions: Iterable of action dictionaries.
|
|
1247
|
-
highlight_new: Whether to highlight new actions.
|
|
1248
1328
|
|
|
1249
1329
|
Returns:
|
|
1250
1330
|
List of (command, description) tuples.
|
|
1251
1331
|
"""
|
|
1252
1332
|
collected: list[tuple[str, str]] = []
|
|
1253
|
-
|
|
1254
|
-
|
|
1333
|
+
|
|
1334
|
+
def sort_key(payload: dict[str, Any]) -> tuple[int, str]:
|
|
1335
|
+
priority = int(payload.get("priority", 0))
|
|
1336
|
+
label = str(payload.get("slash") or payload.get("cli") or "")
|
|
1337
|
+
return (-priority, label.lower())
|
|
1338
|
+
|
|
1339
|
+
for action in sorted(actions, key=sort_key):
|
|
1340
|
+
hint = self._build_quick_action_hint(action)
|
|
1255
1341
|
if hint:
|
|
1256
1342
|
collected.append(hint)
|
|
1257
1343
|
return collected
|
|
@@ -1259,14 +1345,11 @@ class SlashSession:
|
|
|
1259
1345
|
def _build_quick_action_hint(
|
|
1260
1346
|
self,
|
|
1261
1347
|
action: dict[str, Any],
|
|
1262
|
-
*,
|
|
1263
|
-
highlight_new: bool = False,
|
|
1264
1348
|
) -> tuple[str, str] | None:
|
|
1265
1349
|
"""Build a quick action hint from an action definition.
|
|
1266
1350
|
|
|
1267
1351
|
Args:
|
|
1268
1352
|
action: Action dictionary.
|
|
1269
|
-
highlight_new: Whether to highlight as new.
|
|
1270
1353
|
|
|
1271
1354
|
Returns:
|
|
1272
1355
|
Tuple of (command, description) or None.
|
|
@@ -1275,11 +1358,8 @@ class SlashSession:
|
|
|
1275
1358
|
if not command:
|
|
1276
1359
|
return None
|
|
1277
1360
|
description = action.get("description", "")
|
|
1278
|
-
tag
|
|
1279
|
-
|
|
1280
|
-
description = f"[{ACCENT_STYLE}]{tag}[/] · {description}"
|
|
1281
|
-
if highlight_new:
|
|
1282
|
-
description = f"✨ {description}"
|
|
1361
|
+
# Don't include tag or sparkle emoji in quick actions display
|
|
1362
|
+
# The NEW tag will only show in the command dropdown (help text)
|
|
1283
1363
|
return command, description
|
|
1284
1364
|
|
|
1285
1365
|
def _render_quick_action_group(self, hints: list[tuple[str, str]], title: str) -> None:
|
|
@@ -1289,20 +1369,8 @@ class SlashSession:
|
|
|
1289
1369
|
hints: List of (command, description) tuples.
|
|
1290
1370
|
title: Group title.
|
|
1291
1371
|
"""
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
formatted_tokens: list[str] = []
|
|
1295
|
-
for command, description in hints:
|
|
1296
|
-
formatted = format_command_hint(command, description)
|
|
1297
|
-
if formatted:
|
|
1298
|
-
formatted_tokens.append(f"• {formatted}")
|
|
1299
|
-
if not formatted_tokens:
|
|
1300
|
-
return
|
|
1301
|
-
header = f"[dim]{title}[/dim] · {formatted_tokens[0]}"
|
|
1302
|
-
self.console.print(header)
|
|
1303
|
-
remaining = formatted_tokens[1:]
|
|
1304
|
-
for chunk in self._chunk_tokens(remaining, size=3):
|
|
1305
|
-
self.console.print(" " + " ".join(chunk))
|
|
1372
|
+
for line in self._format_quick_action_lines(hints, title):
|
|
1373
|
+
self.console.print(line)
|
|
1306
1374
|
|
|
1307
1375
|
def _chunk_tokens(self, tokens: list[str], *, size: int) -> Iterable[list[str]]:
|
|
1308
1376
|
"""Chunk tokens into groups of specified size.
|
|
@@ -1400,6 +1468,29 @@ class SlashSession:
|
|
|
1400
1468
|
panel_content = Group(*body_lines)
|
|
1401
1469
|
self.console.print(AIPPanel(panel_content, title=title, border_style=SECONDARY_LIGHT, expand=False))
|
|
1402
1470
|
|
|
1471
|
+
def _format_quick_action_lines(self, hints: list[tuple[str, str]], title: str) -> list[str]:
|
|
1472
|
+
"""Return formatted lines for quick action hints."""
|
|
1473
|
+
if not hints:
|
|
1474
|
+
return []
|
|
1475
|
+
formatted_tokens: list[str] = []
|
|
1476
|
+
for command, description in hints:
|
|
1477
|
+
formatted = format_command_hint(command, description)
|
|
1478
|
+
if formatted:
|
|
1479
|
+
formatted_tokens.append(f"• {formatted}")
|
|
1480
|
+
if not formatted_tokens:
|
|
1481
|
+
return []
|
|
1482
|
+
lines: list[str] = []
|
|
1483
|
+
# Use vertical layout (1 per line) for better readability
|
|
1484
|
+
chunks = list(self._chunk_tokens(formatted_tokens, size=1))
|
|
1485
|
+
prefix = f"[dim]{title}[/dim]\n " if title else ""
|
|
1486
|
+
for idx, chunk in enumerate(chunks):
|
|
1487
|
+
row = " ".join(chunk)
|
|
1488
|
+
if idx == 0:
|
|
1489
|
+
lines.append(f"{prefix}{row}" if prefix else row)
|
|
1490
|
+
else:
|
|
1491
|
+
lines.append(f" {row}")
|
|
1492
|
+
return lines
|
|
1493
|
+
|
|
1403
1494
|
def _load_config(self) -> dict[str, Any]:
|
|
1404
1495
|
"""Load configuration with caching.
|
|
1405
1496
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Textual UI helpers for slash commands."""
|
|
2
|
+
|
|
3
|
+
from glaip_sdk.cli.slash.tui.remote_runs_app import (
|
|
4
|
+
RemoteRunsTextualApp,
|
|
5
|
+
RemoteRunsTUICallbacks,
|
|
6
|
+
run_remote_runs_textual,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
__all__ = ["RemoteRunsTextualApp", "RemoteRunsTUICallbacks", "run_remote_runs_textual"]
|