feat: 海外化改造(新加坡市场)—— 阶段 0-3
Some checks failed
Tests / test (push) Has been cancelled

按 U-Claw-海外化改造方案.md 与 范围决策记录.md 实施。这是 fork,不回上游:
海外版删掉的正是上游的中国市场默认值。

阶段 0 地基
- 下载源全部改国际:脚本/CI 61 处 + lockfile 880 条 npmmirror URL 归零
  (lockfile 那 880 条是 npm 的 resolved 字段,脚本层参数化根本绕不过它)
- 移除 install.ps1 里三个第三方 GitHub 加速代理,bundle 改直连 + SHA256 校验
  (原来只检查"文件大于 1MB"就解压运行)
- 技能内容与分发分离:skills/manifest.json 单一来源,install.sh 1170→658 行、
  install.ps1 721→546 行,两者技能内容归零
  实测原来是三份不一致:skills-cn 完整、install.sh 约 40%、install.ps1 约 17%,
  且 7 个通用技能只有 U 盘版有 —— 一键安装的用户一个能用的技能都没有
- Node 版本三种(v22.14/16/22.1)统一,新建 NODE_VERSION 单一来源
- Config 页三份合一。portable/Config.html 用根相对路径调 API 却只从 file:// 打开,
  保存功能已静默失效两个月;现缩为 120 行重定向壳
- 测试接入 CI(此前 node --test 无人运行,所有断言形同虚设)

阶段 1 双语可用
- 浏览器侧 i18n:JSON 为源、生成经典 script(file:// 下 fetch 本地 JSON 被拦)
  语言跟盘走不跟机器走:启动器写 data/.openclaw/locale.js
- 8 处硬编码 lang="zh-CN" 归零,data-i18n 覆盖 213 处,词条 en/zh 各 279 条
- B3 单框 Key:12 张模型卡 → 一个输入框,前缀识别 provider,
  服务端 /api/test-key 发 1-token 请求实测,错误映射成人话
  Key 填错到得知:从"直到对话失败"降到 ≤1 秒
- 区域格式 SG:DD/MM/YYYY、12 小时、S$、Asia/Singapore
  (ICU 在 en-SG 下把 SGD 渲染成裸 $,与美元无法区分,故自行拼 S$)
- README 内容分叉而非翻译,§1.3 证据清单逐条清零

阶段 2 降门槛
- 启动逻辑上移 lib/start.mjs:Windows-Start.bat 220→28 行、
  Mac-Start.command 235→33 行
  修掉 Mac 侧两个 bug:控制台端口硬编码 18788(回落时打开死页)、
  微信插件从未在 Mac 上安装
- U 盘根目录 23 → 3 个可点文件,其余进 advanced/
- 首启向导:语言 → 用途(7 角色,manifest 驱动)→ 密钥,答过不再问
- 三档界面,Simple 档隐藏一切技术名词
- 自动自愈:启动失败先自查自修,修不好导出脱敏诊断包
  (Doctor 从"用户要知道去点的工具"变成后台机制)

阶段 3 技能库
- 19 个英文技能,planned 归零。sg-weather / sg-transport 的端点均实测过
- SkillHub 从 56 张手写第三方卡片改为 manifest 生成:703→125 行,中文归零

其他
- origin.json 收拢所有运行时地址,tests/origin.test.mjs 保证迁移不会漏
- portable/ 下用户可见中文归零(由断言保证)
- 82 项测试

未验证(本机无 Windows / 无 pwsh):
- install.ps1、setup.ps1 约 210 行改动从未经 PowerShell 解析器
- 完整启动路径仅在假 node + 假 openclaw 上冒烟
- 8 个 .bat 的盘根推导仅静态断言
详见 U盘实测清单.md

受阻:
- 隐藏黑窗口 —— 需代码签名证书(.vbs 已被 Windows 弃用,替代方案都要签名)
- 场景卡 —— OpenClaw 上游 Dashboard 无预填 prompt 接口
- 官网 36 条 —— 上游 2026-04-14 拆到私有仓库,无权限
This commit is contained in:
2026-08-17 18:37:49 +08:00
parent 9a23aec097
commit b076815171
123 changed files with 13396 additions and 14723 deletions

View File

@@ -0,0 +1,120 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>U-Claw Settings</title>
<!--
This page used to be a second, hand-maintained copy of the settings UI (748
lines) alongside config-server/public/index.html. It could never actually work:
it called the API through a root-relative path, and from a file:// origin that
resolves to a file:/// URL, which browsers refuse outright. Anyone who
double-clicked it — the route Read me first.html and the README both point at — got a
form whose Save button failed silently.
config-server/public/index.html is now the only implementation. This file is a
redirect to it, so the old entry point keeps working.
-->
<style>
:root { color-scheme: light dark; }
body {
margin: 0; min-height: 100vh;
display: flex; align-items: center; justify-content: center;
font-family: -apple-system, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
background: #f5f6f8; color: #1a1d21;
}
@media (prefers-color-scheme: dark) { body { background: #16181c; color: #e8eaed; } }
.card {
max-width: 30rem; padding: 2.5rem 2rem; text-align: center;
background: #fff; border-radius: 14px; box-shadow: 0 10px 40px rgba(0,0,0,.08);
}
@media (prefers-color-scheme: dark) { .card { background: #22252a; box-shadow: none; } }
.mark { font-size: 2.5rem; }
h1 { font-size: 1.15rem; margin: .75rem 0 .5rem; font-weight: 600; }
p { margin: .5rem 0; line-height: 1.6; color: #5b6169; }
@media (prefers-color-scheme: dark) { p { color: #a8adb5; } }
.action { margin-top: 1.5rem; font-weight: 600; }
code {
padding: .15em .45em; border-radius: 5px; font-size: .9em;
background: rgba(127,127,127,.15);
}
.hidden { display: none; }
</style>
</head>
<body>
<div class="card">
<div class="mark">🦞</div>
<div id="searching">
<h1 data-msg="looking">Opening settings…</h1>
</div>
<div id="offline" class="hidden">
<h1 data-msg="what">U-Claw is not running yet.</h1>
<p data-msg="why">The settings page lives inside U-Claw, so it has to be started first.</p>
<p class="action" data-msg="action">Start U-Claw, then open this page again.</p>
<p><code data-msg="how"></code></p>
</div>
</div>
<script>
// The config server binds the first free port in this range and can fall back
// when 18788 is taken, so probe rather than assume. no-cors keeps this working
// from a file:// origin: the response is opaque, but it still resolves when a
// server answered and rejects when nothing is listening.
const PORT_FROM = 18788;
const PORT_TO = 18798;
const MESSAGES = {
en: {
looking: 'Opening settings…',
what: 'U-Claw is not running yet.',
why: 'The settings page lives inside U-Claw, so it has to be started first.',
action: 'Start U-Claw, then open this page again.',
how: navigator.userAgent.includes('Windows') ? 'Double-click START HERE - Windows.bat' : 'Double-click START HERE - Mac.command',
},
'zh-CN': {
looking: '正在打开设置…',
what: 'U-Claw 还没有启动。',
why: '设置页面是 U-Claw 的一部分,需要先把它启动起来。',
action: '先启动 U-Claw然后重新打开这个页面。',
how: navigator.userAgent.includes('Windows') ? '双击 START HERE - Windows.bat' : '双击 START HERE - Mac.command',
},
};
const locale = String(navigator.language || 'en').toLowerCase().startsWith('zh') ? 'zh-CN' : 'en';
document.documentElement.lang = locale;
function render() {
const strings = MESSAGES[locale];
for (const node of document.querySelectorAll('[data-msg]')) {
node.textContent = strings[node.dataset.msg];
}
}
async function reachable(port) {
try {
await fetch(`http://127.0.0.1:${port}/`, { mode: 'no-cors', cache: 'no-store' });
return true;
} catch {
return false;
}
}
async function findServer() {
for (let port = PORT_FROM; port <= PORT_TO; port++) {
if (await reachable(port)) return port;
}
return null;
}
render();
findServer().then((port) => {
if (port) {
location.replace(`http://127.0.0.1:${port}/`);
return;
}
document.getElementById('searching').classList.add('hidden');
document.getElementById('offline').classList.remove('hidden');
});
</script>
</body>
</html>

View File

@@ -0,0 +1,141 @@
#!/bin/bash
# ============================================================
# U-Claw Diagnostic Tool - macOS
# 诊断工具
# ============================================================
UCLAW_DIR="$(cd "$(dirname "$0")/.." && pwd)"
LOG_FILE="$UCLAW_DIR/diagnostic-log.txt"
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m'
clear
echo ""
echo " ========================================"
echo " U-Claw Diagnostic Tool"
echo " Check-up"
echo " ========================================"
echo ""
echo " Checking this computer…"
echo ""
# Clear old log
cat > "$LOG_FILE" << EOF
U-Claw Diagnostic Report
Generated: $(date)
========================================
EOF
# 1. Check Node.js
echo "[1/6] Checking Node.js runtime..."
ARCH=$(uname -m)
if [ "$ARCH" = "arm64" ]; then
NODE_BIN="$UCLAW_DIR/app/runtime/node-mac-arm64/bin/node"
else
NODE_BIN="$UCLAW_DIR/app/runtime/node-mac-x64/bin/node"
fi
if [ -f "$NODE_BIN" ]; then
echo " [OK] Node.js found" >> "$LOG_FILE"
echo " Version: $($NODE_BIN --version)" >> "$LOG_FILE"
echo -e " ${GREEN}✓${NC} Node.js: Found"
else
echo " [ERROR] Node.js not found" >> "$LOG_FILE"
echo " Path: $NODE_BIN" >> "$LOG_FILE"
echo -e " ${RED}✗${NC} Node.js: NOT FOUND"
fi
# Migration shim: rename old core-mac to core for existing USB users
if [ -d "$UCLAW_DIR/app/core-mac" ] && [ ! -d "$UCLAW_DIR/app/core" ]; then
mv "$UCLAW_DIR/app/core-mac" "$UCLAW_DIR/app/core"
fi
# 2. Check core directory
echo "[2/6] Checking core directory..."
CORE_DIR="$UCLAW_DIR/app/core"
if [ -d "$CORE_DIR" ]; then
echo " [OK] core directory exists" >> "$LOG_FILE"
echo -e " ${GREEN}✓${NC} core: Found"
else
echo " [ERROR] core directory not found" >> "$LOG_FILE"
echo -e " ${RED}✗${NC} core: NOT FOUND"
fi
# 3. Check node_modules
echo "[3/6] Checking dependencies..."
if [ -d "$CORE_DIR/node_modules" ]; then
echo " [OK] node_modules exists" >> "$LOG_FILE"
echo -e " ${GREEN}✓${NC} Dependencies: Found"
else
echo " [ERROR] node_modules not found" >> "$LOG_FILE"
echo -e " ${RED}✗${NC} Dependencies: NOT FOUND"
fi
# 4. Check OpenClaw
echo "[4/6] Checking OpenClaw..."
OPENCLAW_MJS="$CORE_DIR/node_modules/openclaw/openclaw.mjs"
if [ -f "$OPENCLAW_MJS" ]; then
echo " [OK] openclaw.mjs found" >> "$LOG_FILE"
echo -e " ${GREEN}✓${NC} OpenClaw: Found"
else
echo " [ERROR] openclaw.mjs not found" >> "$LOG_FILE"
echo " Path: $OPENCLAW_MJS" >> "$LOG_FILE"
echo -e " ${RED}✗${NC} OpenClaw: NOT FOUND"
fi
# 5. Check port availability
echo "[5/6] Checking port 18789..."
if lsof -i:18789 >/dev/null 2>&1; then
echo " [WARNING] Port 18789 is in use" >> "$LOG_FILE"
lsof -i:18789 >> "$LOG_FILE" 2>&1
echo -e " ${YELLOW}⚠${NC} Port 18789: IN USE"
else
echo " [OK] Port 18789 is available" >> "$LOG_FILE"
echo -e " ${GREEN}✓${NC} Port 18789: Available"
fi
# 6. Test OpenClaw startup
echo "[6/6] Testing OpenClaw startup..."
echo "" >> "$LOG_FILE"
echo "Testing OpenClaw startup:" >> "$LOG_FILE"
echo "----------------------------------------" >> "$LOG_FILE"
export OPENCLAW_HOME="$UCLAW_DIR/data"
export OPENCLAW_STATE_DIR="$UCLAW_DIR/data/.openclaw"
export OPENCLAW_CONFIG_PATH="$OPENCLAW_STATE_DIR/openclaw.json"
if [ -f "$NODE_BIN" ] && [ -f "$OPENCLAW_MJS" ]; then
cd "$CORE_DIR"
"$NODE_BIN" "$OPENCLAW_MJS" --version >> "$LOG_FILE" 2>&1
if [ $? -eq 0 ]; then
echo -e " ${GREEN}✓${NC} OpenClaw: Can run"
else
echo -e " ${RED}✗${NC} OpenClaw: Failed to run"
echo " [ERROR] OpenClaw failed to start" >> "$LOG_FILE"
fi
else
echo -e " ${RED}✗${NC} Cannot test - files missing"
echo " [SKIP] Cannot test - required files missing" >> "$LOG_FILE"
fi
echo ""
echo "" >> "$LOG_FILE"
echo "========================================" >> "$LOG_FILE"
echo "Diagnostic complete." >> "$LOG_FILE"
echo " ========================================"
echo " Diagnostic Complete"
echo " ========================================"
echo ""
echo " Log saved to: diagnostic-log.txt"
echo ""
echo " Next steps:"
echo " 1. Check diagnostic-log.txt for details"
echo " 2. If errors found, try running START HERE - Mac.command"
echo " to auto-install missing dependencies"
echo ""
read -p " Press Enter to exit..."

View File

@@ -0,0 +1,257 @@
#!/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.1)"
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…"

View File

@@ -0,0 +1,33 @@
#!/bin/bash
# ============================================================
# U-Claw - 内网体检 / Intranet Check (macOS)
# 双击运行代理env + 直连可达 + 真发一条对话
# ============================================================
UCLAW_DIR="$(cd "$(dirname "$0")/.." && pwd)"
APP_DIR="$UCLAW_DIR/app"
CONFIG_FILE="$UCLAW_DIR/data/.openclaw/openclaw.json"
ARCH=$(uname -m)
case "$ARCH" in
arm64) NODE_BIN="$APP_DIR/runtime/node-mac-arm64/bin/node" ;;
x86_64) NODE_BIN="$APP_DIR/runtime/node-mac-x64/bin/node" ;;
*) NODE_BIN="" ;;
esac
# 退而求其次:用系统 node
if [ ! -x "$NODE_BIN" ]; then NODE_BIN="$(command -v node)"; fi
if [ -z "$NODE_BIN" ] || [ ! -x "$NODE_BIN" ]; then
echo " Node.js is missing. Start U-Claw once first, then run this."
read -p " Press Enter to close."
exit 1
fi
# 去掉 macOS 隔离属性,避免 Gatekeeper 拦截
xattr -rd com.apple.quarantine "$UCLAW_DIR" 2>/dev/null || true
"$NODE_BIN" "$UCLAW_DIR/lib/intranet-check.mjs" "$CONFIG_FILE"
echo ""
echo " Screenshot this whole window if you need to send it to someone."
echo ""
read -p " Press Enter to close."

View File

@@ -0,0 +1,29 @@
#!/bin/bash
# ============================================================
# U-Claw - 内网/本地模型一键配置 (macOS)
# 双击运行:命令行配 Ollama / newapi 并当场实测(不碰网页设置)
# ============================================================
UCLAW_DIR="$(cd "$(dirname "$0")/.." && pwd)"
APP_DIR="$UCLAW_DIR/app"
CONFIG_FILE="$UCLAW_DIR/data/.openclaw/openclaw.json"
ARCH=$(uname -m)
case "$ARCH" in
arm64) NODE_BIN="$APP_DIR/runtime/node-mac-arm64/bin/node" ;;
x86_64) NODE_BIN="$APP_DIR/runtime/node-mac-x64/bin/node" ;;
*) NODE_BIN="" ;;
esac
if [ ! -x "$NODE_BIN" ]; then NODE_BIN="$(command -v node)"; fi
if [ -z "$NODE_BIN" ] || [ ! -x "$NODE_BIN" ]; then
echo " Node.js is missing. Start U-Claw once first, then run this."
read -p " Press Enter to close."
exit 1
fi
xattr -rd com.apple.quarantine "$UCLAW_DIR" 2>/dev/null || true
"$NODE_BIN" "$UCLAW_DIR/lib/setup-local-model.mjs" "$CONFIG_FILE"
echo ""
read -p " Press Enter to close."

View File

@@ -0,0 +1,306 @@
#!/bin/bash
# U-Claw Menu - Portable AI Agent
# macOS version
UCLAW_DIR="$(cd "$(dirname "$0")/.." && pwd)"
APP_DIR="$UCLAW_DIR/app"
# Migration shim: rename old core-mac to core for existing USB users
if [ -d "$APP_DIR/core-mac" ] && [ ! -d "$APP_DIR/core" ]; then
mv "$APP_DIR/core-mac" "$APP_DIR/core"
fi
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 — detect architecture
ARCH=$(uname -m)
if [ "$ARCH" = "arm64" ]; then
NODE_DIR="$APP_DIR/runtime/node-mac-arm64"
else
NODE_DIR="$APP_DIR/runtime/node-mac-x64"
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"
# Remove macOS quarantine
if xattr -l "$NODE_BIN" 2>/dev/null | grep -q "com.apple.quarantine"; then
xattr -rd com.apple.quarantine "$UCLAW_DIR" 2>/dev/null || true
fi
# 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}not set up${NC}"
[ -f "$CONFIG_PATH" ] && CFG_STATUS="${GREEN}ready${NC}"
echo ""
echo -e " ${CYAN}${BOLD}╔══════════════════════════════════════╗"
echo -e " ║ U-Claw v1.1 ║"
echo -e " ║ Portable AI Agent ║"
echo -e " ╚══════════════════════════════════════╝${NC}"
echo ""
echo -e " Node: ${GREEN}${NODE_VER}${NC} Model: ${CFG_STATUS}"
echo ""
echo -e " ${WHITE}${BOLD}-- Setup ---------------------------------${NC}"
echo -e " ${GREEN}[1]${NC} Set up a model and paste your key"
echo -e " ${GREEN}[2]${NC} Open Settings in the browser"
echo ""
echo -e " ${WHITE}${BOLD}-- Chat apps -----------------------------${NC}"
echo -e " ${GREEN}[3]${NC} Connect WhatsApp / Telegram / Slack / Discord"
echo -e " ${GREEN}[4]${NC} Connect a chat app used in China"
echo ""
echo -e " ${WHITE}${BOLD}-- Maintenance ---------------------------${NC}"
echo -e " ${GREEN}[5]${NC} Check and fix problems"
echo -e " ${GREEN}[6]${NC} Back up your settings"
echo -e " ${GREEN}[7]${NC} Restore a backup"
echo -e " ${GREEN}[8]${NC} System information"
echo ""
echo -e " ${WHITE}${BOLD}-- Advanced ------------------------------${NC}"
echo -e " ${GREEN}[9]${NC} Stop leftover processes"
echo -e " ${GREEN}[10]${NC} View logs"
echo -e " ${GREEN}[11]${NC} Reset to factory settings"
echo -e " ${GREEN}[12]${NC} Uninstall"
echo -e " ${GREEN}[13]${NC} Check for updates"
echo -e " ${GREEN}[14]${NC} Free up space"
echo -e " ${GREEN}[15]${NC} Plugins"
echo ""
echo -e " ${DIM}[0] Quit${NC}"
echo ""
}
# [1] Config wizard
do_config() {
echo ""
echo -e " ${CYAN}${BOLD}--- Set up a model ---${NC}"
echo ""
echo -e " ${WHITE}Picking a provider in the wizard:${NC}"
echo ""
echo -e " DeepSeek → choose Custom Provider"
echo -e " URL: https://api.deepseek.com/v1"
echo -e " model: deepseek-v4-flash"
echo -e " Kimi → choose Moonshot AI"
echo -e " Qwen -> choose Qwen"
echo -e " Doubao -> choose Volcano Engine"
echo ""
read -p " Press Enter to start the wizard…"
run_oc onboard
}
# [2] Web dashboard
do_dashboard() {
echo ""
echo -e " ${CYAN}Opening Settings…${NC}"
echo ""
cd "$CORE_DIR"
# Find free port
local PORT=18789
while lsof -i :$PORT >/dev/null 2>&1; do
PORT=$((PORT + 1))
if [ $PORT -gt 18799 ]; then
echo -e " ${RED}Ports 18789-18799 are all in use — U-Claw may already be running${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}Settings: $URL${NC}"
open "$URL" 2>/dev/null
break
fi
done
echo " Closing this window stops U-Claw"
wait $PID
}
# [3] QQ Bot (pre-installed)
do_qq() {
echo ""
echo -e " ${CYAN}${BOLD}--- Connect a QQ bot ---${NC}"
echo ""
echo -e " ${GREEN}The QQ plugin is already installed. You only need the AppID and AppSecret.${NC}"
echo ""
echo " Get them at q.qq.com by creating a bot"
echo ""
read -p " AppID: " QQ_ID
read -p " AppSecret: " QQ_SECRET
echo ""
if [ -z "$QQ_ID" ] || [ -z "$QQ_SECRET" ]; then
echo -e " ${YELLOW}Cancelled${NC}"
return
fi
run_oc channels add --channel qqbot --token "${QQ_ID}:${QQ_SECRET}" 2>&1 || true
echo ""
read -p " Your QQ number, to allow only you (blank to skip): " QQ_ALLOW
if [ -n "$QQ_ALLOW" ]; then
run_oc config set channels.qqbot.allowFrom "\"${QQ_ALLOW}\"" 2>&1 || true
echo -e " ${GREEN}Allow-list set${NC}"
fi
echo ""
echo -e " ${GREEN}QQ bot configured. Restart U-Claw for it to take effect.${NC}"
}
# [4] Other platforms
do_platforms() {
echo ""
echo -e " ${CYAN}${BOLD}--- Other chat apps ---${NC}"
echo ""
echo -e " ${GREEN}[a]${NC} Feishu / Lark"
echo -e " ${GREEN}[b]${NC} Telegram"
echo -e " ${GREEN}[c]${NC} WeChat (community plugin)"
echo -e " ${GREEN}[d]${NC} Discord"
echo ""
read -p " Choose (a-d): " -n 1 CH
echo ""
echo ""
case $CH in
a) echo " Feishu: create an app at open.feishu.cn/app" ;;
b) echo " Telegram: create a bot with @BotFather" ;;
c)
echo -e " ${YELLOW}Installing the WeChat plugin…${NC}"
run_oc plugins install @icesword760/openclaw-wechat 2>&1 || true
;;
d) echo " Discord: create an app at discord.com/developers/applications" ;;
*) echo " Not a valid choice" ;;
esac
echo ""
echo " Then run [1] to finish connecting it"
}
# [5] Doctor
do_doctor() {
echo ""
echo -e " ${CYAN}━━━ Check and fix problems ━━━${NC}"
echo ""
run_oc doctor --repair 2>&1 || echo -e " ${YELLOW}The check could not run${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}Backed up to $BK${NC}"
echo " Size: $(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}No backups yet${NC}"
return
fi
echo " Available backups:"
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 " Which one? " 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} + settings restored${NC}"
[ -d "$b/memory" ] && cp -R "$b/memory" "$DATA_DIR/" && echo -e " ${GREEN} + memory restored${NC}"
echo -e " ${GREEN}Restored${NC}"
return
fi
j=$((j+1))
done
echo -e " ${RED}Not a valid choice${NC}"
}
# [8] System info
do_sysinfo() {
echo ""
echo " System: $(sw_vers -productName 2>/dev/null) $(sw_vers -productVersion 2>/dev/null)"
echo " CPU: $(uname -m)"
echo " Memory: $(sysctl -n hw.memsize 2>/dev/null | awk '{printf "%.0f GB", $1/1024/1024/1024}')"
echo " Node: $("$NODE_BIN" --version 2>/dev/null)"
echo " Path: $UCLAW_DIR"
echo " Size: $(du -sh "$UCLAW_DIR" 2>/dev/null | cut -f1)"
echo " Disk: $(df -h "$UCLAW_DIR" | tail -1 | awk '{print $4 " free"}')"
}
# Main loop
while true; do
show_menu
read -p " Choose [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}Bye.${NC}"; exit 0 ;;
*) echo -e " ${RED}Not a valid choice${NC}" ;;
esac
echo ""
read -p " Press Enter to go back…"
done

