machineconfig 8.14__py3-none-any.whl → 8.50__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 (273) hide show
  1. machineconfig/cluster/remote/run_cluster.py +1 -1
  2. machineconfig/cluster/remote/run_remote.py +1 -1
  3. machineconfig/cluster/sessions_managers/utils/maker.py +10 -8
  4. machineconfig/cluster/sessions_managers/wt_local.py +1 -1
  5. machineconfig/cluster/sessions_managers/wt_local_manager.py +1 -1
  6. machineconfig/cluster/sessions_managers/zellij_local.py +1 -1
  7. machineconfig/cluster/sessions_managers/zellij_local_manager.py +1 -1
  8. machineconfig/jobs/installer/checks/check_installations.py +133 -0
  9. machineconfig/jobs/installer/checks/install_utils.py +132 -0
  10. machineconfig/jobs/installer/checks/report_utils.py +39 -0
  11. machineconfig/jobs/installer/checks/vt_utils.py +89 -0
  12. machineconfig/jobs/installer/installer_data.json +225 -140
  13. machineconfig/jobs/installer/linux_scripts/docker.sh +6 -9
  14. machineconfig/jobs/installer/package_groups.py +10 -9
  15. machineconfig/jobs/installer/python_scripts/boxes.py +1 -2
  16. machineconfig/jobs/installer/python_scripts/code.py +10 -8
  17. machineconfig/jobs/installer/python_scripts/hx.py +30 -13
  18. machineconfig/jobs/installer/python_scripts/nerfont_windows_helper.py +6 -5
  19. machineconfig/jobs/installer/python_scripts/sysabc.py +25 -19
  20. machineconfig/jobs/installer/python_scripts/yazi.py +33 -17
  21. machineconfig/jobs/scripts/powershell_scripts/cmatrix.ps1 +52 -0
  22. machineconfig/jobs/scripts/powershell_scripts/mount_ssh.ps1 +1 -1
  23. machineconfig/jobs/scripts_dynamic/a.py +413 -10
  24. machineconfig/profile/create_links.py +77 -20
  25. machineconfig/profile/create_links_export.py +63 -58
  26. machineconfig/profile/mapper_data.toml +30 -0
  27. machineconfig/profile/mapper_dotfiles.toml +253 -0
  28. machineconfig/scripts/python/agents.py +70 -172
  29. machineconfig/scripts/python/ai/initai.py +3 -1
  30. machineconfig/scripts/python/ai/scripts/__init__.py +1 -0
  31. machineconfig/scripts/python/ai/scripts/lint_and_type_check.ps1 +2 -0
  32. machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +7 -5
  33. machineconfig/scripts/python/ai/solutions/claude/claude.py +1 -1
  34. machineconfig/scripts/python/ai/solutions/cline/cline.py +1 -1
  35. machineconfig/scripts/python/ai/solutions/copilot/github_copilot.py +1 -1
  36. machineconfig/scripts/python/ai/solutions/copilot/instructions/python/dev.instructions.md +29 -0
  37. machineconfig/scripts/python/ai/solutions/crush/crush.py +1 -1
  38. machineconfig/scripts/python/ai/solutions/cursor/cursors.py +1 -1
  39. machineconfig/scripts/python/ai/solutions/gemini/gemini.py +1 -1
  40. machineconfig/scripts/python/ai/solutions/gemini/settings.json +3 -0
  41. machineconfig/scripts/python/ai/{solutions → utils}/generic.py +2 -15
  42. machineconfig/scripts/python/ai/utils/vscode_tasks.py +6 -3
  43. machineconfig/scripts/python/cloud.py +58 -11
  44. machineconfig/scripts/python/croshell.py +4 -156
  45. machineconfig/scripts/python/devops.py +57 -40
  46. machineconfig/scripts/python/devops_navigator.py +17 -3
  47. machineconfig/scripts/python/fire_jobs.py +8 -207
  48. machineconfig/scripts/python/ftpx.py +5 -225
  49. machineconfig/scripts/python/graph/cli_graph.json +8743 -0
  50. machineconfig/scripts/python/{env_manager → helper_env}/path_manager_tui.py +2 -2
  51. machineconfig/scripts/python/{env_manager → helpers/helper_env}/env_manager_tui.py +1 -1
  52. machineconfig/scripts/python/helpers/helper_env/path_manager_tui.py +228 -0
  53. machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_crush.py +1 -1
  54. machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_cursor_agents.py +1 -1
  55. machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_gemini.py +1 -1
  56. machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_qwen.py +1 -1
  57. machineconfig/scripts/python/helpers/helpers_agents/agents_impl.py +168 -0
  58. machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/fire_agents_help_launch.py +5 -5
  59. machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_copy.py +6 -6
  60. machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_mount.py +10 -5
  61. machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_sync.py +3 -3
  62. machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/helpers2.py +1 -1
  63. machineconfig/scripts/python/helpers/helpers_croshell/croshell_impl.py +225 -0
  64. machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/scheduler.py +4 -4
  65. machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/start_slidev.py +7 -6
  66. machineconfig/scripts/python/helpers/helpers_devops/backup_config.py +149 -0
  67. machineconfig/scripts/python/helpers/helpers_devops/cli_backup_retrieve.py +267 -0
  68. machineconfig/scripts/python/helpers/helpers_devops/cli_config.py +98 -0
  69. machineconfig/scripts/python/helpers/helpers_devops/cli_config_dotfile.py +274 -0
  70. machineconfig/scripts/python/helpers/helpers_devops/cli_data.py +76 -0
  71. machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_nw.py +52 -72
  72. machineconfig/scripts/python/helpers/helpers_devops/cli_repos.py +274 -0
  73. machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_self.py +40 -23
  74. machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_share_file.py +44 -30
  75. machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_share_server.py +26 -43
  76. machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_share_terminal.py +12 -6
  77. machineconfig/scripts/python/helpers/helpers_devops/cli_ssh.py +167 -0
  78. machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/devops_status.py +12 -6
  79. machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/devops_update_repos.py +1 -1
  80. machineconfig/scripts/python/{interactive.py → helpers/helpers_devops/interactive.py} +68 -52
  81. machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/run_script.py +75 -58
  82. machineconfig/scripts/python/helpers/helpers_devops/themes/choose_starship_theme.ps1 +41 -0
  83. machineconfig/scripts/python/helpers/helpers_devops/themes/choose_starship_theme.sh +48 -0
  84. machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/themes/choose_wezterm_theme.py +3 -3
  85. machineconfig/scripts/python/helpers/helpers_fire_command/fire_jobs_impl.py +233 -0
  86. machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/fire_jobs_route_helper.py +3 -3
  87. machineconfig/scripts/python/helpers/helpers_msearch/msearch_impl.py +248 -0
  88. machineconfig/scripts/python/{helpers_msearch → helpers/helpers_msearch}/scripts_linux/fzfg +4 -3
  89. machineconfig/scripts/python/helpers/helpers_msearch/scripts_linux/search_with_context.sh +48 -0
  90. machineconfig/scripts/python/{helpers_msearch → helpers/helpers_msearch}/scripts_windows/fzfg.ps1 +1 -1
  91. machineconfig/scripts/python/helpers/helpers_navigator/__init__.py +20 -0
  92. machineconfig/scripts/python/helpers/helpers_navigator/cli_graph_loader.py +234 -0
  93. machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/command_builder.py +61 -13
  94. machineconfig/scripts/python/helpers/helpers_navigator/command_detail.py +153 -0
  95. machineconfig/scripts/python/helpers/helpers_navigator/command_tree.py +45 -0
  96. machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/data_models.py +18 -11
  97. machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/main_app.py +5 -5
  98. machineconfig/scripts/python/helpers/helpers_network/__init__.py +0 -0
  99. machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/address.py +15 -17
  100. machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/address_switch.py +1 -1
  101. machineconfig/scripts/python/helpers/helpers_network/ftpx_impl.py +276 -0
  102. machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/mount_ssh.py +2 -2
  103. machineconfig/scripts/python/helpers/helpers_network/ssh_add_identity.py +73 -0
  104. machineconfig/scripts/python/helpers/helpers_network/ssh_add_ssh_key.py +175 -0
  105. machineconfig/scripts/python/helpers/helpers_network/ssh_debug_linux.py +319 -0
  106. machineconfig/scripts/python/helpers/helpers_network/ssh_debug_windows.py +275 -0
  107. machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/action.py +3 -3
  108. machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/action_helper.py +3 -3
  109. machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/cloud_repo_sync.py +117 -33
  110. machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/grource.py +3 -2
  111. machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/record.py +33 -13
  112. machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/repo_analyzer_2.py +63 -19
  113. machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/update.py +0 -6
  114. machineconfig/scripts/python/helpers/helpers_search/script_help.py +81 -0
  115. machineconfig/scripts/python/helpers/helpers_sessions/__init__.py +0 -0
  116. machineconfig/scripts/python/helpers/helpers_sessions/sessions_impl.py +186 -0
  117. machineconfig/scripts/python/{helpers_sessions → helpers/helpers_sessions}/sessions_multiprocess.py +1 -1
  118. machineconfig/scripts/python/helpers/helpers_terminal/__init__.py +0 -0
  119. machineconfig/scripts/python/helpers/helpers_terminal/terminal_impl.py +96 -0
  120. machineconfig/scripts/python/{helpers_utils → helpers/helpers_utils}/download.py +1 -1
  121. machineconfig/scripts/python/{helpers_utils → helpers/helpers_utils}/python.py +47 -26
  122. machineconfig/scripts/python/helpers/helpers_utils/specs.py +246 -0
  123. machineconfig/scripts/python/mcfg_entry.py +133 -48
  124. machineconfig/scripts/python/msearch.py +15 -61
  125. machineconfig/scripts/python/sessions.py +59 -194
  126. machineconfig/scripts/python/terminal.py +18 -96
  127. machineconfig/scripts/python/utils.py +101 -20
  128. machineconfig/settings/atuin/config.toml +294 -0
  129. machineconfig/settings/atuin/themes/catppuccin-mocha-mauve.toml +12 -0
  130. machineconfig/settings/linters/.ruff.toml +1 -0
  131. machineconfig/settings/mprocs/windows/mprocs.yaml +2 -2
  132. machineconfig/settings/shells/bash/init.sh +6 -3
  133. machineconfig/settings/shells/pwsh/init.ps1 +69 -1
  134. machineconfig/settings/shells/pwsh/search_pwsh_history.ps1 +99 -0
  135. machineconfig/settings/shells/wezterm/wezterm.lua +4 -1
  136. machineconfig/settings/shells/wt/settings.json +20 -7
  137. machineconfig/settings/shells/zsh/init.sh +25 -4
  138. machineconfig/settings/television/cable_unix/bash-history.toml +1 -1
  139. machineconfig/settings/television/cable_windows/pwsh-history.toml +1 -1
  140. machineconfig/settings/tv/config.toml +234 -0
  141. machineconfig/settings/tv/themes/catppuccin-mocha-sky.toml +22 -0
  142. machineconfig/settings/wsl/.wslconfig +5 -30
  143. machineconfig/settings/yazi/yazi_linux.toml +18 -8
  144. machineconfig/settings/zellij/layouts/st.kdl +2 -2
  145. machineconfig/settings/zellij/layouts/st2.kdl +1 -1
  146. machineconfig/setup_linux/web_shortcuts/interactive.sh +10 -10
  147. machineconfig/setup_linux/web_shortcuts/live_from_github.sh +3 -0
  148. machineconfig/setup_mac/__init__.py +0 -2
  149. machineconfig/setup_windows/__init__.py +0 -1
  150. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +14 -13
  151. machineconfig/setup_windows/web_shortcuts/live_from_github.ps1 +4 -3
  152. machineconfig/setup_windows/web_shortcuts/quick_init.ps1 +3 -3
  153. machineconfig/type_hinting/sql/__init__.py +1 -0
  154. machineconfig/type_hinting/sql/base.py +216 -0
  155. machineconfig/type_hinting/sql/core_schema.py +64 -0
  156. machineconfig/type_hinting/sql/core_schema_typeddict.py +41 -0
  157. machineconfig/type_hinting/sql/typeddict_codegen.py +222 -0
  158. machineconfig/type_hinting/typedict/__init__.py +1 -0
  159. machineconfig/type_hinting/typedict/ast_utils.py +130 -0
  160. machineconfig/type_hinting/typedict/generator_helpers.py +319 -0
  161. machineconfig/type_hinting/typedict/generators.py +231 -0
  162. machineconfig/type_hinting/typedict/polars_schema.py +24 -0
  163. machineconfig/type_hinting/typedict/polars_schema_typeddict.py +63 -0
  164. machineconfig/utils/accessories.py +24 -0
  165. machineconfig/utils/code.py +41 -13
  166. machineconfig/utils/files/ascii_art.py +10 -14
  167. machineconfig/utils/files/headers.py +3 -5
  168. machineconfig/utils/files/read.py +8 -1
  169. machineconfig/utils/installer_utils/github_release_bulk.py +11 -91
  170. machineconfig/utils/installer_utils/github_release_scraper.py +99 -0
  171. machineconfig/utils/installer_utils/install_from_url.py +1 -1
  172. machineconfig/utils/installer_utils/installer_class.py +12 -4
  173. machineconfig/utils/installer_utils/installer_cli.py +1 -15
  174. machineconfig/utils/installer_utils/installer_helper.py +2 -2
  175. machineconfig/utils/installer_utils/installer_locator_utils.py +13 -13
  176. machineconfig/utils/installer_utils/installer_runner.py +4 -4
  177. machineconfig/utils/io.py +25 -8
  178. machineconfig/utils/meta.py +6 -4
  179. machineconfig/utils/options.py +49 -19
  180. machineconfig/utils/options_utils/__init__.py +0 -0
  181. machineconfig/utils/options_utils/options_tv_linux.py +211 -0
  182. machineconfig/utils/options_utils/options_tv_windows.py +88 -0
  183. machineconfig/utils/options_utils/tv_options.py +37 -0
  184. machineconfig/utils/path_extended.py +6 -6
  185. machineconfig/utils/scheduler.py +8 -2
  186. machineconfig/utils/schemas/fire_agents/fire_agents_input.py +1 -1
  187. machineconfig/utils/source_of_truth.py +6 -1
  188. machineconfig/utils/ssh.py +69 -18
  189. machineconfig/utils/ssh_utils/abc.py +1 -1
  190. machineconfig/utils/ssh_utils/copy_from_here.py +17 -12
  191. machineconfig/utils/ssh_utils/utils.py +21 -5
  192. machineconfig/utils/ssh_utils/wsl.py +107 -170
  193. machineconfig/utils/ssh_utils/wsl_helper.py +217 -0
  194. machineconfig/utils/upgrade_packages.py +4 -8
  195. {machineconfig-8.14.dist-info → machineconfig-8.50.dist-info}/METADATA +29 -22
  196. {machineconfig-8.14.dist-info → machineconfig-8.50.dist-info}/RECORD +251 -211
  197. machineconfig/jobs/installer/check_installations.py +0 -248
  198. machineconfig/profile/backup.toml +0 -49
  199. machineconfig/profile/mapper.toml +0 -263
  200. machineconfig/scripts/python/helpers_devops/cli_config.py +0 -105
  201. machineconfig/scripts/python/helpers_devops/cli_config_dotfile.py +0 -89
  202. machineconfig/scripts/python/helpers_devops/cli_data.py +0 -25
  203. machineconfig/scripts/python/helpers_devops/cli_repos.py +0 -208
  204. machineconfig/scripts/python/helpers_devops/devops_backup_retrieve.py +0 -80
  205. machineconfig/scripts/python/helpers_devops/themes/choose_starship_theme.bash +0 -3
  206. machineconfig/scripts/python/helpers_navigator/__init__.py +0 -20
  207. machineconfig/scripts/python/helpers_navigator/command_detail.py +0 -44
  208. machineconfig/scripts/python/helpers_navigator/command_tree.py +0 -620
  209. machineconfig/scripts/python/helpers_network/ssh_add_identity.py +0 -116
  210. machineconfig/scripts/python/helpers_network/ssh_add_ssh_key.py +0 -153
  211. machineconfig/scripts/python/helpers_network/ssh_debug_linux.py +0 -391
  212. machineconfig/scripts/python/helpers_network/ssh_debug_windows.py +0 -338
  213. machineconfig/scripts/python/helpers_repos/entrypoint.py +0 -77
  214. machineconfig/setup_mac/ssh/openssh_setup.sh +0 -114
  215. machineconfig/setup_windows/ssh/add-sshkey.ps1 +0 -29
  216. machineconfig/setup_windows/ssh/openssh-server.ps1 +0 -37
  217. machineconfig/utils/options_tv.py +0 -119
  218. machineconfig/utils/tst.py +0 -20
  219. /machineconfig/{scripts/python/helpers_agents → jobs/installer/checks}/__init__.py +0 -0
  220. /machineconfig/scripts/python/ai/{solutions/_shared.py → utils/shared.py} +0 -0
  221. /machineconfig/scripts/python/{helpers_agents/agentic_frameworks → graph}/__init__.py +0 -0
  222. /machineconfig/scripts/python/{helpers_cloud → helpers}/__init__.py +0 -0
  223. /machineconfig/scripts/python/{env_manager → helpers/helper_env}/__init__.py +0 -0
  224. /machineconfig/scripts/python/{env_manager → helpers/helper_env}/path_manager_backend.py +0 -0
  225. /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_agents}/__init__.py +0 -0
  226. /machineconfig/scripts/python/{helpers_devops → helpers/helpers_agents/agentic_frameworks}/__init__.py +0 -0
  227. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_crush.json +0 -0
  228. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/fire_agents_help_search.py +0 -0
  229. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/fire_agents_helper_types.py +0 -0
  230. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/fire_agents_load_balancer.py +0 -0
  231. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/configs/aichat/config.yaml +0 -0
  232. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/configs/aider/.aider.conf.yml +0 -0
  233. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/configs/copilot/config.yml +0 -0
  234. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/configs/crush/crush.json +0 -0
  235. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/configs/gemini/settings.json +0 -0
  236. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/privacy.py +0 -0
  237. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/templates/prompt.txt +0 -0
  238. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/templates/template.ps1 +0 -0
  239. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/templates/template.sh +0 -0
  240. /machineconfig/scripts/python/{helpers_devops/themes → helpers/helpers_cloud}/__init__.py +0 -0
  241. /machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_helpers.py +0 -0
  242. /machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/helpers5.py +0 -0
  243. /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_croshell}/__init__.py +0 -0
  244. /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/crosh.py +0 -0
  245. /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/pomodoro.py +0 -0
  246. /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/viewer.py +0 -0
  247. /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/viewer_template.py +0 -0
  248. /machineconfig/scripts/python/{helpers_network → helpers/helpers_devops}/__init__.py +0 -0
  249. /machineconfig/scripts/python/{helpers_sessions → helpers/helpers_devops/themes}/__init__.py +0 -0
  250. /machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/themes/choose_pwsh_theme.ps1 +0 -0
  251. /machineconfig/scripts/python/{helpers_devops/themes/choose_starship_theme.ps1 → helpers/helpers_fire_command/__init__.py} +0 -0
  252. /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/cloud_manager.py +0 -0
  253. /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/f.py +0 -0
  254. /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/file_wrangler.py +0 -0
  255. /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/fire_jobs_args_helper.py +0 -0
  256. /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/fire_jobs_streamlit_helper.py +0 -0
  257. /machineconfig/scripts/python/{helpers_msearch → helpers/helpers_msearch}/__init__.py +0 -0
  258. /machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/search_bar.py +0 -0
  259. /machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/mount_nfs.py +0 -0
  260. /machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/mount_nw_drive.py +0 -0
  261. /machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/onetimeshare.py +0 -0
  262. /machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/wifi_conn.py +0 -0
  263. /machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/clone.py +0 -0
  264. /machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/repo_analyzer_1.py +0 -0
  265. /machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/sync.py +0 -0
  266. /machineconfig/scripts/python/helpers/{ast_search.py → helpers_search/ast_search.py} +0 -0
  267. /machineconfig/scripts/python/helpers/{qr_code.py → helpers_search/qr_code.py} +0 -0
  268. /machineconfig/scripts/python/helpers/{repo_rag.py → helpers_search/repo_rag.py} +0 -0
  269. /machineconfig/scripts/python/helpers/{symantic_search.py → helpers_search/symantic_search.py} +0 -0
  270. /machineconfig/scripts/python/{helpers_utils → helpers/helpers_utils}/pdf.py +0 -0
  271. {machineconfig-8.14.dist-info → machineconfig-8.50.dist-info}/WHEEL +0 -0
  272. {machineconfig-8.14.dist-info → machineconfig-8.50.dist-info}/entry_points.txt +0 -0
  273. {machineconfig-8.14.dist-info → machineconfig-8.50.dist-info}/top_level.txt +0 -0
