mihomo-cli 1.1.0 → 1.2.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 CHANGED
@@ -1,5 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.2.0] - 2026-04-05
4
+
5
+ ### 新增功能
6
+
7
+ #### 配置变更自动重启
8
+ - **sub use 自动重启**:切换默认订阅后,如果 mihomo 正在运行则自动重启
9
+ - **ow on/off 自动重启**:启用/禁用覆写配置后,如果 mihomo 正在运行则自动重启
10
+ - **状态检查**:操作前检查是否已是目标状态,避免重复操作
11
+
12
+ ### 重构
13
+
14
+ #### 命名规范统一
15
+ - **函数重命名**:统一使用全称单数
16
+ - `findSubsFuzzy` → `findSubscriptionFuzzy`
17
+ - `pickSingleSub` → `pickSingleSubscription`
18
+ - `printSubList` → `printSubscriptionList`
19
+ - `cmdSub` → `cmdSubscription`
20
+ - `cmdDirs` → `cmdDirectory`
21
+ - `DIR_TARGETS` → `DIRECTORY_TARGETS`
22
+ - **帮助文档统一**:
23
+ - 示例统一使用 `mihomo` 而非 `mihomo-cli`
24
+ - 命令列表使用全称单数 `directory` 而非 `directories`
25
+ - 提示语中的命令示例添加 `mihomo` 前缀
26
+
27
+ ### 修复
28
+
29
+ - 修复缺失的 `path` 模块导入
30
+ - 统一引号风格(命令示例使用双引号)
31
+
3
32
  ## [1.1.0] - 2026-04-05
4
33
 
5
34
  ### 新增功能
package/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ const path = require('path');
3
4
  const { spawn } = require('child_process');
4
5
 
5
6
  const config = require('./src/config');
@@ -60,7 +61,7 @@ function printShortHelp() {
60
61
  ' subscription update 更新订阅\n' +
61
62
  ' subscription use <name> 切换默认订阅\n' +
62
63
  ' overwrite [on|off] 覆写配置(别名 ow)\n' +
63
- ' directories 数据目录(别名 dir)\n' +
64
+ ' directory 数据目录(别名 dir)\n' +
64
65
  ' kernel 更新内核\n' +
65
66
  ' reset 重置配置\n' +
66
67
  ' version 版本信息\n');
