mihomo-cli 3.6.0 → 3.8.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 +75 -0
  2. package/README.md +115 -17
  3. package/dist/index.js +1131 -176
  4. package/package.json +5 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,80 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.8.0] - 2026-09-01
4
+
5
+ ### 新增
6
+
7
+ - **ssh 隧道出口(`tunnel` 子命令,别名 `ssh`)** - 管理 `ssh -D` 动态转发进程的生命周期,把可 ssh 登录的机器变成本地 SOCKS5 出口。分流仍交给覆写机制,本功能补的是「隧道断了 mihomo 不知情、会一直往死端口送流量」这一环
8
+
9
+ ```bash
10
+ mihomo tunnel add work --host m4 --port 1080
11
+ mihomo tunnel up|down|status [名字]
12
+ mihomo tunnel rm <名字>
13
+ ```
14
+
15
+ - **随 `start` 一并拉起**:默认带 `auto` 标记,`start` 顺带启动、`stop` 连带停止(`--no-tunnel` 跳过,`add --no-auto` 不参与)
16
+ - **隧道失败不影响内核启动**:只影响内网分流那部分规则,故仅打印显眼的黄色警告并附上 ssh 给出的原因,其余流量照常
17
+ - **`stop` 只停自己起的**:手动 `tunnel up` 起的隧道带 `manual` 标记,不会被 `mihomo stop` 带走,避免下次 start 又起一个而累积僵尸进程
18
+ - **`status` 真实探测端口**:能识别「进程还在但转发已死」的假活——那正是最误导的形态,此时 mihomo 仍在往死端口送流量
19
+ - **起之前先检测端口占用**,不盲启后失败
20
+ - **`add` 生成覆写模板** `overwrite.tunnel-<名字>.yaml`(已建好 socks5 节点与 select 分组,分流规则留白待填),生成后完全由用户维护,CLI 不再改写,`tunnel rm` 也不删它
21
+ - 新增 `reset tunnel` 目标(先停进程再删运行态,反序会导致 ssh 进程失联且再也停不掉)
22
+
23
+ 安全边界:`-D` 恒绑 `127.0.0.1` 且不提供绑定地址开关(绑 `0.0.0.0` 会让同一 WiFi 下任何设备经本机进内网);`--host` 拒绝 `-` 开头的值(`-oProxyCommand=...` 等同任意命令执行);ssh 参数固定带 `ExitOnForwardFailure`/`BatchMode`/`ConnectTimeout`/`ServerAlive*`
24
+
25
+ 暂不做自动重连保活:断线依靠 `ServerAliveInterval` 让进程自退,再用 `tunnel status` 查出来
26
+
27
+ ### 修复
28
+
29
+ - **`reset --full` 会留下空的 `settings.json`** - 新增的 `tunnel` 目标其 `onAfter` 会 `writeSettings` 清空隧道列表,若排在 `settings` 之后就会把刚删掉的文件重建成 `{}`,与「已重置: 设置」矛盾。现移到 `settings` 之前(与 `subs` 同理),并加单测锁定该顺序约束——这是 v3.7.0 已修过一次的同类问题(当时是参数顺序),换个目标又复发了
30
+
31
+ ## [3.7.0] - 2026-08-22
32
+
33
+ ### 修复
34
+
35
+ - **错误响应体绕过大小上限,可致 OOM** - `!response.ok` 分支直接 `await response.json()`,不经流式大小检查。实测 60MB 错误体使客户端 RSS 增长 303MB——攻击者只需返回非 2xx 即可绕过 50MB 防护。现错误体限量 64KB 读取(仅用于诊断),修后 RSS 增长 9MB
36
+ - **机场返回错误 JSON 会覆盖磁盘上可用的订阅** - 下载后只要内容能解析成对象就原子覆盖写盘。机场返回 `{"error":"quota exceeded"}` 之类响应时报「已更新 (0 节点)」,把原本可用的订阅**不可恢复地覆盖**,随后 mihomo 带零节点启动导致断网;且这在 `start` 的自动更新路径上,用户无操作即触发。现写盘前要求 `proxies`/`proxy-groups`/`proxy-providers` 至少其一非空,并提取服务端错误信息作为提示
37
+ - **`ps` 输出截断致测速实例泄漏并占用端口** - `isProcessCommandMatching` 未带 `-ww`,BSD/macOS 的 `ps` 即使 stdout 非终端也把 command 列截断到 79 列。测速实例的匹配串(`test/runtime/config.yaml`)起始偏移随用户名增长(`alice` 为 80、`jonathan.smith` 为 98),常见家目录下均越界 → 匹配恒失败 → `stopTestInstance` 跳过 SIGKILL 却仍删 pid 文件,内核残留占着 27890/29090 且再无记录,下次 `sub test` 直接启动失败
38
+ - **`sub add` 失败时劫持当前活跃订阅** - `setDefaultSubscription` 在下载之前执行,回滚时 `removeSubscription` 把活跃订阅落到列表首项而非用户原选择。复现:活跃为 `work`、列表为 `[airport-a, work]`,添加一个不可达 URL 失败后活跃变成 `airport-a`,下次 `start` 静默连错机场。现切换移到下载成功之后
39
+ - **`MIHOMO_CLI_DAEMON_LABEL` 未校验导致 root 任意路径写** - 该值经 `path.join` 拼成 plist 路径后,是 `sudo install -m 644 -o root` 的写入目标与 `sudo rm -f` 的删除目标,而 `path.join` 会折叠 `..`(`../../etc/sudoers.d/evil` → `/etc/sudoers.d/evil.plist`),内容还部分可控。现加字符集校验:非法值回退默认标签,并在 `enableDaemon`/`disableDaemon` 入口报错
40
+ - **覆写注入节点的 `exclude-filter` 误排除同前缀节点** - mihomo 的 `exclude-filter` 是无锚点正则搜索。注入名为 `HK` 的节点后,订阅里的 `HK-01`/`HK-02` 全被踢出 `include-all` 分组。现改为 `^(?:...)$` 整名锚定
41
+ - **覆写 YAML 笔误抛裸 `TypeError` 并打印堆栈** - `validateConfig` 的类型断言无校验,四种常见笔误会崩溃:`proxies` 含空列表项、`rules` 漏写 `-` 成标量、`proxy-groups` 写成映射、`rules` 含非字符串。用户配置错误被当成程序 bug。现全部转为带修正提示的 `CliError`
42
+ - **`~key`/`+key` 作用于非数组时静默损坏配置** - 静默包成单元素数组:`~dns: {enable: true}` 把映射 `dns` 变成 `[{enable: true}]` 并丢掉原有字段,而 mihomo 要求 `dns` 是映射;`log-level+: debug` → `["debug"]` 同理。现报错并提示改用 `key!` 或直接写 `key`;目标不存在时仍放行(新增数组的正常用法)
43
+ - **`maskUrl` 逗号切分致 token 明文泄漏** - 无条件按逗号切分,`?nodes=us,hk&token=SECRET` 被劈开后两段都识别不出 token 参数,密钥明文输出。同一根因还让 query 含逗号的合法单 URL(`?flag=clash,meta`)被误判多源、`sub add` 报「无效的 URL」而无法添加。现统一判据为「切分后每段都是合法 http(s) URL 且不止一段」
44
+ - **`settings.json` 为合法 JSON 但非对象时绕过损坏恢复** - `null`/`[]`/`123`/`"hi"` 都直接进缓存,不备份不告警:`null` 让 `getSubscriptions()` 抛裸 `TypeError` 且缓存判定恒失效,字符串被展开成 `{"0":"h","1":"i",...}`。现一并走备份+回退分支
45
+ - **`subscriptions` 非数组时被按字符展开** - 手改成 `"oops"` 后 `addSubscription` 写出 `["o","o","p","s",{...}]` 且不报错。现在唯一读取入口 `getSubscriptions()` 收口校验,并滤掉缺 name/url 的残缺条目
46
+ - **`reset` 忽略停止进程的结果** - root 实例(TUN)下走 `sudo pkill`,用户取消密码时失败的 pid 被静默丢弃,仍继续删除数据,留下孤儿 root 进程跑在已删配置上。现删数据前复查并中止
47
+ - **`reset --full` 残留含密钥的备份文件** - `settings.json.bak`(损坏恢复时生成)带 `controller_secret` 与订阅 token 明文留下,与「已重置: 设置」矛盾。现纳入删除路径
48
+ - **`reset` 结果依赖参数顺序** - `subs` 目标的后置钩子会重建 `settings.json`,故 `reset settings subs` 留下 `{}` 而 `reset subs settings` 才真删。现按注册表顺序执行
49
+ - **`match` 的订阅名匹配与 `sub use` 口径不一致** - `sub use home` 能切到订阅 `Home`(模糊匹配大小写不敏感),但 `match: {subscription: home}` 精确比对匹配不上。现统一为大小写不敏感
50
+ - **`parseIntArg` 接受危险值** - 无范围校验:`-j 0` 让测速起 0 个 worker、结果全空洞、被报成「所有节点失败」(伪造结果);`-t 5s` 静默取 5ms 让全部节点超时。现非正整数一律报错,并把 `test`/`clean` 的参数校验移到运行状态检查之前
51
+ - **合并订阅的错误指向被连带取消的 URL** - 任一 URL 失败即中断其余请求,按顺序取第一个错误报出的往往是被取消的那条,真正的 403/token 过期被隐藏。现优先报非取消类错误
52
+ - **`dir` 子命令的错误绕过统一渲染** - `cmdDirectory` 用 `void` 丢弃 async 分发的 Promise,`dir open <未知目标>` 抛的错误退化成「未处理的 Promise 拒绝」,丢掉标签颜色与可用目标列表
53
+ - **`ow`/`dir` 未知子命令静默回落** - `ow onn` 静默打印列表且退出码 0(对比 `sub adz` 会报错并给纠错建议)。现补齐纠错提示
54
+ - **测速实例的 pid 记录时机存在泄漏窗口** - spawn 与写 pid 文件之间被 Ctrl+C 中断时,只认 pid 文件的清理逻辑会漏掉 detached 子进程。现增加内存记录作为第二来源
55
+ - **`reset` 保活取消路径退出码为 0** - `console.error` + `return` 使「重置中止」被脚本误判成功,且绕过统一渲染
56
+ - **`mihomo on`/`off` 丢弃启动选项** - 唯二不透传后续参数的快捷命令,`mihomo on -s` 静默吞掉 `-s`,而 README 声明其与 `ow on` 等价
57
+ - **`restartDaemon` 抛裸 `Error`** - 最后一处未迁移的数据层预期错误
58
+
59
+ ### 新增
60
+
61
+ - **平台守卫** - `package.json` 声明 `"os": ["darwin"]`,`main()` 开头校验平台(豁免 `help`/`version`,`MIHOMO_CLI_ALLOW_ANY_PLATFORM=1` 为开发逃生阀)。此前非 macOS 上是「部分成功」:`status`/`sub` 看着正常,`daemon on` 输完 root 密码才撞 `/Library/LaunchDaemons`,`ui` 报告成功却什么都没打开(`open` 命令缺失被吞掉,且 Debian 的 `open` 指向 `run-mailcap` 会把 URL 当附件处理)。守卫先于目录创建,避免在不支持的平台留下数据目录
62
+ - **`sub remove` 模糊匹配需确认** - 精确名称直接删除;模糊命中时展示完整名称并要求确认(`-y`/`--yes` 跳过)。此前 `sub remove air` 会无提示删掉 `production-airport`
63
+ - **`subs` 别名** - 与 `directory` 的 `dirs` 对称,落地命名规范的「简写复数」档
64
+ - **GitHub Actions CI** - 在 `macos-latest` 上跑 typecheck / lint / test / build
65
+ - **`prepublishOnly` 钩子** - `dist/` 被 gitignore 且此前无发布钩子,漏跑构建即发布陈旧或缺失产物
66
+
67
+ ### 变更
68
+
69
+ - **破坏性操作在非交互环境报错而非静默取消** - `reset`(无 `-y`)与 `sub remove`(模糊匹配)在管道/CI 下此前打印「已取消」并退出 0,脚本会误判操作已完成。现报错退出 1 并提示加 `-y` 或用完整名称
70
+ - **`confirmPrompt` 收敛到 `commands/shared.ts`** 并增加 TTY 守卫(此前在非交互环境会挂住等输入)
71
+
72
+ ### 内部
73
+
74
+ - **单测从 55 增至 101** - 新增覆盖:配置形态校验、`exclude-filter` 锚定、覆写数组语义误用、`match` 大小写、`parseIntArg` 边界、多源 URL 逗号判据
75
+ - **`CODE_REVIEW.md` 重写** - 上一轮(v2.9.x 基线)的「仍待处理」清单已逐项复核:#12/#14 实际已修复,#10 的后果比原描述严重(是 token 泄漏而非仅显示切碎),#17 的建议不可行——上游 v1.19.30 的 127 个资产中零 checksum 文件,故真实缺口是下载地址的 host 未钉死。新增 9 项待处理(tar symlink 致任意文件 chmod 755、热重载信任任意 9090 响应、`Subscription-Userinfo` 边界等)
76
+ - **`README.md` / `CLAUDE.md` 校正** - 平台说明从「Windows / Linux 正在适配中」改为「仅支持 macOS」(此前无对应代码);覆写实为默认启用(此前文档教用户先 `ow on`);`log -o` 是系统默认程序而非编辑器;补齐 `logs current`、长选项、`reset`/`dir open` 完整目标列表、分阶段调试文件;新增「选项写法」「数据保护」两节
77
+
3
78
  ## [3.6.0] - 2026-08-15
