Merge pull request #175 from wnlen/codex/establish-codex-bootstrap-document-fej638

System-wide proxy management, auto-relay selection, config cleanup, and Codex bootstrap docs
This commit is contained in:
Arvin
2026-04-10 16:39:30 +08:00
committed by GitHub
3 changed files with 188 additions and 118 deletions
+5 -16
View File
@@ -45,11 +45,11 @@ _clash_alias_print_sep() {
}
_clash_alias_proxy_on() {
eval "$(_clashctl_real proxy on)" || return $?
_clashctl_real proxy on >/dev/null || return $?
}
_clash_alias_proxy_off() {
eval "$(_clashctl_real proxy off)" || true
_clashctl_real proxy off >/dev/null || true
}
_clash_alias_proxy_show() {
@@ -85,13 +85,7 @@ _clash_alias_after_on() {
_clash_alias_after_off() {
_clash_alias_set_persist_enabled "false"
_clash_alias_print_sep
echo "🔴 已关闭代理环境"
echo "🧹 当前 Shell 代理变量已清理"
if [ "${CLASH_WRAPPER_EXEC:-0}" = "1" ]; then
echo '💡 若当前终端仍有代理变量,请执行:eval "$(clashctl proxy off)"'
fi
echo "🧭 新终端默认代理:已关闭"
echo "👉 下一步:clashctl status"
echo "🧹 系统代理已关闭"
}
_clash_alias_run_on() {
@@ -110,12 +104,7 @@ _clash_alias_run_off() {
}
_clash_alias_auto_restore_proxy() {
[ "${CLASH_FOR_LINUX_PROXY_AUTO_RESTORED:-0}" = "1" ] && return 0
export CLASH_FOR_LINUX_PROXY_AUTO_RESTORED="1"
if _clash_alias_persist_enabled; then
_clash_alias_proxy_on >/dev/null 2>&1 || true
fi
return 0
}
clashctl() {
@@ -138,7 +127,7 @@ clashctl() {
off)
_clash_alias_proxy_off
_clash_alias_print_sep
echo "🧹 当前 Shell 代理变量已清理"
echo "🧹 系统代理已关闭"
;;
*)
_clashctl_real "$@"
+64 -69
View File
@@ -260,6 +260,11 @@ cmd_on() {
fi
fi
if ! system_proxy_enable; then
service_stop >/dev/null 2>&1 || true
die_state "当前环境不支持系统代理接管(仅支持可写 /etc/environment)" "clashctl proxy show"
fi
load_system_state
print_on_feedback
@@ -274,9 +279,11 @@ cmd_off() {
prepare
service_stop
ui_title "🔴 代理环境已关闭"
ui_info "当前 Shell 代理变量已清理(函数系统已生效)"
ui_next "clashctl status"
if ! system_proxy_disable; then
die_state "当前环境不支持系统代理关闭(仅支持可写 /etc/environment)" "clashctl proxy show"
fi
echo "🧹 系统代理已关闭"
ui_blank
}
@@ -1432,39 +1439,12 @@ status_current_proxy_brief() {
echo "暂无可切换策略组"
}
shell_proxy_enabled() {
[ -n "${http_proxy:-}" ] \
|| [ -n "${https_proxy:-}" ] \
|| [ -n "${HTTP_PROXY:-}" ] \
|| [ -n "${HTTPS_PROXY:-}" ] \
|| [ -n "${all_proxy:-}" ] \
|| [ -n "${ALL_PROXY:-}" ]
}
shell_proxy_http_value() {
if [ -n "${http_proxy:-}" ]; then
echo "$http_proxy"
return 0
system_proxy_supported_state() {
if system_proxy_supported; then
echo "true"
else
echo "false"
fi
if [ -n "${HTTP_PROXY:-}" ]; then
echo "$HTTP_PROXY"
return 0
fi
echo ""
}
shell_proxy_matches_runtime() {
local expected actual
expected="$(proxy_http_url 2>/dev/null || true)"
actual="$(shell_proxy_http_value)"
[ -n "${expected:-}" ] || return 1
[ -n "${actual:-}" ] || return 1
[ "$expected" = "$actual" ]
}
connectivity_issue_code() {
@@ -1504,13 +1484,18 @@ connectivity_issue_code() {
return 0
fi
if ! shell_proxy_enabled; then
echo "shell_proxy_missing"
if ! system_proxy_supported; then
echo "system_proxy_unsupported"
return 0
fi
if ! shell_proxy_matches_runtime; then
echo "shell_proxy_mismatch"
if [ "$(system_proxy_status 2>/dev/null || echo off)" != "on" ]; then
echo "system_proxy_off"
return 0
fi
if ! system_proxy_matches_runtime; then
echo "system_proxy_mismatch"
return 0
fi
@@ -1525,8 +1510,9 @@ connectivity_issue_text() {
config_invalid) echo "异常(当前运行配置不可用)" ;;
subscription_unhealthy) echo "异常(当前主订阅不可用)" ;;
proxy_control_broken) echo "异常(当前无可用策略组或节点控制面异常)" ;;
shell_proxy_missing) echo "未接管(当前 Shell 未注入代理环境)" ;;
shell_proxy_mismatch) echo "异常(当前 Shell 代理与运行时端口不一致)" ;;
system_proxy_unsupported) echo "未接管(当前环境不支持系统代理)" ;;
system_proxy_off) echo "未接管(系统代理未开启)" ;;
system_proxy_mismatch) echo "异常(系统代理端口与运行时不一致)" ;;
*) echo "未知" ;;
esac
}
@@ -1551,11 +1537,14 @@ connectivity_next_action() {
proxy_control_broken)
echo "clashctl status --verbose"
;;
shell_proxy_missing)
echo 'eval "$(clashctl proxy on)"'
system_proxy_unsupported)
echo "clashctl doctor"
;;
shell_proxy_mismatch)
echo 'eval "$(clashctl proxy off)" && eval "$(clashctl proxy on)"'
system_proxy_off)
echo "clashctl proxy on"
;;
system_proxy_mismatch)
echo "clashctl proxy off && clashctl proxy on"
;;
*)
echo "clashctl doctor"
@@ -1566,6 +1555,7 @@ connectivity_next_action() {
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
if status_is_running; then
runtime_running="true"
@@ -1585,7 +1575,9 @@ connectivity_evidence_lines() {
active="$(active_subscription_name 2>/dev/null || true)"
config_source="$(status_runtime_config_source 2>/dev/null || true)"
expected_proxy="$(proxy_http_url 2>/dev/null || true)"
actual_proxy="$(shell_proxy_http_value)"
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)"
echo "• runtime_running = ${runtime_running:-false}"
echo "• controller_reachable = ${controller_ok:-false}"
@@ -1595,12 +1587,9 @@ connectivity_evidence_lines() {
echo "• config_source = ${config_source:-unknown}"
echo "• proxy_group_count = ${group_count:-0}"
if shell_proxy_enabled; then
echo "• shell_proxy_enabled = true"
echo "• shell_proxy_http = ${actual_proxy:-unknown}"
else
echo "• shell_proxy_enabled = false"
fi
echo "• system_proxy_supported = ${system_proxy_supported_text:-false}"
echo "• system_proxy_enabled = ${system_proxy_state:-off}"
[ -n "${actual_proxy:-}" ] && echo "• system_proxy_http = ${actual_proxy}"
if [ -n "${expected_proxy:-}" ]; then
echo "• runtime_proxy_http = $expected_proxy"
@@ -1758,10 +1747,10 @@ print_status_summary_compact() {
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')"
if shell_proxy_persist_enabled 2>/dev/null; then
shell_persist_text="开启"
if system_proxy_supported; then
shell_persist_text="$(system_proxy_status 2>/dev/null || echo off)"
else
shell_persist_text="关闭"
shell_persist_text="unsupported"
fi
if [ -f "$(runtime_dashboard_dir)/index.html" ]; then
dashboard_text="已部署"
@@ -1802,7 +1791,7 @@ print_status_summary_compact() {
echo "⚙️ 运行后端:$(status_runtime_backend_text)"
echo "🧪 环境模式:$(status_container_mode_text)"
echo "🧪 Tun 状态:${tun_text:-未知}"
echo "🧭 新终端代理继承:${shell_persist_text}"
echo "🧭 系统代理状态:${shell_persist_text}"
echo "🧩 Dashboard:${dashboard_text}(来源:${dashboard_source_text})"
echo "🧩 Dashboard 策略:${dashboard_policy_text}"
echo "🔐 控制器密钥:${secret_text}"
@@ -1904,10 +1893,10 @@ print_status_summary_verbose() {
tun_verify_result="$(status_tun_last_verify_result 2>/dev/null || true)"
tun_verify_reason="$(status_tun_last_verify_reason 2>/dev/null || true)"
tun_verify_time="$(status_tun_last_verify_time 2>/dev/null || true)"
if shell_proxy_persist_enabled 2>/dev/null; then
shell_persist_text="开启"
if system_proxy_supported; then
shell_persist_text="$(system_proxy_status 2>/dev/null || echo off)"
else
shell_persist_text="关闭"
shell_persist_text="unsupported"
fi
if [ -f "$(runtime_dashboard_dir)/index.html" ]; then
dashboard_text="已部署"
@@ -1970,7 +1959,7 @@ print_status_summary_verbose() {
echo "🧪 环境模式:${install_container_text:-unknown}"
echo "🧩 安装验证:${install_verify_text:-unknown}"
echo "🧭 端口裁决:${port_adjustment_text:-unknown}"
echo "🧭 新终端代理继承:${shell_persist_text}"
echo "🧭 系统代理状态:${shell_persist_text}"
echo "🧩 Dashboard:${dashboard_text}(来源:${dashboard_source_text})"
echo "🧩 Dashboard 策略:${dashboard_policy_text}"
echo "🔐 控制器密钥:${secret_text}"
@@ -2837,10 +2826,10 @@ doctor_runtime_events() {
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)"
if shell_proxy_persist_enabled 2>/dev/null; then
shell_persist_text="开启"
if system_proxy_supported; then
shell_persist_text="$(system_proxy_status 2>/dev/null || echo off)"
else
shell_persist_text="关闭"
shell_persist_text="unsupported"
fi
if [ -f "$(runtime_dashboard_dir)/index.html" ]; then
dashboard_status="已部署"
@@ -2860,7 +2849,7 @@ doctor_runtime_events() {
doctor_ok "当前风险等级:${risk_level:-unknown}"
doctor_ok "当前配置来源:${config_source:-unknown}"
doctor_ok "新终端代理继承:${shell_persist_text}"
doctor_ok "系统代理状态:${shell_persist_text}"
doctor_ok "Dashboard 运行目录:${dashboard_status}(来源:${dashboard_source})"
doctor_ok ".env 控制器密钥:${secret_status}"
@@ -4809,10 +4798,18 @@ cmd_proxy() {
print_proxy_show
;;
on)
print_proxy_on_script
if ! system_proxy_enable; then
die_state "当前环境不支持系统代理接管(仅支持可写 /etc/environment)" "clashctl proxy show"
fi
ui_ok "系统代理已开启"
print_proxy_show
;;
off)
print_proxy_off_script
if ! system_proxy_disable; then
die_state "当前环境不支持系统代理关闭(仅支持可写 /etc/environment)" "clashctl proxy show"
fi
ui_ok "系统代理已关闭"
print_proxy_show
;;
groups)
cmd_proxy_groups
@@ -4852,7 +4849,7 @@ cmd_proxy() {
echo " clashctl proxy select <策略组> <节点>"
echo
echo "🧩 说明:"
echo " on/off 输出当前 Shell 代理变量脚本"
echo " on/off 开启或关闭系统代理(/etc/environment)"
echo " groups 查看可切换策略组"
echo " current 查看当前节点"
echo " nodes 查看某策略组候选节点"
@@ -4863,8 +4860,6 @@ cmd_proxy() {
echo " clashctl proxy groups"
echo " clashctl proxy select"
echo
echo '🌐 注入当前 Shell:eval "$(clashctl proxy on)"'
echo '🧹 清理当前 Shell:eval "$(clashctl proxy off)"'
echo
ui_next "clashctl select"
ui_blank
+119 -33
View File
@@ -31,48 +31,134 @@ proxy_no_proxy_value() {
echo "${NO_PROXY_DEFAULT:-127.0.0.1,localhost,::1}"
}
system_proxy_env_file() {
echo "${SYSTEM_PROXY_ENV_FILE:-/etc/environment}"
}
system_proxy_block_begin() {
echo "# >>> clash-for-linux system proxy >>>"
}
system_proxy_block_end() {
echo "# <<< clash-for-linux system proxy <<<"
}
system_proxy_supported() {
local file dir
file="$(system_proxy_env_file)"
dir="$(dirname "$file")"
if [ -f "$file" ]; then
[ -w "$file" ]
return $?
fi
[ -d "$dir" ] && [ -w "$dir" ]
}
system_proxy_status() {
local file
file="$(system_proxy_env_file)"
[ -f "$file" ] || {
echo "off"
return 0
}
if grep -Fq "$(system_proxy_block_begin)" "$file" 2>/dev/null; then
echo "on"
else
echo "off"
fi
}
system_proxy_http_value() {
local file value
file="$(system_proxy_env_file)"
[ -f "$file" ] || return 1
value="$(sed -nE 's/^http_proxy="?([^"\r\n]+)"?$/\1/p' "$file" | tail -n 1)"
[ -n "${value:-}" ] || return 1
echo "$value"
}
system_proxy_matches_runtime() {
local expected actual
expected="$(proxy_http_url 2>/dev/null || true)"
actual="$(system_proxy_http_value 2>/dev/null || true)"
[ -n "${expected:-}" ] || return 1
[ -n "${actual:-}" ] || return 1
[ "$expected" = "$actual" ]
}
system_proxy_write_block() {
local mode="$1"
local file tmp http_url socks_url no_proxy
file="$(system_proxy_env_file)"
tmp="$(mktemp)"
[ -f "$file" ] && cat "$file" > "$tmp"
awk -v begin="$(system_proxy_block_begin)" -v end="$(system_proxy_block_end)" '
$0 == begin {skip=1; next}
$0 == end {skip=0; next}
skip != 1 {print}
' "$tmp" > "${tmp}.clean"
mv -f "${tmp}.clean" "$tmp"
if [ "$mode" = "on" ]; then
http_url="$(proxy_http_url)"
socks_url="$(proxy_socks_url)"
no_proxy="$(proxy_no_proxy_value)"
{
echo "$(system_proxy_block_begin)"
echo "http_proxy="$http_url""
echo "https_proxy="$http_url""
echo "HTTP_PROXY="$http_url""
echo "HTTPS_PROXY="$http_url""
echo "all_proxy="$socks_url""
echo "ALL_PROXY="$socks_url""
echo "no_proxy="$no_proxy""
echo "NO_PROXY="$no_proxy""
echo "$(system_proxy_block_end)"
} >> "$tmp"
fi
cat "$tmp" > "$file"
rm -f "$tmp" 2>/dev/null || true
}
system_proxy_enable() {
system_proxy_supported || return 2
system_proxy_write_block "on"
}
system_proxy_disable() {
system_proxy_supported || return 2
system_proxy_write_block "off"
}
print_proxy_show() {
local status
status="$(system_proxy_status)"
echo
echo "😼 当前代理环境"
echo
echo "🌐 HTTP:$(proxy_http_url)"
echo "🧦 SOCKS5:$(proxy_socks_url)"
echo "🚫 NO_PROXY:$(proxy_no_proxy_value)"
echo "🧭 系统代理:${status}($(system_proxy_env_file))"
echo
}
print_proxy_on_script() {
local http_url socks_url no_proxy
http_url="$(proxy_http_url)"
socks_url="$(proxy_socks_url)"
no_proxy="$(proxy_no_proxy_value)"
cat <<EOF
export http_proxy="$http_url"
export https_proxy="$http_url"
export HTTP_PROXY="$http_url"
export HTTPS_PROXY="$http_url"
export all_proxy="$socks_url"
export ALL_PROXY="$socks_url"
export no_proxy="$no_proxy"
export NO_PROXY="$no_proxy"
EOF
}
print_proxy_off_script() {
cat <<'EOF'
unset http_proxy
unset https_proxy
unset HTTP_PROXY
unset HTTPS_PROXY
unset all_proxy
unset ALL_PROXY
unset no_proxy
unset NO_PROXY
EOF
}
controller_addr() {
local config_file="${1:-$RUNTIME_DIR/config.yaml}"
@@ -335,4 +421,4 @@ print_proxy_groups_summary() {
echo "$group"
fi
done < <(proxy_group_list)
}
}