machineconfig 1.97__py3-none-any.whl → 2.1__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 (268) hide show
  1. machineconfig/cluster/cloud_manager.py +22 -29
  2. machineconfig/cluster/data_transfer.py +2 -3
  3. machineconfig/cluster/distribute.py +0 -2
  4. machineconfig/cluster/file_manager.py +4 -5
  5. machineconfig/cluster/job_params.py +1 -4
  6. machineconfig/cluster/loader_runner.py +8 -11
  7. machineconfig/cluster/remote_machine.py +4 -5
  8. machineconfig/cluster/script_execution.py +2 -2
  9. machineconfig/cluster/script_notify_upon_completion.py +0 -1
  10. machineconfig/cluster/sessions_managers/archive/create_zellij_template.py +4 -6
  11. machineconfig/cluster/sessions_managers/archive/session_managers.py +0 -1
  12. machineconfig/cluster/sessions_managers/enhanced_command_runner.py +35 -75
  13. machineconfig/cluster/sessions_managers/wt_local.py +113 -185
  14. machineconfig/cluster/sessions_managers/wt_local_manager.py +127 -197
  15. machineconfig/cluster/sessions_managers/wt_remote.py +60 -67
  16. machineconfig/cluster/sessions_managers/wt_remote_manager.py +110 -149
  17. machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +61 -64
  18. machineconfig/cluster/sessions_managers/wt_utils/process_monitor.py +72 -172
  19. machineconfig/cluster/sessions_managers/wt_utils/remote_executor.py +27 -60
  20. machineconfig/cluster/sessions_managers/wt_utils/session_manager.py +58 -137
  21. machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +46 -74
  22. machineconfig/cluster/sessions_managers/zellij_local.py +91 -147
  23. machineconfig/cluster/sessions_managers/zellij_local_manager.py +165 -190
  24. machineconfig/cluster/sessions_managers/zellij_remote.py +51 -58
  25. machineconfig/cluster/sessions_managers/zellij_remote_manager.py +40 -46
  26. machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +19 -17
  27. machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +30 -31
  28. machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +64 -134
  29. machineconfig/cluster/sessions_managers/zellij_utils/remote_executor.py +7 -11
  30. machineconfig/cluster/sessions_managers/zellij_utils/session_manager.py +27 -55
  31. machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +14 -13
  32. machineconfig/cluster/templates/cli_click.py +0 -1
  33. machineconfig/cluster/templates/cli_gooey.py +0 -2
  34. machineconfig/cluster/templates/cli_trogon.py +0 -1
  35. machineconfig/cluster/templates/run_cloud.py +0 -1
  36. machineconfig/cluster/templates/run_cluster.py +0 -1
  37. machineconfig/cluster/templates/run_remote.py +0 -1
  38. machineconfig/cluster/templates/utils.py +27 -11
  39. machineconfig/jobs/__pycache__/__init__.cpython-313.pyc +0 -0
  40. machineconfig/jobs/linux/msc/cli_agents.sh +16 -0
  41. machineconfig/jobs/python/check_installations.py +9 -9
  42. machineconfig/jobs/python/create_bootable_media.py +0 -2
  43. machineconfig/jobs/python/python_cargo_build_share.py +2 -2
  44. machineconfig/jobs/python/python_ve_symlink.py +9 -11
  45. machineconfig/jobs/python/tasks.py +0 -1
  46. machineconfig/jobs/python/vscode/api.py +5 -5
  47. machineconfig/jobs/python/vscode/link_ve.py +20 -21
  48. machineconfig/jobs/python/vscode/select_interpreter.py +28 -29
  49. machineconfig/jobs/python/vscode/sync_code.py +14 -18
  50. machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  51. machineconfig/jobs/python_custom_installers/archive/ngrok.py +15 -15
  52. machineconfig/jobs/python_custom_installers/dev/aider.py +10 -18
  53. machineconfig/jobs/python_custom_installers/dev/alacritty.py +12 -21
  54. machineconfig/jobs/python_custom_installers/dev/brave.py +13 -22
  55. machineconfig/jobs/python_custom_installers/dev/bypass_paywall.py +13 -20
  56. machineconfig/jobs/python_custom_installers/dev/code.py +17 -24
  57. machineconfig/jobs/python_custom_installers/dev/cursor.py +10 -21
  58. machineconfig/jobs/python_custom_installers/dev/docker_desktop.py +12 -11
  59. machineconfig/jobs/python_custom_installers/dev/espanso.py +19 -23
  60. machineconfig/jobs/python_custom_installers/dev/goes.py +9 -16
  61. machineconfig/jobs/python_custom_installers/dev/lvim.py +13 -21
  62. machineconfig/jobs/python_custom_installers/dev/nerdfont.py +15 -22
  63. machineconfig/jobs/python_custom_installers/dev/redis.py +15 -23
  64. machineconfig/jobs/python_custom_installers/dev/wezterm.py +15 -22
  65. machineconfig/jobs/python_custom_installers/dev/winget.py +32 -50
  66. machineconfig/jobs/python_custom_installers/docker.py +15 -24
  67. machineconfig/jobs/python_custom_installers/gh.py +18 -26
  68. machineconfig/jobs/python_custom_installers/hx.py +33 -17
  69. machineconfig/jobs/python_custom_installers/warp-cli.py +15 -23
  70. machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  71. machineconfig/jobs/python_generic_installers/config.json +412 -389
  72. machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  73. machineconfig/jobs/python_windows_installers/dev/config.json +1 -1
  74. machineconfig/jobs/windows/archive/archive_pygraphviz.ps1 +1 -1
  75. machineconfig/jobs/windows/msc/cli_agents.bat +0 -0
  76. machineconfig/jobs/windows/msc/cli_agents.ps1 +0 -0
  77. machineconfig/jobs/windows/start_terminal.ps1 +1 -1
  78. machineconfig/logger.py +50 -0
  79. machineconfig/profile/create.py +50 -36
  80. machineconfig/profile/create_hardlinks.py +33 -26
  81. machineconfig/profile/shell.py +87 -60
  82. machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
  83. machineconfig/scripts/cloud/init.sh +2 -2
  84. machineconfig/scripts/linux/checkout_versions +1 -1
  85. machineconfig/scripts/linux/choose_wezterm_theme +1 -1
  86. machineconfig/scripts/linux/cloud_copy +1 -1
  87. machineconfig/scripts/linux/cloud_manager +1 -1
  88. machineconfig/scripts/linux/cloud_mount +1 -1
  89. machineconfig/scripts/linux/cloud_repo_sync +1 -1
  90. machineconfig/scripts/linux/cloud_sync +1 -1
  91. machineconfig/scripts/linux/croshell +1 -1
  92. machineconfig/scripts/linux/devops +3 -5
  93. machineconfig/scripts/linux/fire +2 -1
  94. machineconfig/scripts/linux/fire_agents +3 -3
  95. machineconfig/scripts/linux/ftpx +1 -1
  96. machineconfig/scripts/linux/gh_models +1 -1
  97. machineconfig/scripts/linux/kill_process +1 -1
  98. machineconfig/scripts/linux/mcinit +2 -2
  99. machineconfig/scripts/linux/repos +1 -1
  100. machineconfig/scripts/linux/scheduler +1 -1
  101. machineconfig/scripts/linux/start_slidev +1 -1
  102. machineconfig/scripts/linux/start_terminals +1 -1
  103. machineconfig/scripts/linux/url2md +1 -1
  104. machineconfig/scripts/linux/warp-cli.sh +122 -0
  105. machineconfig/scripts/linux/wifi_conn +1 -1
  106. machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc +0 -0
  107. machineconfig/scripts/python/__pycache__/croshell.cpython-313.pyc +0 -0
  108. machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc +0 -0
  109. machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-313.pyc +0 -0
  110. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc +0 -0
  111. machineconfig/scripts/python/__pycache__/fire_jobs.cpython-313.pyc +0 -0
  112. machineconfig/scripts/python/ai/__init__.py +0 -0
  113. machineconfig/scripts/python/ai/__pycache__/__init__.cpython-313.pyc +0 -0
  114. machineconfig/scripts/python/ai/__pycache__/generate_files.cpython-313.pyc +0 -0
  115. machineconfig/scripts/python/ai/__pycache__/mcinit.cpython-313.pyc +0 -0
  116. machineconfig/scripts/python/ai/chatmodes/Thinking-Beast-Mode.chatmode.md +337 -0
  117. machineconfig/scripts/python/ai/chatmodes/Ultimate-Transparent-Thinking-Beast-Mode.chatmode.md +644 -0
  118. machineconfig/scripts/python/ai/chatmodes/deepResearch.chatmode.md +81 -0
  119. machineconfig/scripts/python/ai/configs/.gemini/settings.json +81 -0
  120. machineconfig/scripts/python/ai/generate_files.py +84 -0
  121. machineconfig/scripts/python/ai/instructions/python/dev.instructions.md +45 -0
  122. machineconfig/scripts/python/ai/mcinit.py +107 -0
  123. machineconfig/scripts/python/ai/prompts/allLintersAndTypeCheckers.prompt.md +5 -0
  124. machineconfig/scripts/python/ai/prompts/research-report-skeleton.prompt.md +38 -0
  125. machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +52 -0
  126. machineconfig/scripts/python/archive/tmate_conn.py +5 -5
  127. machineconfig/scripts/python/archive/tmate_start.py +3 -3
  128. machineconfig/scripts/python/choose_wezterm_theme.py +2 -2
  129. machineconfig/scripts/python/cloud_copy.py +20 -19
  130. machineconfig/scripts/python/cloud_mount.py +10 -8
  131. machineconfig/scripts/python/cloud_repo_sync.py +15 -15
  132. machineconfig/scripts/python/cloud_sync.py +1 -1
  133. machineconfig/scripts/python/croshell.py +18 -16
  134. machineconfig/scripts/python/devops.py +6 -6
  135. machineconfig/scripts/python/devops_add_identity.py +9 -7
  136. machineconfig/scripts/python/devops_add_ssh_key.py +19 -19
  137. machineconfig/scripts/python/devops_backup_retrieve.py +14 -14
  138. machineconfig/scripts/python/devops_devapps_install.py +3 -3
  139. machineconfig/scripts/python/devops_update_repos.py +141 -53
  140. machineconfig/scripts/python/dotfile.py +3 -3
  141. machineconfig/scripts/python/fire_agents.py +202 -41
  142. machineconfig/scripts/python/fire_jobs.py +20 -21
  143. machineconfig/scripts/python/ftpx.py +4 -3
  144. machineconfig/scripts/python/gh_models.py +94 -94
  145. machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-313.pyc +0 -0
  146. machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-313.pyc +0 -0
  147. machineconfig/scripts/python/helpers/cloud_helpers.py +3 -3
  148. machineconfig/scripts/python/helpers/helpers2.py +3 -3
  149. machineconfig/scripts/python/helpers/helpers4.py +8 -7
  150. machineconfig/scripts/python/helpers/helpers5.py +7 -7
  151. machineconfig/scripts/python/helpers/repo_sync_helpers.py +2 -2
  152. machineconfig/scripts/python/mount_nfs.py +4 -3
  153. machineconfig/scripts/python/mount_nw_drive.py +4 -4
  154. machineconfig/scripts/python/mount_ssh.py +4 -3
  155. machineconfig/scripts/python/repos.py +9 -9
  156. machineconfig/scripts/python/scheduler.py +1 -1
  157. machineconfig/scripts/python/start_slidev.py +9 -8
  158. machineconfig/scripts/python/start_terminals.py +1 -1
  159. machineconfig/scripts/python/viewer.py +40 -40
  160. machineconfig/scripts/python/wifi_conn.py +65 -66
  161. machineconfig/scripts/python/wsl_windows_transfer.py +2 -2
  162. machineconfig/scripts/windows/checkout_version.ps1 +1 -3
  163. machineconfig/scripts/windows/choose_wezterm_theme.ps1 +1 -3
  164. machineconfig/scripts/windows/cloud_copy.ps1 +2 -6
  165. machineconfig/scripts/windows/cloud_manager.ps1 +1 -1
  166. machineconfig/scripts/windows/cloud_repo_sync.ps1 +1 -2
  167. machineconfig/scripts/windows/cloud_sync.ps1 +2 -2
  168. machineconfig/scripts/windows/croshell.ps1 +2 -2
  169. machineconfig/scripts/windows/devops.ps1 +1 -4
  170. machineconfig/scripts/windows/dotfile.ps1 +1 -3
  171. machineconfig/scripts/windows/fire.ps1 +1 -1
  172. machineconfig/scripts/windows/ftpx.ps1 +2 -2
  173. machineconfig/scripts/windows/gpt.ps1 +1 -1
  174. machineconfig/scripts/windows/kill_process.ps1 +1 -2
  175. machineconfig/scripts/windows/mcinit.ps1 +2 -2
  176. machineconfig/scripts/windows/mount_nfs.ps1 +1 -1
  177. machineconfig/scripts/windows/mount_ssh.ps1 +1 -1
  178. machineconfig/scripts/windows/pomodoro.ps1 +1 -1
  179. machineconfig/scripts/windows/py2exe.ps1 +1 -3
  180. machineconfig/scripts/windows/repos.ps1 +1 -1
  181. machineconfig/scripts/windows/scheduler.ps1 +1 -1
  182. machineconfig/scripts/windows/snapshot.ps1 +2 -2
  183. machineconfig/scripts/windows/start_slidev.ps1 +1 -1
  184. machineconfig/scripts/windows/start_terminals.ps1 +1 -1
  185. machineconfig/scripts/windows/wifi_conn.ps1 +1 -1
  186. machineconfig/scripts/windows/wsl_windows_transfer.ps1 +1 -3
  187. machineconfig/settings/lf/linux/lfrc +1 -1
  188. machineconfig/settings/linters/.ruff.toml +2 -2
  189. machineconfig/settings/linters/.ruff_cache/.gitignore +2 -0
  190. machineconfig/settings/linters/.ruff_cache/CACHEDIR.TAG +1 -0
  191. machineconfig/settings/lvim/windows/archive/config_additional.lua +1 -1
  192. machineconfig/settings/shells/ipy/profiles/default/startup/playext.py +71 -71
  193. machineconfig/settings/shells/wt/settings.json +8 -8
  194. machineconfig/settings/svim/linux/init.toml +1 -1
  195. machineconfig/settings/svim/windows/init.toml +1 -1
  196. machineconfig/setup_linux/web_shortcuts/croshell.sh +0 -54
  197. machineconfig/setup_linux/web_shortcuts/interactive.sh +6 -6
  198. machineconfig/setup_linux/web_shortcuts/tmp.sh +2 -0
  199. machineconfig/setup_windows/web_shortcuts/all.ps1 +2 -2
  200. machineconfig/setup_windows/web_shortcuts/ascii_art.ps1 +1 -1
  201. machineconfig/setup_windows/web_shortcuts/croshell.ps1 +1 -1
  202. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +5 -5
  203. machineconfig/setup_windows/wt_and_pwsh/install_fonts.ps1 +51 -15
  204. machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +75 -18
  205. machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +52 -42
  206. machineconfig/utils/ai/browser_user_wrapper.py +5 -5
  207. machineconfig/utils/ai/generate_file_checklist.py +19 -22
  208. machineconfig/utils/ai/url2md.py +5 -3
  209. machineconfig/utils/cloud/onedrive/setup_oauth.py +5 -4
  210. machineconfig/utils/cloud/onedrive/transaction.py +192 -227
  211. machineconfig/utils/code.py +71 -43
  212. machineconfig/utils/installer.py +77 -85
  213. machineconfig/utils/installer_utils/installer_abc.py +29 -17
  214. machineconfig/utils/installer_utils/installer_class.py +188 -83
  215. machineconfig/utils/io_save.py +3 -15
  216. machineconfig/utils/links.py +22 -11
  217. machineconfig/utils/notifications.py +197 -0
  218. machineconfig/utils/options.py +38 -25
  219. machineconfig/utils/path.py +18 -6
  220. machineconfig/utils/path_reduced.py +637 -316
  221. machineconfig/utils/procs.py +69 -63
  222. machineconfig/utils/scheduling.py +11 -13
  223. machineconfig/utils/ssh.py +351 -0
  224. machineconfig/utils/terminal.py +225 -0
  225. machineconfig/utils/utils.py +13 -12
  226. machineconfig/utils/utils2.py +43 -10
  227. machineconfig/utils/utils5.py +242 -46
  228. machineconfig/utils/ve.py +11 -6
  229. {machineconfig-1.97.dist-info → machineconfig-2.1.dist-info}/METADATA +15 -9
  230. {machineconfig-1.97.dist-info → machineconfig-2.1.dist-info}/RECORD +232 -235
  231. machineconfig/cluster/self_ssh.py +0 -57
  232. machineconfig/jobs/__pycache__/__init__.cpython-311.pyc +0 -0
  233. machineconfig/jobs/python/__pycache__/__init__.cpython-311.pyc +0 -0
  234. machineconfig/jobs/python/archive/python_tools.txt +0 -12
  235. machineconfig/jobs/python/vscode/__pycache__/select_interpreter.cpython-311.pyc +0 -0
  236. machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  237. machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  238. machineconfig/jobs/python_generic_installers/update.py +0 -3
  239. machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  240. machineconfig/profile/__pycache__/__init__.cpython-311.pyc +0 -0
  241. machineconfig/profile/__pycache__/create.cpython-311.pyc +0 -0
  242. machineconfig/profile/__pycache__/shell.cpython-311.pyc +0 -0
  243. machineconfig/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  244. machineconfig/scripts/linux/activate_ve +0 -87
  245. machineconfig/scripts/python/__pycache__/__init__.cpython-311.pyc +0 -0
  246. machineconfig/scripts/python/__pycache__/cloud_copy.cpython-311.pyc +0 -0
  247. machineconfig/scripts/python/__pycache__/cloud_mount.cpython-311.pyc +0 -0
  248. machineconfig/scripts/python/__pycache__/cloud_sync.cpython-311.pyc +0 -0
  249. machineconfig/scripts/python/__pycache__/croshell.cpython-311.pyc +0 -0
  250. machineconfig/scripts/python/__pycache__/devops.cpython-311.pyc +0 -0
  251. machineconfig/scripts/python/__pycache__/devops_backup_retrieve.cpython-311.pyc +0 -0
  252. machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-311.pyc +0 -0
  253. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-311.pyc +0 -0
  254. machineconfig/scripts/python/__pycache__/fire_agents.cpython-311.pyc +0 -0
  255. machineconfig/scripts/python/__pycache__/fire_jobs.cpython-311.pyc +0 -0
  256. machineconfig/scripts/python/__pycache__/get_zellij_cmd.cpython-311.pyc +0 -0
  257. machineconfig/scripts/python/__pycache__/repos.cpython-311.pyc +0 -0
  258. machineconfig/scripts/python/ai/__pycache__/init.cpython-311.pyc +0 -0
  259. machineconfig/scripts/python/ai/init.py +0 -56
  260. machineconfig/scripts/python/ai/rules/python/dev.md +0 -31
  261. machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-311.pyc +0 -0
  262. machineconfig/scripts/python/helpers/__pycache__/cloud_helpers.cpython-311.pyc +0 -0
  263. machineconfig/scripts/python/helpers/__pycache__/helpers2.cpython-311.pyc +0 -0
  264. machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-311.pyc +0 -0
  265. machineconfig/scripts/python/helpers/__pycache__/repo_sync_helpers.cpython-311.pyc +0 -0
  266. machineconfig/scripts/windows/activate_ve.ps1 +0 -54
  267. {machineconfig-1.97.dist-info → machineconfig-2.1.dist-info}/WHEEL +0 -0
  268. {machineconfig-1.97.dist-info → machineconfig-2.1.dist-info}/top_level.txt +0 -0