@@ -6,7 +6,7 @@ capturing all user inputs collected during interactive execution.
6
6
 
7
7
  from pathlib import Path
8
8
  from typing import TypedDict, Literal, NotRequired
9
- from machineconfig.scripts.python.helpers_agents.fire_agents_help_launch import AGENTS
9
+ from machineconfig.scripts.python.helpers.helpers_agents.fire_agents_help_launch import AGENTS
10
10
 
11
11
  SEARCH_STRATEGIES = Literal["file_path", "keyword_search", "filename_pattern"]
12
12
 
@@ -5,7 +5,12 @@ Utils
5
5
  import machineconfig
6
6
  from pathlib import Path
7
7
 
8
- EXCLUDE_DIRS = [".links", ".ai", ".venv", ".git", ".idea", ".vscode", "node_modules", "__pycache__", ".mypy_cache"]
8
+ EXCLUDE_DIRS = [".links", "notebooks",
9
+ "GEMINI.md", "CLAUDE.md", "CRUSH.md", "AGENTS.md",
10
+ ".cursor", ".clinerules", ".github/instructions", ".github/agents", ".github/prompts",
11
+ ".ai",
12
+ ".venv", ".git", ".idea", ".vscode", "node_modules", "__pycache__", ".mypy_cache"
13
+ ]
9
14
  LIBRARY_ROOT = Path(machineconfig.__file__).resolve().parent
