22 Commits

Author SHA1 Message Date
Stefano Fiorini
59dbaf8a6c fix: relax CloakBrowser prerequisite check 2026-03-13 09:33:53 -05:00
Stefano Fiorini
8d5dd046a4 fix: harden mac nordvpn status inference 2026-03-13 00:30:06 -05:00
Stefano Fiorini
fc8e388c0a chore: update cloakbrowser web automation runtime 2026-03-13 00:19:54 -05:00
Stefano Fiorini
e6dccb5656 docs: expand nordvpn client setup and troubleshooting 2026-03-12 02:45:21 -05:00
Stefano Fiorini
7d8eb89911 fix: redact nordvpn path metadata by default 2026-03-12 02:37:16 -05:00
Stefano Fiorini
60f425a4fc fix: stabilize mac nordvpn state reporting 2026-03-12 02:28:57 -05:00
Stefano Fiorini
647828aa78 fix: force mac nordvpn disconnect teardown 2026-03-12 02:22:50 -05:00
Stefano Fiorini
b4c8d3fdb8 fix: clear stale nordvpn disconnect state 2026-03-12 02:08:34 -05:00
Stefano Fiorini
09b1c1e37a fix: simplify mac nordvpn tailscale coordination 2026-03-12 01:55:46 -05:00
Stefano Fiorini
916d8bf95a docs: add nordvpn tailscale coordination plan 2026-03-12 01:46:52 -05:00
Stefano Fiorini
6bc21219a7 docs: add nordvpn macos dns plan 2026-03-12 01:35:32 -05:00
Stefano Fiorini
ca33b2d74a fix: avoid mac wireguard dns rewrites 2026-03-12 01:20:02 -05:00
Stefano Fiorini
d0c50f5d8a fix: harden nordvpn wireguard verification 2026-03-12 00:50:05 -05:00
Stefano Fiorini
a8a285b356 feat: add nordvpn wireguard sudo helper 2026-03-12 00:34:04 -05:00
Stefano Fiorini
045cf6aad2 feat: add default nordvpn credential paths 2026-03-12 00:09:56 -05:00
Stefano Fiorini
78be4fc600 docs: clarify nordvpn mac setup flow 2026-03-11 23:53:15 -05:00
Stefano Fiorini
d1b4d58c5d Merge branch 'feature/nordvpn-wireguard' 2026-03-11 23:44:26 -05:00
Stefano Fiorini
4a539a33c9 feat: add mac wireguard nordvpn backend 2026-03-11 23:44:22 -05:00
Stefano Fiorini
b326153d26 fix: clarify mac nordvpn app-only mode 2026-03-11 22:59:08 -05:00
Stefano Fiorini
2612cef1dc Merge branch 'feature/nordvpn-client' 2026-03-11 22:36:03 -05:00
Stefano Fiorini
120721bbc6 feat: add nordvpn client skill 2026-03-11 22:35:50 -05:00
Stefano Fiorini
fe5b4659fe docs: add nordvpn client plan 2026-03-11 22:02:49 -05:00
20 changed files with 2646 additions and 11 deletions

View File

