machineconfig 8.51__py3-none-any.whl → 8.61__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.
Files changed (91) hide show
  1. machineconfig/jobs/installer/python_scripts/sysabc.py +13 -34
  2. machineconfig/profile/mapper_dotfiles.toml +3 -3
  3. machineconfig/scripts/python/devops.py +1 -1
  4. machineconfig/scripts/python/devops_navigator.py +1 -1
  5. machineconfig/scripts/python/helper_env/path_manager_tui.py +1 -1
  6. machineconfig/scripts/python/helpers/helper_env/env_manager_tui.py +1 -1
  7. machineconfig/scripts/python/helpers/helper_env/path_manager_tui.py +1 -1
  8. machineconfig/scripts/python/helpers/helpers_croshell/croshell_impl.py +8 -4
  9. machineconfig/scripts/python/helpers/helpers_devops/cli_config.py +33 -1
  10. machineconfig/scripts/python/helpers/helpers_devops/cli_config_mount.py +77 -0
  11. machineconfig/scripts/python/helpers/helpers_devops/cli_data.py +4 -0
  12. machineconfig/scripts/python/helpers/helpers_devops/cli_nw.py +90 -6
  13. machineconfig/scripts/python/helpers/helpers_devops/cli_repos.py +3 -3
  14. machineconfig/scripts/python/helpers/helpers_devops/cli_self.py +41 -15
  15. machineconfig/scripts/python/helpers/helpers_devops/cli_share_temp.py +69 -0
  16. machineconfig/scripts/python/helpers/helpers_devops/cli_ssh.py +4 -4
  17. machineconfig/scripts/python/helpers/helpers_devops/mount_helpers/__init__.py +0 -0
  18. machineconfig/scripts/python/helpers/helpers_devops/mount_helpers/commands.py +25 -0
  19. machineconfig/scripts/python/helpers/helpers_devops/mount_helpers/device_entry.py +17 -0
  20. machineconfig/scripts/python/helpers/helpers_devops/mount_helpers/devices.py +17 -0
  21. machineconfig/scripts/python/helpers/helpers_devops/mount_helpers/linux.py +103 -0
  22. machineconfig/scripts/python/helpers/helpers_devops/mount_helpers/macos.py +100 -0
  23. machineconfig/scripts/python/helpers/helpers_devops/mount_helpers/selection.py +47 -0
  24. machineconfig/scripts/python/helpers/helpers_devops/mount_helpers/utils.py +28 -0
  25. machineconfig/scripts/python/helpers/helpers_devops/mount_helpers/windows.py +91 -0
  26. machineconfig/scripts/python/helpers/helpers_msearch/scripts_windows/fzfg.ps1 +1 -6
  27. machineconfig/scripts/python/helpers/helpers_network/ssh/__init__.py +0 -0
  28. machineconfig/scripts/python/helpers/helpers_network/ssh/ssh_add_key_windows.py +23 -0
  29. machineconfig/scripts/python/helpers/helpers_network/{ssh_add_ssh_key.py → ssh/ssh_add_ssh_key.py} +21 -27
  30. machineconfig/scripts/python/helpers/helpers_network/ssh/ssh_cloud_init.py +33 -0
  31. machineconfig/scripts/python/helpers/helpers_network/{ssh_debug_linux.py → ssh/ssh_debug_linux.py} +70 -51
  32. machineconfig/scripts/python/helpers/helpers_network/ssh/ssh_debug_linux_utils.py +35 -0
  33. machineconfig/scripts/python/helpers/helpers_network/{ssh_debug_windows.py → ssh/ssh_debug_windows.py} +12 -42
  34. machineconfig/scripts/python/helpers/helpers_network/ssh/ssh_debug_windows_utils.py +34 -0
  35. machineconfig/scripts/python/helpers/helpers_repos/cloud_repo_sync.py +2 -3
  36. machineconfig/scripts/python/helpers/{helpers_terminal/terminal_impl.py → helpers_sessions/attach_impl.py} +16 -25
  37. machineconfig/scripts/python/helpers/helpers_sessions/sessions_impl.py +57 -129
  38. machineconfig/scripts/python/helpers/helpers_sessions/utils.py +69 -0
  39. machineconfig/scripts/python/mcfg_entry.py +0 -7
  40. machineconfig/scripts/python/sessions.py +95 -14
  41. machineconfig/scripts/python/utils.py +3 -2
  42. machineconfig/settings/shells/bash/init.sh +0 -7
  43. machineconfig/settings/shells/pwsh/init.ps1 +2 -4
  44. machineconfig/settings/shells/wezterm/wezterm.lua +1 -0
  45. machineconfig/settings/shells/wt/settings.json +13 -19
  46. machineconfig/settings/shells/zsh/init.sh +0 -1
  47. machineconfig/settings/zellij/__init__.py +0 -0
  48. machineconfig/settings/zellij/config.kdl +0 -295
  49. machineconfig/settings/zellij/layouts/__init__.py +0 -0
  50. machineconfig/settings/zellij/layouts/st.kdl +0 -1
  51. machineconfig/settings/zellij/layouts/st2.kdl +6 -2
  52. machineconfig/setup_linux/web_shortcuts/interactive.sh +10 -10
  53. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +10 -10
  54. machineconfig/utils/installer_utils/installer_cli.py +6 -2
  55. machineconfig/utils/installer_utils/installer_helper.py +50 -34
  56. machineconfig/utils/installer_utils/installer_locator_utils.py +3 -13
  57. machineconfig/utils/options_utils/tv_options.py +1 -1
  58. machineconfig/utils/procs.py +35 -27
  59. machineconfig/utils/schemas/layouts/layout_types.py +10 -0
  60. machineconfig/utils/source_of_truth.py +1 -0
  61. machineconfig/utils/ssh_utils/abc.py +1 -1
  62. {machineconfig-8.51.dist-info → machineconfig-8.61.dist-info}/METADATA +2 -3
  63. {machineconfig-8.51.dist-info → machineconfig-8.61.dist-info}/RECORD +68 -72
  64. {machineconfig-8.51.dist-info → machineconfig-8.61.dist-info}/entry_points.txt +0 -1
  65. machineconfig/jobs/scripts/bash_scripts/android.sh +0 -2
  66. machineconfig/jobs/scripts/bash_scripts/mount_drive +0 -128
  67. machineconfig/jobs/scripts/bash_scripts/mount_nfs +0 -49
  68. machineconfig/jobs/scripts/bash_scripts/mount_nw_drive +0 -61
  69. machineconfig/jobs/scripts/bash_scripts/mount_smb +0 -3
  70. machineconfig/jobs/scripts/bash_scripts/share_cloud.sh +0 -64
  71. machineconfig/jobs/scripts/bash_scripts/share_nfs +0 -49
  72. machineconfig/jobs/scripts/bash_scripts/start_docker +0 -23
  73. machineconfig/jobs/scripts/powershell_scripts/Restore-ThunderbirdProfile.ps1 +0 -92
  74. machineconfig/jobs/scripts/powershell_scripts/docker.ps1 +0 -7
  75. machineconfig/jobs/scripts/powershell_scripts/mount_nfs.ps1 +0 -42
  76. machineconfig/jobs/scripts/powershell_scripts/mount_nw.ps1 +0 -9
  77. machineconfig/jobs/scripts/powershell_scripts/mount_smb.ps1 +0 -2
  78. machineconfig/jobs/scripts/powershell_scripts/mount_ssh.ps1 +0 -13
  79. machineconfig/jobs/scripts/powershell_scripts/obs.ps1 +0 -4
  80. machineconfig/jobs/scripts/powershell_scripts/power_options.ps1 +0 -7
  81. machineconfig/jobs/scripts/powershell_scripts/share_cloud.cmd +0 -34
  82. machineconfig/jobs/scripts/powershell_scripts/share_smb.ps1 +0 -16
  83. machineconfig/scripts/python/helpers/helpers_network/mount_nfs.py +0 -85
  84. machineconfig/scripts/python/helpers/helpers_network/mount_nw_drive.py +0 -48
  85. machineconfig/scripts/python/helpers/helpers_network/mount_ssh.py +0 -64
  86. machineconfig/scripts/python/terminal.py +0 -58
  87. machineconfig/settings/zellij/config.orig.kdl +0 -295
  88. /machineconfig/{scripts/python/helpers/helpers_terminal → cluster/sessions_managers/wt_utils/examples}/__init__.py +0 -0
  89. /machineconfig/scripts/python/helpers/helpers_network/{ssh_add_identity.py → ssh/ssh_add_identity.py} +0 -0
  90. {machineconfig-8.51.dist-info → machineconfig-8.61.dist-info}/WHEEL +0 -0
  91. {machineconfig-8.51.dist-info → machineconfig-8.61.dist-info}/top_level.txt +0 -0
