@aemeath/surf-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +102 -0
- package/SECURITY.md +21 -0
- package/dist/adapters/credential-store.d.ts +26 -0
- package/dist/adapters/credential-store.js +69 -0
- package/dist/adapters/credential-store.js.map +1 -0
- package/dist/adapters/openvpn-runner.d.ts +56 -0
- package/dist/adapters/openvpn-runner.js +413 -0
- package/dist/adapters/openvpn-runner.js.map +1 -0
- package/dist/adapters/state-store.d.ts +10 -0
- package/dist/adapters/state-store.js +38 -0
- package/dist/adapters/state-store.js.map +1 -0
- package/dist/adapters/surfshark-account-client.d.ts +31 -0
- package/dist/adapters/surfshark-account-client.js +226 -0
- package/dist/adapters/surfshark-account-client.js.map +1 -0
- package/dist/adapters/surfshark-config-client.d.ts +6 -0
- package/dist/adapters/surfshark-config-client.js +29 -0
- package/dist/adapters/surfshark-config-client.js.map +1 -0
- package/dist/adapters/xdg-paths.d.ts +14 -0
- package/dist/adapters/xdg-paths.js +46 -0
- package/dist/adapters/xdg-paths.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +81 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/auth.d.ts +3 -0
- package/dist/commands/auth.js +118 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/doctor.d.ts +3 -0
- package/dist/commands/doctor.js +21 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/prompt.d.ts +4 -0
- package/dist/commands/prompt.js +35 -0
- package/dist/commands/prompt.js.map +1 -0
- package/dist/commands/servers.d.ts +3 -0
- package/dist/commands/servers.js +38 -0
- package/dist/commands/servers.js.map +1 -0
- package/dist/commands/vpn.d.ts +7 -0
- package/dist/commands/vpn.js +137 -0
- package/dist/commands/vpn.js.map +1 -0
- package/dist/core/errors.d.ts +12 -0
- package/dist/core/errors.js +37 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/types.d.ts +56 -0
- package/dist/core/types.js +29 -0
- package/dist/core/types.js.map +1 -0
- package/dist/services/auth-service.d.ts +16 -0
- package/dist/services/auth-service.js +49 -0
- package/dist/services/auth-service.js.map +1 -0
- package/dist/services/doctor-service.d.ts +11 -0
- package/dist/services/doctor-service.js +61 -0
- package/dist/services/doctor-service.js.map +1 -0
- package/dist/services/server-catalog-service.d.ts +17 -0
- package/dist/services/server-catalog-service.js +112 -0
- package/dist/services/server-catalog-service.js.map +1 -0
- package/dist/services/vpn-service.d.ts +26 -0
- package/dist/services/vpn-service.js +133 -0
- package/dist/services/vpn-service.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
2
|
+
import { SurfCliError } from '../core/errors.js';
|
|
3
|
+
import { openVpnCredentialsSchema } from '../core/types.js';
|
|
4
|
+
class CookieJar {
|
|
5
|
+
cookies = new Map();
|
|
6
|
+
setFromHeaders(headers) {
|
|
7
|
+
const getSetCookie = headers.getSetCookie;
|
|
8
|
+
const values = typeof getSetCookie === 'function'
|
|
9
|
+
? getSetCookie.call(headers)
|
|
10
|
+
: splitSetCookie(headers.get('set-cookie'));
|
|
11
|
+
for (const value of values) {
|
|
12
|
+
const [pair] = value.split(';');
|
|
13
|
+
if (!pair) {
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
const separatorIndex = pair.indexOf('=');
|
|
17
|
+
if (separatorIndex < 1) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
const name = pair.slice(0, separatorIndex).trim();
|
|
21
|
+
const cookieValue = pair.slice(separatorIndex + 1).trim();
|
|
22
|
+
this.cookies.set(name, cookieValue);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
header() {
|
|
26
|
+
if (this.cookies.size === 0) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
return [...this.cookies.entries()].map(([key, value]) => `${key}=${value}`).join('; ');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export class SurfsharkAccountClient {
|
|
33
|
+
baseUrl;
|
|
34
|
+
jar = new CookieJar();
|
|
35
|
+
constructor(baseUrl = 'https://my.surfshark.com') {
|
|
36
|
+
this.baseUrl = baseUrl;
|
|
37
|
+
}
|
|
38
|
+
async loginWithEmail(email, password) {
|
|
39
|
+
const result = await this.request('/auth/login', {
|
|
40
|
+
method: 'POST',
|
|
41
|
+
body: { username: email.trim(), password }
|
|
42
|
+
});
|
|
43
|
+
if (!result.ok) {
|
|
44
|
+
const errorCode = extractSurfsharkErrorCode(result.body, result.status);
|
|
45
|
+
if (errorCode === 423) {
|
|
46
|
+
return {
|
|
47
|
+
authenticated: false,
|
|
48
|
+
twoFactorRequired: true,
|
|
49
|
+
credentials: null,
|
|
50
|
+
message: 'Surfshark account requires 2FA. Use --manual or --code.'
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
authenticated: false,
|
|
55
|
+
twoFactorRequired: false,
|
|
56
|
+
credentials: null,
|
|
57
|
+
message: `Surfshark account login failed with status ${result.status}. Use manual OpenVPN credentials.`
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const credentials = await this.fetchOpenVpnCredentialsFromSession();
|
|
61
|
+
return {
|
|
62
|
+
authenticated: true,
|
|
63
|
+
twoFactorRequired: false,
|
|
64
|
+
credentials,
|
|
65
|
+
message: credentials
|
|
66
|
+
? 'Surfshark account login succeeded and OpenVPN service credentials were found.'
|
|
67
|
+
: 'Surfshark account login succeeded, but OpenVPN service credentials were not exposed by the current account API response.'
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
async createLoginCodeChallenge() {
|
|
71
|
+
const result = await this.request('/auth/api/v1/account/authorization/create', {
|
|
72
|
+
method: 'POST'
|
|
73
|
+
});
|
|
74
|
+
if (!result.ok || !result.body) {
|
|
75
|
+
throw new SurfCliError('AUTH_FAILED', `Could not create Surfshark login code challenge. Status: ${result.status}`);
|
|
76
|
+
}
|
|
77
|
+
const code = getString(result.body, 'code');
|
|
78
|
+
const hash = getString(result.body, 'hash');
|
|
79
|
+
const expiresAfter = getNumber(result.body, 'expiresAfter') ?? getNumber(result.body, 'expires_after');
|
|
80
|
+
if (!code || !hash || !expiresAfter) {
|
|
81
|
+
throw new SurfCliError('AUTH_FAILED', 'Surfshark login code challenge response did not include code, hash, and expiry.');
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
code: code.toUpperCase(),
|
|
85
|
+
hash,
|
|
86
|
+
expiresAfterSeconds: expiresAfter
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
async waitForLoginCodeActivation(challenge, options = {}) {
|
|
90
|
+
const pollIntervalMs = options.pollIntervalMs ?? 5_000;
|
|
91
|
+
const timeoutMs = options.timeoutMs ?? challenge.expiresAfterSeconds * 1_000;
|
|
92
|
+
const deadline = Date.now() + timeoutMs;
|
|
93
|
+
while (Date.now() < deadline) {
|
|
94
|
+
const result = await this.request('/auth/login-code', {
|
|
95
|
+
method: 'POST',
|
|
96
|
+
body: { hash: challenge.hash }
|
|
97
|
+
});
|
|
98
|
+
if (result.ok) {
|
|
99
|
+
const credentials = await this.fetchOpenVpnCredentialsFromSession();
|
|
100
|
+
return {
|
|
101
|
+
activated: true,
|
|
102
|
+
credentials,
|
|
103
|
+
message: credentials
|
|
104
|
+
? 'Surfshark code login succeeded and OpenVPN service credentials were found.'
|
|
105
|
+
: 'Surfshark code login succeeded, but OpenVPN service credentials were not exposed by the current account API response.'
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
await delay(pollIntervalMs);
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
activated: false,
|
|
112
|
+
credentials: null,
|
|
113
|
+
message: 'Surfshark login code expired before activation.'
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
async fetchOpenVpnCredentialsFromSession() {
|
|
117
|
+
const result = await this.request('/auth/p_api/v2/account/users/me', {
|
|
118
|
+
method: 'GET'
|
|
119
|
+
});
|
|
120
|
+
if (!result.ok || !result.body) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
return extractOpenVpnCredentials(result.body);
|
|
124
|
+
}
|
|
125
|
+
async request(path, options) {
|
|
126
|
+
const headers = {
|
|
127
|
+
Accept: 'application/json',
|
|
128
|
+
'User-Agent': 'surf-cli/0.1'
|
|
129
|
+
};
|
|
130
|
+
if (options.body !== undefined) {
|
|
131
|
+
headers['Content-Type'] = 'application/json';
|
|
132
|
+
}
|
|
133
|
+
const cookieHeader = this.jar.header();
|
|
134
|
+
if (cookieHeader) {
|
|
135
|
+
headers.Cookie = cookieHeader;
|
|
136
|
+
}
|
|
137
|
+
let response;
|
|
138
|
+
try {
|
|
139
|
+
response = await fetch(new URL(path, this.baseUrl), {
|
|
140
|
+
method: options.method,
|
|
141
|
+
headers,
|
|
142
|
+
...(options.body === undefined ? {} : { body: JSON.stringify(options.body) }),
|
|
143
|
+
redirect: 'manual'
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
throw new SurfCliError('NETWORK_ERROR', 'Could not reach Surfshark account API.', {
|
|
148
|
+
cause: error
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
this.jar.setFromHeaders(response.headers);
|
|
152
|
+
const body = await parseJsonBody(response);
|
|
153
|
+
return {
|
|
154
|
+
ok: response.ok || response.status === 302,
|
|
155
|
+
status: response.status,
|
|
156
|
+
body
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
export function extractOpenVpnCredentials(value) {
|
|
161
|
+
return findCredentials(value, []);
|
|
162
|
+
}
|
|
163
|
+
function findCredentials(value, path) {
|
|
164
|
+
if (!value || typeof value !== 'object') {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
if (!Array.isArray(value)) {
|
|
168
|
+
const record = value;
|
|
169
|
+
const pathHint = path.join('.').toLowerCase();
|
|
170
|
+
const username = record.username;
|
|
171
|
+
const password = record.password;
|
|
172
|
+
const looksLikeCredentialContainer = /credential|openvpn|manual|service|vpn/.test(pathHint);
|
|
173
|
+
if (looksLikeCredentialContainer &&
|
|
174
|
+
typeof username === 'string' &&
|
|
175
|
+
typeof password === 'string') {
|
|
176
|
+
const parsed = openVpnCredentialsSchema.safeParse({ username, password });
|
|
177
|
+
if (parsed.success) {
|
|
178
|
+
return parsed.data;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
for (const [key, nested] of Object.entries(record)) {
|
|
182
|
+
const found = findCredentials(nested, [...path, key]);
|
|
183
|
+
if (found) {
|
|
184
|
+
return found;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
for (const [index, nested] of value.entries()) {
|
|
190
|
+
const found = findCredentials(nested, [...path, String(index)]);
|
|
191
|
+
if (found) {
|
|
192
|
+
return found;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
async function parseJsonBody(response) {
|
|
198
|
+
const text = await response.text();
|
|
199
|
+
if (!text) {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
try {
|
|
203
|
+
return JSON.parse(text);
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function splitSetCookie(header) {
|
|
210
|
+
if (!header) {
|
|
211
|
+
return [];
|
|
212
|
+
}
|
|
213
|
+
return header.split(/,(?=\s*[^;=]+=[^;]+)/g);
|
|
214
|
+
}
|
|
215
|
+
function extractSurfsharkErrorCode(body, fallback) {
|
|
216
|
+
return body?.error?.code ?? body?.code ?? fallback;
|
|
217
|
+
}
|
|
218
|
+
function getString(record, key) {
|
|
219
|
+
const value = record[key];
|
|
220
|
+
return typeof value === 'string' ? value : null;
|
|
221
|
+
}
|
|
222
|
+
function getNumber(record, key) {
|
|
223
|
+
const value = record[key];
|
|
224
|
+
return typeof value === 'number' ? value : null;
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=surfshark-account-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"surfshark-account-client.js","sourceRoot":"","sources":["../../src/adapters/surfshark-account-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { SurfCliError } from '../core/errors.js';
|
|
2
|
+
export const SURFSHARK_OPENVPN_CONFIG_URL = 'https://surfshark.com/api/v1/server/configurations';
|
|
3
|
+
export class SurfsharkConfigClient {
|
|
4
|
+
url;
|
|
5
|
+
constructor(url = SURFSHARK_OPENVPN_CONFIG_URL) {
|
|
6
|
+
this.url = url;
|
|
7
|
+
}
|
|
8
|
+
async fetchOpenVpnConfigurations() {
|
|
9
|
+
let response;
|
|
10
|
+
try {
|
|
11
|
+
response = await fetch(this.url, {
|
|
12
|
+
headers: {
|
|
13
|
+
Accept: 'application/zip, application/octet-stream',
|
|
14
|
+
'User-Agent': 'surf-cli/0.1'
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
throw new SurfCliError('NETWORK_ERROR', 'Could not download Surfshark OpenVPN configurations.', {
|
|
20
|
+
cause: error
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
if (!response.ok) {
|
|
24
|
+
throw new SurfCliError('NETWORK_ERROR', `Surfshark OpenVPN configuration download failed with status ${response.status}.`);
|
|
25
|
+
}
|
|
26
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=surfshark-config-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"surfshark-config-client.js","sourceRoot":"","sources":["../../src/adapters/surfshark-config-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,MAAM,CAAC,MAAM,4BAA4B,GAAG,oDAAoD,CAAC;AAEjG,MAAM,OAAO,qBAAqB;IACH;IAA7B,YAA6B,MAAM,4BAA4B;QAAlC,QAAG,GAAH,GAAG,CAA+B;IAAG,CAAC;IAEnE,KAAK,CAAC,0BAA0B;QAC9B,IAAI,QAAkB,CAAC;QACvB,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE;gBAC/B,OAAO,EAAE;oBACP,MAAM,EAAE,2CAA2C;oBACnD,YAAY,EAAE,cAAc;iBAC7B;aACF,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,YAAY,CACpB,eAAe,EACf,sDAAsD,EACtD;gBACE,KAAK,EAAE,KAAK;aACb,CACF,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,YAAY,CACpB,eAAe,EACf,+DAA+D,QAAQ,CAAC,MAAM,GAAG,CAClF,CAAC;QACJ,CAAC;QAED,OAAO,IAAI,UAAU,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;IACtD,CAAC;CACF"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface AppPaths {
|
|
2
|
+
configDir: string;
|
|
3
|
+
cacheDir: string;
|
|
4
|
+
stateDir: string;
|
|
5
|
+
runtimeDir: string;
|
|
6
|
+
openVpnConfigDir: string;
|
|
7
|
+
stateFile: string;
|
|
8
|
+
pidFile: string;
|
|
9
|
+
statusFile: string;
|
|
10
|
+
authFile: string;
|
|
11
|
+
logFile: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function getAppPaths(): AppPaths;
|
|
14
|
+
export declare function ensureAppDirectories(paths?: AppPaths): Promise<void>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { mkdir } from 'node:fs/promises';
|
|
4
|
+
const APP_DIR_NAME = 'surf-cli';
|
|
5
|
+
function absoluteEnvPath(name, fallback) {
|
|
6
|
+
const value = process.env[name];
|
|
7
|
+
if (value && path.isAbsolute(value)) {
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
return fallback;
|
|
11
|
+
}
|
|
12
|
+
function currentUidSuffix() {
|
|
13
|
+
const getuid = process.getuid;
|
|
14
|
+
return typeof getuid === 'function' ? String(getuid()) : String(process.pid);
|
|
15
|
+
}
|
|
16
|
+
export function getAppPaths() {
|
|
17
|
+
const home = os.homedir();
|
|
18
|
+
const configHome = absoluteEnvPath('XDG_CONFIG_HOME', path.join(home, '.config'));
|
|
19
|
+
const cacheHome = absoluteEnvPath('XDG_CACHE_HOME', path.join(home, '.cache'));
|
|
20
|
+
const stateHome = absoluteEnvPath('XDG_STATE_HOME', path.join(home, '.local', 'state'));
|
|
21
|
+
const runtimeHome = absoluteEnvPath('XDG_RUNTIME_DIR', path.join(os.tmpdir(), `${APP_DIR_NAME}-${currentUidSuffix()}`));
|
|
22
|
+
const configDir = path.join(configHome, APP_DIR_NAME);
|
|
23
|
+
const cacheDir = path.join(cacheHome, APP_DIR_NAME);
|
|
24
|
+
const stateDir = path.join(stateHome, APP_DIR_NAME);
|
|
25
|
+
const runtimeDir = path.join(runtimeHome, APP_DIR_NAME);
|
|
26
|
+
return {
|
|
27
|
+
configDir,
|
|
28
|
+
cacheDir,
|
|
29
|
+
stateDir,
|
|
30
|
+
runtimeDir,
|
|
31
|
+
openVpnConfigDir: path.join(cacheDir, 'openvpn-configs'),
|
|
32
|
+
stateFile: path.join(stateDir, 'state.json'),
|
|
33
|
+
pidFile: path.join(runtimeDir, 'openvpn.pid'),
|
|
34
|
+
statusFile: path.join(runtimeDir, 'openvpn.status'),
|
|
35
|
+
authFile: path.join(runtimeDir, 'openvpn.auth'),
|
|
36
|
+
logFile: path.join(stateDir, 'openvpn.log')
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export async function ensureAppDirectories(paths = getAppPaths()) {
|
|
40
|
+
await mkdir(paths.configDir, { recursive: true, mode: 0o700 });
|
|
41
|
+
await mkdir(paths.cacheDir, { recursive: true, mode: 0o700 });
|
|
42
|
+
await mkdir(paths.stateDir, { recursive: true, mode: 0o700 });
|
|
43
|
+
await mkdir(paths.runtimeDir, { recursive: true, mode: 0o700 });
|
|
44
|
+
await mkdir(paths.openVpnConfigDir, { recursive: true, mode: 0o700 });
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=xdg-paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xdg-paths.js","sourceRoot":"","sources":["../../src/adapters/xdg-paths.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { Command, CommanderError } from 'commander';
|
|
4
|
+
import { isSurfCliError, toErrorMessage } from './core/errors.js';
|
|
5
|
+
import { createAuthCommand } from './commands/auth.js';
|
|
6
|
+
import { createServersCommand } from './commands/servers.js';
|
|
7
|
+
import { createConnectCommand, createDisconnectCommand, createKillCommand, createStatusCommand } from './commands/vpn.js';
|
|
8
|
+
import { createDoctorCommand } from './commands/doctor.js';
|
|
9
|
+
import { VpnService } from './services/vpn-service.js';
|
|
10
|
+
const require = createRequire(import.meta.url);
|
|
11
|
+
const packageJson = require('../package.json');
|
|
12
|
+
export function createProgram() {
|
|
13
|
+
const program = new Command();
|
|
14
|
+
program
|
|
15
|
+
.name('surf-cli')
|
|
16
|
+
.description('CLI for connecting to Surfshark with OpenVPN')
|
|
17
|
+
.version(packageJson.version)
|
|
18
|
+
.showHelpAfterError('(add --help for more information)')
|
|
19
|
+
.showSuggestionAfterError()
|
|
20
|
+
.exitOverride();
|
|
21
|
+
program.addCommand(createAuthCommand());
|
|
22
|
+
program.addCommand(createServersCommand());
|
|
23
|
+
program.addCommand(createConnectCommand());
|
|
24
|
+
program.addCommand(createDisconnectCommand());
|
|
25
|
+
program.addCommand(createKillCommand());
|
|
26
|
+
program.addCommand(createStatusCommand());
|
|
27
|
+
program.addCommand(createDoctorCommand());
|
|
28
|
+
return program;
|
|
29
|
+
}
|
|
30
|
+
async function main() {
|
|
31
|
+
const cleanup = installSignalHandlers();
|
|
32
|
+
try {
|
|
33
|
+
await createProgram().parseAsync(process.argv);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (error instanceof CommanderError) {
|
|
37
|
+
if (error.exitCode !== 0) {
|
|
38
|
+
process.exitCode = error.exitCode;
|
|
39
|
+
}
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (isSurfCliError(error)) {
|
|
43
|
+
console.error(`${error.code}: ${toErrorMessage(error)}`);
|
|
44
|
+
process.exitCode = error.exitCode;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
console.error(toErrorMessage(error));
|
|
48
|
+
process.exitCode = 1;
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
cleanup();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function installSignalHandlers() {
|
|
55
|
+
let handling = false;
|
|
56
|
+
const handler = (signal) => {
|
|
57
|
+
if (handling)
|
|
58
|
+
return;
|
|
59
|
+
handling = true;
|
|
60
|
+
// Best-effort disconnect; don't block exit if it fails
|
|
61
|
+
const vpnService = new VpnService();
|
|
62
|
+
vpnService
|
|
63
|
+
.disconnect()
|
|
64
|
+
.catch(() => {
|
|
65
|
+
// Ignore errors during signal cleanup
|
|
66
|
+
})
|
|
67
|
+
.finally(() => {
|
|
68
|
+
// Re-raise the signal to exit with correct signal status
|
|
69
|
+
process.removeAllListeners(signal);
|
|
70
|
+
process.kill(process.pid, signal);
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
process.on('SIGINT', handler);
|
|
74
|
+
process.on('SIGTERM', handler);
|
|
75
|
+
return () => {
|
|
76
|
+
process.off('SIGINT', handler);
|
|
77
|
+
process.off('SIGTERM', handler);
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
await main();
|
|
81
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { input, password as passwordPrompt, select } from '@inquirer/prompts';
|
|
3
|
+
import { AuthService } from '../services/auth-service.js';
|
|
4
|
+
import { SurfCliError } from '../core/errors.js';
|
|
5
|
+
import { promptForManualCredentials } from './prompt.js';
|
|
6
|
+
export function createAuthCommand(authService = new AuthService()) {
|
|
7
|
+
const auth = new Command('auth').description('Manage Surfshark OpenVPN service credentials');
|
|
8
|
+
auth
|
|
9
|
+
.command('login')
|
|
10
|
+
.description('Log in or store Surfshark OpenVPN service credentials')
|
|
11
|
+
.option('--manual', 'enter OpenVPN service credentials manually')
|
|
12
|
+
.option('--email', 'try experimental Surfshark account email/password login first')
|
|
13
|
+
.option('--code', 'try experimental Surfshark app code login first')
|
|
14
|
+
.action(async (options) => {
|
|
15
|
+
const mode = await resolveLoginMode(options);
|
|
16
|
+
if (mode === 'manual') {
|
|
17
|
+
await saveManual(authService);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (mode === 'email') {
|
|
21
|
+
await loginWithEmail(authService);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
await loginWithCode(authService);
|
|
25
|
+
});
|
|
26
|
+
auth
|
|
27
|
+
.command('status')
|
|
28
|
+
.description('Show credential store status')
|
|
29
|
+
.option('--json', 'print machine-readable JSON')
|
|
30
|
+
.action(async (options) => {
|
|
31
|
+
const storeStatus = await authService.credentialStoreStatus();
|
|
32
|
+
const hasCredentials = !!(await authService.getCredentials().catch(() => null));
|
|
33
|
+
const payload = { hasCredentials, credentialStore: storeStatus };
|
|
34
|
+
if (options.json) {
|
|
35
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
console.log(`Credential store: ${storeStatus.backend} (${storeStatus.available ? 'available' : 'unavailable'})`);
|
|
39
|
+
console.log(`OpenVPN credentials: ${hasCredentials ? 'configured' : 'not configured'}`);
|
|
40
|
+
if (storeStatus.message) {
|
|
41
|
+
console.error(storeStatus.message);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
auth
|
|
45
|
+
.command('logout')
|
|
46
|
+
.description('Delete stored Surfshark OpenVPN service credentials')
|
|
47
|
+
.action(async () => {
|
|
48
|
+
await authService.clearCredentials();
|
|
49
|
+
console.log('Stored Surfshark OpenVPN service credentials were deleted.');
|
|
50
|
+
});
|
|
51
|
+
return auth;
|
|
52
|
+
}
|
|
53
|
+
async function resolveLoginMode(options) {
|
|
54
|
+
const selected = [options.manual, options.email, options.code].filter(Boolean).length;
|
|
55
|
+
if (selected > 1) {
|
|
56
|
+
throw new SurfCliError('INVALID_USAGE', 'Choose only one login mode: --manual, --email, or --code.');
|
|
57
|
+
}
|
|
58
|
+
if (options.manual)
|
|
59
|
+
return 'manual';
|
|
60
|
+
if (options.email)
|
|
61
|
+
return 'email';
|
|
62
|
+
if (options.code)
|
|
63
|
+
return 'code';
|
|
64
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
65
|
+
return 'manual';
|
|
66
|
+
}
|
|
67
|
+
return select({
|
|
68
|
+
message: 'How would you like to configure Surfshark credentials?',
|
|
69
|
+
choices: [
|
|
70
|
+
{
|
|
71
|
+
name: 'Manual OpenVPN service credentials (recommended)',
|
|
72
|
+
value: 'manual'
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'Experimental account email/password login, then manual fallback',
|
|
76
|
+
value: 'email'
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: 'Experimental app code login, then manual fallback',
|
|
80
|
+
value: 'code'
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
async function saveManual(authService) {
|
|
86
|
+
const credentials = await promptForManualCredentials();
|
|
87
|
+
await authService.saveManualCredentials(credentials);
|
|
88
|
+
console.log('Surfshark OpenVPN service credentials were stored in the OS keyring.');
|
|
89
|
+
}
|
|
90
|
+
async function loginWithEmail(authService) {
|
|
91
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
92
|
+
throw new SurfCliError('INVALID_USAGE', 'Email login requires an interactive terminal.');
|
|
93
|
+
}
|
|
94
|
+
const email = await input({ message: 'Surfshark account email' });
|
|
95
|
+
const password = await passwordPrompt({ message: 'Surfshark account password', mask: '*' });
|
|
96
|
+
const outcome = await authService.loginWithEmail(email, password);
|
|
97
|
+
console.error(outcome.message);
|
|
98
|
+
if (outcome.credentials) {
|
|
99
|
+
console.log('Surfshark OpenVPN service credentials were stored in the OS keyring.');
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
console.error('Falling back to manual OpenVPN service credential entry.');
|
|
103
|
+
await saveManual(authService);
|
|
104
|
+
}
|
|
105
|
+
async function loginWithCode(authService) {
|
|
106
|
+
const challenge = await authService.createLoginCodeChallenge();
|
|
107
|
+
console.log(`Surfshark login code: ${challenge.code}`);
|
|
108
|
+
console.error('Open a logged-in Surfshark app and enter this code from Settings > My account > Enter login code.');
|
|
109
|
+
const outcome = await authService.waitForLoginCodeActivation(challenge);
|
|
110
|
+
console.error(outcome.message);
|
|
111
|
+
if (outcome.credentials) {
|
|
112
|
+
console.log('Surfshark OpenVPN service credentials were stored in the OS keyring.');
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
console.error('Falling back to manual OpenVPN service credential entry.');
|
|
116
|
+
await saveManual(authService);
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { DoctorService } from '../services/doctor-service.js';
|
|
3
|
+
export function createDoctorCommand(doctorService = new DoctorService()) {
|
|
4
|
+
return new Command('doctor')
|
|
5
|
+
.description('Check local prerequisites for surf-cli')
|
|
6
|
+
.option('--json', 'print machine-readable JSON')
|
|
7
|
+
.action(async (options) => {
|
|
8
|
+
const checks = await doctorService.run();
|
|
9
|
+
if (options.json) {
|
|
10
|
+
console.log(JSON.stringify(checks, null, 2));
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
for (const check of checks) {
|
|
14
|
+
console.log(`${check.ok ? '✓' : '✗'} ${check.name}: ${check.message}`);
|
|
15
|
+
}
|
|
16
|
+
if (checks.some((check) => !check.ok)) {
|
|
17
|
+
process.exitCode = 3;
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAE9D,MAAM,UAAU,mBAAmB,CAAC,aAAa,GAAG,IAAI,aAAa,EAAE;IACrE,OAAO,IAAI,OAAO,CAAC,QAAQ,CAAC;SACzB,WAAW,CAAC,wCAAwC,CAAC;SACrD,MAAM,CAAC,QAAQ,EAAE,6BAA6B,CAAC;SAC/C,MAAM,CAAC,KAAK,EAAE,OAA2B,EAAE,EAAE;QAC5C,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,CAAC;QACzC,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAC7C,OAAO;QACT,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACzE,CAAC;QAED,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;YACtC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { OpenVpnCredentials, ServerProfile } from '../core/types.js';
|
|
2
|
+
export declare function assertInteractive(message: string): void;
|
|
3
|
+
export declare function promptForManualCredentials(): Promise<OpenVpnCredentials>;
|
|
4
|
+
export declare function promptForServer(profiles: ServerProfile[]): Promise<ServerProfile>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { input, password as passwordPrompt, search } from '@inquirer/prompts';
|
|
2
|
+
import { SurfCliError } from '../core/errors.js';
|
|
3
|
+
export function assertInteractive(message) {
|
|
4
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
5
|
+
throw new SurfCliError('INVALID_USAGE', message);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export async function promptForManualCredentials() {
|
|
9
|
+
assertInteractive('Manual credential entry requires an interactive terminal.');
|
|
10
|
+
const username = await input({ message: 'Surfshark OpenVPN service username' });
|
|
11
|
+
const password = await passwordPrompt({
|
|
12
|
+
message: 'Surfshark OpenVPN service password',
|
|
13
|
+
mask: '*'
|
|
14
|
+
});
|
|
15
|
+
return { username, password };
|
|
16
|
+
}
|
|
17
|
+
export async function promptForServer(profiles) {
|
|
18
|
+
assertInteractive('Server selection requires an interactive terminal. Pass a server name explicitly.');
|
|
19
|
+
const allChoices = profiles.map((profile) => ({
|
|
20
|
+
name: `${profile.locationCode} (${profile.protocol.toUpperCase()})`,
|
|
21
|
+
value: profile
|
|
22
|
+
}));
|
|
23
|
+
return search({
|
|
24
|
+
message: 'Search Surfshark server',
|
|
25
|
+
source: (term) => {
|
|
26
|
+
if (!term)
|
|
27
|
+
return allChoices;
|
|
28
|
+
const query = term.toLowerCase();
|
|
29
|
+
return allChoices.filter((choice) => choice.name.toLowerCase().includes(query) ||
|
|
30
|
+
choice.value.id.toLowerCase().includes(query) ||
|
|
31
|
+
choice.value.host.toLowerCase().includes(query));
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=prompt.js.map
|