machineconfig 7.58__py3-none-any.whl → 7.59__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of machineconfig might be problematic. Click here for more details.

Files changed (54) hide show
  1. machineconfig/jobs/installer/custom/boxes.py +2 -2
  2. machineconfig/jobs/installer/custom/hx.py +3 -3
  3. machineconfig/jobs/installer/custom_dev/cloudflare_warp_cli.py +23 -0
  4. machineconfig/jobs/installer/custom_dev/dubdb_adbc.py +1 -1
  5. machineconfig/jobs/installer/custom_dev/nerfont_windows_helper.py +1 -1
  6. machineconfig/jobs/installer/custom_dev/sysabc.py +1 -20
  7. machineconfig/jobs/installer/custom_dev/wezterm.py +0 -4
  8. machineconfig/jobs/installer/installer_data.json +57 -23
  9. machineconfig/jobs/installer/package_groups.py +20 -13
  10. machineconfig/scripts/python/croshell.py +4 -4
  11. machineconfig/scripts/python/devops.py +1 -1
  12. machineconfig/scripts/python/env_manager/path_manager_tui.py +1 -1
  13. machineconfig/scripts/python/helpers_croshell/crosh.py +2 -2
  14. machineconfig/scripts/python/helpers_devops/cli_config_dotfile.py +4 -5
  15. machineconfig/scripts/python/helpers_devops/cli_self.py +3 -3
  16. machineconfig/scripts/python/helpers_devops/cli_share_file.py +2 -2
  17. machineconfig/scripts/python/helpers_devops/cli_share_server.py +1 -1
  18. machineconfig/scripts/python/helpers_devops/cli_terminal.py +1 -1
  19. machineconfig/scripts/python/helpers_devops/cli_utils.py +0 -72
  20. machineconfig/scripts/python/helpers_devops/devops_backup_retrieve.py +4 -4
  21. machineconfig/scripts/python/helpers_fire_command/file_wrangler.py +2 -3
  22. machineconfig/scripts/python/helpers_fire_command/fire_jobs_route_helper.py +3 -4
  23. machineconfig/scripts/python/helpers_repos/cloud_repo_sync.py +3 -2
  24. machineconfig/scripts/python/helpers_repos/count_lines_frontend.py +1 -1
  25. machineconfig/scripts/python/helpers_repos/entrypoint.py +2 -1
  26. machineconfig/scripts/python/helpers_repos/record.py +2 -1
  27. machineconfig/scripts/python/helpers_sessions/sessions_multiprocess.py +5 -5
  28. machineconfig/scripts/python/helpers_utils/download.py +151 -0
  29. machineconfig/scripts/python/helpers_utils/path.py +1 -1
  30. machineconfig/scripts/python/interactive.py +2 -2
  31. machineconfig/scripts/python/nw/ssh_debug_linux.py +7 -7
  32. machineconfig/scripts/python/nw/ssh_debug_windows.py +4 -4
  33. machineconfig/scripts/python/nw/wsl_windows_transfer.py +3 -2
  34. machineconfig/scripts/python/sessions.py +2 -3
  35. machineconfig/scripts/python/utils.py +2 -1
  36. machineconfig/scripts/windows/mounts/mount_ssh.ps1 +1 -1
  37. machineconfig/setup_linux/web_shortcuts/interactive.sh +10 -10
  38. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +10 -10
  39. machineconfig/utils/files/headers.py +2 -2
  40. machineconfig/utils/installer_utils/installer_class.py +38 -24
  41. machineconfig/utils/installer_utils/{installer.py → installer_cli.py} +29 -15
  42. machineconfig/utils/{installer.py → installer_utils/installer_runner.py} +1 -25
  43. machineconfig/utils/options.py +1 -1
  44. machineconfig/utils/path_extended.py +2 -2
  45. machineconfig/utils/path_helper.py +34 -31
  46. machineconfig/utils/ssh.py +1 -1
  47. {machineconfig-7.58.dist-info → machineconfig-7.59.dist-info}/METADATA +1 -1
  48. {machineconfig-7.58.dist-info → machineconfig-7.59.dist-info}/RECORD +53 -52
  49. machineconfig/jobs/installer/linux_scripts/pgsql.sh +0 -41
  50. /machineconfig/jobs/installer/linux_scripts/{warp-cli.sh → cloudflare_warp_cli.sh} +0 -0
  51. /machineconfig/utils/installer_utils/{installer_abc.py → installer_locator_utils.py} +0 -0
  52. {machineconfig-7.58.dist-info → machineconfig-7.59.dist-info}/WHEEL +0 -0
  53. {machineconfig-7.58.dist-info → machineconfig-7.59.dist-info}/entry_points.txt +0 -0
  54. {machineconfig-7.58.dist-info → machineconfig-7.59.dist-info}/top_level.txt +0 -0
@@ -5,11 +5,10 @@ from typing import Optional
5
5
  import tomllib
6
6
  from pathlib import Path
7
7
  from machineconfig.utils.accessories import randstr
8
- from machineconfig.utils.path_extended import PathExtended
9
8
  from machineconfig.utils.options import choose_from_options
10
9
 
11
10
 
12
- def choose_function_or_lines(choice_file: PathExtended, kwargs_dict: dict[str, object]) -> tuple[Optional[str], PathExtended, dict[str, object]]:
11
+ def choose_function_or_lines(choice_file: Path, kwargs_dict: dict[str, object]) -> tuple[Optional[str], Path, dict[str, object]]:
13
12
  """
14
13
  Choose a function to run from a Python file or lines from a shell script.
15
14
 
@@ -46,7 +45,7 @@ def choose_function_or_lines(choice_file: PathExtended, kwargs_dict: dict[str, o
46
45
  continue
47
46
  options.append(line)
48
47
  chosen_lines = choose_from_options(msg="Choose a line to run", options=options, fzf=True, multi=True)
49
- choice_file = PathExtended.tmpfile(suffix=".sh")
48
+ choice_file = Path.home().joinpath(f"tmp_results/tmp_scripts/shell/{randstr(10)}.sh")
50
49
  choice_file.parent.mkdir(parents=True, exist_ok=True)
51
50
  choice_file.write_text("\n".join(chosen_lines), encoding="utf-8")
52
51
  choice_function = None
@@ -82,7 +81,7 @@ def get_command_streamlit(choice_file: Path, environment: str, repo_root: Option
82
81
  port = config["server"]["port"]
83
82
  secrets_path = toml_path.with_name("secrets.toml")
84
83
  if repo_root is not None:
85
- secrets_template_path = Path.home().joinpath(f"dotfiles/creds/streamlit/{PathExtended(repo_root).name}/{choice_file.name}/secrets.toml")
84
+ secrets_template_path = Path.home().joinpath(f"dotfiles/creds/streamlit/{Path(repo_root).name}/{choice_file.name}/secrets.toml")
86
85
  if environment != "" and not secrets_path.exists() and secrets_template_path.exists():
87
86
  secrets_template = tomllib.loads(secrets_template_path.read_text(encoding="utf-8"))
88
87
  if environment == "ip":
@@ -61,8 +61,9 @@ def main(
61
61
  typer.Exit(code=1)
62
62
  return ""
63
63
  repo_local_root = PathExtended(repo_local_obj.working_dir) # cwd might have been in a sub directory of repo_root, so its better to redefine it.
64
+ local_relative_home = PathExtended(repo_local_root.expanduser().absolute().relative_to(Path.home()))
64
65
  PathExtended(CONFIG_ROOT).joinpath("remote").mkdir(parents=True, exist_ok=True)
65
- repo_remote_root = PathExtended(CONFIG_ROOT).joinpath("remote", repo_local_root.rel2home())
66
+ repo_remote_root = PathExtended(CONFIG_ROOT).joinpath("remote", local_relative_home)
66
67
  repo_remote_root.delete(sure=True)
67
68
  try:
68
69
  console.print(Panel("📥 DOWNLOADING REMOTE REPOSITORY", title_align="left", border_style="blue"))
@@ -104,7 +105,7 @@ git pull originEnc master
104
105
  uv_project_dir = f"""{str(Path.home().joinpath("code/machineconfig"))}"""
105
106
  uv_with = None
106
107
  else:
107
- uv_with = ["machineconfig>=7.58"]
108
+ uv_with = ["machineconfig>=7.59"]
108
109
  uv_project_dir = None
109
110
 
110
111
  import tempfile
@@ -8,7 +8,7 @@ def analyze_repo_development(repo_path: Annotated[str, typer.Argument(..., help=
8
8
  from pathlib import Path
9
9
  count_lines_path = Path(count_lines.__file__)
10
10
  # --project $HOME/code/ machineconfig --group plot
11
- cmd = f"""uv run --python 3.14 --with "machineconfig[plot]>=7.58" {count_lines_path} analyze-over-time {repo_path}"""
11
+ cmd = f"""uv run --python 3.14 --with "machineconfig[plot]>=7.59" {count_lines_path} analyze-over-time {repo_path}"""
12
12
  from machineconfig.utils.code import run_shell_script
13
13
  run_shell_script(cmd)
14
14
 
@@ -38,7 +38,8 @@ def resolve_spec_path(directory: Optional[str], cloud: Optional[str]) -> Path:
38
38
  repos_root = resolve_directory(directory)
39
39
  from machineconfig.utils.path_extended import PathExtended
40
40
  if not repos_root.exists() or repos_root.name != "repos.json":
41
- candidate = Path(CONFIG_ROOT).joinpath("repos").joinpath(PathExtended(repos_root).rel2home()).joinpath("repos.json")
41
+ relative_repos_root = PathExtended(repos_root).expanduser().absolute().relative_to(Path.home())
42
+ candidate = Path(CONFIG_ROOT).joinpath("repos").joinpath(relative_repos_root).joinpath("repos.json")
42
43
  repos_root = candidate
43
44
  if not repos_root.exists():
44
45
  cloud_name: Optional[str]
@@ -242,7 +242,8 @@ def main_record(repos_root: Path):
242
242
  tree_structure = build_tree_structure(repo_records, repos_root)
243
243
  print(tree_structure)
244
244
 
245
- save_path = CONFIG_ROOT.joinpath("repos").joinpath(repos_root.rel2home()).joinpath("repos.json")
245
+ relative_repos_root = PathExtended(repos_root).expanduser().absolute().relative_to(Path.home())
246
+ save_path = CONFIG_ROOT.joinpath("repos").joinpath(relative_repos_root).joinpath("repos.json")
246
247
  save_json(obj=res, path=save_path, indent=4)
247
248
  pprint(f"📁 Result saved at {PathExtended(save_path)}")
248
249
  print(">>>>>>>>> Finished Recording")
@@ -1,7 +1,6 @@
1
1
 
2
2
 
3
3
  from typing import Optional, Annotated
4
- from pathlib import Path
5
4
  import typer
6
5
 
7
6
 
@@ -13,20 +12,21 @@ def create_from_function(
13
12
  from machineconfig.utils.ve import get_ve_activate_line, get_ve_path_and_ipython_profile
14
13
  from machineconfig.utils.options import choose_from_options
15
14
  from machineconfig.utils.path_helper import match_file_name, sanitize_path
16
- from machineconfig.utils.path_extended import PathExtended
17
15
  from machineconfig.utils.accessories import get_repo_root
16
+ from pathlib import Path
17
+
18
18
 
19
19
  path_obj = sanitize_path(path)
20
20
  if not path_obj.exists():
21
21
  suffixes = {".py"}
22
- choice_file = match_file_name(sub_string=path, search_root=PathExtended.cwd(), suffixes=suffixes)
22
+ choice_file = match_file_name(sub_string=path, search_root=Path.cwd(), suffixes=suffixes)
23
23
  elif path_obj.is_dir():
24
24
  from machineconfig.utils.path_helper import search_for_files_of_interest
25
25
  print(f"🔍 Searching recursively for Python, PowerShell and Shell scripts in directory `{path_obj}`")
26
26
  files = search_for_files_of_interest(path_obj, suffixes={".py", ".sh", ".ps1"})
27
27
  print(f"🔍 Got #{len(files)} results.")
28
28
  choice_file = choose_from_options(multi=False, options=files, fzf=True, msg="Choose one option")
29
- choice_file = PathExtended(choice_file)
29
+ choice_file = Path(choice_file)
30
30
  else:
31
31
  choice_file = path_obj
32
32
 
@@ -52,7 +52,7 @@ def create_from_function(
52
52
  from machineconfig.utils.schemas.layouts.layout_types import LayoutConfig
53
53
  layout: LayoutConfig = {"layoutName": "fireNprocess", "layoutTabs": []}
54
54
  for an_arg in range(num_process):
55
- layout["layoutTabs"].append({"tabName": f"tab{an_arg}", "startDir": str(PathExtended.cwd()), "command": f"uv run -m fire {choice_file} {choice_function} --idx={an_arg} --idx_max={num_process}"})
55
+ layout["layoutTabs"].append({"tabName": f"tab{an_arg}", "startDir": str(Path.cwd()), "command": f"uv run -m fire {choice_file} {choice_function} --idx={an_arg} --idx_max={num_process}"})
56
56
  print(layout)
57
57
  run_zellij_layout(layout_config=layout)
58
58
 
@@ -0,0 +1,151 @@
1
+
2
+
3
+ from typing import Annotated, Optional
4
+ import typer
5
+
6
+
7
+ def download(
8
+ url: Annotated[Optional[str], typer.Argument(..., help="The URL to download the file from.")] = None,
9
+ decompress: Annotated[bool, typer.Option(..., "--decompress", "-d", help="Decompress the file if it's an archive.")] = False,
10
+ output: Annotated[Optional[str], typer.Option("--output", "-o", help="The output file path.")] = None,
11
+ output_dir: Annotated[Optional[str], typer.Option("--output-dir", help="Directory to place the downloaded file in.")] = None,
12
+ ) -> Optional["Path"]:
13
+
14
+ import subprocess
15
+ from pathlib import Path
16
+ from urllib.parse import parse_qs, unquote, urlparse
17
+ from requests import Response
18
+ import requests
19
+
20
+ if url is None:
21
+ typer.echo("❌ Error: URL is required.", err=True)
22
+ return None
23
+ if output is not None and output_dir is not None:
24
+ typer.echo("❌ Error: --output and --output-dir cannot be used together.", err=True)
25
+ return None
26
+ typer.echo(f"📥 Downloading from: {url}")
27
+
28
+ def _sanitize_candidate_filename(name: str) -> Optional[str]:
29
+ candidate = Path(name).name.strip()
30
+ if not candidate or candidate in {".", ".."}:
31
+ return None
32
+ return candidate
33
+
34
+ def _filename_from_content_disposition(header_value: Optional[str]) -> Optional[str]:
35
+ if header_value is None:
36
+ return None
37
+ parts = [segment.strip() for segment in header_value.split(";")]
38
+ for part in parts:
39
+ lower = part.lower()
40
+ if lower.startswith("filename*="):
41
+ value = part.split("=", 1)[1]
42
+ value = value.strip().strip('"')
43
+ if "''" in value:
44
+ value = value.split("''", 1)[1]
45
+ decoded = unquote(value)
46
+ sanitized = _sanitize_candidate_filename(decoded)
47
+ if sanitized is not None:
48
+ return sanitized
49
+ if lower.startswith("filename="):
50
+ value = part.split("=", 1)[1].strip().strip('"')
51
+ decoded = unquote(value)
52
+ sanitized = _sanitize_candidate_filename(decoded)
53
+ if sanitized is not None:
54
+ return sanitized
55
+ return None
56
+
57
+ def _filename_from_url(source_url: str) -> Optional[str]:
58
+ parsed = urlparse(source_url)
59
+ url_candidate = _sanitize_candidate_filename(unquote(Path(parsed.path).name))
60
+ if url_candidate is not None:
61
+ return url_candidate
62
+ query_params = parse_qs(parsed.query, keep_blank_values=True)
63
+ for key, values in query_params.items():
64
+ lower_key = key.lower()
65
+ if "name" in lower_key or "file" in lower_key:
66
+ for value in values:
67
+ sanitized = _sanitize_candidate_filename(unquote(value))
68
+ if sanitized is not None:
69
+ return sanitized
70
+ return None
71
+
72
+ def _resolve_download_path(request_url: str, response: Response, requested_output: Optional[str], requested_output_dir: Optional[str]) -> Path:
73
+ if requested_output is not None:
74
+ return Path(requested_output)
75
+ header_candidate = _filename_from_content_disposition(response.headers.get("content-disposition"))
76
+ if header_candidate is None:
77
+ header_candidate = _filename_from_url(response.url)
78
+ if header_candidate is None:
79
+ header_candidate = _filename_from_url(request_url)
80
+ if header_candidate is None:
81
+ header_candidate = "downloaded_file"
82
+ if requested_output_dir is not None:
83
+ return Path(requested_output_dir) / header_candidate
84
+ return Path(header_candidate)
85
+
86
+ try:
87
+ with requests.get(url, allow_redirects=True, stream=True, timeout=60) as response:
88
+ response.raise_for_status()
89
+ download_path = _resolve_download_path(url, response, output, output_dir)
90
+ download_path.parent.mkdir(parents=True, exist_ok=True)
91
+ total_size_header = response.headers.get("content-length", "0")
92
+ try:
93
+ total_size = int(total_size_header)
94
+ except (TypeError, ValueError):
95
+ total_size = 0
96
+ if total_size <= 0:
97
+ with open(download_path, "wb") as file_handle:
98
+ file_handle.write(response.content)
99
+ else:
100
+ downloaded = 0
101
+ chunk_size = 8192 * 4
102
+ with open(download_path, "wb") as file_handle:
103
+ for chunk in response.iter_content(chunk_size=chunk_size):
104
+ if not chunk:
105
+ continue
106
+ file_handle.write(chunk)
107
+ downloaded += len(chunk)
108
+ progress = (downloaded / total_size) * 100
109
+ typer.echo(f"\r⏬ Progress: {progress:.1f}% ({downloaded}/{total_size} bytes)", nl=False)
110
+ typer.echo()
111
+ except requests.exceptions.RequestException as exception:
112
+ typer.echo(f"❌ Download failed: {exception}", err=True)
113
+ return None
114
+ except OSError as exception:
115
+ typer.echo(f"❌ File write error: {exception}", err=True)
116
+ return None
117
+
118
+ typer.echo(f"✅ Downloaded to: {download_path}")
119
+ result_path: Path = download_path
120
+
121
+ if decompress:
122
+ typer.echo(f"📦 Decompressing: {download_path}")
123
+ base_name = download_path.stem
124
+ if base_name in {"", ".", ".."}:
125
+ base_name = "extracted"
126
+ extract_dir = download_path.parent / base_name
127
+ extract_dir.mkdir(parents=True, exist_ok=True)
128
+ try:
129
+ subprocess.run(
130
+ ["ouch", "decompress", str(download_path), "--dir", str(extract_dir)],
131
+ check=True,
132
+ capture_output=True,
133
+ text=True,
134
+ )
135
+ typer.echo(f"✅ Decompressed to: {extract_dir}")
136
+ if download_path.exists():
137
+ download_path.unlink()
138
+ typer.echo(f"🗑️ Removed archive: {download_path}")
139
+ result_path = extract_dir
140
+ except subprocess.CalledProcessError as exception:
141
+ typer.echo(f"❌ Decompression failed: {exception.stderr}", err=True)
142
+ return None
143
+ except FileNotFoundError:
144
+ typer.echo("❌ Error: ouch command not found. Please install ouch.", err=True)
145
+ typer.echo("💡 Install with: cargo install ouch", err=True)
146
+ return None
147
+
148
+ return result_path.resolve()
149
+
150
+ if __name__ == "__main__":
151
+ from pathlib import Path
@@ -17,7 +17,7 @@ def path():
17
17
  uv_with = ["textual"]
18
18
  uv_project_dir = None
19
19
  if not Path.home().joinpath("code/machineconfig").exists():
20
- uv_with.append("machineconfig>=7.58")
20
+ uv_with.append("machineconfig>=7.59")
21
21
  else:
22
22
  uv_project_dir = str(Path.home().joinpath("code/machineconfig"))
23
23
  run_shell_script(get_uv_command_executing_python_script(python_script=path.read_text(encoding="utf-8"), uv_with=uv_with, uv_project_dir=uv_project_dir)[0])
@@ -31,7 +31,7 @@ console = Console()
31
31
 
32
32
 
33
33
  def display_header() -> None:
34
- from machineconfig.utils.installer import get_machineconfig_version
34
+ from machineconfig.utils.installer_utils.installer_runner import get_machineconfig_version
35
35
  from rich.align import Align
36
36
 
37
37
  # Fancy ASCII art header
@@ -111,7 +111,7 @@ def execute_installations(selected_options: list[str]) -> None:
111
111
  console.print(Panel("⚡ [bold bright_yellow]CLI APPLICATIONS[/bold bright_yellow]\n[italic]Command-line tools installation[/italic]", border_style="bright_yellow"))
112
112
  console.print("🔧 Installing CLI applications", style="bold cyan")
113
113
  try:
114
- from machineconfig.utils.installer_utils.installer import main as devops_devapps_install_main
114
+ from machineconfig.utils.installer_utils.installer_cli import main as devops_devapps_install_main
115
115
  devops_devapps_install_main(group=True, which=maybe_a_group, interactive=False)
116
116
  console.print("✅ CLI applications installed successfully", style="bold green")
117
117
  except Exception as e:
@@ -1,7 +1,7 @@
1
1
 
2
2
 
3
3
  from platform import system
4
- from machineconfig.utils.path_extended import PathExtended
4
+ from pathlib import Path
5
5
  from rich.console import Console
6
6
  from rich.panel import Panel
7
7
  from rich import box
@@ -26,7 +26,7 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
26
26
  results: dict[str, dict[str, str | bool]] = {}
27
27
  issues_found: list[str] = []
28
28
 
29
- ssh_dir = PathExtended.home().joinpath(".ssh")
29
+ ssh_dir = Path.home().joinpath(".ssh")
30
30
  authorized_keys = ssh_dir.joinpath("authorized_keys")
31
31
 
32
32
  console.print(Panel("🔐 Checking SSH directory and authorized_keys...", title="[bold blue]File Permissions[/bold blue]", border_style="blue"))
@@ -106,7 +106,7 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
106
106
 
107
107
  console.print(Panel("🔌 Checking SSH port and listening status...", title="[bold blue]Network Status[/bold blue]", border_style="blue"))
108
108
 
109
- sshd_config_paths = [PathExtended("/etc/ssh/sshd_config"), PathExtended("/etc/sshd_config")]
109
+ sshd_config_paths = [Path("/etc/ssh/sshd_config"), Path("/etc/sshd_config")]
110
110
  sshd_config = None
111
111
  for config_path in sshd_config_paths:
112
112
  if config_path.exists():
@@ -236,7 +236,7 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
236
236
 
237
237
  console.print(Panel("🗂️ Checking for problematic files in /etc/...", title="[bold blue]System Files[/bold blue]", border_style="blue"))
238
238
 
239
- hosts_deny = PathExtended("/etc/hosts.deny")
239
+ hosts_deny = Path("/etc/hosts.deny")
240
240
  if hosts_deny.exists():
241
241
  hosts_deny_content = hosts_deny.read_text(encoding="utf-8")
242
242
  active_lines = [line.strip() for line in hosts_deny_content.splitlines() if line.strip() and not line.strip().startswith("#")]
@@ -252,14 +252,14 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
252
252
  results["hosts_deny"] = {"status": "ok", "message": "/etc/hosts.deny does not exist", "action": ""}
253
253
  console.print(Panel("✅ /etc/hosts.deny not present", title="[bold green]OK[/bold green]", border_style="green"))
254
254
 
255
- hosts_allow = PathExtended("/etc/hosts.allow")
255
+ hosts_allow = Path("/etc/hosts.allow")
256
256
  if hosts_allow.exists():
257
257
  results["hosts_allow"] = {"status": "ok", "message": "/etc/hosts.allow exists (check if needed)", "action": ""}
258
258
  console.print(Panel("ℹ️ /etc/hosts.allow exists\n💡 Ensure it allows SSH if using TCP wrappers", title="[bold blue]Info[/bold blue]", border_style="blue"))
259
259
 
260
260
  console.print(Panel("👤 Checking home directory permissions...", title="[bold blue]User Permissions[/bold blue]", border_style="blue"))
261
261
 
262
- home_dir = PathExtended.home()
262
+ home_dir = Path.home()
263
263
  home_stat = os.stat(home_dir)
264
264
  home_perms = oct(home_stat.st_mode)[-3:]
265
265
 
@@ -294,7 +294,7 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
294
294
 
295
295
  console.print(Panel("📋 Checking SSH logs for errors...", title="[bold blue]Logs[/bold blue]", border_style="blue"))
296
296
 
297
- log_files = [PathExtended("/var/log/auth.log"), PathExtended("/var/log/secure")]
297
+ log_files = [Path("/var/log/auth.log"), Path("/var/log/secure")]
298
298
  log_found = False
299
299
  for log_file in log_files:
300
300
  if log_file.exists():
@@ -1,7 +1,7 @@
1
1
 
2
2
 
3
3
  from platform import system
4
- from machineconfig.utils.path_extended import PathExtended
4
+ from pathlib import Path
5
5
  from rich.console import Console
6
6
  from rich.panel import Panel
7
7
  from rich import box
@@ -26,7 +26,7 @@ def ssh_debug_windows() -> dict[str, dict[str, str | bool]]:
26
26
  results: dict[str, dict[str, str | bool]] = {}
27
27
  issues_found: list[str] = []
28
28
 
29
- ssh_dir = PathExtended.home().joinpath(".ssh")
29
+ ssh_dir = Path.home().joinpath(".ssh")
30
30
  authorized_keys = ssh_dir.joinpath("authorized_keys")
31
31
 
32
32
  console.print(Panel("🔐 Checking SSH directory and authorized_keys...", title="[bold blue]File Permissions[/bold blue]", border_style="blue"))
@@ -124,7 +124,7 @@ def ssh_debug_windows() -> dict[str, dict[str, str | bool]]:
124
124
 
125
125
  console.print(Panel("🔌 Checking SSH port and listening status...", title="[bold blue]Network Status[/bold blue]", border_style="blue"))
126
126
 
127
- sshd_config_paths = [PathExtended("C:\\ProgramData\\ssh\\sshd_config"), PathExtended(os.environ.get("PROGRAMDATA", "C:\\ProgramData")).joinpath("ssh", "sshd_config")]
127
+ sshd_config_paths = [Path("C:\\ProgramData\\ssh\\sshd_config"), Path(os.environ.get("PROGRAMDATA", "C:\\ProgramData")).joinpath("ssh", "sshd_config")]
128
128
  sshd_config = None
129
129
  for config_path in sshd_config_paths:
130
130
  if config_path.exists():
@@ -168,7 +168,7 @@ def ssh_debug_windows() -> dict[str, dict[str, str | bool]]:
168
168
  if admin_authorized_keys_lines:
169
169
  console.print(Panel("⚠️ IMPORTANT: Administrators group uses different authorized_keys location\n💡 For admin users, keys should be in: C:\\ProgramData\\ssh\\administrators_authorized_keys\n💡 Not in user's .ssh/authorized_keys!", title="[bold yellow]Admin Users[/bold yellow]", border_style="yellow"))
170
170
 
171
- programdata_auth_keys = PathExtended(os.environ.get("PROGRAMDATA", "C:\\ProgramData")).joinpath("ssh", "administrators_authorized_keys")
171
+ programdata_auth_keys = Path(os.environ.get("PROGRAMDATA", "C:\\ProgramData")).joinpath("ssh", "administrators_authorized_keys")
172
172
  if programdata_auth_keys.exists():
173
173
  console.print(Panel("✅ administrators_authorized_keys file exists", title="[bold green]OK[/bold green]", border_style="green"))
174
174
  else:
@@ -35,15 +35,16 @@ def main(
35
35
  print("=" * 50 + "\n")
36
36
 
37
37
  path_obj = PathExtended(path).expanduser().absolute()
38
+ relative_home = PathExtended(path_obj.expanduser().absolute().relative_to(Path.home()))
38
39
 
39
40
  if same_file_system:
40
41
  print("⚠️ Using a not recommended transfer method! Copying files across the same file system.")
41
42
  if system == "Windows": # move files over to WSL
42
43
  print("📤 Transferring files from Windows to WSL...")
43
- path_obj.copy(folder=WSL_FROM_WIN.joinpath(UserName).joinpath(path_obj.rel2home().parent), overwrite=True) # the following works for files and folders alike.
44
+ path_obj.copy(folder=WSL_FROM_WIN.joinpath(UserName).joinpath(relative_home.parent), overwrite=True) # the following works for files and folders alike.
44
45
  else: # move files from WSL to win
45
46
  print("📤 Transferring files from WSL to Windows...")
46
- path_obj.copy(folder=WIN_FROM_WSL.joinpath(UserName).joinpath(path_obj.rel2home().parent), overwrite=True)
47
+ path_obj.copy(folder=WIN_FROM_WSL.joinpath(UserName).joinpath(relative_home.parent), overwrite=True)
47
48
  print("✅ Transfer completed successfully!\n")
48
49
  else:
49
50
  from machineconfig.utils.ssh import SSH
@@ -50,19 +50,18 @@ def select_layout(layouts_json_file: Path, selected_layouts_names: Optional[list
50
50
 
51
51
 
52
52
  def find_layout_file(layout_path: str, ) -> Path:
53
- from machineconfig.utils.path_extended import PathExtended
54
53
  from machineconfig.utils.path_helper import search_for_files_of_interest
55
54
  from machineconfig.utils.options import choose_from_options
56
55
  from machineconfig.utils.path_helper import match_file_name, sanitize_path
57
56
  path_obj = sanitize_path(layout_path)
58
57
  if not path_obj.exists():
59
- choice_file = match_file_name(sub_string=layout_path, search_root=PathExtended.cwd(), suffixes={".json"})
58
+ choice_file = match_file_name(sub_string=layout_path, search_root=Path.cwd(), suffixes={".json"})
60
59
  elif path_obj.is_dir():
61
60
  print(f"🔍 Searching recursively for Python, PowerShell and Shell scripts in directory `{path_obj}`")
62
61
  files = search_for_files_of_interest(path_obj, suffixes={".py", ".sh", ".ps1"})
63
62
  print(f"🔍 Got #{len(files)} results.")
64
63
  choice_file = choose_from_options(multi=False, options=files, fzf=True, msg="Choose one option")
65
- choice_file = PathExtended(choice_file)
64
+ choice_file = Path(choice_file).expanduser().absolute()
66
65
  else:
67
66
  choice_file = path_obj
68
67
  return choice_file
@@ -1,7 +1,8 @@
1
1
 
2
2
 
3
- from machineconfig.scripts.python.helpers_devops.cli_utils import download, merge_pdfs, compress_pdf
3
+ from machineconfig.scripts.python.helpers_devops.cli_utils import merge_pdfs, compress_pdf
4
4
  from machineconfig.scripts.python.helpers_utils.path import edit_file_with_hx, get_machine_specs, init_project, path
5
+ from machineconfig.scripts.python.helpers_utils.download import download
5
6
  import typer
6
7
  from typing import Annotated
7
8
 
@@ -7,7 +7,7 @@ $user = ''
7
7
  $sharePath = ''
8
8
  $driveLetter = ''
9
9
 
10
- uv run --python 3.14 --with "machineconfig>=7.58" python -m machineconfig.scripts.python.mount_ssh
10
+ uv run --python 3.14 --with "machineconfig>=7.59" python -m machineconfig.scripts.python.mount_ssh
11
11
 
12
12
  net use T: \\sshfs.kr\$user@$host.local
13
13
  # this worked: net use T: \\sshfs\alex@alex-p51s-5.local
@@ -2,16 +2,16 @@
2
2
  . <( curl -sSL "https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/setup_linux/uv.sh")
3
3
  . <( curl -sSL "https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/scripts/linux/wrap_mcfg")
4
4
 
5
- alias devops='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.58" devops'
6
- alias cloud='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.58" cloud'
7
- alias agents='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.58" agents'
8
- alias sessions='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.58" sessions'
9
- alias ftpx='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.58" ftpx'
10
- alias fire='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.58" fire'
11
- alias croshell='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.58" croshell'
12
- alias utils='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.58" utils'
13
- alias terminal='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.58" terminal'
14
- alias msearch='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.58" msearch'
5
+ alias devops='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.59" devops'
6
+ alias cloud='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.59" cloud'
7
+ alias agents='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.59" agents'
8
+ alias sessions='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.59" sessions'
9
+ alias ftpx='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.59" ftpx'
10
+ alias fire='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.59" fire'
11
+ alias croshell='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.59" croshell'
12
+ alias utils='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.59" utils'
13
+ alias terminal='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.59" terminal'
14
+ alias msearch='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.59" msearch'
15
15
 
16
16
  alias d='wrap_in_shell_script devops'
17
17
  alias c='wrap_in_shell_script cloud'
@@ -3,16 +3,16 @@
3
3
  iex (iwr "https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/setup_windows/uv.ps1").Content
4
4
  iex (iwr "https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/scripts/windows/wrap_mcfg.ps1").Content
5
5
 
6
- function devops { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.58" devops $args }
7
- function cloud { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.58" cloud $args }
8
- function agents { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.58" agents $args }
9
- function sessions { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.58" sessions $args }
10
- function ftpx { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.58" ftpx $args }
11
- function fire { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.58" fire $args }
12
- function croshell { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.58" croshell $args }
13
- function utils { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.58" utils $args }
14
- function terminal { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.58" terminal $args }
15
- function msearch { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.58" msearch $args }
6
+ function devops { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.59" devops $args }
7
+ function cloud { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.59" cloud $args }
8
+ function agents { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.59" agents $args }
9
+ function sessions { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.59" sessions $args }
10
+ function ftpx { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.59" ftpx $args }
11
+ function fire { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.59" fire $args }
12
+ function croshell { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.59" croshell $args }
13
+ function utils { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.59" utils $args }
14
+ function terminal { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.59" terminal $args }
15
+ function msearch { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.59" msearch $args }
16
16
 
17
17
  function d { wrap_in_shell_script devops @args }
18
18
  function c { wrap_in_shell_script cloud @args }
@@ -25,7 +25,7 @@ def print_header():
25
25
  table.add_row("Virtual Environment", os.getenv('VIRTUAL_ENV', 'None'))
26
26
  table.add_row("Running @", str(Path.cwd()))
27
27
 
28
- from machineconfig.utils.installer import get_machineconfig_version
28
+ from machineconfig.utils.installer_utils.installer_runner import get_machineconfig_version
29
29
 
30
30
  console.print(Panel(table, title=f"[bold blue]✨ 🐊 Machineconfig Shell {get_machineconfig_version()} ✨ Made with 🐍 | Built with ❤️[/bold blue]", border_style="blue"))
31
31
  def print_logo(logo: str):
@@ -44,7 +44,7 @@ def print_logo(logo: str):
44
44
  _default_art = Path(random.choice(glob.glob(str(Path(__file__).parent.joinpath("art", "*")))))
45
45
  print(_default_art.read_text())
46
46
  elif platform.system() in ["Linux", "Darwin"]: # Explicitly handle both Linux and macOS
47
- from machineconfig.utils.installer_utils.installer_abc import is_executable_in_path
47
+ from machineconfig.utils.installer_utils.installer_locator_utils import is_executable_in_path
48
48
  avail_cowsay = is_executable_in_path("cowsay")
49
49
  avail_lolcat = is_executable_in_path("lolcat")
50
50
  avail_boxes = is_executable_in_path("boxes")