4
79
 
5
80
  ### 修复
package/README.md CHANGED
@@ -1,17 +1,20 @@
1
1
  # mihomo-cli
2
2
 
3
- 一个基于命令行的 mihomo (Clash.Meta) 客户端,专为 macOS 设计。Windows / Linux 正在适配中,敬请期待。
3
+ 一个基于命令行的 mihomo (Clash.Meta) 客户端,**仅支持 macOS**。
4
+
5
+ 进程保活依赖 launchd、目录/UI 打开依赖 `open`、提权依赖 `sudo`,均无其他平台实现,故在非 macOS 上会直接报错退出而非部分可用。Windows / Linux 适配尚无时间表。
4
6
 
5
7
  ## 功能特性
6
8
 
7
9
  - 🌐 **订阅管理** - 添加/更新订阅,支持流量统计和到期时间显示
8
10
  - 🔄 **自动更新** - 启动时自动检查并更新过期订阅
9
- - 🔍 **模糊匹配** - `sub use` / `sub web` 支持订阅名称模糊匹配
11
+ - 🔍 **模糊匹配** - `sub use` / `web` / `update` / `remove` / `test` / `clean` 均支持订阅名称模糊匹配(大小写不敏感)
10
12
  - 🧹 **节点测速清理** - `test` 快速测试、`clean` 清理并重启;`sub test/clean` 独立进程测试任意订阅
