machineconfig 6.82__py3-none-any.whl → 7.98__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 (294) hide show
  1. machineconfig/cluster/remote/cloud_manager.py +1 -1
  2. machineconfig/cluster/sessions_managers/utils/maker.py +25 -13
  3. machineconfig/cluster/sessions_managers/wt_local.py +16 -221
  4. machineconfig/cluster/sessions_managers/wt_local_manager.py +55 -193
  5. machineconfig/cluster/sessions_managers/wt_remote_manager.py +42 -198
  6. machineconfig/cluster/sessions_managers/wt_utils/manager_persistence.py +52 -0
  7. machineconfig/cluster/sessions_managers/wt_utils/monitoring_helpers.py +50 -0
  8. machineconfig/cluster/sessions_managers/wt_utils/status_reporting.py +76 -0
  9. machineconfig/cluster/sessions_managers/wt_utils/wt_helpers.py +199 -0
  10. machineconfig/cluster/sessions_managers/zellij_local_manager.py +3 -1
  11. machineconfig/cluster/sessions_managers/zellij_remote_manager.py +3 -2
  12. machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +2 -2
  13. machineconfig/jobs/installer/custom/boxes.py +2 -2
  14. machineconfig/jobs/installer/custom/hx.py +75 -18
  15. machineconfig/jobs/installer/custom/yazi.py +119 -0
  16. machineconfig/jobs/installer/custom_dev/brave.py +5 -3
  17. machineconfig/jobs/installer/custom_dev/cloudflare_warp_cli.py +23 -0
  18. machineconfig/jobs/installer/custom_dev/code.py +4 -1
  19. machineconfig/jobs/installer/custom_dev/dubdb_adbc.py +1 -1
  20. machineconfig/jobs/installer/custom_dev/nerdfont.py +1 -1
  21. machineconfig/jobs/installer/custom_dev/nerfont_windows_helper.py +27 -22
  22. machineconfig/jobs/installer/custom_dev/sysabc.py +139 -0
  23. machineconfig/jobs/installer/custom_dev/wezterm.py +2 -19
  24. machineconfig/jobs/installer/custom_dev/winget.py +10 -14
  25. machineconfig/jobs/installer/installer_data.json +1287 -216
  26. machineconfig/jobs/installer/linux_scripts/q.sh +10 -7
  27. machineconfig/jobs/installer/linux_scripts/redis.sh +1 -0
  28. machineconfig/jobs/installer/package_groups.py +58 -89
  29. machineconfig/jobs/installer/powershell_scripts/install_fonts.ps1 +129 -34
  30. machineconfig/logger.py +0 -1
  31. machineconfig/profile/create_helper.py +43 -16
  32. machineconfig/profile/create_links.py +2 -1
  33. machineconfig/profile/create_links_export.py +64 -18
  34. machineconfig/profile/create_shell_profile.py +78 -127
  35. machineconfig/profile/mapper.toml +15 -8
  36. machineconfig/scripts/__init__.py +0 -4
  37. machineconfig/scripts/linux/wrap_mcfg +46 -0
  38. machineconfig/scripts/nu/wrap_mcfg.nu +69 -0
  39. machineconfig/scripts/python/agents.py +52 -37
  40. machineconfig/scripts/python/ai/initai.py +1 -1
  41. machineconfig/scripts/python/ai/scripts/command_runner.ps1 +33 -0
  42. machineconfig/scripts/python/ai/{command_runner → scripts}/command_runner.sh +1 -1
  43. machineconfig/scripts/python/ai/solutions/copilot/{chatmodes/Thinking-Beast-Mode.chatmode.md → agents/Thinking-Beast-Mode.agent.md} +0 -1
  44. machineconfig/scripts/python/ai/solutions/copilot/{chatmodes/Ultimate-Transparent-Thinking-Beast-Mode.chatmode.md → agents/Ultimate-Transparent-Thinking-Beast-Mode.agent.md} +0 -1
  45. machineconfig/scripts/python/ai/solutions/copilot/{chatmodes/deepResearch.chatmode.md → agents/deepResearch.agent.md} +2 -2
  46. machineconfig/scripts/python/ai/solutions/copilot/github_copilot.py +5 -5
  47. machineconfig/scripts/python/ai/solutions/copilot/instructions/python/dev.instructions.md +4 -0
  48. machineconfig/scripts/python/ai/solutions/copilot/instructions/python/watch_exec.prompt.md +20 -0
  49. machineconfig/scripts/python/ai/solutions/generic.py +1 -1
  50. machineconfig/scripts/python/ai/{generate_files.py → utils/generate_files.py} +2 -2
  51. machineconfig/scripts/python/ai/{vscode_tasks.py → utils/vscode_tasks.py} +7 -2
  52. machineconfig/scripts/python/croshell.py +77 -78
  53. machineconfig/scripts/python/devops.py +39 -21
  54. machineconfig/scripts/python/devops_navigator.py +0 -4
  55. machineconfig/scripts/python/env_manager/env_manager_tui.py +204 -0
  56. machineconfig/scripts/python/env_manager/path_manager_tui.py +1 -1
  57. machineconfig/scripts/python/fire_jobs.py +84 -115
  58. machineconfig/scripts/python/ftpx.py +42 -16
  59. machineconfig/scripts/python/helpers/ast_search.py +74 -0
  60. machineconfig/scripts/python/helpers/qr_code.py +166 -0
  61. machineconfig/scripts/python/helpers/repo_rag.py +325 -0
  62. machineconfig/scripts/python/helpers/run_py_script.py +79 -0
  63. machineconfig/scripts/python/helpers/symantic_search.py +25 -0
  64. machineconfig/scripts/python/helpers/tmp_py_scripts/a.py +26 -0
  65. machineconfig/scripts/python/{helpers_fire → helpers_agents}/agentic_frameworks/fire_crush.json +1 -1
  66. machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_crush.py +39 -0
  67. machineconfig/scripts/python/{helpers_fire → helpers_agents}/agentic_frameworks/fire_cursor_agents.py +3 -4
  68. machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_gemini.py +55 -0
  69. machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_qwen.py +30 -0
  70. machineconfig/scripts/python/{helpers_fire → helpers_agents}/fire_agents_help_launch.py +32 -13
  71. machineconfig/scripts/python/{helpers_fire → helpers_agents}/fire_agents_helper_types.py +11 -14
  72. machineconfig/scripts/python/helpers_agents/templates/prompt.txt +10 -0
  73. machineconfig/scripts/python/helpers_agents/templates/template.sh +32 -0
  74. machineconfig/scripts/python/helpers_cloud/cloud_copy.py +28 -21
  75. machineconfig/scripts/python/helpers_cloud/cloud_helpers.py +1 -1
  76. machineconfig/scripts/python/helpers_cloud/cloud_mount.py +19 -17
  77. machineconfig/scripts/python/helpers_cloud/cloud_sync.py +8 -7
  78. machineconfig/scripts/python/helpers_croshell/crosh.py +3 -3
  79. machineconfig/scripts/python/helpers_croshell/start_slidev.py +6 -7
  80. machineconfig/scripts/python/helpers_devops/cli_config.py +46 -61
  81. machineconfig/scripts/python/helpers_devops/cli_config_dotfile.py +67 -55
  82. machineconfig/scripts/python/helpers_devops/cli_nw.py +157 -16
  83. machineconfig/scripts/python/helpers_devops/cli_repos.py +55 -21
  84. machineconfig/scripts/python/helpers_devops/cli_self.py +98 -48
  85. machineconfig/scripts/python/helpers_devops/cli_share_file.py +137 -0
  86. machineconfig/scripts/python/helpers_devops/cli_share_server.py +80 -42
  87. machineconfig/scripts/python/helpers_devops/{cli_terminal.py → cli_share_terminal.py} +15 -17
  88. machineconfig/scripts/python/helpers_devops/cli_utils.py +3 -128
  89. machineconfig/scripts/python/helpers_devops/devops_backup_retrieve.py +4 -4
  90. machineconfig/scripts/python/helpers_devops/devops_status.py +7 -19
  91. machineconfig/scripts/python/helpers_devops/themes/choose_wezterm_theme.py +1 -1
  92. machineconfig/scripts/python/{helpers_fire/helpers4.py → helpers_fire_command/file_wrangler.py} +56 -20
  93. machineconfig/scripts/python/helpers_fire_command/fire_jobs_route_helper.py +26 -16
  94. machineconfig/scripts/python/helpers_msearch/__init__.py +5 -0
  95. machineconfig/scripts/{linux → python/helpers_msearch/scripts_linux}/fzfg +3 -3
  96. machineconfig/scripts/python/helpers_msearch/scripts_windows/fzfg.ps1 +59 -0
  97. machineconfig/scripts/python/helpers_navigator/command_tree.py +50 -18
  98. machineconfig/scripts/python/helpers_network/address.py +132 -0
  99. machineconfig/scripts/python/{nw → helpers_network}/devops_add_ssh_key.py +24 -5
  100. machineconfig/scripts/python/{nw → helpers_network}/mount_nfs +0 -1
  101. machineconfig/scripts/python/{nw → helpers_network}/mount_nfs.py +2 -2
  102. machineconfig/scripts/python/{nw → helpers_network}/mount_ssh.py +1 -1
  103. machineconfig/scripts/python/{nw → helpers_network}/ssh_debug_linux.py +7 -7
  104. machineconfig/scripts/python/{nw → helpers_network}/ssh_debug_windows.py +4 -4
  105. machineconfig/scripts/python/{nw → helpers_network}/wifi_conn.py +1 -53
  106. machineconfig/scripts/python/{nw → helpers_network}/wsl_windows_transfer.py +3 -2
  107. machineconfig/scripts/python/helpers_repos/clone.py +0 -1
  108. machineconfig/scripts/python/helpers_repos/cloud_repo_sync.py +46 -19
  109. machineconfig/scripts/python/helpers_repos/entrypoint.py +2 -1
  110. machineconfig/scripts/python/helpers_repos/grource.py +1 -1
  111. machineconfig/scripts/python/helpers_repos/record.py +2 -1
  112. machineconfig/scripts/python/helpers_repos/repo_analyzer_1.py +160 -0
  113. machineconfig/scripts/python/helpers_repos/{count_lines.py → repo_analyzer_2.py} +113 -192
  114. machineconfig/scripts/python/helpers_sessions/sessions_multiprocess.py +20 -13
  115. machineconfig/scripts/python/helpers_utils/download.py +150 -0
  116. machineconfig/scripts/python/helpers_utils/path.py +185 -0
  117. machineconfig/scripts/python/interactive.py +19 -26
  118. machineconfig/scripts/python/{mcfg.py → mcfg_entry.py} +10 -0
  119. machineconfig/scripts/python/msearch.py +71 -0
  120. machineconfig/scripts/python/sessions.py +94 -25
  121. machineconfig/scripts/python/terminal.py +133 -0
  122. machineconfig/scripts/python/utils.py +28 -30
  123. machineconfig/scripts/windows/mounts/mount_ssh.ps1 +1 -1
  124. machineconfig/scripts/windows/wrap_mcfg.ps1 +63 -0
  125. machineconfig/settings/broot/conf.toml +1 -1
  126. machineconfig/settings/helix/config.toml +16 -0
  127. machineconfig/settings/helix/languages.toml +13 -4
  128. machineconfig/settings/helix/yazi-picker.sh +12 -0
  129. machineconfig/settings/lf/linux/exe/lfcd.sh +1 -0
  130. machineconfig/settings/lf/linux/exe/previewer.sh +3 -2
  131. machineconfig/settings/lf/windows/lfcd.ps1 +1 -1
  132. machineconfig/settings/lf/windows/lfrc +14 -16
  133. machineconfig/settings/marimo/marimo.toml +1 -1
  134. machineconfig/settings/marimo/snippets/globalize.py +34 -0
  135. machineconfig/settings/shells/bash/init.sh +43 -11
  136. machineconfig/settings/shells/ipy/profiles/default/startup/playext.py +1 -1
  137. machineconfig/settings/shells/nushell/config.nu +2 -32
  138. machineconfig/settings/shells/nushell/env.nu +45 -6
  139. machineconfig/settings/shells/nushell/init.nu +314 -0
  140. machineconfig/settings/shells/pwsh/init.ps1 +40 -14
  141. machineconfig/settings/shells/starship/starship.toml +16 -0
  142. machineconfig/settings/shells/wezterm/wezterm.lua +2 -0
  143. machineconfig/settings/shells/wt/settings.json +14 -5
  144. machineconfig/settings/shells/zsh/init.sh +17 -19
  145. machineconfig/settings/television/cable_unix/alias.toml +8 -0
  146. machineconfig/settings/television/cable_unix/aws-buckets.toml +14 -0
  147. machineconfig/settings/television/cable_unix/aws-instances.toml +13 -0
  148. machineconfig/settings/television/cable_unix/bash-history.toml +8 -0
  149. machineconfig/settings/television/cable_unix/channels.toml +19 -0
  150. machineconfig/settings/television/cable_unix/dirs.toml +13 -0
  151. machineconfig/settings/television/cable_unix/distrobox-list.toml +42 -0
  152. machineconfig/settings/television/cable_unix/docker-images.toml +13 -0
  153. machineconfig/settings/television/cable_unix/dotfiles.toml +11 -0
  154. machineconfig/settings/television/cable_unix/env.toml +17 -0
  155. machineconfig/settings/television/cable_unix/files.toml +11 -0
  156. machineconfig/settings/television/cable_unix/fish-history.toml +8 -0
  157. machineconfig/settings/television/cable_unix/git-branch.toml +11 -0
  158. machineconfig/settings/television/cable_unix/git-diff.toml +10 -0
  159. machineconfig/settings/television/cable_unix/git-log.toml +12 -0
  160. machineconfig/settings/television/cable_unix/git-reflog.toml +12 -0
  161. machineconfig/settings/television/cable_unix/git-repos.toml +16 -0
  162. machineconfig/settings/television/cable_unix/guix.toml +20 -0
  163. machineconfig/settings/television/cable_unix/just-recipes.toml +18 -0
  164. machineconfig/settings/television/cable_unix/k8s-deployments.toml +36 -0
  165. machineconfig/settings/television/cable_unix/k8s-pods.toml +50 -0
  166. machineconfig/settings/television/cable_unix/k8s-services.toml +36 -0
  167. machineconfig/settings/television/cable_unix/man-pages.toml +24 -0
  168. machineconfig/settings/television/cable_unix/nu-history.toml +7 -0
  169. machineconfig/settings/television/cable_unix/procs.toml +20 -0
  170. machineconfig/settings/television/cable_unix/text.toml +17 -0
  171. machineconfig/settings/television/cable_unix/tldr.toml +18 -0
  172. machineconfig/settings/television/cable_unix/zsh-history.toml +9 -0
  173. machineconfig/settings/television/cable_windows/alias.toml +7 -0
  174. machineconfig/settings/television/cable_windows/dirs.toml +13 -0
  175. machineconfig/settings/television/cable_windows/docker-images.toml +13 -0
  176. machineconfig/settings/television/cable_windows/dotfiles.toml +11 -0
  177. machineconfig/settings/television/cable_windows/env.toml +17 -0
  178. machineconfig/settings/television/cable_windows/files.toml +14 -0
  179. machineconfig/settings/television/cable_windows/git-branch.toml +11 -0
  180. machineconfig/settings/television/cable_windows/git-diff.toml +10 -0
  181. machineconfig/settings/television/cable_windows/git-log.toml +11 -0
  182. machineconfig/settings/television/cable_windows/git-reflog.toml +11 -0
  183. machineconfig/settings/television/cable_windows/git-repos.toml +15 -0
  184. machineconfig/settings/television/cable_windows/nu-history.toml +7 -0
  185. machineconfig/settings/television/cable_windows/pwsh-history.toml +6 -0
  186. machineconfig/settings/television/cable_windows/text.toml +17 -0
  187. machineconfig/settings/yazi/init.lua +61 -0
  188. machineconfig/settings/yazi/keymap_linux.toml +94 -0
  189. machineconfig/settings/yazi/keymap_windows.toml +78 -0
  190. machineconfig/settings/yazi/shell/yazi_cd.ps1 +33 -0
  191. machineconfig/settings/yazi/shell/yazi_cd.sh +8 -0
  192. machineconfig/settings/yazi/theme.toml +4 -0
  193. machineconfig/settings/yazi/yazi_linux.toml +84 -0
  194. machineconfig/settings/yazi/yazi_windows.toml +58 -0
  195. machineconfig/setup_linux/__init__.py +2 -1
  196. machineconfig/setup_linux/web_shortcuts/interactive.sh +27 -12
  197. machineconfig/setup_linux/web_shortcuts/live_from_github.sh +31 -0
  198. machineconfig/setup_mac/__init__.py +2 -3
  199. machineconfig/setup_mac/apps_gui.sh +248 -0
  200. machineconfig/setup_windows/__init__.py +3 -3
  201. machineconfig/setup_windows/uv.ps1 +8 -1
  202. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +26 -11
  203. machineconfig/setup_windows/web_shortcuts/live_from_github.ps1 +30 -0
  204. machineconfig/setup_windows/web_shortcuts/quick_init.ps1 +17 -0
  205. machineconfig/utils/accessories.py +7 -4
  206. machineconfig/utils/code.py +99 -32
  207. machineconfig/utils/files/ascii_art.py +1 -1
  208. machineconfig/utils/files/headers.py +3 -2
  209. machineconfig/utils/installer_utils/github_release_bulk.py +156 -119
  210. machineconfig/utils/installer_utils/install_from_url.py +183 -0
  211. machineconfig/utils/installer_utils/installer_class.py +42 -99
  212. machineconfig/utils/installer_utils/installer_cli.py +175 -0
  213. machineconfig/utils/installer_utils/installer_helper.py +129 -0
  214. machineconfig/utils/installer_utils/{installer_abc.py → installer_locator_utils.py} +36 -85
  215. machineconfig/utils/{installer.py → installer_utils/installer_runner.py} +16 -61
  216. machineconfig/utils/io.py +69 -1
  217. machineconfig/utils/links.py +56 -38
  218. machineconfig/utils/meta.py +33 -18
  219. machineconfig/utils/options.py +46 -18
  220. machineconfig/utils/options_tv.py +119 -0
  221. machineconfig/utils/path_extended.py +44 -95
  222. machineconfig/utils/path_helper.py +76 -23
  223. machineconfig/utils/procs.py +1 -1
  224. machineconfig/utils/scheduler.py +20 -53
  225. machineconfig/utils/scheduling.py +0 -2
  226. machineconfig/utils/schemas/fire_agents/fire_agents_input.py +1 -1
  227. machineconfig/utils/schemas/layouts/layout_types.py +1 -1
  228. machineconfig/utils/ssh.py +159 -412
  229. machineconfig/utils/ssh_utils/abc.py +5 -0
  230. machineconfig/utils/ssh_utils/copy_from_here.py +111 -0
  231. machineconfig/utils/ssh_utils/copy_to_here.py +302 -0
  232. machineconfig/utils/ssh_utils/utils.py +142 -0
  233. machineconfig/utils/ssh_utils/wsl.py +210 -0
  234. machineconfig/utils/terminal.py +1 -0
  235. machineconfig/utils/upgrade_packages.py +104 -28
  236. machineconfig/utils/ve.py +12 -4
  237. machineconfig-7.98.dist-info/METADATA +132 -0
  238. {machineconfig-6.82.dist-info → machineconfig-7.98.dist-info}/RECORD +259 -196
  239. {machineconfig-6.82.dist-info → machineconfig-7.98.dist-info}/entry_points.txt +4 -1
  240. machineconfig/jobs/installer/linux_scripts/pgsql.sh +0 -41
  241. machineconfig/jobs/installer/linux_scripts/timescaledb.sh +0 -71
  242. machineconfig/jobs/installer/powershell_scripts/archive_pygraphviz.ps1 +0 -12
  243. machineconfig/scripts/linux/fzf2g +0 -21
  244. machineconfig/scripts/linux/fzfag +0 -17
  245. machineconfig/scripts/linux/fzffg +0 -25
  246. machineconfig/scripts/linux/fzfrga +0 -21
  247. machineconfig/scripts/linux/mcfgs +0 -38
  248. machineconfig/scripts/linux/other/share_smb +0 -1
  249. machineconfig/scripts/linux/skrg +0 -4
  250. machineconfig/scripts/linux/warp-cli.sh +0 -122
  251. machineconfig/scripts/linux/z_ls +0 -104
  252. machineconfig/scripts/python/ai/command_runner/prompt.txt +0 -9
  253. machineconfig/scripts/python/helpers_fire/agentic_frameworks/fire_crush.py +0 -37
  254. machineconfig/scripts/python/helpers_fire/agentic_frameworks/fire_gemini.py +0 -44
  255. machineconfig/scripts/python/helpers_fire/agentic_frameworks/fire_qwen.py +0 -43
  256. machineconfig/scripts/python/helpers_fire/prompt.txt +0 -2
  257. machineconfig/scripts/python/helpers_fire/template.sh +0 -15
  258. machineconfig/scripts/python/helpers_repos/count_lines_frontend.py +0 -17
  259. machineconfig/scripts/python/helpers_repos/secure_repo.py +0 -15
  260. machineconfig/scripts/python/nw/add_ssh_key.py +0 -148
  261. machineconfig/scripts/windows/fzfb.ps1 +0 -3
  262. machineconfig/scripts/windows/fzfg.ps1 +0 -2
  263. machineconfig/scripts/windows/fzfrga.bat +0 -20
  264. machineconfig/scripts/windows/mcfgs.ps1 +0 -17
  265. machineconfig/settings/lf/linux/exe/fzf_nano.sh +0 -16
  266. machineconfig/settings/lf/windows/fzf_edit.ps1 +0 -6
  267. machineconfig/settings/lf/windows/tst.ps1 +0 -1
  268. machineconfig/settings/yazi/yazi.toml +0 -4
  269. machineconfig/setup_linux/apps.sh +0 -66
  270. machineconfig/setup_linux/others/cli_installation.sh +0 -137
  271. machineconfig/setup_mac/apps.sh +0 -73
  272. machineconfig/setup_windows/apps.ps1 +0 -62
  273. machineconfig/utils/installer_utils/installer.py +0 -225
  274. machineconfig-6.82.dist-info/METADATA +0 -82
  275. /machineconfig/jobs/installer/linux_scripts/{warp-cli.sh → cloudflare_warp_cli.sh} +0 -0
  276. /machineconfig/scripts/python/{helpers_fire → ai/utils}/__init__.py +0 -0
  277. /machineconfig/scripts/python/{helpers_fire/agentic_frameworks → helpers_agents}/__init__.py +0 -0
  278. /machineconfig/scripts/python/{nw → helpers_agents/agentic_frameworks}/__init__.py +0 -0
  279. /machineconfig/scripts/python/{helpers_fire → helpers_agents}/fire_agents_help_search.py +0 -0
  280. /machineconfig/scripts/python/{helpers_fire → helpers_agents}/fire_agents_load_balancer.py +0 -0
  281. /machineconfig/scripts/python/{helpers_fire → helpers_agents/templates}/template.ps1 +0 -0
  282. /machineconfig/{settings/shells/pwsh/profile.ps1 → scripts/python/helpers_fire_command/f.py} +0 -0
  283. /machineconfig/{settings/yazi/keymap.toml → scripts/python/helpers_network/__init__.py} +0 -0
  284. /machineconfig/scripts/python/{nw → helpers_network}/devops_add_identity.py +0 -0
  285. /machineconfig/scripts/python/{nw → helpers_network}/mount_drive +0 -0
  286. /machineconfig/scripts/python/{nw → helpers_network}/mount_nw_drive +0 -0
  287. /machineconfig/scripts/python/{nw → helpers_network}/mount_nw_drive.py +0 -0
  288. /machineconfig/scripts/python/{nw → helpers_network}/mount_smb +0 -0
  289. /machineconfig/scripts/python/{nw → helpers_network}/onetimeshare.py +0 -0
  290. /machineconfig/scripts/{Restore-ThunderbirdProfile.ps1 → windows/mounts/Restore-ThunderbirdProfile.ps1} +0 -0
  291. /machineconfig/{jobs/installer/powershell_scripts → setup_windows/ssh}/openssh-server_add_key.ps1 +0 -0
  292. /machineconfig/{jobs/installer/powershell_scripts → setup_windows/ssh}/openssh-server_copy-ssh-id.ps1 +0 -0
  293. {machineconfig-6.82.dist-info → machineconfig-7.98.dist-info}/WHEEL +0 -0
  294. {machineconfig-6.82.dist-info → machineconfig-7.98.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,132 @@
1
+
2
+ from typing import cast, Optional
3
+
4
+
5
+ def get_all_ipv4_addresses() -> list[tuple[str, str]]:
6
+ import psutil
7
+ import socket
8
+ result: list[tuple[str, str]] = []
9
+ for iface, addrs in psutil.net_if_addrs().items():
10
+ for addr in addrs:
11
+ if addr.family == socket.AF_INET:
12
+ ip = addr.address
13
+ result.append((iface, ip))
14
+ return result
15
+
16
+
17
+ def select_lan_ipv4(prefer_vpn: bool) -> Optional[str]:
18
+ """
19
+ Choose the best 'real LAN' IPv4:
20
+ - Excludes loopback/link-local and (by default) VPN/tunnel/container ifaces
21
+ - Prefers physical-looking ifaces (eth/en*/wlan/wl*)
22
+ - Prefers RFC1918 LANs: 192.168/16 > 10/8 > 172.16/12
23
+ - Requires interface is UP
24
+ Set prefer_vpn=True to allow tunnel/VPN ifaces to compete.
25
+ """
26
+
27
+ import ipaddress
28
+ import re
29
+ from collections.abc import Sequence
30
+ import psutil
31
+
32
+ # Down-rank or exclude: tunnels/VPNs/bridges/containers (add your own if needed)
33
+ VIRTUAL_IFACE_PAT = re.compile(
34
+ r"^(?:lo|loopback|docker\d*|br-.*|veth.*|virbr.*|bridge.*|"
35
+ r"vboxnet.*|vmnet.*|zt.*|ham.*|tailscale.*|wg\d*|utun\d*|llw\d*|awdl\d*|"
36
+ r"tun\d*|tap\d*|cloudflarewarp.*|warp.*)$",
37
+ re.IGNORECASE,
38
+ )
39
+
40
+ # Light preference for names that look like real NICs
41
+ PHYSICAL_IFACE_PAT = re.compile(
42
+ r"^(?:eth\d*|en\d*|enp.*|ens.*|eno.*|wlan\d*|wl.*|.*wifi.*|.*ethernet.*)$",
43
+ re.IGNORECASE,
44
+ )
45
+
46
+ # Known noisy CIDRs to avoid
47
+ NOISY_NETS: list[ipaddress.IPv4Network] = [
48
+ ipaddress.IPv4Network("100.64.0.0/10"), # CGNAT (Tailscale/others)
49
+ ipaddress.IPv4Network("172.17.0.0/16"), # docker0 default
50
+ ipaddress.IPv4Network("172.18.0.0/16"),
51
+ ipaddress.IPv4Network("172.19.0.0/16"),
52
+ ipaddress.IPv4Network("192.168.49.0/24"), # minikube default
53
+ ipaddress.IPv4Network("10.0.2.0/24"), # VirtualBox NAT
54
+ ]
55
+
56
+ def _in_any(ip: ipaddress.IPv4Address, nets: Sequence[ipaddress.IPv4Network]) -> bool:
57
+ return any(ip in n for n in nets)
58
+
59
+ stats = psutil.net_if_stats()
60
+ best = None
61
+ best_score = -10**9
62
+ import socket
63
+ for iface, addrs in psutil.net_if_addrs().items():
64
+ st = stats.get(iface)
65
+ if not st or not st.isup:
66
+ continue
67
+
68
+ for a in addrs:
69
+ if a.family != socket.AF_INET or not a.address:
70
+ continue
71
+
72
+ ip_str = a.address
73
+ try:
74
+ ip = cast(ipaddress.IPv4Address, ipaddress.ip_address(ip_str))
75
+ except ValueError:
76
+ continue
77
+
78
+ # Exclude unusable classes
79
+ if ip.is_loopback or ip.is_link_local: # 127.0.0.0/8, 169.254.0.0/16
80
+ continue
81
+
82
+ # Hard filter: if it looks virtual and we don't prefer VPNs, skip it
83
+ if not prefer_vpn and VIRTUAL_IFACE_PAT.match(iface):
84
+ continue
85
+
86
+ # Hard filter: known noisy subnets (docker, cgnat, etc.)
87
+ if _in_any(ip, NOISY_NETS) and not prefer_vpn:
88
+ continue
89
+
90
+ # Base score
91
+ score = 0
92
+
93
+ # Prefer physical-looking names
94
+ if PHYSICAL_IFACE_PAT.match(iface):
95
+ score += 200
96
+
97
+ # Broadcast present usually means L2 LAN (not point-to-point)
98
+ # (psutil puts it on the same entry as .broadcast)
99
+ if getattr(a, "broadcast", None):
100
+ score += 100
101
+
102
+ # Prefer private RFC1918; rank families
103
+ if ip.is_private:
104
+ # Order: 192.168.x.x > 10.x.x.x > 172.16-31.x.x
105
+ ip_net = ipaddress.IPv4Network((ip, 32), strict=False)
106
+ if ipaddress.IPv4Network("192.168.0.0/16").supernet_of(ip_net):
107
+ score += 90
108
+ elif ipaddress.IPv4Network("10.0.0.0/8").supernet_of(ip_net):
109
+ score += 70
110
+ elif ipaddress.IPv4Network("172.16.0.0/12").supernet_of(ip_net):
111
+ score += 50
112
+ else:
113
+ # Public on a NIC is unusual for a home/office LAN
114
+ score -= 50
115
+
116
+ # Slight nudge by interface speed if known (>0 means psutil knows it)
117
+ # (Many tunnels report 0)
118
+ if getattr(st, "speed", 0) > 0:
119
+ score += 20
120
+
121
+ # Deterministic tie-breaker: prefer shorter iface name (eth0 over eth10)
122
+ score -= len(iface) * 0.01
123
+
124
+ if score > best_score:
125
+ best_score = score
126
+ best = ip_str
127
+
128
+ return best
129
+
130
+
131
+ if __name__ == "__main__":
132
+ print(select_lan_ipv4(False) or "No LAN IPv4 found")
@@ -13,9 +13,9 @@ import typer
13
13
  console = Console()
14
14
 
15
15
 
16
- def get_add_ssh_key_script(path_to_key: PathExtended):
16
+ def get_add_ssh_key_script(path_to_key: PathExtended) -> str:
17
17
  console.print(Panel("🔑 SSH KEY CONFIGURATION", title="[bold blue]SSH Setup[/bold blue]"))
18
- if system() == "Linux":
18
+ if system() == "Linux" or system() == "Darwin":
19
19
  authorized_keys = PathExtended.home().joinpath(".ssh/authorized_keys")
20
20
  console.print(Panel(f"🐧 Linux SSH configuration\n📄 Authorized keys file: {authorized_keys}", title="[bold blue]System Info[/bold blue]"))
21
21
  elif system() == "Windows":
@@ -35,7 +35,7 @@ def get_add_ssh_key_script(path_to_key: PathExtended):
35
35
  program = ""
36
36
  else:
37
37
  console.print(Panel(f"➕ Adding new SSH key to authorized keys\n🔑 Key file: {path_to_key.name}", title="[bold blue]Action[/bold blue]"))
38
- if system() == "Linux":
38
+ if system() == "Linux" or system() == "Darwin":
39
39
  program = f"cat {path_to_key} >> ~/.ssh/authorized_keys"
40
40
  elif system() == "Windows":
41
41
  program_path = LIBRARY_ROOT.joinpath("setup_windows/add-sshkey.ps1")
@@ -48,14 +48,14 @@ def get_add_ssh_key_script(path_to_key: PathExtended):
48
48
  raise NotImplementedError
49
49
  else:
50
50
  console.print(Panel(f"📝 Creating new authorized_keys file\n🔑 Using key: {path_to_key.name}", title="[bold blue]Action[/bold blue]"))
51
- if system() == "Linux":
51
+ if system() == "Linux" or system() == "Darwin":
52
52
  program = f"cat {path_to_key} > ~/.ssh/authorized_keys"
53
53
  else:
54
54
  program_path = LIBRARY_ROOT.joinpath("setup_windows/openssh-server_add-sshkey.ps1")
55
55
  program = PathExtended(program_path).expanduser().read_text(encoding="utf-8").replace('$sshfile=""', f'$sshfile="{path_to_key}"')
56
56
  console.print(Panel("🔧 Configured PowerShell script for Windows\n📝 Set key path in script", title="[bold blue]Configuration[/bold blue]"))
57
57
 
58
- if system() == "Linux":
58
+ if system() == "Linux" or system() == "Darwin":
59
59
  program += """
60
60
  sudo chmod 700 ~/.ssh
61
61
  sudo chmod 644 ~/.ssh/authorized_keys
@@ -66,6 +66,16 @@ sudo service ssh --full-restart
66
66
  return program
67
67
 
68
68
 
69
+ """
70
+ Common pitfalls:
71
+ 🚫 Wrong line endings (LF/CRLF) in config files
72
+ 🌐 Network port conflicts (try 2222 -> 2223) between WSL and Windows
73
+ sudo service ssh restart
74
+ sudo service ssh status
75
+ sudo nano /etc/ssh/sshd_config
76
+ """
77
+
78
+
69
79
  def main(pub_path: Annotated[Optional[str], typer.Argument(..., help="Path to the public key file")] = None,
70
80
  pub_choose: Annotated[bool, typer.Option(..., "--choose", "-c", help="Choose from available public keys in ~/.ssh")] = False,
71
81
  pub_val: Annotated[bool, typer.Option(..., "--paste", "-p", help="Paste the public key content manually")] = False,
@@ -127,6 +137,15 @@ def main(pub_path: Annotated[Optional[str], typer.Argument(..., help="Path to th
127
137
  console.print(Panel("🚀 SSH KEY AUTHORIZATION READY\nRun the generated script to apply changes", box=box.DOUBLE_EDGE, title_align="left"))
128
138
  from machineconfig.utils.code import run_shell_script
129
139
  run_shell_script(script=program)
140
+
141
+ import machineconfig.scripts.python.helpers_network.address as helper
142
+ res = helper.select_lan_ipv4(prefer_vpn=False)
143
+ if res is None:
144
+ console.print(Panel("❌ ERROR: Could not determine local LAN IPv4 address", title="[bold red]Error[/bold red]", border_style="red"))
145
+ raise typer.Exit(code=1)
146
+ local_ip_v4 = res
147
+
148
+ console.print(Panel(f"🌐 This computer is accessible at: {local_ip_v4}", title="[bold green]Network Info[/bold green]", border_style="green"))
130
149
  console.print(Panel("✅ SSH KEY AUTHORIZATION COMPLETED", box=box.DOUBLE_EDGE, title_align="left"))
131
150
 
132
151
 
@@ -5,7 +5,6 @@
5
5
  # mkdir ~/data/local
6
6
  # sudo mount -o nolock,noatime,nodiratime,proto=tcp,timeo=600,retrans=2,noac alex-p51s-5:/home/alex/data/local ./data/local
7
7
 
8
- uv run --python 3.14 --with "machineconfig>=6.81" python -m machineconfig.scripts.python.mount_nfs
9
8
  # Check if remote server is reachable and share folder exists
10
9
  if ! ping -c 1 "$remote_server" &> /dev/null; then
11
10
  echo "💥 Error: Remote server $remote_server is not reachable."
@@ -20,8 +20,8 @@ def main():
20
20
  tmp = choose_ssh_host(multi=False)
21
21
  assert isinstance(tmp, str)
22
22
  ssh = SSH(host=tmp, username=None, hostname=None, ssh_key_path=None, password=None, port=22, enable_compression=False)
23
- default = f"{ssh.hostname}:{ssh.run_shell(command='echo $HOME', verbose_output=False, description='Get home directory', strict_stderr=False, strict_return_code=True).op}/data/share_nfs"
24
- share_info = choose_from_options(msg="📂 Choose a share path:", options=[f"{ssh.hostname}:{item.split(' ')[0]}" for item in ssh.run_shell(command="cat /etc/exports", verbose_output=False, description='Get NFS exports', strict_stderr=False, strict_return_code=False).op.split("\n") if not item.startswith("#")] + [default], default=default, multi=False)
23
+ default = f"{ssh.hostname}:{ssh.run_shell_cmd_on_remote(command='echo $HOME', verbose_output=False, description='Get home directory', strict_stderr=False, strict_return_code=True).op}/data/share_nfs"
24
+ share_info = choose_from_options(msg="📂 Choose a share path:", options=[f"{ssh.hostname}:{item.split(' ')[0]}" for item in ssh.run_shell_cmd_on_remote(command="cat /etc/exports", verbose_output=False, description='Get NFS exports', strict_stderr=False, strict_return_code=False).op.split("\n") if not item.startswith("#")] + [default], default=default, multi=False)
25
25
  assert isinstance(share_info, str), f"❌ share_info must be a string. Got {type(share_info)}"
26
26
 
27
27
  remote_server = share_info.split(":")[0]
@@ -19,7 +19,7 @@ def main():
19
19
  tmp = choose_ssh_host(multi=False)
20
20
  assert isinstance(tmp, str)
21
21
  ssh = SSH(host=tmp, username=None, hostname=None, ssh_key_path=None, password=None, port=22, enable_compression=False)
22
- share_info = f"{ssh.username}@{ssh.hostname}:{ssh.run_shell(command='echo $HOME', verbose_output=False, description='Get home directory', strict_stderr=False, strict_return_code=True).op}/data/share_ssh"
22
+ share_info = f"{ssh.username}@{ssh.hostname}:{ssh.run_shell_cmd_on_remote(command='echo $HOME', verbose_output=False, description='Get home directory', strict_stderr=False, strict_return_code=True).op}/data/share_ssh"
23
23
  else:
24
24
  ssh = SSH(host=share_info.split(":")[0], username=None, hostname=None, ssh_key_path=None, password=None, port=22, enable_compression=False)
25
25
 
@@ -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:
@@ -28,8 +28,6 @@ Usage examples:
28
28
 
29
29
  """
30
30
 
31
- from typing import Annotated
32
- import typer
33
31
  import configparser
34
32
  from pathlib import Path
35
33
  import os
@@ -38,8 +36,7 @@ import subprocess
38
36
  import getpass
39
37
  from typing import List, Dict, Optional
40
38
  from rich.console import Console
41
- from rich.panel import Panel
42
- from rich.prompt import Prompt, Confirm
39
+ from rich.prompt import Prompt
43
40
  from rich.table import Table
44
41
 
45
42
  console = Console()
@@ -263,51 +260,6 @@ def manual_network_selection() -> bool:
263
260
  return False
264
261
 
265
262
 
266
- def main(
267
- ssid: Annotated[str, typer.Option("-n", "--ssid", help="🔗 SSID of WiFi (from config)")] = "MyPhoneHotSpot",
268
- manual: Annotated[bool, typer.Option("-m", "--manual", help="🔍 Manual network selection mode")] = False,
269
- list_: Annotated[bool, typer.Option("-l", "--list", help="📡 List available networks only")] = False,
270
- ) -> None:
271
- """Main function with fallback network selection"""
272
- console.print(Panel("📶 Welcome to the WiFi Connector Tool", title="[bold blue]WiFi Connection[/bold blue]", border_style="blue"))
273
-
274
- # If user just wants to list networks
275
- if list_:
276
- display_available_networks()
277
- return
278
-
279
- # If user wants manual mode, skip config and go straight to selection
280
- if manual:
281
- console.print("[blue]🔍 Manual network selection mode[/blue]")
282
- if manual_network_selection():
283
- console.print("[green]🎉 Successfully connected![/green]")
284
- else:
285
- console.print("[red]❌ Failed to connect[/red]")
286
- return
287
-
288
- # Try to connect using configuration first
289
- console.print(f"[blue]🔍 Attempting to connect to configured network: {ssid}[/blue]")
290
-
291
- if try_config_connection(ssid):
292
- console.print("[green]🎉 Successfully connected using configuration![/green]")
293
- return
294
-
295
- # Configuration failed, offer fallback options
296
- console.print("\n[yellow]⚠️ Configuration connection failed or not available[/yellow]")
297
-
298
- if Confirm.ask("[blue]Would you like to manually select a network?[/blue]", default=True):
299
- if manual_network_selection():
300
- console.print("[green]🎉 Successfully connected![/green]")
301
- else:
302
- console.print("[red]❌ Failed to connect[/red]")
303
- else:
304
- console.print("[blue]👋 Goodbye![/blue]")
305
-
306
-
307
- def arg_parser() -> None:
308
- typer.run(main)
309
-
310
-
311
263
  def get_current_wifi_name() -> str:
312
264
  """Get the name of the currently connected WiFi network"""
313
265
  console.print("\n[blue]🔍 Checking current WiFi connection...[/blue]")
@@ -412,7 +364,3 @@ def create_new_connection(name: str, ssid: str, password: str):
412
364
  except Exception as e:
413
365
  console.print(f"[red]❌ Unexpected error: {e}[/red]")
414
366
  raise
415
-
416
-
417
- if __name__ == "__main__":
418
- arg_parser()
@@ -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
@@ -1,4 +1,3 @@
1
- from __future__ import annotations
2
1
 
3
2
  from pathlib import Path
4
3
  from typing import Literal, Optional, cast
@@ -1,28 +1,47 @@
1
- import git
2
- from rich.console import Console
3
- from rich.panel import Panel
4
- import typer
5
1
 
6
- from machineconfig.utils.path_extended import PathExtended
7
- from machineconfig.utils.terminal import Response
8
- from machineconfig.utils.source_of_truth import CONFIG_ROOT, DEFAULTS_PATH
9
- from machineconfig.utils.code import get_uv_command_executing_python_script
10
- from pathlib import Path
11
- import platform
12
- import subprocess
13
2
  from typing import Optional, Literal, Annotated
14
3
 
15
-
16
- console = Console()
4
+ import typer
17
5
 
18
6
 
19
7
  def main(
20
8
  cloud: Annotated[Optional[str], typer.Option(..., "--cloud", "-c", help="Cloud storage profile name. If not provided, uses default from config.")] = None,
21
9
  repo: Annotated[Optional[str], typer.Option(..., "--repo", "-r", help="Path to the local repository. Defaults to current working directory.")] = None,
22
10
  message: Annotated[Optional[str], typer.Option(..., "--message", "-m", help="Commit message for local changes.")] = None,
23
- on_conflict: Annotated[Literal["ask", "push-local-merge", "overwrite-local", "stop-on-conflict", "remove-rclone-conflict"], typer.Option(..., "--on-conflict", "-oc", help="Action to take on merge conflict. Default is 'ask'.")] = "ask",
11
+ on_conflict: Annotated[Literal["ask", "a",
12
+ "push-local-merge", "p",
13
+ "overwrite-local", "o",
14
+ "stop-on-conflict", "s",
15
+ "remove-rclone-conflict", "r"
16
+ ], typer.Option(..., "--on-conflict", "-o", help="Action to take on merge conflict. Default is 'ask'.")] = "ask",
24
17
  pwd: Annotated[Optional[str], typer.Option(..., "--password", help="Password for encryption/decryption of the remote repository.")] = None,
25
18
  ):
19
+ on_conflict_mapper: dict[str, Literal["ask", "push-local-merge", "overwrite-local", "stop-on-conflict", "remove-rclone-conflict"]] = {
20
+ "a": "ask",
21
+ "ask": "ask",
22
+ "p": "push-local-merge",
23
+ "push-local-merge": "push-local-merge",
24
+ "o": "overwrite-local",
25
+ "overwrite-local": "overwrite-local",
26
+ "s": "stop-on-conflict",
27
+ "stop-on-conflict": "stop-on-conflict",
28
+ "r": "remove-rclone-conflict",
29
+ "remove-rclone-conflict": "remove-rclone-conflict",
30
+ }
31
+ on_conflict = on_conflict_mapper[on_conflict]
32
+ import git
33
+ from rich.console import Console
34
+ from rich.panel import Panel
35
+
36
+ from machineconfig.utils.path_extended import PathExtended
37
+ from machineconfig.utils.terminal import Response
38
+ from machineconfig.utils.source_of_truth import CONFIG_ROOT, DEFAULTS_PATH
39
+ from machineconfig.utils.code import get_uv_command_executing_python_script
40
+ from pathlib import Path
41
+ import platform
42
+ import subprocess
43
+ console = Console()
44
+
26
45
  if cloud is None:
27
46
  try:
28
47
  from machineconfig.utils.io import read_ini
@@ -35,10 +54,16 @@ def main(
35
54
  else:
36
55
  cloud_resolved = cloud
37
56
  repo_local_root = PathExtended.cwd() if repo is None else PathExtended(repo).expanduser().absolute()
38
- repo_local_obj = git.Repo(repo_local_root, search_parent_directories=True)
57
+ try:
58
+ repo_local_obj = git.Repo(repo_local_root, search_parent_directories=True)
59
+ except git.InvalidGitRepositoryError:
60
+ typer.echo(f"[red]Error:[/] The specified path '{repo_local_root}' is not a valid git repository.")
61
+ typer.Exit(code=1)
62
+ return ""
39
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()))
40
65
  PathExtended(CONFIG_ROOT).joinpath("remote").mkdir(parents=True, exist_ok=True)
