feat: refine install flow, TUN experience, and command UX

This commit is contained in:
Arvin
2026-04-12 18:24:35 +08:00
parent b11b3bffef
commit 77b454d6b6
11 changed files with 352 additions and 264 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ export CLASH_DNS_PORT="1053"
export CLASH_AUTO_UPDATE_SUBSCRIPTIONS="true"
# 版本
export MIHOMO_VERSION="v1.19.21"
export MIHOMO_VERSION="v1.19.23"
export CLASH_VERSION="v1.18.0"
export YQ_VERSION="v4.52.4"
export SUBCONVERTER_VERSION="v0.9.0"
+194 -142
View File
@@ -1,25 +1,28 @@
<h1 align="center">
🐧 Linux 一键安装 Clash</a>
🐱 Linux 一键安装 Clash</a>
<br>
</h1>
<h3 align="center">
一个面向 Linux 的 Clash(<a href="https://github.com/MetaCubeX/mihomo">Mihomo</a>)管理工具。
</h3>
<p align="center">
<img src="docs/assets/5.png" width="100%">
<img src="resources/ui.png" width="100%">
</p>
# ✨ 核心特性
- 🚀 **自动识别系统架构**,自动下载并使用对应 Clash 内核
- 🧪 **端口自动检测与分配**,避免冲突
- 🔄 **多订阅管理(clashctl)**,支持自动订阅切换
- 🧠 **Mixin 机制**,可按需追加/覆盖 Clash 配置
- 🌐 **Tun 模式支持**(需 Clash Meta / Premium)
- 🔐 **安全默认配置**,自动生成或自定义 Secret
- 🩺 **内置诊断工具(`doctor`)**,快速排障
- 🚀 **自动识别系统架构:**自动下载并使用对应 Clash 内核
- 🧪 **端口自动检测与分配:**避免冲突
- 🔄 **多订阅管理:**可以保存多个订阅,通过 `clashctl use` 切换当前主订阅。
- 💫 **节点选择:**使用编号交互选择策略组和节点。
- 🌐 **Tun 模式:**用于透明代理接管场景。
- 🧠 **Mixin 机制:**可按需追加/覆盖 Clash 配置
- 👤 **不同权限:**兼容 `root` 与普通用户环境。
- 🔐 **安全默认配置:**自动生成或自定义 Secret
- 🩺 **内置诊断工具(`doctor`):**快速排障
### 适用场景
@@ -46,28 +49,29 @@ bash install.sh
## ⌨️ 命令一览
```bash
用法:
clashctl 命令 [选项]
〽️ 常用命令
clashon 🚀 开启代理
clashoff ⛔ 关闭代理
clashctl select 💫 选择节点
🕹️ 控制台
clashui 🕹️ 查看 Web 控制台
clashsecret 🔑 查看或设置 Web 密钥
📦 订阅
clashctl add ➕ 添加订阅
clashctl use 💱 切换订阅
clashctl ls 📡 查看订阅列表
📌 高级
clashctl tun 🧪 Tun 模式管理
clashctl mixin 🧩 Mixin 配置管理
clashctl sub 🧩 订阅高级管理(启用 / 禁用 / 重命名 / 删除)
clashctl upgrade 🚀 升级当前或指定内核
clashctl update 🔄 更新项目代码
📜 日志
clashctl doctor 🩺 诊断面板
clashctl log/logs 📜 查看日志
指令:
on 开启代理
off 关闭代理
start 启动 Clash
stop 停止 Clash
restart 重启并自动应用当前配置
status 查看当前状态
update 更新到最新版本并自动应用配置
mode 查看当前运行模式(systemd/script/none)
ui 输出 Dashboard 地址
secret 输出当前 secret
doctor 健康检查
logs [-f] [-n 100] 查看日志
sub show|update 查看订阅地址 / 输入或更新订阅并立即生效
tun status|on|off 查看/启用/关闭 Tun
mixin status|on|off 查看/启用/关闭 Mixin
选项:
-h, --help 显示帮助信息
💡 显示更多帮助命令:clashctl -h
💡 更多高级能力:clashctl help advanced
```
------
@@ -77,24 +81,27 @@ bash install.sh
```bash
$ clashui
╔═══════════════════════════════════════════════╗
║ 🐱 Web 控制台 ║
║ 🐱 Web 控制台 ║
║═══════════════════════════════════════════════║
║ ║
║ 🔓 注意放行端口:9090 ║
║ 🏠 内网:http://192.168.0.1:9090/ui ║
║ 🌏 公网:http://8.8.8.8:9090/ui ║
║ ☁️ 公共:http://board.zash.run.place ║
║ 🔓 注意放行端口:9090 ║
║ 📶 状态:可访问 ║
║ 🏠 内网:http://192.168.0.1:9090/ui ║
║ ☁️ 公共:http://board.zash.run.place ║
║ 🌏 公网:http://8.8.8.8:9090/ui ║
║ 🔑 密钥:dada289edb457b59 ║
║ ║
╚═══════════════════════════════════════════════╝
$ clashctl secret mysecret
$ clashsecret mysecret
🐱 密钥更新成功,已重启生效
$ clashctl secret
$ clashsecret
🐱 当前密钥:mysecret
```
- 可通过浏览器打开 `Web` 控制台进行可视化操作,例如切换节点、查看日志等。
- `clashctl secret` 作为兼容入口继续保留,日常优先使用 `clashsecret`。
- 默认使用 [zashboard](https://github.com/Zephyruso/zashboard) 作为控制台前端,如需更换可自行配置。
- 若需将控制台暴露到公网,建议定期更换访问密钥,或通过 `SSH` 端口转发方式进行安全访问。
@@ -103,111 +110,188 @@ $ clashctl secret
## 🧰 常用管理命令
统一管理入口(推荐使用):
```
clashctl status
clashctl start
clashctl restart
clashctl update
clashctl set-url "https://example.com/your-subscribe"
```
### 多订阅管理
```
clashctl sub add office "https://example.com/office"
clashctl sub add personal "https://example.com/personal"
clashctl add <订阅链接> <名称>
clashctl use
clashctl ls
clashctl sub
clashctl sub list
clashctl sub use personal
clashctl sub update
clashctl sub log
clashctl sub enable <名称>
clashctl sub disable <名称>
clashctl sub rename <旧名称> <新名称>
clashctl sub remove <名称>
```
------
## 🏗️ 架构设计
## 🏗️ 架构设计架构简述
```text
.env / 订阅
↓
generate(配置生成)
↓
runtime/config.yaml(运行态)
↓
Clash 内核运行(systemd / script)
↓
status / doctor(状态观测)
项目当前可以按三层理解:
### Control
用户入口层。
- `clashctl`
- `clashon`
- `clashoff`
- `status`
- `doctor`
- `ui`
- `select`
Control 层负责把常用动作收口成可理解的命令和反馈。
### Build
配置生成层。
- 多订阅保存
- 单 active 主订阅
- active-only 编译链
- 订阅下载 / 转换 / 校验
- `config/mixin.yaml` 运行补丁
- 输出 `runtime/config.yaml`
当前规则很明确:`generate_config` 只处理当前 active 主订阅。
### Runtime
实际运行层。
- `runtime/` 是唯一运行时容器
- 后端可以是 `systemd`、`systemd-user` 或 `script`
- 运行配置是 `runtime/config.yaml`
- 日志、状态、缓存和运行时产物都收敛到 runtime 体系
## 配置说明
### `.env`
`.env` 用于覆盖安装和运行参数。常用项包括:
```bash
KERNEL_TYPE=mihomo
MIXED_PORT=7890
EXTERNAL_CONTROLLER=0.0.0.0:9090
CLASH_CONTROLLER_SECRET=your-secret
CLASH_SUBSCRIPTION_URL=https://example.com/sub
MIHOMO_VERSION=latest
CLASH_VERSION=latest
YQ_VERSION=v4.44.3
SUBCONVERTER_VERSION=v0.9.0
MIHOMO_DOWNLOAD_BASE=https://github.com/MetaCubeX/mihomo/releases/download
CLASH_DOWNLOAD_BASE=https://github.com/WindSpiritSR/clash/releases/download
```
核心原则:
按需设置即可,不需要每项都写。
- 配置生成 ≠ 运行
- 运行环境隔离
- 状态必须可观测
### `config/mixin.yaml`
用于对最终运行配置做补丁:
- `override` 覆盖字段
- `prepend` 把数组项放到原始配置前面
- `append` 把数组项放到原始配置后面
查看当前模板:
```bash
clashctl mixin
```
编辑:
```bash
clashctl mixin edit
```
查看最终运行配置:
```bash
clashctl mixin runtime
```
###
------
## 🔄 配置修改与更新
## 🔄 更新
### 修改 Clash 配置并重启
```
vim runtime/config.yaml
clashctl restart
```
> `restart` 不会更新订阅
### 更新订阅
```
```bash
clashctl update
clashctl upgrade
clashctl upgrade mihomo
clashctl upgrade clash
```
或指定订阅:
```
clashctl sub update personal
```
`update` 用于更新项目代码与运行依赖。`upgrade` 用于升级当前或指定代理内核。
------
## 🧩 Mixin 配置(可选)
用于追加或覆盖 Clash 配置。
- 默认读取:`config/mixin.d/*.yaml`(按文件名排序)
- 也可在 `.env` 中指定:
## 🧩 Mixin 配置
```bash
clashctl mixin
clashctl mixin edit
clashctl mixin raw
clashctl mixin runtime
```
export CLASH_MIXIN_DIR='config/mixin.d'
export CLASH_MIXIN_PATHS='config/mixin.d/base.yaml,config/mixin.d/rules.yaml'
Mixin 是运行配置补丁,不是订阅管理。它通过 `config/mixin.yaml` 对当前 active 订阅生成的运行配置执行:
- `override`
- `prepend`
- `append`
示例:
```yaml
override:
dns:
enable: true
prepend:
proxies: []
proxy-groups: []
rules:
- DOMAIN-SUFFIX,example.com,DIRECT
append:
proxies: []
proxy-groups: []
rules:
- MATCH,节点选择
```
编辑后执行:
```bash
clashctl mixin edit
```
它会重新生成配置;如果代理正在运行,会自动重启应用。
------
## 🌐 Tun 模式(可选)
## 🌐 Tun 模式
需 Clash Meta / Premium 支持,在 `.env` 中配置:
```
export CLASH_TUN_ENABLE=true
export CLASH_TUN_STACK=system
export CLASH_TUN_AUTO_ROUTE=true
export CLASH_TUN_AUTO_REDIRECT=false
export CLASH_TUN_STRICT_ROUTE=false
export CLASH_TUN_DNS_HIJACK='any:53'
```bash
clashctl tun on
clashctl tun off
clashctl tun doctor
clashctl tun logs
```
------
Tun 用于透明接管链路。`tun on` 是动作反馈,展示当前关键配置和简短状态;完整证据请看:
## ⛔ 停止服务
```bash
clashctl tun doctor
```
```
clashctl stop
proxy_off
```
Tun 判断不会简单把 `root` 等同于拥有 `CAP_NET_ADMIN`,也不会把 main table 默认路由未切换直接等同于 Tun 未生效。诊断会结合运行后端、容器环境、进程能力、Tun adapter、policy routing、路由表和日志证据。
------
@@ -217,38 +301,6 @@ proxy_off
bash uninstall.sh
```
## subconverter 多架构支持
`subconverter` 用于将订阅内容转换为标准 clash 配置。默认会尝试以下位置:
- `tools/subconverter/subconverter`
- `tools/subconverter/subconverter-<arch>`
- `tools/subconverter/bin/subconverter-<arch>`
其中 `<arch>` 取值为:
- `linux-amd64`
- `linux-arm64`
- `linux-armv7`
自动下载默认使用 `https://github.com/tindy2013/subconverter/releases/latest/download/subconverter_{arch}.tar.gz`,
如果需要自定义来源或关闭下载,可以设置:
- `SUBCONVERTER_PATH`:指定自定义 `subconverter` 可执行文件路径。
- `SUBCONVERTER_AUTO_DOWNLOAD=false`:关闭自动下载(默认会尝试自动下载,需 `curl`/`wget`)。
- `SUBCONVERTER_DOWNLOAD_URL_TEMPLATE`:下载模板,使用 `{arch}` 占位符,如:
```bash
export SUBCONVERTER_AUTO_DOWNLOAD=true
export SUBCONVERTER_DOWNLOAD_URL_TEMPLATE='https://example.com/subconverter_{arch}.tar.gz'
```
当 `subconverter` 不可用时会自动跳过转换,并提示警告。
<br>
## 设置代理
1. 开启 IP 转发
+1 -1
View File
@@ -47,7 +47,7 @@ if [ -n "$(subscription_url 2>/dev/null || true)" ]; then
write_runtime_value "INSTALL_VERIFY_CONTROLLER_READY" "false"
echo
echo "🔴 安装未完成:订阅编译失败"
echo "❗ 安装未完成:订阅编译失败"
if [ -n "$(read_build_value "BUILD_LAST_ERROR_SUMMARY" 2>/dev/null || true)" ]; then
echo "❌ 原因:$(read_build_value "BUILD_LAST_ERROR_SUMMARY" 2>/dev/null || true)"
fi
Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 364 KiB

+4 -4
View File
@@ -154,10 +154,10 @@ _clash_alias_run_on() {
_clash_alias_prepare_on || return $?
echo "🟢 正在开启代理..."
echo "🐱 正在开启代理..."
on_output="$(mktemp "${TMPDIR:-/tmp}/clashon.XXXXXX")" || {
echo "🔴 开启代理失败:无法创建临时输出文件" >&2
echo "❗ 开启代理失败:无法创建临时输出文件" >&2
return 1
}
@@ -188,7 +188,7 @@ _clash_alias_run_on() {
sed 's/^/ /' "$on_output" >&2
fi
else
echo "🔴 开启代理失败(底层返回码:$on_rc)" >&2
echo "❗ 开启代理失败(底层返回码:$on_rc)" >&2
if [ -s "$on_output" ]; then
sed 's/^/ /' "$on_output" >&2
else
@@ -205,7 +205,7 @@ _clash_alias_run_on() {
_clash_alias_after_on || {
on_rc=$?
echo "🔴 开启代理失败:当前 Shell 代理环境同步失败(返回码:$on_rc)" >&2
echo "❗ 开启代理失败:当前 Shell 代理环境同步失败(返回码:$on_rc)" >&2
return "$on_rc"
}
}
+102 -100
View File
@@ -32,8 +32,8 @@ Usage:
ls 📡 查看订阅列表
🕹️ Control:
ui 🕹️ 查看 Web 控制台
secret 🔑 查看或设置 Web 密钥
clashui 🕹️ 查看 Web 控制台
clashsecret 🔑 查看或设置 Web 密钥
🩺 Diagnose:
doctor 🩺 诊断环境与运行状态
@@ -215,7 +215,7 @@ print_on_feedback() {
next_action="$(system_state_default_action 2>/dev/null || echo 'clashctl status')"
echo
echo "🟢 已开启代理环境"
echo "🐱 已开启代理环境"
echo
if [ -n "${mixed_port:-}" ] && [ "$mixed_port" != "null" ]; then
@@ -597,8 +597,8 @@ status_tun_effective_text() {
s="$(status_tun_effective_status)"
case "$s" in
effective) echo "🟢 已生效" ;;
*) echo "🔴 未生效" ;;
effective) echo "🐱 已生效" ;;
*) echo "❗ 未生效" ;;
esac
}
@@ -769,10 +769,10 @@ system_state_risk_text() {
load_system_state
case "$RISK_LEVEL" in
low) echo "🟢 低" ;;
low) echo "🐱 低" ;;
medium) echo "🟡 中" ;;
high) echo "🟠 高" ;;
critical) echo "🔴 严重" ;;
critical) echo "❗ 严重" ;;
*) echo "⚪ 未知" ;;
esac
}
@@ -950,7 +950,7 @@ compute_box_width() {
[ "$line_width" -gt "$max_len" ] && max_len="$line_width"
done
ui_box_width=$((max_len + 8))
ui_box_width=$((max_len + 14))
}
box_border_top() {
@@ -998,7 +998,7 @@ box_empty() {
box_section_line() {
local text="$1"
local inner_width=$((ui_box_width - 2))
local content=" $text"
local content=" $text"
local content_len
content_len="$(box_text_width "$content")"
local right_pad=$((inner_width - content_len))
@@ -1066,10 +1066,10 @@ print_config_apply_feedback() {
case "${build_status:-unknown}" in
success)
echo "🟢 构建结果:success"
echo "🐱 构建结果:success"
;;
failed)
echo "🔴 构建结果:failed"
echo "❗ 构建结果:failed"
;;
blocked)
echo "🚨 构建结果:blocked"
@@ -1081,7 +1081,7 @@ print_config_apply_feedback() {
case "${build_applied:-unknown}" in
true)
echo "🟢 是否应用:true"
echo "🐱 是否应用:true"
;;
false)
echo "🚨 是否应用:false"
@@ -1098,9 +1098,9 @@ print_config_apply_feedback() {
main_feedback_runtime_state() {
if status_is_running; then
ui_kv "🟢" "运行状态" "已运行"
ui_kv "🐱" "运行状态" "已运行"
else
ui_kv "🔴" "运行状态" "未运行"
ui_kv "❗" "运行状态" "未运行"
fi
}
@@ -1192,21 +1192,21 @@ print_select_context() {
local current_proxy
if ! status_is_running; then
ui_kv "🔴" "代理状态" "未运行"
ui_kv "❗" "代理状态" "未运行"
ui_next "clashon"
ui_blank
return 1
fi
if ! proxy_controller_reachable 2>/dev/null; then
ui_kv "🔴" "当前节点" "控制器不可访问"
ui_kv "❗" "当前节点" "控制器不可访问"
ui_next "clashctl doctor"
ui_blank
return 1
fi
if [ "$(proxy_group_count 2>/dev/null || echo 0)" -le 0 ]; then
ui_kv "🔴" "当前节点" "暂无可切换策略组"
ui_kv "❗" "当前节点" "暂无可切换策略组"
ui_next "clashctl status --verbose"
ui_blank
return 1
@@ -1334,7 +1334,7 @@ print_tun_container_gate_feedback() {
case "$mode" in
host)
ui_kv "💻" "环境模式" "主机环境"
ui_kv "🟢" "容器裁决" "允许正常开启"
ui_kv "🐱" "容器裁决" "允许正常开启"
;;
container-safe)
ui_kv "💻" "环境模式" "容器环境"
@@ -1343,8 +1343,8 @@ print_tun_container_gate_feedback() {
;;
container-risky)
ui_kv "💻" "环境模式" "容器环境"
ui_kv "🔴" "容器裁决" "高风险,已阻断开启"
[ -n "${reason:-}" ] && ui_kv "🔴" "阻断原因" "$reason"
ui_kv "❗" "容器裁决" "高风险,已阻断开启"
[ -n "${reason:-}" ] && ui_kv "❗" "阻断原因" "$reason"
ui_next "clashctl tun doctor"
ui_blank
;;
@@ -1377,12 +1377,12 @@ print_tun_on_feedback() {
case "$verify_result" in
ok)
status_icon="🟢"
status_icon="🐱"
status_text="已生效"
detail="已通过 Tun 即时验证"
;;
policy-routing-likely-effective)
status_icon="🟢"
status_icon="🐱"
status_text="已生效"
if tun_log_tun_source_line >/dev/null 2>&1; then
detail="已检测到 Tun 适配器、policy routing 与 Tun 源地址流量"
@@ -1391,7 +1391,7 @@ print_tun_on_feedback() {
fi
;;
disabled-in-state|disabled-in-runtime-config|tun-disabled|runtime-not-running|controller-unreachable)
status_icon="🔴"
status_icon="❗"
status_text="未生效"
detail="即时验证未通过:$verify_result"
;;
@@ -1425,7 +1425,7 @@ print_tun_off_feedback() {
case "$verify_result" in
ok)
ui_kv "🟢" "验证结果" "Tun 已关闭并完成回滚检查"
ui_kv "🐱" "验证结果" "Tun 已关闭并完成回滚检查"
[ -n "${route_dev:-}" ] && ui_kv "🌐" "当前默认路由设备" "$route_dev"
;;
*)
@@ -1912,9 +1912,9 @@ print_status_summary_compact() {
tun_text="$(status_tun_effective_text)"
if status_is_running; then
running_text="🟢 已开启"
running_text="🐱 已开启"
else
running_text="🔴 未开启"
running_text="❗ 未开启"
fi
user_connectivity="$(connectivity_issue_text)"
@@ -1951,7 +1951,7 @@ print_status_summary_compact() {
echo
echo "【当前结果】"
echo "🟢 代理状态:$running_text"
echo "🐱 代理状态:$running_text"
echo "🌐 当前可用性:$user_connectivity"
echo "📡 当前订阅:${current_active:-未设置}"
echo "🚀 当前节点:$current_proxy_brief"
@@ -2037,9 +2037,9 @@ print_status_summary_verbose() {
build_applied_reason="$(status_runtime_build_applied_reason 2>/dev/null || true)"
if status_is_running; then
running_text="🟢 已开启"
running_text="🐱 已开启"
else
running_text="🔴 未开启"
running_text="❗ 未开启"
fi
controller_ok="false"
@@ -2096,7 +2096,7 @@ print_status_summary_verbose() {
echo
echo "【当前结果】"
echo "🟢 代理状态:$running_text"
echo "🐱 代理状态:$running_text"
echo "🌐 当前可用性:$user_connectivity"
echo "📡 当前订阅:${current_active:-未设置}"
echo "🚀 当前节点:$current_proxy_brief"
@@ -2149,9 +2149,9 @@ print_status_summary_verbose() {
echo "【Tun 状态】"
if [ "$tun_enabled" = "true" ]; then
echo "🟢 Tun 开关:已开启"
echo "🐱 Tun 开关:已开启"
else
echo "🔴 Tun 开关:未开启"
echo "❗ Tun 开关:未开启"
fi
echo "🧪 Tun 生效:${tun_effective:-未知}"
@@ -2170,7 +2170,7 @@ print_status_summary_verbose() {
echo "🧩 最近编译:未知"
fi
[ -n "${build_active_sources:-}" ] && echo "🟢 实际参与编译:$build_active_sources"
[ -n "${build_active_sources:-}" ] && echo "🐱 实际参与编译:$build_active_sources"
[ -n "${build_failed_active_sources:-}" ] && echo "❌ 编译失败源:$build_failed_active_sources"
echo
@@ -2187,7 +2187,7 @@ print_status_summary_verbose() {
case "${build_applied:-}" in
true)
echo "🟢 最近构建应用:true @ ${build_applied_time:-unknown}"
echo "🐱 最近构建应用:true @ ${build_applied_time:-unknown}"
;;
false)
echo "🚨 最近构建应用:false @ ${build_applied_time:-unknown}"
@@ -2357,8 +2357,8 @@ cmd_ui_box() {
line0="🔓 注意放行端口:${6:-unknown}"
[ -n "${1:-}" ] && line1="📶 状态:$1"
[ -n "${2:-}" ] && line2="💻 本机:$2"
[ -n "${3:-}" ] && line3="🏠 局域网:$3"
[ -n "${3:-}" ] && line2="🏠 内网:$3"
line3="📡 公共:http://board.zash.run.place"
[ -n "${4:-}" ] && line4="🌏 公网:$4"
[ -n "${secret:-}" ] && line5="🔑 密钥:$secret"
@@ -2390,8 +2390,8 @@ cmd_ui_help_summary() {
printf ' %-18s %s\n' "clashoff" "⛔ 关闭代理"
printf ' %-18s %s\n' "clashctl select" "💫 选择节点"
echo "🕹️ 控制台"
printf ' %-18s %s\n' "clashctl ui" "🕹️ 查看 Web 控制台"
printf ' %-18s %s\n' "clashctl secret" "🔑 查看或设置 Web 密钥"
printf ' %-18s %s\n' "clashui" "🕹️ 查看 Web 控制台"
printf ' %-18s %s\n' "clashsecret" "🔑 查看或设置 Web 密钥"
echo "📦 订阅"
printf ' %-18s %s\n' "clashctl add" "➕ 添加订阅"
printf ' %-18s %s\n' "clashctl use" "💱 切换订阅"
@@ -3175,7 +3175,7 @@ cmd_doctor() {
if doctor_problem_lines | grep -q .; then
doctor_problem_lines | sed 's/^/ /'
else
echo " 🟢 未发现明显问题"
echo " 🐱 未发现明显问题"
fi
ui_blank
@@ -3216,7 +3216,7 @@ cmd_dev() {
bash "$PROJECT_DIR/uninstall.sh" --dev-reset
echo
echo "🟢 开发重置完成"
echo "🐱 开发重置完成"
echo "🧩 保留内容:项目目录、已下载依赖、调试环境"
echo "👉 下一步:重新执行 install.sh 或 clashctl status"
echo
@@ -3245,7 +3245,7 @@ cmd_config_show() {
ui_kv "🚩" "当前主订阅" "${active:-未设置}"
ui_kv "🚀" "当前内核" "${kernel:-mihomo}"
ui_kv "🧩" "编译模式" "active-only"
ui_kv "🟢" "最近构建" "${build_status:-unknown}${build_time:+ @ ${build_time}}"
ui_kv "🐱" "最近构建" "${build_status:-unknown}${build_time:+ @ ${build_time}}"
ui_kv "🧩" "配置来源" "${config_source:-unknown}"
ui_blank
ui_next "clashctl status"
@@ -3463,7 +3463,7 @@ cmd_mixin_edit() {
if status_is_running; then
service_restart
echo "🟢 Mixin 已生效(已自动重启)"
echo "🐱 Mixin 已生效(已自动重启)"
else
echo "🟡 Mixin 已写入(下次启动生效)"
fi
@@ -3588,9 +3588,9 @@ doctor_risk_level() {
doctor_risk_text() {
case "$(doctor_risk_level)" in
low) echo "🟢 低" ;;
low) echo "🐱 低" ;;
medium) echo "🟡 中" ;;
high) echo "🔴 高" ;;
high) echo "❗ 高" ;;
*) echo "⚪ 未知" ;;
esac
}
@@ -3623,7 +3623,7 @@ doctor_problem_lines() {
doctor_primary_conclusion() {
if ! runtime_config_exists; then
echo "🔴 当前不可用:缺少运行配置"
echo "❗ 当前不可用:缺少运行配置"
return 0
fi
@@ -3633,11 +3633,11 @@ doctor_primary_conclusion() {
fi
if ! proxy_controller_reachable 2>/dev/null; then
echo "🔴 当前异常:内核已启动,但控制器不可访问"
echo "❗ 当前异常:内核已启动,但控制器不可访问"
return 0
fi
echo "🟢 当前基本可用:代理内核与控制器均正常"
echo "🐱 当前基本可用:代理内核与控制器均正常"
}
doctor_recommendation_lines() {
@@ -3752,39 +3752,30 @@ cmd_secret() {
case "${1:-}" in
"")
current_secret="$(controller_secret 2>/dev/null || true)"
echo
ui_title "🔑 Web 密钥"
if [ -n "${current_secret:-}" ] && [ "$current_secret" != "null" ]; then
ui_kv "🔑" "当前密钥" "$current_secret"
else
ui_kv "🚨" "当前密钥" "未设置"
fi
ui_kv "🐱" "用途" "用于访问 Clash Web 控制台"
ui_next "clashctl ui"
ui_blank
echo
;;
*)
new_secret="$1"
set_controller_secret "$new_secret"
echo
ui_title "🔑 密钥已更新"
ui_kv "🔑" "新密钥" "$new_secret"
if status_is_running; then
service_restart
ui_kv "🟢" "生效状态" "已重启并生效"
ui_kv "🐱" "状态" "密钥更新成功,已重启生效"
else
ui_kv "🟡" "生效状态" "将在下次启动时生效"
ui_kv "🐱" "状态" "将在下次启动时生效"
fi
ui_next "clashctl ui"
ui_blank
echo
;;
esac
}
@@ -3803,24 +3794,24 @@ cmd_tun_status() {
echo
if [ "$enabled" = "true" ]; then
echo "🟢 当前状态:已开启"
echo "🐱 当前状态:已开启"
else
echo "🔴 当前状态:未开启"
echo "❗ 当前状态:未开启"
fi
echo "🔧 Tun stack:$stack"
echo "💻 环境类型:$env_type"
if can_manage_tun_safely; then
echo "🟢 环境检查:满足基础开启条件"
echo "🐱 环境检查:满足基础开启条件"
else
echo "🚨 环境检查:当前不满足基础开启条件"
fi
if [ "$effective_status" = "effective" ]; then
echo "🟢 已生效"
echo "🐱 已生效"
else
echo "🔴 未生效"
echo "❗ 未生效"
fi
if [ "$enabled" = "true" ]; then
@@ -3864,7 +3855,7 @@ cmd_tun_on() {
if ! can_manage_tun_safely; then
echo
echo "🔴 Tun 模式无法开启"
echo "❗ Tun 模式无法开启"
echo "🚨 原因:当前环境不满足基础 Tun 条件"
echo "👉 下一步:clashctl tun doctor"
echo
@@ -3993,13 +3984,13 @@ doctor_tun_checks() {
echo "【总体结论】"
if [ "$process_cap_rc" -eq 0 ]; then
echo "🟢 mihomo 进程已检测到 CAP_NET_ADMIN;当前执行环境 capability 不作为主因"
echo "🐱 mihomo 进程已检测到 CAP_NET_ADMIN;当前执行环境 capability 不作为主因"
elif [ "$cap_rc" -eq 1 ]; then
echo "🔴 当前执行环境未检测到 CAP_NET_ADMIN;root 不等于一定拥有 CAP_NET_ADMIN"
echo "❗ 当前执行环境未检测到 CAP_NET_ADMIN;root 不等于一定拥有 CAP_NET_ADMIN"
elif can_manage_tun_safely; then
echo "🟢 当前环境满足基础 Tun 开启条件"
echo "🐱 当前环境满足基础 Tun 开启条件"
else
echo "🔴 当前环境不满足基础 Tun 开启条件"
echo "❗ 当前环境不满足基础 Tun 开启条件"
fi
echo "🧪 Tun 是否生效要看 mihomo 进程实际拿到的网络管理能力,能力边界可能来自 systemd unit 或容器环境"
echo "🧪 当前 Tun 状态:$runtime_tun_status"
@@ -4014,7 +4005,7 @@ doctor_tun_checks() {
if [ "$(tun_container_mode 2>/dev/null || echo unknown)" = "container-risky" ]; then
local gate_reason
gate_reason="$(tun_container_risk_reason 2>/dev/null || true)"
[ -n "${gate_reason:-}" ] && echo "🔴 阻断原因:$gate_reason"
[ -n "${gate_reason:-}" ] && echo "❗ 阻断原因:$gate_reason"
fi
echo
@@ -4028,13 +4019,13 @@ doctor_tun_checks() {
if [ -n "${problem_lines:-}" ]; then
printf '%s\n' "$problem_lines" | sed 's/^/ /'
else
echo " 🟢 未发现明显问题"
echo " 🐱 未发现明显问题"
fi
echo
echo "【关键证据】"
if is_root_user; then
echo " 🟢 当前执行用户:root"
echo " 🐱 当前执行用户:root"
else
echo " 🚨 当前执行用户:非 root"
fi
@@ -4042,14 +4033,14 @@ doctor_tun_checks() {
echo " 💻 是否容器:$(tun_doctor_container_evidence_text "$env_type")"
if tun_device_exists; then
echo " 🟢 /dev/net/tun:存在"
echo " 🐱 /dev/net/tun:存在"
else
echo " 🔴 /dev/net/tun:不存在"
echo " ❗ /dev/net/tun:不存在"
fi
if tun_device_exists; then
if tun_device_readable; then
echo " 🟢 /dev/net/tun:可读写"
echo " 🐱 /dev/net/tun:可读写"
else
echo " 🚨 /dev/net/tun:存在但不可正常读写"
fi
@@ -4057,7 +4048,7 @@ doctor_tun_checks() {
case "$cap_rc" in
0)
echo " 🟢 CAP_NET_ADMIN:已检测到"
echo " 🐱 CAP_NET_ADMIN:已检测到"
;;
2)
echo " 🚨 CAP_NET_ADMIN:无法精确判断(缺少 capsh)"
@@ -4068,7 +4059,7 @@ doctor_tun_checks() {
esac
if has_ip_command; then
echo " 🟢 ip 命令:可用"
echo " 🐱 ip 命令:可用"
else
echo " 🚨 ip 命令:缺失"
fi
@@ -4076,7 +4067,7 @@ doctor_tun_checks() {
if runtime_config_exists; then
dns_listen_value="$("$(yq_bin)" eval '.dns.listen // ""' "$RUNTIME_DIR/config.yaml" 2>/dev/null | head -n 1)"
if [ -n "${dns_listen_value:-}" ] && [ "$dns_listen_value" != "null" ]; then
echo " 🟢 DNS 监听:$dns_listen_value"
echo " 🐱 DNS 监听:$dns_listen_value"
else
echo " 🚨 DNS 监听:未解析到"
fi
@@ -4100,7 +4091,7 @@ doctor_tun_checks() {
echo " 🌐 默认路由设备:$route_dev"
case "$route_takeover" in
yes)
echo " 🟢 默认路由接管:看起来已接管(设备命中 tun/clash/mihomo 特征)"
echo " 🐱 默认路由接管:看起来已接管(设备命中 tun/clash/mihomo 特征)"
;;
no)
echo " 📜 主默认路由:仍指向 $route_dev(仅代表 main table;Linux Tun 可通过 policy routing / rule table 生效)"
@@ -4335,13 +4326,13 @@ tun_doctor_log_evidence() {
traffic_line="$(tun_log_tun_source_line 2>/dev/null || true)"
if [ -n "${adapter_line:-}" ]; then
echo " 🟢 Tun adapter 日志:$adapter_line"
echo " 🐱 Tun adapter 日志:$adapter_line"
else
echo " 🚨 Tun adapter 日志:未在 mihomo.out.log 中找到"
fi
if [ -n "${traffic_line:-}" ]; then
echo " 🟢 Tun 流量日志:$traffic_line"
echo " 🐱 Tun 流量日志:$traffic_line"
else
echo " 🚨 Tun 流量日志:未发现 28.0.0.x Tun 源地址流量"
fi
@@ -4354,13 +4345,13 @@ tun_doctor_policy_evidence() {
route_line="$(tun_policy_route_line 2>/dev/null || true)"
if [ -n "${rule_line:-}" ]; then
echo " 🟢 Tun policy rule:$rule_line"
echo " 🐱 Tun policy rule:$rule_line"
else
echo " 🚨 Tun policy rule:未发现 lookup 2022 / iif Meta 相关规则"
fi
if [ -n "${route_line:-}" ]; then
echo " 🟢 Tun policy route:$route_line"
echo " 🐱 Tun policy route:$route_line"
else
echo " 🚨 Tun policy route:未发现 table 2022 默认路由指向 Meta/tun 设备"
fi
@@ -4540,44 +4531,44 @@ tun_doctor_conclusion_line() {
case "${1:-traffic-check-failed}" in
ok)
if tun_has_policy_routing_evidence 2>/dev/null; then
echo "🟢 最终状态:已生效(Tun + policy routing + 主动流量验证通过)"
echo "🐱 最终状态:已生效(Tun + policy routing + 主动流量验证通过)"
else
echo "🟢 最终状态:已生效(主动流量验证通过)"
echo "🐱 最终状态:已生效(主动流量验证通过)"
fi
;;
policy-routing-likely-effective)
if tun_log_tun_source_line >/dev/null 2>&1; then
echo "🟢 最终状态:已生效(Tun + policy routing 已安装,并观察到 Tun 源地址流量)"
echo "🐱 最终状态:已生效(Tun + policy routing 已安装,并观察到 Tun 源地址流量)"
else
echo "🟡 最终状态:很可能已生效(Tun + policy routing 已安装;主动流量验证不足 / 未观察到明确 Tun 源地址日志)"
fi
;;
missing-cap-net-admin)
echo "🔴 Tun 未生效:当前能力检测未通过(CAP_NET_ADMIN)"
echo "❗ Tun 未生效:当前能力检测未通过(CAP_NET_ADMIN)"
;;
missing-tun-device)
echo "🔴 Tun 未生效:缺少 /dev/net/tun"
echo "❗ Tun 未生效:缺少 /dev/net/tun"
;;
tun-device-not-readable)
echo "🔴 Tun 未生效:/dev/net/tun 不可读写"
echo "❗ Tun 未生效:/dev/net/tun 不可读写"
;;
missing-ip-command)
echo "🔴 Tun 未生效:缺少 ip 命令"
echo "❗ Tun 未生效:缺少 ip 命令"
;;
runtime-not-running)
echo "🔴 Tun 未生效:代理内核未运行"
echo "❗ Tun 未生效:代理内核未运行"
;;
controller-unreachable)
echo "🔴 Tun 未生效:控制器不可访问"
echo "❗ Tun 未生效:控制器不可访问"
;;
disabled-in-state|disabled-in-runtime-config)
echo "🔴 Tun 未生效:Tun 状态与运行配置不一致"
echo "❗ Tun 未生效:Tun 状态与运行配置不一致"
;;
tun-disabled)
echo "🔴 Tun 未生效:Tun 未开启"
echo "❗ Tun 未生效:Tun 未开启"
;;
host-ip-unavailable|current-ip-unavailable)
echo "🔴 Tun 未生效:无法完成公网出口验证"
echo "❗ Tun 未生效:无法完成公网出口验证"
;;
traffic-same-as-host)
if tun_log_has_tun_traffic_evidence 2>/dev/null; then
@@ -5024,21 +5015,21 @@ cmd_use() {
if [ -z "${recommended:-}" ]; then
if [ -n "${active:-}" ]; then
ui_title "🟢 当前无更优推荐,保持当前"
ui_title "🐱 当前无更优推荐,保持当前"
ui_kv "🚩" "当前主订阅" "$active"
ui_next "clashctl select 选择节点"
ui_blank
return 0
fi
ui_title "🔴 当前没有可推荐的订阅"
ui_title "❗ 当前没有可推荐的订阅"
ui_next "clashctl select 选择节点"
ui_blank
return 1
fi
if [ "${recommended:-}" = "${active:-}" ]; then
ui_title "🟢 当前已是推荐订阅,无需切换"
ui_title "🐱 当前已是推荐订阅,无需切换"
[ -n "${active:-}" ] && ui_kv "📡" "当前主订阅" "$active"
ui_next "clashctl select 选择节点"
ui_blank
@@ -5637,6 +5628,7 @@ cmd_proxy() {
cmd_upgrade() {
local verbose="false"
local target_kernel=""
local current_version target_version actual_version
prepare
@@ -5656,13 +5648,23 @@ cmd_upgrade() {
done
[ -n "${target_kernel:-}" ] || target_kernel="$(runtime_kernel_type)"
target_kernel="$(normalize_kernel_type "$target_kernel")"
current_version="$(kernel_installed_version_text "$target_kernel")"
target_version="$(kernel_target_version "$target_kernel")"
ui_title "🚀 正在升级 ${target_kernel} 内核 ..."
ui_kv "🧩" "升级方式" "升级到当前设定目标版本,不自动追踪官方 latest"
ui_kv "📦" "当前版本" "$current_version"
ui_kv "🎯" "目标版本" "$target_version"
ui_blank
upgrade_runtime_kernel "$target_kernel" "$verbose"
actual_version="$(kernel_installed_version_text "$target_kernel")"
ui_title "🟢 内核升级完成"
ui_title "🐱 内核升级完成"
ui_kv "🚀" "当前内核" "$(runtime_kernel_type)"
ui_kv "🎯" "目标版本" "$target_version"
ui_kv "🧪" "实际版本" "$actual_version"
ui_kv "🧩" "影响范围" "仅更新代理内核,不更新项目脚本"
ui_next "clashctl status"
ui_blank
@@ -5693,7 +5695,7 @@ cmd_update() {
update_project_code "$force_mode" "$regenerate_mode"
ui_title "🟢 项目代码已更新"
ui_title "🐱 项目代码已更新"
ui_kv "🧩" "影响范围" "脚本、CLI、配置处理逻辑可能已变化"
if [ "$regenerate_mode" = "true" ]; then
ui_kv "🧩" "配置状态" "已重新生成"
+6 -6
View File
@@ -9,7 +9,7 @@
: "${CONFIG_DIR:=}"
: "${RESOURCE_DIR:=}"
DEFAULT_MIHOMO_VERSION="${MIHOMO_VERSION:-v1.19.21}"
DEFAULT_MIHOMO_VERSION="${MIHOMO_VERSION:-v1.19.23}"
DEFAULT_CLASH_VERSION="${CLASH_VERSION:-v1.18.0}"
DEFAULT_SUBCONVERTER_VERSION="${SUBCONVERTER_VERSION:-v0.9.0}"
DEFAULT_YQ_VERSION="${YQ_VERSION:-v4.52.4}"
@@ -85,7 +85,7 @@ ui_section() {
}
ui_ok() {
printf '🟢 %s\n' "$*"
printf '🐱 %s\n' "$*"
}
ui_info() {
@@ -97,7 +97,7 @@ ui_warn() {
}
ui_error() {
printf '🔴 %s\n' "$*" >&2
printf '❗ %s\n' "$*" >&2
}
ui_next() {
@@ -2589,12 +2589,12 @@ install_runtime_brief_line() {
case "$status_text" in
ready)
echo "🟢 当前状态:ready"
echo "🐱 当前状态:ready"
echo "🌐 本地代理:http://127.0.0.1:${mixed_port}"
echo "💻 控制台:http://${controller_display:-$controller}/ui"
;;
stopped)
echo "🔴 当前状态:stopped"
echo "❗ 当前状态:stopped"
;;
verifying)
echo "🟡 当前状态:verifying"
@@ -2607,7 +2607,7 @@ install_runtime_brief_line() {
fi
;;
broken)
echo "🔴 当前状态:broken"
echo "❗ 当前状态:broken"
;;
*)
echo "⚪ 当前状态:unknown"
+1 -1
View File
@@ -859,7 +859,7 @@ print_subscription_health_one() {
echo "📡 订阅名称:$name"
echo "🔧 订阅类型:$type_text"
echo "🟢 启用状态:$enabled_text"
echo "🐱 启用状态:$enabled_text"
echo "❤️ 健康状态:${status:-unknown}"
echo "🚨 失败次数:${fail_count:-0}"
[ -n "${last_success:-}" ] && echo "🕒 最近成功:$last_success"
+42 -8
View File
@@ -55,6 +55,47 @@ remove_clash_binary() {
rm -f "$(clash_bin)" 2>/dev/null || true
}
kernel_target_version() {
local kernel="$1"
case "$(normalize_kernel_type "$kernel")" in
mihomo)
echo "${MIHOMO_VERSION:-$DEFAULT_MIHOMO_VERSION}"
;;
clash)
echo "${CLASH_VERSION:-$DEFAULT_CLASH_VERSION}"
;;
esac
}
kernel_binary_path() {
local kernel="$1"
case "$(normalize_kernel_type "$kernel")" in
mihomo)
mihomo_bin
;;
clash)
clash_bin
;;
esac
}
kernel_installed_version_text() {
local kernel="$1"
local bin version_text
bin="$(kernel_binary_path "$kernel")"
if [ ! -x "$bin" ]; then
echo "未安装"
return 0
fi
version_text="$("$bin" -v 2>/dev/null | head -n 1 || true)"
[ -n "${version_text:-}" ] || version_text="未知"
echo "$version_text"
}
upgrade_runtime_kernel() {
local target_kernel="${1:-}"
local verbose="${2:-false}"
@@ -71,14 +112,7 @@ upgrade_runtime_kernel() {
if [ "$verbose" = "true" ]; then
info "当前架构:$(get_arch)"
info "目标内核:$target_kernel"
case "$target_kernel" in
mihomo)
info "目标版本:${MIHOMO_VERSION:-$DEFAULT_MIHOMO_VERSION}"
;;
clash)
info "目标版本:${CLASH_VERSION:-$DEFAULT_CLASH_VERSION}"
;;
esac
info "目标版本:$(kernel_target_version "$target_kernel")"
fi
case "$target_kernel" in
+1 -1
View File
@@ -88,4 +88,4 @@ else
echo "🧩 保留内容:runtime 数据仍在"
fi
echo "🟢 卸载完成"
echo "✨ 卸载完成"