10
15
 
11
16
  CONFIG_ROOT = Path.home().joinpath(".config/machineconfig")
@@ -2,7 +2,7 @@ from typing import Callable, Optional, Any, cast, Union, Literal
2
2
  import os
3
3
  from pathlib import Path
4
4
  import platform
5
- from machineconfig.scripts.python.helpers_utils.python import MachineSpecs
5
+ from machineconfig.scripts.python.helpers.helpers_utils.python import MachineSpecs
6
6
  from machineconfig.utils.code import get_uv_command
7
7
  import rich.console
8
8
  from machineconfig.utils.terminal import Response
@@ -14,7 +14,6 @@ from machineconfig.utils.ssh_utils.abc import DEFAULT_PICKLE_SUBDIR
14
14
  class SSH:
15
15
  @staticmethod
16
16
  def from_config_file(host: str) -> "SSH":
17
- """Create SSH instance from SSH config file entry."""
18
17
  return SSH(host=host, username=None, hostname=None, ssh_key_path=None, password=None, port=22, enable_compression=False)
19
18
 
20
19
  def __init__(
@@ -148,8 +147,10 @@ class SSH:
148
147
  def view_bar(self, transferred: int, total: int) -> None:
149
148
  if self.progress and self.task is not None:
150
149
  self.progress.update(self.task, completed=transferred, total=total)
150
+
151
151
  self.tqdm_wrap = RichProgressWrapper
152
- from machineconfig.scripts.python.helpers_utils.python import get_machine_specs
152
+ from machineconfig.scripts.python.helpers.helpers_utils.python import get_machine_specs
153
+
153
154
  self.local_specs: MachineSpecs = get_machine_specs()
154
155
  resp = self.run_shell_cmd_on_remote(
155
156
  command="""~/.local/bin/utils get-machine-specs """,
@@ -160,6 +161,7 @@ class SSH:
160
161
  )
161
162
  json_str = resp.op
162
163
  import ast
164
+
163
165
  self.remote_specs: MachineSpecs = cast(MachineSpecs, ast.literal_eval(json_str))
164
166
  self.terminal_responses: list[Response] = []
165
167
 
@@ -177,10 +179,22 @@ class SSH:
177
179
  local_console = rich.console.Console(file=local_buffer, width=40)
178
180
  remote_console = rich.console.Console(file=remote_buffer, width=40)
179
181
  inspect(
180
- type("LocalInfo", (object,), dict(self.local_specs))(), value=False, title="SSHing From", docs=False, dunder=False, sort=False, console=local_console
182
+ type("LocalInfo", (object,), dict(self.local_specs))(),
183
+ value=False,
184
+ title="SSHing From",
185
+ docs=False,
186
+ dunder=False,
187
+ sort=False,
188
+ console=local_console,
181
189
  )
182
190
  inspect(
183
- type("RemoteInfo", (object,), dict(self.remote_specs))(), value=False, title="SSHing To", docs=False, dunder=False, sort=False, console=remote_console
191
+ type("RemoteInfo", (object,), dict(self.remote_specs))(),
192
+ value=False,
193
+ title="SSHing To",
194
+ docs=False,
195
+ dunder=False,
196
+ sort=False,
197
+ console=remote_console,
184
198
  )
185
199
  local_lines = local_buffer.getvalue().split("\n")
186
200
  remote_lines = remote_buffer.getvalue().split("\n")
@@ -215,18 +229,40 @@ class SSH:
215
229
  self.copy_from_here(source_path="~/.ssh/id_rsa.pub", target_rel2home=None, compress_with_zip=False, recursive=False, overwrite_existing=False)
216
230
  if self.remote_specs["system"] != "Windows":
217
231
  raise RuntimeError("send_ssh_key is only supported for Windows remote machines")
218
- code_url = "https://raw.githubusercontent.com/thisismygitrepo/machineconfig/refs/heads/main/src/machineconfig/setup_windows/ssh/openssh-server_add-sshkey.ps1"
219
- import urllib.request
220
- with urllib.request.urlopen(code_url) as response:
221
- code = response.read().decode("utf-8")
222
- return self.run_shell_cmd_on_remote(command=code, verbose_output=True, description="", strict_stderr=False, strict_return_code=False)
232
+ python_code = '''
233
+ from pathlib import Path
234
+ import subprocess
235
+ sshd_dir = Path("C:/ProgramData/ssh")
236
+ admin_auth_keys = sshd_dir / "administrators_authorized_keys"
237
+ sshd_config = sshd_dir / "sshd_config"
238
+ pubkey_path = Path.home() / ".ssh" / "id_rsa.pub"
239
+ key_content = pubkey_path.read_text(encoding="utf-8").strip()
240
+ if admin_auth_keys.exists():
241
+ existing = admin_auth_keys.read_text(encoding="utf-8")
242
+ if not existing.endswith("\\n"):
243
+ existing += "\\n"
244
+ admin_auth_keys.write_text(existing + key_content + "\\n", encoding="utf-8")
245
+ else:
246
+ admin_auth_keys.write_text(key_content + "\\n", encoding="utf-8")
247
+ icacls_cmd = f'icacls "{admin_auth_keys}" /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F"'
248
+ subprocess.run(icacls_cmd, shell=True, check=True)
249
+ if sshd_config.exists():
250
+ config_text = sshd_config.read_text(encoding="utf-8")
251
+ config_text = config_text.replace("#PubkeyAuthentication", "PubkeyAuthentication")
252
+ sshd_config.write_text(config_text, encoding="utf-8")
253
+ subprocess.run("Restart-Service sshd -Force", shell=True, check=True)
254
+ print("SSH key added successfully")
255
+ '''
256
+ return self.run_py_remotely(python_code=python_code, uv_with=None, uv_project_dir=None, description="Adding SSH key to Windows remote", verbose_output=True, strict_stderr=False, strict_return_code=False)
223
257
 
224
258
  def get_remote_repr(self, add_machine: bool = False) -> str:
225
259
  return f"{self.username}@{self.hostname}:{self.port}" + (
226
260
  f" [{self.remote_specs['system']}][{self.remote_specs['distro']}]" if add_machine else ""
227
261
  )
262
+
228
263
  def get_local_repr(self, add_machine: bool = False) -> str:
229
264
  import getpass
265
+
230
266
  return f"{getpass.getuser()}@{platform.node()}" + (f" [{platform.system()}][{self.local_specs['distro']}]" if add_machine else "")
231
267
 
232
268
  def get_ssh_conn_str(self, command: str) -> str:
@@ -246,7 +282,9 @@ class SSH:
246
282
  res.output.returncode = os.system(command)
247
283
  return res
248
284
 
249
- def run_shell_cmd_on_remote(self, command: str, verbose_output: bool, description: str, strict_stderr: bool, strict_return_code: bool) -> Response:
285
+ def run_shell_cmd_on_remote(
286
+ self, command: str, verbose_output: bool, description: str, strict_stderr: bool, strict_return_code: bool
287
+ ) -> Response:
250
288
  raw = self.ssh.exec_command(command)
251
289
  res = Response(stdin=raw[0], stdout=raw[1], stderr=raw[2], cmd=command, desc=description) # type: ignore
252
290
  if verbose_output:
@@ -301,8 +339,7 @@ class SSH:
301
339
  )
