diff --git a/CLAUDE.md b/CLAUDE.md index fbb4af1..f499c6b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -89,5 +89,6 @@ Release artifacts go to GitHub Releases, not the repo. - Mac Apple Silicon (ARM64): ✅ Working - Windows x64: 🚧 In development +- Linux x86_64: ✅ Working (scripts + live USB mode) +- Linux ARM64: ✅ Working (scripts) - Mac Intel (x64): ❌ Not yet -- Linux: ❌ Not yet diff --git a/README.md b/README.md index 91ebde0..33fc25d 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,107 @@ # 🦞 U-Claw -**AI 助手 U 盘 — 克隆、补依赖、拷到 U 盘、双击运行** +> **制作「插上就能用」的 AI 助手 U 盘 — 教程与源代码** +> **Build a plug-and-play AI assistant USB drive — Tutorial & Source Code** [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -U-Claw 把 [OpenClaw](https://github.com/openclaw/openclaw) 打包成 U 盘便携版。这个代码库就是 U 盘里的全部内容,只是不上传大依赖(Node.js、node_modules)。运行 `setup.sh` 补齐依赖后,直接拷贝到 U 盘就能用。 +[中文](#中文) | [English](#english) --- -## 使用方式 + + +## 中文 + +### 这是什么 + +这个仓库是一个**制作教程 + 全套源代码**,教你把 [OpenClaw](https://github.com/openclaw/openclaw)(开源 AI 助手框架)做成 U 盘——插上任意电脑,双击就能用 AI。 + +代码库本身就是 U 盘的文件骨架,运行 `setup.sh` 补齐大依赖后,整个 `portable/` 目录直接拷贝到 U 盘即可。 + +### 两种 U 盘模式 + +| | 便携版 U 盘 | Linux 启动盘 | +|---|---|---| +| **原理** | 在已有系统(Mac/Win/Linux)上运行 U 盘里的脚本 | 从 U 盘启动整个 Linux 系统 + OpenClaw | +| **U 盘要求** | 4GB+ | **64GB+ USB 3.0**(USB 2.0 太慢不推荐) | +| **依赖** | 电脑已有操作系统 | 不依赖任何已装系统 | +| **适合** | 临时用、公共电脑、演示 | 专用 AI 工作站、无系统裸机 | + +### 快速开始:制作便携版 U 盘 ```bash +# 1. 克隆代码 git clone https://github.com/dongsheng123132/u-claw.git -cd u-claw/portable -bash setup.sh # 补齐大依赖(Node.js + OpenClaw,国内镜像,约 1 分钟) + +# 2. 补齐大依赖(Node.js + OpenClaw,国内镜像,约 1 分钟) +cd u-claw/portable && bash setup.sh + +# 3. 拷贝到 U 盘 +cp -R portable/ /Volumes/你的U盘/U-Claw/ # Mac +# 或 Windows 资源管理器直接拖过去 ``` -**完成后 `portable/` 就是完整的 U 盘内容。** 拷贝到 U 盘即可: +**完成!** 插上 U 盘,双击启动脚本就能用。 +### 制作 Linux 启动盘 + +把整个 Linux 系统 + OpenClaw 刻在 U 盘,开机从 USB 启动直接进入 AI 助手桌面。 + +**准备:** +- **64GB+ USB 3.0 U 盘**(推荐三星 BAR Plus、闪迪 CZ880 等高速盘) +- Linux Mint ISO([清华镜像](https://mirrors.tuna.tsinghua.edu.cn/linuxmint-cd/stable/) / [中科大镜像](https://mirrors.ustc.edu.cn/linuxmint-cd/stable/)) +- Rufus v4.0+([rufus.ie](https://rufus.ie/zh/)) + +**步骤:** +1. Rufus 刻录 ISO → 开启 **Persistent Storage**(8-16GB) +2. USB 启动 → 进入 Linux Mint 桌面 → 连网 +3. 运行安装脚本: ```bash -# Mac -cp -R portable/ /Volumes/你的U盘/U-Claw/ - -# 或直接在 Finder 拖过去 +curl -O https://raw.githubusercontent.com/dongsheng123132/u-claw/main/portable/setup-linux-usb.sh +bash setup-linux-usb.sh ``` -## U 盘上能做什么 +详细图文教程见 [guide.html → Linux 启动盘模式](https://u-claw.org/guide.html)。 -| 功能 | Mac | Windows | 说明 | -|------|-----|---------|------| -| **免安装运行** | `Mac-Start.command` | `Windows-Start.bat` | 双击直接跑,不碰电脑 | -| **功能菜单** | `Mac-Menu.command` | `Windows-Menu.bat` | 配置、QQ、备份等 8 项 | -| **安装到电脑** | `Mac-Install.command` | `Windows-Install.bat` | 离线优先,缺啥走国内镜像 | -| **首次配置** | `Config.html` | `Config.html` | 浏览器打开,选模型填 Key | +### U 盘功能一览 -## U 盘文件结构 +| 功能 | Mac | Windows | Linux | +|------|-----|---------|-------| +| **免安装运行** | `Mac-Start.command` | `Windows-Start.bat` | `bash Linux-Start.sh` | +| **功能菜单** | `Mac-Menu.command` | `Windows-Menu.bat` | `bash Linux-Menu.sh` | +| **安装到电脑** | `Mac-Install.command` | `Windows-Install.bat` | `bash Linux-Install.sh` | +| **首次配置** | `Config.html` | `Config.html` | `Config.html` | + +### U 盘文件结构 ``` U-Claw/ ← 整个拷到 U 盘 -├── Mac-Start.command 双击 = Mac 免安装运行 -├── Mac-Menu.command 功能菜单(8 项) +├── Mac-Start.command Mac 免安装运行 +├── Mac-Menu.command Mac 功能菜单 ├── Mac-Install.command 安装到 Mac -├── Windows-Start.bat 双击 = Windows 免安装运行 -├── Windows-Menu.bat 功能菜单 +├── Windows-Start.bat Windows 免安装运行 +├── Windows-Menu.bat Windows 功能菜单 ├── Windows-Install.bat 安装到 Windows +├── Linux-Start.sh Linux 免安装运行 +├── Linux-Menu.sh Linux 功能菜单 +├── Linux-Install.sh 安装到 Linux +├── setup-linux-usb.sh Linux 启动盘内安装 OpenClaw ├── Config.html 首次配置页面 -├── setup.sh 补齐依赖用(开发者用) +├── setup.sh 补齐依赖(开发者用) ├── app/ ← 大依赖(setup.sh 下载,不进 git) │ ├── core/ OpenClaw + QQ 插件 │ └── runtime/ │ ├── node-mac-arm64/ Mac Apple Silicon -│ └── node-win-x64/ Windows 64-bit +│ ├── node-win-x64/ Windows 64-bit +│ └── node-linux-x64/ Linux x86_64 └── data/ ← 用户数据(不进 git) ├── .openclaw/ 配置文件 ├── memory/ AI 记忆 └── backups/ 备份 ``` -**代码库 = U 盘内容 - 大依赖。** `app/` 和 `data/` 在 `.gitignore` 里。 - -## 桌面安装版(Electron App) +### 桌面安装版(Electron App) 除了 U 盘便携版,还有桌面 App 版本: @@ -71,9 +113,9 @@ npm run build:mac-arm64 # 打包 → release/*.dmg npm run build:win # 打包 → release/*.exe ``` -## 支持的 AI 模型 +### 支持的 AI 模型 -### 国产模型(无需翻墙) +**国产模型(无需翻墙):** | 模型 | 推荐场景 | |------|----------| @@ -84,11 +126,9 @@ npm run build:win # 打包 → release/*.exe | MiniMax | 语音多模态 | | 豆包 Doubao | 火山引擎 | -### 国际模型 +**国际模型:** Claude · GPT · Gemini(需翻墙或中转) -Claude · GPT · Gemini(需翻墙或中转) - -## 支持的聊天平台 +### 支持的聊天平台 | 平台 | 状态 | 说明 | |------|------|------| @@ -99,7 +139,7 @@ Claude · GPT · Gemini(需翻墙或中转) | Discord | ✅ 内置 | — | | 微信 | ✅ 社区插件 | iPad 协议 | -## 国内镜像 +### 国内镜像 所有脚本默认走国内镜像,无需翻墙: @@ -109,60 +149,42 @@ Claude · GPT · Gemini(需翻墙或中转) | Node.js | `npmmirror.com/mirrors/node` | | Electron | `npmmirror.com/mirrors/electron` | -## 开发 & 测试 +### 开发 & 贡献 ```bash -# 克隆 git clone https://github.com/dongsheng123132/u-claw.git - -# 补依赖 cd u-claw/portable && bash setup.sh - -# 测试启动 -bash Mac-Start.command # Mac -# 或 Windows-Start.bat # Windows - -# 测试没问题 → 拷到 U 盘 -# 有问题 → 修代码 → 重新测试 +bash Mac-Start.command # Mac 测试 +bash Linux-Start.sh # Linux 测试 ``` -### 提交代码 +**平台支持:** -```bash -git checkout -b feat/your-feature -git add -A -git commit -m "feat: your change" -git push -u origin feat/your-feature -``` +| 平台 | 状态 | +|------|------| +| Mac Apple Silicon (M1-M4) | ✅ | +| Linux x86_64 | ✅ | +| Linux ARM64 | ✅ | +| Windows x64 | 🚧 开发中 | +| Mac Intel | ❌ 暂不支持 | -## 待开发 / 欢迎贡献 +欢迎 PR!特别需要:Windows 脚本完善、新平台支持、教程翻译。 -- [ ] Windows 便携版完善测试 -- [ ] Mac Intel 支持 -- [ ] Linux 支持(AppImage) -- [ ] 桌面版自动更新 -- [ ] SkillHub 技能市场功能完善 -- [ ] 多语言支持(English UI) - -## FAQ +### FAQ **Q: 需要翻墙吗?** -安装不需要。运行需要联网调 API,国产模型无需翻墙。 +不需要。安装和运行全程使用国内镜像,国产模型 API 直连。 **Q: U 盘需要多大?** -建议 4GB 以上(完整版约 2.3GB)。 +便携版 4GB+(完整约 2.3GB)。Linux 启动盘 64GB+ USB 3.0。 **Q: 能分发吗?** -MIT 协议,随便复制。 +MIT 协议,随便复制分发。 **Q: Mac 提示"未验证的开发者"?** 右键脚本 → 打开。 -## License - -[MIT](LICENSE) - -## 联系 +### 联系 - 微信: hecare888 - GitHub: [@dongsheng123132](https://github.com/dongsheng123132) @@ -170,4 +192,185 @@ MIT 协议,随便复制。 --- + + +## English + +### What is this + +This repo is a **tutorial + complete source code** for building an [OpenClaw](https://github.com/openclaw/openclaw) (open-source AI assistant framework) USB drive — plug it into any computer, double-click, and start using AI. + +The codebase itself is the USB file skeleton. Run `setup.sh` to download large dependencies, then copy the entire `portable/` directory to a USB drive. + +### Two USB modes + +| | Portable USB | Linux Live USB | +|---|---|---| +| **How it works** | Runs scripts from USB on existing OS (Mac/Win/Linux) | Boots entire Linux system + OpenClaw from USB | +| **USB requirement** | 4GB+ | **64GB+ USB 3.0** (USB 2.0 too slow) | +| **Depends on** | Existing OS on computer | Nothing — boots independently | +| **Best for** | Temporary use, public computers, demos | Dedicated AI workstation, bare metal | + +### Quick Start: Build a Portable USB + +```bash +# 1. Clone +git clone https://github.com/dongsheng123132/u-claw.git + +# 2. Download dependencies (Node.js + OpenClaw, ~1 min) +cd u-claw/portable && bash setup.sh + +# 3. Copy to USB drive +cp -R portable/ /Volumes/YOUR_USB/U-Claw/ # Mac +# Or drag & drop on Windows/Linux +``` + +**Done!** Plug in the USB, double-click the start script, and you're running AI. + +### Build a Linux Live USB + +Boot an entire Linux system + OpenClaw directly from USB — no existing OS required. + +**You need:** +- **64GB+ USB 3.0 drive** (recommended: Samsung BAR Plus, SanDisk Extreme) +- Linux Mint ISO ([linuxmint.com](https://linuxmint.com/download.php)) +- Rufus v4.0+ ([rufus.ie](https://rufus.ie/en/)) + +**Steps:** +1. Flash ISO with Rufus → enable **Persistent Storage** (8-16GB) +2. Boot from USB → enter Linux Mint desktop → connect to internet +3. Run the install script: +```bash +curl -O https://raw.githubusercontent.com/dongsheng123132/u-claw/main/portable/setup-linux-usb.sh +bash setup-linux-usb.sh +``` + +Full guide at [guide.html → Linux Live USB](https://u-claw.org/guide.html). + +### USB Features + +| Feature | Mac | Windows | Linux | +|---------|-----|---------|-------| +| **Run (no install)** | `Mac-Start.command` | `Windows-Start.bat` | `bash Linux-Start.sh` | +| **Menu** | `Mac-Menu.command` | `Windows-Menu.bat` | `bash Linux-Menu.sh` | +| **Install to PC** | `Mac-Install.command` | `Windows-Install.bat` | `bash Linux-Install.sh` | +| **First-time config** | `Config.html` | `Config.html` | `Config.html` | + +### File Structure + +``` +U-Claw/ ← Copy entire folder to USB +├── Mac-Start.command Mac launcher +├── Mac-Menu.command Mac menu (8 options) +├── Mac-Install.command Install to Mac +├── Windows-Start.bat Windows launcher +├── Windows-Menu.bat Windows menu +├── Windows-Install.bat Install to Windows +├── Linux-Start.sh Linux launcher +├── Linux-Menu.sh Linux menu +├── Linux-Install.sh Install to Linux +├── setup-linux-usb.sh Install OpenClaw in Linux live USB +├── Config.html First-time config page +├── setup.sh Download dependencies (dev use) +├── app/ ← Large deps (downloaded by setup.sh, not in git) +│ ├── core/ OpenClaw + QQ plugin +│ └── runtime/ +│ ├── node-mac-arm64/ Mac Apple Silicon +│ ├── node-win-x64/ Windows 64-bit +│ └── node-linux-x64/ Linux x86_64 +└── data/ ← User data (not in git) + ├── .openclaw/ Config file + ├── memory/ AI memory + └── backups/ Backups +``` + +### Desktop App (Electron) + +```bash +cd u-claw-app +bash setup.sh # One-click dev setup (China mirrors) +npm run dev # Dev mode +npm run build:mac-arm64 # Build → release/*.dmg +npm run build:win # Build → release/*.exe +``` + +### Supported AI Models + +**Chinese models (no VPN needed):** + +| Model | Best for | +|-------|----------| +| DeepSeek | Coding, extremely cheap | +| Kimi K2.5 | Long documents, 256K context | +| Qwen | Large free tier | +| GLM (Zhipu) | Academic use | +| MiniMax | Voice & multimodal | +| Doubao | Volcengine ecosystem | + +**International models:** Claude · GPT · Gemini (VPN or relay required in China) + +### Supported Chat Platforms + +| Platform | Status | Notes | +|----------|--------|-------| +| QQ | ✅ Pre-installed | Enter AppID + Secret | +| Feishu (Lark) | ✅ Built-in | Enterprise favorite | +| Telegram | ✅ Built-in | International | +| WhatsApp | ✅ Built-in | Baileys protocol | +| Discord | ✅ Built-in | — | +| WeChat | ✅ Community plugin | iPad protocol | + +### China Mirrors + +All scripts use China mirrors by default — no VPN needed: + +| Resource | Mirror | +|----------|--------| +| npm packages | `registry.npmmirror.com` | +| Node.js | `npmmirror.com/mirrors/node` | +| Electron | `npmmirror.com/mirrors/electron` | + +### Development & Contributing + +```bash +git clone https://github.com/dongsheng123132/u-claw.git +cd u-claw/portable && bash setup.sh +bash Mac-Start.command # Test on Mac +bash Linux-Start.sh # Test on Linux +``` + +**Platform Support:** + +| Platform | Status | +|----------|--------| +| Mac Apple Silicon (M1-M4) | ✅ | +| Linux x86_64 | ✅ | +| Linux ARM64 | ✅ | +| Windows x64 | 🚧 In progress | +| Mac Intel | ❌ Not yet | + +PRs welcome! Especially: Windows scripts, new platform support, documentation. + +### FAQ + +**Q: Do I need a VPN?** +No. All downloads use China mirrors. Chinese AI model APIs work directly. + +**Q: How big should the USB drive be?** +Portable mode: 4GB+ (~2.3GB full). Linux live USB: 64GB+ USB 3.0. + +**Q: Can I redistribute?** +MIT license — copy and share freely. + +**Q: Mac says "unverified developer"?** +Right-click the script → Open. + +### Contact + +- WeChat: hecare888 +- GitHub: [@dongsheng123132](https://github.com/dongsheng123132) +- Website: [u-claw.org](https://u-claw.org) + +--- + **Made with 🦞 by [dongsheng](https://github.com/dongsheng123132)** diff --git a/portable/Linux-Install.sh b/portable/Linux-Install.sh new file mode 100755 index 0000000..521b66b --- /dev/null +++ b/portable/Linux-Install.sh @@ -0,0 +1,273 @@ +#!/bin/bash +# ============================================================ +# U-Claw - Install to Linux (从 U 盘安装到电脑) +# 优先使用 U 盘内的离线资源,缺失时从国内镜像下载 +# ============================================================ + +set -e + +UCLAW_DIR="$(cd "$(dirname "$0")" && pwd)" +APP_DIR="$UCLAW_DIR/app" +INSTALL_TARGET="$HOME/.uclaw" + +GREEN='\033[0;32m' +CYAN='\033[0;36m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +NC='\033[0m' +BOLD='\033[1m' +DIM='\033[2m' + +NODE_VER="v22.14.0" +MIRROR="https://registry.npmmirror.com" +NODE_MIRROR="https://npmmirror.com/mirrors/node" + +clear +echo "" +echo -e "${CYAN}${BOLD}" +echo " ╔══════════════════════════════════════╗" +echo " ║ U-Claw 安装到 Linux ║" +echo " ║ 从 U 盘离线安装 ║" +echo " ╚══════════════════════════════════════╝" +echo -e "${NC}" +echo "" + +# ---- Check CPU ---- +ARCH=$(uname -m) +if [ "$ARCH" = "x86_64" ]; then + echo -e " ${GREEN}Linux x86_64 ✓${NC}" + NODE_PLATFORM="node-linux-x64" +elif [ "$ARCH" = "aarch64" ]; then + echo -e " ${GREEN}Linux ARM64 ✓${NC}" + NODE_PLATFORM="node-linux-arm64" +else + echo -e " ${RED}Unsupported architecture: $ARCH${NC}" + exit 1 +fi +echo "" + +# ---- Check existing installation ---- +if [ -d "$INSTALL_TARGET" ]; then + echo -e " ${YELLOW}检测到已有安装: $INSTALL_TARGET${NC}" + read -p " 覆盖安装?(y/n): " -n 1 OVERWRITE + echo "" + if [ "$OVERWRITE" != "y" ] && [ "$OVERWRITE" != "Y" ]; then + echo -e " ${DIM}已取消${NC}" + exit 0 + fi + echo "" +fi + +# ---- Step 1: Check environment ---- +echo -e " ${BOLD}[1/4] 检查环境...${NC}" + +NEED_DOWNLOAD_NODE=false +NEED_DOWNLOAD_OPENCLAW=false + +# Check Node.js - prefer USB, then system, then download +USB_NODE="$APP_DIR/runtime/$NODE_PLATFORM/bin/node" +USB_NPM="$APP_DIR/runtime/$NODE_PLATFORM/bin/npm" + +if [ -f "$USB_NODE" ]; then + echo -e " ${GREEN}Node.js: 使用 U 盘内的 ($("$USB_NODE" --version))${NC}" + USE_NODE="usb" +elif command -v node >/dev/null 2>&1; then + SYS_VER=$(node --version) + MAJOR=$(echo "$SYS_VER" | sed 's/v//' | cut -d. -f1) + if [ "$MAJOR" -ge 20 ] 2>/dev/null; then + echo -e " ${GREEN}Node.js: 使用系统的 ($SYS_VER)${NC}" + USE_NODE="system" + else + echo -e " ${YELLOW}Node.js: 系统版本太低 ($SYS_VER),需要 v20+${NC}" + NEED_DOWNLOAD_NODE=true + USE_NODE="download" + fi +else + echo -e " ${YELLOW}Node.js: 未安装${NC}" + NEED_DOWNLOAD_NODE=true + USE_NODE="download" +fi + +# Check OpenClaw +USB_OPENCLAW="$APP_DIR/core/node_modules/openclaw/openclaw.mjs" +if [ -f "$USB_OPENCLAW" ]; then + echo -e " ${GREEN}OpenClaw: 使用 U 盘内的${NC}" + USE_OPENCLAW="usb" +else + echo -e " ${YELLOW}OpenClaw: U 盘内未找到,需要在线下载${NC}" + NEED_DOWNLOAD_OPENCLAW=true + USE_OPENCLAW="download" +fi + +echo "" + +# ---- Step 2: Create install directory ---- +echo -e " ${BOLD}[2/4] 安装到 $INSTALL_TARGET ...${NC}" + +mkdir -p "$INSTALL_TARGET" +mkdir -p "$INSTALL_TARGET/data/.openclaw" +mkdir -p "$INSTALL_TARGET/data/memory" +mkdir -p "$INSTALL_TARGET/data/backups" + +# ---- Step 3: Copy/Download Node.js ---- +echo -e " ${BOLD}[3/4] 安装 Node.js...${NC}" + +NODE_INSTALL_DIR="$INSTALL_TARGET/runtime/$NODE_PLATFORM" + +case $USE_NODE in + usb) + echo -e " ${CYAN}从 U 盘复制 Node.js...${NC}" + mkdir -p "$NODE_INSTALL_DIR" + cp -R "$APP_DIR/runtime/$NODE_PLATFORM/"* "$NODE_INSTALL_DIR/" + chmod +x "$NODE_INSTALL_DIR/bin/node" + INSTALL_NODE="$NODE_INSTALL_DIR/bin/node" + INSTALL_NPM="$NODE_INSTALL_DIR/bin/npm" + echo -e " ${GREEN}Node.js 安装完成 ✓${NC}" + ;; + system) + INSTALL_NODE="$(which node)" + INSTALL_NPM="$(which npm)" + echo -e " ${GREEN}使用系统 Node.js ✓${NC}" + ;; + download) + echo -e " ${CYAN}从国内镜像下载 Node.js $NODE_VER...${NC}" + PLATFORM_NAME="linux-$( [ "$ARCH" = "x86_64" ] && echo "x64" || echo "arm64" )" + TARBALL="node-${NODE_VER}-${PLATFORM_NAME}.tar.gz" + URL="${NODE_MIRROR}/${NODE_VER}/${TARBALL}" + + mkdir -p "$NODE_INSTALL_DIR" + curl -# -L "$URL" -o "/tmp/$TARBALL" + tar -xzf "/tmp/$TARBALL" -C "$NODE_INSTALL_DIR" --strip-components=1 + rm -f "/tmp/$TARBALL" + chmod +x "$NODE_INSTALL_DIR/bin/node" + INSTALL_NODE="$NODE_INSTALL_DIR/bin/node" + INSTALL_NPM="$NODE_INSTALL_DIR/bin/npm" + echo -e " ${GREEN}Node.js 下载安装完成 ✓${NC}" + ;; +esac + +echo "" + +# ---- Step 4: Copy/Download OpenClaw ---- +echo -e " ${BOLD}[4/4] 安装 OpenClaw...${NC}" + +CORE_INSTALL_DIR="$INSTALL_TARGET/core" + +case $USE_OPENCLAW in + usb) + echo -e " ${CYAN}从 U 盘复制 OpenClaw + 插件...${NC}" + mkdir -p "$CORE_INSTALL_DIR" + cp -R "$APP_DIR/core/"* "$CORE_INSTALL_DIR/" + echo -e " ${GREEN}OpenClaw 安装完成 ✓${NC}" + ;; + download) + echo -e " ${CYAN}从国内镜像下载 OpenClaw...${NC}" + mkdir -p "$CORE_INSTALL_DIR" + cat > "$CORE_INSTALL_DIR/package.json" << 'PKGEOF' +{ + "name": "u-claw-core", + "version": "1.0.0", + "private": true, + "dependencies": { + "openclaw": "latest" + } +} +PKGEOF + cd "$CORE_INSTALL_DIR" + "$INSTALL_NODE" "$INSTALL_NPM" install --registry="$MIRROR" 2>&1 | tail -3 + "$INSTALL_NODE" "$INSTALL_NPM" install @sliverp/qqbot@latest --registry="$MIRROR" 2>&1 | tail -2 + echo -e " ${GREEN}OpenClaw 下载安装完成 ✓${NC}" + ;; +esac + +# ---- Default config ---- +CONFIG_PATH="$INSTALL_TARGET/data/.openclaw/openclaw.json" +if [ ! -f "$CONFIG_PATH" ]; then + cat > "$CONFIG_PATH" << 'CFGEOF' +{ + "gateway": { + "mode": "local", + "auth": { "token": "uclaw" } + } +} +CFGEOF +fi + +# ---- Copy HTML pages ---- +for f in Config.html U-Claw.html; do + [ -f "$UCLAW_DIR/$f" ] && cp "$UCLAW_DIR/$f" "$INSTALL_TARGET/" +done + +# ---- Create launch script ---- +cat > "$INSTALL_TARGET/start.sh" << 'STARTEOF' +#!/bin/bash +DIR="$(cd "$(dirname "$0")" && pwd)" +ARCH=$(uname -m) +NODE_PLATFORM="node-linux-$( [ "$ARCH" = "x86_64" ] && echo "x64" || echo "arm64" )" + +NODE_BIN="$DIR/runtime/$NODE_PLATFORM/bin/node" +[ ! -f "$NODE_BIN" ] && NODE_BIN="$(which node)" + +CORE_DIR="$DIR/core" +OPENCLAW_MJS="$CORE_DIR/node_modules/openclaw/openclaw.mjs" + +export OPENCLAW_HOME="$DIR/data" +export OPENCLAW_STATE_DIR="$DIR/data/.openclaw" +export OPENCLAW_CONFIG_PATH="$DIR/data/.openclaw/openclaw.json" + +PORT=18789 +while ss -tlnp 2>/dev/null | grep -q ":$PORT " || netstat -tlnp 2>/dev/null | grep -q ":$PORT "; do + PORT=$((PORT + 1)) + [ $PORT -gt 18799 ] && echo "No available port" && exit 1 +done + +cd "$CORE_DIR" +"$NODE_BIN" "$OPENCLAW_MJS" gateway run --allow-unconfigured --force --port $PORT & +PID=$! + +for i in $(seq 1 30); do + sleep 0.5 + if curl -s -o /dev/null "http://127.0.0.1:$PORT/" 2>/dev/null; then + xdg-open "http://127.0.0.1:$PORT/#token=uclaw" 2>/dev/null || echo "Open: http://127.0.0.1:$PORT/#token=uclaw" + break + fi +done + +wait $PID +STARTEOF +chmod +x "$INSTALL_TARGET/start.sh" + +# ---- Create desktop shortcut ---- +DESKTOP_FILE="$HOME/.local/share/applications/u-claw.desktop" +mkdir -p "$HOME/.local/share/applications" +cat > "$DESKTOP_FILE" << DESKEOF +[Desktop Entry] +Name=U-Claw +Comment=Portable AI Agent +Exec=bash $INSTALL_TARGET/start.sh +Terminal=true +Type=Application +Categories=Utility; +DESKEOF + +echo "" + +# ---- Summary ---- +INSTALL_SIZE=$(du -sh "$INSTALL_TARGET" | cut -f1) + +echo -e " ${GREEN}${BOLD}╔══════════════════════════════════════╗" +echo -e " ║ ✅ 安装成功! ║" +echo -e " ╚══════════════════════════════════════╝${NC}" +echo "" +echo -e " ${BOLD}安装位置:${NC} $INSTALL_TARGET" +echo -e " ${BOLD}大小:${NC} $INSTALL_SIZE" +echo "" +echo -e " ${BOLD}启动方式:${NC}" +echo -e " 终端运行: ${CYAN}bash ~/.uclaw/start.sh${NC}" +echo -e " 或在应用菜单搜索 ${CYAN}U-Claw${NC}" +echo "" +echo -e " ${BOLD}首次使用:${NC}" +echo -e " 启动后浏览器自动打开配置页面" +echo -e " 选择 AI 模型 → 填写 API Key → 开始用" +echo "" +read -p " 按回车关闭..." diff --git a/portable/Linux-Menu.sh b/portable/Linux-Menu.sh new file mode 100755 index 0000000..63d24e8 --- /dev/null +++ b/portable/Linux-Menu.sh @@ -0,0 +1,300 @@ +#!/bin/bash +# U-Claw Menu - Portable AI Agent +# Linux version + +UCLAW_DIR="$(cd "$(dirname "$0")" && pwd)" +APP_DIR="$UCLAW_DIR/app" +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 +ARCH=$(uname -m) +if [ "$ARCH" = "x86_64" ]; then + NODE_DIR="$APP_DIR/runtime/node-linux-x64" +elif [ "$ARCH" = "aarch64" ]; then + NODE_DIR="$APP_DIR/runtime/node-linux-arm64" +else + echo -e "${RED}Unsupported architecture: $ARCH${NC}" + exit 1 +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" + +# 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 (Linux) ║" + 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 ss -tlnp 2>/dev/null | grep -q ":$PORT " || netstat -tlnp 2>/dev/null | grep -q ":$PORT "; 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}" + xdg-open "$URL" 2>/dev/null || echo -e " ${YELLOW}请在浏览器打开: $URL${NC}" + 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 " 系统: $(uname -s) $(uname -r)" + echo " 发行版: $(cat /etc/os-release 2>/dev/null | grep ^PRETTY_NAME | cut -d= -f2 | tr -d '"' || echo 'Unknown')" + echo " CPU: $(uname -m) $(lscpu 2>/dev/null | grep 'Model name' | sed 's/.*:\s*//' || echo '')" + echo " 内存: $(free -h 2>/dev/null | awk '/^Mem:/{print $2}' || echo 'N/A')" + 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 diff --git a/portable/Linux-Start.sh b/portable/Linux-Start.sh new file mode 100755 index 0000000..c5175a1 --- /dev/null +++ b/portable/Linux-Start.sh @@ -0,0 +1,185 @@ +#!/bin/bash +# ============================================================ +# U-Claw - Portable AI Agent +# Linux version - run: bash Linux-Start.sh +# ============================================================ + +UCLAW_DIR="$(cd "$(dirname "$0")" && pwd)" +APP_DIR="$UCLAW_DIR/app" +CORE_DIR="$APP_DIR/core" +DATA_DIR="$UCLAW_DIR/data" +SYSTEM_DIR="$UCLAW_DIR/system" + +GREEN='\033[0;32m' +CYAN='\033[0;36m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +NC='\033[0m' + +clear +echo "" +echo -e "${CYAN}" +echo " ╔══════════════════════════════════════╗" +echo " ║ 🦞 U-Claw v1.1 ║" +echo " ║ Portable AI Agent (Linux) ║" +echo " ╚══════════════════════════════════════╝" +echo -e "${NC}" + +# ---- 1. Detect CPU & set runtime ---- +ARCH=$(uname -m) +if [ "$ARCH" = "x86_64" ]; then + NODE_DIR="$APP_DIR/runtime/node-linux-x64" + echo -e " ${GREEN}Linux x86_64${NC}" +elif [ "$ARCH" = "aarch64" ]; then + NODE_DIR="$APP_DIR/runtime/node-linux-arm64" + echo -e " ${GREEN}Linux ARM64${NC}" +else + echo -e " ${RED}Unsupported architecture: $ARCH${NC}" + echo -e " ${RED}Only x86_64 and arm64 are supported.${NC}" + echo "" + read -p " Press Enter to exit..." + exit 1 +fi + +NODE_BIN="$NODE_DIR/bin/node" +export PATH="$NODE_DIR/bin:$PATH" + +# ---- 2. Check runtime ---- +if [ ! -f "$NODE_BIN" ]; then + echo -e " ${RED}Error: Node.js runtime not found${NC}" + echo " Expected: $NODE_BIN" + echo " Please run setup.sh first or ensure app/runtime/ is complete" + read -p " Press Enter to exit..." + exit 1 +fi + +NODE_VER=$("$NODE_BIN" --version) +echo -e " Node.js: ${GREEN}${NODE_VER}${NC}" +echo "" + +# ---- 3. Check & init data ---- +mkdir -p "$DATA_DIR/memory" "$DATA_DIR/backups" "$DATA_DIR/logs" + +if [ ! -f "$DATA_DIR/config.json" ] && [ ! -f "$DATA_DIR/.openclaw/openclaw.json" ]; then + echo -e " ${YELLOW}First run - creating default config...${NC}" + mkdir -p "$DATA_DIR/.openclaw" + cat > "$DATA_DIR/.openclaw/openclaw.json" << 'CFGEOF' +{ + "gateway": { + "mode": "local", + "auth": { "token": "uclaw" } + } +} +CFGEOF + echo -e " ${GREEN}Config created${NC}" + echo "" +fi + +# ---- 4. Set environment (portable mode) ---- +STATE_DIR="$DATA_DIR/.openclaw" +mkdir -p "$STATE_DIR" + +# Sync config to where OpenClaw expects it +if [ -f "$DATA_DIR/config.json" ] && [ ! -f "$STATE_DIR/openclaw.json" ]; then + cp "$DATA_DIR/config.json" "$STATE_DIR/openclaw.json" +fi + +export OPENCLAW_HOME="$DATA_DIR" +export OPENCLAW_STATE_DIR="$STATE_DIR" +export OPENCLAW_CONFIG_PATH="$STATE_DIR/openclaw.json" + +# ---- 5. Run migration if exists ---- +if [ -f "$SYSTEM_DIR/migrate.js" ]; then + "$NODE_BIN" "$SYSTEM_DIR/migrate.js" "$DATA_DIR" 2>/dev/null || true +fi + +# ---- 6. Check dependencies ---- +if [ ! -d "$CORE_DIR/node_modules" ]; then + echo -e " ${YELLOW}First run - installing dependencies...${NC}" + echo " (Using China mirror)" + cd "$CORE_DIR" + "$NODE_BIN" "$NODE_DIR/bin/npm" install --registry=https://registry.npmmirror.com 2>&1 + echo -e " ${GREEN}Dependencies installed${NC}" + echo "" +fi + +# ---- 7. Find available port ---- +PORT=18789 +while ss -tlnp 2>/dev/null | grep -q ":$PORT " || netstat -tlnp 2>/dev/null | grep -q ":$PORT "; do + echo -e " ${YELLOW}Port $PORT in use, trying next...${NC}" + PORT=$((PORT + 1)) + if [ $PORT -gt 18799 ]; then + echo -e " ${RED}No available port (18789-18799)${NC}" + read -p " Press Enter to exit..." + exit 1 + fi +done + +# Update config with correct port if changed +if [ $PORT -ne 18789 ]; then + "$NODE_BIN" -e " + const fs = require('fs'); + const p = '$DATA_DIR/config.json'; + if (fs.existsSync(p)) { + const c = JSON.parse(fs.readFileSync(p, 'utf8')); + c.gateway = c.gateway || {}; + c.gateway.port = $PORT; + fs.writeFileSync(p, JSON.stringify(c, null, 2)); + } + " 2>/dev/null || true +fi + +# ---- 8. Start gateway ---- +echo -e " ${CYAN}Starting OpenClaw on port $PORT...${NC}" +echo " Do NOT close this window." +echo "" + +cd "$CORE_DIR" + +TOKEN=$(python3 -c "import json,os; p='$STATE_DIR/openclaw.json' if os.path.exists('$STATE_DIR/openclaw.json') else '$DATA_DIR/config.json'; d=json.load(open(p)); print(d.get('gateway',{}).get('auth',{}).get('token','uclaw'))" 2>/dev/null || echo "uclaw") + +OPENCLAW_MJS="$CORE_DIR/node_modules/openclaw/openclaw.mjs" +GW_LOG="$DATA_DIR/logs/gateway.log" +"$NODE_BIN" "$OPENCLAW_MJS" gateway run --allow-unconfigured --force --port $PORT 2>&1 | tee -a "$GW_LOG" & +GW_PID=$! + +# ---- 9. Wait & open browser ---- +for i in $(seq 1 30); do + sleep 0.5 + if curl -s -o /dev/null -w '' "http://127.0.0.1:$PORT/" 2>/dev/null; then + DASHBOARD_URL="http://127.0.0.1:$PORT/#token=${TOKEN}" + CONFIG_PAGE="$UCLAW_DIR/Config.html?port=$PORT" + echo "" + echo -e " ${GREEN}✅ Started successfully!${NC}" + echo "" + + # Check if model is configured + HAS_MODEL=$(python3 -c " +import json,os +for p in ['$STATE_DIR/openclaw.json','$DATA_DIR/config.json']: + if os.path.exists(p): + d=json.load(open(p)) + if d.get('agent',{}).get('model'): + print('yes'); break +" 2>/dev/null) + + if [ "$HAS_MODEL" = "yes" ]; then + echo -e " ${CYAN}Dashboard: ${DASHBOARD_URL}${NC}" + echo "" + xdg-open "$DASHBOARD_URL" 2>/dev/null || echo -e " ${YELLOW}Please open in browser: ${DASHBOARD_URL}${NC}" + else + echo -e " ${YELLOW}首次使用,打开配置页面...${NC}" + echo -e " ${CYAN}配置页面: Config.html${NC}" + echo -e " ${CYAN}控制台: ${DASHBOARD_URL}${NC}" + echo "" + xdg-open "$CONFIG_PAGE" 2>/dev/null || echo -e " ${YELLOW}Please open in browser: ${CONFIG_PAGE}${NC}" + fi + break + fi +done + +wait $GW_PID + +echo "" +echo -e " ${YELLOW}OpenClaw stopped.${NC}" +read -p " Press Enter to close..." diff --git a/portable/Mac-Menu.command b/portable/Mac-Menu.command index 474c77a..00dc772 100755 --- a/portable/Mac-Menu.command +++ b/portable/Mac-Menu.command @@ -27,7 +27,10 @@ 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" +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 @@ -71,6 +74,15 @@ show_menu() { 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 "" } @@ -255,7 +267,7 @@ do_sysinfo() { # Main loop while true; do show_menu - read -p " 请选择 [0-8]: " CHOICE + read -p " 请选择 [0-15]: " CHOICE echo "" case $CHOICE in @@ -267,6 +279,13 @@ while true; do 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 diff --git a/portable/Mac-Start.command b/portable/Mac-Start.command index 67ae4d3..b96a1e9 100755 --- a/portable/Mac-Start.command +++ b/portable/Mac-Start.command @@ -142,7 +142,8 @@ cd "$CORE_DIR" TOKEN=$(python3 -c "import json,os; p='$STATE_DIR/openclaw.json' if os.path.exists('$STATE_DIR/openclaw.json') else '$DATA_DIR/config.json'; d=json.load(open(p)); print(d.get('gateway',{}).get('auth',{}).get('token','uclaw'))" 2>/dev/null || echo "uclaw") OPENCLAW_MJS="$CORE_DIR/node_modules/openclaw/openclaw.mjs" -"$NODE_BIN" "$OPENCLAW_MJS" gateway run --allow-unconfigured --force --port $PORT & +GW_LOG="$DATA_DIR/logs/gateway.log" +"$NODE_BIN" "$OPENCLAW_MJS" gateway run --allow-unconfigured --force --port $PORT 2>&1 | tee -a "$GW_LOG" & GW_PID=$! # ---- 10. Wait & open browser ---- diff --git a/portable/Windows-Menu.bat b/portable/Windows-Menu.bat index cc2bbcb..3989bc9 100644 --- a/portable/Windows-Menu.bat +++ b/portable/Windows-Menu.bat @@ -19,6 +19,7 @@ set "OPENCLAW_MJS=%CORE_DIR%\node_modules\openclaw\openclaw.mjs" if not exist "%STATE_DIR%" mkdir "%STATE_DIR%" if not exist "%DATA_DIR%\memory" mkdir "%DATA_DIR%\memory" if not exist "%DATA_DIR%\backups" mkdir "%DATA_DIR%\backups" +if not exist "%DATA_DIR%\logs" mkdir "%DATA_DIR%\logs" :menu cls @@ -50,9 +51,18 @@ echo [6] Backup config echo [7] Restore backup echo [8] System info echo. +echo -- Advanced -- +echo [9] Kill residual processes +echo [10] View logs +echo [11] Factory reset +echo [12] Uninstall +echo [13] Check for updates +echo [14] Disk cleanup +echo [15] Plugin management +echo. echo [0] Exit echo. -set /p choice=" Choose [0-8]: " +set /p choice=" Choose [0-15]: " if "%choice%"=="1" goto :onboard if "%choice%"=="2" goto :dashboard @@ -62,6 +72,13 @@ if "%choice%"=="5" goto :doctor if "%choice%"=="6" goto :backup if "%choice%"=="7" goto :restore if "%choice%"=="8" goto :sysinfo +if "%choice%"=="9" goto :killproc +if "%choice%"=="10" goto :viewlogs +if "%choice%"=="11" goto :factoryreset +if "%choice%"=="12" goto :uninstall +if "%choice%"=="13" goto :checkupdate +if "%choice%"=="14" goto :diskcleanup +if "%choice%"=="15" goto :plugins if "%choice%"=="0" exit /b 0 echo Invalid choice pause @@ -179,4 +196,172 @@ echo OS: Windows echo Path: %UCLAW_DIR% echo Data: %DATA_DIR% pause +goto :menu + +:killproc +echo. +echo === Kill Residual Processes === +echo. +echo Checking ports 18789-18799... +set FOUND=0 +for /l %%p in (18789,1,18799) do ( + netstat -ano | findstr ":%%p " | findstr "LISTENING" >nul 2>&1 + if not errorlevel 1 ( + echo Port %%p: process found + set FOUND=1 + for /f "tokens=5" %%a in ('netstat -ano ^| findstr ":%%p " ^| findstr "LISTENING"') do ( + echo Killing PID %%a... + taskkill /PID %%a /F >nul 2>&1 + ) + ) +) +if "%FOUND%"=="0" echo No residual processes found. +if "%FOUND%"=="1" echo Processes killed. +pause +goto :menu + +:viewlogs +echo. +echo === View Logs === +echo. +set "LOG_FILE=%DATA_DIR%\logs\gateway.log" +if not exist "%LOG_FILE%" ( + echo No log file found. Start the gateway first. + pause + goto :menu +) +echo [a] View last 50 lines +echo [b] Open log file +echo. +set /p logchoice=" Choose (a-b): " +if "%logchoice%"=="a" ( + echo. + powershell -command "Get-Content '%LOG_FILE%' -Tail 50" +) +if "%logchoice%"=="b" ( + start notepad "%LOG_FILE%" +) +pause +goto :menu + +:factoryreset +echo. +echo === Factory Reset === +echo. +echo WARNING: This will delete all config and memory data! +echo. +echo Type RESET to confirm: +set /p resetconfirm=" > " +if not "%resetconfirm%"=="RESET" ( + echo Cancelled. + pause + goto :menu +) +echo. +echo [1/4] Backing up... +set "TS=%date:~0,4%%date:~5,2%%date:~8,2%_%time:~0,2%%time:~3,2%" +set "TS=%TS: =0%" +set "BK=%DATA_DIR%\backups\pre-reset_%TS%" +mkdir "%BK%" 2>nul +if exist "%STATE_DIR%\openclaw.json" copy "%STATE_DIR%\openclaw.json" "%BK%\" >nul 2>nul +if exist "%DATA_DIR%\memory" xcopy /s /q "%DATA_DIR%\memory" "%BK%\memory\" >nul 2>nul +echo Backup saved: %BK% +echo [2/4] Deleting config... +del "%STATE_DIR%\openclaw.json" 2>nul +del "%DATA_DIR%\config.json" 2>nul +echo [3/4] Clearing memory... +rmdir /s /q "%DATA_DIR%\memory" 2>nul +mkdir "%DATA_DIR%\memory" 2>nul +echo [4/4] Restoring default config... +if exist "%UCLAW_DIR%default-config.json" ( + copy "%UCLAW_DIR%default-config.json" "%STATE_DIR%\openclaw.json" >nul +) else ( + echo {"gateway":{"mode":"local","auth":{"token":"uclaw"}}} > "%STATE_DIR%\openclaw.json" +) +echo. +echo Factory reset complete! Run Setup wizard [1] to reconfigure. +pause +goto :menu + +:uninstall +echo. +echo === Uninstall U-Claw === +echo. +if exist "%USERPROFILE%\.uclaw" ( + echo Found installed data: %USERPROFILE%\.uclaw + echo Type UNINSTALL to delete: + set /p unconfirm=" > " + if "%unconfirm%"=="UNINSTALL" ( + rmdir /s /q "%USERPROFILE%\.uclaw" 2>nul + echo Uninstalled! + ) else ( + echo Cancelled. + ) +) else ( + echo Portable version - just delete this folder to uninstall. + echo Path: %UCLAW_DIR% + echo. + echo For Electron desktop app: + echo Open Settings - Apps - find U-Claw - Uninstall +) +pause +goto :menu + +:checkupdate +echo. +echo === Check for Updates === +echo. +if not exist "%CORE_DIR%\node_modules\openclaw\package.json" ( + echo OpenClaw not installed. + pause + goto :menu +) +echo Checking latest version... +for /f "tokens=*" %%v in ('"%NODE_BIN%" -e "console.log(require('%CORE_DIR:\=/%/node_modules/openclaw/package.json').version)"') do set CUR_VER=%%v +echo Current: %CUR_VER% +echo. +echo To update, run in the core directory: +echo npm install openclaw@latest --registry=https://registry.npmmirror.com +pause +goto :menu + +:diskcleanup +echo. +echo === Disk Cleanup === +echo. +echo Checking sizes... +for /f "tokens=*" %%s in ('powershell -command "(Get-ChildItem '%UCLAW_DIR%' -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum / 1MB" 2^>nul') do echo Total: %%s MB +echo. +echo To clean up manually: +echo - Delete old backups in %DATA_DIR%\backups\ +echo - Delete logs in %DATA_DIR%\logs\ +pause +goto :menu + +:plugins +echo. +echo === Plugin Management === +echo. +echo [a] List installed plugins +echo [b] Install a plugin +echo [c] Remove a plugin +echo. +set /p plgchoice=" Choose (a-c): " +cd /d "%CORE_DIR%" +if "%plgchoice%"=="a" "%NODE_BIN%" "%OPENCLAW_MJS%" plugins list +if "%plgchoice%"=="b" ( + echo. + echo Common plugins: + echo @icesword760/openclaw-wechat + echo. + set /p plgname=" Plugin name: " + if not "%plgname%"=="" "%NODE_BIN%" "%OPENCLAW_MJS%" plugins install "%plgname%" +) +if "%plgchoice%"=="c" ( + "%NODE_BIN%" "%OPENCLAW_MJS%" plugins list + echo. + set /p plgname=" Plugin to remove: " + if not "%plgname%"=="" "%NODE_BIN%" "%OPENCLAW_MJS%" plugins remove "%plgname%" +) +pause goto :menu \ No newline at end of file diff --git a/portable/Windows-Start.bat b/portable/Windows-Start.bat index e1da2ca..08e6136 100644 --- a/portable/Windows-Start.bat +++ b/portable/Windows-Start.bat @@ -104,8 +104,25 @@ echo DO NOT close this window while using U-Claw! echo. cd /d "%CORE_DIR%" + +REM Check if model is configured - open Config.html for first time, dashboard for returning users +set "HAS_MODEL=no" +if exist "%DATA_DIR%\config.json" ( + findstr /c:"agent" "%DATA_DIR%\config.json" >nul 2>&1 && set "HAS_MODEL=yes" +) + +if "%HAS_MODEL%"=="yes" ( + echo Opening dashboard... + start "" http://127.0.0.1:%PORT%/#token=uclaw +) else ( + echo First time - opening Config page... + start "" "%UCLAW_DIR%Config.html?port=%PORT%" +) + +if not exist "%DATA_DIR%\logs" mkdir "%DATA_DIR%\logs" set "OPENCLAW_MJS=%CORE_DIR%\node_modules\openclaw\openclaw.mjs" -"%NODE_BIN%" "%OPENCLAW_MJS%" gateway run --allow-unconfigured --force --port %PORT% +echo Log: %DATA_DIR%\logs\gateway.log +"%NODE_BIN%" "%OPENCLAW_MJS%" gateway run --allow-unconfigured --force --port %PORT% 2>&1 | powershell -command "& { $input | Tee-Object -Append -FilePath '%DATA_DIR%\logs\gateway.log' }" echo. echo OpenClaw stopped. diff --git a/portable/lib/maintain.sh b/portable/lib/maintain.sh new file mode 100644 index 0000000..a39fd38 --- /dev/null +++ b/portable/lib/maintain.sh @@ -0,0 +1,473 @@ +#!/bin/bash +# ============================================================ +# U-Claw Maintenance Functions (shared by Mac & Linux menus) +# Requires caller to set: $UCLAW_DIR, $DATA_DIR, $STATE_DIR, +# $CONFIG_PATH, $NODE_BIN, $NPM_BIN, $CORE_DIR, +# color vars ($RED $GREEN $YELLOW $CYAN $WHITE $NC $BOLD $DIM), +# and run_oc() function. +# ============================================================ + +LOG_DIR="$DATA_DIR/logs" +BACKUP_DIR="$DATA_DIR/backups" +DEFAULT_CONFIG="$UCLAW_DIR/default-config.json" + +# Detect install mode: installed (~/.uclaw), usb (/Volumes or /media), portable (other) +detect_install_mode() { + if echo "$UCLAW_DIR" | grep -q "/.uclaw"; then + echo "installed" + elif echo "$UCLAW_DIR" | grep -qE "/Volumes/|/media/|/mnt/"; then + echo "usb" + else + echo "portable" + fi +} + +# ── [9] Kill residual gateway processes ────────────────────── + +do_kill_gateway() { + echo "" + echo -e " ${CYAN}${BOLD}━━━ 杀死残留进程 ━━━${NC}" + echo "" + + local FOUND=0 + + # Method 1: Check ports 18789-18799 + for PORT in $(seq 18789 18799); do + local PIDS="" + if command -v lsof >/dev/null 2>&1; then + PIDS=$(lsof -ti :$PORT 2>/dev/null) + elif command -v ss >/dev/null 2>&1; then + PIDS=$(ss -tlnp 2>/dev/null | grep ":$PORT " | sed -n 's/.*pid=\([0-9]*\).*/\1/p') + fi + if [ -n "$PIDS" ]; then + for PID in $PIDS; do + local CMD=$(ps -p "$PID" -o comm= 2>/dev/null || echo "unknown") + echo -e " ${YELLOW}端口 $PORT: PID $PID ($CMD)${NC}" + FOUND=1 + done + fi + done + + # Method 2: Check for openclaw.mjs processes + local OC_PIDS=$(pgrep -f "openclaw.mjs gateway" 2>/dev/null) + if [ -n "$OC_PIDS" ]; then + for PID in $OC_PIDS; do + echo -e " ${YELLOW}OpenClaw 网关进程: PID $PID${NC}" + FOUND=1 + done + fi + + if [ "$FOUND" = "0" ]; then + echo -e " ${GREEN}没有发现残留进程${NC}" + return + fi + + echo "" + read -p " 确认杀死以上进程? (y/N): " CONFIRM + if [ "$CONFIRM" != "y" ] && [ "$CONFIRM" != "Y" ]; then + echo -e " ${YELLOW}已取消${NC}" + return + fi + + # Kill port processes + for PORT in $(seq 18789 18799); do + if command -v lsof >/dev/null 2>&1; then + lsof -ti :$PORT 2>/dev/null | xargs kill 2>/dev/null + elif command -v ss >/dev/null 2>&1; then + ss -tlnp 2>/dev/null | grep ":$PORT " | sed -n 's/.*pid=\([0-9]*\).*/\1/p' | xargs kill 2>/dev/null + fi + done + + # Kill openclaw.mjs processes + pgrep -f "openclaw.mjs gateway" 2>/dev/null | xargs kill 2>/dev/null + + sleep 1 + echo -e " ${GREEN}进程已清理${NC}" +} + +# ── [10] View/export/clean logs ────────────────────────────── + +do_logs() { + echo "" + echo -e " ${CYAN}${BOLD}━━━ 日志管理 ━━━${NC}" + echo "" + + mkdir -p "$LOG_DIR" + local LOG_FILE="$LOG_DIR/gateway.log" + + echo -e " ${GREEN}[a]${NC} 查看最近日志(最后 50 行)" + echo -e " ${GREEN}[b]${NC} 导出日志到桌面" + echo -e " ${GREEN}[c]${NC} 清理 7 天前的旧日志" + echo "" + read -p " 选择 (a-c): " -n 1 LOG_CHOICE + echo "" + echo "" + + case $LOG_CHOICE in + a) + if [ -f "$LOG_FILE" ]; then + echo -e " ${DIM}── $LOG_FILE ──${NC}" + echo "" + tail -50 "$LOG_FILE" + else + echo -e " ${YELLOW}日志文件不存在: $LOG_FILE${NC}" + echo " 启动网关后会自动生成日志。" + fi + ;; + b) + if [ ! -f "$LOG_FILE" ]; then + echo -e " ${YELLOW}没有日志可导出${NC}" + return + fi + local TS=$(date +%Y%m%d_%H%M%S) + local EXPORT_DIR="$HOME/Desktop" + [ ! -d "$EXPORT_DIR" ] && EXPORT_DIR="$HOME" + local EXPORT_FILE="$EXPORT_DIR/uclaw-logs-$TS.txt" + cp "$LOG_FILE" "$EXPORT_FILE" + echo -e " ${GREEN}日志已导出: $EXPORT_FILE${NC}" + echo " 大小: $(du -sh "$EXPORT_FILE" | cut -f1)" + ;; + c) + local COUNT=$(find "$LOG_DIR" -name "*.log" -mtime +7 2>/dev/null | wc -l | tr -d ' ') + if [ "$COUNT" = "0" ]; then + echo -e " ${GREEN}没有需要清理的旧日志${NC}" + return + fi + echo -e " 找到 ${YELLOW}$COUNT${NC} 个超过 7 天的日志文件" + read -p " 确认清理? (y/N): " CONFIRM + if [ "$CONFIRM" = "y" ] || [ "$CONFIRM" = "Y" ]; then + find "$LOG_DIR" -name "*.log" -mtime +7 -delete 2>/dev/null + echo -e " ${GREEN}旧日志已清理${NC}" + else + echo -e " ${YELLOW}已取消${NC}" + fi + ;; + *) + echo -e " ${RED}无效选择${NC}" + ;; + esac +} + +# ── [11] Factory reset ─────────────────────────────────────── + +do_factory_reset() { + echo "" + echo -e " ${RED}${BOLD}━━━ 恢复出厂设置 ━━━${NC}" + echo "" + echo -e " ${RED}警告: 此操作将删除所有配置和记忆数据!${NC}" + echo "" + echo " 将执行以下操作:" + echo " 1. 自动备份当前配置和记忆" + echo " 2. 删除配置文件 (openclaw.json)" + echo " 3. 删除记忆数据 (memory/)" + echo " 4. 恢复默认配置" + echo "" + echo -e " ${YELLOW}请输入 RESET 确认(区分大小写):${NC}" + read -p " > " CONFIRM + + if [ "$CONFIRM" != "RESET" ]; then + echo -e " ${YELLOW}已取消${NC}" + return + fi + + # Step 1: Auto backup + echo "" + echo -e " ${CYAN}[1/4] 自动备份...${NC}" + local TS=$(date +%Y%m%d_%H%M%S) + local BK="$BACKUP_DIR/pre-reset_$TS" + mkdir -p "$BK" + [ -f "$CONFIG_PATH" ] && cp "$CONFIG_PATH" "$BK/" 2>/dev/null + [ -d "$DATA_DIR/memory" ] && cp -R "$DATA_DIR/memory" "$BK/" 2>/dev/null + echo -e " ${GREEN}备份已保存: $BK${NC}" + + # Step 2: Delete config + echo -e " ${CYAN}[2/4] 删除配置...${NC}" + rm -f "$CONFIG_PATH" 2>/dev/null + rm -f "$DATA_DIR/config.json" 2>/dev/null + + # Step 3: Delete memory + echo -e " ${CYAN}[3/4] 清理记忆数据...${NC}" + rm -rf "$DATA_DIR/memory" 2>/dev/null + mkdir -p "$DATA_DIR/memory" + + # Step 4: Restore default config + echo -e " ${CYAN}[4/4] 恢复默认配置...${NC}" + mkdir -p "$STATE_DIR" + if [ -f "$DEFAULT_CONFIG" ]; then + cp "$DEFAULT_CONFIG" "$CONFIG_PATH" + else + cat > "$CONFIG_PATH" << 'CFGEOF' +{ + "gateway": { + "mode": "local", + "auth": { "token": "uclaw" } + } +} +CFGEOF + fi + + echo "" + echo -e " ${GREEN}出厂设置已恢复!${NC}" + echo -e " 备份位置: $BK" + echo -e " 请重新运行配置向导 [1] 设置模型。" +} + +# ── [12] Uninstall ─────────────────────────────────────────── + +do_uninstall() { + echo "" + echo -e " ${RED}${BOLD}━━━ 卸载 U-Claw ━━━${NC}" + echo "" + + local MODE=$(detect_install_mode) + + case $MODE in + installed) + echo " 检测到: 已安装版本 (~/.uclaw/)" + echo "" + echo " 将删除以下目录:" + echo -e " ${YELLOW}$HOME/.uclaw/${NC}" + if [ -d "$HOME/.uclaw" ]; then + echo " 大小: $(du -sh "$HOME/.uclaw" 2>/dev/null | cut -f1)" + fi + echo "" + echo -e " ${RED}请输入 UNINSTALL 确认:${NC}" + read -p " > " CONFIRM + if [ "$CONFIRM" != "UNINSTALL" ]; then + echo -e " ${YELLOW}已取消${NC}" + return + fi + rm -rf "$HOME/.uclaw" + echo -e " ${GREEN}卸载完成!${NC}" + ;; + usb) + echo " 检测到: U 盘模式" + echo "" + echo " 便携版不需要卸载 —— 只需:" + echo " 1. 关闭所有 U-Claw 窗口" + echo " 2. 安全弹出 U 盘" + echo " 3. 如需清理电脑上的数据,删除 ~/.uclaw/ (如果存在)" + echo "" + if [ -d "$HOME/.uclaw" ]; then + echo -e " ${YELLOW}发现本机数据: ~/.uclaw/${NC}" + echo " 大小: $(du -sh "$HOME/.uclaw" 2>/dev/null | cut -f1)" + read -p " 是否删除本机数据? (y/N): " DEL + if [ "$DEL" = "y" ] || [ "$DEL" = "Y" ]; then + rm -rf "$HOME/.uclaw" + echo -e " ${GREEN}本机数据已清理${NC}" + fi + fi + ;; + portable) + echo " 检测到: 便携版本" + echo "" + echo " 便携版不需要卸载 —— 直接删除文件夹即可:" + echo -e " ${YELLOW}$UCLAW_DIR${NC}" + echo "" + if [ -d "$HOME/.uclaw" ]; then + echo -e " ${YELLOW}发现本机数据: ~/.uclaw/${NC}" + echo " 大小: $(du -sh "$HOME/.uclaw" 2>/dev/null | cut -f1)" + read -p " 是否删除本机数据? (y/N): " DEL + if [ "$DEL" = "y" ] || [ "$DEL" = "Y" ]; then + rm -rf "$HOME/.uclaw" + echo -e " ${GREEN}本机数据已清理${NC}" + fi + fi + echo "" + echo " Electron 桌面版卸载:" + if [ "$(uname)" = "Darwin" ]; then + echo " macOS: 将 U-Claw.app 从「应用程序」拖到废纸篓" + else + echo " Linux: sudo apt remove u-claw 或 rm ~/.local/share/applications/u-claw.desktop" + fi + ;; + esac +} + +# ── [13] Check for updates (P1) ───────────────────────────── + +do_update() { + echo "" + echo -e " ${CYAN}${BOLD}━━━ 检查更新 ━━━${NC}" + echo "" + + if [ ! -f "$NODE_BIN" ]; then + echo -e " ${RED}Node.js 未找到,无法检查更新${NC}" + return + fi + + echo -e " ${DIM}正在查询最新版本...${NC}" + + # Get current version + local CUR_VER="" + if [ -f "$CORE_DIR/node_modules/openclaw/package.json" ]; then + CUR_VER=$("$NODE_BIN" -e "console.log(require('$CORE_DIR/node_modules/openclaw/package.json').version)" 2>/dev/null) + fi + + if [ -z "$CUR_VER" ]; then + echo -e " ${RED}无法读取当前版本(OpenClaw 可能未安装)${NC}" + return + fi + + # Get latest version from npmmirror + local LATEST_VER=$(curl -s --connect-timeout 10 "https://registry.npmmirror.com/openclaw/latest" 2>/dev/null | "$NODE_BIN" -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{console.log(JSON.parse(d).version)}catch(e){console.log('error')}})" 2>/dev/null) + + if [ -z "$LATEST_VER" ] || [ "$LATEST_VER" = "error" ]; then + echo -e " ${YELLOW}无法获取最新版本(网络问题?)${NC}" + echo " 当前版本: $CUR_VER" + return + fi + + echo " 当前版本: $CUR_VER" + echo " 最新版本: $LATEST_VER" + echo "" + + if [ "$CUR_VER" = "$LATEST_VER" ]; then + echo -e " ${GREEN}已是最新版本!${NC}" + return + fi + + echo -e " ${YELLOW}有新版本可用!${NC}" + read -p " 是否立即升级? (y/N): " DO_UPDATE + if [ "$DO_UPDATE" != "y" ] && [ "$DO_UPDATE" != "Y" ]; then + echo -e " ${YELLOW}已取消${NC}" + return + fi + + echo "" + echo -e " ${CYAN}正在升级...${NC}" + cd "$CORE_DIR" + "$NPM_BIN" install openclaw@latest --registry=https://registry.npmmirror.com 2>&1 + local NEW_VER=$("$NODE_BIN" -e "console.log(require('./node_modules/openclaw/package.json').version)" 2>/dev/null) + echo "" + echo -e " ${GREEN}升级完成!${NC} $CUR_VER → $NEW_VER" +} + +# ── [14] Disk cleanup (P1) ─────────────────────────────────── + +do_cleanup() { + echo "" + echo -e " ${CYAN}${BOLD}━━━ 清理空间 ━━━${NC}" + echo "" + + # Show directory sizes + echo -e " ${WHITE}目录占用:${NC}" + [ -d "$CORE_DIR/node_modules" ] && echo " node_modules: $(du -sh "$CORE_DIR/node_modules" 2>/dev/null | cut -f1)" + [ -d "$DATA_DIR/memory" ] && echo " memory: $(du -sh "$DATA_DIR/memory" 2>/dev/null | cut -f1)" + [ -d "$BACKUP_DIR" ] && echo " backups: $(du -sh "$BACKUP_DIR" 2>/dev/null | cut -f1)" + [ -d "$LOG_DIR" ] && echo " logs: $(du -sh "$LOG_DIR" 2>/dev/null | cut -f1)" + echo " 总计: $(du -sh "$UCLAW_DIR" 2>/dev/null | cut -f1)" + echo "" + + local CLEANED=0 + + # Clean old backups (keep latest 3) + if [ -d "$BACKUP_DIR" ]; then + local BK_COUNT=$(ls -d "$BACKUP_DIR"/*/ 2>/dev/null | wc -l | tr -d ' ') + if [ "$BK_COUNT" -gt 3 ]; then + local OLD_COUNT=$((BK_COUNT - 3)) + echo -e " ${YELLOW}发现 $BK_COUNT 个备份,保留最新 3 个,删除 $OLD_COUNT 个旧备份${NC}" + read -p " 确认? (y/N): " DEL_BK + if [ "$DEL_BK" = "y" ] || [ "$DEL_BK" = "Y" ]; then + ls -dt "$BACKUP_DIR"/*/ 2>/dev/null | tail -n "$OLD_COUNT" | while read -r DIR; do + rm -rf "$DIR" + done + echo -e " ${GREEN}旧备份已清理${NC}" + CLEANED=1 + fi + else + echo " 备份: ${BK_COUNT} 个(无需清理)" + fi + fi + + # Clean old logs (>7 days) + if [ -d "$LOG_DIR" ]; then + local OLD_LOGS=$(find "$LOG_DIR" -name "*.log" -mtime +7 2>/dev/null | wc -l | tr -d ' ') + if [ "$OLD_LOGS" -gt 0 ]; then + echo -e " ${YELLOW}发现 $OLD_LOGS 个超过 7 天的日志${NC}" + read -p " 确认清理? (y/N): " DEL_LOG + if [ "$DEL_LOG" = "y" ] || [ "$DEL_LOG" = "Y" ]; then + find "$LOG_DIR" -name "*.log" -mtime +7 -delete 2>/dev/null + echo -e " ${GREEN}旧日志已清理${NC}" + CLEANED=1 + fi + else + echo " 日志: 无需清理" + fi + fi + + # Clean npm cache + echo "" + read -p " 是否清理 npm 缓存? (y/N): " DEL_CACHE + if [ "$DEL_CACHE" = "y" ] || [ "$DEL_CACHE" = "Y" ]; then + "$NPM_BIN" cache clean --force 2>/dev/null + echo -e " ${GREEN}npm 缓存已清理${NC}" + CLEANED=1 + fi + + if [ "$CLEANED" = "0" ]; then + echo "" + echo -e " ${GREEN}没有需要清理的内容${NC}" + else + echo "" + echo " 清理后总计: $(du -sh "$UCLAW_DIR" 2>/dev/null | cut -f1)" + fi +} + +# ── [15] Plugin management (P1) ───────────────────────────── + +do_plugins() { + echo "" + echo -e " ${CYAN}${BOLD}━━━ 插件管理 ━━━${NC}" + echo "" + echo -e " ${GREEN}[a]${NC} 列出已安装插件" + echo -e " ${GREEN}[b]${NC} 安装插件" + echo -e " ${GREEN}[c]${NC} 卸载插件" + echo "" + read -p " 选择 (a-c): " -n 1 PLG_CHOICE + echo "" + echo "" + + case $PLG_CHOICE in + a) + echo -e " ${WHITE}已安装插件:${NC}" + echo "" + run_oc plugins list 2>&1 || echo -e " ${YELLOW}无法获取插件列表${NC}" + ;; + b) + echo " 常用插件:" + echo " @icesword760/openclaw-wechat — 微信" + echo " @nicepkg/openclaw-plugin-qq — QQ(社区版)" + echo "" + read -p " 输入插件名称(留空取消): " PLG_NAME + if [ -z "$PLG_NAME" ]; then + echo -e " ${YELLOW}已取消${NC}" + return + fi + echo "" + echo -e " ${CYAN}正在安装 $PLG_NAME ...${NC}" + run_oc plugins install "$PLG_NAME" 2>&1 + echo "" + echo -e " ${GREEN}安装完成${NC}" + ;; + c) + echo -e " ${WHITE}已安装插件:${NC}" + echo "" + run_oc plugins list 2>&1 || true + echo "" + read -p " 输入要卸载的插件名称(留空取消): " PLG_NAME + if [ -z "$PLG_NAME" ]; then + echo -e " ${YELLOW}已取消${NC}" + return + fi + echo "" + echo -e " ${CYAN}正在卸载 $PLG_NAME ...${NC}" + run_oc plugins remove "$PLG_NAME" 2>&1 + echo "" + echo -e " ${GREEN}卸载完成${NC}" + ;; + *) + echo -e " ${RED}无效选择${NC}" + ;; + esac +} diff --git a/portable/setup-linux-usb.sh b/portable/setup-linux-usb.sh new file mode 100755 index 0000000..98c99d9 --- /dev/null +++ b/portable/setup-linux-usb.sh @@ -0,0 +1,220 @@ +#!/bin/bash +# ============================================================ +# U-Claw - Linux 启动盘 OpenClaw 安装脚本 +# 在 Linux Mint 启动盘环境中运行此脚本安装 OpenClaw +# +# 使用方法: +# 1. 用 Rufus 制作 Linux Mint 启动盘(开启 Persistent Storage) +# 2. 从 USB 启动进入 Linux Mint +# 3. 连接网络 +# 4. 打开终端,运行: bash setup-linux-usb.sh +# ============================================================ + +set -e + +GREEN='\033[0;32m' +CYAN='\033[0;36m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +NC='\033[0m' +BOLD='\033[1m' + +NODE_VER="v22.14.0" +MIRROR="https://registry.npmmirror.com" +NODE_MIRROR="https://npmmirror.com/mirrors/node" + +INSTALL_DIR="$HOME/U-Claw" + +clear +echo "" +echo -e "${CYAN}${BOLD}" +echo " ╔══════════════════════════════════════╗" +echo " ║ U-Claw Linux 启动盘安装 ║" +echo " ║ 在 Live Linux 环境中安装 OpenClaw ║" +echo " ╚══════════════════════════════════════╝" +echo -e "${NC}" +echo "" + +# ---- Check network ---- +echo -e " ${BOLD}[0/4] 检查网络连接...${NC}" +if ! ping -c 1 -W 3 npmmirror.com >/dev/null 2>&1; then + echo -e " ${RED}无法连接到网络!${NC}" + echo -e " ${YELLOW}请先连接 WiFi 或插入网线,然后重新运行此脚本。${NC}" + echo "" + read -p " 按回车退出..." + exit 1 +fi +echo -e " ${GREEN}网络正常 ✓${NC}" +echo "" + +# ---- Check CPU ---- +ARCH=$(uname -m) +if [ "$ARCH" = "x86_64" ]; then + NODE_PLATFORM="linux-x64" + NODE_DIR_NAME="node-linux-x64" + echo -e " ${GREEN}架构: x86_64 ✓${NC}" +elif [ "$ARCH" = "aarch64" ]; then + NODE_PLATFORM="linux-arm64" + NODE_DIR_NAME="node-linux-arm64" + echo -e " ${GREEN}架构: ARM64 ✓${NC}" +else + echo -e " ${RED}不支持的架构: $ARCH${NC}" + exit 1 +fi +echo "" + +# ---- Step 1: Install Node.js ---- +echo -e " ${BOLD}[1/4] 安装 Node.js $NODE_VER...${NC}" + +NODE_INSTALL_DIR="$INSTALL_DIR/runtime/$NODE_DIR_NAME" + +if [ -f "$NODE_INSTALL_DIR/bin/node" ]; then + echo -e " ${GREEN}Node.js 已存在,跳过${NC}" +else + TARBALL="node-${NODE_VER}-${NODE_PLATFORM}.tar.gz" + URL="${NODE_MIRROR}/${NODE_VER}/${TARBALL}" + + echo -e " ${CYAN}下载: $URL${NC}" + mkdir -p "$NODE_INSTALL_DIR" + curl -# -L "$URL" -o "/tmp/$TARBALL" + tar -xzf "/tmp/$TARBALL" -C "$NODE_INSTALL_DIR" --strip-components=1 + rm -f "/tmp/$TARBALL" + chmod +x "$NODE_INSTALL_DIR/bin/node" + echo -e " ${GREEN}Node.js 安装完成 ✓${NC}" +fi + +NODE_BIN="$NODE_INSTALL_DIR/bin/node" +NPM_BIN="$NODE_INSTALL_DIR/bin/npm" +export PATH="$NODE_INSTALL_DIR/bin:$PATH" + +echo -e " 版本: $("$NODE_BIN" --version)" +echo "" + +# ---- Step 2: Install OpenClaw ---- +echo -e " ${BOLD}[2/4] 安装 OpenClaw...${NC}" + +CORE_DIR="$INSTALL_DIR/core" +mkdir -p "$CORE_DIR" + +cat > "$CORE_DIR/package.json" << 'PKGEOF' +{ + "name": "u-claw-core", + "version": "1.0.0", + "private": true, + "dependencies": { + "openclaw": "latest" + } +} +PKGEOF + +cd "$CORE_DIR" +echo -e " ${CYAN}从国内镜像下载 OpenClaw...${NC}" +"$NODE_BIN" "$NPM_BIN" install --registry="$MIRROR" 2>&1 | tail -5 +echo -e " ${CYAN}安装 QQ 插件...${NC}" +"$NODE_BIN" "$NPM_BIN" install @sliverp/qqbot@latest --registry="$MIRROR" 2>&1 | tail -2 +echo -e " ${GREEN}OpenClaw 安装完成 ✓${NC}" +echo "" + +# ---- Step 3: Create config & launch script ---- +echo -e " ${BOLD}[3/4] 创建配置和启动脚本...${NC}" + +# Default config +mkdir -p "$INSTALL_DIR/data/.openclaw" +mkdir -p "$INSTALL_DIR/data/memory" +mkdir -p "$INSTALL_DIR/data/backups" + +CONFIG_PATH="$INSTALL_DIR/data/.openclaw/openclaw.json" +if [ ! -f "$CONFIG_PATH" ]; then + cat > "$CONFIG_PATH" << 'CFGEOF' +{ + "gateway": { + "mode": "local", + "auth": { "token": "uclaw" } + } +} +CFGEOF +fi + +# Launch script +cat > "$INSTALL_DIR/start.sh" << STARTEOF +#!/bin/bash +DIR="$INSTALL_DIR" +NODE_BIN="$NODE_BIN" +CORE_DIR="\$DIR/core" +OPENCLAW_MJS="\$CORE_DIR/node_modules/openclaw/openclaw.mjs" + +export OPENCLAW_HOME="\$DIR/data" +export OPENCLAW_STATE_DIR="\$DIR/data/.openclaw" +export OPENCLAW_CONFIG_PATH="\$DIR/data/.openclaw/openclaw.json" + +PORT=18789 +while ss -tlnp 2>/dev/null | grep -q ":\$PORT " || netstat -tlnp 2>/dev/null | grep -q ":\$PORT "; do + PORT=\$((PORT + 1)) + [ \$PORT -gt 18799 ] && echo "No available port" && exit 1 +done + +echo "Starting OpenClaw on port \$PORT..." +cd "\$CORE_DIR" +"\$NODE_BIN" "\$OPENCLAW_MJS" gateway run --allow-unconfigured --force --port \$PORT & +PID=\$! + +for i in \$(seq 1 30); do + sleep 0.5 + if curl -s -o /dev/null "http://127.0.0.1:\$PORT/" 2>/dev/null; then + xdg-open "http://127.0.0.1:\$PORT/#token=uclaw" 2>/dev/null || echo "Open: http://127.0.0.1:\$PORT/#token=uclaw" + break + fi +done + +wait \$PID +STARTEOF +chmod +x "$INSTALL_DIR/start.sh" + +echo -e " ${GREEN}启动脚本已创建 ✓${NC}" +echo "" + +# ---- Step 4: Create desktop shortcut ---- +echo -e " ${BOLD}[4/4] 创建桌面快捷方式...${NC}" + +DESKTOP_FILE="$HOME/Desktop/U-Claw.desktop" +cat > "$DESKTOP_FILE" << DESKEOF +[Desktop Entry] +Name=U-Claw AI 助手 +Comment=Portable AI Agent powered by OpenClaw +Exec=bash $INSTALL_DIR/start.sh +Terminal=true +Type=Application +Categories=Utility; +DESKEOF +chmod +x "$DESKTOP_FILE" + +# Also add to applications menu +mkdir -p "$HOME/.local/share/applications" +cp "$DESKTOP_FILE" "$HOME/.local/share/applications/u-claw.desktop" + +echo -e " ${GREEN}桌面快捷方式已创建 ✓${NC}" +echo "" + +# ---- Summary ---- +INSTALL_SIZE=$(du -sh "$INSTALL_DIR" | cut -f1) + +echo -e " ${GREEN}${BOLD}╔══════════════════════════════════════╗" +echo -e " ║ ✅ 安装成功! ║" +echo -e " ╚══════════════════════════════════════╝${NC}" +echo "" +echo -e " ${BOLD}安装位置:${NC} $INSTALL_DIR" +echo -e " ${BOLD}大小:${NC} $INSTALL_SIZE" +echo "" +echo -e " ${BOLD}启动方式:${NC}" +echo -e " 双击桌面的 ${CYAN}U-Claw AI 助手${NC} 图标" +echo -e " 或终端运行: ${CYAN}bash ~/U-Claw/start.sh${NC}" +echo "" +echo -e " ${BOLD}首次使用:${NC}" +echo -e " 启动后浏览器自动打开 → 选择 AI 模型 → 填写 API Key" +echo "" +echo -e " ${YELLOW}${BOLD}⚠️ 持久化提醒:${NC}" +echo -e " ${YELLOW}如果你用 Rufus 制作启动盘时开启了 Persistent Storage,${NC}" +echo -e " ${YELLOW}上述安装和配置在重启后仍然保留。${NC}" +echo -e " ${YELLOW}如果没有开启持久化,重启后需要重新运行此脚本。${NC}" +echo "" +read -p " 按回车关闭..."