diff --git a/.env b/.env index a79bb7d..43e08f2 100644 --- a/.env +++ b/.env @@ -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" diff --git a/README.md b/README.md index 8ee3c9c..0a6bb6b 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,28 @@

- 🐧 Linux 一键安装 Clash + 🐱 Linux 一键安装 Clash

+

一个面向 Linux 的 Clash(Mihomo)管理工具。

-

- +

+ # ✨ 核心特性 -- 🚀 **自动识别系统架构**,自动下载并使用对应 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-` -- `tools/subconverter/bin/subconverter-` - -其中 `` 取值为: - -- `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` 不可用时会自动跳过转换,并提示警告。 - -
- ## 设置代理 1. 开启 IP 转发 diff --git a/install.sh b/install.sh index 2438f45..638702c 100755 --- a/install.sh +++ b/install.sh @@ -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 diff --git a/resources/shell.png b/resources/shell.png new file mode 100644 index 0000000..739681a Binary files /dev/null and b/resources/shell.png differ diff --git a/resources/ui.png b/resources/ui.png new file mode 100644 index 0000000..f1baef3 Binary files /dev/null and b/resources/ui.png differ diff --git a/scripts/core/alias.sh b/scripts/core/alias.sh index 42e291d..cd3fdf1 100644 --- a/scripts/core/alias.sh +++ b/scripts/core/alias.sh @@ -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" } } diff --git a/scripts/core/clashctl.sh b/scripts/core/clashctl.sh index 09593c2..ef40d59 100644 --- a/scripts/core/clashctl.sh +++ b/scripts/core/clashctl.sh @@ -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 "🧩" "配置状态" "已重新生成" diff --git a/scripts/core/common.sh b/scripts/core/common.sh index 5b539cf..779001d 100644 --- a/scripts/core/common.sh +++ b/scripts/core/common.sh @@ -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" diff --git a/scripts/core/config.sh b/scripts/core/config.sh index 21fcf75..d05d36d 100644 --- a/scripts/core/config.sh +++ b/scripts/core/config.sh @@ -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" diff --git a/scripts/core/update.sh b/scripts/core/update.sh index 95251ba..e3979e3 100644 --- a/scripts/core/update.sh +++ b/scripts/core/update.sh @@ -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 diff --git a/uninstall.sh b/uninstall.sh index 49de1c6..6310d60 100755 --- a/uninstall.sh +++ b/uninstall.sh @@ -88,4 +88,4 @@ else echo "🧩 保留内容:runtime 数据仍在" fi -echo "🟢 卸载完成" +echo "✨ 卸载完成"