mihomo-cli 2.0.0 → 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 +38 -0
- package/README.md +3 -2
- package/dist/index.js +73 -20
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
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
|
+
|
|
27
|
+
## [2.0.1] - 2026-04-22
|
|
28
|
+
|
|
29
|
+
### 修复
|
|
30
|
+
|
|
31
|
+
- **TUN DNS 默认值**:使用属性存在性检查替代 falsy 检查,避免订阅中 `dns.enable: false` 等值被覆盖
|
|
32
|
+
- **覆写文件名显示**:`overwrite.yaml` 不再显示为 "yaml",改为 "主文件"
|
|
33
|
+
|
|
34
|
+
### 优化
|
|
35
|
+
|
|
36
|
+
- **消除重复文件扫描**:覆写文件加载从每次构建 2 次减少为 1 次
|
|
37
|
+
- **清理死代码**:移除 `resetUserData`、`getGitHubMirror`、`setGitHubMirror`、未使用的类型字段
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
3
41
|
## [2.0.0] - 2026-04-11
|
|
4
42
|
|
|
5
43
|
### 架构重写
|
package/README.md
CHANGED
|
@@ -94,10 +94,11 @@ mihomo ui yacd # YACD
|
|
|
94
94
|
| 命令 | 说明 |
|
|
95
95
|
| ----------------------------- | -------------------------------------- |
|
|
96
96
|
| `mihomo sub` | 列出所有订阅(含流量、到期时间) |
|
|
97
|
-
| `mihomo sub
|
|
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
|
|
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": ["
|
|
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
|
}
|
|
@@ -2949,9 +2973,9 @@ function loadOverwriteFile() {
|
|
|
2949
2973
|
}
|
|
2950
2974
|
return results;
|
|
2951
2975
|
}
|
|
2952
|
-
function applyOverwrite(baseConfig) {
|
|
2976
|
+
function applyOverwrite(baseConfig, preloadedFiles) {
|
|
2953
2977
|
if (!isOverwriteEnabled()) return baseConfig;
|
|
2954
|
-
const overwriteFiles = loadOverwriteFile();
|
|
2978
|
+
const overwriteFiles = preloadedFiles || loadOverwriteFile();
|
|
2955
2979
|
if (overwriteFiles.length === 0) return baseConfig;
|
|
2956
2980
|
let result = { ...baseConfig };
|
|
2957
2981
|
for (const file of overwriteFiles) {
|
|
@@ -2995,21 +3019,26 @@ function buildConfig(subRawContent, mode) {
|
|
|
2995
3019
|
throw new Error("\u8BA2\u9605\u5185\u5BB9\u4E3A\u7A7A");
|
|
2996
3020
|
}
|
|
2997
3021
|
const overwriteEnabled = isOverwriteEnabled();
|
|
2998
|
-
const withOverwrites = applyOverwrite(subscriptionConfig);
|
|
2999
3022
|
const overwriteFiles = overwriteEnabled ? loadOverwriteFile() : [];
|
|
3023
|
+
const withOverwrites = applyOverwrite(subscriptionConfig, overwriteFiles);
|
|
3000
3024
|
const systemConfig = {};
|
|
3001
3025
|
for (const [key, value] of Object.entries(BASE_CONFIG)) {
|
|
3002
3026
|
if (!(key in withOverwrites)) {
|
|
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 || {};
|
|
3009
3038
|
const dns = {};
|
|
3010
|
-
if (!subDns
|
|
3011
|
-
if (!
|
|
3012
|
-
if (!
|
|
3039
|
+
if (!("enable" in subDns)) dns.enable = true;
|
|
3040
|
+
if (!("enhanced-mode" in subDns)) dns["enhanced-mode"] = "fake-ip";
|
|
3041
|
+
if (!("fake-ip-range" in subDns)) dns["fake-ip-range"] = "198.18.0.1/16";
|
|
3013
3042
|
if (Object.keys(dns).length > 0) {
|
|
3014
3043
|
systemConfig.dns = dns;
|
|
3015
3044
|
}
|
|
@@ -3662,7 +3691,6 @@ function listLogs() {
|
|
|
3662
3691
|
const stat = fs5.statSync(filePath);
|
|
3663
3692
|
result.archives.push({
|
|
3664
3693
|
name: file,
|
|
3665
|
-
timestamp: match[1],
|
|
3666
3694
|
path: filePath,
|
|
3667
3695
|
size: stat.size,
|
|
3668
3696
|
mtime: stat.mtime,
|
|
@@ -3834,9 +3862,10 @@ ${colors.cyan("\u754C\u9762:")}
|
|
|
3834
3862
|
|
|
3835
3863
|
${colors.cyan("\u8BA2\u9605:")}
|
|
3836
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
|
|
3837
3866
|
${colors.bold("subscription")} add <url> [name] \u6DFB\u52A0\u8BA2\u9605
|
|
3838
3867
|
${colors.bold("subscription")} update [name] \u66F4\u65B0\u8BA2\u9605\uFF08\u65E0\u53C2\u66F4\u65B0\u6240\u6709\uFF09
|
|
3839
|
-
${colors.bold("subscription")}
|
|
3868
|
+
${colors.bold("subscription")} remove <name> \u5220\u9664\u8BA2\u9605
|
|
3840
3869
|
${colors.bold("subscription")} web [name] \u6253\u5F00\u8BA2\u9605\u9875\u9762
|
|
3841
3870
|
|
|
3842
3871
|
${colors.cyan("\u914D\u7F6E:")}
|
|
@@ -3938,9 +3967,8 @@ var compareVersions = (v1, v2) => {
|
|
|
3938
3967
|
// src/kernel.ts
|
|
3939
3968
|
var GITHUB_REPO = "MetaCubeX/mihomo";
|
|
3940
3969
|
var KERNEL_HTTP_TIMEOUT = 12e4;
|
|
3941
|
-
var KERNEL_MAX_CONTENT_LENGTH = 200 * 1024 * 1024;
|
|
3942
3970
|
var KERNEL_DOWNLOAD_TIMEOUT = 18e4;
|
|
3943
|
-
var HTTP_CLIENT = createHttpClient({ timeout: KERNEL_HTTP_TIMEOUT
|
|
3971
|
+
var HTTP_CLIENT = createHttpClient({ timeout: KERNEL_HTTP_TIMEOUT });
|
|
3944
3972
|
function withMirror(url, mirror) {
|
|
3945
3973
|
if (mirror && (url.startsWith("https://github.com/") || url.startsWith("https://api.github.com/"))) {
|
|
3946
3974
|
return mirror + url;
|
|
@@ -4000,7 +4028,6 @@ async function checkUpdate(mirror) {
|
|
|
4000
4028
|
latest: latestVersion,
|
|
4001
4029
|
needsUpdate,
|
|
4002
4030
|
assets: latest.assets,
|
|
4003
|
-
htmlUrl: latest.html_url,
|
|
4004
4031
|
release: latest
|
|
4005
4032
|
};
|
|
4006
4033
|
}
|
|
@@ -4241,7 +4268,7 @@ import path5 from "path";
|
|
|
4241
4268
|
|
|
4242
4269
|
// src/subscription.ts
|
|
4243
4270
|
var DEFAULT_UPDATE_INTERVAL_HOURS = 12;
|
|
4244
|
-
var HTTP_CLIENT2 = createHttpClient({ timeout: 6e4
|
|
4271
|
+
var HTTP_CLIENT2 = createHttpClient({ timeout: 6e4 });
|
|
4245
4272
|
function parseUserInfo(header) {
|
|
4246
4273
|
if (!header) return null;
|
|
4247
4274
|
const info = {};
|
|
@@ -4458,7 +4485,7 @@ function printStatus() {
|
|
|
4458
4485
|
console.log(`${colors.gray("\u8BA2\u9605: ")}\u672A\u914D\u7F6E`);
|
|
4459
4486
|
}
|
|
4460
4487
|
if (overwriteEnabled && overwriteFiles.length > 0) {
|
|
4461
|
-
const names = overwriteFiles.map((f) => f.name.replace(/^overwrite\.?/, "")).join(", ");
|
|
4488
|
+
const names = overwriteFiles.map((f) => f.name.replace(/^overwrite\.?/, "").replace(/\.ya?ml$/, "") || "\u4E3B\u6587\u4EF6").join(", ");
|
|
4462
4489
|
console.log(`${colors.gray("\u8986\u5199: ")}${colors.green("\u5DF2\u542F\u7528")} (${names})`);
|
|
4463
4490
|
} else if (overwriteEnabled) {
|
|
4464
4491
|
console.log(`${colors.gray("\u8986\u5199: ")}${colors.green("\u5DF2\u542F\u7528")} (\u65E0\u6587\u4EF6)`);
|
|
@@ -4766,9 +4793,11 @@ async function cmdStop() {
|
|
|
4766
4793
|
}
|
|
4767
4794
|
|
|
4768
4795
|
// src/commands/subscription.ts
|
|
4769
|
-
async function printSubscriptionList() {
|
|
4770
|
-
|
|
4771
|
-
|
|
4796
|
+
async function printSubscriptionList(options) {
|
|
4797
|
+
if (options?.autoUpdate !== false) {
|
|
4798
|
+
const updateResult = await autoUpdateStaleSubscription();
|
|
4799
|
+
if (updateResult.total > 0) console.log("");
|
|
4800
|
+
}
|
|
4772
4801
|
const subs = getSubscriptionsWithCache();
|
|
4773
4802
|
if (subs.length === 0) {
|
|
4774
4803
|
console.log("\u6CA1\u6709\u8BA2\u9605");
|
|
@@ -4808,9 +4837,10 @@ async function printSubscriptionList() {
|
|
|
4808
4837
|
});
|
|
4809
4838
|
console.log("");
|
|
4810
4839
|
console.log("\u5207\u6362\u8BA2\u9605: mihomo sub use <name>");
|
|
4840
|
+
console.log("\u65B0\u589E\u8BA2\u9605: mihomo sub add <url> [name]");
|
|
4811
4841
|
console.log("\u66F4\u65B0\u8BA2\u9605: mihomo sub update [name]");
|
|
4842
|
+
console.log("\u5220\u9664\u8BA2\u9605: mihomo sub remove <name>");
|
|
4812
4843
|
console.log("\u6253\u5F00\u9875\u9762: mihomo sub web [name]");
|
|
4813
|
-
console.log("\u65B0\u589E\u8BA2\u9605: mihomo sub add <url> [name]");
|
|
4814
4844
|
console.log("");
|
|
4815
4845
|
}
|
|
4816
4846
|
async function cmdSubscription(args) {
|
|
@@ -4829,8 +4859,9 @@ async function cmdSubscription(args) {
|
|
|
4829
4859
|
console.log(`\u6DFB\u52A0\u8BA2\u9605: ${name}`);
|
|
4830
4860
|
try {
|
|
4831
4861
|
addSubscription(url, name);
|
|
4862
|
+
setDefaultSubscription(name);
|
|
4832
4863
|
const info = await downloadSubscription(url, name);
|
|
4833
|
-
console.log(`\u5DF2\u6DFB\u52A0 (${formatProxySummary(info)})`);
|
|
4864
|
+
console.log(`\u5DF2\u6DFB\u52A0\u5E76\u5207\u6362\u5230 "${name}" (${formatProxySummary(info)})`);
|
|
4834
4865
|
} catch (e) {
|
|
4835
4866
|
console.error(`\u6DFB\u52A0\u5931\u8D25: ${e.message}`);
|
|
4836
4867
|
process.exit(1);
|
|
@@ -4956,8 +4987,30 @@ async function cmdSubscription(args) {
|
|
|
4956
4987
|
}
|
|
4957
4988
|
return;
|
|
4958
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
|
+
}
|
|
4959
5012
|
console.error("\u9519\u8BEF: \u672A\u77E5\u7684\u8BA2\u9605\u547D\u4EE4");
|
|
4960
|
-
console.log("\u7528\u6CD5: mihomo sub [list|add|update|
|
|
5013
|
+
console.log("\u7528\u6CD5: mihomo sub [list|use|add|update|remove|web]");
|
|
4961
5014
|
process.exit(1);
|
|
4962
5015
|
}
|
|
4963
5016
|
|