aline-ai 0.6.5__py3-none-any.whl → 0.6.7__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.5.dist-info → aline_ai-0.6.7.dist-info}/METADATA +1 -1
- {aline_ai-0.6.5.dist-info → aline_ai-0.6.7.dist-info}/RECORD +41 -34
- realign/__init__.py +1 -1
- realign/agent_names.py +79 -0
- realign/claude_hooks/stop_hook.py +3 -0
- realign/claude_hooks/terminal_state.py +43 -1
- realign/claude_hooks/user_prompt_submit_hook.py +3 -0
- realign/cli.py +62 -0
- realign/codex_detector.py +18 -3
- realign/codex_home.py +65 -16
- realign/codex_terminal_linker.py +18 -7
- realign/commands/agent.py +109 -0
- realign/commands/doctor.py +74 -1
- realign/commands/export_shares.py +448 -0
- realign/commands/import_shares.py +203 -1
- realign/commands/search.py +58 -29
- realign/commands/sync_agent.py +347 -0
- realign/dashboard/app.py +9 -9
- realign/dashboard/clipboard.py +54 -0
- realign/dashboard/screens/__init__.py +4 -0
- realign/dashboard/screens/agent_detail.py +333 -0
- realign/dashboard/screens/create_agent_info.py +244 -0
- realign/dashboard/screens/event_detail.py +6 -27
- realign/dashboard/styles/dashboard.tcss +22 -28
- realign/dashboard/tmux_manager.py +36 -10
- realign/dashboard/widgets/__init__.py +2 -2
- realign/dashboard/widgets/agents_panel.py +1248 -0
- realign/dashboard/widgets/events_table.py +4 -27
- realign/dashboard/widgets/sessions_table.py +4 -27
- realign/db/base.py +69 -0
- realign/db/locks.py +4 -0
- realign/db/schema.py +111 -2
- realign/db/sqlite_db.py +360 -2
- realign/events/agent_summarizer.py +157 -0
- realign/events/session_summarizer.py +25 -0
- realign/watcher_core.py +193 -5
- realign/worker_core.py +59 -1
- realign/dashboard/widgets/terminal_panel.py +0 -1653
- {aline_ai-0.6.5.dist-info → aline_ai-0.6.7.dist-info}/WHEEL +0 -0
- {aline_ai-0.6.5.dist-info → aline_ai-0.6.7.dist-info}/entry_points.txt +0 -0
- {aline_ai-0.6.5.dist-info → aline_ai-0.6.7.dist-info}/licenses/LICENSE +0 -0
- {aline_ai-0.6.5.dist-info → aline_ai-0.6.7.dist-info}/top_level.txt +0 -0
|
@@ -3,9 +3,6 @@
|
|
|
3
3
|
import contextlib
|
|
4
4
|
import io
|
|
5
5
|
import json
|
|
6
|
-
import os
|
|
7
|
-
import shutil
|
|
8
|
-
import subprocess
|
|
9
6
|
import traceback
|
|
10
7
|
from datetime import datetime
|
|
11
8
|
from typing import Optional, Set
|
|
@@ -20,6 +17,7 @@ from textual.worker import Worker, WorkerState
|
|
|
20
17
|
from textual.widgets import Button, DataTable, Static
|
|
21
18
|
|
|
22
19
|
from ...logging_config import setup_logger
|
|
20
|
+
from ..clipboard import copy_text
|
|
23
21
|
from .openable_table import OpenableDataTable
|
|
24
22
|
|
|
25
23
|
logger = setup_logger("realign.dashboard.events", "dashboard.log")
|
|
@@ -613,33 +611,12 @@ class EventsTable(Container):
|
|
|
613
611
|
|
|
614
612
|
# Build copy text
|
|
615
613
|
if slack_message:
|
|
616
|
-
|
|
614
|
+
text_to_copy = str(slack_message) + "\n\n" + str(share_link)
|
|
617
615
|
else:
|
|
618
|
-
|
|
616
|
+
text_to_copy = str(share_link)
|
|
619
617
|
|
|
620
618
|
# Copy to clipboard
|
|
621
|
-
copied =
|
|
622
|
-
if os.environ.get("TMUX") and shutil.which("pbcopy"):
|
|
623
|
-
try:
|
|
624
|
-
copied = (
|
|
625
|
-
subprocess.run(
|
|
626
|
-
["pbcopy"],
|
|
627
|
-
input=copy_text,
|
|
628
|
-
text=True,
|
|
629
|
-
capture_output=False,
|
|
630
|
-
check=False,
|
|
631
|
-
).returncode
|
|
632
|
-
== 0
|
|
633
|
-
)
|
|
634
|
-
except Exception:
|
|
635
|
-
copied = False
|
|
636
|
-
|
|
637
|
-
if not copied:
|
|
638
|
-
try:
|
|
639
|
-
self.app.copy_to_clipboard(copy_text)
|
|
640
|
-
copied = True
|
|
641
|
-
except Exception:
|
|
642
|
-
copied = False
|
|
619
|
+
copied = copy_text(self.app, text_to_copy)
|
|
643
620
|
|
|
644
621
|
suffix = " (copied to clipboard)" if copied else ""
|
|
645
622
|
self.app.notify(f"Share link created{suffix}", title="Share", timeout=4)
|
|
@@ -3,9 +3,6 @@
|
|
|
3
3
|
import contextlib
|
|
4
4
|
import io
|
|
5
5
|
import json
|
|
6
|
-
import os
|
|
7
|
-
import shutil
|
|
8
|
-
import subprocess
|
|
9
6
|
import traceback
|
|
10
7
|
from datetime import datetime
|
|
11
8
|
from pathlib import Path
|
|
@@ -20,6 +17,7 @@ from textual.worker import Worker, WorkerState
|
|
|
20
17
|
from textual.widgets import Button, DataTable, Select, Static
|
|
21
18
|
|
|
22
19
|
from ...logging_config import setup_logger
|
|
20
|
+
from ..clipboard import copy_text
|
|
23
21
|
from .openable_table import OpenableDataTable
|
|
24
22
|
|
|
25
23
|
logger = setup_logger("realign.dashboard.sessions", "dashboard.log")
|
|
@@ -685,33 +683,12 @@ class SessionsTable(Container):
|
|
|
685
683
|
|
|
686
684
|
# Build copy text
|
|
687
685
|
if slack_message:
|
|
688
|
-
|
|
686
|
+
text_to_copy = str(slack_message) + "\n\n" + str(share_link)
|
|
689
687
|
else:
|
|
690
|
-
|
|
688
|
+
text_to_copy = str(share_link)
|
|
691
689
|
|
|
692
690
|
# Copy to clipboard
|
|
693
|
-
copied =
|
|
694
|
-
if os.environ.get("TMUX") and shutil.which("pbcopy"):
|
|
695
|
-
try:
|
|
696
|
-
copied = (
|
|
697
|
-
subprocess.run(
|
|
698
|
-
["pbcopy"],
|
|
699
|
-
input=copy_text,
|
|
700
|
-
text=True,
|
|
701
|
-
capture_output=False,
|
|
702
|
-
check=False,
|
|
703
|
-
).returncode
|
|
704
|
-
== 0
|
|
705
|
-
)
|
|
706
|
-
except Exception:
|
|
707
|
-
copied = False
|
|
708
|
-
|
|
709
|
-
if not copied:
|
|
710
|
-
try:
|
|
711
|
-
self.app.copy_to_clipboard(copy_text)
|
|
712
|
-
copied = True
|
|
713
|
-
except Exception:
|
|
714
|
-
copied = False
|
|
691
|
+
copied = copy_text(self.app, text_to_copy)
|
|
715
692
|
|
|
716
693
|
suffix = " (copied to clipboard)" if copied else ""
|
|
717
694
|
self.app.notify(f"Share link created{suffix}", title="Share", timeout=4)
|
realign/db/base.py
CHANGED
|
@@ -57,6 +57,8 @@ class SessionRecord:
|
|
|
57
57
|
total_turns: Optional[int] = None
|
|
58
58
|
# V12: file mtime when total_turns was cached (for validation)
|
|
59
59
|
total_turns_mtime: Optional[float] = None
|
|
60
|
+
# V19: agent association
|
|
61
|
+
agent_id: Optional[str] = None
|
|
60
62
|
|
|
61
63
|
|
|
62
64
|
@dataclass
|
|
@@ -125,6 +127,39 @@ class AgentRecord:
|
|
|
125
127
|
created_by: Optional[str] = None # Creator UID
|
|
126
128
|
|
|
127
129
|
|
|
130
|
+
@dataclass
|
|
131
|
+
class AgentInfoRecord:
|
|
132
|
+
"""Agent profile/identity data (V20, V22: sync fields)."""
|
|
133
|
+
|
|
134
|
+
id: str
|
|
135
|
+
name: str
|
|
136
|
+
created_at: datetime
|
|
137
|
+
updated_at: datetime
|
|
138
|
+
description: Optional[str] = ""
|
|
139
|
+
visibility: str = "visible"
|
|
140
|
+
# V22: sync metadata
|
|
141
|
+
share_id: Optional[str] = None
|
|
142
|
+
share_url: Optional[str] = None
|
|
143
|
+
share_admin_token: Optional[str] = None
|
|
144
|
+
share_contributor_token: Optional[str] = None
|
|
145
|
+
share_expiry_at: Optional[str] = None
|
|
146
|
+
last_synced_at: Optional[str] = None
|
|
147
|
+
sync_version: int = 0
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@dataclass
|
|
151
|
+
class WindowLinkRecord:
|
|
152
|
+
"""Represents a terminal/session association (V23)."""
|
|
153
|
+
|
|
154
|
+
terminal_id: str
|
|
155
|
+
agent_id: Optional[str] = None
|
|
156
|
+
session_id: Optional[str] = None
|
|
157
|
+
provider: Optional[str] = None
|
|
158
|
+
source: Optional[str] = None
|
|
159
|
+
ts: Optional[float] = None
|
|
160
|
+
created_at: Optional[datetime] = None
|
|
161
|
+
|
|
162
|
+
|
|
128
163
|
@dataclass
|
|
129
164
|
class AgentContextRecord:
|
|
130
165
|
"""Represents a context entry (V15: replaces load.json)."""
|
|
@@ -214,6 +249,27 @@ class DatabaseInterface(ABC):
|
|
|
214
249
|
"""Backfill total_turns for all sessions from turns table (V10 migration)."""
|
|
215
250
|
pass
|
|
216
251
|
|
|
252
|
+
@abstractmethod
|
|
253
|
+
def insert_window_link(
|
|
254
|
+
self,
|
|
255
|
+
*,
|
|
256
|
+
terminal_id: str,
|
|
257
|
+
agent_id: Optional[str],
|
|
258
|
+
session_id: Optional[str],
|
|
259
|
+
provider: Optional[str],
|
|
260
|
+
source: Optional[str],
|
|
261
|
+
ts: Optional[float] = None,
|
|
262
|
+
) -> None:
|
|
263
|
+
"""Insert a window link record (V23)."""
|
|
264
|
+
pass
|
|
265
|
+
|
|
266
|
+
@abstractmethod
|
|
267
|
+
def list_latest_window_links(
|
|
268
|
+
self, *, agent_id: Optional[str] = None, limit: int = 1000
|
|
269
|
+
) -> List[WindowLinkRecord]:
|
|
270
|
+
"""List latest window link per terminal (V23)."""
|
|
271
|
+
pass
|
|
272
|
+
|
|
217
273
|
@abstractmethod
|
|
218
274
|
def list_sessions(
|
|
219
275
|
self, limit: int = 100, workspace_path: Optional[str] = None
|
|
@@ -250,6 +306,19 @@ class DatabaseInterface(ABC):
|
|
|
250
306
|
"""
|
|
251
307
|
pass
|
|
252
308
|
|
|
309
|
+
@abstractmethod
|
|
310
|
+
def get_sessions_by_agent_id(self, agent_id: str, limit: int = 1000) -> List[SessionRecord]:
|
|
311
|
+
"""Get all sessions linked to an agent.
|
|
312
|
+
|
|
313
|
+
Args:
|
|
314
|
+
agent_id: The agent_info ID
|
|
315
|
+
limit: Maximum number of sessions to return
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
List of SessionRecord objects for this agent
|
|
319
|
+
"""
|
|
320
|
+
pass
|
|
321
|
+
|
|
253
322
|
@abstractmethod
|
|
254
323
|
def get_turn_by_hash(self, session_id: str, content_hash: str) -> Optional[TurnRecord]:
|
|
255
324
|
"""Check if a turn with this content hash already exists in the session."""
|
realign/db/locks.py
CHANGED
|
@@ -31,6 +31,10 @@ def lock_key_for_event_summary(event_id: str) -> str:
|
|
|
31
31
|
return f"event_summary:{event_id}"
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
def lock_key_for_agent_description(agent_id: str) -> str:
|
|
35
|
+
return f"agent_description:{agent_id}"
|
|
36
|
+
|
|
37
|
+
|
|
34
38
|
@contextmanager
|
|
35
39
|
def lease_lock(
|
|
36
40
|
db: DatabaseInterface,
|
realign/db/schema.py
CHANGED
|
@@ -76,9 +76,18 @@ Schema V18: UID refactor - created_by/shared_by with users table.
|
|
|
76
76
|
- turns: drop uid and user_name (inherit from session)
|
|
77
77
|
- agents: uid -> created_by, drop user_name
|
|
78
78
|
- Update indexes accordingly
|
|
79
|
+
|
|
80
|
+
Schema V19: Agent association for sessions.
|
|
81
|
+
- sessions.agent_id: Logical agent entity association
|
|
82
|
+
|
|
83
|
+
Schema V20: Agent identity/profile table.
|
|
84
|
+
- agent_info table: name, description for agent profiles
|
|
85
|
+
|
|
86
|
+
Schema V23: WindowLink mapping for terminal/session association.
|
|
87
|
+
- windowlink table: terminal_id/agent_id/session_id with timestamp
|
|
79
88
|
"""
|
|
80
89
|
|
|
81
|
-
SCHEMA_VERSION =
|
|
90
|
+
SCHEMA_VERSION = 23
|
|
82
91
|
|
|
83
92
|
FTS_EVENTS_SCRIPTS = [
|
|
84
93
|
# Full Text Search for Events
|
|
@@ -150,7 +159,8 @@ INIT_SCRIPTS = [
|
|
|
150
159
|
created_by TEXT, -- V18: Creator UID (FK to users.uid)
|
|
151
160
|
shared_by TEXT, -- V18: Sharer UID (who imported this)
|
|
152
161
|
total_turns INTEGER DEFAULT 0, -- V10: Cached total turn count (avoids reading files)
|
|
153
|
-
total_turns_mtime REAL
|
|
162
|
+
total_turns_mtime REAL, -- V12: File mtime when total_turns was cached (for validation)
|
|
163
|
+
agent_id TEXT -- V19: Logical agent association
|
|
154
164
|
);
|
|
155
165
|
""",
|
|
156
166
|
# Turns table (corresponds to git commits, V18: uid/user_name removed)
|
|
@@ -222,6 +232,7 @@ INIT_SCRIPTS = [
|
|
|
222
232
|
"CREATE INDEX IF NOT EXISTS idx_sessions_activity ON sessions(last_activity_at DESC);",
|
|
223
233
|
"CREATE INDEX IF NOT EXISTS idx_sessions_type ON sessions(session_type);",
|
|
224
234
|
"CREATE INDEX IF NOT EXISTS idx_sessions_created_by ON sessions(created_by);", # V18
|
|
235
|
+
"CREATE INDEX IF NOT EXISTS idx_sessions_agent_id ON sessions(agent_id);", # V19
|
|
225
236
|
"CREATE INDEX IF NOT EXISTS idx_turns_session ON turns(session_id);",
|
|
226
237
|
"CREATE INDEX IF NOT EXISTS idx_turns_timestamp ON turns(timestamp DESC);",
|
|
227
238
|
"CREATE INDEX IF NOT EXISTS idx_turns_hash ON turns(content_hash);",
|
|
@@ -335,6 +346,24 @@ INIT_SCRIPTS = [
|
|
|
335
346
|
updated_at TEXT DEFAULT (datetime('now'))
|
|
336
347
|
);
|
|
337
348
|
""",
|
|
349
|
+
# Agent identity/profile table (V20, V22: sync columns)
|
|
350
|
+
"""
|
|
351
|
+
CREATE TABLE IF NOT EXISTS agent_info (
|
|
352
|
+
id TEXT PRIMARY KEY,
|
|
353
|
+
name TEXT NOT NULL,
|
|
354
|
+
description TEXT DEFAULT '',
|
|
355
|
+
visibility TEXT NOT NULL DEFAULT 'visible',
|
|
356
|
+
share_id TEXT,
|
|
357
|
+
share_url TEXT,
|
|
358
|
+
share_admin_token TEXT,
|
|
359
|
+
share_contributor_token TEXT,
|
|
360
|
+
share_expiry_at TEXT,
|
|
361
|
+
last_synced_at TEXT,
|
|
362
|
+
sync_version INTEGER DEFAULT 0,
|
|
363
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
364
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
365
|
+
);
|
|
366
|
+
""",
|
|
338
367
|
*FTS_EVENTS_SCRIPTS,
|
|
339
368
|
]
|
|
340
369
|
|
|
@@ -386,6 +415,21 @@ MIGRATION_V1_TO_V2 = [
|
|
|
386
415
|
"""
|
|
387
416
|
CREATE INDEX IF NOT EXISTS idx_sessions_type ON sessions(session_type);
|
|
388
417
|
""",
|
|
418
|
+
# WindowLink table (V23)
|
|
419
|
+
"""
|
|
420
|
+
CREATE TABLE IF NOT EXISTS windowlink (
|
|
421
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
422
|
+
terminal_id TEXT NOT NULL,
|
|
423
|
+
agent_id TEXT,
|
|
424
|
+
session_id TEXT,
|
|
425
|
+
provider TEXT,
|
|
426
|
+
source TEXT,
|
|
427
|
+
ts REAL,
|
|
428
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
429
|
+
);
|
|
430
|
+
""",
|
|
431
|
+
"CREATE INDEX IF NOT EXISTS idx_windowlink_terminal_ts ON windowlink(terminal_id, ts DESC);",
|
|
432
|
+
"CREATE INDEX IF NOT EXISTS idx_windowlink_agent_ts ON windowlink(agent_id, ts DESC);",
|
|
389
433
|
]
|
|
390
434
|
|
|
391
435
|
# Migration scripts from V2 to V3
|
|
@@ -652,6 +696,56 @@ MIGRATION_V17_TO_V18 = [
|
|
|
652
696
|
]
|
|
653
697
|
|
|
654
698
|
|
|
699
|
+
MIGRATION_V18_TO_V19 = [
|
|
700
|
+
"ALTER TABLE sessions ADD COLUMN agent_id TEXT;",
|
|
701
|
+
"CREATE INDEX IF NOT EXISTS idx_sessions_agent_id ON sessions(agent_id);",
|
|
702
|
+
]
|
|
703
|
+
|
|
704
|
+
MIGRATION_V19_TO_V20 = [
|
|
705
|
+
"""
|
|
706
|
+
CREATE TABLE IF NOT EXISTS agent_info (
|
|
707
|
+
id TEXT PRIMARY KEY,
|
|
708
|
+
name TEXT NOT NULL,
|
|
709
|
+
description TEXT DEFAULT '',
|
|
710
|
+
visibility TEXT NOT NULL DEFAULT 'visible',
|
|
711
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
712
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
713
|
+
);
|
|
714
|
+
""",
|
|
715
|
+
]
|
|
716
|
+
|
|
717
|
+
MIGRATION_V20_TO_V21 = [
|
|
718
|
+
"ALTER TABLE agent_info ADD COLUMN visibility TEXT NOT NULL DEFAULT 'visible';",
|
|
719
|
+
]
|
|
720
|
+
|
|
721
|
+
MIGRATION_V21_TO_V22 = [
|
|
722
|
+
"ALTER TABLE agent_info ADD COLUMN share_id TEXT;",
|
|
723
|
+
"ALTER TABLE agent_info ADD COLUMN share_url TEXT;",
|
|
724
|
+
"ALTER TABLE agent_info ADD COLUMN share_admin_token TEXT;",
|
|
725
|
+
"ALTER TABLE agent_info ADD COLUMN share_contributor_token TEXT;",
|
|
726
|
+
"ALTER TABLE agent_info ADD COLUMN share_expiry_at TEXT;",
|
|
727
|
+
"ALTER TABLE agent_info ADD COLUMN last_synced_at TEXT;",
|
|
728
|
+
"ALTER TABLE agent_info ADD COLUMN sync_version INTEGER DEFAULT 0;",
|
|
729
|
+
]
|
|
730
|
+
|
|
731
|
+
MIGRATION_V22_TO_V23 = [
|
|
732
|
+
"""
|
|
733
|
+
CREATE TABLE IF NOT EXISTS windowlink (
|
|
734
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
735
|
+
terminal_id TEXT NOT NULL,
|
|
736
|
+
agent_id TEXT,
|
|
737
|
+
session_id TEXT,
|
|
738
|
+
provider TEXT,
|
|
739
|
+
source TEXT,
|
|
740
|
+
ts REAL,
|
|
741
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
742
|
+
);
|
|
743
|
+
""",
|
|
744
|
+
"CREATE INDEX IF NOT EXISTS idx_windowlink_terminal_ts ON windowlink(terminal_id, ts DESC);",
|
|
745
|
+
"CREATE INDEX IF NOT EXISTS idx_windowlink_agent_ts ON windowlink(agent_id, ts DESC);",
|
|
746
|
+
]
|
|
747
|
+
|
|
748
|
+
|
|
655
749
|
def get_migration_scripts(from_version: int, to_version: int) -> list:
|
|
656
750
|
"""Get migration scripts for upgrading between versions."""
|
|
657
751
|
scripts = []
|
|
@@ -716,4 +810,19 @@ def get_migration_scripts(from_version: int, to_version: int) -> list:
|
|
|
716
810
|
if from_version < 18 and to_version >= 18:
|
|
717
811
|
scripts.extend(MIGRATION_V17_TO_V18)
|
|
718
812
|
|
|
813
|
+
if from_version < 19 and to_version >= 19:
|
|
814
|
+
scripts.extend(MIGRATION_V18_TO_V19)
|
|
815
|
+
|
|
816
|
+
if from_version < 20 and to_version >= 20:
|
|
817
|
+
scripts.extend(MIGRATION_V19_TO_V20)
|
|
818
|
+
|
|
819
|
+
if from_version < 21 and to_version >= 21:
|
|
820
|
+
scripts.extend(MIGRATION_V20_TO_V21)
|
|
821
|
+
|
|
822
|
+
if from_version < 22 and to_version >= 22:
|
|
823
|
+
scripts.extend(MIGRATION_V21_TO_V22)
|
|
824
|
+
|
|
825
|
+
if from_version < 23 and to_version >= 23:
|
|
826
|
+
scripts.extend(MIGRATION_V22_TO_V23)
|
|
827
|
+
|
|
719
828
|
return scripts
|