View File

@@ -0,0 +1,63 @@
#!/bin/bash
# ============================================================
# U-Claw - Interactive CLI (macOS)
# 进阶用户:双击打开一个配置好环境的终端,可直接敲 openclaw 命令。
# 复用与 START HERE - Mac.command 一致的便携环境(盘内 Node + 盘内数据)。
# ============================================================
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"
RED='\033[0;31m'; GREEN='\033[0;32m'; CYAN='\033[0;36m'; NC='\033[0m'
# Detect CPU & set runtime与 START HERE - Mac.command 一致)
ARCH=$(uname -m)
if [ "$ARCH" = "arm64" ]; then
NODE_DIR="$APP_DIR/runtime/node-mac-arm64"
elif [ "$ARCH" = "x86_64" ]; then
NODE_DIR="$APP_DIR/runtime/node-mac-x64"
else
echo -e " ${RED}Unsupported architecture: $ARCH${NC}"
read -p " Press Enter to exit..."
exit 1
fi
NODE_BIN="$NODE_DIR/bin/node"
if [ ! -f "$NODE_BIN" ]; then
echo -e " ${RED}Node.js runtime not found.${NC} Run START HERE - Mac.command once first."
read -p " Press Enter to exit..."
exit 1
fi
if [ ! -x "$CORE_DIR/node_modules/.bin/openclaw" ]; then
echo -e " ${RED}OpenClaw not installed yet.${NC} Run START HERE - Mac.command once first."
read -p " Press Enter to exit..."
exit 1
fi
export OPENCLAW_HOME="$DATA_DIR"
export OPENCLAW_STATE_DIR="$STATE_DIR"
export OPENCLAW_CONFIG_PATH="$STATE_DIR/openclaw.json"
# 盘内 node 和 .bin 放到 PATH 最前,让 openclaw 命令可直接调用
export PATH="$NODE_DIR/bin:$CORE_DIR/node_modules/.bin:$PATH"
echo ""
echo -e "${CYAN}========================================${NC}"
echo -e "${CYAN} U-Claw Interactive CLI${NC}"
echo -e "${CYAN}========================================${NC}"
echo ""
echo " You can now run the 'openclaw' command directly. Examples:"
echo " openclaw --help Show all commands"
echo " openclaw chat Open the terminal chat UI"
echo " openclaw configure Interactive setup (models, channels)"
echo " openclaw doctor Diagnose and repair"
echo " openclaw gateway status Check the running gateway"
echo ""
echo -e " Type ${GREEN}exit${NC} to close."
echo -e "${CYAN}========================================${NC}"
echo ""
# 开一个继承上述环境的交互 shell
exec "$SHELL" -i

View File

@@ -0,0 +1,52 @@
#!/bin/bash
# ============================================================
# U-Claw - OpenClaw Doctor (官方完整体检, 进阶, 英文)
# 注意:先启动 U-Claw 再跑本工具,否则 doctor 会卡在探测未启动的 gateway 上。
# 只读:故意不传 --fix/--repair/--force。卡住可按 Ctrl+C 安全中断。
# ============================================================
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"
OPENCLAW_MJS="$CORE_DIR/node_modules/openclaw/openclaw.mjs"
ARCH=$(uname -m)
case "$ARCH" in
arm64) NODE_BIN="$APP_DIR/runtime/node-mac-arm64/bin/node" ;;
x86_64) NODE_BIN="$APP_DIR/runtime/node-mac-x64/bin/node" ;;
*) NODE_BIN="" ;;
esac
if [ ! -x "$NODE_BIN" ]; then NODE_BIN="$(command -v node)"; fi
if [ -z "$NODE_BIN" ] || [ ! -x "$NODE_BIN" ]; then
echo " Node.js is missing from this drive."
read -p " Press Enter to close."
exit 1
fi
if [ ! -f "$OPENCLAW_MJS" ]; then
echo " OpenClaw is missing from this drive."
read -p " Press Enter to close."
exit 1
fi
export OPENCLAW_HOME="$DATA_DIR"
export OPENCLAW_STATE_DIR="$STATE_DIR"
export OPENCLAW_CONFIG_PATH="$STATE_DIR/openclaw.json"
export OPENCLAW_DISABLE_BONJOUR=1
xattr -rd com.apple.quarantine "$UCLAW_DIR" 2>/dev/null || true
echo ""
echo " ========================================"
echo " OpenClaw Doctor — the upstream check. Thorough, and slow."
echo " ========================================"
echo " U-Claw needs to be running. Ctrl+C is safe at any point — this only reads."
echo " For a network-focused check, use Mac-IntranetFix.command instead."
echo ""
read -p " Press Enter to start."
"$NODE_BIN" "$OPENCLAW_MJS" doctor --non-interactive
echo ""
read -p " Press Enter to close."

View File

