mihomo-cli 1.2.5 → 1.3.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 +47 -0
- package/README.md +1 -1
- package/index.js +236 -303
- package/package.json +18 -11
- package/src/config.js +59 -40
- package/src/kernel.js +23 -11
- package/src/overwrite.js +7 -1
- package/src/process.js +87 -24
- package/src/subscription.js +72 -15
- package/src/utils.js +99 -4
package/index.js
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// 内置模块
|
|
3
4
|
const path = require('path');
|
|
4
|
-
const { spawn } = require('child_process');
|
|
5
|
+
const { spawn, exec } = require('child_process');
|
|
6
|
+
const { promisify } = require('util');
|
|
7
|
+
const readline = require('readline');
|
|
5
8
|
|
|
9
|
+
// 第三方模块
|
|
10
|
+
// (无第三方模块依赖)
|
|
11
|
+
|
|
12
|
+
// 本地模块
|
|
6
13
|
const config = require('./src/config');
|
|
7
14
|
const kernel = require('./src/kernel');
|
|
8
15
|
const subscription = require('./src/subscription');
|
|
9
|
-
const
|
|
16
|
+
const processManager = require('./src/process');
|
|
10
17
|
const overwrite = require('./src/overwrite');
|
|
11
18
|
const utils = require('./src/utils');
|
|
12
19
|
|
|
13
|
-
const
|
|
20
|
+
const execAsync = promisify(exec);
|
|
21
|
+
const VERSION = utils.VERSION;
|
|
22
|
+
const { colors } = utils;
|
|
14
23
|
|
|
15
24
|
const UI_URLS = {
|
|
16
25
|
zash: 'https://board.zash.run.place',
|
|
@@ -49,20 +58,28 @@ process.on('unhandledRejection', reason => {
|
|
|
49
58
|
});
|
|
50
59
|
|
|
51
60
|
function printShortHelp() {
|
|
52
|
-
console.log('\
|
|
61
|
+
console.log('\n' + colors.cyan(colors.bold('mihomo-cli v' + VERSION)) + ' (mihomo help 查看完整帮助)\n');
|
|
53
62
|
console.log(
|
|
54
63
|
'常用命令:\n' +
|
|
55
|
-
'
|
|
56
|
-
'
|
|
57
|
-
'
|
|
58
|
-
'
|
|
64
|
+
' ' +
|
|
65
|
+
colors.bold('start') +
|
|
66
|
+
' [tun|mixed] 启动/切换代理\n' +
|
|
67
|
+
' ' +
|
|
68
|
+
colors.bold('sub') +
|
|
69
|
+
' [use|update] 订阅管理\n' +
|
|
70
|
+
' ' +
|
|
71
|
+
colors.bold('ui') +
|
|
72
|
+
' [zash|dash|yacd] 打开 Web UI\n' +
|
|
73
|
+
' ' +
|
|
74
|
+
colors.bold('ow') +
|
|
75
|
+
' [on|off] 覆写配置\n',
|
|
59
76
|
);
|
|
60
77
|
}
|
|
61
78
|
|
|
62
79
|
function printHelp() {
|
|
63
80
|
console.log(
|
|
64
|
-
'\
|
|
65
|
-
VERSION +
|
|
81
|
+
'\n' +
|
|
82
|
+
colors.cyan(colors.bold('mihomo-cli v' + VERSION)) +
|
|
66
83
|
'\n' +
|
|
67
84
|
'\n' +
|
|
68
85
|
'命令别名: mihomo, mmc, mh\n' +
|
|
@@ -70,47 +87,95 @@ function printHelp() {
|
|
|
70
87
|
'用法:\n' +
|
|
71
88
|
' mihomo <命令> [选项]\n' +
|
|
72
89
|
'\n' +
|
|
73
|
-
'
|
|
74
|
-
'
|
|
75
|
-
'
|
|
76
|
-
'
|
|
90
|
+
colors.cyan('控制:') +
|
|
91
|
+
'\n' +
|
|
92
|
+
' ' +
|
|
93
|
+
colors.bold('start') +
|
|
94
|
+
' [tun|mixed] 启动/切换代理 (默认 mixed)\n' +
|
|
95
|
+
' ' +
|
|
96
|
+
colors.bold('stop') +
|
|
97
|
+
' 停止代理\n' +
|
|
98
|
+
' ' +
|
|
99
|
+
colors.bold('status') +
|
|
100
|
+
' 查看状态\n' +
|
|
101
|
+
'\n' +
|
|
102
|
+
colors.cyan('界面:') +
|
|
103
|
+
'\n' +
|
|
104
|
+
' ' +
|
|
105
|
+
colors.bold('ui') +
|
|
106
|
+
' [zash|dash|yacd] 打开 Web UI (默认 zash)\n' +
|
|
107
|
+
' ' +
|
|
108
|
+
colors.bold('log') +
|
|
109
|
+
' [-o] 实时日志(-o 打开文件)\n' +
|
|
110
|
+
' ' +
|
|
111
|
+
colors.bold('logs') +
|
|
112
|
+
' [编号] [-n N] [-o] 日志列表(0=当前,1+=归档)\n' +
|
|
113
|
+
'\n' +
|
|
114
|
+
colors.cyan('订阅:') +
|
|
115
|
+
'\n' +
|
|
116
|
+
' ' +
|
|
117
|
+
colors.bold('subscription') +
|
|
118
|
+
' 列出所有订阅(别名 sub)\n' +
|
|
119
|
+
' ' +
|
|
120
|
+
colors.bold('subscription') +
|
|
121
|
+
' add <url> [name] 添加订阅\n' +
|
|
122
|
+
' ' +
|
|
123
|
+
colors.bold('subscription') +
|
|
124
|
+
' update [name] 更新订阅(无参更新所有)\n' +
|
|
125
|
+
' ' +
|
|
126
|
+
colors.bold('subscription') +
|
|
127
|
+
' use <name> 切换默认订阅\n' +
|
|
128
|
+
' ' +
|
|
129
|
+
colors.bold('subscription') +
|
|
130
|
+
' web [name] 打开订阅页面\n' +
|
|
77
131
|
'\n' +
|
|
78
|
-
'
|
|
79
|
-
' ui [zash|dash|yacd] 打开 Web UI (默认 zash)\n' +
|
|
80
|
-
' log [-o] 实时日志(-o 打开文件)\n' +
|
|
81
|
-
' logs [编号] [-n N] [-o] 日志列表(0=当前,1+=归档)\n' +
|
|
132
|
+
colors.cyan('配置:') +
|
|
82
133
|
'\n' +
|
|
83
|
-
'
|
|
84
|
-
'
|
|
85
|
-
'
|
|
86
|
-
'
|
|
87
|
-
'
|
|
88
|
-
'
|
|
134
|
+
' ' +
|
|
135
|
+
colors.bold('overwrite') +
|
|
136
|
+
' 查看覆写状态(别名 ow)\n' +
|
|
137
|
+
' ' +
|
|
138
|
+
colors.bold('overwrite') +
|
|
139
|
+
' on|off 启用/禁用覆写配置\n' +
|
|
140
|
+
' ' +
|
|
141
|
+
colors.bold('directory') +
|
|
142
|
+
' 显示数据目录位置(别名 dir)\n' +
|
|
143
|
+
' ' +
|
|
144
|
+
colors.bold('directory') +
|
|
145
|
+
' open [target] 打开目录: root|subs|logs|overwrites|...\n' +
|
|
89
146
|
'\n' +
|
|
90
|
-
'
|
|
91
|
-
' overwrite 查看覆写状态(别名 ow)\n' +
|
|
92
|
-
' overwrite on|off 启用/禁用覆写配置\n' +
|
|
93
|
-
' directory 显示数据目录位置(别名 dir)\n' +
|
|
94
|
-
' directory open [target] 打开目录: root|subs|logs|overwrites|...\n' +
|
|
147
|
+
colors.cyan('系统:') +
|
|
95
148
|
'\n' +
|
|
96
|
-
'
|
|
97
|
-
'
|
|
98
|
-
'
|
|
99
|
-
'
|
|
100
|
-
'
|
|
101
|
-
'
|
|
149
|
+
' ' +
|
|
150
|
+
colors.bold('kernel') +
|
|
151
|
+
' [镜像|--no-mirror] 更新内核\n' +
|
|
152
|
+
' ' +
|
|
153
|
+
colors.bold('update') +
|
|
154
|
+
' 更新 mihomo-cli (npm install -g)\n' +
|
|
155
|
+
' ' +
|
|
156
|
+
colors.bold('reset') +
|
|
157
|
+
' [--full] 重置用户数据 (--full 同时删除内核)\n' +
|
|
158
|
+
' ' +
|
|
159
|
+
colors.bold('help') +
|
|
160
|
+
', -h 显示帮助\n' +
|
|
161
|
+
' ' +
|
|
162
|
+
colors.bold('version') +
|
|
163
|
+
', -v 显示版本\n' +
|
|
164
|
+
'\n' +
|
|
165
|
+
colors.cyan('示例:') +
|
|
102
166
|
'\n' +
|
|
103
|
-
'示例:\n' +
|
|
104
167
|
' mihomo start # 启动/重启 Mixed 模式\n' +
|
|
105
168
|
' mihomo start tun # 切换到 TUN 透明代理模式\n' +
|
|
106
169
|
' mihomo sub add <url> # 添加订阅 (sub 是 subscription 别名)\n' +
|
|
107
170
|
' mihomo ui # 打开 Web UI\n' +
|
|
108
171
|
'\n' +
|
|
109
|
-
'
|
|
172
|
+
colors.cyan('模式说明:') +
|
|
173
|
+
'\n' +
|
|
110
174
|
' mixed HTTP + SOCKS5 混合端口 (默认)\n' +
|
|
111
175
|
' tun 透明代理,全局自动路由,需要 sudo\n' +
|
|
112
176
|
'\n' +
|
|
113
|
-
'
|
|
177
|
+
colors.cyan('数据目录:') +
|
|
178
|
+
'\n' +
|
|
114
179
|
' 环境变量 MIHOMO_CLI_DIR 可自定义位置\n' +
|
|
115
180
|
' 默认: ' +
|
|
116
181
|
config.USER_DATA_DIR +
|
|
@@ -120,153 +185,66 @@ function printHelp() {
|
|
|
120
185
|
|
|
121
186
|
function printVersion() {
|
|
122
187
|
const kv = config.getKernelVersion() || '未安装';
|
|
123
|
-
console.log('mihomo-cli v' + VERSION);
|
|
124
|
-
console.log('内核: ' + kv);
|
|
125
|
-
console.log('数据目录: ' + config.USER_DATA_DIR);
|
|
188
|
+
console.log(colors.cyan(colors.bold('mihomo-cli v' + VERSION)));
|
|
189
|
+
console.log(colors.gray('内核: ') + kv);
|
|
190
|
+
console.log(colors.gray('数据目录: ') + config.USER_DATA_DIR);
|
|
126
191
|
}
|
|
127
192
|
|
|
128
193
|
function printStatus() {
|
|
129
|
-
const status =
|
|
194
|
+
const status = processManager.getStatus();
|
|
130
195
|
const info = config.getConfigInfo();
|
|
131
|
-
const
|
|
132
|
-
const
|
|
133
|
-
const activeSub = getActiveSubscription();
|
|
196
|
+
const overwriteEnabled = overwrite.isOverwriteEnabled();
|
|
197
|
+
const overwriteFiles = overwrite.listOverwriteFile().files;
|
|
198
|
+
const activeSub = subscription.getActiveSubscription();
|
|
134
199
|
|
|
135
200
|
console.log('');
|
|
136
201
|
let modeLabel = '';
|
|
137
202
|
if (info && status.running) {
|
|
138
|
-
modeLabel = info.tun ? ' (TUN)' : ' (Mixed)';
|
|
203
|
+
modeLabel = colors.cyan(info.tun ? ' (TUN)' : ' (Mixed)');
|
|
139
204
|
}
|
|
140
|
-
|
|
141
|
-
console.log('
|
|
205
|
+
const statusText = status.running ? colors.green('运行中') : colors.yellow('已停止');
|
|
206
|
+
console.log(colors.gray('状态: ') + statusText + modeLabel);
|
|
207
|
+
console.log(colors.gray('内核: ') + (status.kernelVersion || '未安装'));
|
|
142
208
|
|
|
143
209
|
if (status.pid) {
|
|
144
|
-
console.log('PID: ' + status.pid);
|
|
210
|
+
console.log(colors.gray('PID: ') + status.pid);
|
|
145
211
|
if (status.processInfo) {
|
|
146
|
-
console.log('内存: ' + status.processInfo.memory);
|
|
212
|
+
console.log(colors.gray('内存: ') + status.processInfo.memory);
|
|
147
213
|
}
|
|
148
214
|
}
|
|
149
215
|
|
|
150
216
|
if (info) {
|
|
151
217
|
if (info.mixedPort) {
|
|
152
|
-
console.log('端口: ' + info.mixedPort);
|
|
218
|
+
console.log(colors.gray('端口: ') + info.mixedPort);
|
|
153
219
|
} else {
|
|
154
220
|
let ports = [];
|
|
155
221
|
if (info.httpPort) ports.push('HTTP:' + info.httpPort);
|
|
156
222
|
if (info.socksPort) ports.push('SOCKS:' + info.socksPort);
|
|
157
|
-
console.log('端口: ' + (ports.length > 0 ? ports.join(', ') : '未知'));
|
|
223
|
+
console.log(colors.gray('端口: ') + (ports.length > 0 ? ports.join(', ') : '未知'));
|
|
158
224
|
}
|
|
159
225
|
}
|
|
160
226
|
|
|
161
227
|
if (activeSub) {
|
|
162
|
-
let subLine = '订阅: ' + activeSub.name;
|
|
228
|
+
let subLine = colors.gray('订阅: ') + activeSub.name;
|
|
163
229
|
if (info) {
|
|
164
230
|
subLine += ' (' + subscription.formatProxySummary(info) + ')';
|
|
165
231
|
}
|
|
166
232
|
console.log(subLine);
|
|
167
233
|
} else {
|
|
168
|
-
console.log('订阅: 未配置');
|
|
234
|
+
console.log(colors.gray('订阅: ') + '未配置');
|
|
169
235
|
}
|
|
170
236
|
|
|
171
|
-
if (
|
|
172
|
-
const names =
|
|
173
|
-
console.log('覆写: 已启用 (' + names + ')');
|
|
174
|
-
} else if (
|
|
175
|
-
console.log('覆写: 已启用 (无文件)');
|
|
237
|
+
if (overwriteEnabled && overwriteFiles.length > 0) {
|
|
238
|
+
const names = overwriteFiles.map(f => f.name).join(', ');
|
|
239
|
+
console.log(colors.gray('覆写: ') + colors.green('已启用') + ' (' + names + ')');
|
|
240
|
+
} else if (overwriteEnabled) {
|
|
241
|
+
console.log(colors.gray('覆写: ') + colors.green('已启用') + ' (无文件)');
|
|
176
242
|
} else {
|
|
177
|
-
console.log('覆写: 已禁用');
|
|
243
|
+
console.log(colors.gray('覆写: ') + colors.yellow('已禁用'));
|
|
178
244
|
}
|
|
179
245
|
console.log('');
|
|
180
246
|
}
|
|
181
247
|
|
|
182
|
-
function getActiveSubscription() {
|
|
183
|
-
const subs = config.getSubscriptions();
|
|
184
|
-
if (subs.length === 0) {
|
|
185
|
-
return null;
|
|
186
|
-
}
|
|
187
|
-
return subs[0];
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function findSubscriptionFuzzy(subs, pattern) {
|
|
191
|
-
const lowerPattern = pattern.toLowerCase();
|
|
192
|
-
let exact = [];
|
|
193
|
-
let prefix = [];
|
|
194
|
-
let includes = [];
|
|
195
|
-
|
|
196
|
-
for (const s of subs) {
|
|
197
|
-
const name = s.name.toLowerCase();
|
|
198
|
-
if (name === lowerPattern) {
|
|
199
|
-
exact.push(s);
|
|
200
|
-
} else if (name.startsWith(lowerPattern)) {
|
|
201
|
-
prefix.push(s);
|
|
202
|
-
} else if (name.includes(lowerPattern)) {
|
|
203
|
-
includes.push(s);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
if (exact.length > 0) return exact;
|
|
208
|
-
if (prefix.length > 0) return prefix;
|
|
209
|
-
return includes;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function pickSingleSubscription(subs, pattern) {
|
|
213
|
-
if (subs.length === 0) {
|
|
214
|
-
console.error('错误: 未找到匹配 "' + pattern + '" 的订阅');
|
|
215
|
-
process.exit(1);
|
|
216
|
-
}
|
|
217
|
-
if (subs.length === 1) {
|
|
218
|
-
return subs[0];
|
|
219
|
-
}
|
|
220
|
-
console.error('错误: 匹配到多个订阅,请更精确指定');
|
|
221
|
-
console.log('\n匹配的订阅:');
|
|
222
|
-
subs.forEach(s => console.log(' ' + s.name));
|
|
223
|
-
process.exit(1);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
function openLogFile(logPath, label) {
|
|
228
|
-
const displayLabel = label || logPath;
|
|
229
|
-
console.log('用系统默认程序打开: ' + displayLabel);
|
|
230
|
-
const success = processMgr.openUrl(logPath);
|
|
231
|
-
if (!success) {
|
|
232
|
-
console.log('请手动打开: ' + logPath);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
function openDir(dirPath, label) {
|
|
237
|
-
const displayLabel = label || dirPath;
|
|
238
|
-
console.log('正在打开: ' + displayLabel);
|
|
239
|
-
const success = processMgr.openUrl(dirPath);
|
|
240
|
-
if (!success) {
|
|
241
|
-
console.log('请手动打开: ' + dirPath);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
function viewLogWithTail(logPath, options) {
|
|
246
|
-
const follow = options && options.follow;
|
|
247
|
-
const lines = (options && options.lines) || 100;
|
|
248
|
-
|
|
249
|
-
console.log('日志: ' + logPath);
|
|
250
|
-
if (follow) {
|
|
251
|
-
console.log('按 Ctrl+C 退出\n');
|
|
252
|
-
} else {
|
|
253
|
-
console.log('显示最后 ' + lines + ' 行\n');
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
const tailArgs = [];
|
|
257
|
-
if (follow) tailArgs.push('-f');
|
|
258
|
-
tailArgs.push('-n', lines.toString());
|
|
259
|
-
tailArgs.push(logPath);
|
|
260
|
-
|
|
261
|
-
const tail = spawn('tail', tailArgs, { stdio: 'inherit' });
|
|
262
|
-
|
|
263
|
-
tail.on('close', () => process.exit(0));
|
|
264
|
-
tail.on('error', e => {
|
|
265
|
-
console.error('无法读取日志: ' + e.message);
|
|
266
|
-
process.exit(1);
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
|
|
270
248
|
async function cmdStart(args) {
|
|
271
249
|
if (!config.hasKernel()) {
|
|
272
250
|
console.error('错误: 未找到内核,请运行 "mihomo kernel"');
|
|
@@ -275,7 +253,7 @@ async function cmdStart(args) {
|
|
|
275
253
|
|
|
276
254
|
const targetMode = args[1] === 'tun' ? 'tun' : 'mixed';
|
|
277
255
|
|
|
278
|
-
const sub = getActiveSubscription();
|
|
256
|
+
const sub = subscription.getActiveSubscription();
|
|
279
257
|
if (!sub) {
|
|
280
258
|
console.error('错误: 没有订阅,请先添加订阅');
|
|
281
259
|
process.exit(1);
|
|
@@ -283,8 +261,7 @@ async function cmdStart(args) {
|
|
|
283
261
|
|
|
284
262
|
await subscription.autoUpdateStaleSubscription();
|
|
285
263
|
|
|
286
|
-
|
|
287
|
-
const status = processMgr.getStatus();
|
|
264
|
+
const status = processManager.getStatus();
|
|
288
265
|
const hasProcess = status.running || status.allProcesses.length > 0;
|
|
289
266
|
|
|
290
267
|
if (hasProcess) {
|
|
@@ -292,56 +269,55 @@ async function cmdStart(args) {
|
|
|
292
269
|
console.log('停止 ' + count + ' 个进程...');
|
|
293
270
|
}
|
|
294
271
|
|
|
295
|
-
|
|
296
|
-
const stopResult = processMgr.stop(true);
|
|
272
|
+
const stopResult = processManager.stop(true);
|
|
297
273
|
|
|
298
274
|
if (stopResult.remaining && stopResult.remaining.length > 0) {
|
|
299
|
-
console.error('部分进程未终止: ' + stopResult.remaining.join(', '));
|
|
275
|
+
console.error(colors.red('部分进程未终止:') + ' ' + stopResult.remaining.join(', '));
|
|
300
276
|
console.error('请手动运行: sudo pkill -9 mihomo');
|
|
301
277
|
process.exit(1);
|
|
302
278
|
}
|
|
303
279
|
|
|
304
280
|
if (hasProcess) {
|
|
305
|
-
console.log('
|
|
281
|
+
console.log(colors.green('已停止') + '\n');
|
|
306
282
|
}
|
|
307
283
|
|
|
308
|
-
let
|
|
284
|
+
let configInfo;
|
|
309
285
|
try {
|
|
310
|
-
|
|
286
|
+
configInfo = subscription.prepareConfigForStart(targetMode, sub.name);
|
|
311
287
|
} catch (e) {
|
|
312
|
-
console.error('配置错误: ' + e.message);
|
|
288
|
+
console.error(colors.red('配置错误:') + ' ' + e.message);
|
|
313
289
|
process.exit(1);
|
|
314
290
|
}
|
|
315
291
|
|
|
316
292
|
const modeLabel = targetMode === 'tun' ? 'TUN' : 'Mixed';
|
|
317
|
-
console.log([modeLabel, sub.name, subscription.formatProxySummary(
|
|
293
|
+
console.log([colors.cyan(modeLabel), sub.name, subscription.formatProxySummary(configInfo)].join(' · '));
|
|
318
294
|
|
|
319
295
|
try {
|
|
320
|
-
const result = await
|
|
321
|
-
console.log('已启动 (PID ' + result.pid + ')');
|
|
296
|
+
const result = await processManager.start(targetMode);
|
|
297
|
+
console.log(colors.green('已启动') + ' (PID ' + result.pid + ')');
|
|
322
298
|
printStatus();
|
|
323
299
|
} catch (e) {
|
|
324
|
-
console.error('启动失败: ' + e.message.split('\n')[0]);
|
|
300
|
+
console.error(colors.red('启动失败:') + ' ' + e.message.split('\n')[0]);
|
|
325
301
|
process.exit(1);
|
|
326
302
|
}
|
|
327
303
|
}
|
|
328
304
|
|
|
329
305
|
async function cmdStop() {
|
|
330
|
-
const pids =
|
|
306
|
+
const pids = processManager.getAllMihomoPids();
|
|
331
307
|
if (pids.length === 0) {
|
|
332
|
-
console.log('未在运行');
|
|
308
|
+
console.log(colors.yellow('未在运行'));
|
|
333
309
|
return;
|
|
334
310
|
}
|
|
335
311
|
|
|
336
312
|
console.log('停止 ' + pids.length + ' 个进程...');
|
|
337
|
-
const result =
|
|
313
|
+
const result = processManager.stop(true);
|
|
338
314
|
|
|
339
315
|
if (result.remaining && result.remaining.length > 0) {
|
|
340
|
-
console.error('部分进程未终止: ' + result.remaining.join(', '));
|
|
316
|
+
console.error(colors.red('部分进程未终止:') + ' ' + result.remaining.join(', '));
|
|
341
317
|
console.error('请手动运行: sudo pkill -9 mihomo');
|
|
342
318
|
process.exit(1);
|
|
343
319
|
}
|
|
344
|
-
console.log('已停止');
|
|
320
|
+
console.log(colors.green('已停止'));
|
|
345
321
|
}
|
|
346
322
|
|
|
347
323
|
function cmdUI(args) {
|
|
@@ -357,21 +333,21 @@ function cmdUI(args) {
|
|
|
357
333
|
console.log('打开 Web UI: ' + uiName);
|
|
358
334
|
console.log('地址: ' + url);
|
|
359
335
|
|
|
360
|
-
const success =
|
|
336
|
+
const success = processManager.openUrl(url);
|
|
361
337
|
if (!success) {
|
|
362
338
|
console.log('请手动访问上面的地址');
|
|
363
339
|
}
|
|
364
340
|
}
|
|
365
341
|
|
|
366
342
|
function cmdLog(args) {
|
|
367
|
-
const logPath =
|
|
343
|
+
const logPath = processManager.getLogPath();
|
|
368
344
|
|
|
369
345
|
if (utils.hasFlag(args, '-o', '--open')) {
|
|
370
|
-
openLogFile(logPath);
|
|
346
|
+
processManager.openLogFile(logPath);
|
|
371
347
|
return;
|
|
372
348
|
}
|
|
373
349
|
|
|
374
|
-
viewLogWithTail(logPath, { follow: true, lines: 50 });
|
|
350
|
+
processManager.viewLogWithTail(logPath, { follow: true, lines: 50 });
|
|
375
351
|
}
|
|
376
352
|
|
|
377
353
|
function cmdLogs(args) {
|
|
@@ -383,12 +359,11 @@ function cmdLogs(args) {
|
|
|
383
359
|
let logPath;
|
|
384
360
|
|
|
385
361
|
if (targetName === 'current' || targetName === '0') {
|
|
386
|
-
logPath =
|
|
362
|
+
logPath = processManager.getLogPath();
|
|
387
363
|
} else {
|
|
388
|
-
// 纯数字 1+ 表示归档日志的位置(最新=1)
|
|
389
364
|
const parsedIdx = parseInt(targetName);
|
|
390
365
|
if (!isNaN(parsedIdx) && parsedIdx > 0 && String(parsedIdx) === targetName) {
|
|
391
|
-
const archiveLogs =
|
|
366
|
+
const archiveLogs = processManager.listLogs();
|
|
392
367
|
const archive = archiveLogs.archives[parsedIdx - 1];
|
|
393
368
|
if (!archive) {
|
|
394
369
|
console.error('错误: 未找到日志 "' + targetName + '"');
|
|
@@ -397,7 +372,7 @@ function cmdLogs(args) {
|
|
|
397
372
|
}
|
|
398
373
|
logPath = archive.path;
|
|
399
374
|
} else {
|
|
400
|
-
logPath =
|
|
375
|
+
logPath = processManager.getLogPathByName(targetName);
|
|
401
376
|
}
|
|
402
377
|
}
|
|
403
378
|
|
|
@@ -408,15 +383,15 @@ function cmdLogs(args) {
|
|
|
408
383
|
}
|
|
409
384
|
|
|
410
385
|
if (openInViewer) {
|
|
411
|
-
openLogFile(logPath);
|
|
386
|
+
processManager.openLogFile(logPath);
|
|
412
387
|
return;
|
|
413
388
|
}
|
|
414
389
|
|
|
415
|
-
viewLogWithTail(logPath, { follow: false, lines });
|
|
390
|
+
processManager.viewLogWithTail(logPath, { follow: false, lines });
|
|
416
391
|
return;
|
|
417
392
|
}
|
|
418
393
|
|
|
419
|
-
const logs =
|
|
394
|
+
const logs = processManager.listLogs();
|
|
420
395
|
const all = [];
|
|
421
396
|
|
|
422
397
|
if (logs.current) {
|
|
@@ -462,55 +437,8 @@ function cmdLogs(args) {
|
|
|
462
437
|
console.log('');
|
|
463
438
|
}
|
|
464
439
|
|
|
465
|
-
// 解析镜像参数
|
|
466
|
-
function parseMirrorArg(args) {
|
|
467
|
-
// 返回: { mirror: 镜像URL|null, isOverride: boolean }
|
|
468
|
-
// mirror = null 表示禁用镜像
|
|
469
|
-
// mirror = undefined 表示使用默认/配置
|
|
470
|
-
|
|
471
|
-
if (!args || args.length < 2) {
|
|
472
|
-
return { mirror: undefined, isOverride: false };
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
// 检查 --no-mirror
|
|
476
|
-
if (args.includes('--no-mirror') || args.includes('--direct')) {
|
|
477
|
-
return { mirror: null, isOverride: true };
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
// 检查 --mirror <值>
|
|
481
|
-
const mirrorIdx = args.indexOf('--mirror');
|
|
482
|
-
if (mirrorIdx >= 0 && mirrorIdx + 1 < args.length) {
|
|
483
|
-
let mirrorVal = args[mirrorIdx + 1];
|
|
484
|
-
return { mirror: normalizeMirrorUrl(mirrorVal), isOverride: true };
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
// 第一个非 flag 参数作为镜像
|
|
488
|
-
for (let i = 1; i < args.length; i++) {
|
|
489
|
-
const arg = args[i];
|
|
490
|
-
if (!arg.startsWith('-')) {
|
|
491
|
-
return { mirror: normalizeMirrorUrl(arg), isOverride: true };
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
return { mirror: undefined, isOverride: false };
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
function normalizeMirrorUrl(val) {
|
|
499
|
-
if (!val) return null;
|
|
500
|
-
if (val === 'direct' || val === 'no' || val === 'none') return null;
|
|
501
|
-
|
|
502
|
-
let url = val;
|
|
503
|
-
if (!url.startsWith('http')) {
|
|
504
|
-
url = 'https://' + url;
|
|
505
|
-
}
|
|
506
|
-
if (!url.endsWith('/')) {
|
|
507
|
-
url += '/';
|
|
508
|
-
}
|
|
509
|
-
return url;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
440
|
async function cmdKernel(args) {
|
|
513
|
-
const mirrorInfo = parseMirrorArg(args);
|
|
441
|
+
const mirrorInfo = utils.parseMirrorArg(args);
|
|
514
442
|
const effectiveMirror = mirrorInfo.isOverride ? mirrorInfo.mirror : config.getGitHubMirror();
|
|
515
443
|
const isDefault = !mirrorInfo.isOverride && effectiveMirror === config.DEFAULT_GITHUB_MIRROR;
|
|
516
444
|
|
|
@@ -553,7 +481,7 @@ async function cmdKernel(args) {
|
|
|
553
481
|
console.log('\n正在下载...');
|
|
554
482
|
const result = await kernel.downloadKernel(msg => {
|
|
555
483
|
console.log(msg);
|
|
556
|
-
}, mirrorInfo.mirror);
|
|
484
|
+
}, mirrorInfo.mirror);
|
|
557
485
|
console.log('已更新到 ' + result.version);
|
|
558
486
|
} catch (e) {
|
|
559
487
|
console.error('更新失败: ' + e.message);
|
|
@@ -575,16 +503,16 @@ async function printSubscriptionList() {
|
|
|
575
503
|
console.log('');
|
|
576
504
|
return;
|
|
577
505
|
}
|
|
578
|
-
console.log('订阅列表:');
|
|
506
|
+
console.log(colors.cyan('订阅列表:'));
|
|
579
507
|
subs.forEach((s, i) => {
|
|
580
508
|
const time = utils.formatDate(s.updated_at);
|
|
581
|
-
const defaultMark = i === 0 ? ' [默认]' : '';
|
|
509
|
+
const defaultMark = i === 0 ? colors.green(' [默认]') : '';
|
|
582
510
|
const interval = s.update_interval || subscription.DEFAULT_UPDATE_INTERVAL_HOURS;
|
|
583
511
|
console.log(' ' + (i + 1) + '. ' + s.name + defaultMark);
|
|
584
|
-
console.log(' 更新: ' + time + ' (间隔: ' + interval + 'h)');
|
|
512
|
+
console.log(' ' + colors.gray('更新:') + ' ' + time + ' (间隔: ' + interval + 'h)');
|
|
585
513
|
|
|
586
514
|
if (s.username) {
|
|
587
|
-
console.log(' 用户: ' + s.username);
|
|
515
|
+
console.log(' ' + colors.gray('用户:') + ' ' + s.username);
|
|
588
516
|
}
|
|
589
517
|
if (s.download !== undefined || s.total !== undefined) {
|
|
590
518
|
const used = (s.upload || 0) + (s.download || 0);
|
|
@@ -595,13 +523,13 @@ async function printSubscriptionList() {
|
|
|
595
523
|
const percent = Math.min((used / s.total) * 100, 100);
|
|
596
524
|
percentStr = ' (' + percent.toFixed(1) + '%)';
|
|
597
525
|
}
|
|
598
|
-
console.log(' 流量: ' + usedStr + ' / ' + totalStr + percentStr);
|
|
526
|
+
console.log(' ' + colors.gray('流量:') + ' ' + usedStr + ' / ' + totalStr + percentStr);
|
|
599
527
|
}
|
|
600
528
|
if (s.expire !== undefined) {
|
|
601
|
-
console.log(' 到期: ' + utils.formatTimestamp(s.expire));
|
|
529
|
+
console.log(' ' + colors.gray('到期:') + ' ' + utils.formatTimestamp(s.expire));
|
|
602
530
|
}
|
|
603
531
|
if (s.web_page_url) {
|
|
604
|
-
console.log(' 页面: ' + s.web_page_url);
|
|
532
|
+
console.log(' ' + colors.gray('页面:') + ' ' + s.web_page_url);
|
|
605
533
|
}
|
|
606
534
|
});
|
|
607
535
|
console.log('');
|
|
@@ -659,9 +587,9 @@ async function cmdSubscription(args) {
|
|
|
659
587
|
results.forEach(r => {
|
|
660
588
|
if (r.success) {
|
|
661
589
|
ok++;
|
|
662
|
-
console.log('✓ ' + r.name + ': 已更新 (' + subscription.formatProxySummary(r) + ')');
|
|
590
|
+
console.log(colors.green('✓') + ' ' + r.name + ': ' + colors.green('已更新') + ' (' + subscription.formatProxySummary(r) + ')');
|
|
663
591
|
} else {
|
|
664
|
-
console.log('✗ ' + r.name + ': 失败 (' + r.error.split('\n')[0] + ')');
|
|
592
|
+
console.log(colors.red('✗') + ' ' + r.name + ': ' + colors.red('失败') + ' (' + r.error.split('\n')[0] + ')');
|
|
665
593
|
}
|
|
666
594
|
});
|
|
667
595
|
if (ok === 0) process.exit(1);
|
|
@@ -670,8 +598,8 @@ async function cmdSubscription(args) {
|
|
|
670
598
|
return;
|
|
671
599
|
}
|
|
672
600
|
|
|
673
|
-
const matches = findSubscriptionFuzzy(subs, name);
|
|
674
|
-
const target = pickSingleSubscription(matches, name);
|
|
601
|
+
const matches = subscription.findSubscriptionFuzzy(subs, name);
|
|
602
|
+
const target = subscription.pickSingleSubscription(matches, name);
|
|
675
603
|
|
|
676
604
|
console.log('更新订阅: ' + target.name);
|
|
677
605
|
try {
|
|
@@ -699,11 +627,10 @@ async function cmdSubscription(args) {
|
|
|
699
627
|
process.exit(1);
|
|
700
628
|
}
|
|
701
629
|
|
|
702
|
-
const matches = findSubscriptionFuzzy(subs, name);
|
|
703
|
-
const target = pickSingleSubscription(matches, name);
|
|
630
|
+
const matches = subscription.findSubscriptionFuzzy(subs, name);
|
|
631
|
+
const target = subscription.pickSingleSubscription(matches, name);
|
|
704
632
|
|
|
705
|
-
|
|
706
|
-
const currentDefault = getActiveSubscription();
|
|
633
|
+
const currentDefault = subscription.getActiveSubscription();
|
|
707
634
|
const isAlreadyDefault = currentDefault && currentDefault.name === target.name;
|
|
708
635
|
|
|
709
636
|
if (isAlreadyDefault) {
|
|
@@ -713,10 +640,9 @@ async function cmdSubscription(args) {
|
|
|
713
640
|
return;
|
|
714
641
|
}
|
|
715
642
|
|
|
716
|
-
|
|
717
|
-
const
|
|
718
|
-
const
|
|
719
|
-
const currentMode = cfgInfo && cfgInfo.tun ? 'tun' : 'mixed';
|
|
643
|
+
const status = processManager.getStatus();
|
|
644
|
+
const configInfo = config.getConfigInfo();
|
|
645
|
+
const currentMode = configInfo && configInfo.tun ? 'tun' : 'mixed';
|
|
720
646
|
|
|
721
647
|
const success = config.setDefaultSubscription(target.name);
|
|
722
648
|
if (success) {
|
|
@@ -726,7 +652,6 @@ async function cmdSubscription(args) {
|
|
|
726
652
|
process.exit(1);
|
|
727
653
|
}
|
|
728
654
|
|
|
729
|
-
// 如果正在运行,自动重启
|
|
730
655
|
if (status.running) {
|
|
731
656
|
console.log('');
|
|
732
657
|
await cmdStart(['start', currentMode]);
|
|
@@ -749,8 +674,8 @@ async function cmdSubscription(args) {
|
|
|
749
674
|
|
|
750
675
|
let target;
|
|
751
676
|
if (name) {
|
|
752
|
-
const matches = findSubscriptionFuzzy(subs, name);
|
|
753
|
-
target = pickSingleSubscription(matches, name);
|
|
677
|
+
const matches = subscription.findSubscriptionFuzzy(subs, name);
|
|
678
|
+
target = subscription.pickSingleSubscription(matches, name);
|
|
754
679
|
} else {
|
|
755
680
|
target = subs[0];
|
|
756
681
|
}
|
|
@@ -759,8 +684,7 @@ async function cmdSubscription(args) {
|
|
|
759
684
|
if (!webPageUrl) {
|
|
760
685
|
console.log('订阅信息中缺少页面地址,正在更新订阅...');
|
|
761
686
|
try {
|
|
762
|
-
|
|
763
|
-
// 重新读取缓存获取 web_page_url
|
|
687
|
+
await subscription.downloadSubscription(target.url, target.name);
|
|
764
688
|
const cache = config.readSubscriptionsCache();
|
|
765
689
|
if (cache[target.name] && cache[target.name].web_page_url) {
|
|
766
690
|
webPageUrl = cache[target.name].web_page_url;
|
|
@@ -775,7 +699,7 @@ async function cmdSubscription(args) {
|
|
|
775
699
|
}
|
|
776
700
|
|
|
777
701
|
console.log('打开订阅页面: ' + webPageUrl);
|
|
778
|
-
const opened =
|
|
702
|
+
const opened = processManager.openUrl(webPageUrl);
|
|
779
703
|
if (!opened) {
|
|
780
704
|
console.log('请手动访问上面的地址');
|
|
781
705
|
}
|
|
@@ -787,38 +711,57 @@ async function cmdSubscription(args) {
|
|
|
787
711
|
process.exit(1);
|
|
788
712
|
}
|
|
789
713
|
|
|
790
|
-
function cmdUpdate() {
|
|
791
|
-
console.log('
|
|
714
|
+
async function cmdUpdate() {
|
|
715
|
+
console.log('当前版本: ' + colors.cyan(VERSION));
|
|
716
|
+
console.log('');
|
|
717
|
+
console.log('正在更新 mihomo-cli...');
|
|
792
718
|
console.log('');
|
|
793
719
|
|
|
794
|
-
|
|
720
|
+
await new Promise(resolve => {
|
|
721
|
+
const npm = spawn('npm', ['install', '-g', 'mihomo-cli'], { stdio: 'inherit' });
|
|
795
722
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
}
|
|
803
|
-
});
|
|
723
|
+
npm.on('close', code => {
|
|
724
|
+
if (code === 0) {
|
|
725
|
+
resolve();
|
|
726
|
+
} else {
|
|
727
|
+
process.exit(code);
|
|
728
|
+
}
|
|
729
|
+
});
|
|
804
730
|
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
731
|
+
npm.on('error', e => {
|
|
732
|
+
console.error('执行失败: ' + e.message);
|
|
733
|
+
process.exit(1);
|
|
734
|
+
});
|
|
808
735
|
});
|
|
736
|
+
|
|
737
|
+
try {
|
|
738
|
+
const { stdout } = await execAsync('npm list -g mihomo-cli --json --depth=0');
|
|
739
|
+
const result = JSON.parse(stdout);
|
|
740
|
+
const newVersion = result.dependencies?.['mihomo-cli']?.version;
|
|
741
|
+
|
|
742
|
+
console.log('');
|
|
743
|
+
if (newVersion) {
|
|
744
|
+
console.log('更新完成,最新版本: ' + colors.green(newVersion));
|
|
745
|
+
} else {
|
|
746
|
+
console.log('更新完成');
|
|
747
|
+
}
|
|
748
|
+
} catch {
|
|
749
|
+
console.log('');
|
|
750
|
+
console.log('更新完成');
|
|
751
|
+
}
|
|
809
752
|
}
|
|
810
753
|
|
|
811
754
|
async function cmdReset(args) {
|
|
812
755
|
const fullReset = args && (args.includes('--full') || args.includes('-f'));
|
|
813
756
|
const skipConfirm = args && (args.includes('--yes') || args.includes('-y'));
|
|
814
757
|
|
|
815
|
-
const pids =
|
|
758
|
+
const pids = processManager.getAllMihomoPids();
|
|
816
759
|
if (pids.length > 0) {
|
|
817
760
|
console.log('停止 ' + pids.length + ' 个进程...');
|
|
818
|
-
|
|
819
|
-
for (let i = 0; i <
|
|
820
|
-
if (
|
|
821
|
-
await new Promise(r => setTimeout(r,
|
|
761
|
+
processManager.cleanupAll(true);
|
|
762
|
+
for (let i = 0; i < processManager.PROCESS_WAIT_ATTEMPTS; i++) {
|
|
763
|
+
if (processManager.getAllMihomoPids().length === 0) break;
|
|
764
|
+
await new Promise(r => setTimeout(r, processManager.PROCESS_WAIT_INTERVAL));
|
|
822
765
|
}
|
|
823
766
|
}
|
|
824
767
|
|
|
@@ -826,7 +769,6 @@ async function cmdReset(args) {
|
|
|
826
769
|
console.log(mode);
|
|
827
770
|
|
|
828
771
|
if (!skipConfirm) {
|
|
829
|
-
const readline = require('readline');
|
|
830
772
|
const rl = readline.createInterface({
|
|
831
773
|
input: process.stdin,
|
|
832
774
|
output: process.stdout,
|
|
@@ -851,8 +793,9 @@ async function cmdReset(args) {
|
|
|
851
793
|
|
|
852
794
|
function printOverwriteList() {
|
|
853
795
|
const info = overwrite.listOverwriteFile();
|
|
854
|
-
|
|
855
|
-
console.log('
|
|
796
|
+
const statusText = info.enabled ? colors.green('已启用') : colors.yellow('已禁用');
|
|
797
|
+
console.log(colors.gray('状态:') + ' ' + statusText);
|
|
798
|
+
console.log(colors.gray('目录:') + ' ' + info.dir);
|
|
856
799
|
console.log('');
|
|
857
800
|
if (info.files.length === 0) {
|
|
858
801
|
console.log('暂无覆写文件');
|
|
@@ -860,13 +803,13 @@ function printOverwriteList() {
|
|
|
860
803
|
console.log('用法示例: 创建文件 ' + path.join(info.dir, '01-custom.yaml'));
|
|
861
804
|
console.log('');
|
|
862
805
|
} else {
|
|
863
|
-
console.log('覆写文件 (' + info.files.length + ' 个,按顺序加载):');
|
|
806
|
+
console.log(colors.cyan('覆写文件') + ' (' + info.files.length + ' 个,按顺序加载):');
|
|
864
807
|
console.log('');
|
|
865
808
|
info.files.forEach((f, i) => {
|
|
866
809
|
const num = i < 10 ? ' ' + i : '' + i;
|
|
867
810
|
console.log(' ' + num + '. ' + f.name);
|
|
868
811
|
if (f.keys.length > 0) {
|
|
869
|
-
console.log(' 字段: ' + f.keys.join(', '));
|
|
812
|
+
console.log(' ' + colors.gray('字段:') + ' ' + f.keys.join(', '));
|
|
870
813
|
}
|
|
871
814
|
});
|
|
872
815
|
console.log('');
|
|
@@ -879,13 +822,11 @@ function printOverwriteList() {
|
|
|
879
822
|
async function cmdOverwrite(args) {
|
|
880
823
|
const action = args && args[1];
|
|
881
824
|
|
|
882
|
-
|
|
883
|
-
const
|
|
884
|
-
const
|
|
885
|
-
const currentMode = cfgInfo && cfgInfo.tun ? 'tun' : 'mixed';
|
|
825
|
+
const status = processManager.getStatus();
|
|
826
|
+
const configInfo = config.getConfigInfo();
|
|
827
|
+
const currentMode = configInfo && configInfo.tun ? 'tun' : 'mixed';
|
|
886
828
|
|
|
887
829
|
if (action === 'on' || action === 'enable') {
|
|
888
|
-
// 如果已经启用,提示后直接返回
|
|
889
830
|
if (overwrite.isOverwriteEnabled()) {
|
|
890
831
|
console.log('覆写配置已是启用状态');
|
|
891
832
|
console.log('');
|
|
@@ -896,7 +837,6 @@ async function cmdOverwrite(args) {
|
|
|
896
837
|
overwrite.setOverwriteEnabled(true);
|
|
897
838
|
console.log('已启用覆写配置');
|
|
898
839
|
|
|
899
|
-
// 如果正在运行,自动重启
|
|
900
840
|
if (status.running) {
|
|
901
841
|
console.log('');
|
|
902
842
|
await cmdStart(['start', currentMode]);
|
|
@@ -909,7 +849,6 @@ async function cmdOverwrite(args) {
|
|
|
909
849
|
}
|
|
910
850
|
|
|
911
851
|
if (action === 'off' || action === 'disable') {
|
|
912
|
-
// 如果已经禁用,提示后直接返回
|
|
913
852
|
if (!overwrite.isOverwriteEnabled()) {
|
|
914
853
|
console.log('覆写配置已是禁用状态');
|
|
915
854
|
console.log('');
|
|
@@ -920,7 +859,6 @@ async function cmdOverwrite(args) {
|
|
|
920
859
|
overwrite.setOverwriteEnabled(false);
|
|
921
860
|
console.log('已禁用覆写配置');
|
|
922
861
|
|
|
923
|
-
// 如果正在运行,自动重启
|
|
924
862
|
if (status.running) {
|
|
925
863
|
console.log('');
|
|
926
864
|
await cmdStart(['start', currentMode]);
|
|
@@ -932,23 +870,10 @@ async function cmdOverwrite(args) {
|
|
|
932
870
|
return;
|
|
933
871
|
}
|
|
934
872
|
|
|
935
|
-
// 无参数、list、ls 都显示文件列表
|
|
936
873
|
console.log('');
|
|
937
874
|
printOverwriteList();
|
|
938
875
|
}
|
|
939
876
|
|
|
940
|
-
// 目录目标映射(精确匹配)
|
|
941
|
-
const DIRECTORY_TARGETS = {
|
|
942
|
-
root: { path: null, label: '根目录' },
|
|
943
|
-
subs: { path: config.DIRS.subscriptions, label: '订阅目录' },
|
|
944
|
-
logs: { path: config.DIRS.logs, label: '日志目录' },
|
|
945
|
-
data: { path: config.DIRS.data, label: 'mihomo 数据目录' },
|
|
946
|
-
runtime: { path: config.DIRS.runtime, label: '运行时目录' },
|
|
947
|
-
overwrites: { path: config.DIRS.overwrites, label: '覆写目录' },
|
|
948
|
-
settings: { path: config.PATHS.settingsFile, label: '设置文件' },
|
|
949
|
-
kernel: { path: config.DIRS.core, label: '内核目录' },
|
|
950
|
-
};
|
|
951
|
-
|
|
952
877
|
function cmdDirectory(args) {
|
|
953
878
|
const action = args && args[1];
|
|
954
879
|
|
|
@@ -956,14 +881,23 @@ function cmdDirectory(args) {
|
|
|
956
881
|
const target = args[2];
|
|
957
882
|
|
|
958
883
|
if (!target || target === 'root') {
|
|
959
|
-
|
|
884
|
+
const displayLabel = '根目录';
|
|
885
|
+
console.log('正在打开: ' + displayLabel);
|
|
886
|
+
const success = processManager.openUrl(config.USER_DATA_DIR);
|
|
887
|
+
if (!success) {
|
|
888
|
+
console.log('请手动打开: ' + config.USER_DATA_DIR);
|
|
889
|
+
}
|
|
960
890
|
return;
|
|
961
891
|
}
|
|
962
892
|
|
|
963
|
-
const targetInfo = DIRECTORY_TARGETS[target.toLowerCase()];
|
|
893
|
+
const targetInfo = config.DIRECTORY_TARGETS[target.toLowerCase()];
|
|
964
894
|
if (targetInfo) {
|
|
965
|
-
const
|
|
966
|
-
|
|
895
|
+
const targetPath = targetInfo.path || config.USER_DATA_DIR;
|
|
896
|
+
console.log('正在打开: ' + targetInfo.label);
|
|
897
|
+
const success = processManager.openUrl(targetPath);
|
|
898
|
+
if (!success) {
|
|
899
|
+
console.log('请手动打开: ' + targetPath);
|
|
900
|
+
}
|
|
967
901
|
return;
|
|
968
902
|
}
|
|
969
903
|
|
|
@@ -982,7 +916,6 @@ function cmdDirectory(args) {
|
|
|
982
916
|
process.exit(1);
|
|
983
917
|
}
|
|
984
918
|
|
|
985
|
-
// 无参数或未知参数:显示目录列表
|
|
986
919
|
console.log('');
|
|
987
920
|
console.log('数据目录位置:');
|
|
988
921
|
console.log(' 根目录: ' + config.USER_DATA_DIR);
|
|
@@ -1059,7 +992,7 @@ async function main() {
|
|
|
1059
992
|
case 'upd':
|
|
1060
993
|
case 'update':
|
|
1061
994
|
case 'upgrade':
|
|
1062
|
-
cmdUpdate();
|
|
995
|
+
await cmdUpdate();
|
|
1063
996
|
break;
|
|
1064
997
|
case 'sub':
|
|
1065
998
|
case 'subscription':
|