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.

Files changed (101) hide show
  1. machineconfig/__init__.py +30 -0
  2. machineconfig/cluster/sessions_managers/enhanced_command_runner.py +0 -2
  3. machineconfig/cluster/sessions_managers/layout_types.py +29 -0
  4. machineconfig/cluster/sessions_managers/wt_local.py +68 -62
  5. machineconfig/cluster/sessions_managers/wt_local_manager.py +51 -22
  6. machineconfig/cluster/sessions_managers/wt_remote.py +30 -108
  7. machineconfig/cluster/sessions_managers/wt_remote_manager.py +14 -11
  8. machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +33 -37
  9. machineconfig/cluster/sessions_managers/wt_utils/process_monitor.py +22 -17
  10. machineconfig/cluster/sessions_managers/wt_utils/session_manager.py +59 -10
  11. machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +16 -14
  12. machineconfig/cluster/sessions_managers/zellij_local.py +75 -57
  13. machineconfig/cluster/sessions_managers/zellij_local_manager.py +51 -23
  14. machineconfig/cluster/sessions_managers/zellij_remote.py +47 -27
  15. machineconfig/cluster/sessions_managers/zellij_remote_manager.py +13 -12
  16. machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +14 -10
  17. machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +31 -15
  18. machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +47 -21
  19. machineconfig/cluster/sessions_managers/zellij_utils/session_manager.py +1 -1
  20. machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +8 -7
  21. machineconfig/cluster/templates/utils.py +1 -1
  22. machineconfig/profile/create.py +4 -0
  23. machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
  24. machineconfig/scripts/linux/checkout_versions +1 -7
  25. machineconfig/scripts/linux/choose_wezterm_theme +1 -7
  26. machineconfig/scripts/linux/cloud_copy +1 -8
  27. machineconfig/scripts/linux/cloud_manager +1 -7
  28. machineconfig/scripts/linux/cloud_mount +1 -23
  29. machineconfig/scripts/linux/cloud_repo_sync +1 -21
  30. machineconfig/scripts/linux/cloud_sync +1 -23
  31. machineconfig/scripts/linux/croshell +1 -23
  32. machineconfig/scripts/linux/devops +0 -21
  33. machineconfig/scripts/linux/fire +1 -27
  34. machineconfig/scripts/linux/fire_agents +1 -26
  35. machineconfig/scripts/linux/gh_models +1 -10
  36. machineconfig/scripts/linux/kill_process +1 -9
  37. machineconfig/scripts/linux/mcinit +1 -26
  38. machineconfig/scripts/linux/mount_nfs +1 -13
  39. machineconfig/scripts/linux/repos +1 -23
  40. machineconfig/scripts/linux/scheduler +1 -7
  41. machineconfig/scripts/linux/start_slidev +1 -22
  42. machineconfig/scripts/linux/start_terminals +1 -9
  43. machineconfig/scripts/linux/url2md +1 -9
  44. machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc +0 -0
  45. machineconfig/scripts/python/__pycache__/croshell.cpython-313.pyc +0 -0
  46. machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc +0 -0
  47. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc +0 -0
  48. machineconfig/scripts/python/cloud_mount.py +4 -2
  49. machineconfig/scripts/python/cloud_repo_sync.py +5 -2
  50. machineconfig/scripts/python/cloud_sync.py +4 -2
  51. machineconfig/scripts/python/croshell.py +5 -3
  52. machineconfig/scripts/python/devops.py +3 -2
  53. machineconfig/scripts/python/devops_devapps_install.py +1 -0
  54. machineconfig/scripts/python/fire_agents.py +6 -6
  55. machineconfig/scripts/python/fire_jobs.py +26 -72
  56. machineconfig/scripts/python/fire_jobs_args_helper.py +84 -0
  57. machineconfig/scripts/python/fire_jobs_layout_helper.py +66 -0
  58. machineconfig/scripts/python/helpers/helpers4.py +0 -1
  59. machineconfig/scripts/python/mount_nfs.py +12 -8
  60. machineconfig/scripts/python/mount_nw_drive.py +6 -6
  61. machineconfig/scripts/python/mount_ssh.py +4 -2
  62. machineconfig/scripts/python/start_slidev.py +4 -2
  63. machineconfig/scripts/python/start_terminals.py +4 -2
  64. machineconfig/scripts/python/wifi_conn.py +0 -1
  65. machineconfig/settings/__pycache__/__init__.cpython-313.pyc +0 -0
  66. machineconfig/settings/shells/ipy/profiles/default/__pycache__/__init__.cpython-313.pyc +0 -0
  67. machineconfig/settings/shells/ipy/profiles/default/startup/__pycache__/__init__.cpython-313.pyc +0 -0
  68. machineconfig/utils/code.py +5 -6
  69. machineconfig/utils/installer_utils/installer_abc.py +0 -1
  70. machineconfig/utils/options.py +7 -7
  71. machineconfig/utils/path.py +12 -12
  72. machineconfig/utils/path_reduced.py +6 -1
  73. machineconfig/utils/source_of_truth.py +2 -2
  74. machineconfig/utils/ssh.py +11 -1
  75. machineconfig/utils/upgrade_packages.py +12 -12
  76. {machineconfig-2.2.dist-info → machineconfig-2.4.dist-info}/METADATA +1 -1
  77. {machineconfig-2.2.dist-info → machineconfig-2.4.dist-info}/RECORD +90 -93
  78. machineconfig-2.4.dist-info/entry_points.txt +2 -0
  79. machineconfig/cluster/sessions_managers/archive/create_zellij_template.py +0 -60
  80. machineconfig/cluster/sessions_managers/archive/session_managers.py +0 -183
  81. machineconfig/cluster/sessions_managers/demo_rich_zellij.py +0 -0
  82. machineconfig/jobs/__pycache__/__init__.cpython-313.pyc +0 -0
  83. machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  84. machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  85. machineconfig/scripts/linux/archive/tmate_conn +0 -12
  86. machineconfig/scripts/linux/archive/tmate_start +0 -12
  87. machineconfig/scripts/linux/archive/transfer_wsl_win +0 -5
  88. machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-313.pyc +0 -0
  89. machineconfig/scripts/python/__pycache__/fire_agents.cpython-313.pyc +0 -0
  90. /machineconfig/cluster/{cloud_manager.py → remote/cloud_manager.py} +0 -0
  91. /machineconfig/cluster/{data_transfer.py → remote/data_transfer.py} +0 -0
  92. /machineconfig/cluster/{distribute.py → remote/distribute.py} +0 -0
  93. /machineconfig/cluster/{file_manager.py → remote/file_manager.py} +0 -0
  94. /machineconfig/cluster/{job_params.py → remote/job_params.py} +0 -0
  95. /machineconfig/cluster/{loader_runner.py → remote/loader_runner.py} +0 -0
  96. /machineconfig/cluster/{remote_machine.py → remote/remote_machine.py} +0 -0
  97. /machineconfig/cluster/{script_execution.py → remote/script_execution.py} +0 -0
  98. /machineconfig/cluster/{script_notify_upon_completion.py → remote/script_notify_upon_completion.py} +0 -0
  99. /machineconfig/{cluster/sessions_managers/archive/__init__.py → scripts/python/fire_jobs_streamlit_helper.py} +0 -0
  100. {machineconfig-2.2.dist-info → machineconfig-2.4.dist-info}/WHEEL +0 -0
  101. {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, Tuple
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, tab_config: Dict[str, Tuple[str, str]]) -> Dict[str, Any]:
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(tab_config)
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, tab_config: Dict[str, Tuple[str, str]]) -> None:
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(tab_config)
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, tab_config: Dict[str, Tuple[str, str]]) -> Dict[str, Any]:
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(tab_config)
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, tab_config: Dict[str, Tuple[str, str]]) -> Dict[str, Any]:
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 tab_name not in tab_config:
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, tab_config)
192
+ cmd_status = self.process_monitor.check_command_status(tab_name, tabs)
192
193
 
