Files
shell/moltbot/install_mac.sh
user123 ef1d38eceb ADD
2026-01-30 03:27:21 +08:00

502 lines
13 KiB
Bash
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/bash
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
PLAIN='\033[0m'
CONFIG_DIR="${HOME}/.clawdbot"
CONFIG_FILE="${CONFIG_DIR}/clawdbot.json"
PLIST_LABEL="com.moltbot.gateway"
PLIST_PATH="${HOME}/Library/LaunchAgents/${PLIST_LABEL}.plist"
LOG_DIR="${HOME}/Library/Logs/moltbot"
LOG_FILE="${LOG_DIR}/gateway.log"
ERR_FILE="${LOG_DIR}/gateway.error.log"
log_info() {
echo -e "${GREEN}[INFO] $1${PLAIN}"
}
log_warn() {
echo -e "${YELLOW}[WARN] $1${PLAIN}"
}
log_error() {
echo -e "${RED}[ERROR] $1${PLAIN}"
}
check_macos() {
if [ "$(uname -s)" != "Darwin" ]; then
log_error "此脚本仅支持 macOS。"
exit 1
fi
}
ensure_brew() {
if command -v brew >/dev/null 2>&1; then
return
fi
log_warn "未检测到 Homebrew需要先安装。"
read -p "是否安装 Homebrew[y/n]: " install_brew
if [ "$install_brew" != "y" ]; then
log_error "已取消 Homebrew 安装。"
exit 1
fi
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
if [ -x "/opt/homebrew/bin/brew" ]; then
eval "$(/opt/homebrew/bin/brew shellenv)"
elif [ -x "/usr/local/bin/brew" ]; then
eval "$(/usr/local/bin/brew shellenv)"
fi
if ! command -v brew >/dev/null 2>&1; then
log_error "Homebrew 安装失败,请检查网络。"
exit 1
fi
}
install_nodejs() {
log_info "正在检查 Node.js 环境..."
if command -v node >/dev/null 2>&1; then
NODE_VERSION_FULL=$(node -v)
NODE_MAJOR=$(echo "$NODE_VERSION_FULL" | cut -d'v' -f2 | cut -d'.' -f1)
if [ "$NODE_MAJOR" -ge 22 ]; then
log_info "Node.js 已安装且版本符合要求 (${NODE_VERSION_FULL}),无需重复安装。"
return
fi
log_warn "检测到旧版本 Node.js (${NODE_VERSION_FULL}),即将自动升级到 22.x。"
fi
ensure_brew
log_info "正在安装 Node.js 22..."
brew install node@22
brew link --force --overwrite node@22
if command -v node >/dev/null 2>&1; then
NODE_VERSION=$(node -v)
log_info "Node.js 安装成功: ${NODE_VERSION}"
else
log_error "Node.js 安装失败,请检查网络或系统源。"
exit 1
fi
}
install_git() {
log_info "正在检查 Git 环境..."
if command -v git >/dev/null 2>&1; then
log_info "Git 已安装,无需重复安装。"
return
fi
ensure_brew
log_info "正在安装 Git..."
brew install git
if command -v git >/dev/null 2>&1; then
GIT_VERSION=$(git --version)
log_info "Git 安装成功: ${GIT_VERSION}"
else
log_error "Git 安装失败,请检查网络或系统源。"
exit 1
fi
}
install_moltbot_core() {
log_info "正在安装 Moltbot..."
if command -v clawdbot >/dev/null 2>&1; then
CURRENT_VERSION=$(clawdbot --version)
log_warn "ClawdBot (Moltbot) 已安装 (版本: ${CURRENT_VERSION})"
read -p "是否强制重新安装/更新?[y/n]: " force_install
if [ "$force_install" != "y" ]; then
log_info "跳过安装步骤。"
return
fi
fi
npm install -g clawdbot@latest
if command -v clawdbot >/dev/null 2>&1; then
VERSION=$(clawdbot --version)
log_info "ClawdBot 安装成功,版本: ${VERSION}"
else
log_error "ClawdBot 安装失败,请检查 npm 权限或网络。"
exit 1
fi
}
configure_moltbot() {
if [ -f "${CONFIG_FILE}" ]; then
log_warn "检测到已存在配置文件: ${CONFIG_FILE}"
read -p "是否覆盖现有配置?[y/n]: " overwrite_config
if [ "$overwrite_config" != "y" ]; then
log_info "保留现有配置,跳过配置步骤。"
return
fi
fi
log_info "开始配置 Moltbot..."
mkdir -p "${CONFIG_DIR}"
echo -e "${CYAN}请选择 API 类型:${PLAIN}"
echo "1. Anthropic 官方 API"
echo "2. OpenAI 兼容 API (中转站/其他模型)"
read -p "请输入选项 [1/2]: " api_choice
read -p "是否配置 Telegram 机器人?[y/n]: " enable_telegram
if [ "$enable_telegram" == "y" ]; then
read -p "请输入 Telegram Bot Token: " bot_token
read -p "请输入您的 Telegram User ID (用于管理员白名单): " admin_id
else
log_warn "已跳过 Telegram 机器人配置。"
fi
if [ "$api_choice" == "1" ]; then
read -p "请输入 Anthropic API Key (sk-ant-...): " api_key
if [ "$enable_telegram" == "y" ]; then
cat > "${CONFIG_FILE}" <<EOF
{
"gateway": {
"mode": "local",
"bind": "loopback",
"port": 18789
},
"env": {
"ANTHROPIC_API_KEY": "${api_key}"
},
"agents": {
"defaults": {
"model": {
"primary": "anthropic/claude-sonnet-4-5-20261022"
}
}
},
"tools": {
"elevated": {
"enabled": true,
"allowFrom": {
"telegram": ["${admin_id}"]
}
},
"allow": ["exec", "process", "read", "write", "edit", "web_search", "web_fetch", "cron"]
},
"channels": {
"telegram": {
"enabled": true,
"botToken": "${bot_token}",
"dmPolicy": "pairing",
"allowFrom": ["${admin_id}"]
}
}
}
EOF
else
cat > "${CONFIG_FILE}" <<EOF
{
"gateway": {
"mode": "local",
"bind": "loopback",
"port": 18789
},
"env": {
"ANTHROPIC_API_KEY": "${api_key}"
},
"agents": {
"defaults": {
"model": {
"primary": "anthropic/claude-sonnet-4-5-20261022"
}
}
},
"tools": {
"elevated": {
"enabled": false
},
"allow": ["exec", "process", "read", "write", "edit", "web_search", "web_fetch", "cron"]
},
"channels": {
"telegram": {
"enabled": false
}
}
}
EOF
fi
else
read -p "请输入 API Base URL (例如 https://example.com/v1): " base_url
read -p "请输入 API Key (例如 sk-abc123...): " api_key
read -p "请输入模型名称 (例如 gpt-4o): " model_name
if [ "$enable_telegram" == "y" ]; then
cat > "${CONFIG_FILE}" <<EOF
{
"gateway": {
"mode": "local",
"bind": "loopback",
"port": 18789
},
"agents": {
"defaults": {
"model": {
"primary": "openai-compat/${model_name}"
},
"elevatedDefault": "full",
"compaction": {
"mode": "safeguard"
},
"maxConcurrent": 4
}
},
"models": {
"mode": "merge",
"providers": {
"openai-compat": {
"baseUrl": "${base_url}",
"apiKey": "${api_key}",
"api": "openai-completions",
"models": [
{
"id": "${model_name}",
"name": "${model_name}"
}
]
}
}
},
"tools": {
"exec": {
"backgroundMs": 10000,
"timeoutSec": 1800,
"cleanupMs": 1800000,
"notifyOnExit": true
},
"elevated": {
"enabled": true,
"allowFrom": {
"telegram": ["${admin_id}"]
}
},
"allow": ["exec", "process", "read", "write", "edit", "web_search", "web_fetch", "cron"]
},
"channels": {
"telegram": {
"enabled": true,
"botToken": "${bot_token}",
"dmPolicy": "pairing",
"allowFrom": ["${admin_id}"]
}
}
}
EOF
else
cat > "${CONFIG_FILE}" <<EOF
{
"gateway": {
"mode": "local",
"bind": "loopback",
"port": 18789
},
"agents": {
"defaults": {
"model": {
"primary": "openai-compat/${model_name}"
},
"elevatedDefault": "full",
"compaction": {
"mode": "safeguard"
},
"maxConcurrent": 4
}
},
"models": {
"mode": "merge",
"providers": {
"openai-compat": {
"baseUrl": "${base_url}",
"apiKey": "${api_key}",
"api": "openai-completions",
"models": [
{
"id": "${model_name}",
"name": "${model_name}"
}
]
}
}
},
"tools": {
"exec": {
"backgroundMs": 10000,
"timeoutSec": 1800,
"cleanupMs": 1800000,
"notifyOnExit": true
},
"elevated": {
"enabled": false
},
"allow": ["exec", "process", "read", "write", "edit", "web_search", "web_fetch", "cron"]
},
"channels": {
"telegram": {
"enabled": false
}
}
}
EOF
fi
fi
log_info "配置文件已生成: ${CONFIG_FILE}"
log_info "配置文件绝对路径: ${CONFIG_FILE}"
}
setup_launchd() {
log_info "正在配置 LaunchAgent 服务..."
mkdir -p "$(dirname "${PLIST_PATH}")"
mkdir -p "${LOG_DIR}"
GATEWAY_BIN=$(command -v clawdbot)
if [ -z "${GATEWAY_BIN}" ]; then
log_error "未找到 clawdbot 可执行文件。"
exit 1
fi
NODE_BIN=$(command -v node)
if [ -n "${NODE_BIN}" ]; then
NODE_DIR=$(dirname "${NODE_BIN}")
LAUNCH_PATH="${NODE_DIR}:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
else
LAUNCH_PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
fi
cat > "${PLIST_PATH}" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>${PLIST_LABEL}</string>
<key>ProgramArguments</key>
<array>
<string>${GATEWAY_BIN}</string>
<string>gateway</string>
<string>--verbose</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>${LOG_FILE}</string>
<key>StandardErrorPath</key>
<string>${ERR_FILE}</string>
<key>EnvironmentVariables</key>
<dict>
<key>HOME</key>
<string>${HOME}</string>
<key>PATH</key>
<string>${LAUNCH_PATH}</string>
</dict>
</dict>
</plist>
EOF
launchctl bootout "gui/${UID}" "${PLIST_PATH}" >/dev/null 2>&1
launchctl bootstrap "gui/${UID}" "${PLIST_PATH}"
launchctl kickstart -k "gui/${UID}/${PLIST_LABEL}" >/dev/null 2>&1
log_info "服务已启动并设置开机自启。"
}
service_start() {
launchctl bootstrap "gui/${UID}" "${PLIST_PATH}" >/dev/null 2>&1
launchctl kickstart -k "gui/${UID}/${PLIST_LABEL}" >/dev/null 2>&1
log_info "服务已启动"
}
service_stop() {
launchctl bootout "gui/${UID}" "${PLIST_PATH}" >/dev/null 2>&1
log_info "服务已停止"
}
service_restart() {
service_stop
service_start
}
service_status() {
launchctl print "gui/${UID}/${PLIST_LABEL}"
}
service_logs() {
if [ -f "${LOG_FILE}" ] || [ -f "${ERR_FILE}" ]; then
tail -f "${LOG_FILE}" "${ERR_FILE}"
else
log_warn "暂无日志文件,请先启动服务。"
fi
}
install() {
check_macos
install_git
install_nodejs
install_moltbot_core
configure_moltbot
setup_launchd
echo -e "${GREEN}=============================================${PLAIN}"
echo -e "${GREEN} Moltbot 安装配置完成!${PLAIN}"
echo -e "${GREEN}=============================================${PLAIN}"
echo -e "请等待Moltbot初始化完成约1分钟然后使用Telegram向您的Bot发送消息开始使用。"
echo -e "${GREEN}=============================================${PLAIN}"
}
uninstall() {
check_macos
read -p "确定要卸载 Moltbot 吗?配置文件也将被删除 [y/n]: " confirm
if [ "$confirm" != "y" ]; then
echo "已取消。"
return
fi
service_stop
rm -f "${PLIST_PATH}"
npm uninstall -g clawdbot
npm uninstall -g moltbot
rm -rf "${CONFIG_DIR}"
log_info "Moltbot (ClawdBot) 已卸载。"
}
modify_config() {
echo -e "${YELLOW}注意:此操作将重新生成配置文件,并且重启服务。${PLAIN}"
configure_moltbot
log_info "正在重启服务以应用更改..."
service_restart
}
show_menu() {
clear
echo -e "${CYAN}MoltbotClawdbot管理脚本${PLAIN}"
echo -e "${CYAN}------------------------${PLAIN}"
echo -e "1. 安装并配置 Moltbot"
echo -e "2. 启动服务"
echo -e "3. 停止服务"
echo -e "4. 重启服务"
echo -e "5. 查看运行状态"
echo -e "6. 查看实时日志"
echo -e "7. 修改配置文件"
echo -e "8. 卸载 Moltbot"
echo -e "9. 运行doctor自检"
echo -e "0. 退出脚本"
echo -e "${CYAN}------------------------${PLAIN}"
read -p "请输入选项 [0-9]: " choice
case "$choice" in
1) install ;;
2) service_start ;;
3) service_stop ;;
4) service_restart ;;
5) service_status ;;
6) service_logs ;;
7) modify_config ;;
8) uninstall ;;
9) clawdbot doctor ;;
0) exit 0 ;;
*) echo -e "${RED}无效选项,请重新输入${PLAIN}" ;;
esac
}
if [ $# -gt 0 ]; then
case "$1" in
install) install ;;
uninstall) uninstall ;;
*) echo "用法: $0 [install|uninstall] 或直接运行进入菜单" ;;
esac
else
while true; do
show_menu
echo -e "\n按回车键继续..."
read
done
fi