machineconfig 7.50__py3-none-any.whl → 8.12__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 (298) hide show
  1. machineconfig/cluster/remote/cloud_manager.py +1 -1
  2. machineconfig/cluster/sessions_managers/utils/maker.py +23 -11
  3. machineconfig/cluster/sessions_managers/wt_local_manager.py +22 -19
  4. machineconfig/cluster/sessions_managers/wt_remote_manager.py +3 -1
  5. machineconfig/cluster/sessions_managers/zellij_local_manager.py +3 -1
  6. machineconfig/cluster/sessions_managers/zellij_remote_manager.py +3 -2
  7. machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +2 -2
  8. machineconfig/jobs/installer/installer_data.json +1185 -165
  9. machineconfig/jobs/installer/linux_scripts/q.sh +10 -7
  10. machineconfig/jobs/installer/linux_scripts/redis.sh +1 -0
  11. machineconfig/jobs/installer/package_groups.py +52 -84
  12. machineconfig/jobs/installer/powershell_scripts/install_fonts.ps1 +129 -34
  13. machineconfig/jobs/installer/{custom → python_scripts}/boxes.py +2 -2
  14. machineconfig/jobs/installer/{custom_dev → python_scripts}/brave.py +5 -3
  15. machineconfig/jobs/installer/python_scripts/cloudflare_warp_cli.py +23 -0
  16. machineconfig/jobs/installer/{custom_dev → python_scripts}/code.py +4 -1
  17. machineconfig/jobs/installer/{custom_dev → python_scripts}/dubdb_adbc.py +1 -1
  18. machineconfig/jobs/installer/{custom → python_scripts}/hx.py +16 -12
  19. machineconfig/jobs/installer/{custom_dev → python_scripts}/nerdfont.py +2 -2
  20. machineconfig/jobs/installer/{custom_dev → python_scripts}/nerfont_windows_helper.py +27 -22
  21. machineconfig/jobs/installer/python_scripts/sysabc.py +139 -0
  22. machineconfig/jobs/installer/{custom_dev → python_scripts}/wezterm.py +2 -19
  23. machineconfig/jobs/installer/{custom_dev → python_scripts}/winget.py +10 -14
  24. machineconfig/jobs/installer/python_scripts/yazi.py +121 -0
  25. machineconfig/{scripts/python/nw → jobs/scripts/bash_scripts}/mount_nfs +0 -1
  26. machineconfig/jobs/scripts/powershell_scripts/mount_ssh.ps1 +13 -0
  27. machineconfig/jobs/scripts/powershell_scripts/obs.ps1 +4 -0
  28. machineconfig/jobs/scripts_dynamic/a.py +25 -0
  29. machineconfig/logger.py +0 -1
  30. machineconfig/profile/create_helper.py +21 -22
  31. machineconfig/profile/create_links_export.py +25 -11
  32. machineconfig/profile/create_shell_profile.py +14 -3
  33. machineconfig/profile/mapper.toml +8 -6
  34. machineconfig/scripts/__init__.py +0 -4
  35. machineconfig/scripts/linux/wrap_mcfg +20 -21
  36. machineconfig/scripts/python/agents.py +74 -50
  37. machineconfig/scripts/python/ai/initai.py +1 -1
  38. machineconfig/scripts/python/ai/scripts/command_runner.ps1 +33 -0
  39. machineconfig/scripts/python/ai/{command_runner → scripts}/command_runner.sh +1 -1
  40. machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +1 -1
  41. machineconfig/scripts/python/ai/solutions/copilot/{chatmodes/Thinking-Beast-Mode.chatmode.md → agents/Thinking-Beast-Mode.agent.md} +0 -1
  42. machineconfig/scripts/python/ai/solutions/copilot/{chatmodes/Ultimate-Transparent-Thinking-Beast-Mode.chatmode.md → agents/Ultimate-Transparent-Thinking-Beast-Mode.agent.md} +0 -1
  43. machineconfig/scripts/python/ai/solutions/copilot/{chatmodes/deepResearch.chatmode.md → agents/deepResearch.agent.md} +2 -2
  44. machineconfig/scripts/python/ai/solutions/copilot/github_copilot.py +5 -5
  45. machineconfig/scripts/python/ai/solutions/copilot/instructions/python/dev.instructions.md +4 -0
  46. machineconfig/scripts/python/ai/solutions/copilot/instructions/python/watch_exec.prompt.md +20 -0
  47. machineconfig/scripts/python/ai/solutions/generic.py +1 -1
  48. machineconfig/scripts/python/ai/{generate_files.py → utils/generate_files.py} +2 -2
  49. machineconfig/scripts/python/cloud.py +6 -6
  50. machineconfig/scripts/python/croshell.py +67 -60
  51. machineconfig/scripts/python/devops.py +41 -21
  52. machineconfig/scripts/python/devops_navigator.py +0 -4
  53. machineconfig/scripts/python/env_manager/env_manager_tui.py +204 -0
  54. machineconfig/scripts/python/env_manager/path_manager_tui.py +1 -1
  55. machineconfig/scripts/python/fire_jobs.py +95 -67
  56. machineconfig/scripts/python/ftpx.py +44 -17
  57. machineconfig/scripts/python/helpers/ast_search.py +74 -0
  58. machineconfig/scripts/python/helpers/qr_code.py +166 -0
  59. machineconfig/scripts/python/helpers/repo_rag.py +325 -0
  60. machineconfig/scripts/python/helpers/symantic_search.py +25 -0
  61. machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_crush.json +1 -1
  62. machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_crush.py +9 -7
  63. machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_gemini.py +21 -8
  64. machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_qwen.py +0 -12
  65. machineconfig/scripts/python/helpers_agents/fire_agents_help_launch.py +30 -11
  66. machineconfig/scripts/python/helpers_agents/fire_agents_helper_types.py +9 -2
  67. machineconfig/scripts/python/helpers_agents/privacy/configs/aichat/config.yaml +5 -0
  68. machineconfig/scripts/python/helpers_agents/privacy/configs/aider/.aider.conf.yml +2 -0
  69. machineconfig/scripts/python/helpers_agents/privacy/configs/copilot/config.yml +1 -0
  70. machineconfig/scripts/python/helpers_agents/privacy/configs/crush/crush.json +10 -0
  71. machineconfig/scripts/python/helpers_agents/privacy/configs/gemini/settings.json +12 -0
  72. machineconfig/scripts/python/helpers_agents/privacy/privacy.py +109 -0
  73. machineconfig/scripts/python/helpers_agents/templates/prompt.txt +8 -4
  74. machineconfig/scripts/python/helpers_agents/templates/template.sh +18 -8
  75. machineconfig/scripts/python/helpers_cloud/cloud_copy.py +28 -21
  76. machineconfig/scripts/python/helpers_cloud/cloud_helpers.py +1 -1
  77. machineconfig/scripts/python/helpers_cloud/cloud_mount.py +19 -17
  78. machineconfig/scripts/python/helpers_cloud/cloud_sync.py +8 -7
  79. machineconfig/scripts/python/helpers_croshell/crosh.py +3 -3
  80. machineconfig/scripts/python/helpers_croshell/start_slidev.py +6 -7
  81. machineconfig/scripts/python/helpers_devops/cli_config.py +19 -25
  82. machineconfig/scripts/python/helpers_devops/cli_config_dotfile.py +22 -13
  83. machineconfig/scripts/python/helpers_devops/cli_nw.py +113 -26
  84. machineconfig/scripts/python/helpers_devops/cli_repos.py +37 -11
  85. machineconfig/scripts/python/helpers_devops/cli_self.py +84 -39
  86. machineconfig/scripts/python/helpers_devops/cli_share_file.py +9 -9
  87. machineconfig/scripts/python/helpers_devops/cli_share_server.py +13 -12
  88. machineconfig/scripts/python/helpers_devops/{cli_terminal.py → cli_share_terminal.py} +15 -17
  89. machineconfig/scripts/python/helpers_devops/devops_backup_retrieve.py +4 -4
  90. machineconfig/scripts/python/helpers_devops/devops_status.py +7 -19
  91. machineconfig/scripts/python/helpers_devops/run_script.py +168 -0
  92. machineconfig/scripts/python/helpers_devops/themes/choose_wezterm_theme.py +1 -1
  93. machineconfig/scripts/python/helpers_fire_command/file_wrangler.py +2 -19
  94. machineconfig/scripts/python/helpers_fire_command/fire_jobs_args_helper.py +1 -0
  95. machineconfig/scripts/python/helpers_fire_command/fire_jobs_route_helper.py +25 -15
  96. machineconfig/scripts/python/helpers_msearch/scripts_linux/fzfg +3 -3
  97. machineconfig/scripts/python/helpers_msearch/scripts_windows/fzfg.ps1 +58 -1
  98. machineconfig/scripts/python/helpers_navigator/command_tree.py +50 -18
  99. machineconfig/scripts/python/helpers_network/address.py +176 -0
  100. machineconfig/scripts/python/helpers_network/address_switch.py +78 -0
  101. machineconfig/scripts/python/{nw → helpers_network}/mount_nfs.py +2 -2
  102. machineconfig/scripts/python/{nw → helpers_network}/mount_ssh.py +1 -1
  103. machineconfig/scripts/python/{nw/devops_add_identity.py → helpers_network/ssh_add_identity.py} +35 -1
  104. machineconfig/scripts/python/{nw/devops_add_ssh_key.py → helpers_network/ssh_add_ssh_key.py} +26 -7
  105. machineconfig/scripts/python/{nw → helpers_network}/ssh_debug_linux.py +7 -7
  106. machineconfig/scripts/python/{nw → helpers_network}/ssh_debug_windows.py +4 -4
  107. machineconfig/scripts/python/helpers_repos/clone.py +0 -1
  108. machineconfig/scripts/python/helpers_repos/cloud_repo_sync.py +13 -5
  109. machineconfig/scripts/python/helpers_repos/entrypoint.py +2 -1
  110. machineconfig/scripts/python/helpers_repos/record.py +2 -1
  111. machineconfig/scripts/python/helpers_repos/repo_analyzer_1.py +160 -0
  112. machineconfig/scripts/python/helpers_repos/{count_lines.py → repo_analyzer_2.py} +113 -192
  113. machineconfig/scripts/python/helpers_sessions/sessions_multiprocess.py +19 -13
  114. machineconfig/scripts/python/helpers_utils/download.py +150 -0
  115. machineconfig/scripts/python/helpers_utils/pdf.py +96 -0
  116. machineconfig/scripts/python/helpers_utils/python.py +187 -0
  117. machineconfig/scripts/python/interactive.py +17 -26
  118. machineconfig/scripts/python/{machineconfig.py → mcfg_entry.py} +4 -5
  119. machineconfig/scripts/python/msearch.py +57 -6
  120. machineconfig/scripts/python/sessions.py +100 -31
  121. machineconfig/scripts/python/terminal.py +26 -17
  122. machineconfig/scripts/python/utils.py +17 -15
  123. machineconfig/scripts/windows/wrap_mcfg.ps1 +6 -3
  124. machineconfig/settings/lf/windows/lfcd.ps1 +1 -1
  125. machineconfig/settings/linters/.ruff.toml +1 -1
  126. machineconfig/settings/shells/bash/init.sh +29 -2
  127. machineconfig/settings/shells/ipy/profiles/default/startup/playext.py +1 -1
  128. machineconfig/settings/shells/nushell/config.nu +2 -2
  129. machineconfig/settings/shells/nushell/env.nu +45 -6
  130. machineconfig/settings/shells/nushell/init.nu +282 -95
  131. machineconfig/settings/shells/pwsh/init.ps1 +1 -0
  132. machineconfig/settings/shells/wezterm/wezterm.lua +2 -0
  133. machineconfig/settings/shells/zsh/init.sh +1 -8
  134. machineconfig/settings/television/cable_unix/alias.toml +8 -0
  135. machineconfig/settings/television/cable_unix/aws-buckets.toml +14 -0
  136. machineconfig/settings/television/cable_unix/aws-instances.toml +13 -0
  137. machineconfig/settings/television/cable_unix/bash-history.toml +8 -0
  138. machineconfig/settings/television/cable_unix/channels.toml +19 -0
  139. machineconfig/settings/television/cable_unix/dirs.toml +13 -0
  140. machineconfig/settings/television/cable_unix/distrobox-list.toml +42 -0
  141. machineconfig/settings/television/cable_unix/docker-images.toml +13 -0
  142. machineconfig/settings/television/cable_unix/dotfiles.toml +11 -0
  143. machineconfig/settings/television/cable_unix/env.toml +17 -0
  144. machineconfig/settings/television/cable_unix/files.toml +11 -0
  145. machineconfig/settings/television/cable_unix/fish-history.toml +8 -0
  146. machineconfig/settings/television/cable_unix/git-branch.toml +11 -0
  147. machineconfig/settings/television/cable_unix/git-diff.toml +10 -0
  148. machineconfig/settings/television/cable_unix/git-log.toml +12 -0
  149. machineconfig/settings/television/cable_unix/git-reflog.toml +12 -0
  150. machineconfig/settings/television/cable_unix/git-repos.toml +16 -0
  151. machineconfig/settings/television/cable_unix/guix.toml +20 -0
  152. machineconfig/settings/television/cable_unix/just-recipes.toml +18 -0
  153. machineconfig/settings/television/cable_unix/k8s-deployments.toml +36 -0
  154. machineconfig/settings/television/cable_unix/k8s-pods.toml +50 -0
  155. machineconfig/settings/television/cable_unix/k8s-services.toml +36 -0
  156. machineconfig/settings/television/cable_unix/man-pages.toml +24 -0
  157. machineconfig/settings/television/cable_unix/nu-history.toml +7 -0
  158. machineconfig/settings/television/cable_unix/procs.toml +20 -0
  159. machineconfig/settings/television/cable_unix/text.toml +17 -0
  160. machineconfig/settings/television/cable_unix/tldr.toml +18 -0
  161. machineconfig/settings/television/cable_unix/zsh-history.toml +9 -0
  162. machineconfig/settings/television/cable_windows/alias.toml +7 -0
  163. machineconfig/settings/television/cable_windows/dirs.toml +13 -0
  164. machineconfig/settings/television/cable_windows/docker-images.toml +13 -0
  165. machineconfig/settings/television/cable_windows/dotfiles.toml +11 -0
  166. machineconfig/settings/television/cable_windows/env.toml +17 -0
  167. machineconfig/settings/television/cable_windows/files.toml +14 -0
  168. machineconfig/settings/television/cable_windows/git-branch.toml +11 -0
  169. machineconfig/settings/television/cable_windows/git-diff.toml +10 -0
  170. machineconfig/settings/television/cable_windows/git-log.toml +11 -0
  171. machineconfig/settings/television/cable_windows/git-reflog.toml +11 -0
  172. machineconfig/settings/television/cable_windows/git-repos.toml +15 -0
  173. machineconfig/settings/television/cable_windows/nu-history.toml +7 -0
  174. machineconfig/settings/television/cable_windows/pwsh-history.toml +6 -0
  175. machineconfig/settings/television/cable_windows/text.toml +17 -0
  176. machineconfig/settings/wt/__init__.py +0 -0
  177. machineconfig/settings/yazi/init.lua +49 -24
  178. machineconfig/settings/yazi/keymap_linux.toml +19 -4
  179. machineconfig/settings/yazi/keymap_windows.toml +0 -1
  180. machineconfig/settings/yazi/shell/yazi_cd.ps1 +29 -5
  181. machineconfig/settings/yazi/theme.toml +4 -0
  182. machineconfig/settings/yazi/yazi_linux.toml +84 -0
  183. machineconfig/settings/yazi/yazi_windows.toml +58 -0
  184. machineconfig/settings/zellij/layouts/st.kdl +39 -8
  185. machineconfig/setup_linux/__init__.py +1 -2
  186. machineconfig/setup_linux/apps_desktop.sh +8 -27
  187. machineconfig/setup_linux/web_shortcuts/interactive.sh +12 -10
  188. machineconfig/setup_linux/web_shortcuts/live_from_github.sh +31 -0
  189. machineconfig/setup_mac/__init__.py +2 -3
  190. machineconfig/setup_windows/__init__.py +3 -5
  191. machineconfig/setup_windows/ssh/openssh-server.ps1 +1 -1
  192. machineconfig/setup_windows/uv.ps1 +8 -1
  193. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +12 -10
  194. machineconfig/setup_windows/web_shortcuts/live_from_github.ps1 +30 -0
  195. machineconfig/setup_windows/web_shortcuts/quick_init.ps1 +17 -0
  196. machineconfig/utils/accessories.py +7 -4
  197. machineconfig/utils/code.py +39 -11
  198. machineconfig/utils/files/headers.py +2 -2
  199. machineconfig/utils/installer_utils/github_release_bulk.py +156 -119
  200. machineconfig/utils/installer_utils/install_from_url.py +183 -0
  201. machineconfig/utils/installer_utils/installer_class.py +43 -100
  202. machineconfig/utils/installer_utils/installer_cli.py +175 -0
  203. machineconfig/utils/installer_utils/installer_helper.py +129 -0
  204. machineconfig/utils/installer_utils/{installer_abc.py → installer_locator_utils.py} +36 -85
  205. machineconfig/utils/{installer.py → installer_utils/installer_runner.py} +16 -59
  206. machineconfig/utils/io.py +0 -1
  207. machineconfig/utils/links.py +2 -2
  208. machineconfig/utils/meta.py +30 -16
  209. machineconfig/utils/options.py +42 -24
  210. machineconfig/utils/options_tv.py +119 -0
  211. machineconfig/utils/path_extended.py +42 -20
  212. machineconfig/utils/path_helper.py +75 -22
  213. machineconfig/utils/procs.py +1 -1
  214. machineconfig/utils/scheduler.py +20 -53
  215. machineconfig/utils/schemas/layouts/layout_types.py +1 -1
  216. machineconfig/utils/ssh.py +159 -418
  217. machineconfig/utils/ssh_utils/abc.py +5 -0
  218. machineconfig/utils/ssh_utils/copy_from_here.py +111 -0
  219. machineconfig/utils/ssh_utils/copy_to_here.py +303 -0
  220. machineconfig/utils/ssh_utils/utils.py +142 -0
  221. machineconfig/utils/ssh_utils/wsl.py +210 -0
  222. machineconfig/utils/terminal.py +1 -0
  223. machineconfig/utils/upgrade_packages.py +6 -1
  224. machineconfig/utils/ve.py +12 -4
  225. machineconfig-8.12.dist-info/METADATA +132 -0
  226. {machineconfig-7.50.dist-info → machineconfig-8.12.dist-info}/RECORD +265 -215
  227. {machineconfig-7.50.dist-info → machineconfig-8.12.dist-info}/entry_points.txt +2 -4
  228. machineconfig/jobs/installer/linux_scripts/pgsql.sh +0 -41
  229. machineconfig/jobs/installer/linux_scripts/timescaledb.sh +0 -71
  230. machineconfig/jobs/installer/powershell_scripts/archive_pygraphviz.ps1 +0 -12
  231. machineconfig/jobs/installer/powershell_scripts/openssh-server_add_key.ps1 +0 -7
  232. machineconfig/jobs/installer/powershell_scripts/openssh-server_copy-ssh-id.ps1 +0 -14
  233. machineconfig/scripts/linux/other/switch_ip +0 -20
  234. machineconfig/scripts/python/ai/command_runner/prompt.txt +0 -9
  235. machineconfig/scripts/python/define.py +0 -31
  236. machineconfig/scripts/python/explore.py +0 -49
  237. machineconfig/scripts/python/helpers_devops/cli_utils.py +0 -246
  238. machineconfig/scripts/python/helpers_msearch/scripts_linux/fzfag +0 -17
  239. machineconfig/scripts/python/helpers_msearch/scripts_linux/fzfrga +0 -21
  240. machineconfig/scripts/python/helpers_msearch/scripts_linux/skrg +0 -4
  241. machineconfig/scripts/python/helpers_msearch/scripts_windows/fzfb.ps1 +0 -3
  242. machineconfig/scripts/python/helpers_msearch/scripts_windows/fzfrga.bat +0 -20
  243. machineconfig/scripts/python/helpers_repos/count_lines_frontend.py +0 -17
  244. machineconfig/scripts/python/nw/add_ssh_key.py +0 -148
  245. machineconfig/scripts/python/nw/wsl_windows_transfer.py +0 -66
  246. machineconfig/scripts/windows/mounts/mount_ssh.ps1 +0 -13
  247. machineconfig/settings/lf/linux/exe/fzf_nano.sh +0 -16
  248. machineconfig/settings/lf/windows/fzf_edit.ps1 +0 -6
  249. machineconfig/settings/lf/windows/tst.ps1 +0 -1
  250. machineconfig/settings/yazi/yazi.toml +0 -17
  251. machineconfig/setup_linux/apps.sh +0 -66
  252. machineconfig/setup_linux/others/cli_installation.sh +0 -137
  253. machineconfig/setup_linux/ssh/openssh_all.sh +0 -25
  254. machineconfig/setup_linux/ssh/openssh_wsl.sh +0 -38
  255. machineconfig/setup_mac/apps.sh +0 -73
  256. machineconfig/setup_windows/apps.ps1 +0 -62
  257. machineconfig/setup_windows/others/obs.ps1 +0 -4
  258. machineconfig/setup_windows/ssh/add_identity.ps1 +0 -11
  259. machineconfig/utils/installer_utils/installer.py +0 -221
  260. machineconfig-7.50.dist-info/METADATA +0 -92
  261. /machineconfig/jobs/installer/linux_scripts/{warp-cli.sh → cloudflare_warp_cli.sh} +0 -0
  262. /machineconfig/jobs/installer/{custom_dev → python_scripts}/__init__.py +0 -0
  263. /machineconfig/jobs/installer/{custom_dev → python_scripts}/alacritty.py +0 -0
  264. /machineconfig/jobs/installer/{custom_dev → python_scripts}/bypass_paywall.py +0 -0
  265. /machineconfig/jobs/installer/{custom_dev → python_scripts}/cursor.py +0 -0
  266. /machineconfig/jobs/installer/{custom_dev → python_scripts}/espanso.py +0 -0
  267. /machineconfig/jobs/installer/{custom → python_scripts}/gh.py +0 -0
  268. /machineconfig/jobs/installer/{custom_dev → python_scripts}/goes.py +0 -0
  269. /machineconfig/jobs/installer/{custom_dev → python_scripts}/lvim.py +0 -0
  270. /machineconfig/jobs/installer/{custom_dev → python_scripts}/redis.py +0 -0
  271. /machineconfig/{setup_linux/others → jobs/scripts/bash_scripts}/android.sh +0 -0
  272. /machineconfig/jobs/{installer/linux_scripts → scripts/bash_scripts}/lid.sh +0 -0
  273. /machineconfig/{setup_linux/others → jobs/scripts/bash_scripts}/mint_keyboard_shortcuts.sh +0 -0
  274. /machineconfig/{scripts/python/nw → jobs/scripts/bash_scripts}/mount_drive +0 -0
  275. /machineconfig/{scripts/python/nw → jobs/scripts/bash_scripts}/mount_nw_drive +0 -0
  276. /machineconfig/{scripts/python/nw → jobs/scripts/bash_scripts}/mount_smb +0 -0
  277. /machineconfig/{scripts/linux/other → jobs/scripts/bash_scripts}/share_cloud.sh +0 -0
  278. /machineconfig/{scripts/linux/other → jobs/scripts/bash_scripts}/share_nfs +0 -0
  279. /machineconfig/{scripts/linux/other → jobs/scripts/bash_scripts}/start_docker +0 -0
  280. /machineconfig/{scripts → jobs/scripts/powershell_scripts}/Restore-ThunderbirdProfile.ps1 +0 -0
  281. /machineconfig/{setup_windows/others → jobs/scripts/powershell_scripts}/docker.ps1 +0 -0
  282. /machineconfig/{scripts/windows/mounts → jobs/scripts/powershell_scripts}/mount_nfs.ps1 +0 -0
  283. /machineconfig/{scripts/windows/mounts → jobs/scripts/powershell_scripts}/mount_nw.ps1 +0 -0
  284. /machineconfig/{scripts/windows/mounts → jobs/scripts/powershell_scripts}/mount_smb.ps1 +0 -0
  285. /machineconfig/{setup_windows/others → jobs/scripts/powershell_scripts}/power_options.ps1 +0 -0
  286. /machineconfig/{scripts/windows/mounts → jobs/scripts/powershell_scripts}/share_cloud.cmd +0 -0
  287. /machineconfig/{scripts/windows/mounts → jobs/scripts/powershell_scripts}/share_smb.ps1 +0 -0
  288. /machineconfig/{scripts/windows/mounts → jobs/scripts/powershell_scripts}/unlock_bitlocker.ps1 +0 -0
  289. /machineconfig/scripts/python/{nw → ai/utils}/__init__.py +0 -0
  290. /machineconfig/scripts/python/ai/{vscode_tasks.py → utils/vscode_tasks.py} +0 -0
  291. /machineconfig/{settings/shells/pwsh/profile.ps1 → scripts/python/helpers_fire_command/f.py} +0 -0
  292. /machineconfig/{setup_windows/wt_and_pwsh → scripts/python/helpers_network}/__init__.py +0 -0
  293. /machineconfig/scripts/python/{nw → helpers_network}/mount_nw_drive.py +0 -0
  294. /machineconfig/scripts/python/{nw → helpers_network}/onetimeshare.py +0 -0
  295. /machineconfig/scripts/python/{nw → helpers_network}/wifi_conn.py +0 -0
  296. /machineconfig/{setup_windows/wt_and_pwsh → settings/wt}/set_wt_settings.py +0 -0
  297. {machineconfig-7.50.dist-info → machineconfig-8.12.dist-info}/WHEEL +0 -0
  298. {machineconfig-7.50.dist-info → machineconfig-8.12.dist-info}/top_level.txt +0 -0
