mihomo-cli 3.0.0 → 3.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.
Files changed (4) hide show
  1. package/CHANGELOG.md +109 -0
  2. package/README.md +65 -25
  3. package/dist/index.js +1911 -1403
  4. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,114 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.3.0] - 2026-08-15
4
+
5
+ ### 新增
6
+
7
+ - **覆写按 name 就地 patch 数组元素(`~key` 语法)** - 此前覆写数组只有整体替换(`key!`)、前置(`+key`)、追加(`key+`)三种语义,无法「只改数组里某一个元素的部分字段」。新增 `~key`:以 `name` 为主键匹配数组元素,命中则深度合并该元素、保留其余字段与其余元素,找不到则追加。典型用途:修改订阅下发的某个 `proxy-group` 的字段(如给 `select` 组注入 `default-selected` 改默认选中节点),保留原有全部节点、不动其它分组、订阅更新后依然生效。键名真以 `~` 开头时用 `<~key>` 转义
8
+ - **覆写作用域限定(`match` 块)** - 覆写文件顶部可加 `match:` 块,让该文件只对指定订阅生效(无 `match` 仍全局生效,向后兼容)。支持 `subscription`(按订阅名精确匹配)和 `url-domain`(按订阅 URL hostname 后缀匹配)两个条件,所列条件需全部满足(AND),条件值为数组时其内部为 OR。未知匹配键或无法评估的条件均 fail closed(跳过该文件并告警),避免误配置静默全局生效。`ow list` 与 `status` 会展示各文件的作用域
9
+
10
+ ### 修复
11
+
12
+ - **TUN 启动失败误报"密码错误或取消"** - 启动脚本失败时 `exit 1`,与 sudo 鉴权失败的退出码冲突,配置错误、端口占用等真实失败都被误报。脚本失败改用 `exit 2`,调用方据此区分;失败时同时清理 root 属主 pid 文件,避免后续 `start` 因残留死锁(旧提示只让 `pkill`,清不掉 pid 文件)
13
+ - **`reset subs` 半重置状态** - 只删订阅缓存/原始配置文件,settings 里的订阅列表保留,重置后 `start` 报"未找到订阅配置"。现同步清空订阅列表与 `active_subscription`
14
+ - **`sub add` 下载失败留下半成品订阅** - 订阅已写入并可能切为默认后下载才失败,之后 `start` 必然报错。现下载失败自动回滚(移除刚添加的订阅)
15
+ - **覆写文件顶层为数组时被当配置合并** - YAML 顶层数组能通过类型检查,被解构出数字键参与合并。现拒绝并告警
16
+ - **`sub web` 缺页面地址时覆盖已保存配置** - 回退路径重新下载订阅并落盘,副作用超出"打开页面"。现只取响应头,不写盘
17
+ - **`clean` 后订阅文件残留死节点规则** - 只清理了引用已删空分组的规则,直接引用死节点的规则留在文件里(靠构建时兜底)。现保存时一并清理
18
+ - **自动更新超时后全部计为失败** - 超时即丢弃已完成结果。现收齐已完成/已失败的更新再统计,超时只影响未完成的部分
19
+ - **`SUB-RULE` 规则被误删** - 配置校验按「末段为代理/分组名」检查规则目标,但 `SUB-RULE` 末段引用的是 sub-rule 名,被当作无效引用删除。现跳过此类规则的目标校验
20
+ - **空名覆写节点清空 include-all 分组** - 覆写注入 `name` 为空的节点时,exclude-filter 正则出现空分支(`a||b`)匹配所有节点,分组被清空。现过滤空/非字符串节点名
21
+ - **节点重命名未同步规则** - 保存时裁剪节点名只更新了 `proxies`/`proxy-groups`,规则里直接引用旧名会变悬空引用。现一并 remap `rules` 中的目标
22
+ - **`sub add` URL 校验过宽** - 仅判 `startsWith('http')`,`httpfoo://`、`http-evil` 等能通过。现用 `URL` 解析校验协议并 trim 首尾空白
23
+ - **`reset -f` 语义误导** - `-f` 是 `--full`(删全部)的别名,与常见 `-f=force` 直觉冲突,且未知标志(如拼错的 `--ful`)被静默忽略后走默认删除。现移除 `-f` 别名(删全部只认 `--full`,免确认统一 `-y/--yes`),未知标志一律报错退出
24
+ - **HTTP 下载无响应体大小上限** - 订阅/内核下载被劫持或故障返回超大响应时可能 OOM。现按 `Content-Length` 提前拒绝并流式读取,超 50MB 中止
25
+ - **`tar` 解压无路径穿越防护** - 恶意镜像可借含 `../`/绝对路径的归档条目写出目标目录之外。现解压前校验条目路径
26
+ - **内核进程创建失败处理** - 内核二进制不可执行时,`spawn` 的 error 事件无监听会冒泡为未捕获异常、`pid` 缺失会二次抛错。现监听 error 并在 pid 缺失时给出可读提示
27
+
28
+ ### 安全
29
+
30
+ - **错误信息遮蔽路径型订阅令牌** - `maskUrl` 原仅遮蔽 query/userinfo,形如 `/subscribe/<TOKEN>` 的路径令牌会原样出现在错误日志。现对疑似令牌的长路径段一并遮蔽
31
+ - **`ui` 命令不再明文回显 `controller_secret`** - 改为提示密钥已配置、见 `settings.json`,避免进入 shell 历史/日志
32
+
33
+ ### 变更
34
+
35
+ - **`allow-lan` 不再强制锁定** - 订阅/覆写显式提供时按其值(支持局域网设备连入代理端口的入站场景),未提供时默认 `false`
36
+ - **`sub` 列表改为纯只读** - 不再触发自动更新(更新是写操作),`sub add`/`use`/`update` 末尾的列表也不再顺带更新其他订阅。自动更新只在 `start` 与显式 `sub update` 时发生
37
+ - **启动自动清理加冷却** - 节点数超阈值时的自动测速清理改为同一订阅 12 小时内只跑一次(冷却记录在订阅缓存),新增 `--no-clean` 跳过;避免每次 `start` 都全量测速并可能二次重启
38
+ - **隐式停止不再弹 sudo** - `start`/`clean` 遇到 root 属主残留(TUN 实例等)时,不再由 `stop()` 内部 sudo 提权,改为报错引导手动清理或使用 `sub clean`(`stop` 命令本身的 sudo 提权保留)
39
+ - **可选 `controller_secret` 设置** - `settings.json` 设置后,external-controller 启用 Bearer 认证(系统锁定,订阅/覆写无法伪造),`ui` 命令会提示密钥;面向多用户环境
40
+ - **支持 `--flag=value` 形式** - `--timeout=3000`、`--mirror=url` 等与空格分隔形式等价
41
+ - **非 TTY 下测速不再逐节点刷屏** - 管道/重定向时只输出汇总行
42
+ - **`mihomo log` 的 Ctrl+C 不再打印"正在退出..."** - follow 场景这是常规退出
43
+ - **`status` 未运行时也显示模式** - 有配置文件时展示上次构建的 TUN/Mixed
44
+ - **`update` 权限失败给出 sudo 提示**
45
+ - **`test`/`clean` 与 `sub test`/`sub clean` 帮助区分** - 前者经运行中的主实例,后者用隔离实例、无需主实例运行
46
+ - **`status` TUN 模式端口显示** - 不再显示「未知」,改为「TUN 接管」并标注备用监听端口
47
+ - **`sub use`/`ow on`/`ow off` 触发重启时透传启动选项** - `-s`/`-t`/`-j`/`--no-clean` 等不再被丢弃
48
+ - **订阅「永久」到期显示** - 机场 `expire=0` 不再显示成 `1970-01-01`,改为「永久」
49
+ - **`curl` 未安装时明确提示** - 内核下载不再报「退出码 null」
50
+
51
+ ### 内部
52
+
53
+ - 抽出 `src/progress.ts`(进度条与结果格式化),解开 `commands/start` ↔ `commands/subscription` 的循环依赖
54
+ - 订阅缓存损坏时与 settings.json 一致:先备份再回退默认
55
+ - 合并订阅任一来源失败即取消其余下载,不再白等
56
+ - `applyOverwrite` 恒返回浅拷贝,避免构建时的锁定键删除污染订阅原始对象(debug stage1 失真)
57
+ - `isGithubUrl` 对多 URL 合并订阅要求全部来源为 GitHub 才按 GitHub 策略
58
+ - `--mirror` 默认镜像收敛为 `DEFAULT_MIRROR` 常量(与可用镜像列表首项一致)
59
+ - `buildConfig(subRawContent, mode, scope?)` 新增可选订阅作用域参数(`{ subName, subUrl }`),由 `prepareConfigForStart` 组装传入;作用域过滤在 `buildConfig` 顶部统一执行一次,确保被排除的覆写文件不会污染 `exclude-filter` 与 debug 输出
60
+ - `match` 元数据键在 `loadOverwriteFile` 阶段即抽成结构化字段并从 config 剥离,保证它永不进入最终 mihomo 配置
61
+
62
+ ## [3.2.0] - 2026-07-19
63
+
64
+ ### 修复
65
+
66
+ - **测速隔离实例被主进程管理误杀/误判** - `getMihomoPids`(原 `getAllMihomoPids`)此前用 `pgrep -f <内核路径>` 匹配,会连带命中 `sub test`/`sub clean` 启动的、跑同一内核但 `-f` 指向 `test/runtime/config.yaml` 的隔离实例。导致主实例运行时另开终端测速,`stop`/`start` 会误杀测速实例、或把它误判为残留而拒绝启动。改用「内核路径 + 主 configFile」双段正则精确匹配主实例(三种启动方式命令行均含这两段),隔离实例与仅用编辑器打开配置的进程都不再命中
67
+ - **未捕获异常时测速实例泄漏** - `uncaughtException` / `unhandledRejection` / `main().catch` 退出前未执行清理,测速期间崩溃会残留端口 27890 的实例。现三处退出前均调用 `runCleanup()`(此前仅 SIGINT/SIGTERM 有)
68
+ - **TUN 启动脚本路径未安全转义** - 生成的 sudo bash 脚本用双引号直接拼接内核/配置路径,`MIHOMO_CLI_DIR` 含 `"`/`$`/反引号时存在本地注入面。改用单引号字面量转义(`shellQuote`,与 daemon 脚本同一范式)
69
+ - **带 `no-resolve` 的规则在启动时被误删** - `validateConfig` 校验规则时取逗号分隔的末段当目标,`IP-CIDR,1.1.1.1/32,DIRECT,no-resolve` 这类带 `no-resolve` 修饰后缀的规则,其末段是修饰词而非目标,会被当作"引用不存在目标"静默移除(机场订阅中很常见)。现提取 `getRuleTarget()`:末段为 `no-resolve` 时取倒数第二段;`clean` 的规则清理同步改用
70
+ - **`clean` 误删带 `include-all`/`use` 的分组** - `cleanDeadProxies` 只按 `proxies` 清空判定删组,未像 `validateConfig` 那样检查其他节点来源;`{include-all: true, proxies: []}` 这类分组(或引用节点恰好全死但有 `include-all` 兜底)会被从订阅文件里持久删除。补上一致的 `hasOtherSource` 检查
71
+ - **Mixed 模式未清理订阅残留的 `tun` 字段** - 订阅/覆写自带 `tun.enable: true` 时,`start`(Mixed)会以 TUN 静默启动,保活(限定 Mixed)也可能带 tun 配置被 launchd 拉起。现 Mixed 模式显式丢弃订阅侧的 tun 字段(TUN 模式仍由系统 `TUN_CONFIG` 强制覆盖)
72
+ - **`sub add` 同名订阅被静默覆盖** - 两次不带名称的 `sub add` 会让第二个直接替换 `default`,原订阅 URL 无提示丢失。现同名即报错,提示换名或先删除
73
+ - **内核下载可能选中 compatible 版**(Intel Mac)- `-compatible` 变体同样满足"版本号尾缀"判定且字母序靠前,会被优先当作标准版下载(性能低于标准版)。现显式排除,仅在无标准版时回退
74
+ - **订阅名路径穿越**(低危)- 原始配置路径直接拼接订阅名,手改 `settings.json` 塞入 `../` 可让读/写/删越出 subscriptions 目录。现统一校验名称合法性;`sub remove` 对非法名跳过文件清理、仍可正常从列表移除
75
+
76
+ ### 变更
77
+
78
+ - **新增 `runtime.ts` 运行时门面** - 收敛「普通进程(pidFile) vs 保活(launchd 托管)」双轨差异:运行模式判定、运行状态/PID、启停重启统一为三个函数。命令层(`start`/`status`/`sub use`/`ow`/`clean`)不再各自 `if (isDaemonEnabled())` 分支,消除重复与不一致(此前 `clean` 两分支输出已分叉)
79
+ - **命令路由改为注册表驱动** - 新增 `commands/registry.ts`,以数据表描述命令的名称/别名/handler/argv 改写;`index.ts` 从表分发(消除 ~110 行手写 switch,模块加载时校验别名无冲突)。帮助文本的命令清单由各命令的 `usage` 生成(单一真相源),修复此前手写 `help` 与实际命令脱节的问题,并补上「快捷命令」映射说明
80
+ - **保活模式日志不再无限增长** - daemon 常驻时不经 `process.start`,日志轮转/归档清理从不触发。现 `restartDaemon` 检测日志超 10MB 时跳过热重载、改走 sudo kickstart 路径顺便 copy-truncate 轮转(daemon 日志为 root 属主,用户态无法 truncate;运行中 rename 会让 launchd 的日志 fd 继续写进归档文件,只能 copy-truncate),并顺带清理 7 天前归档
81
+ - **`sub update` 后提示重启生效** - 运行中的实例仍使用旧配置,更新完成后提示执行 `mihomo start`
82
+ - **`kernel` 命令输出精简** - 不再每次打印整段镜像用法;仅直连失败时才提示 `--mirror`/`--mirror-all` 与可用镜像列表
83
+
84
+ ### 内部
85
+
86
+ - 提取共用工具消除重复:`escapeRegExp`、`shellQuote`(utils)、`dumpYaml`(config,合并 4 处相同 YAML 序列化选项)
87
+ - `external-controller` 地址统一为常量 `CONTROLLER_ADDR`(constants),供配置生成、测速探测、热重载共用;删除 daemon 中因地址恒定而永不触发的运行时端口解析
88
+ - `HttpClient.get<T>()` 泛型化,json 模式直接返回目标类型,去掉调用点的 `as unknown as` 强转
89
+ - 覆写文件名判定提取为 `isOverwriteFilename`(overwrite),reset 复用
90
+ - 归档日志时间戳从 UTC 改为本地时间(与 `logs` 列表展示的 mtime 时区一致),提取 `formatLocalTimestamp`(utils)
91
+ - 常量收敛:`CONTROLLER_BASE_URL` 入 constants(daemon 热重载与测速探测共用,删除重复的地址构造);`DAEMON_BOOT_WAIT_MS` 合并两处重复的 launchd 等待定义;`cleanupOldLogs` 导出供 daemon 复用
92
+ - 文档同步:CLAUDE.md 架构表补上 `lifecycle.ts`;`allow-lan` 强制 false 标注为有意安全默认(防覆写误开入站代理);README 安全章节说明 controller 仅监听回环、无鉴权的适用边界
93
+
94
+ ---
95
+
96
+ ## [3.1.0] - 2026-07-19
97
+
98
+ ### 修复
99
+
100
+ - **保活模式下经局域网跳板的代理连不通**(v3.0.0 引入的严重 bug)- 用户级 LaunchAgent 启动的内核受 macOS 15+ 本地网络隐私(TCC)限制,访问**局域网其他设备**被静默拦截(报 `no route to host`),导致经局域网 socks5 跳板转发的内网流量在 `daemon on` 后全部失效、`daemon off` 后立刻恢复。手动在系统设置授权对裸命令行二进制无效。改为 **root 级 LaunchDaemon** 彻底解决(系统上下文不受该限制)
101
+
102
+ ### 变更
103
+
104
+ - **保活迁移到系统级 LaunchDaemon** - plist 位于 `/Library/LaunchDaemons/`(`root:wheel`),以 root 运行;`daemon on` / `daemon off` 需输入一次管理员密码(复用 TUN 模式的交互式 sudo 范式,一次密码完成全部操作)
105
+ - **配置变更优先热重载(免密)** - `sub use` / `ow on|off` / `clean` 等触发的重启优先经 external-controller `PUT /configs` 热重载(走 localhost、无需 sudo),失败才回退到需密码的 `launchctl kickstart`
106
+ - **`daemon status` / `status` 免密** - 保活状态查询改用 `pgrep` + root 属主过滤判定运行状态,不再调用需 sudo 的 `launchctl print`
107
+ - **关闭保活时归还文件属主** - `daemon off` 会把 root 守护进程创建的日志、数据文件 `chown` 回当前用户,避免后续非保活模式 `start` 因 root 属主日志无法写入而失败
108
+ - `daemon on/off` 在非交互终端(无 TTY,如 CI)会明确报错而非挂起
109
+
110
+ ---
111
+
3
112
  ## [3.0.0] - 2026-07-19
