mihomo-cli 2.7.2 → 2.8.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/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.8.0] - 2026-05-05
4
+
5
+ ### 新功能
6
+
7
+ - **start/up 支持测速参数** - `-r N` 清理轮次、`-t ms` 超时、`-j N` 并发数
8
+ - **GitHub 订阅差异化策略** - 自动清理阈值 GitHub 50 / 其他 100;默认更新间隔 GitHub 6h / 其他 12h
9
+
10
+ ### 改进
11
+
12
+ - **默认测速轮次** - 从 3 轮调整为 2 轮
13
+ - **统一超时默认值** - 所有测速命令默认超时统一为 2000ms
14
+
15
+ ---
16
+
17
+ ## [2.7.3] - 2026-05-04
18
+
19
+ ### 改进
20
+
21
+ - **代码清理** - 提取 `DEFAULT_CLEAN_ROUNDS` 常量,消除重复魔数;简化进度条内部状态
22
+
23
+ ---
24
+
3
25
  ## [2.7.2] - 2026-05-04
4
26
 
5
27
  ### 修复
package/README.md CHANGED
@@ -83,7 +83,7 @@ mihomo ui yacd # YACD
83
83
 
84
84
  | 命令 | 说明 |
85
85
  | --------------------------- | ---------------------------------------------------------------------------- |
86
- | `mihomo start [tun\|mixed]` | 启动/重启/切换代理模式 |
86
+ | `mihomo start [tun\|mixed]` | 启动/重启/切换代理模式(`-r` 清理轮次,`-t` 超时,`-j` 并发) |
87
87
  | `mihomo stop` | 停止代理 |
88
88
  | `mihomo status` | 查看运行状态 |
89
89
  | `mihomo log` | 实时查看日志 (`-o` 用系统编辑器打开) |
@@ -102,7 +102,7 @@ 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]` | 测速并清理失败节点(`-r` 轮数,默认3)|
105
+ | `mihomo sub clean [name]` | 测速并清理失败节点(`-r` 轮数,默认2)|
106
106
  | `mihomo test` | 快速测试当前节点连通性(`-t` 超时,`-j` 并发) |
107
107
  | `mihomo clean` | 清理失败节点并自动重启(`-t` 超时,`-j` 并发,`-r` 轮数) |
108
108
 
@@ -142,7 +142,7 @@ mihomo ui yacd # YACD
142
142
 
143
143
  | 快捷命令 | 等效于 |
144
144
  | ---------------------- | -------------------------- |
145
- | `mihomo up` | `mihomo start` |
145
+ | `mihomo up` | `mihomo start`(同样支持 `-r`/`-t`/`-j`)|
146
146
  | `mihomo down` | `mihomo stop` |
147
147
  | `mihomo tun` | `mihomo start tun` |
148
148
  | `mihomo use <name>` | `mihomo sub use <name>` |
@@ -194,7 +194,7 @@ mihomo kernel --mirror-all hk.gh-proxy.org
194
194
 
195
195
  ## 订阅自动更新
196
196
 
197
- - 默认更新间隔:4 小时(或订阅服务端指定的 `profile-update-interval`)
197
+ - 默认更新间隔:GitHub 订阅 6 小时,其他订阅 12 小时(订阅服务端可通过 `profile-update-interval` 覆盖)
198
198
  - 触发时机:`start` 命令、`sub list` 命令
199
199
  - 更新失败时继续使用本地缓存,不影响使用
200
200
 
package/dist/index.js CHANGED
@@ -4012,7 +4012,7 @@ ${colors.cyan(colors.bold(`mihomo-cli v${VERSION}`))}
4012
4012
  mihomo <\u547D\u4EE4> [\u9009\u9879]
4013
4013
 
4014
4014
  ${colors.cyan("\u63A7\u5236:")}
4015
- ${colors.bold("start")} [tun|mixed] \u542F\u52A8/\u5207\u6362\u4EE3\u7406 (\u9ED8\u8BA4 mixed)
4015
+ ${colors.bold("start")} [tun|mixed] [-r N] [-t ms] [-j N] \u542F\u52A8/\u5207\u6362\u4EE3\u7406 (\u9ED8\u8BA4 mixed)
4016
4016
  ${colors.bold("stop")} \u505C\u6B62\u4EE3\u7406
4017
4017
  ${colors.bold("status")} \u67E5\u770B\u72B6\u6001
4018
4018
 
@@ -4432,7 +4432,17 @@ function cmdLogs(args) {
4432
4432
  import path6 from "path";
4433
4433
 
4434
4434
  // src/subscription.ts
4435
- var DEFAULT_UPDATE_INTERVAL_HOURS = 4;
4435
+ var DEFAULT_UPDATE_INTERVAL_HOURS = 12;
4436
+ var DEFAULT_UPDATE_INTERVAL_HOURS_GITHUB = 6;
4437
+ var DEFAULT_CLEAN_ROUNDS = 2;
4438
+ var AUTO_CLEAN_THRESHOLD = 100;
4439
+ var AUTO_CLEAN_THRESHOLD_GITHUB = 50;
4440
+ function isGithubUrl(url) {
4441
+ return /github\.com|raw\.githubusercontent\.com/i.test(url);
4442
+ }
4443
+ function getDefaultUpdateInterval(url) {
4444
+ return isGithubUrl(url) ? DEFAULT_UPDATE_INTERVAL_HOURS_GITHUB : DEFAULT_UPDATE_INTERVAL_HOURS;
4445
+ }
4436
4446
  var YAML_DUMP_OPTS = { indent: 2, lineWidth: -1, noCompatMode: true };
4437
4447
  var HTTP_CLIENT2 = createHttpClient({ timeout: 6e4 });
4438
4448
  function isMultiUrl(url) {
@@ -4667,7 +4677,7 @@ function needsAutoUpdate(sub) {
4667
4677
  if (!sub.updated_at) return true;
4668
4678
  const lastUpdate = new Date(sub.updated_at).getTime();
4669
4679
  if (Number.isNaN(lastUpdate)) return true;
4670
- const intervalHours = sub.update_interval || DEFAULT_UPDATE_INTERVAL_HOURS;
4680
+ const intervalHours = sub.update_interval || getDefaultUpdateInterval(sub.url);
4671
4681
  const intervalMs = intervalHours * 60 * 60 * 1e3;
4672
4682
  return Date.now() - lastUpdate > intervalMs;
4673
4683
  }
@@ -4692,7 +4702,7 @@ async function autoUpdateStaleSubscription() {
4692
4702
  }
4693
4703
  if (staleSubs.length === 1) {
4694
4704
  const sub = staleSubs[0];
4695
- const interval = sub.update_interval || DEFAULT_UPDATE_INTERVAL_HOURS;
4705
+ const interval = sub.update_interval || getDefaultUpdateInterval(sub.url);
4696
4706
  console.log(`\u8BA2\u9605 "${sub.name}" \u8D85\u8FC7 ${interval} \u5C0F\u65F6\u672A\u66F4\u65B0\uFF0C\u6B63\u5728\u66F4\u65B0...`);
4697
4707
  } else {
4698
4708
  console.log(`\u68C0\u67E5\u5230 ${staleSubs.length} \u4E2A\u8BA2\u9605\u9700\u8981\u66F4\u65B0\uFF0C\u6B63\u5728\u5E76\u884C\u66F4\u65B0...`);
@@ -4812,7 +4822,7 @@ function cleanDeadProxies(parsed, deadNames) {
4812
4822
  }
4813
4823
  async function autoCleanSubscription(subName, options = {}) {
4814
4824
  const parsed = loadSubscriptionConfig(subName);
4815
- const { onResult, onRetryRound, rounds = 3, ...testOptions } = options;
4825
+ const { onResult, onRetryRound, rounds = DEFAULT_CLEAN_ROUNDS, ...testOptions } = options;
4816
4826
  const wrapOnResult = (round) => onResult ? (r, i, t) => onResult(r, i, t, round) : void 0;
4817
4827
  const summary = await testSubscriptionProxies(subName, {
4818
4828
  ...testOptions,
@@ -5045,7 +5055,6 @@ var BAR_WIDTH = 20;
5045
5055
  function createProgressPrinter(totalRounds = 1) {
5046
5056
  let alive = 0;
5047
5057
  let dead = 0;
5048
- let started = false;
5049
5058
  const resultMap = /* @__PURE__ */ new Map();
5050
5059
  function render(done, total) {
5051
5060
  if (!IS_TTY) return;
@@ -5056,11 +5065,8 @@ function createProgressPrinter(totalRounds = 1) {
5056
5065
  }
5057
5066
  return {
5058
5067
  onResult(result, index, total, round = 1) {
5059
- if (!started) {
5060
- started = true;
5061
- if (totalRounds > 1) {
5062
- console.log(`--- \u7B2C 1 \u8F6E\u6D4B\u8BD5 (${total} \u4E2A\u8282\u70B9) ---`);
5063
- }
5068
+ if (resultMap.size === 0 && totalRounds > 1) {
5069
+ console.log(`--- \u7B2C 1 \u8F6E\u6D4B\u8BD5 (${total} \u4E2A\u8282\u70B9) ---`);
5064
5070
  }
5065
5071
  if (result.delay !== null) alive++;
5066
5072
  else dead++;
@@ -5155,7 +5161,7 @@ async function printSubscriptionList(options) {
5155
5161
  const time = formatDate(s.updated_at);
5156
5162
  const defaultMark = activeSub && s.name === activeSub.name ? colors.green(" [\u4F7F\u7528\u4E2D]") : "";
5157
5163
  const mergeBadge = isMultiUrl(s.url) ? colors.cyan(` [\u5408\u5E76 ${splitUrls(s.url).length} \u6E90]`) : "";
5158
- const interval = s.update_interval || DEFAULT_UPDATE_INTERVAL_HOURS;
5164
+ const interval = s.update_interval || getDefaultUpdateInterval(s.url);
5159
5165
  console.log(` ${i + 1}. ${s.name}${defaultMark}${mergeBadge}`);
5160
5166
  console.log(` ${colors.gray("\u66F4\u65B0: ")}${time} (\u95F4\u9694: ${interval}h)`);
5161
5167
  if (s.username) {
@@ -5389,7 +5395,7 @@ async function cmdSubscription(args) {
5389
5395
  }
5390
5396
  if (action === "clean") {
5391
5397
  const { target, timeout, concurrency } = resolveTestTarget(args);
5392
- const rounds = parseIntArg(args, "-r", "--rounds", 3);
5398
+ const rounds = parseIntArg(args, "-r", "--rounds", DEFAULT_CLEAN_ROUNDS);
5393
5399
  console.log(`\u6E05\u7406\u8BA2\u9605 "${target.name}"...`);
5394
5400
  console.log(`\u8D85\u65F6: ${timeout}ms \u5E76\u53D1: ${concurrency}`);
5395
5401
  console.log("");
@@ -5443,7 +5449,6 @@ async function cmdSubscription(args) {
5443
5449
  }
5444
5450
 
5445
5451
  // src/commands/start.ts
5446
- var AUTO_CLEAN_THRESHOLD = 50;
5447
5452
  function handleStopResult(result) {
5448
5453
  if (result.remaining && result.remaining.length > 0) {
5449
5454
  console.error(`${colors.red("\u90E8\u5206\u8FDB\u7A0B\u672A\u7EC8\u6B62:")} ${result.remaining.join(", ")}`);
@@ -5457,6 +5462,9 @@ async function cmdStart(args) {
5457
5462
  process.exit(1);
5458
5463
  }
5459
5464
  const targetMode = args[1] === "tun" ? "tun" : "mixed";
5465
+ const rounds = parseIntArg(args, "-r", "--rounds", DEFAULT_CLEAN_ROUNDS);
5466
+ const timeout = parseIntArg(args, "-t", "--timeout", 2e3);
5467
+ const concurrency = parseIntArg(args, "-j", "--concurrency", 100);
5460
5468
  const sub = getActiveSubscription();
5461
5469
  if (!sub) {
5462
5470
  console.error("\u9519\u8BEF: \u6CA1\u6709\u8BA2\u9605\uFF0C\u8BF7\u5148\u6DFB\u52A0\u8BA2\u9605");
@@ -5495,13 +5503,17 @@ async function cmdStart(args) {
5495
5503
  }
5496
5504
  process.exit(1);
5497
5505
  }
5498
- if (configInfo.proxies > AUTO_CLEAN_THRESHOLD) {
5506
+ const cleanThreshold = isGithubUrl(sub.url) ? AUTO_CLEAN_THRESHOLD_GITHUB : AUTO_CLEAN_THRESHOLD;
5507
+ if (configInfo.proxies > cleanThreshold) {
5499
5508
  console.log("");
5500
- console.log(`\u8282\u70B9\u6570 ${configInfo.proxies} \u8D85\u8FC7 ${AUTO_CLEAN_THRESHOLD}\uFF0C\u81EA\u52A8\u6E05\u7406...`);
5509
+ console.log(`\u8282\u70B9\u6570 ${configInfo.proxies} \u8D85\u8FC7 ${cleanThreshold}\uFF0C\u81EA\u52A8\u6E05\u7406...`);
5501
5510
  console.log("");
5502
5511
  await sleep(1e3);
5503
- const progress = createProgressPrinter(3);
5512
+ const progress = createProgressPrinter(rounds);
5504
5513
  const cleanResult = await autoCleanSubscription(sub.name, {
5514
+ timeout,
5515
+ concurrency,
5516
+ rounds,
5505
5517
  onResult: progress.onResult,
5506
5518
  onRetryRound: progress.onRetryRound
5507
5519
  });
@@ -5793,7 +5805,7 @@ function requireActiveSub() {
5793
5805
  async function cmdTest(args) {
5794
5806
  requireRunning();
5795
5807
  const activeSub = requireActiveSub();
5796
- const timeout = parseIntArg(args, "-t", "--timeout", 1500);
5808
+ const timeout = parseIntArg(args, "-t", "--timeout", 2e3);
5797
5809
  const concurrency = parseIntArg(args, "-j", "--concurrency", 100);
5798
5810
  console.log(`\u6D4B\u8BD5 "${activeSub.name}" \u8282\u70B9\u8FDE\u901A\u6027...`);
5799
5811
  console.log(`\u8D85\u65F6: ${timeout}ms \u5E76\u53D1: ${concurrency}`);
@@ -5810,9 +5822,9 @@ async function cmdTest(args) {
5810
5822
  async function cmdClean(args) {
5811
5823
  requireRunning();
5812
5824
  const activeSub = requireActiveSub();
5813
- const timeout = parseIntArg(args, "-t", "--timeout", 1500);
5825
+ const timeout = parseIntArg(args, "-t", "--timeout", 2e3);
5814
5826
  const concurrency = parseIntArg(args, "-j", "--concurrency", 100);
5815
- const rounds = parseIntArg(args, "-r", "--rounds", 3);
5827
+ const rounds = parseIntArg(args, "-r", "--rounds", DEFAULT_CLEAN_ROUNDS);
5816
5828
  console.log(`\u6E05\u7406 "${activeSub.name}" \u5931\u8D25\u8282\u70B9...`);
5817
5829
  console.log(`\u8D85\u65F6: ${timeout}ms \u5E76\u53D1: ${concurrency}`);
5818
5830
  console.log("");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mihomo-cli",
3
- "version": "2.7.2",
3
+ "version": "2.8.0",
4
4
  "type": "module",
5
5
  "description": "A terminal-based mihomo (Clash.Meta) client for macOS",
6
6
  "bin": {