193
194
  # Add additional context
194
- cwd, command = tab_config[tab_name]
195
- cmd_status["tab_config"] = {"working_directory": cwd, "command": command}
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.tab_config: Dict[str, tuple[str, str]] = {} # Store entire tab config (cwd, command) for status checking
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(tab_name: str, cwd: str, command: str) -> str:
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 _validate_tab_config(tab_config: Dict[str, tuple[str, str]]) -> None:
79
- if not tab_config:
80
- raise ValueError("Tab configuration cannot be empty")
81
- for tab_name, (cwd, command) in tab_config.items():
82
- if not tab_name.strip():
83
- raise ValueError(f"Invalid tab name: {tab_name}")
84
- if not command.strip():
85
- raise ValueError(f"Invalid command for tab '{tab_name}': {command}")
86
- if not cwd.strip():
87
- raise ValueError(f"Invalid cwd for tab '{tab_name}': {cwd}")
88
-
89
- def create_zellij_layout(self, tab_config: Dict[str, tuple[str, str]], output_dir: Optional[str] = None, session_name: Optional[str] = None) -> str:
90
- ZellijLayoutGenerator._validate_tab_config(tab_config)
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(tab_config)
94
- console.print(f"[bold cyan]📋 Creating Zellij layout[/bold cyan] [bright_green]with {tab_count} tabs[/bright_green]")
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 tab_name, (cwd, command) in tab_config.items():
98
- console.print(f" [yellow]→[/yellow] [bold]{tab_name}[/bold] [dim]in[/dim] [blue]{cwd}[/blue]")
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 entire tab config for status checking
101
- self.session_name = session_name or "default"
102
- self.tab_config = tab_config.copy()
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 tab_name, (cwd, command) in tab_config.items():
106
- layout_content += "\n" + ZellijLayoutGenerator._create_tab_section(tab_name, cwd, command)
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(tab_config: Dict[str, tuple[str, str]], layout_template: str | None = None) -> str:
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._validate_tab_config(tab_config)
150
+ ZellijLayoutGenerator._validate_layout_config(layout_config)
143
151
  layout_content = layout_template
