diff --git a/docs/nordvpn-client.md b/docs/nordvpn-client.md index 2a08833..2c51d65 100644 --- a/docs/nordvpn-client.md +++ b/docs/nordvpn-client.md @@ -35,7 +35,7 @@ node skills/nordvpn-client/scripts/nordvpn-client.js disconnect - `NORDVPN_TOKEN` or `NORDVPN_TOKEN_FILE` - `wireguard-go` - `wireguard-tools` - - non-interactive `sudo` for `wg-quick` + - non-interactive `sudo` for `~/.openclaw/workspace/skills/nordvpn-client/scripts/nordvpn-wireguard-helper.sh` - `NordVPN.app` may stay installed but is only the manual fallback - the app login is not reused by the automated WireGuard backend diff --git a/skills/nordvpn-client/SKILL.md b/skills/nordvpn-client/SKILL.md index eeb3a19..c63b790 100644 --- a/skills/nordvpn-client/SKILL.md +++ b/skills/nordvpn-client/SKILL.md @@ -96,6 +96,6 @@ For an automated macOS flow: - `NORDVPN_TOKEN` or `NORDVPN_TOKEN_FILE` - `wireguard-go` - `wireguard-tools` - - non-interactive `sudo` for `wg-quick` + - non-interactive `sudo` for `~/.openclaw/workspace/skills/nordvpn-client/scripts/nordvpn-wireguard-helper.sh` - `NordVPN.app` login on macOS is not reused by the WireGuard backend. - The Homebrew `nordvpn` app does not need to be uninstalled. It can coexist with the WireGuard backend. diff --git a/skills/nordvpn-client/scripts/nordvpn-client.js b/skills/nordvpn-client/scripts/nordvpn-client.js index 6e19afe..0eabc20 100644 --- a/skills/nordvpn-client/scripts/nordvpn-client.js +++ b/skills/nordvpn-client/scripts/nordvpn-client.js @@ -22,6 +22,15 @@ const OPENCLAW_NORDVPN_CREDENTIALS_DIR = path.join( ); const DEFAULT_TOKEN_FILE = path.join(OPENCLAW_NORDVPN_CREDENTIALS_DIR, "token.txt"); const DEFAULT_PASSWORD_FILE = path.join(OPENCLAW_NORDVPN_CREDENTIALS_DIR, "password.txt"); +const MAC_WG_HELPER_PATH = path.join( + os.homedir(), + ".openclaw", + "workspace", + "skills", + "nordvpn-client", + "scripts", + "nordvpn-wireguard-helper.sh" +); const DEFAULT_DNS_IPV4 = "103.86.96.100"; const DEFAULT_DNS_IPV6 = "2400:bb40:4444::100"; const CLIENT_IPV4 = "10.5.0.2"; @@ -274,7 +283,8 @@ async function probeMacWireguard() { const wgPath = commandExists("wg"); const wgQuickPath = commandExists("wg-quick"); const wireguardGoPath = commandExists("wireguard-go"); - const sudoProbe = await runExec("sudo", ["-n", "true"]); + const helperPath = fileExists(MAC_WG_HELPER_PATH) ? MAC_WG_HELPER_PATH : null; + const sudoProbe = helperPath ? await runExec("sudo", ["-n", helperPath, "probe"]) : { ok: false }; let active = false; let showRaw = ""; let endpoint = ""; @@ -293,6 +303,7 @@ async function probeMacWireguard() { wgPath: wgPath || null, wgQuickPath: wgQuickPath || null, wireguardGoPath: wireguardGoPath || null, + helperPath, dependenciesReady: Boolean(wgPath && wgQuickPath && wireguardGoPath), sudoReady: sudoProbe.ok, interfaceName: MAC_WG_INTERFACE, @@ -390,7 +401,7 @@ function buildStateSummary(installProbe, ipInfo) { recommendedAction = installProbe.tokenAvailable ? installProbe.wireguard.sudoReady ? "Use token-based WireGuard automation on macOS." - : "WireGuard tooling and token are available, but connect/disconnect require non-interactive sudo for wg-quick. Authorize sudo for wg-quick, then rerun login/connect." + : `WireGuard tooling and token are available, but connect/disconnect require non-interactive sudo for ${MAC_WG_HELPER_PATH}. Allow that helper in sudoers, then rerun login/connect.` : `Set NORDVPN_TOKEN, NORDVPN_TOKEN_FILE, or place the token at ${DEFAULT_TOKEN_FILE} for automated macOS NordLynx/WireGuard connects.`; } else if (installProbe.platform === "darwin" && installProbe.appInstalled) { controlMode = "app-manual"; @@ -436,6 +447,7 @@ function buildStateSummary(installProbe, ipInfo) { wgPath: installProbe.wireguard.wgPath, wgQuickPath: installProbe.wireguard.wgQuickPath, wireguardGoPath: installProbe.wireguard.wireguardGoPath, + helperPath: installProbe.wireguard.helperPath, authCache: installProbe.wireguard.authCache, lastConnection: installProbe.wireguard.lastConnection, } @@ -769,12 +781,12 @@ async function connectViaCli(cliPath, target) { } async function runSudoWireguard(installProbe, action) { - const wgQuickPath = installProbe.wireguard && installProbe.wireguard.wgQuickPath; - if (!wgQuickPath) throw new Error("wg-quick is not installed."); + const helperPath = installProbe.wireguard && installProbe.wireguard.helperPath; + if (!helperPath) throw new Error(`WireGuard helper is missing at ${MAC_WG_HELPER_PATH}.`); if (!installProbe.wireguard.sudoReady) { - throw new Error("Non-interactive sudo is required for macOS WireGuard connect/disconnect. Authorize sudo first, then retry."); + throw new Error(`Non-interactive sudo is required for macOS WireGuard connect/disconnect. Allow sudo for ${MAC_WG_HELPER_PATH}, then rerun login/connect.`); } - return runExec("sudo", ["-n", "env", `PATH=${process.env.PATH || ""}`, wgQuickPath, action, WG_CONFIG_PATH]); + return runExec("sudo", ["-n", helperPath, action]); } async function connectViaMacWireguard(installProbe, target) { diff --git a/skills/nordvpn-client/scripts/nordvpn-wireguard-helper.sh b/skills/nordvpn-client/scripts/nordvpn-wireguard-helper.sh new file mode 100755 index 0000000..294e981 --- /dev/null +++ b/skills/nordvpn-client/scripts/nordvpn-wireguard-helper.sh @@ -0,0 +1,24 @@ +#!/bin/sh +set -eu + +ACTION="${1:-}" +case "$ACTION" in + probe|up|down) + ;; + *) + echo "Usage: nordvpn-wireguard-helper.sh [probe|up|down]" >&2 + exit 2 + ;; +esac + +WG_QUICK="/opt/homebrew/bin/wg-quick" +WG_CONFIG="/Users/stefano/.nordvpn-client/wireguard/nordvpnctl.conf" +PATH="/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin" +export PATH + +if [ "$ACTION" = "probe" ]; then + test -x "$WG_QUICK" + exit 0 +fi + +exec "$WG_QUICK" "$ACTION" "$WG_CONFIG"