@@ -6,45 +6,13 @@ from rich.console import Console
6
6
  from rich.panel import Panel
7
7
  from rich.table import Table
8
8
  from rich import box
9
- import subprocess
10
9
  import os
11
10
  import re
12
11
 
13
-
14
- console = Console()
15
-
16
-
17
- def _run(cmd: list[str]) -> tuple[bool, str]:
18
- try:
19
- result = subprocess.run(cmd, capture_output=True, text=True, check=False)
20
- return result.returncode == 0, result.stdout.strip()
21
- except FileNotFoundError:
22
- return False, ""
12
+ from machineconfig.scripts.python.helpers.helpers_network.ssh.ssh_debug_linux_utils import check_sshd_installed, detect_package_manager, run_cmd
23
13
 
24
14
 
25
- def _check_sshd_installed() -> tuple[bool, str]:
26
- sshd_paths = ["/usr/sbin/sshd", "/usr/bin/sshd", "/sbin/sshd"]
27
- for path in sshd_paths:
28
- if Path(path).exists():
29
- return True, path
30
- ok, which_out = _run(["which", "sshd"])
31
- if ok and which_out:
32
- return True, which_out
33
- return False, ""
34
-
35
-
36
- def _detect_package_manager() -> tuple[str, str]:
37
- if Path("/usr/bin/apt").exists() or Path("/usr/bin/apt-get").exists():
38
- return "apt", "sudo apt update && sudo apt install -y openssh-server"
39
- if Path("/usr/bin/dnf").exists():
40
- return "dnf", "sudo dnf install -y openssh-server"
41
- if Path("/usr/bin/yum").exists():
42
- return "yum", "sudo yum install -y openssh-server"
43
- if Path("/usr/bin/pacman").exists():
44
- return "pacman", "sudo pacman -S --noconfirm openssh"
45
- if Path("/usr/bin/zypper").exists():
46
- return "zypper", "sudo zypper install -y openssh"
47
- return "unknown", "# Install openssh-server using your package manager"
15
+ console = Console()
48
16
 
49
17
 
50
18
  def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
@@ -57,12 +25,12 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
57
25
  ssh_port = "22"
58
26
  ip_addresses: list[str] = []
59
27
 
60
- ok, hostname = _run(["hostname"])
28
+ ok, hostname = run_cmd(["hostname"])
61
29
  hostname = hostname if ok else "unknown"
62
30
 
63
31
  install_info: list[str] = []
64
- sshd_installed, sshd_path = _check_sshd_installed()
65
- _pkg_manager, install_cmd = _detect_package_manager()
32
+ sshd_installed, sshd_path = check_sshd_installed()
33
+ _pkg_manager, install_cmd = detect_package_manager()
66
34
  if not sshd_installed:
67
35
  results["installation"] = {"status": "error", "message": "OpenSSH Server not installed"}
68
36
  issues.append(("sshd not installed", "Cannot accept incoming SSH connections", install_cmd))
@@ -124,8 +92,8 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
124
92
  console.print(Panel("\n".join(perm_info), title="[bold]Permissions[/bold]", border_style="blue"))
125
93
 
126
94
  svc_info: list[str] = []
127
- ssh_ok, _ = _run(["systemctl", "is-active", "ssh"])
128
- sshd_ok, _ = _run(["systemctl", "is-active", "sshd"])
95
+ ssh_ok, _ = run_cmd(["systemctl", "is-active", "ssh"])
96
+ sshd_ok, _ = run_cmd(["systemctl", "is-active", "sshd"])
129
97
  if ssh_ok or sshd_ok:
130
98
  svc_name = "ssh" if ssh_ok else "sshd"
131
99
  results["ssh_service"] = {"status": "ok", "message": f"{svc_name} running"}
@@ -138,7 +106,7 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
138
106
  console.print(Panel("\n".join(svc_info), title="[bold]Service[/bold]", border_style="blue"))
139
107
 
140
108
  net_info: list[str] = []
141
- ok, ip_out = _run(["ip", "addr", "show"])
109
+ ok, ip_out = run_cmd(["ip", "addr", "show"])
142
110
  if ok:
143
111
  ip_addresses = re.findall(r'inet\s+(\d+\.\d+\.\d+\.\d+)/\d+.*scope\s+global', ip_out)
144
112
  if ip_addresses:
@@ -151,6 +119,34 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
151
119
  sshd_config = p
152
120
  break
153
121
 
122
+ sshd_config_d = Path("/etc/ssh/sshd_config.d")
123
+ cloud_init_overrides: dict[str, Path] = {}
124
+ cloud_init_files: list[Path] = []
125
+ if sshd_config_d.exists():
126
+ for conf_file in sorted(sshd_config_d.glob("*.conf")):
127
+ cloud_init_files.append(conf_file)
128
+ try:
129
+ conf_text = conf_file.read_text(encoding="utf-8")
130
+ for line in conf_text.split("\n"):
131
+ line_stripped = line.strip()
132
+ if line_stripped and not line_stripped.startswith("#"):
133
+ key = line_stripped.split()[0] if line_stripped.split() else ""
134
+ if key:
135
+ cloud_init_overrides[key] = conf_file
136
+ except Exception:
137
+ pass
138
+
139
+ if cloud_init_files:
140
+ cloud_info: list[str] = []
141
+ cloud_info.append(f"⚠️ Found [yellow]{len(cloud_init_files)}[/yellow] override file(s) in /etc/ssh/sshd_config.d/")
142
+ cloud_info.append(" [dim]These files can override settings in the main sshd_config![/dim]")
143
+ for cf in cloud_init_files:
144
+ cloud_info.append(f" \u2022 [cyan]{cf.name}[/cyan]")
145
+ if cloud_init_overrides:
146
+ overridden_keys = list(cloud_init_overrides.keys())[:5]
147
+ cloud_info.append(f" Overriding: {', '.join(overridden_keys)}{'...' if len(cloud_init_overrides) > 5 else ''}")
148
+ console.print(Panel("\n".join(cloud_info), title="[bold yellow]Cloud-Init SSH Overrides[/bold yellow]", border_style="yellow"))
149
+
154
150
  if sshd_config:
155
151
  try:
156
152
  config_text = sshd_config.read_text(encoding="utf-8")
@@ -160,22 +156,45 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
160
156
  net_info.append(f"🔌 Port: [cyan]{ssh_port}[/cyan]")
161
157
 
162
158
  pubkey_lines = [line for line in config_text.split("\n") if "PubkeyAuthentication" in line and not line.strip().startswith("#")]
159
+ pubkey_override_file: Path | None = cloud_init_overrides.get("PubkeyAuthentication")
160
+ if pubkey_override_file:
161
+ try:
162
+ override_text = pubkey_override_file.read_text(encoding="utf-8")
163
+ override_pubkey_lines = [line for line in override_text.split("\n") if "PubkeyAuthentication" in line and not line.strip().startswith("#")]
164
+ if override_pubkey_lines:
165
+ pubkey_lines = override_pubkey_lines
166
+ except Exception:
167
+ pass
163
168
  if pubkey_lines and "no" in pubkey_lines[-1].lower():
164
169
  results["pubkey_auth"] = {"status": "error", "message": "PubkeyAuthentication disabled"}
165
- issues.append(("PubkeyAuthentication disabled", "Key-based login won't work", f"Edit {sshd_config}: set PubkeyAuthentication yes, then sudo systemctl restart ssh"))
166
- net_info.append("PubkeyAuthentication: [red]disabled[/red]")
170
+ fix_target = pubkey_override_file if pubkey_override_file else sshd_config
171
+ issues.append(("PubkeyAuthentication disabled", "Key-based login won't work", f"Edit {fix_target}: set PubkeyAuthentication yes, then sudo systemctl restart ssh"))
172
+ override_note = f" (overridden in {pubkey_override_file.name})" if pubkey_override_file else ""
173
+ net_info.append(f"❌ PubkeyAuthentication: [red]disabled[/red]{override_note}")
167
174
  else:
168
175
  net_info.append("✅ PubkeyAuthentication: enabled")
169
176
 
170
177
  password_lines = [line for line in config_text.split("\n") if "PasswordAuthentication" in line and not line.strip().startswith("#")]
178
+ password_override_file: Path | None = cloud_init_overrides.get("PasswordAuthentication")
179
+ if password_override_file:
180
+ try:
181
+ override_text = password_override_file.read_text(encoding="utf-8")
182
+ override_password_lines = [line for line in override_text.split("\n") if "PasswordAuthentication" in line and not line.strip().startswith("#")]
183
+ if override_password_lines:
184
+ password_lines = override_password_lines
185
+ except Exception:
186
+ pass
171
187
  if password_lines:
172
188
  password_enabled = "yes" in password_lines[-1].lower()
189
+ override_note = f" (from {password_override_file.name})" if password_override_file else ""
173
190
  if password_enabled:
174
191
  results["password_auth"] = {"status": "ok", "message": "PasswordAuthentication enabled"}
175
- net_info.append("✅ PasswordAuthentication: [green]enabled[/green]")
192
+ net_info.append(f"✅ PasswordAuthentication: [green]enabled[/green]{override_note}")
176
193
  else:
177
194
  results["password_auth"] = {"status": "info", "message": "PasswordAuthentication disabled"}
178
- net_info.append("ℹ️ PasswordAuthentication: [yellow]disabled[/yellow] (key-only)")
195
+ net_info.append(f"ℹ️ PasswordAuthentication: [yellow]disabled[/yellow] (key-only){override_note}")
196
+ if password_override_file:
197
+ issues.append((f"PasswordAuth disabled by {password_override_file.name}", "Password login blocked by cloud-init config", f"Edit {password_override_file}: set PasswordAuthentication yes, then sudo systemctl restart ssh"))
179
198
  else:
180
199
  results["password_auth"] = {"status": "ok", "message": "PasswordAuthentication enabled (default)"}
181
200
  net_info.append("✅ PasswordAuthentication: [green]enabled[/green] (default)")
@@ -187,7 +206,7 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
187
206
  except Exception:
188
207
  pass
189
208
 
190
- ok, ss_out = _run(["ss", "-tlnp"])
209
+ ok, ss_out = run_cmd(["ss", "-tlnp"])
191
210
  if ok:
192
211
  listening = [line for line in ss_out.split("\n") if f":{ssh_port}" in line]
193
212
  if not listening:
@@ -203,7 +222,7 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
203
222
  net_info.append(f"✅ Listening: 0.0.0.0:{ssh_port}")
204
223
 
205
224
  fw_checked = False
206
- ok, ufw_out = _run(["ufw", "status"])
225
+ ok, ufw_out = run_cmd(["ufw", "status"])
207
226
  if ok and "Status: active" in ufw_out:
208
227
  fw_checked = True
209
228
  if f"{ssh_port}/tcp" in ufw_out.lower() or "ssh" in ufw_out.lower() or f" {ssh_port} " in ufw_out:
@@ -216,10 +235,10 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
216
235
  net_info.append(" [dim]Active firewall without SSH rule = blocked[/dim]")
