machineconfig 2.98__py3-none-any.whl → 3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of machineconfig might be problematic. Click here for more details.

Files changed (78) hide show
  1. machineconfig/__init__.py +16 -17
  2. machineconfig/cluster/sessions_managers/enhanced_command_runner.py +6 -6
  3. machineconfig/cluster/sessions_managers/wt_local.py +7 -7
  4. machineconfig/cluster/sessions_managers/wt_local_manager.py +8 -8
  5. machineconfig/cluster/sessions_managers/wt_remote.py +8 -8
  6. machineconfig/cluster/sessions_managers/wt_remote_manager.py +6 -6
  7. machineconfig/cluster/sessions_managers/zellij_local.py +156 -77
  8. machineconfig/cluster/sessions_managers/zellij_local_manager.py +18 -15
  9. machineconfig/cluster/sessions_managers/zellij_remote.py +14 -61
  10. machineconfig/cluster/sessions_managers/zellij_remote_manager.py +15 -12
  11. machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +9 -3
  12. machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +2 -2
  13. machineconfig/cluster/sessions_managers/zellij_utils/monitoring_types.py +40 -67
  14. machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +19 -17
  15. machineconfig/cluster/sessions_managers/zellij_utils/remote_executor.py +2 -2
  16. machineconfig/cluster/sessions_managers/zellij_utils/session_manager.py +1 -1
  17. machineconfig/jobs/python/python_ve_symlink.py +1 -1
  18. machineconfig/profile/create.py +7 -0
  19. machineconfig/scripts/linux/kill_process +1 -1
  20. machineconfig/scripts/python/ai/mcinit.py +2 -3
  21. machineconfig/scripts/python/cloud_mount.py +1 -1
  22. machineconfig/scripts/python/cloud_repo_sync.py +1 -0
  23. machineconfig/scripts/python/cloud_sync.py +1 -0
  24. machineconfig/scripts/python/croshell.py +1 -0
  25. machineconfig/scripts/python/devops.py +11 -0
  26. machineconfig/scripts/python/devops_add_identity.py +1 -0
  27. machineconfig/scripts/python/devops_add_ssh_key.py +1 -0
  28. machineconfig/scripts/python/devops_backup_retrieve.py +1 -0
  29. machineconfig/scripts/python/devops_devapps_install.py +4 -6
  30. machineconfig/scripts/python/devops_update_repos.py +21 -20
  31. machineconfig/scripts/python/fire_agents.py +17 -7
  32. machineconfig/scripts/python/fire_agents_help_launch.py +4 -3
  33. machineconfig/scripts/python/fire_agents_help_search.py +1 -2
  34. machineconfig/scripts/python/fire_agents_load_balancer.py +8 -10
  35. machineconfig/scripts/python/fire_jobs.py +1 -0
  36. machineconfig/scripts/python/helpers/cloud_helpers.py +1 -0
  37. machineconfig/scripts/python/mount_nfs.py +2 -0
  38. machineconfig/scripts/python/mount_nw_drive.py +1 -1
  39. machineconfig/scripts/python/mount_ssh.py +1 -0
  40. machineconfig/scripts/python/repos.py +0 -2
  41. machineconfig/scripts/python/repos_helper_record.py +43 -66
  42. machineconfig/scripts/python/repos_helper_update.py +3 -2
  43. machineconfig/scripts/python/start_slidev.py +1 -0
  44. machineconfig/scripts/python/start_terminals.py +1 -1
  45. machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +1 -0
  46. machineconfig/utils/code.py +0 -1
  47. machineconfig/utils/notifications.py +2 -2
  48. machineconfig/utils/procs.py +10 -15
  49. machineconfig/utils/schemas/fire_agents/fire_agents_input.py +9 -4
  50. machineconfig/utils/schemas/layouts/layout_types.py +2 -0
  51. machineconfig/utils/schemas/repos/repos_types.py +0 -3
  52. machineconfig/utils/ssh.py +9 -11
  53. machineconfig/utils/utils2.py +1 -0
  54. machineconfig/utils/ve.py +0 -1
  55. {machineconfig-2.98.dist-info → machineconfig-3.1.dist-info}/METADATA +1 -1
  56. {machineconfig-2.98.dist-info → machineconfig-3.1.dist-info}/RECORD +59 -78
  57. {machineconfig-2.98.dist-info → machineconfig-3.1.dist-info}/entry_points.txt +1 -0
  58. machineconfig/jobs/__pycache__/__init__.cpython-313.pyc +0 -0
  59. machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  60. machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  61. machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
  62. machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc +0 -0
  63. machineconfig/scripts/python/__pycache__/cloud_repo_sync.cpython-313.pyc +0 -0
  64. machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc +0 -0
  65. machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-313.pyc +0 -0
  66. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc +0 -0
  67. machineconfig/scripts/python/__pycache__/fire_agents.cpython-313.pyc +0 -0
  68. machineconfig/scripts/python/__pycache__/get_zellij_cmd.cpython-313.pyc +0 -0
  69. machineconfig/scripts/python/__pycache__/repos.cpython-313.pyc +0 -0
  70. machineconfig/scripts/python/__pycache__/repos_helper_record.cpython-313.pyc +0 -0
  71. machineconfig/scripts/python/__pycache__/repos_helper_update.cpython-313.pyc +0 -0
  72. machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-313.pyc +0 -0
  73. machineconfig/scripts/python/helpers/__pycache__/repo_sync_helpers.cpython-313.pyc +0 -0
  74. machineconfig/settings/linters/.ruff_cache/.gitignore +0 -2
  75. machineconfig/settings/linters/.ruff_cache/CACHEDIR.TAG +0 -1
  76. machineconfig/settings/shells/bash/.inputrc +0 -3
  77. {machineconfig-2.98.dist-info → machineconfig-3.1.dist-info}/WHEEL +0 -0
  78. {machineconfig-2.98.dist-info → machineconfig-3.1.dist-info}/top_level.txt +0 -0
