machineconfig 5.21__py3-none-any.whl → 5.23__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 (45) hide show
  1. machineconfig/cluster/sessions_managers/helpers/zellij_local_helper.py +298 -0
  2. machineconfig/cluster/sessions_managers/helpers/zellij_local_helper_restart.py +77 -0
  3. machineconfig/cluster/sessions_managers/helpers/zellij_local_helper_with_panes.py +228 -0
  4. machineconfig/cluster/sessions_managers/helpers/zellij_local_manager_helper.py +165 -0
  5. machineconfig/cluster/sessions_managers/wt_local.py +100 -75
  6. machineconfig/cluster/sessions_managers/wt_local_manager.py +17 -21
  7. machineconfig/cluster/sessions_managers/wt_remote.py +51 -43
  8. machineconfig/cluster/sessions_managers/wt_remote_manager.py +16 -8
  9. machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +6 -19
  10. machineconfig/cluster/sessions_managers/zellij_local.py +79 -371
  11. machineconfig/cluster/sessions_managers/zellij_local_manager.py +20 -168
  12. machineconfig/cluster/sessions_managers/zellij_remote.py +38 -39
  13. machineconfig/cluster/sessions_managers/zellij_remote_manager.py +13 -10
  14. machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +4 -1
  15. machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +5 -20
  16. machineconfig/profile/shell.py +1 -1
  17. machineconfig/scripts/python/ai/scripts/lint_and_type_check.ps1 +17 -17
  18. machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +17 -17
  19. machineconfig/scripts/python/ai/solutions/copilot/instructions/python/dev.instructions.md +1 -1
  20. machineconfig/scripts/python/ai/solutions/copilot/prompts/pyright_fix.md +16 -0
  21. machineconfig/scripts/python/ai/solutions/generic.py +15 -4
  22. machineconfig/scripts/python/cloud_repo_sync.py +26 -25
  23. machineconfig/scripts/python/count_lines.py +6 -6
  24. machineconfig/scripts/python/croshell.py +2 -4
  25. machineconfig/scripts/python/devops.py +7 -2
  26. machineconfig/scripts/python/devops_status.py +521 -0
  27. machineconfig/scripts/python/devops_update_repos.py +1 -1
  28. machineconfig/scripts/python/fire_agents_help_launch.py +6 -1
  29. machineconfig/scripts/python/fire_jobs_args_helper.py +4 -1
  30. machineconfig/scripts/python/helpers/repo_sync_helpers.py +0 -43
  31. machineconfig/scripts/python/onetimeshare.py +0 -1
  32. machineconfig/scripts/windows/share_smb.ps1 +0 -6
  33. machineconfig/setup_linux/repos.sh +1 -29
  34. machineconfig/setup_windows/repos.ps1 +0 -12
  35. machineconfig/utils/files/read.py +4 -6
  36. machineconfig/utils/notifications.py +1 -1
  37. machineconfig/utils/source_of_truth.py +1 -1
  38. machineconfig/utils/ssh.py +2 -13
  39. {machineconfig-5.21.dist-info → machineconfig-5.23.dist-info}/METADATA +1 -1
  40. {machineconfig-5.21.dist-info → machineconfig-5.23.dist-info}/RECORD +43 -39
  41. machineconfig/cluster/sessions_managers/ffile.py +0 -4
  42. machineconfig/scripts/python/ai/solutions/copilot/prompts/allLintersAndTypeCheckers.prompt.md +0 -5
  43. {machineconfig-5.21.dist-info → machineconfig-5.23.dist-info}/WHEEL +0 -0
  44. {machineconfig-5.21.dist-info → machineconfig-5.23.dist-info}/entry_points.txt +0 -0
  45. {machineconfig-5.21.dist-info → machineconfig-5.23.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,165 @@
1
+ #!/usr/bin/env python3
2
+ import json
3
+ import logging
4
+ import shutil
5
+ import subprocess
6
+ from datetime import datetime
7
+ from pathlib import Path
8
+ from typing import TYPE_CHECKING, Optional
9
+
10
+ from machineconfig.cluster.sessions_managers.zellij_utils.monitoring_types import ActiveSessionInfo, StartResult
11
+ from machineconfig.utils.schemas.layouts.layout_types import LayoutConfig
12
+
13
+ if TYPE_CHECKING:
14
+ from machineconfig.cluster.sessions_managers.zellij_local import ZellijLayoutGenerator
15
+
16
+
17
+ logger = logging.getLogger(__name__)
18
+ TMP_SERIALIZATION_DIR = Path.home() / "tmp_results" / "zellij_sessions" / "serialized"
19
+
20
+
21
+ def list_saved_sessions() -> list[str]:
22
+ """List all saved session IDs."""
23
+ if not TMP_SERIALIZATION_DIR.exists():
24
+ return []
25
+ sessions = []
26
+ for item in TMP_SERIALIZATION_DIR.iterdir():
27
+ if item.is_dir() and (item / "metadata.json").exists():
28
+ sessions.append(item.name)
29
+ return sorted(sessions)
30
+
31
+
32
+ def delete_session(session_id: str) -> bool:
33
+ """Delete a saved session."""
34
+ session_dir = TMP_SERIALIZATION_DIR / session_id
35
+ if not session_dir.exists():
36
+ logger.warning(f"Session directory not found: {session_dir}")
37
+ return False
38
+ try:
39
+ shutil.rmtree(session_dir)
40
+ logger.info(f"✅ Deleted session: {session_id}")
41
+ return True
42
+ except Exception as e:
43
+ logger.error(f"Failed to delete session {session_id}: {e}")
44
+ return False
45
+
46
+
47
+ def get_all_session_names(managers: list["ZellijLayoutGenerator"]) -> list[str]:
48
+ """Get all managed session names."""
49
+ return [manager.session_name for manager in managers]
50
+
51
+
52
+ def attach_to_session(managers: list["ZellijLayoutGenerator"], session_name: Optional[str]) -> str:
53
+ """
54
+ Generate command to attach to a specific session or list attachment commands for all.
55
+
56
+ Args:
57
+ managers: List of ZellijLayoutGenerator instances
58
+ session_name: Specific session to attach to, or None for all sessions
59
+
60
+ Returns:
61
+ Command string to attach to session(s)
62
+ """
63
+ if session_name:
64
+ for manager in managers:
65
+ if manager.session_name == session_name:
66
+ return f"zellij attach {session_name}"
67
+ raise ValueError(f"Session '{session_name}' not found")
68
+ else:
69
+ commands: list[str] = []
70
+ for manager in managers:
71
+ commands.append(f"# Attach to session '{manager.session_name}':")
72
+ commands.append(f"zellij attach {manager.session_name}")
73
+ commands.append("")
74
+ return "\n".join(commands)
75
+
76
+
77
+ def list_active_sessions(managers: list["ZellijLayoutGenerator"]) -> list[ActiveSessionInfo]:
78
+ """List currently active zellij sessions managed by this instance."""
79
+ active_sessions: list[ActiveSessionInfo] = []
80
+ try:
81
+ result = subprocess.run(["zellij", "list-sessions"], capture_output=True, text=True, timeout=10)
82
+ if result.returncode == 0:
83
+ all_sessions = result.stdout.strip().split("\n") if result.stdout.strip() else []
84
+ for manager in managers:
85
+ session_name = manager.session_name
86
+ is_active = any(session_name in session for session in all_sessions)
87
+ tab_info = []
88
+ tab_count = 0
89
+ if manager.layout_config:
90
+ tab_count = len(manager.layout_config["layoutTabs"])
91
+ tab_info = [tab["tabName"] for tab in manager.layout_config["layoutTabs"]]
92
+ active_sessions.append({"session_name": session_name, "is_active": is_active, "tab_count": tab_count, "tabs": tab_info})
93
+ except Exception as e:
94
+ logger.error(f"Error listing active sessions: {e}")
95
+ return active_sessions
96
+
97
+
98
+ def save_manager(session_layouts: list[LayoutConfig], managers: list["ZellijLayoutGenerator"], session_name_prefix: str, session_id: Optional[str]) -> str:
99
+ """Save the manager state to disk."""
100
+ if session_id is None:
101
+ import uuid
102
+ session_id = str(uuid.uuid4())[:8]
103
+ session_dir = TMP_SERIALIZATION_DIR / session_id
104
+ session_dir.mkdir(parents=True, exist_ok=True)
105
+ config_file = session_dir / "session_layouts.json"
106
+ text = json.dumps(session_layouts, indent=2, ensure_ascii=False)
107
+ config_file.write_text(text, encoding="utf-8")
108
+ metadata = {"session_name_prefix": session_name_prefix, "created_at": str(datetime.now()), "num_managers": len(managers), "sessions": [item["layoutName"] for item in session_layouts], "manager_type": "ZellijLocalManager"}
109
+ metadata_file = session_dir / "metadata.json"
110
+ text = json.dumps(metadata, indent=2, ensure_ascii=False)
111
+ metadata_file.write_text(text, encoding="utf-8")
112
+ managers_dir = session_dir / "managers"
113
+ managers_dir.mkdir(exist_ok=True)
114
+ for i, manager in enumerate(managers):
115
+ manager_data = {"session_name": manager.session_name, "layout_config": manager.layout_config, "layout_path": manager.layout_path}
116
+ manager_file = managers_dir / f"manager_{i}_{manager.session_name}.json"
117
+ text = json.dumps(manager_data, indent=2, ensure_ascii=False)
118
+ manager_file.write_text(text, encoding="utf-8")
119
+ logger.info(f"✅ Saved ZellijLocalManager session to: {session_dir}")
120
+ return session_id
121
+
122
+
123
+ def load_manager(session_id: str):
124
+ """Load a saved manager state from disk and return the data needed to reconstruct ZellijLocalManager."""
125
+ from machineconfig.cluster.sessions_managers.zellij_local import ZellijLayoutGenerator
126
+
127
+ session_dir = TMP_SERIALIZATION_DIR / session_id
128
+ if not session_dir.exists():
129
+ raise FileNotFoundError(f"Session directory not found: {session_dir}")
130
+ config_file = session_dir / "session_layouts.json"
131
+ if not config_file.exists():
132
+ raise FileNotFoundError(f"Configuration file not found: {config_file}")
133
+ text = config_file.read_text(encoding="utf-8")
134
+ session_layouts = json.loads(text)
135
+ managers = []
136
+ managers_dir = session_dir / "managers"
137
+ if managers_dir.exists():
138
+ manager_files = sorted(managers_dir.glob("manager_*.json"))
139
+ for manager_file in manager_files:
140
+ try:
141
+ text = manager_file.read_text(encoding="utf-8")
142
+ manager_data = json.loads(text)
143
+ manager = ZellijLayoutGenerator(layout_config=manager_data["layout_config"], session_name=manager_data["session_name"])
144
+ manager.layout_path = manager_data["layout_path"]
145
+ managers.append(manager)
146
+ except Exception as e:
147
+ logger.warning(f"Failed to load manager from {manager_file}: {e}")
148
+ logger.info(f"✅ Loaded ZellijLocalManager session from: {session_dir}")
149
+ return session_layouts, managers
150
+
151
+
152
+ def kill_all_sessions(managers: list["ZellijLayoutGenerator"]) -> dict[str, StartResult]:
153
+ """Kill all managed zellij sessions."""
154
+ results: dict[str, StartResult] = {}
155
+ for manager in managers:
156
+ try:
157
+ session_name = manager.session_name
158
+ cmd = f"zellij delete-session --force {session_name}"
159
+ logger.info(f"Killing session '{session_name}'")
160
+ result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=10)
161
+ results[session_name] = {"success": result.returncode == 0, "message": "Session killed" if result.returncode == 0 else result.stderr}
162
+ except Exception as e:
163
+ key = getattr(manager, "session_name", None) or f"manager_{managers.index(manager)}"
164
+ results[key] = {"success": False, "error": str(e)}
165
+ return results
@@ -13,27 +13,30 @@ import random
13
13
  import string