@@ -0,0 +1,53 @@
@echo off
chcp 65001 >nul 2>&1
title U-Claw Interactive CLI
REM 进阶用户:打开一个已配置好环境变量的命令行,可直接敲 openclaw 命令。
REM 复用与 START HERE - Windows.bat 一致的便携环境(盘内 Node + 盘内数据)。
for %%I in ("%~dp0..") do set "UCLAW_DIR=%%~fI\"
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 "OPENCLAW_HOME=%DATA_DIR%"
set "OPENCLAW_STATE_DIR=%DATA_DIR%\.openclaw"
set "OPENCLAW_CONFIG_PATH=%DATA_DIR%\.openclaw\openclaw.json"
set "OPENCLAW_DISABLE_BONJOUR=1"
REM 把盘内 node 和 .bin 放到 PATH 最前,让 openclaw 命令可直接调用
set "PATH=%NODE_DIR%;%CORE_DIR%\node_modules\.bin;%PATH%"
if not exist "%NODE_BIN%" (
echo [ERROR] Node.js runtime not found
echo Please run START HERE - Windows.bat once first to set up the runtime.
pause
exit /b 1
)
if not exist "%CORE_DIR%\node_modules\.bin\openclaw.cmd" (
echo [ERROR] OpenClaw not installed yet
echo Please run START HERE - Windows.bat once first.
pause
exit /b 1
)
echo.
echo ========================================
echo U-Claw Interactive CLI
echo ========================================
echo.
echo You can now run the 'openclaw' command directly. Examples:
echo openclaw --help Show all commands
echo openclaw chat Open the terminal chat UI
echo openclaw configure Interactive setup (models, channels)
echo openclaw doctor Diagnose and repair
echo openclaw gateway status Check the running gateway
echo.
echo Type 'exit' to close this window.
echo ========================================
echo.
cmd /k

View File

@@ -0,0 +1,54 @@
@echo off
chcp 65001 >nul 2>&1
title U-Claw - OpenClaw Doctor (advanced)
setlocal
REM Official OpenClaw health check (English, advanced users).
REM NOTE: run this AFTER U-Claw is already running (double-click START HERE - Windows.bat
REM first), otherwise doctor stalls while probing a gateway that is not up yet.
REM Read-only: we deliberately do NOT pass --fix/--repair/--force.
for %%I in ("%~dp0..") do set "UCLAW_DIR=%%~fI\"
set "APP_DIR=%UCLAW_DIR%app"
set "CORE_DIR=%APP_DIR%\core"
set "DATA_DIR=%UCLAW_DIR%data"
set "STATE_DIR=%DATA_DIR%\.openclaw"
set "NODE_BIN=%APP_DIR%\runtime\node-win-x64\node.exe"
set "OPENCLAW_MJS=%CORE_DIR%\node_modules\openclaw\openclaw.mjs"
set "OPENCLAW_HOME=%DATA_DIR%"
set "OPENCLAW_STATE_DIR=%STATE_DIR%"
set "OPENCLAW_CONFIG_PATH=%STATE_DIR%\openclaw.json"
set "OPENCLAW_DISABLE_BONJOUR=1"
if not exist "%NODE_BIN%" (
echo [ERROR] Node runtime not found. Put this file inside your U-Claw folder.
echo.
pause
exit /b 1
)
if not exist "%OPENCLAW_MJS%" (
echo [ERROR] OpenClaw runtime not found ^(app\core\node_modules\openclaw^).
echo.
pause
exit /b 1
)
echo.
echo ========================================
echo OpenClaw Doctor (official, English)
echo ========================================
echo This is the upstream deep health check. It can take a while and is in
echo English. Make sure U-Claw is ALREADY running first.
echo If it seems stuck, press Ctrl+C to stop - it is safe (read-only).
echo.
pause
"%NODE_BIN%" "%OPENCLAW_MJS%" doctor --non-interactive
echo.
echo ----------------------------------------
echo Doctor finished. For a quick Chinese check of your model connection,
echo use Windows-IntranetFix.bat instead.
echo.
pause

View File

@@ -0,0 +1,35 @@
# advanced/
Everything here used to sit in the drive's root folder. Twenty-three clickable
files greeted anyone who plugged the drive in, and the first thing a
non-technical user had to do was work out which of them applied to their
computer — before anything had even run.
The root now holds three things: the two `START HERE` files and a short guide.
Everything else lives here.
| File | What it is |
|---|---|
| `Windows-Menu.bat` · `Mac-Menu.command` | Menu of every tool below |
| `Windows-Diagnose.bat` · `Mac-Diagnose.command` | Health check, collects logs for a bug report |
| `OpenClaw-Doctor.bat` · `Mac-OpenClaw-Doctor.command` | Read-only diagnosis from OpenClaw itself |
| `OpenClaw-CLI.bat` · `Mac-OpenClaw-CLI.command` | Drop into the raw `openclaw` CLI |
| `Windows-Install.bat` · `Mac-Install.command` | Copy the drive's contents onto this computer |
| `Windows-IntranetFix.bat` · `Mac-IntranetFix.command` | Fix a corporate proxy blocking your model endpoint |
| `Windows-LocalModel.bat` · `Mac-LocalModel.command` | Set up an offline local model |
| `setup.sh` · `setup.bat` · `setup.ps1` | Developer build: fetch Node.js + OpenClaw into `../app/` |
| `Config.html` | Redirect to Settings, kept for older links |
| `U-Claw.html` · `SkillHub.html` | Reference pages |
## If you move a script out of here
They all resolve the drive root as "one level up" from wherever they sit. Running
one from somewhere else will not find `app/` or `data/`.
## Naming
The entry points are `START HERE - Windows.bat` and `START HERE - Mac.command`,
with a hyphen rather than brackets. `cmd.exe` treats `)` as the end of an
`IF (...)` block, so a filename containing brackets breaks every `echo` that
mentions it inside a block — which is the flash-exit class of bug that
`tests/windows-launchers.test.mjs` guards against. The hyphen avoids it entirely.

View File

