machineconfig 6.84__py3-none-any.whl → 6.86__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 (58) hide show
  1. machineconfig/cluster/sessions_managers/wt_local.py +16 -221
  2. machineconfig/cluster/sessions_managers/wt_local_manager.py +33 -174
  3. machineconfig/cluster/sessions_managers/wt_remote_manager.py +39 -197
  4. machineconfig/cluster/sessions_managers/wt_utils/manager_persistence.py +52 -0
  5. machineconfig/cluster/sessions_managers/wt_utils/monitoring_helpers.py +50 -0
  6. machineconfig/cluster/sessions_managers/wt_utils/status_reporting.py +76 -0
  7. machineconfig/cluster/sessions_managers/wt_utils/wt_helpers.py +199 -0
  8. machineconfig/scripts/python/agents.py +18 -7
  9. machineconfig/scripts/python/ai/vscode_tasks.py +7 -2
  10. machineconfig/scripts/python/env_manager/path_manager_tui.py +1 -1
  11. machineconfig/scripts/python/fire_jobs.py +31 -66
  12. machineconfig/scripts/python/{helpers_fire → helpers_agents}/agentic_frameworks/fire_crush.py +6 -5
  13. machineconfig/scripts/python/{helpers_fire → helpers_agents}/agentic_frameworks/fire_cursor_agents.py +1 -1
  14. machineconfig/scripts/python/{helpers_fire → helpers_agents}/agentic_frameworks/fire_gemini.py +2 -3
  15. machineconfig/scripts/python/{helpers_fire → helpers_agents}/agentic_frameworks/fire_qwen.py +2 -2
  16. machineconfig/scripts/python/{helpers_fire → helpers_agents}/fire_agents_help_launch.py +4 -4
  17. machineconfig/scripts/python/{helpers_fire → helpers_agents}/fire_agents_helper_types.py +2 -2
  18. machineconfig/scripts/python/helpers_agents/templates/prompt.txt +6 -0
  19. machineconfig/scripts/python/helpers_agents/templates/template.sh +24 -0
  20. machineconfig/scripts/python/helpers_devops/cli_config.py +1 -1
  21. machineconfig/scripts/python/helpers_devops/cli_nw.py +50 -0
  22. machineconfig/scripts/python/helpers_devops/cli_self.py +3 -3
  23. machineconfig/scripts/python/helpers_devops/cli_utils.py +18 -1
  24. machineconfig/scripts/python/{helpers_fire/helpers4.py → helpers_fire_command/file_wrangler.py} +15 -0
  25. machineconfig/scripts/python/helpers_fire_command/fire_jobs_route_helper.py +1 -1
  26. machineconfig/scripts/python/helpers_repos/cloud_repo_sync.py +1 -1
  27. machineconfig/scripts/python/helpers_sessions/sessions_multiprocess.py +1 -1
  28. machineconfig/scripts/python/nw/mount_nfs +1 -1
  29. machineconfig/scripts/python/nw/wifi_conn.py +1 -53
  30. machineconfig/scripts/python/sessions.py +1 -1
  31. machineconfig/scripts/python/terminal.py +46 -17
  32. machineconfig/scripts/python/utils.py +10 -21
  33. machineconfig/scripts/windows/mounts/mount_ssh.ps1 +1 -1
  34. machineconfig/scripts/windows/term.ps1 +48 -0
  35. machineconfig/setup_linux/web_shortcuts/interactive.sh +1 -1
  36. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +1 -1
  37. machineconfig/utils/code.py +18 -13
  38. machineconfig/utils/installer.py +0 -1
  39. machineconfig/utils/meta.py +4 -3
  40. machineconfig/utils/path_helper.py +1 -1
  41. machineconfig/utils/scheduling.py +0 -2
  42. machineconfig/utils/schemas/fire_agents/fire_agents_input.py +1 -1
  43. machineconfig/utils/ssh.py +2 -2
  44. machineconfig/utils/upgrade_packages.py +28 -24
  45. {machineconfig-6.84.dist-info → machineconfig-6.86.dist-info}/METADATA +1 -1
  46. {machineconfig-6.84.dist-info → machineconfig-6.86.dist-info}/RECORD +55 -52
  47. machineconfig/scripts/linux/warp-cli.sh +0 -122
  48. machineconfig/scripts/python/helpers_fire/prompt.txt +0 -2
  49. machineconfig/scripts/python/helpers_fire/template.sh +0 -15
  50. /machineconfig/scripts/python/{helpers_fire → helpers_agents}/__init__.py +0 -0
  51. /machineconfig/scripts/python/{helpers_fire → helpers_agents}/agentic_frameworks/__init__.py +0 -0
  52. /machineconfig/scripts/python/{helpers_fire → helpers_agents}/agentic_frameworks/fire_crush.json +0 -0
  53. /machineconfig/scripts/python/{helpers_fire → helpers_agents}/fire_agents_help_search.py +0 -0
  54. /machineconfig/scripts/python/{helpers_fire → helpers_agents}/fire_agents_load_balancer.py +0 -0
  55. /machineconfig/scripts/python/{helpers_fire → helpers_agents/templates}/template.ps1 +0 -0
  56. {machineconfig-6.84.dist-info → machineconfig-6.86.dist-info}/WHEEL +0 -0
  57. {machineconfig-6.84.dist-info → machineconfig-6.86.dist-info}/entry_points.txt +0 -0
  58. {machineconfig-6.84.dist-info → machineconfig-6.86.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,4 @@
1
1
  from datetime import datetime
2
- import json
3
- import uuid
4
2
  import logging
5
3
  from pathlib import Path
6
4
  from typing import Optional, Any
@@ -8,8 +6,17 @@ from rich.console import Console
8
6
  from machineconfig.utils.scheduler import Scheduler
9
7
  from machineconfig.cluster.sessions_managers.wt_local import run_command_in_wt_tab
10
8
  from machineconfig.cluster.sessions_managers.wt_remote import WTRemoteLayoutGenerator
11
- from machineconfig.cluster.sessions_managers.wt_utils.layout_generator import WTLayoutGenerator
9
+ from machineconfig.cluster.sessions_managers.wt_utils.wt_helpers import generate_random_suffix
12
10
  from machineconfig.utils.schemas.layouts.layout_types import TabConfig, LayoutConfig
11
+ from machineconfig.cluster.sessions_managers.wt_utils.manager_persistence import (
12
+ generate_session_id, save_json_file, load_json_file, list_saved_sessions_in_dir, delete_session_dir, ensure_session_dir_exists
13
+ )
14
+ from machineconfig.cluster.sessions_managers.wt_utils.status_reporting import (
15
+ print_global_summary, print_session_health_status, print_commands_status, calculate_session_summary, calculate_global_summary_from_status
16
+ )
17
+ from machineconfig.cluster.sessions_managers.wt_utils.monitoring_helpers import (
18
+ collect_status_data_from_managers, flatten_status_data, check_if_all_stopped, print_status_table, collect_session_statuses, print_session_statuses
19
+ )
13
20
 
14
21
 
15
22
  # Module-level logger to be used throughout this module
@@ -26,11 +33,8 @@ class WTSessionManager:
26
33
  for machine, tab_config in machine2wt_tabs.items():
27
34
  # Convert legacy dict[str, tuple[str,str]] to LayoutConfig
28
35
  tabs: list[TabConfig] = [{"tabName": name, "startDir": cwd, "command": cmd} for name, (cwd, cmd) in tab_config.items()]
29
- layout_config: LayoutConfig = {
30
- "layoutName": f"{session_name_prefix}_{machine}",
31
- "layoutTabs": tabs
32
- }
33
- session_name = f"{session_name_prefix}_{WTLayoutGenerator.generate_random_suffix(8)}"
36
+ layout_config: LayoutConfig = {"layoutName": f"{session_name_prefix}_{machine}", "layoutTabs": tabs}
37
+ session_name = f"{session_name_prefix}_{generate_random_suffix(8)}"
34
38
  an_m = WTRemoteLayoutGenerator(layout_config=layout_config, remote_name=machine, session_name=session_name)
35
39
  an_m.create_layout_file()
36
40
  self.managers.append(an_m)
@@ -56,103 +60,47 @@ class WTSessionManager:
56
60
  def run_monitoring_routine(self, wait_ms: int = 60000) -> None:
57
61
  def routine(scheduler: Scheduler):
58
62
  if scheduler.cycle % 2 == 0:
59
- statuses = []
60
- for _idx, an_m in enumerate(self.managers):
61
- tabs = an_m.layout_config["layoutTabs"]
62
- a_status = an_m.process_monitor.check_all_commands_status(tabs)
63
- statuses.append(a_status)
64
- keys = []
65
- for item in statuses:
66
- keys.extend(item.keys())
67
- values = []
68
- for item in statuses:
69
- values.extend(item.values())
70
- # Create list of dictionaries instead of DataFrame
71
- status_data = []
72
- for i, key in enumerate(keys):
73
- if i < len(values):
74
- status_data.append({"tabName": key, "status": values[i]})
75
-
76
- # Check if all stopped
77
- running_count = sum(1 for item in status_data if item.get("status", {}).get("running", False))
78
- if running_count == 0: # they all stopped
79
- scheduler.max_cycles = scheduler.cycle # stop the scheduler from calling this routine again
80
-
81
- # Print status
82
- for item in status_data:
83
- print(f"Tab: {item['tabName']}, Status: {item['status']}")
63
+ statuses = collect_status_data_from_managers(self.managers)
64
+ status_data = flatten_status_data(statuses)
65
+ if check_if_all_stopped(status_data):
66
+ scheduler.max_cycles = scheduler.cycle
67
+ print_status_table(status_data)
84
68
  else:
85
- statuses = []
86
- for _idx, an_m in enumerate(self.managers):
87
- a_status = an_m.session_manager.check_wt_session_status()
88
- statuses.append(a_status)
89
-
90
- # Print statuses
91
- for i, status in enumerate(statuses):
92
- print(f"Manager {i}: {status}")
93
-
69
+ statuses = collect_session_statuses(self.managers)
70
+ print_session_statuses(statuses)
94
71
  sched = Scheduler(routine=routine, wait_ms=wait_ms, logger=logger)
95
72
  sched.run()
96
73
 
97
74
  def save(self, session_id: Optional[str] = None) -> str:
98
75
  if session_id is None:
99
- session_id = str(uuid.uuid4())[:8]
100
-
101
- # Create session directory
76
+ session_id = generate_session_id()
102
77
  session_dir = TMP_SERIALIZATION_DIR / session_id
103
- session_dir.mkdir(parents=True, exist_ok=True)
104
-
105
- # Save the machine2wt_tabs configuration
106
- config_file = session_dir / "machine2wt_tabs.json"
107
- text = json.dumps(self.machine2wt_tabs, indent=2, ensure_ascii=False)
108
- config_file.write_text(text, encoding="utf-8")
109
-
110
- # Save session metadata
78
+ ensure_session_dir_exists(session_dir)
79
+ save_json_file(session_dir / "machine2wt_tabs.json", self.machine2wt_tabs, "machine2wt_tabs")
111
80
  metadata = {"session_name_prefix": self.session_name_prefix, "created_at": str(datetime.now()), "num_managers": len(self.managers), "machines": list(self.machine2wt_tabs.keys()), "manager_type": "WTSessionManager"}
112
- metadata_file = session_dir / "metadata.json"
113
- text = json.dumps(metadata, indent=2, ensure_ascii=False)
114
- metadata_file.write_text(text, encoding="utf-8")
115
-
116
- # Save each WTRemoteLayoutGenerator
81
+ save_json_file(session_dir / "metadata.json", metadata, "metadata")
117
82
  managers_dir = session_dir / "managers"
118
83
  managers_dir.mkdir(exist_ok=True)
119
-
120
84
  for i, manager in enumerate(self.managers):
121
- manager_file = managers_dir / f"manager_{i}_{manager.remote_name}.json"
122
- manager.to_json(str(manager_file))
123
-
85
+ manager.to_json(str(managers_dir / f"manager_{i}_{manager.remote_name}.json"))
124
86
  logger.info(f"✅ Saved WTSessionManager session to: {session_dir}")
125
87
  return session_id
126
88
 
127
89
  @classmethod
128
90
  def load(cls, session_id: str) -> "WTSessionManager":
129
91
  session_dir = TMP_SERIALIZATION_DIR / session_id
130
-
131
92
  if not session_dir.exists():
132
93
  raise FileNotFoundError(f"Session directory not found: {session_dir}")
133
- config_file = session_dir / "machine2wt_tabs.json"
134
- if not config_file.exists():
135
- raise FileNotFoundError(f"Configuration file not found: {config_file}")
136
- text = config_file.read_text(encoding="utf-8")
137
- machine2wt_tabs = json.loads(text)
138
-
139
- # Load metadata
140
- metadata_file = session_dir / "metadata.json"
141
- session_name_prefix = "WTJobMgr" # default fallback
142
- if metadata_file.exists():
143
- text = metadata_file.read_text(encoding="utf-8")
144
- metadata = json.loads(text)
145
- session_name_prefix = metadata.get("session_name_prefix", "WTJobMgr")
146
- # Create new instance (this will create new managers)
94
+ loaded_data = load_json_file(session_dir / "machine2wt_tabs.json", "Configuration file")
95
+ machine2wt_tabs = loaded_data if isinstance(loaded_data, dict) else {} # type: ignore[arg-type]
96
+ metadata_data = load_json_file(session_dir / "metadata.json", "Metadata file") if (session_dir / "metadata.json").exists() else {}
97
+ metadata = metadata_data if isinstance(metadata_data, dict) else {} # type: ignore[arg-type]
98
+ session_name_prefix = metadata.get("session_name_prefix", "WTJobMgr") # type: ignore[union-attr]
147
99
  instance = cls(machine2wt_tabs=machine2wt_tabs, session_name_prefix=session_name_prefix)
148
- # Load saved managers to restore their states
149
100
  managers_dir = session_dir / "managers"
150
101
  if managers_dir.exists():
151
- # Clear the auto-created managers and load the saved ones
152
102
  instance.managers = []
153
- # Get all manager files and sort them
154
- manager_files = sorted(managers_dir.glob("manager_*.json"))
155
- for manager_file in manager_files:
103
+ for manager_file in sorted(managers_dir.glob("manager_*.json")):
156
104
  try:
157
105
  loaded_manager = WTRemoteLayoutGenerator.from_json(str(manager_file))
158
106
  instance.managers.append(loaded_manager)
@@ -163,33 +111,11 @@ class WTSessionManager:
163
111
 
164
112
  @staticmethod
165
113
  def list_saved_sessions() -> list[str]:
166
- if not TMP_SERIALIZATION_DIR.exists():
167
- return []
168
-
169
- sessions = []
170
- for item in TMP_SERIALIZATION_DIR.iterdir():
171
- if item.is_dir() and (item / "metadata.json").exists():
172
- sessions.append(item.name)
173
-
174
- return sorted(sessions)
114
+ return list_saved_sessions_in_dir(TMP_SERIALIZATION_DIR)
175
115
 
176
116
  @staticmethod
177
117
  def delete_session(session_id: str) -> bool:
178
- session_dir = TMP_SERIALIZATION_DIR / session_id
179
-
180
- if not session_dir.exists():
181
- logger.warning(f"Session directory not found: {session_dir}")
182
- return False
183
-
184
- try:
185
- import shutil
186
-
187
- shutil.rmtree(session_dir)
188
- logger.info(f"✅ Deleted session: {session_id}")
189
- return True
190
- except Exception as e:
191
- logger.error(f"Failed to delete session {session_id}: {e}")
192
- return False
118
+ return delete_session_dir(TMP_SERIALIZATION_DIR / session_id, session_id)
193
119
 
194
120
  def start_all_sessions(self) -> dict[str, Any]:
195
121
  """Start all Windows Terminal sessions on their respective remote machines."""
@@ -216,122 +142,38 @@ class WTSessionManager:
216
142
  return results
217
143
 
218
144
  def check_all_sessions_status(self) -> dict[str, dict[str, Any]]:
219
- """Check the status of all remote sessions and their commands."""
220
145
  status_report = {}
221
-
222
146
  for manager in self.managers:
223
147
  session_key = f"{manager.remote_name}:{manager.session_name}"
224
-
225
148
  try:
226
- # Get Windows Terminal session status
227
149
  wt_status = manager.session_manager.check_wt_session_status()
228
-
229
- # Get commands status for this session
230
150
  tabs = manager.layout_config["layoutTabs"]
231
151
  commands_status = manager.process_monitor.check_all_commands_status(tabs)
232
-
233
- # Calculate summary for this session
234
- running_count = sum(1 for status in commands_status.values() if status.get("running", False))
235
- total_count = len(commands_status)
236
-
237
- status_report[session_key] = {
238
- "remote_name": manager.remote_name,
239
- "session_name": manager.session_name,
240
- "wt_status": wt_status,
241
- "commands_status": commands_status,
242
- "summary": {"total_commands": total_count, "running_commands": running_count, "stopped_commands": total_count - running_count, "session_healthy": wt_status.get("wt_running", False)},
243
- }
244
-
152
+ summary = calculate_session_summary(commands_status, wt_status.get("wt_running", False))
153
+ status_report[session_key] = {"remote_name": manager.remote_name, "session_name": manager.session_name, "wt_status": wt_status, "commands_status": commands_status, "summary": summary}
245
154
  except Exception as e:
246
155
  status_report[session_key] = {"remote_name": manager.remote_name, "session_name": manager.session_name, "error": str(e), "summary": {"total_commands": 0, "running_commands": 0, "stopped_commands": 0, "session_healthy": False}}
247
156
  logger.error(f"Error checking status for {session_key}: {e}")
248
-
249
157
  return status_report
250
158
 
251
159
  def get_global_summary(self) -> dict[str, Any]:
252
- """Get a global summary across all remote sessions."""
253
160
  all_status = self.check_all_sessions_status()
254
-
255
- total_sessions = len(all_status)
256
- healthy_sessions = sum(1 for status in all_status.values() if status["summary"]["session_healthy"])
257
- total_commands = sum(status["summary"]["total_commands"] for status in all_status.values())
258
- total_running = sum(status["summary"]["running_commands"] for status in all_status.values())
259
-
260
- return {
261
- "total_sessions": total_sessions,
262
- "healthy_sessions": healthy_sessions,
263
- "unhealthy_sessions": total_sessions - healthy_sessions,
264
- "total_commands": total_commands,
265
- "running_commands": total_running,
266
- "stopped_commands": total_commands - total_running,
267
- "all_sessions_healthy": healthy_sessions == total_sessions,
268
- "all_commands_running": total_running == total_commands,
269
- "remote_machines": list(set(status["remote_name"] for status in all_status.values())),
270
- }
161
+ return calculate_global_summary_from_status(all_status, include_remote_machines=True)
271
162
 
272
163
  def print_status_report(self) -> None:
273
- """Print a comprehensive status report for all remote sessions."""
274
164
  all_status = self.check_all_sessions_status()
275
165
  global_summary = self.get_global_summary()
276
-
277
- print("=" * 80)
278
- print("🖥️ WINDOWS TERMINAL REMOTE MANAGER STATUS REPORT")
279
- print("=" * 80)
280
-
281
- # Global summary
282
- print("🌐 GLOBAL SUMMARY:")
283
- print(f" Total sessions: {global_summary['total_sessions']}")
284
- print(f" Healthy sessions: {global_summary['healthy_sessions']}")
285
- print(f" Total commands: {global_summary['total_commands']}")
286
- print(f" Running commands: {global_summary['running_commands']}")
287
- print(f" Remote machines: {len(global_summary['remote_machines'])}")
288
- print(f" All healthy: {'✅' if global_summary['all_sessions_healthy'] else '❌'}")
289
- print()
290
-
291
- # Per-session details
166
+ print_global_summary(global_summary, "WINDOWS TERMINAL REMOTE MANAGER STATUS REPORT")
292
167
  for _, status in all_status.items():
293
- remote_name = status["remote_name"]
294
- session_name = status["session_name"]
295
-
296
- print(f"🖥️ REMOTE: {remote_name} | SESSION: {session_name}")
168
+ print(f"🖥️ REMOTE: {status['remote_name']} | SESSION: {status['session_name']}")
297
169
  print("-" * 60)
298
-
299
170
  if "error" in status:
300
171
  print(f"❌ Error: {status['error']}")
301
172
  print()
302
173
  continue
303
-
304
- wt_status = status["wt_status"]
305
- commands_status = status["commands_status"]
306
- summary = status["summary"]
307
-
308
- # Windows Terminal session health
309
- if wt_status.get("wt_running", False):
310
- if wt_status.get("session_exists", False):
311
- session_windows = wt_status.get("session_windows", [])
312
- all_windows = wt_status.get("all_windows", [])
313
- print(f"✅ Windows Terminal is running on {remote_name}")
314
- print(f" Session windows: {len(session_windows)}")
315
- print(f" Total WT windows: {len(all_windows)}")
316
- else:
317
- print(f"⚠️ Windows Terminal is running but no session windows found on {remote_name}")
318
- else:
319
- print(f"❌ Windows Terminal issue on {remote_name}: {wt_status.get('error', 'Unknown error')}")
320
-
321
- # Commands in this session
322
- print(f" Commands ({summary['running_commands']}/{summary['total_commands']} running):")
323
- for tab_name, cmd_status in commands_status.items():
324
- status_icon = "✅" if cmd_status.get("running", False) else "❌"
325
- cmd_text = cmd_status.get("command", "Unknown")[:50]
326
- if len(cmd_status.get("command", "")) > 50:
327
- cmd_text += "..."
328
- console.print(f" {status_icon} {tab_name}: {cmd_text}")
329
-
330
- if cmd_status.get("processes"):
331
- for proc in cmd_status["processes"][:2]: # Show first 2 processes
332
- console.print(f" [dim]└─[/dim] PID {proc.get('pid', 'Unknown')}: {proc.get('name', 'Unknown')}")
174
+ print_session_health_status(status["wt_status"], remote_name=status["remote_name"])
175
+ print_commands_status(status["commands_status"], status["summary"])
333
176
  print()
334
-
335
177
  print("=" * 80)
336
178
 
337
179
  def get_remote_overview(self) -> dict[str, Any]:
@@ -0,0 +1,52 @@
1
+ import json
2
+ import uuid
3
+ import logging
4
+ import shutil
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ def generate_session_id() -> str:
12
+ return str(uuid.uuid4())[:8]
13
+
14
+
15
+ def save_json_file(file_path: Path, data: dict[str, Any] | list[Any], description: str) -> None:
16
+ text = json.dumps(data, indent=2, ensure_ascii=False)
17
+ file_path.write_text(text, encoding="utf-8")
18
+ logger.debug(f"Saved {description} to {file_path}")
19
+
20
+
21
+ def load_json_file(file_path: Path, description: str) -> dict[str, Any] | list[Any]:
22
+ if not file_path.exists():
23
+ raise FileNotFoundError(f"{description} not found: {file_path}")
24
+ text = file_path.read_text(encoding="utf-8")
25
+ return json.loads(text)
26
+
27
+
28
+ def list_saved_sessions_in_dir(serialization_dir: Path) -> list[str]:
29
+ if not serialization_dir.exists():
30
+ return []
31
+ sessions = []
32
+ for item in serialization_dir.iterdir():
33
+ if item.is_dir() and (item / "metadata.json").exists():
34
+ sessions.append(item.name)
35
+ return sorted(sessions)
36
+
37
+
38
+ def delete_session_dir(session_dir: Path, session_id: str) -> bool:
39
+ if not session_dir.exists():
40
+ logger.warning(f"Session directory not found: {session_dir}")
41
+ return False
42
+ try:
43
+ shutil.rmtree(session_dir)
44
+ logger.info(f"✅ Deleted session: {session_id}")
45
+ return True
46
+ except Exception as e:
47
+ logger.error(f"Failed to delete session {session_id}: {e}")
48
+ return False
49
+
50
+
51
+ def ensure_session_dir_exists(session_dir: Path) -> None:
52
+ session_dir.mkdir(parents=True, exist_ok=True)
@@ -0,0 +1,50 @@
1
+ from typing import Any
2
+
3
+
4
+ def collect_status_data_from_managers(managers: list[Any]) -> list[dict[str, Any]]:
5
+ statuses = []
6
+ for manager in managers:
7
+ tabs = manager.layout_config["layoutTabs"]
8
+ if hasattr(manager, "process_monitor"):
9
+ a_status = manager.process_monitor.check_all_commands_status(tabs)
10
+ else:
11
+ a_status = manager.check_all_commands_status()
12
+ statuses.append(a_status)
13
+ return statuses
14
+
15
+
16
+ def flatten_status_data(statuses: list[dict[str, dict[str, Any]]]) -> list[dict[str, Any]]:
17
+ keys = []
18
+ values = []
19
+ for item in statuses:
20
+ keys.extend(item.keys())
21
+ values.extend(item.values())
22
+
23
+ status_data = []
24
+ for i, key in enumerate(keys):
25
+ if i < len(values):
26
+ status_data.append({"tabName": key, "status": values[i]})
27
+ return status_data
28
+
29
+
30
+ def check_if_all_stopped(status_data: list[dict[str, Any]]) -> bool:
31
+ running_count = sum(1 for item in status_data if item.get("status", {}).get("running", False))
32
+ return running_count == 0
33
+
34
+
35
+ def print_status_table(status_data: list[dict[str, Any]]) -> None:
36
+ for item in status_data:
37
+ print(f"Tab: {item['tabName']}, Status: {item['status']}")
38
+
39
+
40
+ def collect_session_statuses(managers: list[Any]) -> list[dict[str, Any]]:
41
+ statuses = []
42
+ for manager in managers:
43
+ a_status = manager.session_manager.check_wt_session_status()
44
+ statuses.append(a_status)
45
+ return statuses
46
+
47
+
48
+ def print_session_statuses(statuses: list[dict[str, Any]]) -> None:
49
+ for i, status in enumerate(statuses):
50
+ print(f"Manager {i}: {status}")
@@ -0,0 +1,76 @@
1
+ from typing import Any, Optional
2
+ from rich.console import Console
3
+
4
+ console = Console()
5
+
6
+
7
+ def print_global_summary(global_summary: dict[str, Any], title: str) -> None:
8
+ print("=" * 80)
9
+ print(f"🖥️ {title}")
10
+ print("=" * 80)
11
+ print("🌐 GLOBAL SUMMARY:")
12
+ print(f" Total sessions: {global_summary['total_sessions']}")
13
+ print(f" Healthy sessions: {global_summary['healthy_sessions']}")
14
+ print(f" Total commands: {global_summary['total_commands']}")
15
+ print(f" Running commands: {global_summary['running_commands']}")
16
+ if "remote_machines" in global_summary:
17
+ print(f" Remote machines: {len(global_summary['remote_machines'])}")
18
+ print(f" All healthy: {'✅' if global_summary['all_sessions_healthy'] else '❌'}")
19
+ print()
20
+
21
+
22
+ def print_session_health_status(wt_status: dict[str, Any], remote_name: Optional[str] = None) -> None:
23
+ location_str = f" on {remote_name}" if remote_name else ""
24
+ if wt_status.get("wt_running", False):
25
+ if wt_status.get("session_exists", False):
26
+ session_windows = wt_status.get("session_windows", [])
27
+ all_windows = wt_status.get("all_windows", [])
28
+ print(f"✅ Windows Terminal is running{location_str}")
29
+ print(f" Session windows: {len(session_windows)}")
30
+ print(f" Total WT windows: {len(all_windows)}")
31
+ else:
32
+ print(f"⚠️ Windows Terminal is running but no session windows found{location_str}")
33
+ else:
34
+ print(f"❌ Windows Terminal issue{location_str}: {wt_status.get('error', 'Unknown error')}")
35
+
36
+
37
+ def print_commands_status(commands_status: dict[str, dict[str, Any]], summary: dict[str, int]) -> None:
38
+ print(f" Commands ({summary['running_commands']}/{summary['total_commands']} running):")
39
+ for tab_name, cmd_status in commands_status.items():
40
+ status_icon = "✅" if cmd_status.get("running", False) else "❌"
41
+ cmd_text = cmd_status.get("command", "Unknown")[:50]
42
+ if len(cmd_status.get("command", "")) > 50:
43
+ cmd_text += "..."
44
+ console.print(f" {status_icon} {tab_name}: {cmd_text}")
45
+ if cmd_status.get("processes"):
46
+ for proc in cmd_status["processes"][:2]:
47
+ console.print(f" [dim]└─[/dim] PID {proc.get('pid', 'Unknown')}: {proc.get('name', 'Unknown')}")
48
+
49
+
50
+ def calculate_session_summary(commands_status: dict[str, dict[str, Any]], session_healthy: bool) -> dict[str, Any]:
51
+ running_count = sum(1 for status in commands_status.values() if status.get("running", False))
52
+ total_count = len(commands_status)
53
+ return {"total_commands": total_count, "running_commands": running_count, "stopped_commands": total_count - running_count, "session_healthy": session_healthy}
54
+
55
+
56
+ def calculate_global_summary_from_status(all_status: dict[str, dict[str, Any]], include_remote_machines: bool = False) -> dict[str, Any]:
57
+ total_sessions = len(all_status)
58
+ healthy_sessions = sum(1 for status in all_status.values() if status.get("summary", {}).get("session_healthy", False))
59
+ total_commands = sum(status.get("summary", {}).get("total_commands", 0) for status in all_status.values())
60
+ total_running = sum(status.get("summary", {}).get("running_commands", 0) for status in all_status.values())
61
+
62
+ result: dict[str, Any] = {
63
+ "total_sessions": total_sessions,
64
+ "healthy_sessions": healthy_sessions,
65
+ "unhealthy_sessions": total_sessions - healthy_sessions,
66
+ "total_commands": total_commands,
67
+ "running_commands": total_running,
68
+ "stopped_commands": total_commands - total_running,
69
+ "all_sessions_healthy": healthy_sessions == total_sessions,
70
+ "all_commands_running": total_running == total_commands,
71
+ }
72
+
73
+ if include_remote_machines:
74
+ result["remote_machines"] = list(set(status.get("remote_name", "") for status in all_status.values() if "remote_name" in status))
75
+
76
+ return result