217
236
 
218
237
  if not fw_checked:
219
- ok, fwd_out = _run(["firewall-cmd", "--state"])
238
+ ok, fwd_out = run_cmd(["firewall-cmd", "--state"])
220
239
  if ok and "running" in fwd_out.lower():
221
240
  fw_checked = True
222
- ok2, svc_out = _run(["firewall-cmd", "--list-services"])
241
+ ok2, svc_out = run_cmd(["firewall-cmd", "--list-services"])
223
242
  if ok2 and "ssh" in svc_out.lower():
224
243
  results["firewall"] = {"status": "ok", "message": "firewalld allows SSH"}
225
244
  net_info.append("✅ Firewall (firewalld): allows SSH")
@@ -229,7 +248,7 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
229
248
  net_info.append("❌ Firewall (firewalld): [red]blocking SSH[/red]")
230
249
 
231
250
  if not fw_checked:
232
- ok, ipt_out = _run(["iptables", "-L", "INPUT", "-n"])
251
+ ok, ipt_out = run_cmd(["iptables", "-L", "INPUT", "-n"])
233
252
  if ok and ipt_out:
234
253
  has_drop_policy = "policy DROP" in ipt_out or "policy REJECT" in ipt_out
235
254
  has_ssh_allow = f"dpt:{ssh_port}" in ipt_out or "dpt:ssh" in ipt_out
@@ -264,7 +283,7 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
264
283
  except Exception:
265
284
  pass
266
285
 
267
- ok, se_out = _run(["getenforce"])
286
+ ok, se_out = run_cmd(["getenforce"])
268
287
  if ok and se_out:
269
288
  if se_out == "Enforcing":
270
289
  other_info.append("ℹ️ SELinux: Enforcing (run [cyan]restorecon -Rv ~/.ssh[/cyan] if issues)")
@@ -274,7 +293,7 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
274
293
  log_files = [Path("/var/log/auth.log"), Path("/var/log/secure")]
275
294
  for lf in log_files:
276
295
  if lf.exists():
277
- ok, tail = _run(["tail", "-n", "20", str(lf)])
296
+ ok, tail = run_cmd(["tail", "-n", "20", str(lf)])
278
297
  if ok:
279
298
  errors = [line for line in tail.split("\n") if any(k in line.lower() for k in ["error", "failed", "refused", "denied"]) and "ssh" in line.lower()]
280
299
  if errors:
@@ -0,0 +1,35 @@
1
+ from pathlib import Path
2
+ import subprocess
3
+
4
+
5
+ def run_cmd(cmd: list[str]) -> tuple[bool, str]:
6
+ try:
7
+ result = subprocess.run(cmd, capture_output=True, text=True, check=False)
8
+ return result.returncode == 0, result.stdout.strip()
9
+ except FileNotFoundError:
10
+ return False, ""
11
+
12
+
13
+ def check_sshd_installed() -> tuple[bool, str]:
14
+ sshd_paths = ["/usr/sbin/sshd", "/usr/bin/sshd", "/sbin/sshd"]
15
+ for path in sshd_paths:
16
+ if Path(path).exists():
17
+ return True, path
18
+ ok, which_out = run_cmd(["which", "sshd"])
19
+ if ok and which_out:
20
+ return True, which_out
21
+ return False, ""
22
+
23
+
24
+ def detect_package_manager() -> tuple[str, str]:
25
+ if Path("/usr/bin/apt").exists() or Path("/usr/bin/apt-get").exists():
26
+ return "apt", "sudo apt update && sudo apt install -y openssh-server"
27
+ if Path("/usr/bin/dnf").exists():
28
+ return "dnf", "sudo dnf install -y openssh-server"
29
+ if Path("/usr/bin/yum").exists():
30
+ return "yum", "sudo yum install -y openssh-server"
31
+ if Path("/usr/bin/pacman").exists():
32
+ return "pacman", "sudo pacman -S --noconfirm openssh"
33
+ if Path("/usr/bin/zypper").exists():
34
+ return "zypper", "sudo zypper install -y openssh"
35
+ return "unknown", "# Install openssh-server using your package manager"
@@ -9,40 +9,10 @@ from rich import box
9
9
  import subprocess
10
10
  import os
11
11
 
12
-
13
- console = Console()
14
-
15
-
16
- def _run_ps(cmd: str) -> tuple[bool, str]:
17
- result = subprocess.run(["powershell", "-Command", cmd], capture_output=True, text=True, check=False)
18
- return result.returncode == 0, result.stdout.strip()
12
+ from machineconfig.scripts.python.helpers.helpers_network.ssh.ssh_debug_windows_utils import check_sshd_binary_exists, detect_openssh, run_powershell
19
13
 
20
14
 
