machineconfig 1.97__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 +1 -1
- 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 -17
- 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 +1 -1
- 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.97.dist-info → machineconfig-2.0.dist-info}/METADATA +13 -8
- {machineconfig-1.97.dist-info → machineconfig-2.0.dist-info}/RECORD +163 -149
- machineconfig/cluster/self_ssh.py +0 -57
- machineconfig/scripts/python/ai/init.py +0 -56
- machineconfig/scripts/python/ai/rules/python/dev.md +0 -31
- {machineconfig-1.97.dist-info → machineconfig-2.0.dist-info}/WHEEL +0 -0
- {machineconfig-1.97.dist-info → machineconfig-2.0.dist-info}/top_level.txt +0 -0
|
@@ -13,11 +13,11 @@ logger = logging.getLogger(__name__)
|
|
|
13
13
|
|
|
14
14
|
class ProcessMonitor:
|
|
15
15
|
"""Handles process status checking and verification on remote machines."""
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
def __init__(self, remote_executor: RemoteExecutor):
|
|
18
18
|
self.remote_executor = remote_executor
|
|
19
|
-
|
|
20
|
-
def check_command_status(self, tab_name: str, tab_config: Dict[str, Tuple[str, str]],
|
|
19
|
+
|
|
20
|
+
def check_command_status(self, tab_name: str, tab_config: Dict[str, Tuple[str, str]],
|
|
21
21
|
use_verification: bool = True) -> Dict[str, Any]:
|
|
22
22
|
"""Check command status with optional process verification."""
|
|
23
23
|
if tab_name not in tab_config:
|
|
@@ -29,26 +29,26 @@ class ProcessMonitor:
|
|
|
29
29
|
"command": None,
|
|
30
30
|
"remote": self.remote_executor.remote_name
|
|
31
31
|
}
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
# Use the verified method by default for more accurate results
|
|
34
34
|
if use_verification:
|
|
35
35
|
return self.get_verified_process_status(tab_name, tab_config)
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
return self._basic_process_check(tab_name, tab_config)
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
def _basic_process_check(self, tab_name: str, tab_config: Dict[str, Tuple[str, str]]) -> Dict[str, Any]:
|
|
40
40
|
"""Basic process checking without verification."""
|
|
41
41
|
_, command = tab_config[tab_name]
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
try:
|
|
44
44
|
check_script = self._create_process_check_script(command)
|
|
45
45
|
remote_cmd = f"$HOME/venvs/ve/bin/python -c {shlex.quote(check_script)}"
|
|
46
46
|
result = self.remote_executor.run_command(remote_cmd, timeout=15)
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
if result.returncode == 0:
|
|
49
49
|
try:
|
|
50
50
|
matching_processes = json.loads(result.stdout.strip())
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
if matching_processes:
|
|
53
53
|
return {
|
|
54
54
|
"status": "running",
|
|
@@ -79,14 +79,14 @@ class ProcessMonitor:
|
|
|
79
79
|
}
|
|
80
80
|
else:
|
|
81
81
|
return {
|
|
82
|
-
"status": "error",
|
|
82
|
+
"status": "error",
|
|
83
83
|
"error": f"Remote command failed: {result.stderr}",
|
|
84
84
|
"running": False,
|
|
85
85
|
"command": command,
|
|
86
86
|
"tab_name": tab_name,
|
|
87
87
|
"remote": self.remote_executor.remote_name
|
|
88
88
|
}
|
|
89
|
-
|
|
89
|
+
|
|
90
90
|
except Exception as e:
|
|
91
91
|
logger.error(f"Error checking command status for tab '{tab_name}': {e}")
|
|
92
92
|
return {
|
|
@@ -97,7 +97,7 @@ class ProcessMonitor:
|
|
|
97
97
|
"tab_name": tab_name,
|
|
98
98
|
"remote": self.remote_executor.remote_name
|
|
99
99
|
}
|
|
100
|
-
|
|
100
|
+
|
|
101
101
|
def _create_process_check_script(self, command: str) -> str:
|
|
102
102
|
"""Create Python script for checking processes on remote machine."""
|
|
103
103
|
return f"""
|
|
@@ -110,25 +110,25 @@ def check_process():
|
|
|
110
110
|
full_command = '{command}'
|
|
111
111
|
cmd_parts = [part for part in full_command.split() if len(part) > 2]
|
|
112
112
|
current_pid = os.getpid()
|
|
113
|
-
|
|
113
|
+
|
|
114
114
|
primary_cmd = cmd_parts[0] if cmd_parts else ''
|
|
115
|
-
|
|
115
|
+
|
|
116
116
|
for proc in psutil.process_iter(['pid', 'name', 'cmdline', 'status', 'create_time']):
|
|
117
117
|
try:
|
|
118
118
|
if proc.info['pid'] == current_pid:
|
|
119
119
|
continue
|
|
120
|
-
|
|
120
|
+
|
|
121
121
|
if proc.info['cmdline'] and len(proc.info['cmdline']) > 0:
|
|
122
122
|
cmdline_str = ' '.join(proc.info['cmdline'])
|
|
123
|
-
|
|
123
|
+
|
|
124
124
|
if 'check_process()' in cmdline_str or 'psutil.process_iter' in cmdline_str:
|
|
125
125
|
continue
|
|
126
|
-
|
|
126
|
+
|
|
127
127
|
matches_primary = primary_cmd in cmdline_str
|
|
128
128
|
matches_parts = sum(1 for part in cmd_parts[1:] if part in cmdline_str)
|
|
129
|
-
|
|
129
|
+
|
|
130
130
|
if (matches_primary and matches_parts >= 2) or \\
|
|
131
|
-
(full_command in cmdline_str and not any(python_indicator in cmdline_str.lower()
|
|
131
|
+
(full_command in cmdline_str and not any(python_indicator in cmdline_str.lower()
|
|
132
132
|
for python_indicator in ['python -c', 'import psutil', 'def check_process'])):
|
|
133
133
|
matching_processes.append({{
|
|
134
134
|
"pid": proc.info['pid'],
|
|
@@ -140,14 +140,14 @@ def check_process():
|
|
|
140
140
|
}})
|
|
141
141
|
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
|
142
142
|
continue
|
|
143
|
-
|
|
143
|
+
|
|
144
144
|
return matching_processes
|
|
145
145
|
|
|
146
146
|
if __name__ == "__main__":
|
|
147
147
|
processes = check_process()
|
|
148
148
|
print(json.dumps(processes))
|
|
149
149
|
"""
|
|
150
|
-
|
|
150
|
+
|
|
151
151
|
def force_fresh_process_check(self, tab_name: str, tab_config: Dict[str, Tuple[str, str]]) -> Dict[str, Any]:
|
|
152
152
|
"""Force a fresh process check with additional validation."""
|
|
153
153
|
if tab_name not in tab_config:
|
|
@@ -158,23 +158,23 @@ if __name__ == "__main__":
|
|
|
158
158
|
"command": None,
|
|
159
159
|
"remote": self.remote_executor.remote_name
|
|
160
160
|
}
|
|
161
|
-
|
|
161
|
+
|
|
162
162
|
_, command = tab_config[tab_name]
|
|
163
|
-
|
|
163
|
+
|
|
164
164
|
try:
|
|
165
165
|
# Get timestamp for freshness validation
|
|
166
166
|
timestamp_result = self.remote_executor.run_command("date +%s", timeout=5)
|
|
167
167
|
check_timestamp = timestamp_result.stdout.strip() if timestamp_result.returncode == 0 else "unknown"
|
|
168
|
-
|
|
168
|
+
|
|
169
169
|
check_script = self._create_fresh_check_script(command)
|
|
170
170
|
remote_cmd = f"$HOME/venvs/ve/bin/python -c {shlex.quote(check_script)}"
|
|
171
171
|
result = self.remote_executor.run_command(remote_cmd, timeout=15)
|
|
172
|
-
|
|
172
|
+
|
|
173
173
|
if result.returncode == 0:
|
|
174
174
|
try:
|
|
175
175
|
check_result = json.loads(result.stdout.strip())
|
|
176
176
|
matching_processes = check_result.get("processes", [])
|
|
177
|
-
|
|
177
|
+
|
|
178
178
|
return {
|
|
179
179
|
"status": "running" if matching_processes else "not_running",
|
|
180
180
|
"running": bool(matching_processes),
|
|
@@ -198,14 +198,14 @@ if __name__ == "__main__":
|
|
|
198
198
|
}
|
|
199
199
|
else:
|
|
200
200
|
return {
|
|
201
|
-
"status": "error",
|
|
201
|
+
"status": "error",
|
|
202
202
|
"error": f"Remote command failed: {result.stderr}",
|
|
203
203
|
"running": False,
|
|
204
204
|
"command": command,
|
|
205
205
|
"tab_name": tab_name,
|
|
206
206
|
"remote": self.remote_executor.remote_name
|
|
207
207
|
}
|
|
208
|
-
|
|
208
|
+
|
|
209
209
|
except Exception as e:
|
|
210
210
|
logger.error(f"Error in fresh process check for tab '{tab_name}': {e}")
|
|
211
211
|
return {
|
|
@@ -216,11 +216,11 @@ if __name__ == "__main__":
|
|
|
216
216
|
"tab_name": tab_name,
|
|
217
217
|
"remote": self.remote_executor.remote_name
|
|
218
218
|
}
|
|
219
|
-
|
|
219
|
+
|
|
220
220
|
def _create_fresh_check_script(self, command: str) -> str:
|
|
221
221
|
"""Create enhanced process checking script with freshness validation."""
|
|
222
222
|
escaped_command = command.replace("'", "\\'").replace('"', '\\"')
|
|
223
|
-
|
|
223
|
+
|
|
224
224
|
return f'''
|
|
225
225
|
import psutil
|
|
226
226
|
import json
|
|
@@ -229,40 +229,40 @@ import time
|
|
|
229
229
|
|
|
230
230
|
def force_fresh_check():
|
|
231
231
|
time.sleep(0.1)
|
|
232
|
-
|
|
232
|
+
|
|
233
233
|
matching_processes = []
|
|
234
234
|
full_command = '{escaped_command}'
|
|
235
235
|
cmd_parts = [part for part in full_command.split() if len(part) > 2]
|
|
236
236
|
current_pid = os.getpid()
|
|
237
237
|
primary_cmd = cmd_parts[0] if cmd_parts else ''
|
|
238
|
-
|
|
238
|
+
|
|
239
239
|
check_time = time.time()
|
|
240
|
-
|
|
240
|
+
|
|
241
241
|
for proc in psutil.process_iter(['pid', 'name', 'cmdline', 'status', 'create_time']):
|
|
242
242
|
try:
|
|
243
243
|
if proc.info['pid'] == current_pid:
|
|
244
244
|
continue
|
|
245
|
-
|
|
245
|
+
|
|
246
246
|
if proc.info['cmdline'] and len(proc.info['cmdline']) > 0:
|
|
247
247
|
cmdline_str = ' '.join(proc.info['cmdline'])
|
|
248
|
-
|
|
248
|
+
|
|
249
249
|
if any(indicator in cmdline_str for indicator in [
|
|
250
250
|
'check_process()', 'psutil.process_iter', 'force_fresh_check',
|
|
251
251
|
'import psutil', 'def check_process'
|
|
252
252
|
]):
|
|
253
253
|
continue
|
|
254
|
-
|
|
254
|
+
|
|
255
255
|
if proc.info['create_time'] and proc.info['create_time'] > check_time - 5:
|
|
256
256
|
continue
|
|
257
|
-
|
|
257
|
+
|
|
258
258
|
matches_primary = primary_cmd in cmdline_str and primary_cmd != 'python'
|
|
259
259
|
matches_parts = sum(1 for part in cmd_parts[1:] if part in cmdline_str)
|
|
260
|
-
|
|
260
|
+
|
|
261
261
|
if matches_primary and matches_parts >= 2:
|
|
262
262
|
script_indicators = ['-c', 'import ', 'def ', 'psutil']
|
|
263
|
-
is_direct_command = not any(script_indicator in cmdline_str.lower()
|
|
263
|
+
is_direct_command = not any(script_indicator in cmdline_str.lower()
|
|
264
264
|
for script_indicator in script_indicators)
|
|
265
|
-
|
|
265
|
+
|
|
266
266
|
if is_direct_command or (full_command in cmdline_str and 'python -c' not in cmdline_str):
|
|
267
267
|
matching_processes.append({{
|
|
268
268
|
"pid": proc.info['pid'],
|
|
@@ -275,7 +275,7 @@ def force_fresh_check():
|
|
|
275
275
|
}})
|
|
276
276
|
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
|
277
277
|
continue
|
|
278
|
-
|
|
278
|
+
|
|
279
279
|
return {{
|
|
280
280
|
"processes": matching_processes,
|
|
281
281
|
"check_timestamp": check_time,
|
|
@@ -287,23 +287,23 @@ if __name__ == "__main__":
|
|
|
287
287
|
result = force_fresh_check()
|
|
288
288
|
print(json.dumps(result))
|
|
289
289
|
'''
|
|
290
|
-
|
|
290
|
+
|
|
291
291
|
def verify_process_alive(self, pid: int) -> bool:
|
|
292
292
|
"""Verify if a process with given PID is actually alive."""
|
|
293
293
|
try:
|
|
294
294
|
verify_cmd = f"kill -0 {pid} 2>/dev/null && echo 'alive' || echo 'dead'"
|
|
295
295
|
result = self.remote_executor.run_command(verify_cmd, timeout=5)
|
|
296
|
-
|
|
296
|
+
|
|
297
297
|
if result.returncode == 0:
|
|
298
298
|
return result.stdout.strip() == 'alive'
|
|
299
299
|
return False
|
|
300
300
|
except Exception:
|
|
301
301
|
return False
|
|
302
|
-
|
|
302
|
+
|
|
303
303
|
def get_verified_process_status(self, tab_name: str, tab_config: Dict[str, Tuple[str, str]]) -> Dict[str, Any]:
|
|
304
304
|
"""Get process status with additional verification that processes are actually alive."""
|
|
305
305
|
status = self.force_fresh_process_check(tab_name, tab_config)
|
|
306
|
-
|
|
306
|
+
|
|
307
307
|
if status.get("running") and status.get("processes"):
|
|
308
308
|
verified_processes = []
|
|
309
309
|
for proc in status["processes"]:
|
|
@@ -314,20 +314,20 @@ if __name__ == "__main__":
|
|
|
314
314
|
else:
|
|
315
315
|
proc["verified_alive"] = False
|
|
316
316
|
logger.warning(f"Process PID {pid} found in process list but not actually alive")
|
|
317
|
-
|
|
317
|
+
|
|
318
318
|
status["processes"] = verified_processes
|
|
319
319
|
status["running"] = bool(verified_processes)
|
|
320
320
|
status["status"] = "running" if verified_processes else "not_running"
|
|
321
321
|
status["verification_method"] = "kill_signal_check"
|
|
322
|
-
|
|
322
|
+
|
|
323
323
|
return status
|
|
324
|
-
|
|
324
|
+
|
|
325
325
|
def check_all_commands_status(self, tab_config: Dict[str, Tuple[str, str]]) -> Dict[str, Dict[str, Any]]:
|
|
326
326
|
"""Check status of all commands in the tab configuration."""
|
|
327
327
|
if not tab_config:
|
|
328
328
|
logger.warning("No tab configuration provided.")
|
|
329
329
|
return {}
|
|
330
|
-
|
|
330
|
+
|
|
331
331
|
status_report = {}
|
|
332
332
|
for tab_name in tab_config:
|
|
333
333
|
status_report[tab_name] = self.check_command_status(tab_name, tab_config)
|
|
@@ -11,10 +11,10 @@ logger = logging.getLogger(__name__)
|
|
|
11
11
|
|
|
12
12
|
class RemoteExecutor:
|
|
13
13
|
"""Handles SSH command execution on remote machines."""
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
def __init__(self, remote_name: str):
|
|
16
16
|
self.remote_name = remote_name
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
def run_command(self, command: str, timeout: int = 30) -> subprocess.CompletedProcess[str]:
|
|
19
19
|
"""Execute a command on the remote machine via SSH."""
|
|
20
20
|
ssh_cmd = ["ssh", self.remote_name, command]
|
|
@@ -32,7 +32,7 @@ class RemoteExecutor:
|
|
|
32
32
|
except Exception as e:
|
|
33
33
|
logger.error(f"SSH command failed: {e}")
|
|
34
34
|
raise
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
def copy_file_to_remote(self, local_file: str, remote_path: str) -> Dict[str, Any]:
|
|
37
37
|
"""Copy a file to the remote machine using SCP."""
|
|
38
38
|
scp_cmd = ["scp", local_file, f"{self.remote_name}:{remote_path}"]
|
|
@@ -47,7 +47,7 @@ class RemoteExecutor:
|
|
|
47
47
|
except Exception as e:
|
|
48
48
|
logger.error(f"SCP operation failed: {e}")
|
|
49
49
|
return {"success": False, "error": str(e)}
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
def create_remote_directory(self, remote_dir: str) -> bool:
|
|
52
52
|
"""Create a directory on the remote machine."""
|
|
53
53
|
try:
|
|
@@ -56,7 +56,7 @@ class RemoteExecutor:
|
|
|
56
56
|
except Exception as e:
|
|
57
57
|
logger.error(f"Failed to create remote directory {remote_dir}: {e}")
|
|
58
58
|
return False
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
def attach_to_session_interactive(self, session_name: str) -> None:
|
|
61
61
|
"""Attach to a Zellij session interactively via SSH."""
|
|
62
62
|
try:
|
|
@@ -16,39 +16,39 @@ console = Console()
|
|
|
16
16
|
|
|
17
17
|
class SessionManager:
|
|
18
18
|
"""Handles Zellij session operations on remote machines."""
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
def __init__(self, remote_executor: RemoteExecutor, session_name: str, tmp_layout_dir: Path):
|
|
21
21
|
self.remote_executor = remote_executor
|
|
22
22
|
self.session_name = session_name
|
|
23
23
|
self.tmp_layout_dir = tmp_layout_dir
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
def copy_layout_to_remote(self, local_layout_file: Path, random_suffix: str) -> str:
|
|
26
26
|
"""Copy the layout file to the remote machine and return the remote path."""
|
|
27
27
|
remote_layout_dir = f"~/{self.tmp_layout_dir.relative_to(Path.home())}"
|
|
28
28
|
remote_layout_file = f"{remote_layout_dir}/zellij_layout_{self.session_name}_{random_suffix}.kdl"
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
# Create remote directory
|
|
31
31
|
if not self.remote_executor.create_remote_directory(remote_layout_dir):
|
|
32
32
|
raise RuntimeError(f"Failed to create remote directory: {remote_layout_dir}")
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
# Copy layout file to remote machine
|
|
35
35
|
copy_result = self.remote_executor.copy_file_to_remote(str(local_layout_file), remote_layout_file)
|
|
36
36
|
if not copy_result["success"]:
|
|
37
37
|
raise RuntimeError(f"Failed to copy layout file to remote: {copy_result['error']}")
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
# Enhanced Rich logging
|
|
40
40
|
console.print(f"[bold green]📁 Zellij layout file copied to remote:[/bold green] [yellow]{self.remote_executor.remote_name}[/yellow][cyan]:{remote_layout_file}[/cyan]")
|
|
41
41
|
return remote_layout_file
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
def check_zellij_session_status(self) -> Dict[str, Any]:
|
|
44
44
|
"""Check if the Zellij session exists and is running."""
|
|
45
45
|
try:
|
|
46
46
|
result = self.remote_executor.run_command('zellij list-sessions', timeout=10)
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
if result.returncode == 0:
|
|
49
49
|
sessions = result.stdout.strip().split('\n') if result.stdout.strip() else []
|
|
50
50
|
session_running = any(self.session_name in session for session in sessions)
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
return {
|
|
53
53
|
"zellij_running": True,
|
|
54
54
|
"session_exists": session_running,
|
|
@@ -63,7 +63,7 @@ class SessionManager:
|
|
|
63
63
|
"session_name": self.session_name,
|
|
64
64
|
"remote": self.remote_executor.remote_name
|
|
65
65
|
}
|
|
66
|
-
|
|
66
|
+
|
|
67
67
|
except Exception as e:
|
|
68
68
|
return {
|
|
69
69
|
"zellij_running": False,
|
|
@@ -71,7 +71,7 @@ class SessionManager:
|
|
|
71
71
|
"session_name": self.session_name,
|
|
72
72
|
"remote": self.remote_executor.remote_name
|
|
73
73
|
}
|
|
74
|
-
|
|
74
|
+
|
|
75
75
|
def start_zellij_session(self, layout_file_path: Optional[str] = None) -> Dict[str, Any]:
|
|
76
76
|
"""Start a Zellij session on the remote machine with the generated layout."""
|
|
77
77
|
try:
|
|
@@ -80,15 +80,15 @@ class SessionManager:
|
|
|
80
80
|
remote_layout_file = f"~/{self.tmp_layout_dir.relative_to(Path.home())}/{layout_filename}"
|
|
81
81
|
else:
|
|
82
82
|
raise ValueError("No layout file path provided.")
|
|
83
|
-
|
|
83
|
+
|
|
84
84
|
# Enhanced Rich logging for session start
|
|
85
85
|
console.print(f"[bold cyan]🚀 Starting Zellij session[/bold cyan] [yellow]'{self.session_name}'[/yellow] [dim]on remote[/dim] [bold yellow]'{self.remote_executor.remote_name}'[/bold yellow] [dim]with layout:[/dim] [blue]{remote_layout_file}[/blue]")
|
|
86
|
-
|
|
86
|
+
|
|
87
87
|
# Start Zellij session with layout
|
|
88
88
|
start_cmd = f"zellij --layout {remote_layout_file} a -b {self.session_name}"
|
|
89
89
|
console.print(f"[dim]Executing:[/dim] [green]{start_cmd}[/green]")
|
|
90
90
|
result = self.remote_executor.run_command(start_cmd, timeout=30)
|
|
91
|
-
|
|
91
|
+
|
|
92
92
|
if result.returncode == 0:
|
|
93
93
|
console.print(f"[bold green]✅ Zellij session[/bold green] [yellow]'{self.session_name}'[/yellow] [green]started successfully on[/green] [bold yellow]{self.remote_executor.remote_name}[/bold yellow]")
|
|
94
94
|
return {
|
|
@@ -104,7 +104,7 @@ class SessionManager:
|
|
|
104
104
|
"session_name": self.session_name,
|
|
105
105
|
"remote": self.remote_executor.remote_name
|
|
106
106
|
}
|
|
107
|
-
|
|
107
|
+
|
|
108
108
|
except Exception as e:
|
|
109
109
|
logger.error(f"Failed to start Zellij session on {self.remote_executor.remote_name}: {e}")
|
|
110
110
|
return {
|
|
@@ -113,7 +113,7 @@ class SessionManager:
|
|
|
113
113
|
"session_name": self.session_name,
|
|
114
114
|
"remote": self.remote_executor.remote_name
|
|
115
115
|
}
|
|
116
|
-
|
|
116
|
+
|
|
117
117
|
def attach_to_session(self) -> None:
|
|
118
118
|
"""Attach to the Zellij session on the remote machine via SSH."""
|
|
119
119
|
self.remote_executor.attach_to_session_interactive(self.session_name)
|
|
@@ -12,19 +12,19 @@ logger = logging.getLogger(__name__)
|
|
|
12
12
|
|
|
13
13
|
class StatusReporter:
|
|
14
14
|
"""Handles comprehensive status reporting for Zellij remote sessions."""
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
def __init__(self, process_monitor: ProcessMonitor, session_manager: SessionManager):
|
|
17
17
|
self.process_monitor = process_monitor
|
|
18
18
|
self.session_manager = session_manager
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
def get_comprehensive_status(self, tab_config: Dict[str, Tuple[str, str]]) -> Dict[str, Any]:
|
|
21
21
|
"""Get comprehensive status including Zellij session and all commands."""
|
|
22
22
|
zellij_status = self.session_manager.check_zellij_session_status()
|
|
23
23
|
commands_status = self.process_monitor.check_all_commands_status(tab_config)
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
running_count = sum(1 for status in commands_status.values() if status.get("running", False))
|
|
26
26
|
total_count = len(commands_status)
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
return {
|
|
29
29
|
"zellij_session": zellij_status,
|
|
30
30
|
"commands": commands_status,
|
|
@@ -36,17 +36,17 @@ class StatusReporter:
|
|
|
36
36
|
"remote": self.session_manager.remote_executor.remote_name
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
def print_status_report(self, tab_config: Dict[str, Tuple[str, str]]) -> None:
|
|
41
41
|
"""Print a formatted status report to console."""
|
|
42
42
|
status = self.get_comprehensive_status(tab_config)
|
|
43
43
|
remote_name = self.session_manager.remote_executor.remote_name
|
|
44
44
|
session_name = self.session_manager.session_name
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
print("=" * 60)
|
|
47
47
|
print(f"🔍 ZELLIJ REMOTE LAYOUT STATUS REPORT ({remote_name})")
|
|
48
48
|
print("=" * 60)
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
# Zellij session status
|
|
51
51
|
zellij = status["zellij_session"]
|
|
52
52
|
if zellij.get("zellij_running", False):
|
|
@@ -56,13 +56,13 @@ class StatusReporter:
|
|
|
56
56
|
print(f"⚠️ Zellij is running on {remote_name} but session '{session_name}' not found")
|
|
57
57
|
else:
|
|
58
58
|
print(f"❌ Zellij session issue on {remote_name}: {zellij.get('error', 'Unknown error')}")
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
print()
|
|
61
|
-
|
|
61
|
+
|
|
62
62
|
# Commands status
|
|
63
63
|
print("📋 COMMAND STATUS:")
|
|
64
64
|
print("-" * 40)
|
|
65
|
-
|
|
65
|
+
|
|
66
66
|
for tab_name, cmd_status in status["commands"].items():
|
|
67
67
|
if cmd_status.get("running", False):
|
|
68
68
|
print(f"✅ {tab_name}: Running on {remote_name}")
|
|
@@ -73,7 +73,7 @@ class StatusReporter:
|
|
|
73
73
|
print(f"❌ {tab_name}: Not running on {remote_name}")
|
|
74
74
|
print(f" Command: {cmd_status.get('command', 'Unknown')}")
|
|
75
75
|
print()
|
|
76
|
-
|
|
76
|
+
|
|
77
77
|
# Summary
|
|
78
78
|
summary = status["summary"]
|
|
79
79
|
print("📊 SUMMARY:")
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
# from machineconfig.cluster.remote_machine import WorkloadParams
|
|
10
10
|
from typing import Optional
|
|
11
|
-
from
|
|
11
|
+
from machineconfig.utils.path_reduced import P, PLike
|
|
12
12
|
|
|
13
13
|
# def expensive_function(workload_params: WorkloadParams, sim_dict: Optional[dict[str, Any]] = None) -> P:
|
|
14
14
|
# import time
|
|
@@ -40,7 +40,7 @@ from crocodile.file_management import P, PLike
|
|
|
40
40
|
|
|
41
41
|
def to_cloud(localpath: PLike, cloud: str, remotepath: Optional[PLike], zip: bool = False, encrypt: bool = False,
|
|
42
42
|
key: Optional[bytes] = None, pwd: Optional[str] = None, rel2home: bool = False, strict: bool = True,
|
|
43
|
-
obfuscate: bool = False,
|
|
43
|
+
# obfuscate: bool = False,
|
|
44
44
|
share: bool = False, verbose: bool = True, os_specific: bool = False, transfers: int = 10, root: Optional[str] = "myhome") -> 'P':
|
|
45
45
|
to_del = []
|
|
46
46
|
localpath = P(localpath).expanduser().absolute() if not P(localpath).exists() else P(localpath)
|
|
@@ -51,7 +51,7 @@ def to_cloud(localpath: PLike, cloud: str, remotepath: Optional[PLike], zip: boo
|
|
|
51
51
|
localpath = localpath.encrypt(key=key, pwd=pwd, inplace=False)
|
|
52
52
|
to_del.append(localpath)
|
|
53
53
|
if remotepath is None:
|
|
54
|
-
rp = localpath.get_remote_path(root=root, os_specific=os_specific, rel2home=rel2home, strict=strict
|
|
54
|
+
rp = localpath.get_remote_path(root=root, os_specific=os_specific, rel2home=rel2home, strict=strict) # if rel2home else (P(root) / localpath if root is not None else localpath)
|
|
55
55
|
else: rp = P(remotepath)
|
|
56
56
|
|
|
57
57
|
from rclone_python import rclone
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -24,7 +24,7 @@ APP_SUMMARY_PATH = LIBRARY_ROOT.joinpath(f"profile/records/{platform.system().lo
|
|
|
24
24
|
|
|
25
25
|
# def scan(path: P, pct: float = 0.0):
|
|
26
26
|
# import vt # vt-py
|
|
27
|
-
# client = vt.Client(PathExtended.home().joinpath("dotfiles/creds/tokens/virustotal").read_text().split("\n")[0])
|
|
27
|
+
# client = vt.Client(PathExtended.home().joinpath("dotfiles/creds/tokens/virustotal").read_text(encoding="utf-8").split("\n")[0])
|
|
28
28
|
# console = Console()
|
|
29
29
|
# console.rule(f"Scanning {path}. {pct:.2f}% done")
|
|
30
30
|
# if path.is_dir():
|
|
@@ -44,7 +44,7 @@ APP_SUMMARY_PATH = LIBRARY_ROOT.joinpath(f"profile/records/{platform.system().lo
|
|
|
44
44
|
# raise ValueError(f"❌ Error in scanning {path}") from ex
|
|
45
45
|
# print("⚠️ Error in scanning, trying again...")
|
|
46
46
|
# time.sleep(30)
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
# # Convert results to list of dictionaries
|
|
49
49
|
# results_data = list(anal.results.values())
|
|
50
50
|
# malicious = []
|
|
@@ -58,13 +58,13 @@ APP_SUMMARY_PATH = LIBRARY_ROOT.joinpath(f"profile/records/{platform.system().lo
|
|
|
58
58
|
# 'result': getattr(result_item, 'result', None),
|
|
59
59
|
# 'category': getattr(result_item, 'category', 'unknown')
|
|
60
60
|
# }
|
|
61
|
-
|
|
62
|
-
# if result_dict.get('result') is None and result_dict.get('category') in ["undetected", "type-unsupported", "failure", "timeout", "confirmed-timeout"]:
|
|
61
|
+
|
|
62
|
+
# if result_dict.get('result') is None and result_dict.get('category') in ["undetected", "type-unsupported", "failure", "timeout", "confirmed-timeout"]:
|
|
63
63
|
# continue
|
|
64
64
|
# else:
|
|
65
65
|
# pprint(result_dict, f"🔍 Found Category {result_dict.get('category')}")
|
|
66
66
|
# malicious.append(result_item)
|
|
67
|
-
|
|
67
|
+
|
|
68
68
|
# positive_pct: float = round(number=len(malicious) / len(results_data) * 100, ndigits=1)
|
|
69
69
|
# print(f"""
|
|
70
70
|
# {'=' * 50}
|
|
@@ -101,10 +101,9 @@ APP_SUMMARY_PATH = LIBRARY_ROOT.joinpath(f"profile/records/{platform.system().lo
|
|
|
101
101
|
# for an_app in apps_paths_tmp:
|
|
102
102
|
# version_path = [x for x in versions_files_paths if x.stem == an_app.stem]
|
|
103
103
|
# if len(version_path) == 1:
|
|
104
|
-
# app_versions.append(version_path[0].read_text())
|
|
104
|
+
# app_versions.append(version_path[0].read_text(encoding="utf-8"))
|
|
105
105
|
# apps_paths_raw.append(an_app)
|
|
106
106
|
# # if an_app.stem in versions_files_paths.stem:
|
|
107
|
-
# # app_versions.append(versions_files_paths.filter(lambda x: x.stem == an_app.stem.replace(".exe", "")).list[0].read_text())
|
|
108
107
|
# # else:
|
|
109
108
|
# # print(f"🤔 Cloud not find a documented version for installation of {an_app.stem}, trying to get it from the app itself.")
|
|
110
109
|
# # tmp = Terminal().run(f"{an_app.stem} --version", shell="powershell").capture().op_if_successfull_or_default(strict_err=False, strict_returcode=False)
|
|
@@ -176,8 +175,8 @@ APP_SUMMARY_PATH = LIBRARY_ROOT.joinpath(f"profile/records/{platform.system().lo
|
|
|
176
175
|
# return "\n".join([header, separator] + rows)
|
|
177
176
|
|
|
178
177
|
# markdown_content = format_app_table_markdown(app_data)
|
|
179
|
-
# APP_SUMMARY_PATH.with_suffix(".md").write_text(markdown_content)
|
|
180
|
-
|
|
178
|
+
# APP_SUMMARY_PATH.with_suffix(".md").write_text(markdown_content, encoding="utf-8")
|
|
179
|
+
|
|
181
180
|
# print(f"""
|
|
182
181
|
# {'=' * 150}
|
|
183
182
|
# 📊 SAFETY REPORT | Summary of app scanning results
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
cargo install
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
# from
|
|
5
|
+
# from machineconfig.utils.terminal import Terminal
|
|
6
6
|
# from machineconfig.utils.path_reduced import P as PathExtended
|
|
7
7
|
# import platform
|
|
8
8
|
|
|
@@ -27,7 +27,7 @@ cargo install
|
|
|
27
27
|
# {'=' * 150}
|
|
28
28
|
# """)
|
|
29
29
|
# if platform.system() == "Windows":
|
|
30
|
-
# Terminal(stdout=None).run(f". {PathExtended.tmpfile(suffix='.ps1').write_text(script)}", shell="pwsh").print()
|
|
30
|
+
# Terminal(stdout=None).run(f". {PathExtended.tmpfile(suffix='.ps1').write_text(script, encoding="utf-8")}", shell="pwsh").print()
|
|
31
31
|
# else:
|
|
32
32
|
# Terminal(stdout=None).run(script, shell="pwsh")
|
|
33
33
|
|
|
@@ -15,9 +15,9 @@ def select_interpreter(workspace_root: str):
|
|
|
15
15
|
📂 Workspace: {workspace_root}
|
|
16
16
|
{'=' * 150}
|
|
17
17
|
""")
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
path = Path(workspace_root).joinpath('.ve_path')
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
if not path.exists():
|
|
22
22
|
print(f"""
|
|
23
23
|
{'⚠️' * 20}
|
|
@@ -26,12 +26,12 @@ def select_interpreter(workspace_root: str):
|
|
|
26
26
|
{'⚠️' * 20}
|
|
27
27
|
""")
|
|
28
28
|
return
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
with open(path, 'r', encoding='utf-8') as f:
|
|
31
31
|
ve_path = Path(f.read().strip()).expanduser()
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
venv_link = Path(workspace_root).joinpath(".venv")
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
if venv_link.exists() and not venv_link.is_symlink():
|
|
36
36
|
print(f"""
|
|
37
37
|
{'⚠️' * 20}
|
|
@@ -40,9 +40,9 @@ def select_interpreter(workspace_root: str):
|
|
|
40
40
|
{'⚠️' * 20}
|
|
41
41
|
""")
|
|
42
42
|
return
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
venv_link.symlink_to(target=ve_path.expanduser().absolute())
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
print(f"""
|
|
47
47
|
{'=' * 150}
|
|
48
48
|
✅ SUCCESS | Virtual environment linked successfully
|