14
14
  import json
15
15
  import platform
16
- from typing import Dict, List, Optional, Any
16
+ from typing import Optional, Any
17
17
  from pathlib import Path
18
18
  import logging
19
+
19
20
  from rich.console import Console
21
+ from rich.panel import Panel
22
+ from rich.table import Table
20
23
 
21
24
  from machineconfig.utils.schemas.layouts.layout_types import LayoutConfig
22
25
 
23
26
  logging.basicConfig(level=logging.INFO)
24
27
  logger = logging.getLogger(__name__)
25
28
  console = Console()
26
- TMP_LAYOUT_DIR = Path.home().joinpath("tmp_results", "session_manager", "wt", "layout_manager")
27
29
 
28
30
  # Check if we're on Windows
29
31
  IS_WINDOWS = platform.system().lower() == "windows"
30
32
  POWERSHELL_CMD = "powershell" if IS_WINDOWS else "pwsh" # Use pwsh on non-Windows systems
33
+ TMP_LAYOUT_DIR = Path.home() / "tmp_results" / "wt_layouts"
31
34
 
32
35
 
33
36
  class WTLayoutGenerator:
34
- def __init__(self):
35
- self.session_name: Optional[str] = None
36
- self.layout_config: Optional[LayoutConfig] = None # Store the complete layout config
37
+ def __init__(self, layout_config: LayoutConfig, session_name: str):
38
+ self.session_name: str = session_name
39
+ self.layout_config: LayoutConfig = layout_config.copy()
37
40
  self.script_path: Optional[str] = None # Store the full path to the PowerShell script
