machineconfig 2.1__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 (127) 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 +0 -35
  21. machineconfig/jobs/python/check_installations.py +1 -1
  22. machineconfig/jobs/python_custom_installers/dev/code.py +0 -13
  23. machineconfig/jobs/python_generic_installers/config.json +1 -1
  24. machineconfig/profile/create.py +13 -4
  25. machineconfig/profile/create_hardlinks.py +3 -1
  26. machineconfig/profile/shell.py +8 -7
  27. machineconfig/scripts/__init__.py +0 -2
  28. machineconfig/scripts/linux/devops +6 -4
  29. machineconfig/scripts/python/ai/generate_files.py +14 -15
  30. machineconfig/scripts/python/ai/mcinit.py +8 -5
  31. machineconfig/scripts/python/archive/tmate_conn.py +5 -5
  32. machineconfig/scripts/python/archive/tmate_start.py +7 -7
  33. machineconfig/scripts/python/choose_wezterm_theme.py +35 -32
  34. machineconfig/scripts/python/cloud_copy.py +22 -13
  35. machineconfig/scripts/python/cloud_mount.py +35 -23
  36. machineconfig/scripts/python/cloud_repo_sync.py +38 -25
  37. machineconfig/scripts/python/cloud_sync.py +4 -4
  38. machineconfig/scripts/python/croshell.py +37 -28
  39. machineconfig/scripts/python/devops.py +46 -27
  40. machineconfig/scripts/python/devops_add_identity.py +15 -25
  41. machineconfig/scripts/python/devops_add_ssh_key.py +7 -7
  42. machineconfig/scripts/python/devops_backup_retrieve.py +17 -15
  43. machineconfig/scripts/python/devops_devapps_install.py +26 -20
  44. machineconfig/scripts/python/devops_update_repos.py +142 -57
  45. machineconfig/scripts/python/dotfile.py +16 -14
  46. machineconfig/scripts/python/fire_agents.py +30 -23
  47. machineconfig/scripts/python/fire_jobs.py +86 -98
  48. machineconfig/scripts/python/fire_jobs_args_helper.py +84 -0
  49. machineconfig/scripts/python/fire_jobs_layout_helper.py +66 -0
  50. machineconfig/scripts/python/ftpx.py +24 -14
  51. machineconfig/scripts/python/get_zellij_cmd.py +8 -7
  52. machineconfig/scripts/python/helpers/cloud_helpers.py +33 -28
  53. machineconfig/scripts/python/helpers/helpers2.py +25 -14
  54. machineconfig/scripts/python/helpers/helpers4.py +44 -31
  55. machineconfig/scripts/python/helpers/helpers5.py +1 -1
  56. machineconfig/scripts/python/helpers/repo_sync_helpers.py +31 -9
  57. machineconfig/scripts/python/mount_nfs.py +8 -15
  58. machineconfig/scripts/python/mount_nw_drive.py +10 -5
  59. machineconfig/scripts/python/mount_ssh.py +8 -6
  60. machineconfig/scripts/python/repos.py +215 -57
  61. machineconfig/scripts/python/snapshot.py +0 -1
  62. machineconfig/scripts/python/start_slidev.py +10 -5
  63. machineconfig/scripts/python/start_terminals.py +22 -16
  64. machineconfig/scripts/python/viewer_template.py +0 -1
  65. machineconfig/scripts/python/wifi_conn.py +49 -76
  66. machineconfig/scripts/python/wsl_windows_transfer.py +8 -6
  67. machineconfig/settings/lf/linux/lfrc +1 -0
  68. machineconfig/setup_linux/web_shortcuts/croshell.sh +5 -0
  69. machineconfig/setup_linux/web_shortcuts/interactive.sh +1 -1
  70. machineconfig/setup_linux/web_shortcuts/ssh.sh +0 -4
  71. machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +3 -12
  72. machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +1 -1
  73. machineconfig/utils/code.py +2 -3
  74. machineconfig/utils/installer.py +2 -2
  75. machineconfig/utils/installer_utils/installer_abc.py +2 -4
  76. machineconfig/utils/installer_utils/installer_class.py +6 -4
  77. machineconfig/utils/links.py +103 -33
  78. machineconfig/utils/notifications.py +52 -38
  79. machineconfig/utils/options.py +14 -21
  80. machineconfig/utils/path.py +12 -12
  81. machineconfig/utils/path_reduced.py +239 -200
  82. machineconfig/utils/procs.py +1 -1
  83. machineconfig/utils/source_of_truth.py +27 -0
  84. machineconfig/utils/ssh.py +9 -19
  85. machineconfig/utils/terminal.py +4 -2
  86. machineconfig/utils/upgrade_packages.py +91 -0
  87. machineconfig/utils/utils2.py +1 -2
  88. machineconfig/utils/utils5.py +23 -11
  89. machineconfig/utils/ve.py +4 -1
  90. {machineconfig-2.1.dist-info → machineconfig-2.3.dist-info}/METADATA +13 -13
  91. {machineconfig-2.1.dist-info → machineconfig-2.3.dist-info}/RECORD +105 -121
  92. machineconfig-2.3.dist-info/entry_points.txt +2 -0
  93. machineconfig/cluster/sessions_managers/archive/create_zellij_template.py +0 -59
  94. machineconfig/cluster/sessions_managers/archive/session_managers.py +0 -183
  95. machineconfig/cluster/sessions_managers/demo_rich_zellij.py +0 -0
  96. machineconfig/jobs/__pycache__/__init__.cpython-313.pyc +0 -0
  97. machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  98. machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  99. machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  100. machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
  101. machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc +0 -0
  102. machineconfig/scripts/python/__pycache__/croshell.cpython-313.pyc +0 -0
  103. machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc +0 -0
  104. machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-313.pyc +0 -0
  105. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc +0 -0
  106. machineconfig/scripts/python/__pycache__/fire_jobs.cpython-313.pyc +0 -0
  107. machineconfig/scripts/python/ai/__pycache__/__init__.cpython-313.pyc +0 -0
  108. machineconfig/scripts/python/ai/__pycache__/generate_files.cpython-313.pyc +0 -0
  109. machineconfig/scripts/python/ai/__pycache__/mcinit.cpython-313.pyc +0 -0
  110. machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-313.pyc +0 -0
  111. machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-313.pyc +0 -0
  112. machineconfig/setup_linux/web_shortcuts/all.sh +0 -48
  113. machineconfig/setup_linux/web_shortcuts/update_system.sh +0 -48
  114. machineconfig/utils/utils.py +0 -97
  115. /machineconfig/cluster/{cloud_manager.py → remote/cloud_manager.py} +0 -0
  116. /machineconfig/cluster/{data_transfer.py → remote/data_transfer.py} +0 -0
  117. /machineconfig/cluster/{distribute.py → remote/distribute.py} +0 -0
  118. /machineconfig/cluster/{file_manager.py → remote/file_manager.py} +0 -0
  119. /machineconfig/cluster/{job_params.py → remote/job_params.py} +0 -0
  120. /machineconfig/cluster/{loader_runner.py → remote/loader_runner.py} +0 -0
  121. /machineconfig/cluster/{remote_machine.py → remote/remote_machine.py} +0 -0
  122. /machineconfig/cluster/{script_execution.py → remote/script_execution.py} +0 -0
  123. /machineconfig/cluster/{script_notify_upon_completion.py → remote/script_notify_upon_completion.py} +0 -0
  124. /machineconfig/{cluster/sessions_managers/archive/__init__.py → scripts/python/fire_jobs_streamlit_helper.py} +0 -0
  125. /machineconfig/setup_linux/web_shortcuts/{tmp.sh → android.sh} +0 -0
  126. {machineconfig-2.1.dist-info → machineconfig-2.3.dist-info}/WHEEL +0 -0
  127. {machineconfig-2.1.dist-info → machineconfig-2.3.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- from typing import Dict, Tuple, Optional, List, Any
2
+ from typing import Dict, Optional, List, Any
3
3
  from pathlib import Path
4
4
  import logging
5
5
  import json
@@ -11,6 +11,7 @@ from machineconfig.cluster.sessions_managers.wt_utils.layout_generator import WT
11
11
  from machineconfig.cluster.sessions_managers.wt_utils.process_monitor import WTProcessMonitor
12
12
  from machineconfig.cluster.sessions_managers.wt_utils.session_manager import WTSessionManager
13
13
  from machineconfig.cluster.sessions_managers.wt_utils.status_reporter import WTStatusReporter
14
+ from machineconfig.cluster.sessions_managers.layout_types import TabConfig
14
15
 
15
16
  logging.basicConfig(level=logging.INFO)
16
17
  logger = logging.getLogger(__name__)
@@ -21,7 +22,7 @@ class WTRemoteLayoutGenerator:
21
22
  def __init__(self, remote_name: str, session_name_prefix: str):
22
23
  self.remote_name = remote_name
23
24
  self.session_name = session_name_prefix + "_" + WTLayoutGenerator.generate_random_suffix()
24
- self.tab_config: Dict[str, Tuple[str, str]] = {}
25
+ self.tabs: List[TabConfig] = []
25
26
  self.script_path: Optional[str] = None
26
27
 
27
28
  # Initialize modular components
@@ -31,77 +32,22 @@ class WTRemoteLayoutGenerator:
31
32
  self.session_manager = WTSessionManager(self.remote_executor, self.session_name, TMP_LAYOUT_DIR)
32
33
  self.status_reporter = WTStatusReporter(self.process_monitor, self.session_manager)
33
34
 
34
- def copy_script_to_remote(self, local_script_file: Path, random_suffix: str) -> str:
35
- return self.session_manager.copy_script_to_remote(local_script_file, random_suffix)
35
+ # Tabs are stored and used as List[TabConfig]; no legacy dict compatibility
36
36
 
37
- def create_wt_layout(self, tab_config: Dict[str, Tuple[str, str]], output_dir: Optional[str] = None) -> str:
38
- logger.info(f"Creating Windows Terminal layout with {len(tab_config)} tabs for remote '{self.remote_name}'")
39
- self.tab_config = tab_config.copy()
37
+ def create_wt_layout(self, tabs: List[TabConfig], output_dir: Optional[str] = None) -> str:
38
+ logger.info(f"Creating Windows Terminal layout with {len(tabs)} tabs for remote '{self.remote_name}'")
39
+ self.tabs = tabs
40
40
  if output_dir:
41
41
  output_path = Path(output_dir)
42
42
  else:
43
43
  output_path = TMP_LAYOUT_DIR
44
- self.script_path = self.layout_generator.create_wt_script(tab_config, output_path, self.session_name)
44
+ self.script_path = self.layout_generator.create_wt_script(self.tabs, output_path, self.session_name)
45
45
  return self.script_path
46
46
 
47
- def get_layout_preview(self, tab_config: Dict[str, Tuple[str, str]]) -> str:
48
- return self.layout_generator.generate_wt_command(tab_config)
49
-
50
- def check_command_status(self, tab_name: str, use_verification: bool = True) -> Dict[str, Any]:
51
- return self.process_monitor.check_command_status(tab_name, self.tab_config, use_verification)
52
-
53
- def check_all_commands_status(self) -> Dict[str, Dict[str, Any]]:
54
- return self.process_monitor.check_all_commands_status(self.tab_config)
55
-
56
- def check_wt_session_status(self) -> Dict[str, Any]:
57
- return self.session_manager.check_wt_session_status()
58
-
59
- def get_comprehensive_status(self) -> Dict[str, Any]:
60
- return self.status_reporter.get_comprehensive_status(self.tab_config)
61
-
62
- def print_status_report(self) -> None:
63
- self.status_reporter.print_status_report(self.tab_config)
64
-
65
- def start_wt_session(self, script_file_path: Optional[str] = None) -> Dict[str, Any]:
66
- return self.session_manager.start_wt_session(script_file_path or self.script_path)
67
-
68
- def attach_to_session(self) -> None:
69
- self.session_manager.attach_to_session()
70
-
71
47
  # Legacy methods for backward compatibility
72
- def force_fresh_process_check(self, tab_name: str) -> Dict[str, Any]:
73
- return self.process_monitor.force_fresh_process_check(tab_name, self.tab_config)
74
-
75
- def verify_process_alive(self, pid: int) -> bool:
76
- return self.process_monitor.verify_process_alive(pid)
77
-
78
- def get_verified_process_status(self, tab_name: str) -> Dict[str, Any]:
79
- return self.process_monitor.get_verified_process_status(tab_name, self.tab_config)
80
-
81
- # Static methods for backward compatibility
82
- @staticmethod
83
- def run_remote_command(remote_name: str, command: str, timeout: int = 30):
84
- executor = WTRemoteExecutor(remote_name)
85
- return executor.run_command(command, timeout)
86
-
87
- def kill_wt_session(self, force: bool = True) -> Dict[str, Any]:
88
- """Kill Windows Terminal processes on the remote machine."""
89
- return self.session_manager.kill_wt_session(force)
90
-
91
- def create_new_tab(self, tab_name: str, cwd: str, command: str) -> Dict[str, Any]:
92
- """Create a new tab in the Windows Terminal session."""
93
- return self.session_manager.create_new_tab(tab_name, cwd, command, self.session_name)
94
-
95
- def get_wt_version(self) -> Dict[str, Any]:
96
- """Get Windows Terminal version information on the remote machine."""
97
- return self.session_manager.get_wt_version()
98
-
99
- def get_remote_windows_info(self) -> Dict[str, Any]:
100
- """Get information about the remote Windows system."""
101
- return self.remote_executor.get_remote_windows_info()
102
48
 
103
49
  def to_dict(self) -> Dict[str, Any]:
104
- return {"remote_name": self.remote_name, "session_name": self.session_name, "tab_config": self.tab_config, "script_path": self.script_path, "created_at": datetime.now().isoformat(), "class_name": self.__class__.__name__}
50
+ return {"remote_name": self.remote_name, "session_name": self.session_name, "tabs": self.tabs, "script_path": self.script_path, "created_at": datetime.now().isoformat(), "class_name": self.__class__.__name__}
105
51
 
106
52
  def to_json(self, file_path: Optional[str] = None) -> str:
107
53
  # Generate file path if not provided
@@ -160,7 +106,11 @@ class WTRemoteLayoutGenerator:
160
106
 
161
107
  # Restore state
162
108
  instance.session_name = data["session_name"]
163
- instance.tab_config = data["tab_config"]
109
+ # New schema only
110
+ if "tabs" in data:
111
+ instance.tabs = data["tabs"]
112
+ else:
113
+ instance.tabs = []
164
114
  instance.script_path = data["script_path"]
165
115
 
166
116
  logger.info(f"✅ Loaded WTRemoteLayoutGenerator from: {file_path}")
@@ -179,43 +129,15 @@ class WTRemoteLayoutGenerator:
179
129
  json_files = [f.name for f in dir_path.glob("*.json")]
180
130
  return sorted(json_files)
181
131
 
182
- def check_wt_available(self) -> bool:
183
- """Check if Windows Terminal is available on the remote machine."""
184
- return self.remote_executor.check_wt_available()
185
-
186
- def list_wt_processes(self) -> Dict[str, Any]:
187
- """List Windows Terminal processes on the remote machine."""
188
- return self.remote_executor.list_wt_processes()
189
-
190
- def kill_wt_processes(self, process_ids: Optional[List[Any]] = None) -> Dict[str, Any]:
191
- """Kill Windows Terminal processes on the remote machine."""
192
- return self.remote_executor.kill_wt_processes(process_ids)
193
-
194
- def get_windows_terminal_overview(self) -> Dict[str, Any]:
195
- """Get overview of Windows Terminal on the remote machine."""
196
- return self.status_reporter.get_windows_terminal_overview()
197
-
198
- def print_windows_terminal_overview(self) -> None:
199
- """Print overview of Windows Terminal on the remote machine."""
200
- self.status_reporter.print_windows_terminal_overview()
201
-
202
- def generate_status_summary(self) -> Dict[str, Any]:
203
- """Generate a concise status summary for monitoring."""
204
- return self.status_reporter.generate_status_summary(self.tab_config)
205
-
206
- def check_tab_specific_status(self, tab_name: str) -> Dict[str, Any]:
207
- """Get detailed status for a specific tab."""
208
- return self.status_reporter.check_tab_specific_status(tab_name, self.tab_config)
209
-
210
132
 
211
133
  if __name__ == "__main__":
212
134
  # Example usage
213
- sample_tabs = {
214
- "🤖Bot1": ("~/code/bytesense/bithence", "python bot1.py --create_new_bot True"),
215
- "🤖Bot2": ("~/code/bytesense/bithence", "python bot2.py --create_new_bot True"),
216
- "📊Monitor": ("~", "Get-Process | Sort-Object CPU -Descending | Select-Object -First 10"),
217
- "📝Logs": ("C:/logs", "Get-Content app.log -Wait"),
218
- }
135
+ sample_tabs: List[TabConfig] = [
136
+ {"tabName": "🤖Bot1", "startDir": "~/code/bytesense/bithence", "command": "python bot1.py --create_new_bot True"},
137
+ {"tabName": "🤖Bot2", "startDir": "~/code/bytesense/bithence", "command": "python bot2.py --create_new_bot True"},
138
+ {"tabName": "📊Monitor", "startDir": "~", "command": "Get-Process | Sort-Object CPU -Descending | Select-Object -First 10"},
139
+ {"tabName": "📝Logs", "startDir": "C:/logs", "command": "Get-Content app.log -Wait"},
140
+ ]
219
141
 
220
142
  # Replace 'myserver' with an actual SSH config alias for a Windows machine
221
143
  remote_name = "myserver" # This should be in ~/.ssh/config
@@ -228,11 +150,11 @@ if __name__ == "__main__":
228
150
  print(f"✅ Remote layout created successfully: {script_path}")
229
151
 
230
152
  # Check if Windows Terminal is available on remote
231
- wt_available = generator.check_wt_available()
153
+ wt_available = generator.remote_executor.check_wt_available()
232
154
  print(f"🖥️ Windows Terminal available on {remote_name}: {'✅' if wt_available else '❌'}")
233
155
 
234
156
  # Get remote Windows info
235
- windows_info = generator.get_remote_windows_info()
157
+ windows_info = generator.remote_executor.get_remote_windows_info()
236
158
  if windows_info.get("wt_available"):
237
159
  print(f"📦 Remote system info: {windows_info.get('windows_info', 'Unknown')}")
238
160
 
@@ -248,31 +170,31 @@ if __name__ == "__main__":
248
170
  # Demonstrate loading (using the full path)
249
171
  loaded_generator = WTRemoteLayoutGenerator.from_json(saved_path)
250
172
  print(f"✅ Session loaded successfully: {loaded_generator.session_name}")
251
- print(f"📊 Loaded tabs: {list(loaded_generator.tab_config.keys())}")
173
+ print(f"📊 Loaded tabs: {[tab['tabName'] for tab in loaded_generator.tabs]}")
252
174
 
253
175
  # Show command preview
254
- preview = generator.get_layout_preview(sample_tabs)
176
+ preview = generator.layout_generator.generate_wt_command(sample_tabs)
255
177
  print(f"\n📋 Command Preview:\n{preview}")
256
178
 
257
179
  # Demonstrate status checking
258
180
  print(f"\n🔍 Checking command status on remote '{remote_name}':")
259
- generator.print_status_report()
181
+ generator.status_reporter.print_status_report(generator.tabs)
260
182
 
261
183
  # Show Windows Terminal overview
262
184
  print("\n🖥️ Windows Terminal Overview:")
263
- generator.print_windows_terminal_overview()
185
+ generator.status_reporter.print_windows_terminal_overview()
264
186
 
265
187
  # Start the session (uncomment to actually start)
266
- # start_result = generator.start_wt_session()
188
+ # start_result = generator.session_manager.start_wt_session(generator.script_path)
267
189
  # print(f"Session start result: {start_result}")
268
190
 
269
191
  # Attach to session (uncomment to attach)
270
- # generator.attach_to_session()
192
+ # generator.session_manager.attach_to_session()
271
193
 
272
194
  print("\n▶️ To start this session, run:")
273
- print(" generator.start_wt_session()")
195
+ print(" generator.session_manager.start_wt_session(generator.script_path)")
274
196
  print("\n📎 To attach to this session, run:")
275
- print(" generator.attach_to_session()")
197
+ print(" generator.session_manager.attach_to_session()")
276
198
 
277
199
  except Exception as e:
278
200
  print(f"❌ Error: {e}")
@@ -7,6 +7,7 @@ from typing import Optional, Any
7
7
  from machineconfig.utils.utils5 import Scheduler
8
8
  from machineconfig.cluster.sessions_managers.wt_local import run_command_in_wt_tab
9
9
  from machineconfig.cluster.sessions_managers.wt_remote import WTRemoteLayoutGenerator
10
+ from machineconfig.cluster.sessions_managers.layout_types import TabConfig
10
11
 
11
12
  TMP_SERIALIZATION_DIR = Path.home().joinpath("tmp_results", "session_manager", "wt", "remote_manager")
12
13
 
@@ -21,7 +22,9 @@ class WTSessionManager:
21
22
  self.managers: list[WTRemoteLayoutGenerator] = []
22
23
  for machine, tab_config in machine2wt_tabs.items():
23
24
  an_m = WTRemoteLayoutGenerator(remote_name=machine, session_name_prefix=self.session_name_prefix)
24
- an_m.create_wt_layout(tab_config=tab_config)
25
+ # Convert legacy dict[str, tuple[str,str]] to List[TabConfig]
26
+ tabs: list[TabConfig] = [{"tabName": name, "startDir": cwd, "command": cmd} for name, (cwd, cmd) in tab_config.items()]
27
+ an_m.create_wt_layout(tabs=tabs)
25
28
  self.managers.append(an_m)
26
29
 
27
30
  def ssh_to_all_machines(self) -> str:
@@ -40,14 +43,14 @@ class WTSessionManager:
40
43
 
41
44
  def kill_all_sessions(self) -> None:
42
45
  for an_m in self.managers:
43
- WTRemoteLayoutGenerator.run_remote_command(remote_name=an_m.remote_name, command="powershell -Command \"Get-Process -Name 'WindowsTerminal' -ErrorAction SilentlyContinue | Stop-Process -Force\"")
46
+ an_m.remote_executor.run_command("powershell -Command \"Get-Process -Name 'WindowsTerminal' -ErrorAction SilentlyContinue | Stop-Process -Force\"")
44
47
 
45
48
  def run_monitoring_routine(self, wait_ms: int = 60000) -> None:
46
49
  def routine(scheduler: Scheduler):
47
50
  if scheduler.cycle % 2 == 0:
48
51
  statuses = []
49
52
  for _idx, an_m in enumerate(self.managers):
50
- a_status = an_m.check_all_commands_status()
53
+ a_status = an_m.process_monitor.check_all_commands_status(an_m.tabs)
51
54
  statuses.append(a_status)
52
55
  keys = []
53
56
  for item in statuses:
@@ -72,7 +75,7 @@ class WTSessionManager:
72
75
  else:
73
76
  statuses = []
74
77
  for _idx, an_m in enumerate(self.managers):
75
- a_status = an_m.check_wt_session_status()
78
+ a_status = an_m.session_manager.check_wt_session_status()
76
79
  statuses.append(a_status)
77
80
 
78
81
  # Print statuses
@@ -188,7 +191,7 @@ class WTSessionManager:
188
191
  remote_name = manager.remote_name
189
192
 
190
193
  # Start the Windows Terminal session on the remote machine
191
- start_result = manager.start_wt_session()
194
+ start_result = manager.session_manager.start_wt_session(manager.script_path)
192
195
 
193
196
  results[f"{remote_name}:{session_name}"] = start_result
194
197
 
@@ -212,10 +215,10 @@ class WTSessionManager:
212
215
 
213
216
  try:
214
217
  # Get Windows Terminal session status
215
- wt_status = manager.check_wt_session_status()
218
+ wt_status = manager.session_manager.check_wt_session_status()
216
219
 
217
220
  # Get commands status for this session
218
- commands_status = manager.check_all_commands_status()
221
+ commands_status = manager.process_monitor.check_all_commands_status(manager.tabs)
219
222
 
220
223
  # Calculate summary for this session
221
224
  running_count = sum(1 for status in commands_status.values() if status.get("running", False))
@@ -330,15 +333,15 @@ class WTSessionManager:
330
333
  remote_name = manager.remote_name
331
334
 
332
335
  # Get remote Windows info
333
- windows_info = manager.get_remote_windows_info()
336
+ windows_info = manager.remote_executor.get_remote_windows_info()
334
337
 
335
338
  # Get Windows Terminal processes
336
- wt_processes = manager.list_wt_processes()
339
+ wt_processes = manager.remote_executor.list_wt_processes()
337
340
 
338
341
  # Get Windows Terminal version
339
- wt_version = manager.get_wt_version()
342
+ wt_version = manager.session_manager.get_wt_version()
340
343
 
341
- overview[remote_name] = {"windows_info": windows_info, "wt_processes": wt_processes, "wt_version": wt_version, "session_name": manager.session_name, "tab_count": len(manager.tab_config)}
344
+ overview[remote_name] = {"windows_info": windows_info, "wt_processes": wt_processes, "wt_version": wt_version, "session_name": manager.session_name, "tab_count": len(manager.tabs)}
342
345
 
343
346
  except Exception as e:
344
347
  overview[manager.remote_name] = {"error": str(e), "session_name": manager.session_name}
@@ -7,10 +7,13 @@ Based on Windows Terminal documentation: https://learn.microsoft.com/en-us/windo
7
7
  import shlex
8
8
  import random
9
9
  import string
10
- from typing import Dict, List, Tuple
10
+ from typing import List, Tuple
11
11
  from pathlib import Path
12
12
  import logging
13
13
 
14
+ from machineconfig.cluster.sessions_managers.layout_types import TabConfig
15
+
16
+
14
17
  logger = logging.getLogger(__name__)
15
18
 
16
19
 
@@ -78,21 +81,21 @@ class WTLayoutGenerator:
78
81
  return " ".join(tab_parts)
79
82
 
80
83
  @staticmethod
81
- def validate_tab_config(tab_config: Dict[str, Tuple[str, str]]) -> None:
84
+ def validate_tab_config(tabs: List[TabConfig]) -> None:
82
85
  """Validate tab configuration format and content."""
83
- if not tab_config:
86
+ if not tabs:
84
87
  raise ValueError("Tab configuration cannot be empty")
85
- for tab_name, (cwd, command) in tab_config.items():
86
- if not tab_name.strip():
87
- raise ValueError(f"Invalid tab name: {tab_name}")
88
- if not command.strip():
89
- raise ValueError(f"Invalid command for tab '{tab_name}': {command}")
90
- if not cwd.strip():
91
- raise ValueError(f"Invalid cwd for tab '{tab_name}': {cwd}")
92
-
93
- def generate_wt_command(self, tab_config: Dict[str, Tuple[str, str]], window_name: str | None = None, maximized: bool = False, focus: bool = True) -> str:
88
+ for tab in tabs:
89
+ if not tab["tabName"].strip():
90
+ raise ValueError(f"Invalid tab name: {tab['tabName']}")
91
+ if not tab["command"].strip():
92
+ raise ValueError(f"Invalid command for tab '{tab['tabName']}': {tab['command']}")
93
+ if not tab["startDir"].strip():
94
+ raise ValueError(f"Invalid startDir for tab '{tab['tabName']}': {tab['startDir']}")
95
+
96
+ def generate_wt_command(self, tabs: List[TabConfig], window_name: str | None = None, maximized: bool = False, focus: bool = True) -> str:
94
97
  """Generate complete Windows Terminal command string."""
95
- self.validate_tab_config(tab_config)
98
+ self.validate_tab_config(tabs)
96
99
 
97
100
  # Start building the wt command
98
101
  wt_parts = ["wt"]
@@ -109,9 +112,9 @@ class WTLayoutGenerator:
109
112
 
110
113
  # Add tabs
111
114
  tab_commands = []
112
- for i, (tab_name, (cwd, command)) in enumerate(tab_config.items()):
115
+ for i, tab in enumerate(tabs):
113
116
  is_first = i == 0
114
- tab_cmd = self.create_tab_command(tab_name, cwd, command, is_first)
117
+ tab_cmd = self.create_tab_command(tab["tabName"], tab["startDir"], tab["command"], is_first)
115
118
  tab_commands.append(tab_cmd)
116
119
 
117
120
  # Join all parts with semicolons (Windows Terminal command separator)
@@ -127,46 +130,38 @@ class WTLayoutGenerator:
127
130
 
128
131
  return " ".join(wt_parts)
129
132
 
130
- def create_wt_script(self, tab_config: Dict[str, Tuple[str, str]], output_dir: Path, session_name: str, window_name: str | None = None) -> str:
131
- """Create a Windows Terminal script file and return its absolute path."""
132
- self.validate_tab_config(tab_config)
133
+ def create_wt_script(self, tabs: List[TabConfig], output_dir: Path, session_name: str, window_name: str | None = None) -> str:
134
+ """Create a Windows Terminal PowerShell script and return its absolute path."""
135
+ self.validate_tab_config(tabs)
133
136
 
134
137
  # Generate unique suffix for this script
135
138
  random_suffix = self.generate_random_suffix()
136
- wt_command = self.generate_wt_command(tab_config, window_name or session_name)
139
+ wt_command = self.generate_wt_command(tabs, window_name or session_name)
137
140
 
138
141
  try:
139
142
  # Create output directory if it doesn't exist
140
143
  output_dir.mkdir(parents=True, exist_ok=True)
141
144
 
142
- # Create both .bat and .ps1 versions for flexibility
143
- bat_file = output_dir / f"wt_layout_{session_name}_{random_suffix}.bat"
145
+ # Create PowerShell script
144
146
  ps1_file = output_dir / f"wt_layout_{session_name}_{random_suffix}.ps1"
145
147
 
146
- # Create batch file
147
- text = f"""@echo off
148
- REM Windows Terminal layout for {session_name}
149
- {wt_command}
150
- """
151
- bat_file.write_text(text, encoding="utf-8")
152
-
153
- # Create PowerShell file (better for complex commands)
148
+ # Create PowerShell script content
154
149
  text = f"""# Windows Terminal layout for {session_name}
155
150
  # Generated on {random_suffix}
156
151
  {wt_command}
157
152
  """
158
153
  ps1_file.write_text(text, encoding="utf-8")
159
154
 
160
- logger.info(f"Windows Terminal script files created: {bat_file.absolute()}")
161
- return str(bat_file.absolute())
155
+ logger.info(f"Windows Terminal PowerShell script created: {ps1_file.absolute()}")
156
+ return str(ps1_file.absolute())
162
157
 
163
158
  except OSError as e:
164
- logger.error(f"Failed to create script file: {e}")
159
+ logger.error(f"Failed to create PowerShell script: {e}")
165
160
  raise
166
161
 
167
- def generate_split_pane_command(self, tab_config: Dict[str, Tuple[str, str]], window_name: str | None = None) -> str:
162
+ def generate_split_pane_command(self, tabs: List[TabConfig], window_name: str | None = None) -> str:
168
163
  """Generate Windows Terminal command with split panes instead of separate tabs."""
169
- self.validate_tab_config(tab_config)
164
+ self.validate_tab_config(tabs)
170
165
 
171
166
  wt_parts = ["wt"]
172
167
 
@@ -174,8 +169,8 @@ REM Windows Terminal layout for {session_name}
174
169
  wt_parts.extend(["-w", WTLayoutGenerator.escape_for_wt(window_name)])
175
170
 
176
171
  # First pane (main tab)
177
- first_tab = list(tab_config.items())[0]
178
- tab_name, (cwd, command) = first_tab
172
+ first_tab = tabs[0]
173
+ tab_name, cwd, command = first_tab["tabName"], first_tab["startDir"], first_tab["command"]
179
174
 
180
175
  # Start with first tab
181
176
  wt_parts.extend(["-d", WTLayoutGenerator.escape_for_wt(cwd)])
@@ -183,7 +178,8 @@ REM Windows Terminal layout for {session_name}
183
178
  wt_parts.append(WTLayoutGenerator.escape_for_wt(command))
184
179
 
185
180
  # Add split panes for remaining tabs
186
- for tab_name, (cwd, command) in list(tab_config.items())[1:]:
181
+ for tab in tabs[1:]:
182
+ tab_name, cwd, command = tab["tabName"], tab["startDir"], tab["command"]
187
183
  wt_parts.append(";")
188
184
  wt_parts.append("split-pane")
189
185
  wt_parts.extend(["-d", WTLayoutGenerator.escape_for_wt(cwd)])
@@ -7,8 +7,9 @@ Adapted from zellij process monitor but focused on Windows processes.
7
7
  import json
8
8
  import logging
9
9
  import subprocess
10
- from typing import Dict, Tuple, Any, Optional
11
- from .remote_executor import WTRemoteExecutor
10
+ from typing import Dict, Any, Optional, List
11
+ from machineconfig.cluster.sessions_managers.layout_types import TabConfig
12
+ from machineconfig.cluster.sessions_managers.wt_utils.remote_executor import WTRemoteExecutor
12
13
 
13
14
  logger = logging.getLogger(__name__)
14
15
 
@@ -34,20 +35,22 @@ class WTProcessMonitor:
34
35
  raise ValueError("Remote executor is None but is_local is False")
35
36
  return self.remote_executor.run_command(command, timeout)
36
37
 
37
- def check_command_status(self, tab_name: str, tab_config: Dict[str, Tuple[str, str]], use_verification: bool = True) -> Dict[str, Any]:
38
+ def check_command_status(self, tab_name: str, tabs: List[TabConfig], use_verification: bool = True) -> Dict[str, Any]:
38
39
  """Check command status with optional process verification."""
39
- if tab_name not in tab_config:
40
+ the_tab = next((t for t in tabs if t["tabName"] == tab_name), None)
41
+ if the_tab is None:
40
42
  return {"status": "unknown", "error": f"Tab '{tab_name}' not found in tracked configuration", "running": False, "pid": None, "command": None, "location": self.location_name}
41
43
 
42
44
  # Use the verified method by default for more accurate results
43
45
  if use_verification:
44
- return self.get_verified_process_status(tab_name, tab_config)
46
+ return self.get_verified_process_status(tab_name, tabs)
45
47
 
46
- return self._basic_process_check(tab_name, tab_config)
48
+ return self._basic_process_check(tab_name, tabs)
47
49
 
48
- def _basic_process_check(self, tab_name: str, tab_config: Dict[str, Tuple[str, str]]) -> Dict[str, Any]:
50
+ def _basic_process_check(self, tab_name: str, tabs: List[TabConfig]) -> Dict[str, Any]:
49
51
  """Basic process checking without verification."""
50
- _, command = tab_config[tab_name]
52
+ the_tab = next((t for t in tabs if t["tabName"] == tab_name), None)
53
+ command = the_tab["command"] if the_tab is not None else ""
51
54
 
52
55
  try:
53
56
  check_script = self._create_process_check_script(command)
@@ -132,12 +135,13 @@ Get-Process | ForEach-Object {{
132
135
  }}
133
136
  """
134
137
 
135
- def force_fresh_process_check(self, tab_name: str, tab_config: Dict[str, Tuple[str, str]]) -> Dict[str, Any]:
138
+ def force_fresh_process_check(self, tab_name: str, tabs: List[TabConfig]) -> Dict[str, Any]:
136
139
  """Force a fresh process check with additional validation."""
137
- if tab_name not in tab_config:
140
+ the_tab = next((t for t in tabs if t["tabName"] == tab_name), None)
141
+ if the_tab is None:
138
142
  return {"status": "unknown", "error": f"Tab '{tab_name}' not found in tracked configuration", "running": False, "command": None, "location": self.location_name}
139
143
 
140
- _, command = tab_config[tab_name]
144
+ command = the_tab["command"]
141
145
 
142
146
  try:
143
147
  # Get timestamp for freshness validation
@@ -262,9 +266,9 @@ Write-Output $result
262
266
  except Exception:
263
267
  return False
264
268
 
265
- def get_verified_process_status(self, tab_name: str, tab_config: Dict[str, Tuple[str, str]]) -> Dict[str, Any]:
269
+ def get_verified_process_status(self, tab_name: str, tabs: List[TabConfig]) -> Dict[str, Any]:
266
270
  """Get process status with additional verification that processes are actually alive."""
267
- status = self.force_fresh_process_check(tab_name, tab_config)
271
+ status = self.force_fresh_process_check(tab_name, tabs)
268
272
 
269
273
  if status.get("running") and status.get("processes"):
270
274
  verified_processes = []
@@ -284,15 +288,16 @@ Write-Output $result
284
288
 
285
289
  return status
286
290
 
287
- def check_all_commands_status(self, tab_config: Dict[str, Tuple[str, str]]) -> Dict[str, Dict[str, Any]]:
291
+ def check_all_commands_status(self, tabs: List[TabConfig]) -> Dict[str, Dict[str, Any]]:
288
292
  """Check status of all commands in the tab configuration."""
289
- if not tab_config:
293
+ if not tabs:
290
294
  logger.warning("No tab configuration provided.")
291
295
  return {}
292
296
 
293
297
  status_report = {}
294
- for tab_name in tab_config:
295
- status_report[tab_name] = self.check_command_status(tab_name, tab_config)
298
+ for the_tab in tabs:
299
+ tab_name = the_tab["tabName"]
300
+ status_report[tab_name] = self.check_command_status(tab_name, tabs)
296
301
  return status_report
297
302
 
298
303
  def get_windows_terminal_windows(self) -> Dict[str, Any]:
@@ -5,13 +5,62 @@ Windows Terminal session management utilities for local and remote operations.
5
5
 
6
6
  import logging
7
7
  import subprocess
8
- from typing import Dict, Any, Optional
8
+ from typing import Optional, TypedDict, NotRequired
9
9
  from pathlib import Path
10
- from .remote_executor import WTRemoteExecutor
10
+ from machineconfig.cluster.sessions_managers.wt_utils.remote_executor import WTRemoteExecutor
11
11
 
12
12
  logger = logging.getLogger(__name__)
13
13
 
14
14
 
15
+ class WTProcessInfo(TypedDict, total=False):
16
+ Id: int
17
+ ProcessName: str
18
+ StartTime: str
19
+ MainWindowTitle: str
20
+
21
+
22
+ class CheckWTSessionStatusResult(TypedDict):
23
+ wt_running: bool
24
+ session_exists: bool
25
+ session_name: str
26
+ location: str
27
+ all_windows: NotRequired[list[WTProcessInfo]]
28
+ session_windows: NotRequired[list[WTProcessInfo]]
29
+ error: NotRequired[str]
30
+
31
+
32
+ class StartWTSessionResult(TypedDict):
33
+ success: bool
34
+ session_name: str
35
+ location: str
36
+ message: NotRequired[str]
37
+ error: NotRequired[str]
38
+
39
+
40
+ class KillWTSessionResult(TypedDict):
41
+ success: bool
42
+ session_name: str
43
+ location: str
44
+ message: NotRequired[str]
45
+ error: NotRequired[str]
46
+
47
+
48
+ class CreateNewTabResult(TypedDict):
49
+ success: bool
50
+ tab_name: str
51
+ command: str
52
+ location: str
53
+ message: NotRequired[str]
54
+ error: NotRequired[str]
55
+
56
+
57
+ class GetWTVersionResult(TypedDict):
58
+ success: bool
59
+ location: str
60
+ version: NotRequired[str]
61
+ error: NotRequired[str]
62
+
63
+
15
64
  class WTSessionManager:
16
65
  """Handles Windows Terminal session operations on local and remote machines."""
17
66
 
@@ -59,7 +108,7 @@ class WTSessionManager:
59
108
  logger.info(f"Windows Terminal script file copied to remote: {self.remote_executor.remote_name}:{remote_script_file}")
60
109
  return remote_script_file
61
110
 
62
- def check_wt_session_status(self) -> Dict[str, Any]:
111
+ def check_wt_session_status(self) -> CheckWTSessionStatusResult:
63
112
  """Check if Windows Terminal windows exist and are running."""
64
113
  try:
65
114
  # Check for Windows Terminal processes
@@ -94,12 +143,12 @@ ConvertTo-Json -Depth 2
94
143
  else:
95
144
  return {"wt_running": False, "session_exists": False, "session_name": self.session_name, "all_windows": [], "location": self.location_name}
96
145
  else:
97
- return {"wt_running": False, "error": result.stderr, "session_name": self.session_name, "location": self.location_name}
146
+ return {"wt_running": False, "session_exists": False, "error": result.stderr, "session_name": self.session_name, "location": self.location_name}
98
147
 
99
148
  except Exception as e:
100
- return {"wt_running": False, "error": str(e), "session_name": self.session_name, "location": self.location_name}
149
+ return {"wt_running": False, "session_exists": False, "error": str(e), "session_name": self.session_name, "location": self.location_name}
101
150
 
102
- def start_wt_session(self, script_file_path: Optional[str] = None, wt_command: Optional[str] = None) -> Dict[str, Any]:
151
+ def start_wt_session(self, script_file_path: Optional[str] = None, wt_command: Optional[str] = None) -> StartWTSessionResult:
103
152
  """Start a Windows Terminal session with the generated layout."""
104
153
  try:
105
154
  if script_file_path:
@@ -166,7 +215,7 @@ ConvertTo-Json -Depth 2
166
215
  logger.error(f"Failed to attach to Windows Terminal session: {e}")
167
216
  raise
168
217
 
169
- def kill_wt_session(self, force: bool = True) -> Dict[str, Any]:
218
+ def kill_wt_session(self, force: bool = True) -> KillWTSessionResult:
170
219
  """Kill Windows Terminal processes related to this session."""
171
220
  try:
172
221
  if force:
@@ -185,7 +234,7 @@ ConvertTo-Json -Depth 2
185
234
  logger.error(f"Failed to kill Windows Terminal session: {e}")
186
235
  return {"success": False, "error": str(e), "session_name": self.session_name, "location": self.location_name}
187
236
 
188
- def create_new_tab(self, tab_name: str, cwd: str, command: str, window_name: Optional[str] = None) -> Dict[str, Any]:
237
+ def create_new_tab(self, tab_name: str, cwd: str, command: str, window_name: Optional[str] = None) -> CreateNewTabResult:
189
238
  """Create a new tab in the Windows Terminal session."""
190
239
  try:
191
240
  # Build the new-tab command
@@ -208,9 +257,9 @@ ConvertTo-Json -Depth 2
208
257
 
209
258
  except Exception as e:
210
259
  logger.error(f"Failed to create new tab '{tab_name}': {e}")
211
- return {"success": False, "error": str(e), "tab_name": tab_name, "location": self.location_name}
260
+ return {"success": False, "error": str(e), "tab_name": tab_name, "command": command, "location": self.location_name}
212
261
 
213
- def get_wt_version(self) -> Dict[str, Any]:
262
+ def get_wt_version(self) -> GetWTVersionResult:
214
263
  """Get Windows Terminal version information."""
215
264
  try:
216
265
  version_cmd = "wt --version"