@@ -25,7 +25,7 @@ def print_header():
25
25
  table.add_row("Virtual Environment", os.getenv('VIRTUAL_ENV', 'None'))
26
26
  table.add_row("Running @", str(Path.cwd()))
27
27
 
28
- from machineconfig.utils.installer import get_machineconfig_version
28
+ from machineconfig.utils.installer_utils.installer_runner import get_machineconfig_version
29
29
 
30
30
  console.print(Panel(table, title=f"[bold blue]✨ 🐊 Machineconfig Shell {get_machineconfig_version()} ✨ Made with 🐍 | Built with ❤️[/bold blue]", border_style="blue"))
31
31
  def print_logo(logo: str):
@@ -44,7 +44,7 @@ def print_logo(logo: str):
44
44
  _default_art = Path(random.choice(glob.glob(str(Path(__file__).parent.joinpath("art", "*")))))
45
45
  print(_default_art.read_text())
46
46
  elif platform.system() in ["Linux", "Darwin"]: # Explicitly handle both Linux and macOS
47
- from machineconfig.utils.installer_utils.installer_abc import is_executable_in_path
47
+ from machineconfig.utils.installer_utils.installer_locator_utils import is_executable_in_path
48
48
  avail_cowsay = is_executable_in_path("cowsay")
49
49
  avail_lolcat = is_executable_in_path("lolcat")