@@ -0,0 +1,125 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title data-i18n="hub.title">U-Claw Skills</title>
<script src="../data/.openclaw/locale.js"></script>
<script src="../lib/i18n/messages.js"></script>
<script src="../lib/i18n/skills-data.js"></script>
<script src="../lib/i18n/i18n.js"></script>
<!--
What this page used to be: 56 hand-written cards advertising third-party
ClawHub skills, in Chinese, with invented install counts. None of it was ours,
none of it was translated, and the numbers could not be verified.
What it is now: the skills actually on this drive, generated from
skills/manifest.json. Adding a skill to the manifest puts it here — there is
nothing to hand-maintain.
skills-data.js is generated for the same reason messages.js is: this page opens
from file://, where fetching manifest.json is blocked.
-->
<style>
:root { color-scheme: dark; }
* { box-sizing: border-box; }
body {
margin: 0; padding: 3rem 1.5rem 4rem;
font-family: -apple-system, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
background: #16181c; color: #f2f4f7; line-height: 1.6;
}
.wrap { max-width: 60rem; margin: 0 auto; }
h1 { font-size: 1.8rem; margin: 0 0 .5rem; }
.lede { color: #9aa0a8; margin: 0 0 2rem; }
.filters { display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: 2rem; }
.filters button {
padding: .45rem 1rem; font: inherit; font-size: .88rem;
color: #c8ccd2; background: #23262c; border: 1px solid #33373f;
border-radius: 999px; cursor: pointer;
}
.filters button[aria-pressed="true"] { color: #16181c; background: #ff6b35; border-color: #ff6b35; font-weight: 600; }
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); gap: 1rem; }
.skill { padding: 1.1rem; background: #23262c; border: 1px solid #33373f; border-radius: 12px; }
.skill .top { display: flex; align-items: baseline; gap: .6rem; }
.skill .icon { font-size: 1.4rem; }
.skill h3 { margin: 0; font-size: 1rem; }
.skill p { margin: .5rem 0 .75rem; color: #9aa0a8; font-size: .88rem; }
.skill code { font-size: .78rem; color: #6f757d; }
.empty { color: #9aa0a8; padding: 2rem 0; }
footer { margin-top: 3rem; color: #6f757d; font-size: .85rem; }
a { color: #ff6b35; }
</style>
</head>
<body>
<div class="wrap">
<h1 data-i18n="hub.title">U-Claw Skills</h1>
<p class="lede" data-i18n="hub.lede">These are on this drive already. Just ask for what you want — you do not have to name the skill.</p>
<div class="filters" id="filters"></div>
<div class="grid" id="grid"></div>
<p class="empty" id="empty" hidden data-i18n="hub.none">Nothing in this category.</p>
<footer>
<p data-i18n="hub.add_note">Skills live in the skills/ folder on this drive. Dropping a SKILL.md in there adds one.</p>
</footer>
</div>
<script>
(function () {
var t = function (k) { return window.UClawI18n ? window.UClawI18n.t(k) : k; };
var data = window.UCLAW_SKILLS || { skills: [] };
var active = 'all';
function categories() {
var seen = {};
data.skills.forEach(function (s) { (s.categories || []).forEach(function (c) { seen[c] = true; }); });
return Object.keys(seen).sort();
}
function render() {
var grid = document.getElementById('grid');
grid.innerHTML = '';
var shown = data.skills.filter(function (s) {
return active === 'all' || (s.categories || []).indexOf(active) !== -1;
});
shown.sort(function (a, b) { return t('skill.' + a.id + '.name').localeCompare(t('skill.' + b.id + '.name')); });
shown.forEach(function (s) {
var el = document.createElement('div');
el.className = 'skill';
el.innerHTML =
'<div class="top"><span class="icon">' + (s.emoji || '🧩') + '</span>'
+ '<h3></h3></div><p></p><code>' + s.id + '</code>';
// textContent, not innerHTML: catalogue strings are data, not markup.
el.querySelector('h3').textContent = t('skill.' + s.id + '.name');
el.querySelector('p').textContent = t('skill.' + s.id + '.desc');
grid.appendChild(el);
});
document.getElementById('empty').hidden = shown.length > 0;
}
function buildFilters() {
var bar = document.getElementById('filters');
var options = [['all', t('hub.all')]].concat(categories().map(function (c) { return [c, t('cat.' + c)]; }));
options.forEach(function (pair) {
var btn = document.createElement('button');
btn.type = 'button';
btn.textContent = pair[1];
btn.setAttribute('aria-pressed', pair[0] === active ? 'true' : 'false');
btn.addEventListener('click', function () {
active = pair[0];
Array.prototype.forEach.call(bar.children, function (b) { b.setAttribute('aria-pressed', 'false'); });
btn.setAttribute('aria-pressed', 'true');
render();
});
bar.appendChild(btn);
});
}
buildFilters();
render();
})();
</script>
</body>
</html>

View File

@@ -0,0 +1,215 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title data-i18n="nav.title">U-Claw — Start here</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, "Microsoft YaHei", sans-serif; background: #0a0a0a; color: #e0e0e0; min-height: 100vh; }
.container { max-width: 800px; margin: 0 auto; padding: 40px 20px; }
.header { text-align: center; margin-bottom: 40px; }
.header h1 { font-size: 2.5em; margin-bottom: 8px; }
.header h1 span { color: #ff6b35; }
.header p { color: #888; font-size: 1.1em; }
.version { display: inline-block; background: #ff6b35; color: #fff; padding: 2px 10px; border-radius: 12px; font-size: 0.8em; margin-left: 8px; }
.os-tabs { display: flex; justify-content: center; gap: 12px; margin-bottom: 30px; }
.os-tab { padding: 12px 32px; border: 2px solid #333; border-radius: 8px; cursor: pointer; font-size: 1.1em; background: transparent; color: #aaa; transition: all 0.2s; }
.os-tab:hover { border-color: #666; color: #fff; }
.os-tab.active { border-color: #ff6b35; color: #ff6b35; background: rgba(255,107,53,0.1); }
.panel { display: none; }
.panel.active { display: block; }
.card { background: #1a1a1a; border: 1px solid #333; border-radius: 12px; padding: 24px; margin-bottom: 16px; }
.card h3 { color: #ff6b35; margin-bottom: 12px; font-size: 1.2em; }
.card p { color: #bbb; line-height: 1.6; }
.step { display: flex; align-items: flex-start; gap: 16px; margin: 12px 0; }
.step-num { background: #ff6b35; color: #fff; width: 32px; height: 32px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; flex-shrink: 0; }
.step-text { flex: 1; }
.step-text strong { color: #fff; }
.step-text code { background: #2a2a2a; padding: 2px 8px; border-radius: 4px; color: #ff6b35; font-size: 0.95em; }
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 16px; }
.grid-item { background: #222; border: 1px solid #333; border-radius: 8px; padding: 16px; }
.grid-item h4 { color: #fff; margin-bottom: 6px; }
.grid-item p { color: #999; font-size: 0.9em; }
.status-bar { background: #1a1a1a; border: 1px solid #333; border-radius: 8px; padding: 16px; margin-bottom: 24px; display: flex; align-items: center; gap: 12px; }
.status-dot { width: 12px; height: 12px; border-radius: 50%; }
.status-dot.off { background: #666; }
.status-dot.on { background: #4caf50; animation: pulse 2s infinite; }
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.5; } }
.status-link { color: #ff6b35; text-decoration: none; margin-left: auto; }
.status-link:hover { text-decoration: underline; }
.section-title { color: #888; font-size: 0.9em; text-transform: uppercase; letter-spacing: 2px; margin: 30px 0 12px; }
.footer { text-align: center; margin-top: 40px; padding-top: 20px; border-top: 1px solid #222; color: #666; }
.footer a { color: #ff6b35; text-decoration: none; }
</style>
<script src="../data/.openclaw/locale.js"></script>
<script src="../lib/i18n/messages.js"></script>
<script src="../lib/i18n/i18n.js"></script>
</head>
<body>
<div class="container">
<div class="header">
<h1>🦞 <span>U-Claw</span> <span class="version">v1.1</span></h1>
<p data-i18n="nav.tagline">Portable AI agent — plug in and go</p>
</div>
<!-- Status Bar -->
<div class="status-bar">
<div class="status-dot off" id="statusDot"></div>
<span id="statusText" data-i18n="nav.status_off">U-Claw is not running</span>
<a href="#" id="statusLink" class="status-link" style="display:none" target="_blank" data-i18n="nav.open_console">Open it →</a>
</div>
<!-- OS Tabs -->
<div class="os-tabs">
<div class="os-tab active" onclick="switchOS('mac')">🍎 Mac</div>
<div class="os-tab" onclick="switchOS('win')">🪟 Windows</div>
</div>
<!-- Mac Panel -->
<div class="panel active" id="panel-mac">
<div class="card">
<h3>🚀 <span data-i18n="nav.quickstart">Quick start</span></h3>
<div class="step">
<div class="step-num">1</div>
<div class="step-text"><span data-i18n="nav.doubleclick">Double-click</span> <code>START HERE - Mac.command</code></div>
</div>
<div class="step">
<div class="step-num">2</div>
<div class="step-text" data-i18n="nav.step2">Settings opens by itself the first time — pick a model and paste your key.</div>
</div>
<p style="margin-top:12px; color:#999;" data-i18n="nav.mac_gatekeeper">⚠️ If macOS says the developer cannot be verified: right-click the file, then choose Open.</p>
<p style="margin-top:8px;" data-i18n="nav.config_shortcut">📝 You can also double-click Config.html — it takes you to the settings page.</p>
</div>
<div class="card">
<h3>⚙️ <span data-i18n="nav.everything">Everything else</span></h3>
<p><span data-i18n="nav.doubleclick">Double-click</span> <code>Mac-Menu.command</code> <span data-i18n="nav.menu_opens">for the full menu:</span></p>
<div class="grid">
<div class="grid-item"><h4 data-i18n="nav.g_settings">Settings</h4><p data-i18n="nav.g_settings_d">Model, key and chat apps</p></div>
<div class="grid-item"><h4 data-i18n="nav.g_checkup">Check-up</h4><p data-i18n="nav.g_checkup_d">Finds and fixes common problems</p></div>
<div class="grid-item"><h4 data-i18n="nav.g_backup">Backup</h4><p data-i18n="nav.g_backup_d">Save settings and memory in one click</p></div>
<div class="grid-item"><h4 data-i18n="nav.g_skills">Skills</h4><p data-i18n="nav.g_skills_d">Browse what U-Claw can do</p></div>
<div class="grid-item"><h4 data-i18n="nav.g_system">System info</h4><p data-i18n="nav.g_system_d">Version, port, running state</p></div>
<div class="grid-item"><h4 data-i18n="nav.g_cli">Command line</h4><p data-i18n="nav.g_cli_d">For advanced use</p></div>
</div>
</div>
</div>
<!-- Windows Panel -->
<div class="panel" id="panel-win">
<div class="card">
<h3>🚀 <span data-i18n="nav.quickstart">Quick start</span></h3>
<div class="step">
<div class="step-num">1</div>
<div class="step-text"><span data-i18n="nav.doubleclick">Double-click</span> <code>START HERE - Windows.bat</code></div>
</div>
<div class="step">
<div class="step-num">2</div>
<div class="step-text" data-i18n="nav.step2">Settings opens by itself the first time — pick a model and paste your key.</div>
</div>
<p style="margin-top:12px; color:#999;" data-i18n="nav.win_smartscreen">⚠️ If Windows shows a security warning: click More info, then Run anyway.</p>
</div>
<div class="card">
<h3>⚙️ <span data-i18n="nav.everything">Everything else</span></h3>
<p><span data-i18n="nav.doubleclick">Double-click</span> <code>Windows-Menu.bat</code> <span data-i18n="nav.menu_opens">for the full menu:</span></p>
<div class="grid">
<div class="grid-item"><h4 data-i18n="nav.g_settings">Settings</h4><p data-i18n="nav.g_settings_d">Model, key and chat apps</p></div>
<div class="grid-item"><h4 data-i18n="nav.g_checkup">Check-up</h4><p data-i18n="nav.g_checkup_d">Finds and fixes common problems</p></div>
<div class="grid-item"><h4 data-i18n="nav.g_backup">Backup</h4><p data-i18n="nav.g_backup_d">Save settings and memory in one click</p></div>
<div class="grid-item"><h4 data-i18n="nav.g_skills">Skills</h4><p data-i18n="nav.g_skills_d">Browse what U-Claw can do</p></div>
<div class="grid-item"><h4 data-i18n="nav.g_system">System info</h4><p data-i18n="nav.g_system_d">Version, port, running state</p></div>
<div class="grid-item"><h4 data-i18n="nav.g_cli">Command line</h4><p data-i18n="nav.g_cli_d">For advanced use</p></div>
</div>
</div>
</div>
<!-- Common sections -->
<div class="section-title">🤖 <span data-i18n="nav.models_title">Models you can use</span></div>
<div class="card">
<div class="grid">
<div class="grid-item"><h4>Claude</h4><p data-i18n="welcome.model_claude">Best all-rounder</p></div>
<div class="grid-item"><h4>GPT</h4><p data-i18n="welcome.model_gpt">Most widely used</p></div>
<div class="grid-item"><h4>Gemini</h4><p data-i18n="welcome.model_gemini">Free tier, easiest signup</p></div>
<div class="grid-item"><h4>SEA-LION</h4><p data-i18n="nav.model_sealion">Malay, Tamil and Singlish</p></div>
<div class="grid-item"><h4 data-i18n="nav.model_local">Local model</h4><p data-i18n="welcome.model_local">Offline · nothing leaves this machine</p></div>
<div class="grid-item"><h4 data-i18n="nav.model_more">More</h4><p data-i18n="nav.model_more_d">Others are listed in Settings</p></div>
</div>
</div>
<div class="section-title">📖 <span data-i18n="nav.more_title">More</span></div>
<div class="card">
<div class="grid">
<div class="grid-item" onclick="window.open('../Read me first.html')" style="cursor:pointer"><h4>📖 <span data-i18n="nav.link_guide">Getting started</span></h4><p data-i18n="nav.link_guide_d">The three-step walkthrough</p></div>
<div class="grid-item" onclick="window.open('SkillHub.html')" style="cursor:pointer"><h4>🎯 <span data-i18n="nav.link_skills">Skills</span></h4><p data-i18n="nav.link_skills_d">Browse what U-Claw can do</p></div>
<div class="grid-item"><h4>💬 <span data-i18n="nav.link_contact">Contact</span></h4><p><a href="mailto:help@u-claw.org" style="color:#ff6b35">help@u-claw.org</a></p></div>
<div class="grid-item"><h4>🌐 <span data-i18n="nav.link_site">Website</span></h4><p><a href="https://u-claw.org" target="_blank" rel="noopener" style="color:#ff6b35">u-claw.org</a></p></div>
</div>
</div>
<div class="footer">
<p>Made with 🦞 by <a href="https://github.com/dongsheng123132">dongsheng</a> · <a href="https://u-claw.org">u-claw.org</a></p>
</div>
</div>
<script>
// Auto-detect OS
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
if (!isMac) switchOS('win');
function switchOS(os) {
document.querySelectorAll('.os-tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.panel').forEach(p => p.classList.remove('active'));
document.querySelector('#panel-' + os).classList.add('active');
document.querySelectorAll('.os-tab')[os === 'mac' ? 0 : 1].classList.add('active');
}
// Check if gateway is running (scan 18789-18799)
var _gwPort = null;
async function checkStatus() {
var found = null;
if (_gwPort) {
// Fast path: check last known port first
try {
await fetch('http://127.0.0.1:' + _gwPort + '/', { mode: 'no-cors', cache: 'no-cache' });
found = _gwPort;
} catch(e) { _gwPort = null; }
}
if (!found) {
for (var p = 18789; p <= 18799; p++) {
try {
await fetch('http://127.0.0.1:' + p + '/', { mode: 'no-cors', cache: 'no-cache' });
found = p;
_gwPort = p;
break;
} catch(e) {}
}
}
if (found) {
document.getElementById('statusDot').className = 'status-dot on';
document.getElementById('statusText').textContent = window.UClawI18n.t('nav.status_on', { port: found });
var link = document.getElementById('statusLink');
link.style.display = 'inline';
link.href = 'http://127.0.0.1:' + found + '/#token=uclaw';
} else {
document.getElementById('statusDot').className = 'status-dot off';
document.getElementById('statusText').textContent = window.UClawI18n.t('nav.status_off_hint');
document.getElementById('statusLink').style.display = 'none';
}
}
checkStatus();
setInterval(checkStatus, 5000);
</script>
</body>
</html>

View File

@@ -0,0 +1,186 @@
@echo off
setlocal EnableDelayedExpansion
chcp 65001 >nul 2>&1
title U-Claw - Diagnostic Tool
for %%I in ("%~dp0..") do set "UCLAW_DIR=%%~fI\"
set "LOG_FILE=%UCLAW_DIR%diagnostic-log.txt"
echo.
echo ========================================
echo U-Claw Diagnostic Tool
echo ========================================
echo.
echo Checking system...
echo.
REM Clear old log
echo U-Claw Diagnostic Report > "%LOG_FILE%"
echo Generated: %date% %time% >> "%LOG_FILE%"
echo ======================================== >> "%LOG_FILE%"
echo. >> "%LOG_FILE%"
REM System info
echo System Info: >> "%LOG_FILE%"
for /f "tokens=2 delims==" %%v in ('wmic os get Version /format:list 2^>nul ^| findstr "="') do echo Windows Version: %%v >> "%LOG_FILE%"
for /f "tokens=2 delims==" %%v in ('wmic os get OSArchitecture /format:list 2^>nul ^| findstr "="') do echo Architecture: %%v >> "%LOG_FILE%"
echo. >> "%LOG_FILE%"
REM 1. Check Node.js
echo [1/7] Checking Node.js
set "NODE_BIN=%UCLAW_DIR%app\runtime\node-win-x64\node.exe"
set "ERROR_COUNT=0"
if exist "%NODE_BIN%" (
echo [OK] Node.js found >> "%LOG_FILE%"
for /f "tokens=*" %%v in ('"%NODE_BIN%" --version 2^>^&1') do (
echo Version: %%v >> "%LOG_FILE%"
echo [OK] Node.js: %%v
)
) else (
echo [ERROR] Node.js not found >> "%LOG_FILE%"
echo [!!] Node.js: missing
echo Path: %NODE_BIN% >> "%LOG_FILE%"
set /a ERROR_COUNT+=1
)
REM Migration shim: rename old core-win to core for existing USB users
if exist "%UCLAW_DIR%app\core-win" if not exist "%UCLAW_DIR%app\core" ren "%UCLAW_DIR%app\core-win" core
REM 2. Check core directory
echo [2/7] Checking folders
set "CORE_DIR=%UCLAW_DIR%app\core"
if exist "%CORE_DIR%" (
echo [OK] core directory exists >> "%LOG_FILE%"
echo [OK] Folders: ok
) else (
echo [ERROR] core directory not found >> "%LOG_FILE%"
echo [!!] Folders: missing
set /a ERROR_COUNT+=1
)
REM 3. Check node_modules
echo [3/7] Checking dependencies
if exist "%CORE_DIR%\node_modules" (
echo [OK] node_modules exists >> "%LOG_FILE%"
echo [OK] Dependencies: installed
) else (
echo [ERROR] node_modules not found >> "%LOG_FILE%"
echo [!!] Dependencies: not installed
set /a ERROR_COUNT+=1
)
REM 4. Check OpenClaw
echo [4/7] Checking OpenClaw
set "OPENCLAW_MJS=%CORE_DIR%\node_modules\openclaw\openclaw.mjs"
if exist "%OPENCLAW_MJS%" (
echo [OK] openclaw.mjs found >> "%LOG_FILE%"
echo [OK] OpenClaw: ok
) else (
echo [ERROR] openclaw.mjs not found >> "%LOG_FILE%"
echo [!!] OpenClaw: missing
echo Path: %OPENCLAW_MJS% >> "%LOG_FILE%"
set /a ERROR_COUNT+=1
)
REM 5. Check config
echo [5/7] Checking settings
set "STATE_DIR=%UCLAW_DIR%data\.openclaw"
if exist "%STATE_DIR%\openclaw.json" (
echo [OK] Config file exists >> "%LOG_FILE%"
echo [OK] Settings file: ok
REM Check if model is configured
findstr /c:"model" "%STATE_DIR%\openclaw.json" >nul 2>&1
if !errorlevel!==0 (
echo [OK] Model: configured
) else (
echo [ ?] Model: not set up yet
)
) else (
echo [WARNING] Config not found >> "%LOG_FILE%"
echo [ ?] Settings file: not created yet, U-Claw makes it on first run
)
REM 6. Check port availability
echo [6/7] Checking ports
set "PORT_ISSUE=0"
for /l %%p in (18789,1,18799) do (
netstat -an | findstr ":%%p " | findstr "LISTENING" >nul 2>&1
if !errorlevel!==0 (
echo [WARNING] Port %%p is in use >> "%LOG_FILE%"
echo [ ?] Port %%p: in use
for /f "tokens=5" %%a in ('netstat -ano ^| findstr ":%%p " ^| findstr "LISTENING"') do (
echo PID: %%a >> "%LOG_FILE%"
)
set "PORT_ISSUE=1"
)
)
if "!PORT_ISSUE!"=="0" (
echo [OK] Ports 18789-18799 available >> "%LOG_FILE%"
echo [OK] Ports 18789-18799: all free
)
REM 7. Test OpenClaw startup
echo [7/7] Testing OpenClaw
echo. >> "%LOG_FILE%"
echo Testing OpenClaw startup: >> "%LOG_FILE%"
echo ---------------------------------------- >> "%LOG_FILE%"
set "OPENCLAW_HOME=%UCLAW_DIR%data"
set "OPENCLAW_STATE_DIR=%UCLAW_DIR%data\.openclaw"
set "OPENCLAW_CONFIG_PATH=%OPENCLAW_STATE_DIR%\openclaw.json"
if exist "%NODE_BIN%" if exist "%OPENCLAW_MJS%" (
cd /d "%CORE_DIR%"
for /f "tokens=*" %%v in ('"%NODE_BIN%" "%OPENCLAW_MJS%" --version 2^>^&1') do (
echo %%v >> "%LOG_FILE%"
echo [OK] OpenClaw starts: yes (%%v^)
)
if !errorlevel! neq 0 (
echo [!!] OpenClaw starts: no
echo [ERROR] OpenClaw failed to start >> "%LOG_FILE%"
set /a ERROR_COUNT+=1
)
) else (
echo [ ?] OpenClaw starts: skipped, files missing
echo [SKIP] Cannot test - required files missing >> "%LOG_FILE%"
)
REM Disk space check
echo. >> "%LOG_FILE%"
echo Disk Space: >> "%LOG_FILE%"
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 size: %%s MB >> "%LOG_FILE%"
)
echo.
echo. >> "%LOG_FILE%"
echo ======================================== >> "%LOG_FILE%"
echo Diagnostic complete. >> "%LOG_FILE%"
echo Error count: !ERROR_COUNT! >> "%LOG_FILE%"
echo ========================================
echo Check complete
echo ========================================
echo.
if !ERROR_COUNT!==0 (
echo [OK] Everything checks out.
echo Every component U-Claw needs is present.
echo.
echo Next:
echo - Double-click START HERE - Windows.bat to run it
echo - Settings opens by itself the first time
) else (
echo [!!] Found !ERROR_COUNT! problem^(s^).
echo.
echo What to do:
echo 1. Open diagnostic-log.txt for the details
echo 2. Run START HERE - Windows.bat again
echo it reinstalls anything missing by itself
echo 3. Still stuck? See:
echo gitea.fanghe.it.com/zhenghy/u-claw
)
echo.
echo Saved a log to diagnostic-log.txt
echo.
pause

View File

@@ -0,0 +1,229 @@
@echo off
setlocal EnableDelayedExpansion
chcp 65001 >nul 2>&1
title U-Claw - Install to Windows
echo.
echo ========================================
echo Install U-Claw on this PC
echo Offline, from the drive
echo ========================================
echo.
for %%I in ("%~dp0..") do set "UCLAW_DIR=%%~fI\"
set "APP_DIR=%UCLAW_DIR%app"
set "INSTALL_TARGET=%USERPROFILE%\.uclaw"
set "MIRROR=https://registry.npmjs.org"
set "NODE_MIRROR=https://nodejs.org/dist"
set "NODE_VER=v22.22.1"
REM ---- Step 1: Check environment ----
echo [1/4] Checking
set "USE_NODE=none"
set "USB_NODE=%APP_DIR%\runtime\node-win-x64\node.exe"
if exist "%USB_NODE%" (
for /f "tokens=*" %%v in ('"%USB_NODE%" --version') do echo Node.js: using the copy on the drive, %%v
set "USE_NODE=usb"
) else (
where node >nul 2>&1
if !errorlevel!==0 (
for /f "tokens=*" %%v in ('node --version') do (
echo Node.js: checking the system copy, %%v
set "SYS_VER=%%v"
)
REM Use only supported LTS majors. Newer Current releases can break native deps.
for /f "tokens=1 delims=." %%m in ("!SYS_VER:v=!") do set "MAJOR=%%m"
if !MAJOR! equ 20 (
echo Node.js: using the system copy, !SYS_VER!
set "USE_NODE=system"
) else if !MAJOR! equ 22 (
echo Node.js: using the system copy, !SYS_VER!
set "USE_NODE=system"
) else (
echo Node.js: system copy !SYS_VER! is not compatible, using the bundled v22 LTS
set "USE_NODE=download"
)
) else (
echo Node.js: not installed
set "USE_NODE=download"
)
)
set "USB_OPENCLAW=%APP_DIR%\core\node_modules\openclaw\openclaw.mjs"
if exist "%USB_OPENCLAW%" (
echo OpenClaw: using the copy on the drive
set "USE_OPENCLAW=usb"
) else (
echo OpenClaw: not on the drive, will download it
set "USE_OPENCLAW=download"
)
echo.
REM ---- Step 2: Check existing ----
if exist "%INSTALL_TARGET%" (
echo Already installed: %INSTALL_TARGET%
set /p OVERWRITE=" Install over it?(y/n): "
if /i not "!OVERWRITE!"=="y" (
echo Cancelled
pause
exit /b 0
)
echo.
)
REM ---- Step 3: Create directories ----
echo [2/4] Creating folders
mkdir "%INSTALL_TARGET%" 2>nul
mkdir "%INSTALL_TARGET%\data\.openclaw" 2>nul
mkdir "%INSTALL_TARGET%\data\memory" 2>nul
mkdir "%INSTALL_TARGET%\data\backups" 2>nul
mkdir "%INSTALL_TARGET%\data\logs" 2>nul
echo.
REM ---- Step 4: Copy/Download Node.js ----
echo [3/4] Installing Node.js
if "!USE_NODE!"=="usb" (
echo Copying Node.js from the drive
xcopy /s /e /q /y "%APP_DIR%\runtime\node-win-x64" "%INSTALL_TARGET%\runtime\node-win-x64\" >nul
set "INSTALL_NODE=%INSTALL_TARGET%\runtime\node-win-x64\node.exe"
set "INSTALL_NPM=%INSTALL_TARGET%\runtime\node-win-x64\npm.cmd"
echo Node.js Installed!
) else if "!USE_NODE!"=="system" (
set "INSTALL_NODE=node"
set "INSTALL_NPM=npm"
echo Using the system Node.js
) else (
echo Downloading Node.js %NODE_VER%
set "NODE_ZIP=node-%NODE_VER%-win-x64.zip"
set "NODE_URL=%NODE_MIRROR%/%NODE_VER%/!NODE_ZIP!"
echo URL: !NODE_URL!
echo.
REM Download using curl (available on Windows 10+)
curl -# -L "!NODE_URL!" -o "%TEMP%\!NODE_ZIP!"
if !errorlevel! neq 0 (
echo [ERROR] Download failed. Check your network connection.
echo You can also download it yourself: !NODE_URL!
pause
exit /b 1
)
echo Unpacking
mkdir "%INSTALL_TARGET%\runtime\node-win-x64" 2>nul
powershell -command "Expand-Archive -Path '%TEMP%\!NODE_ZIP!' -DestinationPath '%TEMP%\node-extract' -Force"
xcopy /s /e /q /y "%TEMP%\node-extract\node-%NODE_VER%-win-x64\*" "%INSTALL_TARGET%\runtime\node-win-x64\" >nul
rmdir /s /q "%TEMP%\node-extract" 2>nul
del "%TEMP%\!NODE_ZIP!" 2>nul
set "INSTALL_NODE=%INSTALL_TARGET%\runtime\node-win-x64\node.exe"
set "INSTALL_NPM=%INSTALL_TARGET%\runtime\node-win-x64\npm.cmd"
echo Node.js installed
)
echo.
REM ---- Step 5: Copy/Download OpenClaw ----
echo [4/4] Installing OpenClaw
if "!USE_OPENCLAW!"=="usb" (
echo Copying OpenClaw and its plugins from the drive
xcopy /s /e /q /y "%APP_DIR%\core" "%INSTALL_TARGET%\core\" >nul
echo OpenClaw Installed!
) else (
echo Downloading OpenClaw
mkdir "%INSTALL_TARGET%\core" 2>nul
(echo {"name":"u-claw-core","version":"1.0.0","private":true,"dependencies":{"openclaw":"latest"}})>"%INSTALL_TARGET%\core\package.json"
cd /d "%INSTALL_TARGET%\core"
call "!INSTALL_NPM!" install --registry=%MIRROR%
call "!INSTALL_NPM!" install @sliverp/qqbot@latest --registry=%MIRROR%
echo OpenClaw installed
)
set "QQ_DIR=%INSTALL_TARGET%\core\node_modules\@sliverp\qqbot"
if exist "!QQ_DIR!" (
if not exist "!QQ_DIR!\dist\index.js" (
echo Building the QQ plugin
pushd "!QQ_DIR!"
call "!INSTALL_NPM!" install --include=dev --registry=%MIRROR% >nul 2>&1
call "!INSTALL_NPM!" run build >nul 2>&1
call "!INSTALL_NPM!" prune --omit=dev >nul 2>&1
popd
)
if exist "!QQ_DIR!\node_modules\openclaw" rmdir /s /q "!QQ_DIR!\node_modules\openclaw" 2>nul
if exist "!QQ_DIR!\dist\index.js" (
echo QQ plugin ready
) else (
echo [WARNING] The QQ plugin is missing dist\index.js
)
)
REM ---- Copy extensions (WeChat plugin etc.) ----
if exist "%APP_DIR%\extensions\openclaw-weixin\openclaw.plugin.json" (
echo Installing WeChat plugin...
xcopy /s /e /q /y "%APP_DIR%\extensions\openclaw-weixin" "%INSTALL_TARGET%\extensions\openclaw-weixin\" >nul
REM Also install to ~/.openclaw/extensions/ for Gateway
mkdir "%USERPROFILE%\.openclaw\extensions" 2>nul
xcopy /s /e /q /y "%APP_DIR%\extensions\openclaw-weixin" "%USERPROFILE%\.openclaw\extensions\openclaw-weixin\" >nul
echo WeChat plugin installed!
)
REM ---- Default config ----
if not exist "%INSTALL_TARGET%\data\.openclaw\openclaw.json" (
(echo {"gateway":{"mode":"local","auth":{"token":"uclaw"}}})>"%INSTALL_TARGET%\data\.openclaw\openclaw.json"
)
REM ---- Copy HTML pages ----
if exist "%UCLAW_DIR%Config.html" copy "%UCLAW_DIR%Config.html" "%INSTALL_TARGET%\" >nul
if exist "%UCLAW_DIR%U-Claw.html" copy "%UCLAW_DIR%U-Claw.html" "%INSTALL_TARGET%\" >nul
REM ---- Create launch script ----
(
echo @echo off
echo setlocal EnableDelayedExpansion
echo chcp 65001 ^>nul 2^>^&1
echo title U-Claw
echo set "DIR=%%~dp0"
echo set "NODE_BIN=%%DIR%%runtime\node-win-x64\node.exe"
echo if not exist "%%NODE_BIN%%" set "NODE_BIN=node"
echo set "OPENCLAW_MJS=%%DIR%%core\node_modules\openclaw\openclaw.mjs"
echo set "OPENCLAW_HOME=%%DIR%%data"
echo set "OPENCLAW_STATE_DIR=%%DIR%%data\.openclaw"
echo set "OPENCLAW_CONFIG_PATH=%%DIR%%data\.openclaw\openclaw.json"
echo set "OPENCLAW_DISABLE_BONJOUR=1"
echo.
echo REM Find available port
echo set PORT=18789
echo :check_port
echo netstat -an ^| findstr ":%%PORT%% " ^| findstr "LISTENING" ^>nul 2^>^&1
echo if %%errorlevel%%==0 (
echo set /a PORT+=1
echo if !PORT! gtr 18799 (echo No available port ^& pause ^& exit /b 1^)
echo goto :check_port
echo ^)
echo.
echo cd /d "%%DIR%%core"
echo start /B "" cmd /c "timeout /t 3 /nobreak ^>nul ^&^& start http://127.0.0.1:!PORT!/#token=uclaw"
echo "%%NODE_BIN%%" "%%OPENCLAW_MJS%%" gateway run --allow-unconfigured --force --port !PORT!
echo pause
) > "%INSTALL_TARGET%\start.bat"
echo.
echo ========================================
echo Installed!
echo ========================================
echo.
echo Location: %INSTALL_TARGET%
echo.
for /f "tokens=*" %%s in ('powershell -command "(Get-ChildItem '%INSTALL_TARGET%' -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum / 1MB" 2^>nul') do echo Size: %%s MB
echo.
echo To start it:
echo Double-click %INSTALL_TARGET%\start.bat
echo.
echo First time:
echo Settings opens in your browser by itself
echo Paste an API key and you are ready
echo.
pause

View File

@@ -0,0 +1,23 @@
@echo off
chcp 65001 >nul 2>&1
title U-Claw - Intranet Check
setlocal
for %%I in ("%~dp0..") do set "UCLAW_DIR=%%~fI\"
set "NODE_BIN=%UCLAW_DIR%app\runtime\node-win-x64\node.exe"
set "CONFIG_PATH=%UCLAW_DIR%data\.openclaw\openclaw.json"
if not exist "%NODE_BIN%" (
echo [ERROR] Node runtime not found.
echo Put this file inside your U-Claw folder, next to the START HERE files, then run again.
echo.
pause
exit /b 1
)
"%NODE_BIN%" "%UCLAW_DIR%lib\intranet-check.mjs" "%CONFIG_PATH%"
echo.
echo Please screenshot the whole window and send it to support.
echo.
pause

View File

@@ -0,0 +1,21 @@
@echo off
chcp 65001 >nul 2>&1
title U-Claw - Local / Intranet Model Setup
setlocal
for %%I in ("%~dp0..") do set "UCLAW_DIR=%%~fI\"
set "NODE_BIN=%UCLAW_DIR%app\runtime\node-win-x64\node.exe"
set "CONFIG_PATH=%UCLAW_DIR%data\.openclaw\openclaw.json"
if not exist "%NODE_BIN%" (
echo [ERROR] Node runtime not found.
echo Put this file inside your U-Claw folder, next to the START HERE files, then run again.
echo.
pause
exit /b 1
)
"%NODE_BIN%" "%UCLAW_DIR%lib\setup-local-model.mjs" "%CONFIG_PATH%"
echo.
pause

View File

@@ -0,0 +1,630 @@
@echo off
setlocal EnableDelayedExpansion
chcp 65001 >nul 2>&1
title U-Claw Menu
for %%I in ("%~dp0..") do set "UCLAW_DIR=%%~fI\"
set "APP_DIR=%UCLAW_DIR%app"
REM Migration shim: rename old core-win to core for existing USB users
if exist "%APP_DIR%\core-win" if not exist "%APP_DIR%\core" ren "%APP_DIR%\core-win" core
set "CORE_DIR=%APP_DIR%\core"
set "DATA_DIR=%UCLAW_DIR%data"
set "STATE_DIR=%DATA_DIR%\.openclaw"
set "NODE_DIR=%UCLAW_DIR%app\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=%STATE_DIR%"
set "OPENCLAW_CONFIG_PATH=%STATE_DIR%\openclaw.json"
REM U-Claw opens the local dashboard directly; disable mDNS discovery on Windows
REM to avoid OpenClaw/@homebridge ciao crashes during bonjour re-advertise.
set "OPENCLAW_DISABLE_BONJOUR=1"
set "PATH=%NODE_DIR%;%PATH%"
set "OPENCLAW_MJS=%CORE_DIR%\node_modules\openclaw\openclaw.mjs"
set "LOG_DIR=%DATA_DIR%\logs"
set "BACKUP_DIR=%DATA_DIR%\backups"
if not exist "%STATE_DIR%" mkdir "%STATE_DIR%"
if not exist "%DATA_DIR%\memory" mkdir "%DATA_DIR%\memory"
if not exist "%BACKUP_DIR%" mkdir "%BACKUP_DIR%"
if not exist "%LOG_DIR%" mkdir "%LOG_DIR%"
:menu
cls
echo.
echo ========================================
echo U-Claw v1.1 - Menu
echo Portable AI Agent
echo ========================================
echo.
if exist "%NODE_BIN%" (
for /f "tokens=*" %%v in ('"%NODE_BIN%" --version') do echo Node: %%v
) else (
echo [!] Node.js not found
)
if exist "%STATE_DIR%\openclaw.json" (echo Config: OK) else (echo Config: NOT SET)
echo.
echo -- Config --
echo [1] Setup wizard (model, API key)
echo [2] Open web dashboard
echo.
echo -- Chat Platforms --
echo [3] QQ Bot (pre-installed, enter ID only)
echo [4] Other platforms (Feishu/Telegram/WeChat)
echo.
echo -- Maintenance --
echo [5] Diagnostics
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-15]: "
if "%choice%"=="1" goto :onboard
if "%choice%"=="2" goto :dashboard
if "%choice%"=="3" goto :qqbot
if "%choice%"=="4" goto :channels
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
goto :menu
:onboard
echo.
echo === Setup Wizard ===
echo.
echo DeepSeek - Custom Provider, URL: https://api.deepseek.com/v1
echo Kimi - Moonshot AI
echo Qwen - Qwen
echo Doubao - Volcano Engine
echo.
cd /d "%CORE_DIR%"
"%NODE_BIN%" "%OPENCLAW_MJS%" onboard
pause
goto :menu
:dashboard
echo.
echo Starting gateway...
set PORT=18789
:find_port
netstat -an | findstr ":%PORT% " | findstr "LISTENING" >nul 2>&1
if !errorlevel!==0 (
set /a PORT+=1
if !PORT! gtr 18799 (echo No available port & pause & goto :menu)
goto :find_port
)
cd /d "%CORE_DIR%"
if not exist "%STATE_DIR%\openclaw.json" (
(echo {"gateway":{"mode":"local","auth":{"token":"uclaw"}}})>"%STATE_DIR%\openclaw.json"
)
REM Read token from config
set "TOKEN=uclaw"
if exist "%STATE_DIR%\openclaw.json" (
for /f "tokens=*" %%t in ('"%NODE_BIN%" -e "try{const c=JSON.parse(require('fs').readFileSync(process.argv[1],'utf8'));console.log((c.gateway&&c.gateway.auth&&c.gateway.auth.token)||'uclaw')}catch(e){console.log('uclaw')}" "%STATE_DIR%\openclaw.json"') do set "TOKEN=%%t"
)
start /B "" cmd /c "timeout /t 3 /nobreak >nul && start http://127.0.0.1:!PORT!/#token=!TOKEN!"
"%NODE_BIN%" "%OPENCLAW_MJS%" gateway run --allow-unconfigured --force --port !PORT!
pause
goto :menu
:qqbot
echo.
echo === QQ Bot Setup ===
echo.
echo QQ plugin is pre-installed!
echo You only need your AppID and AppSecret.
echo.
echo Get them at: q.qq.com (create a bot)
echo.
set /p qqid=" AppID: "
set /p qqsecret=" AppSecret: "
if "%qqid%"=="" goto :qq_cancel
if "%qqsecret%"=="" goto :qq_cancel
cd /d "%CORE_DIR%"
"%NODE_BIN%" "%OPENCLAW_MJS%" channels add --channel qqbot --token "%qqid%:%qqsecret%"
echo.
set /p qqallow=" Your QQ number (allowlist, empty to skip): "
if not "%qqallow%"=="" "%NODE_BIN%" "%OPENCLAW_MJS%" config set channels.qqbot.allowFrom "%qqallow%"
echo.
echo QQ Bot configured! Restart gateway to apply.
pause
goto :menu
:qq_cancel
echo Cancelled.
pause
goto :menu
:channels
echo.
echo === Other Platforms ===
echo.
echo [a] Feishu (Lark) - Enterprise
echo [b] Telegram - International
echo [c] WeChat (plugin) - iPad protocol
echo [d] Discord
echo.
set /p ch_choice=" Choose (a-d, empty to cancel): "
if "%ch_choice%"=="a" (
echo.
echo Feishu setup:
echo 1. Visit open.feishu.cn/app to create an app
echo 2. Get App ID and App Secret
echo 3. Run Setup wizard [1] to bind
)
if "%ch_choice%"=="b" (
echo.
echo Telegram setup:
echo 1. Message @BotFather on Telegram
echo 2. Create a bot and get the token
echo 3. Run Setup wizard [1] to bind
)
if "%ch_choice%"=="c" (
echo.
echo Installing WeChat plugin...
cd /d "%CORE_DIR%"
"%NODE_BIN%" "%OPENCLAW_MJS%" plugins install @icesword760/openclaw-wechat
echo WeChat plugin installed!
)
if "%ch_choice%"=="d" (
echo.
echo Discord setup:
echo 1. Visit discord.com/developers/applications
echo 2. Create a bot and get the token
echo 3. Run Setup wizard [1] to bind
)
echo.
pause
goto :menu
:doctor
cd /d "%CORE_DIR%"
"%NODE_BIN%" "%OPENCLAW_MJS%" doctor --repair
pause
goto :menu
:backup
echo.
set "TS=%date:~0,4%%date:~5,2%%date:~8,2%_%time:~0,2%%time:~3,2%"
set "TS=!TS: =0!"
set "BK=%BACKUP_DIR%\backup_!TS!"
mkdir "!BK!" 2>nul
set "BK_COUNT=0"
if exist "%STATE_DIR%\openclaw.json" (
copy "%STATE_DIR%\openclaw.json" "!BK!\" >nul
echo + openclaw.json
set /a BK_COUNT+=1
)
if exist "%DATA_DIR%\memory" (
xcopy /s /q "%DATA_DIR%\memory" "!BK!\memory\" >nul 2>nul
echo + memory/
set /a BK_COUNT+=1
)
echo.
if !BK_COUNT!==0 (
echo Nothing to backup.
rmdir "!BK!" 2>nul
) else (
for /f "tokens=*" %%s in ('powershell -command "(Get-ChildItem '!BK!' -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum / 1KB" 2^>nul') do echo Size: %%s KB
echo Backup saved: !BK!
)
pause
goto :menu
:restore
echo.
echo === Restore Backup ===
echo.
echo Available backups:
echo.
set "BK_NUM=0"
for /d %%d in ("%BACKUP_DIR%\*") do (
set /a BK_NUM+=1
echo [!BK_NUM!] %%~nxd
set "BK_PATH_!BK_NUM!=%%d"
)
if !BK_NUM!==0 (
echo No backups found.
pause
goto :menu
)
echo.
set /p rnum=" Choose backup number: "
if "!rnum!"=="" goto :menu
set "RESTORE_PATH=!BK_PATH_%rnum%!"
if "!RESTORE_PATH!"=="" (
echo Invalid choice.
pause
goto :menu
)
if exist "!RESTORE_PATH!\openclaw.json" (
copy "!RESTORE_PATH!\openclaw.json" "%STATE_DIR%\" >nul
echo + Config restored
)
if exist "!RESTORE_PATH!\memory" (
xcopy /s /q "!RESTORE_PATH!\memory" "%DATA_DIR%\memory\" >nul 2>nul
echo + Memory restored
)
echo.
echo Restore complete!
pause
goto :menu
:sysinfo
echo.
echo === System Info ===
echo.
echo OS: Windows
for /f "tokens=2 delims==" %%v in ('wmic os get Version /format:list 2^>nul ^| findstr "="') do echo Ver: %%v
for /f "tokens=2 delims==" %%v in ('wmic os get OSArchitecture /format:list 2^>nul ^| findstr "="') do echo Arch: %%v
for /f "tokens=2 delims==" %%v in ('wmic computersystem get TotalPhysicalMemory /format:list 2^>nul ^| findstr "="') do (
set "MEM=%%v"
set /a "MEM_GB=!MEM:~0,-9!"
echo Memory: !MEM_GB! GB
)
if exist "%NODE_BIN%" (
for /f "tokens=*" %%v in ('"%NODE_BIN%" --version') do echo Node: %%v
)
echo Path: %UCLAW_DIR%
echo Data: %DATA_DIR%
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 Size: %%s MB
for /f "tokens=3" %%s in ('dir /-c "%UCLAW_DIR%." 2^>nul ^| findstr /c:"bytes free"') do echo Free: %%s bytes
echo.
if exist "%CORE_DIR%\node_modules\openclaw\package.json" (
for /f "tokens=*" %%v in ('"%NODE_BIN%" -e "console.log(require('%CORE_DIR:\=/%/node_modules/openclaw/package.json').version)"') do echo OpenClaw: %%v
)
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 PID: %%a
)
)
)
REM Also check for openclaw node processes
for /f "tokens=2" %%p in ('tasklist /fi "imagename eq node.exe" /fo csv /nh 2^>nul ^| findstr /i "node"') do (
set "FOUND_NODE=1"
)
if "!FOUND!"=="0" (
echo No residual processes found on gateway ports.
pause
goto :menu
)
echo.
set /p killconfirm=" Kill these processes? (y/N): "
if /i not "!killconfirm!"=="y" (
echo Cancelled.
pause
goto :menu
)
for /l %%p in (18789,1,18799) do (
for /f "tokens=5" %%a in ('netstat -ano ^| findstr ":%%p " ^| findstr "LISTENING"') do (
taskkill /PID %%a /F >nul 2>&1
)
)
echo Processes killed.
pause
goto :menu
:viewlogs
echo.
echo === Log Management ===
echo.
set "LOG_FILE=%LOG_DIR%\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 in Notepad
echo [c] Export log to Desktop
echo [d] Clean logs older than 7 days
echo.
set /p logchoice=" Choose (a-d): "
if "%logchoice%"=="a" (
echo.
powershell -command "Get-Content '%LOG_FILE%' -Tail 50"
)
if "%logchoice%"=="b" (
start notepad "%LOG_FILE%"
)
if "%logchoice%"=="c" (
set "TS=%date:~0,4%%date:~5,2%%date:~8,2%_%time:~0,2%%time:~3,2%"
set "TS=!TS: =0!"
set "EXPORT=%USERPROFILE%\Desktop\uclaw-logs-!TS!.txt"
copy "%LOG_FILE%" "!EXPORT!" >nul
echo Log exported: !EXPORT!
)
if "%logchoice%"=="d" (
echo.
echo Cleaning logs older than 7 days...
powershell -command "Get-ChildItem '%LOG_DIR%' -Filter '*.log' | Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-7) } | Remove-Item -Force"
echo Old logs cleaned.
)
pause
goto :menu
:factoryreset
echo.
echo === Factory Reset ===
echo.
echo WARNING: This will delete all config and memory data!
echo.
echo Actions:
echo 1. Auto-backup current config and memory
echo 2. Delete config (openclaw.json)
echo 3. Delete memory data
echo 4. Restore default config
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=%BACKUP_DIR%\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 version: %USERPROFILE%\.uclaw
for /f "tokens=*" %%s in ('powershell -command "(Get-ChildItem '%USERPROFILE%\.uclaw' -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum / 1MB" 2^>nul') do echo Size: %%s MB
echo.
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...
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 version: %CUR_VER%
echo Fetching latest version...
for /f "tokens=*" %%v in ('"%NODE_BIN%" -e "const https=require('https');https.get('https://registry.npmjs.org/openclaw/latest',r=>{let d='';r.on('data',c=>d+=c);r.on('end',()=>{try{console.log(JSON.parse(d).version)}catch(e){console.log('error')}})})" 2^>nul') do set LATEST_VER=%%v
if "!LATEST_VER!"=="" (
echo Could not fetch latest version ^(network issue?^)
pause
goto :menu
)
if "!LATEST_VER!"=="error" (
echo Could not fetch latest version ^(network issue?^)
pause
goto :menu
)
echo Latest version: !LATEST_VER!
echo.
if "!CUR_VER!"=="!LATEST_VER!" (
echo Already up to date!
pause
goto :menu
)
echo New version available!
set /p doupdate=" Update now? (y/N): "
if /i not "!doupdate!"=="y" (
echo Cancelled.
pause
goto :menu
)
echo.
echo Updating...
cd /d "%CORE_DIR%"
call "%NPM_BIN%" install openclaw@latest --registry=https://registry.npmjs.org
for /f "tokens=*" %%v in ('"%NODE_BIN%" -e "console.log(require('./node_modules/openclaw/package.json').version)"') do set NEW_VER=%%v
echo.
echo Updated! %CUR_VER% - %NEW_VER%
pause
goto :menu
:diskcleanup
echo.
echo === Disk Cleanup ===
echo.
echo Directory sizes:
if exist "%CORE_DIR%\node_modules" (
for /f "tokens=*" %%s in ('powershell -command "(Get-ChildItem '%CORE_DIR%\node_modules' -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum / 1MB" 2^>nul') do echo node_modules: %%s MB
)
if exist "%DATA_DIR%\memory" (
for /f "tokens=*" %%s in ('powershell -command "(Get-ChildItem '%DATA_DIR%\memory' -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum / 1KB" 2^>nul') do echo memory: %%s KB
)
if exist "%BACKUP_DIR%" (
for /f "tokens=*" %%s in ('powershell -command "(Get-ChildItem '%BACKUP_DIR%' -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum / 1KB" 2^>nul') do echo backups: %%s KB
)
if exist "%LOG_DIR%" (
for /f "tokens=*" %%s in ('powershell -command "(Get-ChildItem '%LOG_DIR%' -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum / 1KB" 2^>nul') do echo logs: %%s KB
)
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.
REM Clean old backups (keep latest 3)
set "BK_COUNT=0"
for /d %%d in ("%BACKUP_DIR%\*") do set /a BK_COUNT+=1
if !BK_COUNT! gtr 3 (
set /a OLD_COUNT=BK_COUNT-3
echo Found !BK_COUNT! backups, keeping latest 3.
set /p delbk=" Delete !OLD_COUNT! old backups? (y/N): "
if /i "!delbk!"=="y" (
set "DEL_NUM=0"
for /f "tokens=*" %%d in ('powershell -command "Get-ChildItem '%BACKUP_DIR%' -Directory | Sort-Object LastWriteTime | Select-Object -First !OLD_COUNT! | ForEach-Object { $_.FullName }"') do (
rmdir /s /q "%%d" 2>nul
set /a DEL_NUM+=1
)
echo Cleaned !DEL_NUM! old backups.
)
) else (
echo Backups: !BK_COUNT! ^(no cleanup needed^)
)
REM Clean old logs (>7 days)
set "OLD_LOGS=0"
for /f "tokens=*" %%n in ('powershell -command "(Get-ChildItem '%LOG_DIR%' -Filter '*.log' -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-7) }).Count" 2^>nul') do set OLD_LOGS=%%n
if !OLD_LOGS! gtr 0 (
echo Found !OLD_LOGS! logs older than 7 days.
set /p dellog=" Delete old logs? (y/N): "
if /i "!dellog!"=="y" (
powershell -command "Get-ChildItem '%LOG_DIR%' -Filter '*.log' | Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-7) } | Remove-Item -Force"
echo Old logs cleaned.
)
) else (
echo Logs: no cleanup needed
)
REM Clean npm cache
echo.
set /p delcache=" Clean npm cache? (y/N): "
if /i "!delcache!"=="y" (
call "%NPM_BIN%" cache clean --force 2>nul
echo npm cache cleaned.
)
echo.
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 after cleanup: %%s MB
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" (
echo.
"%NODE_BIN%" "%OPENCLAW_MJS%" plugins list
)
if "%plgchoice%"=="b" (
echo.
echo Common plugins:
echo @icesword760/openclaw-wechat - WeChat
echo @nicepkg/openclaw-plugin-qq - QQ ^(community^)
echo.
set /p plgname=" Plugin name (empty to cancel): "
if not "!plgname!"=="" (
echo.
echo Installing !plgname! ...
"%NODE_BIN%" "%OPENCLAW_MJS%" plugins install "!plgname!"
echo.
echo Done!
) else (
echo Cancelled.
)
)
if "%plgchoice%"=="c" (
echo.
"%NODE_BIN%" "%OPENCLAW_MJS%" plugins list
echo.
set /p plgname=" Plugin to remove (empty to cancel): "
if not "!plgname!"=="" (
echo.
echo Removing !plgname! ...
"%NODE_BIN%" "%OPENCLAW_MJS%" plugins remove "!plgname!"
echo.
echo Done!
) else (
echo Cancelled.
)
)
pause
goto :menu

200
portable/advanced/setup.bat Normal file
View File

@@ -0,0 +1,200 @@
@echo off
setlocal EnableDelayedExpansion
chcp 65001 >nul 2>&1
title U-Claw Portable Setup
for %%I in ("%~dp0..") do set "SCRIPT_DIR=%%~fI\"
set "APP_DIR=%SCRIPT_DIR%app"
set "CORE_DIR=%APP_DIR%\core"
set "RUNTIME_DIR=%APP_DIR%\runtime"
set "MIRROR=https://registry.npmjs.org"
set "NODE_MIRROR=https://nodejs.org/dist"
set "NODE_VERSION=v22.22.1"
set "ALL_PLATFORMS=false"
if "%~1"=="--all-platforms" set "ALL_PLATFORMS=true"
echo.
echo ========================================
echo U-Claw Portable Setup
echo ========================================
echo.
echo System: Windows x64
echo.
REM ---- 1. Download Node.js (Current Platform - Windows) ----
set "NODE_DIR_NAME=node-win-x64"
set "NODE_TARGET=%RUNTIME_DIR%\%NODE_DIR_NAME%"
if exist "%NODE_TARGET%\node.exe" goto skip_node_download
echo [DOWNLOAD] Downloading Node.js %NODE_VERSION% (win-x64)...
if not exist "%NODE_TARGET%" mkdir "%NODE_TARGET%" 2>nul
set "NODE_URL=%NODE_MIRROR%/%NODE_VERSION%/node-%NODE_VERSION%-win-x64.zip"
echo URL: %NODE_URL%
set "TMP_ZIP=%TEMP%\node-win-x64-%RANDOM%.zip"
curl -fSL "%NODE_URL%" -o "%TMP_ZIP%"
set "DOWNLOAD_OK=0"
if %errorlevel% equ 0 set "DOWNLOAD_OK=1"
if "%DOWNLOAD_OK%"=="0" goto node_download_fail
echo Extracting...
powershell -command "Expand-Archive -Path '%TMP_ZIP%' -DestinationPath '%TEMP%\node-extract' -Force" >nul 2>&1
xcopy /s /e /q /y "%TEMP%\node-extract\node-%NODE_VERSION%-win-x64\*" "%NODE_TARGET%\" >nul
rmdir /s /q "%TEMP%\node-extract" 2>nul
del /f /q "%TMP_ZIP%" 2>nul
if not exist "%NODE_TARGET%\node.exe" goto node_download_fail
echo [OK] Node.js (win-x64) downloaded
goto node_download_done
:node_download_fail
echo [ERROR] Node.js download failed
pause
exit /b 1
:skip_node_download
echo [OK] Node.js (win-x64) exists, skipping
:node_download_done
REM ---- 1b. Download Node.js for Mac (only with --all-platforms) ----
if not "%ALL_PLATFORMS%"=="true" goto skip_mac_download
for %%p in (arm64 x64) do (
set "MAC_NODE_TARGET=%RUNTIME_DIR%\node-mac-%%p"
if exist "!MAC_NODE_TARGET!\bin\node" (
echo [OK] Node.js (mac-%%p) exists, skipping
) else (
echo [DOWNLOAD] Downloading Node.js %NODE_VERSION% (mac-%%p) for Mac support...
if not exist "!MAC_NODE_TARGET!" mkdir "!MAC_NODE_TARGET!" 2>nul
set "MAC_NODE_URL=%NODE_MIRROR%/%NODE_VERSION%/node-%NODE_VERSION%-darwin-%%p.tar.gz"
echo URL: !MAC_NODE_URL!
set "TMP_TAR=%TEMP%\node-mac-%%p-%RANDOM%.tar.gz"
curl -fSL "!MAC_NODE_URL!" -o "!TMP_TAR!"
if !errorlevel! equ 0 (
echo Extracting...
powershell -command "tar -xzf '!TMP_TAR!' -C '!MAC_NODE_TARGET!' --strip-components 1" >nul 2>&1
del /f /q "!TMP_TAR!" 2>nul
if exist "!MAC_NODE_TARGET!\bin\node" (
echo [OK] Node.js (mac-%%p) downloaded
) else (
echo [WARNING] Mac runtime download failed (does not affect current platform)
)
) else (
echo [WARNING] Mac runtime download failed (does not affect current platform)
)
)
)
:skip_mac_download
set "NPM_BIN=%NODE_TARGET%\npm.cmd"
REM ---- 2. Install OpenClaw ----
if exist "%CORE_DIR%\node_modules\openclaw" goto skip_openclaw_install
echo [INSTALL] Installing OpenClaw...
if not exist "%CORE_DIR%" mkdir "%CORE_DIR%" 2>nul
REM Read pinned OpenClaw version from portable root, then repo root in dev checkouts
set "OPENCLAW_VERSION_FILE=%~dp0OPENCLAW_VERSION"
if not exist "%OPENCLAW_VERSION_FILE%" set "OPENCLAW_VERSION_FILE=%~dp0..\OPENCLAW_VERSION"
set "OPENCLAW_VERSION=2026.4.29"
if exist "%OPENCLAW_VERSION_FILE%" (
for /f "usebackq delims=" %%v in ("%OPENCLAW_VERSION_FILE%") do set "OPENCLAW_VERSION=%%v"
)
REM Copy version file into portable/ so USB users can read it without repo root
if exist "%OPENCLAW_VERSION_FILE%" (
copy /y "%OPENCLAW_VERSION_FILE%" "%~dp0OPENCLAW_VERSION" >nul 2>&1
)
if not exist "%CORE_DIR%\package.json" (
echo { "name": "u-claw-core", "version": "1.0.0", "private": true, "dependencies": { "openclaw": "%OPENCLAW_VERSION%" } } > "%CORE_DIR%\package.json"
)
cd /d "%CORE_DIR%"
call "%NPM_BIN%" install --prefix "%CORE_DIR%" --registry="%MIRROR%"
echo [OK] OpenClaw installed
goto openclaw_install_done
:skip_openclaw_install
echo [OK] OpenClaw exists, skipping
:openclaw_install_done
REM ---- 3. Install QQ Plugin ----
if exist "%CORE_DIR%\node_modules\@sliverp\qqbot" goto skip_qq_install
echo [INSTALL] Installing QQ Plugin...
set "NPM_BIN=%NODE_TARGET%\npm.cmd"
cd /d "%CORE_DIR%"
call "%NPM_BIN%" install @sliverp/qqbot@latest --prefix "%CORE_DIR%" --registry="%MIRROR%" >nul 2>&1
echo [OK] QQ Plugin installed
goto qq_install_done
:skip_qq_install
echo [OK] QQ Plugin exists, skipping
:qq_install_done
set "QQ_DIR=%CORE_DIR%\node_modules\@sliverp\qqbot"
if not exist "%QQ_DIR%" goto qq_build_done
if exist "%QQ_DIR%\dist\index.js" goto qq_build_cleanup
echo [BUILD] Building QQ Plugin runtime files...
pushd "%QQ_DIR%"
call "%NPM_BIN%" install --include=dev --registry="%MIRROR%" >nul 2>&1
call "%NPM_BIN%" run build >nul 2>&1
call "%NPM_BIN%" prune --omit=dev >nul 2>&1
popd
:qq_build_cleanup
if exist "%QQ_DIR%\node_modules\openclaw" rmdir /s /q "%QQ_DIR%\node_modules\openclaw" 2>nul
if exist "%QQ_DIR%\dist\index.js" (
echo [OK] QQ Plugin runtime files ready
) else (
echo [WARNING] QQ Plugin dist\index.js is missing
)
:qq_build_done
REM ---- 4. Install skills from the manifest ----
REM Content lives in ..\skills (see skills\README.md); this script carries none.
set "SKILLS_TARGET=%CORE_DIR%\node_modules\openclaw\skills"
set "SKILL_INSTALLER=%SCRIPT_DIR%lib\install-skills.mjs"
if not exist "%SKILL_INSTALLER%" set "SKILL_INSTALLER=%SCRIPT_DIR%..\lib\install-skills.mjs"
if exist "%SKILL_INSTALLER%" if exist "%SKILLS_TARGET%" (
echo [SKILLS] Installing skills...
"%NODE_TARGET%\node.exe" "%SKILL_INSTALLER%" --target "%SKILLS_TARGET%" --locale en
)
REM ---- Done ----
echo.
echo ========================================
echo Setup Complete!
echo ========================================
echo.
echo To start:
echo Mac: bash START HERE - Mac.command
echo Windows: Double-click START HERE - Windows.bat
echo.
echo Directory structure:
echo app\core\ - OpenClaw + dependencies
echo app\runtime\ - Node.js %NODE_VERSION%
echo data\ - Auto-generated after first run
echo.
if "%ALL_PLATFORMS%"=="true" (
echo Note: All platform runtimes downloaded, ready for cross-platform USB
) else (
echo Note: For cross-platform USB use setup.bat --all-platforms
)
echo.
pause

295
portable/advanced/setup.ps1 Normal file
View File

@@ -0,0 +1,295 @@
param(
[switch]$AllPlatforms
)
$ErrorActionPreference = "Stop"
$scriptDir = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)
$appDir = Join-Path $scriptDir "app"
$coreDir = Join-Path $appDir "core"
$runtimeDir = Join-Path $appDir "runtime"
$mirror = "https://registry.npmjs.org"
$nodeMirror = "https://nodejs.org/dist"
$nodeVersion = "v22.22.1"
function Write-Step {
param(
[string]$Symbol,
[string]$Message,
[string]$Color = "Gray"
)
Write-Host (" {0} {1}" -f $Symbol, $Message) -ForegroundColor $Color
}
function Ensure-Directory {
param(
[string]$Path
)
if (-not (Test-Path -Path $Path -PathType Container)) {
New-Item -ItemType Directory -Path $Path -Force | Out-Null
}
}
function Download-File {
param(
[string]$Url,
[string]$Destination
)
Invoke-WebRequest -Uri $Url -OutFile $Destination
}
function Install-WindowsNode {
param(
[string]$TargetDir
)
$nodeExe = Join-Path $TargetDir "node.exe"
if (Test-Path -Path $nodeExe -PathType Leaf) {
Write-Step "OK" "Node.js (win-x64) already exists, skipping download." "Green"
return
}
Write-Step "->" "Downloading Node.js $nodeVersion (win-x64)..." "Cyan"
Ensure-Directory -Path $TargetDir
$zipName = "node-$nodeVersion-win-x64.zip"
$nodeUrl = "$nodeMirror/$nodeVersion/$zipName"
$tempZip = Join-Path $env:TEMP $zipName
$extractRoot = Join-Path $env:TEMP "uclaw-node-win-x64"
$extractDir = Join-Path $extractRoot "node-$nodeVersion-win-x64"
Write-Host " $nodeUrl"
Download-File -Url $nodeUrl -Destination $tempZip
if (Test-Path -Path $extractRoot) {
Remove-Item -Path $extractRoot -Recurse -Force
}
Expand-Archive -Path $tempZip -DestinationPath $extractRoot -Force
Copy-Item -Path (Join-Path $extractDir "*") -Destination $TargetDir -Recurse -Force
Remove-Item -Path $tempZip -Force -ErrorAction SilentlyContinue
Remove-Item -Path $extractRoot -Recurse -Force -ErrorAction SilentlyContinue
if (Test-Path -Path $nodeExe -PathType Leaf) {
Write-Step "OK" "Node.js (win-x64) download completed." "Green"
}
else {
Write-Step "ERR" "Node.js (win-x64) download failed." "Red"
exit 1
}
}
function Install-TarNodeRuntime {
param(
[string]$Platform,
[string]$NodeDirName
)
$targetDir = Join-Path $runtimeDir $NodeDirName
$nodeBinary = Join-Path $targetDir "bin/node"
if (Test-Path -Path $nodeBinary -PathType Leaf) {
Write-Step "OK" "Node.js ($Platform) already exists, skipping download." "Green"
return
}
$tarExe = Get-Command tar.exe -ErrorAction SilentlyContinue
if (-not $tarExe) {
Write-Step "WARN" "tar.exe was not found, skipping $Platform runtime." "Yellow"
return
}
Write-Step "->" "Downloading Node.js $nodeVersion ($Platform)..." "Cyan"
Ensure-Directory -Path $targetDir
$archiveName = "node-$nodeVersion-$Platform.tar.gz"
$nodeUrl = "$nodeMirror/$nodeVersion/$archiveName"
$tempArchive = Join-Path $env:TEMP $archiveName
$extractRoot = Join-Path $env:TEMP ("uclaw-{0}" -f $NodeDirName)
$extractDir = Join-Path $extractRoot "node-$nodeVersion-$Platform"
Write-Host " $nodeUrl"
Download-File -Url $nodeUrl -Destination $tempArchive
if (Test-Path -Path $extractRoot) {
Remove-Item -Path $extractRoot -Recurse -Force
}
Ensure-Directory -Path $extractRoot
& $tarExe.Path -xzf $tempArchive -C $extractRoot
if ($LASTEXITCODE -ne 0) {
Write-Step "WARN" "$Platform runtime extraction failed, skipping it." "Yellow"
Remove-Item -Path $tempArchive -Force -ErrorAction SilentlyContinue
Remove-Item -Path $extractRoot -Recurse -Force -ErrorAction SilentlyContinue
return
}
Copy-Item -Path (Join-Path $extractDir "*") -Destination $targetDir -Recurse -Force
Remove-Item -Path $tempArchive -Force -ErrorAction SilentlyContinue
Remove-Item -Path $extractRoot -Recurse -Force -ErrorAction SilentlyContinue
if (Test-Path -Path $nodeBinary -PathType Leaf) {
Write-Step "OK" "Node.js ($Platform) download completed." "Green"
}
else {
Write-Step "WARN" "$Platform runtime download failed, but Windows setup can continue." "Yellow"
}
}
function Ensure-QQPluginBuild {
param(
[string]$CoreDir,
[string]$NpmCmd
)
$qqDir = Join-Path $CoreDir "node_modules\@sliverp\qqbot"
if (-not (Test-Path -Path $qqDir -PathType Container)) {
return
}
$distIndex = Join-Path $qqDir "dist\index.js"
if (-not (Test-Path -Path $distIndex -PathType Leaf)) {
Write-Step "->" "Building QQ plugin runtime files..." "Cyan"
Push-Location $qqDir
try {
& $NpmCmd install --include=dev --registry=$mirror
& $NpmCmd run build
& $NpmCmd prune --omit=dev
}
catch {
Write-Step "WARN" "QQ plugin build command failed; continuing setup." "Yellow"
}
finally {
Pop-Location
}
}
$nestedOpenClaw = Join-Path $qqDir "node_modules\openclaw"
if (Test-Path -Path $nestedOpenClaw -PathType Container) {
Remove-Item -Path $nestedOpenClaw -Recurse -Force -ErrorAction SilentlyContinue
}
if (Test-Path -Path $distIndex -PathType Leaf) {
Write-Step "OK" "QQ plugin runtime files are ready." "Green"
}
else {
Write-Step "WARN" "QQ plugin is installed but dist/index.js is missing." "Yellow"
}
}
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " U-Claw Portable Setup" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
Write-Host (" System: {0}" -f [System.Runtime.InteropServices.RuntimeInformation]::OSDescription) -ForegroundColor Green
Write-Host ""
Ensure-Directory -Path $appDir
Ensure-Directory -Path $coreDir
Ensure-Directory -Path $runtimeDir
$windowsNodeTarget = Join-Path $runtimeDir "node-win-x64"
Install-WindowsNode -TargetDir $windowsNodeTarget
if ($AllPlatforms) {
Install-TarNodeRuntime -Platform "darwin-arm64" -NodeDirName "node-mac-arm64"
Install-TarNodeRuntime -Platform "darwin-x64" -NodeDirName "node-mac-x64"
}
$packageJsonPath = Join-Path $coreDir "package.json"
if (-not (Test-Path -Path $packageJsonPath -PathType Leaf)) {
$openclawVersionFile = Join-Path $PSScriptRoot "OPENCLAW_VERSION"
if (-not (Test-Path -Path $openclawVersionFile -PathType Leaf)) {
$openclawVersionFile = Join-Path $PSScriptRoot "..\OPENCLAW_VERSION"
}
$openclawVersion = "2026.4.29"
if (Test-Path -Path $openclawVersionFile -PathType Leaf) {
$openclawVersion = (Get-Content -Path $openclawVersionFile -Raw).Trim()
}
$packageJson = @"
{
"name": "u-claw-core",
"version": "1.0.0",
"private": true,
"dependencies": {
"openclaw": "$openclawVersion"
}
}
"@
$packageJson | Out-File -FilePath $packageJsonPath -Encoding utf8
}
$npmCmd = Join-Path $windowsNodeTarget "npm.cmd"
if (Test-Path -Path (Join-Path $coreDir "node_modules\openclaw") -PathType Container) {
Write-Step "OK" "OpenClaw is already installed, skipping." "Green"
}
else {
Write-Step "->" "Installing OpenClaw..." "Cyan"
Push-Location $coreDir
try {
& $npmCmd install --prefix $coreDir --registry=$mirror
}
finally {
Pop-Location
}
if (Test-Path -Path (Join-Path $coreDir "node_modules\openclaw") -PathType Container) {
Write-Step "OK" "OpenClaw installation completed." "Green"
}
else {
Write-Step "ERR" "OpenClaw installation failed." "Red"
exit 1
}
}
if (Test-Path -Path (Join-Path $coreDir "node_modules\@sliverp\qqbot") -PathType Container) {
Write-Step "OK" "QQ plugin is already installed, skipping." "Green"
}
else {
Write-Step "->" "Installing QQ plugin..." "Cyan"
Push-Location $coreDir
try {
& $npmCmd install "@sliverp/qqbot@latest" --prefix $coreDir --registry=$mirror 2>$null
}
catch {
}
finally {
Pop-Location
}
Write-Step "OK" "QQ plugin installation finished." "Green"
}
Ensure-QQPluginBuild -CoreDir $coreDir -NpmCmd $npmCmd
# Skills: content lives in ..\skills (see skills\README.md); this script carries none.
$skillsTarget = Join-Path $coreDir "node_modules\openclaw\skills"
$skillInstaller = Join-Path $scriptDir "lib\install-skills.mjs"
if (-not (Test-Path $skillInstaller)) { $skillInstaller = Join-Path $scriptDir "..\lib\install-skills.mjs" }
if ((Test-Path $skillInstaller) -and (Test-Path -Path $skillsTarget -PathType Container)) {
Write-Step "->" "Installing skills..." "Cyan"
& (Join-Path $windowsNodeTarget "node.exe") $skillInstaller --target $skillsTarget --locale en
}
Write-Host ""
Write-Host "========================================" -ForegroundColor Green
Write-Host " Setup Complete" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Green
Write-Host ""
Write-Host " Launch:"
Write-Host " Mac: bash START HERE - Mac.command" -ForegroundColor Cyan
Write-Host " Windows: double-click START HERE - Windows.bat" -ForegroundColor Cyan
Write-Host ""
Write-Host " Structure:"
Write-Host " app/core/ -> OpenClaw and dependencies"
Write-Host " app/runtime/ -> Node.js $nodeVersion"
Write-Host " data/ -> created automatically after launch"
Write-Host ""
Write-Host " Tip: use .\setup.ps1 -AllPlatforms to prepare a cross-platform USB." -ForegroundColor Cyan

196
portable/advanced/setup.sh Executable file
View File

@@ -0,0 +1,196 @@
#!/bin/bash
# ============================================================
# U-Claw Portable — 开发环境搭建脚本
# 用法: bash setup.sh
# 作用: 下载 Node.js 运行时 + Installing OpenClaw 到 app/ 目录
# ============================================================
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
APP_DIR="$SCRIPT_DIR/app"
CORE_DIR="$APP_DIR/core"
RUNTIME_DIR="$APP_DIR/runtime"
MIRROR="https://registry.npmjs.org"
NODE_MIRROR="https://nodejs.org/dist"
NODE_VERSION="$(tr -d "[:space:]" < "$SCRIPT_DIR/NODE_VERSION" 2>/dev/null || tr -d "[:space:]" < "$SCRIPT_DIR/../NODE_VERSION" 2>/dev/null || echo v22.22.1)"
ALL_PLATFORMS=false
[ "$1" = "--all-platforms" ] && ALL_PLATFORMS=true
GREEN='\033[0;32m'
CYAN='\033[0;36m'
RED='\033[0;31m'
NC='\033[0m'
echo ""
echo -e "${CYAN}╔══════════════════════════════════════╗${NC}"
echo -e "${CYAN}║ 🦞 U-Claw Portable Setup ║${NC}"
echo -e "${CYAN}╚══════════════════════════════════════╝${NC}"
echo ""
# ---- Detect OS & Arch ----
OS=$(uname -s)
ARCH=$(uname -m)
if [ "$OS" = "Darwin" ]; then
if [ "$ARCH" = "arm64" ]; then
PLATFORM="darwin-arm64"
NODE_DIR_NAME="node-mac-arm64"
else
PLATFORM="darwin-x64"
NODE_DIR_NAME="node-mac-x64"
fi
else
echo -e "${RED}Run this on a Mac. On Windows, use setup.bat instead.${NC}"
exit 1
fi
echo -e " System: ${GREEN}$OS $ARCH${NC}"
echo ""
# ---- 1. Download Node.js (Current Platform) ----
NODE_TARGET="$RUNTIME_DIR/$NODE_DIR_NAME"
if [ -f "$NODE_TARGET/bin/node" ]; then
echo -e " ${GREEN}${NC} Node.js ($PLATFORM) already downloaded, skipping"
else
echo -e " ${CYAN}${NC} Downloading Node.js $NODE_VERSION ($PLATFORM)"
mkdir -p "$NODE_TARGET"
NODE_URL="$NODE_MIRROR/$NODE_VERSION/node-$NODE_VERSION-$PLATFORM.tar.gz"
echo " $NODE_URL"
curl -fSL "$NODE_URL" | tar xz -C "$NODE_TARGET" --strip-components=1
if [ -f "$NODE_TARGET/bin/node" ]; then
echo -e " ${GREEN}${NC} Node.js ($PLATFORM) downloaded"
else
echo -e " ${RED}✗ Node.js Download failed${NC}"
exit 1
fi
fi
# ---- 1b. Download Node.js for Windows (only with --all-platforms) ----
if [ "$ALL_PLATFORMS" = "true" ]; then
WIN_NODE_TARGET="$RUNTIME_DIR/node-win-x64"
if [ -f "$WIN_NODE_TARGET/node.exe" ]; then
echo -e " ${GREEN}${NC} Node.js (win-x64) already downloaded, skipping"
else
echo -e " ${CYAN}${NC} Downloading Node.js $NODE_VERSION (win-x64) for Windows"
mkdir -p "$WIN_NODE_TARGET"
WIN_NODE_URL="$NODE_MIRROR/$NODE_VERSION/node-$NODE_VERSION-win-x64.zip"
echo " $WIN_NODE_URL"
TMP_ZIP="/tmp/node-win-x64-$$.zip"
curl -fSL "$WIN_NODE_URL" -o "$TMP_ZIP"
if command -v unzip >/dev/null 2>&1; then
unzip -q "$TMP_ZIP" -d "/tmp/node-win-extract-$$"
cp -r "/tmp/node-win-extract-$$"/node-$NODE_VERSION-win-x64/* "$WIN_NODE_TARGET/"
rm -rf "/tmp/node-win-extract-$$"
else
echo -e " ${RED}✗ unzip not found, skipping Windows runtime${NC}"
fi
rm -f "$TMP_ZIP"
if [ -f "$WIN_NODE_TARGET/node.exe" ]; then
echo -e " ${GREEN}${NC} Node.js (win-x64) downloaded"
else
echo -e " ${CYAN}${NC} Windows runtime download failed — this platform still works"
fi
fi
fi
# ---- 2. Install OpenClaw ----
if [ -d "$CORE_DIR/node_modules/openclaw" ]; then
echo -e " ${GREEN}${NC} OpenClaw already installed, skipping"
else
echo -e " ${CYAN}${NC} Installing OpenClaw..."
mkdir -p "$CORE_DIR"
# Init package.json if not exists (pinned OpenClaw version from OPENCLAW_VERSION)
OPENCLAW_VERSION_FILE="$(dirname "$0")/OPENCLAW_VERSION"
if [ ! -f "$OPENCLAW_VERSION_FILE" ]; then
OPENCLAW_VERSION_FILE="$(dirname "$0")/../OPENCLAW_VERSION"
fi
OPENCLAW_VERSION="2026.7.1-2"
if [ -f "$OPENCLAW_VERSION_FILE" ]; then
OPENCLAW_VERSION="$(tr -d '[:space:]' < "$OPENCLAW_VERSION_FILE")"
# Copy version file into portable/ so USB users can read it without repo root
cp "$OPENCLAW_VERSION_FILE" "$(dirname "$0")/OPENCLAW_VERSION" 2>/dev/null || true
fi
if [ ! -f "$CORE_DIR/package.json" ]; then
cat > "$CORE_DIR/package.json" << PKGJSON
{
"name": "u-claw-core",
"version": "1.0.0",
"private": true,
"dependencies": {
"openclaw": "$OPENCLAW_VERSION"
}
}
PKGJSON
fi
# Install with China mirror缓存留盘内拔盘不留痕
# --ignore-scripts 必须加openclaw 的 preinstall 脚本会调系统 `node`,但便携版
# 用的是 app/runtime 下的 node不在 PATH未装 Node 的 Mac 用户会因
# "node: command not found" 安装失败 (code 127)。与 START HERE - Mac.command 的 fallback 对齐。
NODE_BIN="$NODE_TARGET/bin/node"
NPM_BIN="$NODE_TARGET/bin/npm"
npm_config_cache="$APP_DIR/.npm-cache" "$NODE_BIN" "$NPM_BIN" install --prefix "$CORE_DIR" --registry="$MIRROR" --ignore-scripts --no-audit --no-fund --omit=dev
echo -e " ${GREEN}${NC} OpenClaw installed"
fi
# ---- 3. Install QQ Plugin ----
if [ -d "$CORE_DIR/node_modules/@sliverp/qqbot" ]; then
echo -e " ${GREEN}${NC} QQ plugin already installed, skipping"
else
echo -e " ${CYAN}${NC} Installing the QQ plugin..."
NODE_BIN="$NODE_TARGET/bin/node"
NPM_BIN="$NODE_TARGET/bin/npm"
npm_config_cache="$APP_DIR/.npm-cache" "$NODE_BIN" "$NPM_BIN" install @sliverp/qqbot@latest --prefix "$CORE_DIR" --registry="$MIRROR" --ignore-scripts --no-audit --no-fund --omit=dev 2>/dev/null || true
echo -e " ${GREEN}${NC} QQ plugin installed"
fi
# ---- 3b. Stage what the drive needs to stand alone ----
# release.yml puts these on the drive; a locally built drive has to match, or the
# first-run wizard cannot install skills and the drive cannot switch locale offline.
for VERSION_FILE in OPENCLAW_VERSION NODE_VERSION; do
[ -f "$SCRIPT_DIR/$VERSION_FILE" ] || cp "$SCRIPT_DIR/../$VERSION_FILE" "$SCRIPT_DIR/$VERSION_FILE" 2>/dev/null || true
done
if [ -d "$SCRIPT_DIR/../skills" ] && [ ! -d "$SCRIPT_DIR/skills" ]; then
cp -R "$SCRIPT_DIR/../skills" "$SCRIPT_DIR/skills"
fi
mkdir -p "$SCRIPT_DIR/lib"
[ -f "$SCRIPT_DIR/lib/install-skills.mjs" ] || cp "$SCRIPT_DIR/../lib/install-skills.mjs" "$SCRIPT_DIR/lib/install-skills.mjs" 2>/dev/null || true
# ---- 4. Install skills from the manifest ----
# Content lives in ../skills (see skills/README.md); this script carries none.
SKILLS_TARGET="$CORE_DIR/node_modules/openclaw/skills"
SKILL_INSTALLER="$SCRIPT_DIR/lib/install-skills.mjs"
[ -f "$SKILL_INSTALLER" ] || SKILL_INSTALLER="$SCRIPT_DIR/../lib/install-skills.mjs"
if [ -f "$SKILL_INSTALLER" ] && [ -d "$SKILLS_TARGET" ]; then
echo -e " ${CYAN}\u2193${NC} Installing skills..."
"$NODE_TARGET/bin/node" "$SKILL_INSTALLER" --target "$SKILLS_TARGET" --locale "${UCLAW_LOCALE:-en}"
fi
# ---- Done ----
echo ""
echo -e "${GREEN}════════════════════════════════════════${NC}"
echo -e "${GREEN} Done.${NC}"
echo ""
echo -e " To start it:"
echo -e " Mac: ${CYAN}double-click \"START HERE - Mac.command\"${NC}"
echo -e " Windows: double-click ${CYAN}START HERE - Windows.bat${NC}"
echo ""
echo -e " Layout:"
echo -e " app/core/ OpenClaw and its dependencies"
echo -e " app/runtime/ ← Node.js $NODE_VERSION"
echo -e " data/ created on first run"
echo ""
echo -e " ${CYAN}Tip: use --all-platforms to prepare a drive that works on both Mac and Windows${NC}"
echo -e "${GREEN}════════════════════════════════════════${NC}"