machineconfig 1.96__py3-none-any.whl → 2.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.
Potentially problematic release.
This version of machineconfig might be problematic. Click here for more details.
- machineconfig/cluster/cloud_manager.py +22 -26
- machineconfig/cluster/data_transfer.py +2 -2
- machineconfig/cluster/distribute.py +0 -2
- machineconfig/cluster/file_manager.py +4 -4
- machineconfig/cluster/job_params.py +1 -1
- machineconfig/cluster/loader_runner.py +8 -8
- machineconfig/cluster/remote_machine.py +4 -4
- machineconfig/cluster/script_execution.py +2 -2
- machineconfig/cluster/sessions_managers/archive/create_zellij_template.py +1 -1
- machineconfig/cluster/sessions_managers/enhanced_command_runner.py +23 -23
- machineconfig/cluster/sessions_managers/wt_local.py +78 -76
- machineconfig/cluster/sessions_managers/wt_local_manager.py +91 -91
- machineconfig/cluster/sessions_managers/wt_remote.py +39 -39
- machineconfig/cluster/sessions_managers/wt_remote_manager.py +94 -91
- machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +56 -54
- machineconfig/cluster/sessions_managers/wt_utils/process_monitor.py +49 -49
- machineconfig/cluster/sessions_managers/wt_utils/remote_executor.py +18 -18
- machineconfig/cluster/sessions_managers/wt_utils/session_manager.py +42 -42
- machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +36 -36
- machineconfig/cluster/sessions_managers/zellij_local.py +43 -46
- machineconfig/cluster/sessions_managers/zellij_local_manager.py +139 -120
- machineconfig/cluster/sessions_managers/zellij_remote.py +35 -35
- machineconfig/cluster/sessions_managers/zellij_remote_manager.py +33 -33
- machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +15 -15
- machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +25 -26
- machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +49 -49
- machineconfig/cluster/sessions_managers/zellij_utils/remote_executor.py +5 -5
- machineconfig/cluster/sessions_managers/zellij_utils/session_manager.py +15 -15
- machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +11 -11
- machineconfig/cluster/templates/utils.py +3 -3
- machineconfig/jobs/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python/__pycache__/python_ve_symlink.cpython-311.pyc +0 -0
- machineconfig/jobs/python/check_installations.py +8 -9
- machineconfig/jobs/python/python_cargo_build_share.py +2 -2
- machineconfig/jobs/python/vscode/link_ve.py +7 -7
- machineconfig/jobs/python/vscode/select_interpreter.py +7 -7
- machineconfig/jobs/python/vscode/sync_code.py +5 -5
- machineconfig/jobs/python_custom_installers/archive/ngrok.py +2 -2
- machineconfig/jobs/python_custom_installers/dev/aider.py +3 -3
- machineconfig/jobs/python_custom_installers/dev/alacritty.py +3 -3
- machineconfig/jobs/python_custom_installers/dev/brave.py +3 -3
- machineconfig/jobs/python_custom_installers/dev/bypass_paywall.py +5 -5
- machineconfig/jobs/python_custom_installers/dev/code.py +3 -3
- machineconfig/jobs/python_custom_installers/dev/cursor.py +9 -9
- machineconfig/jobs/python_custom_installers/dev/docker_desktop.py +4 -4
- machineconfig/jobs/python_custom_installers/dev/espanso.py +4 -4
- machineconfig/jobs/python_custom_installers/dev/goes.py +4 -4
- machineconfig/jobs/python_custom_installers/dev/lvim.py +4 -4
- machineconfig/jobs/python_custom_installers/dev/nerdfont.py +3 -3
- machineconfig/jobs/python_custom_installers/dev/redis.py +3 -3
- machineconfig/jobs/python_custom_installers/dev/wezterm.py +3 -3
- machineconfig/jobs/python_custom_installers/dev/winget.py +27 -27
- machineconfig/jobs/python_custom_installers/docker.py +3 -3
- machineconfig/jobs/python_custom_installers/gh.py +7 -7
- machineconfig/jobs/python_custom_installers/hx.py +1 -1
- machineconfig/jobs/python_custom_installers/warp-cli.py +3 -3
- machineconfig/jobs/python_generic_installers/config.json +412 -389
- machineconfig/jobs/python_windows_installers/dev/config.json +1 -1
- machineconfig/logger.py +50 -0
- machineconfig/profile/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/profile/__pycache__/create.cpython-311.pyc +0 -0
- machineconfig/profile/__pycache__/shell.cpython-311.pyc +0 -0
- machineconfig/profile/create.py +23 -16
- machineconfig/profile/create_hardlinks.py +8 -8
- machineconfig/profile/shell.py +41 -37
- machineconfig/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/linux/devops +2 -2
- machineconfig/scripts/linux/fire +1 -0
- machineconfig/scripts/linux/fire_agents +0 -1
- machineconfig/scripts/linux/mcinit +27 -0
- machineconfig/scripts/python/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/croshell.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_agents.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_jobs.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/repos.cpython-311.pyc +0 -0
- machineconfig/scripts/python/ai/__pycache__/init.cpython-311.pyc +0 -0
- machineconfig/scripts/python/ai/__pycache__/mcinit.cpython-311.pyc +0 -0
- machineconfig/scripts/python/ai/chatmodes/Thinking-Beast-Mode.chatmode.md +337 -0
- machineconfig/scripts/python/ai/chatmodes/Ultimate-Transparent-Thinking-Beast-Mode.chatmode.md +644 -0
- machineconfig/scripts/python/ai/chatmodes/deepResearch.chatmode.md +81 -0
- machineconfig/scripts/python/ai/configs/.gemini/settings.json +81 -0
- machineconfig/scripts/python/ai/instructions/python/dev.instructions.md +45 -0
- machineconfig/scripts/python/ai/mcinit.py +103 -0
- machineconfig/scripts/python/ai/prompts/allLintersAndTypeCheckers.prompt.md +5 -0
- machineconfig/scripts/python/ai/prompts/research-report-skeleton.prompt.md +38 -0
- machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +47 -0
- machineconfig/scripts/python/archive/tmate_conn.py +5 -5
- machineconfig/scripts/python/archive/tmate_start.py +3 -3
- machineconfig/scripts/python/choose_wezterm_theme.py +2 -2
- machineconfig/scripts/python/cloud_copy.py +19 -18
- machineconfig/scripts/python/cloud_mount.py +9 -7
- machineconfig/scripts/python/cloud_repo_sync.py +11 -11
- machineconfig/scripts/python/cloud_sync.py +1 -1
- machineconfig/scripts/python/croshell.py +14 -14
- machineconfig/scripts/python/devops.py +6 -6
- machineconfig/scripts/python/devops_add_identity.py +8 -6
- machineconfig/scripts/python/devops_add_ssh_key.py +18 -18
- machineconfig/scripts/python/devops_backup_retrieve.py +13 -13
- machineconfig/scripts/python/devops_devapps_install.py +3 -3
- machineconfig/scripts/python/devops_update_repos.py +1 -1
- machineconfig/scripts/python/dotfile.py +2 -2
- machineconfig/scripts/python/fire_agents.py +183 -41
- machineconfig/scripts/python/fire_jobs.py +17 -11
- machineconfig/scripts/python/ftpx.py +2 -2
- machineconfig/scripts/python/gh_models.py +94 -94
- machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/cloud_helpers.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/helpers2.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/cloud_helpers.py +3 -3
- machineconfig/scripts/python/helpers/helpers2.py +1 -1
- machineconfig/scripts/python/helpers/helpers4.py +8 -6
- machineconfig/scripts/python/helpers/helpers5.py +7 -7
- machineconfig/scripts/python/helpers/repo_sync_helpers.py +1 -1
- machineconfig/scripts/python/mount_nfs.py +3 -2
- machineconfig/scripts/python/mount_nw_drive.py +4 -4
- machineconfig/scripts/python/mount_ssh.py +3 -2
- machineconfig/scripts/python/repos.py +8 -8
- machineconfig/scripts/python/scheduler.py +1 -1
- machineconfig/scripts/python/start_slidev.py +8 -7
- machineconfig/scripts/python/start_terminals.py +1 -1
- machineconfig/scripts/python/viewer.py +40 -40
- machineconfig/scripts/python/wifi_conn.py +65 -66
- machineconfig/scripts/python/wsl_windows_transfer.py +1 -1
- machineconfig/scripts/windows/mcinit.ps1 +4 -0
- machineconfig/settings/linters/.ruff.toml +2 -2
- machineconfig/settings/shells/ipy/profiles/default/startup/playext.py +71 -71
- machineconfig/settings/shells/wt/settings.json +8 -8
- machineconfig/setup_linux/web_shortcuts/tmp.sh +2 -0
- machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +10 -7
- machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +9 -7
- machineconfig/utils/ai/browser_user_wrapper.py +5 -5
- machineconfig/utils/ai/generate_file_checklist.py +11 -12
- machineconfig/utils/ai/url2md.py +1 -1
- machineconfig/utils/cloud/onedrive/setup_oauth.py +4 -4
- machineconfig/utils/cloud/onedrive/transaction.py +129 -129
- machineconfig/utils/code.py +13 -6
- machineconfig/utils/installer.py +51 -53
- machineconfig/utils/installer_utils/installer_abc.py +21 -10
- machineconfig/utils/installer_utils/installer_class.py +42 -16
- machineconfig/utils/io_save.py +3 -15
- machineconfig/utils/options.py +10 -3
- machineconfig/utils/path.py +5 -0
- machineconfig/utils/path_reduced.py +201 -149
- machineconfig/utils/procs.py +23 -23
- machineconfig/utils/scheduling.py +11 -12
- machineconfig/utils/ssh.py +270 -0
- machineconfig/utils/terminal.py +180 -0
- machineconfig/utils/utils.py +1 -2
- machineconfig/utils/utils2.py +43 -0
- machineconfig/utils/utils5.py +163 -34
- machineconfig/utils/ve.py +2 -2
- {machineconfig-1.96.dist-info → machineconfig-2.0.dist-info}/METADATA +13 -8
- {machineconfig-1.96.dist-info → machineconfig-2.0.dist-info}/RECORD +163 -144
- machineconfig/cluster/self_ssh.py +0 -57
- {machineconfig-1.96.dist-info → machineconfig-2.0.dist-info}/WHEEL +0 -0
- {machineconfig-1.96.dist-info → machineconfig-2.0.dist-info}/top_level.txt +0 -0
|
@@ -21,20 +21,20 @@ TMP_LAYOUT_DIR = Path.home().joinpath("tmp_results", "zellij_layouts", "layout_m
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class ZellijRemoteLayoutGenerator:
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
def __init__(self, remote_name: str, session_name_prefix: str):
|
|
26
26
|
self.remote_name = remote_name
|
|
27
27
|
self.session_name = session_name_prefix + "_" + LayoutGenerator.generate_random_suffix()
|
|
28
28
|
self.tab_config: Dict[str, Tuple[str, str]] = {}
|
|
29
29
|
self.layout_path: Optional[str] = None
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
# Initialize modular components
|
|
32
32
|
self.remote_executor = RemoteExecutor(remote_name)
|
|
33
33
|
self.layout_generator = LayoutGenerator()
|
|
34
34
|
self.process_monitor = ProcessMonitor(self.remote_executor)
|
|
35
35
|
self.session_manager = SessionManager(self.remote_executor, self.session_name, TMP_LAYOUT_DIR)
|
|
36
36
|
self.status_reporter = StatusReporter(self.process_monitor, self.session_manager)
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
def copy_layout_to_remote(self, local_layout_file: Path, random_suffix: str) -> str:
|
|
39
39
|
return self.session_manager.copy_layout_to_remote(local_layout_file, random_suffix)
|
|
40
40
|
|
|
@@ -42,22 +42,22 @@ class ZellijRemoteLayoutGenerator:
|
|
|
42
42
|
# Enhanced Rich logging for remote layout creation
|
|
43
43
|
tab_count = len(tab_config)
|
|
44
44
|
console.print(f"[bold cyan]📋 Creating Zellij layout[/bold cyan] [bright_green]with {tab_count} tabs[/bright_green] [magenta]for remote[/magenta] [bold yellow]'{self.remote_name}'[/bold yellow]")
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
# Display tab summary for remote
|
|
47
47
|
for tab_name, (cwd, _) in tab_config.items():
|
|
48
48
|
console.print(f" [yellow]→[/yellow] [bold]{tab_name}[/bold] [dim]in[/dim] [blue]{cwd}[/blue] [dim]on[/dim] [yellow]{self.remote_name}[/yellow]")
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
self.tab_config = tab_config.copy()
|
|
51
51
|
if output_dir:
|
|
52
52
|
output_path = Path(output_dir)
|
|
53
53
|
else:
|
|
54
54
|
output_path = TMP_LAYOUT_DIR
|
|
55
|
-
self.layout_path = self.layout_generator.create_layout_file(tab_config, output_path, self.session_name)
|
|
55
|
+
self.layout_path = self.layout_generator.create_layout_file(tab_config, output_path, self.session_name)
|
|
56
56
|
return self.layout_path
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
def get_layout_preview(self, tab_config: Dict[str, Tuple[str, str]]) -> str:
|
|
59
59
|
return self.layout_generator.generate_layout_content(tab_config)
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
def check_command_status(self, tab_name: str, use_verification: bool = True) -> Dict[str, Any]:
|
|
62
62
|
return self.process_monitor.check_command_status(tab_name, self.tab_config, use_verification)
|
|
63
63
|
|
|
@@ -114,42 +114,42 @@ class ZellijRemoteLayoutGenerator:
|
|
|
114
114
|
file_path_obj = default_dir / f"zellij_session_{random_id}.json"
|
|
115
115
|
else:
|
|
116
116
|
file_path_obj = Path(file_path)
|
|
117
|
-
|
|
117
|
+
|
|
118
118
|
# Ensure .json extension
|
|
119
119
|
if not str(file_path_obj).endswith('.json'):
|
|
120
120
|
file_path_obj = file_path_obj.with_suffix('.json')
|
|
121
|
-
|
|
121
|
+
|
|
122
122
|
# Ensure parent directory exists
|
|
123
123
|
file_path_obj.parent.mkdir(parents=True, exist_ok=True)
|
|
124
|
-
|
|
124
|
+
|
|
125
125
|
# Serialize to JSON
|
|
126
126
|
data = self.to_dict()
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
127
|
+
|
|
128
|
+
text = json.dumps(data, indent=2, ensure_ascii=False)
|
|
129
|
+
file_path_obj.write_text(text, encoding="utf-8")
|
|
130
|
+
|
|
131
131
|
logger.info(f"✅ Serialized ZellijRemoteLayoutGenerator to: {file_path_obj}")
|
|
132
132
|
return str(file_path_obj)
|
|
133
133
|
|
|
134
134
|
@classmethod
|
|
135
135
|
def from_json(cls, file_path: Union[str, Path]) -> 'ZellijRemoteLayoutGenerator':
|
|
136
136
|
file_path = Path(file_path)
|
|
137
|
-
|
|
137
|
+
|
|
138
138
|
# Ensure .json extension
|
|
139
139
|
if not str(file_path).endswith('.json'):
|
|
140
140
|
file_path = file_path.with_suffix('.json')
|
|
141
|
-
|
|
141
|
+
|
|
142
142
|
if not file_path.exists():
|
|
143
143
|
raise FileNotFoundError(f"JSON file not found: {file_path}")
|
|
144
|
-
|
|
144
|
+
|
|
145
145
|
# Load JSON data
|
|
146
146
|
with open(file_path, 'r', encoding='utf-8') as f:
|
|
147
147
|
data = json.load(f)
|
|
148
|
-
|
|
148
|
+
|
|
149
149
|
# Validate that it's the correct class
|
|
150
150
|
if data.get('class_name') != cls.__name__:
|
|
151
151
|
logger.warning(f"Class name mismatch: expected {cls.__name__}, got {data.get('class_name')}")
|
|
152
|
-
|
|
152
|
+
|
|
153
153
|
# Create new instance
|
|
154
154
|
# Extract session name prefix by removing the suffix
|
|
155
155
|
session_name = data['session_name']
|
|
@@ -157,14 +157,14 @@ class ZellijRemoteLayoutGenerator:
|
|
|
157
157
|
session_name_prefix = '_'.join(session_name.split('_')[:-1])
|
|
158
158
|
else:
|
|
159
159
|
session_name_prefix = session_name
|
|
160
|
-
|
|
160
|
+
|
|
161
161
|
instance = cls(remote_name=data['remote_name'], session_name_prefix=session_name_prefix)
|
|
162
|
-
|
|
162
|
+
|
|
163
163
|
# Restore state
|
|
164
164
|
instance.session_name = data['session_name']
|
|
165
165
|
instance.tab_config = data['tab_config']
|
|
166
166
|
instance.layout_path = data['layout_path']
|
|
167
|
-
|
|
167
|
+
|
|
168
168
|
logger.info(f"✅ Loaded ZellijRemoteLayoutGenerator from: {file_path}")
|
|
169
169
|
return instance
|
|
170
170
|
|
|
@@ -174,10 +174,10 @@ class ZellijRemoteLayoutGenerator:
|
|
|
174
174
|
directory_path = Path.home() / "tmp_results" / "zellij_sessions" / "serialized"
|
|
175
175
|
else:
|
|
176
176
|
directory_path = Path(directory_path)
|
|
177
|
-
|
|
177
|
+
|
|
178
178
|
if not directory_path.exists():
|
|
179
179
|
return []
|
|
180
|
-
|
|
180
|
+
|
|
181
181
|
json_files = [f.name for f in directory_path.glob("*.json")]
|
|
182
182
|
return sorted(json_files)
|
|
183
183
|
|
|
@@ -185,45 +185,45 @@ if __name__ == "__main__":
|
|
|
185
185
|
# Example usage
|
|
186
186
|
sample_tabs = {
|
|
187
187
|
"🤖Bot1": ("~/code/bytesense/bithence", "~/scripts/fire -mO go1.py bot1 --kw create_new_bot True"),
|
|
188
|
-
"🤖Bot2": ("~/code/bytesense/bithence", "~/scripts/fire -mO go2.py bot2 --kw create_new_bot True"),
|
|
188
|
+
"🤖Bot2": ("~/code/bytesense/bithence", "~/scripts/fire -mO go2.py bot2 --kw create_new_bot True"),
|
|
189
189
|
"📊Monitor": ("~", "htop"),
|
|
190
190
|
"📝Logs": ("/var/log", "tail -f /var/log/app.log")
|
|
191
191
|
}
|
|
192
|
-
|
|
192
|
+
|
|
193
193
|
# Replace 'myserver' with an actual SSH config alias
|
|
194
194
|
remote_name = "myserver" # This should be in ~/.ssh/config
|
|
195
195
|
session_name = "test_remote_session"
|
|
196
|
-
|
|
196
|
+
|
|
197
197
|
try:
|
|
198
198
|
# Create layout using the remote generator
|
|
199
199
|
generator = ZellijRemoteLayoutGenerator(remote_name=remote_name, session_name_prefix=session_name)
|
|
200
200
|
layout_path = generator.create_zellij_layout(sample_tabs)
|
|
201
201
|
print(f"✅ Remote layout created successfully: {layout_path}")
|
|
202
|
-
|
|
202
|
+
|
|
203
203
|
# Demonstrate serialization
|
|
204
204
|
print("\n💾 Demonstrating serialization...")
|
|
205
205
|
saved_path = generator.to_json()
|
|
206
206
|
print(f"✅ Session saved to: {saved_path}")
|
|
207
|
-
|
|
207
|
+
|
|
208
208
|
# List all saved sessions
|
|
209
209
|
saved_sessions = ZellijRemoteLayoutGenerator.list_saved_sessions()
|
|
210
210
|
print(f"📋 Available saved sessions: {saved_sessions}")
|
|
211
|
-
|
|
211
|
+
|
|
212
212
|
# Demonstrate loading (using the full path)
|
|
213
213
|
loaded_generator = ZellijRemoteLayoutGenerator.from_json(saved_path)
|
|
214
214
|
print(f"✅ Session loaded successfully: {loaded_generator.session_name}")
|
|
215
215
|
print(f"📊 Loaded tabs: {list(loaded_generator.tab_config.keys())}")
|
|
216
|
-
|
|
216
|
+
|
|
217
217
|
# Demonstrate status checking
|
|
218
218
|
print(f"\n🔍 Checking command status on remote '{remote_name}':")
|
|
219
219
|
generator.print_status_report()
|
|
220
|
-
|
|
220
|
+
|
|
221
221
|
# Start the session (uncomment to actually start)
|
|
222
222
|
# start_result = generator.start_zellij_session()
|
|
223
223
|
# print(f"Session start result: {start_result}")
|
|
224
|
-
|
|
224
|
+
|
|
225
225
|
# Attach to session (uncomment to attach)
|
|
226
226
|
# generator.attach_to_session()
|
|
227
|
-
|
|
227
|
+
|
|
228
228
|
except Exception as e:
|
|
229
229
|
print(f"❌ Error: {e}")
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
import json
|
|
3
3
|
import uuid
|
|
4
|
-
import logging
|
|
5
4
|
from pathlib import Path
|
|
6
5
|
from typing import Optional
|
|
7
6
|
from machineconfig.utils.utils5 import Scheduler
|
|
8
7
|
from machineconfig.cluster.sessions_managers.zellij_local import run_command_in_zellij_tab
|
|
9
8
|
from machineconfig.cluster.sessions_managers.zellij_remote import ZellijRemoteLayoutGenerator
|
|
9
|
+
from machineconfig.logger import get_logger
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
TMP_SERIALIAZATION_DIR = Path.home().joinpath("tmp_results", "session_manager", "zellij", "remote_manager")
|
|
13
|
+
logger = get_logger("cluster.sessions_managers.zellij_remote_manager")
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class ZellijSessionManager:
|
|
@@ -35,14 +36,14 @@ class ZellijSessionManager:
|
|
|
35
36
|
a_cmd = run_command_in_zellij_tab(command=ssh_cmd, tab_name=hostname, cwd=None)
|
|
36
37
|
cmds += a_cmd + "\n"
|
|
37
38
|
return cmds
|
|
38
|
-
|
|
39
|
+
|
|
39
40
|
def kill_all_sessions(self) -> None:
|
|
40
41
|
for an_m in self.managers:
|
|
41
42
|
ZellijRemoteLayoutGenerator.run_remote_command(
|
|
42
43
|
remote_name=an_m.remote_name,
|
|
43
44
|
command="zellij kill-all-sessions --yes"
|
|
44
45
|
)
|
|
45
|
-
|
|
46
|
+
|
|
46
47
|
def start_zellij_sessions(self) -> None:
|
|
47
48
|
for an_m in self.managers:
|
|
48
49
|
an_m.start_zellij_session()
|
|
@@ -65,12 +66,12 @@ class ZellijSessionManager:
|
|
|
65
66
|
for i, key in enumerate(keys):
|
|
66
67
|
if i < len(values):
|
|
67
68
|
status_data.append({"tabName": key, "status": values[i]})
|
|
68
|
-
|
|
69
|
+
|
|
69
70
|
# Check if all stopped
|
|
70
71
|
running_count = sum(1 for item in status_data if item.get("status", {}).get("running", False))
|
|
71
72
|
if running_count == 0: # they all stopped
|
|
72
73
|
sched.max_cycles = sched.cycle # stop the scheduler from calling this routine again
|
|
73
|
-
|
|
74
|
+
|
|
74
75
|
# Print status
|
|
75
76
|
for item in status_data:
|
|
76
77
|
print(f"Tab: {item['tabName']}, Status: {item['status']}")
|
|
@@ -79,26 +80,26 @@ class ZellijSessionManager:
|
|
|
79
80
|
for _idx, an_m in enumerate(self.managers):
|
|
80
81
|
a_status = an_m.check_zellij_session_status()
|
|
81
82
|
statuses.append(a_status)
|
|
82
|
-
|
|
83
|
+
|
|
83
84
|
# Print statuses
|
|
84
85
|
for i, status in enumerate(statuses):
|
|
85
86
|
print(f"Manager {i}: {status}")
|
|
86
|
-
sched = Scheduler(routine=routine, wait_ms=
|
|
87
|
+
sched = Scheduler(routine=routine, wait_ms=60_000, logger=logger)
|
|
87
88
|
sched.run()
|
|
88
89
|
|
|
89
90
|
def save(self, session_id: Optional[str] = None) -> str:
|
|
90
91
|
if session_id is None:
|
|
91
92
|
session_id = str(uuid.uuid4())[:8]
|
|
92
|
-
|
|
93
|
+
|
|
93
94
|
# Create session directory
|
|
94
95
|
session_dir = TMP_SERIALIAZATION_DIR / session_id
|
|
95
96
|
session_dir.mkdir(parents=True, exist_ok=True)
|
|
96
|
-
|
|
97
|
+
|
|
97
98
|
# Save the machine2zellij_tabs configuration
|
|
98
99
|
config_file = session_dir / "machine2zellij_tabs.json"
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
text = json.dumps(self.machine2zellij_tabs, indent=2, ensure_ascii=False)
|
|
101
|
+
config_file.write_text(text, encoding="utf-8")
|
|
102
|
+
|
|
102
103
|
# Save session metadata
|
|
103
104
|
metadata = {
|
|
104
105
|
"session_name_prefix": self.session_name_prefix,
|
|
@@ -107,39 +108,38 @@ class ZellijSessionManager:
|
|
|
107
108
|
"machines": list(self.machine2zellij_tabs.keys())
|
|
108
109
|
}
|
|
109
110
|
metadata_file = session_dir / "metadata.json"
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
text = json.dumps(metadata, indent=2, ensure_ascii=False)
|
|
112
|
+
metadata_file.write_text(text, encoding="utf-8")
|
|
113
|
+
|
|
113
114
|
# Save each ZellijRemoteLayoutGenerator
|
|
114
115
|
managers_dir = session_dir / "managers"
|
|
115
116
|
managers_dir.mkdir(exist_ok=True)
|
|
116
|
-
|
|
117
|
+
|
|
117
118
|
for i, manager in enumerate(self.managers):
|
|
118
119
|
manager_file = managers_dir / f"manager_{i}_{manager.remote_name}.json"
|
|
119
120
|
manager.to_json(str(manager_file))
|
|
120
|
-
|
|
121
|
-
logging.info(f"✅ Saved ZellijSessionManager session to: {session_dir}")
|
|
121
|
+
logger.info(f"✅ Saved ZellijSessionManager session to: {session_dir}")
|
|
122
122
|
return session_id
|
|
123
123
|
|
|
124
124
|
@classmethod
|
|
125
125
|
def load(cls, session_id: str) -> 'ZellijSessionManager':
|
|
126
126
|
session_dir = TMP_SERIALIAZATION_DIR / session_id
|
|
127
|
-
|
|
127
|
+
|
|
128
128
|
if not session_dir.exists():
|
|
129
|
-
raise FileNotFoundError(f"Session directory not found: {session_dir}")
|
|
129
|
+
raise FileNotFoundError(f"Session directory not found: {session_dir}")
|
|
130
130
|
config_file = session_dir / "machine2zellij_tabs.json"
|
|
131
131
|
if not config_file.exists():
|
|
132
|
-
raise FileNotFoundError(f"Configuration file not found: {config_file}")
|
|
132
|
+
raise FileNotFoundError(f"Configuration file not found: {config_file}")
|
|
133
133
|
with open(config_file, 'r', encoding='utf-8') as f:
|
|
134
134
|
machine2zellij_tabs = json.load(f)
|
|
135
|
-
|
|
135
|
+
|
|
136
136
|
# Load metadata
|
|
137
137
|
metadata_file = session_dir / "metadata.json"
|
|
138
138
|
session_name_prefix = "JobMgr" # default fallback
|
|
139
139
|
if metadata_file.exists():
|
|
140
140
|
with open(metadata_file, 'r', encoding='utf-8') as f:
|
|
141
141
|
metadata = json.load(f)
|
|
142
|
-
session_name_prefix = metadata.get("session_name_prefix", "JobMgr")
|
|
142
|
+
session_name_prefix = metadata.get("session_name_prefix", "JobMgr")
|
|
143
143
|
# Create new instance (this will create new managers)
|
|
144
144
|
instance = cls(machine2zellij_tabs=machine2zellij_tabs, session_name_prefix=session_name_prefix)
|
|
145
145
|
# Load saved managers to restore their states
|
|
@@ -148,41 +148,41 @@ class ZellijSessionManager:
|
|
|
148
148
|
# Clear the auto-created managers and load the saved ones
|
|
149
149
|
instance.managers = []
|
|
150
150
|
# Get all manager files and sort them
|
|
151
|
-
manager_files = sorted(managers_dir.glob("manager_*.json"))
|
|
151
|
+
manager_files = sorted(managers_dir.glob("manager_*.json"))
|
|
152
152
|
for manager_file in manager_files:
|
|
153
153
|
try:
|
|
154
154
|
loaded_manager = ZellijRemoteLayoutGenerator.from_json(str(manager_file))
|
|
155
155
|
instance.managers.append(loaded_manager)
|
|
156
156
|
except Exception as e:
|
|
157
|
-
|
|
158
|
-
|
|
157
|
+
logger.warning(f"Failed to load manager from {manager_file}: {e}")
|
|
158
|
+
logger.info(f"✅ Loaded ZellijSessionManager session from: {session_dir}")
|
|
159
159
|
return instance
|
|
160
160
|
|
|
161
161
|
@staticmethod
|
|
162
162
|
def list_saved_sessions() -> list[str]:
|
|
163
163
|
if not TMP_SERIALIAZATION_DIR.exists():
|
|
164
164
|
return []
|
|
165
|
-
|
|
165
|
+
|
|
166
166
|
sessions = []
|
|
167
167
|
for item in TMP_SERIALIAZATION_DIR.iterdir():
|
|
168
168
|
if item.is_dir() and (item / "metadata.json").exists():
|
|
169
169
|
sessions.append(item.name)
|
|
170
|
-
|
|
170
|
+
|
|
171
171
|
return sorted(sessions)
|
|
172
172
|
|
|
173
173
|
@staticmethod
|
|
174
174
|
def delete_session(session_id: str) -> bool:
|
|
175
175
|
session_dir = TMP_SERIALIAZATION_DIR / session_id
|
|
176
|
-
|
|
176
|
+
|
|
177
177
|
if not session_dir.exists():
|
|
178
|
-
|
|
178
|
+
logger.warning(f"Session directory not found: {session_dir}")
|
|
179
179
|
return False
|
|
180
|
-
|
|
180
|
+
|
|
181
181
|
try:
|
|
182
182
|
import shutil
|
|
183
183
|
shutil.rmtree(session_dir)
|
|
184
|
-
|
|
184
|
+
logger.info(f"✅ Deleted session: {session_id}")
|
|
185
185
|
return True
|
|
186
186
|
except Exception as e:
|
|
187
|
-
|
|
187
|
+
logger.error(f"Failed to delete session {session_id}: {e}")
|
|
188
188
|
return False
|
|
@@ -7,56 +7,56 @@ from machineconfig.cluster.sessions_managers.zellij_remote import ZellijRemoteLa
|
|
|
7
7
|
|
|
8
8
|
def example_usage():
|
|
9
9
|
"""Demonstrate the refactored modular usage."""
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
# Sample tab configuration
|
|
12
12
|
sample_tabs = {
|
|
13
13
|
"🤖Bot1": ("~/code/bytesense/bithence", "~/scripts/fire -mO go1.py bot1 --kw create_new_bot True"),
|
|
14
|
-
"🤖Bot2": ("~/code/bytesense/bithence", "~/scripts/fire -mO go2.py bot2 --kw create_new_bot True"),
|
|
14
|
+
"🤖Bot2": ("~/code/bytesense/bithence", "~/scripts/fire -mO go2.py bot2 --kw create_new_bot True"),
|
|
15
15
|
"📊Monitor": ("~", "htop"),
|
|
16
16
|
"📝Logs": ("/var/log", "tail -f /var/log/app.log")
|
|
17
17
|
}
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
# Replace 'myserver' with an actual SSH config alias
|
|
20
20
|
remote_name = "myserver" # This should be in ~/.ssh/config
|
|
21
21
|
session_name = "test_remote_session"
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
try:
|
|
24
24
|
# Create layout using the remote generator
|
|
25
25
|
generator = ZellijRemoteLayoutGenerator(remote_name=remote_name, session_name_prefix=session_name)
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
# Create layout file
|
|
28
28
|
layout_path = generator.create_zellij_layout(sample_tabs)
|
|
29
29
|
print(f"✅ Remote layout created successfully: {layout_path}")
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
# Preview the layout content
|
|
32
32
|
preview = generator.get_layout_preview(sample_tabs)
|
|
33
33
|
print(f"📄 Layout preview:\n{preview}")
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
# Check status using the modular components
|
|
36
36
|
print(f"\n🔍 Checking command status on remote '{remote_name}':")
|
|
37
37
|
generator.print_status_report()
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
# The individual components can also be used directly:
|
|
40
40
|
print(f"\n🔧 Direct component usage examples:")
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
# Use remote executor directly
|
|
43
43
|
print(f"Remote executor: {generator.remote_executor.remote_name}")
|
|
44
|
-
|
|
45
|
-
# Use layout generator directly
|
|
44
|
+
|
|
45
|
+
# Use layout generator directly
|
|
46
46
|
layout_content = generator.layout_generator.generate_layout_content(sample_tabs)
|
|
47
47
|
print(f"Layout content length: {len(layout_content)} characters")
|
|
48
|
-
|
|
48
|
+
|
|
49
49
|
# Use process monitor directly
|
|
50
50
|
status = generator.process_monitor.check_all_commands_status(sample_tabs)
|
|
51
51
|
print(f"Command status check completed for {len(status)} commands")
|
|
52
|
-
|
|
52
|
+
|
|
53
53
|
print("\n✅ All modular components working correctly!")
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
# Uncomment these to actually start and attach to the session:
|
|
56
56
|
# start_result = generator.start_zellij_session()
|
|
57
57
|
# print(f"Session start result: {start_result}")
|
|
58
58
|
# generator.attach_to_session()
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
except Exception as e:
|
|
61
61
|
print(f"❌ Error: {e}")
|
|
62
62
|
|
|
@@ -17,7 +17,7 @@ console = Console()
|
|
|
17
17
|
|
|
18
18
|
class LayoutGenerator:
|
|
19
19
|
"""Handles generation of Zellij KDL layout files."""
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
LAYOUT_TEMPLATE = """layout {
|
|
22
22
|
default_tab_template {
|
|
23
23
|
// the default zellij tab-bar and status bar plugins
|
|
@@ -27,29 +27,29 @@ class LayoutGenerator:
|
|
|
27
27
|
children
|
|
28
28
|
}
|
|
29
29
|
"""
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
@staticmethod
|
|
32
32
|
def generate_random_suffix(length: int = 8) -> str:
|
|
33
33
|
"""Generate a random string suffix for unique layout file names."""
|
|
34
34
|
return ''.join(random.choices(string.ascii_lowercase + string.digits, k=length))
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
@staticmethod
|
|
37
37
|
def parse_command(command: str) -> Tuple[str, List[str]]:
|
|
38
38
|
"""Parse a command string into command and arguments."""
|
|
39
39
|
try:
|
|
40
40
|
parts = shlex.split(command)
|
|
41
|
-
if not parts:
|
|
41
|
+
if not parts:
|
|
42
42
|
raise ValueError("Empty command provided")
|
|
43
43
|
return parts[0], parts[1:] if len(parts) > 1 else []
|
|
44
44
|
except ValueError as e:
|
|
45
45
|
logger.error(f"Error parsing command '{command}': {e}")
|
|
46
46
|
parts = command.split()
|
|
47
47
|
return parts[0] if parts else "", parts[1:] if len(parts) > 1 else []
|
|
48
|
-
|
|
48
|
+
|
|
49
49
|
@staticmethod
|
|
50
50
|
def format_args_for_kdl(args: List[str]) -> str:
|
|
51
51
|
"""Format command arguments for KDL syntax."""
|
|
52
|
-
if not args:
|
|
52
|
+
if not args:
|
|
53
53
|
return ""
|
|
54
54
|
formatted_args = []
|
|
55
55
|
for arg in args:
|
|
@@ -59,7 +59,7 @@ class LayoutGenerator:
|
|
|
59
59
|
else:
|
|
60
60
|
formatted_args.append(f'"{arg}"')
|
|
61
61
|
return " ".join(formatted_args)
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
@staticmethod
|
|
64
64
|
def create_tab_section(tab_name: str, cwd: str, command: str) -> str:
|
|
65
65
|
"""Create a KDL tab section for the layout."""
|
|
@@ -67,60 +67,59 @@ class LayoutGenerator:
|
|
|
67
67
|
args_str = LayoutGenerator.format_args_for_kdl(args)
|
|
68
68
|
tab_cwd = cwd or "~"
|
|
69
69
|
escaped_tab_name = tab_name.replace('"', '\\"')
|
|
70
|
-
|
|
70
|
+
|
|
71
71
|
tab_section = f' tab name="{escaped_tab_name}" cwd="{tab_cwd}" {{\n'
|
|
72
72
|
tab_section += f' pane command="{cmd}" {{\n'
|
|
73
|
-
if args_str:
|
|
73
|
+
if args_str:
|
|
74
74
|
tab_section += f' args {args_str}\n'
|
|
75
75
|
tab_section += ' }\n }\n'
|
|
76
76
|
return tab_section
|
|
77
|
-
|
|
77
|
+
|
|
78
78
|
@staticmethod
|
|
79
79
|
def validate_tab_config(tab_config: Dict[str, Tuple[str, str]]) -> None:
|
|
80
80
|
"""Validate tab configuration format and content."""
|
|
81
|
-
if not tab_config:
|
|
81
|
+
if not tab_config:
|
|
82
82
|
raise ValueError("Tab configuration cannot be empty")
|
|
83
83
|
for tab_name, (cwd, command) in tab_config.items():
|
|
84
|
-
if not tab_name.strip():
|
|
84
|
+
if not tab_name.strip():
|
|
85
85
|
raise ValueError(f"Invalid tab name: {tab_name}")
|
|
86
|
-
if not command.strip():
|
|
86
|
+
if not command.strip():
|
|
87
87
|
raise ValueError(f"Invalid command for tab '{tab_name}': {command}")
|
|
88
|
-
if not cwd.strip():
|
|
88
|
+
if not cwd.strip():
|
|
89
89
|
raise ValueError(f"Invalid cwd for tab '{tab_name}': {cwd}")
|
|
90
|
-
|
|
90
|
+
|
|
91
91
|
def generate_layout_content(self, tab_config: Dict[str, Tuple[str, str]]) -> str:
|
|
92
92
|
"""Generate complete KDL layout content."""
|
|
93
93
|
self.validate_tab_config(tab_config)
|
|
94
|
-
|
|
94
|
+
|
|
95
95
|
layout_content = self.LAYOUT_TEMPLATE
|
|
96
96
|
for tab_name, (cwd, command) in tab_config.items():
|
|
97
97
|
layout_content += "\n" + self.create_tab_section(tab_name, cwd, command)
|
|
98
98
|
layout_content += "\n}\n"
|
|
99
|
-
|
|
99
|
+
|
|
100
100
|
return layout_content
|
|
101
|
-
|
|
102
|
-
def create_layout_file(self, tab_config: Dict[str, Tuple[str, str]],
|
|
101
|
+
|
|
102
|
+
def create_layout_file(self, tab_config: Dict[str, Tuple[str, str]],
|
|
103
103
|
output_dir: Path, session_name: str) -> str:
|
|
104
104
|
"""Create a layout file and return its absolute path."""
|
|
105
105
|
self.validate_tab_config(tab_config)
|
|
106
|
-
|
|
106
|
+
|
|
107
107
|
# Generate unique suffix for this layout
|
|
108
108
|
random_suffix = self.generate_random_suffix()
|
|
109
109
|
layout_content = self.generate_layout_content(tab_config)
|
|
110
|
-
|
|
110
|
+
|
|
111
111
|
try:
|
|
112
112
|
# Create output directory if it doesn't exist
|
|
113
113
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
114
114
|
layout_file = output_dir / f"zellij_layout_{session_name}_{random_suffix}.kdl"
|
|
115
|
-
|
|
115
|
+
|
|
116
116
|
# Write layout file
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
layout_file.write_text(layout_content, encoding="utf-8")
|
|
118
|
+
|
|
120
119
|
# Enhanced Rich logging
|
|
121
120
|
console.print(f"[bold green]✅ Zellij layout file created:[/bold green] [cyan]{layout_file.absolute()}[/cyan]")
|
|
122
121
|
return str(layout_file.absolute())
|
|
123
|
-
|
|
122
|
+
|
|
124
123
|
except OSError as e:
|
|
125
124
|
logger.error(f"Failed to create layout file: {e}")
|
|
126
125
|
raise
|