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
@@ -56,52 +56,31 @@ echo "🔄 Updating Homebrew..."
56
56
  brew update || true
57
57
 
58
58
  curl -fsSL https://bun.com/install | bash
59
-
60
- # Note: git and nano are pre-installed on macOS, but we install via Homebrew to ensure latest versions
61
- # brew install git || true
62
- # brew install nano || true
63
- # brew install curl || true
64
- # nvm install node || true
65
- # brew install make
66
- # brew install ffmpeg
67
- # brew install openssl
68
-
69
-
59
+ # brew install openssl ffmpeg make curl nano git
70
60
  echo "✅ Essential tools installation complete."
71
61
  """
72
62
 
73
63
  bash = r"""
64
+
74
65
  sudo apt update -y || true
75
66
  sudo apt install nala -y || true
76
- sudo nala install curl wget gpg lsb-release apt-transport-https -y || true
77
- sudo nala install git net-tools htop nano -y || true
78
- sudo nala install build-essential python3-dev -y || true # C build toolchain: Where build-essential brings gcc, make, etc., and python3-dev ensures headers for your Python version.
79
- # sudo nala install libssl-dev -y
80
- # sudo nala install libaa-bin -y
67
+ sudo nala install curl wget gpg lsb-release apt-transport-https \
68
+ samba fuse3 nfs-common \
69
+ git net-tools htop nano \
70
+ build-essential \ # Where it brings gcc, make, etc.,
71
+ python3-dev \ # ensures headers for your Python version.
72
+ unzip \ # bun installer needs this to unzip files.
73
+ pkg-config \
74
+ libssl-dev \ # essential for SSL/TLS support in many applications, compiling rust binaries will need them.
75
+ -y || true
81
76
 
82
- # curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
83
- # source ~/.bashrc || true
84
- # nvm install node || true
85
-
86
- sudo nala install unzip -y # required by bun installer
87
77
  curl -fsSL https://bun.com/install | bash
88
78
  . ~/.bashrc || true
89
- # sudo ln -s $(which bun) /usr/local/bin/node # trick programs that expect node to use bun runtime.
90
- sudo ln -s ~/.bun/bin/bun /usr/local/bin/node
91
-
92
-
93
- sudo nala install samba -y || true
94
- sudo nala install fuse3 -y || true
95
- sudo nala install nfs-common -y || true
79
+ sudo ln -s ~/.bun/bin/bun /usr/local/bin/node # trick programs that expect node to use bun runtime.
96
80
 
97
81
  # echo 'keyboard-configuration keyboard-configuration/layout select US English' | sudo debconf-set-selections
98
82
  # echo 'keyboard-configuration keyboard-configuration/layoutcode string us' | sudo debconf-set-selections
99
- # sudo DEBIAN_FRONTEND=noninteractive nala install -y cmatrix
100
- # sudo nala install hollywood -y || true
101
-
102
- # sudo nala install ffmpeg -y || true # Required by some dev tools
103
- # sudo nala install make -y || true # Required by LunarVim and SpaceVim
104
- # (curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh) || true
83
+ # sudo DEBIAN_FRONTEND=noninteractive nala install cmatrix hollywood ffmpeg -y make || true
105
84
 
