488 lines
12 KiB
Bash
488 lines
12 KiB
Bash
#!/bin/bash
|
||
#
|
||
# OpenClaw 一键初始化脚本
|
||
# 用于在新电脑上快速部署 OpenClaw 环境
|
||
# 作者: 虾宝宝 🦐
|
||
# 创建时间: 2026-02-05
|
||
#
|
||
|
||
set -e # 遇到错误立即退出
|
||
|
||
# 颜色定义
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# OpenClaw 根目录(基于脚本位置)
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
ROOT_DIR="$(cd "$SCRIPT_DIR/../../../../" && pwd)"
|
||
|
||
# 执行开关(可通过参数控制)
|
||
SKIP_NODE=0
|
||
SKIP_PACKAGES=0
|
||
SKIP_ENV=0
|
||
SKIP_MCP=0
|
||
SKIP_CLAUDE=0
|
||
SKIP_VERIFY=0
|
||
VERIFY_ONLY=0
|
||
UPDATE_REPO=0
|
||
RESET_ENV=0
|
||
|
||
# 打印函数
|
||
print_header() {
|
||
echo -e "${BLUE}================================${NC}"
|
||
echo -e "${BLUE}$1${NC}"
|
||
echo -e "${BLUE}================================${NC}"
|
||
}
|
||
|
||
print_success() {
|
||
echo -e "${GREEN}✓ $1${NC}"
|
||
}
|
||
|
||
print_warning() {
|
||
echo -e "${YELLOW}⚠ $1${NC}"
|
||
}
|
||
|
||
print_error() {
|
||
echo -e "${RED}✗ $1${NC}"
|
||
}
|
||
|
||
usage() {
|
||
cat <<'USAGE'
|
||
OpenClaw Setup Script
|
||
|
||
Usage:
|
||
setup.sh [options]
|
||
|
||
Options:
|
||
--update 拉取最新仓库(git pull --rebase,工作区需干净)
|
||
--verify-only 仅做验证,不执行安装
|
||
--reset-env 重新生成 .env(会备份旧文件)
|
||
--skip-node 跳过 Node.js 安装
|
||
--skip-packages 跳过全局 CLI 安装
|
||
--skip-env 跳过 .env 配置
|
||
--skip-mcp 跳过 MCP 检查/配置
|
||
--skip-claude 跳过 Claude MCP 配置
|
||
--skip-verify 跳过安装验证
|
||
-h, --help 显示帮助
|
||
USAGE
|
||
}
|
||
|
||
# 检查命令是否存在
|
||
command_exists() {
|
||
command -v "$1" >/dev/null 2>&1
|
||
}
|
||
|
||
# 解析参数
|
||
parse_args() {
|
||
while [ $# -gt 0 ]; do
|
||
case "$1" in
|
||
--update) UPDATE_REPO=1 ;;
|
||
--verify-only) VERIFY_ONLY=1 ;;
|
||
--reset-env) RESET_ENV=1 ;;
|
||
--skip-node) SKIP_NODE=1 ;;
|
||
--skip-packages) SKIP_PACKAGES=1 ;;
|
||
--skip-env) SKIP_ENV=1 ;;
|
||
--skip-mcp) SKIP_MCP=1 ;;
|
||
--skip-claude) SKIP_CLAUDE=1 ;;
|
||
--skip-verify) SKIP_VERIFY=1 ;;
|
||
-h|--help) usage; exit 0 ;;
|
||
*)
|
||
print_error "未知参数: $1"
|
||
usage
|
||
exit 1
|
||
;;
|
||
esac
|
||
shift
|
||
done
|
||
}
|
||
|
||
# 检查基础依赖
|
||
check_prereqs() {
|
||
print_header "检查基础依赖"
|
||
|
||
if command_exists git; then
|
||
print_success "Git 已安装"
|
||
else
|
||
print_error "未安装 Git(请先安装)"
|
||
exit 1
|
||
fi
|
||
|
||
if command_exists curl; then
|
||
print_success "curl 已安装"
|
||
else
|
||
print_error "未安装 curl(请先安装)"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# 检查 Node.js 版本
|
||
check_node_version() {
|
||
if command_exists node; then
|
||
NODE_VERSION=$(node --version | cut -d'v' -f2)
|
||
REQUIRED_VERSION="22.0.0"
|
||
|
||
if [ "$(printf '%s\n' "$REQUIRED_VERSION" "$NODE_VERSION" | sort -V | head -n1)" = "$REQUIRED_VERSION" ]; then
|
||
print_success "Node.js 版本符合要求: $NODE_VERSION"
|
||
return 0
|
||
else
|
||
print_warning "Node.js 版本过低: $NODE_VERSION,需要 >= $REQUIRED_VERSION"
|
||
return 1
|
||
fi
|
||
else
|
||
print_error "Node.js 未安装"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# 安装 Node.js
|
||
install_node() {
|
||
print_header "安装 Node.js"
|
||
|
||
if command_exists nvm; then
|
||
print_success "nvm 已安装"
|
||
else
|
||
print_warning "安装 nvm..."
|
||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash
|
||
export NVM_DIR="$HOME/.nvm"
|
||
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
|
||
fi
|
||
|
||
nvm install 22
|
||
nvm use 22
|
||
nvm alias default 22
|
||
|
||
print_success "Node.js $(node --version) 安装完成"
|
||
}
|
||
|
||
# 安装全局 npm 包
|
||
install_global_packages() {
|
||
print_header "安装全局 CLI"
|
||
|
||
if ! command_exists npm; then
|
||
print_error "npm 未安装,无法安装全局 CLI"
|
||
return 1
|
||
fi
|
||
|
||
# package:command
|
||
PACKAGES=(
|
||
"openclaw:openclaw"
|
||
"@openclaw/mcporter:mcporter"
|
||
"codex:codex"
|
||
"@anthropic-ai/claude-code:claude"
|
||
"@google/gemini-cli:gemini"
|
||
)
|
||
|
||
for entry in "${PACKAGES[@]}"; do
|
||
IFS=':' read -r pkg cmd <<< "$entry"
|
||
if command_exists "$cmd"; then
|
||
print_success "$cmd 已安装"
|
||
else
|
||
print_warning "安装 $pkg..."
|
||
npm install -g "$pkg" || print_error "$pkg 安装失败"
|
||
fi
|
||
done
|
||
|
||
print_success "全局 CLI 安装完成"
|
||
}
|
||
|
||
backup_file() {
|
||
local file_path="$1"
|
||
if [ -f "$file_path" ]; then
|
||
local ts
|
||
ts=$(date '+%Y%m%d-%H%M%S')
|
||
cp "$file_path" "${file_path}.bak.${ts}"
|
||
print_warning "已备份: ${file_path}.bak.${ts}"
|
||
fi
|
||
}
|
||
|
||
check_env_placeholders() {
|
||
if [ -f ".env" ]; then
|
||
if grep -n "your_\\|YOUR_" .env >/dev/null 2>&1; then
|
||
print_warning "发现未替换的 .env 占位符,请尽快填写"
|
||
grep -n "your_\\|YOUR_" .env || true
|
||
else
|
||
print_success ".env 已填写(未发现占位符)"
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# 配置环境变量
|
||
setup_environment() {
|
||
print_header "配置环境变量"
|
||
|
||
if [ -f ".env" ] && [ "$RESET_ENV" -eq 0 ]; then
|
||
print_warning ".env 文件已存在,跳过创建"
|
||
print_warning "请检查 .env 文件中的 API Keys 是否正确配置"
|
||
check_env_placeholders
|
||
return 0
|
||
fi
|
||
|
||
if [ -f ".env" ] && [ "$RESET_ENV" -eq 1 ]; then
|
||
backup_file ".env"
|
||
rm -f .env
|
||
fi
|
||
|
||
if [ -f ".env.example" ]; then
|
||
cp .env.example .env
|
||
print_warning "已从 .env.example 创建 .env 文件"
|
||
print_warning "⚠️ 请编辑 .env 文件,填入你的实际 API Keys!"
|
||
else
|
||
cat > .env << 'EOF'
|
||
# ============================================
|
||
# 模型/厂商 API Keys(按需填写)
|
||
# ============================================
|
||
ARK_API_KEY=your_ark_api_key_here
|
||
ZAI_API_KEY=your_zai_api_key_here
|
||
Z_AI_API_KEY=your_zai_api_key_here
|
||
OPENAI_API_KEY=your_openai_api_key_here
|
||
OPENCODE_API_KEY=your_opencode_api_key_here
|
||
ANTHROPIC_API_KEY=your_anthropic_api_key_here
|
||
GEMINI_API_KEY=your_gemini_api_key_here
|
||
|
||
# ============================================
|
||
# 飞书 (Feishu)
|
||
# ============================================
|
||
FEISHU_APP_ID=your_feishu_app_id_main
|
||
FEISHU_APP_SECRET=your_feishu_app_secret_main
|
||
FEISHU_APP_ID_TEST=your_feishu_app_id_test
|
||
FEISHU_APP_SECRET_TEST=your_feishu_app_secret_test
|
||
|
||
# ============================================
|
||
# Telegram
|
||
# ============================================
|
||
TELEGRAM_BOT_TOKEN=your_telegram_bot_token
|
||
|
||
# ============================================
|
||
# OpenClaw Gateway
|
||
# ============================================
|
||
OPENCLAW_GATEWAY_TOKEN=your_gateway_token
|
||
|
||
EOF
|
||
print_warning "已创建默认 .env 文件"
|
||
print_warning "⚠️ 请编辑 .env 文件,填入你的实际 API Keys!"
|
||
fi
|
||
|
||
# 加载环境变量
|
||
export $(cat .env | grep -v '^#' | xargs) 2>/dev/null || true
|
||
|
||
check_env_placeholders
|
||
print_success "环境变量配置完成"
|
||
}
|
||
|
||
# 配置 MCP 服务器
|
||
setup_mcp_servers() {
|
||
print_header "配置 MCP 服务器"
|
||
|
||
if ! command_exists mcporter; then
|
||
print_warning "mcporter 未安装,跳过 MCP 检查"
|
||
return 0
|
||
fi
|
||
|
||
# 确保配置目录存在
|
||
mkdir -p "$ROOT_DIR/config"
|
||
|
||
# 检查 mcporter 配置
|
||
if [ -f "config/mcporter.json" ]; then
|
||
print_success "发现 mcporter.json 配置"
|
||
|
||
# 验证配置
|
||
if mcporter list >/dev/null 2>&1; then
|
||
print_success "MCP 服务器验证通过"
|
||
mcporter list
|
||
else
|
||
print_error "MCP 服务器验证失败,请检查配置和 API Keys"
|
||
fi
|
||
else
|
||
print_warning "未找到 config/mcporter.json,跳过 MCP 配置"
|
||
fi
|
||
|
||
print_success "MCP 服务器配置完成"
|
||
}
|
||
|
||
# 配置 Claude Code MCP
|
||
setup_claude_mcp() {
|
||
print_header "配置 Claude Code MCP"
|
||
|
||
mkdir -p ~/.claude
|
||
|
||
# 从仓库配置复制
|
||
if [ -f ".claude/mcp.json" ]; then
|
||
if [ -f ~/.claude/mcp.json ]; then
|
||
backup_file ~/.claude/mcp.json
|
||
fi
|
||
cp .claude/mcp.json ~/.claude/mcp.json
|
||
print_success "已复制 Claude Code MCP 配置"
|
||
else
|
||
# 创建默认配置
|
||
if [ -f ~/.claude/mcp.json ]; then
|
||
print_warning "~/.claude/mcp.json 已存在,跳过创建"
|
||
else
|
||
cat > ~/.claude/mcp.json << 'EOF'
|
||
{
|
||
"mcpServers": {
|
||
"zai-mcp-server": {
|
||
"type": "stdio",
|
||
"command": "npx",
|
||
"args": ["-y", "@z_ai/mcp-server"],
|
||
"env": {
|
||
"Z_AI_API_KEY": "your_zai_api_key_here",
|
||
"Z_AI_MODE": "ZHIPU"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
EOF
|
||
print_warning "已创建默认 Claude Code MCP 配置"
|
||
print_warning "⚠️ 请编辑 ~/.claude/mcp.json,填入你的实际 API Keys!"
|
||
fi
|
||
fi
|
||
|
||
print_success "Claude Code MCP 配置完成"
|
||
}
|
||
|
||
# 更新仓库
|
||
update_repo() {
|
||
if [ "$UPDATE_REPO" -eq 0 ]; then
|
||
return 0
|
||
fi
|
||
|
||
print_header "更新仓库"
|
||
|
||
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||
print_warning "当前目录不是 Git 仓库,跳过更新"
|
||
return 0
|
||
fi
|
||
|
||
if ! git diff --quiet || ! git diff --staged --quiet; then
|
||
print_warning "工作区有未提交变更,跳过 git pull"
|
||
return 0
|
||
fi
|
||
|
||
print_warning "拉取最新代码..."
|
||
git pull --rebase || print_error "git pull 失败,请手动处理"
|
||
}
|
||
|
||
# 验证安装
|
||
verify_installation() {
|
||
print_header "验证安装"
|
||
|
||
CHECKS=(
|
||
"node:Node.js"
|
||
"npm:npm"
|
||
"git:Git"
|
||
"openclaw:OpenClaw CLI"
|
||
"mcporter:McPorter"
|
||
"codex:Codex CLI"
|
||
"claude:Claude Code"
|
||
"gemini:Gemini CLI"
|
||
)
|
||
|
||
for check in "${CHECKS[@]}"; do
|
||
IFS=':' read -r cmd name <<< "$check"
|
||
if command_exists "$cmd"; then
|
||
version=$($cmd --version 2>/dev/null | head -n1 | tr -d '\n')
|
||
print_success "$name: $version"
|
||
else
|
||
print_error "$name: 未安装"
|
||
fi
|
||
done
|
||
|
||
print_success "安装验证完成"
|
||
}
|
||
|
||
# 主函数
|
||
main() {
|
||
parse_args "$@"
|
||
|
||
print_header "OpenClaw 一键初始化脚本"
|
||
echo ""
|
||
echo "项目名称: OpenClaw AI Assistant Environment"
|
||
echo "主人: 深圳刘家(虾宝宝 🦐)"
|
||
echo "仓库: https://github.com/YOUR_USERNAME/YOUR_REPO"
|
||
echo ""
|
||
|
||
# 切到 OpenClaw 根目录
|
||
if [ ! -d "$ROOT_DIR" ]; then
|
||
print_error "未找到 OpenClaw 根目录: $ROOT_DIR"
|
||
print_error "请确认此脚本位于 OpenClaw 仓库中"
|
||
exit 1
|
||
fi
|
||
|
||
cd "$ROOT_DIR"
|
||
|
||
# 检查是否在正确的目录
|
||
if [ ! -f "openclaw.json" ]; then
|
||
print_error "请在 OpenClaw 根目录运行此脚本"
|
||
print_error "请运行: cd $ROOT_DIR && ./workspace/skills/openclaw-toolbox/scripts/setup.sh"
|
||
exit 1
|
||
fi
|
||
|
||
check_prereqs
|
||
|
||
update_repo
|
||
|
||
if [ "$VERIFY_ONLY" -eq 1 ]; then
|
||
check_node_version || true
|
||
verify_installation
|
||
exit 0
|
||
fi
|
||
|
||
# 执行安装步骤
|
||
if [ "$SKIP_NODE" -eq 0 ]; then
|
||
if ! check_node_version; then
|
||
install_node
|
||
fi
|
||
else
|
||
print_warning "跳过 Node.js 安装"
|
||
fi
|
||
|
||
if [ "$SKIP_PACKAGES" -eq 0 ]; then
|
||
install_global_packages
|
||
else
|
||
print_warning "跳过全局 CLI 安装"
|
||
fi
|
||
|
||
if [ "$SKIP_ENV" -eq 0 ]; then
|
||
setup_environment
|
||
else
|
||
print_warning "跳过 .env 配置"
|
||
fi
|
||
|
||
if [ "$SKIP_MCP" -eq 0 ]; then
|
||
setup_mcp_servers
|
||
else
|
||
print_warning "跳过 MCP 配置"
|
||
fi
|
||
|
||
if [ "$SKIP_CLAUDE" -eq 0 ]; then
|
||
setup_claude_mcp
|
||
else
|
||
print_warning "跳过 Claude MCP 配置"
|
||
fi
|
||
|
||
if [ "$SKIP_VERIFY" -eq 0 ]; then
|
||
verify_installation
|
||
else
|
||
print_warning "跳过安装验证"
|
||
fi
|
||
|
||
print_header "初始化完成!"
|
||
echo ""
|
||
echo "🎉 OpenClaw 环境已成功初始化!"
|
||
echo ""
|
||
echo "下一步操作:"
|
||
echo "1. 编辑 .env 文件,填入你的 API Keys"
|
||
echo "2. 启动 OpenClaw: openclaw gateway start"
|
||
echo "3. 查看状态: openclaw status"
|
||
echo ""
|
||
echo "详细文档: $ROOT_DIR/workspace/skills/openclaw-setup/SETUP.md"
|
||
echo "🦐 虾宝宝为刘家服务"
|
||
echo ""
|
||
}
|
||
|
||
# 运行主函数
|
||
main "$@"
|