feat: add default nordvpn credential paths

This commit is contained in:
Stefano Fiorini
2026-03-12 00:09:56 -05:00
parent 78be4fc600
commit 045cf6aad2
3 changed files with 51 additions and 14 deletions

View File

@@ -42,7 +42,7 @@ node skills/nordvpn-client/scripts/nordvpn-client.js disconnect
Quick start:
1. `node skills/nordvpn-client/scripts/nordvpn-client.js install`
2. set `NORDVPN_TOKEN` or `NORDVPN_TOKEN_FILE`
2. put your token in `~/.openclaw/workspace/.clawdbot/credentials/nordvpn/token.txt` or set `NORDVPN_TOKEN` / `NORDVPN_TOKEN_FILE`
3. `node skills/nordvpn-client/scripts/nordvpn-client.js login`
4. `node skills/nordvpn-client/scripts/nordvpn-client.js connect --country "Italy"` or `--city "Milan"`
5. `node skills/nordvpn-client/scripts/nordvpn-client.js verify`
@@ -65,6 +65,11 @@ Supported env vars:
Do not put secrets in the skill docs or repo.
Default OpenClaw credential paths:
- token: `~/.openclaw/workspace/.clawdbot/credentials/nordvpn/token.txt`
- password: `~/.openclaw/workspace/.clawdbot/credentials/nordvpn/password.txt`
## Verification model
`status`, `verify`, and `connect` emit JSON suitable for agent use:

View File

@@ -59,6 +59,11 @@ Optional credential file env vars:
- `NORDVPN_TOKEN_FILE`
- `NORDVPN_PASSWORD_FILE`
Default OpenClaw credential paths:
- token: `~/.openclaw/workspace/.clawdbot/credentials/nordvpn/token.txt`
- password: `~/.openclaw/workspace/.clawdbot/credentials/nordvpn/password.txt`
## Verification Behavior
`status`, `verify`, and `connect` report machine-readable JSON including:
@@ -79,7 +84,7 @@ Use `verify` when you want an explicit post-connect location check without chang
For an automated macOS flow:
1. `node scripts/nordvpn-client.js install`
2. set `NORDVPN_TOKEN` or `NORDVPN_TOKEN_FILE`
2. put your token in `~/.openclaw/workspace/.clawdbot/credentials/nordvpn/token.txt` or set `NORDVPN_TOKEN` / `NORDVPN_TOKEN_FILE`
3. `node scripts/nordvpn-client.js login`
4. `node scripts/nordvpn-client.js connect --country "Italy"` or `--city "Milan"`
5. `node scripts/nordvpn-client.js verify`

View File