302
340
 
303
341
  def run_lambda_function(self, func: Callable[..., Any], import_module: bool, uv_with: Optional[list[str]], uv_project_dir: Optional[str]):
304
- command = lambda_to_python_script(func,
305
- in_global=True, import_module=import_module)
342
+ command = lambda_to_python_script(func, in_global=True, import_module=import_module)
306
343
  # turns ou that the code below for some reason runs but zellij doesn't start, looks like things are assigned to different user.
307
344
  # return self.run_py(python_code=command, uv_with=uv_with, uv_project_dir=uv_project_dir,
308
345
  # description=f"run_py_func {func.__name__} on {self.get_remote_repr(add_machine=False)}",
@@ -334,25 +371,39 @@ class SSH:
334
371
 
335
372
  def create_parent_dir_and_check_if_exists(self, path_rel2home: str, overwrite_existing: bool) -> None:
336
373
  from machineconfig.utils.ssh_utils.utils import create_dir_and_check_if_exists
374
+
337
375
  return create_dir_and_check_if_exists(self, path_rel2home=path_rel2home, overwrite_existing=overwrite_existing)
338
376
 
339
377
  def check_remote_is_dir(self, source_path: Union[str, Path]) -> bool:
340
378
  from machineconfig.utils.ssh_utils.utils import check_remote_is_dir