41
- repo_remote_root = PathExtended(CONFIG_ROOT).joinpath("remote", repo_local_root.rel2home())
66
+ repo_remote_root = PathExtended(CONFIG_ROOT).joinpath("remote", local_relative_home)
42
67
  repo_remote_root.delete(sure=True)
43
68
  try:
44
69
  console.print(Panel("📥 DOWNLOADING REMOTE REPOSITORY", title_align="left", border_style="blue"))
@@ -80,7 +105,7 @@ git pull originEnc master
80
105
  uv_project_dir = f"""{str(Path.home().joinpath("code/machineconfig"))}"""
81
106
  uv_with = None
82
107
  else:
83
- uv_with = ["machineconfig>=6.81"]
108
+ uv_with = ["machineconfig>=7.98"]
84
109
  uv_project_dir = None
85
110
 
86
111
  import tempfile
@@ -111,7 +136,8 @@ git pull originEnc master
111
136
  def func2(remote_repo: str, local_repo: str, cloud: str):
112
137
  from machineconfig.scripts.python.helpers_repos.sync import delete_remote_repo_copy_and_push_local
113
138
  delete_remote_repo_copy_and_push_local(remote_repo=remote_repo, local_repo=local_repo, cloud=cloud)
114
- 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)
115
141
  program1, _pyfile1 = get_uv_command_executing_python_script(python_script=program_1_py, uv_with=uv_with, uv_project_dir=uv_project_dir)
