按 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:
120
portable/advanced/Config.html
Normal file
120
portable/advanced/Config.html
Normal 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>
|
||||
141
portable/advanced/Mac-Diagnose.command
Normal file
141
portable/advanced/Mac-Diagnose.command
Normal 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..."
|
||||
257
portable/advanced/Mac-Install.command
Executable file
257
portable/advanced/Mac-Install.command
Executable 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…"
|
||||
33
portable/advanced/Mac-IntranetFix.command
Executable file
33
portable/advanced/Mac-IntranetFix.command
Executable 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."
|
||||
29
portable/advanced/Mac-LocalModel.command
Executable file
29
portable/advanced/Mac-LocalModel.command
Executable 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."
|
||||
306
portable/advanced/Mac-Menu.command
Executable file
306
portable/advanced/Mac-Menu.command
Executable 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
|
||||
63
portable/advanced/Mac-OpenClaw-CLI.command
Normal file
63
portable/advanced/Mac-OpenClaw-CLI.command
Normal 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
|
||||
52
portable/advanced/Mac-OpenClaw-Doctor.command
Executable file
52
portable/advanced/Mac-OpenClaw-Doctor.command
Executable 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."
|
||||
53
portable/advanced/OpenClaw-CLI.bat
Normal file
53
portable/advanced/OpenClaw-CLI.bat
Normal 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
|
||||
54
portable/advanced/OpenClaw-Doctor.bat
Normal file
54
portable/advanced/OpenClaw-Doctor.bat
Normal 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
|
||||
35
portable/advanced/README.md
Normal file
35
portable/advanced/README.md
Normal 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.
|
||||
125
portable/advanced/SkillHub.html
Normal file
125
portable/advanced/SkillHub.html
Normal 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>
|
||||
215
portable/advanced/U-Claw.html
Normal file
215
portable/advanced/U-Claw.html
Normal 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>
|
||||
186
portable/advanced/Windows-Diagnose.bat
Normal file
186
portable/advanced/Windows-Diagnose.bat
Normal 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
|
||||
229
portable/advanced/Windows-Install.bat
Normal file
229
portable/advanced/Windows-Install.bat
Normal 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
|
||||
23
portable/advanced/Windows-IntranetFix.bat
Normal file
23
portable/advanced/Windows-IntranetFix.bat
Normal 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
|
||||
21
portable/advanced/Windows-LocalModel.bat
Normal file
21
portable/advanced/Windows-LocalModel.bat
Normal 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
|
||||
630
portable/advanced/Windows-Menu.bat
Normal file
630
portable/advanced/Windows-Menu.bat
Normal 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
200
portable/advanced/setup.bat
Normal 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
295
portable/advanced/setup.ps1
Normal 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
196
portable/advanced/setup.sh
Executable 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}"
|
||||
Reference in New Issue
Block a user