machineconfig 1.94__py3-none-any.whl → 1.95__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 (120) hide show
  1. machineconfig/cluster/data_transfer.py +2 -1
  2. machineconfig/cluster/job_params.py +1 -1
  3. machineconfig/cluster/script_execution.py +1 -1
  4. machineconfig/jobs/__pycache__/__init__.cpython-311.pyc +0 -0
  5. machineconfig/jobs/linux/msc/lid.sh +2 -4
  6. machineconfig/jobs/linux/msc/network.sh +3 -6
  7. machineconfig/jobs/python/check_installations.py +6 -6
  8. machineconfig/jobs/python/checkout_version.py +4 -4
  9. machineconfig/jobs/python/python_cargo_build_share.py +2 -2
  10. machineconfig/jobs/python/python_ve_symlink.py +4 -4
  11. machineconfig/jobs/python/vscode/api.py +2 -2
  12. machineconfig/jobs/python/vscode/link_ve.py +4 -4
  13. machineconfig/jobs/python/vscode/select_interpreter.py +4 -4
  14. machineconfig/jobs/python/vscode/sync_code.py +6 -6
  15. machineconfig/jobs/python_custom_installers/archive/ngrok.py +4 -4
  16. machineconfig/jobs/python_custom_installers/dev/aider.py +4 -4
  17. machineconfig/jobs/python_custom_installers/dev/alacritty.py +4 -4
  18. machineconfig/jobs/python_custom_installers/dev/brave.py +4 -4
  19. machineconfig/jobs/python_custom_installers/dev/bypass_paywall.py +4 -4
  20. machineconfig/jobs/python_custom_installers/dev/code.py +4 -4
  21. machineconfig/jobs/python_custom_installers/dev/docker.py +4 -4
  22. machineconfig/jobs/python_custom_installers/dev/docker_desktop.py +4 -4
  23. machineconfig/jobs/python_custom_installers/dev/espanso.py +8 -8
  24. machineconfig/jobs/python_custom_installers/dev/goes.py +4 -4
  25. machineconfig/jobs/python_custom_installers/dev/lvim.py +4 -4
  26. machineconfig/jobs/python_custom_installers/dev/nerdfont.py +4 -4
  27. machineconfig/jobs/python_custom_installers/dev/redis.py +4 -4
  28. machineconfig/jobs/python_custom_installers/dev/warp-cli.py +4 -4
  29. machineconfig/jobs/python_custom_installers/dev/wezterm.py +4 -4
  30. machineconfig/jobs/python_custom_installers/gh.py +6 -6
  31. machineconfig/jobs/python_custom_installers/hx.py +28 -58
  32. machineconfig/jobs/python_custom_installers/scripts/linux/brave.sh +4 -8
  33. machineconfig/jobs/python_custom_installers/scripts/linux/docker.sh +5 -10
  34. machineconfig/jobs/python_custom_installers/scripts/linux/docker_start.sh +3 -6
  35. machineconfig/jobs/python_custom_installers/scripts/linux/edge.sh +3 -6
  36. machineconfig/jobs/python_custom_installers/scripts/linux/nerdfont.sh +5 -10
  37. machineconfig/jobs/python_custom_installers/scripts/linux/pgsql.sh +4 -8
  38. machineconfig/jobs/python_custom_installers/scripts/linux/redis.sh +5 -10
  39. machineconfig/jobs/python_custom_installers/scripts/linux/timescaledb.sh +6 -12
  40. machineconfig/jobs/python_custom_installers/scripts/linux/vscode.sh +9 -8
  41. machineconfig/jobs/python_custom_installers/scripts/linux/warp-cli.sh +5 -10
  42. machineconfig/jobs/python_custom_installers/scripts/linux/wezterm.sh +3 -6
  43. machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  44. machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  45. machineconfig/profile/shell.py +26 -47
  46. machineconfig/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  47. machineconfig/scripts/cloud/init.sh +9 -18
  48. machineconfig/scripts/linux/fire +5 -24
  49. machineconfig/scripts/linux/share_cloud.sh +6 -12
  50. machineconfig/scripts/python/__pycache__/__init__.cpython-311.pyc +0 -0
  51. machineconfig/scripts/python/__pycache__/cloud_copy.cpython-311.pyc +0 -0
  52. machineconfig/scripts/python/__pycache__/cloud_mount.cpython-311.pyc +0 -0
  53. machineconfig/scripts/python/__pycache__/cloud_repo_sync.cpython-311.pyc +0 -0
  54. machineconfig/scripts/python/__pycache__/cloud_sync.cpython-311.pyc +0 -0
  55. machineconfig/scripts/python/__pycache__/croshell.cpython-311.pyc +0 -0
  56. machineconfig/scripts/python/__pycache__/devops.cpython-311.pyc +0 -0
  57. machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-311.pyc +0 -0
  58. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-311.pyc +0 -0
  59. machineconfig/scripts/python/__pycache__/fire_jobs.cpython-311.pyc +0 -0
  60. machineconfig/scripts/python/__pycache__/get_zellij_cmd.cpython-311.pyc +0 -0
  61. machineconfig/scripts/python/__pycache__/repos.cpython-311.pyc +0 -0
  62. machineconfig/scripts/python/archive/im2text.py +30 -30
  63. machineconfig/scripts/python/archive/tmate_conn.py +10 -13
  64. machineconfig/scripts/python/archive/tmate_start.py +12 -16
  65. machineconfig/scripts/python/choose_wezterm_theme.py +9 -18
  66. machineconfig/scripts/python/cloud_copy.py +38 -93
  67. machineconfig/scripts/python/cloud_manager.py +61 -53
  68. machineconfig/scripts/python/cloud_mount.py +23 -34
  69. machineconfig/scripts/python/cloud_repo_sync.py +20 -69
  70. machineconfig/scripts/python/cloud_sync.py +35 -45
  71. machineconfig/scripts/python/croshell.py +48 -73
  72. machineconfig/scripts/python/devops.py +50 -104
  73. machineconfig/scripts/python/devops_add_identity.py +41 -101
  74. machineconfig/scripts/python/devops_add_ssh_key.py +33 -140
  75. machineconfig/scripts/python/devops_backup_retrieve.py +23 -112
  76. machineconfig/scripts/python/devops_devapps_install.py +0 -4
  77. machineconfig/scripts/python/devops_update_repos.py +1 -1
  78. machineconfig/scripts/python/fire_jobs.py +73 -25
  79. machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-311.pyc +0 -0
  80. machineconfig/scripts/python/helpers/__pycache__/cloud_helpers.cpython-311.pyc +0 -0
  81. machineconfig/scripts/python/helpers/__pycache__/helpers2.cpython-311.pyc +0 -0
  82. machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-311.pyc +0 -0
  83. machineconfig/scripts/python/helpers/__pycache__/repo_sync_helpers.cpython-311.pyc +0 -0
  84. machineconfig/scripts/python/helpers/cloud_helpers.py +37 -34
  85. machineconfig/scripts/python/helpers/helpers2.py +17 -31
  86. machineconfig/scripts/python/helpers/repo_sync_helpers.py +19 -54
  87. machineconfig/scripts/python/pomodoro.py +1 -1
  88. machineconfig/scripts/python/repos.py +49 -34
  89. machineconfig/scripts/python/wifi_conn.py +5 -3
  90. machineconfig/scripts/windows/fire.ps1 +27 -15
  91. machineconfig/settings/__pycache__/__init__.cpython-311.pyc +0 -0
  92. machineconfig/settings/shells/ipy/profiles/default/__pycache__/__init__.cpython-311.pyc +0 -0
  93. machineconfig/settings/shells/ipy/profiles/default/startup/__pycache__/__init__.cpython-311.pyc +0 -0
  94. machineconfig/settings/shells/ipy/profiles/default/startup/__pycache__/playext.cpython-311.pyc +0 -0
  95. machineconfig/setup_linux/nix/cli_installation.sh +9 -18
  96. machineconfig/setup_linux/others/openssh-server_add_pub_key.sh +3 -6
  97. machineconfig/setup_linux/web_shortcuts/all.sh +5 -10
  98. machineconfig/setup_linux/web_shortcuts/ascii_art.sh +7 -14
  99. machineconfig/setup_linux/web_shortcuts/croshell.sh +6 -12
  100. machineconfig/setup_linux/web_shortcuts/interactive.sh +34 -68
  101. machineconfig/setup_linux/web_shortcuts/ssh.sh +8 -16
  102. machineconfig/setup_linux/web_shortcuts/update_system.sh +7 -14
  103. machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +16 -12
  104. machineconfig/utils/ai/browser_user_wrapper.py +60 -45
  105. machineconfig/utils/ai/generate_file_checklist.py +4 -7
  106. machineconfig/utils/ai/url2md.py +13 -5
  107. machineconfig/utils/{utils_code.py → code.py} +4 -10
  108. machineconfig/utils/installer.py +4 -10
  109. machineconfig/utils/{utils_links.py → links.py} +9 -20
  110. machineconfig/utils/{utils_options.py → options.py} +10 -20
  111. machineconfig/utils/{utils_path.py → path.py} +28 -80
  112. machineconfig/utils/procs.py +26 -30
  113. machineconfig/utils/scheduling.py +11 -11
  114. machineconfig/utils/utils.py +12 -19
  115. machineconfig/utils/ve.py +5 -21
  116. machineconfig/utils/ve_utils/ve2.py +15 -2
  117. {machineconfig-1.94.dist-info → machineconfig-1.95.dist-info}/METADATA +4 -2
  118. {machineconfig-1.94.dist-info → machineconfig-1.95.dist-info}/RECORD +120 -118
  119. {machineconfig-1.94.dist-info → machineconfig-1.95.dist-info}/WHEEL +1 -1
  120. {machineconfig-1.94.dist-info → machineconfig-1.95.dist-info}/top_level.txt +0 -0
