mirror of
https://github.com/wnlen/clash-for-linux.git
synced 2026-10-10 12:13:04 +08:00
@@ -157,6 +157,24 @@ clashctl sub rename <旧名称> <新名称>
|
||||
clashctl sub remove <名称>
|
||||
```
|
||||
|
||||
WSL / 普通用户如果无权写入 `/etc/environment`,`clashon` 会自动降级:运行时照常启动,当前 Shell 代理变量生效;系统代理持久接管和开机代理保持不可用。
|
||||
|
||||
### 本地订阅
|
||||
|
||||
可以先手动下载 Clash 格式的订阅 YAML 文件到本地,再通过 `file://` 添加:
|
||||
|
||||
格式:`clashctl add "file:///绝对路径/xxx.yaml" 名称`
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.config/clash-for-linux/subscriptions
|
||||
curl -L "https://example.com/sub.yaml" -o ~/.config/clash-for-linux/subscriptions/home.yaml
|
||||
clashctl add "file://$HOME/.config/clash-for-linux/subscriptions/home.yaml" home
|
||||
clashctl use home
|
||||
clashon
|
||||
```
|
||||
|
||||
`file://` 只支持 Clash 本地 YAML 配置,不支持 convert 格式订阅。如果订阅源需要转换,请先转换为 Clash YAML 后再保存到本地文件。
|
||||
|
||||
------
|
||||
|
||||
## 🏗️ 架构设计架构简述
|
||||
|
||||
+58
-3
@@ -46,6 +46,14 @@ _clash_alias_state_file() {
|
||||
echo "$(_clash_alias_project_dir)/runtime/shell-proxy.env"
|
||||
}
|
||||
|
||||
_clash_alias_runtime_config_file() {
|
||||
echo "$(_clash_alias_project_dir)/runtime/config.yaml"
|
||||
}
|
||||
|
||||
_clash_alias_yq_bin() {
|
||||
echo "$(_clash_alias_project_dir)/runtime/bin/yq"
|
||||
}
|
||||
|
||||
_clash_alias_set_persist_enabled() {
|
||||
local enabled="$1"
|
||||
local state_file
|
||||
@@ -72,6 +80,11 @@ _clash_alias_print_sep() {
|
||||
}
|
||||
|
||||
_clash_alias_proxy_on() {
|
||||
_clashctl_real proxy on >/dev/null || return $?
|
||||
_clash_alias_export_proxy || return $?
|
||||
}
|
||||
|
||||
_clash_alias_proxy_on_system() {
|
||||
_clashctl_real proxy on >/dev/null || return $?
|
||||
_clash_alias_export_system_proxy || return $?
|
||||
}
|
||||
@@ -102,6 +115,44 @@ _clash_alias_export_system_proxy() {
|
||||
[ -n "${all_url:-}" ] || all_url="${http_url/http:\/\//socks5://}"
|
||||
[ -n "${no_proxy:-}" ] || no_proxy="127.0.0.1,localhost,::1"
|
||||
|
||||
_clash_alias_export_proxy_values "$http_url" "$https_url" "$all_url" "$no_proxy"
|
||||
}
|
||||
|
||||
_clash_alias_runtime_proxy_port() {
|
||||
local config_file yq_bin port
|
||||
|
||||
config_file="$(_clash_alias_runtime_config_file)"
|
||||
[ -s "$config_file" ] || return 1
|
||||
|
||||
yq_bin="$(_clash_alias_yq_bin)"
|
||||
if [ -x "$yq_bin" ]; then
|
||||
port="$("$yq_bin" eval '.["mixed-port"] // .port // ""' "$config_file" 2>/dev/null | head -n 1)"
|
||||
else
|
||||
port="$(sed -nE 's/^[[:space:]]*(mixed-port|port):[[:space:]]*"?([0-9]+)"?[[:space:]]*$/\2/p' "$config_file" | head -n 1)"
|
||||
fi
|
||||
|
||||
[ -n "${port:-}" ] && [ "$port" != "null" ] || return 1
|
||||
echo "$port"
|
||||
}
|
||||
|
||||
_clash_alias_export_runtime_proxy() {
|
||||
local port host http_url all_url no_proxy
|
||||
|
||||
port="$(_clash_alias_runtime_proxy_port)" || return $?
|
||||
host="${CLASH_PROXY_HOST:-127.0.0.1}"
|
||||
http_url="http://${host}:${port}"
|
||||
all_url="socks5://${host}:${port}"
|
||||
no_proxy="${NO_PROXY_DEFAULT:-127.0.0.1,localhost,::1}"
|
||||
|
||||
_clash_alias_export_proxy_values "$http_url" "$http_url" "$all_url" "$no_proxy"
|
||||
}
|
||||
|
||||
_clash_alias_export_proxy_values() {
|
||||
local http_url="$1"
|
||||
local https_url="$2"
|
||||
local all_url="$3"
|
||||
local no_proxy="$4"
|
||||
|
||||
export http_proxy="$http_url"
|
||||
export https_proxy="$https_url"
|
||||
export HTTP_PROXY="$http_url"
|
||||
@@ -112,6 +163,10 @@ _clash_alias_export_system_proxy() {
|
||||
export NO_PROXY="$no_proxy"
|
||||
}
|
||||
|
||||
_clash_alias_export_proxy() {
|
||||
_clash_alias_export_system_proxy || _clash_alias_export_runtime_proxy
|
||||
}
|
||||
|
||||
_clash_alias_unset_shell_proxy() {
|
||||
unset \
|
||||
http_proxy https_proxy HTTP_PROXY HTTPS_PROXY \
|
||||
@@ -131,7 +186,7 @@ _clash_alias_prepare_on() {
|
||||
|
||||
_clash_alias_after_on() {
|
||||
_clash_alias_set_persist_enabled "true"
|
||||
_clash_alias_export_system_proxy || return $?
|
||||
_clash_alias_export_proxy || return $?
|
||||
}
|
||||
|
||||
_clash_alias_run_on() {
|
||||
@@ -165,7 +220,7 @@ _clash_alias_run_on() {
|
||||
if [ -s "$on_output" ]; then
|
||||
sed 's/^/ /' "$on_output" >&2
|
||||
fi
|
||||
elif _clash_alias_proxy_on; then
|
||||
elif _clash_alias_proxy_on_system; then
|
||||
echo "🚨 clashctl on 返回非 0,已通过 proxy on 继续同步当前 Shell(底层返回码:$on_rc)" >&2
|
||||
if [ -s "$on_output" ]; then
|
||||
sed 's/^/ /' "$on_output" >&2
|
||||
@@ -201,7 +256,7 @@ _clash_alias_run_off() {
|
||||
|
||||
_clash_alias_auto_restore_proxy() {
|
||||
_clash_alias_persist_enabled || return 0
|
||||
_clash_alias_export_system_proxy || return 0
|
||||
_clash_alias_export_proxy || return 0
|
||||
|
||||
echo "♻️ 已恢复当前 shell 代理环境(来自持久化状态)"
|
||||
return 0
|
||||
|
||||
+435
-24
@@ -213,6 +213,7 @@ print_on_feedback() {
|
||||
cmd_on() {
|
||||
local relay_switch
|
||||
local relay_switch_file relay_err_file relay_rc
|
||||
local system_proxy_rc system_proxy_degraded="false"
|
||||
local already_on="false"
|
||||
|
||||
trap 'rc=$?; ui_error "开启代理失败:cmd_on 在第 ${LINENO} 行执行失败:${BASH_COMMAND}(返回码:${rc})"; ui_next "clashctl logs service"; exit "$rc"' ERR
|
||||
@@ -260,16 +261,32 @@ cmd_on() {
|
||||
rm -f "$relay_switch_file" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
if ! system_proxy_enable; then
|
||||
service_stop >/dev/null 2>&1 || true
|
||||
die_state "当前环境不支持系统代理接管(仅支持可写 /etc/environment)" "clashctl doctor"
|
||||
if system_proxy_enable; then
|
||||
system_proxy_degraded="false"
|
||||
else
|
||||
system_proxy_rc=$?
|
||||
system_proxy_degraded="true"
|
||||
write_runtime_value "RUNTIME_BOOT_PROXY_KEEP" "false" 2>/dev/null || true
|
||||
|
||||
if [ "$system_proxy_rc" -eq 2 ]; then
|
||||
ui_warn "当前环境不支持系统代理持久接管,仅当前 Shell 生效"
|
||||
ui_next "开机代理保持不可用;如需持久接管,请使用可写的 $(system_proxy_env_file)"
|
||||
else
|
||||
ui_warn "系统代理持久接管写入失败,仅当前 Shell 生效"
|
||||
ui_next "clashctl doctor"
|
||||
fi
|
||||
ui_blank
|
||||
fi
|
||||
|
||||
load_system_state
|
||||
print_on_feedback
|
||||
|
||||
if [ "${CLASH_ALIAS_CALL:-0}" != "1" ]; then
|
||||
ui_warn "当前通过 clashctl 子进程执行,已开启系统代理,但不会修改当前 Shell 代理变量"
|
||||
if [ "$system_proxy_degraded" = "true" ]; then
|
||||
ui_warn "当前通过 clashctl 子进程执行,不能修改当前 Shell 代理变量"
|
||||
else
|
||||
ui_warn "当前通过 clashctl 子进程执行,已开启系统代理,但不会修改当前 Shell 代理变量"
|
||||
fi
|
||||
ui_next "需要当前 Shell 立即生效:source shell 入口后执行 clashon,或重新打开终端"
|
||||
ui_blank
|
||||
fi
|
||||
@@ -285,11 +302,22 @@ cmd_on() {
|
||||
}
|
||||
|
||||
cmd_off() {
|
||||
local system_proxy_rc
|
||||
|
||||
prepare
|
||||
service_stop
|
||||
|
||||
if ! system_proxy_disable; then
|
||||
die_state "当前环境不支持系统代理关闭(仅支持可写 /etc/environment)" "clashctl doctor"
|
||||
if system_proxy_disable; then
|
||||
:
|
||||
else
|
||||
system_proxy_rc=$?
|
||||
write_runtime_value "RUNTIME_BOOT_PROXY_KEEP" "false" 2>/dev/null || true
|
||||
if [ "$system_proxy_rc" -eq 2 ]; then
|
||||
ui_warn "当前环境不支持清理系统代理持久块,已继续关闭运行时"
|
||||
else
|
||||
ui_warn "系统代理持久块清理失败,已继续关闭运行时"
|
||||
ui_next "clashctl doctor"
|
||||
fi
|
||||
fi
|
||||
ui_blank
|
||||
echo "🧹 系统代理已关闭"
|
||||
@@ -578,6 +606,215 @@ status_build_effective_status() {
|
||||
system_state_build_status 2>/dev/null || true
|
||||
}
|
||||
|
||||
runtime_mixed_port_bind_failure_line() {
|
||||
local log_file="$LOG_DIR/mihomo.out.log"
|
||||
local mixed_port line
|
||||
|
||||
runtime_config_exists 2>/dev/null || return 1
|
||||
status_is_running 2>/dev/null && return 1
|
||||
[ -f "$log_file" ] || return 1
|
||||
|
||||
mixed_port="$(status_read_mixed_port 2>/dev/null || true)"
|
||||
if [ -n "${mixed_port:-}" ] && [ "$mixed_port" != "null" ]; then
|
||||
line="$(grep -Ei "Start Mixed.*server error: listen tcp .*:${mixed_port}:.*(operation not permitted|permission denied|address already in use)" "$log_file" 2>/dev/null | tail -n 1 || true)"
|
||||
fi
|
||||
|
||||
if [ -z "${line:-}" ]; then
|
||||
line="$(grep -Ei 'Start Mixed.*server error: listen tcp .*:.*(operation not permitted|permission denied|address already in use)' "$log_file" 2>/dev/null | tail -n 1 || true)"
|
||||
fi
|
||||
|
||||
[ -n "${line:-}" ] || return 1
|
||||
printf '%s\n' "$line"
|
||||
}
|
||||
|
||||
runtime_mixed_port_bind_failure_kind() {
|
||||
local line lower
|
||||
|
||||
line="$(runtime_mixed_port_bind_failure_line 2>/dev/null || true)"
|
||||
[ -n "${line:-}" ] || return 1
|
||||
|
||||
lower="$(printf '%s' "$line" | tr '[:upper:]' '[:lower:]')"
|
||||
case "$lower" in
|
||||
*"address already in use"*)
|
||||
echo "address_in_use"
|
||||
;;
|
||||
*"operation not permitted"*|*"permission denied"*)
|
||||
echo "bind_denied"
|
||||
;;
|
||||
*)
|
||||
echo "bind_failed"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
runtime_mixed_port_bind_failure_text() {
|
||||
case "$(runtime_mixed_port_bind_failure_kind 2>/dev/null || true)" in
|
||||
address_in_use) echo "mixed-port 端口被占用" ;;
|
||||
bind_denied) echo "mixed-port 绑定被拒绝" ;;
|
||||
bind_failed) echo "mixed-port 绑定失败" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
runtime_mixed_port_bind_failure_port() {
|
||||
local port line
|
||||
|
||||
port="$(status_read_mixed_port 2>/dev/null || true)"
|
||||
if [ -n "${port:-}" ] && [ "$port" != "null" ]; then
|
||||
echo "$port"
|
||||
return 0
|
||||
fi
|
||||
|
||||
line="$(runtime_mixed_port_bind_failure_line 2>/dev/null || true)"
|
||||
[ -n "${line:-}" ] || return 1
|
||||
|
||||
printf '%s\n' "$line" | grep -Eo ':[0-9]+:' | tail -n 1 | tr -d ':'
|
||||
}
|
||||
|
||||
runtime_mihomo_log_port_listened() {
|
||||
local keyword="$1"
|
||||
local port="$2"
|
||||
local log_file="$LOG_DIR/mihomo.out.log"
|
||||
local line
|
||||
|
||||
[ -n "${keyword:-}" ] || return 1
|
||||
[ -n "${port:-}" ] || return 1
|
||||
[ -f "$log_file" ] || return 1
|
||||
|
||||
line="$(grep -Ei "(${keyword}).*(listen|listening|start|started).*[:.]${port}([^0-9]|$)|[:.]${port}([^0-9]|$).*(listen|listening|start|started).*(${keyword})" "$log_file" 2>/dev/null | tail -n 1 || true)"
|
||||
[ -n "${line:-}" ]
|
||||
}
|
||||
|
||||
runtime_mixed_port_controller_listening() {
|
||||
local port
|
||||
|
||||
proxy_controller_reachable 2>/dev/null && return 0
|
||||
|
||||
port="$(runtime_config_controller_port 2>/dev/null || true)"
|
||||
[ -n "${port:-}" ] || return 1
|
||||
|
||||
runtime_mihomo_log_port_listened "RESTful API|controller" "$port"
|
||||
}
|
||||
|
||||
runtime_mixed_port_dns_listening() {
|
||||
local port
|
||||
|
||||
port="$(runtime_config_dns_port 2>/dev/null || true)"
|
||||
[ -n "${port:-}" ] || return 1
|
||||
|
||||
runtime_mihomo_log_port_listened "DNS|dns" "$port" && return 0
|
||||
status_is_running 2>/dev/null && is_port_in_use "$port"
|
||||
}
|
||||
|
||||
mixed_port_bind_environment_text() {
|
||||
local scope backend container
|
||||
|
||||
scope="$(install_env_scope 2>/dev/null || true)"
|
||||
[ -n "${scope:-}" ] || scope="${INSTALL_SCOPE:-unknown}"
|
||||
|
||||
backend="$(runtime_backend 2>/dev/null || true)"
|
||||
[ -n "${backend:-}" ] || backend="$(install_plan_backend 2>/dev/null || true)"
|
||||
[ -n "${backend:-}" ] || backend="unknown"
|
||||
|
||||
container="$(container_env_type 2>/dev/null || true)"
|
||||
[ -n "${container:-}" ] || container="$(install_env_container 2>/dev/null || true)"
|
||||
[ -n "${container:-}" ] || container="unknown"
|
||||
|
||||
echo "安装范围 ${scope},后端 ${backend},容器环境 ${container}"
|
||||
}
|
||||
|
||||
mixed_port_bind_observation_line() {
|
||||
local port dns_port
|
||||
|
||||
port="$(runtime_mixed_port_bind_failure_port 2>/dev/null || status_read_mixed_port 2>/dev/null || true)"
|
||||
dns_port="$(runtime_config_dns_port 2>/dev/null || true)"
|
||||
|
||||
if runtime_mixed_port_controller_listening 2>/dev/null; then
|
||||
echo "配置已加载,控制器已启动,仅代理端口 ${port:-unknown} 绑定失败;不是订阅/配置主链失败"
|
||||
fi
|
||||
|
||||
if [ -n "${dns_port:-}" ] && runtime_mixed_port_dns_listening 2>/dev/null; then
|
||||
echo "DNS 端口 ${dns_port} 已监听,可先聚焦 mixed-port"
|
||||
fi
|
||||
}
|
||||
|
||||
mixed_port_bind_recommendation_emit() {
|
||||
local style="$1"
|
||||
local number="$2"
|
||||
local text="$3"
|
||||
|
||||
case "$style" in
|
||||
numbered)
|
||||
echo "${number}. ${text}"
|
||||
;;
|
||||
icon)
|
||||
echo "💡 ${text}"
|
||||
;;
|
||||
*)
|
||||
echo "$text"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
mixed_port_bind_recommendation_lines() {
|
||||
local style="${1:-icon}"
|
||||
local kind port env_text n line
|
||||
|
||||
kind="$(runtime_mixed_port_bind_failure_kind 2>/dev/null || true)"
|
||||
[ -n "${kind:-}" ] || return 1
|
||||
|
||||
port="$(runtime_mixed_port_bind_failure_port 2>/dev/null || status_read_mixed_port 2>/dev/null || true)"
|
||||
env_text="$(mixed_port_bind_environment_text 2>/dev/null || echo "当前环境")"
|
||||
n=1
|
||||
|
||||
while IFS= read -r line; do
|
||||
[ -n "${line:-}" ] || continue
|
||||
mixed_port_bind_recommendation_emit "$style" "$n" "$line"
|
||||
n=$((n + 1))
|
||||
done <<EOF
|
||||
$(mixed_port_bind_observation_line 2>/dev/null || true)
|
||||
EOF
|
||||
|
||||
case "$kind" in
|
||||
bind_denied)
|
||||
mixed_port_bind_recommendation_emit "$style" "$n" "当前 ${env_text} 对端口 ${port:-unknown} 的可绑定性受限;优先修改 .env 中 MIXED_PORT 后执行 clashctl config regen"
|
||||
n=$((n + 1))
|
||||
;;
|
||||
address_in_use)
|
||||
mixed_port_bind_recommendation_emit "$style" "$n" "mixed-port ${port:-unknown} 存在端口冲突;先检查占用进程,或修改 .env 中 MIXED_PORT 后执行 clashctl config regen"
|
||||
n=$((n + 1))
|
||||
;;
|
||||
*)
|
||||
mixed_port_bind_recommendation_emit "$style" "$n" "mixed-port ${port:-unknown} 绑定失败;先查看内核日志确认端口错误"
|
||||
n=$((n + 1))
|
||||
;;
|
||||
esac
|
||||
|
||||
mixed_port_bind_recommendation_emit "$style" "$n" "保留日志线索:clashctl logs mihomo"
|
||||
}
|
||||
|
||||
mixed_port_bind_next_action() {
|
||||
local kind port
|
||||
|
||||
kind="$(runtime_mixed_port_bind_failure_kind 2>/dev/null || true)"
|
||||
port="$(runtime_mixed_port_bind_failure_port 2>/dev/null || status_read_mixed_port 2>/dev/null || true)"
|
||||
|
||||
case "$kind" in
|
||||
bind_denied)
|
||||
echo "修改 .env 中 MIXED_PORT 后执行 clashctl config regen"
|
||||
;;
|
||||
address_in_use)
|
||||
echo "检查 ${port:-mixed-port} 占用,或修改 .env 中 MIXED_PORT 后执行 clashctl config regen"
|
||||
;;
|
||||
bind_failed)
|
||||
echo "clashctl logs mihomo"
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
system_state_runtime_status() {
|
||||
if status_is_running; then
|
||||
if proxy_controller_reachable 2>/dev/null; then
|
||||
@@ -620,6 +857,11 @@ system_state_subscription_status() {
|
||||
system_state_risk_level() {
|
||||
local risk
|
||||
|
||||
if runtime_mixed_port_bind_failure_kind >/dev/null 2>&1; then
|
||||
echo "high"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if status_is_running 2>/dev/null && ! proxy_controller_reachable 2>/dev/null; then
|
||||
echo "high"
|
||||
return 0
|
||||
@@ -646,7 +888,7 @@ system_state_summary() {
|
||||
local runtime_status build_status subscription_status risk_level
|
||||
local config_source fallback_used build_applied
|
||||
local tun_enabled tun_effective tun_container_mode tun_kernel_support
|
||||
local overall
|
||||
local bind_failure_kind overall
|
||||
|
||||
runtime_status="$(system_state_runtime_status)"
|
||||
build_status="$(system_state_build_status)"
|
||||
@@ -655,6 +897,7 @@ system_state_summary() {
|
||||
config_source="$(status_runtime_config_source)"
|
||||
fallback_used="$(runtime_last_fallback_used 2>/dev/null || true)"
|
||||
build_applied="$(status_runtime_build_applied)"
|
||||
bind_failure_kind="$(runtime_mixed_port_bind_failure_kind 2>/dev/null || true)"
|
||||
|
||||
tun_enabled="$(status_tun_enabled)"
|
||||
tun_effective="$(status_tun_effective_status)"
|
||||
@@ -665,6 +908,8 @@ system_state_summary() {
|
||||
&& [ "$build_status" = "success" ] \
|
||||
&& [ "$subscription_status" = "healthy" ]; then
|
||||
overall="ready"
|
||||
elif [ "$runtime_status" = "stopped" ] && [ -n "${bind_failure_kind:-}" ]; then
|
||||
overall="broken"
|
||||
elif [ "$runtime_status" = "stopped" ]; then
|
||||
overall="stopped"
|
||||
elif [ "$build_status" = "failed" ] || [ "$build_status" = "blocked" ] || [ "$subscription_status" = "invalid" ] || [ "$subscription_status" = "missing" ]; then
|
||||
@@ -679,6 +924,7 @@ RUNTIME_STATE=$runtime_status
|
||||
BUILD_STATE=$build_status
|
||||
SUBSCRIPTION_STATE=$subscription_status
|
||||
RISK_LEVEL=$risk_level
|
||||
BIND_FAILURE_STATE=${bind_failure_kind:-none}
|
||||
CONFIG_SOURCE=${config_source:-unknown}
|
||||
FALLBACK_USED=${fallback_used:-false}
|
||||
BUILD_APPLIED=${build_applied:-unknown}
|
||||
@@ -710,7 +956,11 @@ system_state_connectivity_text() {
|
||||
echo "未连接(代理未启动)"
|
||||
;;
|
||||
broken)
|
||||
echo "不可用(配置或订阅异常)"
|
||||
if [ "${BIND_FAILURE_STATE:-none}" != "none" ]; then
|
||||
echo "不可用(mixed-port 绑定失败)"
|
||||
else
|
||||
echo "不可用(配置或订阅异常)"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "未知"
|
||||
@@ -735,6 +985,11 @@ system_state_default_action() {
|
||||
|
||||
case "$SYSTEM_STATE" in
|
||||
stopped)
|
||||
if [ "${BIND_FAILURE_STATE:-none}" != "none" ]; then
|
||||
mixed_port_bind_next_action 2>/dev/null || echo "clashctl logs mihomo"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ "$SUBSCRIPTION_STATE" = "missing" ]; then
|
||||
echo "clashctl add <订阅链接>"
|
||||
else
|
||||
@@ -742,6 +997,11 @@ system_state_default_action() {
|
||||
fi
|
||||
;;
|
||||
broken)
|
||||
if [ "${BIND_FAILURE_STATE:-none}" != "none" ]; then
|
||||
mixed_port_bind_next_action 2>/dev/null || echo "clashctl logs mihomo"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ "$SUBSCRIPTION_STATE" = "missing" ]; then
|
||||
echo "clashctl add <订阅链接>"
|
||||
else
|
||||
@@ -765,14 +1025,19 @@ system_state_default_action() {
|
||||
}
|
||||
|
||||
system_state_problem_lines() {
|
||||
local mixed_port
|
||||
|
||||
load_system_state
|
||||
mixed_port="$(runtime_mixed_port_bind_failure_port 2>/dev/null || status_read_mixed_port 2>/dev/null || true)"
|
||||
|
||||
case "$SYSTEM_STATE" in
|
||||
ready)
|
||||
return 0
|
||||
;;
|
||||
stopped)
|
||||
if [ "$SUBSCRIPTION_STATE" = "missing" ]; then
|
||||
if [ "${BIND_FAILURE_STATE:-none}" != "none" ]; then
|
||||
echo "• $(runtime_mixed_port_bind_failure_text)${mixed_port:+:${mixed_port}}"
|
||||
elif [ "$SUBSCRIPTION_STATE" = "missing" ]; then
|
||||
echo "• 当前没有可用订阅"
|
||||
else
|
||||
echo "• 代理内核未启动"
|
||||
@@ -783,6 +1048,7 @@ system_state_problem_lines() {
|
||||
[ "$SUBSCRIPTION_STATE" = "degraded" ] && echo "• 当前主订阅健康异常"
|
||||
;;
|
||||
broken)
|
||||
[ "${BIND_FAILURE_STATE:-none}" != "none" ] && echo "• $(runtime_mixed_port_bind_failure_text)${mixed_port:+:${mixed_port}}"
|
||||
[ "$BUILD_STATE" = "failed" ] && echo "• 最近一次编译失败"
|
||||
[ "$SUBSCRIPTION_STATE" = "missing" ] && echo "• 当前没有主订阅"
|
||||
[ "$SUBSCRIPTION_STATE" = "invalid" ] && echo "• 当前主订阅无效"
|
||||
@@ -795,6 +1061,11 @@ system_state_problem_lines() {
|
||||
system_state_recommendation_lines() {
|
||||
load_system_state
|
||||
|
||||
if [ "${BIND_FAILURE_STATE:-none}" != "none" ]; then
|
||||
mixed_port_bind_recommendation_lines numbered
|
||||
return 0
|
||||
fi
|
||||
|
||||
case "$SYSTEM_STATE" in
|
||||
ready)
|
||||
echo "1. clashctl select"
|
||||
@@ -1457,11 +1728,14 @@ status_risk_reason_lines() {
|
||||
local build_block_reason build_block_time
|
||||
local fallback_used fallback_time fallback_reason
|
||||
local tun_enabled tun_effective tun_container_mode tun_kernel_support tun_verify_reason
|
||||
local bind_failure_text mixed_port
|
||||
|
||||
build_status="$(status_build_last_status 2>/dev/null || true)"
|
||||
active="$(active_subscription_name 2>/dev/null || true)"
|
||||
controller_ok="false"
|
||||
proxy_controller_reachable 2>/dev/null && controller_ok="true"
|
||||
bind_failure_text="$(runtime_mixed_port_bind_failure_text 2>/dev/null || true)"
|
||||
mixed_port="$(runtime_mixed_port_bind_failure_port 2>/dev/null || status_read_mixed_port 2>/dev/null || true)"
|
||||
|
||||
build_block_reason="$(status_runtime_last_build_block_reason 2>/dev/null || true)"
|
||||
build_block_time="$(status_runtime_last_build_block_time 2>/dev/null || true)"
|
||||
@@ -1475,7 +1749,12 @@ status_risk_reason_lines() {
|
||||
tun_verify_reason="$(status_tun_last_verify_reason 2>/dev/null || true)"
|
||||
|
||||
if ! status_is_running; then
|
||||
echo "• 代理内核未启动"
|
||||
if [ -n "${bind_failure_text:-}" ]; then
|
||||
echo "• ${bind_failure_text}${mixed_port:+:${mixed_port}}"
|
||||
mixed_port_bind_observation_line | sed 's/^/• /'
|
||||
else
|
||||
echo "• 代理内核未启动"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$controller_ok" = "false" ] && status_is_running; then
|
||||
@@ -1535,8 +1814,15 @@ status_recommendation_lines() {
|
||||
|
||||
status_current_proxy_brief() {
|
||||
local default_group default_current fallback_group fallback_current
|
||||
local bind_failure_text
|
||||
|
||||
if ! status_is_running; then
|
||||
bind_failure_text="$(runtime_mixed_port_bind_failure_text 2>/dev/null || true)"
|
||||
if [ -n "${bind_failure_text:-}" ]; then
|
||||
echo "$bind_failure_text"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "未启动"
|
||||
return 0
|
||||
fi
|
||||
@@ -1576,8 +1862,25 @@ system_proxy_supported_state() {
|
||||
|
||||
connectivity_issue_code() {
|
||||
local active group_count
|
||||
local bind_failure_kind
|
||||
|
||||
if ! status_is_running; then
|
||||
bind_failure_kind="$(runtime_mixed_port_bind_failure_kind 2>/dev/null || true)"
|
||||
case "${bind_failure_kind:-}" in
|
||||
bind_denied)
|
||||
echo "mixed_port_bind_denied"
|
||||
return 0
|
||||
;;
|
||||
address_in_use)
|
||||
echo "mixed_port_address_in_use"
|
||||
return 0
|
||||
;;
|
||||
bind_failed)
|
||||
echo "mixed_port_bind_failed"
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "runtime_stopped"
|
||||
return 0
|
||||
fi
|
||||
@@ -1630,8 +1933,16 @@ connectivity_issue_code() {
|
||||
}
|
||||
|
||||
connectivity_issue_text() {
|
||||
case "$(connectivity_issue_code)" in
|
||||
local issue port
|
||||
|
||||
issue="$(connectivity_issue_code)"
|
||||
port="$(runtime_mixed_port_bind_failure_port 2>/dev/null || status_read_mixed_port 2>/dev/null || true)"
|
||||
|
||||
case "$issue" in
|
||||
ok) echo "可用(代理链路已闭环)" ;;
|
||||
mixed_port_bind_denied) echo "不可用(mixed-port ${port:-unknown} 绑定被拒绝)" ;;
|
||||
mixed_port_address_in_use) echo "不可用(mixed-port ${port:-unknown} 端口被占用)" ;;
|
||||
mixed_port_bind_failed) echo "不可用(mixed-port ${port:-unknown} 绑定失败)" ;;
|
||||
runtime_stopped) echo "不可用(代理内核未启动)" ;;
|
||||
controller_unreachable) echo "异常(内核已运行,但控制器不可访问)" ;;
|
||||
config_invalid) echo "异常(当前运行配置不可用)" ;;
|
||||
@@ -1652,6 +1963,9 @@ connectivity_next_action() {
|
||||
runtime_stopped)
|
||||
echo "clashon"
|
||||
;;
|
||||
mixed_port_bind_denied|mixed_port_address_in_use|mixed_port_bind_failed)
|
||||
mixed_port_bind_next_action 2>/dev/null || echo "clashctl logs mihomo"
|
||||
;;
|
||||
controller_unreachable)
|
||||
echo "clashctl doctor"
|
||||
;;
|
||||
@@ -1683,6 +1997,7 @@ connectivity_evidence_lines() {
|
||||
local runtime_running controller_ok build_status subscription_status
|
||||
local group_count expected_proxy actual_proxy active config_source
|
||||
local system_proxy_state system_proxy_supported_text
|
||||
local bind_failure_kind bind_failure_line
|
||||
|
||||
if status_is_running; then
|
||||
runtime_running="true"
|
||||
@@ -1705,9 +2020,13 @@ connectivity_evidence_lines() {
|
||||
actual_proxy="$(system_proxy_http_value 2>/dev/null || true)"
|
||||
system_proxy_state="$(system_proxy_status 2>/dev/null || echo off)"
|
||||
system_proxy_supported_text="$(system_proxy_supported_state)"
|
||||
bind_failure_kind="$(runtime_mixed_port_bind_failure_kind 2>/dev/null || true)"
|
||||
bind_failure_line="$(runtime_mixed_port_bind_failure_line 2>/dev/null || true)"
|
||||
|
||||
echo "• runtime_running = ${runtime_running:-false}"
|
||||
echo "• controller_reachable = ${controller_ok:-false}"
|
||||
[ -n "${bind_failure_kind:-}" ] && echo "• mixed_port_bind_failure = ${bind_failure_kind}"
|
||||
[ -n "${bind_failure_line:-}" ] && echo "• mixed_port_bind_log = ${bind_failure_line}"
|
||||
echo "• build_status = ${build_status:-unknown}"
|
||||
echo "• subscription_status = ${subscription_status:-unknown}"
|
||||
echo "• active_subscription = ${active:-unset}"
|
||||
@@ -1904,7 +2223,7 @@ print_status_summary_compact() {
|
||||
local profile mixed_port controller controller_lan controller_public
|
||||
local running_text user_connectivity user_risk current_proxy_brief system_proxy_text
|
||||
local current_active dashboard_text dashboard_source_text dashboard_policy_text secret_text
|
||||
local tun_text
|
||||
local tun_text bind_failure_text next_action
|
||||
|
||||
profile="$(show_active_profile 2>/dev/null || true)"
|
||||
[ -n "${profile:-}" ] || profile="default"
|
||||
@@ -1915,9 +2234,12 @@ print_status_summary_compact() {
|
||||
controller_public="$(status_read_controller_public 2>/dev/null || true)"
|
||||
current_active="$(active_subscription_name 2>/dev/null || true)"
|
||||
tun_text="$(status_tun_effective_text)"
|
||||
bind_failure_text="$(runtime_mixed_port_bind_failure_text 2>/dev/null || true)"
|
||||
|
||||
if status_is_running; then
|
||||
running_text="🐱 已开启"
|
||||
elif [ -n "${bind_failure_text:-}" ]; then
|
||||
running_text="❗ ${bind_failure_text}"
|
||||
else
|
||||
running_text="❗ 未开启"
|
||||
fi
|
||||
@@ -1925,6 +2247,7 @@ print_status_summary_compact() {
|
||||
user_connectivity="$(connectivity_issue_text)"
|
||||
user_risk="$(status_user_risk_text)"
|
||||
current_proxy_brief="$(status_current_proxy_brief)"
|
||||
next_action="$(system_state_default_action 2>/dev/null || echo 'clashctl status --verbose')"
|
||||
if system_proxy_supported; then
|
||||
system_proxy_text="$(system_proxy_status 2>/dev/null || echo off)"
|
||||
else
|
||||
@@ -1961,7 +2284,11 @@ print_status_summary_compact() {
|
||||
echo "📡 当前订阅:${current_active:-未设置}"
|
||||
echo "🚀 当前节点:$current_proxy_brief"
|
||||
echo "🚨 当前风险:$user_risk"
|
||||
echo "👉 clashctl select 切换节点"
|
||||
if [ "$next_action" = "clashctl select" ]; then
|
||||
echo "👉 clashctl select 切换节点"
|
||||
else
|
||||
echo "👉 下一步:$next_action"
|
||||
fi
|
||||
echo
|
||||
|
||||
echo "【核心入口】"
|
||||
@@ -2013,6 +2340,7 @@ print_status_summary_verbose() {
|
||||
local install_backend_text install_container_text install_verify_text port_adjustment_text
|
||||
local tun_enabled tun_effective tun_stack tun_container_text tun_kernel_text tun_verify_result tun_verify_reason tun_verify_time
|
||||
local system_proxy_text dashboard_text dashboard_source_text dashboard_policy_text secret_text
|
||||
local bind_failure_text next_action
|
||||
|
||||
profile="$(show_active_profile 2>/dev/null || true)"
|
||||
[ -n "${profile:-}" ] || profile="default"
|
||||
@@ -2043,9 +2371,12 @@ print_status_summary_verbose() {
|
||||
build_applied="$(status_runtime_build_applied 2>/dev/null || true)"
|
||||
build_applied_time="$(status_runtime_build_applied_time 2>/dev/null || true)"
|
||||
build_applied_reason="$(status_runtime_build_applied_reason 2>/dev/null || true)"
|
||||
bind_failure_text="$(runtime_mixed_port_bind_failure_text 2>/dev/null || true)"
|
||||
|
||||
if status_is_running; then
|
||||
running_text="🐱 已开启"
|
||||
elif [ -n "${bind_failure_text:-}" ]; then
|
||||
running_text="❗ ${bind_failure_text}"
|
||||
else
|
||||
running_text="❗ 未开启"
|
||||
fi
|
||||
@@ -2060,6 +2391,7 @@ print_status_summary_verbose() {
|
||||
user_connectivity="$(connectivity_issue_text)"
|
||||
user_risk="$(status_user_risk_text)"
|
||||
current_proxy_brief="$(status_current_proxy_brief)"
|
||||
next_action="$(system_state_default_action 2>/dev/null || echo 'clashctl status --verbose')"
|
||||
install_backend_text="$(status_runtime_backend_text)"
|
||||
install_container_text="$(status_container_mode_text)"
|
||||
install_verify_text="$(status_install_verify_brief)"
|
||||
@@ -2109,7 +2441,11 @@ print_status_summary_verbose() {
|
||||
echo "📡 当前订阅:${current_active:-未设置}"
|
||||
echo "🚀 当前节点:$current_proxy_brief"
|
||||
echo "🚨 当前风险:$user_risk"
|
||||
echo "👉 clashctl select 切换节点"
|
||||
if [ "$next_action" = "clashctl select" ]; then
|
||||
echo "👉 clashctl select 切换节点"
|
||||
else
|
||||
echo "👉 下一步:$next_action"
|
||||
fi
|
||||
echo
|
||||
|
||||
echo "【核心入口】"
|
||||
@@ -3055,7 +3391,7 @@ doctor_service() {
|
||||
}
|
||||
|
||||
doctor_ports() {
|
||||
local config_file mixed_port controller controller_port
|
||||
local config_file mixed_port controller controller_port dns_port bind_failure_kind
|
||||
|
||||
doctor_print_title "端口检查"
|
||||
|
||||
@@ -3081,9 +3417,17 @@ doctor_ports() {
|
||||
mixed_port="$("$(yq_bin)" eval '.["mixed-port"] // .port // ""' "$config_file" 2>/dev/null | head -n 1)"
|
||||
controller="$("$(yq_bin)" eval '.["external-controller"] // ""' "$config_file" 2>/dev/null | head -n 1)"
|
||||
controller_port="${controller##*:}"
|
||||
dns_port="$(runtime_config_dns_port 2>/dev/null || true)"
|
||||
bind_failure_kind="$(runtime_mixed_port_bind_failure_kind 2>/dev/null || true)"
|
||||
|
||||
if [ -n "${mixed_port:-}" ] && [ "$mixed_port" != "null" ]; then
|
||||
if is_port_in_use "$mixed_port"; then
|
||||
if [ "$bind_failure_kind" = "bind_denied" ]; then
|
||||
doctor_fail "mixed-port 绑定被拒绝:$mixed_port"
|
||||
elif [ "$bind_failure_kind" = "address_in_use" ]; then
|
||||
doctor_fail "mixed-port 端口被占用:$mixed_port"
|
||||
elif [ -n "${bind_failure_kind:-}" ]; then
|
||||
doctor_fail "mixed-port 绑定失败:$mixed_port"
|
||||
elif is_port_in_use "$mixed_port"; then
|
||||
doctor_ok "代理端口已监听:$mixed_port"
|
||||
else
|
||||
doctor_warn "代理端口未监听:$mixed_port"
|
||||
@@ -3102,6 +3446,16 @@ doctor_ports() {
|
||||
doctor_warn "无法检查控制器端口"
|
||||
fi
|
||||
|
||||
if [ -n "${dns_port:-}" ] && [ "$dns_port" != "null" ]; then
|
||||
if is_port_in_use "$dns_port"; then
|
||||
doctor_ok "DNS 端口已监听:$dns_port"
|
||||
else
|
||||
doctor_warn "DNS 端口未监听:$dns_port"
|
||||
fi
|
||||
else
|
||||
doctor_warn "无法检查 DNS 端口"
|
||||
fi
|
||||
|
||||
if [ -x "$(subconverter_bin)" ]; then
|
||||
if is_port_in_use "$(subconverter_port)"; then
|
||||
doctor_ok "subconverter 端口已监听:$(subconverter_port)"
|
||||
@@ -3277,11 +3631,12 @@ doctor_active_switch() {
|
||||
}
|
||||
|
||||
doctor_controller() {
|
||||
local controller group_count current_examples
|
||||
local controller group_count current_examples bind_failure_text
|
||||
|
||||
doctor_print_title "控制器检查"
|
||||
|
||||
controller="$(status_read_controller 2>/dev/null || true)"
|
||||
bind_failure_text="$(runtime_mixed_port_bind_failure_text 2>/dev/null || true)"
|
||||
|
||||
if [ -z "${controller:-}" ] || [ "$controller" = "null" ]; then
|
||||
doctor_fail "未解析到 external-controller"
|
||||
@@ -3291,7 +3646,11 @@ doctor_controller() {
|
||||
doctor_ok "控制器地址:$(display_controller_local_addr "$controller" 2>/dev/null || echo "$controller")"
|
||||
|
||||
if ! status_is_running; then
|
||||
doctor_warn "内核未运行,无法检查控制器 API"
|
||||
if [ -n "${bind_failure_text:-}" ]; then
|
||||
doctor_warn "${bind_failure_text},无法检查控制器 API"
|
||||
else
|
||||
doctor_warn "内核未运行,无法检查控制器 API"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -3928,6 +4287,11 @@ doctor_risk_level() {
|
||||
proxy_controller_reachable 2>/dev/null && controller_ok="true"
|
||||
runtime_config_exists && config_ok="true"
|
||||
|
||||
if runtime_mixed_port_bind_failure_kind >/dev/null 2>&1; then
|
||||
echo "high"
|
||||
return
|
||||
fi
|
||||
|
||||
if [ "$running" = "true" ] && [ "$controller_ok" = "true" ] && [ "$config_ok" = "true" ]; then
|
||||
echo "low"
|
||||
return
|
||||
@@ -3956,15 +4320,19 @@ doctor_risk_text() {
|
||||
}
|
||||
|
||||
doctor_problem_lines() {
|
||||
local active_sub
|
||||
local active_sub bind_failure_text mixed_port
|
||||
|
||||
active_sub="$(active_subscription_name 2>/dev/null || true)"
|
||||
bind_failure_text="$(runtime_mixed_port_bind_failure_text 2>/dev/null || true)"
|
||||
mixed_port="$(runtime_mixed_port_bind_failure_port 2>/dev/null || status_read_mixed_port 2>/dev/null || true)"
|
||||
|
||||
if ! runtime_config_exists; then
|
||||
echo "🚨 运行配置缺失"
|
||||
fi
|
||||
|
||||
if ! status_is_running; then
|
||||
if [ -n "${bind_failure_text:-}" ]; then
|
||||
echo "🚨 ${bind_failure_text}${mixed_port:+:${mixed_port}}"
|
||||
elif ! status_is_running; then
|
||||
echo "🚨 代理内核未运行"
|
||||
fi
|
||||
|
||||
@@ -3988,6 +4356,24 @@ doctor_primary_conclusion() {
|
||||
fi
|
||||
|
||||
if ! status_is_running; then
|
||||
case "$(runtime_mixed_port_bind_failure_kind 2>/dev/null || true)" in
|
||||
bind_denied)
|
||||
echo "❗ 当前不可用:mixed-port 绑定被拒绝"
|
||||
mixed_port_bind_observation_line | sed -n '1p' | sed 's/^/ /'
|
||||
return 0
|
||||
;;
|
||||
address_in_use)
|
||||
echo "❗ 当前不可用:mixed-port 端口被占用"
|
||||
mixed_port_bind_observation_line | sed -n '1p' | sed 's/^/ /'
|
||||
return 0
|
||||
;;
|
||||
bind_failed)
|
||||
echo "❗ 当前不可用:mixed-port 绑定失败"
|
||||
mixed_port_bind_observation_line | sed -n '1p' | sed 's/^/ /'
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "🟡 当前未连接:代理内核未启动"
|
||||
return 0
|
||||
fi
|
||||
@@ -4001,9 +4387,10 @@ doctor_primary_conclusion() {
|
||||
}
|
||||
|
||||
doctor_recommendation_lines() {
|
||||
local active_sub
|
||||
local active_sub bind_failure_kind
|
||||
|
||||
active_sub="$(active_subscription_name 2>/dev/null || true)"
|
||||
bind_failure_kind="$(runtime_mixed_port_bind_failure_kind 2>/dev/null || true)"
|
||||
|
||||
if ! runtime_config_exists; then
|
||||
if [ -n "$(subscription_url 2>/dev/null || true)" ]; then
|
||||
@@ -4014,7 +4401,10 @@ doctor_recommendation_lines() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! status_is_running; then
|
||||
if [ -n "${bind_failure_kind:-}" ]; then
|
||||
mixed_port_bind_recommendation_lines icon
|
||||
return 0
|
||||
elif ! status_is_running; then
|
||||
echo "💡 clashon"
|
||||
return 0
|
||||
fi
|
||||
@@ -4042,11 +4432,14 @@ doctor_recommendation_lines() {
|
||||
}
|
||||
|
||||
doctor_evidence_lines() {
|
||||
local active_sub mixed_port controller
|
||||
local active_sub mixed_port controller bind_failure_kind bind_failure_line dns_port
|
||||
|
||||
active_sub="$(active_subscription_name 2>/dev/null || true)"
|
||||
mixed_port="$(status_read_mixed_port 2>/dev/null || true)"
|
||||
mixed_port="$(runtime_mixed_port_bind_failure_port 2>/dev/null || status_read_mixed_port 2>/dev/null || true)"
|
||||
controller="$(status_read_controller 2>/dev/null || true)"
|
||||
bind_failure_kind="$(runtime_mixed_port_bind_failure_kind 2>/dev/null || true)"
|
||||
bind_failure_line="$(runtime_mixed_port_bind_failure_line 2>/dev/null || true)"
|
||||
dns_port="$(runtime_config_dns_port 2>/dev/null || true)"
|
||||
|
||||
if runtime_config_exists; then
|
||||
echo "🔍 运行配置:存在"
|
||||
@@ -4066,6 +4459,24 @@ doctor_evidence_lines() {
|
||||
echo "🔍 控制器状态:不可访问"
|
||||
fi
|
||||
|
||||
if [ -n "${bind_failure_kind:-}" ]; then
|
||||
echo "🔍 mixed-port 绑定错误:${bind_failure_kind}"
|
||||
echo "🔍 mixed-port 环境:$(mixed_port_bind_environment_text 2>/dev/null || echo unknown)"
|
||||
if runtime_mixed_port_controller_listening 2>/dev/null; then
|
||||
echo "🔍 控制器监听:已确认"
|
||||
else
|
||||
echo "🔍 控制器监听:未确认"
|
||||
fi
|
||||
if [ -n "${dns_port:-}" ]; then
|
||||
if runtime_mixed_port_dns_listening 2>/dev/null; then
|
||||
echo "🔍 DNS 监听:${dns_port}(已确认)"
|
||||
else
|
||||
echo "🔍 DNS 监听:${dns_port}(未确认)"
|
||||
fi
|
||||
fi
|
||||
[ -n "${bind_failure_line:-}" ] && echo "🔍 日志证据:${bind_failure_line}"
|
||||
fi
|
||||
|
||||
if [ -n "${active_sub:-}" ]; then
|
||||
if active_subscription_enabled 2>/dev/null; then
|
||||
echo "🔍 当前订阅:${active_sub}(可用)"
|
||||
|
||||
@@ -812,13 +812,8 @@ download_candidate_probe() {
|
||||
download_candidate_fetch() {
|
||||
local url="$1"
|
||||
local out="$2"
|
||||
local asset_name="${3:-}"
|
||||
local progress_arg="--progress-bar"
|
||||
|
||||
if [ "$asset_name" = "subscription" ]; then
|
||||
progress_arg="--silent"
|
||||
fi
|
||||
|
||||
curl_download \
|
||||
"$progress_arg" \
|
||||
--show-error \
|
||||
@@ -936,13 +931,8 @@ download_http_fetch_to_file() {
|
||||
local ua="${3:-}"
|
||||
local connect_timeout="${4:-10}"
|
||||
local max_time="${5:-300}"
|
||||
local asset_name="${6:-}"
|
||||
local progress_arg="--progress-bar"
|
||||
|
||||
if [ "$asset_name" = "subscription" ]; then
|
||||
progress_arg="--silent"
|
||||
fi
|
||||
|
||||
curl_download \
|
||||
"$progress_arg" \
|
||||
--show-error \
|
||||
|
||||
Reference in New Issue
Block a user