diff --git a/portable/default-config.json b/portable/default-config.json new file mode 100644 index 0000000..21413dd --- /dev/null +++ b/portable/default-config.json @@ -0,0 +1,6 @@ +{ + "gateway": { + "mode": "local", + "auth": { "token": "uclaw" } + } +} diff --git a/portable/migrate.js b/portable/migrate.js new file mode 100644 index 0000000..fbc2a85 --- /dev/null +++ b/portable/migrate.js @@ -0,0 +1,33 @@ +#!/usr/bin/env node +// U-Claw config migration script +// Runs on every startup to ensure config compatibility + +const fs = require('fs'); +const path = require('path'); + +const dataDir = process.argv[2] || path.join(__dirname, '..', 'data'); +const configPath = path.join(dataDir, 'config.json'); + +if (!fs.existsSync(configPath)) process.exit(0); + +try { + const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); + + // Ensure gateway section exists + if (!config.gateway) { + config.gateway = { mode: 'local', auth: { token: 'uclaw' } }; + } + + // Ensure auth token exists + if (!config.gateway.auth || !config.gateway.auth.token) { + config.gateway.auth = { token: 'uclaw' }; + } + + // Future migrations go here: + // if (!config._version) { config._version = '1.1'; ... } + + fs.writeFileSync(configPath, JSON.stringify(config, null, 2)); +} catch (e) { + // Don't crash on migration errors + console.error('Migration warning:', e.message); +} diff --git a/portable/start.bat b/portable/start.bat new file mode 100644 index 0000000..79f402d --- /dev/null +++ b/portable/start.bat @@ -0,0 +1,102 @@ +@echo off +chcp 65001 >/dev/null 2>&1 +title U-Claw - Portable AI Agent + +echo. +echo ======================================== +echo U-Claw v1.1 - Portable AI Agent +echo ======================================== +echo. + +set "UCLAW_DIR=%~dp0" +set "APP_DIR=%UCLAW_DIR%app" +set "CORE_DIR=%APP_DIR%\core" +set "DATA_DIR=%UCLAW_DIR%data" +set "NODE_DIR=%APP_DIR%\runtime\node-win-x64" +set "NODE_BIN=%NODE_DIR%\node.exe" +set "NPM_BIN=%NODE_DIR%\npm.cmd" + +set "OPENCLAW_HOME=%DATA_DIR%" +set "OPENCLAW_STATE_DIR=%DATA_DIR%" +set "OPENCLAW_CONFIG_PATH=%DATA_DIR%\config.json" + +REM Check runtime +if not exist "%NODE_BIN%" ( + echo [ERROR] Node.js runtime not found + echo Please ensure app\runtime\node-win-x64 is complete + pause + exit /b 1 +) + +for /f "tokens=*" %%v in ('"%NODE_BIN%" --version') do set NODE_VER=%%v +echo Node.js: %NODE_VER% +echo. + +set "PATH=%NODE_DIR%;%NODE_DIR%\node_modules\.bin;%PATH%" + +REM Init data +if not exist "%DATA_DIR%" mkdir "%DATA_DIR%" +if not exist "%DATA_DIR%\memory" mkdir "%DATA_DIR%\memory" +if not exist "%DATA_DIR%\backups" mkdir "%DATA_DIR%\backups" + +REM Default config +if not exist "%DATA_DIR%\config.json" ( + echo First run - creating default config... + echo {"gateway":{"mode":"local","auth":{"token":"uclaw"}}} > "%DATA_DIR%\config.json" + echo Config created + echo. +) + +REM Check dependencies +if not exist "%CORE_DIR%\node_modules" ( + echo First run - installing dependencies... + echo Using China mirror, please wait... + echo. + cd /d "%CORE_DIR%" + call "%NPM_BIN%" install --registry=https://registry.npmmirror.com + echo. + echo Dependencies installed! + echo. +) + +if not exist "%CORE_DIR%\dist" ( + echo First run - building... + cd /d "%CORE_DIR%" + call "%NPM_BIN%" run build + echo. +) + +REM Find available port +set PORT=18789 +:check_port +netstat -an | findstr ":%PORT% " | findstr "LISTENING" >/dev/null 2>&1 +if %errorlevel%==0 ( + echo Port %PORT% in use, trying next... + set /a PORT+=1 + if %PORT% gtr 18799 ( + echo No available port 18789-18799 + pause + exit /b 1 + ) + goto :check_port +) + +echo Starting OpenClaw on port %PORT%... +echo DO NOT close this window! +echo. +echo ---------------------------------------- +echo Browser will open automatically. +echo First time setup in web console: +echo 1. Choose AI model (DeepSeek / Kimi / Qwen) +echo 2. Enter API Key +echo 3. Connect chat platform (QQ / Feishu / DingTalk) +echo ---------------------------------------- +echo. + +cd /d "%CORE_DIR%" +start "" http://127.0.0.1:%PORT%/#token=uclaw +"%NODE_BIN%" openclaw.mjs gateway run --allow-unconfigured --force --port %PORT% + +echo. +echo OpenClaw stopped. +pause diff --git a/portable/start.command b/portable/start.command new file mode 100755 index 0000000..dff5895 --- /dev/null +++ b/portable/start.command @@ -0,0 +1,173 @@ +#!/bin/bash +# ============================================================ +# U-Claw - Portable AI Agent +# Double-click to start / 双击启动 +# ============================================================ + +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 ║" +echo " ╚══════════════════════════════════════╝" +echo -e "${NC}" + +# ---- 1. Detect CPU & set runtime ---- +ARCH=$(uname -m) +if [ "$ARCH" = "arm64" ]; then + NODE_DIR="$APP_DIR/runtime/node-mac-arm64" + echo -e " ${GREEN}Apple Silicon (M series)${NC}" +else + NODE_DIR="$APP_DIR/runtime/node-mac-x64" + echo -e " ${GREEN}Intel Mac${NC}" +fi + +NODE_BIN="$NODE_DIR/bin/node" +export PATH="$NODE_DIR/bin:$PATH" + +# ---- 2. Remove macOS quarantine ---- +if xattr -l "$NODE_BIN" 2>/dev/null | grep -q "com.apple.quarantine"; then + echo -e " ${YELLOW}Removing macOS security restriction...${NC}" + xattr -rd com.apple.quarantine "$UCLAW_DIR" 2>/dev/null || true + echo -e " ${GREEN}Done${NC}" +fi + +# ---- 3. Check runtime ---- +if [ ! -f "$NODE_BIN" ]; then + echo -e " ${RED}Error: Node.js runtime not found${NC}" + echo " Please 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 "" + +# ---- 4. 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 + +# ---- 5. 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" + +# ---- 6. Run migration if exists ---- +if [ -f "$SYSTEM_DIR/migrate.js" ]; then + "$NODE_BIN" "$SYSTEM_DIR/migrate.js" "$DATA_DIR" 2>/dev/null || true +fi + +# ---- 7. 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 + +if [ ! -d "$CORE_DIR/dist" ]; then + echo -e " ${YELLOW}First run - building...${NC}" + cd "$CORE_DIR" + "$NODE_BIN" "$NODE_DIR/bin/npm" run build 2>&1 + echo "" +fi + +# ---- 8. Find available port ---- +PORT=18789 +while lsof -i :$PORT >/dev/null 2>&1; 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'; + 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 + +# ---- 9. 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") + +"$NODE_BIN" openclaw.mjs gateway run --allow-unconfigured --force --port $PORT & +GW_PID=$! + +# ---- 10. 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 + URL="http://127.0.0.1:$PORT/#token=${TOKEN}" + echo "" + echo -e " ${GREEN}✅ Started successfully!${NC}" + echo "" + echo -e " ${CYAN}Dashboard: ${URL}${NC}" + echo "" + echo -e " ${YELLOW}First time? Configure in the web console:${NC}" + echo " 1. Choose AI model (DeepSeek / Kimi / Qwen)" + echo " 2. Enter API Key" + echo " 3. Connect chat platform (QQ / Feishu / DingTalk)" + echo "" + open "$URL" 2>/dev/null + break + fi +done + +wait $GW_PID + +echo "" +echo -e " ${YELLOW}OpenClaw stopped.${NC}" +read -p " Press Enter to close..."