@@ -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` |

View File

@@ -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

371
docs/nordvpn-client.md Normal file
View File

@@ -0,0 +1,371 @@
# nordvpn-client
Cross-platform NordVPN lifecycle skill for macOS and Linux.
## Overview
`nordvpn-client` is the operator-facing VPN control skill for OpenClaw. It can:
- detect whether the host is ready for NordVPN automation
- install or bootstrap the required backend
- validate auth
- connect to a target country or city
- verify the public exit location
- disconnect and restore normal local networking state
The skill uses different backends by platform:
- Linux: official `nordvpn` CLI
- macOS: NordLynx/WireGuard with `wireguard-go` and `wireguard-tools`
## 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 "Tokyo"
node skills/nordvpn-client/scripts/nordvpn-client.js connect --country "Japan" --city "Tokyo"
node skills/nordvpn-client/scripts/nordvpn-client.js disconnect
node skills/nordvpn-client/scripts/nordvpn-client.js status --debug
```
## Credentials
Supported inputs:
- `NORDVPN_TOKEN`
- `NORDVPN_TOKEN_FILE`
- `NORDVPN_USERNAME`
- `NORDVPN_PASSWORD`
- `NORDVPN_PASSWORD_FILE`
Default OpenClaw credential paths:
- token: `~/.openclaw/workspace/.clawdbot/credentials/nordvpn/token.txt`
- password: `~/.openclaw/workspace/.clawdbot/credentials/nordvpn/password.txt`
Recommended setup on macOS is a token file with strict permissions:
```bash
mkdir -p ~/.openclaw/workspace/.clawdbot/credentials/nordvpn
chmod 700 ~/.openclaw/workspace/.clawdbot/credentials/nordvpn
printf '%s\n' '<your-nordvpn-token>' > ~/.openclaw/workspace/.clawdbot/credentials/nordvpn/token.txt
chmod 600 ~/.openclaw/workspace/.clawdbot/credentials/nordvpn/token.txt
```
Do not commit secrets into the repo or the skill docs.
## Platform Backends
### macOS
Current macOS backend:
- NordLynx/WireGuard
- `wireguard-go`
- `wireguard-tools`
- NordVPN DNS in the generated WireGuard config:
- `103.86.96.100`
- `103.86.99.100`
Important behavior:
- `NordVPN.app` may remain installed, but the automated backend does not reuse app login state.
- The skill automatically suspends Tailscale before connect if Tailscale is active.
- The skill resumes Tailscale after disconnect, or after a failed connect, if it stopped it.
- The Homebrew NordVPN app does not need to be uninstalled.
### Linux
Current Linux backend:
- official `nordvpn` CLI
- official NordVPN installer
- token login through `nordvpn login --token ...`
## Install / Bootstrap
### macOS
Bootstrap the automation backend:
```bash
node skills/nordvpn-client/scripts/nordvpn-client.js install
```
Equivalent Homebrew command:
```bash
brew install wireguard-go wireguard-tools
```
What `install` does on macOS:
- checks whether `wireguard-go` is present
- checks whether `wg` and `wg-quick` are present
- installs missing packages through Homebrew
### Linux
```bash
node skills/nordvpn-client/scripts/nordvpn-client.js install
```
What `install` does on Linux:
- downloads NordVPNs official installer script
- runs it
- leaves subsequent login/connect to the official `nordvpn` CLI
## macOS sudoers Setup
Automated macOS connect/disconnect requires passwordless `sudo` for the helper script that invokes `wg-quick`.
Installed OpenClaw helper path:
```text
/Users/stefano/.openclaw/workspace/skills/nordvpn-client/scripts/nordvpn-wireguard-helper.sh
```
Edit sudoers safely:
```bash
sudo visudo
```
Add this exact rule:
```sudoers
stefano ALL=(root) NOPASSWD: /Users/stefano/.openclaw/workspace/skills/nordvpn-client/scripts/nordvpn-wireguard-helper.sh probe, /Users/stefano/.openclaw/workspace/skills/nordvpn-client/scripts/nordvpn-wireguard-helper.sh up, /Users/stefano/.openclaw/workspace/skills/nordvpn-client/scripts/nordvpn-wireguard-helper.sh down
```
If you run the repo copy directly instead of the installed OpenClaw skill, adjust the helper path accordingly.
## Common Flows
### Status
```bash
node skills/nordvpn-client/scripts/nordvpn-client.js status
```
Use this first to answer:
- is the correct backend available?
- is the token visible?
- is `sudoReady` true?
- is the machine currently connected?
### Login
```bash
node skills/nordvpn-client/scripts/nordvpn-client.js login
```
On macOS this validates the token and populates the local auth cache. It does not connect the VPN.
### Connect
Country:
```bash
node skills/nordvpn-client/scripts/nordvpn-client.js connect --country "Germany"
```
City:
```bash
node skills/nordvpn-client/scripts/nordvpn-client.js connect --country "Japan" --city "Tokyo"
```
Expected macOS behavior:
- stop Tailscale if active
- select a NordVPN server for the target
- bring up the WireGuard tunnel
- verify the public exit location
- return JSON describing the chosen server and final verified location
### Verify
```bash
node skills/nordvpn-client/scripts/nordvpn-client.js verify --country "Germany"
```
Use this after connect if you want an explicit location check without changing VPN state.
### Disconnect
```bash
node skills/nordvpn-client/scripts/nordvpn-client.js disconnect
```
Expected macOS behavior:
- attempt `wg-quick down` whenever there is active or residual NordVPN WireGuard state
- remove stale local NordVPN state files after teardown
- resume Tailscale if the skill had suspended it
## Output Model
Normal JSON is redacted by default.
Redacted fields in normal mode:
- `cliPath`
- `appPath`
- `wireguard.configPath`
- `wireguard.helperPath`
- `wireguard.authCache.tokenSource`
Operational fields preserved in normal mode:
- `connected`
- `wireguard.active`
- `wireguard.endpoint`
- `requestedTarget`
- `verification`
- public IP and location
For deeper troubleshooting, use:
```bash
node skills/nordvpn-client/scripts/nordvpn-client.js status --debug
```
`--debug` keeps the internal local paths and other low-level metadata in the JSON output.
## Troubleshooting
### `Invalid authorization header`
Meaning:
- the token file was found
- the token value is not valid for NordVPNs API
Actions:
1. generate a fresh NordVPN access token
2. replace the contents of `~/.openclaw/workspace/.clawdbot/credentials/nordvpn/token.txt`
3. run:
```bash
node skills/nordvpn-client/scripts/nordvpn-client.js login
```
### `sudoReady: false`
Meaning:
- the helper script is present
- the agent cannot run `wg-quick` non-interactively
Actions:
1. add the `visudo` rule shown above
2. rerun:
```bash
node skills/nordvpn-client/scripts/nordvpn-client.js status
```
Expected:
- `wireguard.sudoReady: true`
### WireGuard tools missing
Meaning:
- macOS backend is selected
- `wireguard-go`, `wg`, or `wg-quick` is missing
Actions:
```bash
node skills/nordvpn-client/scripts/nordvpn-client.js install
```
or:
```bash
brew install wireguard-go wireguard-tools
```
### Tailscale interaction
Expected behavior on macOS:
- Tailscale is suspended before the NordVPN connect
- Tailscale is resumed after disconnect or failed connect
If a connect succeeds but later traffic is wrong, check:
```bash
node skills/nordvpn-client/scripts/nordvpn-client.js status
/opt/homebrew/bin/tailscale status --json
```
Look for:
- `connected: true` and a foreign exit IP while NordVPN is up
- `connected: false` and Texas/Garland IP after disconnect
### Status says disconnected after a verified connect
This was a previous macOS false-negative path and is now normalized in the connect response.
Current expectation:
- if `connect` verifies the target location successfully
- the returned `state` snapshot should also show:
- `connected: true`
- `wireguard.active: true`
If that regresses, capture:
- `connect` JSON
- `verify` JSON
- `status --debug` JSON
### Disconnect says “no active connection” but traffic is still foreign
The current macOS disconnect path now treats residual WireGuard state as sufficient reason to attempt teardown.
Safe operator check:
```bash
node skills/nordvpn-client/scripts/nordvpn-client.js disconnect
node skills/nordvpn-client/scripts/nordvpn-client.js verify
```
Expected after a good disconnect:
- Texas/Garland public IP again
- `wireguard.configPath: null` in normal status output
- `wireguard.lastConnection: null`
If that regresses, capture:
- `disconnect` JSON
- `verify` JSON
- `status --debug` JSON
## Recommended Agent Workflow
For VPN-routed work:
1. `status`
2. `install` if backend tooling is missing
3. `login` if token validation has not happened yet
4. `connect --country ...` or `connect --country ... --city ...`
5. `verify`
6. run the follow-up skill such as `web-automation`
7. `disconnect`
8. `verify` again if you need proof the machine returned to the normal exit path

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -0,0 +1,76 @@
# NordVPN Client Docs Refresh Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Refresh the `nordvpn-client` documentation so operators and the OpenClaw agent have complete, accurate setup and troubleshooting guidance for the current macOS and Linux backends.
**Architecture:** Expand the canonical repo doc into a full operator guide, tighten the agent-facing `SKILL.md` to match the current behavior, and lightly update summary docs only if their current one-line descriptions are materially incomplete. Sync the updated `SKILL.md` into the installed OpenClaw workspace copy so runtime guidance matches the repo.
**Tech Stack:** Markdown docs, local repo skill docs, OpenClaw workspace skill sync
---
### Task 1: Refresh canonical operator documentation
**Files:**
- Modify: `docs/nordvpn-client.md`
**Step 1: Rewrite the doc structure**
- Add sections for overview, platform backends, prerequisites, credential paths, install/bootstrap, macOS sudoers setup, command flows, output model, and troubleshooting.
**Step 2: Add exact operator setup details**
- Include the exact `visudo` entry for the helper script.
- Document default token/password file locations.
- Document Homebrew install commands for macOS tooling.
**Step 3: Add safe troubleshooting guidance**
- Include only safe operator procedures from the debugging work:
- invalid token handling
- `sudoReady: false`
- Tailscale suspend/resume expectations
- what normal redacted output includes
- how to use `--debug` when deeper inspection is needed
### Task 2: Refresh agent-facing skill documentation
**Files:**
- Modify: `skills/nordvpn-client/SKILL.md`
- Sync: `/Users/stefano/.openclaw/workspace/skills/nordvpn-client/SKILL.md`
**Step 1: Tighten the skill instructions**
- Keep the doc shorter than the canonical operator guide.
- Ensure it explicitly covers the default credential paths, macOS sudoers requirement, Tailscale suspend/resume behavior, and `--debug` usage.
**Step 2: Sync installed OpenClaw copy**
- Copy the updated repo `SKILL.md` into the installed workspace skill path.
### Task 3: Update summary docs if needed
**Files:**
- Check: `README.md`
- Check: `docs/README.md`
- Modify only if current summary text is materially missing the current backend model.
**Step 1: Review summary descriptions**
- Confirm whether the one-line descriptions already adequately describe Linux CLI + macOS NordLynx/WireGuard.
**Step 2: Update only if necessary**
- Avoid churn if the existing summaries are already sufficient.
### Task 4: Verify and publish
**Files:**
- Verify: `docs/nordvpn-client.md`
- Verify: `skills/nordvpn-client/SKILL.md`
- Verify: `/Users/stefano/.openclaw/workspace/skills/nordvpn-client/SKILL.md`
**Step 1: Run doc verification checks**
- Run: `rg -n "sudoers|visudo|--debug|Tailscale|token.txt|wireguard-helper" docs/nordvpn-client.md skills/nordvpn-client/SKILL.md`
- Expected: all required topics present
**Step 2: Confirm installed workspace skill matches repo skill**
- Run: `cmp skills/nordvpn-client/SKILL.md /Users/stefano/.openclaw/workspace/skills/nordvpn-client/SKILL.md`
- Expected: no output
**Step 3: Commit and push**
- Commit message: `docs: expand nordvpn client setup and troubleshooting`

View File

@@ -0,0 +1,40 @@
# NordVPN macOS DNS Design
## Goal
Keep NordVPN DNS while connected on macOS, but only apply it to active physical services so the WireGuard backend does not break Tailscale or other virtual interfaces.
## Behavior
- Keep the generated WireGuard config free of `DNS = ...`
- During `connect` on macOS:
- detect active physical network services
- snapshot current DNS/search-domain settings
- set NordVPN DNS only on those physical services
- During `disconnect`:
- restore the saved DNS/search-domain settings
- During failed `connect` after DNS changes:
- restore DNS before returning the error
## DNS Values
- IPv4 primary: `103.86.96.100`
- IPv4 secondary: `103.86.99.100`
- No IPv6 DNS for now
## Service Selection
Include only enabled physical services from `networksetup`.
Exclude names matching:
- Tailscale
- Bridge
- Thunderbolt Bridge
- Loopback
- VPN
- utun
## Persistence
- Save DNS snapshot under `~/.nordvpn-client`
- Overwrite on each successful connect
- Clear after successful disconnect restore
## Verification
- Unit tests for service selection and DNS snapshot/restore helpers
- Direct logic/config tests
- Avoid live connect tests from this session unless explicitly requested because they can drop connectivity

View File

@@ -0,0 +1,11 @@
# NordVPN macOS DNS Plan
1. Add macOS DNS state file support under `~/.nordvpn-client`.
2. Implement helpers to enumerate eligible physical services and snapshot existing DNS/search-domain settings.
3. Implement helpers to apply NordVPN DNS only to eligible physical services.
4. Implement helpers to restore previous DNS/search-domain settings on disconnect or failed connect.
5. Add unit tests for service filtering and DNS state transitions.
6. Update skill/docs to explain macOS physical-service DNS management.
7. Sync the installed workspace copy.
8. Run tests and non-destructive verification.
9. Commit and push.

