Sync from upstream: ncp is Python now, runs on Windows too
management/ncp.py replaces the bash CLI's logic; nexus-cli.sh and the new ncp.ps1 are thin wrappers, so Linux keeps its entry point and Windows gains one. psutil handles process and port work on both platforms. install-windows.ps1 registers ncp in the PowerShell profile. The panel VPN switch now resolves its WireGuard connection through NetworkManager instead of a hardcoded name, and the .ps1 ASCII guard globs rather than naming files. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -59,6 +59,21 @@ def nmcli(*args):
|
||||
# load failed and each SSID row fell back to a "?" label.
|
||||
ICON_DIR = str(Path(__file__).resolve().parent.parent.parent / "assets" / "panel-icons")
|
||||
|
||||
def wg_connection():
|
||||
"""(name, is_up) for the WireGuard tunnel, or (None, False) if there is
|
||||
none. Asked of NetworkManager rather than hardcoded: this was a literal
|
||||
'wgs_client', so reissuing the tunnel under a new name left the switch
|
||||
silently dead - it read Disconnected forever and only errored on click.
|
||||
Prefers an active tunnel, else the first configured one."""
|
||||
wgs = [p for p in (line.split(":") for line
|
||||
in nmcli("-f", "NAME,TYPE,STATE", "connection", "show").splitlines())
|
||||
if len(p) >= 3 and p[1] == "wireguard"]
|
||||
for name, _, state in wgs:
|
||||
if state == "activated":
|
||||
return name, True
|
||||
return (wgs[0][0], False) if wgs else (None, False)
|
||||
|
||||
|
||||
def signal_icon(sig):
|
||||
if sig >= 80: return f"{ICON_DIR}/network-wireless-signal-excellent.png"
|
||||
if sig >= 60: return f"{ICON_DIR}/network-wireless-signal-good.png"
|
||||
@@ -73,6 +88,7 @@ class NetworkPopup:
|
||||
self._click_x = None
|
||||
self.vpn_switch = None
|
||||
self.vpn_status_lbl = None
|
||||
self.vpn_conn = None
|
||||
self._build_window()
|
||||
|
||||
def _on_sig(s, f):
|
||||
@@ -201,12 +217,17 @@ class NetworkPopup:
|
||||
self._vpn_handler = self.vpn_switch.connect('state-set', self._on_vpn_toggle)
|
||||
vpn_row.pack_start(self.vpn_switch, False, False, 0)
|
||||
|
||||
# Set switch state immediately from sysfs — no nmcli round-trip needed
|
||||
vpn_up = os.path.exists('/sys/class/net/wgs_client')
|
||||
# show() rebuilds this content every time the popup opens, so the
|
||||
# tunnel is re-resolved each time rather than cached at startup.
|
||||
self.vpn_conn, vpn_up = wg_connection()
|
||||
self.vpn_switch.handler_block(self._vpn_handler)
|
||||
self.vpn_switch.set_active(vpn_up)
|
||||
self.vpn_switch.set_sensitive(self.vpn_conn is not None)
|
||||
self.vpn_switch.handler_unblock(self._vpn_handler)
|
||||
self.vpn_status_lbl.set_text("Connected" if vpn_up else "Disconnected")
|
||||
self.vpn_status_lbl.set_text(
|
||||
"Connected" if vpn_up
|
||||
else "Disconnected" if self.vpn_conn
|
||||
else "No tunnel configured")
|
||||
|
||||
outer.pack_start(vpn_row, False, False, 0)
|
||||
outer.pack_start(Gtk.Separator(), False, False, 0)
|
||||
@@ -343,10 +364,15 @@ class NetworkPopup:
|
||||
return False # let GTK move the switch immediately; revert on failure
|
||||
|
||||
def _do_vpn_toggle(self, enable):
|
||||
if not self.vpn_conn:
|
||||
subprocess.Popen(["notify-send", "-u", "critical", "WireGuard",
|
||||
"No WireGuard connection configured"])
|
||||
GLib.idle_add(self._apply_vpn_state, False, "No tunnel configured")
|
||||
return
|
||||
action = "up" if enable else "down"
|
||||
try:
|
||||
subprocess.check_output(
|
||||
["nmcli", "connection", action, "wgs_client"],
|
||||
["nmcli", "connection", action, self.vpn_conn],
|
||||
stderr=subprocess.STDOUT, text=True
|
||||
)
|
||||
label = "Connected" if enable else "Disconnected"
|
||||
|
||||
Reference in New Issue
Block a user