All checks were successful
Tests / test (push) Successful in 1m8s
Sync install scripts and CI container image with NODE_VERSION, regenerate Electron Config.html, and skip gitignored portable/app in origin scan. Co-authored-by: Cursor <cursoragent@cursor.com>
258 lines
7.8 KiB
Bash
Executable File
258 lines
7.8 KiB
Bash
Executable File
#!/bin/bash
|
|
# ============================================================
|
|
# U-Claw - Install to Mac (从 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="$(tr -d "[:space:]" < "$UCLAW_DIR/NODE_VERSION" 2>/dev/null || tr -d "[:space:]" < "$UCLAW_DIR/../NODE_VERSION" 2>/dev/null || echo v22.22.3)"
|
|
MIRROR="https://registry.npmjs.org"
|
|
NODE_MIRROR="https://nodejs.org/dist"
|
|
|
|
clear
|
|
echo ""
|
|
echo -e "${CYAN}${BOLD}"
|
|
echo " ╔══════════════════════════════════════╗"
|
|
echo " ║ Install U-Claw on this Mac ║"
|
|
echo " ║ Offline, from the drive ║"
|
|
echo " ╚══════════════════════════════════════╝"
|
|
echo -e "${NC}"
|
|
echo ""
|
|
|
|
# ---- Check CPU ----
|
|
ARCH=$(uname -m)
|
|
if [ "$ARCH" = "arm64" ]; then
|
|
echo -e " ${GREEN}Apple Silicon${NC}"
|
|
NODE_PLATFORM="node-mac-arm64"
|
|
else
|
|
echo -e " ${YELLOW}Intel Mac${NC}"
|
|
NODE_PLATFORM="node-mac-x64"
|
|
fi
|
|
echo ""
|
|
|
|
# ---- Check existing installation ----
|
|
if [ -d "$INSTALL_TARGET" ]; then
|
|
echo -e " ${YELLOW}Already installed at $INSTALL_TARGET${NC}"
|
|
read -p " Install over it? (y/n): " -n 1 OVERWRITE
|
|
echo ""
|
|
if [ "$OVERWRITE" != "y" ] && [ "$OVERWRITE" != "Y" ]; then
|
|
echo -e " ${DIM}Cancelled${NC}"
|
|
exit 0
|
|
fi
|
|
echo ""
|
|
fi
|
|
|
|
# ---- Step 1: Check environment ----
|
|
echo -e " ${BOLD}[1/4] Checking${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: using the copy on the drive ($("$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: using the system copy ($SYS_VER)${NC}"
|
|
USE_NODE="system"
|
|
else
|
|
echo -e " ${YELLOW}Node.js: the system copy is too old ($SYS_VER); v20 or newer is needed${NC}"
|
|
NEED_DOWNLOAD_NODE=true
|
|
USE_NODE="download"
|
|
fi
|
|
else
|
|
echo -e " ${YELLOW}Node.js: not installed${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: using the copy on the drive${NC}"
|
|
USE_OPENCLAW="usb"
|
|
else
|
|
echo -e " ${YELLOW}OpenClaw: not on the drive, will download it${NC}"
|
|
NEED_DOWNLOAD_OPENCLAW=true
|
|
USE_OPENCLAW="download"
|
|
fi
|
|
|
|
echo ""
|
|
|
|
# ---- Step 2: Create install directory ----
|
|
echo -e " ${BOLD}[2/4] Installing to $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] Installing Node.js${NC}"
|
|
|
|
NODE_INSTALL_DIR="$INSTALL_TARGET/runtime/$NODE_PLATFORM"
|
|
|
|
case $USE_NODE in
|
|
usb)
|
|
echo -e " ${CYAN}Copying Node.js from the drive${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 installed${NC}"
|
|
;;
|
|
system)
|
|
INSTALL_NODE="$(which node)"
|
|
INSTALL_NPM="$(which npm)"
|
|
echo -e " ${GREEN}Using the system Node.js${NC}"
|
|
;;
|
|
download)
|
|
echo -e " ${CYAN}Downloading Node.js $NODE_VER...${NC}"
|
|
PLATFORM_NAME="darwin-$ARCH"
|
|
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 installed${NC}"
|
|
;;
|
|
esac
|
|
|
|
echo ""
|
|
|
|
# ---- Step 4: Copy/Download OpenClaw ----
|
|
echo -e " ${BOLD}[4/4] Installing OpenClaw${NC}"
|
|
|
|
CORE_INSTALL_DIR="$INSTALL_TARGET/core"
|
|
|
|
case $USE_OPENCLAW in
|
|
usb)
|
|
echo -e " ${CYAN}Copying OpenClaw and its plugins from the drive${NC}"
|
|
mkdir -p "$CORE_INSTALL_DIR"
|
|
cp -R "$APP_DIR/core/"* "$CORE_INSTALL_DIR/"
|
|
echo -e " ${GREEN}OpenClaw installed${NC}"
|
|
;;
|
|
download)
|
|
echo -e " ${CYAN}Downloading 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 installed${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 launch scripts ----
|
|
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.command" << 'STARTEOF'
|
|
#!/bin/bash
|
|
DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
ARCH=$(uname -m)
|
|
NODE_PLATFORM="node-mac-$( [ "$ARCH" = "arm64" ] && echo "arm64" || echo "x64" )"
|
|
|
|
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 lsof -i :$PORT >/dev/null 2>&1; 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
|
|
open "http://127.0.0.1:$PORT/#token=uclaw"
|
|
break
|
|
fi
|
|
done
|
|
|
|
wait $PID
|
|
STARTEOF
|
|
chmod +x "$INSTALL_TARGET/start.command"
|
|
|
|
echo ""
|
|
|
|
# ---- Summary ----
|
|
INSTALL_SIZE=$(du -sh "$INSTALL_TARGET" | cut -f1)
|
|
|
|
echo -e " ${GREEN}${BOLD}╔══════════════════════════════════════╗"
|
|
echo -e " ║ Installed. ║"
|
|
echo -e " ╚══════════════════════════════════════╝${NC}"
|
|
echo ""
|
|
echo -e " ${BOLD}Location:${NC} $INSTALL_TARGET"
|
|
echo -e " ${BOLD}Size:${NC} $INSTALL_SIZE"
|
|
echo ""
|
|
echo -e " ${BOLD}To start it:${NC}"
|
|
echo -e " Double-click ${CYAN}$INSTALL_TARGET/start.command${NC}"
|
|
echo -e " or run: ${CYAN}bash ~/.uclaw/start.command${NC}"
|
|
echo ""
|
|
echo -e " ${BOLD}First time:${NC}"
|
|
echo -e " Settings opens in your browser by itself"
|
|
echo -e " Paste an API key and you are ready"
|
|
echo ""
|
|
read -p " Press Enter to close…"
|