21
- def _check_sshd_binary_exists() -> tuple[bool, str]:
22
- sshd_locations = [
23
- Path("C:/Windows/System32/OpenSSH/sshd.exe"),
24
- Path("C:/Program Files/OpenSSH/sshd.exe"),
25
- Path("C:/Program Files (x86)/OpenSSH/sshd.exe"),
26
- ]
27
- for loc in sshd_locations:
28
- if loc.exists():
29
- return True, str(loc)
30
- ok, which_out = _run_ps("Get-Command sshd -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Source")
31
- if ok and which_out:
32
- return True, which_out
33
- return False, ""
34
-
35
-
36
- def _detect_openssh() -> tuple[str, Path | None, Path | None]:
37
- capability_sshd = Path("C:/Windows/System32/OpenSSH/sshd.exe")
38
- winget_sshd = Path("C:/Program Files/OpenSSH/sshd.exe")
39
- programdata_config = Path("C:/ProgramData/ssh")
40
- capability_config = Path("C:/ProgramData/ssh")
41
- if capability_sshd.exists():
42
- return ("capability", capability_sshd, capability_config)
43
- if winget_sshd.exists():
44
- return ("winget", winget_sshd, programdata_config)
45
- return ("not_found", None, None)
15
+ console = Console()
46
16
 
47
17
 
48
18
  def ssh_debug_windows() -> dict[str, dict[str, str | bool]]:
@@ -55,9 +25,9 @@ def ssh_debug_windows() -> dict[str, dict[str, str | bool]]:
55
25
  ssh_port = "22"
56
26
  ip_addresses: list[str] = []
57
27
 
58
- sshd_exists, sshd_path = _check_sshd_binary_exists()
59
- install_type, _sshd_exe, config_dir = _detect_openssh()
60
- ok, hostname = _run_ps("hostname")
28
+ sshd_exists, sshd_path = check_sshd_binary_exists()
29
+ install_type, _sshd_exe, config_dir = detect_openssh()
30
+ ok, hostname = run_powershell("hostname")
61
31
  hostname = hostname if ok else "unknown"
62
32
 
63
33
  install_info: list[str] = []
@@ -76,7 +46,7 @@ def ssh_debug_windows() -> dict[str, dict[str, str | bool]]:
76
46
  install_info.append(f" Binary: {sshd_path}")
77
47
  install_info.append(f" Config: {config_dir}")
78
48
 
79
- ok, status = _run_ps("Get-Service -Name sshd -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Status")
49
+ ok, status = run_powershell("Get-Service -Name sshd -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Status")
80
50
  if not ok or not status:
81
51
  results["ssh_service"] = {"status": "error", "message": "sshd service not found"}
82
52
  issues.append(("sshd service missing", "SSH daemon not installed", "Install OpenSSH Server first"))
@@ -87,7 +57,7 @@ def ssh_debug_windows() -> dict[str, dict[str, str | bool]]:
87
57
  install_info.append(f"❌ sshd service: [yellow]{status}[/yellow]")
88
58
  else:
89
59
  results["ssh_service"] = {"status": "ok", "message": "sshd running"}
90
- ok, startup = _run_ps("Get-Service -Name sshd | Select-Object -ExpandProperty StartType")
60
+ ok, startup = run_powershell("Get-Service -Name sshd | Select-Object -ExpandProperty StartType")
91
61
  startup_note = f" (startup: {startup})" if ok else ""
92
62
  install_info.append(f"✅ sshd service: [green]Running[/green]{startup_note}")
93
63
 
@@ -98,7 +68,7 @@ def ssh_debug_windows() -> dict[str, dict[str, str | bool]]:
98
68
  admin_auth_keys = Path("C:/ProgramData/ssh/administrators_authorized_keys")
99
69
  perm_info: list[str] = []
100
70
 
101
- ok, is_admin_str = _run_ps("([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)")
71
+ ok, is_admin_str = run_powershell("([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)")
102
72
  is_admin = "True" in is_admin_str
103
73
 
104
74
  if is_admin:
@@ -124,7 +94,7 @@ def ssh_debug_windows() -> dict[str, dict[str, str | bool]]:
124
94
  perm_info.append(f"\n⚠️ Could not read {target_auth_keys.name}: {e}")
125
95
 
126
96
  if is_admin and admin_auth_keys.exists():
127
- ok, icacls_out = _run_ps(f'icacls "{admin_auth_keys}"')
97
+ ok, icacls_out = run_powershell(f'icacls "{admin_auth_keys}"')
128
98
  if ok:
129
99
  needs_fix = "BUILTIN\\Users" in icacls_out or "Everyone" in icacls_out
130
100
  if needs_fix:
@@ -137,7 +107,7 @@ def ssh_debug_windows() -> dict[str, dict[str, str | bool]]:
137
107
  console.print(Panel("\n".join(perm_info), title="[bold]Keys & Permissions[/bold]", border_style="blue"))
138
108
 
139
109
  net_info: list[str] = []
