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
@@ -0,0 +1,267 @@
1
+ """BR: Backup and Retrieve"""
2
+
3
+ import re
4
+ from platform import system
5
+ from typing import Literal, Optional
6
+ from pathlib import Path
7
+
8
+ from rich.console import Console
9
+ from rich.panel import Panel
10
+
11
+ from machineconfig.utils.io import read_ini
12
+ from machineconfig.utils.source_of_truth import DEFAULTS_PATH
13
+ from machineconfig.utils.code import print_code
14
+ from machineconfig.utils.options import choose_cloud_interactively
15
+ from machineconfig.scripts.python.helpers.helpers_cloud.helpers2 import ES
16
+ from machineconfig.scripts.python.helpers.helpers_devops.backup_config import (
17
+ BackupConfig, BackupGroup, VALID_OS, USER_BACKUP_PATH, DEFAULT_BACKUP_HEADER,
18
+ normalize_os_name, os_applies, read_backup_config,
19
+ )
20
+ from machineconfig.profile.create_links_export import REPO_LOOSE
21
+
22
+ DIRECTION = Literal["BACKUP", "RETRIEVE"]
23
+
24
+
25
+ def _sanitize_entry_name(value: str) -> str:
26
+ token = value.strip().replace(".", "_").replace("-", "_")
27
+ token = re.sub(r"\s+", "_", token)
28
+ token = re.sub(r"[^A-Za-z0-9_]", "_", token)
29
+ return token or "backup_item"
30
+
31
+
32
+ def _format_backup_entry_block(
33
+ entry_name: str,
34
+ path_local: str,
35
+ path_cloud: str,
36
+ zip: bool,
37
+ encrypt: bool,
38
+ rel2home: bool,
39
+ os_value: str,
40
+ ) -> str:
41
+ parts = [
42
+ f"path_local = '{path_local}'",
43
+ f"path_cloud = '{path_cloud}'",
44
+ f"encrypt = {str(encrypt).lower()}",
45
+ f"zip = {str(zip).lower()}",
46
+ f"rel2home = {str(rel2home).lower()}",
47
+ f"os = '{os_value}'",
48
+ ]
49
+ return f"{entry_name} = {{ {', '.join(parts)} }}"
50
+
51
+
52
+ def _upsert_backup_entry(content: str, group_name: str, entry_name: str, entry_line: str) -> tuple[str, bool]:
53
+ header = f"[{group_name}]"
54
+ lines = content.splitlines()
55
+ new_lines: list[str] = []
56
+ in_target = False
57
+ entry_written = False
58
+ replaced = False
59
+ for line in lines:
60
+ stripped = line.strip()
61
+ if stripped.startswith("[") and stripped.endswith("]"):
62
+ if in_target and not entry_written:
63
+ new_lines.append(entry_line)
64
+ entry_written = True
65
+ in_target = False
66
+ if stripped == header:
67
+ in_target = True
68
+ new_lines.append(line)
69
+ continue
70
+ if in_target:
71
+ if stripped.startswith(f"{entry_name} ="):
72
+ new_lines.append(entry_line)
73
+ replaced = True
74
+ entry_written = True
75
+ continue
76
+ new_lines.append(line)
77
+ if not entry_written:
78
+ if header not in content:
79
+ if new_lines and new_lines[-1].strip():
80
+ new_lines.append("")
81
+ new_lines.append(header)
82
+ new_lines.append(entry_line)
83
+ updated = "\n".join(new_lines).rstrip() + "\n"
84
+ return updated, replaced
85
+
86
+
87
+ def register_backup_entry(
88
+ path_local: str,
89
+ group: str,
90
+ entry_name: Optional[str] = None,
91
+ path_cloud: Optional[str] = None,
92
+ zip: bool = True,
93
+ encrypt: bool = True,
94
+ rel2home: Optional[bool] = None,
95
+ os: str = "any",
96
+ ) -> tuple[Path, str, bool]:
97
+ local_path = Path(path_local).expanduser().absolute()
98
+ if not local_path.exists():
99
+ raise ValueError(f"Local path does not exist: {local_path}")
100
+ os_parts = [part.strip() for part in os.split(",")]
101
+ os_tokens: list[str] = []
102
+ for part in os_parts:
103
+ if not part:
104
+ continue
105
+ token = normalize_os_name(part)
106
+ if token in {"any", "all", "*"}:
107
+ os_tokens = ["any"]
108
+ break
109
+ if token not in VALID_OS:
110
+ raise ValueError(f"Invalid os value: {os!r}. Expected one of: {sorted(VALID_OS)}")
111
+ os_tokens.append(token)
112
+ if not os_tokens:
113
+ raise ValueError(f"Invalid os value: {os!r}. Expected one of: {sorted(VALID_OS)}")
114
+ home = Path.home()
115
+ in_home = local_path.is_relative_to(home)
116
+ if rel2home is None:
117
+ rel2home = in_home
118
+ if rel2home and not in_home:
119
+ raise ValueError("rel2home is true, but the local path is not under the home directory.")
120
+ group_name = _sanitize_entry_name(group) if group and group.strip() else "default"
121
+ if entry_name is None or not entry_name.strip():
122
+ base_name = _sanitize_entry_name(local_path.stem)
123
+ os_tag = "any" if "any" in os_tokens else "_".join(os_tokens)
124
+ entry_name = f"{base_name}_{os_tag}" if os_tag != "any" else base_name
125
+ else:
126
+ entry_name = _sanitize_entry_name(entry_name)
127
+ local_display = f"~/{local_path.relative_to(home)}" if rel2home and in_home else local_path.as_posix()
128
+ cloud_value = path_cloud.strip() if path_cloud and path_cloud.strip() else ES
129
+ os_value = "any" if "any" in os_tokens else ",".join(os_tokens)
130
+ entry_block = _format_backup_entry_block(
131
+ entry_name=entry_name,
132
+ path_local=local_display,
133
+ path_cloud=cloud_value,
134
+ zip=zip,
135
+ encrypt=encrypt,
136
+ rel2home=rel2home,
137
+ os_value=os_value,
138
+ )
139
+ USER_BACKUP_PATH.parent.mkdir(parents=True, exist_ok=True)
140
+ if USER_BACKUP_PATH.exists():
141
+ content = USER_BACKUP_PATH.read_text(encoding="utf-8")
142
+ else:
143
+ content = DEFAULT_BACKUP_HEADER
144
+ updated, replaced = _upsert_backup_entry(content=content, group_name=group_name, entry_name=entry_name, entry_line=entry_block)
145
+ USER_BACKUP_PATH.write_text(updated, encoding="utf-8")
146
+ return USER_BACKUP_PATH, entry_name, replaced
147
+
148
+
149
+ def main_backup_retrieve(direction: DIRECTION, which: Optional[str], cloud: Optional[str], repo: REPO_LOOSE) -> None:
150
+ console = Console()
151
+ if cloud is None or not cloud.strip():
152
+ try:
153
+ cloud = read_ini(DEFAULTS_PATH)["general"]["rclone_config_name"].strip()
154
+ console.print(Panel(f"⚠️ DEFAULT CLOUD CONFIGURATION\n🌥️ Using default cloud: {cloud}", title="[bold blue]Cloud Configuration[/bold blue]", border_style="blue"))
155
+ except (FileNotFoundError, KeyError, IndexError):
156
+ console.print(Panel("🔍 DEFAULT CLOUD NOT FOUND\n🔄 Please select a cloud configuration from the options below", title="[bold red]Error: Cloud Not Found[/bold red]", border_style="red"))
157
+ cloud = choose_cloud_interactively().strip()
158
+ else:
159
+ cloud = cloud.strip()
160
+ console.print(Panel(f"🌥️ Using provided cloud: {cloud}", title="[bold blue]Cloud Configuration[/bold blue]", border_style="blue"))
161
+ assert cloud is not None
162
+ bu_file: BackupConfig = read_backup_config(repo=repo)
163
+ system_raw = system()
164
+ normalized_system = normalize_os_name(system_raw)
165
+ filtered: BackupConfig = {}
166
+ for group_name, group_items in bu_file.items():
167
+ matched: BackupGroup = {}
168
+ for key, val in group_items.items():
169
+ if os_applies(val["os"], system_name=normalized_system):
170
+ matched[key] = val
171
+ if matched:
172
+ filtered[group_name] = matched
173
+ bu_file = filtered
174
+ console.print(Panel(
175
+ f"🖥️ {system_raw} ENVIRONMENT DETECTED\n"
176
+ "🔍 Filtering entries by os field\n"
177
+ f"✅ Found {sum(len(item) for item in bu_file.values())} applicable backup configuration entries",
178
+ title="[bold blue]Environment[/bold blue]",
179
+ border_style="blue",
180
+ ))
181
+
182
+ if which is None:
183
+ # import platform
184
+ # if platform.system() not in {"Linux", "Darwin"}:
185
+ # console.print(Panel(f"🔍 SELECT {direction} ITEMS\n📋 Choose which configuration entries to process", title="[bold blue]Select Items[/bold blue]", border_style="blue"))
186
+ # choices = choose_from_options(multi=True, msg=f"WHICH FILE of the following do you want to {direction}?", options=["all"] + list(bu_file.keys()), tv=True)
187
+ # else:
188
+ from machineconfig.utils.options_utils.options_tv_linux import select_from_options
189
+ choice = select_from_options(
190
+ options_to_preview_mapping=bu_file, extension="toml", multi=False, preview_size_percent=75.0,
191
+ )
192
+ if choice is None:
193
+ console.print(Panel("❌ NO ITEMS SELECTED\n⚠️ Exiting without processing any items", title="[bold red]No Items Selected[/bold red]", border_style="red"))
194
+ return
195
+ choices = [choice]
196
+ else:
197
+ choices = [token.strip() for token in which.split(",")] if which else []
198
+ console.print(Panel(f"🔖 PRE-SELECTED ITEMS\n📝 Using: {', '.join(choices)}", title="[bold blue]Pre-selected Items[/bold blue]", border_style="blue"))
199
+
200
+ items: BackupConfig
201
+ if "all" in choices:
202
+ items = bu_file
203
+ console.print(Panel(f"📋 PROCESSING ALL ENTRIES\n🔢 Total entries to process: {sum(len(item) for item in bu_file.values())}", title="[bold blue]Process All Entries[/bold blue]", border_style="blue"))
204
+ else:
205
+ items = {}
206
+ unknown: list[str] = []
207
+ for choice in choices:
208
+ if not choice:
209
+ continue
210
+ if choice in bu_file:
211
+ items[choice] = bu_file[choice]
212
+ continue
213
+ if "." in choice:
214
+ group_name, item_name = choice.split(".", 1)
215
+ if group_name in bu_file and item_name in bu_file[group_name]:
216
+ items.setdefault(group_name, {})[item_name] = bu_file[group_name][item_name]
217
+ continue
218
+ unknown.append(choice)
219
+ if unknown:
220
+ raise ValueError(f"Unknown backup entries: {', '.join(unknown)}")
221
+ console.print(Panel(f"📋 PROCESSING SELECTED ENTRIES\n🔢 Total entries to process: {sum(len(item) for item in items.values())}", title="[bold blue]Process Selected Entries[/bold blue]", border_style="blue"))
222
+ program = ""
223
+ console.print(Panel(f"🚀 GENERATING {direction} SCRIPT\n🌥️ Cloud: {cloud}\n🗂️ Items: {sum(len(item) for item in items.values())}", title="[bold blue]Script Generation[/bold blue]", border_style="blue"))
224
+ for group_name, group_items in items.items():
225
+ for item_name, item in group_items.items():
226
+ display_name = f"{group_name}.{item_name}"
227
+ flags = ""
228
+ flags += "z" if item["zip"] else ""
229
+ flags += "e" if item["encrypt"] else ""
230
+ flags += "r" if item["rel2home"] else ""
231
+ flags += "o" if "any" not in item["os"] else ""
232
+ local_path = Path(item["path_local"]).as_posix()
233
+ if item["path_cloud"] in (None, ES):
234
+ remote_path = ES
235
+ remote_display = f"{ES} (deduced)"
236
+ else:
237
+ remote_path = Path(item["path_cloud"]).as_posix()
238
+ remote_display = remote_path
239
+ remote_spec = f"{cloud}:{remote_path}"
240
+ console.print(Panel(
241
+ f"📦 PROCESSING: {display_name}\n"
242
+ f"📂 Local path: {local_path}\n"
243
+ f"☁️ Remote path: {remote_display}\n"
244
+ f"🏳️ Flags: {flags or 'None'}",
245
+ title=f"[bold blue]Processing Item: {display_name}[/bold blue]",
246
+ border_style="blue",
247
+ ))
248
+ flag_arg = f" -{flags}" if flags else ""
249
+ if direction == "BACKUP":
250
+ program += f"""\ncloud copy "{local_path}" "{remote_spec}"{flag_arg}\n"""
251
+ elif direction == "RETRIEVE":
252
+ program += f"""\ncloud copy "{remote_spec}" "{local_path}"{flag_arg}\n"""
253
+ else:
254
+ console.print(Panel('❌ ERROR: INVALID DIRECTION\n⚠️ Direction must be either "BACKUP" or "RETRIEVE"', title="[bold red]Error: Invalid Direction[/bold red]", border_style="red"))
255
+ raise RuntimeError(f"Unknown direction: {direction}")
256
+ if group_name == "dotfiles" and system_raw == "Linux":
257
+ program += """\nchmod 700 ~/.ssh/*\n"""
258
+ console.print(Panel("🔒 SPECIAL HANDLING: SSH PERMISSIONS\n🛠️ Setting secure permissions for SSH files\n📝 Command: chmod 700 ~/.ssh/*", title="[bold blue]Special Handling: SSH Permissions[/bold blue]", border_style="blue"))
259
+ print_code(program, lexer="shell", desc=f"{direction} script")
260
+ console.print(Panel(f"✅ {direction} SCRIPT GENERATION COMPLETE\n🚀 Ready to execute the operations", title="[bold green]Script Generation Complete[/bold green]", border_style="green"))
261
+ import subprocess
262
+
263
+ subprocess.run(program, shell=True, check=True)
264
+
265
+
266
+ if __name__ == "__main__":
267
+ pass
@@ -0,0 +1,98 @@
1
+
2
+
3
+ from typing import Literal, Annotated
4
+ from pathlib import Path
5
+ import typer
6
+ import machineconfig.scripts.python.helpers.helpers_devops.cli_config_dotfile as dotfile_module
7
+ import machineconfig.profile.create_links_export as create_links_export
8
+
9
+
10
+ def configure_shell_profile(which: Annotated[Literal["default", "d", "nushell", "n"], typer.Option(..., "--which", "-w", help="Which shell profile to create/configure")]="default"):
11
+ """🔗 Configure your shell profile."""
12
+ from machineconfig.profile.create_shell_profile import create_default_shell_profile, create_nu_shell_profile
13
+ match which:
14
+ case "nushell" | "n":
15
+ create_nu_shell_profile()
16
+ return
17
+ case "default" | "d":
18
+ create_default_shell_profile()
19
+ return
20
+ msg = typer.style("Error: ", fg=typer.colors.RED) + f"Unknown shell profile type: {which}"
21
+ typer.echo(msg)
22
+
23
+
24
+
25
+ def pwsh_theme():
26
+ """🔗 Select powershell prompt theme."""
27
+ import machineconfig.scripts.python.helpers.helpers_devops.themes as themes
28
+ file = Path(themes.__file__).parent / "choose_pwsh_theme.ps1"
29
+ import subprocess
30
+ subprocess.run(["pwsh", "-File", str(file)])
31
+
32
+
33
+ def starship_theme():
34
+ """🔗 Select starship prompt theme."""
35
+ import subprocess
36
+ import platform
37
+ import os
38
+ from pathlib import Path
39
+
40
+ current_dir = Path(__file__).parent.joinpath("themes")
41
+
42
+ if platform.system() == "Windows":
43
+ script_path = current_dir / "choose_starship_theme.ps1"
44
+ try:
45
+ subprocess.run(["pwsh", "-File", str(script_path)], check=True)
46
+ except FileNotFoundError:
47
+ # Fallback to powershell if pwsh is not available
48
+ subprocess.run(["powershell", "-File", str(script_path)], check=True)
49
+ else:
50
+ script_path = current_dir / "choose_starship_theme.sh"
51
+ # Ensure executable
52
+ os.chmod(script_path, 0o755)
53
+ subprocess.run(["bash", str(script_path)])
54
+
55
+
56
+ def copy_assets(which: Annotated[Literal["scripts", "s", "settings", "t", "both", "b"], typer.Argument(..., help="Which assets to copy")]):
57
+ """🔗 Copy asset files from library to machine."""
58
+ import machineconfig.profile.create_helper as create_helper
59
+ match which:
60
+ case "both" | "b":
61
+ create_helper.copy_assets_to_machine(which="scripts")
62
+ create_helper.copy_assets_to_machine(which="settings")
63
+ return
64
+ case "scripts" | "s":
65
+ create_helper.copy_assets_to_machine(which="scripts")
66
+ return
67
+ case "settings" | "t":
68
+ create_helper.copy_assets_to_machine(which="settings")
69
+ return
70
+ msg = typer.style("Error: ", fg=typer.colors.RED) + f"Unknown asset type: {which}"
71
+ typer.echo(msg)
72
+
73
+
74
+ def get_app():
75
+ config_apps = typer.Typer(help="⚙️ [c] configuration subcommands", no_args_is_help=True, add_help_option=True, add_completion=False)
76
+ config_apps.command("sync", no_args_is_help=True, help="🔗 [s] Sync dotfiles.")(create_links_export.main_from_parser)
77
+ config_apps.command("s", no_args_is_help=True, help="Sync dotfiles.", hidden=True)(create_links_export.main_from_parser)
78
+
79
+ config_apps.command("register", no_args_is_help=True, help="🔗 [r] Register dotfiles agains user mapper.toml")(dotfile_module.register_dotfile)
80
+ config_apps.command("r", no_args_is_help=True, hidden=True)(dotfile_module.register_dotfile)
81
+
82
+ config_apps.command("export-dotfiles", no_args_is_help=True, help="🔗 [e] Export dotfiles for migration to new machine.")(dotfile_module.export_dotfiles)
83
+ config_apps.command("e", no_args_is_help=True, help="Export dotfiles for migration to new machine.", hidden=True)(dotfile_module.export_dotfiles)
84
+ config_apps.command("import-dotfiles", no_args_is_help=False, help="🔗 [i] Import dotfiles from exported archive.")(dotfile_module.import_dotfiles)
85
+ config_apps.command("i", no_args_is_help=False, help="Import dotfiles from exported archive.", hidden=True)(dotfile_module.import_dotfiles)
86
+
87
+ config_apps.command("shell", no_args_is_help=False, help="🔗 [S] Configure your shell profile.")(configure_shell_profile)
88
+ config_apps.command("S", no_args_is_help=False, help="Configure your shell profile.", hidden=True)(configure_shell_profile)
89
+ config_apps.command("starship-theme", no_args_is_help=False, help="🔗 [t] Select starship prompt theme.")(starship_theme)
90
+ config_apps.command("t", no_args_is_help=False, help="Select starship prompt theme.", hidden=True)(starship_theme)
91
+ config_apps.command("pwsh-theme", no_args_is_help=False, help="🔗 [T] Select powershell prompt theme.")(pwsh_theme)
92
+ config_apps.command("T", no_args_is_help=False, help="Select powershell prompt theme.", hidden=True)(pwsh_theme)
93
+
94
+ config_apps.command("copy-assets", no_args_is_help=True, help="🔗 [c] Copy asset files from library to machine.", hidden=False)(copy_assets)
95
+ config_apps.command("c", no_args_is_help=True, help="Copy asset files from library to machine.", hidden=True)(copy_assets)
96
+
97
+
98
+ return config_apps
@@ -0,0 +1,274 @@
1
+
2
+ """Like yadm and dotter."""
3
+
4
+ from git import Optional
5
+ from machineconfig.profile.create_links_export import ON_CONFLICT_LOOSE, ON_CONFLICT_MAPPER, METHOD_LOOSE, METHOD_MAP
6
+ from typing import Annotated, Literal
7
+ from pathlib import Path
8
+ import typer
9
+
10
+ from machineconfig.utils.source_of_truth import CONFIG_ROOT
11
+ BACKUP_ROOT_PRIVATE = Path.home().joinpath("dotfiles/machineconfig/mapper/files")
12
+ BACKUP_ROOT_PUBLIC = Path(CONFIG_ROOT).joinpath("dotfiles/mapper")
13
+
14
+
15
+
16
+ def _write_to_user_mapper(section: str, entry_name: str, original_path: Path, self_managed_path: Path, method: Literal["symlink", "copy"], is_contents: bool, os_filter: str) -> Path:
17
+ from machineconfig.profile.create_links import USER_MAPPER_PATH
18
+ mapper_path = USER_MAPPER_PATH
19
+ mapper_path.parent.mkdir(parents=True, exist_ok=True)
20
+ if mapper_path.exists():
21
+ content = mapper_path.read_text(encoding="utf-8")
22
+ else:
23
+ content = "# User-defined config file mappings\n# Created by `d c` CLI tool\n\n"
24
+ home = Path.home()
25
+ original_str = f"~/{original_path.relative_to(home)}" if original_path.is_relative_to(home) else original_path.as_posix()
26
+ self_managed_str = f"~/{self_managed_path.relative_to(home)}" if self_managed_path.is_relative_to(home) else self_managed_path.as_posix()
27
+ entry_dict_parts: list[str] = [f"original = '{original_str}'", f"self_managed = '{self_managed_str}'"]
28
+ if is_contents:
29
+ entry_dict_parts.append("contents = true")
30
+ if method == "copy":
31
+ entry_dict_parts.append("copy = true")
32
+ entry_dict_parts.append(f"os = '{os_filter}'")
33
+ entry_line = f"{entry_name} = {{ {', '.join(entry_dict_parts)} }}"
34
+ section_header = f"[{section}]"
35
+ if section_header in content:
36
+ lines = content.split("\n")
37
+ new_lines: list[str] = []
38
+ in_section = False
39
+ entry_updated = False
40
+ for line in lines:
41
+ if line.strip().startswith("[") and line.strip().endswith("]"):
42
+ if in_section and not entry_updated:
43
+ new_lines.append(entry_line)
44
+ entry_updated = True
45
+ in_section = line.strip() == section_header
46
+ if in_section and line.strip().startswith(f"{entry_name} ="):
47
+ new_lines.append(entry_line)
48
+ entry_updated = True
49
+ continue
50
+ new_lines.append(line)
51
+ if in_section and not entry_updated:
52
+ new_lines.append(entry_line)
53
+ content = "\n".join(new_lines)
54
+ else:
55
+ content = content.rstrip() + f"\n\n{section_header}\n{entry_line}\n"
56
+ mapper_path.write_text(content, encoding="utf-8")
57
+ return mapper_path
58
+
59
+ def record_mapping(orig_path: Path, new_path: Path, method: METHOD_LOOSE, section: str, os_filter: str) -> None:
60
+ entry_name = orig_path.stem.replace(".", "_").replace("-", "_")
61
+ method_resolved = METHOD_MAP[method]
62
+ mapper_file = _write_to_user_mapper(section=section, entry_name=entry_name, original_path=orig_path, self_managed_path=new_path, method=method_resolved, is_contents=False, os_filter=os_filter)
63
+ home = Path.home()
64
+ orig_display = f"~/{orig_path.relative_to(home)}" if orig_path.is_relative_to(home) else orig_path.as_posix()
65
+ new_display = f"~/{new_path.relative_to(home)}" if new_path.is_relative_to(home) else new_path.as_posix()
66
+ from rich.console import Console
67
+ from rich.panel import Panel
68
+ console = Console()
69
+ console.print(Panel(f"📝 Mapping recorded in: [cyan]{mapper_file}[/cyan]\n[{section}]\n{entry_name} = {{ original = '{orig_display}', self_managed = '{new_display}', os = '{os_filter}' }}", title="Mapper Entry Saved", border_style="cyan", padding=(1, 2),))
70
+
71
+
72
+ def get_backup_path(orig_path: Path, sensitivity: Literal["private", "v", "public", "b"], destination: Optional[str], shared: bool) -> Path:
73
+ match sensitivity:
74
+ case "private" | "v":
75
+ backup_root = BACKUP_ROOT_PRIVATE
76
+ case "public" | "b":
77
+ backup_root = BACKUP_ROOT_PUBLIC
78
+ if destination is None:
79
+ if shared:
80
+ new_path = backup_root.joinpath("shared").joinpath(orig_path.name)
81
+ else:
82
+ new_path = backup_root.joinpath(orig_path.relative_to(Path.home()))
83
+ else:
84
+ if shared:
85
+ dest_path = Path(destination).expanduser().absolute()
86
+ new_path = dest_path.joinpath("shared").joinpath(orig_path.name)
87
+ else:
88
+ dest_path = Path(destination).expanduser().absolute()
89
+ new_path = dest_path.joinpath(orig_path.name)
90
+ return new_path
91
+
92
+
93
+ def get_original_path_from_backup_path(backup_path: Path, sensitivity: Literal["private", "v", "public", "b"], destination: Optional[str], shared: bool) -> Path:
94
+ match sensitivity:
95
+ case "private" | "v":
96
+ backup_root = BACKUP_ROOT_PRIVATE
97
+ case "public" | "b":
98
+ backup_root = BACKUP_ROOT_PUBLIC
99
+ if destination is None:
100
+ if shared:
101
+ relative_part = backup_path.relative_to(backup_root.joinpath("shared"))
102
+ else:
103
+ relative_part = backup_path.relative_to(backup_root)
104
+ original_path = Path.home().joinpath(relative_part)
105
+ else:
106
+ dest_path = Path(destination).expanduser().absolute()
107
+ if shared:
108
+ relative_part = backup_path.relative_to(dest_path.joinpath("shared"))
109
+ else:
110
+ relative_part = backup_path.relative_to(dest_path)
111
+ original_path = Path.home().joinpath(relative_part)
112
+ return original_path
113
+
114
+
115
+ def register_dotfile(
116
+ file: Annotated[str, typer.Argument(help="file/folder path.")],
117
+ method: Annotated[METHOD_LOOSE, typer.Option(..., "--method", "-m", help="Method to use for linking files")] = "copy",
118
+ on_conflict: Annotated[ON_CONFLICT_LOOSE, typer.Option(..., "--on-conflict", "-o", help="Action to take on conflict")] = "throw-error",
119
+ sensitivity: Annotated[Literal["private", "v", "public", "b"], typer.Option(..., "--sensitivity", "-s", help="Sensitivity of the config file.")] = "private",
120
+ destination: Annotated[Optional[str], typer.Option("--destination", "-d", help="destination folder (override the default, use at your own risk)")] = None,
121
+ section: Annotated[str, typer.Option("--section", "-se", help="Section name in mapper_dotfiles.toml to record this mapping.")] = "default",
122
+ os_filter: Annotated[str, typer.Option("--os", help="Comma-separated OS list or 'any' to apply everywhere.")] = "any",
123
+ shared: Annotated[bool, typer.Option("--shared", "-sh", help="Whether the config file is shared across destinations directory.")] = False,
124
+ record: Annotated[bool, typer.Option("--record", "-r", help="Record the mapping in user's mapper.toml")] = True,
125
+ ) -> None:
126
+ from rich.console import Console
127
+ from rich.panel import Panel
128
+ from machineconfig.utils.links import symlink_map, copy_map
129
+ console = Console()
130
+ orig_path = Path(file).expanduser().absolute()
131
+ new_path = get_backup_path(orig_path=orig_path, sensitivity=sensitivity, destination=destination, shared=shared)
132
+ if not orig_path.exists() and not new_path.exists():
133
+ console.print(f"[red]Error:[/] Neither original file nor self-managed file exists:\n Original: {orig_path}\n Self-managed: {new_path}")
134
+ raise typer.Exit(code=1)
135
+ new_path.parent.mkdir(parents=True, exist_ok=True)
136
+ match method:
137
+ case "copy" | "c":
138
+ try:
139
+ copy_map(config_file_default_path=orig_path, self_managed_config_file_path=new_path, on_conflict=ON_CONFLICT_MAPPER[on_conflict]) # type: ignore[arg-type]
140
+ except Exception as e:
141
+ msg = typer.style("Error: ", fg=typer.colors.RED) + str(e)
142
+ typer.echo(msg)
143
+ typer.Exit(code=1)
144
+ return
145
+ case "symlink" | "s":
146
+ try:
147
+ symlink_map(config_file_default_path=orig_path, self_managed_config_file_path=new_path, on_conflict=ON_CONFLICT_MAPPER[on_conflict]) # type: ignore[arg-type]
148
+ except Exception as e:
149
+ msg = typer.style("Error: ", fg=typer.colors.RED) + str(e)
150
+ typer.echo(msg)
151
+ typer.Exit(code=1)
152
+ return
153
+ case _:
154
+ raise ValueError(f"Unknown method: {method}")
155
+ console.print(Panel("\n".join(["✅ Symbolic link created successfully!", "🔄 Add the following snippet to mapper.toml to persist this mapping:",]), title="Symlink Created", border_style="green", padding=(1, 2),))
156
+ if record:
157
+ record_mapping(orig_path=orig_path, new_path=new_path, method=method, section=section, os_filter=os_filter)
158
+
159
+
160
+ def export_dotfiles(
161
+ pwd: Annotated[str, typer.Argument(..., help="Password for zip encryption")],
162
+ over_internet: Annotated[bool, typer.Option("--over-internet", "-i", help="Use internet-based transfer (wormhole-magic)")] = False,
163
+ over_ssh: Annotated[bool, typer.Option("--over-ssh", "-s", help="Use SSH-based transfer (scp) to a remote machine")] = False,
164
+ ):
165
+ """🔗 Export dotfiles for migration to new machine."""
166
+ if over_ssh:
167
+ code_sample = """ftpx ~/dotfiles user@remote_host:^ -z"""
168
+ print("🔗 Exporting dotfiles via SSH-based transfer (scp).")
169
+ print(f"💡 Run the following command on your local machine to copy dotfiles to the remote machine:\n{code_sample}")
170
+ remote_address = typer.prompt("Enter the remote machine address (user@host) to copy dotfiles to ")
171
+ code_concrete = f"fptx ~/dotfiles {remote_address}:^ -z"
172
+ from machineconfig.utils.code import run_shell_script
173
+ run_shell_script(code_concrete)
174
+ dotfiles_dir = Path.home().joinpath("dotfiles")
175
+ if not dotfiles_dir.exists() or not dotfiles_dir.is_dir():
176
+ print(f"❌ Dotfiles directory does not exist: {dotfiles_dir}")
177
+ raise typer.Exit(code=1)
178
+ dotfiles_zip = Path.home().joinpath("dotfiles.zip")
179
+ if dotfiles_zip.exists():
180
+ dotfiles_zip.unlink()
181
+ import shutil
182
+ zipfile = shutil.make_archive(base_name=str(dotfiles_zip)[:-4], format="zip", root_dir=str(dotfiles_dir), base_dir=".", verbose=False)
183
+ from machineconfig.utils.io import encrypt
184
+ zipfile_enc_bytes = encrypt(
185
+ msg=Path(zipfile).read_bytes(),
186
+ pwd=pwd,
187
+ )
188
+ Path(zipfile).unlink()
189
+ zipfile_enc_path = Path(f"{zipfile}.enc")
190
+ if zipfile_enc_path.exists():
191
+ zipfile_enc_path.unlink()
192
+ zipfile_enc_path.write_bytes(zipfile_enc_bytes)
193
+ print(f"✅ Dotfiles exported to: {zipfile_enc_path}")
194
+ if over_internet:
195
+ # rm ~/dotfiles.zip || true
196
+ # ouch c ~/dotfiles dotfiles.zip
197
+ # # INSECURE OVER INTERNET: uvx wormhole-magic send ~/dotfiles.zip
198
+ raise NotImplementedError("Internet-based transfer not yet implemented.")
199
+ else:
200
+ # devops network share-server --no-auth ./dotfiles.zip
201
+ import machineconfig.scripts.python.helpers.helpers_devops.cli_share_server as cli_share_server
202
+ from machineconfig.scripts.python.helpers.helpers_network.address import select_lan_ipv4
203
+ localipv4 = select_lan_ipv4(prefer_vpn=False)
204
+ port = 8888
205
+ msg = f"""On the remote machine, run the following:
206
+ d c i -u http://{localipv4}:{port} -p {pwd}
207
+ """
208
+ from machineconfig.utils.accessories import display_with_flashy_style
209
+ display_with_flashy_style(msg=msg, title="Remote Machine Instructions",)
210
+ cli_share_server.web_file_explorer(
211
+ path=str(zipfile_enc_path),
212
+ no_auth=True,
213
+ port=port,
214
+ # bind_address="
215
+ )
216
+
217
+
218
+ def import_dotfiles(
219
+ url: Annotated[Optional[str], typer.Option(..., "--url", "-u", help="URL or local path to the encrypted dotfiles zip")] = None,
220
+ pwd: Annotated[Optional[str], typer.Option(..., "--pwd", "-p", help="Password for zip decryption")] = None,
221
+ use_ssh: Annotated[bool, typer.Option("--use-ssh", "-s", help="Use SSH-based transfer (scp) from a remote machine that has dotfiles.")]=False,
222
+ ):
223
+ # # INSECURE cd $HOME; uvx wormhole-magic receive dotfiles.zip.enc --accept-file
224
+ # ☁️ [bold blue]Method 3: USING INTERNET SECURE SHARE[/bold blue]
225
+ # [dim]cd ~
226
+ # cloud copy SHARE_URL . --config ss[/dim]
227
+ if use_ssh:
228
+ print("🔗 Importing dotfiles via SSH-based transfer (scp).")
229
+ code = """fptx $USER@$(hostname):^ ~/dotfiles -z"""
230
+ print(f"💡 Run the following command on the remote machine that has the dotfiles:\n{code}")
231
+ url = typer.prompt("Enter the remote machine address (user@host) to copy dotfiles from ")
232
+ code_concrete = f"fptx {url}:^ ~/dotfiles -z"
233
+ from machineconfig.utils.code import run_shell_script
234
+ run_shell_script(code_concrete)
235
+ print("✅ Dotfiles copied via SSH.")
236
+ return
237
+ if url is None:
238
+ url = typer.prompt("Enter the URL or local path to the encrypted dotfiles zip (e..g 192.168.20.4:8888) ")
239
+ if pwd is None:
240
+ pwd = typer.prompt("Enter the password for zip decryption", hide_input=True)
241
+ from machineconfig.scripts.python.helpers.helpers_utils.download import download
242
+ downloaded_file = download(url=url, decompress=False, output_dir=str(Path.home()))
243
+ if downloaded_file is None or not downloaded_file.exists():
244
+ print(f"❌ Failed to download file from URL: {url}")
245
+ raise typer.Exit(code=1)
246
+ zipfile_enc_path = downloaded_file
247
+ from machineconfig.utils.io import decrypt
248
+ zipfile_bytes = decrypt(
249
+ token=zipfile_enc_path.read_bytes(),
250
+ pwd=pwd,
251
+ )
252
+ zipfile_path = Path(str(zipfile_enc_path)[:-4])
253
+ if zipfile_path.exists():
254
+ print(f"⚠️ WARNING: Overwriting existing file: {zipfile_path}")
255
+ zipfile_path.unlink()
256
+ zipfile_path.write_bytes(zipfile_bytes)
257
+ print(f"✅ Decrypted zip file saved to: {zipfile_path}")
258
+ import zipfile
259
+ if Path.home().joinpath("dotfiles").exists():
260
+ print(f"⚠️ WARNING: Overwriting existing directory: {Path.home().joinpath('dotfiles')}")
261
+ import shutil
262
+ shutil.rmtree(Path.home().joinpath("dotfiles"))
263
+ with zipfile.ZipFile(zipfile_path, 'r') as zip_ref:
264
+ zip_ref.extractall(Path.home().joinpath("dotfiles"))
265
+ print(f"✅ Dotfiles extracted to: {Path.home().joinpath('dotfiles')}")
266
+ zipfile_path.unlink()
267
+
268
+
269
+ def arg_parser() -> None:
270
+ typer.run(register_dotfile)
271
+
272
+
273
+ if __name__ == "__main__":
274
+ arg_parser()