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
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
aline_ai-0.6.
|
|
2
|
-
realign/__init__.py,sha256=
|
|
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
|
|
3
|
+
realign/agent_names.py,sha256=H4oVJMkqg1ZYCk58vD_Jh9apaAHSFJRswa-C9SPdJxc,1171
|
|
3
4
|
realign/auth.py,sha256=d_1yvCwluN5iIrdgjtuSKpOYAksDzrzNgntKacLVJrw,16583
|
|
4
5
|
realign/claude_detector.py,sha256=ZLSJacMo6zzQclXByABKA70UNpstxqIv3fPGqdpA934,2792
|
|
5
|
-
realign/cli.py,sha256=
|
|
6
|
-
realign/codex_detector.py,sha256=
|
|
7
|
-
realign/codex_home.py,sha256=
|
|
8
|
-
realign/codex_terminal_linker.py,sha256=
|
|
6
|
+
realign/cli.py,sha256=IctmQ0OTb6kLlWRFRQumdhY6-CpcpFtocdc68KiwxvM,37748
|
|
7
|
+
realign/codex_detector.py,sha256=WGIClvlrFVCqJ5vR9DrKVsp1eJhOShvcaXibTHb0Nfc,6304
|
|
8
|
+
realign/codex_home.py,sha256=ljkW8uCfQD4cisEJtPNQmIgaR0yEfWSyHwoVQFY-6p4,4374
|
|
9
|
+
realign/codex_terminal_linker.py,sha256=L2Ha4drlZ7Sbq2jzXyxczOdUY3S5fu1gJqoI5WN9CKk,6211
|
|
9
10
|
realign/config.py,sha256=Znfs43AjiK90LGWnArDPWyrE859sdZQAPIb0KAcU3Ig,9252
|
|
10
11
|
realign/context.py,sha256=8hzgNOg-7_eMW22wt7OM5H9IsmMveKXCv0epG7E0G7w,13917
|
|
11
12
|
realign/file_lock.py,sha256=kLNm1Rra4TCrTMyPM5fwjVascq-CUz2Bzh9HHKtCKOE,3444
|
|
@@ -15,9 +16,9 @@ realign/logging_config.py,sha256=LCAigKFhTj86PSJm4-kUl3Ag9h_GENh3x2iPnMv7qUI,487
|
|
|
15
16
|
realign/mcp_server.py,sha256=LWiQ2qukYoNLsoV2ID2f0vF9jkJlBvB587HpM5jymgE,10193
|
|
16
17
|
realign/mcp_watcher.py,sha256=aK4jWStv7CoCroS4tXFHgZ_y_-q4QDjrpWgm4DxcEj4,1260
|
|
17
18
|
realign/redactor.py,sha256=Zsoi5HfYak2yPmck20JArhm-1cPSB78IdkBJiNVXfrc,17096
|
|
18
|
-
realign/watcher_core.py,sha256=
|
|
19
|
+
realign/watcher_core.py,sha256=XOJarc_jjlf51Gj8ytcdEeaDUkVIq3Ow0bMbFHbKfAM,116690
|
|
19
20
|
realign/watcher_daemon.py,sha256=OHUQ9P1LlagKJHfrf6uRnzO-zDtBRXIxt8ydMFHf5S8,3475
|
|
20
|
-
realign/worker_core.py,sha256=
|
|
21
|
+
realign/worker_core.py,sha256=IXDFvkmeboOUvWyNJ3iZ7xlfxAulPnmFlAtuuJSdgRo,12362
|
|
21
22
|
realign/worker_daemon.py,sha256=X7Xyjw_u6m6KG4E84nx0HpDFw4cWMv8ja1G8btc9PiM,3957
|
|
22
23
|
realign/adapters/__init__.py,sha256=alkJr7DRn_CrJecSJRjRJOHHnkz9EnZ5TnsU8n1Bb0k,719
|
|
23
24
|
realign/adapters/base.py,sha256=2IdAZKGjg5gPB3YLf_8r3V4XAdbK7fHpj06GjjsYEFY,7409
|
|
@@ -28,62 +29,68 @@ realign/adapters/registry.py,sha256=yM6nf9nGTJ1vaK2Uixp-VacseK7PmxZkCdKedmWI8MA,
|
|
|
28
29
|
realign/claude_hooks/__init__.py,sha256=MT9c8TWjLO23xDCM-uBBMy_mOThNd7O-AgN_Khn30qs,594
|
|
29
30
|
realign/claude_hooks/permission_request_hook.py,sha256=jMN7UtL6bMqHObUCP5A5ysvFrooDEcd9KxtmF2-3nCw,6448
|
|
30
31
|
realign/claude_hooks/permission_request_hook_installer.py,sha256=_8Wr_L5MES7iGukJzcaj4bqR0BH8kFL44U_X4iKtw2Y,7791
|
|
31
|
-
realign/claude_hooks/stop_hook.py,sha256=
|
|
32
|
+
realign/claude_hooks/stop_hook.py,sha256=Bzf6CjHQ-0q61SrDrpIvcwt_BmDO1FE-f8cws_aA-Is,13582
|
|
32
33
|
realign/claude_hooks/stop_hook_installer.py,sha256=uyqKOqpix7CQP64ERBvvh7viSPp_wx_JVGNAX18rKh0,7228
|
|
33
|
-
realign/claude_hooks/terminal_state.py,sha256=
|
|
34
|
-
realign/claude_hooks/user_prompt_submit_hook.py,sha256=
|
|
34
|
+
realign/claude_hooks/terminal_state.py,sha256=2ygTbVnh2b59vRLuN-TyWcXR94NKFlaVwOhS3ipqn58,6647
|
|
35
|
+
realign/claude_hooks/user_prompt_submit_hook.py,sha256=8e0zNonT95TH2uuISYp3am_RD7c84Ghh1WRPgs023DI,10625
|
|
35
36
|
realign/claude_hooks/user_prompt_submit_hook_installer.py,sha256=2xLF8yZcE7Iwib9gU-xCkA1NWxNH9Nc5CFKPYK7rtXw,5371
|
|
36
37
|
realign/commands/__init__.py,sha256=WVaVT1orM2Z0PYaG3X6tkKb_t2v3n_3siCadh1qd_QA,107
|
|
37
38
|
realign/commands/add.py,sha256=_Xzt9P15mwndA3JvBBVrki8tn9Cc0UP6SiLwM4RS8Nc,27232
|
|
39
|
+
realign/commands/agent.py,sha256=3CS48bMn7tkdDWKRrfg7CYbhcJK4Pz40YjYMvwD7c2w,3173
|
|
38
40
|
realign/commands/auth.py,sha256=QrPukpP-ogYEDSwztV0NOYI-HDgn5fPxlCQ1-e2n7gU,11082
|
|
39
41
|
realign/commands/config.py,sha256=nYnu_h2pk7GODcrzrV04K51D-s7v06FlRXHJ0HJ-gvU,6732
|
|
40
42
|
realign/commands/context.py,sha256=pM2KfZHVkB-ou4nBhFvKSwnYliLBzwN3zerLyBAbhfE,7095
|
|
41
|
-
realign/commands/doctor.py,sha256=
|
|
42
|
-
realign/commands/export_shares.py,sha256=
|
|
43
|
-
realign/commands/import_shares.py,sha256=
|
|
43
|
+
realign/commands/doctor.py,sha256=0c1TZuA_cw1CSU0yKMVRU-18uTxdqjXKJ8lP2CTTNSQ,20656
|
|
44
|
+
realign/commands/export_shares.py,sha256=b8dpVBx2HkbHVk9pSFXnErlAr0umciAOPpuxvTJyOBI,148467
|
|
45
|
+
realign/commands/import_shares.py,sha256=qAH007WCQ6bwWP09MEJVmgJlRC8c-QicB2HYvMBqyRM,32966
|
|
44
46
|
realign/commands/init.py,sha256=6rBr1LVIrQLbUH_UvoDhkF1qXmMh2xkjNWCYAUz5Tho,35274
|
|
45
47
|
realign/commands/restore.py,sha256=s2BxQZHxQw9r12NzRVsK20KlGafy5AIoSjWMo5PcnHY,11173
|
|
46
|
-
realign/commands/search.py,sha256=
|
|
48
|
+
realign/commands/search.py,sha256=QlUDzRDD6ebq21LTtLe5-OZM62iwDrDqfbnXbuxfklU,27516
|
|
49
|
+
realign/commands/sync_agent.py,sha256=XRcHN00TjfzGwTw3O_OXqb9Yj0lMFfDX0S7oizVpS6E,12454
|
|
47
50
|
realign/commands/upgrade.py,sha256=L3PLOUIN5qAQTbkfoVtSsIbbzEezA_xjjk9F1GMVfjw,12781
|
|
48
51
|
realign/commands/watcher.py,sha256=4WTThIgr-Z5guKh_JqGDcPmerr97XiHrVaaijmckHsA,134350
|
|
49
52
|
realign/commands/worker.py,sha256=jTu7Pj60nTnn7SsH3oNCNnO6zl4TIFCJVNSC1OoQ_0o,23363
|
|
50
53
|
realign/dashboard/__init__.py,sha256=QZkHTsGityH8UkF8rmvA3xW7dMXNe0swEWr443qfgCM,128
|
|
51
|
-
realign/dashboard/app.py,sha256=
|
|
54
|
+
realign/dashboard/app.py,sha256=e257euP0gR9nA0w1susuLkG9tnYQk1IJJdgAICnIYxs,10398
|
|
55
|
+
realign/dashboard/clipboard.py,sha256=81frq83E_urqLkwuCvtl0hiTEjavtdQn8kCi72jJWcs,1207
|
|
52
56
|
realign/dashboard/layout.py,sha256=sZxmFj6QTbkois9MHTvBEMMcnaRVehCDqugdbiFx10k,9072
|
|
53
57
|
realign/dashboard/terminal_backend.py,sha256=MlDfwtqhftyQK6jDNizQGFjAWIo5Bx2TDpSnP3MCZVM,3375
|
|
54
|
-
realign/dashboard/tmux_manager.py,sha256=
|
|
58
|
+
realign/dashboard/tmux_manager.py,sha256=sS6fo7UVPHWxYm1RYtLDPmwsagFh5RO6TRwYd1CuHaI,34581
|
|
55
59
|
realign/dashboard/backends/__init__.py,sha256=POROX7YKtukYZcLB1pi_kO0sSEpuO3y-hwmF3WIN1Kk,163
|
|
56
60
|
realign/dashboard/backends/iterm2.py,sha256=XYYJT5lrrp4pW_MyEqPZYkRI0qyKUwJlezwMidgnsHc,21390
|
|
57
61
|
realign/dashboard/backends/kitty.py,sha256=5jdkR1f2PwB8a4SnS3EG6uOQ2XU-PB7-cpKBfIJq3hU,12066
|
|
58
|
-
realign/dashboard/screens/__init__.py,sha256=
|
|
62
|
+
realign/dashboard/screens/__init__.py,sha256=MiefFamCYRrzTwQXiCUdybaJaFxlK5XKtLHaSQmqDv0,597
|
|
63
|
+
realign/dashboard/screens/agent_detail.py,sha256=N-iUC4434C91OcDu4dkQaxS_NXQ5Yl5sqNBb2mTmoBw,10490
|
|
59
64
|
realign/dashboard/screens/create_agent.py,sha256=06uiQYvz-Xvn4Xm689o3tdhzb2HQ0gdzAA1WHVEwziM,11706
|
|
65
|
+
realign/dashboard/screens/create_agent_info.py,sha256=K2Rbp4zHVdanPT3Fp82We4qlSAM-0IBZXPLuQuevuME,7838
|
|
60
66
|
realign/dashboard/screens/create_event.py,sha256=oiQY1zKpUYnQU-5fQLeuZH9BV5NClE5B5XZIVBYG5A8,5506
|
|
61
|
-
realign/dashboard/screens/event_detail.py,sha256
|
|
67
|
+
realign/dashboard/screens/event_detail.py,sha256=-pqt3NBoeTXGJKtbndZy-msklwXTeNWMS4H12oMG5ks,20175
|
|
62
68
|
realign/dashboard/screens/help_screen.py,sha256=Icrcvbgyz49R2tBiu8vBZ4CLm6iYclv_-FTa2pCFRRQ,3398
|
|
63
69
|
realign/dashboard/screens/session_detail.py,sha256=TBkHqSHyMxsLB2QdZq9m1EoiH8oRVDbPrjt-a8I9sHs,9561
|
|
64
70
|
realign/dashboard/screens/share_import.py,sha256=hl2x0yGVycsoUI76AmdZTAV-br3Q6191g5xHHrZ8hOA,6318
|
|
65
|
-
realign/dashboard/styles/dashboard.tcss,sha256=
|
|
66
|
-
realign/dashboard/widgets/__init__.py,sha256=
|
|
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
|
|
67
74
|
realign/dashboard/widgets/config_panel.py,sha256=eRJRuqImQ8eJIKCEj4O8EvYxI-ht_anrcYbT5JskWyU,15972
|
|
68
|
-
realign/dashboard/widgets/events_table.py,sha256=
|
|
75
|
+
realign/dashboard/widgets/events_table.py,sha256=0cMvE0KdZFBZyvywv7vlt005qsR0aLQnQiMf3ZzK7RY,30218
|
|
69
76
|
realign/dashboard/widgets/header.py,sha256=0HHCFXX7F3C6HII-WDwOJwWkJrajmKPWmdoMWyOkn9E,1587
|
|
70
77
|
realign/dashboard/widgets/openable_table.py,sha256=GeJPDEYp0kRHShqvmPMzAePpYXRZHUNqcWNnxqsqxjA,1963
|
|
71
78
|
realign/dashboard/widgets/search_panel.py,sha256=ZNJDfwDSxUFnCeltYQYsQsPJ6t4HDeNWpENoTOoBdVM,8951
|
|
72
|
-
realign/dashboard/widgets/sessions_table.py,sha256=
|
|
73
|
-
realign/dashboard/widgets/terminal_panel.py,sha256=8WX2_EewlyFlxJYokw2akEqkJUjNt_-F8tzE7St3084,60132
|
|
79
|
+
realign/dashboard/widgets/sessions_table.py,sha256=6y78pEkyAmNsU4_o46PbwXRFW17fc5khgheBi4LjBNg,33374
|
|
74
80
|
realign/dashboard/widgets/watcher_panel.py,sha256=emVY1-aot9Dnf5UI9yyNeEmp4d2Gb-lrC28DjkeLjKA,19575
|
|
75
81
|
realign/dashboard/widgets/worker_panel.py,sha256=F_jKWABuCNmjQgeeuCr4KnFRKdY4CLTNcEXMYwsNaSk,18691
|
|
76
82
|
realign/db/__init__.py,sha256=65LsNdsq_rkwNC1eg1OAr3HC0ORXtelOh0I8MhNGr-g,3288
|
|
77
|
-
realign/db/base.py,sha256=
|
|
78
|
-
realign/db/locks.py,sha256=
|
|
83
|
+
realign/db/base.py,sha256=ShufW-c0ntKYsTWCbiXJ5W-G_H_mWN4YlnUuspWWu34,15589
|
|
84
|
+
realign/db/locks.py,sha256=dUQu9Yo5nZstMSPXZPYzN0xqX8UXhJgNV_PmYEJ-rK0,1801
|
|
79
85
|
realign/db/migrate_agents.py,sha256=cDeVUzKW950dJ0lV74QObHuONqKwErSrXI5akU2vBmQ,9633
|
|
80
86
|
realign/db/migration.py,sha256=af1QFEfIh_qX0pFyXzm5gWFVbQn0sKOUNLSJHlr__FU,13405
|
|
81
|
-
realign/db/schema.py,sha256=
|
|
82
|
-
realign/db/sqlite_db.py,sha256=
|
|
87
|
+
realign/db/schema.py,sha256=IWPbeDYrbC1eZGQAy8k1rk0r2NnABJzXSSg8bb00XBw,33885
|
|
88
|
+
realign/db/sqlite_db.py,sha256=u4yybbXzOApYPnHkHlR59qBSyWPoIqgRppTB4ht5taM,119736
|
|
83
89
|
realign/events/__init__.py,sha256=IM-NxF4Zk2hYFD07k4WrfNRuuiC9ihGjf4GBpJhjd2E,35
|
|
90
|
+
realign/events/agent_summarizer.py,sha256=vh65tYgo1NOYsIpVPR253nnOr-MIejC4KG5dGvDzKv4,5413
|
|
84
91
|
realign/events/debouncer.py,sha256=U3Q7dYpnMsAgWsW_E_IbSC4lrdEoi6H_SFLGLOAazs4,3062
|
|
85
92
|
realign/events/event_summarizer.py,sha256=jJtWM8UWtsG4KGdzYicMqcTxrncWzGNEQs5vdBJPyew,10185
|
|
86
|
-
realign/events/session_summarizer.py,sha256=
|
|
93
|
+
realign/events/session_summarizer.py,sha256=LqBoICEF5cggdv3px2O2TP0Xk7h_LofE1DDRv2TyJ5I,13868
|
|
87
94
|
realign/models/event.py,sha256=ypz74D4l6U2U0RhgL8fzEhiq7iQjhHybmAdLUNDY7P4,5521
|
|
88
95
|
realign/prompts/__init__.py,sha256=PpYR7f-T96fd-QyNYJDRS1U6h9O0rIt_SMsREy9i3aA,443
|
|
89
96
|
realign/prompts/presets.py,sha256=h9oEy0XP4JQ4DCnp8HN_FfF0LmI-yOV6xWJLknIghJ8,7256
|
|
@@ -97,8 +104,8 @@ realign/triggers/next_turn_trigger.py,sha256=-x80_I-WmIjXXzQHEPBykgx_GQW6oKaLDQx
|
|
|
97
104
|
realign/triggers/registry.py,sha256=dkIjSd8Bg-hF0nxaO2Fi2K-0Zipqv6vVjc-HYSrA_fY,3656
|
|
98
105
|
realign/triggers/turn_status.py,sha256=wAZEhXDAmDoX5F-ohWfSnZZ0eA6DAJ9svSPiSv_f6sg,6041
|
|
99
106
|
realign/triggers/turn_summary.py,sha256=f3hEUshgv9skJ9AbfWpoYs417lsv_HK2A_vpPjgryO4,4467
|
|
100
|
-
aline_ai-0.6.
|
|
101
|
-
aline_ai-0.6.
|
|
102
|
-
aline_ai-0.6.
|
|
103
|
-
aline_ai-0.6.
|
|
104
|
-
aline_ai-0.6.
|
|
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,,
|
realign/__init__.py
CHANGED
realign/agent_names.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Random Docker-style agent name generator.
|
|
2
|
+
|
|
3
|
+
Generates names like "bold-turing", "swift-hopper", etc.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import random
|
|
7
|
+
|
|
8
|
+
ADJECTIVES = [
|
|
9
|
+
"bold",
|
|
10
|
+
"bright",
|
|
11
|
+
"calm",
|
|
12
|
+
"clever",
|
|
13
|
+
"crisp",
|
|
14
|
+
"deft",
|
|
15
|
+
"eager",
|
|
16
|
+
"fast",
|
|
17
|
+
"firm",
|
|
18
|
+
"glad",
|
|
19
|
+
"keen",
|
|
20
|
+
"lucid",
|
|
21
|
+
"neat",
|
|
22
|
+
"noble",
|
|
23
|
+
"prime",
|
|
24
|
+
"quick",
|
|
25
|
+
"sharp",
|
|
26
|
+
"sleek",
|
|
27
|
+
"smart",
|
|
28
|
+
"solid",
|
|
29
|
+
"steady",
|
|
30
|
+
"strong",
|
|
31
|
+
"subtle",
|
|
32
|
+
"sure",
|
|
33
|
+
"swift",
|
|
34
|
+
"tidy",
|
|
35
|
+
"vivid",
|
|
36
|
+
"warm",
|
|
37
|
+
"wise",
|
|
38
|
+
"witty",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
SURNAMES = [
|
|
42
|
+
"babbage",
|
|
43
|
+
"bell",
|
|
44
|
+
"boole",
|
|
45
|
+
"cerf",
|
|
46
|
+
"church",
|
|
47
|
+
"conway",
|
|
48
|
+
"curie",
|
|
49
|
+
"darwin",
|
|
50
|
+
"dijkstra",
|
|
51
|
+
"euler",
|
|
52
|
+
"faraday",
|
|
53
|
+
"feynman",
|
|
54
|
+
"gauss",
|
|
55
|
+
"hopper",
|
|
56
|
+
"johnson",
|
|
57
|
+
"kahn",
|
|
58
|
+
"knuth",
|
|
59
|
+
"lamarr",
|
|
60
|
+
"leibniz",
|
|
61
|
+
"lovelace",
|
|
62
|
+
"maxwell",
|
|
63
|
+
"neumann",
|
|
64
|
+
"noether",
|
|
65
|
+
"pascal",
|
|
66
|
+
"planck",
|
|
67
|
+
"ritchie",
|
|
68
|
+
"shannon",
|
|
69
|
+
"tesla",
|
|
70
|
+
"thompson",
|
|
71
|
+
"turing",
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
_rng = random.SystemRandom()
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def generate_agent_name() -> str:
|
|
78
|
+
"""Return a random name, e.g. 'Bold Turing'."""
|
|
79
|
+
return f"{_rng.choice(ADJECTIVES).capitalize()} {_rng.choice(SURNAMES).capitalize()}"
|
|
@@ -66,6 +66,7 @@ def main():
|
|
|
66
66
|
terminal_id = os.environ.get("ALINE_TERMINAL_ID", "")
|
|
67
67
|
inner_socket = os.environ.get("ALINE_INNER_TMUX_SOCKET", "")
|
|
68
68
|
inner_session = os.environ.get("ALINE_INNER_TMUX_SESSION", "")
|
|
69
|
+
agent_id = os.environ.get("ALINE_AGENT_ID", "")
|
|
69
70
|
|
|
70
71
|
if not terminal_id:
|
|
71
72
|
try:
|
|
@@ -104,6 +105,7 @@ def main():
|
|
|
104
105
|
signal_data = {
|
|
105
106
|
"session_id": session_id,
|
|
106
107
|
"terminal_id": terminal_id,
|
|
108
|
+
"agent_id": agent_id,
|
|
107
109
|
"project_dir": project_dir,
|
|
108
110
|
"transcript_path": transcript_path,
|
|
109
111
|
"cwd": cwd,
|
|
@@ -348,6 +350,7 @@ def main():
|
|
|
348
350
|
cwd=cwd,
|
|
349
351
|
project_dir=project_dir,
|
|
350
352
|
source="Stop",
|
|
353
|
+
agent_id=agent_id if agent_id else None,
|
|
351
354
|
)
|
|
352
355
|
except Exception:
|
|
353
356
|
pass
|
|
@@ -41,7 +41,18 @@ def _get_db():
|
|
|
41
41
|
|
|
42
42
|
return get_database(read_only=False)
|
|
43
43
|
except Exception:
|
|
44
|
-
|
|
44
|
+
try:
|
|
45
|
+
import sys
|
|
46
|
+
|
|
47
|
+
root = Path(__file__).resolve().parents[2]
|
|
48
|
+
root_str = str(root)
|
|
49
|
+
if root_str not in sys.path:
|
|
50
|
+
sys.path.insert(0, root_str)
|
|
51
|
+
from realign.db import get_database # type: ignore
|
|
52
|
+
|
|
53
|
+
return get_database(read_only=False)
|
|
54
|
+
except Exception:
|
|
55
|
+
return None
|
|
45
56
|
|
|
46
57
|
|
|
47
58
|
def _write_to_db(
|
|
@@ -56,6 +67,7 @@ def _write_to_db(
|
|
|
56
67
|
source: str = "",
|
|
57
68
|
context_id: Optional[str] = None,
|
|
58
69
|
attention: Optional[str] = None,
|
|
70
|
+
agent_id: Optional[str] = None,
|
|
59
71
|
) -> bool:
|
|
60
72
|
"""Write terminal mapping to database (best-effort).
|
|
61
73
|
|
|
@@ -66,6 +78,10 @@ def _write_to_db(
|
|
|
66
78
|
if not db:
|
|
67
79
|
return False
|
|
68
80
|
|
|
81
|
+
# Force source to agent mapping when agent_id is known
|
|
82
|
+
if agent_id:
|
|
83
|
+
source = f"agent:{agent_id}"
|
|
84
|
+
|
|
69
85
|
# Check if agent exists
|
|
70
86
|
existing = db.get_agent_by_id(terminal_id)
|
|
71
87
|
if existing:
|
|
@@ -96,6 +112,27 @@ def _write_to_db(
|
|
|
96
112
|
source=source if source else None,
|
|
97
113
|
attention=attention,
|
|
98
114
|
)
|
|
115
|
+
|
|
116
|
+
# Link session to agent if both are available (V19+)
|
|
117
|
+
if session_id and agent_id:
|
|
118
|
+
try:
|
|
119
|
+
db.update_session_agent_id(session_id, agent_id)
|
|
120
|
+
except Exception:
|
|
121
|
+
pass
|
|
122
|
+
|
|
123
|
+
# WindowLink: record terminal/session association (V23)
|
|
124
|
+
try:
|
|
125
|
+
db.insert_window_link(
|
|
126
|
+
terminal_id=terminal_id,
|
|
127
|
+
agent_id=agent_id,
|
|
128
|
+
session_id=session_id,
|
|
129
|
+
provider=provider,
|
|
130
|
+
source=source,
|
|
131
|
+
ts=time.time(),
|
|
132
|
+
)
|
|
133
|
+
except Exception:
|
|
134
|
+
pass
|
|
135
|
+
|
|
99
136
|
# Note: Don't close - get_database() returns a singleton
|
|
100
137
|
return True
|
|
101
138
|
except Exception:
|
|
@@ -114,6 +151,7 @@ def update_terminal_mapping(
|
|
|
114
151
|
source: str = "",
|
|
115
152
|
context_id: Optional[str] = None,
|
|
116
153
|
attention: Optional[str] = None,
|
|
154
|
+
agent_id: Optional[str] = None,
|
|
117
155
|
) -> None:
|
|
118
156
|
"""Update terminal->session binding.
|
|
119
157
|
|
|
@@ -124,6 +162,9 @@ def update_terminal_mapping(
|
|
|
124
162
|
Concurrency: uses a simple fcntl lock file for JSON; last writer wins, but updates are atomic.
|
|
125
163
|
"""
|
|
126
164
|
# Phase 1: Write to database (best-effort, don't fail if DB unavailable)
|
|
165
|
+
if agent_id:
|
|
166
|
+
source = f"agent:{agent_id}"
|
|
167
|
+
|
|
127
168
|
_write_to_db(
|
|
128
169
|
terminal_id=terminal_id,
|
|
129
170
|
provider=provider,
|
|
@@ -135,6 +176,7 @@ def update_terminal_mapping(
|
|
|
135
176
|
source=source,
|
|
136
177
|
context_id=context_id,
|
|
137
178
|
attention=attention,
|
|
179
|
+
agent_id=agent_id,
|
|
138
180
|
)
|
|
139
181
|
|
|
140
182
|
# Phase 2: Write to JSON (backward compatibility)
|
|
@@ -52,6 +52,7 @@ def main() -> None:
|
|
|
52
52
|
terminal_id = os.environ.get("ALINE_TERMINAL_ID", "")
|
|
53
53
|
inner_socket = os.environ.get("ALINE_INNER_TMUX_SOCKET", "")
|
|
54
54
|
inner_session = os.environ.get("ALINE_INNER_TMUX_SESSION", "")
|
|
55
|
+
agent_id = os.environ.get("ALINE_AGENT_ID", "")
|
|
55
56
|
|
|
56
57
|
if not terminal_id:
|
|
57
58
|
try:
|
|
@@ -86,6 +87,7 @@ def main() -> None:
|
|
|
86
87
|
signal_data = {
|
|
87
88
|
"session_id": session_id,
|
|
88
89
|
"terminal_id": terminal_id,
|
|
90
|
+
"agent_id": agent_id,
|
|
89
91
|
"prompt": prompt,
|
|
90
92
|
"transcript_path": transcript_path,
|
|
91
93
|
"cwd": cwd,
|
|
@@ -274,6 +276,7 @@ def main() -> None:
|
|
|
274
276
|
cwd=cwd,
|
|
275
277
|
project_dir=project_dir,
|
|
276
278
|
source="UserPromptSubmit",
|
|
279
|
+
agent_id=agent_id if agent_id else None,
|
|
277
280
|
)
|
|
278
281
|
except Exception:
|
|
279
282
|
pass
|
realign/cli.py
CHANGED
|
@@ -19,6 +19,7 @@ from .commands import (
|
|
|
19
19
|
add,
|
|
20
20
|
auth,
|
|
21
21
|
doctor,
|
|
22
|
+
agent,
|
|
22
23
|
)
|
|
23
24
|
|
|
24
25
|
app = typer.Typer(
|
|
@@ -248,6 +249,67 @@ app.add_typer(context_app, name="context")
|
|
|
248
249
|
add_app = typer.Typer(help="Install optional local tooling")
|
|
249
250
|
app.add_typer(add_app, name="add")
|
|
250
251
|
|
|
252
|
+
# Create agent subcommand group
|
|
253
|
+
agent_app = typer.Typer(help="Manage agents")
|
|
254
|
+
app.add_typer(agent_app, name="agent")
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
@agent_app.command(name="new")
|
|
258
|
+
def agent_new_cli(
|
|
259
|
+
name: Optional[str] = typer.Option(None, "--name", "-n", help="Agent name"),
|
|
260
|
+
desc: str = typer.Option("", "--desc", "-d", help="Agent description"),
|
|
261
|
+
):
|
|
262
|
+
"""Create a new agent with a random name (or specify one)."""
|
|
263
|
+
exit_code = agent.agent_new_command(name=name, desc=desc)
|
|
264
|
+
raise typer.Exit(code=exit_code)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
@agent_app.command(name="list")
|
|
268
|
+
def agent_list_cli(
|
|
269
|
+
all: bool = typer.Option(
|
|
270
|
+
False, "--all", "-a", help="Include invisible agents"
|
|
271
|
+
),
|
|
272
|
+
):
|
|
273
|
+
"""List all agents."""
|
|
274
|
+
exit_code = agent.agent_list_command(include_invisible=all)
|
|
275
|
+
raise typer.Exit(code=exit_code)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
@agent_app.command(name="share")
|
|
279
|
+
def agent_share_cli(
|
|
280
|
+
agent_id: str = typer.Argument(..., help="Agent ID to share"),
|
|
281
|
+
password: Optional[str] = typer.Option(
|
|
282
|
+
None, "--password", "-p", help="Password for encrypted share"
|
|
283
|
+
),
|
|
284
|
+
expiry_days: int = typer.Option(7, "--expiry", help="Number of days before share expires"),
|
|
285
|
+
max_views: int = typer.Option(100, "--max-views", help="Maximum number of views allowed"),
|
|
286
|
+
mcp: bool = typer.Option(
|
|
287
|
+
True,
|
|
288
|
+
"--mcp/--no-mcp",
|
|
289
|
+
help="Include MCP usage instructions (default: enabled)",
|
|
290
|
+
),
|
|
291
|
+
json_output: bool = typer.Option(False, "--json", help="Output results in JSON format"),
|
|
292
|
+
):
|
|
293
|
+
"""Share all sessions for an agent.
|
|
294
|
+
|
|
295
|
+
Creates a shareable link for all sessions associated with the specified agent.
|
|
296
|
+
The share includes a generated Slack message for easy sharing.
|
|
297
|
+
|
|
298
|
+
Examples:
|
|
299
|
+
aline agent share abc123de
|
|
300
|
+
aline agent share abc123de --json
|
|
301
|
+
aline agent share abc123de --password mypass
|
|
302
|
+
"""
|
|
303
|
+
exit_code = export_shares.export_agent_shares_command(
|
|
304
|
+
agent_id=agent_id,
|
|
305
|
+
password=password,
|
|
306
|
+
expiry_days=expiry_days,
|
|
307
|
+
max_views=max_views,
|
|
308
|
+
enable_mcp=mcp,
|
|
309
|
+
json_output=json_output,
|
|
310
|
+
)
|
|
311
|
+
raise typer.Exit(code=exit_code)
|
|
312
|
+
|
|
251
313
|
|
|
252
314
|
@add_app.command(name="tmux")
|
|
253
315
|
def add_tmux_cli():
|
realign/codex_detector.py
CHANGED
|
@@ -14,15 +14,30 @@ def _codex_session_roots() -> list[Path]:
|
|
|
14
14
|
# Default Codex home: ~/.codex/sessions
|
|
15
15
|
roots.append(Path.home() / ".codex" / "sessions")
|
|
16
16
|
|
|
17
|
-
# Aline-managed per-terminal CODEX_HOME isolation: ~/.aline/codex_homes/*/sessions
|
|
17
|
+
# Aline-managed per-terminal or per-agent CODEX_HOME isolation: ~/.aline/codex_homes/*/sessions
|
|
18
18
|
try:
|
|
19
19
|
from .codex_home import aline_codex_homes_dir, codex_sessions_dir_for_home
|
|
20
20
|
|
|
21
21
|
homes = aline_codex_homes_dir()
|
|
22
22
|
if homes.exists():
|
|
23
23
|
for child in homes.iterdir():
|
|
24
|
-
if child.is_dir():
|
|
25
|
-
|
|
24
|
+
if not child.is_dir():
|
|
25
|
+
continue
|
|
26
|
+
if child.name.startswith("agent-"):
|
|
27
|
+
# New layout: agent-<id>/<terminal_id>/sessions
|
|
28
|
+
try:
|
|
29
|
+
for grandchild in child.iterdir():
|
|
30
|
+
if grandchild.is_dir():
|
|
31
|
+
nested_sessions = codex_sessions_dir_for_home(grandchild)
|
|
32
|
+
if nested_sessions.exists():
|
|
33
|
+
roots.append(nested_sessions)
|
|
34
|
+
except Exception:
|
|
35
|
+
continue
|
|
36
|
+
else:
|
|
37
|
+
# Terminal layout: <terminal_id>/sessions
|
|
38
|
+
direct_sessions = codex_sessions_dir_for_home(child)
|
|
39
|
+
if direct_sessions.exists():
|
|
40
|
+
roots.append(direct_sessions)
|
|
26
41
|
except Exception:
|
|
27
42
|
pass
|
|
28
43
|
|
realign/codex_home.py
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
"""Codex home/session path helpers.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
We choose deterministic paths under `~/.aline/` so the watcher (a separate process) can
|
|
7
|
-
derive the owning terminal_id purely from the session file path.
|
|
3
|
+
We isolate Codex storage via `CODEX_HOME` so the watcher can infer ownership
|
|
4
|
+
from the session file path. By default it's per-terminal, but when an
|
|
5
|
+
ALINE_AGENT_ID is present we can scope CODEX_HOME per agent.
|
|
8
6
|
"""
|
|
9
7
|
|
|
10
8
|
from __future__ import annotations
|
|
@@ -15,6 +13,7 @@ from typing import Optional
|
|
|
15
13
|
|
|
16
14
|
|
|
17
15
|
ENV_CODEX_HOME = "CODEX_HOME"
|
|
16
|
+
AGENT_HOME_PREFIX = "agent-"
|
|
18
17
|
|
|
19
18
|
|
|
20
19
|
def aline_codex_homes_dir() -> Path:
|
|
@@ -24,11 +23,26 @@ def aline_codex_homes_dir() -> Path:
|
|
|
24
23
|
return Path.home() / ".aline" / "codex_homes"
|
|
25
24
|
|
|
26
25
|
|
|
26
|
+
def _safe_id(raw_id: str) -> str:
|
|
27
|
+
return (raw_id or "").strip().replace("/", "_").replace("\\", "_")
|
|
28
|
+
|
|
29
|
+
|
|
27
30
|
def codex_home_for_terminal(terminal_id: str) -> Path:
|
|
28
|
-
tid = (terminal_id
|
|
31
|
+
tid = _safe_id(terminal_id)
|
|
29
32
|
return aline_codex_homes_dir() / tid
|
|
30
33
|
|
|
31
34
|
|
|
35
|
+
def codex_home_for_agent(agent_id: str) -> Path:
|
|
36
|
+
aid = _safe_id(agent_id)
|
|
37
|
+
return aline_codex_homes_dir() / f"{AGENT_HOME_PREFIX}{aid}"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def codex_home_for_terminal_or_agent(terminal_id: str, agent_id: Optional[str]) -> Path:
|
|
41
|
+
if agent_id:
|
|
42
|
+
return codex_home_for_agent(agent_id) / _safe_id(terminal_id)
|
|
43
|
+
return codex_home_for_terminal(terminal_id)
|
|
44
|
+
|
|
45
|
+
|
|
32
46
|
def codex_sessions_dir_for_home(codex_home: Path) -> Path:
|
|
33
47
|
return codex_home / "sessions"
|
|
34
48
|
|
|
@@ -37,8 +51,14 @@ def codex_sessions_dir_for_terminal(terminal_id: str) -> Path:
|
|
|
37
51
|
return codex_sessions_dir_for_home(codex_home_for_terminal(terminal_id))
|
|
38
52
|
|
|
39
53
|
|
|
40
|
-
def
|
|
41
|
-
|
|
54
|
+
def codex_sessions_dir_for_terminal_or_agent(
|
|
55
|
+
terminal_id: str, agent_id: Optional[str]
|
|
56
|
+
) -> Path:
|
|
57
|
+
return codex_sessions_dir_for_home(codex_home_for_terminal_or_agent(terminal_id, agent_id))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def codex_home_owner_from_session_file(session_file: Path) -> Optional[tuple[str, str]]:
|
|
61
|
+
"""Return ("terminal", id) or ("agent", id) if session_file is under Aline-managed homes."""
|
|
42
62
|
try:
|
|
43
63
|
homes = aline_codex_homes_dir().resolve()
|
|
44
64
|
p = session_file.resolve()
|
|
@@ -53,17 +73,35 @@ def terminal_id_from_codex_session_file(session_file: Path) -> Optional[str]:
|
|
|
53
73
|
parts = rel.parts
|
|
54
74
|
if len(parts) < 3:
|
|
55
75
|
return None
|
|
56
|
-
|
|
57
|
-
if not
|
|
76
|
+
owner = (parts[0] or "").strip()
|
|
77
|
+
if not owner:
|
|
58
78
|
return None
|
|
59
|
-
|
|
79
|
+
# Terminal layout: <homes>/<terminal_id>/sessions/...
|
|
80
|
+
if parts[1] == "sessions":
|
|
81
|
+
if owner.startswith(AGENT_HOME_PREFIX):
|
|
82
|
+
return ("agent", owner[len(AGENT_HOME_PREFIX):])
|
|
83
|
+
return ("terminal", owner)
|
|
84
|
+
# Agent/terminal layout: <homes>/agent-<agent_id>/<terminal_id>/sessions/...
|
|
85
|
+
if owner.startswith(AGENT_HOME_PREFIX) and len(parts) >= 4 and parts[2] == "sessions":
|
|
86
|
+
terminal_id = (parts[1] or "").strip()
|
|
87
|
+
if terminal_id:
|
|
88
|
+
return ("terminal", terminal_id)
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def terminal_id_from_codex_session_file(session_file: Path) -> Optional[str]:
|
|
93
|
+
"""If session_file is under an Aline-managed CODEX_HOME, return terminal_id."""
|
|
94
|
+
owner = codex_home_owner_from_session_file(session_file)
|
|
95
|
+
if not owner:
|
|
96
|
+
return None
|
|
97
|
+
if owner[0] != "terminal":
|
|
60
98
|
return None
|
|
61
|
-
return
|
|
99
|
+
return owner[1]
|
|
62
100
|
|
|
63
101
|
|
|
64
|
-
def prepare_codex_home(terminal_id: str) -> Path:
|
|
65
|
-
"""Create/prepare an isolated CODEX_HOME
|
|
66
|
-
home =
|
|
102
|
+
def prepare_codex_home(terminal_id: str, *, agent_id: Optional[str] = None) -> Path:
|
|
103
|
+
"""Create/prepare an isolated CODEX_HOME (per-agent if agent_id is provided)."""
|
|
104
|
+
home = codex_home_for_terminal_or_agent(terminal_id, agent_id)
|
|
67
105
|
sessions = codex_sessions_dir_for_home(home)
|
|
68
106
|
try:
|
|
69
107
|
sessions.mkdir(parents=True, exist_ok=True)
|
|
@@ -81,5 +119,16 @@ def prepare_codex_home(terminal_id: str) -> Path:
|
|
|
81
119
|
except Exception:
|
|
82
120
|
pass
|
|
83
121
|
|
|
84
|
-
|
|
122
|
+
# Reuse global auth/config to avoid re-login for per-terminal homes.
|
|
123
|
+
try:
|
|
124
|
+
global_home = Path.home() / ".codex"
|
|
125
|
+
for name in ("auth.json", "config.toml"):
|
|
126
|
+
src = global_home / name
|
|
127
|
+
dst = home / name
|
|
128
|
+
if src.exists() and not dst.exists():
|
|
129
|
+
dst.parent.mkdir(parents=True, exist_ok=True)
|
|
130
|
+
dst.symlink_to(src)
|
|
131
|
+
except Exception:
|
|
132
|
+
pass
|
|
85
133
|
|
|
134
|
+
return home
|