@@ -6,9 +6,10 @@ Process monitoring and status checking utilities for remote commands.
6
6
  import json
7
7
  import shlex
8
8
  import logging
9
- from typing import Dict, Any
9
+ from typing import Dict
10
10
  from machineconfig.cluster.sessions_managers.zellij_utils.remote_executor import RemoteExecutor
11
11
  from machineconfig.utils.schemas.layouts.layout_types import LayoutConfig
12
+ from machineconfig.cluster.sessions_managers.zellij_utils.monitoring_types import CommandStatus, ProcessInfo
12
13
 
13
14
  logger = logging.getLogger(__name__)
14
15
 
@@ -19,7 +20,7 @@ class ProcessMonitor:
19
20
  def __init__(self, remote_executor: RemoteExecutor):
20
21
  self.remote_executor = remote_executor
21
22
 
22
- def check_command_status(self, tab_name: str, layout_config: LayoutConfig, use_verification: bool = True) -> Dict[str, Any]:
23
+ def check_command_status(self, tab_name: str, layout_config: LayoutConfig, use_verification: bool) -> CommandStatus:
23
24
  """Check command status with optional process verification."""
24
25
  # Find the tab with the given name
25
26
  tab_config = None
@@ -29,7 +30,7 @@ class ProcessMonitor:
29
30
  break
30
31
 
31
32
  if tab_config is None:
32
- return {"status": "unknown", "error": f"Tab '{tab_name}' not found in layout config", "running": False, "pid": None, "command": None, "remote": self.remote_executor.remote_name}
33
+ return {"status": "unknown", "error": f"Tab '{tab_name}' not found in layout config", "running": False, "command": "", "tab_name": tab_name, "processes": [], "remote": self.remote_executor.remote_name}
33
34
 
34
35
  # Use the verified method by default for more accurate results
35
36
  if use_verification:
@@ -37,7 +38,7 @@ class ProcessMonitor:
37
38
 
38
39
  return self._basic_process_check(tab_name, layout_config)
39
40
 
40
- def _basic_process_check(self, tab_name: str, layout_config: LayoutConfig) -> Dict[str, Any]:
41
+ def _basic_process_check(self, tab_name: str, layout_config: LayoutConfig) -> CommandStatus:
41
42
  """Basic process checking without verification."""
42
43
  # Find the tab with the given name
43
44
  tab_config = None
@@ -47,7 +48,7 @@ class ProcessMonitor:
47
48
  break
48
49
 
49
50
  if tab_config is None:
50
- return {"status": "unknown", "error": f"Tab '{tab_name}' not found in layout config", "running": False, "command": None, "remote": self.remote_executor.remote_name}
51
+ return {"status": "unknown", "error": f"Tab '{tab_name}' not found in layout config", "running": False, "command": "", "tab_name": tab_name, "processes": [], "remote": self.remote_executor.remote_name}
51
52
 
52
53
  command = tab_config["command"]
53
54
 
@@ -66,13 +67,13 @@ class ProcessMonitor:
66
67
  return {"status": "not_running", "running": False, "processes": [], "command": command, "tab_name": tab_name, "remote": self.remote_executor.remote_name}
67
68
  except json.JSONDecodeError as e:
68
69
  logger.error(f"Failed to parse remote process check output: {e}")
69
- return {"status": "error", "error": f"Failed to parse remote output: {e}", "running": False, "command": command, "tab_name": tab_name, "remote": self.remote_executor.remote_name}
70
+ return {"status": "error", "error": f"Failed to parse remote output: {e}", "running": False, "processes": [], "command": command, "tab_name": tab_name, "remote": self.remote_executor.remote_name}
70
71
  else:
71
- return {"status": "error", "error": f"Remote command failed: {result.stderr}", "running": False, "command": command, "tab_name": tab_name, "remote": self.remote_executor.remote_name}
72
+ return {"status": "error", "error": f"Remote command failed: {result.stderr}", "running": False, "processes": [], "command": command, "tab_name": tab_name, "remote": self.remote_executor.remote_name}
72
73
 
73
74
  except Exception as e:
74
75
  logger.error(f"Error checking command status for tab '{tab_name}': {e}")
75
- return {"status": "error", "error": str(e), "running": False, "command": command, "tab_name": tab_name, "remote": self.remote_executor.remote_name}
76
+ return {"status": "error", "error": str(e), "running": False, "processes": [], "command": command, "tab_name": tab_name, "remote": self.remote_executor.remote_name}
76
77
 