11
13
  - 📝 **覆写配置** - 在订阅基础上进行自定义覆写,支持强制覆盖、数组合并、按 name 就地 patch、按订阅限定作用域
12
14
  - 🔄 **智能重启** - `sub use` 切换订阅、`ow on/off` 切换覆写后自动重启
13
15
  - 🚀 **进程管理** - 启动/停止/切换模式,自动清理残留进程
14
16
  - 🛡️ **进程保活** - 基于 launchd(root),崩溃/开机自动拉起,代理后台常驻(`daemon on`)
17
+ - 🔌 **ssh 隧道出口** - 管理 `ssh -D` 进程生命周期,把内网机器变成本地 SOCKS5 出口,随 `start` 一并拉起
15
18
  - 🔄 **双模式支持** - Mixed 模式和 TUN 透明代理模式
16
19
  - 📊 **状态监控** - 查看运行状态、内存占用、订阅流量与到期时间
17
20
  - 📝 **日志管理** - 实时日志 + 历史日志归档(自动轮转,保留7天)
@@ -88,8 +91,10 @@ mihomo ui yacd # YACD
88
91
  | `mihomo start [tun\|mixed]` | 启动/重启/切换代理模式(`-s` 跳过更新,`-u` 更新超时,`-r` 清理轮次,`-t` 超时,`-j` 并发,`--no-clean` 跳过启动自动清理) |
