Files
u-claw/portable/advanced/Mac-Install.command
zheng 08f22edf44
All checks were successful
Tests / test (push) Successful in 1m8s
fix(ci): align Node v22.22.3 pins and unblock pre-push tests
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>
2026-08-18 18:47:41 +08:00

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…"