@@ -2,8 +2,8 @@
2
2
  CC
3
3
  """
4
4
 
5
- from machineconfig.utils.path_reduced import P as PathExtended
6
- from crocodile.meta import RepeatUntilNoException
5
+ from machineconfig.utils.path_reduced import PathExtended as PathExtended
6
+ from tenacity import retry, stop_after_attempt, wait_chain, wait_fixed
7
7
  import getpass
8
8
  import argparse
9
9
  import os
@@ -18,19 +18,19 @@ from machineconfig.utils.utils2 import pprint
18
18
 
19
19
  console = Console()
20
20
 
21
- @RepeatUntilNoException(retry=3, sleep=1)
21
+ @retry(stop=stop_after_attempt(3), wait=wait_chain(wait_fixed(1), wait_fixed(4), wait_fixed(9)))
22
22
  def get_securely_shared_file(url: Optional[str] = None, folder: Optional[str] = None) -> None:
23
23
  console.print(Panel("🚀 Secure File Downloader", title="[bold blue]Downloader[/bold blue]", border_style="blue"))
24
-
24
+
25
25
  folder_obj = PathExtended.cwd() if folder is None else PathExtended(folder)
26
26
  print(f"📂 Target folder: {folder_obj}")
27
-
27
+
28
28
  if os.environ.get("DECRYPTION_PASSWORD") is not None:
29
29
  print("🔑 Using password from environment variables")
30
30
  pwd = str(os.environ.get("DECRYPTION_PASSWORD"))
31
31
  else:
32
32
  pwd = getpass.getpass(prompt="🔑 Enter decryption password: ")
33
-
33
+
34
34
  if url is None:
35
35
  if os.environ.get("SHARE_URL") is not None:
36
36
  url = os.environ.get("SHARE_URL")
@@ -38,21 +38,22 @@ def get_securely_shared_file(url: Optional[str] = None, folder: Optional[str] =
38
38
  print("🔗 Using URL from environment variables")
39
39
  else:
40
40
  url = input("🔗 Enter share URL: ")
41
-
41
+
42
42
  console.print(Panel("📡 Downloading from URL...", title="[bold blue]Download[/bold blue]", border_style="blue"))
43
43
  with Progress(transient=True) as progress:
44
44
  _task = progress.add_task("Downloading... ", total=None)
45
45
  url_obj = PathExtended(url).download(folder=folder_obj)
46
-
46
+
47
47
  console.print(Panel(f"📥 Downloaded file: {url_obj}", title="[bold green]Success[/bold green]", border_style="green"))
48
-
48
+
49
49
  console.print(Panel("🔐 Decrypting and extracting...", title="[bold blue]Processing[/bold blue]", border_style="blue"))
50
50
  with Progress(transient=True) as progress:
51
51
  _task = progress.add_task("Decrypting... ", total=None)
52
52
  tmp_folder = PathExtended.tmpdir(prefix="tmp_unzip")
53
53
  try:
54
54
  res = url_obj.decrypt(pwd=pwd, inplace=True).unzip(inplace=True, folder=tmp_folder)
55
- res.search("*").apply(lambda x: x.move(folder=folder_obj, overwrite=True))
55
+ for x in res.search("*"):
56
+ x.move(folder=folder_obj, overwrite=True)
56
57
  finally:
57
58
  # Clean up temporary folder
58
59
  if tmp_folder.exists():
@@ -61,7 +62,7 @@ def get_securely_shared_file(url: Optional[str] = None, folder: Optional[str] =
61
62
 
62
63
  def arg_parser() -> None:
63
64
  console.print(Panel("☁️ Cloud Copy Utility", title="[bold blue]Cloud Copy[/bold blue]", border_style="blue", width=152))
64
-
65
+
65
66
  parser = argparse.ArgumentParser(description='🚀 Cloud CLI. It wraps rclone with sane defaults for optimum type time.')
66
67
 
67
68
  # positional argument
@@ -105,40 +106,40 @@ def arg_parser() -> None:
105
106
 
106
107
  console.print(Panel("🔍 Parsing source and target paths...", title="[bold blue]Info[/bold blue]", border_style="blue"))
107
108
  cloud, source, target = parse_cloud_source_target(args=args_obj, source=source, target=target)
108
-
109
+
109
110
  console.print(Panel("⚙️ Configuration:", title="[bold blue]Config[/bold blue]", border_style="blue"))
110
111
  pprint(args_obj.__dict__, "CLI config")
111
112
 
112
113
  if args_obj.key is not None:
113
114
  console.print(Panel("❌ Key-based encryption is not supported yet", title="[bold red]Error[/bold red]", border_style="red"))
114
115
  raise ValueError("Key-based encryption is not supported yet.")
115
-
116
+
116
117
  if cloud in source:
117
118
  console.print(Panel(f"📥 DOWNLOADING FROM CLOUD\n☁️ Cloud: {cloud}\n📂 Source: {source.replace(cloud + ':', '')}\n🎯 Target: {target}", title="[bold blue]Download[/bold blue]", border_style="blue", width=152))
118
-
119
+
119
120
  PathExtended(target).from_cloud(cloud=cloud, remotepath=source.replace(cloud + ":", ""),
120
121
  unzip=args_obj.zip, decrypt=args_obj.encrypt, pwd=args_obj.pwd,
121
122
  overwrite=args_obj.overwrite,
122
123
  rel2home=args_obj.rel2home, os_specific=args_obj.os_specific, root=args_obj.root, strict=False,
123
124
  )
124
125
  console.print(Panel("✅ Download completed successfully", title="[bold green]Success[/bold green]", border_style="green", width=152))
125
-
126
+
126
127
  elif cloud in target:
127
128
  console.print(Panel(f"📤 UPLOADING TO CLOUD\n☁️ Cloud: {cloud}\n📂 Source: {source}\n🎯 Target: {target.replace(cloud + ':', '')}", title="[bold blue]Upload[/bold blue]", border_style="blue", width=152))
128
-
129
+
129
130
  res = PathExtended(source).to_cloud(cloud=cloud, remotepath=target.replace(cloud + ":", ""),
130
131
  zip=args_obj.zip, encrypt=args_obj.encrypt, pwd=args_obj.pwd,
131
132
  rel2home=args_obj.rel2home, root=args_obj.root, os_specific=args_obj.os_specific, strict=False,
132
133
  share=args_obj.share)
133
134
  console.print(Panel("✅ Upload completed successfully", title="[bold green]Success[/bold green]", border_style="green", width=152))
134
-
135
+
135
136
  if args_obj.share:
136
137
  fname = f".share_url_{cloud}"
137
138
  if PathExtended(source).is_dir(): share_url_path = PathExtended(source).joinpath(fname)
138
139
  else: share_url_path = PathExtended(source).with_suffix(fname)
139
- share_url_path.write_text(res.as_url_str())
140
+ share_url_path.write_text(res.as_url_str(), encoding="utf-8")
140
141
  console.print(Panel(f"🔗 SHARE URL GENERATED\n📝 URL file: {share_url_path}\n🌍 {res.as_url_str()}", title="[bold blue]Share[/bold blue]", border_style="blue", width=152))
141
- else:
142
+ else:
142
143
  console.print(Panel(f"❌ ERROR: Cloud '{cloud}' not found in source or target", title="[bold red]Error[/bold red]", border_style="red", width=152))
143
144
  raise ValueError(f"Cloud `{cloud}` not found in source or target.")
144
145
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  from machineconfig.utils.utils import PROGRAM_PATH, choose_one_option
6
6
  from machineconfig.utils.utils2 import read_ini
7
- from machineconfig.utils.path_reduced import P as PathExtended
7
+ from machineconfig.utils.path_reduced import PathExtended as PathExtended
8
8
 
9
9
  import platform
10
10
  import argparse
@@ -28,13 +28,15 @@ def get_rclone_config():
28
28
  def get_mprocs_mount_txt(cloud: str, rclone_cmd: str, cloud_brand: str): # cloud_brand = config[cloud]["type"]
29
29
  header = f"{' ' + cloud + ' | ' + cloud_brand + ' '}".center(50, "=")
30
30
  if platform.system() == "Windows":
31
- sub_text_path = PathExtended.tmpfile(suffix=".ps1").write_text(f"""
31
+ sub_text_path = PathExtended.tmpfile(suffix=".ps1")
32
+ sub_text_path.parent.mkdir(parents=True, exist_ok=True)
33
+ sub_text_path.write_text(f"""
32
34
  echo "{header}"
33
35
  iex 'rclone about {cloud}:'
34
36
  echo 'See {DEFAULT_MOUNT}/{cloud} for the mounted cloud'
35
37
 
36
38
  echo ''
37
- """)
39
+ """, encoding="utf-8")
38
40
  txt = f"""
