machineconfig 2.2__py3-none-any.whl → 2.3__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 (63) hide show
  1. machineconfig/cluster/sessions_managers/enhanced_command_runner.py +0 -2
  2. machineconfig/cluster/sessions_managers/layout_types.py +29 -0
  3. machineconfig/cluster/sessions_managers/wt_local.py +68 -62
  4. machineconfig/cluster/sessions_managers/wt_local_manager.py +51 -22
  5. machineconfig/cluster/sessions_managers/wt_remote.py +30 -108
  6. machineconfig/cluster/sessions_managers/wt_remote_manager.py +14 -11
  7. machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +33 -37
  8. machineconfig/cluster/sessions_managers/wt_utils/process_monitor.py +22 -17
  9. machineconfig/cluster/sessions_managers/wt_utils/session_manager.py +59 -10
  10. machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +16 -14
  11. machineconfig/cluster/sessions_managers/zellij_local.py +75 -57
  12. machineconfig/cluster/sessions_managers/zellij_local_manager.py +51 -23
  13. machineconfig/cluster/sessions_managers/zellij_remote.py +47 -27
  14. machineconfig/cluster/sessions_managers/zellij_remote_manager.py +13 -12
  15. machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +14 -10
  16. machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +31 -15
  17. machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +47 -21
  18. machineconfig/cluster/sessions_managers/zellij_utils/session_manager.py +1 -1
  19. machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +8 -7
  20. machineconfig/cluster/templates/utils.py +1 -1
  21. machineconfig/profile/create.py +4 -0
  22. machineconfig/scripts/python/devops.py +1 -0
  23. machineconfig/scripts/python/devops_devapps_install.py +1 -0
  24. machineconfig/scripts/python/fire_agents.py +6 -6
  25. machineconfig/scripts/python/fire_jobs.py +16 -64
  26. machineconfig/scripts/python/fire_jobs_args_helper.py +84 -0
  27. machineconfig/scripts/python/fire_jobs_layout_helper.py +66 -0
  28. machineconfig/scripts/python/helpers/helpers4.py +0 -1
  29. machineconfig/scripts/python/wifi_conn.py +0 -1
  30. machineconfig/utils/code.py +0 -1
  31. machineconfig/utils/installer_utils/installer_abc.py +0 -1
  32. machineconfig/utils/options.py +7 -7
  33. machineconfig/utils/path.py +12 -12
  34. machineconfig/utils/path_reduced.py +6 -1
  35. machineconfig/utils/ssh.py +11 -1
  36. machineconfig/utils/upgrade_packages.py +12 -12
  37. {machineconfig-2.2.dist-info → machineconfig-2.3.dist-info}/METADATA +1 -1
  38. {machineconfig-2.2.dist-info → machineconfig-2.3.dist-info}/RECORD +51 -59
  39. machineconfig-2.3.dist-info/entry_points.txt +2 -0
  40. machineconfig/cluster/sessions_managers/archive/create_zellij_template.py +0 -60
  41. machineconfig/cluster/sessions_managers/archive/session_managers.py +0 -183
  42. machineconfig/cluster/sessions_managers/demo_rich_zellij.py +0 -0
  43. machineconfig/jobs/__pycache__/__init__.cpython-313.pyc +0 -0
  44. machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  45. machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  46. machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
  47. machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc +0 -0
  48. machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc +0 -0
  49. machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-313.pyc +0 -0
  50. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc +0 -0
  51. machineconfig/scripts/python/__pycache__/fire_agents.cpython-313.pyc +0 -0
  52. /machineconfig/cluster/{cloud_manager.py → remote/cloud_manager.py} +0 -0
  53. /machineconfig/cluster/{data_transfer.py → remote/data_transfer.py} +0 -0
  54. /machineconfig/cluster/{distribute.py → remote/distribute.py} +0 -0
  55. /machineconfig/cluster/{file_manager.py → remote/file_manager.py} +0 -0
  56. /machineconfig/cluster/{job_params.py → remote/job_params.py} +0 -0
  57. /machineconfig/cluster/{loader_runner.py → remote/loader_runner.py} +0 -0
  58. /machineconfig/cluster/{remote_machine.py → remote/remote_machine.py} +0 -0
  59. /machineconfig/cluster/{script_execution.py → remote/script_execution.py} +0 -0
  60. /machineconfig/cluster/{script_notify_upon_completion.py → remote/script_notify_upon_completion.py} +0 -0
  61. /machineconfig/{cluster/sessions_managers/archive/__init__.py → scripts/python/fire_jobs_streamlit_helper.py} +0 -0
  62. {machineconfig-2.2.dist-info → machineconfig-2.3.dist-info}/WHEEL +0 -0
  63. {machineconfig-2.2.dist-info → machineconfig-2.3.dist-info}/top_level.txt +0 -0