77
78
  def _create_process_check_script(self, command: str) -> str:
78
79
  """Create Python script for checking processes on remote machine."""
@@ -124,7 +125,7 @@ if __name__ == "__main__":
124
125
  print(json.dumps(processes))
125
126
  """
126
127
 
127
- def force_fresh_process_check(self, tab_name: str, layout_config: LayoutConfig) -> Dict[str, Any]:
128
+ def force_fresh_process_check(self, tab_name: str, layout_config: LayoutConfig) -> CommandStatus:
128
129
  """Force a fresh process check with additional validation."""
129
130
  # Find the tab with the given name
130
131
  tab_config = None
@@ -134,7 +135,7 @@ if __name__ == "__main__":
134
135
  break
135
136
 
136
137
  if tab_config is None:
137
- return {"status": "unknown", "error": f"Tab '{tab_name}' not found in layout config", "running": False, "command": None, "remote": self.remote_executor.remote_name}
138
+ return {"status": "unknown", "error": f"Tab '{tab_name}' not found in layout config", "running": False, "command": "", "tab_name": tab_name, "processes": [], "remote": self.remote_executor.remote_name}
138
139
 
139
140
  command = tab_config["command"]
140
141
 
@@ -150,7 +151,8 @@ if __name__ == "__main__":
150
151
  if result.returncode == 0:
151
152
  try:
152
153
  check_result = json.loads(result.stdout.strip())
153
- matching_processes = check_result.get("processes", [])
154
+ raw_processes = check_result.get("processes", [])
155
+ matching_processes: list[ProcessInfo] = raw_processes # runtime JSON provides shape
154
156
 
155
157
  return {
156
158
  "status": "running" if matching_processes else "not_running",
@@ -164,13 +166,13 @@ if __name__ == "__main__":
164
166
  }
165
167
  except json.JSONDecodeError as e:
166
168
  logger.error(f"Failed to parse fresh check output: {e}")
167
- return {"status": "error", "error": f"Failed to parse output: {e}", "running": False, "command": command, "tab_name": tab_name, "remote": self.remote_executor.remote_name, "raw_output": result.stdout}
169
+ return {"status": "error", "error": f"Failed to parse output: {e}", "running": False, "processes": [], "command": command, "tab_name": tab_name, "remote": self.remote_executor.remote_name, "raw_output": result.stdout}
168
170
  else:
169
- return {"status": "error", "error": f"Remote command failed: {result.stderr}", "running": False, "command": command, "tab_name": tab_name, "remote": self.remote_executor.remote_name}
171
+ return {"status": "error", "error": f"Remote command failed: {result.stderr}", "running": False, "processes": [], "command": command, "tab_name": tab_name, "remote": self.remote_executor.remote_name}
170
172
 
171
173
  except Exception as e:
172
174
  logger.error(f"Error in fresh process check for tab '{tab_name}': {e}")
173
- return {"status": "error", "error": str(e), "running": False, "command": command, "tab_name": tab_name, "remote": self.remote_executor.remote_name}
175
+ return {"status": "error", "error": str(e), "running": False, "processes": [], "command": command, "tab_name": tab_name, "remote": self.remote_executor.remote_name}
174
176
 
175
177
  def _create_fresh_check_script(self, command: str) -> str:
176
178
  """Create enhanced process checking script with freshness validation."""
@@ -255,7 +257,7 @@ if __name__ == "__main__":
255
257
  except Exception:
256
258
  return False
257
259
 
258
- def get_verified_process_status(self, tab_name: str, layout_config: LayoutConfig) -> Dict[str, Any]:
260
+ def get_verified_process_status(self, tab_name: str, layout_config: LayoutConfig) -> CommandStatus:
259
261
  """Get process status with additional verification that processes are actually alive."""
260
262
  status = self.force_fresh_process_check(tab_name, layout_config)
261
263
 
@@ -277,7 +279,7 @@ if __name__ == "__main__":
277
279
 
278
280
  return status
279
281
 
280
- def check_all_commands_status(self, layout_config: LayoutConfig) -> Dict[str, Dict[str, Any]]:
282
+ def check_all_commands_status(self, layout_config: LayoutConfig) -> Dict[str, CommandStatus]:
281
283
  """Check status of all commands in the layout configuration."""
282
284
  if not layout_config or not layout_config.get("layoutTabs"):
283
285
  logger.warning("No layout configuration provided.")
@@ -286,5 +288,5 @@ if __name__ == "__main__":
286
288
  status_report = {}
287
289
  for tab in layout_config["layoutTabs"]:
288
290
  tab_name = tab["tabName"]
289
- status_report[tab_name] = self.check_command_status(tab_name, layout_config)
291
+ status_report[tab_name] = self.check_command_status(tab_name, layout_config, True)
290
292
  return status_report
@@ -16,7 +16,7 @@ class RemoteExecutor:
16
16
  def __init__(self, remote_name: str):
17
17
  self.remote_name = remote_name
18
18
 
19
- def run_command(self, command: str, timeout: int = 30) -> subprocess.CompletedProcess[str]:
19
+ def run_command(self, command: str, timeout: int) -> subprocess.CompletedProcess[str]:
20
20
  """Execute a command on the remote machine via SSH."""
21
21
  ssh_cmd = ["ssh", self.remote_name, command]
22
22
  try:
@@ -47,7 +47,7 @@ class RemoteExecutor:
47
47
  def create_remote_directory(self, remote_dir: str) -> bool:
48
48
  """Create a directory on the remote machine."""
49
49
  try:
50
- result = self.run_command(f"mkdir -p {remote_dir}")
50
+ result = self.run_command(f"mkdir -p {remote_dir}", 30)
51
51
  return result.returncode == 0
52
52
  except Exception as e:
53
53
  logger.error(f"Failed to create remote directory {remote_dir}: {e}")
@@ -57,7 +57,7 @@ class SessionManager:
57
57
  except Exception as e:
58
58
  return {"zellij_running": False, "error": str(e), "session_name": self.session_name, "remote": self.remote_executor.remote_name}
59
59
 
60
- def start_zellij_session(self, layout_file_path: Optional[str] = None) -> Dict[str, Any]:
60
+ def start_zellij_session(self, layout_file_path: Optional[str]) -> Dict[str, Any]:
61
61
  """Start a Zellij session on the remote machine with the generated layout."""
62
62
  try:
63
63
  if layout_file_path:
@@ -22,7 +22,7 @@ def main():
22
22
  🎯 Target: {target}
23
23
  {"=" * 150}
24
24
  """)
