virtuai-cli 0.2.1__tar.gz → 0.2.2__tar.gz
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.
- {virtuai_cli-0.2.1 → virtuai_cli-0.2.2}/PKG-INFO +1 -1
- {virtuai_cli-0.2.1 → virtuai_cli-0.2.2}/pyproject.toml +1 -1
- {virtuai_cli-0.2.1 → virtuai_cli-0.2.2}/src/virtuai_cli/__init__.py +1 -1
- {virtuai_cli-0.2.1 → virtuai_cli-0.2.2}/src/virtuai_cli/chat/tui.py +30 -19
- {virtuai_cli-0.2.1 → virtuai_cli-0.2.2}/src/virtuai_cli/chat/widgets.py +3 -3
- {virtuai_cli-0.2.1 → virtuai_cli-0.2.2}/src/virtuai_cli.egg-info/PKG-INFO +1 -1
- {virtuai_cli-0.2.1 → virtuai_cli-0.2.2}/README.md +0 -0
- {virtuai_cli-0.2.1 → virtuai_cli-0.2.2}/setup.cfg +0 -0
- {virtuai_cli-0.2.1 → virtuai_cli-0.2.2}/src/virtuai_cli/chat/__init__.py +0 -0
- {virtuai_cli-0.2.1 → virtuai_cli-0.2.2}/src/virtuai_cli/chat/command.py +0 -0
- {virtuai_cli-0.2.1 → virtuai_cli-0.2.2}/src/virtuai_cli/chat/sse.py +0 -0
- {virtuai_cli-0.2.1 → virtuai_cli-0.2.2}/src/virtuai_cli/config.py +0 -0
- {virtuai_cli-0.2.1 → virtuai_cli-0.2.2}/src/virtuai_cli/executor.py +0 -0
- {virtuai_cli-0.2.1 → virtuai_cli-0.2.2}/src/virtuai_cli/main.py +0 -0
- {virtuai_cli-0.2.1 → virtuai_cli-0.2.2}/src/virtuai_cli/runner.py +0 -0
- {virtuai_cli-0.2.1 → virtuai_cli-0.2.2}/src/virtuai_cli/security.py +0 -0
- {virtuai_cli-0.2.1 → virtuai_cli-0.2.2}/src/virtuai_cli.egg-info/SOURCES.txt +0 -0
- {virtuai_cli-0.2.1 → virtuai_cli-0.2.2}/src/virtuai_cli.egg-info/dependency_links.txt +0 -0
- {virtuai_cli-0.2.1 → virtuai_cli-0.2.2}/src/virtuai_cli.egg-info/entry_points.txt +0 -0
- {virtuai_cli-0.2.1 → virtuai_cli-0.2.2}/src/virtuai_cli.egg-info/requires.txt +0 -0
- {virtuai_cli-0.2.1 → virtuai_cli-0.2.2}/src/virtuai_cli.egg-info/top_level.txt +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""VirtuAI local CLI."""
|
|
2
|
-
__version__ = "0.2.
|
|
2
|
+
__version__ = "0.2.2"
|
|
@@ -22,22 +22,20 @@ class ChatApp(App):
|
|
|
22
22
|
CSS = """
|
|
23
23
|
Screen { background: $surface; }
|
|
24
24
|
|
|
25
|
-
#conversation {
|
|
26
|
-
height: 1fr;
|
|
27
|
-
padding: 0 1;
|
|
28
|
-
scrollbar-gutter: stable;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
25
|
#status {
|
|
32
|
-
dock: top;
|
|
33
26
|
height: 1;
|
|
34
27
|
padding: 0 1;
|
|
35
28
|
background: $panel;
|
|
36
29
|
color: $text-muted;
|
|
37
30
|
}
|
|
38
31
|
|
|
32
|
+
#conversation {
|
|
33
|
+
height: 1fr;
|
|
34
|
+
padding: 0 1;
|
|
35
|
+
scrollbar-gutter: stable;
|
|
36
|
+
}
|
|
37
|
+
|
|
39
38
|
#input {
|
|
40
|
-
dock: bottom;
|
|
41
39
|
height: 3;
|
|
42
40
|
border: round $primary;
|
|
43
41
|
margin: 0 1 1 1;
|
|
@@ -108,18 +106,25 @@ class ChatApp(App):
|
|
|
108
106
|
|
|
109
107
|
async def _run_ws(self) -> None:
|
|
110
108
|
def on_status(level: str, msg: str) -> None:
|
|
111
|
-
#
|
|
112
|
-
self.
|
|
109
|
+
# Runner runs on the same event loop, so call directly.
|
|
110
|
+
self._set_status(msg)
|
|
113
111
|
|
|
114
112
|
try:
|
|
115
113
|
await ws_runner.run_forever(self.server_url, self.token, self.workdir, on_status=on_status)
|
|
114
|
+
except asyncio.CancelledError:
|
|
115
|
+
raise
|
|
116
116
|
except Exception as exc:
|
|
117
|
-
self.
|
|
117
|
+
self._set_status(f"runner error: {exc}")
|
|
118
118
|
|
|
119
119
|
def _set_status(self, text: str) -> None:
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
# The status widget may have been torn down during app shutdown —
|
|
121
|
+
# swallow the lookup error so we don't surface a NoMatches on exit.
|
|
122
|
+
try:
|
|
123
|
+
self.query_one("#status", Static).update(
|
|
124
|
+
f"{self.workspace_name} · {self.agent_name} · {text}"
|
|
125
|
+
)
|
|
126
|
+
except Exception:
|
|
127
|
+
pass
|
|
123
128
|
|
|
124
129
|
# ── Input submission ──────────────────────────────────────────────────
|
|
125
130
|
@on(Input.Submitted, "#input")
|
|
@@ -199,15 +204,21 @@ class ChatApp(App):
|
|
|
199
204
|
):
|
|
200
205
|
await self._handle_event(event, turn)
|
|
201
206
|
except asyncio.CancelledError:
|
|
202
|
-
|
|
207
|
+
# App may be shutting down — don't touch widgets here.
|
|
203
208
|
raise
|
|
204
209
|
except Exception as exc:
|
|
205
|
-
|
|
206
|
-
|
|
210
|
+
try:
|
|
211
|
+
await turn.append_token(f"\n\n*[error: {exc}]*")
|
|
212
|
+
self._set_status(f"stream error: {exc}")
|
|
213
|
+
except Exception:
|
|
214
|
+
pass
|
|
207
215
|
finally:
|
|
208
216
|
turn.mark_final()
|
|
209
|
-
|
|
210
|
-
|
|
217
|
+
try:
|
|
218
|
+
scroll = self.query_one("#conversation", VerticalScroll)
|
|
219
|
+
scroll.scroll_end(animate=False)
|
|
220
|
+
except Exception:
|
|
221
|
+
pass
|
|
211
222
|
|
|
212
223
|
async def _handle_event(self, event: dict, turn: AssistantTurn) -> None:
|
|
213
224
|
etype = event.get("type")
|
|
@@ -72,9 +72,9 @@ class ToolCallCard(Static):
|
|
|
72
72
|
self.input_str = input_str or ""
|
|
73
73
|
self.output_preview: Optional[str] = None
|
|
74
74
|
self.add_class("-running")
|
|
75
|
-
self.
|
|
75
|
+
self._redraw()
|
|
76
76
|
|
|
77
|
-
def
|
|
77
|
+
def _redraw(self) -> None:
|
|
78
78
|
marker = "✓" if "-done" in self.classes else ("✗" if "-error" in self.classes else "▶")
|
|
79
79
|
head = Text(f"{marker} {self.tool_name}", style="bold")
|
|
80
80
|
if self.input_str:
|
|
@@ -90,7 +90,7 @@ class ToolCallCard(Static):
|
|
|
90
90
|
self.output_preview = output_preview
|
|
91
91
|
self.remove_class("-running")
|
|
92
92
|
self.add_class("-error" if errored else "-done")
|
|
93
|
-
self.
|
|
93
|
+
self._redraw()
|
|
94
94
|
|
|
95
95
|
|
|
96
96
|
class TodoList(Static):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|