mihomo-cli 1.0.0-alpha.1
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/LICENSE +21 -0
- package/README.md +244 -0
- package/index.js +480 -0
- package/package.json +37 -0
- package/src/config.js +328 -0
- package/src/kernel.js +297 -0
- package/src/process.js +532 -0
- package/src/subscription.js +89 -0
package/index.js
ADDED
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { spawn } = require('child_process');
|
|
4
|
+
|
|
5
|
+
const config = require('./src/config');
|
|
6
|
+
const kernel = require('./src/kernel');
|
|
7
|
+
const subscription = require('./src/subscription');
|
|
8
|
+
const processMgr = require('./src/process');
|
|
9
|
+
|
|
10
|
+
const VERSION = '1.0.0-alpha.1';
|
|
11
|
+
|
|
12
|
+
const UI_URLS = {
|
|
13
|
+
zash: 'https://board.zash.run.place',
|
|
14
|
+
dash: 'https://metacubex.github.io/metacubexd',
|
|
15
|
+
yacd: 'https://yacd.metacubex.one',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
let exiting = false;
|
|
19
|
+
|
|
20
|
+
process.on('SIGINT', () => {
|
|
21
|
+
if (exiting) {
|
|
22
|
+
console.log('\n 强制退出');
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
exiting = true;
|
|
26
|
+
console.log('\n 正在退出...');
|
|
27
|
+
process.exit(0);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
process.on('SIGTERM', () => {
|
|
31
|
+
process.exit(0);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
process.on('uncaughtException', (e) => {
|
|
35
|
+
console.error('\n 未捕获的异常: ' + e.message);
|
|
36
|
+
if (e.stack) {
|
|
37
|
+
console.error(' ' + e.stack.split('\n').slice(1).join('\n '));
|
|
38
|
+
}
|
|
39
|
+
process.exit(1);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
process.on('unhandledRejection', (reason) => {
|
|
43
|
+
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
44
|
+
console.error('\n 未处理的 Promise 拒绝: ' + msg);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
function printHelp() {
|
|
49
|
+
console.log('\nmihomo-cli v' + VERSION + '\n' +
|
|
50
|
+
'\n' +
|
|
51
|
+
'用法:\n' +
|
|
52
|
+
' mihomo-cli <命令> [选项]\n' +
|
|
53
|
+
'\n' +
|
|
54
|
+
'命令:\n' +
|
|
55
|
+
' start [tun|mixed] 启动代理 (默认 mixed)\n' +
|
|
56
|
+
' stop 停止代理\n' +
|
|
57
|
+
' restart [tun|mixed] 重启代理\n' +
|
|
58
|
+
' status 查看状态\n' +
|
|
59
|
+
' log 实时日志\n' +
|
|
60
|
+
' ui [zash|dash|yacd] 打开 Web UI (默认 zash)\n' +
|
|
61
|
+
' clean 清理残留进程\n' +
|
|
62
|
+
' kernel 更新内核\n' +
|
|
63
|
+
' sub add <url> [name] 添加订阅\n' +
|
|
64
|
+
' sub update [name] 更新订阅\n' +
|
|
65
|
+
' sub list 列出订阅\n' +
|
|
66
|
+
' reset [--full] 重置用户数据 (--full 同时删除内核)\n' +
|
|
67
|
+
' dirs 显示数据目录位置\n' +
|
|
68
|
+
' help, -h 显示帮助\n' +
|
|
69
|
+
' version, -v 显示版本\n' +
|
|
70
|
+
'\n' +
|
|
71
|
+
'示例:\n' +
|
|
72
|
+
' mihomo-cli start # 启动 Mixed 模式\n' +
|
|
73
|
+
' mihomo-cli start tun # 启动 TUN 模式 (透明代理)\n' +
|
|
74
|
+
' mihomo-cli ui # 打开默认 UI (zash)\n' +
|
|
75
|
+
' mihomo-cli ui dash # 打开 metacubexd\n' +
|
|
76
|
+
' mihomo-cli ui yacd # 打开 YACD\n' +
|
|
77
|
+
'\n' +
|
|
78
|
+
'模式说明:\n' +
|
|
79
|
+
' mixed HTTP + SOCKS5 混合端口 (默认)\n' +
|
|
80
|
+
' tun 透明代理,全局自动路由,需要 sudo\n' +
|
|
81
|
+
'\n' +
|
|
82
|
+
'数据目录:\n' +
|
|
83
|
+
' 环境变量 MIHOMO_CLI_DIR 可自定义位置\n' +
|
|
84
|
+
' 默认: ' + config.USER_DATA_DIR + '\n');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function printVersion() {
|
|
88
|
+
const kv = config.getKernelVersion() || '未安装';
|
|
89
|
+
console.log('mihomo-cli v' + VERSION);
|
|
90
|
+
console.log('内核: ' + kv);
|
|
91
|
+
console.log('数据目录: ' + config.USER_DATA_DIR);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function printStatus() {
|
|
95
|
+
const status = processMgr.getStatus();
|
|
96
|
+
const info = subscription.getConfigInfo();
|
|
97
|
+
|
|
98
|
+
console.log('');
|
|
99
|
+
console.log(' 状态: ' + (status.running ? '运行中' : '已停止'));
|
|
100
|
+
if (status.pid) {
|
|
101
|
+
console.log(' PID: ' + status.pid);
|
|
102
|
+
if (status.processInfo) {
|
|
103
|
+
console.log(' 内存: ' + status.processInfo.memory);
|
|
104
|
+
if (status.processInfo.cpu) {
|
|
105
|
+
console.log(' CPU: ' + status.processInfo.cpu);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (info) {
|
|
110
|
+
console.log(' 节点: ' + info.proxies);
|
|
111
|
+
console.log(' 端口: ' + info.port);
|
|
112
|
+
console.log(' TUN: ' + (info.tun ? '启用' : '未启用'));
|
|
113
|
+
}
|
|
114
|
+
console.log(' 内核: ' + (status.kernelVersion || '未安装'));
|
|
115
|
+
console.log('');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function getActiveSubscription() {
|
|
119
|
+
const subs = config.getSubscriptions();
|
|
120
|
+
if (subs.length === 0) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
return subs[0];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function cmdStart(args) {
|
|
127
|
+
if (!config.hasKernel()) {
|
|
128
|
+
console.error(' 错误: 未找到内核,请运行 \'mihomo-cli kernel\'');
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const targetMode = args[1] === 'tun' ? 'tun' : 'mixed';
|
|
133
|
+
|
|
134
|
+
const sub = getActiveSubscription();
|
|
135
|
+
if (!sub) {
|
|
136
|
+
console.error(' 错误: 没有订阅,请先添加订阅');
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const pids = processMgr.getAllMihomoPids();
|
|
141
|
+
if (pids.length > 0) {
|
|
142
|
+
console.log(' 检测到 ' + pids.length + ' 个运行中的进程,正在停止...');
|
|
143
|
+
processMgr.stop(targetMode === 'tun');
|
|
144
|
+
for (let i = 0; i < 50; i++) {
|
|
145
|
+
if (processMgr.getAllMihomoPids().length === 0) break;
|
|
146
|
+
await new Promise(r => setTimeout(r, 100));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
let cfgInfo;
|
|
151
|
+
try {
|
|
152
|
+
cfgInfo = subscription.prepareConfigForStart(targetMode, sub.name);
|
|
153
|
+
} catch (e) {
|
|
154
|
+
console.error(' 配置错误: ' + e.message);
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const modeLabel = targetMode === 'tun' ? 'TUN' : 'Mixed';
|
|
159
|
+
const groupsLabel = cfgInfo.proxyGroups ? cfgInfo.proxyGroups + ' 组' : '';
|
|
160
|
+
const countLabel = groupsLabel ? (groupsLabel + ' ' + cfgInfo.proxies + ' 节点') : (cfgInfo.proxies + ' 节点');
|
|
161
|
+
console.log(' ' + [modeLabel, sub.name, countLabel].join(' · '));
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
const result = await processMgr.start(targetMode);
|
|
165
|
+
console.log(' 已启动 (PID ' + result.pid + ')');
|
|
166
|
+
} catch (e) {
|
|
167
|
+
console.error(' 启动失败: ' + e.message.split('\n')[0]);
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function doStop() {
|
|
173
|
+
const pids = processMgr.getAllMihomoPids();
|
|
174
|
+
if (pids.length === 0) {
|
|
175
|
+
console.log(' 未在运行');
|
|
176
|
+
return { hasRunning: false, success: true };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
console.log(' 停止 ' + pids.length + ' 个进程...');
|
|
180
|
+
const result = processMgr.stop(true);
|
|
181
|
+
|
|
182
|
+
if (result.remaining && result.remaining.length > 0) {
|
|
183
|
+
console.warn(' 部分进程未终止: ' + result.remaining.join(', '));
|
|
184
|
+
console.warn(' 手动清理: sudo pkill -9 mihomo');
|
|
185
|
+
return { hasRunning: true, success: false, remaining: result.remaining };
|
|
186
|
+
}
|
|
187
|
+
console.log(' 已停止');
|
|
188
|
+
return { hasRunning: true, success: true };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async function cmdStop() {
|
|
192
|
+
const result = doStop();
|
|
193
|
+
if (!result.success) {
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function cmdRestart(args) {
|
|
199
|
+
const stopResult = doStop();
|
|
200
|
+
console.log('');
|
|
201
|
+
await cmdStart(args);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function cmdUi(args) {
|
|
205
|
+
const uiName = args[1] || 'zash';
|
|
206
|
+
const url = UI_URLS[uiName];
|
|
207
|
+
|
|
208
|
+
if (!url) {
|
|
209
|
+
console.error(' 错误: 未知的 UI "' + uiName + '"');
|
|
210
|
+
console.error(' 可用 UI: zash (默认), dash, yacd');
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
console.log(' 打开 Web UI: ' + uiName);
|
|
215
|
+
console.log(' 地址: ' + url);
|
|
216
|
+
|
|
217
|
+
const success = processMgr.openUrl(url);
|
|
218
|
+
if (!success) {
|
|
219
|
+
console.log(' 请手动访问上面的地址');
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function cmdClean() {
|
|
224
|
+
console.log(' 清理残留进程...');
|
|
225
|
+
const result = processMgr.cleanupAll();
|
|
226
|
+
|
|
227
|
+
if (result.killed > 0) {
|
|
228
|
+
console.log(' 已清理 ' + result.killed + ' 个进程');
|
|
229
|
+
}
|
|
230
|
+
if (result.remaining && result.remaining.length > 0) {
|
|
231
|
+
console.warn(' 仍有 ' + result.remaining.length + ' 个进程需要手动清理');
|
|
232
|
+
console.warn(' 手动命令: sudo pkill -9 mihomo');
|
|
233
|
+
process.exit(1);
|
|
234
|
+
}
|
|
235
|
+
if (result.killed === 0 && (!result.remaining || result.remaining.length === 0)) {
|
|
236
|
+
console.log(' 没有发现残留进程');
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function cmdLog() {
|
|
241
|
+
const logPath = processMgr.getLogPath();
|
|
242
|
+
console.log(' 日志: ' + logPath);
|
|
243
|
+
console.log(' 按 Ctrl+C 退出\n');
|
|
244
|
+
|
|
245
|
+
const tail = spawn('tail', ['-f', '-n', '50', logPath], {
|
|
246
|
+
stdio: 'inherit',
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
tail.on('close', () => process.exit(0));
|
|
250
|
+
tail.on('error', (e) => {
|
|
251
|
+
console.error(' 无法启动日志查看: ' + e.message);
|
|
252
|
+
process.exit(1);
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async function cmdKernel() {
|
|
257
|
+
console.log(' 检查内核更新...');
|
|
258
|
+
|
|
259
|
+
try {
|
|
260
|
+
const info = await kernel.checkUpdate();
|
|
261
|
+
console.log(' 当前: ' + info.current);
|
|
262
|
+
console.log(' 最新: ' + info.latest);
|
|
263
|
+
|
|
264
|
+
if (!info.needsUpdate) {
|
|
265
|
+
console.log(' 已是最新版本');
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
console.log('\n 正在下载...');
|
|
270
|
+
const result = await kernel.downloadKernel((msg) => {
|
|
271
|
+
console.log(' ' + msg);
|
|
272
|
+
});
|
|
273
|
+
console.log(' 已更新到 ' + result.version);
|
|
274
|
+
} catch (e) {
|
|
275
|
+
console.error(' 更新失败: ' + e.message);
|
|
276
|
+
process.exit(1);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async function cmdSub(args) {
|
|
281
|
+
const action = args[1];
|
|
282
|
+
|
|
283
|
+
if (!action || action === 'list') {
|
|
284
|
+
const subs = config.getSubscriptions();
|
|
285
|
+
if (subs.length === 0) {
|
|
286
|
+
console.log(' 没有订阅');
|
|
287
|
+
console.log('\n 添加订阅:');
|
|
288
|
+
console.log(' mihomo-cli sub add <url> [name]');
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
console.log(' 订阅列表:');
|
|
292
|
+
subs.forEach((s, i) => {
|
|
293
|
+
const time = s.updatedAt ? new Date(s.updatedAt).toLocaleString('zh-CN') : '未更新';
|
|
294
|
+
console.log(' ' + (i + 1) + '. ' + s.name + ' (' + time + ')');
|
|
295
|
+
});
|
|
296
|
+
console.log('\n 更新订阅:');
|
|
297
|
+
console.log(' mihomo-cli sub update [name]');
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (action === 'add') {
|
|
302
|
+
const url = args[2];
|
|
303
|
+
const name = args[3] || 'default';
|
|
304
|
+
|
|
305
|
+
if (!url || !url.startsWith('http')) {
|
|
306
|
+
console.error(' 错误: 请提供有效的订阅 URL');
|
|
307
|
+
process.exit(1);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
console.log(' 添加订阅: ' + name);
|
|
311
|
+
try {
|
|
312
|
+
config.addSubscription(url, name);
|
|
313
|
+
const info = await subscription.downloadSubscription(url, name);
|
|
314
|
+
console.log(' 已添加 (节点: ' + info.proxies + ')');
|
|
315
|
+
} catch (e) {
|
|
316
|
+
console.error(' 添加失败: ' + e.message);
|
|
317
|
+
process.exit(1);
|
|
318
|
+
}
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (action === 'update') {
|
|
323
|
+
const name = args[2];
|
|
324
|
+
const subs = config.getSubscriptions();
|
|
325
|
+
|
|
326
|
+
if (subs.length === 0) {
|
|
327
|
+
console.error(' 错误: 没有订阅');
|
|
328
|
+
process.exit(1);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
let target = name ? subs.find(s => s.name === name) : subs[0];
|
|
332
|
+
if (!target) {
|
|
333
|
+
console.error(' 错误: 未找到订阅 "' + name + '"');
|
|
334
|
+
process.exit(1);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
console.log(' 更新订阅: ' + target.name);
|
|
338
|
+
try {
|
|
339
|
+
const info = await subscription.downloadSubscription(target.url, target.name);
|
|
340
|
+
console.log(' 已更新 (节点: ' + info.proxies + ')');
|
|
341
|
+
} catch (e) {
|
|
342
|
+
console.error(' 更新失败: ' + e.message);
|
|
343
|
+
process.exit(1);
|
|
344
|
+
}
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
console.error(' 错误: 未知的订阅命令');
|
|
349
|
+
console.log(' 用法: mihomo-cli sub [list|add|update]');
|
|
350
|
+
process.exit(1);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async function cmdReset(args) {
|
|
354
|
+
const fullReset = args && (args.includes('--full') || args.includes('-f'));
|
|
355
|
+
const skipConfirm = args && (args.includes('--yes') || args.includes('-y'));
|
|
356
|
+
|
|
357
|
+
const pids = processMgr.getAllMihomoPids();
|
|
358
|
+
if (pids.length > 0) {
|
|
359
|
+
console.log(' 停止 ' + pids.length + ' 个进程...');
|
|
360
|
+
processMgr.cleanupAll(true);
|
|
361
|
+
for (let i = 0; i < 50; i++) {
|
|
362
|
+
if (processMgr.getAllMihomoPids().length === 0) break;
|
|
363
|
+
await new Promise(r => setTimeout(r, 100));
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const mode = fullReset ? '完整重置 (含内核)' : '重置配置';
|
|
368
|
+
console.log(' ' + mode);
|
|
369
|
+
|
|
370
|
+
if (!skipConfirm) {
|
|
371
|
+
const readline = require('readline');
|
|
372
|
+
const rl = readline.createInterface({
|
|
373
|
+
input: process.stdin,
|
|
374
|
+
output: process.stdout
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
const answer = await new Promise(resolve => {
|
|
378
|
+
rl.question(' 确认? (y/N) ', (a) => {
|
|
379
|
+
rl.close();
|
|
380
|
+
resolve(a);
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
if (answer.toLowerCase() !== 'y' && answer.toLowerCase() !== 'yes') {
|
|
385
|
+
console.log(' 已取消');
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const count = config.resetUserData({ keepKernel: !fullReset });
|
|
391
|
+
console.log(' 已重置 ' + count + ' 项');
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function cmdDirs() {
|
|
395
|
+
console.log('');
|
|
396
|
+
console.log(' 数据目录位置:');
|
|
397
|
+
console.log(' 根目录: ' + config.USER_DATA_DIR);
|
|
398
|
+
console.log(' 全局设置: ' + config.PATHS.settingsFile);
|
|
399
|
+
console.log(' 内核文件: ' + config.PATHS.mihomoBinary);
|
|
400
|
+
console.log(' 订阅目录: ' + config.DIRS.subs);
|
|
401
|
+
console.log(' - xxx.yaml (订阅原始配置,不修改)');
|
|
402
|
+
console.log(' 运行时目录: ' + config.DIRS.runtime);
|
|
403
|
+
console.log(' - config.yaml (启动时生成,stop 自动清除)');
|
|
404
|
+
console.log(' - pid (PID 文件,stop 自动清除)');
|
|
405
|
+
console.log(' 日志文件: ' + config.PATHS.logFile);
|
|
406
|
+
console.log(' mihomo 数据: ' + config.DIRS.data);
|
|
407
|
+
console.log(' - cache.db, Geo*.dat 等 (mihomo 自行管理)');
|
|
408
|
+
console.log('');
|
|
409
|
+
console.log(' 环境变量:');
|
|
410
|
+
console.log(' MIHOMO_CLI_DIR: 自定义根目录位置');
|
|
411
|
+
console.log('');
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
async function main() {
|
|
415
|
+
config.ensureDirs();
|
|
416
|
+
|
|
417
|
+
const args = process.argv.slice(2);
|
|
418
|
+
|
|
419
|
+
if (args.length === 0) {
|
|
420
|
+
printHelp();
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const cmd = args[0].toLowerCase();
|
|
425
|
+
|
|
426
|
+
if (['help', '-h', '--help'].includes(cmd)) {
|
|
427
|
+
printHelp();
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
if (['version', '-v', '--version'].includes(cmd)) {
|
|
431
|
+
printVersion();
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
switch (cmd) {
|
|
436
|
+
case 'start':
|
|
437
|
+
await cmdStart(args);
|
|
438
|
+
break;
|
|
439
|
+
case 'stop':
|
|
440
|
+
await cmdStop();
|
|
441
|
+
break;
|
|
442
|
+
case 'restart':
|
|
443
|
+
await cmdRestart(args);
|
|
444
|
+
break;
|
|
445
|
+
case 'status':
|
|
446
|
+
printStatus();
|
|
447
|
+
break;
|
|
448
|
+
case 'log':
|
|
449
|
+
cmdLog();
|
|
450
|
+
break;
|
|
451
|
+
case 'ui':
|
|
452
|
+
cmdUi(args);
|
|
453
|
+
break;
|
|
454
|
+
case 'clean':
|
|
455
|
+
cmdClean();
|
|
456
|
+
break;
|
|
457
|
+
case 'kernel':
|
|
458
|
+
await cmdKernel();
|
|
459
|
+
break;
|
|
460
|
+
case 'sub':
|
|
461
|
+
case 'subscription':
|
|
462
|
+
await cmdSub(args);
|
|
463
|
+
break;
|
|
464
|
+
case 'dirs':
|
|
465
|
+
cmdDirs();
|
|
466
|
+
break;
|
|
467
|
+
case 'reset':
|
|
468
|
+
await cmdReset(args);
|
|
469
|
+
break;
|
|
470
|
+
default:
|
|
471
|
+
console.error(' 未知命令: ' + cmd);
|
|
472
|
+
console.error(' 使用 "mihomo-cli help" 查看帮助');
|
|
473
|
+
process.exit(1);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
main().catch(e => {
|
|
478
|
+
console.error(' 错误:', e.message);
|
|
479
|
+
process.exit(1);
|
|
480
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mihomo-cli",
|
|
3
|
+
"version": "1.0.0-alpha.1",
|
|
4
|
+
"description": "A terminal-based mihomo (Clash.Meta) client for macOS",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"mihomo-cli": "./index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"index.js",
|
|
11
|
+
"src/**/*.js"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"start": "node index.js"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"mihomo",
|
|
18
|
+
"clash",
|
|
19
|
+
"clash-meta",
|
|
20
|
+
"proxy",
|
|
21
|
+
"vpn",
|
|
22
|
+
"cli",
|
|
23
|
+
"macos"
|
|
24
|
+
],
|
|
25
|
+
"author": "",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=18.0.0"
|
|
29
|
+
},
|
|
30
|
+
"os": ["darwin"],
|
|
31
|
+
"cpu": ["x64", "arm64"],
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"axios": "^1.6.0",
|
|
34
|
+
"compare-versions": "^6.1.0",
|
|
35
|
+
"js-yaml": "^4.1.0"
|
|
36
|
+
}
|
|
37
|
+
}
|