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