View File

@@ -0,0 +1,26 @@
# NordVPN Tailscale Coordination Design
## Goal
Stabilize macOS NordVPN connects by explicitly stopping Tailscale before bringing up the NordVPN WireGuard tunnel, then restarting Tailscale after NordVPN disconnects.
## Behavior
- macOS only
- on `connect`:
- detect whether Tailscale is active
- if active, stop it and record that state
- bring up NordVPN
- on `disconnect`:
- tear down NordVPN
- if the skill stopped Tailscale earlier, start it again
- clear the saved state
- on connect failure after stopping Tailscale:
- attempt to start Tailscale again before returning the error
## State
- persist `tailscaleWasActive` under `~/.nordvpn-client`
- only restart Tailscale if the skill actually stopped it
## Rollback target if successful
- remove the temporary macOS physical-service DNS management patch
- restore the simpler NordVPN config path that uses NordVPN DNS directly in the WireGuard config
- keep Tailscale suspend/resume as the macOS coexistence solution

View File

@@ -0,0 +1,10 @@
# NordVPN Tailscale Coordination Plan
1. Add macOS Tailscale state file support under `~/.nordvpn-client`.
2. Implement helpers to detect, stop, and start Tailscale on macOS.
3. Add unit tests for Tailscale state transitions.
4. Wire Tailscale stop into macOS `connect` before WireGuard up.
5. Wire Tailscale restart into macOS `disconnect` and connect-failure rollback.
6. Sync the installed workspace copy.
7. Run tests and non-destructive verification.
8. Commit and push.