379
+
341
380
  return check_remote_is_dir(self, source_path=source_path)
342
381
 
343
382
  def expand_remote_path(self, source_path: Union[str, Path]) -> str:
344
383
  from machineconfig.utils.ssh_utils.utils import expand_remote_path
384
+
345
385
  return expand_remote_path(self, source_path=source_path)
346
386
 
347
- def copy_from_here(self, source_path: str, target_rel2home: Optional[str], compress_with_zip: bool, recursive: bool, overwrite_existing: bool) -> None:
387
+ def copy_from_here(
388
+ self, source_path: str, target_rel2home: Optional[str], compress_with_zip: bool, recursive: bool, overwrite_existing: bool
389
+ ) -> None:
348
390
  from machineconfig.utils.ssh_utils.copy_from_here import copy_from_here
349
- return copy_from_here(self, source_path=source_path, target_rel2home=target_rel2home, compress_with_zip=compress_with_zip, recursive=recursive, overwrite_existing=overwrite_existing)
350
391
 
351
- def copy_to_here(self, source: Union[str, Path], target: Optional[Union[str, Path]], compress_with_zip: bool, recursive: bool, internal_call: bool = False) -> None:
392
+ return copy_from_here(
393
+ self,
394
+ source_path=source_path,
395
+ target_rel2home=target_rel2home,
396
+ compress_with_zip=compress_with_zip,
397
+ recursive=recursive,
398
+ overwrite_existing=overwrite_existing,
399
+ )
400
+
401
+ def copy_to_here(
402
+ self, source: Union[str, Path], target: Optional[Union[str, Path]], compress_with_zip: bool, recursive: bool, internal_call: bool = False
403
+ ) -> None:
352
404
  from machineconfig.utils.ssh_utils.copy_to_here import copy_to_here
353
- return copy_to_here(self, source=source, target=target, compress_with_zip=compress_with_zip, recursive=recursive, internal_call=internal_call)
354
405
 
355
-
406
+ return copy_to_here(self, source=source, target=target, compress_with_zip=compress_with_zip, recursive=recursive, internal_call=internal_call)
356
407
 
357
408
 
358
409
  if __name__ == "__main__":
@@ -1,5 +1,5 @@
1
1
 
2
2
 
3
- MACHINECONFIG_VERSION = "machineconfig>=8.14"
3
+ MACHINECONFIG_VERSION = "machineconfig>=8.50"
4
4
  DEFAULT_PICKLE_SUBDIR = "tmp_results/tmp_scripts/ssh"
5
5
 
@@ -1,12 +1,21 @@
1
1
 
2
2
 
3
- from typing import Optional
4
- from pathlib import Path
3
+ from typing import Optional, TYPE_CHECKING
4
+ from pathlib import Path, PurePosixPath, PureWindowsPath
5
5
  from machineconfig.utils.accessories import randstr
6
6
  from machineconfig.utils.meta import lambda_to_python_script
7
7
  from machineconfig.utils.ssh_utils.abc import DEFAULT_PICKLE_SUBDIR
8
8
  from machineconfig.utils.code import get_uv_command
9
9
 
10
+ if TYPE_CHECKING:
11
+ from machineconfig.utils.ssh import SSH
12
+
13
+
14
+ def _build_remote_path(self: "SSH", home_dir: str, rel_path: str) -> str:
15
+ if self.remote_specs["system"] == "Windows":
16
+ return str(PureWindowsPath(home_dir) / rel_path)
17
+ return str(PurePosixPath(home_dir) / PurePosixPath(rel_path.replace("\\", "/")))
18
+
10
19
 