25
- print('🔗 Finished creating symlink.')
25
+ print("🔗 Finished creating symlink.")
26
26
 
27
27
 
28
28
  if __name__ == "__main__":
@@ -3,6 +3,7 @@ This script Takes away all config files from the computer, place them in one dir
3
3
  `dotfiles`, and create symlinks to those files from thier original locations.
4
4
 
5
5
  """
6
+
6
7
  from rich.console import Console
7
8
 
8
9
  from machineconfig.utils.path_reduced import PathExtended as PathExtended
@@ -21,10 +22,16 @@ from typing import Optional, Any, TypedDict
21
22
  system = platform.system() # Linux or Windows
22
23
  ERROR_LIST: list[Any] = [] # append to this after every exception captured.
23
24
  SYSTEM = system.lower()
25
+
26
+
24
27
  def get_other_systems(current_system: str) -> list[str]:
25
28
  all_systems = ["linux", "windows", "darwin"]
26
29
  return [s for s in all_systems if s != current_system.lower()]
30
+
31
+
27
32
  OTHER_SYSTEMS = get_other_systems(SYSTEM)
33
+
34
+
28
35
  class SymlinkMapper(TypedDict):
29
36
  this: str
30
37
  to_this: str
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env bash
2
- uv run --python 3.13 --no-dev --project $HOME/code/machineconfig kill_process "$@"
2
+ uv run --python 3.13 --no-dev --project $HOME/code/machineconfig kill_process "$@"
@@ -1,4 +1,3 @@
1
-
2
1
  from pathlib import Path
3
2
  from machineconfig.utils.ve import get_repo_root
4
3
 
@@ -8,7 +7,6 @@ uv add --upgrade-package pytest --dev
8
7
  """
9
8
 
10
9
 
11
-
12
10
  def add_ai_configs(repo_root: Path) -> None:
13
11
  import machineconfig as mc
14
12
 
@@ -85,7 +83,7 @@ uv venv
85
83
  if dot_git_ignore_path.exists():
86
84
  dot_git_ignore_content = dot_git_ignore_path.read_text(encoding="utf-8")
87
85
  to_add: list[str] = []
88
- to_check_for: list[str] = [".links", "notebooks", ".ai", ".scripts", "GEMINI.md", "CLAUDE.md", ".cursor", ".github"]
86
+ to_check_for: list[str] = [".links", "notebooks", ".ai", ".scripts", "GEMINI.md", "CLAUDE.md", ".cursor", ".github/instructions", ".github/chatmodes", ".github/prompts"]
89
87
  for item in to_check_for:
90
88
  if item not in dot_git_ignore_content:
91
89
  to_add.append(item)
@@ -109,5 +107,6 @@ def main() -> None:
109
107
  repo_root = Path.cwd()
110
108
  add_ai_configs(repo_root=repo_root)
111
109
 
110
+
112
111
  if __name__ == "__main__":
113
112
  add_ai_configs(repo_root=Path.cwd())
@@ -1,6 +1,5 @@
1
1
  """Cloud mount script"""
2
2
 
3
-
4
3
  from machineconfig.utils.options import choose_one_option
5
4
  from machineconfig.utils.utils2 import read_ini
6
5
  from machineconfig.utils.path_reduced import PathExtended as PathExtended
@@ -146,6 +145,7 @@ zellij action move-focus up
146
145
  # print(f"running command: \n{txt}")
147
146
  # PROGRAM_PATH.write_text(txt, encoding="utf-8")
148
147
  import subprocess
148
+
149
149
  subprocess.run(txt, shell=True, check=True)
150
150
  # draw success box dynamically
151
151
  title1 = "✅ Cloud mount command prepared successfully"
@@ -163,6 +163,7 @@ git commit -am "finished merging"
163
163
  raise ValueError(f"Unknown action: {action}")
164
164
  # PROGRAM_PATH.write_text(program_content, encoding="utf-8")
165
165
  import subprocess
