From b57aeb068c55176375c23fa08efc7036385737a4 Mon Sep 17 00:00:00 2001 From: hfshfg <38004547@qq.com> Date: Thu, 30 Apr 2026 19:47:57 +0800 Subject: [PATCH] fix portable config and qqbot packaging --- bootable/README.md | 61 ++++++++++++++++++++++++ portable/Config.html | 61 +++++++++++++++++++----- portable/Windows-Install.bat | 18 +++++++ portable/config-server/public/index.html | 51 ++++++++++++++++---- portable/setup.bat | 24 +++++++++- portable/setup.ps1 | 43 +++++++++++++++++ 6 files changed, 236 insertions(+), 22 deletions(-) diff --git a/bootable/README.md b/bootable/README.md index 8b45d1f..531e0ba 100644 --- a/bootable/README.md +++ b/bootable/README.md @@ -81,6 +81,67 @@ cd path\to\u-claw\bootable .\4-copy-to-usb.ps1 ``` +## 自动脚本失败时的手动兜底流程 + +> 由 @wzf9 在 issue #28 反馈整理,适合 Ventoy/ISO 自动下载失败、网络不稳定或需要离线制作的场景。 + +1. **Ventoy 下载或安装失败** + - 手动下载 Ventoy Windows 版:https://github.com/ventoy/Ventoy/releases + - 解压后运行 `Ventoy2Disk.exe` + - 选择目标 U 盘并点击 Install + - 注意:这一步会格式化 U 盘,先备份数据 + +2. **Ubuntu ISO 自动下载失败** + - 手动下载 Ubuntu 24.04.4 Desktop ISO: + https://releases.ubuntu.com/24.04/ubuntu-24.04.4-desktop-amd64.iso + - 手动下载 SHA256SUMS: + https://releases.ubuntu.com/24.04/SHA256SUMS + - 将 ISO 放到 `bootable\.download-cache\ubuntu-24.04.4-desktop-amd64.iso` + - 可用 PowerShell 校验哈希: + +```powershell +(Get-FileHash -Algorithm SHA256 ".\.download-cache\ubuntu-24.04.4-desktop-amd64.iso").Hash -eq "3a4c9877b483ab46d7c3fbe165a0db275e1ae3cfe56a5657e5a47c2f99a99d1e" +``` + +3. **继续创建持久化文件** + +```powershell +.\3-create-persistence.ps1 +``` + +如果脚本提示没有 WSL 或只能创建空文件,首次进入 Ubuntu 后需要手动格式化: + +```bash +sudo mkfs.ext4 -F -L casper-rw /media/*/Ventoy/persistence.dat +``` + +格式化完成后重启,持久化才会生效。 + +4. **拷贝到 U 盘** + - 优先运行: + +```powershell +.\4-copy-to-usb.ps1 +``` + + - 如果自动拷贝失败,也可以手动放到 Ventoy 数据分区根目录: + +| 本地路径 | U 盘目标 | +|---------|----------| +| `bootable\linux-setup\` | `u-claw-linux\` | +| `bootable\ventoy\` | `ventoy\` | +| `bootable\.download-cache\persistence.dat` | `persistence.dat` | +| `bootable\.download-cache\ubuntu-24.04.4-desktop-amd64.iso` | `ubuntu-24.04.4-desktop-amd64.iso` | + +5. **目标电脑启动** + - 从 U 盘启动,Ventoy 菜单选择 Ubuntu + - 如遇 Secure Boot 拦截,进 BIOS 关闭 Secure Boot + - 进入 Ubuntu 桌面后运行: + +```bash +sudo bash /media/*/Ventoy/u-claw-linux/setup-openclaw.sh +``` + ## 每一步做了什么 ### Step 1: 写入 Ventoy 引导 (`1-prepare-usb.ps1`) diff --git a/portable/Config.html b/portable/Config.html index b570e91..f937819 100644 --- a/portable/Config.html +++ b/portable/Config.html @@ -48,16 +48,51 @@ h1 .lobster { color: #ff6b35; } .buy-link:hover { text-decoration: underline; } /* Forms */ -.form-group { margin-bottom: 16px; } -label { display: block; color: #ccc; margin-bottom: 6px; font-size: 0.9em; } -input[type="text"], input[type="password"] { - width: 100%; padding: 11px 14px; background: #222; border: 1px solid #444; border-radius: 8px; - color: #fff; font-size: 0.95em; outline: none; transition: border-color 0.2s; +.form-group { display: block; margin-bottom: 16px; clear: both; } +.form-group label { + display: block; + width: auto; + float: none; + color: #ccc; + margin-bottom: 8px; + font-size: 0.9em; +} +.form-group input, +input[type="text"], +input[type="password"] { + display: block; + width: 100%; + min-width: 0; + min-height: 46px; + padding: 11px 14px; + background: #222; + border: 1px solid #444; + border-radius: 8px; + color: #fff; + font-size: 0.95em; + line-height: 1.3; + outline: none; + transition: border-color 0.2s; } input:focus { border-color: #ff6b35; } -.input-wrap { position: relative; } -.input-wrap input { padding-right: 44px; } -.toggle-pw { position: absolute; right: 12px; top: 50%; transform: translateY(-50%); background: none; border: none; color: #888; cursor: pointer; font-size: 0.85em; } +.input-wrap { position: relative; display: block; width: 100%; min-width: 0; } +.input-wrap input { padding-right: 64px; } +.toggle-pw { + position: absolute; + right: 10px; + top: 50%; + transform: translateY(-50%); + min-width: 38px; + height: 30px; + background: #2d2d2d; + border: 1px solid #444; + border-radius: 6px; + color: #ccc; + cursor: pointer; + font-size: 0.82em; + line-height: 28px; + text-align: center; +} .toggle-pw:hover { color: #ccc; } .hint { color: #888; font-size: 0.8em; margin-top: 6px; line-height: 1.5; } .hint a { color: #ff6b35; text-decoration: none; } @@ -359,10 +394,10 @@ let selectedBase = ''; let selectedModel = ''; // --- Model cards --- -document.querySelectorAll('.model-card').forEach(card => { +document.querySelectorAll('#step1 .model-card').forEach(card => { card.addEventListener('click', (e) => { if (e.target.closest('.buy-link')) return; // don't select when clicking buy link - document.querySelectorAll('.model-card').forEach(c => c.classList.remove('selected')); + document.querySelectorAll('#step1 .model-card').forEach(c => c.classList.remove('selected')); card.classList.add('selected'); selectedProvider = card.dataset.provider; selectedBase = card.dataset.base; @@ -375,7 +410,7 @@ document.getElementById('nextStep1').addEventListener('click', () => goStep(2)); // --- Steps --- function goStep(n) { - document.querySelectorAll('.section').forEach(s => s.classList.remove('active')); + document.querySelectorAll('.section:not(.skills-section)').forEach(s => s.classList.remove('active')); document.getElementById('step' + n).classList.add('active'); // Keep skills section visible document.querySelectorAll('.steps .step').forEach((s, i) => { @@ -393,7 +428,7 @@ function setupStep2() { const providerNames = { minimax: 'MiniMax', kimi: 'Kimi (月之暗面)', deepseek: 'DeepSeek', - zhipu: '智谱 GLM', qwen: '通义千问', doubao: '豆包', + zai: '智谱 GLM', qwen: '通义千问', doubao: '豆包', openai: 'OpenAI', anthropic: 'Anthropic Claude', groq: 'Groq', siliconflow: '硅基流动', custom: '自定义' }; @@ -401,7 +436,7 @@ function setupStep2() { document.getElementById('step2desc').textContent = `请填写 ${name} 的 API Key`; // Buy links - const card = document.querySelector(`.model-card[data-provider="${selectedProvider}"]`); + const card = document.querySelector(`#step1 .model-card[data-provider="${selectedProvider}"]`); const buyLink = card ? card.querySelector('.buy-link') : null; const linksDiv = document.getElementById('providerLinks'); linksDiv.innerHTML = ''; diff --git a/portable/Windows-Install.bat b/portable/Windows-Install.bat index e0e246d..eb39eeb 100644 --- a/portable/Windows-Install.bat +++ b/portable/Windows-Install.bat @@ -139,6 +139,24 @@ if "!USE_OPENCLAW!"=="usb" ( echo OpenClaw 下载安装完成! ) +set "QQ_DIR=%INSTALL_TARGET%\core\node_modules\@sliverp\qqbot" +if exist "!QQ_DIR!" ( + if not exist "!QQ_DIR!\dist\index.js" ( + echo 编译 QQbot 插件运行文件... + 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 QQbot 插件运行文件已就绪! + ) else ( + echo [WARNING] QQbot 插件缺少 dist\index.js + ) +) + REM ---- Copy extensions (WeChat plugin etc.) ---- if exist "%APP_DIR%\extensions\openclaw-weixin\openclaw.plugin.json" ( echo Installing WeChat plugin... diff --git a/portable/config-server/public/index.html b/portable/config-server/public/index.html index 3e55e06..53efc95 100644 --- a/portable/config-server/public/index.html +++ b/portable/config-server/public/index.html @@ -48,16 +48,51 @@ h1 .lobster { color: #ff6b35; } .buy-link:hover { text-decoration: underline; } /* Forms */ -.form-group { margin-bottom: 16px; } -label { display: block; color: #ccc; margin-bottom: 6px; font-size: 0.9em; } -input[type="text"], input[type="password"] { - width: 100%; padding: 11px 14px; background: #222; border: 1px solid #444; border-radius: 8px; - color: #fff; font-size: 0.95em; outline: none; transition: border-color 0.2s; +.form-group { display: block; margin-bottom: 16px; clear: both; } +.form-group label { + display: block; + width: auto; + float: none; + color: #ccc; + margin-bottom: 8px; + font-size: 0.9em; +} +.form-group input, +input[type="text"], +input[type="password"] { + display: block; + width: 100%; + min-width: 0; + min-height: 46px; + padding: 11px 14px; + background: #222; + border: 1px solid #444; + border-radius: 8px; + color: #fff; + font-size: 0.95em; + line-height: 1.3; + outline: none; + transition: border-color 0.2s; } input:focus { border-color: #ff6b35; } -.input-wrap { position: relative; } -.input-wrap input { padding-right: 44px; } -.toggle-pw { position: absolute; right: 12px; top: 50%; transform: translateY(-50%); background: none; border: none; color: #888; cursor: pointer; font-size: 0.85em; } +.input-wrap { position: relative; display: block; width: 100%; min-width: 0; } +.input-wrap input { padding-right: 70px; } +.toggle-pw { + position: absolute; + right: 10px; + top: 50%; + transform: translateY(-50%); + min-width: 44px; + height: 30px; + background: #2d2d2d; + border: 1px solid #444; + border-radius: 6px; + color: #ccc; + cursor: pointer; + font-size: 0.82em; + line-height: 28px; + text-align: center; +} .toggle-pw:hover { color: #ccc; } .hint { color: #888; font-size: 0.8em; margin-top: 6px; line-height: 1.5; } .hint a { color: #ff6b35; text-decoration: none; } diff --git a/portable/setup.bat b/portable/setup.bat index 0ee3689..31c87c6 100644 --- a/portable/setup.bat +++ b/portable/setup.bat @@ -97,6 +97,8 @@ for %%p in (arm64 x64) do ( :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 @@ -107,7 +109,6 @@ if not exist "%CORE_DIR%\package.json" ( echo { "name": "u-claw-core", "version": "1.0.0", "private": true, "dependencies": { "openclaw": "latest" } } > "%CORE_DIR%\package.json" ) -set "NPM_BIN=%NODE_TARGET%\npm.cmd" cd /d "%CORE_DIR%" call "%NPM_BIN%" install --prefix "%CORE_DIR%" --registry="%MIRROR%" @@ -132,6 +133,27 @@ goto qq_install_done 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 China-optimized skills ---- set "SKILLS_CN=%SCRIPT_DIR%skills-cn" set "SKILLS_TARGET=%CORE_DIR%\node_modules\openclaw\skills" diff --git a/portable/setup.ps1 b/portable/setup.ps1 index 4b1433d..3b101f2 100644 --- a/portable/setup.ps1 +++ b/portable/setup.ps1 @@ -141,6 +141,47 @@ function Install-TarNodeRuntime { } } +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 @@ -207,6 +248,8 @@ else { Write-Step "OK" "QQ plugin installation finished." "Green" } +Ensure-QQPluginBuild -CoreDir $coreDir -NpmCmd $npmCmd + $skillsCn = Join-Path $scriptDir "skills-cn" $skillsTarget = Join-Path $coreDir "node_modules\openclaw\skills"