fix: clarify mac nordvpn app-only mode

This commit is contained in:
Stefano Fiorini
2026-03-11 22:59:08 -05:00
parent 2612cef1dc
commit b326153d26
3 changed files with 30 additions and 0 deletions

View File

@@ -60,6 +60,7 @@ Do not put secrets in the skill docs or repo.
- platform - platform
- install state - install state
- control mode (`cli` vs `app-manual`)
- auth state - auth state
- connection state - connection state
- requested target - requested target
@@ -75,3 +76,4 @@ After `connect`, the intended workflow is:
- Linux city targeting is attempted through the CLI target string and then validated by public IP/location checks. - Linux city targeting is attempted through the CLI target string and then validated by public IP/location checks.
- macOS app-only fallback cannot guarantee non-interactive control if the app does not expose a CLI. - macOS app-only fallback cannot guarantee non-interactive control if the app does not expose a CLI.
- On macOS, the Homebrew cask may install only the GUI app. That is still a supported install state; `status` reports `controlMode: "app-manual"` so agents should continue with the app flow instead of concluding NordVPN is unavailable.

View File

@@ -63,6 +63,7 @@ Optional credential file env vars:
- platform - platform
- install state - install state
- control mode (`cli` vs `app-manual`)
- auth state - auth state
- connection state - connection state
- requested target - requested target
@@ -76,3 +77,4 @@ Use `verify` when you want an explicit post-connect location check without chang
- Linux country connect is official CLI behavior. - Linux country connect is official CLI behavior.
- Linux city connect is attempted through the CLI target string and then validated by post-connect IP/location checks. - Linux city connect is attempted through the CLI target string and then validated by post-connect IP/location checks.
- macOS app-only fallback cannot guarantee non-interactive login/connect if the installed app does not expose a CLI. In that case the skill will open the app and return a clear manual-action-required result. - macOS app-only fallback cannot guarantee non-interactive login/connect if the installed app does not expose a CLI. In that case the skill will open the app and return a clear manual-action-required result.
- On macOS, Homebrew can install only the GUI app. That still counts as a supported install state; `status` will report `controlMode: "app-manual"` rather than treating the app as missing.

View File

@@ -235,6 +235,27 @@ function inferConnectionState(probe) {
function buildStateSummary(installProbe, ipInfo) { function buildStateSummary(installProbe, ipInfo) {
const cliProbe = installProbe.cliProbe; const cliProbe = installProbe.cliProbe;
let controlMode = "unavailable";
let automaticControl = false;
let loginMode = "unsupported";
let connectMode = "unsupported";
let recommendedAction = "Install NordVPN first.";
if (installProbe.cliPath) {
controlMode = "cli";
automaticControl = true;
loginMode = "cli";
connectMode = "cli";
recommendedAction = "Use login/connect/disconnect through the nordvpn CLI.";
} else if (installProbe.platform === "darwin" && installProbe.appInstalled) {
controlMode = "app-manual";
automaticControl = false;
loginMode = "app-manual";
connectMode = "app-manual";
recommendedAction =
"NordVPN is installed as a macOS app without a PATH-visible CLI. Use login/connect to open NordVPN.app and complete the action there.";
}
return { return {
platform: installProbe.platform, platform: installProbe.platform,
installed: installProbe.installed, installed: installProbe.installed,
@@ -243,6 +264,11 @@ function buildStateSummary(installProbe, ipInfo) {
appInstalled: installProbe.appInstalled, appInstalled: installProbe.appInstalled,
appPath: installProbe.appInstalled ? installProbe.appPath : null, appPath: installProbe.appInstalled ? installProbe.appPath : null,
brewAvailable: Boolean(installProbe.brewPath), brewAvailable: Boolean(installProbe.brewPath),
controlMode,
automaticControl,
loginMode,
connectMode,
recommendedAction,
authenticated: inferAuthState(cliProbe), authenticated: inferAuthState(cliProbe),
connected: inferConnectionState(cliProbe), connected: inferConnectionState(cliProbe),
localStatusRaw: cliProbe && cliProbe.status ? (cliProbe.status.stdout || cliProbe.status.stderr).trim() : "", localStatusRaw: cliProbe && cliProbe.status ? (cliProbe.status.stdout || cliProbe.status.stderr).trim() : "",