4
113
 
5
114
  ### 新增
package/README.md CHANGED
@@ -8,10 +8,10 @@
8
8
  - 🔄 **自动更新** - 启动时自动检查并更新过期订阅
9
9
  - 🔍 **模糊匹配** - `sub use` / `sub web` 支持订阅名称模糊匹配
10
10
  - 🧹 **节点测速清理** - `test` 快速测试、`clean` 清理并重启;`sub test/clean` 独立进程测试任意订阅
11
- - 📝 **覆写配置** - 在订阅基础上进行自定义覆写,支持强制覆盖、数组合并
11
+ - 📝 **覆写配置** - 在订阅基础上进行自定义覆写,支持强制覆盖、数组合并、按 name 就地 patch、按订阅限定作用域
12
12
  - 🔄 **智能重启** - `sub use` 切换订阅、`ow on/off` 切换覆写后自动重启
13
13
  - 🚀 **进程管理** - 启动/停止/切换模式,自动清理残留进程
14
- - 🛡️ **进程保活** - 基于 launchd,崩溃/开机自动拉起,代理后台常驻(`daemon on`)
14
+ - 🛡️ **进程保活** - 基于 launchd(root),崩溃/开机自动拉起,代理后台常驻(`daemon on`)
15
15
  - 🔄 **双模式支持** - Mixed 模式和 TUN 透明代理模式