144
- for tab_name, (cwd, command) in tab_config.items():
145
- layout_content += "\n" + ZellijLayoutGenerator._create_tab_section(tab_name, cwd, command)
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, tab_config: Dict[str, tuple[str, str]]) -> Dict[str, Any]:
150
- if tab_name not in tab_config:
151
- return {"status": "unknown", "error": f"Tab '{tab_name}' not found in tracked tab config", "running": False, "pid": None, "command": None, "cwd": None}
152
-
153
- cwd, command = tab_config[tab_name]
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.tab_config:
179
- logger.warning("No tab config tracked. Make sure to create a layout first.")
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 tab_name in self.tab_config:
184
- status_report[tab_name] = ZellijLayoutGenerator.check_command_status(tab_name, self.tab_config)
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(tab_config: Dict[str, tuple[str, str]], output_dir: Optional[str] = None) -> str:
309
+ def created_zellij_layout(layout_config: LayoutConfig, output_dir: Optional[str] = None) -> str:
293
310
  generator = ZellijLayoutGenerator()
294
- return generator.create_zellij_layout(tab_config, output_dir)
311
+ return generator.create_zellij_layout(layout_config, output_dir)
295
312
 
296
313
 
297
- def run_zellij_layout(tab_config: Dict[str, tuple[str, str]], session_name: Optional[str] = None) -> str:
298
- if not session_name:
299
- session_name = "".join(random.choices(string.ascii_lowercase + string.digits, k=8))
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
- sample_tabs = {
340
- "🤖Bot1": ("~/code/bytesense/bithence", "~/scripts/fire -mO go1.py bot1 --kw create_new_bot True"),
341
- "🤖Bot2": ("~/code/bytesense/bithence", "~/scripts/fire -mO go2.py bot2 --kw create_new_bot True"),
342
- "📊Monitor": ("~", "htop"),
343
- "📝Logs": ("/var/log", "tail -f /var/log/app.log"),
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(sample_tabs, session_name="test_session")
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
- bot1_status = ZellijLayoutGenerator.check_command_status("🤖Bot1", generator.tab_config)
358
- print(f"Status: {bot1_status['status']}")
359
- print(f"Running: {bot1_status['running']}")
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, session2zellij_tabs: Dict[str, Dict[str, tuple[str, str]]], session_name_prefix: str = "LocalJobMgr"):
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
- session2zellij_tabs: Dict mapping session names to their tab configs
32
- Format: {session_name: {tab_name: (cwd, command), ...}, ...}
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.session2zellij_tabs = session2zellij_tabs # Store the original config
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 session_name, tab_config in session2zellij_tabs.items():
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(tab_config=tab_config, session_name=full_session_name)
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 session2zellij_tabs configuration
330
- config_file = session_dir / "session2zellij_tabs.json"
331
- text = json.dumps(self.session2zellij_tabs, indent=2, ensure_ascii=False)
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": list(self.session2zellij_tabs.keys()), "manager_type": "ZellijLocalManager"}
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, "tab_config": manager.tab_config, "layout_path": manager.layout_path}
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 / "session2zellij_tabs.json"
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
- session2zellij_tabs = json.load(f)
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(session2zellij_tabs=session2zellij_tabs, session_name_prefix=session_name_prefix)
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.tab_config = manager_data["tab_config"]
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
- active_sessions.append({"session_name": session_name, "is_active": is_active, "tab_count": len(manager.tab_config), "tabs": list(manager.tab_config.keys())})
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
- "development": {"🚀Frontend": ("~/code/myapp/frontend", "npm run dev"), "⚙️Backend": ("~/code/myapp/backend", "python manage.py runserver"), "📊Monitor": ("~", "htop")},
467
- "testing": {"🧪Tests": ("~/code/myapp", "pytest --watch"), "🔍Coverage": ("~/code/myapp", "coverage run --source=. -m pytest"), "📝Logs": ("~/logs", "tail -f app.log")},
468
- "deployment": {"🐳Docker": ("~/code/myapp", "docker-compose up"), "☸️K8s": ("~/k8s", "kubectl get pods --watch"), "📈Metrics": ("~", "k9s")},
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, Tuple, Optional, List, Union, Any
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.tab_config: Dict[str, Tuple[str, str]] = {}
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, tab_config: Dict[str, Tuple[str, str]], output_dir: Optional[str] = None) -> str:
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(tab_config)
43
- 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]")
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 tab_name, (cwd, _) in tab_config.items():
47
- console.print(f" [yellow]→[/yellow] [bold]{tab_name}[/bold] [dim]in[/dim] [blue]{cwd}[/blue] [dim]on[/dim] [yellow]{self.remote_name}[/yellow]")
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.tab_config = tab_config.copy()
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(tab_config, output_path, self.session_name)
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, tab_config: Dict[str, Tuple[str, str]]) -> str:
58
- return self.layout_generator.generate_layout_content(tab_config)
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
- return self.process_monitor.check_command_status(tab_name, self.tab_config, use_verification)
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
- return self.process_monitor.check_all_commands_status(self.tab_config)
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
- return self.status_reporter.get_comprehensive_status(self.tab_config)
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.status_reporter.print_status_report(self.tab_config)
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
- return self.process_monitor.force_fresh_process_check(tab_name, self.tab_config)
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
- return self.process_monitor.get_verified_process_status(tab_name, self.tab_config)
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, "tab_config": self.tab_config, "layout_path": self.layout_path, "created_at": datetime.now().isoformat(), "class_name": self.__class__.__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.tab_config = data["tab_config"]
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
- sample_tabs = {
180
- "🤖Bot1": ("~/code/bytesense/bithence", "~/scripts/fire -mO go1.py bot1 --kw create_new_bot True"),
181
- "🤖Bot2": ("~/code/bytesense/bithence", "~/scripts/fire -mO go2.py bot2 --kw create_new_bot True"),
182
- "📊Monitor": ("~", "htop"),
183
- "📝Logs": ("/var/log", "tail -f /var/log/app.log"),
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(sample_tabs)
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
- print(f"📊 Loaded tabs: {list(loaded_generator.tab_config.keys())}")
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}':")