106
85
  """
107
86
 
@@ -207,9 +207,9 @@ config = {original = '~/mprocs.yaml', self_managed = 'CONFIG_ROOT/settings/mproc
207
207
 
208
208
 
209
209
  [zellij_linux]
210
- config = { original = '~/.config/zellij', self_managed = 'CONFIG_ROOT/settings/zellij' , os = 'linux,darwin'}
211
- #themes = { original = '~/.config/zellij/themes', self_managed = 'CONFIG_ROOT/settings/zellij/themes' }
212
- #layouts = { original = '~/.config/zellij/layouts', self_managed = 'CONFIG_ROOT/settings/zellij/layouts' }
210
+ config = { original = '~/.config/zellij', self_managed = 'CONFIG_ROOT/settings/zellij', os = 'linux,darwin'}
211
+ themes = { original = '~/.config/zellij/themes', self_managed = 'CONFIG_ROOT/settings/zellij/themes', os = 'linux,darwin' }
212
+ layouts = { original = '~/.config/zellij/layouts', self_managed = 'CONFIG_ROOT/settings/zellij/layouts', os = 'linux,darwin' }
213
213
 
214
214
 
215
215
  # ===================== IDEs ============================
@@ -60,7 +60,7 @@ def get_app() -> typer.Typer:
60
60
  cli_app = typer.Typer(help="🛠️ DevOps operations", no_args_is_help=True, add_help_option=True, add_completion=False)
61
61
  ctx_settings: dict[str, object] = {"allow_extra_args": True, "allow_interspersed_args": True, "ignore_unknown_options": True, "help_option_names": []}
62
62
 
63
- cli_app.command("install", no_args_is_help=True, help=install.__doc__, short_help="🛠️ [i] Install essential packages")(install)
63
+ cli_app.command("install", no_args_is_help=True, help=install.__doc__, short_help="🛠️ [i] Install essential packages")(install)
64
64
  cli_app.command("i", no_args_is_help=True, help=install.__doc__, hidden=True)(install)
65
65
 
66
66
  cli_app.command("repos", help="📁 [r] Manage development repositories", context_settings=ctx_settings)(repos)
@@ -2,7 +2,7 @@
2
2
  # /// script
3
3
  # requires-python = ">=3.13"
4
4
  # dependencies = [
5
- # "machineconfig>=8.51",
5
+ # "machineconfig>=8.61",
6
6
  # "textual",
7
7
  # ]
8
8
  # ///
@@ -2,7 +2,7 @@
2
2
  # /// script
3
3
  # requires-python = ">=3.13"
4
4
  # dependencies = [
5
- # "machineconfig>=8.51",
5
+ # "machineconfig>=8.61",
6
6
  # "textual",
7
7
  # "pyperclip",
8
8
  # ]
@@ -2,7 +2,7 @@
2
2
  # /// script
3
3
  # requires-python = ">=3.13"
4
4
  # dependencies = [
5
- # "machineconfig>=8.51",
5
+ # "machineconfig>=8.61",
6
6
  # "textual",
7
7
  # "pyperclip",
8
8
  # ]
@@ -2,7 +2,7 @@
2
2
  # /// script
3
3
  # requires-python = ">=3.13"
4
4
  # dependencies = [
5
- # "machineconfig>=8.51",
5
+ # "machineconfig>=8.61",
6
6
  # "textual",
7
7
  # "pyperclip",
8
8
  # ]
@@ -24,12 +24,16 @@ def croshell(
24
24
  user_uv_with_line = f"--with {uv_with},ipython"
25
25
  case "python":
26
26
  user_uv_with_line = f"--with {uv_with}"
27
+ case _:
28
+ raise ValueError(f"Unknown interpreter: {interpreter}")
27
29
  else:
28
30
  match interpreter:
29
31
  case "ipython":
30
32
  user_uv_with_line = "--with ipython"
31
33
  case "python":
32
34
  user_uv_with_line = ""
35
+ case _:
36
+ raise ValueError(f"Unknown interpreter: {interpreter}")
33
37
 
34
38
  if project_path is not None:
35
39
  uv_project_line = f'--project {project_path}'
@@ -188,7 +192,7 @@ def _build_fire_line(
188
192
  if Path.home().joinpath("code/machineconfig").exists():
189
193
  requirements = f"""{user_uv_with_line} {uv_project_line} --with marimo,sqlglot """
190
194
  else:
191
- requirements = f"""{uv_python_line} {user_uv_with_line} {uv_project_line} --with "marimo,sqlglot,cowsay,machineconfig[plot]>=8.51" """
195
+ requirements = f"""{uv_python_line} {user_uv_with_line} {uv_project_line} --with "marimo,sqlglot,cowsay,machineconfig[plot]>=8.61" """
192
196
  fire_line = f"""
193
197
  cd {str(pyfile.parent)}
194
198
  uv run {uv_python_line} --with "marimo" marimo convert {pyfile.name} -o marimo_nb.py
@@ -198,7 +202,7 @@ uv run {requirements} marimo edit --host 0.0.0.0 marimo_nb.py
198
202
  if Path.home().joinpath("code/machineconfig").exists():
199
203
  requirements = f"""{user_uv_with_line} {uv_project_line} --with jupyterlab """
200
204
  else:
201
- requirements = f"""{user_uv_with_line} {uv_project_line} --with "cowsay,machineconfig[plot]>=8.51" """
205
+ requirements = f"""{user_uv_with_line} {uv_project_line} --with "cowsay,machineconfig[plot]>=8.61" """
202
206
  fire_line = f"uv run {requirements} {uv_project_line} jupyter-lab {str(nb_target)}"
203
207
  elif vscode:
204
208
  user_uv_add = f"uv add {uv_with}" if uv_with is not None else ""
@@ -206,7 +210,7 @@ uv run {requirements} marimo edit --host 0.0.0.0 marimo_nb.py
206
210
  cd {str(pyfile.parent)}
207
211
  uv init {uv_python_line}
208
212
  uv venv
209
- uv add "cowsay,machineconfig[plot]>=8.51"
213
+ uv add "cowsay,machineconfig[plot]>=8.61"
210
214
  uv add {user_uv_add}
211
215
  # code serve-web
212
216
  code --new-window {str(pyfile)}
