aline-ai 0.6.0__py3-none-any.whl → 0.6.1__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.0
3
+ Version: 0.6.1
4
4
  Summary: Shared AI memory; everyone knows everything in teams
5
5
  Author: Sharemind
6
6
  License: MIT
@@ -1,8 +1,8 @@
1
- aline_ai-0.6.0.dist-info/licenses/LICENSE,sha256=H8wTqV5IF1oHw_HbBtS1PSDU8G_q81yblEIL_JfV8Vo,1077
2
- realign/__init__.py,sha256=a8wH_-YI_n2n6He2CL8jdy4zSUj9pxtT6cz9iNZgp24,1623
3
- realign/auth.py,sha256=63fdy-KsNoLZ9A6X0Mz_v-0tQOXN_1XncXBGBlEoXqE,16030
1
+ aline_ai-0.6.1.dist-info/licenses/LICENSE,sha256=H8wTqV5IF1oHw_HbBtS1PSDU8G_q81yblEIL_JfV8Vo,1077
2
+ realign/__init__.py,sha256=ohwpTX-XJAcf8rW1lcIJqXudQQ72HJvA9d0RsAJhURU,1623
3
+ realign/auth.py,sha256=d_1yvCwluN5iIrdgjtuSKpOYAksDzrzNgntKacLVJrw,16583
4
4
  realign/claude_detector.py,sha256=ZLSJacMo6zzQclXByABKA70UNpstxqIv3fPGqdpA934,2792
5
- realign/cli.py,sha256=2BhDitD5IYUPJRGtfOXv_m0r7ZsiPU9iGtkp2pfA59M,41637
5
+ realign/cli.py,sha256=HIKpLIx7lwHV-Gd3cbdZVaOst784or6asQdAI-DFE80,43543
6
6
  realign/codex_detector.py,sha256=N9ulgMgvTzDfXE4s4vLd6OoS0hT7R6h2bDFFXWa-2hE,4183
7
7
  realign/config.py,sha256=d8HQ6v1xju6cjNGbR7LlfHaMyvFPcMDofhGbepxpQq8,11634
8
8
  realign/context.py,sha256=8hzgNOg-7_eMW22wt7OM5H9IsmMveKXCv0epG7E0G7w,13917
@@ -34,7 +34,7 @@ realign/claude_hooks/user_prompt_submit_hook.py,sha256=WD-UavhBTueN2TPfnZrnPC7DF
34
34
  realign/claude_hooks/user_prompt_submit_hook_installer.py,sha256=2xLF8yZcE7Iwib9gU-xCkA1NWxNH9Nc5CFKPYK7rtXw,5371
35
35
  realign/commands/__init__.py,sha256=sx_ck55oxaoiF4N3LugG0ZXwonUDxeEZ5uHbBKCC7K8,89
36
36
  realign/commands/add.py,sha256=njZgg3paUmOw-sb-sWkXr_eUaf5bD-hBEiRectaphPs,24332
37
- realign/commands/auth.py,sha256=B1FQjaLUKYc4DsGqrpXZ3JPmsttUXhrtxxrKuOUmP4Q,10844
37
+ realign/commands/auth.py,sha256=QrPukpP-ogYEDSwztV0NOYI-HDgn5fPxlCQ1-e2n7gU,11082
38
38
  realign/commands/config.py,sha256=nYnu_h2pk7GODcrzrV04K51D-s7v06FlRXHJ0HJ-gvU,6732
39
39
  realign/commands/context.py,sha256=pM2KfZHVkB-ou4nBhFvKSwnYliLBzwN3zerLyBAbhfE,7095
40
40
  realign/commands/export_shares.py,sha256=g2SxlPEb7FPMarjTZcoZntviZo5JdqEe-M28vggc2-s,136601