16
16
  - 📊 **状态监控** - 查看运行状态、内存占用
17
17
  - 📝 **日志管理** - 实时日志 + 历史日志归档(自动轮转,保留7天)
@@ -84,7 +84,7 @@ mihomo ui yacd # YACD
84
84
 
85
85
  | 命令 | 说明 |
86
86
  | --------------------------- | ---------------------------------------------------------------------------- |
87
- | `mihomo start [tun\|mixed]` | 启动/重启/切换代理模式(`-s` 跳过更新,`-u` 更新超时,`-r` 清理轮次,`-t` 超时,`-j` 并发) |
87
+ | `mihomo start [tun\|mixed]` | 启动/重启/切换代理模式(`-s` 跳过更新,`-u` 更新超时,`-r` 清理轮次,`-t` 超时,`-j` 并发,`--no-clean` 跳过启动自动清理) |
88
88
  | `mihomo stop` | 停止代理 |
89
89
  | `mihomo status` | 查看运行状态 |
90
90
  | `mihomo log` | 实时查看日志 (`-o` 用系统编辑器打开) |
@@ -97,15 +97,15 @@ mihomo ui yacd # YACD
97
97
  | ----------------------------- | -------------------------------------- |
98
98
  | `mihomo sub` | 列出所有订阅(含流量、到期时间) |
