mihomo-cli 1.4.1 → 1.5.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,42 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.5.0] - 2026-04-10
4
+
5
+ ### 新增功能
6
+
7
+ - **快捷命令**:新增顶层命令快捷方式,减少输入
8
+ - `mihomo up` = `mihomo start`
9
+ - `mihomo down` = `mihomo stop`
10
+ - `mihomo tun` = `mihomo start tun`
11
+ - `mihomo use <name>` = `mihomo sub use <name>`
12
+ - `mihomo on` / `mihomo off` = `mihomo ow on` / `mihomo ow off`
13
+ - `mihomo open <target>` = `mihomo dir open <target>`
14
+ - **订阅选择机制**:使用 `active_subscription` 字段标识当前订阅,不再依赖数组顺序
15
+ - **配置构建调试**:运行时目录生成 3 阶段中间文件,方便排查配置问题
16
+ - `1.subscription.yaml` — 订阅原始配置
17
+ - `2.overwrite.yaml` — 覆写合并内容
18
+ - `3.system.yaml` — 系统补充值(BASE_CONFIG + TUN)
19
+
20
+ ### 重构
21
+
22
+ - **目录结构调整**:
23
+ - `core/` → `kernel/`(内核目录)
24
+ - `.runtime/` → `runtime/`(运行时目录)
25
+ - `overwrites/` 目录 → 根目录 `overwrite.yaml` + `overwrite.*.yaml`(覆写文件扁平化)
26
+ - **配置合并逻辑**:BASE_CONFIG / TUN_CONFIG 改为只补充订阅中缺失的字段,不再强制覆盖已有值
27
+ - **TUN 模式**:移除 `ipv6: false` 硬编码,交由订阅或覆写控制
28
+ - **`dir open` 目标精简**:移除 `overwrites` 和 `settings`,保留 `root|subs|logs|data|runtime|kernel`
29
+
30
+ ### 优化
31
+
32
+ - **文案调整**:
33
+ - "默认订阅" → "当前订阅" / "使用中"
34
+ - 覆写文件名显示去除 `overwrite.` 前缀
35
+ - 覆写配置 "目录" → "位置"
36
+ - **dir 信息**:新增显示内核目录路径
37
+
38
+ ---
39
+
3
40
  ## [1.4.1] - 2026-04-08
4
41
 
5
42
  ### 优化
package/README.md CHANGED
@@ -96,7 +96,7 @@ mihomo ui yacd # YACD
96
96
  | `mihomo sub add <url> [name]` | 添加订阅 |
97
97
  | `mihomo sub update` | 更新所有订阅 |
98
98
  | `mihomo sub update <name>` | 更新指定订阅(支持模糊匹配) |
99
- | `mihomo sub use <name>` | 设置默认订阅(支持模糊匹配,自动重启) |
99
+ | `mihomo sub use <name>` | 切换当前订阅(支持模糊匹配,自动重启) |
100
100
  | `mihomo sub web [name]` | 打开订阅页面(无参打开默认) |
101
101
 
102
102
  ### 覆写配置
@@ -115,7 +115,7 @@ mihomo ui yacd # YACD
115
115
  | `mihomo update` | 更新 mihomo-cli (npm install -g) |
116
116
  | `mihomo ui [zash\|dash\|yacd]` | 打开 Web UI |
117
117
  | `mihomo dir` | 显示数据目录位置 |
118
- | `mihomo dir open [target]` | 打开指定目录(`root`, `subs`, `logs`, `overwrites` 等) |
118
+ | `mihomo dir open [target]` | 打开指定目录(`root`, `subs`, `logs`, `kernel` 等) |
119
119
  | `mihomo reset [目标...] [--full]` | 重置用户数据(可用目标:`subs`, `logs`, `kernel`, `overwrites` 等) |
120
120
  | `mihomo version` | 显示版本信息 |
121
121
  | `mihomo help` | 显示帮助信息 |
