From 39bcb0869f980382e940f1a006ad1d555a7f8832 Mon Sep 17 00:00:00 2001 From: 6vision Date: Tue, 3 Feb 2026 02:56:46 +0800 Subject: [PATCH] feat: enhance one-click deployment script with full lifecycle management --- run.sh | 1069 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 917 insertions(+), 152 deletions(-) diff --git a/run.sh b/run.sh index b201559..69a49ae 100644 --- a/run.sh +++ b/run.sh @@ -1,192 +1,957 @@ -#!/usr/bin/env bash +#!/bin/bash set -e -# 颜色定义 -RED='\033[0;31m' # 红色 -GREEN='\033[0;32m' # 绿色 -YELLOW='\033[0;33m' # 黄色 -BLUE='\033[0;34m' # 蓝色 -NC='\033[0m' # 无颜色 +# ============================ +# CowAgent Management Script +# ============================ -# 获取当前脚本的目录 +# ANSI colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +BOLD='\033[1m' +NC='\033[0m' + +# Check if using Bash +if [ -z "$BASH_VERSION" ]; then + echo -e "${RED}❌ Please run this script with Bash.${NC}" + exit 1 +fi + +# Get current script directory export BASE_DIR=$(cd "$(dirname "$0")"; pwd) -echo -e "${GREEN}📁 BASE_DIR: ${BASE_DIR}${NC}" -# 检查 config.json 文件是否存在 -check_config_file() { - if [ ! -f "${BASE_DIR}/config.json" ]; then - echo -e "${RED}❌ 错误:未找到 config.json 文件。请确保 config.json 存在于当前目录。${NC}" - exit 1 +# Detect if in project directory +IS_PROJECT_DIR=false +if [ -f "${BASE_DIR}/config-template.json" ] && [ -f "${BASE_DIR}/app.py" ]; then + IS_PROJECT_DIR=true +fi + +# Check and install tool +check_and_install_tool() { + local tool_name=$1 + if ! command -v "$tool_name" &> /dev/null; then + echo -e "${YELLOW}⚙️ $tool_name not found, installing...${NC}" + if command -v yum &> /dev/null; then + sudo yum install "$tool_name" -y + elif command -v apt-get &> /dev/null; then + sudo apt-get update && sudo apt-get install "$tool_name" -y + elif command -v brew &> /dev/null; then + brew install "$tool_name" + else + echo -e "${RED}❌ Unsupported package manager. Please install $tool_name manually.${NC}" + return 1 + fi + + if ! command -v "$tool_name" &> /dev/null; then + echo -e "${RED}❌ Failed to install $tool_name.${NC}" + return 1 + else + echo -e "${GREEN}✅ $tool_name installed successfully.${NC}" + return 0 + fi + else + echo -e "${GREEN}✅ $tool_name is already installed.${NC}" + return 0 fi } -# 检查 Python 版本是否大于等于 3.7,并检查 pip 是否可用 +# Detect and set Python command +detect_python_command() { + FOUND_NEWER_VERSION="" + + # Try to find Python command in order of preference + for cmd in python3 python python3.12 python3.11 python3.10 python3.9 python3.8 python3.7; do + if command -v $cmd &> /dev/null; then + # Check Python version + major_version=$($cmd -c 'import sys; print(sys.version_info[0])' 2>/dev/null) + minor_version=$($cmd -c 'import sys; print(sys.version_info[1])' 2>/dev/null) + + if [[ "$major_version" == "3" ]]; then + # Check if version is in supported range (3.7 - 3.12) + if (( minor_version >= 7 && minor_version <= 12 )); then + PYTHON_CMD=$cmd + PYTHON_VERSION="${major_version}.${minor_version}" + break + elif (( minor_version >= 13 )); then + # Found Python 3.13+, but not compatible + if [ -z "$FOUND_NEWER_VERSION" ]; then + FOUND_NEWER_VERSION="${major_version}.${minor_version}" + fi + fi + fi + fi + done + + if [ -z "$PYTHON_CMD" ]; then + echo -e "${YELLOW}Tried: python3, python, python3.12, python3.11, python3.10, python3.9, python3.8, python3.7${NC}" + if [ -n "$FOUND_NEWER_VERSION" ]; then + echo -e "${RED}❌ Found Python $FOUND_NEWER_VERSION, but this project requires Python 3.7-3.12${NC}" + echo -e "${YELLOW}Python 3.13+ has compatibility issues with some dependencies (web.py, cgi module removed)${NC}" + echo -e "${YELLOW}Please install Python 3.7-3.12 (recommend Python 3.12)${NC}" + else + echo -e "${RED}❌ No suitable Python found. Please install Python 3.7-3.12${NC}" + fi + exit 1 + fi + + # Export for global use + export PYTHON_CMD + export PYTHON_VERSION + + echo -e "${GREEN}✅ Found Python: $PYTHON_CMD (version $PYTHON_VERSION)${NC}" +} + +# Check Python version (>= 3.7) check_python_version() { - if ! command -v python3 &> /dev/null; then - echo -e "${RED}❌ 错误:未找到 Python3。请安装 Python 3.7 或以上版本。${NC}" - exit 1 - fi - - PYTHON_VERSION=$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")') - PYTHON_MAJOR=$(echo "$PYTHON_VERSION" | cut -d'.' -f1) - PYTHON_MINOR=$(echo "$PYTHON_VERSION" | cut -d'.' -f2) - - if (( PYTHON_MAJOR < 3 || (PYTHON_MAJOR == 3 && PYTHON_MINOR < 7) )); then - echo -e "${RED}❌ 错误:Python 版本为 ${PYTHON_VERSION}。请安装 Python 3.7 或以上版本。${NC}" - exit 1 - fi - - if ! python3 -m pip --version &> /dev/null; then - echo -e "${RED}❌ 错误:未找到 pip。请安装 pip。${NC}" + detect_python_command + + # Verify pip is available + if ! $PYTHON_CMD -m pip --version &> /dev/null; then + echo -e "${RED}❌ pip not found for $PYTHON_CMD. Please install pip.${NC}" exit 1 fi + + echo -e "${GREEN}✅ pip is available for $PYTHON_CMD${NC}" } -# 检查并安装缺失的依赖 +# Clone project +clone_project() { + echo -e "${GREEN}🔍 Cloning ChatGPT-on-WeChat project...${NC}" + + if [ -d "chatgpt-on-wechat" ]; then + echo -e "${YELLOW}⚠️ Directory 'chatgpt-on-wechat' already exists.${NC}" + read -p "Choose action: overwrite(o), backup(b), or quit(q)? [o/b/q]: " choice + case "$choice" in + o|O) + echo -e "${YELLOW}🗑️ Overwriting 'chatgpt-on-wechat' directory...${NC}" + rm -rf chatgpt-on-wechat + ;; + b|B) + backup_dir="chatgpt-on-wechat_backup_$(date +%s)" + echo -e "${YELLOW}🔀 Backing up to '$backup_dir'...${NC}" + mv chatgpt-on-wechat "$backup_dir" + ;; + q|Q) + echo -e "${RED}❌ Installation cancelled.${NC}" + exit 1 + ;; + *) + echo -e "${RED}❌ Invalid choice. Exiting.${NC}" + exit 1 + ;; + esac + fi + + check_and_install_tool git + + if ! command -v git &> /dev/null; then + echo -e "${YELLOW}⚠️ Git not available. Trying wget/curl...${NC}" + if command -v wget &> /dev/null; then + wget https://gitee.com/zhayujie/chatgpt-on-wechat/repository/archive/master.zip -O chatgpt-on-wechat.zip + unzip chatgpt-on-wechat.zip + mv chatgpt-on-wechat-master chatgpt-on-wechat + rm chatgpt-on-wechat.zip + elif command -v curl &> /dev/null; then + curl -L https://gitee.com/zhayujie/chatgpt-on-wechat/repository/archive/master.zip -o chatgpt-on-wechat.zip + unzip chatgpt-on-wechat.zip + mv chatgpt-on-wechat-master chatgpt-on-wechat + rm chatgpt-on-wechat.zip + else + echo -e "${RED}❌ Cannot download project. Please install Git, wget, or curl.${NC}" + exit 1 + fi + else + git clone https://github.com/zhayujie/chatgpt-on-wechat.git || \ + git clone https://gitee.com/zhayujie/chatgpt-on-wechat.git + if [[ $? -ne 0 ]]; then + echo -e "${RED}❌ Project clone failed. Please check network connection.${NC}" + exit 1 + fi + fi + + cd chatgpt-on-wechat || { echo -e "${RED}❌ Failed to enter project directory.${NC}"; exit 1; } + export BASE_DIR=$(pwd) + echo -e "${GREEN}✅ Project cloned successfully: $BASE_DIR${NC}" + + # Add execute permission to management script + if [ -f "${BASE_DIR}/run.sh" ]; then + chmod +x "${BASE_DIR}/run.sh" 2>/dev/null || true + echo -e "${GREEN}✅ Execute permission added to run.sh${NC}" + fi + + sleep 1 +} + +# Install dependencies install_dependencies() { - echo -e "${YELLOW}⏳ 正在安装依赖...${NC}" - - if [ ! -f "${BASE_DIR}/requirements.txt" ]; then - echo -e "${RED}❌ 错误:未找到 requirements.txt 文件。${NC}" - exit 1 + echo -e "${GREEN}📦 Installing dependencies...${NC}" + + # For Python 3.11+, use --break-system-packages to avoid externally-managed-environment errors + PIP_EXTRA_ARGS="" + if $PYTHON_CMD -c "import sys; exit(0 if sys.version_info >= (3, 11) else 1)" 2>/dev/null; then + PIP_EXTRA_ARGS="--break-system-packages" + echo -e "${YELLOW}Python 3.11+ detected, using --break-system-packages for pip installations${NC}" fi - - # 安装 requirements.txt 中的依赖,使用清华大学的 PyPI 镜像 - pip3 install -r "${BASE_DIR}/requirements.txt" -i https://pypi.tuna.tsinghua.edu.cn/simple - - # 处理 requirements-optional.txt(如果存在) - if [ -f "${BASE_DIR}/requirements-optional.txt" ]; then - echo -e "${YELLOW}⏳ 正在安装可选的依赖...${NC}" - pip3 install -r "${BASE_DIR}/requirements-optional.txt" -i https://pypi.tuna.tsinghua.edu.cn/simple + + # Upgrade pip and basic tools (ignore existing system packages to avoid conflicts) + echo -e "${YELLOW}Upgrading pip and basic tools...${NC}" + set +e + $PYTHON_CMD -m pip install --upgrade pip setuptools wheel importlib_metadata --ignore-installed $PIP_EXTRA_ARGS -i https://pypi.tuna.tsinghua.edu.cn/simple > /tmp/pip_upgrade.log 2>&1 + if [ $? -ne 0 ]; then + echo -e "${YELLOW}⚠️ Some tools failed to upgrade, but continuing...${NC}" + cat /tmp/pip_upgrade.log | head -20 fi + set -e + rm -f /tmp/pip_upgrade.log + + # Common packages that may have distutils/system conflicts + COMMON_CONFLICT_PACKAGES="PyYAML setuptools wheel certifi charset-normalizer" + + # Try normal installation first + echo -e "${YELLOW}Installing project dependencies...${NC}" + + # Save output and capture exit code correctly + set +e # Temporarily disable exit on error + $PYTHON_CMD -m pip install -r requirements.txt $PIP_EXTRA_ARGS -i https://pypi.tuna.tsinghua.edu.cn/simple > /tmp/pip_install.log 2>&1 + INSTALL_EXIT_CODE=$? + set -e # Re-enable exit on error + + # Show output + cat /tmp/pip_install.log + + if [ $INSTALL_EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}✅ Dependencies installed successfully.${NC}" + else + # Check if it's a distutils/system package conflict error + if grep -qE "distutils installed project|uninstall-no-record-file|installed by debian" /tmp/pip_install.log; then + echo -e "${YELLOW}⚠️ Detected system package conflict, retrying with workaround...${NC}" + # Only ignore common conflict packages + IGNORE_PACKAGES="" + for pkg in $COMMON_CONFLICT_PACKAGES; do + IGNORE_PACKAGES="$IGNORE_PACKAGES --ignore-installed $pkg" + done + + if $PYTHON_CMD -m pip install -r requirements.txt $IGNORE_PACKAGES $PIP_EXTRA_ARGS -i https://pypi.tuna.tsinghua.edu.cn/simple; then + echo -e "${GREEN}✅ Dependencies installed successfully (workaround applied).${NC}" + else + echo -e "${YELLOW}⚠️ Some dependencies may have issues, but continuing...${NC}" + fi + elif grep -q "externally-managed-environment" /tmp/pip_install.log; then + echo -e "${YELLOW}⚠️ Detected externally-managed environment, retrying with --break-system-packages...${NC}" + if $PYTHON_CMD -m pip install -r requirements.txt --break-system-packages -i https://pypi.tuna.tsinghua.edu.cn/simple; then + echo -e "${GREEN}✅ Dependencies installed successfully (system packages override applied).${NC}" + else + echo -e "${YELLOW}⚠️ Some dependencies may have issues, but continuing...${NC}" + fi + else + echo -e "${YELLOW}⚠️ Installation had errors, but continuing...${NC}" + fi + fi + + rm -f /tmp/pip_install.log } -# 启动项目 -run_project() { - echo -e "${GREEN}🚀 准备启动项目...${NC}" - cd "${BASE_DIR}" - sleep 2 +# Select model +select_model() { + echo "" + echo -e "${CYAN}${BOLD}=========================================${NC}" + echo -e "${CYAN}${BOLD} Select AI Model${NC}" + echo -e "${CYAN}${BOLD}=========================================${NC}" + echo -e "${YELLOW}1) Claude (claude-sonnet-4-5, claude-opus-4-0, etc.)${NC}" + echo -e "${YELLOW}2) Zhipu AI (glm-4.7, glm-4.6, etc.)${NC}" + echo -e "${YELLOW}3) Gemini (gemini-3-flash-preview, gemini-2.5-pro, etc.)${NC}" + echo -e "${YELLOW}4) OpenAI GPT (gpt-5.2, gpt-4.1, etc.)${NC}" + echo -e "${YELLOW}5) Qwen (qwen3-max, qwen-plus, qwq-plus, etc.)${NC}" + echo -e "${YELLOW}6) MiniMax (MiniMax-M2.1, MiniMax-M2.1-lightning, etc.)${NC}" + echo -e "${YELLOW}7) LinkAI (access multiple models via one API)${NC}" + echo "" + + while true; do + read -p "Enter your choice [1-7]: " model_choice + case "$model_choice" in + 1|2|3|4|5|6|7) + break + ;; + *) + echo -e "${RED}Invalid choice. Please enter 1-7.${NC}" + ;; + esac + done +} +# Configure model +configure_model() { + case "$model_choice" in + 1) + # Claude + echo -e "${GREEN}Configuring Claude...${NC}" + read -p "Enter Claude API Key: " claude_key + read -p "Enter model name [press Enter for default: claude-sonnet-4-5]: " model_name + model_name=${model_name:-claude-sonnet-4-5} + read -p "Enter API Base URL [press Enter for default: https://api.anthropic.com/v1]: " api_base + api_base=${api_base:-https://api.anthropic.com/v1} + + MODEL_NAME="$model_name" + CLAUDE_KEY="$claude_key" + CLAUDE_BASE="$api_base" + ;; + 2) + # Zhipu AI + echo -e "${GREEN}Configuring Zhipu AI...${NC}" + read -p "Enter Zhipu AI API Key: " zhipu_key + read -p "Enter model name [press Enter for default: glm-4.7]: " model_name + model_name=${model_name:-glm-4.7} + + MODEL_NAME="$model_name" + ZHIPU_KEY="$zhipu_key" + ;; + 3) + # Gemini + echo -e "${GREEN}Configuring Gemini...${NC}" + read -p "Enter Gemini API Key: " gemini_key + read -p "Enter model name [press Enter for default: gemini-3-flash-preview]: " model_name + model_name=${model_name:-gemini-3-flash-preview} + read -p "Enter API Base URL [press Enter for default: https://generativelanguage.googleapis.com]: " api_base + api_base=${api_base:-https://generativelanguage.googleapis.com} + + MODEL_NAME="$model_name" + GEMINI_KEY="$gemini_key" + GEMINI_BASE="$api_base" + ;; + 4) + # OpenAI + echo -e "${GREEN}Configuring OpenAI GPT...${NC}" + read -p "Enter OpenAI API Key: " openai_key + read -p "Enter model name [press Enter for default: gpt-4.1]: " model_name + model_name=${model_name:-gpt-4.1} + read -p "Enter API Base URL [press Enter for default: https://api.openai.com/v1]: " api_base + api_base=${api_base:-https://api.openai.com/v1} + + MODEL_NAME="$model_name" + OPENAI_KEY="$openai_key" + OPENAI_BASE="$api_base" + ;; + 5) + # Qwen (DashScope) + echo -e "${GREEN}Configuring Qwen (DashScope)...${NC}" + read -p "Enter DashScope API Key: " dashscope_key + read -p "Enter model name [press Enter for default: qwen3-max]: " model_name + model_name=${model_name:-qwen3-max} + + MODEL_NAME="$model_name" + DASHSCOPE_KEY="$dashscope_key" + ;; + 6) + # MiniMax + echo -e "${GREEN}Configuring MiniMax...${NC}" + read -p "Enter MiniMax API Key: " minimax_key + read -p "Enter model name [press Enter for default: MiniMax-M2.1]: " model_name + model_name=${model_name:-MiniMax-M2.1} + + MODEL_NAME="$model_name" + MINIMAX_KEY="$minimax_key" + ;; + 7) + # LinkAI + echo -e "${GREEN}Configuring LinkAI...${NC}" + read -p "Enter LinkAI API Key: " linkai_key + read -p "Enter model name [press Enter for default: claude-sonnet-4-5]: " model_name + model_name=${model_name:-claude-sonnet-4-5} + + MODEL_NAME="$model_name" + USE_LINKAI="true" + LINKAI_KEY="$linkai_key" + ;; + esac +} + +# Select channel +select_channel() { + echo "" + echo -e "${CYAN}${BOLD}=========================================${NC}" + echo -e "${CYAN}${BOLD} Select Communication Channel${NC}" + echo -e "${CYAN}${BOLD}=========================================${NC}" + echo -e "${YELLOW}1) Feishu (飞书)${NC}" + echo -e "${YELLOW}2) DingTalk (钉钉)${NC}" + echo -e "${YELLOW}3) WeCom (企微应用)${NC}" + echo -e "${YELLOW}4) Web (网页)${NC}" + echo "" + + while true; do + read -p "Enter your choice [1-4, default: 1]: " channel_choice + channel_choice=${channel_choice:-1} + case "$channel_choice" in + 1|2|3|4) + break + ;; + *) + echo -e "${RED}Invalid choice. Please enter 1-4.${NC}" + ;; + esac + done +} + +# Configure channel +configure_channel() { + case "$channel_choice" in + 1) + # Feishu (WebSocket mode) + CHANNEL_TYPE="feishu" + echo -e "${GREEN}Configure Feishu (WebSocket mode)...${NC}" + read -p "Enter Feishu App ID: " fs_app_id + read -p "Enter Feishu App Secret: " fs_app_secret + read -p "Enter Feishu Bot Name: " fs_bot_name + + FEISHU_APP_ID="$fs_app_id" + FEISHU_APP_SECRET="$fs_app_secret" + FEISHU_BOT_NAME="$fs_bot_name" + FEISHU_EVENT_MODE="websocket" + ACCESS_INFO="Feishu channel configured (WebSocket mode)" + ;; + 2) + # DingTalk + CHANNEL_TYPE="dingtalk" + echo -e "${GREEN}Configure DingTalk...${NC}" + read -p "Enter DingTalk Client ID: " dt_client_id + read -p "Enter DingTalk Client Secret: " dt_client_secret + + DT_CLIENT_ID="$dt_client_id" + DT_CLIENT_SECRET="$dt_client_secret" + ACCESS_INFO="DingTalk channel configured" + ;; + 3) + # WeCom + CHANNEL_TYPE="wechatcom_app" + echo -e "${GREEN}Configure WeCom...${NC}" + read -p "Enter WeChat Corp ID: " corp_id + read -p "Enter WeChat Com App Token: " com_token + read -p "Enter WeChat Com App Secret: " com_secret + read -p "Enter WeChat Com App Agent ID: " com_agent_id + read -p "Enter WeChat Com App AES Key: " com_aes_key + read -p "Enter WeChat Com App Port [press Enter for default: 9898]: " com_port + com_port=${com_port:-9898} + + WECHATCOM_CORP_ID="$corp_id" + WECHATCOM_TOKEN="$com_token" + WECHATCOM_SECRET="$com_secret" + WECHATCOM_AGENT_ID="$com_agent_id" + WECHATCOM_AES_KEY="$com_aes_key" + WECHATCOM_PORT="$com_port" + ACCESS_INFO="WeCom channel configured on port ${com_port}" + ;; + 4) + # Web + CHANNEL_TYPE="web" + read -p "Enter web port [press Enter for default: 9899]: " web_port + web_port=${web_port:-9899} + + WEB_PORT="$web_port" + ACCESS_INFO="Web interface will be available at: http://localhost:${web_port}/chat" + ;; + esac +} + +# Generate config file +create_config_file() { + echo -e "${GREEN}📝 Generating config.json...${NC}" + + # Build JSON based on channel type + case "$CHANNEL_TYPE" in + feishu) + cat > config.json < config.json < config.json < config.json < "${BASE_DIR}/nohup.out" 2>&1 & - echo -e "${GREEN}🚀 正在启动 ChatGPT-on-WeChat (Linux)...${NC}" + # Linux: use setsid to detach from terminal + nohup setsid $PYTHON_CMD "${BASE_DIR}/app.py" > "${BASE_DIR}/nohup.out" 2>&1 & + echo -e "${GREEN}${EMOJI_COW} CowAgent started on Linux (using $PYTHON_CMD)${NC}" elif [[ "$OS_TYPE" == "Darwin" ]]; then - # 在 macOS 上直接运行 - python3 "${BASE_DIR}/app.py" > "${BASE_DIR}/nohup.out" 2>&1 & - echo -e "${GREEN}🚀 正在启动 ChatGPT-on-WeChat (macOS)...${NC}" + # macOS: use nohup to prevent SIGHUP + nohup $PYTHON_CMD "${BASE_DIR}/app.py" > "${BASE_DIR}/nohup.out" 2>&1 & + echo -e "${GREEN}${EMOJI_COW} CowAgent started on macOS (using $PYTHON_CMD)${NC}" else - echo -e "${RED}❌ 错误:不支持的操作系统 ${OS_TYPE}。${NC}" + echo -e "${RED}❌ Unsupported OS: ${OS_TYPE}${NC}" exit 1 fi sleep 2 - # 显示日志输出,供用户扫码 + echo "" + echo -e "${CYAN}${BOLD}=========================================${NC}" + echo -e "${GREEN}${EMOJI_CHECK} CowAgent is now running in background!${NC}" + echo -e "${GREEN}${EMOJI_CHECK} Process will continue after closing terminal.${NC}" + echo -e "${CYAN}$ACCESS_INFO${NC}" + echo "" + echo -e "${CYAN}${BOLD}Management Commands:${NC}" + echo -e " ${GREEN}./run.sh stop${NC} Stop the service" + echo -e " ${GREEN}./run.sh restart${NC} Restart the service" + echo -e " ${GREEN}./run.sh status${NC} Check status" + echo -e " ${GREEN}./run.sh logs${NC} View logs" + echo -e " ${GREEN}./run.sh update${NC} Update and restart" + echo -e "${CYAN}${BOLD}=========================================${NC}" + echo "" + + echo -e "${YELLOW}Showing recent logs (Ctrl+C to exit, agent keeps running):${NC}" + sleep 2 tail -n 30 -f "${BASE_DIR}/nohup.out" - } -# 更新项目 -update_project() { - echo -e "${GREEN}🔄 准备更新项目,现在停止项目...${NC}" - cd "${BASE_DIR}" - # 停止项目 - stop_project - echo -e "${GREEN}🔄 开始更新项目...${NC}" - # 更新代码,从 git 仓库拉取最新代码 - if [ -d .git ]; then - GIT_PULL_OUTPUT=$(git pull) - if [ $? -eq 0 ]; then - if [[ "$GIT_PULL_OUTPUT" == *"Already up to date."* ]]; then - echo -e "${GREEN}✅ 代码已经是最新的。${NC}" - else - echo -e "${GREEN}✅ 代码更新完成。${NC}" - fi - else - echo -e "${YELLOW}⚠️ 从 GitHub 更新失败,尝试切换到 Gitee 仓库...${NC}" - # 更改远程仓库为 Gitee - git remote set-url origin https://gitee.com/zhayujie/chatgpt-on-wechat.git - GIT_PULL_OUTPUT=$(git pull) - if [ $? -eq 0 ]; then - if [[ "$GIT_PULL_OUTPUT" == *"Already up to date."* ]]; then - echo -e "${GREEN}✅ 代码已经是最新的。${NC}" - else - echo -e "${GREEN}✅ 从 Gitee 更新成功。${NC}" - fi - else - echo -e "${RED}❌ 错误:从 Gitee 更新仍然失败,请检查网络连接。${NC}" - exit 1 - fi - fi - else - echo -e "${RED}❌ 错误:当前目录不是 git 仓库,无法更新代码。${NC}" +# Show usage +show_usage() { + echo -e "${CYAN}${BOLD}=========================================${NC}" + echo -e "${CYAN}${BOLD} ${EMOJI_COW} CowAgent Management Script${NC}" + echo -e "${CYAN}${BOLD}=========================================${NC}" + echo "" + echo -e "${YELLOW}Usage:${NC}" + echo -e " ${GREEN}./run.sh${NC} ${CYAN}# Install/Configure project${NC}" + echo -e " ${GREEN}./run.sh ${NC} ${CYAN}# Execute management command${NC}" + echo "" + echo -e "${YELLOW}Commands:${NC}" + echo -e " ${GREEN}start${NC} Start the service" + echo -e " ${GREEN}stop${NC} Stop the service" + echo -e " ${GREEN}restart${NC} Restart the service" + echo -e " ${GREEN}status${NC} Check service status" + echo -e " ${GREEN}logs${NC} View logs (tail -f)" + echo -e " ${GREEN}config${NC} Reconfigure project" + echo -e " ${GREEN}update${NC} Update and restart" + echo "" + echo -e "${YELLOW}Examples:${NC}" + echo -e " ${GREEN}./run.sh start${NC}" + echo -e " ${GREEN}./run.sh logs${NC}" + echo -e " ${GREEN}./run.sh status${NC}" + echo -e "${CYAN}${BOLD}=========================================${NC}" +} + +# Check if service is running +is_running() { + if [ -z "$PYTHON_CMD" ]; then + detect_python_command 2>/dev/null || PYTHON_CMD="python3" + fi + pid=$(ps ax | grep -i app.py | grep "${BASE_DIR}" | grep "$PYTHON_CMD" | grep -v grep | awk '{print $1}') + [ -n "$pid" ] +} + +# Get service PID +get_pid() { + if [ -z "$PYTHON_CMD" ]; then + detect_python_command 2>/dev/null || PYTHON_CMD="python3" + fi + ps ax | grep -i app.py | grep "${BASE_DIR}" | grep "$PYTHON_CMD" | grep -v grep | awk '{print $1}' +} + +# Start service +cmd_start() { + # Check if config.json exists + if [ ! -f "${BASE_DIR}/config.json" ]; then + echo -e "${RED}${EMOJI_CROSS} config.json not found${NC}" + echo -e "${YELLOW}Please run './run.sh' to configure first${NC}" exit 1 fi - - # 安装依赖 - install_dependencies - - # 启动项目 - run_project -} - -# 停止项目 -stop_project() { - echo -e "${GREEN}🛑 正在停止项目...${NC}" - cd "${BASE_DIR}" - pid=$(ps ax | grep -i app.py | grep "${BASE_DIR}" | grep python3 | grep -v grep | awk '{print $1}') - if [ -z "$pid" ] ; then - echo -e "${YELLOW}⚠️ 未找到正在运行的 ChatGPT-on-WeChat。${NC}" + + if is_running; then + echo -e "${YELLOW}${EMOJI_WARN} CowAgent is already running (PID: $(get_pid))${NC}" + echo -e "${YELLOW}Use './run.sh restart' to restart${NC}" return fi - - echo -e "${GREEN}🛑 正在运行的 ChatGPT-on-WeChat (PID: ${pid})${NC}" - - kill ${pid} - sleep 3 - - if ps -p $pid > /dev/null; then - echo -e "${YELLOW}⚠️ 进程未停止,尝试强制终止...${NC}" - kill -9 ${pid} - fi - - echo -e "${GREEN}✅ 已停止 ChatGPT-on-WeChat (PID: ${pid})${NC}" + + check_python_version + start_project } -# 主函数,根据用户参数执行操作 -case "$1" in - start) - check_config_file +# Stop service +cmd_stop() { + echo -e "${GREEN}${EMOJI_STOP} Stopping CowAgent...${NC}" + + if ! is_running; then + echo -e "${YELLOW}${EMOJI_WARN} CowAgent is not running${NC}" + return + fi + + pid=$(get_pid) + echo -e "${GREEN}Found running process (PID: ${pid})${NC}" + + kill ${pid} + sleep 3 + + if ps -p ${pid} > /dev/null 2>&1; then + echo -e "${YELLOW}⚠️ Process not stopped, forcing termination...${NC}" + kill -9 ${pid} + fi + + echo -e "${GREEN}${EMOJI_CHECK} CowAgent stopped${NC}" +} + +# Restart service +cmd_restart() { + cmd_stop + sleep 1 + cmd_start +} + +# Check status +cmd_status() { + echo -e "${CYAN}${BOLD}=========================================${NC}" + echo -e "${CYAN}${BOLD} ${EMOJI_COW} CowAgent Status${NC}" + echo -e "${CYAN}${BOLD}=========================================${NC}" + + if is_running; then + pid=$(get_pid) + echo -e "${GREEN}Status:${NC} ✅ Running" + echo -e "${GREEN}PID:${NC} ${pid}" + if [ -f "${BASE_DIR}/nohup.out" ]; then + echo -e "${GREEN}Logs:${NC} ${BASE_DIR}/nohup.out" + fi + else + echo -e "${YELLOW}Status:${NC} ⭐ Stopped" + fi + + if [ -f "${BASE_DIR}/config.json" ]; then + model=$(grep -o '"model"[[:space:]]*:[[:space:]]*"[^"]*"' "${BASE_DIR}/config.json" | cut -d'"' -f4) + channel=$(grep -o '"channel_type"[[:space:]]*:[[:space:]]*"[^"]*"' "${BASE_DIR}/config.json" | cut -d'"' -f4) + echo -e "${GREEN}Model:${NC} ${model}" + echo -e "${GREEN}Channel:${NC} ${channel}" + fi + + echo -e "${CYAN}${BOLD}=========================================${NC}" +} + +# View logs +cmd_logs() { + if [ -f "${BASE_DIR}/nohup.out" ]; then + echo -e "${YELLOW}Viewing logs (Ctrl+C to exit):${NC}" + tail -f "${BASE_DIR}/nohup.out" + else + echo -e "${RED}❌ Log file not found: ${BASE_DIR}/nohup.out${NC}" + fi +} + +# Reconfigure +cmd_config() { + echo -e "${YELLOW}${EMOJI_WRENCH} Reconfiguring CowAgent...${NC}" + + if [ -f "${BASE_DIR}/config.json" ]; then + backup_file="${BASE_DIR}/config.json.backup.$(date +%s)" + cp "${BASE_DIR}/config.json" "${backup_file}" + echo -e "${GREEN}✅ Backed up config to: ${backup_file}${NC}" + fi + + check_python_version + install_dependencies + select_model + configure_model + select_channel + configure_channel + create_config_file + + echo "" + read -p "Restart service now? [Y/n]: " restart_now + if [[ ! $restart_now == [Nn]* ]]; then + cmd_restart + fi +} + +# Update project +cmd_update() { + echo -e "${GREEN}${EMOJI_WRENCH} Updating CowAgent...${NC}" + cd "${BASE_DIR}" + + # Stop service + if is_running; then + cmd_stop + fi + + # Update code + if [ -d .git ]; then + echo -e "${GREEN}🔄 Pulling latest code...${NC}" + git pull || { + echo -e "${YELLOW}⚠️ GitHub failed, trying Gitee...${NC}" + git remote set-url origin https://gitee.com/zhayujie/chatgpt-on-wechat.git + git pull + } + else + echo -e "${YELLOW}⚠️ Not a git repository, skipping code update${NC}" + fi + + # Reinstall dependencies + check_python_version + install_dependencies + + # Restart service + cmd_start +} + +# Installation mode +install_mode() { + clear + echo -e "${CYAN}${BOLD}=========================================${NC}" + echo -e "${CYAN}${BOLD} ${EMOJI_COW} CowAgent Installation${NC}" + echo -e "${CYAN}${BOLD}=========================================${NC}" + echo "" + sleep 1 + + if [ "$IS_PROJECT_DIR" = true ]; then + echo -e "${GREEN}✅ Detected existing project directory.${NC}" + + if [ -f "${BASE_DIR}/config.json" ]; then + echo -e "${GREEN}✅ Project already configured${NC}" + echo "" + show_usage + return + fi + + echo -e "${YELLOW}📝 No config.json found. Let's configure your project!${NC}" + echo "" + + # Project directory already exists, skip clone check_python_version - run_project - ;; - stop) - stop_project - ;; - restart) - stop_project - check_config_file + else + # Remote install mode, need to clone project check_python_version - run_project - ;; - update) - check_config_file - check_python_version - update_project - ;; - *) - echo -e "${YELLOW}=========================================${NC}" - echo -e "${YELLOW}用法:${GREEN}$0 ${BLUE}{start|stop|restart|update}${NC}" - echo -e "${YELLOW}示例:${NC}" - echo -e " ${GREEN}$0 ${BLUE}start${NC}" - echo -e " ${GREEN}$0 ${BLUE}stop${NC}" - echo -e " ${GREEN}$0 ${BLUE}restart${NC}" - echo -e " ${GREEN}$0 ${BLUE}update${NC}" - echo -e "${YELLOW}=========================================${NC}" - exit 1 - ;; -esac \ No newline at end of file + clone_project + fi + + # Install dependencies and configure + install_dependencies + select_model + configure_model + select_channel + configure_channel + create_config_file + + # Show completion message + echo "" + echo -e "${CYAN}${BOLD}=========================================${NC}" + echo -e "${GREEN}${BOLD}✅ Installation Complete!${NC}" + echo -e "${CYAN}${BOLD}=========================================${NC}" + echo "" + show_usage +} + +# Main function +main() { + case "$1" in + start) + if [ "$IS_PROJECT_DIR" = false ]; then + echo -e "${RED}❌ Must run in project directory${NC}" + exit 1 + fi + cmd_start + ;; + stop) + if [ "$IS_PROJECT_DIR" = false ]; then + echo -e "${RED}❌ Must run in project directory${NC}" + exit 1 + fi + cmd_stop + ;; + restart) + if [ "$IS_PROJECT_DIR" = false ]; then + echo -e "${RED}❌ Must run in project directory${NC}" + exit 1 + fi + cmd_restart + ;; + status) + if [ "$IS_PROJECT_DIR" = false ]; then + echo -e "${RED}❌ Must run in project directory${NC}" + exit 1 + fi + cmd_status + ;; + logs) + if [ "$IS_PROJECT_DIR" = false ]; then + echo -e "${RED}❌ Must run in project directory${NC}" + exit 1 + fi + cmd_logs + ;; + config) + if [ "$IS_PROJECT_DIR" = false ]; then + echo -e "${RED}❌ Must run in project directory${NC}" + exit 1 + fi + cmd_config + ;; + update) + if [ "$IS_PROJECT_DIR" = false ]; then + echo -e "${RED}❌ Must run in project directory${NC}" + exit 1 + fi + cmd_update + ;; + help|--help|-h) + show_usage + ;; + "") + # No command - install/configure mode + install_mode + ;; + *) + echo -e "${RED}❌ Unknown command: $1${NC}" + echo "" + show_usage + exit 1 + ;; + esac +} + +# Execute main function +main "$@"