38
41
 
39
42
  @staticmethod
@@ -42,7 +45,7 @@ class WTLayoutGenerator:
42
45
  return "".join(random.choices(string.ascii_lowercase + string.digits, k=length))
43
46
 
44
47
  @staticmethod
45
- def _parse_command(command: str) -> tuple[str, List[str]]:
48
+ def _parse_command(command: str) -> tuple[str, list[str]]:
46
49
  try:
47
50
  parts = shlex.split(command)
48
51
  if not parts:
@@ -77,41 +80,35 @@ class WTLayoutGenerator:
77
80
  if not tab["startDir"].strip():
78
81
  raise ValueError(f"Invalid startDir for tab '{tab['tabName']}': {tab['startDir']}")
79
82
 
80
- def create_wt_layout(self, layout_config: LayoutConfig, output_dir: Optional[str]) -> str:
81
- WTLayoutGenerator._validate_layout_config(layout_config)
82
- logger.info(f"Creating Windows Terminal layout '{layout_config['layoutName']}' with {len(layout_config['layoutTabs'])} tabs")
83
-
84
- # Store session name and layout config for status checking
85
- self.session_name = layout_config["layoutName"]
86
- self.layout_config = layout_config.copy()
83
+ def create_layout_file(self) -> bool:
84
+ """Create Windows Terminal layout file and return success status."""
85
+ WTLayoutGenerator._validate_layout_config(self.layout_config)
86
+ tab_count = len(self.layout_config['layoutTabs'])
87
+ layout_name = self.layout_config['layoutName']
88
+ console.print(f"[bold cyan]📋 Creating Windows Terminal layout[/bold cyan] [bright_green]'{layout_name}' with {tab_count} tabs[/bright_green]")
89
+
90
+ for tab in self.layout_config['layoutTabs']:
91
+ console.print(f" [yellow]→[/yellow] [bold]{tab['tabName']}[/bold] [dim]in[/dim] [blue]{tab['startDir']}[/blue]")
87
92
 