39
41
  cd ~
40
42
  mprocs "powershell {sub_text_path}" "{rclone_cmd}" "btm" "timeout 2 & cd {DEFAULT_MOUNT} & lf" "timeout 2 & cd {DEFAULT_MOUNT} & pwsh" "pwsh" --names "info,service,monitor,explorer,main,terminal"
@@ -49,7 +51,7 @@ def mount(cloud: Optional[str], network: Optional[str], destination: Optional[st
49
51
  # draw header box dynamically
50
52
  title = "☁️ Cloud Mount Utility"
51
53
  console.print(Panel(title, title_align="left", border_style="blue"))
52
-
54
+
53
55
  config = get_rclone_config()
54
56
  if cloud is None:
55
57
  res = choose_one_option(msg="which cloud", options=config.sections(), header="CLOUD MOUNT", default=None)
@@ -62,7 +64,7 @@ def mount(cloud: Optional[str], network: Optional[str], destination: Optional[st
62
64
  mount_loc = PathExtended(DEFAULT_MOUNT).expanduser().joinpath(cloud)
63
65
  else:
64
66
  mount_loc = PathExtended(destination)
65
-
67
+
66
68
  mount_info = f"📂 Mount location: {mount_loc}"
67
69
  console.print(Panel(mount_info, border_style="blue"))
68
70
 
@@ -81,7 +83,7 @@ def mount(cloud: Optional[str], network: Optional[str], destination: Optional[st
81
83
  pass
82
84
  else: raise ValueError("unsupported platform")
83
85
 
84
- elif network and platform.system() == "Windows":
86
+ elif network and platform.system() == "Windows":
85
87
  mount_loc = "X: --network-mode"
86
88
  print(f"🔌 Setting up network mount at {mount_loc}")
87
89
  else: raise ValueError("network mount only supported on windows")
@@ -130,7 +132,7 @@ zellij action move-focus up
130
132
  """
131
133
  else: raise ValueError("unsupported platform")
132
134
  # print(f"running command: \n{txt}")
133
- PROGRAM_PATH.write_text(txt)
135
+ PROGRAM_PATH.write_text(txt, encoding="utf-8")
134
136
  # draw success box dynamically
135
137
  title1 = "✅ Cloud mount command prepared successfully"
136
138
  title2 = "🔄 Running mount process..."
@@ -141,7 +143,7 @@ def main():
141
143
  # draw main title box dynamically
142
144
  main_title = "☁️ RCLONE CLOUD MOUNT"
143
145
  console.print(Panel(main_title, title_align="left", border_style="blue"))
144
-
146
+
145
147
  parser = argparse.ArgumentParser(description='mount cloud')
146
148
  parser.add_argument('cloud', nargs='?', type=str, default=None, help='cloud to mount')
147
149
  parser.add_argument('destination', nargs='?', type=str, default=None, help='destination to mount')
@@ -1,8 +1,9 @@
1
1
  """utils"""
2
2
 
3
+ from pathlib import Path
3
4
  import git
4
- from machineconfig.utils.path_reduced import P as PathExtended
5
- from crocodile.meta import Terminal
5
+ from machineconfig.utils.path_reduced import PathExtended as PathExtended
6
+ from machineconfig.utils.terminal import Terminal
6
7
  from machineconfig.utils.utils2 import randstr, read_ini
7
8
 
8
9
  from machineconfig.scripts.python.helpers.repo_sync_helpers import fetch_dotfiles
@@ -13,7 +14,6 @@ import argparse
13
14
  from typing import Optional, Literal
14
15
  from rich.console import Console
15
16
  from rich.panel import Panel
16
- # from rich.text import Text
17
17
 
18
18
  console = Console()
19
19
 
@@ -31,14 +31,14 @@ def main(cloud: Optional[str] = None, path: Optional[str] = None, message: Optio
31
31
  console.print(Panel(f"❌ ERROR: No cloud profile found\nLocation: {DEFAULTS_PATH}\nPlease set one up or provide one via the --cloud flag.", title="Error", border_style="red"))
32
32
  return ""
33
33
  else: cloud_resolved = cloud
34
-
34
+
35
35
  # repo_root = PathExtended(args.repo).expanduser().absolute()
36
36
  repo_local_root = PathExtended.cwd() if path is None else PathExtended(path).expanduser().absolute()
37
37
  repo_local_obj = git.Repo(repo_local_root, search_parent_directories=True)
38
38
  repo_local_root = PathExtended(repo_local_obj.working_dir) # cwd might have been in a sub directory of repo_root, so its better to redefine it.
39
39
  CONFIG_PATH.joinpath("remote").mkdir(parents=True, exist_ok=True)
40
40
  repo_remote_root = CONFIG_PATH.joinpath("remote", repo_local_root.rel2home()) # .delete(sure=True)
41
-
41
+
42
42
  try:
43
43
  console.print(Panel("📥 DOWNLOADING REMOTE REPOSITORY", title_align="left", border_style="blue"))
44
44
  remote_path = repo_local_root.get_remote_path(rel2home=True, os_specific=False, root="myhome") + ".zip.enc"
@@ -47,7 +47,7 @@ def main(cloud: Optional[str] = None, path: Optional[str] = None, message: Optio
47
47
  console.print(Panel("🆕 Remote repository doesn't exist\n📤 Creating new remote and exiting...", title_align="left", border_style="green"))
48
48
  repo_local_root.to_cloud(cloud=cloud_resolved, zip=True, encrypt=True, rel2home=True, pwd=pwd, os_specific=False)
49
49
  return ""
50
-
50
+
51
51
  repo_remote_obj = git.Repo(repo_remote_root)
52
52
  if repo_remote_obj.is_dirty():
53
53
  console.print(Panel(f"⚠️ WARNING: REMOTE REPOSITORY IS DIRTY\nLocation: {repo_remote_root}\nPlease commit or stash changes before proceeding.", title="Warning", border_style="yellow"))
@@ -92,7 +92,7 @@ git pull originEnc master
92
92
  from machineconfig.scripts.python.helpers.repo_sync_helpers import delete_remote_repo_copy_and_push_local as func
93
93
  func(remote_repo=r'{str(repo_remote_root)}', local_repo=r'{str(repo_local_root)}', cloud=r'{cloud_resolved}')
94
94
  """
95
- shell_file_1 = get_shell_file_executing_python_script(python_script=program_1_py, ve_name="ve")
95
+ shell_file_1 = get_shell_file_executing_python_script(python_script=program_1_py, ve_path=str(Path.home().joinpath("code", "crocodile", ".venv")))
96
96
  # ================================================================================
97
97
 
98
98
  option2 = 'Delete local repo and replace it with remote copy:'
@@ -114,7 +114,7 @@ sudo chmod +x $HOME/dotfiles/scripts/linux -R
114
114
  from machineconfig.scripts.python.helper.repo_sync_helpers import inspect_repos as func
115
115
  func(repo_local_root=r'{str(repo_local_root)}', repo_remote_root=r'{str(repo_remote_root)}')
116
116
  """
117
- shell_file_3 = get_shell_file_executing_python_script(python_script=program_3_py, ve_name="ve")
117
+ shell_file_3 = get_shell_file_executing_python_script(python_script=program_3_py, ve_path=str(Path.home().joinpath("code", "crocodile", ".venv")))
118
118
  # ================================================================================
119
119
 
120
120
  option4 = 'Remove problematic rclone file from repo and replace with remote:'
@@ -129,7 +129,7 @@ git commit -am "finished merging"
129
129
  # ================================================================================
130
130
 
131
131
  console.print(Panel("🔄 RESOLVE MERGE CONFLICT\nChoose an option to resolve the conflict:", title_align="left", border_style="blue"))
132
-
132
+
133
133
  print(f"• 1️⃣ {option1:75} 👉 {shell_file_1}")
134
134
  print(f"• 2️⃣ {option2:75} 👉 {shell_file_2}")
135
135
  print(f"• 3️⃣ {option3:75} 👉 {shell_file_3}")
@@ -139,18 +139,18 @@ git commit -am "finished merging"
139
139
  match action:
140
140
  case "ask":
141
141
  choice = choose_one_option(options=[option1, option2, option3, option4], fzf=False)
142
- if choice == option1: program_content = shell_file_1.read_text()
142
+ if choice == option1: program_content = shell_file_1.read_text(encoding="utf-8")
143
143
  elif choice == option2: program_content = program_2
144
- elif choice == option3: program_content = shell_file_3.read_text()
144
+ elif choice == option3: program_content = shell_file_3.read_text(encoding="utf-8")
145
145
  elif choice == option4: program_content = program_4
146
146
  else: raise NotImplementedError(f"Choice {choice} not implemented.")
147
- case "pushLocalMerge": program_content = shell_file_1.read_text()
147
+ case "pushLocalMerge": program_content = shell_file_1.read_text(encoding="utf-8")
148
148
  case "overwriteLocal": program_content = program_2
149
- case "InspectRepos": program_content = shell_file_3.read_text()
149
+ case "InspectRepos": program_content = shell_file_3.read_text(encoding="utf-8")
150
150
  case "RemoveLocalRclone": program_content = program_4
151
- case _:
151
+ case _:
152
152
  raise ValueError(f"Unknown action: {action}")
153
- PROGRAM_PATH.write_text(program_content)
153
+ PROGRAM_PATH.write_text(program_content, encoding="utf-8")
154
154
  return program_content
155
155
 
156
156
  def args_parser():
@@ -75,7 +75,7 @@ def args_parser():
75
75
  cmd_line = f"{rclone_cmd[:65]}..."
76
76
  console.print(Panel(f"{title}\n{cmd_line}", title="[bold blue]Command[/bold blue]", expand=False))
77
77
 
78
- PROGRAM_PATH.write_text(txt)
78
+ PROGRAM_PATH.write_text(txt, encoding="utf-8")
79
79
 
80
80
 
81
81
  if __name__ == '__main__':
@@ -1,9 +1,11 @@
1
+ #!/usr/bin/env -S uv run --project
2
+
1
3
  """
2
4
  croshell
3
5
  """
4
6
 
5
7
  import argparse
6
- from machineconfig.utils.path_reduced import P as PathExtended
8
+ from machineconfig.utils.path_reduced import PathExtended as PathExtended
7
9
  from machineconfig.utils.utils2 import randstr
8
10
  from machineconfig.utils.utils import PROGRAM_PATH, display_options
9
11
  from machineconfig.utils.ve import get_ve_path_and_ipython_profile, get_ve_activate_line
@@ -18,7 +20,7 @@ console = Console()
18
20
 
19
21
  def add_print_header_pycode(path: str, title: str):
20
22
  return f"""
21
- # from machineconfig.utils.path_reduced import P as PathExtended
23
+ # from machineconfig.utils.path_reduced import P as PathExtended
22
24
  from crocodile.file_management import P as PathExtended
23
25
  pycode = PathExtended(r'{path}').read_text(encoding="utf-8")
24
26
  pycode = pycode.split("except Exception: print(pycode)")[2]
@@ -112,7 +114,7 @@ def build_parser():
112
114
  elif args.fzf:
113
115
  text = "🔍 Searching for Python files..."
114
116
  console.print(Panel(text, title="[bold blue]Info[/bold blue]"))
115
- options = PathExtended.cwd().search("*.py", r=True).apply(str).list
117
+ options = [str(item) for item in PathExtended.cwd().search("*.py", r=True)]
116
118
  file = display_options(msg="Choose a python file to run", options=options, fzf=True, multi=False, )
117
119
  assert isinstance(file, str)
118
120
  program = PathExtended(file).read_text(encoding='utf-8')
@@ -128,16 +130,16 @@ def build_parser():
128
130
 
129
131
  elif args.read != "":
130
132
  if args.streamlit_viewer:
131
- text = "📊 STARTING STREAMLIT VIEWER"
132
- console.print(Panel(text, title="[bold blue]Info[/bold blue]"))
133
- from machineconfig.scripts.python.viewer import run
134
- py_file_path = run(data_path=args.read, data=None, get_figure=None)
135
- final_program = f"""
136
- #!/bin/bash
137
- . $HOME/scripts/activate_ve 've'
138
- streamlit run {py_file_path}
139
- """
140
- PROGRAM_PATH.write_text(data=final_program)
133
+ # text = "📊 STARTING STREAMLIT VIEWER"
134
+ # console.print(Panel(text, title="[bold blue]Info[/bold blue]"))
135
+ # from machineconfig.scripts.python.viewer import run
136
+ # py_file_path = run(data_path=args.read, data=None, get_figure=None)
137
+ # final_program = f"""
138
+ # #!/bin/bash
139
+ # . $HOME/scripts/activate_ve '.venv'
140
+ # streamlit run {py_file_path}
141
+ # """
142
+ # PROGRAM_PATH.write_text(data=final_program, encoding="utf-8")
141
143
  return None
142
144
  file = PathExtended(str(args.read).lstrip()).expanduser().absolute()
143
145
  program = get_read_data_pycode(str(file))
@@ -173,7 +175,7 @@ print_logo(logo="crocodile")
173
175
  ve_root_from_file, ipython_profile = get_ve_path_and_ipython_profile(PathExtended(file))
174
176
  ipython_profile = ipython_profile if ipython_profile is not None else "default"
175
177
  # ve_activateion_line = get_ve_activate_line(ve_name=args.ve or ve_profile_suggested, a_path=str(PathExtended.cwd()))
176
- activate_ve_line = get_ve_activate_line(ve_root=args.ve or ve_root_from_file or "$HOME/venvs/ve")
178
+ activate_ve_line = get_ve_activate_line(ve_root=args.ve or ve_root_from_file or "$HOME/code/machineconfig/.venv")
177
179
  final_program = f"""
178
180
  #!/bin/bash
179
181
 
@@ -187,7 +189,7 @@ print_logo(logo="crocodile")
187
189
  if interpreter == "ipython":
188
190
  fire_line += f" {interactivity} --profile {ipython_profile} --no-banner"
189
191
  fire_line += f" {str(pyfile)}"
190
-
192
+
191
193
  final_program += fire_line
192
194
 
193
195
  title = "🚀 LAUNCHING SCRIPT"
@@ -196,7 +198,7 @@ print_logo(logo="crocodile")
196
198
  launch_message = f"{title} {PROGRAM_PATH}\n{text1}\n{text2}"
197
199
  console.print(Panel(Text(launch_message, justify="left"), expand=False, border_style="blue"))
198
200
 
199
- PROGRAM_PATH.write_text(data=final_program)
201
+ PROGRAM_PATH.write_text(data=final_program, encoding="utf-8")
200
202
  # (PROGRAM_PATH + ".py").write_text(str(pyfile), encoding='utf-8')
201
203
 
202
204
  # if platform.system() == "Windows":
@@ -33,7 +33,7 @@ class Options(Enum):
33
33
  def args_parser():
34
34
  # Print header
35
35
  console.print(Panel("🛠️ DevOps Tool Suite", title_align="left", border_style="blue", width=BOX_WIDTH))
36
-
36
+
37
37
  import argparse
38
38
  parser = argparse.ArgumentParser()
39
39
  new_line = "\n\n"
@@ -67,7 +67,7 @@ def display_task_success(success: str) -> None:
67
67
  def main(which: Optional[str] = None):
68
68
  PROGRAM_PATH.delete(sure=True, verbose=False)
69
69
  console.print(Panel("🚀 Initializing DevOps operation...", width=BOX_WIDTH, border_style="blue"))
70
-
70
+
71
71
  options = [op.value for op in Options]
72
72
  if which is None:
73
73
  try:
@@ -128,7 +128,7 @@ def main(which: Optional[str] = None):
128
128
  console.print(Panel("💾 Creating backup...", width=BOX_WIDTH, border_style="blue"))
129
129
  from machineconfig.scripts.python.devops_backup_retrieve import main_backup_retrieve as helper
130
130
  program = helper(direction="BACKUP")
131
-
131
+
132
132
  elif choice_key == Options.retreive.value:
133
133
  console.print(Panel("📥 Retrieving backup...", width=BOX_WIDTH, border_style="blue"))
134
134
  from machineconfig.scripts.python.devops_backup_retrieve import main_backup_retrieve as helper
@@ -145,14 +145,14 @@ def main(which: Optional[str] = None):
145
145
  from machineconfig.scripts.python.cloud_repo_sync import main as helper, PathExtended
146
146
  program = helper(cloud=None, path=str(PathExtended.home() / "dotfiles"), pwd=None, action="ask")
147
147
 
148
- else:
148
+ else:
149
149
  console.print(Panel("❌ ERROR: Invalid choice", title_align="left", border_style="red", width=BOX_WIDTH))
150
150
  raise ValueError(f"Unimplemented choice: {choice_key}")
151
-
151
+
152
152
  if program:
153
153
  console.print(Panel("📜 Preparing shell script...", width=BOX_WIDTH, border_style="blue"))
154
154
  write_shell_script_to_default_program_path(program=program, display=True, preserve_cwd=True, desc="🔧 Shell script prepared by Python.", execute=True if which is not None else False)
155
- else:
155
+ else:
156
156
  write_shell_script_to_default_program_path(program="echo '✨ Done.'", display=False, desc="🔧 Shell script prepared by Python.", preserve_cwd=True, execute=False)
157
157
 
158
158
 
@@ -3,7 +3,7 @@
3
3
 
4
4
 
5
5
  # from platform import system
6
- from machineconfig.utils.path_reduced import P as PathExtended, modify_text
6
+ from machineconfig.utils.path_reduced import PathExtended as PathExtended, modify_text
7
7
  from machineconfig.utils.options import display_options
8
8
  from rich.panel import Panel
9
9
  from rich.text import Text
@@ -17,14 +17,14 @@ def main():
17
17
 
18
18
  print(Panel("🔍 Searching for existing SSH keys...", expand=False))
19
19
 
20
- private_keys = PathExtended.home().joinpath(".ssh").search("*.pub").apply(lambda x: x.with_name(x.stem)).filter(lambda x: x.exists())
21
-
20
+ private_keys = [x.with_name(x.stem) for x in PathExtended.home().joinpath(".ssh").search("*.pub")]
21
+ private_keys = [x for x in private_keys if x.exists()]
22
22
  if private_keys:
23
23
  print(Panel(f"✅ Found {len(private_keys)} SSH private key(s)", expand=False))
24
24
  else:
25
25
  print(Panel("⚠️ No SSH private keys found", expand=False))
26
26
 
27
- choice = display_options(msg="Path to private key to be used when ssh'ing: ", options=private_keys.apply(str).list + ["I have the path to the key file", "I want to paste the key itself"])
27
+ choice = display_options(msg="Path to private key to be used when ssh'ing: ", options=[str(x) for x in private_keys] + ["I have the path to the key file", "I want to paste the key itself"])
28
28
 
29
29
  if choice == "I have the path to the key file":
30
30
  print(Panel("📄 Please enter the path to your private key file", expand=False))
@@ -34,7 +34,9 @@ def main():
34
34
  elif choice == "I want to paste the key itself":
35
35
  print(Panel("📋 Please provide a filename and paste the private key content", expand=False))
36
36
  key_filename = input("📝 File name (default: my_pasted_key): ") or "my_pasted_key"
37
- path_to_key = PathExtended.home().joinpath(f".ssh/{key_filename}").write_text(input("🔑 Paste the private key here: "))
37
+ path_to_key = PathExtended.home().joinpath(f".ssh/{key_filename}")
38
+ path_to_key.parent.mkdir(parents=True, exist_ok=True)
39
+ path_to_key.write_text(input("🔑 Paste the private key here: "), encoding="utf-8")
38
40
  print(Panel(f"💾 Key saved to: {path_to_key}", expand=False))
39
41
 
40
42
  elif isinstance(choice, str):
@@ -55,7 +57,7 @@ def main():
55
57
  # - If file doesn't exist, seed content with txt_search
56
58
  # - Then run modify_text to replace/append accordingly and write back
57
59
  if config_path.exists():
58
- current = config_path.read_text()
60
+ current = config_path.read_text(encoding="utf-8")
59
61
  print(Panel("✏️ Updated existing SSH config file", expand=False))
60
62
  else:
61
63
  current = txt
@@ -68,7 +70,7 @@ def main():
68
70
  notfound_append=True,
69
71
  prepend=True,
70
72
  )
71
- config_path.write_text(new_content)
73
+ config_path.write_text(new_content, encoding="utf-8")
72
74
 
73
75
  panel_complete = Panel(
74
76
  Text(
@@ -4,7 +4,7 @@
4
4
 
5
5
  from platform import system
6
6
  from machineconfig.utils.utils import LIBRARY_ROOT, display_options
7
- from machineconfig.utils.path_reduced import P as PathExtended
7
+ from machineconfig.utils.path_reduced import PathExtended as PathExtended
8
8
  from rich.console import Console
9
9
  from rich.panel import Panel
10
10
  from rich import box # Import box
@@ -28,11 +28,11 @@ def get_add_ssh_key_script(path_to_key: PathExtended):
28
28
 
29
29
  if authorized_keys.exists():
30
30
  split = "\n"
31
- keys_text = authorized_keys.read_text().split(split)
31
+ keys_text = authorized_keys.read_text(encoding="utf-8").split(split)
32
32
  key_count = len([k for k in keys_text if k.strip()])
33
33
  console.print(Panel(f"🔍 Current SSH authorization status\n✅ Found {key_count} authorized key(s)", title="[bold blue]Status[/bold blue]"))
34
34
 
35
- if path_to_key.read_text() in authorized_keys.read_text():
35
+ if path_to_key.read_text(encoding="utf-8") in authorized_keys.read_text(encoding="utf-8"):
36
36
  console.print(Panel(f"⚠️ Key already authorized\nKey: {path_to_key.name}\nStatus: Already present in authorized_keys file\nNo action required", title="[bold yellow]Warning[/bold yellow]"))
37
37
  program = ""
38
38
  else:
@@ -41,7 +41,7 @@ def get_add_ssh_key_script(path_to_key: PathExtended):
41
41
  program = f"cat {path_to_key} >> ~/.ssh/authorized_keys"
42
42
  elif system() == "Windows":
43
43
  program_path = LIBRARY_ROOT.joinpath("setup_windows/openssh-server_add-sshkey.ps1")
44
- program = program_path.expanduser().read_text()
44
+ program = program_path.expanduser().read_text(encoding="utf-8")
45
45
  place_holder = r'$sshfile = "$env:USERPROFILE\.ssh\pubkey.pub"'
46
46
  assert place_holder in program, f"This section performs string manipulation on the script {program_path} to add the key to the authorized_keys file. The script has changed and the string {place_holder} is not found."
47
47
  program = program.replace(place_holder, f'$sshfile = "{path_to_key}"')
@@ -53,7 +53,7 @@ def get_add_ssh_key_script(path_to_key: PathExtended):
53
53
  program = f"cat {path_to_key} > ~/.ssh/authorized_keys"
54
54
  else:
55
55
  program_path = LIBRARY_ROOT.joinpath("setup_windows/openssh-server_add-sshkey.ps1")
56
- program = PathExtended(program_path).expanduser().read_text().replace('$sshfile=""', f'$sshfile="{path_to_key}"')
56
+ program = PathExtended(program_path).expanduser().read_text(encoding="utf-8").replace('$sshfile=""', f'$sshfile="{path_to_key}"')
57
57
  console.print(Panel("🔧 Configured PowerShell script for Windows\n📝 Set key path in script", title="[bold blue]Configuration[/bold blue]"))
58
58
 
59
59
  if system() == "Linux":
@@ -70,47 +70,47 @@ sudo service ssh --full-restart
70
70
 
71
71
  def main():
72
72
  console.print(Panel("🔐 SSH PUBLIC KEY AUTHORIZATION TOOL", box=box.DOUBLE_EDGE, title_align="left"))
73
-
73
+
74
74
  console.print(Panel("🔍 Searching for public keys...", title="[bold blue]SSH Setup[/bold blue]", border_style="blue"))
75
-
75
+
76
76
  pub_keys = PathExtended.home().joinpath(".ssh").search("*.pub")
77
-
77
+
78
78
  if pub_keys:
79
79
  console.print(Panel(f"✅ Found {len(pub_keys)} public key(s)", title="[bold green]Status[/bold green]", border_style="green"))
80
80
  else:
81
81
  console.print(Panel("⚠️ No public keys found", title="[bold yellow]Warning[/bold yellow]", border_style="yellow"))
82
-
82
+
83
83
  all_keys_option = f"all pub keys available ({len(pub_keys)})"
84
84
  i_have_path_option = "I have the path to the key file"
85
85
  i_paste_option = "I want to paste the key itself"
86
-
87
- res = display_options("Which public key to add? ", options=pub_keys.apply(str).list + [all_keys_option, i_have_path_option, i_paste_option])
86
+
87
+ res = display_options("Which public key to add? ", options=[str(x) for x in pub_keys] + [all_keys_option, i_have_path_option, i_paste_option])
88
88
  assert isinstance(res, str), f"Got {res} of type {type(res)} instead of str."
89
-
89
+
90
90
  if res == all_keys_option:
91
91
  console.print(Panel(f"🔄 Processing all {len(pub_keys)} public keys...", title="[bold blue]Processing[/bold blue]", border_style="blue"))
92
- program = "\n\n\n".join(pub_keys.apply(get_add_ssh_key_script))
93
-
92
+ program = "\n\n\n".join([get_add_ssh_key_script(key) for key in pub_keys])
93
+
94
94
  elif res == i_have_path_option:
95
95
  console.print(Panel("📂 Please provide the path to your public key", title="[bold blue]Input Required[/bold blue]", border_style="blue"))
96
96
  key_path = PathExtended(input("📋 Path: ")).expanduser().absolute()
97
97
  console.print(Panel(f"📄 Using key from path: {key_path}", title="[bold blue]Info[/bold blue]", border_style="blue"))
98
98
  program = get_add_ssh_key_script(key_path)
99
-
99
+
100
100
  elif res == i_paste_option:
101
101
  console.print(Panel("📋 Please provide a filename and paste the public key content", title="[bold blue]Input Required[/bold blue]", border_style="blue"))
102
102
  key_filename = input("📝 File name (default: my_pasted_key.pub): ") or "my_pasted_key.pub"
103
103
  key_path = PathExtended.home().joinpath(f".ssh/{key_filename}")
104
- key_path.write_text(input("🔑 Paste the public key here: "))
104
+ key_path.write_text(input("🔑 Paste the public key here: "), encoding="utf-8")
105
105
  console.print(Panel(f"💾 Key saved to: {key_path}", title="[bold green]Success[/bold green]", border_style="green"))
106
106
  program = get_add_ssh_key_script(key_path)
107
-
107
+
108
108
  else:
109
109
  console.print(Panel(f"🔑 Using selected key: {PathExtended(res).name}", title="[bold blue]Info[/bold blue]", border_style="blue"))
110
110
  program = get_add_ssh_key_script(PathExtended(res))
111
-
111
+
112
112
  console.print(Panel("🚀 SSH KEY AUTHORIZATION READY\nRun the generated script to apply changes", box=box.DOUBLE_EDGE, title_align="left"))
113
-
113
+
114
114
  return program
115
115
 
116
116