50
50
  avail_boxes = is_executable_in_path("boxes")
@@ -5,13 +5,40 @@ Extracts GitHub repository URLs and fetches latest release data with rate limiti
5
5
  """
6
6
 
7
7
  import json
8
- import time
9
- import subprocess
8
+ import requests
10
9
  from pathlib import Path
11
- from typing import Any, Dict, Optional, Set
10
+ from typing import Any, Dict, Optional, Set, TypedDict
12
11
  from urllib.parse import urlparse
13
12
 
14
13
 
14
+ class AssetInfo(TypedDict):
15
+ """Type definition for GitHub release asset information."""
16
+ name: str
17
+ size: int
18
+ download_count: int
19
+ content_type: str
20
+ created_at: str
21
+ updated_at: str
22
+ browser_download_url: str
23
+
24
+
25
+ class ReleaseInfo(TypedDict):
26
+ """Type definition for GitHub release information."""
27
+ tag_name: str
28
+ name: str
29
+ published_at: str
30
+ assets: list[AssetInfo]
31
+ assets_count: int
32
+
33
+
34
+ class OutputData(TypedDict):
35
+ """Type definition for the output JSON data structure."""
36
+ generated_at: str
37
+ total_repositories: int
38
+ successful_fetches: int
39
+ releases: Dict[str, Optional[ReleaseInfo]]
40
+
41
+
15
42
  def is_github_repo(url: str) -> bool:
16
43
  """Check if URL is a GitHub repository URL."""
17
44
  try:
@@ -19,170 +46,180 @@ def is_github_repo(url: str) -> bool:
19
46
  return parsed.netloc == "github.com" and len(parsed.path.split("/")) >= 3
20
47
  except Exception:
21
48
  return False
22
-
23
-
24
49
  def extract_github_repos_from_json(json_file_path: Path) -> Set[str]:
25
50
  """Extract GitHub repository URLs from installer JSON file."""
26
51
  github_repos: Set[str] = set()
27
-
28
52
  try:
29
53
  with open(json_file_path, 'r', encoding='utf-8') as file:
30
54
  data = json.load(file)
31
-
32
55
  for installer in data.get("installers", []):
33
56
  repo_url = installer.get("repoURL", "")
34
57
  if is_github_repo(repo_url):
35
58
  github_repos.add(repo_url)
36
-
37
59
  except (json.JSONDecodeError, FileNotFoundError) as e:
38
60
  print(f"Error reading {json_file_path}: {e}")
39
-
40
61
  return github_repos
41
-
42
-
43
- def get_repo_name_from_url(repo_url: str) -> str:
44
- """Extract owner/repo from GitHub URL."""
62
+ def get_repo_name_from_url(repo_url: str) -> Optional[tuple[str, str]]:
63
+ """Extract owner/repo from GitHub URL as a tuple (username, repo_name)."""
45
64
  try:
46
65
  parsed = urlparse(repo_url)
47
66
  path_parts = parsed.path.strip("/").split("/")
48
- return f"{path_parts[0]}/{path_parts[1]}"
67
+ return (path_parts[0], path_parts[1])
49
68
  except (IndexError, AttributeError):
50
- return ""
69
+ return None
70
+
51
71
 
72
+ def fetch_github_release_data(
73
+ username: str,
74
+ repo_name: str,
75
+ version: Optional[str] = None,
76
+ ) -> Optional[Dict[str, Any]]:
77
+ """Fetch GitHub release data for the latest or a specific tag."""
52
78
 
53
- def fetch_github_release_data(repo_name: str) -> Optional[Dict[str, Any]]:
54
- """Fetch latest release data from GitHub API using curl."""
55
79
  try:
56
- cmd = [
57
- "curl", "-s",
58
- f"https://api.github.com/repos/{repo_name}/releases/latest"
59
- ]
60
-
61
- result = subprocess.run(
62
- cmd,
63
- capture_output=True,
64
- text=True,
65
- timeout=30
66
- )
67
-
68
- if result.returncode != 0:
69
- print(f"❌ Failed to fetch data for {repo_name}: {result.stderr}")
80
+ requested_version = (version or "").strip()
81
+ if requested_version and requested_version.lower() != "latest":
82
+ url = f"https://api.github.com/repos/{username}/{repo_name}/releases/tags/{requested_version}"
83
+ else:
84
+ url = f"https://api.github.com/repos/{username}/{repo_name}/releases/latest"
85
+
86
+ response = requests.get(url, timeout=30)
87
+ if response.status_code != 200:
88
+ print(f"❌ Failed to fetch data for {username}/{repo_name}: HTTP {response.status_code}")
70
89
  return None
71
-
72
- response_data = json.loads(result.stdout)
73
-
74
- # Check if API returned an error
75
- if "message" in response_data:
76
- if "API rate limit exceeded" in response_data.get("message", ""):
77
- print(f"🚫 Rate limit exceeded for {repo_name}")
90
+
91
+ response_data = response.json()
92
+ message = response_data.get("message")
93
+ if isinstance(message, str):
94
+ if "API rate limit exceeded" in message:
95
+ print(f"🚫 Rate limit exceeded for {username}/{repo_name}")
78
96
  return None
79
- elif "Not Found" in response_data.get("message", ""):
80
- print(f"🔍 No releases found for {repo_name}")
97
+ if "Not Found" in message:
98
+ print(f"🔍 No releases found for {username}/{repo_name}")
81
99
  return None
82
-
100
+
83
101
  return response_data
84
-
85
- except (subprocess.TimeoutExpired, json.JSONDecodeError, subprocess.SubprocessError) as e:
86
- print(f"❌ Error fetching {repo_name}: {e}")
102
+
103
+ except (requests.RequestException, requests.Timeout, json.JSONDecodeError) as error:
104
+ print(f"❌ Error fetching {username}/{repo_name}: {error}")
87
105
  return None
88
106
 
89
107
 
90
- def extract_release_info(release_data: Dict[str, Any]) -> Dict[str, Any]:
108
+ def get_release_info(
109
+ username: str,
110
+ repo_name: str,
111
+ version: Optional[str] = None,
112
+ ) -> Optional[ReleaseInfo]:
113
+ """Return sanitized release information for the requested repository."""
114
+ release_data = fetch_github_release_data(username, repo_name, version)
115
+ if not release_data:
116
+ return None
117
+ return extract_release_info(release_data)
118
+
119
+
120
+ def extract_release_info(release_data: Dict[str, Any]) -> Optional[ReleaseInfo]:
91
121
  """Extract relevant information from GitHub release data."""
92
122
  if not release_data:
93
- return {}
94
-
95
- asset_names = [asset["name"] for asset in release_data.get("assets", [])]
96
-
123
+ return None
124
+ assets: list[AssetInfo] = []
125
+ for asset in release_data.get("assets", []):
126
+ asset_info: AssetInfo = {
127
+ "name": asset.get("name", ""),
128
+ "size": asset.get("size", 0),
129
+ "download_count": asset.get("download_count", 0),
130
+ "content_type": asset.get("content_type", ""),
131
+ "created_at": asset.get("created_at", ""),
132
+ "updated_at": asset.get("updated_at", ""),
133
+ "browser_download_url": asset.get("browser_download_url", "")
134
+ }
135
+ assets.append(asset_info)
97
136
  return {
98
137
  "tag_name": release_data.get("tag_name", ""),
99
138
  "name": release_data.get("name", ""),
100
139
  "published_at": release_data.get("published_at", ""),
101
- "assets": asset_names,
102
- "assets_count": len(asset_names)
140
+ "assets": assets,
141
+ "assets_count": len(assets)
103
142
  }
104
143
 
105
144
 
106
- def main() -> None:
107
- """Main function to process installer JSON files and fetch GitHub release data."""
108
- # Define paths
109
- current_dir = Path(__file__).parent
110
- installer_dir = current_dir.parent.parent / "jobs" / "installer"
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"
111
150
 
112
- standard_json = installer_dir / "installer_data.json"
113
- output_json = current_dir / "github_releases.json"
151
+ # standard_json = installer_dir / "installer_data.json"
152
+ # output_json = current_dir / "github_releases.json"
114
153
 
115
- print("🔍 Starting GitHub release data extraction...")
116
- print(f"📁 Processing files from: {installer_dir}")
154
+ # print("🔍 Starting GitHub release data extraction...")
155
+ # print(f"📁 Processing files from: {installer_dir}")
117
156
 
118
- # Extract GitHub repositories from both files
119
- all_github_repos: Set[str] = set()
157
+ # # Extract GitHub repositories from both files
158
+ # all_github_repos: Set[str] = set()
120
159
 
121
- if standard_json.exists():
122
- print(f"📄 Reading {standard_json.name}...")
123
- repos = extract_github_repos_from_json(standard_json)
124
- all_github_repos.update(repos)
125
- print(f" Found {len(repos)} GitHub repos")
126
- else:
127
- print(f"⚠️ File not found: {standard_json}")
128
- print(f"🎯 Total unique GitHub repositories found: {len(all_github_repos)}")
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)}")
129
168
 
130
- if not all_github_repos:
131
- print("❌ No GitHub repositories found. Exiting.")
132
- return
169
+ # if not all_github_repos:
170
+ # print("❌ No GitHub repositories found. Exiting.")
171
+ # return
133
172
 
134
- # Fetch release data with rate limiting
135
- release_mapping: Dict[str, Any] = {}
136
- total_repos = len(all_github_repos)
173
+ # # Fetch release data with rate limiting
174
+ # release_mapping: Dict[str, Optional[ReleaseInfo]] = {}
175
+ # total_repos = len(all_github_repos)
137
176
 
138
- print(f"\n🚀 Fetching release data for {total_repos} repositories...")
139
- print("⏰ Rate limiting: 5 seconds between requests")
140
- print("-" * 60)
177
+ # print(f"\n🚀 Fetching release data for {total_repos} repositories...")
178
+ # print("⏰ Rate limiting: 5 seconds between requests")
179
+ # print("-" * 60)
141
180
 
142
- for i, repo_url in enumerate(sorted(all_github_repos), 1):
143
- repo_name = get_repo_name_from_url(repo_url)
181
+ # for i, repo_url in enumerate(sorted(all_github_repos), 1):
182
+ # repo_info = get_repo_name_from_url(repo_url)
144
183
 
145
- if not repo_name:
146
- print(f"⚠️ [{i:3d}/{total_repos}] Invalid repo URL: {repo_url}")
147
- continue
148
-
149
- print(f"📡 [{i:3d}/{total_repos}] Fetching: {repo_name}", end=" ... ")
184
+ # if not repo_info:
185
+ # print(f"⚠️ [{i:3d}/{total_repos}] Invalid repo URL: {repo_url}")
186
+ # continue
150
187
 
151
- release_data = fetch_github_release_data(repo_name)
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=" ... ")
152
192
 
153
- if release_data:
154
- release_info = extract_release_info(release_data)
155
- release_mapping[repo_url] = release_info
156
- assets_count = release_info.get("assets_count", 0)
157
- tag = release_info.get("tag_name", "unknown")
158
- print(f"✅ {tag} ({assets_count} assets)")
159
- else:
160
- release_mapping[repo_url] = {}
161
- print("❌ No data")
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")
162
203
 
163
- # Rate limiting - wait 5 seconds between requests (except for the last one)
164
- if i < total_repos:
165
- time.sleep(5)
204
+ # # Rate limiting - wait 5 seconds between requests (except for the last one)
205
+ # if i < total_repos:
206
+ # time.sleep(5)
166
207
 
167
- # Save results
168
- output_data = {
169
- "generated_at": time.strftime("%Y-%m-%d %H:%M:%S UTC", time.gmtime()),
170
- "total_repositories": len(all_github_repos),
171
- "successful_fetches": len([v for v in release_mapping.values() if v]),
172
- "releases": release_mapping
173
- }
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
+ # }
174
215
 
175
- with open(output_json, 'w', encoding='utf-8') as f:
176
- json.dump(output_data, f, indent=2, ensure_ascii=False)
216
+ # with open(output_json, 'w', encoding='utf-8') as f:
217
+ # json.dump(output_data, f, indent=2, ensure_ascii=False)
177
218
 
178
- successful = len([v for v in release_mapping.values() if v])
179
- print("\n📊 Summary:")
180
- print(f" Total repositories processed: {len(all_github_repos)}")
181
- print(f" Successful fetches: {successful}")
182
- print(f" Failed fetches: {len(all_github_repos) - successful}")
183
- print(f" Output saved to: {output_json}")
184
- print("✅ Done!")
185
-
186
-
187
- if __name__ == "__main__":
188
- main()
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,183 @@
1
+
2
+
3
+ import platform
4
+ from typing import TYPE_CHECKING, Optional
5
+
6
+ from machineconfig.utils.installer_utils.installer_helper import install_deb_package, download_and_prepare
7
+ from machineconfig.utils.installer_utils.installer_locator_utils import find_move_delete_linux, find_move_delete_windows
8
+ from machineconfig.utils.installer_utils.github_release_bulk import (
9
+ get_repo_name_from_url,
10
+ fetch_github_release_data,
11
+ extract_release_info,
12
+ AssetInfo,
13
+ )
14
+ from machineconfig.utils.path_extended import PathExtended
15
+ from machineconfig.utils.source_of_truth import INSTALL_VERSION_ROOT
16
+
17
+ if TYPE_CHECKING:
18
+ from rich.console import Console
19
+
20
+ SUPPORTED_GITHUB_HOSTS = {"github.com", "www.github.com"}
21
+
22
+
23
+ def _format_size(size_bytes: int) -> str:
24
+ if size_bytes <= 0:
25
+ return "0 B"
26
+ units = ("B", "KiB", "MiB", "GiB", "TiB")
27
+ value = float(size_bytes)
28
+ index = 0
29
+ while value >= 1024 and index < len(units) - 1:
30
+ value /= 1024
31
+ index += 1
32
+ return f"{value:.1f} {units[index]}"
33
+
34
+
35
+ def _derive_tool_name(repo_name: str, asset_name: Optional[str]) -> Optional[str]:
36
+ repo_segment = repo_name.split("/", maxsplit=1)[-1]
37
+ repo_clean = repo_segment.replace(".git", "").lower()
38
+ repo_filtered = "".join(char for char in repo_clean if char.isalnum())
39
+ if repo_filtered:
40
+ return repo_filtered
41
+ if asset_name is None:
42
+ return None
43
+ asset_clean = asset_name.lower()
44
+ asset_filtered = "".join(char for char in asset_clean if char.isalnum())
45
+ if asset_filtered:
46
+ return asset_filtered
47
+ return None
48
+
49
+
50
+ def _finalize_install(repo_name: str, asset_name: Optional[str], version: str, extracted_path: PathExtended, console: "Console") -> None:
51
+ from rich.panel import Panel
52
+ if extracted_path.suffix == ".deb":
53
+ install_deb_package(extracted_path)
54
+ tool_name_deb = _derive_tool_name(repo_name, asset_name)
55
+ if tool_name_deb is not None:
56
+ INSTALL_VERSION_ROOT.joinpath(tool_name_deb).parent.mkdir(parents=True, exist_ok=True)
57
+ INSTALL_VERSION_ROOT.joinpath(tool_name_deb).write_text(version, encoding="utf-8")
58
+ console.print(Panel(f"Installed Debian package for [green]{tool_name_deb}[/green]", title="✅ Complete", border_style="green"))
59
+ return
60
+ system_name = platform.system()
61
+ tool_name = _derive_tool_name(repo_name, asset_name)
62
+ rename_target = f"{tool_name}.exe" if system_name == "Windows" else tool_name
63
+ try:
64
+ if system_name == "Windows":
65
+ installed_path = find_move_delete_windows(downloaded_file_path=extracted_path, tool_name=tool_name, delete=True, rename_to=rename_target)
66
+ elif system_name in {"Linux", "Darwin"}:
67
+ installed_path = find_move_delete_linux(downloaded=extracted_path, tool_name=tool_name, delete=True, rename_to=rename_target)
68
+ else:
69
+ console.print(Panel(f"Unsupported operating system: {system_name}", title="❌ Error", border_style="red"))
70
+ return None
71
+ except IndexError:
72
+ if system_name == "Windows":
73
+ installed_path = find_move_delete_windows(downloaded_file_path=extracted_path, tool_name=None, delete=True, rename_to=rename_target)
74
+ elif system_name in {"Linux", "Darwin"}:
75
+ installed_path = find_move_delete_linux(downloaded=extracted_path, tool_name="", delete=True, rename_to=rename_target)
76
+ else:
77
+ raise
78
+ if tool_name is not None:
79
+ INSTALL_VERSION_ROOT.joinpath(tool_name).parent.mkdir(parents=True, exist_ok=True)
80
+ INSTALL_VERSION_ROOT.joinpath(tool_name).write_text(version, encoding="utf-8")
81
+ console.print(Panel(f"Installed [green]{tool_name}[/green] to {installed_path}\nVersion: {version}", title="✅ Complete", border_style="green"))
82
+
83
+
84
+ def install_from_github_url(github_url: str) -> None:
85
+ from machineconfig.utils.options import choose_from_options
86
+ from rich.console import Console
87
+ from rich.panel import Panel
88
+
89
+ console = Console()
90
+ repo_info = get_repo_name_from_url(github_url)
91
+ if repo_info is None:
92
+ console.print(Panel(f"Invalid GitHub URL: {github_url}", title="❌ Error", border_style="red"))
93
+ return None
94
+ owner, repo = repo_info
95
+ repo_name = f"{owner}/{repo}"
96
+ console.print(Panel(f"Fetching latest release for [green]{repo_name}[/green]", title="🌐 GitHub", border_style="blue"))
97
+ release_raw = fetch_github_release_data(owner, repo)
98
+ if not release_raw:
99
+ console.print(Panel("No releases available for this repository.", title="❌ Error", border_style="red"))
100
+ return None
101
+
102
+ release_info = extract_release_info(release_raw)
103
+ if not release_info:
104
+ console.print(Panel("Failed to parse release information.", title="❌ Error", border_style="red"))
105
+ return None
106
+
107
+ assets = release_info["assets"]
108
+ if not assets:
109
+ console.print(Panel("No downloadable assets found in the latest release.", title="❌ Error", border_style="red"))
110
+ return None
111
+ binary_assets = assets
112
+ selection_pool = binary_assets if binary_assets else assets
113
+ if not selection_pool:
114
+ console.print(Panel("No assets available for installation.", title="❌ Error", border_style="red"))
115
+ return None
116
+
117
+ # First pass: collect all formatted data and calculate column widths
118
+ asset_data = []
119
+ for asset in selection_pool:
120
+ name = asset["name"]
121
+ download_url = asset["browser_download_url"]
122
+ if name == "" or download_url == "":
123
+ continue
124
+ size = asset["size"]
125
+ download_count = asset.get("download_count", 0)
126
+ created_at = asset.get("created_at", "")
127
+
128
+ # Format each field
129
+ size_str = f"[{_format_size(size)}]"
130
+ downloads_str = f"{download_count:,}"
131
+ date_str = created_at.split("T")[0] if created_at else "N/A"
132
+
133
+ asset_data.append({
134
+ "name": name,
135
+ "size_str": size_str,
136
+ "downloads_str": downloads_str,
137
+ "date_str": date_str,
138
+ "asset": asset
139
+ })
140
+
141
+ # Calculate maximum widths for alignment
142
+ max_name_len = max(len(item["name"]) for item in asset_data) if asset_data else 0
143
+ max_size_len = max(len(item["size_str"]) for item in asset_data) if asset_data else 0
144
+ max_downloads_len = max(len(item["downloads_str"]) for item in asset_data) if asset_data else 0
145
+
146
+ # Second pass: build aligned labels
147
+ options_map: dict[str, AssetInfo] = {}
148
+ for item in asset_data:
149
+ name_padded = item["name"].ljust(max_name_len)
150
+ size_padded = item["size_str"].ljust(max_size_len)
151
+ downloads_padded = item["downloads_str"].rjust(max_downloads_len)
152
+
153
+ label = f"{name_padded} {size_padded} | ⬇ {downloads_padded} | 📅 {item['date_str']}"
154
+ options_map[label] = item["asset"]
155
+
156
+ if not options_map:
157
+ console.print(Panel("Release assets lack download URLs.", title="❌ Error", border_style="red"))
158
+ return None
159
+ selection_label = choose_from_options(options=list(options_map.keys()), msg="Select a release asset", multi=False, header="📦 GitHub Release Assets", tv=True)
160
+ selected_asset = options_map[selection_label]
161
+ download_url_value = selected_asset["browser_download_url"]
162
+ asset_name_value = selected_asset["name"]
163
+ if download_url_value == "":
164
+ console.print(Panel("Selected asset lacks a download URL.", title="❌ Error", border_style="red"))
165
+ return None
166
+ asset_name = asset_name_value if asset_name_value != "" else "github_binary"
167
+ version = release_info["tag_name"] if release_info["tag_name"] != "" else "latest"
168
+ console.print(Panel(f"Downloading [cyan]{asset_name}[/cyan]", title="⬇️ Download", border_style="magenta"))
169
+ extracted_path = download_and_prepare(download_url_value)
170
+ _finalize_install(repo_name=repo_name, asset_name=asset_name, version=version, extracted_path=extracted_path, console=console)
171
+
172
+
173
+ def install_from_binary_url(binary_url: str) -> None:
174
+ from rich.console import Console
175
+ # from rich.panel import Panel
176
+ console = Console()
177
+ # parsed = urlparse(binary_url)
178
+ # asset_candidate = parsed.path.split("/")[-1] if parsed.path else ""
179
+ # asset_name = asset_candidate if asset_candidate != "" else "binary_asset"
180
+ # host = parsed.netloc if parsed.netloc != "" else "remote host"
181
+ # console.print(Panel(f"Downloading from [green]{binary_url}[/green]", title="⬇️ Download", border_style="magenta"))
182
+ extracted_path = download_and_prepare(binary_url)
183
+ _finalize_install(repo_name="", asset_name=None, version="latest", extracted_path=extracted_path, console=console)