@@ -12,6 +12,16 @@ const WG_STATE_DIR = path.join(STATE_DIR, "wireguard");
const WG_CONFIG_PATH = path.join(WG_STATE_DIR, `${MAC_WG_INTERFACE}.conf`);
const AUTH_CACHE_PATH = path.join(STATE_DIR, "auth.json");
const LAST_CONNECTION_PATH = path.join(STATE_DIR, "last-connection.json");
const OPENCLAW_NORDVPN_CREDENTIALS_DIR = path.join(
os.homedir(),
".openclaw",
"workspace",
".clawdbot",
"credentials",
"nordvpn"
);
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 DEFAULT_DNS_IPV4 = "103.86.96.100";
const DEFAULT_DNS_IPV6 = "2400:bb40:4444::100";
const CLIENT_IPV4 = "10.5.0.2";
@@ -39,9 +49,11 @@ function usage() {
env: [
"NORDVPN_TOKEN",
"NORDVPN_TOKEN_FILE",
`default token file: ${DEFAULT_TOKEN_FILE}`,
"NORDVPN_USERNAME",
"NORDVPN_PASSWORD",
"NORDVPN_PASSWORD_FILE",
`default password file: ${DEFAULT_PASSWORD_FILE}`,
],
};
}
@@ -75,13 +87,22 @@ function detectPlatform() {
function readSecret(envName, fileEnvName) {
if (process.env[envName]) return process.env[envName];
const filePath = process.env[fileEnvName];
if (!filePath) return "";
const candidates = [];
if (process.env[fileEnvName]) candidates.push(process.env[fileEnvName]);
if (envName === "NORDVPN_TOKEN") candidates.push(DEFAULT_TOKEN_FILE);
if (envName === "NORDVPN_PASSWORD") candidates.push(DEFAULT_PASSWORD_FILE);
for (const candidate of candidates) {
try {
return fs.readFileSync(filePath, "utf8").trim();
const value = fs.readFileSync(candidate, "utf8").trim();
if (value) return value;
} catch {
return "";
// Continue.
}
}
return "";
}
function normalizeLocation(value) {
@@ -370,7 +391,7 @@ function buildStateSummary(installProbe, ipInfo) {
? 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."
: "Set NORDVPN_TOKEN or NORDVPN_TOKEN_FILE for automated macOS NordLynx/WireGuard connects.";
: `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";
automaticControl = false;
@@ -378,7 +399,7 @@ function buildStateSummary(installProbe, ipInfo) {
connectMode = "app-manual";
recommendedAction = installProbe.tokenAvailable
? "NordVPN.app is installed, but automated macOS connects also require wireguard-go and wireguard-tools. Run 'node scripts/nordvpn-client.js install' to install them with Homebrew."
: "NordVPN.app is installed. For automated macOS connects, run 'node scripts/nordvpn-client.js install' to install wireguard-go and wireguard-tools with Homebrew, then set NORDVPN_TOKEN or NORDVPN_TOKEN_FILE and run login.";
: `NordVPN.app is installed. For automated macOS connects, run 'node scripts/nordvpn-client.js install' to install wireguard-go and wireguard-tools with Homebrew, then set NORDVPN_TOKEN, NORDVPN_TOKEN_FILE, or place the token at ${DEFAULT_TOKEN_FILE} and run login.`;
} else if (installProbe.platform === "darwin" && installProbe.brewPath) {
controlMode = "wireguard-bootstrap";
automaticControl = false;
@@ -759,7 +780,7 @@ async function runSudoWireguard(installProbe, action) {
async function connectViaMacWireguard(installProbe, target) {
const token = readSecret("NORDVPN_TOKEN", "NORDVPN_TOKEN_FILE");
if (!token) {
throw new Error("macOS NordLynx/WireGuard automation requires NORDVPN_TOKEN or NORDVPN_TOKEN_FILE.");
throw new Error(`macOS NordLynx/WireGuard automation requires NORDVPN_TOKEN, NORDVPN_TOKEN_FILE, or a token at ${DEFAULT_TOKEN_FILE}.`);
}
if (!installProbe.wireguard || !installProbe.wireguard.dependenciesReady) {
throw new Error("wireguard-go and wireguard-tools are required on macOS. Run install first.");
@@ -912,7 +933,13 @@ async function loginNordvpn(installProbe) {
backend: "wireguard",
validatedAt: new Date().toISOString(),
hasNordlynxPrivateKey: Boolean(credentials.nordlynx_private_key),
tokenSource: process.env.NORDVPN_TOKEN ? "env:NORDVPN_TOKEN" : process.env.NORDVPN_TOKEN_FILE ? "file:NORDVPN_TOKEN_FILE" : "unknown",
tokenSource: process.env.NORDVPN_TOKEN
? "env:NORDVPN_TOKEN"
: process.env.NORDVPN_TOKEN_FILE
? "file:NORDVPN_TOKEN_FILE"
: fileExists(DEFAULT_TOKEN_FILE)
? `default:${DEFAULT_TOKEN_FILE}`
: "unknown",
};
writeJsonFile(AUTH_CACHE_PATH, cache);
return {
@@ -932,7 +959,7 @@ async function loginNordvpn(installProbe) {
backend: "app-manual",
manualActionRequired: true,
message:
"Opened NordVPN.app. Complete login in the app/browser flow, or set NORDVPN_TOKEN / NORDVPN_TOKEN_FILE to use the automated macOS WireGuard backend.",
`Opened NordVPN.app. Complete login in the app/browser flow, or set NORDVPN_TOKEN, NORDVPN_TOKEN_FILE, or place the token at ${DEFAULT_TOKEN_FILE} to use the automated macOS WireGuard backend.`,
};
}
@@ -1007,7 +1034,7 @@ async function main() {
} else if (platform === "darwin" && installProbe.appInstalled) {
connectResult = await connectViaMacApp(target);
} else if (platform === "darwin" && !installProbe.tokenAvailable) {
throw new Error("macOS automated NordLynx/WireGuard connects require NORDVPN_TOKEN or NORDVPN_TOKEN_FILE.");
throw new Error(`macOS automated NordLynx/WireGuard connects require NORDVPN_TOKEN, NORDVPN_TOKEN_FILE, or a token at ${DEFAULT_TOKEN_FILE}.`);
} else if (platform === "darwin") {
throw new Error("No usable macOS NordVPN backend is ready. Run install to bootstrap WireGuard tooling.");
} else if (!installProbe.installed) {