166
+
166
167
  subprocess.run(program_content, shell=True, check=True)
167
168
 
168
169
  return program_content
@@ -77,6 +77,7 @@ def args_parser():
77
77
 
78
78
  # PROGRAM_PATH.write_text(txt, encoding="utf-8")
79
79
  import subprocess
80
+
80
81
  subprocess.run(txt, shell=True, check=True)
81
82
 
82
83
 
@@ -209,6 +209,7 @@ print(f"🐊 Crocodile Shell | Running @ {Path.cwd()}")
209
209
  # PROGRAM_PATH.write_text(data=final_program, encoding="utf-8")
210
210
  # (PROGRAM_PATH + ".py").write_text(str(pyfile), encoding='utf-8')
211
211
  import subprocess
212
+
212
213
  subprocess.run(final_program, shell=True, check=True)
213
214
 
214
215
  # if platform.system() == "Windows":
@@ -31,6 +31,7 @@ class Options(Enum):
31
31
  def args_parser():
32
32
  console.print(Panel("🛠️ DevOps Tool Suite", title_align="left", border_style="blue", width=BOX_WIDTH))
33
33
  import argparse
34
+
34
35
  parser = argparse.ArgumentParser()
35
36
  new_line = "\n\n"
36
37
  parser.add_argument("-w", "--which", help=f"""which option to run\nChoose one of those:\n{new_line.join([f"{item.name}: {item.value}" for item in list(Options)])}""", type=str, default=None) # , choices=[op.value for op in Options]
@@ -56,26 +57,31 @@ def main(which: Optional[str] = None):
56
57
  if choice_key == Options.update.value:
57
58
  console.print(Panel("🔄 Updating essential repositories...", width=BOX_WIDTH, border_style="blue"))
58
59
  import machineconfig.scripts.python.devops_update_repos as helper
60
+
59
61
  helper.main()
60
62
  elif choice_key == Options.cli_install.value:
61
63
  console.print(Panel("⚙️ Installing development applications...", width=BOX_WIDTH, border_style="blue"))
62
64
  import machineconfig.scripts.python.devops_devapps_install as helper
65
+
63
66
  helper.main()
64
67
 
65
68
  elif choice_key == Options.sym_new.value:
66
69
  console.print(Panel("🔄 Creating new symlinks...", width=BOX_WIDTH, border_style="blue"))
67
70
  import machineconfig.jobs.python.python_ve_symlink as helper
71
+
68
72
  helper.main()
69
73
 
70
74
  elif choice_key == Options.sym_path_shell.value:
71
75
  console.print(Panel("🔗 Setting up symlinks, PATH, and shell profile...", width=BOX_WIDTH, border_style="blue"))
72
76
  import machineconfig.profile.create as helper
77
+
73
78
  helper.main()
74
79
  "echo '✅ done with symlinks'"
75
80
 
76
81
  elif choice_key == Options.ssh_add_pubkey.value:
77
82
  console.print(Panel("🔑 Adding public SSH key to this machine...", width=BOX_WIDTH, border_style="blue"))
78
83
  import machineconfig.scripts.python.devops_add_ssh_key as helper
84
+
79
85
  helper.main()
80
86
 
81
87
  elif choice_key == Options.ssh_use_pair.value:
@@ -85,6 +91,7 @@ def main(which: Optional[str] = None):
85
91
  elif choice_key == Options.ssh_add_id.value: # so that you can SSH directly withuot pointing to identity key.
86
92
  console.print(Panel("🗝️ Adding SSH identity (private key) to this machine...", width=BOX_WIDTH, border_style="blue"))
87
93
  import machineconfig.scripts.python.devops_add_identity as helper
94
+
88
95
  helper.main()
89
96
 
90
97
  elif choice_key == Options.ssh_setup.value:
@@ -93,6 +100,7 @@ def main(which: Optional[str] = None):
93
100
  _program_linux = """curl https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/setup_linux/openssh_all.sh | sudo bash # https://github.com/thisismygitrepo.keys"""
94
101
  _program_linux if system() == "Linux" else _program_windows
95
102
  import subprocess
103
+
96
104
  subprocess.run(_program_linux if system() == "Linux" else _program_windows, shell=True, check=True)
97
105
 
98
106
  elif choice_key == Options.ssh_setup_wsl.value:
@@ -102,11 +110,13 @@ def main(which: Optional[str] = None):
102
110
  elif choice_key == Options.backup.value:
103
111
  console.print(Panel("💾 Creating backup...", width=BOX_WIDTH, border_style="blue"))
104
112
  from machineconfig.scripts.python.devops_backup_retrieve import main_backup_retrieve
113
+
105
114
  main_backup_retrieve(direction="BACKUP")
106
115
 
107
116
  elif choice_key == Options.retreive.value:
108
117
  console.print(Panel("📥 Retrieving backup...", width=BOX_WIDTH, border_style="blue"))
109
118
  from machineconfig.scripts.python.devops_backup_retrieve import main_backup_retrieve
119
+
110
120
  main_backup_retrieve(direction="RETRIEVE")
111
121
 
112
122
  elif choice_key == Options.scheduler.value:
@@ -118,5 +128,6 @@ def main(which: Optional[str] = None):
118
128
  console.print(Panel("❌ ERROR: Invalid choice", title_align="left", border_style="red", width=BOX_WIDTH))