116
142
  # ================================================================================
117
143
  option2 = "Delete local repo and replace it with remote copy:"
@@ -136,7 +162,8 @@ sudo chmod +x $HOME/dotfiles/scripts/linux -R
136
162
  inspect_repos(repo_local_root=repo_local_root, repo_remote_root=repo_remote_root)
137
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)})
138
164
  # shell_file_3 = get_shell_file_executing_python_script(python_script=program_3_py, ve_path=None, executable=executable)
139
- 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)
140
167
  program3, _pyfile3 = get_uv_command_executing_python_script(python_script=program_3_py, uv_with=uv_with, uv_project_dir=uv_project_dir)
141
168
  # ================================================================================
142
169
 
@@ -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]
@@ -100,7 +100,7 @@ def install_gource_windows(version: Optional[str] = None) -> None:
100
100
 
101
101
 
102
102
  def visualize(
103
- repo: Annotated[str, typer.Option("--repo", "-r", help="Path to git repository to visualize")] = Path.cwd().__str__(),
103
+ repo: Annotated[str, typer.Option("--repo", "-r", help="Path to git repository to visualize")] = ".",
104
104
  output_file: Annotated[Optional[Path], typer.Option("--output", "-o", help="Output video file (e.g., output.mp4). If specified, gource will render to video.")] = None,
105
105
  resolution: Annotated[str, typer.Option("--resolution", "-res", help="Video resolution (e.g., 1920x1080, 1280x720)")] = "1920x1080",
106
106
  seconds_per_day: Annotated[float, typer.Option("--seconds-per-day", "-spd", help="Speed of simulation (lower = faster)")] = 0.1,
@@ -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")