machineconfig 2.2__py3-none-any.whl → 2.4__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/__init__.py +30 -0
- machineconfig/cluster/sessions_managers/enhanced_command_runner.py +0 -2
- machineconfig/cluster/sessions_managers/layout_types.py +29 -0
- machineconfig/cluster/sessions_managers/wt_local.py +68 -62
- machineconfig/cluster/sessions_managers/wt_local_manager.py +51 -22
- machineconfig/cluster/sessions_managers/wt_remote.py +30 -108
- machineconfig/cluster/sessions_managers/wt_remote_manager.py +14 -11
- machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +33 -37
- machineconfig/cluster/sessions_managers/wt_utils/process_monitor.py +22 -17
- machineconfig/cluster/sessions_managers/wt_utils/session_manager.py +59 -10
- machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +16 -14
- machineconfig/cluster/sessions_managers/zellij_local.py +75 -57
- machineconfig/cluster/sessions_managers/zellij_local_manager.py +51 -23
- machineconfig/cluster/sessions_managers/zellij_remote.py +47 -27
- machineconfig/cluster/sessions_managers/zellij_remote_manager.py +13 -12
- machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +14 -10
- machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +31 -15
- machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +47 -21
- machineconfig/cluster/sessions_managers/zellij_utils/session_manager.py +1 -1
- machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +8 -7
- machineconfig/cluster/templates/utils.py +1 -1
- machineconfig/profile/create.py +4 -0
- machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/linux/checkout_versions +1 -7
- machineconfig/scripts/linux/choose_wezterm_theme +1 -7
- machineconfig/scripts/linux/cloud_copy +1 -8
- machineconfig/scripts/linux/cloud_manager +1 -7
- machineconfig/scripts/linux/cloud_mount +1 -23
- machineconfig/scripts/linux/cloud_repo_sync +1 -21
- machineconfig/scripts/linux/cloud_sync +1 -23
- machineconfig/scripts/linux/croshell +1 -23
- machineconfig/scripts/linux/devops +0 -21
- machineconfig/scripts/linux/fire +1 -27
- machineconfig/scripts/linux/fire_agents +1 -26
- machineconfig/scripts/linux/gh_models +1 -10
- machineconfig/scripts/linux/kill_process +1 -9
- machineconfig/scripts/linux/mcinit +1 -26
- machineconfig/scripts/linux/mount_nfs +1 -13
- machineconfig/scripts/linux/repos +1 -23
- machineconfig/scripts/linux/scheduler +1 -7
- machineconfig/scripts/linux/start_slidev +1 -22
- machineconfig/scripts/linux/start_terminals +1 -9
- machineconfig/scripts/linux/url2md +1 -9
- machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/croshell.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc +0 -0
- machineconfig/scripts/python/cloud_mount.py +4 -2
- machineconfig/scripts/python/cloud_repo_sync.py +5 -2
- machineconfig/scripts/python/cloud_sync.py +4 -2
- machineconfig/scripts/python/croshell.py +5 -3
- machineconfig/scripts/python/devops.py +3 -2
- machineconfig/scripts/python/devops_devapps_install.py +1 -0
- machineconfig/scripts/python/fire_agents.py +6 -6
- machineconfig/scripts/python/fire_jobs.py +26 -72
- machineconfig/scripts/python/fire_jobs_args_helper.py +84 -0
- machineconfig/scripts/python/fire_jobs_layout_helper.py +66 -0
- machineconfig/scripts/python/helpers/helpers4.py +0 -1
- machineconfig/scripts/python/mount_nfs.py +12 -8
- machineconfig/scripts/python/mount_nw_drive.py +6 -6
- machineconfig/scripts/python/mount_ssh.py +4 -2
- machineconfig/scripts/python/start_slidev.py +4 -2
- machineconfig/scripts/python/start_terminals.py +4 -2
- machineconfig/scripts/python/wifi_conn.py +0 -1
- machineconfig/settings/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/settings/shells/ipy/profiles/default/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/settings/shells/ipy/profiles/default/startup/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/utils/code.py +5 -6
- machineconfig/utils/installer_utils/installer_abc.py +0 -1
- machineconfig/utils/options.py +7 -7
- machineconfig/utils/path.py +12 -12
- machineconfig/utils/path_reduced.py +6 -1
- machineconfig/utils/source_of_truth.py +2 -2
- machineconfig/utils/ssh.py +11 -1
- machineconfig/utils/upgrade_packages.py +12 -12
- {machineconfig-2.2.dist-info → machineconfig-2.4.dist-info}/METADATA +1 -1
- {machineconfig-2.2.dist-info → machineconfig-2.4.dist-info}/RECORD +90 -93
- machineconfig-2.4.dist-info/entry_points.txt +2 -0
- machineconfig/cluster/sessions_managers/archive/create_zellij_template.py +0 -60
- machineconfig/cluster/sessions_managers/archive/session_managers.py +0 -183
- machineconfig/cluster/sessions_managers/demo_rich_zellij.py +0 -0
- machineconfig/jobs/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/linux/archive/tmate_conn +0 -12
- machineconfig/scripts/linux/archive/tmate_start +0 -12
- machineconfig/scripts/linux/archive/transfer_wsl_win +0 -5
- machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_agents.cpython-313.pyc +0 -0
- /machineconfig/cluster/{cloud_manager.py → remote/cloud_manager.py} +0 -0
- /machineconfig/cluster/{data_transfer.py → remote/data_transfer.py} +0 -0
- /machineconfig/cluster/{distribute.py → remote/distribute.py} +0 -0
- /machineconfig/cluster/{file_manager.py → remote/file_manager.py} +0 -0
- /machineconfig/cluster/{job_params.py → remote/job_params.py} +0 -0
- /machineconfig/cluster/{loader_runner.py → remote/loader_runner.py} +0 -0
- /machineconfig/cluster/{remote_machine.py → remote/remote_machine.py} +0 -0
- /machineconfig/cluster/{script_execution.py → remote/script_execution.py} +0 -0
- /machineconfig/cluster/{script_notify_upon_completion.py → remote/script_notify_upon_completion.py} +0 -0
- /machineconfig/{cluster/sessions_managers/archive/__init__.py → scripts/python/fire_jobs_streamlit_helper.py} +0 -0
- {machineconfig-2.2.dist-info → machineconfig-2.4.dist-info}/WHEEL +0 -0
- {machineconfig-2.2.dist-info → machineconfig-2.4.dist-info}/top_level.txt +0 -0
|
@@ -4,9 +4,10 @@ Status reporting utilities for Windows Terminal layouts and sessions.
|
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
import logging
|
|
7
|
-
from typing import Dict, Any,
|
|
8
|
-
from .process_monitor import WTProcessMonitor
|
|
9
|
-
from .session_manager import WTSessionManager
|
|
7
|
+
from typing import Dict, Any, List
|
|
8
|
+
from machineconfig.cluster.sessions_managers.wt_utils.process_monitor import WTProcessMonitor
|
|
9
|
+
from machineconfig.cluster.sessions_managers.wt_utils.session_manager import WTSessionManager
|
|
10
|
+
from machineconfig.cluster.sessions_managers.layout_types import TabConfig
|
|
10
11
|
|
|
11
12
|
logger = logging.getLogger(__name__)
|
|
12
13
|
|
|
@@ -18,10 +19,10 @@ class WTStatusReporter:
|
|
|
18
19
|
self.process_monitor = process_monitor
|
|
19
20
|
self.session_manager = session_manager
|
|
20
21
|
|
|
21
|
-
def get_comprehensive_status(self,
|
|
22
|
+
def get_comprehensive_status(self, tabs: List[TabConfig]) -> Dict[str, Any]:
|
|
22
23
|
"""Get comprehensive status including Windows Terminal session and all commands."""
|
|
23
24
|
wt_status = self.session_manager.check_wt_session_status()
|
|
24
|
-
commands_status = self.process_monitor.check_all_commands_status(
|
|
25
|
+
commands_status = self.process_monitor.check_all_commands_status(tabs)
|
|
25
26
|
|
|
26
27
|
running_count = sum(1 for status in commands_status.values() if status.get("running", False))
|
|
27
28
|
total_count = len(commands_status)
|
|
@@ -32,9 +33,9 @@ class WTStatusReporter:
|
|
|
32
33
|
"summary": {"total_commands": total_count, "running_commands": running_count, "stopped_commands": total_count - running_count, "session_healthy": wt_status.get("session_exists", False), "location": wt_status.get("location", "unknown")},
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
def print_status_report(self,
|
|
36
|
+
def print_status_report(self, tabs: List[TabConfig]) -> None:
|
|
36
37
|
"""Print a comprehensive status report for the Windows Terminal session."""
|
|
37
|
-
status = self.get_comprehensive_status(
|
|
38
|
+
status = self.get_comprehensive_status(tabs)
|
|
38
39
|
wt_session = status["wt_session"]
|
|
39
40
|
commands = status["commands"]
|
|
40
41
|
summary = status["summary"]
|
|
@@ -156,10 +157,10 @@ class WTStatusReporter:
|
|
|
156
157
|
|
|
157
158
|
print("=" * 80)
|
|
158
159
|
|
|
159
|
-
def generate_status_summary(self,
|
|
160
|
+
def generate_status_summary(self, tabs: List[TabConfig]) -> Dict[str, Any]:
|
|
160
161
|
"""Generate a concise status summary suitable for monitoring."""
|
|
161
162
|
try:
|
|
162
|
-
comprehensive_status = self.get_comprehensive_status(
|
|
163
|
+
comprehensive_status = self.get_comprehensive_status(tabs)
|
|
163
164
|
wt_overview = self.get_windows_terminal_overview()
|
|
164
165
|
|
|
165
166
|
summary = comprehensive_status["summary"]
|
|
@@ -182,17 +183,18 @@ class WTStatusReporter:
|
|
|
182
183
|
logger.error(f"Failed to generate status summary: {e}")
|
|
183
184
|
return {"error": str(e), "session_name": self.session_manager.session_name, "location": self.process_monitor.location_name}
|
|
184
185
|
|
|
185
|
-
def check_tab_specific_status(self, tab_name: str,
|
|
186
|
+
def check_tab_specific_status(self, tab_name: str, tabs: List[TabConfig]) -> Dict[str, Any]:
|
|
186
187
|
"""Get detailed status for a specific tab."""
|
|
187
|
-
if
|
|
188
|
+
if next((t for t in tabs if t["tabName"] == tab_name), None) is None:
|
|
188
189
|
return {"error": f"Tab '{tab_name}' not found in configuration", "tab_name": tab_name}
|
|
189
190
|
|
|
190
191
|
try:
|
|
191
|
-
cmd_status = self.process_monitor.check_command_status(tab_name,
|
|
192
|
+
cmd_status = self.process_monitor.check_command_status(tab_name, tabs)
|
|
192
193
|
|
|
193
194
|
# Add additional context
|
|
194
|
-
|
|
195
|
-
|
|
195
|
+
the_tab = next((t for t in tabs if t["tabName"] == tab_name), None)
|
|
196
|
+
if the_tab is not None:
|
|
197
|
+
cmd_status["tab_config"] = {"working_directory": the_tab["startDir"], "command": the_tab["command"]}
|
|
196
198
|
|
|
197
199
|
return cmd_status
|
|
198
200
|
except Exception as e:
|
|
@@ -10,6 +10,8 @@ import logging
|
|
|
10
10
|
|
|
11
11
|
from rich.console import Console
|
|
12
12
|
|
|
13
|
+
from machineconfig.cluster.sessions_managers.layout_types import LayoutConfig, TabConfig
|
|
14
|
+
|
|
13
15
|
logging.basicConfig(level=logging.INFO)
|
|
14
16
|
logger = logging.getLogger(__name__)
|
|
15
17
|
console = Console()
|
|
@@ -19,7 +21,7 @@ TMP_LAYOUT_DIR = Path.home().joinpath("tmp_results", "session_manager", "zellij"
|
|
|
19
21
|
class ZellijLayoutGenerator:
|
|
20
22
|
def __init__(self):
|
|
21
23
|
self.session_name: Optional[str] = None
|
|
22
|
-
self.
|
|
24
|
+
self.layout_config: Optional[LayoutConfig] = None # Store the complete layout config
|
|
23
25
|
self.layout_path: Optional[str] = None # Store the full path to the layout file
|
|
24
26
|
self.layout_template = """layout {
|
|
25
27
|
default_tab_template {
|
|
@@ -62,7 +64,11 @@ class ZellijLayoutGenerator:
|
|
|
62
64
|
return " ".join(formatted_args)
|
|
63
65
|
|
|
64
66
|
@staticmethod
|
|
65
|
-
def _create_tab_section(
|
|
67
|
+
def _create_tab_section(tab_config: TabConfig) -> str:
|
|
68
|
+
tab_name = tab_config["tabName"]
|
|
69
|
+
cwd = tab_config["startDir"]
|
|
70
|
+
command = tab_config["command"]
|
|
71
|
+
|
|
66
72
|
cmd, args = ZellijLayoutGenerator._parse_command(command)
|
|
67
73
|
args_str = ZellijLayoutGenerator._format_args_for_kdl(args)
|
|
68
74
|
tab_cwd = cwd or "~"
|
|
@@ -75,36 +81,38 @@ class ZellijLayoutGenerator:
|
|
|
75
81
|
return tab_section
|
|
76
82
|
|
|
77
83
|
@staticmethod
|
|
78
|
-
def
|
|
79
|
-
if not
|
|
80
|
-
raise ValueError("
|
|
81
|
-
for
|
|
82
|
-
if not
|
|
83
|
-
raise ValueError(f"Invalid tab name: {
|
|
84
|
-
if not command.strip():
|
|
85
|
-
raise ValueError(f"Invalid command for tab '{
|
|
86
|
-
if not
|
|
87
|
-
raise ValueError(f"Invalid
|
|
88
|
-
|
|
89
|
-
def create_zellij_layout(self,
|
|
90
|
-
ZellijLayoutGenerator.
|
|
84
|
+
def _validate_layout_config(layout_config: LayoutConfig) -> None:
|
|
85
|
+
if not layout_config["layoutTabs"]:
|
|
86
|
+
raise ValueError("Layout must contain at least one tab")
|
|
87
|
+
for tab in layout_config["layoutTabs"]:
|
|
88
|
+
if not tab["tabName"].strip():
|
|
89
|
+
raise ValueError(f"Invalid tab name: {tab['tabName']}")
|
|
90
|
+
if not tab["command"].strip():
|
|
91
|
+
raise ValueError(f"Invalid command for tab '{tab['tabName']}': {tab['command']}")
|
|
92
|
+
if not tab["startDir"].strip():
|
|
93
|
+
raise ValueError(f"Invalid startDir for tab '{tab['tabName']}': {tab['startDir']}")
|
|
94
|
+
|
|
95
|
+
def create_zellij_layout(self, layout_config: LayoutConfig, output_dir: Optional[str] = None, session_name: Optional[str] = None) -> str:
|
|
96
|
+
ZellijLayoutGenerator._validate_layout_config(layout_config)
|
|
91
97
|
|
|
92
98
|
# Enhanced Rich logging
|
|
93
|
-
tab_count = len(
|
|
94
|
-
|
|
99
|
+
tab_count = len(layout_config["layoutTabs"])
|
|
100
|
+
layout_name = layout_config["layoutName"]
|
|
101
|
+
console.print(f"[bold cyan]📋 Creating Zellij layout[/bold cyan] [bright_green]'{layout_name}' with {tab_count} tabs[/bright_green]")
|
|
95
102
|
|
|
96
103
|
# Display tab summary with emojis and colors
|
|
97
|
-
for
|
|
98
|
-
console.print(f" [yellow]→[/yellow] [bold]{
|
|
104
|
+
for tab in layout_config["layoutTabs"]:
|
|
105
|
+
console.print(f" [yellow]→[/yellow] [bold]{tab['tabName']}[/bold] [dim]in[/dim] [blue]{tab['startDir']}[/blue]")
|
|
99
106
|
|
|
100
|
-
# Store session name and
|
|
101
|
-
self.session_name = session_name or
|
|
102
|
-
self.
|
|
107
|
+
# Store session name and layout config for status checking
|
|
108
|
+
self.session_name = session_name or layout_name
|
|
109
|
+
self.layout_config = layout_config.copy()
|
|
103
110
|
|
|
104
111
|
layout_content = self.layout_template
|
|
105
|
-
for
|
|
106
|
-
layout_content += "\n" + ZellijLayoutGenerator._create_tab_section(
|
|
112
|
+
for tab in layout_config["layoutTabs"]:
|
|
113
|
+
layout_content += "\n" + ZellijLayoutGenerator._create_tab_section(tab)
|
|
107
114
|
layout_content += "\n}\n"
|
|
115
|
+
|
|
108
116
|
try:
|
|
109
117
|
random_suffix = ZellijLayoutGenerator._generate_random_suffix()
|
|
110
118
|
if output_dir:
|
|
@@ -128,7 +136,7 @@ class ZellijLayoutGenerator:
|
|
|
128
136
|
raise
|
|
129
137
|
|
|
130
138
|
@staticmethod
|
|
131
|
-
def get_layout_preview(
|
|
139
|
+
def get_layout_preview(layout_config: LayoutConfig, layout_template: str | None = None) -> str:
|
|
132
140
|
if layout_template is None:
|
|
133
141
|
layout_template = """layout {
|
|
134
142
|
default_tab_template {
|
|
@@ -139,18 +147,26 @@ class ZellijLayoutGenerator:
|
|
|
139
147
|
children
|
|
140
148
|
}
|
|
141
149
|
"""
|
|
142
|
-
ZellijLayoutGenerator.
|
|
150
|
+
ZellijLayoutGenerator._validate_layout_config(layout_config)
|
|
143
151
|
layout_content = layout_template
|
|
144
|
-
for
|
|
145
|
-
layout_content += "\n" + ZellijLayoutGenerator._create_tab_section(
|
|
152
|
+
for tab in layout_config["layoutTabs"]:
|
|
153
|
+
layout_content += "\n" + ZellijLayoutGenerator._create_tab_section(tab)
|
|
146
154
|
return layout_content + "\n}\n"
|
|
147
155
|
|
|
148
156
|
@staticmethod
|
|
149
|
-
def check_command_status(tab_name: str,
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
157
|
+
def check_command_status(tab_name: str, layout_config: LayoutConfig) -> Dict[str, Any]:
|
|
158
|
+
# Find the tab with the given name
|
|
159
|
+
tab_config = None
|
|
160
|
+
for tab in layout_config["layoutTabs"]:
|
|
161
|
+
if tab["tabName"] == tab_name:
|
|
162
|
+
tab_config = tab
|
|
163
|
+
break
|
|
164
|
+
|
|
165
|
+
if tab_config is None:
|
|
166
|
+
return {"status": "unknown", "error": f"Tab '{tab_name}' not found in layout config", "running": False, "pid": None, "command": None, "cwd": None}
|
|
167
|
+
|
|
168
|
+
command = tab_config["command"]
|
|
169
|
+
cwd = tab_config["startDir"]
|
|
154
170
|
cmd, _ = ZellijLayoutGenerator._parse_command(command)
|
|
155
171
|
|
|
156
172
|
try:
|
|
@@ -175,13 +191,14 @@ class ZellijLayoutGenerator:
|
|
|
175
191
|
return {"status": "error", "error": str(e), "running": False, "command": command, "cwd": cwd, "tab_name": tab_name}
|
|
176
192
|
|
|
177
193
|
def check_all_commands_status(self) -> Dict[str, Dict[str, Any]]:
|
|
178
|
-
if not self.
|
|
179
|
-
logger.warning("No
|
|
194
|
+
if not self.layout_config:
|
|
195
|
+
logger.warning("No layout config tracked. Make sure to create a layout first.")
|
|
180
196
|
return {}
|
|
181
197
|
|
|
182
198
|
status_report = {}
|
|
183
|
-
for
|
|
184
|
-
|
|
199
|
+
for tab in self.layout_config["layoutTabs"]:
|
|
200
|
+
tab_name = tab["tabName"]
|
|
201
|
+
status_report[tab_name] = ZellijLayoutGenerator.check_command_status(tab_name, self.layout_config)
|
|
185
202
|
|
|
186
203
|
return status_report
|
|
187
204
|
|
|
@@ -289,20 +306,16 @@ class ZellijLayoutGenerator:
|
|
|
289
306
|
console.print(Panel(summary_text, title="📊 Summary", style="blue"))
|
|
290
307
|
|
|
291
308
|
|
|
292
|
-
def created_zellij_layout(
|
|
309
|
+
def created_zellij_layout(layout_config: LayoutConfig, output_dir: Optional[str] = None) -> str:
|
|
293
310
|
generator = ZellijLayoutGenerator()
|
|
294
|
-
return generator.create_zellij_layout(
|
|
311
|
+
return generator.create_zellij_layout(layout_config, output_dir)
|
|
295
312
|
|
|
296
313
|
|
|
297
|
-
def run_zellij_layout(
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
layout_path = created_zellij_layout(tab_config)
|
|
301
|
-
|
|
302
|
-
# Use enhanced command execution
|
|
314
|
+
def run_zellij_layout(layout_config: LayoutConfig):
|
|
315
|
+
layout_path = created_zellij_layout(layout_config)
|
|
316
|
+
session_name = layout_config["layoutName"]
|
|
303
317
|
try:
|
|
304
|
-
from .enhanced_command_runner import enhanced_zellij_session_start
|
|
305
|
-
|
|
318
|
+
from machineconfig.cluster.sessions_managers.enhanced_command_runner import enhanced_zellij_session_start
|
|
306
319
|
enhanced_zellij_session_start(session_name, layout_path)
|
|
307
320
|
except ImportError:
|
|
308
321
|
# Fallback to original implementation
|
|
@@ -311,7 +324,6 @@ def run_zellij_layout(tab_config: Dict[str, tuple[str, str]], session_name: Opti
|
|
|
311
324
|
|
|
312
325
|
subprocess.run(cmd, shell=True, check=True)
|
|
313
326
|
console.print(f"[bold green]🚀 Zellij layout is running[/bold green] [yellow]@[/yellow] [bold cyan]{session_name}[/bold cyan]")
|
|
314
|
-
return session_name
|
|
315
327
|
|
|
316
328
|
|
|
317
329
|
def run_command_in_zellij_tab(command: str, tab_name: str, cwd: Optional[str]) -> str:
|
|
@@ -336,16 +348,21 @@ zellij action close-pane; sleep 2
|
|
|
336
348
|
|
|
337
349
|
|
|
338
350
|
if __name__ == "__main__":
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
"
|
|
342
|
-
"
|
|
343
|
-
|
|
351
|
+
# Example usage with new schema
|
|
352
|
+
sample_layout: LayoutConfig = {
|
|
353
|
+
"layoutName": "SampleBots",
|
|
354
|
+
"layoutTabs": [
|
|
355
|
+
{"tabName": "🤖Bot1", "startDir": "~/code/bytesense/bithence", "command": "~/scripts/fire -mO go1.py bot1 --kw create_new_bot True"},
|
|
356
|
+
{"tabName": "🤖Bot2", "startDir": "~/code/bytesense/bithence", "command": "~/scripts/fire -mO go2.py bot2 --kw create_new_bot True"},
|
|
357
|
+
{"tabName": "📊Monitor", "startDir": "~", "command": "htop"},
|
|
358
|
+
{"tabName": "📝Logs", "startDir": "/var/log", "command": "tail -f /var/log/app.log"},
|
|
359
|
+
],
|
|
344
360
|
}
|
|
361
|
+
|
|
345
362
|
try:
|
|
346
363
|
# Create layout using the generator directly to access status methods
|
|
347
364
|
generator = ZellijLayoutGenerator()
|
|
348
|
-
layout_path = generator.create_zellij_layout(
|
|
365
|
+
layout_path = generator.create_zellij_layout(sample_layout, session_name="test_session")
|
|
349
366
|
print(f"✅ Layout created successfully: {layout_path}")
|
|
350
367
|
|
|
351
368
|
# Demonstrate status checking
|
|
@@ -354,9 +371,10 @@ if __name__ == "__main__":
|
|
|
354
371
|
|
|
355
372
|
# Individual command status check
|
|
356
373
|
print("\n🔎 Individual command status for Bot1:")
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
374
|
+
if generator.layout_config:
|
|
375
|
+
bot1_status = ZellijLayoutGenerator.check_command_status("🤖Bot1", generator.layout_config)
|
|
376
|
+
print(f"Status: {bot1_status['status']}")
|
|
377
|
+
print(f"Running: {bot1_status['running']}")
|
|
360
378
|
|
|
361
379
|
except Exception as e:
|
|
362
380
|
print(f"❌ Error: {e}")
|
|
@@ -12,6 +12,7 @@ from rich.console import Console
|
|
|
12
12
|
|
|
13
13
|
from machineconfig.utils.utils5 import Scheduler
|
|
14
14
|
from machineconfig.cluster.sessions_managers.zellij_local import ZellijLayoutGenerator
|
|
15
|
+
from machineconfig.cluster.sessions_managers.layout_types import LayoutConfig
|
|
15
16
|
|
|
16
17
|
logging.basicConfig(level=logging.INFO)
|
|
17
18
|
logger = logging.getLogger(__name__)
|
|
@@ -23,24 +24,25 @@ TMP_SERIALIZATION_DIR = Path.home().joinpath("tmp_results", "session_manager", "
|
|
|
23
24
|
class ZellijLocalManager:
|
|
24
25
|
"""Manages multiple local zellij sessions and monitors their tabs and processes."""
|
|
25
26
|
|
|
26
|
-
def __init__(self,
|
|
27
|
+
def __init__(self, session_layouts: list[LayoutConfig], session_name_prefix: str = "LocalJobMgr"):
|
|
27
28
|
"""
|
|
28
29
|
Initialize the local zellij manager.
|
|
29
30
|
|
|
30
31
|
Args:
|
|
31
|
-
|
|
32
|
-
Format: {session_name:
|
|
32
|
+
session_layouts: Dict mapping session names to their layout configs
|
|
33
|
+
Format: {session_name: LayoutConfig, ...}
|
|
33
34
|
session_name_prefix: Prefix for session names
|
|
34
35
|
"""
|
|
35
36
|
self.session_name_prefix = session_name_prefix
|
|
36
|
-
self.
|
|
37
|
+
self.session_layouts = session_layouts # Store the original config
|
|
37
38
|
self.managers: List[ZellijLayoutGenerator] = []
|
|
38
39
|
|
|
39
40
|
# Create a ZellijLayoutGenerator for each session
|
|
40
|
-
for
|
|
41
|
+
for layout_config in session_layouts:
|
|
42
|
+
session_name = layout_config["layoutName"].replace(" ", "_")
|
|
41
43
|
manager = ZellijLayoutGenerator()
|
|
42
44
|
full_session_name = f"{self.session_name_prefix}_{session_name}"
|
|
43
|
-
manager.create_zellij_layout(
|
|
45
|
+
manager.create_zellij_layout(layout_config=layout_config, session_name=full_session_name)
|
|
44
46
|
self.managers.append(manager)
|
|
45
47
|
|
|
46
48
|
# Enhanced Rich logging for initialization
|
|
@@ -326,13 +328,13 @@ class ZellijLocalManager:
|
|
|
326
328
|
session_dir = TMP_SERIALIZATION_DIR / session_id
|
|
327
329
|
session_dir.mkdir(parents=True, exist_ok=True)
|
|
328
330
|
|
|
329
|
-
# Save the
|
|
330
|
-
config_file = session_dir / "
|
|
331
|
-
text = json.dumps(self.
|
|
331
|
+
# Save the session_layouts configuration
|
|
332
|
+
config_file = session_dir / "session_layouts.json"
|
|
333
|
+
text = json.dumps(self.session_layouts, indent=2, ensure_ascii=False)
|
|
332
334
|
config_file.write_text(text, encoding="utf-8")
|
|
333
335
|
|
|
334
336
|
# Save metadata
|
|
335
|
-
metadata = {"session_name_prefix": self.session_name_prefix, "created_at": str(datetime.now()), "num_managers": len(self.managers), "sessions":
|
|
337
|
+
metadata = {"session_name_prefix": self.session_name_prefix, "created_at": str(datetime.now()), "num_managers": len(self.managers), "sessions": [item["layoutName"] for item in self.session_layouts], "manager_type": "ZellijLocalManager"}
|
|
336
338
|
metadata_file = session_dir / "metadata.json"
|
|
337
339
|
text = json.dumps(metadata, indent=2, ensure_ascii=False)
|
|
338
340
|
metadata_file.write_text(text, encoding="utf-8")
|
|
@@ -342,7 +344,7 @@ class ZellijLocalManager:
|
|
|
342
344
|
managers_dir.mkdir(exist_ok=True)
|
|
343
345
|
|
|
344
346
|
for i, manager in enumerate(self.managers):
|
|
345
|
-
manager_data = {"session_name": manager.session_name, "
|
|
347
|
+
manager_data = {"session_name": manager.session_name, "layout_config": manager.layout_config, "layout_path": manager.layout_path}
|
|
346
348
|
manager_file = managers_dir / f"manager_{i}_{manager.session_name}.json"
|
|
347
349
|
text = json.dumps(manager_data, indent=2, ensure_ascii=False)
|
|
348
350
|
manager_file.write_text(text, encoding="utf-8")
|
|
@@ -359,12 +361,12 @@ class ZellijLocalManager:
|
|
|
359
361
|
raise FileNotFoundError(f"Session directory not found: {session_dir}")
|
|
360
362
|
|
|
361
363
|
# Load configuration
|
|
362
|
-
config_file = session_dir / "
|
|
364
|
+
config_file = session_dir / "session_layouts.json"
|
|
363
365
|
if not config_file.exists():
|
|
364
366
|
raise FileNotFoundError(f"Configuration file not found: {config_file}")
|
|
365
367
|
|
|
366
368
|
with open(config_file, "r", encoding="utf-8") as f:
|
|
367
|
-
|
|
369
|
+
session_layouts = json.load(f)
|
|
368
370
|
|
|
369
371
|
# Load metadata
|
|
370
372
|
metadata_file = session_dir / "metadata.json"
|
|
@@ -375,7 +377,7 @@ class ZellijLocalManager:
|
|
|
375
377
|
session_name_prefix = metadata.get("session_name_prefix", "LocalJobMgr")
|
|
376
378
|
|
|
377
379
|
# Create new instance
|
|
378
|
-
instance = cls(
|
|
380
|
+
instance = cls(session_layouts=session_layouts, session_name_prefix=session_name_prefix)
|
|
379
381
|
|
|
380
382
|
# Load saved manager states
|
|
381
383
|
managers_dir = session_dir / "managers"
|
|
@@ -391,7 +393,7 @@ class ZellijLocalManager:
|
|
|
391
393
|
# Recreate the manager
|
|
392
394
|
manager = ZellijLayoutGenerator()
|
|
393
395
|
manager.session_name = manager_data["session_name"]
|
|
394
|
-
manager.
|
|
396
|
+
manager.layout_config = manager_data["layout_config"]
|
|
395
397
|
manager.layout_path = manager_data["layout_path"]
|
|
396
398
|
|
|
397
399
|
instance.managers.append(manager)
|
|
@@ -452,7 +454,13 @@ class ZellijLocalManager:
|
|
|
452
454
|
continue # Skip managers without a session name
|
|
453
455
|
is_active = any(session_name in session for session in all_sessions)
|
|
454
456
|
|
|
455
|
-
|
|
457
|
+
tab_info = []
|
|
458
|
+
tab_count = 0
|
|
459
|
+
if manager.layout_config:
|
|
460
|
+
tab_count = len(manager.layout_config["layoutTabs"])
|
|
461
|
+
tab_info = [tab["tabName"] for tab in manager.layout_config["layoutTabs"]]
|
|
462
|
+
|
|
463
|
+
active_sessions.append({"session_name": session_name, "is_active": is_active, "tab_count": tab_count, "tabs": tab_info})
|
|
456
464
|
|
|
457
465
|
except Exception as e:
|
|
458
466
|
logger.error(f"Error listing active sessions: {e}")
|
|
@@ -461,13 +469,33 @@ class ZellijLocalManager:
|
|
|
461
469
|
|
|
462
470
|
|
|
463
471
|
if __name__ == "__main__":
|
|
464
|
-
# Example usage
|
|
465
|
-
sample_sessions =
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
472
|
+
# Example usage with new schema
|
|
473
|
+
sample_sessions: list[LayoutConfig] = [
|
|
474
|
+
{
|
|
475
|
+
"layoutName": "Development",
|
|
476
|
+
"layoutTabs": [
|
|
477
|
+
{"tabName": "🚀Frontend", "startDir": "~/code/myapp/frontend", "command": "npm run dev"},
|
|
478
|
+
{"tabName": "⚙️Backend", "startDir": "~/code/myapp/backend", "command": "python manage.py runserver"},
|
|
479
|
+
{"tabName": "📊Monitor", "startDir": "~", "command": "htop"},
|
|
480
|
+
],
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
"layoutName": "Testing",
|
|
484
|
+
"layoutTabs": [
|
|
485
|
+
{"tabName": "🧪Tests", "startDir": "~/code/myapp", "command": "pytest --watch"},
|
|
486
|
+
{"tabName": "🔍Coverage", "startDir": "~/code/myapp", "command": "coverage run --source=. -m pytest"},
|
|
487
|
+
{"tabName": "📝Logs", "startDir": "~/logs", "command": "tail -f app.log"},
|
|
488
|
+
],
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
"layoutName": "Deployment",
|
|
492
|
+
"layoutTabs": [
|
|
493
|
+
{"tabName": "🐳Docker", "startDir": "~/code/myapp", "command": "docker-compose up"},
|
|
494
|
+
{"tabName": "☸️K8s", "startDir": "~/k8s", "command": "kubectl get pods --watch"},
|
|
495
|
+
{"tabName": "📈Metrics", "startDir": "~", "command": "k9s"},
|
|
496
|
+
],
|
|
497
|
+
},
|
|
498
|
+
]
|
|
471
499
|
try:
|
|
472
500
|
# Create the local manager
|
|
473
501
|
manager = ZellijLocalManager(sample_sessions, session_name_prefix="DevEnv")
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
from typing import Dict,
|
|
2
|
+
from typing import Dict, Optional, List, Union, Any
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
import logging
|
|
5
5
|
import json
|
|
@@ -13,6 +13,7 @@ from machineconfig.cluster.sessions_managers.zellij_utils.layout_generator impor
|
|
|
13
13
|
from machineconfig.cluster.sessions_managers.zellij_utils.process_monitor import ProcessMonitor
|
|
14
14
|
from machineconfig.cluster.sessions_managers.zellij_utils.session_manager import SessionManager
|
|
15
15
|
from machineconfig.cluster.sessions_managers.zellij_utils.status_reporter import StatusReporter
|
|
16
|
+
from machineconfig.cluster.sessions_managers.layout_types import LayoutConfig
|
|
16
17
|
|
|
17
18
|
logging.basicConfig(level=logging.INFO)
|
|
18
19
|
logger = logging.getLogger(__name__)
|
|
@@ -24,7 +25,7 @@ class ZellijRemoteLayoutGenerator:
|
|
|
24
25
|
def __init__(self, remote_name: str, session_name_prefix: str):
|
|
25
26
|
self.remote_name = remote_name
|
|
26
27
|
self.session_name = session_name_prefix + "_" + LayoutGenerator.generate_random_suffix()
|
|
27
|
-
self.
|
|
28
|
+
self.layout_config: Optional[LayoutConfig] = None
|
|
28
29
|
self.layout_path: Optional[str] = None
|
|
29
30
|
|
|
30
31
|
# Initialize modular components
|
|
@@ -37,40 +38,50 @@ class ZellijRemoteLayoutGenerator:
|
|
|
37
38
|
def copy_layout_to_remote(self, local_layout_file: Path, random_suffix: str) -> str:
|
|
38
39
|
return self.session_manager.copy_layout_to_remote(local_layout_file, random_suffix)
|
|
39
40
|
|
|
40
|
-
def create_zellij_layout(self,
|
|
41
|
+
def create_zellij_layout(self, layout_config: LayoutConfig, output_dir: Optional[str] = None) -> str:
|
|
41
42
|
# Enhanced Rich logging for remote layout creation
|
|
42
|
-
tab_count = len(
|
|
43
|
-
|
|
43
|
+
tab_count = len(layout_config["layoutTabs"])
|
|
44
|
+
layout_name = layout_config["layoutName"]
|
|
45
|
+
console.print(f"[bold cyan]📋 Creating Zellij layout[/bold cyan] [bright_green]'{layout_name}' with {tab_count} tabs[/bright_green] [magenta]for remote[/magenta] [bold yellow]'{self.remote_name}'[/bold yellow]")
|
|
44
46
|
|
|
45
47
|
# Display tab summary for remote
|
|
46
|
-
for
|
|
47
|
-
console.print(f" [yellow]→[/yellow] [bold]{
|
|
48
|
+
for tab in layout_config["layoutTabs"]:
|
|
49
|
+
console.print(f" [yellow]→[/yellow] [bold]{tab['tabName']}[/bold] [dim]in[/dim] [blue]{tab['startDir']}[/blue] [dim]on[/dim] [yellow]{self.remote_name}[/yellow]")
|
|
48
50
|
|
|
49
|
-
self.
|
|
51
|
+
self.layout_config = layout_config.copy()
|
|
50
52
|
if output_dir:
|
|
51
53
|
output_path = Path(output_dir)
|
|
52
54
|
else:
|
|
53
55
|
output_path = TMP_LAYOUT_DIR
|
|
54
|
-
self.layout_path = self.layout_generator.create_layout_file(
|
|
56
|
+
self.layout_path = self.layout_generator.create_layout_file(layout_config, output_path, self.session_name)
|
|
55
57
|
return self.layout_path
|
|
56
58
|
|
|
57
|
-
def get_layout_preview(self,
|
|
58
|
-
return self.layout_generator.generate_layout_content(
|
|
59
|
+
def get_layout_preview(self, layout_config: LayoutConfig) -> str:
|
|
60
|
+
return self.layout_generator.generate_layout_content(layout_config)
|
|
59
61
|
|
|
60
62
|
def check_command_status(self, tab_name: str, use_verification: bool = True) -> Dict[str, Any]:
|
|
61
|
-
|
|
63
|
+
if not self.layout_config:
|
|
64
|
+
return {"status": "error", "error": "No layout config available", "running": False}
|
|
65
|
+
return self.process_monitor.check_command_status(tab_name, self.layout_config, use_verification)
|
|
62
66
|
|
|
63
67
|
def check_all_commands_status(self) -> Dict[str, Dict[str, Any]]:
|
|
64
|
-
|
|
68
|
+
if not self.layout_config:
|
|
69
|
+
return {}
|
|
70
|
+
return self.process_monitor.check_all_commands_status(self.layout_config)
|
|
65
71
|
|
|
66
72
|
def check_zellij_session_status(self) -> Dict[str, Any]:
|
|
67
73
|
return self.session_manager.check_zellij_session_status()
|
|
68
74
|
|
|
69
75
|
def get_comprehensive_status(self) -> Dict[str, Any]:
|
|
70
|
-
|
|
76
|
+
if not self.layout_config:
|
|
77
|
+
return {"error": "No layout config available"}
|
|
78
|
+
return self.status_reporter.get_comprehensive_status(self.layout_config)
|
|
71
79
|
|
|
72
80
|
def print_status_report(self) -> None:
|
|
73
|
-
self.
|
|
81
|
+
if not self.layout_config:
|
|
82
|
+
console.print("[bold red]❌ No layout config available[/bold red]")
|
|
83
|
+
return
|
|
84
|
+
self.status_reporter.print_status_report(self.layout_config)
|
|
74
85
|
|
|
75
86
|
def start_zellij_session(self, layout_file_path: Optional[str] = None) -> Dict[str, Any]:
|
|
76
87
|
return self.session_manager.start_zellij_session(layout_file_path or self.layout_path)
|
|
@@ -80,13 +91,17 @@ class ZellijRemoteLayoutGenerator:
|
|
|
80
91
|
|
|
81
92
|
# Legacy methods for backward compatibility
|
|
82
93
|
def force_fresh_process_check(self, tab_name: str) -> Dict[str, Any]:
|
|
83
|
-
|
|
94
|
+
if not self.layout_config:
|
|
95
|
+
return {"error": "No layout config available"}
|
|
96
|
+
return self.process_monitor.force_fresh_process_check(tab_name, self.layout_config)
|
|
84
97
|
|
|
85
98
|
def verify_process_alive(self, pid: int) -> bool:
|
|
86
99
|
return self.process_monitor.verify_process_alive(pid)
|
|
87
100
|
|
|
88
101
|
def get_verified_process_status(self, tab_name: str) -> Dict[str, Any]:
|
|
89
|
-
|
|
102
|
+
if not self.layout_config:
|
|
103
|
+
return {"error": "No layout config available"}
|
|
104
|
+
return self.process_monitor.get_verified_process_status(tab_name, self.layout_config)
|
|
90
105
|
|
|
91
106
|
# Static methods for backward compatibility
|
|
92
107
|
@staticmethod
|
|
@@ -95,7 +110,7 @@ class ZellijRemoteLayoutGenerator:
|
|
|
95
110
|
return executor.run_command(command, timeout)
|
|
96
111
|
|
|
97
112
|
def to_dict(self) -> Dict[str, Any]:
|
|
98
|
-
return {"remote_name": self.remote_name, "session_name": self.session_name, "
|
|
113
|
+
return {"remote_name": self.remote_name, "session_name": self.session_name, "layout_config": self.layout_config, "layout_path": self.layout_path, "created_at": datetime.now().isoformat(), "class_name": self.__class__.__name__}
|
|
99
114
|
|
|
100
115
|
def to_json(self, file_path: Optional[Union[str, Path]] = None) -> str:
|
|
101
116
|
# Generate file path if not provided
|
|
@@ -154,7 +169,7 @@ class ZellijRemoteLayoutGenerator:
|
|
|
154
169
|
|
|
155
170
|
# Restore state
|
|
156
171
|
instance.session_name = data["session_name"]
|
|
157
|
-
instance.
|
|
172
|
+
instance.layout_config = data["layout_config"]
|
|
158
173
|
instance.layout_path = data["layout_path"]
|
|
159
174
|
|
|
160
175
|
logger.info(f"✅ Loaded ZellijRemoteLayoutGenerator from: {file_path}")
|
|
@@ -175,12 +190,15 @@ class ZellijRemoteLayoutGenerator:
|
|
|
175
190
|
|
|
176
191
|
|
|
177
192
|
if __name__ == "__main__":
|
|
178
|
-
# Example usage
|
|
179
|
-
|
|
180
|
-
"
|
|
181
|
-
"
|
|
182
|
-
|
|
183
|
-
|
|
193
|
+
# Example usage with new schema
|
|
194
|
+
sample_layout: LayoutConfig = {
|
|
195
|
+
"layoutName": "RemoteBots",
|
|
196
|
+
"layoutTabs": [
|
|
197
|
+
{"tabName": "🤖Bot1", "startDir": "~/code/bytesense/bithence", "command": "~/scripts/fire -mO go1.py bot1 --kw create_new_bot True"},
|
|
198
|
+
{"tabName": "🤖Bot2", "startDir": "~/code/bytesense/bithence", "command": "~/scripts/fire -mO go2.py bot2 --kw create_new_bot True"},
|
|
199
|
+
{"tabName": "📊Monitor", "startDir": "~", "command": "htop"},
|
|
200
|
+
{"tabName": "📝Logs", "startDir": "/var/log", "command": "tail -f /var/log/app.log"},
|
|
201
|
+
],
|
|
184
202
|
}
|
|
185
203
|
|
|
186
204
|
# Replace 'myserver' with an actual SSH config alias
|
|
@@ -190,7 +208,7 @@ if __name__ == "__main__":
|
|
|
190
208
|
try:
|
|
191
209
|
# Create layout using the remote generator
|
|
192
210
|
generator = ZellijRemoteLayoutGenerator(remote_name=remote_name, session_name_prefix=session_name)
|
|
193
|
-
layout_path = generator.create_zellij_layout(
|
|
211
|
+
layout_path = generator.create_zellij_layout(sample_layout)
|
|
194
212
|
print(f"✅ Remote layout created successfully: {layout_path}")
|
|
195
213
|
|
|
196
214
|
# Demonstrate serialization
|
|
@@ -205,7 +223,9 @@ if __name__ == "__main__":
|
|
|
205
223
|
# Demonstrate loading (using the full path)
|
|
206
224
|
loaded_generator = ZellijRemoteLayoutGenerator.from_json(saved_path)
|
|
207
225
|
print(f"✅ Session loaded successfully: {loaded_generator.session_name}")
|
|
208
|
-
|
|
226
|
+
if loaded_generator.layout_config:
|
|
227
|
+
tab_names = [tab["tabName"] for tab in loaded_generator.layout_config["layoutTabs"]]
|
|
228
|
+
print(f"📊 Loaded tabs: {tab_names}")
|
|
209
229
|
|
|
210
230
|
# Demonstrate status checking
|
|
211
231
|
print(f"\n🔍 Checking command status on remote '{remote_name}':")
|