@@ -219,7 +223,7 @@ code --new-window {str(pyfile)}
219
223
  if Path.home().joinpath("code/machineconfig").exists():
220
224
  ve_line = f"""{user_uv_with_line} {uv_project_line} """
221
225
  else:
222
- ve_line = f"""{uv_python_line} {user_uv_with_line} {uv_project_line} --with "cowsay,machineconfig[plot]>=8.51" """
226
+ ve_line = f"""{uv_python_line} {user_uv_with_line} {uv_project_line} --with "cowsay,machineconfig[plot]>=8.61" """
223
227
  fire_line = f"uv run {ve_line} {interpreter} {interactivity} {profile} {str(pyfile)}"
224
228
 
225
229
  return fire_line
@@ -25,7 +25,11 @@ def configure_shell_profile(which: Annotated[Literal["default", "d", "nushell",
25
25
  def pwsh_theme():
26
26
  """🔗 Select powershell prompt theme."""
27
27
  import machineconfig.scripts.python.helpers.helpers_devops.themes as themes
28
- file = Path(themes.__file__).parent / "choose_pwsh_theme.ps1"
28
+ file_path = themes.__file__
29
+ # if file_path is None:
30
+ # typer.echo("❌ ERROR: Could not locate themes module file.", err=True)
31
+ # raise typer.Exit(code=1)
32
+ file = Path(file_path).parent / "choose_pwsh_theme.ps1"
29
33
  import subprocess
30
34
  subprocess.run(["pwsh", "-File", str(file)])
31
35
 
@@ -71,6 +75,29 @@ def copy_assets(which: Annotated[Literal["scripts", "s", "settings", "t", "both"
71
75
  typer.echo(msg)
72
76
 
73
77
 
78
+ def list_devices() -> None:
79
+ """🔗 List available mountable devices."""
80
+ import machineconfig.scripts.python.helpers.helpers_devops.cli_config_mount as mount_module
81
+ mount_module.list_devices()
82
+
83
+
84
+ def mount_device(
85
+ device_query: Annotated[str | None, typer.Option("--device", "-d", help="Device query (path, key, or label).")] = None,
86
+ mount_point: Annotated[str | None, typer.Option("--mount-point", "-p", help="Mount point (use '-' for default on macOS).")] = None,
87
+ interactive: Annotated[bool, typer.Option("--interactive", "-i", help="Pick device and mount point interactively.")] = False,
88
+ ) -> None:
89
+ """🔗 Mount a device to a mount point."""
90
+ import machineconfig.scripts.python.helpers.helpers_devops.cli_config_mount as mount_module
91
+ if interactive:
92
+ mount_module.mount_interactive()
93
+ return
94
+ if device_query is None or mount_point is None:
95
+ msg = typer.style("Error: ", fg=typer.colors.RED) + "--device and --mount-point are required unless --interactive is set"
96
+ typer.echo(msg)
97
+ raise typer.Exit(2)
98
+ mount_module.mount_device(device_query=device_query, mount_point=mount_point)
99
+
100
+
74
101
  def get_app():
75
102
  config_apps = typer.Typer(help="⚙️ [c] configuration subcommands", no_args_is_help=True, add_help_option=True, add_completion=False)
76
103
  config_apps.command("sync", no_args_is_help=True, help="🔗 [s] Sync dotfiles.")(create_links_export.main_from_parser)
@@ -94,5 +121,10 @@ def get_app():
94
121
  config_apps.command("copy-assets", no_args_is_help=True, help="🔗 [c] Copy asset files from library to machine.", hidden=False)(copy_assets)
95
122
  config_apps.command("c", no_args_is_help=True, help="Copy asset files from library to machine.", hidden=True)(copy_assets)
96
123
 
124
+ config_apps.command("list-devices", no_args_is_help=False, help="🔗 [l] List available devices for mounting.")(list_devices)
125
+ config_apps.command("l", no_args_is_help=False, help="List available devices for mounting.", hidden=True)(list_devices)
126
+ config_apps.command("mount", no_args_is_help=True, help="🔗 [m] Mount a device to a mount point.")(mount_device)
127
+ config_apps.command("m", no_args_is_help=True, help="Mount a device to a mount point.", hidden=True)(mount_device)
128
+
97
129
 
98
130
  return config_apps
@@ -0,0 +1,77 @@
1
+
2
+ import platform
3
+ from typing import Annotated
4
+
5
+ import typer
6
+
7
+ from machineconfig.scripts.python.helpers.helpers_devops.mount_helpers.devices import list_devices as list_devices_internal
8
+ from machineconfig.scripts.python.helpers.helpers_devops.mount_helpers.linux import mount_linux, select_linux_partition
9
+ from machineconfig.scripts.python.helpers.helpers_devops.mount_helpers.macos import mount_macos
10
+ from machineconfig.scripts.python.helpers.helpers_devops.mount_helpers.selection import pick_device, resolve_device
11
+ from machineconfig.scripts.python.helpers.helpers_devops.mount_helpers.utils import format_device
12
+ from machineconfig.scripts.python.helpers.helpers_devops.mount_helpers.windows import mount_windows
13
+
14
+
15
+ def list_devices() -> None:
16
+ entries = list_devices_internal()
17
+ if not entries:
18
+ typer.echo("No devices found")
19
+ return
20
+ for entry in entries:
21
+ typer.echo(format_device(entry))
22
+
23
+
24
+ def mount_device(
25
+ device_query: Annotated[str, typer.Argument(...)],
26
+ mount_point: Annotated[str, typer.Argument(...)],
27
+ ) -> None:
28
+ try:
29
+ entries = list_devices_internal()
30
+ if not entries:
31
+ typer.echo("No devices found")
32
+ raise typer.Exit(1)
33
+ entry = resolve_device(entries, device_query)
34
+ platform_name = platform.system()
35
+ if platform_name == "Linux":
36
+ selected = select_linux_partition(entries, entry)
37
+ mount_linux(selected, mount_point)
38
+ elif platform_name == "Darwin":
39
+ mount_macos(entry, mount_point)
40
+ elif platform_name == "Windows":
41
+ mount_windows(entry, mount_point)
42
+ else:
43
+ typer.echo(f"Unsupported platform: {platform_name}")
44
+ raise typer.Exit(1)
45
+ typer.echo("Mount completed")
46
+ except RuntimeError as exc:
47
+ typer.echo(f"Mount failed: {exc}")
48
+ raise typer.Exit(1)
49
+
50
+
51
+ def mount_interactive() -> None:
52
+ try:
53
+ entries = list_devices_internal()
54
+ if not entries:
55
+ typer.echo("No devices found")
56
+ raise typer.Exit(1)
57
+ entry = pick_device(entries, header="Available devices")
58
+ platform_name = platform.system()
59
+ if platform_name == "Darwin":
60
+ mount_point = typer.prompt("Mount point (use '-' for default)")
61
+ else:
62
+ mount_point = typer.prompt("Mount point")
63
+ if platform_name == "Linux":
64
+ selected = select_linux_partition(entries, entry)
65
+ mount_linux(selected, mount_point)
66
+ elif platform_name == "Darwin":
67
+ mount_macos(entry, mount_point)
68
+ elif platform_name == "Windows":
69
+ mount_windows(entry, mount_point)
70
+ else:
71
+ typer.echo(f"Unsupported platform: {platform_name}")
72
+ raise typer.Exit(1)
73
+ typer.echo("Mount completed")
74
+ except RuntimeError as exc:
75
+ typer.echo(f"Mount failed: {exc}")
76
+ raise typer.Exit(1)
77
+
@@ -1,3 +1,4 @@
1
+
1
2
  import typer
2
3
  from typing import Annotated, Optional, Literal
3
4
  from machineconfig.profile.create_links_export import REPO_LOOSE
@@ -18,6 +19,9 @@ def sync(
18
19
  direction_resolved = "BACKUP"
19
20
  case "down" | "d":
20
21
  direction_resolved = "RETRIEVE"
22
+ case _:
23
+ typer.echo("Error: Invalid direction. Use 'up' or 'down'.")
24
+ raise typer.Exit(code=1)
21
25
  main_backup_retrieve(direction=direction_resolved, which=which, cloud=cloud, repo=repo)
22
26
 
23
27
 
@@ -3,8 +3,9 @@ import machineconfig.scripts.python.helpers.helpers_devops.cli_share_file
3
3
  import machineconfig.scripts.python.helpers.helpers_devops.cli_share_terminal as cli_share_terminal
4
4
  import machineconfig.scripts.python.helpers.helpers_devops.cli_share_server as cli_share_server
5
5
  import machineconfig.scripts.python.helpers.helpers_devops.cli_ssh as cli_ssh
6
+ import machineconfig.scripts.python.helpers.helpers_devops.cli_share_temp as cli_share_temp
6
7
  import typer
7
- from typing import Annotated
8
+ from typing import Annotated, Literal
8
9
 
9
10
 
10
11
  def switch_public_ip_address(
@@ -124,19 +125,95 @@ def wifi_select(
124
125
 
125
126
 
126
127
 
127
- def reset_cloudflare_tunnel():
128
+ def reset_cloudflare_tunnel(task: Annotated[Literal["oneoff-shell-process", "oneoff-background-process", "as-service"], typer.Option(..., "--task", "-t", help="Task to perform", case_sensitive=False, show_choices=True)],
129
+ tunnel_name: Annotated[str | None, typer.Option("--tunnel-name", "-n", help="Name of the Cloudflare tunnel to run")] = None,
130
+ ):
128
131
  code = """
129
132
  # cloudflared tunnel route dns glenn # creates CNAMES in Cloudflare dashboard
130
133
  # sudo systemctl stop cloudflared
131
- # test: cloudflared tunnel run glenn
134
+ """
135
+ match task:
136
+ case "oneoff-shell-process":
137
+ if tunnel_name is None: tunnel_name = ""
138
+ code = f"""cloudflared tunnel run {tunnel_name} # This is running like a normal command """
139
+ case "oneoff-background-process":
140
+ if tunnel_name is None: tunnel_name = ""
141
+ import getpass
142
+ user_name = getpass.getuser()
143
+ code = f"""
144
+ # This verion runs like a deamon, but its not peristent across reboots
145
+ sudo systemd-run \
146
+ --unit=cloudflared-tunnel \
147
+ --description="Cloudflared Tunnel (transient)" \
148
+ --property=Restart=on-failure \
149
+ --property=RestartSec=5 \
150
+ --property=User={user_name} \
151
+ --property=Group={user_name} \
152
+ --property=Environment=HOME=/home/{user_name} \
153
+ --property=WorkingDirectory=/home/{user_name} \
154
+ /home/{user_name}/.local/bin/cloudflared \
155
+ --config /home/{user_name}/.cloudflared/config.yml \
156
+ tunnel run {tunnel_name}
157
+ """
158
+ case "as-service":
159
+ code = """
132
160
  home_dir=$HOME
133
161
  cloudflared_path="$home_dir/.local/bin/cloudflared"
134
162
  sudo $cloudflared_path service uninstall
135
163
  sudo rm /etc/cloudflared/config.yml || true
136
164
  sudo $cloudflared_path --config $home_dir/.cloudflared/config.yml service install
137
165
  """
138
- print(code)
139
- print("NOTE: Please run the above commands in your terminal to apply the changes.")
166
+
167
+ from machineconfig.utils.code import exit_then_run_shell_script, print_code
168
+ print_code(code, lexer="bash", desc="code to achieve the goal")
169
+ yes = typer.confirm("Do you want to run the above commands now?", default=False)
170
+ if yes:
171
+ exit_then_run_shell_script(code)
172
+
173
+
174
+
175
+ def vscode_share(
176
+ action: Annotated[Literal["run", "install-service", "uninstall-service", "share-local"], typer.Option(..., "--action", "-a", help="Action to perform", case_sensitive=False, show_choices=True)],
177
+ name: Annotated[str | None, typer.Option("--name", "-n", help="Name for the tunnel/service")] = None,
178
+ path: Annotated[str | None, typer.Option("--path", "-p", help="Path to share locally (for share-local)")] = None,
179
+ extra_args: Annotated[str | None, typer.Option("--extra-args", "-e", help="Extra args to append to the code tunnel command")] = None,
180
+ ) -> None:
181
+ """🧑‍💻 Share workspace using VS Code Tunnels ("code tunnel")
182
+
183
+ Note: this helper prints recommended commands and optionally runs them.
184
+ If you need more functionality, consult VS Code Tunnels docs: https://code.visualstudio.com/docs/remote/tunnels
185
+ """
186
+ accept = "--accept-server-license-terms"
187
+ name_part = f"--name {name}" if name else ""
188
+ extra = extra_args or ""
189
+
190
+ if action == "run":
191
+ cmd = f"code tunnel {name_part} {accept} {extra}".strip()
192
+ desc = "Run a one-off VS Code tunnel (foreground)"
193
+ elif action == "install-service":
194
+ cmd = f"code tunnel service install {accept} {name_part}".strip()
195
+ desc = "Install code tunnel as a service"
196
+ elif action == "uninstall-service":
197
+ cmd = "code tunnel service uninstall"
198
+ desc = "Uninstall code tunnel service"
199
+ elif action == "share-local":
200
+ p = path or "."
201
+ # Some VS Code CLI integrations prefer starting a tunnel and then using the editor to expose resources.
202
+ cmd = f"code tunnel {name_part} {accept} {extra}".strip()
203
+ desc = f"Start tunnel and then share local path: {p} (use VS Code UI to forward ports / share files)"
204
+ else:
205
+ print(f"Unknown action: {action}")
206
+ return
207
+
208
+ from machineconfig.utils.code import print_code, exit_then_run_shell_script
209
+ print_code(cmd, lexer="bash", desc=desc)
210
+
211
+ if typer.confirm("Do you want to run the above command now?", default=False):
212
+ exit_then_run_shell_script(cmd)
213
+ else:
214
+ print("Command not executed. Use the printed command in your terminal when ready.")
215
+
216
+
140
217
 
141
218
  def add_ip_exclusion_to_warp(ip: Annotated[str, typer.Option(..., "--ip", help="IP address(es) to exclude from WARP (Comma separated)")]):
142
219
  ips = ip.split(",")
@@ -173,6 +250,9 @@ def get_app():
173
250
  nw_apps.command(name="receive", no_args_is_help=True, hidden=False, help="📁 [rx] receive files to here.")(machineconfig.scripts.python.helpers.helpers_devops.cli_share_file.share_file_receive)
174
251
  nw_apps.command(name="rx", no_args_is_help=True, hidden=True, help="📁 [rx] receive files to here.")(machineconfig.scripts.python.helpers.helpers_devops.cli_share_file.share_file_receive)
175
252
 
253
+ nw_apps.command(name="share-temp-file", help="🌡️ [T] Share a file via temp.sh")(cli_share_temp.upload_file)
254
+ nw_apps.command(name="T", help="Share a file via temp.sh", hidden=True)(cli_share_temp.upload_file)
255
+
176
256
  nw_apps.add_typer(cli_ssh.get_app(), name="ssh", help="🔐 [S] SSH subcommands")
177
257
  nw_apps.add_typer(cli_ssh.get_app(), name="S", help="SSH subcommands", hidden=True)
178
258
 
@@ -193,9 +273,13 @@ def get_app():
193
273
  nw_apps.command(name="l", no_args_is_help=False, help="Link WSL home and Windows home directories.", hidden=True)(link_wsl_and_windows_home)
194
274
 
195
275
 
196
- nw_apps.command(name="reset-cloudflare-tunnel", help="☁️ [r] Reset Cloudflare tunnel service")(reset_cloudflare_tunnel)
276
+ nw_apps.command(name="reset-cloudflare-tunnel", help="☁️ [r] Reset Cloudflare tunnel service")(reset_cloudflare_tunnel)
197
277
  nw_apps.command(name="r", help="Reset Cloudflare tunnel service", hidden=True)(reset_cloudflare_tunnel)
198
278
  nw_apps.command(name="add-ip-exclusion-to-warp", help="🚫 [p] Add IP exclusion to WARP")(add_ip_exclusion_to_warp)
199
279
  nw_apps.command(name="p", help="Add IP exclusion to WARP", hidden=True)(add_ip_exclusion_to_warp)
200
280
 
281
+ # VS Code Tunnels helper
282
+ nw_apps.command(name="vscode-share", no_args_is_help=True, help="🧑‍💻 [v] Share workspace via VS Code Tunnels")(vscode_share)
283
+ nw_apps.command(name="v", no_args_is_help=True, hidden=True, help="Share workspace via VS Code Tunnels")(vscode_share)
284
+
201
285
  return nw_apps
@@ -125,7 +125,7 @@ def count_lines_in_repo(repo_path: Annotated[str, typer.Argument(..., help="Path
125
125
  # from machineconfig.scripts.python.helpers.helpers_repos import repo_analyzer_1
126
126
  # repo_analyzer_1.count_historical_line_edits(repo_path=repo_path)
127
127
  # from machineconfig.utils.code import run_lambda_function
128
- # run_lambda_function(lambda: func(repo_path=repo_path), uv_project_dir=None, uv_with=["machineconfig>=8.51"])
128
+ # run_lambda_function(lambda: func(repo_path=repo_path), uv_project_dir=None, uv_with=["machineconfig>=8.61"])
129
129
  from machineconfig.scripts.python.helpers.helpers_repos import repo_analyzer_1
130
130
  try:
131
131
  repo_analyzer_1.count_historical_line_edits(repo_path=repo_path)
@@ -138,7 +138,7 @@ def print_python_files_by_size(repo_path: Annotated[str, typer.Argument(..., hel
138
138
  # from machineconfig.scripts.python.helpers.helpers_repos.repo_analyzer_2 import print_python_files_by_size_impl
139
139
  # print_python_files_by_size_impl(repo_path=repo_path)
140
140
  # from machineconfig.utils.code import run_lambda_function
141
- # run_lambda_function(lambda: func(repo_path=repo_path), uv_project_dir=None, uv_with=["machineconfig[plot]>=8.51"])
141
+ # run_lambda_function(lambda: func(repo_path=repo_path), uv_project_dir=None, uv_with=["machineconfig[plot]>=8.61"])
142
142
  from machineconfig.scripts.python.helpers.helpers_repos.repo_analyzer_2 import print_python_files_by_size_impl
143
143
  print_python_files_by_size_impl(repo_path=repo_path)
144
144
 
@@ -148,7 +148,7 @@ def analyze_repo_development(repo_path: Annotated[str, typer.Argument(..., help=
148
148
  # from machineconfig.scripts.python.helpers.helpers_repos.repo_analyzer_2 import analyze_over_time
149
149
  # analyze_over_time(repo_path=repo_path)
150
150
  # from machineconfig.utils.code import run_lambda_function
151
- # run_lambda_function(lambda: func(repo_path=repo_path), uv_project_dir=None, uv_with=["machineconfig[plot]>=8.51"])
151
+ # run_lambda_function(lambda: func(repo_path=repo_path), uv_project_dir=None, uv_with=["machineconfig[plot]>=8.61"])
152
152
  from machineconfig.scripts.python.helpers.helpers_repos.repo_analyzer_2 import analyze_over_time
153
153
  analyze_over_time(repo_path=repo_path)
154
154
 
@@ -177,21 +177,47 @@ def readme():
177
177
  console.print(md)
178
178
 
179
179
 
180
+ def buid_docker(
181
+ variant: Annotated[Literal["slim", "ai"], typer.Argument(..., help="Variant to build: 'slim' or 'ai'")] = "slim",
182
+ ) -> None:
183
+ """🧱 `buid_docker` — wrapper for `jobs/shell/docker_build_and_publish.sh`"""
184
+ from pathlib import Path
185
+ import machineconfig
186
+ script_path = Path(machineconfig.__file__).resolve().parent.parent.parent.joinpath("jobs", "shell", "docker_build_and_publish.sh")
187
+ if not script_path.exists():
188
+ typer.echo(f"❌ Script not found: {str(script_path)}")
189
+ raise typer.Exit(code=1)
190
+
191
+ # shell_cmd = f'VARIANT="{variant}" && bash "{str(script_path)}"'\
192
+ from machineconfig.utils.source_of_truth import REPO_ROOT
193
+ shell_cmd = f"""
194
+ export VARIANT="{variant}"
195
+ cd "{str(REPO_ROOT)}"
196
+ bash "{str(script_path)}"
197
+ """
198
+ # Use exit_then_run_shell_script for interactive runs (keeps tty), otherwise run shell script non-interactively
199
+ from machineconfig.utils.code import exit_then_run_shell_script
200
+ exit_then_run_shell_script(shell_cmd, strict=True)
201
+
202
+
180
203
  def get_app():
181
204
  cli_app = typer.Typer(help="🔄 [s] self operations subcommands", no_args_is_help=True, add_help_option=True, add_completion=False)
182
- cli_app.command("update", no_args_is_help=False, help="🔄 [u] UPDATE machineconfig")(update)
183
- cli_app.command("u", no_args_is_help=False, hidden=True)(update)
184
- cli_app.command("interactive", no_args_is_help=False, help="🤖 [ia] INTERACTIVE configuration of machine.")(interactive)
185
- cli_app.command("ia", no_args_is_help=False, help="INTERACTIVE configuration of machine.", hidden=True)(interactive)
186
- cli_app.command(name="init", no_args_is_help=False, help="🦐 [t] Define and manage configurations")(init)
187
- cli_app.command(name="t", no_args_is_help=False, hidden=True)(init)
188
- cli_app.command("status", no_args_is_help=False, help="📊 [s] STATUS of machine, shell profile, apps, symlinks, dotfiles, etc.")(status)
189
- cli_app.command("s", no_args_is_help=False, help="STATUS of machine, shell profile, apps, symlinks, dotfiles, etc.", hidden=True)(status)
190
- cli_app.command("install", no_args_is_help=False, help="📋 [i] CLONE machienconfig locally and incorporate to shell profile for faster execution and nightly updates.")(install)
191
- cli_app.command("i", no_args_is_help=False, help="CLONE machienconfig locally and incorporate to shell profile for faster execution and nightly updates.", hidden=True)(install)
192
- cli_app.command("navigate", no_args_is_help=False, help="📚 [n] NAVIGATE command structure with TUI")(navigate)
193
- cli_app.command("n", no_args_is_help=False, help="NAVIGATE command structure with TUI", hidden=True)(navigate)
194
-
195
- cli_app.command("readme", no_args_is_help=False, help="📚 [r] render readme markdown in terminal.")(readme)
196
- cli_app.command("r", no_args_is_help=False, hidden=True)(readme)
205
+ cli_app.command(name= "update", no_args_is_help=False, help="🔄 [u] UPDATE machineconfig")(update)
206
+ cli_app.command(name= "u", no_args_is_help=False, hidden=True)(update)
207
+ cli_app.command(name= "interactive", no_args_is_help=False, help="🤖 [ia] INTERACTIVE configuration of machine.")(interactive)
208
+ cli_app.command(name= "ia", no_args_is_help=False, help="INTERACTIVE configuration of machine.", hidden=True)(interactive)
209
+ cli_app.command(name= "init", no_args_is_help=False, help="🦐 [t] Define and manage configurations")(init)
210
+ cli_app.command(name= "t", no_args_is_help=False, hidden=True)(init)
211
+ cli_app.command(name= "status", no_args_is_help=False, help="📊 [s] STATUS of machine, shell profile, apps, symlinks, dotfiles, etc.")(status)
212
+ cli_app.command(name= "s", no_args_is_help=False, help="STATUS of machine, shell profile, apps, symlinks, dotfiles, etc.", hidden=True)(status)
213
+ cli_app.command(name= "install", no_args_is_help=False, help="📋 [i] CLONE machienconfig locally and incorporate to shell profile for faster execution and nightly updates.")(install)
214
+ cli_app.command(name= "i", no_args_is_help=False, help="CLONE machienconfig locally and incorporate to shell profile for faster execution and nightly updates.", hidden=True)(install)
215
+ cli_app.command(name= "navigate", no_args_is_help=False, help="📚 [n] NAVIGATE command structure with TUI")(navigate)
216
+ cli_app.command(name= "n", no_args_is_help=False, help="NAVIGATE command structure with TUI", hidden=True)(navigate)
217
+
218
+ cli_app.command(name= "buid_docker", no_args_is_help=False, help="🧱 [d] Build docker images (wraps jobs/shell/docker_build_and_publish.sh)")(buid_docker)
219
+ cli_app.command(name= "d", no_args_is_help=False, help="Build docker images (wraps jobs/shell/docker_build_and_publish.sh)", hidden=True)(buid_docker)
220
+
221
+ cli_app.command(name= "readme", no_args_is_help=False, help="📚 [r] render readme markdown in terminal.")(readme)
222
+ cli_app.command(name= "r", no_args_is_help=False, hidden=True)(readme)
197
223
  return cli_app
@@ -0,0 +1,69 @@
1
+
2
+ import json
3
+ import sys
4
+ from pathlib import Path
5
+ from typing import Annotated
6
+
7
+ import requests
8
+ import typer
9
+
10
+
11
+ TEMP_BASE_URL: str = "https://temp.sh"
12
+ UPLOAD_ENDPOINT: str = f"{TEMP_BASE_URL}/upload"
13
+
14
+
15
+ def _extract_url(response: requests.Response) -> str:
16
+ content_type = response.headers.get("content-type", "")
17
+ text = response.text.strip()
18
+ if "application/json" in content_type:
19
+ try:
20
+ data = response.json()
21
+ except (json.JSONDecodeError, ValueError):
22
+ data = None
23
+ if isinstance(data, dict):
24
+ url_value = data.get("url") or data.get("link") or data.get("download")
25
+ if isinstance(url_value, str) and url_value.strip() != "":
26
+ return url_value.strip()
27
+ for token in text.split():
28
+ if token.startswith("https://") or token.startswith("http://"):
29
+ return token
30
+ return text
31
+
32
+
33
+ def _upload_file_handle(file_name: str, file_handle: object, content_type: str | None) -> str:
34
+ files: dict[str, tuple[str, object, str] | tuple[str, object]]
35
+ if content_type is None:
36
+ files = {"file": (file_name, file_handle)}
37
+ else:
38
+ files = {"file": (file_name, file_handle, content_type)}
39
+ response = requests.post(UPLOAD_ENDPOINT, files=files) # type: ignore[reportArgumentType]
40
+ response.raise_for_status()
41
+ return _extract_url(response=response)
42
+
43
+
44
+ def upload_file(
45
+ file_path: Annotated[Path, typer.Argument(..., exists=True, dir_okay=False, readable=True)],
46
+ ) -> None:
47
+ try:
48
+ with file_path.open("rb") as file_handle:
49
+ url = _upload_file_handle(file_name=file_path.name, file_handle=file_handle, content_type=None)
50
+ except requests.RequestException as exc:
51
+ typer.echo(f"Upload failed: {exc}")
52
+ raise typer.Exit(1)
53
+ typer.echo(url)
54
+
55
+
56
+ def upload_text(
57
+ text: Annotated[str, typer.Argument(...)],
58
+ ) -> None:
59
+ text_value = text
60
+ if text == "-":
61
+ text_value = sys.stdin.read()
62
+ try:
63
+ payload = text_value.encode("utf-8")
64
+ url = _upload_file_handle(file_name="text.txt", file_handle=payload, content_type="text/plain; charset=utf-8")
65
+ except requests.RequestException as exc:
66
+ typer.echo(f"Upload failed: {exc}")
67
+ raise typer.Exit(1)
68
+ typer.echo(url)
69
+