89
92
  | `mihomo stop` | 停止代理 |
90
93
  | `mihomo status` | 查看运行状态(含订阅流量、到期时间) |
91
- | `mihomo log` | 实时查看日志 (`-o` 用系统编辑器打开) |
94
+ | `mihomo log` | 实时查看日志 (`-o` 用系统默认程序打开) |
92
95
  | `mihomo logs` | 列出所有日志(当前 + 历史归档) |
96
+ | `mihomo logs current` | 查看当前日志(等同 `logs 0`) |
97
+ | `mihomo logs <名称/子串>` | 按文件名或子串查看指定归档日志 |
93
98
  | `mihomo logs <编号>` | 查看指定日志(`0`=当前日志,`1+`=归档日志,支持 `-n N` 指定行数、`-o` 打开) |
94
99
 
95
100
  ### 订阅管理
@@ -101,10 +106,10 @@ mihomo ui yacd # YACD
101
106
  | `mihomo sub add <url> [name]` | 添加订阅并自动切换(支持逗号分隔多 URL 合并,名称不可重复) |
102
107
  | `mihomo sub update` | 更新所有订阅 |
103
108
  | `mihomo sub update <name>` | 更新指定订阅(支持模糊匹配) |
104
- | `mihomo sub remove <name>` | 删除订阅(支持模糊匹配) |
105
- | `mihomo sub web [name]` | 打开订阅页面(无参打开默认) |
109
+ | `mihomo sub remove <name>` | 删除订阅(别名 `rm`/`delete`;精确名直接删,模糊匹配需确认,`-y` 跳过) |
110
+ | `mihomo sub web [name]` | 打开订阅页面(别名 `open`,无参打开默认,支持模糊匹配) |
106
111
  | `mihomo sub test [name]` | 测试节点连通性(独立隔离实例,无需运行主实例,`-t` 超时,`-j` 并发) |