@@ -1,51 +1,66 @@
1
- """
2
- playwright install
3
- sudo nala install libavif13
4
- """
5
-
6
- import os
7
- os.environ["ANONYMIZED_TELEMETRY"] = "false"
8
-
9
- from langchain_ollama import ChatOllama
10
- from browser_use import Agent
11
- import asyncio
12
-
13
-
14
- # Create agent with the model
15
- async def main():
16
- print(f"""
17
- ╔{'═' * 70}╗
18
- 🌐 Browser Automation Agent
19
- ╚{'═' * 70}╝
20
- """)
21
-
22
- print("🔄 Initializing LLM model (llama3.1:8b)...")
23
- llm = ChatOllama(model="llama3.1:8b")
24
- print("✅ LLM model initialized")
1
+ # """
2
+ # playwright install
3
+ # sudo nala install libavif13
4
+ # """
5
+
6
+ # import os
7
+ # os.environ["ANONYMIZED_TELEMETRY"] = "false"
8
+
9
+ # from langchain_ollama import ChatOllama
10
+ # from browser_use import Agent
11
+ # import asyncio
12
+ # from rich.panel import Panel
13
+ # from rich import print as rprint
14
+
15
+ # BOX_WIDTH = 150 # width for box drawing
16
+
17
+
18
+ # def _get_padding(text: str, padding_before: int = 2, padding_after: int = 1) -> str:
19
+ # """Calculate the padding needed to align the box correctly.
25
20
 
