machineconfig 1.96__py3-none-any.whl → 2.0__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 (164) hide show
  1. machineconfig/cluster/cloud_manager.py +22 -26
  2. machineconfig/cluster/data_transfer.py +2 -2
  3. machineconfig/cluster/distribute.py +0 -2
  4. machineconfig/cluster/file_manager.py +4 -4
  5. machineconfig/cluster/job_params.py +1 -1
  6. machineconfig/cluster/loader_runner.py +8 -8
  7. machineconfig/cluster/remote_machine.py +4 -4
  8. machineconfig/cluster/script_execution.py +2 -2
  9. machineconfig/cluster/sessions_managers/archive/create_zellij_template.py +1 -1
  10. machineconfig/cluster/sessions_managers/enhanced_command_runner.py +23 -23
  11. machineconfig/cluster/sessions_managers/wt_local.py +78 -76
  12. machineconfig/cluster/sessions_managers/wt_local_manager.py +91 -91
  13. machineconfig/cluster/sessions_managers/wt_remote.py +39 -39
  14. machineconfig/cluster/sessions_managers/wt_remote_manager.py +94 -91
  15. machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +56 -54
  16. machineconfig/cluster/sessions_managers/wt_utils/process_monitor.py +49 -49
  17. machineconfig/cluster/sessions_managers/wt_utils/remote_executor.py +18 -18
  18. machineconfig/cluster/sessions_managers/wt_utils/session_manager.py +42 -42
  19. machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +36 -36
  20. machineconfig/cluster/sessions_managers/zellij_local.py +43 -46
  21. machineconfig/cluster/sessions_managers/zellij_local_manager.py +139 -120
  22. machineconfig/cluster/sessions_managers/zellij_remote.py +35 -35
  23. machineconfig/cluster/sessions_managers/zellij_remote_manager.py +33 -33
  24. machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +15 -15
  25. machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +25 -26
  26. machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +49 -49
  27. machineconfig/cluster/sessions_managers/zellij_utils/remote_executor.py +5 -5
  28. machineconfig/cluster/sessions_managers/zellij_utils/session_manager.py +15 -15
  29. machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +11 -11
  30. machineconfig/cluster/templates/utils.py +3 -3
  31. machineconfig/jobs/__pycache__/__init__.cpython-311.pyc +0 -0
  32. machineconfig/jobs/python/__pycache__/__init__.cpython-311.pyc +0 -0
  33. machineconfig/jobs/python/__pycache__/python_ve_symlink.cpython-311.pyc +0 -0
  34. machineconfig/jobs/python/check_installations.py +8 -9
  35. machineconfig/jobs/python/python_cargo_build_share.py +2 -2
  36. machineconfig/jobs/python/vscode/link_ve.py +7 -7
  37. machineconfig/jobs/python/vscode/select_interpreter.py +7 -7
  38. machineconfig/jobs/python/vscode/sync_code.py +5 -5
  39. machineconfig/jobs/python_custom_installers/archive/ngrok.py +2 -2
  40. machineconfig/jobs/python_custom_installers/dev/aider.py +3 -3
  41. machineconfig/jobs/python_custom_installers/dev/alacritty.py +3 -3
  42. machineconfig/jobs/python_custom_installers/dev/brave.py +3 -3
  43. machineconfig/jobs/python_custom_installers/dev/bypass_paywall.py +5 -5
  44. machineconfig/jobs/python_custom_installers/dev/code.py +3 -3
  45. machineconfig/jobs/python_custom_installers/dev/cursor.py +9 -9
  46. machineconfig/jobs/python_custom_installers/dev/docker_desktop.py +4 -4
  47. machineconfig/jobs/python_custom_installers/dev/espanso.py +4 -4
  48. machineconfig/jobs/python_custom_installers/dev/goes.py +4 -4
  49. machineconfig/jobs/python_custom_installers/dev/lvim.py +4 -4
  50. machineconfig/jobs/python_custom_installers/dev/nerdfont.py +3 -3
  51. machineconfig/jobs/python_custom_installers/dev/redis.py +3 -3
  52. machineconfig/jobs/python_custom_installers/dev/wezterm.py +3 -3
  53. machineconfig/jobs/python_custom_installers/dev/winget.py +27 -27
  54. machineconfig/jobs/python_custom_installers/docker.py +3 -3
  55. machineconfig/jobs/python_custom_installers/gh.py +7 -7
  56. machineconfig/jobs/python_custom_installers/hx.py +1 -1
  57. machineconfig/jobs/python_custom_installers/warp-cli.py +3 -3
  58. machineconfig/jobs/python_generic_installers/config.json +412 -389
  59. machineconfig/jobs/python_windows_installers/dev/config.json +1 -1
  60. machineconfig/logger.py +50 -0
  61. machineconfig/profile/__pycache__/__init__.cpython-311.pyc +0 -0
  62. machineconfig/profile/__pycache__/create.cpython-311.pyc +0 -0
  63. machineconfig/profile/__pycache__/shell.cpython-311.pyc +0 -0
  64. machineconfig/profile/create.py +23 -16
  65. machineconfig/profile/create_hardlinks.py +8 -8
  66. machineconfig/profile/shell.py +41 -37
  67. machineconfig/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  68. machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
  69. machineconfig/scripts/linux/devops +2 -2
  70. machineconfig/scripts/linux/fire +1 -0
  71. machineconfig/scripts/linux/fire_agents +0 -1
  72. machineconfig/scripts/linux/mcinit +27 -0
  73. machineconfig/scripts/python/__pycache__/__init__.cpython-311.pyc +0 -0
  74. machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc +0 -0
  75. machineconfig/scripts/python/__pycache__/croshell.cpython-311.pyc +0 -0
  76. machineconfig/scripts/python/__pycache__/devops.cpython-311.pyc +0 -0
  77. machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc +0 -0
  78. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-311.pyc +0 -0
  79. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc +0 -0
  80. machineconfig/scripts/python/__pycache__/fire_agents.cpython-311.pyc +0 -0
  81. machineconfig/scripts/python/__pycache__/fire_jobs.cpython-311.pyc +0 -0
  82. machineconfig/scripts/python/__pycache__/repos.cpython-311.pyc +0 -0
  83. machineconfig/scripts/python/ai/__pycache__/init.cpython-311.pyc +0 -0
  84. machineconfig/scripts/python/ai/__pycache__/mcinit.cpython-311.pyc +0 -0
  85. machineconfig/scripts/python/ai/chatmodes/Thinking-Beast-Mode.chatmode.md +337 -0
  86. machineconfig/scripts/python/ai/chatmodes/Ultimate-Transparent-Thinking-Beast-Mode.chatmode.md +644 -0
  87. machineconfig/scripts/python/ai/chatmodes/deepResearch.chatmode.md +81 -0
  88. machineconfig/scripts/python/ai/configs/.gemini/settings.json +81 -0
  89. machineconfig/scripts/python/ai/instructions/python/dev.instructions.md +45 -0
  90. machineconfig/scripts/python/ai/mcinit.py +103 -0
  91. machineconfig/scripts/python/ai/prompts/allLintersAndTypeCheckers.prompt.md +5 -0
  92. machineconfig/scripts/python/ai/prompts/research-report-skeleton.prompt.md +38 -0
  93. machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +47 -0
  94. machineconfig/scripts/python/archive/tmate_conn.py +5 -5
  95. machineconfig/scripts/python/archive/tmate_start.py +3 -3
  96. machineconfig/scripts/python/choose_wezterm_theme.py +2 -2
  97. machineconfig/scripts/python/cloud_copy.py +19 -18
  98. machineconfig/scripts/python/cloud_mount.py +9 -7
  99. machineconfig/scripts/python/cloud_repo_sync.py +11 -11
  100. machineconfig/scripts/python/cloud_sync.py +1 -1
  101. machineconfig/scripts/python/croshell.py +14 -14
  102. machineconfig/scripts/python/devops.py +6 -6
  103. machineconfig/scripts/python/devops_add_identity.py +8 -6
  104. machineconfig/scripts/python/devops_add_ssh_key.py +18 -18
  105. machineconfig/scripts/python/devops_backup_retrieve.py +13 -13
  106. machineconfig/scripts/python/devops_devapps_install.py +3 -3
  107. machineconfig/scripts/python/devops_update_repos.py +1 -1
  108. machineconfig/scripts/python/dotfile.py +2 -2
  109. machineconfig/scripts/python/fire_agents.py +183 -41
  110. machineconfig/scripts/python/fire_jobs.py +17 -11
  111. machineconfig/scripts/python/ftpx.py +2 -2
  112. machineconfig/scripts/python/gh_models.py +94 -94
  113. machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-311.pyc +0 -0
  114. machineconfig/scripts/python/helpers/__pycache__/cloud_helpers.cpython-311.pyc +0 -0
  115. machineconfig/scripts/python/helpers/__pycache__/helpers2.cpython-311.pyc +0 -0
  116. machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-311.pyc +0 -0
  117. machineconfig/scripts/python/helpers/cloud_helpers.py +3 -3
  118. machineconfig/scripts/python/helpers/helpers2.py +1 -1
  119. machineconfig/scripts/python/helpers/helpers4.py +8 -6
  120. machineconfig/scripts/python/helpers/helpers5.py +7 -7
  121. machineconfig/scripts/python/helpers/repo_sync_helpers.py +1 -1
  122. machineconfig/scripts/python/mount_nfs.py +3 -2
  123. machineconfig/scripts/python/mount_nw_drive.py +4 -4
  124. machineconfig/scripts/python/mount_ssh.py +3 -2
  125. machineconfig/scripts/python/repos.py +8 -8
  126. machineconfig/scripts/python/scheduler.py +1 -1
  127. machineconfig/scripts/python/start_slidev.py +8 -7
  128. machineconfig/scripts/python/start_terminals.py +1 -1
  129. machineconfig/scripts/python/viewer.py +40 -40
  130. machineconfig/scripts/python/wifi_conn.py +65 -66
  131. machineconfig/scripts/python/wsl_windows_transfer.py +1 -1
  132. machineconfig/scripts/windows/mcinit.ps1 +4 -0
  133. machineconfig/settings/linters/.ruff.toml +2 -2
  134. machineconfig/settings/shells/ipy/profiles/default/startup/playext.py +71 -71
  135. machineconfig/settings/shells/wt/settings.json +8 -8
  136. machineconfig/setup_linux/web_shortcuts/tmp.sh +2 -0
  137. machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +10 -7
  138. machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +9 -7
  139. machineconfig/utils/ai/browser_user_wrapper.py +5 -5
  140. machineconfig/utils/ai/generate_file_checklist.py +11 -12
  141. machineconfig/utils/ai/url2md.py +1 -1
  142. machineconfig/utils/cloud/onedrive/setup_oauth.py +4 -4
  143. machineconfig/utils/cloud/onedrive/transaction.py +129 -129
  144. machineconfig/utils/code.py +13 -6
  145. machineconfig/utils/installer.py +51 -53
  146. machineconfig/utils/installer_utils/installer_abc.py +21 -10
  147. machineconfig/utils/installer_utils/installer_class.py +42 -16
  148. machineconfig/utils/io_save.py +3 -15
  149. machineconfig/utils/options.py +10 -3
  150. machineconfig/utils/path.py +5 -0
  151. machineconfig/utils/path_reduced.py +201 -149
  152. machineconfig/utils/procs.py +23 -23
  153. machineconfig/utils/scheduling.py +11 -12
  154. machineconfig/utils/ssh.py +270 -0
  155. machineconfig/utils/terminal.py +180 -0
  156. machineconfig/utils/utils.py +1 -2
  157. machineconfig/utils/utils2.py +43 -0
  158. machineconfig/utils/utils5.py +163 -34
  159. machineconfig/utils/ve.py +2 -2
  160. {machineconfig-1.96.dist-info → machineconfig-2.0.dist-info}/METADATA +13 -8
  161. {machineconfig-1.96.dist-info → machineconfig-2.0.dist-info}/RECORD +163 -144
  162. machineconfig/cluster/self_ssh.py +0 -57
  163. {machineconfig-1.96.dist-info → machineconfig-2.0.dist-info}/WHEEL +0 -0
  164. {machineconfig-1.96.dist-info → machineconfig-2.0.dist-info}/top_level.txt +0 -0
