machineconfig 7.64__py3-none-any.whl → 7.79__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 (78) hide show
  1. machineconfig/cluster/sessions_managers/utils/maker.py +4 -2
  2. machineconfig/jobs/installer/custom_dev/sysabc.py +26 -0
  3. machineconfig/jobs/installer/installer_data.json +70 -2
  4. machineconfig/profile/create_links_export.py +2 -2
  5. machineconfig/scripts/__init__.py +0 -4
  6. machineconfig/scripts/python/agents.py +22 -17
  7. machineconfig/scripts/python/ai/solutions/copilot/instructions/python/dev.instructions.md +3 -0
  8. machineconfig/scripts/python/croshell.py +22 -17
  9. machineconfig/scripts/python/devops.py +1 -1
  10. machineconfig/scripts/python/devops_navigator.py +0 -4
  11. machineconfig/scripts/python/env_manager/path_manager_tui.py +1 -1
  12. machineconfig/scripts/python/fire_jobs.py +13 -13
  13. machineconfig/scripts/python/ftpx.py +36 -12
  14. machineconfig/scripts/python/helpers/ast_search.py +74 -0
  15. machineconfig/scripts/python/helpers/qr_code.py +166 -0
  16. machineconfig/scripts/python/helpers/repo_rag.py +325 -0
  17. machineconfig/scripts/python/helpers/symantic_search.py +25 -0
  18. machineconfig/scripts/python/helpers_cloud/cloud_copy.py +28 -21
  19. machineconfig/scripts/python/helpers_cloud/cloud_helpers.py +1 -1
  20. machineconfig/scripts/python/helpers_cloud/cloud_mount.py +19 -17
  21. machineconfig/scripts/python/helpers_cloud/cloud_sync.py +8 -7
  22. machineconfig/scripts/python/helpers_croshell/start_slidev.py +6 -7
  23. machineconfig/scripts/python/helpers_devops/cli_nw.py +88 -7
  24. machineconfig/scripts/python/helpers_devops/cli_self.py +7 -6
  25. machineconfig/scripts/python/helpers_devops/cli_share_file.py +7 -7
  26. machineconfig/scripts/python/helpers_devops/cli_share_server.py +12 -11
  27. machineconfig/scripts/python/helpers_devops/cli_terminal.py +6 -5
  28. machineconfig/scripts/python/helpers_devops/cli_utils.py +2 -1
  29. machineconfig/scripts/python/helpers_devops/devops_status.py +7 -19
  30. machineconfig/scripts/python/helpers_fire_command/fire_jobs_route_helper.py +20 -9
  31. machineconfig/scripts/python/helpers_navigator/command_tree.py +50 -18
  32. machineconfig/scripts/python/helpers_repos/cloud_repo_sync.py +5 -3
  33. machineconfig/scripts/python/helpers_repos/count_lines_frontend.py +1 -1
  34. machineconfig/scripts/python/helpers_utils/download.py +4 -3
  35. machineconfig/scripts/python/helpers_utils/path.py +81 -31
  36. machineconfig/scripts/python/interactive.py +1 -1
  37. machineconfig/scripts/python/{machineconfig.py → mcfg_entry.py} +4 -0
  38. machineconfig/scripts/python/msearch.py +21 -2
  39. machineconfig/scripts/python/nw/address.py +132 -0
  40. machineconfig/scripts/python/nw/devops_add_ssh_key.py +8 -5
  41. machineconfig/scripts/python/terminal.py +2 -2
  42. machineconfig/scripts/python/utils.py +10 -9
  43. machineconfig/scripts/windows/mounts/mount_ssh.ps1 +1 -1
  44. machineconfig/settings/lf/windows/lfcd.ps1 +1 -1
  45. machineconfig/settings/shells/nushell/config.nu +2 -2
  46. machineconfig/settings/shells/nushell/env.nu +45 -6
  47. machineconfig/settings/shells/nushell/init.nu +282 -95
  48. machineconfig/settings/shells/pwsh/init.ps1 +1 -0
  49. machineconfig/setup_linux/web_shortcuts/interactive.sh +10 -10
  50. machineconfig/setup_windows/uv.ps1 +8 -1
  51. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +10 -10
  52. machineconfig/setup_windows/web_shortcuts/quick_init.ps1 +3 -2
  53. machineconfig/utils/accessories.py +7 -4
  54. machineconfig/utils/code.py +4 -2
  55. machineconfig/utils/installer_utils/install_from_url.py +180 -0
  56. machineconfig/utils/installer_utils/installer_class.py +18 -10
  57. machineconfig/utils/installer_utils/installer_cli.py +14 -9
  58. machineconfig/utils/links.py +2 -2
  59. machineconfig/utils/meta.py +2 -2
  60. machineconfig/utils/options.py +3 -3
  61. machineconfig/utils/path_extended.py +1 -1
  62. machineconfig/utils/path_helper.py +0 -1
  63. machineconfig/utils/ssh.py +143 -409
  64. machineconfig/utils/ssh_utils/abc.py +8 -0
  65. machineconfig/utils/ssh_utils/copy_from_here.py +110 -0
  66. machineconfig/utils/ssh_utils/copy_to_here.py +302 -0
  67. machineconfig/utils/ssh_utils/utils.py +141 -0
  68. machineconfig/utils/ssh_utils/wsl.py +168 -0
  69. machineconfig/utils/upgrade_packages.py +2 -1
  70. machineconfig/utils/ve.py +11 -4
  71. {machineconfig-7.64.dist-info → machineconfig-7.79.dist-info}/METADATA +1 -1
  72. {machineconfig-7.64.dist-info → machineconfig-7.79.dist-info}/RECORD +77 -68
  73. {machineconfig-7.64.dist-info → machineconfig-7.79.dist-info}/entry_points.txt +2 -2
  74. machineconfig/scripts/python/explore.py +0 -49
  75. /machineconfig/{settings/shells/pwsh/profile.ps1 → scripts/python/helpers_fire_command/f.py} +0 -0
  76. /machineconfig/scripts/{Restore-ThunderbirdProfile.ps1 → windows/mounts/Restore-ThunderbirdProfile.ps1} +0 -0
  77. {machineconfig-7.64.dist-info → machineconfig-7.79.dist-info}/WHEEL +0 -0
  78. {machineconfig-7.64.dist-info → machineconfig-7.79.dist-info}/top_level.txt +0 -0
@@ -1,32 +1,27 @@
1
1
  """Cloud mount script"""
2
2
 
3
- from machineconfig.utils.options import choose_from_options
4
- from machineconfig.utils.io import read_ini
5
- from machineconfig.utils.path_extended import PathExtended
6
-
7
- import platform
8
- from typing import Optional, Annotated
9
3
  import typer
10
- from rich.console import Console
11
- from rich.panel import Panel
12
-
13
- console = Console()
14
-
15
-
16
- DEFAULT_MOUNT = "~/data/rclone"
4
+ from typing import Optional, Annotated
17
5
 
18
6
 
19
7
  def get_rclone_config():
8
+ from machineconfig.utils.io import read_ini
9
+ from pathlib import Path
10
+ import platform
20
11
  if platform.system() == "Windows":
21
- config = read_ini(PathExtended.home().joinpath("AppData/Roaming/rclone/rclone.conf"))
12
+ config = read_ini(Path.home().joinpath("AppData/Roaming/rclone/rclone.conf"))
22
13
  elif platform.system() in ["Linux", "Darwin"]:
23
- config = read_ini(PathExtended.home().joinpath(".config/rclone/rclone.conf"))
14
+ config = read_ini(Path.home().joinpath(".config/rclone/rclone.conf"))
24
15
  else:
25
16
  raise ValueError("unsupported platform")
26
17
  return config
27
18
 
28
19
 
29
20
  def get_mprocs_mount_txt(cloud: str, rclone_cmd: str, cloud_brand: str): # cloud_brand = config[cloud]["type"]
21
+ from machineconfig.utils.path_extended import PathExtended
22
+ import platform
23
+ DEFAULT_MOUNT = "~/data/rclone"
24
+
30
25
  header = f"{' ' + cloud + ' | ' + cloud_brand + ' '}".center(50, "=")
31
26
  if platform.system() == "Windows":
32
27
  sub_text_path = PathExtended.tmpfile(suffix=".ps1")
@@ -57,6 +52,13 @@ def mount(
57
52
  destination: Annotated[Optional[str], typer.Option(help="destination to mount")] = None,
58
53
  network: Annotated[Optional[str], typer.Option(help="mount network drive")] = None,
59
54
  ) -> None:
55
+ from machineconfig.utils.options import choose_from_options
56
+ from pathlib import Path
57
+ import platform
58
+ from rich.console import Console
59
+ from rich.panel import Panel
60
+ console = Console()
61
+ DEFAULT_MOUNT = "~/data/rclone"
60
62
 
61
63
  # draw header box dynamically
62
64
  title = "☁️ Cloud Mount Utility"
@@ -73,9 +75,9 @@ def mount(
73
75
 
74
76
  if network is None:
75
77
  if destination is None:
76
- mount_loc = PathExtended(DEFAULT_MOUNT).expanduser().joinpath(cloud)
78
+ mount_loc = Path(DEFAULT_MOUNT).expanduser().joinpath(cloud)
77
79
  else:
78
- mount_loc = PathExtended(destination)
80
+ mount_loc = Path(destination)
79
81
 
80
82
  mount_info = f"📂 Mount location: {mount_loc}"
81
83
  console.print(Panel(mount_info, border_style="blue"))
@@ -2,16 +2,9 @@
2
2
  TODO: use typer or typed-argument-parser to parse args
3
3
  """
4
4
 
5
- from machineconfig.scripts.python.helpers_cloud.helpers2 import parse_cloud_source_target
6
- from machineconfig.scripts.python.helpers_cloud.cloud_helpers import Args
7
- from machineconfig.scripts.python.helpers_cloud.cloud_mount import get_mprocs_mount_txt
8
5
 
9
6
  from typing import Annotated, Optional
10
7
  import typer
11
- from rich.console import Console
12
- from rich.panel import Panel
13
-
14
- console = Console()
15
8
 
16
9
 
17
10
  def main(
@@ -27,6 +20,14 @@ def main(
27
20
  delete: Annotated[bool, typer.Option("--delete", "-D", help="Delete files in remote that are not in local.")] = False,
28
21
  verbose: Annotated[bool, typer.Option("--verbose", "-v", help="Verbosity of mprocs to show details of syncing.")] = False,
29
22
  ) -> None:
23
+
24
+ from machineconfig.scripts.python.helpers_cloud.helpers2 import parse_cloud_source_target
25
+ from machineconfig.scripts.python.helpers_cloud.cloud_helpers import Args
26
+ from machineconfig.scripts.python.helpers_cloud.cloud_mount import get_mprocs_mount_txt
27
+ from rich.console import Console
28
+ from rich.panel import Panel
29
+ console = Console()
30
+
30
31
  title = "☁️ Cloud Sync Utility"
31
32
  console.print(Panel(title, title_align="left", border_style="blue"))
32
33
 
@@ -95,13 +95,12 @@ def main(
95
95
  if md_file.name != "slides.md":
96
96
  SLIDEV_REPO.joinpath(md_file.name).with_name(name="slides.md", inplace=True, overwrite=True)
97
97
 
98
- import socket
99
-
100
- try:
101
- local_ip_v4 = socket.gethostbyname(socket.gethostname() + ".local")
102
- except Exception:
103
- print("⚠️ Warning: Could not get local_ip_v4. This might be due to running in a WSL instance.")
104
- local_ip_v4 = socket.gethostbyname(socket.gethostname())
98
+ import machineconfig.scripts.python.nw.address as helper
99
+ res = helper.select_lan_ipv4(prefer_vpn=False)
100
+ if res is None:
101
+ print("❌ Error: Could not determine local LAN IPv4 address for presentation.")
102
+ raise typer.Exit(code=1)
103
+ local_ip_v4 = res
105
104
 
106
105
  print("🌐 Presentation will be served at:")
107
106
  print(f" - http://{platform.node()}:{port}")
@@ -35,12 +35,63 @@ def add_ssh_identity():
35
35
 
36
36
 
37
37
  def show_address():
38
- import socket
39
- s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
40
- s.connect(('8.8.8.8',80))
41
- local_ip_v4 = s.getsockname()[0]
42
- s.close()
43
- print(f"This computer is @ {local_ip_v4}")
38
+ """📌 Show this computer addresses on network"""
39
+ from machineconfig.utils.installer_utils.installer_cli import install_if_missing
40
+ from pathlib import Path
41
+ from machineconfig.utils.accessories import randstr
42
+ tmp_file = Path.home().joinpath("tmp_results/tmp_files/ipinfo_result_" + randstr(8) + ".json")
43
+ tmp_file.parent.mkdir(parents=True, exist_ok=True)
44
+ install_if_missing("ipinfo")
45
+ script = f"""
46
+ ipinfo myip --json > "{tmp_file.as_posix()}"
47
+ """
48
+ from machineconfig.utils.code import run_shell_script
49
+ run_shell_script(script=script)
50
+ import json
51
+ loaded_json = json.loads(tmp_file.read_text(encoding="utf-8"))
52
+ from rich import print_json
53
+ print_json(data=loaded_json)
54
+
55
+ import machineconfig.scripts.python.nw.address as helper
56
+ from rich.table import Table
57
+ from rich.console import Console
58
+ res = helper.get_all_ipv4_addresses()
59
+ res.append( ("Public IP", loaded_json.get("ip", "N/A")))
60
+
61
+ # loc = loaded_json["loc"]
62
+ # cmd = f"""curl "https://maps.geoapify.com/v1/staticmap?style=osm-bright&width=600&height=300&center=lonlat:{loc}&zoom=6&marker=lonlat:{loc};color:%23ff0000;size:medium&apiKey=$GEOAPIFY_API_KEY" -o map.png && chafa map.png"""
63
+ # from machineconfig.utils.code import run_shell_script
64
+ # run_shell_script(script=cmd)
65
+
66
+ table = Table(title="Network Interfaces")
67
+ table.add_column("Interface", style="cyan")
68
+ table.add_column("IP Address", style="green")
69
+
70
+ for iface, ip in res:
71
+ table.add_row(iface, ip)
72
+
73
+ console = Console()
74
+ console.print(table)
75
+
76
+ res = helper.select_lan_ipv4(prefer_vpn=False)
77
+ if res is not None:
78
+ # ip, iface = res
79
+ # print(f"Selected IP: {ip} on interface: {iface}")
80
+ print(f"LAN IPv4: {res}")
81
+ else:
82
+ print("No network interfaces found.")
83
+
84
+
85
+
86
+ def bind_wsl_port(port: Annotated[int, typer.Option(..., "--port", "-p", help="Port number to bind")]):
87
+ code = f"""
88
+
89
+ $wsl_ip = (wsl.exe hostname -I).Trim().Split(' ')[0]
90
+ netsh interface portproxy add v4tov4 listenport={port} listenaddress=0.0.0.0 connectport={port} connectaddress=$wsl_ip
91
+
92
+ """
93
+ from machineconfig.utils.code import exit_then_run_shell_script
94
+ exit_then_run_shell_script(code)
44
95
 
45
96
 
46
97
  def debug_ssh():
@@ -101,6 +152,28 @@ def wifi_select(
101
152
  console.print("[blue]👋 Goodbye![/blue]")
102
153
 
103
154
 
155
+
156
+ def reset_cloudflare_tunnel():
157
+ code = """
158
+ # cloudflared tunnel route dns glenn # creates CNAMES in Cloudflare dashboard
159
+ # sudo systemctl stop cloudflared
160
+ # test: cloudflared tunnel run glenn
161
+ home_dir=$HOME
162
+ cloudflared_path="$home_dir/.local/bin/cloudflared"
163
+ sudo $cloudflared_path service uninstall
164
+ sudo rm /etc/cloudflared/config.yml || true
165
+ sudo $cloudflared_path --config $home_dir/.cloudflared/config.yml service install
166
+ """
167
+ print(code)
168
+ def add_ip_exclusion_to_warp(ip: Annotated[str, typer.Option(..., "--ip", help="IP address to exclude from WARP")]):
169
+ code = f"""
170
+ sudo warp-cli tunnel ip add {ip}
171
+ sudo warp-cli disconnect
172
+ sudo warp-cli connect
173
+ """
174
+ print(code)
175
+
176
+
104
177
  def get_app():
105
178
  nw_apps = typer.Typer(help="🔐 [n] Network subcommands", no_args_is_help=True, add_help_option=False, add_completion=False)
106
179
  nw_apps.command(name="share-terminal", help="📡 [t] Share terminal via web browser")(cli_terminal.main)
@@ -128,7 +201,15 @@ def get_app():
128
201
  nw_apps.command(name="debug-ssh", help="🐛 [d] Debug SSH connection")(debug_ssh)
129
202
  nw_apps.command(name="d", help="Debug SSH connection", hidden=True)(debug_ssh)
130
203
 
131
- nw_apps.command(name="wifi-select", no_args_is_help=True, help="📶 WiFi connection utility.")(wifi_select)
204
+ nw_apps.command(name="wifi-select", no_args_is_help=True, help="📶 [w] WiFi connection utility.")(wifi_select)
132
205
  nw_apps.command(name="w", no_args_is_help=True, hidden=True)(wifi_select)
133
206
 
207
+ nw_apps.command(name="bind-wsl-port", help="🔌 [b] Bind WSL port to Windows host", no_args_is_help=True)(bind_wsl_port)
208
+ nw_apps.command(name="b", help="Bind WSL port to Windows host", hidden=True, no_args_is_help=True)(bind_wsl_port)
209
+
210
+ nw_apps.command(name="reset-cloudflare-tunnel", help="☁️ [r] Reset Cloudflare tunnel service")(reset_cloudflare_tunnel)
211
+ nw_apps.command(name="r", help="Reset Cloudflare tunnel service", hidden=True)(reset_cloudflare_tunnel)
212
+ nw_apps.command(name="add-ip-exclusion-to-warp", help="🚫 [p] Add IP exclusion to WARP")(add_ip_exclusion_to_warp)
213
+ nw_apps.command(name="p", help="Add IP exclusion to WARP", hidden=True)(add_ip_exclusion_to_warp)
214
+
134
215
  return nw_apps
@@ -30,7 +30,8 @@ uv tool install --upgrade machineconfig
30
30
  if platform.system() == "Windows":
31
31
  from machineconfig.utils.code import exit_then_run_shell_script, get_uv_command_executing_python_script
32
32
  from machineconfig.utils.meta import lambda_to_python_script
33
- python_script = lambda_to_python_script(lambda: copy_both_assets(), in_global=True, import_module=False)
33
+ python_script = lambda_to_python_script(lambda: copy_both_assets(),
34
+ in_global=True, import_module=False)
34
35
  uv_command, _py_file = get_uv_command_executing_python_script(python_script=python_script, uv_with=["machineconfig"], uv_project_dir=None)
35
36
  exit_then_run_shell_script(shell_script + "\n" + uv_command, strict=True)
36
37
  else:
@@ -52,9 +53,9 @@ def install(no_copy_assets: Annotated[bool, typer.Option("--no-assets-copy", "-n
52
53
  else:
53
54
  import platform
54
55
  if platform.system() == "Windows":
55
- run_shell_script(r"""& "$HOME\.local\bin\uv.exe" tool install --upgrade "machineconfig>=7.64" """)
56
+ run_shell_script(r"""& "$HOME\.local\bin\uv.exe" tool install --upgrade "machineconfig>=7.79" """)
56
57
  else:
57
- run_shell_script("""$HOME/.local/bin/uv tool install --upgrade "machineconfig>=7.64" """)
58
+ run_shell_script("""$HOME/.local/bin/uv tool install --upgrade "machineconfig>=7.79" """)
58
59
  from machineconfig.profile.create_shell_profile import create_default_shell_profile
59
60
  if not no_copy_assets:
60
61
  create_default_shell_profile() # involves copying assets too
@@ -77,10 +78,10 @@ def navigate():
77
78
  import machineconfig.scripts.python as navigator
78
79
  from pathlib import Path
79
80
  path = Path(navigator.__file__).resolve().parent.joinpath("devops_navigator.py")
80
- from machineconfig.utils.code import run_shell_script
81
+ from machineconfig.utils.code import exit_then_run_shell_script
81
82
  if Path.home().joinpath("code/machineconfig").exists(): executable = f"""--project "{str(Path.home().joinpath("code/machineconfig"))}" --with textual"""
82
- else: executable = """--with "machineconfig>=7.64,textual" """
83
- run_shell_script(f"""uv run {executable} {path}""")
83
+ else: executable = """--with "machineconfig>=7.79,textual" """
84
+ exit_then_run_shell_script(f"""uv run {executable} {path}""")
84
85
 
85
86
 
86
87
  def run_python(ip: Annotated[str, typer.Argument(..., help="Python command to run in the machineconfig environment")],
@@ -1,6 +1,5 @@
1
+
1
2
  import typer
2
- # import platform
3
- # import sys
4
3
  from typing import Annotated
5
4
 
6
5
 
@@ -103,12 +102,13 @@ def share_file_send(path: Annotated[str, typer.Argument(help="Path to the file o
103
102
  from machineconfig.utils.installer_utils.installer_cli import install_if_missing
104
103
  install_if_missing(which="croc")
105
104
  # Get relay server IP from environment or use default
106
- import socket
105
+ import machineconfig.scripts.python.nw.address as helper
106
+ res = helper.select_lan_ipv4(prefer_vpn=False)
107
+ if res is None:
108
+ typer.echo("❌ Error: Could not determine local LAN IPv4 address for relay.", err=True)
109
+ raise typer.Exit(code=1)
110
+ local_ip_v4 = res
107
111
  import platform
108
- s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
109
- s.connect(('8.8.8.8',80))
110
- local_ip_v4 = s.getsockname()[0]
111
- s.close()
112
112
  relay_port = "443"
113
113
  is_windows = platform.system() == "Windows"
114
114
 
@@ -38,18 +38,16 @@ def web_file_explorer(
38
38
  username: Annotated[Optional[str], typer.Option("--username", "-u", help="Username for share access (default: current user)")] = None,
39
39
  password: Annotated[Optional[str], typer.Option("--password", "-w", help="Password for share access (default: from ~/dotfiles/creds/passwords/quick_password)")] = None,
40
40
  over_internet: Annotated[bool, typer.Option("--over-internet", "-i", help="Expose the share server over the internet using ngrok")] = False,
41
- backend: Annotated[str, typer.Option("--backend", "-b", help="Backend to use: filebrowser (default), miniserve, or easy-sharing")] = "filebrowser"
41
+ backend: Annotated[str, typer.Option("--backend", "-b", help="Backend to use: filebrowser (default), miniserve, qrcp, or easy-sharing")] = "miniserve",
42
42
  ) -> None:
43
43
  from machineconfig.utils.installer_utils.installer_cli import install_if_missing
44
-
45
- if backend not in ["filebrowser", "miniserve", "easy-sharing"]:
46
- typer.echo(f"❌ ERROR: Invalid backend '{backend}'. Must be one of: filebrowser, miniserve, easy-sharing", err=True)
44
+
45
+ if backend not in ["filebrowser", "miniserve", "qrcp", "easy-sharing"]:
46
+ typer.echo(f"❌ ERROR: Invalid backend '{backend}'. Must be one of: filebrowser, miniserve, qrcp, easy-sharing", err=True)
47
47
  raise typer.Exit(code=1)
48
-
49
48
  install_if_missing(which=backend)
50
49
  if over_internet:
51
50
  install_if_missing(which="ngrok")
52
-
53
51
  if username is None:
54
52
  import getpass
55
53
  username = getpass.getuser()
@@ -65,11 +63,12 @@ def web_file_explorer(
65
63
  if port is None:
66
64
  port = 8080
67
65
 
68
- import socket
69
- s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
70
- s.connect(('8.8.8.8', 80))
71
- local_ip_v4 = s.getsockname()[0]
72
- s.close()
66
+ import machineconfig.scripts.python.nw.address as helper
67
+ res = helper.select_lan_ipv4(prefer_vpn=False)
68
+ if res is None:
69
+ typer.echo("❌ ERROR: Could not determine local LAN IPv4 address for share server.", err=True)
70
+ raise typer.Exit(code=1)
71
+ local_ip_v4 = res
73
72
 
74
73
  protocol = "http"
75
74
  display_share_url(local_ip_v4, port, protocol)
@@ -90,6 +89,8 @@ filebrowser --address 0.0.0.0 --port {port} --root "{path_obj}" --database {db_p
90
89
  command = f"""miniserve --port {port} --interfaces 0.0.0.0 --auth {username}:{password} --upload-files --mkdir --enable-tar --enable-tar-gz --enable-zip --qrcode "{path_obj}" """
91
90
  elif backend == "easy-sharing":
92
91
  command = f"""easy-sharing --port {port} --username {username} --password "{password}" "{path_obj}" """
92
+ elif backend == "qrcp":
93
+ command = f"""qrcp "{path_obj}" """
93
94
  else:
94
95
  typer.echo(f"❌ ERROR: Unknown backend '{backend}'", err=True)
95
96
  raise typer.Exit(code=1)
@@ -96,11 +96,12 @@ def main(
96
96
  if ssl_ca and not Path(ssl_ca).exists():
97
97
  raise FileNotFoundError(f"SSL CA file not found: {ssl_ca}")
98
98
 
99
- import socket
100
- s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
101
- s.connect(('8.8.8.8',80))
102
- local_ip_v4 = s.getsockname()[0]
103
- s.close()
99
+ import machineconfig.scripts.python.nw.address as helper
100
+ res = helper.select_lan_ipv4(prefer_vpn=False)
101
+ if res is None:
102
+ print("❌ Error: Could not determine local LAN IPv4 address for terminal.")
103
+ raise typer.Exit(code=1)
104
+ local_ip_v4 = res
104
105
 
105
106
  # Display the flashy terminal announcement
106
107
  protocol = "https" if ssl else "http"
@@ -36,7 +36,8 @@ def merge_pdfs(
36
36
  writer.write(output_path)
37
37
  print(f"✅ Merged PDF saved to: {output_path}")
38
38
  from machineconfig.utils.meta import lambda_to_python_script
39
- code = lambda_to_python_script(lambda : merge_pdfs_internal(pdfs=pdfs, output=output, compress=compress), in_global=True, import_module=False)
39
+ code = lambda_to_python_script(lambda : merge_pdfs_internal(pdfs=pdfs, output=output, compress=compress),
40
+ in_global=True, import_module=False)
40
41
  from machineconfig.utils.code import run_shell_script, get_uv_command_executing_python_script
41
42
  uv_command, _py_file = get_uv_command_executing_python_script(python_script=code, uv_with=["pypdf"], uv_project_dir=None)
42
43
  run_shell_script(uv_command)
@@ -17,22 +17,6 @@ from machineconfig.utils.source_of_truth import CONFIG_ROOT, DEFAULTS_PATH, LIBR
17
17
  console = Console()
18
18
 
19
19
 
20
- def _check_system_info() -> dict[str, str]:
21
- """Gather basic system information."""
22
- import socket
23
- import os
24
-
25
- return {
26
- "hostname": socket.gethostname(),
27
- "system": platform.system(),
28
- "release": platform.release(),
29
- "version": platform.version(),
30
- "machine": platform.machine(),
31
- "processor": platform.processor() or "Unknown",
32
- "python_version": platform.python_version(),
33
- "user": os.getenv("USER") or os.getenv("USERNAME") or "Unknown",
34
- }
35
-
36
20
 
37
21
  def _check_shell_profile_status() -> dict[str, Any]:
38
22
  """Check shell profile configuration status."""
@@ -480,10 +464,14 @@ def main() -> None:
480
464
  console.print("\n")
481
465
  console.print(Panel(Text("📊 Machine Status Report", justify="center", style="bold white"), style="bold blue", padding=(1, 2)))
482
466
  console.print("\n")
483
-
484
- system_info = _check_system_info()
467
+
468
+ # system_info = _check_system_info()
469
+ # from machineconfig.scripts.python.helpers_devops.devops_system_info import _check_system_info
470
+ from machineconfig.scripts.python.helpers_utils.path import get_machine_specs
471
+ system_info = get_machine_specs()
472
+ from typing import cast
473
+ system_info = cast(dict[str, str], system_info)
485
474
  _display_system_info(system_info)
486
-
487
475
  shell_status = _check_shell_profile_status()
488
476
  _display_shell_status(shell_status)
489
477
 
@@ -54,15 +54,13 @@ def choose_function_or_lines(choice_file: Path, kwargs_dict: dict[str, object])
54
54
 
55
55
 
56
56
  def get_command_streamlit(choice_file: Path, environment: str, repo_root: Optional[Path]) -> str:
57
- import socket
58
- s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
59
- try:
60
- s.connect(("8.8.8.8", 1))
61
- local_ip_v4 = s.getsockname()[0]
62
- except Exception:
63
- local_ip_v4 = socket.gethostbyname(socket.gethostname())
64
- finally:
65
- s.close()
57
+ # from machineconfig.scripts.python.helpers_utils.path import get_machine_specs
58
+ from machineconfig.scripts.python.nw.address import select_lan_ipv4
59
+ res = select_lan_ipv4(prefer_vpn=False)
60
+ if res is None:
61
+ raise RuntimeError("Could not determine local LAN IPv4 address for streamlit app.")
62
+ local_ip_v4 = res
63
+
66
64
  computer_name = platform.node()
67
65
  port = 8501
68
66
  toml_path: Optional[Path] = None
@@ -100,6 +98,19 @@ def get_command_streamlit(choice_file: Path, environment: str, repo_root: Option
100
98
  except Exception as ex:
101
99
  print(ex)
102
100
  raise ex
101
+ from machineconfig.utils.installer_utils.installer_cli import install_if_missing
102
+ install_if_missing("qrterminal")
103
+ script = f"""
104
+ qrterminal "http://{local_ip_v4}:{port}"
105
+ echo "http://{local_ip_v4}:{port}"
106
+ qrterminal "http://{computer_name}:{port}"
107
+ echo "http://{computer_name}:{port}"
108
+ """
109
+ # from machineconfig.utils.code import run_shell_script
110
+ # run_shell_script(script)
111
+ from machineconfig.utils.code import print_code
112
+ print_code(code=script, lexer="shell", desc="Streamlit QR Codes and URLs")
113
+
103
114
  message = f"🚀 Streamlit app is running @:\n1- http://{local_ip_v4}:{port}\n2- http://{computer_name}:{port}\n3- http://localhost:{port}"
104
115
  from rich.panel import Panel
105
116
  from rich import print as rprint
@@ -188,15 +188,7 @@ class CommandTree(Tree[CommandInfo]):
188
188
  description="Configure your shell profile",
189
189
  command="devops config shell",
190
190
  parent="config",
191
- help_text="devops config shell <copy|reference>"
192
- ))
193
-
194
- config_node.add("🔗 path - Navigate PATH variable", data=CommandInfo(
195
- name="path",
196
- description="Navigate PATH variable with TUI",
197
- command="devops config path",
198
- parent="config",
199
- help_text="devops config path"
191
+ help_text="devops config shell --which <default|nushell>"
200
192
  ))
201
193
 
202
194
  config_node.add("🔗 starship-theme - Select starship theme", data=CommandInfo(
@@ -560,7 +552,23 @@ class CommandTree(Tree[CommandInfo]):
560
552
  description="Choose a process to kill interactively",
561
553
  command="utils kill-process",
562
554
  parent="utils",
563
- help_text="utils kill-process"
555
+ help_text="utils kill-process --interactive"
556
+ ))
557
+
558
+ utils_node.add("📚 path - Navigate PATH variable", data=CommandInfo(
559
+ name="path",
560
+ description="Navigate PATH variable with TUI",
561
+ command="utils path",
562
+ parent="utils",
563
+ help_text="utils path"
564
+ ))
565
+
566
+ utils_node.add("⬆️ upgrade-packages - Upgrade dependencies", data=CommandInfo(
567
+ name="upgrade-packages",
568
+ description="Upgrade project dependencies",
569
+ command="utils upgrade-packages",
570
+ parent="utils",
571
+ help_text="utils upgrade-packages"
564
572
  ))
565
573
 
566
574
  utils_node.add("⬇️ download - Download file", data=CommandInfo(
@@ -571,18 +579,42 @@ class CommandTree(Tree[CommandInfo]):
571
579
  help_text="utils download <url> --destination <path> --decompress"
572
580
  ))
573
581
 
574
- utils_node.add("📄 merge-pdfs - Merge PDF files", data=CommandInfo(
575
- name="merge-pdfs",
576
- description="Merge two PDF files into one",
577
- command="utils merge-pdfs",
578
- parent="utils",
579
- help_text="utils merge-pdfs <file1> <file2> --output <file>"
580
- ))
581
-
582
582
  utils_node.add("🖥️ get-machine-specs - Get machine specifications", data=CommandInfo(
583
583
  name="get-machine-specs",
584
584
  description="Get machine specifications",
585
585
  command="utils get-machine-specs",
586
586
  parent="utils",
587
587
  help_text="utils get-machine-specs"
588
+ ))
589
+
590
+ utils_node.add("🚀 init-project - Initialize project", data=CommandInfo(
591
+ name="init-project",
592
+ description="Initialize a project with a uv virtual environment and install dev packages",
593
+ command="utils init-project",
594
+ parent="utils",
595
+ help_text="utils init-project"
596
+ ))
597
+
598
+ utils_node.add("✏️ edit - Open file in editor", data=CommandInfo(
599
+ name="edit",
600
+ description="Open a file in the default editor",
601
+ command="utils edit",
602
+ parent="utils",
603
+ help_text="utils edit <file>"
604
+ ))
605
+
606
+ utils_node.add("📄 pdf-merge - Merge PDF files", data=CommandInfo(
607
+ name="pdf-merge",
608
+ description="Merge two PDF files into one",
609
+ command="utils pdf-merge",
610
+ parent="utils",
611
+ help_text="utils pdf-merge <file1> <file2> --output <file>"
612
+ ))
613
+
614
+ utils_node.add("� pdf-compress - Compress PDF file", data=CommandInfo(
615
+ name="pdf-compress",
616
+ description="Compress a PDF file",
617
+ command="utils pdf-compress",
618
+ parent="utils",
619
+ help_text="utils pdf-compress <file> --output <file>"
588
620
  ))
@@ -105,7 +105,7 @@ git pull originEnc master
105
105
  uv_project_dir = f"""{str(Path.home().joinpath("code/machineconfig"))}"""
106
106
  uv_with = None
107
107
  else:
108
- uv_with = ["machineconfig>=7.64"]
108
+ uv_with = ["machineconfig>=7.79"]
109
109
  uv_project_dir = None
110
110
 
111
111
  import tempfile
@@ -136,7 +136,8 @@ git pull originEnc master
136
136
  def func2(remote_repo: str, local_repo: str, cloud: str):
137
137
  from machineconfig.scripts.python.helpers_repos.sync import delete_remote_repo_copy_and_push_local
138
138
  delete_remote_repo_copy_and_push_local(remote_repo=remote_repo, local_repo=local_repo, cloud=cloud)
139
- program_1_py = lambda_to_python_script(lambda: func2(remote_repo=str(repo_remote_root), local_repo=str(repo_local_root), cloud=str(cloud_resolved)), in_global=True, import_module=False)
139
+ program_1_py = lambda_to_python_script(lambda: func2(remote_repo=str(repo_remote_root), local_repo=str(repo_local_root), cloud=str(cloud_resolved)),
140
+ in_global=True, import_module=False)
140
141
  program1, _pyfile1 = get_uv_command_executing_python_script(python_script=program_1_py, uv_with=uv_with, uv_project_dir=uv_project_dir)
141
142
  # ================================================================================
142
143
  option2 = "Delete local repo and replace it with remote copy:"
@@ -161,7 +162,8 @@ sudo chmod +x $HOME/dotfiles/scripts/linux -R
161
162
  inspect_repos(repo_local_root=repo_local_root, repo_remote_root=repo_remote_root)
162
163
  # program_3_py = function_to_script(func=func, call_with_kwargs={"repo_local_root": str(repo_local_root), "repo_remote_root": str(repo_remote_root)})
163
164
  # shell_file_3 = get_shell_file_executing_python_script(python_script=program_3_py, ve_path=None, executable=executable)
164
- program_3_py = lambda_to_python_script(lambda: func(repo_local_root=str(repo_local_root), repo_remote_root=str(repo_remote_root)), in_global=True, import_module=False)
165
+ program_3_py = lambda_to_python_script(lambda: func(repo_local_root=str(repo_local_root), repo_remote_root=str(repo_remote_root)),
166
+ in_global=True, import_module=False)
165
167
  program3, _pyfile3 = get_uv_command_executing_python_script(python_script=program_3_py, uv_with=uv_with, uv_project_dir=uv_project_dir)
166
168
  # ================================================================================
167
169
 
@@ -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.64" {count_lines_path} analyze-over-time {repo_path}"""
11
+ cmd = f"""uv run --python 3.14 --with "machineconfig[plot]>=7.79" {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
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  from typing import Annotated, Optional
4
4
  import typer
5
+ from pathlib import Path
5
6
 
6
7
 
7
8
  def download(
@@ -12,11 +13,10 @@ def download(
12
13
  ) -> Optional["Path"]:
13
14
 
14
15
  import subprocess
15
- from pathlib import Path
16
16
  from urllib.parse import parse_qs, unquote, urlparse
17
17
  from requests import Response
18
18
  import requests
19
-
19
+ from pathlib import Path
20
20
  if url is None:
21
21
  typer.echo("❌ Error: URL is required.", err=True)
22
22
  return None
@@ -147,5 +147,6 @@ def download(
147
147
 
148
148
  return result_path.resolve()
149
149
 
150
+
150
151
  if __name__ == "__main__":
151
- from pathlib import Path
152
+ pass