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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aline-ai
3
- Version: 0.6.7
3
+ Version: 0.7.0
4
4
  Summary: Shared AI memory; everyone knows everything in teams
5
5
  Author: Sharemind
6
6
  License: MIT
@@ -1,9 +1,9 @@
1
- aline_ai-0.6.7.dist-info/licenses/LICENSE,sha256=H8wTqV5IF1oHw_HbBtS1PSDU8G_q81yblEIL_JfV8Vo,1077
2
- realign/__init__.py,sha256=_XHUZdw-wtPtpgm845liUkFT-VXg86ZiqDK3pdnnh9Q,1623
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=IctmQ0OTb6kLlWRFRQumdhY6-CpcpFtocdc68KiwxvM,37748
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=QrPukpP-ogYEDSwztV0NOYI-HDgn5fPxlCQ1-e2n7gU,11082
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=e257euP0gR9nA0w1susuLkG9tnYQk1IJJdgAICnIYxs,10398
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=33qjCa6WCQ7XojRiStdR73jX2xpKV_RlBqodVDQWkxs,577
73
- realign/dashboard/widgets/agents_panel.py,sha256=TtOX9RlF0CuwRTe1sXoo1xaf7ZykJA-YFmMu0-SKe2g,43299
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.6.7.dist-info/METADATA,sha256=GyI08kzWpN5QDEsgynocnlk8Cp5zSTiKXRltp86xrsM,1597
108
- aline_ai-0.6.7.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
109
- aline_ai-0.6.7.dist-info/entry_points.txt,sha256=TvYELpMoWsUTcQdMV8tBHxCbEf_LbK4sESqK3r8PM6Y,78
110
- aline_ai-0.6.7.dist-info/top_level.txt,sha256=yIL3s2xv9nf1GwD5n71Aq_JEIV4AfzCIDNKBzewuRm4,8
111
- aline_ai-0.6.7.dist-info/RECORD,,
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
@@ -3,7 +3,7 @@
3
3
  import hashlib
4
4
  from pathlib import Path
5
5
 
6
- __version__ = "0.6.7"
6
+ __version__ = "0.7.0"
7
7
 
8
8
 
9
9
  def get_realign_dir(project_root: Path) -> Path:
realign/cli.py CHANGED
@@ -127,7 +127,7 @@ def main(
127
127
 
128
128
  from .dashboard.app import AlineDashboard
129
129
 
130
- dashboard = AlineDashboard(dev_mode=dev, use_native_terminal=use_native_terminal)
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
- # Use dev flag from this command or inherit from parent context
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, dev_mode: bool = False, use_native_terminal: bool | None = None):
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 (dev_mode={dev_mode}, "
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
- tab_ids = self._tab_ids()
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(30.0, self._on_refresh_timer)
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
  )