@@ -21,20 +21,20 @@ TMP_LAYOUT_DIR = Path.home().joinpath("tmp_results", "zellij_layouts", "layout_m
21
21
 
22
22
 
23
23
  class ZellijRemoteLayoutGenerator:
24
-
24
+
25
25
  def __init__(self, remote_name: str, session_name_prefix: str):
26
26
  self.remote_name = remote_name
27
27
  self.session_name = session_name_prefix + "_" + LayoutGenerator.generate_random_suffix()
28
28
  self.tab_config: Dict[str, Tuple[str, str]] = {}
29
29
  self.layout_path: Optional[str] = None
30
-
30
+
31
31
  # Initialize modular components
32
32
  self.remote_executor = RemoteExecutor(remote_name)
33
33
  self.layout_generator = LayoutGenerator()
34
34
  self.process_monitor = ProcessMonitor(self.remote_executor)
35
35
  self.session_manager = SessionManager(self.remote_executor, self.session_name, TMP_LAYOUT_DIR)
36
36
  self.status_reporter = StatusReporter(self.process_monitor, self.session_manager)
37
-
37
+
38
38
  def copy_layout_to_remote(self, local_layout_file: Path, random_suffix: str) -> str:
39
39
  return self.session_manager.copy_layout_to_remote(local_layout_file, random_suffix)
40
40
 
@@ -42,22 +42,22 @@ class ZellijRemoteLayoutGenerator:
42
42
  # Enhanced Rich logging for remote layout creation
43
43
  tab_count = len(tab_config)
44
44
  console.print(f"[bold cyan]📋 Creating Zellij layout[/bold cyan] [bright_green]with {tab_count} tabs[/bright_green] [magenta]for remote[/magenta] [bold yellow]'{self.remote_name}'[/bold yellow]")
45
-
45
+
46
46
  # Display tab summary for remote
47
47
  for tab_name, (cwd, _) in tab_config.items():
48
48
  console.print(f" [yellow]→[/yellow] [bold]{tab_name}[/bold] [dim]in[/dim] [blue]{cwd}[/blue] [dim]on[/dim] [yellow]{self.remote_name}[/yellow]")
49
-
49
+
50
50
  self.tab_config = tab_config.copy()
51
51
  if output_dir:
52
52
  output_path = Path(output_dir)
53
53
  else:
54
54
  output_path = TMP_LAYOUT_DIR
55
- self.layout_path = self.layout_generator.create_layout_file(tab_config, output_path, self.session_name)
55
+ self.layout_path = self.layout_generator.create_layout_file(tab_config, output_path, self.session_name)
56
56
  return self.layout_path
57
-
57
+
58
58
  def get_layout_preview(self, tab_config: Dict[str, Tuple[str, str]]) -> str:
59
59
  return self.layout_generator.generate_layout_content(tab_config)
60
-
60
+
61
61
  def check_command_status(self, tab_name: str, use_verification: bool = True) -> Dict[str, Any]:
62
62
  return self.process_monitor.check_command_status(tab_name, self.tab_config, use_verification)
63
63
 
@@ -114,42 +114,42 @@ class ZellijRemoteLayoutGenerator:
114
114
  file_path_obj = default_dir / f"zellij_session_{random_id}.json"
115
115
  else:
116
116
  file_path_obj = Path(file_path)
117
-
117
+
118
118
  # Ensure .json extension
119
119
  if not str(file_path_obj).endswith('.json'):
120
120
  file_path_obj = file_path_obj.with_suffix('.json')
121
-
121
+
122
122
  # Ensure parent directory exists
123
123
  file_path_obj.parent.mkdir(parents=True, exist_ok=True)
124
-
124
+
125
125
  # Serialize to JSON
126
126
  data = self.to_dict()
127
-
128
- with open(file_path_obj, 'w', encoding='utf-8') as f:
129
- json.dump(data, f, indent=2, ensure_ascii=False)
130
-
127
+
128
+ text = json.dumps(data, indent=2, ensure_ascii=False)
129
+ file_path_obj.write_text(text, encoding="utf-8")
130
+
131
131
  logger.info(f"✅ Serialized ZellijRemoteLayoutGenerator to: {file_path_obj}")
132
132
  return str(file_path_obj)
133
133
 
134
134
  @classmethod
135
135
  def from_json(cls, file_path: Union[str, Path]) -> 'ZellijRemoteLayoutGenerator':
136
136
  file_path = Path(file_path)
137
-
137
+
138
138
  # Ensure .json extension
139
139
  if not str(file_path).endswith('.json'):
140
140
  file_path = file_path.with_suffix('.json')
141
-
141
+
142
142
  if not file_path.exists():
143
143
  raise FileNotFoundError(f"JSON file not found: {file_path}")
144
-
144
+
145
145
  # Load JSON data
146
146
  with open(file_path, 'r', encoding='utf-8') as f:
147
147
  data = json.load(f)
148
-
148
+
149
149
  # Validate that it's the correct class
150
150
  if data.get('class_name') != cls.__name__:
151
151
  logger.warning(f"Class name mismatch: expected {cls.__name__}, got {data.get('class_name')}")
152
-
152
+
153
153
  # Create new instance
154
154
  # Extract session name prefix by removing the suffix
155
155
  session_name = data['session_name']
@@ -157,14 +157,14 @@ class ZellijRemoteLayoutGenerator:
157
157
  session_name_prefix = '_'.join(session_name.split('_')[:-1])
158
158
  else:
159
159
  session_name_prefix = session_name
160
-
160
+
161
161
  instance = cls(remote_name=data['remote_name'], session_name_prefix=session_name_prefix)
162
-
162
+
163
163
  # Restore state
164
164
  instance.session_name = data['session_name']
165
165
  instance.tab_config = data['tab_config']
166
166
  instance.layout_path = data['layout_path']
167
-
167
+
168
168
  logger.info(f"✅ Loaded ZellijRemoteLayoutGenerator from: {file_path}")
169
169
  return instance
170
170
 
@@ -174,10 +174,10 @@ class ZellijRemoteLayoutGenerator:
174
174
  directory_path = Path.home() / "tmp_results" / "zellij_sessions" / "serialized"
175
175
  else:
176
176
  directory_path = Path(directory_path)
177
-
177
+
178
178
  if not directory_path.exists():
179
179
  return []
180
-
180
+
181
181
  json_files = [f.name for f in directory_path.glob("*.json")]
182
182
  return sorted(json_files)
183
183
 
@@ -185,45 +185,45 @@ if __name__ == "__main__":
185
185
  # Example usage
186
186
  sample_tabs = {
187
187
  "🤖Bot1": ("~/code/bytesense/bithence", "~/scripts/fire -mO go1.py bot1 --kw create_new_bot True"),
188
- "🤖Bot2": ("~/code/bytesense/bithence", "~/scripts/fire -mO go2.py bot2 --kw create_new_bot True"),
188
+ "🤖Bot2": ("~/code/bytesense/bithence", "~/scripts/fire -mO go2.py bot2 --kw create_new_bot True"),
189
189
  "📊Monitor": ("~", "htop"),
190
190
  "📝Logs": ("/var/log", "tail -f /var/log/app.log")
191
191
  }
192
-
192
+
193
193
  # Replace 'myserver' with an actual SSH config alias
194
194
  remote_name = "myserver" # This should be in ~/.ssh/config
195
195
  session_name = "test_remote_session"
196
-
196
+
197
197
  try:
198
198
  # Create layout using the remote generator
199
199
  generator = ZellijRemoteLayoutGenerator(remote_name=remote_name, session_name_prefix=session_name)
200
200
  layout_path = generator.create_zellij_layout(sample_tabs)
201
201
  print(f"✅ Remote layout created successfully: {layout_path}")
202
-
202
+
203
203
  # Demonstrate serialization
204
204
  print("\n💾 Demonstrating serialization...")
205
205
  saved_path = generator.to_json()
206
206
  print(f"✅ Session saved to: {saved_path}")
207
-
207
+
208
208
  # List all saved sessions
209
209
  saved_sessions = ZellijRemoteLayoutGenerator.list_saved_sessions()
210
210
  print(f"📋 Available saved sessions: {saved_sessions}")
211
-
211
+
212
212
  # Demonstrate loading (using the full path)
213
213
  loaded_generator = ZellijRemoteLayoutGenerator.from_json(saved_path)
214
214
  print(f"✅ Session loaded successfully: {loaded_generator.session_name}")
215
215
  print(f"📊 Loaded tabs: {list(loaded_generator.tab_config.keys())}")
216
-
216
+
217
217
  # Demonstrate status checking
218
218
  print(f"\n🔍 Checking command status on remote '{remote_name}':")
219
219
  generator.print_status_report()
220
-
220
+
221
221
  # Start the session (uncomment to actually start)
222
222
  # start_result = generator.start_zellij_session()
223
223
  # print(f"Session start result: {start_result}")
224
-
224
+
225
225
  # Attach to session (uncomment to attach)
226
226
  # generator.attach_to_session()
227
-
227
+
228
228
  except Exception as e:
229
229
  print(f"❌ Error: {e}")
@@ -1,15 +1,16 @@
1
1
  from datetime import datetime
2
2
  import json
3
3
  import uuid
4
- import logging
5
4
  from pathlib import Path
6
5
  from typing import Optional
7
6
  from machineconfig.utils.utils5 import Scheduler
8
7
  from machineconfig.cluster.sessions_managers.zellij_local import run_command_in_zellij_tab
9
8
  from machineconfig.cluster.sessions_managers.zellij_remote import ZellijRemoteLayoutGenerator
9
+ from machineconfig.logger import get_logger
10
10
 
11
11
 
12
12
  TMP_SERIALIAZATION_DIR = Path.home().joinpath("tmp_results", "session_manager", "zellij", "remote_manager")
13
+ logger = get_logger("cluster.sessions_managers.zellij_remote_manager")
13
14
 
14
15
 
15
16
  class ZellijSessionManager:
@@ -35,14 +36,14 @@ class ZellijSessionManager:
35
36
  a_cmd = run_command_in_zellij_tab(command=ssh_cmd, tab_name=hostname, cwd=None)
36
37
  cmds += a_cmd + "\n"
37
38
  return cmds
38
-
39
+
39
40
  def kill_all_sessions(self) -> None:
40
41
  for an_m in self.managers:
41
42
  ZellijRemoteLayoutGenerator.run_remote_command(
42
43
  remote_name=an_m.remote_name,
43
44
  command="zellij kill-all-sessions --yes"
44
45
  )
45
-
46
+
46
47
  def start_zellij_sessions(self) -> None:
47
48
  for an_m in self.managers:
48
49
  an_m.start_zellij_session()
@@ -65,12 +66,12 @@ class ZellijSessionManager:
65
66
  for i, key in enumerate(keys):
66
67
  if i < len(values):
67
68
  status_data.append({"tabName": key, "status": values[i]})
68
-
69
+
69
70
  # Check if all stopped
70
71
  running_count = sum(1 for item in status_data if item.get("status", {}).get("running", False))
71
72
  if running_count == 0: # they all stopped
72
73
  sched.max_cycles = sched.cycle # stop the scheduler from calling this routine again
73
-
74
+
74
75
  # Print status
75
76
  for item in status_data:
76
77
  print(f"Tab: {item['tabName']}, Status: {item['status']}")
@@ -79,26 +80,26 @@ class ZellijSessionManager:
79
80
  for _idx, an_m in enumerate(self.managers):
80
81
  a_status = an_m.check_zellij_session_status()
81
82
  statuses.append(a_status)
82
-
83
+
83
84
  # Print statuses
84
85
  for i, status in enumerate(statuses):
85
86
  print(f"Manager {i}: {status}")
86
- sched = Scheduler(routine=routine, wait_ms=60000) # 60 seconds
87
+ sched = Scheduler(routine=routine, wait_ms=60_000, logger=logger)
87
88
  sched.run()
88
89
 
89
90
  def save(self, session_id: Optional[str] = None) -> str:
90
91
  if session_id is None:
91
92
  session_id = str(uuid.uuid4())[:8]
92
-
93
+
93
94
  # Create session directory
94
95
  session_dir = TMP_SERIALIAZATION_DIR / session_id
95
96
  session_dir.mkdir(parents=True, exist_ok=True)
96
-
97
+
97
98
  # Save the machine2zellij_tabs configuration
98
99
  config_file = session_dir / "machine2zellij_tabs.json"
99
- with open(config_file, 'w', encoding='utf-8') as f:
100
- json.dump(self.machine2zellij_tabs, f, indent=2, ensure_ascii=False)
101
-
100
+ text = json.dumps(self.machine2zellij_tabs, indent=2, ensure_ascii=False)
101
+ config_file.write_text(text, encoding="utf-8")
102
+
102
103
  # Save session metadata
103
104
  metadata = {
104
105
  "session_name_prefix": self.session_name_prefix,
@@ -107,39 +108,38 @@ class ZellijSessionManager:
107
108
  "machines": list(self.machine2zellij_tabs.keys())
108
109
  }
109
110
  metadata_file = session_dir / "metadata.json"
110
- with open(metadata_file, 'w', encoding='utf-8') as f:
111
- json.dump(metadata, f, indent=2, ensure_ascii=False)
112
-
111
+ text = json.dumps(metadata, indent=2, ensure_ascii=False)
112
+ metadata_file.write_text(text, encoding="utf-8")
113
+
113
114
  # Save each ZellijRemoteLayoutGenerator
114
115
  managers_dir = session_dir / "managers"
115
116
  managers_dir.mkdir(exist_ok=True)
116
-
117
+
117
118
  for i, manager in enumerate(self.managers):
118
119
  manager_file = managers_dir / f"manager_{i}_{manager.remote_name}.json"
119
120
  manager.to_json(str(manager_file))
120
-
121
- logging.info(f"✅ Saved ZellijSessionManager session to: {session_dir}")
121
+ logger.info(f"✅ Saved ZellijSessionManager session to: {session_dir}")
122
122
  return session_id
123
123
 
124
124
  @classmethod
125
125
  def load(cls, session_id: str) -> 'ZellijSessionManager':
126
126
  session_dir = TMP_SERIALIAZATION_DIR / session_id
127
-
127
+
128
128
  if not session_dir.exists():
129
- raise FileNotFoundError(f"Session directory not found: {session_dir}")
129
+ raise FileNotFoundError(f"Session directory not found: {session_dir}")
130
130
  config_file = session_dir / "machine2zellij_tabs.json"
131
131
  if not config_file.exists():
132
- raise FileNotFoundError(f"Configuration file not found: {config_file}")
132
+ raise FileNotFoundError(f"Configuration file not found: {config_file}")
133
133
  with open(config_file, 'r', encoding='utf-8') as f:
134
134
  machine2zellij_tabs = json.load(f)
135
-
135
+
136
136
  # Load metadata
137
137
  metadata_file = session_dir / "metadata.json"
138
138
  session_name_prefix = "JobMgr" # default fallback
139
139
  if metadata_file.exists():
140
140
  with open(metadata_file, 'r', encoding='utf-8') as f:
141
141
  metadata = json.load(f)
142
- session_name_prefix = metadata.get("session_name_prefix", "JobMgr")
142
+ session_name_prefix = metadata.get("session_name_prefix", "JobMgr")
143
143
  # Create new instance (this will create new managers)
144
144
  instance = cls(machine2zellij_tabs=machine2zellij_tabs, session_name_prefix=session_name_prefix)
145
145
  # Load saved managers to restore their states
@@ -148,41 +148,41 @@ class ZellijSessionManager:
148
148
  # Clear the auto-created managers and load the saved ones
149
149
  instance.managers = []
150
150
  # Get all manager files and sort them
151
- manager_files = sorted(managers_dir.glob("manager_*.json"))
151
+ manager_files = sorted(managers_dir.glob("manager_*.json"))
152
152
  for manager_file in manager_files:
153
153
  try:
154
154
  loaded_manager = ZellijRemoteLayoutGenerator.from_json(str(manager_file))
155
155
  instance.managers.append(loaded_manager)
156
156
  except Exception as e:
157
- logging.warning(f"Failed to load manager from {manager_file}: {e}")
158
- logging.info(f"✅ Loaded ZellijSessionManager session from: {session_dir}")
157
+ logger.warning(f"Failed to load manager from {manager_file}: {e}")
158
+ logger.info(f"✅ Loaded ZellijSessionManager session from: {session_dir}")
159
159
  return instance
160
160
 
161
161
  @staticmethod
162
162
  def list_saved_sessions() -> list[str]:
163
163
  if not TMP_SERIALIAZATION_DIR.exists():
164
164
  return []
165
-
165
+
166
166
  sessions = []
167
167
  for item in TMP_SERIALIAZATION_DIR.iterdir():
168
168
  if item.is_dir() and (item / "metadata.json").exists():
169
169
  sessions.append(item.name)
170
-
170
+
171
171
  return sorted(sessions)
172
172
 
173
173
  @staticmethod
174
174
  def delete_session(session_id: str) -> bool:
175
175
  session_dir = TMP_SERIALIAZATION_DIR / session_id
176
-
176
+
177
177
  if not session_dir.exists():
178
- logging.warning(f"Session directory not found: {session_dir}")
178
+ logger.warning(f"Session directory not found: {session_dir}")
179
179
  return False
180
-
180
+
181
181
  try:
182
182
  import shutil
183
183
  shutil.rmtree(session_dir)
184
- logging.info(f"✅ Deleted session: {session_id}")
184
+ logger.info(f"✅ Deleted session: {session_id}")
185
185
  return True
186
186
  except Exception as e:
187
- logging.error(f"Failed to delete session {session_id}: {e}")
187
+ logger.error(f"Failed to delete session {session_id}: {e}")
188
188
  return False
@@ -7,56 +7,56 @@ from machineconfig.cluster.sessions_managers.zellij_remote import ZellijRemoteLa
7
7
 
8
8
  def example_usage():
9
9
  """Demonstrate the refactored modular usage."""
10
-
10
+
11
11
  # Sample tab configuration
12
12
  sample_tabs = {
13
13
  "🤖Bot1": ("~/code/bytesense/bithence", "~/scripts/fire -mO go1.py bot1 --kw create_new_bot True"),
14
- "🤖Bot2": ("~/code/bytesense/bithence", "~/scripts/fire -mO go2.py bot2 --kw create_new_bot True"),
14
+ "🤖Bot2": ("~/code/bytesense/bithence", "~/scripts/fire -mO go2.py bot2 --kw create_new_bot True"),
15
15
  "📊Monitor": ("~", "htop"),
16
16
  "📝Logs": ("/var/log", "tail -f /var/log/app.log")
17
17
  }
18
-
18
+
19
19
  # Replace 'myserver' with an actual SSH config alias
20
20
  remote_name = "myserver" # This should be in ~/.ssh/config
21
21
  session_name = "test_remote_session"
22
-
22
+
23
23
  try:
24
24
  # Create layout using the remote generator
25
25
  generator = ZellijRemoteLayoutGenerator(remote_name=remote_name, session_name_prefix=session_name)
26
-
26
+
27
27
  # Create layout file
28
28
  layout_path = generator.create_zellij_layout(sample_tabs)
29
29
  print(f"✅ Remote layout created successfully: {layout_path}")
30
-
30
+
31
31
  # Preview the layout content
32
32
  preview = generator.get_layout_preview(sample_tabs)
33
33
  print(f"📄 Layout preview:\n{preview}")
34
-
34
+
35
35
  # Check status using the modular components
36
36
  print(f"\n🔍 Checking command status on remote '{remote_name}':")
37
37
  generator.print_status_report()
38
-
38
+
39
39
  # The individual components can also be used directly:
40
40
  print(f"\n🔧 Direct component usage examples:")
41
-
41
+
42
42
  # Use remote executor directly
43
43
  print(f"Remote executor: {generator.remote_executor.remote_name}")
44
-
45
- # Use layout generator directly
44
+
45
+ # Use layout generator directly
46
46
  layout_content = generator.layout_generator.generate_layout_content(sample_tabs)
47
47
  print(f"Layout content length: {len(layout_content)} characters")
48
-
48
+
49
49
  # Use process monitor directly
50
50
  status = generator.process_monitor.check_all_commands_status(sample_tabs)
51
51
  print(f"Command status check completed for {len(status)} commands")
52
-
52
+
53
53
  print("\n✅ All modular components working correctly!")
54
-
54
+
55
55
  # Uncomment these to actually start and attach to the session:
56
56
  # start_result = generator.start_zellij_session()
57
57
  # print(f"Session start result: {start_result}")
58
58
  # generator.attach_to_session()
59
-
59
+
60
60
  except Exception as e:
61
61
  print(f"❌ Error: {e}")
62
62
 
@@ -17,7 +17,7 @@ console = Console()
17
17
 
18
18
  class LayoutGenerator:
19
19
  """Handles generation of Zellij KDL layout files."""
20
-
20
+
21
21
  LAYOUT_TEMPLATE = """layout {
22
22
  default_tab_template {
23
23
  // the default zellij tab-bar and status bar plugins
@@ -27,29 +27,29 @@ class LayoutGenerator:
27
27
  children
28
28
  }
29
29
  """
30
-
30
+
31
31
  @staticmethod
32
32
  def generate_random_suffix(length: int = 8) -> str:
33
33
  """Generate a random string suffix for unique layout file names."""
34
34
  return ''.join(random.choices(string.ascii_lowercase + string.digits, k=length))
35
-
35
+
36
36
  @staticmethod
37
37
  def parse_command(command: str) -> Tuple[str, List[str]]:
38
38
  """Parse a command string into command and arguments."""
39
39
  try:
40
40
  parts = shlex.split(command)
41
- if not parts:
41
+ if not parts:
42
42
  raise ValueError("Empty command provided")
43
43
  return parts[0], parts[1:] if len(parts) > 1 else []
44
44
  except ValueError as e:
45
45
  logger.error(f"Error parsing command '{command}': {e}")
46
46
  parts = command.split()
47
47
  return parts[0] if parts else "", parts[1:] if len(parts) > 1 else []
48
-
48
+
49
49
  @staticmethod
50
50
  def format_args_for_kdl(args: List[str]) -> str:
51
51
  """Format command arguments for KDL syntax."""
52
- if not args:
52
+ if not args:
53
53
  return ""
54
54
  formatted_args = []
55
55
  for arg in args:
@@ -59,7 +59,7 @@ class LayoutGenerator:
59
59
  else:
60
60
  formatted_args.append(f'"{arg}"')
61
61
  return " ".join(formatted_args)
62
-
62
+
63
63
  @staticmethod
64
64
  def create_tab_section(tab_name: str, cwd: str, command: str) -> str:
65
65
  """Create a KDL tab section for the layout."""
@@ -67,60 +67,59 @@ class LayoutGenerator:
67
67
  args_str = LayoutGenerator.format_args_for_kdl(args)
68
68
  tab_cwd = cwd or "~"
69
69
  escaped_tab_name = tab_name.replace('"', '\\"')
70
-
70
+
71
71
  tab_section = f' tab name="{escaped_tab_name}" cwd="{tab_cwd}" {{\n'
72
72
  tab_section += f' pane command="{cmd}" {{\n'
73
- if args_str:
73
+ if args_str:
74
74
  tab_section += f' args {args_str}\n'
75
75
  tab_section += ' }\n }\n'
76
76
  return tab_section
77
-
77
+
78
78
  @staticmethod
79
79
  def validate_tab_config(tab_config: Dict[str, Tuple[str, str]]) -> None:
80
80
  """Validate tab configuration format and content."""
81
- if not tab_config:
81
+ if not tab_config:
82
82
  raise ValueError("Tab configuration cannot be empty")
83
83
  for tab_name, (cwd, command) in tab_config.items():
84
- if not tab_name.strip():
84
+ if not tab_name.strip():
85
85
  raise ValueError(f"Invalid tab name: {tab_name}")
86
- if not command.strip():
86
+ if not command.strip():
87
87
  raise ValueError(f"Invalid command for tab '{tab_name}': {command}")
88
- if not cwd.strip():
88
+ if not cwd.strip():
89
89
  raise ValueError(f"Invalid cwd for tab '{tab_name}': {cwd}")
90
-
90
+
91
91
  def generate_layout_content(self, tab_config: Dict[str, Tuple[str, str]]) -> str:
92
92
  """Generate complete KDL layout content."""
93
93
  self.validate_tab_config(tab_config)
94
-
94
+
95
95
  layout_content = self.LAYOUT_TEMPLATE
96
96
  for tab_name, (cwd, command) in tab_config.items():
97
97
  layout_content += "\n" + self.create_tab_section(tab_name, cwd, command)
98
98
  layout_content += "\n}\n"
99
-
99
+
100
100
  return layout_content
101
-
102
- def create_layout_file(self, tab_config: Dict[str, Tuple[str, str]],
101
+
102
+ def create_layout_file(self, tab_config: Dict[str, Tuple[str, str]],
103
103
  output_dir: Path, session_name: str) -> str:
104
104
  """Create a layout file and return its absolute path."""
105
105
  self.validate_tab_config(tab_config)
106
-
106
+
107
107
  # Generate unique suffix for this layout
108
108
  random_suffix = self.generate_random_suffix()
109
109
  layout_content = self.generate_layout_content(tab_config)
110
-
110
+
111
111
  try:
112
112
  # Create output directory if it doesn't exist
113
113
  output_dir.mkdir(parents=True, exist_ok=True)
114
114
  layout_file = output_dir / f"zellij_layout_{session_name}_{random_suffix}.kdl"
115
-
115
+
116
116
  # Write layout file
117
- with open(layout_file, 'w', encoding='utf-8') as f:
118
- f.write(layout_content)
119
-
117
+ layout_file.write_text(layout_content, encoding="utf-8")
118
+
120
119
  # Enhanced Rich logging
121
120
  console.print(f"[bold green]✅ Zellij layout file created:[/bold green] [cyan]{layout_file.absolute()}[/cyan]")
122
121
  return str(layout_file.absolute())
123
-
122
+
124
123
  except OSError as e:
125
124
  logger.error(f"Failed to create layout file: {e}")
126
125
  raise