aline-ai 0.6.7__py3-none-any.whl → 0.7.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.
- {aline_ai-0.6.7.dist-info → aline_ai-0.7.0.dist-info}/METADATA +1 -1
- {aline_ai-0.6.7.dist-info → aline_ai-0.7.0.dist-info}/RECORD +12 -12
- realign/__init__.py +1 -1
- realign/cli.py +2 -4
- realign/commands/auth.py +20 -0
- realign/dashboard/app.py +3 -53
- realign/dashboard/widgets/__init__.py +0 -4
- realign/dashboard/widgets/agents_panel.py +15 -1
- {aline_ai-0.6.7.dist-info → aline_ai-0.7.0.dist-info}/WHEEL +0 -0
- {aline_ai-0.6.7.dist-info → aline_ai-0.7.0.dist-info}/entry_points.txt +0 -0
- {aline_ai-0.6.7.dist-info → aline_ai-0.7.0.dist-info}/licenses/LICENSE +0 -0
- {aline_ai-0.6.7.dist-info → aline_ai-0.7.0.dist-info}/top_level.txt +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
aline_ai-0.
|
|
2
|
-
realign/__init__.py,sha256=
|
|
1
|
+
aline_ai-0.7.0.dist-info/licenses/LICENSE,sha256=H8wTqV5IF1oHw_HbBtS1PSDU8G_q81yblEIL_JfV8Vo,1077
|
|
2
|
+
realign/__init__.py,sha256=jmqpELzn0PmqMNOgBDumsQui50MdbRXM3-CbTnSxMAg,1623
|
|
3
3
|
realign/agent_names.py,sha256=H4oVJMkqg1ZYCk58vD_Jh9apaAHSFJRswa-C9SPdJxc,1171
|
|
4
4
|
realign/auth.py,sha256=d_1yvCwluN5iIrdgjtuSKpOYAksDzrzNgntKacLVJrw,16583
|
|
5
5
|
realign/claude_detector.py,sha256=ZLSJacMo6zzQclXByABKA70UNpstxqIv3fPGqdpA934,2792
|
|
6
|
-
realign/cli.py,sha256=
|
|
6
|
+
realign/cli.py,sha256=mPRzNdPeKeZRLJvHUi8mHsl-fq99ZApkEaB-yjU6Gw0,37567
|
|
7
7
|
realign/codex_detector.py,sha256=WGIClvlrFVCqJ5vR9DrKVsp1eJhOShvcaXibTHb0Nfc,6304
|
|
8
8
|
realign/codex_home.py,sha256=ljkW8uCfQD4cisEJtPNQmIgaR0yEfWSyHwoVQFY-6p4,4374
|
|
9
9
|
realign/codex_terminal_linker.py,sha256=L2Ha4drlZ7Sbq2jzXyxczOdUY3S5fu1gJqoI5WN9CKk,6211
|
|
@@ -37,7 +37,7 @@ realign/claude_hooks/user_prompt_submit_hook_installer.py,sha256=2xLF8yZcE7Iwib9
|
|
|
37
37
|
realign/commands/__init__.py,sha256=WVaVT1orM2Z0PYaG3X6tkKb_t2v3n_3siCadh1qd_QA,107
|
|
38
38
|
realign/commands/add.py,sha256=_Xzt9P15mwndA3JvBBVrki8tn9Cc0UP6SiLwM4RS8Nc,27232
|
|
39
39
|
realign/commands/agent.py,sha256=3CS48bMn7tkdDWKRrfg7CYbhcJK4Pz40YjYMvwD7c2w,3173
|
|
40
|
-
realign/commands/auth.py,sha256=
|
|
40
|
+
realign/commands/auth.py,sha256=wcs1lUcSXxv75WcGruzyZ3kgi0xXA8W4lNnUwM4a3CI,11731
|
|
41
41
|
realign/commands/config.py,sha256=nYnu_h2pk7GODcrzrV04K51D-s7v06FlRXHJ0HJ-gvU,6732
|
|
42
42
|
realign/commands/context.py,sha256=pM2KfZHVkB-ou4nBhFvKSwnYliLBzwN3zerLyBAbhfE,7095
|
|
43
43
|
realign/commands/doctor.py,sha256=0c1TZuA_cw1CSU0yKMVRU-18uTxdqjXKJ8lP2CTTNSQ,20656
|
|
@@ -51,7 +51,7 @@ realign/commands/upgrade.py,sha256=L3PLOUIN5qAQTbkfoVtSsIbbzEezA_xjjk9F1GMVfjw,1
|
|
|
51
51
|
realign/commands/watcher.py,sha256=4WTThIgr-Z5guKh_JqGDcPmerr97XiHrVaaijmckHsA,134350
|
|
52
52
|
realign/commands/worker.py,sha256=jTu7Pj60nTnn7SsH3oNCNnO6zl4TIFCJVNSC1OoQ_0o,23363
|
|
53
53
|
realign/dashboard/__init__.py,sha256=QZkHTsGityH8UkF8rmvA3xW7dMXNe0swEWr443qfgCM,128
|
|
54
|
-
realign/dashboard/app.py,sha256=
|
|
54
|
+
realign/dashboard/app.py,sha256=IXF9CDbui4zXufRgc6Gagje7Duw5VlewUru4njbA6lQ,8243
|
|
55
55
|
realign/dashboard/clipboard.py,sha256=81frq83E_urqLkwuCvtl0hiTEjavtdQn8kCi72jJWcs,1207
|
|
56
56
|
realign/dashboard/layout.py,sha256=sZxmFj6QTbkois9MHTvBEMMcnaRVehCDqugdbiFx10k,9072
|
|
57
57
|
realign/dashboard/terminal_backend.py,sha256=MlDfwtqhftyQK6jDNizQGFjAWIo5Bx2TDpSnP3MCZVM,3375
|
|
@@ -69,8 +69,8 @@ realign/dashboard/screens/help_screen.py,sha256=Icrcvbgyz49R2tBiu8vBZ4CLm6iYclv_
|
|
|
69
69
|
realign/dashboard/screens/session_detail.py,sha256=TBkHqSHyMxsLB2QdZq9m1EoiH8oRVDbPrjt-a8I9sHs,9561
|
|
70
70
|
realign/dashboard/screens/share_import.py,sha256=hl2x0yGVycsoUI76AmdZTAV-br3Q6191g5xHHrZ8hOA,6318
|
|
71
71
|
realign/dashboard/styles/dashboard.tcss,sha256=9W5Tx0lgyGb4HU-z-Kn7gBdexIK0aPe0bkVn2k_AseM,3288
|
|
72
|
-
realign/dashboard/widgets/__init__.py,sha256=
|
|
73
|
-
realign/dashboard/widgets/agents_panel.py,sha256=
|
|
72
|
+
realign/dashboard/widgets/__init__.py,sha256=dXsOnbeu_8XhP-6Bu6-R_0LNGqsSM6x7dG7FCDumpa8,460
|
|
73
|
+
realign/dashboard/widgets/agents_panel.py,sha256=CGs3qcHGcDDVIpDw1ERb8Jf2t-l--hSY_ufw9SZzM8E,43846
|
|
74
74
|
realign/dashboard/widgets/config_panel.py,sha256=eRJRuqImQ8eJIKCEj4O8EvYxI-ht_anrcYbT5JskWyU,15972
|
|
75
75
|
realign/dashboard/widgets/events_table.py,sha256=0cMvE0KdZFBZyvywv7vlt005qsR0aLQnQiMf3ZzK7RY,30218
|
|
76
76
|
realign/dashboard/widgets/header.py,sha256=0HHCFXX7F3C6HII-WDwOJwWkJrajmKPWmdoMWyOkn9E,1587
|
|
@@ -104,8 +104,8 @@ realign/triggers/next_turn_trigger.py,sha256=-x80_I-WmIjXXzQHEPBykgx_GQW6oKaLDQx
|
|
|
104
104
|
realign/triggers/registry.py,sha256=dkIjSd8Bg-hF0nxaO2Fi2K-0Zipqv6vVjc-HYSrA_fY,3656
|
|
105
105
|
realign/triggers/turn_status.py,sha256=wAZEhXDAmDoX5F-ohWfSnZZ0eA6DAJ9svSPiSv_f6sg,6041
|
|
106
106
|
realign/triggers/turn_summary.py,sha256=f3hEUshgv9skJ9AbfWpoYs417lsv_HK2A_vpPjgryO4,4467
|
|
107
|
-
aline_ai-0.
|
|
108
|
-
aline_ai-0.
|
|
109
|
-
aline_ai-0.
|
|
110
|
-
aline_ai-0.
|
|
111
|
-
aline_ai-0.
|
|
107
|
+
aline_ai-0.7.0.dist-info/METADATA,sha256=nWZ9aGO494Ci9MG8EUIUQSc-uHxHV1MZkQ-4Z1E1x7M,1597
|
|
108
|
+
aline_ai-0.7.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
109
|
+
aline_ai-0.7.0.dist-info/entry_points.txt,sha256=TvYELpMoWsUTcQdMV8tBHxCbEf_LbK4sESqK3r8PM6Y,78
|
|
110
|
+
aline_ai-0.7.0.dist-info/top_level.txt,sha256=yIL3s2xv9nf1GwD5n71Aq_JEIV4AfzCIDNKBzewuRm4,8
|
|
111
|
+
aline_ai-0.7.0.dist-info/RECORD,,
|
realign/__init__.py
CHANGED
realign/cli.py
CHANGED
|
@@ -127,7 +127,7 @@ def main(
|
|
|
127
127
|
|
|
128
128
|
from .dashboard.app import AlineDashboard
|
|
129
129
|
|
|
130
|
-
dashboard = AlineDashboard(
|
|
130
|
+
dashboard = AlineDashboard(use_native_terminal=use_native_terminal)
|
|
131
131
|
dashboard.run()
|
|
132
132
|
|
|
133
133
|
|
|
@@ -1022,9 +1022,7 @@ def dashboard(
|
|
|
1022
1022
|
|
|
1023
1023
|
from .dashboard.app import AlineDashboard
|
|
1024
1024
|
|
|
1025
|
-
|
|
1026
|
-
dev_mode = dev or (ctx.obj.get("dev", False) if ctx.obj else False)
|
|
1027
|
-
dash = AlineDashboard(dev_mode=dev_mode, use_native_terminal=use_native_terminal)
|
|
1025
|
+
dash = AlineDashboard(use_native_terminal=use_native_terminal)
|
|
1028
1026
|
dash.run()
|
|
1029
1027
|
except Exception as e:
|
|
1030
1028
|
logger.error(f"Dashboard crashed: {e}\n{traceback.format_exc()}")
|
realign/commands/auth.py
CHANGED
|
@@ -170,6 +170,10 @@ def login_command() -> int:
|
|
|
170
170
|
print("User ID synced to local config")
|
|
171
171
|
|
|
172
172
|
logger.info(f"Login successful for {credentials.email}")
|
|
173
|
+
|
|
174
|
+
# Start watcher and worker daemons after login
|
|
175
|
+
_start_daemons()
|
|
176
|
+
|
|
173
177
|
return 0
|
|
174
178
|
|
|
175
179
|
|
|
@@ -227,6 +231,22 @@ def logout_command() -> int:
|
|
|
227
231
|
return 0
|
|
228
232
|
|
|
229
233
|
|
|
234
|
+
def _start_daemons() -> None:
|
|
235
|
+
"""Start watcher and worker daemons if not already running."""
|
|
236
|
+
try:
|
|
237
|
+
from . import watcher as watcher_cmd
|
|
238
|
+
|
|
239
|
+
exit_code = watcher_cmd.watcher_start_command()
|
|
240
|
+
if exit_code == 0:
|
|
241
|
+
if console:
|
|
242
|
+
console.print("[dim]Watcher and worker daemons started.[/dim]")
|
|
243
|
+
logger.info("Daemons started after login")
|
|
244
|
+
else:
|
|
245
|
+
logger.debug(f"watcher_start_command returned {exit_code}")
|
|
246
|
+
except Exception as e:
|
|
247
|
+
logger.debug(f"Failed to start daemons after login: {e}")
|
|
248
|
+
|
|
249
|
+
|
|
230
250
|
def _stop_daemons() -> None:
|
|
231
251
|
"""Stop watcher and worker daemons."""
|
|
232
252
|
import os
|
realign/dashboard/app.py
CHANGED
|
@@ -13,8 +13,6 @@ from textual.widgets import Footer, TabbedContent, TabPane
|
|
|
13
13
|
from ..logging_config import setup_logger
|
|
14
14
|
from .widgets import (
|
|
15
15
|
AlineHeader,
|
|
16
|
-
WatcherPanel,
|
|
17
|
-
WorkerPanel,
|
|
18
16
|
ConfigPanel,
|
|
19
17
|
AgentsPanel,
|
|
20
18
|
)
|
|
@@ -64,30 +62,24 @@ class AlineDashboard(App):
|
|
|
64
62
|
Binding("?", "help", "Help"),
|
|
65
63
|
Binding("tab", "next_tab", "Next Tab", priority=True, show=False),
|
|
66
64
|
Binding("shift+tab", "prev_tab", "Prev Tab", priority=True, show=False),
|
|
67
|
-
Binding("n", "page_next", "Next Page", show=False),
|
|
68
|
-
Binding("p", "page_prev", "Prev Page", show=False),
|
|
69
|
-
Binding("s", "switch_view", "Switch View", show=False),
|
|
70
65
|
Binding("ctrl+c", "quit_confirm", "Quit", priority=True),
|
|
71
66
|
]
|
|
72
67
|
|
|
73
68
|
_quit_confirm_window_s: float = 1.2
|
|
74
69
|
|
|
75
|
-
def __init__(self,
|
|
70
|
+
def __init__(self, use_native_terminal: bool | None = None):
|
|
76
71
|
"""Initialize the dashboard.
|
|
77
72
|
|
|
78
73
|
Args:
|
|
79
|
-
dev_mode: If True, shows developer tabs (Watcher, Worker).
|
|
80
74
|
use_native_terminal: If True, use native terminal backend (iTerm2/Kitty).
|
|
81
75
|
If False, use tmux.
|
|
82
76
|
If None (default), auto-detect from ALINE_TERMINAL_MODE env var.
|
|
83
77
|
"""
|
|
84
78
|
super().__init__()
|
|
85
|
-
self.dev_mode = dev_mode
|
|
86
79
|
self.use_native_terminal = use_native_terminal
|
|
87
80
|
self._native_terminal_mode = self._detect_native_mode()
|
|
88
81
|
logger.info(
|
|
89
|
-
f"AlineDashboard initialized (
|
|
90
|
-
f"native_terminal={self._native_terminal_mode})"
|
|
82
|
+
f"AlineDashboard initialized (native_terminal={self._native_terminal_mode})"
|
|
91
83
|
)
|
|
92
84
|
|
|
93
85
|
def _detect_native_mode(self) -> bool:
|
|
@@ -103,15 +95,9 @@ class AlineDashboard(App):
|
|
|
103
95
|
logger.debug("compose() started")
|
|
104
96
|
try:
|
|
105
97
|
yield AlineHeader()
|
|
106
|
-
|
|
107
|
-
with TabbedContent(initial=tab_ids[0] if tab_ids else "agents"):
|
|
98
|
+
with TabbedContent(initial="agents"):
|
|
108
99
|
with TabPane("Agents", id="agents"):
|
|
109
100
|
yield AgentsPanel()
|
|
110
|
-
if self.dev_mode:
|
|
111
|
-
with TabPane("Watcher", id="watcher"):
|
|
112
|
-
yield WatcherPanel()
|
|
113
|
-
with TabPane("Worker", id="worker"):
|
|
114
|
-
yield WorkerPanel()
|
|
115
101
|
with TabPane("Config", id="config"):
|
|
116
102
|
yield ConfigPanel()
|
|
117
103
|
yield Footer()
|
|
@@ -121,8 +107,6 @@ class AlineDashboard(App):
|
|
|
121
107
|
raise
|
|
122
108
|
|
|
123
109
|
def _tab_ids(self) -> list[str]:
|
|
124
|
-
if self.dev_mode:
|
|
125
|
-
return ["agents", "watcher", "worker", "config"]
|
|
126
110
|
return ["agents", "config"]
|
|
127
111
|
|
|
128
112
|
def on_mount(self) -> None:
|
|
@@ -207,43 +191,9 @@ class AlineDashboard(App):
|
|
|
207
191
|
|
|
208
192
|
if active_tab_id == "agents":
|
|
209
193
|
self.query_one(AgentsPanel).refresh_data()
|
|
210
|
-
elif active_tab_id == "watcher":
|
|
211
|
-
self.query_one(WatcherPanel).refresh_data()
|
|
212
|
-
elif active_tab_id == "worker":
|
|
213
|
-
self.query_one(WorkerPanel).refresh_data()
|
|
214
194
|
elif active_tab_id == "config":
|
|
215
195
|
self.query_one(ConfigPanel).refresh_data()
|
|
216
196
|
|
|
217
|
-
def action_page_next(self) -> None:
|
|
218
|
-
"""Go to next page in current panel."""
|
|
219
|
-
tabbed_content = self.query_one(TabbedContent)
|
|
220
|
-
active_tab_id = tabbed_content.active
|
|
221
|
-
|
|
222
|
-
if active_tab_id == "watcher":
|
|
223
|
-
self.query_one(WatcherPanel).action_next_page()
|
|
224
|
-
elif active_tab_id == "worker":
|
|
225
|
-
self.query_one(WorkerPanel).action_next_page()
|
|
226
|
-
|
|
227
|
-
def action_page_prev(self) -> None:
|
|
228
|
-
"""Go to previous page in current panel."""
|
|
229
|
-
tabbed_content = self.query_one(TabbedContent)
|
|
230
|
-
active_tab_id = tabbed_content.active
|
|
231
|
-
|
|
232
|
-
if active_tab_id == "watcher":
|
|
233
|
-
self.query_one(WatcherPanel).action_prev_page()
|
|
234
|
-
elif active_tab_id == "worker":
|
|
235
|
-
self.query_one(WorkerPanel).action_prev_page()
|
|
236
|
-
|
|
237
|
-
def action_switch_view(self) -> None:
|
|
238
|
-
"""Switch view in current panel (if supported)."""
|
|
239
|
-
tabbed_content = self.query_one(TabbedContent)
|
|
240
|
-
active_tab_id = tabbed_content.active
|
|
241
|
-
|
|
242
|
-
if active_tab_id == "watcher":
|
|
243
|
-
self.query_one(WatcherPanel).action_switch_view()
|
|
244
|
-
elif active_tab_id == "worker":
|
|
245
|
-
self.query_one(WorkerPanel).action_switch_view()
|
|
246
|
-
|
|
247
197
|
def action_help(self) -> None:
|
|
248
198
|
"""Show help information."""
|
|
249
199
|
from .screens import HelpScreen
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
"""Aline Dashboard Widgets."""
|
|
2
2
|
|
|
3
3
|
from .header import AlineHeader
|
|
4
|
-
from .watcher_panel import WatcherPanel
|
|
5
|
-
from .worker_panel import WorkerPanel
|
|
6
4
|
from .sessions_table import SessionsTable
|
|
7
5
|
from .events_table import EventsTable
|
|
8
6
|
from .config_panel import ConfigPanel
|
|
@@ -12,8 +10,6 @@ from .agents_panel import AgentsPanel
|
|
|
12
10
|
|
|
13
11
|
__all__ = [
|
|
14
12
|
"AlineHeader",
|
|
15
|
-
"WatcherPanel",
|
|
16
|
-
"WorkerPanel",
|
|
17
13
|
"SessionsTable",
|
|
18
14
|
"EventsTable",
|
|
19
15
|
"ConfigPanel",
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
+
import json as _json
|
|
6
7
|
import re
|
|
7
8
|
import shlex
|
|
8
9
|
from pathlib import Path
|
|
@@ -163,6 +164,7 @@ class AgentsPanel(Container, can_focus=True):
|
|
|
163
164
|
super().__init__()
|
|
164
165
|
self._refresh_lock = asyncio.Lock()
|
|
165
166
|
self._agents: list[dict] = []
|
|
167
|
+
self._rendered_fingerprint: str = ""
|
|
166
168
|
self._refresh_worker: Optional[Worker] = None
|
|
167
169
|
self._share_worker: Optional[Worker] = None
|
|
168
170
|
self._sync_worker: Optional[Worker] = None
|
|
@@ -178,7 +180,7 @@ class AgentsPanel(Container, can_focus=True):
|
|
|
178
180
|
|
|
179
181
|
def on_show(self) -> None:
|
|
180
182
|
if self._refresh_timer is None:
|
|
181
|
-
self._refresh_timer = self.set_interval(
|
|
183
|
+
self._refresh_timer = self.set_interval(1.0, self._on_refresh_timer)
|
|
182
184
|
else:
|
|
183
185
|
try:
|
|
184
186
|
self._refresh_timer.resume()
|
|
@@ -305,6 +307,14 @@ class AgentsPanel(Container, can_focus=True):
|
|
|
305
307
|
return agents
|
|
306
308
|
|
|
307
309
|
|
|
310
|
+
@staticmethod
|
|
311
|
+
def _fingerprint(agents: list[dict]) -> str:
|
|
312
|
+
"""Fast serialisation used only for change detection."""
|
|
313
|
+
try:
|
|
314
|
+
return _json.dumps(agents, sort_keys=True, default=str)
|
|
315
|
+
except Exception:
|
|
316
|
+
return ""
|
|
317
|
+
|
|
308
318
|
def on_worker_state_changed(self, event: Worker.StateChanged) -> None:
|
|
309
319
|
# Handle refresh worker
|
|
310
320
|
if self._refresh_worker is not None and event.worker is self._refresh_worker:
|
|
@@ -314,6 +324,10 @@ class AgentsPanel(Container, can_focus=True):
|
|
|
314
324
|
self._agents = self._refresh_worker.result or []
|
|
315
325
|
else:
|
|
316
326
|
return
|
|
327
|
+
fp = self._fingerprint(self._agents)
|
|
328
|
+
if fp == self._rendered_fingerprint:
|
|
329
|
+
return # nothing changed – skip re-render to avoid flicker
|
|
330
|
+
self._rendered_fingerprint = fp
|
|
317
331
|
self.run_worker(
|
|
318
332
|
self._render_agents(), group="agents-render", exclusive=True
|
|
319
333
|
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|