107
- | `mihomo sub clean [name]` | 测速并清理失败节点(独立实例,不动主实例,`-r` 轮数,默认2)|
112
+ | `mihomo sub clean [name]` | 测速并清理失败节点(独立实例,不动主实例,`-r` 轮数默认 2,`-t` 超时,`-j` 并发) |
108
113
  | `mihomo test` | 测试当前节点(经运行中的主实例,`-t` 超时,`-j` 并发) |
109
114
  | `mihomo clean` | 清理失败节点并重启(经主实例,`-t` 超时,`-j` 并发,`-r` 轮数) |
110
115
 
@@ -112,9 +117,9 @@ mihomo ui yacd # YACD
112
117
 
113
118
  | 命令 | 说明 |
114
119
  | ------------------------------ | -------------------------- |
115
- | `mihomo ow` / `mihomo ow list` | 查看覆写配置状态和文件列表 |
116
- | `mihomo ow on` | 启用覆写配置(自动重启) |
117
- | `mihomo ow off` | 禁用覆写配置(自动重启) |
120
+ | `mihomo ow` / `mihomo ow list` | 查看覆写配置状态和文件列表(别名 `enable`/`disable` 亦可用于开关) |
121
+ | `mihomo ow on` | 启用覆写配置(**默认已启用**,自动重启) |
122
+ | `mihomo ow off` | 禁用覆写配置(自动重启) |
118
123
 
119
124
  ### 其他命令
120
125
 
@@ -122,11 +127,16 @@ mihomo ui yacd # YACD
122
127
  | --------------------------------- | ------------------------------------------------------------------- |
123
128
  | `mihomo kernel [--mirror [镜像]]` | 更新内核(默认直连,`--mirror` 使用镜像;更新后运行中实例需重启生效) |
124
129
  | `mihomo daemon [on\|off\|status]` | 进程保活:开机自启 + 崩溃自动重启(仅 Mixed 模式,on/off 需管理员密码) |
130
+ | `mihomo tunnel` | 列出 ssh 隧道及真实状态(别名 `ssh`/`tunnels`) |
131
+ | `mihomo tunnel add <名字> --host <主机> --port <端口> [--no-auto]` | 添加隧道并生成覆写模板(默认随 start 拉起) |
132
+ | `mihomo tunnel up\|down [名字]` | 启动/停止隧道(无参即全部) |
133
+ | `mihomo tunnel status [名字]` | 查看隧道状态(真实探测端口,能识别「假活」) |
134
+ | `mihomo tunnel rm <名字> [-y]` | 删除隧道(不删覆写文件) |
125
135
  | `mihomo update` | 更新 mihomo-cli(先查 npm 最新版,已是最新则跳过重装) |
126
136
  | `mihomo ui [zash\|dash\|yacd]` | 打开 Web UI |
127
137
  | `mihomo dir` | 显示数据目录位置 |
128
- | `mihomo dir open [target]` | 打开指定目录(`root`, `subs`, `logs`, `kernel` 等) |
129
- | `mihomo reset [目标...] [--full] [-y]` | 重置用户数据(可用目标:`subs`, `logs`, `kernel`, `overwrites` 等;`--full` 删全部,`-y` 跳过确认) |
138
+ | `mihomo dir open [target]` | 打开指定目录(`root`, `subs`, `logs`, `data`, `runtime`, `kernel`) |
139
+ | `mihomo reset [目标...] [--full] [-y]` | 重置用户数据(可用目标:`subs`, `logs`, `data`, `runtime`, `settings`, `kernel`, `overwrites`, `daemon`, `tunnel`;`--full` 删全部,`-y` 跳过确认) |
130
140
  | `mihomo version` | 显示版本信息 |
131
141
  | `mihomo help` | 显示帮助信息 |
132
142
 
@@ -139,6 +149,8 @@ mihomo ui yacd # YACD
139
149
  - `mhm`
140
150
  - `mh`
141
151
 
152
+ 子命令组亦有别名:`subscription` = `sub`/`subs`/`subscriptions`,`directory` = `dir`/`dirs`/`directories`,`overwrite` = `ow`,`tunnel` = `ssh`/`tunnels`(`tun` 已被 TUN 模式占用)
153
+
142
154
  ### 快捷命令
143
155
 
144
156
  常用操作的快捷方式:
@@ -167,6 +179,54 @@ mihomo ui yacd # YACD
167
179
  - 需要 sudo / 管理员权限
168
180
  - 首次使用会自动配置 DNS 和路由
169
181
 
182
+ ## ssh 隧道出口
183
+
184
+ 把一台可 ssh 登录的机器(如公司内网的机器)变成本地 SOCKS5 出口,配合覆写规则即可只让内网域名走它,其余流量照常走订阅节点。本功能负责的是 **ssh 进程的生命周期管理**——隧道断了 mihomo 并不知情,会一直往死端口送流量。
185
+
186
+ ```bash
187
+ mihomo tunnel add work --host m4 --port 1080 # m4 是 ~/.ssh/config 里的别名
188
+ mihomo tunnel up work # 启动
189
+ mihomo tunnel status # 查看状态(真实探测端口)
190
+ mihomo tunnel down work # 停止
191
+ ```
192
+
193
+ `add` 会生成一份覆写模板 `overwrite.tunnel-<名字>.yaml`,其中已建好 socks5 节点与分组,
194
+ **分流规则留白待你填写**(CLI 无从知道你的内网域名):
195
+
196
+ ```yaml
197
+ # 取消注释并改成你的内网域名/网段
198
+ +rules:
199
+ - DOMAIN-SUFFIX,example.internal,Tunnel-work
200
+ - IP-CIDR,10.0.0.0/8,Tunnel-work
201
+ ```
202
+
203
+ 该文件生成后**完全由你维护**,CLI 不会再改写;`tunnel rm` 也不会删它。改完执行 `mihomo start` 生效。
204
+
205
+ ### 与 start / stop 的联动
206
+
207
+ 隧道默认带 `auto` 标记,`mihomo start` 会顺带拉起、`mihomo stop` 会连带停止(`--no-tunnel` 可跳过;`add` 时加 `--no-auto` 则不参与)。
208
+
209
+ - **隧道起不来不会让 `start` 失败**——它只影响内网分流那部分规则,其余流量正常,但会打印显眼的黄色警告并附上 ssh 给出的原因
210
+ - **`stop` 只停自己起的**:手动 `tunnel up` 起的隧道不会被 `mihomo stop` 带走,避免下次 start 又起一个而累积僵尸进程
211
+
212
+ ### 状态的三种形态
213
+
214
+ `tunnel status` 会**真实探测端口是否在监听**,而不是只看进程在不在:
215
+
216
+ | 状态 | 含义 |
217
+ | --- | --- |
218
+ | 运行中 | 进程在且端口在监听,可正常使用 |
219
+ | **假活** | 进程还在但端口不通——最需要警惕的形态,此时 mihomo 仍在往死端口送流量 |
220
+ | 未运行 | 进程不在 |
221
+
222
+ ### 安全边界
223
+
224
+ - `-D` **恒绑 `127.0.0.1`**,不提供绑定地址开关:绑 `0.0.0.0` 会让同一 WiFi 下任何设备都能经本机进入内网
225
+ - ssh 参数固定带 `ExitOnForwardFailure`/`BatchMode`/`ConnectTimeout`/`ServerAlive*`,分别防「假活」、无 TTY 挂死、久等、断线后端口成僵尸
226
+ - `--host` 拒绝以 `-` 开头的值(`-oProxyCommand=...` 会被 ssh 当选项解析,等同任意命令执行)
227
+
228
+ > 暂不做自动重连保活。断线依靠 `ServerAliveInterval` 让 ssh 进程自行退出,再用 `tunnel status` 查出来。
229
+
170
230
  ## 进程保活