119
129
  raise ValueError(f"Unimplemented choice: {choice_key}")
120
130
 
131
+
121
132
  if __name__ == "__main__":
122
133
  args_parser()
@@ -78,6 +78,7 @@ def main() -> None:
78
78
  print(Panel(Text(success_message, justify="center"), expand=False, border_style="green"))
79
79
 
80
80
  import subprocess
81
+
81
82
  # run program
82
83
  subprocess.run(program, shell=True, check=True, text=True)
83
84
  print(Panel("🔐 Identity added to SSH agent", expand=False, border_style="green"))
@@ -113,6 +113,7 @@ def main() -> None:
113
113
 
114
114
  # return program
115
115
  import subprocess
116
+
116
117
  subprocess.run(program, shell=True, check=True)
117
118
  console.print(Panel("✅ SSH KEY AUTHORIZATION COMPLETED", box=box.DOUBLE_EDGE, title_align="left"))
118
119
 
@@ -74,6 +74,7 @@ def main_backup_retrieve(direction: OPTIONS, which: Optional[str] = None) -> Non
74
74
  print_code(program, lexer="shell", desc=f"{direction} script")
75
75
  console.print(Panel(f"✅ {direction} SCRIPT GENERATION COMPLETE\n🚀 Ready to execute the operations", title="[bold green]Script Generation Complete[/bold green]", border_style="green"))
76
76
  import subprocess
77
+
77
78
  subprocess.run(program, shell=True, check=True)
78
79
 
79
80
 
@@ -39,18 +39,15 @@ def main(which: Optional[WHICH_CAT | str] = None) -> None:
39
39
 
40
40
  # interactive installation
41
41
  installers = [Installer.from_dict(d=vd, name=name) for __kat, vds in get_all_dicts(system=system()).items() for name, vd in vds.items()]
42
-
42
+
43
43
  # Check installed programs with progress indicator
44
- with Progress(
45
- SpinnerColumn(),
46
- TextColumn("[progress.description]{task.description}"),
47
- ) as progress:
44
+ with Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}")) as progress:
48
45
  task = progress.add_task("✅ Checking installed programs...", total=len(installers))
49
46
  options = []
50
47
  for x in installers:
51
48
  options.append(x.get_description())
52
49
  progress.update(task, advance=1)
53
-
50
+
54
51
  options += list(get_args(WHICH_CAT))
55
52
  # print("s"*1000)
56
53
  program_names = choose_multiple_options(msg="", options=options, header="🚀 CHOOSE DEV APP", default="AllEssentials")
@@ -67,6 +64,7 @@ def main(which: Optional[WHICH_CAT | str] = None) -> None:
67
64
  an_installer = installers[options.index(a_program_name)]
68
65
  total_program += "\n" + an_installer.install_robust(version=None) # finish the task
69
66
  import subprocess
67
+
70
68
  subprocess.run(total_program, shell=True, check=True)
71
69
 
72
70
 
@@ -13,20 +13,20 @@ def _display_summary(results: list[RepositoryUpdateResult]) -> None:
13
13
  print("\n" + "=" * 80)
14
14
  print("📊 REPOSITORY UPDATE SUMMARY")
15
15
  print("=" * 80)
16
-
16
+
17
17
  # Calculate statistics
18
18
  total_repos = len(results)
19
19
  successful_repos = sum(1 for r in results if r["status"] == "success")
20
20
  error_repos = sum(1 for r in results if r["status"] == "error")
21
21
  skipped_repos = sum(1 for r in results if r["status"] == "skipped")
22
22
  auth_failed_repos = sum(1 for r in results if r["status"] == "auth_failed")
23
-
23
+
24
24
  repos_with_changes = sum(1 for r in results if r["commits_changed"])
25
25
  repos_with_uncommitted = sum(1 for r in results if r["had_uncommitted_changes"])
26
26
  repos_with_dep_changes = sum(1 for r in results if r["dependencies_changed"])
27
27
  uv_sync_runs = sum(1 for r in results if r["uv_sync_ran"])
28
28
  uv_sync_successes = sum(1 for r in results if r["uv_sync_ran"] and r["uv_sync_success"])
29
-
29
+
30
30
  # Overview statistics
31
31
  print("📈 OVERVIEW:")
32
32
  print(f" Total repositories processed: {total_repos}")
@@ -36,59 +36,59 @@ def _display_summary(results: list[RepositoryUpdateResult]) -> None:
36
36
  if auth_failed_repos > 0:
37
37
  print(f" 🔐 Authentication failed: {auth_failed_repos}")
38
38
  print()
39
-
39
+
40
40
  print("🔄 CHANGES:")
41
41
  print(f" Repositories with new commits: {repos_with_changes}")
42
42
  print(f" Repositories with dependency changes: {repos_with_dep_changes}")
43
43
  print(f" Repositories with uncommitted changes: {repos_with_uncommitted}")
44
44
  print()
45
-
45
+
46
46
  print("📦 UV SYNC:")
47
47
  print(f" uv sync operations attempted: {uv_sync_runs}")
48
48
  print(f" uv sync operations successful: {uv_sync_successes}")
49
49
  if uv_sync_runs > uv_sync_successes:
50
50
  print(f" uv sync operations failed: {uv_sync_runs - uv_sync_successes}")
51
51
  print()
52
-
52
+
53
53
  # Detailed results per repository
54
54
  print("📋 DETAILED RESULTS:")
55
55
  for result in results:
56
56
  repo_name = Path(result["repo_path"]).name
57
57
  status_icon = {"success": "✅", "error": "❌", "skipped": "⏭️", "auth_failed": "🔐"}.get(result["status"], "❓")
58
58
  print(f" {status_icon} {repo_name}")
59
-
59
+
60
60
  if result["status"] == "error" and result["error_message"]:
61
61
  print(f" 💥 Error: {result['error_message']}")
62
-
62
+
63
63
  if result["commits_changed"]:
64
64
  print(f" 🔄 Updated: {result['commit_before'][:8]} → {result['commit_after'][:8]}")
65
65
  elif result["status"] == "success":
66
66
  print(" 📍 Already up to date")
67
-
67
+
68
68
  if result["had_uncommitted_changes"]:
69
69
  files_str = ", ".join(result["uncommitted_files"])
70
70
  print(f" ⚠️ Had uncommitted changes: {files_str}")
71
-
71
+
72
72
  if result["dependencies_changed"]:
73
73
  changes = []
74
74
  if result["pyproject_changed"]:
75
75
  changes.append("pyproject.toml")
76
76
  print(f" 📋 Dependencies changed: {', '.join(changes)}")
77
-
77
+
78
78
  if result["uv_sync_ran"]:
79
79
  sync_status = "✅" if result["uv_sync_success"] else "❌"
80
80
  print(f" 📦 uv sync: {sync_status}")
81
-
81
+
82
82
  if result["is_machineconfig_repo"] and result["permissions_updated"]:
83
83
  print(" 🛠 Updated permissions for machineconfig files")
84
-
84
+
85
85
  if result["remotes_processed"]:
86
86
  print(f" 📡 Processed remotes: {', '.join(result['remotes_processed'])}")
87
87
  if result["remotes_skipped"]:
88
88
  print(f" ⏭️ Skipped remotes: {', '.join(result['remotes_skipped'])}")
89
-
89
+
90
90
  print("\n" + "=" * 80)
91
-
91
+
92
92
  # Final status
93
93
  if error_repos == 0 and auth_failed_repos == 0:
94
94
  print("🎉 All repositories processed successfully!")
@@ -131,18 +131,18 @@ def main(verbose: bool = True, allow_password_prompt: bool = False) -> None:
131
131
  # Process repositories
132
132
  results: list[RepositoryUpdateResult] = []
133
133
  repos_with_changes = []
134
-
134
+
135
135
  for expanded_path in repos:
136
136
  try:
137
137
  repo = git.Repo(str(expanded_path), search_parent_directories=True)
138
138
  # Update repository and get detailed results
139
139
  result = update_repository(repo, allow_password_prompt=allow_password_prompt, auto_sync=True)
140
140
  results.append(result)
141
-
141
+
142
142
  # Keep track of repos with dependency changes for additional uv sync
143
143
  if result["dependencies_changed"] and not result["uv_sync_ran"]:
144
144
  repos_with_changes.append(Path(repo.working_dir))
145
-
145
+
146
146
  except Exception as ex:
147
147
  # Create a result for failed repos
