mihomo-cli 3.4.0 → 3.5.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 +12 -0
- package/dist/index.js +779 -823
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -3602,6 +3602,18 @@ var TimeoutError = class extends Error {
|
|
|
3602
3602
|
this.name = "TimeoutError";
|
|
3603
3603
|
}
|
|
3604
3604
|
};
|
|
3605
|
+
var CliError = class extends Error {
|
|
3606
|
+
hint;
|
|
3607
|
+
label;
|
|
3608
|
+
exitCode;
|
|
3609
|
+
constructor(message, options = {}) {
|
|
3610
|
+
super(message);
|
|
3611
|
+
this.name = "CliError";
|
|
3612
|
+
this.label = options.label ?? "\u9519\u8BEF";
|
|
3613
|
+
this.hint = options.hint === void 0 ? [] : Array.isArray(options.hint) ? options.hint : [options.hint];
|
|
3614
|
+
this.exitCode = options.exitCode ?? 1;
|
|
3615
|
+
}
|
|
3616
|
+
};
|
|
3605
3617
|
function withTimeout(promise, ms) {
|
|
3606
3618
|
return new Promise((resolve, reject) => {
|
|
3607
3619
|
const timer = setTimeout(() => reject(new TimeoutError()), ms);
|
|
@@ -5016,6 +5028,13 @@ function getActiveSubscription() {
|
|
|
5016
5028
|
}
|
|
5017
5029
|
return subs[0];
|
|
5018
5030
|
}
|
|
5031
|
+
function requireActiveSubscription(emptyMsg = "\u6CA1\u6709\u8BA2\u9605\uFF0C\u8BF7\u5148\u6DFB\u52A0\u8BA2\u9605") {
|
|
5032
|
+
const sub = getActiveSubscription();
|
|
5033
|
+
if (!sub) {
|
|
5034
|
+
throw new CliError(emptyMsg);
|
|
5035
|
+
}
|
|
5036
|
+
return sub;
|
|
5037
|
+
}
|
|
5019
5038
|
function findSubscriptionFuzzy(subs, pattern) {
|
|
5020
5039
|
const lowerPattern = pattern.toLowerCase();
|
|
5021
5040
|
const exact = [];
|
|
@@ -5037,14 +5056,15 @@ function findSubscriptionFuzzy(subs, pattern) {
|
|
|
5037
5056
|
}
|
|
5038
5057
|
function pickSingleSubscription(subs, pattern) {
|
|
5039
5058
|
if (subs.length === 0) {
|
|
5040
|
-
|
|
5041
|
-
process.exit(1);
|
|
5059
|
+
throw new CliError(`\u672A\u627E\u5230\u5339\u914D "${pattern}" \u7684\u8BA2\u9605`);
|
|
5042
5060
|
}
|
|
5043
5061
|
if (subs.length === 1) return subs[0];
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5062
|
+
throw new CliError("\u5339\u914D\u5230\u591A\u4E2A\u8BA2\u9605\uFF0C\u8BF7\u66F4\u7CBE\u786E\u6307\u5B9A", {
|
|
5063
|
+
hint: ["", "\u5339\u914D\u7684\u8BA2\u9605:", ...subs.map((s) => ` ${s.name}`)]
|
|
5064
|
+
});
|
|
5065
|
+
}
|
|
5066
|
+
function resolveSubscription(subs, pattern) {
|
|
5067
|
+
return pickSingleSubscription(findSubscriptionFuzzy(subs, pattern), pattern);
|
|
5048
5068
|
}
|
|
5049
5069
|
async function downloadSubscription(url, subName = "default", signal, persist = true) {
|
|
5050
5070
|
let response;
|
|
@@ -5414,174 +5434,475 @@ async function autoCleanSubscription(subName, options = {}) {
|
|
|
5414
5434
|
return { summary, removedProxies, updatedGroups, removedGroups, skipped };
|
|
5415
5435
|
}
|
|
5416
5436
|
|
|
5417
|
-
// src/
|
|
5418
|
-
function
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
console.log("");
|
|
5427
|
-
if (status.enabled) {
|
|
5428
|
-
console.log("\u5173\u95ED\u4FDD\u6D3B: mihomo daemon off");
|
|
5429
|
-
} else {
|
|
5430
|
-
console.log("\u5F00\u542F\u4FDD\u6D3B: mihomo daemon on");
|
|
5431
|
-
console.log(colors.gray(" \u5F00\u673A\u81EA\u542F + \u5D29\u6E83\u81EA\u52A8\u91CD\u542F\uFF08\u4EC5 Mixed \u6A21\u5F0F\uFF09"));
|
|
5437
|
+
// src/runtime.ts
|
|
5438
|
+
function getRuntimeMode() {
|
|
5439
|
+
if (isDaemonEnabled()) return "mixed";
|
|
5440
|
+
return getConfigInfo()?.tun ? "tun" : "mixed";
|
|
5441
|
+
}
|
|
5442
|
+
function getRunningState() {
|
|
5443
|
+
if (isDaemonEnabled()) {
|
|
5444
|
+
const daemon = getDaemonStatus();
|
|
5445
|
+
return { running: isDaemonRunning(daemon), pid: daemon.pid, daemon: true };
|
|
5432
5446
|
}
|
|
5433
|
-
|
|
5447
|
+
const status = getStatus();
|
|
5448
|
+
return { running: status.running, pid: status.pid, daemon: false };
|
|
5434
5449
|
}
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
}
|
|
5442
|
-
const sub = getActiveSubscription();
|
|
5443
|
-
if (!sub) {
|
|
5444
|
-
console.error("\u9519\u8BEF: \u6CA1\u6709\u8BA2\u9605\uFF0C\u8BF7\u5148\u6DFB\u52A0\u8BA2\u9605");
|
|
5445
|
-
process.exit(1);
|
|
5446
|
-
}
|
|
5447
|
-
let configInfo;
|
|
5448
|
-
try {
|
|
5449
|
-
configInfo = prepareConfigForStart("mixed", sub.name);
|
|
5450
|
-
} catch (e) {
|
|
5451
|
-
console.error(`${colors.red("\u914D\u7F6E\u9519\u8BEF:")} ${e.message}`);
|
|
5452
|
-
process.exit(1);
|
|
5453
|
-
}
|
|
5454
|
-
console.log(colors.gray("\u5C06\u8BF7\u6C42\u7BA1\u7406\u5458\u6743\u9650\u4EE5\u5B89\u88C5\u7CFB\u7EDF\u7EA7\u4FDD\u6D3B\u670D\u52A1\uFF08LaunchDaemon\uFF09"));
|
|
5455
|
-
console.log(colors.gray("\u7CFB\u7EDF\u7EA7\u4FDD\u6D3B\u9700\u8981 root\uFF0C\u4EE5\u89E3\u51B3\u5C40\u57DF\u7F51\u8BBF\u95EE\u53D7\u9650\u95EE\u9898"));
|
|
5456
|
-
try {
|
|
5457
|
-
enableDaemon();
|
|
5458
|
-
} catch (e) {
|
|
5459
|
-
console.error(`${colors.red("\u542F\u7528\u4FDD\u6D3B\u5931\u8D25:")} ${e.message}`);
|
|
5460
|
-
process.exit(1);
|
|
5461
|
-
}
|
|
5462
|
-
console.log(`${colors.green("\u5DF2\u542F\u7528\u4FDD\u6D3B")} \xB7 ${sub.name} \xB7 ${formatProxySummary(configInfo)}`);
|
|
5463
|
-
console.log(colors.gray("\u5F00\u673A\u81EA\u542F + \u5D29\u6E83\u81EA\u52A8\u91CD\u542F\uFF0C\u4EE3\u7406\u5C06\u5728\u540E\u53F0\u5E38\u9A7B"));
|
|
5464
|
-
console.log("");
|
|
5450
|
+
function isRestartNeededOnChange() {
|
|
5451
|
+
return isDaemonEnabled() || getStatus().running;
|
|
5452
|
+
}
|
|
5453
|
+
async function launchOrRestart(mode) {
|
|
5454
|
+
if (isDaemonEnabled()) {
|
|
5455
|
+
await restartDaemon();
|
|
5465
5456
|
await sleep(DAEMON_BOOT_WAIT_MS);
|
|
5466
|
-
|
|
5467
|
-
|
|
5457
|
+
return getDaemonStatus().pid;
|
|
5458
|
+
}
|
|
5459
|
+
const result = await start(mode);
|
|
5460
|
+
return result.pid;
|
|
5461
|
+
}
|
|
5462
|
+
|
|
5463
|
+
// src/progress.ts
|
|
5464
|
+
var IS_TTY = process.stdout.isTTY === true;
|
|
5465
|
+
var BAR_WIDTH = 20;
|
|
5466
|
+
function createProgressPrinter(totalRounds = 1) {
|
|
5467
|
+
let alive = 0;
|
|
5468
|
+
let dead = 0;
|
|
5469
|
+
const resultMap = /* @__PURE__ */ new Map();
|
|
5470
|
+
function render(done, total) {
|
|
5471
|
+
if (!IS_TTY) return;
|
|
5472
|
+
const pct = Math.round(done / total * 100);
|
|
5473
|
+
const filled = Math.round(done / total * BAR_WIDTH);
|
|
5474
|
+
const bar = "\u2588".repeat(filled) + "\u2591".repeat(BAR_WIDTH - filled);
|
|
5475
|
+
process.stdout.write(`\r${bar} ${done}/${total} (${pct}%) | ${colors.green(`\u2713${alive}`)} ${colors.red(`\u2717${dead}`)}`);
|
|
5468
5476
|
}
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5477
|
+
return {
|
|
5478
|
+
onResult(result, index, total, round = 1) {
|
|
5479
|
+
if (resultMap.size === 0 && totalRounds > 1) {
|
|
5480
|
+
console.log(`--- \u7B2C 1 \u8F6E\u6D4B\u8BD5 (${total} \u4E2A\u8282\u70B9) ---`);
|
|
5481
|
+
}
|
|
5482
|
+
const prev = resultMap.get(result.name);
|
|
5483
|
+
if (prev) {
|
|
5484
|
+
if (prev.result.delay !== null) alive--;
|
|
5485
|
+
else dead--;
|
|
5486
|
+
}
|
|
5487
|
+
if (result.delay !== null) alive++;
|
|
5488
|
+
else dead++;
|
|
5489
|
+
resultMap.set(result.name, { result, round });
|
|
5490
|
+
render(index + 1, total);
|
|
5491
|
+
},
|
|
5492
|
+
onRetryRound(round, count) {
|
|
5493
|
+
if (IS_TTY) {
|
|
5494
|
+
process.stdout.write("\n");
|
|
5495
|
+
}
|
|
5496
|
+
console.log(`--- \u7B2C ${round} \u8F6E\u91CD\u8BD5 (${count} \u4E2A\u8282\u70B9) ---`);
|
|
5497
|
+
alive = 0;
|
|
5498
|
+
dead = 0;
|
|
5499
|
+
},
|
|
5500
|
+
finish() {
|
|
5501
|
+
if (IS_TTY) {
|
|
5502
|
+
process.stdout.write("\n");
|
|
5503
|
+
}
|
|
5504
|
+
console.log("");
|
|
5505
|
+
if (!IS_TTY) return;
|
|
5506
|
+
const entries = [...resultMap.values()];
|
|
5507
|
+
entries.sort((a, b) => a.result.name.localeCompare(b.result.name));
|
|
5508
|
+
const total = entries.length;
|
|
5509
|
+
console.log("\u8282\u70B9\u6700\u7EC8\u72B6\u6001:");
|
|
5510
|
+
for (let i = 0; i < entries.length; i++) {
|
|
5511
|
+
const { result, round } = entries[i];
|
|
5512
|
+
const prefix = `[${i + 1}/${total}]`;
|
|
5513
|
+
if (result.delay !== null) {
|
|
5514
|
+
const delayColor = result.delay < 300 ? colors.green : result.delay < 800 ? colors.yellow : colors.red;
|
|
5515
|
+
const retryNote = round > 1 ? colors.gray(` (\u7B2C${round}\u8F6E\u901A\u8FC7)`) : "";
|
|
5516
|
+
console.log(`${prefix} ${colors.green("\u2713")} ${result.name} ${delayColor(`${result.delay}ms`)}${retryNote}`);
|
|
5517
|
+
} else {
|
|
5518
|
+
console.log(`${prefix} ${colors.red("\u2717")} ${result.name} ${colors.gray(result.error || "timeout")}`);
|
|
5519
|
+
}
|
|
5520
|
+
}
|
|
5472
5521
|
console.log("");
|
|
5473
|
-
printDaemonStatus();
|
|
5474
|
-
return;
|
|
5475
|
-
}
|
|
5476
|
-
console.log(colors.gray("\u5C06\u8BF7\u6C42\u7BA1\u7406\u5458\u6743\u9650\u4EE5\u79FB\u9664\u7CFB\u7EDF\u7EA7\u4FDD\u6D3B\u670D\u52A1"));
|
|
5477
|
-
try {
|
|
5478
|
-
disableDaemon();
|
|
5479
|
-
} catch (e) {
|
|
5480
|
-
console.error(`${colors.red("\u5173\u95ED\u4FDD\u6D3B\u5931\u8D25:")} ${e.message}`);
|
|
5481
|
-
process.exit(1);
|
|
5482
5522
|
}
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
}
|
|
5488
|
-
if (
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
}
|
|
5494
|
-
console.log("");
|
|
5495
|
-
printDaemonStatus();
|
|
5523
|
+
};
|
|
5524
|
+
}
|
|
5525
|
+
function formatCleanSummary(result) {
|
|
5526
|
+
const parts = [`\u79FB\u9664 ${result.removedProxies} \u4E2A\u8282\u70B9`];
|
|
5527
|
+
if (result.removedGroups > 0) parts.push(`\u5220\u9664 ${result.removedGroups} \u4E2A\u7A7A\u5206\u7EC4`);
|
|
5528
|
+
if (result.updatedGroups > 0) parts.push(`\u66F4\u65B0 ${result.updatedGroups} \u4E2A\u5206\u7EC4`);
|
|
5529
|
+
return parts.join(", ");
|
|
5530
|
+
}
|
|
5531
|
+
function formatTestSummary(summary) {
|
|
5532
|
+
return `\u7ED3\u679C: ${colors.green(`${summary.alive} \u5B58\u6D3B`)} / ${colors.red(`${summary.dead} \u5931\u8D25`)} / ${summary.total} \u603B\u8BA1`;
|
|
5496
5533
|
}
|
|
5497
5534
|
|
|
5498
|
-
// src/commands/
|
|
5499
|
-
function
|
|
5500
|
-
const
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5535
|
+
// src/commands/status.ts
|
|
5536
|
+
function printStatus() {
|
|
5537
|
+
const status = getStatus();
|
|
5538
|
+
const state = getRunningState();
|
|
5539
|
+
const info = getConfigInfo();
|
|
5540
|
+
const overwriteEnabled = isOverwriteEnabled();
|
|
5541
|
+
const overwriteFiles = listOverwriteFile().files;
|
|
5542
|
+
const activeSub = getActiveSubscription();
|
|
5543
|
+
const { running, pid, daemon: daemonManaged } = state;
|
|
5544
|
+
console.log("");
|
|
5545
|
+
let modeLabel = "";
|
|
5546
|
+
if (info) {
|
|
5547
|
+
modeLabel = colors.cyan(info.tun ? " (TUN)" : " (Mixed)");
|
|
5548
|
+
}
|
|
5549
|
+
const statusText = running ? colors.green("\u25CF \u8FD0\u884C\u4E2D") : colors.yellow("\u4E0D\u5728\u8FD0\u884C");
|
|
5550
|
+
console.log(`${colors.gray("\u72B6\u6001: ")}${statusText}${modeLabel}`);
|
|
5551
|
+
console.log(`${colors.gray("\u5185\u6838: ")}${status.kernelVersion || "\u672A\u5B89\u88C5"}`);
|
|
5552
|
+
if (pid) {
|
|
5553
|
+
console.log(`${colors.gray("PID: ")}${pid}`);
|
|
5554
|
+
if (!daemonManaged && status.processInfo) {
|
|
5555
|
+
console.log(`${colors.gray("\u5185\u5B58: ")}${status.processInfo.memory}`);
|
|
5510
5556
|
}
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
if (
|
|
5514
|
-
const
|
|
5515
|
-
console.log(
|
|
5516
|
-
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5557
|
+
}
|
|
5558
|
+
if (info) {
|
|
5559
|
+
if (info.tun) {
|
|
5560
|
+
const extra = info.mixedPort ? `\uFF0C\u53E6\u76D1\u542C ${info.mixedPort}` : "";
|
|
5561
|
+
console.log(`${colors.gray("\u7AEF\u53E3: ")}TUN \u63A5\u7BA1${extra}`);
|
|
5562
|
+
} else if (info.mixedPort) {
|
|
5563
|
+
console.log(`${colors.gray("\u7AEF\u53E3: ")}${info.mixedPort}`);
|
|
5564
|
+
} else {
|
|
5565
|
+
const ports = [];
|
|
5566
|
+
if (info.httpPort) ports.push(`HTTP:${info.httpPort}`);
|
|
5567
|
+
if (info.socksPort) ports.push(`SOCKS:${info.socksPort}`);
|
|
5568
|
+
console.log(`${colors.gray("\u7AEF\u53E3: ")}${ports.length > 0 ? ports.join(", ") : "\u672A\u77E5"}`);
|
|
5521
5569
|
}
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
if (key2 !== "root") {
|
|
5528
|
-
console.log(` ${key2.padEnd(14)}${val.label}`);
|
|
5529
|
-
}
|
|
5570
|
+
}
|
|
5571
|
+
if (activeSub) {
|
|
5572
|
+
let subLine = `${colors.gray("\u8BA2\u9605: ")}${activeSub.name}`;
|
|
5573
|
+
if (info) {
|
|
5574
|
+
subLine += ` (${formatProxySummary(info)})`;
|
|
5530
5575
|
}
|
|
5531
|
-
console.log(
|
|
5532
|
-
|
|
5576
|
+
console.log(subLine);
|
|
5577
|
+
} else {
|
|
5578
|
+
console.log(`${colors.gray("\u8BA2\u9605: ")}\u672A\u914D\u7F6E`);
|
|
5579
|
+
}
|
|
5580
|
+
if (overwriteEnabled && overwriteFiles.length > 0) {
|
|
5581
|
+
const names = overwriteFiles.map((f) => f.name.replace(/^overwrite\.?/, "").replace(/\.ya?ml$/, "") || "\u4E3B\u6587\u4EF6").join(", ");
|
|
5582
|
+
console.log(`${colors.gray("\u8986\u5199: ")}${colors.green("\u5DF2\u542F\u7528")} (${names})`);
|
|
5583
|
+
} else if (overwriteEnabled) {
|
|
5584
|
+
console.log(`${colors.gray("\u8986\u5199: ")}${colors.green("\u5DF2\u542F\u7528")} (\u65E0\u6587\u4EF6)`);
|
|
5585
|
+
} else {
|
|
5586
|
+
console.log(`${colors.gray("\u8986\u5199: ")}${colors.yellow("\u5DF2\u7981\u7528")}`);
|
|
5587
|
+
}
|
|
5588
|
+
if (isDaemonEnabled()) {
|
|
5589
|
+
console.log(`${colors.gray("\u4FDD\u6D3B: ")}${colors.green("\u5DF2\u542F\u7528")} ${colors.gray("(\u5F00\u673A\u81EA\u542F + \u5D29\u6E83\u91CD\u542F)")}`);
|
|
5533
5590
|
}
|
|
5534
|
-
console.log("");
|
|
5535
|
-
console.log("\u6570\u636E\u76EE\u5F55\u4F4D\u7F6E:");
|
|
5536
|
-
console.log(` \u6839\u76EE\u5F55: ${USER_DATA_DIR}`);
|
|
5537
|
-
console.log(` \u5168\u5C40\u8BBE\u7F6E: ${PATHS.settingsFile}`);
|
|
5538
|
-
console.log(` \u5185\u6838\u76EE\u5F55: ${DIRS.kernel}`);
|
|
5539
|
-
console.log(` \u5185\u6838\u6587\u4EF6: ${PATHS.mihomoBinary}`);
|
|
5540
|
-
console.log(` \u8BA2\u9605\u76EE\u5F55: ${DIRS.subscriptions}`);
|
|
5541
|
-
console.log(" - cache.json (\u8BA2\u9605\u7F13\u5B58\uFF1A\u66F4\u65B0\u65F6\u95F4\u3001\u6D41\u91CF\u7B49)");
|
|
5542
|
-
console.log(" - xxx.yaml (\u8BA2\u9605\u539F\u59CB\u914D\u7F6E)");
|
|
5543
|
-
console.log(` \u8FD0\u884C\u65F6\u76EE\u5F55: ${DIRS.runtime}`);
|
|
5544
|
-
console.log(" - config.yaml (\u542F\u52A8\u65F6\u751F\u6210\uFF0Cstop \u81EA\u52A8\u6E05\u9664)");
|
|
5545
|
-
console.log(" - pid (PID \u6587\u4EF6\uFF0Cstop \u81EA\u52A8\u6E05\u9664)");
|
|
5546
|
-
console.log(` \u65E5\u5FD7\u6587\u4EF6: ${PATHS.logFile}`);
|
|
5547
|
-
console.log(` mihomo \u6570\u636E: ${DIRS.data}`);
|
|
5548
|
-
console.log(" - cache.db, Geo*.dat \u7B49 (mihomo \u81EA\u884C\u7BA1\u7406)");
|
|
5549
|
-
console.log("");
|
|
5550
|
-
console.log("\u6253\u5F00\u76EE\u5F55:");
|
|
5551
|
-
console.log(" mihomo dir open \u6253\u5F00\u6839\u76EE\u5F55");
|
|
5552
|
-
console.log(" mihomo dir open subs \u6253\u5F00\u8BA2\u9605\u76EE\u5F55");
|
|
5553
|
-
console.log(" mihomo dir open logs \u6253\u5F00\u65E5\u5FD7\u76EE\u5F55");
|
|
5554
|
-
console.log(" mihomo dir open data \u6253\u5F00 mihomo \u6570\u636E\u76EE\u5F55");
|
|
5555
|
-
console.log(" mihomo dir open runtime \u6253\u5F00\u8FD0\u884C\u65F6\u76EE\u5F55");
|
|
5556
|
-
console.log(" mihomo dir open kernel \u6253\u5F00\u5185\u6838\u76EE\u5F55");
|
|
5557
|
-
console.log("");
|
|
5558
|
-
console.log("\u73AF\u5883\u53D8\u91CF:");
|
|
5559
|
-
console.log(" MIHOMO_CLI_DIR: \u81EA\u5B9A\u4E49\u6839\u76EE\u5F55\u4F4D\u7F6E");
|
|
5560
5591
|
console.log("");
|
|
5561
5592
|
}
|
|
5562
5593
|
|
|
5563
|
-
// src/
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
// node_modules/compare-versions/lib/esm/utils.js
|
|
5569
|
-
var semver = /^[v^~<>=]*?(\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+))?(?:-([\da-z\-]+(?:\.[\da-z\-]+)*))?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?)?)?$/i;
|
|
5570
|
-
var validateAndParse = (version) => {
|
|
5571
|
-
if (typeof version !== "string") {
|
|
5572
|
-
throw new TypeError("Invalid argument expected string");
|
|
5594
|
+
// src/commands/stop.ts
|
|
5595
|
+
function handleStopResult(result) {
|
|
5596
|
+
if (result.remaining && result.remaining.length > 0) {
|
|
5597
|
+
throw new CliError(result.remaining.join(", "), { label: "\u90E8\u5206\u8FDB\u7A0B\u672A\u7EC8\u6B62", hint: "\u8BF7\u624B\u52A8\u8FD0\u884C: sudo pkill -9 mihomo" });
|
|
5573
5598
|
}
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5599
|
+
}
|
|
5600
|
+
async function cmdStop() {
|
|
5601
|
+
if (isDaemonEnabled()) {
|
|
5602
|
+
console.log(colors.yellow("\u4FDD\u6D3B\u5DF2\u542F\u7528\uFF0C\u4EE3\u7406\u7531 launchd \u6258\u7BA1"));
|
|
5603
|
+
console.log("\u76F4\u63A5\u505C\u6B62\u4F1A\u88AB\u81EA\u52A8\u91CD\u65B0\u62C9\u8D77\uFF0C\u8BF7\u7528: mihomo daemon off");
|
|
5604
|
+
return;
|
|
5577
5605
|
}
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5606
|
+
const pids = getMihomoPids();
|
|
5607
|
+
if (pids.length === 0) {
|
|
5608
|
+
console.log(colors.yellow("\u4E0D\u5728\u8FD0\u884C"));
|
|
5609
|
+
return;
|
|
5610
|
+
}
|
|
5611
|
+
console.log(`\u505C\u6B62 ${pids.length} \u4E2A\u8FDB\u7A0B...`);
|
|
5612
|
+
handleStopResult(stop());
|
|
5613
|
+
console.log(colors.green("\u5DF2\u505C\u6B62\u8FDB\u7A0B"));
|
|
5614
|
+
}
|
|
5615
|
+
|
|
5616
|
+
// src/commands/start.ts
|
|
5617
|
+
async function cmdStart(args) {
|
|
5618
|
+
if (!hasKernel()) {
|
|
5619
|
+
throw new CliError('\u672A\u627E\u5230\u5185\u6838\uFF0C\u8BF7\u8FD0\u884C "mihomo kernel"');
|
|
5620
|
+
}
|
|
5621
|
+
const targetMode = args[1] === "tun" ? "tun" : "mixed";
|
|
5622
|
+
const daemonEnabled = isDaemonEnabled();
|
|
5623
|
+
if (targetMode === "tun" && daemonEnabled) {
|
|
5624
|
+
throw new CliError("\u4FDD\u6D3B\u5DF2\u542F\u7528\uFF08\u4EC5\u652F\u6301 Mixed \u6A21\u5F0F\uFF09\uFF0C\u65E0\u6CD5\u542F\u52A8 TUN", { hint: "\u8BF7\u5148\u5173\u95ED\u4FDD\u6D3B: mihomo daemon off" });
|
|
5625
|
+
}
|
|
5626
|
+
const rounds = parseIntArg(args, "-r", "--rounds", DEFAULT_CLEAN_ROUNDS);
|
|
5627
|
+
const timeout = parseIntArg(args, "-t", "--timeout", DEFAULT_TEST_TIMEOUT);
|
|
5628
|
+
const concurrency = parseIntArg(args, "-j", "--concurrency", DEFAULT_TEST_CONCURRENCY);
|
|
5629
|
+
const skipUpdate = hasFlag(args, "-s", "--no-update");
|
|
5630
|
+
const skipClean = hasFlag(args, "--no-clean");
|
|
5631
|
+
const updateTimeout = parseIntArg(args, "-u", "--update-timeout", DEFAULT_AUTO_UPDATE_TIMEOUT);
|
|
5632
|
+
const sub = requireActiveSubscription("\u6CA1\u6709\u8BA2\u9605\uFF0C\u8BF7\u5148\u6DFB\u52A0\u8BA2\u9605");
|
|
5633
|
+
if (!skipUpdate) {
|
|
5634
|
+
await autoUpdateStaleSubscription({ timeout: updateTimeout });
|
|
5635
|
+
}
|
|
5636
|
+
if (!daemonEnabled) {
|
|
5637
|
+
if (hasRootResidue()) {
|
|
5638
|
+
throw new CliError("\u5B58\u5728\u9700\u8981 root \u6743\u9650\u6E05\u7406\u7684\u6B8B\u7559\u8FDB\u7A0B/\u6587\u4EF6", {
|
|
5639
|
+
hint: [`\u8BF7\u5148\u624B\u52A8\u6E05\u7406: sudo pkill -9 mihomo && sudo rm -f ${PATHS.pidFile}`, "\u6216\u5207\u6362\u5230 TUN \u6A21\u5F0F\u542F\u52A8\uFF08\u81EA\u52A8\u6E05\u7406\uFF09: mihomo start tun"]
|
|
5640
|
+
});
|
|
5641
|
+
}
|
|
5642
|
+
const status = getStatus();
|
|
5643
|
+
const hasProcess = status.running || status.allProcesses.length > 0;
|
|
5644
|
+
if (hasProcess) {
|
|
5645
|
+
const count = status.allProcesses.length > 0 ? status.allProcesses.length : 1;
|
|
5646
|
+
console.log(`\u505C\u6B62 ${count} \u4E2A\u8FDB\u7A0B...`);
|
|
5647
|
+
}
|
|
5648
|
+
handleStopResult(stop());
|
|
5649
|
+
if (hasProcess) {
|
|
5650
|
+
console.log(`${colors.green("\u5DF2\u505C\u6B62\u8FDB\u7A0B")}
|
|
5651
|
+
`);
|
|
5652
|
+
}
|
|
5653
|
+
}
|
|
5654
|
+
let configInfo;
|
|
5655
|
+
try {
|
|
5656
|
+
configInfo = prepareConfigForStart(targetMode, sub.name);
|
|
5657
|
+
} catch (e) {
|
|
5658
|
+
if (e instanceof CliError) throw e;
|
|
5659
|
+
throw new CliError(e.message, { label: "\u914D\u7F6E\u9519\u8BEF" });
|
|
5660
|
+
}
|
|
5661
|
+
const modeLabel = targetMode === "tun" ? "TUN" : "Mixed";
|
|
5662
|
+
console.log([colors.cyan(modeLabel), sub.name, formatProxySummary(configInfo)].join(" \xB7 "));
|
|
5663
|
+
try {
|
|
5664
|
+
const pid = await launchOrRestart(targetMode);
|
|
5665
|
+
const label = daemonEnabled ? "\u5DF2\u542F\u52A8 (\u4FDD\u6D3B)" : "\u5DF2\u542F\u52A8";
|
|
5666
|
+
console.log(`${colors.green(label)}${pid ? ` (PID ${pid})` : ""}`);
|
|
5667
|
+
} catch (e) {
|
|
5668
|
+
if (e instanceof CliError) throw e;
|
|
5669
|
+
const lines = e.message.split("\n");
|
|
5670
|
+
throw new CliError(lines[0], { label: "\u542F\u52A8\u5931\u8D25", hint: lines.slice(1) });
|
|
5671
|
+
}
|
|
5672
|
+
const cleanThreshold = isGithubUrl(sub.url) ? AUTO_CLEAN_THRESHOLD_GITHUB : AUTO_CLEAN_THRESHOLD;
|
|
5673
|
+
if (!skipClean && configInfo.proxies > cleanThreshold) {
|
|
5674
|
+
const cache = readSubscriptionCache();
|
|
5675
|
+
const lastCleanAt = cache[sub.name]?.last_auto_clean_at;
|
|
5676
|
+
const withinCooldown = !!lastCleanAt && Date.now() - new Date(lastCleanAt).getTime() < AUTO_CLEAN_COOLDOWN_HOURS * 60 * 60 * 1e3;
|
|
5677
|
+
if (!withinCooldown) {
|
|
5678
|
+
console.log("");
|
|
5679
|
+
console.log(`\u8282\u70B9\u6570 ${configInfo.proxies} \u8D85\u8FC7 ${cleanThreshold}\uFF0C\u81EA\u52A8\u6E05\u7406\uFF08${AUTO_CLEAN_COOLDOWN_HOURS}h \u5185\u4EC5\u4E00\u6B21\uFF0C--no-clean \u8DF3\u8FC7\uFF09...`);
|
|
5680
|
+
console.log("");
|
|
5681
|
+
await sleep(1e3);
|
|
5682
|
+
const progress = createProgressPrinter(rounds);
|
|
5683
|
+
const cleanResult = await autoCleanSubscription(sub.name, {
|
|
5684
|
+
timeout,
|
|
5685
|
+
concurrency,
|
|
5686
|
+
rounds,
|
|
5687
|
+
onResult: progress.onResult,
|
|
5688
|
+
onRetryRound: progress.onRetryRound
|
|
5689
|
+
});
|
|
5690
|
+
progress.finish();
|
|
5691
|
+
console.log(formatTestSummary(cleanResult.summary));
|
|
5692
|
+
if (cleanResult.skipped) {
|
|
5693
|
+
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"));
|
|
5694
|
+
} else if (cleanResult.removedProxies > 0) {
|
|
5695
|
+
console.log(`${colors.green("\u5DF2\u6E05\u7406")}: ${formatCleanSummary(cleanResult)}`);
|
|
5696
|
+
console.log("");
|
|
5697
|
+
console.log("\u91CD\u65B0\u52A0\u8F7D\u914D\u7F6E...");
|
|
5698
|
+
if (!daemonEnabled) handleStopResult(stop());
|
|
5699
|
+
try {
|
|
5700
|
+
configInfo = prepareConfigForStart(targetMode, sub.name);
|
|
5701
|
+
const pid = await launchOrRestart(targetMode);
|
|
5702
|
+
console.log(`${colors.green("\u5DF2\u91CD\u542F")}${pid ? ` (PID ${pid})` : ""} \xB7 ${formatProxySummary(configInfo)}`);
|
|
5703
|
+
} catch (e) {
|
|
5704
|
+
if (e instanceof CliError) throw e;
|
|
5705
|
+
throw new CliError(e.message.split("\n")[0], { label: "\u91CD\u542F\u5931\u8D25" });
|
|
5706
|
+
}
|
|
5707
|
+
}
|
|
5708
|
+
saveSubscriptionCache(sub.name, { last_auto_clean_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
5709
|
+
}
|
|
5710
|
+
}
|
|
5711
|
+
printStatus();
|
|
5712
|
+
}
|
|
5713
|
+
|
|
5714
|
+
// src/commands/shared.ts
|
|
5715
|
+
async function dispatchSubcommand(args, table, options) {
|
|
5716
|
+
const action = args[1];
|
|
5717
|
+
if (action) {
|
|
5718
|
+
const cmd = table.find((c) => c.name === action || c.aliases?.includes(action));
|
|
5719
|
+
if (cmd) return cmd.handler(args);
|
|
5720
|
+
if (options.onUnknown) return options.onUnknown(action);
|
|
5721
|
+
}
|
|
5722
|
+
return options.fallback(args);
|
|
5723
|
+
}
|
|
5724
|
+
function requireRunning() {
|
|
5725
|
+
const state = getRunningState();
|
|
5726
|
+
if (!state.running) {
|
|
5727
|
+
const hint = state.daemon ? "mihomo daemon on" : "mihomo start";
|
|
5728
|
+
throw new CliError(`mihomo \u672A\u8FD0\u884C\uFF0C\u8BF7\u5148\u542F\u52A8 (${hint})`);
|
|
5729
|
+
}
|
|
5730
|
+
}
|
|
5731
|
+
async function restartToApply(args) {
|
|
5732
|
+
if (!isRestartNeededOnChange()) return false;
|
|
5733
|
+
const currentMode = getRuntimeMode();
|
|
5734
|
+
console.log("");
|
|
5735
|
+
await cmdStart(["start", currentMode, ...extractStartOptions(args)]);
|
|
5736
|
+
return true;
|
|
5737
|
+
}
|
|
5738
|
+
|
|
5739
|
+
// src/commands/daemon.ts
|
|
5740
|
+
function printDaemonStatus() {
|
|
5741
|
+
const status = getDaemonStatus();
|
|
5742
|
+
const stateText = status.enabled ? colors.green("\u5DF2\u542F\u7528") : colors.yellow("\u5DF2\u7981\u7528");
|
|
5743
|
+
console.log(`${colors.gray("\u4FDD\u6D3B: ")}${stateText}`);
|
|
5744
|
+
if (status.enabled) {
|
|
5745
|
+
const runText = isDaemonRunning(status) ? colors.green(`\u8FD0\u884C\u4E2D (PID ${status.pid})`) : colors.yellow("\u672A\u8FD0\u884C");
|
|
5746
|
+
console.log(`${colors.gray("\u5185\u6838: ")}${runText}`);
|
|
5747
|
+
}
|
|
5748
|
+
console.log("");
|
|
5749
|
+
if (status.enabled) {
|
|
5750
|
+
console.log("\u5173\u95ED\u4FDD\u6D3B: mihomo daemon off");
|
|
5751
|
+
} else {
|
|
5752
|
+
console.log("\u5F00\u542F\u4FDD\u6D3B: mihomo daemon on");
|
|
5753
|
+
console.log(colors.gray(" \u5F00\u673A\u81EA\u542F + \u5D29\u6E83\u81EA\u52A8\u91CD\u542F\uFF08\u4EC5 Mixed \u6A21\u5F0F\uFF09"));
|
|
5754
|
+
}
|
|
5755
|
+
console.log("");
|
|
5756
|
+
}
|
|
5757
|
+
async function daemonOn() {
|
|
5758
|
+
if (!hasKernel()) {
|
|
5759
|
+
throw new CliError('\u672A\u627E\u5230\u5185\u6838\uFF0C\u8BF7\u8FD0\u884C "mihomo kernel"');
|
|
5760
|
+
}
|
|
5761
|
+
const sub = requireActiveSubscription("\u6CA1\u6709\u8BA2\u9605\uFF0C\u8BF7\u5148\u6DFB\u52A0\u8BA2\u9605");
|
|
5762
|
+
let configInfo;
|
|
5763
|
+
try {
|
|
5764
|
+
configInfo = prepareConfigForStart("mixed", sub.name);
|
|
5765
|
+
} catch (e) {
|
|
5766
|
+
if (e instanceof CliError) throw e;
|
|
5767
|
+
throw new CliError(e.message, { label: "\u914D\u7F6E\u9519\u8BEF" });
|
|
5768
|
+
}
|
|
5769
|
+
console.log(colors.gray("\u5C06\u8BF7\u6C42\u7BA1\u7406\u5458\u6743\u9650\u4EE5\u5B89\u88C5\u7CFB\u7EDF\u7EA7\u4FDD\u6D3B\u670D\u52A1\uFF08LaunchDaemon\uFF09"));
|
|
5770
|
+
console.log(colors.gray("\u7CFB\u7EDF\u7EA7\u4FDD\u6D3B\u9700\u8981 root\uFF0C\u4EE5\u89E3\u51B3\u5C40\u57DF\u7F51\u8BBF\u95EE\u53D7\u9650\u95EE\u9898"));
|
|
5771
|
+
try {
|
|
5772
|
+
enableDaemon();
|
|
5773
|
+
} catch (e) {
|
|
5774
|
+
if (e instanceof CliError) throw e;
|
|
5775
|
+
throw new CliError(e.message, { label: "\u542F\u7528\u4FDD\u6D3B\u5931\u8D25" });
|
|
5776
|
+
}
|
|
5777
|
+
console.log(`${colors.green("\u5DF2\u542F\u7528\u4FDD\u6D3B")} \xB7 ${sub.name} \xB7 ${formatProxySummary(configInfo)}`);
|
|
5778
|
+
console.log(colors.gray("\u5F00\u673A\u81EA\u542F + \u5D29\u6E83\u81EA\u52A8\u91CD\u542F\uFF0C\u4EE3\u7406\u5C06\u5728\u540E\u53F0\u5E38\u9A7B"));
|
|
5779
|
+
console.log("");
|
|
5780
|
+
await sleep(DAEMON_BOOT_WAIT_MS);
|
|
5781
|
+
printDaemonStatus();
|
|
5782
|
+
}
|
|
5783
|
+
function daemonOff() {
|
|
5784
|
+
if (!isDaemonEnabled()) {
|
|
5785
|
+
console.log("\u4FDD\u6D3B\u5DF2\u662F\u5173\u95ED\u72B6\u6001");
|
|
5786
|
+
console.log("");
|
|
5787
|
+
printDaemonStatus();
|
|
5788
|
+
return;
|
|
5789
|
+
}
|
|
5790
|
+
console.log(colors.gray("\u5C06\u8BF7\u6C42\u7BA1\u7406\u5458\u6743\u9650\u4EE5\u79FB\u9664\u7CFB\u7EDF\u7EA7\u4FDD\u6D3B\u670D\u52A1"));
|
|
5791
|
+
try {
|
|
5792
|
+
disableDaemon();
|
|
5793
|
+
} catch (e) {
|
|
5794
|
+
if (e instanceof CliError) throw e;
|
|
5795
|
+
throw new CliError(e.message, { label: "\u5173\u95ED\u4FDD\u6D3B\u5931\u8D25" });
|
|
5796
|
+
}
|
|
5797
|
+
console.log(`${colors.green("\u5DF2\u5173\u95ED\u4FDD\u6D3B")}\uFF0C\u4EE3\u7406\u5DF2\u505C\u6B62`);
|
|
5798
|
+
console.log(colors.gray("\u91CD\u65B0\u542F\u7528: mihomo daemon on"));
|
|
5799
|
+
console.log("");
|
|
5800
|
+
}
|
|
5801
|
+
function printStatusView() {
|
|
5802
|
+
console.log("");
|
|
5803
|
+
printDaemonStatus();
|
|
5804
|
+
}
|
|
5805
|
+
var SUBCOMMANDS = [
|
|
5806
|
+
{ name: "on", aliases: ["enable"], handler: daemonOn },
|
|
5807
|
+
{ name: "off", aliases: ["disable"], handler: daemonOff },
|
|
5808
|
+
{ name: "status", handler: printStatusView }
|
|
5809
|
+
];
|
|
5810
|
+
async function cmdDaemon(args) {
|
|
5811
|
+
await dispatchSubcommand(args, SUBCOMMANDS, {
|
|
5812
|
+
// 无 action → 显示状态;未知 action → 报错
|
|
5813
|
+
fallback: printStatusView,
|
|
5814
|
+
onUnknown: (action) => {
|
|
5815
|
+
throw new CliError(`\u672A\u77E5\u7684 daemon \u5B50\u547D\u4EE4: ${action}`, { hint: ["", "\u53EF\u7528\u5B50\u547D\u4EE4: on, off, status"] });
|
|
5816
|
+
}
|
|
5817
|
+
});
|
|
5818
|
+
}
|
|
5819
|
+
|
|
5820
|
+
// src/commands/directory.ts
|
|
5821
|
+
function openDirectory(args) {
|
|
5822
|
+
const target = args[2];
|
|
5823
|
+
if (!target || target === "root") {
|
|
5824
|
+
console.log("\u6B63\u5728\u6253\u5F00: \u6839\u76EE\u5F55");
|
|
5825
|
+
const success = openUrl(USER_DATA_DIR);
|
|
5826
|
+
if (!success) {
|
|
5827
|
+
console.log(`\u8BF7\u624B\u52A8\u6253\u5F00: ${USER_DATA_DIR}`);
|
|
5828
|
+
}
|
|
5829
|
+
return;
|
|
5830
|
+
}
|
|
5831
|
+
const key = target.toLowerCase();
|
|
5832
|
+
const targetInfo = Object.hasOwn(DIRECTORY_TARGETS, key) ? DIRECTORY_TARGETS[key] : void 0;
|
|
5833
|
+
if (targetInfo) {
|
|
5834
|
+
const targetPath = targetInfo.path || USER_DATA_DIR;
|
|
5835
|
+
console.log(`\u6B63\u5728\u6253\u5F00: ${targetInfo.label}`);
|
|
5836
|
+
const success = openUrl(targetPath);
|
|
5837
|
+
if (!success) {
|
|
5838
|
+
console.log(`\u8BF7\u624B\u52A8\u6253\u5F00: ${targetPath}`);
|
|
5839
|
+
}
|
|
5840
|
+
return;
|
|
5841
|
+
}
|
|
5842
|
+
const hint = ["", "\u53EF\u7528\u76EE\u6807:", " root (\u9ED8\u8BA4) \u6839\u76EE\u5F55"];
|
|
5843
|
+
for (const [k, val] of Object.entries(DIRECTORY_TARGETS)) {
|
|
5844
|
+
if (k !== "root") {
|
|
5845
|
+
hint.push(` ${k.padEnd(14)}${val.label}`);
|
|
5846
|
+
}
|
|
5847
|
+
}
|
|
5848
|
+
throw new CliError(`\u672A\u77E5\u7684\u76EE\u5F55\u76EE\u6807 "${target}"`, { hint });
|
|
5849
|
+
}
|
|
5850
|
+
function printDirectoryInfo() {
|
|
5851
|
+
console.log("");
|
|
5852
|
+
console.log("\u6570\u636E\u76EE\u5F55\u4F4D\u7F6E:");
|
|
5853
|
+
console.log(` \u6839\u76EE\u5F55: ${USER_DATA_DIR}`);
|
|
5854
|
+
console.log(` \u5168\u5C40\u8BBE\u7F6E: ${PATHS.settingsFile}`);
|
|
5855
|
+
console.log(` \u5185\u6838\u76EE\u5F55: ${DIRS.kernel}`);
|
|
5856
|
+
console.log(` \u5185\u6838\u6587\u4EF6: ${PATHS.mihomoBinary}`);
|
|
5857
|
+
console.log(` \u8BA2\u9605\u76EE\u5F55: ${DIRS.subscriptions}`);
|
|
5858
|
+
console.log(" - cache.json (\u8BA2\u9605\u7F13\u5B58\uFF1A\u66F4\u65B0\u65F6\u95F4\u3001\u6D41\u91CF\u7B49)");
|
|
5859
|
+
console.log(" - xxx.yaml (\u8BA2\u9605\u539F\u59CB\u914D\u7F6E)");
|
|
5860
|
+
console.log(` \u8FD0\u884C\u65F6\u76EE\u5F55: ${DIRS.runtime}`);
|
|
5861
|
+
console.log(" - config.yaml (\u542F\u52A8\u65F6\u751F\u6210\uFF0Cstop \u81EA\u52A8\u6E05\u9664)");
|
|
5862
|
+
console.log(" - pid (PID \u6587\u4EF6\uFF0Cstop \u81EA\u52A8\u6E05\u9664)");
|
|
5863
|
+
console.log(` \u65E5\u5FD7\u6587\u4EF6: ${PATHS.logFile}`);
|
|
5864
|
+
console.log(` mihomo \u6570\u636E: ${DIRS.data}`);
|
|
5865
|
+
console.log(" - cache.db, Geo*.dat \u7B49 (mihomo \u81EA\u884C\u7BA1\u7406)");
|
|
5866
|
+
console.log("");
|
|
5867
|
+
console.log("\u6253\u5F00\u76EE\u5F55:");
|
|
5868
|
+
console.log(" mihomo dir open \u6253\u5F00\u6839\u76EE\u5F55");
|
|
5869
|
+
console.log(" mihomo dir open subs \u6253\u5F00\u8BA2\u9605\u76EE\u5F55");
|
|
5870
|
+
console.log(" mihomo dir open logs \u6253\u5F00\u65E5\u5FD7\u76EE\u5F55");
|
|
5871
|
+
console.log(" mihomo dir open data \u6253\u5F00 mihomo \u6570\u636E\u76EE\u5F55");
|
|
5872
|
+
console.log(" mihomo dir open runtime \u6253\u5F00\u8FD0\u884C\u65F6\u76EE\u5F55");
|
|
5873
|
+
console.log(" mihomo dir open kernel \u6253\u5F00\u5185\u6838\u76EE\u5F55");
|
|
5874
|
+
console.log("");
|
|
5875
|
+
console.log("\u73AF\u5883\u53D8\u91CF:");
|
|
5876
|
+
console.log(" MIHOMO_CLI_DIR: \u81EA\u5B9A\u4E49\u6839\u76EE\u5F55\u4F4D\u7F6E");
|
|
5877
|
+
console.log("");
|
|
5878
|
+
}
|
|
5879
|
+
var SUBCOMMANDS2 = [{ name: "open", handler: openDirectory }];
|
|
5880
|
+
function cmdDirectory(args) {
|
|
5881
|
+
void dispatchSubcommand(args, SUBCOMMANDS2, { fallback: printDirectoryInfo });
|
|
5882
|
+
}
|
|
5883
|
+
|
|
5884
|
+
// src/kernel.ts
|
|
5885
|
+
import { spawnSync as spawnSync5 } from "child_process";
|
|
5886
|
+
import fs7 from "fs";
|
|
5887
|
+
import path6 from "path";
|
|
5888
|
+
|
|
5889
|
+
// node_modules/compare-versions/lib/esm/utils.js
|
|
5890
|
+
var semver = /^[v^~<>=]*?(\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+))?(?:-([\da-z\-]+(?:\.[\da-z\-]+)*))?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?)?)?$/i;
|
|
5891
|
+
var validateAndParse = (version) => {
|
|
5892
|
+
if (typeof version !== "string") {
|
|
5893
|
+
throw new TypeError("Invalid argument expected string");
|
|
5894
|
+
}
|
|
5895
|
+
const match = version.match(semver);
|
|
5896
|
+
if (!match) {
|
|
5897
|
+
throw new Error(`Invalid argument not valid semver ('${version}' received)`);
|
|
5898
|
+
}
|
|
5899
|
+
match.shift();
|
|
5900
|
+
return match;
|
|
5901
|
+
};
|
|
5902
|
+
var isWildcard = (s) => s === "*" || s === "x" || s === "X";
|
|
5903
|
+
var tryParse = (v) => {
|
|
5904
|
+
const n = parseInt(v, 10);
|
|
5905
|
+
return isNaN(n) ? v : n;
|
|
5585
5906
|
};
|
|
5586
5907
|
var forceType = (a, b) => typeof a !== typeof b ? [String(a), String(b)] : [a, b];
|
|
5587
5908
|
var compareStrings = (a, b) => {
|
|
@@ -5852,25 +6173,25 @@ async function cmdKernel(args) {
|
|
|
5852
6173
|
\u5DF2\u66F4\u65B0\u5230 ${result.version}`);
|
|
5853
6174
|
}
|
|
5854
6175
|
} catch (e) {
|
|
5855
|
-
|
|
5856
|
-
\u66F4\u65B0\u5931\u8D25: ${e.message}`);
|
|
6176
|
+
if (e instanceof CliError) throw e;
|
|
5857
6177
|
const err = e;
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
}
|
|
6178
|
+
const hint = [];
|
|
6179
|
+
if (err.response?.data?.message) {
|
|
6180
|
+
hint.push(`\u539F\u56E0: ${err.response.data.message}`);
|
|
6181
|
+
}
|
|
6182
|
+
if (err.response?.data?.documentation_url) {
|
|
6183
|
+
hint.push(`\u6587\u6863: ${err.response.data.documentation_url}`);
|
|
5865
6184
|
}
|
|
5866
6185
|
if (!effectiveMirror) {
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
6186
|
+
hint.push(
|
|
6187
|
+
"",
|
|
6188
|
+
"\u63D0\u793A: \u76F4\u8FDE\u5931\u8D25\u6216\u4E0B\u8F7D\u8FC7\u6162\u65F6\u53EF\u4F7F\u7528\u955C\u50CF:",
|
|
6189
|
+
" mihomo kernel --mirror [\u955C\u50CF] # \u4E0B\u8F7D\u8D70\u955C\u50CF\uFF08\u9ED8\u8BA4 v6.gh-proxy.org\uFF09",
|
|
6190
|
+
" mihomo kernel --mirror-all [\u955C\u50CF] # API \u548C\u4E0B\u8F7D\u90FD\u8D70\u955C\u50CF",
|
|
6191
|
+
` \u53EF\u7528\u955C\u50CF: ${AVAILABLE_MIRRORS.join(", ")}`
|
|
6192
|
+
);
|
|
5872
6193
|
}
|
|
5873
|
-
|
|
6194
|
+
throw new CliError(err.message, { label: "\u66F4\u65B0\u5931\u8D25", hint });
|
|
5874
6195
|
}
|
|
5875
6196
|
}
|
|
5876
6197
|
|
|
@@ -5897,9 +6218,7 @@ function cmdLogs(args) {
|
|
|
5897
6218
|
const archiveLogs = listLogs();
|
|
5898
6219
|
const archive = archiveLogs.archives[parsedIdx - 1];
|
|
5899
6220
|
if (!archive) {
|
|
5900
|
-
|
|
5901
|
-
console.log('\u4F7F\u7528 "mihomo logs" \u67E5\u770B\u53EF\u7528\u65E5\u5FD7\u5217\u8868');
|
|
5902
|
-
process.exit(1);
|
|
6221
|
+
throw new CliError(`\u672A\u627E\u5230\u65E5\u5FD7 "${targetName}"`, { hint: '\u4F7F\u7528 "mihomo logs" \u67E5\u770B\u53EF\u7528\u65E5\u5FD7\u5217\u8868' });
|
|
5903
6222
|
}
|
|
5904
6223
|
logPath = archive.path;
|
|
5905
6224
|
} else {
|
|
@@ -5907,350 +6226,55 @@ function cmdLogs(args) {
|
|
|
5907
6226
|
}
|
|
5908
6227
|
}
|
|
5909
6228
|
if (!logPath) {
|
|
5910
|
-
|
|
5911
|
-
console.log('\u4F7F\u7528 "mihomo logs" \u67E5\u770B\u53EF\u7528\u65E5\u5FD7\u5217\u8868');
|
|
5912
|
-
process.exit(1);
|
|
6229
|
+
throw new CliError(`\u672A\u627E\u5230\u65E5\u5FD7 "${targetName}"`, { hint: '\u4F7F\u7528 "mihomo logs" \u67E5\u770B\u53EF\u7528\u65E5\u5FD7\u5217\u8868' });
|
|
5913
6230
|
}
|
|
5914
6231
|
if (openInViewer) {
|
|
5915
6232
|
openLogFile(logPath);
|
|
5916
6233
|
return;
|
|
5917
6234
|
}
|
|
5918
|
-
viewLogWithTail(logPath, { follow: false, lines });
|
|
5919
|
-
return;
|
|
5920
|
-
}
|
|
5921
|
-
const logs = listLogs();
|
|
5922
|
-
const all = [];
|
|
5923
|
-
if (logs.current) all.push(logs.current);
|
|
5924
|
-
all.push(...logs.archives);
|
|
5925
|
-
if (all.length === 0) {
|
|
5926
|
-
console.log("\u6682\u65E0\u65E5\u5FD7");
|
|
5927
|
-
return;
|
|
5928
|
-
}
|
|
5929
|
-
console.log("");
|
|
5930
|
-
console.log("\u65E5\u5FD7\u5217\u8868:");
|
|
5931
|
-
console.log("");
|
|
5932
|
-
let archiveCounter = 0;
|
|
5933
|
-
for (const log of all) {
|
|
5934
|
-
let num;
|
|
5935
|
-
if (log.isCurrent) {
|
|
5936
|
-
num = " 0";
|
|
5937
|
-
} else {
|
|
5938
|
-
archiveCounter++;
|
|
5939
|
-
num = archiveCounter < 10 ? ` ${archiveCounter}` : `${archiveCounter}`;
|
|
5940
|
-
}
|
|
5941
|
-
const time = formatDate(log.mtime);
|
|
5942
|
-
const size = formatBytes(log.size);
|
|
5943
|
-
const name = log.isCurrent ? "mihomo.log (\u5F53\u524D\u8FD0\u884C\u4E2D)" : log.name;
|
|
5944
|
-
console.log(` ${num}. ${name}`);
|
|
5945
|
-
console.log(` \u65F6\u95F4: ${time} \u5927\u5C0F: ${size}`);
|
|
5946
|
-
if (!log.isCurrent) {
|
|
5947
|
-
console.log(` \u67E5\u770B: mihomo logs ${archiveCounter} \u6216 mihomo logs ${archiveCounter} -o`);
|
|
5948
|
-
}
|
|
5949
|
-
console.log("");
|
|
5950
|
-
}
|
|
5951
|
-
console.log("\u7528\u6CD5:");
|
|
5952
|
-
console.log(" mihomo logs 0 # \u67E5\u770B\u5F53\u524D\u65E5\u5FD7 (\u6700\u540E 100 \u884C)");
|
|
5953
|
-
console.log(" mihomo logs 1 # \u67E5\u770B\u7B2C 1 \u4E2A\u5F52\u6863\u65E5\u5FD7\uFF08\u6700\u65B0\uFF09");
|
|
5954
|
-
console.log(" mihomo logs 1 -n 200 # \u67E5\u770B 200 \u884C");
|
|
5955
|
-
console.log(" mihomo logs 1 -o # \u7528\u7CFB\u7EDF\u9ED8\u8BA4\u7A0B\u5E8F\u6253\u5F00");
|
|
5956
|
-
console.log("");
|
|
5957
|
-
}
|
|
5958
|
-
|
|
5959
|
-
// src/commands/overwrite.ts
|
|
5960
|
-
import path7 from "path";
|
|
5961
|
-
|
|
5962
|
-
// src/runtime.ts
|
|
5963
|
-
function getRuntimeMode() {
|
|
5964
|
-
if (isDaemonEnabled()) return "mixed";
|
|
5965
|
-
return getConfigInfo()?.tun ? "tun" : "mixed";
|
|
5966
|
-
}
|
|
5967
|
-
function getRunningState() {
|
|
5968
|
-
if (isDaemonEnabled()) {
|
|
5969
|
-
const daemon = getDaemonStatus();
|
|
5970
|
-
return { running: isDaemonRunning(daemon), pid: daemon.pid, daemon: true };
|
|
5971
|
-
}
|
|
5972
|
-
const status = getStatus();
|
|
5973
|
-
return { running: status.running, pid: status.pid, daemon: false };
|
|
5974
|
-
}
|
|
5975
|
-
function isRestartNeededOnChange() {
|
|
5976
|
-
return isDaemonEnabled() || getStatus().running;
|
|
5977
|
-
}
|
|
5978
|
-
async function launchOrRestart(mode) {
|
|
5979
|
-
if (isDaemonEnabled()) {
|
|
5980
|
-
await restartDaemon();
|
|
5981
|
-
await sleep(DAEMON_BOOT_WAIT_MS);
|
|
5982
|
-
return getDaemonStatus().pid;
|
|
5983
|
-
}
|
|
5984
|
-
const result = await start(mode);
|
|
5985
|
-
return result.pid;
|
|
5986
|
-
}
|
|
5987
|
-
|
|
5988
|
-
// src/progress.ts
|
|
5989
|
-
var IS_TTY = process.stdout.isTTY === true;
|
|
5990
|
-
var BAR_WIDTH = 20;
|
|
5991
|
-
function createProgressPrinter(totalRounds = 1) {
|
|
5992
|
-
let alive = 0;
|
|
5993
|
-
let dead = 0;
|
|
5994
|
-
const resultMap = /* @__PURE__ */ new Map();
|
|
5995
|
-
function render(done, total) {
|
|
5996
|
-
if (!IS_TTY) return;
|
|
5997
|
-
const pct = Math.round(done / total * 100);
|
|
5998
|
-
const filled = Math.round(done / total * BAR_WIDTH);
|
|
5999
|
-
const bar = "\u2588".repeat(filled) + "\u2591".repeat(BAR_WIDTH - filled);
|
|
6000
|
-
process.stdout.write(`\r${bar} ${done}/${total} (${pct}%) | ${colors.green(`\u2713${alive}`)} ${colors.red(`\u2717${dead}`)}`);
|
|
6001
|
-
}
|
|
6002
|
-
return {
|
|
6003
|
-
onResult(result, index, total, round = 1) {
|
|
6004
|
-
if (resultMap.size === 0 && totalRounds > 1) {
|
|
6005
|
-
console.log(`--- \u7B2C 1 \u8F6E\u6D4B\u8BD5 (${total} \u4E2A\u8282\u70B9) ---`);
|
|
6006
|
-
}
|
|
6007
|
-
const prev = resultMap.get(result.name);
|
|
6008
|
-
if (prev) {
|
|
6009
|
-
if (prev.result.delay !== null) alive--;
|
|
6010
|
-
else dead--;
|
|
6011
|
-
}
|
|
6012
|
-
if (result.delay !== null) alive++;
|
|
6013
|
-
else dead++;
|
|
6014
|
-
resultMap.set(result.name, { result, round });
|
|
6015
|
-
render(index + 1, total);
|
|
6016
|
-
},
|
|
6017
|
-
onRetryRound(round, count) {
|
|
6018
|
-
if (IS_TTY) {
|
|
6019
|
-
process.stdout.write("\n");
|
|
6020
|
-
}
|
|
6021
|
-
console.log(`--- \u7B2C ${round} \u8F6E\u91CD\u8BD5 (${count} \u4E2A\u8282\u70B9) ---`);
|
|
6022
|
-
alive = 0;
|
|
6023
|
-
dead = 0;
|
|
6024
|
-
},
|
|
6025
|
-
finish() {
|
|
6026
|
-
if (IS_TTY) {
|
|
6027
|
-
process.stdout.write("\n");
|
|
6028
|
-
}
|
|
6029
|
-
console.log("");
|
|
6030
|
-
if (!IS_TTY) return;
|
|
6031
|
-
const entries = [...resultMap.values()];
|
|
6032
|
-
entries.sort((a, b) => a.result.name.localeCompare(b.result.name));
|
|
6033
|
-
const total = entries.length;
|
|
6034
|
-
console.log("\u8282\u70B9\u6700\u7EC8\u72B6\u6001:");
|
|
6035
|
-
for (let i = 0; i < entries.length; i++) {
|
|
6036
|
-
const { result, round } = entries[i];
|
|
6037
|
-
const prefix = `[${i + 1}/${total}]`;
|
|
6038
|
-
if (result.delay !== null) {
|
|
6039
|
-
const delayColor = result.delay < 300 ? colors.green : result.delay < 800 ? colors.yellow : colors.red;
|
|
6040
|
-
const retryNote = round > 1 ? colors.gray(` (\u7B2C${round}\u8F6E\u901A\u8FC7)`) : "";
|
|
6041
|
-
console.log(`${prefix} ${colors.green("\u2713")} ${result.name} ${delayColor(`${result.delay}ms`)}${retryNote}`);
|
|
6042
|
-
} else {
|
|
6043
|
-
console.log(`${prefix} ${colors.red("\u2717")} ${result.name} ${colors.gray(result.error || "timeout")}`);
|
|
6044
|
-
}
|
|
6045
|
-
}
|
|
6046
|
-
console.log("");
|
|
6047
|
-
}
|
|
6048
|
-
};
|
|
6049
|
-
}
|
|
6050
|
-
function formatCleanSummary(result) {
|
|
6051
|
-
const parts = [`\u79FB\u9664 ${result.removedProxies} \u4E2A\u8282\u70B9`];
|
|
6052
|
-
if (result.removedGroups > 0) parts.push(`\u5220\u9664 ${result.removedGroups} \u4E2A\u7A7A\u5206\u7EC4`);
|
|
6053
|
-
if (result.updatedGroups > 0) parts.push(`\u66F4\u65B0 ${result.updatedGroups} \u4E2A\u5206\u7EC4`);
|
|
6054
|
-
return parts.join(", ");
|
|
6055
|
-
}
|
|
6056
|
-
function formatTestSummary(summary) {
|
|
6057
|
-
return `\u7ED3\u679C: ${colors.green(`${summary.alive} \u5B58\u6D3B`)} / ${colors.red(`${summary.dead} \u5931\u8D25`)} / ${summary.total} \u603B\u8BA1`;
|
|
6058
|
-
}
|
|
6059
|
-
|
|
6060
|
-
// src/commands/status.ts
|
|
6061
|
-
function printStatus() {
|
|
6062
|
-
const status = getStatus();
|
|
6063
|
-
const state = getRunningState();
|
|
6064
|
-
const info = getConfigInfo();
|
|
6065
|
-
const overwriteEnabled = isOverwriteEnabled();
|
|
6066
|
-
const overwriteFiles = listOverwriteFile().files;
|
|
6067
|
-
const activeSub = getActiveSubscription();
|
|
6068
|
-
const { running, pid, daemon: daemonManaged } = state;
|
|
6069
|
-
console.log("");
|
|
6070
|
-
let modeLabel = "";
|
|
6071
|
-
if (info) {
|
|
6072
|
-
modeLabel = colors.cyan(info.tun ? " (TUN)" : " (Mixed)");
|
|
6073
|
-
}
|
|
6074
|
-
const statusText = running ? colors.green("\u25CF \u8FD0\u884C\u4E2D") : colors.yellow("\u4E0D\u5728\u8FD0\u884C");
|
|
6075
|
-
console.log(`${colors.gray("\u72B6\u6001: ")}${statusText}${modeLabel}`);
|
|
6076
|
-
console.log(`${colors.gray("\u5185\u6838: ")}${status.kernelVersion || "\u672A\u5B89\u88C5"}`);
|
|
6077
|
-
if (pid) {
|
|
6078
|
-
console.log(`${colors.gray("PID: ")}${pid}`);
|
|
6079
|
-
if (!daemonManaged && status.processInfo) {
|
|
6080
|
-
console.log(`${colors.gray("\u5185\u5B58: ")}${status.processInfo.memory}`);
|
|
6081
|
-
}
|
|
6082
|
-
}
|
|
6083
|
-
if (info) {
|
|
6084
|
-
if (info.tun) {
|
|
6085
|
-
const extra = info.mixedPort ? `\uFF0C\u53E6\u76D1\u542C ${info.mixedPort}` : "";
|
|
6086
|
-
console.log(`${colors.gray("\u7AEF\u53E3: ")}TUN \u63A5\u7BA1${extra}`);
|
|
6087
|
-
} else if (info.mixedPort) {
|
|
6088
|
-
console.log(`${colors.gray("\u7AEF\u53E3: ")}${info.mixedPort}`);
|
|
6089
|
-
} else {
|
|
6090
|
-
const ports = [];
|
|
6091
|
-
if (info.httpPort) ports.push(`HTTP:${info.httpPort}`);
|
|
6092
|
-
if (info.socksPort) ports.push(`SOCKS:${info.socksPort}`);
|
|
6093
|
-
console.log(`${colors.gray("\u7AEF\u53E3: ")}${ports.length > 0 ? ports.join(", ") : "\u672A\u77E5"}`);
|
|
6094
|
-
}
|
|
6095
|
-
}
|
|
6096
|
-
if (activeSub) {
|
|
6097
|
-
let subLine = `${colors.gray("\u8BA2\u9605: ")}${activeSub.name}`;
|
|
6098
|
-
if (info) {
|
|
6099
|
-
subLine += ` (${formatProxySummary(info)})`;
|
|
6100
|
-
}
|
|
6101
|
-
console.log(subLine);
|
|
6102
|
-
} else {
|
|
6103
|
-
console.log(`${colors.gray("\u8BA2\u9605: ")}\u672A\u914D\u7F6E`);
|
|
6104
|
-
}
|
|
6105
|
-
if (overwriteEnabled && overwriteFiles.length > 0) {
|
|
6106
|
-
const names = overwriteFiles.map((f) => f.name.replace(/^overwrite\.?/, "").replace(/\.ya?ml$/, "") || "\u4E3B\u6587\u4EF6").join(", ");
|
|
6107
|
-
console.log(`${colors.gray("\u8986\u5199: ")}${colors.green("\u5DF2\u542F\u7528")} (${names})`);
|
|
6108
|
-
} else if (overwriteEnabled) {
|
|
6109
|
-
console.log(`${colors.gray("\u8986\u5199: ")}${colors.green("\u5DF2\u542F\u7528")} (\u65E0\u6587\u4EF6)`);
|
|
6110
|
-
} else {
|
|
6111
|
-
console.log(`${colors.gray("\u8986\u5199: ")}${colors.yellow("\u5DF2\u7981\u7528")}`);
|
|
6112
|
-
}
|
|
6113
|
-
if (isDaemonEnabled()) {
|
|
6114
|
-
console.log(`${colors.gray("\u4FDD\u6D3B: ")}${colors.green("\u5DF2\u542F\u7528")} ${colors.gray("(\u5F00\u673A\u81EA\u542F + \u5D29\u6E83\u91CD\u542F)")}`);
|
|
6115
|
-
}
|
|
6116
|
-
console.log("");
|
|
6117
|
-
}
|
|
6118
|
-
|
|
6119
|
-
// src/commands/stop.ts
|
|
6120
|
-
function handleStopResult(result) {
|
|
6121
|
-
if (result.remaining && result.remaining.length > 0) {
|
|
6122
|
-
console.error(`${colors.red("\u90E8\u5206\u8FDB\u7A0B\u672A\u7EC8\u6B62:")} ${result.remaining.join(", ")}`);
|
|
6123
|
-
console.error("\u8BF7\u624B\u52A8\u8FD0\u884C: sudo pkill -9 mihomo");
|
|
6124
|
-
process.exit(1);
|
|
6125
|
-
}
|
|
6126
|
-
}
|
|
6127
|
-
async function cmdStop() {
|
|
6128
|
-
if (isDaemonEnabled()) {
|
|
6129
|
-
console.log(colors.yellow("\u4FDD\u6D3B\u5DF2\u542F\u7528\uFF0C\u4EE3\u7406\u7531 launchd \u6258\u7BA1"));
|
|
6130
|
-
console.log("\u76F4\u63A5\u505C\u6B62\u4F1A\u88AB\u81EA\u52A8\u91CD\u65B0\u62C9\u8D77\uFF0C\u8BF7\u7528: mihomo daemon off");
|
|
6131
|
-
return;
|
|
6132
|
-
}
|
|
6133
|
-
const pids = getMihomoPids();
|
|
6134
|
-
if (pids.length === 0) {
|
|
6135
|
-
console.log(colors.yellow("\u4E0D\u5728\u8FD0\u884C"));
|
|
6136
|
-
return;
|
|
6137
|
-
}
|
|
6138
|
-
console.log(`\u505C\u6B62 ${pids.length} \u4E2A\u8FDB\u7A0B...`);
|
|
6139
|
-
handleStopResult(stop());
|
|
6140
|
-
console.log(colors.green("\u5DF2\u505C\u6B62\u8FDB\u7A0B"));
|
|
6141
|
-
}
|
|
6142
|
-
|
|
6143
|
-
// src/commands/start.ts
|
|
6144
|
-
async function cmdStart(args) {
|
|
6145
|
-
if (!hasKernel()) {
|
|
6146
|
-
console.error('\u9519\u8BEF: \u672A\u627E\u5230\u5185\u6838\uFF0C\u8BF7\u8FD0\u884C "mihomo kernel"');
|
|
6147
|
-
process.exit(1);
|
|
6148
|
-
}
|
|
6149
|
-
const targetMode = args[1] === "tun" ? "tun" : "mixed";
|
|
6150
|
-
const daemonEnabled = isDaemonEnabled();
|
|
6151
|
-
if (targetMode === "tun" && daemonEnabled) {
|
|
6152
|
-
console.error(`${colors.red("\u9519\u8BEF:")} \u4FDD\u6D3B\u5DF2\u542F\u7528\uFF08\u4EC5\u652F\u6301 Mixed \u6A21\u5F0F\uFF09\uFF0C\u65E0\u6CD5\u542F\u52A8 TUN`);
|
|
6153
|
-
console.error("\u8BF7\u5148\u5173\u95ED\u4FDD\u6D3B: mihomo daemon off");
|
|
6154
|
-
process.exit(1);
|
|
6155
|
-
}
|
|
6156
|
-
const rounds = parseIntArg(args, "-r", "--rounds", DEFAULT_CLEAN_ROUNDS);
|
|
6157
|
-
const timeout = parseIntArg(args, "-t", "--timeout", DEFAULT_TEST_TIMEOUT);
|
|
6158
|
-
const concurrency = parseIntArg(args, "-j", "--concurrency", DEFAULT_TEST_CONCURRENCY);
|
|
6159
|
-
const skipUpdate = hasFlag(args, "-s", "--no-update");
|
|
6160
|
-
const skipClean = hasFlag(args, "--no-clean");
|
|
6161
|
-
const updateTimeout = parseIntArg(args, "-u", "--update-timeout", DEFAULT_AUTO_UPDATE_TIMEOUT);
|
|
6162
|
-
const sub = getActiveSubscription();
|
|
6163
|
-
if (!sub) {
|
|
6164
|
-
console.error("\u9519\u8BEF: \u6CA1\u6709\u8BA2\u9605\uFF0C\u8BF7\u5148\u6DFB\u52A0\u8BA2\u9605");
|
|
6165
|
-
process.exit(1);
|
|
6166
|
-
}
|
|
6167
|
-
if (!skipUpdate) {
|
|
6168
|
-
await autoUpdateStaleSubscription({ timeout: updateTimeout });
|
|
6169
|
-
}
|
|
6170
|
-
if (!daemonEnabled) {
|
|
6171
|
-
if (hasRootResidue()) {
|
|
6172
|
-
console.error(`${colors.red("\u9519\u8BEF:")} \u5B58\u5728\u9700\u8981 root \u6743\u9650\u6E05\u7406\u7684\u6B8B\u7559\u8FDB\u7A0B/\u6587\u4EF6`);
|
|
6173
|
-
console.error(`\u8BF7\u5148\u624B\u52A8\u6E05\u7406: sudo pkill -9 mihomo && sudo rm -f ${PATHS.pidFile}`);
|
|
6174
|
-
console.error("\u6216\u5207\u6362\u5230 TUN \u6A21\u5F0F\u542F\u52A8\uFF08\u81EA\u52A8\u6E05\u7406\uFF09: mihomo start tun");
|
|
6175
|
-
process.exit(1);
|
|
6176
|
-
}
|
|
6177
|
-
const status = getStatus();
|
|
6178
|
-
const hasProcess = status.running || status.allProcesses.length > 0;
|
|
6179
|
-
if (hasProcess) {
|
|
6180
|
-
const count = status.allProcesses.length > 0 ? status.allProcesses.length : 1;
|
|
6181
|
-
console.log(`\u505C\u6B62 ${count} \u4E2A\u8FDB\u7A0B...`);
|
|
6182
|
-
}
|
|
6183
|
-
handleStopResult(stop());
|
|
6184
|
-
if (hasProcess) {
|
|
6185
|
-
console.log(`${colors.green("\u5DF2\u505C\u6B62\u8FDB\u7A0B")}
|
|
6186
|
-
`);
|
|
6187
|
-
}
|
|
6235
|
+
viewLogWithTail(logPath, { follow: false, lines });
|
|
6236
|
+
return;
|
|
6188
6237
|
}
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
|
|
6238
|
+
const logs = listLogs();
|
|
6239
|
+
const all = [];
|
|
6240
|
+
if (logs.current) all.push(logs.current);
|
|
6241
|
+
all.push(...logs.archives);
|
|
6242
|
+
if (all.length === 0) {
|
|
6243
|
+
console.log("\u6682\u65E0\u65E5\u5FD7");
|
|
6244
|
+
return;
|
|
6195
6245
|
}
|
|
6196
|
-
|
|
6197
|
-
console.log(
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
for (const line of lines.slice(1)) console.error(line);
|
|
6246
|
+
console.log("");
|
|
6247
|
+
console.log("\u65E5\u5FD7\u5217\u8868:");
|
|
6248
|
+
console.log("");
|
|
6249
|
+
let archiveCounter = 0;
|
|
6250
|
+
for (const log of all) {
|
|
6251
|
+
let num;
|
|
6252
|
+
if (log.isCurrent) {
|
|
6253
|
+
num = " 0";
|
|
6254
|
+
} else {
|
|
6255
|
+
archiveCounter++;
|
|
6256
|
+
num = archiveCounter < 10 ? ` ${archiveCounter}` : `${archiveCounter}`;
|
|
6208
6257
|
}
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
if (!withinCooldown) {
|
|
6217
|
-
console.log("");
|
|
6218
|
-
console.log(`\u8282\u70B9\u6570 ${configInfo.proxies} \u8D85\u8FC7 ${cleanThreshold}\uFF0C\u81EA\u52A8\u6E05\u7406\uFF08${AUTO_CLEAN_COOLDOWN_HOURS}h \u5185\u4EC5\u4E00\u6B21\uFF0C--no-clean \u8DF3\u8FC7\uFF09...`);
|
|
6219
|
-
console.log("");
|
|
6220
|
-
await sleep(1e3);
|
|
6221
|
-
const progress = createProgressPrinter(rounds);
|
|
6222
|
-
const cleanResult = await autoCleanSubscription(sub.name, {
|
|
6223
|
-
timeout,
|
|
6224
|
-
concurrency,
|
|
6225
|
-
rounds,
|
|
6226
|
-
onResult: progress.onResult,
|
|
6227
|
-
onRetryRound: progress.onRetryRound
|
|
6228
|
-
});
|
|
6229
|
-
progress.finish();
|
|
6230
|
-
console.log(formatTestSummary(cleanResult.summary));
|
|
6231
|
-
if (cleanResult.skipped) {
|
|
6232
|
-
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"));
|
|
6233
|
-
} else if (cleanResult.removedProxies > 0) {
|
|
6234
|
-
console.log(`${colors.green("\u5DF2\u6E05\u7406")}: ${formatCleanSummary(cleanResult)}`);
|
|
6235
|
-
console.log("");
|
|
6236
|
-
console.log("\u91CD\u65B0\u52A0\u8F7D\u914D\u7F6E...");
|
|
6237
|
-
if (!daemonEnabled) handleStopResult(stop());
|
|
6238
|
-
try {
|
|
6239
|
-
configInfo = prepareConfigForStart(targetMode, sub.name);
|
|
6240
|
-
const pid = await launchOrRestart(targetMode);
|
|
6241
|
-
console.log(`${colors.green("\u5DF2\u91CD\u542F")}${pid ? ` (PID ${pid})` : ""} \xB7 ${formatProxySummary(configInfo)}`);
|
|
6242
|
-
} catch (e) {
|
|
6243
|
-
console.error(`${colors.red("\u91CD\u542F\u5931\u8D25:")} ${e.message.split("\n")[0]}`);
|
|
6244
|
-
process.exit(1);
|
|
6245
|
-
}
|
|
6246
|
-
}
|
|
6247
|
-
saveSubscriptionCache(sub.name, { last_auto_clean_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
6258
|
+
const time = formatDate(log.mtime);
|
|
6259
|
+
const size = formatBytes(log.size);
|
|
6260
|
+
const name = log.isCurrent ? "mihomo.log (\u5F53\u524D\u8FD0\u884C\u4E2D)" : log.name;
|
|
6261
|
+
console.log(` ${num}. ${name}`);
|
|
6262
|
+
console.log(` \u65F6\u95F4: ${time} \u5927\u5C0F: ${size}`);
|
|
6263
|
+
if (!log.isCurrent) {
|
|
6264
|
+
console.log(` \u67E5\u770B: mihomo logs ${archiveCounter} \u6216 mihomo logs ${archiveCounter} -o`);
|
|
6248
6265
|
}
|
|
6266
|
+
console.log("");
|
|
6249
6267
|
}
|
|
6250
|
-
|
|
6268
|
+
console.log("\u7528\u6CD5:");
|
|
6269
|
+
console.log(" mihomo logs 0 # \u67E5\u770B\u5F53\u524D\u65E5\u5FD7 (\u6700\u540E 100 \u884C)");
|
|
6270
|
+
console.log(" mihomo logs 1 # \u67E5\u770B\u7B2C 1 \u4E2A\u5F52\u6863\u65E5\u5FD7\uFF08\u6700\u65B0\uFF09");
|
|
6271
|
+
console.log(" mihomo logs 1 -n 200 # \u67E5\u770B 200 \u884C");
|
|
6272
|
+
console.log(" mihomo logs 1 -o # \u7528\u7CFB\u7EDF\u9ED8\u8BA4\u7A0B\u5E8F\u6253\u5F00");
|
|
6273
|
+
console.log("");
|
|
6251
6274
|
}
|
|
6252
6275
|
|
|
6253
6276
|
// src/commands/overwrite.ts
|
|
6277
|
+
import path7 from "path";
|
|
6254
6278
|
function printOverwriteList() {
|
|
6255
6279
|
const info = listOverwriteFile();
|
|
6256
6280
|
const statusText = info.enabled ? colors.green("\u5DF2\u542F\u7528") : colors.yellow("\u5DF2\u7981\u7528");
|
|
@@ -6282,48 +6306,30 @@ function printOverwriteList() {
|
|
|
6282
6306
|
console.log("\u7981\u7528\u8986\u5199: mihomo ow off");
|
|
6283
6307
|
console.log("");
|
|
6284
6308
|
}
|
|
6285
|
-
async function
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
const restartNeeded = isRestartNeededOnChange();
|
|
6289
|
-
if (action === "on" || action === "enable") {
|
|
6290
|
-
if (isOverwriteEnabled()) {
|
|
6291
|
-
console.log("\u8986\u5199\u914D\u7F6E\u5DF2\u662F\u542F\u7528\u72B6\u6001");
|
|
6292
|
-
console.log("");
|
|
6293
|
-
printOverwriteList();
|
|
6294
|
-
return;
|
|
6295
|
-
}
|
|
6296
|
-
setOverwriteEnabled(true);
|
|
6297
|
-
console.log("\u5DF2\u542F\u7528\u8986\u5199\u914D\u7F6E");
|
|
6298
|
-
if (restartNeeded) {
|
|
6299
|
-
console.log("");
|
|
6300
|
-
await cmdStart(["start", currentMode, ...extractStartOptions(args)]);
|
|
6301
|
-
return;
|
|
6302
|
-
}
|
|
6309
|
+
async function setOverwrite(enabled, args) {
|
|
6310
|
+
if (isOverwriteEnabled() === enabled) {
|
|
6311
|
+
console.log(`\u8986\u5199\u914D\u7F6E\u5DF2\u662F${enabled ? "\u542F\u7528" : "\u7981\u7528"}\u72B6\u6001`);
|
|
6303
6312
|
console.log("");
|
|
6304
6313
|
printOverwriteList();
|
|
6305
6314
|
return;
|
|
6306
6315
|
}
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6316
|
+
setOverwriteEnabled(enabled);
|
|
6317
|
+
console.log(`\u5DF2${enabled ? "\u542F\u7528" : "\u7981\u7528"}\u8986\u5199\u914D\u7F6E`);
|
|
6318
|
+
if (await restartToApply(args)) return;
|
|
6319
|
+
console.log("");
|
|
6320
|
+
printOverwriteList();
|
|
6321
|
+
}
|
|
6322
|
+
var SUBCOMMANDS3 = [
|
|
6323
|
+
{ name: "on", aliases: ["enable"], handler: (args) => setOverwrite(true, args) },
|
|
6324
|
+
{ name: "off", aliases: ["disable"], handler: (args) => setOverwrite(false, args) }
|
|
6325
|
+
];
|
|
6326
|
+
async function cmdOverwrite(args) {
|
|
6327
|
+
await dispatchSubcommand(args, SUBCOMMANDS3, {
|
|
6328
|
+
fallback: () => {
|
|
6310
6329
|
console.log("");
|
|
6311
6330
|
printOverwriteList();
|
|
6312
|
-
return;
|
|
6313
6331
|
}
|
|
6314
|
-
|
|
6315
|
-
console.log("\u5DF2\u7981\u7528\u8986\u5199\u914D\u7F6E");
|
|
6316
|
-
if (restartNeeded) {
|
|
6317
|
-
console.log("");
|
|
6318
|
-
await cmdStart(["start", currentMode, ...extractStartOptions(args)]);
|
|
6319
|
-
return;
|
|
6320
|
-
}
|
|
6321
|
-
console.log("");
|
|
6322
|
-
printOverwriteList();
|
|
6323
|
-
return;
|
|
6324
|
-
}
|
|
6325
|
-
console.log("");
|
|
6326
|
-
printOverwriteList();
|
|
6332
|
+
});
|
|
6327
6333
|
}
|
|
6328
6334
|
|
|
6329
6335
|
// src/commands/reset.ts
|
|
@@ -6433,10 +6439,7 @@ async function cmdReset(args) {
|
|
|
6433
6439
|
const KNOWN_FLAGS = /* @__PURE__ */ new Set(["--full", "--yes", "-y"]);
|
|
6434
6440
|
const unknownFlags = flags.filter((f) => !KNOWN_FLAGS.has(f));
|
|
6435
6441
|
if (unknownFlags.length > 0) {
|
|
6436
|
-
|
|
6437
|
-
console.log("");
|
|
6438
|
-
console.log("\u53EF\u7528\u9009\u9879: --full\uFF08\u5220\u5168\u90E8\uFF09, -y/--yes\uFF08\u8DF3\u8FC7\u786E\u8BA4\uFF09");
|
|
6439
|
-
process.exit(1);
|
|
6442
|
+
throw new CliError(`\u672A\u77E5\u7684\u9009\u9879: ${unknownFlags.join(", ")}`, { hint: ["", "\u53EF\u7528\u9009\u9879: --full\uFF08\u5220\u5168\u90E8\uFF09, -y/--yes\uFF08\u8DF3\u8FC7\u786E\u8BA4\uFF09"] });
|
|
6440
6443
|
}
|
|
6441
6444
|
const fullReset = flags.includes("--full");
|
|
6442
6445
|
const skipConfirm = flags.includes("--yes") || flags.includes("-y");
|
|
@@ -6446,16 +6449,18 @@ async function cmdReset(args) {
|
|
|
6446
6449
|
} else if (names.length > 0) {
|
|
6447
6450
|
const { matched, unmatched } = resolveResetTargets(names);
|
|
6448
6451
|
if (unmatched.length > 0) {
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
|
|
6458
|
-
|
|
6452
|
+
throw new CliError(`\u672A\u77E5\u7684\u91CD\u7F6E\u76EE\u6807: ${unmatched.join(", ")}`, {
|
|
6453
|
+
hint: [
|
|
6454
|
+
"",
|
|
6455
|
+
`\u53EF\u7528\u76EE\u6807: ${RESET_TARGETS.map((t) => t.aliases[0]).join(", ")}`,
|
|
6456
|
+
"",
|
|
6457
|
+
"\u793A\u4F8B:",
|
|
6458
|
+
" mihomo reset sub log # \u5220\u9664\u8BA2\u9605\u548C\u65E5\u5FD7",
|
|
6459
|
+
" mihomo reset kernel # \u53EA\u5220\u5185\u6838",
|
|
6460
|
+
" mihomo reset --full # \u5220\u9664\u5168\u90E8",
|
|
6461
|
+
" mihomo reset # \u5220\u9664\u5168\u90E8\uFF08\u4FDD\u7559\u8BBE\u7F6E\u3001\u5185\u6838\u3001\u8986\u5199\uFF09"
|
|
6462
|
+
]
|
|
6463
|
+
});
|
|
6459
6464
|
}
|
|
6460
6465
|
targets = matched;
|
|
6461
6466
|
} else {
|
|
@@ -6664,21 +6669,18 @@ function githubRepoUrl(rawUrl) {
|
|
|
6664
6669
|
function resolveTestTarget(args) {
|
|
6665
6670
|
const subs = getSubscriptions();
|
|
6666
6671
|
if (subs.length === 0) {
|
|
6667
|
-
|
|
6668
|
-
process.exit(1);
|
|
6672
|
+
throw new CliError("\u6CA1\u6709\u8BA2\u9605");
|
|
6669
6673
|
}
|
|
6670
6674
|
const nameArg = getNonFlagArg(args, 2);
|
|
6671
6675
|
const timeout = parseIntArg(args, "-t", "--timeout", DEFAULT_TEST_TIMEOUT);
|
|
6672
6676
|
const concurrency = parseIntArg(args, "-j", "--concurrency", DEFAULT_TEST_CONCURRENCY);
|
|
6673
6677
|
let target;
|
|
6674
6678
|
if (nameArg) {
|
|
6675
|
-
|
|
6676
|
-
target = pickSingleSubscription(matches, nameArg);
|
|
6679
|
+
target = resolveSubscription(subs, nameArg);
|
|
6677
6680
|
} else {
|
|
6678
6681
|
const activeSub = getActiveSubscription();
|
|
6679
6682
|
if (!activeSub) {
|
|
6680
|
-
|
|
6681
|
-
process.exit(1);
|
|
6683
|
+
throw new CliError("\u6CA1\u6709\u6D3B\u8DC3\u8BA2\u9605\uFF0C\u8BF7\u6307\u5B9A\u8BA2\u9605\u540D\u79F0");
|
|
6682
6684
|
}
|
|
6683
6685
|
target = activeSub;
|
|
6684
6686
|
}
|
|
@@ -6739,274 +6741,231 @@ function printSubscriptionList() {
|
|
|
6739
6741
|
console.log("\u6253\u5F00\u9875\u9762: mihomo sub web [name]");
|
|
6740
6742
|
console.log("");
|
|
6741
6743
|
}
|
|
6742
|
-
async function
|
|
6743
|
-
const
|
|
6744
|
-
|
|
6745
|
-
|
|
6746
|
-
|
|
6744
|
+
async function subAdd(args) {
|
|
6745
|
+
const url = args[2]?.trim();
|
|
6746
|
+
const name = args[3] || "default";
|
|
6747
|
+
if (!url) {
|
|
6748
|
+
throw new CliError("\u8BF7\u63D0\u4F9B\u6709\u6548\u7684\u8BA2\u9605 URL");
|
|
6747
6749
|
}
|
|
6748
|
-
if (
|
|
6749
|
-
const
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
console.error("\u9519\u8BEF: \u8BF7\u63D0\u4F9B\u6709\u6548\u7684\u8BA2\u9605 URL");
|
|
6753
|
-
process.exit(1);
|
|
6754
|
-
}
|
|
6755
|
-
if (isMultiUrl(url)) {
|
|
6756
|
-
const urls = splitUrls(url);
|
|
6757
|
-
if (urls.length === 0) {
|
|
6758
|
-
console.error("\u9519\u8BEF: \u8BF7\u63D0\u4F9B\u6709\u6548\u7684\u8BA2\u9605 URL");
|
|
6759
|
-
process.exit(1);
|
|
6760
|
-
}
|
|
6761
|
-
for (const u of urls) {
|
|
6762
|
-
if (!isValidHttpUrl(u)) {
|
|
6763
|
-
console.error(`\u9519\u8BEF: \u65E0\u6548\u7684 URL: ${u}`);
|
|
6764
|
-
process.exit(1);
|
|
6765
|
-
}
|
|
6766
|
-
}
|
|
6767
|
-
const normalizedUrl = urls.join(",");
|
|
6768
|
-
console.log(`\u6DFB\u52A0\u5408\u5E76\u8BA2\u9605: ${name} (${urls.length} \u4E2A\u6E90)`);
|
|
6769
|
-
try {
|
|
6770
|
-
addSubscription(normalizedUrl, name);
|
|
6771
|
-
setDefaultSubscription(name);
|
|
6772
|
-
const info = await downloadMergedSubscription(urls, name);
|
|
6773
|
-
console.log(`\u5DF2\u6DFB\u52A0\u5E76\u5207\u6362\u5230 "${name}" (${formatProxySummary(info)}, \u5408\u5E76 ${urls.length} \u6E90)`);
|
|
6774
|
-
} catch (e) {
|
|
6775
|
-
removeSubscription(name);
|
|
6776
|
-
console.error(`\u6DFB\u52A0\u5931\u8D25: ${e.message}`);
|
|
6777
|
-
process.exit(1);
|
|
6778
|
-
}
|
|
6779
|
-
} else {
|
|
6780
|
-
if (!isValidHttpUrl(url)) {
|
|
6781
|
-
console.error("\u9519\u8BEF: \u8BF7\u63D0\u4F9B\u6709\u6548\u7684\u8BA2\u9605 URL\uFF08\u9700\u4EE5 http:// \u6216 https:// \u5F00\u5934\uFF09");
|
|
6782
|
-
process.exit(1);
|
|
6783
|
-
}
|
|
6784
|
-
console.log(`\u6DFB\u52A0\u8BA2\u9605: ${name}`);
|
|
6785
|
-
try {
|
|
6786
|
-
addSubscription(url, name);
|
|
6787
|
-
setDefaultSubscription(name);
|
|
6788
|
-
const info = await downloadSubscription(url, name);
|
|
6789
|
-
const repoUrl = githubRepoUrl(url);
|
|
6790
|
-
if (repoUrl) saveSubscriptionCache(name, { web_page_url: repoUrl });
|
|
6791
|
-
console.log(`\u5DF2\u6DFB\u52A0\u5E76\u5207\u6362\u5230 "${name}" (${formatProxySummary(info)})`);
|
|
6792
|
-
} catch (e) {
|
|
6793
|
-
removeSubscription(name);
|
|
6794
|
-
console.error(`\u6DFB\u52A0\u5931\u8D25: ${e.message}`);
|
|
6795
|
-
process.exit(1);
|
|
6796
|
-
}
|
|
6750
|
+
if (isMultiUrl(url)) {
|
|
6751
|
+
const urls = splitUrls(url);
|
|
6752
|
+
if (urls.length === 0) {
|
|
6753
|
+
throw new CliError("\u8BF7\u63D0\u4F9B\u6709\u6548\u7684\u8BA2\u9605 URL");
|
|
6797
6754
|
}
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
}
|
|
6802
|
-
if (action === "update") {
|
|
6803
|
-
const name = args[2];
|
|
6804
|
-
const subs = getSubscriptions();
|
|
6805
|
-
if (subs.length === 0) {
|
|
6806
|
-
console.error("\u9519\u8BEF: \u6CA1\u6709\u8BA2\u9605");
|
|
6807
|
-
process.exit(1);
|
|
6808
|
-
}
|
|
6809
|
-
if (!name) {
|
|
6810
|
-
console.log(`\u66F4\u65B0\u6240\u6709 ${subs.length} \u4E2A\u8BA2\u9605...`);
|
|
6811
|
-
const results = await Promise.all(subs.map((sub) => tryUpdateOne(sub)));
|
|
6812
|
-
let ok = 0;
|
|
6813
|
-
for (const r of results) {
|
|
6814
|
-
if (r.success) ok++;
|
|
6815
|
-
printUpdateResult(r);
|
|
6755
|
+
for (const u of urls) {
|
|
6756
|
+
if (!isValidHttpUrl(u)) {
|
|
6757
|
+
throw new CliError(`\u65E0\u6548\u7684 URL: ${u}`);
|
|
6816
6758
|
}
|
|
6817
|
-
if (ok === 0) process.exit(1);
|
|
6818
|
-
console.log("");
|
|
6819
|
-
printRestartHintIfRunning();
|
|
6820
|
-
printSubscriptionList();
|
|
6821
|
-
return;
|
|
6822
6759
|
}
|
|
6823
|
-
const
|
|
6824
|
-
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
|
|
6760
|
+
const normalizedUrl = urls.join(",");
|
|
6761
|
+
console.log(`\u6DFB\u52A0\u5408\u5E76\u8BA2\u9605: ${name} (${urls.length} \u4E2A\u6E90)`);
|
|
6762
|
+
try {
|
|
6763
|
+
addSubscription(normalizedUrl, name);
|
|
6764
|
+
setDefaultSubscription(name);
|
|
6765
|
+
const info = await downloadMergedSubscription(urls, name);
|
|
6766
|
+
console.log(`\u5DF2\u6DFB\u52A0\u5E76\u5207\u6362\u5230 "${name}" (${formatProxySummary(info)}, \u5408\u5E76 ${urls.length} \u6E90)`);
|
|
6767
|
+
} catch (e) {
|
|
6768
|
+
removeSubscription(name);
|
|
6769
|
+
throw new CliError(e.message, { label: "\u6DFB\u52A0\u5931\u8D25" });
|
|
6830
6770
|
}
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
printSubscriptionList();
|
|
6835
|
-
return;
|
|
6836
|
-
}
|
|
6837
|
-
if (action === "use") {
|
|
6838
|
-
const name = args[2];
|
|
6839
|
-
const subs = getSubscriptions();
|
|
6840
|
-
if (!name) {
|
|
6841
|
-
console.error("\u9519\u8BEF: \u8BF7\u6307\u5B9A\u8BA2\u9605\u540D\u79F0");
|
|
6842
|
-
if (subs.length > 0) {
|
|
6843
|
-
console.log("\n\u53EF\u7528\u8BA2\u9605:");
|
|
6844
|
-
for (const s of subs) console.log(` ${s.name}`);
|
|
6845
|
-
}
|
|
6846
|
-
process.exit(1);
|
|
6847
|
-
}
|
|
6848
|
-
const matches = findSubscriptionFuzzy(subs, name);
|
|
6849
|
-
const target = pickSingleSubscription(matches, name);
|
|
6850
|
-
const currentDefault = getActiveSubscription();
|
|
6851
|
-
const isAlreadyDefault = currentDefault && currentDefault.name === target.name;
|
|
6852
|
-
if (isAlreadyDefault) {
|
|
6853
|
-
console.log(`"${target.name}" \u5DF2\u662F\u5F53\u524D\u4F7F\u7528\u7684\u8BA2\u9605`);
|
|
6854
|
-
console.log("");
|
|
6855
|
-
printSubscriptionList();
|
|
6856
|
-
return;
|
|
6771
|
+
} else {
|
|
6772
|
+
if (!isValidHttpUrl(url)) {
|
|
6773
|
+
throw new CliError("\u8BF7\u63D0\u4F9B\u6709\u6548\u7684\u8BA2\u9605 URL\uFF08\u9700\u4EE5 http:// \u6216 https:// \u5F00\u5934\uFF09");
|
|
6857
6774
|
}
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6775
|
+
console.log(`\u6DFB\u52A0\u8BA2\u9605: ${name}`);
|
|
6776
|
+
try {
|
|
6777
|
+
addSubscription(url, name);
|
|
6778
|
+
setDefaultSubscription(name);
|
|
6779
|
+
const info = await downloadSubscription(url, name);
|
|
6780
|
+
const repoUrl = githubRepoUrl(url);
|
|
6781
|
+
if (repoUrl) saveSubscriptionCache(name, { web_page_url: repoUrl });
|
|
6782
|
+
console.log(`\u5DF2\u6DFB\u52A0\u5E76\u5207\u6362\u5230 "${name}" (${formatProxySummary(info)})`);
|
|
6783
|
+
} catch (e) {
|
|
6784
|
+
removeSubscription(name);
|
|
6785
|
+
throw new CliError(e.message, { label: "\u6DFB\u52A0\u5931\u8D25" });
|
|
6866
6786
|
}
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6787
|
+
}
|
|
6788
|
+
console.log("");
|
|
6789
|
+
printSubscriptionList();
|
|
6790
|
+
}
|
|
6791
|
+
async function subUpdate(args) {
|
|
6792
|
+
const name = args[2];
|
|
6793
|
+
const subs = getSubscriptions();
|
|
6794
|
+
if (subs.length === 0) {
|
|
6795
|
+
throw new CliError("\u6CA1\u6709\u8BA2\u9605");
|
|
6796
|
+
}
|
|
6797
|
+
if (!name) {
|
|
6798
|
+
console.log(`\u66F4\u65B0\u6240\u6709 ${subs.length} \u4E2A\u8BA2\u9605...`);
|
|
6799
|
+
const results = await Promise.all(subs.map((sub) => tryUpdateOne(sub)));
|
|
6800
|
+
let ok = 0;
|
|
6801
|
+
for (const r of results) {
|
|
6802
|
+
if (r.success) ok++;
|
|
6803
|
+
printUpdateResult(r);
|
|
6871
6804
|
}
|
|
6805
|
+
if (ok === 0) throw new CliError("\u5168\u90E8\u8BA2\u9605\u66F4\u65B0\u5931\u8D25");
|
|
6872
6806
|
console.log("");
|
|
6807
|
+
printRestartHintIfRunning();
|
|
6873
6808
|
printSubscriptionList();
|
|
6874
6809
|
return;
|
|
6875
6810
|
}
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
process.exit(1);
|
|
6882
|
-
}
|
|
6883
|
-
let target;
|
|
6884
|
-
if (name) {
|
|
6885
|
-
const matches = findSubscriptionFuzzy(subs, name);
|
|
6886
|
-
target = pickSingleSubscription(matches, name);
|
|
6887
|
-
} else {
|
|
6888
|
-
target = getActiveSubscription() || subs[0];
|
|
6889
|
-
}
|
|
6890
|
-
const cached = subs.find((s) => s.name === target.name);
|
|
6891
|
-
let webPageUrl = cached?.web_page_url;
|
|
6892
|
-
if (!webPageUrl) {
|
|
6893
|
-
console.log("\u8BA2\u9605\u4FE1\u606F\u4E2D\u7F3A\u5C11\u9875\u9762\u5730\u5740\uFF0C\u6B63\u5728\u67E5\u8BE2\u8BA2\u9605...");
|
|
6894
|
-
try {
|
|
6895
|
-
const info = isMultiUrl(target.url) ? await downloadMergedSubscription(splitUrls(target.url), target.name, void 0, false) : await downloadSubscription(target.url, target.name, void 0, false);
|
|
6896
|
-
if (info.webPageUrl) {
|
|
6897
|
-
webPageUrl = info.webPageUrl;
|
|
6898
|
-
} else {
|
|
6899
|
-
console.error("\u9519\u8BEF: \u8BE5\u8BA2\u9605\u6CA1\u6709\u63D0\u4F9B\u9875\u9762\u5730\u5740");
|
|
6900
|
-
process.exit(1);
|
|
6901
|
-
}
|
|
6902
|
-
} catch (e) {
|
|
6903
|
-
console.error(`\u67E5\u8BE2\u5931\u8D25: ${e.message}`);
|
|
6904
|
-
process.exit(1);
|
|
6905
|
-
}
|
|
6906
|
-
}
|
|
6907
|
-
console.log(`\u6253\u5F00\u8BA2\u9605\u9875\u9762: ${webPageUrl}`);
|
|
6908
|
-
const opened = openUrl(webPageUrl);
|
|
6909
|
-
if (!opened) {
|
|
6910
|
-
console.log("\u8BF7\u624B\u52A8\u8BBF\u95EE\u4E0A\u9762\u7684\u5730\u5740");
|
|
6911
|
-
}
|
|
6912
|
-
return;
|
|
6811
|
+
const target = resolveSubscription(subs, name);
|
|
6812
|
+
console.log(`\u66F4\u65B0\u8BA2\u9605: ${target.name}`);
|
|
6813
|
+
const result = await tryUpdateOne(target);
|
|
6814
|
+
if (!result.success) {
|
|
6815
|
+
throw new CliError((result.error || "").split("\n")[0], { label: "\u66F4\u65B0\u5931\u8D25" });
|
|
6913
6816
|
}
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
|
|
6921
|
-
|
|
6922
|
-
|
|
6923
|
-
|
|
6924
|
-
|
|
6925
|
-
|
|
6926
|
-
|
|
6927
|
-
|
|
6928
|
-
|
|
6929
|
-
|
|
6930
|
-
|
|
6931
|
-
}
|
|
6817
|
+
console.log(`\u5DF2\u66F4\u65B0 (${formatProxySummary(result)})`);
|
|
6818
|
+
console.log("");
|
|
6819
|
+
printRestartHintIfRunning();
|
|
6820
|
+
printSubscriptionList();
|
|
6821
|
+
}
|
|
6822
|
+
async function subUse(args) {
|
|
6823
|
+
const name = args[2];
|
|
6824
|
+
const subs = getSubscriptions();
|
|
6825
|
+
if (!name) {
|
|
6826
|
+
throw new CliError("\u8BF7\u6307\u5B9A\u8BA2\u9605\u540D\u79F0", {
|
|
6827
|
+
hint: subs.length > 0 ? ["", "\u53EF\u7528\u8BA2\u9605:", ...subs.map((s) => ` ${s.name}`)] : void 0
|
|
6828
|
+
});
|
|
6829
|
+
}
|
|
6830
|
+
const target = resolveSubscription(subs, name);
|
|
6831
|
+
const currentDefault = getActiveSubscription();
|
|
6832
|
+
const isAlreadyDefault = currentDefault && currentDefault.name === target.name;
|
|
6833
|
+
if (isAlreadyDefault) {
|
|
6834
|
+
console.log(`"${target.name}" \u5DF2\u662F\u5F53\u524D\u4F7F\u7528\u7684\u8BA2\u9605`);
|
|
6932
6835
|
console.log("");
|
|
6933
6836
|
printSubscriptionList();
|
|
6934
6837
|
return;
|
|
6935
6838
|
}
|
|
6936
|
-
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
|
|
6946
|
-
|
|
6947
|
-
|
|
6948
|
-
|
|
6949
|
-
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
|
|
6953
|
-
|
|
6954
|
-
|
|
6955
|
-
|
|
6956
|
-
|
|
6957
|
-
|
|
6958
|
-
|
|
6959
|
-
|
|
6960
|
-
|
|
6961
|
-
|
|
6962
|
-
|
|
6839
|
+
const success = setDefaultSubscription(target.name);
|
|
6840
|
+
if (!success) {
|
|
6841
|
+
throw new CliError(`\u672A\u627E\u5230\u8BA2\u9605 "${name}"`);
|
|
6842
|
+
}
|
|
6843
|
+
console.log(`\u5DF2\u5207\u6362\u5230 "${target.name}"`);
|
|
6844
|
+
if (await restartToApply(args)) return;
|
|
6845
|
+
console.log("");
|
|
6846
|
+
printSubscriptionList();
|
|
6847
|
+
}
|
|
6848
|
+
async function subWeb(args) {
|
|
6849
|
+
const name = args[2];
|
|
6850
|
+
const subs = getSubscriptionsWithCache();
|
|
6851
|
+
if (subs.length === 0) {
|
|
6852
|
+
throw new CliError("\u6CA1\u6709\u8BA2\u9605");
|
|
6853
|
+
}
|
|
6854
|
+
let target;
|
|
6855
|
+
if (name) {
|
|
6856
|
+
target = resolveSubscription(subs, name);
|
|
6857
|
+
} else {
|
|
6858
|
+
target = getActiveSubscription() || subs[0];
|
|
6859
|
+
}
|
|
6860
|
+
const cached = subs.find((s) => s.name === target.name);
|
|
6861
|
+
let webPageUrl = cached?.web_page_url;
|
|
6862
|
+
if (!webPageUrl) {
|
|
6863
|
+
console.log("\u8BA2\u9605\u4FE1\u606F\u4E2D\u7F3A\u5C11\u9875\u9762\u5730\u5740\uFF0C\u6B63\u5728\u67E5\u8BE2\u8BA2\u9605...");
|
|
6864
|
+
try {
|
|
6865
|
+
const info = isMultiUrl(target.url) ? await downloadMergedSubscription(splitUrls(target.url), target.name, void 0, false) : await downloadSubscription(target.url, target.name, void 0, false);
|
|
6866
|
+
if (!info.webPageUrl) {
|
|
6867
|
+
throw new CliError("\u8BE5\u8BA2\u9605\u6CA1\u6709\u63D0\u4F9B\u9875\u9762\u5730\u5740");
|
|
6963
6868
|
}
|
|
6869
|
+
webPageUrl = info.webPageUrl;
|
|
6870
|
+
} catch (e) {
|
|
6871
|
+
if (e instanceof CliError) throw e;
|
|
6872
|
+
throw new CliError(e.message, { label: "\u67E5\u8BE2\u5931\u8D25" });
|
|
6964
6873
|
}
|
|
6965
|
-
return;
|
|
6966
6874
|
}
|
|
6967
|
-
|
|
6968
|
-
|
|
6969
|
-
|
|
6970
|
-
console.log(
|
|
6971
|
-
console.log("");
|
|
6972
|
-
const progress = createProgressPrinter();
|
|
6973
|
-
const summary = await withTestInstance(target.name, async (apiBase) => {
|
|
6974
|
-
return testSubscriptionProxies(target.name, {
|
|
6975
|
-
timeout,
|
|
6976
|
-
concurrency,
|
|
6977
|
-
apiBase,
|
|
6978
|
-
onResult: progress.onResult
|
|
6979
|
-
});
|
|
6980
|
-
});
|
|
6981
|
-
progress.finish();
|
|
6982
|
-
console.log(formatTestSummary(summary));
|
|
6983
|
-
return;
|
|
6875
|
+
console.log(`\u6253\u5F00\u8BA2\u9605\u9875\u9762: ${webPageUrl}`);
|
|
6876
|
+
const opened = openUrl(webPageUrl);
|
|
6877
|
+
if (!opened) {
|
|
6878
|
+
console.log("\u8BF7\u624B\u52A8\u8BBF\u95EE\u4E0A\u9762\u7684\u5730\u5740");
|
|
6984
6879
|
}
|
|
6985
|
-
console.error("\u9519\u8BEF: \u672A\u77E5\u7684\u8BA2\u9605\u547D\u4EE4");
|
|
6986
|
-
console.log("\u7528\u6CD5: mihomo sub [list|use|add|update|remove|web|test|clean]");
|
|
6987
|
-
process.exit(1);
|
|
6988
6880
|
}
|
|
6989
|
-
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
process.exit(1);
|
|
6881
|
+
function subRemove(args) {
|
|
6882
|
+
const name = args[2];
|
|
6883
|
+
const subs = getSubscriptions();
|
|
6884
|
+
if (!name) {
|
|
6885
|
+
throw new CliError("\u8BF7\u6307\u5B9A\u8981\u5220\u9664\u7684\u8BA2\u9605\u540D\u79F0", {
|
|
6886
|
+
hint: subs.length > 0 ? ["", "\u53EF\u7528\u8BA2\u9605:", ...subs.map((s) => ` ${s.name}`)] : void 0
|
|
6887
|
+
});
|
|
6997
6888
|
}
|
|
6889
|
+
const target = resolveSubscription(subs, name);
|
|
6890
|
+
const switchedTo = removeSubscription(target.name);
|
|
6891
|
+
console.log(`\u5DF2\u5220\u9664\u8BA2\u9605 "${target.name}"`);
|
|
6892
|
+
if (switchedTo) {
|
|
6893
|
+
console.log(`\u5DF2\u81EA\u52A8\u5207\u6362\u5230 "${switchedTo}"`);
|
|
6894
|
+
}
|
|
6895
|
+
console.log("");
|
|
6896
|
+
printSubscriptionList();
|
|
6998
6897
|
}
|
|
6999
|
-
function
|
|
7000
|
-
const
|
|
7001
|
-
|
|
7002
|
-
|
|
7003
|
-
|
|
6898
|
+
async function subClean(args) {
|
|
6899
|
+
const { target, timeout, concurrency } = resolveTestTarget(args);
|
|
6900
|
+
const rounds = parseIntArg(args, "-r", "--rounds", DEFAULT_CLEAN_ROUNDS);
|
|
6901
|
+
console.log(`\u6E05\u7406\u8BA2\u9605 "${target.name}"...`);
|
|
6902
|
+
console.log(`\u8D85\u65F6: ${timeout}ms \u5E76\u53D1: ${concurrency}`);
|
|
6903
|
+
console.log("");
|
|
6904
|
+
const progress = createProgressPrinter(rounds);
|
|
6905
|
+
const result = await withTestInstance(target.name, async (apiBase) => {
|
|
6906
|
+
return autoCleanSubscription(target.name, {
|
|
6907
|
+
timeout,
|
|
6908
|
+
concurrency,
|
|
6909
|
+
rounds,
|
|
6910
|
+
apiBase,
|
|
6911
|
+
onResult: progress.onResult,
|
|
6912
|
+
onRetryRound: progress.onRetryRound
|
|
6913
|
+
});
|
|
6914
|
+
});
|
|
6915
|
+
progress.finish();
|
|
6916
|
+
console.log(formatTestSummary(result.summary));
|
|
6917
|
+
if (result.skipped) {
|
|
6918
|
+
console.log("");
|
|
6919
|
+
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"));
|
|
6920
|
+
} else if (result.removedProxies > 0) {
|
|
6921
|
+
console.log(`${colors.green("\u5DF2\u6E05\u7406")}: ${formatCleanSummary(result)}`);
|
|
6922
|
+
if (getRunningState().running) {
|
|
6923
|
+
console.log("");
|
|
6924
|
+
console.log("\u63D0\u793A: \u9700\u8981\u91CD\u542F mihomo \u4F7F\u66F4\u6539\u751F\u6548 (mihomo start)");
|
|
6925
|
+
}
|
|
7004
6926
|
}
|
|
7005
|
-
return activeSub;
|
|
7006
6927
|
}
|
|
6928
|
+
async function subTest(args) {
|
|
6929
|
+
const { target, timeout, concurrency } = resolveTestTarget(args);
|
|
6930
|
+
console.log(`\u6D4B\u8BD5\u8BA2\u9605 "${target.name}" \u7684\u8282\u70B9\u8FDE\u901A\u6027...`);
|
|
6931
|
+
console.log(`\u8D85\u65F6: ${timeout}ms \u5E76\u53D1: ${concurrency}`);
|
|
6932
|
+
console.log("");
|
|
6933
|
+
const progress = createProgressPrinter();
|
|
6934
|
+
const summary = await withTestInstance(target.name, async (apiBase) => {
|
|
6935
|
+
return testSubscriptionProxies(target.name, {
|
|
6936
|
+
timeout,
|
|
6937
|
+
concurrency,
|
|
6938
|
+
apiBase,
|
|
6939
|
+
onResult: progress.onResult
|
|
6940
|
+
});
|
|
6941
|
+
});
|
|
6942
|
+
progress.finish();
|
|
6943
|
+
console.log(formatTestSummary(summary));
|
|
6944
|
+
}
|
|
6945
|
+
var SUBCOMMANDS4 = [
|
|
6946
|
+
{ name: "list", handler: printSubscriptionList },
|
|
6947
|
+
{ name: "add", handler: subAdd },
|
|
6948
|
+
{ name: "update", handler: subUpdate },
|
|
6949
|
+
{ name: "use", handler: subUse },
|
|
6950
|
+
{ name: "web", aliases: ["open"], handler: subWeb },
|
|
6951
|
+
{ name: "remove", aliases: ["rm", "delete"], handler: subRemove },
|
|
6952
|
+
{ name: "clean", handler: subClean },
|
|
6953
|
+
{ name: "test", handler: subTest }
|
|
6954
|
+
];
|
|
6955
|
+
async function cmdSubscription(args) {
|
|
6956
|
+
await dispatchSubcommand(args, SUBCOMMANDS4, {
|
|
6957
|
+
// 无子命令 → 列表;未知子命令 → 报错
|
|
6958
|
+
fallback: printSubscriptionList,
|
|
6959
|
+
onUnknown: () => {
|
|
6960
|
+
throw new CliError("\u672A\u77E5\u7684\u8BA2\u9605\u547D\u4EE4", { hint: "\u7528\u6CD5: mihomo sub [list|use|add|update|remove|web|test|clean]" });
|
|
6961
|
+
}
|
|
6962
|
+
});
|
|
6963
|
+
}
|
|
6964
|
+
|
|
6965
|
+
// src/commands/test.ts
|
|
7007
6966
|
async function cmdTest(args) {
|
|
7008
6967
|
requireRunning();
|
|
7009
|
-
const activeSub =
|
|
6968
|
+
const activeSub = requireActiveSubscription("\u6CA1\u6709\u6D3B\u8DC3\u8BA2\u9605");
|
|
7010
6969
|
const timeout = parseIntArg(args, "-t", "--timeout", DEFAULT_TEST_TIMEOUT);
|
|
7011
6970
|
const concurrency = parseIntArg(args, "-j", "--concurrency", DEFAULT_TEST_CONCURRENCY);
|
|
7012
6971
|
console.log(`\u6D4B\u8BD5 "${activeSub.name}" \u8282\u70B9\u8FDE\u901A\u6027...`);
|
|
@@ -7023,7 +6982,7 @@ async function cmdTest(args) {
|
|
|
7023
6982
|
}
|
|
7024
6983
|
async function cmdClean(args) {
|
|
7025
6984
|
requireRunning();
|
|
7026
|
-
const activeSub =
|
|
6985
|
+
const activeSub = requireActiveSubscription("\u6CA1\u6709\u6D3B\u8DC3\u8BA2\u9605");
|
|
7027
6986
|
const timeout = parseIntArg(args, "-t", "--timeout", DEFAULT_TEST_TIMEOUT);
|
|
7028
6987
|
const concurrency = parseIntArg(args, "-j", "--concurrency", DEFAULT_TEST_CONCURRENCY);
|
|
7029
6988
|
const rounds = parseIntArg(args, "-r", "--rounds", DEFAULT_CLEAN_ROUNDS);
|
|
@@ -7054,9 +7013,7 @@ async function cmdClean(args) {
|
|
|
7054
7013
|
try {
|
|
7055
7014
|
if (!daemonManaged) {
|
|
7056
7015
|
if (hasRootResidue()) {
|
|
7057
|
-
|
|
7058
|
-
console.error("\u8BF7\u6539\u7528 mihomo sub clean\uFF08\u9694\u79BB\u5B9E\u4F8B\u6D4B\u901F\uFF0C\u65E0\u9700\u505C\u6B62\u4E3B\u5B9E\u4F8B\uFF09");
|
|
7059
|
-
process.exit(1);
|
|
7016
|
+
throw new CliError("\u4E3B\u5B9E\u4F8B\u4EE5 root \u8FD0\u884C\uFF08TUN\uFF09\uFF0C\u505C\u6B62\u5B83\u9700\u8981 sudo", { hint: "\u8BF7\u6539\u7528 mihomo sub clean\uFF08\u9694\u79BB\u5B9E\u4F8B\u6D4B\u901F\uFF0C\u65E0\u9700\u505C\u6B62\u4E3B\u5B9E\u4F8B\uFF09" });
|
|
7060
7017
|
}
|
|
7061
7018
|
handleStopResult(stop());
|
|
7062
7019
|
}
|
|
@@ -7065,8 +7022,8 @@ async function cmdClean(args) {
|
|
|
7065
7022
|
const label = daemonManaged ? "\u5DF2\u91CD\u542F (\u4FDD\u6D3B)" : "\u5DF2\u91CD\u542F";
|
|
7066
7023
|
console.log(`${colors.green(label)}${pid ? ` (PID ${pid})` : ""} \xB7 ${formatProxySummary(configInfo)}`);
|
|
7067
7024
|
} catch (e) {
|
|
7068
|
-
|
|
7069
|
-
|
|
7025
|
+
if (e instanceof CliError) throw e;
|
|
7026
|
+
throw new CliError(e.message.split("\n")[0], { label: "\u91CD\u542F\u5931\u8D25" });
|
|
7070
7027
|
}
|
|
7071
7028
|
}
|
|
7072
7029
|
}
|
|
@@ -7075,9 +7032,7 @@ async function cmdClean(args) {
|
|
|
7075
7032
|
function cmdUI(args) {
|
|
7076
7033
|
const uiName = args[1] || "zash";
|
|
7077
7034
|
if (!Object.hasOwn(UI_URLS, uiName)) {
|
|
7078
|
-
|
|
7079
|
-
console.error("\u53EF\u7528 UI: zash (\u9ED8\u8BA4), dash, yacd");
|
|
7080
|
-
process.exit(1);
|
|
7035
|
+
throw new CliError(`\u672A\u77E5\u7684 UI "${uiName}"`, { hint: "\u53EF\u7528 UI: zash (\u9ED8\u8BA4), dash, yacd" });
|
|
7081
7036
|
}
|
|
7082
7037
|
const url = UI_URLS[uiName];
|
|
7083
7038
|
console.log(`\u6253\u5F00 Web UI: ${uiName}`);
|
|
@@ -7101,23 +7056,18 @@ async function cmdUpdate() {
|
|
|
7101
7056
|
console.log("");
|
|
7102
7057
|
console.log("\u6B63\u5728\u66F4\u65B0 mihomo-cli...");
|
|
7103
7058
|
console.log("");
|
|
7104
|
-
await new Promise((resolve) => {
|
|
7059
|
+
await new Promise((resolve, reject) => {
|
|
7105
7060
|
const npm = spawn3("npm", ["install", "-g", "mihomo-cli"], { stdio: "inherit" });
|
|
7106
7061
|
npm.on("close", (code) => {
|
|
7107
7062
|
if (code === 0) {
|
|
7108
7063
|
resolve();
|
|
7109
7064
|
} else {
|
|
7110
|
-
|
|
7111
|
-
process.exit(code || 1);
|
|
7065
|
+
reject(new CliError("\u66F4\u65B0\u5931\u8D25\u3002\u82E5\u4E3A\u6743\u9650\u95EE\u9898\uFF08EACCES\uFF09\uFF0C\u53EF\u5C1D\u8BD5: sudo npm install -g mihomo-cli", { exitCode: code || 1 }));
|
|
7112
7066
|
}
|
|
7113
7067
|
});
|
|
7114
7068
|
npm.on("error", (e) => {
|
|
7115
|
-
|
|
7116
|
-
|
|
7117
|
-
} else {
|
|
7118
|
-
console.error(`\u6267\u884C\u5931\u8D25: ${e.message}`);
|
|
7119
|
-
}
|
|
7120
|
-
process.exit(1);
|
|
7069
|
+
const perm = e.message.includes("EACCES") || e.message.includes("permission");
|
|
7070
|
+
reject(perm ? new CliError("\u6743\u9650\u4E0D\u8DB3\uFF0C\u53EF\u5C1D\u8BD5: sudo npm install -g mihomo-cli") : new CliError(`\u6267\u884C\u5931\u8D25: ${e.message}`));
|
|
7121
7071
|
});
|
|
7122
7072
|
});
|
|
7123
7073
|
try {
|
|
@@ -7377,14 +7327,20 @@ async function main() {
|
|
|
7377
7327
|
const token = args[0].toLowerCase();
|
|
7378
7328
|
const command = findCommand(token);
|
|
7379
7329
|
if (!command) {
|
|
7380
|
-
|
|
7381
|
-
console.error('\u4F7F\u7528 "mihomo help" \u67E5\u770B\u5E2E\u52A9');
|
|
7382
|
-
process.exit(1);
|
|
7330
|
+
throw new CliError(`\u672A\u77E5\u547D\u4EE4: ${token}`, { hint: '\u4F7F\u7528 "mihomo help" \u67E5\u770B\u5E2E\u52A9' });
|
|
7383
7331
|
}
|
|
7384
7332
|
await command.handler(command.rewrite ? command.rewrite(args) : args);
|
|
7385
7333
|
}
|
|
7386
7334
|
main().catch((e) => {
|
|
7387
|
-
|
|
7335
|
+
if (e instanceof CliError) {
|
|
7336
|
+
console.error(`${colors.red(`${e.label}:`)} ${e.message}`);
|
|
7337
|
+
for (const line of e.hint) console.error(line);
|
|
7338
|
+
runCleanup();
|
|
7339
|
+
process.exit(e.exitCode);
|
|
7340
|
+
}
|
|
7341
|
+
const err = e;
|
|
7342
|
+
console.error(`${colors.red("\u9519\u8BEF:")} ${err.message}`);
|
|
7343
|
+
if (err.stack) console.error(err.stack.split("\n").slice(1).join("\n"));
|
|
7388
7344
|
runCleanup();
|
|
7389
7345
|
process.exit(1);
|
|
7390
7346
|
});
|