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
@@ -35,7 +35,8 @@ def print_code(code: str, lexer: str, desc: str, subtitle: str = ""):
35
35
  print(f"--- End of {desc} ---")
36
36
 
37
37
 
38
- def get_uv_command_executing_python_file(python_file: str, uv_with: Optional[list[str]], uv_project_dir: Optional[str],
38
+ def get_uv_command_executing_python_file(python_file: str, uv_with: Optional[list[str]],
39
+ uv_project_dir: Optional[str],
39
40
  prepend_print: bool = True, ) -> str:
40
41
  # shell script
41
42
  if uv_with is not None and len(uv_with) > 0:
@@ -49,7 +50,7 @@ def get_uv_command_executing_python_file(python_file: str, uv_with: Optional[lis
49
50
  if uv_project_dir is not None:
50
51
  uv_project_dir_arg = "--project" + f' "{uv_project_dir}"'
51
52
  else:
52
- uv_project_dir_arg = ""
53
+ uv_project_dir_arg = "--no-project"
53
54
  import platform
54
55
 
55
56
  uv_command = get_uv_command(platform=platform.system())
@@ -72,11 +73,14 @@ def get_uv_command_executing_python_script(python_script: str, uv_with: Optional
72
73
  return shell_script, python_file
73
74
 
74
75
 
75
- def run_lambda_function(lmb: Callable[[], Any], uv_with: Optional[list[str]], uv_project_dir: Optional[str]) -> None:
76
+ def get_shell_script_running_lambda_function(lmb: Callable[[], Any], uv_with: Optional[list[str]], uv_project_dir: Optional[str]) -> tuple[str, Path]:
76
77
  from machineconfig.utils.meta import lambda_to_python_script
77
78
  code = lambda_to_python_script(lmb,
78
79
  in_global=True, import_module=False)
79
- uv_command, _py_file = get_uv_command_executing_python_script(python_script=code, uv_with=uv_with, uv_project_dir=uv_project_dir)
80
+ uv_command, py_file = get_uv_command_executing_python_script(python_script=code, uv_with=uv_with, uv_project_dir=uv_project_dir)
81
+ return uv_command, py_file
82
+ def run_lambda_function(lmb: Callable[[], Any], uv_with: Optional[list[str]], uv_project_dir: Optional[str]) -> None:
83
+ uv_command, _py_file = get_shell_script_running_lambda_function(lmb=lmb, uv_with=uv_with, uv_project_dir=uv_project_dir)
80
84
  run_shell_script(uv_command)
81
85
 
82
86
 
@@ -111,10 +115,7 @@ def run_shell_file(script_path: str, clean_env: bool):
111
115
  else:
112
116
  raise NotImplementedError(f"Platform {platform.system()} not supported.")
113
117
  return proc
114
-
115
-
116
- def run_shell_script(script: str, display_script: bool = True, clean_env: bool = False) -> Any:
117
-
118
+ def run_shell_script(script: str, display_script: bool = True, clean_env: bool = False):
118
119
  import platform
119
120
  if platform.system() == "Windows":
120
121
  suffix = ".ps1"
@@ -122,12 +123,10 @@ def run_shell_script(script: str, display_script: bool = True, clean_env: bool =
122
123
  else:
123
124
  suffix = ".sh"
124
125
  lexer = "bash"
125
-
126
126
  import tempfile
127
127
  with tempfile.NamedTemporaryFile(mode='w', suffix=suffix, delete=False, encoding='utf-8') as temp_file:
128
128
  temp_file.write(script)
129
129
  temp_shell_script_path = Path(temp_file.name)
130
-
131
130
  from rich.panel import Panel
132
131
  from rich.syntax import Syntax
133
132
  from rich.console import Console
@@ -135,7 +134,6 @@ def run_shell_script(script: str, display_script: bool = True, clean_env: bool =
135
134
  if display_script:
136
135
  from rich.syntax import Syntax
137
136
  console.print(Panel(Syntax(code=script, lexer=lexer), title=f"📄 shell script @ {temp_shell_script_path}", subtitle="shell script being executed"), style="bold red")
138
-
139
137
  proc = run_shell_file(script_path=str(temp_shell_script_path), clean_env=clean_env)
140
138
  # console.print(f"✅ [green]Script executed successfully:[/green] [blue]{temp_script_path}[/blue]")
141
139
  if proc.returncode != 0:
@@ -144,7 +142,6 @@ def run_shell_script(script: str, display_script: bool = True, clean_env: bool =
144
142
  console.print(f"✅ [green]Script executed successfully:[/green] [blue]{temp_shell_script_path}[/blue]")
145
143
  else:
146
144
  console.print(f"⚠️ [yellow]Script executed with warnings (return code {proc.returncode}):[/yellow] [blue]{temp_shell_script_path}[/blue]")
147
-
148
145
  temp_shell_script_path.unlink(missing_ok=True)
149
146
  console.print(f"🗑️ [blue]Temporary script deleted:[/blue] [green]{temp_shell_script_path}[/green]")
150
147
  return proc
@@ -153,7 +150,6 @@ def run_shell_script(script: str, display_script: bool = True, clean_env: bool =
153
150
  def exit_then_run_shell_script(script: str, strict: bool = False):
154
151
  import os
155
152
  from rich.console import Console
156
-
157
153
  console = Console()
158
154
  op_program_path = os.environ.get("OP_PROGRAM_PATH", None)
159
155
  if op_program_path is not None:
@@ -196,3 +192,35 @@ def exit_then_run_shell_script(script: str, strict: bool = False):
196
192
  run_shell_script(script)
197
193
  import sys
198
194
  sys.exit(0)
195
+ def exit_then_run_shell_file(script_path: str, strict: bool):
196
+ import os
197
+ from rich.console import Console
198
+ console = Console()
199
+ op_program_path = os.environ.get("OP_PROGRAM_PATH", None)
200
+ if op_program_path is None or Path(op_program_path).exists():
201
+ if strict:
202
+ console.print("[red]❌ OP_PROGRAM_PATH environment variable is not set or the file already exists in strict mode.[/red]")
203
+ import sys
204
+ sys.exit(1)
205
+ if op_program_path is None or Path(op_program_path).exists():
206
+ console.print("[cyan]ℹ️ OP_PROGRAM_PATH is not set.[/cyan] [yellow]Falling back to direct execution.[/yellow]")
207
+ run_shell_file(script_path=script_path, clean_env=False)
208
+ return
209
+ import platform
210
+ if platform.system() == "Windows":
211
+ suffix = ".ps1"
212
+ lexer = "powershell"
213
+ script = str(script_path)
214
+ elif platform.system() == "Linux" or platform.system() == "Darwin":
215
+ suffix = ".sh"
216
+ lexer = "bash"
217
+ script = f"source {str(script_path)}"
218
+ else:
219
+ raise NotImplementedError(f"Platform {platform.system()} not supported.")
220
+ op_program_path = Path(op_program_path)
221
+ op_program_path.parent.mkdir(parents=True, exist_ok=True)
222
+ op_program_path.write_text(script, encoding="utf-8")
223
+ _ = suffix, lexer
224
+ console.print(f"[cyan]🚀 Handing over to shell script runner via OP_PROGRAM_PATH @ {str(op_program_path)}[/cyan]")
225
+ import sys
226
+ sys.exit(0)
@@ -32,15 +32,11 @@ class BoxStyles:
32
32
 
33
33
  class CowStyles:
34
34
  eyes = ['-b', '-d', '-g', '-h', '-l', '-L', '-n', '-N', '-p', '-s', '-t', '-w', '-y']
35
- # this one for the package installed with sudo apt install cowsay and is located at /usr/games/cowsay. See cowsay -l
36
- figures = ['apt', 'bunny', 'cheese', 'cock', 'cower', 'daemon', 'default', 'dragon',
37
- 'dragon-and-cow', 'duck', 'elephant', 'elephant-in-snake', 'eyes', 'fox', 'ghostbusters',
38
- 'gnu', 'kangaroo', 'kiss', 'milk',
39
- 'moose', 'pony', 'pony-smaller', 'sheep', 'skeleton', 'snowman', 'stegosaurus', # 'suse',
40
- 'three-eyes', 'turkey', 'turtle', 'tux', 'unipony', 'unipony-smaller', 'vader', 'vader'] # 'hellokitty' 'mech-and-cow' # 'moofasa', 'stimpy', 'calvin', , 'ren', 'koala', 'flaming-sheep' , 'bud-frogs' , 'kosh' , 'luke-koala'
35
+ # Available characters from Python cowsay package (uv tool install cowsay)
36
+ figures = ['beavis', 'cheese', 'cow', 'daemon', 'dragon', 'fox', 'ghostbusters', 'kitty', 'meow', 'miki', 'milk', 'octopus', 'pig', 'stegosaurus', 'stimpy', 'trex', 'turkey', 'turtle', 'tux']
41
37
 
42
38
 
43
- FIGLET_FONTS = ['banner', 'big', 'standard']
39
+ FIGLET_FONTS = ['Banner', 'Big', 'Standard']
44
40
 
45
41
  FIGJS_FONTS = ['3D Diagonal', '3D-ASCII', '4Max', '5 Line Oblique', 'Acrobatic', 'ANSI Regular', 'ANSI Shadow',
46
42
  'Avatar', 'Banner', 'Banner3-D', 'Banner4',
@@ -55,8 +51,8 @@ FIGJS_FONTS = ['3D Diagonal', '3D-ASCII', '4Max', '5 Line Oblique', 'Acrobatic',
55
51
 
56
52
  def get_art(comment: Optional[str] = None, artlib: Optional[BOX_OR_CHAR] = None, style: Optional[str] = None, super_style: str = 'scene', prefix: str = ' ', file: Optional[str] = None, verbose: bool = True):
57
53
  """ takes in a comment and does the following wrangling:
58
- * text => figlet font => boxes => lolcat
59
- * text => cowsay => lolcat
54
+ * text => figlet font => boxes => lolcatjs
55
+ * text => cowsay => lolcatjs
60
56
  """
61
57
  if comment is None:
62
58
  try:
@@ -68,10 +64,10 @@ def get_art(comment: Optional[str] = None, artlib: Optional[BOX_OR_CHAR] = None,
68
64
  if artlib == 'boxes':
69
65
  if style is None: style = random.choice(BoxStyles.__dict__[super_style or random.choice(['language', 'scene', 'character'])])
70
66
  fonting = f'figlet -f {random.choice(FIGLET_FONTS)}'
71
- cmd = f"""echo "{comment}" | {fonting} | boxes -d {style} {to_file}"""
67
+ cmd = f"""{fonting} "{comment}" | boxes -d {style} {to_file}"""
72
68
  else:
73
69
  if style is None: style = random.choice(CowStyles.figures)
74
- cmd = f"""echo "{comment}" | /usr/games/cowsay -f {style} {to_file}"""
70
+ cmd = f"""cowsay -c {style} -t "{comment}" {to_file}"""
75
71
  try:
76
72
  res = subprocess.run(cmd, text=True, capture_output=True, shell=True, check=True).stdout
77
73
  except subprocess.CalledProcessError as ex:
@@ -91,7 +87,7 @@ def font_box_color(logo: str):
91
87
  box_style = random.choice(['whirly', 'xes', 'columns', 'parchment', 'scroll', 'scroll-akn', 'diamonds', 'headline', 'nuke', 'spring', 'stark1'])
92
88
  _cmd = f'figlet -f "{font}" "{logo}" | boxes -d "{box_style}" | lolcatjs'
93
89
  # print(_cmd)
94
- os.system(_cmd) # | lolcat
90
+ os.system(_cmd) # | lolcatjs
95
91
  # print("after")
96
92
 
97
93
 
@@ -100,7 +96,7 @@ def character_color(logo: str):
100
96
  with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as f:
101
97
  f.write(ArtLib.cowsay(logo))
102
98
  _new_art = f.name
103
- os.system(f'type {_new_art} | lolcatjs') # | lolcat
99
+ os.system(f'type {_new_art} | lolcatjs') # | lolcatjs
104
100
 
105
101
 
106
102
  def character_or_box_color(logo: str):
@@ -110,7 +106,7 @@ def character_or_box_color(logo: str):
110
106
  get_art(logo, artlib=None, file=_new_art, verbose=False)
111
107
  # Prefer bat on mac if available, fallback to cat
112
108
  pager = "bat" if (platform.system() == "Darwin" and any((Path(p).joinpath("bat").exists() for p in os.environ.get("PATH", "").split(os.pathsep)))) else "cat"
113
- command = f"{pager} {_new_art} | lolcat"
109
+ command = f"{pager} {_new_art} | lolcatjs"
114
110
  os.system(command)
115
111
 
116
112
 
@@ -46,7 +46,7 @@ def print_logo(logo: str):
46
46
  elif platform.system() in ["Linux", "Darwin"]: # Explicitly handle both Linux and macOS
47
47
  from machineconfig.utils.installer_utils.installer_locator_utils import is_executable_in_path
48
48
  avail_cowsay = is_executable_in_path("cowsay")
49
- avail_lolcat = is_executable_in_path("lolcat")
49
+ avail_lolcat = is_executable_in_path("lolcatjs")
50
50
  avail_boxes = is_executable_in_path("boxes")
51
51
  avail_figlet = is_executable_in_path("figlet")
52
52
  if avail_cowsay and avail_lolcat and avail_boxes and avail_figlet:
@@ -56,10 +56,8 @@ def print_logo(logo: str):
56
56
  # print(Path(random.choice(glob.glob(str(Path(__file__).parent.joinpath("art", "*"))))).read_text())
57
57
  character_or_box_color(logo=logo)
58
58
  else:
59
- print("\n" + "🚫 " + "-" * 70 + " 🚫")
60
- install_cmd = "devops install --group TerminalEyeCandy" if platform.system() == "Linux" else "brew install cowsay lolcat boxes figlet"
61
- print(f"🔍 Missing ASCII art dependencies. Install with: {install_cmd}")
62
- print("🚫 " + "-" * 70 + " 🚫\n")
59
+ install_cmd = "devops install --group term-eye-candy "
60
+ print(f"🔍 Missing ASCII art dependencies. Install with: {install_cmd} | {avail_boxes=} {avail_cowsay} {avail_figlet} {avail_lolcat=}")
63
61
  _default_art = Path(random.choice(glob.glob(str(Path(__file__).parent.joinpath("art", "*")))))
64
62
  print(_default_art.read_text())
65
63
  else:
@@ -14,7 +14,11 @@ class Read:
14
14
  if suffix in ("sqlite", "sqlite3", "db", "duckdb"):
15
15
  from machineconfig.utils.files.dbms import DBMS
16
16
  res = DBMS.from_local_db(path=path)
17
- print(res.describe_db())
17
+ try:
18
+ print(res.describe_db())
19
+ except Exception:
20
+ print("💥 Could not describe the database.")
21
+ pass
18
22
  return res
19
23
  try: return getattr(Read, suffix)(str(path), **kwargs)
20
24
  except AttributeError as err:
@@ -28,6 +32,9 @@ class Read:
28
32
  elif suffix == "csv":
29
33
  import polars as pl
30
34
  return pl.read_csv(path, **kwargs)
35
+ elif suffix == "npz" or suffix == "npy":
36
+ import numpy as np
37
+ return np.load(str(path), **kwargs)
31
38
  try:
32
39
  # guess = install_n_import('magic', 'python-magic').from_file(path)
33
40
  guess = "IDKm"
@@ -10,6 +10,8 @@ from pathlib import Path
10
10
  from typing import Any, Dict, Optional, Set, TypedDict
11
11
  from urllib.parse import urlparse
12
12
 
13
+ from machineconfig.utils.installer_utils.github_release_scraper import scrape_github_release_page
14
+
13
15
 
14
16
  class AssetInfo(TypedDict):
15
17
  """Type definition for GitHub release asset information."""
@@ -74,7 +76,7 @@ def fetch_github_release_data(
74
76
  repo_name: str,
75
77
  version: Optional[str] = None,
76
78
  ) -> Optional[Dict[str, Any]]:
77
- """Fetch GitHub release data for the latest or a specific tag."""
79
+ """Fetch GitHub release data for the latest or a specific tag. Falls back to HTML scraping if API fails."""
78
80
 
79
81
  try:
80
82
  requested_version = (version or "").strip()
@@ -85,24 +87,24 @@ def fetch_github_release_data(
85
87
 
86
88
  response = requests.get(url, timeout=30)
87
89
  if response.status_code != 200:
88
- print(f" Failed to fetch data for {username}/{repo_name}: HTTP {response.status_code}")
89
- return None
90
+ print(f"⚠️ API failed for {username}/{repo_name}: HTTP {response.status_code}, trying HTML scraper...")
91
+ return scrape_github_release_page(username, repo_name, version)
90
92
 
91
93
  response_data = response.json()
92
94
  message = response_data.get("message")
93
95
  if isinstance(message, str):
94
96
  if "API rate limit exceeded" in message:
95
- print(f"🚫 Rate limit exceeded for {username}/{repo_name}")
96
- return None
97
+ print(f"🚫 Rate limit exceeded for {username}/{repo_name}, trying HTML scraper...")
98
+ return scrape_github_release_page(username, repo_name, version)
97
99
  if "Not Found" in message:
98
- print(f"🔍 No releases found for {username}/{repo_name}")
99
- return None
100
+ print(f"🔍 No releases found via API for {username}/{repo_name}, trying HTML scraper...")
101
+ return scrape_github_release_page(username, repo_name, version)
100
102
 
101
103
  return response_data
102
104
 
103
105
  except (requests.RequestException, requests.Timeout, json.JSONDecodeError) as error:
104
- print(f" Error fetching {username}/{repo_name}: {error}")
105
- return None
106
+ print(f"⚠️ API error for {username}/{repo_name}: {error}, trying HTML scraper...")
107
+ return scrape_github_release_page(username, repo_name, version)
106
108
 
107
109
 
108
110
  def get_release_info(
@@ -141,85 +143,3 @@ def extract_release_info(release_data: Dict[str, Any]) -> Optional[ReleaseInfo]:
141
143
  "assets_count": len(assets)
142
144
  }
143
145
 
144
-
145
- # def main() -> None:
146
- # """Main function to process installer JSON files and fetch GitHub release data."""
147
- # # Define paths
148
- # current_dir = Path(__file__).parent
149
- # installer_dir = current_dir.parent.parent / "jobs" / "installer"
150
-
151
- # standard_json = installer_dir / "installer_data.json"
152
- # output_json = current_dir / "github_releases.json"
153
-
154
- # print("🔍 Starting GitHub release data extraction...")
155
- # print(f"📁 Processing files from: {installer_dir}")
156
-
157
- # # Extract GitHub repositories from both files
158
- # all_github_repos: Set[str] = set()
159
-
160
- # if standard_json.exists():
161
- # print(f"📄 Reading {standard_json.name}...")
162
- # repos = extract_github_repos_from_json(standard_json)
163
- # all_github_repos.update(repos)
164
- # print(f" Found {len(repos)} GitHub repos")
165
- # else:
166
- # print(f"⚠️ File not found: {standard_json}")
167
- # print(f"🎯 Total unique GitHub repositories found: {len(all_github_repos)}")
168
-
169
- # if not all_github_repos:
170
- # print("❌ No GitHub repositories found. Exiting.")
171
- # return
172
-
173
- # # Fetch release data with rate limiting
174
- # release_mapping: Dict[str, Optional[ReleaseInfo]] = {}
175
- # total_repos = len(all_github_repos)
176
-
177
- # print(f"\n🚀 Fetching release data for {total_repos} repositories...")
178
- # print("⏰ Rate limiting: 5 seconds between requests")
179
- # print("-" * 60)
180
-
181
- # for i, repo_url in enumerate(sorted(all_github_repos), 1):
182
- # repo_info = get_repo_name_from_url(repo_url)
183
-
184
- # if not repo_info:
185
- # print(f"⚠️ [{i:3d}/{total_repos}] Invalid repo URL: {repo_url}")
186
- # continue
187
-
188
- # username, repo_name = repo_info
189
- # repo_full_name = f"{username}/{repo_name}"
190
-
191
- # print(f"📡 [{i:3d}/{total_repos}] Fetching: {repo_full_name}", end=" ... ")
192
-
193
- # release_info = get_release_info(username, repo_name)
194
-
195
- # if release_info:
196
- # release_mapping[repo_url] = release_info
197
- # assets_count = release_info["assets_count"]
198
- # tag = release_info["tag_name"]
199
- # print(f"✅ {tag} ({assets_count} assets)")
200
- # else:
201
- # release_mapping[repo_url] = None
202
- # print("❌ No data")
203
-
204
- # # Rate limiting - wait 5 seconds between requests (except for the last one)
205
- # if i < total_repos:
206
- # time.sleep(5)
207
-
208
- # # Save results
209
- # output_data: OutputData = {
210
- # "generated_at": time.strftime("%Y-%m-%d %H:%M:%S UTC", time.gmtime()),
211
- # "total_repositories": len(all_github_repos),
212
- # "successful_fetches": len([v for v in release_mapping.values() if v]),
213
- # "releases": release_mapping
214
- # }
215
-
216
- # with open(output_json, 'w', encoding='utf-8') as f:
217
- # json.dump(output_data, f, indent=2, ensure_ascii=False)
218
-
219
- # successful = len([v for v in release_mapping.values() if v])
220
- # print("\n📊 Summary:")
221
- # print(f" Total repositories processed: {len(all_github_repos)}")
222
- # print(f" Successful fetches: {successful}")
223
- # print(f" Failed fetches: {len(all_github_repos) - successful}")
224
- # print(f" Output saved to: {output_json}")
225
- # print("✅ Done!")
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env python3
2
+ """HTML scraper for GitHub release pages as fallback when API rate limit is exceeded."""
3
+
4
+ import re
5
+ from typing import Any, Optional
6
+ import requests
7
+
8
+
9
+ def extract_tag_from_html(html: str, owner: str, repo: str) -> str:
10
+ patterns = [
11
+ rf'/{re.escape(owner)}/{re.escape(repo)}/releases/tag/([^"\'<>\s]+)',
12
+ rf'/{re.escape(owner)}/{re.escape(repo)}/tree/([^"\'<>\s]+)',
13
+ r'<span[^>]*class="[^"]*ml-1[^"]*"[^>]*>([^<]+)</span>',
14
+ ]
15
+ for pattern in patterns:
16
+ match = re.search(pattern, html, re.IGNORECASE)
17
+ if match:
18
+ tag = match.group(1).strip()
19
+ if tag and not tag.startswith("http"):
20
+ return tag
21
+ return ""
22
+
23
+
24
+ def extract_release_name(html: str) -> str:
25
+ patterns = [
26
+ r'<h1[^>]*class="[^"]*d-inline[^"]*"[^>]*>([^<]+)</h1>',
27
+ r'<bdi[^>]*class="[^"]*mr-2[^"]*"[^>]*>([^<]+)</bdi>',
28
+ r'<h1[^>]*>([^<]+)</h1>',
29
+ ]
30
+ for pattern in patterns:
31
+ match = re.search(pattern, html)
32
+ if match:
33
+ name = match.group(1).strip()
34
+ if name:
35
+ return name
36
+ return ""
37
+
38
+
39
+ def extract_published_at(html: str) -> str:
40
+ pattern = r'<relative-time[^>]*datetime="([^"]+)"'
41
+ match = re.search(pattern, html)
42
+ if match:
43
+ return match.group(1)
44
+ return ""
45
+
46
+
47
+ def fetch_expanded_assets(username: str, repo_name: str, tag_name: str, headers: dict[str, str]) -> list[dict[str, Any]]:
48
+ """Fetch assets from the expanded_assets endpoint which contains all downloadable files."""
49
+ assets: list[dict[str, Any]] = []
50
+ expanded_url = f"https://github.com/{username}/{repo_name}/releases/expanded_assets/{tag_name}"
51
+ try:
52
+ response = requests.get(expanded_url, timeout=30, headers=headers)
53
+ if response.status_code != 200:
54
+ print(f"⚠️ [Scraper] Could not fetch expanded assets for {username}/{repo_name}: HTTP {response.status_code}")
55
+ return assets
56
+ html = response.text
57
+ pattern = r'href="([^"]*?/releases/download/[^"]+)"[^>]*>.*?<span[^>]*class="[^"]*Truncate-text[^"]*text-bold[^"]*"[^>]*>([^<]+)</span>'
58
+ seen_urls: set[str] = set()
59
+ matches = re.findall(pattern, html, re.DOTALL)
60
+ for href, name in matches:
61
+ asset_name = name.strip()
62
+ if not asset_name or asset_name.isspace():
63
+ continue
64
+ download_url = f"https://github.com{href}" if href.startswith("/") else href
65
+ if download_url in seen_urls:
66
+ continue
67
+ seen_urls.add(download_url)
68
+ assets.append({"name": asset_name, "size": 0, "download_count": 0, "content_type": "", "created_at": "", "updated_at": "", "browser_download_url": download_url})
69
+ except requests.RequestException as error:
70
+ print(f"⚠️ [Scraper] Error fetching expanded assets for {username}/{repo_name}: {error}")
71
+ return assets
72
+
73
+
74
+ def scrape_github_release_page(username: str, repo_name: str, version: Optional[str] = None) -> Optional[dict[str, Any]]:
75
+ """Scrape GitHub release page HTML to extract release information. Falls back to this when API rate limit is hit."""
76
+ try:
77
+ requested_version = (version or "").strip()
78
+ if requested_version and requested_version.lower() != "latest":
79
+ url = f"https://github.com/{username}/{repo_name}/releases/tag/{requested_version}"
80
+ else:
81
+ url = f"https://github.com/{username}/{repo_name}/releases/latest"
82
+ headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"}
83
+ response = requests.get(url, timeout=30, headers=headers, allow_redirects=True)
84
+ if response.status_code != 200:
85
+ print(f"❌ [Scraper] Failed to fetch page for {username}/{repo_name}: HTTP {response.status_code}")
86
+ return None
87
+ html = response.text
88
+ tag_name = extract_tag_from_html(html, username, repo_name)
89
+ if not tag_name:
90
+ print(f"🔍 [Scraper] No tag found for {username}/{repo_name}")
91
+ return None
92
+ release_name = extract_release_name(html) or tag_name
93
+ published_at = extract_published_at(html)
94
+ assets = fetch_expanded_assets(username, repo_name, tag_name, headers)
95
+ print(f"✅ [Scraper] Found {len(assets)} assets for {username}/{repo_name} @ {tag_name}")
96
+ return {"tag_name": tag_name, "name": release_name, "published_at": published_at, "assets": assets}
97
+ except requests.RequestException as error:
98
+ print(f"❌ [Scraper] Error fetching {username}/{repo_name}: {error}")
99
+ return None
@@ -150,7 +150,7 @@ def install_from_github_url(github_url: str) -> None:
150
150
  size_padded = item["size_str"].ljust(max_size_len)
151
151
  downloads_padded = item["downloads_str"].rjust(max_downloads_len)
152
152
 
153
- label = f"{name_padded} {size_padded} | {downloads_padded} | 📅 {item['date_str']}"
153
+ label = f"{name_padded} {size_padded} | #🔽 {downloads_padded} | 📅 {item['date_str']}"
154
154
  options_map[label] = item["asset"]
155
155
 
156
156
  if not options_map:
@@ -13,6 +13,9 @@ import subprocess
13
13
  from typing import Optional
14
14
 
15
15
 
16
+ PACAKGE_MANAGERS = ["bun", "npm", "pip", "uv", "winget", "powershell", "irm", "brew", "curl", "sudo"]
17
+
18
+
16
19
  class Installer:
17
20
  def __init__(self, installer_data: InstallerData):
18
21
  self.installer_data: InstallerData = installer_data
@@ -62,8 +65,13 @@ class Installer:
62
65
  if installer_arch_os is None:
63
66
  raise ValueError(f"No installation pattern for {exe_name} on {os_name} {arch}")
64
67
  version_to_be_installed: str = "unknown" # Initialize to ensure it's always bound
65
- if repo_url == "CMD":
66
- if any(pm in installer_arch_os for pm in ["npm ", "pip ", "winget ", "brew ", "curl ", "sudo"]):
68
+
69
+ package_manager_installer = any(pm in installer_arch_os.split(" ") for pm in PACAKGE_MANAGERS)
70
+ script_installer = installer_arch_os.endswith((".sh", ".py", ".ps1"))
71
+ binary_download_link = installer_arch_os.startswith("https://") or installer_arch_os.startswith("http://")
72
+
73
+ if (repo_url == "CMD") or package_manager_installer or script_installer or binary_download_link:
74
+ if package_manager_installer:
67
75
  from rich import print as rprint
68
76
  from rich.panel import Panel
69
77
  from rich.console import Group
@@ -81,7 +89,7 @@ class Installer:
81
89
  sub_panels.append(Panel(result.stderr, title="STDERR", style="red"))
82
90
  group_content = Group(f"❌ {desc} failed\nReturn code: {result.returncode}", *sub_panels)
83
91
  rprint(Panel(group_content, title=desc, style="red"))
84
- elif installer_arch_os.endswith((".sh", ".py", ".ps1")):
92
+ elif script_installer:
85
93
  import machineconfig.jobs.installer as module
86
94
  from pathlib import Path
87
95
  search_root = Path(module.__file__).parent
@@ -106,7 +114,7 @@ class Installer:
106
114
  import runpy
107
115
  runpy.run_path(str(installer_path), run_name=None)["main"](self.installer_data, version=version)
108
116
  version_to_be_installed = str(version)
109
- elif installer_arch_os.startswith("https://") or installer_arch_os.startswith("http://"):
117
+ elif binary_download_link:
110
118
  downloaded_object = download_and_prepare(installer_arch_os)
111
119
  if downloaded_object.suffix in [".exe", ""]: # likely an executable
112
120
  if platform.system() == "Windows":
@@ -1,18 +1,5 @@
1
- """Devops Devapps Install
2
-
3
-
4
- sudo apt update && sudo apt install -y \
5
- git gcc g++ clang \
6
- yasm nasm pkg-config \
7
- meson ninja-build \
8
- autoconf automake libtool \
9
- libx11-dev libxext-dev libxrandr-dev libxrender-dev libxss-dev \
10
- libvdpau-dev libgl1-mesa-dev libegl1-mesa-dev libxv-dev \
11
- libasound2-dev libpulse-dev \
12
- libfribidi-dev libfreetype-dev libfontconfig1-dev libharfbuzz-dev \
13
- libjpeg-dev libssl-dev zlib1g-dev python3-pip
14
-
15
1
 
2
+ """Devops Devapps Install
16
3
  """
17
4
 
18
5
  from machineconfig.utils.installer_utils.installer_helper import get_group_name_to_repr
@@ -20,7 +7,6 @@ import typer
20
7
  from typing import Annotated, Optional
21
8
 
22
9
 
23
-
24
10
  def main_installer_cli(
25
11
  which: Annotated[Optional[str], typer.Argument(..., help="Comma-separated list of program/groups names to install (if --group flag is set).")] = None,
26
12
  group: Annotated[bool, typer.Option(..., "--group", "-g", help="Treat 'which' as a group name. A group is bundle of apps.")] = False,
@@ -104,7 +104,7 @@ def install_deb_package(downloaded: Path) -> None:
104
104
 
105
105
  def download_and_prepare(download_url: str) -> PathExtended:
106
106
  # archive_path = PathExtended(download_url).download(folder=INSTALL_TMP_DIR)
107
- from machineconfig.scripts.python.helpers_utils.download import download
107
+ from machineconfig.scripts.python.helpers.helpers_utils.download import download
108
108
  downloaded_object = download(download_url, output_dir=str(INSTALL_TMP_DIR))
109
109
  if downloaded_object is None:
110
110
  raise ValueError(f"Failed to download from URL: {download_url}")
@@ -121,7 +121,7 @@ def download_and_prepare(download_url: str) -> PathExtended:
121
121
  if nested_path.is_file() and any(ex in nested_path.suffixes for ex in DECOMPRESS_SUPPORTED_FORMATS):
122
122
  extracted_path = nested_path.decompress()
123
123
  nested_path.delete(sure=True)
124
- elif extracted_path.is_dir() and len(extracted_path.search("*", r=True)) == 1:
124
+ elif extracted_path.is_dir() and len([p for p in extracted_path.rglob("*") if not p.name.startswith(".")]) == 1:
125
125
  only_file_in = next(extracted_path.glob("*"))
126
126
  if only_file_in.is_file() and any(ext in str(only_file_in) for ext in DECOMPRESS_SUPPORTED_FORMATS): # further decompress
127
127
  extracted_path = only_file_in.decompress()
@@ -22,15 +22,15 @@ def find_move_delete_windows(downloaded_file_path: PathExtended, tool_name: Opti
22
22
  else:
23
23
  print(f"🔎 Searching for executable in: {downloaded_file_path}")
24
24
  if tool_name is None:
25
- exe = downloaded_file_path.search("*.exe", r=True)[0]
25
+ exe = list(downloaded_file_path.rglob("*.exe"))[0]
26
26
  print(f"✅ Found executable: {exe}")
27
27
  else:
28
- tmp = downloaded_file_path.search(f"{tool_name}.exe", r=True)
28
+ tmp = list(downloaded_file_path.rglob(f"{tool_name}.exe"))
29
29
  if len(tmp) == 1:
30
30
  exe = tmp[0]
31
31
  print(f"✅ Found exact match for {tool_name}.exe: {exe}")
32
32
  else:
33
- search_res = downloaded_file_path.search("*.exe", r=True)
33
+ search_res = list(downloaded_file_path.rglob("*.exe"))
34
34
  if len(search_res) == 0:
35
35
  print(f"❌ ERROR: No executable found in {downloaded_file_path}")
36
36
  raise IndexError(f"No executable found in {downloaded_file_path}")
@@ -38,8 +38,8 @@ def find_move_delete_windows(downloaded_file_path: PathExtended, tool_name: Opti
38
38
  exe = search_res[0]
39
39
  print(f"✅ Found single executable: {exe}")
40
40
  else:
41
- exe = max(search_res, key=lambda x: x.size("kb"))
42
- print(f"✅ Selected largest executable ({exe.size('kb')} KB): {exe}")
41
+ exe = max(search_res, key=lambda x: x.stat().st_size)
42
+ print(f"✅ Selected largest executable ({round(exe.stat().st_size / 1024, 1)} KB): {exe}")
43
43
  if rename_to and exe.name != rename_to:
44
44
  print(f"🏷️ Renaming '{exe.name}' to '{rename_to}'")
45
45
  exe = exe.with_name(name=rename_to, inplace=True)
@@ -70,20 +70,20 @@ def find_move_delete_linux(downloaded: PathExtended, tool_name: Optional[str], d
70
70
  print(f"📄 Found direct executable file: {exe}")
71
71
  else:
72
72
  print(f"🔎 Searching for executable in: {downloaded}")
73
- res = downloaded.search(f"*{tool_name}*", folders=False, r=True)
73
+ res = [p for p in downloaded.rglob(f"*{tool_name}*") if p.is_file()]
74
74
  if len(res) == 1:
75
75
  exe = res[0]
76
76
  print(f"✅ Found match for pattern '*{tool_name}*': {exe}")
77
77
  else:
78
78
  if tool_name is None: # no tool name provided, get the largest executable
79
- search_res = downloaded.search("*", folders=False, files=True, r=True)
79
+ search_res = [p for p in downloaded.rglob("*") if p.is_file()]
80
80
  if len(search_res) == 0:
81
81
  print(f"❌ ERROR: No search results in `{downloaded}`")
82
82
  raise IndexError(f"No executable found in {downloaded}")
83
- exe = max(search_res, key=lambda x: x.size("kb"))
84
- print(f"✅ Selected largest executable ({exe.size('kb')} KB): {exe}")
83
+ exe = max(search_res, key=lambda x: x.stat().st_size)
84
+ print(f"✅ Selected largest executable ({round(exe.stat().st_size / 1024, 1)} KB): {exe}")
85
85
  else:
86
- exe_search_res = downloaded.search(tool_name, folders=False, r=True)
86
+ exe_search_res = [p for p in downloaded.rglob(tool_name) if p.is_file()]
87
87
  if len(exe_search_res) == 0:
88
88
  print(f"❌ ERROR: No search results for `{tool_name}` in `{downloaded}`")
89
89
  raise IndexError(f"No executable found in {downloaded}")
@@ -91,8 +91,8 @@ def find_move_delete_linux(downloaded: PathExtended, tool_name: Optional[str], d
91
91
  exe = exe_search_res[0]
92
92
  print(f"✅ Found exact match for '{tool_name}': {exe}")
93
93
  else:
94
- exe = max(exe_search_res, key=lambda x: x.size("kb"))
95
- print(f"✅ Selected largest executable ({exe.size('kb')} KB): {exe}")
94
+ exe = max(exe_search_res, key=lambda x: x.stat().st_size)
95
+ print(f"✅ Selected largest executable ({round(exe.stat().st_size / 1024, 1)} KB): {exe}")
96
96
 
97
97
  if rename_to and exe.name != rename_to:
98
98
  print(f"🏷️ Renaming '{exe.name}' to '{rename_to}'")
@@ -155,7 +155,7 @@ def check_tool_exists(tool_name: str) -> bool:
155
155
  version = result.stdout.strip().lstrip('v')
156
156
  nvm_bin_path = Path.home() / ".nvm" / "versions" / "node" / f"v{version}" / "bin" / tool_name
157
157
  npm_check = nvm_bin_path.is_file()
158
- except subprocess.CalledProcessError:
158
+ except (subprocess.CalledProcessError, FileNotFoundError):
159
159
  pass
160
160
  return npm_check
161
161
  else: