Mac-Menu, Mac-Diagnose, and Windows-Diagnose were missing the core-mac/core-win → core migration shim, causing them to fail on existing USB drives that still have the old directory name.
307 lines
9.8 KiB
Bash
Executable File
307 lines
9.8 KiB
Bash
Executable File
#!/bin/bash
|
||
# U-Claw Menu - Portable AI Agent
|
||
# macOS version
|
||
|
||
UCLAW_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||
APP_DIR="$UCLAW_DIR/app"
|
||
|
||
# Migration shim: rename old core-mac to core for existing USB users
|
||
if [ -d "$APP_DIR/core-mac" ] && [ ! -d "$APP_DIR/core" ]; then
|
||
mv "$APP_DIR/core-mac" "$APP_DIR/core"
|
||
fi
|
||
|
||
CORE_DIR="$APP_DIR/core"
|
||
DATA_DIR="$UCLAW_DIR/data"
|
||
STATE_DIR="$DATA_DIR/.openclaw"
|
||
CONFIG_PATH="$STATE_DIR/openclaw.json"
|
||
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
CYAN='\033[0;36m'
|
||
WHITE='\033[1;37m'
|
||
NC='\033[0m'
|
||
BOLD='\033[1m'
|
||
DIM='\033[2m'
|
||
|
||
# Node.js — detect architecture
|
||
ARCH=$(uname -m)
|
||
if [ "$ARCH" = "arm64" ]; then
|
||
NODE_DIR="$APP_DIR/runtime/node-mac-arm64"
|
||
else
|
||
NODE_DIR="$APP_DIR/runtime/node-mac-x64"
|
||
fi
|
||
NODE_BIN="$NODE_DIR/bin/node"
|
||
NPM_BIN="$NODE_DIR/bin/npm"
|
||
export PATH="$NODE_DIR/bin:$PATH"
|
||
export OPENCLAW_HOME="$DATA_DIR"
|
||
export OPENCLAW_STATE_DIR="$STATE_DIR"
|
||
export OPENCLAW_CONFIG_PATH="$CONFIG_PATH"
|
||
|
||
mkdir -p "$STATE_DIR" "$DATA_DIR/memory" "$DATA_DIR/backups" "$DATA_DIR/logs"
|
||
|
||
# Load maintenance functions
|
||
source "$UCLAW_DIR/lib/maintain.sh"
|
||
|
||
# Remove macOS quarantine
|
||
if xattr -l "$NODE_BIN" 2>/dev/null | grep -q "com.apple.quarantine"; then
|
||
xattr -rd com.apple.quarantine "$UCLAW_DIR" 2>/dev/null || true
|
||
fi
|
||
|
||
# Run openclaw command
|
||
OPENCLAW_MJS="$CORE_DIR/node_modules/openclaw/openclaw.mjs"
|
||
|
||
run_oc() {
|
||
cd "$CORE_DIR"
|
||
"$NODE_BIN" "$OPENCLAW_MJS" "$@"
|
||
}
|
||
|
||
# Show menu
|
||
show_menu() {
|
||
clear
|
||
local NODE_VER=$("$NODE_BIN" --version 2>/dev/null || echo "N/A")
|
||
local CFG_STATUS="${RED}未配置${NC}"
|
||
[ -f "$CONFIG_PATH" ] && CFG_STATUS="${GREEN}已配置${NC}"
|
||
|
||
echo ""
|
||
echo -e " ${CYAN}${BOLD}╔══════════════════════════════════════╗"
|
||
echo -e " ║ U-Claw 虾盘 v1.1 ║"
|
||
echo -e " ║ Portable AI Agent ║"
|
||
echo -e " ╚══════════════════════════════════════╝${NC}"
|
||
echo ""
|
||
echo -e " Node: ${GREEN}${NODE_VER}${NC} 配置: ${CFG_STATUS}"
|
||
echo ""
|
||
echo -e " ${WHITE}${BOLD}── 配置 ──────────────────────────────${NC}"
|
||
echo -e " ${GREEN}[1]${NC} 配置向导(选模型、填 API Key)"
|
||
echo -e " ${GREEN}[2]${NC} 打开网页控制台"
|
||
echo ""
|
||
echo -e " ${WHITE}${BOLD}── 聊天平台 ──────────────────────────${NC}"
|
||
echo -e " ${GREEN}[3]${NC} 接入 QQ 机器人(推荐,1分钟搞定)"
|
||
echo -e " ${GREEN}[4]${NC} 接入其他平台(飞书/Telegram/微信)"
|
||
echo ""
|
||
echo -e " ${WHITE}${BOLD}── 维护 ──────────────────────────────${NC}"
|
||
echo -e " ${GREEN}[5]${NC} 诊断修复"
|
||
echo -e " ${GREEN}[6]${NC} 备份配置"
|
||
echo -e " ${GREEN}[7]${NC} 恢复备份"
|
||
echo -e " ${GREEN}[8]${NC} 系统信息"
|
||
echo ""
|
||
echo -e " ${WHITE}${BOLD}── 高级维护 ──────────────────────────${NC}"
|
||
echo -e " ${GREEN}[9]${NC} 杀死残留进程"
|
||
echo -e " ${GREEN}[10]${NC} 查看日志"
|
||
echo -e " ${GREEN}[11]${NC} 恢复出厂设置"
|
||
echo -e " ${GREEN}[12]${NC} 卸载"
|
||
echo -e " ${GREEN}[13]${NC} 检查更新"
|
||
echo -e " ${GREEN}[14]${NC} 清理空间"
|
||
echo -e " ${GREEN}[15]${NC} 插件管理"
|
||
echo ""
|
||
echo -e " ${DIM}[0] 退出${NC}"
|
||
echo ""
|
||
}
|
||
|
||
# [1] Config wizard
|
||
do_config() {
|
||
echo ""
|
||
echo -e " ${CYAN}${BOLD}━━━ 配置向导 ━━━${NC}"
|
||
echo ""
|
||
echo -e " ${WHITE}国产模型选择提示:${NC}"
|
||
echo ""
|
||
echo -e " DeepSeek → 选 Custom Provider"
|
||
echo -e " URL: https://api.deepseek.com/v1"
|
||
echo -e " 模型: deepseek-chat"
|
||
echo -e " Kimi → 选 Moonshot AI"
|
||
echo -e " 通义千问 → 选 Qwen"
|
||
echo -e " 豆包 → 选 Volcano Engine"
|
||
echo ""
|
||
read -p " 按回车启动配置向导..."
|
||
run_oc onboard
|
||
}
|
||
|
||
# [2] Web dashboard
|
||
do_dashboard() {
|
||
echo ""
|
||
echo -e " ${CYAN}启动网页控制台...${NC}"
|
||
echo ""
|
||
cd "$CORE_DIR"
|
||
|
||
# Find free port
|
||
local PORT=18789
|
||
while lsof -i :$PORT >/dev/null 2>&1; do
|
||
PORT=$((PORT + 1))
|
||
if [ $PORT -gt 18799 ]; then
|
||
echo -e " ${RED}端口 18789-18799 全被占用${NC}"
|
||
return
|
||
fi
|
||
done
|
||
|
||
local TOKEN=$(python3 -c "import json,os; p='$CONFIG_PATH'; d=json.load(open(p)) if os.path.exists(p) else {}; print(d.get('gateway',{}).get('auth',{}).get('token','uclaw'))" 2>/dev/null || echo "uclaw")
|
||
|
||
"$NODE_BIN" "$OPENCLAW_MJS" gateway run --allow-unconfigured --force --port $PORT &
|
||
local PID=$!
|
||
|
||
for i in $(seq 1 30); do
|
||
sleep 0.5
|
||
if curl --noproxy '*' -s -o /dev/null "http://127.0.0.1:$PORT/" 2>/dev/null; then
|
||
local URL="http://127.0.0.1:$PORT/#token=$TOKEN"
|
||
echo -e " ${GREEN}控制台: $URL${NC}"
|
||
open "$URL" 2>/dev/null
|
||
break
|
||
fi
|
||
done
|
||
|
||
echo " 关闭此窗口会停止服务"
|
||
wait $PID
|
||
}
|
||
|
||
# [3] QQ Bot (pre-installed)
|
||
do_qq() {
|
||
echo ""
|
||
echo -e " ${CYAN}${BOLD}━━━ 接入 QQ 机器人 ━━━${NC}"
|
||
echo ""
|
||
echo -e " ${GREEN}QQ 插件已预装!只需输入 AppID 和 AppSecret。${NC}"
|
||
echo ""
|
||
echo " 获取方式: 访问 q.qq.com → 创建机器人"
|
||
echo ""
|
||
read -p " AppID: " QQ_ID
|
||
read -p " AppSecret: " QQ_SECRET
|
||
echo ""
|
||
|
||
if [ -z "$QQ_ID" ] || [ -z "$QQ_SECRET" ]; then
|
||
echo -e " ${YELLOW}已取消${NC}"
|
||
return
|
||
fi
|
||
|
||
run_oc channels add --channel qqbot --token "${QQ_ID}:${QQ_SECRET}" 2>&1 || true
|
||
echo ""
|
||
read -p " 你的 QQ 号(设白名单,留空跳过): " QQ_ALLOW
|
||
if [ -n "$QQ_ALLOW" ]; then
|
||
run_oc config set channels.qqbot.allowFrom "\"${QQ_ALLOW}\"" 2>&1 || true
|
||
echo -e " ${GREEN}白名单已设置${NC}"
|
||
fi
|
||
echo ""
|
||
echo -e " ${GREEN}QQ 机器人配置完成!重启网关后生效。${NC}"
|
||
}
|
||
|
||
# [4] Other platforms
|
||
do_platforms() {
|
||
echo ""
|
||
echo -e " ${CYAN}${BOLD}━━━ 其他聊天平台 ━━━${NC}"
|
||
echo ""
|
||
echo -e " ${GREEN}[a]${NC} 飞书 Feishu — 企业首选"
|
||
echo -e " ${GREEN}[b]${NC} Telegram — 海外推荐"
|
||
echo -e " ${GREEN}[c]${NC} 微信(社区插件) — iPad协议"
|
||
echo -e " ${GREEN}[d]${NC} Discord"
|
||
echo ""
|
||
read -p " 选择 (a-d): " -n 1 CH
|
||
echo ""
|
||
echo ""
|
||
|
||
case $CH in
|
||
a) echo " 飞书: 访问 open.feishu.cn/app 创建应用" ;;
|
||
b) echo " Telegram: 找 @BotFather 创建机器人" ;;
|
||
c)
|
||
echo -e " ${YELLOW}安装微信插件...${NC}"
|
||
run_oc plugins install @icesword760/openclaw-wechat 2>&1 || true
|
||
;;
|
||
d) echo " Discord: 访问 discord.com/developers/applications" ;;
|
||
*) echo " 无效选择" ;;
|
||
esac
|
||
echo ""
|
||
echo " 配置完成后运行配置向导 [1] 绑定平台"
|
||
}
|
||
|
||
# [5] Doctor
|
||
do_doctor() {
|
||
echo ""
|
||
echo -e " ${CYAN}━━━ 诊断修复 ━━━${NC}"
|
||
echo ""
|
||
run_oc doctor --repair 2>&1 || echo -e " ${YELLOW}诊断命令执行失败${NC}"
|
||
}
|
||
|
||
# [6] Backup
|
||
do_backup() {
|
||
echo ""
|
||
local TS=$(date +%Y%m%d_%H%M%S)
|
||
local BK="$DATA_DIR/backups/backup_$TS"
|
||
mkdir -p "$BK"
|
||
|
||
[ -f "$CONFIG_PATH" ] && cp "$CONFIG_PATH" "$BK/" && echo -e " ${GREEN} + openclaw.json${NC}"
|
||
[ -d "$DATA_DIR/memory" ] && cp -R "$DATA_DIR/memory" "$BK/" 2>/dev/null && echo -e " ${GREEN} + memory/${NC}"
|
||
|
||
echo ""
|
||
echo -e " ${GREEN}备份完成: $BK${NC}"
|
||
echo " 大小: $(du -sh "$BK" | cut -f1)"
|
||
}
|
||
|
||
# [7] Restore
|
||
do_restore() {
|
||
echo ""
|
||
local BK_DIR="$DATA_DIR/backups"
|
||
if [ ! -d "$BK_DIR" ] || [ -z "$(ls -A "$BK_DIR" 2>/dev/null)" ]; then
|
||
echo -e " ${YELLOW}没有备份${NC}"
|
||
return
|
||
fi
|
||
|
||
echo " 可用备份:"
|
||
local i=1
|
||
for b in "$BK_DIR"/*/; do
|
||
echo -e " ${GREEN}[$i]${NC} $(basename "$b") ($(du -sh "$b" | cut -f1))"
|
||
i=$((i+1))
|
||
done
|
||
echo ""
|
||
read -p " 选择编号: " NUM
|
||
|
||
local j=1
|
||
for b in "$BK_DIR"/*/; do
|
||
if [ "$j" = "$NUM" ]; then
|
||
[ -f "$b/openclaw.json" ] && cp "$b/openclaw.json" "$CONFIG_PATH" && echo -e " ${GREEN} + 配置已恢复${NC}"
|
||
[ -d "$b/memory" ] && cp -R "$b/memory" "$DATA_DIR/" && echo -e " ${GREEN} + 记忆已恢复${NC}"
|
||
echo -e " ${GREEN}恢复完成${NC}"
|
||
return
|
||
fi
|
||
j=$((j+1))
|
||
done
|
||
echo -e " ${RED}无效选择${NC}"
|
||
}
|
||
|
||
# [8] System info
|
||
do_sysinfo() {
|
||
echo ""
|
||
echo " 系统: $(sw_vers -productName 2>/dev/null) $(sw_vers -productVersion 2>/dev/null)"
|
||
echo " CPU: $(uname -m)"
|
||
echo " 内存: $(sysctl -n hw.memsize 2>/dev/null | awk '{printf "%.0f GB", $1/1024/1024/1024}')"
|
||
echo " Node: $("$NODE_BIN" --version 2>/dev/null)"
|
||
echo " 路径: $UCLAW_DIR"
|
||
echo " 大小: $(du -sh "$UCLAW_DIR" 2>/dev/null | cut -f1)"
|
||
echo " 磁盘: $(df -h "$UCLAW_DIR" | tail -1 | awk '{print $4 " 可用"}')"
|
||
}
|
||
|
||
# Main loop
|
||
while true; do
|
||
show_menu
|
||
read -p " 请选择 [0-15]: " CHOICE
|
||
echo ""
|
||
|
||
case $CHOICE in
|
||
1) do_config ;;
|
||
2) do_dashboard ;;
|
||
3) do_qq ;;
|
||
4) do_platforms ;;
|
||
5) do_doctor ;;
|
||
6) do_backup ;;
|
||
7) do_restore ;;
|
||
8) do_sysinfo ;;
|
||
9) do_kill_gateway ;;
|
||
10) do_logs ;;
|
||
11) do_factory_reset ;;
|
||
12) do_uninstall ;;
|
||
13) do_update ;;
|
||
14) do_cleanup ;;
|
||
15) do_plugins ;;
|
||
0) echo -e " ${CYAN}再见!${NC}"; exit 0 ;;
|
||
*) echo -e " ${RED}无效选择${NC}" ;;
|
||
esac
|
||
|
||
echo ""
|
||
read -p " 按回车返回..."
|
||
done
|