Files
Clash/clash
T
lukeopen cdfbcec6b7 refactor: 现代化 CLI 风格,修复面板跳转与残留 TUN 劫持
- 菜单精简为左右两列:1 启动 / 2 停止 / 3 打开面板,移除冗余项
- 合并 import 到 link,新增 doctor 网络诊断,统一输出风格
- /ui 自动跳转 zashboard,clash open 直接打开正确面板地址
- 按 fake-IP 网关(198.18.0.1)识别并清理残留 TUN,修复停止后国内直连绕路/高延迟
- stop 未运行时也清理残留,start 启动前自动清理;系统代理/DNS 遍历所有网络服务
2026-08-10 15:50:27 +08:00

674 lines
21 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/zsh
# ═══════════════════════════════════════════════════════════
# Mihomo Kernel Manager — macOS
# clash <command> | clash 进入交互菜单 | clash help
# ═══════════════════════════════════════════════════════════
CONFIG_DIR="${HOME}/.config/mihomo"
CONFIG_FILE="${CONFIG_DIR}/config.yaml"
PID_FILE="${CONFIG_DIR}/mihomo.pid"
LOG_FILE="${CONFIG_DIR}/mihomo.log"
LINK_FILE="${CONFIG_DIR}/source.txt" # 源 YAML 路径
SOURCE_DIR="${HOME}/Mihomo" # 源配置目录(自动检测最新 YAML)
MIXED_PORT="7890"
API_PORT="9090"
MIHOMO_BIN="/opt/homebrew/bin/mihomo"
[[ -d "$CONFIG_DIR" ]] || mkdir -p "$CONFIG_DIR"
# ─── 颜色 ───
BOLD='\033[1m'; DIM='\033[2m'
GREEN='\033[0;32m'; BOLD_GREEN='\033[1;32m'
RED='\033[0;31m'; BOLD_RED='\033[1;31m'
YELLOW='\033[0;33m'; BOLD_YELLOW='\033[1;33m'
BLUE='\033[0;34m'; BOLD_BLUE='\033[1;34m'
PURPLE='\033[0;35m'; BOLD_PURPLE='\033[1;35m'
CYAN='\033[0;36m'; BOLD_CYAN='\033[1;36m'
WHITE='\033[1;37m'
NC='\033[0m'
SEP="${DIM}──────────────────────────────────────────${NC}"
# ─── 输出辅助 ───
info() { echo " ${CYAN}${NC} $*"; }
ok() { echo " ${GREEN}${NC} $*"; }
warn() { echo " ${YELLOW}${NC} $*"; }
err() { echo " ${RED}${NC} $*"; }
dim() { echo " ${DIM}$*${NC}"; }
section(){ echo ""; echo " ${BOLD}── $* ──${NC}"; }
# ─── 运行状态 ───
is_running() {
[[ -f "$PID_FILE" ]] && pgrep -F "$PID_FILE" &>/dev/null
}
# ========== 源文件自动检测 ==========
# 优先级:link 固定路径 > SOURCE_DIR 最新 .yaml/.yml
auto_detect_source() {
if [[ -f "$LINK_FILE" ]]; then
local linked=$(cat "$LINK_FILE")
if [[ -f "$linked" ]]; then
echo "$linked"
return 0
fi
warn "链接文件不存在: $linked,尝试自动检测..."
fi
if [[ -d "$SOURCE_DIR" ]]; then
local latest=$(ls -t "$SOURCE_DIR"/*.yaml(N) "$SOURCE_DIR"/*.yml(N) 2>/dev/null | head -1)
if [[ -n "$latest" && -f "$latest" ]]; then
echo "$latest"
return 0
fi
fi
return 1
}
# ========== macOS 适配:复制源文件并做兼容转换(不改原件)==========
macos_adapt_config() {
local src="${1:-}"
if [[ -z "$src" ]]; then
src=$(auto_detect_source)
fi
if [[ -z "$src" || ! -f "$src" ]]; then
err "未找到 YAML 配置文件"
dim "请把 .yaml 放到 $SOURCE_DIR,或用: clash link /path/to/file.yaml"
return 1
fi
echo -n " 适配 macOS 配置 ... "
cp "$src" "$CONFIG_FILE"
# macOS 兼容修复
sed -i '' '/^[[:space:]]*device: nikki[[:space:]]*$/d' "$CONFIG_FILE"
sed -i '' '/^[[:space:]]*device: nikki$/d' "$CONFIG_FILE"
sed -i '' 's/^\([[:space:]]*stack:\) system/\1 gvisor/' "$CONFIG_FILE"
sed -i '' '/^[[:space:]]*auto-redirect: true/d' "$CONFIG_FILE"
sed -i '' 's/ - tcp:\/\// - /g' "$CONFIG_FILE"
sed -i '' 's/ - udp:\/\// - /g' "$CONFIG_FILE"
ok "完成"
dim "源文件 $src"
dim "适配后 $CONFIG_FILE"
return 0
}
# ========== 管理面板 ==========
# /ui 下可能有多个面板子目录(如 zashboard),自动定位并生成首页跳转
ui_url() {
local ui_dir="$CONFIG_DIR/ui"
if [[ -d "$ui_dir" ]]; then
local dash
dash=$(find "$ui_dir" -maxdepth 1 -mindepth 1 -type d 2>/dev/null | sort | head -1)
if [[ -n "$dash" ]]; then
echo "http://127.0.0.1:${API_PORT}/ui/$(basename "$dash")/"
return 0
fi
fi
echo "http://127.0.0.1:${API_PORT}/ui"
}
ensure_ui_index() {
local ui_dir="$CONFIG_DIR/ui"
[[ -d "$ui_dir" ]] || return 0
local dash
dash=$(find "$ui_dir" -maxdepth 1 -mindepth 1 -type d 2>/dev/null | sort | head -1)
[[ -z "$dash" ]] && return 0
local dash_name=$(basename "$dash")
local index_file="$ui_dir/index.html"
if [[ ! -f "$index_file" ]]; then
cat > "$index_file" <<EOF
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="refresh" content="0; url=./${dash_name}/">
<title>Mihomo Dashboard</title>
</head>
<body>
<p>正在打开面板 <a href="./${dash_name}/">${dash_name}</a> ...</p>
</body>
</html>
EOF
info "已生成面板首页跳转: $index_file"
fi
}
# ========== 残留网络状态检测 ==========
# 返回 0 = 存在 mihomo 残留进程 / 残留 TUN 劫持 / 系统代理 / DNS 劫持
network_leftovers() {
pgrep -l mihomo >/dev/null 2>&1 && return 0
local dev
for dev in $(ifconfig -l 2>/dev/null); do
[[ "$dev" != utun* ]] && continue
local idx="${dev#utun}"
[[ "$idx" != <-> ]] && continue
if ifconfig "$dev" 2>/dev/null | grep -q "inet 198.18.0.1 "; then
return 0
fi
if netstat -rn -f inet 2>/dev/null | grep -qE "198\.18\.0\.1[[:space:]]+.*[[:space:]]+$dev"; then
return 0
fi
(( idx >= 1024 )) && return 0
done
local hijacked=0
while IFS= read -r service; do
local clean=$(printf '%s' "$service" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
[[ -z "$clean" ]] && continue
local wp=$(networksetup -getwebproxy "$clean" 2>/dev/null)
local sp=$(networksetup -getsocksfirewallproxy "$clean" 2>/dev/null)
if echo "$wp$sp" | grep -qE "Server: 127\.0\.0\.1" && echo "$wp$sp" | grep -qE "Port: ${MIXED_PORT}"; then
hijacked=1
break
fi
done < <(networksetup -listallnetworkservices 2>/dev/null | tail -n +2)
[[ $hijacked -eq 1 ]] && return 0
scutil --dns 2>/dev/null | grep -qE "nameserver\[[0-9]+\].*(127\.0\.0\.1|198\.18\.)" && return 0
return 1
}
# ========== 网络恢复(TUN/路由/代理/DNS 全面清理)==========
network_restore() {
local verbose=${1:-true}
$verbose && section "网络恢复"
# 1. 清理残留 mihomo 进程
if pgrep -l mihomo >/dev/null 2>&1; then
$verbose && warn "发现残留进程,正在清理..."
sudo pkill -9 mihomo 2>/dev/null
rm -f "$PID_FILE"
fi
# 2. 清理 mihomo 残留 TUN(按 fake-IP 网关 198.18.0.1 / 路由特征识别,
# macOS 上 mihomo 常创建 utun7/utun8 这类低编号,不能只认 >=1024)
local tun_list=$(ifconfig -l 2>/dev/null)
local route_devs=$(netstat -rn -f inet 2>/dev/null | awk '$2=="198.18.0.1" && $4 ~ /^utun[0-9]+$/ {print $4}' | sort -u)
local mihomo_tuns=""
for dev in ${(s: :)tun_list}; do
[[ "$dev" != utun* ]] && continue
local idx="${dev#utun}"
[[ "$idx" != <-> ]] && continue
if ifconfig "$dev" 2>/dev/null | grep -q "inet 198.18.0.1 "; then
mihomo_tuns+=" $dev"
continue
fi
if [[ -n "$route_devs" ]] && echo "$route_devs" | grep -qx "$dev"; then
mihomo_tuns+=" $dev"
continue
fi
(( idx >= 1024 )) && mihomo_tuns+=" $dev"
done
for dev in ${(s: :)mihomo_tuns}; do
$verbose && echo -n " 清理残留 TUN $dev ... "
sudo route -n delete -net 0.0.0.0/1 198.18.0.1 2>/dev/null
sudo route -n delete -net 128.0.0.0/1 198.18.0.1 2>/dev/null
sudo route -n delete -net 198.18.0.0/16 198.18.0.1 2>/dev/null
sudo ifconfig "$dev" down 2>/dev/null
sudo ifconfig "$dev" destroy 2>/dev/null
$verbose && echo "${GREEN}${NC}"
done
# 3. 关闭系统 HTTP/HTTPS/SOCKS 代理(遍历所有网络服务)
while IFS= read -r service; do
local clean=$(printf '%s' "$service" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
[[ -z "$clean" ]] && continue
local wp=$(networksetup -getwebproxy "$clean" 2>/dev/null)
local sp=$(networksetup -getsocksfirewallproxy "$clean" 2>/dev/null)
if echo "$wp$sp" | grep -qE "Server: 127\.0\.0\.1" && echo "$wp$sp" | grep -qE "Port: ${MIXED_PORT}"; then
$verbose && echo -n " 关闭系统代理 ($clean) ... "
sudo networksetup -setwebproxystate "$clean" off 2>/dev/null
sudo networksetup -setsecurewebproxystate "$clean" off 2>/dev/null
sudo networksetup -setsocksfirewallproxystate "$clean" off 2>/dev/null
$verbose && echo "${GREEN}${NC}"
fi
done < <(networksetup -listallnetworkservices 2>/dev/null | tail -n +2)
# 4. 刷新 DNS 缓存
$verbose && echo -n " 刷新 DNS 缓存 ... "
sudo dscacheutil -flushcache 2>/dev/null
sudo killall -HUP mDNSResponder 2>/dev/null
$verbose && echo "${GREEN}${NC}"
# 5. 还原系统 DNS(如果被 mihomo 指到 127.0.0.1 / 198.18.x
if scutil --dns 2>/dev/null | grep -qE "nameserver\[[0-9]+\].*(127\.0\.0\.1|198\.18\.)"; then
$verbose && echo -n " 还原系统 DNS ... "
while IFS= read -r service; do
local clean=$(printf '%s' "$service" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
[[ -z "$clean" ]] && continue
if networksetup -getdnsservers "$clean" 2>/dev/null | grep -qE "^(127\.0\.0\.1|198\.18\.)"; then
sudo networksetup -setdnsservers "$clean" empty 2>/dev/null
fi
done < <(networksetup -listallnetworkservices 2>/dev/null | tail -n +2)
sudo dscacheutil -flushcache 2>/dev/null
sudo killall -HUP mDNSResponder 2>/dev/null
$verbose && echo "${GREEN}${NC}"
fi
$verbose && echo ""
$verbose && ok "网络已恢复"
}
# ========== 网络诊断 ==========
network_health_check() {
local issues=0
section "网络诊断"
if nslookup baidu.com >/dev/null 2>&1; then
ok "DNS 解析正常 (baidu.com)"
else
err "DNS 解析失败"
issues=1
fi
if ping -c 1 -W 2 8.8.8.8 >/dev/null 2>&1; then
ok "外网连通 (8.8.8.8)"
else
err "外网不可达"
issues=1
fi
local tun_count=$(ifconfig -l 2>/dev/null | tr ' ' '\n' | grep -c "^utun")
local tun_high=0
local dev
for dev in $(ifconfig -l 2>/dev/null); do
[[ "$dev" != utun* ]] && continue
local idx="${dev#utun}"
[[ "$idx" != <-> ]] && continue
(( idx >= 1024 )) && (( tun_high++ ))
done
if [[ $tun_high -eq 0 ]]; then
ok "TUN 接口 ${tun_count} 个,无残留"
else
warn "TUN ${tun_count} 个(${tun_high} 个高编号残留)"
issues=1
fi
if [[ $issues -eq 1 ]]; then
warn "网络可能有异常,试试: clash cleanup"
else
ok "网络一切正常"
fi
}
# ========== 启动 ==========
cmd_start() {
local src="${1:-}"
local do_adapt=true
if [[ -n "$src" ]]; then
if [[ "$src" == "$CONFIG_FILE" ]] || [[ "$src" == "$(cat "$LINK_FILE" 2>/dev/null)" ]]; then
do_adapt=false
fi
fi
if [[ -z "$src" ]]; then
src=$(auto_detect_source)
fi
if $do_adapt && [[ "$src" != "$CONFIG_FILE" ]]; then
macos_adapt_config "$src" || return 1
elif [[ ! -f "$CONFIG_FILE" ]]; then
err "配置文件不存在: $CONFIG_FILE"
dim "试试: clash link /path/to/your.yaml"
return 1
fi
if is_running; then
warn "mihomo 已经在运行了"
return 0
fi
# 启动前清理上次异常退出残留(旧 TUN/路由会继续劫持全部流量)
if network_leftovers; then
echo ""
info "检测到上次残留网络状态,先清理 ..."
network_restore false
fi
echo ""
echo -n " 启动中 ... "
nohup sudo "$MIHOMO_BIN" -d "$CONFIG_DIR" -f "$CONFIG_FILE" > "$LOG_FILE" 2>&1 &
echo $! > "$PID_FILE"
sleep 1
if is_running; then
ok "已就绪"
ensure_ui_index
echo ""
printf " ${DIM}%-10s${NC} ${CYAN}127.0.0.1:${MIXED_PORT}${NC} ${DIM}(HTTP/SOCKS5)${NC}\n" "代理端口"
printf " ${DIM}%-10s${NC} ${CYAN}127.0.0.1:${API_PORT}${NC}\n" "API 控制"
printf " ${DIM}%-10s${NC} ${CYAN}$(ui_url)${NC}\n" "管理面板"
echo ""
else
err "启动失败"
dim "查看日志: tail $LOG_FILE"
rm -f "$PID_FILE"
return 1
fi
}
# ========== 停止 ==========
cmd_stop() {
if ! is_running; then
warn "mihomo 没有在运行"
# 进程没了但可能残留 TUN/路由/代理(例如被强杀),顺手清理
if network_leftovers; then
echo ""
info "检测到残留网络状态,正在清理 ..."
network_restore true
echo ""
network_health_check
fi
return 0
fi
local pid=$(cat "$PID_FILE")
echo ""
echo -n " 停止中 ... "
sudo kill "$pid" 2>/dev/null
for i in {1..10}; do
if ! pgrep -F "$PID_FILE" &>/dev/null 2>&1; then
ok "已停止 (PID: $pid)"
rm -f "$PID_FILE"
echo ""
network_restore false
network_health_check
return 0
fi
sleep 0.5
done
echo ""
warn "超时,强制终止..."
sudo kill -9 "$pid" 2>/dev/null
sleep 0.5
rm -f "$PID_FILE"
if pgrep -l mihomo >/dev/null 2>&1; then
err "停止失败,残留进程仍在运行"
dim "试试: sudo pkill -9 mihomo"
return 1
fi
warn "已强制停止"
info "正在恢复网络 ..."
network_restore true
echo ""
network_health_check
}
# ========== 重启 / 热重载 ==========
cmd_restart() {
cmd_stop
sleep 1
cmd_start "$@"
}
cmd_reload() {
local cfg="${1:-$CONFIG_FILE}"
if ! is_running; then
warn "mihomo 没在运行,直接启动"
cmd_start "$cfg"
return $?
fi
if [[ ! -f "$cfg" ]]; then
err "配置文件不存在: $cfg"
return 1
fi
echo -n " 热重载配置 ... "
if curl -s -X PUT "http://127.0.0.1:${API_PORT}/configs" \
-H "Content-Type: application/json" \
-d "{\"path\":\"${cfg}\"}" > /dev/null 2>&1; then
ok "成功"
else
err "API 不可达,尝试重启..."
cmd_restart "$cfg"
fi
}
# ========== 状态 ==========
cmd_status() {
if is_running; then
local pid=$(cat "$PID_FILE")
echo ""
echo " ${BOLD_GREEN}● 运行中${NC} ${DIM}PID: $pid${NC}"
echo " $SEP"
printf " ${DIM}%-10s${NC} ${CYAN}127.0.0.1:${MIXED_PORT}${NC} (HTTP/SOCKS5)\n" "代理端口"
printf " ${DIM}%-10s${NC} ${CYAN}127.0.0.1:${API_PORT}${NC}\n" "API 端口"
printf " ${DIM}%-10s${NC} ${CYAN}$(ui_url)${NC}\n" "管理面板"
printf " ${DIM}%-10s${NC} %s\n" "配置文件" "$CONFIG_FILE"
printf " ${DIM}%-10s${NC} %s\n" "日志文件" "$LOG_FILE"
local src=$(cat "$LINK_FILE" 2>/dev/null || auto_detect_source 2>/dev/null)
[[ -n "$src" ]] && printf " ${DIM}%-10s${NC} %s\n" "源文件" "$src"
echo ""
else
echo ""
echo " ${BOLD_RED}○ 已停止${NC}"
echo " $SEP"
dim "mihomo 当前未运行"
echo ""
rm -f "$PID_FILE"
fi
}
# ========== 链接源配置(查看/设置,合并原 import==========
cmd_link() {
local src="${1:-}"
if [[ -z "$src" ]]; then
echo ""
section "源文件链接"
if [[ -f "$LINK_FILE" ]]; then
info "当前链接: $(cat "$LINK_FILE")"
else
local detected=$(auto_detect_source)
if [[ -n "$detected" ]]; then
info "自动检测: $detected"
else
warn "未检测到源文件"
fi
fi
echo ""
echo -n " 请输入 YAML 文件路径 ${DIM}(Enter 取消)${NC}: "
read src
[[ -z "$src" ]] && { dim "已取消"; return 0; }
src=$(echo "$src" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//;s/^"//;s/"$//')
fi
if [[ ! -f "$src" ]]; then
err "文件不存在: $src"
return 1
fi
echo "$src" > "$LINK_FILE"
ok "已链接"
dim "源文件 $src"
echo ""
echo -n " 立即适配并启动? (y/n) "
read -q ans
echo ""
if [[ "$ans" == "y" ]]; then
macos_adapt_config "$src" || return 1
if is_running; then
cmd_restart
else
cmd_start
fi
fi
}
# ========== 日志 / 编辑 / 面板 / 修复 / 诊断 ==========
cmd_log() {
if [[ -f "$LOG_FILE" ]]; then
echo ""
section "实时日志 (Ctrl+C 退出)"
tail -f "$LOG_FILE"
else
warn "日志文件不存在"
fi
}
cmd_edit() {
info "打开编辑器: $CONFIG_FILE"
${EDITOR:-vim} "$CONFIG_FILE"
}
cmd_open() {
if is_running; then
ensure_ui_index
local url=$(ui_url)
open "$url"
ok "已打开管理面板"
dim "$url"
else
warn "mihomo 未运行,请先启动"
fi
}
cmd_cleanup() {
network_restore true
}
cmd_doctor() {
network_health_check
}
# ========== 关闭 Clash Verge 后台服务 ==========
cmd_verge_off() {
echo ""
section "关闭 Clash Verge"
local svc_pid=$(pgrep -f "clash-verge-service" 2>/dev/null)
if [[ -n "$svc_pid" ]]; then
info "发现 Clash Verge 服务 (PID: $svc_pid)"
echo -n " 正在停止 ... "
sudo kill "$svc_pid" 2>/dev/null
sleep 1
if pgrep -f "clash-verge-service" >/dev/null 2>&1; then
sudo kill -9 "$svc_pid" 2>/dev/null
fi
ok "已停止"
else
ok "Clash Verge 服务未运行"
fi
echo ""
echo -n " 是否同时卸载开机自启? (y/n) "
read -q ans
echo ""
if [[ "$ans" == "y" ]]; then
sudo /bin/launchctl unload /Library/LaunchDaemons/io.github.clash-verge-rev.clash-verge-rev.plist 2>/dev/null
ok "已卸载开机自启"
fi
echo ""
ok "现在可以纯用 clash 脚本管理了"
echo ""
}
# ========== 帮助 ==========
show_help() {
echo ""
echo " ${BOLD_PURPLE}Mihomo Kernel Manager${NC} ${DIM}— macOS${NC}"
echo " $SEP"
echo ""
echo " ${BOLD}用法:${NC}"
echo " ${CYAN}clash <command>${NC} 执行子命令"
echo " ${CYAN}clash${NC} 进入交互菜单"
echo " ${CYAN}clash help${NC} 显示本帮助"
echo ""
echo " ${BOLD}核心:${NC}"
printf " ${CYAN}%-14s${NC} %s\n" "start [file]" "启动(自动适配 macOS 配置)"
printf " ${CYAN}%-14s${NC} %s\n" "stop" "停止并恢复网络"
printf " ${CYAN}%-14s${NC} %s\n" "restart" "重启"
printf " ${CYAN}%-14s${NC} %s\n" "status" "查看运行状态"
printf " ${CYAN}%-14s${NC} %s\n" "reload [file]" "热重载配置"
printf " ${CYAN}%-14s${NC} %s\n" "open" "打开管理面板"
printf " ${CYAN}%-14s${NC} %s\n" "log" "实时日志"
echo ""
echo " ${BOLD}配置:${NC}"
printf " ${CYAN}%-14s${NC} %s\n" "link [file]" "查看/设置源 YAML(含原 import"
printf " ${CYAN}%-14s${NC} %s\n" "edit" "编辑当前配置"
echo ""
echo " ${BOLD}网络:${NC}"
printf " ${CYAN}%-14s${NC} %s\n" "cleanup" "网络修复(清理残留 TUN/路由/代理/DNS"
printf " ${CYAN}%-14s${NC} %s\n" "doctor" "网络诊断"
echo ""
echo " ${BOLD}系统:${NC}"
printf " ${CYAN}%-14s${NC} %s\n" "verge-off" "关闭 Clash Verge 后台服务"
echo ""
echo " ${BOLD}示例:${NC}"
dim "clash start"
dim "clash link ~/Mihomo/config.yaml"
dim "clash cleanup"
echo ""
}
# ========== 交互菜单 ==========
show_menu() {
clear
echo ""
echo " ${BOLD_PURPLE}Mihomo Kernel Manager${NC} ${DIM}— macOS${NC}"
echo " $SEP"
if is_running; then
printf " ${BOLD_GREEN}● 运行中${NC} ${DIM}PID: %s${NC} ${DIM}代理: %s${NC} ${DIM}API: %s${NC}\n" \
"$(cat "$PID_FILE" 2>/dev/null)" "${MIXED_PORT}" "${API_PORT}"
else
echo " ${BOLD_RED}○ 已停止${NC}"
fi
echo ""
echo " ${BOLD}1${NC} 启动 ${BOLD}5${NC} 状态"
echo " ${BOLD}2${NC} 停止 ${BOLD}6${NC} 实时日志"
echo " ${BOLD}3${NC} 打开面板 ${BOLD}7${NC} 热重载"
echo " ${BOLD}4${NC} 重启 ${BOLD}8${NC} 网络修复"
echo " ${BOLD}0${NC} 退出"
echo ""
echo -n " ${BOLD}${NC} "
}
interactive_menu() {
while true; do
show_menu
read choice
echo ""
case "$choice" in
1) cmd_start ;;
2) cmd_stop ;;
3) cmd_open ;;
4) cmd_restart ;;
5) cmd_status ;;
6) cmd_log ;;
7) cmd_reload ;;
8) cmd_cleanup ;;
0|q|Q)
echo " ${DIM}再见 👋${NC}"
echo ""
break
;;
*) warn "无效选项,请输入 0-8" ;;
esac
echo ""
echo -n " 按 Enter 返回菜单..."
read
done
}
# ========== 入口 ==========
case "${1:-}" in
start|s) cmd_start "$2" ;;
stop) cmd_stop ;;
restart|r) cmd_restart "$2" ;;
reload|rl) cmd_reload "$2" ;;
status|st) cmd_status ;;
log|l) cmd_log ;;
link|lk) cmd_link "$2" ;;
open|o) cmd_open ;;
edit|e) cmd_edit ;;
cleanup|c) cmd_cleanup ;;
doctor|d) cmd_doctor ;;
verge-off) cmd_verge_off ;;
help|-h|--help|h) show_help ;;
*) interactive_menu ;;
esac