mihomo-cli 2.2.0 → 2.2.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 +26 -0
- package/dist/index.js +41 -20
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.2.2] - 2026-05-01
|
|
4
|
+
|
|
5
|
+
### 修复
|
|
6
|
+
|
|
7
|
+
- **文件描述符泄漏**:修复 `startMixedMode` 中 spawn 后未关闭 fd 的问题
|
|
8
|
+
- **forceSudo 参数失效**:修复 `cleanupAll` 忽略调用方传入的强制 sudo 参数
|
|
9
|
+
- **formatBytes 溢出**:修复超大字节值(>1PB)导致显示 `undefined` 单位
|
|
10
|
+
- **YAML 解析类型检查**:`parseYamlOrJson` 现在拒绝非对象类型的 YAML 内容
|
|
11
|
+
- **spawn 错误处理**:`openUrl` 添加 error 事件处理,防止未捕获异常
|
|
12
|
+
- **UserInfo 类型转换**:移除 `parseUserInfo` 中多余的 `as unknown` 双重转换
|
|
13
|
+
|
|
14
|
+
### 安全
|
|
15
|
+
|
|
16
|
+
- **订阅名称校验**:新增文件名安全校验,防止路径穿越等不安全名称
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## [2.2.1] - 2026-05-01
|
|
21
|
+
|
|
22
|
+
### 修复
|
|
23
|
+
|
|
24
|
+
- **节点名称精简时序**:修复 `shortenProxyNames` 在测速前执行导致 API 返回 "Resource not found" 的问题,改为测速完成后再精简
|
|
25
|
+
- **清理安全阈值**:存活节点不足 1% 时跳过清理,提示用户检查原始订阅
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
3
29
|
## [2.2.0] - 2026-05-01
|
|
4
30
|
|
|
5
31
|
### 新增
|
package/dist/index.js
CHANGED
|
@@ -2808,7 +2808,14 @@ function getSubscriptionsWithCache() {
|
|
|
2808
2808
|
...cache[s.name] || {}
|
|
2809
2809
|
}));
|
|
2810
2810
|
}
|
|
2811
|
+
var SAFE_NAME_RE = /^[\w\-\p{Unified_Ideograph}]{1,64}$/u;
|
|
2812
|
+
function validateSubscriptionName(name) {
|
|
2813
|
+
if (!name || !SAFE_NAME_RE.test(name)) {
|
|
2814
|
+
throw new Error(`\u8BA2\u9605\u540D\u79F0\u65E0\u6548: "${name}"\uFF0C\u53EA\u5141\u8BB8\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u4E0B\u5212\u7EBF\u3001\u77ED\u6A2A\u7EBF\u548C\u4E2D\u6587\uFF08\u6700\u957F 64 \u5B57\u7B26\uFF09`);
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2811
2817
|
function addSubscription(url, name = "default") {
|
|
2818
|
+
validateSubscriptionName(name);
|
|
2812
2819
|
const settings = readSettings();
|
|
2813
2820
|
const subs = settings.subscriptions || [];
|
|
2814
2821
|
const existingIndex = subs.findIndex((s) => s.name === name);
|
|
@@ -3006,7 +3013,7 @@ function parseYamlOrJson(content, errorMsg) {
|
|
|
3006
3013
|
}
|
|
3007
3014
|
try {
|
|
3008
3015
|
const result = jsYaml.load(content);
|
|
3009
|
-
if (result
|
|
3016
|
+
if (result != null && typeof result === "object" && !Array.isArray(result)) return result;
|
|
3010
3017
|
} catch {
|
|
3011
3018
|
}
|
|
3012
3019
|
try {
|
|
@@ -3160,7 +3167,7 @@ function formatBytes(bytes) {
|
|
|
3160
3167
|
if (num === 0) return "0 B";
|
|
3161
3168
|
const k = 1024;
|
|
3162
3169
|
const sizes = ["B", "KB", "MB", "GB", "TB"];
|
|
3163
|
-
const i = Math.floor(Math.log(num) / Math.log(k));
|
|
3170
|
+
const i = Math.min(Math.floor(Math.log(num) / Math.log(k)), sizes.length - 1);
|
|
3164
3171
|
return `${parseFloat((num / k ** i).toFixed(2))} ${sizes[i]}`;
|
|
3165
3172
|
}
|
|
3166
3173
|
function formatTimestamp(ts) {
|
|
@@ -3405,14 +3412,14 @@ function killAllMihomo(forceSudo = false) {
|
|
|
3405
3412
|
}
|
|
3406
3413
|
}
|
|
3407
3414
|
}
|
|
3408
|
-
function cleanupAll(
|
|
3415
|
+
function cleanupAll(forceSudo = false) {
|
|
3409
3416
|
const pids = getAllMihomoPids();
|
|
3410
3417
|
if (pids.length === 0) {
|
|
3411
3418
|
clearPid();
|
|
3412
3419
|
return { killed: 0, failed: 0, remaining: [] };
|
|
3413
3420
|
}
|
|
3414
3421
|
const hasRootProcess = pids.some((p) => isProcessRoot(p));
|
|
3415
|
-
const needsSudo = hasRootProcess;
|
|
3422
|
+
const needsSudo = forceSudo || hasRootProcess;
|
|
3416
3423
|
let killedCount = 0;
|
|
3417
3424
|
const failedPids = [];
|
|
3418
3425
|
if (needsSudo) {
|
|
@@ -3560,12 +3567,12 @@ async function startMixedMode(staleState) {
|
|
|
3560
3567
|
const configFile = PATHS.configFile;
|
|
3561
3568
|
const logFile = PATHS.logFile;
|
|
3562
3569
|
const args = ["-d", DIRS.data, "-f", configFile];
|
|
3563
|
-
const
|
|
3564
|
-
const err = fs5.openSync(logFile, "a");
|
|
3570
|
+
const logFd = fs5.openSync(logFile, "a");
|
|
3565
3571
|
const child = spawn(PATHS.mihomoBinary, args, {
|
|
3566
3572
|
detached: true,
|
|
3567
|
-
stdio: ["ignore",
|
|
3573
|
+
stdio: ["ignore", logFd, logFd]
|
|
3568
3574
|
});
|
|
3575
|
+
fs5.closeSync(logFd);
|
|
3569
3576
|
child.unref();
|
|
3570
3577
|
const pid = child.pid;
|
|
3571
3578
|
savePid(pid);
|
|
@@ -3735,7 +3742,10 @@ function getLogPathByName(name) {
|
|
|
3735
3742
|
}
|
|
3736
3743
|
function openUrl(url) {
|
|
3737
3744
|
try {
|
|
3738
|
-
spawn("open", [url], { stdio: "ignore", detached: true });
|
|
3745
|
+
const child = spawn("open", [url], { stdio: "ignore", detached: true });
|
|
3746
|
+
child.unref();
|
|
3747
|
+
child.on("error", () => {
|
|
3748
|
+
});
|
|
3739
3749
|
return true;
|
|
3740
3750
|
} catch {
|
|
3741
3751
|
return false;
|
|
@@ -4293,6 +4303,7 @@ function loadSubscriptionConfig(subName) {
|
|
|
4293
4303
|
};
|
|
4294
4304
|
}
|
|
4295
4305
|
function saveSubscriptionConfig(subName, parsed) {
|
|
4306
|
+
shortenProxyNames(parsed);
|
|
4296
4307
|
parsed.raw.proxies = parsed.proxies;
|
|
4297
4308
|
parsed.raw["proxy-groups"] = parsed.proxyGroups;
|
|
4298
4309
|
saveSubscriptionRawConfig(subName, jsYaml.dump(parsed.raw, YAML_DUMP_OPTS));
|
|
@@ -4571,21 +4582,26 @@ function cleanDeadProxies(parsed, deadNames) {
|
|
|
4571
4582
|
}
|
|
4572
4583
|
async function autoCleanSubscription(subName, options = {}) {
|
|
4573
4584
|
const parsed = loadSubscriptionConfig(subName);
|
|
4574
|
-
shortenProxyNames(parsed);
|
|
4575
|
-
saveSubscriptionConfig(subName, parsed);
|
|
4576
4585
|
const summary = await testSubscriptionProxies(subName, { ...options, parsed });
|
|
4577
4586
|
let removedProxies = 0;
|
|
4578
4587
|
let updatedGroups = 0;
|
|
4579
4588
|
let removedGroups = 0;
|
|
4589
|
+
let skipped = false;
|
|
4580
4590
|
if (summary.dead > 0) {
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4591
|
+
if (summary.alive === 0 || summary.alive / summary.total < 0.01) {
|
|
4592
|
+
skipped = true;
|
|
4593
|
+
} else {
|
|
4594
|
+
const deadNames = new Set(summary.results.filter((r) => r.delay === null).map((r) => r.name));
|
|
4595
|
+
const cleanResult = cleanDeadProxies(parsed, deadNames);
|
|
4596
|
+
removedProxies = cleanResult.removedProxies;
|
|
4597
|
+
updatedGroups = cleanResult.updatedGroups;
|
|
4598
|
+
removedGroups = cleanResult.removedGroups;
|
|
4599
|
+
}
|
|
4600
|
+
}
|
|
4601
|
+
if (!skipped) {
|
|
4586
4602
|
saveSubscriptionConfig(subName, parsed);
|
|
4587
4603
|
}
|
|
4588
|
-
return { summary, removedProxies, updatedGroups, removedGroups };
|
|
4604
|
+
return { summary, removedProxies, updatedGroups, removedGroups, skipped };
|
|
4589
4605
|
}
|
|
4590
4606
|
|
|
4591
4607
|
// src/commands/status.ts
|
|
@@ -4921,11 +4937,14 @@ async function cmdSubscription(args) {
|
|
|
4921
4937
|
});
|
|
4922
4938
|
console.log("");
|
|
4923
4939
|
console.log(formatTestSummary(result.summary));
|
|
4924
|
-
if (result.
|
|
4940
|
+
if (result.skipped) {
|
|
4941
|
+
console.log("");
|
|
4942
|
+
console.log(colors.yellow("\u5B58\u6D3B\u8282\u70B9\u4E0D\u8DB3 1%\uFF0C\u8DF3\u8FC7\u6E05\u7406\u3002\u8BF7\u68C0\u67E5\u539F\u59CB\u8BA2\u9605\u662F\u5426\u6709\u6548"));
|
|
4943
|
+
} else if (result.removedProxies > 0) {
|
|
4925
4944
|
console.log(`${colors.green("\u5DF2\u6E05\u7406")}: ${formatCleanSummary(result)}`);
|
|
4945
|
+
console.log("");
|
|
4946
|
+
console.log("\u63D0\u793A: \u9700\u8981\u91CD\u542F mihomo \u4F7F\u66F4\u6539\u751F\u6548 (mihomo start)");
|
|
4926
4947
|
}
|
|
4927
|
-
console.log("");
|
|
4928
|
-
console.log("\u63D0\u793A: \u9700\u8981\u91CD\u542F mihomo \u4F7F\u66F4\u6539\u751F\u6548 (mihomo start)");
|
|
4929
4948
|
return;
|
|
4930
4949
|
}
|
|
4931
4950
|
if (action === "test") {
|
|
@@ -5003,7 +5022,9 @@ async function cmdStart(args) {
|
|
|
5003
5022
|
const cleanResult = await autoCleanSubscription(sub.name, { onResult: printTestResult });
|
|
5004
5023
|
console.log("");
|
|
5005
5024
|
console.log(formatTestSummary(cleanResult.summary));
|
|
5006
|
-
if (cleanResult.
|
|
5025
|
+
if (cleanResult.skipped) {
|
|
5026
|
+
console.log(colors.yellow("\u5B58\u6D3B\u8282\u70B9\u4E0D\u8DB3 1%\uFF0C\u8DF3\u8FC7\u6E05\u7406\u3002\u8BF7\u68C0\u67E5\u539F\u59CB\u8BA2\u9605\u662F\u5426\u6709\u6548"));
|
|
5027
|
+
} else if (cleanResult.removedProxies > 0) {
|
|
5007
5028
|
console.log(`${colors.green("\u5DF2\u6E05\u7406")}: ${formatCleanSummary(cleanResult)}`);
|
|
5008
5029
|
console.log("");
|
|
5009
5030
|
console.log("\u91CD\u65B0\u52A0\u8F7D\u914D\u7F6E...");
|