diff --git a/skills/nordvpn-client/scripts/nordvpn-client.js b/skills/nordvpn-client/scripts/nordvpn-client.js index e840515..5d8ab17 100644 --- a/skills/nordvpn-client/scripts/nordvpn-client.js +++ b/skills/nordvpn-client/scripts/nordvpn-client.js @@ -417,6 +417,39 @@ function normalizeSuccessfulConnectState(state, connectResult, verified) { }; } +function normalizeStatusState(state) { + if ( + !state || + state.platform !== "darwin" || + state.controlMode !== "wireguard" || + state.connected || + !state.wireguard || + state.wireguard.active || + !state.wireguard.endpoint || + !state.wireguard.lastConnection || + !state.publicIp || + !state.publicIp.ok + ) { + return state; + } + + const target = + state.wireguard.lastConnection.resolvedTarget || + state.wireguard.lastConnection.requestedTarget; + if (!target || !locationMatches(state.publicIp, target)) { + return state; + } + + return { + ...state, + connected: true, + wireguard: { + ...state.wireguard, + active: true, + }, + }; +} + function fetchJson(url, headers = {}) { return new Promise((resolve) => { const targetUrl = new URL(url); @@ -1293,7 +1326,7 @@ async function main() { try { if (action === "status") { const ipInfo = await getPublicIpInfo(); - emitJson(buildStateSummary(installProbe, ipInfo)); + emitJson(normalizeStatusState(buildStateSummary(installProbe, ipInfo))); } if (action === "install") { diff --git a/skills/nordvpn-client/scripts/nordvpn-client.test.js b/skills/nordvpn-client/scripts/nordvpn-client.test.js index d606ea6..d2a72eb 100644 --- a/skills/nordvpn-client/scripts/nordvpn-client.test.js +++ b/skills/nordvpn-client/scripts/nordvpn-client.test.js @@ -23,6 +23,8 @@ module.exports = { typeof isMacTailscaleActive === "function" ? isMacTailscaleActive : undefined, normalizeSuccessfulConnectState: typeof normalizeSuccessfulConnectState === "function" ? normalizeSuccessfulConnectState : undefined, + normalizeStatusState: + typeof normalizeStatusState === "function" ? normalizeStatusState : undefined, sanitizeOutputPayload: typeof sanitizeOutputPayload === "function" ? sanitizeOutputPayload : undefined, shouldAttemptMacWireguardDisconnect: @@ -204,6 +206,33 @@ test("normalizeSuccessfulConnectState marks the connect snapshot active after ve 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");