dlab-cli 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.
- dlab/__init__.py +6 -0
- dlab/cli.py +1075 -0
- dlab/config.py +190 -0
- dlab/create_dpack.py +1096 -0
- dlab/create_dpack_wizard.py +1471 -0
- dlab/create_parallel_agent_wizard.py +582 -0
- dlab/data/__init__.py +0 -0
- dlab/data/models.json +1793 -0
- dlab/docker.py +591 -0
- dlab/local.py +269 -0
- dlab/model_fallback.py +360 -0
- dlab/parallel_tool.py +18 -0
- dlab/session.py +389 -0
- dlab/timeline.py +684 -0
- dlab/tui/__init__.py +9 -0
- dlab/tui/app.py +664 -0
- dlab/tui/log_watcher.py +208 -0
- dlab/tui/models.py +438 -0
- dlab/tui/widgets/__init__.py +18 -0
- dlab/tui/widgets/agent_list.py +170 -0
- dlab/tui/widgets/artifacts_pane.py +618 -0
- dlab/tui/widgets/log_view.py +505 -0
- dlab/tui/widgets/search_popup.py +151 -0
- dlab/tui/widgets/status_bar.py +106 -0
- dlab_cli-0.1.0.dist-info/METADATA +237 -0
- dlab_cli-0.1.0.dist-info/RECORD +30 -0
- dlab_cli-0.1.0.dist-info/WHEEL +5 -0
- dlab_cli-0.1.0.dist-info/entry_points.txt +2 -0
- dlab_cli-0.1.0.dist-info/licenses/LICENSE +201 -0
- dlab_cli-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agent selector sidebar widget.
|
|
3
|
+
|
|
4
|
+
Displays list of agents/sources with status indicators.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
|
|
9
|
+
from textual.widgets import ListView, ListItem, Static
|
|
10
|
+
from textual.reactive import reactive
|
|
11
|
+
from textual.message import Message
|
|
12
|
+
from rich.text import Text
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def shorten_agent_name(name: str) -> str:
|
|
16
|
+
"""
|
|
17
|
+
Shorten agent names for display.
|
|
18
|
+
|
|
19
|
+
Converts "poet-parallel-run-1234567890/instance-1" to "⟝ poet (…90) / inst-1".
|
|
20
|
+
Converts "poet-parallel-run-1234567890/consolidator" to "⟝ poet (…90) / cnsldtr".
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
name : str
|
|
25
|
+
Full agent name.
|
|
26
|
+
|
|
27
|
+
Returns
|
|
28
|
+
-------
|
|
29
|
+
str
|
|
30
|
+
Shortened display name.
|
|
31
|
+
"""
|
|
32
|
+
# Match pattern: agent-parallel-run-<number>/suffix
|
|
33
|
+
match = re.match(r"^(.+)-parallel-run-(\d+)/(.+)$", name)
|
|
34
|
+
if match:
|
|
35
|
+
agent = match.group(1)
|
|
36
|
+
number = match.group(2)
|
|
37
|
+
suffix = match.group(3)
|
|
38
|
+
|
|
39
|
+
# Shorten suffix
|
|
40
|
+
if suffix.startswith("instance-"):
|
|
41
|
+
suffix = "inst-" + suffix[9:]
|
|
42
|
+
elif suffix == "consolidator":
|
|
43
|
+
suffix = "cnsldtr"
|
|
44
|
+
elif suffix.startswith("consolidator-"):
|
|
45
|
+
suffix = "cnsldtr-" + suffix[13:]
|
|
46
|
+
|
|
47
|
+
return f"⟝ {agent} …{number[-2:]}/ {suffix}"
|
|
48
|
+
return name
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class AgentListItem(ListItem):
|
|
52
|
+
"""
|
|
53
|
+
List item for a single agent.
|
|
54
|
+
|
|
55
|
+
Displays agent name with status indicator.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
def __init__(self, name: str, agent_running: bool = True) -> None:
|
|
59
|
+
"""
|
|
60
|
+
Initialize agent list item.
|
|
61
|
+
|
|
62
|
+
Parameters
|
|
63
|
+
----------
|
|
64
|
+
name : str
|
|
65
|
+
Agent name.
|
|
66
|
+
agent_running : bool
|
|
67
|
+
Whether agent is still running.
|
|
68
|
+
"""
|
|
69
|
+
super().__init__()
|
|
70
|
+
self.agent_name = name
|
|
71
|
+
self.agent_running = agent_running
|
|
72
|
+
|
|
73
|
+
def compose(self):
|
|
74
|
+
"""Compose the widget."""
|
|
75
|
+
if self.agent_running:
|
|
76
|
+
indicator = Text("● ", style="bold #A6E22E")
|
|
77
|
+
else:
|
|
78
|
+
indicator = Text("○ ", style="#75715E")
|
|
79
|
+
|
|
80
|
+
name_style = "" if self.agent_running else "#75715E"
|
|
81
|
+
display_name = shorten_agent_name(self.agent_name)
|
|
82
|
+
name_text = Text(display_name, style=name_style)
|
|
83
|
+
|
|
84
|
+
yield Static(indicator + name_text)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class AgentSelector(ListView):
|
|
88
|
+
"""
|
|
89
|
+
Sidebar list widget for selecting log sources.
|
|
90
|
+
|
|
91
|
+
Features:
|
|
92
|
+
- Show running agents with green indicator
|
|
93
|
+
- Grey out completed agents
|
|
94
|
+
- Natural sort order (main first, instances, consolidator last)
|
|
95
|
+
- Click/keyboard to select agent
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
class AgentSelected(Message):
|
|
99
|
+
"""Message sent when an agent is selected."""
|
|
100
|
+
|
|
101
|
+
def __init__(self, agent_name: str) -> None:
|
|
102
|
+
self.agent_name = agent_name
|
|
103
|
+
super().__init__()
|
|
104
|
+
|
|
105
|
+
selected_agent: reactive[str | None] = reactive(None)
|
|
106
|
+
|
|
107
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
108
|
+
super().__init__(*args, **kwargs)
|
|
109
|
+
self._agents: dict[str, bool] = {} # name -> is_running
|
|
110
|
+
|
|
111
|
+
def update_agents(
|
|
112
|
+
self,
|
|
113
|
+
agents: list[str],
|
|
114
|
+
running: set[str],
|
|
115
|
+
) -> None:
|
|
116
|
+
"""
|
|
117
|
+
Update the agent list with current states.
|
|
118
|
+
|
|
119
|
+
Parameters
|
|
120
|
+
----------
|
|
121
|
+
agents : list[str]
|
|
122
|
+
All agent names (should be pre-sorted).
|
|
123
|
+
running : set[str]
|
|
124
|
+
Names of currently running agents.
|
|
125
|
+
"""
|
|
126
|
+
# Track what changed
|
|
127
|
+
new_agents = {name: name in running for name in agents}
|
|
128
|
+
|
|
129
|
+
if new_agents == self._agents:
|
|
130
|
+
return
|
|
131
|
+
|
|
132
|
+
self._agents = new_agents
|
|
133
|
+
|
|
134
|
+
# Rebuild list
|
|
135
|
+
self.clear()
|
|
136
|
+
for name in agents:
|
|
137
|
+
running_status = name in running
|
|
138
|
+
item = AgentListItem(name, agent_running=running_status)
|
|
139
|
+
self.append(item)
|
|
140
|
+
|
|
141
|
+
# Re-select if needed
|
|
142
|
+
if self.selected_agent and self.selected_agent in self._agents:
|
|
143
|
+
self._select_by_name(self.selected_agent)
|
|
144
|
+
|
|
145
|
+
def _select_by_name(self, name: str) -> None:
|
|
146
|
+
"""Select agent by name."""
|
|
147
|
+
for i, item in enumerate(self.children):
|
|
148
|
+
if isinstance(item, AgentListItem) and item.agent_name == name:
|
|
149
|
+
self.index = i
|
|
150
|
+
break
|
|
151
|
+
|
|
152
|
+
def on_list_view_selected(self, event: ListView.Selected) -> None:
|
|
153
|
+
"""Handle selection (Enter key or click)."""
|
|
154
|
+
if isinstance(event.item, AgentListItem):
|
|
155
|
+
self.selected_agent = event.item.agent_name
|
|
156
|
+
self.post_message(self.AgentSelected(event.item.agent_name))
|
|
157
|
+
|
|
158
|
+
def on_list_view_highlighted(self, event: ListView.Highlighted) -> None:
|
|
159
|
+
"""Handle highlight change (arrow key navigation)."""
|
|
160
|
+
if isinstance(event.item, AgentListItem):
|
|
161
|
+
self.selected_agent = event.item.agent_name
|
|
162
|
+
self.post_message(self.AgentSelected(event.item.agent_name))
|
|
163
|
+
|
|
164
|
+
def select_first(self) -> None:
|
|
165
|
+
"""Select the first agent."""
|
|
166
|
+
if self._agents:
|
|
167
|
+
first_name = next(iter(self._agents.keys()))
|
|
168
|
+
self.selected_agent = first_name
|
|
169
|
+
self._select_by_name(first_name)
|
|
170
|
+
self.post_message(self.AgentSelected(first_name))
|