mihomo-cli 2.10.0 → 3.0.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 +18 -0
- package/README.md +26 -0
- package/dist/index.js +1298 -1008
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,71 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// src/paths.ts
|
|
4
|
-
import fs from "fs";
|
|
5
|
-
import os from "os";
|
|
6
|
-
import path from "path";
|
|
7
|
-
function getUserDataDir() {
|
|
8
|
-
if (process.env.MIHOMO_CLI_DIR) {
|
|
9
|
-
return process.env.MIHOMO_CLI_DIR;
|
|
10
|
-
}
|
|
11
|
-
return path.join(os.homedir(), ".mihomo-cli");
|
|
12
|
-
}
|
|
13
|
-
var USER_DATA_DIR = getUserDataDir();
|
|
14
|
-
var DIRS = {
|
|
15
|
-
kernel: path.join(USER_DATA_DIR, "kernel"),
|
|
16
|
-
subscriptions: path.join(USER_DATA_DIR, "subscriptions"),
|
|
17
|
-
logs: path.join(USER_DATA_DIR, "logs"),
|
|
18
|
-
data: path.join(USER_DATA_DIR, "data"),
|
|
19
|
-
runtime: path.join(USER_DATA_DIR, "runtime")
|
|
20
|
-
};
|
|
21
|
-
var PATHS = {
|
|
22
|
-
mihomoBinary: path.join(DIRS.kernel, "mihomo"),
|
|
23
|
-
settingsFile: path.join(USER_DATA_DIR, "settings.json"),
|
|
24
|
-
subscriptionsCacheFile: path.join(DIRS.subscriptions, "cache.json"),
|
|
25
|
-
configFile: path.join(DIRS.runtime, "config.yaml"),
|
|
26
|
-
logFile: path.join(DIRS.logs, "mihomo.log"),
|
|
27
|
-
pidFile: path.join(DIRS.runtime, "pid"),
|
|
28
|
-
configStage1Subscription: path.join(DIRS.runtime, "1.subscription.yaml"),
|
|
29
|
-
configStage2Overwrite: path.join(DIRS.runtime, "2.overwrite.yaml"),
|
|
30
|
-
configStage3System: path.join(DIRS.runtime, "3.system.yaml")
|
|
31
|
-
};
|
|
32
|
-
var DIRECTORY_TARGETS = {
|
|
33
|
-
root: { path: null, label: "\u6839\u76EE\u5F55" },
|
|
34
|
-
subs: { path: DIRS.subscriptions, label: "\u8BA2\u9605\u76EE\u5F55" },
|
|
35
|
-
logs: { path: DIRS.logs, label: "\u65E5\u5FD7\u76EE\u5F55" },
|
|
36
|
-
data: { path: DIRS.data, label: "mihomo \u6570\u636E\u76EE\u5F55" },
|
|
37
|
-
runtime: { path: DIRS.runtime, label: "\u8FD0\u884C\u65F6\u76EE\u5F55" },
|
|
38
|
-
kernel: { path: DIRS.kernel, label: "\u5185\u6838\u76EE\u5F55" }
|
|
39
|
-
};
|
|
40
|
-
function ensureDirs() {
|
|
41
|
-
for (const dir of Object.values(DIRS)) {
|
|
42
|
-
if (!fs.existsSync(dir)) {
|
|
43
|
-
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
function atomicWriteFileSync(filePath, content, options) {
|
|
48
|
-
const tmp = `${filePath}.${process.pid}.tmp`;
|
|
49
|
-
try {
|
|
50
|
-
fs.writeFileSync(tmp, content, options);
|
|
51
|
-
fs.renameSync(tmp, filePath);
|
|
52
|
-
} catch (e) {
|
|
53
|
-
try {
|
|
54
|
-
fs.unlinkSync(tmp);
|
|
55
|
-
} catch {
|
|
56
|
-
}
|
|
57
|
-
throw e;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
function rmrf(dir) {
|
|
61
|
-
fs.rmSync(dir, { recursive: true, force: true });
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// src/process.ts
|
|
65
|
-
import { spawn, spawnSync as spawnSync3 } from "child_process";
|
|
66
|
-
import fs5 from "fs";
|
|
67
|
-
import path3 from "path";
|
|
68
|
-
|
|
69
3
|
// src/config.ts
|
|
70
4
|
import { spawnSync } from "child_process";
|
|
71
5
|
import fs4 from "fs";
|
|
@@ -2989,6 +2923,7 @@ var UI_URLS = {
|
|
|
2989
2923
|
dash: "https://metacubex.github.io/metacubexd",
|
|
2990
2924
|
yacd: "https://yacd.metacubex.one"
|
|
2991
2925
|
};
|
|
2926
|
+
var LAUNCH_AGENT_LABEL = "com.mihomo-cli.daemon";
|
|
2992
2927
|
var TUN_CONFIG = {
|
|
2993
2928
|
tun: {
|
|
2994
2929
|
enable: true,
|
|
@@ -3040,6 +2975,69 @@ var AUTO_CLEAN_THRESHOLD_GITHUB = 50;
|
|
|
3040
2975
|
import fs3 from "fs";
|
|
3041
2976
|
import path2 from "path";
|
|
3042
2977
|
|
|
2978
|
+
// src/paths.ts
|
|
2979
|
+
import fs from "fs";
|
|
2980
|
+
import os from "os";
|
|
2981
|
+
import path from "path";
|
|
2982
|
+
function getUserDataDir() {
|
|
2983
|
+
if (process.env.MIHOMO_CLI_DIR) {
|
|
2984
|
+
return process.env.MIHOMO_CLI_DIR;
|
|
2985
|
+
}
|
|
2986
|
+
return path.join(os.homedir(), ".mihomo-cli");
|
|
2987
|
+
}
|
|
2988
|
+
var USER_DATA_DIR = getUserDataDir();
|
|
2989
|
+
var DIRS = {
|
|
2990
|
+
kernel: path.join(USER_DATA_DIR, "kernel"),
|
|
2991
|
+
subscriptions: path.join(USER_DATA_DIR, "subscriptions"),
|
|
2992
|
+
logs: path.join(USER_DATA_DIR, "logs"),
|
|
2993
|
+
data: path.join(USER_DATA_DIR, "data"),
|
|
2994
|
+
runtime: path.join(USER_DATA_DIR, "runtime")
|
|
2995
|
+
};
|
|
2996
|
+
var PATHS = {
|
|
2997
|
+
mihomoBinary: path.join(DIRS.kernel, "mihomo"),
|
|
2998
|
+
settingsFile: path.join(USER_DATA_DIR, "settings.json"),
|
|
2999
|
+
subscriptionsCacheFile: path.join(DIRS.subscriptions, "cache.json"),
|
|
3000
|
+
configFile: path.join(DIRS.runtime, "config.yaml"),
|
|
3001
|
+
logFile: path.join(DIRS.logs, "mihomo.log"),
|
|
3002
|
+
pidFile: path.join(DIRS.runtime, "pid"),
|
|
3003
|
+
configStage1Subscription: path.join(DIRS.runtime, "1.subscription.yaml"),
|
|
3004
|
+
configStage2Overwrite: path.join(DIRS.runtime, "2.overwrite.yaml"),
|
|
3005
|
+
configStage3System: path.join(DIRS.runtime, "3.system.yaml"),
|
|
3006
|
+
// launchd LaunchAgent plist 必须位于真实用户主目录,不受 MIHOMO_CLI_DIR 影响
|
|
3007
|
+
launchAgentPlist: path.join(os.homedir(), "Library", "LaunchAgents", `${LAUNCH_AGENT_LABEL}.plist`)
|
|
3008
|
+
};
|
|
3009
|
+
var DIRECTORY_TARGETS = {
|
|
3010
|
+
root: { path: null, label: "\u6839\u76EE\u5F55" },
|
|
3011
|
+
subs: { path: DIRS.subscriptions, label: "\u8BA2\u9605\u76EE\u5F55" },
|
|
3012
|
+
logs: { path: DIRS.logs, label: "\u65E5\u5FD7\u76EE\u5F55" },
|
|
3013
|
+
data: { path: DIRS.data, label: "mihomo \u6570\u636E\u76EE\u5F55" },
|
|
3014
|
+
runtime: { path: DIRS.runtime, label: "\u8FD0\u884C\u65F6\u76EE\u5F55" },
|
|
3015
|
+
kernel: { path: DIRS.kernel, label: "\u5185\u6838\u76EE\u5F55" }
|
|
3016
|
+
};
|
|
3017
|
+
function ensureDirs() {
|
|
3018
|
+
for (const dir of Object.values(DIRS)) {
|
|
3019
|
+
if (!fs.existsSync(dir)) {
|
|
3020
|
+
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
3021
|
+
}
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
function atomicWriteFileSync(filePath, content, options) {
|
|
3025
|
+
const tmp = `${filePath}.${process.pid}.tmp`;
|
|
3026
|
+
try {
|
|
3027
|
+
fs.writeFileSync(tmp, content, options);
|
|
3028
|
+
fs.renameSync(tmp, filePath);
|
|
3029
|
+
} catch (e) {
|
|
3030
|
+
try {
|
|
3031
|
+
fs.unlinkSync(tmp);
|
|
3032
|
+
} catch {
|
|
3033
|
+
}
|
|
3034
|
+
throw e;
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
function rmrf(dir) {
|
|
3038
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
3039
|
+
}
|
|
3040
|
+
|
|
3043
3041
|
// src/settings.ts
|
|
3044
3042
|
import fs2 from "fs";
|
|
3045
3043
|
var settingsCache = null;
|
|
@@ -3581,6 +3579,16 @@ function clearKernelVersionCache() {
|
|
|
3581
3579
|
kernelVersionCached = false;
|
|
3582
3580
|
}
|
|
3583
3581
|
|
|
3582
|
+
// src/daemon.ts
|
|
3583
|
+
import { spawnSync as spawnSync4 } from "child_process";
|
|
3584
|
+
import fs6 from "fs";
|
|
3585
|
+
import path4 from "path";
|
|
3586
|
+
|
|
3587
|
+
// src/process.ts
|
|
3588
|
+
import { spawn, spawnSync as spawnSync3 } from "child_process";
|
|
3589
|
+
import fs5 from "fs";
|
|
3590
|
+
import path3 from "path";
|
|
3591
|
+
|
|
3584
3592
|
// src/utils.ts
|
|
3585
3593
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
3586
3594
|
import { createRequire } from "module";
|
|
@@ -4271,1021 +4279,1234 @@ function viewLogWithTail(logPath, options) {
|
|
|
4271
4279
|
});
|
|
4272
4280
|
}
|
|
4273
4281
|
|
|
4274
|
-
// src/
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4282
|
+
// src/daemon.ts
|
|
4283
|
+
var LAUNCHCTL_TIMEOUT_MS = 1e4;
|
|
4284
|
+
function launchctl(args) {
|
|
4285
|
+
const result = spawnSync4("launchctl", args, { encoding: "utf8", timeout: LAUNCHCTL_TIMEOUT_MS });
|
|
4286
|
+
return {
|
|
4287
|
+
status: result.status,
|
|
4288
|
+
stdout: result.stdout || "",
|
|
4289
|
+
stderr: result.stderr || ""
|
|
4290
|
+
};
|
|
4291
|
+
}
|
|
4292
|
+
function getUid() {
|
|
4293
|
+
const uid = process.getuid?.();
|
|
4294
|
+
if (uid === void 0) {
|
|
4295
|
+
throw new Error("\u65E0\u6CD5\u83B7\u53D6\u7528\u6237 UID\uFF0C\u4FDD\u6D3B\u529F\u80FD\u4EC5\u652F\u6301 macOS");
|
|
4296
|
+
}
|
|
4297
|
+
return uid;
|
|
4298
|
+
}
|
|
4299
|
+
function guiDomain(uid) {
|
|
4300
|
+
return `gui/${uid}`;
|
|
4301
|
+
}
|
|
4302
|
+
function serviceTarget(uid) {
|
|
4303
|
+
return `gui/${uid}/${LAUNCH_AGENT_LABEL}`;
|
|
4304
|
+
}
|
|
4305
|
+
function launchctlError(action, result) {
|
|
4306
|
+
const detail = (result.stderr || result.stdout || "").trim() || `\u9000\u51FA\u7801 ${result.status}`;
|
|
4307
|
+
return new Error(`launchctl ${action} \u5931\u8D25: ${detail}`);
|
|
4308
|
+
}
|
|
4309
|
+
function escapeXml(s) {
|
|
4310
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
4311
|
+
}
|
|
4312
|
+
function buildPlist() {
|
|
4313
|
+
const programArguments = [PATHS.mihomoBinary, "-d", DIRS.data, "-f", PATHS.configFile];
|
|
4314
|
+
const argsXml = programArguments.map((a) => ` <string>${escapeXml(a)}</string>`).join("\n");
|
|
4315
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
4316
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
4317
|
+
<plist version="1.0">
|
|
4318
|
+
<dict>
|
|
4319
|
+
<key>Label</key>
|
|
4320
|
+
<string>${escapeXml(LAUNCH_AGENT_LABEL)}</string>
|
|
4321
|
+
<key>ProgramArguments</key>
|
|
4322
|
+
<array>
|
|
4323
|
+
${argsXml}
|
|
4324
|
+
</array>
|
|
4325
|
+
<key>RunAtLoad</key>
|
|
4326
|
+
<true/>
|
|
4327
|
+
<key>KeepAlive</key>
|
|
4328
|
+
<true/>
|
|
4329
|
+
<key>StandardOutPath</key>
|
|
4330
|
+
<string>${escapeXml(PATHS.logFile)}</string>
|
|
4331
|
+
<key>StandardErrorPath</key>
|
|
4332
|
+
<string>${escapeXml(PATHS.logFile)}</string>
|
|
4333
|
+
<key>WorkingDirectory</key>
|
|
4334
|
+
<string>/tmp</string>
|
|
4335
|
+
</dict>
|
|
4336
|
+
</plist>
|
|
4337
|
+
`;
|
|
4338
|
+
}
|
|
4339
|
+
function isDaemonEnabled() {
|
|
4340
|
+
return fs6.existsSync(PATHS.launchAgentPlist);
|
|
4341
|
+
}
|
|
4342
|
+
function getDaemonStatus() {
|
|
4343
|
+
if (!isDaemonEnabled()) {
|
|
4344
|
+
return { enabled: false, loaded: false, pid: null };
|
|
4345
|
+
}
|
|
4346
|
+
const uid = process.getuid?.();
|
|
4347
|
+
if (uid === void 0) {
|
|
4348
|
+
const pids = getAllMihomoPids();
|
|
4349
|
+
return { enabled: true, loaded: pids.length > 0, pid: pids[0] ?? null };
|
|
4350
|
+
}
|
|
4351
|
+
const result = launchctl(["print", serviceTarget(uid)]);
|
|
4352
|
+
if (result.status !== 0) {
|
|
4353
|
+
return { enabled: true, loaded: false, pid: null };
|
|
4354
|
+
}
|
|
4355
|
+
const pidMatch = result.stdout.match(/\bpid = (\d+)/);
|
|
4356
|
+
const pid = pidMatch ? parseInt(pidMatch[1], 10) : null;
|
|
4357
|
+
return { enabled: true, loaded: true, pid };
|
|
4358
|
+
}
|
|
4359
|
+
function isDaemonRunning(status) {
|
|
4360
|
+
return status.loaded && status.pid !== null;
|
|
4361
|
+
}
|
|
4362
|
+
function enableDaemon() {
|
|
4363
|
+
if (!fs6.existsSync(PATHS.mihomoBinary)) {
|
|
4364
|
+
throw new Error("\u672A\u627E\u5230 mihomo \u5185\u6838\uFF0C\u8BF7\u5148\u4E0B\u8F7D\u5185\u6838");
|
|
4365
|
+
}
|
|
4366
|
+
if (!fs6.existsSync(PATHS.configFile)) {
|
|
4367
|
+
throw new Error("\u672A\u627E\u5230\u8FD0\u884C\u65F6\u914D\u7F6E\uFF0C\u8BF7\u5148\u6DFB\u52A0\u8BA2\u9605");
|
|
4368
|
+
}
|
|
4369
|
+
const uid = getUid();
|
|
4370
|
+
launchctl(["bootout", serviceTarget(uid)]);
|
|
4371
|
+
cleanupAll();
|
|
4372
|
+
fs6.mkdirSync(path4.dirname(PATHS.launchAgentPlist), { recursive: true });
|
|
4373
|
+
atomicWriteFileSync(PATHS.launchAgentPlist, buildPlist(), { mode: 384 });
|
|
4374
|
+
const result = launchctl(["bootstrap", guiDomain(uid), PATHS.launchAgentPlist]);
|
|
4375
|
+
if (result.status !== 0) {
|
|
4376
|
+
launchctl(["bootout", serviceTarget(uid)]);
|
|
4377
|
+
try {
|
|
4378
|
+
fs6.unlinkSync(PATHS.launchAgentPlist);
|
|
4379
|
+
} catch {
|
|
4297
4380
|
}
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4381
|
+
throw launchctlError("bootstrap", result);
|
|
4382
|
+
}
|
|
4383
|
+
}
|
|
4384
|
+
function disableDaemon() {
|
|
4385
|
+
const uid = process.getuid?.();
|
|
4386
|
+
if (uid !== void 0) {
|
|
4387
|
+
launchctl(["bootout", serviceTarget(uid)]);
|
|
4388
|
+
}
|
|
4389
|
+
try {
|
|
4390
|
+
fs6.unlinkSync(PATHS.launchAgentPlist);
|
|
4391
|
+
} catch {
|
|
4392
|
+
}
|
|
4393
|
+
}
|
|
4394
|
+
function restartDaemon() {
|
|
4395
|
+
const uid = getUid();
|
|
4396
|
+
if (!fs6.existsSync(PATHS.launchAgentPlist)) {
|
|
4397
|
+
throw new Error("\u4FDD\u6D3B\u672A\u542F\u7528\uFF0C\u65E0\u6CD5\u91CD\u542F");
|
|
4398
|
+
}
|
|
4399
|
+
if (launchctl(["print", serviceTarget(uid)]).status === 0) {
|
|
4400
|
+
const result2 = launchctl(["kickstart", "-k", serviceTarget(uid)]);
|
|
4401
|
+
if (result2.status !== 0) {
|
|
4402
|
+
throw launchctlError("kickstart", result2);
|
|
4306
4403
|
}
|
|
4307
|
-
|
|
4308
|
-
|
|
4404
|
+
return;
|
|
4405
|
+
}
|
|
4406
|
+
const result = launchctl(["bootstrap", guiDomain(uid), PATHS.launchAgentPlist]);
|
|
4407
|
+
if (result.status !== 0) {
|
|
4408
|
+
throw launchctlError("bootstrap", result);
|
|
4309
4409
|
}
|
|
4310
|
-
console.log("");
|
|
4311
|
-
console.log("\u6570\u636E\u76EE\u5F55\u4F4D\u7F6E:");
|
|
4312
|
-
console.log(` \u6839\u76EE\u5F55: ${USER_DATA_DIR}`);
|
|
4313
|
-
console.log(` \u5168\u5C40\u8BBE\u7F6E: ${PATHS.settingsFile}`);
|
|
4314
|
-
console.log(` \u5185\u6838\u76EE\u5F55: ${DIRS.kernel}`);
|
|
4315
|
-
console.log(` \u5185\u6838\u6587\u4EF6: ${PATHS.mihomoBinary}`);
|
|
4316
|
-
console.log(` \u8BA2\u9605\u76EE\u5F55: ${DIRS.subscriptions}`);
|
|
4317
|
-
console.log(" - cache.json (\u8BA2\u9605\u7F13\u5B58\uFF1A\u66F4\u65B0\u65F6\u95F4\u3001\u6D41\u91CF\u7B49)");
|
|
4318
|
-
console.log(" - xxx.yaml (\u8BA2\u9605\u539F\u59CB\u914D\u7F6E)");
|
|
4319
|
-
console.log(` \u8FD0\u884C\u65F6\u76EE\u5F55: ${DIRS.runtime}`);
|
|
4320
|
-
console.log(" - config.yaml (\u542F\u52A8\u65F6\u751F\u6210\uFF0Cstop \u81EA\u52A8\u6E05\u9664)");
|
|
4321
|
-
console.log(" - pid (PID \u6587\u4EF6\uFF0Cstop \u81EA\u52A8\u6E05\u9664)");
|
|
4322
|
-
console.log(` \u65E5\u5FD7\u6587\u4EF6: ${PATHS.logFile}`);
|
|
4323
|
-
console.log(` mihomo \u6570\u636E: ${DIRS.data}`);
|
|
4324
|
-
console.log(" - cache.db, Geo*.dat \u7B49 (mihomo \u81EA\u884C\u7BA1\u7406)");
|
|
4325
|
-
console.log("");
|
|
4326
|
-
console.log("\u6253\u5F00\u76EE\u5F55:");
|
|
4327
|
-
console.log(" mihomo dir open \u6253\u5F00\u6839\u76EE\u5F55");
|
|
4328
|
-
console.log(" mihomo dir open subs \u6253\u5F00\u8BA2\u9605\u76EE\u5F55");
|
|
4329
|
-
console.log(" mihomo dir open logs \u6253\u5F00\u65E5\u5FD7\u76EE\u5F55");
|
|
4330
|
-
console.log(" mihomo dir open data \u6253\u5F00 mihomo \u6570\u636E\u76EE\u5F55");
|
|
4331
|
-
console.log(" mihomo dir open runtime \u6253\u5F00\u8FD0\u884C\u65F6\u76EE\u5F55");
|
|
4332
|
-
console.log(" mihomo dir open kernel \u6253\u5F00\u5185\u6838\u76EE\u5F55");
|
|
4333
|
-
console.log("");
|
|
4334
|
-
console.log("\u73AF\u5883\u53D8\u91CF:");
|
|
4335
|
-
console.log(" MIHOMO_CLI_DIR: \u81EA\u5B9A\u4E49\u6839\u76EE\u5F55\u4F4D\u7F6E");
|
|
4336
|
-
console.log("");
|
|
4337
4410
|
}
|
|
4338
4411
|
|
|
4339
|
-
// src/
|
|
4340
|
-
function
|
|
4341
|
-
|
|
4342
|
-
${colors.cyan(colors.bold(`mihomo-cli v${VERSION}`))} (mihomo help \u67E5\u770B\u5B8C\u6574\u5E2E\u52A9)
|
|
4343
|
-
`);
|
|
4344
|
-
console.log(
|
|
4345
|
-
`\u5E38\u7528\u547D\u4EE4:
|
|
4346
|
-
${colors.bold("start")} [tun|mixed] \u542F\u52A8/\u5207\u6362\u4EE3\u7406
|
|
4347
|
-
${colors.bold("sub")} [use|update] \u8BA2\u9605\u7BA1\u7406
|
|
4348
|
-
${colors.bold("ow")} [on|off] \u8986\u5199\u914D\u7F6E
|
|
4349
|
-
${colors.bold("ui")} [zash|dash|yacd] \u6253\u5F00 Web UI
|
|
4350
|
-
`
|
|
4351
|
-
);
|
|
4412
|
+
// src/subscription.ts
|
|
4413
|
+
function isGithubUrl(url) {
|
|
4414
|
+
return /github\.com|raw\.githubusercontent\.com/i.test(url);
|
|
4352
4415
|
}
|
|
4353
|
-
function
|
|
4354
|
-
|
|
4355
|
-
`
|
|
4356
|
-
${colors.cyan(colors.bold(`mihomo-cli v${VERSION}`))}
|
|
4357
|
-
|
|
4358
|
-
\u547D\u4EE4\u522B\u540D: mihomo, mhm, mh
|
|
4359
|
-
|
|
4360
|
-
\u7528\u6CD5:
|
|
4361
|
-
mihomo <\u547D\u4EE4> [\u9009\u9879]
|
|
4362
|
-
|
|
4363
|
-
${colors.cyan("\u63A7\u5236:")}
|
|
4364
|
-
${colors.bold("start")} [tun|mixed] [-s] [-u ms] \u542F\u52A8/\u5207\u6362\u4EE3\u7406 (\u9ED8\u8BA4 mixed)
|
|
4365
|
-
[-r N] [-t ms] [-j N]
|
|
4366
|
-
${colors.bold("stop")} \u505C\u6B62\u4EE3\u7406
|
|
4367
|
-
${colors.bold("status")} \u67E5\u770B\u72B6\u6001
|
|
4368
|
-
|
|
4369
|
-
${colors.cyan("\u754C\u9762:")}
|
|
4370
|
-
${colors.bold("ui")} [zash|dash|yacd] \u6253\u5F00 Web UI (\u9ED8\u8BA4 zash)
|
|
4371
|
-
${colors.bold("log")} [-o] \u5B9E\u65F6\u65E5\u5FD7\uFF08-o \u6253\u5F00\u6587\u4EF6\uFF09
|
|
4372
|
-
${colors.bold("logs")} [\u7F16\u53F7] [-n N] [-o] \u65E5\u5FD7\u5217\u8868\uFF080=\u5F53\u524D\uFF0C1+=\u5F52\u6863\uFF09
|
|
4373
|
-
|
|
4374
|
-
${colors.cyan("\u8BA2\u9605:")}
|
|
4375
|
-
${colors.bold("subscription")} \u5217\u51FA\u6240\u6709\u8BA2\u9605\uFF08\u522B\u540D sub\uFF09
|
|
4376
|
-
${colors.bold("subscription")} use <name> \u5207\u6362\u5F53\u524D\u8BA2\u9605
|
|
4377
|
-
${colors.bold("subscription")} add <url> [name] \u6DFB\u52A0\u8BA2\u9605
|
|
4378
|
-
${colors.bold("subscription")} update [name] \u66F4\u65B0\u8BA2\u9605\uFF08\u65E0\u53C2\u66F4\u65B0\u6240\u6709\uFF09
|
|
4379
|
-
${colors.bold("subscription")} remove <name> \u5220\u9664\u8BA2\u9605
|
|
4380
|
-
${colors.bold("subscription")} web [name] \u6253\u5F00\u8BA2\u9605\u9875\u9762
|
|
4381
|
-
${colors.bold("subscription")} test [name] \u6D4B\u8BD5\u8282\u70B9\u8FDE\u901A\u6027
|
|
4382
|
-
${colors.bold("subscription")} clean [name] \u6D4B\u901F\u5E76\u6E05\u7406\u5931\u8D25\u8282\u70B9
|
|
4383
|
-
${colors.bold("test")} [-t ms] [-j N] \u5FEB\u901F\u6D4B\u8BD5\u5F53\u524D\u8282\u70B9\u8FDE\u901A\u6027
|
|
4384
|
-
${colors.bold("clean")} [-t ms] [-j N] [-r N] \u6E05\u7406\u5931\u8D25\u8282\u70B9\u5E76\u81EA\u52A8\u91CD\u542F
|
|
4385
|
-
|
|
4386
|
-
${colors.cyan("\u914D\u7F6E:")}
|
|
4387
|
-
${colors.bold("overwrite")} \u67E5\u770B\u8986\u5199\u72B6\u6001\uFF08\u522B\u540D ow\uFF09
|
|
4388
|
-
${colors.bold("overwrite")} on|off \u542F\u7528/\u7981\u7528\u8986\u5199\u914D\u7F6E
|
|
4389
|
-
${colors.bold("directory")} \u663E\u793A\u6570\u636E\u76EE\u5F55\u4F4D\u7F6E\uFF08\u522B\u540D dir\uFF09
|
|
4390
|
-
${colors.bold("directory")} open [target] \u6253\u5F00\u76EE\u5F55: root|subs|logs|runtime|...
|
|
4391
|
-
|
|
4392
|
-
${colors.cyan("\u7CFB\u7EDF:")}
|
|
4393
|
-
${colors.bold("kernel")} [--mirror [\u955C\u50CF]] \u66F4\u65B0\u5185\u6838\uFF08\u9ED8\u8BA4\u76F4\u8FDE\uFF0C--mirror \u4F7F\u7528 v6\uFF09
|
|
4394
|
-
${colors.bold("update")} \u66F4\u65B0 mihomo-cli (npm install -g)
|
|
4395
|
-
${colors.bold("reset")} [\u76EE\u6807...] [--full] \u91CD\u7F6E: \u7559\u7A7A\u4FDD\u7559\u8BBE\u7F6E/\u5185\u6838/\u8986\u5199, \u6307\u5B9A\u76EE\u6807\u5220\u5BF9\u5E94\u9879, --full \u5220\u5168\u90E8
|
|
4396
|
-
${colors.bold("help")}, -h \u663E\u793A\u5E2E\u52A9
|
|
4397
|
-
${colors.bold("version")}, -v \u663E\u793A\u7248\u672C
|
|
4398
|
-
|
|
4399
|
-
${colors.cyan("\u793A\u4F8B:")}
|
|
4400
|
-
mihomo start # \u542F\u52A8/\u91CD\u542F Mixed \u6A21\u5F0F
|
|
4401
|
-
mihomo start tun # \u5207\u6362\u5230 TUN \u900F\u660E\u4EE3\u7406\u6A21\u5F0F
|
|
4402
|
-
mihomo start -s # \u8DF3\u8FC7\u81EA\u52A8\u66F4\u65B0\u8BA2\u9605
|
|
4403
|
-
mihomo start -u 30000 # \u81EA\u52A8\u66F4\u65B0\u8D85\u65F6 30 \u79D2 (\u9ED8\u8BA4 10s)
|
|
4404
|
-
mihomo sub add <url> # \u6DFB\u52A0\u8BA2\u9605 (sub \u662F subscription \u522B\u540D)
|
|
4405
|
-
mihomo ui # \u6253\u5F00 Web UI
|
|
4406
|
-
|
|
4407
|
-
${colors.cyan("\u6A21\u5F0F\u8BF4\u660E:")}
|
|
4408
|
-
mixed HTTP + SOCKS5 \u6DF7\u5408\u7AEF\u53E3 (\u9ED8\u8BA4)
|
|
4409
|
-
tun \u900F\u660E\u4EE3\u7406\uFF0C\u5168\u5C40\u81EA\u52A8\u8DEF\u7531\uFF0C\u9700\u8981 sudo
|
|
4410
|
-
|
|
4411
|
-
${colors.cyan("\u6570\u636E\u76EE\u5F55:")}
|
|
4412
|
-
\u73AF\u5883\u53D8\u91CF MIHOMO_CLI_DIR \u53EF\u81EA\u5B9A\u4E49\u4F4D\u7F6E
|
|
4413
|
-
\u9ED8\u8BA4: ${USER_DATA_DIR}
|
|
4414
|
-
`
|
|
4415
|
-
);
|
|
4416
|
-
}
|
|
4417
|
-
function printVersion() {
|
|
4418
|
-
const kv = getKernelVersion() || "\u672A\u5B89\u88C5";
|
|
4419
|
-
console.log(colors.cyan(colors.bold(`mihomo-cli v${VERSION}`)));
|
|
4420
|
-
console.log(`${colors.gray("\u5185\u6838: ")}${kv}`);
|
|
4421
|
-
console.log(`${colors.gray("\u6570\u636E\u76EE\u5F55: ")}${USER_DATA_DIR}`);
|
|
4416
|
+
function getDefaultUpdateInterval(url) {
|
|
4417
|
+
return isGithubUrl(url) ? DEFAULT_UPDATE_INTERVAL_HOURS_GITHUB : DEFAULT_UPDATE_INTERVAL_HOURS;
|
|
4422
4418
|
}
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
import { spawnSync as spawnSync4 } from "child_process";
|
|
4426
|
-
import fs6 from "fs";
|
|
4427
|
-
import path4 from "path";
|
|
4428
|
-
|
|
4429
|
-
// node_modules/compare-versions/lib/esm/utils.js
|
|
4430
|
-
var semver = /^[v^~<>=]*?(\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+))?(?:-([\da-z\-]+(?:\.[\da-z\-]+)*))?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?)?)?$/i;
|
|
4431
|
-
var validateAndParse = (version) => {
|
|
4432
|
-
if (typeof version !== "string") {
|
|
4433
|
-
throw new TypeError("Invalid argument expected string");
|
|
4434
|
-
}
|
|
4435
|
-
const match = version.match(semver);
|
|
4436
|
-
if (!match) {
|
|
4437
|
-
throw new Error(`Invalid argument not valid semver ('${version}' received)`);
|
|
4438
|
-
}
|
|
4439
|
-
match.shift();
|
|
4440
|
-
return match;
|
|
4441
|
-
};
|
|
4442
|
-
var isWildcard = (s) => s === "*" || s === "x" || s === "X";
|
|
4443
|
-
var tryParse = (v) => {
|
|
4444
|
-
const n = parseInt(v, 10);
|
|
4445
|
-
return isNaN(n) ? v : n;
|
|
4446
|
-
};
|
|
4447
|
-
var forceType = (a, b) => typeof a !== typeof b ? [String(a), String(b)] : [a, b];
|
|
4448
|
-
var compareStrings = (a, b) => {
|
|
4449
|
-
if (isWildcard(a) || isWildcard(b))
|
|
4450
|
-
return 0;
|
|
4451
|
-
const [ap, bp] = forceType(tryParse(a), tryParse(b));
|
|
4452
|
-
if (ap > bp)
|
|
4453
|
-
return 1;
|
|
4454
|
-
if (ap < bp)
|
|
4455
|
-
return -1;
|
|
4456
|
-
return 0;
|
|
4457
|
-
};
|
|
4458
|
-
var compareSegments = (a, b) => {
|
|
4459
|
-
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
4460
|
-
const r = compareStrings(a[i] || "0", b[i] || "0");
|
|
4461
|
-
if (r !== 0)
|
|
4462
|
-
return r;
|
|
4463
|
-
}
|
|
4464
|
-
return 0;
|
|
4465
|
-
};
|
|
4466
|
-
|
|
4467
|
-
// node_modules/compare-versions/lib/esm/compareVersions.js
|
|
4468
|
-
var compareVersions = (v1, v2) => {
|
|
4469
|
-
const n1 = validateAndParse(v1);
|
|
4470
|
-
const n2 = validateAndParse(v2);
|
|
4471
|
-
const p1 = n1.pop();
|
|
4472
|
-
const p2 = n2.pop();
|
|
4473
|
-
const r = compareSegments(n1, n2);
|
|
4474
|
-
if (r !== 0)
|
|
4475
|
-
return r;
|
|
4476
|
-
if (p1 && p2) {
|
|
4477
|
-
return compareSegments(p1.split("."), p2.split("."));
|
|
4478
|
-
} else if (p1 || p2) {
|
|
4479
|
-
return p1 ? -1 : 1;
|
|
4480
|
-
}
|
|
4481
|
-
return 0;
|
|
4482
|
-
};
|
|
4483
|
-
|
|
4484
|
-
// src/kernel.ts
|
|
4485
|
-
var GITHUB_REPO = "MetaCubeX/mihomo";
|
|
4486
|
-
var KERNEL_HTTP_TIMEOUT = 12e4;
|
|
4487
|
-
var KERNEL_DOWNLOAD_TIMEOUT = 18e4;
|
|
4488
|
-
var HTTP_CLIENT = createHttpClient({ timeout: KERNEL_HTTP_TIMEOUT });
|
|
4489
|
-
function withMirror(url, mirror) {
|
|
4490
|
-
if (mirror && (url.startsWith("https://github.com/") || url.startsWith("https://api.github.com/"))) {
|
|
4491
|
-
return mirror + url;
|
|
4492
|
-
}
|
|
4493
|
-
return url;
|
|
4419
|
+
function resolveUpdateInterval(url, cachedInterval) {
|
|
4420
|
+
return cachedInterval && cachedInterval > 0 ? cachedInterval : getDefaultUpdateInterval(url);
|
|
4494
4421
|
}
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
return arch;
|
|
4422
|
+
var YAML_DUMP_OPTS = { indent: 2, lineWidth: -1, schema: CORE_SCHEMA };
|
|
4423
|
+
var HTTP_CLIENT = createHttpClient({ timeout: 6e4 });
|
|
4424
|
+
function isMultiUrl(url) {
|
|
4425
|
+
return url.includes(",");
|
|
4500
4426
|
}
|
|
4501
|
-
function
|
|
4502
|
-
|
|
4503
|
-
const matchingAssets = assets.filter(
|
|
4504
|
-
(a) => a.name.startsWith(prefix) && a.name.endsWith(".gz") || a.name.startsWith(`${prefix}-`) && a.name.endsWith(".gz")
|
|
4505
|
-
);
|
|
4506
|
-
if (matchingAssets.length === 0) return null;
|
|
4507
|
-
if (matchingAssets.length === 1) return matchingAssets[0];
|
|
4508
|
-
const standardAsset = matchingAssets.find((a) => {
|
|
4509
|
-
const nameWithoutGz = a.name.slice(0, -3);
|
|
4510
|
-
const parts = nameWithoutGz.split("-");
|
|
4511
|
-
const lastPart = parts[parts.length - 1];
|
|
4512
|
-
return /^v?\d+\.\d+\.\d+/.test(lastPart) && !nameWithoutGz.includes("-go");
|
|
4513
|
-
});
|
|
4514
|
-
return standardAsset || matchingAssets[0];
|
|
4427
|
+
function splitUrls(url) {
|
|
4428
|
+
return url.split(",").map((u) => u.trim()).filter(Boolean);
|
|
4515
4429
|
}
|
|
4516
|
-
|
|
4517
|
-
const
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
if (!Array.isArray(releases) || releases.length === 0) {
|
|
4521
|
-
throw new Error("\u65E0\u6CD5\u83B7\u53D6\u7248\u672C\u4FE1\u606F");
|
|
4430
|
+
function loadSubscriptionConfig(subName) {
|
|
4431
|
+
const rawContent = readSubscriptionRawConfig(subName);
|
|
4432
|
+
if (!rawContent) {
|
|
4433
|
+
throw new Error(`\u672A\u627E\u5230\u8BA2\u9605\u914D\u7F6E "${subName}"`);
|
|
4522
4434
|
}
|
|
4523
|
-
const
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4435
|
+
const raw = parseYamlOrJson(rawContent, "\u8BA2\u9605\u5185\u5BB9");
|
|
4436
|
+
return {
|
|
4437
|
+
raw,
|
|
4438
|
+
proxies: raw.proxies || [],
|
|
4439
|
+
proxyGroups: raw["proxy-groups"] || []
|
|
4440
|
+
};
|
|
4527
4441
|
}
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4442
|
+
function saveSubscriptionConfig(subName, parsed) {
|
|
4443
|
+
normalizeProxyNamesBeforeSave(parsed);
|
|
4444
|
+
parsed.raw.proxies = parsed.proxies;
|
|
4445
|
+
parsed.raw["proxy-groups"] = parsed.proxyGroups;
|
|
4446
|
+
saveSubscriptionRawConfig(subName, dump(parsed.raw, YAML_DUMP_OPTS));
|
|
4447
|
+
}
|
|
4448
|
+
function parseUserInfo(header) {
|
|
4449
|
+
if (!header) return null;
|
|
4450
|
+
const info = {};
|
|
4451
|
+
const parts = header.split(";").map((p) => p.trim());
|
|
4452
|
+
for (const part of parts) {
|
|
4453
|
+
const [key, val] = part.split("=").map((s) => s.trim());
|
|
4454
|
+
if (key && val !== void 0) {
|
|
4455
|
+
const numVal = parseFloat(val);
|
|
4456
|
+
info[key] = Number.isNaN(numVal) ? 0 : numVal;
|
|
4541
4457
|
}
|
|
4542
4458
|
}
|
|
4459
|
+
return info;
|
|
4460
|
+
}
|
|
4461
|
+
function parsePositiveInterval(header) {
|
|
4462
|
+
if (!header) return null;
|
|
4463
|
+
const n = parseInt(header, 10);
|
|
4464
|
+
return Number.isFinite(n) && n > 0 ? n : null;
|
|
4465
|
+
}
|
|
4466
|
+
function parseUsernameFromContentDisposition(header) {
|
|
4467
|
+
if (!header) return null;
|
|
4468
|
+
const match = header.match(/filename\s*=\s*["']?([^"';\s]+)["']?/i);
|
|
4469
|
+
if (!match) return null;
|
|
4470
|
+
const filename = match[1];
|
|
4471
|
+
const parts = filename.split("/");
|
|
4472
|
+
return parts[parts.length - 1] || null;
|
|
4473
|
+
}
|
|
4474
|
+
function extractSubscriptionMeta(headers) {
|
|
4543
4475
|
return {
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
release: latest
|
|
4476
|
+
userInfo: parseUserInfo(headers?.get("subscription-userinfo") ?? null),
|
|
4477
|
+
updateInterval: parsePositiveInterval(headers?.get("profile-update-interval")),
|
|
4478
|
+
webPageUrl: headers?.get("profile-web-page-url") || null,
|
|
4479
|
+
username: parseUsernameFromContentDisposition(headers?.get("content-disposition") ?? null)
|
|
4549
4480
|
};
|
|
4550
4481
|
}
|
|
4551
|
-
function
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
const found = findBinaryInDir(fullPath, maxDepth - 1);
|
|
4559
|
-
if (found) return found;
|
|
4560
|
-
continue;
|
|
4561
|
-
}
|
|
4562
|
-
if (f === "mihomo") return fullPath;
|
|
4563
|
-
if (f.includes("mihomo") && !f.endsWith(".gz")) return fullPath;
|
|
4482
|
+
function saveSubscriptionMeta(subName, meta) {
|
|
4483
|
+
const cacheData = { updated_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
4484
|
+
if (meta.userInfo) {
|
|
4485
|
+
cacheData.upload = meta.userInfo.upload;
|
|
4486
|
+
cacheData.download = meta.userInfo.download;
|
|
4487
|
+
cacheData.total = meta.userInfo.total;
|
|
4488
|
+
cacheData.expire = meta.userInfo.expire;
|
|
4564
4489
|
}
|
|
4565
|
-
|
|
4490
|
+
if (meta.updateInterval) cacheData.update_interval = meta.updateInterval;
|
|
4491
|
+
if (meta.webPageUrl) cacheData.web_page_url = meta.webPageUrl;
|
|
4492
|
+
if (meta.username) cacheData.username = meta.username;
|
|
4493
|
+
saveSubscriptionCache(subName, cacheData);
|
|
4566
4494
|
}
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
const downloadUrl = withMirror(asset.browser_download_url, mirror);
|
|
4582
|
-
const tempPath = path4.join(DIRS.kernel, asset.name);
|
|
4583
|
-
const sizeMB = (asset.size / 1024 / 1024).toFixed(2);
|
|
4584
|
-
if (mirror && progressCallback) {
|
|
4585
|
-
progressCallback("\u63D0\u793A: \u7ECF\u7B2C\u4E09\u65B9\u955C\u50CF\u4E2D\u8F6C\u4E0B\u8F7D\uFF0C\u65E0\u6CD5\u9A8C\u8BC1\u6765\u6E90\u5B8C\u6574\u6027\uFF0C\u5EFA\u8BAE\u76F4\u8FDE\u6216\u81EA\u884C\u6821\u9A8C\u4EA7\u7269");
|
|
4586
|
-
}
|
|
4587
|
-
if (progressCallback) {
|
|
4588
|
-
progressCallback(`\u4E0B\u8F7D\u5185\u6838: ${asset.name} (${sizeMB} MB)`);
|
|
4495
|
+
function formatProxySummary(info) {
|
|
4496
|
+
const parts = [];
|
|
4497
|
+
if (info.proxyGroups && info.proxyGroups > 0) parts.push(`${info.proxyGroups} \u7EC4`);
|
|
4498
|
+
parts.push(`${info.proxies || 0} \u8282\u70B9`);
|
|
4499
|
+
return parts.join(", ");
|
|
4500
|
+
}
|
|
4501
|
+
function getActiveSubscription() {
|
|
4502
|
+
const subs = getSubscriptions();
|
|
4503
|
+
if (subs.length === 0) return null;
|
|
4504
|
+
const settings = readSettings();
|
|
4505
|
+
const activeName = settings.active_subscription;
|
|
4506
|
+
if (activeName) {
|
|
4507
|
+
const found = subs.find((s) => s.name === activeName);
|
|
4508
|
+
if (found) return found;
|
|
4589
4509
|
}
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4510
|
+
return subs[0];
|
|
4511
|
+
}
|
|
4512
|
+
function findSubscriptionFuzzy(subs, pattern) {
|
|
4513
|
+
const lowerPattern = pattern.toLowerCase();
|
|
4514
|
+
const exact = [];
|
|
4515
|
+
const prefix = [];
|
|
4516
|
+
const includes = [];
|
|
4517
|
+
for (const s of subs) {
|
|
4518
|
+
const name = s.name.toLowerCase();
|
|
4519
|
+
if (name === lowerPattern) {
|
|
4520
|
+
exact.push(s);
|
|
4521
|
+
} else if (name.startsWith(lowerPattern)) {
|
|
4522
|
+
prefix.push(s);
|
|
4523
|
+
} else if (name.includes(lowerPattern)) {
|
|
4524
|
+
includes.push(s);
|
|
4599
4525
|
}
|
|
4600
|
-
throw new Error(`\u4E0B\u8F7D\u5931\u8D25 (curl \u9000\u51FA\u7801 ${curlResult.status})`);
|
|
4601
|
-
}
|
|
4602
|
-
if (!fs6.existsSync(tempPath)) {
|
|
4603
|
-
throw new Error("\u4E0B\u8F7D\u5931\u8D25: \u6587\u4EF6\u672A\u751F\u6210");
|
|
4604
4526
|
}
|
|
4605
|
-
if (
|
|
4606
|
-
|
|
4527
|
+
if (exact.length > 0) return exact;
|
|
4528
|
+
if (prefix.length > 0) return prefix;
|
|
4529
|
+
return includes;
|
|
4530
|
+
}
|
|
4531
|
+
function pickSingleSubscription(subs, pattern) {
|
|
4532
|
+
if (subs.length === 0) {
|
|
4533
|
+
console.error(`\u9519\u8BEF: \u672A\u627E\u5230\u5339\u914D "${pattern}" \u7684\u8BA2\u9605`);
|
|
4534
|
+
process.exit(1);
|
|
4607
4535
|
}
|
|
4608
|
-
|
|
4609
|
-
|
|
4536
|
+
if (subs.length === 1) return subs[0];
|
|
4537
|
+
console.error("\u9519\u8BEF: \u5339\u914D\u5230\u591A\u4E2A\u8BA2\u9605\uFF0C\u8BF7\u66F4\u7CBE\u786E\u6307\u5B9A");
|
|
4538
|
+
console.log("\n\u5339\u914D\u7684\u8BA2\u9605:");
|
|
4539
|
+
for (const s of subs) console.log(` ${s.name}`);
|
|
4540
|
+
process.exit(1);
|
|
4541
|
+
}
|
|
4542
|
+
async function downloadSubscription(url, subName = "default", signal) {
|
|
4543
|
+
let response;
|
|
4610
4544
|
try {
|
|
4611
|
-
|
|
4612
|
-
const tarResult = spawnSync4("tar", ["-xzf", tempPath, "-C", extractPath], { stdio: ["ignore", "ignore", "inherit"], timeout: 6e4 });
|
|
4613
|
-
if (tarResult.error) throw tarResult.error;
|
|
4614
|
-
if (tarResult.status !== 0) throw new Error(`tar \u9000\u51FA\u7801 ${tarResult.status}`);
|
|
4615
|
-
} else if (tempPath.endsWith(".gz")) {
|
|
4616
|
-
const baseName = path4.basename(tempPath, ".gz");
|
|
4617
|
-
const outputPath = path4.join(extractPath, baseName);
|
|
4618
|
-
const gzipResult = spawnSync4("gzip", ["-dc", tempPath], { maxBuffer: 256 * 1024 * 1024, timeout: 6e4 });
|
|
4619
|
-
if (gzipResult.error) throw gzipResult.error;
|
|
4620
|
-
if (gzipResult.status !== 0) throw new Error(`gzip \u9000\u51FA\u7801 ${gzipResult.status}`);
|
|
4621
|
-
fs6.writeFileSync(outputPath, gzipResult.stdout, { mode: 493 });
|
|
4622
|
-
extractedBinary = outputPath;
|
|
4623
|
-
}
|
|
4545
|
+
response = await HTTP_CLIENT.get(url, { responseType: "text", signal });
|
|
4624
4546
|
} catch (e) {
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4547
|
+
const maskedUrl = maskUrl(url);
|
|
4548
|
+
let errorMsg = `\u83B7\u53D6\u8BA2\u9605\u5931\u8D25: ${e.message}`;
|
|
4549
|
+
const err = e;
|
|
4550
|
+
if (err.response) {
|
|
4551
|
+
errorMsg += ` (HTTP ${err.response.status})`;
|
|
4628
4552
|
}
|
|
4629
|
-
|
|
4553
|
+
errorMsg += `
|
|
4554
|
+
URL: ${maskedUrl}`;
|
|
4555
|
+
throw new Error(errorMsg);
|
|
4630
4556
|
}
|
|
4631
|
-
const
|
|
4632
|
-
if (!
|
|
4633
|
-
|
|
4634
|
-
fs6.unlinkSync(tempPath);
|
|
4635
|
-
} catch {
|
|
4636
|
-
}
|
|
4637
|
-
throw new Error("\u89E3\u538B\u540E\u672A\u627E\u5230\u53EF\u6267\u884C\u6587\u4EF6");
|
|
4557
|
+
const content = response.data;
|
|
4558
|
+
if (!content?.trim()) {
|
|
4559
|
+
throw new Error("\u8BA2\u9605\u5185\u5BB9\u4E3A\u7A7A");
|
|
4638
4560
|
}
|
|
4639
|
-
const
|
|
4640
|
-
if (
|
|
4641
|
-
|
|
4642
|
-
|
|
4561
|
+
const parsed = parseYamlOrJson(content, "\u8BA2\u9605\u5185\u5BB9");
|
|
4562
|
+
if (!parsed) throw new Error("\u8BA2\u9605\u5185\u5BB9\u4E3A\u7A7A");
|
|
4563
|
+
saveSubscriptionRawConfig(subName, content);
|
|
4564
|
+
const meta = extractSubscriptionMeta(response.headers);
|
|
4565
|
+
saveSubscriptionMeta(subName, meta);
|
|
4566
|
+
const proxies = parsed.proxies;
|
|
4567
|
+
const proxyGroups = parsed["proxy-groups"];
|
|
4568
|
+
return {
|
|
4569
|
+
proxies: proxies ? proxies.length : 0,
|
|
4570
|
+
proxyGroups: proxyGroups ? proxyGroups.length : 0,
|
|
4571
|
+
userInfo: meta.userInfo,
|
|
4572
|
+
updateInterval: meta.updateInterval,
|
|
4573
|
+
webPageUrl: meta.webPageUrl,
|
|
4574
|
+
username: meta.username
|
|
4575
|
+
};
|
|
4576
|
+
}
|
|
4577
|
+
async function downloadMergedSubscription(urls, subName, signal) {
|
|
4578
|
+
const responses = await Promise.all(
|
|
4579
|
+
urls.map(async (url, index) => {
|
|
4643
4580
|
try {
|
|
4644
|
-
|
|
4645
|
-
|
|
4581
|
+
const response = await HTTP_CLIENT.get(url, { responseType: "text", signal });
|
|
4582
|
+
return { url, index, response, error: null };
|
|
4583
|
+
} catch (e) {
|
|
4584
|
+
return { url, index, response: null, error: e };
|
|
4646
4585
|
}
|
|
4586
|
+
})
|
|
4587
|
+
);
|
|
4588
|
+
for (const r of responses) {
|
|
4589
|
+
if (r.error) {
|
|
4590
|
+
const maskedUrl = maskUrl(r.url);
|
|
4591
|
+
throw new Error(`\u5408\u5E76\u8BA2\u9605\u7B2C ${r.index + 1} \u4E2A URL \u83B7\u53D6\u5931\u8D25: ${r.error.message}
|
|
4592
|
+
URL: ${maskedUrl}`);
|
|
4647
4593
|
}
|
|
4648
|
-
fs6.renameSync(foundBinary, targetPath);
|
|
4649
4594
|
}
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
const
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4595
|
+
const parsed = responses.map((r, i) => {
|
|
4596
|
+
const content = r.response?.data;
|
|
4597
|
+
if (!content?.trim()) throw new Error(`\u5408\u5E76\u8BA2\u9605\u7B2C ${i + 1} \u4E2A URL \u5185\u5BB9\u4E3A\u7A7A`);
|
|
4598
|
+
return parseYamlOrJson(content, `\u5408\u5E76\u8BA2\u9605\u7B2C ${i + 1} \u4E2A`);
|
|
4599
|
+
});
|
|
4600
|
+
const base = parsed[0];
|
|
4601
|
+
const baseProxies = base.proxies || [];
|
|
4602
|
+
const seenNames = new Set(baseProxies.map((p) => p.name));
|
|
4603
|
+
for (let i = 1; i < parsed.length; i++) {
|
|
4604
|
+
const extraProxies = parsed[i].proxies || [];
|
|
4605
|
+
for (const proxy of extraProxies) {
|
|
4606
|
+
if (!seenNames.has(proxy.name)) {
|
|
4607
|
+
baseProxies.push(proxy);
|
|
4608
|
+
seenNames.add(proxy.name);
|
|
4609
|
+
}
|
|
4664
4610
|
}
|
|
4665
|
-
throw new Error(`\u5185\u6838\u81EA\u68C0\u5931\u8D25\uFF08\u53EF\u80FD\u4E0B\u8F7D\u635F\u574F\u6216\u67B6\u6784\u4E0D\u5339\u914D\uFF09\uFF0C\u5DF2\u5220\u9664
|
|
4666
|
-
\u9000\u51FA\u7801: ${check.status}
|
|
4667
|
-
\u8F93\u51FA: ${checkOutput || "(\u7A7A)"}`);
|
|
4668
4611
|
}
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4612
|
+
base.proxies = baseProxies;
|
|
4613
|
+
const mergedContent = dump(base, YAML_DUMP_OPTS);
|
|
4614
|
+
saveSubscriptionRawConfig(subName, mergedContent);
|
|
4615
|
+
const meta = extractSubscriptionMeta(responses[0].response?.headers);
|
|
4616
|
+
saveSubscriptionMeta(subName, meta);
|
|
4617
|
+
const proxyGroups = base["proxy-groups"];
|
|
4618
|
+
return {
|
|
4619
|
+
proxies: baseProxies.length,
|
|
4620
|
+
proxyGroups: proxyGroups ? proxyGroups.length : 0,
|
|
4621
|
+
userInfo: meta.userInfo,
|
|
4622
|
+
updateInterval: meta.updateInterval,
|
|
4623
|
+
webPageUrl: meta.webPageUrl,
|
|
4624
|
+
username: meta.username
|
|
4625
|
+
};
|
|
4675
4626
|
}
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
const effectiveMirror = mirrorInfo.mirror;
|
|
4681
|
-
if (effectiveMirror) {
|
|
4682
|
-
const mirrorDesc = mirrorInfo.type === "all" ? " (API\u548C\u4E0B\u8F7D\u5747\u4F7F\u7528\u955C\u50CF)" : " (\u4E0B\u8F7D\u65F6\u4F7F\u7528\u955C\u50CF)";
|
|
4683
|
-
console.log(`\u955C\u50CF: ${effectiveMirror}${mirrorDesc}`);
|
|
4627
|
+
function prepareConfigForStart(mode, subName = "default") {
|
|
4628
|
+
const rawContent = readSubscriptionRawConfig(subName);
|
|
4629
|
+
if (!rawContent) {
|
|
4630
|
+
throw new Error(`\u672A\u627E\u5230\u8BA2\u9605\u914D\u7F6E "${subName}"\uFF0C\u8BF7\u5148\u6DFB\u52A0\u8BA2\u9605`);
|
|
4684
4631
|
}
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
console.log(" mihomo kernel --mirror-all hk.gh-proxy.org # API\u548C\u4E0B\u8F7D\u90FD\u4F7F\u7528\u6307\u5B9A\u955C\u50CF");
|
|
4692
|
-
console.log("\n\u53EF\u7528\u955C\u50CF:");
|
|
4693
|
-
for (const m of AVAILABLE_MIRRORS) {
|
|
4694
|
-
const isCurrent = effectiveMirror && (effectiveMirror.includes(`//${m}/`) || effectiveMirror.includes(`//${m}:`) || effectiveMirror.endsWith(`//${m}`));
|
|
4695
|
-
console.log(` ${m}${isCurrent ? " (\u5F53\u524D)" : ""}`);
|
|
4632
|
+
const buildResult = buildConfig(rawContent, mode);
|
|
4633
|
+
if (buildResult.warnings.length > 0) {
|
|
4634
|
+
for (const warning of buildResult.warnings) {
|
|
4635
|
+
console.log(`${colors.yellow("\u81EA\u52A8\u4FEE\u590D:")} ${warning}`);
|
|
4636
|
+
}
|
|
4637
|
+
console.log("");
|
|
4696
4638
|
}
|
|
4697
|
-
|
|
4698
|
-
|
|
4639
|
+
writeMihomoConfig(buildResult.config);
|
|
4640
|
+
writeDebugConfig(buildResult);
|
|
4641
|
+
const proxies = buildResult.config.proxies;
|
|
4642
|
+
const proxyGroups = buildResult.config["proxy-groups"];
|
|
4643
|
+
return {
|
|
4644
|
+
proxies: proxies ? proxies.length : 0,
|
|
4645
|
+
proxyGroups: proxyGroups ? proxyGroups.length : 0
|
|
4646
|
+
};
|
|
4647
|
+
}
|
|
4648
|
+
function needsAutoUpdate(sub) {
|
|
4649
|
+
if (!sub.updated_at) return true;
|
|
4650
|
+
const lastUpdate = new Date(sub.updated_at).getTime();
|
|
4651
|
+
if (Number.isNaN(lastUpdate)) return true;
|
|
4652
|
+
const intervalHours = resolveUpdateInterval(sub.url, sub.update_interval);
|
|
4653
|
+
const intervalMs = intervalHours * 60 * 60 * 1e3;
|
|
4654
|
+
return Date.now() - lastUpdate > intervalMs;
|
|
4655
|
+
}
|
|
4656
|
+
async function tryUpdateOne(sub, signal) {
|
|
4699
4657
|
try {
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
console.log(`\u6700\u65B0: ${info.latest}`);
|
|
4704
|
-
if (!info.needsUpdate) {
|
|
4705
|
-
console.log("\u5DF2\u662F\u6700\u65B0\u7248\u672C");
|
|
4658
|
+
let info;
|
|
4659
|
+
if (isMultiUrl(sub.url)) {
|
|
4660
|
+
info = await downloadMergedSubscription(splitUrls(sub.url), sub.name, signal);
|
|
4706
4661
|
} else {
|
|
4707
|
-
|
|
4708
|
-
const result = await downloadKernel((msg) => console.log(msg), mirrorInfo.mirror, info.release);
|
|
4709
|
-
console.log(`
|
|
4710
|
-
\u5DF2\u66F4\u65B0\u5230 ${result.version}`);
|
|
4662
|
+
info = await downloadSubscription(sub.url, sub.name, signal);
|
|
4711
4663
|
}
|
|
4664
|
+
return { name: sub.name, success: true, proxies: info.proxies, proxyGroups: info.proxyGroups };
|
|
4712
4665
|
} catch (e) {
|
|
4713
|
-
|
|
4714
|
-
\u66F4\u65B0\u5931\u8D25: ${e.message}`);
|
|
4715
|
-
const err = e;
|
|
4716
|
-
if (err.response?.data) {
|
|
4717
|
-
if (err.response.data.message) {
|
|
4718
|
-
console.error(`\u539F\u56E0: ${err.response.data.message}`);
|
|
4719
|
-
}
|
|
4720
|
-
if (err.response.data.documentation_url) {
|
|
4721
|
-
console.error(`\u6587\u6863: ${err.response.data.documentation_url}`);
|
|
4722
|
-
}
|
|
4723
|
-
}
|
|
4724
|
-
process.exit(1);
|
|
4666
|
+
return { name: sub.name, success: false, error: e.message };
|
|
4725
4667
|
}
|
|
4726
4668
|
}
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
return;
|
|
4734
|
-
}
|
|
4735
|
-
viewLogWithTail(logPath, { follow: true, lines: 50 });
|
|
4669
|
+
function printUpdateResult(r) {
|
|
4670
|
+
if (r.success) {
|
|
4671
|
+
console.log(`${colors.green("\u2713")} ${r.name}: ${colors.green("\u5DF2\u66F4\u65B0")} (${formatProxySummary(r)})`);
|
|
4672
|
+
} else {
|
|
4673
|
+
console.log(`${colors.red("\u2717")} ${r.name}: ${colors.red("\u5931\u8D25")} (${(r.error || "").split("\n")[0]})`);
|
|
4674
|
+
}
|
|
4736
4675
|
}
|
|
4737
|
-
function
|
|
4738
|
-
const
|
|
4739
|
-
const
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
}
|
|
4676
|
+
async function autoUpdateStaleSubscription(options = {}) {
|
|
4677
|
+
const allSubs = getSubscriptionsWithCache();
|
|
4678
|
+
const staleSubs = allSubs.filter(needsAutoUpdate);
|
|
4679
|
+
if (staleSubs.length === 0) {
|
|
4680
|
+
return { total: 0, updated: 0, failed: 0 };
|
|
4681
|
+
}
|
|
4682
|
+
if (staleSubs.length === 1) {
|
|
4683
|
+
const sub = staleSubs[0];
|
|
4684
|
+
const interval = resolveUpdateInterval(sub.url, sub.update_interval);
|
|
4685
|
+
console.log(`\u8BA2\u9605 "${sub.name}" \u8D85\u8FC7 ${interval} \u5C0F\u65F6\u672A\u66F4\u65B0\uFF0C\u6B63\u5728\u66F4\u65B0...`);
|
|
4686
|
+
} else {
|
|
4687
|
+
console.log(`\u68C0\u67E5\u5230 ${staleSubs.length} \u4E2A\u8BA2\u9605\u9700\u8981\u66F4\u65B0\uFF0C\u6B63\u5728\u5E76\u884C\u66F4\u65B0...`);
|
|
4688
|
+
}
|
|
4689
|
+
const timeoutMs = options.timeout ?? DEFAULT_AUTO_UPDATE_TIMEOUT;
|
|
4690
|
+
const controller = new AbortController();
|
|
4691
|
+
let results;
|
|
4692
|
+
try {
|
|
4693
|
+
results = await withTimeout(Promise.all(staleSubs.map((sub) => tryUpdateOne(sub, controller.signal))), timeoutMs);
|
|
4694
|
+
} catch (e) {
|
|
4695
|
+
if (e instanceof TimeoutError) {
|
|
4696
|
+
controller.abort();
|
|
4697
|
+
console.log(colors.yellow(`\u81EA\u52A8\u66F4\u65B0\u8D85\u65F6 (${timeoutMs / 1e3}s)\uFF0C\u8DF3\u8FC7\u66F4\u65B0\uFF0C\u4F7F\u7528\u7F13\u5B58\u914D\u7F6E`));
|
|
4698
|
+
return { total: staleSubs.length, updated: 0, failed: staleSubs.length };
|
|
4759
4699
|
}
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4700
|
+
throw e;
|
|
4701
|
+
}
|
|
4702
|
+
let updatedCount = 0;
|
|
4703
|
+
for (const r of results) {
|
|
4704
|
+
if (r.success) updatedCount++;
|
|
4705
|
+
printUpdateResult(r);
|
|
4706
|
+
}
|
|
4707
|
+
return { total: staleSubs.length, updated: updatedCount, failed: staleSubs.length - updatedCount };
|
|
4708
|
+
}
|
|
4709
|
+
var API_BASE = `http://${BASE_CONFIG["external-controller"]}`;
|
|
4710
|
+
async function testProxyDelay(proxyName, timeout, testUrl, client, apiBase = API_BASE) {
|
|
4711
|
+
const encodedName = encodeURIComponent(proxyName);
|
|
4712
|
+
const url = `${apiBase}/proxies/${encodedName}/delay?timeout=${timeout}&url=${encodeURIComponent(testUrl)}`;
|
|
4713
|
+
try {
|
|
4714
|
+
const response = await client.get(url);
|
|
4715
|
+
const data = JSON.parse(response.data);
|
|
4716
|
+
if (data.delay && data.delay > 0) {
|
|
4717
|
+
return { name: proxyName, delay: data.delay };
|
|
4764
4718
|
}
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4719
|
+
return { name: proxyName, delay: null, error: data.message || "no delay" };
|
|
4720
|
+
} catch (e) {
|
|
4721
|
+
const err = e;
|
|
4722
|
+
let errorMsg = "timeout";
|
|
4723
|
+
if (err.response?.data?.message) {
|
|
4724
|
+
errorMsg = String(err.response.data.message);
|
|
4725
|
+
} else if (err.message) {
|
|
4726
|
+
errorMsg = err.message;
|
|
4768
4727
|
}
|
|
4769
|
-
|
|
4770
|
-
return;
|
|
4728
|
+
return { name: proxyName, delay: null, error: errorMsg };
|
|
4771
4729
|
}
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
if (
|
|
4777
|
-
|
|
4778
|
-
return;
|
|
4730
|
+
}
|
|
4731
|
+
async function testSubscriptionProxies(subName, options = {}) {
|
|
4732
|
+
const { timeout = DEFAULT_TEST_TIMEOUT, concurrency = DEFAULT_TEST_CONCURRENCY, testUrl = DEFAULT_TEST_URL, apiBase = API_BASE, onResult } = options;
|
|
4733
|
+
const { proxies } = options.parsed || loadSubscriptionConfig(subName);
|
|
4734
|
+
if (proxies.length === 0) {
|
|
4735
|
+
return { total: 0, alive: 0, dead: 0, results: [] };
|
|
4779
4736
|
}
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
let
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4737
|
+
const client = createHttpClient({ timeout: timeout + 3e3 });
|
|
4738
|
+
const results = new Array(proxies.length);
|
|
4739
|
+
let completedCount = 0;
|
|
4740
|
+
let nextIndex = 0;
|
|
4741
|
+
async function runNext() {
|
|
4742
|
+
while (nextIndex < proxies.length) {
|
|
4743
|
+
const idx = nextIndex++;
|
|
4744
|
+
const result = await testProxyDelay(proxies[idx].name, timeout, testUrl, client, apiBase);
|
|
4745
|
+
results[idx] = result;
|
|
4746
|
+
onResult?.(result, completedCount, proxies.length);
|
|
4747
|
+
completedCount++;
|
|
4748
|
+
}
|
|
4749
|
+
}
|
|
4750
|
+
const workers = Array.from({ length: Math.min(concurrency, proxies.length) }, () => runNext());
|
|
4751
|
+
await Promise.all(workers);
|
|
4752
|
+
const alive = results.filter((r) => r.delay !== null).length;
|
|
4753
|
+
return { total: results.length, alive, dead: results.length - alive, results };
|
|
4754
|
+
}
|
|
4755
|
+
function normalizeProxyNamesBeforeSave(parsed) {
|
|
4756
|
+
const { proxies, proxyGroups } = parsed;
|
|
4757
|
+
const renameMap = /* @__PURE__ */ new Map();
|
|
4758
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
4759
|
+
for (const proxy of proxies) {
|
|
4760
|
+
const shortened = proxy.name.replace(/_github\.com\/[^_]+/, "");
|
|
4761
|
+
if (shortened !== proxy.name && !usedNames.has(shortened)) {
|
|
4762
|
+
renameMap.set(proxy.name, shortened);
|
|
4763
|
+
usedNames.add(shortened);
|
|
4788
4764
|
} else {
|
|
4789
|
-
|
|
4790
|
-
num = archiveCounter < 10 ? ` ${archiveCounter}` : `${archiveCounter}`;
|
|
4765
|
+
usedNames.add(proxy.name);
|
|
4791
4766
|
}
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4767
|
+
}
|
|
4768
|
+
if (renameMap.size === 0) return 0;
|
|
4769
|
+
for (const proxy of proxies) {
|
|
4770
|
+
const newName = renameMap.get(proxy.name);
|
|
4771
|
+
if (newName) proxy.name = newName;
|
|
4772
|
+
}
|
|
4773
|
+
for (const group of proxyGroups) {
|
|
4774
|
+
if (Array.isArray(group.proxies)) {
|
|
4775
|
+
group.proxies = group.proxies.map((name) => renameMap.get(name) || name);
|
|
4799
4776
|
}
|
|
4800
|
-
console.log("");
|
|
4801
4777
|
}
|
|
4802
|
-
|
|
4803
|
-
console.log(" mihomo logs 0 # \u67E5\u770B\u5F53\u524D\u65E5\u5FD7 (\u6700\u540E 100 \u884C)");
|
|
4804
|
-
console.log(" mihomo logs 1 # \u67E5\u770B\u7B2C 1 \u4E2A\u5F52\u6863\u65E5\u5FD7\uFF08\u6700\u65B0\uFF09");
|
|
4805
|
-
console.log(" mihomo logs 1 -n 200 # \u67E5\u770B 200 \u884C");
|
|
4806
|
-
console.log(" mihomo logs 1 -o # \u7528\u7CFB\u7EDF\u9ED8\u8BA4\u7A0B\u5E8F\u6253\u5F00");
|
|
4807
|
-
console.log("");
|
|
4808
|
-
}
|
|
4809
|
-
|
|
4810
|
-
// src/commands/overwrite.ts
|
|
4811
|
-
import path6 from "path";
|
|
4812
|
-
|
|
4813
|
-
// src/subscription.ts
|
|
4814
|
-
function isGithubUrl(url) {
|
|
4815
|
-
return /github\.com|raw\.githubusercontent\.com/i.test(url);
|
|
4816
|
-
}
|
|
4817
|
-
function getDefaultUpdateInterval(url) {
|
|
4818
|
-
return isGithubUrl(url) ? DEFAULT_UPDATE_INTERVAL_HOURS_GITHUB : DEFAULT_UPDATE_INTERVAL_HOURS;
|
|
4819
|
-
}
|
|
4820
|
-
function resolveUpdateInterval(url, cachedInterval) {
|
|
4821
|
-
return cachedInterval && cachedInterval > 0 ? cachedInterval : getDefaultUpdateInterval(url);
|
|
4822
|
-
}
|
|
4823
|
-
var YAML_DUMP_OPTS = { indent: 2, lineWidth: -1, schema: CORE_SCHEMA };
|
|
4824
|
-
var HTTP_CLIENT2 = createHttpClient({ timeout: 6e4 });
|
|
4825
|
-
function isMultiUrl(url) {
|
|
4826
|
-
return url.includes(",");
|
|
4827
|
-
}
|
|
4828
|
-
function splitUrls(url) {
|
|
4829
|
-
return url.split(",").map((u) => u.trim()).filter(Boolean);
|
|
4778
|
+
return renameMap.size;
|
|
4830
4779
|
}
|
|
4831
|
-
function
|
|
4832
|
-
const
|
|
4833
|
-
|
|
4834
|
-
|
|
4780
|
+
function cleanDeadProxies(parsed, deadNames) {
|
|
4781
|
+
const { proxies, proxyGroups } = parsed;
|
|
4782
|
+
const originalCount = proxies.length;
|
|
4783
|
+
parsed.proxies = proxies.filter((p) => !deadNames.has(p.name));
|
|
4784
|
+
const removedProxies = originalCount - parsed.proxies.length;
|
|
4785
|
+
let updatedGroups = 0;
|
|
4786
|
+
const removedGroupNames = /* @__PURE__ */ new Set();
|
|
4787
|
+
for (const group of proxyGroups) {
|
|
4788
|
+
if (Array.isArray(group.proxies)) {
|
|
4789
|
+
const before = group.proxies.length;
|
|
4790
|
+
group.proxies = group.proxies.filter((name) => !deadNames.has(name));
|
|
4791
|
+
if (group.proxies.length < before) {
|
|
4792
|
+
updatedGroups++;
|
|
4793
|
+
}
|
|
4794
|
+
if (group.proxies.length === 0) {
|
|
4795
|
+
removedGroupNames.add(group.name);
|
|
4796
|
+
}
|
|
4797
|
+
}
|
|
4835
4798
|
}
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
}
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
const info = {};
|
|
4852
|
-
const parts = header.split(";").map((p) => p.trim());
|
|
4853
|
-
for (const part of parts) {
|
|
4854
|
-
const [key, val] = part.split("=").map((s) => s.trim());
|
|
4855
|
-
if (key && val !== void 0) {
|
|
4856
|
-
const numVal = parseFloat(val);
|
|
4857
|
-
info[key] = Number.isNaN(numVal) ? 0 : numVal;
|
|
4799
|
+
if (removedGroupNames.size > 0) {
|
|
4800
|
+
parsed.proxyGroups = proxyGroups.filter((g) => !removedGroupNames.has(g.name));
|
|
4801
|
+
for (const group of parsed.proxyGroups) {
|
|
4802
|
+
if (Array.isArray(group.proxies)) {
|
|
4803
|
+
group.proxies = group.proxies.filter((name) => !removedGroupNames.has(name));
|
|
4804
|
+
}
|
|
4805
|
+
}
|
|
4806
|
+
const rules = parsed.raw.rules;
|
|
4807
|
+
if (Array.isArray(rules)) {
|
|
4808
|
+
parsed.raw.rules = rules.filter((rule) => {
|
|
4809
|
+
if (typeof rule !== "string") return true;
|
|
4810
|
+
const parts = rule.split(",");
|
|
4811
|
+
if (parts.length < 2) return true;
|
|
4812
|
+
return !removedGroupNames.has(parts[parts.length - 1].trim());
|
|
4813
|
+
});
|
|
4858
4814
|
}
|
|
4859
4815
|
}
|
|
4860
|
-
return
|
|
4861
|
-
}
|
|
4862
|
-
function parsePositiveInterval(header) {
|
|
4863
|
-
if (!header) return null;
|
|
4864
|
-
const n = parseInt(header, 10);
|
|
4865
|
-
return Number.isFinite(n) && n > 0 ? n : null;
|
|
4816
|
+
return { removedProxies, updatedGroups, removedGroups: removedGroupNames.size };
|
|
4866
4817
|
}
|
|
4867
|
-
function
|
|
4868
|
-
|
|
4869
|
-
const
|
|
4870
|
-
|
|
4871
|
-
const
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4818
|
+
async function autoCleanSubscription(subName, options = {}) {
|
|
4819
|
+
const parsed = loadSubscriptionConfig(subName);
|
|
4820
|
+
const { onResult, onRetryRound, rounds = DEFAULT_CLEAN_ROUNDS, ...testOptions } = options;
|
|
4821
|
+
const wrapOnResult = (round) => onResult ? (r, i, t) => onResult(r, i, t, round) : void 0;
|
|
4822
|
+
const summary = await testSubscriptionProxies(subName, {
|
|
4823
|
+
...testOptions,
|
|
4824
|
+
parsed,
|
|
4825
|
+
onResult: wrapOnResult(1)
|
|
4826
|
+
});
|
|
4827
|
+
let removedProxies = 0;
|
|
4828
|
+
let updatedGroups = 0;
|
|
4829
|
+
let removedGroups = 0;
|
|
4830
|
+
let skipped = false;
|
|
4831
|
+
if (summary.dead > 0) {
|
|
4832
|
+
if (summary.alive === 0 || summary.alive / summary.total < 0.01) {
|
|
4833
|
+
skipped = true;
|
|
4834
|
+
} else {
|
|
4835
|
+
const deadNames = new Set(summary.results.filter((r) => r.delay === null).map((r) => r.name));
|
|
4836
|
+
const deadProxies = parsed.proxies.filter((p) => deadNames.has(p.name));
|
|
4837
|
+
for (let retry = 0; retry < rounds - 1; retry++) {
|
|
4838
|
+
const round = retry + 2;
|
|
4839
|
+
const retryTargets = deadProxies.filter((p) => deadNames.has(p.name));
|
|
4840
|
+
if (retryTargets.length === 0) break;
|
|
4841
|
+
onRetryRound?.(round, retryTargets.length);
|
|
4842
|
+
const retryParsed = { raw: {}, proxies: retryTargets, proxyGroups: [] };
|
|
4843
|
+
const retrySummary = await testSubscriptionProxies(subName, {
|
|
4844
|
+
...testOptions,
|
|
4845
|
+
parsed: retryParsed,
|
|
4846
|
+
onResult: wrapOnResult(round)
|
|
4847
|
+
});
|
|
4848
|
+
for (const r of retrySummary.results) {
|
|
4849
|
+
if (r.delay !== null) {
|
|
4850
|
+
deadNames.delete(r.name);
|
|
4851
|
+
}
|
|
4852
|
+
}
|
|
4853
|
+
}
|
|
4854
|
+
summary.dead = deadNames.size;
|
|
4855
|
+
summary.alive = summary.total - summary.dead;
|
|
4856
|
+
if (deadNames.size > 0) {
|
|
4857
|
+
const cleanResult = cleanDeadProxies(parsed, deadNames);
|
|
4858
|
+
removedProxies = cleanResult.removedProxies;
|
|
4859
|
+
updatedGroups = cleanResult.updatedGroups;
|
|
4860
|
+
removedGroups = cleanResult.removedGroups;
|
|
4861
|
+
}
|
|
4862
|
+
}
|
|
4890
4863
|
}
|
|
4891
|
-
if (
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
}
|
|
4896
|
-
function formatProxySummary(info) {
|
|
4897
|
-
const parts = [];
|
|
4898
|
-
if (info.proxyGroups && info.proxyGroups > 0) parts.push(`${info.proxyGroups} \u7EC4`);
|
|
4899
|
-
parts.push(`${info.proxies || 0} \u8282\u70B9`);
|
|
4900
|
-
return parts.join(", ");
|
|
4864
|
+
if (!skipped && removedProxies > 0) {
|
|
4865
|
+
saveSubscriptionConfig(subName, parsed);
|
|
4866
|
+
}
|
|
4867
|
+
return { summary, removedProxies, updatedGroups, removedGroups, skipped };
|
|
4901
4868
|
}
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
const
|
|
4906
|
-
const
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4869
|
+
|
|
4870
|
+
// src/commands/daemon.ts
|
|
4871
|
+
function printDaemonStatus() {
|
|
4872
|
+
const status = getDaemonStatus();
|
|
4873
|
+
const stateText = status.enabled ? colors.green("\u5DF2\u542F\u7528") : colors.yellow("\u5DF2\u7981\u7528");
|
|
4874
|
+
console.log(`${colors.gray("\u4FDD\u6D3B: ")}${stateText}`);
|
|
4875
|
+
if (status.enabled) {
|
|
4876
|
+
const runText = isDaemonRunning(status) ? colors.green(`\u8FD0\u884C\u4E2D (PID ${status.pid})`) : colors.yellow("\u672A\u8FD0\u884C");
|
|
4877
|
+
console.log(`${colors.gray("\u5185\u6838: ")}${runText}`);
|
|
4910
4878
|
}
|
|
4911
|
-
|
|
4879
|
+
console.log("");
|
|
4880
|
+
if (status.enabled) {
|
|
4881
|
+
console.log("\u5173\u95ED\u4FDD\u6D3B: mihomo daemon off");
|
|
4882
|
+
} else {
|
|
4883
|
+
console.log("\u5F00\u542F\u4FDD\u6D3B: mihomo daemon on");
|
|
4884
|
+
console.log(colors.gray(" \u5F00\u673A\u81EA\u542F + \u5D29\u6E83\u81EA\u52A8\u91CD\u542F\uFF08\u4EC5 Mixed \u6A21\u5F0F\uFF09"));
|
|
4885
|
+
}
|
|
4886
|
+
console.log("");
|
|
4912
4887
|
}
|
|
4913
|
-
function
|
|
4914
|
-
const
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
const name = s.name.toLowerCase();
|
|
4920
|
-
if (name === lowerPattern) {
|
|
4921
|
-
exact.push(s);
|
|
4922
|
-
} else if (name.startsWith(lowerPattern)) {
|
|
4923
|
-
prefix.push(s);
|
|
4924
|
-
} else if (name.includes(lowerPattern)) {
|
|
4925
|
-
includes.push(s);
|
|
4888
|
+
async function cmdDaemon(args) {
|
|
4889
|
+
const action = args?.[1];
|
|
4890
|
+
if (action === "on" || action === "enable") {
|
|
4891
|
+
if (!hasKernel()) {
|
|
4892
|
+
console.error('\u9519\u8BEF: \u672A\u627E\u5230\u5185\u6838\uFF0C\u8BF7\u8FD0\u884C "mihomo kernel"');
|
|
4893
|
+
process.exit(1);
|
|
4926
4894
|
}
|
|
4895
|
+
const sub = getActiveSubscription();
|
|
4896
|
+
if (!sub) {
|
|
4897
|
+
console.error("\u9519\u8BEF: \u6CA1\u6709\u8BA2\u9605\uFF0C\u8BF7\u5148\u6DFB\u52A0\u8BA2\u9605");
|
|
4898
|
+
process.exit(1);
|
|
4899
|
+
}
|
|
4900
|
+
let configInfo;
|
|
4901
|
+
try {
|
|
4902
|
+
configInfo = prepareConfigForStart("mixed", sub.name);
|
|
4903
|
+
} catch (e) {
|
|
4904
|
+
console.error(`${colors.red("\u914D\u7F6E\u9519\u8BEF:")} ${e.message}`);
|
|
4905
|
+
process.exit(1);
|
|
4906
|
+
}
|
|
4907
|
+
try {
|
|
4908
|
+
enableDaemon();
|
|
4909
|
+
} catch (e) {
|
|
4910
|
+
console.error(`${colors.red("\u542F\u7528\u4FDD\u6D3B\u5931\u8D25:")} ${e.message}`);
|
|
4911
|
+
process.exit(1);
|
|
4912
|
+
}
|
|
4913
|
+
console.log(`${colors.green("\u5DF2\u542F\u7528\u4FDD\u6D3B")} \xB7 ${sub.name} \xB7 ${formatProxySummary(configInfo)}`);
|
|
4914
|
+
console.log(colors.gray("\u5F00\u673A\u81EA\u542F + \u5D29\u6E83\u81EA\u52A8\u91CD\u542F\uFF0C\u4EE3\u7406\u5C06\u5728\u540E\u53F0\u5E38\u9A7B"));
|
|
4915
|
+
console.log("");
|
|
4916
|
+
printDaemonStatus();
|
|
4917
|
+
return;
|
|
4927
4918
|
}
|
|
4928
|
-
if (
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4919
|
+
if (action === "off" || action === "disable") {
|
|
4920
|
+
if (!isDaemonEnabled()) {
|
|
4921
|
+
console.log("\u4FDD\u6D3B\u5DF2\u662F\u5173\u95ED\u72B6\u6001");
|
|
4922
|
+
console.log("");
|
|
4923
|
+
printDaemonStatus();
|
|
4924
|
+
return;
|
|
4925
|
+
}
|
|
4926
|
+
try {
|
|
4927
|
+
disableDaemon();
|
|
4928
|
+
} catch (e) {
|
|
4929
|
+
console.error(`${colors.red("\u5173\u95ED\u4FDD\u6D3B\u5931\u8D25:")} ${e.message}`);
|
|
4930
|
+
process.exit(1);
|
|
4931
|
+
}
|
|
4932
|
+
console.log(`${colors.green("\u5DF2\u5173\u95ED\u4FDD\u6D3B")}\uFF0C\u4EE3\u7406\u5DF2\u505C\u6B62`);
|
|
4933
|
+
console.log(colors.gray("\u91CD\u65B0\u542F\u7528: mihomo daemon on"));
|
|
4934
|
+
console.log("");
|
|
4935
|
+
return;
|
|
4936
|
+
}
|
|
4937
|
+
if (action !== void 0 && action !== "status") {
|
|
4938
|
+
console.error(`\u9519\u8BEF: \u672A\u77E5\u7684 daemon \u5B50\u547D\u4EE4: ${action}`);
|
|
4939
|
+
console.log("");
|
|
4940
|
+
console.log("\u53EF\u7528\u5B50\u547D\u4EE4: on, off, status");
|
|
4935
4941
|
process.exit(1);
|
|
4936
4942
|
}
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
console.log("\n\u5339\u914D\u7684\u8BA2\u9605:");
|
|
4940
|
-
for (const s of subs) console.log(` ${s.name}`);
|
|
4941
|
-
process.exit(1);
|
|
4943
|
+
console.log("");
|
|
4944
|
+
printDaemonStatus();
|
|
4942
4945
|
}
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
const
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4946
|
+
|
|
4947
|
+
// src/commands/directory.ts
|
|
4948
|
+
function cmdDirectory(args) {
|
|
4949
|
+
const action = args?.[1];
|
|
4950
|
+
if (action === "open") {
|
|
4951
|
+
const target = args[2];
|
|
4952
|
+
if (!target || target === "root") {
|
|
4953
|
+
console.log("\u6B63\u5728\u6253\u5F00: \u6839\u76EE\u5F55");
|
|
4954
|
+
const success = openUrl(USER_DATA_DIR);
|
|
4955
|
+
if (!success) {
|
|
4956
|
+
console.log(`\u8BF7\u624B\u52A8\u6253\u5F00: ${USER_DATA_DIR}`);
|
|
4957
|
+
}
|
|
4958
|
+
return;
|
|
4953
4959
|
}
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4960
|
+
const key = target.toLowerCase();
|
|
4961
|
+
const targetInfo = Object.hasOwn(DIRECTORY_TARGETS, key) ? DIRECTORY_TARGETS[key] : void 0;
|
|
4962
|
+
if (targetInfo) {
|
|
4963
|
+
const targetPath = targetInfo.path || USER_DATA_DIR;
|
|
4964
|
+
console.log(`\u6B63\u5728\u6253\u5F00: ${targetInfo.label}`);
|
|
4965
|
+
const success = openUrl(targetPath);
|
|
4966
|
+
if (!success) {
|
|
4967
|
+
console.log(`\u8BF7\u624B\u52A8\u6253\u5F00: ${targetPath}`);
|
|
4968
|
+
}
|
|
4969
|
+
return;
|
|
4970
|
+
}
|
|
4971
|
+
console.error(`\u9519\u8BEF: \u672A\u77E5\u7684\u76EE\u5F55\u76EE\u6807 "${target}"`);
|
|
4972
|
+
console.log("");
|
|
4973
|
+
console.log("\u53EF\u7528\u76EE\u6807:");
|
|
4974
|
+
console.log(" root (\u9ED8\u8BA4) \u6839\u76EE\u5F55");
|
|
4975
|
+
for (const [key2, val] of Object.entries(DIRECTORY_TARGETS)) {
|
|
4976
|
+
if (key2 !== "root") {
|
|
4977
|
+
console.log(` ${key2.padEnd(14)}${val.label}`);
|
|
4978
|
+
}
|
|
4979
|
+
}
|
|
4980
|
+
console.log("");
|
|
4981
|
+
process.exit(1);
|
|
4961
4982
|
}
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4983
|
+
console.log("");
|
|
4984
|
+
console.log("\u6570\u636E\u76EE\u5F55\u4F4D\u7F6E:");
|
|
4985
|
+
console.log(` \u6839\u76EE\u5F55: ${USER_DATA_DIR}`);
|
|
4986
|
+
console.log(` \u5168\u5C40\u8BBE\u7F6E: ${PATHS.settingsFile}`);
|
|
4987
|
+
console.log(` \u5185\u6838\u76EE\u5F55: ${DIRS.kernel}`);
|
|
4988
|
+
console.log(` \u5185\u6838\u6587\u4EF6: ${PATHS.mihomoBinary}`);
|
|
4989
|
+
console.log(` \u8BA2\u9605\u76EE\u5F55: ${DIRS.subscriptions}`);
|
|
4990
|
+
console.log(" - cache.json (\u8BA2\u9605\u7F13\u5B58\uFF1A\u66F4\u65B0\u65F6\u95F4\u3001\u6D41\u91CF\u7B49)");
|
|
4991
|
+
console.log(" - xxx.yaml (\u8BA2\u9605\u539F\u59CB\u914D\u7F6E)");
|
|
4992
|
+
console.log(` \u8FD0\u884C\u65F6\u76EE\u5F55: ${DIRS.runtime}`);
|
|
4993
|
+
console.log(" - config.yaml (\u542F\u52A8\u65F6\u751F\u6210\uFF0Cstop \u81EA\u52A8\u6E05\u9664)");
|
|
4994
|
+
console.log(" - pid (PID \u6587\u4EF6\uFF0Cstop \u81EA\u52A8\u6E05\u9664)");
|
|
4995
|
+
console.log(` \u65E5\u5FD7\u6587\u4EF6: ${PATHS.logFile}`);
|
|
4996
|
+
console.log(` mihomo \u6570\u636E: ${DIRS.data}`);
|
|
4997
|
+
console.log(" - cache.db, Geo*.dat \u7B49 (mihomo \u81EA\u884C\u7BA1\u7406)");
|
|
4998
|
+
console.log("");
|
|
4999
|
+
console.log("\u6253\u5F00\u76EE\u5F55:");
|
|
5000
|
+
console.log(" mihomo dir open \u6253\u5F00\u6839\u76EE\u5F55");
|
|
5001
|
+
console.log(" mihomo dir open subs \u6253\u5F00\u8BA2\u9605\u76EE\u5F55");
|
|
5002
|
+
console.log(" mihomo dir open logs \u6253\u5F00\u65E5\u5FD7\u76EE\u5F55");
|
|
5003
|
+
console.log(" mihomo dir open data \u6253\u5F00 mihomo \u6570\u636E\u76EE\u5F55");
|
|
5004
|
+
console.log(" mihomo dir open runtime \u6253\u5F00\u8FD0\u884C\u65F6\u76EE\u5F55");
|
|
5005
|
+
console.log(" mihomo dir open kernel \u6253\u5F00\u5185\u6838\u76EE\u5F55");
|
|
5006
|
+
console.log("");
|
|
5007
|
+
console.log("\u73AF\u5883\u53D8\u91CF:");
|
|
5008
|
+
console.log(" MIHOMO_CLI_DIR: \u81EA\u5B9A\u4E49\u6839\u76EE\u5F55\u4F4D\u7F6E");
|
|
5009
|
+
console.log("");
|
|
4977
5010
|
}
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
5011
|
+
|
|
5012
|
+
// src/commands/help.ts
|
|
5013
|
+
function printShortHelp() {
|
|
5014
|
+
console.log(`
|
|
5015
|
+
${colors.cyan(colors.bold(`mihomo-cli v${VERSION}`))} (mihomo help \u67E5\u770B\u5B8C\u6574\u5E2E\u52A9)
|
|
5016
|
+
`);
|
|
5017
|
+
console.log(
|
|
5018
|
+
`\u5E38\u7528\u547D\u4EE4:
|
|
5019
|
+
${colors.bold("start")} [tun|mixed] \u542F\u52A8/\u5207\u6362\u4EE3\u7406
|
|
5020
|
+
${colors.bold("sub")} [use|update] \u8BA2\u9605\u7BA1\u7406
|
|
5021
|
+
${colors.bold("ow")} [on|off] \u8986\u5199\u914D\u7F6E
|
|
5022
|
+
${colors.bold("ui")} [zash|dash|yacd] \u6253\u5F00 Web UI
|
|
5023
|
+
`
|
|
4988
5024
|
);
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
5025
|
+
}
|
|
5026
|
+
function printHelp() {
|
|
5027
|
+
console.log(
|
|
5028
|
+
`
|
|
5029
|
+
${colors.cyan(colors.bold(`mihomo-cli v${VERSION}`))}
|
|
5030
|
+
|
|
5031
|
+
\u547D\u4EE4\u522B\u540D: mihomo, mhm, mh
|
|
5032
|
+
|
|
5033
|
+
\u7528\u6CD5:
|
|
5034
|
+
mihomo <\u547D\u4EE4> [\u9009\u9879]
|
|
5035
|
+
|
|
5036
|
+
${colors.cyan("\u63A7\u5236:")}
|
|
5037
|
+
${colors.bold("start")} [tun|mixed] [-s] [-u ms] \u542F\u52A8/\u5207\u6362\u4EE3\u7406 (\u9ED8\u8BA4 mixed)
|
|
5038
|
+
[-r N] [-t ms] [-j N]
|
|
5039
|
+
${colors.bold("stop")} \u505C\u6B62\u4EE3\u7406
|
|
5040
|
+
${colors.bold("status")} \u67E5\u770B\u72B6\u6001
|
|
5041
|
+
|
|
5042
|
+
${colors.cyan("\u754C\u9762:")}
|
|
5043
|
+
${colors.bold("ui")} [zash|dash|yacd] \u6253\u5F00 Web UI (\u9ED8\u8BA4 zash)
|
|
5044
|
+
${colors.bold("log")} [-o] \u5B9E\u65F6\u65E5\u5FD7\uFF08-o \u6253\u5F00\u6587\u4EF6\uFF09
|
|
5045
|
+
${colors.bold("logs")} [\u7F16\u53F7] [-n N] [-o] \u65E5\u5FD7\u5217\u8868\uFF080=\u5F53\u524D\uFF0C1+=\u5F52\u6863\uFF09
|
|
5046
|
+
|
|
5047
|
+
${colors.cyan("\u8BA2\u9605:")}
|
|
5048
|
+
${colors.bold("subscription")} \u5217\u51FA\u6240\u6709\u8BA2\u9605\uFF08\u522B\u540D sub\uFF09
|
|
5049
|
+
${colors.bold("subscription")} use <name> \u5207\u6362\u5F53\u524D\u8BA2\u9605
|
|
5050
|
+
${colors.bold("subscription")} add <url> [name] \u6DFB\u52A0\u8BA2\u9605
|
|
5051
|
+
${colors.bold("subscription")} update [name] \u66F4\u65B0\u8BA2\u9605\uFF08\u65E0\u53C2\u66F4\u65B0\u6240\u6709\uFF09
|
|
5052
|
+
${colors.bold("subscription")} remove <name> \u5220\u9664\u8BA2\u9605
|
|
5053
|
+
${colors.bold("subscription")} web [name] \u6253\u5F00\u8BA2\u9605\u9875\u9762
|
|
5054
|
+
${colors.bold("subscription")} test [name] \u6D4B\u8BD5\u8282\u70B9\u8FDE\u901A\u6027
|
|
5055
|
+
${colors.bold("subscription")} clean [name] \u6D4B\u901F\u5E76\u6E05\u7406\u5931\u8D25\u8282\u70B9
|
|
5056
|
+
${colors.bold("test")} [-t ms] [-j N] \u5FEB\u901F\u6D4B\u8BD5\u5F53\u524D\u8282\u70B9\u8FDE\u901A\u6027
|
|
5057
|
+
${colors.bold("clean")} [-t ms] [-j N] [-r N] \u6E05\u7406\u5931\u8D25\u8282\u70B9\u5E76\u81EA\u52A8\u91CD\u542F
|
|
5058
|
+
|
|
5059
|
+
${colors.cyan("\u914D\u7F6E:")}
|
|
5060
|
+
${colors.bold("overwrite")} \u67E5\u770B\u8986\u5199\u72B6\u6001\uFF08\u522B\u540D ow\uFF09
|
|
5061
|
+
${colors.bold("overwrite")} on|off \u542F\u7528/\u7981\u7528\u8986\u5199\u914D\u7F6E
|
|
5062
|
+
${colors.bold("directory")} \u663E\u793A\u6570\u636E\u76EE\u5F55\u4F4D\u7F6E\uFF08\u522B\u540D dir\uFF09
|
|
5063
|
+
${colors.bold("directory")} open [target] \u6253\u5F00\u76EE\u5F55: root|subs|logs|runtime|...
|
|
5064
|
+
|
|
5065
|
+
${colors.cyan("\u7CFB\u7EDF:")}
|
|
5066
|
+
${colors.bold("kernel")} [--mirror [\u955C\u50CF]] \u66F4\u65B0\u5185\u6838\uFF08\u9ED8\u8BA4\u76F4\u8FDE\uFF0C--mirror \u4F7F\u7528 v6\uFF09
|
|
5067
|
+
${colors.bold("daemon")} on|off \u5F00\u673A\u81EA\u542F + \u5D29\u6E83\u91CD\u542F\uFF08\u4EC5 Mixed\uFF09
|
|
5068
|
+
${colors.bold("daemon")} status \u67E5\u770B\u4FDD\u6D3B\u72B6\u6001
|
|
5069
|
+
${colors.bold("update")} \u66F4\u65B0 mihomo-cli (npm install -g)
|
|
5070
|
+
${colors.bold("reset")} [\u76EE\u6807...] [--full] \u91CD\u7F6E: \u7559\u7A7A\u4FDD\u7559\u8BBE\u7F6E/\u5185\u6838/\u8986\u5199, \u6307\u5B9A\u76EE\u6807\u5220\u5BF9\u5E94\u9879, --full \u5220\u5168\u90E8
|
|
5071
|
+
${colors.bold("help")}, -h \u663E\u793A\u5E2E\u52A9
|
|
5072
|
+
${colors.bold("version")}, -v \u663E\u793A\u7248\u672C
|
|
5073
|
+
|
|
5074
|
+
${colors.cyan("\u793A\u4F8B:")}
|
|
5075
|
+
mihomo start # \u542F\u52A8/\u91CD\u542F Mixed \u6A21\u5F0F
|
|
5076
|
+
mihomo start tun # \u5207\u6362\u5230 TUN \u900F\u660E\u4EE3\u7406\u6A21\u5F0F
|
|
5077
|
+
mihomo start -s # \u8DF3\u8FC7\u81EA\u52A8\u66F4\u65B0\u8BA2\u9605
|
|
5078
|
+
mihomo start -u 30000 # \u81EA\u52A8\u66F4\u65B0\u8D85\u65F6 30 \u79D2 (\u9ED8\u8BA4 10s)
|
|
5079
|
+
mihomo daemon on # \u5F00\u542F\u4FDD\u6D3B\uFF08\u5F00\u673A\u81EA\u542F + \u5D29\u6E83\u91CD\u542F\uFF09
|
|
5080
|
+
mihomo sub add <url> # \u6DFB\u52A0\u8BA2\u9605 (sub \u662F subscription \u522B\u540D)
|
|
5081
|
+
mihomo ui # \u6253\u5F00 Web UI
|
|
5082
|
+
|
|
5083
|
+
${colors.cyan("\u6A21\u5F0F\u8BF4\u660E:")}
|
|
5084
|
+
mixed HTTP + SOCKS5 \u6DF7\u5408\u7AEF\u53E3 (\u9ED8\u8BA4)
|
|
5085
|
+
tun \u900F\u660E\u4EE3\u7406\uFF0C\u5168\u5C40\u81EA\u52A8\u8DEF\u7531\uFF0C\u9700\u8981 sudo
|
|
5086
|
+
|
|
5087
|
+
${colors.cyan("\u6570\u636E\u76EE\u5F55:")}
|
|
5088
|
+
\u73AF\u5883\u53D8\u91CF MIHOMO_CLI_DIR \u53EF\u81EA\u5B9A\u4E49\u4F4D\u7F6E
|
|
5089
|
+
\u9ED8\u8BA4: ${USER_DATA_DIR}
|
|
5090
|
+
`
|
|
5091
|
+
);
|
|
5092
|
+
}
|
|
5093
|
+
function printVersion() {
|
|
5094
|
+
const kv = getKernelVersion() || "\u672A\u5B89\u88C5";
|
|
5095
|
+
console.log(colors.cyan(colors.bold(`mihomo-cli v${VERSION}`)));
|
|
5096
|
+
console.log(`${colors.gray("\u5185\u6838: ")}${kv}`);
|
|
5097
|
+
console.log(`${colors.gray("\u6570\u636E\u76EE\u5F55: ")}${USER_DATA_DIR}`);
|
|
5098
|
+
}
|
|
5099
|
+
|
|
5100
|
+
// src/kernel.ts
|
|
5101
|
+
import { spawnSync as spawnSync5 } from "child_process";
|
|
5102
|
+
import fs7 from "fs";
|
|
5103
|
+
import path5 from "path";
|
|
5104
|
+
|
|
5105
|
+
// node_modules/compare-versions/lib/esm/utils.js
|
|
5106
|
+
var semver = /^[v^~<>=]*?(\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+))?(?:-([\da-z\-]+(?:\.[\da-z\-]+)*))?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?)?)?$/i;
|
|
5107
|
+
var validateAndParse = (version) => {
|
|
5108
|
+
if (typeof version !== "string") {
|
|
5109
|
+
throw new TypeError("Invalid argument expected string");
|
|
4995
5110
|
}
|
|
4996
|
-
const
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5111
|
+
const match = version.match(semver);
|
|
5112
|
+
if (!match) {
|
|
5113
|
+
throw new Error(`Invalid argument not valid semver ('${version}' received)`);
|
|
5114
|
+
}
|
|
5115
|
+
match.shift();
|
|
5116
|
+
return match;
|
|
5117
|
+
};
|
|
5118
|
+
var isWildcard = (s) => s === "*" || s === "x" || s === "X";
|
|
5119
|
+
var tryParse = (v) => {
|
|
5120
|
+
const n = parseInt(v, 10);
|
|
5121
|
+
return isNaN(n) ? v : n;
|
|
5122
|
+
};
|
|
5123
|
+
var forceType = (a, b) => typeof a !== typeof b ? [String(a), String(b)] : [a, b];
|
|
5124
|
+
var compareStrings = (a, b) => {
|
|
5125
|
+
if (isWildcard(a) || isWildcard(b))
|
|
5126
|
+
return 0;
|
|
5127
|
+
const [ap, bp] = forceType(tryParse(a), tryParse(b));
|
|
5128
|
+
if (ap > bp)
|
|
5129
|
+
return 1;
|
|
5130
|
+
if (ap < bp)
|
|
5131
|
+
return -1;
|
|
5132
|
+
return 0;
|
|
5133
|
+
};
|
|
5134
|
+
var compareSegments = (a, b) => {
|
|
5135
|
+
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
5136
|
+
const r = compareStrings(a[i] || "0", b[i] || "0");
|
|
5137
|
+
if (r !== 0)
|
|
5138
|
+
return r;
|
|
5139
|
+
}
|
|
5140
|
+
return 0;
|
|
5141
|
+
};
|
|
5142
|
+
|
|
5143
|
+
// node_modules/compare-versions/lib/esm/compareVersions.js
|
|
5144
|
+
var compareVersions = (v1, v2) => {
|
|
5145
|
+
const n1 = validateAndParse(v1);
|
|
5146
|
+
const n2 = validateAndParse(v2);
|
|
5147
|
+
const p1 = n1.pop();
|
|
5148
|
+
const p2 = n2.pop();
|
|
5149
|
+
const r = compareSegments(n1, n2);
|
|
5150
|
+
if (r !== 0)
|
|
5151
|
+
return r;
|
|
5152
|
+
if (p1 && p2) {
|
|
5153
|
+
return compareSegments(p1.split("."), p2.split("."));
|
|
5154
|
+
} else if (p1 || p2) {
|
|
5155
|
+
return p1 ? -1 : 1;
|
|
5156
|
+
}
|
|
5157
|
+
return 0;
|
|
5158
|
+
};
|
|
5159
|
+
|
|
5160
|
+
// src/kernel.ts
|
|
5161
|
+
var GITHUB_REPO = "MetaCubeX/mihomo";
|
|
5162
|
+
var KERNEL_HTTP_TIMEOUT = 12e4;
|
|
5163
|
+
var KERNEL_DOWNLOAD_TIMEOUT = 18e4;
|
|
5164
|
+
var HTTP_CLIENT2 = createHttpClient({ timeout: KERNEL_HTTP_TIMEOUT });
|
|
5165
|
+
function withMirror(url, mirror) {
|
|
5166
|
+
if (mirror && (url.startsWith("https://github.com/") || url.startsWith("https://api.github.com/"))) {
|
|
5167
|
+
return mirror + url;
|
|
5168
|
+
}
|
|
5169
|
+
return url;
|
|
5170
|
+
}
|
|
5171
|
+
function getArch() {
|
|
5172
|
+
const arch = process.arch;
|
|
5173
|
+
if (arch === "arm64") return "arm64";
|
|
5174
|
+
if (arch === "x64") return "amd64";
|
|
5175
|
+
return arch;
|
|
5176
|
+
}
|
|
5177
|
+
function findMatchingAsset(assets, platform, arch) {
|
|
5178
|
+
const prefix = `mihomo-${platform}-${arch}`;
|
|
5179
|
+
const matchingAssets = assets.filter(
|
|
5180
|
+
(a) => a.name.startsWith(prefix) && a.name.endsWith(".gz") || a.name.startsWith(`${prefix}-`) && a.name.endsWith(".gz")
|
|
5181
|
+
);
|
|
5182
|
+
if (matchingAssets.length === 0) return null;
|
|
5183
|
+
if (matchingAssets.length === 1) return matchingAssets[0];
|
|
5184
|
+
const standardAsset = matchingAssets.find((a) => {
|
|
5185
|
+
const nameWithoutGz = a.name.slice(0, -3);
|
|
5186
|
+
const parts = nameWithoutGz.split("-");
|
|
5187
|
+
const lastPart = parts[parts.length - 1];
|
|
5188
|
+
return /^v?\d+\.\d+\.\d+/.test(lastPart) && !nameWithoutGz.includes("-go");
|
|
5000
5189
|
});
|
|
5001
|
-
|
|
5002
|
-
const baseProxies = base.proxies || [];
|
|
5003
|
-
const seenNames = new Set(baseProxies.map((p) => p.name));
|
|
5004
|
-
for (let i = 1; i < parsed.length; i++) {
|
|
5005
|
-
const extraProxies = parsed[i].proxies || [];
|
|
5006
|
-
for (const proxy of extraProxies) {
|
|
5007
|
-
if (!seenNames.has(proxy.name)) {
|
|
5008
|
-
baseProxies.push(proxy);
|
|
5009
|
-
seenNames.add(proxy.name);
|
|
5010
|
-
}
|
|
5011
|
-
}
|
|
5012
|
-
}
|
|
5013
|
-
base.proxies = baseProxies;
|
|
5014
|
-
const mergedContent = dump(base, YAML_DUMP_OPTS);
|
|
5015
|
-
saveSubscriptionRawConfig(subName, mergedContent);
|
|
5016
|
-
const meta = extractSubscriptionMeta(responses[0].response?.headers);
|
|
5017
|
-
saveSubscriptionMeta(subName, meta);
|
|
5018
|
-
const proxyGroups = base["proxy-groups"];
|
|
5019
|
-
return {
|
|
5020
|
-
proxies: baseProxies.length,
|
|
5021
|
-
proxyGroups: proxyGroups ? proxyGroups.length : 0,
|
|
5022
|
-
userInfo: meta.userInfo,
|
|
5023
|
-
updateInterval: meta.updateInterval,
|
|
5024
|
-
webPageUrl: meta.webPageUrl,
|
|
5025
|
-
username: meta.username
|
|
5026
|
-
};
|
|
5190
|
+
return standardAsset || matchingAssets[0];
|
|
5027
5191
|
}
|
|
5028
|
-
function
|
|
5029
|
-
const
|
|
5030
|
-
|
|
5031
|
-
|
|
5192
|
+
async function getLatestRelease(repo, mirror) {
|
|
5193
|
+
const url = withMirror(`https://api.github.com/repos/${repo}/releases`, mirror);
|
|
5194
|
+
const response = await HTTP_CLIENT2.get(url, { responseType: "json" });
|
|
5195
|
+
const releases = response.data;
|
|
5196
|
+
if (!Array.isArray(releases) || releases.length === 0) {
|
|
5197
|
+
throw new Error("\u65E0\u6CD5\u83B7\u53D6\u7248\u672C\u4FE1\u606F");
|
|
5032
5198
|
}
|
|
5033
|
-
const
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5199
|
+
const stableReleases = releases.filter(
|
|
5200
|
+
(r) => !r.prerelease && !r.tag_name.toLowerCase().includes("alpha") && !r.tag_name.toLowerCase().includes("beta") && !r.tag_name.toLowerCase().includes("prerelease")
|
|
5201
|
+
);
|
|
5202
|
+
return stableReleases.length > 0 ? stableReleases[0] : releases[0];
|
|
5203
|
+
}
|
|
5204
|
+
async function checkUpdate(mirror) {
|
|
5205
|
+
const currentVersion = getKernelVersion();
|
|
5206
|
+
const latest = await getLatestRelease(GITHUB_REPO, mirror);
|
|
5207
|
+
const latestVersion = latest.tag_name;
|
|
5208
|
+
let needsUpdate = false;
|
|
5209
|
+
const currentDisplay = currentVersion || "\u672A\u5B89\u88C5";
|
|
5210
|
+
if (!currentVersion) {
|
|
5211
|
+
needsUpdate = true;
|
|
5212
|
+
} else {
|
|
5213
|
+
try {
|
|
5214
|
+
needsUpdate = compareVersions(latestVersion.replace(/^v/, ""), currentVersion.replace(/^v/, "")) > 0;
|
|
5215
|
+
} catch {
|
|
5216
|
+
needsUpdate = latestVersion !== currentVersion;
|
|
5037
5217
|
}
|
|
5038
|
-
console.log("");
|
|
5039
5218
|
}
|
|
5040
|
-
writeMihomoConfig(buildResult.config);
|
|
5041
|
-
writeDebugConfig(buildResult);
|
|
5042
|
-
const proxies = buildResult.config.proxies;
|
|
5043
|
-
const proxyGroups = buildResult.config["proxy-groups"];
|
|
5044
5219
|
return {
|
|
5045
|
-
|
|
5046
|
-
|
|
5220
|
+
current: currentDisplay,
|
|
5221
|
+
latest: latestVersion,
|
|
5222
|
+
needsUpdate,
|
|
5223
|
+
assets: latest.assets,
|
|
5224
|
+
release: latest
|
|
5047
5225
|
};
|
|
5048
5226
|
}
|
|
5049
|
-
function
|
|
5050
|
-
if (
|
|
5051
|
-
const
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
let info;
|
|
5060
|
-
if (isMultiUrl(sub.url)) {
|
|
5061
|
-
info = await downloadMergedSubscription(splitUrls(sub.url), sub.name, signal);
|
|
5062
|
-
} else {
|
|
5063
|
-
info = await downloadSubscription(sub.url, sub.name, signal);
|
|
5227
|
+
function findBinaryInDir(dir, maxDepth = 4) {
|
|
5228
|
+
if (maxDepth <= 0) return null;
|
|
5229
|
+
const files = fs7.readdirSync(dir);
|
|
5230
|
+
for (const f of files) {
|
|
5231
|
+
const fullPath = path5.join(dir, f);
|
|
5232
|
+
const stat = fs7.statSync(fullPath);
|
|
5233
|
+
if (stat.isDirectory()) {
|
|
5234
|
+
const found = findBinaryInDir(fullPath, maxDepth - 1);
|
|
5235
|
+
if (found) return found;
|
|
5236
|
+
continue;
|
|
5064
5237
|
}
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
return { name: sub.name, success: false, error: e.message };
|
|
5238
|
+
if (f === "mihomo") return fullPath;
|
|
5239
|
+
if (f.includes("mihomo") && !f.endsWith(".gz")) return fullPath;
|
|
5068
5240
|
}
|
|
5241
|
+
return null;
|
|
5069
5242
|
}
|
|
5070
|
-
function
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5243
|
+
async function downloadKernel(progressCallback, mirror, releaseInfo) {
|
|
5244
|
+
ensureDirs();
|
|
5245
|
+
const latest = releaseInfo || await getLatestRelease(GITHUB_REPO, mirror);
|
|
5246
|
+
const arch = getArch();
|
|
5247
|
+
const platform = process.platform;
|
|
5248
|
+
const asset = findMatchingAsset(latest.assets, platform, arch);
|
|
5249
|
+
if (!asset) {
|
|
5250
|
+
const available = latest.assets.map((a) => a.name).join(", ");
|
|
5251
|
+
let hint = "";
|
|
5252
|
+
if (available) hint = `
|
|
5253
|
+
\u53EF\u7528\u7248\u672C: ${available}`;
|
|
5254
|
+
throw new Error(`\u672A\u627E\u5230\u5339\u914D\u7684\u5185\u6838\u6587\u4EF6
|
|
5255
|
+
\u5E73\u53F0: ${platform}, \u67B6\u6784: ${arch}${hint}`);
|
|
5075
5256
|
}
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
const
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
return { total: 0, updated: 0, failed: 0 };
|
|
5257
|
+
const downloadUrl = withMirror(asset.browser_download_url, mirror);
|
|
5258
|
+
const tempPath = path5.join(DIRS.kernel, asset.name);
|
|
5259
|
+
const sizeMB = (asset.size / 1024 / 1024).toFixed(2);
|
|
5260
|
+
if (mirror && progressCallback) {
|
|
5261
|
+
progressCallback("\u63D0\u793A: \u7ECF\u7B2C\u4E09\u65B9\u955C\u50CF\u4E2D\u8F6C\u4E0B\u8F7D\uFF0C\u65E0\u6CD5\u9A8C\u8BC1\u6765\u6E90\u5B8C\u6574\u6027\uFF0C\u5EFA\u8BAE\u76F4\u8FDE\u6216\u81EA\u884C\u6821\u9A8C\u4EA7\u7269");
|
|
5082
5262
|
}
|
|
5083
|
-
if (
|
|
5084
|
-
|
|
5085
|
-
const interval = resolveUpdateInterval(sub.url, sub.update_interval);
|
|
5086
|
-
console.log(`\u8BA2\u9605 "${sub.name}" \u8D85\u8FC7 ${interval} \u5C0F\u65F6\u672A\u66F4\u65B0\uFF0C\u6B63\u5728\u66F4\u65B0...`);
|
|
5087
|
-
} else {
|
|
5088
|
-
console.log(`\u68C0\u67E5\u5230 ${staleSubs.length} \u4E2A\u8BA2\u9605\u9700\u8981\u66F4\u65B0\uFF0C\u6B63\u5728\u5E76\u884C\u66F4\u65B0...`);
|
|
5263
|
+
if (progressCallback) {
|
|
5264
|
+
progressCallback(`\u4E0B\u8F7D\u5185\u6838: ${asset.name} (${sizeMB} MB)`);
|
|
5089
5265
|
}
|
|
5090
|
-
const
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
return { total: staleSubs.length, updated: 0, failed: staleSubs.length };
|
|
5266
|
+
const curlResult = spawnSync5(
|
|
5267
|
+
"curl",
|
|
5268
|
+
["-L", "--progress-bar", "--connect-timeout", "30", "--max-time", String(Math.floor(KERNEL_DOWNLOAD_TIMEOUT / 1e3)), "-o", tempPath, downloadUrl],
|
|
5269
|
+
{ stdio: "inherit" }
|
|
5270
|
+
);
|
|
5271
|
+
if (curlResult.status !== 0) {
|
|
5272
|
+
try {
|
|
5273
|
+
fs7.unlinkSync(tempPath);
|
|
5274
|
+
} catch {
|
|
5100
5275
|
}
|
|
5101
|
-
throw
|
|
5276
|
+
throw new Error(`\u4E0B\u8F7D\u5931\u8D25 (curl \u9000\u51FA\u7801 ${curlResult.status})`);
|
|
5102
5277
|
}
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
if (r.success) updatedCount++;
|
|
5106
|
-
printUpdateResult(r);
|
|
5278
|
+
if (!fs7.existsSync(tempPath)) {
|
|
5279
|
+
throw new Error("\u4E0B\u8F7D\u5931\u8D25: \u6587\u4EF6\u672A\u751F\u6210");
|
|
5107
5280
|
}
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
const url = `${apiBase}/proxies/${encodedName}/delay?timeout=${timeout}&url=${encodeURIComponent(testUrl)}`;
|
|
5281
|
+
if (progressCallback) {
|
|
5282
|
+
progressCallback("\u89E3\u538B\u5185\u6838...");
|
|
5283
|
+
}
|
|
5284
|
+
const extractPath = DIRS.kernel;
|
|
5285
|
+
let extractedBinary = null;
|
|
5114
5286
|
try {
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5287
|
+
if (tempPath.endsWith(".tar.gz") || tempPath.endsWith(".tgz")) {
|
|
5288
|
+
const tarResult = spawnSync5("tar", ["-xzf", tempPath, "-C", extractPath], { stdio: ["ignore", "ignore", "inherit"], timeout: 6e4 });
|
|
5289
|
+
if (tarResult.error) throw tarResult.error;
|
|
5290
|
+
if (tarResult.status !== 0) throw new Error(`tar \u9000\u51FA\u7801 ${tarResult.status}`);
|
|
5291
|
+
} else if (tempPath.endsWith(".gz")) {
|
|
5292
|
+
const baseName = path5.basename(tempPath, ".gz");
|
|
5293
|
+
const outputPath = path5.join(extractPath, baseName);
|
|
5294
|
+
const gzipResult = spawnSync5("gzip", ["-dc", tempPath], { maxBuffer: 256 * 1024 * 1024, timeout: 6e4 });
|
|
5295
|
+
if (gzipResult.error) throw gzipResult.error;
|
|
5296
|
+
if (gzipResult.status !== 0) throw new Error(`gzip \u9000\u51FA\u7801 ${gzipResult.status}`);
|
|
5297
|
+
fs7.writeFileSync(outputPath, gzipResult.stdout, { mode: 493 });
|
|
5298
|
+
extractedBinary = outputPath;
|
|
5119
5299
|
}
|
|
5120
|
-
return { name: proxyName, delay: null, error: data.message || "no delay" };
|
|
5121
5300
|
} catch (e) {
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
errorMsg = String(err.response.data.message);
|
|
5126
|
-
} else if (err.message) {
|
|
5127
|
-
errorMsg = err.message;
|
|
5301
|
+
try {
|
|
5302
|
+
fs7.unlinkSync(tempPath);
|
|
5303
|
+
} catch {
|
|
5128
5304
|
}
|
|
5129
|
-
|
|
5305
|
+
throw new Error(`\u89E3\u538B\u5931\u8D25: ${e.message}`);
|
|
5130
5306
|
}
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5307
|
+
const foundBinary = extractedBinary || findBinaryInDir(extractPath);
|
|
5308
|
+
if (!foundBinary) {
|
|
5309
|
+
try {
|
|
5310
|
+
fs7.unlinkSync(tempPath);
|
|
5311
|
+
} catch {
|
|
5312
|
+
}
|
|
5313
|
+
throw new Error("\u89E3\u538B\u540E\u672A\u627E\u5230\u53EF\u6267\u884C\u6587\u4EF6");
|
|
5137
5314
|
}
|
|
5138
|
-
const
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
results[idx] = result;
|
|
5147
|
-
onResult?.(result, completedCount, proxies.length);
|
|
5148
|
-
completedCount++;
|
|
5315
|
+
const targetPath = PATHS.mihomoBinary;
|
|
5316
|
+
if (foundBinary !== targetPath) {
|
|
5317
|
+
if (fs7.existsSync(targetPath)) {
|
|
5318
|
+
fs7.chmodSync(targetPath, 493);
|
|
5319
|
+
try {
|
|
5320
|
+
fs7.unlinkSync(targetPath);
|
|
5321
|
+
} catch {
|
|
5322
|
+
}
|
|
5149
5323
|
}
|
|
5324
|
+
fs7.renameSync(foundBinary, targetPath);
|
|
5150
5325
|
}
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
}
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
const shortened = proxy.name.replace(/_github\.com\/[^_]+/, "");
|
|
5162
|
-
if (shortened !== proxy.name && !usedNames.has(shortened)) {
|
|
5163
|
-
renameMap.set(proxy.name, shortened);
|
|
5164
|
-
usedNames.add(shortened);
|
|
5165
|
-
} else {
|
|
5166
|
-
usedNames.add(proxy.name);
|
|
5326
|
+
fs7.chmodSync(targetPath, 493);
|
|
5327
|
+
if (progressCallback) {
|
|
5328
|
+
progressCallback("\u6821\u9A8C\u5185\u6838...");
|
|
5329
|
+
}
|
|
5330
|
+
const check = spawnSync5(targetPath, ["-v"], { encoding: "utf8", timeout: 5e3 });
|
|
5331
|
+
const checkOutput = `${check.stdout || ""}${check.stderr || ""}`.trim();
|
|
5332
|
+
if (check.error || check.status !== 0 || !/v?\d+\.\d+\.\d+/.test(checkOutput)) {
|
|
5333
|
+
try {
|
|
5334
|
+
fs7.unlinkSync(targetPath);
|
|
5335
|
+
} catch {
|
|
5167
5336
|
}
|
|
5337
|
+
try {
|
|
5338
|
+
fs7.unlinkSync(tempPath);
|
|
5339
|
+
} catch {
|
|
5340
|
+
}
|
|
5341
|
+
throw new Error(`\u5185\u6838\u81EA\u68C0\u5931\u8D25\uFF08\u53EF\u80FD\u4E0B\u8F7D\u635F\u574F\u6216\u67B6\u6784\u4E0D\u5339\u914D\uFF09\uFF0C\u5DF2\u5220\u9664
|
|
5342
|
+
\u9000\u51FA\u7801: ${check.status}
|
|
5343
|
+
\u8F93\u51FA: ${checkOutput || "(\u7A7A)"}`);
|
|
5344
|
+
}
|
|
5345
|
+
try {
|
|
5346
|
+
fs7.unlinkSync(tempPath);
|
|
5347
|
+
} catch {
|
|
5348
|
+
}
|
|
5349
|
+
clearKernelVersionCache();
|
|
5350
|
+
return { version: latest.tag_name, path: targetPath };
|
|
5351
|
+
}
|
|
5352
|
+
|
|
5353
|
+
// src/commands/kernel.ts
|
|
5354
|
+
async function cmdKernel(args) {
|
|
5355
|
+
const mirrorInfo = parseMirrorArg(args);
|
|
5356
|
+
const effectiveMirror = mirrorInfo.mirror;
|
|
5357
|
+
if (effectiveMirror) {
|
|
5358
|
+
const mirrorDesc = mirrorInfo.type === "all" ? " (API\u548C\u4E0B\u8F7D\u5747\u4F7F\u7528\u955C\u50CF)" : " (\u4E0B\u8F7D\u65F6\u4F7F\u7528\u955C\u50CF)";
|
|
5359
|
+
console.log(`\u955C\u50CF: ${effectiveMirror}${mirrorDesc}`);
|
|
5168
5360
|
}
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5361
|
+
console.log("\n\u63D0\u793A: \u5982\u679C\u4E0B\u8F7D\u901F\u5EA6\u8FC7\u6162\u6216\u76F4\u8FDE\u5931\u8D25\uFF0C\u53EF\u4F7F\u7528 --mirror \u53C2\u6570\u901A\u8FC7\u955C\u50CF\u4E0B\u8F7D");
|
|
5362
|
+
console.log("\n\u7528\u6CD5:");
|
|
5363
|
+
console.log(" mihomo kernel # \u76F4\u8FDE");
|
|
5364
|
+
console.log(" mihomo kernel --mirror # \u4E0B\u8F7D\u4F7F\u7528\u9ED8\u8BA4\u955C\u50CF (v6.gh-proxy.org)");
|
|
5365
|
+
console.log(" mihomo kernel --mirror hk.gh-proxy.org # \u4E0B\u8F7D\u4F7F\u7528\u6307\u5B9A\u955C\u50CF");
|
|
5366
|
+
console.log(" mihomo kernel --mirror-all # API\u8BF7\u6C42\u548C\u4E0B\u8F7D\u90FD\u4F7F\u7528\u9ED8\u8BA4\u955C\u50CF");
|
|
5367
|
+
console.log(" mihomo kernel --mirror-all hk.gh-proxy.org # API\u548C\u4E0B\u8F7D\u90FD\u4F7F\u7528\u6307\u5B9A\u955C\u50CF");
|
|
5368
|
+
console.log("\n\u53EF\u7528\u955C\u50CF:");
|
|
5369
|
+
for (const m of AVAILABLE_MIRRORS) {
|
|
5370
|
+
const isCurrent = effectiveMirror && (effectiveMirror.includes(`//${m}/`) || effectiveMirror.includes(`//${m}:`) || effectiveMirror.endsWith(`//${m}`));
|
|
5371
|
+
console.log(` ${m}${isCurrent ? " (\u5F53\u524D)" : ""}`);
|
|
5173
5372
|
}
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5373
|
+
console.log("");
|
|
5374
|
+
console.log("\u68C0\u67E5\u5185\u6838\u66F4\u65B0...");
|
|
5375
|
+
try {
|
|
5376
|
+
const apiMirror = mirrorInfo.type === "all" ? effectiveMirror : null;
|
|
5377
|
+
const info = await checkUpdate(apiMirror);
|
|
5378
|
+
console.log(`\u5F53\u524D: ${info.current}`);
|
|
5379
|
+
console.log(`\u6700\u65B0: ${info.latest}`);
|
|
5380
|
+
if (!info.needsUpdate) {
|
|
5381
|
+
console.log("\u5DF2\u662F\u6700\u65B0\u7248\u672C");
|
|
5382
|
+
} else {
|
|
5383
|
+
console.log("\n\u6B63\u5728\u4E0B\u8F7D...");
|
|
5384
|
+
const result = await downloadKernel((msg) => console.log(msg), mirrorInfo.mirror, info.release);
|
|
5385
|
+
console.log(`
|
|
5386
|
+
\u5DF2\u66F4\u65B0\u5230 ${result.version}`);
|
|
5177
5387
|
}
|
|
5178
|
-
}
|
|
5179
|
-
|
|
5180
|
-
}
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
const removedProxies = originalCount - parsed.proxies.length;
|
|
5186
|
-
let updatedGroups = 0;
|
|
5187
|
-
const removedGroupNames = /* @__PURE__ */ new Set();
|
|
5188
|
-
for (const group of proxyGroups) {
|
|
5189
|
-
if (Array.isArray(group.proxies)) {
|
|
5190
|
-
const before = group.proxies.length;
|
|
5191
|
-
group.proxies = group.proxies.filter((name) => !deadNames.has(name));
|
|
5192
|
-
if (group.proxies.length < before) {
|
|
5193
|
-
updatedGroups++;
|
|
5388
|
+
} catch (e) {
|
|
5389
|
+
console.error(`
|
|
5390
|
+
\u66F4\u65B0\u5931\u8D25: ${e.message}`);
|
|
5391
|
+
const err = e;
|
|
5392
|
+
if (err.response?.data) {
|
|
5393
|
+
if (err.response.data.message) {
|
|
5394
|
+
console.error(`\u539F\u56E0: ${err.response.data.message}`);
|
|
5194
5395
|
}
|
|
5195
|
-
if (
|
|
5196
|
-
|
|
5396
|
+
if (err.response.data.documentation_url) {
|
|
5397
|
+
console.error(`\u6587\u6863: ${err.response.data.documentation_url}`);
|
|
5197
5398
|
}
|
|
5198
5399
|
}
|
|
5400
|
+
process.exit(1);
|
|
5199
5401
|
}
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
if (Array.isArray(rules)) {
|
|
5209
|
-
parsed.raw.rules = rules.filter((rule) => {
|
|
5210
|
-
if (typeof rule !== "string") return true;
|
|
5211
|
-
const parts = rule.split(",");
|
|
5212
|
-
if (parts.length < 2) return true;
|
|
5213
|
-
return !removedGroupNames.has(parts[parts.length - 1].trim());
|
|
5214
|
-
});
|
|
5215
|
-
}
|
|
5402
|
+
}
|
|
5403
|
+
|
|
5404
|
+
// src/commands/log.ts
|
|
5405
|
+
function cmdLog(args) {
|
|
5406
|
+
const logPath = getLogPath();
|
|
5407
|
+
if (hasFlag(args, "-o", "--open")) {
|
|
5408
|
+
openLogFile(logPath);
|
|
5409
|
+
return;
|
|
5216
5410
|
}
|
|
5217
|
-
|
|
5411
|
+
viewLogWithTail(logPath, { follow: true, lines: 50 });
|
|
5218
5412
|
}
|
|
5219
|
-
|
|
5220
|
-
const
|
|
5221
|
-
const
|
|
5222
|
-
const
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
});
|
|
5228
|
-
let removedProxies = 0;
|
|
5229
|
-
let updatedGroups = 0;
|
|
5230
|
-
let removedGroups = 0;
|
|
5231
|
-
let skipped = false;
|
|
5232
|
-
if (summary.dead > 0) {
|
|
5233
|
-
if (summary.alive === 0 || summary.alive / summary.total < 0.01) {
|
|
5234
|
-
skipped = true;
|
|
5413
|
+
function cmdLogs(args) {
|
|
5414
|
+
const targetName = getNonFlagArg(args, 1);
|
|
5415
|
+
const lines = parseIntArg(args, "-n", "--lines", 100);
|
|
5416
|
+
const openInViewer = hasFlag(args, "-o", "--open");
|
|
5417
|
+
if (targetName) {
|
|
5418
|
+
let logPath;
|
|
5419
|
+
if (targetName === "current" || targetName === "0") {
|
|
5420
|
+
logPath = getLogPath();
|
|
5235
5421
|
} else {
|
|
5236
|
-
const
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
const
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
const retrySummary = await testSubscriptionProxies(subName, {
|
|
5245
|
-
...testOptions,
|
|
5246
|
-
parsed: retryParsed,
|
|
5247
|
-
onResult: wrapOnResult(round)
|
|
5248
|
-
});
|
|
5249
|
-
for (const r of retrySummary.results) {
|
|
5250
|
-
if (r.delay !== null) {
|
|
5251
|
-
deadNames.delete(r.name);
|
|
5252
|
-
}
|
|
5422
|
+
const parsedIdx = parseInt(targetName, 10);
|
|
5423
|
+
if (!Number.isNaN(parsedIdx) && parsedIdx > 0 && String(parsedIdx) === targetName) {
|
|
5424
|
+
const archiveLogs = listLogs();
|
|
5425
|
+
const archive = archiveLogs.archives[parsedIdx - 1];
|
|
5426
|
+
if (!archive) {
|
|
5427
|
+
console.error(`\u9519\u8BEF: \u672A\u627E\u5230\u65E5\u5FD7 "${targetName}"`);
|
|
5428
|
+
console.log('\u4F7F\u7528 "mihomo logs" \u67E5\u770B\u53EF\u7528\u65E5\u5FD7\u5217\u8868');
|
|
5429
|
+
process.exit(1);
|
|
5253
5430
|
}
|
|
5431
|
+
logPath = archive.path;
|
|
5432
|
+
} else {
|
|
5433
|
+
logPath = getLogPathByName(targetName);
|
|
5254
5434
|
}
|
|
5255
|
-
summary.dead = deadNames.size;
|
|
5256
|
-
summary.alive = summary.total - summary.dead;
|
|
5257
|
-
if (deadNames.size > 0) {
|
|
5258
|
-
const cleanResult = cleanDeadProxies(parsed, deadNames);
|
|
5259
|
-
removedProxies = cleanResult.removedProxies;
|
|
5260
|
-
updatedGroups = cleanResult.updatedGroups;
|
|
5261
|
-
removedGroups = cleanResult.removedGroups;
|
|
5262
|
-
}
|
|
5263
5435
|
}
|
|
5436
|
+
if (!logPath) {
|
|
5437
|
+
console.error(`\u9519\u8BEF: \u672A\u627E\u5230\u65E5\u5FD7 "${targetName}"`);
|
|
5438
|
+
console.log('\u4F7F\u7528 "mihomo logs" \u67E5\u770B\u53EF\u7528\u65E5\u5FD7\u5217\u8868');
|
|
5439
|
+
process.exit(1);
|
|
5440
|
+
}
|
|
5441
|
+
if (openInViewer) {
|
|
5442
|
+
openLogFile(logPath);
|
|
5443
|
+
return;
|
|
5444
|
+
}
|
|
5445
|
+
viewLogWithTail(logPath, { follow: false, lines });
|
|
5446
|
+
return;
|
|
5264
5447
|
}
|
|
5265
|
-
|
|
5266
|
-
|
|
5448
|
+
const logs = listLogs();
|
|
5449
|
+
const all = [];
|
|
5450
|
+
if (logs.current) all.push(logs.current);
|
|
5451
|
+
all.push(...logs.archives);
|
|
5452
|
+
if (all.length === 0) {
|
|
5453
|
+
console.log("\u6682\u65E0\u65E5\u5FD7");
|
|
5454
|
+
return;
|
|
5267
5455
|
}
|
|
5268
|
-
|
|
5456
|
+
console.log("");
|
|
5457
|
+
console.log("\u65E5\u5FD7\u5217\u8868:");
|
|
5458
|
+
console.log("");
|
|
5459
|
+
let archiveCounter = 0;
|
|
5460
|
+
for (const log of all) {
|
|
5461
|
+
let num;
|
|
5462
|
+
if (log.isCurrent) {
|
|
5463
|
+
num = " 0";
|
|
5464
|
+
} else {
|
|
5465
|
+
archiveCounter++;
|
|
5466
|
+
num = archiveCounter < 10 ? ` ${archiveCounter}` : `${archiveCounter}`;
|
|
5467
|
+
}
|
|
5468
|
+
const time = formatDate(log.mtime);
|
|
5469
|
+
const size = formatBytes(log.size);
|
|
5470
|
+
const name = log.isCurrent ? "mihomo.log (\u5F53\u524D\u8FD0\u884C\u4E2D)" : log.name;
|
|
5471
|
+
console.log(` ${num}. ${name}`);
|
|
5472
|
+
console.log(` \u65F6\u95F4: ${time} \u5927\u5C0F: ${size}`);
|
|
5473
|
+
if (!log.isCurrent) {
|
|
5474
|
+
console.log(` \u67E5\u770B: mihomo logs ${archiveCounter} \u6216 mihomo logs ${archiveCounter} -o`);
|
|
5475
|
+
}
|
|
5476
|
+
console.log("");
|
|
5477
|
+
}
|
|
5478
|
+
console.log("\u7528\u6CD5:");
|
|
5479
|
+
console.log(" mihomo logs 0 # \u67E5\u770B\u5F53\u524D\u65E5\u5FD7 (\u6700\u540E 100 \u884C)");
|
|
5480
|
+
console.log(" mihomo logs 1 # \u67E5\u770B\u7B2C 1 \u4E2A\u5F52\u6863\u65E5\u5FD7\uFF08\u6700\u65B0\uFF09");
|
|
5481
|
+
console.log(" mihomo logs 1 -n 200 # \u67E5\u770B 200 \u884C");
|
|
5482
|
+
console.log(" mihomo logs 1 -o # \u7528\u7CFB\u7EDF\u9ED8\u8BA4\u7A0B\u5E8F\u6253\u5F00");
|
|
5483
|
+
console.log("");
|
|
5269
5484
|
}
|
|
5270
5485
|
|
|
5486
|
+
// src/commands/overwrite.ts
|
|
5487
|
+
import path7 from "path";
|
|
5488
|
+
|
|
5271
5489
|
// src/commands/status.ts
|
|
5272
5490
|
function printStatus() {
|
|
5273
5491
|
const status = getStatus();
|
|
5492
|
+
const daemon = getDaemonStatus();
|
|
5274
5493
|
const info = getConfigInfo();
|
|
5275
5494
|
const overwriteEnabled = isOverwriteEnabled();
|
|
5276
5495
|
const overwriteFiles = listOverwriteFile().files;
|
|
5277
5496
|
const activeSub = getActiveSubscription();
|
|
5497
|
+
const running = daemon.enabled ? isDaemonRunning(daemon) : status.running;
|
|
5498
|
+
const pid = daemon.enabled ? daemon.pid : status.pid;
|
|
5278
5499
|
console.log("");
|
|
5279
5500
|
let modeLabel = "";
|
|
5280
|
-
if (info &&
|
|
5501
|
+
if (info && running) {
|
|
5281
5502
|
modeLabel = colors.cyan(info.tun ? " (TUN)" : " (Mixed)");
|
|
5282
5503
|
}
|
|
5283
|
-
const statusText =
|
|
5504
|
+
const statusText = running ? colors.green("\u25CF \u8FD0\u884C\u4E2D") : colors.yellow("\u4E0D\u5728\u8FD0\u884C");
|
|
5284
5505
|
console.log(`${colors.gray("\u72B6\u6001: ")}${statusText}${modeLabel}`);
|
|
5285
5506
|
console.log(`${colors.gray("\u5185\u6838: ")}${status.kernelVersion || "\u672A\u5B89\u88C5"}`);
|
|
5286
|
-
if (
|
|
5287
|
-
console.log(`${colors.gray("PID: ")}${
|
|
5288
|
-
if (status.processInfo) {
|
|
5507
|
+
if (pid) {
|
|
5508
|
+
console.log(`${colors.gray("PID: ")}${pid}`);
|
|
5509
|
+
if (!daemon.enabled && status.processInfo) {
|
|
5289
5510
|
console.log(`${colors.gray("\u5185\u5B58: ")}${status.processInfo.memory}`);
|
|
5290
5511
|
}
|
|
5291
5512
|
}
|
|
@@ -5316,6 +5537,9 @@ function printStatus() {
|
|
|
5316
5537
|
} else {
|
|
5317
5538
|
console.log(`${colors.gray("\u8986\u5199: ")}${colors.yellow("\u5DF2\u7981\u7528")}`);
|
|
5318
5539
|
}
|
|
5540
|
+
if (daemon.enabled) {
|
|
5541
|
+
console.log(`${colors.gray("\u4FDD\u6D3B: ")}${colors.green("\u5DF2\u542F\u7528")} ${colors.gray("(\u5F00\u673A\u81EA\u542F + \u5D29\u6E83\u91CD\u542F)")}`);
|
|
5542
|
+
}
|
|
5319
5543
|
console.log("");
|
|
5320
5544
|
}
|
|
5321
5545
|
|
|
@@ -5328,6 +5552,11 @@ function handleStopResult(result) {
|
|
|
5328
5552
|
}
|
|
5329
5553
|
}
|
|
5330
5554
|
async function cmdStop() {
|
|
5555
|
+
if (isDaemonEnabled()) {
|
|
5556
|
+
console.log(colors.yellow("\u4FDD\u6D3B\u5DF2\u542F\u7528\uFF0C\u4EE3\u7406\u7531 launchd \u6258\u7BA1"));
|
|
5557
|
+
console.log("\u76F4\u63A5\u505C\u6B62\u4F1A\u88AB\u81EA\u52A8\u91CD\u65B0\u62C9\u8D77\uFF0C\u8BF7\u7528: mihomo daemon off");
|
|
5558
|
+
return;
|
|
5559
|
+
}
|
|
5331
5560
|
const pids = getAllMihomoPids();
|
|
5332
5561
|
if (pids.length === 0) {
|
|
5333
5562
|
console.log(colors.yellow("\u4E0D\u5728\u8FD0\u884C"));
|
|
@@ -5340,8 +5569,8 @@ async function cmdStop() {
|
|
|
5340
5569
|
|
|
5341
5570
|
// src/test-instance.ts
|
|
5342
5571
|
import { spawn as spawn2 } from "child_process";
|
|
5343
|
-
import
|
|
5344
|
-
import
|
|
5572
|
+
import fs8 from "fs";
|
|
5573
|
+
import path6 from "path";
|
|
5345
5574
|
|
|
5346
5575
|
// src/lifecycle.ts
|
|
5347
5576
|
var cleanupFns = /* @__PURE__ */ new Set();
|
|
@@ -5362,20 +5591,20 @@ function runCleanup() {
|
|
|
5362
5591
|
}
|
|
5363
5592
|
|
|
5364
5593
|
// src/test-instance.ts
|
|
5365
|
-
var TEST_DIR =
|
|
5594
|
+
var TEST_DIR = path6.join(USER_DATA_DIR, "test");
|
|
5366
5595
|
var TEST_DIRS = {
|
|
5367
|
-
data:
|
|
5368
|
-
runtime:
|
|
5596
|
+
data: path6.join(TEST_DIR, "data"),
|
|
5597
|
+
runtime: path6.join(TEST_DIR, "runtime")
|
|
5369
5598
|
};
|
|
5370
5599
|
var TEST_PATHS = {
|
|
5371
|
-
configFile:
|
|
5372
|
-
pidFile:
|
|
5373
|
-
logFile:
|
|
5600
|
+
configFile: path6.join(TEST_DIRS.runtime, "config.yaml"),
|
|
5601
|
+
pidFile: path6.join(TEST_DIRS.runtime, "pid"),
|
|
5602
|
+
logFile: path6.join(TEST_DIR, "test.log")
|
|
5374
5603
|
};
|
|
5375
5604
|
var TEST_API = `http://${TEST_CONFIG["external-controller"]}`;
|
|
5376
5605
|
function ensureTestDirs() {
|
|
5377
5606
|
for (const dir of Object.values(TEST_DIRS)) {
|
|
5378
|
-
|
|
5607
|
+
fs8.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
5379
5608
|
}
|
|
5380
5609
|
}
|
|
5381
5610
|
function cleanupTestDir() {
|
|
@@ -5413,21 +5642,21 @@ function buildTestConfig(subName) {
|
|
|
5413
5642
|
rules: ["MATCH,PROXY"]
|
|
5414
5643
|
};
|
|
5415
5644
|
const content = dump(config, { indent: 2, lineWidth: -1, schema: CORE_SCHEMA });
|
|
5416
|
-
|
|
5645
|
+
fs8.writeFileSync(TEST_PATHS.configFile, content, { mode: 384 });
|
|
5417
5646
|
}
|
|
5418
5647
|
async function startTestInstance() {
|
|
5419
5648
|
const binary = PATHS.mihomoBinary;
|
|
5420
|
-
if (!
|
|
5649
|
+
if (!fs8.existsSync(binary)) throw new Error("\u672A\u627E\u5230 mihomo \u5185\u6838");
|
|
5421
5650
|
stopTestInstance();
|
|
5422
|
-
const logFd =
|
|
5651
|
+
const logFd = fs8.openSync(TEST_PATHS.logFile, "a");
|
|
5423
5652
|
const child = spawn2(binary, ["-d", TEST_DIRS.data, "-f", TEST_PATHS.configFile], {
|
|
5424
5653
|
detached: true,
|
|
5425
5654
|
stdio: ["ignore", logFd, logFd]
|
|
5426
5655
|
});
|
|
5427
|
-
|
|
5656
|
+
fs8.closeSync(logFd);
|
|
5428
5657
|
child.unref();
|
|
5429
5658
|
const pid = child.pid;
|
|
5430
|
-
|
|
5659
|
+
fs8.writeFileSync(TEST_PATHS.pidFile, pid.toString(), { mode: 384 });
|
|
5431
5660
|
const client = createHttpClient({ timeout: 2e3 });
|
|
5432
5661
|
let ready = false;
|
|
5433
5662
|
for (let i = 0; i < 60; i++) {
|
|
@@ -5443,7 +5672,7 @@ async function startTestInstance() {
|
|
|
5443
5672
|
if (!isProcessRunning(pid)) {
|
|
5444
5673
|
let errorDetail = "";
|
|
5445
5674
|
try {
|
|
5446
|
-
errorDetail =
|
|
5675
|
+
errorDetail = fs8.readFileSync(TEST_PATHS.logFile, "utf8").slice(-1e3);
|
|
5447
5676
|
} catch {
|
|
5448
5677
|
}
|
|
5449
5678
|
throw new Error(`\u6D4B\u8BD5\u5B9E\u4F8B\u542F\u52A8\u5931\u8D25${errorDetail ? `
|
|
@@ -5456,7 +5685,7 @@ ${errorDetail}` : ""}`);
|
|
|
5456
5685
|
function stopTestInstance() {
|
|
5457
5686
|
let pid;
|
|
5458
5687
|
try {
|
|
5459
|
-
pid = parseInt(
|
|
5688
|
+
pid = parseInt(fs8.readFileSync(TEST_PATHS.pidFile, "utf8").trim(), 10);
|
|
5460
5689
|
} catch {
|
|
5461
5690
|
return;
|
|
5462
5691
|
}
|
|
@@ -5468,7 +5697,7 @@ function stopTestInstance() {
|
|
|
5468
5697
|
}
|
|
5469
5698
|
}
|
|
5470
5699
|
try {
|
|
5471
|
-
|
|
5700
|
+
fs8.unlinkSync(TEST_PATHS.pidFile);
|
|
5472
5701
|
} catch {
|
|
5473
5702
|
}
|
|
5474
5703
|
}
|
|
@@ -5744,7 +5973,7 @@ async function cmdSubscription(args) {
|
|
|
5744
5973
|
}
|
|
5745
5974
|
const status = getStatus();
|
|
5746
5975
|
const configInfo = getConfigInfo();
|
|
5747
|
-
const currentMode = configInfo?.tun ? "tun" : "mixed";
|
|
5976
|
+
const currentMode = isDaemonEnabled() ? "mixed" : configInfo?.tun ? "tun" : "mixed";
|
|
5748
5977
|
const success = setDefaultSubscription(target.name);
|
|
5749
5978
|
if (success) {
|
|
5750
5979
|
console.log(`\u5DF2\u5207\u6362\u5230 "${target.name}"`);
|
|
@@ -5752,7 +5981,7 @@ async function cmdSubscription(args) {
|
|
|
5752
5981
|
console.error(`\u9519\u8BEF: \u672A\u627E\u5230\u8BA2\u9605 "${name}"`);
|
|
5753
5982
|
process.exit(1);
|
|
5754
5983
|
}
|
|
5755
|
-
if (status.running) {
|
|
5984
|
+
if (status.running || isDaemonEnabled()) {
|
|
5756
5985
|
console.log("");
|
|
5757
5986
|
await cmdStart(["start", currentMode]);
|
|
5758
5987
|
return;
|
|
@@ -5878,12 +6107,19 @@ async function cmdSubscription(args) {
|
|
|
5878
6107
|
}
|
|
5879
6108
|
|
|
5880
6109
|
// src/commands/start.ts
|
|
6110
|
+
var DAEMON_RESTART_WAIT_MS = 500;
|
|
5881
6111
|
async function cmdStart(args) {
|
|
5882
6112
|
if (!hasKernel()) {
|
|
5883
6113
|
console.error('\u9519\u8BEF: \u672A\u627E\u5230\u5185\u6838\uFF0C\u8BF7\u8FD0\u884C "mihomo kernel"');
|
|
5884
6114
|
process.exit(1);
|
|
5885
6115
|
}
|
|
5886
6116
|
const targetMode = args[1] === "tun" ? "tun" : "mixed";
|
|
6117
|
+
const daemonEnabled = isDaemonEnabled();
|
|
6118
|
+
if (targetMode === "tun" && daemonEnabled) {
|
|
6119
|
+
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`);
|
|
6120
|
+
console.error("\u8BF7\u5148\u5173\u95ED\u4FDD\u6D3B: mihomo daemon off");
|
|
6121
|
+
process.exit(1);
|
|
6122
|
+
}
|
|
5887
6123
|
const rounds = parseIntArg(args, "-r", "--rounds", DEFAULT_CLEAN_ROUNDS);
|
|
5888
6124
|
const timeout = parseIntArg(args, "-t", "--timeout", DEFAULT_TEST_TIMEOUT);
|
|
5889
6125
|
const concurrency = parseIntArg(args, "-j", "--concurrency", DEFAULT_TEST_CONCURRENCY);
|
|
@@ -5897,16 +6133,27 @@ async function cmdStart(args) {
|
|
|
5897
6133
|
if (!skipUpdate) {
|
|
5898
6134
|
await autoUpdateStaleSubscription({ timeout: updateTimeout });
|
|
5899
6135
|
}
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
6136
|
+
async function launchOrRestart() {
|
|
6137
|
+
if (daemonEnabled) {
|
|
6138
|
+
restartDaemon();
|
|
6139
|
+
await sleep(DAEMON_RESTART_WAIT_MS);
|
|
6140
|
+
return getDaemonStatus().pid;
|
|
6141
|
+
}
|
|
6142
|
+
const result = await start(targetMode);
|
|
6143
|
+
return result.pid;
|
|
5905
6144
|
}
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
6145
|
+
if (!daemonEnabled) {
|
|
6146
|
+
const status = getStatus();
|
|
6147
|
+
const hasProcess = status.running || status.allProcesses.length > 0;
|
|
6148
|
+
if (hasProcess) {
|
|
6149
|
+
const count = status.allProcesses.length > 0 ? status.allProcesses.length : 1;
|
|
6150
|
+
console.log(`\u505C\u6B62 ${count} \u4E2A\u8FDB\u7A0B...`);
|
|
6151
|
+
}
|
|
6152
|
+
handleStopResult(stop());
|
|
6153
|
+
if (hasProcess) {
|
|
6154
|
+
console.log(`${colors.green("\u5DF2\u505C\u6B62\u8FDB\u7A0B")}
|
|
5909
6155
|
`);
|
|
6156
|
+
}
|
|
5910
6157
|
}
|
|
5911
6158
|
let configInfo;
|
|
5912
6159
|
try {
|
|
@@ -5918,8 +6165,9 @@ async function cmdStart(args) {
|
|
|
5918
6165
|
const modeLabel = targetMode === "tun" ? "TUN" : "Mixed";
|
|
5919
6166
|
console.log([colors.cyan(modeLabel), sub.name, formatProxySummary(configInfo)].join(" \xB7 "));
|
|
5920
6167
|
try {
|
|
5921
|
-
const
|
|
5922
|
-
|
|
6168
|
+
const pid = await launchOrRestart();
|
|
6169
|
+
const label = daemonEnabled ? "\u5DF2\u542F\u52A8 (\u4FDD\u6D3B)" : "\u5DF2\u542F\u52A8";
|
|
6170
|
+
console.log(`${colors.green(label)}${pid ? ` (PID ${pid})` : ""}`);
|
|
5923
6171
|
} catch (e) {
|
|
5924
6172
|
const msg = e.message;
|
|
5925
6173
|
const lines = msg.split("\n");
|
|
@@ -5951,11 +6199,11 @@ async function cmdStart(args) {
|
|
|
5951
6199
|
console.log(`${colors.green("\u5DF2\u6E05\u7406")}: ${formatCleanSummary(cleanResult)}`);
|
|
5952
6200
|
console.log("");
|
|
5953
6201
|
console.log("\u91CD\u65B0\u52A0\u8F7D\u914D\u7F6E...");
|
|
5954
|
-
handleStopResult(stop());
|
|
6202
|
+
if (!daemonEnabled) handleStopResult(stop());
|
|
5955
6203
|
try {
|
|
5956
6204
|
configInfo = prepareConfigForStart(targetMode, sub.name);
|
|
5957
|
-
const
|
|
5958
|
-
console.log(`${colors.green("\u5DF2\u91CD\u542F")} (PID ${
|
|
6205
|
+
const pid = await launchOrRestart();
|
|
6206
|
+
console.log(`${colors.green("\u5DF2\u91CD\u542F")}${pid ? ` (PID ${pid})` : ""} \xB7 ${formatProxySummary(configInfo)}`);
|
|
5959
6207
|
} catch (e) {
|
|
5960
6208
|
console.error(`${colors.red("\u91CD\u542F\u5931\u8D25:")} ${e.message.split("\n")[0]}`);
|
|
5961
6209
|
process.exit(1);
|
|
@@ -5975,8 +6223,8 @@ function printOverwriteList() {
|
|
|
5975
6223
|
if (info.files.length === 0) {
|
|
5976
6224
|
console.log("\u6682\u65E0\u8986\u5199\u6587\u4EF6");
|
|
5977
6225
|
console.log("");
|
|
5978
|
-
console.log(`\u7528\u6CD5\u793A\u4F8B: \u521B\u5EFA\u6587\u4EF6 ${
|
|
5979
|
-
console.log(` \u6216 ${
|
|
6226
|
+
console.log(`\u7528\u6CD5\u793A\u4F8B: \u521B\u5EFA\u6587\u4EF6 ${path7.join(info.dir, "overwrite.yaml")}`);
|
|
6227
|
+
console.log(` \u6216 ${path7.join(info.dir, "overwrite.dns.yaml")}`);
|
|
5980
6228
|
console.log("");
|
|
5981
6229
|
} else {
|
|
5982
6230
|
console.log(`${colors.cyan("\u8986\u5199\u6587\u4EF6")} (${info.files.length} \u4E2A\uFF0C\u6309\u987A\u5E8F\u52A0\u8F7D):`);
|
|
@@ -5998,7 +6246,7 @@ async function cmdOverwrite(args) {
|
|
|
5998
6246
|
const action = args?.[1];
|
|
5999
6247
|
const status = getStatus();
|
|
6000
6248
|
const configInfo = getConfigInfo();
|
|
6001
|
-
const currentMode = configInfo?.tun ? "tun" : "mixed";
|
|
6249
|
+
const currentMode = isDaemonEnabled() ? "mixed" : configInfo?.tun ? "tun" : "mixed";
|
|
6002
6250
|
if (action === "on" || action === "enable") {
|
|
6003
6251
|
if (isOverwriteEnabled()) {
|
|
6004
6252
|
console.log("\u8986\u5199\u914D\u7F6E\u5DF2\u662F\u542F\u7528\u72B6\u6001");
|
|
@@ -6008,7 +6256,7 @@ async function cmdOverwrite(args) {
|
|
|
6008
6256
|
}
|
|
6009
6257
|
setOverwriteEnabled(true);
|
|
6010
6258
|
console.log("\u5DF2\u542F\u7528\u8986\u5199\u914D\u7F6E");
|
|
6011
|
-
if (status.running) {
|
|
6259
|
+
if (status.running || isDaemonEnabled()) {
|
|
6012
6260
|
console.log("");
|
|
6013
6261
|
await cmdStart(["start", currentMode]);
|
|
6014
6262
|
return;
|
|
@@ -6026,7 +6274,7 @@ async function cmdOverwrite(args) {
|
|
|
6026
6274
|
}
|
|
6027
6275
|
setOverwriteEnabled(false);
|
|
6028
6276
|
console.log("\u5DF2\u7981\u7528\u8986\u5199\u914D\u7F6E");
|
|
6029
|
-
if (status.running) {
|
|
6277
|
+
if (status.running || isDaemonEnabled()) {
|
|
6030
6278
|
console.log("");
|
|
6031
6279
|
await cmdStart(["start", currentMode]);
|
|
6032
6280
|
return;
|
|
@@ -6040,7 +6288,7 @@ async function cmdOverwrite(args) {
|
|
|
6040
6288
|
}
|
|
6041
6289
|
|
|
6042
6290
|
// src/commands/reset.ts
|
|
6043
|
-
import
|
|
6291
|
+
import fs9 from "fs";
|
|
6044
6292
|
import readline from "readline";
|
|
6045
6293
|
var RESET_TARGETS = [
|
|
6046
6294
|
{
|
|
@@ -6095,10 +6343,22 @@ var RESET_TARGETS = [
|
|
|
6095
6343
|
label: "\u8986\u5199",
|
|
6096
6344
|
paths: () => {
|
|
6097
6345
|
const dir = USER_DATA_DIR;
|
|
6098
|
-
if (!
|
|
6099
|
-
return
|
|
6346
|
+
if (!fs9.existsSync(dir)) return [];
|
|
6347
|
+
return fs9.readdirSync(dir).filter((f) => f === "overwrite.yaml" || /^overwrite\..+\.ya?ml$/.test(f)).map((f) => `${dir}/${f}`);
|
|
6100
6348
|
},
|
|
6101
6349
|
needsStop: false
|
|
6350
|
+
},
|
|
6351
|
+
{
|
|
6352
|
+
id: "daemon",
|
|
6353
|
+
aliases: ["daemon"],
|
|
6354
|
+
label: "\u4FDD\u6D3B",
|
|
6355
|
+
// 删除全部交给 onAfter:disableDaemon 会先 bootout 卸载、再删 plist、再兜底清理进程,
|
|
6356
|
+
// 顺序正确(先卸载再删文件);此处返回空避免提前删掉 plist 破坏卸载。
|
|
6357
|
+
paths: () => [],
|
|
6358
|
+
needsStop: false,
|
|
6359
|
+
onAfter: () => disableDaemon(),
|
|
6360
|
+
checkEmpty: () => !isDaemonEnabled(),
|
|
6361
|
+
emptyMsg: "\u4FDD\u6D3B\u672A\u542F\u7528\uFF0C\u65E0\u9700\u5220\u9664"
|
|
6102
6362
|
}
|
|
6103
6363
|
];
|
|
6104
6364
|
function resolveResetTargets(names) {
|
|
@@ -6148,7 +6408,7 @@ async function cmdReset(args) {
|
|
|
6148
6408
|
}
|
|
6149
6409
|
targets = matched;
|
|
6150
6410
|
} else {
|
|
6151
|
-
targets = RESET_TARGETS.filter((t) => !["settings", "kernel", "overwrites"].includes(t.id));
|
|
6411
|
+
targets = RESET_TARGETS.filter((t) => !["settings", "kernel", "overwrites", "daemon"].includes(t.id));
|
|
6152
6412
|
}
|
|
6153
6413
|
for (const t of targets) {
|
|
6154
6414
|
if (t.checkEmpty?.()) {
|
|
@@ -6160,25 +6420,34 @@ async function cmdReset(args) {
|
|
|
6160
6420
|
}
|
|
6161
6421
|
const needsStop = targets.some((t) => t.needsStop);
|
|
6162
6422
|
const warnRunning = targets.some((t) => t.warnIfRunning);
|
|
6423
|
+
const kernelTargeted = targets.some((t) => t.id === "kernel");
|
|
6424
|
+
const disablesDaemon = needsStop || kernelTargeted;
|
|
6163
6425
|
const pids = needsStop || warnRunning ? getAllMihomoPids() : [];
|
|
6164
|
-
if (
|
|
6165
|
-
console.log(`\u505C\u6B62 ${pids.length} \u4E2A\u8FDB\u7A0B...`);
|
|
6166
|
-
cleanupAll();
|
|
6167
|
-
for (let i = 0; i < PROCESS_WAIT_ATTEMPTS; i++) {
|
|
6168
|
-
if (getAllMihomoPids().length === 0) break;
|
|
6169
|
-
await new Promise((r) => setTimeout(r, PROCESS_WAIT_INTERVAL));
|
|
6170
|
-
}
|
|
6171
|
-
} else if (warnRunning && pids.length > 0) {
|
|
6426
|
+
if (warnRunning && pids.length > 0) {
|
|
6172
6427
|
console.log(colors.yellow(`\u8B66\u544A: mihomo \u6B63\u5728\u8FD0\u884C (PID ${pids.join(", ")})\uFF0C\u5220\u9664\u5185\u6838\u540E\u5C06\u65E0\u6CD5\u91CD\u65B0\u542F\u52A8`));
|
|
6173
6428
|
}
|
|
6429
|
+
if (disablesDaemon && isDaemonEnabled()) {
|
|
6430
|
+
console.log(colors.yellow("\u4FDD\u6D3B\u5DF2\u542F\u7528\uFF0C\u91CD\u7F6E\u5C06\u4E00\u5E76\u5173\u95ED\u4FDD\u6D3B\uFF08\u79FB\u9664\u5F00\u673A\u81EA\u542F\uFF09"));
|
|
6431
|
+
}
|
|
6174
6432
|
console.log(`\u5C06\u5220\u9664: ${targets.map((t) => t.label).join("\u3001")}`);
|
|
6175
6433
|
if (!skipConfirm && !await confirmPrompt("\u786E\u8BA4?")) {
|
|
6176
6434
|
console.log("\u5DF2\u53D6\u6D88");
|
|
6177
6435
|
return;
|
|
6178
6436
|
}
|
|
6437
|
+
if (disablesDaemon && isDaemonEnabled()) {
|
|
6438
|
+
disableDaemon();
|
|
6439
|
+
}
|
|
6440
|
+
if (needsStop && getAllMihomoPids().length > 0) {
|
|
6441
|
+
console.log("\u505C\u6B62\u8FDB\u7A0B...");
|
|
6442
|
+
cleanupAll();
|
|
6443
|
+
for (let i = 0; i < PROCESS_WAIT_ATTEMPTS; i++) {
|
|
6444
|
+
if (getAllMihomoPids().length === 0) break;
|
|
6445
|
+
await new Promise((r) => setTimeout(r, PROCESS_WAIT_INTERVAL));
|
|
6446
|
+
}
|
|
6447
|
+
}
|
|
6179
6448
|
for (const t of targets) {
|
|
6180
6449
|
for (const p of t.paths()) {
|
|
6181
|
-
if (
|
|
6450
|
+
if (fs9.existsSync(p)) {
|
|
6182
6451
|
try {
|
|
6183
6452
|
rmrf(p);
|
|
6184
6453
|
} catch (e) {
|
|
@@ -6197,6 +6466,13 @@ async function cmdReset(args) {
|
|
|
6197
6466
|
|
|
6198
6467
|
// src/commands/test.ts
|
|
6199
6468
|
function requireRunning() {
|
|
6469
|
+
if (isDaemonEnabled()) {
|
|
6470
|
+
if (!isDaemonRunning(getDaemonStatus())) {
|
|
6471
|
+
console.error("\u9519\u8BEF: mihomo \u672A\u8FD0\u884C\uFF0C\u8BF7\u5148\u542F\u52A8 (mihomo daemon on)");
|
|
6472
|
+
process.exit(1);
|
|
6473
|
+
}
|
|
6474
|
+
return;
|
|
6475
|
+
}
|
|
6200
6476
|
const status = getStatus();
|
|
6201
6477
|
if (!status.running) {
|
|
6202
6478
|
console.error("\u9519\u8BEF: mihomo \u672A\u8FD0\u884C\uFF0C\u8BF7\u5148\u542F\u52A8 (mihomo start)");
|
|
@@ -6256,11 +6532,22 @@ async function cmdClean(args) {
|
|
|
6256
6532
|
console.log(`${colors.green("\u5DF2\u6E05\u7406")}: ${formatCleanSummary(result)}`);
|
|
6257
6533
|
console.log("");
|
|
6258
6534
|
console.log("\u91CD\u542F mihomo \u4F7F\u66F4\u6539\u751F\u6548...");
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
|
|
6535
|
+
if (isDaemonEnabled()) {
|
|
6536
|
+
const configInfo = prepareConfigForStart("mixed", activeSub.name);
|
|
6537
|
+
try {
|
|
6538
|
+
restartDaemon();
|
|
6539
|
+
} catch (e) {
|
|
6540
|
+
console.error(`${colors.red("\u91CD\u542F\u5931\u8D25:")} ${e.message.split("\n")[0]}`);
|
|
6541
|
+
process.exit(1);
|
|
6542
|
+
}
|
|
6543
|
+
console.log(`${colors.green("\u5DF2\u91CD\u542F (\u4FDD\u6D3B)")} \xB7 ${formatProxySummary(configInfo)}`);
|
|
6544
|
+
} else {
|
|
6545
|
+
const currentMode = getConfigInfo()?.tun ? "tun" : "mixed";
|
|
6546
|
+
handleStopResult(stop());
|
|
6547
|
+
const configInfo = prepareConfigForStart(currentMode, activeSub.name);
|
|
6548
|
+
const startResult = await start(currentMode);
|
|
6549
|
+
console.log(`${colors.green("\u5DF2\u91CD\u542F")} (PID ${startResult.pid}) \xB7 ${formatProxySummary(configInfo)}`);
|
|
6550
|
+
}
|
|
6264
6551
|
}
|
|
6265
6552
|
}
|
|
6266
6553
|
|
|
@@ -6422,6 +6709,9 @@ async function main() {
|
|
|
6422
6709
|
case "reset":
|
|
6423
6710
|
await cmdReset(args);
|
|
6424
6711
|
break;
|
|
6712
|
+
case "daemon":
|
|
6713
|
+
await cmdDaemon(args);
|
|
6714
|
+
break;
|
|
6425
6715
|
case "on":
|
|
6426
6716
|
await cmdOverwrite(["ow", "on"]);
|
|
6427
6717
|
break;
|