View File

@@ -32,6 +32,16 @@ pnpm approve-builds
pnpm rebuild better-sqlite3 esbuild pnpm rebuild better-sqlite3 esbuild
``` ```
## Updating CloakBrowser
```bash
cd ~/.openclaw/workspace/skills/web-automation/scripts
pnpm up cloakbrowser playwright-core
npx cloakbrowser install
pnpm approve-builds
pnpm rebuild better-sqlite3 esbuild
```
## System libraries (for OpenClaw Docker builds) ## System libraries (for OpenClaw Docker builds)
```bash ```bash

View File

@@ -0,0 +1,108 @@
---
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.
## Use This Skill For
- probing whether NordVPN automation is ready
- bootstrapping missing backend dependencies
- validating auth
- connecting to a country or city
- verifying the public exit location
- disconnecting and restoring the normal network state
## 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 "Germany"
node scripts/nordvpn-client.js verify --country "Japan" --city "Tokyo"
node scripts/nordvpn-client.js connect --country "Germany"
node scripts/nordvpn-client.js connect --country "Japan" --city "Tokyo"
node scripts/nordvpn-client.js disconnect
node scripts/nordvpn-client.js status --debug
```
## Backend Model
- Linux:
- use the official `nordvpn` CLI
- `install` uses the official NordVPN installer
- token login is supported
- macOS:
- use NordLynx/WireGuard through `wireguard-go` and `wireguard-tools`
- `install` bootstraps them with Homebrew
- `login` validates the token for the WireGuard backend
- Tailscale is suspended before connect and resumed after disconnect or failed connect
- `NordVPN.app` may remain installed but is only the manual fallback
## Credentials
Default OpenClaw credential paths:
- token: `~/.openclaw/workspace/.clawdbot/credentials/nordvpn/token.txt`
- password: `~/.openclaw/workspace/.clawdbot/credentials/nordvpn/password.txt`
Supported env vars:
- `NORDVPN_TOKEN`
- `NORDVPN_TOKEN_FILE`
- `NORDVPN_USERNAME`
- `NORDVPN_PASSWORD`
- `NORDVPN_PASSWORD_FILE`
## macOS Requirements
Automated macOS connects require all of:
- `wireguard-go`
- `wireguard-tools`
- `NORDVPN_TOKEN` or the default token file
- non-interactive `sudo` for the installed helper script:
- `~/.openclaw/workspace/skills/nordvpn-client/scripts/nordvpn-wireguard-helper.sh`
Exact `visudo` rule for the installed OpenClaw skill:
```sudoers
stefano ALL=(root) NOPASSWD: /Users/stefano/.openclaw/workspace/skills/nordvpn-client/scripts/nordvpn-wireguard-helper.sh probe, /Users/stefano/.openclaw/workspace/skills/nordvpn-client/scripts/nordvpn-wireguard-helper.sh up, /Users/stefano/.openclaw/workspace/skills/nordvpn-client/scripts/nordvpn-wireguard-helper.sh down
```
## Agent Guidance
- run `status` first when the machine state is unclear
- on macOS, if tooling is missing, run `install`
- if auth is unclear, run `login`
- use `connect` before location-sensitive skills such as `web-automation`
- use `verify` after connect when you need an explicit location check
- use `disconnect` after the follow-up task
## Output Rules
- normal JSON output redacts local path metadata
- use `--debug` only when deeper troubleshooting requires internal local paths and helper/config metadata
## Troubleshooting Cues
- `Invalid authorization header`:
- token file exists but the token is invalid; replace the token and rerun `login`
- `sudoReady: false`:
- the helper is not allowed in sudoers; add the `visudo` rule above
- connect succeeds but final state looks inconsistent:
- rely on the verified public IP/location first
- then inspect `status --debug`
- disconnect should leave:
- normal public IP restored
- no active WireGuard state
- Tailscale resumed if the skill suspended it
For full operator setup and troubleshooting, see:
- `docs/nordvpn-client.md`

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,309 @@
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 = {
buildMacTailscaleState:
typeof buildMacTailscaleState === "function" ? buildMacTailscaleState : undefined,
buildWireguardConfig:
typeof buildWireguardConfig === "function" ? buildWireguardConfig : undefined,
buildLookupResult:
typeof buildLookupResult === "function" ? buildLookupResult : undefined,
cleanupMacWireguardState:
typeof cleanupMacWireguardState === "function" ? cleanupMacWireguardState : undefined,
getMacTailscalePath:
typeof getMacTailscalePath === "function" ? getMacTailscalePath : undefined,
isMacTailscaleActive:
typeof isMacTailscaleActive === "function" ? isMacTailscaleActive : undefined,
normalizeSuccessfulConnectState:
typeof normalizeSuccessfulConnectState === "function" ? normalizeSuccessfulConnectState : undefined,
normalizeStatusState:
typeof normalizeStatusState === "function" ? normalizeStatusState : undefined,
sanitizeOutputPayload:
typeof sanitizeOutputPayload === "function" ? sanitizeOutputPayload : undefined,
shouldAttemptMacWireguardDisconnect:
typeof shouldAttemptMacWireguardDisconnect === "function" ? shouldAttemptMacWireguardDisconnect : 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 includes NordVPN DNS for the vanilla macOS config path", () => {
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 = 103.86.96.100, 103.86.99.100"), true);
assert.equal(config.includes("AllowedIPs = 0.0.0.0/0"), true);
});
test("getMacTailscalePath falls back to /opt/homebrew/bin/tailscale when PATH lookup is missing", () => {
const { getMacTailscalePath } = loadInternals();
assert.equal(typeof getMacTailscalePath, "function");
assert.equal(
getMacTailscalePath({
commandExists: () => "",
fileExists: (target) => target === "/opt/homebrew/bin/tailscale",
}),
"/opt/homebrew/bin/tailscale"
);
});
test("buildMacTailscaleState records whether tailscale was active", () => {
const { buildMacTailscaleState } = loadInternals();
assert.equal(typeof buildMacTailscaleState, "function");
assert.equal(
JSON.stringify(buildMacTailscaleState(true)),
JSON.stringify({
tailscaleWasActive: true,
})
);
});
test("cleanupMacWireguardState removes stale config and last-connection files", () => {
const { cleanupMacWireguardState } = loadInternals();
assert.equal(typeof cleanupMacWireguardState, "function");
const tmpDir = fs.mkdtempSync(path.join(fs.mkdtempSync("/tmp/nordvpn-client-test-"), "state-"));
const configPath = path.join(tmpDir, "nordvpnctl.conf");
const lastConnectionPath = path.join(tmpDir, "last-connection.json");
fs.writeFileSync(configPath, "wireguard-config");
fs.writeFileSync(lastConnectionPath, "{\"country\":\"Germany\"}");
const result = cleanupMacWireguardState({
configPath,
lastConnectionPath,
});
assert.equal(result.cleaned, true);
assert.equal(fs.existsSync(configPath), false);
assert.equal(fs.existsSync(lastConnectionPath), false);
});
test("shouldAttemptMacWireguardDisconnect does not trust active=false when residual state exists", () => {
const { shouldAttemptMacWireguardDisconnect } = loadInternals();
assert.equal(typeof shouldAttemptMacWireguardDisconnect, "function");
assert.equal(
shouldAttemptMacWireguardDisconnect({
active: false,
configPath: "/Users/stefano/.nordvpn-client/wireguard/nordvpnctl.conf",
endpoint: null,
lastConnection: null,
}),
true
);
assert.equal(
shouldAttemptMacWireguardDisconnect({
active: false,
configPath: null,
endpoint: null,
lastConnection: { country: "Italy" },
}),
true
);
assert.equal(
shouldAttemptMacWireguardDisconnect({
active: false,
configPath: null,
endpoint: null,
lastConnection: null,
}),
false
);
});
test("normalizeSuccessfulConnectState marks the connect snapshot active after verified macOS wireguard connect", () => {
const { normalizeSuccessfulConnectState } = loadInternals();
assert.equal(typeof normalizeSuccessfulConnectState, "function");
const state = normalizeSuccessfulConnectState(
{
platform: "darwin",
controlMode: "wireguard",
connected: false,
wireguard: {
active: false,
endpoint: null,
},
},
{
backend: "wireguard",
server: {
hostname: "de1227.nordvpn.com",
},
},
{
ok: true,
ipInfo: {
country: "Germany",
},
}
);
assert.equal(state.connected, true);
assert.equal(state.wireguard.active, true);
assert.equal(state.wireguard.endpoint, "de1227.nordvpn.com:51820");
});
test("normalizeStatusState marks macOS wireguard connected when public IP matches the last successful target", () => {
const { normalizeStatusState } = loadInternals();
assert.equal(typeof normalizeStatusState, "function");
const state = normalizeStatusState({
platform: "darwin",
controlMode: "wireguard",
connected: false,
wireguard: {
active: false,
endpoint: "tr73.nordvpn.com:51820",
lastConnection: {
requestedTarget: { country: "Turkey", city: "" },
resolvedTarget: { country: "Turkey", city: "Istanbul" },
},
},
publicIp: {
ok: true,
country: "Turkey",
city: "Istanbul",
},
});
assert.equal(state.connected, true);
assert.equal(state.wireguard.active, true);
});
test("sanitizeOutputPayload redacts local path metadata from normal JSON output", () => {
const { sanitizeOutputPayload } = loadInternals();
assert.equal(typeof sanitizeOutputPayload, "function");
const sanitized = sanitizeOutputPayload({
cliPath: "/opt/homebrew/bin/nordvpn",
appPath: "/Applications/NordVPN.app",
wireguard: {
configPath: "/Users/stefano/.nordvpn-client/wireguard/nordvpnctl.conf",
helperPath: "/Users/stefano/.openclaw/workspace/skills/nordvpn-client/scripts/nordvpn-wireguard-helper.sh",
authCache: {
tokenSource: "default:/Users/stefano/.openclaw/workspace/.clawdbot/credentials/nordvpn/token.txt",
},
endpoint: "jp454.nordvpn.com:51820",
},
});
assert.equal(sanitized.cliPath, null);
assert.equal(sanitized.appPath, null);
assert.equal(sanitized.wireguard.configPath, null);
assert.equal(sanitized.wireguard.helperPath, null);
assert.equal(sanitized.wireguard.authCache.tokenSource, null);
assert.equal(sanitized.wireguard.endpoint, "jp454.nordvpn.com:51820");
});
test("isMacTailscaleActive treats Running backend as active", () => {
const { isMacTailscaleActive } = loadInternals();
assert.equal(typeof isMacTailscaleActive, "function");
assert.equal(isMacTailscaleActive({ BackendState: "Running" }), true);
assert.equal(isMacTailscaleActive({ BackendState: "Stopped" }), false);
});
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"]);
});

View File

@@ -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"

View File

@@ -32,14 +32,24 @@ pnpm approve-builds
pnpm rebuild better-sqlite3 esbuild pnpm rebuild better-sqlite3 esbuild
``` ```
## Updating CloakBrowser
```bash
cd ~/.openclaw/workspace/skills/web-automation/scripts
pnpm up cloakbrowser playwright-core
npx cloakbrowser install
pnpm approve-builds
pnpm rebuild better-sqlite3 esbuild
```
## Prerequisite Check (MANDATORY) ## Prerequisite Check (MANDATORY)
Before running any automation, verify CloakBrowser and Playwright Core dependencies are installed and scripts are configured to use CloakBrowser. Before running any automation, verify CloakBrowser and Playwright Core dependencies are installed and scripts are configured to use CloakBrowser.
```bash ```bash
cd ~/.openclaw/workspace/skills/web-automation/scripts cd ~/.openclaw/workspace/skills/web-automation/scripts
node -e "require.resolve('cloakbrowser');require.resolve('playwright-core/package.json');console.log('OK: cloakbrowser + playwright-core installed')" node --input-type=module -e "await import('cloakbrowser');import 'playwright-core';console.log('OK: cloakbrowser + playwright-core installed')"
node -e "const fs=require('fs');const t=fs.readFileSync('browse.ts','utf8');if(!/launchPersistentContext\s*from\s*\'cloakbrowser\'/.test(t)){throw new Error('browse.ts is not configured for CloakBrowser')}console.log('OK: CloakBrowser integration detected in browse.ts')" node -e "const fs=require('fs');const t=fs.readFileSync('browse.ts','utf8');if(!/import\s*\{[^}]*launchPersistentContext[^}]*\}\s*from\s*['\"]cloakbrowser['\"]/.test(t)){throw new Error('browse.ts is not configured for CloakBrowser')}console.log('OK: CloakBrowser integration detected in browse.ts')"
``` ```
If any check fails, stop and return: If any check fails, stop and return:

View File

@@ -15,7 +15,7 @@
"cloakbrowser": "^0.3.14", "cloakbrowser": "^0.3.14",
"jsdom": "^24.0.0", "jsdom": "^24.0.0",
"minimist": "^1.2.8", "minimist": "^1.2.8",
"playwright-core": "^1.40.0", "playwright-core": "^1.58.2",
"turndown": "^7.1.2", "turndown": "^7.1.2",
"turndown-plugin-gfm": "^1.0.2" "turndown-plugin-gfm": "^1.0.2"
}, },

View File

@@ -16,7 +16,7 @@ importers:
version: 12.6.2 version: 12.6.2
cloakbrowser: cloakbrowser:
specifier: ^0.3.14 specifier: ^0.3.14
version: 0.3.14(mmdb-lib@3.0.1)(playwright-core@1.57.0) version: 0.3.14(mmdb-lib@3.0.1)(playwright-core@1.58.2)
jsdom: jsdom:
specifier: ^24.0.0 specifier: ^24.0.0
version: 24.1.3 version: 24.1.3
@@ -24,8 +24,8 @@ importers:
specifier: ^1.2.8 specifier: ^1.2.8
version: 1.2.8 version: 1.2.8
playwright-core: playwright-core:
specifier: ^1.40.0 specifier: ^1.58.2
version: 1.57.0 version: 1.58.2
turndown: turndown:
specifier: ^7.1.2 specifier: ^7.1.2
version: 7.2.2 version: 7.2.2
@@ -534,8 +534,8 @@ packages:
parse5@7.3.0: parse5@7.3.0:
resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==}
playwright-core@1.57.0: playwright-core@1.58.2:
resolution: {integrity: sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==} resolution: {integrity: sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==}
engines: {node: '>=18'} engines: {node: '>=18'}
hasBin: true hasBin: true
@@ -873,12 +873,12 @@ snapshots:
chownr@3.0.0: {} chownr@3.0.0: {}
cloakbrowser@0.3.14(mmdb-lib@3.0.1)(playwright-core@1.57.0): cloakbrowser@0.3.14(mmdb-lib@3.0.1)(playwright-core@1.58.2):
dependencies: dependencies:
tar: 7.5.11 tar: 7.5.11
optionalDependencies: optionalDependencies:
mmdb-lib: 3.0.1 mmdb-lib: 3.0.1
playwright-core: 1.57.0 playwright-core: 1.58.2
combined-stream@1.0.8: combined-stream@1.0.8:
dependencies: dependencies:
@@ -1122,7 +1122,7 @@ snapshots:
dependencies: dependencies:
entities: 6.0.1 entities: 6.0.1
playwright-core@1.57.0: {} playwright-core@1.58.2: {}
prebuild-install@7.1.3: prebuild-install@7.1.3:
dependencies: dependencies: