mihomo-cli 2.0.1 → 2.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/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.1.0] - 2026-05-01
4
+
5
+ ### 新增
6
+
7
+ - **删除订阅**:`sub remove <name>` 删除订阅(别名 `rm`/`delete`),同时清理缓存和配置文件
8
+ - 删除当前使用中的订阅时自动切换到第一个剩余订阅
9
+ - **添加即切换**:`sub add` 添加订阅后自动切换为当前使用的订阅
10
+
11
+ ### 安全
12
+
13
+ - **强制 `allow-lan: false`**:无论订阅配置如何,始终禁止局域网访问
14
+ - **强制 `external-controller: 127.0.0.1:9090`**:控制面板仅监听本地,防止不可信订阅暴露控制接口
15
+ - **剥离 `external-ui` 相关字段**:构建配置时强制删除 `external-ui`/`external-ui-name`/`external-ui-url`,防止订阅触发额外下载
16
+
17
+ ### 优化
18
+
19
+ - **TUN DNS 劫持**:`dns-hijack` 从 `['0.0.0.0:53']` 改为 `['any:53', 'tcp://any:53']`,同时劫持 UDP 和 TCP DNS,覆盖 IPv4/IPv6
20
+ - **帮助顺序统一**:订阅子命令统一为 use → add → update → remove → web 顺序
21
+ - **`removeSubscription` 返回切换信息**:返回自动切换到的订阅名,避免调用方重复读取状态
22
+ - **`setDefaultSubscription` 跳过冗余写入**:已是同值时直接返回
23
+ - **删除后跳过自动更新**:`sub remove` 后列出订阅时不触发网络更新
24
+
25
+ ---
26
+
3
27
  ## [2.0.1] - 2026-04-22
4
28
 
5
29
  ### 修复
package/README.md CHANGED
@@ -94,10 +94,11 @@ mihomo ui yacd # YACD
94
94
  | 命令 | 说明 |
95
95
  | ----------------------------- | -------------------------------------- |
96
96
  | `mihomo sub` | 列出所有订阅(含流量、到期时间) |
97
- | `mihomo sub add <url> [name]` | 添加订阅 |
97
+ | `mihomo sub use <name>` | 切换当前订阅(支持模糊匹配,自动重启) |
98
+ | `mihomo sub add <url> [name]` | 添加订阅并自动切换 |
98
99
  | `mihomo sub update` | 更新所有订阅 |
99
100
  | `mihomo sub update <name>` | 更新指定订阅(支持模糊匹配) |
100
- | `mihomo sub use <name>` | 切换当前订阅(支持模糊匹配,自动重启) |
101
+ | `mihomo sub remove <name>` | 删除订阅(支持模糊匹配) |
101
102
  | `mihomo sub web [name]` | 打开订阅页面(无参打开默认) |
102
103
 
103
104
  ### 覆写配置
package/dist/index.js CHANGED
@@ -2695,13 +2695,15 @@ var TUN_CONFIG = {
2695
2695
  tun: {
2696
2696
  enable: true,
2697
2697
  stack: "mixed",
2698
- "dns-hijack": ["0.0.0.0:53"],
2698
+ "dns-hijack": ["any:53", "tcp://any:53"],
2699
2699
  "auto-route": true,
2700
2700
  "auto-detect-interface": true,
2701
2701
  "strict-route": true
2702
2702
  }
2703
2703
  };
2704
2704
  var BASE_CONFIG = {
2705
+ "allow-lan": false,
2706
+ "external-controller": "127.0.0.1:9090",
2705
2707
  "log-level": "warning",
2706
2708
  "geodata-mode": true,
2707
2709
  "geo-update-interval": 24,
@@ -2819,11 +2821,33 @@ function addSubscription(url, name = "default") {
2819
2821
  }
2820
2822
  writeSettings(updates);
2821
2823
  }
2824
+ function removeSubscription(name) {
2825
+ const settings = readSettings();
2826
+ const subs = settings.subscriptions || [];
2827
+ const idx = subs.findIndex((s) => s.name === name);
2828
+ if (idx < 0) return null;
2829
+ subs.splice(idx, 1);
2830
+ const updates = { subscriptions: subs };
2831
+ let switchedTo = null;
2832
+ if (settings.active_subscription === name) {
2833
+ switchedTo = subs.length > 0 ? subs[0].name : null;
2834
+ updates.active_subscription = switchedTo ?? void 0;
2835
+ }
2836
+ writeSettings(updates);
2837
+ const cache = readSubscriptionCache();
2838
+ if (cache[name]) {
2839
+ delete cache[name];
2840
+ writeSubscriptionCache(cache);
2841
+ }
2842
+ fs2.rmSync(getSubscriptionRawConfigPath(name), { force: true });
2843
+ return switchedTo;
2844
+ }
2822
2845
  function setDefaultSubscription(name) {
2823
2846
  const settings = readSettings();
2824
2847
  const subs = settings.subscriptions || [];
2825
2848
  const idx = subs.findIndex((s) => s.name === name);
2826
2849
  if (idx < 0) return false;
2850
+ if (settings.active_subscription === name) return true;
2827
2851
  writeSettings({ active_subscription: name });
2828
2852
  return true;
2829
2853
  }
@@ -3003,6 +3027,11 @@ function buildConfig(subRawContent, mode) {
3003
3027
  systemConfig[key] = value;
3004
3028
  }
3005
3029
  }
3030
+ systemConfig["allow-lan"] = false;
3031
+ systemConfig["external-controller"] = BASE_CONFIG["external-controller"];
3032
+ delete withOverwrites["external-ui"];
3033
+ delete withOverwrites["external-ui-name"];
3034
+ delete withOverwrites["external-ui-url"];
3006
3035
  if (mode === "tun") {
3007
3036
  systemConfig.tun = TUN_CONFIG.tun;
3008
3037
  const subDns = withOverwrites.dns || {};
@@ -3833,9 +3862,10 @@ ${colors.cyan("\u754C\u9762:")}
3833
3862
 
3834
3863
  ${colors.cyan("\u8BA2\u9605:")}
3835
3864
  ${colors.bold("subscription")} \u5217\u51FA\u6240\u6709\u8BA2\u9605\uFF08\u522B\u540D sub\uFF09
3865
+ ${colors.bold("subscription")} use <name> \u5207\u6362\u5F53\u524D\u8BA2\u9605
3836
3866
  ${colors.bold("subscription")} add <url> [name] \u6DFB\u52A0\u8BA2\u9605
3837
3867
  ${colors.bold("subscription")} update [name] \u66F4\u65B0\u8BA2\u9605\uFF08\u65E0\u53C2\u66F4\u65B0\u6240\u6709\uFF09
3838
- ${colors.bold("subscription")} use <name> \u5207\u6362\u5F53\u524D\u8BA2\u9605
3868
+ ${colors.bold("subscription")} remove <name> \u5220\u9664\u8BA2\u9605
3839
3869
  ${colors.bold("subscription")} web [name] \u6253\u5F00\u8BA2\u9605\u9875\u9762
3840
3870
 
3841
3871
  ${colors.cyan("\u914D\u7F6E:")}
@@ -4763,9 +4793,11 @@ async function cmdStop() {
4763
4793
  }
4764
4794
 
4765
4795
  // src/commands/subscription.ts
4766
- async function printSubscriptionList() {
4767
- const updateResult = await autoUpdateStaleSubscription();
4768
- if (updateResult.total > 0) console.log("");
4796
+ async function printSubscriptionList(options) {
4797
+ if (options?.autoUpdate !== false) {
4798
+ const updateResult = await autoUpdateStaleSubscription();
4799
+ if (updateResult.total > 0) console.log("");
4800
+ }
4769
4801
  const subs = getSubscriptionsWithCache();
4770
4802
  if (subs.length === 0) {
4771
4803
  console.log("\u6CA1\u6709\u8BA2\u9605");
@@ -4805,9 +4837,10 @@ async function printSubscriptionList() {
4805
4837
  });
4806
4838
  console.log("");
4807
4839
  console.log("\u5207\u6362\u8BA2\u9605: mihomo sub use <name>");
4840
+ console.log("\u65B0\u589E\u8BA2\u9605: mihomo sub add <url> [name]");
4808
4841
  console.log("\u66F4\u65B0\u8BA2\u9605: mihomo sub update [name]");
4842
+ console.log("\u5220\u9664\u8BA2\u9605: mihomo sub remove <name>");
4809
4843
  console.log("\u6253\u5F00\u9875\u9762: mihomo sub web [name]");
4810
- console.log("\u65B0\u589E\u8BA2\u9605: mihomo sub add <url> [name]");
4811
4844
  console.log("");
4812
4845
  }
4813
4846
  async function cmdSubscription(args) {
@@ -4826,8 +4859,9 @@ async function cmdSubscription(args) {
4826
4859
  console.log(`\u6DFB\u52A0\u8BA2\u9605: ${name}`);
4827
4860
  try {
4828
4861
  addSubscription(url, name);
4862
+ setDefaultSubscription(name);
4829
4863
  const info = await downloadSubscription(url, name);
4830
- console.log(`\u5DF2\u6DFB\u52A0 (${formatProxySummary(info)})`);
4864
+ console.log(`\u5DF2\u6DFB\u52A0\u5E76\u5207\u6362\u5230 "${name}" (${formatProxySummary(info)})`);
4831
4865
  } catch (e) {
4832
4866
  console.error(`\u6DFB\u52A0\u5931\u8D25: ${e.message}`);
4833
4867
  process.exit(1);
@@ -4953,8 +4987,30 @@ async function cmdSubscription(args) {
4953
4987
  }
4954
4988
  return;
4955
4989
  }
4990
+ if (action === "remove" || action === "rm" || action === "delete") {
4991
+ const name = args[2];
4992
+ const subs = getSubscriptions();
4993
+ if (!name) {
4994
+ console.error("\u9519\u8BEF: \u8BF7\u6307\u5B9A\u8981\u5220\u9664\u7684\u8BA2\u9605\u540D\u79F0");
4995
+ if (subs.length > 0) {
4996
+ console.log("\n\u53EF\u7528\u8BA2\u9605:");
4997
+ for (const s of subs) console.log(` ${s.name}`);
4998
+ }
4999
+ process.exit(1);
5000
+ }
5001
+ const matches = findSubscriptionFuzzy(subs, name);
5002
+ const target = pickSingleSubscription(matches, name);
5003
+ const switchedTo = removeSubscription(target.name);
5004
+ console.log(`\u5DF2\u5220\u9664\u8BA2\u9605 "${target.name}"`);
5005
+ if (switchedTo) {
5006
+ console.log(`\u5DF2\u81EA\u52A8\u5207\u6362\u5230 "${switchedTo}"`);
5007
+ }
5008
+ console.log("");
5009
+ await printSubscriptionList({ autoUpdate: false });
5010
+ return;
5011
+ }
4956
5012
  console.error("\u9519\u8BEF: \u672A\u77E5\u7684\u8BA2\u9605\u547D\u4EE4");
4957
- console.log("\u7528\u6CD5: mihomo sub [list|add|update|use|web]");
5013
+ console.log("\u7528\u6CD5: mihomo sub [list|use|add|update|remove|web]");
4958
5014
  process.exit(1);
4959
5015
  }
4960
5016
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mihomo-cli",
3
- "version": "2.0.1",
3
+ "version": "2.1.0",
4
4
  "type": "module",
5
5
  "description": "A terminal-based mihomo (Clash.Meta) client for macOS",
6
6
  "bin": {