mihomo-cli 1.2.2 → 1.2.4
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 +41 -0
- package/README.md +44 -42
- package/index.js +134 -160
- package/package.json +1 -1
- package/src/config.js +32 -35
- package/src/kernel.js +16 -12
- package/src/overwrite.js +8 -8
- package/src/process.js +57 -91
- package/src/subscription.js +10 -40
- package/src/utils.js +101 -0
package/index.js
CHANGED
|
@@ -8,6 +8,7 @@ const kernel = require('./src/kernel');
|
|
|
8
8
|
const subscription = require('./src/subscription');
|
|
9
9
|
const processMgr = require('./src/process');
|
|
10
10
|
const overwrite = require('./src/overwrite');
|
|
11
|
+
const utils = require('./src/utils');
|
|
11
12
|
|
|
12
13
|
const VERSION = require('./package.json').version;
|
|
13
14
|
|
|
@@ -33,7 +34,7 @@ process.on('SIGTERM', () => {
|
|
|
33
34
|
process.exit(0);
|
|
34
35
|
});
|
|
35
36
|
|
|
36
|
-
process.on('uncaughtException',
|
|
37
|
+
process.on('uncaughtException', e => {
|
|
37
38
|
console.error('\n未捕获的异常: ' + e.message);
|
|
38
39
|
if (e.stack) {
|
|
39
40
|
console.error(e.stack.split('\n').slice(1).join('\n'));
|
|
@@ -41,80 +42,79 @@ process.on('uncaughtException', (e) => {
|
|
|
41
42
|
process.exit(1);
|
|
42
43
|
});
|
|
43
44
|
|
|
44
|
-
process.on('unhandledRejection',
|
|
45
|
+
process.on('unhandledRejection', reason => {
|
|
45
46
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
46
47
|
console.error('\n未处理的 Promise 拒绝: ' + msg);
|
|
47
48
|
process.exit(1);
|
|
48
49
|
});
|
|
49
50
|
|
|
50
51
|
function printShortHelp() {
|
|
51
|
-
console.log('\nmihomo-cli v' + VERSION);
|
|
52
|
-
console.log(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
' logs 日志列表\n' +
|
|
60
|
-
' subscription 订阅管理(别名 sub)\n' +
|
|
61
|
-
' overwrite [on|off] 覆写配置(别名 ow)\n' +
|
|
62
|
-
' directory 数据目录(别名 dir)\n' +
|
|
63
|
-
' kernel 更新内核\n' +
|
|
64
|
-
' reset 重置配置\n' +
|
|
65
|
-
' version 版本信息\n');
|
|
52
|
+
console.log('\nmihomo-cli v' + VERSION + ' (mihomo help 查看完整帮助)\n');
|
|
53
|
+
console.log(
|
|
54
|
+
'常用命令:\n' +
|
|
55
|
+
' start [tun|mixed] 启动/切换代理\n' +
|
|
56
|
+
' ui [zash|dash|yacd] 打开 Web UI\n' +
|
|
57
|
+
' ow [on|off] 覆写配置\n' +
|
|
58
|
+
' sub [use|update] 订阅管理\n',
|
|
59
|
+
);
|
|
66
60
|
}
|
|
67
61
|
|
|
68
62
|
function printHelp() {
|
|
69
|
-
console.log(
|
|
70
|
-
'\
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
63
|
+
console.log(
|
|
64
|
+
'\nmihomo-cli v' +
|
|
65
|
+
VERSION +
|
|
66
|
+
'\n' +
|
|
67
|
+
'\n' +
|
|
68
|
+
'命令别名: mihomo, mmc, mh\n' +
|
|
69
|
+
'\n' +
|
|
70
|
+
'用法:\n' +
|
|
71
|
+
' mihomo <命令> [选项]\n' +
|
|
72
|
+
'\n' +
|
|
73
|
+
'控制:\n' +
|
|
74
|
+
' start [tun|mixed] 启动/切换代理 (默认 mixed)\n' +
|
|
75
|
+
' stop 停止代理\n' +
|
|
76
|
+
' status 查看状态\n' +
|
|
77
|
+
'\n' +
|
|
78
|
+
'界面:\n' +
|
|
79
|
+
' ui [zash|dash|yacd] 打开 Web UI (默认 zash)\n' +
|
|
80
|
+
' log [-o] 实时日志(-o 打开文件)\n' +
|
|
81
|
+
' logs [编号] [-n N] [-o] 日志列表(0=当前,1+=归档)\n' +
|
|
82
|
+
'\n' +
|
|
83
|
+
'订阅:\n' +
|
|
84
|
+
' subscription 列出所有订阅(别名 sub)\n' +
|
|
85
|
+
' subscription add <url> [name] 添加订阅\n' +
|
|
86
|
+
' subscription update [name] 更新订阅(无参更新所有)\n' +
|
|
87
|
+
' subscription use <name> 切换默认订阅\n' +
|
|
88
|
+
' subscription web [name] 打开订阅页面\n' +
|
|
89
|
+
'\n' +
|
|
90
|
+
'配置:\n' +
|
|
91
|
+
' overwrite 查看覆写状态(别名 ow)\n' +
|
|
92
|
+
' overwrite on|off 启用/禁用覆写配置\n' +
|
|
93
|
+
' directory 显示数据目录位置(别名 dir)\n' +
|
|
94
|
+
' directory open [target] 打开目录: root|subs|logs|overwrites|...\n' +
|
|
95
|
+
'\n' +
|
|
96
|
+
'系统:\n' +
|
|
97
|
+
' kernel [镜像|--no-mirror] 更新内核\n' +
|
|
98
|
+
' reset [--full] 重置用户数据 (--full 同时删除内核)\n' +
|
|
99
|
+
' help, -h 显示帮助\n' +
|
|
100
|
+
' version, -v 显示版本\n' +
|
|
101
|
+
'\n' +
|
|
102
|
+
'示例:\n' +
|
|
103
|
+
' mihomo start # 启动/重启 Mixed 模式\n' +
|
|
104
|
+
' mihomo start tun # 切换到 TUN 透明代理模式\n' +
|
|
105
|
+
' mihomo sub add <url> # 添加订阅 (sub 是 subscription 别名)\n' +
|
|
106
|
+
' mihomo ui # 打开 Web UI\n' +
|
|
107
|
+
'\n' +
|
|
108
|
+
'模式说明:\n' +
|
|
109
|
+
' mixed HTTP + SOCKS5 混合端口 (默认)\n' +
|
|
110
|
+
' tun 透明代理,全局自动路由,需要 sudo\n' +
|
|
111
|
+
'\n' +
|
|
112
|
+
'数据目录:\n' +
|
|
113
|
+
' 环境变量 MIHOMO_CLI_DIR 可自定义位置\n' +
|
|
114
|
+
' 默认: ' +
|
|
115
|
+
config.USER_DATA_DIR +
|
|
116
|
+
'\n',
|
|
117
|
+
);
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
function printVersion() {
|
|
@@ -126,9 +126,9 @@ function printVersion() {
|
|
|
126
126
|
|
|
127
127
|
function printStatus() {
|
|
128
128
|
const status = processMgr.getStatus();
|
|
129
|
-
const info =
|
|
129
|
+
const info = config.getConfigInfo();
|
|
130
130
|
const owEnabled = overwrite.isOverwriteEnabled();
|
|
131
|
-
const owFiles = overwrite.
|
|
131
|
+
const owFiles = overwrite.listOverwriteFile().files;
|
|
132
132
|
const activeSub = getActiveSubscription();
|
|
133
133
|
|
|
134
134
|
console.log('');
|
|
@@ -160,12 +160,7 @@ function printStatus() {
|
|
|
160
160
|
if (activeSub) {
|
|
161
161
|
let subLine = '订阅: ' + activeSub.name;
|
|
162
162
|
if (info) {
|
|
163
|
-
|
|
164
|
-
if (info.proxyGroups && info.proxyGroups > 0) {
|
|
165
|
-
parts.push(info.proxyGroups + ' 组');
|
|
166
|
-
}
|
|
167
|
-
parts.push(info.proxies + ' 节点');
|
|
168
|
-
subLine += ' (' + parts.join(', ') + ')';
|
|
163
|
+
subLine += ' (' + subscription.formatProxySummary(info) + ')';
|
|
169
164
|
}
|
|
170
165
|
console.log(subLine);
|
|
171
166
|
} else {
|
|
@@ -213,7 +208,7 @@ function findSubscriptionFuzzy(subs, pattern) {
|
|
|
213
208
|
return includes;
|
|
214
209
|
}
|
|
215
210
|
|
|
216
|
-
function pickSingleSubscription(subs, pattern
|
|
211
|
+
function pickSingleSubscription(subs, pattern) {
|
|
217
212
|
if (subs.length === 0) {
|
|
218
213
|
console.error('错误: 未找到匹配 "' + pattern + '" 的订阅');
|
|
219
214
|
process.exit(1);
|
|
@@ -227,32 +222,6 @@ function pickSingleSubscription(subs, pattern, actionName) {
|
|
|
227
222
|
process.exit(1);
|
|
228
223
|
}
|
|
229
224
|
|
|
230
|
-
function hasFlag(args, short, long) {
|
|
231
|
-
return args && (args.includes(short) || args.includes(long));
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
function parseIntArg(args, short, long, defaultValue) {
|
|
235
|
-
if (!args) return defaultValue;
|
|
236
|
-
for (let i = 0; i < args.length; i++) {
|
|
237
|
-
if (args[i] === short || args[i] === long) {
|
|
238
|
-
if (i + 1 < args.length) {
|
|
239
|
-
const val = parseInt(args[i + 1]);
|
|
240
|
-
return isNaN(val) ? defaultValue : val;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
return defaultValue;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
function getNonFlagArg(args, startIdx) {
|
|
248
|
-
if (!args) return null;
|
|
249
|
-
for (let i = startIdx; i < args.length; i++) {
|
|
250
|
-
if (!args[i].startsWith('-')) {
|
|
251
|
-
return args[i];
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
return null;
|
|
255
|
-
}
|
|
256
225
|
|
|
257
226
|
function openLogFile(logPath, label) {
|
|
258
227
|
const displayLabel = label || logPath;
|
|
@@ -291,7 +260,7 @@ function viewLogWithTail(logPath, options) {
|
|
|
291
260
|
const tail = spawn('tail', tailArgs, { stdio: 'inherit' });
|
|
292
261
|
|
|
293
262
|
tail.on('close', () => process.exit(0));
|
|
294
|
-
tail.on('error',
|
|
263
|
+
tail.on('error', e => {
|
|
295
264
|
console.error('无法读取日志: ' + e.message);
|
|
296
265
|
process.exit(1);
|
|
297
266
|
});
|
|
@@ -311,7 +280,7 @@ async function cmdStart(args) {
|
|
|
311
280
|
process.exit(1);
|
|
312
281
|
}
|
|
313
282
|
|
|
314
|
-
await subscription.
|
|
283
|
+
await subscription.autoUpdateStaleSubscription();
|
|
315
284
|
|
|
316
285
|
// 每次 start 都先确保完全干净的状态(停止进程 + 清理运行时文件)
|
|
317
286
|
const status = processMgr.getStatus();
|
|
@@ -344,10 +313,7 @@ async function cmdStart(args) {
|
|
|
344
313
|
}
|
|
345
314
|
|
|
346
315
|
const modeLabel = targetMode === 'tun' ? 'TUN' : 'Mixed';
|
|
347
|
-
|
|
348
|
-
if (cfgInfo.proxyGroups && cfgInfo.proxyGroups > 0) parts.push(cfgInfo.proxyGroups + ' 组');
|
|
349
|
-
parts.push(cfgInfo.proxies + ' 节点');
|
|
350
|
-
console.log([modeLabel, sub.name, parts.join(', ')].join(' · '));
|
|
316
|
+
console.log([modeLabel, sub.name, subscription.formatProxySummary(cfgInfo)].join(' · '));
|
|
351
317
|
|
|
352
318
|
try {
|
|
353
319
|
const result = await processMgr.start(targetMode);
|
|
@@ -377,7 +343,7 @@ async function cmdStop() {
|
|
|
377
343
|
console.log('已停止');
|
|
378
344
|
}
|
|
379
345
|
|
|
380
|
-
function
|
|
346
|
+
function cmdUI(args) {
|
|
381
347
|
const uiName = args[1] || 'zash';
|
|
382
348
|
const url = UI_URLS[uiName];
|
|
383
349
|
|
|
@@ -396,11 +362,10 @@ function cmdUi(args) {
|
|
|
396
362
|
}
|
|
397
363
|
}
|
|
398
364
|
|
|
399
|
-
|
|
400
365
|
function cmdLog(args) {
|
|
401
366
|
const logPath = processMgr.getLogPath();
|
|
402
367
|
|
|
403
|
-
if (hasFlag(args, '-o', '--open')) {
|
|
368
|
+
if (utils.hasFlag(args, '-o', '--open')) {
|
|
404
369
|
openLogFile(logPath);
|
|
405
370
|
return;
|
|
406
371
|
}
|
|
@@ -409,9 +374,9 @@ function cmdLog(args) {
|
|
|
409
374
|
}
|
|
410
375
|
|
|
411
376
|
function cmdLogs(args) {
|
|
412
|
-
const targetName = getNonFlagArg(args, 1);
|
|
413
|
-
const lines = parseIntArg(args, '-n', '--lines', 100);
|
|
414
|
-
const openInViewer = hasFlag(args, '-o', '--open');
|
|
377
|
+
const targetName = utils.getNonFlagArg(args, 1);
|
|
378
|
+
const lines = utils.parseIntArg(args, '-n', '--lines', 100);
|
|
379
|
+
const openInViewer = utils.hasFlag(args, '-o', '--open');
|
|
415
380
|
|
|
416
381
|
if (targetName) {
|
|
417
382
|
let logPath;
|
|
@@ -419,7 +384,20 @@ function cmdLogs(args) {
|
|
|
419
384
|
if (targetName === 'current' || targetName === '0') {
|
|
420
385
|
logPath = processMgr.getLogPath();
|
|
421
386
|
} else {
|
|
422
|
-
|
|
387
|
+
// 纯数字 1+ 表示归档日志的位置(最新=1)
|
|
388
|
+
const parsedIdx = parseInt(targetName);
|
|
389
|
+
if (!isNaN(parsedIdx) && parsedIdx > 0 && String(parsedIdx) === targetName) {
|
|
390
|
+
const archiveLogs = processMgr.listLogs();
|
|
391
|
+
const archive = archiveLogs.archives[parsedIdx - 1];
|
|
392
|
+
if (!archive) {
|
|
393
|
+
console.error('错误: 未找到日志 "' + targetName + '"');
|
|
394
|
+
console.log('使用 "mihomo logs" 查看可用日志列表');
|
|
395
|
+
process.exit(1);
|
|
396
|
+
}
|
|
397
|
+
logPath = archive.path;
|
|
398
|
+
} else {
|
|
399
|
+
logPath = processMgr.getLogPathByName(targetName);
|
|
400
|
+
}
|
|
423
401
|
}
|
|
424
402
|
|
|
425
403
|
if (!logPath) {
|
|
@@ -454,25 +432,32 @@ function cmdLogs(args) {
|
|
|
454
432
|
console.log('日志列表:');
|
|
455
433
|
console.log('');
|
|
456
434
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
435
|
+
let archiveCounter = 0;
|
|
436
|
+
all.forEach(log => {
|
|
437
|
+
let num;
|
|
438
|
+
if (log.isCurrent) {
|
|
439
|
+
num = ' 0';
|
|
440
|
+
} else {
|
|
441
|
+
archiveCounter++;
|
|
442
|
+
num = archiveCounter < 10 ? ' ' + archiveCounter : '' + archiveCounter;
|
|
443
|
+
}
|
|
444
|
+
const time = utils.formatDate(log.mtime);
|
|
445
|
+
const size = utils.formatBytes(log.size);
|
|
461
446
|
const name = log.isCurrent ? 'mihomo.log (当前运行中)' : log.name;
|
|
462
447
|
|
|
463
448
|
console.log(' ' + num + '. ' + name);
|
|
464
449
|
console.log(' 时间: ' + time + ' 大小: ' + size);
|
|
465
450
|
if (!log.isCurrent) {
|
|
466
|
-
console.log(' 查看: mihomo logs ' +
|
|
451
|
+
console.log(' 查看: mihomo logs ' + archiveCounter + ' 或 mihomo logs ' + archiveCounter + ' -o');
|
|
467
452
|
}
|
|
468
453
|
console.log('');
|
|
469
454
|
});
|
|
470
455
|
|
|
471
456
|
console.log('用法:');
|
|
472
457
|
console.log(' mihomo logs 0 # 查看当前日志 (最后 100 行)');
|
|
473
|
-
console.log(' mihomo logs 1 # 查看第 1
|
|
458
|
+
console.log(' mihomo logs 1 # 查看第 1 个归档日志(最新)');
|
|
474
459
|
console.log(' mihomo logs 1 -n 200 # 查看 200 行');
|
|
475
|
-
console.log(' mihomo logs 1 -o
|
|
460
|
+
console.log(' mihomo logs 1 -o # 用系统默认程序打开');
|
|
476
461
|
console.log('');
|
|
477
462
|
}
|
|
478
463
|
|
|
@@ -542,11 +527,8 @@ async function cmdKernel(args) {
|
|
|
542
527
|
|
|
543
528
|
console.log('\n可用镜像:');
|
|
544
529
|
config.AVAILABLE_MIRRORS.forEach(m => {
|
|
545
|
-
const isCurrent =
|
|
546
|
-
effectiveMirror.includes('//' + m + '/') ||
|
|
547
|
-
effectiveMirror.includes('//' + m + ':') ||
|
|
548
|
-
effectiveMirror.endsWith('//' + m)
|
|
549
|
-
);
|
|
530
|
+
const isCurrent =
|
|
531
|
+
effectiveMirror && (effectiveMirror.includes('//' + m + '/') || effectiveMirror.includes('//' + m + ':') || effectiveMirror.endsWith('//' + m));
|
|
550
532
|
console.log(' ' + m + (isCurrent ? ' (当前)' : ''));
|
|
551
533
|
});
|
|
552
534
|
|
|
@@ -568,9 +550,9 @@ async function cmdKernel(args) {
|
|
|
568
550
|
}
|
|
569
551
|
|
|
570
552
|
console.log('\n正在下载...');
|
|
571
|
-
const result = await kernel.downloadKernel(
|
|
553
|
+
const result = await kernel.downloadKernel(msg => {
|
|
572
554
|
console.log(msg);
|
|
573
|
-
}, mirrorInfo.mirror);
|
|
555
|
+
}, mirrorInfo.mirror); // 传递镜像参数(undefined = 用配置,null = 禁用)
|
|
574
556
|
console.log('已更新到 ' + result.version);
|
|
575
557
|
} catch (e) {
|
|
576
558
|
console.error('更新失败: ' + e.message);
|
|
@@ -579,7 +561,7 @@ async function cmdKernel(args) {
|
|
|
579
561
|
}
|
|
580
562
|
|
|
581
563
|
async function printSubscriptionList() {
|
|
582
|
-
const updateResult = await subscription.
|
|
564
|
+
const updateResult = await subscription.autoUpdateStaleSubscription();
|
|
583
565
|
if (updateResult.total > 0) {
|
|
584
566
|
console.log('');
|
|
585
567
|
}
|
|
@@ -594,7 +576,7 @@ async function printSubscriptionList() {
|
|
|
594
576
|
}
|
|
595
577
|
console.log('订阅列表:');
|
|
596
578
|
subs.forEach((s, i) => {
|
|
597
|
-
const time =
|
|
579
|
+
const time = utils.formatDate(s.updated_at);
|
|
598
580
|
const defaultMark = i === 0 ? ' [默认]' : '';
|
|
599
581
|
const interval = s.update_interval || subscription.DEFAULT_UPDATE_INTERVAL_HOURS;
|
|
600
582
|
console.log(' ' + (i + 1) + '. ' + s.name + defaultMark);
|
|
@@ -605,8 +587,8 @@ async function printSubscriptionList() {
|
|
|
605
587
|
}
|
|
606
588
|
if (s.download !== undefined || s.total !== undefined) {
|
|
607
589
|
const used = (s.upload || 0) + (s.download || 0);
|
|
608
|
-
const usedStr =
|
|
609
|
-
const totalStr =
|
|
590
|
+
const usedStr = utils.formatBytes(used);
|
|
591
|
+
const totalStr = utils.formatBytes(s.total);
|
|
610
592
|
let percentStr = '';
|
|
611
593
|
if (s.total && s.total > 0) {
|
|
612
594
|
const percent = Math.min((used / s.total) * 100, 100);
|
|
@@ -615,7 +597,7 @@ async function printSubscriptionList() {
|
|
|
615
597
|
console.log(' 流量: ' + usedStr + ' / ' + totalStr + percentStr);
|
|
616
598
|
}
|
|
617
599
|
if (s.expire !== undefined) {
|
|
618
|
-
console.log(' 到期: ' +
|
|
600
|
+
console.log(' 到期: ' + utils.formatTimestamp(s.expire));
|
|
619
601
|
}
|
|
620
602
|
if (s.web_page_url) {
|
|
621
603
|
console.log(' 页面: ' + s.web_page_url);
|
|
@@ -625,6 +607,7 @@ async function printSubscriptionList() {
|
|
|
625
607
|
console.log('切换默认: mihomo sub use <name>');
|
|
626
608
|
console.log('更新订阅: mihomo sub update [name]');
|
|
627
609
|
console.log('打开页面: mihomo sub web [name]');
|
|
610
|
+
console.log('新增订阅: mihomo sub add <url> [name]');
|
|
628
611
|
console.log('');
|
|
629
612
|
}
|
|
630
613
|
|
|
@@ -649,10 +632,7 @@ async function cmdSubscription(args) {
|
|
|
649
632
|
try {
|
|
650
633
|
config.addSubscription(url, name);
|
|
651
634
|
const info = await subscription.downloadSubscription(url, name);
|
|
652
|
-
|
|
653
|
-
if (info.proxyGroups && info.proxyGroups > 0) parts.push(info.proxyGroups + ' 组');
|
|
654
|
-
parts.push(info.proxies + ' 节点');
|
|
655
|
-
console.log('已添加 (' + parts.join(', ') + ')');
|
|
635
|
+
console.log('已添加 (' + subscription.formatProxySummary(info) + ')');
|
|
656
636
|
} catch (e) {
|
|
657
637
|
console.error('添加失败: ' + e.message);
|
|
658
638
|
process.exit(1);
|
|
@@ -678,10 +658,7 @@ async function cmdSubscription(args) {
|
|
|
678
658
|
results.forEach(r => {
|
|
679
659
|
if (r.success) {
|
|
680
660
|
ok++;
|
|
681
|
-
|
|
682
|
-
if (r.proxyGroups && r.proxyGroups > 0) parts.push(r.proxyGroups + ' 组');
|
|
683
|
-
parts.push(r.proxies + ' 节点');
|
|
684
|
-
console.log('✓ ' + r.name + ': 已更新 (' + parts.join(', ') + ')');
|
|
661
|
+
console.log('✓ ' + r.name + ': 已更新 (' + subscription.formatProxySummary(r) + ')');
|
|
685
662
|
} else {
|
|
686
663
|
console.log('✗ ' + r.name + ': 失败 (' + r.error.split('\n')[0] + ')');
|
|
687
664
|
}
|
|
@@ -693,15 +670,12 @@ async function cmdSubscription(args) {
|
|
|
693
670
|
}
|
|
694
671
|
|
|
695
672
|
const matches = findSubscriptionFuzzy(subs, name);
|
|
696
|
-
const target = pickSingleSubscription(matches, name
|
|
673
|
+
const target = pickSingleSubscription(matches, name);
|
|
697
674
|
|
|
698
675
|
console.log('更新订阅: ' + target.name);
|
|
699
676
|
try {
|
|
700
677
|
const info = await subscription.downloadSubscription(target.url, target.name);
|
|
701
|
-
|
|
702
|
-
if (info.proxyGroups && info.proxyGroups > 0) parts.push(info.proxyGroups + ' 组');
|
|
703
|
-
parts.push(info.proxies + ' 节点');
|
|
704
|
-
console.log('已更新 (' + parts.join(', ') + ')');
|
|
678
|
+
console.log('已更新 (' + subscription.formatProxySummary(info) + ')');
|
|
705
679
|
} catch (e) {
|
|
706
680
|
console.error('更新失败: ' + e.message);
|
|
707
681
|
process.exit(1);
|
|
@@ -725,7 +699,7 @@ async function cmdSubscription(args) {
|
|
|
725
699
|
}
|
|
726
700
|
|
|
727
701
|
const matches = findSubscriptionFuzzy(subs, name);
|
|
728
|
-
const target = pickSingleSubscription(matches, name
|
|
702
|
+
const target = pickSingleSubscription(matches, name);
|
|
729
703
|
|
|
730
704
|
// 检查是否已是当前默认订阅
|
|
731
705
|
const currentDefault = getActiveSubscription();
|
|
@@ -775,7 +749,7 @@ async function cmdSubscription(args) {
|
|
|
775
749
|
let target;
|
|
776
750
|
if (name) {
|
|
777
751
|
const matches = findSubscriptionFuzzy(subs, name);
|
|
778
|
-
target = pickSingleSubscription(matches, name
|
|
752
|
+
target = pickSingleSubscription(matches, name);
|
|
779
753
|
} else {
|
|
780
754
|
target = subs[0];
|
|
781
755
|
}
|
|
@@ -833,11 +807,11 @@ async function cmdReset(args) {
|
|
|
833
807
|
const readline = require('readline');
|
|
834
808
|
const rl = readline.createInterface({
|
|
835
809
|
input: process.stdin,
|
|
836
|
-
output: process.stdout
|
|
810
|
+
output: process.stdout,
|
|
837
811
|
});
|
|
838
812
|
|
|
839
813
|
const answer = await new Promise(resolve => {
|
|
840
|
-
rl.question('确认? (y/N) ',
|
|
814
|
+
rl.question('确认? (y/N) ', a => {
|
|
841
815
|
rl.close();
|
|
842
816
|
resolve(a);
|
|
843
817
|
});
|
|
@@ -854,7 +828,7 @@ async function cmdReset(args) {
|
|
|
854
828
|
}
|
|
855
829
|
|
|
856
830
|
function printOverwriteList() {
|
|
857
|
-
const info = overwrite.
|
|
831
|
+
const info = overwrite.listOverwriteFile();
|
|
858
832
|
console.log('状态: ' + (info.enabled ? '已启用' : '已禁用'));
|
|
859
833
|
console.log('目录: ' + info.dir);
|
|
860
834
|
console.log('');
|
|
@@ -943,14 +917,14 @@ async function cmdOverwrite(args) {
|
|
|
943
917
|
|
|
944
918
|
// 目录目标映射(精确匹配)
|
|
945
919
|
const DIRECTORY_TARGETS = {
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
920
|
+
root: { path: null, label: '根目录' },
|
|
921
|
+
subs: { path: config.DIRS.subscriptions, label: '订阅目录' },
|
|
922
|
+
logs: { path: config.DIRS.logs, label: '日志目录' },
|
|
923
|
+
data: { path: config.DIRS.data, label: 'mihomo 数据目录' },
|
|
924
|
+
runtime: { path: config.DIRS.runtime, label: '运行时目录' },
|
|
925
|
+
overwrites: { path: config.DIRS.overwrites, label: '覆写目录' },
|
|
926
|
+
settings: { path: config.PATHS.settingsFile, label: '设置文件' },
|
|
927
|
+
kernel: { path: config.DIRS.core, label: '内核目录' },
|
|
954
928
|
};
|
|
955
929
|
|
|
956
930
|
function cmdDirectory(args) {
|
|
@@ -1055,7 +1029,7 @@ async function main() {
|
|
|
1055
1029
|
cmdLogs(args);
|
|
1056
1030
|
break;
|
|
1057
1031
|
case 'ui':
|
|
1058
|
-
|
|
1032
|
+
cmdUI(args);
|
|
1059
1033
|
break;
|
|
1060
1034
|
case 'kernel':
|
|
1061
1035
|
await cmdKernel(args);
|