99
99
  | `mihomo sub use <name>` | 切换当前订阅(支持模糊匹配,自动重启) |
100
- | `mihomo sub add <url> [name]` | 添加订阅并自动切换(支持逗号分隔多 URL 合并) |
100
+ | `mihomo sub add <url> [name]` | 添加订阅并自动切换(支持逗号分隔多 URL 合并,名称不可重复) |
101
101
  | `mihomo sub update` | 更新所有订阅 |
102
102
  | `mihomo sub update <name>` | 更新指定订阅(支持模糊匹配) |
103
103
  | `mihomo sub remove <name>` | 删除订阅(支持模糊匹配) |
104
104
  | `mihomo sub web [name]` | 打开订阅页面(无参打开默认) |
105
- | `mihomo sub test [name]` | 测试节点连通性(`-t` 超时,`-j` 并发) |
106
- | `mihomo sub clean [name]` | 测速并清理失败节点(`-r` 轮数,默认2)|
107
- | `mihomo test` | 快速测试当前节点连通性(`-t` 超时,`-j` 并发) |
108
- | `mihomo clean` | 清理失败节点并自动重启(`-t` 超时,`-j` 并发,`-r` 轮数) |
105
+ | `mihomo sub test [name]` | 测试节点连通性(独立隔离实例,无需运行主实例,`-t` 超时,`-j` 并发) |
106
+ | `mihomo sub clean [name]` | 测速并清理失败节点(独立实例,不动主实例,`-r` 轮数,默认2)|
107
+ | `mihomo test` | 测试当前节点(经运行中的主实例,`-t` 超时,`-j` 并发) |
108
+ | `mihomo clean` | 清理失败节点并重启(经主实例,`-t` 超时,`-j` 并发,`-r` 轮数) |
109
109
 
110
110
  ### 覆写配置
111
111
 
@@ -120,12 +120,12 @@ mihomo ui yacd # YACD
120
120
  | 命令 | 说明 |
121
121
  | --------------------------------- | ------------------------------------------------------------------- |
122
122
  | `mihomo kernel [--mirror [镜像]]` | 更新内核(默认直连,`--mirror` 使用镜像) |
123
- | `mihomo daemon [on\|off\|status]` | 进程保活:开机自启 + 崩溃自动重启(仅 Mixed 模式) |
123
+ | `mihomo daemon [on\|off\|status]` | 进程保活:开机自启 + 崩溃自动重启(仅 Mixed 模式,on/off 需管理员密码) |
124
124
  | `mihomo update` | 更新 mihomo-cli (npm install -g) |
125
125
  | `mihomo ui [zash\|dash\|yacd]` | 打开 Web UI |
126
126
  | `mihomo dir` | 显示数据目录位置 |
127
127
  | `mihomo dir open [target]` | 打开指定目录(`root`, `subs`, `logs`, `kernel` 等) |
128
- | `mihomo reset [目标...] [--full]` | 重置用户数据(可用目标:`subs`, `logs`, `kernel`, `overwrites` 等) |
128
+ | `mihomo reset [目标...] [--full] [-y]` | 重置用户数据(可用目标:`subs`, `logs`, `kernel`, `overwrites` 等;`--full` 删全部,`-y` 跳过确认) |
129
129
  | `mihomo version` | 显示版本信息 |
