feat(usb-release): 统一发布版,Mac + Windows 跨平台支持

基于 Pro 精简结构,去掉 app/ 中间层,core/runtime/data/ 直接在根目录。
- Mac: 启动 U-Claw.command + 重新配置.command(含 Config GUI 首次配置)
- Windows: Windows-Start.bat + Windows-Menu.bat(15 项功能菜单)
- setup.sh: --all-platforms 三平台 runtime, --with-toolkit 下载安装包
- 10 个中国优化技能 (skills/)
- toolkit/ 放 Node.js pkg/msi 供离线装机
This commit is contained in:
dongsheng123132
2026-03-19 21:05:45 +08:00
parent c0d047496b
commit 025bb62a5b
20 changed files with 2875 additions and 0 deletions

183
usb-release/启动 U-Claw.command Executable file
View File

@@ -0,0 +1,183 @@
#!/bin/bash
# ============================================================
# U-Claw — 便携版启动器 (macOS)
# 双击此文件即可启动 OpenClaw
# ============================================================
DIR="$(cd "$(dirname "$0")" && pwd)"
ARCH=$(uname -m)
# --- Colors ---
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
echo ""
echo -e "${CYAN} 🦞 U-Claw — 便携版${NC}"
echo -e " ════════════════════════════════"
echo ""
# --- Find Node.js ---
if [ "$ARCH" = "arm64" ]; then
NODE_BIN="$DIR/runtime/node-mac-arm64/bin/node"
else
NODE_BIN="$DIR/runtime/node-mac-x64/bin/node"
fi
[ ! -f "$NODE_BIN" ] && NODE_BIN="$(which node 2>/dev/null)"
if [ -z "$NODE_BIN" ] || [ ! -f "$NODE_BIN" ]; then
echo -e "${RED} ✗ 未找到 Node.js请确保 runtime/ 目录完整${NC}"
echo " 按回车退出..."; read; exit 1
fi
NODE_VER=$("$NODE_BIN" --version 2>/dev/null)
echo -e " ${GREEN}✓${NC} Node.js: $NODE_VER ($ARCH)"
# --- Remove macOS quarantine ---
if xattr -l "$NODE_BIN" 2>/dev/null | grep -q "com.apple.quarantine"; then
echo -e " ${YELLOW}移除 macOS 安全限制...${NC}"
xattr -rd com.apple.quarantine "$DIR" 2>/dev/null || true
echo -e " ${GREEN}✓${NC} 已移除"
fi
# --- Find OpenClaw ---
CORE_DIR="$DIR/core"
OPENCLAW_MJS="$CORE_DIR/node_modules/openclaw/openclaw.mjs"
if [ ! -f "$OPENCLAW_MJS" ]; then
echo -e "${RED} ✗ 未找到 OpenClaw请确保 core/ 目录完整${NC}"
echo " 按回车退出..."; read; exit 1
fi
echo -e " ${GREEN}✓${NC} OpenClaw: 已就绪"
# --- Setup portable data dir ---
DATA_DIR="$DIR/data"
export OPENCLAW_HOME="$DATA_DIR"
export OPENCLAW_STATE_DIR="$DATA_DIR/.openclaw"
export OPENCLAW_CONFIG_PATH="$DATA_DIR/.openclaw/openclaw.json"
mkdir -p "$DATA_DIR/.openclaw" "$DATA_DIR/memory" "$DATA_DIR/backups" "$DATA_DIR/logs"
# --- Check if config needs setup (first run) ---
CONFIG_FILE="$DATA_DIR/.openclaw/openclaw.json"
NEEDS_CONFIG=false
if [ ! -f "$CONFIG_FILE" ]; then
NEEDS_CONFIG=true
else
if ! grep -qE '"apiKey"|"ZAI_API_KEY"|"providers"' "$CONFIG_FILE" 2>/dev/null; then
NEEDS_CONFIG=true
fi
fi
# --- If first run, open Config GUI first ---
if [ "$NEEDS_CONFIG" = "true" ]; then
echo ""
echo -e " ${YELLOW}⚡ 首次运行,请先配置模型和 API Key${NC}"
echo -e " ${YELLOW} 正在打开配置页面...${NC}"
echo ""
CONFIG_PORT=18780
while lsof -i :$CONFIG_PORT >/dev/null 2>&1; do
CONFIG_PORT=$((CONFIG_PORT + 1))
[ $CONFIG_PORT -gt 18785 ] && break
done
"$NODE_BIN" -e "
const http = require('http');
const fs = require('fs');
const path = require('path');
const configPath = '$CONFIG_FILE';
const htmlPath = path.join('$DIR', 'Config.html');
const server = http.createServer((req, res) => {
if (req.method === 'GET' && (req.url === '/' || req.url === '/index.html')) {
res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'});
res.end(fs.readFileSync(htmlPath, 'utf-8'));
} else if (req.method === 'GET' && req.url === '/api/config') {
try {
const cfg = fs.existsSync(configPath) ? JSON.parse(fs.readFileSync(configPath, 'utf-8')) : {};
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(JSON.stringify(cfg));
} catch(e) {
res.writeHead(200, {'Content-Type': 'application/json'});
res.end('{}');
}
} else if (req.method === 'POST' && req.url === '/api/config') {
let body = '';
req.on('data', c => body += c);
req.on('end', () => {
try {
const cfg = JSON.parse(body);
fs.mkdirSync(path.dirname(configPath), {recursive: true});
fs.writeFileSync(configPath, JSON.stringify(cfg, null, 2));
res.writeHead(200, {'Content-Type': 'application/json'});
res.end('{\"ok\":true}');
} catch(e) {
res.writeHead(400, {'Content-Type': 'application/json'});
res.end('{\"error\":\"' + e.message + '\"}');
}
});
} else if (req.method === 'POST' && req.url === '/api/done') {
res.writeHead(200, {'Content-Type': 'application/json'});
res.end('{\"ok\":true}');
setTimeout(() => { server.close(); process.exit(0); }, 300);
} else {
res.writeHead(404); res.end('Not Found');
}
});
server.listen($CONFIG_PORT, '127.0.0.1', () => {
console.log('Config server: http://127.0.0.1:$CONFIG_PORT');
});
" &
CONFIG_PID=$!
sleep 1
open "http://127.0.0.1:$CONFIG_PORT/"
echo -e " ${CYAN}等待配置完成...${NC}"
echo -e " (在浏览器中选择模型、填写 API Key点击「保存并启动」"
echo ""
wait $CONFIG_PID 2>/dev/null
echo -e " ${GREEN}✓${NC} 配置已保存"
echo ""
fi
# --- Check dependencies ---
if [ ! -d "$CORE_DIR/node_modules" ]; then
echo -e " ${YELLOW}首次运行,安装依赖...${NC}"
NODE_DIR="$(dirname "$(dirname "$NODE_BIN")")"
cd "$CORE_DIR"
"$NODE_BIN" "$NODE_DIR/bin/npm" install --registry=https://registry.npmmirror.com 2>&1
echo -e " ${GREEN}✓${NC} 依赖安装完成"
echo ""
fi
# --- Find available port ---
PORT=18789
while lsof -i :$PORT >/dev/null 2>&1; do
PORT=$((PORT + 1))
[ $PORT -gt 18799 ] && echo -e "${RED} ✗ 没有可用端口 (18789-18799)${NC}" && exit 1
done
echo -e " ${CYAN}🚀 正在启动 OpenClaw...${NC}"
echo ""
# --- Start OpenClaw gateway ---
cd "$CORE_DIR"
"$NODE_BIN" "$OPENCLAW_MJS" gateway run --allow-unconfigured --force --port $PORT &
GW_PID=$!
# --- Wait for gateway, then open browser ---
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
TOKEN=$("$NODE_BIN" -e "try{const c=JSON.parse(require('fs').readFileSync('$CONFIG_FILE','utf8'));console.log(c.gateway?.auth?.token||'uclaw')}catch(e){console.log('uclaw')}" 2>/dev/null)
open "http://127.0.0.1:$PORT/#token=$TOKEN" 2>/dev/null || true
break
fi
done
TOKEN=$("$NODE_BIN" -e "try{const c=JSON.parse(require('fs').readFileSync('$CONFIG_FILE','utf8'));console.log(c.gateway?.auth?.token||'uclaw')}catch(e){console.log('uclaw')}" 2>/dev/null)
echo -e " ${GREEN}════════════════════════════════${NC}"
echo -e " ${GREEN}🦞 U-Claw 正在运行!${NC}"
echo -e " ${GREEN} 地址: http://127.0.0.1:$PORT/#token=$TOKEN${NC}"
echo ""
echo -e " ${YELLOW}按 Ctrl+C 停止服务${NC}"
echo -e " ${GREEN}════════════════════════════════${NC}"
echo ""
trap "kill $GW_PID 2>/dev/null; echo ''; echo ' 🦞 U-Claw 已停止'; exit 0" INT TERM
wait $GW_PID