v1.5.0: network interface binding, SSH fixes, terminal, release script

- Add network interface selection per server (VPN/multi-NIC support)
- Fix "Install Everything" button hanging on error
- Add interactive SSH terminal with PTY (pyte + xterm-256color)
- Add release.py for automated versioning and changelog generation
- Add CLAUDE.md with project instructions
- Add screenshots and release binaries for v1.1–v1.4

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
chrome-storm-c442
2026-02-23 14:06:41 -05:00
parent 0c89e77417
commit a83a97c9d5
33 changed files with 1221 additions and 173 deletions

View File

@@ -9,6 +9,7 @@ from tkinter import filedialog, messagebox
import customtkinter as ctk
from core.claude_setup import check_status, install_all, install_ssh_script, install_skill, generate_ssh_key
from core.i18n import t
from core.logger import log
class SetupTab(ctk.CTkFrame):
@@ -88,6 +89,35 @@ class SetupTab(ctk.CTkFrame):
command=self._refresh_status)
self.refresh_btn.pack(side="right")
# ── Monitoring section ─────────────────────────
monitor_frame = ctk.CTkFrame(self)
monitor_frame.pack(fill="x", padx=20, pady=(5, 5))
self.monitor_title = ctk.CTkLabel(
monitor_frame, text=t("monitoring"),
font=ctk.CTkFont(size=14, weight="bold"), anchor="w"
)
self.monitor_title.pack(fill="x", padx=15, pady=(10, 5))
interval_row = ctk.CTkFrame(monitor_frame, fg_color="transparent")
interval_row.pack(fill="x", padx=15, pady=(0, 10))
self.interval_label = ctk.CTkLabel(interval_row, text=t("check_interval"), anchor="w")
self.interval_label.pack(side="left", padx=(0, 10))
self._interval_buttons: dict[int, ctk.CTkButton] = {}
current_interval = store.get_check_interval()
for seconds, key in [(30, "interval_30s"), (60, "interval_60s"), (120, "interval_120s"), (300, "interval_300s")]:
is_active = (seconds == current_interval)
btn = ctk.CTkButton(
interval_row, text=t(key), width=60, height=28,
fg_color="#3b82f6" if is_active else "#6b7280",
hover_color="#2563eb" if is_active else "#4b5563",
command=lambda s=seconds: self._set_interval(s)
)
btn.pack(side="left", padx=2)
self._interval_buttons[seconds] = btn
# ── Configuration section ─────────────────────
config_frame = ctk.CTkFrame(self)
config_frame.pack(fill="x", padx=20, pady=(5, 5))
@@ -148,6 +178,14 @@ class SetupTab(ctk.CTkFrame):
# Initial status check
self._refresh_status()
def _set_interval(self, seconds: int):
self.store.set_check_interval(seconds)
for s, btn in self._interval_buttons.items():
if s == seconds:
btn.configure(fg_color="#3b82f6", hover_color="#2563eb")
else:
btn.configure(fg_color="#6b7280", hover_color="#4b5563")
def _log(self, text: str):
self.log.configure(state="normal")
self.log.insert("end", text + "\n")
@@ -166,12 +204,17 @@ class SetupTab(ctk.CTkFrame):
self.install_all_btn.configure(state="disabled", text=t("installing_all"))
def _do():
results = install_all()
for msg in results:
self.after(0, lambda m=msg: self._log(m))
self.after(0, self._refresh_status)
self.after(0, lambda: self._log("\n" + t("install_done")))
self.after(0, lambda: self.install_all_btn.configure(state="normal", text=t("install_everything")))
try:
results = install_all()
for msg in results:
self.after(0, lambda m=msg: self._log(m))
self.after(0, self._refresh_status)
self.after(0, lambda: self._log("\n" + t("install_done")))
except Exception as e:
log.error(f"install_all failed: {e}")
self.after(0, lambda: self._log(f"ERROR: {e}"))
finally:
self.after(0, lambda: self.install_all_btn.configure(state="normal", text=t("install_everything")))
threading.Thread(target=_do, daemon=True).start()
@@ -186,8 +229,12 @@ class SetupTab(ctk.CTkFrame):
self._refresh_status()
def _gen_key(self):
msg = generate_ssh_key()
self._log(msg)
try:
msg = generate_ssh_key()
self._log(msg)
except Exception as e:
log.error(f"generate_ssh_key failed: {e}")
self._log(f"ERROR: {e}")
self._refresh_status()
# ── Configuration methods ─────────────────────────