148
148
  error_result: RepositoryUpdateResult = {
@@ -167,13 +167,14 @@ def main(verbose: bool = True, allow_password_prompt: bool = False) -> None:
167
167
  print(f"""❌ Repository Error: Path: {expanded_path}
168
168
  Exception: {ex}
169
169
  {"-" * 50}""")
170
-
170
+
171
171
  # Run uv sync for repositories where pyproject.toml changed but sync wasn't run yet
172
172
  for repo_path in repos_with_changes:
173
173
  run_uv_sync(repo_path)
174
-
174
+
175
175
  # Generate and display summary
176
176
  _display_summary(results)
177
177
 
178
+
178
179
  if __name__ == "__main__":
179
180
  main()
@@ -16,7 +16,7 @@ from machineconfig.scripts.python.fire_agents_help_launch import prep_agent_laun
16
16
  from machineconfig.scripts.python.fire_agents_help_search import search_files_by_pattern, search_python_files
17
17
  from machineconfig.scripts.python.fire_agents_load_balancer import chunk_prompts, SPLITTING_STRATEGY, DEFAULT_AGENT_CAP
18
18
  from machineconfig.utils.options import choose_one_option
19
- from machineconfig.utils.schemas.layouts.layout_types import LayoutsFile
19
+ from machineconfig.utils.schemas.layouts.layout_types import TabConfig, LayoutConfig
20
20
  from machineconfig.utils.ve import get_repo_root
21
21
 
22
22
  SEARCH_STRATEGIES: TypeAlias = Literal["file_path", "keyword_search", "filename_pattern"]
@@ -138,25 +138,35 @@ manager.run_monitoring_routine()
138
138
  """
139
139
  (agents_dir / "aa_agents_relaunch.py").write_text(data=regenerate_py_code, encoding="utf-8")
140
140
  (agents_dir / "layout.json").write_text(data=json.dumps(layoutfile, indent=2), encoding="utf-8")
141
- if len(layoutfile["layouts"][0]["layoutTabs"]) > 25:
142
- print("Too many agents (>25) to launch. Skipping launch.")
141
+
142
+ MAX_TABS = 10
143
+ if len(layoutfile["layouts"][0]["layoutTabs"]) > MAX_TABS:
144
+ print(f"Too many tabs (>{MAX_TABS}) to launch. Skipping launch.")
143
145
  sys.exit(0)
144
146
  from machineconfig.cluster.sessions_managers.zellij_local_manager import ZellijLocalManager
147
+
145
148
  manager = ZellijLocalManager(session_layouts=layoutfile["layouts"])
146
149
  manager.start_all_sessions()
147
150
  manager.run_monitoring_routine()
148
151
 
149
152
 
150
- def launch_sequentially(layoutfile: "LayoutsFile"):
153
+ def split_too_many_tabs_to_run_in_sequential_sessions(layout_tabs: list[TabConfig], every: int):
154
+ from machineconfig.utils.utils2 import split
155
+ from machineconfig.cluster.sessions_managers.zellij_local_manager import ZellijLocalManager
156
+ for idx, layout_tabs_chunk in enumerate(split(layout_tabs, every=every)):
157
+ a_layout_file: LayoutConfig = {"layoutName": f"split_{idx}", "layoutTabs": layout_tabs_chunk}
158
+ manager = ZellijLocalManager(session_layouts=[a_layout_file])
159
+ manager.start_all_sessions()
160
+ manager.run_monitoring_routine()
161
+ manager.kill_all_sessions()
162
+ def split_too_many_layouts_to_run_in_sequential_sessions(layouts: list[LayoutConfig], every: int):
151
163
  from machineconfig.utils.utils2 import split
152
164
  from machineconfig.cluster.sessions_managers.zellij_local_manager import ZellijLocalManager
153
- for layout_chunk in split(layoutfile["layouts"], every=3):
165
+ for _idx, layout_chunk in enumerate(split(layouts, every=every)):
154
166
  manager = ZellijLocalManager(session_layouts=layout_chunk)
155
167
  manager.start_all_sessions()
156
168
  manager.run_monitoring_routine()
157
169
 
158
170
 
159
-
160
-
161
171
  if __name__ == "__main__": # pragma: no cover
162
172
  main()
@@ -1,4 +1,3 @@
1
-
2
1
  from machineconfig.utils.utils2 import randstr
3
2
 
4
3
  import random
@@ -16,6 +15,7 @@ AGENT_NAME_FORMATTER = "agent_{idx}_cmd.sh" # e.g., agent_0_cmd.sh
16
15
 
17
16
  def get_gemini_api_keys() -> list[str]:
18
17
  from machineconfig.utils.utils2 import read_ini
18
+
19
19
  config = read_ini(Path.home().joinpath("dotfiles/creds/llm/gemini/api_keys.ini"))
20
20
  res: list[str] = []
21
21
  for a_section_name in list(config.sections()):
@@ -27,7 +27,8 @@ def get_gemini_api_keys() -> list[str]:
27
27
  print(f"Found {len(res)} Gemini API keys configured.")
28
28
  return res
29
29
 
30
- def prep_agent_launch(repo_root: Path, prompts_material: list[str], prompt_prefix: str, keep_material_in_separate_file: bool, agent: AGENTS, *, job_name: str) -> Path:
30
+
31
+ def prep_agent_launch(repo_root: Path, prompts_material: list[str], prompt_prefix: str, keep_material_in_separate_file: bool, agent: AGENTS, *, job_name: str) -> Path:
31
32
  session_root = repo_root / ".ai" / f"tmp_prompts/{job_name}_{randstr()}"
32
33
  session_root.mkdir(parents=True, exist_ok=True)
33
34
  prompt_folder = session_root / "prompts"
@@ -115,7 +116,6 @@ echo "---------END OF AGENT OUTPUT---------"
115
116
  """
116
117
  agent_cmd_launch_path.write_text(cmd_prefix + cmd + cmd_postfix, encoding="utf-8")
117
118
 
118
-
119
119
  # print(f"Launching a template with #{len(tab_config)} agents")
120
120
  if len(all_materials_scripts) > 0:
121
121
  all_materials_list_path = session_root / "all_materials_redistributed.txt"
@@ -126,6 +126,7 @@ echo "---------END OF AGENT OUTPUT---------"
126
126
 
127
127
  def get_agents_launch_layout(session_root: Path):
128
128
  from machineconfig.utils.schemas.layouts.layout_types import TabConfig, LayoutConfig, LayoutsFile
129
+
129
130
  tab_config: list[TabConfig] = []
130
131
  prompt_root = session_root / "prompts"
131
132
  all_dirs_under_prompts = [d for d in prompt_root.iterdir() if d.is_dir()]
@@ -1,4 +1,3 @@
1
-
2
1
  from machineconfig.utils.source_of_truth import EXCLUDE_DIRS
3
2
  import fnmatch
4
3
  from pathlib import Path
@@ -79,4 +78,4 @@ def search_python_files(repo_root: Path, keyword: str) -> list[Path]:
79
78
  return
80
79
 
81
80
  _walk_and_filter(repo_root)
82
- return matches
81
+ return matches