@@ -84,7 +84,6 @@ def enhanced_zellij_session_start(session_name: str, layout_path: str) -> Dict[s
84
84
  """
85
85
  console.print()
86
86
  console.print(Panel.fit(f"🚀 Starting Zellij Session: [bold cyan]{session_name}[/bold cyan]", style="green", box=box.ROUNDED))
87
-
88
87
  # Delete existing session first (suppress normal output)
89
88
  delete_cmd = f"zellij delete-session --force {session_name}"
90
89
  run_enhanced_command(
@@ -93,7 +92,6 @@ def enhanced_zellij_session_start(session_name: str, layout_path: str) -> Dict[s
93
92
  show_progress=False,
94
93
  timeout=5, # Quick timeout for cleanup
95
94
  )
96
-
97
95
  # Start new session (use -b for background to avoid hanging)
98
96
  start_cmd = f"zellij --layout {layout_path} a -b {session_name}"
99
97
  start_result = run_enhanced_command(
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Type definitions for the standardized layout configuration schema.
4
+ This module defines the data structures that match the layout.json schema.
5
+ """
6
+
7
+ from typing import TypedDict, List
8
+
9
+
10
+ class TabConfig(TypedDict):
11
+ """Configuration for a single tab in a layout."""
12
+
13
+ tabName: str
14
+ startDir: str
15
+ command: str
16
+
17
+
18
+ class LayoutConfig(TypedDict):
19
+ """Configuration for a complete layout with its tabs."""
20
+
21
+ layoutName: str
22
+ layoutTabs: List[TabConfig]
23
+
24
+
25
+ class LayoutsFile(TypedDict):
26
+ """Complete layout file structure."""
27
+
28
+ version: str
29
+ layouts: List[LayoutConfig]
@@ -13,6 +13,8 @@ from typing import Dict, List, Optional, Any
13
13
  from pathlib import Path
14
14
  import logging
15
15
 
16
+ from machineconfig.cluster.sessions_managers.layout_types import LayoutConfig, TabConfig
17
+
16
18
  logging.basicConfig(level=logging.INFO)
17
19
  logger = logging.getLogger(__name__)
18
20
  TMP_LAYOUT_DIR = Path.home().joinpath("tmp_results", "session_manager", "wt", "layout_manager")
@@ -21,12 +23,12 @@ TMP_LAYOUT_DIR = Path.home().joinpath("tmp_results", "session_manager", "wt", "l
21
23
  class WTLayoutGenerator:
22
24
  def __init__(self):
23
25
  self.session_name: Optional[str] = None
24
- self.tab_config: Dict[str, tuple[str, str]] = {} # Store entire tab config (cwd, command) for status checking
25
- self.script_path: Optional[str] = None # Store the full path to the script file
26
+ self.layout_config: Optional[LayoutConfig] = None # Store the complete layout config
27
+ self.script_path: Optional[str] = None # Store the full path to the PowerShell script
26
28
 
27
29
  @staticmethod
28
30
  def _generate_random_suffix(length: int = 8) -> str:
29
- """Generate a random string suffix for unique script file names."""
31
+ """Generate a random string suffix for unique PowerShell script names."""
30
32
  return "".join(random.choices(string.ascii_lowercase + string.digits, k=length))
31
33
 
32
34
  @staticmethod
@@ -51,8 +53,12 @@ class WTLayoutGenerator:
51
53
  return text
52
54
 
53
55
  @staticmethod
54
- def _create_tab_command(tab_name: str, cwd: str, command: str, is_first_tab: bool = False) -> str:
55
- """Create a Windows Terminal tab command string."""
56
+ def _create_tab_command(tab_config: TabConfig, is_first_tab: bool = False) -> str:
57
+ """Create a Windows Terminal tab command string from tab config."""
58
+ tab_name = tab_config["tabName"]
59
+ cwd = tab_config["startDir"]
60
+ command = tab_config["command"]
61
+
56
62
  # Convert paths to Windows format if needed
57
63
  if cwd.startswith("~/"):
58
64
  cwd = cwd.replace("~/", f"{Path.home()}/")
@@ -77,63 +83,55 @@ class WTLayoutGenerator:
77
83
  return " ".join(tab_parts)
78
84
 
79
85
  @staticmethod
80
- def _validate_tab_config(tab_config: Dict[str, tuple[str, str]]) -> None:
81
- """Validate tab configuration format and content."""
82
- if not tab_config:
83
- raise ValueError("Tab configuration cannot be empty")
84
- for tab_name, (cwd, command) in tab_config.items():
85
- if not tab_name.strip():
86
- raise ValueError(f"Invalid tab name: {tab_name}")
87
- if not command.strip():
88
- raise ValueError(f"Invalid command for tab '{tab_name}': {command}")
89
- if not cwd.strip():
90
- raise ValueError(f"Invalid cwd for tab '{tab_name}': {cwd}")
91
-
92
- def create_wt_layout(self, tab_config: Dict[str, tuple[str, str]], output_dir: Optional[str] = None, session_name: Optional[str] = None) -> str:
93
- WTLayoutGenerator._validate_tab_config(tab_config)
94
- logger.info(f"Creating Windows Terminal layout with {len(tab_config)} tabs")
95
-
96
- # Store session name and entire tab config for status checking
97
- self.session_name = session_name or "default"
98
- self.tab_config = tab_config.copy()
86
+ def _validate_layout_config(layout_config: LayoutConfig) -> None:
87
+ """Validate layout configuration format and content."""
88
+ if not layout_config["layoutTabs"]:
89
+ raise ValueError("Layout must contain at least one tab")
90
+ for tab in layout_config["layoutTabs"]:
91
+ if not tab["tabName"].strip():
92
+ raise ValueError(f"Invalid tab name: {tab['tabName']}")
93
+ if not tab["command"].strip():
94
+ raise ValueError(f"Invalid command for tab '{tab['tabName']}': {tab['command']}")
95
+ if not tab["startDir"].strip():
96
+ raise ValueError(f"Invalid startDir for tab '{tab['tabName']}': {tab['startDir']}")
97
+
98
+ def create_wt_layout(self, layout_config: LayoutConfig, output_dir: Optional[str] = None) -> str:
99
+ WTLayoutGenerator._validate_layout_config(layout_config)
100
+ logger.info(f"Creating Windows Terminal layout '{layout_config['layoutName']}' with {len(layout_config['layoutTabs'])} tabs")
101
+
102
+ # Store session name and layout config for status checking
103
+ self.session_name = layout_config["layoutName"]
104
+ self.layout_config = layout_config.copy()
99
105
 
100
106
  # Generate Windows Terminal command
101
- wt_command = self._generate_wt_command_string(tab_config, self.session_name)
107
+ wt_command = self._generate_wt_command_string(layout_config, self.session_name)
102
108
 
103
109
  try:
104
110
  random_suffix = WTLayoutGenerator._generate_random_suffix()
105
111
  if output_dir:
106
112
  output_path = Path(output_dir)
107
113
  output_path.mkdir(parents=True, exist_ok=True)
108
- script_file = output_path / f"wt_layout_{random_suffix}.bat"
114
+ script_file = output_path / f"wt_layout_{random_suffix}.ps1"
109
115
  else:
110
116
  # Use the predefined TMP_LAYOUT_DIR for temporary files
111
117
  TMP_LAYOUT_DIR.mkdir(parents=True, exist_ok=True)
112
- script_file = TMP_LAYOUT_DIR / f"wt_layout_{self.session_name}_{random_suffix}.bat"
113
-
114
- # Create batch script
115
- text = f"""@echo off
116
- REM Windows Terminal layout for {self.session_name}
117
- {wt_command}
118
- """
119
- script_file.write_text(text, encoding="utf-8")
118
+ script_file = TMP_LAYOUT_DIR / f"wt_layout_{self.session_name}_{random_suffix}.ps1"
120
119
 
121
- # Also create PowerShell script for better command handling
122
- ps1_file = script_file.with_suffix(".ps1")
120
+ # Create PowerShell script
123
121
  text = f"""# Windows Terminal layout for {self.session_name}
124
122
  # Generated with random suffix: {random_suffix}
125
123
  {wt_command}
126
124
  """
127
- ps1_file.write_text(text, encoding="utf-8")
125
+ script_file.write_text(text, encoding="utf-8")
128
126
 
129
127
  self.script_path = str(script_file.absolute())
130
- logger.info(f"Windows Terminal script file created: {self.script_path}")
128
+ logger.info(f"Windows Terminal PowerShell script created: {self.script_path}")
131
129
  return self.script_path
132
130
  except OSError as e:
133
- logger.error(f"Failed to create script file: {e}")
131
+ logger.error(f"Failed to create PowerShell script: {e}")
134
132
  raise
135
133
 
136
- def _generate_wt_command_string(self, tab_config: Dict[str, tuple[str, str]], window_name: str) -> str:
134
+ def _generate_wt_command_string(self, layout_config: LayoutConfig, window_name: str) -> str:
137
135
  """Generate complete Windows Terminal command string."""
138
136
  # Start building the wt command
139
137
  wt_parts = ["wt"]
@@ -143,9 +141,9 @@ REM Windows Terminal layout for {self.session_name}
143
141
 
144
142
  # Add tabs
145
143
  tab_commands = []
146
- for i, (tab_name, (cwd, command)) in enumerate(tab_config.items()):
144
+ for i, tab in enumerate(layout_config["layoutTabs"]):
147
145
  is_first = i == 0
148
- tab_cmd = self._create_tab_command(tab_name, cwd, command, is_first)
146
+ tab_cmd = self._create_tab_command(tab, is_first)
149
147
  tab_commands.append(tab_cmd)
150
148
 
151
149
  # Join all parts with semicolons (Windows Terminal command separator)
@@ -161,19 +159,20 @@ REM Windows Terminal layout for {self.session_name}
161
159
 
162
160
  return " ".join(wt_parts)
163
161
 
164
- def get_wt_layout_preview(self, tab_config: Dict[str, tuple[str, str]]) -> str:
162
+ def get_wt_layout_preview(self, layout_config: LayoutConfig) -> str:
165
163
  """Generate preview of the Windows Terminal command that would be created."""
166
- WTLayoutGenerator._validate_tab_config(tab_config)
167
- return self._generate_wt_command_string(tab_config, "preview")
164
+ WTLayoutGenerator._validate_layout_config(layout_config)
165
+ return self._generate_wt_command_string(layout_config, "preview")
168
166
 
169
167
  def check_all_commands_status(self) -> Dict[str, Dict[str, Any]]:
170
- if not self.tab_config:
171
- logger.warning("No tab config tracked. Make sure to create a layout first.")
168
+ if not self.layout_config:
169
+ logger.warning("No layout config tracked. Make sure to create a layout first.")
172
170
  return {}
173
171
 
174
172
  status_report = {}
175
- for tab_name in self.tab_config:
176
- status_report[tab_name] = WTLayoutGenerator.check_command_status(tab_name, self.tab_config)
173
+ for tab in self.layout_config["layoutTabs"]:
174
+ tab_name = tab["tabName"]
175
+ status_report[tab_name] = WTLayoutGenerator.check_command_status(tab_name, self.layout_config)
177
176
 
178
177
  return status_report
179
178
 
@@ -218,12 +217,19 @@ REM Windows Terminal layout for {self.session_name}
218
217
  return {"wt_running": False, "error": str(e), "session_name": session_name}
219
218
 
220
219
  @staticmethod
221
- def check_command_status(tab_name: str, tab_config: Dict[str, tuple[str, str]]) -> Dict[str, Any]:
220
+ def check_command_status(tab_name: str, layout_config: LayoutConfig) -> Dict[str, Any]:
222
221
  """Check if a command is running by looking for processes."""
223
- if tab_name not in tab_config:
224
- return {"status": "unknown", "error": f"Tab '{tab_name}' not found in tracked configuration", "running": False, "pid": None, "command": None}
222
+ # Find the tab with the given name
223
+ tab_config = None
224
+ for tab in layout_config["layoutTabs"]:
225
+ if tab["tabName"] == tab_name:
226
+ tab_config = tab
227
+ break
228
+
229
+ if tab_config is None:
230
+ return {"status": "unknown", "error": f"Tab '{tab_name}' not found in layout config", "running": False, "pid": None, "command": None}
225
231
 
226
- _, command = tab_config[tab_name]
232
+ command = tab_config["command"]
227
233
 
228
234
  try:
229
235
  # Create PowerShell script to check for processes
@@ -361,22 +367,22 @@ Get-Process | ForEach-Object {{
361
367
  print("=" * 80)
362
368
 
363
369
 
364
- def create_wt_layout(tab_config: Dict[str, tuple[str, str]], output_dir: Optional[str] = None) -> str:
370
+ def create_wt_layout(layout_config: LayoutConfig, output_dir: Optional[str] = None) -> str:
365
371
  generator = WTLayoutGenerator()
366
- return generator.create_wt_layout(tab_config, output_dir)
372
+ return generator.create_wt_layout(layout_config, output_dir)
367
373
 
368
374
 
369
- def run_wt_layout(tab_config: Dict[str, tuple[str, str]], session_name: Optional[str] = None) -> str:
375
+ def run_wt_layout(layout_config: LayoutConfig) -> str:
370
376
  """Create and run a Windows Terminal layout."""
371
377
  generator = WTLayoutGenerator()
372
- script_path = generator.create_wt_layout(tab_config, session_name=session_name)
378
+ script_path = generator.create_wt_layout(layout_config)
373
379
 
374
380
  # Execute the script
375
381
  cmd = f'powershell -ExecutionPolicy Bypass -File "{script_path}"'
376
382
  result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
377
383
 
378
384
  if result.returncode == 0:
379
- print(f"Windows Terminal layout is running @ {session_name}")
385
+ print(f"Windows Terminal layout is running @ {layout_config['layoutName']}")
380
386
  return script_path
381
387
  else:
382
388
  logger.error(f"Failed to run Windows Terminal layout: {result.stderr}")
@@ -394,17 +400,17 @@ wt new-tab --title "{tab_name}" {cwd_part} "{command}"
394
400
 
395
401
 
396
402
  if __name__ == "__main__":
397
- # Example usage
398
- sample_tabs = {"Frontend": ("~/code", "btm"), "Monitor": ("~", "lf")}
403
+ # Example usage with new schema
404
+ sample_layout: LayoutConfig = {"layoutName": "TestLayout", "layoutTabs": [{"tabName": "Frontend", "startDir": "~/code", "command": "btm"}, {"tabName": "Monitor", "startDir": "~", "command": "lf"}]}
399
405
 
400
406
  try:
401
407
  # Create layout using the generator
402
408
  generator = WTLayoutGenerator()
403
- script_path = generator.create_wt_layout(sample_tabs, session_name="test_session")
409
+ script_path = generator.create_wt_layout(sample_layout)
404
410
  print(f"✅ Windows Terminal layout created: {script_path}")
405
411
 
406
412
  # Show preview
407
- preview = generator.get_wt_layout_preview(sample_tabs)
413
+ preview = generator.get_wt_layout_preview(sample_layout)
408
414
  print(f"\n📋 Command Preview:\n{preview}")
409
415
 
410
416
  # Check status (won't find anything since we haven't run it)
@@ -8,6 +8,7 @@ from pathlib import Path
8
8
  from typing import Optional, Dict, List, Any
9
9
  from machineconfig.utils.utils5 import Scheduler
10
10
  from machineconfig.cluster.sessions_managers.wt_local import WTLayoutGenerator
11
+ from machineconfig.cluster.sessions_managers.layout_types import LayoutConfig
11
12
 
12
13
  logging.basicConfig(level=logging.INFO)
13
14
  logger = logging.getLogger(__name__)
@@ -18,24 +19,23 @@ TMP_SERIALIZATION_DIR = Path.home().joinpath("tmp_results", "session_manager", "
18
19
  class WTLocalManager:
19
20
  """Manages multiple local Windows Terminal sessions and monitors their tabs and processes."""
20
21
 
21
- def __init__(self, session2wt_tabs: Dict[str, Dict[str, tuple[str, str]]], session_name_prefix: str = "LocalWTMgr"):
22
+ def __init__(self, session_layouts: list[LayoutConfig], session_name_prefix: str = "LocalWTMgr"):
22
23
  """
23
24
  Initialize the local Windows Terminal manager.
24
25
 
25
26
  Args:
26
- session2wt_tabs: Dict mapping session names to their tab configs
27
- Format: {session_name: {tab_name: (cwd, command), ...}, ...}
27
+ session_layouts: Dict mapping session names to their layout configs
28
+ Format: {session_name: LayoutConfig, ...}
28
29
  session_name_prefix: Prefix for session names
29
30
  """
30
31
  self.session_name_prefix = session_name_prefix
31
- self.session2wt_tabs = session2wt_tabs # Store the original config
32
+ self.session_layouts = session_layouts # Store the original config
32
33
  self.managers: List[WTLayoutGenerator] = []
33
34
 
34
35
  # Create a WTLayoutGenerator for each session
35
- for session_name, tab_config in session2wt_tabs.items():
36
+ for layout_config in session_layouts:
36
37
  manager = WTLayoutGenerator()
37
- full_session_name = f"{self.session_name_prefix}_{session_name}"
38
- manager.create_wt_layout(tab_config=tab_config, session_name=full_session_name)
38
+ manager.create_wt_layout(layout_config=layout_config)
39
39
  self.managers.append(manager)
40
40
 
41
41
  logger.info(f"Initialized WTLocalManager with {len(self.managers)} sessions")
@@ -288,12 +288,12 @@ class WTLocalManager:
288
288
  session_dir.mkdir(parents=True, exist_ok=True)
289
289
 
290
290
  # Save the session2wt_tabs configuration
291
- config_file = session_dir / "session2wt_tabs.json"
292
- text = json.dumps(self.session2wt_tabs, indent=2, ensure_ascii=False)
291
+ config_file = session_dir / "session_layouts.json"
292
+ text = json.dumps(self.session_layouts, indent=2, ensure_ascii=False)
293
293
  config_file.write_text(text, encoding="utf-8")
294
294
 
295
295
  # Save metadata
296
- metadata = {"session_name_prefix": self.session_name_prefix, "created_at": str(datetime.now()), "num_managers": len(self.managers), "sessions": list(self.session2wt_tabs.keys()), "manager_type": "WTLocalManager"}
296
+ 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": "WTLocalManager"}
297
297
  metadata_file = session_dir / "metadata.json"
298
298
  text = json.dumps(metadata, indent=2, ensure_ascii=False)
299
299
  metadata_file.write_text(text, encoding="utf-8")
@@ -303,7 +303,7 @@ class WTLocalManager:
303
303
  managers_dir.mkdir(exist_ok=True)
304
304
 
305
305
  for i, manager in enumerate(self.managers):
306
- manager_data = {"session_name": manager.session_name, "tab_config": manager.tab_config, "script_path": manager.script_path}
306
+ manager_data = {"session_name": manager.session_name, "layout_config": manager.layout_config, "script_path": manager.script_path}
307
307
  manager_file = managers_dir / f"manager_{i}_{manager.session_name}.json"
308
308
  text = json.dumps(manager_data, indent=2, ensure_ascii=False)
309
309
  manager_file.write_text(text, encoding="utf-8")
@@ -320,12 +320,12 @@ class WTLocalManager:
320
320
  raise FileNotFoundError(f"Session directory not found: {session_dir}")
321
321
 
322
322
  # Load configuration
323
- config_file = session_dir / "session2wt_tabs.json"
323
+ config_file = session_dir / "session_layouts.json"
324
324
  if not config_file.exists():
325
325
  raise FileNotFoundError(f"Configuration file not found: {config_file}")
326
326
 
327
327
  with open(config_file, "r", encoding="utf-8") as f:
328
- session2wt_tabs = json.load(f)
328
+ session_layouts = json.load(f)
329
329
 
330
330
  # Load metadata
331
331
  metadata_file = session_dir / "metadata.json"
@@ -336,7 +336,7 @@ class WTLocalManager:
336
336
  session_name_prefix = metadata.get("session_name_prefix", "LocalWTMgr")
337
337
 
338
338
  # Create new instance
339
- instance = cls(session2wt_tabs=session2wt_tabs, session_name_prefix=session_name_prefix)
339
+ instance = cls(session_layouts=session_layouts, session_name_prefix=session_name_prefix)
340
340
 
341
341
  # Load saved manager states
342
342
  managers_dir = session_dir / "managers"
@@ -352,7 +352,7 @@ class WTLocalManager:
352
352
  # Recreate the manager
353
353
  manager = WTLayoutGenerator()
354
354
  manager.session_name = manager_data["session_name"]
355
- manager.tab_config = manager_data["tab_config"]
355
+ manager.layout_config = manager_data["layout_config"]
356
356
  manager.script_path = manager_data["script_path"]
357
357
 
358
358
  instance.managers.append(manager)
@@ -422,7 +422,15 @@ class WTLocalManager:
422
422
  if session_name in window_title or not window_title:
423
423
  session_windows.append(proc)
424
424
 
425
- active_sessions.append({"session_name": session_name, "is_active": len(session_windows) > 0, "tab_count": len(manager.tab_config), "tabs": list(manager.tab_config.keys()), "windows": session_windows})
425
+ active_sessions.append(
426
+ {
427
+ "session_name": session_name,
428
+ "is_active": len(session_windows) > 0,
429
+ "tab_count": len(manager.layout_config["layoutTabs"]) if manager.layout_config else 0,
430
+ "tabs": [tab["tabName"] for tab in manager.layout_config["layoutTabs"]] if manager.layout_config else [],
431
+ "windows": session_windows,
432
+ }
433
+ )
426
434
 
427
435
  except Exception as e:
428
436
  logger.error(f"Error listing active sessions: {e}")
@@ -451,12 +459,33 @@ class WTLocalManager:
451
459
 
452
460
 
453
461
  if __name__ == "__main__":
454
- # Example usage
455
- sample_sessions = {
456
- "development": {"🚀Frontend": ("~/code/myapp/frontend", "npm run dev"), "⚙️Backend": ("~/code/myapp/backend", "python manage.py runserver"), "📊Monitor": ("~", "Get-Process | Sort-Object CPU -Descending | Select-Object -First 10")},
457
- "testing": {"🧪Tests": ("~/code/myapp", "pytest --watch"), "🔍Coverage": ("~/code/myapp", "python -m coverage run --source=. -m pytest"), "📝Logs": ("~/logs", "Get-Content app.log -Wait")},
458
- "deployment": {"🐳Docker": ("~/code/myapp", "docker-compose up"), "☸️K8s": ("~/k8s", "kubectl get pods --watch"), "📈Metrics": ("~", 'Get-Counter "\\Processor(_Total)\\% Processor Time" -SampleInterval 2 -MaxSamples 30')},
459
- }
462
+ # Example usage with new schema
463
+ sample_sessions: list[LayoutConfig] = [
464
+ {
465
+ "layoutName": "DevelopmentEnv",
466
+ "layoutTabs": [
467
+ {"tabName": "🚀Frontend", "startDir": "~/code/myapp/frontend", "command": "npm run dev"},
468
+ {"tabName": "⚙️Backend", "startDir": "~/code/myapp/backend", "command": "python manage.py runserver"},
469
+ {"tabName": "📊Monitor", "startDir": "~", "command": "Get-Process | Sort-Object CPU -Descending | Select-Object -First 10"},
470
+ ],
471
+ },
472
+ {
473
+ "layoutName": "TestingEnv",
474
+ "layoutTabs": [
475
+ {"tabName": "🧪Tests", "startDir": "~/code/myapp", "command": "pytest --watch"},
476
+ {"tabName": "🔍Coverage", "startDir": "~/code/myapp", "command": "python -m coverage run --source=. -m pytest"},
477
+ {"tabName": "📝Logs", "startDir": "~/logs", "command": "Get-Content app.log -Wait"},
478
+ ],
479
+ },
480
+ {
481
+ "layoutName": "DeploymentEnv",
482
+ "layoutTabs": [
483
+ {"tabName": "🐳Docker", "startDir": "~/code/myapp", "command": "docker-compose up"},
484
+ {"tabName": "☸️K8s", "startDir": "~/k8s", "command": "kubectl get pods --watch"},
485
+ {"tabName": "📈Metrics", "startDir": "~", "command": 'Get-Counter "\\Processor(_Total)\\% Processor Time" -SampleInterval 2 -MaxSamples 30'},
486
+ ],
487
+ },
488
+ ]
460
489
 
461
490
  try:
462
491
  # Create the local manager