88
93
  # Generate Windows Terminal command
89
- wt_command = self._generate_wt_command_string(layout_config, self.session_name)
90
-
91
- try:
92
- random_suffix = WTLayoutGenerator._generate_random_suffix(8)
93
- if output_dir:
94
- output_path = Path(output_dir)
95
- output_path.mkdir(parents=True, exist_ok=True)
96
- script_file = output_path / f"wt_layout_{random_suffix}.ps1"
97
- else:
98
- # Use the predefined TMP_LAYOUT_DIR for temporary files
99
- TMP_LAYOUT_DIR.mkdir(parents=True, exist_ok=True)
100
- script_file = TMP_LAYOUT_DIR / f"wt_layout_{self.session_name}_{random_suffix}.ps1"
94
+ wt_command = self._generate_wt_command_string(self.layout_config, self.session_name)
101
95
 
102
- # Create PowerShell script
103
- text = f"""# Windows Terminal layout for {self.session_name}
96
+ random_suffix = WTLayoutGenerator._generate_random_suffix(8)
97
+ # Create PowerShell script content
98
+ script_content = f"""# Windows Terminal layout for {self.session_name}
104
99
  # Generated with random suffix: {random_suffix}
105
100
  {wt_command}
106
101
  """
107
- script_file.write_text(text, encoding="utf-8")
102
+ # Write to file
103
+ random_suffix = WTLayoutGenerator._generate_random_suffix(8)
104
+ tmp_dir = Path(TMP_LAYOUT_DIR)
105
+ tmp_dir.mkdir(parents=True, exist_ok=True)
106
+ script_file = tmp_dir / f"wt_layout_{self.session_name}_{random_suffix}.ps1"
107
+ script_file.write_text(script_content, encoding="utf-8")
108
+ self.script_path = str(script_file.absolute())
108
109
 
109
- self.script_path = str(script_file.absolute())
110
- logger.info(f"Windows Terminal PowerShell script created: {self.script_path}")
111
- return self.script_path
112
- except OSError as e:
113
- logger.error(f"Failed to create PowerShell script: {e}")
114
- raise
110
+ console.print(f"[bold green]✅ Layout created successfully:[/bold green] [cyan]{self.script_path}[/cyan]")
111
+ return True
115
112
 
116
113
  def _generate_wt_command_string(self, layout_config: LayoutConfig, window_name: str) -> str:
117
114
  """Generate complete Windows Terminal command string."""
@@ -154,7 +151,7 @@ class WTLayoutGenerator:
154
151
  WTLayoutGenerator._validate_layout_config(layout_config)
155
152
  return self._generate_wt_command_string(layout_config, "preview")
156
153
 
157
- def check_all_commands_status(self) -> Dict[str, Dict[str, Any]]:
154
+ def check_all_commands_status(self) -> dict[str, dict[str, Any]]:
158
155
  if not self.layout_config:
159
156
  logger.warning("No layout config tracked. Make sure to create a layout first.")
160
157
  return {}
@@ -167,7 +164,7 @@ class WTLayoutGenerator:
167
164
  return status_report
168
165
 
169
166
  @staticmethod
170
- def check_wt_session_status(session_name: str) -> Dict[str, Any]:
167
+ def check_wt_session_status(session_name: str) -> dict[str, Any]:
171
168
  try:
172
169
  # Simplified Windows Terminal process check
173
170
  ps_script = """
