machineconfig 2.2__py3-none-any.whl → 2.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of machineconfig might be problematic. Click here for more details.
- machineconfig/__init__.py +30 -0
- machineconfig/cluster/sessions_managers/enhanced_command_runner.py +0 -2
- machineconfig/cluster/sessions_managers/layout_types.py +29 -0
- machineconfig/cluster/sessions_managers/wt_local.py +68 -62
- machineconfig/cluster/sessions_managers/wt_local_manager.py +51 -22
- machineconfig/cluster/sessions_managers/wt_remote.py +30 -108
- machineconfig/cluster/sessions_managers/wt_remote_manager.py +14 -11
- machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +33 -37
- machineconfig/cluster/sessions_managers/wt_utils/process_monitor.py +22 -17
- machineconfig/cluster/sessions_managers/wt_utils/session_manager.py +59 -10
- machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +16 -14
- machineconfig/cluster/sessions_managers/zellij_local.py +75 -57
- machineconfig/cluster/sessions_managers/zellij_local_manager.py +51 -23
- machineconfig/cluster/sessions_managers/zellij_remote.py +47 -27
- machineconfig/cluster/sessions_managers/zellij_remote_manager.py +13 -12
- machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +14 -10
- machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +31 -15
- machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +47 -21
- machineconfig/cluster/sessions_managers/zellij_utils/session_manager.py +1 -1
- machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +8 -7
- machineconfig/cluster/templates/utils.py +1 -1
- machineconfig/profile/create.py +4 -0
- machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/linux/checkout_versions +1 -7
- machineconfig/scripts/linux/choose_wezterm_theme +1 -7
- machineconfig/scripts/linux/cloud_copy +1 -8
- machineconfig/scripts/linux/cloud_manager +1 -7
- machineconfig/scripts/linux/cloud_mount +1 -23
- machineconfig/scripts/linux/cloud_repo_sync +1 -21
- machineconfig/scripts/linux/cloud_sync +1 -23
- machineconfig/scripts/linux/croshell +1 -23
- machineconfig/scripts/linux/devops +0 -21
- machineconfig/scripts/linux/fire +1 -27
- machineconfig/scripts/linux/fire_agents +1 -26
- machineconfig/scripts/linux/gh_models +1 -10
- machineconfig/scripts/linux/kill_process +1 -9
- machineconfig/scripts/linux/mcinit +1 -26
- machineconfig/scripts/linux/mount_nfs +1 -13
- machineconfig/scripts/linux/repos +1 -23
- machineconfig/scripts/linux/scheduler +1 -7
- machineconfig/scripts/linux/start_slidev +1 -22
- machineconfig/scripts/linux/start_terminals +1 -9
- machineconfig/scripts/linux/url2md +1 -9
- machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/croshell.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc +0 -0
- machineconfig/scripts/python/cloud_mount.py +4 -2
- machineconfig/scripts/python/cloud_repo_sync.py +5 -2
- machineconfig/scripts/python/cloud_sync.py +4 -2
- machineconfig/scripts/python/croshell.py +5 -3
- machineconfig/scripts/python/devops.py +3 -2
- machineconfig/scripts/python/devops_devapps_install.py +1 -0
- machineconfig/scripts/python/fire_agents.py +6 -6
- machineconfig/scripts/python/fire_jobs.py +26 -72
- machineconfig/scripts/python/fire_jobs_args_helper.py +84 -0
- machineconfig/scripts/python/fire_jobs_layout_helper.py +66 -0
- machineconfig/scripts/python/helpers/helpers4.py +0 -1
- machineconfig/scripts/python/mount_nfs.py +12 -8
- machineconfig/scripts/python/mount_nw_drive.py +6 -6
- machineconfig/scripts/python/mount_ssh.py +4 -2
- machineconfig/scripts/python/start_slidev.py +4 -2
- machineconfig/scripts/python/start_terminals.py +4 -2
- machineconfig/scripts/python/wifi_conn.py +0 -1
- machineconfig/settings/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/settings/shells/ipy/profiles/default/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/settings/shells/ipy/profiles/default/startup/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/utils/code.py +5 -6
- machineconfig/utils/installer_utils/installer_abc.py +0 -1
- machineconfig/utils/options.py +7 -7
- machineconfig/utils/path.py +12 -12
- machineconfig/utils/path_reduced.py +6 -1
- machineconfig/utils/source_of_truth.py +2 -2
- machineconfig/utils/ssh.py +11 -1
- machineconfig/utils/upgrade_packages.py +12 -12
- {machineconfig-2.2.dist-info → machineconfig-2.4.dist-info}/METADATA +1 -1
- {machineconfig-2.2.dist-info → machineconfig-2.4.dist-info}/RECORD +90 -93
- machineconfig-2.4.dist-info/entry_points.txt +2 -0
- machineconfig/cluster/sessions_managers/archive/create_zellij_template.py +0 -60
- machineconfig/cluster/sessions_managers/archive/session_managers.py +0 -183
- machineconfig/cluster/sessions_managers/demo_rich_zellij.py +0 -0
- machineconfig/jobs/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/linux/archive/tmate_conn +0 -12
- machineconfig/scripts/linux/archive/tmate_start +0 -12
- machineconfig/scripts/linux/archive/transfer_wsl_win +0 -5
- machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_agents.cpython-313.pyc +0 -0
- /machineconfig/cluster/{cloud_manager.py → remote/cloud_manager.py} +0 -0
- /machineconfig/cluster/{data_transfer.py → remote/data_transfer.py} +0 -0
- /machineconfig/cluster/{distribute.py → remote/distribute.py} +0 -0
- /machineconfig/cluster/{file_manager.py → remote/file_manager.py} +0 -0
- /machineconfig/cluster/{job_params.py → remote/job_params.py} +0 -0
- /machineconfig/cluster/{loader_runner.py → remote/loader_runner.py} +0 -0
- /machineconfig/cluster/{remote_machine.py → remote/remote_machine.py} +0 -0
- /machineconfig/cluster/{script_execution.py → remote/script_execution.py} +0 -0
- /machineconfig/cluster/{script_notify_upon_completion.py → remote/script_notify_upon_completion.py} +0 -0
- /machineconfig/{cluster/sessions_managers/archive/__init__.py → scripts/python/fire_jobs_streamlit_helper.py} +0 -0
- {machineconfig-2.2.dist-info → machineconfig-2.4.dist-info}/WHEEL +0 -0
- {machineconfig-2.2.dist-info → machineconfig-2.4.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
from typing import Dict,
|
|
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.
|
|
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
|
-
|
|
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,
|
|
38
|
-
logger.info(f"Creating Windows Terminal layout with {len(
|
|
39
|
-
self.
|
|
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(
|
|
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, "
|
|
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
|
-
|
|
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":
|
|
215
|
-
"🤖Bot2":
|
|
216
|
-
"📊Monitor":
|
|
217
|
-
"📝Logs":
|
|
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: {
|
|
173
|
+
print(f"📊 Loaded tabs: {[tab['tabName'] for tab in loaded_generator.tabs]}")
|
|
252
174
|
|
|
253
175
|
# Show command preview
|
|
254
|
-
preview = generator.
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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(
|
|
84
|
+
def validate_tab_config(tabs: List[TabConfig]) -> None:
|
|
82
85
|
"""Validate tab configuration format and content."""
|
|
83
|
-
if not
|
|
86
|
+
if not tabs:
|
|
84
87
|
raise ValueError("Tab configuration cannot be empty")
|
|
85
|
-
for
|
|
86
|
-
if not
|
|
87
|
-
raise ValueError(f"Invalid tab name: {
|
|
88
|
-
if not command.strip():
|
|
89
|
-
raise ValueError(f"Invalid command for tab '{
|
|
90
|
-
if not
|
|
91
|
-
raise ValueError(f"Invalid
|
|
92
|
-
|
|
93
|
-
def generate_wt_command(self,
|
|
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(
|
|
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,
|
|
115
|
+
for i, tab in enumerate(tabs):
|
|
113
116
|
is_first = i == 0
|
|
114
|
-
tab_cmd = self.create_tab_command(
|
|
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,
|
|
131
|
-
"""Create a Windows Terminal script
|
|
132
|
-
self.validate_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(
|
|
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
|
|
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
|
|
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
|
|
161
|
-
return str(
|
|
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
|
|
159
|
+
logger.error(f"Failed to create PowerShell script: {e}")
|
|
165
160
|
raise
|
|
166
161
|
|
|
167
|
-
def generate_split_pane_command(self,
|
|
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(
|
|
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 =
|
|
178
|
-
tab_name,
|
|
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
|
|
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,
|
|
11
|
-
from .
|
|
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,
|
|
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
|
-
|
|
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,
|
|
46
|
+
return self.get_verified_process_status(tab_name, tabs)
|
|
45
47
|
|
|
46
|
-
return self._basic_process_check(tab_name,
|
|
48
|
+
return self._basic_process_check(tab_name, tabs)
|
|
47
49
|
|
|
48
|
-
def _basic_process_check(self, tab_name: str,
|
|
50
|
+
def _basic_process_check(self, tab_name: str, tabs: List[TabConfig]) -> Dict[str, Any]:
|
|
49
51
|
"""Basic process checking without verification."""
|
|
50
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
293
|
+
if not tabs:
|
|
290
294
|
logger.warning("No tab configuration provided.")
|
|
291
295
|
return {}
|
|
292
296
|
|
|
293
297
|
status_report = {}
|
|
294
|
-
for
|
|
295
|
-
|
|
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
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
262
|
+
def get_wt_version(self) -> GetWTVersionResult:
|
|
214
263
|
"""Get Windows Terminal version information."""
|
|
215
264
|
try:
|
|
216
265
|
version_cmd = "wt --version"
|