140
- ok, ip_out = _run_ps("Get-NetIPAddress -AddressFamily IPv4 -PrefixOrigin Dhcp,Manual | Where-Object {$_.IPAddress -notlike '127.*' -and $_.IPAddress -notlike '169.254.*'} | Select-Object -ExpandProperty IPAddress")
110
+ ok, ip_out = run_powershell("Get-NetIPAddress -AddressFamily IPv4 -PrefixOrigin Dhcp,Manual | Where-Object {$_.IPAddress -notlike '127.*' -and $_.IPAddress -notlike '169.254.*'} | Select-Object -ExpandProperty IPAddress")
141
111
  if ok and ip_out:
142
112
  ip_addresses = [ip.strip() for ip in ip_out.split("\n") if ip.strip()]
143
113
  net_info.append(f"🌐 IP addresses: [cyan]{', '.join(ip_addresses)}[/cyan]")
@@ -148,7 +118,7 @@ def ssh_debug_windows() -> dict[str, dict[str, str | bool]]:
148
118
  try:
149
119
  config_text = sshd_config.read_text(encoding="utf-8")
150
120
  except PermissionError:
151
- ok, config_text_ps = _run_ps(f'Get-Content "{sshd_config}" -Raw')
121
+ ok, config_text_ps = run_powershell(f'Get-Content "{sshd_config}" -Raw')
152
122
  config_text = config_text_ps if ok and config_text_ps else None
153
123
  except Exception:
154
124
  config_text = None
@@ -219,7 +189,7 @@ def ssh_debug_windows() -> dict[str, dict[str, str | bool]]:
219
189
  $rules | Select-Object Name, DisplayName, Enabled, Action | Format-List
220
190
  }}
