mihomo-cli 2.7.0 → 2.7.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.7.2] - 2026-05-04
4
+
5
+ ### 修复
6
+
7
+ - **进度条轮次标题** - 多轮测试时第 1 轮标题正确显示在进度条之前,单轮不显示轮次标题
8
+
9
+ ### 改进
10
+
11
+ - **自定义轮数** - clean 命令支持 `-r N` / `--rounds N` 指定测试轮数(默认 3)
12
+
13
+ ---
14
+
15
+ ## [2.7.1] - 2026-05-04
16
+
17
+ ### 修复
18
+
19
+ - **进度条计数器修复** - 清理/测试多轮重试时,✓/✗ 计数不再跨轮累加,每轮独立计数
20
+ - **轮次标题位置修复** - 移除错位的"第 1 轮测试"标题,重试轮标题正确显示在对应进度条之前
21
+
22
+ ---
23
+
3
24
  ## [2.7.0] - 2026-05-03
4
25
 
5
26
  ### 移除
package/README.md CHANGED
@@ -102,9 +102,9 @@ mihomo ui yacd # YACD
102
102
  | `mihomo sub remove <name>` | 删除订阅(支持模糊匹配) |
103
103
  | `mihomo sub web [name]` | 打开订阅页面(无参打开默认) |
104
104
  | `mihomo sub test [name]` | 测试节点连通性(`-t` 超时,`-j` 并发) |
105
- | `mihomo sub clean [name]` | 测速并清理失败节点 |
105
+ | `mihomo sub clean [name]` | 测速并清理失败节点(`-r` 轮数,默认3)|
106
106
  | `mihomo test` | 快速测试当前节点连通性(`-t` 超时,`-j` 并发) |
107
- | `mihomo clean` | 清理失败节点并自动重启(`-t` 超时,`-j` 并发) |
107
+ | `mihomo clean` | 清理失败节点并自动重启(`-t` 超时,`-j` 并发,`-r` 轮数) |
108
108
 
109
109
  ### 覆写配置
110
110
 
package/dist/index.js CHANGED
@@ -4031,7 +4031,7 @@ ${colors.cyan("\u8BA2\u9605:")}
4031
4031
  ${colors.bold("subscription")} test [name] \u6D4B\u8BD5\u8282\u70B9\u8FDE\u901A\u6027
4032
4032
  ${colors.bold("subscription")} clean [name] \u6D4B\u901F\u5E76\u6E05\u7406\u5931\u8D25\u8282\u70B9
4033
4033
  ${colors.bold("test")} [-t ms] [-j N] \u5FEB\u901F\u6D4B\u8BD5\u5F53\u524D\u8282\u70B9\u8FDE\u901A\u6027
4034
- ${colors.bold("clean")} [-t ms] [-j N] \u6E05\u7406\u5931\u8D25\u8282\u70B9\u5E76\u81EA\u52A8\u91CD\u542F
4034
+ ${colors.bold("clean")} [-t ms] [-j N] [-r N] \u6E05\u7406\u5931\u8D25\u8282\u70B9\u5E76\u81EA\u52A8\u91CD\u542F
4035
4035
 
4036
4036
  ${colors.cyan("\u914D\u7F6E:")}
4037
4037
  ${colors.bold("overwrite")} \u67E5\u770B\u8986\u5199\u72B6\u6001\uFF08\u522B\u540D ow\uFF09
@@ -4812,7 +4812,7 @@ function cleanDeadProxies(parsed, deadNames) {
4812
4812
  }