@@ -129,6 +129,19 @@ mihomo ui yacd # YACD
129
129
  - `mmc`
130
130
  - `mh`
131
131
 
132
+ ### 快捷命令
133
+
134
+ 常用操作的快捷方式:
135
+
136
+ | 快捷命令 | 等效于 |
137
+ | ---------------------- | -------------------------- |
138
+ | `mihomo up` | `mihomo start` |
139
+ | `mihomo down` | `mihomo stop` |
140
+ | `mihomo tun` | `mihomo start tun` |
141
+ | `mihomo use <name>` | `mihomo sub use <name>` |
142
+ | `mihomo on` / `off` | `mihomo ow on` / `ow off` |
143
+ | `mihomo open <target>` | `mihomo dir open <target>` |
144
+
132
145
  ## 模式说明
133
146
 
134
147
  ### Mixed 模式(默认)
@@ -185,17 +198,18 @@ mihomo kernel --mirror-all hk.gh-proxy.org
185
198
  ```
186
199
  ~/.mihomo-cli/
187
200
  ├── settings.json # 用户设置(订阅列表等)
201
+ ├── overwrite.yaml # 覆写配置(主文件,可选)
202
+ ├── overwrite.*.yaml # 覆写配置(扩展文件,如 overwrite.dns.yaml)
188
203
  ├── subscriptions/
189
204
  │ ├── cache.json # 订阅动态缓存(更新时间、流量、到期时间等)
190
205
  │ └── <name>.yaml # 订阅原始配置
191
- ├── overwrites/ # 覆写配置(按文件名排序加载)
192
- ├── core/
206
+ ├── kernel/
193
207
  │ └── mihomo # mihomo 内核二进制
194
208
  ├── logs/
195
209
  │ ├── mihomo.log # 当前日志
196
210
  │ └── mihomo.YYYY-MM-DD_HH-MM-SS.log # 归档日志
197
211
  ├── data/ # mihomo 运行数据(GeoIP 等,由内核自行管理)
198
- └── .runtime/ # 运行时临时文件(stop 自动清除)
212
+ └── runtime/ # 运行时临时文件(stop 自动清除)
199
213
  ├── pid # 进程 PID
200
214
  └── config.yaml # 运行时生成的配置
201
215
  ```
@@ -208,8 +222,10 @@ mihomo kernel --mirror-all hk.gh-proxy.org
208
222
 
209
223
  ### 使用方法
210
224
 
211
- 1. 在 `~/.mihomo-cli/overwrites/` 目录下创建 `.yaml` 或 `.yml` 文件
212
- 2. 文件按**文件名顺序**加载,后面的文件会覆盖前面的配置
225
+ 1. 在 `~/.mihomo-cli/` 目录下创建覆写文件:
226
+ - `overwrite.yaml` — 主覆写文件
227
+ - `overwrite.dns.yaml` — 按功能拆分的扩展文件(`overwrite.*.yaml` 格式)
228
+ 2. `overwrite.yaml` 始终最先加载,扩展文件按文件名排序加载
213
229
  3. 使用 `mihomo ow on` 启用覆写配置(会自动重启)
214
230
 
215
231
  ### 特殊语法
@@ -226,7 +242,7 @@ mihomo kernel --mirror-all hk.gh-proxy.org
226
242
  ### 示例
227
243
 
228
244
  ```yaml
229
- # ~/.mihomo-cli/overwrites/01-custom.yaml
245
+ # ~/.mihomo-cli/overwrite.yaml
230
246
 
231
247
  # 强制覆盖 dns 配置
232
248
  dns!:
package/index.js CHANGED
@@ -124,7 +124,7 @@ function printHelp() {
124
124
  ' update [name] 更新订阅(无参更新所有)\n' +
125
125
  ' ' +
126
126
  colors.bold('subscription') +
127
- ' use <name> 切换默认订阅\n' +
127
+ ' use <name> 切换当前订阅\n' +
128
128
  ' ' +
129
129
  colors.bold('subscription') +
130
130
  ' web [name] 打开订阅页面\n' +
@@ -142,7 +142,7 @@ function printHelp() {
142
142
  ' 显示数据目录位置(别名 dir)\n' +
143
143
  ' ' +
144
144
  colors.bold('directory') +
145
- ' open [target] 打开目录: root|subs|logs|overwrites|...\n' +
145
+ ' open [target] 打开目录: root|subs|logs|runtime|...\n' +
146
146
  '\n' +
147
147
  colors.cyan('系统:') +
148
148
  '\n' +
@@ -235,7 +235,7 @@ function printStatus() {
235
235
  }
236
236
 
237
237
  if (overwriteEnabled && overwriteFiles.length > 0) {
238
- const names = overwriteFiles.map(f => f.name).join(', ');
238
+ const names = overwriteFiles.map(f => f.name.replace(/^overwrite\.?/, '')).join(', ');
239
239
  console.log(colors.gray('覆写: ') + colors.green('已启用') + ' (' + names + ')');
240
240
  } else if (overwriteEnabled) {
241
241
  console.log(colors.gray('覆写: ') + colors.green('已启用') + ' (无文件)');
@@ -507,10 +507,11 @@ async function printSubscriptionList() {
507
507
  console.log('');
508
508
  return;
509
509
  }
510
+ const activeSub = subscription.getActiveSubscription();
510
511
  console.log(colors.cyan('订阅列表:'));
511
512
  subs.forEach((s, i) => {
512
513
  const time = utils.formatDate(s.updated_at);
513
- const defaultMark = i === 0 ? colors.green(' [默认]') : '';
514
+ const defaultMark = activeSub && s.name === activeSub.name ? colors.green(' [使用中]') : '';
514
515
  const interval = s.update_interval || subscription.DEFAULT_UPDATE_INTERVAL_HOURS;
515
516
  console.log(' ' + (i + 1) + '. ' + s.name + defaultMark);
516
517
  console.log(' ' + colors.gray('更新: ') + time + ' (间隔: ' + interval + 'h)');
@@ -537,7 +538,7 @@ async function printSubscriptionList() {
537
538
  }
538
539
  });
539
540
  console.log('');
540
- console.log('切换默认: mihomo sub use <name>');
541
+ console.log('切换订阅: mihomo sub use <name>');
541
542
  console.log('更新订阅: mihomo sub update [name]');
542
543
  console.log('打开页面: mihomo sub web [name]');
543
544
  console.log('新增订阅: mihomo sub add <url> [name]');
@@ -638,7 +639,7 @@ async function cmdSubscription(args) {
638
639
  const isAlreadyDefault = currentDefault && currentDefault.name === target.name;
639
640
 
640
641
  if (isAlreadyDefault) {
641
- console.log('"' + target.name + '" 已是当前默认订阅');
642
+ console.log('"' + target.name + '" 已是当前使用的订阅');
642
643
  console.log('');
643
644
  await printSubscriptionList();
644
645
  return;
@@ -650,7 +651,7 @@ async function cmdSubscription(args) {
650
651
 
651
652
  const success = config.setDefaultSubscription(target.name);
652
653
  if (success) {
653
- console.log('已设置 "' + target.name + '" 为默认订阅');
654
+ console.log('已切换到 "' + target.name + '"');
654
655
  } else {
655
656
  console.error('错误: 未找到订阅 "' + name + '"');
656
657
  process.exit(1);
@@ -806,7 +807,7 @@ const RESET_TARGETS = [
806
807
  id: 'kernel',
807
808
  aliases: ['kernel', 'core'],
808
809
  label: '内核',
809
- paths: () => [config.DIRS.core],
810
+ paths: () => [config.DIRS.kernel],
810
811
  needsStop: false,
811
812
  onAfter: () => config.clearKernelVersionCache(),
812
813
  checkEmpty: () => !config.hasKernel(),
@@ -817,7 +818,15 @@ const RESET_TARGETS = [
817
818
  id: 'overwrites',
818
819
  aliases: ['overwrite', 'overwrites', 'ow'],
819
820
  label: '覆写',
820
- paths: () => [config.DIRS.overwrites],
821
+ paths: () => {
822
+ const fs = require('fs');
823
+ const dir = config.USER_DATA_DIR;
824
+ if (!fs.existsSync(dir)) return [];
825
+ return fs
826
+ .readdirSync(dir)
827
+ .filter(f => f === 'overwrite.yaml' || /^overwrite\..+\.ya?ml$/.test(f))
828
+ .map(f => require('path').join(dir, f));
829
+ },
821
830
  needsStop: false,
822
831
  },
823
832
  ];
@@ -922,12 +931,13 @@ function printOverwriteList() {
922
931
  const info = overwrite.listOverwriteFile();
923
932
  const statusText = info.enabled ? colors.green('已启用') : colors.yellow('已禁用');
924
933
  console.log(colors.gray('状态: ') + statusText);
925
- console.log(colors.gray('目录: ') + info.dir);
934
+ console.log(colors.gray('位置: ') + info.dir);
926
935
  console.log('');
927
936
  if (info.files.length === 0) {
928
937
  console.log('暂无覆写文件');
929
938
  console.log('');
930
- console.log('用法示例: 创建文件 ' + path.join(info.dir, '01-custom.yaml'));
939
+ console.log('用法示例: 创建文件 ' + path.join(info.dir, 'overwrite.yaml'));
940
+ console.log(' 或 ' + path.join(info.dir, 'overwrite.dns.yaml'));
931
941
  console.log('');
932
942
  } else {
933
943
  console.log(colors.cyan('覆写文件') + ' (' + info.files.length + ' 个,按顺序加载):');
@@ -1045,6 +1055,7 @@ function cmdDirectory(args) {
1045
1055
  console.log('数据目录位置:');
1046
1056
  console.log(' 根目录: ' + config.USER_DATA_DIR);
1047
1057
  console.log(' 全局设置: ' + config.PATHS.settingsFile);
1058
+ console.log(' 内核目录: ' + config.DIRS.kernel);
1048
1059
  console.log(' 内核文件: ' + config.PATHS.mihomoBinary);
1049
1060
  console.log(' 订阅目录: ' + config.DIRS.subscriptions);
1050
1061
  console.log(' - cache.json (订阅缓存:更新时间、流量等)');
@@ -1061,8 +1072,6 @@ function cmdDirectory(args) {
1061
1072
  console.log(' mihomo dir open subs 打开订阅目录');
1062
1073
  console.log(' mihomo dir open logs 打开日志目录');
1063
1074
  console.log(' mihomo dir open runtime 打开运行时目录');
1064
- console.log(' mihomo dir open overwrites 打开覆写目录');
1065
- console.log(' mihomo dir open settings 打开设置文件');
1066
1075
  console.log(' mihomo dir open kernel 打开内核目录');
1067
1076
  console.log('');
1068
1077
  console.log('环境变量:');
@@ -1093,9 +1102,14 @@ async function main() {
1093
1102
  }
1094
1103
 
1095
1104
  switch (cmd) {
1105
+ case 'up':
1096
1106
  case 'start':
1097
1107
  await cmdStart(args);
1098
1108
  break;
1109
+ case 'tun':
1110
+ await cmdStart(['start', 'tun']);
1111
+ break;
1112
+ case 'down':
1099
1113
  case 'stop':
1100
1114
  await cmdStop();
1101
1115
  break;
@@ -1108,6 +1122,9 @@ async function main() {
1108
1122
  case 'logs':
1109
1123
  cmdLogs(args);
1110
1124
  break;
1125
+ case 'open':
1126
+ cmdDirectory(['dir', 'open', ...args.slice(1)]);
1127
+ break;
1111
1128
  case 'ui':
1112
1129
  cmdUI(args);
1113
1130
  break;
@@ -1119,6 +1136,9 @@ async function main() {
1119
1136
  case 'upgrade':
1120
1137
  await cmdUpdate();
1121
1138
  break;
1139
+ case 'use':
1140
+ await cmdSubscription(['sub', 'use', ...args.slice(1)]);
1141
+ break;
1122
1142
  case 'sub':
1123
1143
  case 'subscription':
1124
1144
  case 'subscriptions':
@@ -1133,6 +1153,12 @@ async function main() {
1133
1153
  case 'reset':
1134
1154
  await cmdReset(args);
1135
1155
  break;
1156
+ case 'on':
1157
+ await cmdOverwrite(['ow', 'on']);
1158
+ break;
1159
+ case 'off':
1160
+ await cmdOverwrite(['ow', 'off']);
1161
+ break;
1136
1162
  case 'ow':
1137
1163
  case 'overwrite':
1138
1164
  await cmdOverwrite(args);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mihomo-cli",
3
- "version": "1.4.1",
3
+ "version": "1.5.0",
4
4
  "description": "A terminal-based mihomo (Clash.Meta) client for macOS",
5
5
  "bin": {
6
6
  "mihomo-cli": "index.js",
package/src/config.js CHANGED
@@ -30,22 +30,24 @@ const USER_DATA_DIR = getUserDataDir();
30
30
 
31
31
  const DIRS = {
32
32
  root: PROJECT_ROOT,
33
- core: path.join(USER_DATA_DIR, 'core'),
33
+ kernel: path.join(USER_DATA_DIR, 'kernel'),
34
34
  subscriptions: path.join(USER_DATA_DIR, 'subscriptions'),
35
35
  logs: path.join(USER_DATA_DIR, 'logs'),
36
36
  data: path.join(USER_DATA_DIR, 'data'),
37
- runtime: path.join(USER_DATA_DIR, '.runtime'),
38
- overwrites: path.join(USER_DATA_DIR, 'overwrites'),
37
+ runtime: path.join(USER_DATA_DIR, 'runtime'),
39
38
  };
40
39
 
41
40
  const PATHS = {
42
41
  root: DIRS.root,
43
- mihomoBinary: path.join(DIRS.core, 'mihomo'),
42
+ mihomoBinary: path.join(DIRS.kernel, 'mihomo'),
44
43
  settingsFile: path.join(USER_DATA_DIR, 'settings.json'),
45
44
  subscriptionsCacheFile: path.join(DIRS.subscriptions, 'cache.json'),
46
45
  configFile: path.join(DIRS.runtime, 'config.yaml'),
47
46
  logFile: path.join(DIRS.logs, 'mihomo.log'),
48
47
  pidFile: path.join(DIRS.runtime, 'pid'),
48
+ configStage1Subscription: path.join(DIRS.runtime, '1.subscription.yaml'),
49
+ configStage2Overwrite: path.join(DIRS.runtime, '2.overwrite.yaml'),
50
+ configStage3System: path.join(DIRS.runtime, '3.system.yaml'),
49
51
  };
50
52
 
51
53
  function ensureDirs() {
@@ -206,7 +208,11 @@ function addSubscription(url, name) {
206
208
  } else {
207
209
  subs.push({ name, url });
208
210
  }
209
- writeSettings({ subscriptions: subs });
211
+ const updates = { subscriptions: subs };
212
+ if (!settings.active_subscription && subs.length === 1) {
213
+ updates.active_subscription = name;
214
+ }
215
+ writeSettings(updates);
210
216
  }
211
217
 
212
218
  function setDefaultSubscription(name) {
@@ -216,12 +222,7 @@ function setDefaultSubscription(name) {
216
222
  if (idx < 0) {
217
223
  return false;
218
224
  }
219
- if (idx === 0) {
220
- return true; // 已经是第一个
221
- }
222
- const [sub] = subs.splice(idx, 1);
223
- subs.unshift(sub);
224
- writeSettings({ subscriptions: subs });
225
+ writeSettings({ active_subscription: name });
225
226
  return true;
226
227
  }
227
228
 
@@ -283,7 +284,6 @@ const TUN_CONFIG = {
283
284
  'auto-detect-interface': true,
284
285
  'strict-route': true,
285
286
  },
286
- ipv6: false,
287
287
  };
288
288
 
289
289
  const BASE_CONFIG = {
@@ -314,9 +314,9 @@ function parseYamlOrJson(content, errorMsg) {
314
314
  }
315
315
 
316
316
  function buildConfig(subRawContent, mode) {
317
- const baseConfig = parseYamlOrJson(subRawContent, '订阅内容');
317
+ const subscriptionConfig = parseYamlOrJson(subRawContent, '订阅内容');
318
318
 
319
- if (!baseConfig) {
319
+ if (!subscriptionConfig) {
320
320
  throw new Error('订阅内容为空');
321
321
  }
322
322
 
@@ -324,24 +324,48 @@ function buildConfig(subRawContent, mode) {
324
324
  const overwrite = require('./overwrite');
325
325
 
326
326
  // 应用覆写配置
327
- const withOverwrites = overwrite.applyOverwrite(baseConfig);
328
-
329
- // 合并 BASE_CONFIG(优先级高于覆写)
330
- const merged = { ...withOverwrites, ...BASE_CONFIG };
327
+ const overwriteEnabled = overwrite.isOverwriteEnabled();
328
+ const withOverwrites = overwrite.applyOverwrite(subscriptionConfig);
329
+ const overwriteFiles = overwriteEnabled ? overwrite.loadOverwriteFile() : [];
330
+
331
+ // 构建系统覆盖值(BASE_CONFIG + 可选 TUN)
332
+ // 只补充订阅中缺失的字段,不覆盖已有值
333
+ const systemConfig = {};
334
+ for (const [key, value] of Object.entries(BASE_CONFIG)) {
335
+ if (!(key in withOverwrites)) {
336
+ systemConfig[key] = value;
337
+ }
338
+ }
331
339
 
332
340
  if (mode === 'tun') {
333
- // 合并 TUN 配置
334
- merged.tun = TUN_CONFIG.tun;
335
- merged.ipv6 = TUN_CONFIG.ipv6;
336
-
337
- // 确保 DNS 配置与 TUN 模式兼容(保留订阅的 DNS 服务器)
338
- merged.dns = merged.dns || {};
339
- merged.dns.enable = true;
340
- merged.dns['enhanced-mode'] = 'fake-ip';
341
- merged.dns['fake-ip-range'] = merged.dns['fake-ip-range'] || '198.18.0.1/16';
341
+ // tun 块始终由系统控制
342
+ systemConfig.tun = TUN_CONFIG.tun;
343
+ // dns 只补充 TUN 必需的字段
344
+ const subDns = withOverwrites.dns || {};
345
+ systemConfig.dns = {};
346
+ if (!subDns.enable) systemConfig.dns.enable = true;
347
+ if (!subDns['enhanced-mode']) systemConfig.dns['enhanced-mode'] = 'fake-ip';
348
+ if (!subDns['fake-ip-range']) systemConfig.dns['fake-ip-range'] = '198.18.0.1/16';
349
+ // 如果没有需要补充的 dns 字段,则不设置
350
+ if (Object.keys(systemConfig.dns).length === 0) {
351
+ delete systemConfig.dns;
352
+ }
342
353
  }
343
354
 
344
- return merged;
355
+ // 合并:订阅(+overwrite) → 系统补充
356
+ const merged = { ...withOverwrites, ...systemConfig };
357
+
358
+ // dns 需要深度合并:保留订阅的 DNS 服务器,叠加系统补充
359
+ if (systemConfig.dns) {
360
+ merged.dns = { ...(withOverwrites.dns || {}), ...systemConfig.dns };
361
+ }
362
+
363
+ return {
364
+ config: merged,
365
+ subscriptionConfig,
366
+ overwriteFiles,
367
+ systemConfig,
368
+ };
345
369
  }
346
370
 
347
371
  function writeMihomoConfig(configObj) {
@@ -354,6 +378,25 @@ function writeMihomoConfig(configObj) {
354
378
  fs.writeFileSync(PATHS.configFile, content, { mode: 0o600 });
355
379
  }
356
380
 
381
+ function writeDebugConfig(buildResult) {
382
+ ensureDirs();
383
+ const dumpOpts = { indent: 2, lineWidth: -1, noCompat: true };
384
+
385
+ // 1. 订阅原始配置
386
+ fs.writeFileSync(PATHS.configStage1Subscription, yaml.dump(buildResult.subscriptionConfig, dumpOpts), { mode: 0o600 });
387
+
388
+ // 2. overwrite 覆写内容(禁用时写空文件)
389
+ const overwriteMerged = {};
390
+ for (const f of buildResult.overwriteFiles) {
391
+ Object.assign(overwriteMerged, f.config);
392
+ }
393
+ const overwriteContent = buildResult.overwriteFiles.length > 0 ? yaml.dump(overwriteMerged, dumpOpts) : '# overwrite 已禁用或无覆写文件\n';
394
+ fs.writeFileSync(PATHS.configStage2Overwrite, overwriteContent, { mode: 0o600 });
395
+
396
+ // 3. 系统覆盖值(BASE_CONFIG + TUN_CONFIG)
397
+ fs.writeFileSync(PATHS.configStage3System, yaml.dump(buildResult.systemConfig, dumpOpts), { mode: 0o600 });
398
+ }
399
+
357
400
  function hasConfig() {
358
401
  return fs.existsSync(PATHS.configFile);
359
402
  }
@@ -394,11 +437,11 @@ function resetUserData(options) {
394
437
 
395
438
  let itemsToRemove;
396
439
  if (kernelOnly) {
397
- itemsToRemove = [DIRS.core];
440
+ itemsToRemove = [DIRS.kernel];
398
441
  } else {
399
442
  itemsToRemove = [PATHS.settingsFile, DIRS.subscriptions, DIRS.logs, DIRS.data, DIRS.runtime];
400
443
  if (!keepKernel) {
401
- itemsToRemove.push(DIRS.core);
444
+ itemsToRemove.push(DIRS.kernel);
402
445
  }
403
446
  }
404
447
 
@@ -428,9 +471,7 @@ const DIRECTORY_TARGETS = {
428
471
  logs: { path: DIRS.logs, label: '日志目录' },
429
472
  data: { path: DIRS.data, label: 'mihomo 数据目录' },
430
473
  runtime: { path: DIRS.runtime, label: '运行时目录' },
431
- overwrites: { path: DIRS.overwrites, label: '覆写目录' },
432
- settings: { path: PATHS.settingsFile, label: '设置文件' },
433
- kernel: { path: DIRS.core, label: '内核目录' },
474
+ kernel: { path: DIRS.kernel, label: '内核目录' },
434
475
  };
435
476
 
436
477
  module.exports = {
@@ -463,6 +504,7 @@ module.exports = {
463
504
  parseYamlOrJson,
464
505
  buildConfig,
465
506
  writeMihomoConfig,
507
+ writeDebugConfig,
466
508
  hasConfig,
467
509
  getConfigInfo,
468
510
  resetUserData,
package/src/overwrite.js CHANGED
@@ -150,13 +150,6 @@ function deepMergeWithOverrides(target, override) {
150
150
  return result;
151
151
  }
152
152
 
153
- /**
154
- * 获取覆写目录路径
155
- */
156
- function getOverwritesDir() {
157
- return config.DIRS.overwrites;
158
- }
159
-
160
153
  /**
161
154
  * 检查覆写功能是否启用
162
155
  */
@@ -173,11 +166,12 @@ function setOverwriteEnabled(enabled) {
173
166
  }
174
167
 
175
168
  /**
176
- * 读取 overwrites 目录下的所有 yaml 文件
177
- * 按文件名排序返回
169
+ * 读取根目录下的覆写文件
170
+ * 匹配 overwrite.yaml 和 overwrite.*.yaml,按文件名排序
171
+ * overwrite.yaml 始终第一
178
172
  */
179
173
  function loadOverwriteFile() {
180
- const dir = getOverwritesDir();
174
+ const dir = config.USER_DATA_DIR;
181
175
 
182
176
  if (!fs.existsSync(dir)) {
183
177
  return [];
@@ -185,8 +179,12 @@ function loadOverwriteFile() {
185
179
 
186
180
  const files = fs
187
181
  .readdirSync(dir)
188
- .filter(f => f.endsWith('.yaml') || f.endsWith('.yml'))
189
- .sort();
182
+ .filter(f => f === 'overwrite.yaml' || /^overwrite\..+\.ya?ml$/.test(f))
183
+ .sort((a, b) => {
184
+ if (a === 'overwrite.yaml') return -1;
185
+ if (b === 'overwrite.yaml') return 1;
186
+ return a.localeCompare(b);
187
+ });
190
188
 
191
189
  const results = [];
192
190
 
@@ -239,11 +237,10 @@ function applyOverwrite(baseConfig) {
239
237
  function listOverwriteFile() {
240
238
  const files = loadOverwriteFile();
241
239
  const enabled = isOverwriteEnabled();
242
- const dir = getOverwritesDir();
243
240
 
244
241
  return {
245
242
  enabled,
246
- dir,
243
+ dir: config.USER_DATA_DIR,
247
244
  files: files.map(f => ({
248
245
  name: f.name,
249
246
  path: f.path,
@@ -257,4 +254,5 @@ module.exports = {
257
254
  setOverwriteEnabled,
258
255
  applyOverwrite,
259
256
  listOverwriteFile,
257
+ loadOverwriteFile,
260
258
  };
@@ -48,13 +48,19 @@ function formatProxySummary(info) {
48
48
  }
49
49
 
50
50
  /**
51
- * 获取当前默认订阅(从 index.js 移入)
51
+ * 获取当前使用的订阅
52
52
  */
53
53
  function getActiveSubscription() {
54
54
  const subs = config.getSubscriptions();
55
55
  if (subs.length === 0) {
56
56
  return null;
57
57
  }
58
+ const settings = config.readSettings();
59
+ const activeName = settings.active_subscription;
60
+ if (activeName) {
61
+ const found = subs.find(s => s.name === activeName);
62
+ if (found) return found;
63
+ }
58
64
  return subs[0];
59
65
  }
60
66
 
@@ -175,12 +181,13 @@ function prepareConfigForStart(mode, subName) {
175
181
  throw new Error('未找到订阅配置 "' + subName + '",请先添加订阅');
176
182
  }
177
183
 
178
- const mergedConfig = config.buildConfig(rawContent, mode);
179
- config.writeMihomoConfig(mergedConfig);
184
+ const buildResult = config.buildConfig(rawContent, mode);
185
+ config.writeMihomoConfig(buildResult.config);
186
+ config.writeDebugConfig(buildResult);
180
187
 
181
188
  return {
182
- proxies: mergedConfig.proxies ? mergedConfig.proxies.length : 0,
183
- proxyGroups: mergedConfig['proxy-groups'] ? mergedConfig['proxy-groups'].length : 0,
189
+ proxies: buildResult.config.proxies ? buildResult.config.proxies.length : 0,
190
+ proxyGroups: buildResult.config['proxy-groups'] ? buildResult.config['proxy-groups'].length : 0,
184
191
  };
185
192
  }
186
193