130
130
  | `mihomo help` | 显示帮助信息 |
131
131
 
@@ -150,6 +150,7 @@ mihomo ui yacd # YACD
150
150
  | `mihomo use <name>` | `mihomo sub use <name>` |
151
151
  | `mihomo on` / `off` | `mihomo ow on` / `ow off` |
152
152
  | `mihomo open <target>` | `mihomo dir open <target>` |
153
+ | `mihomo upd` / `upgrade` | `mihomo update` |
153
154
 
154
155
  ## 模式说明
155
156
 
@@ -170,24 +171,26 @@ mihomo ui yacd # YACD
170
171
  默认情况下,mihomo 内核在后台独立运行,但如果内核崩溃、被系统 kill(如内存不足)、或重启/重新登录后,代理就会失效且不会自动恢复。进程保活用 macOS 原生的 **launchd** 解决这个问题。
171
172
 
172
173
  ```bash
173
- mihomo daemon on # 开启保活
174
- mihomo daemon off # 关闭保活并停止代理
175
- mihomo daemon status # 查看保活状态
174
+ mihomo daemon on # 开启保活(需管理员密码)
175
+ mihomo daemon off # 关闭保活并停止代理(需管理员密码)
176
+ mihomo daemon status # 查看保活状态(无需密码)
176
177
  ```
177
178
 
178
179
  ### 原理
179
180
 
180
- - 基于用户级 **LaunchAgent**(`~/Library/LaunchAgents/`),装载/卸载**无需 sudo**
181
+ - 基于系统级 **LaunchDaemon**(`/Library/LaunchDaemons/`,以 root 运行),`daemon on/off` 需输入一次管理员密码
181
182
  - **`KeepAlive`** - 内核崩溃或被杀后由 launchd 自动拉起(约 10 秒节流后重启)
182
- - **`RunAtLoad`** - 登录/开机后自动启动,无需手动 `start`
183
+ - **`RunAtLoad`** - 开机后自动启动,无需手动 `start`
183
184
  - 常驻的是系统 launchd 进程本身,**不额外占用系统资源、无轮询**
184
185
 
186
+ > **为什么用 root 级 LaunchDaemon**:早期版本用用户级 LaunchAgent(免密),但 macOS 15+ 的本地网络隐私限制会静默拦截其对**局域网其他设备**的访问(经局域网跳板的代理会连不通,报 `no route to host`)。系统级 root 守护进程不受此限制,是唯一可靠方案。
187
+
185
188
  ### 注意事项
186
189
 
187
190
  - **仅支持 Mixed 模式**。保活开启时执行 `start tun` 会被拦截,需先 `daemon off`
188
191
  - 保活开启后,`mihomo stop` 不再直接停止(会被自动拉起),请用 `mihomo daemon off`
189
- - 切换订阅、覆写开关、清理节点后的重启会自动生效(内部走 `launchctl kickstart`)
190
- - 保活模式下日志持续追加到 `mihomo.log`(不触发启动时的日志轮转)
192
+ - 切换订阅、覆写开关、清理节点后的重启**优先走内核热重载(免密)**,失败才回退到需密码的 `launchctl kickstart`
193
+ - 保活模式下日志持续追加到 `mihomo.log`;超过 10MB 时,下次配置变更触发的重启会走 kickstart(需密码)并顺便轮转归档,不再无限增长
191
194
 
192
195
  ## 内核更新镜像
193
196
 
@@ -220,10 +223,17 @@ mihomo kernel --mirror-all hk.gh-proxy.org
220
223
  ## 订阅自动更新
221
224
 
222
225
  - 默认更新间隔:GitHub 订阅 6 小时,其他订阅 12 小时(订阅服务端可通过 `profile-update-interval` 覆盖)
223
- - 触发时机:`start` 命令、`sub list` 命令
226
+ - 触发时机:`start` 命令(`sub` 列表为纯只读,不再触发更新)
224
227
  - 更新失败时继续使用本地缓存,不影响使用
225
228
  - 自动更新默认超时 10 秒,可通过 `-u <ms>` 调整;使用 `-s` 可完全跳过自动更新
226
229
 
230
+ ## 启动自动清理
231
+
232
+ 节点数超过阈值(GitHub 订阅 50、其他 100)时,`start` 会自动测速清理死节点:
233
+
234
+ - 同一订阅 12 小时内只自动清理一次(冷却记录在订阅缓存),避免每次启动都全量测速
235
+ - `--no-clean` 可跳过;随时可用 `mihomo clean` / `mihomo sub clean` 手动清理
236
+
227
237
  ## 数据目录
228
238
 
229
239
  用户数据存储位置(与安装位置分离,更新不丢失):
@@ -265,12 +275,24 @@ mihomo kernel --mirror-all hk.gh-proxy.org
265
275
 
266
276
  覆写配置支持以下特殊操作符:
267
277
 
268
- | 语法 | 作用 | 示例 |
269
- | -------- | ------------------------------ | -------------------- |
270
- | `key!` | 强制覆盖整个对象(不深度合并) | `dns!`: { ... } |
271
- | `+key` | 数组前置插入 | `+proxies`: [...] |
272
- | `key+` | 数组追加 | `rules+`: [...] |
273
- | `<+key>` | 键名以 `+` 开头时转义 | `<+.google.cn>`: ... |
278
+ | 语法 | 作用 | 示例 |
279
+ | -------- | ----------------------------------------- | -------------------- |
280
+ | `key!` | 强制覆盖整个对象(不深度合并) | `dns!`: { ... } |
281
+ | `+key` | 数组前置插入 | `+proxies`: [...] |
282
+ | `key+` | 数组追加 | `rules+`: [...] |
283
+ | `~key` | `name` 就地合并数组中的单个元素 | `~proxy-groups`: [...] |
284
+ | `<+key>` | 键名以 `+`/`~` 等符号开头时转义 | `<+.google.cn>`: ... |
285
+
286
+ `~key` 用于**只修改数组里某一个元素的部分字段**,而不动其余元素、也不必复制整个元素。以 `name` 为主键匹配:命中同名元素则深度合并该元素,找不到则追加。典型用途:修改订阅下发的某个 `proxy-group` 的字段(如默认选中的节点),订阅更新后依然生效。
287
+
288
+ ### 作用域限定(match)
289
+
290
+ 在覆写文件顶部加 `match:` 块,可让该文件**只对指定订阅生效**(无 `match` 则全局生效)。所列条件需全部满足(AND),条件值为数组时其内部为 OR:
291
+
292
+ | 匹配键 | 作用 |
293
+ | ------------- | ----------------------------- |
294
+ | `subscription` | 按订阅名精确匹配 |
295
+ | `url-domain` | 按订阅 URL 的 hostname 后缀匹配 |
274
296
 
275
297
  ### 示例
276
298
 
@@ -289,6 +311,19 @@ rules+:
289
311
  - 'DOMAIN-SUFFIX,example.com,DIRECT'
290
312
  ```
291
313
 
314
+ ```yaml
315
+ # ~/.mihomo-cli/overwrite.edu1.yaml
316
+ # 只对 edu1 订阅生效:把订阅下发的 Developer 分组默认选中改为 TW Fixed IP
317
+ match:
318
+ subscription: edu1 # 或 url-domain: glados-config.com
319
+
320
+ ~proxy-groups:
321
+ - name: Developer
322
+ default-selected: TW Fixed IP
323
+ ```
324
+
325
+ > 注:`default-selected` 由 mihomo 内核决定默认选中项,优先级低于 `store-selected` 缓存的历史选择。若之前手动选过、且开启了 `store-selected`,需 `mihomo reset data` 清缓存后才能看到默认值接管。
326
+
292
327
  ## Web UI
293
328
 
294
329
  内置三个常用 Web UI:
@@ -328,11 +363,16 @@ sudo pkill -9 mihomo
328
363
 
329
364
  ## 安全特性
330
365
 
331
- - **URL 脱敏**:订阅 URL 中的 token、key、password 等敏感参数自动替换为 `***`
366
+ - **URL 脱敏**:订阅 URL 中的 token、key、password 等敏感参数(含 query、userinfo 及路径型令牌)自动替换为 `***`
332
367
  - **文件权限**:配置文件使用 `0o600` 权限(仅所有者可读可写),目录使用 `0o700` 权限
368
+ - **入站默认关闭**:订阅/覆写未指定时 `allow-lan` 默认 `false`;如需局域网设备连入代理端口,可在订阅或覆写中显式开启
333
369
  - **信号处理**:优雅处理 SIGINT/SIGTERM 信号
334
370
  - **异常捕获**:全局 uncaughtException 和 unhandledRejection 处理
335
371
 
372
+ > **注意**:外部控制器(`127.0.0.1:9090`)默认无鉴权,与 Clash 系工具惯例一致。它仅监听本机回环、局域网不可达;但本机其他进程(含浏览器中的网页)可访问它,请勿在不可信的多用户环境使用。
373
+ >
374
+ > 多用户环境可在 `settings.json` 中设置 `controller_secret`(写入配置后随启动生效,`ui` 命令会提示密钥),为控制器 API 加上 Bearer 认证;密钥由系统锁定,订阅/覆写无法伪造。
375
+
336
376
  ## 许可证
337
377
 
338
378
  MIT License - 详见 [LICENSE](LICENSE) 文件。