machineconfig 1.92__py3-none-any.whl → 1.94__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 (694) hide show
  1. machineconfig/__init__.py +0 -8
  2. machineconfig/cluster/__init__.py +0 -0
  3. machineconfig/cluster/cloud_manager.py +359 -0
  4. machineconfig/cluster/data_transfer.py +56 -0
  5. machineconfig/cluster/distribute.py +280 -0
  6. machineconfig/cluster/file_manager.py +238 -0
  7. machineconfig/cluster/job_params.py +148 -0
  8. machineconfig/cluster/loader_runner.py +150 -0
  9. machineconfig/cluster/remote_machine.py +281 -0
  10. machineconfig/cluster/script_execution.py +209 -0
  11. machineconfig/cluster/script_notify_upon_completion.py +63 -0
  12. machineconfig/cluster/self_ssh.py +59 -0
  13. machineconfig/cluster/session_managers.py +183 -0
  14. machineconfig/cluster/templates/__init__.py +0 -0
  15. machineconfig/cluster/templates/cli_click.py +104 -0
  16. machineconfig/cluster/templates/cli_gooey.py +119 -0
  17. machineconfig/cluster/templates/cli_trogon.py +21 -0
  18. machineconfig/cluster/templates/f.py +4 -0
  19. machineconfig/cluster/templates/run_cloud.py +52 -0
  20. machineconfig/cluster/templates/run_cluster.py +69 -0
  21. machineconfig/cluster/templates/run_remote.py +67 -0
  22. machineconfig/cluster/templates/utils.py +37 -0
  23. machineconfig/jobs/__pycache__/__init__.cpython-311.pyc +0 -0
  24. machineconfig/jobs/linux/msc/lid.sh +26 -0
  25. machineconfig/jobs/linux/msc/network.sh +39 -0
  26. machineconfig/jobs/python/__pycache__/__init__.cpython-311.pyc +0 -0
  27. machineconfig/jobs/python/__pycache__/check_installations.cpython-311.pyc +0 -0
  28. machineconfig/jobs/python/__pycache__/checkout_version.cpython-311.pyc +0 -0
  29. machineconfig/jobs/python/__pycache__/python_ve_symlink.cpython-311.pyc +0 -0
  30. machineconfig/jobs/python/archive/python_tools.txt +12 -0
  31. machineconfig/jobs/python/check_installations.py +26 -11
  32. machineconfig/jobs/python/checkout_version.py +24 -13
  33. machineconfig/jobs/python/create_zellij_template.py +4 -4
  34. machineconfig/jobs/python/python_cargo_build_share.py +13 -4
  35. machineconfig/jobs/python/python_ve_symlink.py +15 -4
  36. machineconfig/jobs/python/vscode/__pycache__/api.cpython-311.pyc +0 -0
  37. machineconfig/jobs/python/vscode/__pycache__/link_ve.cpython-311.pyc +0 -0
  38. machineconfig/jobs/python/vscode/api.py +48 -0
  39. machineconfig/jobs/python/vscode/link_ve.py +64 -0
  40. machineconfig/jobs/python/vscode/select_interpreter.py +84 -0
  41. machineconfig/jobs/python/vscode/sync_code.py +60 -0
  42. machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  43. machineconfig/jobs/python_custom_installers/__pycache__/hx.cpython-311.pyc +0 -0
  44. machineconfig/jobs/python_custom_installers/archive/ngrok.py +62 -0
  45. machineconfig/jobs/python_custom_installers/dev/aider.py +37 -0
  46. machineconfig/jobs/python_custom_installers/dev/alacritty.py +72 -0
  47. machineconfig/jobs/python_custom_installers/dev/brave.py +67 -0
  48. machineconfig/jobs/python_custom_installers/dev/bypass_paywall.py +50 -0
  49. machineconfig/jobs/python_custom_installers/dev/code.py +62 -0
  50. machineconfig/jobs/python_custom_installers/dev/cursor.py +67 -0
  51. machineconfig/jobs/python_custom_installers/dev/docker.py +70 -0
  52. machineconfig/jobs/python_custom_installers/dev/docker_desktop.py +77 -0
  53. machineconfig/jobs/python_custom_installers/dev/espanso.py +82 -0
  54. machineconfig/jobs/python_custom_installers/dev/goes.py +59 -0
  55. machineconfig/jobs/python_custom_installers/dev/lvim.py +76 -0
  56. machineconfig/jobs/python_custom_installers/dev/nerdfont.py +67 -0
  57. machineconfig/jobs/python_custom_installers/dev/redis.py +69 -0
  58. machineconfig/jobs/python_custom_installers/dev/reverse_proxy.md +31 -0
  59. machineconfig/jobs/python_custom_installers/dev/warp-cli.py +71 -0
  60. machineconfig/jobs/python_custom_installers/dev/wezterm.py +71 -0
  61. machineconfig/jobs/python_custom_installers/gh.py +46 -4
  62. machineconfig/jobs/python_custom_installers/hx.py +103 -18
  63. machineconfig/jobs/python_custom_installers/scripts/linux/brave.sh +52 -0
  64. machineconfig/jobs/python_custom_installers/scripts/linux/docker.sh +140 -0
  65. machineconfig/jobs/python_custom_installers/scripts/linux/docker_start.sh +48 -0
  66. machineconfig/jobs/python_custom_installers/scripts/linux/edge.sh +47 -0
  67. machineconfig/jobs/python_custom_installers/scripts/linux/nerdfont.sh +64 -0
  68. machineconfig/jobs/python_custom_installers/scripts/linux/pgsql.sh +53 -0
  69. machineconfig/jobs/python_custom_installers/scripts/linux/redis.sh +76 -0
  70. machineconfig/jobs/python_custom_installers/scripts/linux/timescaledb.sh +91 -0
  71. machineconfig/jobs/python_custom_installers/scripts/linux/vscode.sh +66 -0
  72. machineconfig/jobs/python_custom_installers/scripts/linux/warp-cli.sh +77 -0
  73. machineconfig/jobs/python_custom_installers/scripts/linux/wezterm.sh +43 -0
  74. machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  75. machineconfig/jobs/python_generic_installers/config.json +267 -0
  76. machineconfig/jobs/python_generic_installers/dev/config.archive.json +18 -0
  77. machineconfig/jobs/python_generic_installers/dev/config.json +394 -0
  78. machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  79. machineconfig/jobs/python_linux_installers/archive/config.json +10 -0
  80. machineconfig/jobs/python_linux_installers/config.json +74 -0
  81. machineconfig/jobs/python_linux_installers/dev/config.json +138 -0
  82. machineconfig/jobs/python_windows_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  83. machineconfig/jobs/python_windows_installers/archive/file.json +11 -0
  84. machineconfig/jobs/python_windows_installers/config.json +50 -0
  85. machineconfig/jobs/python_windows_installers/dev/config.json +3 -0
  86. machineconfig/jobs/windows/archive/archive_pygraphviz.ps1 +14 -0
  87. machineconfig/jobs/windows/archive/openssh-server_add_key.ps1 +7 -0
  88. machineconfig/jobs/windows/archive/openssh-server_copy-ssh-id.ps1 +14 -0
  89. machineconfig/jobs/windows/start_terminal.ps1 +6 -0
  90. machineconfig/jobs/windows/startup_file.cmd +2 -0
  91. machineconfig/profile/__pycache__/__init__.cpython-311.pyc +0 -0
  92. machineconfig/profile/__pycache__/create.cpython-311.pyc +0 -0
  93. machineconfig/profile/__pycache__/shell.cpython-311.pyc +0 -0
  94. machineconfig/profile/create.py +47 -13
  95. machineconfig/profile/create_hardlinks.py +42 -12
  96. machineconfig/profile/records/generic/shares.toml +5 -0
  97. machineconfig/profile/records/linux/apps_summary_report.csv +48 -0
  98. machineconfig/profile/records/linux/apps_summary_report.md +49 -0
  99. machineconfig/profile/records/windows/apps_summary_report.csv +1 -0
  100. machineconfig/profile/records/windows/apps_summary_report.md +2 -0
  101. machineconfig/profile/shell.py +62 -11
  102. machineconfig/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  103. machineconfig/scripts/cloud/init.sh +128 -0
  104. machineconfig/scripts/linux/activate_ve +87 -0
  105. machineconfig/scripts/linux/archive/tmate_conn +12 -0
  106. machineconfig/scripts/linux/archive/tmate_start +12 -0
  107. machineconfig/scripts/linux/archive/transfer_wsl_win +5 -0
  108. machineconfig/scripts/linux/checkout_versions +8 -0
  109. machineconfig/scripts/linux/choose_wezterm_theme +9 -0
  110. machineconfig/scripts/linux/cloud_copy +9 -0
  111. machineconfig/scripts/linux/cloud_manager +8 -0
  112. machineconfig/scripts/linux/cloud_mount +24 -0
  113. machineconfig/scripts/linux/cloud_repo_sync +22 -0
  114. machineconfig/scripts/linux/cloud_sync +24 -0
  115. machineconfig/scripts/linux/croshell +24 -0
  116. machineconfig/scripts/linux/devops +24 -0
  117. machineconfig/scripts/linux/fire +46 -0
  118. machineconfig/scripts/linux/ftpx +8 -0
  119. machineconfig/scripts/linux/fzf2g +21 -0
  120. machineconfig/scripts/linux/fzfag +17 -0
  121. machineconfig/scripts/linux/fzffg +25 -0
  122. machineconfig/scripts/linux/fzfg +23 -0
  123. machineconfig/scripts/linux/fzfrga +21 -0
  124. machineconfig/scripts/linux/gh_models +11 -0
  125. machineconfig/scripts/linux/kill_process +10 -0
  126. machineconfig/scripts/linux/mount_drive +128 -0
  127. machineconfig/scripts/linux/mount_nfs +62 -0
  128. machineconfig/scripts/linux/mount_nw_drive +72 -0
  129. machineconfig/scripts/linux/mount_smb +3 -0
  130. machineconfig/scripts/linux/programs +21 -0
  131. machineconfig/scripts/linux/repos +24 -0
  132. machineconfig/scripts/linux/scheduler +8 -0
  133. machineconfig/scripts/linux/share_cloud.sh +81 -0
  134. machineconfig/scripts/linux/share_nfs +49 -0
  135. machineconfig/scripts/linux/share_smb +1 -0
  136. machineconfig/scripts/linux/skrg +4 -0
  137. machineconfig/scripts/linux/start_docker +23 -0
  138. machineconfig/scripts/linux/start_slidev +23 -0
  139. machineconfig/scripts/linux/start_terminals +12 -0
  140. machineconfig/scripts/linux/switch_ip +20 -0
  141. machineconfig/scripts/linux/url2md +10 -0
  142. machineconfig/scripts/linux/z_ls +104 -0
  143. machineconfig/scripts/python/.mypy_cache/.gitignore +2 -0
  144. machineconfig/scripts/python/.mypy_cache/3.11/@plugins_snapshot.json +1 -0
  145. machineconfig/scripts/python/.mypy_cache/3.11/__future__.data.json +1 -0
  146. machineconfig/scripts/python/.mypy_cache/3.11/__future__.meta.json +1 -0
  147. machineconfig/scripts/python/.mypy_cache/3.11/_ast.data.json +1 -0
  148. machineconfig/scripts/python/.mypy_cache/3.11/_ast.meta.json +1 -0
  149. machineconfig/scripts/python/.mypy_cache/3.11/_bz2.data.json +1 -0
  150. machineconfig/scripts/python/.mypy_cache/3.11/_bz2.meta.json +1 -0
  151. machineconfig/scripts/python/.mypy_cache/3.11/_codecs.data.json +1 -0
  152. machineconfig/scripts/python/.mypy_cache/3.11/_codecs.meta.json +1 -0
  153. machineconfig/scripts/python/.mypy_cache/3.11/_collections_abc.data.json +1 -0
  154. machineconfig/scripts/python/.mypy_cache/3.11/_collections_abc.meta.json +1 -0
  155. machineconfig/scripts/python/.mypy_cache/3.11/_compression.data.json +1 -0
  156. machineconfig/scripts/python/.mypy_cache/3.11/_compression.meta.json +1 -0
  157. machineconfig/scripts/python/.mypy_cache/3.11/_decimal.data.json +1 -0
  158. machineconfig/scripts/python/.mypy_cache/3.11/_decimal.meta.json +1 -0
  159. machineconfig/scripts/python/.mypy_cache/3.11/_frozen_importlib.data.json +1 -0
  160. machineconfig/scripts/python/.mypy_cache/3.11/_frozen_importlib.meta.json +1 -0
  161. machineconfig/scripts/python/.mypy_cache/3.11/_frozen_importlib_external.data.json +1 -0
  162. machineconfig/scripts/python/.mypy_cache/3.11/_frozen_importlib_external.meta.json +1 -0
  163. machineconfig/scripts/python/.mypy_cache/3.11/_io.data.json +1 -0
  164. machineconfig/scripts/python/.mypy_cache/3.11/_io.meta.json +1 -0
  165. machineconfig/scripts/python/.mypy_cache/3.11/_locale.data.json +1 -0
  166. machineconfig/scripts/python/.mypy_cache/3.11/_locale.meta.json +1 -0
  167. machineconfig/scripts/python/.mypy_cache/3.11/_stat.data.json +1 -0
  168. machineconfig/scripts/python/.mypy_cache/3.11/_stat.meta.json +1 -0
  169. machineconfig/scripts/python/.mypy_cache/3.11/_struct.data.json +1 -0
  170. machineconfig/scripts/python/.mypy_cache/3.11/_struct.meta.json +1 -0
  171. machineconfig/scripts/python/.mypy_cache/3.11/_thread.data.json +1 -0
  172. machineconfig/scripts/python/.mypy_cache/3.11/_thread.meta.json +1 -0
  173. machineconfig/scripts/python/.mypy_cache/3.11/_typeshed/__init__.data.json +1 -0
  174. machineconfig/scripts/python/.mypy_cache/3.11/_typeshed/__init__.meta.json +1 -0
  175. machineconfig/scripts/python/.mypy_cache/3.11/_typeshed/importlib.data.json +1 -0
  176. machineconfig/scripts/python/.mypy_cache/3.11/_typeshed/importlib.meta.json +1 -0
  177. machineconfig/scripts/python/.mypy_cache/3.11/_warnings.data.json +1 -0
  178. machineconfig/scripts/python/.mypy_cache/3.11/_warnings.meta.json +1 -0
  179. machineconfig/scripts/python/.mypy_cache/3.11/_weakref.data.json +1 -0
  180. machineconfig/scripts/python/.mypy_cache/3.11/_weakref.meta.json +1 -0
  181. machineconfig/scripts/python/.mypy_cache/3.11/_weakrefset.data.json +1 -0
  182. machineconfig/scripts/python/.mypy_cache/3.11/_weakrefset.meta.json +1 -0
  183. machineconfig/scripts/python/.mypy_cache/3.11/abc.data.json +1 -0
  184. machineconfig/scripts/python/.mypy_cache/3.11/abc.meta.json +1 -0
  185. machineconfig/scripts/python/.mypy_cache/3.11/argparse.data.json +1 -0
  186. machineconfig/scripts/python/.mypy_cache/3.11/argparse.meta.json +1 -0
  187. machineconfig/scripts/python/.mypy_cache/3.11/ast.data.json +1 -0
  188. machineconfig/scripts/python/.mypy_cache/3.11/ast.meta.json +1 -0
  189. machineconfig/scripts/python/.mypy_cache/3.11/binascii.data.json +1 -0
  190. machineconfig/scripts/python/.mypy_cache/3.11/binascii.meta.json +1 -0
  191. machineconfig/scripts/python/.mypy_cache/3.11/builtins.data.json +1 -0
  192. machineconfig/scripts/python/.mypy_cache/3.11/builtins.meta.json +1 -0
  193. machineconfig/scripts/python/.mypy_cache/3.11/bz2.data.json +1 -0
  194. machineconfig/scripts/python/.mypy_cache/3.11/bz2.meta.json +1 -0
  195. machineconfig/scripts/python/.mypy_cache/3.11/calendar.data.json +1 -0
  196. machineconfig/scripts/python/.mypy_cache/3.11/calendar.meta.json +1 -0
  197. machineconfig/scripts/python/.mypy_cache/3.11/codecs.data.json +1 -0
  198. machineconfig/scripts/python/.mypy_cache/3.11/codecs.meta.json +1 -0
  199. machineconfig/scripts/python/.mypy_cache/3.11/collections/__init__.data.json +1 -0
  200. machineconfig/scripts/python/.mypy_cache/3.11/collections/__init__.meta.json +1 -0
  201. machineconfig/scripts/python/.mypy_cache/3.11/collections/abc.data.json +1 -0
  202. machineconfig/scripts/python/.mypy_cache/3.11/collections/abc.meta.json +1 -0
  203. machineconfig/scripts/python/.mypy_cache/3.11/configparser.data.json +1 -0
  204. machineconfig/scripts/python/.mypy_cache/3.11/configparser.meta.json +1 -0
  205. machineconfig/scripts/python/.mypy_cache/3.11/contextlib.data.json +1 -0
  206. machineconfig/scripts/python/.mypy_cache/3.11/contextlib.meta.json +1 -0
  207. machineconfig/scripts/python/.mypy_cache/3.11/dataclasses.data.json +1 -0
  208. machineconfig/scripts/python/.mypy_cache/3.11/dataclasses.meta.json +1 -0
  209. machineconfig/scripts/python/.mypy_cache/3.11/datetime.data.json +1 -0
  210. machineconfig/scripts/python/.mypy_cache/3.11/datetime.meta.json +1 -0
  211. machineconfig/scripts/python/.mypy_cache/3.11/decimal.data.json +1 -0
  212. machineconfig/scripts/python/.mypy_cache/3.11/decimal.meta.json +1 -0
  213. machineconfig/scripts/python/.mypy_cache/3.11/dis.data.json +1 -0
  214. machineconfig/scripts/python/.mypy_cache/3.11/dis.meta.json +1 -0
  215. machineconfig/scripts/python/.mypy_cache/3.11/email/__init__.data.json +1 -0
  216. machineconfig/scripts/python/.mypy_cache/3.11/email/__init__.meta.json +1 -0
  217. machineconfig/scripts/python/.mypy_cache/3.11/email/_policybase.data.json +1 -0
  218. machineconfig/scripts/python/.mypy_cache/3.11/email/_policybase.meta.json +1 -0
  219. machineconfig/scripts/python/.mypy_cache/3.11/email/charset.data.json +1 -0
  220. machineconfig/scripts/python/.mypy_cache/3.11/email/charset.meta.json +1 -0
  221. machineconfig/scripts/python/.mypy_cache/3.11/email/contentmanager.data.json +1 -0
  222. machineconfig/scripts/python/.mypy_cache/3.11/email/contentmanager.meta.json +1 -0
  223. machineconfig/scripts/python/.mypy_cache/3.11/email/errors.data.json +1 -0
  224. machineconfig/scripts/python/.mypy_cache/3.11/email/errors.meta.json +1 -0
  225. machineconfig/scripts/python/.mypy_cache/3.11/email/header.data.json +1 -0
  226. machineconfig/scripts/python/.mypy_cache/3.11/email/header.meta.json +1 -0
  227. machineconfig/scripts/python/.mypy_cache/3.11/email/message.data.json +1 -0
  228. machineconfig/scripts/python/.mypy_cache/3.11/email/message.meta.json +1 -0
  229. machineconfig/scripts/python/.mypy_cache/3.11/email/policy.data.json +1 -0
  230. machineconfig/scripts/python/.mypy_cache/3.11/email/policy.meta.json +1 -0
  231. machineconfig/scripts/python/.mypy_cache/3.11/enum.data.json +1 -0
  232. machineconfig/scripts/python/.mypy_cache/3.11/enum.meta.json +1 -0
  233. machineconfig/scripts/python/.mypy_cache/3.11/fnmatch.data.json +1 -0
  234. machineconfig/scripts/python/.mypy_cache/3.11/fnmatch.meta.json +1 -0
  235. machineconfig/scripts/python/.mypy_cache/3.11/functools.data.json +1 -0
  236. machineconfig/scripts/python/.mypy_cache/3.11/functools.meta.json +1 -0
  237. machineconfig/scripts/python/.mypy_cache/3.11/gc.data.json +1 -0
  238. machineconfig/scripts/python/.mypy_cache/3.11/gc.meta.json +1 -0
  239. machineconfig/scripts/python/.mypy_cache/3.11/genericpath.data.json +1 -0
  240. machineconfig/scripts/python/.mypy_cache/3.11/genericpath.meta.json +1 -0
  241. machineconfig/scripts/python/.mypy_cache/3.11/getpass.data.json +1 -0
  242. machineconfig/scripts/python/.mypy_cache/3.11/getpass.meta.json +1 -0
  243. machineconfig/scripts/python/.mypy_cache/3.11/git/__init__.data.json +1 -0
  244. machineconfig/scripts/python/.mypy_cache/3.11/git/__init__.meta.json +1 -0
  245. machineconfig/scripts/python/.mypy_cache/3.11/git/cmd.data.json +1 -0
  246. machineconfig/scripts/python/.mypy_cache/3.11/git/cmd.meta.json +1 -0
  247. machineconfig/scripts/python/.mypy_cache/3.11/git/compat.data.json +1 -0
  248. machineconfig/scripts/python/.mypy_cache/3.11/git/compat.meta.json +1 -0
  249. machineconfig/scripts/python/.mypy_cache/3.11/git/config.data.json +1 -0
  250. machineconfig/scripts/python/.mypy_cache/3.11/git/config.meta.json +1 -0
  251. machineconfig/scripts/python/.mypy_cache/3.11/git/db.data.json +1 -0
  252. machineconfig/scripts/python/.mypy_cache/3.11/git/db.meta.json +1 -0
  253. machineconfig/scripts/python/.mypy_cache/3.11/git/diff.data.json +1 -0
  254. machineconfig/scripts/python/.mypy_cache/3.11/git/diff.meta.json +1 -0
  255. machineconfig/scripts/python/.mypy_cache/3.11/git/exc.data.json +1 -0
  256. machineconfig/scripts/python/.mypy_cache/3.11/git/exc.meta.json +1 -0
  257. machineconfig/scripts/python/.mypy_cache/3.11/git/index/__init__.data.json +1 -0
  258. machineconfig/scripts/python/.mypy_cache/3.11/git/index/__init__.meta.json +1 -0
  259. machineconfig/scripts/python/.mypy_cache/3.11/git/index/base.data.json +1 -0
  260. machineconfig/scripts/python/.mypy_cache/3.11/git/index/base.meta.json +1 -0
  261. machineconfig/scripts/python/.mypy_cache/3.11/git/index/fun.data.json +1 -0
  262. machineconfig/scripts/python/.mypy_cache/3.11/git/index/fun.meta.json +1 -0
  263. machineconfig/scripts/python/.mypy_cache/3.11/git/index/typ.data.json +1 -0
  264. machineconfig/scripts/python/.mypy_cache/3.11/git/index/typ.meta.json +1 -0
  265. machineconfig/scripts/python/.mypy_cache/3.11/git/index/util.data.json +1 -0
  266. machineconfig/scripts/python/.mypy_cache/3.11/git/index/util.meta.json +1 -0
  267. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/__init__.data.json +1 -0
  268. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/__init__.meta.json +1 -0
  269. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/base.data.json +1 -0
  270. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/base.meta.json +1 -0
  271. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/blob.data.json +1 -0
  272. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/blob.meta.json +1 -0
  273. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/commit.data.json +1 -0
  274. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/commit.meta.json +1 -0
  275. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/fun.data.json +1 -0
  276. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/fun.meta.json +1 -0
  277. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/__init__.data.json +1 -0
  278. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/__init__.meta.json +1 -0
  279. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/base.data.json +1 -0
  280. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/base.meta.json +1 -0
  281. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/root.data.json +1 -0
  282. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/root.meta.json +1 -0
  283. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/util.data.json +1 -0
  284. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/util.meta.json +1 -0
  285. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/tag.data.json +1 -0
  286. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/tag.meta.json +1 -0
  287. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/tree.data.json +1 -0
  288. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/tree.meta.json +1 -0
  289. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/util.data.json +1 -0
  290. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/util.meta.json +1 -0
  291. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/__init__.data.json +1 -0
  292. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/__init__.meta.json +1 -0
  293. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/head.data.json +1 -0
  294. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/head.meta.json +1 -0
  295. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/log.data.json +1 -0
  296. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/log.meta.json +1 -0
  297. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/reference.data.json +1 -0
  298. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/reference.meta.json +1 -0
  299. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/remote.data.json +1 -0
  300. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/remote.meta.json +1 -0
  301. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/symbolic.data.json +1 -0
  302. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/symbolic.meta.json +1 -0
  303. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/tag.data.json +1 -0
  304. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/tag.meta.json +1 -0
  305. machineconfig/scripts/python/.mypy_cache/3.11/git/remote.data.json +1 -0
  306. machineconfig/scripts/python/.mypy_cache/3.11/git/remote.meta.json +1 -0
  307. machineconfig/scripts/python/.mypy_cache/3.11/git/repo/__init__.data.json +1 -0
  308. machineconfig/scripts/python/.mypy_cache/3.11/git/repo/__init__.meta.json +1 -0
  309. machineconfig/scripts/python/.mypy_cache/3.11/git/repo/base.data.json +1 -0
  310. machineconfig/scripts/python/.mypy_cache/3.11/git/repo/base.meta.json +1 -0
  311. machineconfig/scripts/python/.mypy_cache/3.11/git/repo/fun.data.json +1 -0
  312. machineconfig/scripts/python/.mypy_cache/3.11/git/repo/fun.meta.json +1 -0
  313. machineconfig/scripts/python/.mypy_cache/3.11/git/types.data.json +1 -0
  314. machineconfig/scripts/python/.mypy_cache/3.11/git/types.meta.json +1 -0
  315. machineconfig/scripts/python/.mypy_cache/3.11/git/util.data.json +1 -0
  316. machineconfig/scripts/python/.mypy_cache/3.11/git/util.meta.json +1 -0
  317. machineconfig/scripts/python/.mypy_cache/3.11/glob.data.json +1 -0
  318. machineconfig/scripts/python/.mypy_cache/3.11/glob.meta.json +1 -0
  319. machineconfig/scripts/python/.mypy_cache/3.11/gzip.data.json +1 -0
  320. machineconfig/scripts/python/.mypy_cache/3.11/gzip.meta.json +1 -0
  321. machineconfig/scripts/python/.mypy_cache/3.11/importlib/__init__.data.json +1 -0
  322. machineconfig/scripts/python/.mypy_cache/3.11/importlib/__init__.meta.json +1 -0
  323. machineconfig/scripts/python/.mypy_cache/3.11/importlib/_abc.data.json +1 -0
  324. machineconfig/scripts/python/.mypy_cache/3.11/importlib/_abc.meta.json +1 -0
  325. machineconfig/scripts/python/.mypy_cache/3.11/importlib/_bootstrap.data.json +1 -0
  326. machineconfig/scripts/python/.mypy_cache/3.11/importlib/_bootstrap.meta.json +1 -0
  327. machineconfig/scripts/python/.mypy_cache/3.11/importlib/_bootstrap_external.data.json +1 -0
  328. machineconfig/scripts/python/.mypy_cache/3.11/importlib/_bootstrap_external.meta.json +1 -0
  329. machineconfig/scripts/python/.mypy_cache/3.11/importlib/abc.data.json +1 -0
  330. machineconfig/scripts/python/.mypy_cache/3.11/importlib/abc.meta.json +1 -0
  331. machineconfig/scripts/python/.mypy_cache/3.11/importlib/machinery.data.json +1 -0
  332. machineconfig/scripts/python/.mypy_cache/3.11/importlib/machinery.meta.json +1 -0
  333. machineconfig/scripts/python/.mypy_cache/3.11/importlib/metadata/__init__.data.json +1 -0
  334. machineconfig/scripts/python/.mypy_cache/3.11/importlib/metadata/__init__.meta.json +1 -0
  335. machineconfig/scripts/python/.mypy_cache/3.11/importlib/metadata/_meta.data.json +1 -0
  336. machineconfig/scripts/python/.mypy_cache/3.11/importlib/metadata/_meta.meta.json +1 -0
  337. machineconfig/scripts/python/.mypy_cache/3.11/importlib/readers.data.json +1 -0
  338. machineconfig/scripts/python/.mypy_cache/3.11/importlib/readers.meta.json +1 -0
  339. machineconfig/scripts/python/.mypy_cache/3.11/importlib/resources/__init__.data.json +1 -0
  340. machineconfig/scripts/python/.mypy_cache/3.11/importlib/resources/__init__.meta.json +1 -0
  341. machineconfig/scripts/python/.mypy_cache/3.11/importlib/resources/_common.data.json +1 -0
  342. machineconfig/scripts/python/.mypy_cache/3.11/importlib/resources/_common.meta.json +1 -0
  343. machineconfig/scripts/python/.mypy_cache/3.11/importlib/resources/abc.data.json +1 -0
  344. machineconfig/scripts/python/.mypy_cache/3.11/importlib/resources/abc.meta.json +1 -0
  345. machineconfig/scripts/python/.mypy_cache/3.11/inspect.data.json +1 -0
  346. machineconfig/scripts/python/.mypy_cache/3.11/inspect.meta.json +1 -0
  347. machineconfig/scripts/python/.mypy_cache/3.11/io.data.json +1 -0
  348. machineconfig/scripts/python/.mypy_cache/3.11/io.meta.json +1 -0
  349. machineconfig/scripts/python/.mypy_cache/3.11/itertools.data.json +1 -0
  350. machineconfig/scripts/python/.mypy_cache/3.11/itertools.meta.json +1 -0
  351. machineconfig/scripts/python/.mypy_cache/3.11/locale.data.json +1 -0
  352. machineconfig/scripts/python/.mypy_cache/3.11/locale.meta.json +1 -0
  353. machineconfig/scripts/python/.mypy_cache/3.11/logging/__init__.data.json +1 -0
  354. machineconfig/scripts/python/.mypy_cache/3.11/logging/__init__.meta.json +1 -0
  355. machineconfig/scripts/python/.mypy_cache/3.11/mimetypes.data.json +1 -0
  356. machineconfig/scripts/python/.mypy_cache/3.11/mimetypes.meta.json +1 -0
  357. machineconfig/scripts/python/.mypy_cache/3.11/mmap.data.json +1 -0
  358. machineconfig/scripts/python/.mypy_cache/3.11/mmap.meta.json +1 -0
  359. machineconfig/scripts/python/.mypy_cache/3.11/numbers.data.json +1 -0
  360. machineconfig/scripts/python/.mypy_cache/3.11/numbers.meta.json +1 -0
  361. machineconfig/scripts/python/.mypy_cache/3.11/opcode.data.json +1 -0
  362. machineconfig/scripts/python/.mypy_cache/3.11/opcode.meta.json +1 -0
  363. machineconfig/scripts/python/.mypy_cache/3.11/os/__init__.data.json +1 -0
  364. machineconfig/scripts/python/.mypy_cache/3.11/os/__init__.meta.json +1 -0
  365. machineconfig/scripts/python/.mypy_cache/3.11/os/path.data.json +1 -0
  366. machineconfig/scripts/python/.mypy_cache/3.11/os/path.meta.json +1 -0
  367. machineconfig/scripts/python/.mypy_cache/3.11/pathlib.data.json +1 -0
  368. machineconfig/scripts/python/.mypy_cache/3.11/pathlib.meta.json +1 -0
  369. machineconfig/scripts/python/.mypy_cache/3.11/platform.data.json +1 -0
  370. machineconfig/scripts/python/.mypy_cache/3.11/platform.meta.json +1 -0
  371. machineconfig/scripts/python/.mypy_cache/3.11/posixpath.data.json +1 -0
  372. machineconfig/scripts/python/.mypy_cache/3.11/posixpath.meta.json +1 -0
  373. machineconfig/scripts/python/.mypy_cache/3.11/re.data.json +1 -0
  374. machineconfig/scripts/python/.mypy_cache/3.11/re.meta.json +1 -0
  375. machineconfig/scripts/python/.mypy_cache/3.11/resource.data.json +1 -0
  376. machineconfig/scripts/python/.mypy_cache/3.11/resource.meta.json +1 -0
  377. machineconfig/scripts/python/.mypy_cache/3.11/shlex.data.json +1 -0
  378. machineconfig/scripts/python/.mypy_cache/3.11/shlex.meta.json +1 -0
  379. machineconfig/scripts/python/.mypy_cache/3.11/shutil.data.json +1 -0
  380. machineconfig/scripts/python/.mypy_cache/3.11/shutil.meta.json +1 -0
  381. machineconfig/scripts/python/.mypy_cache/3.11/signal.data.json +1 -0
  382. machineconfig/scripts/python/.mypy_cache/3.11/signal.meta.json +1 -0
  383. machineconfig/scripts/python/.mypy_cache/3.11/src/__init__.data.json +1 -0
  384. machineconfig/scripts/python/.mypy_cache/3.11/src/__init__.meta.json +1 -0
  385. machineconfig/scripts/python/.mypy_cache/3.11/src/machineconfig/__init__.data.json +1 -0
  386. machineconfig/scripts/python/.mypy_cache/3.11/src/machineconfig/__init__.meta.json +1 -0
  387. machineconfig/scripts/python/.mypy_cache/3.11/src/machineconfig/scripts/__init__.data.json +1 -0
  388. machineconfig/scripts/python/.mypy_cache/3.11/src/machineconfig/scripts/__init__.meta.json +1 -0
  389. machineconfig/scripts/python/.mypy_cache/3.11/src/machineconfig/scripts/python/__init__.data.json +1 -0
  390. machineconfig/scripts/python/.mypy_cache/3.11/src/machineconfig/scripts/python/__init__.meta.json +1 -0
  391. machineconfig/scripts/python/.mypy_cache/3.11/sre_compile.data.json +1 -0
  392. machineconfig/scripts/python/.mypy_cache/3.11/sre_compile.meta.json +1 -0
  393. machineconfig/scripts/python/.mypy_cache/3.11/sre_constants.data.json +1 -0
  394. machineconfig/scripts/python/.mypy_cache/3.11/sre_constants.meta.json +1 -0
  395. machineconfig/scripts/python/.mypy_cache/3.11/sre_parse.data.json +1 -0
  396. machineconfig/scripts/python/.mypy_cache/3.11/sre_parse.meta.json +1 -0
  397. machineconfig/scripts/python/.mypy_cache/3.11/stat.data.json +1 -0
  398. machineconfig/scripts/python/.mypy_cache/3.11/stat.meta.json +1 -0
  399. machineconfig/scripts/python/.mypy_cache/3.11/string.data.json +1 -0
  400. machineconfig/scripts/python/.mypy_cache/3.11/string.meta.json +1 -0
  401. machineconfig/scripts/python/.mypy_cache/3.11/struct.data.json +1 -0
  402. machineconfig/scripts/python/.mypy_cache/3.11/struct.meta.json +1 -0
  403. machineconfig/scripts/python/.mypy_cache/3.11/subprocess.data.json +1 -0
  404. machineconfig/scripts/python/.mypy_cache/3.11/subprocess.meta.json +1 -0
  405. machineconfig/scripts/python/.mypy_cache/3.11/sys/__init__.data.json +1 -0
  406. machineconfig/scripts/python/.mypy_cache/3.11/sys/__init__.meta.json +1 -0
  407. machineconfig/scripts/python/.mypy_cache/3.11/tarfile.data.json +1 -0
  408. machineconfig/scripts/python/.mypy_cache/3.11/tarfile.meta.json +1 -0
  409. machineconfig/scripts/python/.mypy_cache/3.11/tempfile.data.json +1 -0
  410. machineconfig/scripts/python/.mypy_cache/3.11/tempfile.meta.json +1 -0
  411. machineconfig/scripts/python/.mypy_cache/3.11/textwrap.data.json +1 -0
  412. machineconfig/scripts/python/.mypy_cache/3.11/textwrap.meta.json +1 -0
  413. machineconfig/scripts/python/.mypy_cache/3.11/threading.data.json +1 -0
  414. machineconfig/scripts/python/.mypy_cache/3.11/threading.meta.json +1 -0
  415. machineconfig/scripts/python/.mypy_cache/3.11/time.data.json +1 -0
  416. machineconfig/scripts/python/.mypy_cache/3.11/time.meta.json +1 -0
  417. machineconfig/scripts/python/.mypy_cache/3.11/types.data.json +1 -0
  418. machineconfig/scripts/python/.mypy_cache/3.11/types.meta.json +1 -0
  419. machineconfig/scripts/python/.mypy_cache/3.11/typing.data.json +1 -0
  420. machineconfig/scripts/python/.mypy_cache/3.11/typing.meta.json +1 -0
  421. machineconfig/scripts/python/.mypy_cache/3.11/typing_extensions.data.json +1 -0
  422. machineconfig/scripts/python/.mypy_cache/3.11/typing_extensions.meta.json +1 -0
  423. machineconfig/scripts/python/.mypy_cache/3.11/urllib/__init__.data.json +1 -0
  424. machineconfig/scripts/python/.mypy_cache/3.11/urllib/__init__.meta.json +1 -0
  425. machineconfig/scripts/python/.mypy_cache/3.11/urllib/parse.data.json +1 -0
  426. machineconfig/scripts/python/.mypy_cache/3.11/urllib/parse.meta.json +1 -0
  427. machineconfig/scripts/python/.mypy_cache/3.11/uuid.data.json +1 -0
  428. machineconfig/scripts/python/.mypy_cache/3.11/uuid.meta.json +1 -0
  429. machineconfig/scripts/python/.mypy_cache/3.11/warnings.data.json +1 -0
  430. machineconfig/scripts/python/.mypy_cache/3.11/warnings.meta.json +1 -0
  431. machineconfig/scripts/python/.mypy_cache/3.11/weakref.data.json +1 -0
  432. machineconfig/scripts/python/.mypy_cache/3.11/weakref.meta.json +1 -0
  433. machineconfig/scripts/python/.mypy_cache/3.11/zipfile/__init__.data.json +1 -0
  434. machineconfig/scripts/python/.mypy_cache/3.11/zipfile/__init__.meta.json +1 -0
  435. machineconfig/scripts/python/.mypy_cache/3.11/zlib.data.json +1 -0
  436. machineconfig/scripts/python/.mypy_cache/3.11/zlib.meta.json +1 -0
  437. machineconfig/scripts/python/.mypy_cache/CACHEDIR.TAG +3 -0
  438. machineconfig/scripts/python/__pycache__/__init__.cpython-311.pyc +0 -0
  439. machineconfig/scripts/python/__pycache__/cloud_copy.cpython-311.pyc +0 -0
  440. machineconfig/scripts/python/__pycache__/cloud_mount.cpython-311.pyc +0 -0
  441. machineconfig/scripts/python/__pycache__/cloud_repo_sync.cpython-311.pyc +0 -0
  442. machineconfig/scripts/python/__pycache__/cloud_sync.cpython-311.pyc +0 -0
  443. machineconfig/scripts/python/__pycache__/croshell.cpython-311.pyc +0 -0
  444. machineconfig/scripts/python/__pycache__/devops.cpython-311.pyc +0 -0
  445. machineconfig/scripts/python/__pycache__/devops_backup_retrieve.cpython-311.pyc +0 -0
  446. machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-311.pyc +0 -0
  447. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-311.pyc +0 -0
  448. machineconfig/scripts/python/__pycache__/fire_jobs.cpython-311.pyc +0 -0
  449. machineconfig/scripts/python/__pycache__/gh_models.cpython-311.pyc +0 -0
  450. machineconfig/scripts/python/__pycache__/repos.cpython-311.pyc +0 -0
  451. machineconfig/scripts/python/__pycache__/url2md.cpython-311.pyc +0 -0
  452. machineconfig/scripts/python/__pycache__/viewer.cpython-311.pyc +0 -0
  453. machineconfig/scripts/python/__pycache__/vscode_api.cpython-311.pyc +0 -0
  454. machineconfig/scripts/python/archive/im2text.py +36 -0
  455. machineconfig/scripts/python/archive/tmate_conn.py +44 -0
  456. machineconfig/scripts/python/archive/tmate_start.py +48 -0
  457. machineconfig/scripts/python/choose_wezterm_theme.py +23 -4
  458. machineconfig/scripts/python/cloud_copy.py +131 -29
  459. machineconfig/scripts/python/cloud_manager.py +55 -2
  460. machineconfig/scripts/python/cloud_mount.py +43 -4
  461. machineconfig/scripts/python/cloud_repo_sync.py +114 -103
  462. machineconfig/scripts/python/cloud_sync.py +36 -220
  463. machineconfig/scripts/python/croshell.py +102 -32
  464. machineconfig/scripts/python/devops.py +143 -42
  465. machineconfig/scripts/python/devops_add_identity.py +112 -9
  466. machineconfig/scripts/python/devops_add_ssh_key.py +170 -15
  467. machineconfig/scripts/python/devops_backup_retrieve.py +132 -14
  468. machineconfig/scripts/python/devops_devapps_install.py +33 -11
  469. machineconfig/scripts/python/devops_update_repos.py +36 -29
  470. machineconfig/scripts/python/dotfile.py +8 -3
  471. machineconfig/scripts/python/fire_jobs.py +145 -289
  472. machineconfig/scripts/python/ftpx.py +76 -18
  473. machineconfig/scripts/python/get_zellij_cmd.py +14 -0
  474. machineconfig/scripts/python/gh_models.py +72 -25
  475. machineconfig/scripts/python/helpers/__init__.py +0 -0
  476. machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-311.pyc +0 -0
  477. machineconfig/scripts/python/helpers/__pycache__/cloud_helpers.cpython-311.pyc +0 -0
  478. machineconfig/scripts/python/helpers/__pycache__/helpers2.cpython-311.pyc +0 -0
  479. machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-311.pyc +0 -0
  480. machineconfig/scripts/python/helpers/cloud_helpers.py +134 -0
  481. machineconfig/scripts/python/helpers/helpers2.py +149 -0
  482. machineconfig/scripts/python/helpers/helpers4.py +178 -0
  483. machineconfig/scripts/python/helpers/helpers5.py +50 -0
  484. machineconfig/scripts/python/helpers/repo_sync_helpers.py +126 -0
  485. machineconfig/scripts/python/mount_nfs.py +46 -15
  486. machineconfig/scripts/python/mount_nw_drive.py +22 -10
  487. machineconfig/scripts/python/mount_ssh.py +28 -12
  488. machineconfig/scripts/python/onetimeshare.py +23 -15
  489. machineconfig/scripts/python/pomodoro.py +38 -24
  490. machineconfig/scripts/python/repos.py +29 -32
  491. machineconfig/scripts/python/scheduler.py +19 -18
  492. machineconfig/scripts/python/snapshot.py +14 -6
  493. machineconfig/scripts/python/start_slidev.py +24 -32
  494. machineconfig/scripts/python/start_terminals.py +23 -10
  495. machineconfig/scripts/python/viewer.py +53 -0
  496. machineconfig/scripts/python/viewer_template.py +140 -0
  497. machineconfig/scripts/python/wifi_conn.py +20 -12
  498. machineconfig/scripts/python/wsl_windows_transfer.py +18 -10
  499. machineconfig/scripts/windows/activate_ve.ps1 +54 -0
  500. machineconfig/scripts/windows/archive/gource2vid.ps1 +14 -0
  501. machineconfig/scripts/windows/archive/im2text.ps1 +27 -0
  502. machineconfig/scripts/windows/archive/secure_pull.ps1 +46 -0
  503. machineconfig/scripts/windows/archive/secure_push.ps1 +85 -0
  504. machineconfig/scripts/windows/archive/tmate_conn.ps1 +7 -0
  505. machineconfig/scripts/windows/checkout_version.ps1 +6 -0
  506. machineconfig/scripts/windows/choose_wezterm_theme.ps1 +20 -0
  507. machineconfig/scripts/windows/cloud_copy.ps1 +17 -0
  508. machineconfig/scripts/windows/cloud_manager.ps1 +8 -0
  509. machineconfig/scripts/windows/cloud_mount.ps1 +25 -0
  510. machineconfig/scripts/windows/cloud_repo_sync.ps1 +8 -0
  511. machineconfig/scripts/windows/cloud_sync.ps1 +21 -0
  512. machineconfig/scripts/windows/croshell.ps1 +40 -0
  513. machineconfig/scripts/windows/devops.ps1 +32 -0
  514. machineconfig/scripts/windows/dotfile.ps1 +9 -0
  515. machineconfig/scripts/windows/fire.ps1 +33 -0
  516. machineconfig/scripts/windows/ftpx.ps1 +5 -0
  517. machineconfig/scripts/windows/fzfb.ps1 +3 -0
  518. machineconfig/scripts/windows/fzfg.ps1 +2 -0
  519. machineconfig/scripts/windows/fzfrga.bat +20 -0
  520. machineconfig/scripts/windows/gpt.ps1 +23 -0
  521. machineconfig/scripts/windows/grep.ps1 +2 -0
  522. machineconfig/scripts/windows/kill_process.ps1 +8 -0
  523. machineconfig/scripts/windows/mount_nfs.ps1 +44 -0
  524. machineconfig/scripts/windows/mount_nw.ps1 +9 -0
  525. machineconfig/scripts/windows/mount_smb.ps1 +2 -0
  526. machineconfig/scripts/windows/mount_ssh.ps1 +17 -0
  527. machineconfig/scripts/windows/nano.ps1 +3 -0
  528. machineconfig/scripts/windows/neofetch.ps1 +2 -0
  529. machineconfig/scripts/windows/pomodoro.ps1 +8 -0
  530. machineconfig/scripts/windows/py2exe.ps1 +12 -0
  531. machineconfig/scripts/windows/reload_path.ps1 +3 -0
  532. machineconfig/scripts/windows/repos.ps1 +27 -0
  533. machineconfig/scripts/windows/scheduler.ps1 +6 -0
  534. machineconfig/scripts/windows/share_cloud.cmd +34 -0
  535. machineconfig/scripts/windows/share_nfs.ps1 +0 -0
  536. machineconfig/scripts/windows/share_smb.ps1 +22 -0
  537. machineconfig/scripts/windows/snapshot.ps1 +5 -0
  538. machineconfig/scripts/windows/start_slidev.ps1 +21 -0
  539. machineconfig/scripts/windows/start_terminals.ps1 +22 -0
  540. machineconfig/scripts/windows/unlock_bitlocker.ps1 +10 -0
  541. machineconfig/scripts/windows/utils/op_script_delete.ps1 +7 -0
  542. machineconfig/scripts/windows/wifi_conn.ps1 +7 -0
  543. machineconfig/scripts/windows/wsl_rdp_windows_port_forwarding.ps1 +46 -0
  544. machineconfig/scripts/windows/wsl_ssh_windows_port_forwarding.ps1 +76 -0
  545. machineconfig/scripts/windows/wsl_windows_transfer.ps1 +7 -0
  546. machineconfig/settings/__pycache__/__init__.cpython-311.pyc +0 -0
  547. machineconfig/settings/broot/br.sh +51 -0
  548. machineconfig/settings/broot/brootcd.ps1 +32 -0
  549. machineconfig/settings/broot/conf.toml +5 -0
  550. machineconfig/settings/glow/glow.yml +11 -0
  551. machineconfig/settings/gromit-mpx/gromit-mpx.cfg +34 -0
  552. machineconfig/settings/helix/config.toml +27 -0
  553. machineconfig/settings/helix/languages.toml +22 -0
  554. machineconfig/settings/keras/keras.json +6 -0
  555. machineconfig/settings/keyboard/espanso/config/default.yml +45 -0
  556. machineconfig/settings/keyboard/espanso/match/base.yml +57 -0
  557. machineconfig/settings/keyboard/kanata/kanata.kbd +0 -0
  558. machineconfig/settings/lf/linux/autocall/delete.sh +0 -0
  559. machineconfig/settings/lf/linux/autocall/on-cd.sh +0 -0
  560. machineconfig/settings/lf/linux/autocall/on-quit.sh +0 -0
  561. machineconfig/settings/lf/linux/autocall/open.sh +0 -0
  562. machineconfig/settings/lf/linux/autocall/paste.sh +0 -0
  563. machineconfig/settings/lf/linux/autocall/pre-cd.sh +0 -0
  564. machineconfig/settings/lf/linux/autocall/rename.sh +0 -0
  565. machineconfig/settings/lf/linux/colors +196 -0
  566. machineconfig/settings/lf/linux/exe/cleaner.sh +7 -0
  567. machineconfig/settings/lf/linux/exe/fzf_nano.sh +16 -0
  568. machineconfig/settings/lf/linux/exe/leftpane_previewer.sh +5 -0
  569. machineconfig/settings/lf/linux/exe/lfcd.sh +32 -0
  570. machineconfig/settings/lf/linux/exe/previewer.sh +37 -0
  571. machineconfig/settings/lf/linux/exe/previewer_archive.sh +155 -0
  572. machineconfig/settings/lf/linux/icons +357 -0
  573. machineconfig/settings/lf/linux/lfrc +226 -0
  574. machineconfig/settings/lf/windows/autocall/delete.ps1 +0 -0
  575. machineconfig/settings/lf/windows/autocall/on-cd.ps1 +0 -0
  576. machineconfig/settings/lf/windows/autocall/on-quit.ps1 +0 -0
  577. machineconfig/settings/lf/windows/autocall/open.ps1 +0 -0
  578. machineconfig/settings/lf/windows/autocall/paste.ps1 +0 -0
  579. machineconfig/settings/lf/windows/autocall/pre-cd.ps1 +0 -0
  580. machineconfig/settings/lf/windows/autocall/rename.ps1 +0 -0
  581. machineconfig/settings/lf/windows/cd_tere.ps1 +4 -0
  582. machineconfig/settings/lf/windows/cd_zoxide.ps1 +4 -0
  583. machineconfig/settings/lf/windows/cd_zoxide2.ps1 +4 -0
  584. machineconfig/settings/lf/windows/colors +159 -0
  585. machineconfig/settings/lf/windows/fzf_edit.ps1 +6 -0
  586. machineconfig/settings/lf/windows/icons +357 -0
  587. machineconfig/settings/lf/windows/leftpane_previewer.ps1 +3 -0
  588. machineconfig/settings/lf/windows/lfcd.ps1 +35 -0
  589. machineconfig/settings/lf/windows/lfrc +133 -0
  590. machineconfig/settings/lf/windows/mkdir.ps1 +3 -0
  591. machineconfig/settings/lf/windows/mkfile.ps1 +3 -0
  592. machineconfig/settings/lf/windows/previewer.ps1 +7 -0
  593. machineconfig/settings/lf/windows/tst.ps1 +1 -0
  594. machineconfig/settings/linters/.flake8 +24 -0
  595. machineconfig/settings/linters/.mypy.ini +29 -0
  596. machineconfig/settings/linters/.pylintrc +91 -0
  597. machineconfig/settings/linters/.ruff.toml +77 -0
  598. machineconfig/settings/linters/.ruff_cache/.gitignore +2 -0
  599. machineconfig/settings/linters/.ruff_cache/CACHEDIR.TAG +1 -0
  600. machineconfig/settings/lvim/linux/config.lua +0 -0
  601. machineconfig/settings/lvim/windows/archive/config_additional.lua +39 -0
  602. machineconfig/settings/lvim/windows/lua/user/custom_config.lua +13 -0
  603. machineconfig/settings/mprocs/windows/mprocs.yaml +55 -0
  604. machineconfig/settings/mprocs/windows/other +12 -0
  605. machineconfig/settings/pistol/pistol.conf +8 -0
  606. machineconfig/settings/presenterm/config.yaml +76 -0
  607. machineconfig/settings/procs/.procs.toml +142 -0
  608. machineconfig/settings/pudb/pudb.cfg +25 -0
  609. machineconfig/settings/rofi/config.rasi +4 -0
  610. machineconfig/settings/rofi/config_default.rasi +147 -0
  611. machineconfig/settings/shells/alacritty/alacritty.toml +40 -0
  612. machineconfig/settings/shells/alacritty/alacritty.yml +0 -0
  613. machineconfig/settings/shells/bash/.inputrc +3 -0
  614. machineconfig/settings/shells/bash/init.sh +66 -0
  615. machineconfig/settings/shells/hyper/.hyper.js +201 -0
  616. machineconfig/settings/shells/ipy/profiles/default/__init__.py +0 -0
  617. machineconfig/settings/shells/ipy/profiles/default/__pycache__/__init__.cpython-311.pyc +0 -0
  618. machineconfig/settings/shells/ipy/profiles/default/startup/__init__.py +0 -0
  619. machineconfig/settings/shells/ipy/profiles/default/startup/__pycache__/__init__.cpython-311.pyc +0 -0
  620. machineconfig/settings/shells/ipy/profiles/default/startup/__pycache__/playext.cpython-311.pyc +0 -0
  621. machineconfig/settings/shells/ipy/profiles/default/startup/playext.py +83 -0
  622. machineconfig/settings/shells/kitty/kitty.conf +1476 -0
  623. machineconfig/settings/shells/nushell/config.nu +36 -0
  624. machineconfig/settings/shells/nushell/env.nu +13 -0
  625. machineconfig/settings/shells/pwsh/init.ps1 +88 -0
  626. machineconfig/settings/shells/pwsh/profile.ps1 +0 -0
  627. machineconfig/settings/shells/starship/starship.toml +58 -0
  628. machineconfig/settings/shells/vtm/settings.xml +297 -0
  629. machineconfig/settings/shells/wezterm/wezterm.lua +193 -0
  630. machineconfig/settings/shells/wt/settings.json +526 -0
  631. machineconfig/settings/streamlit/config.toml +22 -0
  632. machineconfig/settings/svim/linux/init.toml +48 -0
  633. machineconfig/settings/svim/windows/init.toml +48 -0
  634. machineconfig/settings/tere/terecd.ps1 +8 -0
  635. machineconfig/settings/tere/terecd.sh +8 -0
  636. machineconfig/settings/tmux/.tmate.conf +3 -0
  637. machineconfig/settings/tmux/.tmux.conf +6 -0
  638. machineconfig/settings/wsl/.wslconfig +35 -0
  639. machineconfig/settings/yazi/keymap.toml +0 -0
  640. machineconfig/settings/yazi/theme.toml +0 -0
  641. machineconfig/settings/yazi/yazi.toml +4 -0
  642. machineconfig/settings/zed/settings.json +35 -0
  643. machineconfig/settings/zellij/commands/monitor +9 -0
  644. machineconfig/settings/zellij/commands/standard_panes +33 -0
  645. machineconfig/settings/zellij/config.kdl +295 -0
  646. machineconfig/settings/zellij/config.orig.kdl +295 -0
  647. machineconfig/settings/zellij/layouts/hist +13 -0
  648. machineconfig/settings/zellij/layouts/panes.kdl +20 -0
  649. machineconfig/settings/zellij/layouts/st.kdl +21 -0
  650. machineconfig/settings/zellij/layouts/st2.kdl +59 -0
  651. machineconfig/settings/zellij/layouts/stacked_panes.kdl +11 -0
  652. machineconfig/setup_linux/nix/cli_installation.sh +166 -0
  653. machineconfig/setup_linux/others/mint_keyboard_shortcuts.sh +30 -0
  654. machineconfig/setup_linux/others/openssh-server_add_pub_key.sh +60 -0
  655. machineconfig/setup_linux/web_shortcuts/all.sh +53 -0
  656. machineconfig/setup_linux/web_shortcuts/ascii_art.sh +100 -0
  657. machineconfig/setup_linux/web_shortcuts/croshell.sh +66 -0
  658. machineconfig/setup_linux/web_shortcuts/interactive.sh +241 -0
  659. machineconfig/setup_linux/web_shortcuts/ssh.sh +64 -0
  660. machineconfig/setup_linux/web_shortcuts/update_system.sh +55 -0
  661. machineconfig/setup_windows/others/docker.ps1 +7 -0
  662. machineconfig/setup_windows/others/obs.ps1 +4 -0
  663. machineconfig/setup_windows/web_shortcuts/all.ps1 +18 -0
  664. machineconfig/setup_windows/web_shortcuts/ascii_art.ps1 +36 -0
  665. machineconfig/setup_windows/web_shortcuts/croshell.ps1 +16 -0
  666. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +200 -0
  667. machineconfig/setup_windows/web_shortcuts/ssh.ps1 +11 -0
  668. machineconfig/setup_windows/wt_and_pwsh/install_fonts.ps1 +27 -0
  669. machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +12 -2
  670. machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +45 -9
  671. machineconfig/utils/ai/__init__.py +0 -0
  672. machineconfig/utils/ai/browser_user_wrapper.py +51 -0
  673. machineconfig/utils/ai/generate_file_checklist.py +74 -0
  674. machineconfig/utils/ai/url2md.py +75 -0
  675. machineconfig/utils/installer.py +101 -253
  676. machineconfig/utils/installer_utils/__init__.py +0 -0
  677. machineconfig/utils/installer_utils/installer_abc.py +100 -0
  678. machineconfig/utils/installer_utils/installer_class.py +253 -0
  679. machineconfig/utils/procs.py +69 -16
  680. machineconfig/utils/scheduling.py +47 -13
  681. machineconfig/utils/utils.py +39 -371
  682. machineconfig/utils/utils_code.py +82 -0
  683. machineconfig/utils/utils_links.py +88 -0
  684. machineconfig/utils/utils_options.py +163 -0
  685. machineconfig/utils/utils_path.py +151 -0
  686. machineconfig/utils/ve.py +37 -225
  687. machineconfig/utils/ve_utils/ve1.py +111 -0
  688. machineconfig/utils/ve_utils/ve2.py +142 -0
  689. {machineconfig-1.92.dist-info → machineconfig-1.94.dist-info}/METADATA +25 -21
  690. machineconfig-1.94.dist-info/RECORD +710 -0
  691. {machineconfig-1.92.dist-info → machineconfig-1.94.dist-info}/WHEEL +1 -1
  692. machineconfig-1.92.dist-info/LICENSE +0 -201
  693. machineconfig-1.92.dist-info/RECORD +0 -70
  694. {machineconfig-1.92.dist-info → machineconfig-1.94.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,150 @@
1
+
2
+ """
3
+ Runner
4
+ """
5
+
6
+ from rich.console import Console
7
+ import pandas as pd
8
+
9
+ from crocodile.core import List as L, Struct as S, randstr
10
+ from crocodile.file_management_helpers.file4 import Read
11
+ from crocodile.meta import SSH
12
+ from machineconfig.cluster.self_ssh import SelfSSH
13
+
14
+ from typing import Optional, Any, Literal, TypeAlias, Union
15
+ from dataclasses import dataclass, field
16
+
17
+
18
+ JOB_STATUS: TypeAlias = Literal["queued", "running", "completed", "failed"]
19
+ TRANSFER_METHOD: TypeAlias = Literal["sftp", "transfer_sh", "cloud"]
20
+ LAUNCH_METHOD: TypeAlias = Literal["remotely", "cloud_manager"]
21
+ console = Console()
22
+
23
+
24
+ @dataclass
25
+ class WorkloadParams:
26
+ idx_min: int = 0
27
+ idx_max: int = 1000
28
+ idx_start: int = 0
29
+ idx_end: int = 1000
30
+ idx: int = 0
31
+ jobs: int = 3
32
+ job_id: str = ''
33
+ @property
34
+ def save_suffix(self) -> str: return f"machine_{self.idx_start}_{self.idx_end}"
35
+ def split_to_jobs(self, jobs: Optional[int] = None) -> L['WorkloadParams']:
36
+ # Note: like MachineLoadCalculator get_kwargs, the behaviour is to include the edge cases on both ends of subsequent intervals.
37
+ res = L(range(self.idx_start, self.idx_end, 1)).split(to=jobs or self.jobs).apply(lambda sub_list: WorkloadParams(idx_start=sub_list.list[0], idx_end=sub_list.list[-1] + 1, idx_max=self.idx_max, jobs=self.jobs))
38
+ for idx, item in enumerate(res): item.idx = idx
39
+ return res
40
+ def get_section_from_series(self, series: list[pd.Timestamp]):
41
+ from math import floor
42
+ min_idx_start = int(floor((len(series) - 1) * self.idx_start / self.idx_max))
43
+ min_idx_end = int(floor((len(series) - 1) * self.idx_end / self.idx_max))
44
+ min_start = series[min_idx_start]
45
+ min_end = series[min_idx_end]
46
+ return min_start, min_end
47
+ def print(self): S(self.__dict__).print(as_config=True, title="Job Workload")
48
+ def viz(self):
49
+ print(f"This machine will execute ({(self.idx_end - self.idx_start) / self.idx_max * 100:.2f}%) of total job workload.")
50
+ print(f"This share of workload will be split among {self.jobs} of threads on this machine.")
51
+
52
+
53
+ @dataclass
54
+ class JobStatus:
55
+ pid: int
56
+ job_id: str
57
+ status: Literal['locked', 'unlocked']
58
+ submission_time: pd.Timestamp
59
+ start_time: Optional[pd.Timestamp] = None
60
+
61
+
62
+ @dataclass
63
+ class EmailParams:
64
+ addressee: str
65
+ speaker: str
66
+ ssh_conn_str: str
67
+ executed_obj: str
68
+ email_config_name: str
69
+ to_email: str
70
+ file_manager_path: str
71
+ @staticmethod
72
+ def from_empty() -> 'EmailParams': return EmailParams(addressee="", speaker="", ssh_conn_str="", executed_obj="", email_config_name="", to_email="", file_manager_path="")
73
+
74
+
75
+ @dataclass
76
+ class LogEntry:
77
+ name: str
78
+ submission_time: str
79
+ start_time: Optional[str]
80
+ end_time: Optional[str]
81
+ run_machine: Optional[str]
82
+ session_name: Optional[str]
83
+ pid: Optional[int]
84
+ cmd: Optional[str]
85
+ source_machine: str
86
+ note: str
87
+ @staticmethod
88
+ def from_dict(a_dict: dict[str, Any]):
89
+ return LogEntry(name=a_dict["name"], submission_time=pd.to_datetime(a_dict["submission_time"]), start_time=pd.to_datetime(a_dict["start_time"]), end_time=pd.to_datetime(a_dict["end_time"]),
90
+ run_machine=a_dict["run_machine"], source_machine=a_dict["source_machine"], note=a_dict["note"], pid=a_dict["pid"], cmd=a_dict["cmd"], session_name=a_dict["session_name"])
91
+
92
+
93
+
94
+ @dataclass
95
+ class RemoteMachineConfig:
96
+ # conn
97
+ job_id: str = field(default_factory=lambda: randstr(noun=True))
98
+ base_dir: str = "~/tmp_results/remote_machines/jobs"
99
+ description: str = ""
100
+ ssh_params: dict[str, Union[str, int]] = field(default_factory=lambda: {})
101
+ ssh_obj: Union[SSH, 'SelfSSH', None] = None
102
+
103
+ # data
104
+ copy_repo: bool = False
105
+ update_repo: bool = False
106
+ install_repo: bool = False
107
+ update_essential_repos: bool = True
108
+ data: Optional[list[Any]] = None
109
+ transfer_method: TRANSFER_METHOD = "sftp"
110
+ cloud_name: Optional[str] = None
111
+
112
+ # remote machine behaviour
113
+ allowed_remotes: Optional[list[str]] = None
114
+ open_console: bool = True
115
+ notify_upon_completion: bool = False
116
+ to_email: Optional[str] = None
117
+ email_config_name: Optional[str] = None
118
+
119
+ # execution behaviour
120
+ launch_method: LAUNCH_METHOD = "remotely"
121
+ kill_on_completion: bool = False
122
+ ipython: bool = False
123
+ interactive: bool = False
124
+ pdb: bool = False
125
+ pudb: bool = False
126
+ wrap_in_try_except: bool = False
127
+ parallelize: bool = False
128
+ lock_resources: bool = True
129
+ max_simulataneous_jobs: int = 1
130
+ workload_params: Optional[WorkloadParams] = None
131
+ def __post_init__(self) -> None:
132
+ if self.interactive and self.lock_resources: print("RemoteMachineConfig Warning: If interactive is ON along with lock_resources, the job might never end. ⚠️")
133
+ if self.transfer_method == "cloud": assert self.cloud_name is not None, "Cloud name is not provided. 🤷‍♂️"
134
+ if self.notify_upon_completion and self.to_email is None:
135
+ from machineconfig.utils.utils import DEFAULTS_PATH
136
+ try:
137
+ section = Read.ini(DEFAULTS_PATH)['general']
138
+ self.to_email = section['to_email']
139
+ except (FileNotFoundError, KeyError, IndexError) as err: raise ValueError(f"Email address is not provided. 🤷‍♂️ & default could not be read @ `{DEFAULTS_PATH}`") from err
140
+ if self.notify_upon_completion and self.email_config_name is None:
141
+ from machineconfig.utils.utils import DEFAULTS_PATH
142
+ try:
143
+ section = Read.ini(DEFAULTS_PATH)['general']
144
+ self.email_config_name = section['email_config_name']
145
+ except (FileNotFoundError, KeyError, IndexError) as err: raise ValueError(f"Email config name is not provided. 🤷‍♂️ & default could not be read @ `{DEFAULTS_PATH}`") from err
146
+
147
+
148
+
149
+ if __name__ == '__main__':
150
+ pass
@@ -0,0 +1,281 @@
1
+ """RM
2
+ """
3
+
4
+ from typing import Optional, Any, Union, Callable
5
+ import time
6
+ import platform
7
+ import getpass
8
+
9
+ from crocodile.core import randstr, Struct as S
10
+ from crocodile.file_management import P, Save
11
+ from crocodile.meta import SSH
12
+ from machineconfig.cluster.session_managers import Zellij, WindowsTerminal
13
+ from machineconfig.cluster.self_ssh import SelfSSH
14
+ from machineconfig.cluster.loader_runner import EmailParams, WorkloadParams, LAUNCH_METHOD, JOB_STATUS, LogEntry, RemoteMachineConfig
15
+ from machineconfig.cluster.file_manager import FileManager
16
+ from machineconfig.cluster.cloud_manager import CloudManager
17
+ from machineconfig.cluster.job_params import JobParams
18
+ import machineconfig.cluster as cluster
19
+
20
+ from rich.panel import Panel
21
+ from rich.syntax import Syntax
22
+ from rich import inspect
23
+ # from rich.text import Text
24
+ from rich.console import Console
25
+ import pandas as pd
26
+
27
+
28
+ console = Console()
29
+
30
+
31
+ class RemoteMachine:
32
+ def __getstate__(self) -> dict[str, Any]: return self.__dict__
33
+ def __setstate__(self, state: dict[str, Any]): self.__dict__ = state
34
+ def __repr__(self): return f"Compute Machine {self.ssh.get_remote_repr(add_machine=True)}"
35
+ def __init__(self, func: Union[str, Callable[..., Any]], config: RemoteMachineConfig, func_kwargs: Optional[dict[str, Any]] = None, data: Optional[list[P]] = None):
36
+ self.config: RemoteMachineConfig = config
37
+ self.job_params: JobParams = JobParams.from_func(func=func)
38
+ if self.config.install_repo is True: assert self.job_params.is_installabe()
39
+
40
+ if self.config.workload_params is not None and func_kwargs is not None: assert "workload_params" not in func_kwargs, "workload_params provided twice, once in config and once in func_kwargs. 🤷‍♂️"
41
+ self.kwargs = func_kwargs or {}
42
+ self.data = data if data is not None else []
43
+ # conn
44
+ self.ssh = self.config.ssh_obj if self.config.ssh_obj is not None else SSH(**self.config.ssh_params) # type: ignore
45
+ # scripts
46
+ self.file_manager = FileManager(job_id=self.config.job_id, remote_machine_type=self.ssh.get_remote_machine(), base=self.config.base_dir, max_simulataneous_jobs=self.config.max_simulataneous_jobs, lock_resources=self.config.lock_resources)
47
+ # flags
48
+ # self.execution_command: Optional[str] = None
49
+ self.submitted: bool = False
50
+ self.scipts_generated: bool = False
51
+ self.results_downloaded: bool = False
52
+ self.results_path: Optional[P] = None
53
+
54
+ def get_session_manager(self): return Zellij() if self.ssh.get_remote_machine() != "Windows" else WindowsTerminal()
55
+ def fire(self, run: bool = False, open_console: bool = True, launch_method: LAUNCH_METHOD = "remotely") -> tuple[int, str]:
56
+ assert self.submitted, "Job even not submitted yet. 🤔"
57
+ console.rule(f"Firing job `{self.config.job_id}` @ remote machine {self.ssh}")
58
+ session_manager = self.get_session_manager()
59
+ ssh = self.ssh
60
+ sess_name = self.job_params.session_name
61
+ if open_console and self.config.open_console:
62
+ if isinstance(session_manager, Zellij):
63
+ sess_name = session_manager.get_current_zellij_session()
64
+ # This is a workaround that uses the same existing session and make special tab for new jobs, until zellij implements detached session capability.
65
+ # no need to assert session started, as it is already started. Plus, The lack of suffix `sess_name (current)` creates problems.
66
+ self.job_params.session_name = sess_name
67
+ Save.pickle(obj=self, path=self.file_manager.remote_machine_path.expanduser(), verbose=False)
68
+ else:
69
+ # As for Windows Terminal, there is another problem preventing us from using the same window; there is no kill-pane or kill-tab or even kill-window, the only way is to kill process (kills window).
70
+ # Thus, we can't terminate a job unless it has a window of its own. So we follow that apporach here.
71
+ session_manager.open_console(sess_name=sess_name, ssh=self.ssh)
72
+ session_manager.asssert_session_started(ssh=ssh, sess_name=sess_name)
73
+ cmd = self.file_manager.get_fire_command(launch_method=launch_method)
74
+ session_manager.setup_layout(ssh=ssh, sess_name=self.job_params.session_name, cmd=cmd, run=run, job_wd=self.file_manager.job_root.expanduser().absolute().as_posix(), tab_name=self.job_params.tab_name, compact=True).print()
75
+ if isinstance(ssh, SelfSSH):
76
+ pid_path = self.file_manager.execution_log_dir.expanduser().joinpath("pid.txt")
77
+ while True:
78
+ print(f"🧑‍💻 Waiting for Python process to start and declare its pid @ `{pid_path}` as dictated in python script ... ")
79
+ time.sleep(3)
80
+ try:
81
+ pid = int(pid_path.read_text())
82
+ import psutil
83
+ process_command = " ".join(psutil.Process(pid).cmdline())
84
+ print(f"🎉 Python process started running @ {pid=} & {process_command=}")
85
+ break
86
+ except Exception: pass
87
+ else:
88
+ pid = 0
89
+ process_command = "haha"
90
+ print("\n")
91
+ time.sleep(5) # allow time for job to write essential log files to define itself (see execution header and repo updates lines prior to py file).
92
+ return pid, process_command
93
+
94
+ def run(self, run: bool = True, open_console: bool = True, show_scripts: bool = True):
95
+ self.generate_scripts()
96
+ if show_scripts: self.show_scripts()
97
+ self.submit()
98
+ self.fire(run=run, open_console=open_console)
99
+ return self
100
+
101
+ def submit(self) -> None:
102
+ console.rule(title="🚀 Submitting job")
103
+ if type(self.ssh) is SelfSSH: pass
104
+ else:
105
+ from machineconfig.cluster.data_transfer import Submission # import here to avoid circular import.
106
+ if self.config.transfer_method == "transfer_sh": Submission.transfer_sh(rm=self)
107
+ elif self.config.transfer_method == "cloud": Submission.cloud(rm=self)
108
+ elif self.config.transfer_method == "sftp": Submission.sftp(self)
109
+ else: raise ValueError(f"Transfer method {self.config.transfer_method} not recognized. 🤷‍")
110
+ self.submitted = True # before sending `self` to the remote.
111
+
112
+ def generate_scripts(self):
113
+ console.rule(f"📝 Generating scripts for job `{self.file_manager.job_id}` @ Machine `{self.__repr__()}`")
114
+ self.job_params.ssh_repr = repr(self.ssh)
115
+ self.job_params.ssh_repr_remote = self.ssh.get_remote_repr()
116
+ self.job_params.description = self.config.description
117
+ self.job_params.file_manager_path = self.file_manager.file_manager_path.collapseuser().as_posix()
118
+ self.job_params.session_name = "TS-" + randstr(noun=True) # TS: TerminalSession-CloudManager, to distinguish from other sessions created manually.
119
+ self.job_params.tab_name = f'🏃‍♂️{self.file_manager.job_id}' # randstr(noun=True)
120
+ execution_line = self.job_params.get_execution_line(parallelize=self.config.parallelize, workload_params=self.config.workload_params, wrap_in_try_except=self.config.wrap_in_try_except)
121
+ py_script = P(cluster.__file__).parent.joinpath("script_execution.py").read_text(encoding="utf-8").replace("params = JobParams.from_empty()", f"params = {self.job_params}").replace("# execution_line", execution_line)
122
+ if self.config.notify_upon_completion:
123
+ executed_obj = f"""File *{P(self.job_params.repo_path_rh).joinpath(self.job_params.file_path_r).collapseuser().as_posix()}*""" # for email.
124
+ assert self.config.email_config_name is not None, "Email config name is not provided. 🤷‍♂️"
125
+ assert self.config.to_email is not None, "Email address is not provided. 🤷‍♂️"
126
+ email_params = EmailParams(addressee=self.ssh.get_local_repr(add_machine=True),
127
+ speaker=self.ssh.get_remote_repr(add_machine=True),
128
+ ssh_conn_str=self.ssh.get_remote_repr(add_machine=False),
129
+ executed_obj=executed_obj,
130
+ file_manager_path=self.file_manager.file_manager_path.collapseuser().as_posix(),
131
+ to_email=self.config.to_email, email_config_name=self.config.email_config_name)
132
+ email_script = P(cluster.__file__).parent.joinpath("script_notify_upon_completion.py").read_text(encoding="utf-8").replace("email_params = EmailParams.from_empty()", f"email_params = {email_params}").replace('manager = FileManager.from_pickle(params.file_manager_path)', '')
133
+ py_script = py_script.replace("# NOTIFICATION-CODE-PLACEHOLDER", email_script)
134
+ ve_path = P(self.job_params.repo_path_rh).expanduser().joinpath(".ve_path")
135
+ if ve_path.exists(): ve_name = P(ve_path.read_text()).expanduser().name
136
+ else:
137
+ import sys
138
+ ve_name = P(sys.executable).parent.parent.name
139
+ shell_script = f"""
140
+
141
+ # EXTRA-PLACEHOLDER-PRE
142
+
143
+ echo "~~~~~~~~~~~~~~~~SHELL START~~~~~~~~~~~~~~~"
144
+ {'~/scripts/devops -w update' if self.config.update_essential_repos else ''}
145
+ {f'cd {P(self.job_params.repo_path_rh).collapseuser().as_posix()}'}
146
+ . activate_ve {ve_name}
147
+ {'git pull' if self.config.update_repo else ''}
148
+ {'pip install -e .' if self.config.install_repo else ''}
149
+ echo "~~~~~~~~~~~~~~~~SHELL END ~~~~~~~~~~~~~~~"
150
+
151
+ echo ""
152
+ echo "Starting job {self.config.job_id} 🚀"
153
+ echo "Executing Python wrapper script: {self.file_manager.py_script_path.as_posix()}"
154
+
155
+ # EXTRA-PLACEHOLDER-POST
156
+
157
+ cd ~
158
+ {'python' if (not self.config.ipython and not self.config.pdb) else 'ipython'} {'-i' if self.config.interactive else ''} {'--pdb' if self.config.pdb else ''} {' -m pudb ' if self.config.pudb else ''} ./{self.file_manager.py_script_path.rel2home().as_posix()}
159
+
160
+ deactivate
161
+
162
+ """ # EVERYTHING in the script above is shell-agnostic. Ensure this is the case when adding new lines.
163
+ # shell_script_path.write_text(shell_script, encoding='utf-8', newline={"Windows": None, "Linux": "\n"}[ssh.get_remote_machine()]) # LF vs CRLF requires py3.10
164
+ with open(file=self.file_manager.shell_script_path.expanduser().create(parents_only=True), mode='w', encoding="utf-8", newline={"Windows": None, "Linux": "\n"}[self.ssh.get_remote_machine()]) as file: file.write(shell_script)
165
+ self.file_manager.py_script_path.expanduser().create(parents_only=True).write_text(py_script, encoding='utf-8') # py_version = sys.version.split(".")[1]
166
+ Save.pickle(obj=self.kwargs, path=self.file_manager.kwargs_path.expanduser(), verbose=False)
167
+ Save.pickle(obj=self.file_manager.__getstate__(), path=self.file_manager.file_manager_path.expanduser(), verbose=False)
168
+ Save.pickle(obj=self.config, path=self.file_manager.remote_machine_config_path.expanduser(), verbose=False)
169
+ Save.pickle(obj=self, path=self.file_manager.remote_machine_path.expanduser(), verbose=False)
170
+ job_status: JOB_STATUS = "queued"
171
+ self.file_manager.execution_log_dir.expanduser().create().joinpath("status.txt").write_text(job_status)
172
+ print("\n")
173
+
174
+ def show_scripts(self) -> None:
175
+ Console().print(Panel(Syntax(self.file_manager.shell_script_path.expanduser().read_text(encoding='utf-8'), lexer="ps1" if self.ssh.get_remote_machine() == "Windows" else "sh", theme="monokai", line_numbers=True), title="prepared shell script"))
176
+ Console().print(Panel(Syntax(self.file_manager.py_script_path.expanduser().read_text(encoding='utf-8'), lexer="ps1" if self.ssh.get_remote_machine() == "Windows" else "sh", theme="monokai", line_numbers=True), title="prepared python script"))
177
+ inspect(S(shell_script=repr(P(self.file_manager.shell_script_path).expanduser()), python_script=repr(P(self.file_manager.py_script_path).expanduser()), kwargs_file=repr(P(self.file_manager.kwargs_path).expanduser())), title="Prepared scripts and files.", value=False, docs=False, sort=False)
178
+
179
+ def wait_for_results(self, sleep_minutes: int = 10) -> None:
180
+ assert self.submitted, "Job even not submitted yet. 🤔"
181
+ assert not self.results_downloaded, "Job already completed. 🤔"
182
+ while True:
183
+ tmp = self.check_job_status()
184
+ if tmp is not None: break
185
+ time.sleep(60 * sleep_minutes)
186
+ self.download_results()
187
+ if self.config.notify_upon_completion: pass
188
+
189
+ def check_job_status(self) -> Optional[P]:
190
+ if not self.submitted:
191
+ print("Job even not submitted yet. 🤔")
192
+ return None
193
+ elif self.results_downloaded:
194
+ print("Job already completed. 🤔")
195
+ return None
196
+
197
+ base = self.file_manager.execution_log_dir.expanduser().create()
198
+ try: self.ssh.copy_to_here(self.file_manager.execution_log_dir.as_posix(), z=True)
199
+ except Exception: pass # type: ignore # the directory doesn't exist yet at the remote.
200
+ end_time_file = base.joinpath("end_time.txt")
201
+
202
+ if not end_time_file.exists():
203
+ start_time_file = base.joinpath("start_time.txt")
204
+ if not start_time_file.exists():
205
+ print(f"Job {self.config.job_id} is still in the queue. 😯")
206
+ else:
207
+ start_time = start_time_file.read_text()
208
+ txt = f"Machine {self.ssh.get_remote_repr(add_machine=True)} has not yet finished job `{self.config.job_id}`. 😟"
209
+ txt += f"\nIt started at {start_time}. 🕒, and is still running. 🏃‍♂️"
210
+ txt += f"\nExecution time so far: {pd.Timestamp.now() - pd.to_datetime(start_time)}. 🕒"
211
+ console.print(Panel(txt, title=f"Job `{self.config.job_id}` Status", subtitle=self.ssh.get_remote_repr(), highlight=True, border_style="bold red", style="bold"))
212
+ print("\n")
213
+ else:
214
+ results_folder_file = base.joinpath("results_folder_path.txt") # it could be one returned by function executed or one made up by the running context.
215
+ results_folder = results_folder_file.read_text()
216
+ print("\n" * 2)
217
+ console.rule("Job Completed 🎉🥳🎆🥂🍾🎊🪅")
218
+ print(f"""Machine {self.ssh.get_remote_repr(add_machine=True)} has finished job `{self.config.job_id}`. 😁
219
+ 📁 results_folder_path: {results_folder} """)
220
+ try:
221
+ inspect(base.joinpath("execution_times.Struct.pkl").readit(), value=False, title="Execution Times", docs=False, sort=False)
222
+ except Exception as err: print(f"Could not read execution times files. 🤷‍♂️, here is the error:\n {err}️")
223
+ print("\n")
224
+
225
+ self.results_path = P(results_folder)
226
+ return self.results_path
227
+ return None
228
+
229
+ def download_results(self, target: Optional[str] = None, r: bool = True, zip_first: bool = False):
230
+ assert self.results_path is not None, "Results path is unknown until job execution is finalized. 🤔\nTry checking the job status first."
231
+ if self.results_downloaded: print(f"Results already downloaded. 🤔\nSee `{self.results_path.expanduser().absolute()}`"); return
232
+ self.ssh.copy_to_here(source=self.results_path.collapseuser().as_posix(), target=target, r=r, z=zip_first)
233
+ self.results_downloaded = True
234
+ return self
235
+ def delete_remote_results(self):
236
+ if self.results_path is not None:
237
+ self.ssh.run_py(cmd=f"P(r'{self.results_path.as_posix()}').delete(sure=True)", verbose=False)
238
+ return self
239
+ else:
240
+ print("Results path is unknown until job execution is finalized. 🤔\nTry checking the job status first.")
241
+ return self
242
+
243
+ def submit_to_cloud(self, cm: CloudManager, split: int = 5, reset_cloud: bool = False) -> list['RemoteMachine']:
244
+ """The only authority responsible for adding entries to queue df."""
245
+ assert self.config.transfer_method == "cloud", "CloudManager only works with `transfer_method` set to `cloud`."
246
+ assert self.config.launch_method == "cloud_manager", "CloudManager only works with `launch_method` set to `cloud_manager`."
247
+ assert isinstance(self.ssh, SelfSSH), "CloudManager only works with `SelfSSH` objects."
248
+ assert self.config.workload_params is None, "CloudManager only works with `workload_params` set to `None`."
249
+ self.job_params.auto_commit()
250
+ if reset_cloud: cm.reset_cloud()
251
+ cm.claim_lock() # before adding any new jobs, make sure the global jobs folder is mirrored locally.
252
+ from copy import deepcopy
253
+ self.config.base_dir = CloudManager.base_path.joinpath("jobs").collapseuser().as_posix()
254
+ self.file_manager.base_dir = P(self.config.base_dir).collapseuser()
255
+ wl = WorkloadParams().split_to_jobs(jobs=split)
256
+ rms: list[RemoteMachine] = []
257
+ new_log_entries: list[LogEntry] = []
258
+ for idx, a_workload_params in enumerate(wl):
259
+ rm = deepcopy(self)
260
+ rm.config.job_id = f"{rm.config.job_id}-{idx + 1}-{split}"
261
+ if len(wl) == 1: rm.config.workload_params = None
262
+ else: rm.config.workload_params = a_workload_params
263
+ rm.file_manager.job_root = self.file_manager.base_dir.joinpath(f"{rm.config.job_id}").collapseuser()
264
+ rm.file_manager.job_id = rm.config.job_id
265
+ rm.submitted = True # must be done before generate_script which performs the pickling.
266
+ rm.generate_scripts()
267
+ rms.append(rm)
268
+ new_log_entries.append(LogEntry(name=rm.config.job_id, submission_time=pd.Timestamp.now().strftime("%Y-%m-%d %H:%M:%S"), start_time=None, end_time=None, run_machine=None,
269
+ source_machine=f"{getpass.getuser()}@{platform.node()}", note="", pid=None, cmd="", session_name=""))
270
+ log = cm.read_log() # this claims lock internally.
271
+ new_queued_df: 'pd.DataFrame' = pd.DataFrame([item.__dict__ for item in new_log_entries])
272
+ total_queued_df = pd.concat([log["queued"], new_queued_df], ignore_index=True, sort=False)
273
+ log["queued"] = total_queued_df
274
+ cm.write_log(log=log)
275
+ cm.release_lock() # all base_dir is synced anyway: self.resources.base_dir.joinpath(status_init).to_cloud(cloud=cm.cloud, rel2home=True)
276
+ return rms
277
+
278
+
279
+ if __name__ == '__main__':
280
+ # try_main()
281
+ pass
@@ -0,0 +1,209 @@
1
+ """
2
+ Job Execution Script
3
+ """
4
+
5
+ import os
6
+ import getpass
7
+ import platform
8
+
9
+ from crocodile.core import Struct as S
10
+ from crocodile.file_management import P, Read
11
+ from crocodile.meta import generate_readme, Terminal
12
+ from machineconfig.cluster.loader_runner import WorkloadParams, JOB_STATUS
13
+ from machineconfig.cluster.remote_machine import RemoteMachineConfig
14
+ from machineconfig.cluster.file_manager import FileManager
15
+ from machineconfig.cluster.job_params import JobParams
16
+
17
+ from importlib.machinery import SourceFileLoader
18
+ from rich.console import Console
19
+ from rich.panel import Panel
20
+ from rich import inspect
21
+ from rich.text import Text
22
+ import pandas as pd
23
+
24
+ console = Console()
25
+
26
+ # EXTRA-PLACEHOLDER-PRE
27
+
28
+ _ = SourceFileLoader, WorkloadParams
29
+
30
+ params = JobParams.from_empty()
31
+
32
+ print("\n" + "═" * 80 + "\n")
33
+ manager: FileManager = FileManager.from_pickle(params.file_manager_path)
34
+ manager.secure_resources()
35
+ pid: int = os.getpid()
36
+ manager.execution_log_dir.expanduser().joinpath("pid.txt").create(parents_only=True).write_text(str(pid))
37
+ job_status: JOB_STATUS = "running"
38
+ manager.execution_log_dir.expanduser().joinpath("status.txt").write_text(job_status)
39
+
40
+
41
+ # keep those values after lock is released
42
+ time_at_execution_start_utc = pd.Timestamp.utcnow()
43
+ time_at_execution_start_local = pd.Timestamp.now()
44
+ manager.execution_log_dir.expanduser().create().joinpath("start_time.txt").write_text(str(time_at_execution_start_local))
45
+ func_kwargs = Read.pickle(path=manager.kwargs_path.expanduser())
46
+
47
+ # EXTRA-PLACEHOLDER-POST
48
+
49
+
50
+ # ######################### EXECUTION ####################################
51
+
52
+ print("\n" + "═" * 80 + "\n")
53
+ console.rule(title="🚀 PYTHON EXECUTION SCRIPT - STARTING 🚀", style="bold red", characters="═")
54
+ print("\n" + "═" * 80 + "\n")
55
+
56
+ console.print(f"""
57
+ 📂 Executing File: {P(rf'{params.repo_path_rh}').expanduser().collapseuser().as_posix()}/{params.file_path_r}
58
+ 🔧 Function: {params.func_name}
59
+ ⏰ Time: {time_at_execution_start_local}
60
+ """, style="bold blue")
61
+
62
+ if isinstance(func_kwargs, dict):
63
+ S(func_kwargs).print(title="📋 Function Arguments", as_config=True)
64
+ else:
65
+ inspect(func_kwargs, value=False, title=f"📋 Function Arguments from `{manager.kwargs_path.collapseuser().as_posix()}`", docs=False, sort=False)
66
+
67
+ print("\n" + "•" * 60 + "\n")
68
+
69
+ res = ""
70
+ func = ""
71
+
72
+ # execution_line
73
+
74
+ print("\n" + "•" * 60 + "\n")
75
+ console.rule(title="✅ FINISHED PYTHON EXECUTION SCRIPT ✅", characters="═", style="bold green")
76
+ print("\n" + "═" * 80 + "\n")
77
+
78
+ # ######################### END OF EXECUTION #############################
79
+
80
+
81
+ if type(res) is P or (type(res) is str and P(res).expanduser().exists()):
82
+ res_folder = P(res).expanduser()
83
+ else:
84
+ res_folder = P.tmp(folder=rf"tmp_dirs/{manager.job_id}").create()
85
+ console.print(Panel(f"""
86
+ ⚠️ WARNING ⚠️
87
+ The executed function did not return a path to a results directory.
88
+ Execution metadata will be saved separately in:
89
+ {res_folder.collapseuser().as_posix()}
90
+ """, title="📁 Result Directory Warning", border_style="yellow"))
91
+ print("\n\n")
92
+ # try:
93
+ # Save.pickle(obj=res, path=res_folder.joinpath("result.pkl"))
94
+ # except TypeError as e:
95
+ # print(e)
96
+ # print(f"Could not pickle res object to path `{res_folder.joinpath('result.pkl').collapseuser().as_posix()}`.")
97
+
98
+ time_at_execution_end_utc = pd.Timestamp.utcnow()
99
+ time_at_execution_end_local = pd.Timestamp.now()
100
+ delta = time_at_execution_end_utc - time_at_execution_start_utc
101
+ exec_times = S({"start_utc 🌍⏲️": time_at_execution_start_utc, "end_utc 🌍⏰": time_at_execution_end_utc,
102
+ "start_local ⏲️": time_at_execution_start_local, "end_local ⏰": time_at_execution_end_local, "delta ⏳": delta,
103
+ "submission_time": manager.submission_time, "wait_time": time_at_execution_start_local - manager.submission_time})
104
+
105
+ # save the following in results folder and execution log folder.:
106
+ manager.execution_log_dir.expanduser().joinpath("end_time.txt").write_text(str(time_at_execution_end_local))
107
+ manager.execution_log_dir.expanduser().joinpath("results_folder_path.txt").write_text(res_folder.collapseuser().as_posix())
108
+ manager.execution_log_dir.expanduser().joinpath("error_message.txt").write_text(params.error_message)
109
+ exec_times.save(path=manager.execution_log_dir.expanduser().joinpath("execution_times.Struct.pkl"))
110
+ if params.error_message == "":
111
+ job_status = "completed"
112
+ manager.execution_log_dir.expanduser().joinpath("status.txt").write_text(job_status)
113
+ print(f"""
114
+ ✅ ═════════════════════ JOB COMPLETED SUCCESSFULLY ═════════════════════
115
+ 🔖 Job ID: {manager.job_id}
116
+ ⏱️ Total execution time: {delta}
117
+ 📂 Results located at: {res_folder.collapseuser().as_posix()}
118
+ ══════════════════════════════════════════════════════════════════════
119
+ """)
120
+ else:
121
+ job_status = "failed"
122
+ manager.execution_log_dir.expanduser().joinpath("status.txt").write_text(job_status)
123
+ print(f"""
124
+ ❌ ═════════════════════ JOB EXECUTION FAILED ═════════════════════
125
+ 🔖 Job ID: {manager.job_id}
126
+ ⏱️ Total execution time: {delta}
127
+ 🔍 Error message: {params.error_message}
128
+ 📂 Results located at: {res_folder.collapseuser().as_posix()}
129
+ ═════════════════════════════════════════════════════════════════
130
+ """)
131
+
132
+
133
+ generate_readme(path=manager.job_root.expanduser().joinpath("execution_log.md"), obj=func, desc=f'''
134
+
135
+ Job executed via tb.cluster.Machine
136
+ remote: {params.ssh_repr}
137
+ job_id: {manager.job_id}
138
+
139
+ py_script_path @ `{manager.py_script_path.collapseuser()}`
140
+ shell_script_path @ `{manager.shell_script_path.collapseuser()}`
141
+ kwargs_path @ `{manager.kwargs_path.collapseuser()}`
142
+
143
+ ### Execution Time:
144
+ {exec_times.print(as_config=True, return_str=True)}
145
+
146
+ ### Job description
147
+ {params.description}
148
+
149
+ ''')
150
+
151
+
152
+ # manager.root_dir.expanduser().copy(folder=res_folder, overwrite=True)
153
+
154
+ # print to execution console:
155
+ exec_times.print(title="⏱️ Execution Times", as_config=True)
156
+ print("\n" + "─" * 60 + "\n")
157
+ ssh_repr_remote = params.ssh_repr_remote or f"{getpass.getuser()}@{platform.node()}" # os.getlogin() can throw an error in non-login shells.
158
+ console.print(Panel(Text(f'''
159
+ ftprx {ssh_repr_remote} {res_folder.collapseuser()} -r
160
+ ''', style="bold blue on white"), title="📥 Pull results with this line:", border_style="bold red"))
161
+
162
+
163
+ if params.session_name != "":
164
+ if platform.system() == "Linux":
165
+ Terminal().run(f"""zellij --session {params.session_name} action new-tab --name results """)
166
+ # --layout ~/code/machineconfig/src/machineconfig/settings/zellij/layouts/d.kdl --cwd {res_folder.as_posix()}
167
+ Terminal().run(f"""zellij --session {params.session_name} action write-chars "cd {res_folder.as_posix()};lf" """)
168
+ elif platform.system() == "Windows":
169
+ Terminal().run(f"""wt --window {params.session_name} new-tab --title results -startingDirectory {res_folder.as_posix()} lf """)
170
+
171
+
172
+ # NOTIFICATION-CODE-PLACEHOLDER
173
+
174
+
175
+ manager.unlock_resources()
176
+ rm_conf: RemoteMachineConfig = Read.pickle(path=manager.remote_machine_config_path.expanduser())
177
+
178
+
179
+ if rm_conf.kill_on_completion:
180
+ # assert rm_conf.launch_method == "cloud_manager"
181
+ if platform.system() == "Linux":
182
+ from machineconfig.cluster.session_managers import Zellij # type: ignore # pylint: disable=C0412
183
+ current_session = Zellij.get_current_zellij_session()
184
+ # Zellij.close_tab(sess_name=params.session_name, tab_name=params.tab_name)
185
+ print(f"""
186
+ 🔄 ════════════════ SESSION MANAGEMENT ════════════════
187
+ 🛑 Killing session `{params.session_name}` on `{params.ssh_repr}`
188
+ ════════════════════════════════════════════════════════
189
+ """)
190
+ Terminal().run(f"zellij --session {current_session} go-to-tab-name '{params.tab_name}'; sleep 2; zellij --session {current_session} action close-tab").print() # i.e. current tab
191
+ elif platform.system() == "Windows":
192
+ print(f"""
193
+ 🔄 ════════════════ SESSION MANAGEMENT ════════════════
194
+ 🛑 Killing session `{params.session_name}` on `{params.ssh_repr}`
195
+ ════════════════════════════════════════════════════════
196
+ """)
197
+ from machineconfig.utils.procs import ProcessManager
198
+ pm = ProcessManager()
199
+ pm.kill(commands=[params.session_name])
200
+ else: raise NotImplementedError(f"❌ kill_on_completion is not implemented for platform `{platform.system()}`")
201
+ else:
202
+ print(f"""
203
+ 🔄 ════════════════ SESSION MANAGEMENT ════════════════
204
+ ✅ Keeping the tab `{params.tab_name}` on `{params.ssh_repr}`
205
+ ════════════════════════════════════════════════════════
206
+ """)
207
+
208
+
209
+ console.rule(title="🏁 END OF PYTHON EXECUTION SCRIPT 🏁", style="bold green", characters="═")