machineconfig 1.96__py3-none-any.whl → 2.0__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/cluster/cloud_manager.py +22 -26
- machineconfig/cluster/data_transfer.py +2 -2
- machineconfig/cluster/distribute.py +0 -2
- machineconfig/cluster/file_manager.py +4 -4
- machineconfig/cluster/job_params.py +1 -1
- machineconfig/cluster/loader_runner.py +8 -8
- machineconfig/cluster/remote_machine.py +4 -4
- machineconfig/cluster/script_execution.py +2 -2
- machineconfig/cluster/sessions_managers/archive/create_zellij_template.py +1 -1
- machineconfig/cluster/sessions_managers/enhanced_command_runner.py +23 -23
- machineconfig/cluster/sessions_managers/wt_local.py +78 -76
- machineconfig/cluster/sessions_managers/wt_local_manager.py +91 -91
- machineconfig/cluster/sessions_managers/wt_remote.py +39 -39
- machineconfig/cluster/sessions_managers/wt_remote_manager.py +94 -91
- machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +56 -54
- machineconfig/cluster/sessions_managers/wt_utils/process_monitor.py +49 -49
- machineconfig/cluster/sessions_managers/wt_utils/remote_executor.py +18 -18
- machineconfig/cluster/sessions_managers/wt_utils/session_manager.py +42 -42
- machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +36 -36
- machineconfig/cluster/sessions_managers/zellij_local.py +43 -46
- machineconfig/cluster/sessions_managers/zellij_local_manager.py +139 -120
- machineconfig/cluster/sessions_managers/zellij_remote.py +35 -35
- machineconfig/cluster/sessions_managers/zellij_remote_manager.py +33 -33
- machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +15 -15
- machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +25 -26
- machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +49 -49
- machineconfig/cluster/sessions_managers/zellij_utils/remote_executor.py +5 -5
- machineconfig/cluster/sessions_managers/zellij_utils/session_manager.py +15 -15
- machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +11 -11
- machineconfig/cluster/templates/utils.py +3 -3
- machineconfig/jobs/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python/__pycache__/python_ve_symlink.cpython-311.pyc +0 -0
- machineconfig/jobs/python/check_installations.py +8 -9
- machineconfig/jobs/python/python_cargo_build_share.py +2 -2
- machineconfig/jobs/python/vscode/link_ve.py +7 -7
- machineconfig/jobs/python/vscode/select_interpreter.py +7 -7
- machineconfig/jobs/python/vscode/sync_code.py +5 -5
- machineconfig/jobs/python_custom_installers/archive/ngrok.py +2 -2
- machineconfig/jobs/python_custom_installers/dev/aider.py +3 -3
- machineconfig/jobs/python_custom_installers/dev/alacritty.py +3 -3
- machineconfig/jobs/python_custom_installers/dev/brave.py +3 -3
- machineconfig/jobs/python_custom_installers/dev/bypass_paywall.py +5 -5
- machineconfig/jobs/python_custom_installers/dev/code.py +3 -3
- machineconfig/jobs/python_custom_installers/dev/cursor.py +9 -9
- machineconfig/jobs/python_custom_installers/dev/docker_desktop.py +4 -4
- machineconfig/jobs/python_custom_installers/dev/espanso.py +4 -4
- machineconfig/jobs/python_custom_installers/dev/goes.py +4 -4
- machineconfig/jobs/python_custom_installers/dev/lvim.py +4 -4
- machineconfig/jobs/python_custom_installers/dev/nerdfont.py +3 -3
- machineconfig/jobs/python_custom_installers/dev/redis.py +3 -3
- machineconfig/jobs/python_custom_installers/dev/wezterm.py +3 -3
- machineconfig/jobs/python_custom_installers/dev/winget.py +27 -27
- machineconfig/jobs/python_custom_installers/docker.py +3 -3
- machineconfig/jobs/python_custom_installers/gh.py +7 -7
- machineconfig/jobs/python_custom_installers/hx.py +1 -1
- machineconfig/jobs/python_custom_installers/warp-cli.py +3 -3
- machineconfig/jobs/python_generic_installers/config.json +412 -389
- machineconfig/jobs/python_windows_installers/dev/config.json +1 -1
- machineconfig/logger.py +50 -0
- machineconfig/profile/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/profile/__pycache__/create.cpython-311.pyc +0 -0
- machineconfig/profile/__pycache__/shell.cpython-311.pyc +0 -0
- machineconfig/profile/create.py +23 -16
- machineconfig/profile/create_hardlinks.py +8 -8
- machineconfig/profile/shell.py +41 -37
- machineconfig/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/linux/devops +2 -2
- machineconfig/scripts/linux/fire +1 -0
- machineconfig/scripts/linux/fire_agents +0 -1
- machineconfig/scripts/linux/mcinit +27 -0
- machineconfig/scripts/python/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/croshell.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_agents.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_jobs.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/repos.cpython-311.pyc +0 -0
- machineconfig/scripts/python/ai/__pycache__/init.cpython-311.pyc +0 -0
- machineconfig/scripts/python/ai/__pycache__/mcinit.cpython-311.pyc +0 -0
- machineconfig/scripts/python/ai/chatmodes/Thinking-Beast-Mode.chatmode.md +337 -0
- machineconfig/scripts/python/ai/chatmodes/Ultimate-Transparent-Thinking-Beast-Mode.chatmode.md +644 -0
- machineconfig/scripts/python/ai/chatmodes/deepResearch.chatmode.md +81 -0
- machineconfig/scripts/python/ai/configs/.gemini/settings.json +81 -0
- machineconfig/scripts/python/ai/instructions/python/dev.instructions.md +45 -0
- machineconfig/scripts/python/ai/mcinit.py +103 -0
- machineconfig/scripts/python/ai/prompts/allLintersAndTypeCheckers.prompt.md +5 -0
- machineconfig/scripts/python/ai/prompts/research-report-skeleton.prompt.md +38 -0
- machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +47 -0
- machineconfig/scripts/python/archive/tmate_conn.py +5 -5
- machineconfig/scripts/python/archive/tmate_start.py +3 -3
- machineconfig/scripts/python/choose_wezterm_theme.py +2 -2
- machineconfig/scripts/python/cloud_copy.py +19 -18
- machineconfig/scripts/python/cloud_mount.py +9 -7
- machineconfig/scripts/python/cloud_repo_sync.py +11 -11
- machineconfig/scripts/python/cloud_sync.py +1 -1
- machineconfig/scripts/python/croshell.py +14 -14
- machineconfig/scripts/python/devops.py +6 -6
- machineconfig/scripts/python/devops_add_identity.py +8 -6
- machineconfig/scripts/python/devops_add_ssh_key.py +18 -18
- machineconfig/scripts/python/devops_backup_retrieve.py +13 -13
- machineconfig/scripts/python/devops_devapps_install.py +3 -3
- machineconfig/scripts/python/devops_update_repos.py +1 -1
- machineconfig/scripts/python/dotfile.py +2 -2
- machineconfig/scripts/python/fire_agents.py +183 -41
- machineconfig/scripts/python/fire_jobs.py +17 -11
- machineconfig/scripts/python/ftpx.py +2 -2
- machineconfig/scripts/python/gh_models.py +94 -94
- machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/cloud_helpers.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/helpers2.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/cloud_helpers.py +3 -3
- machineconfig/scripts/python/helpers/helpers2.py +1 -1
- machineconfig/scripts/python/helpers/helpers4.py +8 -6
- machineconfig/scripts/python/helpers/helpers5.py +7 -7
- machineconfig/scripts/python/helpers/repo_sync_helpers.py +1 -1
- machineconfig/scripts/python/mount_nfs.py +3 -2
- machineconfig/scripts/python/mount_nw_drive.py +4 -4
- machineconfig/scripts/python/mount_ssh.py +3 -2
- machineconfig/scripts/python/repos.py +8 -8
- machineconfig/scripts/python/scheduler.py +1 -1
- machineconfig/scripts/python/start_slidev.py +8 -7
- machineconfig/scripts/python/start_terminals.py +1 -1
- machineconfig/scripts/python/viewer.py +40 -40
- machineconfig/scripts/python/wifi_conn.py +65 -66
- machineconfig/scripts/python/wsl_windows_transfer.py +1 -1
- machineconfig/scripts/windows/mcinit.ps1 +4 -0
- machineconfig/settings/linters/.ruff.toml +2 -2
- machineconfig/settings/shells/ipy/profiles/default/startup/playext.py +71 -71
- machineconfig/settings/shells/wt/settings.json +8 -8
- machineconfig/setup_linux/web_shortcuts/tmp.sh +2 -0
- machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +10 -7
- machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +9 -7
- machineconfig/utils/ai/browser_user_wrapper.py +5 -5
- machineconfig/utils/ai/generate_file_checklist.py +11 -12
- machineconfig/utils/ai/url2md.py +1 -1
- machineconfig/utils/cloud/onedrive/setup_oauth.py +4 -4
- machineconfig/utils/cloud/onedrive/transaction.py +129 -129
- machineconfig/utils/code.py +13 -6
- machineconfig/utils/installer.py +51 -53
- machineconfig/utils/installer_utils/installer_abc.py +21 -10
- machineconfig/utils/installer_utils/installer_class.py +42 -16
- machineconfig/utils/io_save.py +3 -15
- machineconfig/utils/options.py +10 -3
- machineconfig/utils/path.py +5 -0
- machineconfig/utils/path_reduced.py +201 -149
- machineconfig/utils/procs.py +23 -23
- machineconfig/utils/scheduling.py +11 -12
- machineconfig/utils/ssh.py +270 -0
- machineconfig/utils/terminal.py +180 -0
- machineconfig/utils/utils.py +1 -2
- machineconfig/utils/utils2.py +43 -0
- machineconfig/utils/utils5.py +163 -34
- machineconfig/utils/ve.py +2 -2
- {machineconfig-1.96.dist-info → machineconfig-2.0.dist-info}/METADATA +13 -8
- {machineconfig-1.96.dist-info → machineconfig-2.0.dist-info}/RECORD +163 -144
- machineconfig/cluster/self_ssh.py +0 -57
- {machineconfig-1.96.dist-info → machineconfig-2.0.dist-info}/WHEEL +0 -0
- {machineconfig-1.96.dist-info → machineconfig-2.0.dist-info}/top_level.txt +0 -0
|
@@ -15,25 +15,25 @@ logger = logging.getLogger(__name__)
|
|
|
15
15
|
|
|
16
16
|
class WTLayoutGenerator:
|
|
17
17
|
"""Handles generation of Windows Terminal command strings for multi-tab layouts."""
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
@staticmethod
|
|
20
20
|
def generate_random_suffix(length: int = 8) -> str:
|
|
21
21
|
"""Generate a random string suffix for unique window names."""
|
|
22
22
|
return ''.join(random.choices(string.ascii_lowercase + string.digits, k=length))
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
@staticmethod
|
|
25
25
|
def parse_command(command: str) -> Tuple[str, List[str]]:
|
|
26
26
|
"""Parse a command string into command and arguments."""
|
|
27
27
|
try:
|
|
28
28
|
parts = shlex.split(command)
|
|
29
|
-
if not parts:
|
|
29
|
+
if not parts:
|
|
30
30
|
raise ValueError("Empty command provided")
|
|
31
31
|
return parts[0], parts[1:] if len(parts) > 1 else []
|
|
32
32
|
except ValueError as e:
|
|
33
33
|
logger.error(f"Error parsing command '{command}': {e}")
|
|
34
34
|
parts = command.split()
|
|
35
35
|
return parts[0] if parts else "", parts[1:] if len(parts) > 1 else []
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
@staticmethod
|
|
38
38
|
def escape_for_wt(text: str) -> str:
|
|
39
39
|
"""Escape text for use in Windows Terminal commands."""
|
|
@@ -43,79 +43,79 @@ class WTLayoutGenerator:
|
|
|
43
43
|
if ' ' in text or ';' in text or '&' in text or '|' in text:
|
|
44
44
|
return f'"{text}"'
|
|
45
45
|
return text
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
@staticmethod
|
|
48
48
|
def create_tab_command(tab_name: str, cwd: str, command: str, is_first_tab: bool = False) -> str:
|
|
49
49
|
"""Create a Windows Terminal tab command string."""
|
|
50
50
|
cmd, args = WTLayoutGenerator.parse_command(command)
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
# Convert paths to Windows format if needed
|
|
53
53
|
if cwd.startswith('~/'):
|
|
54
54
|
cwd = cwd.replace('~/', f"{Path.home()}/")
|
|
55
55
|
elif cwd == '~':
|
|
56
56
|
cwd = str(Path.home())
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
# Build the wt command parts
|
|
59
59
|
tab_parts = []
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
if not is_first_tab:
|
|
62
62
|
tab_parts.append("new-tab")
|
|
63
|
-
|
|
63
|
+
|
|
64
64
|
# Add profile if specified (could be extended to support different shells)
|
|
65
65
|
# tab_parts.extend(["-p", "\"Command Prompt\""]) # or "PowerShell", "WSL", etc.
|
|
66
|
-
|
|
66
|
+
|
|
67
67
|
# Add starting directory
|
|
68
68
|
tab_parts.extend(["-d", WTLayoutGenerator.escape_for_wt(cwd)])
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
# Add tab title
|
|
71
71
|
tab_parts.extend(["--title", WTLayoutGenerator.escape_for_wt(tab_name)])
|
|
72
|
-
|
|
72
|
+
|
|
73
73
|
# Add the command to execute
|
|
74
74
|
full_command = command if not args else f"{cmd} {' '.join(args)}"
|
|
75
75
|
tab_parts.append(WTLayoutGenerator.escape_for_wt(full_command))
|
|
76
|
-
|
|
76
|
+
|
|
77
77
|
return " ".join(tab_parts)
|
|
78
|
-
|
|
78
|
+
|
|
79
79
|
@staticmethod
|
|
80
80
|
def validate_tab_config(tab_config: Dict[str, Tuple[str, str]]) -> None:
|
|
81
81
|
"""Validate tab configuration format and content."""
|
|
82
|
-
if not tab_config:
|
|
82
|
+
if not tab_config:
|
|
83
83
|
raise ValueError("Tab configuration cannot be empty")
|
|
84
84
|
for tab_name, (cwd, command) in tab_config.items():
|
|
85
|
-
if not tab_name.strip():
|
|
85
|
+
if not tab_name.strip():
|
|
86
86
|
raise ValueError(f"Invalid tab name: {tab_name}")
|
|
87
|
-
if not command.strip():
|
|
87
|
+
if not command.strip():
|
|
88
88
|
raise ValueError(f"Invalid command for tab '{tab_name}': {command}")
|
|
89
|
-
if not cwd.strip():
|
|
89
|
+
if not cwd.strip():
|
|
90
90
|
raise ValueError(f"Invalid cwd for tab '{tab_name}': {cwd}")
|
|
91
|
-
|
|
92
|
-
def generate_wt_command(self, tab_config: Dict[str, Tuple[str, str]],
|
|
93
|
-
window_name: str | None = None,
|
|
91
|
+
|
|
92
|
+
def generate_wt_command(self, tab_config: Dict[str, Tuple[str, str]],
|
|
93
|
+
window_name: str | None = None,
|
|
94
94
|
maximized: bool = False,
|
|
95
95
|
focus: bool = True) -> str:
|
|
96
96
|
"""Generate complete Windows Terminal command string."""
|
|
97
97
|
self.validate_tab_config(tab_config)
|
|
98
|
-
|
|
98
|
+
|
|
99
99
|
# Start building the wt command
|
|
100
100
|
wt_parts = ["wt"]
|
|
101
|
-
|
|
101
|
+
|
|
102
102
|
# Add window options
|
|
103
103
|
if maximized:
|
|
104
104
|
wt_parts.append("--maximized")
|
|
105
|
-
|
|
105
|
+
|
|
106
106
|
if focus:
|
|
107
107
|
wt_parts.append("--focus")
|
|
108
|
-
|
|
108
|
+
|
|
109
109
|
if window_name:
|
|
110
110
|
wt_parts.extend(["-w", WTLayoutGenerator.escape_for_wt(window_name)])
|
|
111
|
-
|
|
111
|
+
|
|
112
112
|
# Add tabs
|
|
113
113
|
tab_commands = []
|
|
114
114
|
for i, (tab_name, (cwd, command)) in enumerate(tab_config.items()):
|
|
115
115
|
is_first = i == 0
|
|
116
116
|
tab_cmd = self.create_tab_command(tab_name, cwd, command, is_first)
|
|
117
117
|
tab_commands.append(tab_cmd)
|
|
118
|
-
|
|
118
|
+
|
|
119
119
|
# Join all parts with semicolons (Windows Terminal command separator)
|
|
120
120
|
if tab_commands:
|
|
121
121
|
if len(tab_commands) == 1:
|
|
@@ -126,65 +126,67 @@ class WTLayoutGenerator:
|
|
|
126
126
|
wt_parts.append(tab_commands[0]) # First tab
|
|
127
127
|
for tab_cmd in tab_commands[1:]:
|
|
128
128
|
wt_parts.extend([";", tab_cmd])
|
|
129
|
-
|
|
129
|
+
|
|
130
130
|
return " ".join(wt_parts)
|
|
131
|
-
|
|
132
|
-
def create_wt_script(self, tab_config: Dict[str, Tuple[str, str]],
|
|
131
|
+
|
|
132
|
+
def create_wt_script(self, tab_config: Dict[str, Tuple[str, str]],
|
|
133
133
|
output_dir: Path, session_name: str,
|
|
134
134
|
window_name: str | None = None) -> str:
|
|
135
135
|
"""Create a Windows Terminal script file and return its absolute path."""
|
|
136
136
|
self.validate_tab_config(tab_config)
|
|
137
|
-
|
|
137
|
+
|
|
138
138
|
# Generate unique suffix for this script
|
|
139
139
|
random_suffix = self.generate_random_suffix()
|
|
140
140
|
wt_command = self.generate_wt_command(tab_config, window_name or session_name)
|
|
141
|
-
|
|
141
|
+
|
|
142
142
|
try:
|
|
143
143
|
# Create output directory if it doesn't exist
|
|
144
144
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
145
|
-
|
|
145
|
+
|
|
146
146
|
# Create both .bat and .ps1 versions for flexibility
|
|
147
147
|
bat_file = output_dir / f"wt_layout_{session_name}_{random_suffix}.bat"
|
|
148
148
|
ps1_file = output_dir / f"wt_layout_{session_name}_{random_suffix}.ps1"
|
|
149
|
-
|
|
149
|
+
|
|
150
150
|
# Create batch file
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
151
|
+
text = f"""@echo off
|
|
152
|
+
REM Windows Terminal layout for {session_name}
|
|
153
|
+
{wt_command}
|
|
154
|
+
"""
|
|
155
|
+
bat_file.write_text(text, encoding="utf-8")
|
|
156
|
+
|
|
156
157
|
# Create PowerShell file (better for complex commands)
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
158
|
+
text = f"""# Windows Terminal layout for {session_name}
|
|
159
|
+
# Generated on {random_suffix}
|
|
160
|
+
{wt_command}
|
|
161
|
+
"""
|
|
162
|
+
ps1_file.write_text(text, encoding="utf-8")
|
|
163
|
+
|
|
162
164
|
logger.info(f"Windows Terminal script files created: {bat_file.absolute()}")
|
|
163
165
|
return str(bat_file.absolute())
|
|
164
|
-
|
|
166
|
+
|
|
165
167
|
except OSError as e:
|
|
166
168
|
logger.error(f"Failed to create script file: {e}")
|
|
167
169
|
raise
|
|
168
|
-
|
|
169
|
-
def generate_split_pane_command(self, tab_config: Dict[str, Tuple[str, str]],
|
|
170
|
+
|
|
171
|
+
def generate_split_pane_command(self, tab_config: Dict[str, Tuple[str, str]],
|
|
170
172
|
window_name: str | None = None) -> str:
|
|
171
173
|
"""Generate Windows Terminal command with split panes instead of separate tabs."""
|
|
172
174
|
self.validate_tab_config(tab_config)
|
|
173
|
-
|
|
175
|
+
|
|
174
176
|
wt_parts = ["wt"]
|
|
175
|
-
|
|
177
|
+
|
|
176
178
|
if window_name:
|
|
177
179
|
wt_parts.extend(["-w", WTLayoutGenerator.escape_for_wt(window_name)])
|
|
178
|
-
|
|
180
|
+
|
|
179
181
|
# First pane (main tab)
|
|
180
182
|
first_tab = list(tab_config.items())[0]
|
|
181
183
|
tab_name, (cwd, command) = first_tab
|
|
182
|
-
|
|
184
|
+
|
|
183
185
|
# Start with first tab
|
|
184
186
|
wt_parts.extend(["-d", WTLayoutGenerator.escape_for_wt(cwd)])
|
|
185
187
|
wt_parts.extend(["--title", WTLayoutGenerator.escape_for_wt(tab_name)])
|
|
186
188
|
wt_parts.append(WTLayoutGenerator.escape_for_wt(command))
|
|
187
|
-
|
|
189
|
+
|
|
188
190
|
# Add split panes for remaining tabs
|
|
189
191
|
for tab_name, (cwd, command) in list(tab_config.items())[1:]:
|
|
190
192
|
wt_parts.append(";")
|
|
@@ -192,5 +194,5 @@ class WTLayoutGenerator:
|
|
|
192
194
|
wt_parts.extend(["-d", WTLayoutGenerator.escape_for_wt(cwd)])
|
|
193
195
|
wt_parts.extend(["--title", WTLayoutGenerator.escape_for_wt(tab_name)])
|
|
194
196
|
wt_parts.append(WTLayoutGenerator.escape_for_wt(command))
|
|
195
|
-
|
|
196
|
-
return " ".join(wt_parts)
|
|
197
|
+
|
|
198
|
+
return " ".join(wt_parts)
|
|
@@ -14,16 +14,16 @@ logger = logging.getLogger(__name__)
|
|
|
14
14
|
|
|
15
15
|
class WTProcessMonitor:
|
|
16
16
|
"""Handles process status checking and verification on local and remote Windows machines."""
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
def __init__(self, remote_executor: Optional[WTRemoteExecutor] = None):
|
|
19
19
|
self.remote_executor = remote_executor
|
|
20
20
|
self.is_local = remote_executor is None
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
@property
|
|
23
23
|
def location_name(self) -> str:
|
|
24
24
|
"""Get the location name for status reporting."""
|
|
25
25
|
return "local" if self.is_local else (self.remote_executor.remote_name if self.remote_executor else "unknown")
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
def _run_command(self, command: str, timeout: int = 30) -> subprocess.CompletedProcess[str]:
|
|
28
28
|
"""Run command either locally or remotely."""
|
|
29
29
|
if self.is_local:
|
|
@@ -37,8 +37,8 @@ class WTProcessMonitor:
|
|
|
37
37
|
if self.remote_executor is None:
|
|
38
38
|
raise ValueError("Remote executor is None but is_local is False")
|
|
39
39
|
return self.remote_executor.run_command(command, timeout)
|
|
40
|
-
|
|
41
|
-
def check_command_status(self, tab_name: str, tab_config: Dict[str, Tuple[str, str]],
|
|
40
|
+
|
|
41
|
+
def check_command_status(self, tab_name: str, tab_config: Dict[str, Tuple[str, str]],
|
|
42
42
|
use_verification: bool = True) -> Dict[str, Any]:
|
|
43
43
|
"""Check command status with optional process verification."""
|
|
44
44
|
if tab_name not in tab_config:
|
|
@@ -50,27 +50,27 @@ class WTProcessMonitor:
|
|
|
50
50
|
"command": None,
|
|
51
51
|
"location": self.location_name
|
|
52
52
|
}
|
|
53
|
-
|
|
53
|
+
|
|
54
54
|
# Use the verified method by default for more accurate results
|
|
55
55
|
if use_verification:
|
|
56
56
|
return self.get_verified_process_status(tab_name, tab_config)
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
return self._basic_process_check(tab_name, tab_config)
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
def _basic_process_check(self, tab_name: str, tab_config: Dict[str, Tuple[str, str]]) -> Dict[str, Any]:
|
|
61
61
|
"""Basic process checking without verification."""
|
|
62
62
|
_, command = tab_config[tab_name]
|
|
63
|
-
|
|
63
|
+
|
|
64
64
|
try:
|
|
65
65
|
check_script = self._create_process_check_script(command)
|
|
66
66
|
result = self._run_command(check_script, timeout=15)
|
|
67
|
-
|
|
67
|
+
|
|
68
68
|
if result.returncode == 0:
|
|
69
69
|
try:
|
|
70
70
|
# Parse PowerShell output (JSON format)
|
|
71
71
|
output_lines = [line.strip() for line in result.stdout.strip().split('\n') if line.strip()]
|
|
72
72
|
matching_processes = []
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
for line in output_lines:
|
|
75
75
|
if line.startswith('{') and line.endswith('}'):
|
|
76
76
|
try:
|
|
@@ -78,7 +78,7 @@ class WTProcessMonitor:
|
|
|
78
78
|
matching_processes.append(proc_info)
|
|
79
79
|
except json.JSONDecodeError:
|
|
80
80
|
continue
|
|
81
|
-
|
|
81
|
+
|
|
82
82
|
if matching_processes:
|
|
83
83
|
return {
|
|
84
84
|
"status": "running",
|
|
@@ -109,14 +109,14 @@ class WTProcessMonitor:
|
|
|
109
109
|
}
|
|
110
110
|
else:
|
|
111
111
|
return {
|
|
112
|
-
"status": "error",
|
|
112
|
+
"status": "error",
|
|
113
113
|
"error": f"Command failed: {result.stderr}",
|
|
114
114
|
"running": False,
|
|
115
115
|
"command": command,
|
|
116
116
|
"tab_name": tab_name,
|
|
117
117
|
"location": self.location_name
|
|
118
118
|
}
|
|
119
|
-
|
|
119
|
+
|
|
120
120
|
except Exception as e:
|
|
121
121
|
logger.error(f"Error checking command status for tab '{tab_name}': {e}")
|
|
122
122
|
return {
|
|
@@ -127,14 +127,14 @@ class WTProcessMonitor:
|
|
|
127
127
|
"tab_name": tab_name,
|
|
128
128
|
"location": self.location_name
|
|
129
129
|
}
|
|
130
|
-
|
|
130
|
+
|
|
131
131
|
def _create_process_check_script(self, command: str) -> str:
|
|
132
132
|
"""Create PowerShell script for checking processes."""
|
|
133
133
|
# Escape command for PowerShell
|
|
134
134
|
escaped_command = command.replace("'", "''").replace('"', '""')
|
|
135
135
|
cmd_parts = [part for part in command.split() if len(part) > 2]
|
|
136
136
|
primary_cmd = cmd_parts[0] if cmd_parts else ''
|
|
137
|
-
|
|
137
|
+
|
|
138
138
|
return f"""
|
|
139
139
|
$targetCommand = '{escaped_command}'
|
|
140
140
|
$cmdParts = @({', '.join([f"'{part}'" for part in cmd_parts])})
|
|
@@ -144,23 +144,23 @@ $currentPid = $PID
|
|
|
144
144
|
Get-Process | ForEach-Object {{
|
|
145
145
|
try {{
|
|
146
146
|
if ($_.Id -eq $currentPid) {{ return }}
|
|
147
|
-
|
|
147
|
+
|
|
148
148
|
$cmdline = ""
|
|
149
149
|
try {{
|
|
150
150
|
$cmdline = (Get-WmiObject Win32_Process -Filter "ProcessId = $($_.Id)").CommandLine
|
|
151
151
|
}} catch {{
|
|
152
152
|
$cmdline = $_.ProcessName
|
|
153
153
|
}}
|
|
154
|
-
|
|
154
|
+
|
|
155
155
|
if ($cmdline -and $cmdline -ne "") {{
|
|
156
156
|
if ($cmdline -like "*PowerShell*" -and $cmdline -like "*Get-Process*") {{ return }}
|
|
157
|
-
|
|
157
|
+
|
|
158
158
|
$matchesPrimary = $cmdline -like "*$primaryCmd*" -and $primaryCmd -ne "powershell"
|
|
159
159
|
$matchCount = 0
|
|
160
160
|
foreach ($part in $cmdParts[1..($cmdParts.Length-1)]) {{
|
|
161
161
|
if ($cmdline -like "*$part*") {{ $matchCount++ }}
|
|
162
162
|
}}
|
|
163
|
-
|
|
163
|
+
|
|
164
164
|
if ($matchesPrimary -and $matchCount -ge 2) {{
|
|
165
165
|
$procInfo = @{{
|
|
166
166
|
"pid" = $_.Id
|
|
@@ -178,7 +178,7 @@ Get-Process | ForEach-Object {{
|
|
|
178
178
|
}}
|
|
179
179
|
}}
|
|
180
180
|
"""
|
|
181
|
-
|
|
181
|
+
|
|
182
182
|
def force_fresh_process_check(self, tab_name: str, tab_config: Dict[str, Tuple[str, str]]) -> Dict[str, Any]:
|
|
183
183
|
"""Force a fresh process check with additional validation."""
|
|
184
184
|
if tab_name not in tab_config:
|
|
@@ -189,18 +189,18 @@ Get-Process | ForEach-Object {{
|
|
|
189
189
|
"command": None,
|
|
190
190
|
"location": self.location_name
|
|
191
191
|
}
|
|
192
|
-
|
|
192
|
+
|
|
193
193
|
_, command = tab_config[tab_name]
|
|
194
|
-
|
|
194
|
+
|
|
195
195
|
try:
|
|
196
196
|
# Get timestamp for freshness validation
|
|
197
197
|
timestamp_cmd = "Get-Date -UFormat %s"
|
|
198
198
|
timestamp_result = self._run_command(timestamp_cmd, timeout=5)
|
|
199
199
|
check_timestamp = timestamp_result.stdout.strip() if timestamp_result.returncode == 0 else "unknown"
|
|
200
|
-
|
|
200
|
+
|
|
201
201
|
check_script = self._create_fresh_check_script(command)
|
|
202
202
|
result = self._run_command(check_script, timeout=15)
|
|
203
|
-
|
|
203
|
+
|
|
204
204
|
if result.returncode == 0:
|
|
205
205
|
try:
|
|
206
206
|
# Parse the output to extract JSON
|
|
@@ -209,7 +209,7 @@ Get-Process | ForEach-Object {{
|
|
|
209
209
|
if line.startswith('{') and '"processes"' in line:
|
|
210
210
|
check_result = json.loads(line)
|
|
211
211
|
matching_processes = check_result.get("processes", [])
|
|
212
|
-
|
|
212
|
+
|
|
213
213
|
return {
|
|
214
214
|
"status": "running" if matching_processes else "not_running",
|
|
215
215
|
"running": bool(matching_processes),
|
|
@@ -220,7 +220,7 @@ Get-Process | ForEach-Object {{
|
|
|
220
220
|
"check_timestamp": check_timestamp,
|
|
221
221
|
"method": "force_fresh_check"
|
|
222
222
|
}
|
|
223
|
-
|
|
223
|
+
|
|
224
224
|
# Fallback if no JSON found
|
|
225
225
|
return {
|
|
226
226
|
"status": "not_running",
|
|
@@ -244,14 +244,14 @@ Get-Process | ForEach-Object {{
|
|
|
244
244
|
}
|
|
245
245
|
else:
|
|
246
246
|
return {
|
|
247
|
-
"status": "error",
|
|
247
|
+
"status": "error",
|
|
248
248
|
"error": f"Command failed: {result.stderr}",
|
|
249
249
|
"running": False,
|
|
250
250
|
"command": command,
|
|
251
251
|
"tab_name": tab_name,
|
|
252
252
|
"location": self.location_name
|
|
253
253
|
}
|
|
254
|
-
|
|
254
|
+
|
|
255
255
|
except Exception as e:
|
|
256
256
|
logger.error(f"Error in fresh process check for tab '{tab_name}': {e}")
|
|
257
257
|
return {
|
|
@@ -262,13 +262,13 @@ Get-Process | ForEach-Object {{
|
|
|
262
262
|
"tab_name": tab_name,
|
|
263
263
|
"location": self.location_name
|
|
264
264
|
}
|
|
265
|
-
|
|
265
|
+
|
|
266
266
|
def _create_fresh_check_script(self, command: str) -> str:
|
|
267
267
|
"""Create enhanced PowerShell process checking script with freshness validation."""
|
|
268
268
|
escaped_command = command.replace("'", "''").replace('"', '""')
|
|
269
269
|
cmd_parts = [part for part in command.split() if len(part) > 2]
|
|
270
270
|
primary_cmd = cmd_parts[0] if cmd_parts else ''
|
|
271
|
-
|
|
271
|
+
|
|
272
272
|
return f"""
|
|
273
273
|
Start-Sleep -Milliseconds 100
|
|
274
274
|
|
|
@@ -282,30 +282,30 @@ $matchingProcesses = @()
|
|
|
282
282
|
Get-Process | ForEach-Object {{
|
|
283
283
|
try {{
|
|
284
284
|
if ($_.Id -eq $currentPid) {{ return }}
|
|
285
|
-
|
|
285
|
+
|
|
286
286
|
$cmdline = ""
|
|
287
287
|
try {{
|
|
288
288
|
$cmdline = (Get-WmiObject Win32_Process -Filter "ProcessId = $($_.Id)").CommandLine
|
|
289
289
|
}} catch {{
|
|
290
290
|
$cmdline = $_.ProcessName
|
|
291
291
|
}}
|
|
292
|
-
|
|
292
|
+
|
|
293
293
|
if ($cmdline -and $cmdline -ne "") {{
|
|
294
294
|
# Skip our own checking processes
|
|
295
295
|
if ($cmdline -like "*Get-Process*" -or $cmdline -like "*force_fresh_check*") {{ return }}
|
|
296
|
-
|
|
296
|
+
|
|
297
297
|
# Skip processes that started very recently (likely our own scripts)
|
|
298
298
|
if ($_.StartTime -and ($checkTime - $_.StartTime).TotalSeconds -lt 5) {{ return }}
|
|
299
|
-
|
|
299
|
+
|
|
300
300
|
$matchesPrimary = $cmdline -like "*$primaryCmd*" -and $primaryCmd -ne "powershell"
|
|
301
301
|
$matchCount = 0
|
|
302
302
|
foreach ($part in $cmdParts[1..($cmdParts.Length-1)]) {{
|
|
303
303
|
if ($cmdline -like "*$part*") {{ $matchCount++ }}
|
|
304
304
|
}}
|
|
305
|
-
|
|
305
|
+
|
|
306
306
|
if ($matchesPrimary -and $matchCount -ge 2) {{
|
|
307
307
|
$isDirectCommand = -not ($cmdline -like "*-Command*" -or $cmdline -like "*Get-Process*")
|
|
308
|
-
|
|
308
|
+
|
|
309
309
|
if ($isDirectCommand -or ($targetCommand -in $cmdline -and $cmdline -notlike "*powershell -Command*")) {{
|
|
310
310
|
$procInfo = @{{
|
|
311
311
|
"pid" = $_.Id
|
|
@@ -334,21 +334,21 @@ $result = @{{
|
|
|
334
334
|
|
|
335
335
|
Write-Output $result
|
|
336
336
|
"""
|
|
337
|
-
|
|
337
|
+
|
|
338
338
|
def verify_process_alive(self, pid: int) -> bool:
|
|
339
339
|
"""Verify if a process with given PID is actually alive."""
|
|
340
340
|
try:
|
|
341
341
|
verify_cmd = f"Get-Process -Id {pid} -ErrorAction SilentlyContinue | Select-Object -First 1"
|
|
342
342
|
result = self._run_command(verify_cmd, timeout=5)
|
|
343
|
-
|
|
343
|
+
|
|
344
344
|
return result.returncode == 0 and result.stdout.strip() != ""
|
|
345
345
|
except Exception:
|
|
346
346
|
return False
|
|
347
|
-
|
|
347
|
+
|
|
348
348
|
def get_verified_process_status(self, tab_name: str, tab_config: Dict[str, Tuple[str, str]]) -> Dict[str, Any]:
|
|
349
349
|
"""Get process status with additional verification that processes are actually alive."""
|
|
350
350
|
status = self.force_fresh_process_check(tab_name, tab_config)
|
|
351
|
-
|
|
351
|
+
|
|
352
352
|
if status.get("running") and status.get("processes"):
|
|
353
353
|
verified_processes = []
|
|
354
354
|
for proc in status["processes"]:
|
|
@@ -359,35 +359,35 @@ Write-Output $result
|
|
|
359
359
|
else:
|
|
360
360
|
proc["verified_alive"] = False
|
|
361
361
|
logger.warning(f"Process PID {pid} found in process list but not actually alive")
|
|
362
|
-
|
|
362
|
+
|
|
363
363
|
status["processes"] = verified_processes
|
|
364
364
|
status["running"] = bool(verified_processes)
|
|
365
365
|
status["status"] = "running" if verified_processes else "not_running"
|
|
366
366
|
status["verification_method"] = "get_process_check"
|
|
367
|
-
|
|
367
|
+
|
|
368
368
|
return status
|
|
369
|
-
|
|
369
|
+
|
|
370
370
|
def check_all_commands_status(self, tab_config: Dict[str, Tuple[str, str]]) -> Dict[str, Dict[str, Any]]:
|
|
371
371
|
"""Check status of all commands in the tab configuration."""
|
|
372
372
|
if not tab_config:
|
|
373
373
|
logger.warning("No tab configuration provided.")
|
|
374
374
|
return {}
|
|
375
|
-
|
|
375
|
+
|
|
376
376
|
status_report = {}
|
|
377
377
|
for tab_name in tab_config:
|
|
378
378
|
status_report[tab_name] = self.check_command_status(tab_name, tab_config)
|
|
379
379
|
return status_report
|
|
380
|
-
|
|
380
|
+
|
|
381
381
|
def get_windows_terminal_windows(self) -> Dict[str, Any]:
|
|
382
382
|
"""Get information about currently running Windows Terminal windows."""
|
|
383
383
|
try:
|
|
384
384
|
wt_info_cmd = """
|
|
385
|
-
Get-Process -Name 'WindowsTerminal' -ErrorAction SilentlyContinue |
|
|
385
|
+
Get-Process -Name 'WindowsTerminal' -ErrorAction SilentlyContinue |
|
|
386
386
|
Select-Object Id, ProcessName, StartTime, @{Name="WindowTitle";Expression={(Get-Process -Id $_.Id).MainWindowTitle}} |
|
|
387
387
|
ConvertTo-Json -Depth 2
|
|
388
388
|
"""
|
|
389
389
|
result = self._run_command(wt_info_cmd, timeout=15)
|
|
390
|
-
|
|
390
|
+
|
|
391
391
|
if result.returncode == 0 and result.stdout.strip():
|
|
392
392
|
try:
|
|
393
393
|
wt_processes = json.loads(result.stdout)
|
|
@@ -415,4 +415,4 @@ ConvertTo-Json -Depth 2
|
|
|
415
415
|
"success": False,
|
|
416
416
|
"error": str(e),
|
|
417
417
|
"location": self.location_name
|
|
418
|
-
}
|
|
418
|
+
}
|