@@ -72,7 +73,7 @@ function printHelp() {
72
73
  '命令别名: mihomo, mmc, mh\n' +
73
74
  '\n' +
74
75
  '用法:\n' +
75
- ' mihomo-cli <命令> [选项]\n' +
76
+ ' mihomo <命令> [选项]\n' +
76
77
  '\n' +
77
78
  '控制:\n' +
78
79
  ' start [tun|mixed] 启动/切换代理 (默认 mixed)\n' +
@@ -92,7 +93,7 @@ function printHelp() {
92
93
  '\n' +
93
94
  '配置:\n' +
94
95
  ' overwrite [on|off] 覆写配置(别名 ow)\n' +
95
- ' directories [open] 数据目录(别名 dir)\n' +
96
+ ' directory [open] 数据目录(别名 dir)\n' +
96
97
  '\n' +
97
98
  '系统:\n' +
98
99
  ' kernel [镜像|--no-mirror] 更新内核\n' +
@@ -101,10 +102,10 @@ function printHelp() {
101
102
  ' version, -v 显示版本\n' +
102
103
  '\n' +
103
104
  '示例:\n' +
104
- ' mihomo-cli start # 启动/重启 Mixed 模式\n' +
105
- ' mihomo-cli start tun # 切换到 TUN 透明代理模式\n' +
106
- ' mihomo-cli sub add <url> # 添加订阅 (sub 是 subscription 别名)\n' +
107
- ' mihomo-cli ui # 打开 Web UI\n' +
105
+ ' mihomo start # 启动/重启 Mixed 模式\n' +
106
+ ' mihomo start tun # 切换到 TUN 透明代理模式\n' +
107
+ ' mihomo sub add <url> # 添加订阅 (sub 是 subscription 别名)\n' +
108
+ ' mihomo ui # 打开 Web UI\n' +
108
109
  '\n' +
109
110
  '模式说明:\n' +
110
111
  ' mixed HTTP + SOCKS5 混合端口 (默认)\n' +
@@ -189,7 +190,7 @@ function getActiveSubscription() {
189
190
  return subs[0];
190
191
  }
191
192
 
192
- function findSubsFuzzy(subs, pattern) {
193
+ function findSubscriptionFuzzy(subs, pattern) {
193
194
  const lowerPattern = pattern.toLowerCase();
194
195
  let exact = [];
195
196
  let prefix = [];
@@ -211,7 +212,7 @@ function findSubsFuzzy(subs, pattern) {
211
212
  return includes;
212
213
  }
213
214
 
214
- function pickSingleSub(subs, pattern, actionName) {
215
+ function pickSingleSubscription(subs, pattern, actionName) {
215
216
  if (subs.length === 0) {
216
217
  console.error('错误: 未找到匹配 "' + pattern + '" 的订阅');
217
218
  process.exit(1);
@@ -297,7 +298,7 @@ function viewLogWithTail(logPath, options) {
297
298
 
298
299
  async function cmdStart(args) {
299
300
  if (!config.hasKernel()) {
300
- console.error('错误: 未找到内核,请运行 \'mihomo-cli kernel\'');
301
+ console.error('错误: 未找到内核,请运行 "mihomo kernel"');
301
302
  process.exit(1);
302
303
  }
303
304
 
@@ -422,7 +423,7 @@ function cmdLogs(args) {
422
423
 
423
424
  if (!logPath) {
424
425
  console.error('错误: 未找到日志 "' + targetName + '"');
425
- console.log('使用 "mihomo-cli logs" 查看可用日志列表');
426
+ console.log('使用 "mihomo logs" 查看可用日志列表');
426
427
  process.exit(1);
427
428
  }
428
429
 
@@ -461,16 +462,16 @@ function cmdLogs(args) {
461
462
  console.log(' ' + num + '. ' + name);
462
463
  console.log(' 时间: ' + time + ' 大小: ' + size);
463
464
  if (!log.isCurrent) {
464
- console.log(' 查看: mihomo-cli logs ' + idx + ' 或 mihomo-cli logs -o ' + idx);
465
+ console.log(' 查看: mihomo logs ' + idx + ' 或 mihomo logs -o ' + idx);
465
466
  }
466
467
  console.log('');
467
468
  });
468
469
 
469
470
  console.log('用法:');
470
- console.log(' mihomo-cli logs 0 # 查看当前日志 (最后 100 行)');
471
- console.log(' mihomo-cli logs 1 # 查看第 1 个归档日志');
472
- console.log(' mihomo-cli logs 1 -n 200 # 查看 200 行');
473
- console.log(' mihomo-cli logs 1 -o # 用系统默认程序打开');
471
+ console.log(' mihomo logs 0 # 查看当前日志 (最后 100 行)');
472
+ console.log(' mihomo logs 1 # 查看第 1 个归档日志');
473
+ console.log(' mihomo logs 1 -n 200 # 查看 200 行');
474
+ console.log(' mihomo logs 1 -o # 用系统默认程序打开');
474
475
  console.log('');
475
476
  }
476
477
 
@@ -549,10 +550,10 @@ async function cmdKernel(args) {
549
550
  });
550
551
 
551
552
  console.log('\n用法:');
552
- console.log(' mihomo-cli kernel # 使用默认镜像');
553
- console.log(' mihomo-cli kernel hk.gh-proxy.org # 使用指定镜像');
554
- console.log(' mihomo-cli kernel --mirror hk.gh-proxy.org');
555
- console.log(' mihomo-cli kernel --no-mirror # 直连,不使用镜像');
553
+ console.log(' mihomo kernel # 使用默认镜像');
554
+ console.log(' mihomo kernel hk.gh-proxy.org # 使用指定镜像');
555
+ console.log(' mihomo kernel --mirror hk.gh-proxy.org');
556
+ console.log(' mihomo kernel --no-mirror # 直连,不使用镜像');
556
557
  console.log('');
557
558
 
558
559
  try {
@@ -576,7 +577,7 @@ async function cmdKernel(args) {
576
577
  }
577
578
  }
578
579
 
579
- async function printSubList() {
580
+ async function printSubscriptionList() {
580
581
  const updateResult = await subscription.autoUpdateStaleSubscriptions();
581
582
  if (updateResult.total > 0) {
582
583
  console.log('');
@@ -586,7 +587,7 @@ async function printSubList() {
586
587
  if (subs.length === 0) {
587
588
  console.log('没有订阅');
588
589
  console.log('');
589
- console.log('添加订阅: mihomo-cli sub add <url> [name]');
590
+ console.log('添加订阅: mihomo sub add <url> [name]');
590
591
  console.log('');
591
592
  return;
592
593
  }
@@ -620,17 +621,17 @@ async function printSubList() {
620
621
  }
621
622
  });
622
623
  console.log('');
623
- console.log('切换默认: sub use <name>');
624
- console.log('更新订阅: sub update [name]');
625
- console.log('打开页面: sub web [name]');
624
+ console.log('切换默认: mihomo sub use <name>');
625
+ console.log('更新订阅: mihomo sub update [name]');
626
+ console.log('打开页面: mihomo sub web [name]');
626
627
  console.log('');
627
628
  }
628
629
 
629
- async function cmdSub(args) {
630
+ async function cmdSubscription(args) {
630
631
  const action = args[1];
631
632
 
632
633
  if (!action || action === 'list') {
633
- await printSubList();
634
+ await printSubscriptionList();
634
635
  return;
635
636
  }
636
637
 
@@ -656,7 +657,7 @@ async function cmdSub(args) {
656
657
  process.exit(1);
657
658
  }
658
659
  console.log('');
659
- await printSubList();
660
+ await printSubscriptionList();
660
661
  return;
661
662
  }
662
663
 
@@ -686,12 +687,12 @@ async function cmdSub(args) {
686
687
  });
687
688
  if (ok === 0) process.exit(1);
688
689
  console.log('');
689
- await printSubList();
690
+ await printSubscriptionList();
690
691
  return;
691
692
  }
692
693
 
693
- const matches = findSubsFuzzy(subs, name);
694
- const target = pickSingleSub(matches, name, '更新');
694
+ const matches = findSubscriptionFuzzy(subs, name);
695
+ const target = pickSingleSubscription(matches, name, '更新');
695
696
 
696
697
  console.log('更新订阅: ' + target.name);
697
698
  try {
@@ -705,7 +706,7 @@ async function cmdSub(args) {
705
706
  process.exit(1);
706
707
  }
707
708
  console.log('');
708
- await printSubList();
709
+ await printSubscriptionList();
709
710
  return;
710
711
  }
711
712
 
@@ -722,8 +723,24 @@ async function cmdSub(args) {
722
723
  process.exit(1);
723
724
  }
724
725
 
725
- const matches = findSubsFuzzy(subs, name);
726
- const target = pickSingleSub(matches, name, '切换');
726
+ const matches = findSubscriptionFuzzy(subs, name);
727
+ const target = pickSingleSubscription(matches, name, '切换');
728
+
729
+ // 检查是否已是当前默认订阅
730
+ const currentDefault = getActiveSubscription();
731
+ const isAlreadyDefault = currentDefault && currentDefault.name === target.name;
732
+
733
+ if (isAlreadyDefault) {
734
+ console.log('"' + target.name + '" 已是当前默认订阅');
735
+ console.log('');
736
+ await printSubscriptionList();
737
+ return;
738
+ }
739
+
740
+ // 检查当前运行状态和模式
741
+ const status = processMgr.getStatus();
742
+ const cfgInfo = config.getConfigInfo();
743
+ const currentMode = cfgInfo && cfgInfo.tun ? 'tun' : 'mixed';
727
744
 
728
745
  const success = config.setDefaultSubscription(target.name);
729
746
  if (success) {
@@ -732,8 +749,16 @@ async function cmdSub(args) {
732
749
  console.error('错误: 未找到订阅 "' + name + '"');
733
750
  process.exit(1);
734
751
  }
752
+
753
+ // 如果正在运行,自动重启
754
+ if (status.running) {
755
+ console.log('');
756
+ await cmdStart(['start', currentMode]);
757
+ return;
758
+ }
759
+
735
760
  console.log('');
736
- await printSubList();
761
+ await printSubscriptionList();
737
762
  return;
738
763
  }
739
764
 
@@ -748,8 +773,8 @@ async function cmdSub(args) {
748
773
 
749
774
  let target;
750
775
  if (name) {
751
- const matches = findSubsFuzzy(subs, name);
752
- target = pickSingleSub(matches, name, '打开');
776
+ const matches = findSubscriptionFuzzy(subs, name);
777
+ target = pickSingleSubscription(matches, name, '打开');
753
778
  } else {
754
779
  target = subs[0];
755
780
  }
@@ -782,7 +807,7 @@ async function cmdSub(args) {
782
807
  }
783
808
 
784
809
  console.error('错误: 未知的订阅命令');
785
- console.log('用法: mihomo-cli sub [list|add|update|use|web]');
810
+ console.log('用法: mihomo sub [list|add|update|use|web]');
786
811
  process.exit(1);
787
812
  }
788
813
 
@@ -829,7 +854,6 @@ async function cmdReset(args) {
829
854
 
830
855
  function printOverwriteList() {
831
856
  const info = overwrite.listOverwriteFiles();
832
- console.log('');
833
857
  console.log('状态: ' + (info.enabled ? '已启用' : '已禁用'));
834
858
  console.log('目录: ' + info.dir);
835
859
  console.log('');
@@ -850,38 +874,87 @@ function printOverwriteList() {
850
874
  });
851
875
  console.log('');
852
876
  }
853
- console.log('启用覆写: ow on');
854
- console.log('禁用覆写: ow off');
877
+ console.log('启用覆写: mihomo ow on');
878
+ console.log('禁用覆写: mihomo ow off');
855
879
  console.log('');
856
880
  }
857
881
 
858
- function cmdOverwrite(args) {
882
+ async function cmdOverwrite(args) {
859
883
  const action = args && args[1];
860
884
 
885
+ // 检查当前运行状态和模式
886
+ const status = processMgr.getStatus();
887
+ const cfgInfo = config.getConfigInfo();
888
+ const currentMode = cfgInfo && cfgInfo.tun ? 'tun' : 'mixed';
889
+
861
890
  if (action === 'on' || action === 'enable') {
891
+ // 如果已经启用,提示后直接返回
892
+ if (overwrite.isOverwriteEnabled()) {
893
+ console.log('覆写配置已是启用状态');
894
+ console.log('');
895
+ printOverwriteList();
896
+ return;
897
+ }
898
+
862
899
  overwrite.setOverwriteEnabled(true);
863
900
  console.log('已启用覆写配置');
901
+
902
+ // 如果正在运行,自动重启
903
+ if (status.running) {
904
+ console.log('');
905
+ await cmdStart(['start', currentMode]);
906
+ return;
907
+ }
908
+
864
909
  console.log('');
865
910
  printOverwriteList();
866
911
  return;
867
912
  }
868
913
 
869
914
  if (action === 'off' || action === 'disable') {
915
+ // 如果已经禁用,提示后直接返回
916
+ if (!overwrite.isOverwriteEnabled()) {
917
+ console.log('覆写配置已是禁用状态');
918
+ console.log('');
919
+ printOverwriteList();
920
+ return;
921
+ }
922
+
870
923
  overwrite.setOverwriteEnabled(false);
871
924
  console.log('已禁用覆写配置');
925
+
926
+ // 如果正在运行,自动重启
927
+ if (status.running) {
928
+ console.log('');
929
+ await cmdStart(['start', currentMode]);
930
+ return;
931
+ }
932
+
872
933
  console.log('');
873
934
  printOverwriteList();
874
935
  return;
875
936
  }
876
937
 
877
938
  // 无参数、list、ls 都显示文件列表
939
+ console.log('');
878
940
  printOverwriteList();
879
941
  }
880
942
 
881
- function cmdDirs(args) {
943
+ // 目录目标映射(精确匹配)
944
+ const DIRECTORY_TARGETS = {
945
+ 'root': { path: null, label: '根目录' },
946
+ 'subs': { path: config.DIRS.subscriptions, label: '订阅目录' },
947
+ 'logs': { path: config.DIRS.logs, label: '日志目录' },
948
+ 'data': { path: config.DIRS.data, label: 'mihomo 数据目录' },
949
+ 'runtime': { path: config.DIRS.runtime, label: '运行时目录' },
950
+ 'overwrites': { path: config.DIRS.overwrites, label: '覆写目录' },
951
+ 'settings': { path: config.PATHS.settingsFile, label: '设置文件' },
952
+ 'kernel': { path: config.DIRS.core, label: '内核目录' },
953
+ };
954
+
955
+ function cmdDirectory(args) {
882
956
  const action = args && args[1];
883
957
 
884
- // dirs open [root|subs|logs|data|runtime|config|kernel]
885
958
  if (action === 'open') {
886
959
  const target = args[2];
887
960
 
@@ -890,23 +963,10 @@ function cmdDirs(args) {
890
963
  return;
891
964
  }
892
965
 
893
- const dirMap = {
894
- 'subs': { path: config.DIRS.subscriptions, label: '订阅目录' },
895
- 'subscriptions': { path: config.DIRS.subscriptions, label: '订阅目录' },
896
- 'logs': { path: config.DIRS.logs, label: '日志目录' },
897
- 'data': { path: config.DIRS.data, label: 'mihomo 数据目录' },
898
- 'runtime': { path: config.DIRS.runtime, label: '运行时目录' },
899
- };
900
-
901
- const fileMap = {
902
- 'config': { path: config.PATHS.settingsFile, label: '设置文件' },
903
- 'settings': { path: config.PATHS.settingsFile, label: '设置文件' },
904
- 'kernel': { path: config.DIRS.core, label: '内核目录' },
905
- };
906
-
907
- const targetInfo = dirMap[target] || fileMap[target];
966
+ const targetInfo = DIRECTORY_TARGETS[target.toLowerCase()];
908
967
  if (targetInfo) {
909
- openDir(targetInfo.path, targetInfo.label);
968
+ const path = targetInfo.path || config.USER_DATA_DIR;
969
+ openDir(path, targetInfo.label);
910
970
  return;
911
971
  }
912
972
 
@@ -918,7 +978,8 @@ function cmdDirs(args) {
918
978
  console.log(' logs 日志目录');
919
979
  console.log(' data mihomo 数据目录');
920
980
  console.log(' runtime 运行时目录');
921
- console.log(' config 设置文件 (settings.json)');
981
+ console.log(' overwrites 覆写目录');
982
+ console.log(' settings 设置文件 (settings.json)');
922
983
  console.log(' kernel 内核目录');
923
984
  console.log('');
924
985
  process.exit(1);
@@ -941,10 +1002,13 @@ function cmdDirs(args) {
941
1002
  console.log(' - cache.db, Geo*.dat 等 (mihomo 自行管理)');
942
1003
  console.log('');
943
1004
  console.log('打开目录:');
944
- console.log(' mihomo-cli dirs open 打开根目录');
945
- console.log(' mihomo-cli dirs open subscriptions 打开订阅目录');
946
- console.log(' mihomo-cli dirs open logs 打开日志目录');
947
- console.log(' mihomo-cli dirs open config 打开设置文件');
1005
+ console.log(' mihomo dir open 打开根目录');
1006
+ console.log(' mihomo dir open subs 打开订阅目录');
1007
+ console.log(' mihomo dir open logs 打开日志目录');
1008
+ console.log(' mihomo dir open runtime 打开运行时目录');
1009
+ console.log(' mihomo dir open overwrites 打开覆写目录');
1010
+ console.log(' mihomo dir open settings 打开设置文件');
1011
+ console.log(' mihomo dir open kernel 打开内核目录');
948
1012
  console.log('');
949
1013
  console.log('环境变量:');
950
1014
  console.log(' MIHOMO_CLI_DIR: 自定义根目录位置');
@@ -998,24 +1062,24 @@ async function main() {
998
1062
  case 'sub':
999
1063
  case 'subscription':
1000
1064
  case 'subscriptions':
1001
- await cmdSub(args);
1065
+ await cmdSubscription(args);
1002
1066
  break;
1003
1067
  case 'dir':
1004
1068
  case 'dirs':
1005
1069
  case 'directory':
1006
1070
  case 'directories':
1007
- cmdDirs(args);
1071
+ cmdDirectory(args);
1008
1072
  break;
1009
1073
  case 'reset':
1010
1074
  await cmdReset(args);
1011
1075
  break;
1012
- case 'overwrite':
1013
1076
  case 'ow':
1014
- cmdOverwrite(args);
1077
+ case 'overwrite':
1078
+ await cmdOverwrite(args);
1015
1079
  break;
1016
1080
  default:
1017
1081
  console.error('未知命令: ' + cmd);
1018
- console.error('使用 "mihomo-cli help" 查看帮助');
1082
+ console.error('使用 "mihomo help" 查看帮助');
1019
1083
  process.exit(1);
1020
1084
  }
1021
1085
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mihomo-cli",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "A terminal-based mihomo (Clash.Meta) client for macOS",
5
5
  "main": "index.js",
6
6
  "bin": {
package/src/overwrite.js CHANGED
@@ -247,12 +247,8 @@ function listOverwriteFiles() {
247
247
  }
248
248
 
249
249
  module.exports = {
250
- parseOverrideKey,
251
- deepMergeWithOverrides,
252
- getOverwritesDir,
253
250
  isOverwriteEnabled,
254
251
  setOverwriteEnabled,
255
- loadOverwriteFiles,
256
252
  applyOverwrites,
257
253
  listOverwriteFiles,
258
254
  };