26
- print(f"""
27
- ╭{'─' * 70}╮
28
- 🤖 Task: Open https://chat.openai.com/ and ask how many r's in │
29
- │ rrraaararewey, use Thinking Button and type the answer │
30
- ╰{'─' * 70}╯
31
- """)
21
+ # Args:
22
+ # text: The text to pad
23
+ # padding_before: The space taken before the text (usually "║ ")
24
+ # padding_after: The space needed after the text (usually " ║")
32
25
 
33
- print("🚀 Creating and launching browser agent...")
34
- agent = Agent(
35
- task="open https://chat.openai.com/ and ask how many r's in rrraaararewey, use Thinking Button and type the answer",
36
- llm=llm
37
- )
38
-
39
- print("🏃‍♂️ Running agent task...")
40
- await agent.run()
26
+ # Returns:
27
+ # A string of spaces for padding
28
+ # """
29
+ # # Count visible characters (might not be perfect for all Unicode characters)
30
+ # text_length = len(text)
31
+ # padding_length = BOX_WIDTH - padding_before - text_length - padding_after
32
+ # return ' ' * max(0, padding_length)
33
+
34
+
35
+ # # Create agent with the model
36
+ # async def main():
37
+ # # header for browser automation agent
38
+ # title = "🌐 Browser Automation Agent"
39
+ # rprint(Panel(title, title="Status", width=BOX_WIDTH))
40
+
41
+ # rprint("🔄 Initializing LLM model (llama3.1:8b)...")
42
+ # llm = ChatOllama(model="llama3.1:8b")
43
+ # rprint("✅ LLM model initialized")
44
+
45
+ # task_line1 = "🤖 Task: Open https://chat.openai.com/ and ask how many r's in"
46
+ # task_line2 = "rrraaararewey, use Thinking Button and type the answer"
47
+ # task_content = f"{task_line1}\n{task_line2}"
48
+ # rprint(Panel(task_content, title="Task", width=BOX_WIDTH))
49
+
50
+ # rprint("🚀 Creating and launching browser agent...")
51
+ # agent = Agent(
52
+ # task="open https://chat.openai.com/ and ask how many r's in rrraaararewey, use Thinking Button and type the answer",
53
+ # llm=llm
54
+ # )
55
+
56
+ # rprint("🏃‍♂️ Running agent task...")
57
+ # await agent.run()
41
58
 
42
- print(f"""
43
- ╔{'═' * 70}╗
44
- Browser automation task completed
45
- ╚{'═' * 70}╝
46
- """)
59
+ # # footer success box
60
+ # title = "✅ Browser automation task completed"
61
+ # rprint(Panel(title, title="Status", width=BOX_WIDTH))
47
62
 
48
63
 
49
- if __name__ == "__main__":
50
- asyncio.run(main())
64
+ # if __name__ == "__main__":
65
+ # asyncio.run(main())
51
66
 
@@ -2,6 +2,8 @@
2
2
 
3
3
  from pathlib import Path
4
4
  from typing import List, Optional, Union
5
+ from rich.console import Console
6
+ from rich.panel import Panel
5
7
 
6
8
 
7
9
  def generate_file_checklist(repo_root: Union[str, Path], exclude_dirs: Optional[List[str]] = None) -> Path:
@@ -9,7 +11,6 @@ def generate_file_checklist(repo_root: Union[str, Path], exclude_dirs: Optional[
9
11
  actual_exclude_dirs: List[str] = ['.venv', '.git', '__pycache__', 'build', 'dist', '*.egg-info']
10
12
  if exclude_dirs is not None:
11
13
  actual_exclude_dirs = exclude_dirs
12
-
13
14
  repo_root = Path(repo_root).expanduser().absolute()
14
15
  output_path: Path = repo_root / ".ai" / "repo_task" / "file_checklist.md"
15
16
  py_files: List[Path] = []
@@ -62,12 +63,8 @@ def main() -> None:
62
63
  return
63
64
 
64
65
  output_path = generate_file_checklist(args.repo, exclude_dirs)
65
- print(f"""
66
- {'=' * 60}
67
- ✅ SUCCESS | Markdown checklist generated successfully!
68
- 📄 File Location: {output_path}
69
- {'=' * 60}
70
- """)
66
+ console = Console()
67
+ console.print(Panel(f"✅ SUCCESS | Markdown checklist generated successfully!\n📄 File Location: {output_path}", border_style="bold blue", expand=False))
71
68
 
72
69
 
73
70
  if __name__ == "__main__":
@@ -18,17 +18,25 @@ depth = input("🔍 Enter the crawl depth (default: 4): ") or "4"
18
18
 
19
19
  website_name_as_valid_filename = url.split("//")[-1].split("/")[0].replace(".", "_").replace(":", "_")
20
20
  domain = url.split("//")[-1].split("/")[0]
21
- op_dir = cwd.joinpath(".website", website_name_as_valid_filename)
21
+ op_dir = cwd.joinpath(".ai/website", website_name_as_valid_filename)
22
22
  op_dir.mkdir(exist_ok=True, parents=True)
23
23
  urls_file = op_dir.joinpath("urls", "urls.txt")
24
24
  urls_file.parent.mkdir(exist_ok=True, parents=True)
25
- if urls_file.exists():
26
- urls_file.unlink()
25
+ # if urls_file.exists():
26
+ # urls_file.unlink()
27
27
 
28
28
  print("🌐 Crawling the website to extract URLs...")
29
29
  command = f"""xcrawl3r --url {url} --domain {domain} --depth {depth} --concurrency 20 --parallelism 4 --output {urls_file} """
30
- print(f"Running command: {command}")
31
- subprocess.run(command, shell=True, check=True)
30
+
31
+ try:
32
+ print(f"Running command: {command}")
33
+ subprocess.run(command, shell=True, check=True)
34
+ except KeyboardInterrupt:
35
+ if urls_file.exists():
36
+ print("\n❌ Process interrupted by user, proceeding with existing URLs...")
37
+ else:
38
+ print("\n❌ Process interrupted by user, no URLs found.")
39
+ exit(1)
32
40
 
33
41
  all_urls = urls_file.read_text().splitlines()
34
42
  relevant_urls = list(set(all_urls)) # remove duplicates
@@ -46,13 +46,7 @@ def write_shell_script_to_default_program_path(program: str, desc: str, preserve
46
46
  program = "$orig_path = $pwd\n" + program + "\ncd $orig_path"
47
47
  else:
48
48
  program = 'orig_path=$(cd -- "." && pwd)\n' + program + '\ncd "$orig_path" || exit'
49
- if display:
50
- print(f"""
51
- {'=' * 60}
52
- ⚙️ SHELL SCRIPT | Executing at {PROGRAM_PATH}
53
- {'=' * 60}
54
- """)
55
- print_code(code=program, lexer="shell", desc=desc)
49
+ if display: print_code(code=program, lexer="shell", desc=desc, subtitle=str(PROGRAM_PATH))
56
50
  PROGRAM_PATH.create(parents_only=True).write_text(program)
57
51
  if execute:
58
52
  Terminal().run(f". {PROGRAM_PATH}", shell="powershell").capture().print_if_unsuccessful(desc="🛠️ EXECUTION | Shell script running", strict_err=True, strict_returncode=True)
@@ -65,18 +59,18 @@ code = r'''{python_script}'''
65
59
  try:
66
60
  from machineconfig.utils.utils import print_code
67
61
  print_code(code=code, lexer="python", desc="Python Script")
68
- except ImportError: print(f"\\n{'=' * 60}\\n📜 PYTHON SCRIPT:\\n\\n{{code}}\\n{'=' * 60}\\n")
62
+ except ImportError: console.print(Panel(f"📜 PYTHON SCRIPT:\n\n{python_script}", title="Python Script", expand=False))
69
63
  """ + python_script
70
64
  python_file = P.tmp().joinpath("tmp_scripts", "python", randstr() + ".py").create(parents_only=True).write_text(python_script)
71
65
  shell_script = get_shell_script_executing_python_file(python_file=python_file.to_str(), ve_name=ve_name)
72
66
  shell_file = write_shell_script_to_file(shell_script)
73
67
  return shell_file
74
68
 
75
- def print_code(code: str, lexer: str, desc: str):
69
+ def print_code(code: str, lexer: str, desc: str, subtitle: str=""):
76
70
  if lexer == "shell":
77
71
  if platform.system() == "Windows": lexer = "powershell"
78
72
  elif platform.system() == "Linux": lexer = "sh"
79
73
  else: raise NotImplementedError(f"Platform {platform.system()} not supported for lexer {lexer}")
80
74
  console = Console()
81
- console.print(Panel(Syntax(code=code, lexer=lexer), title=f"📄 {desc}"), style="bold red")
75
+ console.print(Panel(Syntax(code=code, lexer=lexer), title=f"📄 {desc}", subtitle=subtitle), style="bold red")
82
76
 
@@ -3,6 +3,7 @@
3
3
  from machineconfig.utils.installer_utils.installer_abc import LINUX_INSTALL_PATH, CATEGORY
4
4
  from machineconfig.utils.installer_utils.installer_class import Installer
5
5
  from rich.console import Console
6
+ from rich.panel import Panel # Added import
6
7
 
7
8
  from crocodile.file_management import P, Read
8
9
  from crocodile.core import List as L
@@ -15,11 +16,8 @@ import platform
15
16
 
16
17
 
17
18
  def check_latest():
18
- print(f"""
19
- ╔{'═'*78}╗
20
- ║ 🔍 CHECKING FOR LATEST VERSIONS ║
21
- ╚{'═'*78}╝
22
- """)
19
+ console = Console() # Added console initialization
20
+ console.print(Panel("🔍 CHECKING FOR LATEST VERSIONS", title="Status", expand=False)) # Replaced print with Panel
23
21
  installers = get_installers(system=platform.system(), dev=False)
24
22
  # installers += get_installers(system=platform.system(), dev=True)
25
23
  installers_gitshub = []
@@ -49,11 +47,7 @@ def check_latest():
49
47
 
50
48
  from crocodile.core import Display
51
49
  Display.set_pandas_display()
52
- print(f"""
53
- ╔{'═'*78}╗
54
- ║ 📊 INSTALLATION STATUS SUMMARY ║
55
- ╚{'═'*78}╝
56
- """)
50
+ console.print(Panel("📊 INSTALLATION STATUS SUMMARY", title="Status", expand=False)) # Replaced print with Panel
57
51
  print(res_df)
58
52
  print(f"{'═'*80}")
59
53
 
@@ -1,5 +1,10 @@
1
1
  from crocodile.file_management import P, PLike
2
2
  from crocodile.core_modules.core_1 import randstr
3
+ from rich.console import Console
4
+ from rich.panel import Panel
5
+
6
+
7
+ console = Console()
3
8
 
4
9
 
5
10
  def build_links(target_paths: list[tuple[PLike, str]], repo_root: PLike):
@@ -46,18 +51,10 @@ def symlink_func(this: P, to_this: P, prioritize_to_this: bool=True):
46
51
  else: # this doesn't exist.
47
52
  if not to_this.exists(): to_this.touch() # we have to touch it (file) or create it (folder)
48
53
  try:
49
- print(f"""
50
- {'=' * 60}
51
- 🔗 LINKING | Creating symlink from {this} ➡️ {to_this}
52
- {'=' * 60}
53
- """)
54
+ console.print(Panel(f"🔗 LINKING | Creating symlink from {this} ➡️ {to_this}", title="Linking", expand=False))
54
55
  P(this).symlink_to(target=to_this, verbose=True, overwrite=True)
55
56
  except Exception as ex:
56
- print(f"""
57
- ❌ ERROR | Failed at linking {this} ➡️ {to_this}.
58
- Reason: {ex}
59
- {'=' * 60}
60
- """)
57
+ console.print(Panel(f"❌ ERROR | Failed at linking {this} ➡️ {to_this}. Reason: {ex}", title="Error", expand=False))
61
58
 
62
59
  def symlink_copy(this: P, to_this: P, prioritize_to_this: bool=True):
63
60
  this = P(this).expanduser().absolute()
@@ -74,15 +71,7 @@ def symlink_copy(this: P, to_this: P, prioritize_to_this: bool=True):
74
71
  else: # this doesn't exist.
75
72
  if not to_this.exists(): to_this.touch() # we have to touch it (file) or create it (folder)
76
73
  try:
77
- print(f"""
78
- {'=' * 60}
79
- 📋 COPYING | Copying {to_this} to {this}
80
- {'=' * 60}
81
- """)
74
+ console.print(Panel(f"📋 COPYING | Copying {to_this} to {this}", title="Copying", expand=False))
82
75
  to_this.copy(path=this, overwrite=True, verbose=True)
83
76
  except Exception as ex:
84
- print(f"""
85
- ❌ ERROR | Failed at copying {to_this} to {this}.
86
- Reason: {ex}
87
- {'=' * 60}
88
- """)
77
+ console.print(Panel(f"❌ ERROR | Failed at copying {to_this} to {this}. Reason: {ex}", title="Error", expand=False))
@@ -15,7 +15,6 @@ T = TypeVar("T")
15
15
 
16
16
 
17
17
  def check_tool_exists(tool_name: str, install_script: Optional[str] = None) -> bool:
18
- """This is the CLI equivalent of `install_n_import` function of crocodile. """
19
18
  if platform.system() == "Windows":
20
19
  tool_name = tool_name.replace(".exe", "") + ".exe"
21
20
 
@@ -29,11 +28,8 @@ def check_tool_exists(tool_name: str, install_script: Optional[str] = None) -> b
29
28
  except (subprocess.CalledProcessError, FileNotFoundError):
30
29
  res = False
31
30
  if res is False and install_script is not None:
32
- print(f"""
33
- {'=' * 60}
34
- 📥 INSTALLING TOOL | Installing {tool_name}...
35
- {'=' * 60}
36
- """)
31
+ console = Console()
32
+ console.print(Panel(f"📥 INSTALLING TOOL | Installing {tool_name}...", border_style="bold blue", expand=False))
37
33
  Terminal().run(install_script, shell="powershell").print()
38
34
  return check_tool_exists(tool_name=tool_name, install_script=None)
39
35
  return res
@@ -63,6 +59,7 @@ def display_options(msg: str, options: Iterable[T], header: str="", tail: str=""
63
59
  tool_name = "fzf"
64
60
  options_strings: list[str] = [str(x) for x in options]
65
61
  default_string = str(default) if default is not None else None
62
+ console = Console()
66
63
  if fzf and check_tool_exists(tool_name):
67
64
  from pyfzf.pyfzf import FzfPrompt
68
65
  fzf_prompt = FzfPrompt()
@@ -81,7 +78,6 @@ def display_options(msg: str, options: Iterable[T], header: str="", tail: str=""
81
78
  choice_idx_s = [options_strings.index(x) for x in choice_string_multi]
82
79
  return [list(options)[x] for x in choice_idx_s]
83
80
  else:
84
- console = Console()
85
81
  if default is not None:
86
82
  assert default in options, f"Default `{default}` option not in options `{list(options)}`"
87
83
  default_msg = Text(" <<<<-------- DEFAULT", style="bold red")
@@ -99,7 +95,7 @@ def display_options(msg: str, options: Iterable[T], header: str="", tail: str=""
99
95
 
100
96
  if choice_string == "":
101
97
  if default_string is None:
102
- print("🧨 Default option not available!")
98
+ console.print(Panel("🧨 Default option not available!", title="Error", expand=False))
103
99
  return display_options(msg=msg, options=options, header=header, tail=tail, prompt=prompt, default=default, fzf=fzf, multi=multi, custom_input=custom_input)
104
100
  choice_idx = options_strings.index(default_string)
105
101
  assert default is not None, "🧨 Default option not available!"
@@ -116,7 +112,7 @@ def display_options(msg: str, options: Iterable[T], header: str="", tail: str=""
116
112
  else:
117
113
  _ = ie
118
114
  # raise ValueError(f"Unknown choice. {choice_string}") from ie
119
- print(f"❓ Unknown choice: '{choice_string}'")
115
+ console.print(Panel(f"❓ Unknown choice: '{choice_string}'", title="Error", expand=False))
120
116
  return display_options(msg=msg, options=options, header=header, tail=tail, prompt=prompt, default=default, fzf=fzf, multi=multi, custom_input=custom_input)
121
117
  except TypeError as te: # int(choice_string) failed due to # either the number is invalid, or the input is custom.
122
118
  if choice_string in options_strings: # string input
@@ -127,19 +123,16 @@ def display_options(msg: str, options: Iterable[T], header: str="", tail: str=""
127
123
  else:
128
124
  _ = te
129
125
  # raise ValueError(f"Unknown choice. {choice_string}") from te
130
- print(f"❓ Unknown choice: '{choice_string}'")
126
+ console.print(Panel(f"❓ Unknown choice: '{choice_string}'", title="Error", expand=False))
131
127
  return display_options(msg=msg, options=options, header=header, tail=tail, prompt=prompt, default=default, fzf=fzf, multi=multi, custom_input=custom_input)
132
- print(f"✅ Selected option {choice_idx}: {choice_one}")
128
+ console.print(Panel(f"✅ Selected option {choice_idx}: {choice_one}", title="Selected", expand=False))
133
129
  if multi: return [choice_one]
134
130
  return choice_one
135
131
 
136
132
 
137
133
  def choose_cloud_interactively() -> str:
138
- print(f"""
139
- {'=' * 60}
140
- 🔍 LISTING CLOUD REMOTES | Fetching available cloud remotes...
141
- {'=' * 60}
142
- """)
134
+ console = Console()
135
+ console.print(Panel("🔍 LISTING CLOUD REMOTES | Fetching available cloud remotes...", border_style="bold blue", expand=False))
143
136
  tmp = Terminal().run("rclone listremotes").op_if_successfull_or_default(strict_returcode=False)
144
137
  # consider this: remotes = Read.ini(P.home().joinpath(".config/rclone/rclone.conf")).sections()
145
138
  if isinstance(tmp, str):
@@ -149,10 +142,7 @@ def choose_cloud_interactively() -> str:
149
142
  if len(remotes) == 0:
150
143
  raise RuntimeError("You don't have remotes. Configure your rclone first to get cloud services access.")
151
144
  cloud: str = choose_one_option(msg="WHICH CLOUD?", options=list(remotes), default=remotes[0], fzf=True)
152
- print(f"""
153
- ✅ SELECTED CLOUD | {cloud}
154
- {'=' * 60}
155
- """)
145
+ console.print(Panel(f"✅ SELECTED CLOUD | {cloud}", border_style="bold blue", expand=False))
156
146
  return cloud
157
147
 
158
148
  def get_ssh_hosts() -> list[str]:
@@ -1,11 +1,14 @@
1
1
  from crocodile.core import List as L
2
2
  from crocodile.file_management import P
3
+ from machineconfig.utils.options import check_tool_exists, choose_one_option
4
+ from rich.console import Console
5
+ from rich.panel import Panel
3
6
  import platform
4
7
  import subprocess
5
8
  from typing import Optional, TypeVar
6
- from machineconfig.utils.utils_options import check_tool_exists, choose_one_option
7
9
 
8
10
  T = TypeVar("T")
11
+ console = Console()
9
12
 
10
13
  def sanitize_path(a_path: P) -> P:
11
14
  path = P(a_path)
@@ -13,71 +16,48 @@ def sanitize_path(a_path: P) -> P:
13
16
  if platform.system() == "Windows": # path copied from Linux to Windows
14
17
  path = P.home().joinpath(*path.parts[3:]) # exclude /home/username
15
18
  assert path.exists(), f"File not found: {path}"
16
- print(f"""
17
- {'=' * 60}
18
- 🔗 PATH MAPPING | Linux → Windows: `{a_path}` ➡️ `{path}`
19
- {'=' * 60}
20
- """)
19
+ console.print(Panel(f"🔗 PATH MAPPING | Linux → Windows: `{a_path}` ➡️ `{path}`", title="Path Mapping", expand=False))
21
20
  elif platform.system() == "Linux" and P.home().as_posix() not in path.as_posix(): # copied from Linux to Linux with different username
22
21
  path = P.home().joinpath(*path.parts[3:]) # exclude /home/username (three parts: /, home, username)
23
22
  assert path.exists(), f"File not found: {path}"
24
- print(f"""
25
- {'=' * 60}
26
- 🔗 PATH MAPPING | Linux → Linux: `{a_path}` ➡️ `{path}`
27
- {'=' * 60}
28
- """)
23
+ console.print(Panel(f"🔗 PATH MAPPING | Linux → Linux: `{a_path}` ➡️ `{path}`", title="Path Mapping", expand=False))
29
24
  elif path.as_posix().startswith("C:"):
30
25
  if platform.system() == "Linux": # path copied from Windows to Linux
31
- xx = str(a_path).replace("\\", "/")
32
- path = P.home().joinpath(*P(xx).parts[3:]) # exclude C:\Users\username
26
+ xx = str(a_path).replace("\\\\", "/")
27
+ path = P.home().joinpath(*P(xx).parts[3:]) # exclude C:\\Users\\username
33
28
  assert path.exists(), f"File not found: {path}"
34
- print(f"""
35
- {'=' * 60}
36
- 🔗 PATH MAPPING | Windows → Linux: `{a_path}` ➡️ `{path}`
37
- {'=' * 60}
38
- """)
29
+ console.print(Panel(f"🔗 PATH MAPPING | Windows → Linux: `{a_path}` ➡️ `{path}`", title="Path Mapping", expand=False))
39
30
  elif platform.system() == "Windows" and P.home().as_posix() not in path.as_posix(): # copied from Windows to Windows with different username
40
31
  path = P.home().joinpath(*path.parts[2:])
41
32
  assert path.exists(), f"File not found: {path}"
42
- print(f"""
43
- {'=' * 60}
44
- 🔗 PATH MAPPING | Windows → Windows: `{a_path}` ➡️ `{path}`
45
- {'=' * 60}
46
- """)
33
+ console.print(Panel(f"🔗 PATH MAPPING | Windows → Windows: `{a_path}` ➡️ `{path}`", title="Path Mapping", expand=False))
47
34
  return path.expanduser().absolute()
48
35
 
49
36
  def match_file_name(sub_string: str, search_root: Optional[P] = None) -> P:
50
37
  """Look up current directory for file name that matches the passed substring."""
51
38
  search_root_obj = search_root if search_root is not None else P.cwd()
52
39
  search_root_obj = search_root_obj.absolute()
53
- print(f"""
54
- {'=' * 60}
55
- 🔍 SEARCH | Looking for '{sub_string}' in {search_root_obj}
56
- {'=' * 60}
57
- """)
40
+ console.print(Panel(f"🔍 SEARCH | Looking for '{sub_string}' in {search_root_obj}", title="Search", expand=False))
58
41
 
59
42
  search_root_objects = search_root_obj.search("*", not_in=["links", ".venv", ".git", ".idea", ".vscode", "node_modules", "__pycache__"])
60
- search_results: L[P] = L([a_search_root_obj.search(f"*{sub_string}*", r=True) for a_search_root_obj in search_root_objects]).reduce(lambda x, y: x + y) # type: ignore
43
+ search_results: L[P] = L([a_search_root_obj.search(f"*{sub_string}*", r=True) for a_search_root_obj in search_root_objects])
44
+ if len(search_results) > 0:
45
+ search_results = search_results.reduce(lambda x, y: x + y) # type: ignore
46
+ else:
47
+ pass
61
48
  search_results = search_results.filter(lambda x: x.suffix in (".py", ".sh", ".ps1"))
62
-
63
49
  if len(search_results) == 1:
64
50
  path_obj = search_results.list[0]
65
51
  elif len(search_results) > 1:
66
52
  msg = "Search results are ambiguous or non-existent, choose manually:"
67
- print(f"""
68
- ⚠️ WARNING | {msg}
69
- {'=' * 60}
70
- """)
53
+ console.print(Panel(f"⚠️ WARNING | {msg}", title="Warning", expand=False))
71
54
  choice = choose_one_option(msg=msg, options=search_results.list, fzf=True)
72
55
  path_obj = P(choice)
73
56
  else:
74
57
  # let's do a final retry with sub_string.small()
75
58
  sub_string_small = sub_string.lower()
76
59
  if sub_string_small != sub_string:
77
- print(f"""
78
- 🔄 RETRY | Searching with lowercase letters
79
- {'=' * 60}
80
- """)
60
+ console.print(Panel("🔄 RETRY | Searching with lowercase letters", title="Retry", expand=False))
81
61
  return match_file_name(sub_string=sub_string_small)
82
62
  from git.repo import Repo
83
63
  from git.exc import InvalidGitRepositoryError
@@ -85,10 +65,7 @@ def match_file_name(sub_string: str, search_root: Optional[P] = None) -> P:
85
65
  repo = Repo(search_root_obj, search_parent_directories=True)
86
66
  repo_root_dir = P(repo.working_dir)
87
67
  if repo_root_dir != search_root_obj: # may be user is in a subdirectory of the repo root, try with root dir.
88
- print(f"""
89
- 🔄 RETRY | Searching from repository root instead of current directory
90
- {'=' * 60}
91
- """)
68
+ console.print(Panel("🔄 RETRY | Searching from repository root instead of current directory", title="Retry", expand=False))
92
69
  return match_file_name(sub_string=sub_string, search_root=repo_root_dir)
93
70
  else:
94
71
  search_root_obj = repo_root_dir
@@ -97,55 +74,26 @@ def match_file_name(sub_string: str, search_root: Optional[P] = None) -> P:
97
74
 
98
75
  if check_tool_exists(tool_name="fzf"):
99
76
  try:
100
- print(f"""
101
- 🔍 SEARCH STRATEGY | Using fd to search for '{sub_string}' in '{search_root_obj}' ...
102
- {'=' * 60}
103
- """)
77
+ console.print(Panel(f"🔍 SEARCH STRATEGY | Using fd to search for '{sub_string}' in '{search_root_obj}' ...", title="Search Strategy", expand=False))
104
78
  fzf_cmd = f"cd '{search_root_obj}'; fd --type f --strip-cwd-prefix | fzf --filter={sub_string}"
105
- search_res = subprocess.run(fzf_cmd, stdout=subprocess.PIPE, text=True, check=True, shell=True).stdout.split("\n")[:-1]
79
+ search_res = subprocess.run(fzf_cmd, stdout=subprocess.PIPE, text=True, check=True, shell=True).stdout.split("\\n")[:-1]
106
80
  except subprocess.CalledProcessError as cpe:
107
- print(f"""
108
- ERROR | FZF search failed with '{sub_string}' in '{search_root_obj}'.
109
- {cpe}
110
- {'=' * 60}
111
- """)
112
- msg = f"""
113
- {'=' * 60}
114
- 💥 FILE NOT FOUND | Path {sub_string} does not exist. No search results
115
- {'=' * 60}
116
- """
81
+ console.print(Panel(f"❌ ERROR | FZF search failed with '{sub_string}' in '{search_root_obj}'.\\n{cpe}", title="Error", expand=False))
82
+ msg = Panel(f"💥 FILE NOT FOUND | Path {sub_string} does not exist. No search results", title="File Not Found", expand=False)
117
83
  raise FileNotFoundError(msg) from cpe
118
84
 
119
85
  if len(search_res) == 1: return search_root_obj.joinpath(search_res[0])
120
86
  else:
121
- print(f"""
122
- 🔍 SEARCH STRATEGY | Trying with raw fzf search ...
123
- {'=' * 60}
124
- """)
87
+ console.print(Panel("🔍 SEARCH STRATEGY | Trying with raw fzf search ...", title="Search Strategy", expand=False))
125
88
  try:
126
89
  res = subprocess.run(f"cd '{search_root_obj}'; fd | fzf --query={sub_string}", check=True, stdout=subprocess.PIPE, text=True, shell=True).stdout.strip()
127
90
  except subprocess.CalledProcessError as cpe:
128
- print(f"""
129
- ERROR | FZF search failed with '{sub_string}' in '{search_root_obj}'. {cpe}
130
- {'=' * 60}
131
- """)
132
- msg = f"""
133
- {'=' * 60}
134
- 💥 FILE NOT FOUND | Path {sub_string} does not exist. No search results
135
- {'=' * 60}
136
- """
91
+ console.print(Panel(f"❌ ERROR | FZF search failed with '{sub_string}' in '{search_root_obj}'. {cpe}", title="Error", expand=False))
92
+ msg = Panel(f"💥 FILE NOT FOUND | Path {sub_string} does not exist. No search results", title="File Not Found", expand=False)
137
93
  raise FileNotFoundError(msg) from cpe
138
94
  return search_root_obj.joinpath(res)
139
95
 
140
- msg = f"""
141
- {'=' * 60}
142
- 💥 FILE NOT FOUND | Path {sub_string} does not exist. No search results
143
- {'=' * 60}
144
- """
96
+ msg = Panel(f"💥 FILE NOT FOUND | Path {sub_string} does not exist. No search results", title="File Not Found", expand=False)
145
97
  raise FileNotFoundError(msg)
146
- print(f"""
147
- {'=' * 60}
148
- ✅ MATCH FOUND | `{sub_string}` ➡️ `{path_obj}`
149
- {'=' * 60}
150
- """)
98
+ console.print(Panel(f"✅ MATCH FOUND | `{sub_string}` ➡️ `{path_obj}`", title="Match Found", expand=False))
151
99
  return path_obj