WSL2 + Claude Code CLI (WSL2 原生) + Unity MCP Server (AnkleBreaker) + Rust Token Killer + v2rayN 代理自动配置 - deploy.ps1:8 步全自动部署,支持幂等重跑 - Invoke-WSL 改用 base64 编码规避 PowerShell→WSL CRLF 问题 - nvm 安装 Node.js,正确排除 Windows interop 路径 - claude mcp add --scope user 全局注册 Unity MCP - .wslconfig networkingMode=mirrored,代理直连 127.0.0.1 - 摘要显示复用已检测变量,避免二次 WSL 调用 - .env.example 提供配置模板,.env 已加入 .gitignore Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
272 lines
12 KiB
Bash
272 lines
12 KiB
Bash
#!/usr/bin/env bash
|
||
# =============================================================
|
||
# WSL2 内部环境检测 & 安装脚本
|
||
# 可单独在 WSL2 Ubuntu 中运行:bash wsl-setup.sh
|
||
# 也会被 deploy.ps1 自动调用(通过 Invoke-WSL)
|
||
# =============================================================
|
||
set -euo pipefail
|
||
|
||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
|
||
CYAN='\033[0;36m'; NC='\033[0m'
|
||
|
||
log() { echo -e "${GREEN}[INFO]${NC} $*"; }
|
||
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
|
||
step() { echo -e "\n${CYAN}====== $* ======${NC}"; }
|
||
|
||
# ──────────────────────────────────────────────────────────────
|
||
# 加载 .env(与脚本同目录)
|
||
# ──────────────────────────────────────────────────────────────
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
ENV_FILE="$SCRIPT_DIR/.env"
|
||
ENV_EXAMPLE="$SCRIPT_DIR/.env.example"
|
||
|
||
if [ ! -f "$ENV_FILE" ]; then
|
||
if [ -f "$ENV_EXAMPLE" ]; then
|
||
cp "$ENV_EXAMPLE" "$ENV_FILE"
|
||
warn "已从 .env.example 创建 .env,请按需编辑后重新运行"
|
||
exit 0
|
||
fi
|
||
fi
|
||
|
||
if [ -f "$ENV_FILE" ]; then
|
||
sed -i 's/\r$//' "$ENV_FILE"
|
||
set -a; source "$ENV_FILE"; set +a
|
||
fi
|
||
|
||
ANTHROPIC_API_KEY="${ANTHROPIC_API_KEY:-}"
|
||
ANTHROPIC_BASE_URL="${ANTHROPIC_BASE_URL:-https://api.anthropic.com}"
|
||
CLAUDE_MODEL="${CLAUDE_MODEL:-claude-opus-4-5}"
|
||
BRIDGE_PORT="${BRIDGE_PORT:-7890}"
|
||
|
||
# ──────────────────────────────────────────────────────────────
|
||
# Step 1: v2rayN 代理配置(WSL2 侧)
|
||
# ──────────────────────────────────────────────────────────────
|
||
step "1/6 代理配置"
|
||
|
||
# 优先读取 deploy.ps1 写入的 ~/.mcp-proxy.env
|
||
if [ -f ~/.mcp-proxy.env ]; then
|
||
source ~/.mcp-proxy.env 2>/dev/null || true
|
||
if [ -n "${http_proxy:-}" ]; then
|
||
log "代理已从 ~/.mcp-proxy.env 加载: $http_proxy"
|
||
fi
|
||
fi
|
||
|
||
# 若代理未设置但提供了 PROXY_PORT,则动态检测 Windows 主机 IP
|
||
PROXY_PORT="${PROXY_PORT:-0}"
|
||
if [ -z "${http_proxy:-}" ] && [ "$PROXY_PORT" -gt 0 ] 2>/dev/null; then
|
||
WIN_HOST=$(ip route 2>/dev/null | grep default | awk '{print $3}' | head -1)
|
||
[ -z "$WIN_HOST" ] && WIN_HOST=$(grep nameserver /etc/resolv.conf 2>/dev/null | awk '{print $2}' | head -1)
|
||
if [ -n "$WIN_HOST" ]; then
|
||
export http_proxy="http://${WIN_HOST}:${PROXY_PORT}"
|
||
export https_proxy="$http_proxy"
|
||
export HTTP_PROXY="$http_proxy"
|
||
export HTTPS_PROXY="$http_proxy"
|
||
export no_proxy="localhost,127.0.0.1,::1"
|
||
log "WSL2 代理已设置: $http_proxy (Windows 主机: $WIN_HOST)"
|
||
else
|
||
warn "无法获取 Windows 主机 IP,代理未设置"
|
||
fi
|
||
fi
|
||
|
||
# 写入 ~/.mcp-proxy.env 并持久化到 ~/.bashrc
|
||
if [ -n "${http_proxy:-}" ] && [ -z "$(grep -s 'mcp-proxy.env' ~/.bashrc)" ]; then
|
||
cat >> ~/.bashrc << 'BASHRCBLOCK'
|
||
|
||
# Unity MCP WSL2 proxy (generated by wsl-setup.sh)
|
||
[ -f ~/.mcp-proxy.env ] && . ~/.mcp-proxy.env
|
||
BASHRCBLOCK
|
||
log "代理配置已写入 ~/.bashrc"
|
||
fi
|
||
|
||
# 写入 git 代理
|
||
if [ -n "${http_proxy:-}" ]; then
|
||
git config --global http.proxy "$http_proxy" 2>/dev/null || true
|
||
git config --global https.proxy "$https_proxy" 2>/dev/null || true
|
||
log "git 代理已配置"
|
||
fi
|
||
|
||
if [ -z "${http_proxy:-}" ]; then
|
||
warn "未配置代理,将使用直连网络(如下载失败请通过 PROXY_PORT=10809 重新运行)"
|
||
fi
|
||
|
||
# ──────────────────────────────────────────────────────────────
|
||
# Step 2: 系统依赖
|
||
# ──────────────────────────────────────────────────────────────
|
||
step "2/6 系统依赖"
|
||
|
||
export DEBIAN_FRONTEND=noninteractive
|
||
sudo apt-get update -qq
|
||
sudo apt-get install -y -qq \
|
||
curl wget git build-essential pkg-config libssl-dev \
|
||
ca-certificates gnupg lsb-release unzip python3
|
||
log "系统依赖安装完成"
|
||
|
||
# ──────────────────────────────────────────────────────────────
|
||
# Step 3: Node.js LTS
|
||
# ──────────────────────────────────────────────────────────────
|
||
step "3/6 Node.js LTS"
|
||
|
||
if command -v node &>/dev/null; then
|
||
log "Node.js 已安装: $(node --version)"
|
||
else
|
||
log "通过 NodeSource 安装 Node.js LTS..."
|
||
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash - > /dev/null 2>&1
|
||
sudo apt-get install -y -qq nodejs
|
||
log "Node.js 安装完成: $(node --version)"
|
||
fi
|
||
|
||
# 设置 npm 代理
|
||
if [ -n "${http_proxy:-}" ] && command -v npm &>/dev/null; then
|
||
npm config set proxy "$http_proxy" 2>/dev/null || true
|
||
npm config set https-proxy "$https_proxy" 2>/dev/null || true
|
||
log "npm 代理已配置"
|
||
fi
|
||
|
||
# ──────────────────────────────────────────────────────────────
|
||
# Step 4: Claude Code CLI
|
||
# ──────────────────────────────────────────────────────────────
|
||
step "4/6 Claude Code CLI"
|
||
|
||
if command -v claude &>/dev/null; then
|
||
log "Claude Code 已安装: $(claude --version 2>&1 | head -1)"
|
||
else
|
||
log "安装 @anthropic-ai/claude-code..."
|
||
sudo npm install -g @anthropic-ai/claude-code --quiet
|
||
log "Claude Code 安装完成: $(claude --version 2>&1 | head -1)"
|
||
fi
|
||
|
||
mkdir -p ~/.claude
|
||
cat > ~/.claude/settings.json << SETTINGS
|
||
{"model": "$CLAUDE_MODEL"}
|
||
SETTINGS
|
||
log "已写入 ~/.claude/settings.json"
|
||
|
||
PROFILE_BLOCK=""
|
||
[ -n "$ANTHROPIC_API_KEY" ] && PROFILE_BLOCK+="export ANTHROPIC_API_KEY='$ANTHROPIC_API_KEY'\n"
|
||
[ -n "$ANTHROPIC_BASE_URL" ] && PROFILE_BLOCK+="export ANTHROPIC_BASE_URL='$ANTHROPIC_BASE_URL'\n"
|
||
PROFILE_BLOCK+="export CLAUDE_MODEL='$CLAUDE_MODEL'\n"
|
||
|
||
for rc in ~/.bashrc ~/.profile; do
|
||
if ! grep -q "ANTHROPIC_API_KEY" "$rc" 2>/dev/null; then
|
||
printf "\n# Claude Code CLI\n${PROFILE_BLOCK}" >> "$rc"
|
||
log "已追加环境变量到 $rc"
|
||
fi
|
||
done
|
||
|
||
# ──────────────────────────────────────────────────────────────
|
||
# Step 5: Unity MCP Server
|
||
# ──────────────────────────────────────────────────────────────
|
||
step "5/6 Unity MCP Server (AnkleBreaker-Studio)"
|
||
|
||
MCP_SERVER_DIR="$HOME/.mcp-servers/unity-mcp-server"
|
||
mkdir -p "$HOME/.mcp-servers"
|
||
|
||
if [ -d "$MCP_SERVER_DIR/.git" ]; then
|
||
log "unity-mcp-server 已存在,更新至最新..."
|
||
git -C "$MCP_SERVER_DIR" pull --quiet
|
||
else
|
||
log "克隆 unity-mcp-server..."
|
||
git clone --quiet https://github.com/AnkleBreaker-Studio/unity-mcp-server.git "$MCP_SERVER_DIR"
|
||
fi
|
||
|
||
log "安装 npm 依赖..."
|
||
(cd "$MCP_SERVER_DIR" && npm install --prefer-offline --quiet 2>/dev/null || npm install --quiet)
|
||
|
||
# 确定入口文件
|
||
if [ -f "$MCP_SERVER_DIR/src/index.js" ]; then
|
||
SERVER_ENTRY="$MCP_SERVER_DIR/src/index.js"
|
||
elif [ -f "$MCP_SERVER_DIR/build/index.js" ]; then
|
||
SERVER_ENTRY="$MCP_SERVER_DIR/build/index.js"
|
||
else
|
||
SERVER_ENTRY="$MCP_SERVER_DIR/index.js"
|
||
fi
|
||
log "MCP Server 入口:$SERVER_ENTRY"
|
||
|
||
# 写入 Claude Code (WSL2) MCP 配置
|
||
MCP_CONFIG_DIR="$HOME/.config/Claude"
|
||
MCP_CONFIG_FILE="$MCP_CONFIG_DIR/claude_desktop_config.json"
|
||
mkdir -p "$MCP_CONFIG_DIR"
|
||
|
||
python3 - << PYEOF
|
||
import json, os
|
||
|
||
cfg_file = "$MCP_CONFIG_FILE"
|
||
try:
|
||
with open(cfg_file) as f:
|
||
cfg = json.load(f)
|
||
except:
|
||
cfg = {}
|
||
|
||
cfg.setdefault("mcpServers", {})
|
||
cfg["mcpServers"]["unity-mcp"] = {
|
||
"command": "node",
|
||
"args": ["$SERVER_ENTRY"],
|
||
"env": {"UNITY_BRIDGE_PORT": "$BRIDGE_PORT"}
|
||
}
|
||
|
||
with open(cfg_file, "w") as f:
|
||
json.dump(cfg, f, indent=2)
|
||
print("Claude Code (WSL2) MCP config written")
|
||
PYEOF
|
||
|
||
log "MCP 配置已写入 $MCP_CONFIG_FILE"
|
||
|
||
# ──────────────────────────────────────────────────────────────
|
||
# Step 6: Rust 工具链 & Token Killer
|
||
# ──────────────────────────────────────────────────────────────
|
||
step "6/6 Rust 工具链 & Token Killer"
|
||
|
||
[ -f "$HOME/.cargo/env" ] && source "$HOME/.cargo/env"
|
||
|
||
if command -v rustc &>/dev/null; then
|
||
log "Rust 已安装: $(rustc --version)"
|
||
else
|
||
log "通过 rustup 安装 Rust stable..."
|
||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
|
||
| sh -s -- -y --default-toolchain stable --no-modify-path
|
||
source "$HOME/.cargo/env"
|
||
log "Rust 安装完成: $(rustc --version)"
|
||
fi
|
||
|
||
for rc in ~/.bashrc ~/.profile; do
|
||
grep -q 'cargo/env' "$rc" 2>/dev/null || echo 'source ~/.cargo/env 2>/dev/null || true' >> "$rc"
|
||
done
|
||
|
||
if command -v rtk &>/dev/null; then
|
||
log "rtk 已安装: $(rtk --version 2>/dev/null || echo ok)"
|
||
else
|
||
log "安装 rtk (Rust Token Killer)..."
|
||
sudo apt-get install -y -qq pkg-config libssl-dev 2>/dev/null || true
|
||
if cargo install --git https://github.com/rtk-ai/rtk 2>&1 | tail -3; then
|
||
log "rtk 安装成功"
|
||
else
|
||
warn "rtk 安装失败,请手动运行: cargo install --git https://github.com/rtk-ai/rtk"
|
||
fi
|
||
fi
|
||
|
||
# ──────────────────────────────────────────────────────────────
|
||
# 安装摘要
|
||
# ──────────────────────────────────────────────────────────────
|
||
echo ""
|
||
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"
|
||
echo -e "${GREEN} ✅ WSL2 环境部署完成${NC}"
|
||
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"
|
||
echo -e " Node.js $(node --version 2>/dev/null || echo 未安装)"
|
||
echo -e " npm $(npm --version 2>/dev/null || echo 未安装)"
|
||
echo -e " Claude Code $(claude --version 2>/dev/null | head -1 || echo 未安装)"
|
||
echo -e " Rust $(rustc --version 2>/dev/null || echo 未安装)"
|
||
echo -e " rtk $(rtk --version 2>/dev/null || echo 未安装)"
|
||
echo -e " MCP Server $SERVER_ENTRY"
|
||
if [ -n "${http_proxy:-}" ]; then
|
||
echo -e " Proxy $http_proxy"
|
||
fi
|
||
echo ""
|
||
echo -e " ${YELLOW}⚠ 后续步骤(手动):${NC}"
|
||
echo -e " 1. Unity Package Manager 安装插件:"
|
||
echo -e " ${CYAN}https://github.com/AnkleBreaker-Studio/unity-mcp-plugin.git${NC}"
|
||
echo -e " 2. 确认 Unity Bridge 在线: http://127.0.0.1:${BRIDGE_PORT}/api/ping"
|
||
echo -e " 3. 重启 AI 客户端(Claude Desktop / Cursor / Windsurf)"
|
||
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"
|
||
echo ""
|
||
log "请重新加载 shell: source ~/.bashrc" |