171
231
 
172
232
  默认情况下,mihomo 内核在后台独立运行,但如果内核崩溃、被系统 kill(如内存不足)、或重启/重新登录后,代理就会失效且不会自动恢复。进程保活用 macOS 原生的 **launchd** 解决这个问题。
@@ -235,6 +295,35 @@ mihomo kernel --mirror-all hk.gh-proxy.org
235
295
  - 同一订阅 12 小时内只自动清理一次(冷却记录在订阅缓存),避免每次启动都全量测速
236
296
  - `--no-clean` 可跳过;随时可用 `mihomo clean` / `mihomo sub clean` 手动清理
237
297
 
298
+ ## 选项写法
299
+
300
+ 带值选项支持三种等价写法,长短选项对应关系:
301
+
302
+ | 短 | 长 | 用途 | 默认 |
303
+ | --- | --- | --- | --- |
304
+ | `-t` | `--timeout` | 测速超时(ms) | 2000 |
305
+ | `-j` | `--concurrency` | 测速并发数 | 100 |
306
+ | `-r` | `--rounds` | 清理时失败节点重试轮数 | 2 |
307
+ | `-u` | `--update-timeout` | 启动时自动更新订阅超时(ms) | 10000 |
308
+ | `-n` | `--lines` | 日志显示行数 | 100 |
309
+
310
+ ```bash
311
+ mihomo test -t 3000 # 短选项 + 空格
312
+ mihomo test --timeout 3000 # 长选项 + 空格
313
+ mihomo test --timeout=3000 # 长选项 + 等号
314
+ ```
315
+
316
+ 布尔开关:`-s`(跳过订阅更新)、`--no-update`、`--no-clean`、`-y`/`--yes`(跳过确认)、`-o`(用系统默认程序打开)。
317
+
318
+ 上述数值选项只接受 **>= 1 的整数**,非法值(`0`、负数、`5s`、`abc`)会直接报错而非静默取默认值——避免 `-j 0` 之类静默产出"全部节点失败"的假结果。
319
+
320
+ ## 数据保护
321
+
322
+ - **订阅内容校验**:下载到的内容必须含 `proxies` / `proxy-groups` / `proxy-providers` 之一才写盘。机场返回配额或错误 JSON(如 `{"error":"quota exceeded"}`)时报错并**保留磁盘上原有的可用配置**,不会被覆盖
323
+ - **`sub add` 失败回滚**:下载失败时移除半成品订阅,且不改动当前活跃订阅
324
+ - **`settings.json` 损坏恢复**:格式损坏(含合法 JSON 但非对象的情况)时自动备份为 `.bak` 并回退默认设置
325
+ - **`reset` 停止确认**:需要停止进程的重置会先确认进程真的已终止,未能停止时中止重置而非留下孤儿进程跑在已删配置上
326
+
238
327
  ## 数据目录
239
328
 
240
329
  用户数据存储位置(与安装位置分离,更新不丢失):
@@ -244,6 +333,7 @@ mihomo kernel --mirror-all hk.gh-proxy.org
244
333
  ├── settings.json # 用户设置(订阅列表等)
245
334
  ├── overwrite.yaml # 覆写配置(主文件,可选)
246
335
  ├── overwrite.*.yaml # 覆写配置(扩展文件,如 overwrite.dns.yaml)
336
+ ├── overwrite.tunnel-*.yaml # 隧道覆写(首次由 tunnel add 生成,此后由你维护)
247
337
  ├── subscriptions/
248
338
  │ ├── cache.json # 订阅动态缓存(更新时间、流量、到期时间等)
249
339
  │ └── <name>.yaml # 订阅原始配置
@@ -251,11 +341,17 @@ mihomo kernel --mirror-all hk.gh-proxy.org
251
341
  │ └── mihomo # mihomo 内核二进制
252
342
  ├── logs/
253
343
  │ ├── mihomo.log # 当前日志
254
- └── mihomo.YYYY-MM-DD_HH-MM-SS.log # 归档日志
344
+ ├── mihomo.YYYY-MM-DD_HH-MM-SS.log # 归档日志
345
+ │ └── tunnel-<name>.log # 隧道 ssh 输出(每次启动覆写)
255
346
  ├── data/ # mihomo 运行数据(GeoIP 等,由内核自行管理)
347
+ ├── tunnel/ # 隧道运行态(stop 不清除,故不放在 runtime/)
348
+ │ └── <name>.json # PID、谁启动的、启动时间
256
349
  └── runtime/ # 运行时临时文件(stop 自动清除)
257
350
  ├── pid # 进程 PID
258
- └── config.yaml # 运行时生成的配置
351
+ ├── config.yaml # 运行时生成的配置
352
+ ├── 1.subscription.yaml # 分阶段调试:订阅原始配置
353
+ ├── 2.overwrite.yaml # 分阶段调试:应用覆写后
354
+ └── 3.system.yaml # 分阶段调试:合并系统配置后
259
355
  ```
260
356
 
261
357
  可通过环境变量 `MIHOMO_CLI_DIR` 自定义数据目录位置。
@@ -270,7 +366,7 @@ mihomo kernel --mirror-all hk.gh-proxy.org
270
366
  - `overwrite.yaml` — 主覆写文件
271
367
  - `overwrite.dns.yaml` — 按功能拆分的扩展文件(`overwrite.*.yaml` 格式)
272
368
  2. `overwrite.yaml` 始终最先加载,扩展文件按文件名排序加载
273
- 3. 使用 `mihomo ow on` 启用覆写配置(会自动重启)
369
+ 3. 覆写**默认即启用**,放好文件后重启生效(`mihomo start`);如曾 `ow off` 禁用过,用 `mihomo ow on` 重新启用(会自动重启)
274
370
 
275
371
  ### 特殊语法
276
372
 
@@ -286,14 +382,16 @@ mihomo kernel --mirror-all hk.gh-proxy.org
286
382
 
287
383
  `~key` 用于**只修改数组里某一个元素的部分字段**,而不动其余元素、也不必复制整个元素。以 `name` 为主键匹配:命中同名元素则深度合并该元素,找不到则追加。典型用途:修改订阅下发的某个 `proxy-group` 的字段(如默认选中的节点),订阅更新后依然生效。
288
384
 
385
+ > `~key` / `+key` / `key+` 都是**数组语义**:若目标键已存在且不是数组(如 `~dns` 作用于映射、`log-level+` 作用于字符串),会直接报错而非静默包成单元素数组——后者会丢掉原有字段并生成 mihomo 无法解析的配置。要覆盖非数组值请用 `key!`(强制覆盖)或直接写 `key`(深度合并)。
386
+
289
387
  ### 作用域限定(match)
290
388
 
291
389
  在覆写文件顶部加 `match:` 块,可让该文件**只对指定订阅生效**(无 `match` 则全局生效)。所列条件需全部满足(AND),条件值为数组时其内部为 OR:
292
390
 
293
391
  | 匹配键 | 作用 |
294
392
  | ------------- | ----------------------------- |
295
- | `subscription` | 按订阅名精确匹配 |
296
- | `url-domain` | 按订阅 URL 的 hostname 后缀匹配 |
393
+ | `subscription` | 按订阅名匹配(大小写不敏感,与 `sub use` 口径一致) |
394
+ | `url-domain` | 按订阅 URL 的 hostname 后缀匹配(大小写不敏感) |
297
395
 
298
396
  ### 示例
299
397
 
@@ -364,7 +462,7 @@ sudo pkill -9 mihomo
364
462
 
365
463
  ## 安全特性
366
464
 
367
- - **URL 脱敏**:订阅 URL 中的 token、key、password 等敏感参数(含 query、userinfo 及路径型令牌)自动替换为 `***`
465
+ - **URL 脱敏**:订阅 URL 中的 token、key、password 等敏感参数(含 query、userinfo 及路径型令牌)自动替换为 `***`。逗号分隔的多源订阅逐段脱敏;query 内含逗号的单条 URL 不会被误拆(否则参数被劈开会导致 token 漏脱敏)
368
466
  - **文件权限**:配置文件使用 `0o600` 权限(仅所有者可读可写),目录使用 `0o700` 权限
369
467
  - **入站默认关闭**:订阅/覆写未指定时 `allow-lan` 默认 `false`;如需局域网设备连入代理端口,可在订阅或覆写中显式开启
370
468
  - **信号处理**:优雅处理 SIGINT/SIGTERM 信号