mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-05-06 20:02:49 +08:00
feat: 飞书官方插件迁移 + 配对审批 + Gateway防卡死 + 微信升级修复 + 更新检测修复
- 飞书渠道从 @openclaw/feishu 迁移到 @larksuite/openclaw-lark 官方插件 - 保存飞书配置时自动禁用旧 feishu 插件,防止新旧插件冲突 - 所有主要渠道(飞书/Telegram/Discord/Slack)启用配对审批UI - gateway_command 增加20s超时,超时后force-kill+fresh start - 全平台启动前端口占用检查,防止Guardian无限拉起 - Linux gateway_command 补齐 Duration 导入和 cleanup_zombie 实现 - Guardian自动守护在Tauri桌面端也启用,轮询间隔30s→15s - 微信渠道:升级操作不再弹出扫码二维码,按钮文案区分安装/升级 - 版本更新检测:CI不再将minAppVersion写死为当前版本 - 部署脚本增强OpenClaw检测,支持已安装的官方版 - 日间/夜间模式圆形扩散切换动画(View Transitions API) - API错误信息完整展示(429限流等),URL自动转可点击链接 - 第三方API接入引导优化:移除内置密钥,引导式流程 - 修复全平台 Clippy 警告(strip_prefix/dead_code/unnecessary_unwrap等) - Rust代码格式化修复(cargo fmt) - toast组件支持HTML内容渲染 - Rust后端test_model返回详细错误信息
This commit is contained in:
@@ -1772,7 +1772,7 @@ const handlers = {
|
||||
install_qqbot_plugin() {
|
||||
const bin = findOpenclawBin() || 'openclaw'
|
||||
try {
|
||||
execSync(`${bin} plugins install @sliverp/qqbot@latest`, { timeout: 60000, cwd: homedir() })
|
||||
execSync(`${bin} plugins install @tencent-connect/openclaw-qqbot@latest`, { timeout: 600000, cwd: homedir() })
|
||||
return '安装成功'
|
||||
} catch (e) {
|
||||
throw new Error('QQBot 插件安装失败: ' + (e.message || e))
|
||||
@@ -3122,6 +3122,75 @@ const handlers = {
|
||||
return result
|
||||
},
|
||||
|
||||
// Agent 渠道绑定管理
|
||||
list_all_bindings() {
|
||||
const cfg = readConfig()
|
||||
const bindings = cfg.bindings || []
|
||||
return { bindings }
|
||||
},
|
||||
|
||||
save_agent_binding({ agentId, channel, accountId, bindingConfig }) {
|
||||
const cfg = readConfig()
|
||||
if (!cfg.bindings) cfg.bindings = []
|
||||
const bindings = cfg.bindings
|
||||
|
||||
// 构建新绑定
|
||||
const newBinding = {
|
||||
type: 'route',
|
||||
agentId,
|
||||
match: {
|
||||
channel,
|
||||
...(accountId ? { accountId } : {}),
|
||||
},
|
||||
}
|
||||
|
||||
// 合并 peer 配置到 match
|
||||
if (bindingConfig && typeof bindingConfig === 'object') {
|
||||
if (bindingConfig.peer) {
|
||||
newBinding.match.peer = bindingConfig.peer
|
||||
}
|
||||
}
|
||||
|
||||
// 查找并更新现有绑定(相同 agentId + channel + accountId)
|
||||
const accountKey = accountId || ''
|
||||
let found = false
|
||||
for (let i = 0; i < bindings.length; i++) {
|
||||
const b = bindings[i]
|
||||
if (b.agentId === agentId && b.match?.channel === channel) {
|
||||
const existingAccount = b.match?.accountId || ''
|
||||
if (existingAccount === accountKey) {
|
||||
bindings[i] = newBinding
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
bindings.push(newBinding)
|
||||
}
|
||||
|
||||
saveConfig(cfg)
|
||||
return { ok: true }
|
||||
},
|
||||
|
||||
delete_agent_binding({ agentId, channel, accountId }) {
|
||||
const cfg = readConfig()
|
||||
if (!cfg.bindings) cfg.bindings = []
|
||||
const bindings = cfg.bindings
|
||||
const accountKey = accountId || ''
|
||||
|
||||
const before = bindings.length
|
||||
cfg.bindings = bindings.filter(b => {
|
||||
if (b.agentId !== agentId) return true
|
||||
if (b.match?.channel !== channel) return true
|
||||
const existingAccount = b.match?.accountId || ''
|
||||
return existingAccount !== accountKey
|
||||
})
|
||||
|
||||
saveConfig(cfg)
|
||||
return { ok: true, removed: before - cfg.bindings.length }
|
||||
},
|
||||
|
||||
// 记忆文件
|
||||
list_memory_files({ category, agent_id }) {
|
||||
const suffix = agent_id && agent_id !== 'main' ? `/agents/${agent_id}` : ''
|
||||
|
||||
536
scripts/docker-deploy.sh
Normal file
536
scripts/docker-deploy.sh
Normal file
@@ -0,0 +1,536 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# ClawPanel Docker 部署脚本
|
||||
# =============================================================================
|
||||
# 功能:
|
||||
# 1. 检查 Docker 环境
|
||||
# 2. 构建 Docker 镜像
|
||||
# 3. 启动/停止/重启容器
|
||||
# 4. 查看日志
|
||||
# 5. 常见问题排查
|
||||
# =============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 颜色定义
|
||||
# -----------------------------------------------------------------------------
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 配置
|
||||
# -----------------------------------------------------------------------------
|
||||
CONTAINER_NAME="clawpanel"
|
||||
IMAGE_NAME="clawpanel"
|
||||
IMAGE_TAG="latest"
|
||||
DEFAULT_PORT=1420
|
||||
CONFIG_DIR="$HOME/.openclaw"
|
||||
DATA_DIR="$(pwd)/data"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 工具函数
|
||||
# -----------------------------------------------------------------------------
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "${CYAN}[STEP]${NC} $1"
|
||||
}
|
||||
|
||||
separator() {
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 检查 Docker 环境
|
||||
# -----------------------------------------------------------------------------
|
||||
check_docker() {
|
||||
log_step "检查 Docker 环境..."
|
||||
|
||||
# 检查 Docker 是否安装
|
||||
if ! command -v docker &> /dev/null; then
|
||||
log_error "Docker 未安装或不在 PATH 中"
|
||||
echo ""
|
||||
echo "请先安装 Docker:"
|
||||
echo " Ubuntu/Debian: curl -fsSL https://get.docker.com | sh"
|
||||
echo " CentOS/RHEL: yum install -y docker-ce"
|
||||
echo " Arch Linux: pacman -S docker"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查 Docker 服务是否运行
|
||||
if ! docker info &> /dev/null; then
|
||||
log_error "Docker 服务未运行"
|
||||
echo ""
|
||||
echo "请启动 Docker 服务:"
|
||||
echo " sudo systemctl start docker"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查 Docker Compose
|
||||
if docker compose version &> /dev/null; then
|
||||
COMPOSE_CMD="docker compose"
|
||||
log_info "Docker Compose v2 可用"
|
||||
elif command -v docker-compose &> /dev/null; then
|
||||
COMPOSE_CMD="docker-compose"
|
||||
log_info "Docker Compose v1 可用"
|
||||
else
|
||||
log_warn "Docker Compose 未安装,部分功能可能不可用"
|
||||
COMPOSE_CMD=""
|
||||
fi
|
||||
|
||||
log_info "Docker 环境检查通过"
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 检查前置要求
|
||||
# -----------------------------------------------------------------------------
|
||||
check_requirements() {
|
||||
log_step "检查前置要求..."
|
||||
|
||||
# 检查构建上下文
|
||||
if [ ! -f "Dockerfile" ]; then
|
||||
log_error "Dockerfile 不存在,请确保在项目根目录运行此脚本"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "package.json" ]; then
|
||||
log_error "package.json 不存在,请确保在项目根目录运行此脚本"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 创建必要目录
|
||||
mkdir -p "$DATA_DIR"
|
||||
|
||||
log_info "前置要求检查通过"
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 拉取最新代码(可选)
|
||||
# -----------------------------------------------------------------------------
|
||||
pull_latest() {
|
||||
log_step "检查更新..."
|
||||
|
||||
if [ -d ".git" ]; then
|
||||
git fetch origin main
|
||||
LOCAL=$(git rev-parse @)
|
||||
REMOTE=$(git rev-parse origin/main)
|
||||
|
||||
if [ "$LOCAL" != "$REMOTE" ]; then
|
||||
log_warn "本地版本落后于远程,是否更新?"
|
||||
read -p "输入 y 更新,其他跳过: " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
log_info "更新代码..."
|
||||
git pull origin main
|
||||
fi
|
||||
else
|
||||
log_info "已是最新版本"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 构建镜像
|
||||
# -----------------------------------------------------------------------------
|
||||
build_image() {
|
||||
log_step "构建 Docker 镜像..."
|
||||
|
||||
# 启用 BuildKit
|
||||
export DOCKER_BUILDKIT=1
|
||||
|
||||
# 构建镜像
|
||||
log_info "构建镜像: ${IMAGE_NAME}:${IMAGE_TAG}"
|
||||
|
||||
if docker build \
|
||||
--tag "${IMAGE_NAME}:${IMAGE_TAG}" \
|
||||
--tag "${IMAGE_NAME}:latest" \
|
||||
--build-arg NPM_REGISTRY=https://registry.npmmirror.com \
|
||||
--progress=plain \
|
||||
.; then
|
||||
log_info "镜像构建成功"
|
||||
else
|
||||
log_error "镜像构建失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 显示镜像大小
|
||||
IMAGE_SIZE=$(docker images "${IMAGE_NAME}:${IMAGE_TAG}" --format "{{.Size}}")
|
||||
log_info "镜像大小: $IMAGE_SIZE"
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 启动容器
|
||||
# -----------------------------------------------------------------------------
|
||||
start_container() {
|
||||
log_step "启动容器..."
|
||||
|
||||
# 检查容器是否已存在
|
||||
if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
||||
if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
||||
log_warn "容器已在运行中"
|
||||
return 0
|
||||
else
|
||||
log_info "容器已存在,重新启动..."
|
||||
docker rm -f "$CONTAINER_NAME" > /dev/null 2>&1
|
||||
fi
|
||||
fi
|
||||
|
||||
# 检查配置目录
|
||||
if [ ! -d "$CONFIG_DIR" ]; then
|
||||
log_warn "OpenClaw 配置目录不存在: $CONFIG_DIR"
|
||||
log_info "将创建目录..."
|
||||
mkdir -p "$CONFIG_DIR"
|
||||
fi
|
||||
|
||||
# 启动容器(使用 host 网络模式)
|
||||
log_info "启动容器 (host 网络模式)..."
|
||||
|
||||
docker run -d \
|
||||
--name "$CONTAINER_NAME" \
|
||||
--hostname "$CONTAINER_NAME" \
|
||||
--network host \
|
||||
--restart unless-stopped \
|
||||
--volume "$CONFIG_DIR:/root/.openclaw" \
|
||||
--volume "$DATA_DIR:/app/data" \
|
||||
--env "NODE_ENV=production" \
|
||||
--env "OPENCLAW_URL=http://127.0.0.1:18789" \
|
||||
--env "TZ=Asia/Shanghai" \
|
||||
--health-cmd "curl -f http://localhost:1420/ || exit 1" \
|
||||
--health-interval "30s" \
|
||||
--health-timeout "5s" \
|
||||
--health-retries "3" \
|
||||
--log-driver "json-file" \
|
||||
--log-opt "max-size=10m" \
|
||||
--log-opt "max-file=3" \
|
||||
"${IMAGE_NAME}:${IMAGE_TAG}"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_info "容器启动成功"
|
||||
else
|
||||
log_error "容器启动失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 停止容器
|
||||
# -----------------------------------------------------------------------------
|
||||
stop_container() {
|
||||
log_step "停止容器..."
|
||||
|
||||
if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
||||
docker stop "$CONTAINER_NAME"
|
||||
log_info "容器已停止"
|
||||
else
|
||||
log_warn "容器未运行"
|
||||
fi
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 重启容器
|
||||
# -----------------------------------------------------------------------------
|
||||
restart_container() {
|
||||
log_step "重启容器..."
|
||||
stop_container
|
||||
sleep 2
|
||||
start_container
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 删除容器
|
||||
# -----------------------------------------------------------------------------
|
||||
remove_container() {
|
||||
log_step "删除容器..."
|
||||
|
||||
if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
||||
docker rm -f "$CONTAINER_NAME" > /dev/null 2>&1
|
||||
log_info "容器已删除"
|
||||
else
|
||||
log_warn "容器不存在"
|
||||
fi
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 查看状态
|
||||
# -----------------------------------------------------------------------------
|
||||
show_status() {
|
||||
log_step "容器状态:"
|
||||
|
||||
if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
||||
echo ""
|
||||
docker ps -a --filter "name=${CONTAINER_NAME}" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
|
||||
|
||||
# 显示资源使用
|
||||
echo ""
|
||||
log_info "资源使用:"
|
||||
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}" "$CONTAINER_NAME" 2>/dev/null || true
|
||||
else
|
||||
log_warn "容器不存在"
|
||||
fi
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 查看日志
|
||||
# -----------------------------------------------------------------------------
|
||||
show_logs() {
|
||||
log_step "容器日志 (Ctrl+C 退出):"
|
||||
echo ""
|
||||
|
||||
if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
||||
docker logs -f "$CONTAINER_NAME"
|
||||
else
|
||||
log_error "容器未运行,无法查看日志"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 进入容器
|
||||
# -----------------------------------------------------------------------------
|
||||
enter_container() {
|
||||
log_step "进入容器 shell..."
|
||||
echo ""
|
||||
|
||||
if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
||||
docker exec -it "$CONTAINER_NAME" /bin/sh
|
||||
else
|
||||
log_error "容器未运行,无法进入"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 常见问题排查
|
||||
# -----------------------------------------------------------------------------
|
||||
troubleshoot() {
|
||||
echo ""
|
||||
separator
|
||||
log_info "常见问题排查"
|
||||
separator
|
||||
echo ""
|
||||
|
||||
log_info "1. 检查容器是否运行:"
|
||||
echo " docker ps | grep $CONTAINER_NAME"
|
||||
echo ""
|
||||
|
||||
log_info "2. 查看容器日志:"
|
||||
echo " docker logs $CONTAINER_NAME"
|
||||
echo ""
|
||||
|
||||
log_info "3. 检查端口占用:"
|
||||
echo " netstat -tlnp | grep 1420"
|
||||
echo " ss -tlnp | grep 1420"
|
||||
echo ""
|
||||
|
||||
log_info "4. 检查 OpenClaw 配置:"
|
||||
echo " cat ~/.openclaw/openclaw.json"
|
||||
echo ""
|
||||
|
||||
log_info "5. 测试 Gateway 连接:"
|
||||
echo " curl http://localhost:18789/health"
|
||||
echo ""
|
||||
|
||||
log_info "6. 重建容器:"
|
||||
echo " ./docker-deploy.sh rebuild"
|
||||
echo ""
|
||||
|
||||
log_info "7. 完全重置:"
|
||||
echo " docker stop $CONTAINER_NAME && docker rm $CONTAINER_NAME"
|
||||
echo " docker rmi ${IMAGE_NAME}:${IMAGE_TAG}"
|
||||
echo " ./docker-deploy.sh start"
|
||||
echo ""
|
||||
|
||||
log_info "8. 查看 OpenClaw 日志:"
|
||||
echo " docker exec $CONTAINER_NAME cat /home/appuser/.openclaw/logs/gateway.log"
|
||||
echo ""
|
||||
|
||||
separator
|
||||
echo ""
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 获取本机 IP
|
||||
# -----------------------------------------------------------------------------
|
||||
get_local_ip() {
|
||||
ip route get 1 2>/dev/null | awk '{print $7; exit}' || \
|
||||
hostname -I 2>/dev/null | awk '{print $1}' || \
|
||||
echo "localhost"
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 显示访问信息
|
||||
# -----------------------------------------------------------------------------
|
||||
show_access_info() {
|
||||
local ip=$(get_local_ip)
|
||||
|
||||
echo ""
|
||||
separator
|
||||
log_info "部署完成!"
|
||||
separator
|
||||
echo ""
|
||||
echo -e " ${CYAN}🌐 访问地址:${NC}"
|
||||
echo " http://${ip}:1420"
|
||||
echo ""
|
||||
echo -e " ${CYAN}📁 配置目录:${NC}"
|
||||
echo " $CONFIG_DIR"
|
||||
echo ""
|
||||
echo -e " ${CYAN}📋 容器名称:${NC}"
|
||||
echo " $CONTAINER_NAME"
|
||||
echo ""
|
||||
echo " 常用命令:"
|
||||
echo " ./docker-deploy.sh logs # 查看日志"
|
||||
echo " ./docker-deploy.sh status # 查看状态"
|
||||
echo " ./docker-deploy.sh stop # 停止"
|
||||
echo " ./docker-deploy.sh start # 启动"
|
||||
echo " ./docker-deploy.sh restart # 重启"
|
||||
echo " ./docker-deploy.sh rebuild # 重建"
|
||||
echo " ./docker-deploy.sh shell # 进入容器"
|
||||
echo " ./docker-deploy.sh help # 帮助"
|
||||
echo ""
|
||||
separator
|
||||
echo ""
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 使用 Docker Compose 方式
|
||||
# -----------------------------------------------------------------------------
|
||||
compose_up() {
|
||||
if [ -z "$COMPOSE_CMD" ]; then
|
||||
log_error "Docker Compose 不可用,请使用单机模式"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_step "使用 Docker Compose 启动..."
|
||||
|
||||
if [ ! -f "docker-compose.yml" ]; then
|
||||
log_error "docker-compose.yml 不存在"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
$COMPOSE_CMD up -d
|
||||
log_info "服务已启动"
|
||||
}
|
||||
|
||||
compose_down() {
|
||||
if [ -z "$COMPOSE_CMD" ]; then
|
||||
log_error "Docker Compose 不可用"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_step "停止 Docker Compose 服务..."
|
||||
$COMPOSE_CMD down
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 显示帮助
|
||||
# -----------------------------------------------------------------------------
|
||||
show_help() {
|
||||
echo ""
|
||||
echo "ClawPanel Docker 部署脚本"
|
||||
echo ""
|
||||
echo "用法: $0 [命令]"
|
||||
echo ""
|
||||
echo "命令:"
|
||||
echo " start 启动容器"
|
||||
echo " stop 停止容器"
|
||||
echo " restart 重启容器"
|
||||
echo " rebuild 重建容器(删除并重新创建)"
|
||||
echo " remove 删除容器(保留镜像)"
|
||||
echo " status 查看容器状态"
|
||||
echo " logs 查看容器日志"
|
||||
echo " shell 进入容器 shell"
|
||||
echo " troubleshoot 常见问题排查"
|
||||
echo " compose 使用 Docker Compose 方式启动"
|
||||
echo " help 显示帮助"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 start # 启动容器"
|
||||
echo " $0 logs -f # 实时查看日志"
|
||||
echo " $0 rebuild # 重建容器"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 主流程
|
||||
# -----------------------------------------------------------------------------
|
||||
main() {
|
||||
case "${1:-help}" in
|
||||
check)
|
||||
check_docker
|
||||
;;
|
||||
build)
|
||||
check_docker
|
||||
check_requirements
|
||||
pull_latest
|
||||
build_image
|
||||
;;
|
||||
start)
|
||||
check_docker
|
||||
check_requirements
|
||||
start_container
|
||||
show_access_info
|
||||
;;
|
||||
stop)
|
||||
stop_container
|
||||
;;
|
||||
restart)
|
||||
restart_container
|
||||
;;
|
||||
rebuild)
|
||||
check_docker
|
||||
remove_container
|
||||
build_image
|
||||
start_container
|
||||
show_access_info
|
||||
;;
|
||||
remove)
|
||||
remove_container
|
||||
;;
|
||||
status)
|
||||
check_docker
|
||||
show_status
|
||||
;;
|
||||
logs)
|
||||
show_logs
|
||||
;;
|
||||
shell)
|
||||
enter_container
|
||||
;;
|
||||
troubleshoot)
|
||||
troubleshoot
|
||||
;;
|
||||
compose)
|
||||
check_docker
|
||||
compose_up
|
||||
show_access_info
|
||||
;;
|
||||
compose-down)
|
||||
compose_down
|
||||
;;
|
||||
help|--help|-h)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
log_error "未知命令: $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -129,18 +129,69 @@ install_git() {
|
||||
echo "✅ Git 安装完成"
|
||||
}
|
||||
|
||||
# 查找 openclaw 可执行文件(兼容各种安装方式)
|
||||
find_openclaw() {
|
||||
# 1. 直接在 PATH 中查找
|
||||
if command -v openclaw &> /dev/null; then
|
||||
echo "$(command -v openclaw)"
|
||||
return 0
|
||||
fi
|
||||
# 2. 常见 npm 全局安装路径
|
||||
local candidates=(
|
||||
"/usr/local/bin/openclaw"
|
||||
"/usr/bin/openclaw"
|
||||
"$HOME/.npm-global/bin/openclaw"
|
||||
"$HOME/.local/bin/openclaw"
|
||||
)
|
||||
# 3. 从 npm prefix 获取(不使用 sudo,避免触发密码提示)
|
||||
local npm_prefix=$(npm config get prefix 2>/dev/null)
|
||||
if [ -n "$npm_prefix" ]; then
|
||||
candidates+=("$npm_prefix/bin/openclaw")
|
||||
fi
|
||||
for p in "${candidates[@]}"; do
|
||||
if [ -x "$p" ]; then
|
||||
echo "$p"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# 检测 OpenClaw 版本来源(官方 vs 汉化版)
|
||||
detect_openclaw_source() {
|
||||
local oc_bin="$1"
|
||||
local ver=$("$oc_bin" --version 2>/dev/null || echo "")
|
||||
if echo "$ver" | grep -qi "zh\|汉化\|chinese"; then
|
||||
echo "chinese"
|
||||
else
|
||||
echo "official"
|
||||
fi
|
||||
}
|
||||
|
||||
# 安装 OpenClaw
|
||||
install_openclaw() {
|
||||
if command -v openclaw &> /dev/null; then
|
||||
echo "✅ OpenClaw 已安装: $(openclaw --version 2>/dev/null || echo '未知版本')"
|
||||
local oc_path=$(find_openclaw)
|
||||
if [ -n "$oc_path" ]; then
|
||||
local oc_ver=$("$oc_path" --version 2>/dev/null || echo "未知版本")
|
||||
local oc_src=$(detect_openclaw_source "$oc_path")
|
||||
if [ "$oc_src" = "chinese" ]; then
|
||||
echo "✅ OpenClaw 汉化版已安装: $oc_ver (${oc_path})"
|
||||
else
|
||||
echo "✅ OpenClaw 已安装: $oc_ver (${oc_path})"
|
||||
fi
|
||||
# 确保 openclaw 在 PATH 中(防止后续步骤找不到)
|
||||
if ! command -v openclaw &> /dev/null; then
|
||||
export PATH="$(dirname "$oc_path"):$PATH"
|
||||
echo "ℹ️ 已将 $(dirname "$oc_path") 加入 PATH"
|
||||
fi
|
||||
else
|
||||
echo "📦 安装 OpenClaw 汉化版..."
|
||||
if [ "$IS_ROOT" = true ]; then
|
||||
npm install -g @qingchencloud/openclaw-zh --registry "$NPM_REGISTRY" || \
|
||||
npm install -g @qingchencloud/openclaw-zh --registry https://registry.npmjs.org
|
||||
else
|
||||
sudo npm install -g @qingchencloud/openclaw-zh --registry "$NPM_REGISTRY" || \
|
||||
sudo npm install -g @qingchencloud/openclaw-zh --registry https://registry.npmjs.org
|
||||
sudo -E npm install -g @qingchencloud/openclaw-zh --registry "$NPM_REGISTRY" || \
|
||||
sudo -E npm install -g @qingchencloud/openclaw-zh --registry https://registry.npmjs.org
|
||||
fi
|
||||
echo "✅ OpenClaw 安装完成"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user