11
20
  def copy_from_here(
12
21
  self: "SSH", source_path: str, target_rel2home: Optional[str], compress_with_zip: bool, recursive: bool, overwrite_existing: bool
@@ -54,15 +63,14 @@ def copy_from_here(
54
63
  target_rel2home = target_rel2home + ".zip"
55
64
  if Path(target_rel2home).parent.as_posix() not in {"", "."}:
56
65
  self.create_parent_dir_and_check_if_exists(path_rel2home=target_rel2home, overwrite_existing=overwrite_existing)
57
- print(f"""📤 [SFTP UPLOAD] Sending file: {repr(source_obj)} ==> Remote Path: {target_rel2home}""")
66
+ remote_target_full = _build_remote_path(self, self.remote_specs["home_dir"], target_rel2home)
67
+ print(f"""📤 [SFTP UPLOAD] Sending file: {repr(source_obj)} ==> Remote Path: {remote_target_full}""")
58
68
  try:
59
69
  with self.tqdm_wrap(ascii=True, unit="b", unit_scale=True) as pbar:
60
70
  if self.sftp is None: # type: ignore[unreachable]
61
71
  raise RuntimeError(f"SFTP connection lost for {self.hostname}")
62
- print(f"Uploading {source_obj} to\n{Path(self.remote_specs['home_dir']).joinpath(target_rel2home)}")
63
- self.sftp.put(
64
- localpath=str(source_obj), remotepath=str(Path(self.remote_specs["home_dir"]).joinpath(target_rel2home)), callback=pbar.view_bar
65
- )
72
+ print(f"Uploading {source_obj} to\n{remote_target_full}")
73
+ self.sftp.put(localpath=str(source_obj), remotepath=remote_target_full, callback=pbar.view_bar)
66
74
  except Exception:
67
75
  if compress_with_zip and source_obj.exists() and str(source_obj).endswith("_archive.zip"):
68
76
  source_obj.unlink()
@@ -83,12 +91,9 @@ def copy_from_here(
83
91
  archive_handle.extractall(extraction_directory)
84
92
  archive_path.unlink()
85
93
 
94
+ remote_zip_path = _build_remote_path(self, self.remote_specs["home_dir"], target_rel2home)
86
95
  command = lambda_to_python_script(
87
- lambda: unzip_archive(
88
- zip_file_path=str(Path(self.remote_specs["home_dir"]).joinpath(target_rel2home)), overwrite_flag=overwrite_existing
89
- ),
90
- in_global=True,
91
- import_module=False,
96
+ lambda: unzip_archive(zip_file_path=remote_zip_path, overwrite_flag=overwrite_existing), in_global=True, import_module=False
92
97
  )
93
98
  tmp_py_file = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/create_target_dir_{randstr()}.py")
94
99
  tmp_py_file.parent.mkdir(parents=True, exist_ok=True)
@@ -1,15 +1,30 @@
1
1
 
2
- from pathlib import Path
2
+ from pathlib import Path, PurePosixPath, PureWindowsPath
3
3
  from machineconfig.utils.accessories import randstr
4
4
  from machineconfig.utils.meta import lambda_to_python_script
5
5
  from machineconfig.utils.ssh_utils.abc import MACHINECONFIG_VERSION, DEFAULT_PICKLE_SUBDIR
6
6
  from machineconfig.utils.code import get_uv_command
7
- from typing import Union
7
+ from typing import Union, TYPE_CHECKING
8
+
9
+ if TYPE_CHECKING:
10
+ from machineconfig.utils.ssh import SSH
11
+
12
+
13
+ def _build_remote_path(self: "SSH", home_dir: str, rel_path: str) -> str:
14
+ if self.remote_specs["system"] == "Windows":
15
+ return str(PureWindowsPath(home_dir) / rel_path)
16
+ return str(PurePosixPath(home_dir) / PurePosixPath(rel_path.replace("\\", "/")))
17
+
18
+
19
+ def _normalize_rel_path_for_remote(self: "SSH", rel_path: str) -> str:
20
+ if self.remote_specs["system"] == "Windows":
21
+ return str(PureWindowsPath(rel_path))
22
+ return rel_path.replace("\\", "/")
8
23
 
9
24
 
10
25
  def create_dir_and_check_if_exists(self: "SSH", path_rel2home: str, overwrite_existing: bool) -> None:
11
26
  """Helper to create a directory on remote machine and return its path."""
12
-
27
+ path_rel2home_normalized = _normalize_rel_path_for_remote(self, path_rel2home)
13
28
  def create_target_dir(target_rel2home: str, overwrite: bool):
14
29
  from pathlib import Path
15
30
  import shutil
@@ -26,7 +41,7 @@ def create_dir_and_check_if_exists(self: "SSH", path_rel2home: str, overwrite_ex
26
41
  print(f"Creating directory for path: {target_path_abs}")
27
42
  target_path_abs.parent.mkdir(parents=True, exist_ok=True)
28
43
  command = lambda_to_python_script(
29
- lambda: create_target_dir(target_rel2home=path_rel2home, overwrite=overwrite_existing),
44
+ lambda: create_target_dir(target_rel2home=path_rel2home_normalized, overwrite=overwrite_existing),
30
45
  in_global=True, import_module=False
31
46
  )
32
47
  tmp_py_file = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/create_target_dir_{randstr()}.py")
@@ -34,7 +49,8 @@ def create_dir_and_check_if_exists(self: "SSH", path_rel2home: str, overwrite_ex
34
49
  tmp_py_file.write_text(command, encoding="utf-8")
35
50
  assert self.sftp is not None
36
51
  tmp_remote_path = ".tmp_pyfile.py"
37
- self.sftp.put(localpath=str(tmp_py_file), remotepath=str(Path(self.remote_specs["home_dir"]).joinpath(tmp_remote_path)))
52
+ remote_tmp_full = _build_remote_path(self, self.remote_specs["home_dir"], tmp_remote_path)
53
+ self.sftp.put(localpath=str(tmp_py_file), remotepath=remote_tmp_full)
38
54
  resp = self.run_shell_cmd_on_remote(
39
55
  command=f"""{get_uv_command(platform=self.remote_specs['system'])} run python {tmp_remote_path}""",
40
56
  verbose_output=False,
@@ -1,167 +1,35 @@
1
- import os
2
1
  import platform
3
- import stat
2
+ import re
4
3
  import shutil
5
4
  import subprocess
6
- from pathlib import Path, PureWindowsPath
7
-
8
-
9
- def _ensure_relative_path(requested: Path | str) -> Path:
10
- path = Path(requested)
11
- if path.is_absolute():
12
- raise ValueError("paths must be relative to the home directory")
13
- if any(part == ".." for part in path.parts):
14
- raise ValueError("paths must stay within the home directory")
15
- return path
16
-
17
-
18
- def _remove_path(path: Path) -> None:
19
- if path.is_symlink() or path.is_file():
20
- path.unlink()
21
- return
22
- shutil.rmtree(path)
23
-
24
-
25
- def _ensure_wsl_environment() -> None:
26
- if os.environ.get("WSL_DISTRO_NAME"):
27
- return
28
- if "microsoft" in platform.release().lower():
29
- return
30
- raise RuntimeError("copy_when_inside_wsl must run inside WSL")
31
-
32
-
33
- def _ensure_windows_environment() -> None:
34
- if os.name != "nt":
35
- raise RuntimeError("copy_when_inside_windows must run inside Windows")
36
- if os.environ.get("WSL_DISTRO_NAME"):
37
- raise RuntimeError("copy_when_inside_windows must run inside Windows")
38
-
39
-
40
- def _infer_windows_home_from_permissions() -> Path:
41
- base_dir = Path("/mnt/c/Users")
42
- try:
43
- entries = list(base_dir.iterdir())
44
- except FileNotFoundError as exc:
45
- raise RuntimeError("unable to find /mnt/c/Users") from exc
46
- candidates: list[Path] = []
47
- for entry in entries:
48
- if not entry.is_dir():
49
- continue
50
- if entry.name.lower() == "public" or entry.name.lower() == "all users":
51
- continue
52
- try:
53
- mode = stat.S_IMODE(entry.stat().st_mode)
54
- except OSError:
55
- continue
56
- if mode == 0o777:
57
- candidates.append(entry)
58
- if len(candidates) != 1:
59
- options = ", ".join(sorted(candidate.name for candidate in candidates)) or "none"
60
- raise RuntimeError(f"unable to infer Windows home directory (candidates: {options})")
61
- return candidates[0]
62
-
63
-
64
- def _resolve_windows_home_from_wsl() -> Path:
65
- user_profile = os.environ.get("USERPROFILE")
66
- if user_profile:
67
- windows_path = PureWindowsPath(user_profile)
68
- drive = windows_path.drive
69
- if drive:
70
- drive_letter = drive.rstrip(":").lower()
71
- tail = Path(*windows_path.parts[1:])
72
- candidate = Path("/mnt") / drive_letter / tail
73
- if candidate.exists():
74
- return candidate
75
- return _infer_windows_home_from_permissions()
76
-
77
-
78
- def _decode_wsl_output(raw_bytes: bytes) -> str:
79
- try:
80
- return raw_bytes.decode("utf-16-le")
81
- except UnicodeDecodeError:
82
- return raw_bytes.decode()
83
-
84
-
85
- def _get_single_wsl_distribution() -> str:
86
- process = subprocess.run(["wsl.exe", "-l"], capture_output=True, text=False, check=True)
87
- stdout = _decode_wsl_output(process.stdout).replace("\ufeff", "")
88
- distributions: list[str] = []
89
- for raw_line in stdout.splitlines():
90
- line = raw_line.strip()
91
- if not line or line.lower().startswith("windows subsystem for linux"):
92
- continue
93
- normalized = line.lstrip("* ").replace("(Default)", "").strip()
94
- if normalized:
95
- distributions.append(normalized)
96
- if len(distributions) != 1:
97
- raise RuntimeError("unable to pick a single WSL distribution")
98
- return distributions[0]
99
-
100
-
101
- def _resolve_wsl_home_on_windows() -> Path:
102
- distribution = _get_single_wsl_distribution()
103
- home_root = Path(rf"\\wsl$\{distribution}\home")
104
- try:
105
- entries = list(home_root.iterdir())
106
- except FileNotFoundError as exc:
107
- raise RuntimeError(f"unable to locate WSL home directories for {distribution}") from exc
108
- except OSError as exc:
109
- raise RuntimeError(f"unable to inspect WSL home directories for {distribution}") from exc
110
- user_dirs = [entry for entry in entries if entry.is_dir()]
111
- if len(user_dirs) != 1:
112
- options = ", ".join(sorted(entry.name for entry in user_dirs)) or "none"
113
- raise RuntimeError(f"unable to infer WSL user directory (candidates: {options})")
114
- return user_dirs[0]
115
-
116
-
117
- def _quote_for_powershell(path: Path) -> str:
118
- return "'" + str(path).replace("'", "''") + "'"
119
-
120
-
121
- def _run_windows_copy_command(source_path: Path, target_path: Path) -> None:
122
- source_is_dir = source_path.is_dir()
123
- parent_literal = _quote_for_powershell(target_path.parent)
124
- source_literal = _quote_for_powershell(source_path)
125
- target_literal = _quote_for_powershell(target_path)
126
- script = (
127
- "$ErrorActionPreference = 'Stop'; "
128
- f"New-Item -ItemType Directory -Path {parent_literal} -Force | Out-Null; "
129
- f"Copy-Item -LiteralPath {source_literal} -Destination {target_literal}"
130
- f"{' -Recurse' if source_is_dir else ''} -Force"
131
- )
132
- print(f"Copying {source_path} to {target_path}")
133
- subprocess.run(
134
- ["powershell.exe", "-NoLogo", "-NoProfile", "-Command", script],
135
- check=True,
136
- )
137
-
138
-
139
- def _ensure_symlink(link_path: Path, target_path: Path) -> None:
140
- if not target_path.exists():
141
- raise FileNotFoundError(target_path)
142
- if link_path.is_symlink():
143
- existing_target = Path(os.path.realpath(link_path))
144
- desired_target = Path(os.path.realpath(target_path))
145
- if os.path.normcase(str(existing_target)) == os.path.normcase(str(desired_target)):
146
- return
147
- link_path.unlink()
148
- elif link_path.exists():
149
- raise FileExistsError(link_path)
150
- link_path.symlink_to(target_path, target_is_directory=True)
151
-
152
-
153
- def copy_when_inside_wsl(source: Path | str, target: Path | str, overwrite: bool) -> None:
154
- _ensure_wsl_environment()
155
- source_relative = _ensure_relative_path(source)
156
- target_relative = _ensure_relative_path(target)
5
+ from pathlib import Path
6
+
7
+ from machineconfig.utils.ssh_utils.wsl_helper import (
8
+ ensure_relative_path,
9
+ remove_path,
10
+ ensure_wsl_environment,
11
+ ensure_windows_environment,
12
+ ensure_linux_environment,
13
+ resolve_windows_home_from_wsl,
14
+ resolve_wsl_home_on_windows,
15
+ run_windows_copy_command,
16
+ ensure_symlink,
17
+ normalize_port_spec_for_firewall,
18
+ )
19
+
20
+
21
+ def copy_when_inside_wsl(source: Path | str, target: Path | str, overwrite: bool, windows_username: str | None) -> None:
22
+ ensure_wsl_environment()
23
+ source_relative = ensure_relative_path(source)
24
+ target_relative = ensure_relative_path(target)
157
25
  source_path = Path.home() / source_relative
158
- target_path = _resolve_windows_home_from_wsl() / target_relative
26
+ target_path = resolve_windows_home_from_wsl(windows_username) / target_relative
159
27
  if not source_path.exists():
160
28
  raise FileNotFoundError(source_path)
161
29
  if target_path.exists():
162
30
  if not overwrite:
163
31
  raise FileExistsError(target_path)
164
- _remove_path(target_path)
32
+ remove_path(target_path)
165
33
  target_path.parent.mkdir(parents=True, exist_ok=True)
166
34
  if source_path.is_dir():
167
35
  shutil.copytree(source_path, target_path, dirs_exist_ok=False)
@@ -171,40 +39,109 @@ def copy_when_inside_wsl(source: Path | str, target: Path | str, overwrite: bool
171
39
 
172
40
 
173
41
  def copy_when_inside_windows(source: Path | str, target: Path | str, overwrite: bool) -> None:
174
- _ensure_windows_environment()
175
- source_relative = _ensure_relative_path(source)
176
- target_relative = _ensure_relative_path(target)
42
+ ensure_windows_environment()
43
+ source_relative = ensure_relative_path(source)
44
+ target_relative = ensure_relative_path(target)
177
45
  source_path = Path.home() / source_relative
178
- target_path = _resolve_wsl_home_on_windows() / target_relative
46
+ target_path = resolve_wsl_home_on_windows() / target_relative
179
47
  if not source_path.exists():
180
48
  raise FileNotFoundError(source_path)
181
49
  if target_path.exists():
182
50
  if not overwrite:
183
51
  raise FileExistsError(target_path)
184
- _remove_path(target_path)
185
- _run_windows_copy_command(source_path, target_path)
52
+ remove_path(target_path)
53
+ run_windows_copy_command(source_path, target_path)
186
54
 
187
55
 
188
- def link_wsl_and_windows() -> None:
56
+ def link_wsl_and_windows(windows_username: str | None) -> None:
189
57
  system = platform.system()
190
58
  if system == "Darwin":
191
59
  raise RuntimeError("link_wsl_and_windows is not designed for macOS")
192
60
  try:
193
- _ensure_wsl_environment()
61
+ ensure_wsl_environment()
194
62
  except RuntimeError:
195
63
  try:
196
- _ensure_windows_environment()
64
+ ensure_windows_environment()
197
65
  except RuntimeError as exc:
198
66
  raise RuntimeError("link_wsl_and_windows must run inside Windows or WSL") from exc
199
- target_path = _resolve_wsl_home_on_windows()
67
+ print("🔗 Running inside Windows, linking to WSL home...")
68
+ target_path = resolve_wsl_home_on_windows()
200
69
  link_path = Path.home() / "wsl"
201
- _ensure_symlink(link_path, target_path)
70
+ created = ensure_symlink(link_path, target_path)
71
+ if created:
72
+ print(f"✅ Created symlink: {link_path} -> {target_path}")
73
+ else:
74
+ print(f"✅ Symlink already exists: {link_path} -> {target_path}")
202
75
  return
203
- target_path = _resolve_windows_home_from_wsl()
76
+ print("🔗 Running inside WSL, linking to Windows home...")
77
+ target_path = resolve_windows_home_from_wsl(windows_username)
204
78
  link_path = Path.home() / "win"
205
- _ensure_symlink(link_path, target_path)
79
+ created = ensure_symlink(link_path, target_path)
80
+ if created:
81
+ print(f"✅ Created symlink: {link_path} -> {target_path}")
82
+ else:
83
+ print(f"✅ Symlink already exists: {link_path} -> {target_path}")
84
+
85
+
86
+ def open_wsl_port(ports_spec: str) -> None:
87
+ ensure_windows_environment()
88
+ normalized_ports, description = normalize_port_spec_for_firewall(ports_spec)
89
+ rule_name = f"WSL Ports {description}"
90
+ # Build PowerShell array syntax for -LocalPort parameter (e.g., @('3000-4000','8080'))
91
+ port_parts = normalized_ports.split(",")
92
+ ps_array = "@(" + ",".join(f"'{p}'" for p in port_parts) + ")"
93
+ script = f"New-NetFirewallRule -DisplayName '{rule_name}' -Direction Inbound -LocalPort {ps_array} -Protocol TCP -Action Allow"
94
+ print(f"🔥 Opening firewall for ports: {description}...")
95
+ result = subprocess.run(["powershell.exe", "-NoLogo", "-NoProfile", "-Command", script], capture_output=True, text=True)
96
+ if result.returncode == 0:
97
+ print(f"✅ Firewall rule created for ports: {description}")
98
+ else:
99
+ print(f"❌ Failed to create firewall rule: {result.stderr.strip()}")
100
+
101
+
102
+ def change_ssh_port(port: int) -> None:
103
+ ensure_linux_environment()
104
+ if port < 1 or port > 65535:
105
+ raise ValueError(f"Invalid port number: {port}")
106
+
107
+ sshd_config = Path("/etc/ssh/sshd_config")
108
+ if not sshd_config.exists():
109
+ raise FileNotFoundError(f"SSH config file not found: {sshd_config}")
110
+
111
+ print(f"🔧 Changing SSH port to {port}...")
112
+
113
+ content = sshd_config.read_text()
114
+ new_content = re.sub(r"^#?\s*Port\s+\d+", f"Port {port}", content, flags=re.MULTILINE)
115
+ if f"Port {port}" not in new_content:
116
+ new_content = f"Port {port}\n" + new_content
117
+
118
+ print(f"📝 Updating {sshd_config}...")
119
+ result = subprocess.run(["sudo", "tee", str(sshd_config)], input=new_content.encode(), capture_output=True)
120
+ if result.returncode != 0:
121
+ raise RuntimeError(f"Failed to update sshd_config: {result.stderr.decode()}")
122
+ print(f"✅ Updated {sshd_config}")
123
+
124
+ override_dir = Path("/etc/systemd/system/ssh.socket.d")
125
+ override_file = override_dir / "override.conf"
126
+ override_content = f"""[Socket]
127
+ ListenStream=
128
+ ListenStream={port}
129
+ """
130
+ print(f"📝 Creating systemd socket override at {override_file}...")
131
+ subprocess.run(["sudo", "mkdir", "-p", str(override_dir)], check=True)
132
+ result = subprocess.run(["sudo", "tee", str(override_file)], input=override_content.encode(), capture_output=True)
133
+ if result.returncode != 0:
134
+ raise RuntimeError(f"Failed to create override file: {result.stderr.decode()}")
135
+ print("✅ Created systemd socket override")
136
+
137
+ print("🔄 Restarting SSH services...")
138
+ subprocess.run(["sudo", "systemctl", "daemon-reload"], check=True)
139
+ subprocess.run(["sudo", "systemctl", "restart", "ssh.socket"], check=False)
140
+ subprocess.run(["sudo", "service", "ssh", "restart"], check=False)
141
+ print(f"✅ SSH port changed to {port}")
142
+ print(f"⚠️ Remember to open port {port} in Windows Firewall if running in WSL")
206
143
 
207
144
 
208
145
  if __name__ == "__main__":
209
- copy_when_inside_wsl(Path("projects/source.txt"), Path("windows_projects/source.txt"), True)
210
- copy_when_inside_windows(Path("documents/example.txt"), Path("linux_documents/example.txt"), True)
146
+ copy_when_inside_wsl(Path("projects/source.txt"), Path("windows_projects/source.txt"), overwrite=True, windows_username=None)
147
+ copy_when_inside_windows(Path("documents/example.txt"), Path("linux_documents/example.txt"), overwrite=True)