@@ -223,7 +220,7 @@ try {
223
220
  return {"wt_running": False, "error": str(e), "session_name": session_name}
224
221
 
225
222
  @staticmethod
226
- def check_command_status(tab_name: str, layout_config: LayoutConfig) -> Dict[str, Any]:
223
+ def check_command_status(tab_name: str, layout_config: LayoutConfig) -> dict[str, Any]:
227
224
  """Check if a command is running by looking for processes."""
228
225
  # Find the tab with the given name
229
226
  tab_config = None
@@ -290,7 +287,7 @@ try {{
290
287
  logger.error(f"Error checking command status for tab '{tab_name}': {e}")
291
288
  return {"status": "error", "error": str(e), "running": False, "command": command, "tab_name": tab_name}
292
289
 
293
- def get_status_report(self) -> Dict[str, Any]:
290
+ def get_status_report(self) -> dict[str, Any]:
294
291
  """Get status report for this layout including Windows Terminal and commands."""
295
292
  wt_status = WTLayoutGenerator.check_wt_session_status(self.session_name or "default")
296
293
  commands_status = self.check_all_commands_status()
@@ -311,64 +308,90 @@ try {{
311
308
  commands = status["commands"]
312
309
  summary = status["summary"]
313
310
 
314
- print("=" * 80)
315
- print("🖥️ WINDOWS TERMINAL LAYOUT STATUS REPORT")
316
- print("=" * 80)
311
+ console.print()
312
+ console.print(Panel.fit("WINDOWS TERMINAL LAYOUT STATUS REPORT", style="bold cyan"))
317
313
 
318
314
  # Windows Terminal session status
319
- print(f"🪟 Session: {self.session_name}")
320
315
  if wt_session.get("wt_running", False):
321
316
  if wt_session.get("session_exists", False):
322
317
  session_windows = wt_session.get("session_windows", [])
323
318
  all_windows = wt_session.get("all_windows", [])
324
- print("✅ Windows Terminal is running")
325
- print(f" Session windows: {len(session_windows)}")
326
- print(f" Total WT windows: {len(all_windows)}")
319
+ console.print(f"[bold green]✅ Windows Terminal session[/bold green] [yellow]'{self.session_name}'[/yellow] [green]is running[/green]")
320
+ console.print(f" Session windows: {len(session_windows)}")
321
+ console.print(f" Total WT windows: {len(all_windows)}")
327
322
  else:
328
- print("⚠️ Windows Terminal is running but no session windows found")
323
+ console.print(f"[bold yellow]⚠️ Windows Terminal is running but session[/bold yellow] [yellow]'{self.session_name}'[/yellow] [yellow]not found[/yellow]")
329
324
  else:
330
325
  error_msg = wt_session.get("error", "Unknown error")
331
- print(f"❌ Windows Terminal session issue: {error_msg}")
326
+ console.print(f"[bold red]❌ Windows Terminal session issue:[/bold red] [red]{error_msg}[/red]")
332
327
 
333
- print()
328
+ console.print()
329
+
330
+ # Commands status table
331
+ table = Table(title="📋 COMMAND STATUS", show_header=True, header_style="bold magenta")
332
+ table.add_column("Tab", style="cyan", no_wrap=True)
333
+ table.add_column("Status", justify="center")
334
+ table.add_column("PID", justify="center", style="dim")
335
+ table.add_column("Command", style="green", max_width=40)
334
336
 
335
- # Commands in this layout
336
- print(f"📋 COMMANDS ({summary['running_commands']}/{summary['total_commands']} running):")
337
337
  for tab_name, cmd_status in commands.items():
338
- status_icon = "✅" if cmd_status.get("running", False) else "❌"
339
- cmd_text = cmd_status.get("command", "Unknown")[:50]
340
- if len(cmd_status.get("command", "")) > 50:
341
- cmd_text += "..."
338
+ if cmd_status.get("running", False):
339
+ status_text = "[bold green]✅ Running[/bold green]"
340
+ processes = cmd_status.get("processes", [])
341
+ if processes:
342
+ proc = processes[0]
343
+ pid = str(proc.get("pid", "N/A"))
344
+ else:
345
+ pid = "N/A"
346
+ else:
347
+ status_text = "[bold red]❌ Stopped[/bold red]"
348
+ pid = "N/A"
342
349
 
343
- print(f" {status_icon} {tab_name}: {cmd_text}")
350
+ command = cmd_status.get("command", "Unknown")
351
+ if len(command) > 35:
352
+ command = command[:32] + "..."
344
353
 
345
- if cmd_status.get("processes"):
346
- for proc in cmd_status["processes"][:2]: # Show first 2 processes
347
- pid = proc.get("pid", "Unknown")
348
- name = proc.get("name", "Unknown")
349
- console.print(f" [dim]└─[/dim] PID {pid}: {name}")
350
- print()
354
+ table.add_row(tab_name, status_text, pid, command)
351
355
 
352
- print("=" * 80)
356
+ console.print(table)
357
+ console.print()
353
358
 
359
+ # Summary panel
360
+ summary_text = f"""[bold]Total commands:[/bold] {summary['total_commands']}
361
+ [green]Running:[/green] {summary['running_commands']}
362
+ [red]Stopped:[/red] {summary['stopped_commands']}
363
+ [yellow]Session healthy:[/yellow] {"✅" if summary['session_healthy'] else "❌"}"""
354
364
 
355
- def create_wt_layout(layout_config: LayoutConfig, output_dir: Optional[str]) -> str:
356
- generator = WTLayoutGenerator()
357
- return generator.create_wt_layout(layout_config, output_dir)
365
+ console.print(Panel(summary_text, title="📊 Summary", style="blue"))
358
366
 
359
367
 
360
- def run_wt_layout(layout_config: LayoutConfig) -> str:
368
+ def create_wt_layout(layout_config: LayoutConfig, output_path: str) -> str:
369
+ session_name = layout_config["layoutName"]
370
+ generator = WTLayoutGenerator(layout_config=layout_config, session_name=session_name)
371
+ generator.create_layout_file()
372
+
373
+ if generator.script_path is None:
374
+ raise RuntimeError("Script path was not set after creating layout file")
375
+
376
+ logger.info(f"Windows Terminal PowerShell script created: {generator.script_path}")
377
+ return generator.script_path
378
+
379
+
380
+ def run_wt_layout(layout_config: LayoutConfig) -> None:
361
381
  """Create and run a Windows Terminal layout."""
362
- generator = WTLayoutGenerator()
363
- script_path = generator.create_wt_layout(layout_config, None)
382
+ session_name = layout_config["layoutName"]
383
+ generator = WTLayoutGenerator(layout_config=layout_config, session_name=session_name)
384
+ generator.create_layout_file()
385
+
386
+ if generator.script_path is None:
387
+ raise RuntimeError("Script path was not set after creating layout file")
364
388
 
365
389
  # Execute the script
366
- cmd = f'powershell -ExecutionPolicy Bypass -File "{script_path}"'
390
+ cmd = f'powershell -ExecutionPolicy Bypass -File "{generator.script_path}"'
367
391
  result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
368
392
 
369
393
  if result.returncode == 0:
370
394
  print(f"Windows Terminal layout is running @ {layout_config['layoutName']}")
371
- return script_path
372
395
  else:
373
396
  logger.error(f"Failed to run Windows Terminal layout: {result.stderr}")
374
397
  raise RuntimeError(f"Failed to run layout: {result.stderr}")
@@ -391,15 +414,17 @@ if __name__ == "__main__":
391
414
  {"tabName": "Monitor", "startDir": "~", "command": "lf"}
392
415
  ]}
393
416
  try:
394
- generator = WTLayoutGenerator()
395
- script_path = generator.create_wt_layout(layout_config=sample_layout, output_dir=None)
396
- print(f"✅ Windows Terminal layout created: {script_path}")
397
- preview = generator.get_wt_layout_preview(layout_config=sample_layout)
417
+ session_name = sample_layout["layoutName"]
418
+ generator = WTLayoutGenerator(layout_config=sample_layout, session_name=session_name)
419
+ generator.create_layout_file()
420
+
421
+ print(f"✅ Windows Terminal layout created: {generator.script_path}")
422
+ preview = generator.get_wt_layout_preview(generator.layout_config)
398
423
  print(f"📋 Command Preview:\n{preview}")
399
424
  print("🔍 Current status:")
400
425
  generator.print_status_report()
401
426
  print("▶️ To run this layout, execute:")
402
- print(f' powershell -ExecutionPolicy Bypass -File "{script_path}"')
427
+ print(f' powershell -ExecutionPolicy Bypass -File "{generator.script_path}"')
403
428
  except Exception as e:
404
429
  print(f"❌ Error: {e}")
405
430
  import traceback
@@ -5,26 +5,23 @@ import uuid
5
5
  import logging
6
6
  import subprocess
7
7
  from pathlib import Path
8
- from typing import TypedDict, Optional, Dict, List, Any
8
+ from typing import Optional, Any
9
9
  from rich.console import Console
10
10
  from machineconfig.utils.scheduler import Scheduler
11
11
  from machineconfig.cluster.sessions_managers.wt_local import WTLayoutGenerator
12
12
  from machineconfig.utils.schemas.layouts.layout_types import LayoutConfig
13
13
  from machineconfig.cluster.sessions_managers.zellij_utils.monitoring_types import (
14
- StartResult, GlobalSummary, ActiveSessionInfo, CommandStatus, CommandSummary
14
+ StartResult, GlobalSummary, ActiveSessionInfo
15
15
  )
16
16
 
17
17
 
18
- class WTSessionReport(TypedDict):
19
- session_status: Dict[str, Any] # WT-specific session status
20
- commands_status: Dict[str, CommandStatus]
21
- summary: CommandSummary
18
+
22
19
 
23
20
  logging.basicConfig(level=logging.INFO)
24
21
  logger = logging.getLogger(__name__)
25
22
  console = Console()
23
+ TMP_SERIALIZATION_DIR = Path.home() / "tmp_results" / "wt_sessions" / "serialized"
26
24
 
27
- TMP_SERIALIZATION_DIR = Path.home().joinpath("tmp_results", "session_manager", "wt", "local_manager")
28
25
 
29
26
 
30
27
  class WTLocalManager:
@@ -41,21 +38,22 @@ class WTLocalManager:
41
38
  """
42
39
  self.session_name_prefix = session_name_prefix
43
40
  self.session_layouts = session_layouts # Store the original config
44
- self.managers: List[WTLayoutGenerator] = []
41
+ self.managers: list[WTLayoutGenerator] = []
45
42
 
46
43
  # Create a WTLayoutGenerator for each session
47
44
  for layout_config in session_layouts:
48
- manager = WTLayoutGenerator()
49
- manager.create_wt_layout(layout_config=layout_config, output_dir=None)
45
+ session_name = layout_config["layoutName"]
46
+ manager = WTLayoutGenerator(layout_config=layout_config, session_name=session_name)
47
+ manager.create_layout_file()
50
48
  self.managers.append(manager)
51
49
 
52
50
  logger.info(f"Initialized WTLocalManager with {len(self.managers)} sessions")
53
51
 
54
- def get_all_session_names(self) -> List[str]:
52
+ def get_all_session_names(self) -> list[str]:
55
53
  """Get all managed session names."""
56
- return [manager.session_name for manager in self.managers if manager.session_name is not None]
54
+ return [manager.session_name for manager in self.managers]
57
55
 
58
- def start_all_sessions(self) -> Dict[str, StartResult]:
56
+ def start_all_sessions(self) -> dict[str, StartResult]:
59
57
  """Start all Windows Terminal sessions with their layouts."""
60
58
  results = {}
61
59
  for manager in self.managers:
@@ -86,7 +84,7 @@ class WTLocalManager:
86
84
 
87
85
  return results
88
86
 
89
- def kill_all_sessions(self) -> Dict[str, StartResult]:
87
+ def kill_all_sessions(self) -> dict[str, StartResult]:
90
88
  """Kill all managed Windows Terminal sessions."""
91
89
  results = {}
92
90
  for manager in self.managers:
@@ -130,7 +128,7 @@ class WTLocalManager:
130
128
  commands.append("")
131
129
  return "\n".join(commands)
132
130
 
133
- def check_all_sessions_status(self) -> Dict[str, WTSessionReport]:
131
+ def check_all_sessions_status(self) -> dict[str, dict[str, Any]]:
134
132
  """Check the status of all sessions and their commands."""
135
133
  status_report = {}
136
134
 
@@ -361,9 +359,7 @@ class WTLocalManager:
361
359
  manager_data = json.loads(text)
362
360
 
363
361
  # Recreate the manager
364
- manager = WTLayoutGenerator()
365
- manager.session_name = manager_data["session_name"]
366
- manager.layout_config = manager_data["layout_config"]
362
+ manager = WTLayoutGenerator(layout_config=manager_data["layout_config"], session_name=manager_data["session_name"])
367
363
  manager.script_path = manager_data["script_path"]
368
364
 
369
365
  instance.managers.append(manager)
@@ -375,7 +371,7 @@ class WTLocalManager:
375
371
  return instance
376
372
 
377
373
  @staticmethod
378
- def list_saved_sessions() -> List[str]:
374
+ def list_saved_sessions() -> list[str]:
379
375
  """List all saved session IDs."""
380
376
  if not TMP_SERIALIZATION_DIR.exists():
381
377
  return []
@@ -406,7 +402,7 @@ class WTLocalManager:
406
402
  logger.error(f"Failed to delete session {session_id}: {e}")
407
403
  return False
408
404
 
409
- def list_active_sessions(self) -> List[ActiveSessionInfo]:
405
+ def list_active_sessions(self) -> list[ActiveSessionInfo]:
410
406
  """List currently active Windows Terminal sessions managed by this instance."""
411
407
  active_sessions = []
412
408
 
@@ -448,7 +444,7 @@ class WTLocalManager:
448
444
 
449
445
  return active_sessions
450
446
 
451
- def get_wt_overview(self) -> Dict[str, Any]:
447
+ def get_wt_overview(self) -> dict[str, Any]:
452
448
  """Get overview of all Windows Terminal windows and processes."""
453
449
  try:
454
450
  result = subprocess.run(