machineconfig 1.97__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.
- machineconfig/cluster/cloud_manager.py +22 -26
- machineconfig/cluster/data_transfer.py +2 -2
- machineconfig/cluster/distribute.py +0 -2
- machineconfig/cluster/file_manager.py +4 -4
- machineconfig/cluster/job_params.py +1 -1
- machineconfig/cluster/loader_runner.py +8 -8
- machineconfig/cluster/remote_machine.py +4 -4
- machineconfig/cluster/script_execution.py +2 -2
- machineconfig/cluster/sessions_managers/archive/create_zellij_template.py +1 -1
- machineconfig/cluster/sessions_managers/enhanced_command_runner.py +23 -23
- machineconfig/cluster/sessions_managers/wt_local.py +78 -76
- machineconfig/cluster/sessions_managers/wt_local_manager.py +91 -91
- machineconfig/cluster/sessions_managers/wt_remote.py +39 -39
- machineconfig/cluster/sessions_managers/wt_remote_manager.py +94 -91
- machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +56 -54
- machineconfig/cluster/sessions_managers/wt_utils/process_monitor.py +49 -49
- machineconfig/cluster/sessions_managers/wt_utils/remote_executor.py +18 -18
- machineconfig/cluster/sessions_managers/wt_utils/session_manager.py +42 -42
- machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +36 -36
- machineconfig/cluster/sessions_managers/zellij_local.py +43 -46
- machineconfig/cluster/sessions_managers/zellij_local_manager.py +139 -120
- machineconfig/cluster/sessions_managers/zellij_remote.py +35 -35
- machineconfig/cluster/sessions_managers/zellij_remote_manager.py +33 -33
- machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +15 -15
- machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +25 -26
- machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +49 -49
- machineconfig/cluster/sessions_managers/zellij_utils/remote_executor.py +5 -5
- machineconfig/cluster/sessions_managers/zellij_utils/session_manager.py +15 -15
- machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +11 -11
- machineconfig/cluster/templates/utils.py +3 -3
- machineconfig/jobs/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python/__pycache__/python_ve_symlink.cpython-311.pyc +0 -0
- machineconfig/jobs/python/check_installations.py +8 -9
- machineconfig/jobs/python/python_cargo_build_share.py +2 -2
- machineconfig/jobs/python/vscode/link_ve.py +7 -7
- machineconfig/jobs/python/vscode/select_interpreter.py +7 -7
- machineconfig/jobs/python/vscode/sync_code.py +5 -5
- machineconfig/jobs/python_custom_installers/archive/ngrok.py +2 -2
- machineconfig/jobs/python_custom_installers/dev/aider.py +3 -3
- machineconfig/jobs/python_custom_installers/dev/alacritty.py +3 -3
- machineconfig/jobs/python_custom_installers/dev/brave.py +3 -3
- machineconfig/jobs/python_custom_installers/dev/bypass_paywall.py +5 -5
- machineconfig/jobs/python_custom_installers/dev/code.py +3 -3
- machineconfig/jobs/python_custom_installers/dev/cursor.py +9 -9
- machineconfig/jobs/python_custom_installers/dev/docker_desktop.py +4 -4
- machineconfig/jobs/python_custom_installers/dev/espanso.py +4 -4
- machineconfig/jobs/python_custom_installers/dev/goes.py +4 -4
- machineconfig/jobs/python_custom_installers/dev/lvim.py +4 -4
- machineconfig/jobs/python_custom_installers/dev/nerdfont.py +3 -3
- machineconfig/jobs/python_custom_installers/dev/redis.py +3 -3
- machineconfig/jobs/python_custom_installers/dev/wezterm.py +3 -3
- machineconfig/jobs/python_custom_installers/dev/winget.py +27 -27
- machineconfig/jobs/python_custom_installers/docker.py +3 -3
- machineconfig/jobs/python_custom_installers/gh.py +7 -7
- machineconfig/jobs/python_custom_installers/hx.py +1 -1
- machineconfig/jobs/python_custom_installers/warp-cli.py +3 -3
- machineconfig/jobs/python_generic_installers/config.json +412 -389
- machineconfig/jobs/python_windows_installers/dev/config.json +1 -1
- machineconfig/logger.py +50 -0
- machineconfig/profile/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/profile/__pycache__/create.cpython-311.pyc +0 -0
- machineconfig/profile/__pycache__/shell.cpython-311.pyc +0 -0
- machineconfig/profile/create.py +23 -16
- machineconfig/profile/create_hardlinks.py +8 -8
- machineconfig/profile/shell.py +41 -37
- machineconfig/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/linux/devops +2 -2
- machineconfig/scripts/linux/fire +1 -0
- machineconfig/scripts/linux/fire_agents +0 -1
- machineconfig/scripts/linux/mcinit +1 -1
- machineconfig/scripts/python/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/croshell.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_agents.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_jobs.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/repos.cpython-311.pyc +0 -0
- machineconfig/scripts/python/ai/__pycache__/init.cpython-311.pyc +0 -0
- machineconfig/scripts/python/ai/__pycache__/mcinit.cpython-311.pyc +0 -0
- machineconfig/scripts/python/ai/chatmodes/Thinking-Beast-Mode.chatmode.md +337 -0
- machineconfig/scripts/python/ai/chatmodes/Ultimate-Transparent-Thinking-Beast-Mode.chatmode.md +644 -0
- machineconfig/scripts/python/ai/chatmodes/deepResearch.chatmode.md +81 -0
- machineconfig/scripts/python/ai/configs/.gemini/settings.json +81 -0
- machineconfig/scripts/python/ai/instructions/python/dev.instructions.md +45 -0
- machineconfig/scripts/python/ai/mcinit.py +103 -0
- machineconfig/scripts/python/ai/prompts/allLintersAndTypeCheckers.prompt.md +5 -0
- machineconfig/scripts/python/ai/prompts/research-report-skeleton.prompt.md +38 -0
- machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +47 -0
- machineconfig/scripts/python/archive/tmate_conn.py +5 -5
- machineconfig/scripts/python/archive/tmate_start.py +3 -3
- machineconfig/scripts/python/choose_wezterm_theme.py +2 -2
- machineconfig/scripts/python/cloud_copy.py +19 -18
- machineconfig/scripts/python/cloud_mount.py +9 -7
- machineconfig/scripts/python/cloud_repo_sync.py +11 -11
- machineconfig/scripts/python/cloud_sync.py +1 -1
- machineconfig/scripts/python/croshell.py +14 -14
- machineconfig/scripts/python/devops.py +6 -6
- machineconfig/scripts/python/devops_add_identity.py +8 -6
- machineconfig/scripts/python/devops_add_ssh_key.py +18 -18
- machineconfig/scripts/python/devops_backup_retrieve.py +13 -13
- machineconfig/scripts/python/devops_devapps_install.py +3 -3
- machineconfig/scripts/python/devops_update_repos.py +1 -1
- machineconfig/scripts/python/dotfile.py +2 -2
- machineconfig/scripts/python/fire_agents.py +183 -41
- machineconfig/scripts/python/fire_jobs.py +17 -17
- machineconfig/scripts/python/ftpx.py +2 -2
- machineconfig/scripts/python/gh_models.py +94 -94
- machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/cloud_helpers.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/helpers2.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/cloud_helpers.py +3 -3
- machineconfig/scripts/python/helpers/helpers2.py +1 -1
- machineconfig/scripts/python/helpers/helpers4.py +8 -6
- machineconfig/scripts/python/helpers/helpers5.py +7 -7
- machineconfig/scripts/python/helpers/repo_sync_helpers.py +1 -1
- machineconfig/scripts/python/mount_nfs.py +3 -2
- machineconfig/scripts/python/mount_nw_drive.py +4 -4
- machineconfig/scripts/python/mount_ssh.py +3 -2
- machineconfig/scripts/python/repos.py +8 -8
- machineconfig/scripts/python/scheduler.py +1 -1
- machineconfig/scripts/python/start_slidev.py +8 -7
- machineconfig/scripts/python/start_terminals.py +1 -1
- machineconfig/scripts/python/viewer.py +40 -40
- machineconfig/scripts/python/wifi_conn.py +65 -66
- machineconfig/scripts/python/wsl_windows_transfer.py +1 -1
- machineconfig/scripts/windows/mcinit.ps1 +1 -1
- machineconfig/settings/linters/.ruff.toml +2 -2
- machineconfig/settings/shells/ipy/profiles/default/startup/playext.py +71 -71
- machineconfig/settings/shells/wt/settings.json +8 -8
- machineconfig/setup_linux/web_shortcuts/tmp.sh +2 -0
- machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +10 -7
- machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +9 -7
- machineconfig/utils/ai/browser_user_wrapper.py +5 -5
- machineconfig/utils/ai/generate_file_checklist.py +11 -12
- machineconfig/utils/ai/url2md.py +1 -1
- machineconfig/utils/cloud/onedrive/setup_oauth.py +4 -4
- machineconfig/utils/cloud/onedrive/transaction.py +129 -129
- machineconfig/utils/code.py +13 -6
- machineconfig/utils/installer.py +51 -53
- machineconfig/utils/installer_utils/installer_abc.py +21 -10
- machineconfig/utils/installer_utils/installer_class.py +42 -16
- machineconfig/utils/io_save.py +3 -15
- machineconfig/utils/options.py +10 -3
- machineconfig/utils/path.py +5 -0
- machineconfig/utils/path_reduced.py +201 -149
- machineconfig/utils/procs.py +23 -23
- machineconfig/utils/scheduling.py +11 -12
- machineconfig/utils/ssh.py +270 -0
- machineconfig/utils/terminal.py +180 -0
- machineconfig/utils/utils.py +1 -2
- machineconfig/utils/utils2.py +43 -0
- machineconfig/utils/utils5.py +163 -34
- machineconfig/utils/ve.py +2 -2
- {machineconfig-1.97.dist-info β machineconfig-2.0.dist-info}/METADATA +13 -8
- {machineconfig-1.97.dist-info β machineconfig-2.0.dist-info}/RECORD +163 -149
- machineconfig/cluster/self_ssh.py +0 -57
- machineconfig/scripts/python/ai/init.py +0 -56
- machineconfig/scripts/python/ai/rules/python/dev.md +0 -31
- {machineconfig-1.97.dist-info β machineconfig-2.0.dist-info}/WHEEL +0 -0
- {machineconfig-1.97.dist-info β machineconfig-2.0.dist-info}/top_level.txt +0 -0
|
@@ -4,6 +4,7 @@ import json
|
|
|
4
4
|
import uuid
|
|
5
5
|
import logging
|
|
6
6
|
import subprocess
|
|
7
|
+
import time
|
|
7
8
|
from pathlib import Path
|
|
8
9
|
from typing import Optional, Dict, List, Any
|
|
9
10
|
|
|
@@ -21,11 +22,11 @@ TMP_SERIALIZATION_DIR = Path.home().joinpath("tmp_results", "session_manager", "
|
|
|
21
22
|
|
|
22
23
|
class ZellijLocalManager:
|
|
23
24
|
"""Manages multiple local zellij sessions and monitors their tabs and processes."""
|
|
24
|
-
|
|
25
|
+
|
|
25
26
|
def __init__(self, session2zellij_tabs: Dict[str, Dict[str, tuple[str, str]]], session_name_prefix: str = "LocalJobMgr"):
|
|
26
27
|
"""
|
|
27
28
|
Initialize the local zellij manager.
|
|
28
|
-
|
|
29
|
+
|
|
29
30
|
Args:
|
|
30
31
|
session2zellij_tabs: Dict mapping session names to their tab configs
|
|
31
32
|
Format: {session_name: {tab_name: (cwd, command), ...}, ...}
|
|
@@ -34,14 +35,14 @@ class ZellijLocalManager:
|
|
|
34
35
|
self.session_name_prefix = session_name_prefix
|
|
35
36
|
self.session2zellij_tabs = session2zellij_tabs # Store the original config
|
|
36
37
|
self.managers: List[ZellijLayoutGenerator] = []
|
|
37
|
-
|
|
38
|
+
|
|
38
39
|
# Create a ZellijLayoutGenerator for each session
|
|
39
40
|
for session_name, tab_config in session2zellij_tabs.items():
|
|
40
41
|
manager = ZellijLayoutGenerator()
|
|
41
42
|
full_session_name = f"{self.session_name_prefix}_{session_name}"
|
|
42
43
|
manager.create_zellij_layout(tab_config=tab_config, session_name=full_session_name)
|
|
43
44
|
self.managers.append(manager)
|
|
44
|
-
|
|
45
|
+
|
|
45
46
|
# Enhanced Rich logging for initialization
|
|
46
47
|
console.print(f"[bold green]π§ Initialized ZellijLocalManager[/bold green] [dim]with[/dim] [bright_green]{len(self.managers)} sessions[/bright_green]")
|
|
47
48
|
|
|
@@ -49,55 +50,73 @@ class ZellijLocalManager:
|
|
|
49
50
|
"""Get all managed session names."""
|
|
50
51
|
return [manager.session_name for manager in self.managers if manager.session_name is not None]
|
|
51
52
|
|
|
52
|
-
def start_all_sessions(self) -> Dict[str, Any]:
|
|
53
|
-
"""Start all zellij sessions with their layouts.
|
|
54
|
-
|
|
53
|
+
def start_all_sessions(self, poll_seconds: float = 5.0, poll_interval: float = 0.25) -> Dict[str, Any]:
|
|
54
|
+
"""Start all zellij sessions with their layouts without blocking on the interactive TUI.
|
|
55
|
+
|
|
56
|
+
Rationale:
|
|
57
|
+
Previous implementation used subprocess.run(... timeout=30) on an "attach" command
|
|
58
|
+
which never returns (interactive) causing a timeout. We now:
|
|
59
|
+
1. Ensure any old session is deleted (best-effort, short timeout)
|
|
60
|
+
2. Launch new session in background with Popen (no wait)
|
|
61
|
+
3. Poll 'zellij list-sessions' to confirm creation
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
poll_seconds: Total seconds to wait for session to appear
|
|
65
|
+
poll_interval: Delay between polls
|
|
66
|
+
Returns:
|
|
67
|
+
Dict mapping session name to success metadata.
|
|
68
|
+
"""
|
|
69
|
+
results: Dict[str, Any] = {}
|
|
55
70
|
for manager in self.managers:
|
|
71
|
+
session_name = manager.session_name
|
|
56
72
|
try:
|
|
57
|
-
session_name = manager.session_name
|
|
58
73
|
if session_name is None:
|
|
59
|
-
continue
|
|
60
|
-
|
|
74
|
+
continue
|
|
61
75
|
layout_path = manager.layout_path
|
|
62
|
-
|
|
63
76
|
if not layout_path:
|
|
64
|
-
results[session_name] = {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
77
|
+
results[session_name] = {"success": False, "error": "No layout file path available"}
|
|
78
|
+
continue
|
|
79
|
+
|
|
80
|
+
# 1. Best-effort delete existing session
|
|
81
|
+
delete_cmd = ["zellij", "delete-session", "--force", session_name]
|
|
82
|
+
try:
|
|
83
|
+
subprocess.run(delete_cmd, capture_output=True, text=True, timeout=5)
|
|
84
|
+
except subprocess.TimeoutExpired:
|
|
85
|
+
logger.warning(f"Timeout deleting session {session_name}; continuing")
|
|
86
|
+
except FileNotFoundError:
|
|
87
|
+
results[session_name] = {"success": False, "error": "'zellij' executable not found in PATH"}
|
|
68
88
|
continue
|
|
69
|
-
|
|
70
|
-
#
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
89
|
+
|
|
90
|
+
# 2. Launch new session. We intentionally do NOT wait for completion.
|
|
91
|
+
# Using the same pattern as before (attach --create) but detached via env var.
|
|
92
|
+
# ZELLIJ_AUTO_ATTACH=0 prevents auto-attach if compiled with that feature; harmless otherwise.
|
|
93
|
+
start_cmd = [
|
|
94
|
+
"bash", "-lc",
|
|
95
|
+
f"ZELLIJ_AUTO_ATTACH=0 zellij --layout {layout_path} attach {session_name} --create >/dev/null 2>&1 &"
|
|
96
|
+
]
|
|
97
|
+
console.print(f"[bold cyan]π Starting session[/bold cyan] [yellow]'{session_name}'[/yellow] with layout [blue]{layout_path}[/blue] (non-blocking)...")
|
|
98
|
+
subprocess.Popen(start_cmd)
|
|
99
|
+
|
|
100
|
+
# 3. Poll for presence
|
|
101
|
+
deadline = time.time() + poll_seconds
|
|
102
|
+
appeared = False
|
|
103
|
+
while time.time() < deadline:
|
|
104
|
+
list_res = subprocess.run(["zellij", "list-sessions"], capture_output=True, text=True)
|
|
105
|
+
if list_res.returncode == 0 and session_name in list_res.stdout:
|
|
106
|
+
appeared = True
|
|
107
|
+
break
|
|
108
|
+
time.sleep(poll_interval)
|
|
109
|
+
|
|
110
|
+
if appeared:
|
|
111
|
+
results[session_name] = {"success": True, "message": f"Session '{session_name}' started"}
|
|
112
|
+
console.print(f"[bold green]β
Session[/bold green] [yellow]'{session_name}'[/yellow] [green]is up[/green]")
|
|
82
113
|
else:
|
|
83
|
-
results[session_name] = {
|
|
84
|
-
|
|
85
|
-
"error": result.stderr or result.stdout
|
|
86
|
-
}
|
|
87
|
-
console.print(f"[bold red]β Failed to start session[/bold red] [yellow]'{session_name}'[/yellow][red]:[/red] [dim]{result.stderr}[/dim]")
|
|
88
|
-
|
|
114
|
+
results[session_name] = {"success": False, "error": "Session did not appear within poll window"}
|
|
115
|
+
console.print(f"[bold red]β Session '{session_name}' did not appear after {poll_seconds:.1f}s[/bold red]")
|
|
89
116
|
except Exception as e:
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
key = getattr(manager, 'session_name', None) or f"manager_{self.managers.index(manager)}"
|
|
93
|
-
except Exception:
|
|
94
|
-
key = f"manager_{self.managers.index(manager)}"
|
|
95
|
-
results[key] = {
|
|
96
|
-
"success": False,
|
|
97
|
-
"error": str(e)
|
|
98
|
-
}
|
|
117
|
+
key = session_name or f"manager_{self.managers.index(manager)}"
|
|
118
|
+
results[key] = {"success": False, "error": str(e)}
|
|
99
119
|
logger.error(f"β Exception starting session '{key}': {e}")
|
|
100
|
-
|
|
101
120
|
return results
|
|
102
121
|
|
|
103
122
|
def kill_all_sessions(self) -> Dict[str, Any]:
|
|
@@ -108,17 +127,17 @@ class ZellijLocalManager:
|
|
|
108
127
|
session_name = manager.session_name
|
|
109
128
|
if session_name is None:
|
|
110
129
|
continue # Skip managers without a session name
|
|
111
|
-
|
|
130
|
+
|
|
112
131
|
cmd = f"zellij delete-session --force {session_name}"
|
|
113
|
-
|
|
132
|
+
|
|
114
133
|
logger.info(f"Killing session '{session_name}'")
|
|
115
134
|
result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=10)
|
|
116
|
-
|
|
135
|
+
|
|
117
136
|
results[session_name] = {
|
|
118
137
|
"success": result.returncode == 0,
|
|
119
138
|
"message": "Session killed" if result.returncode == 0 else result.stderr
|
|
120
139
|
}
|
|
121
|
-
|
|
140
|
+
|
|
122
141
|
except Exception as e:
|
|
123
142
|
# Use a fallback key since session_name might not be defined here
|
|
124
143
|
key = getattr(manager, 'session_name', None) or f"manager_{self.managers.index(manager)}"
|
|
@@ -126,16 +145,16 @@ class ZellijLocalManager:
|
|
|
126
145
|
"success": False,
|
|
127
146
|
"error": str(e)
|
|
128
147
|
}
|
|
129
|
-
|
|
148
|
+
|
|
130
149
|
return results
|
|
131
150
|
|
|
132
151
|
def attach_to_session(self, session_name: Optional[str] = None) -> str:
|
|
133
152
|
"""
|
|
134
153
|
Generate command to attach to a specific session or list attachment commands for all.
|
|
135
|
-
|
|
154
|
+
|
|
136
155
|
Args:
|
|
137
156
|
session_name: Specific session to attach to, or None for all sessions
|
|
138
|
-
|
|
157
|
+
|
|
139
158
|
Returns:
|
|
140
159
|
Command string to attach to session(s)
|
|
141
160
|
"""
|
|
@@ -157,22 +176,22 @@ class ZellijLocalManager:
|
|
|
157
176
|
def check_all_sessions_status(self) -> Dict[str, Dict[str, Any]]:
|
|
158
177
|
"""Check the status of all sessions and their commands."""
|
|
159
178
|
status_report = {}
|
|
160
|
-
|
|
179
|
+
|
|
161
180
|
for manager in self.managers:
|
|
162
181
|
session_name = manager.session_name
|
|
163
182
|
if session_name is None:
|
|
164
183
|
continue # Skip managers without a session name
|
|
165
|
-
|
|
184
|
+
|
|
166
185
|
# Get session status
|
|
167
186
|
session_status = ZellijLayoutGenerator.check_zellij_session_status(session_name)
|
|
168
|
-
|
|
187
|
+
|
|
169
188
|
# Get commands status for this session
|
|
170
189
|
commands_status = manager.check_all_commands_status()
|
|
171
|
-
|
|
190
|
+
|
|
172
191
|
# Calculate summary for this session
|
|
173
192
|
running_count = sum(1 for status in commands_status.values() if status.get("running", False))
|
|
174
193
|
total_count = len(commands_status)
|
|
175
|
-
|
|
194
|
+
|
|
176
195
|
status_report[session_name] = {
|
|
177
196
|
"session_status": session_status,
|
|
178
197
|
"commands_status": commands_status,
|
|
@@ -183,21 +202,21 @@ class ZellijLocalManager:
|
|
|
183
202
|
"session_healthy": session_status.get("session_exists", False)
|
|
184
203
|
}
|
|
185
204
|
}
|
|
186
|
-
|
|
205
|
+
|
|
187
206
|
return status_report
|
|
188
207
|
|
|
189
208
|
def get_global_summary(self) -> Dict[str, Any]:
|
|
190
209
|
"""Get a global summary across all sessions."""
|
|
191
210
|
all_status = self.check_all_sessions_status()
|
|
192
|
-
|
|
211
|
+
|
|
193
212
|
total_sessions = len(all_status)
|
|
194
|
-
healthy_sessions = sum(1 for status in all_status.values()
|
|
213
|
+
healthy_sessions = sum(1 for status in all_status.values()
|
|
195
214
|
if status["summary"]["session_healthy"])
|
|
196
|
-
total_commands = sum(status["summary"]["total_commands"]
|
|
215
|
+
total_commands = sum(status["summary"]["total_commands"]
|
|
197
216
|
for status in all_status.values())
|
|
198
|
-
total_running = sum(status["summary"]["running_commands"]
|
|
217
|
+
total_running = sum(status["summary"]["running_commands"]
|
|
199
218
|
for status in all_status.values())
|
|
200
|
-
|
|
219
|
+
|
|
201
220
|
return {
|
|
202
221
|
"total_sessions": total_sessions,
|
|
203
222
|
"healthy_sessions": healthy_sessions,
|
|
@@ -213,11 +232,11 @@ class ZellijLocalManager:
|
|
|
213
232
|
"""Print a comprehensive status report for all sessions."""
|
|
214
233
|
all_status = self.check_all_sessions_status()
|
|
215
234
|
global_summary = self.get_global_summary()
|
|
216
|
-
|
|
235
|
+
|
|
217
236
|
print("=" * 80)
|
|
218
237
|
print("π ZELLIJ LOCAL MANAGER STATUS REPORT")
|
|
219
238
|
print("=" * 80)
|
|
220
|
-
|
|
239
|
+
|
|
221
240
|
# Global summary
|
|
222
241
|
print("π GLOBAL SUMMARY:")
|
|
223
242
|
print(f" Total sessions: {global_summary['total_sessions']}")
|
|
@@ -226,50 +245,50 @@ class ZellijLocalManager:
|
|
|
226
245
|
print(f" Running commands: {global_summary['running_commands']}")
|
|
227
246
|
print(f" All healthy: {'β
' if global_summary['all_sessions_healthy'] else 'β'}")
|
|
228
247
|
print()
|
|
229
|
-
|
|
248
|
+
|
|
230
249
|
# Per-session details
|
|
231
250
|
for session_name, status in all_status.items():
|
|
232
251
|
session_status = status["session_status"]
|
|
233
252
|
commands_status = status["commands_status"]
|
|
234
253
|
summary = status["summary"]
|
|
235
|
-
|
|
254
|
+
|
|
236
255
|
print(f"π SESSION: {session_name}")
|
|
237
256
|
print("-" * 60)
|
|
238
|
-
|
|
257
|
+
|
|
239
258
|
# Session health
|
|
240
259
|
if session_status.get("session_exists", False):
|
|
241
260
|
print("β
Session is running")
|
|
242
261
|
else:
|
|
243
262
|
print(f"β Session not found: {session_status.get('error', 'Unknown error')}")
|
|
244
|
-
|
|
263
|
+
|
|
245
264
|
# Commands in this session
|
|
246
265
|
print(f" Commands ({summary['running_commands']}/{summary['total_commands']} running):")
|
|
247
266
|
for tab_name, cmd_status in commands_status.items():
|
|
248
267
|
status_icon = "β
" if cmd_status.get("running", False) else "β"
|
|
249
268
|
print(f" {status_icon} {tab_name}: {cmd_status.get('command', 'Unknown')}")
|
|
250
|
-
|
|
269
|
+
|
|
251
270
|
if cmd_status.get("processes"):
|
|
252
271
|
for proc in cmd_status["processes"][:2]: # Show first 2 processes
|
|
253
272
|
print(f" ββ PID {proc['pid']}: {proc['name']} ({proc['status']})")
|
|
254
273
|
print()
|
|
255
|
-
|
|
274
|
+
|
|
256
275
|
print("=" * 80)
|
|
257
276
|
|
|
258
277
|
def run_monitoring_routine(self, wait_ms: int = 30000) -> None:
|
|
259
278
|
"""
|
|
260
279
|
Run a continuous monitoring routine that checks status periodically.
|
|
261
|
-
|
|
280
|
+
|
|
262
281
|
Args:
|
|
263
282
|
wait_ms: How long to wait between checks in milliseconds (default: 30000ms = 30s)
|
|
264
283
|
"""
|
|
265
284
|
def routine(scheduler: Scheduler):
|
|
266
285
|
print(f"\nβ° Monitoring cycle {scheduler.cycle} at {datetime.now()}")
|
|
267
286
|
print("-" * 50)
|
|
268
|
-
|
|
287
|
+
|
|
269
288
|
if scheduler.cycle % 2 == 0:
|
|
270
289
|
# Detailed status check every other cycle
|
|
271
290
|
all_status = self.check_all_sessions_status()
|
|
272
|
-
|
|
291
|
+
|
|
273
292
|
# Create DataFrame for easier viewing
|
|
274
293
|
status_data = []
|
|
275
294
|
for session_name, status in all_status.items():
|
|
@@ -281,7 +300,7 @@ class ZellijLocalManager:
|
|
|
281
300
|
"command": cmd_status.get("command", "Unknown")[:50] + "..." if len(cmd_status.get("command", "")) > 50 else cmd_status.get("command", ""),
|
|
282
301
|
"processes": len(cmd_status.get("processes", []))
|
|
283
302
|
})
|
|
284
|
-
|
|
303
|
+
|
|
285
304
|
if status_data:
|
|
286
305
|
# Format data as table
|
|
287
306
|
if status_data:
|
|
@@ -294,7 +313,7 @@ class ZellijLocalManager:
|
|
|
294
313
|
for row in status_data:
|
|
295
314
|
values = [str(row.get(h, ""))[:15] for h in headers]
|
|
296
315
|
print(" | ".join(f"{v:<15}" for v in values))
|
|
297
|
-
|
|
316
|
+
|
|
298
317
|
# Check if all sessions have stopped
|
|
299
318
|
running_count = sum(1 for row in status_data if row.get("running", False))
|
|
300
319
|
if running_count == 0:
|
|
@@ -307,25 +326,25 @@ class ZellijLocalManager:
|
|
|
307
326
|
# Quick summary check
|
|
308
327
|
global_summary = self.get_global_summary()
|
|
309
328
|
print(f"π Quick Summary: {global_summary['running_commands']}/{global_summary['total_commands']} commands running across {global_summary['healthy_sessions']}/{global_summary['total_sessions']} sessions")
|
|
310
|
-
|
|
329
|
+
|
|
311
330
|
logger.info(f"Starting monitoring routine with {wait_ms}ms intervals")
|
|
312
|
-
sched = Scheduler(routine=routine, wait_ms=wait_ms)
|
|
331
|
+
sched = Scheduler(routine=routine, wait_ms=wait_ms, logger=logger)
|
|
313
332
|
sched.run()
|
|
314
333
|
|
|
315
334
|
def save(self, session_id: Optional[str] = None) -> str:
|
|
316
335
|
"""Save the manager state to disk."""
|
|
317
336
|
if session_id is None:
|
|
318
337
|
session_id = str(uuid.uuid4())[:8]
|
|
319
|
-
|
|
338
|
+
|
|
320
339
|
# Create session directory
|
|
321
340
|
session_dir = TMP_SERIALIZATION_DIR / session_id
|
|
322
341
|
session_dir.mkdir(parents=True, exist_ok=True)
|
|
323
|
-
|
|
342
|
+
|
|
324
343
|
# Save the session2zellij_tabs configuration
|
|
325
344
|
config_file = session_dir / "session2zellij_tabs.json"
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
345
|
+
text = json.dumps(self.session2zellij_tabs, indent=2, ensure_ascii=False)
|
|
346
|
+
config_file.write_text(text, encoding="utf-8")
|
|
347
|
+
|
|
329
348
|
# Save metadata
|
|
330
349
|
metadata = {
|
|
331
350
|
"session_name_prefix": self.session_name_prefix,
|
|
@@ -335,13 +354,13 @@ class ZellijLocalManager:
|
|
|
335
354
|
"manager_type": "ZellijLocalManager"
|
|
336
355
|
}
|
|
337
356
|
metadata_file = session_dir / "metadata.json"
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
357
|
+
text = json.dumps(metadata, indent=2, ensure_ascii=False)
|
|
358
|
+
metadata_file.write_text(text, encoding="utf-8")
|
|
359
|
+
|
|
341
360
|
# Save each manager's state
|
|
342
361
|
managers_dir = session_dir / "managers"
|
|
343
362
|
managers_dir.mkdir(exist_ok=True)
|
|
344
|
-
|
|
363
|
+
|
|
345
364
|
for i, manager in enumerate(self.managers):
|
|
346
365
|
manager_data = {
|
|
347
366
|
"session_name": manager.session_name,
|
|
@@ -349,9 +368,9 @@ class ZellijLocalManager:
|
|
|
349
368
|
"layout_path": manager.layout_path
|
|
350
369
|
}
|
|
351
370
|
manager_file = managers_dir / f"manager_{i}_{manager.session_name}.json"
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
371
|
+
text = json.dumps(manager_data, indent=2, ensure_ascii=False)
|
|
372
|
+
manager_file.write_text(text, encoding="utf-8")
|
|
373
|
+
|
|
355
374
|
logger.info(f"β
Saved ZellijLocalManager session to: {session_dir}")
|
|
356
375
|
return session_id
|
|
357
376
|
|
|
@@ -359,18 +378,18 @@ class ZellijLocalManager:
|
|
|
359
378
|
def load(cls, session_id: str) -> 'ZellijLocalManager':
|
|
360
379
|
"""Load a saved manager state from disk."""
|
|
361
380
|
session_dir = TMP_SERIALIZATION_DIR / session_id
|
|
362
|
-
|
|
381
|
+
|
|
363
382
|
if not session_dir.exists():
|
|
364
383
|
raise FileNotFoundError(f"Session directory not found: {session_dir}")
|
|
365
|
-
|
|
384
|
+
|
|
366
385
|
# Load configuration
|
|
367
386
|
config_file = session_dir / "session2zellij_tabs.json"
|
|
368
387
|
if not config_file.exists():
|
|
369
388
|
raise FileNotFoundError(f"Configuration file not found: {config_file}")
|
|
370
|
-
|
|
389
|
+
|
|
371
390
|
with open(config_file, 'r', encoding='utf-8') as f:
|
|
372
391
|
session2zellij_tabs = json.load(f)
|
|
373
|
-
|
|
392
|
+
|
|
374
393
|
# Load metadata
|
|
375
394
|
metadata_file = session_dir / "metadata.json"
|
|
376
395
|
session_name_prefix = "LocalJobMgr" # default fallback
|
|
@@ -378,32 +397,32 @@ class ZellijLocalManager:
|
|
|
378
397
|
with open(metadata_file, 'r', encoding='utf-8') as f:
|
|
379
398
|
metadata = json.load(f)
|
|
380
399
|
session_name_prefix = metadata.get("session_name_prefix", "LocalJobMgr")
|
|
381
|
-
|
|
400
|
+
|
|
382
401
|
# Create new instance
|
|
383
402
|
instance = cls(session2zellij_tabs=session2zellij_tabs, session_name_prefix=session_name_prefix)
|
|
384
|
-
|
|
403
|
+
|
|
385
404
|
# Load saved manager states
|
|
386
405
|
managers_dir = session_dir / "managers"
|
|
387
406
|
if managers_dir.exists():
|
|
388
407
|
instance.managers = []
|
|
389
408
|
manager_files = sorted(managers_dir.glob("manager_*.json"))
|
|
390
|
-
|
|
409
|
+
|
|
391
410
|
for manager_file in manager_files:
|
|
392
411
|
try:
|
|
393
412
|
with open(manager_file, 'r', encoding='utf-8') as f:
|
|
394
413
|
manager_data = json.load(f)
|
|
395
|
-
|
|
414
|
+
|
|
396
415
|
# Recreate the manager
|
|
397
416
|
manager = ZellijLayoutGenerator()
|
|
398
417
|
manager.session_name = manager_data["session_name"]
|
|
399
418
|
manager.tab_config = manager_data["tab_config"]
|
|
400
419
|
manager.layout_path = manager_data["layout_path"]
|
|
401
|
-
|
|
420
|
+
|
|
402
421
|
instance.managers.append(manager)
|
|
403
|
-
|
|
422
|
+
|
|
404
423
|
except Exception as e:
|
|
405
424
|
logger.warning(f"Failed to load manager from {manager_file}: {e}")
|
|
406
|
-
|
|
425
|
+
|
|
407
426
|
logger.info(f"β
Loaded ZellijLocalManager session from: {session_dir}")
|
|
408
427
|
return instance
|
|
409
428
|
|
|
@@ -412,23 +431,23 @@ class ZellijLocalManager:
|
|
|
412
431
|
"""List all saved session IDs."""
|
|
413
432
|
if not TMP_SERIALIZATION_DIR.exists():
|
|
414
433
|
return []
|
|
415
|
-
|
|
434
|
+
|
|
416
435
|
sessions = []
|
|
417
436
|
for item in TMP_SERIALIZATION_DIR.iterdir():
|
|
418
437
|
if item.is_dir() and (item / "metadata.json").exists():
|
|
419
438
|
sessions.append(item.name)
|
|
420
|
-
|
|
439
|
+
|
|
421
440
|
return sorted(sessions)
|
|
422
441
|
|
|
423
442
|
@staticmethod
|
|
424
443
|
def delete_session(session_id: str) -> bool:
|
|
425
444
|
"""Delete a saved session."""
|
|
426
445
|
session_dir = TMP_SERIALIZATION_DIR / session_id
|
|
427
|
-
|
|
446
|
+
|
|
428
447
|
if not session_dir.exists():
|
|
429
448
|
logger.warning(f"Session directory not found: {session_dir}")
|
|
430
449
|
return False
|
|
431
|
-
|
|
450
|
+
|
|
432
451
|
try:
|
|
433
452
|
import shutil
|
|
434
453
|
shutil.rmtree(session_dir)
|
|
@@ -441,7 +460,7 @@ class ZellijLocalManager:
|
|
|
441
460
|
def list_active_sessions(self) -> List[Dict[str, Any]]:
|
|
442
461
|
"""List currently active zellij sessions managed by this instance."""
|
|
443
462
|
active_sessions = []
|
|
444
|
-
|
|
463
|
+
|
|
445
464
|
try:
|
|
446
465
|
# Get all running zellij sessions
|
|
447
466
|
result = subprocess.run(
|
|
@@ -450,27 +469,27 @@ class ZellijLocalManager:
|
|
|
450
469
|
text=True,
|
|
451
470
|
timeout=10
|
|
452
471
|
)
|
|
453
|
-
|
|
472
|
+
|
|
454
473
|
if result.returncode == 0:
|
|
455
474
|
all_sessions = result.stdout.strip().split('\n') if result.stdout.strip() else []
|
|
456
|
-
|
|
475
|
+
|
|
457
476
|
# Filter to only our managed sessions
|
|
458
477
|
for manager in self.managers:
|
|
459
478
|
session_name = manager.session_name
|
|
460
479
|
if session_name is None:
|
|
461
480
|
continue # Skip managers without a session name
|
|
462
481
|
is_active = any(session_name in session for session in all_sessions)
|
|
463
|
-
|
|
482
|
+
|
|
464
483
|
active_sessions.append({
|
|
465
484
|
"session_name": session_name,
|
|
466
485
|
"is_active": is_active,
|
|
467
486
|
"tab_count": len(manager.tab_config),
|
|
468
487
|
"tabs": list(manager.tab_config.keys())
|
|
469
488
|
})
|
|
470
|
-
|
|
489
|
+
|
|
471
490
|
except Exception as e:
|
|
472
491
|
logger.error(f"Error listing active sessions: {e}")
|
|
473
|
-
|
|
492
|
+
|
|
474
493
|
return active_sessions
|
|
475
494
|
|
|
476
495
|
|
|
@@ -493,41 +512,41 @@ if __name__ == "__main__":
|
|
|
493
512
|
"πMetrics": ("~", "k9s")
|
|
494
513
|
}
|
|
495
514
|
}
|
|
496
|
-
|
|
515
|
+
|
|
497
516
|
try:
|
|
498
517
|
# Create the local manager
|
|
499
518
|
manager = ZellijLocalManager(sample_sessions, session_name_prefix="DevEnv")
|
|
500
519
|
print(f"β
Local manager created with {len(manager.managers)} sessions")
|
|
501
|
-
|
|
520
|
+
|
|
502
521
|
# Show session names
|
|
503
522
|
print(f"π Sessions: {manager.get_all_session_names()}")
|
|
504
|
-
|
|
523
|
+
|
|
505
524
|
# Print attachment commands (without actually starting)
|
|
506
525
|
print("\nπ Attachment commands:")
|
|
507
526
|
print(manager.attach_to_session())
|
|
508
|
-
|
|
527
|
+
|
|
509
528
|
# Show current status
|
|
510
529
|
print("\nπ Current status:")
|
|
511
530
|
manager.print_status_report()
|
|
512
|
-
|
|
531
|
+
|
|
513
532
|
# Demonstrate save/load
|
|
514
533
|
print("\nπΎ Demonstrating save/load...")
|
|
515
534
|
session_id = manager.save()
|
|
516
535
|
print(f"β
Saved session: {session_id}")
|
|
517
|
-
|
|
536
|
+
|
|
518
537
|
# List saved sessions
|
|
519
538
|
saved_sessions = ZellijLocalManager.list_saved_sessions()
|
|
520
539
|
print(f"π Saved sessions: {saved_sessions}")
|
|
521
|
-
|
|
540
|
+
|
|
522
541
|
# Load and verify
|
|
523
542
|
loaded_manager = ZellijLocalManager.load(session_id)
|
|
524
543
|
print(f"β
Loaded session with {len(loaded_manager.managers)} sessions")
|
|
525
|
-
|
|
544
|
+
|
|
526
545
|
# Show how to start monitoring (commented out to prevent infinite loop in demo)
|
|
527
546
|
print("\nβ° To start monitoring, run:")
|
|
528
547
|
print("manager.run_monitoring_routine(wait_ms=30000) # 30 seconds")
|
|
529
|
-
|
|
548
|
+
|
|
530
549
|
except Exception as e:
|
|
531
550
|
print(f"β Error: {e}")
|
|
532
551
|
import traceback
|
|
533
|
-
traceback.print_exc()
|
|
552
|
+
traceback.print_exc()
|