@swimmingliu/autovpn 1.6.5 → 1.6.7

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.
@@ -0,0 +1,27 @@
1
+ const SUBSCRIPTION_PAYLOAD = `__MAIN_DATA__`;
2
+
3
+ function buildRandomPayload() {
4
+ const randomBytes = new Uint8Array(Math.floor(Math.random() * 100));
5
+ crypto.getRandomValues(randomBytes);
6
+ return String.fromCharCode.apply(null, randomBytes);
7
+ }
8
+
9
+ async function handleSubscriptionRequest(request) {
10
+ try {
11
+ const url = new URL(request.url);
12
+ const secretToken = url.searchParams.get("serect_key");
13
+ const responsePayload = secretToken === "swimmingliu"
14
+ ? SUBSCRIPTION_PAYLOAD
15
+ : buildRandomPayload();
16
+ return new Response(btoa(responsePayload));
17
+ } catch (error) {
18
+ console.log(error);
19
+ return new Response(error.toString());
20
+ }
21
+ }
22
+
23
+ export default {
24
+ async fetch(request) {
25
+ return handleSubscriptionRequest(request);
26
+ },
27
+ };
@@ -5,7 +5,7 @@ const ZH_MESSAGES = {
5
5
  locale: 'zh-CN',
6
6
  appTitle: 'AutoVPN',
7
7
  sidebarTitle: 'AutoVPN',
8
- sidebarVersion: 'v.1.6.5',
8
+ sidebarVersion: 'v.1.6.7',
9
9
  brandSubtitle: '概览、运行、结果、订阅、日志、设置统一管理',
10
10
  languageLabel: '',
11
11
  saveButton: '保存配置',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swimmingliu/autovpn",
3
- "version": "1.6.5",
3
+ "version": "1.6.7",
4
4
  "description": "npm wrapper for the AutoVPN headless CLI",
5
5
  "type": "module",
6
6
  "repository": {
@@ -14,16 +14,15 @@
14
14
  "files": [
15
15
  "bin/",
16
16
  "dist/",
17
- "lib/",
18
17
  "README.md"
19
18
  ],
20
19
  "publishConfig": {
21
20
  "access": "public"
22
21
  },
23
22
  "scripts": {
24
- "build": "tsc -p tsconfig.json && node scripts/copy-web-assets.mjs",
23
+ "build": "node -e \"require('fs').rmSync('dist', { recursive: true, force: true })\" && tsc -p tsconfig.json && node scripts/copy-web-assets.mjs",
25
24
  "prepack": "npm run build",
26
- "test": "npm run build && node --test test/*.test.mjs test/doctor/*.test.mjs test/parity/*.test.mjs test/jobs/*.test.mjs test/pipeline/*.test.mjs test/runtime/*.test.mjs test/server/*.test.mjs"
25
+ "test": "npm run build && node --test test/*.test.mjs test/doctor/*.test.mjs test/jobs/*.test.mjs test/pipeline/*.test.mjs test/runtime/*.test.mjs test/server/*.test.mjs"
27
26
  },
28
27
  "engines": {
29
28
  "node": ">=22.5.0"
@@ -1,242 +0,0 @@
1
- import { spawn as defaultSpawn } from 'node:child_process';
2
- import { parseEventLine } from '../events/schema.js';
3
- import { mergeProjectEnv } from '../runtime/env.js';
4
- function pushOption(argv, name, value) {
5
- if (value !== undefined && value !== '') {
6
- argv.push(name, String(value));
7
- }
8
- }
9
- function runArgs(options) {
10
- const argv = ['run', '--project-root', options.projectRoot, '--output', options.output ?? 'jsonl'];
11
- if (options.resumeLatest)
12
- argv.push('--resume-latest');
13
- if (options.skipDeploy)
14
- argv.push('--skip-deploy');
15
- if (options.skipVerify)
16
- argv.push('--skip-verify');
17
- pushOption(argv, '--event-log', options.eventLog);
18
- pushOption(argv, '--human-log', options.humanLog);
19
- return argv;
20
- }
21
- function retryArgs(options) {
22
- const argv = [
23
- 'retry-stage',
24
- '--project-root',
25
- options.projectRoot,
26
- '--artifact-dir',
27
- options.artifactDir,
28
- '--stage',
29
- options.stage,
30
- '--output',
31
- options.output ?? 'jsonl'
32
- ];
33
- pushOption(argv, '--event-log', options.eventLog);
34
- pushOption(argv, '--human-log', options.humanLog);
35
- return argv;
36
- }
37
- function resumeArgs(options) {
38
- const argv = [
39
- 'resume',
40
- options.mode,
41
- '--project-root',
42
- options.projectRoot,
43
- '--session',
44
- options.session,
45
- '--output',
46
- options.output ?? 'jsonl'
47
- ];
48
- pushOption(argv, '--event-log', options.eventLog);
49
- pushOption(argv, '--human-log', options.humanLog);
50
- return argv;
51
- }
52
- async function* splitLines(stream) {
53
- const queue = [];
54
- let ended = false;
55
- let pendingResolve;
56
- let buffer = '';
57
- stream.on('data', (chunk) => {
58
- buffer += String(chunk);
59
- const parts = buffer.split(/\r?\n/);
60
- buffer = parts.pop() ?? '';
61
- queue.push(...parts.filter((line) => line.trim()));
62
- pendingResolve?.();
63
- pendingResolve = undefined;
64
- });
65
- stream.on('end', () => {
66
- if (buffer.trim())
67
- queue.push(buffer);
68
- ended = true;
69
- pendingResolve?.();
70
- pendingResolve = undefined;
71
- });
72
- while (!ended || queue.length) {
73
- if (!queue.length) {
74
- await new Promise((resolve) => {
75
- pendingResolve = resolve;
76
- });
77
- continue;
78
- }
79
- yield queue.shift();
80
- }
81
- }
82
- function waitForClose(child) {
83
- return new Promise((resolve, reject) => {
84
- child.once('error', reject);
85
- child.once('close', (code, signal) => {
86
- resolve({ code, signal });
87
- });
88
- });
89
- }
90
- function errorSummary(stderr) {
91
- const summary = stderr.trim().split(/\r?\n/).filter(Boolean).slice(-3).join('\n');
92
- return summary ? `: ${summary}` : '';
93
- }
94
- function parseJsonPayload(stdout) {
95
- const line = stdout.split(/\r?\n/).find((candidate) => candidate.trim());
96
- if (!line) {
97
- throw new Error('Python backend returned empty JSON payload');
98
- }
99
- const payload = JSON.parse(line);
100
- if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
101
- throw new Error('Python backend JSON payload must be an object');
102
- }
103
- return payload;
104
- }
105
- function projectRootFromArgv(argv) {
106
- const index = argv.indexOf('--project-root');
107
- if (index >= 0 && argv[index + 1]) {
108
- return argv[index + 1];
109
- }
110
- const inline = argv.find((item) => item.startsWith('--project-root='));
111
- if (inline) {
112
- return inline.slice('--project-root='.length) || undefined;
113
- }
114
- return undefined;
115
- }
116
- export class PythonBackend {
117
- kind = 'python';
118
- env;
119
- cwd;
120
- runForwarder;
121
- resolvePythonCli;
122
- spawn;
123
- constructor(options = {}) {
124
- this.env = options.env ?? process.env;
125
- this.cwd = options.cwd ?? process.cwd();
126
- this.runForwarder = options.runForwarder;
127
- this.resolvePythonCli = options.resolvePythonCli;
128
- this.spawn = options.spawn ?? defaultSpawn;
129
- }
130
- async executeCli(argv) {
131
- const env = this.envForArgv(argv);
132
- const forwarderOptions = { env, cwd: this.cwd };
133
- if (this.runForwarder) {
134
- return this.runForwarder(argv, forwarderOptions);
135
- }
136
- // @ts-expect-error Phase 1 runner remains plain ESM JavaScript.
137
- const runner = await import('../../lib/runner.mjs');
138
- return Number(await runner.runForwarder(argv, forwarderOptions));
139
- }
140
- run(options) {
141
- return this.streamEvents(runArgs(options));
142
- }
143
- retryStage(options) {
144
- return this.streamEvents(retryArgs(options));
145
- }
146
- resume(options) {
147
- return this.streamEvents(resumeArgs(options));
148
- }
149
- async startDetached(options) {
150
- const argv = runArgs(options);
151
- argv.push('--detach');
152
- argv.push('--json');
153
- return this.captureJson(argv);
154
- }
155
- async stopJob(jobId, options = {}) {
156
- const argv = jobId === 'latest'
157
- ? ['stop']
158
- : ['jobs', 'stop', jobId];
159
- pushOption(argv, '--project-root', options.projectRoot);
160
- pushOption(argv, '--timeout', options.timeout);
161
- return this.captureJson(argv);
162
- }
163
- async readJob(jobId, options = {}) {
164
- const argv = jobId === 'latest'
165
- ? ['status', '--json']
166
- : ['jobs', 'status', jobId, '--json'];
167
- pushOption(argv, '--project-root', options.projectRoot);
168
- return this.captureJson(argv);
169
- }
170
- async *readLogs(options) {
171
- const argv = options.jobId
172
- ? ['jobs', 'logs', options.jobId, '--project-root', options.projectRoot]
173
- : ['logs', '--project-root', options.projectRoot];
174
- if (options.format)
175
- argv.push('--format', options.format);
176
- pushOption(argv, '--tail', options.tail);
177
- if (options.follow)
178
- argv.push('--follow');
179
- yield* this.streamLines(argv);
180
- }
181
- async *streamEvents(argv) {
182
- for await (const line of this.streamLines(argv)) {
183
- yield parseEventLine(line);
184
- }
185
- }
186
- async *streamLines(argv) {
187
- const env = this.envForArgv(argv);
188
- const resolved = this.resolvePythonCli ? this.resolvePythonCli() : await this.defaultResolvePythonCli(env);
189
- const child = this.spawn(resolved.command, [...resolved.args, ...argv], {
190
- cwd: this.cwd,
191
- env,
192
- stdio: ['ignore', 'pipe', 'pipe']
193
- });
194
- let stderr = '';
195
- child.stderr?.on('data', (chunk) => {
196
- stderr += String(chunk);
197
- });
198
- const closePromise = waitForClose(child);
199
- const stdout = child.stdout;
200
- if (!stdout) {
201
- throw new Error('Python backend did not expose stdout for event streaming');
202
- }
203
- for await (const line of splitLines(stdout)) {
204
- yield line;
205
- }
206
- const { code, signal } = await closePromise;
207
- if (code !== 0) {
208
- throw new Error(`Python backend exited with ${signal ? `signal ${signal}` : `code ${code}`}${errorSummary(stderr)}`);
209
- }
210
- }
211
- async captureJson(argv) {
212
- const env = this.envForArgv(argv);
213
- const resolved = this.resolvePythonCli ? this.resolvePythonCli() : await this.defaultResolvePythonCli(env);
214
- const child = this.spawn(resolved.command, [...resolved.args, ...argv], {
215
- cwd: this.cwd,
216
- env,
217
- stdio: ['ignore', 'pipe', 'pipe']
218
- });
219
- let stdout = '';
220
- let stderr = '';
221
- child.stdout?.on('data', (chunk) => {
222
- stdout += String(chunk);
223
- });
224
- child.stderr?.on('data', (chunk) => {
225
- stderr += String(chunk);
226
- });
227
- const { code, signal } = await waitForClose(child);
228
- if (code !== 0) {
229
- throw new Error(`Python backend exited with ${signal ? `signal ${signal}` : `code ${code}`}${errorSummary(stderr)}`);
230
- }
231
- return parseJsonPayload(stdout);
232
- }
233
- envForArgv(argv) {
234
- const projectRoot = projectRootFromArgv(argv);
235
- return projectRoot ? mergeProjectEnv(projectRoot, this.env) : this.env;
236
- }
237
- async defaultResolvePythonCli(env = this.env) {
238
- // @ts-expect-error Phase 1 runner remains plain ESM JavaScript.
239
- const runner = await import('../../lib/runner.mjs');
240
- return runner.resolveOrInstallPythonCli({ env });
241
- }
242
- }
package/lib/cache.mjs DELETED
@@ -1,14 +0,0 @@
1
- import os from 'node:os';
2
- import path from 'node:path';
3
-
4
- export function getCacheDir(env = process.env) {
5
- const override = String(env.AUTOVPN_CACHE_DIR ?? '').trim();
6
- if (override) {
7
- return override;
8
- }
9
- return path.join(os.homedir(), '.cache', 'autovpn', 'npm-wrapper');
10
- }
11
-
12
- export function pythonBinName(platform = process.platform) {
13
- return platform === 'win32' ? 'Scripts' : 'bin';
14
- }
package/lib/errors.mjs DELETED
@@ -1,11 +0,0 @@
1
- export class WrapperError extends Error {
2
- constructor(message, options = {}) {
3
- super(message);
4
- this.name = 'WrapperError';
5
- this.cause = options.cause;
6
- }
7
- }
8
-
9
- export function isEnabled(value) {
10
- return ['1', 'true', 'yes', 'on'].includes(String(value ?? '').trim().toLowerCase());
11
- }
@@ -1,63 +0,0 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import { spawnSync as defaultSpawnSync } from 'node:child_process';
4
-
5
- import { getCacheDir, pythonBinName } from './cache.mjs';
6
- import { isEnabled, WrapperError } from './errors.mjs';
7
-
8
- export function buildPipInstallArgs({ env = process.env, packageVersion }) {
9
- const packageSpec = String(env.AUTOVPN_WHEEL_URL ?? '').trim()
10
- || `${String(env.AUTOVPN_PYTHON_PACKAGE ?? 'vpn-subscription-automation').trim()}==${packageVersion}`;
11
- const args = ['-m', 'pip', 'install'];
12
- if (env.AUTOVPN_PIP_INDEX_URL) {
13
- args.push('--index-url', String(env.AUTOVPN_PIP_INDEX_URL));
14
- }
15
- if (env.AUTOVPN_PIP_EXTRA_INDEX_URL) {
16
- args.push('--extra-index-url', String(env.AUTOVPN_PIP_EXTRA_INDEX_URL));
17
- }
18
- args.push(packageSpec);
19
- return args;
20
- }
21
-
22
- export function installPythonCli({
23
- env = process.env,
24
- packageVersion,
25
- spawnSync = defaultSpawnSync,
26
- cacheDir = getCacheDir(env),
27
- platform = process.platform
28
- } = {}) {
29
- if (isEnabled(env.AUTOVPN_NO_INSTALL)) {
30
- throw new WrapperError('No compatible Python autovpn CLI found and AUTOVPN_NO_INSTALL is enabled.');
31
- }
32
- if (!packageVersion) {
33
- throw new WrapperError('Cannot install Python AutoVPN backend without an npm package version.');
34
- }
35
-
36
- const venvDir = path.join(cacheDir, `python-${packageVersion}`);
37
- const executable = platform === 'win32' ? 'autovpn.exe' : 'autovpn';
38
- const cliPath = path.join(venvDir, pythonBinName(platform), executable);
39
- const forceInstall = isEnabled(env.AUTOVPN_FORCE_INSTALL);
40
-
41
- if (!forceInstall && fs.existsSync(cliPath)) {
42
- return { command: cliPath, args: [], source: 'managed-venv' };
43
- }
44
-
45
- fs.mkdirSync(cacheDir, { recursive: true });
46
- const pythonCommand = String(env.PYTHON ?? env.PYTHON3 ?? 'python3');
47
- const venvResult = spawnSync(pythonCommand, ['-m', 'venv', venvDir], { stdio: 'inherit', env });
48
- if (venvResult.status !== 0) {
49
- throw new WrapperError(`Failed to create AutoVPN Python backend venv with ${pythonCommand}.`);
50
- }
51
-
52
- const pythonPath = path.join(venvDir, pythonBinName(platform), platform === 'win32' ? 'python.exe' : 'python');
53
- const installResult = spawnSync(pythonPath, buildPipInstallArgs({ env, packageVersion }), { stdio: 'inherit', env });
54
- if (installResult.status !== 0) {
55
- throw new WrapperError('Failed to install AutoVPN Python backend into the wrapper-managed venv.');
56
- }
57
-
58
- if (!fs.existsSync(cliPath)) {
59
- throw new WrapperError(`Installed Python backend did not provide autovpn at ${cliPath}.`);
60
- }
61
-
62
- return { command: cliPath, args: [], source: 'managed-venv' };
63
- }
package/lib/runner.mjs DELETED
@@ -1,113 +0,0 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
- import {
5
- spawn as defaultSpawn,
6
- spawnSync as defaultSpawnSync
7
- } from 'node:child_process';
8
-
9
- import { isEnabled, WrapperError } from './errors.mjs';
10
- import { installPythonCli } from './install-python-cli.mjs';
11
-
12
- const packageJsonPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'package.json');
13
-
14
- export function readPackageVersion() {
15
- const payload = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
16
- return String(payload.version);
17
- }
18
-
19
- function normalizeVersionOutput(stdout) {
20
- return String(stdout ?? '').trim();
21
- }
22
-
23
- export function versionMatches(stdout, packageVersion) {
24
- return normalizeVersionOutput(stdout) === `autovpn ${packageVersion}`;
25
- }
26
-
27
- export function resolvePythonCli({
28
- env = process.env,
29
- packageVersion = readPackageVersion(),
30
- spawnSync = defaultSpawnSync
31
- } = {}) {
32
- if (isEnabled(env.AUTOVPN_NO_PYTHON)) {
33
- throw new WrapperError('Python backend is disabled by AUTOVPN_NO_PYTHON.');
34
- }
35
-
36
- const explicit = String(env.AUTOVPN_PYTHON_CLI ?? '').trim();
37
- if (explicit) {
38
- return { command: explicit, args: [], source: 'AUTOVPN_PYTHON_CLI' };
39
- }
40
-
41
- const probe = spawnSync('autovpn', ['--version'], {
42
- encoding: 'utf-8',
43
- env: { ...env, AUTOVPN_WRAPPER_PROBE: '1' },
44
- stdio: ['ignore', 'pipe', 'pipe']
45
- });
46
- if (probe.status === 0) {
47
- if (versionMatches(probe.stdout, packageVersion) || isEnabled(env.AUTOVPN_ALLOW_VERSION_MISMATCH)) {
48
- return { command: 'autovpn', args: [], source: 'PATH' };
49
- }
50
- }
51
-
52
- throw new WrapperError(
53
- `No compatible Python autovpn CLI found for version ${packageVersion}. Set AUTOVPN_PYTHON_CLI or allow the wrapper to install the backend.`
54
- );
55
- }
56
-
57
- export function resolveOrInstallPythonCli({
58
- env = process.env,
59
- packageVersion = readPackageVersion(),
60
- spawnSync = defaultSpawnSync,
61
- installer = installPythonCli
62
- } = {}) {
63
- try {
64
- return resolvePythonCli({ env, packageVersion, spawnSync });
65
- } catch (error) {
66
- if (isEnabled(env.AUTOVPN_NO_PYTHON)) {
67
- throw error;
68
- }
69
- return installer({ env, packageVersion, spawnSync });
70
- }
71
- }
72
-
73
- export function runForwarder(argv = process.argv.slice(2), {
74
- env = process.env,
75
- cwd = process.cwd(),
76
- packageVersion = readPackageVersion(),
77
- spawn = defaultSpawn,
78
- spawnSync = defaultSpawnSync,
79
- installer = installPythonCli
80
- } = {}) {
81
- const resolved = resolveOrInstallPythonCli({ env, packageVersion, spawnSync, installer });
82
- const child = spawn(resolved.command, [...resolved.args, ...argv], {
83
- cwd,
84
- env,
85
- stdio: 'inherit'
86
- });
87
-
88
- return new Promise((resolve, reject) => {
89
- child.on('error', reject);
90
- child.on('exit', (code, signal) => {
91
- if (signal) {
92
- resolve(1);
93
- return;
94
- }
95
- resolve(typeof code === 'number' ? code : 1);
96
- });
97
- });
98
- }
99
-
100
- export async function main(argv = process.argv.slice(2), options = {}) {
101
- const env = options.env ?? process.env;
102
- if (isEnabled(env.AUTOVPN_WRAPPER_PROBE) && argv.length === 1 && argv[0] === '--version') {
103
- return 42;
104
- }
105
-
106
- try {
107
- return await runForwarder(argv, { ...options, env });
108
- } catch (error) {
109
- const message = error instanceof Error ? error.message : String(error);
110
- console.error(`autovpn npm wrapper error: ${message}`);
111
- return 1;
112
- }
113
- }