221
191
  """
222
- ok, fw_out = _run_ps(fw_cmd)
192
+ ok, fw_out = run_powershell(fw_cmd)
223
193
  if ok and fw_out.strip():
224
194
  has_allow = "Enabled : True" in fw_out and "Action : Allow" in fw_out
225
195
  if has_allow:
@@ -0,0 +1,34 @@
1
+ from pathlib import Path
2
+ import subprocess
3
+
4
+
5
+ def run_powershell(cmd: str) -> tuple[bool, str]:
6
+ result = subprocess.run(["powershell", "-Command", cmd], capture_output=True, text=True, check=False)
7
+ return result.returncode == 0, result.stdout.strip()
8
+
9
+
10
+ def check_sshd_binary_exists() -> tuple[bool, str]:
11
+ sshd_locations = [
12
+ Path("C:/Windows/System32/OpenSSH/sshd.exe"),
13
+ Path("C:/Program Files/OpenSSH/sshd.exe"),
14
+ Path("C:/Program Files (x86)/OpenSSH/sshd.exe"),
15
+ ]
16
+ for loc in sshd_locations:
17
+ if loc.exists():
18
+ return True, str(loc)
19
+ ok, which_out = run_powershell("Get-Command sshd -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Source")
20
+ if ok and which_out:
21
+ return True, which_out
22
+ return False, ""
23
+
24
+
25
+ def detect_openssh() -> tuple[str, Path | None, Path | None]:
26
+ capability_sshd = Path("C:/Windows/System32/OpenSSH/sshd.exe")
27
+ winget_sshd = Path("C:/Program Files/OpenSSH/sshd.exe")
28
+ programdata_config = Path("C:/ProgramData/ssh")
29
+ capability_config = Path("C:/ProgramData/ssh")
30
+ if capability_sshd.exists():
31
+ return ("capability", capability_sshd, capability_config)
32
+ if winget_sshd.exists():
33
+ return ("winget", winget_sshd, programdata_config)
34
+ return ("not_found", None, None)
@@ -20,8 +20,8 @@ def get_tmp_file():
20
20
 
21
21
 
22
22
  def main(
23
+ repo: Annotated[str, typer.Argument(..., help="Path to the local repository. Defaults to current working directory.")],
23
24
  cloud: Annotated[Optional[str], typer.Option(..., "--cloud", "-c", help="Cloud storage profile name. If not provided, uses default from config.")] = None,
24
- repo: Annotated[Optional[str], typer.Option(..., "--repo", "-r", help="Path to the local repository. Defaults to current working directory.")] = None,
25
25
  message: Annotated[Optional[str], typer.Option(..., "--message", "-m", help="Commit message for local changes.")] = None,
26
26
  on_conflict: Annotated[Literal["ask", "a",
27
27
  "push-local-merge", "p",
@@ -66,7 +66,6 @@ def main(
66
66
  if cloud is None:
67
67
  try:
68
68
  from machineconfig.utils.io import read_ini
69
-
70
69
  cloud_resolved = read_ini(DEFAULTS_PATH)["general"]["rclone_config_name"]
71
70
  console.print(Panel(f"⚠️ Using default cloud: `{cloud_resolved}` from {DEFAULTS_PATH}", title="Default Cloud", border_style="yellow"))
72
71
  except FileNotFoundError:
@@ -74,7 +73,7 @@ def main(
74
73
  return ""
75
74
  else:
76
75
  cloud_resolved = cloud
77
- repo_local_root = PathExtended.cwd() if repo is None else PathExtended(repo).expanduser().absolute()
76
+ repo_local_root = PathExtended.cwd() if repo == "." else PathExtended(repo).expanduser().absolute()
78
77
  try:
79
78
  repo_local_obj = git.Repo(repo_local_root, search_parent_directories=True)
80
79
  except git.InvalidGitRepositoryError:
@@ -1,23 +1,29 @@
1
- import re
1
+
2
2
  import subprocess
3
3
  from pathlib import Path
4
+ from machineconfig.settings.zellij import layouts
4
5
 
5
-
6
- _ANSI_ESCAPE_RE = re.compile(
7
- r"(?:\x1B|\u001B|\033)\[[0-?]*[ -/]*[@-~]|\[[0-9;?]+[ -/]*[@-~]|\[m"
8
- )
9
-
6
+ root = layouts.__path__[0]
7
+ STANDARD = Path(root).joinpath("st2.kdl")
8
+ # STANDARD = "st2.kdl"
9
+ # a = 1 + "sd"
10
10
 
11
11
  def strip_ansi_codes(text: str) -> str:
12
+ import re
13
+ _ANSI_ESCAPE_RE = re.compile(
14
+ r"(?:\x1B|\u001B|\033)\[[0-?]*[ -/]*[@-~]|\[[0-9;?]+[ -/]*[@-~]|\[m"
15
+ )
12
16
  return _ANSI_ESCAPE_RE.sub("", text)
13
17
 
14
18
 
15
19
  def choose_zellij_session(name: str | None, new_session: bool, kill_all: bool) -> tuple[str, str | None]:
16
20
  """Choose a Zellij session. Returns tuple of (action, script_to_run) where action is 'run_script', 'exit', or 'error'."""
21
+ # from machineoncif
22
+
17
23
  if name is not None:
18
24
  return ("run_script", f"zellij attach {name}")
19
25
  if new_session:
20
- cmd = "zellij --layout st2"
26
+ cmd = f"zellij --layout {STANDARD}"
21
27
  if kill_all:
22
28
  cmd = f"zellij kill-all-sessions --yes\n{cmd}"
23
29
  return ("run_script", cmd)
@@ -32,7 +38,7 @@ def choose_zellij_session(name: str | None, new_session: bool, kill_all: bool) -
32
38
  if "current" in sessions:
33
39
  return ("error", "Already in a Zellij session, avoiding nesting and exiting.")
34
40
  if len(sessions) == 0:
35
- return ("run_script", "zellij --layout st2")
41
+ return ("run_script", f"zellij --layout {STANDARD}")
36
42
  if len(sessions) == 1:
37
43
  sn = strip_ansi_codes(sessions[0])
38
44
  session_name = sn.split(" [Created")[0]
@@ -46,12 +52,12 @@ def choose_zellij_session(name: str | None, new_session: bool, kill_all: bool) -
46
52
  except Exception as e:
47
53
  return ("error", f"Error choosing Zellij session: {e}")
48
54
  if session_name == NEW_SESSION_LABEL:
49
- cmd = "zellij --layout st2"
55
+ cmd = f"zellij --layout {STANDARD}"
50
56
  if kill_all:
51
57
  cmd = f"zellij kill-all-sessions --yes\n{cmd}"
52
58
  return ("run_script", cmd)
53
59
  if session_name == KILL_ALL_AND_NEW_LABEL:
54
- return ("run_script", "zellij kill-all-sessions --yes\nzellij --layout st2")
60
+ return ("run_script", f"zellij kill-all-sessions --yes\nzellij --layout {STANDARD}")
55
61
  session_name_clean = strip_ansi_codes(session_name)
56
62
  session_name_clean = session_name_clean.split(" [Created")[0]
57
63
  return ("run_script", f"zellij attach {session_name_clean}")
@@ -79,18 +85,3 @@ def get_session_tabs() -> list[tuple[str, str]]:
79
85
  return result
80
86
 
81
87
 
82
- def start_wt(layout_name: str) -> tuple[str, str | None]:
83
- """Start a Windows Terminal layout by name. Returns tuple of (status, message) where status is 'success' or 'error'."""
84
- import json
85
- from machineconfig.utils.schemas.layouts.layout_types import LayoutsFile
86
- from machineconfig.cluster.sessions_managers.wt_local import run_wt_layout
87
- layouts_file = Path.home().joinpath("dotfiles/machineconfig/layouts.json")
88
- if not layouts_file.exists():
89
- return ("error", f"❌ Layouts file not found: {layouts_file}")
90
- layouts_data: LayoutsFile = json.loads(layouts_file.read_text(encoding="utf-8"))
91
- chosen_layout = next((a_layout for a_layout in layouts_data["layouts"] if a_layout["layoutName"] == layout_name), None)
92
- if not chosen_layout:
93
- available_layouts = [a_layout["layoutName"] for a_layout in layouts_data["layouts"]]
94
- return ("error", f"❌ Layout '{layout_name}' not found in layouts file.\nAvailable layouts: {', '.join(available_layouts)}")
95
- run_wt_layout(layout_config=chosen_layout)
96
- return ("success", None)