4813
4813
  async function autoCleanSubscription(subName, options = {}) {
4814
4814
  const parsed = loadSubscriptionConfig(subName);
4815
- const { onResult, onRetryRound, ...testOptions } = options;
4815
+ const { onResult, onRetryRound, rounds = 3, ...testOptions } = options;
4816
4816
  const wrapOnResult = (round) => onResult ? (r, i, t) => onResult(r, i, t, round) : void 0;
4817
4817
  const summary = await testSubscriptionProxies(subName, {
4818
4818
  ...testOptions,
@@ -4829,7 +4829,7 @@ async function autoCleanSubscription(subName, options = {}) {
4829
4829
  } else {
4830
4830
  const deadNames = new Set(summary.results.filter((r) => r.delay === null).map((r) => r.name));
4831
4831
  const deadProxies = parsed.proxies.filter((p) => deadNames.has(p.name));
4832
- for (let retry = 0; retry < 2; retry++) {
4832
+ for (let retry = 0; retry < rounds - 1; retry++) {
4833
4833
  const round = retry + 2;
4834
4834
  const retryTargets = deadProxies.filter((p) => deadNames.has(p.name));
4835
4835
  if (retryTargets.length === 0) break;
@@ -5042,10 +5042,10 @@ async function withTestInstance(subName, fn) {
5042
5042
  // src/commands/subscription.ts
5043
5043
  var IS_TTY = process.stdout.isTTY === true;
5044
5044
  var BAR_WIDTH = 20;
5045
- function createProgressPrinter() {
5045
+ function createProgressPrinter(totalRounds = 1) {
5046
5046
  let alive = 0;
5047
5047
  let dead = 0;
5048
- let hasRetry = false;
5048
+ let started = false;
5049
5049
  const resultMap = /* @__PURE__ */ new Map();
5050
5050
  function render(done, total) {
5051
5051
  if (!IS_TTY) return;
@@ -5056,31 +5056,24 @@ function createProgressPrinter() {
5056
5056
  }
5057
5057
  return {
5058
5058
  onResult(result, index, total, round = 1) {
5059
- const prev = resultMap.get(result.name);
5060
- if (prev) {
5061
- if (prev.result.delay === null && result.delay !== null) {
5062
- alive++;
5063
- dead--;
5059
+ if (!started) {
5060
+ started = true;
5061
+ if (totalRounds > 1) {
5062
+ console.log(`--- \u7B2C 1 \u8F6E\u6D4B\u8BD5 (${total} \u4E2A\u8282\u70B9) ---`);
5064
5063
  }
5065
- } else {
5066
- if (result.delay !== null) alive++;
5067
- else dead++;
5068
5064
  }
5065
+ if (result.delay !== null) alive++;
5066
+ else dead++;
5069
5067
  resultMap.set(result.name, { result, round });
5070
5068
  render(index + 1, total);
5071
5069
  },
5072
5070
  onRetryRound(round, count) {
5073
- if (!hasRetry) {
5074
- hasRetry = true;
5075
- if (IS_TTY) {
5076
- process.stdout.write("\n");
5077
- }
5078
- console.log(`--- \u7B2C 1 \u8F6E\u6D4B\u8BD5 (${resultMap.size} \u4E2A\u8282\u70B9) ---`);
5079
- }
5080
5071
  if (IS_TTY) {
5081
5072
  process.stdout.write("\n");
5082
5073
  }
5083
5074
  console.log(`--- \u7B2C ${round} \u8F6E\u91CD\u8BD5 (${count} \u4E2A\u8282\u70B9) ---`);
5075
+ alive = 0;
5076
+ dead = 0;
5084
5077
  },
5085
5078
  finish() {
5086
5079
  if (IS_TTY) {
@@ -5396,14 +5389,16 @@ async function cmdSubscription(args) {
5396
5389
  }
5397
5390
  if (action === "clean") {
5398
5391
  const { target, timeout, concurrency } = resolveTestTarget(args);
5392
+ const rounds = parseIntArg(args, "-r", "--rounds", 3);
5399
5393
  console.log(`\u6E05\u7406\u8BA2\u9605 "${target.name}"...`);
5400
5394
  console.log(`\u8D85\u65F6: ${timeout}ms \u5E76\u53D1: ${concurrency}`);
5401
5395
  console.log("");
5402
- const progress = createProgressPrinter();
5396
+ const progress = createProgressPrinter(rounds);
5403
5397
  const result = await withTestInstance(target.name, async (apiBase) => {
5404
5398
  return autoCleanSubscription(target.name, {
5405
5399
  timeout,
5406
5400
  concurrency,
5401
+ rounds,
5407
5402
  apiBase,
5408
5403
  onResult: progress.onResult,
5409
5404
  onRetryRound: progress.onRetryRound
@@ -5505,7 +5500,7 @@ async function cmdStart(args) {
5505
5500
  console.log(`\u8282\u70B9\u6570 ${configInfo.proxies} \u8D85\u8FC7 ${AUTO_CLEAN_THRESHOLD}\uFF0C\u81EA\u52A8\u6E05\u7406...`);
5506
5501
  console.log("");
5507
5502
  await sleep(1e3);
5508
- const progress = createProgressPrinter();
5503
+ const progress = createProgressPrinter(3);
5509
5504
  const cleanResult = await autoCleanSubscription(sub.name, {
5510
5505
  onResult: progress.onResult,
5511
5506
  onRetryRound: progress.onRetryRound
@@ -5817,13 +5812,15 @@ async function cmdClean(args) {
5817
5812
  const activeSub = requireActiveSub();
5818
5813
  const timeout = parseIntArg(args, "-t", "--timeout", 1500);
5819
5814
  const concurrency = parseIntArg(args, "-j", "--concurrency", 100);
5815
+ const rounds = parseIntArg(args, "-r", "--rounds", 3);
5820
5816
  console.log(`\u6E05\u7406 "${activeSub.name}" \u5931\u8D25\u8282\u70B9...`);
5821
5817
  console.log(`\u8D85\u65F6: ${timeout}ms \u5E76\u53D1: ${concurrency}`);
5822
5818
  console.log("");
5823
- const progress = createProgressPrinter();
5819
+ const progress = createProgressPrinter(rounds);
5824
5820
  const result = await autoCleanSubscription(activeSub.name, {
5825
5821
  timeout,
5826
5822
  concurrency,
5823
+ rounds,
5827
5824
  onResult: progress.onResult,
5828
5825
  onRetryRound: progress.onRetryRound
5829
5826
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mihomo-cli",
3
- "version": "2.7.0",
3
+ "version": "2.7.2",
4
4
  "type": "module",
5
5
  "description": "A terminal-based mihomo (Clash.Meta) client for macOS",
6
6
  "bin": {