Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ca33b2d74a | |||
| d0c50f5d8a | |||
| a8a285b356 | |||
| 045cf6aad2 | |||
| 78be4fc600 | |||
| d1b4d58c5d | |||
| 4a539a33c9 | |||
| b326153d26 | |||
| 2612cef1dc | |||
| 120721bbc6 | |||
| fe5b4659fe |
@@ -18,6 +18,7 @@ This repository contains practical OpenClaw skills and companion integrations. I
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `elevenlabs-stt` | Transcribe local audio files with ElevenLabs Speech-to-Text, with diarization, language hints, event tags, and JSON output. | `skills/elevenlabs-stt` |
|
| `elevenlabs-stt` | Transcribe local audio files with ElevenLabs Speech-to-Text, with diarization, language hints, event tags, and JSON output. | `skills/elevenlabs-stt` |
|
||||||
| `gitea-api` | Interact with Gitea via REST API (repos, issues, PRs, releases, branches, user info). | `skills/gitea-api` |
|
| `gitea-api` | Interact with Gitea via REST API (repos, issues, PRs, releases, branches, user info). | `skills/gitea-api` |
|
||||||
|
| `nordvpn-client` | Install, log in to, connect, disconnect, and verify NordVPN sessions across Linux CLI and macOS NordLynx/WireGuard backends. | `skills/nordvpn-client` |
|
||||||
| `portainer` | Manage Portainer stacks via API (list, start/stop/restart, update, prune images). | `skills/portainer` |
|
| `portainer` | Manage Portainer stacks via API (list, start/stop/restart, update, prune images). | `skills/portainer` |
|
||||||
| `searxng` | Search through a local or self-hosted SearXNG instance for web, news, images, and more. | `skills/searxng` |
|
| `searxng` | Search through a local or self-hosted SearXNG instance for web, news, images, and more. | `skills/searxng` |
|
||||||
| `web-automation` | One-shot extraction plus broader browsing/scraping with Playwright-compatible CloakBrowser (auth flows, extraction, bot-protected sites). | `skills/web-automation` |
|
| `web-automation` | One-shot extraction plus broader browsing/scraping with Playwright-compatible CloakBrowser (auth flows, extraction, bot-protected sites). | `skills/web-automation` |
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ This folder contains detailed docs for each skill in this repository.
|
|||||||
|
|
||||||
- [`elevenlabs-stt`](elevenlabs-stt.md) — Local audio transcription through ElevenLabs Speech-to-Text
|
- [`elevenlabs-stt`](elevenlabs-stt.md) — Local audio transcription through ElevenLabs Speech-to-Text
|
||||||
- [`gitea-api`](gitea-api.md) — REST-based Gitea automation (no `tea` CLI required)
|
- [`gitea-api`](gitea-api.md) — REST-based Gitea automation (no `tea` CLI required)
|
||||||
|
- [`nordvpn-client`](nordvpn-client.md) — Cross-platform NordVPN install, login, connect, disconnect, and verification with Linux CLI and macOS NordLynx/WireGuard support
|
||||||
- [`portainer`](portainer.md) — Portainer stack management (list, lifecycle, updates, image pruning)
|
- [`portainer`](portainer.md) — Portainer stack management (list, lifecycle, updates, image pruning)
|
||||||
- [`searxng`](searxng.md) — Privacy-respecting metasearch via a local or self-hosted SearXNG instance
|
- [`searxng`](searxng.md) — Privacy-respecting metasearch via a local or self-hosted SearXNG instance
|
||||||
- [`web-automation`](web-automation.md) — One-shot extraction plus Playwright-compatible CloakBrowser browser automation and scraping
|
- [`web-automation`](web-automation.md) — One-shot extraction plus Playwright-compatible CloakBrowser browser automation and scraping
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
# nordvpn-client
|
||||||
|
|
||||||
|
Cross-platform NordVPN lifecycle skill for macOS and Linux.
|
||||||
|
|
||||||
|
## What it does
|
||||||
|
|
||||||
|
- Probes whether NordVPN is already installed or automation-ready
|
||||||
|
- Bootstraps the required backend if missing
|
||||||
|
- Handles login/bootstrap
|
||||||
|
- Connects to a country or city target
|
||||||
|
- Disconnects and reports status
|
||||||
|
- Verifies public IP and geolocation after connect
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node skills/nordvpn-client/scripts/nordvpn-client.js status
|
||||||
|
node skills/nordvpn-client/scripts/nordvpn-client.js install
|
||||||
|
node skills/nordvpn-client/scripts/nordvpn-client.js login
|
||||||
|
node skills/nordvpn-client/scripts/nordvpn-client.js verify
|
||||||
|
node skills/nordvpn-client/scripts/nordvpn-client.js verify --country "Italy"
|
||||||
|
node skills/nordvpn-client/scripts/nordvpn-client.js verify --country "Italy" --city "Milan"
|
||||||
|
node skills/nordvpn-client/scripts/nordvpn-client.js connect --country "Italy"
|
||||||
|
node skills/nordvpn-client/scripts/nordvpn-client.js connect --city "Milan"
|
||||||
|
node skills/nordvpn-client/scripts/nordvpn-client.js disconnect
|
||||||
|
```
|
||||||
|
|
||||||
|
## Platform behavior
|
||||||
|
|
||||||
|
### macOS
|
||||||
|
|
||||||
|
- preferred backend: NordLynx/WireGuard
|
||||||
|
- install path: `brew install wireguard-go wireguard-tools`
|
||||||
|
- automation requirements:
|
||||||
|
- `NORDVPN_TOKEN` or `NORDVPN_TOKEN_FILE`
|
||||||
|
- `wireguard-go`
|
||||||
|
- `wireguard-tools`
|
||||||
|
- non-interactive `sudo` for `~/.openclaw/workspace/skills/nordvpn-client/scripts/nordvpn-wireguard-helper.sh`
|
||||||
|
- the macOS WireGuard config intentionally omits `DNS = ...`
|
||||||
|
- reason: `wg-quick` on macOS rewrites system DNS across services when `DNS` is present, which can break connectivity and other tunnels
|
||||||
|
- `NordVPN.app` may stay installed but is only the manual fallback
|
||||||
|
- the app login is not reused by the automated WireGuard backend
|
||||||
|
|
||||||
|
Quick start:
|
||||||
|
|
||||||
|
1. `node skills/nordvpn-client/scripts/nordvpn-client.js install`
|
||||||
|
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`
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
|
||||||
|
- install path follows NordVPN's official installer script
|
||||||
|
- primary control path is the official `nordvpn` CLI
|
||||||
|
- token login is supported through `nordvpn login --token <token>`
|
||||||
|
|
||||||
|
## Credentials
|
||||||
|
|
||||||
|
Supported env vars:
|
||||||
|
|
||||||
|
- `NORDVPN_TOKEN`
|
||||||
|
- `NORDVPN_TOKEN_FILE`
|
||||||
|
- `NORDVPN_USERNAME`
|
||||||
|
- `NORDVPN_PASSWORD`
|
||||||
|
- `NORDVPN_PASSWORD_FILE`
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
- platform
|
||||||
|
- install state
|
||||||
|
- control mode (`cli`, `wireguard`, `app-manual`)
|
||||||
|
- auth state
|
||||||
|
- connection state
|
||||||
|
- requested target
|
||||||
|
- public IP / geolocation lookup
|
||||||
|
|
||||||
|
After `connect`, the intended workflow is:
|
||||||
|
|
||||||
|
1. `nordvpn-client connect ...`
|
||||||
|
2. `nordvpn-client verify ...` if an explicit location check is needed
|
||||||
|
3. run the follow-up task such as `web-automation`
|
||||||
|
|
||||||
|
## Limitations
|
||||||
|
|
||||||
|
- Linux behavior still depends on the official `nordvpn` CLI.
|
||||||
|
- macOS automated connects require token-based WireGuard setup; GUI-app login alone is insufficient.
|
||||||
|
- The Homebrew `nordvpn` app does not need to be uninstalled.
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# NordVPN Client Skill Design
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
Create a `nordvpn-client` skill that works on macOS and Linux gateway hosts. It should detect whether NordVPN is already installed, bootstrap it if missing, handle login/auth setup, connect to a requested country or city, verify the VPN state and public IP location, disconnect when requested, and then be usable alongside other skills like `web-automation`.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
The skill exposes one logical interface with platform-specific backends. Linux uses the official NordVPN CLI path. macOS probes for a usable CLI first, but falls back to the official app workflow when needed. The skill is responsible only for VPN lifecycle and verification, not for wrapping arbitrary commands inside a VPN session.
|
||||||
|
|
||||||
|
## Interface
|
||||||
|
Single script entrypoint:
|
||||||
|
- `node scripts/nordvpn-client.js install`
|
||||||
|
- `node scripts/nordvpn-client.js login`
|
||||||
|
- `node scripts/nordvpn-client.js connect --country "Italy"`
|
||||||
|
- `node scripts/nordvpn-client.js connect --city "Milan"`
|
||||||
|
- `node scripts/nordvpn-client.js disconnect`
|
||||||
|
- `node scripts/nordvpn-client.js status`
|
||||||
|
|
||||||
|
## Platform Model
|
||||||
|
### Linux
|
||||||
|
- Probe for `nordvpn`
|
||||||
|
- If missing, bootstrap official NordVPN package/CLI
|
||||||
|
- Prefer token-based login for non-interactive auth
|
||||||
|
- Connect/disconnect/status through official CLI
|
||||||
|
|
||||||
|
### macOS
|
||||||
|
- Probe for `nordvpn` CLI if available
|
||||||
|
- Otherwise probe/install the official app
|
||||||
|
- Use CLI when present, otherwise automate the app/login flow
|
||||||
|
- Verify connection using app/CLI state plus external IP/geolocation
|
||||||
|
|
||||||
|
## Auth and Safety
|
||||||
|
- Do not store raw NordVPN secrets in skill docs
|
||||||
|
- Read token/credentials from env vars or a local credential file path
|
||||||
|
- Keep the skill focused on install/login/connect/disconnect/status
|
||||||
|
- After `connect`, verify both local VPN state and external IP/location before the agent proceeds to tasks like `web-automation`
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
- `status` reports platform, install state, auth state, connection state, and public IP/location check
|
||||||
|
- `connect` verifies the requested target as closely as available data allows
|
||||||
|
- Local validation happens first in the OpenClaw workspace, then the proven skill is copied into `stef-openclaw-skills`, documented, committed, and pushed
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
# NordVPN Client Skill Implementation Plan
|
||||||
|
|
||||||
|
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||||
|
|
||||||
|
**Goal:** Build a cross-platform `nordvpn-client` skill for macOS and Linux that can install/bootstrap NordVPN, log in, connect to a target country or city, verify the VPN session, disconnect, and report status.
|
||||||
|
|
||||||
|
**Architecture:** Implement one skill with one script entrypoint and platform-specific backends. Linux uses the official NordVPN CLI. macOS uses a CLI path when present and otherwise falls back to the NordVPN app workflow. The skill manages VPN state only, leaving follow-up operations like `web-automation` to separate agent steps.
|
||||||
|
|
||||||
|
**Tech Stack:** Node.js, shell/OS commands, NordVPN CLI/app integration, OpenClaw skills, git
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1: Create isolated worktree
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: repo git metadata only
|
||||||
|
|
||||||
|
**Step 1: Create worktree**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
git -C /Users/stefano/.openclaw/workspace/projects/stef-openclaw-skills worktree add /Users/stefano/.openclaw/workspace/projects/stef-openclaw-skills/.worktrees/nordvpn-client -b feature/nordvpn-client
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 2: Verify baseline**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
git -C /Users/stefano/.openclaw/workspace/projects/stef-openclaw-skills/.worktrees/nordvpn-client status --short --branch
|
||||||
|
```
|
||||||
|
Expected: clean feature branch
|
||||||
|
|
||||||
|
### Task 2: Create the local skill runtime
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `skills/nordvpn-client/SKILL.md`
|
||||||
|
- Create: `skills/nordvpn-client/scripts/nordvpn-client.js`
|
||||||
|
- Optional Create: helper files under `skills/nordvpn-client/scripts/`
|
||||||
|
|
||||||
|
**Step 1: Write the failing checks**
|
||||||
|
- Missing command/action should fail with clear usage output
|
||||||
|
- Unsupported platform should fail clearly
|
||||||
|
|
||||||
|
**Step 2: Implement platform detection and install probe**
|
||||||
|
- detect `darwin` vs `linux`
|
||||||
|
- detect whether NordVPN CLI/app is already present
|
||||||
|
- expose `status` with install/auth/connect fields
|
||||||
|
|
||||||
|
### Task 3: Implement install and auth bootstrap
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `skills/nordvpn-client/scripts/nordvpn-client.js`
|
||||||
|
|
||||||
|
**Step 1: Linux install/login path**
|
||||||
|
- implement official CLI probe/install path
|
||||||
|
- implement token-based login path
|
||||||
|
|
||||||
|
**Step 2: macOS install/login path**
|
||||||
|
- probe CLI first
|
||||||
|
- if absent, probe/install NordVPN app path
|
||||||
|
- implement login/bootstrap state verification for the app workflow
|
||||||
|
|
||||||
|
**Step 3: Keep secrets external**
|
||||||
|
- env vars or local credential path only
|
||||||
|
- no raw secrets in docs or skill text
|
||||||
|
|
||||||
|
### Task 4: Implement connect/disconnect/status/verification
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `skills/nordvpn-client/scripts/nordvpn-client.js`
|
||||||
|
|
||||||
|
**Step 1: Connect**
|
||||||
|
- support `--country` and `--city`
|
||||||
|
- normalize target handling per platform
|
||||||
|
|
||||||
|
**Step 2: Verify**
|
||||||
|
- report local connection state
|
||||||
|
- run public IP / geolocation verification
|
||||||
|
- fail if connection target cannot be reasonably verified
|
||||||
|
|
||||||
|
**Step 3: Disconnect and status**
|
||||||
|
- implement clean disconnect
|
||||||
|
- ensure `status` emits machine-readable output for agent use
|
||||||
|
|
||||||
|
### Task 5: Validate locally in OpenClaw workspace
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Test: local workspace copy of `nordvpn-client`
|
||||||
|
|
||||||
|
**Step 1: Direct command validation**
|
||||||
|
- usage errors are correct
|
||||||
|
- install probe works on this host
|
||||||
|
- status output is coherent before login/connect
|
||||||
|
|
||||||
|
**Step 2: One real connect flow**
|
||||||
|
- connect to a test country/city if credentials are available
|
||||||
|
- verify local state + external IP/location
|
||||||
|
- disconnect cleanly
|
||||||
|
|
||||||
|
### Task 6: Promote to repo docs and publish
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `README.md`
|
||||||
|
- Modify: `docs/README.md`
|
||||||
|
- Create: `docs/nordvpn-client.md`
|
||||||
|
- Create/Modify: `skills/nordvpn-client/...`
|
||||||
|
|
||||||
|
**Step 1: Document the skill**
|
||||||
|
- install/bootstrap behavior
|
||||||
|
- auth expectations
|
||||||
|
- connect/disconnect/status commands
|
||||||
|
- macOS vs Linux notes
|
||||||
|
|
||||||
|
**Step 2: Commit and push**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
git add skills/nordvpn-client docs README.md
|
||||||
|
git commit -m "feat: add nordvpn client skill"
|
||||||
|
git push -u origin feature/nordvpn-client
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 3: Merge and cleanup**
|
||||||
|
- fast-forward or merge to `main`
|
||||||
|
- push `main`
|
||||||
|
- remove the worktree
|
||||||
|
- delete the feature branch
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# NordVPN macOS WireGuard Backend Design
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
Replace the current macOS app-manual fallback in `nordvpn-client` with a scripted WireGuard/NordLynx backend inspired by `wg-nord` and `wgnord`, while preserving the official Linux `nordvpn` CLI backend.
|
||||||
|
|
||||||
|
## Key decisions
|
||||||
|
- Keep Linux on the official `nordvpn` CLI.
|
||||||
|
- Prefer a native macOS WireGuard backend over the GUI app.
|
||||||
|
- Do not vendor third-party scripts directly; reimplement the needed logic in our own JSON-based Node skill.
|
||||||
|
- Do not require uninstalling the Homebrew `nordvpn` app. The new backend can coexist with it.
|
||||||
|
|
||||||
|
## macOS backend model
|
||||||
|
- Bootstrap via Homebrew:
|
||||||
|
- `wireguard-tools`
|
||||||
|
- `wireguard-go`
|
||||||
|
- Read NordVPN token from existing env/file inputs.
|
||||||
|
- Discover a WireGuard-capable NordVPN server via the public Nord API.
|
||||||
|
- Generate a private key locally.
|
||||||
|
- Exchange the private key for Nord-provided interface credentials using the token.
|
||||||
|
- Materialize a temporary WireGuard config under a skill-owned state directory.
|
||||||
|
- Connect and disconnect via `wg-quick`.
|
||||||
|
- Verify with public IP/geolocation after connect.
|
||||||
|
|
||||||
|
## Data/state
|
||||||
|
- Keep state under a skill-owned directory in the user's home, not `/etc`.
|
||||||
|
- Persist only what is needed for reconnect/disconnect/status.
|
||||||
|
- Never store secrets in docs.
|
||||||
|
|
||||||
|
## Rollout
|
||||||
|
1. Implement the macOS WireGuard backend in the skill.
|
||||||
|
2. Update status output so backend selection is explicit.
|
||||||
|
3. Update skill docs and repo docs.
|
||||||
|
4. Verify non-destructive flows on this host.
|
||||||
|
5. Commit, push, and then decide whether to run a live connect test.
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# NordVPN macOS WireGuard Backend Plan
|
||||||
|
|
||||||
|
1. Add a backend selector to `nordvpn-client`.
|
||||||
|
2. Keep Linux CLI behavior unchanged.
|
||||||
|
3. Add macOS WireGuard dependency probing and install guidance.
|
||||||
|
4. Implement token-based NordLynx config generation inspired by `wg-nord`/`wgnord`.
|
||||||
|
5. Replace the current preferred macOS control mode from `app-manual` to WireGuard when dependencies and token are available.
|
||||||
|
6. Keep app-manual as the last fallback only.
|
||||||
|
7. Update `status`, `login`, `connect`, `disconnect`, and `verify` JSON to expose the backend in use.
|
||||||
|
8. Update repo docs and skill docs to reflect the new model and required token/dependencies.
|
||||||
|
9. Verify command behavior locally without forcing a live VPN connection unless requested.
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
---
|
||||||
|
name: nordvpn-client
|
||||||
|
description: Use when managing NordVPN on macOS or Linux, including install/bootstrap, login, connect, disconnect, status checks, or verifying a VPN location before running another skill.
|
||||||
|
---
|
||||||
|
|
||||||
|
# NordVPN Client
|
||||||
|
|
||||||
|
Cross-platform NordVPN lifecycle management for macOS and Linux hosts.
|
||||||
|
|
||||||
|
## What This Skill Is For
|
||||||
|
|
||||||
|
- Probing whether NordVPN is already installed or automation-ready
|
||||||
|
- Bootstrapping the required NordVPN backend if it is missing
|
||||||
|
- Logging in through the Linux CLI or validating a NordVPN token for the macOS WireGuard backend
|
||||||
|
- Connecting to a country or city before a follow-up action such as `web-automation`
|
||||||
|
- Disconnecting and checking VPN status
|
||||||
|
- Verifying public IP and geolocation after connect
|
||||||
|
|
||||||
|
## Command Surface
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node scripts/nordvpn-client.js status
|
||||||
|
node scripts/nordvpn-client.js install
|
||||||
|
node scripts/nordvpn-client.js login
|
||||||
|
node scripts/nordvpn-client.js verify
|
||||||
|
node scripts/nordvpn-client.js verify --country "Italy"
|
||||||
|
node scripts/nordvpn-client.js verify --country "Italy" --city "Milan"
|
||||||
|
node scripts/nordvpn-client.js connect --country "Italy"
|
||||||
|
node scripts/nordvpn-client.js connect --city "Milan"
|
||||||
|
node scripts/nordvpn-client.js connect --country "Italy" --city "Milan"
|
||||||
|
node scripts/nordvpn-client.js disconnect
|
||||||
|
```
|
||||||
|
|
||||||
|
## Platform Notes
|
||||||
|
|
||||||
|
- Linux:
|
||||||
|
- uses the official `nordvpn` CLI
|
||||||
|
- install path follows NordVPN's Linux installer
|
||||||
|
- token login is supported through `NORDVPN_TOKEN`
|
||||||
|
- macOS:
|
||||||
|
- preferred backend is NordLynx/WireGuard using `wireguard-go` and `wireguard-tools`
|
||||||
|
- `install` bootstraps those tools with Homebrew
|
||||||
|
- equivalent Homebrew command: `brew install wireguard-go wireguard-tools`
|
||||||
|
- `login` validates `NORDVPN_TOKEN` / `NORDVPN_TOKEN_FILE` for the WireGuard backend
|
||||||
|
- the generated WireGuard config intentionally omits `DNS = ...` so `wg-quick` does not rewrite system resolvers or break other interfaces such as Tailscale
|
||||||
|
- `NordVPN.app` can remain installed, but it is only the manual fallback
|
||||||
|
|
||||||
|
## Credentials
|
||||||
|
|
||||||
|
Do not store secrets in this skill.
|
||||||
|
|
||||||
|
Supported env vars:
|
||||||
|
|
||||||
|
- `NORDVPN_TOKEN`
|
||||||
|
- `NORDVPN_USERNAME`
|
||||||
|
- `NORDVPN_PASSWORD`
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
- platform
|
||||||
|
- install state
|
||||||
|
- control mode (`cli`, `wireguard`, `app-manual`)
|
||||||
|
- auth state
|
||||||
|
- connection state
|
||||||
|
- requested target
|
||||||
|
- public IP lookup and geolocation
|
||||||
|
|
||||||
|
Use this skill first, then run the follow-up task under the active VPN session.
|
||||||
|
Use `verify` when you want an explicit post-connect location check without changing VPN state.
|
||||||
|
|
||||||
|
## macOS Quick Start
|
||||||
|
|
||||||
|
For an automated macOS flow:
|
||||||
|
|
||||||
|
1. `node scripts/nordvpn-client.js install`
|
||||||
|
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`
|
||||||
|
|
||||||
|
## Known Boundaries
|
||||||
|
|
||||||
|
- Linux country/city connect remains whatever the official `nordvpn` CLI supports.
|
||||||
|
- macOS automated connects require all of:
|
||||||
|
- `NORDVPN_TOKEN` or `NORDVPN_TOKEN_FILE`
|
||||||
|
- `wireguard-go`
|
||||||
|
- `wireguard-tools`
|
||||||
|
- 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.
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,123 @@
|
|||||||
|
const test = require("node:test");
|
||||||
|
const assert = require("node:assert/strict");
|
||||||
|
const fs = require("node:fs");
|
||||||
|
const path = require("node:path");
|
||||||
|
const vm = require("node:vm");
|
||||||
|
|
||||||
|
function loadInternals() {
|
||||||
|
const scriptPath = path.join(__dirname, "nordvpn-client.js");
|
||||||
|
const source = fs.readFileSync(scriptPath, "utf8").replace(/\nmain\(\);\s*$/, "\n");
|
||||||
|
const wrapped = `${source}
|
||||||
|
module.exports = {
|
||||||
|
buildWireguardConfig:
|
||||||
|
typeof buildWireguardConfig === "function" ? buildWireguardConfig : undefined,
|
||||||
|
buildLookupResult:
|
||||||
|
typeof buildLookupResult === "function" ? buildLookupResult : undefined,
|
||||||
|
detectMacWireguardActiveFromIfconfig:
|
||||||
|
typeof detectMacWireguardActiveFromIfconfig === "function" ? detectMacWireguardActiveFromIfconfig : undefined,
|
||||||
|
resolveHostnameWithFallback:
|
||||||
|
typeof resolveHostnameWithFallback === "function" ? resolveHostnameWithFallback : undefined,
|
||||||
|
verifyConnectionWithRetry:
|
||||||
|
typeof verifyConnectionWithRetry === "function" ? verifyConnectionWithRetry : undefined,
|
||||||
|
};`;
|
||||||
|
|
||||||
|
const sandbox = {
|
||||||
|
require,
|
||||||
|
module: { exports: {} },
|
||||||
|
exports: {},
|
||||||
|
__dirname,
|
||||||
|
__filename: scriptPath,
|
||||||
|
process: { ...process, exit() {} },
|
||||||
|
console,
|
||||||
|
setTimeout,
|
||||||
|
clearTimeout,
|
||||||
|
Buffer,
|
||||||
|
};
|
||||||
|
|
||||||
|
vm.runInNewContext(wrapped, sandbox, { filename: scriptPath });
|
||||||
|
return sandbox.module.exports;
|
||||||
|
}
|
||||||
|
|
||||||
|
test("detectMacWireguardActiveFromIfconfig detects nordvpn utun client address", () => {
|
||||||
|
const { detectMacWireguardActiveFromIfconfig } = loadInternals();
|
||||||
|
assert.equal(typeof detectMacWireguardActiveFromIfconfig, "function");
|
||||||
|
|
||||||
|
const ifconfig = `
|
||||||
|
utun8: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1380
|
||||||
|
utun9: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1420
|
||||||
|
\tinet 10.5.0.2 --> 10.5.0.2 netmask 0xff000000
|
||||||
|
`;
|
||||||
|
|
||||||
|
assert.equal(detectMacWireguardActiveFromIfconfig(ifconfig), true);
|
||||||
|
assert.equal(detectMacWireguardActiveFromIfconfig("utun7: flags=8051\n\tinet 100.64.0.4"), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("buildLookupResult supports lookup all=true mode", () => {
|
||||||
|
const { buildLookupResult } = loadInternals();
|
||||||
|
assert.equal(typeof buildLookupResult, "function");
|
||||||
|
assert.equal(
|
||||||
|
JSON.stringify(buildLookupResult("104.26.9.44", { all: true })),
|
||||||
|
JSON.stringify([{ address: "104.26.9.44", family: 4 }])
|
||||||
|
);
|
||||||
|
assert.equal(JSON.stringify(buildLookupResult("104.26.9.44", { all: false })), JSON.stringify(["104.26.9.44", 4]));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("buildWireguardConfig omits DNS so macOS wg-quick does not rewrite system resolvers", () => {
|
||||||
|
const { buildWireguardConfig } = loadInternals();
|
||||||
|
assert.equal(typeof buildWireguardConfig, "function");
|
||||||
|
|
||||||
|
const config = buildWireguardConfig(
|
||||||
|
{
|
||||||
|
hostname: "tr73.nordvpn.com",
|
||||||
|
ips: [{ ip: { version: 4, ip: "45.89.52.1" } }],
|
||||||
|
technologies: [{ identifier: "wireguard_udp", metadata: [{ name: "public_key", value: "PUBKEY" }] }],
|
||||||
|
},
|
||||||
|
"PRIVATEKEY"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(config.includes("DNS ="), false);
|
||||||
|
assert.equal(config.includes("AllowedIPs = 0.0.0.0/0"), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("verifyConnectionWithRetry retries transient reachability failures", async () => {
|
||||||
|
const { verifyConnectionWithRetry } = loadInternals();
|
||||||
|
assert.equal(typeof verifyConnectionWithRetry, "function");
|
||||||
|
|
||||||
|
let attempts = 0;
|
||||||
|
const result = await verifyConnectionWithRetry(
|
||||||
|
{ country: "Italy", city: "Milan" },
|
||||||
|
{
|
||||||
|
attempts: 3,
|
||||||
|
delayMs: 1,
|
||||||
|
getPublicIpInfo: async () => {
|
||||||
|
attempts += 1;
|
||||||
|
if (attempts === 1) {
|
||||||
|
return { ok: false, error: "read EHOSTUNREACH" };
|
||||||
|
}
|
||||||
|
return { ok: true, country: "Italy", city: "Milan" };
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(result.ok, true);
|
||||||
|
assert.equal(result.ipInfo.country, "Italy");
|
||||||
|
assert.equal(attempts, 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("resolveHostnameWithFallback uses fallback resolvers when system lookup fails", async () => {
|
||||||
|
const { resolveHostnameWithFallback } = loadInternals();
|
||||||
|
assert.equal(typeof resolveHostnameWithFallback, "function");
|
||||||
|
|
||||||
|
const calls = [];
|
||||||
|
const address = await resolveHostnameWithFallback("ipapi.co", {
|
||||||
|
resolvers: ["1.1.1.1", "8.8.8.8"],
|
||||||
|
resolveWithResolver: async (hostname, resolver) => {
|
||||||
|
calls.push(`${resolver}:${hostname}`);
|
||||||
|
if (resolver === "1.1.1.1") return [];
|
||||||
|
return ["104.26.9.44"];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(address, "104.26.9.44");
|
||||||
|
assert.deepEqual(calls, ["1.1.1.1:ipapi.co", "8.8.8.8:ipapi.co"]);
|
||||||
|
});
|
||||||
@@ -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"
|
||||||
Reference in New Issue
Block a user