@swimmingliu/autovpn 1.6.2 → 1.6.3

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.
@@ -41,6 +41,21 @@ function commandPath(name, env, platform = process.platform) {
41
41
  }
42
42
  return '';
43
43
  }
44
+ function firstExistingPath(candidates) {
45
+ return candidates.find((candidate) => candidate && fs.existsSync(candidate)) ?? '';
46
+ }
47
+ function mihomoInstallCandidates(env) {
48
+ const homeDir = String(env.HOME ?? env.USERPROFILE ?? '').trim();
49
+ return [
50
+ homeDir ? path.join(homeDir, 'clashctl', 'bin', 'mihomo') : '',
51
+ '/opt/homebrew/bin/mihomo',
52
+ '/usr/local/bin/mihomo',
53
+ '/usr/bin/mihomo'
54
+ ];
55
+ }
56
+ function mihomoPath(env) {
57
+ return commandPath('mihomo', env) || firstExistingPath(mihomoInstallCandidates(env));
58
+ }
44
59
  function safeRun(command, env) {
45
60
  try {
46
61
  const { executable, args } = normalizeManagedToolCommandForSpawn(command);
@@ -98,7 +113,7 @@ function loadProfile(profilePath) {
98
113
  }
99
114
  function checkProxyRuntime(env) {
100
115
  const checks = [];
101
- const mihomo = commandPath('mihomo', env);
116
+ const mihomo = mihomoPath(env);
102
117
  if (!mihomo) {
103
118
  checks.push(check('mihomo', 'fail', 'mihomo binary is missing'));
104
119
  }
@@ -423,6 +423,30 @@ export async function runNodePipeline(options, context = {}) {
423
423
  const limitAvailability = createLimiter(Number(speedConfig.concurrency ?? 1));
424
424
  let speedCompleted = 0;
425
425
  let availabilityCompleted = 0;
426
+ let speedArtifactFlush = Promise.resolve();
427
+ let availabilityArtifactFlush = Promise.resolve();
428
+ const flushStreamingSpeedArtifacts = async () => {
429
+ const resultSnapshot = [...speedResults];
430
+ const linkSnapshot = [...passedSpeedLinks];
431
+ speedArtifactFlush = speedArtifactFlush.catch(() => undefined).then(async () => {
432
+ summary.counts.speedtest_links = linkSnapshot.length;
433
+ await writeLines(artifactDir, 'vpn_node_speedtest.txt', linkSnapshot);
434
+ await writeJson(artifactDir, 'vpn_node_speedtest_report.json', resultSnapshot);
435
+ await writeReport();
436
+ });
437
+ await speedArtifactFlush;
438
+ };
439
+ const flushStreamingAvailabilityArtifacts = async () => {
440
+ const resultSnapshot = [...availabilityResults];
441
+ const linkSnapshot = resultSnapshot.filter((result) => result.all_passed).map((result) => result.link);
442
+ availabilityArtifactFlush = availabilityArtifactFlush.catch(() => undefined).then(async () => {
443
+ summary.counts.availability_links = linkSnapshot.length;
444
+ await writeLines(artifactDir, 'vpn_node_availability.txt', linkSnapshot);
445
+ await writeJson(artifactDir, 'vpn_node_availability_report.json', resultSnapshot);
446
+ await writeReport();
447
+ });
448
+ await availabilityArtifactFlush;
449
+ };
426
450
  const runStreamingAvailability = (speedResult) => {
427
451
  const task = limitAvailability(async () => {
428
452
  const results = context.stages?.availability
@@ -436,15 +460,14 @@ export async function runNodePipeline(options, context = {}) {
436
460
  for (const result of results) {
437
461
  availabilityResults.push(result);
438
462
  availabilityCompleted += 1;
439
- if (context.stages?.availability) {
440
- emit('availability_link_result', {
441
- completed: availabilityCompleted,
442
- total: passedSpeedLinks.length,
443
- link: result.link,
444
- all_passed: result.all_passed,
445
- provider_results: result.provider_results
446
- });
447
- }
463
+ await flushStreamingAvailabilityArtifacts();
464
+ emit('availability_link_result', {
465
+ completed: availabilityCompleted,
466
+ total: passedSpeedLinks.length,
467
+ link: result.link,
468
+ all_passed: result.all_passed,
469
+ provider_results: result.provider_results
470
+ });
448
471
  }
449
472
  });
450
473
  availabilityTasks.push(task);
@@ -476,8 +499,12 @@ export async function runNodePipeline(options, context = {}) {
476
499
  });
477
500
  if (passed) {
478
501
  passedSpeedLinks.push(result.link);
502
+ await flushStreamingSpeedArtifacts();
479
503
  runStreamingAvailability(result);
480
504
  }
505
+ else if (useStreamingStages) {
506
+ await flushStreamingSpeedArtifacts();
507
+ }
481
508
  });
482
509
  speedTasks.push(task);
483
510
  task.catch(() => undefined);
@@ -178,10 +178,36 @@ async function closeChildProcess(process) {
178
178
  process.kill('SIGTERM');
179
179
  });
180
180
  }
181
- async function resolveMihomoCommand(runtimePath) {
181
+ async function fileExists(candidate) {
182
+ try {
183
+ const info = await stat(candidate);
184
+ return info.isFile();
185
+ }
186
+ catch {
187
+ return false;
188
+ }
189
+ }
190
+ async function firstExistingFile(candidates) {
191
+ for (const candidate of candidates) {
192
+ if (candidate && await fileExists(candidate)) {
193
+ return candidate;
194
+ }
195
+ }
196
+ return '';
197
+ }
198
+ function mihomoInstallCandidates(env = process.env) {
199
+ const homeDir = String(env.HOME ?? env.USERPROFILE ?? '').trim();
200
+ return [
201
+ homeDir ? path.join(homeDir, 'clashctl', 'bin', 'mihomo') : '',
202
+ '/opt/homebrew/bin/mihomo',
203
+ '/usr/local/bin/mihomo',
204
+ '/usr/bin/mihomo'
205
+ ];
206
+ }
207
+ async function resolveMihomoCommand(runtimePath, env = process.env) {
182
208
  const candidate = String(runtimePath ?? '').trim();
183
209
  if (!candidate) {
184
- return 'mihomo';
210
+ return await firstExistingFile(mihomoInstallCandidates(env)) || 'mihomo';
185
211
  }
186
212
  try {
187
213
  const info = await stat(candidate);
@@ -189,12 +215,12 @@ async function resolveMihomoCommand(runtimePath) {
189
215
  return candidate;
190
216
  }
191
217
  if (info.isDirectory() && path.basename(candidate) === 'runtime') {
192
- return 'mihomo';
218
+ return await firstExistingFile(mihomoInstallCandidates(env)) || 'mihomo';
193
219
  }
194
220
  }
195
221
  catch {
196
222
  if (path.basename(candidate) === 'runtime') {
197
- return 'mihomo';
223
+ return await firstExistingFile(mihomoInstallCandidates(env)) || 'mihomo';
198
224
  }
199
225
  }
200
226
  return candidate;
@@ -214,7 +240,7 @@ export async function openMihomoRuntime(link, options = {}) {
214
240
  const tempDir = await mkdtemp(path.join(os.tmpdir(), 'autovpn-mihomo-'));
215
241
  const configPath = path.join(tempDir, 'config.json');
216
242
  await writeFile(configPath, JSON.stringify(config), 'utf8');
217
- const command = await resolveMihomoCommand(options.runtimePath);
243
+ const command = await resolveMihomoCommand(options.runtimePath, options.env ?? process.env);
218
244
  const child = (options.spawn ?? defaultSpawn)(command, ['-f', configPath], {
219
245
  stdio: ['ignore', 'pipe', 'pipe'],
220
246
  env: stripProxyEnv(options.env ?? process.env)
@@ -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.2',
8
+ sidebarVersion: 'v.1.6.3',
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.2",
3
+ "version": "1.6.3",
4
4
  "description": "npm wrapper for the AutoVPN headless CLI",
5
5
  "type": "module",
6
6
  "repository": {