@@ -46,8 +46,13 @@ realign/commands/upgrade.py,sha256=L3PLOUIN5qAQTbkfoVtSsIbbzEezA_xjjk9F1GMVfjw,1
46
46
  realign/commands/watcher.py,sha256=rB3x2diC7scqghl7wLAeBNgMj4NLXUKbr0ou1_VVUIU,136292
47
47
  realign/commands/worker.py,sha256=jTu7Pj60nTnn7SsH3oNCNnO6zl4TIFCJVNSC1OoQ_0o,23363
48
48
  realign/dashboard/__init__.py,sha256=QZkHTsGityH8UkF8rmvA3xW7dMXNe0swEWr443qfgCM,128
49
- realign/dashboard/app.py,sha256=jyW6mqmItTy253CPSqInxctkWzkrGEikdy-ikuShQ14,13299
49
+ realign/dashboard/app.py,sha256=l4qbjQfnUJCzX2M0A5vPJ-fTX-Tz2Ykw4_cY0eMC1LE,16029
50
+ realign/dashboard/layout.py,sha256=sZxmFj6QTbkois9MHTvBEMMcnaRVehCDqugdbiFx10k,9072
51
+ realign/dashboard/terminal_backend.py,sha256=MlDfwtqhftyQK6jDNizQGFjAWIo5Bx2TDpSnP3MCZVM,3375
50
52
  realign/dashboard/tmux_manager.py,sha256=Vt_30WNtDg7c_9SEh8xdDtBLJ8kNq6bGSPh5r3VXpg0,26276
53
+ realign/dashboard/backends/__init__.py,sha256=POROX7YKtukYZcLB1pi_kO0sSEpuO3y-hwmF3WIN1Kk,163
54
+ realign/dashboard/backends/iterm2.py,sha256=XYYJT5lrrp4pW_MyEqPZYkRI0qyKUwJlezwMidgnsHc,21390
55
+ realign/dashboard/backends/kitty.py,sha256=5jdkR1f2PwB8a4SnS3EG6uOQ2XU-PB7-cpKBfIJq3hU,12066
51
56
  realign/dashboard/screens/__init__.py,sha256=US6sAmQs5VVkH2tFkH_z0WDT4H8cVhLL-JckfSR1yQY,446
52
57
  realign/dashboard/screens/create_agent.py,sha256=lpcT1zLq_p02codtHTE8KdbEzCEaNLnk1lqU3QLcXCg,10057
53
58
  realign/dashboard/screens/create_event.py,sha256=oiQY1zKpUYnQU-5fQLeuZH9BV5NClE5B5XZIVBYG5A8,5506
@@ -63,7 +68,7 @@ realign/dashboard/widgets/header.py,sha256=0HHCFXX7F3C6HII-WDwOJwWkJrajmKPWmdoMW
63
68
  realign/dashboard/widgets/openable_table.py,sha256=GeJPDEYp0kRHShqvmPMzAePpYXRZHUNqcWNnxqsqxjA,1963
64
69
  realign/dashboard/widgets/search_panel.py,sha256=ZNJDfwDSxUFnCeltYQYsQsPJ6t4HDeNWpENoTOoBdVM,8951
65
70
  realign/dashboard/widgets/sessions_table.py,sha256=GyaWzvt-elKx1iE2YR94CBNPyjqM8B7g0j7zsukiXi0,36517
66
- realign/dashboard/widgets/terminal_panel.py,sha256=uoi3LjgYWyFE6Yr208KC5iKg0QxLcXpN6hCERlI6pBg,29069
71
+ realign/dashboard/widgets/terminal_panel.py,sha256=7VrLN_Wsk98O0rojckrqJ_k5TGM47hP4UqXFvuRTvRs,45097
67
72
  realign/dashboard/widgets/watcher_panel.py,sha256=O_mdDacgc87xA-5KEfta53Ik_Xsk_B2OfwenMOTtGw8,19722
68
73
  realign/dashboard/widgets/worker_panel.py,sha256=F_jKWABuCNmjQgeeuCr4KnFRKdY4CLTNcEXMYwsNaSk,18691
69
74
  realign/db/__init__.py,sha256=65LsNdsq_rkwNC1eg1OAr3HC0ORXtelOh0I8MhNGr-g,3288
@@ -91,8 +96,8 @@ realign/triggers/next_turn_trigger.py,sha256=BpP0PWn4mU1MZd6mv89jWcjs8Jtv0zEWapW
91
96
  realign/triggers/registry.py,sha256=cb-AVLbYB2pqwfWL3q1DQxLv4kOw7g7m-GshTdfFESc,3827
92
97
  realign/triggers/turn_status.py,sha256=wAZEhXDAmDoX5F-ohWfSnZZ0eA6DAJ9svSPiSv_f6sg,6041
93
98
  realign/triggers/turn_summary.py,sha256=f3hEUshgv9skJ9AbfWpoYs417lsv_HK2A_vpPjgryO4,4467
94
- aline_ai-0.6.0.dist-info/METADATA,sha256=oukhmryjZ2KFfmgLAIPBjpr8S4_8pxboUCOAgIWLx4Y,1597
95
- aline_ai-0.6.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
96
- aline_ai-0.6.0.dist-info/entry_points.txt,sha256=TvYELpMoWsUTcQdMV8tBHxCbEf_LbK4sESqK3r8PM6Y,78
97
- aline_ai-0.6.0.dist-info/top_level.txt,sha256=yIL3s2xv9nf1GwD5n71Aq_JEIV4AfzCIDNKBzewuRm4,8
98
- aline_ai-0.6.0.dist-info/RECORD,,
99
+ aline_ai-0.6.1.dist-info/METADATA,sha256=ftCsc3aI1fjTZhCJd8rE_7-teyrN8ARYIyEmDGMtJdg,1597
100
+ aline_ai-0.6.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
101
+ aline_ai-0.6.1.dist-info/entry_points.txt,sha256=TvYELpMoWsUTcQdMV8tBHxCbEf_LbK4sESqK3r8PM6Y,78
102
+ aline_ai-0.6.1.dist-info/top_level.txt,sha256=yIL3s2xv9nf1GwD5n71Aq_JEIV4AfzCIDNKBzewuRm4,8
103
+ aline_ai-0.6.1.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.0"
6
+ __version__ = "0.6.1"
7
7
 
8
8
 
9
9
  def get_realign_dir(project_root: Path) -> Path:
realign/auth.py CHANGED
@@ -44,6 +44,7 @@ logger = setup_logger("realign.auth", "auth.log")
44
44
  # Hardcoded Supabase/backend configuration
45
45
  DEFAULT_AUTH_URL = "https://realign-server.vercel.app"
46
46
  CLI_LOGIN_PATH = "/cli-login"
47
+ CLI_LOGOUT_PATH = "/cli-logout"
47
48
  CLI_VALIDATE_PATH = "/api/auth/cli/validate"
48
49
  CLI_REFRESH_PATH = "/api/auth/cli/refresh"
49
50
 
@@ -394,6 +395,26 @@ def open_login_page(callback_port: Optional[int] = None) -> str:
394
395
  return login_url
395
396
 
396
397
 
398
+ def open_logout_page() -> str:
399
+ """
400
+ Open the web logout page in browser to sign out from web session.
401
+
402
+ Returns:
403
+ The URL that was opened
404
+ """
405
+ config = ReAlignConfig.load()
406
+ backend_url = config.share_backend_url or DEFAULT_AUTH_URL
407
+ logout_url = f"{backend_url}{CLI_LOGOUT_PATH}"
408
+
409
+ try:
410
+ webbrowser.open(logout_url)
411
+ logger.info(f"Opened logout page: {logout_url}")
412
+ except Exception as e:
413
+ logger.warning(f"Failed to open browser: {e}")
414
+
415
+ return logout_url
416
+
417
+
397
418
  # Local callback server for automatic token receipt
398
419
  _received_token: Optional[str] = None
399
420
  _server_error: Optional[str] = None
realign/cli.py CHANGED
@@ -61,13 +61,33 @@ def main(
61
61
  raise typer.Exit(0)
62
62
 
63
63
  # Launch the dashboard when no subcommand is provided
64
- from .dashboard.tmux_manager import bootstrap_dashboard_into_tmux
64
+ import os
65
65
 
66
- bootstrap_dashboard_into_tmux()
66
+ terminal_mode = os.environ.get("ALINE_TERMINAL_MODE", "").strip().lower()
67
+ use_native_terminal = terminal_mode in {"native", "iterm2", "iterm", "kitty"}
68
+
69
+ right_pane_session_id = None
70
+
71
+ if not use_native_terminal:
72
+ # Only bootstrap tmux for tmux mode (default)
73
+ from .dashboard.tmux_manager import bootstrap_dashboard_into_tmux
74
+
75
+ bootstrap_dashboard_into_tmux()
76
+ elif terminal_mode in {"iterm2", "iterm"}:
77
+ # Set up split pane layout for iTerm2
78
+ try:
79
+ from .dashboard.backends.iterm2 import setup_split_pane_layout_sync
80
+
81
+ right_pane_session_id = setup_split_pane_layout_sync()
82
+ if right_pane_session_id:
83
+ # Store in environment for dashboard to pick up
84
+ os.environ["ALINE_ITERM2_RIGHT_PANE"] = right_pane_session_id
85
+ except Exception as e:
86
+ console.print(f"[yellow]Warning: Could not set up split pane: {e}[/yellow]")
67
87
 
68
88
  from .dashboard.app import AlineDashboard
69
89
 
70
- dashboard = AlineDashboard(dev_mode=dev)
90
+ dashboard = AlineDashboard(dev_mode=dev, use_native_terminal=use_native_terminal)
71
91
  dashboard.run()
72
92
 
73
93
 
@@ -1100,7 +1120,6 @@ def dashboard(
1100
1120
  if debug:
1101
1121
  os.environ["REALIGN_LOG_LEVEL"] = "DEBUG"
1102
1122
 
1103
- from .dashboard.tmux_manager import bootstrap_dashboard_into_tmux
1104
1123
  from .logging_config import setup_logger
1105
1124
 
1106
1125
  # Initialize logger before dashboard
@@ -1108,13 +1127,32 @@ def dashboard(
1108
1127
  logger.info(f"Dashboard command invoked (dev={dev}, debug={debug})")
1109
1128
 
1110
1129
  try:
1111
- bootstrap_dashboard_into_tmux()
1130
+ # Check terminal mode
1131
+ terminal_mode = os.environ.get("ALINE_TERMINAL_MODE", "").strip().lower()
1132
+ use_native_terminal = terminal_mode in {"native", "iterm2", "iterm", "kitty"}
1133
+
1134
+ if not use_native_terminal:
1135
+ # Only bootstrap tmux for tmux mode (default)
1136
+ from .dashboard.tmux_manager import bootstrap_dashboard_into_tmux
1137
+
1138
+ bootstrap_dashboard_into_tmux()
1139
+ elif terminal_mode in {"iterm2", "iterm"}:
1140
+ # Set up split pane layout for iTerm2
1141
+ try:
1142
+ from .dashboard.backends.iterm2 import setup_split_pane_layout_sync
1143
+
1144
+ right_pane_session_id = setup_split_pane_layout_sync()
1145
+ if right_pane_session_id:
1146
+ os.environ["ALINE_ITERM2_RIGHT_PANE"] = right_pane_session_id
1147
+ logger.info(f"Set up split pane with right pane: {right_pane_session_id}")
1148
+ except Exception as e:
1149
+ logger.warning(f"Could not set up split pane: {e}")
1112
1150
 
1113
1151
  from .dashboard.app import AlineDashboard
1114
1152
 
1115
1153
  # Use dev flag from this command or inherit from parent context
1116
1154
  dev_mode = dev or (ctx.obj.get("dev", False) if ctx.obj else False)
1117
- dash = AlineDashboard(dev_mode=dev_mode)
1155
+ dash = AlineDashboard(dev_mode=dev_mode, use_native_terminal=use_native_terminal)
1118
1156
  dash.run()
1119
1157
  except Exception as e:
1120
1158
  logger.error(f"Dashboard crashed: {e}\n{traceback.format_exc()}")
realign/commands/auth.py CHANGED
@@ -25,6 +25,7 @@ from ..auth import (
25
25
  save_credentials,
26
26
  clear_credentials,
27
27
  open_login_page,
28
+ open_logout_page,
28
29
  validate_cli_token,
29
30
  find_free_port,
30
31
  start_callback_server,
@@ -207,6 +208,14 @@ def logout_command() -> int:
207
208
  print("Error: Failed to clear credentials", file=sys.stderr)
208
209
  return 1
209
210
 
211
+ # Open browser to sign out from web session
212
+ if console:
213
+ console.print("[dim]Signing out from web session...[/dim]")
214
+ else:
215
+ print("Signing out from web session...")
216
+
217
+ open_logout_page()
218
+
210
219
  if console:
211
220
  console.print(f"[green]Logged out successfully.[/green]")
212
221
  console.print(f"Cleared credentials for: {email}")
realign/dashboard/app.py CHANGED
@@ -1,5 +1,6 @@
1
1
  """Aline Dashboard - Main Application."""
2
2
 
3
+ import os
3
4
  import subprocess
4
5
  import sys
5
6
  import time
@@ -22,6 +23,9 @@ from .widgets import (
22
23
  TerminalPanel,
23
24
  )
24
25
 
26
+ # Environment variable to control terminal mode
27
+ ENV_TERMINAL_MODE = "ALINE_TERMINAL_MODE"
28
+
25
29
  # Set up dashboard logger - logs to ~/.aline/.logs/dashboard.log
26
30
  logger = setup_logger("realign.dashboard", "dashboard.log")
27
31
 
@@ -74,15 +78,31 @@ class AlineDashboard(App):
74
78
 
75
79
  _quit_confirm_window_s: float = 1.2
76
80
 
77
- def __init__(self, dev_mode: bool = False):
81
+ def __init__(self, dev_mode: bool = False, use_native_terminal: bool | None = None):
78
82
  """Initialize the dashboard.
79
83
 
80
84
  Args:
81
85
  dev_mode: If True, shows developer tabs (Watcher, Worker).
86
+ use_native_terminal: If True, use native terminal backend (iTerm2/Kitty).
87
+ If False, use tmux.
88
+ If None (default), auto-detect from ALINE_TERMINAL_MODE env var.
82
89
  """
83
90
  super().__init__()
84
91
  self.dev_mode = dev_mode
85
- logger.info(f"AlineDashboard initialized (dev_mode={dev_mode})")
92
+ self.use_native_terminal = use_native_terminal
93
+ self._native_terminal_mode = self._detect_native_mode()
94
+ logger.info(
95
+ f"AlineDashboard initialized (dev_mode={dev_mode}, "
96
+ f"native_terminal={self._native_terminal_mode})"
97
+ )
98
+
99
+ def _detect_native_mode(self) -> bool:
100
+ """Detect if native terminal mode should be used."""
101
+ if self.use_native_terminal is not None:
102
+ return self.use_native_terminal
103
+
104
+ mode = os.environ.get(ENV_TERMINAL_MODE, "").strip().lower()
105
+ return mode in {"native", "iterm2", "iterm", "kitty"}
86
106
 
87
107
  def compose(self) -> ComposeResult:
88
108
  """Compose the dashboard layout."""
@@ -92,7 +112,7 @@ class AlineDashboard(App):
92
112
  tab_ids = self._tab_ids()
93
113
  with TabbedContent(initial=tab_ids[0] if tab_ids else "terminal"):
94
114
  with TabPane("Agents", id="terminal"):
95
- yield TerminalPanel()
115
+ yield TerminalPanel(use_native_terminal=self.use_native_terminal)
96
116
  if self.dev_mode:
97
117
  with TabPane("Watcher", id="watcher"):
98
118
  yield WatcherPanel()
@@ -125,11 +145,47 @@ class AlineDashboard(App):
125
145
  # Check for system theme changes every 2 seconds
126
146
  self.set_interval(2, self._sync_theme)
127
147
  self._quit_confirm_deadline: float | None = None
148
+
149
+ # Set up side-by-side layout for native terminal mode
150
+ if self._native_terminal_mode:
151
+ self._setup_native_terminal_layout()
152
+
128
153
  logger.info("on_mount() completed successfully")
129
154
  except Exception as e:
130
155
  logger.error(f"on_mount() failed: {e}\n{traceback.format_exc()}")
131
156
  raise
132
157
 
158
+ def _setup_native_terminal_layout(self) -> None:
159
+ """Set up side-by-side layout for Dashboard and native terminal."""
160
+ # Skip if using iTerm2 split pane mode (already set up by CLI)
161
+ if os.environ.get("ALINE_ITERM2_RIGHT_PANE"):
162
+ logger.info("Using iTerm2 split pane mode, skipping window layout")
163
+ return
164
+
165
+ try:
166
+ from .layout import setup_side_by_side_layout
167
+
168
+ # Determine the target terminal app
169
+ mode = os.environ.get(ENV_TERMINAL_MODE, "").strip().lower()
170
+ if mode == "kitty":
171
+ terminal_app = "Kitty"
172
+ else:
173
+ terminal_app = "iTerm2"
174
+
175
+ # Set up side-by-side layout (Dashboard on left, terminal on right)
176
+ success = setup_side_by_side_layout(
177
+ terminal_app=terminal_app,
178
+ dashboard_on_left=True,
179
+ dashboard_width_percent=40, # Dashboard takes 40%, terminal takes 60%
180
+ )
181
+
182
+ if success:
183
+ logger.info(f"Set up side-by-side layout with {terminal_app}")
184
+ else:
185
+ logger.warning("Failed to set up side-by-side layout")
186
+ except Exception as e:
187
+ logger.warning(f"Could not set up native terminal layout: {e}")
188
+
133
189
  def _sync_theme(self) -> None:
134
190
  """Sync app theme with system theme."""
135
191
  target_theme = "textual-dark" if _detect_system_dark_mode() else "textual-light"
@@ -357,11 +413,17 @@ class AlineDashboard(App):
357
413
  self.notify(f"Loaded {what} into {context_id}", title="Load Context", timeout=3)
358
414
 
359
415
 
360
- def run_dashboard() -> None:
361
- """Run the Aline Dashboard."""
416
+ def run_dashboard(use_native_terminal: bool | None = None) -> None:
417
+ """Run the Aline Dashboard.
418
+
419
+ Args:
420
+ use_native_terminal: If True, use native terminal backend (iTerm2/Kitty).
421
+ If False, use tmux.
422
+ If None (default), auto-detect from ALINE_TERMINAL_MODE env var.
423
+ """
362
424
  logger.info("Starting Aline Dashboard")
363
425
  try:
364
- app = AlineDashboard()
426
+ app = AlineDashboard(use_native_terminal=use_native_terminal)
365
427
  app.run()
366
428
  logger.info("Aline Dashboard exited normally")
367
429
  except Exception as e:
@@ -0,0 +1,6 @@
1
+ """Terminal backends for native terminal support."""
2
+
3
+ from .iterm2 import ITermBackend
4
+ from .kitty import KittyBackend
5
+
6
+ __all__ = ["ITermBackend", "KittyBackend"]